Skip to content

Commit 6e90bff

Browse files
committed
Fixed Github issue #46 in PBKDF
Proposed fix runs correctly with proposed test data. Also did some formatting in the HMAC unit tests.
1 parent 8393d4b commit 6e90bff

File tree

2 files changed

+135
-76
lines changed

2 files changed

+135
-76
lines changed

Source/DECHashAuthentication.pas

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1253,7 +1253,7 @@ class function TDECHashAuthentication.PBKDF2(const Password, Salt: TBytes; Itera
12531253
// Prepare Key for HMAC calculation
12541254
// PrepareKeyForHMAC;
12551255
I := 0;
1256-
if PassLength > BlockSize then
1256+
if (PassLength > BlockSize) then
12571257
begin
12581258
TrimmedKey := Hash.CalcBytes(Password);
12591259
PassLength := DigestLength;
@@ -1263,13 +1263,14 @@ class function TDECHashAuthentication.PBKDF2(const Password, Salt: TBytes; Itera
12631263

12641264
SetLength(InnerKeyPad, BlockSize);
12651265
SetLength(OuterKeyPad, BlockSize);
1266-
while I < PassLength do
1266+
while (I < PassLength) do
12671267
begin
12681268
InnerKeyPad[I] := TrimmedKey[I] xor $36;
12691269
OuterKeyPad[I] := TrimmedKey[I] xor $5C;
12701270
Inc(I);
12711271
end;
1272-
while I < BlockSize do
1272+
1273+
while (I < BlockSize) do
12731274
begin
12741275
InnerKeyPad[I] := $36;
12751276
OuterKeyPad[I] := $5C;
@@ -1301,19 +1302,23 @@ class function TDECHashAuthentication.PBKDF2(const Password, Salt: TBytes; Itera
13011302
U := Hash.DigestAsBytes; // Ui
13021303
// F = U1 ^ U2 ^ ... ^ Uc
13031304
J := 0;
1304-
while J < HashLengthRounded do
1305+
while (J < HashLengthRounded) do
13051306
begin
13061307
PNativeUInt(@T[J])^ := PNativeUInt(@T[J])^ xor PNativeUInt(@U[J])^;
13071308
Inc(J, SizeOf(NativeUInt));
13081309
end;
1309-
while J < DigestLength do
1310+
1311+
while (J < DigestLength) do
13101312
begin
13111313
T[J] := T[J] xor U[J];
13121314
Inc(J);
13131315
end;
13141316
end;
13151317

1316-
Result := Result + T; // DK += F , DK = DK || Ti
1318+
if (I = 1) then
1319+
Result := Copy(T)
1320+
else
1321+
Result := Result + T; // DK += F , DK = DK || Ti
13171322
end;
13181323
finally
13191324
Hash.Free;

Unit Tests/Tests/TestDECHashMAC.pas

Lines changed: 124 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -76,22 +76,32 @@ procedure TestTHash_HMAC.TestAAx80StringBytes;
7676
FillChar(AAx80String[0], 80, $AA);
7777

7878
Result := THash_MD5.HMAC(AAx80String,
79-
BytesOf('Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data'));
80-
CheckEquals('6F630FAD67CDA0EE1FB1F562DB3AA53E', StringOf(ValidFormat(TFormat_HEX).Encode(Result)),
81-
'Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data');
79+
BytesOf('Test Using Larger Than Block-Size Key and ' +
80+
'Larger Than One Block-Size Data'));
81+
CheckEquals('6F630FAD67CDA0EE1FB1F562DB3AA53E',
82+
StringOf(ValidFormat(TFormat_HEX).Encode(Result)),
83+
'Test Using Larger Than Block-Size Key and Larger Than One ' +
84+
'Block-Size Data');
8285

83-
Result := THash_MD5.HMAC(AAx80String, BytesOf('Test Using Larger Than Block-Size Key - Hash Key First'));
84-
CheckEquals('6B1AB7FE4BD7BF8F0B62E6CE61B9D0CD', StringOf(ValidFormat(TFormat_HEX).Encode(Result)),
86+
Result := THash_MD5.HMAC(AAx80String, BytesOf('Test Using Larger Than ' +
87+
'Block-Size Key - Hash Key First'));
88+
CheckEquals('6B1AB7FE4BD7BF8F0B62E6CE61B9D0CD',
89+
StringOf(ValidFormat(TFormat_HEX).Encode(Result)),
8590
'MD5 Test Using Larger Than Block-Size Key - Hash Key First');
8691

8792
Result := THash_SHA1.HMAC(AAx80String,
88-
BytesOf('Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data'));
89-
CheckEquals('E8E99D0F45237D786D6BBAA7965C7808BBFF1A91', StringOf(ValidFormat(TFormat_HEX).Encode(Result)),
90-
'SHA1 Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data');
93+
BytesOf('Test Using Larger Than Block-Size Key and ' +
94+
'Larger Than One Block-Size Data'));
95+
CheckEquals('E8E99D0F45237D786D6BBAA7965C7808BBFF1A91',
96+
StringOf(ValidFormat(TFormat_HEX).Encode(Result)),
97+
'SHA1 Test Using Larger Than Block-Size Key and Larger Than One ' +
98+
'Block-Size Data');
9199

92100
Result := THash_SHA1.HMAC(AAx80String,
93-
BytesOf('Test Using Larger Than Block-Size Key - Hash Key First'));
94-
CheckEquals('AA4AE5E15272D00E95705637CE8A3B55ED402112', StringOf(ValidFormat(TFormat_HEX).Encode(Result)),
101+
BytesOf('Test Using Larger Than Block-Size Key - ' +
102+
'Hash Key First'));
103+
CheckEquals('AA4AE5E15272D00E95705637CE8A3B55ED402112',
104+
StringOf(ValidFormat(TFormat_HEX).Encode(Result)),
95105
'SHA1 Test Using Larger Than Block-Size Key - Hash Key First');
96106
end;
97107

@@ -104,23 +114,32 @@ procedure TestTHash_HMAC.TestAAx80StringString;
104114
FillChar(AAx80String[0], 80, $AA);
105115

106116
Result := THash_MD5.HMAC(RawByteString(StringOf(AAx80String)),
107-
RawByteString('Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data'));
108-
CheckEquals('6F630FAD67CDA0EE1FB1F562DB3AA53E', StringOf(ValidFormat(TFormat_HEX).Encode(Result)),
109-
'Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data');
117+
RawByteString('Test Using Larger Than Block-Size ' +
118+
'Key and Larger Than One Block-Size Data'));
119+
CheckEquals('6F630FAD67CDA0EE1FB1F562DB3AA53E',
120+
StringOf(ValidFormat(TFormat_HEX).Encode(Result)),
121+
'Test Using Larger Than Block-Size Key and Larger Than One ' +
122+
'Block-Size Data');
110123

111124
Result := THash_MD5.HMAC(RawByteString(StringOf(AAx80String)),
112125
RawByteString('Test Using Larger Than Block-Size Key - Hash Key First'));
113-
CheckEquals('6B1AB7FE4BD7BF8F0B62E6CE61B9D0CD', StringOf(ValidFormat(TFormat_HEX).Encode(Result)),
126+
CheckEquals('6B1AB7FE4BD7BF8F0B62E6CE61B9D0CD',
127+
StringOf(ValidFormat(TFormat_HEX).Encode(Result)),
114128
'MD5 Test Using Larger Than Block-Size Key - Hash Key First');
115129

116130
Result := THash_SHA1.HMAC(RawByteString(StringOf(AAx80String)),
117-
RawByteString('Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data'));
118-
CheckEquals('E8E99D0F45237D786D6BBAA7965C7808BBFF1A91', StringOf(ValidFormat(TFormat_HEX).Encode(Result)),
119-
'SHA1 Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data');
131+
RawByteString('Test Using Larger Than Block-Size ' +
132+
'Key and Larger Than One Block-Size Data'));
133+
CheckEquals('E8E99D0F45237D786D6BBAA7965C7808BBFF1A91',
134+
StringOf(ValidFormat(TFormat_HEX).Encode(Result)),
135+
'SHA1 Test Using Larger Than Block-Size Key and Larger Than One ' +
136+
'Block-Size Data');
120137

121138
Result := THash_SHA1.HMAC(RawByteString(StringOf(AAx80String)),
122-
RawByteString('Test Using Larger Than Block-Size Key - Hash Key First'));
123-
CheckEquals('AA4AE5E15272D00E95705637CE8A3B55ED402112', StringOf(ValidFormat(TFormat_HEX).Encode(Result)),
139+
RawByteString('Test Using Larger Than Block-Size ' +
140+
'Key - Hash Key First'));
141+
CheckEquals('AA4AE5E15272D00E95705637CE8A3B55ED402112',
142+
StringOf(ValidFormat(TFormat_HEX).Encode(Result)),
124143
'SHA1 Test Using Larger Than Block-Size Key - Hash Key First');
125144
end;
126145

@@ -129,15 +148,20 @@ procedure TestTHash_HMAC.TestBytes;
129148
Res: TBytes;
130149
begin
131150
// test vectors from https://en.wikipedia.org/wiki/HMAC
132-
Res := THash_MD5.HMAC(BytesOf('key'), BytesOf('The quick brown fox jumps over the lazy dog'));
133-
CheckEquals('80070713463E7749B90C2DC24911E275', StringOf(ValidFormat(TFormat_HEX).Encode(Res)),
151+
Res := THash_MD5.HMAC(BytesOf('key'), BytesOf('The quick brown fox jumps ' +
152+
'over the lazy dog'));
153+
CheckEquals('80070713463E7749B90C2DC24911E275',
154+
StringOf(ValidFormat(TFormat_HEX).Encode(Res)),
134155
'MD5 failure in The quick...');
135156

136-
Res := THash_SHA1.HMAC(BytesOf('key'), BytesOf('The quick brown fox jumps over the lazy dog'));
137-
CheckEquals('DE7C9B85B8B78AA6BC8A7A36F70A90701C9DB4D9', StringOf(ValidFormat(TFormat_HEX).Encode(Res)),
157+
Res := THash_SHA1.HMAC(BytesOf('key'), BytesOf('The quick brown fox jumps ' +
158+
'over the lazy dog'));
159+
CheckEquals('DE7C9B85B8B78AA6BC8A7A36F70A90701C9DB4D9',
160+
StringOf(ValidFormat(TFormat_HEX).Encode(Res)),
138161
'SHA1 failure in The quick...');
139162

140-
Res := THash_SHA256.HMAC(BytesOf('key'), BytesOf('The quick brown fox jumps over the lazy dog'));
163+
Res := THash_SHA256.HMAC(BytesOf('key'), BytesOf('The quick brown fox jumps ' +
164+
'over the lazy dog'));
141165
CheckEquals('F7BC83F430538424B13298E6AA6FB143EF4D59A14946175997479DBC2D1A3CD8',
142166
StringOf(ValidFormat(TFormat_HEX).Encode(Res)),
143167
'SHA256 failure in The quick...');
@@ -148,15 +172,20 @@ procedure TestTHash_HMAC.TestRawByteString;
148172
Result: TBytes;
149173
begin
150174
// test vectors from https://en.wikipedia.org/wiki/HMAC
151-
Result := THash_MD5.HMAC(RawByteString('key'), RawByteString('The quick brown fox jumps over the lazy dog'));
152-
CheckEquals('80070713463E7749B90C2DC24911E275', StringOf(ValidFormat(TFormat_HEX).Encode(Result)),
175+
Result := THash_MD5.HMAC(RawByteString('key'),
176+
RawByteString('The quick brown fox jumps over the lazy dog'));
177+
CheckEquals('80070713463E7749B90C2DC24911E275',
178+
StringOf(ValidFormat(TFormat_HEX).Encode(Result)),
153179
'MD5 failure in The quick...');
154180

155-
Result := THash_SHA1.HMAC(RawByteString('key'), RawByteString('The quick brown fox jumps over the lazy dog'));
156-
CheckEquals('DE7C9B85B8B78AA6BC8A7A36F70A90701C9DB4D9', StringOf(ValidFormat(TFormat_HEX).Encode(Result)),
181+
Result := THash_SHA1.HMAC(RawByteString('key'),
182+
RawByteString('The quick brown fox jumps over the lazy dog'));
183+
CheckEquals('DE7C9B85B8B78AA6BC8A7A36F70A90701C9DB4D9',
184+
StringOf(ValidFormat(TFormat_HEX).Encode(Result)),
157185
'SHA1 failure in The quick...');
158186

159-
Result := THash_SHA256.HMAC(RawByteString('key'), RawByteString('The quick brown fox jumps over the lazy dog'));
187+
Result := THash_SHA256.HMAC(RawByteString('key'),
188+
RawByteString('The quick brown fox jumps over the lazy dog'));
160189
CheckEquals('F7BC83F430538424B13298E6AA6FB143EF4D59A14946175997479DBC2D1A3CD8',
161190
StringOf(ValidFormat(TFormat_HEX).Encode(Result)),
162191
'SHA256 failure in The quick...');
@@ -168,56 +197,81 @@ procedure TestTHash_PBKDF2.TestBug46;
168197
var
169198
Result : string;
170199
begin
171-
Result := StringOf(ValidFormat(TFormat_HEXL).Encode(THash_SHA1.PBKDF2(BytesOf('PassWord'),
172-
BytesOf('Salt'), 1000, 16)));
173-
CheckEquals('05e7b33f93ea1d35', result, 'SHA1 password salt 1000 16');
174-
175-
// Result := StringOf(ValidFormat(TFormat_HEXL).Encode(THash_SHA1.PBKDF2(BytesOf('PassWord'),
176-
// BytesOf('Salt'), 1000, 32)));
177-
// CheckEquals('05e7b33f93ea1d35939b32ad5994ef63', result, 'SHA1 password salt 1000 32');
178-
//
179-
// Result := StringOf(ValidFormat(TFormat_HEXL).Encode(THash_SHA1.PBKDF2(BytesOf('PassWord'),
180-
// BytesOf('Salt'), 1000, 48)));
181-
// CheckEquals('05e7b33f93ea1d35939b32ad5994ef6314f6170b191368e9', result, 'SHA1 password salt 1000 48');
200+
Result := StringOf(ValidFormat(TFormat_HEX).Encode(THash_SHA1.PBKDF2('password',
201+
'salt', 1, 16)));
202+
CheckEquals('0C60C80F961F0E71F3A9B524AF601206', result, 'SHA1 password salt 1 16');
203+
204+
Result := StringOf(ValidFormat(TFormat_HEX).Encode(THash_SHA1.PBKDF2('password',
205+
'salt', 1, 20)));
206+
CheckEquals('0C60C80F961F0E71F3A9B524AF6012062FE037A6', result, 'SHA1 password salt 1 20');
207+
208+
Result := StringOf(ValidFormat(TFormat_HEX).Encode(THash_SHA1.PBKDF2('password',
209+
'salt', 1, 32)));
210+
CheckEquals('0C60C80F961F0E71F3A9B524AF6012062FE037A6E0F0EB94FE8FC46BDC637164',
211+
Result, 'SHA1 password salt 1 32');
212+
213+
// This is testing 3 blocks
214+
Result := StringOf(ValidFormat(TFormat_HEX).Encode(THash_SHA1.PBKDF2('password',
215+
'salt', 1, 48)));
216+
CheckEquals('0C60C80F961F0E71F3A9B524AF6012062FE037A6E0F0EB94FE8FC46BDC63' +
217+
'7164AC2E7A8E3F9D2E83ACE57E0D50E5E107', Result, 'SHA1 password salt 1 48');
182218
end;
183219

184220
procedure TestTHash_PBKDF2.TestBytes;
185221
var
186222
Result : string;
187223
begin
188-
result := StringOf(ValidFormat(TFormat_HEX).Encode(THash_SHA1.PBKDF2(BytesOf('password'), BytesOf('salt'), 1, 20)));
189-
CheckEquals('0C60C80F961F0E71F3A9B524AF6012062FE037A6', result, 'SHA1 password salt 1 20');
190-
191-
result := StringOf(ValidFormat(TFormat_HEX).Encode(THash_SHA256.PBKDF2(BytesOf('password'), BytesOf('salt'), 1, 32)));
192-
CheckEquals('120FB6CFFCF8B32C43E7225256C4F837A86548C92CCC35480805987CB70BE17B', result, 'SHA256 password salt 1 32');
193-
194-
result := StringOf(ValidFormat(TFormat_HEX).Encode(THash_SHA256.PBKDF2(BytesOf('password'), BytesOf('salt'), 2, 32)));
195-
CheckEquals('AE4D0C95AF6B46D32D0ADFF928F06DD02A303F8EF3C251DFD6E2D85A95474C43', result, 'SHA256 password salt 2 32');
196-
197-
result := StringOf(ValidFormat(TFormat_HEX).Encode(THash_SHA256.PBKDF2(BytesOf('password'), BytesOf('salt'), 4096, 32)));
198-
CheckEquals('C5E478D59288C841AA530DB6845C4C8D962893A001CE4E11A4963873AA98134A', result, 'SHA256 password salt 4096 32');
199-
200-
// PBKDF2-HMAC-SHA512 test vectors from https://stackoverflow.com/questions/15593184/pbkdf2-hmac-sha-512-test-vectors
201-
result := StringOf(ValidFormat(TFormat_HEX).Encode(THash_SHA512.PBKDF2(BytesOf('password'), BytesOf('salt'), 1, 64)));
202-
CheckEquals('867F70CF1ADE02CFF3752599A3A53DC4AF34C7A669815AE5D513554E1C8CF252C02D470A285A0501BAD999BFE943C08F050235D7D68B1DA55E63F73B60A57FCE',
203-
result, 'SHA512 password salt 1 64');
204-
205-
result := StringOf(ValidFormat(TFormat_HEX).Encode(THash_SHA512.PBKDF2(BytesOf('password'), BytesOf('salt'), 2, 64)));
206-
CheckEquals('E1D9C16AA681708A45F5C7C4E215CEB66E011A2E9F0040713F18AEFDB866D53CF76CAB2868A39B9F7840EDCE4FEF5A82BE67335C77A6068E04112754F27CCF4E',
207-
result, 'SHA512 password salt 2 64');
208-
209-
result := StringOf(ValidFormat(TFormat_HEX).Encode(THash_SHA512.PBKDF2(BytesOf('password'), BytesOf('salt'), 4096, 64)));
210-
CheckEquals('D197B1B33DB0143E018B12F3D1D1479E6CDEBDCC97C5C0F87F6902E072F457B5143F30602641B3D55CD335988CB36B84376060ECD532E039B742A239434AF2D5',
211-
result, 'SHA512 password salt 4096 64');
212-
213-
result := StringOf(ValidFormat(TFormat_HEX).Encode(THash_SHA256.PBKDF2(BytesOf('password'), BytesOf('salt'), 1000000, 32)));
224+
Result := StringOf(ValidFormat(TFormat_HEX).Encode(THash_SHA1.PBKDF2(BytesOf('password'),
225+
BytesOf('salt'), 1, 20)));
226+
CheckEquals('0C60C80F961F0E71F3A9B524AF6012062FE037A6',
227+
Result, 'SHA1 password salt 1 20');
228+
229+
Result := StringOf(ValidFormat(TFormat_HEX).Encode(THash_SHA256.PBKDF2(BytesOf('password'),
230+
BytesOf('salt'), 1, 32)));
231+
CheckEquals('120FB6CFFCF8B32C43E7225256C4F837A86548C92CCC35480805987CB70BE17B',
232+
Result, 'SHA256 password salt 1 32');
233+
234+
Result := StringOf(ValidFormat(TFormat_HEX).Encode(THash_SHA256.PBKDF2(BytesOf('password'),
235+
BytesOf('salt'), 2, 32)));
236+
CheckEquals('AE4D0C95AF6B46D32D0ADFF928F06DD02A303F8EF3C251DFD6E2D85A95474C43',
237+
Result, 'SHA256 password salt 2 32');
238+
239+
Result := StringOf(ValidFormat(TFormat_HEX).Encode(THash_SHA256.PBKDF2(BytesOf('password'),
240+
BytesOf('salt'), 4096, 32)));
241+
CheckEquals('C5E478D59288C841AA530DB6845C4C8D962893A001CE4E11A4963873AA98134A',
242+
Result, 'SHA256 password salt 4096 32');
243+
244+
// PBKDF2-HMAC-SHA512 test vectors from
245+
// https://stackoverflow.com/questions/15593184/pbkdf2-hmac-sha-512-test-vectors
246+
Result := StringOf(ValidFormat(TFormat_HEX).Encode(THash_SHA512.PBKDF2(BytesOf('password'),
247+
BytesOf('salt'), 1, 64)));
248+
CheckEquals('867F70CF1ADE02CFF3752599A3A53DC4AF34C7A669815AE5D513554E1C8CF252C' +
249+
'02D470A285A0501BAD999BFE943C08F050235D7D68B1DA55E63F73B60A57FCE',
250+
Result, 'SHA512 password salt 1 64');
251+
252+
Result := StringOf(ValidFormat(TFormat_HEX).Encode(THash_SHA512.PBKDF2(BytesOf('password'),
253+
BytesOf('salt'), 2, 64)));
254+
CheckEquals('E1D9C16AA681708A45F5C7C4E215CEB66E011A2E9F0040713F18AEFDB866D53CF' +
255+
'76CAB2868A39B9F7840EDCE4FEF5A82BE67335C77A6068E04112754F27CCF4E',
256+
Result, 'SHA512 password salt 2 64');
257+
258+
Result := StringOf(ValidFormat(TFormat_HEX).Encode(THash_SHA512.PBKDF2(BytesOf('password'),
259+
BytesOf('salt'), 4096, 64)));
260+
CheckEquals('D197B1B33DB0143E018B12F3D1D1479E6CDEBDCC97C5C0F87F6902E072F457B51' +
261+
'43F30602641B3D55CD335988CB36B84376060ECD532E039B742A239434AF2D5',
262+
Result, 'SHA512 password salt 4096 64');
263+
264+
Result := StringOf(ValidFormat(TFormat_HEX).Encode(THash_SHA256.PBKDF2(BytesOf('password'),
265+
BytesOf('salt'), 1000000, 32)));
214266
CheckEquals('505112A590BE61AC9D3A235BF0A8EECEA40E54652EC0E3C257C227C9AA5E664C',
215-
result, 'SHA256 password salt 1000000 32');
267+
Result, 'SHA256 password salt 1000000 32');
216268

217269
// test with a password longer than block length
218-
result := StringOf(ValidFormat(TFormat_HEX).Encode(
219-
THash_SHA1.PBKDF2(BytesOf('passwordabcdefghijklmnopqrstuvwxyzpasswordabcdefghijklmnopqrstuvwxyz'), BytesOf('salt'), 1, 20)));
220-
CheckEquals('2945F3DD721E717BA7676B7D7447423B158DD3A4', result, 'SHA1 password salt 1 20');
270+
Result := StringOf(ValidFormat(TFormat_HEX).Encode(
271+
THash_SHA1.PBKDF2(BytesOf('passwordabcdefghijklmnopqrstuvwxyzpasswordabcdefg' +
272+
'hijklmnopqrstuvwxyz'), BytesOf('salt'), 1, 20)));
273+
CheckEquals('2945F3DD721E717BA7676B7D7447423B158DD3A4',
274+
Result, 'SHA1 password salt 1 20');
221275
end;
222276

223277
procedure TestTHash_PBKDF2.TestRawByteString;

0 commit comments

Comments
 (0)