1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
|
/* -*- 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
|