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.
 
 
 
 
 

461 lines
14 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 <decaf/shake.hxx>
  13. #include <decaf/crypto.h>
  14. #include <decaf/crypto.hxx>
  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. static __inline__ void __attribute__((unused)) ignore_result ( int result ) { (void)result; }
  23. static double now(void) {
  24. struct timeval tv;
  25. gettimeofday(&tv, NULL);
  26. return tv.tv_sec + tv.tv_usec/1000000.0;
  27. }
  28. // RDTSC from the chacha code
  29. #ifndef __has_builtin
  30. #define __has_builtin(X) 0
  31. #endif
  32. #if defined(__clang__) && __has_builtin(__builtin_readcyclecounter)
  33. #define rdtsc __builtin_readcyclecounter
  34. #else
  35. static inline uint64_t rdtsc(void) {
  36. u_int64_t out = 0;
  37. # if (defined(__i386__) || defined(__x86_64__))
  38. __asm__ __volatile__ ("rdtsc" : "=A"(out));
  39. # endif
  40. return out;
  41. }
  42. #endif
  43. static void printSI(double x, const char *unit, const char *spacer = " ") {
  44. const char *small[] = {" ","m","ยต","n","p"};
  45. const char *big[] = {" ","k","M","G","T"};
  46. if (x < 1) {
  47. unsigned di=0;
  48. for (di=0; di<sizeof(small)/sizeof(*small)-1 && x && x < 1; di++) {
  49. x *= 1000.0;
  50. }
  51. printf("%6.2f%s%s%s", x, spacer, small[di], unit);
  52. } else {
  53. unsigned di=0;
  54. for (di=0; di<sizeof(big)/sizeof(*big)-1 && x && x >= 1000; di++) {
  55. x /= 1000.0;
  56. }
  57. printf("%6.2f%s%s%s", x, spacer, big[di], unit);
  58. }
  59. }
  60. class Benchmark {
  61. static const int NTESTS = 20, NSAMPLES=50, DISCARD=2;
  62. static double totalCy, totalS;
  63. public:
  64. int i, j, ntests, nsamples;
  65. double begin;
  66. uint64_t tsc_begin;
  67. std::vector<double> times;
  68. std::vector<uint64_t> cycles;
  69. Benchmark(const char *s, double factor = 1) {
  70. printf("%s:", s);
  71. if (strlen(s) < 25) printf("%*s",int(25-strlen(s)),"");
  72. fflush(stdout);
  73. i = j = 0;
  74. ntests = NTESTS * factor;
  75. nsamples = NSAMPLES;
  76. begin = now();
  77. tsc_begin = rdtsc();
  78. times = std::vector<double>(NSAMPLES);
  79. cycles = std::vector<uint64_t>(NSAMPLES);
  80. }
  81. ~Benchmark() {
  82. double tsc = 0;
  83. double t = 0;
  84. std::sort(times.begin(), times.end());
  85. std::sort(cycles.begin(), cycles.end());
  86. for (int k=DISCARD; k<nsamples-DISCARD; k++) {
  87. tsc += cycles[k];
  88. t += times[k];
  89. }
  90. totalCy += tsc;
  91. totalS += t;
  92. t /= ntests*(nsamples-2*DISCARD);
  93. tsc /= ntests*(nsamples-2*DISCARD);
  94. printSI(t,"s");
  95. printf(" ");
  96. printSI(1/t,"/s");
  97. if (tsc) { printf(" "); printSI(tsc, "cy"); }
  98. printf("\n");
  99. }
  100. inline bool iter() {
  101. i++;
  102. if (i >= ntests) {
  103. uint64_t tsc = rdtsc() - tsc_begin;
  104. double t = now() - begin;
  105. begin += t;
  106. tsc_begin += tsc;
  107. assert(j >= 0 && j < nsamples);
  108. cycles[j] = tsc;
  109. times[j] = t;
  110. j++;
  111. i = 0;
  112. }
  113. return j < nsamples;
  114. }
  115. static void calib() {
  116. if (totalS && totalCy) {
  117. const char *s = "Cycle calibration";
  118. printf("%s:", s);
  119. if (strlen(s) < 25) printf("%*s",int(25-strlen(s)),"");
  120. printSI(totalCy / totalS, "Hz");
  121. printf("\n");
  122. }
  123. }
  124. };
  125. double Benchmark::totalCy = 0, Benchmark::totalS = 0;
  126. template<typename Group> struct Benches {
  127. typedef typename Group::Scalar Scalar;
  128. typedef typename Group::Point Point;
  129. typedef typename Group::Precomputed Precomputed;
  130. static void tdh (
  131. SpongeRng &clientRng,
  132. SpongeRng &serverRng,
  133. Scalar x, const Block &gx,
  134. Scalar y, const Block &gy
  135. ) {
  136. /* "TripleDH". A bit of a hack, really: the real TripleDH
  137. * sends gx and gy and certs over the channel, but its goal
  138. * is actually the opposite of STROBE in this case: it doesn't
  139. * hash gx and gy into the session secret (only into the MAC
  140. * and AD) because of IPR concerns.
  141. */
  142. Strobe client(Strobe::CLIENT), server(Strobe::SERVER);
  143. Scalar xe(clientRng);
  144. SecureBuffer gxe((Precomputed::base() * xe).serialize());
  145. client.send_plaintext(gxe);
  146. server.recv_plaintext(gxe);
  147. Scalar ye(serverRng);
  148. SecureBuffer gye((Precomputed::base() * ye).serialize());
  149. server.send_plaintext(gye);
  150. client.recv_plaintext(gye);
  151. Point pgxe(gxe);
  152. server.key(pgxe*ye);
  153. SecureBuffer tag1 = server.produce_auth();
  154. //SecureBuffer ct = server.encrypt(gy);
  155. server.key(pgxe*y);
  156. SecureBuffer tag2 = server.produce_auth();
  157. Point pgye(gye);
  158. client.key(pgye*xe);
  159. client.verify_auth(tag1);
  160. client.key(Point(gy) * xe);
  161. client.verify_auth(tag2);
  162. // ct = client.encrypt(gx);
  163. client.key(pgye * x);
  164. tag1 = client.produce_auth();
  165. client.respec(STROBE_KEYED_128);
  166. server.key(Point(gx) * ye);
  167. server.verify_auth(tag1);
  168. server.respec(STROBE_KEYED_128);
  169. }
  170. static void fhmqv (
  171. SpongeRng &clientRng,
  172. SpongeRng &serverRng,
  173. Scalar x, const Block &gx,
  174. Scalar y, const Block &gy
  175. ) {
  176. /* Don't use this, it's probably patented */
  177. Strobe client(Strobe::CLIENT), server(Strobe::SERVER);
  178. Scalar xe(clientRng);
  179. client.send_plaintext(gx);
  180. server.recv_plaintext(gx);
  181. SecureBuffer gxe((Precomputed::base() * xe).serialize());
  182. server.send_plaintext(gxe);
  183. client.recv_plaintext(gxe);
  184. Scalar ye(serverRng);
  185. server.send_plaintext(gy);
  186. client.recv_plaintext(gy);
  187. SecureBuffer gye((Precomputed::base() * ye).serialize());
  188. server.send_plaintext(gye);
  189. Scalar schx(server.prng(Scalar::SER_BYTES));
  190. Scalar schy(server.prng(Scalar::SER_BYTES));
  191. Scalar yec = y + ye*schy;
  192. server.key(Point::double_scalarmul(Point(gx),yec,Point(gxe),yec*schx));
  193. SecureBuffer as = server.produce_auth();
  194. client.recv_plaintext(gye);
  195. Scalar cchx(client.prng(Scalar::SER_BYTES));
  196. Scalar cchy(client.prng(Scalar::SER_BYTES));
  197. Scalar xec = x + xe*schx;
  198. client.key(Point::double_scalarmul(Point(gy),xec,Point(gye),xec*schy));
  199. client.verify_auth(as);
  200. SecureBuffer ac = client.produce_auth();
  201. client.respec(STROBE_KEYED_128);
  202. server.verify_auth(ac);
  203. server.respec(STROBE_KEYED_128);
  204. }
  205. static void spake2ee(
  206. SpongeRng &clientRng,
  207. SpongeRng &serverRng,
  208. const Block &hashed_password,
  209. bool aug
  210. ) {
  211. Strobe client(Strobe::CLIENT), server(Strobe::SERVER);
  212. Scalar x(clientRng);
  213. SHAKE<256> shake;
  214. shake.update(hashed_password);
  215. SecureBuffer h0 = shake.output(Point::HASH_BYTES);
  216. SecureBuffer h1 = shake.output(Point::HASH_BYTES);
  217. SecureBuffer h2 = shake.output(Scalar::SER_BYTES);
  218. Scalar gs(h2);
  219. Point hc = Point::from_hash(h0);
  220. hc = Point::from_hash(h0); // double-count
  221. Point hs = Point::from_hash(h1);
  222. hs = Point::from_hash(h1); // double-count
  223. SecureBuffer gx((Precomputed::base() * x + hc).serialize());
  224. client.send_plaintext(gx);
  225. server.recv_plaintext(gx);
  226. Scalar y(serverRng);
  227. SecureBuffer gy((Precomputed::base() * y + hs).serialize());
  228. server.send_plaintext(gy);
  229. client.recv_plaintext(gy);
  230. server.key(h1);
  231. server.key((Point(gx) - hc)*y);
  232. if(aug) {
  233. /* This step isn't actually online but whatever, it's fastish */
  234. SecureBuffer serverAug((Precomputed::base() * gs).serialize());
  235. server.key(Point(serverAug)*y);
  236. }
  237. SecureBuffer tag = server.produce_auth();
  238. client.key(h1);
  239. Point pgy(gy); pgy -= hs;
  240. client.key(pgy*x);
  241. if (aug) client.key(pgy * gs);
  242. client.verify_auth(tag);
  243. tag = client.produce_auth();
  244. client.respec(STROBE_KEYED_128);
  245. /* TODO: fork... */
  246. server.verify_auth(tag);
  247. server.respec(STROBE_KEYED_128);
  248. }
  249. static void macro() {
  250. printf("\nMacro-benchmarks for %s:\n", Group::name());
  251. printf("Crypto benchmarks:\n");
  252. SpongeRng rng(Block("macro rng seed"));
  253. PublicKey<Group> p1((NOINIT())), p2((NOINIT()));
  254. PrivateKey<Group> s1((NOINIT())), s2((NOINIT()));
  255. SecureBuffer message = rng.read(5), sig;
  256. for (Benchmark b("Create private key",1); b.iter(); ) {
  257. s1 = PrivateKey<Group>(rng);
  258. SecureBuffer bb = s1.serialize();
  259. }
  260. for (Benchmark b("Sign",1); b.iter(); ) {
  261. sig = s1.sign(message);
  262. }
  263. p1 = s1.pub();
  264. for (Benchmark b("Verify",1); b.iter(); ) {
  265. rng.read(Buffer(message));
  266. try { p1.verify(message, sig); } catch (CryptoException) {}
  267. }
  268. printf("\nProtocol benchmarks:\n");
  269. SpongeRng clientRng(Block("client rng seed"));
  270. SpongeRng serverRng(Block("server rng seed"));
  271. SecureBuffer hashedPassword(Block("hello world"));
  272. for (Benchmark b("Spake2ee c+s",0.1); b.iter(); ) {
  273. spake2ee(clientRng, serverRng, hashedPassword,false);
  274. }
  275. for (Benchmark b("Spake2ee c+s aug",0.1); b.iter(); ) {
  276. spake2ee(clientRng, serverRng, hashedPassword,true);
  277. }
  278. Scalar x(clientRng);
  279. SecureBuffer gx((Precomputed::base() * x).serialize());
  280. Scalar y(serverRng);
  281. SecureBuffer gy((Precomputed::base() * y).serialize());
  282. for (Benchmark b("FHMQV c+s",0.1); b.iter(); ) {
  283. fhmqv(clientRng, serverRng,x,gx,y,gy);
  284. }
  285. for (Benchmark b("TripleDH anon c+s",0.1); b.iter(); ) {
  286. tdh(clientRng, serverRng, x,gx,y,gy);
  287. }
  288. }
  289. static void micro() {
  290. SpongeRng rng(Block("per-curve-benchmarks"));
  291. Precomputed pBase;
  292. Point p,q;
  293. Scalar s(1),t(2);
  294. SecureBuffer ep, ep2(Point::SER_BYTES*2);
  295. printf("\nMicro-benchmarks for %s:\n", Group::name());
  296. for (Benchmark b("Scalar add", 1000); b.iter(); ) { s+=t; }
  297. for (Benchmark b("Scalar times", 100); b.iter(); ) { s*=t; }
  298. for (Benchmark b("Scalar inv", 1); b.iter(); ) { s.inverse(); }
  299. for (Benchmark b("Point add", 100); b.iter(); ) { p += q; }
  300. for (Benchmark b("Point double", 100); b.iter(); ) { p.double_in_place(); }
  301. for (Benchmark b("Point scalarmul"); b.iter(); ) { p * s; }
  302. for (Benchmark b("Point encode"); b.iter(); ) { ep = p.serialize(); }
  303. for (Benchmark b("Point decode"); b.iter(); ) { p = Point(ep); }
  304. for (Benchmark b("Point create/destroy"); b.iter(); ) { Point r; }
  305. for (Benchmark b("Point hash nonuniform"); b.iter(); ) { Point::from_hash(ep); }
  306. for (Benchmark b("Point hash uniform"); b.iter(); ) { Point::from_hash(ep2); }
  307. for (Benchmark b("Point unhash nonuniform"); b.iter(); ) { ignore_result(p.invert_elligator(ep,0)); }
  308. for (Benchmark b("Point unhash uniform"); b.iter(); ) { ignore_result(p.invert_elligator(ep2,0)); }
  309. for (Benchmark b("Point steg"); b.iter(); ) { p.steg_encode(rng); }
  310. for (Benchmark b("Point double scalarmul"); b.iter(); ) { Point::double_scalarmul(p,s,q,t); }
  311. for (Benchmark b("Point dual scalarmul"); b.iter(); ) { p.dual_scalarmul(p,q,s,t); }
  312. for (Benchmark b("Point precmp scalarmul"); b.iter(); ) { pBase * s; }
  313. for (Benchmark b("Point double scalarmul_v"); b.iter(); ) {
  314. s = Scalar(rng);
  315. t = Scalar(rng);
  316. p.non_secret_combo_with_base(s,t);
  317. }
  318. }
  319. }; /* template <typename group> struct Benches */
  320. int main(int argc, char **argv) {
  321. bool micro = false;
  322. if (argc >= 2 && !strcmp(argv[1], "--micro"))
  323. micro = true;
  324. decaf_255_public_key_t p1,p2;
  325. decaf_255_private_key_t s1,s2;
  326. decaf_255_symmetric_key_t r1,r2;
  327. decaf_255_signature_t sig1;
  328. unsigned char ss[32];
  329. memset(r1,1,sizeof(r1));
  330. memset(r2,2,sizeof(r2));
  331. unsigned char umessage[] = {1,2,3,4,5};
  332. size_t lmessage = sizeof(umessage);
  333. SpongeRng rng(Block("micro-benchmarks"));
  334. if (micro) {
  335. printf("\nMicro-benchmarks:\n");
  336. SHAKE<128> shake1;
  337. SHAKE<256> shake2;
  338. SHA3<512> sha5;
  339. Strobe strobe(Strobe::CLIENT);
  340. unsigned char b1024[1024] = {1};
  341. for (Benchmark b("SHAKE128 1kiB", 30); b.iter(); ) { shake1 += Buffer(b1024,1024); }
  342. for (Benchmark b("SHAKE256 1kiB", 30); b.iter(); ) { shake2 += Buffer(b1024,1024); }
  343. for (Benchmark b("SHA3-512 1kiB", 30); b.iter(); ) { sha5 += Buffer(b1024,1024); }
  344. strobe.key(Buffer(b1024,1024));
  345. strobe.respec(STROBE_128);
  346. for (Benchmark b("STROBE128 1kiB", 10); b.iter(); ) {
  347. strobe.encrypt_no_auth(Buffer(b1024,1024),Buffer(b1024,1024),b.i>1);
  348. }
  349. strobe.respec(STROBE_256);
  350. for (Benchmark b("STROBE256 1kiB", 10); b.iter(); ) {
  351. strobe.encrypt_no_auth(Buffer(b1024,1024),Buffer(b1024,1024),b.i>1);
  352. }
  353. strobe.respec(STROBE_KEYED_128);
  354. for (Benchmark b("STROBEk128 1kiB", 10); b.iter(); ) {
  355. strobe.encrypt_no_auth(Buffer(b1024,1024),Buffer(b1024,1024),b.i>1);
  356. }
  357. strobe.respec(STROBE_KEYED_256);
  358. for (Benchmark b("STROBEk256 1kiB", 10); b.iter(); ) {
  359. strobe.encrypt_no_auth(Buffer(b1024,1024),Buffer(b1024,1024),b.i>1);
  360. }
  361. Benches<IsoEd25519>::micro();
  362. Benches<Ed448Goldilocks>::micro();
  363. }
  364. /* TODO: 255->448 */
  365. printf("\nMacro-benchmarks:\n");
  366. for (Benchmark b("Keygen"); b.iter(); ) {
  367. decaf_255_derive_private_key(s1,r1);
  368. }
  369. decaf_255_private_to_public(p1,s1);
  370. decaf_255_derive_private_key(s2,r2);
  371. decaf_255_private_to_public(p2,s2);
  372. for (Benchmark b("Shared secret"); b.iter(); ) {
  373. decaf_bool_t ret = decaf_255_shared_secret(ss,sizeof(ss),s1,p2);
  374. ignore_result(ret);
  375. assert(ret);
  376. }
  377. for (Benchmark b("Sign"); b.iter(); ) {
  378. decaf_255_sign(sig1,s1,umessage,lmessage);
  379. }
  380. for (Benchmark b("Verify"); b.iter(); ) {
  381. decaf_bool_t ret = decaf_255_verify(sig1,p1,umessage,lmessage);
  382. rng.read(Buffer(umessage,lmessage));
  383. // umessage[0]++;
  384. // umessage[1]^=umessage[0];
  385. ignore_result(ret);
  386. }
  387. Benches<IsoEd25519>::macro();
  388. Benches<Ed448Goldilocks>::macro();
  389. printf("\n");
  390. Benchmark::calib();
  391. printf("\n");
  392. return 0;
  393. }