336 lines
12 KiB
ObjectPascal
336 lines
12 KiB
ObjectPascal
{******************************************************************************}
|
|
{ }
|
|
{ Library: Fundamentals TLS }
|
|
{ File name: cTLSCipher.pas }
|
|
{ File version: 5.04 }
|
|
{ Description: TLS cipher }
|
|
{ }
|
|
{ Copyright: Copyright (c) 2008-2018, 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 version. }
|
|
{ 2010/11/30 0.02 Revision. }
|
|
{ 2010/12/16 0.03 AES support. }
|
|
{ 2018/07/17 5.04 Revised for Fundamentals 5. }
|
|
{ }
|
|
{******************************************************************************}
|
|
|
|
{$INCLUDE flcTLS.inc}
|
|
|
|
unit flcTLSCipher;
|
|
|
|
interface
|
|
|
|
uses
|
|
{ Cipher }
|
|
|
|
flcCipher,
|
|
|
|
{ TLS }
|
|
|
|
flcTLSCipherSuite;
|
|
|
|
|
|
|
|
{ }
|
|
{ Cipher info }
|
|
{ }
|
|
type
|
|
TTLSCipherSuiteCipherCipherInfo = record
|
|
CipherType : TCipherType;
|
|
CipherMode : TCipherMode;
|
|
Padding : TCipherPadding;
|
|
end;
|
|
PTLSCipherSuiteCipherCipherInfo = ^TTLSCipherSuiteCipherCipherInfo;
|
|
|
|
const
|
|
TLSCipherSuiteCipherCipherInfo : array[TTLSCipherSuiteCipher] of TTLSCipherSuiteCipherCipherInfo = (
|
|
( // None
|
|
CipherType : ctNone;
|
|
CipherMode : cmECB;
|
|
Padding : cpNone),
|
|
( // NULL
|
|
CipherType : ctNone;
|
|
CipherMode : cmECB;
|
|
Padding : cpNone),
|
|
( // RC2_CBC_40
|
|
CipherType : ctRC2;
|
|
CipherMode : cmCBC;
|
|
Padding : cpNone),
|
|
( // RC4_40
|
|
CipherType : ctRC4;
|
|
CipherMode : cmECB;
|
|
Padding : cpNone),
|
|
( // RC4_56
|
|
CipherType : ctRC4;
|
|
CipherMode : cmECB;
|
|
Padding : cpNone),
|
|
( // RC4_128
|
|
CipherType : ctRC4;
|
|
CipherMode : cmECB;
|
|
Padding : cpNone),
|
|
( // IDEA_CBC
|
|
CipherType : ctNone;
|
|
CipherMode : cmCBC;
|
|
Padding : cpNone),
|
|
( // DES40_CBC
|
|
CipherType : ctDES;
|
|
CipherMode : cmCBC;
|
|
Padding : cpNone),
|
|
( // DES_CBC
|
|
CipherType : ctDES;
|
|
CipherMode : cmCBC;
|
|
Padding : cpNone),
|
|
( // 3DES_EDE_CBC
|
|
CipherType : ctTripleDES3EDE; //// TripleDESEDE or TripleDES3EDE ?
|
|
CipherMode : cmCBC;
|
|
Padding : cpNone),
|
|
( // AES_128_CBC
|
|
CipherType : ctAES;
|
|
CipherMode : cmCBC;
|
|
Padding : cpNone),
|
|
( // AES_256_CBC
|
|
CipherType : ctAES;
|
|
CipherMode : cmCBC;
|
|
Padding : cpNone)
|
|
);
|
|
|
|
|
|
|
|
{ }
|
|
{ Cipher state }
|
|
{ }
|
|
type
|
|
TTLSCipherOperation = (
|
|
tlscoNone,
|
|
tlscoEncrypt,
|
|
tlscoDecrypt);
|
|
|
|
TTLSCipherState = record
|
|
Operation : TTLSCipherOperation;
|
|
TLSCipher : TTLSCipherSuiteCipher;
|
|
TLSCipherInfo : TTLSCipherSuiteCipherInfo;
|
|
CipherInfo : TTLSCipherSuiteCipherCipherInfo;
|
|
CipherState : TCipherState;
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ Cipher }
|
|
{ }
|
|
procedure TLSCipherInitNone(
|
|
var CipherState: TTLSCipherState;
|
|
const Operation: TTLSCipherOperation);
|
|
procedure TLSCipherInitNULL(
|
|
var CipherState: TTLSCipherState;
|
|
const Operation: TTLSCipherOperation);
|
|
procedure TLSCipherInit(
|
|
var CipherState: TTLSCipherState;
|
|
const Operation: TTLSCipherOperation;
|
|
const TLSCipher: TTLSCipherSuiteCipher;
|
|
const KeyBuf;
|
|
const KeySize: Integer);
|
|
|
|
procedure TLSCipherBuf(
|
|
var CipherState: TTLSCipherState;
|
|
const MessageBuf;
|
|
const MessageSize: Integer;
|
|
var CipherBuf;
|
|
const CipherBufSize: Integer;
|
|
out CipherSize: Integer;
|
|
const IVBufPtr: Pointer;
|
|
const IVBufSize: Integer);
|
|
|
|
procedure TLSCipherFinalise(var CipherState: TTLSCipherState);
|
|
|
|
|
|
|
|
{ }
|
|
{ Test cases }
|
|
{ }
|
|
{$IFDEF TLS_TEST}
|
|
procedure Test;
|
|
{$ENDIF}
|
|
|
|
|
|
|
|
implementation
|
|
|
|
uses
|
|
{ Utils }
|
|
|
|
flcStdTypes,
|
|
|
|
{ TLS }
|
|
|
|
flcTLSErrors;
|
|
|
|
|
|
|
|
{ }
|
|
{ Cipher }
|
|
{ }
|
|
procedure TLSCipherInitNone(
|
|
var CipherState: TTLSCipherState;
|
|
const Operation: TTLSCipherOperation);
|
|
begin
|
|
FillChar(CipherState, SizeOf(CipherState), 0);
|
|
CipherState.Operation := Operation;
|
|
CipherState.TLSCipher := tlscscNone;
|
|
CipherState.TLSCipherInfo := TLSCipherSuiteCipherInfo[tlscscNone];
|
|
CipherState.CipherInfo := TLSCipherSuiteCipherCipherInfo[tlscscNone];
|
|
end;
|
|
|
|
procedure TLSCipherInitNULL(
|
|
var CipherState: TTLSCipherState;
|
|
const Operation: TTLSCipherOperation);
|
|
begin
|
|
FillChar(CipherState, SizeOf(CipherState), 0);
|
|
CipherState.Operation := Operation;
|
|
CipherState.TLSCipher := tlscscNULL;
|
|
CipherState.TLSCipherInfo := TLSCipherSuiteCipherInfo[tlscscNULL];
|
|
CipherState.CipherInfo := TLSCipherSuiteCipherCipherInfo[tlscscNULL];
|
|
end;
|
|
|
|
procedure TLSCipherInit(
|
|
var CipherState: TTLSCipherState;
|
|
const Operation: TTLSCipherOperation;
|
|
const TLSCipher: TTLSCipherSuiteCipher;
|
|
const KeyBuf;
|
|
const KeySize: Integer);
|
|
begin
|
|
FillChar(CipherState, SizeOf(CipherState), 0);
|
|
CipherState.Operation := Operation;
|
|
CipherState.TLSCipher := TLSCipher;
|
|
CipherState.TLSCipherInfo := TLSCipherSuiteCipherInfo[TLSCipher];
|
|
CipherState.CipherInfo := TLSCipherSuiteCipherCipherInfo[TLSCipher];
|
|
case Operation of
|
|
tlscoEncrypt :
|
|
EncryptInit(
|
|
CipherState.CipherState,
|
|
CipherState.CipherInfo.CipherType,
|
|
CipherState.CipherInfo.CipherMode,
|
|
CipherState.CipherInfo.Padding,
|
|
CipherState.TLSCipherInfo.KeyBits, @KeyBuf, KeySize);
|
|
tlscoDecrypt :
|
|
DecryptInit(
|
|
CipherState.CipherState,
|
|
CipherState.CipherInfo.CipherType,
|
|
CipherState.CipherInfo.CipherMode,
|
|
CipherState.CipherInfo.Padding,
|
|
CipherState.TLSCipherInfo.KeyBits, @KeyBuf, KeySize);
|
|
end;
|
|
end;
|
|
|
|
procedure TLSCipherBuf(
|
|
var CipherState: TTLSCipherState;
|
|
const MessageBuf;
|
|
const MessageSize: Integer;
|
|
var CipherBuf;
|
|
const CipherBufSize: Integer;
|
|
out CipherSize: Integer;
|
|
const IVBufPtr: Pointer;
|
|
const IVBufSize: Integer);
|
|
begin
|
|
if CipherState.Operation = tlscoNone then
|
|
raise ETLSError.Create(TLSError_InvalidParameter); // cipher not initialised
|
|
if MessageSize < 0 then
|
|
raise ETLSError.Create(TLSError_InvalidBuffer);
|
|
if CipherBufSize < MessageSize then
|
|
raise ETLSError.Create(TLSError_InvalidBuffer);
|
|
if CipherState.TLSCipher in [tlscscNone, tlscscNULL] then
|
|
begin
|
|
Move(MessageBuf, CipherBuf, MessageSize);
|
|
CipherSize := MessageSize;
|
|
end
|
|
else
|
|
case CipherState.Operation of
|
|
tlscoEncrypt :
|
|
CipherSize := EncryptBuf(CipherState.CipherState,
|
|
@MessageBuf, MessageSize,
|
|
@CipherBuf, CipherBufSize,
|
|
IVBufPtr, IVBufSize);
|
|
tlscoDecrypt :
|
|
begin
|
|
Move(MessageBuf, CipherBuf, MessageSize);
|
|
CipherSize := DecryptBuf(CipherState.CipherState,
|
|
@CipherBuf, MessageSize,
|
|
IVBufPtr, IVBufSize);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TLSCipherFinalise(var CipherState: TTLSCipherState);
|
|
begin
|
|
case CipherState.Operation of
|
|
tlscoEncrypt : EncryptFinalise(CipherState.CipherState);
|
|
tlscoDecrypt : DecryptFinalise(CipherState.CipherState);
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ Test cases }
|
|
{ }
|
|
{$IFDEF TLS_TEST}
|
|
procedure Test;
|
|
const
|
|
ClientWriteKey = RawByteString(#$10#$D0#$D6#$C2#$D9#$B7#$62#$CB#$2C#$74#$BF#$5F#$85#$3C#$6F#$E7);
|
|
var
|
|
S, T : RawByteString;
|
|
C : TTLSCipherState;
|
|
B : RawByteString;
|
|
L : Integer;
|
|
begin
|
|
// //
|
|
// Example from http://download.oracle.com/javase/1.5.0/docs/guide/security/jsse/ReadDebug.html //
|
|
|
|
// //
|
|
|
|
S := ClientWriteKey;
|
|
TLSCipherInit(C, tlscoEncrypt, tlscscRC4_128, S[1], Length(S));
|
|
T := RawByteString(
|
|
#$14#$00#$00#$0C#$F2#$62#$42#$AA#$7C#$7C#$CC#$E7#$49#$0F#$ED#$AC +
|
|
#$FA#$06#$3C#$9F#$8C#$41#$1D#$ED#$2B#$06#$D0#$5A#$ED#$31#$F2#$80);
|
|
SetLength(B, 1024);
|
|
L := 1024;
|
|
TLSCipherBuf(C, T[1], Length(T), B[1], L, L, nil, 0);
|
|
SetLength(B, L);
|
|
Assert(B = RawByteString(
|
|
#$15#$8C#$25#$BA#$4E#$73#$F5#$27#$79#$49#$B1 +
|
|
#$E9#$F5#$7E#$C8#$48#$A7#$D3#$A6#$9B#$BD#$6F#$8E#$A5#$8E#$2B#$B7 +
|
|
#$EE#$DC#$BD#$F4#$D7));
|
|
end;
|
|
{$ENDIF}
|
|
|
|
|
|
|
|
end.
|
|
|