You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

402 lines
12 KiB

  1. /**
  2. * @file test_decaf.cxx
  3. * @author Mike Hamburg
  4. *
  5. * @copyright
  6. * Copyright (c) 2015 Cryptography Research, Inc. \n
  7. * Released under the MIT License. See LICENSE.txt for license information.
  8. *
  9. * @brief C++ benchmarks, because that's easier.
  10. */
  11. #include "decaf.hxx"
  12. #include "shake.hxx"
  13. #include "shake.h"
  14. #include "decaf_crypto.h"
  15. #include <stdio.h>
  16. #include <sys/time.h>
  17. #include <assert.h>
  18. #include <stdint.h>
  19. #include <vector>
  20. #include <algorithm>
  21. using namespace decaf;
  22. typedef EcGroup<448>::Scalar Scalar;
  23. typedef EcGroup<448>::Point Point;
  24. typedef EcGroup<448>::Precomputed Precomputed;
  25. static __inline__ void __attribute__((unused)) ignore_result ( int result ) { (void)result; }
  26. static double now(void) {
  27. struct timeval tv;
  28. gettimeofday(&tv, NULL);
  29. return tv.tv_sec + tv.tv_usec/1000000.0;
  30. }
  31. // RDTSC from the chacha code
  32. #ifndef __has_builtin
  33. #define __has_builtin(X) 0
  34. #endif
  35. #if defined(__clang__) && __has_builtin(__builtin_readcyclecounter)
  36. #define rdtsc __builtin_readcyclecounter
  37. #else
  38. static inline uint64_t rdtsc(void) {
  39. u_int64_t out = 0;
  40. # if (defined(__i386__) || defined(__x86_64__))
  41. __asm__ __volatile__ ("rdtsc" : "=A"(out));
  42. # endif
  43. return out;
  44. }
  45. #endif
  46. static void printSI(double x, const char *unit, const char *spacer = " ") {
  47. const char *small[] = {" ","m","ยต","n","p"};
  48. const char *big[] = {" ","k","M","G","T"};
  49. if (x < 1) {
  50. unsigned di=0;
  51. for (di=0; di<sizeof(small)/sizeof(*small)-1 && x && x < 1; di++) {
  52. x *= 1000.0;
  53. }
  54. printf("%6.2f%s%s%s", x, spacer, small[di], unit);
  55. } else {
  56. unsigned di=0;
  57. for (di=0; di<sizeof(big)/sizeof(*big)-1 && x && x >= 1000; di++) {
  58. x /= 1000.0;
  59. }
  60. printf("%6.2f%s%s%s", x, spacer, big[di], unit);
  61. }
  62. }
  63. class Benchmark {
  64. static const int NTESTS = 20, NSAMPLES=50, DISCARD=2;
  65. static double totalCy, totalS;
  66. /* FIXME Tcy if get descheduled */
  67. public:
  68. int i, j, ntests, nsamples;
  69. double begin;
  70. uint64_t tsc_begin;
  71. std::vector<double> times;
  72. std::vector<uint64_t> cycles;
  73. Benchmark(const char *s, double factor = 1) {
  74. printf("%s:", s);
  75. if (strlen(s) < 25) printf("%*s",int(25-strlen(s)),"");
  76. fflush(stdout);
  77. i = j = 0;
  78. ntests = NTESTS * factor;
  79. nsamples = NSAMPLES;
  80. begin = now();
  81. tsc_begin = rdtsc();
  82. times = std::vector<double>(NSAMPLES);
  83. cycles = std::vector<uint64_t>(NSAMPLES);
  84. }
  85. ~Benchmark() {
  86. double tsc = 0;
  87. double t = 0;
  88. std::sort(times.begin(), times.end());
  89. std::sort(cycles.begin(), cycles.end());
  90. for (int k=DISCARD; k<nsamples-DISCARD; k++) {
  91. tsc += cycles[k];
  92. t += times[k];
  93. }
  94. totalCy += tsc;
  95. totalS += t;
  96. t /= ntests*(nsamples-2*DISCARD);
  97. tsc /= ntests*(nsamples-2*DISCARD);
  98. printSI(t,"s");
  99. printf(" ");
  100. printSI(1/t,"/s");
  101. if (tsc) { printf(" "); printSI(tsc, "cy"); }
  102. printf("\n");
  103. }
  104. inline bool iter() {
  105. i++;
  106. if (i >= ntests) {
  107. uint64_t tsc = rdtsc() - tsc_begin;
  108. double t = now() - begin;
  109. begin += t;
  110. tsc_begin += tsc;
  111. assert(j >= 0 && j < nsamples);
  112. cycles[j] = tsc;
  113. times[j] = t;
  114. j++;
  115. i = 0;
  116. }
  117. return j < nsamples;
  118. }
  119. static void calib() {
  120. if (totalS && totalCy) {
  121. const char *s = "Cycle calibration";
  122. printf("%s:", s);
  123. if (strlen(s) < 25) printf("%*s",int(25-strlen(s)),"");
  124. printSI(totalCy / totalS, "Hz");
  125. printf("\n");
  126. }
  127. }
  128. };
  129. double Benchmark::totalCy = 0, Benchmark::totalS = 0;
  130. static void tdh (
  131. SpongeRng &clientRng,
  132. SpongeRng &serverRng,
  133. Scalar x, const Block &gx,
  134. Scalar y, const Block &gy
  135. ) {
  136. Strobe client(Strobe::CLIENT), server(Strobe::SERVER);
  137. Scalar xe(clientRng);
  138. SecureBuffer gxe = Precomputed::base() * xe;
  139. client.send_plaintext(gxe);
  140. server.recv_plaintext(gxe);
  141. Scalar ye(serverRng);
  142. SecureBuffer gye = Precomputed::base() * ye;
  143. server.send_plaintext(gye);
  144. client.recv_plaintext(gye);
  145. Point pgxe(gxe);
  146. server.key(pgxe*ye);
  147. SecureBuffer tag1 = server.produce_auth();
  148. SecureBuffer ct = server.encrypt(gy);
  149. server.key(pgxe*y);
  150. SecureBuffer tag2 = server.produce_auth();
  151. Point pgye(gye);
  152. client.key(pgye*xe);
  153. client.verify_auth(tag1);
  154. client.key(Point(client.decrypt(ct)) * xe);
  155. client.verify_auth(tag2);
  156. ct = client.encrypt(gx);
  157. client.key(pgye * x);
  158. tag1 = client.produce_auth();
  159. client.respec(STROBE_KEYED_128);
  160. server.key(Point(server.decrypt(ct)) * ye);
  161. server.verify_auth(tag1);
  162. server.respec(STROBE_KEYED_128);
  163. }
  164. static void fhmqv (
  165. SpongeRng &clientRng,
  166. SpongeRng &serverRng,
  167. Scalar x, const Block &gx,
  168. Scalar y, const Block &gy
  169. ) {
  170. /* Don't use this, it's probably patented */
  171. Strobe client(Strobe::CLIENT), server(Strobe::SERVER);
  172. Scalar xe(clientRng);
  173. client.send_plaintext(gx);
  174. server.recv_plaintext(gx);
  175. SecureBuffer gxe = Precomputed::base() * xe;
  176. server.send_plaintext(gxe);
  177. client.recv_plaintext(gxe);
  178. Scalar ye(serverRng);
  179. server.send_plaintext(gy);
  180. client.recv_plaintext(gy);
  181. SecureBuffer gye = Precomputed::base() * ye;
  182. server.send_plaintext(gye);
  183. Scalar schx(server.prng(Scalar::SER_BYTES));
  184. Scalar schy(server.prng(Scalar::SER_BYTES));
  185. Scalar yec = y + ye*schy;
  186. server.key(Point::double_scalarmul(Point(gx),yec,Point(gxe),yec*schx));
  187. SecureBuffer as = server.produce_auth();
  188. client.recv_plaintext(gye);
  189. Scalar cchx(client.prng(Scalar::SER_BYTES));
  190. Scalar cchy(client.prng(Scalar::SER_BYTES));
  191. Scalar xec = x + xe*schx;
  192. client.key(Point::double_scalarmul(Point(gy),xec,Point(gye),xec*schy));
  193. client.verify_auth(as);
  194. SecureBuffer ac = client.produce_auth();
  195. client.respec(STROBE_KEYED_128);
  196. server.verify_auth(ac);
  197. server.respec(STROBE_KEYED_128);
  198. }
  199. static void spake2ee(
  200. SpongeRng &clientRng,
  201. SpongeRng &serverRng,
  202. const Block &hashed_password,
  203. bool aug
  204. ) {
  205. Strobe client(Strobe::CLIENT), server(Strobe::SERVER);
  206. Scalar x(clientRng);
  207. SHAKE<256> shake;
  208. shake.update(hashed_password);
  209. SecureBuffer h0 = shake.output(Point::HASH_BYTES);
  210. SecureBuffer h1 = shake.output(Point::HASH_BYTES);
  211. SecureBuffer h2 = shake.output(Scalar::SER_BYTES);
  212. Scalar gs(h2);
  213. Point hc = Point::from_hash(h0);
  214. hc = Point::from_hash(h0); // double-count
  215. Point hs = Point::from_hash(h1);
  216. hs = Point::from_hash(h1); // double-count
  217. SecureBuffer gx(Precomputed::base() * x + hc);
  218. client.send_plaintext(gx);
  219. server.recv_plaintext(gx);
  220. Scalar y(serverRng);
  221. SecureBuffer gy(Precomputed::base() * y + hs);
  222. server.send_plaintext(gy);
  223. client.recv_plaintext(gy);
  224. server.key(h1);
  225. server.key((Point(gx) - hc)*y);
  226. if(aug) {
  227. /* This step isn't actually online but whatever, it's fastish */
  228. SecureBuffer serverAug(Precomputed::base() * gs);
  229. server.key(Point(serverAug)*y);
  230. }
  231. SecureBuffer tag = server.produce_auth();
  232. client.key(h1);
  233. Point pgy(gy); pgy -= hs;
  234. client.key(pgy*x);
  235. if (aug) client.key(pgy * gs);
  236. client.verify_auth(tag);
  237. tag = client.produce_auth();
  238. client.respec(STROBE_KEYED_128);
  239. /* TODO: fork... */
  240. server.verify_auth(tag);
  241. server.respec(STROBE_KEYED_128);
  242. }
  243. int main(int argc, char **argv) {
  244. bool micro = false;
  245. if (argc >= 2 && !strcmp(argv[1], "--micro"))
  246. micro = true;
  247. decaf_448_public_key_t p1,p2;
  248. decaf_448_private_key_t s1,s2;
  249. decaf_448_symmetric_key_t r1,r2;
  250. decaf_448_signature_t sig1;
  251. unsigned char ss[32];
  252. memset(r1,1,sizeof(r1));
  253. memset(r2,2,sizeof(r2));
  254. unsigned char umessage[] = {1,2,3,4,5};
  255. size_t lmessage = sizeof(umessage);
  256. if (micro) {
  257. Precomputed pBase;
  258. Point p,q;
  259. Scalar s,t;
  260. SecureBuffer ep, ep2(Point::SER_BYTES*2);
  261. printf("\nMicro-benchmarks:\n");
  262. SHAKE<128> shake1;
  263. SHAKE<256> shake2;
  264. SHA3<512> sha5;
  265. Strobe strobe(Strobe::CLIENT);
  266. unsigned char b1024[1024] = {1};
  267. for (Benchmark b("SHAKE128 1kiB", 30); b.iter(); ) { shake1 += TmpBuffer(b1024,1024); }
  268. for (Benchmark b("SHAKE256 1kiB", 30); b.iter(); ) { shake2 += TmpBuffer(b1024,1024); }
  269. for (Benchmark b("SHA3-512 1kiB", 30); b.iter(); ) { sha5 += TmpBuffer(b1024,1024); }
  270. strobe.key(TmpBuffer(b1024,1024));
  271. strobe.respec(STROBE_128);
  272. for (Benchmark b("STROBE128 1kiB", 10); b.iter(); ) {
  273. strobe.encrypt_no_auth(TmpBuffer(b1024,1024),TmpBuffer(b1024,1024),b.i>1);
  274. }
  275. strobe.respec(STROBE_256);
  276. for (Benchmark b("STROBE256 1kiB", 10); b.iter(); ) {
  277. strobe.encrypt_no_auth(TmpBuffer(b1024,1024),TmpBuffer(b1024,1024),b.i>1);
  278. }
  279. strobe.respec(STROBE_KEYED_128);
  280. for (Benchmark b("STROBEk128 1kiB", 10); b.iter(); ) {
  281. strobe.encrypt_no_auth(TmpBuffer(b1024,1024),TmpBuffer(b1024,1024),b.i>1);
  282. }
  283. strobe.respec(STROBE_KEYED_256);
  284. for (Benchmark b("STROBEk256 1kiB", 10); b.iter(); ) {
  285. strobe.encrypt_no_auth(TmpBuffer(b1024,1024),TmpBuffer(b1024,1024),b.i>1);
  286. }
  287. for (Benchmark b("Scalar add", 1000); b.iter(); ) { s+=t; }
  288. for (Benchmark b("Scalar times", 100); b.iter(); ) { s*=t; }
  289. for (Benchmark b("Scalar inv", 1); b.iter(); ) { s.inverse(); }
  290. for (Benchmark b("Point add", 100); b.iter(); ) { p += q; }
  291. for (Benchmark b("Point double", 100); b.iter(); ) { p.double_in_place(); }
  292. for (Benchmark b("Point scalarmul"); b.iter(); ) { p * s; }
  293. for (Benchmark b("Point encode"); b.iter(); ) { ep = SecureBuffer(p); }
  294. for (Benchmark b("Point decode"); b.iter(); ) { p = Point(ep); }
  295. for (Benchmark b("Point create/destroy"); b.iter(); ) { Point r; }
  296. for (Benchmark b("Point hash nonuniform"); b.iter(); ) { Point::from_hash(ep); }
  297. for (Benchmark b("Point hash uniform"); b.iter(); ) { Point::from_hash(ep2); }
  298. for (Benchmark b("Point double scalarmul"); b.iter(); ) { Point::double_scalarmul(p,s,q,t); }
  299. for (Benchmark b("Point precmp scalarmul"); b.iter(); ) { pBase * s; }
  300. /* TODO: scalarmul for verif, etc */
  301. }
  302. printf("\nMacro-benchmarks:\n");
  303. for (Benchmark b("Keygen"); b.iter(); ) {
  304. decaf_448_derive_private_key(s1,r1);
  305. }
  306. decaf_448_private_to_public(p1,s1);
  307. decaf_448_derive_private_key(s2,r2);
  308. decaf_448_private_to_public(p2,s2);
  309. for (Benchmark b("Shared secret"); b.iter(); ) {
  310. decaf_bool_t ret = decaf_448_shared_secret(ss,sizeof(ss),s1,p2);
  311. ignore_result(ret);
  312. assert(ret);
  313. }
  314. for (Benchmark b("Sign"); b.iter(); ) {
  315. decaf_448_sign(sig1,s1,umessage,lmessage);
  316. }
  317. for (Benchmark b("Verify"); b.iter(); ) {
  318. decaf_bool_t ret = decaf_448_verify(sig1,p1,umessage,lmessage);
  319. umessage[0]++;
  320. umessage[1]^=umessage[0];
  321. ignore_result(ret);
  322. }
  323. printf("\nProtocol benchmarks:\n");
  324. SpongeRng clientRng(Block("client rng seed"));
  325. SpongeRng serverRng(Block("server rng seed"));
  326. SecureBuffer hashedPassword("hello world");
  327. for (Benchmark b("Spake2ee c+s",0.1); b.iter(); ) {
  328. spake2ee(clientRng, serverRng, hashedPassword,false);
  329. }
  330. for (Benchmark b("Spake2ee c+s aug",0.1); b.iter(); ) {
  331. spake2ee(clientRng, serverRng, hashedPassword,true);
  332. }
  333. Scalar x(clientRng);
  334. SecureBuffer gx(Precomputed::base() * x);
  335. Scalar y(serverRng);
  336. SecureBuffer gy(Precomputed::base() * y);
  337. for (Benchmark b("FHMQV c+s",0.1); b.iter(); ) {
  338. fhmqv(clientRng, serverRng,x,gx,y,gy);
  339. }
  340. for (Benchmark b("TripleDH anon c+s",0.1); b.iter(); ) {
  341. tdh(clientRng, serverRng, x,gx,y,gy);
  342. }
  343. printf("\n");
  344. Benchmark::calib();
  345. printf("\n");
  346. return 0;
  347. }