Mercurial > projects > dcrypt
comparison dcrypt/crypto/prngs/PBKDF2.d @ 27:8b5eaf3c2979
Fixed error in hash message padding reported by Glenn Haecker.
author | Thomas Dixon <reikon@reikon.us> |
---|---|
date | Sat, 09 May 2009 23:29:20 -0400 |
parents | 176c933827a8 |
children | ad687db713a4 |
comparison
equal
deleted
inserted
replaced
26:176c933827a8 | 27:8b5eaf3c2979 |
---|---|
19 * Implementation of RSA Security's Password-Based Key Derivation Function 2 | 19 * Implementation of RSA Security's Password-Based Key Derivation Function 2 |
20 * | 20 * |
21 * Conforms: PKCS #5 v2.0 / RFC 2898 | 21 * Conforms: PKCS #5 v2.0 / RFC 2898 |
22 * References: http://www.truecrypt.org/docs/pkcs5v2-0.pdf | 22 * References: http://www.truecrypt.org/docs/pkcs5v2-0.pdf |
23 */ | 23 */ |
24 class PBKDF2 : PRNG { | 24 class PBKDF2 : PRNG |
25 private { | 25 { |
26 private | |
27 { | |
26 ubyte[] salt, | 28 ubyte[] salt, |
27 buffer; | 29 buffer; |
28 | 30 |
29 char[] password; | 31 char[] password; |
30 | 32 |
40 * password = User supplied password | 42 * password = User supplied password |
41 * salt = (preferably random) salt | 43 * salt = (preferably random) salt |
42 * iterations = The number of total iterations | 44 * iterations = The number of total iterations |
43 * prf = The pseudo-random function | 45 * prf = The pseudo-random function |
44 */ | 46 */ |
45 this(char[] password, void[] salt_, | 47 this(char[] password, void[] salt_, uint iterations=1000, MAC prf=new HMAC(new SHA1)) |
46 uint iterations=1000, MAC prf=new HMAC(new SHA1)) { | 48 { |
47 | 49 |
48 salt = cast(ubyte[])salt_; | 50 salt = cast(ubyte[])salt_; |
49 if (salt == null) | 51 if (salt == null) |
50 throw new InvalidParameterError(name()~": No salt specified."); | 52 throw new InvalidParameterError(name()~": No salt specified."); |
51 | 53 |
61 | 63 |
62 prf.init(new SymmetricKey(cast(ubyte[])this.password)); | 64 prf.init(new SymmetricKey(cast(ubyte[])this.password)); |
63 blockCount = 0; | 65 blockCount = 0; |
64 buffer = new ubyte[this.prf.macSize]; | 66 buffer = new ubyte[this.prf.macSize]; |
65 index = this.prf.macSize; | 67 index = this.prf.macSize; |
68 | |
66 _initialized = true; | 69 _initialized = true; |
67 } | 70 } |
68 | 71 |
69 void addEntropy(ubyte[] input) { | 72 void addEntropy(ubyte[] input) |
73 { | |
70 throw new NotSupportedError(name()~": Not supported."); | 74 throw new NotSupportedError(name()~": Not supported."); |
71 } | 75 } |
72 | 76 |
73 /** | 77 /** |
74 * Throws: LimitReachedError after 2^32 blocks. | 78 * Throws: LimitReachedError after 2^32 blocks. |
75 */ | 79 */ |
76 uint read(ubyte[] output) { | 80 uint read(ubyte[] output) |
77 for (uint i = 0; i < output.length; i++) { | 81 { |
78 if (index == buffer.length) { | 82 for (uint i = 0; i < output.length; i++) |
83 { | |
84 if (index == buffer.length) | |
85 { | |
79 if (++blockCount == 0) // Catch rollover | 86 if (++blockCount == 0) // Catch rollover |
80 throw new LimitReachedError(name()~": Output limit reached."); | 87 throw new LimitReachedError(name()~": Output limit reached."); |
81 | 88 |
82 buffer[] = 0; | 89 buffer[] = 0; |
83 | 90 |
84 ubyte[] t = new ubyte[salt.length + uint.sizeof]; | 91 ubyte[] t = new ubyte[salt.length + uint.sizeof]; |
85 t[0..salt.length] = salt; | 92 t[0..salt.length] = salt; |
86 t[salt.length..salt.length+int.sizeof] = ByteConverter.BigEndian.from!(uint)(blockCount); | 93 t[salt.length..salt.length+int.sizeof] = ByteConverter.BigEndian.from!(uint)(blockCount); |
87 | 94 |
88 for (uint j = 0; j < iterations; j++) { | 95 for (uint j = 0; j < iterations; j++) |
96 { | |
89 prf.reset(); | 97 prf.reset(); |
90 prf.update(t); | 98 prf.update(t); |
91 t = prf.digest(); | 99 t = prf.digest(); |
92 | 100 |
93 for (uint k = 0; k < buffer.length; k++) | 101 for (uint k = 0; k < buffer.length; k++) |
94 buffer[k] ^= t[k]; | 102 buffer[k] ^= t[k]; |
95 } | 103 } |
104 | |
96 index = 0; | 105 index = 0; |
97 } | 106 } |
107 | |
98 output[i] = buffer[index++]; | 108 output[i] = buffer[index++]; |
99 } | 109 } |
100 | 110 |
101 return output.length; | 111 return output.length; |
102 } | 112 } |
103 | 113 |
104 char[] name() { | 114 char[] name() |
115 { | |
105 return "PBKDF2-"~prf.name; | 116 return "PBKDF2-"~prf.name; |
106 } | 117 } |
107 | 118 |
108 debug (UnitTest) { | 119 debug (UnitTest) |
109 unittest { | 120 { |
121 unittest | |
122 { | |
110 static char[][] test_passwords = [ | 123 static char[][] test_passwords = [ |
111 "password", | 124 "password", |
112 "password", | 125 "password", |
113 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"~ | 126 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"~ |
114 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", | 127 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", |
136 "9ccad6d468770cd51b10e6a68721be61"~ | 149 "9ccad6d468770cd51b10e6a68721be61"~ |
137 "1a8b4d282601db3b36be9246915ec82a" | 150 "1a8b4d282601db3b36be9246915ec82a" |
138 ]; | 151 ]; |
139 | 152 |
140 PBKDF2 pbkdf2; | 153 PBKDF2 pbkdf2; |
141 foreach (uint i, char[] p; test_passwords) { | 154 foreach (uint i, char[] p; test_passwords) |
155 { | |
142 pbkdf2 = new PBKDF2(p, test_salts[i], test_iterations[i]); | 156 pbkdf2 = new PBKDF2(p, test_salts[i], test_iterations[i]); |
143 ubyte[] result = new ubyte[test_results[i].length >> 1]; | 157 ubyte[] result = new ubyte[test_results[i].length >> 1]; |
144 pbkdf2.read(result); | 158 pbkdf2.read(result); |
145 char[] hexResult = ByteConverter.hexEncode(result); | 159 char[] hexResult = ByteConverter.hexEncode(result); |
146 assert(hexResult == test_results[i], | 160 assert(hexResult == test_results[i], |