Forum > FPC development

TCryptManager Proposal for the FPC RTL


Following a discussion with Michael.

Here is a proposal of RTL-level hooks for cryptography.
Idea is to propose a default pascal and cross-platform implementation, with an optional overwrite with optimized alternatives (GnuTLS, OpenSSL, mORMot).
It is implemented as a set of classes, and an internal thread-safe list of case-insensitive identifiers to register the actual implementations.

Here is a first draft, to something which may be possible:

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---type  ECrypt = class(Exception);  TCryptIDClass = class of TCryptID;   // abstract class implemented e.g. by TCryptRandom/TCryptSymmetric  TCryptID = class(TObject)  protected    fName: string;    // case-insensitive quick lookup of the algorithms    class function InternalFind(const aName: string): TCryptID; virtual; abstract;  public    // internal constructor: use Find() instead    constructor Create(const aName: string); virtual; abstract;    /// register this class to override one or several identifiers implementation    class procedure Implements(const aName: array of string); virtual; abstract;    // typical values may follow OpenSSL naming, e.g. 'MD5', 'AES-128-GCM' or    // 'prime256v1'    property Name: string read fName;  end;   // abstract class implemented e.g. by TCryptHash/TCryptCipher/TCryptKey  TCryptInstance = class(TObject)  protected    fCryptID: TCryptID;  public    constructor Create(aCryptID: TCryptID); overload; virtual; abstract;    constructor Create(const aName: string); overload; virtual; abstract;    /// register this class to override one or several identifiers implementation    class procedure Implements(const aName: array of string); virtual; abstract;    function Clone: TCryptInstance; virtual; abstract;    property CryptID: TCryptID read fCryptID;  end;   TCryptRandom = class(TCryptID)  public    // case-insensitive quick lookup of the algorithms    // use TCryptRandom.Find('rnd-entropy').GetRandom() gather OS entropy    class function Find(const aName: string = 'rnd-default'): TCryptRandom; virtual; abstract;    procedure GetRandom(dst: pointer; dstlen: PtrInt); virtual; abstract;  end;   TCryptHash = class(TCryptInstance)  public    // hashing methods    procedure Update(buf: pointer; buflen: PtrInt); virtual; abstract;    procedure Final(digest: pointer; digestlen: PtrInt); virtual; abstract;  end;   TCryptCipher = class(TCryptInstance)  public    // dst=nil for AEAD    function Process(src, dst: pointer; srclen, dstlen: PtrInt): PtrInt; virtual; abstract;    function Final(dst, tag: pointer; dstlen, taglen: PtrInt): PtrInt; virtual; abstract;  end;   TCryptKey = class(TCryptInstance)  public    function Retrieve(buf: pointer; buflen: PtrInt): boolean; virtual; abstract;   { TODO: other persistence methods, and overloaded Create }  end;  { NOTE: as alternative, we may just use TCryptKey = TBytes }   TCryptSymmetric = class(TCryptID)  public    // case-insensitive quick lookup of the algorithms    class function Find(const aName: string): TCryptSymmetric; virtual; abstract;    // public key cryptography methods    function MakeKeyPair(pub, priv: pointer; publen, privlen: PtrInt): boolean; virtual; abstract;    function NewKey(key: pointer; keylen: PtrInt; pub: boolean): TCryptKey; virtual; abstract;    procedure Sign(priv: TCryptKey; hash, sign: pointer; hashlen, signlen: PtrInt); virtual; abstract;    function Verify(pub: TCryptKey; hash, sign: pointer; hashlen, signlen: PtrInt): boolean; virtual; abstract;    procedure SharedSecret(priv, pub: TCryptKey; secret: pointer; secretlen: PtrInt); virtual; abstract;  end;   { TODO: abstract PKI manager? } 

Looks good at first sight.
Maybe an extra init for the hash, so a single instance can be reused.

We can add some overloads with TBytes/String for convenience.

What would you put in the PKI manager ?

I have implemented a full set of classes and interfaces in mORMot 2.
It could be used as a reference - the dependencies with mORMot are very small, and could easily be replaced for the RTL.

There is abstract classes, then a set of inherited classes which calls the mORMot engines.
It should cover most usecases about random generators, hashing, signing, encryption and public key cryptography.
The use of interfaces for hash and encryption instances allow a fluent and simple call in end user code.
The classes and methods have been designed to reuse as much code as possible, but also been safe from use.

Please check this commit:

This looks good.

I will do some refactoring - mostly remove the JSON support but in essence these classes look good.

The global functions can maybe be moved/copied to class methods of  TCryptAlgo

There is some other new news: DCPCrypt is abandoned.

I have received permission from the original author to distribute it under FPC with the standard FPC license,
 so this can serve as the 'basic' implementation for many of the algorithms.
With the factory methods, we can always register more performant algorithms, but by default FPC will come with a pretty complete hash/cypher suite.


[0] Message Index

Go to full version