1 /* Copyright (c) 2014, Google Inc.
2  *
3  * Permission to use, copy, modify, and/or distribute this software for any
4  * purpose with or without fee is hereby granted, provided that the above
5  * copyright notice and this permission notice appear in all copies.
6  *
7  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
14 
15 #include <string>
16 #include <functional>
17 #include <memory>
18 #include <vector>
19 
20 #include <stdint.h>
21 #include <string.h>
22 
23 #include <openssl/aead.h>
24 #include <openssl/curve25519.h>
25 #include <openssl/digest.h>
26 #include <openssl/err.h>
27 #include <openssl/obj.h>
28 #include <openssl/rand.h>
29 #include <openssl/rsa.h>
30 
31 #if defined(OPENSSL_WINDOWS)
32 #pragma warning(push, 3)
33 #include <windows.h>
34 #pragma warning(pop)
35 #elif defined(OPENSSL_APPLE)
36 #include <sys/time.h>
37 #endif
38 
39 #include "../crypto/test/scoped_types.h"
40 #include "internal.h"
41 
42 
43 // TimeResults represents the results of benchmarking a function.
44 struct TimeResults {
45   // num_calls is the number of function calls done in the time period.
46   unsigned num_calls;
47   // us is the number of microseconds that elapsed in the time period.
48   unsigned us;
49 
PrintTimeResults50   void Print(const std::string &description) {
51     printf("Did %u %s operations in %uus (%.1f ops/sec)\n", num_calls,
52            description.c_str(), us,
53            (static_cast<double>(num_calls) / us) * 1000000);
54   }
55 
PrintWithBytesTimeResults56   void PrintWithBytes(const std::string &description, size_t bytes_per_call) {
57     printf("Did %u %s operations in %uus (%.1f ops/sec): %.1f MB/s\n",
58            num_calls, description.c_str(), us,
59            (static_cast<double>(num_calls) / us) * 1000000,
60            static_cast<double>(bytes_per_call * num_calls) / us);
61   }
62 };
63 
64 #if defined(OPENSSL_WINDOWS)
time_now()65 static uint64_t time_now() { return GetTickCount64() * 1000; }
66 #elif defined(OPENSSL_APPLE)
time_now()67 static uint64_t time_now() {
68   struct timeval tv;
69   uint64_t ret;
70 
71   gettimeofday(&tv, NULL);
72   ret = tv.tv_sec;
73   ret *= 1000000;
74   ret += tv.tv_usec;
75   return ret;
76 }
77 #else
time_now()78 static uint64_t time_now() {
79   struct timespec ts;
80   clock_gettime(CLOCK_MONOTONIC, &ts);
81 
82   uint64_t ret = ts.tv_sec;
83   ret *= 1000000;
84   ret += ts.tv_nsec / 1000;
85   return ret;
86 }
87 #endif
88 
TimeFunction(TimeResults * results,std::function<bool ()> func)89 static bool TimeFunction(TimeResults *results, std::function<bool()> func) {
90   // kTotalMS is the total amount of time that we'll aim to measure a function
91   // for.
92   static const uint64_t kTotalUS = 1000000;
93   uint64_t start = time_now(), now, delta;
94   unsigned done = 0, iterations_between_time_checks;
95 
96   if (!func()) {
97     return false;
98   }
99   now = time_now();
100   delta = now - start;
101   if (delta == 0) {
102     iterations_between_time_checks = 250;
103   } else {
104     // Aim for about 100ms between time checks.
105     iterations_between_time_checks =
106         static_cast<double>(100000) / static_cast<double>(delta);
107     if (iterations_between_time_checks > 1000) {
108       iterations_between_time_checks = 1000;
109     } else if (iterations_between_time_checks < 1) {
110       iterations_between_time_checks = 1;
111     }
112   }
113 
114   for (;;) {
115     for (unsigned i = 0; i < iterations_between_time_checks; i++) {
116       if (!func()) {
117         return false;
118       }
119       done++;
120     }
121 
122     now = time_now();
123     if (now - start > kTotalUS) {
124       break;
125     }
126   }
127 
128   results->us = now - start;
129   results->num_calls = done;
130   return true;
131 }
132 
SpeedRSA(const std::string & key_name,RSA * key,const std::string & selected)133 static bool SpeedRSA(const std::string &key_name, RSA *key,
134                      const std::string &selected) {
135   if (!selected.empty() && key_name.find(selected) == std::string::npos) {
136     return true;
137   }
138 
139   std::unique_ptr<uint8_t[]> sig(new uint8_t[RSA_size(key)]);
140   const uint8_t fake_sha256_hash[32] = {0};
141   unsigned sig_len;
142 
143   TimeResults results;
144   if (!TimeFunction(&results,
145                     [key, &sig, &fake_sha256_hash, &sig_len]() -> bool {
146         return RSA_sign(NID_sha256, fake_sha256_hash, sizeof(fake_sha256_hash),
147                         sig.get(), &sig_len, key);
148       })) {
149     fprintf(stderr, "RSA_sign failed.\n");
150     ERR_print_errors_fp(stderr);
151     return false;
152   }
153   results.Print(key_name + " signing");
154 
155   if (!TimeFunction(&results,
156                     [key, &fake_sha256_hash, &sig, sig_len]() -> bool {
157         return RSA_verify(NID_sha256, fake_sha256_hash,
158                           sizeof(fake_sha256_hash), sig.get(), sig_len, key);
159       })) {
160     fprintf(stderr, "RSA_verify failed.\n");
161     ERR_print_errors_fp(stderr);
162     return false;
163   }
164   results.Print(key_name + " verify");
165 
166   return true;
167 }
168 
align(uint8_t * in,unsigned alignment)169 static uint8_t *align(uint8_t *in, unsigned alignment) {
170   return reinterpret_cast<uint8_t *>(
171       (reinterpret_cast<uintptr_t>(in) + alignment) &
172       ~static_cast<size_t>(alignment - 1));
173 }
174 
SpeedAEADChunk(const EVP_AEAD * aead,const std::string & name,size_t chunk_len,size_t ad_len)175 static bool SpeedAEADChunk(const EVP_AEAD *aead, const std::string &name,
176                            size_t chunk_len, size_t ad_len) {
177   static const unsigned kAlignment = 16;
178 
179   EVP_AEAD_CTX ctx;
180   const size_t key_len = EVP_AEAD_key_length(aead);
181   const size_t nonce_len = EVP_AEAD_nonce_length(aead);
182   const size_t overhead_len = EVP_AEAD_max_overhead(aead);
183 
184   std::unique_ptr<uint8_t[]> key(new uint8_t[key_len]);
185   memset(key.get(), 0, key_len);
186   std::unique_ptr<uint8_t[]> nonce(new uint8_t[nonce_len]);
187   memset(nonce.get(), 0, nonce_len);
188   std::unique_ptr<uint8_t[]> in_storage(new uint8_t[chunk_len + kAlignment]);
189   std::unique_ptr<uint8_t[]> out_storage(new uint8_t[chunk_len + overhead_len + kAlignment]);
190   std::unique_ptr<uint8_t[]> ad(new uint8_t[ad_len]);
191   memset(ad.get(), 0, ad_len);
192 
193   uint8_t *const in = align(in_storage.get(), kAlignment);
194   memset(in, 0, chunk_len);
195   uint8_t *const out = align(out_storage.get(), kAlignment);
196   memset(out, 0, chunk_len + overhead_len);
197 
198   if (!EVP_AEAD_CTX_init_with_direction(&ctx, aead, key.get(), key_len,
199                                         EVP_AEAD_DEFAULT_TAG_LENGTH,
200                                         evp_aead_seal)) {
201     fprintf(stderr, "Failed to create EVP_AEAD_CTX.\n");
202     ERR_print_errors_fp(stderr);
203     return false;
204   }
205 
206   TimeResults results;
207   if (!TimeFunction(&results, [chunk_len, overhead_len, nonce_len, ad_len, in,
208                                out, &ctx, &nonce, &ad]() -> bool {
209         size_t out_len;
210 
211         return EVP_AEAD_CTX_seal(
212             &ctx, out, &out_len, chunk_len + overhead_len, nonce.get(),
213             nonce_len, in, chunk_len, ad.get(), ad_len);
214       })) {
215     fprintf(stderr, "EVP_AEAD_CTX_seal failed.\n");
216     ERR_print_errors_fp(stderr);
217     return false;
218   }
219 
220   results.PrintWithBytes(name + " seal", chunk_len);
221 
222   EVP_AEAD_CTX_cleanup(&ctx);
223 
224   return true;
225 }
226 
SpeedAEAD(const EVP_AEAD * aead,const std::string & name,size_t ad_len,const std::string & selected)227 static bool SpeedAEAD(const EVP_AEAD *aead, const std::string &name,
228                       size_t ad_len, const std::string &selected) {
229   if (!selected.empty() && name.find(selected) == std::string::npos) {
230     return true;
231   }
232 
233   return SpeedAEADChunk(aead, name + " (16 bytes)", 16, ad_len) &&
234          SpeedAEADChunk(aead, name + " (1350 bytes)", 1350, ad_len) &&
235          SpeedAEADChunk(aead, name + " (8192 bytes)", 8192, ad_len);
236 }
237 
SpeedHashChunk(const EVP_MD * md,const std::string & name,size_t chunk_len)238 static bool SpeedHashChunk(const EVP_MD *md, const std::string &name,
239                            size_t chunk_len) {
240   EVP_MD_CTX *ctx = EVP_MD_CTX_create();
241   uint8_t scratch[8192];
242 
243   if (chunk_len > sizeof(scratch)) {
244     return false;
245   }
246 
247   TimeResults results;
248   if (!TimeFunction(&results, [ctx, md, chunk_len, &scratch]() -> bool {
249         uint8_t digest[EVP_MAX_MD_SIZE];
250         unsigned int md_len;
251 
252         return EVP_DigestInit_ex(ctx, md, NULL /* ENGINE */) &&
253                EVP_DigestUpdate(ctx, scratch, chunk_len) &&
254                EVP_DigestFinal_ex(ctx, digest, &md_len);
255       })) {
256     fprintf(stderr, "EVP_DigestInit_ex failed.\n");
257     ERR_print_errors_fp(stderr);
258     return false;
259   }
260 
261   results.PrintWithBytes(name, chunk_len);
262 
263   EVP_MD_CTX_destroy(ctx);
264 
265   return true;
266 }
SpeedHash(const EVP_MD * md,const std::string & name,const std::string & selected)267 static bool SpeedHash(const EVP_MD *md, const std::string &name,
268                       const std::string &selected) {
269   if (!selected.empty() && name.find(selected) == std::string::npos) {
270     return true;
271   }
272 
273   return SpeedHashChunk(md, name + " (16 bytes)", 16) &&
274          SpeedHashChunk(md, name + " (256 bytes)", 256) &&
275          SpeedHashChunk(md, name + " (8192 bytes)", 8192);
276 }
277 
SpeedRandomChunk(const std::string name,size_t chunk_len)278 static bool SpeedRandomChunk(const std::string name, size_t chunk_len) {
279   uint8_t scratch[8192];
280 
281   if (chunk_len > sizeof(scratch)) {
282     return false;
283   }
284 
285   TimeResults results;
286   if (!TimeFunction(&results, [chunk_len, &scratch]() -> bool {
287         RAND_bytes(scratch, chunk_len);
288         return true;
289       })) {
290     return false;
291   }
292 
293   results.PrintWithBytes(name, chunk_len);
294   return true;
295 }
296 
SpeedRandom(const std::string & selected)297 static bool SpeedRandom(const std::string &selected) {
298   if (!selected.empty() && selected != "RNG") {
299     return true;
300   }
301 
302   return SpeedRandomChunk("RNG (16 bytes)", 16) &&
303          SpeedRandomChunk("RNG (256 bytes)", 256) &&
304          SpeedRandomChunk("RNG (8192 bytes)", 8192);
305 }
306 
SpeedECDHCurve(const std::string & name,int nid,const std::string & selected)307 static bool SpeedECDHCurve(const std::string &name, int nid,
308                            const std::string &selected) {
309   if (!selected.empty() && name.find(selected) == std::string::npos) {
310     return true;
311   }
312 
313   TimeResults results;
314   if (!TimeFunction(&results, [nid]() -> bool {
315         ScopedEC_KEY key(EC_KEY_new_by_curve_name(nid));
316         if (!key ||
317             !EC_KEY_generate_key(key.get())) {
318           return false;
319         }
320         const EC_GROUP *const group = EC_KEY_get0_group(key.get());
321         ScopedEC_POINT point(EC_POINT_new(group));
322         ScopedBN_CTX ctx(BN_CTX_new());
323 
324         ScopedBIGNUM x(BN_new());
325         ScopedBIGNUM y(BN_new());
326 
327         if (!point || !ctx || !x || !y ||
328             !EC_POINT_mul(group, point.get(), NULL,
329                           EC_KEY_get0_public_key(key.get()),
330                           EC_KEY_get0_private_key(key.get()), ctx.get()) ||
331             !EC_POINT_get_affine_coordinates_GFp(group, point.get(), x.get(),
332                                                  y.get(), ctx.get())) {
333           return false;
334         }
335 
336         return true;
337       })) {
338     return false;
339   }
340 
341   results.Print(name);
342   return true;
343 }
344 
SpeedECDSACurve(const std::string & name,int nid,const std::string & selected)345 static bool SpeedECDSACurve(const std::string &name, int nid,
346                             const std::string &selected) {
347   if (!selected.empty() && name.find(selected) == std::string::npos) {
348     return true;
349   }
350 
351   ScopedEC_KEY key(EC_KEY_new_by_curve_name(nid));
352   if (!key ||
353       !EC_KEY_generate_key(key.get())) {
354     return false;
355   }
356 
357   uint8_t signature[256];
358   if (ECDSA_size(key.get()) > sizeof(signature)) {
359     return false;
360   }
361   uint8_t digest[20];
362   memset(digest, 42, sizeof(digest));
363   unsigned sig_len;
364 
365   TimeResults results;
366   if (!TimeFunction(&results, [&key, &signature, &digest, &sig_len]() -> bool {
367         return ECDSA_sign(0, digest, sizeof(digest), signature, &sig_len,
368                           key.get()) == 1;
369       })) {
370     return false;
371   }
372 
373   results.Print(name + " signing");
374 
375   if (!TimeFunction(&results, [&key, &signature, &digest, sig_len]() -> bool {
376         return ECDSA_verify(0, digest, sizeof(digest), signature, sig_len,
377                             key.get()) == 1;
378       })) {
379     return false;
380   }
381 
382   results.Print(name + " verify");
383 
384   return true;
385 }
386 
SpeedECDH(const std::string & selected)387 static bool SpeedECDH(const std::string &selected) {
388   return SpeedECDHCurve("ECDH P-224", NID_secp224r1, selected) &&
389          SpeedECDHCurve("ECDH P-256", NID_X9_62_prime256v1, selected) &&
390          SpeedECDHCurve("ECDH P-384", NID_secp384r1, selected) &&
391          SpeedECDHCurve("ECDH P-521", NID_secp521r1, selected);
392 }
393 
SpeedECDSA(const std::string & selected)394 static bool SpeedECDSA(const std::string &selected) {
395   return SpeedECDSACurve("ECDSA P-224", NID_secp224r1, selected) &&
396          SpeedECDSACurve("ECDSA P-256", NID_X9_62_prime256v1, selected) &&
397          SpeedECDSACurve("ECDSA P-384", NID_secp384r1, selected) &&
398          SpeedECDSACurve("ECDSA P-521", NID_secp521r1, selected);
399 }
400 
Speed25519(const std::string & selected)401 static bool Speed25519(const std::string &selected) {
402   if (!selected.empty() && selected.find("25519") == std::string::npos) {
403     return true;
404   }
405 
406   TimeResults results;
407 
408   uint8_t public_key[32], private_key[64];
409 
410   if (!TimeFunction(&results, [&public_key, &private_key]() -> bool {
411         ED25519_keypair(public_key, private_key);
412         return true;
413       })) {
414     return false;
415   }
416 
417   results.Print("Ed25519 key generation");
418 
419   static const uint8_t kMessage[] = {0, 1, 2, 3, 4, 5};
420   uint8_t signature[64];
421 
422   if (!TimeFunction(&results, [&private_key, &signature]() -> bool {
423         return ED25519_sign(signature, kMessage, sizeof(kMessage),
424                             private_key) == 1;
425       })) {
426     return false;
427   }
428 
429   results.Print("Ed25519 signing");
430 
431   if (!TimeFunction(&results, [&public_key, &signature]() -> bool {
432         return ED25519_verify(kMessage, sizeof(kMessage), signature,
433                               public_key) == 1;
434       })) {
435     fprintf(stderr, "Ed25519 verify failed.\n");
436     return false;
437   }
438 
439   results.Print("Ed25519 verify");
440 
441   if (!TimeFunction(&results, []() -> bool {
442         uint8_t out[32], in[32];
443         memset(in, 0, sizeof(in));
444         X25519_public_from_private(out, in);
445         return true;
446       })) {
447     fprintf(stderr, "Curve25519 base-point multiplication failed.\n");
448     return false;
449   }
450 
451   results.Print("Curve25519 base-point multiplication");
452 
453   if (!TimeFunction(&results, []() -> bool {
454         uint8_t out[32], in1[32], in2[32];
455         memset(in1, 0, sizeof(in1));
456         memset(in2, 0, sizeof(in2));
457         in1[0] = 1;
458         in2[0] = 9;
459         return X25519(out, in1, in2) == 1;
460       })) {
461     fprintf(stderr, "Curve25519 arbitrary point multiplication failed.\n");
462     return false;
463   }
464 
465   results.Print("Curve25519 arbitrary point multiplication");
466 
467   return true;
468 }
469 
Speed(const std::vector<std::string> & args)470 bool Speed(const std::vector<std::string> &args) {
471   std::string selected;
472   if (args.size() > 1) {
473     fprintf(stderr, "Usage: bssl speed [speed test selector, i.e. 'RNG']\n");
474     return false;
475   }
476   if (args.size() > 0) {
477     selected = args[0];
478   }
479 
480   RSA *key = RSA_private_key_from_bytes(kDERRSAPrivate2048,
481                                         kDERRSAPrivate2048Len);
482   if (key == NULL) {
483     fprintf(stderr, "Failed to parse RSA key.\n");
484     ERR_print_errors_fp(stderr);
485     return false;
486   }
487 
488   if (!SpeedRSA("RSA 2048", key, selected)) {
489     return false;
490   }
491 
492   RSA_free(key);
493   key = RSA_private_key_from_bytes(kDERRSAPrivate3Prime2048,
494                                    kDERRSAPrivate3Prime2048Len);
495   if (key == NULL) {
496     fprintf(stderr, "Failed to parse RSA key.\n");
497     ERR_print_errors_fp(stderr);
498     return false;
499   }
500 
501   if (!SpeedRSA("RSA 2048 (3 prime, e=3)", key, selected)) {
502     return false;
503   }
504 
505   RSA_free(key);
506   key = RSA_private_key_from_bytes(kDERRSAPrivate4096,
507                                    kDERRSAPrivate4096Len);
508   if (key == NULL) {
509     fprintf(stderr, "Failed to parse 4096-bit RSA key.\n");
510     ERR_print_errors_fp(stderr);
511     return 1;
512   }
513 
514   if (!SpeedRSA("RSA 4096", key, selected)) {
515     return false;
516   }
517 
518   RSA_free(key);
519 
520   // kTLSADLen is the number of bytes of additional data that TLS passes to
521   // AEADs.
522   static const size_t kTLSADLen = 13;
523   // kLegacyADLen is the number of bytes that TLS passes to the "legacy" AEADs.
524   // These are AEADs that weren't originally defined as AEADs, but which we use
525   // via the AEAD interface. In order for that to work, they have some TLS
526   // knowledge in them and construct a couple of the AD bytes internally.
527   static const size_t kLegacyADLen = kTLSADLen - 2;
528 
529   if (!SpeedAEAD(EVP_aead_aes_128_gcm(), "AES-128-GCM", kTLSADLen, selected) ||
530       !SpeedAEAD(EVP_aead_aes_256_gcm(), "AES-256-GCM", kTLSADLen, selected) ||
531       !SpeedAEAD(EVP_aead_chacha20_poly1305(), "ChaCha20-Poly1305", kTLSADLen,
532                  selected) ||
533       !SpeedAEAD(EVP_aead_chacha20_poly1305_old(), "ChaCha20-Poly1305-Old",
534                  kTLSADLen, selected) ||
535       !SpeedAEAD(EVP_aead_rc4_md5_tls(), "RC4-MD5", kLegacyADLen, selected) ||
536       !SpeedAEAD(EVP_aead_rc4_sha1_tls(), "RC4-SHA1", kLegacyADLen, selected) ||
537       !SpeedAEAD(EVP_aead_des_ede3_cbc_sha1_tls(), "DES-EDE3-CBC-SHA1",
538                  kLegacyADLen, selected) ||
539       !SpeedAEAD(EVP_aead_aes_128_cbc_sha1_tls(), "AES-128-CBC-SHA1",
540                  kLegacyADLen, selected) ||
541       !SpeedAEAD(EVP_aead_aes_256_cbc_sha1_tls(), "AES-256-CBC-SHA1",
542                  kLegacyADLen, selected) ||
543       !SpeedHash(EVP_sha1(), "SHA-1", selected) ||
544       !SpeedHash(EVP_sha256(), "SHA-256", selected) ||
545       !SpeedHash(EVP_sha512(), "SHA-512", selected) ||
546       !SpeedRandom(selected) ||
547       !SpeedECDH(selected) ||
548       !SpeedECDSA(selected) ||
549       !Speed25519(selected)) {
550     return false;
551   }
552 
553   return true;
554 }
555