Commit Graph
100 Commits
Author SHA1 Message Date
Michael Brown ed4a9b2e67 [tls] Clear any saved state when saving a new session
If the server switches from providing stateless session tickets to
providing stateful session IDs, then we will currently end up with a
stored session that retains the old session ticket along with the new
session ID.  (This behaviour has been observed when connecting to an
Apache server that requires per-directory client authentication or
per-directory cipher suites: the server originally provides a
stateless session ticket but then, after renegotiation to request the
client certificate, provides a stateful session ID instead.)

Reset both the session ID and the session ticket whenever a new
session is saved (i.e. if the server provides either a new session ID
or a new session ticket), so that we do not retain stale credentials.

If no new session ID is provided, then create a new random session ID
(rather than leaving the session with an empty session ID) since we
rely on the echoed non-empty session ID to determine whether or not
the server is choosing to resume, even for stateless sessions.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-21 17:48:06 +01:00
Michael Brown b36e8472d4 [tls] Use standalone TLS data structure builder
Define structure descriptors and mappings for every data structure
currently sent by the TLS protocol engine, and use the standalone data
structure builder to remove a large amount of open-coded layout and
assembly logic.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-21 00:23:03 +01:00
Michael Brown 2e549711c2 [tls] Use server certificate's own public key algorithm for verification
For TLS version 1.1 (without explicit signature hash algorithm
identifiers), we currently use the cipher suite's public key algorithm
when verifying the ServerKeyExchange signature.  This is indirectly
guaranteed to match the server certificate's own public key algorithm.

Use the server certificate's own public key algorithm directly, to be
consistent with the behaviour for client certificates.  The signed
certificate is, by definition, the authoritative source for its own
choice of public key algorithm.

