diff --git a/docs/architecture/psa-migration/psa-limitations.md b/docs/architecture/psa-migration/psa-limitations.md new file mode 100644 index 000000000000..c60eddc6fbf4 --- /dev/null +++ b/docs/architecture/psa-migration/psa-limitations.md @@ -0,0 +1,393 @@ +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](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. + +Limitations relevant for G1 (performing crypto operations) +========================================================== + +Restartable ECC operations +-------------------------- + +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`. + +Things that are in the API but not implemented yet +-------------------------------------------------- + +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][ffdh] on +github. + +[ffdh]: https://github.com/ARMmbed/mbedtls/issues/3261 + +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][ecjp] on github. + +[ecjp]: https://github.com/orgs/ARMmbed/projects/18#column-15836385 + +Arbitrary parameters for FFDH +----------------------------- + +(See also the first paragraph in the previous section.) + +Currently, the PSA Crypto API can only perform FFDH with a limited set of +well-know 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 is 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, the groups used by RFC 7919 and TLS 1.3 are part those defined +in the specification.) + +There are several options here: + +1. Implement support for custom FFDH parameters in PSA Crypto: this would pose + non-trivial API design problem, but most importantly seems backwards, as +the crypto community is moving away from custom FFDH parameters. +2. Drop the DHE-RSA and DHE-PSK key exchanges in TLS 1.2 when moving to PSA. +3. Implement RFC 7919, support DHE-RSA and DHE-PSK only in conjunction with it + when moving to PSA. We can modify our server so that it only selects a DHE + ciphersuite if the client offered name FFDH groups; unfortunately +client-side the only option is to offer named groups and break the handshake +if the server didn't take on our offer. This is not fully satisfying, but is +perhaps the least unsatisfying option in terms of result; it's also probably +the one that requires the most work, but it would deliver value beyond PSA +migration by implementing RFC 7919. + +RSA-PSS parameters +------------------ + +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: +- a hash algorithm used for the encoding function +- a mask generation function + - most commonly MGF1, which in turn is parametrized by a hash algorithm +- a salt length +- a trailer field - this is universally 0xBC as far as I've seen + +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. + +### API comparison + +- RSA: + - signature: `mbedtls_rsa_rsassa_pss_sign()` + - message hashed externally + - encoding hash = MGF1 hash (from context, or argument = message hash) + - salt length: always using the maximum legal value + - signature: `mbedtls_rsa_rsassa_pss_sign_ext()` + - message hashed externally + - encoding hash = MGF1 hash (from context, or argument = message hash) + - salt length: specified explicitly + - verification: `mbedtls_rsassa_pss_verify()` + - message hashed externally + - encoding hash = MGF1 hash (from context, or argument = message hash) + - salt length: any valid length accepted + - verification: `mbedtls_rsassa_pss_verify_ext()` + - message hashed externally + - encoding hash = MGF1 hash from dedicated argument + - expected salt length: specified explicitly, can specify "ANY" +- PK: + - signature: not supported + - verification: `mbedtls_pk_verify_ext()` + - message hashed externally + - encoding hash = MGF1 hash, specified explicitly + - expected salt length: specified explicitly, can specify "ANY" +- PSA: + - algorithm specification: + - hash alg used for message hashing, encoding and MGF1 + - salt length can be either "standard" (<= hashlen, see note) or "any" + - signature generation: + - salt length: always <= hashlen (see note) and random salt + - verification: + - salt length: either <= hashlen (see note), or any depending on algorithm + +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. + +### Use in X.509 + +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: +- the extra "trailer field" parameter must have its default value +- the mask generation function is MGF1 +- encoding hash = message hashing algorithm (may differ from MGF1 hash) + +When it comes to cryptographic operations, only two things are supported: +- verifying the signature on a certificate from its parent; +- verifying the signature on a CRL from the issuing CA. + +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()` use 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. + +### Use in TLS + +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: +- the mask generation function is MGF1 +- all three hashes are equal +- the length of the salt MUST be equal to the length of the digest algorithm + +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. + +### Current testing - X509 + +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. + +### Possible courses of action + +There's no question about what to do with TLS (any version); the only question +is about X.509 signature verification. Options include: + +1. Doing all verifications with `PSA_ALG_RSA_PSS_ANY_SALT` - while this + wouldn't cause a concrete security issue, this would be non-compliant. +2. Doing verifications with `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. +3. Reject all certificates with saltlen != hashlen. This includes all + certificates generate with OpenSSL using the default parameters, so it's +probably not acceptable. +4. Request an extension to the PSA Crypto API and use one of the above options + in the meantime. Such an extension seems inconvenient and not motivated by +strong security arguments, so it's unclear whether it would be accepted. + +HKDF: Expand not exposed on its own (TLS 1.3) +--------------------------------------------- + +The HKDF function uses and Extract-then-Expand approch, 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][13hs] +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). + +[13hs]: https://datatracker.ietf.org/doc/html/rfc8446#page-93 + +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 + +Limitations relevant for G2 (isolation of long-term secrets) +============================================================ + +Custom key derivations for mixed-PSK handshake +---------------------------------------------- + +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.) + +HKDF: Expand not exposed on its own (TLS 1.3) +--------------------------------------------- + +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). diff --git a/docs/architecture/psa-migration/strategy.md b/docs/architecture/psa-migration/strategy.md new file mode 100644 index 000000000000..205c6cd2c133 --- /dev/null +++ b/docs/architecture/psa-migration/strategy.md @@ -0,0 +1,377 @@ +This document explains the strategy that was used so far in starting the +migration to PSA Crypto and mentions future perspectives and open questions. + +Goals +===== + +Several benefits are expected from migrating to PSA Crypto: + +G1. Use PSA Crypto drivers when available. +G2. Allow isolation of long-term secrets (for example, private keys). +G3. Allow isolation of short-term secrets (for example, TLS session keys). +G4. Have a clean, unified API for Crypto (retire the legacy API). +G5. Code size: compile out our implementation when a driver is available. + +Currently, some parts of (G1) and (G2) are implemented when +`MBEDTLS_USE_PSA_CRYPTO` is enabled. For (G2) to take effect, the application +needs to be changed to use new APIs. + +Generally speaking, the numbering above doesn't mean that each goal requires +the preceding ones to be completed, for example G2-G5 could be done in any +order; however they all either depend on G1 or are just much more convenient +if G1 is done before (note that this is not a dependency on G1 being complete, +it's more like each bit of G2-G5 is helped by some specific bit in G1). + +So, a solid intermediate goal would be to complete (G1) when +`MBEDTLS_USA_PSA_CRYPTO` is enabled - that is, all crypto operations in X.509 +and TLS would be done via the PSA Crypto API. + +Compile-time options +==================== + +We currently have two compile-time options that are relevant to the migration: + +- `MBEDTLS_PSA_CRYPTO_C` - enabled by default, controls the presence of the PSA + Crypto APIs. +- `MBEDTLS_USE_PSA_CRYPTO` - disabled by default (enabled in "full" config), + controls usage of PSA Crypto APIs to perform operations in X.509 and TLS +(G1 above), as well as the availability of some new APIs (G2 above). + +The reasons why `MBEDTLS_USE_PSA_CRYPTO` is optional and disabled by default +are: +- it's incompatible with `MBEDTLS_ECP_RESTARTABLE`; +- historical: used to be incompatible + `MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER` (fixed early 2022, see + ); +- it does not work well with `MBEDTLS_PSA_CRYPTO_CONFIG` (could compile with + both of them, but then `MBEDTLS_PSA_CRYPTO_CONFIG` won't have the desired +effect) +- to avoid a hard/default dependency of TLS, X.509 and PK on + `MBEDTLS_PSA_CRYPTO_C`, for backward compatibility reasons: + - when `MBEDTLS_PSA_CRYPTO_C` is enabled and used, applications need to call + `psa_crypto_init()` before TLS/X.509 uses PSA functions + - `MBEDTLS_PSA_CRYPTO_C` has a hard depend on `MBEDTLS_ENTROPY_C || + MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG` but it's + currently possible to compilte TLS and X.509 without any of the options. + Also, we can't just auto-enable `MBEDTLS_ENTROPY_C` as it doesn't build + out of the box on all platforms, and even less + `MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG` as it requires a user-provided RNG + function. + +The downside of this approach is that until we feel ready to make +`MBDEDTLS_USE_PSA_CRYPTO` non-optional (always enabled), we have to maintain +two versions of some parts of the code: one using PSA, the other using the +legacy APIs. However, see next section for strategies that can lower that +cost. The rest of this section explains the reasons for the +incompatibilities mentioned above. + +In the medium term (writing this in early 2020), we're going to look for ways +to make `MBEDTLS_USE_PSA_CRYPTO` non-optional (always enabled). + +### `MBEDTLS_ECP_RESTARTABLE` + +Currently this option controls not only the presence of restartable APIs in +the crypto library, but also their use in the TLS and X.509 layers. Since PSA +Crypto does not support restartable operations, there's a clear conflict: the +TLS and X.509 layers can't both use only PSA APIs and get restartable +behaviour. + +Supporting this in PSA is on our roadmap (it's been requested). But it's way +below generalizing support for `MBEDTLS_USE_PSA_CRYPTO` for “mainstream” use +cases on our priority list. So in the medium term `MBEDTLS_ECP_RESTARTABLE` is +incompatible with `MBEDTLS_USE_PSA_CRYPTO`. + +Note: it is possible to make the options compatible at build time simply by +deciding that when `USE_PSA_CRYPTO` is enabled, PSA APIs are used except if +restartable behaviour was requested at run-time (in addition to enabling +`MBEDTLS_ECP_RESTARTABLE` in the build). + +### `MBEDTLS_PSA_CRYPTO_CONFIG` + +(This section taken from a comment by Gilles.) + +X509 and TLS code use `MBEDTLS_xxx` macros to decide whether an algorithm is +supported. This doesn't make `MBEDTLS_USE_PSA_CRYPTO` incompatible with +`MBEDTLS_PSA_CRYPTO_CONFIG` per se, but it makes it incompatible with most +useful uses of `MBEDTLS_PSA_CRYPTO_CONFIG`. The point of +`MBEDTLS_PSA_CRYPTO_CONFIG` is to be able to build a library with support for +an algorithm through a PSA driver only, without building the software +implementation of that algorithm. But then the TLS code would consider the +algorithm unavailable. + +This is tracked in https://github.com/ARMmbed/mbedtls/issues/3674 and +https://github.com/ARMmbed/mbedtls/issues/3677. But now that I look at it with +fresh eyes, I don't think the approach we were planning to use would actually +works. This needs more design effort. + +This is something we need to support eventually, and several partners want it. +I don't know what the priority is for `MBEDTLS_USE_PSA_CRYPTO` between +improving driver support and covering more of the protocol. It seems to me +that it'll be less work overall to first implement a good architecture for +`MBEDTLS_USE_PSA_CRYPTO + MBEDTLS_PSA_CRYPTO_CONFIG` and then extend to more +protocol features, because implementing that architecture will require changes +to the existing code and the less code there is at this point the better, +whereas extending to more protocol features will require the same amount of +work either way. + +### Backward compatibility issues with making it always on + +1. Existing applications may not be calling `psa_crypto_init()` before using + TLS, X.509 or PK. We can try to work around that by calling (the relevant +part of) it ourselves under the hood as needed, but that would likely require +splitting init between the parts that can fail and the parts that can't (see +https://github.com/ARM-software/psa-crypto-api/pull/536 for that). +2. It's currently not possible to enable `MBEDTLS_PSA_CRYPTO_C` in + configurations that don't have `MBEDTLS_ENTROPY_C`, and we can't just +auto-enable the latter, as it won't build or work out of the box on all +platforms. There are two kinds of things we'd need to do if we want to work +around that: + 1. Make it possible to enable the parts of PSA Crypto that don't require an + RNG (typically, public key operations, symmetric crypto, some key +management functions (destroy etc)) in configurations that don't have +`ENTROPY_C`. This requires going through the PSA code base to adjust +dependencies. Risk: there may be annoying dependencies, some of which may be +surprising. + 2. For operations that require an RNG, provide an alternative function + accepting an explicit `f_rng` parameter (see #5238), that would be +available in entropy-less builds. (Then code using those functions still needs +to have one version using it, for entropy-less builds, and one version using +the standard function, for driver support in build with entropy.) + +See https://github.com/ARMmbed/mbedtls/issues/5156 + +Taking advantage of the existing abstractions layers - or not +============================================================= + +The Crypto library in Mbed TLS currently has 3 abstraction layers that offer +algorithm-agnostic APIs for a class of algorithms: + +- MD for messages digests aka hashes (including HMAC) +- Cipher for symmetric ciphers (included AEAD) +- PK for asymmetric (aka public-key) cryptography (excluding key exchange) + +Note: key exchange (FFDH, ECDH) is not covered by an abstraction layer. + +These abstraction layers typically provide, in addition to the API for crypto +operations, types and numerical identifiers for algorithms (for +example `mbedtls_cipher_mode_t` and its values). The +current strategy is to keep using those identifiers in most of the code, in +particular in existing structures and public APIs, even when +`MBEDTLS_USE_PSA_CRYPTO` is enabled. (This is not an issue for G1, G2, G3 +above, and is only potentially relevant for G4.) + +The are multiple strategies that can be used regarding the place of those +layers in the migration to PSA. + +Silently call to PSA from the abstraction layer +----------------------------------------------- + +- Provide a new definition (conditionally on `USE_PSA_CRYPTO`) of wrapper + functions in the abstraction layer, that calls PSA instead of the legacy +crypto API. +- Upside: changes contained to a single place, no need to change TLS or X.509 + code anywhere. +- Downside: tricky to implement if the PSA implementation is currently done on + top of that layer (dependency loop). + +This strategy is currently (late 2021) used for ECDSA signature +verification in the PK layer, and could be extended to all operations in the +PK layer. + +This strategy is not very well suited to the Cipher layer, as the PSA +implementation is currently done on top of that layer. + +This strategy will probably be used for some time for the PK layer, while we +figure out what the future of that layer is: parts of it (parse/write, ECDSA +signatures in the format that X.509 & TLS want) are not covered by PSA, so +they will need to keep existing in some way. Also the PK layer is also a good +place for dispatching to either PSA or `mbedtls_xxx_restartable` while that +part is not covered by PSA yet. + +Replace calls for each operation +-------------------------------- + +- For every operation that's done through this layer in TLS or X.509, just + replace function call with calls to PSA (conditionally on `USE_PSA_CRYPTO`) +- Upside: conceptually simple, and if the PSA implementation is currently done + on top of that layer, avoids concerns about dependency loops. +- Upside: opens the door to building TLS/X.509 without that layer, saving some + code size. +- Downside: TLS/X.509 code has to be done for each operation. + +This strategy is currently (late 2021) used for the MD layer. (Currently only +a subset of calling places, but will be extended to all of them.) + +In the future (early 2022) we're going to use it for the Cipher layer as well. + +Opt-in use of PSA from the abstraction layer +-------------------------------------------- + +- Provide a new way to set up a context that causes operations on that context + to be done via PSA. +- Upside: changes mostly contained in one place, TLS/X.509 code only needs to + be changed when setting up the context, but not when using it. In + particular, no changes to/duplication of existing public APIs that expect a + key to be passed as a context of this layer (eg, `mbedtls_pk_context`). +- Upside: avoids dependency loop when PSA implemented on top of that layer. +- Downside: when the context is typically set up by the application, requires + changes in application code. + +This strategy is not useful when no context is used, for example with the +one-shot function `mbedtls_md()`. + +There are two variants of this strategy: one where using the new setup +function also allows for key isolation (the key is only held by PSA, +supporting both G1 and G2 in that area), and one without isolation (the key is +still stored outside of PSA most of the time, supporting only G1). + +This strategy, with support for key isolation, is currently (end of 2021) used for ECDSA +signature generation in the PK layer - see `mbedtls_pk_setup_opaque()`. This +allows use of PSA-held private ECDSA keys in TLS and X.509 with no change to +the TLS/X.509 code, but a contained change in the application. If could be +extended to other private key operations in the PK layer, which is the plan as +of early 2022. + +This strategy, without key isolation, is also currently used in the Cipher +layer - see `mbedtls_cipher_setup_psa()`. This allows use of PSA for cipher +operations in TLS with no change to the application code, and a +contained change in TLS code. (It currently only supports a subset of +ciphers.) However, we'll move to the "Replace calls for each operation" +strategy (early 2022), in the hope of being able to build without this layer +in order to save some code size in the future. + +Note: for private key operations in the PK layer, both the "silent" and the +"opt-in" strategy can apply, and can complement each other, as one provides +support for key isolation, but at the (unavoidable) code of change in +application code, while the other requires no application change to get +support for drivers, but fails to provide isolation support. + +Summary +------- + +Strategies currently used with each abstraction layer: + +- PK (for G1): silently call PSA +- PK (for G2): opt-in use of PSA (new key type) +- Cipher (G1): + - late 2021: opt-in use of PSA (new setup function) + - early 2022: moving to "replace calls at each call site" +- MD (G1): replace calls at each call site + +Migrating away from the legacy API +================================== + +This section briefly introduces questions and possible plans towards G4, +mainly as they relate to choices in previous stages. + +The role of the PK/Cipher/MD APIs in user migration +--------------------------------------------------- + +We're currently taking advantage of the existing PK and Cipher layers in order +to reduce the number of places where library code needs to be changed. It's +only natural to consider using the same strategy (with the PK, MD and Cipher +layers) for facilitating migration of application code. + +Note: a necessary first step for that would be to make sure PSA is no longer +implemented of top of the concerned layers + +### Zero-cost compatibility layer? + +The most favourable case is if we can have a zero-cost abstraction (no +runtime, RAM usage or code size penalty), for example just a bunch of +`#define`s, essentially mapping `mbedtls_` APIs to their `psa_` equivalent. + +Unfortunately that's unlikely fully work. For example, the MD layer uses the +same context type for hashes and HMACs, while the PSA API (rightfully) has +distinct operation types. Similarly, the Cipher layer uses the same context +type for unauthenticated and AEAD ciphers, which again the PSA API +distinguishes. + +It is unclear how much value, if any, a zero-cost compatibility layer that's +incomplete (for example, for MD covering only hashes, or for Cipher covering +only AEAD) or differs significantly from the existing API (for example, +introducing new context types) would provide to users. + +### Low-cost compatibility layers? + +Another possibility is to keep most or all of the existing API for the PK, MD +and Cipher layers, implemented on top of PSA, aiming for the lowest possible +cost. For example, `mbedtls_md_context_t` would be defined as a (tagged) union +of `psa_hash_operation_t` and `psa_mac_operation_t`, then `mbedtls_md_setup()` +would initialize the correct part, and the rest of the functions be simple +wrappers around PSA functions. This would vastly reduce the complexity of the +layers compared to the existing (no need to dispatch through function +pointers, just call the corresponding PSA API). + +Since this would still represent a non-zero cost, not only in terms of code +size, but also in terms of maintenance (testing, etc.) this would probably +be a temporary solution: for example keep the compatibility layers in 4.0 (and +make them optional), but remove them in 5.0. + +Again, this provides the most value to users if we can manage to keep the +existing API unchanged. Their might be conflicts between this goal and that of +reducing the cost, and judgment calls may need to be made. + +Note: when it comes to holding public keys in the PK layer, depending on how +the rest of the code is structured, it may be worth holding the key data in +memory controlled by the PK layer as opposed to a PSA key slot, moving it to a +slot only when needed (see current `ecdsa_verify_wrap` when +`MBEDTLS_USE_PSA_CRYPTO` is defined) For example, when parsing a large +number, N, of X.509 certificates (for example the list of trusted roots), it +might be undesirable to use N PSA key slots for their public keys as long as +the certs are loaded. OTOH, this could also be addressed by merging the "X.509 +parsing on-demand" (#2478), and then the public key data would be held as +bytes in the X.509 CRT structure, and only moved to a PK context / PSA slot +when it's actually used. + +Note: the PK layer actually consists of two relatively distinct parts: crypto +operations, which will be covered by PSA, and parsing/writing (exporting) +from/to various formats, which is currently not fully covered by the PSA +Crypto API. + +### Algorithm identifiers and other identifiers + +It should be easy to provide the user with a bunch of `#define`s for algorithm +identifiers, for example `#define MBEDTLS_MD_SHA256 PSA_ALG_SHA_256`; most of +those would be in the MD, Cipher and PK compatibility layers mentioned above, +but there might be some in other modules that may be worth considering, for +example identifiers for elliptic curves. + +### Lower layers + +Generally speaking, we would retire all of the low-level, non-generic modules, +such as AES, SHA-256, RSA, DHM, ECDH, ECP, bignum, etc, without providing +compatibility APIs for them. People would be encouraged to switch to the PSA +API. (The compatibility implementation of the existing PK, MD, Cipher APIs +would mostly benefit people who already used those generic APis rather than +the low-level, alg-specific ones.) + +### APIs in TLS and X.509 + +Public APIs in TLS and X.509 may be affected by the migration in at least two +ways: + +1. APIs that rely on a legacy `mbedtls_` crypto type: for example + `mbedtls_ssl_conf_own_cert()` to configure a (certificate and the +associated) private key. Currently the private key is passed as a +`mbedtls_pk_context` object, which would probably change to a `psa_key_id_t`. +Since some users would probably still be using the compatibility PK layer, it +would need a way to easily extract the PSA key ID from the PK context. + +2. APIs the accept list of identifiers: for example + `mbedtls_ssl_conf_curves()` taking a list of `mbedtls_ecp_group_id`s. This +could be changed to accept a list of pairs (`psa_ecc_familiy_t`, size) but we +should probably take this opportunity to move to a identifier independent from +the underlying crypto implementation and use TLS-specific identifiers instead +(based on IANA values or custom enums), as is currently done in the new +`mbedtls_ssl_conf_groups()` API, see #4859). + +Testing +------- + +An question that needs careful consideration when we come around to removing +the low-level crypto APIs and making PK, MD and Cipher optional compatibility +layers is to be sure to preserve testing quality. A lot of the existing test +cases use the low level crypto APIs; we would need to either keep using that +API for tests, or manually migrated test to the PSA Crypto API. Perhaps a +combination of both, perhaps evolving gradually over time. diff --git a/docs/architecture/psa-migration/syms.sh b/docs/architecture/psa-migration/syms.sh new file mode 100755 index 000000000000..5c34b28d1a9c --- /dev/null +++ b/docs/architecture/psa-migration/syms.sh @@ -0,0 +1,58 @@ +#!/bin/sh +# +# Copyright The Mbed TLS Contributors +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Purpose +# +# Show symbols in the X.509 and TLS libraries that are defined in another +# libmbedtlsXXX.a library. This is usually done to list Crypto dependencies. +# +# Usage: +# - build the library with debug symbols and the config you're interested in +# (default, full minus MBEDTLS_USE_PSA_CRYPTO, full, etc.) +# - run this script with the name of your config as the only argument + +set -eu + +# list mbedtls_ symbols of a given type in a static library +syms() { + TYPE="$1" + FILE="$2" + + nm "$FILE" | sed -n "s/[0-9a-f ]*${TYPE} \(mbedtls_.*\)/\1/p" | sort -u +} + +# create listings for the given library +list() { + NAME="$1" + FILE="library/libmbed${NAME}.a" + PREF="${CONFIG}-$NAME" + + syms '[TRrD]' $FILE > ${PREF}-defined + syms U $FILE > ${PREF}-unresolved + + diff ${PREF}-defined ${PREF}-unresolved \ + | sed -n 's/^> //p' > ${PREF}-external + sed 's/mbedtls_\([^_]*\).*/\1/' ${PREF}-external \ + | uniq -c | sort -rn > ${PREF}-modules + + rm ${PREF}-defined ${PREF}-unresolved +} + +CONFIG="${1:-unknown}" + +list x509 +list tls diff --git a/docs/architecture/psa-migration/tasks-g2.md b/docs/architecture/psa-migration/tasks-g2.md new file mode 100644 index 000000000000..72bd37791289 --- /dev/null +++ b/docs/architecture/psa-migration/tasks-g2.md @@ -0,0 +1,80 @@ +This document is temporary; it lists tasks to achieve G2 as described in +`strategy.md` while the strategy is being reviewed - once that's done, +corresponding github issues will be created and this document removed. + +For all of the tasks here, specific testing (integration and unit test depending +on the task) is required, see `testing.md`. + +RSA Signature operations +======================== + +In PK +----- + +### Modify existing `PK_OPAQUE` type to allow for RSA keys + +- the following must work and be tested: `mbedtls_pk_get_type()`, + `mbedtls_pk_get_name()`, `mbedtls_pk_get_bitlen()`, `mbedtls_pk_get_len()`, +`mbedtls_pk_can_do()`. +- most likely adapt `pk_psa_genkey()` in `test_suite_pk.function`. +- all other function (sign, verify, encrypt, decrypt, check pair, debug) will + return `MBEDTLS_ERR_PK_TYPE_MISMATCH` and this will be tested too. + +### Modify `mbedtls_pk_wrap_as_opaque()` to work with RSA. + +- OK to have policy hardcoded on signing with PKCS1v1.5, or allow more if + available at this time + +### Modify `mbedtls_pk_write_pubkey_der()` to work with RSA-opaque. + +- OK to just test that a generated key (with `pk_psa_genkey()`) can be + written, without checking for correctness of the result - this will be +tested as part of another task + +### Make `mbedtls_pk_sign()` work with RSA-opaque. + +- testing may extend `pk_psa_sign()` in `test_suite_pk_function` by adding + selector for ECDSA/RSA. + +In X.509 +-------- + +### Test using RSA-opaque for CSR generation + +- similar to what's already done with ECDSA-opaque + +### Test using opaque keys for Certificate generation + +- similar to what's done with testing CSR generation +- should test both RSA and ECDSA as ECDSA is not tested yet +- might require slight code adaptations, even if unlikely + + +In TLS +------ + +### Test using RSA-opaque for TLS client auth + +- similar to what's already done with ECDSA-opaque + +### Test using RSA-opaque for TLS server auth + +- similar to what's already done with ECDSA-opaque +- key exchanges: ECDHE-RSA and DHE-RSA + +RSA decrypt +=========== + +### Extend `PK_OPAQUE` to allow RSA decryption (PKCS1 v1.5) + +### Test using that in TLS for RSA and RSA-PSK key exchange. + +Support opaque PSKs for "mixed-PSK" key exchanges +================================================= + +See `PSA-limitations.md`. + +Possible split: +- one task to extend PSA (see `PSA-limitations.md`) +- then one task per handshake: DHE-PSK, ECDHE-PSK, RSA-PSK (with tests for + each) diff --git a/docs/architecture/psa-migration/testing.md b/docs/architecture/psa-migration/testing.md new file mode 100644 index 000000000000..70229ce797b7 --- /dev/null +++ b/docs/architecture/psa-migration/testing.md @@ -0,0 +1,99 @@ +Testing strategy for `MBEDTLS_USE_PSA_CRYPTO` +============================================= + +This document records the testing strategy used so far in implementing +`MBEDTLS_USE_PSA_CRYPTO`. + + +General considerations +---------------------- + +There needs to be at least one build in `all.sh` that enables +`MBEDTLS_USE_PSA_CRYPTO` and runs the full battery of tests; currently that's +ensured by the fact that `scripts/config.py full` enables +`MBEDTLS_USE_PSA_CRYPTO`. There needs to be at least one build with +`MBEDTLS_USE_PSA_CRYPTO` disabled (as long as it's optional); currently that's +ensured by the fact that it's disabled in the default config. + +Generally, code review is enough to ensure that PSA APIs are indeed used where +they should be when `MBEDTLS_USE_PSA_CRYPTO` is enabled. + +However, when it comes to TLS, we also have the option of using debug messages +to confirm which code path is taken. This is generally unnecessary, except when +a decision is made at run-time about whether to use the PSA or legacy code +path. For example, for record protection, currently some ciphers are supported +via PSA while some others aren't, with a run-time fallback. In this case, it's +good to have a debug message checked by the test case to confirm that the +right decision was made at run-time, i. e. that we didn't use the fallback for +ciphers that are supposed to be supported. + + +New APIs meant for application use +---------------------------------- + +For example, `mbedtls_pk_setup_opaque()` is meant to be used by applications +in order to create PK contexts that can then be passed to existing TLS and +X.509 APIs (which remain unchanged). + +In that case, we want: + +- unit testing of the new API and directly-related APIs - for example: + - in `test_suite_pk` we have a new test function `pk_psa_utils` that exercises + `mbedtls_pk_setup_opaque()` and checks that various utility functions + (`mbedtls_pk_get_type()` etc.) work and the functions that are expected to + fail (`mbedtls_pk_verify()` etc) return the expected error. + - in `test_suite_pk` we modified the existing `pk_psa_sign` test function to + check that signature generation works as expected + - in `test_suite_pkwrite` we should have a new test function checking that + exporting (writing out) the public part of the key works as expected and + that exporting the private key fails as expected. +- integration testing of the new API with each existing API which should + accepts a context created this way - for example: + - in `programs/ssl/ssl_client2` a new option `key_opaque` that causes the + new API to be used, and one or more tests in `ssl-opt.sh` using that. + (We should have the same server-side.) + - in `test_suite_x509write` we have a new test function + `x509_csr_check_opaque()` checking integration of the new API with the + existing `mbedtls_x509write_csr_set_key()`. + (We should have something similar for + `mbedtls_x509write_crt_set_issuer_key()`.) + +For some APIs, for example with `mbedtls_ssl_conf_psk_opaque()`, testing in +`test_suite_ssl` was historically not possible, so we only have testing in +`ssl-opt.sh`. + +New APIs meant for internal use +------------------------------- + +For example, `mbedtls_cipher_setup_psa()` is meant to be used by the TLS +layer, but probably not directly by applications. + +In that case, we want: + +- unit testing of the new API and directly-related APIs - for example: + - in `test_suite_cipher`, the existing test functions `auth_crypt_tv` and + `test_vec_crypt` gained a new parameter `use_psa` and corresponding test + cases +- integration testing: + - usually already covered by existing tests for higher-level modules: + - for example simple use of `mbedtls_cipher_setup_psa()` in TLS is already + covered by running the existing TLS tests in a build with + `MBEDTLS_USA_PSA_CRYPTO` enabled + - however if use of the new API in higher layers involves more logic that + use of the old API, specific integrations test may be required + - for example, the logic to fall back from `mbedtls_cipher_setup_psa()` to + `mbedtls_cipher_setup()` in TLS is tested by `run_test_psa` in + `ssl-opt.sh`. + +Internal changes +---------------- + +For example, use of PSA to compute the TLS 1.2 PRF. + +Changes in this category rarely require specific testing, as everything should +be already be covered by running the existing tests in a build with +`MBEDTLS_USE_PSA_CRYPTO` enabled; however we need to make sure the existing +test have sufficient coverage, and improve them if necessary. + +However, if additional logic is involved, or there are run-time decisions about +whether to use the PSA or legacy code paths, specific tests might be in order. diff --git a/docs/use-psa-crypto.md b/docs/use-psa-crypto.md index 4a78e47e7e99..9c97b5d5c741 100644 --- a/docs/use-psa-crypto.md +++ b/docs/use-psa-crypto.md @@ -59,10 +59,9 @@ this is supported on both sides, it's currently only tested client-side); - `mbedtls_x509write_csr_set_key()` to generate a CSR (certificate signature request). -In the TLS and X.509 API, there are two other functions which accept a key or -keypair as a PK context: `mbedtls_x509write_crt_set_subject_key()` and -`mbedtls_x509write_crt_set_issuer_key()`. Use of opaque contexts here probably -works but is so far untested. +In the TLS and X.509 API, there's one other function which accepts a keypair +as a PK context: `mbedtls_x509write_crt_set_issuer_key()`. Use of opaque +contexts here probably works but is so far untested. ### PSA-held (opaque) keys for TLS pre-shared keys (PSK)