[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>
This commit is contained in:
Michael Brown
2026-09-14 13:55:08 +01:00
parent 84af36a21b
commit 35a1abdbf8
22 changed files with 78 additions and 17 deletions
+3 -2
View File
@@ -33,9 +33,10 @@ FILE_SECBOOT ( PERMITTED );
struct tls_cipher_suite tls_aes_128_gcm_sha256 __tls_cipher_suite ( 31 ) = {
.code = htons ( TLS_AES_128_GCM_SHA256 ),
.key_len = ( 128 / 8 ),
.fixed_iv_len = 4,
.record_iv_len = 8,
.fixed_iv_len = 12,
.record_iv_len = 0,
.mac_len = 0,
.flags = TLS_CIPHER_FL_SEQUENTIAL_IV,
.exchange = &tls_null_exchange_algorithm,
.pubkey = &pubkey_null,
.cipher = &aes_gcm_algorithm,
+3 -2
View File
@@ -33,9 +33,10 @@ FILE_SECBOOT ( PERMITTED );
struct tls_cipher_suite tls_aes_256_gcm_sha384 __tls_cipher_suite ( 32 ) = {
.code = htons ( TLS_AES_256_GCM_SHA384 ),
.key_len = ( 256 / 8 ),
.fixed_iv_len = 4,
.record_iv_len = 8,
.fixed_iv_len = 12,
.record_iv_len = 0,
.mac_len = 0,
.flags = TLS_CIPHER_FL_SEQUENTIAL_IV,
.exchange = &tls_null_exchange_algorithm,
.pubkey = &pubkey_null,
.cipher = &aes_gcm_algorithm,
@@ -39,6 +39,7 @@ tls_dhe_rsa_with_aes_128_cbc_sha __tls_cipher_suite ( 15 ) = {
.fixed_iv_len = 0,
.record_iv_len = AES_BLOCKSIZE,
.mac_len = SHA1_DIGEST_SIZE,
.flags = 0,
.exchange = &tls_dhe_exchange_algorithm,
.pubkey = &rsa_algorithm,
.cipher = &aes_cbc_algorithm,
@@ -54,6 +55,7 @@ tls_dhe_rsa_with_aes_256_cbc_sha __tls_cipher_suite ( 16 ) = {
.fixed_iv_len = 0,
.record_iv_len = AES_BLOCKSIZE,
.mac_len = SHA1_DIGEST_SIZE,
.flags = 0,
.exchange = &tls_dhe_exchange_algorithm,
.pubkey = &rsa_algorithm,
.cipher = &aes_cbc_algorithm,
@@ -38,6 +38,7 @@ tls_dhe_rsa_with_aes_128_cbc_sha256 __tls_cipher_suite ( 13 ) = {
.fixed_iv_len = 0,
.record_iv_len = AES_BLOCKSIZE,
.mac_len = SHA256_DIGEST_SIZE,
.flags = 0,
.exchange = &tls_dhe_exchange_algorithm,
.pubkey = &rsa_algorithm,
.cipher = &aes_cbc_algorithm,
@@ -53,6 +54,7 @@ tls_dhe_rsa_with_aes_256_cbc_sha256 __tls_cipher_suite ( 14 ) = {
.fixed_iv_len = 0,
.record_iv_len = AES_BLOCKSIZE,
.mac_len = SHA256_DIGEST_SIZE,
.flags = 0,
.exchange = &tls_dhe_exchange_algorithm,
.pubkey = &rsa_algorithm,
.cipher = &aes_cbc_algorithm,
@@ -38,6 +38,7 @@ tls_dhe_rsa_with_aes_128_gcm_sha256 __tls_cipher_suite ( 11 ) = {
.fixed_iv_len = 4,
.record_iv_len = 8,
.mac_len = 0,
.flags = TLS_CIPHER_FL_SEQUENTIAL_IV,
.exchange = &tls_dhe_exchange_algorithm,
.pubkey = &rsa_algorithm,
.cipher = &aes_gcm_algorithm,
@@ -38,6 +38,7 @@ tls_dhe_rsa_with_aes_256_gcm_sha384 __tls_cipher_suite ( 12 ) = {
.fixed_iv_len = 4,
.record_iv_len = 8,
.mac_len = 0,
.flags = TLS_CIPHER_FL_SEQUENTIAL_IV,
.exchange = &tls_dhe_exchange_algorithm,
.pubkey = &rsa_algorithm,
.cipher = &aes_gcm_algorithm,
@@ -39,6 +39,7 @@ tls_ecdhe_ecdsa_with_aes_128_cbc_sha __tls_cipher_suite ( 05 ) = {
.fixed_iv_len = 0,
.record_iv_len = AES_BLOCKSIZE,
.mac_len = SHA1_DIGEST_SIZE,
.flags = 0,
.exchange = &tls_ecdhe_exchange_algorithm,
.pubkey = &ecdsa_algorithm,
.cipher = &aes_cbc_algorithm,
@@ -54,6 +55,7 @@ tls_ecdhe_ecdsa_with_aes_256_cbc_sha __tls_cipher_suite ( 06 ) = {
.fixed_iv_len = 0,
.record_iv_len = AES_BLOCKSIZE,
.mac_len = SHA1_DIGEST_SIZE,
.flags = 0,
.exchange = &tls_ecdhe_exchange_algorithm,
.pubkey = &ecdsa_algorithm,
.cipher = &aes_cbc_algorithm,
@@ -38,6 +38,7 @@ tls_ecdhe_ecdsa_with_aes_128_cbc_sha256 __tls_cipher_suite ( 03 ) = {
.fixed_iv_len = 0,
.record_iv_len = AES_BLOCKSIZE,
.mac_len = SHA256_DIGEST_SIZE,
.flags = 0,
.exchange = &tls_ecdhe_exchange_algorithm,
.pubkey = &ecdsa_algorithm,
.cipher = &aes_cbc_algorithm,
@@ -38,6 +38,7 @@ tls_ecdhe_ecdsa_with_aes_256_cbc_sha384 __tls_cipher_suite ( 04 ) = {
.fixed_iv_len = 0,
.record_iv_len = AES_BLOCKSIZE,
.mac_len = SHA384_DIGEST_SIZE,
.flags = 0,
.exchange = &tls_ecdhe_exchange_algorithm,
.pubkey = &ecdsa_algorithm,
.cipher = &aes_cbc_algorithm,
@@ -38,6 +38,7 @@ tls_ecdhe_ecdsa_with_aes_128_gcm_sha256 __tls_cipher_suite ( 01 ) = {
.fixed_iv_len = 4,
.record_iv_len = 8,
.mac_len = 0,
.flags = TLS_CIPHER_FL_SEQUENTIAL_IV,
.exchange = &tls_ecdhe_exchange_algorithm,
.pubkey = &ecdsa_algorithm,
.cipher = &aes_gcm_algorithm,
@@ -38,6 +38,7 @@ tls_ecdhe_ecdsa_with_aes_256_gcm_sha384 __tls_cipher_suite ( 02 ) = {
.fixed_iv_len = 4,
.record_iv_len = 8,
.mac_len = 0,
.flags = TLS_CIPHER_FL_SEQUENTIAL_IV,
.exchange = &tls_ecdhe_exchange_algorithm,
.pubkey = &ecdsa_algorithm,
.cipher = &aes_gcm_algorithm,
@@ -39,6 +39,7 @@ tls_ecdhe_rsa_with_aes_128_cbc_sha __tls_cipher_suite ( 05 ) = {
.fixed_iv_len = 0,
.record_iv_len = AES_BLOCKSIZE,
.mac_len = SHA1_DIGEST_SIZE,
.flags = 0,
.exchange = &tls_ecdhe_exchange_algorithm,
.pubkey = &rsa_algorithm,
.cipher = &aes_cbc_algorithm,
@@ -54,6 +55,7 @@ tls_ecdhe_rsa_with_aes_256_cbc_sha __tls_cipher_suite ( 06 ) = {
.fixed_iv_len = 0,
.record_iv_len = AES_BLOCKSIZE,
.mac_len = SHA1_DIGEST_SIZE,
.flags = 0,
.exchange = &tls_ecdhe_exchange_algorithm,
.pubkey = &rsa_algorithm,
.cipher = &aes_cbc_algorithm,
@@ -38,6 +38,7 @@ tls_ecdhe_rsa_with_aes_128_cbc_sha256 __tls_cipher_suite ( 03 ) = {
.fixed_iv_len = 0,
.record_iv_len = AES_BLOCKSIZE,
.mac_len = SHA256_DIGEST_SIZE,
.flags = 0,
.exchange = &tls_ecdhe_exchange_algorithm,
.pubkey = &rsa_algorithm,
.cipher = &aes_cbc_algorithm,
@@ -38,6 +38,7 @@ tls_ecdhe_rsa_with_aes_256_cbc_sha384 __tls_cipher_suite ( 04 ) = {
.fixed_iv_len = 0,
.record_iv_len = AES_BLOCKSIZE,
.mac_len = SHA384_DIGEST_SIZE,
.flags = 0,
.exchange = &tls_ecdhe_exchange_algorithm,
.pubkey = &rsa_algorithm,
.cipher = &aes_cbc_algorithm,
@@ -38,6 +38,7 @@ tls_ecdhe_rsa_with_aes_128_gcm_sha256 __tls_cipher_suite ( 01 ) = {
.fixed_iv_len = 4,
.record_iv_len = 8,
.mac_len = 0,
.flags = TLS_CIPHER_FL_SEQUENTIAL_IV,
.exchange = &tls_ecdhe_exchange_algorithm,
.pubkey = &rsa_algorithm,
.cipher = &aes_gcm_algorithm,
@@ -38,6 +38,7 @@ tls_ecdhe_rsa_with_aes_256_gcm_sha384 __tls_cipher_suite ( 02 ) = {
.fixed_iv_len = 4,
.record_iv_len = 8,
.mac_len = 0,
.flags = TLS_CIPHER_FL_SEQUENTIAL_IV,
.exchange = &tls_ecdhe_exchange_algorithm,
.pubkey = &rsa_algorithm,
.cipher = &aes_gcm_algorithm,
+2
View File
@@ -39,6 +39,7 @@ tls_rsa_with_aes_128_cbc_sha __tls_cipher_suite ( 25 ) = {
.fixed_iv_len = 0,
.record_iv_len = AES_BLOCKSIZE,
.mac_len = SHA1_DIGEST_SIZE,
.flags = 0,
.exchange = &tls_pubkey_exchange_algorithm,
.pubkey = &rsa_algorithm,
.cipher = &aes_cbc_algorithm,
@@ -54,6 +55,7 @@ tls_rsa_with_aes_256_cbc_sha __tls_cipher_suite ( 26 ) = {
.fixed_iv_len = 0,
.record_iv_len = AES_BLOCKSIZE,
.mac_len = SHA1_DIGEST_SIZE,
.flags = 0,
.exchange = &tls_pubkey_exchange_algorithm,
.pubkey = &rsa_algorithm,
.cipher = &aes_cbc_algorithm,
+2
View File
@@ -38,6 +38,7 @@ tls_rsa_with_aes_128_cbc_sha256 __tls_cipher_suite ( 23 ) = {
.fixed_iv_len = 0,
.record_iv_len = AES_BLOCKSIZE,
.mac_len = SHA256_DIGEST_SIZE,
.flags = 0,
.exchange = &tls_pubkey_exchange_algorithm,
.pubkey = &rsa_algorithm,
.cipher = &aes_cbc_algorithm,
@@ -53,6 +54,7 @@ tls_rsa_with_aes_256_cbc_sha256 __tls_cipher_suite ( 24 ) = {
.fixed_iv_len = 0,
.record_iv_len = AES_BLOCKSIZE,
.mac_len = SHA256_DIGEST_SIZE,
.flags = 0,
.exchange = &tls_pubkey_exchange_algorithm,
.pubkey = &rsa_algorithm,
.cipher = &aes_cbc_algorithm,
+1
View File
@@ -38,6 +38,7 @@ tls_rsa_with_aes_128_gcm_sha256 __tls_cipher_suite ( 21 ) = {
.fixed_iv_len = 4,
.record_iv_len = 8,
.mac_len = 0,
.flags = TLS_CIPHER_FL_SEQUENTIAL_IV,
.exchange = &tls_pubkey_exchange_algorithm,
.pubkey = &rsa_algorithm,
.cipher = &aes_gcm_algorithm,
+1
View File
@@ -38,6 +38,7 @@ tls_rsa_with_aes_256_gcm_sha384 __tls_cipher_suite ( 22 ) = {
.fixed_iv_len = 4,
.record_iv_len = 8,
.mac_len = 0,
.flags = TLS_CIPHER_FL_SEQUENTIAL_IV,
.exchange = &tls_pubkey_exchange_algorithm,
.pubkey = &rsa_algorithm,
.cipher = &aes_gcm_algorithm,
+5
View File
@@ -283,8 +283,13 @@ struct tls_cipher_suite {
uint8_t record_iv_len;
/** MAC length */
uint8_t mac_len;
/** Flags */
uint8_t flags;
};
/** Cipher XORs sequence number into the initialisation vector */
#define TLS_CIPHER_FL_SEQUENTIAL_IV 0x01
/** TLS cipher suite table */
#define TLS_CIPHER_SUITES \
__table ( struct tls_cipher_suite, "tls_cipher_suites" )
+42 -12
View File
@@ -275,6 +275,21 @@ static void tls_set_uint24 ( tls24_t *field24, unsigned long value ) {
field24->low = cpu_to_be16 ( value );
}
/**
* XOR data block
*
* @v dst Destination data
* @v src Source data
* @v len Length of data
*/
static void tls_xor ( void *dst, const void *src, size_t len ) {
const uint8_t *src_bytes = src;
uint8_t *dst_bytes = dst;
while ( len-- )
*(dst_bytes++) ^= *(src_bytes++);
}
/**
* Determine if TLS connection is ready for application data
*
@@ -3476,7 +3491,7 @@ static int tls_send_record ( struct tls_connection *tls, unsigned int type,
struct cipher_algorithm *cipher = pipe->cipher;
struct {
uint8_t fixed[suite->fixed_iv_len];
uint8_t rec[suite->record_iv_len];
uint8_t record[suite->record_iv_len];
} __attribute__ (( packed )) iv;
struct tls_auth_header authhdr;
struct tls_header *tlshdr;
@@ -3520,9 +3535,17 @@ static int tls_send_record ( struct tls_connection *tls, unsigned int type,
/* Construct and set initialisation vector */
memcpy ( iv.fixed, cipherspec->fixed_iv, sizeof ( iv.fixed ) );
if ( suite->flags & TLS_CIPHER_FL_SEQUENTIAL_IV ) {
memset ( iv.record, 0, sizeof ( iv.record ) );
assert ( sizeof ( iv ) >= sizeof ( authhdr.seq ) );
tls_xor ( ( ( ( void * ) &iv ) + sizeof ( iv )
- sizeof ( authhdr.seq ) ),
&authhdr.seq, sizeof ( authhdr.seq ) );
} else {
channel_ephemeral ( &tls->channel, &authhdr,
sizeof ( authhdr ), iv.rec,
sizeof ( iv.rec ) );
sizeof ( authhdr ), iv.record,
sizeof ( iv.record ) );
}
if ( ( rc = cipher_setiv ( cipher, pipe->ctx, &iv,
sizeof ( iv ) ) ) != 0 ) {
DBGC ( tls, "TLS %p could not set TX IV: %s\n",
@@ -3554,12 +3577,12 @@ static int tls_send_record ( struct tls_connection *tls, unsigned int type,
tlshdr = iob_put ( iobuf, sizeof ( *tlshdr ) );
tlshdr->type = type;
tlshdr->version = htons ( tls->legacy_version );
tlshdr->length = htons ( sizeof ( iv.rec ) + encrypt_len +
tlshdr->length = htons ( sizeof ( iv.record ) + encrypt_len +
cipher->authsize );
/* Add record initialisation vector, if applicable */
memcpy ( iob_put ( iobuf, sizeof ( iv.rec ) ), iv.rec,
sizeof ( iv.rec ) );
memcpy ( iob_put ( iobuf, sizeof ( iv.record ) ), iv.record,
sizeof ( iv.record ) );
/* Copy plaintext data if necessary */
ciphertext = iob_put ( iobuf, record_len );
@@ -3712,6 +3735,11 @@ static int tls_new_ciphertext ( struct tls_connection *tls,
first = list_first_entry ( rx_data, struct io_buffer, list );
last = list_last_entry ( rx_data, struct io_buffer, list );
/* Construct authentication data (excluding length) */
authhdr.seq = cpu_to_be64 ( cipherspec->seq++ );
authhdr.header.type = tlshdr->type;
authhdr.header.version = tlshdr->version;
/* Extract initialisation vector */
if ( iob_len ( first ) < sizeof ( iv.record ) ) {
DBGC ( tls, "TLS %p received underlength IV\n", tls );
@@ -3719,6 +3747,13 @@ static int tls_new_ciphertext ( struct tls_connection *tls,
return -EINVAL_IV;
}
memcpy ( iv.fixed, cipherspec->fixed_iv, sizeof ( iv.fixed ) );
if ( suite->flags & TLS_CIPHER_FL_SEQUENTIAL_IV ) {
memset ( iv.record, 0, sizeof ( iv.record ) );
assert ( sizeof ( iv ) >= sizeof ( authhdr.seq ) );
tls_xor ( ( ( ( void * ) &iv ) + sizeof ( iv ) -
sizeof ( authhdr.seq ) ), &authhdr.seq,
sizeof ( authhdr.seq ) );
}
memcpy ( iv.record, first->data, sizeof ( iv.record ) );
iob_pull ( first, sizeof ( iv.record ) );
len -= sizeof ( iv.record );
@@ -3734,12 +3769,6 @@ static int tls_new_ciphertext ( struct tls_connection *tls,
len -= cipher->authsize;
auth = last->tail;
/* Construct authentication data */
authhdr.seq = cpu_to_be64 ( cipherspec->seq++ );
authhdr.header.type = tlshdr->type;
authhdr.header.version = tlshdr->version;
authhdr.header.length = htons ( len );
/* Set initialisation vector */
if ( ( rc = cipher_setiv ( cipher, pipe->ctx, &iv,
sizeof ( iv ) ) ) != 0 ) {
@@ -3749,6 +3778,7 @@ static int tls_new_ciphertext ( struct tls_connection *tls,
}
/* Process authentication data, if applicable */
authhdr.header.length = htons ( len );
if ( is_auth_cipher ( cipher ) ) {
cipher_decrypt ( cipher, pipe->ctx, &authhdr,
NULL, sizeof ( authhdr ) );