This leaves the public key algorithm aspect of the cipher suite as
being unused outside of debug messages.  We do not enforce the
specification that the certificate's public key algorithm must match
the cipher suite's public key algorithm (if any) because doing so
serves no cryptographic purpose.  For TLS version 1.2 and later, the
explicit signature hash algorithm identifiers override the cipher
suite, and in TLS version 1.3 the whole concept of a public key
algorithm is removed from the scope of the cipher suite.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-20 20:33:15 +01:00
Michael Brown 69c5cbea77 [tls] Use client certificate's own public key algorithm for signing
Mirroring commit cbdb572 ("[crypto] Use certificate's own public key
algorithm for key matching"), the client CertificateVerify is also
currently constructed using the certificate's signature algorithm
(i.e. the public key algorithm of the issuer's key) rather than the
certificate's own public key algorithm.

Fix by using the certificate's own public key algorithm.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-20 15:11:50 +01:00
Michael Brown 76f7b88f93 [crypto] Allow for signable MD5 or SHA-1 digests with TLS version 1.1
For TLS version 1.1, non-RSA signed digests use SHA-1 instead of
MD5+SHA1.  Commit f095adb ("[tls] Use SHA-1 for TLS version 1.1 ECDSA
signatures") selected the correct digest algorithm for both server and
client authentication.

However, the key schedule currently refuses to generate client
CertificateVerify digests for any digest algorithm other than
MD5+SHA1, on the basis that only the MD5+SHA1 running transcript
digest value is available.

An MD5+SHA1 digest value is just the concatenation of an MD5 digest
value with a SHA-1 digest value, and so the SHA-1 digest value can be
provided for use with ECDSA client certificates.

Fix by special-casing the SHA-1 (and MD5) algorithms when generating a
signable digest value from the TLS version 1.1 key schedule.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-20 14:39:41 +01:00
Michael Brown cbdb57278d [crypto] Use certificate's own public key algorithm for key matching
When finding the certificate corresponding to a private key, the match
is currently performed using the certificate's signature algorithm
(i.e. the public key algorithm of the issuer's key) rather than the
certificate's own public key algorithm.  This breaks key matching for
heterogenous certificate chains (e.g. an ECDSA client certificate
issued by an RSA intermediate certificate).

Fix by using the certificate's own public key algorithm.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-20 14:39:41 +01:00
Michael Brown cff60d28d6 [build] Allow for non-RSA private keys
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-20 14:39:41 +01:00
Michael Brown b4d9949db1 [tls] Propagate sizing errors to containing data structures
The existing ad hoc code that builds TLS data structures generally
performs a single length calculation and a single allocation, and if
the allocation succeeds then the data structure will be filled in with
no possible further errors arising.

Allow this pattern to be replicated when using the generic builder, by
ensuring that an error in calculating the length of a contained data
structure (e.g. an extension) will automatically propagate that error
to the calculation of the length of the containing structure (e.g. a
ClientHello that contains the extension).

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-20 14:39:41 +01:00
Michael Brown 65450656e5 [tls] Add a standalone TLS data structure builder
Provide a generic builder that can construct an arbitrary TLS data
structure based upon the same descriptors and binary-encoded mappings
as used for the generic parser.

The design allows for the caller to either provide data in advance or
to write through the descriptor pointers after building the data
structure.  For example: this will allow the public key within a
ClientHello to be populated in situ within the key_share extension,
rather than requiring the caller to allocate and populate a temporary
buffer before building the ClientHello.

This necessitates removing the "const" from all pointers within the
descriptors, which is a worthwhile tradeoff for the sake of avoiding a
large number of temporary allocations.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-19 12:48:31 +01:00
Michael Brown 05fa853241 [tls] Allow for an empty list of extensions of interest
The binary encoding used by the TLS data structure parser currently
cannot correctly express the concept of an extensions field unless
there is at least one extension of interest.  An extensions field that
has zero extensions of interest ends up being encoded as just a
standard variable-length field.

This effectively makes any such extensions fields non-optional, since
the test for optionality can only detect fields with a non-zero number
of extensions of interest.  (This is a somewhat hypothetical issue,
since the only optional extension fields are found in ClientHello and
ServerHello, both of which have extensions of interest.)

Adjust the encoding to allow extensions fields to be identified even
if there are no extensions of interest therein.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-18 22:52:19 +01:00
Michael Brown 42826fa9d9 [build] Allow MD5+SHA1 algorithm to be discarded at link time
All symbol references to the MD5+SHA1 algorithm will be eliminated if
TLS_VERSION_MIN is set to a version greater than TLS version 1.1.

However, the dummy RSA digestInfo prefix is retained since it is a
linker table entry.  This dummy prefix object holds a pointer to the
MD5+SHA1 algorithm and so prevents it from being garbage collected by
the linker.

Remove the dummy empty RSA digestInfo prefix and instead allow a
digest to be encoded without any prefix for the MD5+SHA1 digest
algorithm.

Use a custom test to allow the MD5+SHA1 algorithm to be identified
without using a symbol reference to md5_sha1_algorithm, since even a
weak reference would suffice to cause the symbol to be retained once
some other object drags it in to the build, and this is essentially
guaranteed to happen: tlskey_md5_sha1 drags in md5_sha1_algorithm,
which causes any existing weak references to be promoted to strong
references, and those strong references remain even if tlskey_md5_sha1
is later garbage collected.

The custom test uses a one-byte sentinel in .bss to give us a unique
value to place in digest->priv: this can then be used to identify
MD5+SHA1 without any symbol reference to md5_sha1_algorithm.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-18 19:24:00 +01:00
Michael Brown f095adbed2 [tls] Use SHA-1 for TLS version 1.1 ECDSA signatures
We currently use the MD5+SHA1 algorithm for all signatures when using
TLS version 1.1.  This is incorrect for ECDSA (or for any non-RSA
public-key algorithm): these should instead use SHA-1.

Fix by using SHA-1 for any non-RSA public-key algorithm for TLS
version 1.1.

Provide a weak rsa_algorithm symbol to use in the comparison, to avoid
unconditionally dragging in RSA support.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-18 16:38:24 +01:00
Michael Brown 6deb51d66c [tls] Avoid dragging in MD5+SHA1 algorithm unconditionally
Use a version check to gate the selection of md5_sha1_algorithm as the
signature digest algorithm, since this can be optimised out at build
time if the minimum version has been configured to be higher than TLS
version 1.1.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-18 16:17:15 +01:00
Michael Brown 8402079a17 [tls] Use standalone TLS data structure parser
Use the standalone data structure parser to remove a large amount of
open-coded parsing and bounds checking logic.

We retain the simple open-coded check for the handshake data structure
itself, since tls_new_handshake() needs to be able to treat partially
received data as valid (and defer processing until enough data is
available to cover a complete handshake).

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-18 15:02:38 +01:00
Michael Brown 89cfb681a4 [tls] Add a standalone TLS data structure parser
iPXE originally supported only TLS version 1.0, which uses mostly
fixed-size data structures and required only a few mostly simple
bounds checks.

Over the years, the amount of open-coded parsing and bounds checking
code has grown gradually to the point that it comprises a substantial
portion of the overall TLS implementation.  This makes the code
difficult to read, and requires careful review to ensure that all of
the different parsing and bounds checking code is correct.

Define an abstraction for decomposing the component parts of a TLS
data structure into a descriptor structure comprising a sequence of
field data pointers and lengths, along with an efficient binary
encoding that can describe the mapping between the decomposition and
the raw data structure.

Provide a generic parser that can interpret the binary-encoded mapping
and populate the descriptor structure, along with mappings for every
data structure currently interpreted by the TLS protocol engine.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-18 15:01:14 +01:00
Michael Brown 1135b79660 [tls] Fix building with TLS_VERSION_MAX set to TLS version 1.1
Commit 356bb14 ("[tls] Detect version downgrade attacks") introduced a
build failure under -Werror and -Wtype-limits when TLS_VERSION_MAX is
set to TLS_VERSION_TLS_1_1 due to the constructed test that checks if
an unsigned integer is less than zero.

Downgrade attack detection is impossible anyway when the maximum
version offered is TLS version 1.1, and so this always-false test is
perfectly correct: the desired outcome is that the downgrade detection
is optimised out at build time.

Fix the build error by adjusting the comparison to be performed using
signed integers to avoid the -Wtype-limits check.  Add a separate
check that the maximum version is higher than TLS_VERSION_TLS_1_1 to
ensure that the whole downgrade detection code block is optimised out
as dead code if it cannot ever be reached.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-18 15:01:12 +01:00
Michael Brown 01081fd911 [libc] Set return type for byte-swapping macros regardless of endianness
When the byte-swapping macros are no-ops (i.e. when they match the
platform's native endianness), the type of the parameter is used
directly as the type of the expression.  This can result in the type
of the expression differing between little-endian and big-endian
platforms.

Fix by including a cast within the no-op variants, so that the type of
the expression is consistent across all platforms.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-18 12:52:57 +01:00
Michael Brown 7cd92e01d6 [tls] Handle TLS version 1.3 client Finished
The TLS version 1.3 client Finished is somewhat messy to handle: its
verify_data must be calculated with the key schedule still holding the
client handshake traffic secret, the application traffic secret must
be calculated before the client Finished is added to the transcript
digest, and the application traffic keys must be activated only after
sending the client Finished.  Since the action of sending the client
Finished also adds the client Finished to the transcript digest, this
necessitates an awkward sequence of events.  (A cleaner protocol
design might have chosen to derive the client application traffic
secret from the transcript digest up to and including the client
Finished.)

Perform the various necessary contortions to construct the client
Finished and to transition to using the application traffic secrets.

With this commit, TLS version 1.3 is functional for the first time.
It is not yet enabled by default, since there are still some missing
features such as session resumption.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-14 22:47:45 +01:00
Michael Brown 267445294a [tls] Do not schedule sending of unused records in TLS version 1.3
The ClientKeyExchange record does not exist in TLS version 1.3 (since
key exchange happens instead via ClientHello).

The Change Cipher record does not exist in TLS version 1.3, at least
not in the form of something that can be transmitted via the normal
active cipher.

Skip scheduling both of these records for transmission.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-14 22:42:55 +01:00
Michael Brown ba64f5d2fb [tls] Ignore TLS version 1.3 NewSessionTicket
Session resumption in TLS version 1.3 is structurally different from
TLS version 1.2, and will not initially be supported.

Ignore any NewSessionTicket records for now, since they will otherwise
cause the connection to be aborted.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-14 22:34:44 +01:00
Michael Brown 78851666b3 [tls] Handle TLS version 1.3 server Finished
When a certificate chain is provided, the TLS version 1.3 server
Finished provides the point at which we can start validating the
certificate chain, equivalent to the ServerHelloDone in TLS version
1.2 and earlier.

The TLS version 1.3 server Finished also provides a convenient point
at which we can calculate the master secret, and defines the point at
which we must schedule the receive cipher to transition to using the
application traffic key.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-14 22:31:20 +01:00
Michael Brown 8087d6ac22 [tls] Transition to handshake traffic keys after receiving ServerHello
The ServerHello provides the earliest point at which the handshake
traffic keys can be generated, and the defined point at which the
receive cipher must transition to using the handshake traffic key.

We do not intend to support sending early data, and so this also
provides a convenient point at which to transition the tranmit cipher
to using the handshake traffic key.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-14 22:26:03 +01:00
Michael Brown 9077d45852 [tls] Allow for scheduled traffic phase changes
There are several point within the TLS version 1.3 handshake sequence
at which a handshake message handler needs to transition one or both
ciphers to a new traffic phase, but the new cipher keys cannot be
calculated by the key schedule until the triggering handshake message
has been added to the transcript digest.

Handshake messages are added to the transcript digest only after the
message handler returns, to accommodate the fact that the transcript
digest algorithm cannot be known until the initial ServerHello has
been processed.

Allow a new traffic phase to be recorded in the cipher specification,
which will be activated after the handshake message handlers have
returned.

Changing traffic phase requires changing the cipher in use, and so is
permitted only for the last handshake message in a handshake record.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-14 22:22:21 +01:00
Michael Brown 7655629db0 [tls] Allow for variable-length verification data
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-14 19:57:17 +01:00
Michael Brown c2e9bd951a [tls] Add support for binding via a CertificateVerify record
The format of the signature found within a CertificateVerify is
identical to the format of the signature within a ServerKeyExchange.

Abstract out the logic for verifying a ServerKeyExchange and use it to
verify the signature for both ServerKeyExchange and CertificateVerify.

Note that a CertificateVerify that is erroneously received under TLS
version 1.2 will always fail verification because the key schedule is
not able to generate a signable digest for the server endpoint.

A ServerKeyExchange that is erroneously received under TLS version 1.3
will fail validation because the TLS version 1.3 cipher suites provide
no way to parse the ServerKeyExchange parameters.  (An interestingly
deviant server that chooses to negotiate TLS version 1.3 with a TLS
version 1.2 cipher suite would be able to send a ServerKeyExchange
with a valid signature and have that key contribute accumulatively to
the key schedule: this would not conform to the protocol, but does not
actually weaken any of the security properties required to establish
the secure channel.)

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-14 18:22:49 +01:00
Michael Brown 14c23c5bb8 [tls] Add support for parsing TLS version 1.3 Certificate record
The TLS version 1.3 Certificate record includes a certificate request
context and an arbitrary list of extensions, both of which we ignore.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-14 16:23:11 +01:00
Michael Brown d646c92574 [tls] Handle inner plaintext for TLS version 1.3
TLS version 1.3 masquerades as TLS version 1.2 on the wire for the
benefit of badly engineered firewalls and other intermediate devices
that attempt to inspect the protocol stream.

Once a non-plaintext cipher is in use, all records masquerade as
application data, with the unencrypted record comprising the real
record content followed by the real type byte and an arbitrary amount
of zero padding.

Extract the inner plaintext on receive, and create the simplest
possible inner plaintext (with no zero padding) on transmit.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-14 16:16:46 +01:00
Michael Brown 47b934676a [tls] Allow support for newer TLS versions to be optimised out
The TLS version check already optimises down to a compile-time
constant if the specified version is guaranteed by the configured
minimum supported version.

Extend this check to also take into account the configured maximum
supported version.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-14 15:51:17 +01:00
Michael Brown 6872d143d6 [tls] Discard received Change Cipher records under TLS version 1.3
TLS version 1.3 allows unencrypted Change Cipher records to be sent
after switching to use the handshake traffic keys.  This is an ugly
protocol hack to work around badly implemented firewalls of the kind
beloved by large organisations.

Ignore and discard any such records, which would otherwise cause
decryption failures.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-14 13:56:59 +01:00
Michael Brown 4ac73a5cbb [tls] Exclude sequence number from authentication for TLS version 1.3
The authentication header used for TLS version 1.3 no longer includes
the sequence number, since the use of sequential initialisation
vectors renders it redundant.

Skip authenticating this portion of the authentication header for TLS
version 1.3 or later.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-14 13:56:25 +01:00
Michael Brown 35a1abdbf8 [tls] Allow for sequential cipher initialisation vectors
The CBC ciphers require a fully unpredictable initialisation vector,
which we currently generate as a channel ephemeral secret.  The GCM
ciphers require only a unique initialisation vector: there is no
requirement for it also to be unpredictable.  The content of the
record IV portion of the IV is a free choice of the sender, and we
currently use an unpredictable value for both CBC and GCM.

TLS version 1.3 removes the record IV portion for GCM ciphers, instead
constructing the IV by XORing the sequence number into the end of the
fixed IV.

Define the concept of a sequential initialisation vector as meaning
that the sequence number is XORed into the end of the overall
initialisation vector (which may be either the fixed IV or the record
IV portion), with no per-record unpredictable value required.  This
allows us to represent the mechanism required for TLS version 1.3, and
avoid the unnecessary cost of generating a channel ephemeral secret
for a GCM cipher under TLS version 1.2.

On the receive side, the XORed portion may be overwritten by the real
record IV, since the sender's choice is always definitive for the
contents of the record IV.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-14 13:55:08 +01:00
Michael Brown 84af36a21b [tls] Limit to TLS version 1.2 in transmitted record headers
TLS version 1.3 masquerades as TLS version 1.2 on the wire for the
benefit of badly engineered firewalls and other intermediate devices
that attempt to inspect the protocol stream.

Limit the maximum version in transmitted record headers to be TLS
version 1.2.  (Continue to accept any version in received record
headers, since this value has never had any significance.)

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-14 12:29:12 +01:00
Michael Brown 6f1646d7bd [tls] Remove the concept of a pending cipher specification
The cipher specifications are currently modelled as an active cipher
specification that corresponds to the cipher currently in use by the
secure channel abstraction, and a pending cipher specification that
corresponds to the cipher that will be swapped in after the next
ChangeCipherSpec.

This design reflects the wording of RFC 2246 through to RFC 5246:
"there are always four connection states outstanding: the current read
and write states, and the pending read and write states".

This model does not map well to TLS version 1.3, with its multiple
phases of traffic secrets and somewhat idiosyncratic choices of
transcript boundaries.  The client Finished message is a particular
problem: the client application traffic secret must be calculated
after constructing the client Finished verify_data but before adding
the client Finished to the transcript digest (i.e. before encrypting
it with the client handshake traffic keys).  This is an irritating
asymmetry with the server application traffic secret, which may be
calculated cleanly after the server Finished message has been added to
the transcript digest.

Switch to a model in which only the active cipher specification
exists, and always corresponds to the cipher currently in use by the
secure channel abstraction.

Move the record sequence number to become part of the cipher
specification, so that the sequence number reset logic can be shared
between the transmit and receive paths.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-14 00:39:50 +01:00
Michael Brown 76e53bc23f [tls] Add support for the key share extension
RFC 8446 defines the key_share extension as a mechanism for ephemeral
key exchange (replacing ServerKeyExchange and ClientKeyExchange).

Add support for sharing a key in our ClientHello and for parsing the
shared key from a ServerHello.  Construct the ClientHello on the heap
rather than on the stack, since the shared key values may be large
(e.g. for FFDHE4096).

Select the most preferred named group for sending the initial shared
key.  (We do not yet handle a HelloRetryRequest: if the server chooses
a different named group then we will record this group but do not yet
support sending the second ClientHello.)

We do not explicitly reject a key share extension received from a
server that negotiated TLS version 1.2 or lower.  Any such key will be
successfully used to establish a shared secret (and so the secure
channel will become keyed), but there is no way for this shared secret
to subsequently be successfully bound to the server's identity: a
ServerKeyExchange would replace the shared secret (since the TLS
version 1.2 key schedule is not accumulative), and a CertificateVerify
would fail to generate a signable digest.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-13 16:01:07 +01:00
Michael Brown aaccac9436 [tls] Add ClientHello to transcript before selecting named group
The TLS version 1.3 ClientHello includes a key_share extension whose
value will depend upon the selected key exchange named group.  The
incorporation of the initial ClientHello into the selected handshake
digest must therefore be done before the named group is potentially
modified.

Move responsibility for adding the initial ClientHello to the
transcript digest from tls_new_server_hello() to tls_select_cipher(),
so that this can be done before updating the selected named group.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-13 15:58:21 +01:00
Michael Brown d703617cc4 [tls] Handle TLS version 1.3 session ID echoing
RFC 8446 redefines the session ID within a TLS version 1.3 ServerHello
as being a field that must always echo the session ID from ClientHello
(and no longer indicates that session resumption is taking place), as
a workaround for badly engineered middleware boxes.

Skip ID-based session resumption if the negotiated version is TLS
version 1.3 or later, and instead abort the connection if the session
ID is not echoed verbatim (as per the RFC).

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-13 15:58:21 +01:00
Michael Brown 744d9e8b56 [tls] Use HKDF-based key schedule for TLS version 1.3 or later
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-13 15:58:21 +01:00
Michael Brown 30f0db898a [crypto] Refuse to allow zero-length ServerKeyExchange parameters
The signable digest value is used to bind the server identity to the
shared secret, and so the digest must be computed over the parameters
used to establish the shared secret.

For both endpoints in TLS version 1.3 and for the client endpoint in
TLS version 1.2, the digest is computed over the running transcript
hash and so already includes the parameters used to establish the
shared secret.

For the server endpoint in TLS version 1.2, the digest is computed
over only the client and server random bytes plus any additional data
passed in by the caller, and so this additional data must include the
parameters used to establish the shared secret.

The signable digest value for the server endpoint is currently
computed only in response to a ServerKeyExchange record, in which case
the additional data correctly contains the parameters from that record
that were used to establish the shared secret.

Adding support for TLS version 1.3 will necessitate adding the ability
to parse a received CertificateVerify record, which will attempt to
construct a signable digest value with no additional data.

Require additional data to be provided when constructing a signable
digest value for the server endpoint using the TLS version 1.2 key
schedule, to prevent a CertificateVerify from potentially being used
to verify a digest that was not computed over the parameters used to
establish the shared secret.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-13 15:58:17 +01:00
Michael Brown 7a3b423f69 [crypto] Ensure TLS signable digests cover a shared secret
The signable digest value is used to bind the server identity to the
shared secret, and so the digest must be computed over the parameters
used to establish the shared secret in order to be meaningful.

The secure channel will refuse to bind the peer identity on the basis
of a verified signable digest if the channel does not already contain
key material derived from a shared secret.

A signable digest that was erroneously constructed before a shared
secret was applied is therefore guaranteed to be unusable for binding
the channel, provided that the caller uses a sensible sequence of
operations (i.e. constructs the signable digest and then immediately
attempts to use it to bind the peer identity).

Strengthen this guarantee further by refusing to generate a signable
digest value unless the key schedule already contains key material.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-13 15:55:37 +01:00
Michael Brown 356bb14f33 [tls] Detect version downgrade attacks
RFC 8446 defines a mechanism that allows (but does not guarantee) the
detection of version downgrade attacks, based on magic signature
values placed within the ServerHello random bytes.  The magic
signature will be present if the server supports any version higher
than the negotiated version, and so may be present if the server
supports a higher version than we are offering.

The last byte of the magic signature is non-constant and is defined to
match the server's negotiated protocol version, encoded as a delta
from the value 0x0302 representing TLS version 1.1 (or lower).  Since
the server random bytes are always used in the construction of
verify_data (even in older versions of TLS without the extended master
secret), this encoding of the negotiated version cannot be forged by
an attacker.

If the version that is negotiated is lower than the version that we
offered (i.e. if a downgrade attack could possibly be happening), then
check for the range of magic signatures that could indicate a
downgrade attack, and terminate the connection if applicable.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-13 11:34:35 +01:00
Michael Brown f26cb52f16 [tls] Add support for the supported versions extension
RFC 8446 caps the version number field in ClientHello and ServerHello
to represent at most TLS version 1.2, as a workaround for badly
engineered servers and middleware boxes.  The actual protocol version
is instead negotiated via the supported_versions extension.

Send the list of supported versions in the ClientHello, and parse the
selected version from the supported_version extenion if present in the
ServerHello.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-13 11:27:04 +01:00
Michael Brown 79df48adae [tls] Add definitions for TLS version 1.3 cipher suites
RFC 8446 redefines the concept of a cipher suite for TLS version 1.3
to exclude the key exchange algorithm, leaving it specifying only the
block cipher algorithm and the handshake digest algorithm.

Add definitions for the two cipher suites that we can currently
support (TLS_AES_128_GCM_SHA256 and TLS_AES_256_GCM_SHA384).

We define these as using the null key exchange algorithm.  The null
key exchange algorithm will fail on any attempt at key agreement.  A
server that attempts to rely on the key exchange algorithm implied by
the cipher suite (e.g. a server attempting to illegally use these
cipher suites with TLS version 1.2) will therefore be unable to
establish a shared secret and so will not be able to cause the secure
channel to become established.

We therefore do not explicitly check for and reject a server's attempt
to negotiate a TLS version 1.3 cipher suite under TLS version 1.2 or
earlier: the secure channel abstraction already ensures that such a
negotiation is doomed to failure.

Under TLS version 1.3, the cipher suite's key exchange algorithm
specification will not be used.  We therefore do not explicitly check
for and reject a server's attempt to negotiate a TLS version 1.2 or
earlier cipher suite under TLS version 1.3 or later: we instead just
ignore the key exchange algorithm aspect of that cipher suite.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-13 11:26:40 +01:00
Michael Brown dbdbaa871c [tls] Record named group rather than key exchange algorithm
The concept of a named group is currently relevant only at the point
of parsing a ServerKeyExchange record to determine the key exchange
algorithm: once parsed, the group's numeric code is no longer required
and so we currently record only the resulting key exchange algorithm.

For TLS version 1.3, the numeric code will also be needed when
constructing the key_share extension in the ClientHello.

Switch from recording the key exchange algorithm to recording the
functionally equivalent named group, thereby making it possible to
retrieve the numeric code when needed.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-13 00:27:50 +01:00
Michael Brown ff6e52063e [crypto] Add AES acceleration using the Arm Cryptographic Extensions
Implement AES hardware acceleration for AArch64 using the AES subset
of the Cryptographic Extensions.  All supported runtime environments
already allow for use of the SIMD/FP registers, and so the only
required compiler quirk is to annotate the functions as being
permitted to emit the AES instructions.

Feature detection relies upon the ability to read the ID_AA64ISAR0_EL1
system register.  This works as expected in all supported runtime
environments:

  - As a UEFI binary, we are running in a real EL1 and so can just
    read the system register for the current (and only active) core

  - As a Linux userspace binary running in EL0, the kernel (since
    4.11) will emulate the read to report the subset of features that
    are supported by all online cores

  - As a Linux userspace binary run via QEMU's binary translation,
    QEMU (since 4.0.0) will similarly emulate the read to report the
    features supported by the selected CPU model

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-07 15:14:39 +01:00
Michael Brown 0214e4391a [crypto] Add support for AES-NI hardware acceleration
Implement AES hardware acceleration for i386 and x86_64, using the
AES-NI instructions (and the SSE2 "pxor" instruction for the initial
AddRoundKey), using the unmodified existing key schedule as generated
by aes_setkey().

For raw AES (ignoring the block cipher mode of operation), this
results in a speed improvement from approximately 20 cycles per byte
down to approximately 1 cycle per byte.

The AES-NI instructions use SSE registers.  For the sake of not having
to think about the possible consequences across all various runtime
environments (BIOS/UEFI/Linux), we choose not to enable "-msse" in
CFLAGS for this file.  We include ".arch" directives to ensure that
the assembler knows that it is permitted to emit the SSE2 and AES-NI
instructions, use a fixed "%xmm0" rather than an "x" constraint (which
GCC would consider to be impossible without "-msse"), and restore the
value of "%xmm0" after use to meet the requirements of the most
restrictive ABI for which this file can be built.

In an ideal world, we would also use a ".arch push" / ".arch pop" pair
to restore the permitted instruction set, rather than leaving the SSE2
and AES-NI instructions as permitted outside the scope of the inline
asm.  Unfortunately this feature would require binutils 2.34 or newer,
and so would prevent building iPXE on some still-current distros such
as RHEL8.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-06 23:47:24 +01:00
Michael Brown 7ada3e0f04 [crypto] Align AES round keys within the AES context
Some AES hardware acceleration instructions require each 16-byte round
key to be aligned on a 16-byte boundary.  Cipher contexts are byte
arrays allocated by the caller and do not have any guaranteed
alignment.

Increase the AES context size to allow space for alignment padding,
and align the context before use.  Reduce the round count field from
an unsigned int to a uint8_t, to minimise wasted space and to ensure
that the resulting padded context size is itself reasonably aligned.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-06 19:46:31 +01:00
Michael Brown c4023b98e3 [crypto] Allow for AES hardware acceleration
Allow architectures to detect support for AES hardware acceleration at
runtime and to replace the AES algorithm's encrypt() and decrypt()
method pointers with hardware accelerated implementations.

Extend the automated tests to run the AES tests twice: once with
hardware acceleration explicitly disabled (to test the unaccelerated
software implementation) and once with acceleration re-enabled.  Skip
the second test if no hardware acceleration is available: this avoids
unnecessarily repeating the test of the unaccelerated implementation,
and allows a non-zero test count for "aes-hw" to indicate that the
hardware acceleration was tested.  For example:

On a system that supports AES hardware acceleration:

   OK: "aes" 120 tests passed
   OK: "aes-hw" 120 tests passed

On a system that does not support AES hardware acceleration:

   OK: "aes" 120 tests passed
   OK: "aes-hw" 0 tests passed

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-06 19:46:31 +01:00
Michael Brown d916dfcfae [librm] Enable the use of SSE instructions if supported by the CPU
Clear CR0.EM, clear CR0.TS, and set CR4.OSFXSR in order to allow the
execution of SSE instructions such as the AES-NI instructions for AES
hardware acceleration.

Note that we have to assert CR4.OSFXSR to allow these instructions to
execute, but our context switching logic (e.g. the protected-mode and
long-mode interrupt handlers) does not actually preserve the
FPU/MMX/SSE registers.

Our C code therefore cannot in general presume that the FPU/MMX/SSE
registers will be preserved across arbitrary context boundaries.
However, since C code executes with interrupts disabled, an individual
function may safely use temporary FPU/MMX/SSE registers provided that
it does not enable interrupts or otherwise relinquish the context.

For the same C code to also be usable under the UEFI IA-32 ABI, it
must preserve all registers other than %eax, %ecx, and %edx, including
preserving all MMX and XMM registers.

The practical upshot is therefore that C code may use SSE instructions
and may assume that SSE registers will not be changed arbitrarily
during execution (either because the ABI guarantees preservation, as
with UEFI or Linux, or because the runtime environment guarantees that
interrupts are disabled), but the C code must itself restore the
values of any modified FPU/MMX/SSE registers.

The "fxsave"/"fxrstor" performed by virt_call() would allow for a
slightly more relaxed constraint if support for the UEFI IA-32 ABI
were ever to be dropped in future.  A real-mode caller that is making
use of SSE must have already set OSFXSR, and so its non-64-bit
registers %xmm0-%xmm7 would already be saved and restored across
virt_call().  The tightest constraint would then become the UEFI X64
ABI, which defines %xmm0-%xmm5 as volatile (i.e. caller-saved): this
would allow C code in iPXE to use %xmm0-%xmm5 without needing to
explicitly save and restore their values.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-06 19:43:49 +01:00
Michael Brown f5828201ea [librm] Preserve CR0 across virt_call()
Clearing the CR0.EM and CR0.TS flags is a prerequisite for using the
AES-NI instructions for AES hardware acceleration: if CR0.EM is set
then the CPU will raise an undefined-instruction exception, and if
CR0.TS is set then the CPU will raise a device-not-available exception
(expecting the OS to have installed an exception handler that would
perform a deferred context switch of the FPU/MMX/SSE registers).

Preserve CR0 across virt_call(), to allow the CR0.EM and CR0.TS flags
to be modified as needed.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-06 15:13:38 +01:00
Michael Brown a69ad34217 [librm] Preserve CR4 across virt_call() if FXSR is supported
Setting the CR4.OSFXSR flag is a prerequisite for using the AES-NI
instructions for AES hardware acceleration: if this flag is not set
then the CPU will raise an undefined-instruction exception.

We currently preserve CR4 across virt_call() only for 64-bit builds,
since those will modify CR4 by setting CR4.PAE.  Extend this to
preserve CR4 across virt_call() if FXSR is supported, to allow the
CR4.OSFXSR flag to be modified as needed.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-06 15:13:38 +01:00
Michael Brown 8f334fd55d [librm] Skip runtime check for FXSR support in 64-bit builds
SSE is an architectural requirement for x86_64, and FXSR is an
architectural requirement for SSE.  We can therefore skip the FXSR
check in a 64-bit build, since no 64-bit CPU can exist that does not
advertise support for FXSR.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-06 15:13:38 +01:00
Michael Brown af9604576d [librm] Remove conditionalisation of the Tivoli VMM workaround
Commit 71560d1 ("[librm] Preserve FPU, MMX and SSE state across calls
to virt_call()") originally introduced the use of "fxsave" and
"fxrstor" to work around a bug in the implementation of memcpy()
within the IBM Tivoli Provisioning Manager's VMM.  This commit assumed
(with justification given in the commit message) that SSE support
could be assumed to be present on any realistic in-scope CPU, and so
these instructions may safely be assumed to be supported.

Commit dd9a14d ("[librm] Conditionalize the workaround for the Tivoli
VMM's SSE garbling") then made this workaround a compile-time
conditional, to work around a missing feature in QEMU that caused the
use of "fxsave" and "fxrstor" to fail in QEMU VMs on some host CPUs.

Commit 900f1f9 ("[librm] Test for FXSAVE/FXRSTOR instruction support")
then added a runtime CPUID check for the FXSR feature, to allow the
unmodified iPXE binary to be used on older CPUs.

Supporting AES hardware acceleration via AES-NI will require setting
the CR4.OSFXSR control bit, which in turn must be conditionalised upon
the same runtime CPUID check for the FXSR feature.

Perform the runtime check unconditionally, and assume that we no
longer need the compile-time conditional (i.e. assume either that
newer versions of QEMU emulate "fxsave" and "fxrstor" when needed, or
that QEMU reports via CPUID that FXSR is not supported if it cannot
support those instructions).

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-06 15:13:38 +01:00
Michael Brown aef505b823 [librm] Clarify layout of saved GDTR/IDTR
The separate VC_TMP_GDT and VC_TMP_IDT fields suggest that these could
be moved freely relative to each other.  This is not the case: callers
of prot_to_real() must pass a single pointer to the combined pair.

Collapse to a single field, with the name adjusted to VC_TMP_GDTR_IDTR
to more closely match the rm_default_gdtr_idtr structure that
necessarily shares the same layout.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-06 15:13:38 +01:00
Michael Brown b57bc764a5 [librm] Always disable paging when switching to protected mode
Commit 6143057 ("[librm] Add support for running in 64-bit long mode")
treated disabling paging on the transition into protected mode as
something that needed to be done as a precaution only in a 64-bit
build, on the assumption that in a 32-bit BIOS system nothing else
would be enabling paging.

The Intel SDM states that setting CR0.PG in real mode (with CR0.PE
clear) will raise a general-protection exception anyway, and so we
should never encounter a situation in which CR0.PG is set at this
point.  A review of the bochs source code suggests that it may be
possible to encounter the combination of CR0.PG set with CR0.PE clear
in an SVM guest.

Err on the side of paranoia and always disable paging as part of the
transition from real mode to protected mode.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-06 15:13:38 +01:00
Michael Brown 787ed9397e [crypto] Treat high tag numbers as invalid
ASN.1 allows for multi-byte tag numbers by setting the low five bits
of the first tag byte to 0x1f.  No tag that we need to handle has this
format, and the existing checks for specific tag numbers will already
fail to match against such a tag (treating it as a normal single-byte
tag number).

Refuse to parse any tag with a high tag number format, to guard
against future bugs that could arise because the tag length would be
calculated incorrectly.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-04 22:42:40 +01:00
Michael Brown d89765d5f0 [test] Add Project Wycheproof RSA-PSS signature verification tests
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-04 21:48:48 +01:00
Michael Brown 20613766c9 [test] Add a build target for slow self-tests
The Project Wycheproof self-tests are deliberately not included in the
normal per-commit test suite since they are extremely slow to run.

Add a build target that includes the slow self-tests, and run these
tests on a push to the "slowtest" branch.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-04 18:32:39 +01:00
Michael Brown 6935ca31e0 [test] Add Project Wycheproof ECDSA signature verification tests
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-04 16:55:41 +01:00
Michael Brown 1901e32240 [crypto] Reject non-canonical ECDSA signature encodings
As detailed in commit 511dfd2 ("[crypto] Reject non-canonical ECDSA
signature data structures"), changing the representation of a valid
ECDSA signature to a different valid representation of the same
signature does not conceptually make it an invalid signature.

However, some large public test vector sets conflate the concepts of
"altered representation" and "invalid representation" in a way that
makes it difficult to determine which tests ought to pass and which
ought to fail without extensive manual analysis.

Reject any ECDSA signature object that does not have the expected
total length.  The signature parsing logic already ensures that the
expected structure exists, and so the total length can be correct only
if every object used the expected DER encoding.

This length check completely subsumes the checks that were introduced
in commit 511dfd2 ("[crypto] Reject non-canonical ECDSA signature data
structures"), since there is no way to insert additional information
without also affecting the length.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-04 16:51:54 +01:00
Michael Brown 3a62e9ded4 [crypto] Reject indefinite and unrepresentable length encodings
An indefinite length encoding will currently be parsed as having a
length of zero, and an encoded length that exceeds the range of an
unsigned int will be truncated.

Tighten up the parsing of lengths to explicitly reject indefinite
length encodings or unrepresentable lengths.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-04 16:51:54 +01:00
Michael Brown de04e79ae5 [crypto] Reject ASN.1 unsigned integers holding negative values
We use asn1_enter_unsigned() essentially as a convenience mechanism to
skip the initial zero byte found when an encoder had to insert the
zero to prevent a logically unsigned value from being interpreted as
negative.

We currently accept malformed values where the initial byte has the
MSB set, and allow them to be interpreted as unsigned values.

Tighten up the parsing of unsigned integers so that values where the
initial byte has the MSB set will be rejected as invalid, and ensure
that the resulting cursor is minimal by skipping any number of initial
padding zero bytes (so that asn1_compare() can then be used without
the risk of false negatives).

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-04 14:28:27 +01:00
Michael Brown 511dfd2c4d [crypto] Reject non-canonical ECDSA signature data structures
An ECDSA signature value is a vector of two integers (r,s) modulo the
curve group order.  The ECDSA algorithm itself does not define the
encoding to be used for these two integers.  At least two different
standards exist for representing the vector (r,s): the ASN.1 structure
originally defined in RFC 3279 (which uses a SEQUENCE of two INTEGER
values) and the raw byte concatenation structure defined in IEEE
P1363.  A valid signature vector (r,s) may be freely converted between
these two formats.  Changing the format does not logically change the
validity of the signature.

Due to the mathematics underlying ECDSA, the vector (r,-s) is also
always a valid signature for the same content.

With the ASN.1 structure, there exists the possibility of adding extra
data that would currently be ignored by the parser: either objects
following the top-level SEQUENCE, or objects within the SEQUENCE
following the two INTEGER values.  Adding this data does not logically
change the validity of the signature, in the same way that converting
between ASN.1 and P1363 does not logically change the validity of the
signature.  However, some public test vector sets check for the
rejection of signatures containing inserted data.

Reject any ECDSA signature object that includes data following the
top-level SEQUENCE, or that includes data following the "r" and "s"
INTEGER values.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-04 13:43:05 +01:00
Michael Brown 0a1d5fae46 [test] Simplify class hierarchy for Project Wycheproof import tool
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-03 14:14:15 +01:00
Michael Brown 2d2dd525bd [test] Derive comment labels from Project Wycheproof input files
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-03 13:15:44 +01:00
Michael Brown ffa20bcb7e [test] Derive algorithm names from Project Wycheproof input files
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-03 12:53:12 +01:00
Michael Brown d6032f05e8 [test] Use a single shared class for Project Wycheproof test flags
We don't need the ability to validate that the test flags are
appropriate for the type of test.  Reduce duplication by using a
single shared class for all existent test flags.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-03 10:58:11 +01:00
Michael Brown b684d09fd9 [test] Add Project Wycheproof RSA PKCS#1 signature verification tests
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-03 00:51:20 +01:00
Michael Brown d7226b9a99 [test] Add Project Wycheproof RSA PKCS#1 signature generation tests
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-03 00:15:07 +01:00
Michael Brown 7b46fa94a5 [test] Allow for invocation of individual Project Wycheproof tests
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-03 00:15:07 +01:00
Michael Brown 6e73f2aade [crypto] Reject non-canonical RSA inputs
An RSA signature value (or encrypted message value) is a congruence
class modulo the field prime, and so adding a multiple of the field
prime does not logically change the validity of the signature (or the
content of the encrypted message).

However, RFC 8017 states that both the decryption primitive (section
5.1.2) and the verification primitive (section 5.2.2) should reject
non-canonical input values (i.e. any value that is not strictly less
than the field prime), and some public test vector sets check for this
rejection.

Treat any signature value or encrypted message value that is equal to
or greater than the field prime as being invalid.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-03 00:08:01 +01:00
Michael Brown c6bda17e58 [crypto] Add OID-identified algorithms for RSA with SHA512/224 and SHA512/256
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-02 23:33:56 +01:00
Michael Brown 22727c6ce3 [test] Add Project Wycheproof RSA PKCS#1 decryption tests
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-02 16:36:41 +01:00
Michael Brown 2b1eaff499 [crypto] Add missing padding length check in RSA decryption
RSA PKCS#1 requires a minimum of eight non-zero padding bytes for
encryption.  This limit is currently enforced when encrypting but not
validated when decrypting.

The only existing code path that can currently lead to RSA decryption
is CMS decryption, which can use RSA to decrypt the cipher key.  With
underlength PKCS#1 padding (and hence an overlength plaintext), the
decrypted cipher key would be rejected by the immediately following
call to cipher_setkey().

Add the missing padding length check as part of RSA decryption, and
add a test case (imported from Project Wycheproof) to ensure that this
check remains in place in future.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-02 16:36:41 +01:00
Michael Brown a2f979a300 [test] Allow for public-key algorithm tests with incomplete key pairs
Some public RSA and ECDSA test vector sets provide only the private or
public half of the key pair, and reuse the same key for multiple tests
within the set.

Allow public-key tests to omit either half of the key pair, and to
therefore perform separate tests for encryption, decryption, signature
generation, and signature verification.

The existing RSA and ECDSA tests (which all include a full key pair)
are unchanged by this reorganisation.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-02 16:36:41 +01:00
Michael Brown ef58461294 [test] Allow for per-group definition code in Project Wycheproof tests
The Project Wycheproof RSA and ECDSA tests define the key as a
property of the test group rather than of the individual test case.

Allow test groups to have stable identifiers and to participate in
generating the source code for the test definitions.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-02 16:36:41 +01:00
Michael Brown d9df5d9bdd [test] Add schema and count validators in Project Wycheproof tests
The schema and test counts are currently validated only at the point
of attempting to generate source code.  Promote these checks to become
standard validators.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-02 16:36:41 +01:00
Michael Brown 35bd9a8fd3 [test] Add Project Wycheproof AES-GCM tests
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-01 14:24:51 +01:00
Michael Brown e49e1db1c2 [crypto] Reject zero-length IVs for GCM ciphers
A zero-length IV is not permitted by the NIST GCM specification, since
it would lead to leaking the authentication key.

The only existing code path that can currently lead to the use of a
GCM cipher with a zero-length initialisation vector is CMS decryption.
Modifying a CMS encrypted message to include a zero-length IV would
leak information required to obtain the authentication key into the
transient decrypted image, but this transient image would then fail
the GCM authentication tag check and so the decrypted plaintext would
be immediately overwritten (with the re-encrypted ciphertext).

Improve robustness by rejecting a zero-length initialisation vector
for a GCM cipher, and add a test case to ensure that this rejection
remains in place in future.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-01 14:24:42 +01:00
Michael Brown 474abc95d9 [test] Add ability to test for cipher key and IV failures
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-09-01 12:57:35 +01:00
Michael Brown 5fe9965198 [test] Add Project Wycheproof HKDF tests
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-08-31 21:23:35 +01:00
Michael Brown 6981f2372f [test] Allow for HKDF tests without an expected PRK
Some public HKDF test vectors provide only the expected output key
material, without specifying the expected pseudorandom key used to
generate the output key material.

Allow HKDF tests to omit the expected pseudorandom key, so that we can
use these public test vectors without needing to synthesize an
expected pseudorandom key value.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-08-31 21:23:35 +01:00
Michael Brown 16479af7f5 [test] Add Project Wycheproof HMAC tests
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-08-31 21:23:35 +01:00
Michael Brown a1c38478f2 [test] Allow for HMAC tests with partial expected output digest values
Some public HMAC test vectors provide expected output digest values
that are shorter than the digest size.

Allow HMAC tests to provide expected output digest values of any
non-zero length up to and including the digest size, so that we can
use these public test vectors without needing to synthesize the
remainder of the output digest value.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-08-31 21:23:35 +01:00
Michael Brown 0a2a0ef50e [test] Reduce duplication in Project Wycheproof import tool
Move per-algorithm parameters (such as the key sizes for key exchange
algorithms) to the test file level of the data structure, thereby
avoiding the need to create three classes (test file, test group, and
test case) for every per-algorithm specialisation.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-08-31 21:23:35 +01:00
Michael Brown d26e0496e7 [test] Add Project Wycheproof import tool and key exchange tests
Project Wycheproof (https://github.com/C2SP/wycheproof) provides test
vectors designed to exercise cryptographic algorithms with corner
cases that are not covered by the standard known-answer tests such as
those provided by NIST or in RFCs.

Create an import tool to read the applicable subset of the Project
Wycheproof key exchange test vectors and generate the corresponding
iPXE key exchange test cases, and create a wrapper for running the
resulting tests within iPXE.

The full imported test set is extremely large and slow to run.  We
deliberately choose not to included these tests within the standard
per-commit test suite, since this would substantially delay all test
runs for no significant benefit.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-08-30 15:27:59 +01:00
Michael Brown 26f46a2b4f [tls] Reject a duplicate ServerHelloDone
A duplicate ServerHelloDone could cause the server validation pending
operation to be incremented twice but only decremented once, leaving
the total pending operation count above zero and thereby causing any
future "sync" command with no timeout to wait indefinitely.

Fix by rejecting ServerHelloDone if validation is already pending.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-08-27 13:08:39 +01:00
Michael Brown 61a75eca5f [crypto] Ensure that a closed channel is left with unusable ciphers
We currently protect against a consumer that erroneously uses values
generated from the ephemeral master secret after closing a previously
opened channel, by deliberately replacing the ephemeral master secret
rather than zeroing it when the channel is closed.

Extend this concept to protect against a consumer that erroneously
uses the ciphers after closing a previously opened channel (or that
erroneously uses the ciphers from a channel that failed to open
successfully), by using the dead ciphers by default and by enabling
the plaintext (null) ciphers when and only when the channel has been
successfully opened.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-08-27 12:47:57 +01:00
Michael Brown 2d66bfa038 [crypto] Guard against mutation of certificate chain during validation
Certificate chains are reference-counted structures, and both the TLS
connection and the validator hold a reference to the same certificate
chain while validation is in progress.  The TLS connection will not
mutate the chain during this time: if a second (illegal) Certificate
record were to arrive then it would drop its reference to the existing
chain (leaving the validator as the sole possessor) before creating a
new chain to hold the received certificates.

The validator currently holds two pointers that could be invalidated
if a future code change were to cause the chain to be externally
mutatated while validation is in progress:

  - a pointer to the current certificate (for OCSP or cross-signed
    downloads) that does not hold its own reference and relies upon
    the chain's reference to keep the certificate pointer valid

  - a pointer to the current link within the certificate chain

Guard against this class of potential future code changes by promoting
the certificate pointer to hold its own reference to the certificate
so that it is guaranteed to remain valid, deleting the stored pointer
to the current link completely, and adding a function x509_link() that
is used to locate the certificate link by traversing the chain.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-08-27 12:01:01 +01:00
Michael Brown 2ebc0d49c6 [tls] Add documentation to assist future code review
Document the deliberate absence of enforced handshake record ordering,
and update data structure comments to clarify which pointer fields are
allowed to have NULL values.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-08-25 23:49:15 +01:00
Michael Brown 0167b5a39f [tls] Make certificate processing visibly safe at point of use
Following the example of commit 8ef91dd ("[tls] Make certificate
sending visibly safe at point of use"), add runtime checks in the
functions that consume the server certificate chain so that the safety
guarantee becomes immediately visible, and to avoid potential future
bugs.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-08-25 23:16:41 +01:00
Michael Brown 8ef91ddc8d [tls] Make certificate sending visibly safe at point of use
Sending a client certificate chain and its corresponding certificate
verification will be triggered only if we actually have these items to
send, and so the pointer dereferences within those functions are safe.
However, this safety guarantee is not visible at the point of use.

Add runtime checks in those functions so that the safety guarantee
becomes immediately visible, and to avoid potential future bugs.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-08-25 22:12:00 +01:00
Michael Brown 11b793c586 [tls] Treat secure channel's cipher algorithm as definitive
The transmit and receive ciphers are now owned by the secure channel.
We assert at the point of use that the cipher algorithm used by the
secure channel matches the algorithm in the currently active cipher
specification's cipher suite, but this does not guard against future
code changes that could accidentally leave these out of sync, and
thereby end up passing an incorrectly sized context to the cipher.

Fix by treating the secure channel as having the definitive record of
the current cipher algorithm, and treating the algorithm specified in
the cipher suite as being used only as a parameter for initialising
the transmit or receive pipe via the secure channel.

This matches the way that we treat the key schedule as having the
definitive record of the current handshake digest algorithm, with the
algorithm specified in the cipher suite being used only as a parameter
for initialising the key schedule.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-08-25 21:17:01 +01:00
Michael Brown dc962df26f [tls] Ensure that ServerKeyExchange parsing method is always non-NULL
Use a dedicated parser function for unexpected ServerKeyExchange
records, to create an invariant that the parser function field always
has a non-NULL value.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-08-25 20:00:00 +01:00
Michael Brown c6e8c42238 [tls] Use null key exchange algorithm as default for DHE and ECDHE
The DHE and ECDHE ServerKeyExchange parsers will always return the
identified key exchange algorithm in the parsed parameters.  The
default key exchange algorithm is therefore never used for these key
exchange mechanisms.

Set the default key exchange algorithm for DHE and ECDHE to be the
null key exchange algorithm (which will always fail operations), and
also set this as the in-use key exchange algorithm when a TLS
connection is first initialised.

This creates an invariant that the TLS connection always has a valid
key exchange algorithm pointer even if that pointer does not yet
represent the algorithm being used, matching the usage pattern for the
TLS connection's cipher suite and reducing the opportunities for
potential future NULL pointer dereferences.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-08-25 19:54:17 +01:00
Michael Brown 8383acf487 [tls] Ensure that broken cipher suites can never be selected
Enforce the invariant that all pointer fields have non-NULL values in
all cipher suites by refusing to ever select a cipher suite that fails
this invariant check.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-08-25 19:17:14 +01:00
Michael Brown cc87836a00 [tls] Remove redundant cipher suite name components in debug messages
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-08-25 18:52:27 +01:00
Michael Brown 202466d8a6 [tls] Use null handshake digest algorithm for the null cipher suite
A cipher suite's handshake digest algorithm is used only once that
cipher suite has been selected.  The null cipher suite can never be
explicitly selected, and so its handshake digest algorithm can never
be used and is currently left as a NULL pointer.

Set the null cipher suite's handshake digest algorithm to be the null
digest algorithm, to create an invariant that all pointer fields have
non-NULL values in all cipher suites.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-08-25 18:13:05 +01:00
Michael Brown 7afbcac103 [tls] Use null MAC digest algorithm for AEAD ciphers
The AEAD cipher suites all have a zero MAC length, since message
authentication is provided by the AEAD cipher itself and the MAC
digest algorithm is never used.

Avoid an unnecessary and unused stack allocation (for the MAC digest
output value) by switching these cipher suites to use the null digest
algorithm instead.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-08-25 17:46:55 +01:00
Michael Brown d86e86dff8 [tls] Guarantee that receive data buffer list is non-empty
Since commit 72db146 ("[tls] Split received records over multiple I/O
buffers"), receive record processing has worked by allocating an I/O
buffer list with exactly enough combined tailroom to hold the incoming
record.

A zero-length record is not permitted by the protocol (and is
impossible when a non-plaintext cipher is in use due to the extra
space reserved at the start of the buffer list to accommodate the IV),
but would currently result in an empty I/O buffer list.

Fix by ensuring that at least one buffer is always allocated even for
a zero total length.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-08-25 00:07:06 +01:00
Michael Brown e97772c34a [tls] Use standalone secure channel and key schedule implementations
The TLS implementation has become too complex to reason about safely,
and needs to be split up into smaller and well-defined units before
adding any further complexity (e.g. to support TLS version 1.3).

Use the standalone secure channel implementation to ensure that the
connection can be used for encrypted communication with a trusted
peer, with the secure channel operations being provided by the
standalone TLS key schedule implementation.

The TLS implementation is now just a protocol engine, and is no longer
responsible for policy decisions on whether or not the connection is
secure.  The TLS code delegates this decision to the underlying secure
channel, by refusing to transmit or receive application data unless
the secure channel has successfully been marked as established.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-08-24 18:01:51 +01:00