Merge pull request #5004 from mpg/doc-psa-migration

Document PSA migration strategy
This commit is contained in:
Manuel Pégourié-Gonnard 2022-02-09 12:07:12 +01:00 committed by GitHub
commit 2c5fbad479
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 1010 additions and 4 deletions

View file

@ -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).

View file

@ -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
<https://github.com/ARMmbed/mbedtls/issues/5259>);
- 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.

View file

@ -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

View file

@ -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)

View file

@ -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.

View file

@ -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)