Why OpenSSL?

OpenSSL is the reference library for cryptography and secure TLS/HTTPS communication. It is part of most Linux/BSD systems, and covers a lot of use cases and algorithms. Even if it had some vulnerabilities in the past, it has been audited and validated for business use. Some algorithms have been deeply optimized, with very tuned assembly code.

mORMot 2 can now access all OpenSSL 1.1.1 features, on all supported targets.
OpenSSL 1.1.1 is the latest stable branch, with LTS support, and nice new additions like TLS 1.3 and ed25519.

On Windows, you can use our internal assembly code for encryption, hashing and randomness (e.g. AES or SHA2/3), and we provide SChannel support since mORMot 1, which allows to create a TLS/HTTPS layer without shipping any external library - using OpenSSL .dll files can be a real PITA on Windows, since it is very likely that several versions do exist on your systems, even in your path! We just discovered and fixed some problems with SChannel, which seems now stable and working fine even on latest Windows revisions.

Of course, if you really need to use OpenSSL on Windows, you can, and our little mORMot will try to do its best to find the right .dll for you. Windows' SChannel is behind OpenSSL in terms of ciphers support, and also about performance - especially if your Windows version is old or not updated. But it is part of the system, so you can rely on it if you expect basic TLS support.

On Linux / BSD / POSIX, OpenSSL 1.1.1 is the reference cryptographic library. It is already installed on any recent system.
On MacOS and Android, you can use static linking to your application.

OpenSSL 1.1.1 in mORMot 2

Our OpenSSL integration in mORMot 2 has several key features:

  • Split into two units: mormot.lib.openssl11.pas for the raw API, and mormot.core.crypto.openssl.pas for integration with the other mORMot 2 features;
  • mormot.lib.openssl11.pas leverages OpenSSL 1.1.1, which is the latest stable version, and the current TLS branch - when OpenSSL 3 will be released, the API should remain compatible;
  • mormot.lib.openssl11.pas by default loads dynamically OpenSSL 1.1.1 libraries, so even if your system won't have OpenSSL installed, your application will still start, with explicit error messages when you would try to use OpenSSL features;
  • mormot.lib.openssl11.pas has a "full-API" conditional, which is used as reference when adding new features, and could be defined in your projects if you need specific OpenSSL features;
  • mormot.lib.openssl11.pas publishes a TLS layer, recognized by our mormot.net.sock.pas unit, just like SChannel on Windows, to enabled TLS/HTTPS client connections;
  • mormot.core.crypto.openssl.pas exposes the OpenSSL PRNG, which may be used for regulatory purposes, if our own PRNG doesn't meet your requirements;
  • mormot.core.crypto.openssl.pas exposes the main (and safest) AES ciphers, and Hashing/Signing offered by OpenSSL, in a mormot.core.crypto.pas compatible way;
  • mormot.core.crypto.openssl.pas defines some JWT classes, with full coverage of all the algorithms defined by the official JWT website (it is the only Delphi library defining all of them);
  • mormot.core.crypto.openssl.pas replaces the mormot.core.crypto.pas and mormot.core.ecc256r1.pas functions with the OpenSSL version, if they are faster;
  • We (re)validated our mormot.core.crypto.pas and mormot.core.ecc256r1.pas implementation against OpenSSL as reference, and also enhanced the test coverage (with more reference vectors for instance) for mORMot 2.

In a nutshell, OpenSSL is slower than our mormot.core.crypto.pas assembly on x86_64 for most AES process (ciphers and PRNG) - but AES-GCM. But OpenSSL is much faster than mormot.core.ecc256r1.pas for ECC public key cryptography, so is automatically selected, which is a good idea e.g. on a Linux server environment.

AES-PRNG, AES-CTR and AES-GCM

See this article for actual performance numbers of the AES encryption, and how our internal mormot.core.crypto.pas assembly is faster than OpenSSL in most cases.
Our x86_64 tuned assembly, with full AES-NI and PCLMUL support does wonders.

Public Key Cryptography and JWT

