/* -*- 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 "nss.h"
#include "pk11pub.h"
#include "gtest/gtest.h"
#include "nss_scoped_ptrs.h"
namespace nss_test {
static unsigned char* ToUcharPtr(std::string& str) {
return const_cast<
unsigned char*>(
reinterpret_cast<
const unsigned char*>(str.c_str()));
}
class Pkcs11Pbkdf2Test :
public ::testing::Test {
public:
void Derive(std::vector<uint8_t>& derived, SECOidTag hash_alg) {
// Shared between test vectors.
const unsigned int kIterations =
4096;
std::string pass(
"passwordPASSWORDpassword");
std::string salt(
"saltSALTsaltSALTsaltSALTsaltSALTsalt");
// Derivation must succeed with the right values.
EXPECT_TRUE(DeriveBytes(pass, salt, derived, hash_alg, kIterations));
// Derivation must fail when the password is bogus.
std::string bogus_pass(
"PasswordPASSWORDpassword");
EXPECT_FALSE(DeriveBytes(bogus_pass, salt, derived, hash_alg, kIterations));
// Derivation must fail when the salt is bogus.
std::string bogus_salt(
"SaltSALTsaltSALTsaltSALTsaltSALTsalt");
EXPECT_FALSE(DeriveBytes(pass, bogus_salt, derived, hash_alg, kIterations));
// Derivation must fail when using the wrong hash function.
SECOidTag next_hash_alg =
static_cast<SECOidTag>(hash_alg +
1);
EXPECT_FALSE(DeriveBytes(pass, salt, derived, next_hash_alg, kIterations));
// Derivation must fail when using the wrong number of kIterations.
EXPECT_FALSE(DeriveBytes(pass, salt, derived, hash_alg, kIterations +
1));
}
void KeySizes(SECOidTag hash_alg) {
// These tests will only validate the controls around the key sizes.
// The resulting key is tested above, with valid key sizes.
const unsigned int kIterations =
10;
std::string pass(
"passwordPASSWORDpassword");
std::string salt(
"saltSALTsaltSALTsaltSALTsaltSALTsalt");
std::string salt_empty(
"");
// Derivation must fail when using key sizes bigger than MAX_KEY_LEN.
const int big_key_size =
768;
EXPECT_FALSE(KeySizeParam(pass, salt, big_key_size, hash_alg, kIterations));
// Zero is acceptable as key size and will be managed internally.
const int zero_key_size =
0;
EXPECT_TRUE(KeySizeParam(pass, salt, zero_key_size, hash_alg, kIterations));
// Zero is acceptable as salt size and will be managed internally.
EXPECT_TRUE(
KeySizeParam(pass, salt_empty, zero_key_size, hash_alg, kIterations));
// -1 will be set to 0 internally and this means that the key size will be
// obtained from the template. If the template doesn't have this defined,
// it must fail.
const int minus_key_size = -
1;
EXPECT_FALSE(
KeySizeParam(pass, salt, minus_key_size, hash_alg, kIterations));
// Lower than -1 is not allowed, as -1 means no keyLen defined.
const int negative_key_size = -
10;
EXPECT_FALSE(
KeySizeParam(pass, salt, negative_key_size, hash_alg, kIterations));
// Malformed inputs are handled without crashing
EXPECT_FALSE(
MalformedPass(pass, salt, big_key_size, hash_alg, kIterations));
EXPECT_FALSE(
MalformedSalt(pass, salt, big_key_size, hash_alg, kIterations));
}
private:
bool DeriveBytes(std::string& pass, std::string& salt,
std::vector<uint8_t>& derived, SECOidTag hash_alg,
unsigned int kIterations) {
SECItem pass_item = {siBuffer, ToUcharPtr(pass),
static_cast<
unsigned int>(pass.length())};
SECItem salt_item = {siBuffer, ToUcharPtr(salt),
static_cast<
unsigned int>(salt.length())};
// Set up PBKDF2 params.
ScopedSECAlgorithmID alg_id(
PK11_CreatePBEV2AlgorithmID(SEC_OID_PKCS5_PBKDF2, hash_alg, hash_alg,
derived.size(), kIterations, &salt_item));
// Derive.
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ScopedPK11SymKey sym_key(
PK11_PBEKeyGen(slot.get(), alg_id.get(), &pass_item,
false, nullptr));
SECStatus rv = PK11_ExtractKeyValue(sym_key.get());
EXPECT_EQ(rv, SECSuccess);
SECItem* key_data = PK11_GetKeyData(sym_key.get());
return !memcmp(&derived[
0], key_data->data, key_data->len);
}
bool GenerateKey(SECItem pass_item, SECItem salt_item,
const int key_size,
SECOidTag hash_alg,
unsigned int kIterations) {
// Set up PBKDF2 params.
ScopedSECAlgorithmID alg_id(
PK11_CreatePBEV2AlgorithmID(SEC_OID_PKCS5_PBKDF2, hash_alg, hash_alg,
key_size, kIterations, &salt_item));
// Try to generate a key with the defined params.
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ScopedPK11SymKey sym_key(
PK11_PBEKeyGen(slot.get(), alg_id.get(), &pass_item,
false, nullptr));
// Should be nullptr if fail.
return sym_key.get();
}
bool KeySizeParam(std::string& pass, std::string& salt,
const int key_size,
SECOidTag hash_alg,
unsigned int kIterations) {
SECItem pass_item = {siBuffer, ToUcharPtr(pass),
static_cast<
unsigned int>(pass.length())};
SECItem salt_item = {siBuffer, ToUcharPtr(salt),
static_cast<
unsigned int>(salt.length())};
return GenerateKey(pass_item, salt_item, key_size, hash_alg, kIterations);
}
bool MalformedSalt(std::string& pass, std::string& salt,
const int key_size,
SECOidTag hash_alg,
unsigned int kIterations) {
SECItem pass_item = {siBuffer, ToUcharPtr(pass),
static_cast<
unsigned int>(pass.length())};
SECItem salt_item = {siBuffer, nullptr,
0};
return GenerateKey(pass_item, salt_item, key_size, hash_alg, kIterations);
}
bool MalformedPass(std::string& pass, std::string& salt,
const int key_size,
SECOidTag hash_alg,
unsigned int kIterations) {
SECItem pass_item = {siBuffer, nullptr,
0};
SECItem salt_item = {siBuffer, ToUcharPtr(salt),
static_cast<
unsigned int>(salt.length())};
return GenerateKey(pass_item, salt_item, key_size, hash_alg, kIterations);
}
};
// RFC 6070 <http://tools.ietf.org/html/rfc6070>
TEST_F(Pkcs11Pbkdf2Test, DeriveKnown1) {
std::vector<uint8_t> derived = {
0x3d,
0x2e,
0xec,
0x4f,
0xe4,
0x1c,
0x84,
0x9b,
0x80,
0xc8,
0xd8,
0x36,
0x62,
0xc0,
0xe4,
0x4a,
0x8b,
0x29,
0x1a,
0x96,
0x4c,
0xf2,
0xf0,
0x70,
0x38};
Derive(derived, SEC_OID_HMAC_SHA1);
}
// https://stackoverflow.com/questions/5130513/pbkdf2-hmac-sha2-test-vectors
TEST_F(Pkcs11Pbkdf2Test, DeriveKnown2) {
std::vector<uint8_t> derived = {
0x34,
0x8c,
0x89,
0xdb,
0xcb,
0xd3,
0x2b,
0x2f,
0x32,
0xd8,
0x14,
0xb8,
0x11,
0x6e,
0x84,
0xcf,
0x2b,
0x17,
0x34,
0x7e,
0xbc,
0x18,
0x00,
0x18,
0x1c,
0x4e,
0x2a,
0x1f,
0xb8,
0xdd,
0x53,
0xe1,
0xc6,
0x35,
0x51,
0x8c,
0x7d,
0xac,
0x47,
0xe9};
Derive(derived, SEC_OID_HMAC_SHA256);
}
TEST_F(Pkcs11Pbkdf2Test, KeyLenSizes) {
// The size controls are regardless of the algorithms.
KeySizes(SEC_OID_HMAC_SHA256);
}
}
// namespace nss_test