{$IFNDEF FPC_DOTTEDUNITS}
unit sslbase;
{$ENDIF FPC_DOTTEDUNITS}
{
    This file is part of the Free Pascal run time library.
    Copyright (c) 1999-2022 by Michael van Canney and other members of the
    Free Pascal development team

    SSL Base unit

    See the file COPYING.FPC, included in this distribution,
    for details about the copyright.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

 **********************************************************************}

{$mode objfpc}{$H+}

interface

{$IFDEF FPC_DOTTEDUNITS}
uses
  System.Classes, System.SysUtils;
{$ELSE FPC_DOTTEDUNITS}
uses
  Classes, SysUtils;
{$ENDIF FPC_DOTTEDUNITS}

Type
  TSSLType = (stAny,stSSLv2,stSSLv3,stTLSv1,stTLSv1_1,stTLSv1_2);

  { TSSLData }

  TSSLData = Class(TPersistent)
  private
    FFileName: String;
    FValue: TBytes;
  Public
    Function Empty : Boolean;
    Procedure Assign(Source : TPersistent);override;
    Property FileName : String Read FFileName Write FFileName;
    Property Value: TBytes Read FValue Write FValue;
  end;

Const
  SSLDataCount = 4; // 0 based.
  StrDataCount = 2; // 0 based.

Type
  { TSSLSocketHandler }

  { TCertificateData }

  TCertificateData = Class(TPersistent)
  Private
    FStrData : Array[0..StrDataCount] of string;
    FCertData : Array[0..SSLDataCount] of TSSLData;
    FTrustedCertsDir: String;
    function GetSSLData(AIndex: Integer): TSSLData;
    procedure SetSSLData(AIndex: Integer; AValue: TSSLData);
    function GetString(AIndex: Integer): String;
    procedure SetString(AIndex: Integer; const AValue: String);
  Public
    constructor Create;
    Destructor Destroy; override;
    Procedure Assign(Source : TPersistent); override;
    Function NeedCertificateData : Boolean;
  Published
    property KeyPassword: string Index 0 read GetString write SetString;
    property CipherList: string Index 1 read GetString write SetString;
    Property HostName : String Index 2 read GetString write SetString;
    property Certificate : TSSLData Index 0 Read GetSSLData Write SetSSLData;
    property TrustedCertificate : TSSLData Index 1 Read GetSSLData Write SetSSLData;
    property PrivateKey : TSSLData Index 2 Read GetSSLData Write SetSSLData;
    property PFX: TSSLData Index 3 Read GetSSLData Write SetSSLData;
    property CertCA: TSSLData Index 4 Read GetSSLData Write SetSSLData;
    // OpenSSL allows both a PEM file or a Dir. We separate out the dir.
    Property TrustedCertsDir : String Read FTrustedCertsDir Write FTrustedCertsDir;
  end;

    { TX509Certificate }
  TCertAndKey = Record
    Certificate : TBytes;
    PrivateKey : TBytes;
  end;

  TX509Certificate = Class (TObject)
  private
    FCommonName: string;
    FCountry: String;
    FHostName: string;
    FKeySize: Integer;
    FOrganization: String;
    FSerial: Integer;
    FValidFrom: TDateTime;
    FValidTo: TDateTime;
    FVersion: Integer;
    function GetKeySize: Integer;
    function GetValidFrom: TDateTime;
    function GetValidTo: TDateTime;
    function GetVersion: Integer;
  Protected
    Function GetRealSerial : Integer;
  Public
    Function CreateCertificateAndKey : TCertAndKey; virtual; abstract;
    Procedure CreateCertificateAndKey(Var aCertificate,aKey : TBytes);
    Property Country : String Read FCountry Write FCountry;
    Property HostName : string Read FHostName Write FHostName;
    Property CommonName : string Read FCommonName Write FCommonName;
    Property Organization : String Read FOrganization Write FOrganization;
    Property KeySize : Integer Read GetKeySize Write FKeySize;
    // Valid from. Default today -1;
    Property ValidFrom : TDateTime Read GetValidFrom Write FValidFrom;
    // Valid To. Default today + 31;
    Property ValidTo : TDateTime Read GetValidTo Write FValidTo;
    // Version Default 1.
    Property Version : Integer Read GetVersion Write FVersion;
    // Serial. If zero, then a serial is generated.
    Property Serial : Integer Read FSerial Write FSerial;

  end;

implementation

{ TSSLData }

Function TSSLData.Empty: Boolean;
begin
  Result:=(Length(Value)=0) and (FileName='');
end;

Procedure TSSLData.Assign(Source: TPersistent);

begin
  if Source is TSSLData then
   With TSSLData(Source) do
    begin
    Self.FValue:=FValue;
    Self.FFileName:=FFileName;
    end
  else
    inherited Assign(Source);
end;

{ TCertificateData }

function TCertificateData.GetSSLData(AIndex: Integer): TSSLData;
begin
  Result:=FCertData[AIndex];
end;

procedure TCertificateData.SetSSLData(AIndex: Integer; AValue: TSSLData);
begin
  FCertData[AIndex].Assign(AValue);
end;

function TCertificateData.GetString(AIndex: Integer): String;
begin
  Result:=FStrData[AIndex];
  if (AIndex=2) and (result='') then
    Result:='localhost';
end;

procedure TCertificateData.SetString(AIndex: Integer; const AValue: String);
begin
  FStrData[AIndex]:=aValue;
end;

constructor TCertificateData.Create;

Var
  I : Integer;

begin
  CipherList:='DEFAULT';
  HostName:='localhost';
  For I:=0 to SSLDataCount do
    FCertData[i]:=TSSLData.Create;
end;

destructor TCertificateData.Destroy;

Var
  I : Integer;

begin
  For I:=0 to SSLDataCount do
    FreeAndNil(FCertData[i]);
  inherited Destroy;
end;

procedure TCertificateData.Assign(Source: TPersistent);

Var
  CD : TCertificateData;
  I : Integer;

begin
  if Source is TCertificateData then
    begin
    CD:=Source as TCertificateData;
    For I:=0 to StrDataCount do
      FStrData[i]:=CD.FStrData[i];
    For I:=0 to SSLDataCount do
      FCertData[i].Assign(CD.FCertData[i])
    end
  else
    inherited Assign(Source);
end;

function TCertificateData.NeedCertificateData: Boolean;
begin
  Result:=Certificate.Empty and PFX.Empty;
end;

function TX509Certificate.GetKeySize: Integer;
begin
  Result:=FKeySize;
  if Result=0 then
    Result:=2048;
end;

function TX509Certificate.GetValidFrom: TDateTime;
begin
  Result:=FValidFrom;
  If Result=0 then
    Result:=Date-1;
end;

function TX509Certificate.GetValidTo: TDateTime;
begin
  Result:=FValidTo;
  If Result=0 then
    Result:=Date+31;
end;


function TX509Certificate.GetVersion: Integer;
begin
  Result:=FVersion;
  if FVersion=0 then
    FVersion:=1;
end;

function TX509Certificate.GetRealSerial: Integer;
begin
  Result:=FSerial;
  if Result=0 then
    Result:=10; // MinutesBetween(Now,EncodeDate(2019,1,1));
end;

procedure TX509Certificate.CreateCertificateAndKey(var aCertificate, aKey: TBytes);

Var
  CK : TCertAndKey;

begin
  CK:=CreateCertificateAndKey;
  aCertificate:=CK.Certificate;
  aKey:=CK.PrivateKey;
end;

end.

