I am writing in an old version of Delphi (Delphi 5) for reasons I could not possibly explain in this thread. I am trying to implement an HOTP and currently all of my code is working short of the HMACSHA1 portion and I do not understand why. I implemented HMACSHA as per RFC 2202 and I compared my results to numerous other HMACSHA1 generators. All of these matched.
I then began implementing RFC 4226 for HOTP and was surprised to see that my intermediate HMACSHA1 results were wrong when compared to the sample data they provided at the bottom of the RFC (https://www.ietf.org/rfc/rfc4226.txt).
Here is a test vector from the RFC
The following test data uses the ASCII string "12345678901234567890" for the secret:
Secret = 0x3132333435363738393031323334353637383930
Table 1 details for each count, the intermediate HMAC value.
Count Hexadecimal HMAC-SHA-1(secret, count)
0 cc93cf18508d94934c64b65d8ba7667fb7cde4b0
But, using my HMACSHA1 function as shown below:
function HMAC_SHA1(Text, Key: AnsiString): AnsiString;
var
ipad, opad, s: AnsiString;
n: Integer;
SHA1Context: TSHA1Ctx;
begin
if Length(Key) > 64 then
Key := SHA1(Key);
ipad := StringOfChar(#$36, 64);
opad := StringOfChar(#$5C, 64);
for n := 1 to Length(Key) do
begin
ipad[n] := AnsiChar(Byte(ipad[n]) xor Byte(Key[n]));
opad[n] := AnsiChar(Byte(opad[n]) xor Byte(Key[n]));
end;
SHA1Init(SHA1Context);
SHA1Update(SHA1Context, ipad);
SHA1Update(SHA1Context, Text);
s := SHA1Final(SHA1Context);
SHA1Init(SHA1Context);
SHA1Update(SHA1Context, opad);
SHA1Update(SHA1Context, s);
Result := SHA1Final(SHA1Context);
end;
I attempt:
HMAC_SHA1('12345678901234567890', '0');
and my answer comes back as
948d4b44f3e0aac05904d6fd82ab7b8bbe761a4c
Which is the same as the online generator linked to below
http://www.freeformatter.com/hmac-generator.html#ad-output
So, what am I doing wrong?
At least one issue - you are checking for keys > blocksize (64), but you also have to check for Keys shorter than blocksize (64) which need to be right-padded with zeros.
Something like this in your code above...
Note - much better with Byte Arrays