/* -*- 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 #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 input_key(kInputKeyLen); std::vector wrapped_key( kInputKeyLen + AES_BLOCK_SIZE); // One block of padding std::vector 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(wrapped_key.size()), input_key.data(), static_cast(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(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 input_key(kInputKeyLen); std::vector wrapped_key( kInputKeyLen + AES_BLOCK_SIZE); // One block of padding std::vector 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(wrapped_key.size()), input_key.data(), static_cast(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(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 input_key(kInputKeyLen); std::vector wrapped_key( kInputKeyLen + AES_BLOCK_SIZE); // One block of padding std::vector 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(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 wrapped_key(kInputKeyLen + AES_BLOCK_SIZE); std::vector 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(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 wrapped_key(kInputKeyLen + AES_BLOCK_SIZE); std::vector 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(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 wrapped_key(kInputKeyLen + AES_BLOCK_SIZE); std::vector 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(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 wrapped_key(kInputKeyLen + AES_BLOCK_SIZE); std::vector 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(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 wrapped_key(kInputKeyLen + AES_BLOCK_SIZE); std::vector 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(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