<div dir="ltr">2013/3/23 David Butler <span dir="ltr"><<a href="mailto:djbutler@gmail.com" target="_blank">djbutler@gmail.com</a>></span><br><div class="gmail_extra"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">

What do you mean by "native"? <div><br></div><div>It is pure pascal code that compiles under Delphi and FreePascal.</div><div><br></div><div>Using it is as easy as:</div><div><br></div><div>SHA1DigestToHexA(CalcHMAC_SHA1('secret', 'message')</div>

</blockquote><div><br></div><div style>To not implement a big code like this:</div><div style><br></div><div style><div>unit Unit1;</div><div><br></div><div>{$mode objfpc}{$H+}</div><div><br></div><div>interface</div><div>

<br></div><div>uses</div><div>  Forms, StdCtrls;</div><div><br></div><div>type</div><div>  TForm1 = class(TForm)</div><div>    Button1: TButton;</div><div>    Edit1: TEdit;</div><div>    procedure Button1Click(Sender: TObject);</div>

<div>  end;</div><div><br></div><div>  T512BitBuf  = array[0..63] of Byte;</div><div><br></div><div>  T160BitDigest = record</div><div>    case integer of</div><div>      0 : (Longs : array[0..4] of LongWord);</div><div>
      1 : (Words : array[0..9] of Word);</div>
<div>      2 : (Bytes : array[0..19] of Byte);</div><div>    end;</div><div><br></div><div>var</div><div>  Form1: TForm1;</div><div><br></div><div>implementation</div><div><br></div><div>{$R *.lfm}</div><div><br></div><div>

procedure SHA1InitDigest(var Digest: T160BitDigest);</div><div>begin</div><div>  Digest.Longs[0] := $67452301;</div><div>  Digest.Longs[1] := $EFCDAB89;</div><div>  Digest.Longs[2] := $98BADCFE;</div><div>  Digest.Longs[3] := $10325476;</div>

<div>  Digest.Longs[4] := $C3D2E1F0;</div><div>end;</div><div><br></div><div>function RotateLeftBits(const Value: LongWord; const Bits: Byte): LongWord;</div><div>var I : Integer;</div><div>begin</div><div>  Result := Value;</div>

<div>  for I := 1 to Bits do</div><div>    if Result and $80000000 = 0 then</div><div>      Result := Value shl 1 else</div><div>      Result := (Value shl 1) or 1;</div><div>end;</div><div><br></div><div>procedure TransformSHABuffer(var Digest: T160BitDigest; const Buffer; const SHA1: Boolean);</div>

<div>var A, B, C, D, E : LongWord;</div><div>    W : array[0..79] of LongWord;</div><div>    P, Q : PLongWord;</div><div>    I : Integer;</div><div>    J : LongWord;</div><div>begin</div><div>  P := @Buffer;</div><div>  Q := @W;</div>

<div>  for I := 0 to 15 do</div><div>    begin</div><div>      Q^ := SwapEndian(P^);</div><div>      Inc(P);</div><div>      Inc(Q);</div><div>    end;</div><div>  for I := 0 to 63 do</div><div>    begin</div><div>      P := Q;</div>

<div>      Dec(P, 16);</div><div>      J := P^;</div><div>      Inc(P, 2);</div><div>      J := J xor P^;</div><div>      Inc(P, 6);</div><div>      J := J xor P^;</div><div>      Inc(P, 5);</div><div>      J := J xor P^;</div>

<div>      if SHA1 then</div><div>        J := RotateLeftBits(J, 1);</div><div>      Q^ := J;</div><div>      Inc(Q);</div><div>    end;</div><div><br></div><div>  A := Digest.Longs[0];</div><div>  B := Digest.Longs[1];</div>

<div>  C := Digest.Longs[2];</div><div>  D := Digest.Longs[3];</div><div>  E := Digest.Longs[4];</div><div><br></div><div>  P := @W;</div><div>  for I := 0 to 3 do</div><div>    begin</div><div>      Inc(E, (A shl 5 or A shr 27) + (D xor (B and (C xor D))) + P^ + $5A827999); B := B shr 2 or B shl 30; Inc(P);</div>

<div>      Inc(D, (E shl 5 or E shr 27) + (C xor (A and (B xor C))) + P^ + $5A827999); A := A shr 2 or A shl 30; Inc(P);</div><div>      Inc(C, (D shl 5 or D shr 27) + (B xor (E and (A xor B))) + P^ + $5A827999); E := E shr 2 or E shl 30; Inc(P);</div>

<div>      Inc(B, (C shl 5 or C shr 27) + (A xor (D and (E xor A))) + P^ + $5A827999); D := D shr 2 or D shl 30; Inc(P);</div><div>      Inc(A, (B shl 5 or B shr 27) + (E xor (C and (D xor E))) + P^ + $5A827999); C := C shr 2 or C shl 30; Inc(P);</div>

<div>    end;</div><div><br></div><div>  for I := 0 to 3 do</div><div>    begin</div><div>      Inc(E, (A shl 5 or A shr 27) + (D xor B xor C) + P^ + $6ED9EBA1); B := B shr 2 or B shl 30; Inc(P);</div><div>      Inc(D, (E shl 5 or E shr 27) + (C xor A xor B) + P^ + $6ED9EBA1); A := A shr 2 or A shl 30; Inc(P);</div>

<div>      Inc(C, (D shl 5 or D shr 27) + (B xor E xor A) + P^ + $6ED9EBA1); E := E shr 2 or E shl 30; Inc(P);</div><div>      Inc(B, (C shl 5 or C shr 27) + (A xor D xor E) + P^ + $6ED9EBA1); D := D shr 2 or D shl 30; Inc(P);</div>

<div>      Inc(A, (B shl 5 or B shr 27) + (E xor C xor D) + P^ + $6ED9EBA1); C := C shr 2 or C shl 30; Inc(P);</div><div>    end;</div><div><br></div><div>  for I := 0 to 3 do</div><div>    begin</div><div>      Inc(E, (A shl 5 or A shr 27) + ((B and C) or (D and (B or C))) + P^ + $8F1BBCDC); B := B shr 2 or B shl 30; Inc(P);</div>

<div>      Inc(D, (E shl 5 or E shr 27) + ((A and B) or (C and (A or B))) + P^ + $8F1BBCDC); A := A shr 2 or A shl 30; Inc(P);</div><div>      Inc(C, (D shl 5 or D shr 27) + ((E and A) or (B and (E or A))) + P^ + $8F1BBCDC); E := E shr 2 or E shl 30; Inc(P);</div>

<div>      Inc(B, (C shl 5 or C shr 27) + ((D and E) or (A and (D or E))) + P^ + $8F1BBCDC); D := D shr 2 or D shl 30; Inc(P);</div><div>      Inc(A, (B shl 5 or B shr 27) + ((C and D) or (E and (C or D))) + P^ + $8F1BBCDC); C := C shr 2 or C shl 30; Inc(P);</div>

<div>    end;</div><div><br></div><div>  for I := 0 to 3 do</div><div>    begin</div><div>      Inc(E, (A shl 5 or A shr 27) + (D xor B xor C) + P^ + $CA62C1D6); B := B shr 2 or B shl 30; Inc(P);</div><div>      Inc(D, (E shl 5 or E shr 27) + (C xor A xor B) + P^ + $CA62C1D6); A := A shr 2 or A shl 30; Inc(P);</div>

<div>      Inc(C, (D shl 5 or D shr 27) + (B xor E xor A) + P^ + $CA62C1D6); E := E shr 2 or E shl 30; Inc(P);</div><div>      Inc(B, (C shl 5 or C shr 27) + (A xor D xor E) + P^ + $CA62C1D6); D := D shr 2 or D shl 30; Inc(P);</div>

<div>      Inc(A, (B shl 5 or B shr 27) + (E xor C xor D) + P^ + $CA62C1D6); C := C shr 2 or C shl 30; Inc(P);</div><div>    end;</div><div><br></div><div>  Inc(Digest.Longs[0], A);</div><div>  Inc(Digest.Longs[1], B);</div>

<div>  Inc(Digest.Longs[2], C);</div><div>  Inc(Digest.Longs[3], D);</div><div>  Inc(Digest.Longs[4], E);</div><div>end;</div><div><br></div><div>procedure SHA1Buf(var Digest: T160BitDigest; const Buf; const BufSize: Integer);</div>

<div>var P : PByte;</div><div>    I, J : Integer;</div><div>begin</div><div>  I := BufSize;</div><div>  if I <= 0 then</div><div>    exit;</div><div>  Assert(I mod 64 = 0, 'BufSize must be multiple of 64 bytes');</div>

<div>  P := @Buf;</div><div>  for J := 0 to I div 64 - 1 do</div><div>    begin</div><div>      TransformSHABuffer(Digest, P^, True);</div><div>      Inc(P, 64);</div><div>    end;</div><div>end;</div><div><br></div><div>

procedure ReverseMem(var Buf; const BufSize: Integer);</div><div>var I : Integer;</div><div>    P : PByte;</div><div>    Q : PByte;</div><div>    T : Byte;</div><div>begin</div><div>  P := @Buf;</div><div>  Q := P;</div>
<div>
  Inc(Q, BufSize - 1);</div><div>  for I := 1 to BufSize div 2 do</div><div>    begin</div><div>      T := P^;</div><div>      P^ := Q^;</div><div>      Q^ := T;</div><div>      Inc(P);</div><div>      Dec(Q);</div><div>
    end;</div>
<div>end;</div><div><br></div><div>procedure StdFinalBuf512(</div><div>          const Buf; const BufSize: Integer; const TotalSize: Int64;</div><div>          var Buf1, Buf2: T512BitBuf;</div><div>          var FinalBufs: Integer;</div>

<div>          const SwapEndian: Boolean);</div><div>var P, Q : PByte;</div><div>    I : Integer;</div><div>    L : Int64;</div><div>begin</div><div>  Assert(BufSize < 64, 'Final BufSize must be less than 64 bytes');</div>

<div>  Assert(TotalSize >= BufSize, 'TotalSize >= BufSize');</div><div><br></div><div>  P := @Buf;</div><div>  Q := @Buf1[0];</div><div>  if BufSize > 0 then</div><div>    begin</div><div>      Move(P^, Q^, BufSize);</div>

<div>      Inc(Q, BufSize);</div><div>    end;</div><div>  Q^ := $80;</div><div>  Inc(Q);</div><div><br></div><div>  L := Int64(TotalSize * 8);</div><div>  if SwapEndian then</div><div>    ReverseMem(L, 8);</div><div>  if BufSize + 1 > 64 - Sizeof(Int64) then</div>

<div>    begin</div><div>      FillChar(Q^, 64 - BufSize - 1, #0);</div><div>      Q := @Buf2[0];</div><div>      FillChar(Q^, 64 - Sizeof(Int64), #0);</div><div>      Inc(Q, 64 - Sizeof(Int64));</div><div>      PInt64(Q)^ := L;</div>

<div>      FinalBufs := 2;</div><div>    end</div><div>  else</div><div>    begin</div><div>      I := 64 - Sizeof(Int64) - BufSize - 1;</div><div>      FillChar(Q^, I, #0);</div><div>      Inc(Q, I);</div><div>      PInt64(Q)^ := L;</div>

<div>      FinalBufs := 1;</div><div>    end;</div><div>end;</div><div><br></div><div>procedure SwapEndianBuf(var Buf; const Count: Integer);</div><div>var P : PLongWord;</div><div>    I : Integer;</div><div>begin</div><div>

  P := @Buf;</div><div>  for I := 1 to Count do</div><div>    begin</div><div>      P^ := SwapEndian(P^);</div><div>      Inc(P);</div><div>    end;</div><div>end;</div><div><br></div><div>procedure SecureClear(var Buf; const BufSize: Integer);</div>

<div>begin</div><div>  if BufSize <= 0 then</div><div>    exit;</div><div>  FillChar(Buf, BufSize, #$00);</div><div>end;</div><div><br></div><div>procedure SecureClear512(var Buf: T512BitBuf);</div><div>begin</div><div>

  SecureClear(Buf, SizeOf(Buf));</div><div>end;</div><div><br></div><div>procedure SHA1FinalBuf(var Digest: T160BitDigest; const Buf; const BufSize: Integer; const TotalSize: Int64);</div><div>var B1, B2 : T512BitBuf;</div>

<div>    C : Integer;</div><div>begin</div><div>  StdFinalBuf512(Buf, BufSize, TotalSize, B1, B2, C, True);</div><div>  TransformSHABuffer(Digest, B1, True);</div><div>  if C > 1 then</div><div>    TransformSHABuffer(Digest, B2, True);</div>

<div>  SwapEndianBuf(Digest, Sizeof(Digest) div Sizeof(LongWord));</div><div>  SecureClear512(B1);</div><div>  if C > 1 then</div><div>    SecureClear512(B2);</div><div>end;</div><div><br></div><div>function CalcSHA1(const Buf; const BufSize: Integer): T160BitDigest;</div>

<div>var I, J : Integer;</div><div>    P    : PByte;</div><div>begin</div><div>  SHA1InitDigest(Result);</div><div>  P := @Buf;</div><div>  if BufSize <= 0 then</div><div>    I := 0 else</div><div>    I := BufSize;</div>

<div>  J := (I div 64) * 64;</div><div>  if J > 0 then</div><div>    begin</div><div>      SHA1Buf(Result, P^, J);</div><div>      Inc(P, J);</div><div>      Dec(I, J);</div><div>    end;</div><div>  SHA1FinalBuf(Result, P^, I, BufSize);</div>

<div>end;</div><div><br></div><div>procedure HMAC_KeyBlock512(const Key; const KeySize: Integer; var Buf: T512BitBuf);</div><div>var P : PAnsiChar;</div><div>begin</div><div>  Assert(KeySize <= 64);</div><div>  P := @Buf;</div>

<div>  if KeySize > 0 then</div><div>    begin</div><div>      Move(Key, P^, KeySize);</div><div>      Inc(P, KeySize);</div><div>    end;</div><div>  FillChar(P^, 64 - KeySize, #0);</div><div>end;</div><div><br></div>

<div>procedure XORBlock512(var Buf: T512BitBuf; const XOR8: Byte);</div><div>var I : Integer;</div><div>begin</div><div>  for I := 0 to SizeOf(Buf) - 1 do</div><div>    Buf[I] := Buf[I] xor XOR8;</div><div>end;</div><div>

<br></div><div>procedure HMAC_SHA1Init(const Key: Pointer; const KeySize: Integer; var Digest: T160BitDigest; var K: T512BitBuf);</div><div>var D : T160BitDigest;</div><div>    S : T512BitBuf;</div><div>begin</div><div>  SHA1InitDigest(Digest);</div>

<div><br></div><div>  if KeySize > 64 then</div><div>    begin</div><div>      D := CalcSHA1(Key^, KeySize);</div><div>      HMAC_KeyBlock512(D, Sizeof(D), K);</div><div>    end else</div><div>    HMAC_KeyBlock512(Key^, KeySize, K);</div>

<div><br></div><div>  Move(K, S, SizeOf(K));</div><div>  XORBlock512(S, $36);</div><div>  TransformSHABuffer(Digest, S, True);</div><div>  SecureClear512(S);</div><div>end;</div><div><br></div><div>procedure HMAC_SHA1Buf(var Digest: T160BitDigest; const Buf; const BufSize: Integer);</div>

<div>begin</div><div>  SHA1Buf(Digest, Buf, BufSize);</div><div>end;</div><div><br></div><div>procedure HMAC_SHA1FinalBuf(const K: T512BitBuf; var Digest: T160BitDigest; const Buf; const BufSize: Integer; const TotalSize: Int64);</div>

<div>var</div><div>  FinBuf : packed record</div><div>    K : T512BitBuf;</div><div>    D : T160BitDigest;</div><div>  end;</div><div>begin</div><div>  SHA1FinalBuf(Digest, Buf, BufSize, TotalSize + 64);</div><div>  Move(K, FinBuf.K, SizeOf(K));</div>

<div>  XORBlock512(FinBuf.K, $5C);</div><div>  Move(Digest, FinBuf.D, SizeOf(Digest));</div><div>  Digest := CalcSHA1(FinBuf, SizeOf(FinBuf));</div><div>  SecureClear(FinBuf, SizeOf(FinBuf));</div><div>end;</div><div><br>

</div><div>function CalcHMAC_SHA1(const Key: Pointer; const KeySize: Integer; const Buf; const BufSize: Integer): T160BitDigest;</div><div>var I, J : Integer;</div><div>    P    : PByte;</div><div>    K    : T512BitBuf;</div>

<div>begin</div><div>  HMAC_SHA1Init(Key, KeySize, Result, K);</div><div>  P := @Buf;</div><div>  if BufSize <= 0 then</div><div>    I := 0 else</div><div>    I := BufSize;</div><div>  J := (I div 64) * 64;</div><div>
  if J > 0 then</div>
<div>    begin</div><div>      HMAC_SHA1Buf(Result, P^, J);</div><div>      Inc(P, J);</div><div>      Dec(I, J);</div><div>    end;</div><div>  HMAC_SHA1FinalBuf(K, Result, P^, I, BufSize);</div><div>  SecureClear512(K);</div>

<div>end;</div><div><br></div><div>function CalcHMAC_SHA1(const Key: AnsiString; const Buf; const BufSize: Integer): T160BitDigest;</div><div>begin</div><div>  Result := CalcHMAC_SHA1(Pointer(Key), Length(Key), Buf, BufSize);</div>

<div>end;</div><div><br></div><div>function CalcHMAC_SHA1(const Key, Buf: AnsiString): T160BitDigest;</div><div>begin</div><div>  Result := CalcHMAC_SHA1(Key, Pointer(Buf)^, Length(Buf));</div><div>end;</div><div><br></div>

<div>procedure DigestToHexBuf(const Digest; const Size: Integer; const Buf);</div><div>const s_HexDigitsLower : String[16] = '0123456789abcdef';</div><div>var I : Integer;</div><div>    P : PAnsiChar;</div><div>    Q : PByte;</div>

<div>begin</div><div>  P := @Buf;;</div><div>  Assert(Assigned(P));</div><div>  Q := @Digest;</div><div>  Assert(Assigned(Q));</div><div>  for I := 0 to Size - 1 do</div><div>    begin</div><div>      P^ := s_HexDigitsLower[Q^ shr 4 + 1];</div>

<div>      Inc(P);</div><div>      P^ := s_HexDigitsLower[Q^ and 15 + 1];</div><div>      Inc(P);</div><div>      Inc(Q);</div><div>    end;</div><div>end;</div><div><br></div><div>function DigestToHex(const Digest; const Size: Integer): AnsiString;</div>

<div>begin</div><div>  SetLength(Result, Size * 2);</div><div>  DigestToHexBuf(Digest, Size, Pointer(Result)^);</div><div>end;</div><div><br></div><div>function SHA1DigestToHex(const Digest: T160BitDigest): AnsiString;</div>

<div>begin</div><div>  Result := DigestToHex(Digest, Sizeof(Digest));</div><div>end;</div><div><br></div><div>procedure TForm1.Button1Click(Sender: TObject);</div><div>begin</div><div>  Edit1.Text := SHA1DigestToHex(CalcHMAC_SHA1('secret',</div>

<div>    'The quick brown fox jumped over the lazy dog.'));  // 5d4db2701c7b07de0e23db3e4f22e88bc1a31a49</div><div>end;</div><div><br></div><div>end.</div></div><div><br></div><div>-- </div></div>Silvio Clécio<br>

My public projects - <a href="http://github.com/silvioprog" target="_blank">github.com/silvioprog</a>
</div></div>