xtool/contrib/fundamentals/TLS/flcTLSKeys.pas

428 lines
19 KiB
ObjectPascal

{******************************************************************************}
{ }
{ Library: Fundamentals TLS }
{ File name: flcTLSKeys.pas }
{ File version: 5.02 }
{ Description: TLS Keys }
{ }
{ 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 flcTLSKeys unit from flcTLSUtils unit. }
{ }
{******************************************************************************}
{$INCLUDE flcTLS.inc}
unit flcTLSKeys;
interface
uses
{ Utils }
flcStdTypes,
{ TLS }
flcTLSProtocolVersion;
{ }
{ Key block }
{ }
function tls10KeyBlock(const MasterSecret, ServerRandom, ClientRandom: RawByteString; const Size: Integer): RawByteString;
function tls12SHA256KeyBlock(const MasterSecret, ServerRandom, ClientRandom: RawByteString; const Size: Integer): RawByteString;
function tls12SHA512KeyBlock(const MasterSecret, ServerRandom, ClientRandom: RawByteString; const Size: Integer): RawByteString;
function TLSKeyBlock(const ProtocolVersion: TTLSProtocolVersion;
const MasterSecret, ServerRandom, ClientRandom: RawByteString; const Size: Integer): RawByteString;
{ }
{ Master secret }
{ }
function tls10MasterSecret(const PreMasterSecret, ClientRandom, ServerRandom: RawByteString): RawByteString;
function tls12SHA256MasterSecret(const PreMasterSecret, ClientRandom, ServerRandom: RawByteString): RawByteString;
function tls12SHA512MasterSecret(const PreMasterSecret, ClientRandom, ServerRandom: RawByteString): RawByteString;
function TLSMasterSecret(const ProtocolVersion: TTLSProtocolVersion;
const PreMasterSecret, ClientRandom, ServerRandom: RawByteString): RawByteString;
{ }
{ TLS Keys }
{ }
type
TTLSKeys = record
KeyBlock : RawByteString;
ClientMACKey : RawByteString;
ServerMACKey : RawByteString;
ClientEncKey : RawByteString;
ServerEncKey : RawByteString;
ClientIV : RawByteString;
ServerIV : RawByteString;
end;
procedure GenerateTLSKeys(
const ProtocolVersion: TTLSProtocolVersion;
const MACKeyBits, CipherKeyBits, IVBits: Integer;
const MasterSecret, ServerRandom, ClientRandom: RawByteString;
var TLSKeys: TTLSKeys);
procedure GenerateFinalTLSKeys(
const ProtocolVersion: TTLSProtocolVersion;
const IsExportable: Boolean;
const ExpandedKeyBits: Integer;
const ServerRandom, ClientRandom: RawByteString;
var TLSKeys: TTLSKeys);
{ }
{ Test }
{ }
{$IFDEF TLS_TEST}
procedure Test;
{$ENDIF}
implementation
uses
{ Utils }
flcHash,
flcStrings,
{ TLS }
flcTLSErrors,
flcTLSPRF;
{ }
{ Key block }
{ }
{ SSL 3.0: }
{ key_block = }
{ MD5(master_secret + SHA('A' + master_secret + }
{ ServerHello.random + ClientHello.random)) + }
{ MD5(master_secret + SHA('BB' + master_secret + }
{ ServerHello.random + ClientHello.random)) + }
{ MD5(master_secret + SHA('CCC' + master_secret + }
{ ServerHello.random + ClientHello.random)) + }
{ [...]; }
{ }
{ TLS 1.0 / 1.1 / 1.2: }
{ key_block = PRF(SecurityParameters.master_secret, }
{ "key expansion", }
{ SecurityParameters.server_random + }
{ SecurityParameters.client_random); }
{ }
function ssl30KeyBlockP(const Prefix, MasterSecret, ServerRandom, ClientRandom: RawByteString): RawByteString;
begin
Result :=
MD5DigestToStrA(
CalcMD5(MasterSecret +
SHA1DigestToStrA(
CalcSHA1(Prefix + MasterSecret + ServerRandom + ClientRandom))));
end;
function ssl30KeyBlockPF(const MasterSecret, ServerRandom, ClientRandom: RawByteString; const Size: Integer): RawByteString;
var Salt : RawByteString;
I : Integer;
begin
Result := '';
I := 1;
while Length(Result) < Size do
begin
if I > 26 then
raise ETLSError.Create(TLSError_InvalidParameter);
Salt := DupCharB(ByteChar(Ord('A') + I - 1), I);
Result := Result +
ssl30KeyBlockP(Salt, MasterSecret, ServerRandom, ClientRandom);
Inc(I);
end;
SetLength(Result, Size);
end;
function ssl30KeyBlock(const MasterSecret, ServerRandom, ClientRandom: RawByteString; const Size: Integer): RawByteString;
begin
Result := ssl30KeyBlockPF(MasterSecret, ServerRandom, ClientRandom, Size);
end;
const
LabelKeyExpansion = 'key expansion';
function tls10KeyBlock(const MasterSecret, ServerRandom, ClientRandom: RawByteString; const Size: Integer): RawByteString;
var S : RawByteString;
begin
S := ServerRandom + ClientRandom;
Result := tls10PRF(MasterSecret, LabelKeyExpansion, S, Size);
end;
function tls12SHA256KeyBlock(const MasterSecret, ServerRandom, ClientRandom: RawByteString; const Size: Integer): RawByteString;
var S : RawByteString;
begin
S := ServerRandom + ClientRandom;
Result := tls12PRF_SHA256(MasterSecret, LabelKeyExpansion, S, Size);
end;
function tls12SHA512KeyBlock(const MasterSecret, ServerRandom, ClientRandom: RawByteString; const Size: Integer): RawByteString;
var S : RawByteString;
begin
S := ServerRandom + ClientRandom;
Result := tls12PRF_SHA512(MasterSecret, LabelKeyExpansion, S, Size);
end;
function TLSKeyBlock(const ProtocolVersion: TTLSProtocolVersion;
const MasterSecret, ServerRandom, ClientRandom: RawByteString; const Size: Integer): RawByteString;
begin
if IsTLS12OrLater(ProtocolVersion) then
Result := tls12SHA256KeyBlock(MasterSecret, ServerRandom, ClientRandom, Size) else
if IsTLS10OrLater(ProtocolVersion) then
Result := tls10KeyBlock(MasterSecret, ServerRandom, ClientRandom, Size) else
if IsSSL3(ProtocolVersion) then
Result := ssl30KeyBlock(MasterSecret, ServerRandom, ClientRandom, Size)
else
raise ETLSError.Create(TLSError_InvalidParameter);
end;
{ }
{ Master secret }
{ }
{ SSL 3: }
{ master_secret = }
{ MD5(pre_master_secret + SHA('A' + pre_master_secret + }
{ ClientHello.random + ServerHello.random)) + }
{ MD5(pre_master_secret + SHA('BB' + pre_master_secret + }
{ ClientHello.random + ServerHello.random)) + }
{ MD5(pre_master_secret + SHA('CCC' + pre_master_secret + }
{ ClientHello.random + ServerHello.random)); }
{ }
{ TLS 1.0 1.1 1.2: }
{ master_secret = PRF(pre_master_secret, }
{ "master secret", }
{ ClientHello.random + ServerHello.random) }
{ }
{ The master secret is always exactly 48 bytes in length. The length of }
{ the premaster secret will vary depending on key exchange method. }
{ }
const
LabelMasterSecret = 'master secret';
MasterSecretSize = 48;
function ssl30MasterSecretP(const Prefix, PreMasterSecret, ClientRandom, ServerRandom: RawByteString): RawByteString;
begin
Result :=
MD5DigestToStrA(
CalcMD5(PreMasterSecret +
SHA1DigestToStrA(
CalcSHA1(Prefix + PreMasterSecret + ClientRandom + ServerRandom))));
end;
function ssl30MasterSecret(const PreMasterSecret, ClientRandom, ServerRandom: RawByteString): RawByteString;
begin
Result :=
ssl30MasterSecretP('A', PreMasterSecret, ClientRandom, ServerRandom) +
ssl30MasterSecretP('BB', PreMasterSecret, ClientRandom, ServerRandom) +
ssl30MasterSecretP('CCC', PreMasterSecret, ClientRandom, ServerRandom);
end;
function tls10MasterSecret(const PreMasterSecret, ClientRandom, ServerRandom: RawByteString): RawByteString;
var S : RawByteString;
begin
S := ClientRandom + ServerRandom;
Result := tls10PRF(PreMasterSecret, LabelMasterSecret, S, MasterSecretSize);
end;
function tls12SHA256MasterSecret(const PreMasterSecret, ClientRandom, ServerRandom: RawByteString): RawByteString;
var S : RawByteString;
begin
S := ClientRandom + ServerRandom;
Result := tls12PRF_SHA256(PreMasterSecret, LabelMasterSecret, S, MasterSecretSize);
end;
function tls12SHA512MasterSecret(const PreMasterSecret, ClientRandom, ServerRandom: RawByteString): RawByteString;
var S : RawByteString;
begin
S := ClientRandom + ServerRandom;
Result := tls12PRF_SHA512(PreMasterSecret, LabelMasterSecret, S, MasterSecretSize);
end;
function TLSMasterSecret(const ProtocolVersion: TTLSProtocolVersion;
const PreMasterSecret, ClientRandom, ServerRandom: RawByteString): RawByteString;
begin
if IsTLS12OrLater(ProtocolVersion) then
Result := tls12SHA256MasterSecret(PreMasterSecret, ClientRandom, ServerRandom) else
if IsTLS10OrLater(ProtocolVersion) then
Result := tls10MasterSecret(PreMasterSecret, ClientRandom, ServerRandom) else
if IsSSL3(ProtocolVersion) then
Result := ssl30MasterSecret(PreMasterSecret, ClientRandom, ServerRandom)
else
raise ETLSError.Create(TLSError_InvalidParameter);
end;
{ }
{ TLS Keys }
{ }
procedure GenerateTLSKeys(
const ProtocolVersion: TTLSProtocolVersion;
const MACKeyBits, CipherKeyBits, IVBits: Integer;
const MasterSecret, ServerRandom, ClientRandom: RawByteString;
var TLSKeys: TTLSKeys);
var L, I, N : Integer;
S : RawByteString;
begin
Assert(MACKeyBits mod 8 = 0);
Assert(CipherKeyBits mod 8 = 0);
Assert(IVBits mod 8 = 0);
L := MACKeyBits * 2 + CipherKeyBits * 2 + IVBits * 2;
L := L div 8;
S := TLSKeyBlock(ProtocolVersion, MasterSecret, ServerRandom, ClientRandom, L);
TLSKeys.KeyBlock := S;
I := 1;
N := MACKeyBits div 8;
TLSKeys.ClientMACKey := Copy(S, I, N);
TLSKeys.ServerMACKey := Copy(S, I + N, N);
Inc(I, N * 2);
N := CipherKeyBits div 8;
TLSKeys.ClientEncKey := Copy(S, I, N);
TLSKeys.ServerEncKey := Copy(S, I + N, N);
Inc(I, N * 2);
N := IVBits div 8;
TLSKeys.ClientIV := Copy(S, I, N);
TLSKeys.ServerIV := Copy(S, I + N, N);
end;
{ TLS 1.0: }
{ final_client_write_key = PRF(SecurityParameters.client_write_key, }
{ "client write key", }
{ SecurityParameters.client_random + SecurityParameters.server_random); }
{ final_server_write_key = PRF(SecurityParameters.server_write_key, }
{ "server write key", }
{ SecurityParameters.client_random + SecurityParameters.server_random); }
{ iv_block = PRF("", "IV block", }
{ SecurityParameters.client_random + SecurityParameters.server_random); }
const
LabelClientWriteKey = 'client write key';
LabelServerWriteKey = 'server write key';
LabelIVBlock = 'IV block';
procedure GenerateFinalTLSKeys(
const ProtocolVersion: TTLSProtocolVersion;
const IsExportable: Boolean;
const ExpandedKeyBits: Integer;
const ServerRandom, ClientRandom: RawByteString;
var TLSKeys: TTLSKeys);
var S : RawByteString;
L : Integer;
V : RawByteString;
begin
if IsTLS11OrLater(ProtocolVersion) then
exit;
if not IsExportable then
exit;
if IsSSL2(ProtocolVersion) or IsSSL3(ProtocolVersion) then
raise ETLSError.Create(TLSError_InvalidParameter, 'Unsupported version');
S := ClientRandom + ServerRandom;
Assert(ExpandedKeyBits mod 8 = 0);
L := ExpandedKeyBits div 8;
TLSKeys.ClientEncKey := tls10PRF(TLSKeys.ClientEncKey, LabelClientWriteKey, S, L);
TLSKeys.ServerEncKey := tls10PRF(TLSKeys.ServerEncKey, LabelServerWriteKey, S, L);
L := Length(TLSKeys.ClientIV);
if L > 0 then
begin
V := tls10PRF('', LabelIVBlock, S, L * 2);
TLSKeys.ClientIV := Copy(V, 1, L);
TLSKeys.ServerIV := Copy(V, L + 1, L);
end;
end;
{ }
{ Test }
{ }
{$IFDEF TLS_TEST}
{$ASSERTIONS ON}
const
PreMasterSecret = RawByteString(
#$03#$01#$84#$54#$F5#$D6#$EB#$F5#$A8#$08#$BA#$FA#$7A#$22#$61#$2D +
#$75#$DC#$40#$E8#$98#$F9#$0E#$B2#$87#$80#$B8#$1A#$8F#$68#$25#$B8 +
#$51#$D0#$54#$45#$61#$8A#$50#$C9#$BB#$0E#$39#$53#$45#$78#$BE#$79);
ClientRandom = RawByteString(
#$40#$FC#$30#$AE#$2D#$63#$84#$BB#$C5#$4B#$27#$FD#$58#$21#$CA#$90 +
#$05#$F6#$A7#$7B#$37#$BB#$72#$E1#$FC#$1D#$1B#$6A#$F5#$1C#$C8#$9F);
ServerRandom = RawByteString(
#$40#$FC#$31#$10#$79#$AB#$17#$66#$FA#$8B#$3F#$AA#$FD#$5E#$48#$23 +
#$FA#$90#$31#$D8#$3C#$B9#$A3#$2C#$8C#$F5#$E9#$81#$9B#$A2#$63#$6C);
MasterSecret = RawByteString(
#$B0#$00#$22#$34#$59#$03#$16#$B7#$7A#$6C#$56#$9B#$89#$D2#$7A#$CC +
#$F3#$85#$55#$59#$3A#$14#$76#$3D#$54#$BF#$EB#$3F#$E0#$2F#$B1#$4B +
#$79#$8C#$75#$A9#$78#$55#$6C#$8E#$A2#$14#$60#$B7#$45#$EB#$77#$B2);
MACWriteKey = RawByteString(
#$85#$F0#$56#$F8#$07#$1D#$B1#$89#$89#$D0#$E1#$33#$3C#$CA#$63#$F9);
procedure TestKeyBlock;
var S : RawByteString;
begin
// //
// Example from http://download.oracle.com/javase/1.5.0/docs/guide/security/jsse/ReadDebug.html //
// //
Assert(tls10MasterSecret(PreMasterSecret, ClientRandom, ServerRandom) = MasterSecret);
S := tls10KeyBlock(MasterSecret, ServerRandom, ClientRandom, 64);
Assert(Copy(S, 1, 48) =
MACWriteKey +
#$1E#$4D#$D1#$D3#$0A#$78#$EE#$B7#$4F#$EC#$15#$79#$B2#$59#$18#$40 +
#$10#$D0#$D6#$C2#$D9#$B7#$62#$CB#$2C#$74#$BF#$5F#$85#$3C#$6F#$E7);
end;
procedure Test;
begin
TestKeyBlock;
end;
{$ENDIF}
end.