428 lines
19 KiB
ObjectPascal
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.
|