1200 lines
44 KiB
ObjectPascal
1200 lines
44 KiB
ObjectPascal
{******************************************************************************}
|
|
{ }
|
|
{ Library: Fundamentals TLS }
|
|
{ File name: flcTLSHandshake.pas }
|
|
{ File version: 5.04 }
|
|
{ Description: TLS handshake protocol }
|
|
{ }
|
|
{ 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: Headers }
|
|
{ 2010/11/26 0.02 Initial development: Messages }
|
|
{ 2016/01/08 0.03 String changes }
|
|
{ 2018/07/17 5.04 Revised for Fundamentals 5. }
|
|
{ }
|
|
{******************************************************************************}
|
|
|
|
{$INCLUDE flcTLS.inc}
|
|
|
|
unit flcTLSHandshake;
|
|
|
|
interface
|
|
|
|
uses
|
|
{ Utils }
|
|
|
|
flcStdTypes,
|
|
|
|
{ Cipher }
|
|
|
|
flcCipherRSA,
|
|
|
|
{ TLS }
|
|
|
|
flcTLSProtocolVersion,
|
|
flcTLSAlgorithmTypes,
|
|
flcTLSRandom,
|
|
flcTLSCipherSuite,
|
|
flcTLSSessionID,
|
|
flcTLSKeyExchangeParams,
|
|
flcTLSCertificate,
|
|
flcTLSHandshakeExtension;
|
|
|
|
|
|
|
|
{ }
|
|
{ HandshakeHeader }
|
|
{ }
|
|
type
|
|
TTLSHandshakeType = (
|
|
tlshtHello_request = 0, // Not used in TLS 1.3
|
|
tlshtClient_hello = 1,
|
|
tlshtServer_hello = 2,
|
|
tlshtNew_session_ticket = 4, // TLS 1.3
|
|
tlshtEnd_of_early_data = 5, // TLS 1.3
|
|
tlshtEncrypted_extensions = 8, // TLS 1.3
|
|
tlshtCertificate = 11,
|
|
tlshtServer_key_exchange = 12, // Not used in TLS 1.3
|
|
tlshtCertificate_request = 13,
|
|
tlshtServer_hello_done = 14, // Not used in TLS 1.3
|
|
tlshtCertificate_verify = 15,
|
|
tlshtClient_key_exchange = 16, // Not used in TLS 1.3
|
|
tlshtFinished = 20,
|
|
tlshtCertificate_url = 21, // RFC 6066 // Not used in TLS 1.3
|
|
tlshtCertificate_status = 22, // RFC 6066 // Not used in TLS 1.3
|
|
tlshtKey_update = 24, // TLS 1.3
|
|
tlshtMessage_hash = 254, // TLS 1.3
|
|
tlshtMax = 255
|
|
);
|
|
|
|
TTLSHandshakeHeader = packed record
|
|
msg_type : TTLSHandshakeType; // handshake type
|
|
length : array[0..2] of Byte; // bytes in message
|
|
end;
|
|
PTLSHandshakeHeader = ^TTLSHandshakeHeader;
|
|
|
|
const
|
|
TLSHandshakeHeaderSize = Sizeof(TTLSHandshakeHeader);
|
|
|
|
function TLSHandshakeTypeToStr(const A: TTLSHandshakeType): String;
|
|
|
|
procedure EncodeTLSHandshakeHeader(
|
|
var Handshake: TTLSHandshakeHeader;
|
|
const MsgType: TTLSHandshakeType;
|
|
const Length: Integer);
|
|
procedure DecodeTLSHandshakeHeader(
|
|
const Handshake: TTLSHandshakeHeader;
|
|
var MsgType: TTLSHandshakeType;
|
|
var Length: Integer);
|
|
|
|
function TLSHandshakeDataPtr(
|
|
const Buffer; const Size: Integer;
|
|
var DataSize: Integer): Pointer;
|
|
function EncodeTLSHandshake(
|
|
var Buffer; const Size: Integer;
|
|
const MsgType: TTLSHandshakeType; const MsgSize: Integer): Integer;
|
|
|
|
|
|
|
|
{ }
|
|
{ HelloRequest }
|
|
{ }
|
|
function EncodeTLSHandshakeHelloRequest(var Buffer; const Size: Integer): Integer;
|
|
|
|
|
|
|
|
{ }
|
|
{ ClientHello }
|
|
{ }
|
|
type
|
|
TTLSClientHello = record
|
|
ProtocolVersion : TTLSProtocolVersion;
|
|
SessionID : TTLSSessionID;
|
|
CipherSuites : TTLSCipherSuites;
|
|
CompressionMethods : TTLSCompressionMethods;
|
|
Random : TTLSRandom;
|
|
ExtensionsPresent : Boolean;
|
|
SignAndHashAlgos : TTLSSignatureAndHashAlgorithmArray;
|
|
end;
|
|
|
|
procedure InitTLSClientHello(
|
|
var ClientHello: TTLSClientHello;
|
|
const ProtocolVersion: TTLSProtocolVersion;
|
|
const SessionID: RawByteString);
|
|
|
|
function EncodeTLSClientHello(
|
|
var Buffer; const Size: Integer;
|
|
var ClientHello: TTLSClientHello): Integer;
|
|
|
|
function DecodeTLSClientHello(
|
|
const Buffer; const Size: Integer;
|
|
var ClientHello: TTLSClientHello): Integer;
|
|
|
|
function EncodeTLSHandshakeClientHello(
|
|
var Buffer; const Size: Integer;
|
|
var ClientHello: TTLSClientHello): Integer;
|
|
|
|
|
|
|
|
{ }
|
|
{ ServerHello }
|
|
{ }
|
|
type
|
|
TTLSServerHello = record
|
|
ProtocolVersion : TTLSProtocolVersion;
|
|
SessionID : TTLSSessionID;
|
|
CipherSuite : TTLSCipherSuiteRec;
|
|
CompressionMethod : TTLSCompressionMethod;
|
|
Random : TTLSRandom;
|
|
end;
|
|
|
|
procedure InitTLSServerHello(
|
|
var ServerHello: TTLSServerHello;
|
|
const ProtocolVersion: TTLSProtocolVersion;
|
|
const SessionID: RawByteString;
|
|
const CipherSuite: TTLSCipherSuiteRec;
|
|
const CompressionMethod: TTLSCompressionMethod);
|
|
|
|
function EncodeTLSServerHello(
|
|
var Buffer; const Size: Integer;
|
|
const ServerHello: TTLSServerHello): Integer;
|
|
|
|
function DecodeTLSServerHello(
|
|
const Buffer; const Size: Integer;
|
|
var ServerHello: TTLSServerHello): Integer;
|
|
|
|
function EncodeTLSHandshakeServerHello(
|
|
var Buffer; const Size: Integer;
|
|
const ServerHello: TTLSServerHello): Integer;
|
|
|
|
|
|
|
|
{ }
|
|
{ Certificate }
|
|
{ }
|
|
function EncodeTLSHandshakeCertificate(
|
|
var Buffer; const Size: Integer;
|
|
const CertificateList: TTLSCertificateList): Integer;
|
|
|
|
|
|
|
|
{ }
|
|
{ ServerKeyExchange }
|
|
{ }
|
|
function EncodeTLSHandshakeServerKeyExchange(
|
|
var Buffer; const Size: Integer;
|
|
const KeyExchangeAlgorithm: TTLSKeyExchangeAlgorithm;
|
|
const ServerKeyExchange: TTLSServerKeyExchange): Integer;
|
|
|
|
|
|
|
|
{ }
|
|
{ CertificateRequest }
|
|
{ }
|
|
type
|
|
TTLSClientCertificateTypeArray = array of TTLSClientCertificateType;
|
|
|
|
TTLSCertificateRequest = record
|
|
CertificateTypes : TTLSClientCertificateTypeArray;
|
|
SupportedSignatureAlgorithms : TTLSSignatureAndHashAlgorithmArray;
|
|
DistinguishedName : RawByteString;
|
|
end;
|
|
|
|
function EncodeTLSCertificateRequest(
|
|
var Buffer; const Size: Integer;
|
|
const CertificateRequest: TTLSCertificateRequest): Integer;
|
|
|
|
function DecodeTLSCertificateRequest(
|
|
const Buffer; const Size: Integer;
|
|
var CertificateRequest: TTLSCertificateRequest): Integer;
|
|
|
|
function EncodeTLSHandshakeCertificateRequest(
|
|
var Buffer; const Size: Integer;
|
|
const CertificateRequest: TTLSCertificateRequest): Integer;
|
|
|
|
|
|
|
|
{ }
|
|
{ ServerHelloDone }
|
|
{ }
|
|
function EncodeTLSHandshakeServerHelloDone(var Buffer; const Size: Integer): Integer;
|
|
|
|
|
|
|
|
{ }
|
|
{ CertificateVerify }
|
|
{ }
|
|
function EncodeTLSHandshakeCertificateVerify(var Buffer; const Size: Integer): Integer;
|
|
|
|
|
|
|
|
{ }
|
|
{ PreMasterSecret }
|
|
{ }
|
|
type
|
|
TTLSPreMasterSecret = packed record
|
|
client_version : TTLSProtocolVersion;
|
|
random : array[0..45] of Byte;
|
|
end;
|
|
|
|
const
|
|
TLSPreMasterSecretSize = SizeOf(TTLSPreMasterSecret);
|
|
|
|
procedure InitTLSPreMasterSecret_Random(
|
|
var PreMasterSecret: TTLSPreMasterSecret;
|
|
const ClientVersion: TTLSProtocolVersion);
|
|
|
|
function TLSPreMasterSecretToStr(const PreMasterSecret: TTLSPreMasterSecret): RawByteString;
|
|
|
|
procedure InitTLSEncryptedPreMasterSecret_RSA(
|
|
var EncryptedPreMasterSecret: RawByteString;
|
|
const PreMasterSecret: TTLSPreMasterSecret;
|
|
const RSAPublicKey: TRSAPublicKey);
|
|
|
|
|
|
|
|
{ }
|
|
{ ClientKeyExchange }
|
|
{ }
|
|
function EncodeTLSHandshakeClientKeyExchange(
|
|
var Buffer; const Size: Integer;
|
|
const KeyExchangeAlgorithm: TTLSKeyExchangeAlgorithm;
|
|
const ClientKeyExchange: TTLSClientKeyExchange): Integer;
|
|
|
|
|
|
|
|
{ }
|
|
{ Finished }
|
|
{ }
|
|
function EncodeTLSHandshakeFinished(var Buffer; const Size: Integer;
|
|
const MasterSecret: RawByteString;
|
|
const ProtocolVersion: TTLSProtocolVersion;
|
|
const HandshakeData: RawByteString;
|
|
const SenderIsClient: Boolean): Integer;
|
|
|
|
|
|
|
|
{ }
|
|
{ ChangeCipherSpec Protocol }
|
|
{ }
|
|
type
|
|
TTLSChangeCipherSpecType = (
|
|
spectype_change_cipher_spec = 1);
|
|
TTLSChangeCipherSpec = packed record
|
|
_type : TTLSChangeCipherSpecType;
|
|
end;
|
|
PTLSChangeCipherSpec = ^TTLSChangeCipherSpec;
|
|
|
|
const
|
|
TLSChangeCipherSpecSize = SizeOf(TTLSChangeCipherSpec);
|
|
|
|
procedure InitTLSChangeCipherSpec(var ChangeCipherSpec: TTLSChangeCipherSpec);
|
|
|
|
|
|
|
|
{ }
|
|
{ Test }
|
|
{ }
|
|
{$IFDEF TLS_TEST}
|
|
procedure Test;
|
|
{$ENDIF}
|
|
|
|
|
|
|
|
implementation
|
|
|
|
uses
|
|
{ System }
|
|
|
|
SysUtils,
|
|
|
|
{ Utils }
|
|
|
|
flcStrings,
|
|
flcHash,
|
|
|
|
{ Cipher }
|
|
|
|
flcCipherRandom,
|
|
|
|
{ TLS }
|
|
|
|
flcTLSAlert,
|
|
flcTLSErrors,
|
|
flcTLSPRF,
|
|
flcTLSOpaqueEncoding;
|
|
|
|
|
|
|
|
{ }
|
|
{ Handshake Header }
|
|
{ }
|
|
function TLSHandshakeTypeToStr(const A: TTLSHandshakeType): String;
|
|
begin
|
|
case A of
|
|
tlshtHello_request : Result := 'Hello request';
|
|
tlshtClient_hello : Result := 'Client hello';
|
|
tlshtServer_hello : Result := 'Server hello';
|
|
tlshtCertificate : Result := 'Certificate';
|
|
tlshtServer_key_exchange : Result := 'Server key exchange';
|
|
tlshtCertificate_request : Result := 'Certficiate request';
|
|
tlshtServer_hello_done : Result := 'Server hello done';
|
|
tlshtCertificate_verify : Result := 'Certificate verify';
|
|
tlshtClient_key_exchange : Result := 'Client key exchange';
|
|
tlshtFinished : Result := 'Finished';
|
|
else
|
|
Result := '[Handshake#' + IntToStr(Ord(A)) + ']';
|
|
end;
|
|
end;
|
|
|
|
procedure EncodeTLSHandshakeHeader(
|
|
var Handshake: TTLSHandshakeHeader;
|
|
const MsgType: TTLSHandshakeType;
|
|
const Length: Integer);
|
|
var I : Byte;
|
|
L : Integer;
|
|
begin
|
|
Assert(Length >= 0);
|
|
Assert(Length <= $FFFFFF);
|
|
Handshake.msg_type := MsgType;
|
|
L := Length;
|
|
I := L div $10000;
|
|
Handshake.length[0] := I;
|
|
L := L - I * $10000;
|
|
I := L div $100;
|
|
Handshake.length[1] := I;
|
|
L := L - I * $100;
|
|
I := Byte(L);
|
|
Handshake.length[2] := I;
|
|
end;
|
|
|
|
procedure DecodeTLSHandshakeHeader(
|
|
const Handshake: TTLSHandshakeHeader;
|
|
var MsgType: TTLSHandshakeType;
|
|
var Length: Integer);
|
|
begin
|
|
MsgType := Handshake.msg_type;
|
|
Length :=
|
|
Handshake.length[0] * $10000 +
|
|
Handshake.length[1] * $100 +
|
|
Handshake.length[2];
|
|
end;
|
|
|
|
function TLSHandshakeDataPtr(
|
|
const Buffer; const Size: Integer;
|
|
var DataSize: Integer): Pointer;
|
|
var P : PByte;
|
|
begin
|
|
P := @Buffer;
|
|
if not Assigned(P) or (Size < TLSHandshakeHeaderSize) then
|
|
raise ETLSError.Create(TLSError_InvalidBuffer);
|
|
DataSize := Size - TLSHandshakeHeaderSize;
|
|
Inc(P, TLSHandshakeHeaderSize);
|
|
Result := P;
|
|
end;
|
|
|
|
function EncodeTLSHandshake(
|
|
var Buffer; const Size: Integer;
|
|
const MsgType: TTLSHandshakeType; const MsgSize: Integer): Integer;
|
|
var L : Integer;
|
|
P : PByte;
|
|
begin
|
|
Assert(MsgSize >= 0);
|
|
P := @Buffer;
|
|
L := TLSHandshakeHeaderSize + MsgSize;
|
|
if not Assigned(P) or (Size < L) then
|
|
raise ETLSError.CreateAlertBufferEncode;
|
|
EncodeTLSHandshakeHeader(PTLSHandshakeHeader(P)^, MsgType, MsgSize);
|
|
Result := L;
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ Hello Request }
|
|
{ }
|
|
function EncodeTLSHandshakeHelloRequest(var Buffer; const Size: Integer): Integer;
|
|
begin
|
|
Result := EncodeTLSHandshake(Buffer, Size, tlshtHello_request, 0);
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ ClientHello }
|
|
{ client_version : TTLSProtocolVersion; }
|
|
{ random : TTLSRandom; }
|
|
{ session_id : TLSSessionID; }
|
|
{ cipher_suites_count : Word; }
|
|
{ cipher_suites : <2..2^16-1> TTLSCipherSuite; }
|
|
{ compression_methods_count : Byte; }
|
|
{ compression_methods : <1..2^8-1> TTLSCompressionMethod; }
|
|
{ select (extensions_present) }
|
|
{ case false: ; }
|
|
{ case true: Extension extensions<0..2^16-1>; }
|
|
{ }
|
|
procedure InitTLSClientHello(
|
|
var ClientHello: TTLSClientHello;
|
|
const ProtocolVersion: TTLSProtocolVersion;
|
|
const SessionID: RawByteString);
|
|
begin
|
|
ClientHello.ProtocolVersion := ProtocolVersion;
|
|
InitTLSSessionID(ClientHello.SessionID, SessionID);
|
|
InitTLSRandom(ClientHello.Random);
|
|
end;
|
|
|
|
function EncodeTLSClientHello(
|
|
var Buffer; const Size: Integer;
|
|
var ClientHello: TTLSClientHello): Integer;
|
|
var P : PByte;
|
|
L, N : Integer;
|
|
C : TTLSCipherSuite;
|
|
D : TTLSCompressionMethod;
|
|
Suite : TTLSCipherSuiteRec;
|
|
SignRSA : Boolean;
|
|
begin
|
|
Assert(Assigned(@Buffer));
|
|
|
|
N := Size;
|
|
P := @Buffer;
|
|
|
|
// client_version
|
|
Dec(N, TLSProtocolVersionSize);
|
|
if N < 0 then
|
|
raise ETLSError.CreateAlertBufferEncode;
|
|
PTLSProtocolVersion(P)^ := ClientHello.ProtocolVersion;
|
|
Inc(P, TLSProtocolVersionSize);
|
|
// random
|
|
Dec(N, TLSRandomSize);
|
|
if N < 0 then
|
|
raise ETLSError.CreateAlertBufferEncode;
|
|
PTLSRandom(P)^ := ClientHello.Random;
|
|
Inc(P, TLSRandomSize);
|
|
// session_id
|
|
L := EncodeTLSSessionID(P^, N, ClientHello.SessionID);
|
|
Dec(N, L);
|
|
Inc(P, L);
|
|
// cipher_suites
|
|
L := 0;
|
|
for C := Low(TTLSCipherSuite) to High(TTLSCipherSuite) do
|
|
if C in ClientHello.CipherSuites then
|
|
Inc(L);
|
|
Assert(L <= $FFFF);
|
|
EncodeTLSLen16(P^, N, L * TLSCipherSuiteRecSize);
|
|
Inc(P, 2);
|
|
Dec(N, 2 + L * TLSCipherSuiteRecSize);
|
|
if N < 0 then
|
|
raise ETLSError.CreateAlertBufferEncode;
|
|
SignRSA := False;
|
|
for C := Low(TTLSCipherSuite) to High(TTLSCipherSuite) do
|
|
if C in ClientHello.CipherSuites then
|
|
begin
|
|
Suite.B1 := TLSCipherSuiteInfo[C].Rec.B1;
|
|
Suite.B2 := TLSCipherSuiteInfo[C].Rec.B2;
|
|
PTLSCipherSuiteRec(P)^ := Suite;
|
|
Inc(P, TLSCipherSuiteRecSize);
|
|
if TLSCipherSuiteInfo[C].Authentication = tlscsaRSA then
|
|
SignRSA := True;
|
|
end;
|
|
// compression_methods
|
|
L := 0;
|
|
for D := Low(TTLSCompressionMethod) to High(TTLSCompressionMethod) do
|
|
if D in ClientHello.CompressionMethods then
|
|
Inc(L);
|
|
Assert(L <= $FF);
|
|
Dec(N, 1 + L);
|
|
if N < 0 then
|
|
raise ETLSError.CreateAlertBufferEncode;
|
|
P^ := L;
|
|
Inc(P);
|
|
for D := Low(TTLSCompressionMethod) to High(TTLSCompressionMethod) do
|
|
if D in ClientHello.CompressionMethods then
|
|
begin
|
|
P^ := Ord(D);
|
|
Inc(P);
|
|
end;
|
|
// extensions
|
|
{
|
|
if IsTLS12(ClientHello.ProtocolVersion) then
|
|
if SignRSA then
|
|
begin
|
|
SetLength(ClientHello.SignAndHashAlgos, 5);
|
|
ClientHello.SignAndHashAlgos[0].Hash := tlshaSHA1;
|
|
ClientHello.SignAndHashAlgos[0].Signature := tlssaRSA;
|
|
ClientHello.SignAndHashAlgos[1].Hash := tlshaSHA224;
|
|
ClientHello.SignAndHashAlgos[1].Signature := tlssaRSA;
|
|
ClientHello.SignAndHashAlgos[2].Hash := tlshaSHA256;
|
|
ClientHello.SignAndHashAlgos[2].Signature := tlssaRSA;
|
|
ClientHello.SignAndHashAlgos[3].Hash := tlshaSHA384;
|
|
ClientHello.SignAndHashAlgos[3].Signature := tlssaRSA;
|
|
ClientHello.SignAndHashAlgos[4].Hash := tlshaSHA512;
|
|
ClientHello.SignAndHashAlgos[4].Signature := tlssaRSA;
|
|
ClientHello.ExtensionsPresent := True;
|
|
L := EncodeTLSClientHelloExtension_SignatureAlgorithms(P^, N, ClientHello);
|
|
Dec(N, L);
|
|
end;
|
|
}
|
|
Result := Size - N;
|
|
end;
|
|
|
|
function DecodeTLSClientHello(
|
|
const Buffer; const Size: Integer;
|
|
var ClientHello: TTLSClientHello): Integer;
|
|
var P : PByte;
|
|
L, N, C, ExtType, ExtLen : Integer;
|
|
I : Word;
|
|
E : TTLSCipherSuite;
|
|
F : TTLSCipherSuiteRec;
|
|
D : TTLSCompressionMethod;
|
|
begin
|
|
Assert(Assigned(@Buffer));
|
|
Assert(Size >= 0);
|
|
|
|
N := Size;
|
|
P := @Buffer;
|
|
|
|
// client_version
|
|
Dec(N, TLSProtocolVersionSize);
|
|
if N < 0 then
|
|
raise ETLSError.CreateAlertBufferDecode;
|
|
ClientHello.ProtocolVersion := PTLSProtocolVersion(P)^;
|
|
Inc(P, TLSProtocolVersionSize);
|
|
// random
|
|
Dec(N, TLSRandomSize);
|
|
if N < 0 then
|
|
raise ETLSError.CreateAlertBufferDecode;
|
|
ClientHello.Random := PTLSRandom(P)^;
|
|
Inc(P, TLSRandomSize);
|
|
// session_id
|
|
L := DecodeTLSSessionID(P^, N, ClientHello.SessionID);
|
|
Dec(N, L);
|
|
Inc(P, L);
|
|
// cipher_suites
|
|
ClientHello.CipherSuites := [];
|
|
L := DecodeTLSLen16(P^, N, C);
|
|
Dec(N, L);
|
|
Inc(P, L);
|
|
Dec(N, C);
|
|
if N < 0 then
|
|
raise ETLSError.CreateAlertBufferDecode;
|
|
C := C div TLSCipherSuiteRecSize;
|
|
for I := 0 to C - 1 do
|
|
begin
|
|
F := PTLSCipherSuiteRec(P)^;
|
|
E := GetCipherSuiteByRec(F.B1, F.B2);
|
|
Include(ClientHello.CipherSuites, E);
|
|
Inc(P, TLSCipherSuiteRecSize);
|
|
end;
|
|
// compression_methods
|
|
Dec(N);
|
|
if N < 0 then
|
|
raise ETLSError.CreateAlertBufferDecode;
|
|
ClientHello.CompressionMethods := [];
|
|
C := P^;
|
|
Inc(P);
|
|
Dec(N, C);
|
|
if N < 0 then
|
|
raise ETLSError.CreateAlertBufferDecode;
|
|
for I := 0 to C - 1 do
|
|
begin
|
|
D := PTLSCompressionMethod(P)^;
|
|
Include(ClientHello.CompressionMethods, D);
|
|
Inc(P, TLSCompressionMethodSize);
|
|
end;
|
|
// extensions
|
|
ClientHello.ExtensionsPresent := N > 0;
|
|
while N > 0 do
|
|
begin
|
|
if N < 4 then
|
|
raise ETLSError.CreateAlertBufferDecode;
|
|
DecodeTLSWord16(P^, N, ExtType);
|
|
Inc(P, 2);
|
|
Dec(N, 2);
|
|
DecodeTLSLen16(P^, N, ExtLen);
|
|
Inc(P, 2);
|
|
Dec(N, 2);
|
|
Dec(N, ExtLen);
|
|
if N < 0 then
|
|
raise ETLSError.CreateAlertBufferDecode;
|
|
Inc(P, ExtLen);
|
|
end;
|
|
|
|
Result := Size - N;
|
|
end;
|
|
|
|
function EncodeTLSHandshakeClientHello(
|
|
var Buffer; const Size: Integer;
|
|
var ClientHello: TTLSClientHello): Integer;
|
|
var P : Pointer;
|
|
N, L : Integer;
|
|
begin
|
|
P := TLSHandshakeDataPtr(Buffer, Size, N);
|
|
L := EncodeTLSClientHello(P^, N, ClientHello);
|
|
Result := EncodeTLSHandshake(Buffer, Size, tlshtClient_hello, L);
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ ServerHello }
|
|
{ server_version : TTLSProtocolVersion; }
|
|
{ random : TTLSRandom; }
|
|
{ session_id : TLSSessionID; }
|
|
{ cipher_suite : TTLSCipherSuite; }
|
|
{ compression_method : TTLSCompressionMethod; }
|
|
{ select (extensions_present) }
|
|
{ case false: ; }
|
|
{ case true: Extension extensions<0..2^16-1>; }
|
|
{ }
|
|
procedure InitTLSServerHello(
|
|
var ServerHello: TTLSServerHello;
|
|
const ProtocolVersion: TTLSProtocolVersion;
|
|
const SessionID: RawByteString;
|
|
const CipherSuite: TTLSCipherSuiteRec;
|
|
const CompressionMethod: TTLSCompressionMethod);
|
|
begin
|
|
ServerHello.ProtocolVersion := ProtocolVersion;
|
|
InitTLSSessionID(ServerHello.SessionID, SessionID);
|
|
ServerHello.CipherSuite := CipherSuite;
|
|
ServerHello.CompressionMethod := CompressionMethod;
|
|
InitTLSRandom(ServerHello.Random);
|
|
end;
|
|
|
|
function EncodeTLSServerHello(
|
|
var Buffer; const Size: Integer;
|
|
const ServerHello: TTLSServerHello): Integer;
|
|
var P : PByte;
|
|
L, N : Integer;
|
|
begin
|
|
Assert(Assigned(@Buffer));
|
|
Assert(Size >= 0);
|
|
|
|
N := Size;
|
|
P := @Buffer;
|
|
// server_version
|
|
Dec(N, TLSProtocolVersionSize);
|
|
if N < 0 then
|
|
raise ETLSError.CreateAlertBufferEncode;
|
|
PTLSProtocolVersion(P)^ := ServerHello.ProtocolVersion;
|
|
Inc(P, TLSProtocolVersionSize);
|
|
// random
|
|
Dec(N, TLSRandomSize);
|
|
if N < 0 then
|
|
raise ETLSError.CreateAlertBufferEncode;
|
|
PTLSRandom(P)^ := ServerHello.Random;
|
|
Inc(P, TLSRandomSize);
|
|
// session_id
|
|
L := EncodeTLSSessionID(P^, N, ServerHello.SessionID);
|
|
Dec(N, L);
|
|
Inc(P, L);
|
|
// cipher_suite
|
|
Dec(N, TLSCipherSuiteRecSize);
|
|
if N < 0 then
|
|
raise ETLSError.CreateAlertBufferEncode;
|
|
PTLSCipherSuiteRec(P)^ := ServerHello.CipherSuite;
|
|
Inc(P, TLSCipherSuiteRecSize);
|
|
// compression_method
|
|
Dec(N, TLSCompressionMethodSize);
|
|
if N < 0 then
|
|
raise ETLSError.CreateAlertBufferEncode;
|
|
PTLSCompressionMethod(P)^ := ServerHello.CompressionMethod;
|
|
|
|
Result := Size - N;
|
|
end;
|
|
|
|
function DecodeTLSServerHello(
|
|
const Buffer; const Size: Integer;
|
|
var ServerHello: TTLSServerHello): Integer;
|
|
var P : PByte;
|
|
L, N : Integer;
|
|
begin
|
|
Assert(Assigned(@Buffer));
|
|
Assert(Size >= 0);
|
|
|
|
N := Size;
|
|
P := @Buffer;
|
|
// server_version
|
|
Dec(N, TLSProtocolVersionSize);
|
|
if N < 0 then
|
|
raise ETLSError.CreateAlertBufferDecode;
|
|
ServerHello.ProtocolVersion := PTLSProtocolVersion(P)^;
|
|
Inc(P, TLSProtocolVersionSize);
|
|
// random
|
|
Dec(N, TLSRandomSize);
|
|
if N < 0 then
|
|
raise ETLSError.CreateAlertBufferDecode;
|
|
ServerHello.Random := PTLSRandom(P)^;
|
|
Inc(P, TLSRandomSize);
|
|
// session_id
|
|
L := DecodeTLSSessionID(P^, N, ServerHello.SessionID);
|
|
Dec(N, L);
|
|
Inc(P, L);
|
|
// cipher_suite
|
|
Dec(N, TLSCipherSuiteRecSize);
|
|
if N < 0 then
|
|
raise ETLSError.CreateAlertBufferDecode;
|
|
ServerHello.CipherSuite := PTLSCipherSuiteRec(P)^;
|
|
Inc(P, TLSCipherSuiteRecSize);
|
|
// compression_method
|
|
Dec(N, TLSCompressionMethodSize);
|
|
if N < 0 then
|
|
raise ETLSError.CreateAlertBufferDecode;
|
|
ServerHello.CompressionMethod := PTLSCompressionMethod(P)^;
|
|
|
|
Result := Size - N;
|
|
end;
|
|
|
|
function EncodeTLSHandshakeServerHello(
|
|
var Buffer; const Size: Integer;
|
|
const ServerHello: TTLSServerHello): Integer;
|
|
var P : Pointer;
|
|
N, L : Integer;
|
|
begin
|
|
P := TLSHandshakeDataPtr(Buffer, Size, N);
|
|
L := EncodeTLSServerHello(P^, N, ServerHello);
|
|
Result := EncodeTLSHandshake(Buffer, Size, tlshtServer_hello, L);
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ Certificate }
|
|
{ certificate_list : <0..2^24-1> ASN.1Cert; }
|
|
{ }
|
|
{ ASN.1Cert = <1..2^24-1> opaque; }
|
|
{ }
|
|
function EncodeTLSHandshakeCertificate(
|
|
var Buffer; const Size: Integer;
|
|
const CertificateList: TTLSCertificateList): Integer;
|
|
var P : Pointer;
|
|
N, L : Integer;
|
|
begin
|
|
P := TLSHandshakeDataPtr(Buffer, Size, N);
|
|
L := EncodeTLSCertificate(P^, N, CertificateList);
|
|
Result := EncodeTLSHandshake(Buffer, Size, tlshtCertificate, L);
|
|
end;
|
|
|
|
|
|
|
|
|
|
{ }
|
|
{ ServerKeyExchange }
|
|
{ }
|
|
function EncodeTLSHandshakeServerKeyExchange(
|
|
var Buffer; const Size: Integer;
|
|
const KeyExchangeAlgorithm: TTLSKeyExchangeAlgorithm;
|
|
const ServerKeyExchange: TTLSServerKeyExchange): Integer;
|
|
var P : Pointer;
|
|
N, L : Integer;
|
|
begin
|
|
P := TLSHandshakeDataPtr(Buffer, Size, N);
|
|
L := EncodeTLSServerKeyExchange(P^, N, KeyExchangeAlgorithm, ServerKeyExchange);
|
|
Result := EncodeTLSHandshake(Buffer, Size, tlshtServer_key_exchange, L);
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ Certificate Request }
|
|
{ certificate_types : ClientCertificateType <1..2^8-1>; }
|
|
{ supported_signature_algorithms : SignatureAndHashAlgorithm <2^16-1>; }
|
|
{ certificate_authorities : DistinguishedName <0..2^16-1>; }
|
|
{ DistinguishedName = opaque <1..2^16-1> }
|
|
{ }
|
|
function EncodeTLSCertificateRequest(
|
|
var Buffer; const Size: Integer;
|
|
const CertificateRequest: TTLSCertificateRequest): Integer;
|
|
var P : PByte;
|
|
N, L, I : Integer;
|
|
begin
|
|
Assert(Assigned(@Buffer));
|
|
Assert(Size >= 0);
|
|
|
|
N := Size;
|
|
P := @Buffer;
|
|
// certificate_types
|
|
L := Length(CertificateRequest.CertificateTypes);
|
|
if (L <= 0) or (L > 255) then
|
|
raise ETLSError.Create(TLSError_InvalidParameter);
|
|
Dec(N, 1 + L);
|
|
if N < 0 then
|
|
raise ETLSError.Create(TLSError_InvalidBuffer);
|
|
P^ := L;
|
|
Inc(P);
|
|
for I := 0 to L - 1 do
|
|
begin
|
|
P^ := Ord(CertificateRequest.CertificateTypes[I]);
|
|
Inc(P);
|
|
end;
|
|
// supported_signature_algorithms
|
|
L := Length(CertificateRequest.SupportedSignatureAlgorithms);
|
|
EncodeTLSLen16(P^, N, L * TLSSignatureAndHashAlgorithmSize);
|
|
Inc(P, 2);
|
|
Dec(N, 2 + L * TLSSignatureAndHashAlgorithmSize);
|
|
if N < 0 then
|
|
raise ETLSError.Create(TLSError_InvalidBuffer);
|
|
for I := 0 to L - 1 do
|
|
begin
|
|
PTLSSignatureAndHashAlgorithm(P)^ := CertificateRequest.SupportedSignatureAlgorithms[I];
|
|
Inc(P, TLSSignatureAndHashAlgorithmSize);
|
|
end;
|
|
// certificate_authorities
|
|
L := EncodeTLSOpaque16(P^, N, CertificateRequest.DistinguishedName);
|
|
Dec(N, L);
|
|
Result := Size - N;
|
|
end;
|
|
|
|
function DecodeTLSCertificateRequest(
|
|
const Buffer; const Size: Integer;
|
|
var CertificateRequest: TTLSCertificateRequest): Integer;
|
|
var P : PByte;
|
|
N, L, I : Integer;
|
|
begin
|
|
Assert(Assigned(@Buffer));
|
|
Assert(Size >= 0);
|
|
|
|
N := Size;
|
|
P := @Buffer;
|
|
// certificate_types
|
|
Dec(N);
|
|
if N < 0 then
|
|
raise ETLSError.CreateAlertBufferDecode;
|
|
L := P^;
|
|
Inc(P);
|
|
Dec(N, L);
|
|
if N < 0 then
|
|
raise ETLSError.CreateAlertBufferDecode;
|
|
SetLength(CertificateRequest.CertificateTypes, L);
|
|
for I := 0 to L - 1 do
|
|
begin
|
|
CertificateRequest.CertificateTypes[I] := TTLSClientCertificateType(P^);
|
|
Inc(P);
|
|
end;
|
|
// supported_signature_algorithms
|
|
DecodeTLSLen16(P^, N, L);
|
|
Assert(L mod TLSSignatureAndHashAlgorithmSize = 0);
|
|
L := L div TLSSignatureAndHashAlgorithmSize;
|
|
Inc(P, 2);
|
|
Dec(N, 2 + L * TLSSignatureAndHashAlgorithmSize);
|
|
if N < 0 then
|
|
raise ETLSError.CreateAlertBufferDecode;
|
|
SetLength(CertificateRequest.SupportedSignatureAlgorithms, L);
|
|
for I := 0 to L - 1 do
|
|
begin
|
|
CertificateRequest.SupportedSignatureAlgorithms[I] := PTLSSignatureAndHashAlgorithm(P)^;
|
|
Inc(P, TLSSignatureAndHashAlgorithmSize);
|
|
end;
|
|
// certificate_authorities
|
|
L := DecodeTLSOpaque16(P^, N, CertificateRequest.DistinguishedName);
|
|
Dec(N, L);
|
|
Result := Size - N;
|
|
end;
|
|
|
|
function EncodeTLSHandshakeCertificateRequest(
|
|
var Buffer; const Size: Integer;
|
|
const CertificateRequest: TTLSCertificateRequest): Integer;
|
|
var P : Pointer;
|
|
N, L : Integer;
|
|
C : TTLSCertificateRequest;
|
|
begin
|
|
P := TLSHandshakeDataPtr(Buffer, Size, N);
|
|
L := EncodeTLSCertificateRequest(P^, N, C);
|
|
Result := EncodeTLSHandshake(Buffer, Size, tlshtCertificate_request, L);
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ Server Hello Done }
|
|
{ }
|
|
function EncodeTLSHandshakeServerHelloDone(var Buffer; const Size: Integer): Integer;
|
|
begin
|
|
Result := EncodeTLSHandshake(Buffer, Size, tlshtServer_hello_done, 0);
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ Certificate Verify }
|
|
{ }
|
|
function EncodeTLSHandshakeCertificateVerify(var Buffer; const Size: Integer): Integer;
|
|
begin
|
|
Result := EncodeTLSHandshake(Buffer, Size, tlshtCertificate_verify, 0);
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ PreMasterSecret }
|
|
{ client_version : ProtocolVersion; }
|
|
{ random : opaque[46]; }
|
|
{ }
|
|
{ EncryptedPreMasterSecret = public-key-encrypted PreMasterSecret }
|
|
{ Public-key-encrypted data is represented as an opaque vector <0..2^16-1>. }
|
|
{ Thus, the RSA-encrypted PreMasterSecret in a ClientKeyExchange is preceded }
|
|
{ by two length bytes. }
|
|
{ }
|
|
procedure InitTLSPreMasterSecret_Random(var PreMasterSecret: TTLSPreMasterSecret;
|
|
const ClientVersion: TTLSProtocolVersion);
|
|
begin
|
|
PreMasterSecret.client_version := ClientVersion;
|
|
SecureRandomBuf(PreMasterSecret.random, SizeOf(PreMasterSecret.random));
|
|
end;
|
|
|
|
function TLSPreMasterSecretToStr(const PreMasterSecret: TTLSPreMasterSecret): RawByteString;
|
|
begin
|
|
SetLength(Result, TLSPreMasterSecretSize);
|
|
Move(PreMasterSecret, Result[1], TLSPreMasterSecretSize);
|
|
end;
|
|
|
|
procedure InitTLSEncryptedPreMasterSecret_RSA(
|
|
var EncryptedPreMasterSecret: RawByteString;
|
|
const PreMasterSecret: TTLSPreMasterSecret;
|
|
const RSAPublicKey: TRSAPublicKey);
|
|
var B : array[0..1023] of Byte;
|
|
L : Integer;
|
|
begin
|
|
L := RSAEncrypt(rsaetRSAES_PKCS1, RSAPublicKey, PreMasterSecret, SizeOf(PreMasterSecret),
|
|
B, SizeOf(B));
|
|
SetLength(EncryptedPreMasterSecret, L + 2);
|
|
EncodeTLSLen16(EncryptedPremasterSecret[1], L + 2, L);
|
|
Move(B, EncryptedPreMasterSecret[3], L);
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ ClientKeyExchange }
|
|
{ }
|
|
{ select (KeyExchangeAlgorithm) }
|
|
{ case rsa : EncryptedPreMasterSecret; }
|
|
{ case dhe_dss : }
|
|
{ case dhe_rsa : }
|
|
{ case dh_dss : }
|
|
{ case dh_rsa : }
|
|
{ case dh_anon : ClientDiffieHellmanPublic; }
|
|
{ }
|
|
function EncodeTLSHandshakeClientKeyExchange(
|
|
var Buffer; const Size: Integer;
|
|
const KeyExchangeAlgorithm: TTLSKeyExchangeAlgorithm;
|
|
const ClientKeyExchange: TTLSClientKeyExchange): Integer;
|
|
var N, L : Integer;
|
|
P : PByte;
|
|
begin
|
|
P := TLSHandshakeDataPtr(Buffer, Size, N);
|
|
L := EncodeTLSClientKeyExchange(P^, N, KeyExchangeAlgorithm, ClientKeyExchange);
|
|
Result := EncodeTLSHandshake(Buffer, Size, tlshtClient_key_exchange, L);
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ Finished }
|
|
{ }
|
|
{ SSL 3: }
|
|
{ Finished }
|
|
{ opaque md5_hash[16]; }
|
|
{ opaque sha_hash[20]; }
|
|
{ }
|
|
{ TLS 1.0 1.1 1.2: }
|
|
{ Finished }
|
|
{ opaque verify_data[12]; }
|
|
{ }
|
|
type
|
|
TSSLFinishedMD5Hash = array[0..15] of Byte;
|
|
TSSLFinishedSHAHash = array[0..19] of Byte;
|
|
TSSLFinished = packed record
|
|
md5_hash : TSSLFinishedMD5Hash;
|
|
sha_hash : TSSLFinishedSHAHash;
|
|
end;
|
|
PSSLFinished = ^TSSLFinished;
|
|
|
|
TTLSFinished = packed record
|
|
verify_data : array[0..11] of Byte;
|
|
end;
|
|
PTLSFinished = ^TTLSFinished;
|
|
|
|
const
|
|
SSLFinishedSize = SizeOf(TSSLFinished);
|
|
TLSFinishedSize = Sizeof(TTLSFinished);
|
|
|
|
|
|
|
|
{ }
|
|
{ SSL 3.0: }
|
|
{ md5_hash = MD5(master_secret + pad2 + }
|
|
{ MD5(handshake_messages + Sender + master_secret + pad1)); }
|
|
{ sha_hash = SHA(master_secret + pad2 + }
|
|
{ SHA(handshake_messages + Sender + master_secret + pad1)); }
|
|
{ pad_1 = The character 0x36 repeated 48 times for MD5 or 40 times for SHA. }
|
|
{ pad_2 = The character 0x5c repeated 48 times for MD5 or 40 times for SHA. }
|
|
{ Sender = enum ( client(0x434C4E54), server(0x53525652) ) }
|
|
{ }
|
|
{ TLS 1.0: verify_data = }
|
|
{ PRF(master_secret, finished_label, MD5(handshake_messages) + }
|
|
{ SHA-1(handshake_messages)) [0..11]; }
|
|
{ }
|
|
{ TLS 1.2: verify_data = }
|
|
{ PRF(master_secret, finished_label, Hash(handshake_messages)) }
|
|
{ [0..verify_data_length-1]; }
|
|
{ }
|
|
{ For the PRF defined in Section 5, the Hash MUST be the Hash used as the }
|
|
{ basis for the PRF. Any cipher suite which defines a different PRF MUST }
|
|
{ also define the Hash to use in the Finished computation. }
|
|
{ In previous versions of TLS, the verify_data was always 12 octets }
|
|
{ long. In the current version of TLS, it depends on the cipher suite. }
|
|
{ Any cipher suite which does not explicitly specify verify_data_length has a }
|
|
{ verify_data_length equal to 12. This includes all existing cipher suites. }
|
|
{ All of the data from all messages in this handshake (not }
|
|
{ including any HelloRequest messages) up to, but not including, }
|
|
{ this message. This is only data visible at the handshake layer }
|
|
{ and does not include record layer headers. This is the }
|
|
{ concatenation of all the Handshake structures as defined in }
|
|
{ Section 7.4, exchanged thus far. }
|
|
{ }
|
|
procedure CalcSSLVerifyData(
|
|
const MasterSecret: RawByteString;
|
|
const HandshakeData: RawByteString;
|
|
const SenderIsClient: Boolean;
|
|
var md5_hash: TSSLFinishedMD5Hash;
|
|
var sha_hash: TSSLFinishedSHAHash);
|
|
var
|
|
M, S, T : RawByteString;
|
|
begin
|
|
if SenderIsClient then
|
|
T := #$43#$4C#$4E#$54
|
|
else
|
|
T := #$53#$52#$56#$52;
|
|
M := MD5DigestToStrA(
|
|
CalcMD5(MasterSecret + DupCharB(#$5C, 48) +
|
|
MD5DigestToStrA(
|
|
CalcMD5(HandshakeData + T + MasterSecret + DupCharB(#$36, 48)))));
|
|
S := SHA1DigestToStrA(
|
|
CalcSHA1(MasterSecret + DupCharB(#$5C, 40) +
|
|
SHA1DigestToStrA(
|
|
CalcSHA1(HandshakeData + T + MasterSecret + DupCharB(#$36, 40)))));
|
|
Move(M[1], md5_hash, 16);
|
|
Move(S[1], sha_hash, 20);
|
|
end;
|
|
|
|
const
|
|
client_finished_label = 'client finished';
|
|
server_finished_label = 'server finished';
|
|
|
|
function CalcTLSVerifyData(
|
|
const ProtocolVersion: TTLSProtocolVersion;
|
|
const MasterSecret: RawByteString;
|
|
const HandshakeData: RawByteString): RawByteString;
|
|
var VDL : Integer;
|
|
D1 : T128BitDigest;
|
|
D2 : T160BitDigest;
|
|
D3 : T256BitDigest;
|
|
Seed : RawByteString;
|
|
PRF : RawByteString;
|
|
begin
|
|
if IsTLS12OrLater(ProtocolVersion) then
|
|
begin
|
|
VDL := 12;
|
|
D3 := CalcSHA256(HandshakeData[1], Length(HandshakeData));
|
|
SetLength(Seed, SizeOf(D3));
|
|
Move(D3, Seed[1], SizeOf(D3));
|
|
PRF := tls12PRF_SHA256(MasterSecret, client_finished_label, Seed, VDL);
|
|
end else
|
|
if IsTLS10OrLater(ProtocolVersion) then
|
|
begin
|
|
VDL := 12;
|
|
D1 := CalcMD5(HandshakeData);
|
|
D2 := CalcSHA1(HandshakeData);
|
|
SetLength(Seed, SizeOf(D1) + SizeOf(D2));
|
|
Move(D1, Seed[1], SizeOf(D1));
|
|
Move(D2, Seed[SizeOf(D1) + 1], SizeOf(D2));
|
|
PRF := tls10PRF(MasterSecret, client_finished_label, Seed, VDL);
|
|
end
|
|
else
|
|
raise ETLSError.Create(TLSError_InvalidParameter);
|
|
Result := PRF;
|
|
end;
|
|
|
|
function EncodeTLSHandshakeFinished(var Buffer; const Size: Integer;
|
|
const MasterSecret: RawByteString;
|
|
const ProtocolVersion: TTLSProtocolVersion;
|
|
const HandshakeData: RawByteString;
|
|
const SenderIsClient: Boolean): Integer;
|
|
var N : Integer;
|
|
P : PByte;
|
|
V : RawByteString;
|
|
F : PSSLFinished;
|
|
begin
|
|
P := TLSHandshakeDataPtr(Buffer, Size, N);
|
|
if IsTLS10OrLater(ProtocolVersion) then
|
|
begin
|
|
V := CalcTLSVerifyData(ProtocolVersion, MasterSecret, HandshakeData);
|
|
Assert(Length(V) >= 12);
|
|
Move(V[1], PTLSFinished(P)^.verify_data[0], 12);
|
|
Result := EncodeTLSHandshake(Buffer, Size, tlshtFinished, TLSFinishedSize);
|
|
end else
|
|
if IsSSL3(ProtocolVersion) then
|
|
begin
|
|
F := PSSLFinished(P);
|
|
CalcSSLVerifyData(MasterSecret, HandshakeData, SenderIsClient, F^.md5_hash, F^.sha_hash);
|
|
Result := EncodeTLSHandshake(Buffer, Size, tlshtFinished, SSLFinishedSize);
|
|
end
|
|
else
|
|
raise ETLSError.Create(TLSError_InvalidParameter);
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ ChangeCipherSpec Protocol }
|
|
{ }
|
|
procedure InitTLSChangeCipherSpec(var ChangeCipherSpec: TTLSChangeCipherSpec);
|
|
begin
|
|
ChangeCipherSpec._type := spectype_change_cipher_spec;
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ Unit test }
|
|
{ }
|
|
{$IFDEF TLS_TEST}
|
|
{$ASSERTIONS ON}
|
|
procedure Test;
|
|
begin
|
|
Assert(TLSHandshakeHeaderSize = 4);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
|
|
|
|
end.
|
|
|