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],