<commit_before>unit UCrypto;

{ Copyright (c) 2016 by <NAME>

  Distributed under the MIT software license, see the accompanying file LICENSE
  or visit http://www.opensource.org/licenses/mit-license.php.

  This unit is a part of the PascalCoin Project, an infinitely scalable
  cryptocurrency. Find us here:
  Web: https://www.pascalcoin.org
  Source: https://github.com/PascalCoin/PascalCoin

  If you like it, consider a donation using Bitcoin:
  16K3HCZRhFUtM8GdWRcfKeaa6KsuyxZaYk

  THIS LICENSE HEADER MUST NOT BE REMOVED.
}

{$IFDEF FPC}
  {$MODE Delphi}
{$ENDIF}

{$I config.inc}

interface

uses
  Classes, SysUtils, UOpenSSL, URandomHash, UBaseTypes, UPCDataTypes;

Type
  ECryptoException = Class(Exception);

  { TECPrivateKey }

  TECPrivateKey = Class
  private
    FPrivateKey: PEC_KEY;
    FEC_OpenSSL_NID : Word;
    procedure SetPrivateKey(const Value: PEC_KEY);
    function GetPublicKey: TECDSA_Public;
    function GetPublicKeyPoint: PEC_POINT;
  public
    Constructor Create;
    Procedure GenerateRandomPrivateKey(EC_OpenSSL_NID : Word);
    Destructor Destroy; override;
    Property PrivateKey : PEC_KEY read FPrivateKey;
    Property PublicKey : TECDSA_Public read GetPublicKey;
    Property PublicKeyPoint : PEC_POINT read GetPublicKeyPoint;
    Function SetPrivateKeyFromHexa(EC_OpenSSL_NID : Word; const hexa : String) : Boolean;
    Property EC_OpenSSL_NID : Word Read FEC_OpenSSL_NID;
    class function IsValidPublicKey(PubKey : TECDSA_Public; var errors : String) : Boolean; overload;
    class function IsValidPublicKey(PubKey : TECDSA_Public) : Boolean; overload;
    Function ExportToRaw : TRawBytes;
    class Function ImportFromRaw(Const raw : TRawBytes) : TECPrivateKey; static;
  End;

  { TCrypto }

  TCrypto = Class
  private
  public
    class function IsHexString(const AHexString: String) : boolean;
    class function ToHexaString(const raw : TRawBytes) : String; // DEPRECATED: Use TRawBytes.ToHexaString instead
    class function HexaToRaw(const HexaString : String) : TRawBytes; overload;
    class function HexaToRaw(const HexaString : String; var raw : TRawBytes) : Boolean; overload;
    class function DoSha256(p : PAnsiChar; plength : Cardinal) : TRawBytes; overload;
    class function DoSha256(const TheMessage : TRawBytes) : TRawBytes; overload;
    class procedure DoSha256(const TheMessage : TRawBytes; out ResultSha256 : TRawBytes);  overload;
    class function DoDoubleSha256(const TheMessage : TRawBytes) : TRawBytes; overload;
    class procedure DoDoubleSha256(p : PAnsiChar; plength : Cardinal; out ResultSha256 : TRawBytes); overload;
    class function DoRandomHash(const TheMessage : TRawBytes) : TRawBytes; overload;
    class procedure DoRandomHash(p : PAnsiChar; plength : Cardinal; out ResultSha256 : TRawBytes); overload;
    class procedure DoRandomHash(AFastHasher : TRandomHashFast; p : PAnsiChar; plength : Cardinal; out ResultSha256 : TRawBytes); overload;
    class function DoRipeMD160_HEXASTRING(const TheMessage : TRawBytes) : TRawBytes; overload;
    class function DoRipeMD160AsRaw(p : PAnsiChar; plength : Cardinal) : TRawBytes; overload;
    class function DoRipeMD160AsRaw(const TheMessage : TRawBytes) : TRawBytes; overload;
    class function PrivateKey2Hexa(Key : PEC_KEY) : String;
    class function ECDSASign(Key : PEC_KEY; const digest : TRawBytes) : TECDSA_SIG;
    class function ECDSAVerify(EC_OpenSSL_NID : Word; PubKey : EC_POINT; const digest : TRawBytes; Signature : TECDSA_SIG) : Boolean; overload;
    class function ECDSAVerify(PubKey : TECDSA_Public; const digest : TRawBytes; Signature : TECDSA_SIG) : Boolean; overload;
    class procedure InitCrypto;
    class function IsHumanReadable(Const ReadableText : TRawBytes) : Boolean;
    class function EncodeSignature(const signature : TECDSA_SIG) : TRawBytes;
    class function DecodeSignature(const rawSignature : TRawBytes; out signature : TECDSA_SIG) : Boolean;
  End;

  TBigNum = Class
  private
    FBN : PBIGNUM;
    procedure SetHexaValue(const Value: String);
    function GetHexaValue: String;
    procedure SetValue(const Value: Int64);
    function GetValue: Int64;
    function GetDecimalValue: String;
    procedure SetDecimalValue(const Value: String);
    function GetRawValue: TRawBytes;
    procedure SetRawValue(const Value: TRawBytes);
  public
    Constructor Create; overload;
    Constructor Create(initialValue : Int64); overload;
    Constructor Create(const hexaValue : String); overload;
    Destructor Destroy; override;
    Function Copy : TBigNum;
    Function Add(BN : TBigNum) : TBigNum; overload;
    Function Add(int : Int64) : TBigNum; overload;
    Function Sub(BN : TBigNum) : TBigNum; overload;
    Function Sub(int : Int64) : TBigNum; overload;
    Function Multiply(BN : TBigNum) : TBigNum; overload;
    Function Multiply(int : Int64) : TBigNum; overload;
    Function LShift(nbits : Integer) : TBigNum;
    Function RShift(nbits : Integer) : TBigNum;
    Function CompareTo(BN : TBigNum) : Integer;
    Function Divide(BN : TBigNum) : TBigNum; overload;
    Function Divide(int : Int64) : TBigNum; overload;
    Procedure Divide(dividend, remainder : TBigNum); overload;
    Function ToInt64(var int : Int64) : TBigNum;
    Function ToDecimal : String;
    Property HexaValue : String read GetHexaValue write SetHexaValue;
    Property RawValue : TRawBytes read GetRawValue write SetRawValue;
    Property DecimalValue : String read GetDecimalValue write SetDecimalValue;
    Property Value : Int64 read GetValue write SetValue;
    Function IsZero : Boolean;
    Class Function HexaToDecimal(hexa : String) : String;
    Class Function TargetToHashRate(EncodedTarget : Cardinal) : TBigNum;
  End;

Const
  CT_TECDSA_Public_Nul : TECDSA_Public = (EC_OpenSSL_NID:0;x:Nil;y:Nil);
  CT_TECDSA_SIG_Nul : TECDSA_SIG = (r:Nil;s:Nil);

implementation

uses
  ULog, UConst, UAccounts;

Var _initialized : Boolean = false;

Procedure _DoInit;
var err : String;
 c : Cardinal;
Begin
  if Not (_initialized) then begin
    _initialized := true;
    If Not InitSSLFunctions then begin
      err := 'Cannot load OpenSSL library '+SSL_C_LIB;
      TLog.NewLog(ltError,'OpenSSL',err);
      Raise Exception.Create(err);
    end;
    If Not Assigned(OpenSSL_version_num) then begin
      err := 'OpenSSL library is not v1.1 version: '+SSL_C_LIB;
      TLog.NewLog(ltError,'OpenSSL',err);
      Raise Exception.Create(err);
    end;
    c := OpenSSL_version_num;
    if (c<$10100000) Or (c>$1010FFFF) then begin
      err := 'OpenSSL library is not v1.1 version ('+IntToHex(c,8)+'): '+SSL_C_LIB;
      TLog.NewLog(ltError,'OpenSSL',err);
      Raise Exception.Create(err);
    end;
  end;
End;

{ TECPrivateKey }

constructor TECPrivateKey.Create;
begin
  FPrivateKey := Nil;
  FEC_OpenSSL_NID := CT_Default_EC_OpenSSL_NID;
end;

destructor TECPrivateKey.Destroy;
begin
  if Assigned(FPrivateKey) then EC_KEY_free(FPrivateKey);
  inherited;
end;

function TECPrivateKey.ExportToRaw: TRawBytes;
Var ms : TStream;
  aux : TRawBytes;
begin
  ms := TMemoryStream.Create;
  Try
    ms.Write(FEC_OpenSSL_NID,sizeof(FEC_OpenSSL_NID));
    SetLength(aux,BN_num_bytes(EC_KEY_get0_private_key(FPrivateKey)));
    BN_bn2bin(EC_KEY_get0_private_key(FPrivateKey),@aux[Low(aux)]);
    TStreamOp.WriteAnsiString(ms,aux);
    SetLength(Result,ms.Size);
    ms.Position := 0;
    ms.Read(Result[Low(Result)],ms.Size);
  Finally
    ms.Free;
  End;
end;

procedure TECPrivateKey.GenerateRandomPrivateKey(EC_OpenSSL_NID : Word);
Var i : Integer;
begin
  if Assigned(FPrivateKey) then EC_KEY_free(FPrivateKey);
  FEC_OpenSSL_NID := EC_OpenSSL_NID;
  FPrivateKey := EC_KEY_new_by_curve_name(EC_OpenSSL_NID);
  i := EC_KEY_generate_key(FPrivateKey);
  if i<>1 then Raise ECryptoException.Create('Error generating new Random Private Key');
