From bc19aeca5f6c695ad3db0196057d155e4f64584e Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 6 Sep 2022 13:02:17 +0100 Subject: [ipv6] Fix mask calculation when prefix length is not a multiple of 8 Signed-off-by: Michael Brown --- src/net/ipv6.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/net') diff --git a/src/net/ipv6.c b/src/net/ipv6.c index 4b2c33eb4..901203c40 100644 --- a/src/net/ipv6.c +++ b/src/net/ipv6.c @@ -251,7 +251,7 @@ int ipv6_add_miniroute ( struct net_device *netdev, struct in6_addr *address, *prefix_mask = 0xff; } if ( remaining ) - *prefix_mask <<= ( 8 - remaining ); + *prefix_mask = ( 0xff << ( 8 - remaining ) ); } /* Add to start of routing table */ -- cgit v1.2.3-55-g7522 From 8f5fc161436a020ba65d07f91f62d34f4c22db61 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 13 Sep 2022 13:25:19 +0100 Subject: [ipv6] Ignore SLAAC on prefixes with an incompatible prefix length Experience suggests that routers are often misconfigured to advertise SLAAC even on prefixes that do not have a SLAAC-compatible prefix length. iPXE will currently treat this as an error, resulting in the prefix being ignored completely. Handle this misconfiguration by ignoring the autonomous address flag when the prefix length is unsuitable for SLAAC. Reported-by: Malte Janduda Signed-off-by: Michael Brown --- src/net/ndp.c | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) (limited to 'src/net') diff --git a/src/net/ndp.c b/src/net/ndp.c index 75e531648..c8e8ebad3 100644 --- a/src/net/ndp.c +++ b/src/net/ndp.c @@ -789,29 +789,43 @@ static int ndp_prefix_fetch_ip6 ( struct settings *settings, void *data, container_of ( ndpset->settings.parent, struct net_device, settings.settings ); struct ndp_prefix_information_option *prefix = prefset->prefix; - struct in6_addr ip6; + struct in6_addr *ip6 = &prefix->prefix; + struct in6_addr slaac; int prefix_len; + int rc; /* Skip dead prefixes */ if ( ! prefix->valid ) return -ENOENT; /* Construct IPv6 address via SLAAC, if applicable */ - memcpy ( &ip6, &prefix->prefix, sizeof ( ip6 ) ); if ( prefix->flags & NDP_PREFIX_AUTONOMOUS ) { - prefix_len = ipv6_eui64 ( &ip6, netdev ); - if ( prefix_len < 0 ) - return prefix_len; - if ( prefix_len != prefix->prefix_len ) - return -EINVAL; + memcpy ( &slaac, ip6, sizeof ( slaac ) ); + prefix_len = ipv6_eui64 ( &slaac, netdev ); + if ( prefix_len == prefix->prefix_len ) { + /* Correctly configured prefix: use SLAAC address */ + ip6 = &slaac; + } else if ( prefix_len < 0 ) { + /* Link layer does not support SLAAC */ + rc = prefix_len; + DBGC ( netdev, "NDP %s does not support SLAAC: %s\n", + netdev->name, strerror ( rc ) ); + } else { + /* Prefix length incorrect: assume a badly + * configured router and ignore SLAAC address. + */ + DBGC ( netdev, "NDP %s ignoring misconfigured SLAAC " + "on prefix %s/%d\n", netdev->name, + inet6_ntoa ( ip6 ), prefix->prefix_len ); + } } /* Fill in IPv6 address */ - if ( len > sizeof ( ip6 ) ) - len = sizeof ( ip6 ); - memcpy ( data, &ip6, len ); + if ( len > sizeof ( *ip6 ) ) + len = sizeof ( *ip6 ); + memcpy ( data, ip6, len ); - return sizeof ( ip6 ); + return sizeof ( *ip6 ); } /** -- cgit v1.2.3-55-g7522 From 007d3cb800fd0e4b01be8a76f0cce2c795cfc89b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 9 Oct 2022 15:14:41 +0100 Subject: [crypto] Simplify internal HMAC API Simplify the internal HMAC API so that the key is provided only at the point of calling hmac_init(), and the (potentially reduced) key is stored as part of the context for later use by hmac_final(). This simplifies the calling code, and avoids the need for callers such as TLS to allocate a potentially variable length block in order to retain a copy of the unmodified key. Signed-off-by: Michael Brown --- src/crypto/hmac.c | 96 ++++++++++++++++------------------------------- src/crypto/hmac_drbg.c | 16 +++----- src/crypto/ntlm.c | 19 ++++------ src/crypto/sha1extra.c | 16 ++++---- src/include/ipxe/hmac.h | 40 +++++++++++++++----- src/include/ipxe/md4.h | 3 ++ src/include/ipxe/md5.h | 3 ++ src/include/ipxe/sha1.h | 3 ++ src/include/ipxe/sha256.h | 3 ++ src/include/ipxe/sha512.h | 3 ++ src/net/80211/wpa_ccmp.c | 11 +++--- src/net/80211/wpa_tkip.c | 9 ++--- src/net/pccrc.c | 9 ++--- src/net/tls.c | 51 +++++++++++-------------- src/tests/hmac_test.c | 14 +++---- src/tests/pccrc_test.c | 9 ++--- 16 files changed, 142 insertions(+), 163 deletions(-) (limited to 'src/net') diff --git a/src/crypto/hmac.c b/src/crypto/hmac.c index f898619c8..7109bbf6a 100644 --- a/src/crypto/hmac.c +++ b/src/crypto/hmac.c @@ -46,94 +46,62 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include -/** - * Reduce HMAC key length - * - * @v digest Digest algorithm to use - * @v digest_ctx Digest context - * @v key Key - * @v key_len Length of key - */ -static void hmac_reduce_key ( struct digest_algorithm *digest, - void *key, size_t *key_len ) { - uint8_t digest_ctx[digest->ctxsize]; - - digest_init ( digest, digest_ctx ); - digest_update ( digest, digest_ctx, key, *key_len ); - digest_final ( digest, digest_ctx, key ); - *key_len = digest->digestsize; -} - /** * Initialise HMAC * * @v digest Digest algorithm to use - * @v digest_ctx Digest context + * @v ctx HMAC context * @v key Key * @v key_len Length of key - * - * The length of the key should be less than the block size of the - * digest algorithm being used. (If the key length is greater, it - * will be replaced with its own digest, and key_len will be updated - * accordingly). */ -void hmac_init ( struct digest_algorithm *digest, void *digest_ctx, - void *key, size_t *key_len ) { - unsigned char k_ipad[digest->blocksize]; +void hmac_init ( struct digest_algorithm *digest, void *ctx, const void *key, + size_t key_len ) { + hmac_context_t ( digest ) *hctx = ctx; unsigned int i; - /* Reduce key if necessary */ - if ( *key_len > sizeof ( k_ipad ) ) - hmac_reduce_key ( digest, key, key_len ); - /* Construct input pad */ - memset ( k_ipad, 0, sizeof ( k_ipad ) ); - memcpy ( k_ipad, key, *key_len ); - for ( i = 0 ; i < sizeof ( k_ipad ) ; i++ ) { - k_ipad[i] ^= 0x36; + memset ( hctx->pad, 0, sizeof ( hctx->pad ) ); + if ( key_len <= sizeof ( hctx->pad ) ) { + memcpy ( hctx->pad, key, key_len ); + } else { + digest_init ( digest, hctx->ctx ); + digest_update ( digest, hctx->ctx, key, key_len ); + digest_final ( digest, hctx->ctx, hctx->pad ); + } + for ( i = 0 ; i < sizeof ( hctx->pad ) ; i++ ) { + hctx->pad[i] ^= 0x36; } - + /* Start inner hash */ - digest_init ( digest, digest_ctx ); - digest_update ( digest, digest_ctx, k_ipad, sizeof ( k_ipad ) ); + digest_init ( digest, hctx->ctx ); + digest_update ( digest, hctx->ctx, hctx->pad, sizeof ( hctx->pad ) ); } /** * Finalise HMAC * * @v digest Digest algorithm to use - * @v digest_ctx Digest context - * @v key Key - * @v key_len Length of key + * @v ctx HMAC context * @v hmac HMAC digest to fill in - * - * The length of the key should be less than the block size of the - * digest algorithm being used. (If the key length is greater, it - * will be replaced with its own digest, and key_len will be updated - * accordingly). */ -void hmac_final ( struct digest_algorithm *digest, void *digest_ctx, - void *key, size_t *key_len, void *hmac ) { - unsigned char k_opad[digest->blocksize]; +void hmac_final ( struct digest_algorithm *digest, void *ctx, void *hmac ) { + hmac_context_t ( digest ) *hctx = ctx; unsigned int i; - /* Reduce key if necessary */ - if ( *key_len > sizeof ( k_opad ) ) - hmac_reduce_key ( digest, key, key_len ); - - /* Construct output pad */ - memset ( k_opad, 0, sizeof ( k_opad ) ); - memcpy ( k_opad, key, *key_len ); - for ( i = 0 ; i < sizeof ( k_opad ) ; i++ ) { - k_opad[i] ^= 0x5c; + /* Construct output pad from input pad */ + for ( i = 0 ; i < sizeof ( hctx->pad ) ; i++ ) { + hctx->pad[i] ^= 0x6a; } - + /* Finish inner hash */ - digest_final ( digest, digest_ctx, hmac ); + digest_final ( digest, hctx->ctx, hmac ); /* Perform outer hash */ - digest_init ( digest, digest_ctx ); - digest_update ( digest, digest_ctx, k_opad, sizeof ( k_opad ) ); - digest_update ( digest, digest_ctx, hmac, digest->digestsize ); - digest_final ( digest, digest_ctx, hmac ); + digest_init ( digest, hctx->ctx ); + digest_update ( digest, hctx->ctx, hctx->pad, sizeof ( hctx->pad ) ); + digest_update ( digest, hctx->ctx, hmac, digest->digestsize ); + digest_final ( digest, hctx->ctx, hmac ); + + /* Erase output pad (from which the key may be derivable) */ + memset ( hctx->pad, 0, sizeof ( hctx->pad ) ); } diff --git a/src/crypto/hmac_drbg.c b/src/crypto/hmac_drbg.c index 098297716..57bde4d1d 100644 --- a/src/crypto/hmac_drbg.c +++ b/src/crypto/hmac_drbg.c @@ -79,7 +79,7 @@ static void hmac_drbg_update_key ( struct digest_algorithm *hash, struct hmac_drbg_state *state, const void *data, size_t len, const uint8_t single ) { - uint8_t context[ hash->ctxsize ]; + uint8_t context[ hmac_ctxsize ( hash ) ]; size_t out_len = hash->digestsize; DBGC ( state, "HMAC_DRBG_%s %p provided data :\n", hash->name, state ); @@ -92,13 +92,11 @@ static void hmac_drbg_update_key ( struct digest_algorithm *hash, assert ( ( single == 0x00 ) || ( single == 0x01 ) ); /* K = HMAC ( K, V || single || provided_data ) */ - hmac_init ( hash, context, state->key, &out_len ); - assert ( out_len == hash->digestsize ); + hmac_init ( hash, context, state->key, out_len ); hmac_update ( hash, context, state->value, out_len ); hmac_update ( hash, context, &single, sizeof ( single ) ); hmac_update ( hash, context, data, len ); - hmac_final ( hash, context, state->key, &out_len, state->key ); - assert ( out_len == hash->digestsize ); + hmac_final ( hash, context, state->key ); DBGC ( state, "HMAC_DRBG_%s %p K = HMAC ( K, V || %#02x || " "provided_data ) :\n", hash->name, state, single ); @@ -122,7 +120,7 @@ static void hmac_drbg_update_key ( struct digest_algorithm *hash, */ static void hmac_drbg_update_value ( struct digest_algorithm *hash, struct hmac_drbg_state *state ) { - uint8_t context[ hash->ctxsize ]; + uint8_t context[ hmac_ctxsize ( hash ) ]; size_t out_len = hash->digestsize; /* Sanity checks */ @@ -130,11 +128,9 @@ static void hmac_drbg_update_value ( struct digest_algorithm *hash, assert ( state != NULL ); /* V = HMAC ( K, V ) */ - hmac_init ( hash, context, state->key, &out_len ); - assert ( out_len == hash->digestsize ); + hmac_init ( hash, context, state->key, out_len ); hmac_update ( hash, context, state->value, out_len ); - hmac_final ( hash, context, state->key, &out_len, state->value ); - assert ( out_len == hash->digestsize ); + hmac_final ( hash, context, state->value ); DBGC ( state, "HMAC_DRBG_%s %p V = HMAC ( K, V ) :\n", hash->name, state ); diff --git a/src/crypto/ntlm.c b/src/crypto/ntlm.c index 870af2132..fb120f8db 100644 --- a/src/crypto/ntlm.c +++ b/src/crypto/ntlm.c @@ -117,10 +117,9 @@ void ntlm_key ( const char *domain, const char *username, struct digest_algorithm *md5 = &md5_algorithm; union { uint8_t md4[MD4_CTX_SIZE]; - uint8_t md5[MD5_CTX_SIZE]; + uint8_t md5[ MD5_CTX_SIZE + MD5_BLOCK_SIZE ]; } ctx; uint8_t digest[MD4_DIGEST_SIZE]; - size_t digest_len; uint8_t c; uint16_t wc; @@ -141,8 +140,7 @@ void ntlm_key ( const char *domain, const char *username, digest_final ( md4, ctx.md4, digest ); /* Construct HMAC-MD5 of (Unicode) upper-case username */ - digest_len = sizeof ( digest ); - hmac_init ( md5, ctx.md5, digest, &digest_len ); + hmac_init ( md5, ctx.md5, digest, sizeof ( digest ) ); while ( ( c = *(username++) ) ) { wc = cpu_to_le16 ( toupper ( c ) ); hmac_update ( md5, ctx.md5, &wc, sizeof ( wc ) ); @@ -151,7 +149,7 @@ void ntlm_key ( const char *domain, const char *username, wc = cpu_to_le16 ( c ); hmac_update ( md5, ctx.md5, &wc, sizeof ( wc ) ); } - hmac_final ( md5, ctx.md5, digest, &digest_len, key->raw ); + hmac_final ( md5, ctx.md5, key->raw ); DBGC ( key, "NTLM key:\n" ); DBGC_HDA ( key, 0, key, sizeof ( *key ) ); } @@ -170,8 +168,7 @@ void ntlm_response ( struct ntlm_challenge_info *info, struct ntlm_key *key, struct ntlm_nt_response *nt ) { struct digest_algorithm *md5 = &md5_algorithm; struct ntlm_nonce tmp_nonce; - uint8_t ctx[MD5_CTX_SIZE]; - size_t key_len = sizeof ( *key ); + uint8_t ctx[ MD5_CTX_SIZE + MD5_BLOCK_SIZE ]; unsigned int i; /* Generate random nonce, if needed */ @@ -183,10 +180,10 @@ void ntlm_response ( struct ntlm_challenge_info *info, struct ntlm_key *key, /* Construct LAN Manager response */ memcpy ( &lm->nonce, nonce, sizeof ( lm->nonce ) ); - hmac_init ( md5, ctx, key->raw, &key_len ); + hmac_init ( md5, ctx, key->raw, sizeof ( *key ) ); hmac_update ( md5, ctx, info->nonce, sizeof ( *info->nonce ) ); hmac_update ( md5, ctx, &lm->nonce, sizeof ( lm->nonce ) ); - hmac_final ( md5, ctx, key->raw, &key_len, lm->digest ); + hmac_final ( md5, ctx, lm->digest ); DBGC ( key, "NTLM LAN Manager response:\n" ); DBGC_HDA ( key, 0, lm, sizeof ( *lm ) ); @@ -195,14 +192,14 @@ void ntlm_response ( struct ntlm_challenge_info *info, struct ntlm_key *key, nt->version = NTLM_VERSION_NTLMV2; nt->high = NTLM_VERSION_NTLMV2; memcpy ( &nt->nonce, nonce, sizeof ( nt->nonce ) ); - hmac_init ( md5, ctx, key->raw, &key_len ); + hmac_init ( md5, ctx, key->raw, sizeof ( *key ) ); hmac_update ( md5, ctx, info->nonce, sizeof ( *info->nonce ) ); hmac_update ( md5, ctx, &nt->version, ( sizeof ( *nt ) - offsetof ( typeof ( *nt ), version ) ) ); hmac_update ( md5, ctx, info->target, info->len ); hmac_update ( md5, ctx, &nt->zero, sizeof ( nt->zero ) ); - hmac_final ( md5, ctx, key->raw, &key_len, nt->digest ); + hmac_final ( md5, ctx, nt->digest ); DBGC ( key, "NTLM NT response prefix:\n" ); DBGC_HDA ( key, 0, nt, sizeof ( *nt ) ); } diff --git a/src/crypto/sha1extra.c b/src/crypto/sha1extra.c index cec0d35e5..9e296eb2c 100644 --- a/src/crypto/sha1extra.c +++ b/src/crypto/sha1extra.c @@ -49,7 +49,7 @@ void prf_sha1 ( const void *key, size_t key_len, const char *label, u8 in[strlen ( label ) + 1 + data_len + 1]; /* message to HMAC */ u8 *in_blknr; /* pointer to last byte of in, block number */ u8 out[SHA1_DIGEST_SIZE]; /* HMAC-SHA1 result */ - u8 sha1_ctx[SHA1_CTX_SIZE]; /* SHA1 context */ + u8 ctx[SHA1_CTX_SIZE + SHA1_BLOCK_SIZE]; /* HMAC-SHA1 context */ const size_t label_len = strlen ( label ); /* The HMAC-SHA-1 is calculated using the given key on the @@ -65,9 +65,9 @@ void prf_sha1 ( const void *key, size_t key_len, const char *label, for ( blk = 0 ;; blk++ ) { *in_blknr = blk; - hmac_init ( &sha1_algorithm, sha1_ctx, keym, &key_len ); - hmac_update ( &sha1_algorithm, sha1_ctx, in, sizeof ( in ) ); - hmac_final ( &sha1_algorithm, sha1_ctx, keym, &key_len, out ); + hmac_init ( &sha1_algorithm, ctx, keym, key_len ); + hmac_update ( &sha1_algorithm, ctx, in, sizeof ( in ) ); + hmac_final ( &sha1_algorithm, ctx, out ); if ( prf_len <= sizeof ( out ) ) { memcpy ( prf, out, prf_len ); @@ -100,7 +100,7 @@ static void pbkdf2_sha1_f ( const void *passphrase, size_t pass_len, u8 pass[pass_len]; /* modifiable passphrase */ u8 in[salt_len + 4]; /* input buffer to first round */ u8 last[SHA1_DIGEST_SIZE]; /* output of round N, input of N+1 */ - u8 sha1_ctx[SHA1_CTX_SIZE]; + u8 ctx[SHA1_CTX_SIZE + SHA1_BLOCK_SIZE]; u8 *next_in = in; /* changed to `last' after first round */ int next_size = sizeof ( in ); int i; @@ -114,9 +114,9 @@ static void pbkdf2_sha1_f ( const void *passphrase, size_t pass_len, memset ( block, 0, sizeof ( last ) ); for ( i = 0; i < iterations; i++ ) { - hmac_init ( &sha1_algorithm, sha1_ctx, pass, &pass_len ); - hmac_update ( &sha1_algorithm, sha1_ctx, next_in, next_size ); - hmac_final ( &sha1_algorithm, sha1_ctx, pass, &pass_len, last ); + hmac_init ( &sha1_algorithm, ctx, pass, pass_len ); + hmac_update ( &sha1_algorithm, ctx, next_in, next_size ); + hmac_final ( &sha1_algorithm, ctx, last ); for ( j = 0; j < sizeof ( last ); j++ ) { block[j] ^= last[j]; diff --git a/src/include/ipxe/hmac.h b/src/include/ipxe/hmac.h index 09d3e273d..cf9d08677 100644 --- a/src/include/ipxe/hmac.h +++ b/src/include/ipxe/hmac.h @@ -10,23 +10,45 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include +/** HMAC context type */ +#define hmac_context_t( digest ) struct { \ + /** Digest context */ \ + uint8_t ctx[ digest->ctxsize ]; \ + /** HMAC input/output padding */ \ + uint8_t pad[ digest->blocksize ]; \ + } __attribute__ (( packed )) + +/** + * Calculate HMAC context size + * + * @v digest Digest algorithm to use + * @ret len HMAC context size + */ +static inline __attribute__ (( always_inline )) size_t +hmac_ctxsize ( struct digest_algorithm *digest ) { + hmac_context_t ( digest ) *hctx; + + return sizeof ( *hctx ); +} + /** * Update HMAC * * @v digest Digest algorithm to use - * @v digest_ctx Digest context + * @v ctx HMAC context * @v data Data * @v len Length of data */ -static inline void hmac_update ( struct digest_algorithm *digest, - void *digest_ctx, const void *data, - size_t len ) { - digest_update ( digest, digest_ctx, data, len ); +static inline void hmac_update ( struct digest_algorithm *digest, void *ctx, + const void *data, size_t len ) { + hmac_context_t ( digest ) *hctx = ctx; + + digest_update ( digest, hctx->ctx, data, len ); } -extern void hmac_init ( struct digest_algorithm *digest, void *digest_ctx, - void *key, size_t *key_len ); -extern void hmac_final ( struct digest_algorithm *digest, void *digest_ctx, - void *key, size_t *key_len, void *hmac ); +extern void hmac_init ( struct digest_algorithm *digest, void *ctx, + const void *key, size_t key_len ); +extern void hmac_final ( struct digest_algorithm *digest, void *ctx, + void *hmac ); #endif /* _IPXE_HMAC_H */ diff --git a/src/include/ipxe/md4.h b/src/include/ipxe/md4.h index 8f172e626..9f6cb8a5f 100644 --- a/src/include/ipxe/md4.h +++ b/src/include/ipxe/md4.h @@ -65,6 +65,9 @@ struct md4_context { /** MD4 context size */ #define MD4_CTX_SIZE sizeof ( struct md4_context ) +/** MD4 block size */ +#define MD4_BLOCK_SIZE sizeof ( union md4_block ) + /** MD4 digest size */ #define MD4_DIGEST_SIZE sizeof ( struct md4_digest ) diff --git a/src/include/ipxe/md5.h b/src/include/ipxe/md5.h index 05c3974c8..527ad3658 100644 --- a/src/include/ipxe/md5.h +++ b/src/include/ipxe/md5.h @@ -65,6 +65,9 @@ struct md5_context { /** MD5 context size */ #define MD5_CTX_SIZE sizeof ( struct md5_context ) +/** MD5 block size */ +#define MD5_BLOCK_SIZE sizeof ( union md5_block ) + /** MD5 digest size */ #define MD5_DIGEST_SIZE sizeof ( struct md5_digest ) diff --git a/src/include/ipxe/sha1.h b/src/include/ipxe/sha1.h index a97035ec7..9cbbebdee 100644 --- a/src/include/ipxe/sha1.h +++ b/src/include/ipxe/sha1.h @@ -65,6 +65,9 @@ struct sha1_context { /** SHA-1 context size */ #define SHA1_CTX_SIZE sizeof ( struct sha1_context ) +/** SHA-1 block size */ +#define SHA1_BLOCK_SIZE sizeof ( union sha1_block ) + /** SHA-1 digest size */ #define SHA1_DIGEST_SIZE sizeof ( struct sha1_digest ) diff --git a/src/include/ipxe/sha256.h b/src/include/ipxe/sha256.h index e234cce33..f226ad07b 100644 --- a/src/include/ipxe/sha256.h +++ b/src/include/ipxe/sha256.h @@ -70,6 +70,9 @@ struct sha256_context { /** SHA-256 context size */ #define SHA256_CTX_SIZE sizeof ( struct sha256_context ) +/** SHA-256 block size */ +#define SHA256_BLOCK_SIZE sizeof ( union sha256_block ) + /** SHA-256 digest size */ #define SHA256_DIGEST_SIZE sizeof ( struct sha256_digest ) diff --git a/src/include/ipxe/sha512.h b/src/include/ipxe/sha512.h index 8e22d8357..82a9e4e69 100644 --- a/src/include/ipxe/sha512.h +++ b/src/include/ipxe/sha512.h @@ -72,6 +72,9 @@ struct sha512_context { /** SHA-512 context size */ #define SHA512_CTX_SIZE sizeof ( struct sha512_context ) +/** SHA-512 block size */ +#define SHA512_BLOCK_SIZE sizeof ( union sha512_block ) + /** SHA-512 digest size */ #define SHA512_DIGEST_SIZE sizeof ( struct sha512_digest ) diff --git a/src/net/80211/wpa_ccmp.c b/src/net/80211/wpa_ccmp.c index a073c6a3c..0abd217e7 100644 --- a/src/net/80211/wpa_ccmp.c +++ b/src/net/80211/wpa_ccmp.c @@ -478,16 +478,15 @@ struct net80211_crypto ccmp_crypto __net80211_crypto = { static void ccmp_kie_mic ( const void *kck, const void *msg, size_t len, void *mic ) { - u8 sha1_ctx[SHA1_CTX_SIZE]; + u8 ctx[SHA1_CTX_SIZE + SHA1_BLOCK_SIZE]; u8 kckb[16]; u8 hash[SHA1_DIGEST_SIZE]; - size_t kck_len = 16; - memcpy ( kckb, kck, kck_len ); + memcpy ( kckb, kck, sizeof ( kckb ) ); - hmac_init ( &sha1_algorithm, sha1_ctx, kckb, &kck_len ); - hmac_update ( &sha1_algorithm, sha1_ctx, msg, len ); - hmac_final ( &sha1_algorithm, sha1_ctx, kckb, &kck_len, hash ); + hmac_init ( &sha1_algorithm, ctx, kckb, sizeof ( kckb ) ); + hmac_update ( &sha1_algorithm, ctx, msg, len ); + hmac_final ( &sha1_algorithm, ctx, hash ); memcpy ( mic, hash, 16 ); } diff --git a/src/net/80211/wpa_tkip.c b/src/net/80211/wpa_tkip.c index 3b1934b59..3bd651512 100644 --- a/src/net/80211/wpa_tkip.c +++ b/src/net/80211/wpa_tkip.c @@ -545,15 +545,14 @@ struct net80211_crypto tkip_crypto __net80211_crypto = { static void tkip_kie_mic ( const void *kck, const void *msg, size_t len, void *mic ) { - uint8_t ctx[MD5_CTX_SIZE]; + uint8_t ctx[MD5_CTX_SIZE + MD5_BLOCK_SIZE]; u8 kckb[16]; - size_t kck_len = 16; - memcpy ( kckb, kck, kck_len ); + memcpy ( kckb, kck, sizeof ( kckb ) ); - hmac_init ( &md5_algorithm, ctx, kckb, &kck_len ); + hmac_init ( &md5_algorithm, ctx, kckb, sizeof ( kckb ) ); hmac_update ( &md5_algorithm, ctx, msg, len ); - hmac_final ( &md5_algorithm, ctx, kckb, &kck_len, mic ); + hmac_final ( &md5_algorithm, ctx, mic ); } /** diff --git a/src/net/pccrc.c b/src/net/pccrc.c index 4cd82cd1c..a94bc0e11 100644 --- a/src/net/pccrc.c +++ b/src/net/pccrc.c @@ -104,9 +104,8 @@ static void peerdist_info_segment_hash ( struct peerdist_info_segment *segment, const void *hash, const void *secret ){ const struct peerdist_info *info = segment->info; struct digest_algorithm *digest = info->digest; - uint8_t ctx[digest->ctxsize]; + uint8_t ctx[ hmac_ctxsize ( digest ) ]; size_t digestsize = info->digestsize; - size_t secretsize = digestsize; static const uint16_t magic[] = PEERDIST_SEGMENT_ID_MAGIC; /* Sanity check */ @@ -121,12 +120,10 @@ static void peerdist_info_segment_hash ( struct peerdist_info_segment *segment, memcpy ( segment->secret, secret, digestsize ); /* Calculate segment identifier */ - hmac_init ( digest, ctx, segment->secret, &secretsize ); - assert ( secretsize == digestsize ); + hmac_init ( digest, ctx, segment->secret, digestsize ); hmac_update ( digest, ctx, segment->hash, digestsize ); hmac_update ( digest, ctx, magic, sizeof ( magic ) ); - hmac_final ( digest, ctx, segment->secret, &secretsize, segment->id ); - assert ( secretsize == digestsize ); + hmac_final ( digest, ctx, segment->id ); } /****************************************************************************** diff --git a/src/net/tls.c b/src/net/tls.c index 3c4144450..21f707340 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -458,17 +458,17 @@ static int tls_generate_random ( struct tls_connection *tls, * Update HMAC with a list of ( data, len ) pairs * * @v digest Hash function to use - * @v digest_ctx Digest context + * @v ctx HMAC context * @v args ( data, len ) pairs of data, terminated by NULL */ static void tls_hmac_update_va ( struct digest_algorithm *digest, - void *digest_ctx, va_list args ) { + void *ctx, va_list args ) { void *data; size_t len; while ( ( data = va_arg ( args, void * ) ) ) { len = va_arg ( args, size_t ); - hmac_update ( digest, digest_ctx, data, len ); + hmac_update ( digest, ctx, data, len ); } } @@ -485,43 +485,38 @@ static void tls_hmac_update_va ( struct digest_algorithm *digest, */ static void tls_p_hash_va ( struct tls_connection *tls, struct digest_algorithm *digest, - void *secret, size_t secret_len, + const void *secret, size_t secret_len, void *out, size_t out_len, va_list seeds ) { - uint8_t secret_copy[secret_len]; - uint8_t digest_ctx[digest->ctxsize]; - uint8_t digest_ctx_partial[digest->ctxsize]; + uint8_t ctx[ hmac_ctxsize ( digest ) ]; + uint8_t ctx_partial[ sizeof ( ctx ) ]; uint8_t a[digest->digestsize]; uint8_t out_tmp[digest->digestsize]; size_t frag_len = digest->digestsize; va_list tmp; - /* Copy the secret, in case HMAC modifies it */ - memcpy ( secret_copy, secret, secret_len ); - secret = secret_copy; DBGC2 ( tls, "TLS %p %s secret:\n", tls, digest->name ); DBGC2_HD ( tls, secret, secret_len ); /* Calculate A(1) */ - hmac_init ( digest, digest_ctx, secret, &secret_len ); + hmac_init ( digest, ctx, secret, secret_len ); va_copy ( tmp, seeds ); - tls_hmac_update_va ( digest, digest_ctx, tmp ); + tls_hmac_update_va ( digest, ctx, tmp ); va_end ( tmp ); - hmac_final ( digest, digest_ctx, secret, &secret_len, a ); + hmac_final ( digest, ctx, a ); DBGC2 ( tls, "TLS %p %s A(1):\n", tls, digest->name ); DBGC2_HD ( tls, &a, sizeof ( a ) ); /* Generate as much data as required */ while ( out_len ) { /* Calculate output portion */ - hmac_init ( digest, digest_ctx, secret, &secret_len ); - hmac_update ( digest, digest_ctx, a, sizeof ( a ) ); - memcpy ( digest_ctx_partial, digest_ctx, digest->ctxsize ); + hmac_init ( digest, ctx, secret, secret_len ); + hmac_update ( digest, ctx, a, sizeof ( a ) ); + memcpy ( ctx_partial, ctx, sizeof ( ctx_partial ) ); va_copy ( tmp, seeds ); - tls_hmac_update_va ( digest, digest_ctx, tmp ); + tls_hmac_update_va ( digest, ctx, tmp ); va_end ( tmp ); - hmac_final ( digest, digest_ctx, - secret, &secret_len, out_tmp ); + hmac_final ( digest, ctx, out_tmp ); /* Copy output */ if ( frag_len > out_len ) @@ -531,8 +526,7 @@ static void tls_p_hash_va ( struct tls_connection *tls, DBGC2_HD ( tls, out, frag_len ); /* Calculate A(i) */ - hmac_final ( digest, digest_ctx_partial, - secret, &secret_len, a ); + hmac_final ( digest, ctx_partial, a ); DBGC2 ( tls, "TLS %p %s A(n):\n", tls, digest->name ); DBGC2_HD ( tls, &a, sizeof ( a ) ); @@ -551,13 +545,13 @@ static void tls_p_hash_va ( struct tls_connection *tls, * @v out_len Length of output buffer * @v ... ( data, len ) pairs of seed data, terminated by NULL */ -static void tls_prf ( struct tls_connection *tls, void *secret, +static void tls_prf ( struct tls_connection *tls, const void *secret, size_t secret_len, void *out, size_t out_len, ... ) { va_list seeds; va_list tmp; size_t subsecret_len; - void *md5_secret; - void *sha1_secret; + const void *md5_secret; + const void *sha1_secret; uint8_t buf[out_len]; unsigned int i; @@ -2213,7 +2207,7 @@ static void tls_hmac_init ( struct tls_cipherspec *cipherspec, void *ctx, uint64_t seq, struct tls_header *tlshdr ) { struct digest_algorithm *digest = cipherspec->suite->digest; - hmac_init ( digest, ctx, cipherspec->mac_secret, &digest->digestsize ); + hmac_init ( digest, ctx, cipherspec->mac_secret, digest->digestsize ); seq = cpu_to_be64 ( seq ); hmac_update ( digest, ctx, &seq, sizeof ( seq ) ); hmac_update ( digest, ctx, tlshdr, sizeof ( *tlshdr ) ); @@ -2245,8 +2239,7 @@ static void tls_hmac_final ( struct tls_cipherspec *cipherspec, void *ctx, void *hmac ) { struct digest_algorithm *digest = cipherspec->suite->digest; - hmac_final ( digest, ctx, cipherspec->mac_secret, - &digest->digestsize, hmac ); + hmac_final ( digest, ctx, hmac ); } /** @@ -2263,7 +2256,7 @@ static void tls_hmac ( struct tls_cipherspec *cipherspec, uint64_t seq, struct tls_header *tlshdr, const void *data, size_t len, void *hmac ) { struct digest_algorithm *digest = cipherspec->suite->digest; - uint8_t ctx[digest->ctxsize]; + uint8_t ctx[ hmac_ctxsize ( digest ) ]; tls_hmac_init ( cipherspec, ctx, seq, tlshdr ); tls_hmac_update ( cipherspec, ctx, data, len ); @@ -2545,7 +2538,7 @@ static int tls_new_ciphertext ( struct tls_connection *tls, struct tls_cipherspec *cipherspec = &tls->rx_cipherspec; struct cipher_algorithm *cipher = cipherspec->suite->cipher; struct digest_algorithm *digest = cipherspec->suite->digest; - uint8_t ctx[digest->ctxsize]; + uint8_t ctx[ hmac_ctxsize ( digest ) ]; uint8_t verify_mac[digest->digestsize]; struct io_buffer *iobuf; void *mac; diff --git a/src/tests/hmac_test.c b/src/tests/hmac_test.c index 871926f90..5267999e4 100644 --- a/src/tests/hmac_test.c +++ b/src/tests/hmac_test.c @@ -100,26 +100,22 @@ struct hmac_test { static void hmac_okx ( struct hmac_test *test, const char *file, unsigned int line ) { struct digest_algorithm *digest = test->digest; - uint8_t ctx[digest->ctxsize]; + uint8_t ctx[ hmac_ctxsize ( digest ) ]; uint8_t hmac[digest->digestsize]; - uint8_t key[test->key_len]; - size_t key_len; /* Sanity checks */ + okx ( sizeof ( ctx ) == ( digest->ctxsize + digest->blocksize ), + file, line ); okx ( test->expected_len == digest->digestsize, file, line ); - /* Create modifiable copy of key */ - memcpy ( key, test->key, test->key_len ); - key_len = test->key_len; - /* Calculate HMAC */ DBGC ( test, "HMAC-%s key:\n", digest->name ); DBGC_HDA ( test, 0, test->key, test->key_len ); DBGC ( test, "HMAC-%s data:\n", digest->name ); DBGC_HDA ( test, 0, test->data, test->data_len ); - hmac_init ( digest, ctx, key, &key_len ); + hmac_init ( digest, ctx, test->key, test->key_len ); hmac_update ( digest, ctx, test->data, test->data_len ); - hmac_final ( digest, ctx, key, &key_len, hmac ); + hmac_final ( digest, ctx, hmac ); DBGC ( test, "HMAC-%s result:\n", digest->name ); DBGC_HDA ( test, 0, hmac, sizeof ( hmac ) ); diff --git a/src/tests/pccrc_test.c b/src/tests/pccrc_test.c index f4ab573ac..e69493202 100644 --- a/src/tests/pccrc_test.c +++ b/src/tests/pccrc_test.c @@ -467,11 +467,10 @@ peerdist_info_passphrase_okx ( struct peerdist_info_segment_test *test, uint8_t *pass, size_t pass_len, const char *file, unsigned int line ) { struct digest_algorithm *digest = info->digest; - uint8_t ctx[digest->ctxsize]; + uint8_t ctx[ hmac_ctxsize ( digest ) ]; uint8_t secret[digest->digestsize]; uint8_t expected[digest->digestsize]; size_t digestsize = info->digestsize; - size_t secretsize = digestsize; /* Calculate server secret */ digest_init ( digest, ctx ); @@ -479,11 +478,9 @@ peerdist_info_passphrase_okx ( struct peerdist_info_segment_test *test, digest_final ( digest, ctx, secret ); /* Calculate expected segment secret */ - hmac_init ( digest, ctx, secret, &secretsize ); - assert ( secretsize == digestsize ); + hmac_init ( digest, ctx, secret, digestsize ); hmac_update ( digest, ctx, test->expected_hash, digestsize ); - hmac_final ( digest, ctx, secret, &secretsize, expected ); - assert ( secretsize == digestsize ); + hmac_final ( digest, ctx, expected ); /* Verify segment secret */ okx ( memcmp ( test->expected_secret, expected, digestsize ) == 0, -- cgit v1.2.3-55-g7522 From 1a7317e7d46f134f21244f6d272f90648beda4e7 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 6 Oct 2022 16:06:44 +0100 Subject: [tls] Generate master secret at point of sending ClientKeyExchange The master secret is currently constructed upon receiving the ServerHello message. This precludes the use of key exchange mechanisms such as Ephemeral Diffie-Hellman (DHE), which require a ServerKeyExchange message to exchange additional key material before the pre-master secret and master secret can be constructed. Allow for the use of such cipher suites by deferring generation of the master secret until the point of sending the ClientKeyExchange message. Signed-off-by: Michael Brown --- src/net/tls.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) (limited to 'src/net') diff --git a/src/net/tls.c b/src/net/tls.c index 21f707340..89ed6fceb 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -1220,6 +1220,16 @@ static int tls_send_client_key_exchange ( struct tls_connection *tls ) { int len; int rc; + /* Generate master secret */ + tls_generate_master_secret ( tls ); + + /* Generate keys */ + if ( ( rc = tls_generate_keys ( tls ) ) != 0 ) { + DBGC ( tls, "TLS %p could not generate keys: %s\n", + tls, strerror ( rc ) ); + return rc; + } + /* Encrypt pre-master secret using server's public key */ memset ( &key_xchg, 0, sizeof ( key_xchg ) ); len = pubkey_encrypt ( pubkey, cipherspec->pubkey_ctx, @@ -1622,7 +1632,7 @@ static int tls_new_server_hello ( struct tls_connection *tls, if ( ( rc = tls_select_cipher ( tls, hello_b->cipher_suite ) ) != 0 ) return rc; - /* Reuse or generate master secret */ + /* Check session ID */ if ( hello_a->session_id_len && ( hello_a->session_id_len == tls->session_id_len ) && ( memcmp ( session_id, tls->session_id, @@ -1631,12 +1641,11 @@ static int tls_new_server_hello ( struct tls_connection *tls, /* Session ID match: reuse master secret */ DBGC ( tls, "TLS %p resuming session ID:\n", tls ); DBGC_HDA ( tls, 0, tls->session_id, tls->session_id_len ); + if ( ( rc = tls_generate_keys ( tls ) ) != 0 ) + return rc; } else { - /* Generate new master secret */ - tls_generate_master_secret ( tls ); - /* Record new session ID, if present */ if ( hello_a->session_id_len && ( hello_a->session_id_len <= sizeof ( tls->session_id ))){ @@ -1649,10 +1658,6 @@ static int tls_new_server_hello ( struct tls_connection *tls, } } - /* Generate keys */ - if ( ( rc = tls_generate_keys ( tls ) ) != 0 ) - return rc; - /* Handle secure renegotiation */ if ( tls->secure_renegotiation ) { -- cgit v1.2.3-55-g7522 From 028aac99a397f591de6cc6f6f2b4763f55aa8962 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 11 Oct 2022 13:47:06 +0100 Subject: [tls] Generate pre-master secret at point of sending ClientKeyExchange The pre-master secret is currently constructed at the time of instantiating the TLS connection. This precludes the use of key exchange mechanisms such as Ephemeral Diffie-Hellman (DHE), which require a ServerKeyExchange message to exchange additional key material before the pre-master secret can be constructed. Allow for the use of such cipher suites by deferring generation of the master secret until the point of sending the ClientKeyExchange message. Signed-off-by: Michael Brown --- src/include/ipxe/tls.h | 13 +++---------- src/net/tls.c | 40 ++++++++++++++++++++++++---------------- 2 files changed, 27 insertions(+), 26 deletions(-) (limited to 'src/net') diff --git a/src/include/ipxe/tls.h b/src/include/ipxe/tls.h index 8b03579cc..65608970a 100644 --- a/src/include/ipxe/tls.h +++ b/src/include/ipxe/tls.h @@ -48,6 +48,9 @@ struct tls_header { /** TLS version 1.2 */ #define TLS_VERSION_TLS_1_2 0x0303 +/** Maximum supported TLS version */ +#define TLS_VERSION_MAX TLS_VERSION_TLS_1_2 + /** Change cipher content type */ #define TLS_TYPE_CHANGE_CIPHER 20 @@ -209,14 +212,6 @@ struct tls_signature_hash_algorithm { #define __tls_sig_hash_algorithm \ __table_entry ( TLS_SIG_HASH_ALGORITHMS, 01 ) -/** TLS pre-master secret */ -struct tls_pre_master_secret { - /** TLS version */ - uint16_t version; - /** Random data */ - uint8_t random[46]; -} __attribute__ (( packed )); - /** TLS client random data */ struct tls_client_random { /** GMT Unix time */ @@ -309,8 +304,6 @@ struct tls_connection { struct tls_cipherspec rx_cipherspec; /** Next RX cipher specification */ struct tls_cipherspec rx_cipherspec_pending; - /** Premaster secret */ - struct tls_pre_master_secret pre_master_secret; /** Master secret */ uint8_t master_secret[48]; /** Server random bytes */ diff --git a/src/net/tls.c b/src/net/tls.c index 89ed6fceb..4fb5b179e 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -616,21 +616,23 @@ static void tls_prf ( struct tls_connection *tls, const void *secret, * Generate master secret * * @v tls TLS connection + * @v pre_master_secret Pre-master secret + * @v pre_master_secret_len Length of pre-master secret * - * The pre-master secret and the client and server random values must - * already be known. + * The client and server random values must already be known. */ -static void tls_generate_master_secret ( struct tls_connection *tls ) { +static void tls_generate_master_secret ( struct tls_connection *tls, + const void *pre_master_secret, + size_t pre_master_secret_len ) { + DBGC ( tls, "TLS %p pre-master-secret:\n", tls ); - DBGC_HD ( tls, &tls->pre_master_secret, - sizeof ( tls->pre_master_secret ) ); + DBGC_HD ( tls, pre_master_secret, pre_master_secret_len ); DBGC ( tls, "TLS %p client random bytes:\n", tls ); DBGC_HD ( tls, &tls->client_random, sizeof ( tls->client_random ) ); DBGC ( tls, "TLS %p server random bytes:\n", tls ); DBGC_HD ( tls, &tls->server_random, sizeof ( tls->server_random ) ); - tls_prf_label ( tls, &tls->pre_master_secret, - sizeof ( tls->pre_master_secret ), + tls_prf_label ( tls, pre_master_secret, pre_master_secret_len, &tls->master_secret, sizeof ( tls->master_secret ), "master secret", &tls->client_random, sizeof ( tls->client_random ), @@ -1211,6 +1213,10 @@ static int tls_send_client_key_exchange ( struct tls_connection *tls ) { struct tls_cipherspec *cipherspec = &tls->tx_cipherspec_pending; struct pubkey_algorithm *pubkey = cipherspec->suite->pubkey; size_t max_len = pubkey_max_len ( pubkey, cipherspec->pubkey_ctx ); + struct { + uint16_t version; + uint8_t random[46]; + } __attribute__ (( packed )) pre_master_secret; struct { uint32_t type_length; uint16_t encrypted_pre_master_secret_len; @@ -1220,8 +1226,16 @@ static int tls_send_client_key_exchange ( struct tls_connection *tls ) { int len; int rc; + /* Generate pre-master secret */ + pre_master_secret.version = htons ( TLS_VERSION_MAX ); + if ( ( rc = tls_generate_random ( tls, &pre_master_secret.random, + ( sizeof ( pre_master_secret.random ) ) ) ) != 0 ) { + return rc; + } + /* Generate master secret */ - tls_generate_master_secret ( tls ); + tls_generate_master_secret ( tls, &pre_master_secret, + sizeof ( pre_master_secret ) ); /* Generate keys */ if ( ( rc = tls_generate_keys ( tls ) ) != 0 ) { @@ -1233,8 +1247,7 @@ static int tls_send_client_key_exchange ( struct tls_connection *tls ) { /* Encrypt pre-master secret using server's public key */ memset ( &key_xchg, 0, sizeof ( key_xchg ) ); len = pubkey_encrypt ( pubkey, cipherspec->pubkey_ctx, - &tls->pre_master_secret, - sizeof ( tls->pre_master_secret ), + &pre_master_secret, sizeof ( pre_master_secret ), key_xchg.encrypted_pre_master_secret ); if ( len < 0 ) { rc = len; @@ -3173,7 +3186,7 @@ int add_tls ( struct interface *xfer, const char *name, &tls->refcnt ); tls->key = privkey_get ( key ? key : &private_key ); tls->root = x509_root_get ( root ? root : &root_certificates ); - tls->version = TLS_VERSION_TLS_1_2; + tls->version = TLS_VERSION_MAX; tls_clear_cipher ( tls, &tls->tx_cipherspec ); tls_clear_cipher ( tls, &tls->tx_cipherspec_pending ); tls_clear_cipher ( tls, &tls->rx_cipherspec ); @@ -3186,11 +3199,6 @@ int add_tls ( struct interface *xfer, const char *name, ( sizeof ( tls->client_random.random ) ) ) ) != 0 ) { goto err_random; } - tls->pre_master_secret.version = htons ( tls->version ); - if ( ( rc = tls_generate_random ( tls, &tls->pre_master_secret.random, - ( sizeof ( tls->pre_master_secret.random ) ) ) ) != 0 ) { - goto err_random; - } if ( ( rc = tls_session ( tls, name ) ) != 0 ) goto err_session; list_add_tail ( &tls->list, &tls->session->conn ); -- cgit v1.2.3-55-g7522 From 80c45c5c71af76e4313c37528d29aa485b247073 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 11 Oct 2022 13:49:57 +0100 Subject: [tls] Record ServerKeyExchange record, if provided Accept and record the ServerKeyExchange record, which is required for key exchange mechanisms such as Ephemeral Diffie-Hellman (DHE). Signed-off-by: Michael Brown --- src/include/ipxe/tls.h | 4 ++++ src/net/tls.c | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) (limited to 'src/net') diff --git a/src/include/ipxe/tls.h b/src/include/ipxe/tls.h index 65608970a..672cfbd7e 100644 --- a/src/include/ipxe/tls.h +++ b/src/include/ipxe/tls.h @@ -310,6 +310,10 @@ struct tls_connection { uint8_t server_random[32]; /** Client random bytes */ struct tls_client_random client_random; + /** Server Key Exchange record (if any) */ + void *server_key; + /** Server Key Exchange record length */ + size_t server_key_len; /** MD5+SHA1 context for handshake verification */ uint8_t handshake_md5_sha1_ctx[MD5_SHA1_CTX_SIZE]; /** SHA256 context for handshake verification */ diff --git a/src/net/tls.c b/src/net/tls.c index 4fb5b179e..a1ffcacd7 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -377,6 +377,7 @@ static void free_tls ( struct refcnt *refcnt ) { tls_clear_cipher ( tls, &tls->tx_cipherspec_pending ); tls_clear_cipher ( tls, &tls->rx_cipherspec ); tls_clear_cipher ( tls, &tls->rx_cipherspec_pending ); + free ( tls->server_key ); list_for_each_entry_safe ( iobuf, tmp, &tls->rx_data, list ) { list_del ( &iobuf->list ); free_iob ( iobuf ); @@ -1867,6 +1868,37 @@ static int tls_new_certificate ( struct tls_connection *tls, return 0; } +/** + * Receive new Server Key Exchange handshake record + * + * @v tls TLS connection + * @v data Plaintext handshake record + * @v len Length of plaintext handshake record + * @ret rc Return status code + */ +static int tls_new_server_key_exchange ( struct tls_connection *tls, + const void *data, size_t len ) { + + /* Free any existing server key exchange record */ + free ( tls->server_key ); + tls->server_key_len = 0; + + /* Allocate copy of server key exchange record */ + tls->server_key = malloc ( len ); + if ( ! tls->server_key ) + return -ENOMEM; + + /* Store copy of server key exchange record for later + * processing. We cannot verify the signature at this point + * since the certificate validation will not yet have + * completed. + */ + memcpy ( tls->server_key, data, len ); + tls->server_key_len = len; + + return 0; +} + /** * Receive new Certificate Request handshake record * @@ -2100,6 +2132,10 @@ static int tls_new_handshake ( struct tls_connection *tls, case TLS_CERTIFICATE: rc = tls_new_certificate ( tls, payload, payload_len ); break; + case TLS_SERVER_KEY_EXCHANGE: + rc = tls_new_server_key_exchange ( tls, payload, + payload_len ); + break; case TLS_CERTIFICATE_REQUEST: rc = tls_new_certificate_request ( tls, payload, payload_len ); -- cgit v1.2.3-55-g7522 From ea33ea33c0d77b853c39d7b0e8c54f1a6f56b6bc Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 11 Oct 2022 13:54:34 +0100 Subject: [tls] Add key exchange mechanism to definition of cipher suite Allow for the key exchange mechanism to vary depending upon the selected cipher suite. Signed-off-by: Michael Brown --- src/crypto/mishmash/rsa_aes_cbc_sha1.c | 2 ++ src/crypto/mishmash/rsa_aes_cbc_sha256.c | 2 ++ src/include/ipxe/tls.h | 19 +++++++++++++++++++ src/net/tls.c | 28 +++++++++++++++++++++++++--- 4 files changed, 48 insertions(+), 3 deletions(-) (limited to 'src/net') diff --git a/src/crypto/mishmash/rsa_aes_cbc_sha1.c b/src/crypto/mishmash/rsa_aes_cbc_sha1.c index 06722c0e1..04b4ce2a7 100644 --- a/src/crypto/mishmash/rsa_aes_cbc_sha1.c +++ b/src/crypto/mishmash/rsa_aes_cbc_sha1.c @@ -33,6 +33,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct tls_cipher_suite tls_rsa_with_aes_128_cbc_sha __tls_cipher_suite (03) = { .code = htons ( TLS_RSA_WITH_AES_128_CBC_SHA ), .key_len = ( 128 / 8 ), + .exchange = &tls_pubkey_exchange_algorithm, .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, .digest = &sha1_algorithm, @@ -42,6 +43,7 @@ struct tls_cipher_suite tls_rsa_with_aes_128_cbc_sha __tls_cipher_suite (03) = { struct tls_cipher_suite tls_rsa_with_aes_256_cbc_sha __tls_cipher_suite (04) = { .code = htons ( TLS_RSA_WITH_AES_256_CBC_SHA ), .key_len = ( 256 / 8 ), + .exchange = &tls_pubkey_exchange_algorithm, .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, .digest = &sha1_algorithm, diff --git a/src/crypto/mishmash/rsa_aes_cbc_sha256.c b/src/crypto/mishmash/rsa_aes_cbc_sha256.c index c609eacea..1021f76f4 100644 --- a/src/crypto/mishmash/rsa_aes_cbc_sha256.c +++ b/src/crypto/mishmash/rsa_aes_cbc_sha256.c @@ -33,6 +33,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct tls_cipher_suite tls_rsa_with_aes_128_cbc_sha256 __tls_cipher_suite(01)={ .code = htons ( TLS_RSA_WITH_AES_128_CBC_SHA256 ), .key_len = ( 128 / 8 ), + .exchange = &tls_pubkey_exchange_algorithm, .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, .digest = &sha256_algorithm, @@ -42,6 +43,7 @@ struct tls_cipher_suite tls_rsa_with_aes_128_cbc_sha256 __tls_cipher_suite(01)={ struct tls_cipher_suite tls_rsa_with_aes_256_cbc_sha256 __tls_cipher_suite(02)={ .code = htons ( TLS_RSA_WITH_AES_256_CBC_SHA256 ), .key_len = ( 256 / 8 ), + .exchange = &tls_pubkey_exchange_algorithm, .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, .digest = &sha256_algorithm, diff --git a/src/include/ipxe/tls.h b/src/include/ipxe/tls.h index 672cfbd7e..80cdd12fe 100644 --- a/src/include/ipxe/tls.h +++ b/src/include/ipxe/tls.h @@ -23,6 +23,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +struct tls_connection; + /** A TLS header */ struct tls_header { /** Content type @@ -143,8 +145,23 @@ enum tls_tx_pending { TLS_TX_FINISHED = 0x0020, }; +/** A TLS key exchange algorithm */ +struct tls_key_exchange_algorithm { + /** Algorithm name */ + const char *name; + /** + * Transmit Client Key Exchange record + * + * @v tls TLS connection + * @ret rc Return status code + */ + int ( * exchange ) ( struct tls_connection *tls ); +}; + /** A TLS cipher suite */ struct tls_cipher_suite { + /** Key exchange algorithm */ + struct tls_key_exchange_algorithm *exchange; /** Public-key encryption algorithm */ struct pubkey_algorithm *pubkey; /** Bulk encryption cipher algorithm */ @@ -385,6 +402,8 @@ struct tls_connection { /** RX I/O buffer alignment */ #define TLS_RX_ALIGN 16 +extern struct tls_key_exchange_algorithm tls_pubkey_exchange_algorithm; + extern int add_tls ( struct interface *xfer, const char *name, struct x509_root *root, struct private_key *key ); diff --git a/src/net/tls.c b/src/net/tls.c index a1ffcacd7..b209e0d80 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -734,6 +734,7 @@ static int tls_generate_keys ( struct tls_connection *tls ) { /** Null cipher suite */ struct tls_cipher_suite tls_cipher_suite_null = { + .exchange = &tls_pubkey_exchange_algorithm, .pubkey = &pubkey_null, .cipher = &cipher_null, .digest = &digest_null, @@ -849,7 +850,8 @@ static int tls_select_cipher ( struct tls_connection *tls, suite ) ) != 0 ) return rc; - DBGC ( tls, "TLS %p selected %s-%s-%d-%s\n", tls, suite->pubkey->name, + DBGC ( tls, "TLS %p selected %s-%s-%s-%d-%s\n", tls, + suite->exchange->name, suite->pubkey->name, suite->cipher->name, ( suite->key_len * 8 ), suite->digest->name ); @@ -1205,12 +1207,12 @@ static int tls_send_certificate ( struct tls_connection *tls ) { } /** - * Transmit Client Key Exchange record + * Transmit Client Key Exchange record using public key exchange * * @v tls TLS connection * @ret rc Return status code */ -static int tls_send_client_key_exchange ( struct tls_connection *tls ) { +static int tls_send_client_key_exchange_pubkey ( struct tls_connection *tls ) { struct tls_cipherspec *cipherspec = &tls->tx_cipherspec_pending; struct pubkey_algorithm *pubkey = cipherspec->suite->pubkey; size_t max_len = pubkey_max_len ( pubkey, cipherspec->pubkey_ctx ); @@ -1269,6 +1271,26 @@ static int tls_send_client_key_exchange ( struct tls_connection *tls ) { ( sizeof ( key_xchg ) - unused ) ); } +/** Public key exchange algorithm */ +struct tls_key_exchange_algorithm tls_pubkey_exchange_algorithm = { + .name = "pubkey", + .exchange = tls_send_client_key_exchange_pubkey, +}; + +/** + * Transmit Client Key Exchange record + * + * @v tls TLS connection + * @ret rc Return status code + */ +static int tls_send_client_key_exchange ( struct tls_connection *tls ) { + struct tls_cipherspec *cipherspec = &tls->tx_cipherspec_pending; + struct tls_cipher_suite *suite = cipherspec->suite; + + /* Transmit Client Key Exchange record via key exchange algorithm */ + return suite->exchange->exchange ( tls ); +} + /** * Transmit Certificate Verify record * -- cgit v1.2.3-55-g7522 From 6b2c94d3a7d93a8fc47fcb0b895477d4dafca5f0 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 11 Oct 2022 13:55:56 +0100 Subject: [tls] Add support for Ephemeral Diffie-Hellman key exchange Signed-off-by: Michael Brown --- src/include/ipxe/tls.h | 1 + src/net/tls.c | 246 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 247 insertions(+) (limited to 'src/net') diff --git a/src/include/ipxe/tls.h b/src/include/ipxe/tls.h index 80cdd12fe..6d6c82de0 100644 --- a/src/include/ipxe/tls.h +++ b/src/include/ipxe/tls.h @@ -403,6 +403,7 @@ struct tls_connection { #define TLS_RX_ALIGN 16 extern struct tls_key_exchange_algorithm tls_pubkey_exchange_algorithm; +extern struct tls_key_exchange_algorithm tls_dhe_exchange_algorithm; extern int add_tls ( struct interface *xfer, const char *name, struct x509_root *root, struct private_key *key ); diff --git a/src/net/tls.c b/src/net/tls.c index b209e0d80..4aa4d9e29 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -49,6 +49,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include #include @@ -109,6 +110,10 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define EINFO_EINVAL_TICKET \ __einfo_uniqify ( EINFO_EINVAL, 0x0e, \ "Invalid New Session Ticket record") +#define EINVAL_KEY_EXCHANGE __einfo_error ( EINFO_EINVAL_KEY_EXCHANGE ) +#define EINFO_EINVAL_KEY_EXCHANGE \ + __einfo_uniqify ( EINFO_EINVAL, 0x0f, \ + "Invalid Server Key Exchange record" ) #define EIO_ALERT __einfo_error ( EINFO_EIO_ALERT ) #define EINFO_EIO_ALERT \ __einfo_uniqify ( EINFO_EIO, 0x01, \ @@ -177,6 +182,10 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define EINFO_EPERM_RENEG_VERIFY \ __einfo_uniqify ( EINFO_EPERM, 0x05, \ "Secure renegotiation verification failed" ) +#define EPERM_KEY_EXCHANGE __einfo_error ( EINFO_EPERM_KEY_EXCHANGE ) +#define EINFO_EPERM_KEY_EXCHANGE \ + __einfo_uniqify ( EINFO_EPERM, 0x06, \ + "ServerKeyExchange verification failed" ) #define EPROTO_VERSION __einfo_error ( EINFO_EPROTO_VERSION ) #define EINFO_EPROTO_VERSION \ __einfo_uniqify ( EINFO_EPROTO, 0x01, \ @@ -915,6 +924,44 @@ tls_signature_hash_algorithm ( struct pubkey_algorithm *pubkey, return NULL; } +/** + * Find TLS signature algorithm + * + * @v code Signature and hash algorithm identifier + * @ret pubkey Public key algorithm, or NULL + */ +static struct pubkey_algorithm * +tls_signature_hash_pubkey ( struct tls_signature_hash_id code ) { + struct tls_signature_hash_algorithm *sig_hash; + + /* Identify signature and hash algorithm */ + for_each_table_entry ( sig_hash, TLS_SIG_HASH_ALGORITHMS ) { + if ( sig_hash->code.signature == code.signature ) + return sig_hash->pubkey; + } + + return NULL; +} + +/** + * Find TLS hash algorithm + * + * @v code Signature and hash algorithm identifier + * @ret digest Digest algorithm, or NULL + */ +static struct digest_algorithm * +tls_signature_hash_digest ( struct tls_signature_hash_id code ) { + struct tls_signature_hash_algorithm *sig_hash; + + /* Identify signature and hash algorithm */ + for_each_table_entry ( sig_hash, TLS_SIG_HASH_ALGORITHMS ) { + if ( sig_hash->code.hash == code.hash ) + return sig_hash->digest; + } + + return NULL; +} + /****************************************************************************** * * Handshake verification @@ -1277,6 +1324,205 @@ struct tls_key_exchange_algorithm tls_pubkey_exchange_algorithm = { .exchange = tls_send_client_key_exchange_pubkey, }; +/** + * Transmit Client Key Exchange record using DHE key exchange + * + * @v tls TLS connection + * @ret rc Return status code + */ +static int tls_send_client_key_exchange_dhe ( struct tls_connection *tls ) { + struct tls_cipherspec *cipherspec = &tls->tx_cipherspec_pending; + struct pubkey_algorithm *pubkey; + struct digest_algorithm *digest; + int use_sig_hash = tls_version ( tls, TLS_VERSION_TLS_1_2 ); + uint8_t private[ sizeof ( tls->client_random.random ) ]; + const struct { + uint16_t len; + uint8_t data[0]; + } __attribute__ (( packed )) *dh_val[3]; + const struct { + struct tls_signature_hash_id sig_hash[use_sig_hash]; + uint16_t signature_len; + uint8_t signature[0]; + } __attribute__ (( packed )) *sig; + const void *data; + size_t remaining; + size_t frag_len; + unsigned int i; + int rc; + + /* Parse ServerKeyExchange */ + data = tls->server_key; + remaining = tls->server_key_len; + for ( i = 0 ; i < ( sizeof ( dh_val ) / sizeof ( dh_val[0] ) ) ; i++ ){ + dh_val[i] = data; + if ( ( sizeof ( *dh_val[i] ) > remaining ) || + ( ntohs ( dh_val[i]->len ) > ( remaining - + sizeof ( *dh_val[i] ) ) )){ + DBGC ( tls, "TLS %p received underlength " + "ServerKeyExchange\n", tls ); + DBGC_HDA ( tls, 0, tls->server_key, + tls->server_key_len ); + rc = -EINVAL_KEY_EXCHANGE; + goto err_header; + } + frag_len = ( sizeof ( *dh_val[i] ) + ntohs ( dh_val[i]->len )); + data += frag_len; + remaining -= frag_len; + } + sig = data; + if ( ( sizeof ( *sig ) > remaining ) || + ( ntohs ( sig->signature_len ) > ( remaining - + sizeof ( *sig ) ) ) ) { + DBGC ( tls, "TLS %p received underlength ServerKeyExchange\n", + tls ); + DBGC_HDA ( tls, 0, tls->server_key, tls->server_key_len ); + rc = -EINVAL_KEY_EXCHANGE; + goto err_header; + } + + /* Identify signature and hash algorithm */ + if ( use_sig_hash ) { + pubkey = tls_signature_hash_pubkey ( sig->sig_hash[0] ); + digest = tls_signature_hash_digest ( sig->sig_hash[0] ); + if ( ( ! pubkey ) || ( ! digest ) ) { + DBGC ( tls, "TLS %p ServerKeyExchange unsupported " + "signature and hash algorithm\n", tls ); + rc = -ENOTSUP_SIG_HASH; + goto err_sig_hash; + } + if ( pubkey != cipherspec->suite->pubkey ) { + DBGC ( tls, "TLS %p ServerKeyExchange incorrect " + "signature algorithm %s (expected %s)\n", tls, + pubkey->name, cipherspec->suite->pubkey->name ); + rc = -EPERM_KEY_EXCHANGE; + goto err_sig_hash; + } + } else { + pubkey = cipherspec->suite->pubkey; + digest = &md5_sha1_algorithm; + } + + /* Verify signature */ + { + const void *signature = sig->signature; + size_t signature_len = ntohs ( sig->signature_len ); + uint8_t ctx[digest->ctxsize]; + uint8_t hash[digest->digestsize]; + + /* Calculate digest */ + digest_init ( digest, ctx ); + digest_update ( digest, ctx, &tls->client_random, + sizeof ( tls->client_random ) ); + digest_update ( digest, ctx, tls->server_random, + sizeof ( tls->server_random ) ); + digest_update ( digest, ctx, tls->server_key, + ( tls->server_key_len - remaining ) ); + digest_final ( digest, ctx, hash ); + + /* Verify signature */ + if ( ( rc = pubkey_verify ( pubkey, cipherspec->pubkey_ctx, + digest, hash, signature, + signature_len ) ) != 0 ) { + DBGC ( tls, "TLS %p ServerKeyExchange failed " + "verification\n", tls ); + DBGC_HDA ( tls, 0, tls->server_key, + tls->server_key_len ); + rc = -EPERM_KEY_EXCHANGE; + goto err_verify; + } + } + + /* Generate Diffie-Hellman private key */ + if ( ( rc = tls_generate_random ( tls, private, + sizeof ( private ) ) ) != 0 ) { + goto err_random; + } + + /* Construct pre-master secret and ClientKeyExchange record */ + { + typeof ( dh_val[0] ) dh_p = dh_val[0]; + typeof ( dh_val[1] ) dh_g = dh_val[1]; + typeof ( dh_val[2] ) dh_ys = dh_val[2]; + size_t len = ntohs ( dh_p->len ); + struct { + uint32_t type_length; + uint16_t dh_xs_len; + uint8_t dh_xs[len]; + } __attribute__ (( packed )) *key_xchg; + struct { + uint8_t pre_master_secret[len]; + typeof ( *key_xchg ) key_xchg; + } *dynamic; + uint8_t *pre_master_secret; + + /* Allocate space */ + dynamic = malloc ( sizeof ( *dynamic ) ); + if ( ! dynamic ) { + rc = -ENOMEM; + goto err_alloc; + } + pre_master_secret = dynamic->pre_master_secret; + key_xchg = &dynamic->key_xchg; + key_xchg->type_length = + ( cpu_to_le32 ( TLS_CLIENT_KEY_EXCHANGE ) | + htonl ( sizeof ( *key_xchg ) - + sizeof ( key_xchg->type_length ) ) ); + key_xchg->dh_xs_len = htons ( len ); + + /* Calculate pre-master secret and client public value */ + if ( ( rc = dhe_key ( dh_p->data, len, + dh_g->data, ntohs ( dh_g->len ), + dh_ys->data, ntohs ( dh_ys->len ), + private, sizeof ( private ), + key_xchg->dh_xs, + pre_master_secret ) ) != 0 ) { + DBGC ( tls, "TLS %p could not calculate DHE key: %s\n", + tls, strerror ( rc ) ); + goto err_dhe_key; + } + + /* Strip leading zeroes from pre-master secret */ + while ( len && ( ! *pre_master_secret ) ) { + pre_master_secret++; + len--; + } + + /* Generate master secret */ + tls_generate_master_secret ( tls, pre_master_secret, len ); + + /* Generate keys */ + if ( ( rc = tls_generate_keys ( tls ) ) != 0 ) { + DBGC ( tls, "TLS %p could not generate keys: %s\n", + tls, strerror ( rc ) ); + goto err_generate_keys; + } + + /* Transmit Client Key Exchange record */ + if ( ( rc = tls_send_handshake ( tls, key_xchg, + sizeof ( *key_xchg ) ) ) !=0){ + goto err_send_handshake; + } + + err_send_handshake: + err_generate_keys: + err_dhe_key: + free ( dynamic ); + } + err_alloc: + err_random: + err_verify: + err_sig_hash: + err_header: + return rc; +} + +/** Ephemeral Diffie-Hellman key exchange algorithm */ +struct tls_key_exchange_algorithm tls_dhe_exchange_algorithm = { + .name = "dhe", + .exchange = tls_send_client_key_exchange_dhe, +}; + /** * Transmit Client Key Exchange record * -- cgit v1.2.3-55-g7522