From 2c78242732765be200f81a84cc95037ba2924e42 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 11 Oct 2022 13:57:05 +0100 Subject: [tls] Add support for DHE variants of the existing cipher suites Signed-off-by: Michael Brown --- src/crypto/mishmash/rsa_aes_cbc_sha1.c | 28 ++++++++++++++++++++++++++-- src/crypto/mishmash/rsa_aes_cbc_sha256.c | 28 ++++++++++++++++++++++++++-- 2 files changed, 52 insertions(+), 4 deletions(-) (limited to 'src/crypto') diff --git a/src/crypto/mishmash/rsa_aes_cbc_sha1.c b/src/crypto/mishmash/rsa_aes_cbc_sha1.c index 04b4ce2a7..b054a01c7 100644 --- a/src/crypto/mishmash/rsa_aes_cbc_sha1.c +++ b/src/crypto/mishmash/rsa_aes_cbc_sha1.c @@ -29,8 +29,31 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +/** TLS_DHE_RSA_WITH_AES_128_CBC_SHA cipher suite */ +struct tls_cipher_suite +tls_dhe_rsa_with_aes_128_cbc_sha __tls_cipher_suite ( 03 ) = { + .code = htons ( TLS_DHE_RSA_WITH_AES_128_CBC_SHA ), + .key_len = ( 128 / 8 ), + .exchange = &tls_dhe_exchange_algorithm, + .pubkey = &rsa_algorithm, + .cipher = &aes_cbc_algorithm, + .digest = &sha1_algorithm, +}; + +/** TLS_DHE_RSA_WITH_AES_256_CBC_SHA cipher suite */ +struct tls_cipher_suite +tls_dhe_rsa_with_aes_256_cbc_sha __tls_cipher_suite ( 04 ) = { + .code = htons ( TLS_DHE_RSA_WITH_AES_256_CBC_SHA ), + .key_len = ( 256 / 8 ), + .exchange = &tls_dhe_exchange_algorithm, + .pubkey = &rsa_algorithm, + .cipher = &aes_cbc_algorithm, + .digest = &sha1_algorithm, +}; + /** TLS_RSA_WITH_AES_128_CBC_SHA cipher suite */ -struct tls_cipher_suite tls_rsa_with_aes_128_cbc_sha __tls_cipher_suite (03) = { +struct tls_cipher_suite +tls_rsa_with_aes_128_cbc_sha __tls_cipher_suite ( 13 ) = { .code = htons ( TLS_RSA_WITH_AES_128_CBC_SHA ), .key_len = ( 128 / 8 ), .exchange = &tls_pubkey_exchange_algorithm, @@ -40,7 +63,8 @@ struct tls_cipher_suite tls_rsa_with_aes_128_cbc_sha __tls_cipher_suite (03) = { }; /** TLS_RSA_WITH_AES_256_CBC_SHA cipher suite */ -struct tls_cipher_suite tls_rsa_with_aes_256_cbc_sha __tls_cipher_suite (04) = { +struct tls_cipher_suite +tls_rsa_with_aes_256_cbc_sha __tls_cipher_suite ( 14 ) = { .code = htons ( TLS_RSA_WITH_AES_256_CBC_SHA ), .key_len = ( 256 / 8 ), .exchange = &tls_pubkey_exchange_algorithm, diff --git a/src/crypto/mishmash/rsa_aes_cbc_sha256.c b/src/crypto/mishmash/rsa_aes_cbc_sha256.c index 1021f76f4..b003523d5 100644 --- a/src/crypto/mishmash/rsa_aes_cbc_sha256.c +++ b/src/crypto/mishmash/rsa_aes_cbc_sha256.c @@ -29,8 +29,31 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +/** TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 cipher suite */ +struct tls_cipher_suite +tls_dhe_rsa_with_aes_128_cbc_sha256 __tls_cipher_suite ( 01 ) = { + .code = htons ( TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 ), + .key_len = ( 128 / 8 ), + .exchange = &tls_dhe_exchange_algorithm, + .pubkey = &rsa_algorithm, + .cipher = &aes_cbc_algorithm, + .digest = &sha256_algorithm, +}; + +/** TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 cipher suite */ +struct tls_cipher_suite +tls_dhe_rsa_with_aes_256_cbc_sha256 __tls_cipher_suite ( 02 ) = { + .code = htons ( TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 ), + .key_len = ( 256 / 8 ), + .exchange = &tls_dhe_exchange_algorithm, + .pubkey = &rsa_algorithm, + .cipher = &aes_cbc_algorithm, + .digest = &sha256_algorithm, +}; + /** TLS_RSA_WITH_AES_128_CBC_SHA256 cipher suite */ -struct tls_cipher_suite tls_rsa_with_aes_128_cbc_sha256 __tls_cipher_suite(01)={ +struct tls_cipher_suite +tls_rsa_with_aes_128_cbc_sha256 __tls_cipher_suite ( 11 ) = { .code = htons ( TLS_RSA_WITH_AES_128_CBC_SHA256 ), .key_len = ( 128 / 8 ), .exchange = &tls_pubkey_exchange_algorithm, @@ -40,7 +63,8 @@ struct tls_cipher_suite tls_rsa_with_aes_128_cbc_sha256 __tls_cipher_suite(01)={ }; /** TLS_RSA_WITH_AES_256_CBC_SHA256 cipher suite */ -struct tls_cipher_suite tls_rsa_with_aes_256_cbc_sha256 __tls_cipher_suite(02)={ +struct tls_cipher_suite +tls_rsa_with_aes_256_cbc_sha256 __tls_cipher_suite ( 12 ) = { .code = htons ( TLS_RSA_WITH_AES_256_CBC_SHA256 ), .key_len = ( 256 / 8 ), .exchange = &tls_pubkey_exchange_algorithm, -- cgit v1.2.3-55-g7522 From 52f72d298abd81a6102ddddf2fff4918e4b077ce Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 25 Oct 2022 12:59:06 +0100 Subject: [crypto] Expose null crypto algorithm methods for reuse Signed-off-by: Michael Brown --- src/crypto/aes.c | 12 +-------- src/crypto/arc4.c | 8 +----- src/crypto/crypto_null.c | 62 ++++++++++++++++++++++------------------------- src/include/ipxe/crypto.h | 23 ++++++++++++++++++ 4 files changed, 54 insertions(+), 51 deletions(-) (limited to 'src/crypto') diff --git a/src/crypto/aes.c b/src/crypto/aes.c index b9e206bfb..d7393285f 100644 --- a/src/crypto/aes.c +++ b/src/crypto/aes.c @@ -778,23 +778,13 @@ static int aes_setkey ( void *ctx, const void *key, size_t keylen ) { return 0; } -/** - * Set initialisation vector - * - * @v ctx Context - * @v iv Initialisation vector - */ -static void aes_setiv ( void *ctx __unused, const void *iv __unused ) { - /* Nothing to do */ -} - /** Basic AES algorithm */ struct cipher_algorithm aes_algorithm = { .name = "aes", .ctxsize = sizeof ( struct aes_context ), .blocksize = AES_BLOCKSIZE, .setkey = aes_setkey, - .setiv = aes_setiv, + .setiv = cipher_null_setiv, .encrypt = aes_encrypt, .decrypt = aes_decrypt, }; diff --git a/src/crypto/arc4.c b/src/crypto/arc4.c index 91a732019..0dba2fc59 100644 --- a/src/crypto/arc4.c +++ b/src/crypto/arc4.c @@ -96,12 +96,6 @@ static void arc4_xor ( void *ctxv, const void *srcv, void *dstv, ctx->j = j; } -static void arc4_setiv ( void *ctx __unused, const void *iv __unused ) -{ - /* ARC4 does not use a fixed-length IV */ -} - - /** * Perform ARC4 encryption or decryption, skipping initial keystream bytes * @@ -126,7 +120,7 @@ struct cipher_algorithm arc4_algorithm = { .ctxsize = ARC4_CTX_SIZE, .blocksize = 1, .setkey = arc4_setkey, - .setiv = arc4_setiv, + .setiv = cipher_null_setiv, .encrypt = arc4_xor, .decrypt = arc4_xor, }; diff --git a/src/crypto/crypto_null.c b/src/crypto/crypto_null.c index 15a1c538b..91077177c 100644 --- a/src/crypto/crypto_null.c +++ b/src/crypto/crypto_null.c @@ -32,16 +32,16 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include -static void digest_null_init ( void *ctx __unused ) { +void digest_null_init ( void *ctx __unused ) { /* Do nothing */ } -static void digest_null_update ( void *ctx __unused, const void *src __unused, - size_t len __unused ) { +void digest_null_update ( void *ctx __unused, const void *src __unused, + size_t len __unused ) { /* Do nothing */ } -static void digest_null_final ( void *ctx __unused, void *out __unused ) { +void digest_null_final ( void *ctx __unused, void *out __unused ) { /* Do nothing */ } @@ -55,24 +55,23 @@ struct digest_algorithm digest_null = { .final = digest_null_final, }; -static int cipher_null_setkey ( void *ctx __unused, const void *key __unused, - size_t keylen __unused ) { +int cipher_null_setkey ( void *ctx __unused, const void *key __unused, + size_t keylen __unused ) { /* Do nothing */ return 0; } -static void cipher_null_setiv ( void *ctx __unused, - const void *iv __unused ) { +void cipher_null_setiv ( void *ctx __unused, const void *iv __unused ) { /* Do nothing */ } -static void cipher_null_encrypt ( void *ctx __unused, const void *src, - void *dst, size_t len ) { +void cipher_null_encrypt ( void *ctx __unused, const void *src, void *dst, + size_t len ) { memcpy ( dst, src, len ); } -static void cipher_null_decrypt ( void *ctx __unused, const void *src, - void *dst, size_t len ) { +void cipher_null_decrypt ( void *ctx __unused, const void *src, void *dst, + size_t len ) { memcpy ( dst, src, len ); } @@ -86,45 +85,42 @@ struct cipher_algorithm cipher_null = { .decrypt = cipher_null_decrypt, }; -static int pubkey_null_init ( void *ctx __unused, const void *key __unused, - size_t key_len __unused ) { +int pubkey_null_init ( void *ctx __unused, const void *key __unused, + size_t key_len __unused ) { return 0; } -static size_t pubkey_null_max_len ( void *ctx __unused ) { +size_t pubkey_null_max_len ( void *ctx __unused ) { return 0; } -static int pubkey_null_encrypt ( void *ctx __unused, - const void *plaintext __unused, - size_t plaintext_len __unused, - void *ciphertext __unused ) { +int pubkey_null_encrypt ( void *ctx __unused, const void *plaintext __unused, + size_t plaintext_len __unused, + void *ciphertext __unused ) { return 0; } -static int pubkey_null_decrypt ( void *ctx __unused, - const void *ciphertext __unused, - size_t ciphertext_len __unused, - void *plaintext __unused ) { +int pubkey_null_decrypt ( void *ctx __unused, const void *ciphertext __unused, + size_t ciphertext_len __unused, + void *plaintext __unused ) { return 0; } -static int pubkey_null_sign ( void *ctx __unused, - struct digest_algorithm *digest __unused, - const void *value __unused, - void *signature __unused ) { +int pubkey_null_sign ( void *ctx __unused, + struct digest_algorithm *digest __unused, + const void *value __unused, void *signature __unused ) { return 0; } -static int pubkey_null_verify ( void *ctx __unused, - struct digest_algorithm *digest __unused, - const void *value __unused, - const void *signature __unused , - size_t signature_len __unused ) { +int pubkey_null_verify ( void *ctx __unused, + struct digest_algorithm *digest __unused, + const void *value __unused, + const void *signature __unused , + size_t signature_len __unused ) { return 0; } -static void pubkey_null_final ( void *ctx __unused ) { +void pubkey_null_final ( void *ctx __unused ) { /* Do nothing */ } diff --git a/src/include/ipxe/crypto.h b/src/include/ipxe/crypto.h index fc0d8b22b..34ab38930 100644 --- a/src/include/ipxe/crypto.h +++ b/src/include/ipxe/crypto.h @@ -263,6 +263,29 @@ static inline int pubkey_match ( struct pubkey_algorithm *pubkey, public_key_len ); } +extern void digest_null_init ( void *ctx ); +extern void digest_null_update ( void *ctx, const void *src, size_t len ); +extern void digest_null_final ( void *ctx, void *out ); + +extern int cipher_null_setkey ( void *ctx, const void *key, size_t keylen ); +extern void cipher_null_setiv ( void *ctx, const void *iv ); +extern void cipher_null_encrypt ( void *ctx, const void *src, void *dst, + size_t len ); +extern void cipher_null_decrypt ( void *ctx, const void *src, void *dst, + size_t len ); + +extern int pubkey_null_init ( void *ctx, const void *key, size_t key_len ); +extern size_t pubkey_null_max_len ( void *ctx ); +extern int pubkey_null_encrypt ( void *ctx, const void *plaintext, + size_t plaintext_len, void *ciphertext ); +extern int pubkey_null_decrypt ( void *ctx, const void *ciphertext, + size_t ciphertext_len, void *plaintext ); +extern int pubkey_null_sign ( void *ctx, struct digest_algorithm *digest, + const void *value, void *signature ); +extern int pubkey_null_verify ( void *ctx, struct digest_algorithm *digest, + const void *value, const void *signature , + size_t signature_len ); + extern struct digest_algorithm digest_null; extern struct cipher_algorithm cipher_null; extern struct pubkey_algorithm pubkey_null; -- cgit v1.2.3-55-g7522 From 8e478e648fb68ac6f07e4e5cd80a5c1fefcb1cf5 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 24 Oct 2022 16:52:24 +0100 Subject: [crypto] Allow initialisation vector length to vary from cipher blocksize Signed-off-by: Michael Brown --- src/crypto/crypto_null.c | 3 ++- src/include/ipxe/cbc.h | 10 +++++++--- src/include/ipxe/crypto.h | 9 +++++---- src/include/ipxe/ecb.h | 5 +++-- src/net/peerblk.c | 3 ++- src/net/tls.c | 4 ++-- src/tests/cipher_test.c | 6 +++--- 7 files changed, 24 insertions(+), 16 deletions(-) (limited to 'src/crypto') diff --git a/src/crypto/crypto_null.c b/src/crypto/crypto_null.c index 91077177c..ef6041b5b 100644 --- a/src/crypto/crypto_null.c +++ b/src/crypto/crypto_null.c @@ -61,7 +61,8 @@ int cipher_null_setkey ( void *ctx __unused, const void *key __unused, return 0; } -void cipher_null_setiv ( void *ctx __unused, const void *iv __unused ) { +void cipher_null_setiv ( void *ctx __unused, const void *iv __unused, + size_t ivlen __unused ) { /* Do nothing */ } diff --git a/src/include/ipxe/cbc.h b/src/include/ipxe/cbc.h index 18a94e144..5c8740365 100644 --- a/src/include/ipxe/cbc.h +++ b/src/include/ipxe/cbc.h @@ -33,12 +33,15 @@ static inline int cbc_setkey ( void *ctx, const void *key, size_t keylen, * * @v ctx Context * @v iv Initialisation vector + * @v ivlen Initialisation vector length * @v raw_cipher Underlying cipher algorithm * @v cbc_ctx CBC context */ -static inline void cbc_setiv ( void *ctx __unused, const void *iv, +static inline void cbc_setiv ( void *ctx __unused, + const void *iv, size_t ivlen, struct cipher_algorithm *raw_cipher, void *cbc_ctx ) { + assert ( ivlen == raw_cipher->blocksize ); memcpy ( cbc_ctx, iv, raw_cipher->blocksize ); } @@ -70,9 +73,10 @@ static int _cbc_name ## _setkey ( void *ctx, const void *key, \ return cbc_setkey ( &_cbc_name ## _ctx->raw_ctx, key, keylen, \ &_raw_cipher, &_cbc_name ## _ctx->cbc_ctx );\ } \ -static void _cbc_name ## _setiv ( void *ctx, const void *iv ) { \ +static void _cbc_name ## _setiv ( void *ctx, const void *iv, \ + size_t ivlen ) { \ struct _cbc_name ## _context * _cbc_name ## _ctx = ctx; \ - cbc_setiv ( &_cbc_name ## _ctx->raw_ctx, iv, \ + cbc_setiv ( &_cbc_name ## _ctx->raw_ctx, iv, ivlen, \ &_raw_cipher, &aes_cbc_ctx->cbc_ctx ); \ } \ static void _cbc_name ## _encrypt ( void *ctx, const void *src, \ diff --git a/src/include/ipxe/crypto.h b/src/include/ipxe/crypto.h index 34ab38930..931be0502 100644 --- a/src/include/ipxe/crypto.h +++ b/src/include/ipxe/crypto.h @@ -64,8 +64,9 @@ struct cipher_algorithm { * * @v ctx Context * @v iv Initialisation vector + * @v ivlen Initialisation vector length */ - void ( * setiv ) ( void *ctx, const void *iv ); + void ( * setiv ) ( void *ctx, const void *iv, size_t ivlen ); /** Encrypt data * * @v ctx Context @@ -190,8 +191,8 @@ static inline int cipher_setkey ( struct cipher_algorithm *cipher, } static inline void cipher_setiv ( struct cipher_algorithm *cipher, - void *ctx, const void *iv ) { - cipher->setiv ( ctx, iv ); + void *ctx, const void *iv, size_t ivlen ) { + cipher->setiv ( ctx, iv, ivlen ); } static inline void cipher_encrypt ( struct cipher_algorithm *cipher, @@ -268,7 +269,7 @@ extern void digest_null_update ( void *ctx, const void *src, size_t len ); extern void digest_null_final ( void *ctx, void *out ); extern int cipher_null_setkey ( void *ctx, const void *key, size_t keylen ); -extern void cipher_null_setiv ( void *ctx, const void *iv ); +extern void cipher_null_setiv ( void *ctx, const void *iv, size_t ivlen ); extern void cipher_null_encrypt ( void *ctx, const void *src, void *dst, size_t len ); extern void cipher_null_decrypt ( void *ctx, const void *src, void *dst, diff --git a/src/include/ipxe/ecb.h b/src/include/ipxe/ecb.h index 4e6aa3c81..6c40c6126 100644 --- a/src/include/ipxe/ecb.h +++ b/src/include/ipxe/ecb.h @@ -31,8 +31,9 @@ static int _ecb_name ## _setkey ( void *ctx, const void *key, \ size_t keylen ) { \ return cipher_setkey ( &_raw_cipher, ctx, key, keylen ); \ } \ -static void _ecb_name ## _setiv ( void *ctx, const void *iv ) { \ - cipher_setiv ( &_raw_cipher, ctx, iv ); \ +static void _ecb_name ## _setiv ( void *ctx, const void *iv, \ + size_t ivlen ) { \ + cipher_setiv ( &_raw_cipher, ctx, iv, ivlen ); \ } \ static void _ecb_name ## _encrypt ( void *ctx, const void *src, \ void *dst, size_t len ) { \ diff --git a/src/net/peerblk.c b/src/net/peerblk.c index f8994f42c..bbd5f16ed 100644 --- a/src/net/peerblk.c +++ b/src/net/peerblk.c @@ -1033,7 +1033,8 @@ static int peerblk_parse_iv ( struct peerdist_block *peerblk, size_t buf_len, } /* Set initialisation vector */ - cipher_setiv ( peerblk->cipher, peerblk->cipherctx, msg->msg.iv.data ); + cipher_setiv ( peerblk->cipher, peerblk->cipherctx, msg->msg.iv.data, + blksize ); return 0; } diff --git a/src/net/tls.c b/src/net/tls.c index 4aa4d9e29..3545f12db 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -717,14 +717,14 @@ static int tls_generate_keys ( struct tls_connection *tls ) { /* TX initialisation vector */ cipher_setiv ( tx_cipherspec->suite->cipher, - tx_cipherspec->cipher_ctx, key ); + tx_cipherspec->cipher_ctx, key, iv_size ); DBGC ( tls, "TLS %p TX IV:\n", tls ); DBGC_HD ( tls, key, iv_size ); key += iv_size; /* RX initialisation vector */ cipher_setiv ( rx_cipherspec->suite->cipher, - rx_cipherspec->cipher_ctx, key ); + rx_cipherspec->cipher_ctx, key, iv_size ); DBGC ( tls, "TLS %p RX IV:\n", tls ); DBGC_HD ( tls, key, iv_size ); key += iv_size; diff --git a/src/tests/cipher_test.c b/src/tests/cipher_test.c index 800d6c138..5361502ff 100644 --- a/src/tests/cipher_test.c +++ b/src/tests/cipher_test.c @@ -61,7 +61,7 @@ void cipher_encrypt_okx ( struct cipher_test *test, const char *file, /* Initialise cipher */ okx ( cipher_setkey ( cipher, ctx, test->key, test->key_len ) == 0, file, line ); - cipher_setiv ( cipher, ctx, test->iv ); + cipher_setiv ( cipher, ctx, test->iv, test->iv_len ); /* Perform encryption */ cipher_encrypt ( cipher, ctx, test->plaintext, ciphertext, len ); @@ -87,7 +87,7 @@ void cipher_decrypt_okx ( struct cipher_test *test, const char *file, /* Initialise cipher */ okx ( cipher_setkey ( cipher, ctx, test->key, test->key_len ) == 0, file, line ); - cipher_setiv ( cipher, ctx, test->iv ); + cipher_setiv ( cipher, ctx, test->iv, test->iv_len ); /* Perform encryption */ cipher_decrypt ( cipher, ctx, test->ciphertext, plaintext, len ); @@ -143,7 +143,7 @@ cipher_cost ( struct cipher_algorithm *cipher, size_t key_len, /* Initialise cipher */ rc = cipher_setkey ( cipher, ctx, key, key_len ); assert ( rc == 0 ); - cipher_setiv ( cipher, ctx, iv ); + cipher_setiv ( cipher, ctx, iv, sizeof ( iv ) ); /* Profile cipher operation */ memset ( &profiler, 0, sizeof ( profiler ) ); -- cgit v1.2.3-55-g7522 From da81214cec87201dc18c0ce71224367e13a6edfb Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 24 Oct 2022 19:20:41 +0100 Subject: [crypto] Add concept of authentication tag to cipher algorithms Some ciphers (such as GCM) support the concept of a tag that can be used to authenticate the encrypted data. Add a cipher method for generating an authentication tag. Signed-off-by: Michael Brown --- src/crypto/aes.c | 2 ++ src/crypto/arc4.c | 2 ++ src/crypto/crypto_null.c | 6 ++++++ src/include/ipxe/cbc.h | 2 ++ src/include/ipxe/crypto.h | 18 ++++++++++++++++++ src/include/ipxe/ecb.h | 2 ++ src/tests/aes_test.c | 12 ++++++------ src/tests/cipher_test.c | 14 ++++++++++++++ src/tests/cipher_test.h | 13 ++++++++++++- 9 files changed, 64 insertions(+), 7 deletions(-) (limited to 'src/crypto') diff --git a/src/crypto/aes.c b/src/crypto/aes.c index d7393285f..4a7668b6b 100644 --- a/src/crypto/aes.c +++ b/src/crypto/aes.c @@ -783,10 +783,12 @@ struct cipher_algorithm aes_algorithm = { .name = "aes", .ctxsize = sizeof ( struct aes_context ), .blocksize = AES_BLOCKSIZE, + .authsize = 0, .setkey = aes_setkey, .setiv = cipher_null_setiv, .encrypt = aes_encrypt, .decrypt = aes_decrypt, + .auth = cipher_null_auth, }; /* AES in Electronic Codebook mode */ diff --git a/src/crypto/arc4.c b/src/crypto/arc4.c index 0dba2fc59..4d98abead 100644 --- a/src/crypto/arc4.c +++ b/src/crypto/arc4.c @@ -119,8 +119,10 @@ struct cipher_algorithm arc4_algorithm = { .name = "ARC4", .ctxsize = ARC4_CTX_SIZE, .blocksize = 1, + .authsize = 0, .setkey = arc4_setkey, .setiv = cipher_null_setiv, .encrypt = arc4_xor, .decrypt = arc4_xor, + .auth = cipher_null_auth, }; diff --git a/src/crypto/crypto_null.c b/src/crypto/crypto_null.c index ef6041b5b..26cfbfc4e 100644 --- a/src/crypto/crypto_null.c +++ b/src/crypto/crypto_null.c @@ -76,14 +76,20 @@ void cipher_null_decrypt ( void *ctx __unused, const void *src, void *dst, memcpy ( dst, src, len ); } +void cipher_null_auth ( void *ctx __unused, void *auth __unused ) { + /* Do nothing */ +} + struct cipher_algorithm cipher_null = { .name = "null", .ctxsize = 0, .blocksize = 1, + .authsize = 0, .setkey = cipher_null_setkey, .setiv = cipher_null_setiv, .encrypt = cipher_null_encrypt, .decrypt = cipher_null_decrypt, + .auth = cipher_null_auth, }; int pubkey_null_init ( void *ctx __unused, const void *key __unused, diff --git a/src/include/ipxe/cbc.h b/src/include/ipxe/cbc.h index 5c8740365..eead045ed 100644 --- a/src/include/ipxe/cbc.h +++ b/src/include/ipxe/cbc.h @@ -95,10 +95,12 @@ struct cipher_algorithm _cbc_cipher = { \ .name = #_cbc_name, \ .ctxsize = sizeof ( struct _cbc_name ## _context ), \ .blocksize = _blocksize, \ + .authsize = 0, \ .setkey = _cbc_name ## _setkey, \ .setiv = _cbc_name ## _setiv, \ .encrypt = _cbc_name ## _encrypt, \ .decrypt = _cbc_name ## _decrypt, \ + .auth = cipher_null_auth, \ }; #endif /* _IPXE_CBC_H */ diff --git a/src/include/ipxe/crypto.h b/src/include/ipxe/crypto.h index d41448024..e807aeb52 100644 --- a/src/include/ipxe/crypto.h +++ b/src/include/ipxe/crypto.h @@ -52,6 +52,8 @@ struct cipher_algorithm { size_t ctxsize; /** Block size */ size_t blocksize; + /** Authentication tag size */ + size_t authsize; /** Set key * * @v ctx Context @@ -89,6 +91,12 @@ struct cipher_algorithm { */ void ( * decrypt ) ( void *ctx, const void *src, void *dst, size_t len ); + /** Generate authentication tag + * + * @v ctx Context + * @v auth Authentication tag + */ + void ( * auth ) ( void *ctx, void *auth ); }; /** A public key algorithm */ @@ -215,10 +223,19 @@ static inline void cipher_decrypt ( struct cipher_algorithm *cipher, cipher_decrypt ( (cipher), (ctx), (src), (dst), (len) ); \ } while ( 0 ) +static inline void cipher_auth ( struct cipher_algorithm *cipher, void *ctx, + void *auth ) { + cipher->auth ( ctx, auth ); +} + static inline int is_stream_cipher ( struct cipher_algorithm *cipher ) { return ( cipher->blocksize == 1 ); } +static inline int is_auth_cipher ( struct cipher_algorithm *cipher ) { + return cipher->authsize; +} + static inline int pubkey_init ( struct pubkey_algorithm *pubkey, void *ctx, const void *key, size_t key_len ) { return pubkey->init ( ctx, key, key_len ); @@ -274,6 +291,7 @@ extern void cipher_null_encrypt ( void *ctx, const void *src, void *dst, size_t len ); extern void cipher_null_decrypt ( void *ctx, const void *src, void *dst, size_t len ); +extern void cipher_null_auth ( void *ctx, void *auth ); extern int pubkey_null_init ( void *ctx, const void *key, size_t key_len ); extern size_t pubkey_null_max_len ( void *ctx ); diff --git a/src/include/ipxe/ecb.h b/src/include/ipxe/ecb.h index 6c40c6126..1d2ebf716 100644 --- a/src/include/ipxe/ecb.h +++ b/src/include/ipxe/ecb.h @@ -47,10 +47,12 @@ struct cipher_algorithm _ecb_cipher = { \ .name = #_ecb_name, \ .ctxsize = sizeof ( _raw_context ), \ .blocksize = _blocksize, \ + .authsize = 0, \ .setkey = _ecb_name ## _setkey, \ .setiv = _ecb_name ## _setiv, \ .encrypt = _ecb_name ## _encrypt, \ .decrypt = _ecb_name ## _decrypt, \ + .auth = cipher_null_auth, \ }; #endif /* _IPXE_ECB_H */ diff --git a/src/tests/aes_test.c b/src/tests/aes_test.c index e7201fca6..be119c8d8 100644 --- a/src/tests/aes_test.c +++ b/src/tests/aes_test.c @@ -94,7 +94,7 @@ CIPHER_TEST ( aes_128_ecb, &aes_ecb_algorithm, 0x43, 0xb1, 0xcd, 0x7f, 0x59, 0x8e, 0xce, 0x23, 0x88, 0x1b, 0x00, 0xe3, 0xed, 0x03, 0x06, 0x88, 0x7b, 0x0c, 0x78, 0x5e, 0x27, 0xe8, 0xad, 0x3f, - 0x82, 0x23, 0x20, 0x71, 0x04, 0x72, 0x5d, 0xd4 ) ); + 0x82, 0x23, 0x20, 0x71, 0x04, 0x72, 0x5d, 0xd4 ), AUTH() ); /** AES-128-CBC */ CIPHER_TEST ( aes_128_cbc, &aes_cbc_algorithm, @@ -106,7 +106,7 @@ CIPHER_TEST ( aes_128_cbc, &aes_cbc_algorithm, 0x73, 0xbe, 0xd6, 0xb8, 0xe3, 0xc1, 0x74, 0x3b, 0x71, 0x16, 0xe6, 0x9e, 0x22, 0x22, 0x95, 0x16, 0x3f, 0xf1, 0xca, 0xa1, 0x68, 0x1f, 0xac, 0x09, - 0x12, 0x0e, 0xca, 0x30, 0x75, 0x86, 0xe1, 0xa7 ) ); + 0x12, 0x0e, 0xca, 0x30, 0x75, 0x86, 0xe1, 0xa7 ), AUTH() ); /** AES-192-ECB (same test as AES-192-Core) */ CIPHER_TEST ( aes_192_ecb, &aes_ecb_algorithm, @@ -118,7 +118,7 @@ CIPHER_TEST ( aes_192_ecb, &aes_ecb_algorithm, 0xef, 0x7a, 0xfd, 0x22, 0x70, 0xe2, 0xe6, 0x0a, 0xdc, 0xe0, 0xba, 0x2f, 0xac, 0xe6, 0x44, 0x4e, 0x9a, 0x4b, 0x41, 0xba, 0x73, 0x8d, 0x6c, 0x72, - 0xfb, 0x16, 0x69, 0x16, 0x03, 0xc1, 0x8e, 0x0e ) ); + 0xfb, 0x16, 0x69, 0x16, 0x03, 0xc1, 0x8e, 0x0e ), AUTH() ); /** AES-192-CBC */ CIPHER_TEST ( aes_192_cbc, &aes_cbc_algorithm, @@ -130,7 +130,7 @@ CIPHER_TEST ( aes_192_cbc, &aes_cbc_algorithm, 0x57, 0x1b, 0x24, 0x20, 0x12, 0xfb, 0x7a, 0xe0, 0x7f, 0xa9, 0xba, 0xac, 0x3d, 0xf1, 0x02, 0xe0, 0x08, 0xb0, 0xe2, 0x79, 0x88, 0x59, 0x88, 0x81, - 0xd9, 0x20, 0xa9, 0xe6, 0x4f, 0x56, 0x15, 0xcd ) ); + 0xd9, 0x20, 0xa9, 0xe6, 0x4f, 0x56, 0x15, 0xcd ), AUTH() ); /** AES-256-ECB (same test as AES-256-Core) */ CIPHER_TEST ( aes_256_ecb, &aes_ecb_algorithm, @@ -142,7 +142,7 @@ CIPHER_TEST ( aes_256_ecb, &aes_ecb_algorithm, 0xb6, 0xed, 0x21, 0xb9, 0x9c, 0xa6, 0xf4, 0xf9, 0xf1, 0x53, 0xe7, 0xb1, 0xbe, 0xaf, 0xed, 0x1d, 0x23, 0x30, 0x4b, 0x7a, 0x39, 0xf9, 0xf3, 0xff, - 0x06, 0x7d, 0x8d, 0x8f, 0x9e, 0x24, 0xec, 0xc7 ) ); + 0x06, 0x7d, 0x8d, 0x8f, 0x9e, 0x24, 0xec, 0xc7 ), AUTH() ); /** AES-256-CBC */ CIPHER_TEST ( aes_256_cbc, &aes_cbc_algorithm, @@ -154,7 +154,7 @@ CIPHER_TEST ( aes_256_cbc, &aes_cbc_algorithm, 0x39, 0xf2, 0x33, 0x69, 0xa9, 0xd9, 0xba, 0xcf, 0xa5, 0x30, 0xe2, 0x63, 0x04, 0x23, 0x14, 0x61, 0xb2, 0xeb, 0x05, 0xe2, 0xc3, 0x9b, 0xe9, 0xfc, - 0xda, 0x6c, 0x19, 0x07, 0x8c, 0x6a, 0x9d, 0x1b ) ); + 0xda, 0x6c, 0x19, 0x07, 0x8c, 0x6a, 0x9d, 0x1b ), AUTH() ); /** * Perform AES self-test diff --git a/src/tests/cipher_test.c b/src/tests/cipher_test.c index c49b4b69b..cc732c2e0 100644 --- a/src/tests/cipher_test.c +++ b/src/tests/cipher_test.c @@ -57,6 +57,7 @@ void cipher_encrypt_okx ( struct cipher_test *test, const char *file, size_t len = test->len; uint8_t ctx[cipher->ctxsize]; uint8_t ciphertext[len]; + uint8_t auth[cipher->authsize]; /* Initialise cipher */ okx ( cipher_setkey ( cipher, ctx, test->key, test->key_len ) == 0, @@ -65,6 +66,7 @@ void cipher_encrypt_okx ( struct cipher_test *test, const char *file, /* Process additional data, if applicable */ if ( test->additional_len ) { + okx ( is_auth_cipher ( cipher ), file, line ); cipher_encrypt ( cipher, ctx, test->additional, NULL, test->additional_len ); } @@ -74,6 +76,11 @@ void cipher_encrypt_okx ( struct cipher_test *test, const char *file, /* Compare against expected ciphertext */ okx ( memcmp ( ciphertext, test->ciphertext, len ) == 0, file, line ); + + /* Check authentication tag */ + okx ( cipher->authsize == test->auth_len, file, line ); + cipher_auth ( cipher, ctx, auth ); + okx ( memcmp ( auth, test->auth, test->auth_len ) == 0, file, line ); } /** @@ -89,6 +96,7 @@ void cipher_decrypt_okx ( struct cipher_test *test, const char *file, size_t len = test->len; uint8_t ctx[cipher->ctxsize]; uint8_t plaintext[len]; + uint8_t auth[cipher->authsize]; /* Initialise cipher */ okx ( cipher_setkey ( cipher, ctx, test->key, test->key_len ) == 0, @@ -97,6 +105,7 @@ void cipher_decrypt_okx ( struct cipher_test *test, const char *file, /* Process additional data, if applicable */ if ( test->additional_len ) { + okx ( is_auth_cipher ( cipher ), file, line ); cipher_decrypt ( cipher, ctx, test->additional, NULL, test->additional_len ); } @@ -106,6 +115,11 @@ void cipher_decrypt_okx ( struct cipher_test *test, const char *file, /* Compare against expected plaintext */ okx ( memcmp ( plaintext, test->plaintext, len ) == 0, file, line ); + + /* Check authentication tag */ + okx ( cipher->authsize == test->auth_len, file, line ); + cipher_auth ( cipher, ctx, auth ); + okx ( memcmp ( auth, test->auth, test->auth_len ) == 0, file, line ); } /** diff --git a/src/tests/cipher_test.h b/src/tests/cipher_test.h index 4139a7788..519d12e8d 100644 --- a/src/tests/cipher_test.h +++ b/src/tests/cipher_test.h @@ -35,6 +35,10 @@ struct cipher_test { const void *ciphertext; /** Length of text */ size_t len; + /** Authentication tag */ + const void *auth; + /** Length of authentication tag */ + size_t auth_len; }; /** Define inline key */ @@ -52,6 +56,9 @@ struct cipher_test { /** Define inline ciphertext data */ #define CIPHERTEXT(...) { __VA_ARGS__ } +/** Define inline authentication tag */ +#define AUTH(...) { __VA_ARGS__ } + /** * Define a cipher test * @@ -62,16 +69,18 @@ struct cipher_test { * @v ADDITIONAL Additional data * @v PLAINTEXT Plaintext * @v CIPHERTEXT Ciphertext + * @v AUTH Authentication tag * @ret test Cipher test */ #define CIPHER_TEST( name, CIPHER, KEY, IV, ADDITIONAL, PLAINTEXT, \ - CIPHERTEXT ) \ + CIPHERTEXT, AUTH ) \ static const uint8_t name ## _key [] = KEY; \ static const uint8_t name ## _iv [] = IV; \ static const uint8_t name ## _additional [] = ADDITIONAL; \ static const uint8_t name ## _plaintext [] = PLAINTEXT; \ static const uint8_t name ## _ciphertext \ [ sizeof ( name ## _plaintext ) ] = CIPHERTEXT; \ + static const uint8_t name ## _auth [] = AUTH; \ static struct cipher_test name = { \ .cipher = CIPHER, \ .key = name ## _key, \ @@ -83,6 +92,8 @@ struct cipher_test { .plaintext = name ## _plaintext, \ .ciphertext = name ## _ciphertext, \ .len = sizeof ( name ## _plaintext ), \ + .auth = name ## _auth, \ + .auth_len = sizeof ( name ## _auth ), \ } extern void cipher_encrypt_okx ( struct cipher_test *test, const char *file, -- cgit v1.2.3-55-g7522 From 8fce26730c4df7a9792bb144c75c2c5b998c91af Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 24 Oct 2022 18:52:21 +0100 Subject: [crypto] Add block cipher Galois/Counter mode of operation Signed-off-by: Michael Brown --- src/crypto/aes.c | 5 + src/crypto/gcm.c | 531 ++++++++++++++++++++++++++++++++++++++++++++++ src/include/ipxe/aes.h | 1 + src/include/ipxe/crypto.h | 1 + src/include/ipxe/gcm.h | 132 ++++++++++++ src/tests/gcm_test.c | 401 ++++++++++++++++++++++++++++++++++ src/tests/tests.c | 1 + 7 files changed, 1072 insertions(+) create mode 100644 src/crypto/gcm.c create mode 100644 src/include/ipxe/gcm.h create mode 100644 src/tests/gcm_test.c (limited to 'src/crypto') diff --git a/src/crypto/aes.c b/src/crypto/aes.c index 4a7668b6b..aeeaa1d2c 100644 --- a/src/crypto/aes.c +++ b/src/crypto/aes.c @@ -38,6 +38,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include /** AES strides @@ -798,3 +799,7 @@ ECB_CIPHER ( aes_ecb, aes_ecb_algorithm, /* AES in Cipher Block Chaining mode */ CBC_CIPHER ( aes_cbc, aes_cbc_algorithm, aes_algorithm, struct aes_context, AES_BLOCKSIZE ); + +/* AES in Galois/Counter mode */ +GCM_CIPHER ( aes_gcm, aes_gcm_algorithm, + aes_algorithm, struct aes_context, AES_BLOCKSIZE ); diff --git a/src/crypto/gcm.c b/src/crypto/gcm.c new file mode 100644 index 000000000..dfccd16ef --- /dev/null +++ b/src/crypto/gcm.c @@ -0,0 +1,531 @@ +/* + * Copyright (C) 2022 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * Galois/Counter Mode (GCM) + * + * The GCM algorithm is specified in + * + * https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf + * https://csrc.nist.rip/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-spec.pdf + * + */ + +#include +#include +#include +#include +#include + +/** + * GCM field polynomial + * + * GCM treats 128-bit blocks as polynomials in GF(2^128) with the + * field polynomial f(x) = 1 + x + x^2 + x^7 + x^128. + * + * In a somewhat bloody-minded interpretation of "big-endian", the + * constant term (with degree zero) is arbitrarily placed in the + * leftmost bit of the big-endian binary representation (i.e. the most + * significant bit of byte 0), thereby failing to correspond to the + * bit ordering in any CPU architecture in existence. This + * necessitates some wholly gratuitous byte reversals when + * constructing the multiplication tables, since all CPUs will treat + * bit 0 as being the least significant bit within a byte. + * + * The field polynomial maps to the 128-bit constant + * 0xe1000000000000000000000000000000 (with the x^128 term outside the + * 128-bit range), and can therefore be treated as a single-byte + * value. + */ +#define GCM_POLY 0xe1 + +/** + * Hash key for which multiplication tables are cached + * + * GCM operates much more efficiently with a cached multiplication + * table, which costs 4kB per hash key. Since this exceeds the + * available stack space, we place a single 4kB cache in .bss and + * recalculate the cached values as required. In the common case of a + * single HTTPS connection being used to download a (relatively) large + * file, the same key will be used repeatedly for almost all GCM + * operations, and so the overhead of recalculation is negligible. + */ +static const union gcm_block *gcm_cached_key; + +/** + * Cached multiplication table (M0) for Shoup's method + * + * Each entry within this table represents the result of multiplying + * the cached hash key by an arbitrary 8-bit polynomial. + */ +static union gcm_block gcm_cached_mult[256]; + +/** + * Cached reduction table (R) for Shoup's method + * + * Each entry within this table represents the result of multiplying + * the fixed polynomial x^128 by an arbitrary 8-bit polynomial. Only + * the leftmost 16 bits are stored, since all other bits within the + * result will always be zero. + */ +static uint16_t gcm_cached_reduce[256]; + +/** + * Reverse bits in a byte + * + * @v byte Byte + * @ret etyb Bit-reversed byte + */ +static inline __attribute__ (( always_inline )) uint8_t +gcm_reverse ( const uint8_t byte ) { + uint8_t etyb = etyb; + uint8_t mask; + + for ( mask = 1 ; mask ; mask <<= 1 ) { + etyb <<= 1; + if ( byte & mask ) + etyb |= 1; + } + return etyb; +} + +/** + * Update GCM counter + * + * @v ctr Counter + * @v delta Amount to add to counter + */ +static inline __attribute__ (( always_inline )) void +gcm_count ( union gcm_block *ctr, uint32_t delta ) { + uint32_t *value = &ctr->ctr.value; + + /* Update counter modulo 2^32 */ + *value = cpu_to_be32 ( be32_to_cpu ( *value ) + delta ); +} + +/** + * XOR partial data block + * + * @v src1 Source buffer 1 + * @v src2 Source buffer 2 + * @v dst Destination buffer + * @v len Length + */ +static inline void gcm_xor ( const void *src1, const void *src2, void *dst, + size_t len ) { + uint8_t *dst_bytes = dst; + const uint8_t *src1_bytes = src1; + const uint8_t *src2_bytes = src2; + + /* XOR one byte at a time */ + while ( len-- ) + *(dst_bytes++) = ( *(src1_bytes++) ^ *(src2_bytes++) ); +} + +/** + * XOR whole data block in situ + * + * @v src Source block + * @v dst Destination block + */ +static inline void gcm_xor_block ( const union gcm_block *src, + union gcm_block *dst ) { + + /* XOR whole dwords */ + dst->dword[0] ^= src->dword[0]; + dst->dword[1] ^= src->dword[1]; + dst->dword[2] ^= src->dword[2]; + dst->dword[3] ^= src->dword[3]; +} + +/** + * Multiply polynomial by (x) + * + * @v mult Multiplicand + * @v res Result + */ +static void gcm_multiply_x ( const union gcm_block *mult, + union gcm_block *res ) { + unsigned int i; + uint8_t byte; + uint8_t carry; + + /* Multiply by (x) by shifting all bits rightward */ + for ( i = 0, carry = 0 ; i < sizeof ( res->byte ) ; i++ ) { + byte = mult->byte[i]; + res->byte[i] = ( ( carry << 7 ) | ( byte >> 1 ) ); + carry = ( byte & 0x01 ); + } + + /* If result overflows, reduce modulo the field polynomial */ + if ( carry ) + res->byte[0] ^= GCM_POLY; +} + +/** + * Construct cached tables + * + * @v key Hash key + * @v context Context + */ +static void gcm_cache ( const union gcm_block *key ) { + union gcm_block *mult; + uint16_t reduce; + unsigned int this; + unsigned int other; + unsigned int i; + + /* Calculate M0[1..255] and R[1..255] + * + * The R[] values are independent of the key, but the overhead + * of recalculating them here is negligible and saves on + * overall code size since the calculations are related. + */ + for ( i = 1 ; i < 256 ; i++ ) { + + /* Reverse bit order to compensate for poor life choices */ + this = gcm_reverse ( i ); + + /* Construct entries */ + mult = &gcm_cached_mult[this]; + if ( this & 0x80 ) { + + /* Odd number: entry[i] = entry[i - 1] + poly */ + other = ( this & 0x7f ); /* bit-reversed (i - 1) */ + gcm_xor ( key, &gcm_cached_mult[other], mult, + sizeof ( *mult ) ); + reduce = gcm_cached_reduce[other]; + reduce ^= be16_to_cpu ( GCM_POLY << 8 ); + gcm_cached_reduce[this] = reduce; + + } else { + + /* Even number: entry[i] = entry[i/2] * (x) */ + other = ( this << 1 ); /* bit-reversed (i / 2) */ + gcm_multiply_x ( &gcm_cached_mult[other], mult ); + reduce = be16_to_cpu ( gcm_cached_reduce[other] ); + reduce >>= 1; + gcm_cached_reduce[this] = cpu_to_be16 ( reduce ); + } + } + + /* Record cached key */ + gcm_cached_key = key; +} + +/** + * Multiply polynomial by (x^8) in situ + * + * @v poly Multiplicand and result + */ +static void gcm_multiply_x_8 ( union gcm_block *poly ) { + uint8_t *byte; + uint8_t msb; + + /* Reduction table must already have been calculated */ + assert ( gcm_cached_key != NULL ); + + /* Record most significant byte */ + byte = &poly->byte[ sizeof ( poly->byte ) - 1 ]; + msb = *byte; + + /* Multiply least significant bytes by shifting */ + for ( ; byte > &poly->byte[0] ; byte-- ) + *byte = *( byte - 1 ); + *byte = 0; + + /* Multiply most significant byte via reduction table */ + poly->word[0] ^= gcm_cached_reduce[msb]; +} + +/** + * Multiply polynomial by hash key in situ + * + * @v key Hash key + * @v poly Multiplicand and result + */ +static void gcm_multiply_key ( const union gcm_block *key, + union gcm_block *poly ) { + union gcm_block res; + uint8_t *byte; + + /* Construct tables, if necessary */ + if ( gcm_cached_key != key ) + gcm_cache ( key ); + + /* Multiply using Shoup's algorithm */ + byte = &poly->byte[ sizeof ( poly->byte ) - 1 ]; + memcpy ( &res, &gcm_cached_mult[ *byte ], sizeof ( res ) ); + for ( byte-- ; byte >= &poly->byte[0] ; byte-- ) { + gcm_multiply_x_8 ( &res ); + gcm_xor_block ( &gcm_cached_mult[ *byte ], &res ); + } + + /* Overwrite result */ + memcpy ( poly, &res, sizeof ( *poly ) ); +} + +/** + * Encrypt/decrypt/authenticate data + * + * @v context Context + * @v src Input data, or NULL to process additional data + * @v dst Output data, or NULL to process additional data + * @v hash Hash input data + * @v len Length of data + */ +static void gcm_process ( struct gcm_context *context, const void *src, + void *dst, const void *hash, size_t len ) { + union gcm_block tmp; + uint64_t *total; + size_t frag_len; + unsigned int block; + + /* Sanity checks */ + assert ( hash != NULL ); + assert ( ( ( src == NULL ) && ( dst == NULL ) ) || + ( ( hash == src ) || ( hash == dst ) ) ); + + /* Calculate block number (for debugging) */ + block = ( ( ( context->len.len.add + 8 * sizeof ( tmp ) - 1 ) / + ( 8 * sizeof ( tmp ) ) ) + + ( ( context->len.len.data + 8 * sizeof ( tmp ) - 1 ) / + ( 8 * sizeof ( tmp ) ) ) + 1 ); + + /* Update total length (in bits) */ + total = ( src ? &context->len.len.data : &context->len.len.add ); + *total += ( len * 8 ); + + /* Process data */ + for ( ; len ; hash += frag_len, len -= frag_len, block++ ) { + + /* Calculate fragment length */ + frag_len = len; + if ( frag_len > sizeof ( tmp ) ) + frag_len = sizeof ( tmp ); + + /* Encrypt/decrypt block, if applicable */ + if ( dst ) { + + /* Increment counter */ + gcm_count ( &context->ctr, 1 ); + + /* Encrypt counter */ + DBGC2 ( context, "GCM %p Y[%d]:\n", context, block ); + DBGC2_HDA ( context, 0, &context->ctr, + sizeof ( context->ctr ) ); + cipher_encrypt ( context->raw_cipher, &context->raw_ctx, + &context->ctr, &tmp, sizeof ( tmp ) ); + DBGC2 ( context, "GCM %p E(K,Y[%d]):\n", + context, block ); + DBGC2_HDA ( context, 0, &tmp, sizeof ( tmp ) ); + + /* Encrypt/decrypt data */ + gcm_xor ( src, &tmp, dst, frag_len ); + src += frag_len; + dst += frag_len; + } + + /* Update hash */ + gcm_xor ( hash, &context->hash, &context->hash, frag_len ); + gcm_multiply_key ( &context->key, &context->hash ); + DBGC2 ( context, "GCM %p X[%d]:\n", context, block ); + DBGC2_HDA ( context, 0, &context->hash, + sizeof ( context->hash ) ); + } +} + +/** + * Construct hash + * + * @v context Context + * @v hash Hash to fill in + */ +static void gcm_hash ( struct gcm_context *context, union gcm_block *hash ) { + + /* Construct big-endian lengths block */ + hash->len.add = cpu_to_be64 ( context->len.len.add ); + hash->len.data = cpu_to_be64 ( context->len.len.data ); + DBGC2 ( context, "GCM %p len(A)||len(C):\n", context ); + DBGC2_HDA ( context, 0, hash, sizeof ( *hash ) ); + + /* Update hash */ + gcm_xor_block ( &context->hash, hash ); + gcm_multiply_key ( &context->key, hash ); + DBGC2 ( context, "GCM %p GHASH(H,A,C):\n", context ); + DBGC2_HDA ( context, 0, hash, sizeof ( *hash ) ); +} + +/** + * Construct tag + * + * @v context Context + * @v tag Tag + */ +void gcm_tag ( struct gcm_context *context, union gcm_block *tag ) { + union gcm_block tmp; + uint32_t offset; + + /* Construct hash */ + gcm_hash ( context, tag ); + + /* Construct encrypted initial counter value */ + memcpy ( &tmp, &context->ctr, sizeof ( tmp ) ); + offset = ( ( -context->len.len.data ) / ( 8 * sizeof ( tmp ) ) ); + gcm_count ( &tmp, offset ); + cipher_encrypt ( context->raw_cipher, &context->raw_ctx, &tmp, + &tmp, sizeof ( tmp ) ); + DBGC2 ( context, "GCM %p E(K,Y[0]):\n", context ); + DBGC2_HDA ( context, 0, &tmp, sizeof ( tmp ) ); + + /* Construct tag */ + gcm_xor_block ( &tmp, tag ); + DBGC2 ( context, "GCM %p T:\n", context ); + DBGC2_HDA ( context, 0, tag, sizeof ( *tag ) ); +} + +/** + * Set key + * + * @v context Context + * @v key Key + * @v keylen Key length + * @v raw_cipher Underlying cipher + * @ret rc Return status code + */ +int gcm_setkey ( struct gcm_context *context, const void *key, size_t keylen, + struct cipher_algorithm *raw_cipher ) { + int rc; + + /* Initialise GCM context */ + memset ( context, 0, sizeof ( *context ) ); + context->raw_cipher = raw_cipher; + + /* Set underlying block cipher key */ + if ( ( rc = cipher_setkey ( raw_cipher, context->raw_ctx, key, + keylen ) ) != 0 ) + return rc; + + /* Construct GCM hash key */ + cipher_encrypt ( raw_cipher, context->raw_ctx, &context->ctr, + &context->key, sizeof ( context->key ) ); + DBGC2 ( context, "GCM %p H:\n", context ); + DBGC2_HDA ( context, 0, &context->key, sizeof ( context->key ) ); + + /* Reset counter */ + context->ctr.ctr.value = cpu_to_be32 ( 1 ); + + /* Construct cached tables */ + gcm_cache ( &context->key ); + + return 0; +} + +/** + * Set initialisation vector + * + * @v ctx Context + * @v iv Initialisation vector + * @v ivlen Initialisation vector length + */ +void gcm_setiv ( struct gcm_context *context, const void *iv, size_t ivlen ) { + + /* Reset counter */ + memset ( context->ctr.ctr.iv, 0, sizeof ( context->ctr.ctr.iv ) ); + context->ctr.ctr.value = cpu_to_be32 ( 1 ); + + /* Process initialisation vector */ + if ( ivlen == sizeof ( context->ctr.ctr.iv ) ) { + + /* Initialisation vector is exactly 96 bits, use it as-is */ + memcpy ( context->ctr.ctr.iv, iv, ivlen ); + + } else { + + /* Calculate hash over initialisation vector */ + gcm_process ( context, iv, NULL, iv, ivlen ); + gcm_hash ( context, &context->ctr ); + + /* Reset accumulated hash */ + memset ( &context->hash, 0, sizeof ( context->hash ) ); + + /* Reset data lengths */ + assert ( context->len.len.add == 0 ); + context->len.len.data = 0; + } + + DBGC2 ( context, "GCM %p Y[0]:\n", context ); + DBGC2_HDA ( context, 0, &context->ctr, sizeof ( context->ctr ) ); +} + +/** + * Encrypt data + * + * @v context Context + * @v src Data to encrypt + * @v dst Buffer for encrypted data, or NULL for additional data + * @v len Length of data + */ +void gcm_encrypt ( struct gcm_context *context, const void *src, void *dst, + size_t len ) { + const void *hash; + + /* Determine hash input */ + if ( dst ) { + /* Encrypting: hash the encrypted data */ + hash = dst; + } else { + /* Authenticating: hash the input data */ + hash = src; + src = NULL; + } + + /* Process data */ + gcm_process ( context, src, dst, hash, len ); +} + +/** + * Decrypt data + * + * @v context Context + * @v src Data to decrypt + * @v dst Buffer for decrypted data, or NULL for additional data + * @v len Length of data + */ +void gcm_decrypt ( struct gcm_context *context, const void *src, void *dst, + size_t len ) { + const void *hash; + + /* Determine hash input */ + hash = src; + if ( ! dst ) { + /* Authenticating: only hash */ + src = NULL; + } + + /* Process data */ + gcm_process ( context, src, dst, hash, len ); +} diff --git a/src/include/ipxe/aes.h b/src/include/ipxe/aes.h index 0432e43ee..8731de6ba 100644 --- a/src/include/ipxe/aes.h +++ b/src/include/ipxe/aes.h @@ -47,6 +47,7 @@ struct aes_context { extern struct cipher_algorithm aes_algorithm; extern struct cipher_algorithm aes_ecb_algorithm; extern struct cipher_algorithm aes_cbc_algorithm; +extern struct cipher_algorithm aes_gcm_algorithm; int aes_wrap ( const void *kek, const void *src, void *dest, int nblk ); int aes_unwrap ( const void *kek, const void *src, void *dest, int nblk ); diff --git a/src/include/ipxe/crypto.h b/src/include/ipxe/crypto.h index e807aeb52..842f2f633 100644 --- a/src/include/ipxe/crypto.h +++ b/src/include/ipxe/crypto.h @@ -11,6 +11,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +#include /** A message digest algorithm */ struct digest_algorithm { diff --git a/src/include/ipxe/gcm.h b/src/include/ipxe/gcm.h new file mode 100644 index 000000000..658685486 --- /dev/null +++ b/src/include/ipxe/gcm.h @@ -0,0 +1,132 @@ +#ifndef _IPXE_GCM_H +#define _IPXE_GCM_H + +/** @file + * + * Galois/Counter Mode (GCM) + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include + +/** A GCM counter */ +struct gcm_counter { + /** Initialisation vector */ + uint8_t iv[12]; + /** Counter value */ + uint32_t value; +} __attribute__ (( packed )); + +/** A GCM length pair */ +struct gcm_lengths { + /** Additional data length */ + uint64_t add; + /** Data length */ + uint64_t data; +} __attribute__ (( packed )); + +/** A GCM block */ +union gcm_block { + /** Raw bytes */ + uint8_t byte[16]; + /** Raw words */ + uint16_t word[8]; + /** Raw dwords */ + uint32_t dword[4]; + /** Counter */ + struct gcm_counter ctr; + /** Lengths */ + struct gcm_lengths len; +} __attribute__ (( packed )); + +/** GCM context */ +struct gcm_context { + /** Hash key (H) */ + union gcm_block key; + /** Counter (Y) */ + union gcm_block ctr; + /** Accumulated hash (X) */ + union gcm_block hash; + /** Accumulated lengths */ + union gcm_block len; + /** Underlying block cipher */ + struct cipher_algorithm *raw_cipher; + /** Underlying block cipher context */ + uint8_t raw_ctx[0]; +}; + +extern void gcm_tag ( struct gcm_context *context, union gcm_block *tag ); +extern int gcm_setkey ( struct gcm_context *context, const void *key, + size_t keylen, struct cipher_algorithm *raw_cipher ); +extern void gcm_setiv ( struct gcm_context *context, const void *iv, + size_t ivlen ); +extern void gcm_encrypt ( struct gcm_context *context, const void *src, + void *dst, size_t len ); +extern void gcm_decrypt ( struct gcm_context *context, const void *src, + void *dst, size_t len ); + +/** + * Create a GCM mode of behaviour of an existing cipher + * + * @v _cbc_name Name for the new CBC cipher + * @v _cbc_cipher New cipher algorithm + * @v _raw_cipher Underlying cipher algorithm + * @v _raw_context Context structure for the underlying cipher + * @v _blocksize Cipher block size + */ +#define GCM_CIPHER( _gcm_name, _gcm_cipher, _raw_cipher, _raw_context, \ + _blocksize ) \ +struct _gcm_name ## _context { \ + /** GCM context */ \ + struct gcm_context gcm; \ + /** Underlying block cipher context */ \ + _raw_context raw; \ +}; \ +static int _gcm_name ## _setkey ( void *ctx, const void *key, \ + size_t keylen ) { \ + struct _gcm_name ## _context *context = ctx; \ + linker_assert ( _blocksize == sizeof ( context->gcm.key ), \ + _gcm_name ## _unsupported_blocksize ); \ + linker_assert ( ( ( void * ) &context->gcm ) == ctx, \ + _gcm_name ## _context_layout_error ); \ + linker_assert ( ( ( void * ) &context->raw ) == \ + ( ( void * ) context->gcm.raw_ctx ), \ + _gcm_name ## _context_layout_error ); \ + return gcm_setkey ( &context->gcm, key, keylen, &_raw_cipher ); \ +} \ +static void _gcm_name ## _setiv ( void *ctx, const void *iv, \ + size_t ivlen ) { \ + struct _gcm_name ## _context *context = ctx; \ + gcm_setiv ( &context->gcm, iv, ivlen ); \ +} \ +static void _gcm_name ## _encrypt ( void *ctx, const void *src, \ + void *dst, size_t len ) { \ + struct _gcm_name ## _context *context = ctx; \ + gcm_encrypt ( &context->gcm, src, dst, len ); \ +} \ +static void _gcm_name ## _decrypt ( void *ctx, const void *src, \ + void *dst, size_t len ) { \ + struct _gcm_name ## _context *context = ctx; \ + gcm_decrypt ( &context->gcm, src, dst, len ); \ +} \ +static void _gcm_name ## _auth ( void *ctx, void *auth ) { \ + struct _gcm_name ## _context *context = ctx; \ + union gcm_block *tag = auth; \ + gcm_tag ( &context->gcm, tag ); \ +} \ +struct cipher_algorithm _gcm_cipher = { \ + .name = #_gcm_name, \ + .ctxsize = sizeof ( struct _gcm_name ## _context ), \ + .blocksize = 1, \ + .authsize = sizeof ( union gcm_block ), \ + .setkey = _gcm_name ## _setkey, \ + .setiv = _gcm_name ## _setiv, \ + .encrypt = _gcm_name ## _encrypt, \ + .decrypt = _gcm_name ## _decrypt, \ + .auth = _gcm_name ## _auth, \ +}; + +#endif /* _IPXE_GCM_H */ diff --git a/src/tests/gcm_test.c b/src/tests/gcm_test.c new file mode 100644 index 000000000..04a42b5c9 --- /dev/null +++ b/src/tests/gcm_test.c @@ -0,0 +1,401 @@ +/* + * Copyright (C) 2022 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * Galois/Counter Mode (GCM) tests + * + * These test vectors are provided by NIST as part of the GCM proposed + * specification document (which, unlike the final published + * specification document, includes test vectors with intermediate + * values): + * + * https://csrc.nist.rip/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-spec.pdf + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include +#include +#include +#include +#include "cipher_test.h" + +/** 128-bit zero key */ +#define GCM_KEY_128_ZERO \ + KEY ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ) + +/** 128-bit key */ +#define GCM_KEY_128 \ + KEY ( 0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c, 0x6d, \ + 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08 ) + +/** 192-bit zero key */ +#define GCM_KEY_192_ZERO \ + KEY ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ) + +/** 192-bit key */ +#define GCM_KEY_192 \ + KEY ( 0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c, 0x6d, \ + 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08, 0xfe, 0xff, \ + 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c ) + +/** 256-bit zero key */ +#define GCM_KEY_256_ZERO \ + KEY ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00 ) + +/** 256-bit key */ +#define GCM_KEY_256 \ + KEY ( 0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c, 0x6d, \ + 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08, 0xfe, 0xff, \ + 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c, 0x6d, 0x6a, 0x8f, \ + 0x94, 0x67, 0x30, 0x83, 0x08 ) + +/** 64-bit IV */ +#define GCM_IV_64 \ + IV ( 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad ) + +/** 96-bit zero IV */ +#define GCM_IV_96_ZERO \ + IV ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00 ) + +/** 96-bit IV */ +#define GCM_IV_96 \ + IV ( 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad, 0xde, \ + 0xca, 0xf8, 0x88 ) + +/** 480-bit IV */ +#define GCM_IV_480 \ + IV ( 0x93, 0x13, 0x22, 0x5d, 0xf8, 0x84, 0x06, 0xe5, 0x55, \ + 0x90, 0x9c, 0x5a, 0xff, 0x52, 0x69, 0xaa, 0x6a, 0x7a, \ + 0x95, 0x38, 0x53, 0x4f, 0x7d, 0xa1, 0xe4, 0xc3, 0x03, \ + 0xd2, 0xa3, 0x18, 0xa7, 0x28, 0xc3, 0xc0, 0xc9, 0x51, \ + 0x56, 0x80, 0x95, 0x39, 0xfc, 0xf0, 0xe2, 0x42, 0x9a, \ + 0x6b, 0x52, 0x54, 0x16, 0xae, 0xdb, 0xf5, 0xa0, 0xde, \ + 0x6a, 0x57, 0xa6, 0x37, 0xb3, 0x9b ) + +/** Empty additional data */ +#define GCM_ADDITIONAL_EMPTY ADDITIONAL() + +/** 160-bit additional data */ +#define GCM_ADDITIONAL_160 \ + ADDITIONAL ( 0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef, \ + 0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef, \ + 0xab, 0xad, 0xda, 0xd2 ) + +/** Empty plaintext */ +#define GCM_PLAINTEXT_EMPTY PLAINTEXT() + +/** 128-bit zero plaintext */ +#define GCM_PLAINTEXT_128_ZERO \ + PLAINTEXT ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ) + +/** 512-bit plaintext */ +#define GCM_PLAINTEXT_512 \ + PLAINTEXT ( 0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5, \ + 0xa5, 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26, 0x9a, \ + 0x86, 0xa7, 0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda, \ + 0x2e, 0x4c, 0x30, 0x3d, 0x8a, 0x31, 0x8a, 0x72, \ + 0x1c, 0x3c, 0x0c, 0x95, 0x95, 0x68, 0x09, 0x53, \ + 0x2f, 0xcf, 0x0e, 0x24, 0x49, 0xa6, 0xb5, 0x25, \ + 0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d, 0xe6, 0x57, \ + 0xba, 0x63, 0x7b, 0x39, 0x1a, 0xaf, 0xd2, 0x55 ) + +/** 480-bit plaintext */ +#define GCM_PLAINTEXT_480 \ + PLAINTEXT ( 0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5, \ + 0xa5, 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26, 0x9a, \ + 0x86, 0xa7, 0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda, \ + 0x2e, 0x4c, 0x30, 0x3d, 0x8a, 0x31, 0x8a, 0x72, \ + 0x1c, 0x3c, 0x0c, 0x95, 0x95, 0x68, 0x09, 0x53, \ + 0x2f, 0xcf, 0x0e, 0x24, 0x49, 0xa6, 0xb5, 0x25, \ + 0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d, 0xe6, 0x57, \ + 0xba, 0x63, 0x7b, 0x39 ) + +/** Test 1 */ +CIPHER_TEST ( gcm_test_1, &aes_gcm_algorithm, GCM_KEY_128_ZERO, + GCM_IV_96_ZERO, GCM_ADDITIONAL_EMPTY, GCM_PLAINTEXT_EMPTY, + CIPHERTEXT(), + AUTH ( 0x58, 0xe2, 0xfc, 0xce, 0xfa, 0x7e, 0x30, 0x61, 0x36, + 0x7f, 0x1d, 0x57, 0xa4, 0xe7, 0x45, 0x5a ) ); + +/** Test 2 */ +CIPHER_TEST ( gcm_test_2, &aes_gcm_algorithm, GCM_KEY_128_ZERO, + GCM_IV_96_ZERO, GCM_ADDITIONAL_EMPTY, GCM_PLAINTEXT_128_ZERO, + CIPHERTEXT ( 0x03, 0x88, 0xda, 0xce, 0x60, 0xb6, 0xa3, 0x92, + 0xf3, 0x28, 0xc2, 0xb9, 0x71, 0xb2, 0xfe, 0x78 ), + AUTH ( 0xab, 0x6e, 0x47, 0xd4, 0x2c, 0xec, 0x13, 0xbd, 0xf5, + 0x3a, 0x67, 0xb2, 0x12, 0x57, 0xbd, 0xdf ) ); + +/** Test 3 */ +CIPHER_TEST ( gcm_test_3, &aes_gcm_algorithm, GCM_KEY_128, + GCM_IV_96, GCM_ADDITIONAL_EMPTY, GCM_PLAINTEXT_512, + CIPHERTEXT ( 0x42, 0x83, 0x1e, 0xc2, 0x21, 0x77, 0x74, 0x24, + 0x4b, 0x72, 0x21, 0xb7, 0x84, 0xd0, 0xd4, 0x9c, + 0xe3, 0xaa, 0x21, 0x2f, 0x2c, 0x02, 0xa4, 0xe0, + 0x35, 0xc1, 0x7e, 0x23, 0x29, 0xac, 0xa1, 0x2e, + 0x21, 0xd5, 0x14, 0xb2, 0x54, 0x66, 0x93, 0x1c, + 0x7d, 0x8f, 0x6a, 0x5a, 0xac, 0x84, 0xaa, 0x05, + 0x1b, 0xa3, 0x0b, 0x39, 0x6a, 0x0a, 0xac, 0x97, + 0x3d, 0x58, 0xe0, 0x91, 0x47, 0x3f, 0x59, 0x85 ), + AUTH ( 0x4d, 0x5c, 0x2a, 0xf3, 0x27, 0xcd, 0x64, 0xa6, 0x2c, + 0xf3, 0x5a, 0xbd, 0x2b, 0xa6, 0xfa, 0xb4 ) ); + +/** Test 4 */ +CIPHER_TEST ( gcm_test_4, &aes_gcm_algorithm, GCM_KEY_128, + GCM_IV_96, GCM_ADDITIONAL_160, GCM_PLAINTEXT_480, + CIPHERTEXT ( 0x42, 0x83, 0x1e, 0xc2, 0x21, 0x77, 0x74, 0x24, + 0x4b, 0x72, 0x21, 0xb7, 0x84, 0xd0, 0xd4, 0x9c, + 0xe3, 0xaa, 0x21, 0x2f, 0x2c, 0x02, 0xa4, 0xe0, + 0x35, 0xc1, 0x7e, 0x23, 0x29, 0xac, 0xa1, 0x2e, + 0x21, 0xd5, 0x14, 0xb2, 0x54, 0x66, 0x93, 0x1c, + 0x7d, 0x8f, 0x6a, 0x5a, 0xac, 0x84, 0xaa, 0x05, + 0x1b, 0xa3, 0x0b, 0x39, 0x6a, 0x0a, 0xac, 0x97, + 0x3d, 0x58, 0xe0, 0x91 ), + AUTH ( 0x5b, 0xc9, 0x4f, 0xbc, 0x32, 0x21, 0xa5, 0xdb, 0x94, + 0xfa, 0xe9, 0x5a, 0xe7, 0x12, 0x1a, 0x47 ) ); + +/** Test 5 */ +CIPHER_TEST ( gcm_test_5, &aes_gcm_algorithm, GCM_KEY_128, + GCM_IV_64, GCM_ADDITIONAL_160, GCM_PLAINTEXT_480, + CIPHERTEXT ( 0x61, 0x35, 0x3b, 0x4c, 0x28, 0x06, 0x93, 0x4a, + 0x77, 0x7f, 0xf5, 0x1f, 0xa2, 0x2a, 0x47, 0x55, + 0x69, 0x9b, 0x2a, 0x71, 0x4f, 0xcd, 0xc6, 0xf8, + 0x37, 0x66, 0xe5, 0xf9, 0x7b, 0x6c, 0x74, 0x23, + 0x73, 0x80, 0x69, 0x00, 0xe4, 0x9f, 0x24, 0xb2, + 0x2b, 0x09, 0x75, 0x44, 0xd4, 0x89, 0x6b, 0x42, + 0x49, 0x89, 0xb5, 0xe1, 0xeb, 0xac, 0x0f, 0x07, + 0xc2, 0x3f, 0x45, 0x98 ), + AUTH ( 0x36, 0x12, 0xd2, 0xe7, 0x9e, 0x3b, 0x07, 0x85, 0x56, + 0x1b, 0xe1, 0x4a, 0xac, 0xa2, 0xfc, 0xcb ) ); + +/** Test 6 */ +CIPHER_TEST ( gcm_test_6, &aes_gcm_algorithm, GCM_KEY_128, + GCM_IV_480, GCM_ADDITIONAL_160, GCM_PLAINTEXT_480, + CIPHERTEXT ( 0x8c, 0xe2, 0x49, 0x98, 0x62, 0x56, 0x15, 0xb6, + 0x03, 0xa0, 0x33, 0xac, 0xa1, 0x3f, 0xb8, 0x94, + 0xbe, 0x91, 0x12, 0xa5, 0xc3, 0xa2, 0x11, 0xa8, + 0xba, 0x26, 0x2a, 0x3c, 0xca, 0x7e, 0x2c, 0xa7, + 0x01, 0xe4, 0xa9, 0xa4, 0xfb, 0xa4, 0x3c, 0x90, + 0xcc, 0xdc, 0xb2, 0x81, 0xd4, 0x8c, 0x7c, 0x6f, + 0xd6, 0x28, 0x75, 0xd2, 0xac, 0xa4, 0x17, 0x03, + 0x4c, 0x34, 0xae, 0xe5 ), + AUTH ( 0x61, 0x9c, 0xc5, 0xae, 0xff, 0xfe, 0x0b, 0xfa, 0x46, + 0x2a, 0xf4, 0x3c, 0x16, 0x99, 0xd0, 0x50 ) ); + +/** Test 7 */ +CIPHER_TEST ( gcm_test_7, &aes_gcm_algorithm, GCM_KEY_192_ZERO, + GCM_IV_96_ZERO, GCM_ADDITIONAL_EMPTY, GCM_PLAINTEXT_EMPTY, + CIPHERTEXT(), + AUTH ( 0xcd, 0x33, 0xb2, 0x8a, 0xc7, 0x73, 0xf7, 0x4b, 0xa0, + 0x0e, 0xd1, 0xf3, 0x12, 0x57, 0x24, 0x35 ) ); + +/** Test 8 */ +CIPHER_TEST ( gcm_test_8, &aes_gcm_algorithm, GCM_KEY_192_ZERO, + GCM_IV_96_ZERO, GCM_ADDITIONAL_EMPTY, GCM_PLAINTEXT_128_ZERO, + CIPHERTEXT ( 0x98, 0xe7, 0x24, 0x7c, 0x07, 0xf0, 0xfe, 0x41, + 0x1c, 0x26, 0x7e, 0x43, 0x84, 0xb0, 0xf6, 0x00 ), + AUTH ( 0x2f, 0xf5, 0x8d, 0x80, 0x03, 0x39, 0x27, 0xab, 0x8e, + 0xf4, 0xd4, 0x58, 0x75, 0x14, 0xf0, 0xfb ) ); + +/** Test 9 */ +CIPHER_TEST ( gcm_test_9, &aes_gcm_algorithm, GCM_KEY_192, + GCM_IV_96, GCM_ADDITIONAL_EMPTY, GCM_PLAINTEXT_512, + CIPHERTEXT ( 0x39, 0x80, 0xca, 0x0b, 0x3c, 0x00, 0xe8, 0x41, + 0xeb, 0x06, 0xfa, 0xc4, 0x87, 0x2a, 0x27, 0x57, + 0x85, 0x9e, 0x1c, 0xea, 0xa6, 0xef, 0xd9, 0x84, + 0x62, 0x85, 0x93, 0xb4, 0x0c, 0xa1, 0xe1, 0x9c, + 0x7d, 0x77, 0x3d, 0x00, 0xc1, 0x44, 0xc5, 0x25, + 0xac, 0x61, 0x9d, 0x18, 0xc8, 0x4a, 0x3f, 0x47, + 0x18, 0xe2, 0x44, 0x8b, 0x2f, 0xe3, 0x24, 0xd9, + 0xcc, 0xda, 0x27, 0x10, 0xac, 0xad, 0xe2, 0x56 ), + AUTH ( 0x99, 0x24, 0xa7, 0xc8, 0x58, 0x73, 0x36, 0xbf, 0xb1, + 0x18, 0x02, 0x4d, 0xb8, 0x67, 0x4a, 0x14 ) ); + +/** Test 10 */ +CIPHER_TEST ( gcm_test_10, &aes_gcm_algorithm, GCM_KEY_192, + GCM_IV_96, GCM_ADDITIONAL_160, GCM_PLAINTEXT_480, + CIPHERTEXT ( 0x39, 0x80, 0xca, 0x0b, 0x3c, 0x00, 0xe8, 0x41, + 0xeb, 0x06, 0xfa, 0xc4, 0x87, 0x2a, 0x27, 0x57, + 0x85, 0x9e, 0x1c, 0xea, 0xa6, 0xef, 0xd9, 0x84, + 0x62, 0x85, 0x93, 0xb4, 0x0c, 0xa1, 0xe1, 0x9c, + 0x7d, 0x77, 0x3d, 0x00, 0xc1, 0x44, 0xc5, 0x25, + 0xac, 0x61, 0x9d, 0x18, 0xc8, 0x4a, 0x3f, 0x47, + 0x18, 0xe2, 0x44, 0x8b, 0x2f, 0xe3, 0x24, 0xd9, + 0xcc, 0xda, 0x27, 0x10 ), + AUTH ( 0x25, 0x19, 0x49, 0x8e, 0x80, 0xf1, 0x47, 0x8f, 0x37, + 0xba, 0x55, 0xbd, 0x6d, 0x27, 0x61, 0x8c ) ); + +/** Test 11 */ +CIPHER_TEST ( gcm_test_11, &aes_gcm_algorithm, GCM_KEY_192, + GCM_IV_64, GCM_ADDITIONAL_160, GCM_PLAINTEXT_480, + CIPHERTEXT ( 0x0f, 0x10, 0xf5, 0x99, 0xae, 0x14, 0xa1, 0x54, + 0xed, 0x24, 0xb3, 0x6e, 0x25, 0x32, 0x4d, 0xb8, + 0xc5, 0x66, 0x63, 0x2e, 0xf2, 0xbb, 0xb3, 0x4f, + 0x83, 0x47, 0x28, 0x0f, 0xc4, 0x50, 0x70, 0x57, + 0xfd, 0xdc, 0x29, 0xdf, 0x9a, 0x47, 0x1f, 0x75, + 0xc6, 0x65, 0x41, 0xd4, 0xd4, 0xda, 0xd1, 0xc9, + 0xe9, 0x3a, 0x19, 0xa5, 0x8e, 0x8b, 0x47, 0x3f, + 0xa0, 0xf0, 0x62, 0xf7 ), + AUTH ( 0x65, 0xdc, 0xc5, 0x7f, 0xcf, 0x62, 0x3a, 0x24, 0x09, + 0x4f, 0xcc, 0xa4, 0x0d, 0x35, 0x33, 0xf8 ) ); + +/** Test 12 */ +CIPHER_TEST ( gcm_test_12, &aes_gcm_algorithm, GCM_KEY_192, + GCM_IV_480, GCM_ADDITIONAL_160, GCM_PLAINTEXT_480, + CIPHERTEXT ( 0xd2, 0x7e, 0x88, 0x68, 0x1c, 0xe3, 0x24, 0x3c, + 0x48, 0x30, 0x16, 0x5a, 0x8f, 0xdc, 0xf9, 0xff, + 0x1d, 0xe9, 0xa1, 0xd8, 0xe6, 0xb4, 0x47, 0xef, + 0x6e, 0xf7, 0xb7, 0x98, 0x28, 0x66, 0x6e, 0x45, + 0x81, 0xe7, 0x90, 0x12, 0xaf, 0x34, 0xdd, 0xd9, + 0xe2, 0xf0, 0x37, 0x58, 0x9b, 0x29, 0x2d, 0xb3, + 0xe6, 0x7c, 0x03, 0x67, 0x45, 0xfa, 0x22, 0xe7, + 0xe9, 0xb7, 0x37, 0x3b ), + AUTH ( 0xdc, 0xf5, 0x66, 0xff, 0x29, 0x1c, 0x25, 0xbb, 0xb8, + 0x56, 0x8f, 0xc3, 0xd3, 0x76, 0xa6, 0xd9 ) ); + +/** Test 13 */ +CIPHER_TEST ( gcm_test_13, &aes_gcm_algorithm, GCM_KEY_256_ZERO, + GCM_IV_96_ZERO, GCM_ADDITIONAL_EMPTY, GCM_PLAINTEXT_EMPTY, + CIPHERTEXT(), + AUTH ( 0x53, 0x0f, 0x8a, 0xfb, 0xc7, 0x45, 0x36, 0xb9, 0xa9, + 0x63, 0xb4, 0xf1, 0xc4, 0xcb, 0x73, 0x8b ) ); + +/** Test 14 */ +CIPHER_TEST ( gcm_test_14, &aes_gcm_algorithm, GCM_KEY_256_ZERO, + GCM_IV_96_ZERO, GCM_ADDITIONAL_EMPTY, GCM_PLAINTEXT_128_ZERO, + CIPHERTEXT ( 0xce, 0xa7, 0x40, 0x3d, 0x4d, 0x60, 0x6b, 0x6e, + 0x07, 0x4e, 0xc5, 0xd3, 0xba, 0xf3, 0x9d, 0x18 ), + AUTH ( 0xd0, 0xd1, 0xc8, 0xa7, 0x99, 0x99, 0x6b, 0xf0, 0x26, + 0x5b, 0x98, 0xb5, 0xd4, 0x8a, 0xb9, 0x19 ) ); + +/** Test 15 */ +CIPHER_TEST ( gcm_test_15, &aes_gcm_algorithm, GCM_KEY_256, + GCM_IV_96, GCM_ADDITIONAL_EMPTY, GCM_PLAINTEXT_512, + CIPHERTEXT ( 0x52, 0x2d, 0xc1, 0xf0, 0x99, 0x56, 0x7d, 0x07, + 0xf4, 0x7f, 0x37, 0xa3, 0x2a, 0x84, 0x42, 0x7d, + 0x64, 0x3a, 0x8c, 0xdc, 0xbf, 0xe5, 0xc0, 0xc9, + 0x75, 0x98, 0xa2, 0xbd, 0x25, 0x55, 0xd1, 0xaa, + 0x8c, 0xb0, 0x8e, 0x48, 0x59, 0x0d, 0xbb, 0x3d, + 0xa7, 0xb0, 0x8b, 0x10, 0x56, 0x82, 0x88, 0x38, + 0xc5, 0xf6, 0x1e, 0x63, 0x93, 0xba, 0x7a, 0x0a, + 0xbc, 0xc9, 0xf6, 0x62, 0x89, 0x80, 0x15, 0xad ), + AUTH ( 0xb0, 0x94, 0xda, 0xc5, 0xd9, 0x34, 0x71, 0xbd, 0xec, + 0x1a, 0x50, 0x22, 0x70, 0xe3, 0xcc, 0x6c ) ); + +/** Test 16 */ +CIPHER_TEST ( gcm_test_16, &aes_gcm_algorithm, GCM_KEY_256, + GCM_IV_96, GCM_ADDITIONAL_160, GCM_PLAINTEXT_480, + CIPHERTEXT ( 0x52, 0x2d, 0xc1, 0xf0, 0x99, 0x56, 0x7d, 0x07, + 0xf4, 0x7f, 0x37, 0xa3, 0x2a, 0x84, 0x42, 0x7d, + 0x64, 0x3a, 0x8c, 0xdc, 0xbf, 0xe5, 0xc0, 0xc9, + 0x75, 0x98, 0xa2, 0xbd, 0x25, 0x55, 0xd1, 0xaa, + 0x8c, 0xb0, 0x8e, 0x48, 0x59, 0x0d, 0xbb, 0x3d, + 0xa7, 0xb0, 0x8b, 0x10, 0x56, 0x82, 0x88, 0x38, + 0xc5, 0xf6, 0x1e, 0x63, 0x93, 0xba, 0x7a, 0x0a, + 0xbc, 0xc9, 0xf6, 0x62 ), + AUTH ( 0x76, 0xfc, 0x6e, 0xce, 0x0f, 0x4e, 0x17, 0x68, 0xcd, + 0xdf, 0x88, 0x53, 0xbb, 0x2d, 0x55, 0x1b ) ); + +/** Test 17 */ +CIPHER_TEST ( gcm_test_17, &aes_gcm_algorithm, GCM_KEY_256, + GCM_IV_64, GCM_ADDITIONAL_160, GCM_PLAINTEXT_480, + CIPHERTEXT ( 0xc3, 0x76, 0x2d, 0xf1, 0xca, 0x78, 0x7d, 0x32, + 0xae, 0x47, 0xc1, 0x3b, 0xf1, 0x98, 0x44, 0xcb, + 0xaf, 0x1a, 0xe1, 0x4d, 0x0b, 0x97, 0x6a, 0xfa, + 0xc5, 0x2f, 0xf7, 0xd7, 0x9b, 0xba, 0x9d, 0xe0, + 0xfe, 0xb5, 0x82, 0xd3, 0x39, 0x34, 0xa4, 0xf0, + 0x95, 0x4c, 0xc2, 0x36, 0x3b, 0xc7, 0x3f, 0x78, + 0x62, 0xac, 0x43, 0x0e, 0x64, 0xab, 0xe4, 0x99, + 0xf4, 0x7c, 0x9b, 0x1f ), + AUTH ( 0x3a, 0x33, 0x7d, 0xbf, 0x46, 0xa7, 0x92, 0xc4, 0x5e, + 0x45, 0x49, 0x13, 0xfe, 0x2e, 0xa8, 0xf2 ) ); + +/** Test 18 */ +CIPHER_TEST ( gcm_test_18, &aes_gcm_algorithm, GCM_KEY_256, + GCM_IV_480, GCM_ADDITIONAL_160, GCM_PLAINTEXT_480, + CIPHERTEXT ( 0x5a, 0x8d, 0xef, 0x2f, 0x0c, 0x9e, 0x53, 0xf1, + 0xf7, 0x5d, 0x78, 0x53, 0x65, 0x9e, 0x2a, 0x20, + 0xee, 0xb2, 0xb2, 0x2a, 0xaf, 0xde, 0x64, 0x19, + 0xa0, 0x58, 0xab, 0x4f, 0x6f, 0x74, 0x6b, 0xf4, + 0x0f, 0xc0, 0xc3, 0xb7, 0x80, 0xf2, 0x44, 0x45, + 0x2d, 0xa3, 0xeb, 0xf1, 0xc5, 0xd8, 0x2c, 0xde, + 0xa2, 0x41, 0x89, 0x97, 0x20, 0x0e, 0xf8, 0x2e, + 0x44, 0xae, 0x7e, 0x3f ), + AUTH ( 0xa4, 0x4a, 0x82, 0x66, 0xee, 0x1c, 0x8e, 0xb0, 0xc8, + 0xb5, 0xd4, 0xcf, 0x5a, 0xe9, 0xf1, 0x9a ) ); + +/** + * Perform Galois/Counter Mode self-test + * + */ +static void gcm_test_exec ( void ) { + struct cipher_algorithm *gcm = &aes_gcm_algorithm; + unsigned int keylen; + + /* Correctness tests */ + cipher_ok ( &gcm_test_1 ); + cipher_ok ( &gcm_test_2 ); + cipher_ok ( &gcm_test_3 ); + cipher_ok ( &gcm_test_4 ); + cipher_ok ( &gcm_test_5 ); + cipher_ok ( &gcm_test_6 ); + cipher_ok ( &gcm_test_7 ); + cipher_ok ( &gcm_test_8 ); + cipher_ok ( &gcm_test_9 ); + cipher_ok ( &gcm_test_10 ); + cipher_ok ( &gcm_test_11 ); + cipher_ok ( &gcm_test_12 ); + cipher_ok ( &gcm_test_13 ); + cipher_ok ( &gcm_test_14 ); + cipher_ok ( &gcm_test_15 ); + cipher_ok ( &gcm_test_16 ); + cipher_ok ( &gcm_test_17 ); + cipher_ok ( &gcm_test_18 ); + + /* Speed tests */ + for ( keylen = 128 ; keylen <= 256 ; keylen += 64 ) { + DBG ( "AES-%d-GCM encryption required %ld cycles per byte\n", + keylen, cipher_cost_encrypt ( gcm, ( keylen / 8 ) ) ); + DBG ( "AES-%d-GCM decryption required %ld cycles per byte\n", + keylen, cipher_cost_decrypt ( gcm, ( keylen / 8 ) ) ); + } +} + +/** Galois/Counter Mode self-test */ +struct self_test gcm_test __self_test = { + .name = "gcm", + .exec = gcm_test_exec, +}; diff --git a/src/tests/tests.c b/src/tests/tests.c index 54694fa45..187037d1b 100644 --- a/src/tests/tests.c +++ b/src/tests/tests.c @@ -79,3 +79,4 @@ REQUIRE_OBJECT ( utf8_test ); REQUIRE_OBJECT ( acpi_test ); REQUIRE_OBJECT ( hmac_test ); REQUIRE_OBJECT ( dhe_test ); +REQUIRE_OBJECT ( gcm_test ); -- cgit v1.2.3-55-g7522 From d1bc872a2e3b682169da4dd708dfe143d539eaaf Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 28 Oct 2022 13:06:11 +0100 Subject: [tls] Formalise notions of fixed and record initialisation vectors TLS block ciphers always use CBC (as per RFC 5246 section 6.2.3.2) with a record initialisation vector length that is equal to the cipher block size, and no fixed initialisation vector. The initialisation vector for AEAD ciphers such as GCM is less straightforward, and requires both a fixed and per-record component. Extend the definition of a cipher suite to include fixed and record initialisation vector lengths, and generate the fixed portion (if any) as part of key expansion. Do not add explicit calls to cipher_setiv() in tls_assemble_block() and tls_split_block(), since the constraints imposed by RFC 5246 are specifically chosen to allow implementations to avoid doing so. (Instead, add a sanity check that the record initialisation vector length is equal to the cipher block size.) Signed-off-by: Michael Brown --- src/crypto/mishmash/rsa_aes_cbc_sha1.c | 8 ++++++++ src/crypto/mishmash/rsa_aes_cbc_sha256.c | 8 ++++++++ src/include/ipxe/tls.h | 10 ++++++++-- src/net/tls.c | 27 ++++++++++++++++++++++++--- 4 files changed, 48 insertions(+), 5 deletions(-) (limited to 'src/crypto') diff --git a/src/crypto/mishmash/rsa_aes_cbc_sha1.c b/src/crypto/mishmash/rsa_aes_cbc_sha1.c index b054a01c7..765ed1138 100644 --- a/src/crypto/mishmash/rsa_aes_cbc_sha1.c +++ b/src/crypto/mishmash/rsa_aes_cbc_sha1.c @@ -34,6 +34,8 @@ struct tls_cipher_suite tls_dhe_rsa_with_aes_128_cbc_sha __tls_cipher_suite ( 03 ) = { .code = htons ( TLS_DHE_RSA_WITH_AES_128_CBC_SHA ), .key_len = ( 128 / 8 ), + .fixed_iv_len = 0, + .record_iv_len = AES_BLOCKSIZE, .exchange = &tls_dhe_exchange_algorithm, .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, @@ -45,6 +47,8 @@ struct tls_cipher_suite tls_dhe_rsa_with_aes_256_cbc_sha __tls_cipher_suite ( 04 ) = { .code = htons ( TLS_DHE_RSA_WITH_AES_256_CBC_SHA ), .key_len = ( 256 / 8 ), + .fixed_iv_len = 0, + .record_iv_len = AES_BLOCKSIZE, .exchange = &tls_dhe_exchange_algorithm, .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, @@ -56,6 +60,8 @@ struct tls_cipher_suite tls_rsa_with_aes_128_cbc_sha __tls_cipher_suite ( 13 ) = { .code = htons ( TLS_RSA_WITH_AES_128_CBC_SHA ), .key_len = ( 128 / 8 ), + .fixed_iv_len = 0, + .record_iv_len = AES_BLOCKSIZE, .exchange = &tls_pubkey_exchange_algorithm, .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, @@ -67,6 +73,8 @@ struct tls_cipher_suite tls_rsa_with_aes_256_cbc_sha __tls_cipher_suite ( 14 ) = { .code = htons ( TLS_RSA_WITH_AES_256_CBC_SHA ), .key_len = ( 256 / 8 ), + .fixed_iv_len = 0, + .record_iv_len = AES_BLOCKSIZE, .exchange = &tls_pubkey_exchange_algorithm, .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, diff --git a/src/crypto/mishmash/rsa_aes_cbc_sha256.c b/src/crypto/mishmash/rsa_aes_cbc_sha256.c index b003523d5..1cc7dfe27 100644 --- a/src/crypto/mishmash/rsa_aes_cbc_sha256.c +++ b/src/crypto/mishmash/rsa_aes_cbc_sha256.c @@ -34,6 +34,8 @@ struct tls_cipher_suite tls_dhe_rsa_with_aes_128_cbc_sha256 __tls_cipher_suite ( 01 ) = { .code = htons ( TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 ), .key_len = ( 128 / 8 ), + .fixed_iv_len = 0, + .record_iv_len = AES_BLOCKSIZE, .exchange = &tls_dhe_exchange_algorithm, .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, @@ -45,6 +47,8 @@ struct tls_cipher_suite tls_dhe_rsa_with_aes_256_cbc_sha256 __tls_cipher_suite ( 02 ) = { .code = htons ( TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 ), .key_len = ( 256 / 8 ), + .fixed_iv_len = 0, + .record_iv_len = AES_BLOCKSIZE, .exchange = &tls_dhe_exchange_algorithm, .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, @@ -56,6 +60,8 @@ struct tls_cipher_suite tls_rsa_with_aes_128_cbc_sha256 __tls_cipher_suite ( 11 ) = { .code = htons ( TLS_RSA_WITH_AES_128_CBC_SHA256 ), .key_len = ( 128 / 8 ), + .fixed_iv_len = 0, + .record_iv_len = AES_BLOCKSIZE, .exchange = &tls_pubkey_exchange_algorithm, .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, @@ -67,6 +73,8 @@ struct tls_cipher_suite tls_rsa_with_aes_256_cbc_sha256 __tls_cipher_suite ( 12 ) = { .code = htons ( TLS_RSA_WITH_AES_256_CBC_SHA256 ), .key_len = ( 256 / 8 ), + .fixed_iv_len = 0, + .record_iv_len = AES_BLOCKSIZE, .exchange = &tls_pubkey_exchange_algorithm, .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, diff --git a/src/include/ipxe/tls.h b/src/include/ipxe/tls.h index 0b34dee7c..8bb1ccceb 100644 --- a/src/include/ipxe/tls.h +++ b/src/include/ipxe/tls.h @@ -169,10 +169,14 @@ struct tls_cipher_suite { struct cipher_algorithm *cipher; /** MAC digest algorithm */ struct digest_algorithm *digest; - /** Key length */ - uint16_t key_len; /** Numeric code (in network-endian order) */ uint16_t code; + /** Key length */ + uint8_t key_len; + /** Fixed initialisation vector length */ + uint8_t fixed_iv_len; + /** Record initialisation vector length */ + uint8_t record_iv_len; }; /** TLS cipher suite table */ @@ -195,6 +199,8 @@ struct tls_cipherspec { void *cipher_ctx; /** MAC secret */ void *mac_secret; + /** Fixed initialisation vector */ + void *fixed_iv; }; /** A TLS signature and hash algorithm identifier */ diff --git a/src/net/tls.c b/src/net/tls.c index 9ad8448cf..f4f8d930d 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -664,7 +664,8 @@ static int tls_generate_keys ( struct tls_connection *tls ) { struct tls_cipherspec *rx_cipherspec = &tls->rx_cipherspec_pending; size_t hash_size = tx_cipherspec->suite->digest->digestsize; size_t key_size = tx_cipherspec->suite->key_len; - size_t total = ( 2 * ( hash_size + key_size ) ); + size_t iv_size = tx_cipherspec->suite->fixed_iv_len; + size_t total = ( 2 * ( hash_size + key_size + iv_size ) ); uint8_t key_block[total]; uint8_t *key; int rc; @@ -714,6 +715,18 @@ static int tls_generate_keys ( struct tls_connection *tls ) { DBGC_HD ( tls, key, key_size ); key += key_size; + /* TX initialisation vector */ + memcpy ( tx_cipherspec->fixed_iv, key, iv_size ); + DBGC ( tls, "TLS %p TX IV:\n", tls ); + DBGC_HD ( tls, key, iv_size ); + key += iv_size; + + /* RX initialisation vector */ + memcpy ( rx_cipherspec->fixed_iv, key, iv_size ); + DBGC ( tls, "TLS %p RX IV:\n", tls ); + DBGC_HD ( tls, key, iv_size ); + key += iv_size; + assert ( ( key_block + total ) == key ); return 0; @@ -792,9 +805,10 @@ static int tls_set_cipher ( struct tls_connection *tls, /* Clear out old cipher contents, if any */ tls_clear_cipher ( tls, cipherspec ); - + /* Allocate dynamic storage */ - total = ( pubkey->ctxsize + cipher->ctxsize + digest->digestsize ); + total = ( pubkey->ctxsize + cipher->ctxsize + digest->digestsize + + suite->fixed_iv_len ); dynamic = zalloc ( total ); if ( ! dynamic ) { DBGC ( tls, "TLS %p could not allocate %zd bytes for crypto " @@ -807,6 +821,7 @@ static int tls_set_cipher ( struct tls_connection *tls, cipherspec->pubkey_ctx = dynamic; dynamic += pubkey->ctxsize; cipherspec->cipher_ctx = dynamic; dynamic += cipher->ctxsize; cipherspec->mac_secret = dynamic; dynamic += digest->digestsize; + cipherspec->fixed_iv = dynamic; dynamic += suite->fixed_iv_len; assert ( ( cipherspec->dynamic + total ) == dynamic ); /* Store parameters */ @@ -2627,6 +2642,9 @@ static void * tls_assemble_block ( struct tls_connection *tls, void *mac; void *padding; + /* Sanity check */ + assert ( iv_len == tls->tx_cipherspec.suite->record_iv_len ); + /* Calculate block-ciphered struct length */ padding_len = ( ( blocksize - 1 ) & -( iv_len + len + mac_len + 1 ) ); *plaintext_len = ( iv_len + len + mac_len + padding_len + 1 ); @@ -2781,6 +2799,9 @@ static int tls_split_block ( struct tls_connection *tls, uint8_t *padding; size_t padding_len; + /* Sanity check */ + assert ( iv_len == tls->rx_cipherspec.suite->record_iv_len ); + /* Extract initialisation vector */ iobuf = list_first_entry ( rx_data, struct io_buffer, list ); if ( iob_len ( iobuf ) < iv_len ) { -- cgit v1.2.3-55-g7522 From 30243ad73957a2e1cc4aedc3f23be66cdf399f00 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 28 Oct 2022 16:27:10 +0100 Subject: [crypto] Add concept of cipher alignment size The GCM cipher mode of operation (in common with other counter-based modes of operation) has a notion of blocksize that does not neatly fall into our current abstraction: it does operate in 16-byte blocks but allows for an arbitrary overall data length (i.e. the final block may be incomplete). Model this by adding a concept of alignment size. Each call to encrypt() or decrypt() must begin at a multiple of the alignment size from the start of the data stream. This allows us to model GCM by using a block size of 1 byte and an alignment size of 16 bytes. As a side benefit, this same concept allows us to neatly model the fact that raw AES can encrypt only a single 16-byte block, by specifying an alignment size of zero on this cipher. Signed-off-by: Michael Brown --- src/crypto/aes.c | 1 + src/crypto/arc4.c | 1 + src/crypto/crypto_null.c | 1 + src/include/ipxe/cbc.h | 1 + src/include/ipxe/crypto.h | 18 +++++++++++++++++- src/include/ipxe/ecb.h | 1 + src/include/ipxe/gcm.h | 1 + src/tests/cipher_test.c | 10 ++++++++++ 8 files changed, 33 insertions(+), 1 deletion(-) (limited to 'src/crypto') diff --git a/src/crypto/aes.c b/src/crypto/aes.c index aeeaa1d2c..5200e7760 100644 --- a/src/crypto/aes.c +++ b/src/crypto/aes.c @@ -784,6 +784,7 @@ struct cipher_algorithm aes_algorithm = { .name = "aes", .ctxsize = sizeof ( struct aes_context ), .blocksize = AES_BLOCKSIZE, + .alignsize = 0, .authsize = 0, .setkey = aes_setkey, .setiv = cipher_null_setiv, diff --git a/src/crypto/arc4.c b/src/crypto/arc4.c index 4d98abead..3b6adec19 100644 --- a/src/crypto/arc4.c +++ b/src/crypto/arc4.c @@ -119,6 +119,7 @@ struct cipher_algorithm arc4_algorithm = { .name = "ARC4", .ctxsize = ARC4_CTX_SIZE, .blocksize = 1, + .alignsize = 1, .authsize = 0, .setkey = arc4_setkey, .setiv = cipher_null_setiv, diff --git a/src/crypto/crypto_null.c b/src/crypto/crypto_null.c index 26cfbfc4e..0ad463c3e 100644 --- a/src/crypto/crypto_null.c +++ b/src/crypto/crypto_null.c @@ -84,6 +84,7 @@ struct cipher_algorithm cipher_null = { .name = "null", .ctxsize = 0, .blocksize = 1, + .alignsize = 1, .authsize = 0, .setkey = cipher_null_setkey, .setiv = cipher_null_setiv, diff --git a/src/include/ipxe/cbc.h b/src/include/ipxe/cbc.h index eead045ed..382fc9036 100644 --- a/src/include/ipxe/cbc.h +++ b/src/include/ipxe/cbc.h @@ -95,6 +95,7 @@ struct cipher_algorithm _cbc_cipher = { \ .name = #_cbc_name, \ .ctxsize = sizeof ( struct _cbc_name ## _context ), \ .blocksize = _blocksize, \ + .alignsize = _blocksize, \ .authsize = 0, \ .setkey = _cbc_name ## _setkey, \ .setiv = _cbc_name ## _setiv, \ diff --git a/src/include/ipxe/crypto.h b/src/include/ipxe/crypto.h index 842f2f633..ba09c9468 100644 --- a/src/include/ipxe/crypto.h +++ b/src/include/ipxe/crypto.h @@ -51,8 +51,24 @@ struct cipher_algorithm { const char *name; /** Context size */ size_t ctxsize; - /** Block size */ + /** Block size + * + * Every call to encrypt() or decrypt() must be for a multiple + * of this size. + */ size_t blocksize; + /** Alignment size + * + * Every call to encrypt() or decrypt() must begin at a + * multiple of this offset from the start of the stream. + * (Equivalently: all but the last call to encrypt() or + * decrypt() must be for a multiple of this size.) + * + * For ciphers supporting additional data, the main data + * stream and additional data stream are both considered to + * begin at offset zero. + */ + size_t alignsize; /** Authentication tag size */ size_t authsize; /** Set key diff --git a/src/include/ipxe/ecb.h b/src/include/ipxe/ecb.h index 1d2ebf716..db22d996d 100644 --- a/src/include/ipxe/ecb.h +++ b/src/include/ipxe/ecb.h @@ -47,6 +47,7 @@ struct cipher_algorithm _ecb_cipher = { \ .name = #_ecb_name, \ .ctxsize = sizeof ( _raw_context ), \ .blocksize = _blocksize, \ + .alignsize = _blocksize, \ .authsize = 0, \ .setkey = _ecb_name ## _setkey, \ .setiv = _ecb_name ## _setiv, \ diff --git a/src/include/ipxe/gcm.h b/src/include/ipxe/gcm.h index 658685486..d93eecd8e 100644 --- a/src/include/ipxe/gcm.h +++ b/src/include/ipxe/gcm.h @@ -121,6 +121,7 @@ struct cipher_algorithm _gcm_cipher = { \ .name = #_gcm_name, \ .ctxsize = sizeof ( struct _gcm_name ## _context ), \ .blocksize = 1, \ + .alignsize = sizeof ( union gcm_block ), \ .authsize = sizeof ( union gcm_block ), \ .setkey = _gcm_name ## _setkey, \ .setiv = _gcm_name ## _setiv, \ diff --git a/src/tests/cipher_test.c b/src/tests/cipher_test.c index cc732c2e0..2ead3c827 100644 --- a/src/tests/cipher_test.c +++ b/src/tests/cipher_test.c @@ -131,8 +131,18 @@ void cipher_decrypt_okx ( struct cipher_test *test, const char *file, */ void cipher_okx ( struct cipher_test *test, const char *file, unsigned int line ) { + struct cipher_algorithm *cipher = test->cipher; + size_t len = test->len; + /* Sanity checks */ + okx ( cipher->blocksize != 0, file, line ); + okx ( ( len % cipher->blocksize ) == 0, file, line ); + okx ( ( cipher->alignsize % cipher->blocksize ) == 0, file, line ); + + /* Report encryption test result */ cipher_encrypt_okx ( test, file, line ); + + /* Report decryption test result */ cipher_decrypt_okx ( test, file, line ); } -- cgit v1.2.3-55-g7522 From c453b4c284dbedb5de0663f6b30878b425a7a3e8 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 7 Nov 2022 18:11:36 +0000 Subject: [tls] Add MAC length as a cipher suite parameter TLS stream and block ciphers use a MAC with a length equal to the output length of the digest algorithm in use. For AEAD ciphers there is no MAC, with the equivalent functionality provided by the cipher algorithm's authentication tag. Allow for the existence of AEAD cipher suites by making the MAC length a parameter of the cipher suite. Assume that the MAC key length is equal to the MAC length, since this is true for all currently supported cipher suites. Signed-off-by: Michael Brown --- src/crypto/mishmash/rsa_aes_cbc_sha1.c | 4 ++++ src/crypto/mishmash/rsa_aes_cbc_sha256.c | 4 ++++ src/include/ipxe/tls.h | 2 ++ src/net/tls.c | 34 +++++++++++++++++--------------- 4 files changed, 28 insertions(+), 16 deletions(-) (limited to 'src/crypto') diff --git a/src/crypto/mishmash/rsa_aes_cbc_sha1.c b/src/crypto/mishmash/rsa_aes_cbc_sha1.c index 765ed1138..4f399a036 100644 --- a/src/crypto/mishmash/rsa_aes_cbc_sha1.c +++ b/src/crypto/mishmash/rsa_aes_cbc_sha1.c @@ -36,6 +36,7 @@ tls_dhe_rsa_with_aes_128_cbc_sha __tls_cipher_suite ( 03 ) = { .key_len = ( 128 / 8 ), .fixed_iv_len = 0, .record_iv_len = AES_BLOCKSIZE, + .mac_len = SHA1_DIGEST_SIZE, .exchange = &tls_dhe_exchange_algorithm, .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, @@ -49,6 +50,7 @@ tls_dhe_rsa_with_aes_256_cbc_sha __tls_cipher_suite ( 04 ) = { .key_len = ( 256 / 8 ), .fixed_iv_len = 0, .record_iv_len = AES_BLOCKSIZE, + .mac_len = SHA1_DIGEST_SIZE, .exchange = &tls_dhe_exchange_algorithm, .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, @@ -62,6 +64,7 @@ tls_rsa_with_aes_128_cbc_sha __tls_cipher_suite ( 13 ) = { .key_len = ( 128 / 8 ), .fixed_iv_len = 0, .record_iv_len = AES_BLOCKSIZE, + .mac_len = SHA1_DIGEST_SIZE, .exchange = &tls_pubkey_exchange_algorithm, .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, @@ -75,6 +78,7 @@ tls_rsa_with_aes_256_cbc_sha __tls_cipher_suite ( 14 ) = { .key_len = ( 256 / 8 ), .fixed_iv_len = 0, .record_iv_len = AES_BLOCKSIZE, + .mac_len = SHA1_DIGEST_SIZE, .exchange = &tls_pubkey_exchange_algorithm, .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, diff --git a/src/crypto/mishmash/rsa_aes_cbc_sha256.c b/src/crypto/mishmash/rsa_aes_cbc_sha256.c index 1cc7dfe27..4b02a7743 100644 --- a/src/crypto/mishmash/rsa_aes_cbc_sha256.c +++ b/src/crypto/mishmash/rsa_aes_cbc_sha256.c @@ -36,6 +36,7 @@ tls_dhe_rsa_with_aes_128_cbc_sha256 __tls_cipher_suite ( 01 ) = { .key_len = ( 128 / 8 ), .fixed_iv_len = 0, .record_iv_len = AES_BLOCKSIZE, + .mac_len = SHA256_DIGEST_SIZE, .exchange = &tls_dhe_exchange_algorithm, .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, @@ -49,6 +50,7 @@ tls_dhe_rsa_with_aes_256_cbc_sha256 __tls_cipher_suite ( 02 ) = { .key_len = ( 256 / 8 ), .fixed_iv_len = 0, .record_iv_len = AES_BLOCKSIZE, + .mac_len = SHA256_DIGEST_SIZE, .exchange = &tls_dhe_exchange_algorithm, .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, @@ -62,6 +64,7 @@ tls_rsa_with_aes_128_cbc_sha256 __tls_cipher_suite ( 11 ) = { .key_len = ( 128 / 8 ), .fixed_iv_len = 0, .record_iv_len = AES_BLOCKSIZE, + .mac_len = SHA256_DIGEST_SIZE, .exchange = &tls_pubkey_exchange_algorithm, .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, @@ -75,6 +78,7 @@ tls_rsa_with_aes_256_cbc_sha256 __tls_cipher_suite ( 12 ) = { .key_len = ( 256 / 8 ), .fixed_iv_len = 0, .record_iv_len = AES_BLOCKSIZE, + .mac_len = SHA256_DIGEST_SIZE, .exchange = &tls_pubkey_exchange_algorithm, .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, diff --git a/src/include/ipxe/tls.h b/src/include/ipxe/tls.h index be192b7ef..8796fe931 100644 --- a/src/include/ipxe/tls.h +++ b/src/include/ipxe/tls.h @@ -185,6 +185,8 @@ struct tls_cipher_suite { uint8_t fixed_iv_len; /** Record initialisation vector length */ uint8_t record_iv_len; + /** MAC length */ + uint8_t mac_len; }; /** TLS cipher suite table */ diff --git a/src/net/tls.c b/src/net/tls.c index a613a08ed..06d01aeb3 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -662,7 +662,7 @@ static void tls_generate_master_secret ( struct tls_connection *tls, static int tls_generate_keys ( struct tls_connection *tls ) { struct tls_cipherspec *tx_cipherspec = &tls->tx_cipherspec_pending; struct tls_cipherspec *rx_cipherspec = &tls->rx_cipherspec_pending; - size_t hash_size = tx_cipherspec->suite->digest->digestsize; + size_t hash_size = tx_cipherspec->suite->mac_len; size_t key_size = tx_cipherspec->suite->key_len; size_t iv_size = tx_cipherspec->suite->fixed_iv_len; size_t total = ( 2 * ( hash_size + key_size + iv_size ) ); @@ -799,7 +799,6 @@ static int tls_set_cipher ( struct tls_connection *tls, struct tls_cipher_suite *suite ) { struct pubkey_algorithm *pubkey = suite->pubkey; struct cipher_algorithm *cipher = suite->cipher; - struct digest_algorithm *digest = suite->digest; size_t total; void *dynamic; @@ -807,7 +806,7 @@ static int tls_set_cipher ( struct tls_connection *tls, tls_clear_cipher ( tls, cipherspec ); /* Allocate dynamic storage */ - total = ( pubkey->ctxsize + cipher->ctxsize + digest->digestsize + + total = ( pubkey->ctxsize + cipher->ctxsize + suite->mac_len + suite->fixed_iv_len ); dynamic = zalloc ( total ); if ( ! dynamic ) { @@ -820,7 +819,7 @@ static int tls_set_cipher ( struct tls_connection *tls, cipherspec->dynamic = dynamic; cipherspec->pubkey_ctx = dynamic; dynamic += pubkey->ctxsize; cipherspec->cipher_ctx = dynamic; dynamic += cipher->ctxsize; - cipherspec->mac_secret = dynamic; dynamic += digest->digestsize; + cipherspec->mac_secret = dynamic; dynamic += suite->mac_len; cipherspec->fixed_iv = dynamic; dynamic += suite->fixed_iv_len; assert ( ( cipherspec->dynamic + total ) == dynamic ); @@ -2525,9 +2524,10 @@ static int tls_new_record ( struct tls_connection *tls, unsigned int type, */ static void tls_hmac_init ( struct tls_cipherspec *cipherspec, void *ctx, struct tls_auth_header *authhdr ) { - struct digest_algorithm *digest = cipherspec->suite->digest; + struct tls_cipher_suite *suite = cipherspec->suite; + struct digest_algorithm *digest = suite->digest; - hmac_init ( digest, ctx, cipherspec->mac_secret, digest->digestsize ); + hmac_init ( digest, ctx, cipherspec->mac_secret, suite->mac_len ); hmac_update ( digest, ctx, authhdr, sizeof ( *authhdr ) ); } @@ -2593,7 +2593,7 @@ static void tls_hmac ( struct tls_cipherspec *cipherspec, static void * __malloc tls_assemble_stream ( struct tls_connection *tls, const void *data, size_t len, void *digest, size_t *plaintext_len ) { - size_t mac_len = tls->tx_cipherspec.suite->digest->digestsize; + size_t mac_len = tls->tx_cipherspec.suite->mac_len; void *plaintext; void *content; void *mac; @@ -2629,7 +2629,7 @@ static void * tls_assemble_block ( struct tls_connection *tls, const void *data, size_t len, void *digest, size_t *plaintext_len ) { size_t blocksize = tls->tx_cipherspec.suite->cipher->blocksize; - size_t mac_len = tls->tx_cipherspec.suite->digest->digestsize; + size_t mac_len = tls->tx_cipherspec.suite->mac_len; size_t iv_len = blocksize; size_t padding_len; void *plaintext; @@ -2675,15 +2675,16 @@ static void * tls_assemble_block ( struct tls_connection *tls, static int tls_send_plaintext ( struct tls_connection *tls, unsigned int type, const void *data, size_t len ) { struct tls_cipherspec *cipherspec = &tls->tx_cipherspec; - struct cipher_algorithm *cipher = cipherspec->suite->cipher; + struct tls_cipher_suite *suite = cipherspec->suite; + struct cipher_algorithm *cipher = suite->cipher; + struct digest_algorithm *digest = suite->digest; struct tls_auth_header authhdr; struct tls_header *tlshdr; void *plaintext = NULL; size_t plaintext_len; struct io_buffer *ciphertext = NULL; size_t ciphertext_len; - size_t mac_len = cipherspec->suite->digest->digestsize; - uint8_t mac[mac_len]; + uint8_t mac[digest->digestsize]; int rc; /* Construct header */ @@ -2762,7 +2763,7 @@ static int tls_send_plaintext ( struct tls_connection *tls, unsigned int type, */ static int tls_split_stream ( struct tls_connection *tls, struct list_head *rx_data, void **mac ) { - size_t mac_len = tls->rx_cipherspec.suite->digest->digestsize; + size_t mac_len = tls->rx_cipherspec.suite->mac_len; struct io_buffer *iobuf; /* Extract MAC */ @@ -2789,7 +2790,7 @@ static int tls_split_stream ( struct tls_connection *tls, */ static int tls_split_block ( struct tls_connection *tls, struct list_head *rx_data, void **mac ) { - size_t mac_len = tls->rx_cipherspec.suite->digest->digestsize; + size_t mac_len = tls->rx_cipherspec.suite->mac_len; size_t iv_len = tls->rx_cipherspec.suite->cipher->blocksize; struct io_buffer *iobuf; uint8_t *padding_final; @@ -2850,8 +2851,9 @@ static int tls_new_ciphertext ( struct tls_connection *tls, struct tls_header *tlshdr, struct list_head *rx_data ) { struct tls_cipherspec *cipherspec = &tls->rx_cipherspec; - struct cipher_algorithm *cipher = cipherspec->suite->cipher; - struct digest_algorithm *digest = cipherspec->suite->digest; + struct tls_cipher_suite *suite = cipherspec->suite; + struct cipher_algorithm *cipher = suite->cipher; + struct digest_algorithm *digest = suite->digest; struct tls_auth_header authhdr; uint8_t ctx[ hmac_ctxsize ( digest ) ]; uint8_t verify_mac[digest->digestsize]; @@ -2893,7 +2895,7 @@ static int tls_new_ciphertext ( struct tls_connection *tls, iob_len ( iobuf ) ); } tls_hmac_final ( cipherspec, ctx, verify_mac ); - if ( memcmp ( mac, verify_mac, sizeof ( verify_mac ) ) != 0 ) { + if ( memcmp ( mac, verify_mac, suite->mac_len ) != 0 ) { DBGC ( tls, "TLS %p failed MAC verification\n", tls ); return -EINVAL_MAC; } -- cgit v1.2.3-55-g7522 From 7256a6eb24720adfd30c0307a415e51e9a402059 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 9 Nov 2022 14:04:43 +0000 Subject: [tls] Allow handshake digest algorithm to be specified by cipher suite All existing cipher suites use SHA-256 as the TLSv1.2 and above handshake digest algorithm (even when using SHA-1 as the MAC digest algorithm). Some GCM cipher suites use SHA-384 as the handshake digest algorithm. Allow the cipher suite to specify the handshake (and PRF) digest algorithm to be used for TLSv1.2 and above. This requires some restructuring to allow for the fact that the ClientHello message must be included within the handshake digest, even though the relevant digest algorithm is not yet known at the point that the ClientHello is sent. Fortunately, the ClientHello may be reproduced verbatim at the point of receiving the ServerHello, so we rely on reconstructing (rather than storing) this message. Signed-off-by: Michael Brown --- src/crypto/mishmash/rsa_aes_cbc_sha1.c | 5 + src/crypto/mishmash/rsa_aes_cbc_sha256.c | 4 + src/include/ipxe/tls.h | 7 +- src/net/tls.c | 177 ++++++++++++++++++++----------- 4 files changed, 125 insertions(+), 68 deletions(-) (limited to 'src/crypto') diff --git a/src/crypto/mishmash/rsa_aes_cbc_sha1.c b/src/crypto/mishmash/rsa_aes_cbc_sha1.c index 4f399a036..c2ecf1205 100644 --- a/src/crypto/mishmash/rsa_aes_cbc_sha1.c +++ b/src/crypto/mishmash/rsa_aes_cbc_sha1.c @@ -27,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include /** TLS_DHE_RSA_WITH_AES_128_CBC_SHA cipher suite */ @@ -41,6 +42,7 @@ tls_dhe_rsa_with_aes_128_cbc_sha __tls_cipher_suite ( 03 ) = { .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, .digest = &sha1_algorithm, + .handshake = &sha256_algorithm, }; /** TLS_DHE_RSA_WITH_AES_256_CBC_SHA cipher suite */ @@ -55,6 +57,7 @@ tls_dhe_rsa_with_aes_256_cbc_sha __tls_cipher_suite ( 04 ) = { .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, .digest = &sha1_algorithm, + .handshake = &sha256_algorithm, }; /** TLS_RSA_WITH_AES_128_CBC_SHA cipher suite */ @@ -69,6 +72,7 @@ tls_rsa_with_aes_128_cbc_sha __tls_cipher_suite ( 13 ) = { .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, .digest = &sha1_algorithm, + .handshake = &sha256_algorithm, }; /** TLS_RSA_WITH_AES_256_CBC_SHA cipher suite */ @@ -83,4 +87,5 @@ tls_rsa_with_aes_256_cbc_sha __tls_cipher_suite ( 14 ) = { .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, .digest = &sha1_algorithm, + .handshake = &sha256_algorithm, }; diff --git a/src/crypto/mishmash/rsa_aes_cbc_sha256.c b/src/crypto/mishmash/rsa_aes_cbc_sha256.c index 4b02a7743..7f114b1fd 100644 --- a/src/crypto/mishmash/rsa_aes_cbc_sha256.c +++ b/src/crypto/mishmash/rsa_aes_cbc_sha256.c @@ -41,6 +41,7 @@ tls_dhe_rsa_with_aes_128_cbc_sha256 __tls_cipher_suite ( 01 ) = { .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, .digest = &sha256_algorithm, + .handshake = &sha256_algorithm, }; /** TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 cipher suite */ @@ -55,6 +56,7 @@ tls_dhe_rsa_with_aes_256_cbc_sha256 __tls_cipher_suite ( 02 ) = { .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, .digest = &sha256_algorithm, + .handshake = &sha256_algorithm, }; /** TLS_RSA_WITH_AES_128_CBC_SHA256 cipher suite */ @@ -69,6 +71,7 @@ tls_rsa_with_aes_128_cbc_sha256 __tls_cipher_suite ( 11 ) = { .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, .digest = &sha256_algorithm, + .handshake = &sha256_algorithm, }; /** TLS_RSA_WITH_AES_256_CBC_SHA256 cipher suite */ @@ -83,4 +86,5 @@ tls_rsa_with_aes_256_cbc_sha256 __tls_cipher_suite ( 12 ) = { .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, .digest = &sha256_algorithm, + .handshake = &sha256_algorithm, }; diff --git a/src/include/ipxe/tls.h b/src/include/ipxe/tls.h index 8796fe931..355814132 100644 --- a/src/include/ipxe/tls.h +++ b/src/include/ipxe/tls.h @@ -16,7 +16,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include -#include #include #include #include @@ -177,6 +176,8 @@ struct tls_cipher_suite { struct cipher_algorithm *cipher; /** MAC digest algorithm */ struct digest_algorithm *digest; + /** Handshake digest algorithm (for TLSv1.2 and above) */ + struct digest_algorithm *handshake; /** Numeric code (in network-endian order) */ uint16_t code; /** Key length */ @@ -346,10 +347,6 @@ struct tls_connection { 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 */ - uint8_t handshake_sha256_ctx[SHA256_CTX_SIZE]; /** Digest algorithm used for handshake verification */ struct digest_algorithm *handshake_digest; /** Digest algorithm context used for handshake verification */ diff --git a/src/net/tls.c b/src/net/tls.c index af310a58f..899629626 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -383,6 +383,7 @@ static void free_tls ( struct refcnt *refcnt ) { tls_clear_cipher ( tls, &tls->rx_cipherspec ); tls_clear_cipher ( tls, &tls->rx_cipherspec_pending ); free ( tls->server_key ); + free ( tls->handshake_ctx ); list_for_each_entry_safe ( iobuf, tmp, &tls->rx_data, list ) { list_del ( &iobuf->list ); free_iob ( iobuf ); @@ -564,8 +565,8 @@ static void tls_prf ( struct tls_connection *tls, const void *secret, va_start ( seeds, out_len ); if ( tls_version ( tls, TLS_VERSION_TLS_1_2 ) ) { - /* Use P_SHA256 for TLSv1.2 and later */ - tls_p_hash_va ( tls, &sha256_algorithm, secret, secret_len, + /* Use handshake digest PRF for TLSv1.2 and later */ + tls_p_hash_va ( tls, tls->handshake_digest, secret, secret_len, out, out_len, seeds ); } else { /* Use combination of P_MD5 and P_SHA-1 for TLSv1.1 @@ -728,6 +729,83 @@ static int tls_generate_keys ( struct tls_connection *tls ) { return 0; } +/****************************************************************************** + * + * Handshake verification + * + ****************************************************************************** + */ + +/** + * Clear handshake digest algorithm + * + * @v tls TLS connection + */ +static void tls_clear_handshake ( struct tls_connection *tls ) { + + /* Select null digest algorithm */ + tls->handshake_digest = &digest_null; + + /* Free any existing context */ + free ( tls->handshake_ctx ); + tls->handshake_ctx = NULL; +} + +/** + * Select handshake digest algorithm + * + * @v tls TLS connection + * @v digest Handshake digest algorithm + * @ret rc Return status code + */ +static int tls_select_handshake ( struct tls_connection *tls, + struct digest_algorithm *digest ) { + + /* Clear existing handshake digest */ + tls_clear_handshake ( tls ); + + /* Allocate and initialise context */ + tls->handshake_ctx = malloc ( digest->ctxsize ); + if ( ! tls->handshake_ctx ) + return -ENOMEM; + tls->handshake_digest = digest; + digest_init ( digest, tls->handshake_ctx ); + + return 0; +} + +/** + * Add handshake record to verification hash + * + * @v tls TLS connection + * @v data Handshake record + * @v len Length of handshake record + * @ret rc Return status code + */ +static int tls_add_handshake ( struct tls_connection *tls, + const void *data, size_t len ) { + struct digest_algorithm *digest = tls->handshake_digest; + + digest_update ( digest, tls->handshake_ctx, data, len ); + return 0; +} + +/** + * Calculate handshake verification hash + * + * @v tls TLS connection + * @v out Output buffer + * + * Calculates the digest over all handshake messages seen so far. + */ +static void tls_verify_handshake ( struct tls_connection *tls, void *out ) { + struct digest_algorithm *digest = tls->handshake_digest; + uint8_t ctx[ digest->ctxsize ]; + + memcpy ( ctx, tls->handshake_ctx, sizeof ( ctx ) ); + digest_final ( digest, ctx, out ); +} + /****************************************************************************** * * Cipher suite management @@ -835,6 +913,7 @@ static int tls_set_cipher ( struct tls_connection *tls, static int tls_select_cipher ( struct tls_connection *tls, unsigned int cipher_suite ) { struct tls_cipher_suite *suite; + struct digest_algorithm *digest; int rc; /* Identify cipher suite */ @@ -845,6 +924,12 @@ static int tls_select_cipher ( struct tls_connection *tls, return -ENOTSUP_CIPHER; } + /* Set handshake digest algorithm */ + digest = ( tls_version ( tls, TLS_VERSION_TLS_1_2 ) ? + suite->handshake : &md5_sha1_algorithm ); + if ( ( rc = tls_select_handshake ( tls, digest ) ) != 0 ) + return rc; + /* Set ciphers */ if ( ( rc = tls_set_cipher ( tls, &tls->tx_cipherspec_pending, suite ) ) != 0 ) @@ -956,46 +1041,6 @@ tls_signature_hash_digest ( struct tls_signature_hash_id code ) { return NULL; } -/****************************************************************************** - * - * Handshake verification - * - ****************************************************************************** - */ - -/** - * Add handshake record to verification hash - * - * @v tls TLS connection - * @v data Handshake record - * @v len Length of handshake record - */ -static void tls_add_handshake ( struct tls_connection *tls, - const void *data, size_t len ) { - - digest_update ( &md5_sha1_algorithm, tls->handshake_md5_sha1_ctx, - data, len ); - digest_update ( &sha256_algorithm, tls->handshake_sha256_ctx, - data, len ); -} - -/** - * Calculate handshake verification hash - * - * @v tls TLS connection - * @v out Output buffer - * - * Calculates the MD5+SHA1 or SHA256 digest over all handshake - * messages seen so far. - */ -static void tls_verify_handshake ( struct tls_connection *tls, void *out ) { - struct digest_algorithm *digest = tls->handshake_digest; - uint8_t ctx[ digest->ctxsize ]; - - memcpy ( ctx, tls->handshake_ctx, sizeof ( ctx ) ); - digest_final ( digest, ctx, out ); -} - /****************************************************************************** * * Record handling @@ -1037,12 +1082,6 @@ static void tls_restart ( struct tls_connection *tls ) { assert ( ! is_pending ( &tls->server_negotiation ) ); assert ( ! is_pending ( &tls->validation ) ); - /* (Re)initialise handshake context */ - digest_init ( &md5_sha1_algorithm, tls->handshake_md5_sha1_ctx ); - digest_init ( &sha256_algorithm, tls->handshake_sha256_ctx ); - tls->handshake_digest = &sha256_algorithm; - tls->handshake_ctx = tls->handshake_sha256_ctx; - /* (Re)start negotiation */ tls->tx_pending = TLS_TX_CLIENT_HELLO; tls_tx_resume ( tls ); @@ -1059,7 +1098,7 @@ static void tls_restart ( struct tls_connection *tls ) { * @ret rc Return status code */ static int tls_send_handshake ( struct tls_connection *tls, - void *data, size_t len ) { + const void *data, size_t len ) { /* Add to handshake digest */ tls_add_handshake ( tls, data, len ); @@ -1069,12 +1108,16 @@ static int tls_send_handshake ( struct tls_connection *tls, } /** - * Transmit Client Hello record + * Digest or transmit Client Hello record * * @v tls TLS connection + * @v action Action to take on Client Hello record * @ret rc Return status code */ -static int tls_send_client_hello ( struct tls_connection *tls ) { +static int tls_client_hello ( struct tls_connection *tls, + int ( * action ) ( struct tls_connection *tls, + const void *data, + size_t len ) ) { struct tls_session *session = tls->session; size_t name_len = strlen ( session->name ); struct { @@ -1182,7 +1225,18 @@ static int tls_send_client_hello ( struct tls_connection *tls ) { memcpy ( hello.extensions.session_ticket.data, session->ticket, sizeof ( hello.extensions.session_ticket.data ) ); - return tls_send_handshake ( tls, &hello, sizeof ( hello ) ); + return action ( tls, &hello, sizeof ( hello ) ); +} + +/** + * Transmit Client Hello record + * + * @v tls TLS connection + * @ret rc Return status code + */ +static int tls_send_client_hello ( struct tls_connection *tls ) { + + return tls_client_hello ( tls, tls_send_handshake ); } /** @@ -1892,22 +1946,18 @@ static int tls_new_server_hello ( struct tls_connection *tls, DBGC ( tls, "TLS %p using protocol version %d.%d\n", tls, ( version >> 8 ), ( version & 0xff ) ); - /* Use MD5+SHA1 digest algorithm for handshake verification - * for versions earlier than TLSv1.2. - */ - if ( ! tls_version ( tls, TLS_VERSION_TLS_1_2 ) ) { - tls->handshake_digest = &md5_sha1_algorithm; - tls->handshake_ctx = tls->handshake_md5_sha1_ctx; - } + /* Select cipher suite */ + if ( ( rc = tls_select_cipher ( tls, hello_b->cipher_suite ) ) != 0 ) + return rc; + + /* Add preceding Client Hello to handshake digest */ + if ( ( rc = tls_client_hello ( tls, tls_add_handshake ) ) != 0 ) + return rc; /* Copy out server random bytes */ memcpy ( &tls->server_random, &hello_a->random, sizeof ( tls->server_random ) ); - /* Select cipher suite */ - if ( ( rc = tls_select_cipher ( tls, hello_b->cipher_suite ) ) != 0 ) - return rc; - /* Check session ID */ if ( hello_a->session_id_len && ( hello_a->session_id_len == tls->session_id_len ) && @@ -3504,6 +3554,7 @@ int add_tls ( struct interface *xfer, const char *name, tls_clear_cipher ( tls, &tls->tx_cipherspec_pending ); tls_clear_cipher ( tls, &tls->rx_cipherspec ); tls_clear_cipher ( tls, &tls->rx_cipherspec_pending ); + tls_clear_handshake ( tls ); tls->client_random.gmt_unix_time = time ( NULL ); iob_populate ( &tls->rx_header_iobuf, &tls->rx_header, 0, sizeof ( tls->rx_header ) ); -- cgit v1.2.3-55-g7522 From 63577207ab95a53b29c1fa441be25ee15747bbe0 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 9 Nov 2022 16:45:54 +0000 Subject: [crypto] Ensure relevant GCM cipher state is cleared by cipher_setiv() Reset the accumulated authentication state when cipher_setiv() is called, to allow the cipher to be reused without resetting the key. Signed-off-by: Michael Brown --- src/crypto/gcm.c | 20 +++++++++++++------- src/include/ipxe/gcm.h | 8 ++++---- 2 files changed, 17 insertions(+), 11 deletions(-) (limited to 'src/crypto') diff --git a/src/crypto/gcm.c b/src/crypto/gcm.c index dfccd16ef..f8d425434 100644 --- a/src/crypto/gcm.c +++ b/src/crypto/gcm.c @@ -452,9 +452,18 @@ int gcm_setkey ( struct gcm_context *context, const void *key, size_t keylen, * @v ivlen Initialisation vector length */ void gcm_setiv ( struct gcm_context *context, const void *iv, size_t ivlen ) { + union gcm_block *check = ( ( void * ) context ); + + /* Sanity checks */ + linker_assert ( &context->hash == check, gcm_bad_layout ); + linker_assert ( &context->len == check + 1, gcm_bad_layout ); + linker_assert ( &context->ctr == check + 2, gcm_bad_layout ); + linker_assert ( &context->key == check + 3, gcm_bad_layout ); + + /* Reset non-key state */ + memset ( context, 0, offsetof ( typeof ( *context ), key ) ); /* Reset counter */ - memset ( context->ctr.ctr.iv, 0, sizeof ( context->ctr.ctr.iv ) ); context->ctr.ctr.value = cpu_to_be32 ( 1 ); /* Process initialisation vector */ @@ -468,13 +477,10 @@ void gcm_setiv ( struct gcm_context *context, const void *iv, size_t ivlen ) { /* Calculate hash over initialisation vector */ gcm_process ( context, iv, NULL, iv, ivlen ); gcm_hash ( context, &context->ctr ); - - /* Reset accumulated hash */ - memset ( &context->hash, 0, sizeof ( context->hash ) ); - - /* Reset data lengths */ assert ( context->len.len.add == 0 ); - context->len.len.data = 0; + + /* Reset non-key, non-counter state */ + memset ( context, 0, offsetof ( typeof ( *context ), ctr ) ); } DBGC2 ( context, "GCM %p Y[0]:\n", context ); diff --git a/src/include/ipxe/gcm.h b/src/include/ipxe/gcm.h index d93eecd8e..90ef0b522 100644 --- a/src/include/ipxe/gcm.h +++ b/src/include/ipxe/gcm.h @@ -44,14 +44,14 @@ union gcm_block { /** GCM context */ struct gcm_context { - /** Hash key (H) */ - union gcm_block key; - /** Counter (Y) */ - union gcm_block ctr; /** Accumulated hash (X) */ union gcm_block hash; /** Accumulated lengths */ union gcm_block len; + /** Counter (Y) */ + union gcm_block ctr; + /** Hash key (H) */ + union gcm_block key; /** Underlying block cipher */ struct cipher_algorithm *raw_cipher; /** Underlying block cipher context */ -- cgit v1.2.3-55-g7522 From 4acded7e574fb18dd653a18f48a7b6066c543712 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 9 Nov 2022 16:48:04 +0000 Subject: [crypto] Support in-place decryption for GCM ciphers The hash calculation is currently performed incorrectly when decrypting in place, since the ciphertext will have been overwritten with the plaintext before being used to update the hash value. Restructure the code to allow for in-place encryption and decryption. Choose to optimise for the decryption case, since we are likely to decrypt much more data than we encrypt. Signed-off-by: Michael Brown --- src/crypto/gcm.c | 66 +++++++++++++++++++++++++++----------------------------- 1 file changed, 32 insertions(+), 34 deletions(-) (limited to 'src/crypto') diff --git a/src/crypto/gcm.c b/src/crypto/gcm.c index f8d425434..9d8bae824 100644 --- a/src/crypto/gcm.c +++ b/src/crypto/gcm.c @@ -40,6 +40,22 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +/** + * Perform encryption + * + * This value is chosen to allow for ANDing with a fragment length. + */ +#define GCM_FL_ENCRYPT 0x00ff + +/** + * Calculate hash over an initialisation vector value + * + * The hash calculation for a non 96-bit initialisation vector is + * identical to the calculation used for additional data, except that + * the non-additional data length counter is used. + */ +#define GCM_FL_IV 0x0100 + /** * GCM field polynomial * @@ -292,23 +308,18 @@ static void gcm_multiply_key ( const union gcm_block *key, * Encrypt/decrypt/authenticate data * * @v context Context - * @v src Input data, or NULL to process additional data + * @v src Input data * @v dst Output data, or NULL to process additional data - * @v hash Hash input data * @v len Length of data + * @v flags Operation flags */ static void gcm_process ( struct gcm_context *context, const void *src, - void *dst, const void *hash, size_t len ) { + void *dst, size_t len, unsigned int flags ) { union gcm_block tmp; uint64_t *total; size_t frag_len; unsigned int block; - /* Sanity checks */ - assert ( hash != NULL ); - assert ( ( ( src == NULL ) && ( dst == NULL ) ) || - ( ( hash == src ) || ( hash == dst ) ) ); - /* Calculate block number (for debugging) */ block = ( ( ( context->len.len.add + 8 * sizeof ( tmp ) - 1 ) / ( 8 * sizeof ( tmp ) ) ) + @@ -316,17 +327,21 @@ static void gcm_process ( struct gcm_context *context, const void *src, ( 8 * sizeof ( tmp ) ) ) + 1 ); /* Update total length (in bits) */ - total = ( src ? &context->len.len.data : &context->len.len.add ); + total = ( ( dst || ( flags & GCM_FL_IV ) ) ? + &context->len.len.data : &context->len.len.add ); *total += ( len * 8 ); /* Process data */ - for ( ; len ; hash += frag_len, len -= frag_len, block++ ) { + for ( ; len ; src += frag_len, len -= frag_len, block++ ) { /* Calculate fragment length */ frag_len = len; if ( frag_len > sizeof ( tmp ) ) frag_len = sizeof ( tmp ); + /* Update hash with input data */ + gcm_xor ( src, &context->hash, &context->hash, frag_len ); + /* Encrypt/decrypt block, if applicable */ if ( dst ) { @@ -345,12 +360,14 @@ static void gcm_process ( struct gcm_context *context, const void *src, /* Encrypt/decrypt data */ gcm_xor ( src, &tmp, dst, frag_len ); - src += frag_len; dst += frag_len; + + /* Update hash with encrypted data, if applicable */ + gcm_xor ( &tmp, &context->hash, &context->hash, + ( frag_len & flags ) ); } /* Update hash */ - gcm_xor ( hash, &context->hash, &context->hash, frag_len ); gcm_multiply_key ( &context->key, &context->hash ); DBGC2 ( context, "GCM %p X[%d]:\n", context, block ); DBGC2_HDA ( context, 0, &context->hash, @@ -475,7 +492,7 @@ void gcm_setiv ( struct gcm_context *context, const void *iv, size_t ivlen ) { } else { /* Calculate hash over initialisation vector */ - gcm_process ( context, iv, NULL, iv, ivlen ); + gcm_process ( context, iv, NULL, ivlen, GCM_FL_IV ); gcm_hash ( context, &context->ctr ); assert ( context->len.len.add == 0 ); @@ -497,20 +514,9 @@ void gcm_setiv ( struct gcm_context *context, const void *iv, size_t ivlen ) { */ void gcm_encrypt ( struct gcm_context *context, const void *src, void *dst, size_t len ) { - const void *hash; - - /* Determine hash input */ - if ( dst ) { - /* Encrypting: hash the encrypted data */ - hash = dst; - } else { - /* Authenticating: hash the input data */ - hash = src; - src = NULL; - } /* Process data */ - gcm_process ( context, src, dst, hash, len ); + gcm_process ( context, src, dst, len, GCM_FL_ENCRYPT ); } /** @@ -523,15 +529,7 @@ void gcm_encrypt ( struct gcm_context *context, const void *src, void *dst, */ void gcm_decrypt ( struct gcm_context *context, const void *src, void *dst, size_t len ) { - const void *hash; - - /* Determine hash input */ - hash = src; - if ( ! dst ) { - /* Authenticating: only hash */ - src = NULL; - } /* Process data */ - gcm_process ( context, src, dst, hash, len ); + gcm_process ( context, src, dst, len, 0 ); } -- cgit v1.2.3-55-g7522 From 688646fe6d034e98fe7cbcc9403a2d0f70434f40 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 7 Nov 2022 18:09:09 +0000 Subject: [tls] Add GCM cipher suites Signed-off-by: Michael Brown --- src/config/config_crypto.c | 12 +++++++ src/config/crypto.h | 3 ++ src/crypto/mishmash/rsa_aes_cbc_sha1.c | 8 ++--- src/crypto/mishmash/rsa_aes_cbc_sha256.c | 8 ++--- src/crypto/mishmash/rsa_aes_gcm_sha256.c | 60 ++++++++++++++++++++++++++++++++ src/crypto/mishmash/rsa_aes_gcm_sha384.c | 60 ++++++++++++++++++++++++++++++++ src/include/ipxe/tls.h | 4 +++ 7 files changed, 147 insertions(+), 8 deletions(-) create mode 100644 src/crypto/mishmash/rsa_aes_gcm_sha256.c create mode 100644 src/crypto/mishmash/rsa_aes_gcm_sha384.c (limited to 'src/crypto') diff --git a/src/config/config_crypto.c b/src/config/config_crypto.c index 440bf4ce1..fa1996a55 100644 --- a/src/config/config_crypto.c +++ b/src/config/config_crypto.c @@ -124,3 +124,15 @@ REQUIRE_OBJECT ( rsa_aes_cbc_sha1 ); defined ( CRYPTO_DIGEST_SHA256 ) REQUIRE_OBJECT ( rsa_aes_cbc_sha256 ); #endif + +/* RSA, AES-GCM, and SHA-256 */ +#if defined ( CRYPTO_PUBKEY_RSA ) && defined ( CRYPTO_CIPHER_AES_GCM ) && \ + defined ( CRYPTO_DIGEST_SHA256 ) +REQUIRE_OBJECT ( rsa_aes_gcm_sha256 ); +#endif + +/* RSA, AES-GCM, and SHA-384 */ +#if defined ( CRYPTO_PUBKEY_RSA ) && defined ( CRYPTO_CIPHER_AES_GCM ) && \ + defined ( CRYPTO_DIGEST_SHA384 ) +REQUIRE_OBJECT ( rsa_aes_gcm_sha384 ); +#endif diff --git a/src/config/crypto.h b/src/config/crypto.h index 7c0251758..76bf14d41 100644 --- a/src/config/crypto.h +++ b/src/config/crypto.h @@ -18,6 +18,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** AES-CBC block cipher */ #define CRYPTO_CIPHER_AES_CBC +/** AES-GCM block cipher */ +#define CRYPTO_CIPHER_AES_GCM + /** MD4 digest algorithm */ //#define CRYPTO_DIGEST_MD4 diff --git a/src/crypto/mishmash/rsa_aes_cbc_sha1.c b/src/crypto/mishmash/rsa_aes_cbc_sha1.c index c2ecf1205..9f8193de0 100644 --- a/src/crypto/mishmash/rsa_aes_cbc_sha1.c +++ b/src/crypto/mishmash/rsa_aes_cbc_sha1.c @@ -32,7 +32,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** TLS_DHE_RSA_WITH_AES_128_CBC_SHA cipher suite */ struct tls_cipher_suite -tls_dhe_rsa_with_aes_128_cbc_sha __tls_cipher_suite ( 03 ) = { +tls_dhe_rsa_with_aes_128_cbc_sha __tls_cipher_suite ( 05 ) = { .code = htons ( TLS_DHE_RSA_WITH_AES_128_CBC_SHA ), .key_len = ( 128 / 8 ), .fixed_iv_len = 0, @@ -47,7 +47,7 @@ tls_dhe_rsa_with_aes_128_cbc_sha __tls_cipher_suite ( 03 ) = { /** TLS_DHE_RSA_WITH_AES_256_CBC_SHA cipher suite */ struct tls_cipher_suite -tls_dhe_rsa_with_aes_256_cbc_sha __tls_cipher_suite ( 04 ) = { +tls_dhe_rsa_with_aes_256_cbc_sha __tls_cipher_suite ( 06 ) = { .code = htons ( TLS_DHE_RSA_WITH_AES_256_CBC_SHA ), .key_len = ( 256 / 8 ), .fixed_iv_len = 0, @@ -62,7 +62,7 @@ tls_dhe_rsa_with_aes_256_cbc_sha __tls_cipher_suite ( 04 ) = { /** TLS_RSA_WITH_AES_128_CBC_SHA cipher suite */ struct tls_cipher_suite -tls_rsa_with_aes_128_cbc_sha __tls_cipher_suite ( 13 ) = { +tls_rsa_with_aes_128_cbc_sha __tls_cipher_suite ( 15 ) = { .code = htons ( TLS_RSA_WITH_AES_128_CBC_SHA ), .key_len = ( 128 / 8 ), .fixed_iv_len = 0, @@ -77,7 +77,7 @@ tls_rsa_with_aes_128_cbc_sha __tls_cipher_suite ( 13 ) = { /** TLS_RSA_WITH_AES_256_CBC_SHA cipher suite */ struct tls_cipher_suite -tls_rsa_with_aes_256_cbc_sha __tls_cipher_suite ( 14 ) = { +tls_rsa_with_aes_256_cbc_sha __tls_cipher_suite ( 16 ) = { .code = htons ( TLS_RSA_WITH_AES_256_CBC_SHA ), .key_len = ( 256 / 8 ), .fixed_iv_len = 0, diff --git a/src/crypto/mishmash/rsa_aes_cbc_sha256.c b/src/crypto/mishmash/rsa_aes_cbc_sha256.c index 7f114b1fd..d0dc84964 100644 --- a/src/crypto/mishmash/rsa_aes_cbc_sha256.c +++ b/src/crypto/mishmash/rsa_aes_cbc_sha256.c @@ -31,7 +31,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 cipher suite */ struct tls_cipher_suite -tls_dhe_rsa_with_aes_128_cbc_sha256 __tls_cipher_suite ( 01 ) = { +tls_dhe_rsa_with_aes_128_cbc_sha256 __tls_cipher_suite ( 03 ) = { .code = htons ( TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 ), .key_len = ( 128 / 8 ), .fixed_iv_len = 0, @@ -46,7 +46,7 @@ tls_dhe_rsa_with_aes_128_cbc_sha256 __tls_cipher_suite ( 01 ) = { /** TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 cipher suite */ struct tls_cipher_suite -tls_dhe_rsa_with_aes_256_cbc_sha256 __tls_cipher_suite ( 02 ) = { +tls_dhe_rsa_with_aes_256_cbc_sha256 __tls_cipher_suite ( 04 ) = { .code = htons ( TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 ), .key_len = ( 256 / 8 ), .fixed_iv_len = 0, @@ -61,7 +61,7 @@ tls_dhe_rsa_with_aes_256_cbc_sha256 __tls_cipher_suite ( 02 ) = { /** TLS_RSA_WITH_AES_128_CBC_SHA256 cipher suite */ struct tls_cipher_suite -tls_rsa_with_aes_128_cbc_sha256 __tls_cipher_suite ( 11 ) = { +tls_rsa_with_aes_128_cbc_sha256 __tls_cipher_suite ( 13 ) = { .code = htons ( TLS_RSA_WITH_AES_128_CBC_SHA256 ), .key_len = ( 128 / 8 ), .fixed_iv_len = 0, @@ -76,7 +76,7 @@ tls_rsa_with_aes_128_cbc_sha256 __tls_cipher_suite ( 11 ) = { /** TLS_RSA_WITH_AES_256_CBC_SHA256 cipher suite */ struct tls_cipher_suite -tls_rsa_with_aes_256_cbc_sha256 __tls_cipher_suite ( 12 ) = { +tls_rsa_with_aes_256_cbc_sha256 __tls_cipher_suite ( 14 ) = { .code = htons ( TLS_RSA_WITH_AES_256_CBC_SHA256 ), .key_len = ( 256 / 8 ), .fixed_iv_len = 0, diff --git a/src/crypto/mishmash/rsa_aes_gcm_sha256.c b/src/crypto/mishmash/rsa_aes_gcm_sha256.c new file mode 100644 index 000000000..cf9c4c279 --- /dev/null +++ b/src/crypto/mishmash/rsa_aes_gcm_sha256.c @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2022 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include + +/** TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 cipher suite */ +struct tls_cipher_suite +tls_dhe_rsa_with_aes_128_gcm_sha256 __tls_cipher_suite ( 01 ) = { + .code = htons ( TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 ), + .key_len = ( 128 / 8 ), + .fixed_iv_len = 4, + .record_iv_len = 8, + .mac_len = 0, + .exchange = &tls_dhe_exchange_algorithm, + .pubkey = &rsa_algorithm, + .cipher = &aes_gcm_algorithm, + .digest = &sha256_algorithm, + .handshake = &sha256_algorithm, +}; + +/** TLS_RSA_WITH_AES_128_GCM_SHA256 cipher suite */ +struct tls_cipher_suite +tls_rsa_with_aes_128_gcm_sha256 __tls_cipher_suite ( 11 ) = { + .code = htons ( TLS_RSA_WITH_AES_128_GCM_SHA256 ), + .key_len = ( 128 / 8 ), + .fixed_iv_len = 4, + .record_iv_len = 8, + .mac_len = 0, + .exchange = &tls_pubkey_exchange_algorithm, + .pubkey = &rsa_algorithm, + .cipher = &aes_gcm_algorithm, + .digest = &sha256_algorithm, + .handshake = &sha256_algorithm, +}; diff --git a/src/crypto/mishmash/rsa_aes_gcm_sha384.c b/src/crypto/mishmash/rsa_aes_gcm_sha384.c new file mode 100644 index 000000000..10a977f7f --- /dev/null +++ b/src/crypto/mishmash/rsa_aes_gcm_sha384.c @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2022 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include + +/** TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 cipher suite */ +struct tls_cipher_suite +tls_dhe_rsa_with_aes_256_gcm_sha384 __tls_cipher_suite ( 02 ) = { + .code = htons ( TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 ), + .key_len = ( 256 / 8 ), + .fixed_iv_len = 4, + .record_iv_len = 8, + .mac_len = 0, + .exchange = &tls_dhe_exchange_algorithm, + .pubkey = &rsa_algorithm, + .cipher = &aes_gcm_algorithm, + .digest = &sha384_algorithm, + .handshake = &sha384_algorithm, +}; + +/** TLS_RSA_WITH_AES_256_GCM_SHA384 cipher suite */ +struct tls_cipher_suite +tls_rsa_with_aes_256_gcm_sha384 __tls_cipher_suite ( 12 ) = { + .code = htons ( TLS_RSA_WITH_AES_256_GCM_SHA384 ), + .key_len = ( 256 / 8 ), + .fixed_iv_len = 4, + .record_iv_len = 8, + .mac_len = 0, + .exchange = &tls_pubkey_exchange_algorithm, + .pubkey = &rsa_algorithm, + .cipher = &aes_gcm_algorithm, + .digest = &sha384_algorithm, + .handshake = &sha384_algorithm, +}; diff --git a/src/include/ipxe/tls.h b/src/include/ipxe/tls.h index 355814132..6fcb69bef 100644 --- a/src/include/ipxe/tls.h +++ b/src/include/ipxe/tls.h @@ -89,6 +89,10 @@ struct tls_header { #define TLS_RSA_WITH_AES_256_CBC_SHA256 0x003d #define TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 0x0067 #define TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 0x006b +#define TLS_RSA_WITH_AES_128_GCM_SHA256 0x009c +#define TLS_RSA_WITH_AES_256_GCM_SHA384 0x009d +#define TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 0x009e +#define TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 0x009f /* TLS hash algorithm identifiers */ #define TLS_MD5_ALGORITHM 1 -- cgit v1.2.3-55-g7522 From 9f17d1116d27696ec76c48c5c77df34cba521380 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 17 Feb 2023 16:56:11 +0000 Subject: [rng] Allow entropy source to be selected at runtime As noted in commit 3c83843 ("[rng] Check for several functioning RTC interrupts"), experimentation shows that Hyper-V cannot be trusted to reliably generate RTC interrupts. (As noted in commit f3ba0fb ("[hyperv] Provide timer based on the 10MHz time reference count MSR"), Hyper-V appears to suffer from a general problem in reliably generating any legacy interrupts.) An alternative entropy source is therefore required for an image that may be used in a Hyper-V Gen1 virtual machine. The x86 RDRAND instruction provides a suitable alternative entropy source, but may not be supported by all CPUs. We must therefore allow for multiple entropy sources to be compiled in, with the single active entropy source selected only at runtime. Restructure the internal entropy API to allow a working entropy source to be detected and chosen at runtime. Enable the RDRAND entropy source for all x86 builds, since it is likely to be substantially faster than any other source. Signed-off-by: Michael Brown --- src/arch/arm/include/bits/entropy.h | 12 - src/arch/loong64/include/bits/entropy.h | 12 - src/arch/x86/core/rdrand.c | 32 ++- src/arch/x86/include/bits/entropy.h | 15 - src/arch/x86/include/ipxe/rdrand.h | 37 --- src/arch/x86/include/ipxe/rtc_entropy.h | 62 ----- src/arch/x86/interface/pcbios/rtc_entropy.c | 38 ++- src/config/config_entropy.c | 48 ++++ src/config/defaults/efi.h | 1 + src/config/defaults/linux.h | 4 + src/config/defaults/pcbios.h | 1 + src/crypto/entropy.c | 283 +++++++------------ src/crypto/null_entropy.c | 40 --- src/include/ipxe/efi/efi_entropy.h | 35 --- src/include/ipxe/entropy.h | 414 +++++++++++++++++++++------- src/include/ipxe/linux/linux_entropy.h | 34 --- src/include/ipxe/null_entropy.h | 52 ---- src/interface/efi/efi_entropy.c | 19 +- src/interface/linux/linux_entropy.c | 20 +- 19 files changed, 540 insertions(+), 619 deletions(-) delete mode 100644 src/arch/arm/include/bits/entropy.h delete mode 100644 src/arch/loong64/include/bits/entropy.h delete mode 100644 src/arch/x86/include/bits/entropy.h delete mode 100644 src/arch/x86/include/ipxe/rdrand.h delete mode 100644 src/arch/x86/include/ipxe/rtc_entropy.h create mode 100644 src/config/config_entropy.c delete mode 100644 src/crypto/null_entropy.c delete mode 100644 src/include/ipxe/efi/efi_entropy.h delete mode 100644 src/include/ipxe/linux/linux_entropy.h delete mode 100644 src/include/ipxe/null_entropy.h (limited to 'src/crypto') diff --git a/src/arch/arm/include/bits/entropy.h b/src/arch/arm/include/bits/entropy.h deleted file mode 100644 index 75fdc90ea..000000000 --- a/src/arch/arm/include/bits/entropy.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef _BITS_ENTROPY_H -#define _BITS_ENTROPY_H - -/** @file - * - * ARM-specific entropy API implementations - * - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#endif /* _BITS_ENTROPY_H */ diff --git a/src/arch/loong64/include/bits/entropy.h b/src/arch/loong64/include/bits/entropy.h deleted file mode 100644 index 8d3726930..000000000 --- a/src/arch/loong64/include/bits/entropy.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef _BITS_ENTROPY_H -#define _BITS_ENTROPY_H - -/** @file - * - * LoongArch64-specific entropy API implementations - * - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#endif /* _BITS_ENTROPY_H */ diff --git a/src/arch/x86/core/rdrand.c b/src/arch/x86/core/rdrand.c index 29605ab2f..850ab1f11 100644 --- a/src/arch/x86/core/rdrand.c +++ b/src/arch/x86/core/rdrand.c @@ -32,12 +32,15 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include + +struct entropy_source rdrand_entropy __entropy_source ( ENTROPY_PREFERRED ); /** Number of times to retry RDRAND instruction */ #define RDRAND_RETRY_COUNT 16 /** Colour for debug messages */ -#define colour CPUID_FEATURES_INTEL_ECX_RDRAND +#define colour &rdrand_entropy /** * Enable entropy gathering @@ -54,16 +57,15 @@ static int rdrand_entropy_enable ( void ) { return -ENOTSUP; } - return 0; -} - -/** - * Disable entropy gathering - * - */ -static void rdrand_entropy_disable ( void ) { + /* Data returned by RDRAND is theoretically full entropy, up + * to a security strength of 128 bits, so assume that each + * sample contains exactly 8 bits of entropy. + */ + if ( DRBG_SECURITY_STRENGTH > 128 ) + return -ENOTSUP; + entropy_init ( &rdrand_entropy, MIN_ENTROPY ( 8.0 ) ); - /* Nothing to do */ + return 0; } /** @@ -93,7 +95,9 @@ static int rdrand_get_noise ( noise_sample_t *noise ) { return 0; } -PROVIDE_ENTROPY_INLINE ( rdrand, min_entropy_per_sample ); -PROVIDE_ENTROPY ( rdrand, entropy_enable, rdrand_entropy_enable ); -PROVIDE_ENTROPY ( rdrand, entropy_disable, rdrand_entropy_disable ); -PROVIDE_ENTROPY ( rdrand, get_noise, rdrand_get_noise ); +/** Hardware random number generator entropy source */ +struct entropy_source rdrand_entropy __entropy_source ( ENTROPY_PREFERRED ) = { + .name = "rdrand", + .enable = rdrand_entropy_enable, + .get_noise = rdrand_get_noise, +}; diff --git a/src/arch/x86/include/bits/entropy.h b/src/arch/x86/include/bits/entropy.h deleted file mode 100644 index 7accea33f..000000000 --- a/src/arch/x86/include/bits/entropy.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef _BITS_ENTROPY_H -#define _BITS_ENTROPY_H - -/** @file - * - * x86-specific entropy API implementations - * - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include -#include - -#endif /* _BITS_ENTROPY_H */ diff --git a/src/arch/x86/include/ipxe/rdrand.h b/src/arch/x86/include/ipxe/rdrand.h deleted file mode 100644 index c9c170fb0..000000000 --- a/src/arch/x86/include/ipxe/rdrand.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef _IPXE_RDRAND_H -#define _IPXE_RDRAND_H - -/** @file - * - * Hardware random number generator - * - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include -#include - -#ifdef ENTROPY_RDRAND -#define ENTROPY_PREFIX_rdrand -#else -#define ENTROPY_PREFIX_rdrand __rdrand_ -#endif - -/** - * min-entropy per sample - * - * @ret min_entropy min-entropy of each sample - */ -static inline __always_inline min_entropy_t -ENTROPY_INLINE ( rdrand, min_entropy_per_sample ) ( void ) { - - /* Data returned by RDRAND is theoretically full entropy, up - * to a security strength of 128 bits. - */ - if ( DRBG_SECURITY_STRENGTH > 128 ) - return 0; - return MIN_ENTROPY ( 8 * sizeof ( noise_sample_t ) ); -} - -#endif /* _IPXE_RDRAND_H */ diff --git a/src/arch/x86/include/ipxe/rtc_entropy.h b/src/arch/x86/include/ipxe/rtc_entropy.h deleted file mode 100644 index 581abcd3e..000000000 --- a/src/arch/x86/include/ipxe/rtc_entropy.h +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef _IPXE_RTC_ENTROPY_H -#define _IPXE_RTC_ENTROPY_H - -/** @file - * - * RTC-based entropy source - * - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include - -#ifdef ENTROPY_RTC -#define ENTROPY_PREFIX_rtc -#else -#define ENTROPY_PREFIX_rtc __rtc_ -#endif - -/** - * min-entropy per sample - * - * @ret min_entropy min-entropy of each sample - */ -static inline __always_inline min_entropy_t -ENTROPY_INLINE ( rtc, min_entropy_per_sample ) ( void ) { - - /* The min-entropy has been measured on several platforms - * using the entropy_sample test code. Modelling the samples - * as independent, and using a confidence level of 99.99%, the - * measurements were as follows: - * - * qemu-kvm : 7.38 bits - * VMware : 7.46 bits - * Physical hardware : 2.67 bits - * - * We choose the lowest of these (2.67 bits) and apply a 50% - * safety margin to allow for some potential non-independence - * of samples. - */ - return MIN_ENTROPY ( 1.3 ); -} - -extern uint8_t rtc_sample ( void ); - -/** - * Get noise sample - * - * @ret noise Noise sample - * @ret rc Return status code - */ -static inline __always_inline int -ENTROPY_INLINE ( rtc, get_noise ) ( noise_sample_t *noise ) { - - /* Get sample */ - *noise = rtc_sample(); - - /* Always successful */ - return 0; -} - -#endif /* _IPXE_RTC_ENTROPY_H */ diff --git a/src/arch/x86/interface/pcbios/rtc_entropy.c b/src/arch/x86/interface/pcbios/rtc_entropy.c index c400d8a78..8f47ff6b8 100644 --- a/src/arch/x86/interface/pcbios/rtc_entropy.c +++ b/src/arch/x86/interface/pcbios/rtc_entropy.c @@ -39,6 +39,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +struct entropy_source rtc_entropy __entropy_source ( ENTROPY_NORMAL ); + /** Maximum time to wait for an RTC interrupt, in milliseconds */ #define RTC_MAX_WAIT_MS 100 @@ -203,6 +205,21 @@ static int rtc_entropy_enable ( void ) { if ( ( rc = rtc_entropy_check() ) != 0 ) goto err_check; + /* The min-entropy has been measured on several platforms + * using the entropy_sample test code. Modelling the samples + * as independent, and using a confidence level of 99.99%, the + * measurements were as follows: + * + * qemu-kvm : 7.38 bits + * VMware : 7.46 bits + * Physical hardware : 2.67 bits + * + * We choose the lowest of these (2.67 bits) and apply a 50% + * safety margin to allow for some potential non-independence + * of samples. + */ + entropy_init ( &rtc_entropy, MIN_ENTROPY ( 1.3 ) ); + return 0; err_check: @@ -226,11 +243,12 @@ static void rtc_entropy_disable ( void ) { } /** - * Measure a single RTC tick + * Get noise sample * - * @ret delta Length of RTC tick (in TSC units) + * @ret noise Noise sample + * @ret rc Return status code */ -uint8_t rtc_sample ( void ) { +static int rtc_get_noise ( noise_sample_t *noise ) { uint32_t before; uint32_t after; uint32_t temp; @@ -265,10 +283,14 @@ uint8_t rtc_sample ( void ) { : "=a" ( after ), "=d" ( before ), "=Q" ( temp ) : "2" ( 0 ) ); - return ( after - before ); + *noise = ( after - before ); + return 0; } -PROVIDE_ENTROPY_INLINE ( rtc, min_entropy_per_sample ); -PROVIDE_ENTROPY ( rtc, entropy_enable, rtc_entropy_enable ); -PROVIDE_ENTROPY ( rtc, entropy_disable, rtc_entropy_disable ); -PROVIDE_ENTROPY_INLINE ( rtc, get_noise ); +/** RTC entropy source */ +struct entropy_source rtc_entropy __entropy_source ( ENTROPY_NORMAL ) = { + .name = "rtc", + .enable = rtc_entropy_enable, + .disable = rtc_entropy_disable, + .get_noise = rtc_get_noise, +}; diff --git a/src/config/config_entropy.c b/src/config/config_entropy.c new file mode 100644 index 000000000..e96019a58 --- /dev/null +++ b/src/config/config_entropy.c @@ -0,0 +1,48 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** @file + * + * Entropy configuration options + * + */ + +PROVIDE_REQUIRING_SYMBOL(); + +/* + * Drag in entropy sources + */ +#ifdef ENTROPY_RTC +REQUIRE_OBJECT ( rtc_entropy ); +#endif +#ifdef ENTROPY_EFI +REQUIRE_OBJECT ( efi_entropy ); +#endif +#ifdef ENTROPY_LINUX +REQUIRE_OBJECT ( linux_entropy ); +#endif +#ifdef ENTROPY_RDRAND +REQUIRE_OBJECT ( rdrand ); +#endif diff --git a/src/config/defaults/efi.h b/src/config/defaults/efi.h index 625ae055c..16c561660 100644 --- a/src/config/defaults/efi.h +++ b/src/config/defaults/efi.h @@ -50,6 +50,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #if defined ( __i386__ ) || defined ( __x86_64__ ) #define IOAPI_X86 #define NAP_EFIX86 +#define ENTROPY_RDRAND #define CPUID_CMD /* x86 CPU feature detection command */ #define UNSAFE_STD /* Avoid setting direction flag */ #endif diff --git a/src/config/defaults/linux.h b/src/config/defaults/linux.h index 5c4106d30..21de2a2e2 100644 --- a/src/config/defaults/linux.h +++ b/src/config/defaults/linux.h @@ -33,4 +33,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define SANBOOT_PROTO_FCP #define SANBOOT_PROTO_HTTP +#if defined ( __i386__ ) || defined ( __x86_64__ ) +#define ENTROPY_RDRAND +#endif + #endif /* CONFIG_DEFAULTS_LINUX_H */ diff --git a/src/config/defaults/pcbios.h b/src/config/defaults/pcbios.h index 83835805a..ee342d41b 100644 --- a/src/config/defaults/pcbios.h +++ b/src/config/defaults/pcbios.h @@ -20,6 +20,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define SMBIOS_PCBIOS #define SANBOOT_PCBIOS #define ENTROPY_RTC +#define ENTROPY_RDRAND #define TIME_RTC #define REBOOT_PCBIOS #define ACPI_RSDP diff --git a/src/crypto/entropy.c b/src/crypto/entropy.c index ced6fd921..204e6bb1e 100644 --- a/src/crypto/entropy.c +++ b/src/crypto/entropy.c @@ -50,46 +50,61 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define EINFO_EPIPE_ADAPTIVE_PROPORTION_TEST \ __einfo_uniqify ( EINFO_EPIPE, 0x02, "Adaptive proportion test failed" ) +/** Current entropy source */ +static struct entropy_source *entropy_source; + /** - * Calculate cutoff value for the repetition count test - * - * @ret cutoff Cutoff value + * Enable entropy gathering * - * This is the cutoff value for the Repetition Count Test defined in - * ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.2. + * @ret rc Return status code */ -static inline __attribute__ (( always_inline )) unsigned int -repetition_count_cutoff ( void ) { - double max_repetitions; - unsigned int cutoff; +int entropy_enable ( void ) { + int rc; - /* The cutoff formula for the repetition test is: - * - * C = ( 1 + ( -log2(W) / H_min ) ) - * - * where W is set at 2^(-30) (in ANS X9.82 Part 2 (October - * 2011 Draft) Section 8.5.2.1.3.1). - */ - max_repetitions = ( 1 + ( MIN_ENTROPY ( 30 ) / - min_entropy_per_sample() ) ); + /* Enable selected source, if applicable */ + if ( entropy_source ) { - /* Round up to a whole number of repetitions. We don't have - * the ceil() function available, so do the rounding by hand. - */ - cutoff = max_repetitions; - if ( cutoff < max_repetitions ) - cutoff++; - linker_assert ( ( cutoff >= max_repetitions ), rounding_error ); - - /* Floating-point operations are not allowed in iPXE since we - * never set up a suitable environment. Abort the build - * unless the calculated number of repetitions is a - * compile-time constant. - */ - linker_assert ( __builtin_constant_p ( cutoff ), - repetition_count_cutoff_not_constant ); + /* Enable entropy source */ + if ( ( rc = entropy_source->enable() ) != 0 ) { + DBGC ( &entropy_source, "ENTROPY could not enable " + "source \"%s\": %s\n", entropy_source->name, + strerror ( rc ) ); + return rc; + } - return cutoff; + /* Sanity checks */ + assert ( entropy_source->min_entropy_per_sample > 0 ); + assert ( entropy_source->repetition_count_cutoff > 0 ); + assert ( entropy_source->adaptive_proportion_cutoff > 0 ); + assert ( entropy_source->startup_test_count > 0 ); + + return 0; + } + + /* Find the first working source */ + rc = -ENOENT; + for_each_table_entry ( entropy_source, ENTROPY_SOURCES ) { + if ( ( rc = entropy_enable() ) == 0 ) { + DBGC ( &entropy_source, "ENTROPY using source \"%s\"\n", + entropy_source->name ); + break; + } + } + return rc; +} + +/** + * Disable entropy gathering + * + */ +void entropy_disable ( void ) { + + /* Sanity check */ + assert ( entropy_source != NULL ); + + /* Disable entropy gathering, if applicable */ + if ( entropy_source->disable ) + entropy_source->disable(); } /** @@ -104,6 +119,8 @@ repetition_count_cutoff ( void ) { static int repetition_count_test ( noise_sample_t sample ) { static noise_sample_t most_recent_sample; static unsigned int repetition_count = 0; + unsigned int repetition_count_cutoff = + entropy_source->repetition_count_cutoff; /* A = the most recently seen sample value * B = the number of times that value A has been seen in a row @@ -124,7 +141,7 @@ static int repetition_count_test ( noise_sample_t sample ) { /* i. If B >= C, then an error condition is raised * due to a failure of the test */ - if ( repetition_count >= repetition_count_cutoff() ) + if ( repetition_count >= repetition_count_cutoff ) return -EPIPE_REPETITION_COUNT_TEST; } else { @@ -141,117 +158,6 @@ static int repetition_count_test ( noise_sample_t sample ) { return 0; } -/** - * Window size for the adaptive proportion test - * - * ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.3.1.1 allows - * five possible window sizes: 16, 64, 256, 4096 and 65536. - * - * We expect to generate relatively few (<256) entropy samples during - * a typical iPXE run; the use of a large window size would mean that - * the test would never complete a single cycle. We use a window size - * of 64, which is the smallest window size that permits values of - * H_min down to one bit per sample. - */ -#define ADAPTIVE_PROPORTION_WINDOW_SIZE 64 - -/** - * Combine adaptive proportion test window size and min-entropy - * - * @v n N (window size) - * @v h H (min-entropy) - * @ret n_h (N,H) combined value - */ -#define APC_N_H( n, h ) ( ( (n) << 8 ) | (h) ) - -/** - * Define a row of the adaptive proportion cutoff table - * - * @v h H (min-entropy) - * @v c16 Cutoff for N=16 - * @v c64 Cutoff for N=64 - * @v c256 Cutoff for N=256 - * @v c4096 Cutoff for N=4096 - * @v c65536 Cutoff for N=65536 - */ -#define APC_TABLE_ROW( h, c16, c64, c256, c4096, c65536) \ - case APC_N_H ( 16, h ) : return c16; \ - case APC_N_H ( 64, h ) : return c64; \ - case APC_N_H ( 256, h ) : return c256; \ - case APC_N_H ( 4096, h ) : return c4096; \ - case APC_N_H ( 65536, h ) : return c65536; - -/** Value used to represent "N/A" in adaptive proportion cutoff table */ -#define APC_NA 0 - -/** - * Look up value in adaptive proportion test cutoff table - * - * @v n N (window size) - * @v h H (min-entropy) - * @ret cutoff Cutoff - * - * This is the table of cutoff values defined in ANS X9.82 Part 2 - * (October 2011 Draft) Section 8.5.2.1.3.1.2. - */ -static inline __attribute__ (( always_inline )) unsigned int -adaptive_proportion_cutoff_lookup ( unsigned int n, unsigned int h ) { - switch ( APC_N_H ( n, h ) ) { - APC_TABLE_ROW ( 1, APC_NA, 51, 168, 2240, 33537 ); - APC_TABLE_ROW ( 2, APC_NA, 35, 100, 1193, 17053 ); - APC_TABLE_ROW ( 3, 10, 24, 61, 643, 8705 ); - APC_TABLE_ROW ( 4, 8, 16, 38, 354, 4473 ); - APC_TABLE_ROW ( 5, 6, 12, 25, 200, 2321 ); - APC_TABLE_ROW ( 6, 5, 9, 17, 117, 1220 ); - APC_TABLE_ROW ( 7, 4, 7, 15, 71, 653 ); - APC_TABLE_ROW ( 8, 4, 5, 9, 45, 358 ); - APC_TABLE_ROW ( 9, 3, 4, 7, 30, 202 ); - APC_TABLE_ROW ( 10, 3, 4, 5, 21, 118 ); - APC_TABLE_ROW ( 11, 2, 3, 4, 15, 71 ); - APC_TABLE_ROW ( 12, 2, 3, 4, 11, 45 ); - APC_TABLE_ROW ( 13, 2, 2, 3, 9, 30 ); - APC_TABLE_ROW ( 14, 2, 2, 3, 7, 21 ); - APC_TABLE_ROW ( 15, 1, 2, 2, 6, 15 ); - APC_TABLE_ROW ( 16, 1, 2, 2, 5, 11 ); - APC_TABLE_ROW ( 17, 1, 1, 2, 4, 9 ); - APC_TABLE_ROW ( 18, 1, 1, 2, 4, 7 ); - APC_TABLE_ROW ( 19, 1, 1, 1, 3, 6 ); - APC_TABLE_ROW ( 20, 1, 1, 1, 3, 5 ); - default: - return APC_NA; - } -} - -/** - * Calculate cutoff value for the adaptive proportion test - * - * @ret cutoff Cutoff value - * - * This is the cutoff value for the Adaptive Proportion Test defined - * in ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.3.1.2. - */ -static inline __attribute__ (( always_inline )) unsigned int -adaptive_proportion_cutoff ( void ) { - unsigned int h; - unsigned int n; - unsigned int cutoff; - - /* Look up cutoff value in cutoff table */ - n = ADAPTIVE_PROPORTION_WINDOW_SIZE; - h = ( min_entropy_per_sample() / MIN_ENTROPY_SCALE ); - cutoff = adaptive_proportion_cutoff_lookup ( n, h ); - - /* Fail unless cutoff value is a build-time constant */ - linker_assert ( __builtin_constant_p ( cutoff ), - adaptive_proportion_cutoff_not_constant ); - - /* Fail if cutoff value is N/A */ - linker_assert ( ( cutoff != APC_NA ), - adaptive_proportion_cutoff_not_applicable ); - - return cutoff; -} - /** * Perform adaptive proportion test * @@ -265,6 +171,8 @@ static int adaptive_proportion_test ( noise_sample_t sample ) { static noise_sample_t current_counted_sample; static unsigned int sample_count = ADAPTIVE_PROPORTION_WINDOW_SIZE; static unsigned int repetition_count; + unsigned int adaptive_proportion_cutoff = + entropy_source->adaptive_proportion_cutoff; /* A = the sample value currently being counted * B = the number of samples examined in this run of the test so far @@ -312,7 +220,7 @@ static int adaptive_proportion_test ( noise_sample_t sample ) { * condition, because the test has * detected a failure */ - if ( repetition_count > adaptive_proportion_cutoff() ) + if ( repetition_count > adaptive_proportion_cutoff ) return -EPIPE_ADAPTIVE_PROPORTION_TEST; } @@ -321,6 +229,23 @@ static int adaptive_proportion_test ( noise_sample_t sample ) { return 0; } +/** + * Get noise sample + * + * @ret noise Noise sample + * @ret rc Return status code + * + * This is the GetNoise function defined in ANS X9.82 Part 2 + * (October 2011 Draft) Section 6.5.2. + */ +int get_noise ( noise_sample_t *noise ) { + + /* Sanity check */ + assert ( entropy_source != NULL ); + + return entropy_source->get_noise ( noise ); +} + /** * Get entropy sample * @@ -334,6 +259,9 @@ static int get_entropy ( entropy_sample_t *entropy ) { static int rc = 0; noise_sample_t noise; + /* Sanity check */ + assert ( entropy_source != NULL ); + /* Any failure is permanent */ if ( rc != 0 ) return rc; @@ -357,31 +285,6 @@ static int get_entropy ( entropy_sample_t *entropy ) { return 0; } -/** - * Calculate number of samples required for startup tests - * - * @ret num_samples Number of samples required - * - * ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.5 requires - * that at least one full cycle of the continuous tests must be - * performed at start-up. - */ -static inline __attribute__ (( always_inline )) unsigned int -startup_test_count ( void ) { - unsigned int num_samples; - - /* At least max(N,C) samples shall be generated by the noise - * source for start-up testing. - */ - num_samples = repetition_count_cutoff(); - if ( num_samples < adaptive_proportion_cutoff() ) - num_samples = adaptive_proportion_cutoff(); - linker_assert ( __builtin_constant_p ( num_samples ), - startup_test_count_not_constant ); - - return num_samples; -} - /** * Create next nonce value * @@ -402,7 +305,7 @@ static uint32_t make_next_nonce ( void ) { /** * Obtain entropy input temporary buffer * - * @v num_samples Number of entropy samples + * @v min_entropy Min-entropy required * @v tmp Temporary buffer * @v tmp_len Length of temporary buffer * @ret rc Return status code @@ -412,11 +315,8 @@ static uint32_t make_next_nonce ( void ) { * and condensing each entropy source output after each GetEntropy * call) as defined in ANS X9.82 Part 4 (April 2011 Draft) Section * 13.3.4.2. - * - * To minimise code size, the number of samples required is calculated - * at compilation time. */ -int get_entropy_input_tmp ( unsigned int num_samples, uint8_t *tmp, +int get_entropy_input_tmp ( min_entropy_t min_entropy, uint8_t *tmp, size_t tmp_len ) { static unsigned int startup_tested = 0; struct { @@ -424,6 +324,8 @@ int get_entropy_input_tmp ( unsigned int num_samples, uint8_t *tmp, entropy_sample_t sample; } __attribute__ (( packed )) data;; uint8_t df_buf[tmp_len]; + min_entropy_t entropy_total; + unsigned int num_samples; unsigned int i; int rc; @@ -432,22 +334,20 @@ int get_entropy_input_tmp ( unsigned int num_samples, uint8_t *tmp, return rc; /* Perform mandatory startup tests, if not yet performed */ - for ( ; startup_tested < startup_test_count() ; startup_tested++ ) { + for ( ; startup_tested < entropy_source->startup_test_count ; + startup_tested++ ) { if ( ( rc = get_entropy ( &data.sample ) ) != 0 ) goto err_get_entropy; } - /* 3. entropy_total = 0 - * - * (Nothing to do; the number of entropy samples required has - * already been precalculated.) - */ + /* 3. entropy_total = 0 */ + entropy_total = MIN_ENTROPY ( 0 ); /* 4. tmp = a fixed n-bit value, such as 0^n */ memset ( tmp, 0, tmp_len ); /* 5. While ( entropy_total < min_entropy ) */ - while ( num_samples-- ) { + for ( num_samples = 0 ; entropy_total < min_entropy ; num_samples++ ) { /* 5.1. ( status, entropy_bitstring, assessed_entropy ) * = GetEntropy() * 5.2. If status indicates an error, return ( status, Null ) @@ -466,19 +366,24 @@ int get_entropy_input_tmp ( unsigned int num_samples, uint8_t *tmp, for ( i = 0 ; i < tmp_len ; i++ ) tmp[i] ^= df_buf[i]; - /* 5.5. entropy_total = entropy_total + assessed_entropy - * - * (Nothing to do; the number of entropy samples - * required has already been precalculated.) - */ + /* 5.5. entropy_total = entropy_total + assessed_entropy */ + entropy_total += entropy_source->min_entropy_per_sample; } /* Disable entropy gathering */ entropy_disable(); + DBGC ( &entropy_source, "ENTROPY gathered %d bits in %d samples\n", + ( min_entropy / MIN_ENTROPY_SCALE ), num_samples ); return 0; err_get_entropy: entropy_disable(); return rc; } + +/* Drag in objects via entropy_enable */ +REQUIRING_SYMBOL ( entropy_enable ); + +/* Drag in entropy configuration */ +REQUIRE_OBJECT ( config_entropy ); diff --git a/src/crypto/null_entropy.c b/src/crypto/null_entropy.c deleted file mode 100644 index d1e1a6f73..000000000 --- a/src/crypto/null_entropy.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2012 Michael Brown . - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * - * You can also choose to distribute this program under the terms of - * the Unmodified Binary Distribution Licence (as given in the file - * COPYING.UBDL), provided that you have satisfied its requirements. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -/** @file - * - * Nonexistent entropy source - * - * - * This source provides no entropy and must NOT be used in a - * security-sensitive environment. - */ - -#include - -PROVIDE_ENTROPY_INLINE ( null, min_entropy_per_sample ); -PROVIDE_ENTROPY_INLINE ( null, entropy_enable ); -PROVIDE_ENTROPY_INLINE ( null, entropy_disable ); -PROVIDE_ENTROPY_INLINE ( null, get_noise ); diff --git a/src/include/ipxe/efi/efi_entropy.h b/src/include/ipxe/efi/efi_entropy.h deleted file mode 100644 index 5b16fd7f9..000000000 --- a/src/include/ipxe/efi/efi_entropy.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef _IPXE_EFI_ENTROPY_H -#define _IPXE_EFI_ENTROPY_H - -/** @file - * - * EFI entropy source - * - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include - -#ifdef ENTROPY_EFI -#define ENTROPY_PREFIX_efi -#else -#define ENTROPY_PREFIX_efi __efi_ -#endif - -/** - * min-entropy per sample - * - * @ret min_entropy min-entropy of each sample - */ -static inline __always_inline min_entropy_t -ENTROPY_INLINE ( efi, min_entropy_per_sample ) ( void ) { - - /* We use essentially the same mechanism as for the BIOS - * RTC-based entropy source, and so assume the same - * min-entropy per sample. - */ - return MIN_ENTROPY ( 1.3 ); -} - -#endif /* _IPXE_EFI_ENTROPY_H */ diff --git a/src/include/ipxe/entropy.h b/src/include/ipxe/entropy.h index d2e3ce501..108c37669 100644 --- a/src/include/ipxe/entropy.h +++ b/src/include/ipxe/entropy.h @@ -12,40 +12,11 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include -#include #include #include +#include #include -/** - * Calculate static inline entropy API function name - * - * @v _prefix Subsystem prefix - * @v _api_func API function - * @ret _subsys_func Subsystem API function - */ -#define ENTROPY_INLINE( _subsys, _api_func ) \ - SINGLE_API_INLINE ( ENTROPY_PREFIX_ ## _subsys, _api_func ) - -/** - * Provide a entropy API implementation - * - * @v _prefix Subsystem prefix - * @v _api_func API function - * @v _func Implementing function - */ -#define PROVIDE_ENTROPY( _subsys, _api_func, _func ) \ - PROVIDE_SINGLE_API ( ENTROPY_PREFIX_ ## _subsys, _api_func, _func ) - -/** - * Provide a static inline entropy API implementation - * - * @v _prefix Subsystem prefix - * @v _api_func API function - */ -#define PROVIDE_ENTROPY_INLINE( _subsys, _api_func ) \ - PROVIDE_SINGLE_API_INLINE ( ENTROPY_PREFIX_ ## _subsys, _api_func ) - /** A noise sample */ typedef uint8_t noise_sample_t; @@ -71,56 +42,93 @@ typedef unsigned int min_entropy_t; #define MIN_ENTROPY( bits ) \ ( ( min_entropy_t ) ( (bits) * MIN_ENTROPY_SCALE ) ) -/* Include all architecture-independent entropy API headers */ -#include -#include -#include +/** An entropy source */ +struct entropy_source { + /** Name */ + const char *name; + /** + * min-entropy per sample + * + * min-entropy is defined in ANS X9.82 Part 1-2006 Section 8.3 and in + * NIST SP 800-90 Appendix C.3 as + * + * H_min = -log2 ( p_max ) + * + * where p_max is the probability of the most likely sample value. + * + * Filled in by entropy_init(). + */ + min_entropy_t min_entropy_per_sample; + /** + * Repetition count test cutoff value + * + * This is the cutoff value for the Repetition Count Test + * defined in ANS X9.82 Part 2 (October 2011 Draft) Section + * 8.5.2.1.2. + * + * Filled in by entropy_init(). + */ + unsigned int repetition_count_cutoff; + /** + * Adaptive proportion test cutoff value + * + * This is the cutoff value for the Adaptive Proportion Test + * defined in ANS X9.82 Part 2 (October 2011 Draft) Section + * 8.5.2.1.3.1.2. + * + * Filled in by entropy_init(). + */ + unsigned int adaptive_proportion_cutoff; + /** + * Startup test count + * + * ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.5 + * requires that at least one full cycle of the continuous + * tests must be performed at start-up. + */ + unsigned int startup_test_count; + /** + * Enable entropy gathering + * + * @ret rc Return status code + */ + int ( * enable ) ( void ); + /** + * Disable entropy gathering + * + */ + void ( * disable ) ( void ); + /** + * Get noise sample + * + * @ret noise Noise sample + * @ret rc Return status code + * + * This is the GetNoise function defined in ANS X9.82 Part 2 + * (October 2011 Draft) Section 6.5.2. + */ + int ( * get_noise ) ( noise_sample_t *noise ); +}; -/* Include all architecture-dependent entropy API headers */ -#include +/** Entropy source table */ +#define ENTROPY_SOURCES __table ( struct entropy_source, "entropy_sources" ) -/** - * Enable entropy gathering - * - * @ret rc Return status code - */ -int entropy_enable ( void ); +/** Declare an entropy source */ +#define __entropy_source( order ) __table_entry ( ENTROPY_SOURCES, order ) -/** - * Disable entropy gathering +/** @defgroup entropy_source_order Entropy source order * + * @{ */ -void entropy_disable ( void ); -/** - * min-entropy per sample - * - * @ret min_entropy min-entropy of each sample - * - * min-entropy is defined in ANS X9.82 Part 1-2006 Section 8.3 and in - * NIST SP 800-90 Appendix C.3 as - * - * H_min = -log2 ( p_max ) - * - * where p_max is the probability of the most likely sample value. - * - * This must be a compile-time constant. - */ -min_entropy_t min_entropy_per_sample ( void ); +#define ENTROPY_PREFERRED 01 /**< Preferred entropy source */ +#define ENTROPY_NORMAL 02 /**< Normal entropy source */ +#define ENTROPY_FALLBACK 03 /**< Fallback entropy source */ -/** - * Get noise sample - * - * @ret noise Noise sample - * @ret rc Return status code - * - * This is the GetNoise function defined in ANS X9.82 Part 2 - * (October 2011 Draft) Section 6.5.2. - */ -int get_noise ( noise_sample_t *noise ); +/** @} */ -extern int get_entropy_input_tmp ( unsigned int num_samples, - uint8_t *tmp, size_t tmp_len ); +extern int get_entropy_input_tmp ( min_entropy_t min_entropy, uint8_t *tmp, + size_t tmp_len ); /** Use SHA-256 as the underlying hash algorithm for Hash_df * @@ -145,8 +153,8 @@ extern int get_entropy_input_tmp ( unsigned int num_samples, * each entropy source output after each GetEntropy call) as defined * in ANS X9.82 Part 4 (April 2011 Draft) Section 13.3.4.2. * - * To minimise code size, the number of samples required is calculated - * at compilation time. + * This function is inlined since the entropy amount and length inputs + * are always compile-time constants. */ static inline __attribute__ (( always_inline )) int get_entropy_input ( unsigned int min_entropy_bits, void *data, size_t min_len, @@ -154,41 +162,16 @@ get_entropy_input ( unsigned int min_entropy_bits, void *data, size_t min_len, size_t tmp_len = ( ( ( min_entropy_bits * 2 ) + 7 ) / 8 ); uint8_t tmp_buf[ tmp_len ]; uint8_t *tmp = ( ( tmp_len > max_len ) ? tmp_buf : data ); - double min_samples; - unsigned int num_samples; unsigned int n; int rc; - /* Sanity checks */ - linker_assert ( ( min_entropy_per_sample() <= - MIN_ENTROPY ( 8 * sizeof ( noise_sample_t ) ) ), - min_entropy_per_sample_is_impossibly_high ); + /* Sanity check */ linker_assert ( ( min_entropy_bits <= ( 8 * max_len ) ), entropy_buffer_too_small ); /* Round up minimum entropy to an integral number of bytes */ min_entropy_bits = ( ( min_entropy_bits + 7 ) & ~7 ); - /* Calculate number of samples required to contain sufficient entropy */ - min_samples = ( MIN_ENTROPY ( min_entropy_bits ) / - min_entropy_per_sample() ); - - /* Round up to a whole number of samples. We don't have the - * ceil() function available, so do the rounding by hand. - */ - num_samples = min_samples; - if ( num_samples < min_samples ) - num_samples++; - linker_assert ( ( num_samples >= min_samples ), rounding_error ); - - /* Floating-point operations are not allowed in iPXE since we - * never set up a suitable environment. Abort the build - * unless the calculated number of samples is a compile-time - * constant. - */ - linker_assert ( __builtin_constant_p ( num_samples ), - num_samples_not_constant ); - /* (Unnumbered). The output length of the hash function shall * meet or exceed the security strength indicated by the * min_entropy parameter. @@ -218,8 +201,10 @@ get_entropy_input ( unsigned int min_entropy_bits, void *data, size_t min_len, linker_assert ( __builtin_constant_p ( tmp_len ), tmp_len_not_constant ); linker_assert ( ( n == ( 8 * tmp_len ) ), tmp_len_mismatch ); - if ( ( rc = get_entropy_input_tmp ( num_samples, tmp, tmp_len ) ) != 0 ) + if ( ( rc = get_entropy_input_tmp ( MIN_ENTROPY ( min_entropy_bits ), + tmp, tmp_len ) ) != 0 ) { return rc; + } /* 6. If ( n < min_length ), then tmp = tmp || 0^(min_length-n) * 7. If ( n > max_length ), then tmp = df ( tmp, max_length ) @@ -242,4 +227,231 @@ get_entropy_input ( unsigned int min_entropy_bits, void *data, size_t min_len, } } +/** + * Calculate cutoff value for the repetition count test + * + * @v min_entropy_per_sample Min-entropy per sample + * @ret cutoff Cutoff value + * + * This is the cutoff value for the Repetition Count Test defined in + * ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.2. + */ +static inline __attribute__ (( always_inline )) unsigned int +entropy_repetition_count_cutoff ( min_entropy_t min_entropy_per_sample ) { + double max_repetitions; + unsigned int cutoff; + + /* The cutoff formula for the repetition test is: + * + * C = ( 1 + ( -log2(W) / H_min ) ) + * + * where W is set at 2^(-30) (in ANS X9.82 Part 2 (October + * 2011 Draft) Section 8.5.2.1.3.1). + */ + max_repetitions = ( 1 + ( MIN_ENTROPY ( 30 ) / + min_entropy_per_sample ) ); + + /* Round up to a whole number of repetitions. We don't have + * the ceil() function available, so do the rounding by hand. + */ + cutoff = max_repetitions; + if ( cutoff < max_repetitions ) + cutoff++; + linker_assert ( ( cutoff >= max_repetitions ), rounding_error ); + + /* Floating-point operations are not allowed in iPXE since we + * never set up a suitable environment. Abort the build + * unless the calculated number of repetitions is a + * compile-time constant. + */ + linker_assert ( __builtin_constant_p ( cutoff ), + repetition_count_cutoff_not_constant ); + + return cutoff; +} + +/** + * Window size for the adaptive proportion test + * + * ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.3.1.1 allows + * five possible window sizes: 16, 64, 256, 4096 and 65536. + * + * We expect to generate relatively few (<256) entropy samples during + * a typical iPXE run; the use of a large window size would mean that + * the test would never complete a single cycle. We use a window size + * of 64, which is the smallest window size that permits values of + * H_min down to one bit per sample. + */ +#define ADAPTIVE_PROPORTION_WINDOW_SIZE 64 + +/** + * Combine adaptive proportion test window size and min-entropy + * + * @v n N (window size) + * @v h H (min-entropy) + * @ret n_h (N,H) combined value + */ +#define APC_N_H( n, h ) ( ( (n) << 8 ) | (h) ) + +/** + * Define a row of the adaptive proportion cutoff table + * + * @v h H (min-entropy) + * @v c16 Cutoff for N=16 + * @v c64 Cutoff for N=64 + * @v c256 Cutoff for N=256 + * @v c4096 Cutoff for N=4096 + * @v c65536 Cutoff for N=65536 + */ +#define APC_TABLE_ROW( h, c16, c64, c256, c4096, c65536) \ + case APC_N_H ( 16, h ) : return c16; \ + case APC_N_H ( 64, h ) : return c64; \ + case APC_N_H ( 256, h ) : return c256; \ + case APC_N_H ( 4096, h ) : return c4096; \ + case APC_N_H ( 65536, h ) : return c65536; + +/** Value used to represent "N/A" in adaptive proportion cutoff table */ +#define APC_NA 0 + +/** + * Look up value in adaptive proportion test cutoff table + * + * @v n N (window size) + * @v h H (min-entropy) + * @ret cutoff Cutoff + * + * This is the table of cutoff values defined in ANS X9.82 Part 2 + * (October 2011 Draft) Section 8.5.2.1.3.1.2. + */ +static inline __attribute__ (( always_inline )) unsigned int +entropy_adaptive_proportion_cutoff_lookup ( unsigned int n, unsigned int h ) { + switch ( APC_N_H ( n, h ) ) { + APC_TABLE_ROW ( 1, APC_NA, 51, 168, 2240, 33537 ); + APC_TABLE_ROW ( 2, APC_NA, 35, 100, 1193, 17053 ); + APC_TABLE_ROW ( 3, 10, 24, 61, 643, 8705 ); + APC_TABLE_ROW ( 4, 8, 16, 38, 354, 4473 ); + APC_TABLE_ROW ( 5, 6, 12, 25, 200, 2321 ); + APC_TABLE_ROW ( 6, 5, 9, 17, 117, 1220 ); + APC_TABLE_ROW ( 7, 4, 7, 15, 71, 653 ); + APC_TABLE_ROW ( 8, 4, 5, 9, 45, 358 ); + APC_TABLE_ROW ( 9, 3, 4, 7, 30, 202 ); + APC_TABLE_ROW ( 10, 3, 4, 5, 21, 118 ); + APC_TABLE_ROW ( 11, 2, 3, 4, 15, 71 ); + APC_TABLE_ROW ( 12, 2, 3, 4, 11, 45 ); + APC_TABLE_ROW ( 13, 2, 2, 3, 9, 30 ); + APC_TABLE_ROW ( 14, 2, 2, 3, 7, 21 ); + APC_TABLE_ROW ( 15, 1, 2, 2, 6, 15 ); + APC_TABLE_ROW ( 16, 1, 2, 2, 5, 11 ); + APC_TABLE_ROW ( 17, 1, 1, 2, 4, 9 ); + APC_TABLE_ROW ( 18, 1, 1, 2, 4, 7 ); + APC_TABLE_ROW ( 19, 1, 1, 1, 3, 6 ); + APC_TABLE_ROW ( 20, 1, 1, 1, 3, 5 ); + default: + return APC_NA; + } +} + +/** + * Calculate cutoff value for the adaptive proportion test + * + * @v min_entropy_per_sample Min-entropy per sample + * @ret cutoff Cutoff value + * + * This is the cutoff value for the Adaptive Proportion Test defined + * in ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.3.1.2. + */ +static inline __attribute__ (( always_inline )) unsigned int +entropy_adaptive_proportion_cutoff ( min_entropy_t min_entropy_per_sample ) { + unsigned int h; + unsigned int n; + unsigned int cutoff; + + /* Look up cutoff value in cutoff table */ + n = ADAPTIVE_PROPORTION_WINDOW_SIZE; + h = ( min_entropy_per_sample / MIN_ENTROPY_SCALE ); + cutoff = entropy_adaptive_proportion_cutoff_lookup ( n, h ); + + /* Fail unless cutoff value is a compile-time constant */ + linker_assert ( __builtin_constant_p ( cutoff ), + adaptive_proportion_cutoff_not_constant ); + + /* Fail if cutoff value is N/A */ + linker_assert ( ( cutoff != APC_NA ), + adaptive_proportion_cutoff_not_applicable ); + + return cutoff; +} + +/** + * Calculate number of samples required for startup tests + * + * @v repetition_count_cutoff Repetition count test cutoff value + * @v adaptive_proportion_cutoff Adaptive proportion test cutoff value + * @ret num_samples Number of samples required + * + * ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.5 requires + * that at least one full cycle of the continuous tests must be + * performed at start-up. + */ +static inline __attribute__ (( always_inline )) unsigned int +entropy_startup_test_count ( unsigned int repetition_count_cutoff, + unsigned int adaptive_proportion_cutoff ) { + unsigned int num_samples; + + /* At least max(N,C) samples shall be generated by the noise + * source for start-up testing. + */ + num_samples = repetition_count_cutoff; + if ( num_samples < adaptive_proportion_cutoff ) + num_samples = adaptive_proportion_cutoff; + linker_assert ( __builtin_constant_p ( num_samples ), + startup_test_count_not_constant ); + + return num_samples; +} + +/** + * Initialise entropy source + * + * @v source Entropy source + * @v min_entropy_per_sample Min-entropy per sample + * + * The cutoff value calculations for the repetition count test and the + * adaptive proportion test are provided as static inline functions + * since the results will always be compile-time constants. + */ +static inline __attribute__ (( always_inline )) void +entropy_init ( struct entropy_source *source, + min_entropy_t min_entropy_per_sample ) { + unsigned int repetition_count_cutoff; + unsigned int adaptive_proportion_cutoff; + unsigned int startup_test_count; + + /* Sanity check */ + linker_assert ( min_entropy_per_sample > MIN_ENTROPY ( 0 ), + min_entropy_per_sample_is_zero ); + linker_assert ( ( min_entropy_per_sample <= + MIN_ENTROPY ( 8 * sizeof ( noise_sample_t ) ) ), + min_entropy_per_sample_is_impossibly_high ); + + /* Calculate test cutoff values */ + repetition_count_cutoff = + entropy_repetition_count_cutoff ( min_entropy_per_sample ); + adaptive_proportion_cutoff = + entropy_adaptive_proportion_cutoff ( min_entropy_per_sample ); + startup_test_count = + entropy_startup_test_count ( repetition_count_cutoff, + adaptive_proportion_cutoff ); + + /* Record min-entropy per sample and test cutoff values */ + source->min_entropy_per_sample = min_entropy_per_sample; + source->repetition_count_cutoff = repetition_count_cutoff; + source->adaptive_proportion_cutoff = adaptive_proportion_cutoff; + source->startup_test_count = startup_test_count; +} + +extern int entropy_enable ( void ); +extern void entropy_disable ( void ); +extern int get_noise ( noise_sample_t *noise ); + #endif /* _IPXE_ENTROPY_H */ diff --git a/src/include/ipxe/linux/linux_entropy.h b/src/include/ipxe/linux/linux_entropy.h deleted file mode 100644 index ea8c1f16c..000000000 --- a/src/include/ipxe/linux/linux_entropy.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef _IPXE_LINUX_ENTROPY_H -#define _IPXE_LINUX_ENTROPY_H - -/** @file - * - * /dev/random-based entropy source - * - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#ifdef ENTROPY_LINUX -#define ENTROPY_PREFIX_linux -#else -#define ENTROPY_PREFIX_linux __linux_ -#endif - -/** - * min-entropy per sample - * - * @ret min_entropy min-entropy of each sample - */ -static inline __always_inline min_entropy_t -ENTROPY_INLINE ( linux, min_entropy_per_sample ) ( void ) { - - /* linux_get_noise() reads a single byte from /dev/random, - * which is supposed to block until a sufficient amount of - * entropy is available. We therefore assume that each sample - * contains exactly 8 bits of entropy. - */ - return MIN_ENTROPY ( 8.0 ); -} - -#endif /* _IPXE_LINUX_ENTROPY_H */ diff --git a/src/include/ipxe/null_entropy.h b/src/include/ipxe/null_entropy.h deleted file mode 100644 index 5a6bb6218..000000000 --- a/src/include/ipxe/null_entropy.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef _IPXE_NULL_ENTROPY_H -#define _IPXE_NULL_ENTROPY_H - -/** @file - * - * Nonexistent entropy source - * - * This source provides no entropy and must NOT be used in a - * security-sensitive environment. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include - -#ifdef ENTROPY_NULL -#define ENTROPY_PREFIX_null -#else -#define ENTROPY_PREFIX_null __null_ -#endif - -static inline __always_inline int -ENTROPY_INLINE ( null, entropy_enable ) ( void ) { - /* Do nothing */ - return 0; -} - -static inline __always_inline void -ENTROPY_INLINE ( null, entropy_disable ) ( void ) { - /* Do nothing */ -} - -static inline __always_inline min_entropy_t -ENTROPY_INLINE ( null, min_entropy_per_sample ) ( void ) { - /* Actual amount of min-entropy is zero. To avoid - * division-by-zero errors and to allow compilation of - * entropy-consuming code, pretend to have 1 bit of entropy in - * each sample. - */ - return MIN_ENTROPY ( 1.0 ); -} - -static inline __always_inline int -ENTROPY_INLINE ( null, get_noise ) ( noise_sample_t *noise ) { - - /* All sample values are constant */ - *noise = 0x01; - - return 0; -} - -#endif /* _IPXE_NULL_ENTROPY_H */ diff --git a/src/interface/efi/efi_entropy.c b/src/interface/efi/efi_entropy.c index 1e8ddfb68..e5c393562 100644 --- a/src/interface/efi/efi_entropy.c +++ b/src/interface/efi/efi_entropy.c @@ -36,6 +36,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ +struct entropy_source efi_entropy __entropy_source ( ENTROPY_NORMAL ); + /** Random number generator protocol */ static EFI_RNG_PROTOCOL *efirng; EFI_REQUEST_PROTOCOL ( EFI_RNG_PROTOCOL, &efirng ); @@ -91,6 +93,12 @@ static int efi_entropy_enable ( void ) { return rc; } + /* We use essentially the same mechanism as for the BIOS + * RTC-based entropy source, and so assume the same + * min-entropy per sample. + */ + entropy_init ( &efi_entropy, MIN_ENTROPY ( 1.3 ) ); + return 0; } @@ -235,7 +243,10 @@ static int efi_get_noise ( noise_sample_t *noise ) { return 0; } -PROVIDE_ENTROPY_INLINE ( efi, min_entropy_per_sample ); -PROVIDE_ENTROPY ( efi, entropy_enable, efi_entropy_enable ); -PROVIDE_ENTROPY ( efi, entropy_disable, efi_entropy_disable ); -PROVIDE_ENTROPY ( efi, get_noise, efi_get_noise ); +/** EFI entropy source */ +struct entropy_source efi_entropy __entropy_source ( ENTROPY_NORMAL ) = { + .name = "efi", + .enable = efi_entropy_enable, + .disable = efi_entropy_disable, + .get_noise = efi_get_noise, +}; diff --git a/src/interface/linux/linux_entropy.c b/src/interface/linux/linux_entropy.c index 257e993a0..f24969794 100644 --- a/src/interface/linux/linux_entropy.c +++ b/src/interface/linux/linux_entropy.c @@ -34,6 +34,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +struct entropy_source linux_entropy __entropy_source ( ENTROPY_NORMAL ); + /** Entropy source filename */ static const char entropy_filename[] = "/dev/random"; @@ -55,6 +57,13 @@ static int linux_entropy_enable ( void ) { return entropy_fd; } + /* linux_get_noise() reads a single byte from /dev/random, + * which is supposed to block until a sufficient amount of + * entropy is available. We therefore assume that each sample + * contains exactly 8 bits of entropy. + */ + entropy_init ( &linux_entropy, MIN_ENTROPY ( 8.0 ) ); + return 0; } @@ -95,7 +104,10 @@ static int linux_get_noise ( noise_sample_t *noise ) { return 0; } -PROVIDE_ENTROPY_INLINE ( linux, min_entropy_per_sample ); -PROVIDE_ENTROPY ( linux, entropy_enable, linux_entropy_enable ); -PROVIDE_ENTROPY ( linux, entropy_disable, linux_entropy_disable ); -PROVIDE_ENTROPY ( linux, get_noise, linux_get_noise ); +/** Linux entropy source */ +struct entropy_source linux_entropy __entropy_source ( ENTROPY_NORMAL ) = { + .name = "linux", + .enable = linux_entropy_enable, + .disable = linux_entropy_disable, + .get_noise = linux_get_noise, +}; -- cgit v1.2.3-55-g7522 From 7d71cf318a2a6fedde7aaf9303b1cdec0cf51660 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 20 Feb 2023 13:55:40 +0000 Subject: [rng] Allow for entropy sources that fail during startup tests Provide per-source state variables for the repetition count test and adaptive proportion test, to allow for the situation in which an entropy source can be enabled but then fails during the startup tests, thereby requiring an alternative entropy source to be used. Signed-off-by: Michael Brown --- src/crypto/entropy.c | 349 +++++++++++++++++++++++++++++---------------- src/include/ipxe/entropy.h | 133 +++++++++++++---- src/tests/entropy_sample.c | 26 +++- 3 files changed, 349 insertions(+), 159 deletions(-) (limited to 'src/crypto') diff --git a/src/crypto/entropy.c b/src/crypto/entropy.c index 204e6bb1e..419007159 100644 --- a/src/crypto/entropy.c +++ b/src/crypto/entropy.c @@ -50,77 +50,34 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define EINFO_EPIPE_ADAPTIVE_PROPORTION_TEST \ __einfo_uniqify ( EINFO_EPIPE, 0x02, "Adaptive proportion test failed" ) -/** Current entropy source */ -static struct entropy_source *entropy_source; - /** - * Enable entropy gathering + * Initialise repetition count test * - * @ret rc Return status code + * @v source Entropy source */ -int entropy_enable ( void ) { - int rc; - - /* Enable selected source, if applicable */ - if ( entropy_source ) { - - /* Enable entropy source */ - if ( ( rc = entropy_source->enable() ) != 0 ) { - DBGC ( &entropy_source, "ENTROPY could not enable " - "source \"%s\": %s\n", entropy_source->name, - strerror ( rc ) ); - return rc; - } - - /* Sanity checks */ - assert ( entropy_source->min_entropy_per_sample > 0 ); - assert ( entropy_source->repetition_count_cutoff > 0 ); - assert ( entropy_source->adaptive_proportion_cutoff > 0 ); - assert ( entropy_source->startup_test_count > 0 ); +static void repetition_count_test_init ( struct entropy_source *source ) { + struct entropy_repetition_count_test *test = + &source->repetition_count_test; - return 0; - } - - /* Find the first working source */ - rc = -ENOENT; - for_each_table_entry ( entropy_source, ENTROPY_SOURCES ) { - if ( ( rc = entropy_enable() ) == 0 ) { - DBGC ( &entropy_source, "ENTROPY using source \"%s\"\n", - entropy_source->name ); - break; - } - } - return rc; -} - -/** - * Disable entropy gathering - * - */ -void entropy_disable ( void ) { - - /* Sanity check */ - assert ( entropy_source != NULL ); - - /* Disable entropy gathering, if applicable */ - if ( entropy_source->disable ) - entropy_source->disable(); + /* Sanity checks */ + assert ( test->repetition_count == 0 ); + assert ( test->cutoff > 0 ); } /** * Perform repetition count test * + * @v source Entropy source * @v sample Noise sample * @ret rc Return status code * * This is the Repetition Count Test defined in ANS X9.82 Part 2 * (October 2011 Draft) Section 8.5.2.1.2. */ -static int repetition_count_test ( noise_sample_t sample ) { - static noise_sample_t most_recent_sample; - static unsigned int repetition_count = 0; - unsigned int repetition_count_cutoff = - entropy_source->repetition_count_cutoff; +static int repetition_count_test ( struct entropy_source *source, + noise_sample_t sample ) { + struct entropy_repetition_count_test *test = + &source->repetition_count_test; /* A = the most recently seen sample value * B = the number of times that value A has been seen in a row @@ -133,49 +90,71 @@ static int repetition_count_test ( noise_sample_t sample ) { * the initial value of most_recent_sample is treated as being * undefined.) */ - if ( ( sample == most_recent_sample ) && ( repetition_count > 0 ) ) { + if ( ( sample == test->most_recent_sample ) && + ( test->repetition_count > 0 ) ) { /* a) If the new sample = A, then B is incremented by one. */ - repetition_count++; + test->repetition_count++; /* i. If B >= C, then an error condition is raised * due to a failure of the test */ - if ( repetition_count >= repetition_count_cutoff ) + if ( test->repetition_count >= test->cutoff ) { + DBGC ( source, "ENTROPY %s excessively repeated " + "value %d (%d/%d)\n", source->name, sample, + test->repetition_count, test->cutoff ); return -EPIPE_REPETITION_COUNT_TEST; + } } else { /* b) Else: * i. A = new sample */ - most_recent_sample = sample; + test->most_recent_sample = sample; /* ii. B = 1 */ - repetition_count = 1; + test->repetition_count = 1; } return 0; } +/** + * Initialise adaptive proportion test + * + * @v source Entropy source + */ +static void adaptive_proportion_test_init ( struct entropy_source *source ) { + struct entropy_adaptive_proportion_test *test = + &source->adaptive_proportion_test; + + /* Sanity checks */ + assert ( test->sample_count == 0 ); + assert ( test->repetition_count == 0 ); + assert ( test->cutoff > 0 ); + + /* Ensure that a new test run starts immediately */ + test->sample_count = ADAPTIVE_PROPORTION_WINDOW_SIZE; +} + /** * Perform adaptive proportion test * + * @v source Entropy source * @v sample Noise sample * @ret rc Return status code * * This is the Adaptive Proportion Test for the Most Common Value * defined in ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.3. */ -static int adaptive_proportion_test ( noise_sample_t sample ) { - static noise_sample_t current_counted_sample; - static unsigned int sample_count = ADAPTIVE_PROPORTION_WINDOW_SIZE; - static unsigned int repetition_count; - unsigned int adaptive_proportion_cutoff = - entropy_source->adaptive_proportion_cutoff; +static int adaptive_proportion_test ( struct entropy_source *source, + noise_sample_t sample ) { + struct entropy_adaptive_proportion_test *test = + &source->adaptive_proportion_test; /* A = the sample value currently being counted - * B = the number of samples examined in this run of the test so far + * S = the number of samples examined in this run of the test so far * N = the total number of samples that must be observed in * one run of the test, also known as the "window size" of * the test @@ -192,97 +171,224 @@ static int adaptive_proportion_test ( noise_sample_t sample ) { */ /* 2. If S = N, then a new run of the test begins: */ - if ( sample_count == ADAPTIVE_PROPORTION_WINDOW_SIZE ) { + if ( test->sample_count == ADAPTIVE_PROPORTION_WINDOW_SIZE ) { /* a. A = the current sample */ - current_counted_sample = sample; + test->current_counted_sample = sample; /* b. S = 0 */ - sample_count = 0; + test->sample_count = 0; /* c. B = 0 */ - repetition_count = 0; + test->repetition_count = 0; } else { /* Else: (the test is already running) * a. S = S + 1 */ - sample_count++; + test->sample_count++; /* b. If A = the current sample, then: */ - if ( sample == current_counted_sample ) { + if ( sample == test->current_counted_sample ) { /* i. B = B + 1 */ - repetition_count++; + test->repetition_count++; /* ii. If S (sic) > C then raise an error * condition, because the test has * detected a failure */ - if ( repetition_count > adaptive_proportion_cutoff ) + if ( test->repetition_count > test->cutoff ) { + DBGC ( source, "ENTROPY %s excessively " + "repeated value %d (%d/%d)\n", + source->name, sample, + test->repetition_count, test->cutoff ); return -EPIPE_ADAPTIVE_PROPORTION_TEST; - + } } } return 0; } -/** - * Get noise sample - * - * @ret noise Noise sample - * @ret rc Return status code - * - * This is the GetNoise function defined in ANS X9.82 Part 2 - * (October 2011 Draft) Section 6.5.2. - */ -int get_noise ( noise_sample_t *noise ) { - - /* Sanity check */ - assert ( entropy_source != NULL ); - - return entropy_source->get_noise ( noise ); -} - /** * Get entropy sample * + * @v source Entropy source * @ret entropy Entropy sample * @ret rc Return status code * * This is the GetEntropy function defined in ANS X9.82 Part 2 * (October 2011 Draft) Section 6.5.1. */ -static int get_entropy ( entropy_sample_t *entropy ) { - static int rc = 0; +static int get_entropy ( struct entropy_source *source, + entropy_sample_t *entropy ) { noise_sample_t noise; - - /* Sanity check */ - assert ( entropy_source != NULL ); + int rc; /* Any failure is permanent */ - if ( rc != 0 ) - return rc; + if ( ( rc = source->rc ) != 0 ) + goto err_broken; /* Get noise sample */ - if ( ( rc = get_noise ( &noise ) ) != 0 ) - return rc; + if ( ( rc = get_noise ( source, &noise ) ) != 0 ) + goto err_get_noise; /* Perform Repetition Count Test and Adaptive Proportion Test * as mandated by ANS X9.82 Part 2 (October 2011 Draft) * Section 8.5.2.1.1. */ - if ( ( rc = repetition_count_test ( noise ) ) != 0 ) - return rc; - if ( ( rc = adaptive_proportion_test ( noise ) ) != 0 ) - return rc; + if ( ( rc = repetition_count_test ( source, noise ) ) != 0 ) + goto err_repetition_count_test; + if ( ( rc = adaptive_proportion_test ( source, noise ) ) != 0 ) + goto err_adaptive_proportion_test; /* We do not use any optional conditioning component */ *entropy = noise; return 0; + + err_adaptive_proportion_test: + err_repetition_count_test: + err_get_noise: + source->rc = rc; + err_broken: + return rc; +} + +/** + * Initialise startup test + * + * @v source Entropy source + */ +static void startup_test_init ( struct entropy_source *source ) { + struct entropy_startup_test *test = &source->startup_test; + + /* Sanity check */ + assert ( test->tested == 0 ); + assert ( test->count > 0 ); +} + +/** + * Perform startup test + * + * @v source Entropy source + * @ret rc Return status code + */ +static int startup_test ( struct entropy_source *source ) { + struct entropy_startup_test *test = &source->startup_test; + entropy_sample_t sample; + int rc; + + /* Perform mandatory number of startup tests */ + for ( ; test->tested < test->count ; test->tested++ ) { + if ( ( rc = get_entropy ( source, &sample ) ) != 0 ) { + DBGC ( source, "ENTROPY %s failed: %s\n", + source->name, strerror ( rc ) ); + return rc; + } + } + + return 0; +} + +/** + * Enable entropy gathering + * + * @v source Entropy source + * @ret rc Return status code + */ +int entropy_enable ( struct entropy_source *source ) { + int rc; + + /* Refuse to enable a previously failed source */ + if ( ( rc = source->rc ) != 0 ) + return rc; + + /* Enable entropy source */ + if ( ( rc = source->enable() ) != 0 ) { + DBGC ( source, "ENTROPY %s could not enable: %s\n", + source->name, strerror ( rc ) ); + source->rc = rc; + return rc; + } + + /* Sanity check */ + assert ( source->min_entropy_per_sample > 0 ); + + /* Initialise test state if this source has not previously been used */ + if ( source->startup_test.tested == 0 ) { + repetition_count_test_init ( source ); + adaptive_proportion_test_init ( source ); + startup_test_init ( source ); + } + + DBGC ( source, "ENTROPY %s enabled\n", source->name ); + return 0; +} + +/** + * Enable and test entropy source + * + * @v source Entropy source + * @ret rc Return status code + */ +static int entropy_enable_and_test ( struct entropy_source *source ) { + int rc; + + /* Enable source */ + if ( ( rc = entropy_enable ( source ) ) != 0 ) + goto err_enable; + + /* Test source */ + if ( ( rc = startup_test ( source ) ) != 0 ) + goto err_test; + + DBGC ( source, "ENTROPY %s passed %d startup tests\n", + source->name, source->startup_test.count ); + return 0; + + err_test: + entropy_disable ( source ); + err_enable: + assert ( source->rc == rc ); + return rc; +} + +/** + * Enable first working entropy source + * + * @v source Entropy source to fill in + * @ret rc Return status code + */ +static int entropy_enable_working ( struct entropy_source **source ) { + int rc; + + /* Find the first working source */ + rc = -ENOENT; + for_each_table_entry ( *source, ENTROPY_SOURCES ) { + if ( ( rc = entropy_enable_and_test ( *source ) ) == 0 ) + return 0; + } + + DBGC ( *source, "ENTROPY has no working sources: %s\n", + strerror ( rc ) ); + return rc; +} + +/** + * Disable entropy gathering + * + * @v source Entropy source + */ +void entropy_disable ( struct entropy_source *source ) { + + /* Disable entropy gathering, if applicable */ + if ( source->disable ) + source->disable(); + + DBGC ( source, "ENTROPY %s disabled\n", source->name ); } /** @@ -318,7 +424,7 @@ static uint32_t make_next_nonce ( void ) { */ int get_entropy_input_tmp ( min_entropy_t min_entropy, uint8_t *tmp, size_t tmp_len ) { - static unsigned int startup_tested = 0; + struct entropy_source *source; struct { uint32_t nonce; entropy_sample_t sample; @@ -330,15 +436,12 @@ int get_entropy_input_tmp ( min_entropy_t min_entropy, uint8_t *tmp, int rc; /* Enable entropy gathering */ - if ( ( rc = entropy_enable() ) != 0 ) - return rc; + if ( ( rc = entropy_enable_working ( &source ) ) != 0 ) + goto err_enable_working; - /* Perform mandatory startup tests, if not yet performed */ - for ( ; startup_tested < entropy_source->startup_test_count ; - startup_tested++ ) { - if ( ( rc = get_entropy ( &data.sample ) ) != 0 ) - goto err_get_entropy; - } + /* Sanity checks */ + assert ( source->startup_test.count > 0 ); + assert ( source->startup_test.tested >= source->startup_test.count ); /* 3. entropy_total = 0 */ entropy_total = MIN_ENTROPY ( 0 ); @@ -352,7 +455,7 @@ int get_entropy_input_tmp ( min_entropy_t min_entropy, uint8_t *tmp, * = GetEntropy() * 5.2. If status indicates an error, return ( status, Null ) */ - if ( ( rc = get_entropy ( &data.sample ) ) != 0 ) + if ( ( rc = get_entropy ( source, &data.sample ) ) != 0 ) goto err_get_entropy; /* 5.3. nonce = MakeNextNonce() */ @@ -367,18 +470,20 @@ int get_entropy_input_tmp ( min_entropy_t min_entropy, uint8_t *tmp, tmp[i] ^= df_buf[i]; /* 5.5. entropy_total = entropy_total + assessed_entropy */ - entropy_total += entropy_source->min_entropy_per_sample; + entropy_total += source->min_entropy_per_sample; } /* Disable entropy gathering */ - entropy_disable(); + entropy_disable ( source ); - DBGC ( &entropy_source, "ENTROPY gathered %d bits in %d samples\n", - ( min_entropy / MIN_ENTROPY_SCALE ), num_samples ); + DBGC ( source, "ENTROPY %s gathered %d bits in %d samples\n", + source->name, ( min_entropy / MIN_ENTROPY_SCALE ), num_samples ); return 0; err_get_entropy: - entropy_disable(); + entropy_disable ( source ); + assert ( source->rc == rc ); + err_enable_working: return rc; } diff --git a/src/include/ipxe/entropy.h b/src/include/ipxe/entropy.h index 108c37669..240feace0 100644 --- a/src/include/ipxe/entropy.h +++ b/src/include/ipxe/entropy.h @@ -42,6 +42,76 @@ typedef unsigned int min_entropy_t; #define MIN_ENTROPY( bits ) \ ( ( min_entropy_t ) ( (bits) * MIN_ENTROPY_SCALE ) ) +/** + * Repetition count test state + * + * This is the state for the repetition Count Test defined in ANS + * X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.2. + */ +struct entropy_repetition_count_test { + /** + * A = the most recently seen sample value + */ + noise_sample_t most_recent_sample; + /** + * B = the number of times that value A has been seen in a row + */ + unsigned int repetition_count; + /** + * C = the cutoff value above which the repetition test should fail + * + * Filled in by entropy_init(). + */ + unsigned int cutoff; +}; + +/** + * Adaptive proportion test state + * + * This is the state for the Adaptive Proportion Test for the Most + * Common Value defined in ANS X9.82 Part 2 (October 2011 Draft) + * Section 8.5.2.1.3. + */ +struct entropy_adaptive_proportion_test { + /** + * A = the sample value currently being counted + */ + noise_sample_t current_counted_sample; + /** + * S = the number of samples examined in this run of the test so far + */ + unsigned int sample_count; + /** + * B = the current number of times that S (sic) has been seen + * in the W (sic) samples examined so far + */ + unsigned int repetition_count; + /** + * C = the cutoff value above which the repetition test should fail + * + * Filled in by entropy_init(). + */ + unsigned int cutoff; +}; + +/** + * Startup test state + * + * ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.5 requires + * that at least one full cycle of the continuous tests must be + * performed at start-up. + */ +struct entropy_startup_test { + /** Number of startup tests performed */ + unsigned int tested; + /** + * Number of startup tests required for one full cycle + * + * Filled in by entropy_init(). + */ + unsigned int count; +}; + /** An entropy source */ struct entropy_source { /** Name */ @@ -59,34 +129,19 @@ struct entropy_source { * Filled in by entropy_init(). */ min_entropy_t min_entropy_per_sample; + /** Repetition count test state */ + struct entropy_repetition_count_test repetition_count_test; + /** Adaptive proportion test state */ + struct entropy_adaptive_proportion_test adaptive_proportion_test; + /** Startup test state */ + struct entropy_startup_test startup_test; /** - * Repetition count test cutoff value - * - * This is the cutoff value for the Repetition Count Test - * defined in ANS X9.82 Part 2 (October 2011 Draft) Section - * 8.5.2.1.2. - * - * Filled in by entropy_init(). - */ - unsigned int repetition_count_cutoff; - /** - * Adaptive proportion test cutoff value - * - * This is the cutoff value for the Adaptive Proportion Test - * defined in ANS X9.82 Part 2 (October 2011 Draft) Section - * 8.5.2.1.3.1.2. - * - * Filled in by entropy_init(). - */ - unsigned int adaptive_proportion_cutoff; - /** - * Startup test count + * Failure status (if any) * - * ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.5 - * requires that at least one full cycle of the continuous - * tests must be performed at start-up. + * Any failure of an entropy source is regarded as permanent. */ - unsigned int startup_test_count; + int rc; + /** * Enable entropy gathering * @@ -139,6 +194,22 @@ extern int get_entropy_input_tmp ( min_entropy_t min_entropy, uint8_t *tmp, /** Underlying hash algorithm output length (in bytes) */ #define ENTROPY_HASH_DF_OUTLEN_BYTES SHA256_DIGEST_SIZE +/** + * Get noise sample + * + * @v source Entropy source + * @ret noise Noise sample + * @ret rc Return status code + * + * This is the GetNoise function defined in ANS X9.82 Part 2 + * (October 2011 Draft) Section 6.5.2. + */ +static inline __attribute__ (( always_inline )) int +get_noise ( struct entropy_source *source, noise_sample_t *noise ) { + + return source->get_noise ( noise ); +} + /** * Obtain entropy input * @@ -445,13 +516,13 @@ entropy_init ( struct entropy_source *source, /* Record min-entropy per sample and test cutoff values */ source->min_entropy_per_sample = min_entropy_per_sample; - source->repetition_count_cutoff = repetition_count_cutoff; - source->adaptive_proportion_cutoff = adaptive_proportion_cutoff; - source->startup_test_count = startup_test_count; + source->repetition_count_test.cutoff = repetition_count_cutoff; + source->adaptive_proportion_test.cutoff = adaptive_proportion_cutoff; + source->startup_test.count = startup_test_count; } -extern int entropy_enable ( void ); -extern void entropy_disable ( void ); -extern int get_noise ( noise_sample_t *noise ); +extern int entropy_enable ( struct entropy_source *source ); +extern void entropy_disable ( struct entropy_source *source ); +extern int get_noise ( struct entropy_source *source, noise_sample_t *noise ); #endif /* _IPXE_ENTROPY_H */ diff --git a/src/tests/entropy_sample.c b/src/tests/entropy_sample.c index b45648c11..3c2386eab 100644 --- a/src/tests/entropy_sample.c +++ b/src/tests/entropy_sample.c @@ -42,8 +42,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * Generate entropy samples for external testing * + * @v source Entropy source */ -static void entropy_sample_test_exec ( void ) { +static void entropy_sample ( struct entropy_source *source ) { static noise_sample_t samples[SAMPLE_BLOCKSIZE]; unsigned int i; unsigned int j; @@ -53,22 +54,35 @@ static void entropy_sample_test_exec ( void ) { for ( i = 0 ; i < ( SAMPLE_COUNT / SAMPLE_BLOCKSIZE ) ; i++ ) { /* Collect one block of samples */ - rc = entropy_enable(); + rc = entropy_enable ( source ); ok ( rc == 0 ); for ( j = 0 ; j < SAMPLE_BLOCKSIZE ; j++ ) { - rc = get_noise ( &samples[j] ); + rc = get_noise ( source, &samples[j] ); ok ( rc == 0 ); } - entropy_disable(); + entropy_disable ( source ); /* Print out sample values */ for ( j = 0 ; j < SAMPLE_BLOCKSIZE ; j++ ) { - printf ( "SAMPLE %d %d\n", ( i * SAMPLE_BLOCKSIZE + j ), - samples[j] ); + printf ( "SAMPLE %s %d %d\n", source->name, + ( i * SAMPLE_BLOCKSIZE + j ), samples[j] ); } } } +/** + * Generate entropy samples for external testing + * + */ +static void entropy_sample_test_exec ( void ) { + struct entropy_source *source; + + /* Test each entropy source */ + for_each_table_entry ( source, ENTROPY_SOURCES ) { + entropy_sample ( source ); + } +} + /** Entropy sampling self-test */ struct self_test entropy_sample_test __self_test = { .name = "entropy_sample", -- cgit v1.2.3-55-g7522