end;

function TECPrivateKey.GetPublicKey: TECDSA_Public;
var ps : PAnsiChar;
  BNx,BNy : PBIGNUM;
  ctx : PBN_CTX;
begin
  Result.EC_OpenSSL_NID := FEC_OpenSSL_NID;
  ctx := BN_CTX_new;
  BNx := BN_new;
  BNy := BN_new;
  Try
    EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(FPrivateKey),EC_KEY_get0_public_key(FPrivateKey),BNx,BNy,ctx);
    SetLength(Result.x,BN_num_bytes(BNx));
    BN_bn2bin(BNx,@Result.x[Low(Result.x)]);
    SetLength(Result.y,BN_num_bytes(BNy));
    BN_bn2bin(BNy,@Result.y[Low(Result.y)]);
  Finally
    BN_CTX_free(ctx);
    BN_free(BNx);
    BN_free(BNy);
  End;
end;

function TECPrivateKey.GetPublicKeyPoint: PEC_POINT;
begin
  Result := EC_KEY_get0_public_key(FPrivateKey);
end;

class function TECPrivateKey.ImportFromRaw(const raw: TRawBytes): TECPrivateKey;
Var ms : TStream;
  aux : TRawBytes;
  BNx : PBIGNUM;
  ECID : Word;
  PAC : PAnsiChar;
begin
  Result := Nil;
  ms := TMemoryStream.Create;
  Try
    ms.WriteBuffer(raw[Low(raw)],Length(raw));
    ms.Position := 0;
    if ms.Read(ECID,sizeof(ECID))<>sizeof(ECID) then exit;
    If TStreamOp.ReadAnsiString(ms,aux)<0 then exit;
    BNx := BN_bin2bn(PAnsiChar(aux),Length(aux),nil);
    if assigned(BNx) then begin
      try
        PAC := BN_bn2hex(BNx);
        try
          Result := TECPrivateKey.Create;
          Try
            If Not Result.SetPrivateKeyFromHexa(ECID,PAC) then begin
              FreeAndNil(Result);
            end;
          Except
            On E:Exception do begin
              FreeAndNil(Result);
              // Note: Will not raise Exception, only will log it
              TLog.NewLog(lterror,ClassName,'Error importing private key from '+TCrypto.ToHexaString(raw)+' ECID:'+IntToStr(ECID)+' ('+E.ClassName+'): '+E.Message);
            end;
          end;
        finally
          OpenSSL_free(PAC);
        end;
      finally
        BN_free(BNx);
      end;
    end;
  Finally
    ms.Free;
  End;
end;

class function TECPrivateKey.IsValidPublicKey(PubKey: TECDSA_Public; var errors : String): Boolean;
Var BNx,BNy : PBIGNUM;
  ECG : PEC_GROUP;
  ctx : PBN_CTX;
  pub_key : PEC_POINT;
begin
  Result := False;
  BNx := BN_bin2bn(PAnsiChar(PubKey.x),length(PubKey.x),nil);
  if Not Assigned(BNx) then Exit;
  try
    BNy := BN_bin2bn(PAnsiChar(PubKey.y),length(PubKey.y),nil);
    if Not Assigned(BNy) then Exit;
    try
      ECG := EC_GROUP_new_by_curve_name(PubKey.EC_OpenSSL_NID);
      if Not Assigned(ECG) then Exit;
      try
        pub_key := EC_POINT_new(ECG);
        try
          if Not Assigned(pub_key) then Exit;
          ctx := BN_CTX_new;
          try
            Result := EC_POINT_set_affine_coordinates_GFp(ECG,pub_key,BNx,BNy,ctx)=1;
            if not Result then begin
              errors := Format('Invalid Public key type:%d - Length x:%d y:%d Error:%s',[PubKey.EC_OpenSSL_NID,length(PubKey.x),length(PubKey.y), CaptureLastSSLError]);
            end;
          finally
            BN_CTX_free(ctx);
          end;
        finally
          EC_POINT_free(pub_key);
        end;
      finally
        EC_GROUP_free(ECG);
      end;
    finally
      BN_free(BNy);
    end;
  finally
    BN_free(BNx);
  end;
end;

class function TECPrivateKey.IsValidPublicKey(PubKey: TECDSA_Public): Boolean;
var Ltmp : String;
begin
  Result := IsValidPublicKey(PubKey,Ltmp);
end;

procedure TECPrivateKey.SetPrivateKey(const Value: PEC_KEY);
begin
  if Assigned(FPrivateKey) then EC_KEY_free(FPrivateKey);
  FPrivateKey := Value;
end;

function TECPrivateKey.SetPrivateKeyFromHexa(EC_OpenSSL_NID : Word; const hexa : String) : Boolean;
var bn : PBIGNUM;
  ctx : PBN_CTX;
  pub_key : PEC_POINT;
  tmp_ansistring : RawByteString;
begin
  Result := False;
  bn := BN_new;
  try
    tmp_ansistring := hexa;
    if BN_hex2bn(@bn,PAnsiChar(tmp_ansistring))=0 then Raise ECryptoException.Create('Invalid hexa string to convert to Hexadecimal value');

    if Assigned(FPrivateKey) then EC_KEY_free(FPrivateKey);
    FEC_OpenSSL_NID := EC_OpenSSL_NID;
    FPrivateKey := EC_KEY_new_by_curve_name(EC_OpenSSL_NID);
    If Not Assigned(FPrivateKey) then Exit;
    if EC_KEY_set_private_key(FPrivateKey,bn)<>1 then raise ECryptoException.Create('Invalid num to set as private key');
    //
    ctx := BN_CTX_new;
    pub_key := EC_POINT_new(EC_KEY_get0_group(FPrivateKey));
    try
      if EC_POINT_mul(EC_KEY_get0_group(FPrivateKey),pub_key,bn,nil,nil,ctx)<>1 then raise ECryptoException.Create('Error obtaining public key');
      EC_KEY_set_public_key(FPrivateKey,pub_key);
    finally
      BN_CTX_free(ctx);
      EC_POINT_free(pub_key);
    end;
  finally
    BN_free(bn);
  end;
  Result := True;
end;

{ TCrypto }

{ New at Build 1.0.2
  Note: Delphi is slowly when working with Strings (allowing space)... so to
  increase speed we use a String as a pointer, and only increase speed if
  needed. Also the same with functions "GetMem" and "FreeMem" }
class procedure TCrypto.DoDoubleSha256(p: PAnsiChar; plength: Cardinal; out ResultSha256: TRawBytes);
Var PS : PAnsiChar;
begin
  If length(ResultSha256)<>32 then SetLength(ResultSha256,32);
  PS := @ResultSha256[Low(ResultSha256)];
  SHA256(p,plength,PS);
  SHA256(PS,32,PS);
end;

class function TCrypto.DoDoubleSha256(const TheMessage: TRawBytes): TRawBytes;
begin
  Result := DoSha256(DoSha256(TheMessage));
end;

class function TCrypto.DoRipeMD160_HEXASTRING(const TheMessage: TRawBytes): TRawBytes;
Var PS : PAnsiChar;
  PC : PAnsiChar;
  i : Integer;
  tmp : String;
begin
  GetMem(PS,33);
  RIPEMD160(PAnsiChar(@TheMessage[Low(TheMessage)]),Length(TheMessage),PS);
  PC := PS;
  tmp := '';
  for i := 1 to 20 do begin
    tmp := tmp + IntToHex(PtrInt(PC^),2);
    inc(PC);
  end;
  FreeMem(PS,33);
  Result := TEncoding.ASCII.GetBytes(tmp);
end;

class function TCrypto.DoRipeMD160AsRaw(p: PAnsiChar; plength: Cardinal): TRawBytes;
Var PS : PAnsiChar;
begin
  SetLength(Result,20);
  PS := @Result[Low(Result)];
  RIPEMD160(p,plength,PS);
end;

class function TCrypto.DoRipeMD160AsRaw(const TheMessage: TRawBytes): TRawBytes;
Var PS : PAnsiChar;
begin
  SetLength(Result,20);
  PS := @Result[Low(Result)];
  RIPEMD160(PAnsiChar(@TheMessage[Low(TheMessage)]),Length(TheMessage),PS);
end;

class function TCrypto.DoSha256(p: PAnsiChar; plength: Cardinal): TRawBytes;
Var PS : PAnsiChar;
begin
  SetLength(Result,32);
  PS := @Result[Low(Result)];
  SHA256(p,plength,PS);
end;

class function TCrypto.DoSha256(const TheMessage: TRawBytes): TRawBytes;
Var PS : PAnsiChar;
begin
  SetLength(Result,32);
  PS := @Result[Low(Result)];
  SHA256(@TheMessage[Low(TheMessage)],Length(TheMessage),PS);
end;

{ New at Build 2.1.6
  Note: Delphi is slowly when working with Strings (allowing space)... so to
  increase speed we use a String as a pointer, and only increase speed if
  needed. Also the same with functions "GetMem" and "FreeMem" }
class procedure TCrypto.DoSha256(const TheMessage: TRawBytes; out ResultSha256: TRawBytes);
Var PS : PAnsiChar;
begin
  If length(ResultSha256)<>32 then SetLength(ResultSha256,32);
  PS := @ResultSha256[Low(ResultSha256)];
  SHA256(@TheMessage[Low(TheMessage)],Length(TheMessage),PS);
end;

class function TCrypto.ECDSASign(Key: PEC_KEY; const digest: TRawBytes): TECDSA_SIG;
Var PECS : PECDSA_SIG;
  p : PAnsiChar;
  i : Integer;
