Monday, August 21, 2017

An overview of GnuTLS 3.6.0

The new 3.6.0 GnuTLS release contains several new features, back-end changes and clean ups. This is a release which re-spins the so-called 'stable-next' branch, meaning that once considered stable enough, this branch will replace the current stable branch. The main target of this release was to have a library ready to incorporate new protocol additions such  as TLS 1.3, which is currently in draft specification and is expected to be finalized in the coming months.  That "preparation", spans from introducing new functionality needed for the new protocol features, improving the testing and fuzzying infrastructure of the library to reduce regressions and non-standards compliant behavior, to the removal of features and components which are no longer relevant in today's Internet Public Key Infrastructure.

In short, this release introduces a new lock-free random generator and adds new TLS extensions shared by both TLS 1.2 and 1.3, such as Finite Field Diffie Hellman negotiation, Ed25519 and RSA-PSS signatures. These additions modernize the current TLS 1.2 support and pave the way for TLS 1.3 support in the library. Furthermore, tlsfuzzer is introduced in our continuous integration test suite. Tlsfuzzer, is a meticulous TLS test suite, which tests the behavior of the implementation on various corner (and not) cases, and acts complementary to the internal GnuTLS test suite and its unit testing. This release, also eliminates a lot of legacy code, in order to reduce complexity and improve the manageability of the library, preventing legacy code to be used as a potential attack vector.

Further changes to support TLS 1.3 will be included on this release branch.

The following paragraphs go through the most significant changes of the 3.6.0 release.

Testing and Fuzzying

Fuzzying infrastructure

Fuzzying in the sense of trying arbitrary input to the library and testing its behavior under invalid and valid but rare inputs is not something new. However, in GnuTLS previously, fuzzying of various components of the library was done in a non-systematic way, usually by 3rd parties who then reported any issues found. That as you can imagine is an unreliable process. Without common fuzzying infrastructure, there is no fuzzying code or infrastructure re-use, forcing each and every person attempting to fuzz GnuTLS functionality to re-invent the wheel. Driven by the availability of Google's OSS-Fuzz project, and with the contributions of Alex Gaynor, Tim Ruehsen and yours truly, there is now a common fuzzying base testing several aspects of the library. That fuzzying test suite is run automatically under OSS-Fuzz, uncovering issues, and filling bugs against the library (and that's the part where automation stops).


TLS fuzzer is TLS server implementation testing tool used at Red Hat for testing the quality of TLS implementations. It checks the behavior of an implementation on various corner (and not) cases, providing a tool not only for testing correctness of an implementation but for ensuring that no behavioral regressions are introduced undetected. With this release GnuTLS incorporates this testing tool on its continuous integration infrastructure, ensuring behavioral stability and thorough testing of existing and new functionality. Since the library is now modified for TLS 1.3, tlsfuzzer's use is invaluable as it allows detecting major or minor behavioral changes in the old protocol support, early.

CII best practices

The Core Infrastructure Initiative (CII) of Linux Foundation provides a best practices badge, as a way for Free software to demonstrate they follow best software engineering practices. The practices include change control, quality, static analysis, security, even bug reporting. Although several of these practices were already in use in the project, the process of going through that manual inspection of processes, uncovered several weaknesses and omissions which are now resolved. Taking the time to go through the manual inspection was the most difficult part, as there is always something more important to address; however, I believe that the time spent on it was worthwhile. My impression is that there has been quality work behind the formulation of these practices, and I'd recommend any free software project to seriously consider following them. You can view the result of GnuTLS' inspection here.


Random Number Generation

A new lock-free random generator

Versions of GnuTLS 3.3 and later rely on two random generators. The default is based on a combination of Salsa20/12 stream cipher for nonces, and Yarrow/AES for everything else.  The other generator is the AES-CTR-DRBG, which is an AES-based deterministric random bit generator and is used optionally when the library is compiled with FIPS140-2 support and the system is in FIPS140-2 mode. Both of these generators operate under a global lock, making them a performance bottleneck for multi-threaded applications. Having such bottleneck in single-CPU systems or even 2-4 CPU systems in the past may have been an acceptable cost; today however as the number of CPUs in a single system increase past these numbers, such global locks severely harm performance. To address that, in GnuTLS 3.6.0 the random generator component was essentially re-written to address the bottleneck issue, simplify entropy gathering, as well as fix other issues found over the years. The end result, is a separate random generator per-thread, while the default generator is now based on the stream cipher CHACHA. The optional generator AES-CTR-DRBG remains the same. I'll not go further in the design of the new random generator, though you can find a detailed description of the changes on this post.


