diff options
-rw-r--r-- | cpputil/nss_scoped_ptrs.h | 4 | ||||
-rw-r--r-- | gtests/pk11_gtest/pk11_chacha20poly1305_unittest.cc | 312 | ||||
-rw-r--r-- | gtests/pk11_gtest/pk11_cipherop_unittest.cc | 114 | ||||
-rw-r--r-- | lib/pk11wrap/pk11mech.c | 2 | ||||
-rw-r--r-- | lib/softoken/pkcs11.c | 1 | ||||
-rw-r--r-- | lib/softoken/pkcs11c.c | 8 |
6 files changed, 286 insertions, 155 deletions
diff --git a/cpputil/nss_scoped_ptrs.h b/cpputil/nss_scoped_ptrs.h index 2c57986b1..db3429908 100644 --- a/cpputil/nss_scoped_ptrs.h +++ b/cpputil/nss_scoped_ptrs.h @@ -8,8 +8,10 @@ #define nss_scoped_ptrs_h__ #include <memory> + #include "cert.h" #include "keyhi.h" +#include "nss.h" #include "p12.h" #include "pk11hpke.h" #include "pk11pqg.h" @@ -54,6 +56,7 @@ struct ScopedDelete { void operator()(SEC_PKCS12DecoderContext* dcx) { SEC_PKCS12DecoderFinish(dcx); } + void operator()(NSSInitContext* init) { NSS_ShutdownContext(init); } }; template <class T> @@ -75,6 +78,7 @@ SCOPED(CERTDistNames); SCOPED(CERTName); SCOPED(CERTSubjectPublicKeyInfo); SCOPED(HpkeContext); +SCOPED(NSSInitContext); SCOPED(PK11Context); SCOPED(PK11GenericObject); SCOPED(PK11SlotInfo); diff --git a/gtests/pk11_gtest/pk11_chacha20poly1305_unittest.cc b/gtests/pk11_gtest/pk11_chacha20poly1305_unittest.cc index 7c1dca26b..6989e3ca6 100644 --- a/gtests/pk11_gtest/pk11_chacha20poly1305_unittest.cc +++ b/gtests/pk11_gtest/pk11_chacha20poly1305_unittest.cc @@ -19,53 +19,41 @@ namespace nss_test { -static const CK_MECHANISM_TYPE kMech = CKM_NSS_CHACHA20_POLY1305; -static const CK_MECHANISM_TYPE kMechXor = CKM_NSS_CHACHA20_CTR; +static const CK_MECHANISM_TYPE kMech = CKM_CHACHA20_POLY1305; +static const CK_MECHANISM_TYPE kMechLegacy = CKM_NSS_CHACHA20_POLY1305; +static const CK_MECHANISM_TYPE kMechXor = CKM_CHACHA20; +static const CK_MECHANISM_TYPE kMechXorLegacy = CKM_NSS_CHACHA20_CTR; // Some test data for simple tests. static const uint8_t kKeyData[32] = {'k'}; -static const uint8_t kCtrNonce[16] = {'c', 0, 0, 0, 'n'}; +static const uint8_t kXorParamsLegacy[16] = {'c', 0, 0, 0, 'n'}; +static const uint8_t kCounter[4] = {'c', 0}; +static const uint8_t kNonce[12] = {'n', 0}; +static const CK_CHACHA20_PARAMS kXorParams{ + /* pBlockCounter */ const_cast<CK_BYTE_PTR>(kCounter), + /* blockCounterBits */ sizeof(kCounter) * 8, + /* pNonce */ const_cast<CK_BYTE_PTR>(kNonce), + /* ulNonceBits */ sizeof(kNonce) * 8, +}; static const uint8_t kData[16] = {'d'}; +static const uint8_t kExpectedXor[sizeof(kData)] = { + 0xd8, 0x15, 0xd3, 0xb3, 0xe9, 0x34, 0x3b, 0x7a, + 0x24, 0xf6, 0x5f, 0xd7, 0x95, 0x3d, 0xd3, 0x51}; +static const size_t kTagLen = 16; class Pkcs11ChaCha20Poly1305Test : public ::testing::TestWithParam<ChaChaTestVector> { public: void EncryptDecrypt(const ScopedPK11SymKey& key, const bool invalid_iv, const bool invalid_tag, const uint8_t* data, - size_t data_len, const uint8_t* aad, size_t aad_len, - const uint8_t* iv, size_t iv_len, + size_t data_len, CK_MECHANISM_TYPE mech, SECItem* params, + std::vector<uint8_t>* nonce, std::vector<uint8_t>* aad, const uint8_t* ct = nullptr, size_t ct_len = 0) { - // Prepare AEAD params. - CK_NSS_AEAD_PARAMS aead_params; - aead_params.pNonce = toUcharPtr(iv); - aead_params.ulNonceLen = iv_len; - aead_params.pAAD = toUcharPtr(aad); - aead_params.ulAADLen = aad_len; - aead_params.ulTagLen = 16; - - SECItem params = {siBuffer, reinterpret_cast<unsigned char*>(&aead_params), - sizeof(aead_params)}; - - // Encrypt with bad parameters (TagLen is too long). + std::vector<uint8_t> encrypted(data_len + kTagLen); unsigned int encrypted_len = 0; - std::vector<uint8_t> encrypted(data_len + aead_params.ulTagLen); - aead_params.ulTagLen = 158072; - SECStatus rv = - PK11_Encrypt(key.get(), kMech, ¶ms, encrypted.data(), - &encrypted_len, encrypted.size(), data, data_len); - EXPECT_EQ(SECFailure, rv); - EXPECT_EQ(0U, encrypted_len); - - // Encrypt with bad parameters (TagLen is too short). - aead_params.ulTagLen = 2; - rv = PK11_Encrypt(key.get(), kMech, ¶ms, encrypted.data(), - &encrypted_len, encrypted.size(), data, data_len); - EXPECT_EQ(SECFailure, rv); - EXPECT_EQ(0U, encrypted_len); - // Encrypt. - aead_params.ulTagLen = 16; - rv = PK11_Encrypt(key.get(), kMech, ¶ms, encrypted.data(), - &encrypted_len, encrypted.size(), data, data_len); + SECStatus rv = + PK11_Encrypt(key.get(), mech, params, encrypted.data(), &encrypted_len, + encrypted.size(), data, data_len); // Return if encryption failure was expected due to invalid IV. // Without valid ciphertext, all further tests can be skipped. @@ -92,7 +80,7 @@ class Pkcs11ChaCha20Poly1305Test // passed to a subsequent decryption call (for AEAD we // must authenticate even when the pt is zero-length). unsigned int decrypt_bytes_needed = 0; - rv = PK11_Decrypt(key.get(), kMech, ¶ms, nullptr, &decrypt_bytes_needed, + rv = PK11_Decrypt(key.get(), mech, params, nullptr, &decrypt_bytes_needed, 0, encrypted.data(), encrypted_len); EXPECT_EQ(rv, SECSuccess); EXPECT_GT(decrypt_bytes_needed, data_len); @@ -100,9 +88,8 @@ class Pkcs11ChaCha20Poly1305Test // Now decrypt it std::vector<uint8_t> decrypted(decrypt_bytes_needed); unsigned int decrypted_len = 0; - rv = PK11_Decrypt(key.get(), kMech, ¶ms, decrypted.data(), - &decrypted_len, decrypted.size(), encrypted.data(), - encrypted.size()); + rv = PK11_Decrypt(key.get(), mech, params, decrypted.data(), &decrypted_len, + decrypted.size(), encrypted.data(), encrypted.size()); EXPECT_EQ(rv, SECSuccess); // Check the plaintext. @@ -115,7 +102,7 @@ class Pkcs11ChaCha20Poly1305Test decrypted_len = 0; std::vector<uint8_t> bogus_ciphertext(encrypted); bogus_ciphertext[0] ^= 0xff; - rv = PK11_Decrypt(key.get(), kMech, ¶ms, decrypted.data(), + rv = PK11_Decrypt(key.get(), mech, params, decrypted.data(), &decrypted_len, decrypted.size(), bogus_ciphertext.data(), encrypted_len); EXPECT_EQ(rv, SECFailure); @@ -128,47 +115,32 @@ class Pkcs11ChaCha20Poly1305Test decrypted_len = 0; std::vector<uint8_t> bogus_tag(encrypted); bogus_tag[encrypted_len - 1] ^= 0xff; - rv = PK11_Decrypt(key.get(), kMech, ¶ms, decrypted.data(), + rv = PK11_Decrypt(key.get(), mech, params, decrypted.data(), &decrypted_len, decrypted.size(), bogus_tag.data(), encrypted_len); EXPECT_EQ(rv, SECFailure); EXPECT_EQ(0U, decrypted_len); } - // Decrypt with bogus IV. - // iv_len == 0 is invalid and should be caught earlier. - // Still skip, if there's no IV to modify. - if (iv_len != 0) { - decrypted_len = 0; - SECItem bogus_params(params); - CK_NSS_AEAD_PARAMS bogusAeadParams(aead_params); - bogus_params.data = reinterpret_cast<unsigned char*>(&bogusAeadParams); - - std::vector<uint8_t> bogusIV(iv, iv + iv_len); - bogusAeadParams.pNonce = toUcharPtr(bogusIV.data()); - bogusIV[0] ^= 0xff; - - rv = PK11_Decrypt(key.get(), kMech, &bogus_params, decrypted.data(), - &decrypted_len, data_len, encrypted.data(), - encrypted.size()); - EXPECT_EQ(rv, SECFailure); - EXPECT_EQ(0U, decrypted_len); - } + // Decrypt with bogus nonce. + // A nonce length of 0 is invalid and should be caught earlier. + ASSERT_NE(0U, nonce->size()); + decrypted_len = 0; + nonce->data()[0] ^= 0xff; + rv = PK11_Decrypt(key.get(), mech, params, decrypted.data(), &decrypted_len, + data_len, encrypted.data(), encrypted.size()); + EXPECT_EQ(rv, SECFailure); + EXPECT_EQ(0U, decrypted_len); + nonce->data()[0] ^= 0xff; // restore value // Decrypt with bogus additional data. // Skip when AAD was empty and can't be modified. // Alternatively we could generate random aad. - if (aad_len != 0) { + if (aad->size() != 0) { decrypted_len = 0; - SECItem bogus_params(params); - CK_NSS_AEAD_PARAMS bogus_aead_params(aead_params); - bogus_params.data = reinterpret_cast<unsigned char*>(&bogus_aead_params); + aad->data()[0] ^= 0xff; - std::vector<uint8_t> bogus_aad(aad, aad + aad_len); - bogus_aead_params.pAAD = toUcharPtr(bogus_aad.data()); - bogus_aad[0] ^= 0xff; - - rv = PK11_Decrypt(key.get(), kMech, &bogus_params, decrypted.data(), + rv = PK11_Decrypt(key.get(), mech, params, decrypted.data(), &decrypted_len, data_len, encrypted.data(), encrypted.size()); EXPECT_EQ(rv, SECFailure); @@ -176,6 +148,69 @@ class Pkcs11ChaCha20Poly1305Test } } + void EncryptDecrypt(const ScopedPK11SymKey& key, const bool invalid_iv, + const bool invalid_tag, const uint8_t* data, + size_t data_len, const uint8_t* aad_ptr, size_t aad_len, + const uint8_t* iv_ptr, size_t iv_len, + const uint8_t* ct = nullptr, size_t ct_len = 0) { + std::vector<uint8_t> nonce(iv_ptr, iv_ptr + iv_len); + std::vector<uint8_t> aad(aad_ptr, aad_ptr + aad_len); + // Prepare AEAD params. + CK_SALSA20_CHACHA20_POLY1305_PARAMS aead_params; + aead_params.pNonce = toUcharPtr(nonce.data()); + aead_params.ulNonceLen = nonce.size(); + aead_params.pAAD = toUcharPtr(aad.data()); + aead_params.ulAADLen = aad.size(); + + SECItem params = {siBuffer, reinterpret_cast<unsigned char*>(&aead_params), + sizeof(aead_params)}; + + EncryptDecrypt(key, invalid_iv, invalid_tag, data, data_len, kMech, ¶ms, + &nonce, &aad, ct, ct_len); + } + + void EncryptDecryptLegacy(const ScopedPK11SymKey& key, const bool invalid_iv, + const bool invalid_tag, const uint8_t* data, + size_t data_len, const uint8_t* aad_ptr, + size_t aad_len, const uint8_t* iv_ptr, + size_t iv_len, const uint8_t* ct = nullptr, + size_t ct_len = 0) { + std::vector<uint8_t> nonce(iv_ptr, iv_ptr + iv_len); + std::vector<uint8_t> aad(aad_ptr, aad_ptr + aad_len); + // Prepare AEAD params. + CK_NSS_AEAD_PARAMS aead_params; + aead_params.pNonce = toUcharPtr(nonce.data()); + aead_params.ulNonceLen = nonce.size(); + aead_params.pAAD = toUcharPtr(aad.data()); + aead_params.ulAADLen = aad.size(); + aead_params.ulTagLen = kTagLen; + + SECItem params = {siBuffer, reinterpret_cast<unsigned char*>(&aead_params), + sizeof(aead_params)}; + + // Encrypt with bad parameters (TagLen is too long). + unsigned int encrypted_len = 0; + std::vector<uint8_t> encrypted(data_len + aead_params.ulTagLen); + aead_params.ulTagLen = 158072; + SECStatus rv = + PK11_Encrypt(key.get(), kMechLegacy, ¶ms, encrypted.data(), + &encrypted_len, encrypted.size(), data, data_len); + EXPECT_EQ(SECFailure, rv); + EXPECT_EQ(0U, encrypted_len); + + // Encrypt with bad parameters (TagLen is too short). + aead_params.ulTagLen = 2; + rv = PK11_Encrypt(key.get(), kMechLegacy, ¶ms, encrypted.data(), + &encrypted_len, encrypted.size(), data, data_len); + EXPECT_EQ(SECFailure, rv); + EXPECT_EQ(0U, encrypted_len); + + // Encrypt. + aead_params.ulTagLen = kTagLen; + EncryptDecrypt(key, invalid_iv, invalid_tag, data, data_len, kMechLegacy, + ¶ms, &nonce, &aad, ct, ct_len); + } + void EncryptDecrypt(const ChaChaTestVector testvector) { ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); SECItem keyItem = {siBuffer, toUcharPtr(testvector.key.data()), @@ -203,7 +238,7 @@ class Pkcs11ChaCha20Poly1305Test PK11_KeyGen(slot.get(), mech, nullptr, 32, nullptr)); ASSERT_NE(nullptr, sym_key); - int tagSize = 16; + int tagSize = kTagLen; int cipher_simulated_size; int output_len_message = 0; int output_len_simulated = 0; @@ -218,8 +253,8 @@ class Pkcs11ChaCha20Poly1305Test std::vector<uint8_t> cipher_simulated(33); std::vector<uint8_t> cipher_v24(33); std::vector<uint8_t> aad(16); - std::vector<uint8_t> tag_message(16); - std::vector<uint8_t> tag_simulated(16); + std::vector<uint8_t> tag_message(kTagLen); + std::vector<uint8_t> tag_simulated(kTagLen); // Prepare AEAD v2.40 params. CK_SALSA20_CHACHA20_POLY1305_PARAMS chacha_params; @@ -387,10 +422,6 @@ TEST_F(Pkcs11ChaCha20Poly1305Test, GenerateEncryptDecrypt) { } TEST_F(Pkcs11ChaCha20Poly1305Test, Xor) { - static const uint8_t kExpected[sizeof(kData)] = { - 0xd8, 0x15, 0xd3, 0xb3, 0xe9, 0x34, 0x3b, 0x7a, - 0x24, 0xf6, 0x5f, 0xd7, 0x95, 0x3d, 0xd3, 0x51}; - ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); SECItem keyItem = {siBuffer, toUcharPtr(kKeyData), static_cast<unsigned int>(sizeof(kKeyData))}; @@ -398,30 +429,66 @@ TEST_F(Pkcs11ChaCha20Poly1305Test, Xor) { slot.get(), kMechXor, PK11_OriginUnwrap, CKA_ENCRYPT, &keyItem, nullptr)); EXPECT_TRUE(!!key); - SECItem ctrNonceItem = {siBuffer, toUcharPtr(kCtrNonce), - static_cast<unsigned int>(sizeof(kCtrNonce))}; + SECItem params = {siBuffer, + toUcharPtr(reinterpret_cast<const uint8_t*>(&kXorParams)), + static_cast<unsigned int>(sizeof(kXorParams))}; uint8_t encrypted[sizeof(kData)]; unsigned int encrypted_len = 88; // This should be overwritten. SECStatus rv = - PK11_Encrypt(key.get(), kMechXor, &ctrNonceItem, encrypted, + PK11_Encrypt(key.get(), kMechXor, ¶ms, encrypted, &encrypted_len, + sizeof(encrypted), kData, sizeof(kData)); + ASSERT_EQ(SECSuccess, rv); + ASSERT_EQ(sizeof(kExpectedXor), static_cast<size_t>(encrypted_len)); + EXPECT_EQ(0, memcmp(kExpectedXor, encrypted, sizeof(kExpectedXor))); + + // Decrypting has the same effect. + rv = PK11_Decrypt(key.get(), kMechXor, ¶ms, encrypted, &encrypted_len, + sizeof(encrypted), kData, sizeof(kData)); + ASSERT_EQ(SECSuccess, rv); + ASSERT_EQ(sizeof(kData), static_cast<size_t>(encrypted_len)); + EXPECT_EQ(0, memcmp(kExpectedXor, encrypted, sizeof(kExpectedXor))); + + // Operating in reverse too. + rv = PK11_Encrypt(key.get(), kMechXor, ¶ms, encrypted, &encrypted_len, + sizeof(encrypted), kExpectedXor, sizeof(kExpectedXor)); + ASSERT_EQ(SECSuccess, rv); + ASSERT_EQ(sizeof(kExpectedXor), static_cast<size_t>(encrypted_len)); + EXPECT_EQ(0, memcmp(kData, encrypted, sizeof(kData))); +} + +TEST_F(Pkcs11ChaCha20Poly1305Test, XorLegacy) { + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + SECItem keyItem = {siBuffer, toUcharPtr(kKeyData), + static_cast<unsigned int>(sizeof(kKeyData))}; + ScopedPK11SymKey key(PK11_ImportSymKey(slot.get(), kMechXorLegacy, + PK11_OriginUnwrap, CKA_ENCRYPT, + &keyItem, nullptr)); + EXPECT_TRUE(!!key); + + SECItem ctrNonceItem = {siBuffer, toUcharPtr(kXorParamsLegacy), + static_cast<unsigned int>(sizeof(kXorParamsLegacy))}; + uint8_t encrypted[sizeof(kData)]; + unsigned int encrypted_len = 88; // This should be overwritten. + SECStatus rv = + PK11_Encrypt(key.get(), kMechXorLegacy, &ctrNonceItem, encrypted, &encrypted_len, sizeof(encrypted), kData, sizeof(kData)); ASSERT_EQ(SECSuccess, rv); - ASSERT_EQ(sizeof(kExpected), static_cast<size_t>(encrypted_len)); - EXPECT_EQ(0, memcmp(kExpected, encrypted, sizeof(kExpected))); + ASSERT_EQ(sizeof(kExpectedXor), static_cast<size_t>(encrypted_len)); + EXPECT_EQ(0, memcmp(kExpectedXor, encrypted, sizeof(kExpectedXor))); // Decrypting has the same effect. - rv = PK11_Decrypt(key.get(), kMechXor, &ctrNonceItem, encrypted, + rv = PK11_Decrypt(key.get(), kMechXorLegacy, &ctrNonceItem, encrypted, &encrypted_len, sizeof(encrypted), kData, sizeof(kData)); ASSERT_EQ(SECSuccess, rv); ASSERT_EQ(sizeof(kData), static_cast<size_t>(encrypted_len)); - EXPECT_EQ(0, memcmp(kExpected, encrypted, sizeof(kExpected))); + EXPECT_EQ(0, memcmp(kExpectedXor, encrypted, sizeof(kExpectedXor))); // Operating in reverse too. - rv = PK11_Encrypt(key.get(), kMechXor, &ctrNonceItem, encrypted, - &encrypted_len, sizeof(encrypted), kExpected, - sizeof(kExpected)); + rv = PK11_Encrypt(key.get(), kMechXorLegacy, &ctrNonceItem, encrypted, + &encrypted_len, sizeof(encrypted), kExpectedXor, + sizeof(kExpectedXor)); ASSERT_EQ(SECSuccess, rv); - ASSERT_EQ(sizeof(kExpected), static_cast<size_t>(encrypted_len)); + ASSERT_EQ(sizeof(kExpectedXor), static_cast<size_t>(encrypted_len)); EXPECT_EQ(0, memcmp(kData, encrypted, sizeof(kData))); } @@ -429,18 +496,45 @@ TEST_F(Pkcs11ChaCha20Poly1305Test, Xor) { // function. The result is random and therefore cannot be checked. TEST_F(Pkcs11ChaCha20Poly1305Test, GenerateXor) { ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); - ScopedPK11SymKey key(PK11_KeyGen(slot.get(), kMech, nullptr, 32, nullptr)); + ScopedPK11SymKey key(PK11_KeyGen(slot.get(), kMechXor, nullptr, 32, nullptr)); EXPECT_TRUE(!!key); std::vector<uint8_t> iv(16); SECStatus rv = PK11_GenerateRandomOnSlot(slot.get(), iv.data(), iv.size()); EXPECT_EQ(SECSuccess, rv); - SECItem ctrNonceItem = {siBuffer, toUcharPtr(iv.data()), - static_cast<unsigned int>(iv.size())}; + CK_CHACHA20_PARAMS chacha_params; + chacha_params.pBlockCounter = iv.data(); + chacha_params.blockCounterBits = 32; + chacha_params.pNonce = iv.data() + 4; + chacha_params.ulNonceBits = 96; + + SECItem params = { + siBuffer, toUcharPtr(reinterpret_cast<const uint8_t*>(&chacha_params)), + static_cast<unsigned int>(sizeof(chacha_params))}; uint8_t encrypted[sizeof(kData)]; unsigned int encrypted_len = 88; // This should be overwritten. - rv = PK11_Encrypt(key.get(), kMechXor, &ctrNonceItem, encrypted, + rv = PK11_Encrypt(key.get(), kMechXor, ¶ms, encrypted, &encrypted_len, + sizeof(encrypted), kData, sizeof(kData)); + ASSERT_EQ(SECSuccess, rv); + ASSERT_EQ(sizeof(kData), static_cast<size_t>(encrypted_len)); +} + +TEST_F(Pkcs11ChaCha20Poly1305Test, GenerateXorLegacy) { + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ScopedPK11SymKey key( + PK11_KeyGen(slot.get(), kMechXorLegacy, nullptr, 32, nullptr)); + EXPECT_TRUE(!!key); + + std::vector<uint8_t> iv(16); + SECStatus rv = PK11_GenerateRandomOnSlot(slot.get(), iv.data(), iv.size()); + EXPECT_EQ(SECSuccess, rv); + + SECItem params = {siBuffer, toUcharPtr(iv.data()), + static_cast<unsigned int>(iv.size())}; + uint8_t encrypted[sizeof(kData)]; + unsigned int encrypted_len = 88; // This should be overwritten. + rv = PK11_Encrypt(key.get(), kMechXorLegacy, ¶ms, encrypted, &encrypted_len, sizeof(encrypted), kData, sizeof(kData)); ASSERT_EQ(SECSuccess, rv); ASSERT_EQ(sizeof(kData), static_cast<size_t>(encrypted_len)); @@ -451,18 +545,40 @@ TEST_F(Pkcs11ChaCha20Poly1305Test, XorInvalidParams) { ScopedPK11SymKey key(PK11_KeyGen(slot.get(), kMech, nullptr, 32, nullptr)); EXPECT_TRUE(!!key); - SECItem ctrNonceItem = {siBuffer, toUcharPtr(kCtrNonce), - static_cast<unsigned int>(sizeof(kCtrNonce)) - 1}; + SECItem params = {siBuffer, + toUcharPtr(reinterpret_cast<const uint8_t*>(&kXorParams)), + static_cast<unsigned int>(sizeof(kXorParams)) - 1}; uint8_t encrypted[sizeof(kData)]; unsigned int encrypted_len = 88; SECStatus rv = - PK11_Encrypt(key.get(), kMechXor, &ctrNonceItem, encrypted, - &encrypted_len, sizeof(encrypted), kData, sizeof(kData)); + PK11_Encrypt(key.get(), kMechXor, ¶ms, encrypted, &encrypted_len, + sizeof(encrypted), kData, sizeof(kData)); EXPECT_EQ(SECFailure, rv); - ctrNonceItem.data = nullptr; - rv = PK11_Encrypt(key.get(), kMechXor, &ctrNonceItem, encrypted, - &encrypted_len, sizeof(encrypted), kData, sizeof(kData)); + params.data = nullptr; + rv = PK11_Encrypt(key.get(), kMechXor, ¶ms, encrypted, &encrypted_len, + sizeof(encrypted), kData, sizeof(kData)); + EXPECT_EQ(SECFailure, rv); + EXPECT_EQ(SEC_ERROR_BAD_DATA, PORT_GetError()); +} + +TEST_F(Pkcs11ChaCha20Poly1305Test, XorLegacyInvalidParams) { + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ScopedPK11SymKey key(PK11_KeyGen(slot.get(), kMech, nullptr, 32, nullptr)); + EXPECT_TRUE(!!key); + + SECItem params = {siBuffer, toUcharPtr(kXorParamsLegacy), + static_cast<unsigned int>(sizeof(kXorParamsLegacy)) - 1}; + uint8_t encrypted[sizeof(kData)]; + unsigned int encrypted_len = 88; + SECStatus rv = + PK11_Encrypt(key.get(), kMechXor, ¶ms, encrypted, &encrypted_len, + sizeof(encrypted), kData, sizeof(kData)); + EXPECT_EQ(SECFailure, rv); + + params.data = nullptr; + rv = PK11_Encrypt(key.get(), kMechXor, ¶ms, encrypted, &encrypted_len, + sizeof(encrypted), kData, sizeof(kData)); EXPECT_EQ(SECFailure, rv); EXPECT_EQ(SEC_ERROR_BAD_DATA, PORT_GetError()); } diff --git a/gtests/pk11_gtest/pk11_cipherop_unittest.cc b/gtests/pk11_gtest/pk11_cipherop_unittest.cc index 700750cc9..b43583377 100644 --- a/gtests/pk11_gtest/pk11_cipherop_unittest.cc +++ b/gtests/pk11_gtest/pk11_cipherop_unittest.cc @@ -3,6 +3,7 @@ // You can obtain one at http://mozilla.org/MPL/2.0/. #include "gtest/gtest.h" +#include "nss_scoped_ptrs.h" #include <assert.h> #include <limits.h> @@ -21,30 +22,29 @@ namespace nss_test { // cipher context with data that is not cipher block aligned. // -static SECStatus GetBytes(PK11Context* ctx, uint8_t* bytes, size_t len) { +static SECStatus GetBytes(const ScopedPK11Context& ctx, size_t len) { std::vector<uint8_t> in(len, 0); + uint8_t outbuf[128]; + PORT_Assert(len <= sizeof(outbuf)); int outlen; - SECStatus rv = PK11_CipherOp(ctx, bytes, &outlen, len, &in[0], len); + SECStatus rv = PK11_CipherOp(ctx.get(), outbuf, &outlen, len, in.data(), len); if (static_cast<size_t>(outlen) != len) { - return SECFailure; + EXPECT_EQ(rv, SECFailure); } return rv; } TEST(Pkcs11CipherOp, SingleCtxMultipleUnalignedCipherOps) { - PK11SlotInfo* slot; - PK11SymKey* key; - PK11Context* ctx; - - NSSInitContext* globalctx = - NSS_InitContext("", "", "", "", NULL, - NSS_INIT_READONLY | NSS_INIT_NOCERTDB | NSS_INIT_NOMODDB | - NSS_INIT_FORCEOPEN | NSS_INIT_NOROOTINIT); + ScopedNSSInitContext globalctx(NSS_InitContext( + "", "", "", "", NULL, NSS_INIT_READONLY | NSS_INIT_NOCERTDB | + NSS_INIT_NOMODDB | NSS_INIT_FORCEOPEN | + NSS_INIT_NOROOTINIT)); + ASSERT_TRUE(globalctx); const CK_MECHANISM_TYPE cipher = CKM_AES_CTR; - slot = PK11_GetInternalSlot(); + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); ASSERT_TRUE(slot); // Use arbitrary bytes for the AES key @@ -61,35 +61,28 @@ TEST(Pkcs11CipherOp, SingleCtxMultipleUnalignedCipherOps) { SECItem paramItem = {siBuffer, reinterpret_cast<unsigned char*>(¶m), sizeof(CK_AES_CTR_PARAMS)}; - key = PK11_ImportSymKey(slot, cipher, PK11_OriginUnwrap, CKA_ENCRYPT, - &keyItem, NULL); - ctx = PK11_CreateContextBySymKey(cipher, CKA_ENCRYPT, key, ¶mItem); + ScopedPK11SymKey key(PK11_ImportSymKey(slot.get(), cipher, PK11_OriginUnwrap, + CKA_ENCRYPT, &keyItem, NULL)); ASSERT_TRUE(key); + ScopedPK11Context ctx( + PK11_CreateContextBySymKey(cipher, CKA_ENCRYPT, key.get(), ¶mItem)); ASSERT_TRUE(ctx); - uint8_t outbuf[128]; - ASSERT_EQ(GetBytes(ctx, outbuf, 7), SECSuccess); - ASSERT_EQ(GetBytes(ctx, outbuf, 17), SECSuccess); - - PK11_FreeSymKey(key); - PK11_FreeSlot(slot); - PK11_DestroyContext(ctx, PR_TRUE); - NSS_ShutdownContext(globalctx); + ASSERT_EQ(GetBytes(ctx, 7), SECSuccess); + ASSERT_EQ(GetBytes(ctx, 17), SECSuccess); } -TEST(Pkcs11CipherOp, SingleCtxMultipleUnalignedCipherOpsChaCha20) { - PK11SlotInfo* slot; - PK11SymKey* key; - PK11Context* ctx; - - NSSInitContext* globalctx = - NSS_InitContext("", "", "", "", NULL, - NSS_INIT_READONLY | NSS_INIT_NOCERTDB | NSS_INIT_NOMODDB | - NSS_INIT_FORCEOPEN | NSS_INIT_NOROOTINIT); - - const CK_MECHANISM_TYPE cipher = CKM_NSS_CHACHA20_CTR; - - slot = PK11_GetInternalSlot(); +// A context can't be used for Chacha20 as the underlying +// PK11_CipherOp operation is calling the C_EncryptUpdate function for +// which multi-part is disabled for ChaCha20 in counter mode. +void ChachaMulti(CK_MECHANISM_TYPE cipher, SECItem* param) { + ScopedNSSInitContext globalctx(NSS_InitContext( + "", "", "", "", NULL, NSS_INIT_READONLY | NSS_INIT_NOCERTDB | + NSS_INIT_NOMODDB | NSS_INIT_FORCEOPEN | + NSS_INIT_NOROOTINIT)); + ASSERT_TRUE(globalctx); + + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); ASSERT_TRUE(slot); // Use arbitrary bytes for the ChaCha20 key and IV @@ -97,33 +90,42 @@ TEST(Pkcs11CipherOp, SingleCtxMultipleUnalignedCipherOpsChaCha20) { for (size_t i = 0; i < 32; i++) { key_bytes[i] = i; } - SECItem keyItem = {siBuffer, key_bytes, 32}; + SECItem keyItem = {siBuffer, key_bytes, sizeof(key_bytes)}; + + ScopedPK11SymKey key(PK11_ImportSymKey(slot.get(), cipher, PK11_OriginUnwrap, + CKA_ENCRYPT, &keyItem, NULL)); + ASSERT_TRUE(key); + ScopedSECItem param_item(PK11_ParamFromIV(cipher, param)); + ASSERT_TRUE(param_item); + ScopedPK11Context ctx(PK11_CreateContextBySymKey( + cipher, CKA_ENCRYPT, key.get(), param_item.get())); + ASSERT_TRUE(ctx); + ASSERT_EQ(GetBytes(ctx, 7), SECFailure); +} + +TEST(Pkcs11CipherOp, ChachaMultiLegacy) { uint8_t iv_bytes[16]; for (size_t i = 0; i < 16; i++) { - key_bytes[i] = i; + iv_bytes[i] = i; } - SECItem ivItem = {siBuffer, iv_bytes, 16}; + SECItem param_item = {siBuffer, iv_bytes, sizeof(iv_bytes)}; - SECItem* param = PK11_ParamFromIV(cipher, &ivItem); + ChachaMulti(CKM_NSS_CHACHA20_CTR, ¶m_item); +} - key = PK11_ImportSymKey(slot, cipher, PK11_OriginUnwrap, CKA_ENCRYPT, - &keyItem, NULL); - ctx = PK11_CreateContextBySymKey(cipher, CKA_ENCRYPT, key, param); - ASSERT_TRUE(key); - ASSERT_TRUE(ctx); +TEST(Pkcs11CipherOp, ChachaMulti) { + uint8_t iv_bytes[16]; + for (size_t i = 0; i < 16; i++) { + iv_bytes[i] = i; + } + CK_CHACHA20_PARAMS chacha_params = { + iv_bytes, 32, iv_bytes + 4, 96, + }; + SECItem param_item = {siBuffer, reinterpret_cast<uint8_t*>(&chacha_params), + sizeof(chacha_params)}; - uint8_t outbuf[128]; - // This is supposed to fail for Chacha20. This is because the underlying - // PK11_CipherOp operation is calling the C_EncryptUpdate function for - // which multi-part is disabled for ChaCha20 in counter mode. - ASSERT_EQ(GetBytes(ctx, outbuf, 7), SECFailure); - - PK11_FreeSymKey(key); - PK11_FreeSlot(slot); - SECITEM_FreeItem(param, PR_TRUE); - PK11_DestroyContext(ctx, PR_TRUE); - NSS_ShutdownContext(globalctx); + ChachaMulti(CKM_CHACHA20, ¶m_item); } } // namespace nss_test diff --git a/lib/pk11wrap/pk11mech.c b/lib/pk11wrap/pk11mech.c index d94d59a32..685f0e934 100644 --- a/lib/pk11wrap/pk11mech.c +++ b/lib/pk11wrap/pk11mech.c @@ -235,7 +235,7 @@ PK11_GetKeyType(CK_MECHANISM_TYPE type, unsigned long len) case CKM_CHACHA20_POLY1305: case CKM_CHACHA20_KEY_GEN: case CKM_CHACHA20: - return CKK_NSS_CHACHA20; + return CKK_CHACHA20; case CKM_AES_ECB: case CKM_AES_CBC: case CKM_AES_CCM: diff --git a/lib/softoken/pkcs11.c b/lib/softoken/pkcs11.c index dcb0c729d..6eb38eb2b 100644 --- a/lib/softoken/pkcs11.c +++ b/lib/softoken/pkcs11.c @@ -422,6 +422,7 @@ static const struct mechanismList mechanisms[] = { { CKM_NSS_CHACHA20_POLY1305, { 32, 32, CKF_EN_DE }, PR_TRUE }, { CKM_NSS_CHACHA20_CTR, { 32, 32, CKF_EN_DE }, PR_TRUE }, { CKM_CHACHA20_KEY_GEN, { 32, 32, CKF_GENERATE }, PR_TRUE }, + { CKM_CHACHA20, { 32, 32, CKF_EN_DE }, PR_TRUE }, { CKM_CHACHA20_POLY1305, { 32, 32, CKF_EN_DE_MSG }, PR_TRUE }, #endif /* NSS_DISABLE_CHACHAPOLY */ /* ------------------------- Hashing Operations ----------------------- */ diff --git a/lib/softoken/pkcs11c.c b/lib/softoken/pkcs11c.c index 201a0c728..9f94b1425 100644 --- a/lib/softoken/pkcs11c.c +++ b/lib/softoken/pkcs11c.c @@ -1263,6 +1263,10 @@ sftk_CryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, case CKM_NSS_CHACHA20_POLY1305: case CKM_CHACHA20_POLY1305: if (pMechanism->mechanism == CKM_NSS_CHACHA20_POLY1305) { + if (key_type != CKK_NSS_CHACHA20) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } if ((pMechanism->pParameter == NULL) || (pMechanism->ulParameterLen != sizeof(CK_NSS_AEAD_PARAMS))) { crv = CKR_MECHANISM_PARAM_INVALID; @@ -1271,6 +1275,10 @@ sftk_CryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, nss_aead_params_ptr = (CK_NSS_AEAD_PARAMS *)pMechanism->pParameter; } else { CK_SALSA20_CHACHA20_POLY1305_PARAMS_PTR chacha_poly_params; + if (key_type != CKK_CHACHA20) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } if ((pMechanism->pParameter == NULL) || (pMechanism->ulParameterLen != sizeof(CK_SALSA20_CHACHA20_POLY1305_PARAMS))) { |