begin
  PECS := ECDSA_do_sign(PAnsiChar(@digest[Low(digest)]),Length(digest),Key);
  Try
    if PECS = Nil then raise ECryptoException.Create('Error signing');

    i := BN_num_bytes(PECS^._r);
    SetLength(Result.r,i);
    p := @Result.r[Low(Result.r)];
    i := BN_bn2bin(PECS^._r,p);

    i := BN_num_bytes(PECS^._s);
    SetLength(Result.s,i);
    p := @Result.s[Low(Result.s)];
    i := BN_bn2bin(PECS^._s,p);
  Finally
    ECDSA_SIG_free(PECS);
  End;
end;

class function TCrypto.ECDSAVerify(EC_OpenSSL_NID : Word; PubKey: EC_POINT; const digest: TRawBytes; Signature: TECDSA_SIG): Boolean;
Var PECS : PECDSA_SIG;
  PK : PEC_KEY;
  {$IFDEF OpenSSL10}
  {$ELSE}
  bnr,bns : PBIGNUM;
  {$ENDIF}
begin
  PECS := ECDSA_SIG_new;
  Try
    {$IFDEF OpenSSL10}
    BN_bin2bn(PAnsiChar(Signature.r),length(Signature.r),PECS^._r);
    BN_bin2bn(PAnsiChar(Signature.s),length(Signature.s),PECS^._s);
    {$ELSE}
{    ECDSA_SIG_get0(PECS,@bnr,@bns);
    BN_bin2bn(PAnsiChar(Signature.r),length(Signature.r),bnr);
    BN_bin2bn(PAnsiChar(Signature.s),length(Signature.s),bns);}
    bnr := BN_bin2bn(PAnsiChar(Signature.r),length(Signature.r),nil);
    bns := BN_bin2bn(PAnsiChar(Signature.s),length(Signature.s),nil);
    if ECDSA_SIG_set0(PECS,bnr,bns)<>1 then Raise Exception.Create('Dev error 20161019-1 '+CaptureLastSSLError);
    {$ENDIF}

    PK := EC_KEY_new_by_curve_name(EC_OpenSSL_NID);
    EC_KEY_set_public_key(PK,@PubKey);
    Case ECDSA_do_verify(@digest[Low(digest)],Length(digest),PECS,PK) of
      1 : Result := true;
      0 : Result := false;
    Else
      raise ECryptoException.Create('Error on Verify');
    End;
    EC_KEY_free(PK);
  Finally
    ECDSA_SIG_free(PECS);
  End;
end;

class function TCrypto.ECDSAVerify(PubKey: TECDSA_Public; const digest: TRawBytes; Signature: TECDSA_SIG): Boolean;
Var BNx,BNy : PBIGNUM;
  ECG : PEC_GROUP;
  ctx : PBN_CTX;
  pub_key : PEC_POINT;
begin
  BNx := BN_bin2bn(PAnsiChar(PubKey.x),length(PubKey.x),nil);
  BNy := BN_bin2bn(PAnsiChar(PubKey.y),length(PubKey.y),nil);

  ECG := EC_GROUP_new_by_curve_name(PubKey.EC_OpenSSL_NID);
  pub_key := EC_POINT_new(ECG);
  ctx := BN_CTX_new;
  if EC_POINT_set_affine_coordinates_GFp(ECG,pub_key,BNx,BNy,ctx)=1 then begin
    Result := ECDSAVerify(PubKey.EC_OpenSSL_NID, pub_key^,digest,signature);
  end else begin
    Result := false;
  end;
  {$IFDEF HIGHLOG}
  TLog.NewLog(ltdebug,ClassName,Format('ECDSAVerify %s x:%s y:%s Digest:%s Signature r:%s s:%s',
    [TAccountComp.GetECInfoTxt(PubKey.EC_OpenSSL_NID),ToHexaString(PubKey.x),ToHexaString(PubKey.y),
      ToHexaString(digest),ToHexaString(Signature.r),ToHexaString(Signature.s)]));
  {$ENDIF}
  BN_CTX_free(ctx);
  EC_POINT_free(pub_key);
  EC_GROUP_free(ECG);
  BN_free(BNx);
  BN_free(BNy);
end;

class function TCrypto.HexaToRaw(const HexaString: String): TRawBytes;
begin
  HexaToRaw(HexaString,Result);
end;

class function TCrypto.HexaToRaw(const HexaString: String; var raw: TRawBytes): Boolean;
Var i : Integer;
  LHexaRaw : TRawBytes;
  {$IFDEF FPC}
  P : PAnsiChar;
  {$ENDIF}
begin
  LHexaRaw.FromString(LowerCase(HexaString));
  if (Length(LHexaRaw)=0) then begin
    SetLength(raw,0);
    Exit(True);
  end;
  if ((Length(LHexaRaw) MOD 2)<>0) then Exit(False); // odd string
  SetLength(raw,Length(LHexaRaw) DIV 2);
  {$IFDEF FPC}
  P := @raw[0];
  i := HexToBin(PAnsiChar(LHexaRaw.ToString),P,Length(raw));
  {$ELSE}
  i := HexToBin(LHexaRaw,0,raw,0,Length(raw));
  {$ENDIF}
  Result := (i = (Length(raw)));
end;

class procedure TCrypto.InitCrypto;
begin
  _DoInit;
end;

class function TCrypto.IsHumanReadable(const ReadableText: TRawBytes): Boolean;
  // Will return TRUE if all bytes are between 32..126 (ASCII printable bytes)
Var i : Integer;
Begin
  Result := true;
  for i := Low(ReadableText) to High(ReadableText) do begin
    if (ord(ReadableText[i])<32) Or (ord(ReadableText[i])>=127) then begin
      Result := false;
      Exit;
    end;
  end;
end;

class function TCrypto.EncodeSignature(const signature: TECDSA_SIG): TRawBytes;
Var ms : TStream;
begin
  ms := TMemoryStream.Create;
  Try
    TStreamOp.WriteAnsiString(ms,signature.r);
    TStreamOp.WriteAnsiString(ms,signature.s);
    Result := TStreamOp.SaveStreamToRaw(ms);
  finally
    ms.Free;
  end;
end;

class function TCrypto.DecodeSignature(const rawSignature : TRawBytes; out signature : TECDSA_SIG) : Boolean;
var ms : TStream;
begin
  signature := CT_TECDSA_SIG_Nul;
  Result := False;
  ms := TMemoryStream.Create;
  Try
    TStreamOp.LoadStreamFromRaw(ms,rawSignature);
    ms.Position:=0;
    if TStreamOp.ReadAnsiString(ms,signature.r)<0 then Exit;
    if TStreamOp.ReadAnsiString(ms,signature.s)<0 then Exit;
    if ms.Position<ms.Size then Exit; // Invalid position
    Result := True;
  finally
    ms.Free;
  end;
end;

class function TCrypto.PrivateKey2Hexa(Key: PEC_KEY): String;
Var p : PAnsiChar;
begin
  p := BN_bn2hex(EC_KEY_get0_private_key(Key));
  {$IFDEF NO_ANSISTRING}
  Result := p^; // TODO: Not tested when AnsiString not available!
  {$ELSE}
  Result := StrPas(p);
  {$ENDIF}
  OPENSSL_free(p);
end;

class function TCrypto.ToHexaString(const raw: TRawBytes): String;
begin
  Result := raw.ToHexaString;
end;

class function TCrypto.IsHexString(const AHexString: String) : boolean;
var
  i : Integer;
begin
  Result := true;
  for i := Low(AHexString) to High(AHexString) do
    if (NOT (AHexString[i] in ['0'..'9'])) AND
       (NOT (AHexString[i] in ['a'..'f'])) AND
       (NOT (AHexString[i] in ['A'..'F'])) then begin
       Result := false;
       exit;
    end;
end;

{ New at Build 4.0.0 }

class function TCrypto.DoRandomHash(const TheMessage: TRawBytes): TRawBytes;
begin
  Result := TRandomHashFast.Compute(TheMessage);
end;

class procedure TCrypto.DoRandomHash(p : PAnsiChar; plength : Cardinal; out ResultSha256 : TRawBytes);
var
  LInput : TBytes;
  LResult : TBytes;
begin
  if Length(ResultSha256) <> 32 then SetLength(ResultSha256, 32);
  SetLength(LInput, plength);
  Move(p^, LInput[0], plength);
  LResult := TRandomHashFast.Compute(LInput);
  Move(LResult[0], ResultSha256[Low(ResultSha256)], 32);
end;

class procedure TCrypto.DoRandomHash(AFastHasher : TRandomHashFast; p : PAnsiChar; plength : Cardinal; out ResultSha256 : TRawBytes);
var
  LInput : TBytes;
  LResult : TBytes;
begin
  if Length(ResultSha256) <> 32 then SetLength(ResultSha256, 32);
  SetLength(LInput, plength);
  Move(p^, LInput[0], plength);
  LResult := AFastHasher.Hash(LInput);
  Move(LResult[0], ResultSha256[Low(ResultSha256)], 32);
end;

{ TBigNum }

function TBigNum.Add(BN: TBigNum): TBigNum;
begin
  BN_add(FBN,BN.FBN,FBN);
  Result := Self;
end;

function TBigNum.Add(int: Int64): TBigNum;
Var bn : TBigNum;
begin
  bn := TBigNum.Create(int);
  Result := Add(bn);
  bn.Free;
end;

function TBigNum.CompareTo(BN: TBigNum): Integer;
begin
  Result := BN_cmp(FBN,BN.FBN);