The mormot.core.crypto.openssl.pas unit covers all possible JWT algorithms, with very interesting results, as shown by our regression tests:

  mormot.core.crypto.pas algorithms:
     1000 HS256 in 2.16ms i.e. 461,041/s, aver. 2us
     1000 HS384 in 2.19ms i.e. 456,204/s, aver. 2us
     1000 HS512 in 2.18ms i.e. 457,456/s, aver. 2us
     1000 S3224 in 1.91ms i.e. 521,376/s, aver. 1us
     1000 S3256 in 1.89ms i.e. 526,870/s, aver. 1us
     1000 S3384 in 1.92ms i.e. 518,941/s, aver. 1us
     1000 S3512 in 1.94ms i.e. 513,874/s, aver. 1us
     1000 S3S128 in 1.92ms i.e. 520,833/s, aver. 1us
     1000 S3S256 in 1.94ms i.e. 513,610/s, aver. 1us
     100 ES256 in 14.65ms i.e. 6,823/s, aver. 146us
  mormot.core.crypto.openssl.pas algorithms:
     100 RS256 in 5.35ms i.e. 18,674/s, aver. 53us
     100 RS384 in 5.16ms i.e. 19,368/s, aver. 51us
     100 RS512 in 5.16ms i.e. 19,357/s, aver. 51us
     100 PS256 in 5.56ms i.e. 17,985/s, aver. 55us
     100 PS384 in 5.52ms i.e. 18,102/s, aver. 55us
     100 PS512 in 5.65ms i.e. 17,677/s, aver. 56us
     100 ES256 in 14.38ms i.e. 6,951/s, aver. 143us
     100 ES384 in 123.79ms i.e. 807/s, aver. 1.23ms
     100 ES512 in 94.50ms i.e. 1,058/s, aver. 945us
     100 ES256K in 63.09ms i.e. 1,584/s, aver. 630us
     100 EdDSA in 18.83ms i.e. 5,310/s, aver. 188us

The timing above are to verify a JWT signature.
We can see that the ES256 has very tuned assembly code, and that the other ECC algorithms (ES384/ES512/ES256K) use standard coding, which are much slower. EdDSA is the ed25519 public cryptography algorithm, which is efficient on non-Intel/AMD platforms too, e.g. on ARM.

Raw asymmetric / public key cryptography using OpenSSL is therefore consistent with the expectations:

     3 RSA 2048 Generation in 184.43ms i.e. 16/s, aver. 61.47ms
     30 RSA 2048 Sign in 48.66ms i.e. 616/s, aver. 1.62ms
     30 RSA 2048 Verify in 1.53ms i.e. 19,505/s, aver. 51us
     3 RSA-PSS 2048 Generation in 205.60ms i.e. 14/s, aver. 68.53ms
     30 RSA-PSS 2048 Sign in 47.32ms i.e. 633/s, aver. 1.57ms
     30 RSA-PSS 2048 Verify in 1.57ms i.e. 19,047/s, aver. 52us
     100 prime256v1 Generation in 8.26ms i.e. 12,096/s, aver. 82us
     100 prime256v1 Sign in 7.02ms i.e. 14,226/s, aver. 70us
     100 prime256v1 Verify in 13.45ms i.e. 7,433/s, aver. 134us
     100 ed25519 Generation in 7.33ms i.e. 13,642/s, aver. 73us
     100 ed25519 Sign in 13.67ms i.e. 7,310/s, aver. 136us
     100 ed25519 Verify in 18.44ms i.e. 5,422/s, aver. 184us

RSA generation is really slow in comparison to other options (but verification is fast). In practice, prime256v1 (ES256) is to be privileged - especially since we offer our own pure standalone version if OpenSSL is not available.
Note that any other algorithm exposed by OpenSSL are available - you are not restricted to the list above.

We introduced an easy way to generate public/private key pairs in a single function call, which is mandatory for any automated server work. No need to try the obfuscated openssl ecparam -name prime256v1 -genkey -noout -out key.pem && openssl ec -in key.pem -pubout -out public.pem command line switches: just call OpenSslGenerateKeys() or TJwtAbstractOsl.GenerateKeys and you are ready to go!

TLS Layer

We integrated the OpenSSL TLS layer to our mormot.net.sock.pas unit, with a powerful and easy way of using it:

with THttpClientSocket.Create do
try
  TLS.WithPeerInfo := true;
  TLS.IgnoreCertificateErrors := true;
  TLS.CipherList := 'ECDHE-RSA-AES256-GCM-SHA384';
  OpenBind('synopse.info', '443', {bind=}false, {tls=}true);
  writeln(TLS.PeerInfo);
  writeln(TLS.CipherName);
  writeln(Get('/forum/', 1000), ' len=', ContentLength);
  writeln(Get('/fossil/wiki/Synopse+OpenSource', 1000));
finally
  Free;
end;

The TCrtSocket.TLS new field can be used to set the private and public keys or root certificates, if needed. Its PeerInfo return the whole certificate of the server side, and CipherName returns the current cipher used, e.g. 'ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(128) Mac=AEAD'.

The very same code can be used with the SChannel version of mormot.net.sock.windows.inc - but with less features by now.

OpenSource ForEver

The source code of the units is available for review:

It works for FPC and Delphi, in the usual targets supported by mORMot 2.

Interfacing OpenSSL is really tricky sometimes, due to the opaque API names, lack of documentation, and weak/deprecated samples on the net.
We tried to leverage our integration, so that using OpenSSL would be seamless for the mORMot 2 users: you just use the mORMot classes, and don't have to care about which engine (mORMot's or OpenSSL's) is actually running them.

And feedback is welcome on our forum, as usual!