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.
 
 
 
 
 

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