end;

function TBigNum.Copy: TBigNum;
begin
  Result := TBigNum.Create(0);
  BN_copy(Result.FBN,FBN);
end;

constructor TBigNum.Create;
begin
  Create(0);
end;

constructor TBigNum.Create(const hexaValue: String);
begin
  Create(0);
  SetHexaValue(hexaValue);
end;

constructor TBigNum.Create(initialValue : Int64);
begin
  FBN := BN_new;
  SetValue(initialValue);
end;

destructor TBigNum.Destroy;
begin
  BN_free(FBN);
  inherited;
end;

procedure TBigNum.Divide(dividend, remainder: TBigNum);
Var ctx : PBN_CTX;
begin
  ctx := BN_CTX_new;
  BN_div(FBN,remainder.FBN,FBN,dividend.FBN,ctx);
  BN_CTX_free(ctx);
end;

function TBigNum.Divide(int: Int64): TBigNum;
Var bn : TBigNum;
begin
  bn := TBigNum.Create(int);
  Result := Divide(bn);
  bn.Free;
end;

function TBigNum.Divide(BN: TBigNum): TBigNum;
Var _div,_rem : PBIGNUM;
  ctx : PBN_CTX;
begin
  _div := BN_new;
  _rem := BN_new;
  ctx := BN_CTX_new;
  BN_div(FBN,_rem,FBN,BN.FBN,ctx);
  BN_free(_div);
  BN_free(_rem);
  BN_CTX_free(ctx);
  Result := Self;
end;

function TBigNum.GetDecimalValue: String;
var p : PAnsiChar;
begin
  p := BN_bn2dec(FBN);
  {$IFDEF NO_ANSISTRING}
  Result := p^; // TODO: Not tested when AnsiString not available!
  {$ELSE}
  Result := StrPas(p);
  {$ENDIF}
  OpenSSL_free(p);
end;

function TBigNum.GetHexaValue: String;
Var p : PAnsiChar;
begin
  p := BN_bn2hex(FBN);
  {$IFDEF NO_ANSISTRING}
  Result := p^; // TODO: Not tested when AnsiString not available!
  {$ELSE}
  Result := StrPas(p);
  {$ENDIF}
  OPENSSL_free(p);
end;

function TBigNum.GetRawValue: TRawBytes;
Var p : PAnsiChar;
  i : Integer;
begin
  i := BN_num_bytes(FBN);
  SetLength(Result,i);
  p := @Result[Low(Result)];
  i := BN_bn2bin(FBN,p);
end;

function TBigNum.GetValue: Int64;
Var p : PAnsiChar;
  a : RawByteString;
  err : Integer;
begin
  p := BN_bn2dec(FBN);
  {$IFDEF NO_ANSISTRING}
  a := p^; // TODO: Not tested when AnsiString not available!
  {$ELSE}
  a := StrPas(p);
  {$ENDIF}
  OPENSSL_free(p);
  Val(a,Result,err);
end;

class function TBigNum.HexaToDecimal(hexa: String): String;
Var bn : TBigNum;
begin
  bn := TBigNum.Create(hexa);
  result := bn.ToDecimal;
  bn.Free;
end;

function TBigNum.IsZero: Boolean;
Var dv : String;
begin
  dv := DecimalValue;
  Result := dv='0';
end;

function TBigNum.LShift(nbits: Integer): TBigNum;
begin
  if BN_lshift(FBN,FBN,nbits)<>1 then raise ECryptoException.Create('Error on LShift');
  Result := Self;
end;

function TBigNum.Multiply(int: Int64): TBigNum;
Var n : TBigNum;
  ctx : PBN_CTX;
begin
  n := TBigNum.Create(int);
  Try
    ctx := BN_CTX_new;
    if BN_mul(FBN,FBN,n.FBN,ctx)<>1 then raise ECryptoException.Create('Error on multiply');
    Result := Self;
  Finally
    BN_CTX_free(ctx);
    n.Free;
  End;
end;

function TBigNum.RShift(nbits: Integer): TBigNum;
begin
  if BN_rshift(FBN,FBN,nbits)<>1 then raise ECryptoException.Create('Error on LShift');
  Result := Self;
end;

function TBigNum.Multiply(BN: TBigNum): TBigNum;
Var ctx : PBN_CTX;
begin
  ctx := BN_CTX_new;
  if BN_mul(FBN,FBN,BN.FBN,ctx)<>1 then raise ECryptoException.Create('Error on multiply');
  Result := Self;
  BN_CTX_free(ctx);
  Result := Self;
end;

procedure TBigNum.SetDecimalValue(const Value: String);
Var i : Integer;
  tmp_ansistring : RawByteString;
begin
  tmp_ansistring := Value;
  if BN_dec2bn(@FBN,PAnsiChar(tmp_ansistring))=0 then raise ECryptoException.Create('Error on dec2bn');
end;

procedure TBigNum.SetHexaValue(const Value: String);
Var i : Integer;
  tmp_ansistring : RawByteString;
begin
  tmp_ansistring := Value;
  i := BN_hex2bn(@FBN,PAnsiChar(tmp_ansistring));
  if i=0 then begin
      Raise ECryptoException.Create('Invalid Hexadecimal value:'+Value);
  end;
end;

procedure TBigNum.SetRawValue(const Value: TRawBytes);
var p : PBIGNUM;
begin
  p := BN_bin2bn(PAnsiChar(Value),length(Value),FBN);
  if (p<>FBN) Or (p=Nil) then Raise ECryptoException.Create('Error decoding Raw value to BigNum "'+TCrypto.ToHexaString(Value)+'" ('+inttostr(length(value))+')'+#10+
    CaptureLastSSLError);
end;

procedure TBigNum.SetValue(const Value: Int64);
var a : UInt64;
begin
  if Value<0 then a := (Value * (-1))
  else a := Value;
  if BN_set_word(FBN,a)<>1 then raise ECryptoException.Create('Error on set Value');
  if Value<0 then BN_set_negative(FBN,1)
  else BN_set_negative(FBN,0);
end;

function TBigNum.Sub(BN: TBigNum): TBigNum;
begin
  BN_sub(FBN,FBN,BN.FBN);
  Result := Self;
end;

function TBigNum.Sub(int: Int64): TBigNum;
Var bn : TBigNum;
begin
  bn := TBigNum.Create(int);
  Result := Sub(bn);
  bn.Free;
end;

class function TBigNum.TargetToHashRate(EncodedTarget: Cardinal): TBigNum;
Var bn1,bn2 : TBigNum;
  part_A, part_B : Cardinal;
  ctx : PBN_CTX;
begin
  { Target is 2 parts: First byte (A) is "0" bits on the left. Bytes 1,2,3 (B) are number after first "1" bit
    Example: Target 23FEBFCE
       Part_A: 23  -> 35 decimal
       Part_B: FEBFCE
    Target to Hash rate Formula:
      Result = 2^Part_A + ( (2^(Part_A-24)) * Part_B )
  }
  Result := TBigNum.Create(2);
  part_A := EncodedTarget shr 24;
  bn1 := TBigNum.Create(part_A);
  ctx := BN_CTX_new;
  try
    if BN_exp(Result.FBN,Result.FBN,bn1.FBN,ctx)<>1 then raise Exception.Create('Error 20161017-3');
  finally
    BN_CTX_free(ctx);
    bn1.Free;
  end;
  //
  part_B := (EncodedTarget shl 8) shr 8;
  //
  if (part_A<24) then begin
    // exponent is negative... 2^(Part_A-24)
    part_B := (part_B shr (24-part_A));
    bn1 := TBigNum.Create(part_B);
    Try
      Result.Add(bn1);
      Exit;
    Finally
      bn1.Free;
    End;
  end;
  //
  bn2 := TBigNum.Create(2);
  Try
    bn1 := TBigNum.Create(Int64(part_A) - 24);
    ctx := BN_CTX_new;
    try
      If BN_exp(bn2.FBN,bn2.FBN,bn1.FBN,ctx)<>1 then raise Exception.Create('Error 20161017-4');
    finally
      BN_CTX_free(ctx);
      bn1.Free;
    end;
    bn2.Multiply(part_B);
    Result.Add(bn2);
  Finally
    bn2.Free;
  End;
end;

function TBigNum.ToDecimal: String;
var p : PAnsiChar;
begin
  p := BN_bn2dec(FBN);
  {$IFDEF NO_ANSISTRING}
  Result := p^; // TODO: Not tested when AnsiString not available!
  {$ELSE}
  Result := StrPas(p);
  {$ENDIF}
  OpenSSL_free(p);
end;

function TBigNum.ToInt64(var int: Int64): TBigNum;
Var s : String;
 err : Integer;
 p : PAnsiChar;
begin
  p := BN_bn2dec(FBN);
  {$IFDEF NO_ANSISTRING}
  s := p^; // TODO: Not tested when AnsiString not available!
  {$ELSE}
  s := StrPas(p);
  {$ENDIF}
  OPENSSL_free(p);
  Val(s,int,err);
  if err<>0 then int := 0;
  Result := Self;
end;


initialization
  Randomize; // Initial random generator based on system time
finalization
end.<commit_msg>Update UCrypto.pas<commit_after>unit UCrypto;

{ Copyright (c) 2016 by <NAME>

  Distributed under the MIT software license, see the accompanying file LICENSE
  or visit http://www.opensource.org/licenses/mit-license.php.

  This unit is a part of the PascalCoin Project, an infinitely scalable
  cryptocurrency. Find us here:
  Web: https://www.pascalcoin.org
  Source: https://github.com/PascalCoin/PascalCoin

  If you like it, consider a donation using Bitcoin:
  16K3HCZRhFUtM8GdWRcfKeaa6KsuyxZaYk

  THIS LICENSE HEADER MUST NOT BE REMOVED.
}

