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/tests/cipher_test.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/tests') 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 0c383bf00afbef1a9cfe02829d1bc6ee46e1c16b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 24 Oct 2022 18:49:43 +0100 Subject: [crypto] Add concept of additional data to cipher algorithms Some ciphers (such as GCM) support the concept of additional authenticated data, which does not appear in the ciphertext but may affect the operation of the cipher. Allow cipher_encrypt() and cipher_decrypt() to be called with a NULL destination buffer in order to pass additional data. Signed-off-by: Michael Brown --- src/include/ipxe/crypto.h | 30 +++++++++++++++--------------- src/tests/aes_test.c | 12 ++++++------ src/tests/cipher_test.c | 14 +++++++++++++- src/tests/cipher_test.h | 14 +++++++++++++- 4 files changed, 47 insertions(+), 23 deletions(-) (limited to 'src/tests') diff --git a/src/include/ipxe/crypto.h b/src/include/ipxe/crypto.h index 931be0502..d41448024 100644 --- a/src/include/ipxe/crypto.h +++ b/src/include/ipxe/crypto.h @@ -54,25 +54,25 @@ struct cipher_algorithm { size_t blocksize; /** Set key * - * @v ctx Context - * @v key Key - * @v keylen Key length - * @ret rc Return status code + * @v ctx Context + * @v key Key + * @v keylen Key length + * @ret rc Return status code */ int ( * setkey ) ( void *ctx, const void *key, size_t keylen ); /** Set initialisation vector * - * @v ctx Context - * @v iv Initialisation vector - * @v ivlen Initialisation vector length + * @v ctx Context + * @v iv Initialisation vector + * @v ivlen Initialisation vector length */ void ( * setiv ) ( void *ctx, const void *iv, size_t ivlen ); /** Encrypt data * - * @v ctx Context - * @v src Data to encrypt - * @v dst Buffer for encrypted data - * @v len Length of data + * @v ctx Context + * @v src Data to encrypt + * @v dst Buffer for encrypted data, or NULL for additional data + * @v len Length of data * * @v len is guaranteed to be a multiple of @c blocksize. */ @@ -80,10 +80,10 @@ struct cipher_algorithm { size_t len ); /** Decrypt data * - * @v ctx Context - * @v src Data to decrypt - * @v dst Buffer for decrypted data - * @v len Length of data + * @v ctx Context + * @v src Data to decrypt + * @v dst Buffer for decrypted data, or NULL for additional data + * @v len Length of data * * @v len is guaranteed to be a multiple of @c blocksize. */ diff --git a/src/tests/aes_test.c b/src/tests/aes_test.c index ad66c734c..e7201fca6 100644 --- a/src/tests/aes_test.c +++ b/src/tests/aes_test.c @@ -86,7 +86,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** AES-128-ECB (same test as AES-128-Core) */ CIPHER_TEST ( aes_128_ecb, &aes_ecb_algorithm, - AES_KEY_NIST_128, AES_IV_NIST_DUMMY, AES_PLAINTEXT_NIST, + AES_KEY_NIST_128, AES_IV_NIST_DUMMY, ADDITIONAL(), AES_PLAINTEXT_NIST, CIPHERTEXT ( 0x3a, 0xd7, 0x7b, 0xb4, 0x0d, 0x7a, 0x36, 0x60, 0xa8, 0x9e, 0xca, 0xf3, 0x24, 0x66, 0xef, 0x97, 0xf5, 0xd3, 0xd5, 0x85, 0x03, 0xb9, 0x69, 0x9d, @@ -98,7 +98,7 @@ CIPHER_TEST ( aes_128_ecb, &aes_ecb_algorithm, /** AES-128-CBC */ CIPHER_TEST ( aes_128_cbc, &aes_cbc_algorithm, - AES_KEY_NIST_128, AES_IV_NIST_CBC, AES_PLAINTEXT_NIST, + AES_KEY_NIST_128, AES_IV_NIST_CBC, ADDITIONAL(), AES_PLAINTEXT_NIST, CIPHERTEXT ( 0x76, 0x49, 0xab, 0xac, 0x81, 0x19, 0xb2, 0x46, 0xce, 0xe9, 0x8e, 0x9b, 0x12, 0xe9, 0x19, 0x7d, 0x50, 0x86, 0xcb, 0x9b, 0x50, 0x72, 0x19, 0xee, @@ -110,7 +110,7 @@ CIPHER_TEST ( aes_128_cbc, &aes_cbc_algorithm, /** AES-192-ECB (same test as AES-192-Core) */ CIPHER_TEST ( aes_192_ecb, &aes_ecb_algorithm, - AES_KEY_NIST_192, AES_IV_NIST_DUMMY, AES_PLAINTEXT_NIST, + AES_KEY_NIST_192, AES_IV_NIST_DUMMY, ADDITIONAL(), AES_PLAINTEXT_NIST, CIPHERTEXT ( 0xbd, 0x33, 0x4f, 0x1d, 0x6e, 0x45, 0xf2, 0x5f, 0xf7, 0x12, 0xa2, 0x14, 0x57, 0x1f, 0xa5, 0xcc, 0x97, 0x41, 0x04, 0x84, 0x6d, 0x0a, 0xd3, 0xad, @@ -122,7 +122,7 @@ CIPHER_TEST ( aes_192_ecb, &aes_ecb_algorithm, /** AES-192-CBC */ CIPHER_TEST ( aes_192_cbc, &aes_cbc_algorithm, - AES_KEY_NIST_192, AES_IV_NIST_CBC, AES_PLAINTEXT_NIST, + AES_KEY_NIST_192, AES_IV_NIST_CBC, ADDITIONAL(), AES_PLAINTEXT_NIST, CIPHERTEXT ( 0x4f, 0x02, 0x1d, 0xb2, 0x43, 0xbc, 0x63, 0x3d, 0x71, 0x78, 0x18, 0x3a, 0x9f, 0xa0, 0x71, 0xe8, 0xb4, 0xd9, 0xad, 0xa9, 0xad, 0x7d, 0xed, 0xf4, @@ -134,7 +134,7 @@ CIPHER_TEST ( aes_192_cbc, &aes_cbc_algorithm, /** AES-256-ECB (same test as AES-256-Core) */ CIPHER_TEST ( aes_256_ecb, &aes_ecb_algorithm, - AES_KEY_NIST_256, AES_IV_NIST_DUMMY, AES_PLAINTEXT_NIST, + AES_KEY_NIST_256, AES_IV_NIST_DUMMY, ADDITIONAL(), AES_PLAINTEXT_NIST, CIPHERTEXT ( 0xf3, 0xee, 0xd1, 0xbd, 0xb5, 0xd2, 0xa0, 0x3c, 0x06, 0x4b, 0x5a, 0x7e, 0x3d, 0xb1, 0x81, 0xf8, 0x59, 0x1c, 0xcb, 0x10, 0xd4, 0x10, 0xed, 0x26, @@ -146,7 +146,7 @@ CIPHER_TEST ( aes_256_ecb, &aes_ecb_algorithm, /** AES-256-CBC */ CIPHER_TEST ( aes_256_cbc, &aes_cbc_algorithm, - AES_KEY_NIST_256, AES_IV_NIST_CBC, AES_PLAINTEXT_NIST, + AES_KEY_NIST_256, AES_IV_NIST_CBC, ADDITIONAL(), AES_PLAINTEXT_NIST, CIPHERTEXT ( 0xf5, 0x8c, 0x4c, 0x04, 0xd6, 0xe5, 0xf1, 0xba, 0x77, 0x9e, 0xab, 0xfb, 0x5f, 0x7b, 0xfb, 0xd6, 0x9c, 0xfc, 0x4e, 0x96, 0x7e, 0xdb, 0x80, 0x8d, diff --git a/src/tests/cipher_test.c b/src/tests/cipher_test.c index 5361502ff..c49b4b69b 100644 --- a/src/tests/cipher_test.c +++ b/src/tests/cipher_test.c @@ -63,6 +63,12 @@ void cipher_encrypt_okx ( struct cipher_test *test, const char *file, file, line ); cipher_setiv ( cipher, ctx, test->iv, test->iv_len ); + /* Process additional data, if applicable */ + if ( test->additional_len ) { + cipher_encrypt ( cipher, ctx, test->additional, NULL, + test->additional_len ); + } + /* Perform encryption */ cipher_encrypt ( cipher, ctx, test->plaintext, ciphertext, len ); @@ -89,7 +95,13 @@ void cipher_decrypt_okx ( struct cipher_test *test, const char *file, file, line ); cipher_setiv ( cipher, ctx, test->iv, test->iv_len ); - /* Perform encryption */ + /* Process additional data, if applicable */ + if ( test->additional_len ) { + cipher_decrypt ( cipher, ctx, test->additional, NULL, + test->additional_len ); + } + + /* Perform decryption */ cipher_decrypt ( cipher, ctx, test->ciphertext, plaintext, len ); /* Compare against expected plaintext */ diff --git a/src/tests/cipher_test.h b/src/tests/cipher_test.h index d7c5aef8f..4139a7788 100644 --- a/src/tests/cipher_test.h +++ b/src/tests/cipher_test.h @@ -25,6 +25,10 @@ struct cipher_test { const void *iv; /** Length of initialisation vector */ size_t iv_len; + /** Additional data */ + const void *additional; + /** Length of additional data */ + size_t additional_len; /** Plaintext */ const void *plaintext; /** Ciphertext */ @@ -39,6 +43,9 @@ struct cipher_test { /** Define inline initialisation vector */ #define IV(...) { __VA_ARGS__ } +/** Define inline additional data */ +#define ADDITIONAL(...) { __VA_ARGS__ } + /** Define inline plaintext data */ #define PLAINTEXT(...) { __VA_ARGS__ } @@ -52,13 +59,16 @@ struct cipher_test { * @v CIPHER Cipher algorithm * @v KEY Key * @v IV Initialisation vector + * @v ADDITIONAL Additional data * @v PLAINTEXT Plaintext * @v CIPHERTEXT Ciphertext * @ret test Cipher test */ -#define CIPHER_TEST( name, CIPHER, KEY, IV, PLAINTEXT, CIPHERTEXT ) \ +#define CIPHER_TEST( name, CIPHER, KEY, IV, ADDITIONAL, PLAINTEXT, \ + CIPHERTEXT ) \ 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; \ @@ -68,6 +78,8 @@ struct cipher_test { .key_len = sizeof ( name ## _key ), \ .iv = name ## _iv, \ .iv_len = sizeof ( name ## _iv ), \ + .additional = name ## _additional, \ + .additional_len = sizeof ( name ## _additional ), \ .plaintext = name ## _plaintext, \ .ciphertext = name ## _ciphertext, \ .len = sizeof ( name ## _plaintext ), \ -- 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/tests') 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/tests') 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 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/tests') 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 63fdd9b5816c752120c315c04c1d6878f3c13143 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 9 Nov 2022 16:14:42 +0000 Subject: [tests] Verify ability to reset cipher initialisation vector TLS relies upon the ability to reuse a cipher by resetting only the initialisation vector while reusing the existing key. Add verification of resetting the initialisation vector to the cipher self-tests. Signed-off-by: Michael Brown --- src/tests/cipher_test.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'src/tests') diff --git a/src/tests/cipher_test.c b/src/tests/cipher_test.c index 2ead3c827..0d0eac1d8 100644 --- a/src/tests/cipher_test.c +++ b/src/tests/cipher_test.c @@ -81,6 +81,25 @@ void cipher_encrypt_okx ( struct cipher_test *test, const char *file, okx ( cipher->authsize == test->auth_len, file, line ); cipher_auth ( cipher, ctx, auth ); okx ( memcmp ( auth, test->auth, test->auth_len ) == 0, file, line ); + + /* Reset initialisation vector */ + cipher_setiv ( cipher, ctx, test->iv, test->iv_len ); + + /* Process additional data, if applicable */ + if ( test->additional_len ) { + cipher_encrypt ( cipher, ctx, test->additional, NULL, + test->additional_len ); + } + + /* Perform encryption */ + cipher_encrypt ( cipher, ctx, test->plaintext, ciphertext, len ); + + /* Compare against expected ciphertext */ + okx ( memcmp ( ciphertext, test->ciphertext, len ) == 0, file, line ); + + /* Check authentication tag */ + cipher_auth ( cipher, ctx, auth ); + okx ( memcmp ( auth, test->auth, test->auth_len ) == 0, file, line ); } /** @@ -120,6 +139,25 @@ void cipher_decrypt_okx ( struct cipher_test *test, const char *file, okx ( cipher->authsize == test->auth_len, file, line ); cipher_auth ( cipher, ctx, auth ); okx ( memcmp ( auth, test->auth, test->auth_len ) == 0, file, line ); + + /* Reset initialisation vector */ + cipher_setiv ( cipher, ctx, test->iv, test->iv_len ); + + /* Process additional data, if applicable */ + if ( test->additional_len ) { + cipher_decrypt ( cipher, ctx, test->additional, NULL, + test->additional_len ); + } + + /* Perform decryption */ + cipher_decrypt ( cipher, ctx, test->ciphertext, plaintext, len ); + + /* Compare against expected plaintext */ + okx ( memcmp ( plaintext, test->plaintext, len ) == 0, file, line ); + + /* Check authentication tag */ + cipher_auth ( cipher, ctx, auth ); + okx ( memcmp ( auth, test->auth, test->auth_len ) == 0, file, line ); } /** -- cgit v1.2.3-55-g7522 From f5c829b6f8397c4083bb19b00aa147bd7a628e5e Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 9 Nov 2022 16:50:01 +0000 Subject: [tests] Verify ability to perform in-place encryption and decryption TLS relies upon the ability of ciphers to perform in-place decryption, in order to avoid allocating additional I/O buffers for received data. Add verification of in-place encryption and decryption to the cipher self-tests. Signed-off-by: Michael Brown --- src/tests/cipher_test.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'src/tests') diff --git a/src/tests/cipher_test.c b/src/tests/cipher_test.c index 0d0eac1d8..b7a982752 100644 --- a/src/tests/cipher_test.c +++ b/src/tests/cipher_test.c @@ -91,8 +91,9 @@ void cipher_encrypt_okx ( struct cipher_test *test, const char *file, test->additional_len ); } - /* Perform encryption */ - cipher_encrypt ( cipher, ctx, test->plaintext, ciphertext, len ); + /* Perform in-place encryption */ + memcpy ( ciphertext, test->plaintext, len ); + cipher_encrypt ( cipher, ctx, ciphertext, ciphertext, len ); /* Compare against expected ciphertext */ okx ( memcmp ( ciphertext, test->ciphertext, len ) == 0, file, line ); @@ -149,8 +150,9 @@ void cipher_decrypt_okx ( struct cipher_test *test, const char *file, test->additional_len ); } - /* Perform decryption */ - cipher_decrypt ( cipher, ctx, test->ciphertext, plaintext, len ); + /* Perform in-place decryption */ + memcpy ( plaintext, test->ciphertext, len ); + cipher_decrypt ( cipher, ctx, plaintext, plaintext, len ); /* Compare against expected plaintext */ okx ( memcmp ( plaintext, test->plaintext, len ) == 0, file, line ); -- cgit v1.2.3-55-g7522 From 47af48012e2afaaf56108466fb967009670660bb Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 14 Jan 2023 00:09:20 +0000 Subject: [netdevice] Separate concept of scope ID from network device name index The network device index currently serves two purposes: acting as a sequential index for network device names ("net0", "net1", etc), and acting as an opaque unique integer identifier used in socket address scope IDs. There is no particular need for these usages to be linked, and it can lead to situations in which devices are named unexpectedly. For example: if a system has two network devices "net0" and "net1", a VLAN is created as "net1-42", and then a USB NIC is connected, then the USB NIC will be named "net3" rather than the expected "net2" since the VLAN device "net1-42" will have consumed an index. Separate the usages: rename the "index" field to "scope_id" (matching its one and only use case), and assign the name without reference to the scope ID by finding the first unused name. For consistency, assign the scope ID by similarly finding the first unused scope ID. Signed-off-by: Michael Brown --- src/include/ipxe/netdevice.h | 6 +++--- src/interface/efi/efi_pxe.c | 2 +- src/interface/efi/efi_snp.c | 4 ++-- src/net/ipv4.c | 2 +- src/net/ipv6.c | 12 ++++++------ src/net/ndp.c | 4 ++-- src/net/netdevice.c | 31 +++++++++++++++++-------------- src/net/peerdisc.c | 2 +- src/net/udp/dhcpv6.c | 2 +- src/tests/ipv6_test.c | 4 ++-- 10 files changed, 36 insertions(+), 33 deletions(-) (limited to 'src/tests') diff --git a/src/include/ipxe/netdevice.h b/src/include/ipxe/netdevice.h index 294f7b367..29358dba0 100644 --- a/src/include/ipxe/netdevice.h +++ b/src/include/ipxe/netdevice.h @@ -356,8 +356,8 @@ struct net_device { struct list_head list; /** List of open network devices */ struct list_head open_list; - /** Index of this network device */ - unsigned int index; + /** Scope ID */ + unsigned int scope_id; /** Name of this network device */ char name[NETDEV_NAME_LEN]; /** Underlying hardware device */ @@ -726,7 +726,7 @@ extern void netdev_close ( struct net_device *netdev ); extern void unregister_netdev ( struct net_device *netdev ); extern void netdev_irq ( struct net_device *netdev, int enable ); extern struct net_device * find_netdev ( const char *name ); -extern struct net_device * find_netdev_by_index ( unsigned int index ); +extern struct net_device * find_netdev_by_scope_id ( unsigned int scope_id ); extern struct net_device * find_netdev_by_location ( unsigned int bus_type, unsigned int location ); extern struct net_device * diff --git a/src/interface/efi/efi_pxe.c b/src/interface/efi/efi_pxe.c index 15224a5e4..843ebb5e7 100644 --- a/src/interface/efi/efi_pxe.c +++ b/src/interface/efi/efi_pxe.c @@ -199,7 +199,7 @@ static void efi_pxe_ip_sockaddr ( struct efi_pxe *pxe, EFI_IP_ADDRESS *ip, memset ( sockaddr, 0, sizeof ( *sockaddr ) ); sockaddr->sa.sa_family = pxe->tcpip->sa_family; memcpy ( &sockaddr->se.se_addr, ip, pxe->net->net_addr_len ); - sockaddr->se.se_scope_id = pxe->netdev->index; + sockaddr->se.se_scope_id = pxe->netdev->scope_id; } /** diff --git a/src/interface/efi/efi_snp.c b/src/interface/efi/efi_snp.c index 088a3fdbd..c4f7d4ea8 100644 --- a/src/interface/efi/efi_snp.c +++ b/src/interface/efi/efi_snp.c @@ -934,11 +934,11 @@ static uint8_t efi_undi_checksum ( void *data, size_t len ) { */ static unsigned int efi_undi_ifnum ( struct efi_snp_device *snpdev ) { - /* iPXE network device indexes are one-based (leaving zero + /* iPXE network device scope IDs are one-based (leaving zero * meaning "unspecified"). UNDI interface numbers are * zero-based. */ - return ( snpdev->netdev->index - 1 ); + return ( snpdev->netdev->scope_id - 1 ); } /** diff --git a/src/net/ipv4.c b/src/net/ipv4.c index b9ce5e7f7..b91fa2ad0 100644 --- a/src/net/ipv4.c +++ b/src/net/ipv4.c @@ -163,7 +163,7 @@ static struct ipv4_miniroute * ipv4_route ( unsigned int scope_id, /* If destination is non-global, and the scope ID * matches this network device, then use this route. */ - if ( miniroute->netdev->index == scope_id ) + if ( miniroute->netdev->scope_id == scope_id ) return miniroute; } else { diff --git a/src/net/ipv6.c b/src/net/ipv6.c index 901203c40..ef5e51daa 100644 --- a/src/net/ipv6.c +++ b/src/net/ipv6.c @@ -330,7 +330,7 @@ struct ipv6_miniroute * ipv6_route ( unsigned int scope_id, /* Skip entries with a non-matching scope ID, if * destination specifies a scope ID. */ - if ( scope_id && ( miniroute->netdev->index != scope_id ) ) + if ( scope_id && ( miniroute->netdev->scope_id != scope_id ) ) continue; /* Skip entries that are out of scope */ @@ -789,12 +789,12 @@ static int ipv6_rx ( struct io_buffer *iobuf, struct net_device *netdev, src.sin6.sin6_family = AF_INET6; memcpy ( &src.sin6.sin6_addr, &iphdr->src, sizeof ( src.sin6.sin6_addr ) ); - src.sin6.sin6_scope_id = netdev->index; + src.sin6.sin6_scope_id = netdev->scope_id; memset ( &dest, 0, sizeof ( dest ) ); dest.sin6.sin6_family = AF_INET6; memcpy ( &dest.sin6.sin6_addr, &iphdr->dest, sizeof ( dest.sin6.sin6_addr ) ); - dest.sin6.sin6_scope_id = netdev->index; + dest.sin6.sin6_scope_id = netdev->scope_id; iob_pull ( iobuf, hdrlen ); pshdr_csum = ipv6_pshdr_chksum ( iphdr, iob_len ( iobuf ), next_header, TCPIP_EMPTY_CSUM ); @@ -957,7 +957,7 @@ static const char * ipv6_sock_ntoa ( struct sockaddr *sa ) { /* Identify network device, if applicable */ if ( IN6_IS_ADDR_LINKLOCAL ( in ) || IN6_IS_ADDR_MULTICAST ( in ) ) { - netdev = find_netdev_by_index ( sin6->sin6_scope_id ); + netdev = find_netdev_by_scope_id ( sin6->sin6_scope_id ); netdev_name = ( netdev ? netdev->name : "UNKNOWN" ); } else { netdev_name = NULL; @@ -1020,7 +1020,7 @@ static int ipv6_sock_aton ( const char *string, struct sockaddr *sa ) { rc = -ENODEV; goto err_find_netdev; } - sin6->sin6_scope_id = netdev->index; + sin6->sin6_scope_id = netdev->scope_id; } else if ( IN6_IS_ADDR_LINKLOCAL ( &in ) || IN6_IS_ADDR_MULTICAST ( &in ) ) { @@ -1031,7 +1031,7 @@ static int ipv6_sock_aton ( const char *string, struct sockaddr *sa ) { */ netdev = last_opened_netdev(); if ( netdev ) - sin6->sin6_scope_id = netdev->index; + sin6->sin6_scope_id = netdev->scope_id; } /* Copy IPv6 address portion to socket address */ diff --git a/src/net/ndp.c b/src/net/ndp.c index c8e8ebad3..373a9360b 100644 --- a/src/net/ndp.c +++ b/src/net/ndp.c @@ -140,7 +140,7 @@ static int ndp_tx_request ( struct net_device *netdev, /* Construct multicast destination address */ memset ( &sin6_dest, 0, sizeof ( sin6_dest ) ); sin6_dest.sin6_family = AF_INET6; - sin6_dest.sin6_scope_id = netdev->index; + sin6_dest.sin6_scope_id = netdev->scope_id; ipv6_solicited_node ( &sin6_dest.sin6_addr, net_dest ); /* Construct neighbour header */ @@ -177,7 +177,7 @@ static int ndp_tx_router_solicitation ( struct net_device *netdev ) { /* Construct multicast destination address */ memset ( &sin6_dest, 0, sizeof ( sin6_dest ) ); sin6_dest.sin6_family = AF_INET6; - sin6_dest.sin6_scope_id = netdev->index; + sin6_dest.sin6_scope_id = netdev->scope_id; ipv6_all_routers ( &sin6_dest.sin6_addr ); /* Construct router solicitation */ diff --git a/src/net/netdevice.c b/src/net/netdevice.c index 51d1831cc..597c62285 100644 --- a/src/net/netdevice.c +++ b/src/net/netdevice.c @@ -55,9 +55,6 @@ struct list_head net_devices = LIST_HEAD_INIT ( net_devices ); /** List of open network devices, in reverse order of opening */ static struct list_head open_net_devices = LIST_HEAD_INIT ( open_net_devices ); -/** Network device index */ -static unsigned int netdev_index = 0; - /** Network polling profiler */ static struct profiler net_poll_profiler __profiler = { .name = "net.poll" }; @@ -723,6 +720,7 @@ int register_netdev ( struct net_device *netdev ) { struct ll_protocol *ll_protocol = netdev->ll_protocol; struct net_driver *driver; struct net_device *duplicate; + unsigned int i; uint32_t seed; int rc; @@ -757,12 +755,21 @@ int register_netdev ( struct net_device *netdev ) { goto err_duplicate; } - /* Record device index and create device name */ + /* Assign a unique device name, if not already set */ if ( netdev->name[0] == '\0' ) { - snprintf ( netdev->name, sizeof ( netdev->name ), "net%d", - netdev_index ); + for ( i = 0 ; ; i++ ) { + snprintf ( netdev->name, sizeof ( netdev->name ), + "net%d", i ); + if ( find_netdev ( netdev->name ) == NULL ) + break; + } + } + + /* Assign a unique non-zero scope ID */ + for ( netdev->scope_id = 1 ; ; netdev->scope_id++ ) { + if ( find_netdev_by_scope_id ( netdev->scope_id ) == NULL ) + break; } - netdev->index = ++netdev_index; /* Use least significant bits of the link-layer address to * improve the randomness of the (non-cryptographic) random @@ -916,10 +923,6 @@ void unregister_netdev ( struct net_device *netdev ) { DBGC ( netdev, "NETDEV %s unregistered\n", netdev->name ); list_del ( &netdev->list ); netdev_put ( netdev ); - - /* Reset network device index if no devices remain */ - if ( list_empty ( &net_devices ) ) - netdev_index = 0; } /** Enable or disable interrupts @@ -962,17 +965,17 @@ struct net_device * find_netdev ( const char *name ) { } /** - * Get network device by index + * Get network device by scope ID * * @v index Network device index * @ret netdev Network device, or NULL */ -struct net_device * find_netdev_by_index ( unsigned int index ) { +struct net_device * find_netdev_by_scope_id ( unsigned int scope_id ) { struct net_device *netdev; /* Identify network device by index */ list_for_each_entry ( netdev, &net_devices, list ) { - if ( netdev->index == index ) + if ( netdev->scope_id == scope_id ) return netdev; } diff --git a/src/net/peerdisc.c b/src/net/peerdisc.c index d7e0d2989..86ff94a87 100644 --- a/src/net/peerdisc.c +++ b/src/net/peerdisc.c @@ -189,7 +189,7 @@ static void peerdisc_socket_tx ( const char *uuid, const char *id ) { /* Skip unopened network devices */ if ( ! netdev_is_open ( netdev ) ) continue; - address.st.st_scope_id = netdev->index; + address.st.st_scope_id = netdev->scope_id; /* Discard request (for test purposes) if applicable */ if ( inject_fault ( PEERDISC_DISCARD_RATE ) ) diff --git a/src/net/udp/dhcpv6.c b/src/net/udp/dhcpv6.c index 253032e4e..28c6f7be4 100644 --- a/src/net/udp/dhcpv6.c +++ b/src/net/udp/dhcpv6.c @@ -955,7 +955,7 @@ int start_dhcpv6 ( struct interface *job, struct net_device *netdev, addresses.client.sin6.sin6_port = htons ( DHCPV6_CLIENT_PORT ); addresses.server.sin6.sin6_family = AF_INET6; ipv6_all_dhcp_relay_and_servers ( &addresses.server.sin6.sin6_addr ); - addresses.server.sin6.sin6_scope_id = netdev->index; + addresses.server.sin6.sin6_scope_id = netdev->scope_id; addresses.server.sin6.sin6_port = htons ( DHCPV6_SERVER_PORT ); /* Construct client DUID from system UUID */ diff --git a/src/tests/ipv6_test.c b/src/tests/ipv6_test.c index de8edc8ad..3b7d813a5 100644 --- a/src/tests/ipv6_test.c +++ b/src/tests/ipv6_test.c @@ -127,7 +127,7 @@ static const struct in6_addr sample_multicast = { /** Dummy network device used for routing tests */ static struct net_device ipv6_test_netdev = { .refcnt = REF_INIT ( ref_no_free ), - .index = 42, + .scope_id = 42, .state = NETDEV_OPEN, }; @@ -349,7 +349,7 @@ static void ipv6_route_okx ( struct ipv6_test_table *table, const char *dest, /* Perform routing */ actual = &in_dest; - miniroute = ipv6_route ( ipv6_test_netdev.index, &actual ); + miniroute = ipv6_route ( ipv6_test_netdev.scope_id, &actual ); /* Validate result */ if ( src ) { -- cgit v1.2.3-55-g7522 From 66a2ff442dca892d242e39dddfdbb15eb338ffe2 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 31 Jan 2023 10:17:57 +0000 Subject: [tests] Verify ability to sleep the CPU The self-test suite does not currently ever attempt to sleep the CPU. This is an operation that may fail (e.g. by attempting to execute a privileged instruction while running as a Linux userspace binary, or by halting the CPU with all interrupts disabled). Add a trivial self-test to exercise the ability to sleep the CPU without crashing or halting forever. Inspired-by: Xiaotian Wu Signed-off-by: Michael Brown --- src/tests/nap_test.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/tests/tests.c | 1 + 2 files changed, 54 insertions(+) create mode 100644 src/tests/nap_test.c (limited to 'src/tests') diff --git a/src/tests/nap_test.c b/src/tests/nap_test.c new file mode 100644 index 000000000..16406a880 --- /dev/null +++ b/src/tests/nap_test.c @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2023 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 + * + * CPU sleeping test + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include +#include + +/** + * Perform CPU sleeping self-test + * + */ +static void nap_test_exec ( void ) { + + /* Check that we can sleep without crashing or halting forever */ + cpu_nap(); + ok ( 1 ); +} + +/** CPU sleeping self-test */ +struct self_test nap_test __self_test = { + .name = "nap", + .exec = nap_test_exec, +}; diff --git a/src/tests/tests.c b/src/tests/tests.c index 187037d1b..fbdf562c6 100644 --- a/src/tests/tests.c +++ b/src/tests/tests.c @@ -80,3 +80,4 @@ REQUIRE_OBJECT ( acpi_test ); REQUIRE_OBJECT ( hmac_test ); REQUIRE_OBJECT ( dhe_test ); REQUIRE_OBJECT ( gcm_test ); +REQUIRE_OBJECT ( nap_test ); -- cgit v1.2.3-55-g7522 From 84cb7743906f88a09b7c1f5f8b56e43bb3f7709e Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 6 Feb 2023 21:03:39 +0000 Subject: [test] Include build architecture in test suite banner The test suites for the various architectures are often run back to back, and there is currently nothing to visually distinguish one test run from another. Include the architecture name within the self-test startup banner, to aid in visual identification of test results. Signed-off-by: Michael Brown --- src/tests/test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/tests') diff --git a/src/tests/test.c b/src/tests/test.c index 67bd4cf89..4c49d4c16 100644 --- a/src/tests/test.c +++ b/src/tests/test.c @@ -119,7 +119,7 @@ static int run_all_tests ( void ) { unsigned int total = 0; /* Run all compiled-in self-tests */ - printf ( "Starting self-tests\n" ); + printf ( "Starting %s self-tests\n", _S2 ( ARCH ) ); for_each_table_entry ( tests, SELF_TESTS ) run_tests ( tests ); -- 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/tests') 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 From 33cb56cf1b7a7138542fe18fd86898fdca2e8f0a Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 28 Feb 2023 16:22:19 +0000 Subject: [params] Rename "form parameter" to "request parameter" Prepare for the parameter mechanism to be generalised to specifying request parameters that are passed via mechanisms other than an application/x-www-form-urlencoded form. Signed-off-by: Michael Brown --- src/config/general.h | 2 +- src/core/params.c | 10 +++++----- src/core/parseopt.c | 2 +- src/hci/commands/param_cmd.c | 4 ++-- src/include/ipxe/params.h | 16 ++++++++-------- src/include/ipxe/uri.h | 2 +- src/tests/uri_test.c | 22 +++++++++++----------- 7 files changed, 29 insertions(+), 29 deletions(-) (limited to 'src/tests') diff --git a/src/config/general.h b/src/config/general.h index 72a6a9a1b..e75a2affd 100644 --- a/src/config/general.h +++ b/src/config/general.h @@ -150,7 +150,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); //#define POWEROFF_CMD /* Power off command */ //#define IMAGE_TRUST_CMD /* Image trust management commands */ //#define PCI_CMD /* PCI commands */ -//#define PARAM_CMD /* Form parameter commands */ +//#define PARAM_CMD /* Request parameter commands */ //#define NEIGHBOUR_CMD /* Neighbour management commands */ //#define PING_CMD /* Ping command */ //#define CONSOLE_CMD /* Console command */ diff --git a/src/core/params.c b/src/core/params.c index e1f66acca..23206bef6 100644 --- a/src/core/params.c +++ b/src/core/params.c @@ -25,7 +25,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * - * Form parameters + * Request parameters * */ @@ -37,7 +37,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); static LIST_HEAD ( parameters ); /** - * Free form parameter list + * Free request parameter list * * @v refcnt Reference count */ @@ -60,7 +60,7 @@ static void free_parameters ( struct refcnt *refcnt ) { } /** - * Find form parameter list by name + * Find request parameter list by name * * @v name Parameter list name (may be NULL) * @ret params Parameter list, or NULL if not found @@ -78,7 +78,7 @@ struct parameters * find_parameters ( const char *name ) { } /** - * Create form parameter list + * Create request parameter list * * @v name Parameter list name (may be NULL) * @ret params Parameter list, or NULL on failure @@ -118,7 +118,7 @@ struct parameters * create_parameters ( const char *name ) { } /** - * Add form parameter + * Add request parameter * * @v params Parameter list * @v key Parameter key diff --git a/src/core/parseopt.c b/src/core/parseopt.c index 007080088..1dbfc7aef 100644 --- a/src/core/parseopt.c +++ b/src/core/parseopt.c @@ -302,7 +302,7 @@ int parse_autovivified_setting ( char *text, struct named_setting *setting ) { } /** - * Parse form parameter list name + * Parse request parameter list name * * @v text Text * @ret params Parameter list diff --git a/src/hci/commands/param_cmd.c b/src/hci/commands/param_cmd.c index bff04f2ff..9e3260de1 100644 --- a/src/hci/commands/param_cmd.c +++ b/src/hci/commands/param_cmd.c @@ -25,7 +25,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * - * Form parameter commands + * Request parameter commands * */ @@ -154,7 +154,7 @@ static int param_exec ( int argc, char **argv ) { return rc; } -/** Form parameter commands */ +/** Request parameter commands */ struct command param_commands[] __command = { { .name = "params", diff --git a/src/include/ipxe/params.h b/src/include/ipxe/params.h index dd3292efc..955f57acc 100644 --- a/src/include/ipxe/params.h +++ b/src/include/ipxe/params.h @@ -3,7 +3,7 @@ /** @file * - * Form parameters + * Request parameters * */ @@ -12,7 +12,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include -/** A form parameter list */ +/** A request parameter list */ struct parameters { /** Reference count */ struct refcnt refcnt; @@ -24,9 +24,9 @@ struct parameters { struct list_head entries; }; -/** A form parameter */ +/** A request parameter */ struct parameter { - /** List of form parameters */ + /** List of request parameters */ struct list_head list; /** Key */ const char *key; @@ -35,7 +35,7 @@ struct parameter { }; /** - * Increment form parameter list reference count + * Increment request parameter list reference count * * @v params Parameter list, or NULL * @ret params Parameter list as passed in @@ -47,7 +47,7 @@ params_get ( struct parameters *params ) { } /** - * Decrement form parameter list reference count + * Decrement request parameter list reference count * * @v params Parameter list, or NULL */ @@ -57,7 +57,7 @@ params_put ( struct parameters *params ) { } /** - * Claim ownership of form parameter list + * Claim ownership of request parameter list * * @v params Parameter list * @ret params Parameter list @@ -71,7 +71,7 @@ claim_parameters ( struct parameters *params ) { return params; } -/** Iterate over all form parameters in a list */ +/** Iterate over all request parameters in a list */ #define for_each_param( param, params ) \ list_for_each_entry ( (param), &(params)->entries, list ) diff --git a/src/include/ipxe/uri.h b/src/include/ipxe/uri.h index e5b7c8616..a94b525e1 100644 --- a/src/include/ipxe/uri.h +++ b/src/include/ipxe/uri.h @@ -84,7 +84,7 @@ struct uri { const char *equery; /** Fragment (with original URI encoding) */ const char *efragment; - /** Form parameters */ + /** Request parameters */ struct parameters *params; } __attribute__ (( packed )); diff --git a/src/tests/uri_test.c b/src/tests/uri_test.c index 338f479cd..5e7060323 100644 --- a/src/tests/uri_test.c +++ b/src/tests/uri_test.c @@ -92,7 +92,7 @@ struct uri_churi_test { const char *expected; }; -/** A form parameter URI test list */ +/** A request parameter URI test list */ struct uri_params_test_list { /** Key */ const char *key; @@ -100,7 +100,7 @@ struct uri_params_test_list { const char *value; }; -/** A form parameter URI test */ +/** A request parameter URI test */ struct uri_params_test { /** URI string */ const char *string; @@ -403,9 +403,9 @@ static void uri_churi_okx ( struct uri_churi_test *test, const char *file, #define uri_churi_ok( test ) uri_churi_okx ( test, __FILE__, __LINE__ ) /** - * Report form parameter URI test list result + * Report request parameter URI test list result * - * @v test Form parameter URI test + * @v test Request parameter URI test * @v uri URI * @v file Test code file * @v line Test code line @@ -437,9 +437,9 @@ static void uri_params_list_okx ( struct uri_params_test *test, uri_params_list_okx ( test, __FILE__, __LINE__ ) /** - * Report form parameter URI test result + * Report request parameter URI test result * - * @v test Form parameter URI test + * @v test Request parameter URI test * @v file Test code file * @v line Test code line */ @@ -879,7 +879,7 @@ static struct uri_churi_test uri_churi[] = { } }; -/** Form parameter URI test list */ +/** Request parameter URI test list */ static struct uri_params_test_list uri_params_list[] = { { "vendor", @@ -899,7 +899,7 @@ static struct uri_params_test_list uri_params_list[] = { } }; -/** Form parameter URI test */ +/** Request parameter URI test */ static struct uri_params_test uri_params = { "http://boot.ipxe.org/demo/boot.php##params", { @@ -912,7 +912,7 @@ static struct uri_params_test uri_params = { uri_params_list, }; -/** Named form parameter URI test list */ +/** Named request parameter URI test list */ static struct uri_params_test_list uri_named_params_list[] = { { "mac", @@ -928,7 +928,7 @@ static struct uri_params_test_list uri_named_params_list[] = { } }; -/** Named form parameter URI test */ +/** Named request parameter URI test */ static struct uri_params_test uri_named_params = { "http://192.168.100.4:3001/register##params=foo", { @@ -996,7 +996,7 @@ static void uri_test_exec ( void ) { /* Current working URI tests */ uri_churi_ok ( uri_churi ); - /* Form parameter URI tests */ + /* Request parameter URI tests */ uri_params_ok ( &uri_params ); uri_params_ok ( &uri_named_params ); } -- cgit v1.2.3-55-g7522 From 96bb6ba441653a30729ade38dc6c23bc9e2d2339 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 28 Feb 2023 17:46:13 +0000 Subject: [params] Allow for arbitrary HTTP request headers to be specified Extend the request parameter mechanism to allow for arbitrary HTTP headers to be specified via e.g.: params param --header Referer http://www.example.com imgfetch http://192.168.0.1/script.ipxe##params Signed-off-by: Michael Brown --- src/core/params.c | 11 ++++++++--- src/hci/commands/param_cmd.c | 10 +++++++++- src/include/ipxe/params.h | 11 ++++++++++- src/net/tcp/httpcore.c | 43 ++++++++++++++++++++++++++++++++++--------- src/tests/uri_test.c | 13 ++++++++++++- 5 files changed, 73 insertions(+), 15 deletions(-) (limited to 'src/tests') diff --git a/src/core/params.c b/src/core/params.c index 23206bef6..58c829f62 100644 --- a/src/core/params.c +++ b/src/core/params.c @@ -123,10 +123,12 @@ struct parameters * create_parameters ( const char *name ) { * @v params Parameter list * @v key Parameter key * @v value Parameter value + * @v flags Parameter flags * @ret param Parameter, or NULL on failure */ struct parameter * add_parameter ( struct parameters *params, - const char *key, const char *value ) { + const char *key, const char *value, + unsigned int flags ) { struct parameter *param; size_t key_len; size_t value_len; @@ -147,11 +149,14 @@ struct parameter * add_parameter ( struct parameters *params, param->key = key_copy; strcpy ( value_copy, value ); param->value = value_copy; + param->flags = flags; /* Add to list of parameters */ list_add_tail ( ¶m->list, ¶ms->entries ); - DBGC ( params, "PARAMS \"%s\" added \"%s\"=\"%s\"\n", - params->name, param->key, param->value ); + DBGC ( params, "PARAMS \"%s\" added \"%s\"=\"%s\"%s%s\n", + params->name, param->key, param->value, + ( ( param->flags & PARAMETER_FORM ) ? " (form)" : "" ), + ( ( param->flags & PARAMETER_HEADER ) ? " (header)" : "" ) ); return param; } diff --git a/src/hci/commands/param_cmd.c b/src/hci/commands/param_cmd.c index 9e3260de1..dad99f840 100644 --- a/src/hci/commands/param_cmd.c +++ b/src/hci/commands/param_cmd.c @@ -90,12 +90,16 @@ static int params_exec ( int argc, char **argv ) { struct param_options { /** Parameter list name */ char *params; + /** Parameter is a header */ + int header; }; /** "param" option list */ static struct option_descriptor param_opts[] = { OPTION_DESC ( "params", 'p', required_argument, struct param_options, params, parse_string ), + OPTION_DESC ( "header", 'H', no_argument, + struct param_options, header, parse_flag ), }; /** "param" command descriptor */ @@ -114,6 +118,7 @@ static int param_exec ( int argc, char **argv ) { struct param_options opts; char *key; char *value; + unsigned int flags; struct parameters *params; struct parameter *param; int rc; @@ -132,12 +137,15 @@ static int param_exec ( int argc, char **argv ) { goto err_parse_value; } + /* Construct flags */ + flags = ( opts.header ? PARAMETER_HEADER : PARAMETER_FORM ); + /* Identify parameter list */ if ( ( rc = parse_parameters ( opts.params, ¶ms ) ) != 0 ) goto err_parse_parameters; /* Add parameter */ - param = add_parameter ( params, key, value ); + param = add_parameter ( params, key, value, flags ); if ( ! param ) { rc = -ENOMEM; goto err_add_parameter; diff --git a/src/include/ipxe/params.h b/src/include/ipxe/params.h index 955f57acc..61e46e029 100644 --- a/src/include/ipxe/params.h +++ b/src/include/ipxe/params.h @@ -32,8 +32,16 @@ struct parameter { const char *key; /** Value */ const char *value; + /** Flags */ + unsigned int flags; }; +/** Request parameter is a form parameter */ +#define PARAMETER_FORM 0x0001 + +/** Request parameter is a header parameter */ +#define PARAMETER_HEADER 0x0002 + /** * Increment request parameter list reference count * @@ -78,6 +86,7 @@ claim_parameters ( struct parameters *params ) { extern struct parameters * find_parameters ( const char *name ); extern struct parameters * create_parameters ( const char *name ); extern struct parameter * add_parameter ( struct parameters *params, - const char *key, const char *value ); + const char *key, const char *value, + unsigned int flags ); #endif /* _IPXE_PARAMS_H */ diff --git a/src/net/tcp/httpcore.c b/src/net/tcp/httpcore.c index c970d54bf..9ad39656d 100644 --- a/src/net/tcp/httpcore.c +++ b/src/net/tcp/httpcore.c @@ -830,7 +830,9 @@ static int http_transfer_complete ( struct http_transaction *http ) { */ static int http_format_headers ( struct http_transaction *http, char *buf, size_t len ) { + struct parameters *params = http->uri->params; struct http_request_header *header; + struct parameter *param; size_t used; size_t remaining; char *line; @@ -844,7 +846,7 @@ static int http_format_headers ( struct http_transaction *http, char *buf, DBGC2 ( http, "HTTP %p TX %s\n", http, buf ); used += ssnprintf ( ( buf + used ), ( len - used ), "\r\n" ); - /* Construct all headers */ + /* Construct all fixed headers */ for_each_table_entry ( header, HTTP_REQUEST_HEADERS ) { /* Determine header value length */ @@ -869,6 +871,23 @@ static int http_format_headers ( struct http_transaction *http, char *buf, used += ssnprintf ( ( buf + used ), ( len - used ), "\r\n" ); } + /* Construct parameter headers, if any */ + if ( params ) { + + /* Construct all parameter headers */ + for_each_param ( param, params ) { + + /* Skip non-header parameters */ + if ( ! ( param->flags & PARAMETER_HEADER ) ) + continue; + + /* Add parameter */ + used += ssnprintf ( ( buf + used ), ( len - used ), + "%s: %s\r\n", param->key, + param->value ); + } + } + /* Construct terminating newline */ used += ssnprintf ( ( buf + used ), ( len - used ), "\r\n" ); @@ -1851,14 +1870,15 @@ static struct http_state http_trailers = { */ /** - * Construct HTTP parameter list + * Construct HTTP form parameter list * * @v params Parameter list * @v buf Buffer to contain HTTP POST parameters * @v len Length of buffer * @ret len Length of parameter list (excluding terminating NUL) */ -static size_t http_params ( struct parameters *params, char *buf, size_t len ) { +static size_t http_form_params ( struct parameters *params, char *buf, + size_t len ) { struct parameter *param; ssize_t remaining = len; size_t frag_len; @@ -1867,6 +1887,10 @@ static size_t http_params ( struct parameters *params, char *buf, size_t len ) { len = 0; for_each_param ( param, params ) { + /* Skip non-form parameters */ + if ( ! ( param->flags & PARAMETER_FORM ) ) + continue; + /* Add the "&", if applicable */ if ( len ) { if ( remaining > 0 ) @@ -1920,25 +1944,26 @@ int http_open_uri ( struct interface *xfer, struct uri *uri ) { size_t check_len; int rc; - /* Calculate length of parameter list, if any */ - len = ( params ? http_params ( params, NULL, 0 ) : 0 ); + /* Calculate length of form parameter list, if any */ + len = ( params ? http_form_params ( params, NULL, 0 ) : 0 ); - /* Use POST if and only if there are parameters */ + /* Use POST if and only if there are form parameters */ if ( len ) { /* Use POST */ method = &http_post; type = "application/x-www-form-urlencoded"; - /* Allocate temporary parameter list */ + /* Allocate temporary form parameter list */ data = zalloc ( len + 1 /* NUL */ ); if ( ! data ) { rc = -ENOMEM; goto err_alloc; } - /* Construct temporary parameter list */ - check_len = http_params ( params, data, ( len + 1 /* NUL */ ) ); + /* Construct temporary form parameter list */ + check_len = http_form_params ( params, data, + ( len + 1 /* NUL */ ) ); assert ( check_len == len ); } else { diff --git a/src/tests/uri_test.c b/src/tests/uri_test.c index 5e7060323..9d2f6dba5 100644 --- a/src/tests/uri_test.c +++ b/src/tests/uri_test.c @@ -98,6 +98,8 @@ struct uri_params_test_list { const char *key; /** Value */ const char *value; + /** Flags */ + unsigned int flags; }; /** A request parameter URI test */ @@ -428,6 +430,7 @@ static void uri_params_list_okx ( struct uri_params_test *test, file, line ); okx ( strcmp ( param->value, list->value ) == 0, file, line ); + okx ( param->flags == list->flags, file, line ); list++; } okx ( list->key == NULL, file, line ); @@ -456,7 +459,8 @@ static void uri_params_okx ( struct uri_params_test *test, const char *file, okx ( params != NULL, file, line ); if ( params ) { for ( list = test->list ; list->key ; list++ ) { - param = add_parameter ( params, list->key, list->value); + param = add_parameter ( params, list->key, list->value, + list->flags ); okx ( param != NULL, file, line ); } } @@ -884,18 +888,22 @@ static struct uri_params_test_list uri_params_list[] = { { "vendor", "10ec", + PARAMETER_FORM, }, { "device", "8139", + PARAMETER_FORM, }, { "uuid", "f59fac00-758f-498f-9fe5-87d790045d94", + PARAMETER_HEADER, }, { NULL, NULL, + 0, } }; @@ -917,14 +925,17 @@ static struct uri_params_test_list uri_named_params_list[] = { { "mac", "00:1e:65:80:d3:b6", + PARAMETER_FORM, }, { "serial", "LXTQ20Z1139322762F2000", + PARAMETER_FORM, }, { NULL, NULL, + 0, } }; -- cgit v1.2.3-55-g7522