`mbedtls_ssl_handshake_params::extended_ms` holds the state of the
ExtendedMasterSecret extension in the current handshake. Initially
set to 'disabled' for both client and server,
- the client sets it to 'enabled' as soon as it finds the ExtendedMS
extension in the `ServerHello` and it has advertised that extension
in its ClientHello,
- the server sets it to 'enabled' as soon as it finds the ExtendedMS
extension in the `ClientHello` and is willing to advertise is in its
`ServerHello`.
This commit slightly restructures this logic in prepraration for the
removal of `mbedtls_ssl_handshake_params::extended_ms` in case both
the use and the enforcement of the ExtendedMasterSecret extension have
been fixed at compile-time. Namely, in this case there is no need for
the `extended_ms` field in the handshake structure, as the ExtendedMS
must be in use if the handshake progresses beyond the Hello stage.
Paving the way for the removal of mbedtls_ssl_handshake_params::extended_ms
this commit introduces a temporary variable tracking the presence of the
ExtendedMS extension in the ClientHello/ServerHello messages, leaving
the derivation of `extended_ms` (and potential failure) to the end of
the parsing routine.
This commit is the first in a series demonstrating how code-size
can be reduced by hardcoding parts of the SSL configuration at
compile-time, focusing on the example of the configuration of
the ExtendedMasterSecret extension.
The flexibility of an SSL configuration defined a runtime vs.
compile-time is necessary for the use of Mbed TLS as a
dynamically linked library, but is undesirable in constrained
environments because it introduces the following overhead:
- Definition of SSL configuration API (code-size overhead)
(and on the application-side: The API needs to be called)
- Additional fields in the SSL configuration (RAM overhead,
and potentially code-size overhead if structures grow
beyond immediate-offset bounds).
- Dereferencing is needed to obtain configuration settings.
- Code contains branches and potentially additional structure
fields to distinguish between different configurations.
Considering the example of the ExtendedMasterSecret extension,
this instantiates as follows:
- mbedtls_ssl_conf_extended_master_secret() and
mbedtls_ssl_conf_extended_master_secret_enforced()
are introduced to configure the ExtendedMasterSecret extension.
- mbedtls_ssl_config contains bitflags `extended_ms` and
`enforce_extended_master_secret` reflecting the runtime
configuration of the ExtendedMasterSecret extension.
- Whenever we need to access these fields, we need a chain
of dereferences `ssl->conf->extended_ms`.
- Determining whether Client/Server should write the
ExtendedMasterSecret extension needs a branch
depending on `extended_ms`, and the state of the
ExtendedMasterSecret negotiation needs to be stored in a new
handshake-local variable mbedtls_ssl_handshake_params::extended_ms.
Finally (that's the point of ExtendedMasterSecret) key derivation
depends on this handshake-local state of ExtendedMasterSecret.
All this is unnecessary if it is known at compile-time that the
ExtendedMasterSecret extension is used and enforced:
- No API calls are necessary because the configuration is fixed
at compile-time.
- No SSL config fields are necessary because there are corresponding
compile-time constants instead.
- Accordingly, no dereferences for field accesses are necessary,
and these accesses can instead be replaced by the corresponding
compile-time constants.
- Branches can be eliminated at compile-time because the compiler
knows the configuration. Also, specifically for the ExtendedMasterSecret
extension, the field `extended_ms` in the handshake structure
is unnecessary, because we can fail immediately during the Hello-
stage of the handshake if the ExtendedMasterSecret extension
is not negotiated; accordingly, the non-ExtendedMS code-path
can be eliminated from the key derivation logic.
A way needs to be found to allow fixing parts of the SSL configuration
at compile-time which removes this overhead in case it is used,
while at the same time maintaining readability and backwards
compatibility.
This commit proposes the following approach:
From the user perspective, for aspect of the SSL configuration
mbedtls_ssl_config that should be configurable at compile-time,
introduce a compile-time option MBEDTLS_SSL_CONF_FIELD_NAME.
If this option is not defined, the field is kept and configurable
at runtime as usual. If the option is defined, the field is logically
forced to the value of the option at compile time.
Internally, read-access to fields in the SSL configuration which are
configurable at compile-time gets replaced by new `static inline` getter
functions which evaluate to the corresponding field access or to the
constant MBEDTLS_SSL_CONF_FIELD_NAME, depending on whether the latter
is defined or not.
Write-access to fields which are configurable at compile-time needs
to be removed: Specifically, the corresponding API itself either
needs to be removed or replaced by a stub function without effect.
This commit takes the latter approach, which has the benefit of
not requiring any change on the example applications, but introducing
the risk of mismatching API calls and compile-time configuration,
in case a user doesn't correctly keep track of which parts of the
configuration have been fixed at compile-time, and which haven't.
Write-access for the purpose of setting defaults is simply omitted.
If `MBEDTLS_SSL_KEEP_PEER_CERTIFICATE` is not set, `mbedtls_ssl_session`
contains the digest of the peer's certificate for the sole purpose of
detecting a CRT change on renegotiation. Hence, it is not needed if
renegotiation is disabled.
This commit removes the `peer_cert_digest` fields (and friends) from
`mbedtls_ssl_session` if
`!MBEDTLS_SSL_KEEP_PEER_CERTIFICATE + !MBEDTLS_SSL_RENEGOTIATION`,
which is a sensible configuration for constrained devices.
Apart from straightforward replacements of
`if !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)`
by
`if !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) && \
defined(MBEDTLS_SSL_RENEGOTIATION)`,
there's one notable change: On the server-side, the CertificateVerify
parsing function is a no-op if the client hasn't sent a certificate.
So far, this was determined by either looking at the peer CRT or the
peer CRT digest in the SSL session structure (depending on the setting
of `MBEDTLS_SSL_KEEP_PEER_CERTIFICATE`), which now no longer works if
`MBEDTLS_SSL_KEEP_PEER_CERTIFICATE` is unset. Instead, this function
now checks whether the temporary copy of the peer's public key within
the handshake structure is initialized or not (which is also a
beneficial simplification in its own right, because the pubkey is
all the function needs anyway).
The previous placing of the return statement made it look like there
are configurations for which no return statement is emitted; while
that's not true (if this function is used, at least some version of
TLS must be enabled), it's still clearer to move the failing return
statement to outside of all preprocessor guards.
So far, `ssl_client2` printed the CRT info for the peer's CRT
by requesting the latter through `mbedtls_ssl_get_peer_cert()`
at the end of the handshake, and printing it via
`mbedtls_x509_crt_info()`. When `MBEDTLS_SSL_KEEP_PEER_CERTIFICATE`
is disabled, this does no longer work because the peer's CRT
isn't stored beyond the handshake.
This makes some tests in `ssl-opt.sh` fail which rely on the CRT
info output for the peer certificate.
This commit modifies `ssl_client2` to extract the peer CRT info
from the verification callback, which is always called at a time
when the peer's CRT is available. This way, the peer's CRT info
is still printed if `MBEDTLS_SSL_KEEP_PEER_CERTIFICATE` is disabled.
`MBEDTLS_SSL__ECP_RESTARTABLE` is only defined if
`MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED` is set, which
requires `MBEDTLS_X509_PARSE_C` to be set (this is checked
in `check_config.`). The additional `MBEDTLS_X509_PARSE_C`
guard around the `ecrs_peer_cert` field is therefore not
necessary; moreover, it's misleading, because it hasn't
been used consistently throughout the code.
If we don't need to store the peer's CRT chain permanently, we may
free it immediately after verifying it. Moreover, since we parse the
CRT chain in-place from the input buffer in this case, pointers from
the CRT structure remain valid after freeing the structure, and we
use that to extract the digest and pubkey from the CRT after freeing
the structure.
It is used in `mbedtls_ssl_session_free()` under
`MBEDTLS_X509_CRT_PARSE_C`, but defined only if
`MBEDTLS_KEY_EXCHANGE__WITH_CERT__ENABLED`.
Issue #2422 tracks the use of
`MBEDTLS_KEY_EXCHANGE__WITH_CERT_ENABLED` instead of
`MBEDTLS_X509_CRT_PARSE_C` for code and fields
related to CRT-based ciphersuites.
The server expects a CertificateVerify message only if it has
previously received a Certificate from the client.
So far, this was detected by looking at the `peer_cert` field
in the current session. Preparing to remove the latter, this
commit changes this to instead determine the presence of a peer
certificate by checking the new `peer_cert_digest` pointer.
We must dispatch between the peer's public key stored as part of
the peer's CRT in the current session structure (situation until
now, and future behaviour if MBEDTLS_SSL_KEEP_PEER_CERTIFICATE is
enabled), and the sole public key stored in the handshake structure
(new, if MBEDTLS_SSL_KEEP_PEER_CERTIFICATE is disabled).
We must dispatch between the peer's public key stored as part of
the peer's CRT in the current session structure (situation until
now, and future behaviour if MBEDTLS_SSL_KEEP_PEER_CERTIFICATE is
enabled), and the sole public key stored in the handshake structure
(new, if MBEDTLS_SSL_KEEP_PEER_CERTIFICATE is disabled).
We must dispatch between the peer's public key stored as part of
the peer's CRT in the current session structure (situation until
now, and future behaviour if MBEDTLS_SSL_KEEP_PEER_CERTIFICATE is
enabled), and the sole public key stored in the handshake structure
(new, if MBEDTLS_SSL_KEEP_PEER_CERTIFICATE is disabled).
We must dispatch between the peer's public key stored as part of
the peer's CRT in the current session structure (situation until
now, and future behaviour if MBEDTLS_SSL_KEEP_PEER_CERTIFICATE is
enabled), and the sole public key stored in the handshake structure
(new, if MBEDTLS_SSL_KEEP_PEER_CERTIFICATE is disabled).
This commit modifies `mbedtls_ssl_parse_certificate()` to store a
copy of the peer's public key after parsing and verifying the peer's
CRT chain.
So far, this leads to heavy memory duplication: We have the CRT chain
in the I/O buffer, then parse (and, thereby, copy) it to a
`mbedtls_x509_crt` structure, and then make another copy of the
peer's public key, plus the overhead from the MPI and ECP structures.
This inefficiency will soon go away to a significant extend, because:
- Another PR adds functionality to parse CRTs without taking
ownership of the input buffers. Applying this here will allow
parsing and verifying the peer's chain without making an additional
raw copy. The overhead reduces to the size of `mbedtls_x509_crt`,
the public key, and the DN structures referenced in the CRT.
- Once copyless parsing is in place and the removal of the peer CRT
is fully implemented, we can extract the public key bounds from
the parsed certificate and then free the entire chain before
parsing the public key again. This means that we never store
the parsed public key twice at the same time.
When removing the (session-local) copy of the peer's CRT chain, we must
keep a handshake-local copy of the peer's public key, as (naturally) every
key exchange will make use of that public key at some point to verify that
the peer actually owns the corresponding private key (e.g., verify signatures
from ServerKeyExchange or CertificateVerify, or encrypt a PMS in a RSA-based
exchange, or extract static (EC)DH parameters).
This commit adds a PK context field `peer_pubkey` to the handshake parameter
structure `mbedtls_handshake_params_init()` and adapts the init and free
functions accordingly. It does not yet make actual use of the new field.