{$IFDEF FPC}
  {$MODE Delphi}
{$ENDIF}

{$I config.inc}

interface

uses
  Classes, SysUtils, UOpenSSL, URandomHash, UBaseTypes, UPCDataTypes;

Type
  ECryptoException = Class(Exception);

  { TECPrivateKey }

  TECPrivateKey = Class
  private
    FPrivateKey: PEC_KEY;
    FEC_OpenSSL_NID : Word;
    procedure SetPrivateKey(const Value: PEC_KEY);
    function GetPublicKey: TECDSA_Public;
    function GetPublicKeyPoint: PEC_POINT;
  public
    Constructor Create;
    Procedure GenerateRandomPrivateKey(EC_OpenSSL_NID : Word);
    Destructor Destroy; override;
    Property PrivateKey : PEC_KEY read FPrivateKey;
    Property PublicKey : TECDSA_Public read GetPublicKey;
    Property PublicKeyPoint : PEC_POINT read GetPublicKeyPoint;
    Function SetPrivateKeyFromHexa(EC_OpenSSL_NID : Word; const hexa : String) : Boolean;
    Property EC_OpenSSL_NID : Word Read FEC_OpenSSL_NID;
    class function IsValidPublicKey(PubKey : TECDSA_Public; var errors : String) : Boolean; overload;
    class function IsValidPublicKey(PubKey : TECDSA_Public) : Boolean; overload;
    Function ExportToRaw : TRawBytes;
    class Function ImportFromRaw(Const raw : TRawBytes) : TECPrivateKey; static;
  End;

  { TCrypto }

  TCrypto = Class
  private
  public
    class function IsHexString(const AHexString: String) : boolean;
    class function ToHexaString(const raw : TRawBytes) : String; // DEPRECATED: Use TRawBytes.ToHexaString instead
    class function HexaToRaw(const HexaString : String) : TRawBytes; overload;
    class function HexaToRaw(const HexaString : String; var raw : TRawBytes) : Boolean; overload;
    class function DoSha256(p : PAnsiChar; plength : Cardinal) : TRawBytes; overload;
    class function DoSha256(const TheMessage : TRawBytes) : TRawBytes; overload;
    class procedure DoSha256(const TheMessage : TRawBytes; out ResultSha256 : TRawBytes);  overload;
    class function DoDoubleSha256(const TheMessage : TRawBytes) : TRawBytes; overload;
    class procedure DoDoubleSha256(p : PAnsiChar; plength : Cardinal; out ResultSha256 : TRawBytes); overload;
    class function DoRandomHash(const TheMessage : TRawBytes) : TRawBytes; overload;
    class procedure DoRandomHash(p : PAnsiChar; plength : Cardinal; out ResultSha256 : TRawBytes); overload;
    class procedure DoRandomHash(AFastHasher : TRandomHashFast; p : PAnsiChar; plength : Cardinal; out ResultSha256 : TRawBytes); overload;
    class function DoRipeMD160_HEXASTRING(const TheMessage : TRawBytes) : TRawBytes; overload;
    class function DoRipeMD160AsRaw(p : PAnsiChar; plength : Cardinal) : TRawBytes; overload;
    class function DoRipeMD160AsRaw(const TheMessage : TRawBytes) : TRawBytes; overload;
    class function PrivateKey2Hexa(Key : PEC_KEY) : String;
    class function ECDSASign(Key : PEC_KEY; const digest : TRawBytes) : TECDSA_SIG;
    class function ECDSAVerify(EC_OpenSSL_NID : Word; PubKey : EC_POINT; const digest : TRawBytes; Signature : TECDSA_SIG) : Boolean; overload;
    class function ECDSAVerify(PubKey : TECDSA_Public; const digest : TRawBytes; Signature : TECDSA_SIG) : Boolean; overload;
    class procedure InitCrypto;
    class function IsHumanReadable(Const ReadableText : TRawBytes) : Boolean;
    class function EncodeSignature(const signature : TECDSA_SIG) : TRawBytes;
    class function DecodeSignature(const rawSignature : TRawBytes; out signature : TECDSA_SIG) : Boolean;
  End;

  TBigNum = Class
  private
    FBN : PBIGNUM;
    procedure SetHexaValue(const Value: String);
    function GetHexaValue: String;
    procedure SetValue(const Value: Int64);
    function GetValue: Int64;
    function GetDecimalValue: String;
    procedure SetDecimalValue(const Value: String);
    function GetRawValue: TRawBytes;
    procedure SetRawValue(const Value: TRawBytes);
  public
    Constructor Create; overload;
    Constructor Create(initialValue : Int64); overload;
    Constructor Create(const hexaValue : String); overload;
    Destructor Destroy; override;
    Function Copy : TBigNum;
    Function Add(BN : TBigNum) : TBigNum; overload;
    Function Add(int : Int64) : TBigNum; overload;
    Function Sub(BN : TBigNum) : TBigNum; overload;
    Function Sub(int : Int64) : TBigNum; overload;
    Function Multiply(BN : TBigNum) : TBigNum; overload;
    Function Multiply(int : Int64) : TBigNum; overload;
    Function LShift(nbits : Integer) : TBigNum;
    Function RShift(nbits : Integer) : TBigNum;
    Function CompareTo(BN : TBigNum) : Integer;
    Function Divide(BN : TBigNum) : TBigNum; overload;
    Function Divide(int : Int64) : TBigNum; overload;
    Procedure Divide(dividend, remainder : TBigNum); overload;
    Function ToInt64(var int : Int64) : TBigNum;
    Function ToDecimal : String;
    Property HexaValue : String read GetHexaValue write SetHexaValue;
    Property RawValue : TRawBytes read GetRawValue write SetRawValue;
    Property DecimalValue : String read GetDecimalValue write SetDecimalValue;
    Property Value : Int64 read GetValue write SetValue;
    Function IsZero : Boolean;
    Class Function HexaToDecimal(hexa : String) : String;
    Class Function TargetToHashRate(EncodedTarget : Cardinal) : TBigNum;
  End;

Const
  CT_TECDSA_Public_Nul : TECDSA_Public = (EC_OpenSSL_NID:0;x:Nil;y:Nil);
  CT_TECDSA_SIG_Nul : TECDSA_SIG = (r:Nil;s:Nil);

implementation

uses
  ULog, UConst, UAccounts;

Var _initialized : Boolean = false;

Procedure _DoInit;
var err : String;
 c : Cardinal;
Begin
  if Not (_initialized) then begin
    _initialized := true;
    If Not InitSSLFunctions then begin
      err := 'Cannot load OpenSSL library '+SSL_C_LIB;
      TLog.NewLog(ltError,'OpenSSL',err);
      Raise Exception.Create(err);
    end;
    {$IFNDEF OpenSSL10}
    If Not Assigned(OpenSSL_version_num) then begin
      err := 'OpenSSL library is not v1.1 version: '+SSL_C_LIB;
      TLog.NewLog(ltError,'OpenSSL',err);
      Raise Exception.Create(err);
    end;
    c := OpenSSL_version_num;
    if (c<$10100000) Or (c>$1010FFFF) then begin
      err := 'OpenSSL library is not v1.1 version ('+IntToHex(c,8)+'): '+SSL_C_LIB;
      TLog.NewLog(ltError,'OpenSSL',err);
      Raise Exception.Create(err);
    end;
    {$ENDIF}
  end;
End;

{ TECPrivateKey }

constructor TECPrivateKey.Create;
begin
  FPrivateKey := Nil;
  FEC_OpenSSL_NID := CT_Default_EC_OpenSSL_NID;
end;

destructor TECPrivateKey.Destroy;
begin
  if Assigned(FPrivateKey) then EC_KEY_free(FPrivateKey);
  inherited;
end;

function TECPrivateKey.ExportToRaw: TRawBytes;
Var ms : TStream;
  aux : TRawBytes;
begin
  ms := TMemoryStream.Create;
  Try
    ms.Write(FEC_OpenSSL_NID,sizeof(FEC_OpenSSL_NID));
    SetLength(aux,BN_num_bytes(EC_KEY_get0_private_key(FPrivateKey)));
    BN_bn2bin(EC_KEY_get0_private_key(FPrivateKey),@aux[Low(aux)]);
    TStreamOp.WriteAnsiString(ms,aux);
    SetLength(Result,ms.Size);
    ms.Position := 0;
    ms.Read(Result[Low(Result)],ms.Size);
  Finally
    ms.Free;
  End;
end;

procedure TECPrivateKey.GenerateRandomPrivateKey(EC_OpenSSL_NID : Word);
Var i : Integer;
begin
  if Assigned(FPrivateKey) then EC_KEY_free(FPrivateKey);
  FEC_OpenSSL_NID := EC_OpenSSL_NID;
  FPrivateKey := EC_KEY_new_by_curve_name(EC_OpenSSL_NID);
  i := EC_KEY_generate_key(FPrivateKey);
  if i<>1 then Raise ECryptoException.Create('Error generating new Random Private Key');
end;

function TECPrivateKey.GetPublicKey: TECDSA_Public;
var ps : PAnsiChar;
  BNx,BNy : PBIGNUM;
  ctx : PBN_CTX;
