xtool/contrib/fundamentals/TLS/flcTLSTransportServer.pas

1127 lines
37 KiB
ObjectPascal

{******************************************************************************}
{ }
{ Library: Fundamentals TLS }
{ File name: flcTLSTransportServer.pas }
{ File version: 5.04 }
{ Description: TLS Transport Server }
{ }
{ 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: }
{ }
{ 2010/12/02 0.01 Initial development. }
{ 2010/12/15 0.02 Development. Simple client server test case. }
{ 2016/01/08 0.03 String changes. }
{ 2018/07/17 5.04 Revised for Fundamentals 5. }
{ 2020/05/19 5.08 Sign RSA authentication signature for DHE_RSA. }
{ }
{******************************************************************************}
{$INCLUDE flcTLS.inc}
unit flcTLSTransportServer;
interface
uses
{ System }
SyncObjs,
{ Utils }
flcStdTypes,
{ Cipher }
flcCipherRSA,
flcCipherDH,
{ X509 }
flcX509Certificate,
{ PEM }
flcPEM,
{ TLS }
flcTLSProtocolVersion,
flcTLSAlgorithmTypes,
flcTLSRandom,
flcTLSCipherSuite,
flcTLSAlert,
flcTLSKeyExchangeParams,
flcTLSCertificate,
flcTLSKeys,
flcTLSHandshake,
flcTLSTransportTypes,
flcTLSTransportConnection;
{ }
{ TLS Server }
{ }
const
DefaultTLSServerOptions = [];
DefaultTLSServerVersionOptions = AllTLSVersionOptions - [tlsvoSSL3];
DefaultTLSServerKeyExchangeOptions = AllTLSKeyExchangeOptions;
DefaultTLSServerCipherOptions = AllTLSCipherOptions;
DefaultTLSServerHashOptions = AllTLSHashOptions;
type
TTLSServer = class;
TTLSServerClientState = (
tlsscInit,
tlsscHandshakeAwaitingClientHello,
tlsscHandshakeAwaitingClientKeyExchange,
tlsscHandshakeAwaitingFinish,
tlsscConnection
);
TTLSServerClient = class(TTLSConnection)
protected
FServer : TTLSServer;
FUserObj : TObject;
FClientId : Integer;
FClientState : TTLSServerClientState;
FSessionID : RawByteString;
FCipherSuite : TTLSCipherSuite;
FCompression : TTLSCompressionMethod;
FClientHello : TTLSClientHello;
FClientHelloRandomStr : RawByteString;
FServerHello : TTLSServerHello;
FServerHelloRandomStr : RawByteString;
FServerKeyExchange : TTLSServerKeyExchange;
FClientKeyExchange : TTLSClientKeyExchange;
FPreMasterSecret : RawByteString;
FMasterSecret : RawByteString;
FDHState : PDHState;
procedure TriggerLog(const LogType: TTLSLogType; const LogMsg: String; const LogLevel: Integer); override;
procedure TriggerConnectionStateChange; override;
procedure TriggerAlert(const Level: TTLSAlertLevel; const Description: TTLSAlertDescription); override;
procedure TriggerHandshakeFinished; override;
procedure SetClientState(const State: TTLSServerClientState);
procedure TransportLayerSendProc(const Sender: TTLSConnection; const Buffer; const Size: Integer);
procedure SelectCompression(var Compression: TTLSCompressionMethod);
procedure SelectCipherSuite(var CipherSuite: TTLSCipherSuite);
procedure InitDHState;
procedure InitProtocolVersion;
procedure InitHandshakeServerHello;
procedure InitHandshakeServerKeyExchange;
procedure SendHandshakeHelloRequest;
procedure SendHandshakeServerHello;
procedure SendHandshakeCertificate;
procedure SendHandshakeServerKeyExchange;
procedure SendHandshakeCertificateRequest;
procedure SendHandshakeServerHelloDone;
procedure SendHandshakeFinished;
procedure HandleHandshakeClientHello(const Buffer; const Size: Integer);
procedure HandleHandshakeCertificateVerify(const Buffer; const Size: Integer);
procedure HandleHandshakeClientKeyExchange(const Buffer; const Size: Integer);
procedure HandleHandshakeFinished(const Buffer; const Size: Integer);
procedure HandleHandshakeMessage(const MsgType: TTLSHandshakeType; const Buffer; const Size: Integer); override;
procedure InitCipherSpecNone;
procedure DoStart;
public
constructor Create(const AServer: TTLSServer; const AUserObj: TObject);
destructor Destroy; override;
property UserObj: TObject read FUserObj;
procedure Start;
end;
TTLSServerOptions = set of (
tlssoNone
);
TTLSServerState = (
tlssInit,
tlssActive,
tlssStopped
);
TTLSServerTransportLayerSendProc = procedure (Server: TTLSServer; Client: TTLSServerClient; const Buffer; const Size: Integer) of object;
TTLSServerNotifyEvent = procedure (Sender: TTLSServer) of object;
TTLSServerLogEvent = procedure (Sender: TTLSServer; LogType: TTLSLogType; LogMsg: String; LogLevel: Integer) of object;
TTLSServerClientEvent = procedure (Sender: TTLSServer; Client: TTLSServerClient) of object;
TTLSServerClientAlertEvent = procedure (Sender: TTLSServer; Client: TTLSServerClient; Level: TTLSAlertLevel; Description: TTLSAlertDescription) of object;
TTLSServer = class
protected
FOnLog : TTLSServerLogEvent;
FOnClientStateChange : TTLSServerClientEvent;
FOnClientAlert : TTLSServerClientAlertEvent;
FOnClientHandshakeFinished : TTLSServerClientEvent;
FTransportLayerSendProc : TTLSServerTransportLayerSendProc;
FServerOptions : TTLSServerOptions;
FVersionOptions : TTLSVersionOptions;
FKeyExchangeOptions : TTLSKeyExchangeOptions;
FCipherOptions : TTLSCipherOptions;
FHashOptions : TTLSHashOptions;
FCertificateList : TTLSCertificateList;
FPrivateKeyRSA : RawByteString;
FPEMFileName : String;
FPEMText : RawByteString;
FDHKeySize : Integer;
FLock : TCriticalSection;
FState : TTLSServerState;
FClients : array of TTLSServerClient;
FClientNr : Integer;
FX509RSAPrivateKey : TX509RSAPrivateKey;
FRSAPrivateKey : TRSAPrivateKey;
procedure Init; virtual;
procedure Lock;
procedure Unlock;
procedure Log(const LogType: TTLSLogType; const LogMsg: String; const LogLevel: Integer = 0); overload; virtual;
procedure Log(const LogType: TTLSLogType; const LogMsg: String; const Args: array of const; const LogLevel: Integer = 0); overload;
procedure CheckNotActive;
procedure CheckActive;
procedure SetServerOptions(const AServerOptions: TTLSServerOptions);
procedure SetVersionOptions(const AVersionOptions: TTLSVersionOptions);
procedure SetCipherOptions(const ACipherOptions: TTLSCipherOptions);
procedure SetKeyExchangeOptions(const AKeyExchangeOptions: TTLSKeyExchangeOptions);
procedure SetHashOptions(const AHashOptions: TTLSHashOptions);
procedure SetCertificateList(const List: TTLSCertificateList);
procedure SetPrivateKeyRSA(const APrivateKeyRSA: RawByteString);
function GetPrivateKeyRSAPEM: RawByteString;
procedure SetPrivateKeyRSAPEM(const APrivateKeyRSAPEM: RawByteString);
procedure SetPEMFileName(const APEMFileName: String);
procedure SetPEMText(const APEMText: RawByteString);
procedure SetDHKeySize(const ADHKeySize: Integer);
procedure ClientLog(const Client: TTLSServerClient; const LogType: TTLSLogType; const LogMsg: String; const LogLevel: Integer);
procedure ClientStateChange(const Client: TTLSServerClient);
procedure ClientAlert(const Client: TTLSServerClient; const Level: TTLSAlertLevel; const Description: TTLSAlertDescription);
procedure ClientHandshakeFinished(const Client: TTLSServerClient);
function CreateClient(const UserObj: TObject): TTLSServerClient; virtual;
function GetClientCount: Integer;
function GetClient(const Idx: Integer): TTLSServerClient;
function GetClientIndex(const Client: TTLSServerClient): Integer;
procedure ClientTransportLayerSend(const Sender: TTLSServerClient; const Buffer; const Size: Integer);
procedure InitFromPEM;
procedure InitPrivateKey;
procedure AllocateSessionID(var SessionID: RawByteString);
procedure DoStart;
procedure DoStop;
public
constructor Create(const ATransportLayerSendProc: TTLSServerTransportLayerSendProc);
destructor Destroy; override;
property OnLog: TTLSServerLogEvent read FOnLog write FOnLog;
property OnClientAlert: TTLSServerClientAlertEvent read FOnClientAlert write FOnClientAlert;
property OnClientStateChange: TTLSServerClientEvent read FOnClientStateChange write FOnClientStateChange;
property OnClientHandshakeFinished: TTLSServerClientEvent read FOnClientHandshakeFinished write FOnClientHandshakeFinished;
property ServerOptions: TTLSServerOptions read FServerOptions write SetServerOptions default DefaultTLSServerOptions;
property VersionOptions: TTLSVersionOptions read FVersionOptions write SetVersionOptions default DefaultTLSServerVersionOptions;
property KeyExchangeOptions: TTLSKeyExchangeOptions read FKeyExchangeOptions write SetKeyExchangeOptions default DefaultTLSServerKeyExchangeOptions;
property CipherOptions: TTLSCipherOptions read FCipherOptions write SetCipherOptions default DefaultTLSServerCipherOptions;
property HashOptions: TTLSHashOptions read FHashOptions write SetHashOptions default DefaultTLSServerHashOptions;
property CertificateList: TTLSCertificateList read FCertificateList write SetCertificateList;
property PrivateKeyRSA: RawByteString read FPrivateKeyRSA write SetPrivateKeyRSA;
property PrivateKeyRSAPEM: RawByteString read GetPrivateKeyRSAPEM write SetPrivateKeyRSAPEM;
property PEMFileName: String read FPEMFileName write SetPEMFileName;
property PEMText: RawByteString read FPEMText write SetPEMText;
property DHKeySize: Integer read FDHKeySize write SetDHKeySize;
property State: TTLSServerState read FState;
procedure Start;
procedure Stop;
property ClientCount: Integer read GetClientCount;
property Client[const Idx: Integer]: TTLSServerClient read GetClient;
function AddClient(const UserObj: TObject): TTLSServerClient;
procedure RemoveClient(const Client: TTLSServerClient);
procedure ProcessTransportLayerReceivedData(
const AClient: TTLSServerClient;
const Buffer; const Size: Integer);
end;
implementation
uses
{ System }
SysUtils,
{ Utils }
flcBase64,
flcHugeInt,
{ Cipher }
flcCipherUtils,
flcCipherRandom,
{ TLS }
flcTLSErrors,
flcTLSSessionID,
flcTLSCipher;
{ }
{ TLS Server Client }
{ }
constructor TTLSServerClient.Create(const AServer: TTLSServer; const AUserObj: TObject);
begin
Assert(Assigned(AServer));
inherited Create(TransportLayerSendProc);
FServer := AServer;
FUserObj := AUserObj;
end;
destructor TTLSServerClient.Destroy;
begin
if Assigned(FDHState) then
begin
DHStateFinalise(FDHState^);
Dispose(FDHState);
FDHState := nil;
end;
inherited Destroy;
end;
procedure TTLSServerClient.TriggerLog(const LogType: TTLSLogType; const LogMsg: String; const LogLevel: Integer);
begin
inherited;
FServer.ClientLog(self, LogType, LogMsg, LogLevel);
end;
procedure TTLSServerClient.TriggerConnectionStateChange;
begin
inherited;
FServer.ClientStateChange(self);
end;
procedure TTLSServerClient.TriggerAlert(const Level: TTLSAlertLevel; const Description: TTLSAlertDescription);
begin
inherited;
FServer.ClientAlert(self, Level, Description);
end;
procedure TTLSServerClient.TriggerHandshakeFinished;
begin
inherited;
FServer.ClientHandshakeFinished(self);
end;
procedure TTLSServerClient.SetClientState(const State: TTLSServerClientState);
begin
FClientState := State;
end;
procedure TTLSServerClient.TransportLayerSendProc(const Sender: TTLSConnection; const Buffer; const Size: Integer);
begin
FServer.ClientTransportLayerSend(self, Buffer, Size);
end;
procedure TTLSServerClient.SelectCompression(var Compression: TTLSCompressionMethod);
begin
Compression := tlscmNull;
end;
procedure TTLSServerClient.SelectCipherSuite(var CipherSuite: TTLSCipherSuite);
var
I : TTLSCipherSuite;
C : PTLSCipherSuiteInfo;
begin
for I := High(TTLSCipherSuite) downto Low(TTLSCipherSuite) do
if (I <> tlscsNone) and (I in FClientHello.CipherSuites) then
begin
C := @TLSCipherSuiteInfo[I];
if C^.ServerSupport then
begin
CipherSuite := I;
exit;
end;
end;
CipherSuite := tlscsNone;
end;
procedure TTLSServerClient.InitDHState;
begin
New(FDHState);
DHStateInit(FDHState^);
(*
P = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A63A3620FFFFFFFFFFFFFFFF'
G = '00000002'
*)
{ HugeWordToHexB(FDHState^.Q);
HugeWordToHexB(FDHState^.X);
HugeWordToHexB(FDHState^.Y);
}
(*
DHInitHashAlgorithm(FDHState^, dhhSHA1);
HexToHugeWordB('FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A63A3620FFFFFFFFFFFFFFFF',
FDHState^.P);
HexToHugeWordB('00000002', FDHState^.G);
HexToHugeWordB('AF2FCC0B5ADD8F623266378ADBF72664D5913FFF', FDHState^.Q);
HexToHugeWordB('8E62C571E6ECCC720CF65E432FA779BE56EFAB46', FDHState^.X);
HexToHugeWordB('A37CCB97CAB7AC5C3C2B59FC7C33007B2CDD9FA6C616D8BA4A202EC901FE5F37F790295D5DAA990E22121C154B78E2201B36AB4A8944E61D34ADA541221653B49E432B782A5A59806E87ADBCC57033385FB9197CEC137170DD80466D82F431BA',
FDHState^.Y);
FDHState^.PrimePBitCount := DHWellKnownGroup[0].PBitCount;
FDHState^.PrimeQBitCount := DHWellKnownGroup[0].QBitCount;
*)
//DHGenerateKeys(FDHState^, dhhSHA1, DHQBitCount(FServer.FDHKeySize), FServer.FDHKeySize);
//// Use well known pairs
//// x/prikey is a random nr
HexToHugeWordB(DHWellKnownGroup[0].P_Hex, FDHState^.P);
HexToHugeWordB(DHWellKnownGroup[0].G_Hex, FDHState^.G);
DHDeriveKeysFromGroupParametersPG(
FDHState^, dhhSHA1, DHWellKnownGroup[0].QBitCount, DHWellKnownGroup[0].PBitCount,
FDHState^.P, FDHState^.G);
end;
procedure TTLSServerClient.InitProtocolVersion;
begin
FProtocolVersion := FClientHello.ProtocolVersion;
if IsSSL2(FProtocolVersion) then
raise ETLSError.CreateAlertBadProtocolVersion; // SSL2 not supported
if IsPostTLS12(FProtocolVersion) then
FProtocolVersion := TLSProtocolVersion12;
if not IsKnownTLSVersion(FProtocolVersion) then
raise ETLSError.CreateAlertBadProtocolVersion; // unknown SSL version
end;
procedure TTLSServerClient.InitHandshakeServerHello;
begin
InitTLSServerHello(FServerHello,
FProtocolVersion,
FSessionID,
FCipherSpecNew.CipherSuiteDetails.CipherSuiteInfo^.Rec,
FCompression);
{$IFDEF TLS_TEST_NO_RANDOM_HELLO}
FClientHello.Random.gmt_unix_time := 123;
FillChar(FServerHello.Random.random_bytes, 28, 117);
{$ENDIF}
FServerHelloRandomStr := TLSRandomToStr(FServerHello.Random);
end;
procedure TTLSServerClient.InitHandshakeServerKeyExchange;
begin
InitTLSServerKeyExchange(FServerKeyExchange);
case FCipherSpecNew.KeyExchangeAlgorithm of
tlskeaDHE_DSS,
tlskeaDHE_RSA,
tlskeaDH_Anon :
begin
InitDHState;
AssignTLSServerKeyExchangeDHParams(
FServerKeyExchange,
DHHugeWordKeyEncodeBytes(FDHState^.P),
DHHugeWordKeyEncodeBytes(FDHState^.G),
DHHugeWordKeyEncodeBytes(FDHState^.Y)); /////////
SignTLSServerKeyExchangeDH_RSA(
FServerKeyExchange,
PTLSClientServerRandom(@FClientHello.Random)^,
PTLSClientServerRandom(@FServerHello.Random)^,
FServer.FRSAPrivateKey);
end;
end;
end;
const
MaxHandshakeHelloRequestSize = 2048;
MaxHandshakeServerHelloSize = 2048;
MaxHandshakeCertificateSize = 65536;
MaxHandshakeServerKeyExchangeSize = 16384;
MaxHandshakeCertificateRequestSize = 16384;
MaxHandshakeServerHelloDoneSize = 16384;
MaxHandshakeFinishedSize = 2048;
procedure TTLSServerClient.SendHandshakeHelloRequest;
var
Buf : array[0..MaxHandshakeHelloRequestSize - 1] of Byte;
Size : Integer;
begin
Size := EncodeTLSHandshakeHelloRequest(Buf, SizeOf(Buf));
SendHandshake(Buf, Size);
end;
procedure TTLSServerClient.SendHandshakeServerHello;
var
Buf : array[0..MaxHandshakeServerHelloSize - 1] of Byte;
Size : Integer;
begin
InitHandshakeServerHello;
Size := EncodeTLSHandshakeServerHello(Buf, SizeOf(Buf), FServerHello);
SendHandshake(Buf, Size);
end;
procedure TTLSServerClient.SendHandshakeCertificate;
var
Buf : array[0..MaxHandshakeCertificateSize - 1] of Byte;
Size : Integer;
begin
Size := EncodeTLSHandshakeCertificate(Buf, SizeOf(Buf), FServer.FCertificateList);
SendHandshake(Buf, Size);
end;
procedure TTLSServerClient.SendHandshakeServerKeyExchange;
var
Buf : array[0..MaxHandshakeServerKeyExchangeSize - 1] of Byte;
Size : Integer;
begin
InitHandshakeServerKeyExchange;
Size := EncodeTLSHandshakeServerKeyExchange(Buf, SizeOf(Buf),
FCipherSpecNew.KeyExchangeAlgorithm, FServerKeyExchange);
SendHandshake(Buf, Size);
end;
procedure TTLSServerClient.SendHandshakeCertificateRequest;
var
Buf : array[0..MaxHandshakeCertificateRequestSize - 1] of Byte;
Size : Integer;
CReq : TTLSCertificateRequest;
begin
Size := EncodeTLSHandshakeCertificateRequest(Buf, SizeOf(Buf), CReq);
SendHandshake(Buf, Size);
end;
procedure TTLSServerClient.SendHandshakeServerHelloDone;
var
Buf : array[0..MaxHandshakeServerHelloDoneSize - 1] of Byte;
Size : Integer;
begin
Size := EncodeTLSHandshakeServerHelloDone(Buf, SizeOf(Buf));
SendHandshake(Buf, Size);
end;
procedure TTLSServerClient.SendHandshakeFinished;
var
Buf : array[0..MaxHandshakeFinishedSize - 1] of Byte;
Size : Integer;
begin
Size := EncodeTLSHandshakeFinished(Buf, SizeOf(Buf), FMasterSecret, FProtocolVersion, FVerifyHandshakeData, False);
SendHandshake(Buf, Size);
end;
procedure TTLSServerClient.HandleHandshakeClientHello(const Buffer; const Size: Integer);
begin
if FClientState <> tlsscHandshakeAwaitingClientHello then
raise ETLSError.CreateAlertUnexpectedMessage;
DecodeTLSClientHello(Buffer, Size, FClientHello);
FClientHelloRandomStr := TLSRandomToStr(FClientHello.Random);
{$IFDEF TLS_DEBUG}
Log(tlsltDebug, 'ClientHello:%s', [TLSProtocolVersionName(FClientHello.ProtocolVersion)]);
{$ENDIF}
InitProtocolVersion;
{$IFDEF TLS_DEBUG}
Log(tlsltDebug, 'ProtocolVersion:%s', [TLSProtocolVersionName(FProtocolVersion)]);
{$ENDIF}
SelectCompression(FCompression);
SelectCipherSuite(FCipherSuite);
{$IFDEF TLS_DEBUG}
Log(tlsltDebug, 'CipherSuite:%s', [TLSCipherSuiteInfo[FCipherSuite].Name]);
{$ENDIF}
if FCipherSuite = tlscsNone then
raise ETLSError.CreateAlert(TLSError_BadProtocol, tlsadHandshake_failure);
InitTLSSecurityParameters(FCipherSpecNew, FCompression, FCipherSuite);
SetClientState(tlsscHandshakeAwaitingClientKeyExchange);
SendHandshakeServerHello;
SendHandshakeCertificate;
{ The ServerKeyExchange message is sent by the server only when the ServerCertificate message (if sent) }
{ does not contain enough data to allow the user-agent to exchange a premaster secret. }
{ This is true for the following key exchange methods: }
{ RSA_EXPORT (if the public key in the server certificate is longer than 512 bits) }
{ DHE_DSS, DHE_DSS_EXPORT, DHE_RSA, DHE_RSA_EXPORT, DH_anon }
{ It is not legal to send the server key exchange message for the following key exchange methods: }
{ RSA, DH_DSS, DH_RSA, }
{ RSA_EXPORT (when the public key in the server certificate is less than or equal to 512 bits in length) }
{ Additionally, a ServerKeyExchange message may be sent, if it is required (e.g., if the server has no }
{ certificate, or if its certificate is for signing only). If the server is authenticated, it may }
{ request a certificate from the client. }
if FCipherSpecNew.KeyExchangeAlgorithm in [tlskeaDHE_DSS, tlskeaDHE_RSA, tlskeaDH_Anon] then
SendHandshakeServerKeyExchange;
SendHandshakeServerHelloDone;
end;
procedure TTLSServerClient.HandleHandshakeCertificateVerify(const Buffer; const Size: Integer);
begin
if FClientState <> tlsscHandshakeAwaitingClientKeyExchange then
raise ETLSError.CreateAlertUnexpectedMessage;
end;
procedure TTLSServerClient.HandleHandshakeClientKeyExchange(const Buffer; const Size: Integer);
var
Yc : HugeWord;
ZZ : HugeWord;
begin
if FClientState <> tlsscHandshakeAwaitingClientKeyExchange then
raise ETLSError.CreateAlertUnexpectedMessage;
DecodeTLSClientKeyExchange(Buffer, Size,
FCipherSpecNew.KeyExchangeAlgorithm,
True, FClientKeyExchange);
// pre-master-secret
case FCipherSpecNew.KeyExchangeAlgorithm of
tlskeaNone : ;
tlskeaNULL : ;
tlskeaRSA :
begin
{ RFC 5246: When RSA is used for server authentication and key exchange, a 48- }
{ byte pre_master_secret is generated by the client, encrypted under }
{ the server's public key, and sent to the server. The server uses its }
{ private key to decrypt the pre_master_secret. Both parties then }
{ convert the pre_master_secret into the master_secret, as specified above. }
FPreMasterSecret := RSADecryptStr(rsaetRSAES_PKCS1, FServer.FRSAPrivateKey,
FClientKeyExchange.EncryptedPreMasterSecret);
end;
tlskeaDHE_DSS,
tlskeaDHE_RSA,
tlskeaDH_Anon :
begin
Assert(Assigned(FDHState));
{ RFC 5246: A conventional Diffie-Hellman computation is performed. The }
{ negotiated key (Z) is used as the pre_master_secret, and is converted }
{ into the master_secret, as specified above. Leading bytes of Z that }
{ contain all zero bits are stripped before it is used as the }
{ pre_master_secret }
HugeWordInit(Yc);
HugeWordInit(ZZ);
DHHugeWordKeyDecodeBytes(Yc, FClientKeyExchange.ClientDiffieHellmanPublic.dh_Yc);
HugeWordPowerAndMod(ZZ, Yc, FDHState^.X, FDHState^.P);
FPreMasterSecret := DHHugeWordKeyEncodeBytes(ZZ);
HugeWordFinalise(ZZ);
HugeWordFinalise(Yc);
end;
else
raise ETLSError.CreateAlert(TLSError_BadProtocol, tlsadHandshake_failure);
end;
case FCipherSpecNew.KeyExchangeAlgorithm of
tlskeaRSA,
tlskeaDHE_DSS,
tlskeaDHE_RSA,
tlskeaDH_Anon :
begin
Assert(FPreMasterSecret <> '');
FMasterSecret := TLSMasterSecret(FProtocolVersion, FPreMasterSecret,
FClientHelloRandomStr, FServerHelloRandomStr);
SecureClearStr(FPreMasterSecret);
GenerateTLSKeys(FProtocolVersion,
FCipherSpecNew.CipherSuiteDetails.HashInfo^.KeyLength,
FCipherSpecNew.CipherSuiteDetails.CipherInfo^.KeyBits,
FCipherSpecNew.CipherSuiteDetails.CipherInfo^.IVSize * 8,
FMasterSecret,
FServerHelloRandomStr,
FClientHelloRandomStr,
FKeys);
GenerateFinalTLSKeys(FProtocolVersion,
FCipherSpecNew.CipherSuiteDetails.CipherInfo^.Exportable,
FCipherSpecNew.CipherSuiteDetails.CipherInfo^.ExpKeyMat * 8,
FServerHelloRandomStr,
FClientHelloRandomStr,
FKeys);
SetEncodeKeys(FKeys.ServerMACKey, FKeys.ServerEncKey, FKeys.ServerIV);
SetDecodeKeys(FKeys.ClientMACKey, FKeys.ClientEncKey, FKeys.ClientIV);
end;
end;
SetClientState(tlsscHandshakeAwaitingFinish);
end;
procedure TTLSServerClient.HandleHandshakeFinished(const Buffer; const Size: Integer);
begin
if FClientState <> tlsscHandshakeAwaitingFinish then
raise ETLSError.CreateAlertUnexpectedMessage;
SendChangeCipherSpec;
ChangeEncryptCipherSpec;
SetClientState(tlsscConnection);
SetConnectionState(tlscoApplicationData);
SendHandshakeFinished;
TriggerHandshakeFinished;
end;
procedure TTLSServerClient.HandleHandshakeMessage(const MsgType: TTLSHandshakeType; const Buffer; const Size: Integer);
begin
{$IFDEF TLS_DEBUG}
Log(tlsltDebug, 'R:Handshake:%s:%db', [TLSHandshakeTypeToStr(MsgType), Size]);
{$ENDIF}
case MsgType of
tlshtHello_request : ShutdownBadProtocol(tlsadUnexpected_message);
tlshtClient_hello : HandleHandshakeClientHello(Buffer, Size);
tlshtServer_hello : ShutdownBadProtocol(tlsadUnexpected_message);
tlshtCertificate : ShutdownBadProtocol(tlsadUnexpected_message);
tlshtServer_key_exchange : ShutdownBadProtocol(tlsadUnexpected_message);
tlshtCertificate_request : ShutdownBadProtocol(tlsadUnexpected_message);
tlshtServer_hello_done : ShutdownBadProtocol(tlsadUnexpected_message);
tlshtCertificate_verify : HandleHandshakeCertificateVerify(Buffer, Size);
tlshtClient_key_exchange : HandleHandshakeClientKeyExchange(Buffer, Size);
tlshtFinished : HandleHandshakeFinished(Buffer, Size);
else
ShutdownBadProtocol(tlsadUnexpected_message);
end;
end;
procedure TTLSServerClient.InitCipherSpecNone;
begin
InitTLSSecurityParametersNone(FCipherEncryptSpec);
InitTLSSecurityParametersNone(FCipherDecryptSpec);
TLSCipherInitNone(FCipherEncryptState, tlscoEncrypt);
TLSCipherInitNone(FCipherDecryptState, tlscoDecrypt);
end;
procedure TTLSServerClient.DoStart;
begin
SetConnectionState(tlscoStart);
InitCipherSpecNone;
SetConnectionState(tlscoHandshaking);
SetClientState(tlsscHandshakeAwaitingClientHello);
FServer.AllocateSessionID(FSessionID);
end;
procedure TTLSServerClient.Start;
begin
Assert(FConnectionState = tlscoInit);
Assert(FClientState = tlsscInit);
DoStart;
end;
{ }
{ TLS Server }
{ }
constructor TTLSServer.Create(const ATransportLayerSendProc: TTLSServerTransportLayerSendProc);
begin
inherited Create;
Init;
if not Assigned(ATransportLayerSendProc) then
raise ETLSError.Create(TLSError_InvalidParameter);
FTransportLayerSendProc := ATransportLayerSendProc;
end;
procedure TTLSServer.Init;
begin
FServerOptions := DefaultTLSServerOptions;
FVersionOptions := DefaultTLSServerVersionOptions;
FKeyExchangeOptions := DefaultTLSServerKeyExchangeOptions;
FCipherOptions := DefaultTLSServerCipherOptions;
FHashOptions := DefaultTLSServerHashOptions;
FState := tlssInit;
FLock := TCriticalSection.Create;
RSAPrivateKeyInit(FRSAPrivateKey);
FDHKeySize := 1024;
end;
destructor TTLSServer.Destroy;
var I : Integer;
begin
for I := Length(FClients) - 1 downto 0 do
FreeAndNil(FClients[I]);
RSAPrivateKeyFinalise(FRSAPrivateKey);
FreeAndNil(FLock);
inherited Destroy;
end;
procedure TTLSServer.Lock;
begin
Assert(Assigned(FLock));
FLock.Acquire;
end;
procedure TTLSServer.Unlock;
begin
FLock.Release;
end;
procedure TTLSServer.Log(const LogType: TTLSLogType; const LogMsg: String; const LogLevel: Integer);
begin
if Assigned(FOnLog) then
FOnLog(self, LogType, LogMsg, LogLevel);
end;
procedure TTLSServer.Log(const LogType: TTLSLogType; const LogMsg: String; const Args: array of const; const LogLevel: Integer);
begin
Log(LogType, Format(LogMsg, Args), LogLevel);
end;
procedure TTLSServer.CheckNotActive;
begin
if FState = tlssActive then
raise ETLSError.Create(TLSError_InvalidState, 'Operation not allowed while active');
end;
procedure TTLSServer.CheckActive;
begin
if FState <> tlssActive then
raise ETLSError.Create(TLSError_InvalidState, 'Operation not allowed while not active');
end;
procedure TTLSServer.SetServerOptions(const AServerOptions: TTLSServerOptions);
begin
if AServerOptions = FServerOptions then
exit;
CheckNotActive;
FServerOptions := AServerOptions;
end;
procedure TTLSServer.SetVersionOptions(const AVersionOptions: TTLSVersionOptions);
begin
if AVersionOptions = FVersionOptions then
exit;
CheckNotActive;
if AVersionOptions = [] then
raise ETLSError.Create(TLSError_InvalidParameter, 'Invalid version options');
FVersionOptions := AVersionOptions;
end;
procedure TTLSServer.SetCipherOptions(const ACipherOptions: TTLSCipherOptions);
begin
if ACipherOptions = FCipherOptions then
exit;
CheckNotActive;
if ACipherOptions = [] then
raise ETLSError.Create(TLSError_InvalidParameter, 'Invalid cipher options');
FCipherOptions := ACipherOptions;
end;
procedure TTLSServer.SetKeyExchangeOptions(const AKeyExchangeOptions: TTLSKeyExchangeOptions);
begin
if AKeyExchangeOptions = FKeyExchangeOptions then
exit;
CheckNotActive;
if AKeyExchangeOptions = [] then
raise ETLSError.Create(TLSError_InvalidParameter, 'Invalid key exchange options');
FKeyExchangeOptions := AKeyExchangeOptions;
end;
procedure TTLSServer.SetHashOptions(const AHashOptions: TTLSHashOptions);
begin
if AHashOptions = FHashOptions then
exit;
CheckNotActive;
if AHashOptions = [] then
raise ETLSError.Create(TLSError_InvalidParameter, 'Invalid hash options');
FHashOptions := AHashOptions;
end;
procedure TTLSServer.SetCertificateList(const List: TTLSCertificateList);
begin
CheckNotActive;
FCertificateList := Copy(List);
end;
procedure TTLSServer.SetPrivateKeyRSA(const APrivateKeyRSA: RawByteString);
begin
if APrivateKeyRSA = FPrivateKeyRSA then
exit;
CheckNotActive;
FPrivateKeyRSA := APrivateKeyRSA;
end;
function TTLSServer.GetPrivateKeyRSAPEM: RawByteString;
begin
Result := MIMEBase64Encode(PrivateKeyRSA);
end;
procedure TTLSServer.SetPrivateKeyRSAPEM(const APrivateKeyRSAPEM: RawByteString);
begin
SetPrivateKeyRSA(MIMEBase64Decode(APrivateKeyRSAPEM));
end;
procedure TTLSServer.SetPEMFileName(const APEMFileName: String);
begin
if APEMFileName = FPEMFileName then
exit;
CheckNotActive;
FPEMFileName := APEMFileName;
end;
procedure TTLSServer.SetPEMText(const APEMText: RawByteString);
begin
if APEMText = FPEMText then
exit;
CheckNotActive;
FPEMText := APEMText;
end;
procedure TTLSServer.SetDHKeySize(const ADHKeySize: Integer);
begin
if ADHKeySize = FDHKeySize then
exit;
CheckNotActive;
FDHKeySize := ADHKeySize;
end;
procedure TTLSServer.ClientLog(const Client: TTLSServerClient; const LogType: TTLSLogType; const LogMsg: String; const LogLevel: Integer);
begin
Log(LogType, 'C[%d]:%s', [Client.FClientId, LogMsg], LogLevel + 1);
end;
procedure TTLSServer.ClientStateChange(const Client: TTLSServerClient);
begin
if Assigned(FOnClientStateChange) then
FOnClientStateChange(self, Client);
end;
procedure TTLSServer.ClientAlert(const Client: TTLSServerClient; const Level: TTLSAlertLevel; const Description: TTLSAlertDescription);
begin
if Assigned(FOnClientAlert) then
FOnClientAlert(self, Client, Level, Description);
end;
procedure TTLSServer.ClientHandshakeFinished(const Client: TTLSServerClient);
begin
if Assigned(FOnClientHandshakeFinished) then
FOnClientHandshakeFinished(self, Client);
end;
function TTLSServer.CreateClient(const UserObj: TObject): TTLSServerClient;
begin
Result := TTLSServerClient.Create(self, UserObj);
end;
function TTLSServer.GetClientCount: Integer;
begin
Result := Length(FClients);
end;
function TTLSServer.GetClient(const Idx: Integer): TTLSServerClient;
begin
Assert(Idx >= 0);
Assert(Idx < Length(FClients));
Result := FClients[Idx];
end;
function TTLSServer.GetClientIndex(const Client: TTLSServerClient): Integer;
var I : Integer;
begin
for I := 0 to Length(FClients) - 1 do
if FClients[I] = Client then
begin
Result := I;
exit;
end;
Result := -1;
end;
function TTLSServer.AddClient(const UserObj: TObject): TTLSServerClient;
var L : Integer;
C : TTLSServerClient;
begin
CheckActive;
C := CreateClient(UserObj);
Lock;
try
Inc(FClientNr);
C.FClientId := FClientNr;
L := Length(FClients);
SetLength(FClients, L + 1);
FClients[L] := C;
finally
Unlock;
end;
Result := C;
end;
procedure TTLSServer.RemoveClient(const Client: TTLSServerClient);
var I, J, L : Integer;
begin
Lock;
try
I := GetClientIndex(Client);
if I < 0 then
raise ETLSError.Create(TLSError_InvalidParameter);
L := Length(FClients);
for J := I to L - 2 do
FClients[J] := FClients[J + 1];
SetLength(FClients, L - 1);
finally
Unlock;
end;
Client.Free;
end;
procedure TTLSServer.ClientTransportLayerSend(const Sender: TTLSServerClient; const Buffer; const Size: Integer);
begin
Assert(Assigned(FTransportLayerSendProc));
Assert(Size > 0);
FTransportLayerSendProc(self, Sender, Buffer, Size);
end;
procedure TTLSServer.ProcessTransportLayerReceivedData(const AClient: TTLSServerClient; const Buffer; const Size: Integer);
begin
if not Assigned(AClient) then
raise ETLSError.Create(TLSError_InvalidParameter);
AClient.ProcessTransportLayerReceivedData(Buffer, Size);
end;
procedure TTLSServer.InitFromPEM;
var P : TPEMFile;
L, I : Integer;
begin
if (FPEMFileName = '') and (FPEMText = '') then
exit;
P := TPEMFile.Create;
try
if FPEMFileName <> '' then
P.LoadFromFile(FPEMFileName)
else
P.LoadFromText(FPEMText);
FPrivateKeyRSA := P.RSAPrivateKey;
L := P.CertificateCount;
SetLength(FCertificateList, L);
for I := 0 to L - 1 do
FCertificateList[I] := P.Certificate[I];
finally
P.Free;
end;
end;
procedure TTLSServer.InitPrivateKey;
var L1, L2 : Integer;
begin
if FPrivateKeyRSA = '' then
raise ETLSError.Create(TLSError_InvalidCertificate, 'No private key');
ParseX509RSAPrivateKeyStr(FPrivateKeyRSA, FX509RSAPrivateKey);
L1 := NormaliseX509IntKeyBuf(FX509RSAPrivateKey.Modulus);
L2 := NormaliseX509IntKeyBuf(FX509RSAPrivateKey.PrivateExponent);
if L2 > L1 then
L1 := L2;
RSAPrivateKeyAssignBufStr(FRSAPrivateKey, L1 * 8,
FX509RSAPrivateKey.Modulus,
FX509RSAPrivateKey.PrivateExponent);
end;
procedure TTLSServer.AllocateSessionID(var SessionID: RawByteString);
begin
SessionID := SecureRandomStrA(TLSSessionIDMaxLen);
end;
procedure TTLSServer.DoStart;
begin
Assert(FState <> tlssActive);
{$IFDEF TLS_DEBUG}
Log(tlsltDebug, 'Start');
{$ENDIF}
FClientNr := 0;
InitFromPEM;
InitPrivateKey;
FState := tlssActive;
end;
procedure TTLSServer.DoStop;
begin
Assert(FState = tlssActive);
{$IFDEF TLS_DEBUG}
Log(tlsltDebug, 'Stop');
{$ENDIF}
FState := tlssStopped;
end;
procedure TTLSServer.Start;
begin
if FState = tlssActive then
exit;
DoStart;
end;
procedure TTLSServer.Stop;
begin
if FState <> tlssActive then
exit;
DoStop;
end;
end.