This commit replaces the previous calls to `mbedtls_x509_name_cmp()`
during CRT verification (to match child and parent, to check whether
a CRT is self-issued, and to match CRLs and CAs) by calls to the new
`mbedtls_x509_name_cmp_raw()` using the raw ASN.1 data; it passes the
raw buffers introduced in the last commits.
The previous name comparison function mbedtls_x509_name_cmp() is now
both unused and unneeded, and is removed.
To make use of the X.509 name comparison function based on raw
ASN.1 data that was introduced in the previous commit, this commit
adds an ASN.1 buffer field `issuer_raw_no_hdr` to `mbedtls_x509_crl`
which delimits the raw contents of the CRLs `Issuer` field.
The previous field `issuer_raw` isn't suitable for that because
it includes the ASN.1 header.
This commit provides a new function `mbedtls_x509_name_cmp_raw()`
to x509.c for comparing to X.509 names by traversing the raw ASN.1
data (as opposed to using the dynamically allocated linked list
of `mbedtls_x509_name` structures). It has external linkage because
it will be needed in `x509_crt` and `x509_crl`, but is marked
internal and hence not part of the public API.
The function `mbedtls_x509_sig_alg_gets()` previously needed the
raw ASN.1 OID string even though it is implicit in the PK and MD
parameters.
This commit modifies `mbedtls_x509_sig_alg_gets()` to infer the OID
and remove it from the parameters.
This will be needed for the new X.509 CRT structure which will
likely not store the signature OID.
Care has to be taken to handle the case of RSASSA-PSS correctly,
where the hash algorithm in the OID list is set to MBEDTLS_MD_NONE
because it's only determined by the algorithm parameters.
The previous code
- checked that at least 1 byte of ASN.1 tag data is available,
- read and stored that ASN.1 tag,
- called the ASN.1 parsing function, part of which is checking
that enough space is available and that the ASN.1 tag matches
the expected value MBEDTLS_ASN1_OID.
Since the ASN.1 parsing function includes bounds checks,
this can be streamlined to:
- call the ASN.1 parsing function directly,
- on success, store MBEDTLS_ASN1_OID in the tag field.
This commit applies this simplification to mbedtls_asn1_get_alg().
Consider the following code-template:
int beef();
static int foo()
{
/* ... */
ret = beef();
if( ret != 0 )
return( ret + HIGH_LEVEL );
/* ... */
}
int bar()
{
/* ... */
ret = foo();
if( ret != 0 )
...
/* ... */
}
This leads to slightly larger code than expected, because when the
compiler inlines foo() into bar(), the sequence of return sequences
cannot be squashed, because compiler might not have knowledge that
the wrapping `ret + HIGH_LEVEL` of the return value of beef() doesn't
lead to foo() returning 0.
This can be avoided by performing error code wrapping in nested
functions calls at the top of the call chain.
This commit applies this slight optimization to mbedtls_x509_get_name().
It also moves various return statements into a single exit section,
again with the intend to save code.
X.509 names in ASN.1 are encoded as ASN.1 SEQUENCEs of ASN.1 SETs
of Attribute-Value pairs, one for each component in the name. (For
example, there could be an Attribute-Value pair for "DN=www.mbedtls.org").
So far, `mbedtls_x509_get_name()` parsed such names by two nested
loops, the outer one traversing the outer ASN.1 SEQUENCE and the
inner one the ASN.1 SETs.
This commit introduces a helper function `x509_set_sequence_iterate()`
which implements an iterator through an ASN.1 name buffer; the state
of the iterator is a triple consisting of
- the current read pointer
- the end of the current SET
- the end of the name buffer
The iteration step reads a new SET if the current read pointer has
reached the end of the current SET, and afterwards reads the next
AttributeValue pair.
This way, iteration through the X.509 name data can be implemented
in a single loop, which increases readability and slightly reduces
the code-size.
This commit introduces a macro `MBEDTLS_ASN1_IS_STRING_TAG`
that can be used to check if an ASN.1 tag is among the list
of string tags:
- MBEDTLS_ASN1_BMP_STRING
- MBEDTLS_ASN1_UTF8_STRING
- MBEDTLS_ASN1_T61_STRING
- MBEDTLS_ASN1_IA5_STRING
- MBEDTLS_ASN1_UNIVERSAL_STRING
- MBEDTLS_ASN1_PRINTABLE_STRING
- MBEDTLS_ASN1_BIT_STRING
`x509_get_attr_type_value()` checks for the presence of a tag byte
and reads and stores it before calling `mbedtls_asn1_get_tag()` which
fails if either the tag byte is not present or not as expected. Therefore,
the manual check can be removed and left to `mbedtls_asn1_get_tag()`, and
the tag can be hardcoded after the call succeeded. This saves a few bytes
of code.
The server-side routine `ssl_pick_cert()` is responsible for
picking a suitable CRT from the list of CRTs configured on the
server. For that, it previously used the public key context
from the certificate to check whether its type (including the
curve type for ECC keys) suits the ciphersuite and the client's
preferences.
This commit changes the code to instead use the PK context
holding the corresponding private key. For inferring the type
of the key, this makes no difference, and it removes a PK-from-CRT
extraction step which, if CRTs are stored raw, is costly in terms
of computation and memory: CRTs need to be parsed, and memory needs
to be allocated for the PK context.
The server-side routine `ssl_decrypt_encrypted_pms()` is
responsible for decrypting the RSA-encrypted PMS in case of
an RSA-based ciphersuite.
Previously, the code checked that the length of the PMS sent
by the client matches the bit length of the RSA key. This commit
removes this check -- thereby removing the need to access the
server's own CRT -- because the RSA decryption routine performs
this check itself, too.
`mbedtls_x509_name` and `mbedtls_x509_sequence` are dynamically allocated
linked lists that need a loop to free properly. Introduce a static helper
function to do that and use it in `mbedtls_x509_crt_free()`, where the
CRT's issuer and subject names (of type `mbedtls_x509_name`) and the
SubjectAlternativeName and ExtendedKeyUsage extensions (of type
`mbedtls_x509_sequence`) need freeing. Increases code-clarity and saves
a few bytes of flash.
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.
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.
This commit adds an ASN.1 buffer field `pk_raw` to `mbedtls_x509_crt`
which stores the bounds of the raw public key data within an X.509 CRT.
This will be useful in subsequent commits to extract the peer's public
key from its certificate chain.
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.