/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include #include "gtest/gtest.h" #include "gtest/MozGTestBench.h" // For MOZ_GTEST_BENCH #include "nss.h" #include "ScopedNSSTypes.h" namespace nss_test { #define WARMUP (0ull) #define KILOBYTE (1'024ull) #define MEGABYTE (KILOBYTE * KILOBYTE) template class SymmetricKeyTest : public testing::Test { protected: mozilla::UniquePK11SymKey sym_key; void SetUp() override { if (!NSS_IsInitialized()) { ASSERT_EQ(NSS_NoDB_Init(nullptr), SECSuccess); } mozilla::UniquePK11SlotInfo slot(PK11_GetInternalSlot()); ASSERT_TRUE(!!slot); sym_key = mozilla::UniquePK11SymKey( PK11_KeyGen(slot.get(), MECH, nullptr, 16, nullptr)); ASSERT_TRUE(!!sym_key); } }; template class KeyPairTest : public testing::Test { protected: mozilla::UniqueSECKEYPrivateKey priv_key; mozilla::UniqueSECKEYPublicKey pub_key; virtual void* MakeParams() = 0; void SetUp() override { if (!NSS_IsInitialized()) { ASSERT_EQ(NSS_NoDB_Init(nullptr), SECSuccess); } mozilla::UniquePK11SlotInfo slot(PK11_GetInternalSlot()); ASSERT_TRUE(!!slot); priv_key = mozilla::UniqueSECKEYPrivateKey(PK11_GenerateKeyPair( slot.get(), TYPE, MakeParams(), TempPtrToSetter(&pub_key), PR_FALSE, PR_TRUE, nullptr)); ASSERT_TRUE(!!priv_key); ASSERT_TRUE(!!pub_key); } }; template class DigestTest : public testing::Test { protected: mozilla::UniquePK11Context context; void SetUp() override { if (!NSS_IsInitialized()) { ASSERT_EQ(NSS_NoDB_Init(nullptr), SECSuccess); } context = mozilla::UniquePK11Context(PK11_CreateDigestContext(TAG)); ASSERT_TRUE(!!context); } }; class BenchRunner : public testing::WithParamInterface { public: virtual void Warmup() = 0; virtual void RunWithSize(size_t size) = 0; void Runner() { const size_t& size(GetParam()); if (size == 0) { return Warmup(); } RunWithSize(size); } const char* SuiteName() const { const char* instantiation = testing::UnitTest::GetInstance()->current_test_suite()->name(); const char* suite = strchr(instantiation, '/'); return suite ? suite + 1 : instantiation; } const char* TestName() const { const char* test = testing::UnitTest::GetInstance()->current_test_info()->name(); const char* size = strchr(test, '/'); return size ? size + 1 : test; } }; class BenchEncryptRunner : public BenchRunner { public: void Warmup() override { std::vector data(KILOBYTE); // data size + tag size std::vector output(KILOBYTE + 16); EncryptData(data, output); DecryptData(output, data); } void RunWithSize(size_t size) override { std::vector data(size); std::vector encrypted(size + 16ull), decrypted(size); PK11_GenerateRandom(data.data(), data.size()); mozilla::GTestBench( SuiteName(), (std::string(TestName()) + "_encrypt").c_str(), ([this, &data, &encrypted] { this->EncryptData(data, encrypted); })); mozilla::GTestBench(SuiteName(), (std::string(TestName()) + "_decrypt").c_str(), ([this, &encrypted, &decrypted] { this->DecryptData(encrypted, decrypted); })); ASSERT_EQ(data, decrypted); } virtual void EncryptData(std::vector& data, std::vector& output) = 0; virtual void DecryptData(std::vector& data, std::vector& output) = 0; }; class BenchDigestRunner : public BenchRunner { public: void Warmup() override { std::vector data(KILOBYTE); std::vector output(DigestedSize()); DigestData(data, output); } void RunWithSize(size_t size) override { std::vector data(size); std::vector encrypted(DigestedSize()); PK11_GenerateRandom(data.data(), data.size()); mozilla::GTestBench(SuiteName(), TestName(), ([this, &data, &encrypted] { this->DigestData(data, encrypted); })); } virtual size_t DigestedSize() = 0; virtual void DigestData(std::vector& data, std::vector& output) = 0; }; class BenchSignRunner : public BenchRunner { public: void Warmup() override { std::vector data(64); std::vector output(SignatureSize()); SignData(data, output); } void RunWithSize(size_t size) override { std::vector data(size); std::vector sign(SignatureSize()); PK11_GenerateRandom(data.data(), data.size()); bool verified = false; mozilla::GTestBench(SuiteName(), (std::string(TestName()) + "_sign").c_str(), ([this, &data, &sign] { this->SignData(data, sign); })); mozilla::GTestBench(SuiteName(), (std::string(TestName()) + "_verify").c_str(), ([this, &data, &sign, &verified] { verified = this->VerifyData(data, sign); })); ASSERT_TRUE(verified); } virtual size_t SignatureSize() const = 0; virtual void SignData(std::vector& data, std::vector& output) = 0; virtual bool VerifyData(std::vector& data, std::vector& signature) = 0; }; class BenchECDHRunner : public BenchRunner { public: void Warmup() override { mozilla::UniquePK11SymKey(this->DeriveKey()); } void RunWithSize(size_t size) override { mozilla::UniquePK11SymKey key = nullptr; mozilla::GTestBench(SuiteName(), "derive", ([this, &key] { key.reset(this->DeriveKey()); })); ASSERT_TRUE(!!key); } virtual PK11SymKey* DeriveKey() = 0; }; // =============================================== // ------------------- Encrypt ------------------- // =============================================== template class BenchEncrypt : public SymmetricKeyTest, public BenchEncryptRunner { virtual SECItem MakeParams() = 0; public: void SetUp() override { SymmetricKeyTest::SetUp(); } void EncryptData(std::vector& data, std::vector& output) override { SECItem params = MakeParams(); unsigned int output_len = 0; SECStatus rv = PK11_Encrypt(this->sym_key.get(), MECH, ¶ms, output.data(), &output_len, output.size(), data.data(), data.size()); ASSERT_EQ(rv, SECSuccess); output.resize(output_len); } void DecryptData(std::vector& data, std::vector& output) override { SECItem params = MakeParams(); unsigned int output_len = 0; SECStatus rv = PK11_Decrypt(this->sym_key.get(), MECH, ¶ms, output.data(), &output_len, output.size(), data.data(), data.size()); ASSERT_EQ(rv, SECSuccess); output.resize(output_len); } }; class BenchEncrypt_AES_GCM : public BenchEncrypt { std::vector iv = std::vector(16, 0); std::vector aad = std::vector(0); CK_NSS_GCM_PARAMS gcm_params; protected: SECItem MakeParams() override { gcm_params.pIv = iv.data(); gcm_params.ulIvLen = iv.size(); gcm_params.pAAD = aad.data(); gcm_params.ulAADLen = aad.size(); gcm_params.ulTagBits = 128; SECItem params = {siBuffer, reinterpret_cast(&gcm_params), sizeof(gcm_params)}; return params; } }; class BenchEncrypt_CHACHA20_POLY1305 : public BenchEncrypt { std::vector iv = std::vector(12); std::vector aad = std::vector(16); CK_SALSA20_CHACHA20_POLY1305_PARAMS chacha_params; protected: SECItem MakeParams() override { chacha_params.pNonce = iv.data(); chacha_params.ulNonceLen = iv.size(); chacha_params.pAAD = aad.data(); chacha_params.ulAADLen = aad.size(); SECItem params = {siBuffer, reinterpret_cast(&chacha_params), sizeof(chacha_params)}; return params; } }; class BenchEncrypt_CHACHA20 : public BenchEncrypt { std::vector iv = std::vector(12); uint32_t counter; CK_CHACHA20_PARAMS chacha_params; protected: SECItem MakeParams() override { chacha_params.pBlockCounter = reinterpret_cast(&counter); chacha_params.blockCounterBits = 32; chacha_params.pNonce = iv.data(); chacha_params.ulNonceBits = iv.size(); SECItem params = {siBuffer, reinterpret_cast(&chacha_params), sizeof(chacha_params)}; return params; } }; // =============================================== // ------------------- Digest ------------------- // =============================================== template class BenchDigest : public DigestTest, public BenchDigestRunner { public: size_t DigestedSize() override { return DIGEST_SIZE; } void DigestData(std::vector& data, std::vector& output) override { SECStatus rv = PK11_DigestBegin(this->context.get()); ASSERT_EQ(rv, SECSuccess); rv = PK11_DigestOp(this->context.get(), data.data(), data.size()); ASSERT_EQ(rv, SECSuccess); unsigned int output_len = 0; rv = PK11_DigestFinal(this->context.get(), output.data(), &output_len, output.size()); ASSERT_EQ(rv, SECSuccess); output.resize(output_len); } }; using BenchDigest_SHA256 = BenchDigest; using BenchDigest_SHA512 = BenchDigest; template class SymmetricKeySignTest : public SymmetricKeyTest, public BenchDigestRunner { public: size_t DigestedSize() override { return DIGEST_SIZE; } void DigestData(std::vector& data, std::vector& output) override { SECItem hash = {siBuffer, data.data(), static_cast(data.size())}; SECItem out = {siBuffer, output.data(), static_cast(output.size())}; SECStatus rv = PK11_SignWithSymKey(this->sym_key.get(), MECH, nullptr, &out, &hash); ASSERT_EQ(rv, SECSuccess); } }; using BenchSign_SHA256_HMAC = SymmetricKeySignTest; using BenchSign_SHA384_HMAC = SymmetricKeySignTest; using BenchSign_SHA512_HMAC = SymmetricKeySignTest; using BenchSign_SHA3_224_HMAC = SymmetricKeySignTest; using BenchSign_SHA3_256_HMAC = SymmetricKeySignTest; using BenchSign_SHA3_384_HMAC = SymmetricKeySignTest; using BenchSign_SHA3_512_HMAC = SymmetricKeySignTest; // =============================================== // --------------- Sign and verify --------------- // =============================================== template class KeyPairSignTest : public KeyPairTest, public BenchSignRunner { protected: size_t SignatureSize() const override { return PK11_SignatureLen(this->priv_key.get()); } void SignData(std::vector& data, std::vector& output) override { SECItem hash = {siBuffer, data.data(), static_cast(data.size())}; SECItem out = {siBuffer, output.data(), static_cast(output.size())}; SECStatus rv = PK11_Sign(this->priv_key.get(), &out, &hash); ASSERT_EQ(rv, SECSuccess); } bool VerifyData(std::vector& data, std::vector& signature) override { SECItem dat = {siBuffer, data.data(), static_cast(data.size())}; SECItem sig = {siBuffer, signature.data(), static_cast(signature.size())}; SECStatus rv = PK11_Verify(this->pub_key.get(), &sig, &dat, NULL); return rv == SECSuccess; } }; template class BenchSignEC : public KeyPairSignTest { SECKEYECParams params; std::unique_ptr extra; public: void* MakeParams() { /* For the case of ED curve_oid contains a EdDSA OID. */ SECOidData* curve = SECOID_FindOIDByTag(CURVE); size_t plen = curve->oid.len + 2; extra.reset(new uint8_t[plen]); extra[0] = SEC_ASN1_OBJECT_ID; extra[1] = static_cast(curve->oid.len); memcpy(&extra[2], curve->oid.data, curve->oid.len); params = {siBuffer, extra.get(), static_cast(plen)}; return ¶ms; } }; using BenchSign_P256 = BenchSignEC; using BenchSign_P384 = BenchSignEC; using BenchSign_P521 = BenchSignEC; template class BenchSignRSA : public KeyPairSignTest { PK11RSAGenParams rsa_params; public: void* MakeParams() { rsa_params.keySizeInBits = KEY_SIZE; rsa_params.pe = 0x10001; return &rsa_params; } }; using BenchSign_RSA1024 = BenchSignRSA<1024>; using BenchSign_RSA2048 = BenchSignRSA<2048>; using BenchSign_RSA4096 = BenchSignRSA<4096>; // =============================================== // --------- Diffie-Hellman key exchange --------- // =============================================== template class BenchECDH : public KeyPairTest, public BenchECDHRunner { SECKEYECParams params; std::unique_ptr extra; public: void* MakeParams() override { /* For the case of ED curve_oid contains a EdDSA OID. */ SECOidData* curve = SECOID_FindOIDByTag(CURVE); size_t plen = curve->oid.len + 2; extra.reset(new uint8_t[plen]); extra[0] = SEC_ASN1_OBJECT_ID; extra[1] = static_cast(curve->oid.len); memcpy(&extra[2], curve->oid.data, curve->oid.len); params = {siBuffer, extra.get(), static_cast(plen)}; return ¶ms; } PK11SymKey* DeriveKey() override { return PK11_PubDeriveWithKDF( this->priv_key.get(), this->pub_key.get(), false, nullptr, nullptr, CKM_ECDH1_DERIVE, TARGET, CKA_DERIVE, 0, CKD_NULL, nullptr, nullptr); } }; using BenchECDH_P256_SHA256 = BenchECDH; using BenchECDH_P384_SHA256 = BenchECDH; using BenchECDH_P521_SHA256 = BenchECDH; // =============================================== // ------------------ Harnesses ------------------ // =============================================== std::string MakeTestName(const testing::TestParamInfo& info) { if (info.param == 0) { return "warmup"; } if (info.param < KILOBYTE) { return std::to_string(info.param); } if (info.param < MEGABYTE) { return std::to_string(info.param / KILOBYTE) + "k"; } return std::to_string(info.param / MEGABYTE) + "m"; } #define NSS_BENCH_P(suite, params) \ TEST_P(suite, Run) { Runner(); } \ INSTANTIATE_TEST_SUITE_P(psm_##suite, suite, testing::ValuesIn(params), \ MakeTestName); static const size_t BENCH_ENCRYPT_SIZE[] = { WARMUP, KILOBYTE, 16 * KILOBYTE, MEGABYTE, 16 * MEGABYTE, 128 * MEGABYTE}; // These fail because the treeherder instances run out of memory // // 512 * MEGABYTE, GIGABYTE - MEGABYTE, GIGABYTE, // // These fail because some of the SHA3 HMAC suites time out // // 4 * GIGABYTE - MEGABYTE, 4 * GIGABYTE - 32, // // These fail because the size parameters in PK11_Encrypt/PK11_Decrypt // are unsigned int, i.e. 32bits. So any data size + tag size > 4GiB will // fail // // 4 * GIGABYTE - 1, 4 * GIGABYTE, 4 * GIGABYTE + MEGABYTE, 16 * GIGABYTE, static const size_t BENCH_DH_SIZE[] = {WARMUP, 1}; static const size_t BENCH_RSA1024_SIZE[] = {WARMUP, 32, 64, 117}; static const size_t BENCH_RSA2048_SIZE[] = {WARMUP, 32, 64, 128, 245}; static const size_t BENCH_RSA4096_SIZE[] = {WARMUP, 32, 64, 128, 256, 501}; NSS_BENCH_P(BenchEncrypt_AES_GCM, BENCH_ENCRYPT_SIZE) NSS_BENCH_P(BenchEncrypt_CHACHA20, BENCH_ENCRYPT_SIZE) NSS_BENCH_P(BenchEncrypt_CHACHA20_POLY1305, BENCH_ENCRYPT_SIZE) NSS_BENCH_P(BenchDigest_SHA256, BENCH_ENCRYPT_SIZE) NSS_BENCH_P(BenchDigest_SHA512, BENCH_ENCRYPT_SIZE) NSS_BENCH_P(BenchSign_SHA256_HMAC, BENCH_ENCRYPT_SIZE) NSS_BENCH_P(BenchSign_SHA384_HMAC, BENCH_ENCRYPT_SIZE) NSS_BENCH_P(BenchSign_SHA512_HMAC, BENCH_ENCRYPT_SIZE) NSS_BENCH_P(BenchSign_SHA3_224_HMAC, BENCH_ENCRYPT_SIZE) NSS_BENCH_P(BenchSign_SHA3_256_HMAC, BENCH_ENCRYPT_SIZE) NSS_BENCH_P(BenchSign_SHA3_384_HMAC, BENCH_ENCRYPT_SIZE) NSS_BENCH_P(BenchSign_SHA3_512_HMAC, BENCH_ENCRYPT_SIZE) NSS_BENCH_P(BenchSign_P256, BENCH_ENCRYPT_SIZE) NSS_BENCH_P(BenchSign_P384, BENCH_ENCRYPT_SIZE) NSS_BENCH_P(BenchSign_P521, BENCH_ENCRYPT_SIZE) NSS_BENCH_P(BenchECDH_P256_SHA256, BENCH_DH_SIZE) NSS_BENCH_P(BenchECDH_P384_SHA256, BENCH_DH_SIZE) NSS_BENCH_P(BenchECDH_P521_SHA256, BENCH_DH_SIZE) NSS_BENCH_P(BenchSign_RSA1024, BENCH_RSA1024_SIZE) NSS_BENCH_P(BenchSign_RSA2048, BENCH_RSA2048_SIZE) NSS_BENCH_P(BenchSign_RSA4096, BENCH_RSA4096_SIZE) } // namespace nss_test