begin
  Result.EC_OpenSSL_NID := FEC_OpenSSL_NID;
  ctx := BN_CTX_new;
  BNx := BN_new;
  BNy := BN_new;
  Try
    EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(FPrivateKey),EC_KEY_get0_public_key(FPrivateKey),BNx,BNy,ctx);
    SetLength(Result.x,BN_num_bytes(BNx));
    BN_bn2bin(BNx,@Result.x[Low(Result.x)]);
    SetLength(Result.y,BN_num_bytes(BNy));
    BN_bn2bin(BNy,@Result.y[Low(Result.y)]);
  Finally
    BN_CTX_free(ctx);
    BN_free(BNx);
    BN_free(BNy);
  End;
end;

function TECPrivateKey.GetPublicKeyPoint: PEC_POINT;
begin
  Result := EC_KEY_get0_public_key(FPrivateKey);
end;

class function TECPrivateKey.ImportFromRaw(const raw: TRawBytes): TECPrivateKey;
Var ms : TStream;
  aux : TRawBytes;
  BNx : PBIGNUM;
  ECID : Word;
  PAC : PAnsiChar;
begin
  Result := Nil;
  ms := TMemoryStream.Create;
  Try
    ms.WriteBuffer(raw[Low(raw)],Length(raw));
    ms.Position := 0;
    if ms.Read(ECID,sizeof(ECID))<>sizeof(ECID) then exit;
    If TStreamOp.ReadAnsiString(ms,aux)<0 then exit;
    BNx := BN_bin2bn(PAnsiChar(aux),Length(aux),nil);
    if assigned(BNx) then begin
      try
        PAC := BN_bn2hex(BNx);
        try
          Result := TECPrivateKey.Create;
          Try
            If Not Result.SetPrivateKeyFromHexa(ECID,{$IFDEF NO_ANSISTRING}UBaseTypes.{$ENDIF}StrPas(PAC)) then begin
              FreeAndNil(Result);
            end;
          Except
            On E:Exception do begin
              FreeAndNil(Result);
              // Note: Will not raise Exception, only will log it
              TLog.NewLog(lterror,ClassName,'Error importing private key from '+TCrypto.ToHexaString(raw)+' ECID:'+IntToStr(ECID)+' ('+E.ClassName+'): '+E.Message);
            end;
          end;
        finally
          OpenSSL_free(PAC);
        end;
      finally
        BN_free(BNx);
      end;
    end;
  Finally
    ms.Free;
  End;
end;

class function TECPrivateKey.IsValidPublicKey(PubKey: TECDSA_Public; var errors : String): Boolean;
Var BNx,BNy : PBIGNUM;
  ECG : PEC_GROUP;
  ctx : PBN_CTX;
  pub_key : PEC_POINT;
begin
  Result := False;
  BNx := BN_bin2bn(PAnsiChar(PubKey.x),length(PubKey.x),nil);
  if Not Assigned(BNx) then Exit;
  try
    BNy := BN_bin2bn(PAnsiChar(PubKey.y),length(PubKey.y),nil);
    if Not Assigned(BNy) then Exit;
    try
      ECG := EC_GROUP_new_by_curve_name(PubKey.EC_OpenSSL_NID);
      if Not Assigned(ECG) then Exit;
      try
        pub_key := EC_POINT_new(ECG);
        try
          if Not Assigned(pub_key) then Exit;
          ctx := BN_CTX_new;
          try
            Result := EC_POINT_set_affine_coordinates_GFp(ECG,pub_key,BNx,BNy,ctx)=1;
            if not Result then begin
              errors := Format('Invalid Public key type:%d - Length x:%d y:%d Error:%s',[PubKey.EC_OpenSSL_NID,length(PubKey.x),length(PubKey.y), CaptureLastSSLError]);
            end;
          finally
            BN_CTX_free(ctx);
          end;
        finally
          EC_POINT_free(pub_key);
        end;
      finally
        EC_GROUP_free(ECG);
      end;
    finally
      BN_free(BNy);
    end;
  finally
    BN_free(BNx);
  end;
end;

class function TECPrivateKey.IsValidPublicKey(PubKey: TECDSA_Public): Boolean;
var Ltmp : String;
begin
  Result := IsValidPublicKey(PubKey,Ltmp);
end;

procedure TECPrivateKey.SetPrivateKey(const Value: PEC_KEY);
begin
  if Assigned(FPrivateKey) then EC_KEY_free(FPrivateKey);
  FPrivateKey := Value;
end;

function TECPrivateKey.SetPrivateKeyFromHexa(EC_OpenSSL_NID : Word; const hexa : String) : Boolean;
var bn : PBIGNUM;
  ctx : PBN_CTX;
  pub_key : PEC_POINT;
  tmp_ansistring : RawByteString;
begin
  Result := False;
  bn := BN_new;
  try
    tmp_ansistring := hexa;
    if BN_hex2bn(@bn,PAnsiChar(tmp_ansistring))=0 then Raise ECryptoException.Create('Invalid hexa string to convert to Hexadecimal value');

    if Assigned(FPrivateKey) then EC_KEY_free(FPrivateKey);
    FEC_OpenSSL_NID := EC_OpenSSL_NID;
    FPrivateKey := EC_KEY_new_by_curve_name(EC_OpenSSL_NID);
    If Not Assigned(FPrivateKey) then Exit;
    if EC_KEY_set_private_key(FPrivateKey,bn)<>1 then raise ECryptoException.Create('Invalid num to set as private key');
    //
    ctx := BN_CTX_new;
    pub_key := EC_POINT_new(EC_KEY_get0_group(FPrivateKey));
    try
      if EC_POINT_mul(EC_KEY_get0_group(FPrivateKey),pub_key,bn,nil,nil,ctx)<>1 then raise ECryptoException.Create('Error obtaining public key');
      EC_KEY_set_public_key(FPrivateKey,pub_key);
    finally
      BN_CTX_free(ctx);
      EC_POINT_free(pub_key);
    end;
  finally
    BN_free(bn);
  end;
  Result := True;
end;

{ TCrypto }

{ New at Build 1.0.2
  Note: Delphi is slowly when working with Strings (allowing space)... so to
  increase speed we use a String as a pointer, and only increase speed if
  needed. Also the same with functions "GetMem" and "FreeMem" }
class procedure TCrypto.DoDoubleSha256(p: PAnsiChar; plength: Cardinal; out ResultSha256: TRawBytes);
Var PS : PAnsiChar;
begin
  If length(ResultSha256)<>32 then SetLength(ResultSha256,32);
  PS := @ResultSha256[Low(ResultSha256)];
  SHA256(p,plength,PS);
  SHA256(PS,32,PS);
end;

class function TCrypto.DoDoubleSha256(const TheMessage: TRawBytes): TRawBytes;
begin
  Result := DoSha256(DoSha256(TheMessage));
end;

class function TCrypto.DoRipeMD160_HEXASTRING(const TheMessage: TRawBytes): TRawBytes;
Var PS : PAnsiChar;
  PC : PAnsiChar;
  i : Integer;
  tmp : String;
begin
  GetMem(PS,33);
  RIPEMD160(PAnsiChar(@TheMessage[Low(TheMessage)]),Length(TheMessage),PS);
  PC := PS;
  tmp := '';
  for i := 1 to 20 do begin
    tmp := tmp + IntToHex(PtrInt(PC^),2);
    inc(PC);
  end;
  FreeMem(PS,33);
  Result := TEncoding.ASCII.GetBytes(tmp);
end;

class function TCrypto.DoRipeMD160AsRaw(p: PAnsiChar; plength: Cardinal): TRawBytes;
Var PS : PAnsiChar;
begin
  SetLength(Result,20);
  PS := @Result[Low(Result)];
  RIPEMD160(p,plength,PS);
end;

class function TCrypto.DoRipeMD160AsRaw(const TheMessage: TRawBytes): TRawBytes;
Var PS : PAnsiChar;
begin
  SetLength(Result,20);
  PS := @Result[Low(Result)];
  RIPEMD160(PAnsiChar(@TheMessage[Low(TheMessage)]),Length(TheMessage),PS);
end;

class function TCrypto.DoSha256(p: PAnsiChar; plength: Cardinal): TRawBytes;
Var PS : PAnsiChar;
begin
  SetLength(Result,32);
  PS := @Result[Low(Result)];
  SHA256(p,plength,PS);
end;

class function TCrypto.DoSha256(const TheMessage: TRawBytes): TRawBytes;
Var PS : PAnsiChar;
begin
  SetLength(Result,32);
  PS := @Result[Low(Result)];
  SHA256(@TheMessage[Low(TheMessage)],Length(TheMessage),PS);
end;

{ New at Build 2.1.6
  Note: Delphi is slowly when working with Strings (allowing space)... so to
  increase speed we use a String as a pointer, and only increase speed if
  needed. Also the same with functions "GetMem" and "FreeMem" }
class procedure TCrypto.DoSha256(const TheMessage: TRawBytes; out ResultSha256: TRawBytes);
Var PS : PAnsiChar;
begin
  If length(ResultSha256)<>32 then SetLength(ResultSha256,32);
  PS := @ResultSha256[Low(ResultSha256)];
  SHA256(@TheMessage[Low(TheMessage)],Length(TheMessage),PS);
end;

class function TCrypto.ECDSASign(Key: PEC_KEY; const digest: TRawBytes): TECDSA_SIG;
Var PECS : PECDSA_SIG;
  p : PAnsiChar;
  i : Integer;
