NSSBenchTest.cpp (18130B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include <string> 7 8 #include "gtest/gtest.h" 9 #include "gtest/MozGTestBench.h" // For MOZ_GTEST_BENCH 10 #include "nss.h" 11 #include "ScopedNSSTypes.h" 12 13 namespace nss_test { 14 15 #define WARMUP (0ull) 16 #define KILOBYTE (1'024ull) 17 #define MEGABYTE (KILOBYTE * KILOBYTE) 18 19 template <CK_MECHANISM_TYPE MECH> 20 class SymmetricKeyTest : public testing::Test { 21 protected: 22 mozilla::UniquePK11SymKey sym_key; 23 24 void SetUp() override { 25 if (!NSS_IsInitialized()) { 26 ASSERT_EQ(NSS_NoDB_Init(nullptr), SECSuccess); 27 } 28 29 mozilla::UniquePK11SlotInfo slot(PK11_GetInternalSlot()); 30 ASSERT_TRUE(!!slot); 31 32 sym_key = mozilla::UniquePK11SymKey( 33 PK11_KeyGen(slot.get(), MECH, nullptr, 16, nullptr)); 34 ASSERT_TRUE(!!sym_key); 35 } 36 }; 37 38 template <CK_MECHANISM_TYPE TYPE> 39 class KeyPairTest : public testing::Test { 40 protected: 41 mozilla::UniqueSECKEYPrivateKey priv_key; 42 mozilla::UniqueSECKEYPublicKey pub_key; 43 44 virtual void* MakeParams() = 0; 45 46 void SetUp() override { 47 if (!NSS_IsInitialized()) { 48 ASSERT_EQ(NSS_NoDB_Init(nullptr), SECSuccess); 49 } 50 51 mozilla::UniquePK11SlotInfo slot(PK11_GetInternalSlot()); 52 ASSERT_TRUE(!!slot); 53 54 priv_key = mozilla::UniqueSECKEYPrivateKey(PK11_GenerateKeyPair( 55 slot.get(), TYPE, MakeParams(), TempPtrToSetter(&pub_key), PR_FALSE, 56 PR_TRUE, nullptr)); 57 58 ASSERT_TRUE(!!priv_key); 59 ASSERT_TRUE(!!pub_key); 60 } 61 }; 62 63 template <SECOidTag TAG> 64 class DigestTest : public testing::Test { 65 protected: 66 mozilla::UniquePK11Context context; 67 68 void SetUp() override { 69 if (!NSS_IsInitialized()) { 70 ASSERT_EQ(NSS_NoDB_Init(nullptr), SECSuccess); 71 } 72 73 context = mozilla::UniquePK11Context(PK11_CreateDigestContext(TAG)); 74 ASSERT_TRUE(!!context); 75 } 76 }; 77 78 class BenchRunner : public testing::WithParamInterface<size_t> { 79 public: 80 virtual void Warmup() = 0; 81 virtual void RunWithSize(size_t size) = 0; 82 83 void Runner() { 84 const size_t& size(GetParam()); 85 86 if (size == 0) { 87 return Warmup(); 88 } 89 90 RunWithSize(size); 91 } 92 93 const char* SuiteName() const { 94 const char* instantiation = 95 testing::UnitTest::GetInstance()->current_test_suite()->name(); 96 const char* suite = strchr(instantiation, '/'); 97 return suite ? suite + 1 : instantiation; 98 } 99 100 const char* TestName() const { 101 const char* test = 102 testing::UnitTest::GetInstance()->current_test_info()->name(); 103 const char* size = strchr(test, '/'); 104 return size ? size + 1 : test; 105 } 106 }; 107 108 class BenchEncryptRunner : public BenchRunner { 109 public: 110 void Warmup() override { 111 std::vector<uint8_t> data(KILOBYTE); 112 // data size + tag size 113 std::vector<uint8_t> output(KILOBYTE + 16); 114 EncryptData(data, output); 115 DecryptData(output, data); 116 } 117 118 void RunWithSize(size_t size) override { 119 std::vector<uint8_t> data(size); 120 std::vector<uint8_t> encrypted(size + 16ull), decrypted(size); 121 122 PK11_GenerateRandom(data.data(), data.size()); 123 124 mozilla::GTestBench( 125 SuiteName(), (std::string(TestName()) + "_encrypt").c_str(), 126 ([this, &data, &encrypted] { this->EncryptData(data, encrypted); })); 127 mozilla::GTestBench(SuiteName(), 128 (std::string(TestName()) + "_decrypt").c_str(), 129 ([this, &encrypted, &decrypted] { 130 this->DecryptData(encrypted, decrypted); 131 })); 132 ASSERT_EQ(data, decrypted); 133 } 134 135 virtual void EncryptData(std::vector<uint8_t>& data, 136 std::vector<uint8_t>& output) = 0; 137 virtual void DecryptData(std::vector<uint8_t>& data, 138 std::vector<uint8_t>& output) = 0; 139 }; 140 141 class BenchDigestRunner : public BenchRunner { 142 public: 143 void Warmup() override { 144 std::vector<uint8_t> data(KILOBYTE); 145 std::vector<uint8_t> output(DigestedSize()); 146 DigestData(data, output); 147 } 148 149 void RunWithSize(size_t size) override { 150 std::vector<uint8_t> data(size); 151 std::vector<uint8_t> encrypted(DigestedSize()); 152 153 PK11_GenerateRandom(data.data(), data.size()); 154 155 mozilla::GTestBench(SuiteName(), TestName(), ([this, &data, &encrypted] { 156 this->DigestData(data, encrypted); 157 })); 158 } 159 160 virtual size_t DigestedSize() = 0; 161 virtual void DigestData(std::vector<uint8_t>& data, 162 std::vector<uint8_t>& output) = 0; 163 }; 164 165 class BenchSignRunner : public BenchRunner { 166 public: 167 void Warmup() override { 168 std::vector<uint8_t> data(64); 169 std::vector<uint8_t> output(SignatureSize()); 170 SignData(data, output); 171 } 172 173 void RunWithSize(size_t size) override { 174 std::vector<uint8_t> data(size); 175 std::vector<uint8_t> sign(SignatureSize()); 176 177 PK11_GenerateRandom(data.data(), data.size()); 178 179 bool verified = false; 180 181 mozilla::GTestBench(SuiteName(), 182 (std::string(TestName()) + "_sign").c_str(), 183 ([this, &data, &sign] { this->SignData(data, sign); })); 184 mozilla::GTestBench(SuiteName(), 185 (std::string(TestName()) + "_verify").c_str(), 186 ([this, &data, &sign, &verified] { 187 verified = this->VerifyData(data, sign); 188 })); 189 ASSERT_TRUE(verified); 190 } 191 192 virtual size_t SignatureSize() const = 0; 193 virtual void SignData(std::vector<uint8_t>& data, 194 std::vector<uint8_t>& output) = 0; 195 virtual bool VerifyData(std::vector<uint8_t>& data, 196 std::vector<uint8_t>& signature) = 0; 197 }; 198 199 class BenchECDHRunner : public BenchRunner { 200 public: 201 void Warmup() override { mozilla::UniquePK11SymKey(this->DeriveKey()); } 202 203 void RunWithSize(size_t size) override { 204 mozilla::UniquePK11SymKey key = nullptr; 205 mozilla::GTestBench(SuiteName(), "derive", 206 ([this, &key] { key.reset(this->DeriveKey()); })); 207 ASSERT_TRUE(!!key); 208 } 209 210 virtual PK11SymKey* DeriveKey() = 0; 211 }; 212 213 // =============================================== 214 // ------------------- Encrypt ------------------- 215 // =============================================== 216 217 template <CK_MECHANISM_TYPE MECH> 218 class BenchEncrypt : public SymmetricKeyTest<MECH>, public BenchEncryptRunner { 219 virtual SECItem MakeParams() = 0; 220 221 public: 222 void SetUp() override { SymmetricKeyTest<MECH>::SetUp(); } 223 224 void EncryptData(std::vector<uint8_t>& data, 225 std::vector<uint8_t>& output) override { 226 SECItem params = MakeParams(); 227 228 unsigned int output_len = 0; 229 SECStatus rv = 230 PK11_Encrypt(this->sym_key.get(), MECH, ¶ms, output.data(), 231 &output_len, output.size(), data.data(), data.size()); 232 233 ASSERT_EQ(rv, SECSuccess); 234 235 output.resize(output_len); 236 } 237 238 void DecryptData(std::vector<uint8_t>& data, 239 std::vector<uint8_t>& output) override { 240 SECItem params = MakeParams(); 241 242 unsigned int output_len = 0; 243 SECStatus rv = 244 PK11_Decrypt(this->sym_key.get(), MECH, ¶ms, output.data(), 245 &output_len, output.size(), data.data(), data.size()); 246 247 ASSERT_EQ(rv, SECSuccess); 248 249 output.resize(output_len); 250 } 251 }; 252 253 class BenchEncrypt_AES_GCM : public BenchEncrypt<CKM_AES_GCM> { 254 std::vector<uint8_t> iv = std::vector<uint8_t>(16, 0); 255 std::vector<uint8_t> aad = std::vector<uint8_t>(0); 256 CK_NSS_GCM_PARAMS gcm_params; 257 258 protected: 259 SECItem MakeParams() override { 260 gcm_params.pIv = iv.data(); 261 gcm_params.ulIvLen = iv.size(); 262 gcm_params.pAAD = aad.data(); 263 gcm_params.ulAADLen = aad.size(); 264 gcm_params.ulTagBits = 128; 265 266 SECItem params = {siBuffer, reinterpret_cast<unsigned char*>(&gcm_params), 267 sizeof(gcm_params)}; 268 return params; 269 } 270 }; 271 272 class BenchEncrypt_CHACHA20_POLY1305 273 : public BenchEncrypt<CKM_CHACHA20_POLY1305> { 274 std::vector<uint8_t> iv = std::vector<uint8_t>(12); 275 std::vector<uint8_t> aad = std::vector<uint8_t>(16); 276 CK_SALSA20_CHACHA20_POLY1305_PARAMS chacha_params; 277 278 protected: 279 SECItem MakeParams() override { 280 chacha_params.pNonce = iv.data(); 281 chacha_params.ulNonceLen = iv.size(); 282 chacha_params.pAAD = aad.data(); 283 chacha_params.ulAADLen = aad.size(); 284 285 SECItem params = {siBuffer, 286 reinterpret_cast<unsigned char*>(&chacha_params), 287 sizeof(chacha_params)}; 288 289 return params; 290 } 291 }; 292 293 class BenchEncrypt_CHACHA20 : public BenchEncrypt<CKM_CHACHA20> { 294 std::vector<uint8_t> iv = std::vector<uint8_t>(12); 295 uint32_t counter; 296 CK_CHACHA20_PARAMS chacha_params; 297 298 protected: 299 SECItem MakeParams() override { 300 chacha_params.pBlockCounter = reinterpret_cast<CK_BYTE_PTR>(&counter); 301 chacha_params.blockCounterBits = 32; 302 chacha_params.pNonce = iv.data(); 303 chacha_params.ulNonceBits = iv.size(); 304 SECItem params = {siBuffer, 305 reinterpret_cast<unsigned char*>(&chacha_params), 306 sizeof(chacha_params)}; 307 308 return params; 309 } 310 }; 311 312 // =============================================== 313 // ------------------- Digest ------------------- 314 // =============================================== 315 316 template <SECOidTag TAG, size_t DIGEST_SIZE> 317 class BenchDigest : public DigestTest<TAG>, public BenchDigestRunner { 318 public: 319 size_t DigestedSize() override { return DIGEST_SIZE; } 320 321 void DigestData(std::vector<uint8_t>& data, 322 std::vector<uint8_t>& output) override { 323 SECStatus rv = PK11_DigestBegin(this->context.get()); 324 ASSERT_EQ(rv, SECSuccess); 325 326 rv = PK11_DigestOp(this->context.get(), data.data(), data.size()); 327 ASSERT_EQ(rv, SECSuccess); 328 329 unsigned int output_len = 0; 330 331 rv = PK11_DigestFinal(this->context.get(), output.data(), &output_len, 332 output.size()); 333 ASSERT_EQ(rv, SECSuccess); 334 335 output.resize(output_len); 336 } 337 }; 338 339 using BenchDigest_SHA256 = BenchDigest<SEC_OID_SHA256, SHA256_LENGTH>; 340 using BenchDigest_SHA512 = BenchDigest<SEC_OID_SHA512, SHA512_LENGTH>; 341 342 template <CK_MECHANISM_TYPE MECH, size_t DIGEST_SIZE> 343 class SymmetricKeySignTest : public SymmetricKeyTest<MECH>, 344 public BenchDigestRunner { 345 public: 346 size_t DigestedSize() override { return DIGEST_SIZE; } 347 348 void DigestData(std::vector<uint8_t>& data, 349 std::vector<uint8_t>& output) override { 350 SECItem hash = {siBuffer, data.data(), 351 static_cast<unsigned int>(data.size())}; 352 SECItem out = {siBuffer, output.data(), 353 static_cast<unsigned int>(output.size())}; 354 355 SECStatus rv = 356 PK11_SignWithSymKey(this->sym_key.get(), MECH, nullptr, &out, &hash); 357 ASSERT_EQ(rv, SECSuccess); 358 } 359 }; 360 361 using BenchSign_SHA256_HMAC = 362 SymmetricKeySignTest<CKM_SHA256_HMAC, SHA256_LENGTH>; 363 using BenchSign_SHA384_HMAC = 364 SymmetricKeySignTest<CKM_SHA384_HMAC, SHA384_LENGTH>; 365 using BenchSign_SHA512_HMAC = 366 SymmetricKeySignTest<CKM_SHA512_HMAC, SHA512_LENGTH>; 367 using BenchSign_SHA3_224_HMAC = 368 SymmetricKeySignTest<CKM_SHA3_224_HMAC, SHA3_224_LENGTH>; 369 using BenchSign_SHA3_256_HMAC = 370 SymmetricKeySignTest<CKM_SHA3_256_HMAC, SHA3_256_LENGTH>; 371 using BenchSign_SHA3_384_HMAC = 372 SymmetricKeySignTest<CKM_SHA3_384_HMAC, SHA3_384_LENGTH>; 373 using BenchSign_SHA3_512_HMAC = 374 SymmetricKeySignTest<CKM_SHA3_512_HMAC, SHA3_512_LENGTH>; 375 376 // =============================================== 377 // --------------- Sign and verify --------------- 378 // =============================================== 379 380 template <CK_MECHANISM_TYPE TYPE> 381 class KeyPairSignTest : public KeyPairTest<TYPE>, public BenchSignRunner { 382 protected: 383 size_t SignatureSize() const override { 384 return PK11_SignatureLen(this->priv_key.get()); 385 } 386 387 void SignData(std::vector<uint8_t>& data, 388 std::vector<uint8_t>& output) override { 389 SECItem hash = {siBuffer, data.data(), 390 static_cast<unsigned int>(data.size())}; 391 SECItem out = {siBuffer, output.data(), 392 static_cast<unsigned int>(output.size())}; 393 394 SECStatus rv = PK11_Sign(this->priv_key.get(), &out, &hash); 395 ASSERT_EQ(rv, SECSuccess); 396 } 397 398 bool VerifyData(std::vector<uint8_t>& data, 399 std::vector<uint8_t>& signature) override { 400 SECItem dat = {siBuffer, data.data(), 401 static_cast<unsigned int>(data.size())}; 402 SECItem sig = {siBuffer, signature.data(), 403 static_cast<unsigned int>(signature.size())}; 404 405 SECStatus rv = PK11_Verify(this->pub_key.get(), &sig, &dat, NULL); 406 return rv == SECSuccess; 407 } 408 }; 409 410 template <SECOidTag CURVE> 411 class BenchSignEC : public KeyPairSignTest<CKM_EC_KEY_PAIR_GEN> { 412 SECKEYECParams params; 413 std::unique_ptr<uint8_t[]> extra; 414 415 public: 416 void* MakeParams() { 417 /* For the case of ED curve_oid contains a EdDSA OID. */ 418 SECOidData* curve = SECOID_FindOIDByTag(CURVE); 419 420 size_t plen = curve->oid.len + 2; 421 extra.reset(new uint8_t[plen]); 422 extra[0] = SEC_ASN1_OBJECT_ID; 423 extra[1] = static_cast<uint8_t>(curve->oid.len); 424 memcpy(&extra[2], curve->oid.data, curve->oid.len); 425 426 params = {siBuffer, extra.get(), static_cast<unsigned int>(plen)}; 427 428 return ¶ms; 429 } 430 }; 431 432 using BenchSign_P256 = BenchSignEC<SEC_OID_SECG_EC_SECP256R1>; 433 using BenchSign_P384 = BenchSignEC<SEC_OID_SECG_EC_SECP384R1>; 434 using BenchSign_P521 = BenchSignEC<SEC_OID_SECG_EC_SECP521R1>; 435 436 template <size_t KEY_SIZE> 437 class BenchSignRSA : public KeyPairSignTest<CKM_RSA_PKCS_KEY_PAIR_GEN> { 438 PK11RSAGenParams rsa_params; 439 440 public: 441 void* MakeParams() { 442 rsa_params.keySizeInBits = KEY_SIZE; 443 rsa_params.pe = 0x10001; 444 445 return &rsa_params; 446 } 447 }; 448 449 using BenchSign_RSA1024 = BenchSignRSA<1024>; 450 using BenchSign_RSA2048 = BenchSignRSA<2048>; 451 using BenchSign_RSA4096 = BenchSignRSA<4096>; 452 453 // =============================================== 454 // --------- Diffie-Hellman key exchange --------- 455 // =============================================== 456 457 template <SECOidTag CURVE, CK_MECHANISM_TYPE TARGET> 458 class BenchECDH : public KeyPairTest<CKM_EC_KEY_PAIR_GEN>, 459 public BenchECDHRunner { 460 SECKEYECParams params; 461 std::unique_ptr<uint8_t[]> extra; 462 463 public: 464 void* MakeParams() override { 465 /* For the case of ED curve_oid contains a EdDSA OID. */ 466 SECOidData* curve = SECOID_FindOIDByTag(CURVE); 467 468 size_t plen = curve->oid.len + 2; 469 extra.reset(new uint8_t[plen]); 470 extra[0] = SEC_ASN1_OBJECT_ID; 471 extra[1] = static_cast<uint8_t>(curve->oid.len); 472 memcpy(&extra[2], curve->oid.data, curve->oid.len); 473 474 params = {siBuffer, extra.get(), static_cast<unsigned int>(plen)}; 475 476 return ¶ms; 477 } 478 479 PK11SymKey* DeriveKey() override { 480 return PK11_PubDeriveWithKDF( 481 this->priv_key.get(), this->pub_key.get(), false, nullptr, nullptr, 482 CKM_ECDH1_DERIVE, TARGET, CKA_DERIVE, 0, CKD_NULL, nullptr, nullptr); 483 } 484 }; 485 486 using BenchECDH_P256_SHA256 = 487 BenchECDH<SEC_OID_SECG_EC_SECP256R1, CKM_SHA256_HMAC>; 488 using BenchECDH_P384_SHA256 = 489 BenchECDH<SEC_OID_SECG_EC_SECP384R1, CKM_SHA256_HMAC>; 490 using BenchECDH_P521_SHA256 = 491 BenchECDH<SEC_OID_SECG_EC_SECP521R1, CKM_SHA256_HMAC>; 492 493 // =============================================== 494 // ------------------ Harnesses ------------------ 495 // =============================================== 496 497 std::string MakeTestName(const testing::TestParamInfo<size_t>& info) { 498 if (info.param == 0) { 499 return "warmup"; 500 } 501 502 if (info.param < KILOBYTE) { 503 return std::to_string(info.param); 504 } 505 506 if (info.param < MEGABYTE) { 507 return std::to_string(info.param / KILOBYTE) + "k"; 508 } 509 510 return std::to_string(info.param / MEGABYTE) + "m"; 511 } 512 513 #define NSS_BENCH_P(suite, params) \ 514 TEST_P(suite, Run) { Runner(); } \ 515 INSTANTIATE_TEST_SUITE_P(psm_##suite, suite, testing::ValuesIn(params), \ 516 MakeTestName); 517 518 static const size_t BENCH_ENCRYPT_SIZE[] = { 519 WARMUP, KILOBYTE, 16 * KILOBYTE, MEGABYTE, 16 * MEGABYTE, 128 * MEGABYTE}; 520 // These fail because the treeherder instances run out of memory 521 // 522 // 512 * MEGABYTE, GIGABYTE - MEGABYTE, GIGABYTE, 523 // 524 // These fail because some of the SHA3 HMAC suites time out 525 // 526 // 4 * GIGABYTE - MEGABYTE, 4 * GIGABYTE - 32, 527 // 528 // These fail because the size parameters in PK11_Encrypt/PK11_Decrypt 529 // are unsigned int, i.e. 32bits. So any data size + tag size > 4GiB will 530 // fail 531 // 532 // 4 * GIGABYTE - 1, 4 * GIGABYTE, 4 * GIGABYTE + MEGABYTE, 16 * GIGABYTE, 533 534 static const size_t BENCH_DH_SIZE[] = {WARMUP, 1}; 535 static const size_t BENCH_RSA1024_SIZE[] = {WARMUP, 32, 64, 117}; 536 static const size_t BENCH_RSA2048_SIZE[] = {WARMUP, 32, 64, 128, 245}; 537 static const size_t BENCH_RSA4096_SIZE[] = {WARMUP, 32, 64, 128, 256, 501}; 538 539 NSS_BENCH_P(BenchEncrypt_AES_GCM, BENCH_ENCRYPT_SIZE) 540 NSS_BENCH_P(BenchEncrypt_CHACHA20, BENCH_ENCRYPT_SIZE) 541 NSS_BENCH_P(BenchEncrypt_CHACHA20_POLY1305, BENCH_ENCRYPT_SIZE) 542 543 NSS_BENCH_P(BenchDigest_SHA256, BENCH_ENCRYPT_SIZE) 544 NSS_BENCH_P(BenchDigest_SHA512, BENCH_ENCRYPT_SIZE) 545 NSS_BENCH_P(BenchSign_SHA256_HMAC, BENCH_ENCRYPT_SIZE) 546 NSS_BENCH_P(BenchSign_SHA384_HMAC, BENCH_ENCRYPT_SIZE) 547 NSS_BENCH_P(BenchSign_SHA512_HMAC, BENCH_ENCRYPT_SIZE) 548 NSS_BENCH_P(BenchSign_SHA3_224_HMAC, BENCH_ENCRYPT_SIZE) 549 NSS_BENCH_P(BenchSign_SHA3_256_HMAC, BENCH_ENCRYPT_SIZE) 550 NSS_BENCH_P(BenchSign_SHA3_384_HMAC, BENCH_ENCRYPT_SIZE) 551 NSS_BENCH_P(BenchSign_SHA3_512_HMAC, BENCH_ENCRYPT_SIZE) 552 553 NSS_BENCH_P(BenchSign_P256, BENCH_ENCRYPT_SIZE) 554 NSS_BENCH_P(BenchSign_P384, BENCH_ENCRYPT_SIZE) 555 NSS_BENCH_P(BenchSign_P521, BENCH_ENCRYPT_SIZE) 556 557 NSS_BENCH_P(BenchECDH_P256_SHA256, BENCH_DH_SIZE) 558 NSS_BENCH_P(BenchECDH_P384_SHA256, BENCH_DH_SIZE) 559 NSS_BENCH_P(BenchECDH_P521_SHA256, BENCH_DH_SIZE) 560 561 NSS_BENCH_P(BenchSign_RSA1024, BENCH_RSA1024_SIZE) 562 NSS_BENCH_P(BenchSign_RSA2048, BENCH_RSA2048_SIZE) 563 NSS_BENCH_P(BenchSign_RSA4096, BENCH_RSA4096_SIZE) 564 565 } // namespace nss_test