Commit d6a88cbb authored by Robert Relyea's avatar Robert Relyea

Bug 1236720 - Provide sym key derive mechanism as result of encryption of message, r=mt

- The test part of the original patched modifified to convert it into a gtests unit test
- Modifications where a collaboration betewen Martin Thompson and Elio Maldonado

--HG--
extra : amend_source : 92d3fca7e540353c2330ffc756e57d6a22b7045a
parent f2f5bf44
......@@ -10,7 +10,7 @@
#include <cassert>
#include <vector>
std::vector<uint8_t> hex_string_to_bytes(std::string s) {
static inline std::vector<uint8_t> hex_string_to_bytes(std::string s) {
std::vector<uint8_t> bytes;
for (size_t i = 0; i < s.length(); i += 2) {
bytes.push_back(std::stoul(s.substr(i, 2), nullptr, 16));
......
......@@ -11,6 +11,7 @@ CPPSRCS = \
pk11_chacha20poly1305_unittest.cc \
pk11_curve25519_unittest.cc \
pk11_ecdsa_unittest.cc \
pk11_encrypt_derive_unittest.cc \
pk11_export_unittest.cc \
pk11_pbkdf2_unittest.cc \
pk11_prf_unittest.cc \
......
/* 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 "pk11pub.h"
#include "nssutil.h"
#include <stdio.h>
#include "prerror.h"
#include "nss.h"
#include "gtest/gtest.h"
#include "scoped_ptrs.h"
#include "cpputil.h"
#include "databuffer.h"
#include "util.h"
namespace nss_test {
static const uint8_t kIv[] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
static const uint8_t kInput[] = {0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00,
0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00};
class EncryptDeriveTest
: public ::testing::Test,
public ::testing::WithParamInterface<CK_MECHANISM_TYPE> {
public:
void TestEncryptDerive() {
ScopedPK11SymKey derived_key(PK11_Derive(key_.get(), derive_mech(),
derive_param(), encrypt_mech(),
CKA_DECRYPT, keysize()));
ASSERT_TRUE(derived_key);
uint8_t derived_key_data[keysize()];
GetKeyData(derived_key, derived_key_data, sizeof(derived_key_data));
RemoveChecksum(derived_key_data);
uint8_t reference_key_data[keysize()];
unsigned int reference_len = 0;
SECStatus rv = PK11_Encrypt(key_.get(), encrypt_mech(), encrypt_param(),
reference_key_data, &reference_len, keysize(),
kInput, 16);
ASSERT_EQ(SECSuccess, rv);
ASSERT_EQ(keysize(), static_cast<size_t>(reference_len));
RemoveChecksum(reference_key_data);
EXPECT_EQ(DataBuffer(reference_key_data, keysize()),
DataBuffer(derived_key_data, keysize()));
}
protected:
unsigned int keysize() const { return 16; }
private:
CK_MECHANISM_TYPE encrypt_mech() const { return GetParam(); }
CK_MECHANISM_TYPE derive_mech() const {
switch (encrypt_mech()) {
case CKM_DES3_ECB:
return CKM_DES3_ECB_ENCRYPT_DATA;
case CKM_DES3_CBC:
return CKM_DES3_CBC_ENCRYPT_DATA;
case CKM_AES_ECB:
return CKM_AES_ECB_ENCRYPT_DATA;
case CKM_AES_CBC:
return CKM_AES_CBC_ENCRYPT_DATA;
case CKM_CAMELLIA_ECB:
return CKM_CAMELLIA_ECB_ENCRYPT_DATA;
case CKM_CAMELLIA_CBC:
return CKM_CAMELLIA_CBC_ENCRYPT_DATA;
case CKM_SEED_ECB:
return CKM_SEED_ECB_ENCRYPT_DATA;
case CKM_SEED_CBC:
return CKM_SEED_CBC_ENCRYPT_DATA;
default:
ADD_FAILURE() << "Unknown mechanism";
break;
}
return CKM_INVALID_MECHANISM;
}
SECItem* derive_param() const {
static CK_AES_CBC_ENCRYPT_DATA_PARAMS aes_data;
static CK_DES_CBC_ENCRYPT_DATA_PARAMS des_data;
static CK_KEY_DERIVATION_STRING_DATA string_data;
static SECItem param = {siBuffer, NULL, 0};
switch (encrypt_mech()) {
case CKM_DES3_ECB:
case CKM_AES_ECB:
case CKM_CAMELLIA_ECB:
case CKM_SEED_ECB:
string_data.pData = toUcharPtr(kInput);
string_data.ulLen = 16;
param.data = reinterpret_cast<uint8_t*>(&string_data);
param.len = sizeof(string_data);
break;
case CKM_DES3_CBC:
des_data.pData = toUcharPtr(kInput);
des_data.length = 16;
PORT_Memcpy(des_data.iv, kIv, 8);
param.data = reinterpret_cast<uint8_t*>(&des_data);
param.len = sizeof(des_data);
break;
case CKM_AES_CBC:
case CKM_CAMELLIA_CBC:
case CKM_SEED_CBC:
aes_data.pData = toUcharPtr(kInput);
aes_data.length = 16;
PORT_Memcpy(aes_data.iv, kIv, 16);
param.data = reinterpret_cast<uint8_t*>(&aes_data);
param.len = sizeof(aes_data);
break;
default:
ADD_FAILURE() << "Unknown mechanism";
break;
}
return &param;
}
SECItem* encrypt_param() const {
static SECItem param = {siBuffer, NULL, 0};
switch (encrypt_mech()) {
case CKM_DES3_ECB:
case CKM_AES_ECB:
case CKM_CAMELLIA_ECB:
case CKM_SEED_ECB:
// No parameter needed here.
break;
case CKM_DES3_CBC:
case CKM_AES_CBC:
case CKM_CAMELLIA_CBC:
case CKM_SEED_CBC:
param.data = toUcharPtr(kIv);
param.len = 16;
break;
default:
ADD_FAILURE() << "Unknown mechanism";
break;
}
return &param;
}
virtual void SetUp() {
slot_.reset(PK11_GetBestSlot(derive_mech(), NULL));
ASSERT_TRUE(slot_);
key_.reset(PK11_TokenKeyGenWithFlags(slot_.get(), encrypt_mech(), NULL,
keysize(), NULL,
CKF_ENCRYPT | CKF_DERIVE, 0, NULL));
ASSERT_TRUE(key_);
}
void GetKeyData(ScopedPK11SymKey& key, uint8_t* buf, size_t max_len) const {
ASSERT_EQ(SECSuccess, PK11_ExtractKeyValue(key.get()));
SECItem* data = PK11_GetKeyData(key.get());
ASSERT_TRUE(data);
ASSERT_EQ(max_len, static_cast<size_t>(data->len));
PORT_Memcpy(buf, data->data, data->len);
}
// Remove checksum if the key is a 3DES key.
void RemoveChecksum(uint8_t* key_data) const {
if (encrypt_mech() != CKM_DES3_CBC && encrypt_mech() != CKM_DES3_ECB) {
return;
}
for (size_t i = 0; i < keysize(); ++i) {
key_data[i] &= 0xfe;
}
}
ScopedPK11SlotInfo slot_;
ScopedPK11SymKey key_;
};
TEST_P(EncryptDeriveTest, Test) {
TestEncryptDerive();
}
static const CK_MECHANISM_TYPE kEncryptDeriveMechanisms[] = {
CKM_DES3_ECB, CKM_DES3_CBC, CKM_AES_ECB, CKM_AES_ECB, CKM_AES_CBC,
CKM_CAMELLIA_ECB, CKM_CAMELLIA_CBC, CKM_SEED_ECB, CKM_SEED_CBC};
INSTANTIATE_TEST_CASE_P(EncryptDeriveTests, EncryptDeriveTest,
::testing::ValuesIn(kEncryptDeriveMechanisms));
// This class handles the case where 3DES takes a 192-bit key
// where all 24 octets will be used.
class EncryptDerive3Test : public EncryptDeriveTest {
protected:
unsigned int keysize() const { return 24; }
};
TEST_P(EncryptDerive3Test, Test) {
TestEncryptDerive();
}
static const CK_MECHANISM_TYPE kDES3EncryptDeriveMechanisms[] = {CKM_DES3_ECB,
CKM_DES3_CBC};
INSTANTIATE_TEST_CASE_P(Encrypt3DeriveTests, EncryptDerive3Test,
::testing::ValuesIn(kDES3EncryptDeriveMechanisms));
} // namespace nss_test
......@@ -16,6 +16,7 @@
'pk11_chacha20poly1305_unittest.cc',
'pk11_curve25519_unittest.cc',
'pk11_ecdsa_unittest.cc',
'pk11_encrypt_derive_unittest.cc',
'pk11_pbkdf2_unittest.cc',
'pk11_prf_unittest.cc',
'pk11_prng_unittest.cc',
......
......@@ -421,11 +421,20 @@ static const struct mechanismList mechanisms[] = {
#endif
/* --------------------- Secret Key Operations ------------------------ */
{ CKM_GENERIC_SECRET_KEY_GEN, { 1, 32, CKF_GENERATE }, PR_TRUE },
{ CKM_CONCATENATE_BASE_AND_KEY, { 1, 32, CKF_GENERATE }, PR_FALSE },
{ CKM_CONCATENATE_BASE_AND_DATA, { 1, 32, CKF_GENERATE }, PR_FALSE },
{ CKM_CONCATENATE_DATA_AND_BASE, { 1, 32, CKF_GENERATE }, PR_FALSE },
{ CKM_XOR_BASE_AND_DATA, { 1, 32, CKF_GENERATE }, PR_FALSE },
{ CKM_CONCATENATE_BASE_AND_KEY, { 1, 32, CKF_DERIVE }, PR_FALSE },
{ CKM_CONCATENATE_BASE_AND_DATA, { 1, 32, CKF_DERIVE }, PR_FALSE },
{ CKM_CONCATENATE_DATA_AND_BASE, { 1, 32, CKF_DERIVE }, PR_FALSE },
{ CKM_XOR_BASE_AND_DATA, { 1, 32, CKF_DERIVE }, PR_FALSE },
{ CKM_EXTRACT_KEY_FROM_KEY, { 1, 32, CKF_DERIVE }, PR_FALSE },
{ CKM_DES3_ECB_ENCRYPT_DATA, { 1, 32, CKF_DERIVE }, PR_FALSE },
{ CKM_DES3_CBC_ENCRYPT_DATA, { 1, 32, CKF_DERIVE }, PR_FALSE },
{ CKM_AES_ECB_ENCRYPT_DATA, { 1, 32, CKF_DERIVE }, PR_FALSE },
{ CKM_AES_CBC_ENCRYPT_DATA, { 1, 32, CKF_DERIVE }, PR_FALSE },
{ CKM_CAMELLIA_ECB_ENCRYPT_DATA, { 1, 32, CKF_DERIVE }, PR_FALSE },
{ CKM_CAMELLIA_CBC_ENCRYPT_DATA, { 1, 32, CKF_DERIVE }, PR_FALSE },
{ CKM_SEED_ECB_ENCRYPT_DATA, { 1, 32, CKF_DERIVE }, PR_FALSE },
{ CKM_SEED_CBC_ENCRYPT_DATA, { 1, 32, CKF_DERIVE }, PR_FALSE },
/* ---------------------- SSL Key Derivations ------------------------- */
{ CKM_SSL3_PRE_MASTER_KEY_GEN, { 48, 48, CKF_GENERATE }, PR_FALSE },
{ CKM_SSL3_MASTER_KEY_DERIVE, { 48, 48, CKF_DERIVE }, PR_FALSE },
......
......@@ -6241,6 +6241,43 @@ sftk_ANSI_X9_63_kdf(CK_BYTE **key, CK_ULONG key_len,
return CKR_MECHANISM_INVALID;
}
/*
* Handle the derive from a block encryption cipher
*/
CK_RV
sftk_DeriveEncrypt(SFTKCipher encrypt, void *cipherInfo,
int blockSize, SFTKObject *key, CK_ULONG keySize,
unsigned char *data, CK_ULONG len)
{
/* large enough for a 512-bit key */
unsigned char tmpdata[SFTK_MAX_DERIVE_KEY_SIZE];
SECStatus rv;
unsigned int outLen;
CK_RV crv;
if ((len % blockSize) != 0) {
return CKR_MECHANISM_PARAM_INVALID;
}
if (len > SFTK_MAX_DERIVE_KEY_SIZE) {
return CKR_MECHANISM_PARAM_INVALID;
}
if (keySize && (len < keySize)) {
return CKR_MECHANISM_PARAM_INVALID;
}
if (keySize == 0) {
keySize = len;
}
rv = (*encrypt)(cipherInfo, &tmpdata, &outLen, len, data, len);
if (rv != SECSuccess) {
crv = sftk_MapCryptError(PORT_GetError());
return crv;
}
crv = sftk_forceAttribute(key, CKA_VALUE, tmpdata, keySize);
return crv;
}
/*
* SSL Key generation given pre master secret
*/
......@@ -6899,6 +6936,171 @@ NSC_DeriveKey(CK_SESSION_HANDLE hSession,
break;
}
case CKM_DES3_ECB_ENCRYPT_DATA:
case CKM_DES3_CBC_ENCRYPT_DATA: {
void *cipherInfo;
unsigned char des3key[MAX_DES3_KEY_SIZE];
CK_DES_CBC_ENCRYPT_DATA_PARAMS *desEncryptPtr;
int mode;
unsigned char *iv;
unsigned char *data;
CK_ULONG len;
if (mechanism == CKM_DES3_ECB_ENCRYPT_DATA) {
stringPtr = (CK_KEY_DERIVATION_STRING_DATA *)
pMechanism->pParameter;
mode = NSS_DES_EDE3;
iv = NULL;
data = stringPtr->pData;
len = stringPtr->ulLen;
} else {
mode = NSS_DES_EDE3_CBC;
desEncryptPtr =
(CK_DES_CBC_ENCRYPT_DATA_PARAMS *)
pMechanism->pParameter;
iv = desEncryptPtr->iv;
data = desEncryptPtr->pData;
len = desEncryptPtr->length;
}
if (att->attrib.ulValueLen == 16) {
PORT_Memcpy(des3key, att->attrib.pValue, 16);
PORT_Memcpy(des3key + 16, des3key, 8);
} else if (att->attrib.ulValueLen == 24) {
PORT_Memcpy(des3key, att->attrib.pValue, 24);
} else {
crv = CKR_KEY_SIZE_RANGE;
break;
}
cipherInfo = DES_CreateContext(des3key, iv, mode, PR_TRUE);
PORT_Memset(des3key, 0, 24);
if (cipherInfo == NULL) {
crv = CKR_HOST_MEMORY;
break;
}
crv = sftk_DeriveEncrypt((SFTKCipher)DES_Encrypt,
cipherInfo, 8, key, keySize,
data, len);
DES_DestroyContext(cipherInfo, PR_TRUE);
break;
}
case CKM_AES_ECB_ENCRYPT_DATA:
case CKM_AES_CBC_ENCRYPT_DATA: {
void *cipherInfo;
CK_AES_CBC_ENCRYPT_DATA_PARAMS *aesEncryptPtr;
int mode;
unsigned char *iv;
unsigned char *data;
CK_ULONG len;
if (mechanism == CKM_AES_ECB_ENCRYPT_DATA) {
mode = NSS_AES;
iv = NULL;
stringPtr = (CK_KEY_DERIVATION_STRING_DATA *)pMechanism->pParameter;
data = stringPtr->pData;
len = stringPtr->ulLen;
} else {
aesEncryptPtr =
(CK_AES_CBC_ENCRYPT_DATA_PARAMS *) pMechanism->pParameter;
mode = NSS_AES_CBC;
iv = aesEncryptPtr->iv;
data = aesEncryptPtr->pData;
len = aesEncryptPtr->length;
}
cipherInfo = AES_CreateContext((unsigned char *)att->attrib.pValue,
iv, mode, PR_TRUE,
att->attrib.ulValueLen, 16);
if (cipherInfo == NULL) {
crv = CKR_HOST_MEMORY;
break;
}
crv = sftk_DeriveEncrypt((SFTKCipher)AES_Encrypt,
cipherInfo, 16, key, keySize,
data, len);
AES_DestroyContext(cipherInfo, PR_TRUE);
break;
}
case CKM_CAMELLIA_ECB_ENCRYPT_DATA:
case CKM_CAMELLIA_CBC_ENCRYPT_DATA: {
void *cipherInfo;
CK_AES_CBC_ENCRYPT_DATA_PARAMS *aesEncryptPtr;
int mode;
unsigned char *iv;
unsigned char *data;
CK_ULONG len;
if (mechanism == CKM_CAMELLIA_ECB_ENCRYPT_DATA) {
stringPtr = (CK_KEY_DERIVATION_STRING_DATA *)
pMechanism->pParameter;
aesEncryptPtr = NULL;
mode = NSS_CAMELLIA;
data = stringPtr->pData;
len = stringPtr->ulLen;
iv = NULL;
} else {
stringPtr = NULL;
aesEncryptPtr = (CK_AES_CBC_ENCRYPT_DATA_PARAMS *)
pMechanism->pParameter;
mode = NSS_CAMELLIA_CBC;
iv = aesEncryptPtr->iv;
data = aesEncryptPtr->pData;
len = aesEncryptPtr->length;
}
cipherInfo = Camellia_CreateContext((unsigned char *)att->attrib.pValue,
iv, mode, PR_TRUE,
att->attrib.ulValueLen);
if (cipherInfo == NULL) {
crv = CKR_HOST_MEMORY;
break;
}
crv = sftk_DeriveEncrypt((SFTKCipher)Camellia_Encrypt,
cipherInfo, 16, key, keySize,
data, len);
Camellia_DestroyContext(cipherInfo, PR_TRUE);
break;
}
case CKM_SEED_ECB_ENCRYPT_DATA:
case CKM_SEED_CBC_ENCRYPT_DATA: {
void *cipherInfo;
CK_AES_CBC_ENCRYPT_DATA_PARAMS *aesEncryptPtr;
int mode;
unsigned char *iv;
unsigned char *data;
CK_ULONG len;
if (mechanism == CKM_SEED_ECB_ENCRYPT_DATA) {
mode = NSS_SEED;
stringPtr = (CK_KEY_DERIVATION_STRING_DATA *)
pMechanism->pParameter;
aesEncryptPtr = NULL;
data = stringPtr->pData;
len = stringPtr->ulLen;
} else {
mode = NSS_SEED_CBC;
aesEncryptPtr = (CK_AES_CBC_ENCRYPT_DATA_PARAMS *)
pMechanism->pParameter;
iv = aesEncryptPtr->iv;
data = aesEncryptPtr->pData;
len = aesEncryptPtr->length;
}
cipherInfo = SEED_CreateContext((unsigned char *)att->attrib.pValue,
iv, mode, PR_TRUE);
if (cipherInfo == NULL) {
crv = CKR_HOST_MEMORY;
break;
}
crv = sftk_DeriveEncrypt((SFTKCipher)SEED_Encrypt,
cipherInfo, 16, key, keySize,
data, len);
SEED_DestroyContext(cipherInfo, PR_TRUE);
break;
}
case CKM_CONCATENATE_BASE_AND_KEY: {
SFTKObject *newKey;
......
......@@ -9,6 +9,9 @@
#define _SOFTOKNT_H_
#define NSS_SOFTOKEN_DEFAULT_CHUNKSIZE 2048
#define DES_BLOCK_SIZE 8 /* bytes */
#define MAX_DES3_KEY_SIZE 24 /* DES_BLOCK_SIZE * 3 */
#define SFTK_MAX_DERIVE_KEY_SIZE 64
/*
* FIPS 140-2 auditing
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment