/* -*- 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_SUITE_P(VariousSizes, SelfEncryptTestVariable,
::testing::Values(
0,
15,
16,
31,
255,
256,
257));
}
// namespace nss_test