TLS Features 

Finite Field Diffie-Hellman parameter negotiation (RFC7919)

If you have setup any TLS server, or have developed an application which uses TLS, most likely you would have seen references to Diffie-Hellman parameter files and their generation. In the early versions of GnuTLS I recommended generating them daily or weekly, provided certtool options to generate them by following specific security levels, and so on. The truth is that there were no available best practices that protocol designers, or implementers could refer to on how these parameters should be used correctly. As it always happens in these cases the burden is pushed to the application writers, and I guess the application writers push that further to the application users. Fortunately, with the publication of RFC7919, the DH parameter handling becomes the responsibility of the TLS protocol itself, and they are now negotiated without any input from the application (maybe except the desired security parameter/level). GnuTLS 3.6.0 implements that feature removing the need for server applications to specify Diffie-Hellman parameters, but in a backwards compatible way. Applications which already specify explicitly the DH parameters, will still function by overriding that negotiation.

In practice this feature introduces the notion of groups, that replace the previous notion of curves. Applications which were setting support for explicit curves via priority strings like "NORMAL:+CURVE-X25519", could now use "NORMAL:+GROUP-X25519" with identical functionality. The groups act as a superset of the curves, and contain the finite field groups for Diffie-Hellman, such as GROUP-FFDHE2048, GROUP-FFDHE3072, etc. 

Digital signatures with Ed25519

Although curve x25519 was already supported for TLS ephemeral key exchange, there was no way to utilize certificates and private keys with the Ed25519 signature algorithm. This is now supported both for certificate signing and verification, as well as for TLS key exchange (following draft-ietf-tls-rfc4492bis-17). In contrast with RSA or even ECDSA, these keys offer an impressive performance, and are notoriously small, even in their PKCS#8 container. That is easily demonstrated with the certtool output below. The key consists of three lines, two of which are the PEM boilerplate. 
certtool --generate-privkey --key-type ed25519

Given the expected switch to post-quantum resistant algorithms in the not-so-far away future, that may be the last chance to utilize algorithms with such a small key size.

Digital signatures with RSA-PSS

That was one the change that required by far the largest amount of code changes in GnuTLS 3.6.0; it required changes both in GnuTLS and nettle, so I'll dedicate few more lines to it. The feature was contributed mainly by Daiki Ueno, and was further extended by me. If you are not aware of the spicy details of cryptographic protocols today, RSA signing today is universally being used with a construction called PKCS#1 v1.5, as a tribute to the document that it was described at. Even though no attacks are known for the PKCS#1 v1.5 signing algorithm, the very similar PKCS#1 RSA decryption construction was successfully attacked in 1996 by Bleichenbacher, generating doubt on its cryptographic properties.

In order to prevent similar issues in the future, another RSA signing construction was defined in a later revision (v2) of the PKCS#1 document, named RSASSA-PSS (referred to as RSA-PSS from now on). That method, involves hash functions, and a salt, in order to provide a primitive with a security proof. The proof guarantees that the longer the salt, the stronger the security properties, with stronger being, unfortunately, undefined in any tangible terms. The best current practice followed by the PKCS#1 2.2 document is to tie the salt size with the size of the hash function involved, and thus associate directly the key security parameter with the hash function used in RSA-PSS signatures.

