diff options
Diffstat (limited to 'security/nss/gtests/pk11_gtest/pk11_aeskeywrappad_unittest.cc')
-rw-r--r-- | security/nss/gtests/pk11_gtest/pk11_aeskeywrappad_unittest.cc | 423 |
1 files changed, 423 insertions, 0 deletions
diff --git a/security/nss/gtests/pk11_gtest/pk11_aeskeywrappad_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_aeskeywrappad_unittest.cc new file mode 100644 index 0000000000..9ab172bedf --- /dev/null +++ b/security/nss/gtests/pk11_gtest/pk11_aeskeywrappad_unittest.cc @@ -0,0 +1,423 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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 <memory> +#include "gtest/gtest.h" +#include "nss.h" +#include "nss_scoped_ptrs.h" +#include "pk11pub.h" + +namespace nss_test { + +class Pkcs11AESKeyWrapPadTest : public ::testing::Test {}; + +// Encrypt an ephemeral EC key (U2F use case) +TEST_F(Pkcs11AESKeyWrapPadTest, WrapUnwrapECKey) { + const uint32_t kwrappedBufLen = 256; + const uint32_t kPublicKeyLen = 65; + const uint32_t kOidLen = 65; + unsigned char param_buf[kOidLen]; + unsigned char unwrap_buf[kPublicKeyLen]; + + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ASSERT_NE(nullptr, slot); + + SECItem ecdsa_params = {siBuffer, param_buf, sizeof(param_buf)}; + SECOidData* oid_data = SECOID_FindOIDByTag(SEC_OID_SECG_EC_SECP256R1); + ASSERT_NE(oid_data, nullptr); + ecdsa_params.data[0] = SEC_ASN1_OBJECT_ID; + ecdsa_params.data[1] = oid_data->oid.len; + memcpy(ecdsa_params.data + 2, oid_data->oid.data, oid_data->oid.len); + ecdsa_params.len = oid_data->oid.len + 2; + + SECKEYPublicKey* pub_tmp; + ScopedSECKEYPublicKey pub_key; + ScopedSECKEYPrivateKey priv_key( + PK11_GenerateKeyPair(slot.get(), CKM_EC_KEY_PAIR_GEN, &ecdsa_params, + &pub_tmp, PR_FALSE, PR_TRUE, nullptr)); + ASSERT_NE(nullptr, priv_key); + ASSERT_NE(nullptr, pub_tmp); + pub_key.reset(pub_tmp); + + // Generate a KEK. + ScopedPK11SymKey kek( + PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr)); + ASSERT_NE(nullptr, kek); + + // Wrap the key + ScopedSECItem wrapped(::SECITEM_AllocItem(nullptr, nullptr, kwrappedBufLen)); + ScopedSECItem param(PK11_ParamFromIV(CKM_NSS_AES_KEY_WRAP_PAD, nullptr)); + + SECStatus rv = PK11_WrapPrivKey(slot.get(), kek.get(), priv_key.get(), + CKM_NSS_AES_KEY_WRAP_PAD, param.get(), + wrapped.get(), nullptr); + ASSERT_EQ(rv, SECSuccess); + + SECItem pubKey = {siBuffer, unwrap_buf, kPublicKeyLen}; + CK_ATTRIBUTE_TYPE usages[] = {CKA_SIGN}; + int usageCount = 1; + + ScopedSECKEYPrivateKey unwrapped( + PK11_UnwrapPrivKey(slot.get(), kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, + param.get(), wrapped.get(), nullptr, &pubKey, false, + true, CKK_EC, usages, usageCount, nullptr)); + ASSERT_EQ(0, PORT_GetError()); + ASSERT_TRUE(!!unwrapped); + + // Try it with internal params allocation. + SECKEYPrivateKey* tmp = PK11_UnwrapPrivKey( + slot.get(), kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, nullptr, wrapped.get(), + nullptr, &pubKey, false, true, CKK_EC, usages, usageCount, nullptr); + ASSERT_EQ(0, PORT_GetError()); + ASSERT_NE(nullptr, tmp); + unwrapped.reset(tmp); +} + +// Encrypt an ephemeral RSA key +TEST_F(Pkcs11AESKeyWrapPadTest, WrapUnwrapRsaKey) { + const uint32_t kwrappedBufLen = 648; + unsigned char unwrap_buf[kwrappedBufLen]; + + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ASSERT_NE(nullptr, slot); + + PK11RSAGenParams rsa_param; + rsa_param.keySizeInBits = 1024; + rsa_param.pe = 65537L; + + SECKEYPublicKey* pub_tmp; + ScopedSECKEYPublicKey pub_key; + ScopedSECKEYPrivateKey priv_key( + PK11_GenerateKeyPair(slot.get(), CKM_RSA_PKCS_KEY_PAIR_GEN, &rsa_param, + &pub_tmp, PR_FALSE, PR_FALSE, nullptr)); + ASSERT_NE(nullptr, priv_key); + ASSERT_NE(nullptr, pub_tmp); + pub_key.reset(pub_tmp); + + // Generate a KEK. + ScopedPK11SymKey kek( + PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr)); + ASSERT_NE(nullptr, kek); + + // Wrap the key + ScopedSECItem wrapped(::SECITEM_AllocItem(nullptr, nullptr, kwrappedBufLen)); + ScopedSECItem param(PK11_ParamFromIV(CKM_NSS_AES_KEY_WRAP_PAD, nullptr)); + + SECStatus rv = PK11_WrapPrivKey(slot.get(), kek.get(), priv_key.get(), + CKM_NSS_AES_KEY_WRAP_PAD, param.get(), + wrapped.get(), nullptr); + ASSERT_EQ(rv, SECSuccess); + + SECItem pubKey = {siBuffer, unwrap_buf, kwrappedBufLen}; + CK_ATTRIBUTE_TYPE usages[] = {CKA_SIGN}; + int usageCount = 1; + + ScopedSECKEYPrivateKey unwrapped( + PK11_UnwrapPrivKey(slot.get(), kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, + param.get(), wrapped.get(), nullptr, &pubKey, false, + false, CKK_EC, usages, usageCount, nullptr)); + ASSERT_EQ(0, PORT_GetError()); + ASSERT_TRUE(!!unwrapped); + + ScopedSECItem priv_key_data( + PK11_ExportDERPrivateKeyInfo(priv_key.get(), nullptr)); + ScopedSECItem unwrapped_data( + PK11_ExportDERPrivateKeyInfo(unwrapped.get(), nullptr)); + EXPECT_TRUE(!!priv_key_data); + EXPECT_TRUE(!!unwrapped_data); + ASSERT_EQ(priv_key_data->len, unwrapped_data->len); + ASSERT_EQ( + 0, memcmp(priv_key_data->data, unwrapped_data->data, priv_key_data->len)); +} + +// Wrap a random that's a multiple of the block size, and compare the unwrap +// result. +TEST_F(Pkcs11AESKeyWrapPadTest, WrapUnwrapRandom_EvenBlock) { + const uint32_t kInputKeyLen = 128; + uint32_t out_len = 0; + std::vector<unsigned char> input_key(kInputKeyLen); + std::vector<unsigned char> wrapped_key( + kInputKeyLen + AES_BLOCK_SIZE); // One block of padding + std::vector<unsigned char> unwrapped_key( + kInputKeyLen + AES_BLOCK_SIZE); // One block of padding + + // Generate input key material + SECStatus rv = PK11_GenerateRandom(input_key.data(), input_key.size()); + EXPECT_EQ(SECSuccess, rv); + + // Generate a KEK. + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ASSERT_NE(nullptr, slot); + ScopedPK11SymKey kek( + PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr)); + ASSERT_NE(nullptr, kek); + + // Wrap the key + rv = PK11_Encrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr, + wrapped_key.data(), &out_len, + static_cast<unsigned int>(wrapped_key.size()), + input_key.data(), + static_cast<unsigned int>(input_key.size())); + ASSERT_EQ(SECSuccess, rv); + + rv = PK11_Decrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr, + unwrapped_key.data(), &out_len, + static_cast<unsigned int>(unwrapped_key.size()), + wrapped_key.data(), out_len); + ASSERT_EQ(SECSuccess, rv); + ASSERT_EQ(input_key.size(), out_len); + ASSERT_EQ(0, memcmp(input_key.data(), unwrapped_key.data(), out_len)); +} + +// Wrap a random that's NOT a multiple of the block size, and compare the unwrap +// result. +TEST_F(Pkcs11AESKeyWrapPadTest, WrapUnwrapRandom_OddBlock1) { + const uint32_t kInputKeyLen = 65; + uint32_t out_len = 0; + std::vector<unsigned char> input_key(kInputKeyLen); + std::vector<unsigned char> wrapped_key( + kInputKeyLen + AES_BLOCK_SIZE); // One block of padding + std::vector<unsigned char> unwrapped_key( + kInputKeyLen + AES_BLOCK_SIZE); // One block of padding + + // Generate input key material + SECStatus rv = PK11_GenerateRandom(input_key.data(), input_key.size()); + EXPECT_EQ(SECSuccess, rv); + + // Generate a KEK. + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ASSERT_NE(nullptr, slot); + ScopedPK11SymKey kek( + PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr)); + ASSERT_NE(nullptr, kek); + + // Wrap the key + rv = PK11_Encrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr, + wrapped_key.data(), &out_len, + static_cast<unsigned int>(wrapped_key.size()), + input_key.data(), + static_cast<unsigned int>(input_key.size())); + ASSERT_EQ(SECSuccess, rv); + + rv = PK11_Decrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr, + unwrapped_key.data(), &out_len, + static_cast<unsigned int>(unwrapped_key.size()), + wrapped_key.data(), out_len); + ASSERT_EQ(SECSuccess, rv); + ASSERT_EQ(input_key.size(), out_len); + ASSERT_EQ(0, memcmp(input_key.data(), unwrapped_key.data(), out_len)); +} + +// Wrap a random that's NOT a multiple of the block size, and compare the unwrap +// result. +TEST_F(Pkcs11AESKeyWrapPadTest, WrapUnwrapRandom_OddBlock2) { + const uint32_t kInputKeyLen = 63; + uint32_t out_len = 0; + std::vector<unsigned char> input_key(kInputKeyLen); + std::vector<unsigned char> wrapped_key( + kInputKeyLen + AES_BLOCK_SIZE); // One block of padding + std::vector<unsigned char> unwrapped_key( + kInputKeyLen + AES_BLOCK_SIZE); // One block of padding + + // Generate input key material + SECStatus rv = PK11_GenerateRandom(input_key.data(), input_key.size()); + EXPECT_EQ(SECSuccess, rv); + + // Generate a KEK. + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ASSERT_NE(nullptr, slot); + ScopedPK11SymKey kek( + PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr)); + ASSERT_NE(nullptr, kek); + + // Wrap the key + rv = PK11_Encrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr, + wrapped_key.data(), &out_len, wrapped_key.size(), + input_key.data(), input_key.size()); + ASSERT_EQ(SECSuccess, rv); + + rv = PK11_Decrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr, + unwrapped_key.data(), &out_len, + static_cast<unsigned int>(unwrapped_key.size()), + wrapped_key.data(), out_len); + ASSERT_EQ(SECSuccess, rv); + ASSERT_EQ(input_key.size(), out_len); + ASSERT_EQ(0, memcmp(input_key.data(), unwrapped_key.data(), out_len)); +} + +// Invalid long padding (over the block size, but otherwise valid) +TEST_F(Pkcs11AESKeyWrapPadTest, WrapUnwrapRandom_PaddingTooLong) { + const uint32_t kInputKeyLen = 32; + uint32_t out_len = 0; + + // Apply our own padding + const unsigned char buf[32] = { + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20}; + std::vector<unsigned char> wrapped_key(kInputKeyLen + AES_BLOCK_SIZE); + std::vector<unsigned char> unwrapped_key(kInputKeyLen + AES_BLOCK_SIZE); + + // Generate a KEK. + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ASSERT_NE(nullptr, slot); + ScopedPK11SymKey kek( + PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr)); + ASSERT_NE(nullptr, kek); + + // Wrap the key + SECStatus rv = + PK11_Encrypt(kek.get(), CKM_NSS_AES_KEY_WRAP, // Don't apply more padding + /* param */ nullptr, wrapped_key.data(), &out_len, + wrapped_key.size(), buf, sizeof(buf)); + ASSERT_EQ(SECSuccess, rv); + + rv = PK11_Decrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr, + unwrapped_key.data(), &out_len, + static_cast<unsigned int>(unwrapped_key.size()), + wrapped_key.data(), out_len); + ASSERT_EQ(SECFailure, rv); +} + +// Invalid 0-length padding (there should be a full block if the message doesn't +// need to be padded) +TEST_F(Pkcs11AESKeyWrapPadTest, WrapUnwrapRandom_NoPadding) { + const uint32_t kInputKeyLen = 32; + uint32_t out_len = 0; + + // Apply our own padding + const unsigned char buf[32] = {0}; + std::vector<unsigned char> wrapped_key(kInputKeyLen + AES_BLOCK_SIZE); + std::vector<unsigned char> unwrapped_key(kInputKeyLen + AES_BLOCK_SIZE); + + // Generate a KEK. + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ASSERT_NE(nullptr, slot); + ScopedPK11SymKey kek( + PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr)); + ASSERT_NE(nullptr, kek); + + // Wrap the key + SECStatus rv = + PK11_Encrypt(kek.get(), CKM_NSS_AES_KEY_WRAP, // Don't apply more padding + /* param */ nullptr, wrapped_key.data(), &out_len, + wrapped_key.size(), buf, sizeof(buf)); + ASSERT_EQ(SECSuccess, rv); + + rv = PK11_Decrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr, + unwrapped_key.data(), &out_len, + static_cast<unsigned int>(unwrapped_key.size()), + wrapped_key.data(), out_len); + ASSERT_EQ(SECFailure, rv); +} + +// Invalid padding +TEST_F(Pkcs11AESKeyWrapPadTest, WrapUnwrapRandom_BadPadding1) { + const uint32_t kInputKeyLen = 32; + uint32_t out_len = 0; + + // Apply our own padding + const unsigned char buf[32] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08}; // Check all 8 bytes + std::vector<unsigned char> wrapped_key(kInputKeyLen + AES_BLOCK_SIZE); + std::vector<unsigned char> unwrapped_key(kInputKeyLen + AES_BLOCK_SIZE); + + // Generate a KEK. + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ASSERT_NE(nullptr, slot); + ScopedPK11SymKey kek( + PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr)); + ASSERT_NE(nullptr, kek); + + // Wrap the key + SECStatus rv = + PK11_Encrypt(kek.get(), CKM_NSS_AES_KEY_WRAP, // Don't apply more padding + /* param */ nullptr, wrapped_key.data(), &out_len, + wrapped_key.size(), buf, sizeof(buf)); + ASSERT_EQ(SECSuccess, rv); + + rv = PK11_Decrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr, + unwrapped_key.data(), &out_len, + static_cast<unsigned int>(unwrapped_key.size()), + wrapped_key.data(), out_len); + ASSERT_EQ(SECFailure, rv); +} + +// Invalid padding +TEST_F(Pkcs11AESKeyWrapPadTest, WrapUnwrapRandom_BadPadding2) { + const uint32_t kInputKeyLen = 32; + uint32_t out_len = 0; + + // Apply our own padding + const unsigned char + buf[32] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x02}; // Check first loop repeat + std::vector<unsigned char> wrapped_key(kInputKeyLen + AES_BLOCK_SIZE); + std::vector<unsigned char> unwrapped_key(kInputKeyLen + AES_BLOCK_SIZE); + + // Generate a KEK. + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ASSERT_NE(nullptr, slot); + ScopedPK11SymKey kek( + PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr)); + ASSERT_NE(nullptr, kek); + + // Wrap the key + SECStatus rv = + PK11_Encrypt(kek.get(), CKM_NSS_AES_KEY_WRAP, // Don't apply more padding + /* param */ nullptr, wrapped_key.data(), &out_len, + wrapped_key.size(), buf, sizeof(buf)); + ASSERT_EQ(SECSuccess, rv); + + rv = PK11_Decrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr, + unwrapped_key.data(), &out_len, + static_cast<unsigned int>(unwrapped_key.size()), + wrapped_key.data(), out_len); + ASSERT_EQ(SECFailure, rv); +} + +// Minimum valid padding +TEST_F(Pkcs11AESKeyWrapPadTest, WrapUnwrapRandom_ShortValidPadding) { + const uint32_t kInputKeyLen = 32; + uint32_t out_len = 0; + + // Apply our own padding + const unsigned char buf[kInputKeyLen] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}; // Minimum + std::vector<unsigned char> wrapped_key(kInputKeyLen + AES_BLOCK_SIZE); + std::vector<unsigned char> unwrapped_key(kInputKeyLen + AES_BLOCK_SIZE); + + // Generate a KEK. + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ASSERT_NE(nullptr, slot); + ScopedPK11SymKey kek( + PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr)); + ASSERT_NE(nullptr, kek); + + // Wrap the key + SECStatus rv = + PK11_Encrypt(kek.get(), CKM_NSS_AES_KEY_WRAP, // Don't apply more padding + /* param */ nullptr, wrapped_key.data(), &out_len, + wrapped_key.size(), buf, sizeof(buf)); + ASSERT_EQ(SECSuccess, rv); + + rv = PK11_Decrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr, + unwrapped_key.data(), &out_len, + static_cast<unsigned int>(unwrapped_key.size()), + wrapped_key.data(), out_len); + ASSERT_EQ(SECSuccess, rv); + ASSERT_EQ(kInputKeyLen - 1, out_len); + ASSERT_EQ(0, memcmp(buf, unwrapped_key.data(), out_len)); +} + +} // namespace nss_test |