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