As mentioned above, RSA-PSS introduces optional parameters to a key or certificate. The parameters help the operator of the key (e.g., the software) sign using the desired security level. These parameters include the following information.

   RSASSA-PSS-params ::= SEQUENCE {
       hashAlgorithm      [0] HashAlgorithm,
       maskGenAlgorithm   [1] MaskGenAlgorithm,
       saltLength         [2] INTEGER,
       trailerField       [3] TrailerField
That is, a key is associated with two different hashes (hashAlgorithm and maskGenAlgorithm), a variable salt size, and an unused for Internet PKI trailerField. To simplify generation and usage of such keys, GnuTLS 3.6.0 generates keys by default with no parameters, that is, keys that can be used with any hash or salt size (except SHA1 which is intentionally not supported). That form of keys is most suitable for servers which typically sign using any algorithm supported by the connected client. For CA keys and keys which require a consistent security level to be used, these parameters can be set, though GnuTLS will require the hash algorithms in hashAlgorithm and maskGenAlgorithm to match. Keys with non-matching algorithms, e.g., a key using SHA256 for hashAlgorithm and SHA512 for maskGenAlgorithm, are rejected as invalid.

To generate an RSA key for use only with RSA-PSS signatures, use the following command.
certtool --generate-privkey --key-type rsa-pss

To generate a key for RSA-PSS with a specific hash algorithm (the salt size will be obtained from it), use the following command:
certtool --generate-privkey --key-type rsa-pss --hash sha384

Note however, that very few applications accept keys intended only for RSA-PSS signatures. A more compatible approach is to generate an RSA key (which works for any purpose), and utilize that one to sign with the RSA-PSS signature algorithm. When that key is used in the context of TLS, it will be used for both RSA-PSS and plain PKCS#1 v1.5 signatures. As a cryptographer would tell you, that usage invalidates the RSA-PSS security proof, and underlines the need to utilize separate keys for the different algorithms.

As such, it is possible with GnuTLS to use separate keys for RSA PKCS#1 v1.5, and RSA-PSS, in order to reduce any risk due to the interaction between these two algorithms. When a GnuTLS server is provided with two keys, RSA and RSA-PSS, the latter will be used for RSA-PSS operations, and the former for the legacy PKCS#1 v1.5 operations.

Removed/Disabled functionality

3DES cipher is no longer enabled by default for TLS

Although the 3DES cipher is the mandatory option for TLS 1.0 and TLS 1.1, the cipher is unfortunately a relic of a different era. It is a 64-bit block cipher, which limits the amount of data ut can safely operate on, it is based on cipher with a 56-bit key size, and operates in encryption-decryption-encryption (EDE) mode to overcome the previous limitation. As such, that cipher provides a performance unacceptable for today and is only being used to interoperate with legacy hardware and software. As such, this cipher will no longer be enabled by default, but applications requiring should provide the end-user the necessary knobs to enable it (e.g., a priority string which includes "+3DES-CBC").

SHA1 is no longer acceptable for certificate signing

SHA1 used to be the de facto algorithm in X.509 certificates, or any other digital signature standards. Given the collision attacks on SHA1 and the fact that it has been phased out from the public web, GnuTLS will not accept SHA1 signatures on certificates as trusted by default. SHA1 will remain acceptable for other types of signatures as it is still widely used. Note, however, that the existing collision attacks do not translate directly to an attack on digital signatures with SHA1. The removal is a precaution and preparation for its complete phasing out. The reason is, that even though direct attacks are not applicable on SHA1-based digital signatures, the experience with the attacks on MD5 the previous decade, shows that there can be clever ways to take advantage of collision attacks in order to forge certificates.

OpenPGP functionality was removed

When I originally started working on GnuTLS I was envisioning a future where OpenPGP certificates will be used instead of X.509. My driver was the perceived simplicity of OpenPGP certificate format, and the fact that obtaining a certificate at the time required the intervention of costly CA, in contrast with OpenPGP where one had to generate a key and manage its web of trust. That never took off as a deployment nor idea, and today none of the original driving factors are valid. OpenPGP certificates are not significantly simpler to parse than X.509, the web-of-trust proved to be a more difficult problem than Internet PKI, and the costly CAs verification issue is no longer relevant after

IDNA2003 is no longer supported

IETF has switched to IDNA2008 for internationalized domain names since long time and as such GnuTLS will no longer provide compatibility code for the older standard. Internationalized domain names may not be widely known in the english speaking world, however, their use varies around the world. Hence, supporting them is necessary in order to be able to properly handle of PKIX (X.509) certificates and verification, with internationalized domain names. See my previous post for a more detailed description of IDNA today.

TLS compression functionality was removed

Compression prior to encryption was always considered a good thing, not because it eliminates correlations in plaintext due to language or file format in use, but also because it reduces the size of the transmitted data, and the latter is a significant performance benefit in restricted by bandwidth lines. Why did we remove it then? The reason is that after compression the ciphertext length, which in TLS 1.2 is in clear, may reveal more information about the data, and that, becomes a confidentiality breach issue when data are partially under the control of the attacker. This property has been exploited in attacks like the fancy-named CRIME attack.

Given the above, the currently held belief in protocol design is to delegate compression to application protocols, e.g., TLS 1.3 will not include support for compression, and for that we believe that there are more benefits in removing that feature completely, reducing the attack surface of the library, rather than keeping it as a legacy feature.

Concluding remarks

I'd like to sincerely thank everyone who has contributed for the GnuTLS 3.6.0 release to be possible. The git shortlog follows; happy hacking!

Alex Gaynor (12):
      Migrated fuzzers from the oss-repo to here.
      Added a server fuzzer
      Move to the devel dir
      Describe the integration
      Added a parser for PKCS7 importing and printing
      Added a fuzzer for OpenPGP cert parsing
      Do not infinite loop if an EOF occurs while skipping a PGP packet
      Attempt to fix a leak in OpenPGP cert parsing.
      Corrected a leak in OpenPGP sub-packet parsing.
      Enforce the max packet length for OpenPGP subpackets as well
      Do not attempt to parse a 32-bit integer if a packet is not 4 bytes.
      Do not attempt to parse a 32-bit integer if a packet is not 4 bytes.

Alexander Kanavin (1):
      Do not add cli-args.h to cli-args.stamp Makefile target

Alon Bar-Lev (19):
      tests: suite: pkcs11: skip if no softhsm
      tests: cert-tests: pkcs12 drop builddir usage
      tests: skip tests that requires tools if tools are disabled
      gitignore: sort()
      gitignore: update [ci skip]
      tests: skip tests that requires tools if tools are disabled
      tests: suite: chain: support separate builddir
      tests: remove bash usage
      tests: skip tests that requires tools if tools are disabled
      configure: remove void statement
      valgrind: support separate builddir for suppressions.valgrind
      .gitlab-ci.yml: add Fedora/x86_64/no-tools
      build: doc: install images also into htmldir
      tests: scripts: suppress which errors
      tests: remove unused suppressions.valgrind
      tests: suppressions.valgrind: supress fillin_rpath
      tests: cert-tests: openpgp-certs: align test redirection
      build: tests: resolve as-needed issue with seccomp
      build: disable valgrind tests by default

Andreas Metzler (3):
      Use NORMAL priority for SSLv23_*_method.
      gnutls-cli: Use CRLF with --starttls-proto=smtp.
      Fix autoconf progress message concerning heartbeat [ci skip]

Daiki Ueno (3):
      build: import files from Nettle for RSA-PSS
      x509: implement RSA-PSS signature scheme
      nettle: ported fix for assertion failure in pss_verify_mgf1

Daniel Kahn Gillmor (1):
      clarify documentation and arguments for psktool

David Caldwell (2):
      Rename uint64 to gnutls_uint64 to avoid conflict with macOS
      gnutls_x509_trust_list_add_system_trust: Add macOS keychain support

Dmitry Eremin-Solenikov (13): remove autogen'erated files only if necessary
      Add special MD5+SHA1 digest to simplify TLS signature code
      Rewrite SSL/TLS signing code to use combined MD5+SHA1 digest
      Rewrite SSL/TLS signature verification to use combined MD5+SHA1 digest
      Use MAC_MD5_SHA1 instead of MAC_UNKNOWN to specify TLS 1.0 PRF
      Cache MAC algorithm used for PRF function
      Rework setting next cipher suite
      Rework setting next compression method
      Drop _gnutls_epoch_get_compression
      Don't let GnuTLS headers in NETTLE_CFLAGS override local headers
      Fix two memory leaks in debug output of gnutls tools
      gnutls-serv: allow user to specify multiple x509certile/x509keyfile
      Rework KX -> PK mappings

Karl Tarbe (2):
      certtool: allow multiple certificates in --p7-sign
      tests: add test for signing with certificate list

Marcin Cieślak (1):
       only if HAVE_ALLOCA_H

Martin Storsjo (2):
      Fix a typo in a variable name in an m4 script
      Avoid deprecation warnings when including gnutls/abstract.h

Matt Turner (1):
      tests: Copy template out of ${srcdir}

Nicolas Dufresne (1):
      rsa-psk: Use the correct username datum

Nikos Mavrogiannopoulos (1148):

Rical Jasan (1):
      tests: Improve port-checking infrastructure.

Robert Scheck (1):
      Add LMTP, POP3, NNTP, Sieve and PostgreSQL support to gnutls-cli

Tim Rühsen (11):
      Add support for libidn2 (IDNA 2008 + TR46)
      lib/system/fastopen: Add TCP Fast Open for OSX
      Fix memleak in gnutls_x509_crl_list_import()
      Fix memleaks in gnutls_x509_trust_list_add_crls()
      fuzzer: Initial check in for improved fuzzing
      fuzzer: Suppress unsigned integer overflow in rnd-fuzzer.c
      fuzzer: Suppress leak in libgmp <= 6.1.2
      fuzzer: Move regression corpora from tests/ to fuzz/
      fuzzer: Add 'make -C fuzz coverage' [ci skip]
      fuzzer: Fix include path in [skip ci]
      fuzzer: Update base64 fuzzers + corpora