Delphi HMAC-SHA1 unexpected results

1.3k Views Asked by At

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?

1

There are 1 best solutions below

0
On

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...

//current check if > length
if Length(Key) > 64 then
    Key := SHA1(Key);

//also check < length
if Length(Key) < 64 then
   Key := Key + StringOfChar(#0, 64 - Length(Key));

Note - much better with Byte Arrays