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.
 
 
 
 
 

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