xtool/contrib/fundamentals/TLS/flcTLSPRF.pas

316 lines
13 KiB
ObjectPascal

{******************************************************************************}
{ }
{ Library: Fundamentals TLS }
{ File name: flcTLSPRF.pas }
{ File version: 5.02 }
{ Description: TLS PRF (Pseudo Random Function) }
{ }
{ Copyright: Copyright (c) 2008-2020, David J Butler }
{ All rights reserved. }
{ Redistribution and use in source and binary forms, with }
{ or without modification, are permitted provided that }
{ the following conditions are met: }
{ Redistributions of source code must retain the above }
{ copyright notice, this list of conditions and the }
{ following disclaimer. }
{ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND }
{ CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED }
{ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED }
{ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A }
{ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL }
{ THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, }
{ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR }
{ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, }
{ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF }
{ USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) }
{ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER }
{ IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING }
{ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE }
{ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE }
{ POSSIBILITY OF SUCH DAMAGE. }
{ }
{ Github: https://github.com/fundamentalslib }
{ E-mail: fundamentals.library at gmail.com }
{ }
{ Revision history: }
{ }
{ 2008/01/18 0.01 Initial development. }
{ 2020/05/09 5.02 Create flcTLSPRF unit from flcTLSUtils unit. }
{ }
{******************************************************************************}
{$INCLUDE flcTLS.inc}
unit flcTLSPRF;
interface
uses
{ Utils }
flcStdTypes,
{ TLS }
flcTLSProtocolVersion;
{ }
{ PRFAlgorithm }
{ }
type
TTLSPRFAlgorithm = (
tlspaSHA256
);
{ }
{ PRF (Pseudo-Random Function) }
{ }
function tlsP_MD5(const Secret, Seed: RawByteString; const Size: Integer): RawByteString;
function tlsP_SHA1(const Secret, Seed: RawByteString; const Size: Integer): RawByteString;
function tlsP_SHA256(const Secret, Seed: RawByteString; const Size: Integer): RawByteString;
function tlsP_SHA512(const Secret, Seed: RawByteString; const Size: Integer): RawByteString;
function tls10PRF(const Secret, ALabel, Seed: RawByteString; const Size: Integer): RawByteString;
function tls12PRF_SHA256(const Secret, ALabel, Seed: RawByteString; const Size: Integer): RawByteString;
function tls12PRF_SHA512(const Secret, ALabel, Seed: RawByteString; const Size: Integer): RawByteString;
function TLSPRF(const ProtocolVersion: TTLSProtocolVersion;
const Secret, ALabel, Seed: RawByteString; const Size: Integer): RawByteString;
{ }
{ Test }
{ }
{$IFDEF TLS_TEST}
procedure Test;
{$ENDIF}
implementation
uses
{ Utils }
flcHash,
{ TLS }
flcTLSErrors;
{ }
{ P_hash }
{ P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) + }
{ HMAC_hash(secret, A(2) + seed) + }
{ HMAC_hash(secret, A(3) + seed) + ... }
{ Where + indicates concatenation. }
{ A() is defined as: }
{ A(0) = seed }
{ A(i) = HMAC_hash(secret, A(i-1)) }
{ }
function tlsP_MD5(const Secret, Seed: RawByteString; const Size: Integer): RawByteString;
var A, P : RawByteString;
L : Integer;
begin
P := '';
L := 0;
A := Seed;
repeat
A := MD5DigestToStrA(CalcHMAC_MD5(Secret, A));
P := P + MD5DigestToStrA(CalcHMAC_MD5(Secret, A + Seed));
Inc(L, 16);
until L >= Size;
if L > Size then
SetLength(P, Size);
Result := P;
end;
function tlsP_SHA1(const Secret, Seed: RawByteString; const Size: Integer): RawByteString;
var A, P : RawByteString;
L : Integer;
begin
P := '';
L := 0;
A := Seed;
repeat
A := SHA1DigestToStrA(CalcHMAC_SHA1(Secret, A));
P := P + SHA1DigestToStrA(CalcHMAC_SHA1(Secret, A + Seed));
Inc(L, 20);
until L >= Size;
if L > Size then
SetLength(P, Size);
Result := P;
end;
function tlsP_SHA256(const Secret, Seed: RawByteString; const Size: Integer): RawByteString;
var A, P : RawByteString;
L : Integer;
begin
P := '';
L := 0;
A := Seed;
repeat
A := SHA256DigestToStrA(CalcHMAC_SHA256(Secret, A));
P := P + SHA256DigestToStrA(CalcHMAC_SHA256(Secret, A + Seed));
Inc(L, 32);
until L >= Size;
if L > Size then
SetLength(P, Size);
Result := P;
end;
function tlsP_SHA512(const Secret, Seed: RawByteString; const Size: Integer): RawByteString;
var A, P : RawByteString;
L : Integer;
begin
P := '';
L := 0;
A := Seed;
repeat
A := SHA512DigestToStrA(CalcHMAC_SHA512(Secret, A));
P := P + SHA512DigestToStrA(CalcHMAC_SHA512(Secret, A + Seed));
Inc(L, 64);
until L >= Size;
if L > Size then
SetLength(P, Size);
Result := P;
end;
{ }
{ PRF }
{ TLS 1.0: }
{ PRF(secret, label, seed) = P_MD5(S1, label + seed) XOR }
{ P_SHA-1(S2, label + seed); }
{ S1 and S2 are the two halves of the secret and each is the same length. }
{ S1 is taken from the first half of the secret, S2 from the second half. }
{ Their length is created by rounding up the length of the overall secret }
{ divided by two; thus, if the original secret is an odd number of bytes }
{ long, the last byte of S1 will be the same as the first byte of S2. }
{ }
{ TLS 1.2: }
{ PRF(secret, label, seed) = P_<hash>(secret, label + seed) }
{ P_SHA-256 }
{ }
procedure tls10PRFSplitSecret(const Secret: RawByteString; var S1, S2: RawByteString);
var L, N : Integer;
begin
N := Length(Secret);
L := N;
if L mod 2 = 1 then
Inc(L);
L := L div 2;
S1 := Copy(Secret, 1, L);
S2 := Copy(Secret, N - L + 1, L);
end;
function tls10PRF(const Secret, ALabel, Seed: RawByteString; const Size: Integer): RawByteString;
var S1, S2 : RawByteString;
P1, P2 : RawByteString;
R : RawByteString;
I : Integer;
begin
tls10PRFSplitSecret(Secret, S1, S2);
P1 := tlsP_MD5(S1, ALabel + Seed, Size);
P2 := tlsP_SHA1(S2, ALabel + Seed, Size);
SetLength(R, Size);
for I := 1 to Size do
R[I] := AnsiChar(Byte(P1[I]) xor Byte(P2[I]));
Result := R;
end;
function tls12PRF_SHA256(const Secret, ALabel, Seed: RawByteString; const Size: Integer): RawByteString;
begin
Result := tlsP_SHA256(Secret, ALabel + Seed, Size);
end;
function tls12PRF_SHA512(const Secret, ALabel, Seed: RawByteString; const Size: Integer): RawByteString;
begin
Result := tlsP_SHA512(Secret, ALabel + Seed, Size);
end;
function TLSPRF(const ProtocolVersion: TTLSProtocolVersion;
const Secret, ALabel, Seed: RawByteString; const Size: Integer): RawByteString;
begin
if IsTLS12OrLater(ProtocolVersion) then
Result := tls12PRF_SHA256(Secret, ALabel, Seed, Size) else
if IsTLS10OrLater(ProtocolVersion) then
Result := tls10PRF(Secret, ALabel, Seed, Size)
else
raise ETLSError.Create(TLSError_InvalidParameter);
end;
{ }
{ Test }
{ }
{$IFDEF TLS_TEST}
{$ASSERTIONS ON}
procedure Test;
begin
// //
// Test vectors from http://www6.ietf.org/mail-archive/web/tls/current/msg03416.html //
// //
Assert(tls12PRF_SHA256(
RawByteString(#$9b#$be#$43#$6b#$a9#$40#$f0#$17#$b1#$76#$52#$84#$9a#$71#$db#$35),
'test label',
RawByteString(#$a0#$ba#$9f#$93#$6c#$da#$31#$18#$27#$a6#$f7#$96#$ff#$d5#$19#$8c), 100) =
#$e3#$f2#$29#$ba#$72#$7b#$e1#$7b +
#$8d#$12#$26#$20#$55#$7c#$d4#$53 +
#$c2#$aa#$b2#$1d#$07#$c3#$d4#$95 +
#$32#$9b#$52#$d4#$e6#$1e#$db#$5a +
#$6b#$30#$17#$91#$e9#$0d#$35#$c9 +
#$c9#$a4#$6b#$4e#$14#$ba#$f9#$af +
#$0f#$a0#$22#$f7#$07#$7d#$ef#$17 +
#$ab#$fd#$37#$97#$c0#$56#$4b#$ab +
#$4f#$bc#$91#$66#$6e#$9d#$ef#$9b +
#$97#$fc#$e3#$4f#$79#$67#$89#$ba +
#$a4#$80#$82#$d1#$22#$ee#$42#$c5 +
#$a7#$2e#$5a#$51#$10#$ff#$f7#$01 +
#$87#$34#$7b#$66);
Assert(tls12PRF_SHA512(
RawByteString(#$b0#$32#$35#$23#$c1#$85#$35#$99#$58#$4d#$88#$56#$8b#$bb#$05#$eb),
'test label',
RawByteString(#$d4#$64#$0e#$12#$e4#$bc#$db#$fb#$43#$7f#$03#$e6#$ae#$41#$8e#$e5), 196) =
#$12#$61#$f5#$88#$c7#$98#$c5#$c2 +
#$01#$ff#$03#$6e#$7a#$9c#$b5#$ed +
#$cd#$7f#$e3#$f9#$4c#$66#$9a#$12 +
#$2a#$46#$38#$d7#$d5#$08#$b2#$83 +
#$04#$2d#$f6#$78#$98#$75#$c7#$14 +
#$7e#$90#$6d#$86#$8b#$c7#$5c#$45 +
#$e2#$0e#$b4#$0c#$1c#$f4#$a1#$71 +
#$3b#$27#$37#$1f#$68#$43#$25#$92 +
#$f7#$dc#$8e#$a8#$ef#$22#$3e#$12 +
#$ea#$85#$07#$84#$13#$11#$bf#$68 +
#$65#$3d#$0c#$fc#$40#$56#$d8#$11 +
#$f0#$25#$c4#$5d#$df#$a6#$e6#$fe +
#$c7#$02#$f0#$54#$b4#$09#$d6#$f2 +
#$8d#$d0#$a3#$23#$3e#$49#$8d#$a4 +
#$1a#$3e#$75#$c5#$63#$0e#$ed#$be +
#$22#$fe#$25#$4e#$33#$a1#$b0#$e9 +
#$f6#$b9#$82#$66#$75#$be#$c7#$d0 +
#$1a#$84#$56#$58#$dc#$9c#$39#$75 +
#$45#$40#$1d#$40#$b9#$f4#$6c#$7a +
#$40#$0e#$e1#$b8#$f8#$1c#$a0#$a6 +
#$0d#$1a#$39#$7a#$10#$28#$bf#$f5 +
#$d2#$ef#$50#$66#$12#$68#$42#$fb +
#$8d#$a4#$19#$76#$32#$bd#$b5#$4f +
#$f6#$63#$3f#$86#$bb#$c8#$36#$e6 +
#$40#$d4#$d8#$98);
end;
{$ENDIF}
end.