begin
  PECS := ECDSA_do_sign(PAnsiChar(@digest[Low(digest)]),Length(digest),Key);
  Try
    if PECS = Nil then raise ECryptoException.Create('Error signing');

    i := BN_num_bytes(PECS^._r);
    SetLength(Result.r,i);
    p := @Result.r[Low(Result.r)];
    i := BN_bn2bin(PECS^._r,p);

    i := BN_num_bytes(PECS^._s);
    SetLength(Result.s,i);
    p := @Result.s[Low(Result.s)];
    i := BN_bn2bin(PECS^._s,p);
  Finally
    ECDSA_SIG_free(PECS);
  End;
end;

class function TCrypto.ECDSAVerify(EC_OpenSSL_NID : Word; PubKey: EC_POINT; const digest: TRawBytes; Signature: TECDSA_SIG): Boolean;
Var PECS : PECDSA_SIG;
  PK : PEC_KEY;
  {$IFDEF OpenSSL10}
  {$ELSE}
  bnr,bns : PBIGNUM;
  {$ENDIF}
begin
  PECS := ECDSA_SIG_new;
  Try
    {$IFDEF OpenSSL10}
    BN_bin2bn(PAnsiChar(Signature.r),length(Signature.r),PECS^._r);
    BN_bin2bn(PAnsiChar(Signature.s),length(Signature.s),PECS^._s);
    {$ELSE}
{    ECDSA_SIG_get0(PECS,@bnr,@bns);
    BN_bin2bn(PAnsiChar(Signature.r),length(Signature.r),bnr);
    BN_bin2bn(PAnsiChar(Signature.s),length(Signature.s),bns);}
    bnr := BN_bin2bn(PAnsiChar(Signature.r),length(Signature.r),nil);
    bns := BN_bin2bn(PAnsiChar(Signature.s),length(Signature.s),nil);
    if ECDSA_SIG_set0(PECS,bnr,bns)<>1 then Raise Exception.Create('Dev error 20161019-1 '+CaptureLastSSLError);
    {$ENDIF}

    PK := EC_KEY_new_by_curve_name(EC_OpenSSL_NID);
    EC_KEY_set_public_key(PK,@PubKey);
    Case ECDSA_do_verify(@digest[Low(digest)],Length(digest),PECS,PK) of
      1 : Result := true;
      0 : Result := false;
    Else
      raise ECryptoException.Create('Error on Verify');
    End;
    EC_KEY_free(PK);
  Finally
    ECDSA_SIG_free(PECS);
  End;
end;

class function TCrypto.ECDSAVerify(PubKey: TECDSA_Public; const digest: TRawBytes; Signature: TECDSA_SIG): Boolean;
Var BNx,BNy : PBIGNUM;
  ECG : PEC_GROUP;
  ctx : PBN_CTX;
  pub_key : PEC_POINT;
begin
  BNx := BN_bin2bn(PAnsiChar(PubKey.x),length(PubKey.x),nil);
  BNy := BN_bin2bn(PAnsiChar(PubKey.y),length(PubKey.y),nil);

  ECG := EC_GROUP_new_by_curve_name(PubKey.EC_OpenSSL_NID);
  pub_key := EC_POINT_new(ECG);
  ctx := BN_CTX_new;
  if EC_POINT_set_affine_coordinates_GFp(ECG,pub_key,BNx,BNy,ctx)=1 then begin
    Result := ECDSAVerify(PubKey.EC_OpenSSL_NID, pub_key^,digest,signature);
  end else begin
    Result := false;
  end;
  {$IFDEF HIGHLOG}
  TLog.NewLog(ltdebug,ClassName,Format('ECDSAVerify %s x:%s y:%s Digest:%s Signature r:%s s:%s',
    [TAccountComp.GetECInfoTxt(PubKey.EC_OpenSSL_NID),ToHexaString(PubKey.x),ToHexaString(PubKey.y),
      ToHexaString(digest),ToHexaString(Signature.r),ToHexaString(Signature.s)]));
  {$ENDIF}
  BN_CTX_free(ctx);
  EC_POINT_free(pub_key);
  EC_GROUP_free(ECG);
  BN_free(BNx);
  BN_free(BNy);
end;

class function TCrypto.HexaToRaw(const HexaString: String): TRawBytes;
begin
  HexaToRaw(HexaString,Result);
end;

class function TCrypto.HexaToRaw(const HexaString: String; var raw: TRawBytes): Boolean;
Var i : Integer;
  LHexaRaw : TRawBytes;
  {$IFDEF FPC}
  P : PAnsiChar;
  {$ENDIF}
begin
  LHexaRaw.FromString(LowerCase(HexaString));
  if (Length(LHexaRaw)=0) then begin
    SetLength(raw,0);
    Exit(True);
  end;
  if ((Length(LHexaRaw) MOD 2)<>0) then Exit(False); // odd string
  SetLength(raw,Length(LHexaRaw) DIV 2);
  {$IFDEF FPC}
  P := @raw[0];
  i := HexToBin(PAnsiChar(LHexaRaw.ToString),P,Length(raw));
  {$ELSE}
  i := HexToBin(LHexaRaw,0,raw,0,Length(raw));
  {$ENDIF}
  Result := (i = (Length(raw)));
end;

class procedure TCrypto.InitCrypto;
begin
  _DoInit;
end;

class function TCrypto.IsHumanReadable(const ReadableText: TRawBytes): Boolean;
  // Will return TRUE if all bytes are between 32..126 (ASCII printable bytes)
Var i : Integer;
Begin
  Result := true;
  for i := Low(ReadableText) to High(ReadableText) do begin
    if (ord(ReadableText[i])<32) Or (ord(ReadableText[i])>=127) then begin
      Result := false;
      Exit;
    end;
  end;
end;

class function TCrypto.EncodeSignature(const signature: TECDSA_SIG): TRawBytes;
Var ms : TStream;
begin
  ms := TMemoryStream.Create;
  Try
    TStreamOp.WriteAnsiString(ms,signature.r);
    TStreamOp.WriteAnsiString(ms,signature.s);
    Result := TStreamOp.SaveStreamToRaw(ms);
  finally
    ms.Free;
  end;
end;

class function TCrypto.DecodeSignature(const rawSignature : TRawBytes; out signature : TECDSA_SIG) : Boolean;
var ms : TStream;
begin
  signature := CT_TECDSA_SIG_Nul;
  Result := False;
  ms := TMemoryStream.Create;
  Try
    TStreamOp.LoadStreamFromRaw(ms,rawSignature);
    ms.Position:=0;
    if TStreamOp.ReadAnsiString(ms,signature.r)<0 then Exit;
    if TStreamOp.ReadAnsiString(ms,signature.s)<0 then Exit;
    if ms.Position<ms.Size then Exit; // Invalid position
    Result := True;
  finally
    ms.Free;
  end;
end;

class function TCrypto.PrivateKey2Hexa(Key: PEC_KEY): String;
Var p : PAnsiChar;
begin
  p := BN_bn2hex(EC_KEY_get0_private_key(Key));
  {$IFDEF NO_ANSISTRING}
  Result := UBaseTypes.StrPas(p); // TODO: Not tested when AnsiString not available!
  {$ELSE}
  Result := StrPas(p);
  {$ENDIF}
  OPENSSL_free(p);
end;

class function TCrypto.ToHexaString(const raw: TRawBytes): String;
begin
  Result := raw.ToHexaString;
end;

class function TCrypto.IsHexString(const AHexString: String) : boolean;
var
  i : Integer;
begin
  Result := true;
  for i := Low(AHexString) to High(AHexString) do
    if (NOT (AHexString[i] in ['0'..'9'])) AND
       (NOT (AHexString[i] in ['a'..'f'])) AND
       (NOT (AHexString[i] in ['A'..'F'])) then begin
       Result := false;
       exit;
    end;
end;

{ New at Build 4.0.0 }

class function TCrypto.DoRandomHash(const TheMessage: TRawBytes): TRawBytes;
begin
  Result := TRandomHashFast.Compute(TheMessage);
end;

class procedure TCrypto.DoRandomHash(p : PAnsiChar; plength : Cardinal; out ResultSha256 : TRawBytes);
var
  LInput : TBytes;
  LResult : TBytes;
begin
  if Length(ResultSha256) <> 32 then SetLength(ResultSha256, 32);
  SetLength(LInput, plength);
  Move(p^, LInput[0], plength);
  LResult := TRandomHashFast.Compute(LInput);
  Move(LResult[0], ResultSha256[Low(ResultSha256)], 32);
end;

class procedure TCrypto.DoRandomHash(AFastHasher : TRandomHashFast; p : PAnsiChar; plength : Cardinal; out ResultSha256 : TRawBytes);
var
  LInput : TBytes;
  LResult : TBytes;
begin
  if Length(ResultSha256) <> 32 then SetLength(ResultSha256, 32);
  SetLength(LInput, plength);
  Move(p^, LInput[0], plength);
  LResult := AFastHasher.Hash(LInput);
  Move(LResult[0], ResultSha256[Low(ResultSha256)], 32);
end;

{ TBigNum }

function TBigNum.Add(BN: TBigNum): TBigNum;
begin
  BN_add(FBN,BN.FBN,FBN);
  Result := Self;
end;

function TBigNum.Add(int: Int64): TBigNum;
Var bn : TBigNum;
begin
  bn := TBigNum.Create(int);
  Result := Add(bn);
  bn.Free;
end;

function TBigNum.CompareTo(BN: TBigNum): Integer;
begin
  Result := BN_cmp(FBN,BN.FBN);
end;

function TBigNum.Copy: TBigNum;
begin
  Result := TBigNum.Create(0);
  BN_copy(Result.FBN,FBN);
