| /* -*- 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 <functional> |
| #include <memory> |
| #include "nss.h" |
| #include "pk11pub.h" |
| #include "prerror.h" |
| #include "secerr.h" |
| #include "ssl.h" |
| #include "sslerr.h" |
| extern "C" { |
| #include "sslimpl.h" |
| #include "selfencrypt.h" |
| } |
| |
| #include "databuffer.h" |
| #include "gtest_utils.h" |
| #include "nss_scoped_ptrs.h" |
| |
| namespace nss_test { |
| |
| static const uint8_t kAesKey1Buf[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, |
| 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, |
| 0x0c, 0x0d, 0x0e, 0x0f}; |
| static const DataBuffer kAesKey1(kAesKey1Buf, sizeof(kAesKey1Buf)); |
| |
| static const uint8_t kAesKey2Buf[] = {0x10, 0x11, 0x12, 0x13, 0x14, 0x15, |
| 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, |
| 0x1c, 0x1d, 0x1e, 0x1f}; |
| static const DataBuffer kAesKey2(kAesKey2Buf, sizeof(kAesKey2Buf)); |
| |
| static const uint8_t kHmacKey1Buf[] = { |
| 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, |
| 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, |
| 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f}; |
| static const DataBuffer kHmacKey1(kHmacKey1Buf, sizeof(kHmacKey1Buf)); |
| |
| static const uint8_t kHmacKey2Buf[] = { |
| 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, |
| 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, |
| 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f}; |
| static const DataBuffer kHmacKey2(kHmacKey2Buf, sizeof(kHmacKey2Buf)); |
| |
| static const uint8_t* kKeyName1 = |
| reinterpret_cast<const unsigned char*>("KEY1KEY1KEY1KEY1"); |
| static const uint8_t* kKeyName2 = |
| reinterpret_cast<const uint8_t*>("KEY2KEY2KEY2KEY2"); |
| |
| static void ImportKey(const DataBuffer& key, PK11SlotInfo* slot, |
| CK_MECHANISM_TYPE mech, CK_ATTRIBUTE_TYPE cka, |
| ScopedPK11SymKey* to) { |
| SECItem key_item = {siBuffer, const_cast<uint8_t*>(key.data()), |
| static_cast<unsigned int>(key.len())}; |
| |
| PK11SymKey* inner = |
| PK11_ImportSymKey(slot, mech, PK11_OriginUnwrap, cka, &key_item, nullptr); |
| ASSERT_NE(nullptr, inner); |
| to->reset(inner); |
| } |
| |
| extern "C" { |
| extern char ssl_trace; |
| extern FILE* ssl_trace_iob; |
| } |
| |
| class SelfEncryptTestBase : public ::testing::Test { |
| public: |
| SelfEncryptTestBase(size_t message_size) |
| : aes1_(), |
| aes2_(), |
| hmac1_(), |
| hmac2_(), |
| message_(), |
| slot_(PK11_GetInternalSlot()) { |
| EXPECT_NE(nullptr, slot_); |
| char* ev = getenv("SSLTRACE"); |
| if (ev && ev[0]) { |
| ssl_trace = atoi(ev); |
| ssl_trace_iob = stderr; |
| } |
| message_.Allocate(message_size); |
| for (size_t i = 0; i < message_.len(); ++i) { |
| message_.data()[i] = i; |
| } |
| } |
| |
| void SetUp() { |
| message_.Allocate(100); |
| for (size_t i = 0; i < 100; ++i) { |
| message_.data()[i] = i; |
| } |
| ImportKey(kAesKey1, slot_.get(), CKM_AES_CBC, CKA_ENCRYPT, &aes1_); |
| ImportKey(kAesKey2, slot_.get(), CKM_AES_CBC, CKA_ENCRYPT, &aes2_); |
| ImportKey(kHmacKey1, slot_.get(), CKM_SHA256_HMAC, CKA_SIGN, &hmac1_); |
| ImportKey(kHmacKey2, slot_.get(), CKM_SHA256_HMAC, CKA_SIGN, &hmac2_); |
| } |
| |
| void SelfTest( |
| const uint8_t* writeKeyName, const ScopedPK11SymKey& writeAes, |
| const ScopedPK11SymKey& writeHmac, const uint8_t* readKeyName, |
| const ScopedPK11SymKey& readAes, const ScopedPK11SymKey& readHmac, |
| PRErrorCode protect_error_code = 0, PRErrorCode unprotect_error_code = 0, |
| std::function<void(uint8_t* ciphertext, unsigned int* ciphertext_len)> |
| mutate = nullptr) { |
| uint8_t ciphertext[1000]; |
| unsigned int ciphertext_len; |
| uint8_t plaintext[1000]; |
| unsigned int plaintext_len; |
| |
| SECStatus rv = ssl_SelfEncryptProtectInt( |
| writeAes.get(), writeHmac.get(), writeKeyName, message_.data(), |
| message_.len(), ciphertext, &ciphertext_len, sizeof(ciphertext)); |
| if (rv != SECSuccess) { |
| std::cerr << "Error: " << PORT_ErrorToName(PORT_GetError()) << std::endl; |
| } |
| if (protect_error_code) { |
| ASSERT_EQ(protect_error_code, PORT_GetError()); |
| return; |
| } |
| ASSERT_EQ(SECSuccess, rv); |
| |
| if (mutate) { |
| mutate(ciphertext, &ciphertext_len); |
| } |
| rv = ssl_SelfEncryptUnprotectInt(readAes.get(), readHmac.get(), readKeyName, |
| ciphertext, ciphertext_len, plaintext, |
| &plaintext_len, sizeof(plaintext)); |
| if (rv != SECSuccess) { |
| std::cerr << "Error: " << PORT_ErrorToName(PORT_GetError()) << std::endl; |
| } |
| if (!unprotect_error_code) { |
| ASSERT_EQ(SECSuccess, rv); |
| EXPECT_EQ(message_.len(), plaintext_len); |
| EXPECT_EQ(0, memcmp(message_.data(), plaintext, message_.len())); |
| } else { |
| ASSERT_EQ(SECFailure, rv); |
| EXPECT_EQ(unprotect_error_code, PORT_GetError()); |
| } |
| } |
| |
| protected: |
| ScopedPK11SymKey aes1_; |
| ScopedPK11SymKey aes2_; |
| ScopedPK11SymKey hmac1_; |
| ScopedPK11SymKey hmac2_; |
| DataBuffer message_; |
| |
| private: |
| ScopedPK11SlotInfo slot_; |
| }; |
| |
| class SelfEncryptTestVariable : public SelfEncryptTestBase, |
| public ::testing::WithParamInterface<size_t> { |
| public: |
| SelfEncryptTestVariable() : SelfEncryptTestBase(GetParam()) {} |
| }; |
| |
| class SelfEncryptTest128 : public SelfEncryptTestBase { |
| public: |
| SelfEncryptTest128() : SelfEncryptTestBase(128) {} |
| }; |
| |
| TEST_P(SelfEncryptTestVariable, SuccessCase) { |
| SelfTest(kKeyName1, aes1_, hmac1_, kKeyName1, aes1_, hmac1_); |
| } |
| |
| TEST_P(SelfEncryptTestVariable, WrongMacKey) { |
| SelfTest(kKeyName1, aes1_, hmac1_, kKeyName1, aes1_, hmac2_, 0, |
| SEC_ERROR_BAD_DATA); |
| } |
| |
| TEST_P(SelfEncryptTestVariable, WrongKeyName) { |
| SelfTest(kKeyName1, aes1_, hmac1_, kKeyName2, aes1_, hmac1_, 0, |
| SEC_ERROR_NOT_A_RECIPIENT); |
| } |
| |
| TEST_P(SelfEncryptTestVariable, AddAByte) { |
| SelfTest(kKeyName1, aes1_, hmac1_, kKeyName1, aes1_, hmac1_, 0, |
| SEC_ERROR_BAD_DATA, |
| [](uint8_t* ciphertext, unsigned int* ciphertext_len) { |
| (*ciphertext_len)++; |
| }); |
| } |
| |
| TEST_P(SelfEncryptTestVariable, SubtractAByte) { |
| SelfTest(kKeyName1, aes1_, hmac1_, kKeyName1, aes1_, hmac1_, 0, |
| SEC_ERROR_BAD_DATA, |
| [](uint8_t* ciphertext, unsigned int* ciphertext_len) { |
| (*ciphertext_len)--; |
| }); |
| } |
| |
| TEST_P(SelfEncryptTestVariable, BogusIv) { |
| SelfTest(kKeyName1, aes1_, hmac1_, kKeyName1, aes1_, hmac1_, 0, |
| SEC_ERROR_BAD_DATA, |
| [](uint8_t* ciphertext, unsigned int* ciphertext_len) { |
| ciphertext[16]++; |
| }); |
| } |
| |
| TEST_P(SelfEncryptTestVariable, BogusCiphertext) { |
| SelfTest(kKeyName1, aes1_, hmac1_, kKeyName1, aes1_, hmac1_, 0, |
| SEC_ERROR_BAD_DATA, |
| [](uint8_t* ciphertext, unsigned int* ciphertext_len) { |
| ciphertext[32]++; |
| }); |
| } |
| |
| TEST_P(SelfEncryptTestVariable, BadMac) { |
| SelfTest(kKeyName1, aes1_, hmac1_, kKeyName1, aes1_, hmac1_, 0, |
| SEC_ERROR_BAD_DATA, |
| [](uint8_t* ciphertext, unsigned int* ciphertext_len) { |
| ciphertext[*ciphertext_len - 1]++; |
| }); |
| } |
| |
| TEST_F(SelfEncryptTest128, DISABLED_BadPadding) { |
| SelfTest(kKeyName1, aes1_, hmac1_, kKeyName1, aes2_, hmac1_, 0, |
| SEC_ERROR_BAD_DATA); |
| } |
| |
| TEST_F(SelfEncryptTest128, ShortKeyName) { |
| SelfTest(kKeyName1, aes1_, hmac1_, kKeyName1, aes1_, hmac1_, 0, |
| SEC_ERROR_BAD_DATA, |
| [](uint8_t* ciphertext, unsigned int* ciphertext_len) { |
| *ciphertext_len = 15; |
| }); |
| } |
| |
| TEST_F(SelfEncryptTest128, ShortIv) { |
| SelfTest(kKeyName1, aes1_, hmac1_, kKeyName1, aes1_, hmac1_, 0, |
| SEC_ERROR_BAD_DATA, |
| [](uint8_t* ciphertext, unsigned int* ciphertext_len) { |
| *ciphertext_len = 31; |
| }); |
| } |
| |
| TEST_F(SelfEncryptTest128, ShortCiphertextLen) { |
| SelfTest(kKeyName1, aes1_, hmac1_, kKeyName1, aes1_, hmac1_, 0, |
| SEC_ERROR_BAD_DATA, |
| [](uint8_t* ciphertext, unsigned int* ciphertext_len) { |
| *ciphertext_len = 32; |
| }); |
| } |
| |
| TEST_F(SelfEncryptTest128, ShortCiphertext) { |
| SelfTest(kKeyName1, aes1_, hmac1_, kKeyName1, aes1_, hmac1_, 0, |
| SEC_ERROR_BAD_DATA, |
| [](uint8_t* ciphertext, unsigned int* ciphertext_len) { |
| *ciphertext_len -= 17; |
| }); |
| } |
| |
| TEST_F(SelfEncryptTest128, MacWithAESKeyEncrypt) { |
| SelfTest(kKeyName1, aes1_, aes1_, kKeyName1, aes1_, hmac1_, |
| SEC_ERROR_LIBRARY_FAILURE); |
| } |
| |
| TEST_F(SelfEncryptTest128, AESWithMacKeyEncrypt) { |
| SelfTest(kKeyName1, hmac1_, hmac1_, kKeyName1, aes1_, hmac1_, |
| SEC_ERROR_INVALID_KEY); |
| } |
| |
| TEST_F(SelfEncryptTest128, MacWithAESKeyDecrypt) { |
| SelfTest(kKeyName1, aes1_, hmac1_, kKeyName1, aes1_, aes1_, 0, |
| SEC_ERROR_LIBRARY_FAILURE); |
| } |
| |
| TEST_F(SelfEncryptTest128, AESWithMacKeyDecrypt) { |
| SelfTest(kKeyName1, aes1_, hmac1_, kKeyName1, hmac1_, hmac1_, 0, |
| SEC_ERROR_INVALID_KEY); |
| } |
| |
| INSTANTIATE_TEST_CASE_P(VariousSizes, SelfEncryptTestVariable, |
| ::testing::Values(0, 15, 16, 31, 255, 256, 257)); |
| |
| } // namespace nss_test |