This commit makes a first step towards modularizing the incoming record
processing by having it operate on instances of the structure mbedtls_record
representing SSL records.
So far, only record encryption/decryption operate in terms of record
instances, but the rest of the parsing doesn't. In particular,
ssl_parse_record_header() operates directly on the fixed input buffer,
setting the various ssl->in_xxx pointers and fields, and only directly
before/after calling ssl_decrypt_buf() these fields a converted to/from
mbedtls_record instances.
This commit does not yet remove the ssl->in_xxx fields, but makes a step
towards extending the lifetime of mbedtls_record structure representing
incoming records, by modifying ssl_parse_record_header() to setup an
instance of mbedtls_record, and setting the ssl->in_xxx fields from that
instance. The instance so-constructed isn't used further so far, and in
particular it is not yet consolidated with the instance set up for use
in ssl_decrypt_record(). That's for a later commit.
Previously, ssl_parse_record_header() did not check whether the current
datagram is large enough to hold a record of the advertised size. This
could lead to records being silently skipped over or backed up on the
basis of an invalid record length. Concretely, the following would happen:
1) In the case of a record from an old epoch, the record would be
'skipped over' by setting next_record_offset according to the advertised
but non-validated length, and only in the subsequent mbedtls_ssl_fetch_input()
it would be noticed in an assertion failure if the record length is too
large for the current incoming datagram.
While not critical, this is fragile, and also contrary to the intend
that MBEDTLS_ERR_SSL_INTERNAL_ERROR should never be trigger-able by
external input.
2) In the case of a future record being buffered, it might be that we
backup a record before we have validated its length, hence copying
parts of the input buffer that don't belong to the current record.
This is a bug, and it's by luck that it doesn't seem to have critical
consequences.
This commit fixes this by modifying ssl_parse_record_header() to check that
the current incoming datagram is large enough to hold a record of the
advertised length, returning MBEDTLS_ERR_SSL_INVALID_RECORD otherwise.
We don't send alerts on other instances of ill-formed records,
so why should we do it here? If we want to keep it, the alerts
should rather be sent ssl_get_next_record().
As explained in the previous commit, if mbedtls_ssl_fetch_input()
is called multiple times, all but the first call are equivalent to
bounds checks in the incoming datagram.
In DTLS, if mbedtls_ssl_fetch_input() is called multiple times without
resetting the input buffer in between, the non-initial calls are functionally
equivalent to mere bounds checks ensuring that the incoming datagram is
large enough to hold the requested data. In the interest of code-size
and modularity (removing a call to a non-const function which is logically
const in this instance), this commit replaces such a call to
mbedtls_ssl_fetch_input() by an explicit bounds check in
ssl_parse_record_header().
Previously, `ssl_handle_possible_reconnect()` was part of
`ssl_parse_record_header()`, which was required to return a non-zero error
code to indicate a record which should not be further processed because it
was invalid, unexpected, duplicate, .... In this case, some error codes
would lead to some actions to be taken, e.g. `MBEDTLS_ERR_SSL_EARLY_MESSAGE`
to potential buffering of the record, but eventually, the record would be
dropped regardless of the precise value of the error code. The error code
`MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED` returned from
`ssl_handle_possible_reconnect()` did not receive any special treatment and
lead to silent dopping of the record - in particular, it was never returned
to the user.
In the new logic this commit introduces, `ssl_handle_possible_reconnect()` is
part of `ssl_check_client_reconnect()` which is triggered _after_
`ssl_parse_record_header()` found an unexpected record, which is already in
the code-path eventually dropping the record; we want to leave this code-path
only if a valid cookie has been found and we want to reset, but do nothing
otherwise. That's why `ssl_handle_possible_reconnect()` now returns `0` unless
a valid cookie has been found or a fatal error occurred.
Availability of sufficient incoming data should be checked when
it is needed, which is in mbedtls_ssl_fetch_input(), and this
function has the necessary bounds checks in place.
mbedtls_ssl_decrypt_buf() asserts that the passed transform is not NULL,
but the function is only invoked in a single place, and this invocation
is clearly visible to be within a branch ensuring that the incoming
transform isn't NULL. Remove the assertion for the benefit of code-size.
The previous code performed architectural maximum record length checks
both before and after record decryption. Since MBEDTLS_SSL_IN_CONTENT_LEN
bounds the maximum length of the record plaintext, it suffices to check
only once after (potential) decryption.
This must not be confused with the internal check that the record
length is small enough to make the record fit into the internal input
buffer; this is done in mbedtls_ssl_fetch_input().
The check is in terms of the internal input buffer length and is
hence likely to be originally intended to protect against overflow
of the input buffer when fetching data from the underlying
transport in mbedtls_ssl_fetch_input(). For locality of reasoning,
it's better to perform such a check close to where it's needed,
and in fact, mbedtls_ssl_fetch_input() _does_ contain an equivalent
bounds check, too, rendering the bounds check in question redundant.
Breaking into a series of statements makes things easier when stepping through
the code in a debugger.
Previous comments we stating the opposite or what the code tested for (what we
want vs what we're erroring out on) which was confusing.
Also expand a bit on the reasons for these restrictions.
ssl_get_next_record() may pend fatal alerts in response to receiving
invalid records. Previously, however, those were never actually sent
because there was no code-path checking for pending alerts.
This commit adds a call to ssl_send_pending_fatal_alert() after
the invocation of ssl_get_next_record() to fix this.
Modelled after the config-checking header from session s11n.
The list of relevant config flags was established by manually checking the
fields serialized in the format, and which config.h flags they depend on.
This probably deserves double-checking by reviewers.
Since the type of cid_len is unsigned but shorter than int, it gets
"promoted" to int (which is also the type of the result), unless we make the
other operand an unsigned int which then forces the expression to unsigned int
as well.
The number of meaning of the flags will be determined later, when handling the
relevant struct members. For now three bytes are reserved as an example, but
this number may change later.
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.