blob: 307b0b9745b5712ea9da339b3ce8a56b5c265e87 [file] [log] [blame]
Adam Langleyd9e397b2015-01-22 14:27:53 -08001/* 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>
Adam Langleyd9e397b2015-01-22 14:27:53 -080022
23#include <openssl/aead.h>
Adam Langleyd9e397b2015-01-22 14:27:53 -080024#include <openssl/digest.h>
Adam Langleye9ada862015-05-11 17:20:37 -070025#include <openssl/err.h>
Adam Langleyd9e397b2015-01-22 14:27:53 -080026#include <openssl/obj.h>
Adam Langleye9ada862015-05-11 17:20:37 -070027#include <openssl/rand.h>
Adam Langleyd9e397b2015-01-22 14:27:53 -080028#include <openssl/rsa.h>
29
30#if defined(OPENSSL_WINDOWS)
31#pragma warning(push, 3)
Kenny Rootac6c5372015-03-04 12:52:47 -080032#include <windows.h>
Adam Langleyd9e397b2015-01-22 14:27:53 -080033#pragma warning(pop)
34#elif defined(OPENSSL_APPLE)
35#include <sys/time.h>
36#endif
37
Adam Langleye9ada862015-05-11 17:20:37 -070038#include "../crypto/test/scoped_types.h"
Kenny Rootb8494592015-09-25 02:29:14 +000039#include "internal.h"
Adam Langleye9ada862015-05-11 17:20:37 -070040
Adam Langleyd9e397b2015-01-22 14:27:53 -080041
Adam Langleyd9e397b2015-01-22 14:27:53 -080042// TimeResults represents the results of benchmarking a function.
43struct TimeResults {
44 // num_calls is the number of function calls done in the time period.
45 unsigned num_calls;
46 // us is the number of microseconds that elapsed in the time period.
47 unsigned us;
48
49 void Print(const std::string &description) {
50 printf("Did %u %s operations in %uus (%.1f ops/sec)\n", num_calls,
51 description.c_str(), us,
52 (static_cast<double>(num_calls) / us) * 1000000);
53 }
54
55 void PrintWithBytes(const std::string &description, size_t bytes_per_call) {
56 printf("Did %u %s operations in %uus (%.1f ops/sec): %.1f MB/s\n",
57 num_calls, description.c_str(), us,
58 (static_cast<double>(num_calls) / us) * 1000000,
59 static_cast<double>(bytes_per_call * num_calls) / us);
60 }
61};
62
63#if defined(OPENSSL_WINDOWS)
64static uint64_t time_now() { return GetTickCount64() * 1000; }
65#elif defined(OPENSSL_APPLE)
66static uint64_t time_now() {
67 struct timeval tv;
68 uint64_t ret;
69
70 gettimeofday(&tv, NULL);
71 ret = tv.tv_sec;
72 ret *= 1000000;
73 ret += tv.tv_usec;
74 return ret;
75}
76#else
77static uint64_t time_now() {
78 struct timespec ts;
79 clock_gettime(CLOCK_MONOTONIC, &ts);
80
81 uint64_t ret = ts.tv_sec;
82 ret *= 1000000;
83 ret += ts.tv_nsec / 1000;
84 return ret;
85}
86#endif
87
88static bool TimeFunction(TimeResults *results, std::function<bool()> func) {
89 // kTotalMS is the total amount of time that we'll aim to measure a function
90 // for.
Adam Langleye9ada862015-05-11 17:20:37 -070091 static const uint64_t kTotalUS = 1000000;
Adam Langleyd9e397b2015-01-22 14:27:53 -080092 uint64_t start = time_now(), now, delta;
93 unsigned done = 0, iterations_between_time_checks;
94
95 if (!func()) {
96 return false;
97 }
98 now = time_now();
99 delta = now - start;
100 if (delta == 0) {
101 iterations_between_time_checks = 250;
102 } else {
103 // Aim for about 100ms between time checks.
104 iterations_between_time_checks =
105 static_cast<double>(100000) / static_cast<double>(delta);
106 if (iterations_between_time_checks > 1000) {
107 iterations_between_time_checks = 1000;
108 } else if (iterations_between_time_checks < 1) {
109 iterations_between_time_checks = 1;
110 }
111 }
112
113 for (;;) {
114 for (unsigned i = 0; i < iterations_between_time_checks; i++) {
115 if (!func()) {
116 return false;
117 }
118 done++;
119 }
120
121 now = time_now();
122 if (now - start > kTotalUS) {
123 break;
124 }
125 }
126
127 results->us = now - start;
128 results->num_calls = done;
129 return true;
130}
131
Adam Langleye9ada862015-05-11 17:20:37 -0700132static bool SpeedRSA(const std::string &key_name, RSA *key,
133 const std::string &selected) {
134 if (!selected.empty() && key_name.find(selected) == std::string::npos) {
135 return true;
136 }
Adam Langleyd9e397b2015-01-22 14:27:53 -0800137
138 std::unique_ptr<uint8_t[]> sig(new uint8_t[RSA_size(key)]);
139 const uint8_t fake_sha256_hash[32] = {0};
140 unsigned sig_len;
141
Adam Langleye9ada862015-05-11 17:20:37 -0700142 TimeResults results;
Adam Langleyd9e397b2015-01-22 14:27:53 -0800143 if (!TimeFunction(&results,
144 [key, &sig, &fake_sha256_hash, &sig_len]() -> bool {
145 return RSA_sign(NID_sha256, fake_sha256_hash, sizeof(fake_sha256_hash),
146 sig.get(), &sig_len, key);
147 })) {
148 fprintf(stderr, "RSA_sign failed.\n");
Adam Langleye9ada862015-05-11 17:20:37 -0700149 ERR_print_errors_fp(stderr);
Adam Langleyd9e397b2015-01-22 14:27:53 -0800150 return false;
151 }
152 results.Print(key_name + " signing");
153
154 if (!TimeFunction(&results,
155 [key, &fake_sha256_hash, &sig, sig_len]() -> bool {
156 return RSA_verify(NID_sha256, fake_sha256_hash,
157 sizeof(fake_sha256_hash), sig.get(), sig_len, key);
158 })) {
159 fprintf(stderr, "RSA_verify failed.\n");
Adam Langleye9ada862015-05-11 17:20:37 -0700160 ERR_print_errors_fp(stderr);
Adam Langleyd9e397b2015-01-22 14:27:53 -0800161 return false;
162 }
163 results.Print(key_name + " verify");
164
165 return true;
166}
167
Adam Langleyd9e397b2015-01-22 14:27:53 -0800168static uint8_t *align(uint8_t *in, unsigned alignment) {
169 return reinterpret_cast<uint8_t *>(
Adam Langleye9ada862015-05-11 17:20:37 -0700170 (reinterpret_cast<uintptr_t>(in) + alignment) &
171 ~static_cast<size_t>(alignment - 1));
Adam Langleyd9e397b2015-01-22 14:27:53 -0800172}
173
174static bool SpeedAEADChunk(const EVP_AEAD *aead, const std::string &name,
175 size_t chunk_len, size_t ad_len) {
176 static const unsigned kAlignment = 16;
177
178 EVP_AEAD_CTX ctx;
179 const size_t key_len = EVP_AEAD_key_length(aead);
180 const size_t nonce_len = EVP_AEAD_nonce_length(aead);
181 const size_t overhead_len = EVP_AEAD_max_overhead(aead);
182
183 std::unique_ptr<uint8_t[]> key(new uint8_t[key_len]);
184 memset(key.get(), 0, key_len);
185 std::unique_ptr<uint8_t[]> nonce(new uint8_t[nonce_len]);
186 memset(nonce.get(), 0, nonce_len);
Adam Langleye9ada862015-05-11 17:20:37 -0700187 std::unique_ptr<uint8_t[]> in_storage(new uint8_t[chunk_len + kAlignment]);
188 std::unique_ptr<uint8_t[]> out_storage(new uint8_t[chunk_len + overhead_len + kAlignment]);
Adam Langleyd9e397b2015-01-22 14:27:53 -0800189 std::unique_ptr<uint8_t[]> ad(new uint8_t[ad_len]);
190 memset(ad.get(), 0, ad_len);
191
192 uint8_t *const in = align(in_storage.get(), kAlignment);
193 memset(in, 0, chunk_len);
194 uint8_t *const out = align(out_storage.get(), kAlignment);
195 memset(out, 0, chunk_len + overhead_len);
196
Adam Langleye9ada862015-05-11 17:20:37 -0700197 if (!EVP_AEAD_CTX_init_with_direction(&ctx, aead, key.get(), key_len,
198 EVP_AEAD_DEFAULT_TAG_LENGTH,
199 evp_aead_seal)) {
Adam Langleyd9e397b2015-01-22 14:27:53 -0800200 fprintf(stderr, "Failed to create EVP_AEAD_CTX.\n");
Adam Langleye9ada862015-05-11 17:20:37 -0700201 ERR_print_errors_fp(stderr);
Adam Langleyd9e397b2015-01-22 14:27:53 -0800202 return false;
203 }
204
205 TimeResults results;
206 if (!TimeFunction(&results, [chunk_len, overhead_len, nonce_len, ad_len, in,
207 out, &ctx, &nonce, &ad]() -> bool {
208 size_t out_len;
209
210 return EVP_AEAD_CTX_seal(
211 &ctx, out, &out_len, chunk_len + overhead_len, nonce.get(),
212 nonce_len, in, chunk_len, ad.get(), ad_len);
213 })) {
214 fprintf(stderr, "EVP_AEAD_CTX_seal failed.\n");
Adam Langleye9ada862015-05-11 17:20:37 -0700215 ERR_print_errors_fp(stderr);
Adam Langleyd9e397b2015-01-22 14:27:53 -0800216 return false;
217 }
218
219 results.PrintWithBytes(name + " seal", chunk_len);
220
221 EVP_AEAD_CTX_cleanup(&ctx);
222
223 return true;
224}
225
226static bool SpeedAEAD(const EVP_AEAD *aead, const std::string &name,
Adam Langleye9ada862015-05-11 17:20:37 -0700227 size_t ad_len, const std::string &selected) {
228 if (!selected.empty() && name.find(selected) == std::string::npos) {
229 return true;
230 }
231
Adam Langleyd9e397b2015-01-22 14:27:53 -0800232 return SpeedAEADChunk(aead, name + " (16 bytes)", 16, ad_len) &&
233 SpeedAEADChunk(aead, name + " (1350 bytes)", 1350, ad_len) &&
234 SpeedAEADChunk(aead, name + " (8192 bytes)", 8192, ad_len);
235}
236
237static bool SpeedHashChunk(const EVP_MD *md, const std::string &name,
238 size_t chunk_len) {
239 EVP_MD_CTX *ctx = EVP_MD_CTX_create();
240 uint8_t scratch[8192];
241
242 if (chunk_len > sizeof(scratch)) {
243 return false;
244 }
245
246 TimeResults results;
247 if (!TimeFunction(&results, [ctx, md, chunk_len, &scratch]() -> bool {
248 uint8_t digest[EVP_MAX_MD_SIZE];
249 unsigned int md_len;
250
251 return EVP_DigestInit_ex(ctx, md, NULL /* ENGINE */) &&
252 EVP_DigestUpdate(ctx, scratch, chunk_len) &&
253 EVP_DigestFinal_ex(ctx, digest, &md_len);
254 })) {
255 fprintf(stderr, "EVP_DigestInit_ex failed.\n");
Adam Langleye9ada862015-05-11 17:20:37 -0700256 ERR_print_errors_fp(stderr);
Adam Langleyd9e397b2015-01-22 14:27:53 -0800257 return false;
258 }
259
260 results.PrintWithBytes(name, chunk_len);
261
262 EVP_MD_CTX_destroy(ctx);
263
264 return true;
265}
Adam Langleye9ada862015-05-11 17:20:37 -0700266static bool SpeedHash(const EVP_MD *md, const std::string &name,
267 const std::string &selected) {
268 if (!selected.empty() && name.find(selected) == std::string::npos) {
269 return true;
270 }
271
Adam Langleyd9e397b2015-01-22 14:27:53 -0800272 return SpeedHashChunk(md, name + " (16 bytes)", 16) &&
273 SpeedHashChunk(md, name + " (256 bytes)", 256) &&
274 SpeedHashChunk(md, name + " (8192 bytes)", 8192);
275}
276
Adam Langleye9ada862015-05-11 17:20:37 -0700277static bool SpeedRandomChunk(const std::string name, size_t chunk_len) {
278 uint8_t scratch[8192];
Adam Langleyd9e397b2015-01-22 14:27:53 -0800279
Adam Langleye9ada862015-05-11 17:20:37 -0700280 if (chunk_len > sizeof(scratch)) {
Adam Langleyd9e397b2015-01-22 14:27:53 -0800281 return false;
282 }
283
Adam Langleye9ada862015-05-11 17:20:37 -0700284 TimeResults results;
285 if (!TimeFunction(&results, [chunk_len, &scratch]() -> bool {
286 RAND_bytes(scratch, chunk_len);
287 return true;
288 })) {
289 return false;
290 }
291
292 results.PrintWithBytes(name, chunk_len);
293 return true;
294}
295
296static bool SpeedRandom(const std::string &selected) {
297 if (!selected.empty() && selected != "RNG") {
298 return true;
299 }
300
301 return SpeedRandomChunk("RNG (16 bytes)", 16) &&
302 SpeedRandomChunk("RNG (256 bytes)", 256) &&
303 SpeedRandomChunk("RNG (8192 bytes)", 8192);
304}
305
306static bool SpeedECDHCurve(const std::string &name, int nid,
307 const std::string &selected) {
308 if (!selected.empty() && name.find(selected) == std::string::npos) {
309 return true;
310 }
311
312 TimeResults results;
313 if (!TimeFunction(&results, [nid]() -> bool {
314 ScopedEC_KEY key(EC_KEY_new_by_curve_name(nid));
315 if (!key ||
316 !EC_KEY_generate_key(key.get())) {
317 return false;
318 }
319 const EC_GROUP *const group = EC_KEY_get0_group(key.get());
320 ScopedEC_POINT point(EC_POINT_new(group));
321 ScopedBN_CTX ctx(BN_CTX_new());
322
323 ScopedBIGNUM x(BN_new());
324 ScopedBIGNUM y(BN_new());
325
326 if (!point || !ctx || !x || !y ||
327 !EC_POINT_mul(group, point.get(), NULL,
328 EC_KEY_get0_public_key(key.get()),
329 EC_KEY_get0_private_key(key.get()), ctx.get()) ||
330 !EC_POINT_get_affine_coordinates_GFp(group, point.get(), x.get(),
331 y.get(), ctx.get())) {
332 return false;
333 }
334
335 return true;
336 })) {
337 return false;
338 }
339
340 results.Print(name);
341 return true;
342}
343
344static bool SpeedECDSACurve(const std::string &name, int nid,
345 const std::string &selected) {
346 if (!selected.empty() && name.find(selected) == std::string::npos) {
347 return true;
348 }
349
350 ScopedEC_KEY key(EC_KEY_new_by_curve_name(nid));
351 if (!key ||
352 !EC_KEY_generate_key(key.get())) {
353 return false;
354 }
355
356 uint8_t signature[256];
357 if (ECDSA_size(key.get()) > sizeof(signature)) {
358 return false;
359 }
360 uint8_t digest[20];
361 memset(digest, 42, sizeof(digest));
362 unsigned sig_len;
363
364 TimeResults results;
365 if (!TimeFunction(&results, [&key, &signature, &digest, &sig_len]() -> bool {
366 return ECDSA_sign(0, digest, sizeof(digest), signature, &sig_len,
367 key.get()) == 1;
368 })) {
369 return false;
370 }
371
372 results.Print(name + " signing");
373
374 if (!TimeFunction(&results, [&key, &signature, &digest, sig_len]() -> bool {
375 return ECDSA_verify(0, digest, sizeof(digest), signature, sig_len,
376 key.get()) == 1;
377 })) {
378 return false;
379 }
380
381 results.Print(name + " verify");
382
383 return true;
384}
385
386static bool SpeedECDH(const std::string &selected) {
387 return SpeedECDHCurve("ECDH P-224", NID_secp224r1, selected) &&
388 SpeedECDHCurve("ECDH P-256", NID_X9_62_prime256v1, selected) &&
389 SpeedECDHCurve("ECDH P-384", NID_secp384r1, selected) &&
390 SpeedECDHCurve("ECDH P-521", NID_secp521r1, selected);
391}
392
393static bool SpeedECDSA(const std::string &selected) {
394 return SpeedECDSACurve("ECDSA P-224", NID_secp224r1, selected) &&
395 SpeedECDSACurve("ECDSA P-256", NID_X9_62_prime256v1, selected) &&
396 SpeedECDSACurve("ECDSA P-384", NID_secp384r1, selected) &&
397 SpeedECDSACurve("ECDSA P-521", NID_secp521r1, selected);
398}
399
400bool Speed(const std::vector<std::string> &args) {
401 std::string selected;
402 if (args.size() > 1) {
403 fprintf(stderr, "Usage: bssl speed [speed test selector, i.e. 'RNG']\n");
404 return false;
405 }
406 if (args.size() > 0) {
407 selected = args[0];
408 }
409
Kenny Rootb8494592015-09-25 02:29:14 +0000410 RSA *key = RSA_private_key_from_bytes(kDERRSAPrivate2048,
411 kDERRSAPrivate2048Len);
412 if (key == NULL) {
Adam Langleye9ada862015-05-11 17:20:37 -0700413 fprintf(stderr, "Failed to parse RSA key.\n");
414 ERR_print_errors_fp(stderr);
415 return false;
416 }
417
418 if (!SpeedRSA("RSA 2048", key, selected)) {
Adam Langleyd9e397b2015-01-22 14:27:53 -0800419 return false;
420 }
421
422 RSA_free(key);
Kenny Rootb8494592015-09-25 02:29:14 +0000423 key = RSA_private_key_from_bytes(kDERRSAPrivate3Prime2048,
424 kDERRSAPrivate3Prime2048Len);
425 if (key == NULL) {
426 fprintf(stderr, "Failed to parse RSA key.\n");
427 ERR_print_errors_fp(stderr);
428 return false;
429 }
Adam Langleyd9e397b2015-01-22 14:27:53 -0800430
Kenny Rootb8494592015-09-25 02:29:14 +0000431 if (!SpeedRSA("RSA 2048 (3 prime, e=3)", key, selected)) {
432 return false;
433 }
434
435 RSA_free(key);
436 key = RSA_private_key_from_bytes(kDERRSAPrivate4096,
437 kDERRSAPrivate4096Len);
438 if (key == NULL) {
Adam Langleyd9e397b2015-01-22 14:27:53 -0800439 fprintf(stderr, "Failed to parse 4096-bit RSA key.\n");
Adam Langleye9ada862015-05-11 17:20:37 -0700440 ERR_print_errors_fp(stderr);
Adam Langleyd9e397b2015-01-22 14:27:53 -0800441 return 1;
442 }
443
Adam Langleye9ada862015-05-11 17:20:37 -0700444 if (!SpeedRSA("RSA 4096", key, selected)) {
Adam Langleyd9e397b2015-01-22 14:27:53 -0800445 return false;
446 }
447
448 RSA_free(key);
449
450 // kTLSADLen is the number of bytes of additional data that TLS passes to
451 // AEADs.
452 static const size_t kTLSADLen = 13;
453 // kLegacyADLen is the number of bytes that TLS passes to the "legacy" AEADs.
454 // These are AEADs that weren't originally defined as AEADs, but which we use
455 // via the AEAD interface. In order for that to work, they have some TLS
456 // knowledge in them and construct a couple of the AD bytes internally.
457 static const size_t kLegacyADLen = kTLSADLen - 2;
458
Adam Langleye9ada862015-05-11 17:20:37 -0700459 if (!SpeedAEAD(EVP_aead_aes_128_gcm(), "AES-128-GCM", kTLSADLen, selected) ||
460 !SpeedAEAD(EVP_aead_aes_256_gcm(), "AES-256-GCM", kTLSADLen, selected) ||
461 !SpeedAEAD(EVP_aead_chacha20_poly1305(), "ChaCha20-Poly1305", kTLSADLen,
462 selected) ||
463 !SpeedAEAD(EVP_aead_rc4_md5_tls(), "RC4-MD5", kLegacyADLen, selected) ||
464 !SpeedAEAD(EVP_aead_aes_128_cbc_sha1_tls(), "AES-128-CBC-SHA1",
465 kLegacyADLen, selected) ||
466 !SpeedAEAD(EVP_aead_aes_256_cbc_sha1_tls(), "AES-256-CBC-SHA1",
467 kLegacyADLen, selected) ||
468 !SpeedHash(EVP_sha1(), "SHA-1", selected) ||
469 !SpeedHash(EVP_sha256(), "SHA-256", selected) ||
470 !SpeedHash(EVP_sha512(), "SHA-512", selected) ||
471 !SpeedRandom(selected) ||
472 !SpeedECDH(selected) ||
473 !SpeedECDSA(selected)) {
Adam Langleyd9e397b2015-01-22 14:27:53 -0800474 return false;
475 }
476
Adam Langleye9ada862015-05-11 17:20:37 -0700477 return true;
Adam Langleyd9e397b2015-01-22 14:27:53 -0800478}