authorgravatar for rob@neverwood.orgRob Napier <rob@neverwood.org> 2020-09-12 18:17:04-04:00
committergravatar for rob@neverwood.orgRob Napier <rob@neverwood.org> 2020-09-12 18:17:04-04:00
log3f450b7e931b76b20e36d2bab2e2d1fed3ee19ec
tree53aed9505b2a31cd96d9905783869f65ddf286ba
parent37db93e4260dfcb90ac1553cf096a35ada1825ca

Replace Hash function with Prf. Correct offset bit-width.


1 files changed, 36 insertions(+), 15 deletions(-)

lib/std/crypto/pbkdf2.zig+36-15
...@@ -10,6 +10,14 @@ const debug = std.debug;...@@ -10,6 +10,14 @@ const debug = std.debug;
10const assert = debug.assert;10const assert = debug.assert;
11const mem = std.mem;11const mem = std.mem;
1212
13//! PBKDF2 (Password-Based Key Derivation Function 2) is a specific Key Derivation Function,
14//! intended to turn a weak, human generated password into a strong key, suitable for cryptographic
15//! uses. It does this by salting and stretching the password. Salting injects non-secret random
16//! data, so that identical passwords will be converted into unique keys. Stretching applies a
17//! deliberately slow hashing function to frustrate brute-force guessing.
18//!
19//! PBKDF2 is defined in RFC 2898, and is a recommendation of NIST SP 800-132.
20
13// RFC 2898 Section 5.221// RFC 2898 Section 5.2
14//22//
15// FromSpec:23// FromSpec:
...@@ -38,19 +46,33 @@ const mem = std.mem;...@@ -38,19 +46,33 @@ const mem = std.mem;
3846
39// Based on Apple's CommonKeyDerivation, based originally on code by Damien Bergamini.47// Based on Apple's CommonKeyDerivation, based originally on code by Damien Bergamini.
4048
41pub fn pbkdf2(derivedKey: []u8, password: []const u8, salt: []const u8, rounds: u32, comptime Hash: type) void {49/// Given a password, salt, iteration count (rounds), and a pseudo-random function, generates a
50/// derived key in the provided buffer slice.
51///
52/// derivedKey: Slice of appropriate size for generated key. Generally 16 or 32 bytes in length.
53/// May be uninitialized. All bytes will be written.
54/// Maximum size is (2^32 - 1) * Hash.digest_length
55/// It is a programming error to pass buffer longer than the maximum size.
56///
57/// password: Arbitrary sequence of bytes of any length, including empty.
58///
59/// salt: Arbitrary sequence of bytes of any length, including empty. A common length is 8 bytes.
60///
61/// rounds: Iteration count. Must be greater than 0. Common values range from 1,000 to 100,000.
62///
63/// Prf: Pseudo-random function to use. The most common choice is std.crypto.auth.hmac.HmacSha256.
64pub fn pbkdf2(derivedKey: []u8, password: []const u8, salt: []const u8, rounds: u32, comptime Prf: type) void {
42 assert(rounds >= 1);65 assert(rounds >= 1);
4366
44 const dkLen = derivedKey.len;67 const dkLen: u64 = derivedKey.len;
45 const hLen = Hash.digest_length;68 const hLen: u32 = Prf.mac_length; // Force type to ensure multiplications can't overflow
46 const Prf = crypto.auth.hmac.Hmac(Hash);
4769
48 // FromSpec:70 // FromSpec:
49 //71 //
50 // 1. If dkLen > (2^32 - 1) * hLen, output "derived key too long" and72 // 1. If dkLen > (2^32 - 1) * hLen, output "derived key too long" and
51 // stop.73 // stop.
52 //74 //
53 assert(dkLen > 0 and dkLen <= (1 << 32 - 1) * hLen);75 assert(dkLen > 0 and dkLen <= @as(u64, 1 << 32 - 1) * hLen);
5476
55 // FromSpec:77 // FromSpec:
56 //78 //
...@@ -108,7 +130,7 @@ pub fn pbkdf2(derivedKey: []u8, password: []const u8, salt: []const u8, rounds:...@@ -108,7 +130,7 @@ pub fn pbkdf2(derivedKey: []u8, password: []const u8, salt: []const u8, rounds:
108 ctx.final(prevBlock[0..]);130 ctx.final(prevBlock[0..]);
109131
110 // Choose portion of DK to write into (T_n) and initialize132 // Choose portion of DK to write into (T_n) and initialize
111 const offset: usize = block * hLen;133 const offset: u64 = @as(u64, block) * hLen;
112 const blockLen = if (block != l - 1) hLen else r;134 const blockLen = if (block != l - 1) hLen else r;
113 var dkBlock = derivedKey[offset..(offset + blockLen)];135 var dkBlock = derivedKey[offset..(offset + blockLen)];
114 mem.copy(u8, dkBlock, prevBlock[0..dkBlock.len]);136 mem.copy(u8, dkBlock, prevBlock[0..dkBlock.len]);
...@@ -138,7 +160,7 @@ test "RFC 6070 one iteration" {...@@ -138,7 +160,7 @@ test "RFC 6070 one iteration" {
138160
139 var derivedKey: [dkLen]u8 = undefined;161 var derivedKey: [dkLen]u8 = undefined;
140162
141 pbkdf2(&derivedKey, p, s, c, crypto.hash.Sha1);163 pbkdf2(&derivedKey, p, s, c, crypto.auth.hmac.HmacSha1);
142164
143 const expected = "0c60c80f961f0e71f3a9b524af6012062fe037a6";165 const expected = "0c60c80f961f0e71f3a9b524af6012062fe037a6";
144166
...@@ -153,7 +175,7 @@ test "RFC 6070 two iterations" {...@@ -153,7 +175,7 @@ test "RFC 6070 two iterations" {
153175
154 var derivedKey: [dkLen]u8 = undefined;176 var derivedKey: [dkLen]u8 = undefined;
155177
156 pbkdf2(&derivedKey, p, s, c, crypto.hash.Sha1);178 pbkdf2(&derivedKey, p, s, c, crypto.auth.hmac.HmacSha1);
157179
158 const expected = "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957";180 const expected = "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957";
159181
...@@ -168,7 +190,7 @@ test "RFC 6070 4096 iterations" {...@@ -168,7 +190,7 @@ test "RFC 6070 4096 iterations" {
168190
169 var derivedKey: [dkLen]u8 = undefined;191 var derivedKey: [dkLen]u8 = undefined;
170192
171 pbkdf2(&derivedKey, p, s, c, crypto.hash.Sha1);193 pbkdf2(&derivedKey, p, s, c, crypto.auth.hmac.HmacSha1);
172194
173 const expected = "4b007901b765489abead49d926f721d065a429c1";195 const expected = "4b007901b765489abead49d926f721d065a429c1";
174196
...@@ -188,7 +210,7 @@ test "RFC 6070 16,777,216 iterations" {...@@ -188,7 +210,7 @@ test "RFC 6070 16,777,216 iterations" {
188210
189 var derivedKey = [_]u8{0} ** dkLen;211 var derivedKey = [_]u8{0} ** dkLen;
190212
191 pbkdf2(&derivedKey, p, s, c, crypto.hash.Sha1);213 pbkdf2(&derivedKey, p, s, c, crypto.auth.hmac.HmacSha1);
192214
193 const expected = "eefe3d61cd4da4e4e9945b3d6ba2158c2634e984";215 const expected = "eefe3d61cd4da4e4e9945b3d6ba2158c2634e984";
194216
...@@ -203,7 +225,7 @@ test "RFC 6070 multi-block salt and password" {...@@ -203,7 +225,7 @@ test "RFC 6070 multi-block salt and password" {
203225
204 var derivedKey: [dkLen]u8 = undefined;226 var derivedKey: [dkLen]u8 = undefined;
205227
206 pbkdf2(&derivedKey, p, s, c, crypto.hash.Sha1);228 pbkdf2(&derivedKey, p, s, c, crypto.auth.hmac.HmacSha1);
207229
208 const expected = "3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038";230 const expected = "3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038";
209231
...@@ -218,7 +240,7 @@ test "RFC 6070 embedded NUL" {...@@ -218,7 +240,7 @@ test "RFC 6070 embedded NUL" {
218240
219 var derivedKey: [dkLen]u8 = undefined;241 var derivedKey: [dkLen]u8 = undefined;
220242
221 pbkdf2(&derivedKey, p, s, c, crypto.hash.Sha1);243 pbkdf2(&derivedKey, p, s, c, crypto.auth.hmac.HmacSha1);
222244
223 const expected = "56fa6aa75548099dcc37d7f03425e0c3";245 const expected = "56fa6aa75548099dcc37d7f03425e0c3";
224246
...@@ -226,11 +248,10 @@ test "RFC 6070 embedded NUL" {...@@ -226,11 +248,10 @@ test "RFC 6070 embedded NUL" {
226}248}
227249
228test "Very large dkLen" {250test "Very large dkLen" {
229 // These iteration tests are slow so we always skip them. Results have been verified.251 // This test allocates 8GB of memory and is expected to take several hours to run.
230 if (true) {252 if (true) {
231 return error.SkipZigTest;253 return error.SkipZigTest;
232 }254 }
233
234 const p = "password";255 const p = "password";
235 const s = "salt";256 const s = "salt";
236 const c = 1;257 const c = 1;
...@@ -241,7 +262,7 @@ test "Very large dkLen" {...@@ -241,7 +262,7 @@ test "Very large dkLen" {
241 std.testing.allocator.free(derivedKey);262 std.testing.allocator.free(derivedKey);
242 }263 }
243264
244 pbkdf2(derivedKey, p, s, c, crypto.hash.Sha1);265 pbkdf2(derivedKey, p, s, c, crypto.auth.hmac.HmacSha1);
245266
246 const expected = "0c60c80f961f0e71f3a9b524af6012062fe037a6";267 const expected = "0c60c80f961f0e71f3a9b524af6012062fe037a6";
247268