This mainly follows the design document (saving all fields marked "saved" in
the main structure and the transform sub-structure) with two exceptions:
- things related to renegotiation are excluded here (there weren't quite in
the design document as the possibility of allowing renegotiation was still
on the table, which is no longer is) - also, ssl.secure_renegotiation (which
is not guarded by MBEDTLS_SSL_RENEGOTIATION because it's used in initial
handshakes even with renegotiation disabled) is still excluded, as we don't
need it after the handshake.
- things related to Connection ID are added, as they weren't present at the
time the design document was written.
The exact format of the header (value of the bitflag indicating compile-time
options, whether and how to merge it with the serialized session header) will
be determined later.
Enforce restrictions indicated in the documentation.
This allows to make some simplifying assumptions (no need to worry about
saving IVs for CBC in TLS < 1.1, nor about saving handshake data) and
guarantees that all values marked as "forced" in the design document have the
intended values and can be skipped when serialising.
Some of the "forced" values are not checked because their value is a
consequence of other checks (for example, session_negotiated == NULL outside
handshakes). We do however check that session and transform are not NULL (even
if that's also a consequence of the initial handshake being over) as we're
going to dereference them and static analyzers may appreciate the info.
At that point, the timer might not yet be configured.
The timer is reset at the following occasions:
- when it is initially configured through
mbedtls_ssl_set_timer_cb() or
mbedtls_ssl_set_timer_cb_cx()
- when a session is reset in mbedtls_ssl_session_reset()
- when a handshake finishes via mbedtls_ssl_handshake_wrap()
This commit introduces the option MBEDTLS_SSL_CONF_SINGLE_HASH
which can be used to register a single supported signature hash
algorithm at compile time. It replaces the runtime configuration
API mbedtls_ssl_conf_sig_hashes() which allows to register a _list_
of supported signature hash algorithms.
In contrast to other options used to hardcode configuration options,
MBEDTLS_SSL_CONF_SINGLE_HASH isn't a numeric option, but instead it's
only relevant if it's defined or not. To actually set the single
supported hash algorithm that should be supported, numeric options
MBEDTLS_SSL_CONF_SINGLE_HASH_TLS_ID
MBEDTLS_SSL_CONF_SINGLE_HASH_MD_ID
must both be defined and provide the TLS ID and the Mbed TLS internal
ID and the chosen hash algorithm, respectively.
mbedtls_ssL_set_calc_verify_md() is used to select valid hashes when
writing the server's CertificateRequest message, as well as to verify
and act on the client's choice when reading its CertificateVerify
message.
If enabled at compile-time and configured via mbedtls_ssl_conf_sig_hashes()
the current code also offers SHA-1 in TLS 1.2. However, the SHA-1-based
handshake transcript in TLS 1.2 is different from the SHA-1 handshake
transcript used in TLS < 1.2, and we only maintain the latter
(through ssl_update_checksum_md5sha1()), but not the former.
Concretely, this will lead to CertificateVerify verification failure
if the client picks SHA-1 for the CertificateVerify message in a TLS 1.2
handshake.
This commit removes SHA-1 from the list of supported hashes in
the CertificateRequest message, and adapts two tests in ssl-opt.sh
which expect SHA-1 to be listed in the CertificateRequest message.
mbedtls_ssl_set_calc_verify_md() is only called from places
where it has been checked that TLS 1.2 is being used. The
corresponding compile-time and runtime guards checking the
version in mbedtls_ssl_set_calc_verify_md() are therefore
redundant and can be removed.
This commit introduces the option MBEDTLS_SSL_CONF_SINGLE_EC
which can be used to register a single supported elliptic curve
at compile time. It replaces the runtime configuration API
mbedtls_ssl_conf_curves() which allows to register a _list_
of supported elliptic curves.
In contrast to other options used to hardcode configuration options,
MBEDTLS_SSL_CONF_SINGLE_EC isn't a numeric option, but instead it's
only relevant if it's defined or not. To actually set the single
elliptic curve that should be supported, numeric options
MBEDTLS_SSL_CONF_SINGLE_EC_TLS_ID
MBEDTLS_SSL_CONF_SINGLE_EC_GRP_ID
must both be defined and provide the TLS ID and the Mbed TLS internal
ID and the chosen curve, respectively.
For both client/server the EC curve list is assumed not to be NULL:
- On the client-side, it's assumed when writing the
supported elliptic curve extension:
c54ee936d7/library/ssl_cli.c (L316)
- On the server, it is assumed when searching for a
suitable curve for the ECDHE exchange:
c54ee936d7/library/ssl_srv.c (L3200)
It is therefore not necessary to check this in mbedtls_ssl_check_curve().
Reasons:
- If the transport type is fixed at compile-time,
mbedtls_ssl_read_version() and mbedtls_ssl_write_version()
are called with a compile-time determined `transport`
parameter, so the transport-type branch in their body
can be eliminated at compile-time.
- mbedtls_ssl_read_version() is called with addresses of
local variables, which so far need to be put on the stack
to be addressable. Inlining the call allows to read directly
into the registers holding these local variables.
This saves 60 bytes w.r.t. the measurement performed by
> ./scripts/baremetal.sh --rom --gcc
This commit introduces the numeric compile-time constants
- MBEDTLS_SSL_CONF_MIN_MINOR_VER
- MBEDTLS_SSL_CONF_MAX_MINOR_VER
- MBEDTLS_SSL_CONF_MIN_MAJOR_VER
- MBEDTLS_SSL_CONF_MAX_MAJOR_VER
which, when defined, overwrite the runtime configurable fields
mbedtls_ssl_config::min_major_ver etc. in the SSL configuration.
As for the preceding case of the ExtendedMasterSecret configuration,
it also introduces and puts to use getter functions for these variables
which evaluate to either a field access or the macro value, maintaining
readability of the code.
The runtime configuration API mbedtls_ssl_conf_{min|max}_version()
is kept for now but has no effect if MBEDTLS_SSL_CONF_XXX are set.
This is likely to be changed in a later commit but deliberately omitted
for now, in order to be able to study code-size benefits earlier in the
process.
If MBEDTLS_SSL_SINGLE_CIPHERSUITE is enabled, the type
mbedtls_ssl_ciphersuite_handle_t
is logically a boolean (concretely realized as `unsigned char`),
containing the invalid handle and the unique valid handle, which
represents the single enabled ciphersuite.
The SSL session structure mbedtls_ssl_session contains an instance
of mbedtls_ssl_ciphersuite_handle_t which is guaranteed to be valid,
and which is hence redundant in any two-valued implementation of
mbedtls_ssl_ciphersuite_handle_t.
This commit replaces read-uses of
mbedtls_ssl_session::ciphersuite_info
by a getter functions which, and defines this getter function
either by just reading the field from the session structure
(in case MBEDTLS_SSL_SINGLE_CIPHERSUITE is disabled), or by
returning the single valid ciphersuite handle (in case
MBEDTLS_SSL_SINGLE_CIPHERSUITE is enabled) and removing the
field from mbedtls_ssl_session in this case.
If MBEDTLS_SSL_SINGLE_CIPHERSUITE is enabled, it overwrites
the runtime configuration of supported ciphersuites, which
includes both the configuration API and the fields which are
used to store the configuration. Both are therefore no longer
needed and should be removed for the benefit of code-size,
memory usage, and API clarity (no accidental hiccup of runtime
vs. compile-time configuration possible).
The configuration API mbedtls_ssl_conf_ciphersuites() has
already been removed in case MBEDTLS_SSL_SINGLE_CIPHERSUITE,
and this commit removes the field
mbedtls_ssl_config::ciphersuite_list
which it updates.
If MBEDTLS_SSL_SINGLE_CIPHERSUITE is enabled, the type
mbedtls_ssl_ciphersuite_handle_t
is logically a boolean (concretely realized as `unsigned char`),
containing the invalid handle and the unique valid handle, which
represents the single enabled ciphersuite.
The SSL handshake structure mbedtls_ssl_handshake_params contains
an instance of mbedtls_ssl_ciphersuite_handle_t which is guaranteed
to be valid, and which is hence redundant in any two-valued
implementation of mbedtls_ssl_ciphersuite_handle_t.
This commit replaces read-uses of
mbedtls_ssl_handshake_params::ciphersuite_info
by a getter functions which, and defines this getter function
either by just reading the field from the handshake structure
(in case MBEDTLS_SSL_SINGLE_CIPHERSUITE is disabled), or by
returning the single valid ciphersuite handle (in case
MBEDTLS_SSL_SINGLE_CIPHERSUITE is enabled) and removing the
field from mbedtls_ssl_handshake_params in this case.
This commit introduces an internal zero-cost abstraction layer for
SSL ciphersuites: Instead of addressing ciphersuites via pointers
to instances of mbedtls_ssl_ciphersuite_t and accessing their fields
directly, this commit introduces an opaque type
mbedtls_ssl_ciphersuite_handle_t,
and getter functions
mbedtls_ssl_suite_get_xxx()
operating on ciphersuite handles.
The role of NULL is played by a new macro constant
MBEDTLS_SSL_CIPHERSUITE_INVALID_HANDLE
which results of functions returning handles can be checked against.
(For example, when doing a lookup of a ciphersuite from a peer-provided
ciphersuite ID in the per's Hello message).
The getter functions have the validity of the handle as a precondition
and are undefined if the handle is invalid.
So far, there's only one implementation of this abstraction layer, namely
mbedtls_ssl_ciphersuite_handle_t being mbedtls_ssl_ciphersuite_t const *
and
getter functions being field accesses.
In subsequent commits, however, the abstraction layer will be useful
to save code in the situation where only a single ciphersuite is enabled.
So far, the client-proposed list of elliptic curves was stored for the
duration of the entire handshake in a heap-allocated buffer referenced
from mbedtls_ssl_handshake_params::curves. It is used in the following
places:
1) When the server chooses a suitable ciphersuite, it checks that
it has a certificate matching the ciphersuite; in particular, if
the ciphersuite involves ECDHE, the server needs an EC certificate
with a curve suitable for the client.
2) When performing the ECDHE key exchange, the server choose one
curve among those proposed by the client which matches the server's
own supported curve configuration.
This commit removes the hold back the entire client-side curve list
during the handshake, by performing (1) and (2) on during ClientHello
parsing, and in case of (2) only remembering the curve chosen for ECDHE
within mbedtls_ssl_handshake_params.
Fix an "unused variable" warning that happened in some configurations
(without EC, found by depend-pkalg.pl) and was not present in any parent PR
but only in the result of merging them: one of the PRs clarified the
distinction between `ret` and `verify_ret` and the other removed one
occurrence of using `ret`, and the conjunction of the two made `ret` unused in
some cases. Resolving by reducing the scope of that variable.
* restricted/pr/608:
programs: Make `make clean` clean all programs always
ssl_tls: Enable Suite B with subset of ECP curves
windows: Fix Release x64 configuration
timing: Remove redundant include file
net_sockets: Fix typo in net_would_block()
Add all.sh component that exercises invalid_param checks
Remove mbedtls_param_failed from programs
Make it easier to define MBEDTLS_PARAM_FAILED as assert
Make test suites compatible with #include <assert.h>
Pass -m32 to the linker as well
Update library to 2.16.2
Use 'config.pl baremetal' in all.sh
Clarify ChangeLog entry for fix to #1628Fix#2370, minor typos and spelling mistakes
Add Changelog entry for clang test-ref-configs.pl fix
Enable more compiler warnings in tests/Makefile
Change file scoping of test helpers.function
* restricted/pr/594:
Adapt baremetal.h and baremetal.sh
Don't incl. CAs in CertReq message in baremetal build
Allow config'n of incl of CertificateReq CA list Y/N at compile-time
Allow configuration of endpoint (cli/srv) at compile-time
Allow configuration of read timeouts at compile-time
Allow configuration of ConnectionID at compile-time
Allow compile-time configuration of legacy renegotiation
Allow compile-time configuration of authentication mode
Allow compile-time configuration of DTLS badmac limit
Allow compile-time configuration of DTLS anti replay
* restricted/pr/601: (27 commits)
Fix compile-time guard for optional field in struct
Move code to reduce probability of conflicts
Fix typos caught by check-names.sh
Clarify conditions related to resumption in client
Introduce getter function for renego_status
Add getter function for handshake->resume
Remove now-redundant code
Remove cache callbacks from config on client
Fix a few style issues
Expand documentation of new options a bit
Fix renaming oversight in documentation
Remove backticks in doxygen in config.h
Declare dependency on tickets for two ssl-opt.sh tests
Exclude new negative options from config.pl full
Restore config.h defaults
Address review comments
Fix ssl_cli resumption guards
Fix check-files, check-names and check-generated-features
Add test to all.sh
Add changelog entry
...
* restricted/pr/584: (140 commits)
Remove superfluous new line in x509.c
Add comment about X.509 name comparison of buffer with itself
[Fixup] Add missing PK release call in Cert Verify parsing
Fix guard controlling whether nested acquire calls are allowed
Add X.509 CRT test for nested calls for CRT frame / PK acquire
Don't return threading error on release()-without-acquire() calls
Don't allow nested CRT acquire()-calls if MBEDTLS_X509_ALWAYS_FLUSH
Make X.509 CRT cache reference counting unconditional
Remove memory buffer alloc from i386 test in all.sh
Don't mention pk_sign() in the context of public-key contexts
Don't use assertion for failures of mbedtls_x509_crt_x_acquire()
Fix copy pasta in x509_crt.h
Reference copy-less versions of X.509 CRT frame/PK getters
x509_crt.c: Add blank line to increase readability
[FIXUP] Fix bug in ASN.1 traversal of silently ignored tag
[FIXUP] Fix typo in declaration of mbedtls_x509_memcasecmp()
Move signature-info extraction out of MBEDTLS_X509_REMOVE_INFO
Fix certificate validity checking logic to work with !TIME_DATE
Simplify X.509 CRT version check in UID parsing
Remove unused variable warning in on-demand X.509 parsing
...
Introduces MBEDTLS_SSL_CONF_CERT_REQ_CA_LIST which allows to configure
at compile-time whether a CA list should be included in the
CertificateRequest message sent by the server.
Impact on code-size:
| | GCC 8.2.1 | ARMC5 5.06 | ARMC6 6.12 |
| --- | --- | --- | --- |
| `libmbedtls.a` before | 23131 | 23805 | 26673 |
| `libmbedtls.a` after | 23099 | 23781 | 26639 |
| gain in Bytes | 32 | 24 | 34 |
Introduces MBEDTLS_SSL_CONF_BADMAC_LIMIT to fix the maximum
number of records with bad MAC tolerated in DTLS at compile-time.
Impact on code-size:
| | GCC | ARMC5 | ARMC6 |
| --- | --- | --- | --- |
| `libmbedtls.a` before | 23511 | 24049 | 27903 |
| `libmbedtls.a` after | 23487 | 24025 | 27885 |
| gain in Bytes | 24 | 24 | 18 |
Add a new configuration option MBEDTLS_SSL_SESSION_RESUMPTION
to enable/disable the session resumption feature including
ticket and cache based session resumption.
Access the peer's PK through the PK acquire/release API only.
Care has to be taken not to accidentally overwrite the return
value `ret` from the CRT chain verification.
If the ExtendedMasterSecret extension is configured at compile-time
by setting MBEDTLS_SSL_CONF_EXTENDED_MASTER_SECRET and/or
MBEDTLS_SSL_CONF_ENFORCE_EXTENDED_MASTER_SECRET, the runtime
configuration APIs mbedtls_ssl_conf_extended_master_secret()
and mbedtls_ssl_conf_extended_master_secret_enforce() must
either be removed or modified to take no effect (or at most
check that the runtime value matches the hardcoded one, but
that would undermine the code-size benefits the hardcoding
is supposed to bring in the first place).
Previously, the API was kept but modified to have no effect.
While convenient for us because we don't have to adapt example
applications, this comes at the danger of users calling the runtime
configuration API, forgetting that the respective fields are
potentially already hardcoded at compile-time - and hence silently
using a configuration they don't intend to use.
This commit changes the approach to removing the configuration
API in case the respective field is hardcoded at compile-time,
and exemplifies it in the only case implemented so far, namely
the configuration of the ExtendedMasterSecret extension.
It adapts ssl_client2 and ssl_server2 by omitting the call to
the corresponding API if MBEDTLS_SSL_CONF_XXX are defined and
removing the command line parameters for the runtime configuration
of the ExtendedMasterSecret extension.
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.
* origin/pr/2714:
programs: Make `make clean` clean all programs always
ssl_tls: Enable Suite B with subset of ECP curves
windows: Fix Release x64 configuration
timing: Remove redundant include file
net_sockets: Fix typo in net_would_block()
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.
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.
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.
This commit changes the format of session tickets to include
the digest of the peer's CRT if MBEDTLS_SSL_KEEP_PEER_CERTIFICATE
is disabled.
This commit does not yet remove the peer CRT itself.
`mbedtls_ssl_parse_certificate()` parses the peer's certificate chain
directly into the `peer_cert` field of the `mbedtls_ssl_session`
structure being established. To allow to optionally remove this field
from the session structure, this commit changes this to parse the peer's
chain into a local variable instead first, which can then either be freed
after CRT verification - in case the chain should not be stored - or
mapped to the `peer_cert` if it should be kept. For now, only the latter
is implemented.
A subsequent commit will need this function in the session ticket
and session cache implementations. As the latter are server-side,
this commit also removes the MBEDTLS_SSL_CLI_C guard.
For now, the function is declared in ssl_internal.h and hence not
part of the public API.
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 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.
Some TLS-only code paths were not protected by an #ifdef and while some
compiler are happy to just silently remove them, armc5 complains:
Warning: #111-D: statement is unreachable
Let's make armc5 happy.
This commit handles occurrences of case 2 and 3 in the following list:
1. Some DTLS-specific code with no TLS-specific code (most frequent)
2. Some specific code for each protocol
3. Some TLS-specific code with no DTLS-specific code (least frequent)
Case 3 previously had a weird structure in that the TLS-specific code was
always present, but the if structure was conditional on DTLS being enabled.
This is changed by this commit to a more logical structure where both the code
and the test are conditional on TLS being enabled.
Case 2 doesn't require any change in the code structure in general. However,
there is one occurrence where the if/else structure is simplified to assigning
the result of a boolean operation, and one occurrence where I also noticed a
useless use of `ssl_ep_len()` in a TLS-specific branch, that I turned to the
constant 0 as it makes more sense.
Case 1 will be handled in the next commit, as it can easily be handled in an
automated way - only cases 2 and 3 (sometimes) required manual intervention.
The list of occurrences for cases 2 and 3 was established manually by looking
for occurrences of '= MBEDTLS_SSL_TRANSPORT_' in the code and manually
checking if there was a TLS-specific branch.
New sizes (see previous commit for the measuring script):
```
both
text data bss dec hex filename
1820 0 4 1824 720 debug.o (ex library/libmbedtls.a)
0 0 0 0 0 net_sockets.o (ex library/libmbedtls.a)
548 0 0 548 224 ssl_cache.o (ex library/libmbedtls.a)
11155 0 596 11751 2de7 ssl_ciphersuites.o (ex library/libmbedtls.a)
17156 0 0 17156 4304 ssl_cli.o (ex library/libmbedtls.a)
460 0 0 460 1cc ssl_cookie.o (ex library/libmbedtls.a)
17649 0 0 17649 44f1 ssl_srv.o (ex library/libmbedtls.a)
800 0 0 800 320 ssl_ticket.o (ex library/libmbedtls.a)
39286 60 0 39346 99b2 ssl_tls.o (ex library/libmbedtls.a)
88874 60 600 89534 15dbe (TOTALS)
DTLS-only
text data bss dec hex filename
1820 0 4 1824 720 debug.o (ex library/libmbedtls.a)
0 0 0 0 0 net_sockets.o (ex library/libmbedtls.a)
548 0 0 548 224 ssl_cache.o (ex library/libmbedtls.a)
11155 0 596 11751 2de7 ssl_ciphersuites.o (ex library/libmbedtls.a)
17068 0 0 17068 42ac ssl_cli.o (ex library/libmbedtls.a)
460 0 0 460 1cc ssl_cookie.o (ex library/libmbedtls.a)
17553 0 0 17553 4491 ssl_srv.o (ex library/libmbedtls.a)
800 0 0 800 320 ssl_ticket.o (ex library/libmbedtls.a)
38499 60 0 38559 969f ssl_tls.o (ex library/libmbedtls.a)
87903 60 600 88563 159f3 (TOTALS)
TLS-only
text data bss dec hex filename
1820 0 4 1824 720 debug.o (ex library/libmbedtls.a)
0 0 0 0 0 net_sockets.o (ex library/libmbedtls.a)
548 0 0 548 224 ssl_cache.o (ex library/libmbedtls.a)
11155 0 596 11751 2de7 ssl_ciphersuites.o (ex library/libmbedtls.a)
14912 0 0 14912 3a40 ssl_cli.o (ex library/libmbedtls.a)
460 0 0 460 1cc ssl_cookie.o (ex library/libmbedtls.a)
15868 0 0 15868 3dfc ssl_srv.o (ex library/libmbedtls.a)
800 0 0 800 320 ssl_ticket.o (ex library/libmbedtls.a)
27619 60 0 27679 6c1f ssl_tls.o (ex library/libmbedtls.a)
73182 60 600 73842 12072 (TOTALS)
```
And use those tools in a few places. For now the purpose is just to validate
those tools before using them in all occurrences of transport-specific code.
The effect of these changes was measured with the following script:
```
set -eu
build() {
printf "\n$1\n"
CC=arm-none-eabi-gcc CFLAGS='-Werror -Os -march=armv6-m -mthumb' \
AR=arm-none-eabi-ar LD=arm-none-eabi-ld make clean lib >/dev/null
arm-none-eabi-size -t library/libmbedtls.a
}
git checkout -- include/mbedtls/config.h
scripts/config.pl unset MBEDTLS_NET_C
scripts/config.pl unset MBEDTLS_TIMING_C
scripts/config.pl unset MBEDTLS_FS_IO
scripts/config.pl unset MBEDTLS_ENTROPY_NV_SEED
scripts/config.pl set MBEDTLS_NO_PLATFORM_ENTROPY
build "both"
scripts/config.pl unset MBEDTLS_SSL_PROTO_TLS
build "DTLS-only"
scripts/config.pl set MBEDTLS_SSL_PROTO_TLS
scripts/config.pl unset MBEDTLS_SSL_PROTO_DTLS
scripts/config.pl unset MBEDTLS_SSL_DTLS_HELLO_VERIFY
scripts/config.pl unset MBEDTLS_SSL_DTLS_ANTI_REPLAY
scripts/config.pl unset MBEDTLS_SSL_DTLS_BADMAC_LIMIT
scripts/config.pl unset MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE
build "TLS-only"
git checkout -- include/mbedtls/config.h
```
The output of the script is as follows:
```
both
text data bss dec hex filename
1820 0 4 1824 720 debug.o (ex library/libmbedtls.a)
0 0 0 0 0 net_sockets.o (ex library/libmbedtls.a)
548 0 0 548 224 ssl_cache.o (ex library/libmbedtls.a)
11155 0 596 11751 2de7 ssl_ciphersuites.o (ex library/libmbedtls.a)
17160 0 0 17160 4308 ssl_cli.o (ex library/libmbedtls.a)
460 0 0 460 1cc ssl_cookie.o (ex library/libmbedtls.a)
17637 0 0 17637 44e5 ssl_srv.o (ex library/libmbedtls.a)
800 0 0 800 320 ssl_ticket.o (ex library/libmbedtls.a)
39322 60 0 39382 99d6 ssl_tls.o (ex library/libmbedtls.a)
88902 60 600 89562 15dda (TOTALS)
DTLS-only
text data bss dec hex filename
1820 0 4 1824 720 debug.o (ex library/libmbedtls.a)
0 0 0 0 0 net_sockets.o (ex library/libmbedtls.a)
548 0 0 548 224 ssl_cache.o (ex library/libmbedtls.a)
11155 0 596 11751 2de7 ssl_ciphersuites.o (ex library/libmbedtls.a)
17072 0 0 17072 42b0 ssl_cli.o (ex library/libmbedtls.a)
460 0 0 460 1cc ssl_cookie.o (ex library/libmbedtls.a)
17565 0 0 17565 449d ssl_srv.o (ex library/libmbedtls.a)
800 0 0 800 320 ssl_ticket.o (ex library/libmbedtls.a)
38953 60 0 39013 9865 ssl_tls.o (ex library/libmbedtls.a)
88373 60 600 89033 15bc9 (TOTALS)
TLS-only
text data bss dec hex filename
1820 0 4 1824 720 debug.o (ex library/libmbedtls.a)
0 0 0 0 0 net_sockets.o (ex library/libmbedtls.a)
548 0 0 548 224 ssl_cache.o (ex library/libmbedtls.a)
11155 0 596 11751 2de7 ssl_ciphersuites.o (ex library/libmbedtls.a)
14916 0 0 14916 3a44 ssl_cli.o (ex library/libmbedtls.a)
460 0 0 460 1cc ssl_cookie.o (ex library/libmbedtls.a)
15852 0 0 15852 3dec ssl_srv.o (ex library/libmbedtls.a)
800 0 0 800 320 ssl_ticket.o (ex library/libmbedtls.a)
27623 60 0 27683 6c23 ssl_tls.o (ex library/libmbedtls.a)
73174 60 600 73834 1206a (TOTALS)
```
It can be seen that a DTLS-only build is now starting to be a bit smaller than
a dual-mode build, which is the purpose of the new build option.
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.
When MBEDTLS_SSL_ENCRYPT_THEN_MAC is enabled, but not
MBEDTLS_SSL_SOME_MODES_USE_MAC, mbedtls_ssl_derive_keys() and
build_transforms() will attempt to use a non-existent `encrypt_then_mac`
field in the ssl_transform.
Compile [ 93.7%]: ssl_tls.c
[Error] ssl_tls.c@865,14: 'mbedtls_ssl_transform {aka struct mbedtls_ssl_transform}' ha
s no member named 'encrypt_then_mac'
[ERROR] ./mbed-os/features/mbedtls/src/ssl_tls.c: In function 'mbedtls_ssl_derive_keys'
:
./mbed-os/features/mbedtls/src/ssl_tls.c:865:14: error: 'mbedtls_ssl_transform {aka str
uct mbedtls_ssl_transform}' has no member named 'encrypt_then_mac'
transform->encrypt_then_mac = session->encrypt_then_mac;
^~
Change mbedtls_ssl_derive_keys() and build_transforms() to only access
`encrypt_then_mac` if `encrypt_then_mac` is actually present. Fix any
unused variable warnings along the way, by additionally wrapping
function parameters with MBEDTLS_SSL_SOME_MODES_USE_MAC.
Add a regression test to detect when we have regressions with
configurations that do not include any MAC ciphersuites.
Fixes 92231325a7 ("Reduce size of `ssl_transform` if no MAC ciphersuite is enabled")
This commit introduces a new SSL error code
`MBEDTLS_ERR_SSL_VERSION_MISMATCH`
which can be used to indicate operation failure due to a
mismatch of version or configuration.
It is put to use in the implementation of `mbedtls_ssl_session_load()`
to signal the attempt to de-serialize a session which has been serialized
in a build of Mbed TLS using a different version or configuration.
This commit makes use of the added space in the session header to
encode the state of those parts of the compile-time configuration
which influence the structure of the serialized session in the
present version of Mbed TLS. Specifically, these are
- the options which influence the presence/omission of fields
from mbedtls_ssl_session (which is currently shallow-copied
into the serialized session)
- the setting of MBEDTLS_X509_CRT_PARSE_C, which determines whether
the serialized session contains a CRT-length + CRT-value pair after
the shallow-copied mbedtls_ssl_session instance.
- the setting of MBEDTLS_SSL_SESSION_TICKETS, which determines whether
the serialized session contains a session ticket.
This commit adds space for two bytes in the header of serizlied
SSL sessions which can be used to determine the structure of the
remaining serialized session in the respective version of Mbed TLS.
Specifically, if parts of the session depend on whether specific
compile-time options are set or not, the setting of these options
can be encoded in the added space.
This commit doesn't yet make use of the fields.
The format of serialized SSL sessions depends on the version and the
configuration of Mbed TLS; attempts to restore sessions established
in different versions and/or configurations lead to undefined behaviour.
This commit adds an 3-byte version header to the serialized session
generated and cleanly fails ticket parsing in case a session from a
non-matching version of Mbed TLS is presented.
We have explicit recommendations to use US spelling for technical writing, so
let's apply this to code as well for uniformity. (My fingers tend to prefer UK
spelling, so this needs to be fixed in many places.)
sed -i 's/\([Ss]eriali\)s/\1z/g' **/*.[ch] **/*.function **/*.data ChangeLog
This uncovered a bug that led to a double-free (in practice, in general could
be free() on any invalid value): initially the session structure is loaded
with `memcpy()` which copies the previous values of pointers peer_cert and
ticket to heap-allocated buffers (or any other value if the input is
attacker-controlled). Now if we exit before we got a chance to replace those
invalid values with valid ones (for example because the input buffer is too
small, or because the second malloc() failed), then the next call to
session_free() is going to call free() on invalid pointers.
This bug is fixed in this commit by always setting the pointers to NULL right
after they've been read from the serialised state, so that the invalid values
can never be used.
(An alternative would be to NULL-ify them when writing, which was rejected
mostly because we need to do it when reading anyway (as the consequences of
free(invalid) are too severe to take any risk), so doing it when writing as
well is redundant and a waste of code size.)
Also, while thinking about what happens in case of errors, it became apparent
to me that it was bad practice to leave the session structure in an
half-initialised state and rely on the caller to call session_free(), so this
commit also ensures we always clear the structure when loading failed.
This allows callers to discover what an appropriate size is. Otherwise they'd
have to either try repeatedly, or allocate an overly large buffer (or some
combination of those).
Adapt documentation an example usage in ssl_client2.
Avoid useless copy with mbedtls_ssl_get_session() before serialising.
Used in ssl_client2 for testing and demonstrating usage, but unfortunately
that means mbedtls_ssl_get_session() is no longer tested, which will be fixed
in the next commit.
On client side, this is required for the main use case where of serialising a
session for later resumption, in case tickets are used.
On server side, this doesn't change much as ticket_len will always be 0.
This unblocks testing the functions by using them in ssl_client2, which will
be done in the next commit.
This finishes making these functions public. Next step is to get them tested,
but there's currently a blocker for that, see next commit (and the commit
after it for tests).
This commit modifies mbedtls_ssl_get_peer_cid() to also allow passing
NULL pointers in the arguments for the peer's CID value and length, in
case this information is needed.
For example, some users might only be interested in whether the use of
the CID was negotiated, in which case both CID value and length pointers
can be set to NULL. Other users might only be interested in confirming
that the use of CID was negotiated and the peer chose the empty CID,
in which case the CID value pointer only would be set to NULL.
It doesn't make sense to pass a NULL pointer for the CID length but a
non-NULL pointer for the CID value, as the caller has no way of telling
the length of the returned CID - and this case is therefore forbidden.
This commit modifies the CID configuration API mbedtls_ssl_conf_cid_len()
to allow the configuration of the stack's behaviour when receiving an
encrypted DTLS record with unexpected CID.
There are two options:
1. Don't set it, and don't use it during record protection,
guarding the respective paths by a check whether TLS or
DTLS is used.
2. Set it to the default value even for TLS, and avoid the
protocol-dependent branch during record protection.
This commit picks option 2.
This commit changes the stack's behaviour when facing a record
with a non-matching CID. Previously, the stack failed in this
case, while now we silently skip over the current record.
Previously, ssl_get_next_record() would fetch 13 Bytes for the
record header and hand over to ssl_parse_record_header() to parse
and validate these. With the introduction of CID-based records, the
record length is not known in advance, and parsing and validating
must happen at the same time. ssl_parse_record_header() is therefore
rewritten in the following way:
1. Fetch and validate record content type and version.
2. If the record content type indicates a record including a CID,
adjust the record header pointers accordingly; here, we use the
statically configured length of incoming CIDs, avoiding any
elaborate CID parsing mechanism or dependency on the record
epoch, as explained in the previous commit.
3. Fetch the rest of the record header (note: this doesn't actually
fetch anything, but makes sure that the datagram fetched in the
earlier call to ssl_fetch_input() contains enough data).
4. Parse and validate the rest of the record header as before.
This commit modifies the code surrounding the invocations of
ssl_decrypt_buf() and ssl_encrypt_buf() to deal with a change
of record content type during CID-based record encryption/decryption.
mbedtls_ssl_context contains pointers in_buf, in_hdr, in_len, ...
which point to various parts of the header of an incoming TLS or
DTLS record; similarly, there are pointers out_buf, ... for
outgoing records.
This commit adds fields in_cid and out_cid which point to where
the CID of incoming/outgoing records should reside, if present,
namely prior to where the record length resides.
Quoting https://tools.ietf.org/html/draft-ietf-tls-dtls-connection-id-04:
The DTLSInnerPlaintext value is then encrypted and the CID added to
produce the final DTLSCiphertext.
struct {
ContentType special_type = tls12_cid; /* 25 */
ProtocolVersion version;
uint16 epoch;
uint48 sequence_number;
opaque cid[cid_length]; // New field
uint16 length;
opaque enc_content[DTLSCiphertext.length];
} DTLSCiphertext;
For outgoing records, out_cid is set in ssl_update_out_pointers()
based on the settings in the current outgoing transform.
For incoming records, ssl_update_in_pointers() sets in_cid as if no
CID was present, and it is the responsibility of ssl_parse_record_header()
to update the field (as well as in_len, in_msg and in_iv) when parsing
records that do contain a CID. This will be done in a subsequent commit.
Finally, the code around the invocations of ssl_decrypt_buf()
and ssl_encrypt_buf() is adapted to transfer the CID from the
input/output buffer to the CID field in the internal record
structure (which is what ssl_{encrypt/decrypt}_buf() uses).
Note that mbedtls_ssl_in_hdr_len() doesn't need change because
it infers the header length as in_iv - in_hdr, which will account
for the CID for records using such.
Using the Connection ID extension increases the maximum record expansion
because
- the real record content type is added to the plaintext
- the plaintext may be padded with an arbitrary number of
zero bytes, in order to prevent leakage of information
through package length analysis. Currently, we always
pad the plaintext in a minimal way so that its length
is a multiple of 16 Bytes.
This commit adapts the various parts of the library to account
for that additional source of record expansion.
Context:
The CID draft does not require that the length of CIDs used for incoming
records must not change in the course of a connection. Since the record
header does not contain a length field for the CID, this means that if
CIDs of varying lengths are used, the CID length must be inferred from
other aspects of the record header (such as the epoch) and/or by means
outside of the protocol, e.g. by coding its length in the CID itself.
Inferring the CID length from the record's epoch is theoretically possible
in DTLS 1.2, but it requires the information about the epoch to be present
even if the epoch is no longer used: That's because one should silently drop
records from old epochs, but not the entire datagrams to which they belong
(there might be entire flights in a single datagram, including a change of
epoch); however, in order to do so, one needs to parse the record's content
length, the position of which is only known once the CID length for the epoch
is known. In conclusion, it puts a significant burden on the implementation
to infer the CID length from the record epoch, which moreover mangles record
processing with the high-level logic of the protocol (determining which epochs
are in use in which flights, when they are changed, etc. -- this would normally
determine when we drop epochs).
Moreover, with DTLS 1.3, CIDs are no longer uniquely associated to epochs,
but every epoch may use a set of CIDs of varying lengths -- in that case,
it's even theoretically impossible to do record header parsing based on
the epoch configuration only.
We must therefore seek a way for standalone record header parsing, which
means that we must either (a) fix the CID lengths for incoming records,
or (b) allow the application-code to configure a callback to implement
an application-specific CID parsing which would somehow infer the length
of the CID from the CID itself.
Supporting multiple lengths for incoming CIDs significantly increases
complexity while, on the other hand, the restriction to a fixed CID length
for incoming CIDs (which the application controls - in contrast to the
lengths of the CIDs used when writing messages to the peer) doesn't
appear to severely limit the usefulness of the CID extension.
Therefore, the initial implementation of the CID feature will require
a fixed length for incoming CIDs, which is what this commit enforces,
in the following way:
In order to avoid a change of API in case support for variable lengths
CIDs shall be added at some point, we keep mbedtls_ssl_set_cid(), which
includes a CID length parameter, but add a new API mbedtls_ssl_conf_cid_len()
which applies to an SSL configuration, and which fixes the CID length that
any call to mbetls_ssl_set_cid() which applies to an SSL context that is bound
to the given SSL configuration must use.
While this creates a slight redundancy of parameters, it allows to
potentially add an API like mbedtls_ssl_conf_cid_len_cb() later which
could allow users to register a callback which dynamically infers the
length of a CID at record header parsing time, without changing the
rest of the API.
The function mbedtls_ssl_hdr_len() returns the length of the record
header (so far: always 13 Bytes for DTLS, and always 5 Bytes for TLS).
With the introduction of the CID extension, the lengths of record
headers depends on whether the records are incoming or outgoing,
and also on the current transform.
Preparing for this, this commit splits mbedtls_ssl_hdr_len() in two
-- so far unmodified -- functions mbedtls_ssl_in_hdr_len() and
mbedtls_ssl_out_hdr_len() and replaces the uses of mbedtls_ssl_hdr_len()
according to whether they are about incoming or outgoing records.
There is no need to change the signature of mbedtls_ssl_{in/out}_hdr_len()
in preparation for its dependency on the currently active transform,
since the SSL context is passed as an argument, and the currently
active transform is referenced from that.
With the introduction of the CID feature, the stack needs to be able
to handle a change of record content type during record protection,
which in particular means that the record content type check will
need to move or be duplicated.
This commit introduces a tiny static helper function which checks
the validity of record content types, which hopefully makes it
easier to subsequently move or duplicate this check.
With the introduction of the CID extension, the record content type
may change during decryption; we must therefore re-consider every
record content type check that happens before decryption, and either
move or duplicate it to ensure it also applies to records whose
real content type is only revealed during decryption.
This commit does this for the silent dropping of unexpected
ApplicationData records in DTLS. Previously, this was caught
in ssl_parse_record_header(), returning
MBEDTLS_ERR_SSL_UNEXPECTED_RECORD which in ssl_get_next_record()
would lead to silent skipping of the record.
When using CID, this check wouldn't trigger e.g. when delayed
encrypted ApplicationData records come on a CID-based connection
during a renegotiation.
This commit moves the check to mbedtls_ssl_handle_message_type()
and returns MBEDTLS_ERR_SSL_NON_FATAL if it triggers, which leads
so silent skipover in the caller mbedtls_ssl_read_record().
The SSL context structure mbedtls_ssl_context contains several pointers
ssl->in_hdr, ssl->in_len, ssl->in_iv, ssl->in_msg pointing to various
parts of the record header in an incoming record, and they are setup
in the static function ssl_update_in_pointers() based on the _expected_
transform for the next incoming record.
In particular, the pointer ssl->in_msg is set to where the record plaintext
should reside after record decryption, and an assertion double-checks this
after each call to ssl_decrypt_buf().
This commit removes the dependency of ssl_update_in_pointers() on the
expected incoming transform by setting ssl->in_msg to ssl->in_iv --
the beginning of the record content (potentially including the IV) --
and adjusting ssl->in_msg after calling ssl_decrypt_buf() on a protected
record.
Care has to be taken to not load ssl->in_msg before calling
mbedtls_ssl_read_record(), then, which was previously the
case in ssl_parse_server_hello(); the commit fixes that.
If a record exhibits an invalid feature only after successful
authenticated decryption, this is a protocol violation by the
peer and should hence lead to connection failure. The previous
code, however, would silently ignore such records. This commit
fixes this.
So far, the only case to which this applies is the non-acceptance
of empty non-AD records in TLS 1.2. With the present commit, such
records lead to connection failure, while previously, they were
silently ignored.
With the introduction of the Connection ID extension (or TLS 1.3),
this will also apply to records whose real content type -- which
is only revealed during authenticated decryption -- is invalid.
In contrast to other aspects of the Connection ID extension,
the CID-based additional data for MAC computations differs from
the non-CID case even if the CID length is 0, because it
includes the CID length.
Quoting the CID draft 04:
- Block Ciphers:
MAC(MAC_write_key, seq_num +
tls12_cid + // New input
DTLSPlaintext.version +
cid + // New input
cid_length + // New input
length_of_DTLSInnerPlaintext + // New input
DTLSInnerPlaintext.content + // New input
DTLSInnerPlaintext.real_type + // New input
DTLSInnerPlaintext.zeros // New input
)
And similar for AEAD and Encrypt-then-MAC.
While 'session hash' is currently unique, so suitable to prove that the
intended code path has been taken, it's a generic enough phrase that in the
future we might add other debug messages containing it in completely unrelated
code paths. In order to future-proof the accuracy of the test, let's use a
more specific string.
The previous comment used "TLS" as a shortcut for "TLS 1.0/1.1" which was
confusing. This partially reflected the names of the calc_verify/finished that
go ssl, tls (for 1.0/1.1) tls_shaxxx (for 1.2), but still it's clearer to be
explicit in the comment - and perhaps in the long term the function names
could be clarified instead.
This commit temporarily comments the copying of the negotiated CIDs
into the established ::mbedtls_ssl_transform in mbedtls_ssl_derive_keys()
until the CID feature has been fully implemented.
While mbedtls_ssl_decrypt_buf() and mbedtls_ssl_encrypt_buf() do
support CID-based record protection by now and can be unit tested,
the following two changes in the rest of the stack are still missing
before CID-based record protection can be integrated:
- Parsing of CIDs in incoming records.
- Allowing the new CID record content type for incoming records.
- Dealing with a change of record content type during record
decryption.
Further, since mbedtls_ssl_get_peer_cid() judges the use of CIDs by
the CID fields in the currently transforms, this change also requires
temporarily disabling some grepping for ssl_client2 / ssl_server2
debug output in ssl-opt.sh.
This commit modifies ssl_decrypt_buf() and ssl_encrypt_buf()
to include the CID into authentication data during record
protection.
It does not yet implement the new DTLSInnerPlaintext format
from https://tools.ietf.org/html/draft-ietf-tls-dtls-connection-id-04
When using this function to deserialize, it's not a problem to have a session
structure as input as we'll have one around anyway (most probably freshly
deserialised).
However for tests it's convenient to be able to build a transform without
having a session structure around.
Also, removing this structure from parameters makes the function signature
more uniform, the only exception left being the ssl param at the end that's
hard to avoid for now.
Configs with no DEBUG_C are use for example in test-ref-configs.pl, which also
runs parts of compat.sh or ssl-opt.sh on them, so the added 'ssl = NULL'
statements will be exercised in those tests at least.