Mercurial > projects > dcrypt
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 } |