author | J.C. Jones <jc@mozilla.com> |
Fri, 18 Oct 2019 17:05:24 +0000 | |
changeset 498235 | 27bbc1fba01518ef3f0ef836fb4a0473d8f71915 |
parent 498234 | c11aa23a063905003bdf0ebc16f78a36474f4b26 |
child 498236 | 80ffb5ef8efb71442034e56a093a4367ac06cdf6 |
push id | 36708 |
push user | nbeleuzu@mozilla.com |
push date | Fri, 18 Oct 2019 21:48:04 +0000 |
treeherder | mozilla-central@27bbc1fba015 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | kjacobs |
bugs | 1577822, 1459141, 1589120, 1588244 |
milestone | 71.0a1 |
first release with | nightly linux32
27bbc1fba015
/
71.0a1
/
20191018214804
/
files
nightly linux64
27bbc1fba015
/
71.0a1
/
20191018214804
/
files
nightly mac
27bbc1fba015
/
71.0a1
/
20191018214804
/
files
nightly win32
27bbc1fba015
/
71.0a1
/
20191018214804
/
files
nightly win64
27bbc1fba015
/
71.0a1
/
20191018214804
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
71.0a1
/
20191018214804
/
pushlog to previous
nightly linux64
71.0a1
/
20191018214804
/
pushlog to previous
nightly mac
71.0a1
/
20191018214804
/
pushlog to previous
nightly win32
71.0a1
/
20191018214804
/
pushlog to previous
nightly win64
71.0a1
/
20191018214804
/
pushlog to previous
|
--- a/security/nss/TAG-INFO +++ b/security/nss/TAG-INFO @@ -1,1 +1,1 @@ -NSS_3_47_BETA3 \ No newline at end of file +NSS_3_47_BETA4 \ No newline at end of file
--- a/security/nss/coreconf/coreconf.dep +++ b/security/nss/coreconf/coreconf.dep @@ -5,9 +5,8 @@ /* * A dummy header file that is a dependency for all the object files. * Used to force a full recompilation of NSS in Mozilla's Tinderbox * depend builds. See comments in rules.mk. */ #error "Do not include this header file." -
--- a/security/nss/gtests/pk11_gtest/manifest.mn +++ b/security/nss/gtests/pk11_gtest/manifest.mn @@ -4,16 +4,17 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. CORE_DEPTH = ../.. DEPTH = ../.. MODULE = nss CPPSRCS = \ pk11_aes_gcm_unittest.cc \ pk11_aeskeywrap_unittest.cc \ + pk11_aeskeywrappad_unittest.cc \ pk11_cbc_unittest.cc \ pk11_chacha20poly1305_unittest.cc \ pk11_curve25519_unittest.cc \ pk11_der_private_key_import_unittest.cc \ pk11_ecdsa_unittest.cc \ pk11_encrypt_derive_unittest.cc \ pk11_export_unittest.cc \ pk11_find_certs_unittest.cc \
new file mode 100644 --- /dev/null +++ b/security/nss/gtests/pk11_gtest/pk11_aeskeywrappad_unittest.cc @@ -0,0 +1,415 @@ +/* -*- 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); +} + +// 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)); +} + +} /* nss_test */
--- a/security/nss/gtests/pk11_gtest/pk11_cbc_unittest.cc +++ b/security/nss/gtests/pk11_gtest/pk11_cbc_unittest.cc @@ -36,16 +36,27 @@ class Pkcs11CbcPadTest : public ::testin case CKM_DES3_CBC: return false; default: ADD_FAILURE() << "Unknown mechanism " << GetParam(); } return false; } + uint32_t GetUnpaddedParam() const { + switch (GetParam()) { + case CKM_AES_CBC_PAD: + return CKM_AES_CBC; + case CKM_DES3_CBC_PAD: + return CKM_DES3_CBC; + default: + ADD_FAILURE() << "Unknown padded mechanism " << GetParam(); + } + return 0; + } size_t block_size() const { return static_cast<size_t>(PK11_GetBlockSize(GetParam(), nullptr)); } size_t GetInputLen(CK_ATTRIBUTE_TYPE op) const { if (is_padded() && op == CKA_ENCRYPT) { // Anything goes for encryption when padded. @@ -335,13 +346,146 @@ TEST_P(Pkcs11CbcPadTest, ContextFailDecr SECStatus rv = PK11_CipherOp(dctx.get(), output, &output_len, sizeof(output), kInput, GetInputLen(CKA_DECRYPT) - 1); EXPECT_EQ(SECFailure, rv); // Because PK11_CipherOp is partial, it can return data on failure. // This means that it needs to reset its output length to 0 when it starts. EXPECT_EQ(0, output_len) << "output_len is reset"; } +TEST_P(Pkcs11CbcPadTest, EncryptDecrypt_PaddingTooLong) { + if (!is_padded()) { + return; + } + + // Padding that's over the block size + const std::vector<uint8_t> input = { + 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, 0x00, 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<uint8_t> encrypted(input.size()); + uint32_t encrypted_len = 0; + + ScopedPK11SymKey ek = MakeKey(CKA_ENCRYPT); + SECStatus rv = PK11_Encrypt(ek.get(), GetUnpaddedParam(), GetIv(), + encrypted.data(), &encrypted_len, + encrypted.size(), input.data(), input.size()); + ASSERT_EQ(SECSuccess, rv); + EXPECT_EQ(input.size(), encrypted_len); + + std::vector<uint8_t> decrypted(input.size()); + uint32_t decrypted_len = 0; + ScopedPK11SymKey dk = MakeKey(CKA_DECRYPT); + rv = PK11_Decrypt(dk.get(), GetParam(), GetIv(), decrypted.data(), + &decrypted_len, decrypted.size(), encrypted.data(), + encrypted_len); + EXPECT_EQ(SECFailure, rv); + EXPECT_EQ(0U, decrypted_len); +} + +TEST_P(Pkcs11CbcPadTest, EncryptDecrypt_BadPadding1) { + if (!is_padded()) { + return; + } + + // Padding that's one byte short + const std::vector<uint8_t> input = { + 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, 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, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}; + std::vector<uint8_t> encrypted(input.size()); + uint32_t encrypted_len = 0; + + ScopedPK11SymKey ek = MakeKey(CKA_ENCRYPT); + SECStatus rv = PK11_Encrypt(ek.get(), GetUnpaddedParam(), GetIv(), + encrypted.data(), &encrypted_len, + encrypted.size(), input.data(), input.size()); + ASSERT_EQ(SECSuccess, rv); + EXPECT_EQ(input.size(), encrypted_len); + + std::vector<uint8_t> decrypted(input.size()); + uint32_t decrypted_len = 0; + ScopedPK11SymKey dk = MakeKey(CKA_DECRYPT); + rv = PK11_Decrypt(dk.get(), GetParam(), GetIv(), decrypted.data(), + &decrypted_len, decrypted.size(), encrypted.data(), + encrypted_len); + EXPECT_EQ(SECFailure, rv); + EXPECT_EQ(0U, decrypted_len); +} + +TEST_P(Pkcs11CbcPadTest, EncryptDecrypt_BadPadding2) { + if (!is_padded()) { + return; + } + + // Padding that's one byte short + const std::vector<uint8_t> input = { + 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, 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, 0x02}; + std::vector<uint8_t> encrypted(input.size()); + uint32_t encrypted_len = 0; + + ScopedPK11SymKey ek = MakeKey(CKA_ENCRYPT); + SECStatus rv = PK11_Encrypt(ek.get(), GetUnpaddedParam(), GetIv(), + encrypted.data(), &encrypted_len, + encrypted.size(), input.data(), input.size()); + ASSERT_EQ(SECSuccess, rv); + EXPECT_EQ(input.size(), encrypted_len); + + std::vector<uint8_t> decrypted(input.size()); + uint32_t decrypted_len = 0; + ScopedPK11SymKey dk = MakeKey(CKA_DECRYPT); + rv = PK11_Decrypt(dk.get(), GetParam(), GetIv(), decrypted.data(), + &decrypted_len, decrypted.size(), encrypted.data(), + encrypted_len); + EXPECT_EQ(SECFailure, rv); + EXPECT_EQ(0U, decrypted_len); +} + +TEST_P(Pkcs11CbcPadTest, EncryptDecrypt_ShortValidPadding) { + if (!is_padded()) { + return; + } + + // Minimal valid padding + const std::vector<uint8_t> input = { + 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, 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, 0x00, 0x01}; + std::vector<uint8_t> encrypted(input.size()); + uint32_t encrypted_len = 0; + + ScopedPK11SymKey ek = MakeKey(CKA_ENCRYPT); + SECStatus rv = PK11_Encrypt(ek.get(), GetUnpaddedParam(), GetIv(), + encrypted.data(), &encrypted_len, + encrypted.size(), input.data(), input.size()); + ASSERT_EQ(SECSuccess, rv); + EXPECT_EQ(input.size(), encrypted_len); + + std::vector<uint8_t> decrypted(input.size()); + uint32_t decrypted_len = 0; + ScopedPK11SymKey dk = MakeKey(CKA_DECRYPT); + rv = PK11_Decrypt(dk.get(), GetParam(), GetIv(), decrypted.data(), + &decrypted_len, decrypted.size(), encrypted.data(), + encrypted_len); + EXPECT_EQ(SECSuccess, rv); + EXPECT_EQ(input.size() - 1, decrypted_len); + EXPECT_EQ(0, memcmp(decrypted.data(), input.data(), decrypted_len)); +} + INSTANTIATE_TEST_CASE_P(EncryptDecrypt, Pkcs11CbcPadTest, ::testing::Values(CKM_AES_CBC_PAD, CKM_AES_CBC, CKM_DES3_CBC_PAD, CKM_DES3_CBC)); } // namespace nss_test
--- a/security/nss/gtests/pk11_gtest/pk11_gtest.gyp +++ b/security/nss/gtests/pk11_gtest/pk11_gtest.gyp @@ -9,16 +9,17 @@ 'targets': [ { 'target_name': 'pk11_gtest', 'type': 'executable', 'sources': [ 'pk11_aes_cmac_unittest.cc', 'pk11_aes_gcm_unittest.cc', 'pk11_aeskeywrap_unittest.cc', + 'pk11_aeskeywrappad_unittest.cc', 'pk11_cbc_unittest.cc', 'pk11_chacha20poly1305_unittest.cc', 'pk11_cipherop_unittest.cc', 'pk11_curve25519_unittest.cc', 'pk11_der_private_key_import_unittest.cc', 'pk11_ecdsa_unittest.cc', 'pk11_encrypt_derive_unittest.cc', 'pk11_find_certs_unittest.cc',
--- a/security/nss/gtests/ssl_gtest/tls_agent.cc +++ b/security/nss/gtests/ssl_gtest/tls_agent.cc @@ -43,16 +43,17 @@ const std::string TlsAgent::kServerRsaPs const std::string TlsAgent::kServerRsaDecrypt = "rsa_decrypt"; const std::string TlsAgent::kServerEcdsa256 = "ecdsa256"; const std::string TlsAgent::kServerEcdsa384 = "ecdsa384"; const std::string TlsAgent::kServerEcdsa521 = "ecdsa521"; const std::string TlsAgent::kServerEcdhRsa = "ecdh_rsa"; const std::string TlsAgent::kServerEcdhEcdsa = "ecdh_ecdsa"; const std::string TlsAgent::kServerDsa = "dsa"; const std::string TlsAgent::kDelegatorEcdsa256 = "delegator_ecdsa256"; +const std::string TlsAgent::kDelegatorRsae2048 = "delegator_rsae2048"; static const uint8_t kCannedTls13ServerHello[] = { 0x03, 0x03, 0x9c, 0xbc, 0x14, 0x9b, 0x0e, 0x2e, 0xfa, 0x0d, 0xf3, 0xf0, 0x5c, 0x70, 0x7a, 0xe0, 0xd1, 0x9b, 0x3e, 0x5a, 0x44, 0x6b, 0xdf, 0xe5, 0xc2, 0x28, 0x64, 0xf7, 0x00, 0xc1, 0x9c, 0x08, 0x76, 0x08, 0x00, 0x13, 0x01, 0x00, 0x00, 0x2e, 0x00, 0x33, 0x00, 0x24, 0x00, 0x1d, 0x00, 0x20, 0xc2, 0xcf, 0x23, 0x17, 0x64, 0x23, 0x03, 0xf0, 0xfb, 0x45, 0x98, 0x26, 0xd1, 0x65, 0x24, 0xa1, 0x6c, 0xa9,
--- a/security/nss/gtests/ssl_gtest/tls_agent.h +++ b/security/nss/gtests/ssl_gtest/tls_agent.h @@ -72,16 +72,17 @@ class TlsAgent : public PollTarget { static const std::string kServerRsaDecrypt; static const std::string kServerEcdsa256; static const std::string kServerEcdsa384; static const std::string kServerEcdsa521; static const std::string kServerEcdhEcdsa; static const std::string kServerEcdhRsa; static const std::string kServerDsa; static const std::string kDelegatorEcdsa256; // draft-ietf-tls-subcerts + static const std::string kDelegatorRsae2048; // draft-ietf-tls-subcerts TlsAgent(const std::string& name, Role role, SSLProtocolVariant variant); virtual ~TlsAgent(); void SetPeer(std::shared_ptr<TlsAgent>& peer) { adapter_->SetPeer(peer->adapter_); }
--- a/security/nss/gtests/ssl_gtest/tls_subcerts_unittest.cc +++ b/security/nss/gtests/ssl_gtest/tls_subcerts_unittest.cc @@ -11,17 +11,18 @@ #include "ssl.h" #include "gtest_utils.h" #include "tls_agent.h" #include "tls_connect.h" namespace nss_test { -const std::string kDelegatorId = TlsAgent::kDelegatorEcdsa256; +const std::string kEcdsaDelegatorId = TlsAgent::kDelegatorEcdsa256; +const std::string kRsaeDelegatorId = TlsAgent::kDelegatorRsae2048; const std::string kDCId = TlsAgent::kServerEcdsa256; const SSLSignatureScheme kDCScheme = ssl_sig_ecdsa_secp256r1_sha256; const PRUint32 kDCValidFor = 60 * 60 * 24 * 7 /* 1 week (seconds */; static void CheckPeerDelegCred(const std::shared_ptr<TlsAgent>& client, bool expected, PRUint32 key_bits = 0) { EXPECT_EQ(expected, client->info().peerDelegCred); if (expected) { @@ -32,88 +33,110 @@ static void CheckPeerDelegCred(const std // Attempt to configure a DC when either the DC or DC private key is missing. TEST_P(TlsConnectTls13, DCNotConfigured) { // Load and delegate the credential. ScopedSECKEYPublicKey pub; ScopedSECKEYPrivateKey priv; EXPECT_TRUE(TlsAgent::LoadKeyPairFromCert(kDCId, &pub, &priv)); StackSECItem dc; - TlsAgent::DelegateCredential(kDelegatorId, pub, kDCScheme, kDCValidFor, now(), - &dc); + TlsAgent::DelegateCredential(kEcdsaDelegatorId, pub, kDCScheme, kDCValidFor, + now(), &dc); // Attempt to install the certificate and DC with a missing DC private key. EnsureTlsSetup(); SSLExtraServerCertData extra_data_missing_dc_priv_key = { ssl_auth_null, nullptr, nullptr, nullptr, &dc, nullptr}; - EXPECT_FALSE(server_->ConfigServerCert(kDelegatorId, true, + EXPECT_FALSE(server_->ConfigServerCert(kEcdsaDelegatorId, true, &extra_data_missing_dc_priv_key)); // Attempt to install the certificate and with only the DC private key. EnsureTlsSetup(); SSLExtraServerCertData extra_data_missing_dc = { ssl_auth_null, nullptr, nullptr, nullptr, nullptr, priv.get()}; - EXPECT_FALSE( - server_->ConfigServerCert(kDelegatorId, true, &extra_data_missing_dc)); + EXPECT_FALSE(server_->ConfigServerCert(kEcdsaDelegatorId, true, + &extra_data_missing_dc)); } // Connected with ECDSA-P256. TEST_P(TlsConnectTls13, DCConnectEcdsaP256) { - Reset(kDelegatorId); + Reset(kEcdsaDelegatorId); client_->EnableDelegatedCredentials(); server_->AddDelegatedCredential(TlsAgent::kServerEcdsa256, ssl_sig_ecdsa_secp256r1_sha256, kDCValidFor, now()); auto cfilter = MakeTlsFilter<TlsExtensionCapture>( client_, ssl_delegated_credentials_xtn); Connect(); EXPECT_TRUE(cfilter->captured()); CheckPeerDelegCred(client_, true, 256); EXPECT_EQ(ssl_sig_ecdsa_secp256r1_sha256, client_->info().signatureScheme); } // Connected with ECDSA-P521. TEST_P(TlsConnectTls13, DCConnectEcdsaP521) { - Reset(kDelegatorId); + Reset(kEcdsaDelegatorId); client_->EnableDelegatedCredentials(); server_->AddDelegatedCredential(TlsAgent::kServerEcdsa521, ssl_sig_ecdsa_secp521r1_sha512, kDCValidFor, now()); client_->EnableDelegatedCredentials(); auto cfilter = MakeTlsFilter<TlsExtensionCapture>( client_, ssl_delegated_credentials_xtn); Connect(); EXPECT_TRUE(cfilter->captured()); CheckPeerDelegCred(client_, true, 521); EXPECT_EQ(ssl_sig_ecdsa_secp521r1_sha512, client_->info().signatureScheme); } -// Connected with RSA-PSS, using an RSAE SPKI. +// Connected with RSA-PSS, using an RSAE DC SPKI. TEST_P(TlsConnectTls13, DCConnectRsaPssRsae) { - Reset(kDelegatorId); + Reset(kEcdsaDelegatorId); client_->EnableDelegatedCredentials(); server_->AddDelegatedCredential( TlsAgent::kServerRsaPss, ssl_sig_rsa_pss_rsae_sha256, kDCValidFor, now()); auto cfilter = MakeTlsFilter<TlsExtensionCapture>( client_, ssl_delegated_credentials_xtn); Connect(); EXPECT_TRUE(cfilter->captured()); CheckPeerDelegCred(client_, true, 1024); EXPECT_EQ(ssl_sig_rsa_pss_rsae_sha256, client_->info().signatureScheme); } +// Connected with RSA-PSS, using a RSAE Delegator SPKI. +TEST_P(TlsConnectTls13, DCConnectRsaeDelegator) { + Reset(kRsaeDelegatorId); + + static const SSLSignatureScheme kSchemes[] = {ssl_sig_rsa_pss_rsae_sha256, + ssl_sig_rsa_pss_pss_sha256}; + client_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes)); + server_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes)); + + client_->EnableDelegatedCredentials(); + server_->AddDelegatedCredential( + TlsAgent::kServerRsaPss, ssl_sig_rsa_pss_pss_sha256, kDCValidFor, now()); + + auto cfilter = MakeTlsFilter<TlsExtensionCapture>( + client_, ssl_delegated_credentials_xtn); + Connect(); + + EXPECT_TRUE(cfilter->captured()); + CheckPeerDelegCred(client_, true, 1024); + EXPECT_EQ(ssl_sig_rsa_pss_pss_sha256, client_->info().signatureScheme); +} + // Connected with RSA-PSS, using a PSS SPKI. TEST_P(TlsConnectTls13, DCConnectRsaPssPss) { - Reset(kDelegatorId); + Reset(kEcdsaDelegatorId); // Need to enable PSS-PSS, which is not on by default. static const SSLSignatureScheme kSchemes[] = {ssl_sig_ecdsa_secp256r1_sha256, ssl_sig_rsa_pss_pss_sha256}; client_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes)); server_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes)); client_->EnableDelegatedCredentials(); @@ -167,34 +190,34 @@ static void GenerateWeakRsaKey(ScopedSEC break; } ADD_FAILURE() << "Unable to generate an RSA key: " << PORT_ErrorToName(PORT_GetError()); } // Fail to connect with a weak RSA key. TEST_P(TlsConnectTls13, DCWeakKey) { - Reset(kDelegatorId); + Reset(kEcdsaDelegatorId); EnsureTlsSetup(); ScopedSECKEYPrivateKey dc_priv; ScopedSECKEYPublicKey dc_pub; GenerateWeakRsaKey(dc_priv, dc_pub); ASSERT_TRUE(dc_priv); // Construct a DC. StackSECItem dc; - TlsAgent::DelegateCredential(kDelegatorId, dc_pub, + TlsAgent::DelegateCredential(kEcdsaDelegatorId, dc_pub, ssl_sig_rsa_pss_rsae_sha256, kDCValidFor, now(), &dc); // Configure the DC on the server. SSLExtraServerCertData extra_data = {ssl_auth_null, nullptr, nullptr, nullptr, &dc, dc_priv.get()}; - EXPECT_TRUE(server_->ConfigServerCert(kDelegatorId, true, &extra_data)); + EXPECT_TRUE(server_->ConfigServerCert(kEcdsaDelegatorId, true, &extra_data)); client_->EnableDelegatedCredentials(); auto cfilter = MakeTlsFilter<TlsExtensionCapture>( client_, ssl_delegated_credentials_xtn); ConnectExpectAlert(client_, kTlsAlertInsufficientSecurity); } @@ -210,58 +233,58 @@ class ReplaceDCSigScheme : public TlsHan *output = input; output->Write(0, ssl_sig_ecdsa_secp384r1_sha384, 2); return CHANGE; } }; // Aborted because of incorrect DC signature algorithm indication. TEST_P(TlsConnectTls13, DCAbortBadExpectedCertVerifyAlg) { - Reset(kDelegatorId); + Reset(kEcdsaDelegatorId); client_->EnableDelegatedCredentials(); server_->AddDelegatedCredential(TlsAgent::kServerEcdsa256, ssl_sig_ecdsa_secp256r1_sha256, kDCValidFor, now()); auto filter = MakeTlsFilter<ReplaceDCSigScheme>(server_); filter->EnableDecryption(); ConnectExpectAlert(client_, kTlsAlertIllegalParameter); client_->CheckErrorCode(SSL_ERROR_DC_CERT_VERIFY_ALG_MISMATCH); server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT); } // Aborted because of invalid DC signature. TEST_P(TlsConnectTls13, DCAbortBadSignature) { - Reset(kDelegatorId); + Reset(kEcdsaDelegatorId); EnsureTlsSetup(); client_->EnableDelegatedCredentials(); ScopedSECKEYPublicKey pub; ScopedSECKEYPrivateKey priv; EXPECT_TRUE(TlsAgent::LoadKeyPairFromCert(kDCId, &pub, &priv)); StackSECItem dc; - TlsAgent::DelegateCredential(kDelegatorId, pub, kDCScheme, kDCValidFor, now(), - &dc); + TlsAgent::DelegateCredential(kEcdsaDelegatorId, pub, kDCScheme, kDCValidFor, + now(), &dc); ASSERT_TRUE(dc.data != nullptr); // Flip the first bit of the DC so that the signature is invalid. dc.data[0] ^= 0x01; SSLExtraServerCertData extra_data = {ssl_auth_null, nullptr, nullptr, nullptr, &dc, priv.get()}; - EXPECT_TRUE(server_->ConfigServerCert(kDelegatorId, true, &extra_data)); + EXPECT_TRUE(server_->ConfigServerCert(kEcdsaDelegatorId, true, &extra_data)); ConnectExpectAlert(client_, kTlsAlertIllegalParameter); client_->CheckErrorCode(SSL_ERROR_DC_BAD_SIGNATURE); server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT); } // Aborted because of expired DC. TEST_P(TlsConnectTls13, DCAbortExpired) { - Reset(kDelegatorId); + Reset(kEcdsaDelegatorId); server_->AddDelegatedCredential(kDCId, kDCScheme, kDCValidFor, now()); client_->EnableDelegatedCredentials(); // When the client checks the time, it will be at least one second after the // DC expired. AdvanceTime((static_cast<PRTime>(kDCValidFor) + 1) * PR_USEC_PER_SEC); ConnectExpectAlert(client_, kTlsAlertIllegalParameter); client_->CheckErrorCode(SSL_ERROR_DC_EXPIRED); server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT); @@ -273,43 +296,43 @@ TEST_P(TlsConnectTls13, DCAbortBadKeyUsa Reset(TlsAgent::kServerEcdsa256); client_->EnableDelegatedCredentials(); server_->AddDelegatedCredential(kDCId, kDCScheme, kDCValidFor, now()); ConnectExpectAlert(client_, kTlsAlertIllegalParameter); } // Connected without DC because of no client indication. TEST_P(TlsConnectTls13, DCConnectNoClientSupport) { - Reset(kDelegatorId); + Reset(kEcdsaDelegatorId); server_->AddDelegatedCredential(kDCId, kDCScheme, kDCValidFor, now()); auto cfilter = MakeTlsFilter<TlsExtensionCapture>( client_, ssl_delegated_credentials_xtn); Connect(); EXPECT_FALSE(cfilter->captured()); CheckPeerDelegCred(client_, false); } // Connected without DC because of no server DC. TEST_P(TlsConnectTls13, DCConnectNoServerSupport) { - Reset(kDelegatorId); + Reset(kEcdsaDelegatorId); client_->EnableDelegatedCredentials(); auto cfilter = MakeTlsFilter<TlsExtensionCapture>( client_, ssl_delegated_credentials_xtn); Connect(); EXPECT_TRUE(cfilter->captured()); CheckPeerDelegCred(client_, false); } // Connected without DC because client doesn't support TLS 1.3. TEST_P(TlsConnectTls13, DCConnectClientNoTls13) { - Reset(kDelegatorId); + Reset(kEcdsaDelegatorId); client_->EnableDelegatedCredentials(); server_->AddDelegatedCredential(kDCId, kDCScheme, kDCValidFor, now()); client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2, SSL_LIBRARY_VERSION_TLS_1_2); server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2, SSL_LIBRARY_VERSION_TLS_1_3); @@ -319,17 +342,17 @@ TEST_P(TlsConnectTls13, DCConnectClientN // Should fallback to TLS 1.2 and not negotiate a DC. EXPECT_FALSE(cfilter->captured()); CheckPeerDelegCred(client_, false); } // Connected without DC because server doesn't support TLS 1.3. TEST_P(TlsConnectTls13, DCConnectServerNoTls13) { - Reset(kDelegatorId); + Reset(kEcdsaDelegatorId); client_->EnableDelegatedCredentials(); server_->AddDelegatedCredential(kDCId, kDCScheme, kDCValidFor, now()); client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2, SSL_LIBRARY_VERSION_TLS_1_3); server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2, SSL_LIBRARY_VERSION_TLS_1_2); @@ -340,17 +363,17 @@ TEST_P(TlsConnectTls13, DCConnectServerN // Should fallback to TLS 1.2 and not negotiate a DC. The client will still // send the indication because it supports 1.3. EXPECT_TRUE(cfilter->captured()); CheckPeerDelegCred(client_, false); } // Connected without DC because client doesn't support the signature scheme. TEST_P(TlsConnectTls13, DCConnectExpectedCertVerifyAlgNotSupported) { - Reset(kDelegatorId); + Reset(kEcdsaDelegatorId); client_->EnableDelegatedCredentials(); static const SSLSignatureScheme kClientSchemes[] = { ssl_sig_ecdsa_secp256r1_sha256, }; client_->SetSignatureSchemes(kClientSchemes, PR_ARRAY_SIZE(kClientSchemes)); server_->AddDelegatedCredential(TlsAgent::kServerEcdsa521, ssl_sig_ecdsa_secp521r1_sha512, kDCValidFor, @@ -366,17 +389,17 @@ TEST_P(TlsConnectTls13, DCConnectExpecte } class DCDelegation : public ::testing::Test {}; TEST_F(DCDelegation, DCDelegations) { PRTime now = PR_Now(); ScopedCERTCertificate cert; ScopedSECKEYPrivateKey priv; - ASSERT_TRUE(TlsAgent::LoadCertificate(kDelegatorId, &cert, &priv)); + ASSERT_TRUE(TlsAgent::LoadCertificate(kEcdsaDelegatorId, &cert, &priv)); ScopedSECKEYPublicKey pub_rsa; ScopedSECKEYPrivateKey priv_rsa; ASSERT_TRUE( TlsAgent::LoadKeyPairFromCert(TlsAgent::kServerRsa, &pub_rsa, &priv_rsa)); StackSECItem dc; EXPECT_EQ(SECFailure,
--- a/security/nss/lib/softoken/pkcs11c.c +++ b/security/nss/lib/softoken/pkcs11c.c @@ -1600,16 +1600,78 @@ NSC_DecryptUpdate(CK_SESSION_HANDLE hSes maxout, pEncryptedPart, ulEncryptedPartLen); if (rv != SECSuccess) { return sftk_MapDecryptError(PORT_GetError()); } *pulPartLen = (CK_ULONG)(outlen + padoutlen); return CKR_OK; } +/* From ssl3con.c: Constant-time helper macro that copies the MSB of x to all + * other bits. */ +#define DUPLICATE_MSB_TO_ALL(x) ((unsigned int)((int)(x) >> (sizeof(int) * 8 - 1))) +/* CK_RVToMask returns, in constant time, a mask value of + * all ones if rv == CKR_OK. Otherwise it returns zero. */ +static unsigned int +CK_RVToMask(CK_RV rv) +{ + unsigned int good; + /* rv ^ CKR_OK is zero iff rv == CKR_OK. Subtracting one results + * in the MSB being set to one iff it was zero before. */ + good = rv ^ CKR_OK; + good--; + return DUPLICATE_MSB_TO_ALL(good); +} +/* Constant-time helper macro that selects l or r depending on all-1 or all-0 + * mask m */ +#define CT_SEL(m, l, r) (((m) & (l)) | (~(m) & (r))) +/* Constant-time helper macro that returns all-1s if x is not 0; and all-0s + * otherwise. */ +#define CT_NOT_ZERO(x) (DUPLICATE_MSB_TO_ALL(((x) | (0 - x)))) + +/* sftk_CheckCBCPadding checks, in constant time, the padding validity and + * accordingly sets the pad length. */ +static CK_RV +sftk_CheckCBCPadding(CK_BYTE_PTR pLastPart, + unsigned int blockSize, unsigned int *outPadSize) +{ + PORT_Assert(outPadSize); + + unsigned int padSize = (unsigned int)pLastPart[blockSize - 1]; + + /* If padSize <= blockSize, set goodPad to all-1s and all-0s otherwise.*/ + unsigned int goodPad = DUPLICATE_MSB_TO_ALL(~(blockSize - padSize)); + /* padSize should not be 0 */ + goodPad &= CT_NOT_ZERO(padSize); + + unsigned int i; + for (i = 0; i < blockSize; i++) { + /* If i < padSize, set loopMask to all-1s and all-0s otherwise.*/ + unsigned int loopMask = DUPLICATE_MSB_TO_ALL(~(padSize - 1 - i)); + /* Get the padding value (should be padSize) from buffer */ + unsigned int padVal = pLastPart[blockSize - 1 - i]; + /* Update goodPad only if i < padSize */ + goodPad &= CT_SEL(loopMask, ~(padVal ^ padSize), goodPad); + } + + /* If any of the final padding bytes had the wrong value, one or more + * of the lower eight bits of |goodPad| will be cleared. We AND the + * bottom 8 bits together and duplicate the result to all the bits. */ + goodPad &= goodPad >> 4; + goodPad &= goodPad >> 2; + goodPad &= goodPad >> 1; + goodPad <<= sizeof(goodPad) * 8 - 1; + goodPad = DUPLICATE_MSB_TO_ALL(goodPad); + + /* Set outPadSize to padSize or 0 */ + *outPadSize = CT_SEL(goodPad, padSize, 0); + /* Return OK if the pad is valid */ + return CT_SEL(goodPad, CKR_OK, CKR_ENCRYPTED_DATA_INVALID); +} + /* NSC_DecryptFinal finishes a multiple-part decryption operation. */ CK_RV NSC_DecryptFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pLastPart, CK_ULONG_PTR pulLastPartLen) { SFTKSession *session; SFTKSessionContext *context; unsigned int outlen; @@ -1638,34 +1700,20 @@ NSC_DecryptFinal(CK_SESSION_HANDLE hSess if (context->padDataLength != 0) { /* this assumes that pLastPart is big enough to hold the *whole* * buffer!!! */ rv = (*context->update)(context->cipherInfo, pLastPart, &outlen, maxout, context->padBuf, context->blockSize); if (rv != SECSuccess) { crv = sftk_MapDecryptError(PORT_GetError()); } else { - unsigned int padSize = - (unsigned int)pLastPart[context->blockSize - 1]; - if ((padSize > context->blockSize) || (padSize == 0)) { - crv = CKR_ENCRYPTED_DATA_INVALID; - } else { - unsigned int i; - unsigned int badPadding = 0; /* used as a boolean */ - for (i = 0; i < padSize; i++) { - badPadding |= - (unsigned int)pLastPart[context->blockSize - 1 - i] ^ - padSize; - } - if (badPadding) { - crv = CKR_ENCRYPTED_DATA_INVALID; - } else { - *pulLastPartLen = outlen - padSize; - } - } + unsigned int padSize = 0; + crv = sftk_CheckCBCPadding(&pLastPart[outlen - context->blockSize], context->blockSize, &padSize); + /* Update pulLastPartLen, in constant time, if crv is OK */ + *pulLastPartLen = CT_SEL(CK_RVToMask(crv), outlen - padSize, *pulLastPartLen); } } } sftk_TerminateOp(session, SFTK_DECRYPT, context); finish: sftk_FreeSession(session); return crv; @@ -1688,17 +1736,17 @@ NSC_Decrypt(CK_SESSION_HANDLE hSession, CHECK_FORK(); /* make sure we're legal */ crv = sftk_GetContext(hSession, &context, SFTK_DECRYPT, PR_FALSE, &session); if (crv != CKR_OK) return crv; if (!pData) { - outlen = ulEncryptedDataLen + context->blockSize; + *pulDataLen = (CK_ULONG)(ulEncryptedDataLen + context->blockSize); goto done; } if (context->doPad && context->multi) { CK_ULONG updateLen = maxoutlen; CK_ULONG finalLen; /* padding is fairly complicated, have the update and final * code deal with it */ @@ -1716,39 +1764,29 @@ NSC_Decrypt(CK_SESSION_HANDLE hSession, } return crv == CKR_OK ? crv2 : crv; } rv = (*context->update)(context->cipherInfo, pData, &outlen, maxoutlen, pEncryptedData, ulEncryptedDataLen); /* XXX need to do MUCH better error mapping than this. */ crv = (rv == SECSuccess) ? CKR_OK : sftk_MapDecryptError(PORT_GetError()); - if (rv == SECSuccess && context->doPad) { - unsigned int padding = pData[outlen - 1]; - if (padding > context->blockSize || !padding) { - crv = CKR_ENCRYPTED_DATA_INVALID; + if (rv == SECSuccess) { + if (context->doPad) { + unsigned int padSize = 0; + crv = sftk_CheckCBCPadding(&pData[outlen - context->blockSize], context->blockSize, &padSize); + /* Update pulDataLen, in constant time, if crv is OK */ + *pulDataLen = CT_SEL(CK_RVToMask(crv), outlen - padSize, *pulDataLen); } else { - unsigned int i; - unsigned int badPadding = 0; /* used as a boolean */ - for (i = 0; i < padding; i++) { - badPadding |= (unsigned int)pData[outlen - 1 - i] ^ padding; - } - if (badPadding) { - crv = CKR_ENCRYPTED_DATA_INVALID; - } else { - outlen -= padding; - } + *pulDataLen = (CK_ULONG)outlen; } } sftk_TerminateOp(session, SFTK_DECRYPT, context); done: sftk_FreeSession(session); - if (crv == CKR_OK) { - *pulDataLen = (CK_ULONG)outlen; - } return crv; } /* ************** Crypto Functions: Digest (HASH) ************************ */ /* NSC_DigestInit initializes a message-digesting operation. */
--- a/security/nss/lib/ssl/ssl3con.c +++ b/security/nss/lib/ssl/ssl3con.c @@ -4250,17 +4250,17 @@ ssl_SignatureSchemeMatchesSpkiOid(SSLSig case ssl_sig_ed448: break; } PORT_Assert(0); return PR_FALSE; } /* Validate that the signature scheme works for the given key type. */ -static PRBool +PRBool ssl_SignatureSchemeValid(SSLSignatureScheme scheme, SECOidTag spkiOid, PRBool isTls13) { if (!ssl_IsSupportedSignatureScheme(scheme)) { return PR_FALSE; } if (!ssl_SignatureSchemeMatchesSpkiOid(scheme, spkiOid)) { return PR_FALSE;
--- a/security/nss/lib/ssl/sslimpl.h +++ b/security/nss/lib/ssl/sslimpl.h @@ -1732,16 +1732,18 @@ SECStatus ssl_PickSignatureScheme(sslSoc SECOidTag ssl3_HashTypeToOID(SSLHashType hashType); SSLHashType ssl_SignatureSchemeToHashType(SSLSignatureScheme scheme); SSLAuthType ssl_SignatureSchemeToAuthType(SSLSignatureScheme scheme); SECStatus ssl3_SetupCipherSuite(sslSocket *ss, PRBool initHashes); SECStatus ssl_InsertRecordHeader(const sslSocket *ss, ssl3CipherSpec *cwSpec, SSLContentType contentType, sslBuffer *wrBuf, PRBool *needsLength); +PRBool ssl_SignatureSchemeValid(SSLSignatureScheme scheme, SECOidTag spkiOid, + PRBool isTls13); /* Pull in DTLS functions */ #include "dtlscon.h" /* Pull in TLS 1.3 functions */ #include "tls13con.h" #include "dtls13con.h"
--- a/security/nss/lib/ssl/tls13subcerts.c +++ b/security/nss/lib/ssl/tls13subcerts.c @@ -698,16 +698,28 @@ SSLExp_DelegateCredential(const CERTCert goto loser; } rv = ssl_SignatureSchemeFromSpki(&cert->subjectPublicKeyInfo, PR_TRUE /* isTls13 */, &dc->alg); if (rv != SECSuccess) { goto loser; } + + if (dc->alg == ssl_sig_none) { + SECOidTag spkiOid = SECOID_GetAlgorithmTag(&cert->subjectPublicKeyInfo.algorithm); + /* If the Cert SPKI contained an AlgorithmIdentifier of "rsaEncryption", set a + * default rsa_pss_rsae_sha256 scheme. */ + if (spkiOid == SEC_OID_PKCS1_RSA_ENCRYPTION) { + SSLSignatureScheme scheme = ssl_sig_rsa_pss_rsae_sha256; + if (ssl_SignatureSchemeValid(scheme, spkiOid, PR_TRUE /* isTls13 */)) { + dc->alg = scheme; + } + } + } PORT_Assert(dc->alg != ssl_sig_none); rv = tls13_AppendCredentialParams(&dcBuf, dc); if (rv != SECSuccess) { goto loser; } /* Hash signature message. */
--- a/security/nss/tests/common/certsetup.sh +++ b/security/nss/tests/common/certsetup.sh @@ -46,16 +46,21 @@ make_cert() { rsapss_chain) type_args=(-g 1024);sign=(-c rsa_pss_ca);type=rsa;; rsa_ca_rsapss_chain) type_args=(-g 1024 --pss-sign);sign=(-c rsa_ca);type=rsa;; ecdh_rsa) type_args=(-q nistp256);sign=(-c rsa_ca);type=ec ;; delegator_p256) touch empty.txt type_args=(-q nistp256 --extGeneric 1.3.6.1.4.1.44363.44:not-critical:empty.txt) type=ec ;; + delegator_rsae2048) + touch empty.txt + type_args=(-g 2048 --extGeneric 1.3.6.1.4.1.44363.44:not-critical:empty.txt) + type=rsa + ;; esac msg="create certificate: $@" shift 2 counter=$(($counter + 1)) cmd=(${BINDIR}/certutil -S \ -z "$R_NOISE_FILE" -d "$PROFILEDIR" \ -n $name -s "CN=$name" -t "$trust" "${sign[@]}" -m "$counter" \ -w -2 -v 120 -k "$type" "${type_args[@]}" "${sighash[@]}" -1 -2)
--- a/security/nss/tests/ssl_gtests/ssl_gtests.sh +++ b/security/nss/tests/ssl_gtests/ssl_gtests.sh @@ -53,16 +53,17 @@ ssl_gtest_certs() { make_cert rsa_ca rsa_ca ca make_cert rsa_chain rsa_chain sign make_cert rsa_pss_ca rsapss_ca ca make_cert rsa_pss_chain rsapss_chain sign make_cert rsa_ca_rsa_pss_chain rsa_ca_rsapss_chain sign make_cert ecdh_rsa ecdh_rsa kex make_cert dsa dsa sign make_cert delegator_ecdsa256 delegator_p256 sign + make_cert delegator_rsae2048 delegator_rsae2048 sign } ############################## ssl_gtest_init ########################## # local shell function to initialize this script ######################################################################## ssl_gtest_init() { SCRIPTNAME=ssl_gtest.sh # sourced - $0 would point to all.sh