This commit modifies the helper `ssl_parse_certificate_chain()` to
accep any target X.509 CRT chain instead of hardcoding it to
`session_negotiate->peer_cert`. This increases modularity and paves
the way towards removing `mbedtls_ssl_session::peer_cert`.
This commit simplifies the client-side code for outgoing CertificateVerify
messages, and server-side code for outgoing CertificateRequest messages and
incoming CertificateVerify messages, through the use of the macro
`MBEDTLS_KEY_EXCHANGE__CERT_REQ_ALLOWED__ENABLED`
indicating whether a ciphersuite allowing CertificateRequest messages
is enabled in the configuration, as well as the helper function
`mbedtls_ssl_ciphersuite_cert_req_allowed()`
indicating whether a particular ciphersuite allows CertificateRequest
messages.
These were already used in the client-side code to simplify the
parsing functions for CertificateRequest messages.
This commit adds a helper function `ssl_parse_certificate_coordinate()`
which checks whether a `Certificate` message is expected from the peer.
The logic is the following:
- For ciphersuites which don't use server-side CRTs, no Certificate
message is expected (neither for the server, nor the client).
- On the server, no client certificate is expected in the following cases:
* The server server didn't request a Certificate, which is controlled
by the `authmode` setting.
* A RSA-PSK suite is used; this is the only suite using server CRTs
but not allowing client-side authentication.
This commit introduces a static helper function
`mbedtls_ssl_ciphersuite_uses_srv_cert()`
which determines whether a ciphersuite may make use of server-side CRTs.
This function is in turn uses in `mbedtls_ssl_parse_certificate()` to
skip certificate parsing for ciphersuites which don't involve CRTs.
Note: Ciphersuites not using server-side CRTs don't allow client-side CRTs
either, so it is safe to guard `mbedtls_ssl_{parse/write}_certificate()`
this way.
Note: Previously, the code uses a positive check over the suites
- MBEDTLS_KEY_EXCHANGE_PSK
- MBEDTLS_KEY_EXCHANGE_DHE_PSK
- MBEDTLS_KEY_EXCHANGE_ECDHE_PSK
- MBEDTLS_KEY_EXCHANGE_ECJPAKE,
while now, it uses a negative check over `mbedtls_ssl_ciphersuite_uses_srv_cert()`,
which checks for the suites
- MBEDTLS_KEY_EXCHANGE_RSA
- MBEDTLS_KEY_EXCHANGE_RSA_PSK
- MBEDTLS_KEY_EXCHANGE_DHE_RSA
- MBEDTLS_KEY_EXCHANGE_ECDH_RSA
- MBEDTLS_KEY_EXCHANGE_ECDHE_RSA
- MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA
- MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA
This is equivalent since, together, those are all ciphersuites.
Quoting ssl_ciphersuites.h:
```
typedef enum {
MBEDTLS_KEY_EXCHANGE_NONE = 0,
MBEDTLS_KEY_EXCHANGE_RSA,
MBEDTLS_KEY_EXCHANGE_DHE_RSA,
MBEDTLS_KEY_EXCHANGE_ECDHE_RSA,
MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA,
MBEDTLS_KEY_EXCHANGE_PSK,
MBEDTLS_KEY_EXCHANGE_DHE_PSK,
MBEDTLS_KEY_EXCHANGE_RSA_PSK,
MBEDTLS_KEY_EXCHANGE_ECDHE_PSK,
MBEDTLS_KEY_EXCHANGE_ECDH_RSA,
MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA,
MBEDTLS_KEY_EXCHANGE_ECJPAKE,
} mbedtls_key_exchange_type_t;
```
The handler `mbedtls_ssl_parse_certificate()` for incoming `Certificate`
messages contains many branches updating the handshake state. For easier
reasoning about state evolution, this commit introduces a single code-path
updating the state machine at the end of `mbedtls_ssl_parse_certificate()`.
If an attempt for session resumption fails, the `session_negotiate` structure
might be partially filled, and in particular already contain a peer certificate
structure. This certificate structure needs to be freed before parsing the
certificate sent in the `Certificate` message.
This commit moves the code-path taking care of this from the helper
function `ssl_parse_certificate_chain()`, whose purpose should be parsing
only, to the top-level handler `mbedtls_ssl_parse_certificate()`.
The fact that we don't know the state of `ssl->session_negotiate` after
a failed attempt for session resumption is undesirable, and a separate
issue #2414 has been opened to improve on this.
This commit introduces a server-side static helper function
`ssl_srv_check_client_no_crt_notification()`, which checks if
the message we received during the incoming certificate state
notifies the server of the lack of certificate on the client.
For SSLv3, such a notification comes as a specific alert,
while for all other TLS versions, it comes as a `Certificate`
handshake message with an empty CRT list.
So far, we've used the `peer_cert` pointer to detect whether
we're parsing the first CRT, but that will soon be removed
if `MBEDTLS_SSL_KEEP_PEER_CERTIFICATE` is unset.
This commit introduces a helper function `ssl_clear_peer_cert()`
which frees all data related to the peer's certificate from an
`mbedtls_ssl_session` structure. Currently, this is the peer's
certificate itself, while eventually, it'll be its digest only.
After mitigating the 'triple handshake attack' by checking that
the peer's end-CRT didn't change during renegotation, the current
code avoids re-parsing the CRT by moving the CRT-pointer from the
old session to the new one. While efficient, this will no longer
work once only the hash of the peer's CRT is stored beyond the
handshake.
This commit removes the code-path moving the old CRT, and instead
frees the entire peer CRT chain from the initial handshake as soon
as the 'triple handshake attack' protection has completed.
- Populate the ECDH private key slot with a fresh private EC key
designated for the correct algorithm.
- Export the public part of the ECDH private key from PSA and
reformat it to suite the format of the ClientKeyExchange message.
- Perform the PSA-based ECDH key agreement and store the result
as the premaster secret for the connection.
- Reformat the server's ECDH public key to make it suitable
for the PSA key agreement API. Currently, the key agreement
API needs a full SubjectPublicKeyInfo structure, while the
TLS ServerKeyExchange message only contains a ECPoint structure.
Enable handling of zero-length null output in PKCS1 v1.5 decryption.
Prevent undefined behavior by avoiding a memcpy() to zero-length null
output buffers.
In mbedtls_rsa_rsaes_oaep_encrypt and
mbedtls_rsa_rsaes_pkcs1_v15_encrypt, if the input length is 0 (which
is unusual and mostly useless, but permitted) then it is fine for the
input pointer to be NULL. Don't return an error in this case.
When `input` is NULL, `memcpy( p, input, ilen )` has undefined behavior
even if `ilen` is zero. So skip the `memcpy` call in this case.
Likewise, in `mbedtls_rsa_rsaes_oaep_decrypt`, skip the `memcpy` call if
`*olen` is zero.
Context: During a handshake, the SSL/TLS handshake logic constructs
an instance of ::mbedtls_ssl_session representing the SSL session
being established. This structure contains information such as the
session's master secret, the peer certificate, or the session ticket
issues by the server (if applicable).
During a renegotiation, the new session is constructed aside the existing
one and destroys and replaces the latter only when the renegotiation is
complete. While conceptually clear, this means that during the renegotiation,
large pieces of information such as the peer's CRT or the session ticket
exist twice in memory, even though the original versions are removed
eventually.
This commit removes the simultaneous presence of two peer CRT chains
in memory during renegotiation, in the following way:
- Unlike in the case of SessionTickets handled in the previous commit,
we cannot simply free the peer's CRT chain from the previous handshake
before parsing the new one, as we need to verify that the peer's end-CRT
hasn't changed to mitigate the 'Triple Handshake Attack'.
- Instead, we perform a binary comparison of the original peer end-CRT
with the one presented during renegotiation, and if it succeeds, we
avoid re-parsing CRT by moving the corresponding CRT pointer from the
old to the new session structure.
- The remaining CRTs in the peer's chain are not affected by the triple
handshake attack protection, and for them we may employ the canonical
approach of freeing them before parsing the remainder of the new chain.
Note that this commit intends to not change any observable behavior
of the stack. In particular:
- The peer's CRT chain is still verified during renegotiation.
- The tail of the peer's CRT chain may change during renegotiation.
Context: During a handshake, the SSL/TLS handshake logic constructs
an instance of ::mbedtls_ssl_session representing the SSL session
being established. This structure contains information such as the
session's master secret, the peer certificate, or the session ticket
issues by the server (if applicable).
During a renegotiation, the new session is constructed aside the existing
one and destroys and replaces the latter only when the renegotiation is
complete. While conceptually clear, this means that during the renegotiation,
large pieces of information such as the peer's CRT or the session ticket
exist twice in memory, even though the original versions are removed
eventually.
This commit starts removing this memory inefficiency by freeing the old
session's SessionTicket before the one for the new session is allocated.
Context:
The existing API `mbedtls_x509_parse_crt_der()` for parsing DER
encoded X.509 CRTs unconditionally makes creates a copy of the
input buffer in RAM. While this comes at the benefit of easy use,
-- specifically: allowing the user to free or re-use the input
buffer right after the call -- it creates a significant memory
overhead, as the CRT is duplicated in memory (at least temporarily).
This might not be tolerable a resource constrained device.
As a remedy, this commit adds a new X.509 API call
`mbedtls_x509_parse_crt_der_nocopy()`
which has the same signature as `mbedtls_x509_parse_crt_der()`
and almost the same semantics, with one difference: The input
buffer must persist and be unmodified for the lifetime of the
established instance of `mbedtls_x509_crt`, that is, until
`mbedtls_x509_crt_free()` is called.
Resolve incompatibilties in the RSA module where changes made for
parameter validation prevent Mbed Crypto from working. Mbed Crypto
depends on being able to pass zero-length buffers that are NULL to RSA
encryption functions.
This reverts commit 2f660d047d.
Context: There are two public key writing functions in Mbed TLS. First,
mbedtls_pk_write_pubkey(), which exports a public key in the form of a
SubjectPublicKey structure containing the raw keying material
(for example, EC point coordinates for an EC public key, without
reference to the underlying curve). Secondly, mbedtls_pk_write_pubkey_der(),
which exports a public key in the form of a SubjectPublicKeyInfo structure,
wrapping the SubjectPublicKey structure by additional information
identifying the type of public key (and for ECC, e.g., it'd also contain
the ECC group identifier). The implementation of mbedtls_pk_write_pubkey_der()
calls mbedtls_pk_write_pubkey() first and then adds the corresponding
algorithm identifier wrapper.
Both of these functions need to be provided for PSA-based opaque PK contexts,
based on PSA's public key export function.
Previously, PSA used the SubjectPublicKeyInfo structure as its export format,
so mbedtls_pk_write_pubkey_der() could be easily implemented, while
mbedtls_pk_write_pubkey() would need to trim the output of the PSA export.
The previous implementation of mbedtls_pk_write_pubkey() is not quite right
because it calls PSA export doesn't do any trimming, hence exporting the large
SubjectPublicKeyInfo structure instead of the small SubjectPublicKey.
mbedtls_pk_write_pubkey_der(), in turn, immediately returns after calling
mbedtls_pk_write_pubkey(), hence also returning the SubjectPublicKeyInfo
structure, which is correct.
By now, the PSA public key export format has changed to the smaller
SubjectPublicKey structure. This means that, now, mbedtls_pk_write_pubkey()
can be implemented by just calling the PSA export, and that
mbedtls_pk_write_pubkey_der() needs to add the algorithm information around
it, just as in the other types of PK contexts. While not correct for the
old format, the existing code for mbedtls_pk_write_pubkey() is therefore
correct for the new PSA public key format, and needs no change apart from
the missing pointer shift in the last commit.
The implementation of mbedtls_pk_write_pubkey_der() needs a special code
path for PSA-based opaque PK contexts, as the PK context only contains
the PSA key handle, and the PSA API needs to be used to extract the
underlying EC curve to be able to write the AlgorithmParameter structure
that's part of the SubjectPublicKeyInfo structure.
That's what this commit does, (hopefully) making both
mbedtls_pk_write_pubkey() and mbedtls_pk_write_pubkey_der() export
the correctly formatted public key based on the new PSA public key format.
Additional changes to temporarily enable running tests:
ssl_srv.c and test_suite_ecdh use mbedtls_ecp_group_load instead of
mbedtls_ecdh_setup
test_suite_ctr_drbg uses mbedtls_ctr_drbg_update instead of
mbedtls_ctr_drbg_update_ret
Previously, PSA used SubjectPublicKeyInfo structures to serialize EC public keys.
This has recently been changed to using ECPoint structures instead, but the wrapper
making PSA ECDSA verification available through Mbed TLS' PK API hasn't yet been
adapted accordingly - which is what this commit does.
Luckily, Mbed TLS' PK API offers two functions mbedtls_pk_write_pubkey()
and mbedtls_pk_write_pubkey_der(), the latter exporting a SubjectPublicKeyInfo
structure and the former exporting an ECPoint structure in case of EC public
keys. For the adaptation of the ECDSA wrapper ecdsa_verify_wrap() it is therefore
sufficient to use mbedtls_pk_write_pubkey() instead of mbedtls_pk_write_pubkey_der().
Return the error code if failed, instead of returning value `1`.
If not failed, return the call of the underlying function,
in `mbedtls_ecdsa_genkey()`.
Use `cmake -D CMAKE_BUILD_TYPE=Asan` rather than manually setting
`-fsanitize=address`. This lets cmake determine the necessary compiler
and linker flags.
With UNSAFE_BUILD on, force -Wno-error. This is necessary to build
with MBEDTLS_TEST_NULL_ENTROPY.
mbedtls_mpi_read_binary() calls memcpy() with the source pointer being
the source pointer passed to mbedtls_mpi_read_binary(), the latter may
be NULL if the buffer length is 0 (and this happens e.g. in the ECJPAKE
test suite). The behavior of memcpy(), in contrast, is undefined when
called with NULL source buffer, even if the length of the copy operation
is 0.
This commit fixes this by explicitly checking that the source pointer is
not NULL before calling memcpy(), and skipping the call otherwise.
Context: The function `mbedtls_mpi_fill_random()` uses a temporary stack
buffer to hold the random data before reading it into the target MPI.
Problem: This is inefficient both computationally and memory-wise.
Memory-wise, it may lead to a stack overflow on constrained devices with
limited stack.
Fix: This commit introduces the following changes to get rid of the
temporary stack buffer entirely:
1. It modifies the call to the PRNG to output the random data directly
into the target MPI's data buffer.
This alone, however, constitutes a change of observable behaviour:
The previous implementation guaranteed to interpret the bytes emitted by
the PRNG in a big-endian fashion, while rerouting the PRNG output into the
target MPI's limb array leads to an interpretation that depends on the
endianness of the host machine.
As a remedy, the following change is applied, too:
2. Reorder the bytes emitted from the PRNG within the target MPI's
data buffer to ensure big-endian semantics.
Luckily, the byte reordering was already implemented as part of
`mbedtls_mpi_read_binary()`, so:
3. Extract bigendian-to-host byte reordering from
`mbedtls_mpi_read_binary()` to a separate internal function
`mpi_bigendian_to_host()` to be used by `mbedtls_mpi_read_binary()`
and `mbedtls_mpi_fill_random()`.