This document lists current limitations of the PSA Crypto API (as of version 1.1) that may impact our ability to (1) use it for all crypto operations in TLS and X.509 and (2) support isolation of all long-term secrets in TLS (that is, goals G1 and G2 in strategy.md in the same directory).
This is supposed to be a complete list, based on a exhaustive review of crypto operations done in TLS and X.509 code, but of course it's still possible that subtle-but-important issues have been missed. The only way to be really sure is, of course, to actually do the migration work.
There is currently no support for that in PSA at all. API design, as well as implementation, would be non-trivial.
Currently, MBEDTLS_USE_PSA_CRYPTO
is simply incompatible with MBEDTLS_ECP_RESTARTABLE
.
PSA Crypto has an API for FFDH, but it's not implemented in Mbed TLS yet. (Regarding FFDH, see the next section as well.) See issue 3261 on github.
PSA Crypto has an experimental API for EC J-PAKE, but it's not implemented in Mbed TLS yet. See the EC J-PAKE follow-up EPIC on github.
(See also the first paragraph in the previous section.)
Currently, the PSA Crypto API can only perform FFDH with a limited set of well-known parameters (some of them defined in the spec, but implementations are free to extend that set).
TLS 1.2 (and earlier) on the other hand have the server send explicit parameters (P and G) in its ServerKeyExchange message. This has been found to be suboptimal for security, as it is prohibitively hard for the client to verify the strength of these parameters. This led to the development of RFC 7919 which allows use of named groups in TLS 1.2 - however as this is only an extension, servers can still send custom parameters if they don't support the extension.
In TLS 1.3 the situation will be simpler: named groups are the only option, so the current PSA Crypto API is a good match for that. (Not coincidentally, all the groups used by RFC 7919 and TLS 1.3 are included in the PSA specification.)
There are several options here:
RSA-PSS signatures are defined by PKCS#1 v2, re-published as RFC 8017 (previously RFC 3447).
As standardized, the signature scheme takes several parameters, in addition to the hash algorithm potentially used to hash the message being signed:
Both the existing mbedtls_
API and the PSA API support only MGF1 as the generation function (and only 0xBC as the trailer field), but there are discrepancies in handling the salt length and which of the various hash algorithms can differ from each other.
mbedtls_rsa_rsassa_pss_sign()
mbedtls_rsa_rsassa_pss_sign_ext()
mbedtls_rsassa_pss_verify()
mbedtls_rsassa_pss_verify_ext()
mbedtls_pk_verify_ext()
Note: above, “<= hashlen” means that hashlen is used if possible, but if it doesn't fit because the key is too short, then the maximum length that fits is used.
The RSA/PK API is in principle more flexible than the PSA Crypto API. The following sub-sections study whether and how this matters in practice.
RFC 4055 Section 3.1 defines the encoding of RSA-PSS that‘s used in X.509. It allows independently specifying the message hash (also used for encoding hash), the MGF (and its hash if MGF1 is used), and the salt length (plus an extra parameter “trailer field” that doesn’t vary in practice"). These can be encoded as part of the key, and of the signature. If both encoding are presents, all values must match except possibly for the salt length, where the value from the signature parameters is used.
In Mbed TLS, RSA-PSS parameters can be parsed and displayed for various objects (certificates, CRLs, CSRs). During parsing, the following properties are enforced:
When it comes to cryptographic operations, only two things are supported:
The verification is done using mbedtls_pk_verify_ext()
.
Note: since X.509 parsing ensures that message hash = encoding hash, and mbedtls_pk_verify_ext()
uses encoding hash = mgf1 hash, it looks like all three hash algorithms must be equal, which would be good news as it would match a limitation of the PSA API.
It is unclear what parameters people use in practice. It looks like by default OpenSSL picks saltlen = keylen - hashlen - 2 (tested with openssl 1.1.1f). The certool
command provided by GnuTLS seems to be picking saltlen = hashlen by default (tested with GnuTLS 3.6.13). FIPS 186-4 requires 0 <= saltlen <= hashlen.
In TLS 1.2 (or lower), RSA-PSS signatures are never used, except via X.509.
In TLS 1.3, RSA-PSS signatures can be used directly in the protocol (in addition to indirect use via X.509). It has two sets of three signature algorithm identifiers (for SHA-256, SHA-384 and SHA-512), depending of what the OID of the public key is (rsaEncryption or RSASSA-PSS).
In both cases, it specifies that:
When signing, the salt length picked by PSA is the one required by TLS 1.3 (unless the key is unreasonably small).
When verifying signatures, PSA will by default enforce the salt len is the one required by TLS 1.3.
All test files use the default trailer field of 0xBC, as enforced by our parser. (There's a negative test for that using the x509_parse_rsassa_pss_params
test function and hex data.)
Files with “bad” in the name are expected to be invalid and rejected in tests.
Test certificates:
server9-bad-mgfhash.crt (announcing mgf1(sha224), signed with another mgf) Hash Algorithm: sha256 Mask Algorithm: mgf1 with sha224 Salt Length: 0xDE server9-bad-saltlen.crt (announcing saltlen = 0xDE, signed with another len) Hash Algorithm: sha256 Mask Algorithm: mgf1 with sha256 Salt Length: 0xDE server9-badsign.crt (one bit flipped in the signature) Hash Algorithm: sha1 (default) Mask Algorithm: mgf1 with sha1 (default) Salt Length: 0xEA server9-defaults.crt Hash Algorithm: sha1 (default) Mask Algorithm: mgf1 with sha1 (default) Salt Length: 0x14 (default) server9-sha224.crt Hash Algorithm: sha224 Mask Algorithm: mgf1 with sha224 Salt Length: 0xE2 server9-sha256.crt Hash Algorithm: sha256 Mask Algorithm: mgf1 with sha256 Salt Length: 0xDE server9-sha384.crt Hash Algorithm: sha384 Mask Algorithm: mgf1 with sha384 Salt Length: 0xCE server9-sha512.crt Hash Algorithm: sha512 Mask Algorithm: mgf1 with sha512 Salt Length: 0xBE server9-with-ca.crt Hash Algorithm: sha1 (default) Mask Algorithm: mgf1 with sha1 (default) Salt Length: 0xEA server9.crt Hash Algorithm: sha1 (default) Mask Algorithm: mgf1 with sha1 (default) Salt Length: 0xEA
These certificates are signed with a 2048-bit key. It appears that they are all using saltlen = keylen - hashlen - 2, except for server9-defaults which is using saltlen = hashlen.
Test CRLs:
crl-rsa-pss-sha1-badsign.pem Hash Algorithm: sha1 (default) Mask Algorithm: mgf1 with sha1 (default) Salt Length: 0xEA crl-rsa-pss-sha1.pem Hash Algorithm: sha1 (default) Mask Algorithm: mgf1 with sha1 (default) Salt Length: 0xEA crl-rsa-pss-sha224.pem Hash Algorithm: sha224 Mask Algorithm: mgf1 with sha224 Salt Length: 0xE2 crl-rsa-pss-sha256.pem Hash Algorithm: sha256 Mask Algorithm: mgf1 with sha256 Salt Length: 0xDE crl-rsa-pss-sha384.pem Hash Algorithm: sha384 Mask Algorithm: mgf1 with sha384 Salt Length: 0xCE crl-rsa-pss-sha512.pem Hash Algorithm: sha512 Mask Algorithm: mgf1 with sha512 Salt Length: 0xBE
These CRLs are signed with a 2048-bit key. It appears that they are all using saltlen = keylen - hashlen - 2.
Test CSRs:
server9.req.sha1 Hash Algorithm: sha1 (default) Mask Algorithm: mgf1 with sha1 (default) Salt Length: 0x6A server9.req.sha224 Hash Algorithm: sha224 Mask Algorithm: mgf1 with sha224 Salt Length: 0x62 server9.req.sha256 Hash Algorithm: sha256 Mask Algorithm: mgf1 with sha256 Salt Length: 0x5E server9.req.sha384 Hash Algorithm: sha384 Mask Algorithm: mgf1 with sha384 Salt Length: 0x4E server9.req.sha512 Hash Algorithm: sha512 Mask Algorithm: mgf1 with sha512 Salt Length: 0x3E
These CSRss are signed with a 2048-bit key. It appears that they are all using saltlen = keylen - hashlen - 2.
There's no question about what to do with TLS (any version); the only question is about X.509 signature verification. Options include:
PSA_ALG_RSA_PSS_ANY_SALT
- while this wouldn't cause a concrete security issue, this would be non-compliant.PSA_ALG_RSA_PSS
when we're lucky and the encoded saltlen happens to match hashlen, and falling back to ANY_SALT
otherwise. Same issue as with the previous point, except more contained.The HKDF function uses an Extract-then-Expand approach, that is:
HKDF(x, ...) = HKDF-Expand(HKDF-Extract(x, ...), ...)
Only the full HKDF function is safe in general, however there are cases when one case safely use the individual Extract and Expand; the TLS 1.3 key schedule does so. Specifically, looking at the hierarchy of secrets is seems that Expand and Extract are always chained, so that this hierarchy can be implemented using only the full HKDF. However, looking at the derivation of traffic keys (7.3) and the update mechanism (7.2) it appears that calls to HKDF-Expand are iterated without any intermediated call to HKDF-Extract : that is, the traffic keys are computed as
HKDF-Expand(HKDF-Expand(HKDF-Extract(...)))
(with possibly more than two Expands in a row with update).
In the short term (early 2022), we'll work around that by re-implementing HKDF in ssl_tls13_keys.c
based on the psa_mac_
APIs (for HMAC).
In the long term, it is desirable to extend the PSA API. See https://github.com/ARM-software/psa-crypto-api/issues/539
Currently, MBEDTLS_USE_PSA_CRYPTO
enables the new configuration function mbedtls_ssl_conf_psk_opaque()
which allows a PSA-held key to be used for the (pure) PSK
key exchange in TLS 1.2. This requires that the derivation of the Master Secret (MS) be done on the PSA side. To support this, an algorithm family PSA_ALG_TLS12_PSK_TO_MS(hash_alg)
was added to PSA Crypto.
If we want to support key isolation for the “mixed PSK” key exchanges: DHE-PSK, RSA-PSK, ECDHE-PSK, where the PSK is concatenated with the result of a DH key agreement (resp. RSA decryption) to form the pre-master secret (PMS) from which the MS is derived. If the value of the PSK is to remain hidden, we need the derivation PSK + secondary secret -> MS to be implemented as an ad-hoc PSA key derivation algorithm.
Adding this new, TLS-specific, key derivation algorithm to PSA Crypto should be no harder than it was to add PSA_ALG_TLS12_PSK_TO_MS()
but still requires an extension to PSA Crypto.
Note: looking at RFCs 4279 and 5489, it appears that the structure of the PMS is always the same: 2-byte length of the secondary secret, secondary secret, 2-byte length of the PSK, PSK. So, a single key derivation algorithm should be able to cover the 3 key exchanges DHE-PSK, RSA-PSK and ECDHE-PSK. (That's a minor gain: adding 3 algorithms would not be a blocker anyway.)
Note: if later we want to also isolate short-term secret (G3), the “secondary secret” (output of DHE/ECDHE key agreement or RSA decryption) could be a candidate. This wouldn't be a problem as the PSA key derivation API always allows inputs from key slots. (Tangent: the hard part in isolating the result of RSA decryption would be still checking that is has the correct format: 48 bytes, the first two matching the TLS version - note that this is timing sensitive.)
See the section with the same name in the G1 part above for background.
The work-around mentioned there works well enough just for acceleration, but is not sufficient for key isolation or generally proper key management (it requires marking keys are usable for HMAC while they should only be used for key derivation).
The obvious long-term solution is to make HKDF-Expand available as a new KDF (in addition to the full HKDF) in PSA (with appropriate warnings in the documentation).