comparison dcrypt/crypto/prngs/PBKDF2.d @ 15:0de48552be35

Added LimitReachedError and PBKDF2. Fixed some errors with the previous commit in PRNGFromHash, etc. Re-implemented HMAC. Changed the name() format of HMAC and PBKDF2.
author Thomas Dixon <reikon@reikon.us>
date Wed, 19 Nov 2008 19:30:52 -0500
parents
children 4589f8c5eb3c
comparison
equal deleted inserted replaced
14:5ce3012f1def 15:0de48552be35
1 /**
2 * This file is part of the dcrypt project.
3 *
4 * Copyright: Copyright (C) dcrypt contributors 2008. All rights reserved.
5 * License: MIT
6 * Authors: Thomas Dixon
7 */
8
9 module dcrypt.crypto.prngs.PBKDF2;
10
11 import dcrypt.misc.Util;
12 import dcrypt.crypto.PRNG;
13 import dcrypt.crypto.MAC;
14 import dcrypt.crypto.macs.HMAC;
15 import dcrypt.crypto.hashes.SHA1;
16 import dcrypt.crypto.errors.LimitReachedError;
17
18 /**
19 * Implementation of RSA Security's Password-Based Key Derivation Function 2
20 *
21 * Conforms: PKCS #5 v2.0 / RFC 2898
22 * References: http://www.truecrypt.org/docs/pkcs5v2-0.pdf
23 */
24 class PBKDF2 : PRNG {
25 private {
26 ubyte[] salt,
27 buffer;
28
29 char[] password;
30
31 MAC prf;
32
33 uint iterations,
34 blockCount,
35 index;
36 }
37 /**
38 * Params:
39 * password = User supplied password
40 * salt = (preferably random) salt
41 * iterations = The number of total iterations
42 * prf = The pseudo-random function
43 */
44 this(char[] password, void[] salt_,
45 uint iterations=1000, MAC prf=new HMAC(new SHA1)) {
46
47 salt = cast(ubyte[])salt_;
48 if (salt == null)
49 throw new InvalidParameterError(name()~": No salt specified.");
50
51 this.password = password;
52 if (this.password == null)
53 throw new InvalidParameterError(name()~": No password specified.");
54
55 this.prf = prf;
56 if (this.prf is null)
57 throw new InvalidParameterError(name()~": No PRF specified.");
58
59 this.iterations = iterations;
60
61 prf.init(new SymmetricKey(cast(ubyte[])this.password));
62 blockCount = 0;
63 buffer = new ubyte[this.prf.macSize];
64 index = this.prf.macSize;
65 _initialized = true;
66 }
67
68 void addEntropy(ubyte[] input) {
69 throw new NotSupportedError(name()~": Not supported.");
70 }
71
72 /**
73 * Throws: LimitReachedError after 2^32 blocks.
74 */
75 uint read(ubyte[] output) {
76 for (uint i = 0; i < output.length; i++) {
77 if (index == buffer.length) {
78 if (++blockCount == 0) // Catch rollover
79 throw new LimitReachedError(name()~": Output limit reached.");
80
81 buffer[] = 0;
82
83 ubyte[] t = new ubyte[salt.length + uint.sizeof];
84 t[0..salt.length] = salt;
85 Util.uintToUbytesBig(blockCount, t, salt.length);
86
87 for (uint j = 0; j < iterations; j++) {
88 prf.reset();
89 prf.update(t);
90 t = prf.digest();
91
92 for (uint k = 0; k < buffer.length; k++)
93 buffer[k] ^= t[k];
94 }
95 index = 0;
96 }
97 output[i] = buffer[index++];
98 }
99
100 return output.length;
101 }
102
103 char[] name() {
104 return "PBKDF2-"~prf.name;
105 }
106
107 version (UnitTest) {
108 unittest {
109 static char[][] test_passwords = [
110 "password",
111 "password",
112 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"~
113 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
114 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"~
115 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
116 ];
117
118 static char[][] test_salts = [
119 "ATHENA.MIT.EDUraeburn",
120 "ATHENA.MIT.EDUraeburn",
121 "pass phrase equals block size",
122 "pass phrase exceeds block size"
123 ];
124
125 static const int[] test_iterations = [
126 1, 1200, 1200, 1200
127 ];
128
129 static const char[][] test_results = [
130 "cdedb5281bb2f801565a1122b2563515",
131 "5c08eb61fdf71e4e4ec3cf6ba1f5512b"~
132 "a7e52ddbc5e5142f708a31e2e62b1e13",
133 "139c30c0966bc32ba55fdbf212530ac9"~
134 "c5ec59f1a452f5cc9ad940fea0598ed1",
135 "9ccad6d468770cd51b10e6a68721be61"~
136 "1a8b4d282601db3b36be9246915ec82a"
137 ];
138
139 PBKDF2 pbkdf2;
140 foreach (uint i, char[] p; test_passwords) {
141 pbkdf2 = new PBKDF2(p, test_salts[i], test_iterations[i]);
142 ubyte[] result = new ubyte[test_results[i].length >> 1];
143 pbkdf2.read(result);
144 char[] hexResult = Util.ubytesToHex(result);
145 assert(hexResult == test_results[i],
146 pbkdf2.name~": ("~hexResult~") != ("~test_results[i]~")");
147 }
148 }
149 }
150 }