end;

constructor TBigNum.Create;
begin
  Create(0);
end;

constructor TBigNum.Create(const hexaValue: String);
begin
  Create(0);
  SetHexaValue(hexaValue);
end;

constructor TBigNum.Create(initialValue : Int64);
begin
  FBN := BN_new;
  SetValue(initialValue);
end;

destructor TBigNum.Destroy;
begin
  BN_free(FBN);
  inherited;
end;

procedure TBigNum.Divide(dividend, remainder: TBigNum);
Var ctx : PBN_CTX;
begin
  ctx := BN_CTX_new;
  BN_div(FBN,remainder.FBN,FBN,dividend.FBN,ctx);
  BN_CTX_free(ctx);
end;

function TBigNum.Divide(int: Int64): TBigNum;
Var bn : TBigNum;
begin
  bn := TBigNum.Create(int);
  Result := Divide(bn);
  bn.Free;
end;

function TBigNum.Divide(BN: TBigNum): TBigNum;
Var _div,_rem : PBIGNUM;
  ctx : PBN_CTX;
begin
  _div := BN_new;
  _rem := BN_new;
  ctx := BN_CTX_new;
  BN_div(FBN,_rem,FBN,BN.FBN,ctx);
  BN_free(_div);
  BN_free(_rem);
  BN_CTX_free(ctx);
  Result := Self;
end;

function TBigNum.GetDecimalValue: String;
var p : PAnsiChar;
begin
  p := BN_bn2dec(FBN);
  {$IFDEF NO_ANSISTRING}
  Result := UBaseTypes.StrPas(p); // TODO: Not tested when AnsiString not available!
  {$ELSE}
  Result := StrPas(p);
  {$ENDIF}
  OpenSSL_free(p);
end;

function TBigNum.GetHexaValue: String;
Var p : PAnsiChar;
begin
  p := BN_bn2hex(FBN);
  {$IFDEF NO_ANSISTRING}
  Result := UBaseTypes.StrPas(p); // TODO: Not tested when AnsiString not available!
  {$ELSE}
  Result := StrPas(p);
  {$ENDIF}
  OPENSSL_free(p);
end;

function TBigNum.GetRawValue: TRawBytes;
Var p : PAnsiChar;
  i : Integer;
begin
  i := BN_num_bytes(FBN);
  SetLength(Result,i);
  p := @Result[Low(Result)];
  i := BN_bn2bin(FBN,p);
end;

function TBigNum.GetValue: Int64;
Var p : PAnsiChar;
  a : RawByteString;
  err : Integer;
begin
  p := BN_bn2dec(FBN);
  {$IFDEF NO_ANSISTRING}
  a := UBaseTypes.StrPas(p); // TODO: Not tested when AnsiString not available!
  {$ELSE}
  a := StrPas(p);
  {$ENDIF}
  OPENSSL_free(p);
  Val(a,Result,err);
end;

class function TBigNum.HexaToDecimal(hexa: String): String;
Var bn : TBigNum;
begin
  bn := TBigNum.Create(hexa);
  result := bn.ToDecimal;
  bn.Free;
end;

function TBigNum.IsZero: Boolean;
Var dv : String;
begin
  dv := DecimalValue;
  Result := dv='0';
end;

function TBigNum.LShift(nbits: Integer): TBigNum;
begin
  if BN_lshift(FBN,FBN,nbits)<>1 then raise ECryptoException.Create('Error on LShift');
  Result := Self;
end;

function TBigNum.Multiply(int: Int64): TBigNum;
Var n : TBigNum;
  ctx : PBN_CTX;
begin
  n := TBigNum.Create(int);
  Try
    ctx := BN_CTX_new;
    if BN_mul(FBN,FBN,n.FBN,ctx)<>1 then raise ECryptoException.Create('Error on multiply');
    Result := Self;
  Finally
    BN_CTX_free(ctx);
    n.Free;
  End;
end;

function TBigNum.RShift(nbits: Integer): TBigNum;
begin
  if BN_rshift(FBN,FBN,nbits)<>1 then raise ECryptoException.Create('Error on LShift');
  Result := Self;
end;

function TBigNum.Multiply(BN: TBigNum): TBigNum;
Var ctx : PBN_CTX;
begin
  ctx := BN_CTX_new;
  if BN_mul(FBN,FBN,BN.FBN,ctx)<>1 then raise ECryptoException.Create('Error on multiply');
  Result := Self;
  BN_CTX_free(ctx);
  Result := Self;
end;

procedure TBigNum.SetDecimalValue(const Value: String);
Var i : Integer;
  tmp_ansistring : RawByteString;
begin
  tmp_ansistring := Value;
  if BN_dec2bn(@FBN,PAnsiChar(tmp_ansistring))=0 then raise ECryptoException.Create('Error on dec2bn');
end;

procedure TBigNum.SetHexaValue(const Value: String);
Var i : Integer;
  tmp_ansistring : RawByteString;
begin
  tmp_ansistring := Value;
  i := BN_hex2bn(@FBN,PAnsiChar(tmp_ansistring));
  if i=0 then begin
      Raise ECryptoException.Create('Invalid Hexadecimal value:'+Value);
  end;
end;

procedure TBigNum.SetRawValue(const Value: TRawBytes);
var p : PBIGNUM;
begin
  p := BN_bin2bn(PAnsiChar(Value),length(Value),FBN);
  if (p<>FBN) Or (p=Nil) then Raise ECryptoException.Create('Error decoding Raw value to BigNum "'+TCrypto.ToHexaString(Value)+'" ('+inttostr(length(value))+')'+#10+
    CaptureLastSSLError);
end;

procedure TBigNum.SetValue(const Value: Int64);
var a : UInt64;
begin
  if Value<0 then a := (Value * (-1))
  else a := Value;
  if BN_set_word(FBN,a)<>1 then raise ECryptoException.Create('Error on set Value');
  if Value<0 then BN_set_negative(FBN,1)
  else BN_set_negative(FBN,0);
end;

function TBigNum.Sub(BN: TBigNum): TBigNum;
begin
  BN_sub(FBN,FBN,BN.FBN);
  Result := Self;
end;

function TBigNum.Sub(int: Int64): TBigNum;
Var bn : TBigNum;
begin
  bn := TBigNum.Create(int);
  Result := Sub(bn);
  bn.Free;
end;

class function TBigNum.TargetToHashRate(EncodedTarget: Cardinal): TBigNum;
Var bn1,bn2 : TBigNum;
  part_A, part_B : Cardinal;
  ctx : PBN_CTX;
begin
  { Target is 2 parts: First byte (A) is "0" bits on the left. Bytes 1,2,3 (B) are number after first "1" bit
    Example: Target 23FEBFCE
       Part_A: 23  -> 35 decimal
       Part_B: FEBFCE
    Target to Hash rate Formula:
      Result = 2^Part_A + ( (2^(Part_A-24)) * Part_B )
  }
  Result := TBigNum.Create(2);
  part_A := EncodedTarget shr 24;
  bn1 := TBigNum.Create(part_A);
  ctx := BN_CTX_new;
  try
    if BN_exp(Result.FBN,Result.FBN,bn1.FBN,ctx)<>1 then raise Exception.Create('Error 20161017-3');
  finally
    BN_CTX_free(ctx);
    bn1.Free;
  end;
  //
  part_B := (EncodedTarget shl 8) shr 8;
  //
  if (part_A<24) then begin
    // exponent is negative... 2^(Part_A-24)
    part_B := (part_B shr (24-part_A));
    bn1 := TBigNum.Create(part_B);
    Try
      Result.Add(bn1);
      Exit;
    Finally
      bn1.Free;
    End;
  end;
  //
  bn2 := TBigNum.Create(2);
  Try
    bn1 := TBigNum.Create(Int64(part_A) - 24);
    ctx := BN_CTX_new;
    try
      If BN_exp(bn2.FBN,bn2.FBN,bn1.FBN,ctx)<>1 then raise Exception.Create('Error 20161017-4');
    finally
      BN_CTX_free(ctx);
      bn1.Free;
    end;
    bn2.Multiply(part_B);
    Result.Add(bn2);
  Finally
    bn2.Free;
  End;
end;

function TBigNum.ToDecimal: String;
var p : PAnsiChar;
begin
  p := BN_bn2dec(FBN);
  {$IFDEF NO_ANSISTRING}
  Result := UBaseTypes.StrPas(p); // TODO: Not tested when AnsiString not available!
  {$ELSE}
  Result := StrPas(p);
  {$ENDIF}
  OpenSSL_free(p);
end;

function TBigNum.ToInt64(var int: Int64): TBigNum;
Var s : String;
 err : Integer;
 p : PAnsiChar;
begin
  p := BN_bn2dec(FBN);
  {$IFDEF NO_ANSISTRING}
  s := UBaseTypes.StrPas(p); // TODO: Not tested when AnsiString not available!
  {$ELSE}
  s := StrPas(p);
  {$ENDIF}
  OPENSSL_free(p);
  Val(s,int,err);
  if err<>0 then int := 0;
  Result := Self;
end;


initialization
  Randomize; // Initial random generator based on system time
finalization
end.
{'fraction_non_alphanumeric': 0.11869229023755913, 'fraction_numerical': 0.01563945471365485, 'mean_word_length': 4.328497023809524, 'pattern_counts': {'":': 0, '<': 61, '<?xml version=': 0, '>': 49, 'https://': 4, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '74157', 'n_tokens_mistral': 22291, 'n_tokens_neox': 21271, 'n_words': 4772}