diff options
| author | Simon Rettberg | 2026-01-28 12:53:53 +0100 |
|---|---|---|
| committer | Simon Rettberg | 2026-01-28 12:53:53 +0100 |
| commit | 8e82785c584dc13e20f9229decb95bd17bbe9cd1 (patch) | |
| tree | a8b359e59196be5b2e3862bed189107f4bc9975f /src/crypto | |
| parent | Merge branch 'master' into openslx (diff) | |
| parent | [prefix] Make unlzma.S compatible with 386 class CPUs (diff) | |
| download | ipxe-openslx.tar.gz ipxe-openslx.tar.xz ipxe-openslx.zip | |
Merge branch 'master' into openslxopenslx
Diffstat (limited to 'src/crypto')
90 files changed, 5458 insertions, 938 deletions
diff --git a/src/crypto/aes.c b/src/crypto/aes.c index 5200e7760..fe6ccb222 100644 --- a/src/crypto/aes.c +++ b/src/crypto/aes.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** @file * diff --git a/src/crypto/asn1.c b/src/crypto/asn1.c index dc9d1c54d..98d5b638f 100644 --- a/src/crypto/asn1.c +++ b/src/crypto/asn1.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <stdint.h> #include <stddef.h> @@ -32,6 +33,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <time.h> #include <ipxe/tables.h> #include <ipxe/image.h> +#include <ipxe/crypto.h> #include <ipxe/asn1.h> /** @file @@ -87,15 +89,18 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * * @v cursor ASN.1 object cursor * @v type Expected type, or ASN1_ANY - * @v extra Additional length not present within partial cursor * @ret len Length of object body, or negative error * * The object cursor will be updated to point to the start of the * object body (i.e. the first byte following the length byte(s)), and * the length of the object body (i.e. the number of bytes until the * following object tag, if any) is returned. + * + * If the expected type is not found, the object cursor will not be + * modified. If any other error occurs, the object cursor will be + * invalidated. */ -int asn1_start ( struct asn1_cursor *cursor, unsigned int type, size_t extra ) { +static int asn1_start ( struct asn1_cursor *cursor, unsigned int type ) { unsigned int len_len; unsigned int len; @@ -103,6 +108,7 @@ int asn1_start ( struct asn1_cursor *cursor, unsigned int type, size_t extra ) { if ( cursor->len < 2 /* Tag byte and first length byte */ ) { if ( cursor->len ) DBGC ( cursor, "ASN1 %p too short\n", cursor ); + asn1_invalidate_cursor ( cursor ); return -EINVAL_ASN1_EMPTY; } @@ -127,6 +133,7 @@ int asn1_start ( struct asn1_cursor *cursor, unsigned int type, size_t extra ) { if ( cursor->len < len_len ) { DBGC ( cursor, "ASN1 %p bad length field length %d (max " "%zd)\n", cursor, len_len, cursor->len ); + asn1_invalidate_cursor ( cursor ); return -EINVAL_ASN1_LEN_LEN; } @@ -137,9 +144,10 @@ int asn1_start ( struct asn1_cursor *cursor, unsigned int type, size_t extra ) { cursor->data++; cursor->len--; } - if ( ( cursor->len + extra ) < len ) { + if ( cursor->len < len ) { DBGC ( cursor, "ASN1 %p bad length %d (max %zd)\n", - cursor, len, ( cursor->len + extra ) ); + cursor, len, cursor->len ); + asn1_invalidate_cursor ( cursor ); return -EINVAL_ASN1_LEN; } @@ -154,22 +162,26 @@ int asn1_start ( struct asn1_cursor *cursor, unsigned int type, size_t extra ) { * @ret rc Return status code * * The object cursor will be updated to point to the body of the - * current ASN.1 object. If any error occurs, the object cursor will - * be invalidated. + * current ASN.1 object. + * + * If any error occurs, the object cursor will be invalidated. */ int asn1_enter ( struct asn1_cursor *cursor, unsigned int type ) { int len; - len = asn1_start ( cursor, type, 0 ); + /* Parse current object */ + len = asn1_start ( cursor, type ); if ( len < 0 ) { asn1_invalidate_cursor ( cursor ); return len; } - cursor->len = len; + /* Update cursor */ + if ( ( ( size_t ) len ) <= cursor->len ) + cursor->len = len; + DBGC ( cursor, "ASN1 %p entered object type %02x (len %x)\n", cursor, type, len ); - return 0; } @@ -181,26 +193,26 @@ int asn1_enter ( struct asn1_cursor *cursor, unsigned int type ) { * @ret rc Return status code * * The object cursor will be updated to point to the next ASN.1 - * object. If any error occurs, the object cursor will not be - * modified. + * object. + * + * If the expected type is not found, the object cursor will not be + * modified. If any other error occurs, the object cursor will be + * invalidated. */ int asn1_skip_if_exists ( struct asn1_cursor *cursor, unsigned int type ) { int len; - len = asn1_start ( cursor, type, 0 ); + /* Parse current object */ + len = asn1_start ( cursor, type ); if ( len < 0 ) return len; + /* Update cursor */ cursor->data += len; cursor->len -= len; + DBGC ( cursor, "ASN1 %p skipped object type %02x (len %x)\n", cursor, type, len ); - - if ( ! cursor->len ) { - DBGC ( cursor, "ASN1 %p reached end of object\n", cursor ); - return -ENOENT; - } - return 0; } @@ -212,8 +224,9 @@ int asn1_skip_if_exists ( struct asn1_cursor *cursor, unsigned int type ) { * @ret rc Return status code * * The object cursor will be updated to point to the next ASN.1 - * object. If any error occurs, the object cursor will be - * invalidated. + * object. + * + * If any error occurs, the object cursor will be invalidated. */ int asn1_skip ( struct asn1_cursor *cursor, unsigned int type ) { int rc; @@ -234,8 +247,9 @@ int asn1_skip ( struct asn1_cursor *cursor, unsigned int type ) { * @ret rc Return status code * * The object cursor will be shrunk to contain only the current ASN.1 - * object. If any error occurs, the object cursor will be - * invalidated. + * object. + * + * If any error occurs, the object cursor will be invalidated. */ int asn1_shrink ( struct asn1_cursor *cursor, unsigned int type ) { struct asn1_cursor temp; @@ -244,7 +258,7 @@ int asn1_shrink ( struct asn1_cursor *cursor, unsigned int type ) { /* Find end of object */ memcpy ( &temp, cursor, sizeof ( temp ) ); - len = asn1_start ( &temp, type, 0 ); + len = asn1_start ( &temp, type ); if ( len < 0 ) { asn1_invalidate_cursor ( cursor ); return len; @@ -288,6 +302,88 @@ int asn1_shrink_any ( struct asn1_cursor *cursor ) { } /** + * Enter ASN.1 bit string + * + * @v cursor ASN.1 cursor + * @v unused Unused bits to fill in (or NULL to require all used) + * @ret rc Return status code + */ +int asn1_enter_bits ( struct asn1_cursor *cursor, unsigned int *unused ) { + const struct { + uint8_t unused; + uint8_t data[0]; + } __attribute__ (( packed )) *bit_string; + const uint8_t *last; + unsigned int unused_bits; + uint8_t unused_mask; + int rc; + + /* Enter bit string */ + if ( ( rc = asn1_enter ( cursor, ASN1_BIT_STRING ) ) != 0 ) + return rc; + + /* Check that bit string header exists */ + if ( cursor->len < sizeof ( *bit_string ) ) { + DBGC ( cursor, "ASN1 %p invalid bit string:\n", cursor ); + DBGC_HDA ( cursor, 0, cursor->data, cursor->len ); + asn1_invalidate_cursor ( cursor ); + return -EINVAL_BIT_STRING; + } + bit_string = cursor->data; + cursor->data = &bit_string->data; + cursor->len -= offsetof ( typeof ( *bit_string ), data ); + unused_bits = bit_string->unused; + + /* Check validity of unused bits */ + unused_mask = ( 0xff >> ( 8 - unused_bits ) ); + last = ( cursor->data + cursor->len - 1 ); + if ( ( unused_bits >= 8 ) || + ( ( unused_bits > 0 ) && ( cursor->len == 0 ) ) || + ( ( *last & unused_mask ) != 0 ) ) { + DBGC ( cursor, "ASN1 %p invalid bit string:\n", cursor ); + DBGC_HDA ( cursor, 0, cursor->data, cursor->len ); + asn1_invalidate_cursor ( cursor ); + return -EINVAL_BIT_STRING; + } + + /* Record or check number of unused bits, as applicable */ + if ( unused ) { + *unused = unused_bits; + } else if ( unused_bits ) { + DBGC ( cursor, "ASN1 %p invalid integral bit string:\n", + cursor ); + DBGC_HDA ( cursor, 0, cursor->data, cursor->len ); + asn1_invalidate_cursor ( cursor ); + return -EINVAL_BIT_STRING; + } + + return 0; +} + +/** + * Enter ASN.1 unsigned integer + * + * @v cursor ASN.1 object cursor + * @ret rc Return status code + */ +int asn1_enter_unsigned ( struct asn1_cursor *cursor ) { + int rc; + + /* Enter integer */ + if ( ( rc = asn1_enter ( cursor, ASN1_INTEGER ) ) != 0 ) + return rc; + + /* Skip initial positive sign byte if applicable */ + if ( ( cursor->len > 1 ) && + ( *( ( uint8_t * ) cursor->data ) == 0x00 ) ) { + cursor->data++; + cursor->len--; + } + + return 0; +} + +/** * Parse value of ASN.1 boolean * * @v cursor ASN.1 object cursor @@ -350,87 +446,6 @@ int asn1_integer ( const struct asn1_cursor *cursor, int *value ) { } /** - * Parse ASN.1 bit string - * - * @v cursor ASN.1 cursor - * @v bits Bit string to fill in - * @ret rc Return status code - */ -int asn1_bit_string ( const struct asn1_cursor *cursor, - struct asn1_bit_string *bits ) { - struct asn1_cursor contents; - const struct { - uint8_t unused; - uint8_t data[0]; - } __attribute__ (( packed )) *bit_string; - size_t len; - unsigned int unused; - uint8_t unused_mask; - const uint8_t *last; - int rc; - - /* Enter bit string */ - memcpy ( &contents, cursor, sizeof ( contents ) ); - if ( ( rc = asn1_enter ( &contents, ASN1_BIT_STRING ) ) != 0 ) { - DBGC ( cursor, "ASN1 %p cannot locate bit string:\n", cursor ); - DBGC_HDA ( cursor, 0, cursor->data, cursor->len ); - return rc; - } - - /* Validity checks */ - if ( contents.len < sizeof ( *bit_string ) ) { - DBGC ( cursor, "ASN1 %p invalid bit string:\n", cursor ); - DBGC_HDA ( cursor, 0, cursor->data, cursor->len ); - return -EINVAL_BIT_STRING; - } - bit_string = contents.data; - len = ( contents.len - offsetof ( typeof ( *bit_string ), data ) ); - unused = bit_string->unused; - unused_mask = ( 0xff >> ( 8 - unused ) ); - last = ( bit_string->data + len - 1 ); - if ( ( unused >= 8 ) || - ( ( unused > 0 ) && ( len == 0 ) ) || - ( ( *last & unused_mask ) != 0 ) ) { - DBGC ( cursor, "ASN1 %p invalid bit string:\n", cursor ); - DBGC_HDA ( cursor, 0, cursor->data, cursor->len ); - return -EINVAL_BIT_STRING; - } - - /* Populate bit string */ - bits->data = &bit_string->data; - bits->len = len; - bits->unused = unused; - - return 0; -} - -/** - * Parse ASN.1 bit string that must be an integral number of bytes - * - * @v cursor ASN.1 cursor - * @v bits Bit string to fill in - * @ret rc Return status code - */ -int asn1_integral_bit_string ( const struct asn1_cursor *cursor, - struct asn1_bit_string *bits ) { - int rc; - - /* Parse bit string */ - if ( ( rc = asn1_bit_string ( cursor, bits ) ) != 0 ) - return rc; - - /* Check that there are no unused bits at end of string */ - if ( bits->unused ) { - DBGC ( cursor, "ASN1 %p invalid integral bit string:\n", - cursor ); - DBGC_HDA ( cursor, 0, cursor->data, cursor->len ); - return -EINVAL_BIT_STRING; - } - - return 0; -} - -/** * Compare two ASN.1 objects * * @v cursor1 ASN.1 object cursor @@ -473,18 +488,26 @@ asn1_find_algorithm ( const struct asn1_cursor *cursor ) { * * @v cursor ASN.1 object cursor * @ret algorithm Algorithm + * @ret params Algorithm parameters, or NULL * @ret rc Return status code */ int asn1_algorithm ( const struct asn1_cursor *cursor, - struct asn1_algorithm **algorithm ) { + struct asn1_algorithm **algorithm, + struct asn1_cursor *params ) { struct asn1_cursor contents; int rc; - /* Enter signatureAlgorithm */ + /* Enter algorithm */ memcpy ( &contents, cursor, sizeof ( contents ) ); asn1_enter ( &contents, ASN1_SEQUENCE ); - /* Enter algorithm */ + /* Get raw parameters, if applicable */ + if ( params ) { + memcpy ( params, &contents, sizeof ( *params ) ); + asn1_skip_any ( params ); + } + + /* Enter algorithm identifier */ if ( ( rc = asn1_enter ( &contents, ASN1_OID ) ) != 0 ) { DBGC ( cursor, "ASN1 %p cannot locate algorithm OID:\n", cursor ); @@ -500,6 +523,14 @@ int asn1_algorithm ( const struct asn1_cursor *cursor, return -ENOTSUP_ALGORITHM; } + /* Parse parameters, if applicable */ + if ( params && (*algorithm)->parse && + ( ( rc = (*algorithm)->parse ( *algorithm, params ) ) != 0 ) ) { + DBGC ( cursor, "ASN1 %p cannot parse %s parameters: %s\n", + cursor, (*algorithm)->name, strerror ( rc ) ); + return rc; + } + return 0; } @@ -515,7 +546,7 @@ int asn1_pubkey_algorithm ( const struct asn1_cursor *cursor, int rc; /* Parse algorithm */ - if ( ( rc = asn1_algorithm ( cursor, algorithm ) ) != 0 ) + if ( ( rc = asn1_algorithm ( cursor, algorithm, NULL ) ) != 0 ) return rc; /* Check algorithm has a public key */ @@ -541,7 +572,7 @@ int asn1_digest_algorithm ( const struct asn1_cursor *cursor, int rc; /* Parse algorithm */ - if ( ( rc = asn1_algorithm ( cursor, algorithm ) ) != 0 ) + if ( ( rc = asn1_algorithm ( cursor, algorithm, NULL ) ) != 0 ) return rc; /* Check algorithm has a digest */ @@ -556,6 +587,34 @@ int asn1_digest_algorithm ( const struct asn1_cursor *cursor, } /** + * Parse ASN.1 OID-identified cipher algorithm + * + * @v cursor ASN.1 object cursor + * @ret algorithm Algorithm + * @ret params Algorithm parameters, or NULL + * @ret rc Return status code + */ +int asn1_cipher_algorithm ( const struct asn1_cursor *cursor, + struct asn1_algorithm **algorithm, + struct asn1_cursor *params ) { + int rc; + + /* Parse algorithm */ + if ( ( rc = asn1_algorithm ( cursor, algorithm, params ) ) != 0 ) + return rc; + + /* Check algorithm has a cipher */ + if ( ! (*algorithm)->cipher ) { + DBGC ( cursor, "ASN1 %p algorithm %s is not a cipher " + "algorithm:\n", cursor, (*algorithm)->name ); + DBGC_HDA ( cursor, 0, cursor->data, cursor->len ); + return -ENOTTY_ALGORITHM; + } + + return 0; +} + +/** * Parse ASN.1 OID-identified signature algorithm * * @v cursor ASN.1 object cursor @@ -567,7 +626,7 @@ int asn1_signature_algorithm ( const struct asn1_cursor *cursor, int rc; /* Parse algorithm */ - if ( ( rc = asn1_algorithm ( cursor, algorithm ) ) != 0 ) + if ( ( rc = asn1_algorithm ( cursor, algorithm, NULL ) ) != 0 ) return rc; /* Check algorithm has a public key */ @@ -590,19 +649,67 @@ int asn1_signature_algorithm ( const struct asn1_cursor *cursor, } /** + * Parse ASN.1 OID-identified elliptic curve algorithm + * + * @v cursor ASN.1 object cursor + * @v wrapper Optional wrapper algorithm, or NULL + * @ret algorithm Algorithm + * @ret rc Return status code + */ +int asn1_curve_algorithm ( const struct asn1_cursor *cursor, + struct asn1_algorithm *wrapper, + struct asn1_algorithm **algorithm ) { + struct asn1_cursor curve; + + /* Elliptic curves are identified as either: + * + * - a wrapper algorithm "id-ecPublicKey" with the actual + * curve specified in the algorithm parameters, or + * + * - a standalone object identifier for the curve + */ + if ( ( wrapper == NULL ) || + ( asn1_check_algorithm ( cursor, wrapper, &curve ) != 0 ) ) { + memcpy ( &curve, cursor, sizeof ( curve ) ); + } + + /* Identify curve */ + asn1_enter ( &curve, ASN1_OID ); + *algorithm = asn1_find_algorithm ( &curve ); + if ( ! *algorithm ) { + DBGC ( cursor, "ASN1 %p unrecognised EC algorithm:\n", + cursor ); + DBGC_HDA ( cursor, 0, cursor->data, cursor->len ); + return -ENOTSUP_ALGORITHM; + } + + /* Check algorithm has an elliptic curve */ + if ( ! (*algorithm)->curve ) { + DBGC ( cursor, "ASN1 %p algorithm %s is not an elliptic curve " + "algorithm:\n", cursor, (*algorithm)->name ); + DBGC_HDA ( cursor, 0, cursor->data, cursor->len ); + return -ENOTTY_ALGORITHM; + } + + return 0; +} + +/** * Check ASN.1 OID-identified algorithm * * @v cursor ASN.1 object cursor * @v expected Expected algorithm + * @ret params Algorithm parameters, or NULL * @ret rc Return status code */ int asn1_check_algorithm ( const struct asn1_cursor *cursor, - struct asn1_algorithm *expected ) { + struct asn1_algorithm *expected, + struct asn1_cursor *params ) { struct asn1_algorithm *actual; int rc; /* Parse algorithm */ - if ( ( rc = asn1_algorithm ( cursor, &actual ) ) != 0 ) + if ( ( rc = asn1_algorithm ( cursor, &actual, params ) ) != 0 ) return rc; /* Check algorithm matches */ @@ -616,6 +723,47 @@ int asn1_check_algorithm ( const struct asn1_cursor *cursor, } /** + * Parse ASN.1 CBC cipher parameters + * + * @v algorithm Algorithm + * @v param Parameters to parse + * @ret rc Return status code + */ +int asn1_parse_cbc ( struct asn1_algorithm *algorithm, + struct asn1_cursor *params ) { + struct cipher_algorithm *cipher = algorithm->cipher; + + /* Sanity check */ + assert ( cipher != NULL ); + + /* Enter parameters */ + asn1_enter ( params, ASN1_OCTET_STRING ); + + /* Check length */ + if ( params->len != cipher->blocksize ) + return -EINVAL; + + return 0; +} + +/** + * Parse ASN.1 GCM cipher parameters + * + * @v algorithm Algorithm + * @v param Parameters to parse + * @ret rc Return status code + */ +int asn1_parse_gcm ( struct asn1_algorithm *algorithm __unused, + struct asn1_cursor *params ) { + + /* Enter parameters */ + asn1_enter ( params, ASN1_SEQUENCE ); + + /* Enter nonce */ + return asn1_enter ( params, ASN1_OCTET_STRING ); +} + +/** * Parse ASN.1 GeneralizedTime * * @v cursor ASN.1 cursor diff --git a/src/crypto/bigint.c b/src/crypto/bigint.c index 656f979e5..5d2f7b560 100644 --- a/src/crypto/bigint.c +++ b/src/crypto/bigint.c @@ -22,11 +22,12 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <stdint.h> #include <string.h> #include <assert.h> -#include <ipxe/profile.h> +#include <stdio.h> #include <ipxe/bigint.h> /** @file @@ -34,21 +35,51 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * Big integer support */ -/** Modular multiplication overall profiler */ -static struct profiler bigint_mod_multiply_profiler __profiler = - { .name = "bigint_mod_multiply" }; +/** Minimum number of least significant bytes included in transcription */ +#define BIGINT_NTOA_LSB_MIN 16 -/** Modular multiplication multiply step profiler */ -static struct profiler bigint_mod_multiply_multiply_profiler __profiler = - { .name = "bigint_mod_multiply.multiply" }; +/** + * Transcribe big integer (for debugging) + * + * @v value0 Element 0 of big integer to be transcribed + * @v size Number of elements + * @ret string Big integer in string form (may be abbreviated) + */ +const char * bigint_ntoa_raw ( const bigint_element_t *value0, + unsigned int size ) { + const bigint_t ( size ) __attribute__ (( may_alias )) + *value = ( ( const void * ) value0 ); + bigint_element_t element; + static char buf[256]; + unsigned int count; + int threshold; + uint8_t byte; + char *tmp; + int i; + + /* Calculate abbreviation threshold, if any */ + count = ( size * sizeof ( element ) ); + threshold = count; + if ( count >= ( ( sizeof ( buf ) - 1 /* NUL */ ) / 2 /* "xx" */ ) ) { + threshold -= ( ( sizeof ( buf ) - 3 /* "..." */ + - ( BIGINT_NTOA_LSB_MIN * 2 /* "xx" */ ) + - 1 /* NUL */ ) / 2 /* "xx" */ ); + } -/** Modular multiplication rescale step profiler */ -static struct profiler bigint_mod_multiply_rescale_profiler __profiler = - { .name = "bigint_mod_multiply.rescale" }; + /* Transcribe bytes, abbreviating with "..." if necessary */ + for ( tmp = buf, i = ( count - 1 ) ; i >= 0 ; i-- ) { + element = value->element[ i / sizeof ( element ) ]; + byte = ( element >> ( 8 * ( i % sizeof ( element ) ) ) ); + tmp += sprintf ( tmp, "%02x", byte ); + if ( i == threshold ) { + tmp += sprintf ( tmp, "..." ); + i = BIGINT_NTOA_LSB_MIN; + } + } + assert ( tmp < ( buf + sizeof ( buf ) ) ); -/** Modular multiplication subtract step profiler */ -static struct profiler bigint_mod_multiply_subtract_profiler __profiler = - { .name = "bigint_mod_multiply.subtract" }; + return buf; +} /** * Conditionally swap big integers (in constant time) @@ -76,72 +107,635 @@ void bigint_swap_raw ( bigint_element_t *first0, bigint_element_t *second0, } /** - * Perform modular multiplication of big integers + * Multiply big integers * * @v multiplicand0 Element 0 of big integer to be multiplied + * @v multiplicand_size Number of elements in multiplicand * @v multiplier0 Element 0 of big integer to be multiplied + * @v multiplier_size Number of elements in multiplier + * @v result0 Element 0 of big integer to hold result + */ +void bigint_multiply_raw ( const bigint_element_t *multiplicand0, + unsigned int multiplicand_size, + const bigint_element_t *multiplier0, + unsigned int multiplier_size, + bigint_element_t *result0 ) { + unsigned int result_size = ( multiplicand_size + multiplier_size ); + const bigint_t ( multiplicand_size ) __attribute__ (( may_alias )) + *multiplicand = ( ( const void * ) multiplicand0 ); + const bigint_t ( multiplier_size ) __attribute__ (( may_alias )) + *multiplier = ( ( const void * ) multiplier0 ); + bigint_t ( result_size ) __attribute__ (( may_alias )) + *result = ( ( void * ) result0 ); + bigint_element_t multiplicand_element; + const bigint_element_t *multiplier_element; + bigint_element_t *result_element; + bigint_element_t carry_element; + unsigned int i; + unsigned int j; + + /* Zero required portion of result + * + * All elements beyond the length of the multiplier will be + * written before they are read, and so do not need to be + * zeroed in advance. + */ + memset ( result, 0, sizeof ( *multiplier ) ); + + /* Multiply integers one element at a time, adding the low + * half of the double-element product directly into the + * result, and maintaining a running single-element carry. + * + * The running carry can never overflow beyond a single + * element. At each step, the calculation we perform is: + * + * carry:result[i+j] := ( ( multiplicand[i] * multiplier[j] ) + * + result[i+j] + carry ) + * + * The maximum value (for n-bit elements) is therefore: + * + * (2^n - 1)*(2^n - 1) + (2^n - 1) + (2^n - 1) = 2^(2n) - 1 + * + * This is precisely the maximum value for a 2n-bit integer, + * and so the carry out remains within the range of an n-bit + * integer, i.e. a single element. + */ + for ( i = 0 ; i < multiplicand_size ; i++ ) { + multiplicand_element = multiplicand->element[i]; + multiplier_element = &multiplier->element[0]; + result_element = &result->element[i]; + carry_element = 0; + for ( j = 0 ; j < multiplier_size ; j++ ) { + bigint_multiply_one ( multiplicand_element, + *(multiplier_element++), + result_element++, + &carry_element ); + } + *result_element = carry_element; + } +} + +/** + * Reduce big integer R^2 modulo N + * * @v modulus0 Element 0 of big integer modulus * @v result0 Element 0 of big integer to hold result - * @v size Number of elements in base, modulus, and result - * @v tmp Temporary working space + * @v size Number of elements in modulus and result + * + * Reduce the value R^2 modulo N, where R=2^n and n is the number of + * bits in the representation of the modulus N, including any leading + * zero bits. */ -void bigint_mod_multiply_raw ( const bigint_element_t *multiplicand0, - const bigint_element_t *multiplier0, - const bigint_element_t *modulus0, - bigint_element_t *result0, - unsigned int size, void *tmp ) { - const bigint_t ( size ) __attribute__ (( may_alias )) *multiplicand = - ( ( const void * ) multiplicand0 ); - const bigint_t ( size ) __attribute__ (( may_alias )) *multiplier = - ( ( const void * ) multiplier0 ); - const bigint_t ( size ) __attribute__ (( may_alias )) *modulus = - ( ( const void * ) modulus0 ); - bigint_t ( size ) __attribute__ (( may_alias )) *result = - ( ( void * ) result0 ); - struct { - bigint_t ( size * 2 ) result; - bigint_t ( size * 2 ) modulus; - } *temp = tmp; - int rotation; - int i; +void bigint_reduce_raw ( const bigint_element_t *modulus0, + bigint_element_t *result0, unsigned int size ) { + const bigint_t ( size ) __attribute__ (( may_alias )) + *modulus = ( ( const void * ) modulus0 ); + bigint_t ( size ) __attribute__ (( may_alias )) + *result = ( ( void * ) result0 ); + const unsigned int width = ( 8 * sizeof ( bigint_element_t ) ); + unsigned int shift; + int max; + int sign; + int msb; + int carry; + + /* We have the constants: + * + * N = modulus + * + * n = number of bits in the modulus (including any leading zeros) + * + * R = 2^n + * + * Let r be the extension of the n-bit result register by a + * separate two's complement sign bit, such that -R <= r < R, + * and define: + * + * x = r * 2^k + * + * as the value being reduced modulo N, where k is a + * non-negative integer bit shift. + * + * We want to reduce the initial value R^2=2^(2n), which we + * may trivially represent using r=1 and k=2n. + * + * We then iterate over decrementing k, maintaining the loop + * invariant: + * + * -N <= r < N + * + * On each iteration we must first double r, to compensate for + * having decremented k: + * + * k' = k - 1 + * + * r' = 2r + * + * x = r * 2^k = 2r * 2^(k-1) = r' * 2^k' + * + * Note that doubling the n-bit result register will create a + * value of n+1 bits: this extra bit needs to be handled + * separately during the calculation. + * + * We then subtract N (if r is currently non-negative) or add + * N (if r is currently negative) to restore the loop + * invariant: + * + * 0 <= r < N => r" = 2r - N => -N <= r" < N + * -N <= r < 0 => r" = 2r + N => -N <= r" < N + * + * Note that since N may use all n bits, the most significant + * bit of the n-bit result register is not a valid two's + * complement sign bit for r: the extra sign bit therefore + * also needs to be handled separately. + * + * Once we reach k=0, we have x=r and therefore: + * + * -N <= x < N + * + * After this last loop iteration (with k=0), we may need to + * add a single multiple of N to ensure that x is positive, + * i.e. lies within the range 0 <= x < N. + * + * Since neither the modulus nor the value R^2 are secret, we + * may elide approximately half of the total number of + * iterations by constructing the initial representation of + * R^2 as r=2^m and k=2n-m (for some m such that 2^m < N). + */ + + /* Initialise x=R^2 */ + memset ( result, 0, sizeof ( *result ) ); + max = ( bigint_max_set_bit ( modulus ) - 2 ); + if ( max < 0 ) { + /* Degenerate case of N=0 or N=1: return a zero result */ + return; + } + bigint_set_bit ( result, max ); + shift = ( ( 2 * size * width ) - max ); + sign = 0; + + /* Iterate as described above */ + while ( shift-- ) { + + /* Calculate 2r, storing extra bit separately */ + msb = bigint_shl ( result ); + + /* Add or subtract N according to current sign */ + if ( sign ) { + carry = bigint_add ( modulus, result ); + } else { + carry = bigint_subtract ( modulus, result ); + } + + /* Calculate new sign of result + * + * We know the result lies in the range -N <= r < N + * and so the tuple (old sign, msb, carry) cannot ever + * take the values (0, 1, 0) or (1, 0, 0). We can + * therefore treat these as don't-care inputs, which + * allows us to simplify the boolean expression by + * ignoring the old sign completely. + */ + assert ( ( sign == msb ) || carry ); + sign = ( msb ^ carry ); + } - /* Start profiling */ - profile_start ( &bigint_mod_multiply_profiler ); + /* Add N to make result positive if necessary */ + if ( sign ) + bigint_add ( modulus, result ); /* Sanity check */ - assert ( sizeof ( *temp ) == bigint_mod_multiply_tmp_len ( modulus ) ); - - /* Perform multiplication */ - profile_start ( &bigint_mod_multiply_multiply_profiler ); - bigint_multiply ( multiplicand, multiplier, &temp->result ); - profile_stop ( &bigint_mod_multiply_multiply_profiler ); - - /* Rescale modulus to match result */ - profile_start ( &bigint_mod_multiply_rescale_profiler ); - bigint_grow ( modulus, &temp->modulus ); - rotation = ( bigint_max_set_bit ( &temp->result ) - - bigint_max_set_bit ( &temp->modulus ) ); - for ( i = 0 ; i < rotation ; i++ ) - bigint_rol ( &temp->modulus ); - profile_stop ( &bigint_mod_multiply_rescale_profiler ); - - /* Subtract multiples of modulus */ - profile_start ( &bigint_mod_multiply_subtract_profiler ); - for ( i = 0 ; i <= rotation ; i++ ) { - if ( bigint_is_geq ( &temp->result, &temp->modulus ) ) - bigint_subtract ( &temp->modulus, &temp->result ); - bigint_ror ( &temp->modulus ); + assert ( ! bigint_is_geq ( result, modulus ) ); +} + +/** + * Compute inverse of odd big integer modulo any power of two + * + * @v invertend0 Element 0 of odd big integer to be inverted + * @v inverse0 Element 0 of big integer to hold result + * @v size Number of elements in invertend and result + */ +void bigint_mod_invert_raw ( const bigint_element_t *invertend0, + bigint_element_t *inverse0, unsigned int size ) { + const bigint_t ( size ) __attribute__ (( may_alias )) + *invertend = ( ( const void * ) invertend0 ); + bigint_t ( size ) __attribute__ (( may_alias )) + *inverse = ( ( void * ) inverse0 ); + bigint_element_t accum; + bigint_element_t bit; + unsigned int i; + + /* Sanity check */ + assert ( bigint_bit_is_set ( invertend, 0 ) ); + + /* Initialise output */ + memset ( inverse, 0xff, sizeof ( *inverse ) ); + + /* Compute inverse modulo 2^(width) + * + * This method is a lightly modified version of the pseudocode + * presented in "A New Algorithm for Inversion mod p^k (Koç, + * 2017)". + * + * Each inner loop iteration calculates one bit of the + * inverse. The residue value is the two's complement + * negation of the value "b" as used by Koç, to allow for + * division by two using a logical right shift (since we have + * no arithmetic right shift operation for big integers). + * + * The residue is stored in the as-yet uncalculated portion of + * the inverse. The size of the residue therefore decreases + * by one element for each outer loop iteration. Trivial + * inspection of the algorithm shows that any higher bits + * could not contribute to the eventual output value, and so + * we may safely reuse storage this way. + * + * Due to the suffix property of inverses mod 2^k, the result + * represents the least significant bits of the inverse modulo + * an arbitrarily large 2^k. + */ + for ( i = size ; i > 0 ; i-- ) { + const bigint_t ( i ) __attribute__ (( may_alias )) + *addend = ( ( const void * ) invertend ); + bigint_t ( i ) __attribute__ (( may_alias )) + *residue = ( ( void * ) inverse ); + + /* Calculate one element's worth of inverse bits */ + for ( accum = 0, bit = 1 ; bit ; bit <<= 1 ) { + if ( bigint_bit_is_set ( residue, 0 ) ) { + accum |= bit; + bigint_add ( addend, residue ); + } + bigint_shr ( residue ); + } + + /* Store in the element no longer required to hold residue */ + inverse->element[ i - 1 ] = accum; + } + + /* Correct order of inverse elements */ + for ( i = 0 ; i < ( size / 2 ) ; i++ ) { + accum = inverse->element[i]; + inverse->element[i] = inverse->element[ size - 1 - i ]; + inverse->element[ size - 1 - i ] = accum; + } +} + +/** + * Perform relaxed Montgomery reduction (REDC) of a big integer + * + * @v modulus0 Element 0 of big integer odd modulus + * @v value0 Element 0 of big integer to be reduced + * @v result0 Element 0 of big integer to hold result + * @v size Number of elements in modulus and result + * @ret carry Carry out + * + * The value to be reduced will be made divisible by the size of the + * modulus while retaining its residue class (i.e. multiples of the + * modulus will be added until the low half of the value is zero). + * + * The result may be expressed as + * + * tR = x + mN + * + * where x is the input value, N is the modulus, R=2^n (where n is the + * number of bits in the representation of the modulus, including any + * leading zero bits), and m is the number of multiples of the modulus + * added to make the result tR divisible by R. + * + * The maximum addend is mN <= (R-1)*N (and such an m can be proven to + * exist since N is limited to being odd and therefore coprime to R). + * + * Since the result of this addition is one bit larger than the input + * value, a carry out bit is also returned. The caller may be able to + * prove that the carry out is always zero, in which case it may be + * safely ignored. + * + * The upper half of the output value (i.e. t) will also be copied to + * the result pointer. It is permissible for the result pointer to + * overlap the lower half of the input value. + * + * External knowledge of constraints on the modulus and the input + * value may be used to prove constraints on the result. The + * constraint on the modulus may be generally expressed as + * + * R > kN + * + * for some positive integer k. The value k=1 is allowed, and simply + * expresses that the modulus fits within the number of bits in its + * own representation. + * + * For classic Montgomery reduction, we have k=1, i.e. R > N and a + * separate constraint that the input value is in the range x < RN. + * This gives the result constraint + * + * tR < RN + (R-1)N + * < 2RN - N + * < 2RN + * t < 2N + * + * A single subtraction of the modulus may therefore be required to + * bring it into the range t < N. + * + * When the input value is known to be a product of two integers A and + * B, with A < aN and B < bN, we get the result constraint + * + * tR < abN^2 + (R-1)N + * < (ab/k)RN + RN - N + * < (1 + ab/k)RN + * t < (1 + ab/k)N + * + * If we have k=a=b=1, i.e. R > N with A < N and B < N, then the + * result is in the range t < 2N and may require a single subtraction + * of the modulus to bring it into the range t < N so that it may be + * used as an input on a subsequent iteration. + * + * If we have k=4 and a=b=2, i.e. R > 4N with A < 2N and B < 2N, then + * the result is in the range t < 2N and may immediately be used as an + * input on a subsequent iteration, without requiring a subtraction. + * + * Larger values of k may be used to allow for larger values of a and + * b, which can be useful to elide intermediate reductions in a + * calculation chain that involves additions and subtractions between + * multiplications (as used in elliptic curve point addition, for + * example). As a general rule: each intermediate addition or + * subtraction will require k to be doubled. + * + * When the input value is known to be a single integer A, with A < aN + * (as used when converting out of Montgomery form), we get the result + * constraint + * + * tR < aN + (R-1)N + * < RN + (a-1)N + * + * If we have a=1, i.e. A < N, then the constraint becomes + * + * tR < RN + * t < N + * + * and so the result is immediately in the range t < N with no + * subtraction of the modulus required. + * + * For any larger value of a, the result value t=N becomes possible. + * Additional external knowledge may potentially be used to prove that + * t=N cannot occur. For example: if the caller is performing modular + * exponentiation with a prime modulus (or, more generally, a modulus + * that is coprime to the base), then there is no way for a non-zero + * base value to end up producing an exact multiple of the modulus. + * If t=N cannot be disproved, then conversion out of Montgomery form + * may require an additional subtraction of the modulus. + */ +int bigint_montgomery_relaxed_raw ( const bigint_element_t *modulus0, + bigint_element_t *value0, + bigint_element_t *result0, + unsigned int size ) { + const bigint_t ( size ) __attribute__ (( may_alias )) + *modulus = ( ( const void * ) modulus0 ); + union { + bigint_t ( size * 2 ) full; + struct { + bigint_t ( size ) low; + bigint_t ( size ) high; + } __attribute__ (( packed )); + } __attribute__ (( may_alias )) *value = ( ( void * ) value0 ); + bigint_t ( size ) __attribute__ (( may_alias )) + *result = ( ( void * ) result0 ); + static bigint_t ( 1 ) cached; + static bigint_t ( 1 ) negmodinv; + bigint_element_t multiple; + bigint_element_t carry; + unsigned int i; + unsigned int j; + int overflow; + + /* Sanity checks */ + assert ( bigint_bit_is_set ( modulus, 0 ) ); + + /* Calculate inverse (or use cached version) */ + if ( cached.element[0] != modulus->element[0] ) { + bigint_mod_invert ( modulus, &negmodinv ); + negmodinv.element[0] = -negmodinv.element[0]; + cached.element[0] = modulus->element[0]; + } + + /* Perform multiprecision Montgomery reduction */ + for ( i = 0 ; i < size ; i++ ) { + + /* Determine scalar multiple for this round */ + multiple = ( value->low.element[i] * negmodinv.element[0] ); + + /* Multiply value to make it divisible by 2^(width*i) */ + carry = 0; + for ( j = 0 ; j < size ; j++ ) { + bigint_multiply_one ( multiple, modulus->element[j], + &value->full.element[ i + j ], + &carry ); + } + + /* Since value is now divisible by 2^(width*i), we + * know that the current low element must have been + * zeroed. + */ + assert ( value->low.element[i] == 0 ); + + /* Store the multiplication carry out in the result, + * avoiding the need to immediately propagate the + * carry through the remaining elements. + */ + result->element[i] = carry; } - profile_stop ( &bigint_mod_multiply_subtract_profiler ); - /* Resize result */ - bigint_shrink ( &temp->result, result ); + /* Add the accumulated carries */ + overflow = bigint_add ( result, &value->high ); + + /* Copy to result buffer */ + bigint_copy ( &value->high, result ); + + return overflow; +} + +/** + * Perform classic Montgomery reduction (REDC) of a big integer + * + * @v modulus0 Element 0 of big integer odd modulus + * @v value0 Element 0 of big integer to be reduced + * @v result0 Element 0 of big integer to hold result + * @v size Number of elements in modulus and result + */ +void bigint_montgomery_raw ( const bigint_element_t *modulus0, + bigint_element_t *value0, + bigint_element_t *result0, + unsigned int size ) { + const bigint_t ( size ) __attribute__ (( may_alias )) + *modulus = ( ( const void * ) modulus0 ); + union { + bigint_t ( size * 2 ) full; + struct { + bigint_t ( size ) low; + bigint_t ( size ) high; + } __attribute__ (( packed )); + } __attribute__ (( may_alias )) *value = ( ( void * ) value0 ); + bigint_t ( size ) __attribute__ (( may_alias )) + *result = ( ( void * ) result0 ); + int overflow; + int underflow; /* Sanity check */ - assert ( bigint_is_geq ( modulus, result ) ); + assert ( ! bigint_is_geq ( &value->high, modulus ) ); + + /* Perform relaxed Montgomery reduction */ + overflow = bigint_montgomery_relaxed ( modulus, &value->full, result ); - /* Stop profiling */ - profile_stop ( &bigint_mod_multiply_profiler ); + /* Conditionally subtract the modulus once */ + underflow = bigint_subtract ( modulus, result ); + bigint_swap ( result, &value->high, ( underflow & ~overflow ) ); + + /* Sanity check */ + assert ( ! bigint_is_geq ( result, modulus ) ); +} + +/** + * Perform generalised exponentiation via a Montgomery ladder + * + * @v result0 Element 0 of result (initialised to identity element) + * @v multiple0 Element 0 of multiple (initialised to generator) + * @v size Number of elements in result and multiple + * @v exponent0 Element 0 of exponent + * @v exponent_size Number of elements in exponent + * @v op Montgomery ladder commutative operation + * @v ctx Operation context (if needed) + * @v tmp Temporary working space (if needed) + * + * The Montgomery ladder may be used to perform any operation that is + * isomorphic to exponentiation, i.e. to compute the result + * + * r = g^e = g * g * g * g * .... * g + * + * for an arbitrary commutative operation "*", generator "g" and + * exponent "e". + * + * The result "r" is computed in constant time (assuming that the + * underlying operation is constant time) in k steps, where k is the + * number of bits in the big integer representation of the exponent. + * + * The result "r" must be initialised to the operation's identity + * element, and the multiple must be initialised to the generator "g". + * On exit, the multiple will contain + * + * m = r * g = g^(e+1) + * + * Note that the terminology used here refers to exponentiation + * defined as repeated multiplication, but that the ladder may equally + * well be used to perform any isomorphic operation (such as + * multiplication defined as repeated addition). + */ +void bigint_ladder_raw ( bigint_element_t *result0, + bigint_element_t *multiple0, unsigned int size, + const bigint_element_t *exponent0, + unsigned int exponent_size, bigint_ladder_op_t *op, + const void *ctx, void *tmp ) { + bigint_t ( size ) __attribute__ (( may_alias )) + *result = ( ( void * ) result0 ); + bigint_t ( size ) __attribute__ (( may_alias )) + *multiple = ( ( void * ) multiple0 ); + const bigint_t ( exponent_size ) __attribute__ (( may_alias )) + *exponent = ( ( const void * ) exponent0 ); + const unsigned int width = ( 8 * sizeof ( bigint_element_t ) ); + unsigned int bit = ( exponent_size * width ); + int toggle = 0; + int previous; + + /* We have two physical storage locations: Rres (the "result" + * register) and Rmul (the "multiple" register). + * + * On entry we have: + * + * Rres = g^0 = 1 (the identity element) + * Rmul = g (the generator) + * + * For calculation purposes, define two virtual registers R[0] + * and R[1], mapped to the underlying physical storage + * locations via a boolean toggle state "t": + * + * R[t] -> Rres + * R[~t] -> Rmul + * + * Define the initial toggle state to be t=0, so that we have: + * + * R[0] = g^0 = 1 (the identity element) + * R[1] = g (the generator) + * + * The Montgomery ladder then iterates over each bit "b" of + * the exponent "e" (from MSB down to LSB), computing: + * + * R[1] := R[0] * R[1], R[0] := R[0] * R[0] (if b=0) + * R[0] := R[1] * R[0], R[1] := R[1] * R[1] (if b=1) + * + * i.e. + * + * R[~b] := R[b] * R[~b], R[b] := R[b] * R[b] + * + * To avoid data-dependent memory access patterns, we + * implement this as: + * + * Rmul := Rres * Rmul + * Rres := Rres * Rres + * + * and update the toggle state "t" (via constant-time + * conditional swaps) as needed to ensure that R[b] always + * maps to Rres and R[~b] always maps to Rmul. + */ + while ( bit-- ) { + + /* Update toggle state t := b + * + * If the new toggle state is not equal to the old + * toggle state, then we must swap R[0] and R[1] (in + * constant time). + */ + previous = toggle; + toggle = bigint_bit_is_set ( exponent, bit ); + bigint_swap ( result, multiple, ( toggle ^ previous ) ); + + /* Calculate Rmul = R[~b] := R[b] * R[~b] = Rres * Rmul */ + op ( result->element, multiple->element, size, ctx, tmp ); + + /* Calculate Rres = R[b] := R[b] * R[b] = Rres * Rres */ + op ( result->element, result->element, size, ctx, tmp ); + } + + /* Reset toggle state to zero */ + bigint_swap ( result, multiple, toggle ); +} + +/** + * Perform modular multiplication as part of a Montgomery ladder + * + * @v operand Element 0 of first input operand (may overlap result) + * @v result Element 0 of second input operand and result + * @v size Number of elements in operands and result + * @v ctx Operation context (odd modulus, or NULL) + * @v tmp Temporary working space + */ +void bigint_mod_exp_ladder ( const bigint_element_t *multiplier0, + bigint_element_t *result0, unsigned int size, + const void *ctx, void *tmp ) { + const bigint_t ( size ) __attribute__ (( may_alias )) + *multiplier = ( ( const void * ) multiplier0 ); + bigint_t ( size ) __attribute__ (( may_alias )) + *result = ( ( void * ) result0 ); + const bigint_t ( size ) __attribute__ (( may_alias )) + *modulus = ( ( const void * ) ctx ); + bigint_t ( size * 2 ) __attribute__ (( may_alias )) + *product = ( ( void * ) tmp ); + + /* Multiply and reduce */ + bigint_multiply ( result, multiplier, product ); + if ( modulus ) { + bigint_montgomery ( modulus, product, result ); + } else { + bigint_shrink ( product, result ); + } } /** @@ -169,25 +763,108 @@ void bigint_mod_exp_raw ( const bigint_element_t *base0, *exponent = ( ( const void * ) exponent0 ); bigint_t ( size ) __attribute__ (( may_alias )) *result = ( ( void * ) result0 ); - size_t mod_multiply_len = bigint_mod_multiply_tmp_len ( modulus ); + const unsigned int width = ( 8 * sizeof ( bigint_element_t ) ); struct { - bigint_t ( size ) base; - bigint_t ( exponent_size ) exponent; - uint8_t mod_multiply[mod_multiply_len]; + struct { + bigint_t ( size ) modulus; + bigint_t ( size ) stash; + }; + union { + bigint_t ( 2 * size ) full; + bigint_t ( size ) low; + } product; } *temp = tmp; - static const uint8_t start[1] = { 0x01 }; + const uint8_t one[1] = { 1 }; + bigint_element_t submask; + unsigned int subsize; + unsigned int scale; + unsigned int i; - memcpy ( &temp->base, base, sizeof ( temp->base ) ); - memcpy ( &temp->exponent, exponent, sizeof ( temp->exponent ) ); - bigint_init ( result, start, sizeof ( start ) ); + /* Sanity check */ + assert ( sizeof ( *temp ) == bigint_mod_exp_tmp_len ( modulus ) ); - while ( ! bigint_is_zero ( &temp->exponent ) ) { - if ( bigint_bit_is_set ( &temp->exponent, 0 ) ) { - bigint_mod_multiply ( result, &temp->base, modulus, - result, temp->mod_multiply ); - } - bigint_ror ( &temp->exponent ); - bigint_mod_multiply ( &temp->base, &temp->base, modulus, - &temp->base, temp->mod_multiply ); + /* Handle degenerate case of zero modulus */ + if ( ! bigint_max_set_bit ( modulus ) ) { + memset ( result, 0, sizeof ( *result ) ); + return; + } + + /* Factor modulus as (N * 2^scale) where N is odd */ + bigint_copy ( modulus, &temp->modulus ); + for ( scale = 0 ; ( ! bigint_bit_is_set ( &temp->modulus, 0 ) ) ; + scale++ ) { + bigint_shr ( &temp->modulus ); + } + subsize = ( ( scale + width - 1 ) / width ); + submask = ( ( 1UL << ( scale % width ) ) - 1 ); + if ( ! submask ) + submask = ~submask; + + /* Calculate (R^2 mod N) */ + bigint_reduce ( &temp->modulus, &temp->stash ); + + /* Initialise result = Montgomery(1, R^2 mod N) */ + bigint_grow ( &temp->stash, &temp->product.full ); + bigint_montgomery ( &temp->modulus, &temp->product.full, result ); + + /* Convert base into Montgomery form */ + bigint_multiply ( base, &temp->stash, &temp->product.full ); + bigint_montgomery ( &temp->modulus, &temp->product.full, + &temp->stash ); + + /* Calculate x1 = base^exponent modulo N */ + bigint_ladder ( result, &temp->stash, exponent, bigint_mod_exp_ladder, + &temp->modulus, &temp->product ); + + /* Convert back out of Montgomery form */ + bigint_grow ( result, &temp->product.full ); + bigint_montgomery_relaxed ( &temp->modulus, &temp->product.full, + result ); + + /* Handle even moduli via Garner's algorithm */ + if ( subsize ) { + const bigint_t ( subsize ) __attribute__ (( may_alias )) + *subbase = ( ( const void * ) base ); + bigint_t ( subsize ) __attribute__ (( may_alias )) + *submodulus = ( ( void * ) &temp->modulus ); + bigint_t ( subsize ) __attribute__ (( may_alias )) + *substash = ( ( void * ) &temp->stash ); + bigint_t ( subsize ) __attribute__ (( may_alias )) + *subresult = ( ( void * ) result ); + union { + bigint_t ( 2 * subsize ) full; + bigint_t ( subsize ) low; + } __attribute__ (( may_alias )) + *subproduct = ( ( void * ) &temp->product.full ); + + /* Calculate x2 = base^exponent modulo 2^k */ + bigint_init ( substash, one, sizeof ( one ) ); + bigint_copy ( subbase, submodulus ); + bigint_ladder ( substash, submodulus, exponent, + bigint_mod_exp_ladder, NULL, subproduct ); + + /* Reconstruct N */ + bigint_copy ( modulus, &temp->modulus ); + for ( i = 0 ; i < scale ; i++ ) + bigint_shr ( &temp->modulus ); + + /* Calculate N^-1 modulo 2^k */ + bigint_mod_invert ( submodulus, &subproduct->low ); + bigint_copy ( &subproduct->low, submodulus ); + + /* Calculate y = (x2 - x1) * N^-1 modulo 2^k */ + bigint_subtract ( subresult, substash ); + bigint_multiply ( substash, submodulus, &subproduct->full ); + subproduct->low.element[ subsize - 1 ] &= submask; + bigint_grow ( &subproduct->low, &temp->stash ); + + /* Reconstruct N */ + bigint_mod_invert ( submodulus, &subproduct->low ); + bigint_copy ( &subproduct->low, submodulus ); + + /* Calculate x = x1 + N * y */ + bigint_multiply ( &temp->modulus, &temp->stash, + &temp->product.full ); + bigint_add ( &temp->product.low, result ); } } diff --git a/src/crypto/cbc.c b/src/crypto/cbc.c index 0ba17ee48..ddba7abd9 100644 --- a/src/crypto/cbc.c +++ b/src/crypto/cbc.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <string.h> #include <assert.h> diff --git a/src/crypto/certstore.c b/src/crypto/certstore.c index 2676c7e1e..8472a2eed 100644 --- a/src/crypto/certstore.c +++ b/src/crypto/certstore.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <string.h> #include <stdlib.h> @@ -44,7 +45,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #undef CERT #define CERT( _index, _path ) \ extern char stored_cert_ ## _index ## _data[]; \ - extern char stored_cert_ ## _index ## _len[]; \ + extern size_t ABS_SYMBOL ( stored_cert_ ## _index ## _len ); \ __asm__ ( ".section \".rodata\", \"a\", " PROGBITS "\n\t" \ "\nstored_cert_" #_index "_data:\n\t" \ ".incbin \"" _path "\"\n\t" \ @@ -59,7 +60,7 @@ CERT_ALL #undef CERT #define CERT( _index, _path ) { \ .data = stored_cert_ ## _index ## _data, \ - .len = ( size_t ) stored_cert_ ## _index ## _len, \ + .len = ABS_VALUE_INIT ( stored_cert_ ## _index ## _len ), \ }, static struct asn1_cursor certstore_raw[] = { CERT_ALL @@ -69,66 +70,28 @@ static struct asn1_cursor certstore_raw[] = { static struct x509_certificate certstore_certs[ sizeof ( certstore_raw ) / sizeof ( certstore_raw[0] ) ]; -/** Certificate store */ -struct x509_chain certstore = { - .refcnt = REF_INIT ( ref_no_free ), - .links = LIST_HEAD_INIT ( certstore.links ), -}; - /** * Mark stored certificate as most recently used * + * @v store Certificate store * @v cert X.509 certificate - * @ret cert X.509 certificate */ -static struct x509_certificate * -certstore_found ( struct x509_certificate *cert ) { +static void certstore_found ( struct x509_chain *store, + struct x509_certificate *cert ) { /* Mark as most recently used */ list_del ( &cert->store.list ); - list_add ( &cert->store.list, &certstore.links ); - DBGC2 ( &certstore, "CERTSTORE found certificate %s\n", + list_add ( &cert->store.list, &store->links ); + DBGC2 ( store, "CERTSTORE found certificate %s\n", x509_name ( cert ) ); - - return cert; -} - -/** - * Find certificate in store - * - * @v raw Raw certificate data - * @ret cert X.509 certificate, or NULL if not found - */ -struct x509_certificate * certstore_find ( struct asn1_cursor *raw ) { - struct x509_certificate *cert; - - /* Search for certificate within store */ - list_for_each_entry ( cert, &certstore.links, store.list ) { - if ( asn1_compare ( raw, &cert->raw ) == 0 ) - return certstore_found ( cert ); - } - return NULL; } -/** - * Find certificate in store corresponding to a private key - * - * @v key Private key - * @ret cert X.509 certificate, or NULL if not found - */ -struct x509_certificate * certstore_find_key ( struct private_key *key ) { - struct x509_certificate *cert; - - /* Search for certificate within store */ - list_for_each_entry ( cert, &certstore.links, store.list ) { - if ( pubkey_match ( cert->signature_algorithm->pubkey, - key->builder.data, key->builder.len, - cert->subject.public_key.raw.data, - cert->subject.public_key.raw.len ) == 0 ) - return certstore_found ( cert ); - } - return NULL; -} +/** Certificate store */ +struct x509_chain certstore = { + .refcnt = REF_INIT ( ref_no_free ), + .links = LIST_HEAD_INIT ( certstore.links ), + .found = certstore_found, +}; /** * Add certificate to store @@ -219,7 +182,7 @@ static void certstore_init ( void ) { /* Skip if certificate already present in store */ raw = &certstore_raw[i]; - if ( ( cert = certstore_find ( raw ) ) != NULL ) { + if ( ( cert = x509_find ( &certstore, raw ) ) != NULL ) { DBGC ( &certstore, "CERTSTORE permanent certificate %d " "is a duplicate of %s\n", i, x509_name ( cert )); continue; @@ -248,6 +211,7 @@ static void certstore_init ( void ) { /** Certificate store initialisation function */ struct init_fn certstore_init_fn __init_fn ( INIT_LATE ) = { + .name = "certstore", .initialise = certstore_init, }; @@ -304,3 +268,9 @@ static int certstore_apply_settings ( void ) { struct settings_applicator certstore_applicator __settings_applicator = { .apply = certstore_apply_settings, }; + +/* Drag in objects via certificate store */ +REQUIRING_SYMBOL ( certstore ); + +/* Drag in alternative certificate sources */ +REQUIRE_OBJECT ( config_certs ); diff --git a/src/crypto/chap.c b/src/crypto/chap.c index c90c16def..008229133 100644 --- a/src/crypto/chap.c +++ b/src/crypto/chap.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <stddef.h> #include <stdlib.h> diff --git a/src/crypto/cms.c b/src/crypto/cms.c index 9511cec8a..4c0f3f5a6 100644 --- a/src/crypto/cms.c +++ b/src/crypto/cms.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** @file * @@ -37,8 +38,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <errno.h> #include <ipxe/asn1.h> #include <ipxe/x509.h> +#include <ipxe/image.h> #include <ipxe/malloc.h> -#include <ipxe/uaccess.h> +#include <ipxe/privkey.h> #include <ipxe/cms.h> /* Disambiguate the various error causes */ @@ -58,60 +60,102 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); __einfo_error ( EINFO_EACCES_NO_SIGNATURES ) #define EINFO_EACCES_NO_SIGNATURES \ __einfo_uniqify ( EINFO_EACCES, 0x05, "No signatures present" ) -#define EINVAL_DIGEST \ - __einfo_error ( EINFO_EINVAL_DIGEST ) -#define EINFO_EINVAL_DIGEST \ - __einfo_uniqify ( EINFO_EINVAL, 0x01, "Not a digest algorithm" ) -#define EINVAL_PUBKEY \ - __einfo_error ( EINFO_EINVAL_PUBKEY ) -#define EINFO_EINVAL_PUBKEY \ - __einfo_uniqify ( EINFO_EINVAL, 0x02, "Not a public-key algorithm" ) -#define ENOTSUP_SIGNEDDATA \ - __einfo_error ( EINFO_ENOTSUP_SIGNEDDATA ) -#define EINFO_ENOTSUP_SIGNEDDATA \ - __einfo_uniqify ( EINFO_ENOTSUP, 0x01, "Not a digital signature" ) - -/** "pkcs7-signedData" object identifier */ +#define EACCES_NO_RECIPIENTS \ + __einfo_error ( EINFO_EACCES_NO_RECIPIENTS ) +#define EINFO_EACCES_NO_RECIPIENTS \ + __einfo_uniqify ( EINFO_EACCES, 0x06, "No usable recipients" ) +#define EACCES_LEN \ + __einfo_error ( EINFO_EACCES_LEN ) +#define EINFO_EACCES_LEN \ + __einfo_uniqify ( EINFO_EACCES, 0x07, "Bad file length" ) +#define EACCES_PAD \ + __einfo_error ( EINFO_EACCES_PAD ) +#define EINFO_EACCES_PAD \ + __einfo_uniqify ( EINFO_EACCES, 0x08, "Bad block padding" ) +#define EACCES_MAC \ + __einfo_error ( EINFO_EACCES_MAC ) +#define EINFO_EACCES_MAC \ + __einfo_uniqify ( EINFO_EACCES, 0x09, "Invalid MAC" ) +#define ENOTSUP_TYPE \ + __einfo_error ( EINFO_ENOTSUP_TYPE ) +#define EINFO_ENOTSUP_TYPE \ + __einfo_uniqify ( EINFO_ENOTSUP, 0x01, "Unrecognised message type" ) + +static int cms_parse_signed ( struct cms_message *cms, + const struct asn1_cursor *raw ); +static int cms_parse_enveloped ( struct cms_message *cms, + const struct asn1_cursor *raw ); + +/** "id-signedData" object identifier */ static uint8_t oid_signeddata[] = { ASN1_OID_SIGNEDDATA }; -/** "pkcs7-signedData" object identifier cursor */ -static struct asn1_cursor oid_signeddata_cursor = - ASN1_CURSOR ( oid_signeddata ); +/** "id-envelopedData" object identifier */ +static uint8_t oid_envelopeddata[] = { ASN1_OID_ENVELOPEDDATA }; + +/** "id-authEnvelopedData" object identifier */ +static uint8_t oid_authenvelopeddata[] = { ASN1_OID_AUTHENVELOPEDDATA }; + +/** CMS message types */ +static struct cms_type cms_types[] = { + { + .name = "signed", + .oid = ASN1_CURSOR ( oid_signeddata ), + .parse = cms_parse_signed, + }, + { + .name = "enveloped", + .oid = ASN1_CURSOR ( oid_envelopeddata ), + .parse = cms_parse_enveloped, + }, + { + .name = "authEnveloped", + .oid = ASN1_CURSOR ( oid_authenvelopeddata ), + .parse = cms_parse_enveloped, + } +}; /** - * Parse CMS signature content type + * Parse CMS message content type * - * @v sig CMS signature + * @v cms CMS message * @v raw ASN.1 cursor * @ret rc Return status code */ -static int cms_parse_content_type ( struct cms_signature *sig, +static int cms_parse_content_type ( struct cms_message *cms, const struct asn1_cursor *raw ) { struct asn1_cursor cursor; + struct cms_type *type; + unsigned int i; /* Enter contentType */ memcpy ( &cursor, raw, sizeof ( cursor ) ); asn1_enter ( &cursor, ASN1_OID ); - /* Check OID is pkcs7-signedData */ - if ( asn1_compare ( &cursor, &oid_signeddata_cursor ) != 0 ) { - DBGC ( sig, "CMS %p does not contain signedData:\n", sig ); - DBGC_HDA ( sig, 0, raw->data, raw->len ); - return -ENOTSUP_SIGNEDDATA; + /* Check for a recognised OID */ + for ( i = 0 ; i < ( sizeof ( cms_types ) / + sizeof ( cms_types[0] ) ) ; i++ ) { + type = &cms_types[i]; + if ( asn1_compare ( &cursor, &type->oid ) == 0 ) { + cms->type = type; + DBGC ( cms, "CMS %p contains %sData\n", + cms, type->name ); + return 0; + } } - DBGC ( sig, "CMS %p contains signedData\n", sig ); - return 0; + DBGC ( cms, "CMS %p is not a recognised message type:\n", cms ); + DBGC_HDA ( cms, 0, raw->data, raw->len ); + return -ENOTSUP_TYPE; } /** - * Parse CMS signature certificate list + * Parse CMS message certificate list * - * @v sig CMS signature + * @v cms CMS message * @v raw ASN.1 cursor * @ret rc Return status code */ -static int cms_parse_certificates ( struct cms_signature *sig, +static int cms_parse_certificates ( struct cms_message *cms, const struct asn1_cursor *raw ) { struct asn1_cursor cursor; struct x509_certificate *cert; @@ -125,16 +169,16 @@ static int cms_parse_certificates ( struct cms_signature *sig, while ( cursor.len ) { /* Add certificate to chain */ - if ( ( rc = x509_append_raw ( sig->certificates, cursor.data, + if ( ( rc = x509_append_raw ( cms->certificates, cursor.data, cursor.len ) ) != 0 ) { - DBGC ( sig, "CMS %p could not append certificate: %s\n", - sig, strerror ( rc) ); - DBGC_HDA ( sig, 0, cursor.data, cursor.len ); + DBGC ( cms, "CMS %p could not append certificate: %s\n", + cms, strerror ( rc) ); + DBGC_HDA ( cms, 0, cursor.data, cursor.len ); return rc; } - cert = x509_last ( sig->certificates ); - DBGC ( sig, "CMS %p found certificate %s\n", - sig, x509_name ( cert ) ); + cert = x509_last ( cms->certificates ); + DBGC ( cms, "CMS %p found certificate %s\n", + cms, x509_name ( cert ) ); /* Move to next certificate */ asn1_skip_any ( &cursor ); @@ -144,44 +188,16 @@ static int cms_parse_certificates ( struct cms_signature *sig, } /** - * Identify CMS signature certificate by issuer and serial number - * - * @v sig CMS signature - * @v issuer Issuer - * @v serial Serial number - * @ret cert X.509 certificate, or NULL if not found - */ -static struct x509_certificate * -cms_find_issuer_serial ( struct cms_signature *sig, - const struct asn1_cursor *issuer, - const struct asn1_cursor *serial ) { - struct x509_link *link; - struct x509_certificate *cert; - - /* Scan through certificate list */ - list_for_each_entry ( link, &sig->certificates->links, list ) { - - /* Check issuer and serial number */ - cert = link->cert; - if ( ( asn1_compare ( issuer, &cert->issuer.raw ) == 0 ) && - ( asn1_compare ( serial, &cert->serial.raw ) == 0 ) ) - return cert; - } - - return NULL; -} - -/** - * Parse CMS signature signer identifier + * Parse CMS message participant identifier * - * @v sig CMS signature - * @v info Signer information to fill in + * @v cms CMS message + * @v part Participant information to fill in * @v raw ASN.1 cursor * @ret rc Return status code */ -static int cms_parse_signer_identifier ( struct cms_signature *sig, - struct cms_signer_info *info, - const struct asn1_cursor *raw ) { +static int cms_parse_identifier ( struct cms_message *cms, + struct cms_participant *part, + const struct asn1_cursor *raw ) { struct asn1_cursor cursor; struct asn1_cursor serial; struct asn1_cursor issuer; @@ -195,46 +211,46 @@ static int cms_parse_signer_identifier ( struct cms_signature *sig, /* Identify issuer */ memcpy ( &issuer, &cursor, sizeof ( issuer ) ); if ( ( rc = asn1_shrink ( &issuer, ASN1_SEQUENCE ) ) != 0 ) { - DBGC ( sig, "CMS %p/%p could not locate issuer: %s\n", - sig, info, strerror ( rc ) ); - DBGC_HDA ( sig, 0, raw->data, raw->len ); + DBGC ( cms, "CMS %p/%p could not locate issuer: %s\n", + cms, part, strerror ( rc ) ); + DBGC_HDA ( cms, 0, raw->data, raw->len ); return rc; } - DBGC ( sig, "CMS %p/%p issuer is:\n", sig, info ); - DBGC_HDA ( sig, 0, issuer.data, issuer.len ); + DBGC ( cms, "CMS %p/%p issuer is:\n", cms, part ); + DBGC_HDA ( cms, 0, issuer.data, issuer.len ); asn1_skip_any ( &cursor ); /* Identify serialNumber */ memcpy ( &serial, &cursor, sizeof ( serial ) ); if ( ( rc = asn1_shrink ( &serial, ASN1_INTEGER ) ) != 0 ) { - DBGC ( sig, "CMS %p/%p could not locate serialNumber: %s\n", - sig, info, strerror ( rc ) ); - DBGC_HDA ( sig, 0, raw->data, raw->len ); + DBGC ( cms, "CMS %p/%p could not locate serialNumber: %s\n", + cms, part, strerror ( rc ) ); + DBGC_HDA ( cms, 0, raw->data, raw->len ); return rc; } - DBGC ( sig, "CMS %p/%p serial number is:\n", sig, info ); - DBGC_HDA ( sig, 0, serial.data, serial.len ); + DBGC ( cms, "CMS %p/%p serial number is:\n", cms, part ); + DBGC_HDA ( cms, 0, serial.data, serial.len ); /* Identify certificate */ - cert = cms_find_issuer_serial ( sig, &issuer, &serial ); + cert = x509_find_issuer_serial ( cms->certificates, &issuer, &serial ); if ( ! cert ) { - DBGC ( sig, "CMS %p/%p could not identify signer's " - "certificate\n", sig, info ); - return -ENOENT; + DBGC ( cms, "CMS %p/%p could not identify certificate\n", + cms, part ); + return ( cms_is_signature ( cms ) ? -ENOENT : 0 ); } /* Append certificate to chain */ - if ( ( rc = x509_append ( info->chain, cert ) ) != 0 ) { - DBGC ( sig, "CMS %p/%p could not append certificate: %s\n", - sig, info, strerror ( rc ) ); + if ( ( rc = x509_append ( part->chain, cert ) ) != 0 ) { + DBGC ( cms, "CMS %p/%p could not append certificate: %s\n", + cms, part, strerror ( rc ) ); return rc; } /* Append remaining certificates to chain */ - if ( ( rc = x509_auto_append ( info->chain, - sig->certificates ) ) != 0 ) { - DBGC ( sig, "CMS %p/%p could not append certificates: %s\n", - sig, info, strerror ( rc ) ); + if ( ( rc = x509_auto_append ( part->chain, + cms->certificates ) ) != 0 ) { + DBGC ( cms, "CMS %p/%p could not append certificates: %s\n", + cms, part, strerror ( rc ) ); return rc; } @@ -242,173 +258,287 @@ static int cms_parse_signer_identifier ( struct cms_signature *sig, } /** - * Parse CMS signature digest algorithm + * Parse CMS message digest algorithm * - * @v sig CMS signature - * @v info Signer information to fill in + * @v cms CMS message + * @v part Participant information to fill in * @v raw ASN.1 cursor * @ret rc Return status code */ -static int cms_parse_digest_algorithm ( struct cms_signature *sig, - struct cms_signer_info *info, +static int cms_parse_digest_algorithm ( struct cms_message *cms, + struct cms_participant *part, const struct asn1_cursor *raw ) { struct asn1_algorithm *algorithm; int rc; /* Identify algorithm */ if ( ( rc = asn1_digest_algorithm ( raw, &algorithm ) ) != 0 ) { - DBGC ( sig, "CMS %p/%p could not identify digest algorithm: " - "%s\n", sig, info, strerror ( rc ) ); - DBGC_HDA ( sig, 0, raw->data, raw->len ); + DBGC ( cms, "CMS %p/%p could not identify digest algorithm: " + "%s\n", cms, part, strerror ( rc ) ); + DBGC_HDA ( cms, 0, raw->data, raw->len ); return rc; } /* Record digest algorithm */ - info->digest = algorithm->digest; - DBGC ( sig, "CMS %p/%p digest algorithm is %s\n", - sig, info, algorithm->name ); + part->digest = algorithm->digest; + DBGC ( cms, "CMS %p/%p digest algorithm is %s\n", + cms, part, algorithm->name ); return 0; } /** - * Parse CMS signature algorithm + * Parse CMS message public-key algorithm * - * @v sig CMS signature - * @v info Signer information to fill in + * @v cms CMS message + * @v part Participant information to fill in * @v raw ASN.1 cursor * @ret rc Return status code */ -static int cms_parse_signature_algorithm ( struct cms_signature *sig, - struct cms_signer_info *info, - const struct asn1_cursor *raw ) { +static int cms_parse_pubkey_algorithm ( struct cms_message *cms, + struct cms_participant *part, + const struct asn1_cursor *raw ) { struct asn1_algorithm *algorithm; int rc; /* Identify algorithm */ if ( ( rc = asn1_pubkey_algorithm ( raw, &algorithm ) ) != 0 ) { - DBGC ( sig, "CMS %p/%p could not identify public-key " - "algorithm: %s\n", sig, info, strerror ( rc ) ); - DBGC_HDA ( sig, 0, raw->data, raw->len ); + DBGC ( cms, "CMS %p/%p could not identify public-key " + "algorithm: %s\n", cms, part, strerror ( rc ) ); + DBGC_HDA ( cms, 0, raw->data, raw->len ); return rc; } - /* Record signature algorithm */ - info->pubkey = algorithm->pubkey; - DBGC ( sig, "CMS %p/%p public-key algorithm is %s\n", - sig, info, algorithm->name ); + /* Record public-key algorithm */ + part->pubkey = algorithm->pubkey; + DBGC ( cms, "CMS %p/%p public-key algorithm is %s\n", + cms, part, algorithm->name ); return 0; } /** - * Parse CMS signature value + * Parse CMS message cipher algorithm * - * @v sig CMS signature - * @v info Signer information to fill in + * @v cms CMS message * @v raw ASN.1 cursor * @ret rc Return status code */ -static int cms_parse_signature_value ( struct cms_signature *sig, - struct cms_signer_info *info, - const struct asn1_cursor *raw ) { - struct asn1_cursor cursor; +static int cms_parse_cipher_algorithm ( struct cms_message *cms, + const struct asn1_cursor *raw ) { + struct asn1_algorithm *algorithm; int rc; - /* Enter signature */ - memcpy ( &cursor, raw, sizeof ( cursor ) ); - if ( ( rc = asn1_enter ( &cursor, ASN1_OCTET_STRING ) ) != 0 ) { - DBGC ( sig, "CMS %p/%p could not locate signature:\n", - sig, info ); - DBGC_HDA ( sig, 0, raw->data, raw->len ); + /* Identify algorithm */ + if ( ( rc = asn1_cipher_algorithm ( raw, &algorithm, + &cms->iv ) ) != 0 ) { + DBGC ( cms, "CMS %p could not identify cipher algorithm: %s\n", + cms, strerror ( rc ) ); + DBGC_HDA ( cms, 0, raw->data, raw->len ); return rc; } - /* Record signature */ - info->signature_len = cursor.len; - info->signature = malloc ( info->signature_len ); - if ( ! info->signature ) - return -ENOMEM; - memcpy ( info->signature, cursor.data, info->signature_len ); - DBGC ( sig, "CMS %p/%p signature value is:\n", sig, info ); - DBGC_HDA ( sig, 0, info->signature, info->signature_len ); + /* Record cipher */ + cms->cipher = algorithm->cipher; + DBGC ( cms, "CMS %p cipher algorithm is %s\n", cms, algorithm->name ); + + return 0; +} + +/** + * Parse CMS message signature or key value + * + * @v cms CMS message + * @v part Participant information to fill in + * @v raw ASN.1 cursor + * @ret rc Return status code + */ +static int cms_parse_value ( struct cms_message *cms, + struct cms_participant *part, + const struct asn1_cursor *raw ) { + int rc; + + /* Enter signature or encryptedKey */ + memcpy ( &part->value, raw, sizeof ( part->value ) ); + if ( ( rc = asn1_enter ( &part->value, ASN1_OCTET_STRING ) ) != 0 ) { + DBGC ( cms, "CMS %p/%p could not locate value:\n", + cms, part ); + DBGC_HDA ( cms, 0, raw->data, raw->len ); + return rc; + } + DBGC ( cms, "CMS %p/%p value is:\n", cms, part ); + DBGC_HDA ( cms, 0, part->value.data, part->value.len ); return 0; } /** - * Parse CMS signature signer information + * Parse CMS message participant information * - * @v sig CMS signature - * @v info Signer information to fill in + * @v cms CMS message + * @v part Participant information to fill in * @v raw ASN.1 cursor * @ret rc Return status code */ -static int cms_parse_signer_info ( struct cms_signature *sig, - struct cms_signer_info *info, +static int cms_parse_participant ( struct cms_message *cms, + struct cms_participant *part, const struct asn1_cursor *raw ) { struct asn1_cursor cursor; int rc; - /* Enter signerInfo */ + /* Enter signerInfo or ktri */ memcpy ( &cursor, raw, sizeof ( cursor ) ); asn1_enter ( &cursor, ASN1_SEQUENCE ); /* Skip version */ asn1_skip ( &cursor, ASN1_INTEGER ); - /* Parse sid */ - if ( ( rc = cms_parse_signer_identifier ( sig, info, &cursor ) ) != 0 ) + /* Parse sid or rid */ + if ( ( rc = cms_parse_identifier ( cms, part, &cursor ) ) != 0 ) return rc; asn1_skip_any ( &cursor ); - /* Parse digestAlgorithm */ - if ( ( rc = cms_parse_digest_algorithm ( sig, info, &cursor ) ) != 0 ) - return rc; - asn1_skip_any ( &cursor ); + /* Parse signature-only objects */ + if ( cms_is_signature ( cms ) ) { - /* Skip signedAttrs, if present */ - asn1_skip_if_exists ( &cursor, ASN1_EXPLICIT_TAG ( 0 ) ); + /* Parse digestAlgorithm */ + if ( ( rc = cms_parse_digest_algorithm ( cms, part, + &cursor ) ) != 0 ) + return rc; + asn1_skip_any ( &cursor ); + + /* Skip signedAttrs, if present */ + asn1_skip_if_exists ( &cursor, ASN1_EXPLICIT_TAG ( 0 ) ); + } - /* Parse signatureAlgorithm */ - if ( ( rc = cms_parse_signature_algorithm ( sig, info, &cursor ) ) != 0) + /* Parse signatureAlgorithm or contentEncryptionAlgorithm */ + if ( ( rc = cms_parse_pubkey_algorithm ( cms, part, &cursor ) ) != 0 ) return rc; asn1_skip_any ( &cursor ); - /* Parse signature */ - if ( ( rc = cms_parse_signature_value ( sig, info, &cursor ) ) != 0 ) + /* Parse signature or encryptedKey */ + if ( ( rc = cms_parse_value ( cms, part, &cursor ) ) != 0 ) return rc; return 0; } /** - * Parse CMS signature from ASN.1 data + * Parse CMS message participants information * - * @v sig CMS signature + * @v cms CMS message * @v raw ASN.1 cursor * @ret rc Return status code */ -static int cms_parse ( struct cms_signature *sig, - const struct asn1_cursor *raw ) { +static int cms_parse_participants ( struct cms_message *cms, + const struct asn1_cursor *raw ) { struct asn1_cursor cursor; - struct cms_signer_info *info; + struct cms_participant *part; int rc; - /* Enter contentInfo */ + /* Enter signerInfos or recipientInfos */ + memcpy ( &cursor, raw, sizeof ( cursor ) ); + asn1_enter ( &cursor, ASN1_SET ); + + /* Add each signerInfo or recipientInfo. Errors are handled + * by ensuring that cms_put() will always be able to free any + * allocated memory. + */ + while ( cursor.len ) { + + /* Allocate participant information block */ + part = zalloc ( sizeof ( *part ) ); + if ( ! part ) + return -ENOMEM; + list_add ( &part->list, &cms->participants ); + part->digest = &digest_null; + part->pubkey = &pubkey_null; + + /* Allocate certificate chain */ + part->chain = x509_alloc_chain(); + if ( ! part->chain ) + return -ENOMEM; + + /* Parse signerInfo or recipientInfo */ + if ( ( rc = cms_parse_participant ( cms, part, + &cursor ) ) != 0 ) + return rc; + asn1_skip_any ( &cursor ); + } + + return 0; +} + +/** + * Parse CMS message encrypted content information + * + * @v cms CMS message + * @v raw ASN.1 cursor + * @ret rc Return status code + */ +static int cms_parse_encrypted ( struct cms_message *cms, + const struct asn1_cursor *raw ) { + struct asn1_cursor cursor; + int rc; + + /* Enter encryptedContentInfo */ memcpy ( &cursor, raw, sizeof ( cursor ) ); asn1_enter ( &cursor, ASN1_SEQUENCE ); - /* Parse contentType */ + /* Skip contentType */ + asn1_skip ( &cursor, ASN1_OID ); - if ( ( rc = cms_parse_content_type ( sig, &cursor ) ) != 0 ) + /* Parse contentEncryptionAlgorithm */ + if ( ( rc = cms_parse_cipher_algorithm ( cms, &cursor ) ) != 0 ) return rc; - asn1_skip_any ( &cursor ); - /* Enter content */ - asn1_enter ( &cursor, ASN1_EXPLICIT_TAG ( 0 ) ); + return 0; +} + +/** + * Parse CMS message MAC + * + * @v cms CMS message + * @v raw ASN.1 cursor + * @ret rc Return status code + */ +static int cms_parse_mac ( struct cms_message *cms, + const struct asn1_cursor *raw ) { + int rc; + + /* Enter mac */ + memcpy ( &cms->mac, raw, sizeof ( cms->mac ) ); + if ( ( rc = asn1_enter ( &cms->mac, ASN1_OCTET_STRING ) ) != 0 ) { + DBGC ( cms, "CMS %p could not locate mac: %s\n", + cms, strerror ( rc ) ); + DBGC_HDA ( cms, 0, raw->data, raw->len ); + return rc; + } + DBGC ( cms, "CMS %p mac is:\n", cms ); + DBGC_HDA ( cms, 0, cms->mac.data, cms->mac.len ); + + return 0; +} + +/** + * Parse CMS signed data + * + * @v cms CMS message + * @v raw ASN.1 cursor + * @ret rc Return status code + */ +static int cms_parse_signed ( struct cms_message *cms, + const struct asn1_cursor *raw ) { + struct asn1_cursor cursor; + int rc; + + /* Allocate certificate list */ + cms->certificates = x509_alloc_chain(); + if ( ! cms->certificates ) + return -ENOMEM; /* Enter signedData */ + memcpy ( &cursor, raw, sizeof ( cursor ) ); asn1_enter ( &cursor, ASN1_SEQUENCE ); /* Skip version */ @@ -421,108 +551,159 @@ static int cms_parse ( struct cms_signature *sig, asn1_skip ( &cursor, ASN1_SEQUENCE ); /* Parse certificates */ - if ( ( rc = cms_parse_certificates ( sig, &cursor ) ) != 0 ) + if ( ( rc = cms_parse_certificates ( cms, &cursor ) ) != 0 ) return rc; asn1_skip_any ( &cursor ); /* Skip crls, if present */ asn1_skip_if_exists ( &cursor, ASN1_EXPLICIT_TAG ( 1 ) ); - /* Enter signerInfos */ - asn1_enter ( &cursor, ASN1_SET ); + /* Parse signerInfos */ + if ( ( rc = cms_parse_participants ( cms, &cursor ) ) != 0 ) + return rc; - /* Add each signerInfo. Errors are handled by ensuring that - * cms_put() will always be able to free any allocated memory. - */ - while ( cursor.len ) { + return 0; +} - /* Allocate signer information block */ - info = zalloc ( sizeof ( *info ) ); - if ( ! info ) - return -ENOMEM; - list_add ( &info->list, &sig->info ); +/** + * Parse CMS enveloped data + * + * @v cms CMS message + * @v raw ASN.1 cursor + * @ret rc Return status code + */ +static int cms_parse_enveloped ( struct cms_message *cms, + const struct asn1_cursor *raw ) { + struct asn1_cursor cursor; + int rc; - /* Allocate certificate chain */ - info->chain = x509_alloc_chain(); - if ( ! info->chain ) - return -ENOMEM; + /* Enter envelopedData or authEnvelopedData */ + memcpy ( &cursor, raw, sizeof ( cursor ) ); + asn1_enter ( &cursor, ASN1_SEQUENCE ); - /* Parse signerInfo */ - if ( ( rc = cms_parse_signer_info ( sig, info, - &cursor ) ) != 0 ) - return rc; - asn1_skip_any ( &cursor ); - } + /* Skip version */ + asn1_skip ( &cursor, ASN1_INTEGER ); + + /* Skip originatorInfo, if present */ + asn1_skip_if_exists ( &cursor, ASN1_IMPLICIT_TAG ( 0 ) ); + + /* Parse recipientInfos */ + if ( ( rc = cms_parse_participants ( cms, &cursor ) ) != 0 ) + return rc; + asn1_skip_any ( &cursor ); + + /* Parse encryptedContentInfo or authEncryptedContentInfo */ + if ( ( rc = cms_parse_encrypted ( cms, &cursor ) ) != 0 ) + return rc; + asn1_skip_any ( &cursor ); + assert ( cms->cipher != NULL ); + + /* Skip unprotectedAttrs or authAttrs, if present */ + asn1_skip_if_exists ( &cursor, ASN1_IMPLICIT_TAG ( 1 ) ); + + /* Parse mac, if present */ + if ( ( cms->cipher->authsize != 0 ) && + ( ( rc = cms_parse_mac ( cms, &cursor ) ) != 0 ) ) + return rc; + + return 0; +} + +/** + * Parse CMS message from ASN.1 data + * + * @v cms CMS message + * @ret rc Return status code + */ +static int cms_parse ( struct cms_message *cms ) { + struct asn1_cursor cursor; + int rc; + + /* Enter contentInfo */ + memcpy ( &cursor, cms->raw, sizeof ( cursor ) ); + asn1_enter ( &cursor, ASN1_SEQUENCE ); + + /* Parse contentType */ + if ( ( rc = cms_parse_content_type ( cms, &cursor ) ) != 0 ) + return rc; + asn1_skip_any ( &cursor ); + + /* Enter content */ + asn1_enter ( &cursor, ASN1_EXPLICIT_TAG ( 0 ) ); + + /* Parse type-specific content */ + if ( ( rc = cms->type->parse ( cms, &cursor ) ) != 0 ) + return rc; return 0; } /** - * Free CMS signature + * Free CMS message * * @v refcnt Reference count */ static void cms_free ( struct refcnt *refcnt ) { - struct cms_signature *sig = - container_of ( refcnt, struct cms_signature, refcnt ); - struct cms_signer_info *info; - struct cms_signer_info *tmp; - - list_for_each_entry_safe ( info, tmp, &sig->info, list ) { - list_del ( &info->list ); - x509_chain_put ( info->chain ); - free ( info->signature ); - free ( info ); - } - x509_chain_put ( sig->certificates ); - free ( sig ); + struct cms_message *cms = + container_of ( refcnt, struct cms_message, refcnt ); + struct cms_participant *part; + struct cms_participant *tmp; + + list_for_each_entry_safe ( part, tmp, &cms->participants, list ) { + list_del ( &part->list ); + x509_chain_put ( part->chain ); + free ( part ); + } + x509_chain_put ( cms->certificates ); + free ( cms->raw ); + free ( cms ); } /** - * Create CMS signature + * Create CMS message * - * @v data Raw signature data - * @v len Length of raw data - * @ret sig CMS signature + * @v image Image + * @ret sig CMS message * @ret rc Return status code * - * On success, the caller holds a reference to the CMS signature, and + * On success, the caller holds a reference to the CMS message, and * is responsible for ultimately calling cms_put(). */ -int cms_signature ( const void *data, size_t len, struct cms_signature **sig ) { - struct asn1_cursor cursor; +int cms_message ( struct image *image, struct cms_message **cms ) { + int next; int rc; - /* Allocate and initialise signature */ - *sig = zalloc ( sizeof ( **sig ) ); - if ( ! *sig ) { + /* Allocate and initialise message */ + *cms = zalloc ( sizeof ( **cms ) ); + if ( ! *cms ) { rc = -ENOMEM; goto err_alloc; } - ref_init ( &(*sig)->refcnt, cms_free ); - INIT_LIST_HEAD ( &(*sig)->info ); - - /* Allocate certificate list */ - (*sig)->certificates = x509_alloc_chain(); - if ( ! (*sig)->certificates ) { - rc = -ENOMEM; - goto err_alloc_chain; + ref_init ( &(*cms)->refcnt, cms_free ); + INIT_LIST_HEAD ( &(*cms)->participants ); + (*cms)->cipher = &cipher_null; + + /* Get raw message data */ + next = image_asn1 ( image, 0, &(*cms)->raw ); + if ( next < 0 ) { + rc = next; + DBGC ( *cms, "CMS %p could not get raw ASN.1 data: %s\n", + *cms, strerror ( rc ) ); + goto err_asn1; } - /* Initialise cursor */ - cursor.data = data; - cursor.len = len; - asn1_shrink_any ( &cursor ); + /* Use only first message in image */ + asn1_shrink_any ( (*cms)->raw ); - /* Parse signature */ - if ( ( rc = cms_parse ( *sig, &cursor ) ) != 0 ) + /* Parse message */ + if ( ( rc = cms_parse ( *cms ) ) != 0 ) goto err_parse; return 0; err_parse: - err_alloc_chain: - cms_put ( *sig ); + err_asn1: + cms_put ( *cms ); err_alloc: return rc; } @@ -530,94 +711,67 @@ int cms_signature ( const void *data, size_t len, struct cms_signature **sig ) { /** * Calculate digest of CMS-signed data * - * @v sig CMS signature - * @v info Signer information + * @v cms CMS message + * @v part Participant information * @v data Signed data * @v len Length of signed data * @v out Digest output */ -static void cms_digest ( struct cms_signature *sig, - struct cms_signer_info *info, - userptr_t data, size_t len, void *out ) { - struct digest_algorithm *digest = info->digest; +static void cms_digest ( struct cms_message *cms, + struct cms_participant *part, + const void *data, size_t len, void *out ) { + struct digest_algorithm *digest = part->digest; uint8_t ctx[ digest->ctxsize ]; - uint8_t block[ digest->blocksize ]; - size_t offset = 0; - size_t frag_len; - /* Initialise digest */ + /* Calculate digest */ digest_init ( digest, ctx ); - - /* Process data one block at a time */ - while ( len ) { - frag_len = len; - if ( frag_len > sizeof ( block ) ) - frag_len = sizeof ( block ); - copy_from_user ( block, data, offset, frag_len ); - digest_update ( digest, ctx, block, frag_len ); - offset += frag_len; - len -= frag_len; - } - - /* Finalise digest */ + digest_update ( digest, ctx, data, len ); digest_final ( digest, ctx, out ); - DBGC ( sig, "CMS %p/%p digest value:\n", sig, info ); - DBGC_HDA ( sig, 0, out, digest->digestsize ); + DBGC ( cms, "CMS %p/%p digest value:\n", cms, part ); + DBGC_HDA ( cms, 0, out, digest->digestsize ); } /** * Verify digest of CMS-signed data * - * @v sig CMS signature - * @v info Signer information + * @v cms CMS message + * @v part Participant information * @v cert Corresponding certificate * @v data Signed data * @v len Length of signed data * @ret rc Return status code */ -static int cms_verify_digest ( struct cms_signature *sig, - struct cms_signer_info *info, +static int cms_verify_digest ( struct cms_message *cms, + struct cms_participant *part, struct x509_certificate *cert, - userptr_t data, size_t len ) { - struct digest_algorithm *digest = info->digest; - struct pubkey_algorithm *pubkey = info->pubkey; - struct x509_public_key *public_key = &cert->subject.public_key; + const void *data, size_t len ) { + struct digest_algorithm *digest = part->digest; + struct pubkey_algorithm *pubkey = part->pubkey; + const struct asn1_cursor *key = &cert->subject.public_key.raw; + const struct asn1_cursor *value = &part->value; uint8_t digest_out[ digest->digestsize ]; - uint8_t ctx[ pubkey->ctxsize ]; int rc; /* Generate digest */ - cms_digest ( sig, info, data, len, digest_out ); - - /* Initialise public-key algorithm */ - if ( ( rc = pubkey_init ( pubkey, ctx, public_key->raw.data, - public_key->raw.len ) ) != 0 ) { - DBGC ( sig, "CMS %p/%p could not initialise public key: %s\n", - sig, info, strerror ( rc ) ); - goto err_init; - } + cms_digest ( cms, part, data, len, digest_out ); /* Verify digest */ - if ( ( rc = pubkey_verify ( pubkey, ctx, digest, digest_out, - info->signature, - info->signature_len ) ) != 0 ) { - DBGC ( sig, "CMS %p/%p signature verification failed: %s\n", - sig, info, strerror ( rc ) ); - goto err_verify; + if ( ( rc = pubkey_verify ( pubkey, key, digest, digest_out, + value ) ) != 0 ) { + DBGC ( cms, "CMS %p/%p signature verification failed: %s\n", + cms, part, strerror ( rc ) ); + return rc; } - err_verify: - pubkey_final ( pubkey, ctx ); - err_init: - return rc; + return 0; } /** - * Verify CMS signature signer information + * Verify CMS message signer * - * @v sig CMS signature - * @v info Signer information + * @v cms CMS message + * @v part Participant information * @v data Signed data * @v len Length of signed data * @v time Time at which to validate certificates @@ -625,42 +779,42 @@ static int cms_verify_digest ( struct cms_signature *sig, * @v root Root certificate list, or NULL to use default * @ret rc Return status code */ -static int cms_verify_signer_info ( struct cms_signature *sig, - struct cms_signer_info *info, - userptr_t data, size_t len, - time_t time, struct x509_chain *store, - struct x509_root *root ) { +static int cms_verify_signer ( struct cms_message *cms, + struct cms_participant *part, + const void *data, size_t len, + time_t time, struct x509_chain *store, + struct x509_root *root ) { struct x509_certificate *cert; int rc; /* Validate certificate chain */ - if ( ( rc = x509_validate_chain ( info->chain, time, store, + if ( ( rc = x509_validate_chain ( part->chain, time, store, root ) ) != 0 ) { - DBGC ( sig, "CMS %p/%p could not validate chain: %s\n", - sig, info, strerror ( rc ) ); + DBGC ( cms, "CMS %p/%p could not validate chain: %s\n", + cms, part, strerror ( rc ) ); return rc; } /* Extract code-signing certificate */ - cert = x509_first ( info->chain ); + cert = x509_first ( part->chain ); assert ( cert != NULL ); /* Check that certificate can create digital signatures */ if ( ! ( cert->extensions.usage.bits & X509_DIGITAL_SIGNATURE ) ) { - DBGC ( sig, "CMS %p/%p certificate cannot create signatures\n", - sig, info ); + DBGC ( cms, "CMS %p/%p certificate cannot create signatures\n", + cms, part ); return -EACCES_NON_SIGNING; } /* Check that certificate can sign code */ if ( ! ( cert->extensions.ext_usage.bits & X509_CODE_SIGNING ) ) { - DBGC ( sig, "CMS %p/%p certificate is not code-signing\n", - sig, info ); + DBGC ( cms, "CMS %p/%p certificate is not code-signing\n", + cms, part ); return -EACCES_NON_CODE_SIGNING; } /* Verify digest */ - if ( ( rc = cms_verify_digest ( sig, info, cert, data, len ) ) != 0 ) + if ( ( rc = cms_verify_digest ( cms, part, cert, data, len ) ) != 0 ) return rc; return 0; @@ -669,30 +823,37 @@ static int cms_verify_signer_info ( struct cms_signature *sig, /** * Verify CMS signature * - * @v sig CMS signature - * @v data Signed data - * @v len Length of signed data + * @v cms CMS message + * @v image Signed image * @v name Required common name, or NULL to check all signatures * @v time Time at which to validate certificates * @v store Certificate store, or NULL to use default * @v root Root certificate list, or NULL to use default * @ret rc Return status code */ -int cms_verify ( struct cms_signature *sig, userptr_t data, size_t len, +int cms_verify ( struct cms_message *cms, struct image *image, const char *name, time_t time, struct x509_chain *store, struct x509_root *root ) { - struct cms_signer_info *info; + struct cms_participant *part; struct x509_certificate *cert; int count = 0; int rc; - /* Verify using all signerInfos */ - list_for_each_entry ( info, &sig->info, list ) { - cert = x509_first ( info->chain ); + /* Mark image as untrusted */ + image_untrust ( image ); + + /* Sanity check */ + if ( ! cms_is_signature ( cms ) ) + return -ENOTTY; + + /* Verify using all signers */ + list_for_each_entry ( part, &cms->participants, list ) { + cert = x509_first ( part->chain ); if ( name && ( x509_check_name ( cert, name ) != 0 ) ) continue; - if ( ( rc = cms_verify_signer_info ( sig, info, data, len, time, - store, root ) ) != 0 ) + if ( ( rc = cms_verify_signer ( cms, part, image->data, + image->len, time, store, + root ) ) != 0 ) return rc; count++; } @@ -700,14 +861,286 @@ int cms_verify ( struct cms_signature *sig, userptr_t data, size_t len, /* Check that we have verified at least one signature */ if ( count == 0 ) { if ( name ) { - DBGC ( sig, "CMS %p had no signatures matching name " - "%s\n", sig, name ); + DBGC ( cms, "CMS %p had no signatures matching name " + "%s\n", cms, name ); return -EACCES_WRONG_NAME; } else { - DBGC ( sig, "CMS %p had no signatures\n", sig ); + DBGC ( cms, "CMS %p had no signatures\n", cms ); return -EACCES_NO_SIGNATURES; } } + /* Mark image as trusted */ + image_trust ( image ); + return 0; } + +/** + * Identify CMS recipient corresponding to private key + * + * @v cms CMS message + * @v private_key Private key + * @ret part Participant information, or NULL if not found + */ +static struct cms_participant * +cms_recipient ( struct cms_message *cms, struct private_key *private_key ) { + struct cms_participant *part; + struct x509_certificate *cert; + + /* Identify certificate (if any) for which we have a private key */ + cert = x509_find_key ( NULL, private_key ); + if ( ! cert ) + return NULL; + + /* Identify corresponding recipient, if any */ + list_for_each_entry ( part, &cms->participants, list ) { + if ( cert == x509_first ( part->chain ) ) + return part; + } + + return NULL; +} + +/** + * Set CMS cipher key + * + * @v cms CMS message + * @v part Participant information + * @v private_key Private key + * @v ctx Cipher context + * @ret rc Return status code + */ +static int cms_cipher_key ( struct cms_message *cms, + struct cms_participant *part, + struct private_key *private_key, void *ctx ) { + struct cipher_algorithm *cipher = cms->cipher; + struct pubkey_algorithm *pubkey = part->pubkey; + const struct asn1_cursor *key = privkey_cursor ( private_key ); + const struct asn1_cursor *value = &part->value; + struct asn1_builder cipher_key = { NULL, 0 }; + int rc; + + /* Decrypt cipher key */ + if ( ( rc = pubkey_decrypt ( pubkey, key, value, + &cipher_key ) ) != 0 ) { + DBGC ( cms, "CMS %p/%p could not decrypt cipher key: %s\n", + cms, part, strerror ( rc ) ); + DBGC_HDA ( cms, 0, value->data, value->len ); + goto err_decrypt; + } + DBGC ( cms, "CMS %p/%p cipher key:\n", cms, part ); + DBGC_HDA ( cms, 0, cipher_key.data, cipher_key.len ); + + /* Set cipher key */ + if ( ( rc = cipher_setkey ( cipher, ctx, cipher_key.data, + cipher_key.len ) ) != 0 ) { + DBGC ( cms, "CMS %p could not set cipher key: %s\n", + cms, strerror ( rc ) ); + goto err_setkey; + } + + /* Set cipher initialization vector */ + cipher_setiv ( cipher, ctx, cms->iv.data, cms->iv.len ); + if ( cms->iv.len ) { + DBGC ( cms, "CMS %p cipher IV:\n", cms ); + DBGC_HDA ( cms, 0, cms->iv.data, cms->iv.len ); + } + + err_setkey: + err_decrypt: + free ( cipher_key.data ); + return rc; +} + +/** + * Initialise cipher for CMS decryption + * + * @v cms CMS message + * @v private_key Private key + * @v ctx Cipher context + * @ret rc Return status code + */ +static int cms_cipher ( struct cms_message *cms, + struct private_key *private_key, void *ctx ) { + struct cms_participant *part; + int rc; + + /* Identify a usable recipient */ + part = cms_recipient ( cms, private_key ); + if ( ! part ) { + DBGC ( cms, "CMS %p had no usable recipients\n", cms ); + return -EACCES_NO_RECIPIENTS; + } + + /* Decrypt and set cipher key */ + if ( ( rc = cms_cipher_key ( cms, part, private_key, ctx ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Check CMS padding + * + * @v cms CMS message + * @v data Final block + * @v len Final block length + * @ret len Padding length, or negative error + */ +static int cms_verify_padding ( struct cms_message *cms, const void *data, + size_t len ) { + struct cipher_algorithm *cipher = cms->cipher; + const uint8_t *pad; + size_t pad_len; + unsigned int i; + + /* Non-block ciphers do not use padding */ + if ( ! is_block_cipher ( cipher ) ) + return 0; + + /* Block padding can never produce an empty file */ + if ( len == 0 ) { + DBGC ( cms, "CMS %p invalid empty padding\n", cms ); + return -EACCES_PAD; + } + + /* Sanity check */ + assert ( len >= cipher->blocksize ); + + /* Extract and verify padding */ + pad = ( data + len - 1 ); + pad_len = *pad; + if ( ( pad_len == 0 ) || ( pad_len > len ) ) { + DBGC ( cms, "CMS %p invalid padding length %zd\n", + cms, pad_len ); + return -EACCES_PAD; + } + for ( i = 0 ; i < pad_len ; i++ ) { + if ( *(pad--) != pad_len ) { + DBGC ( cms, "CMS %p invalid padding\n", cms ); + DBGC_HDA ( cms, 0, ( data + len - pad_len ), pad_len ); + return -EACCES_PAD; + } + } + + return pad_len; +} + +/** + * Decrypt CMS message + * + * @v cms CMS message + * @v image Image to decrypt + * @v name Decrypted image name, or NULL to use default + * @v private_key Private key + * @ret rc Return status code + */ +int cms_decrypt ( struct cms_message *cms, struct image *image, + const char *name, struct private_key *private_key ) { + struct cipher_algorithm *cipher = cms->cipher; + const unsigned int original_flags = image->flags; + uint8_t ctx[ cipher->ctxsize ]; + uint8_t ctxdup[ cipher->ctxsize ]; + uint8_t auth[ cipher->authsize ]; + uint8_t final[ cipher->blocksize ]; + size_t final_len; + size_t bulk_len; + int pad_len; + int rc; + + /* Check block size */ + if ( ( image->len & ( cipher->blocksize - 1 ) ) != 0 ) { + DBGC ( cms, "CMS %p invalid length %zd\n", cms, image->len ); + rc = -EACCES_LEN; + goto err_blocksize; + } + + /* Initialise cipher */ + if ( ( rc = cms_cipher ( cms, private_key, ctx ) ) != 0 ) + goto err_cipher; + + /* Duplicate cipher context for potential reencryption on error */ + memcpy ( ctxdup, ctx, cipher->ctxsize ); + + /* Clear trusted flag before modifying image */ + image_untrust ( image ); + + /* Temporarily unregister image, if applicable */ + if ( original_flags & IMAGE_REGISTERED ) { + image_get ( image ); + unregister_image ( image ); + } + + /* Decrypt all but the final block */ + final_len = ( ( image->len && is_block_cipher ( cipher ) ) ? + cipher->blocksize : 0 ); + bulk_len = ( image->len - final_len ); + cipher_decrypt ( cipher, ctx, image->data, image->rwdata, bulk_len ); + + /* Decrypt final block */ + cipher_decrypt ( cipher, ctx, ( image->data + bulk_len ), final, + final_len ); + + /* Check authentication tag, if applicable */ + cipher_auth ( cipher, ctx, auth ); + if ( ( cms->mac.len != cipher->authsize ) || + ( memcmp ( cms->mac.data, auth, cipher->authsize ) != 0 ) ) { + DBGC ( cms, "CMS %p invalid authentication tag\n", cms ); + DBGC_HDA ( cms, 0, auth, cipher->authsize ); + rc = -EACCES_MAC; + goto err_auth; + } + + /* Check block padding, if applicable */ + if ( ( pad_len = cms_verify_padding ( cms, final, final_len ) ) < 0 ) { + rc = pad_len; + goto err_pad; + } + + /* Update image name. Do this as the last possible failure, so + * that we do not have to include any error-handling code path + * to restore the original image name (which may itself fail). + */ + if ( name ) { + if ( ( rc = image_set_name ( image, name ) ) != 0 ) + goto err_set_name; + } else { + image_strip_suffix ( image ); + } + + /* Overwrite final fragment and strip block padding. Do this + * only once no further failure paths exist, so that we do not + * have to include include any error-handling code path to + * reconstruct the block padding. + */ + memcpy ( ( image->rwdata + bulk_len ), final, final_len ); + image->len -= pad_len; + + /* Clear image type and re-register image, if applicable */ + image->type = NULL; + if ( original_flags & IMAGE_REGISTERED ) { + register_image ( image ); + image_put ( image ); + } + + return 0; + + err_set_name: + err_pad: + err_auth: + /* Reencrypt all overwritten portions. This can be done since + * we have deliberately not overwritten the final block + * containing the potentially invalid (and therefore + * unreproducible) block padding. + */ + cipher_encrypt ( cipher, ctxdup, image->data, image->rwdata, bulk_len ); + if ( original_flags & IMAGE_REGISTERED ) { + register_image ( image ); /* Cannot fail on re-registration */ + image_put ( image ); + } + image->flags = original_flags; + err_cipher: + err_blocksize: + return rc; +} diff --git a/src/crypto/crc32.c b/src/crypto/crc32.c index cfef68c02..9ab4899c6 100644 --- a/src/crypto/crc32.c +++ b/src/crypto/crc32.c @@ -20,6 +20,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/crc32.h> diff --git a/src/crypto/crypto_null.c b/src/crypto/crypto_null.c index 0ad463c3e..8637987b1 100644 --- a/src/crypto/crypto_null.c +++ b/src/crypto/crypto_null.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** * @file @@ -93,53 +94,36 @@ struct cipher_algorithm cipher_null = { .auth = cipher_null_auth, }; -int pubkey_null_init ( void *ctx __unused, const void *key __unused, - size_t key_len __unused ) { +int pubkey_null_encrypt ( const struct asn1_cursor *key __unused, + const struct asn1_cursor *plaintext __unused, + struct asn1_builder *ciphertext __unused ) { return 0; } -size_t pubkey_null_max_len ( void *ctx __unused ) { +int pubkey_null_decrypt ( const struct asn1_cursor *key __unused, + const struct asn1_cursor *ciphertext __unused, + struct asn1_builder *plaintext __unused ) { return 0; } -int pubkey_null_encrypt ( void *ctx __unused, const void *plaintext __unused, - size_t plaintext_len __unused, - void *ciphertext __unused ) { - return 0; -} - -int pubkey_null_decrypt ( void *ctx __unused, const void *ciphertext __unused, - size_t ciphertext_len __unused, - void *plaintext __unused ) { - return 0; -} - -int pubkey_null_sign ( void *ctx __unused, +int pubkey_null_sign ( const struct asn1_cursor *key __unused, struct digest_algorithm *digest __unused, - const void *value __unused, void *signature __unused ) { + const void *value __unused, + struct asn1_builder *signature __unused ) { return 0; } -int pubkey_null_verify ( void *ctx __unused, +int pubkey_null_verify ( const struct asn1_cursor *key __unused, struct digest_algorithm *digest __unused, const void *value __unused, - const void *signature __unused , - size_t signature_len __unused ) { + const struct asn1_cursor *signature __unused ) { return 0; } -void pubkey_null_final ( void *ctx __unused ) { - /* Do nothing */ -} - struct pubkey_algorithm pubkey_null = { .name = "null", - .ctxsize = 0, - .init = pubkey_null_init, - .max_len = pubkey_null_max_len, .encrypt = pubkey_null_encrypt, .decrypt = pubkey_null_decrypt, .sign = pubkey_null_sign, .verify = pubkey_null_verify, - .final = pubkey_null_final, }; diff --git a/src/crypto/deflate.c b/src/crypto/deflate.c index 7ad39ec1b..1d54749e0 100644 --- a/src/crypto/deflate.c +++ b/src/crypto/deflate.c @@ -22,13 +22,13 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <string.h> #include <strings.h> #include <errno.h> #include <assert.h> #include <ctype.h> -#include <ipxe/uaccess.h> #include <ipxe/deflate.h> /** @file @@ -300,24 +300,21 @@ static int deflate_alphabet ( struct deflate *deflate, * Attempt to accumulate bits from input stream * * @v deflate Decompressor - * @v in Compressed input data * @v target Number of bits to accumulate * @ret excess Number of excess bits accumulated (may be negative) */ static int deflate_accumulate ( struct deflate *deflate, - struct deflate_chunk *in, unsigned int target ) { uint8_t byte; while ( deflate->bits < target ) { /* Check for end of input */ - if ( in->offset >= in->len ) + if ( deflate->in == deflate->end ) break; /* Acquire byte from input */ - copy_from_user ( &byte, in->data, in->offset++, - sizeof ( byte ) ); + byte = *(deflate->in++); deflate->accumulator = ( deflate->accumulator | ( byte << deflate->bits ) ); deflate->rotalumucca = ( deflate->rotalumucca | @@ -359,12 +356,10 @@ static int deflate_consume ( struct deflate *deflate, unsigned int count ) { * Attempt to extract a fixed number of bits from input stream * * @v deflate Decompressor - * @v in Compressed input data * @v target Number of bits to extract * @ret data Extracted bits (or negative if not yet accumulated) */ -static int deflate_extract ( struct deflate *deflate, struct deflate_chunk *in, - unsigned int target ) { +static int deflate_extract ( struct deflate *deflate, unsigned int target ) { int excess; int data; @@ -373,7 +368,7 @@ static int deflate_extract ( struct deflate *deflate, struct deflate_chunk *in, return 0; /* Attempt to accumulate bits */ - excess = deflate_accumulate ( deflate, in, target ); + excess = deflate_accumulate ( deflate, target ); if ( excess < 0 ) return excess; @@ -389,12 +384,10 @@ static int deflate_extract ( struct deflate *deflate, struct deflate_chunk *in, * Attempt to decode a Huffman-coded symbol from input stream * * @v deflate Decompressor - * @v in Compressed input data * @v alphabet Huffman alphabet * @ret code Raw code (or negative if not yet accumulated) */ static int deflate_decode ( struct deflate *deflate, - struct deflate_chunk *in, struct deflate_alphabet *alphabet ) { struct deflate_huf_symbols *huf_sym; uint16_t huf; @@ -407,7 +400,7 @@ static int deflate_decode ( struct deflate *deflate, * even if the stream still contains some complete * Huffman-coded symbols. */ - deflate_accumulate ( deflate, in, DEFLATE_HUFFMAN_BITS ); + deflate_accumulate ( deflate, DEFLATE_HUFFMAN_BITS ); /* Normalise the bit-reversed accumulated value to 16 bits */ huf = ( deflate->rotalumucca >> 16 ); @@ -449,24 +442,22 @@ static void deflate_discard_to_byte ( struct deflate *deflate ) { * Copy data to output buffer (if available) * * @v out Output data buffer - * @v start Source data - * @v offset Starting offset within source data + * @v in Input data * @v len Length to copy */ -static void deflate_copy ( struct deflate_chunk *out, - userptr_t start, size_t offset, size_t len ) { - size_t out_offset = out->offset; +static void deflate_copy ( struct deflate_chunk *out, const void *in, + size_t len ) { + const uint8_t *in_byte = in; + uint8_t *out_byte = ( out->data + out->offset ); size_t copy_len; /* Copy data one byte at a time, to allow for overlap */ - if ( out_offset < out->len ) { - copy_len = ( out->len - out_offset ); + if ( out->offset < out->len ) { + copy_len = ( out->len - out->offset ); if ( copy_len > len ) copy_len = len; - while ( copy_len-- ) { - memcpy_user ( out->data, out_offset++, - start, offset++, 1 ); - } + while ( copy_len-- ) + *(out_byte++) = *(in_byte++); } out->offset += len; } @@ -475,7 +466,8 @@ static void deflate_copy ( struct deflate_chunk *out, * Inflate compressed data * * @v deflate Decompressor - * @v in Compressed input data + * @v data Compressed input data + * @v len Length of compressed input data * @v out Output data buffer * @ret rc Return status code * @@ -489,10 +481,13 @@ static void deflate_copy ( struct deflate_chunk *out, * caller can use this to find the length of the decompressed data * before allocating the output data buffer. */ -int deflate_inflate ( struct deflate *deflate, - struct deflate_chunk *in, +int deflate_inflate ( struct deflate *deflate, const void *data, size_t len, struct deflate_chunk *out ) { + /* Store input data pointers */ + deflate->in = data; + deflate->end = ( data + len ); + /* This could be implemented more neatly if gcc offered a * means for enforcing tail recursion. */ @@ -509,7 +504,7 @@ int deflate_inflate ( struct deflate *deflate, int cm; /* Extract header */ - header = deflate_extract ( deflate, in, ZLIB_HEADER_BITS ); + header = deflate_extract ( deflate, ZLIB_HEADER_BITS ); if ( header < 0 ) { deflate->resume = &&zlib_header; return 0; @@ -538,7 +533,7 @@ int deflate_inflate ( struct deflate *deflate, int btype; /* Extract block header */ - header = deflate_extract ( deflate, in, DEFLATE_HEADER_BITS ); + header = deflate_extract ( deflate, DEFLATE_HEADER_BITS ); if ( header < 0 ) { deflate->resume = &&block_header; return 0; @@ -571,17 +566,17 @@ int deflate_inflate ( struct deflate *deflate, } literal_len: { - int len; + int llen; /* Extract LEN field */ - len = deflate_extract ( deflate, in, DEFLATE_LITERAL_LEN_BITS ); - if ( len < 0 ) { + llen = deflate_extract ( deflate, DEFLATE_LITERAL_LEN_BITS ); + if ( llen < 0 ) { deflate->resume = &&literal_len; return 0; } /* Record length of literal data */ - deflate->remaining = len; + deflate->remaining = llen; DBGC2 ( deflate, "DEFLATE %p literal block length %#04zx\n", deflate, deflate->remaining ); } @@ -590,7 +585,7 @@ int deflate_inflate ( struct deflate *deflate, int nlen; /* Extract NLEN field */ - nlen = deflate_extract ( deflate, in, DEFLATE_LITERAL_LEN_BITS); + nlen = deflate_extract ( deflate, DEFLATE_LITERAL_LEN_BITS ); if ( nlen < 0 ) { deflate->resume = &&literal_nlen; return 0; @@ -608,20 +603,20 @@ int deflate_inflate ( struct deflate *deflate, literal_data: { size_t in_remaining; - size_t len; + size_t dlen; /* Calculate available amount of literal data */ - in_remaining = ( in->len - in->offset ); - len = deflate->remaining; - if ( len > in_remaining ) - len = in_remaining; + in_remaining = ( deflate->end - deflate->in ); + dlen = deflate->remaining; + if ( dlen > in_remaining ) + dlen = in_remaining; /* Copy data to output buffer */ - deflate_copy ( out, in->data, in->offset, len ); + deflate_copy ( out, deflate->in, dlen ); /* Consume data from input buffer */ - in->offset += len; - deflate->remaining -= len; + deflate->in += dlen; + deflate->remaining -= dlen; /* Finish processing if we are blocked */ if ( deflate->remaining ) { @@ -657,7 +652,7 @@ int deflate_inflate ( struct deflate *deflate, unsigned int hclen; /* Extract block header */ - header = deflate_extract ( deflate, in, DEFLATE_DYNAMIC_BITS ); + header = deflate_extract ( deflate, DEFLATE_DYNAMIC_BITS ); if ( header < 0 ) { deflate->resume = &&dynamic_header; return 0; @@ -684,7 +679,7 @@ int deflate_inflate ( struct deflate *deflate, } dynamic_codelen: { - int len; + int clen; unsigned int index; int rc; @@ -692,18 +687,18 @@ int deflate_inflate ( struct deflate *deflate, while ( deflate->length_index < deflate->length_target ) { /* Extract code length length */ - len = deflate_extract ( deflate, in, - DEFLATE_CODELEN_BITS ); - if ( len < 0 ) { + clen = deflate_extract ( deflate, + DEFLATE_CODELEN_BITS ); + if ( clen < 0 ) { deflate->resume = &&dynamic_codelen; return 0; } /* Store code length */ index = deflate_codelen_map[deflate->length_index++]; - deflate_set_length ( deflate, index, len ); + deflate_set_length ( deflate, index, clen ); DBGCP ( deflate, "DEFLATE %p codelen for %d is %d\n", - deflate, index, len ); + deflate, index, clen ); } /* Generate code length alphabet */ @@ -722,25 +717,25 @@ int deflate_inflate ( struct deflate *deflate, } dynamic_litlen_distance: { - int len; + int clen; int index; /* Decode literal/length/distance code length */ - len = deflate_decode ( deflate, in, &deflate->distance_codelen); - if ( len < 0 ) { + clen = deflate_decode ( deflate, &deflate->distance_codelen ); + if ( clen < 0 ) { deflate->resume = &&dynamic_litlen_distance; return 0; } /* Prepare for extra bits */ - if ( len < 16 ) { - deflate->length = len; + if ( clen < 16 ) { + deflate->length = clen; deflate->extra_bits = 0; deflate->dup_len = 1; } else { static const uint8_t dup_len[3] = { 3, 3, 11 }; static const uint8_t extra_bits[3] = { 2, 3, 7 }; - index = ( len - 16 ); + index = ( clen - 16 ); deflate->dup_len = dup_len[index]; deflate->extra_bits = extra_bits[index]; if ( index ) @@ -753,7 +748,7 @@ int deflate_inflate ( struct deflate *deflate, unsigned int dup_len; /* Extract extra bits */ - extra = deflate_extract ( deflate, in, deflate->extra_bits ); + extra = deflate_extract ( deflate, deflate->extra_bits ); if ( extra < 0 ) { deflate->resume = &&dynamic_litlen_distance_extra; return 0; @@ -830,7 +825,7 @@ int deflate_inflate ( struct deflate *deflate, while ( 1 ) { /* Decode Huffman code */ - code = deflate_decode ( deflate, in, &deflate->litlen ); + code = deflate_decode ( deflate, &deflate->litlen ); if ( code < 0 ) { deflate->resume = &&lzhuf_litlen; return 0; @@ -844,8 +839,7 @@ int deflate_inflate ( struct deflate *deflate, DBGCP ( deflate, "DEFLATE %p literal %#02x " "('%c')\n", deflate, byte, ( isprint ( byte ) ? byte : '.' ) ); - deflate_copy ( out, virt_to_user ( &byte ), 0, - sizeof ( byte ) ); + deflate_copy ( out, &byte, sizeof ( byte ) ); } else if ( code == DEFLATE_LITLEN_END ) { @@ -876,7 +870,7 @@ int deflate_inflate ( struct deflate *deflate, int extra; /* Extract extra bits */ - extra = deflate_extract ( deflate, in, deflate->extra_bits ); + extra = deflate_extract ( deflate, deflate->extra_bits ); if ( extra < 0 ) { deflate->resume = &&lzhuf_litlen_extra; return 0; @@ -892,8 +886,7 @@ int deflate_inflate ( struct deflate *deflate, unsigned int bits; /* Decode Huffman code */ - code = deflate_decode ( deflate, in, - &deflate->distance_codelen ); + code = deflate_decode ( deflate, &deflate->distance_codelen ); if ( code < 0 ) { deflate->resume = &&lzhuf_distance; return 0; @@ -914,7 +907,7 @@ int deflate_inflate ( struct deflate *deflate, size_t dup_distance; /* Extract extra bits */ - extra = deflate_extract ( deflate, in, deflate->extra_bits ); + extra = deflate_extract ( deflate, deflate->extra_bits ); if ( extra < 0 ) { deflate->resume = &&lzhuf_distance_extra; return 0; @@ -934,7 +927,7 @@ int deflate_inflate ( struct deflate *deflate, } /* Copy data, allowing for overlap */ - deflate_copy ( out, out->data, ( out->offset - dup_distance ), + deflate_copy ( out, ( out->data + out->offset - dup_distance ), dup_len ); /* Process next literal/length symbol */ @@ -972,7 +965,7 @@ int deflate_inflate ( struct deflate *deflate, * cases involved in calling deflate_extract() to * obtain a full 32 bits. */ - excess = deflate_accumulate ( deflate, in, ZLIB_ADLER32_BITS ); + excess = deflate_accumulate ( deflate, ZLIB_ADLER32_BITS ); if ( excess < 0 ) { deflate->resume = &&zlib_adler32; return 0; diff --git a/src/crypto/des.c b/src/crypto/des.c index 6918bec3e..206f78d50 100644 --- a/src/crypto/des.c +++ b/src/crypto/des.c @@ -369,6 +369,7 @@ static void des_init ( void ) { /** Initialisation function */ struct init_fn des_init_fn __init_fn ( INIT_NORMAL ) = { + .name = "des", .initialise = des_init, }; diff --git a/src/crypto/dhe.c b/src/crypto/dhe.c index 2da107d24..2785a500b 100644 --- a/src/crypto/dhe.c +++ b/src/crypto/dhe.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** @file * @@ -57,8 +58,7 @@ int dhe_key ( const void *modulus, size_t len, const void *generator, unsigned int size = bigint_required_size ( len ); unsigned int private_size = bigint_required_size ( private_len ); bigint_t ( size ) *mod; - bigint_t ( private_size ) *exp; - size_t tmp_len = bigint_mod_exp_tmp_len ( mod, exp ); + size_t tmp_len = bigint_mod_exp_tmp_len ( mod ); struct { bigint_t ( size ) modulus; bigint_t ( size ) generator; diff --git a/src/crypto/drbg.c b/src/crypto/drbg.c index a3366e806..c4dc7646d 100644 --- a/src/crypto/drbg.c +++ b/src/crypto/drbg.c @@ -34,6 +34,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** @file * diff --git a/src/crypto/ecb.c b/src/crypto/ecb.c index 3c9cf340c..73eef09c2 100644 --- a/src/crypto/ecb.c +++ b/src/crypto/ecb.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <assert.h> #include <ipxe/crypto.h> diff --git a/src/crypto/ecdhe.c b/src/crypto/ecdhe.c new file mode 100644 index 000000000..016253457 --- /dev/null +++ b/src/crypto/ecdhe.c @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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_SECBOOT ( PERMITTED ); + +/** @file + * + * Elliptic Curve Ephemeral Diffie-Hellman (ECDHE) key exchange + * + */ + +#include <string.h> +#include <errno.h> +#include <ipxe/ecdhe.h> + +/** + * Calculate ECDHE key + * + * @v curve Elliptic curve + * @v partner Partner public curve point + * @v private Private key + * @v public Public curve point to fill in (may overlap partner key) + * @v shared Shared secret curve point to fill in + * @ret rc Return status code + */ +int ecdhe_key ( struct elliptic_curve *curve, const void *partner, + const void *private, void *public, void *shared ) { + int rc; + + /* Construct shared key */ + if ( ( rc = elliptic_multiply ( curve, partner, private, + shared ) ) != 0 ) { + DBGC ( curve, "CURVE %s could not generate shared key: %s\n", + curve->name, strerror ( rc ) ); + return rc; + } + + /* Construct public key */ + if ( ( rc = elliptic_multiply ( curve, curve->base, private, + public ) ) != 0 ) { + DBGC ( curve, "CURVE %s could not generate public key: %s\n", + curve->name, strerror ( rc ) ); + return rc; + } + + /* Check that partner and shared keys are not the point at infinity */ + if ( elliptic_is_infinity ( curve, shared ) ) { + DBGC ( curve, "CURVE %s constructed point at infinity\n", + curve->name ); + return -EPERM; + } + + return 0; +} diff --git a/src/crypto/ecdsa.c b/src/crypto/ecdsa.c new file mode 100644 index 000000000..6f10a1a0f --- /dev/null +++ b/src/crypto/ecdsa.c @@ -0,0 +1,944 @@ +/* + * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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_SECBOOT ( PERMITTED ); + +/** @file + * + * Elliptic curve digital signature algorithm (ECDSA) + * + * The elliptic curve public key format is documented in RFC 5480. + * The original private key format is documented in RFC 5915, and the + * generic container PKCS#8 format documented in RFC 5208. + * + */ + +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <ipxe/crypto.h> +#include <ipxe/bigint.h> +#include <ipxe/hmac_drbg.h> +#include <ipxe/ecdsa.h> + +/* Disambiguate the various error causes */ +#define EINVAL_POINTSIZE \ + __einfo_error ( EINFO_EINVAL_POINTSIZE ) +#define EINFO_EINVAL_POINTSIZE \ + __einfo_uniqify ( EINFO_EINVAL, 0x01, "Invalid point size" ) +#define EINVAL_KEYSIZE \ + __einfo_error ( EINFO_EINVAL_KEYSIZE ) +#define EINFO_EINVAL_KEYSIZE \ + __einfo_uniqify ( EINFO_EINVAL, 0x02, "Invalid key size" ) +#define EINVAL_COMPRESSION \ + __einfo_error ( EINFO_EINVAL_COMPRESSION ) +#define EINFO_EINVAL_COMPRESSION \ + __einfo_uniqify ( EINFO_EINVAL, 0x03, "Invalid compression") +#define EINVAL_INFINITY \ + __einfo_error ( EINFO_EINVAL_INFINITY ) +#define EINFO_EINVAL_INFINITY \ + __einfo_uniqify ( EINFO_EINVAL, 0x04, "Point is infinity" ) +#define EINVAL_SIGNATURE \ + __einfo_error ( EINFO_EINVAL_SIGNATURE ) +#define EINFO_EINVAL_SIGNATURE \ + __einfo_uniqify ( EINFO_EINVAL, 0x05, "Invalid signature" ) + +/** "ecPublicKey" object identifier */ +static uint8_t oid_ecpublickey[] = { ASN1_OID_ECPUBLICKEY }; + +/** Generic elliptic curve container algorithm + * + * The actual curve to be used is identified via the algorithm + * parameters, rather than the top-level OID. + */ +struct asn1_algorithm ecpubkey_algorithm __asn1_algorithm = { + .name = "ecPublicKey", + .oid = ASN1_CURSOR ( oid_ecpublickey ), + .pubkey = &ecdsa_algorithm, +}; + +/** An ECDSA key */ +struct ecdsa_key { + /** Elliptic curve */ + struct elliptic_curve *curve; + /** Public curve point */ + const void *public; + /** Private multiple of base curve point (if applicable) */ + const void *private; +}; + +/** ECDSA context */ +struct ecdsa_context { + /** Key */ + struct ecdsa_key key; + /** Big integer size */ + unsigned int size; + /** Digest algorithm */ + struct digest_algorithm *digest; + /** Digest length */ + size_t zlen; + + /** Dynamically allocated storage */ + void *dynamic; + /** Element 0 of modulus N (i.e. curve group order */ + bigint_element_t *modulus0; + /** Element 0 of constant N-2 (for Fermat's little theorem) */ + bigint_element_t *fermat0; + /** Element 0 of Montgomery constant R^2 mod N */ + bigint_element_t *square0; + /** Element 0 of constant 1 (in Montgomery form) */ + bigint_element_t *one0; + /** Element 0 of digest value "z" */ + bigint_element_t *z0; + /** Element 0 of random key "k" */ + bigint_element_t *k0; + /** Element 0 of signature value "r" */ + bigint_element_t *r0; + /** Element 0 of signature value "s" */ + bigint_element_t *s0; + /** Element 0 of temporary value */ + bigint_element_t *temp0; + /** Element 0 of product buffer */ + bigint_element_t *product0; + /** Curve point 1 */ + void *point1; + /** Curve point 2 */ + void *point2; + /** Scalar value */ + void *scalar; + /** HMAC_DRBG state for random value generation */ + struct hmac_drbg_state *drbg; +}; + +/** + * Parse ECDSA key + * + * @v key ECDSA key + * @v raw ASN.1 cursor + * @ret rc Return status code + */ +static int ecdsa_parse_key ( struct ecdsa_key *key, + const struct asn1_cursor *raw ) { + struct asn1_algorithm *algorithm; + struct asn1_cursor cursor; + struct asn1_cursor curve; + struct asn1_cursor private; + const uint8_t *compression; + int is_private; + int rc; + + /* Enter subjectPublicKeyInfo/ECPrivateKey */ + memcpy ( &cursor, raw, sizeof ( cursor ) ); + asn1_enter ( &cursor, ASN1_SEQUENCE ); + asn1_invalidate_cursor ( &curve ); + asn1_invalidate_cursor ( &private ); + + /* Determine key format */ + if ( asn1_type ( &cursor ) == ASN1_INTEGER ) { + + /* Private key */ + is_private = 1; + + /* Skip version */ + asn1_skip_any ( &cursor ); + + /* Parse privateKeyAlgorithm, if present */ + if ( asn1_type ( &cursor ) == ASN1_SEQUENCE ) { + + /* PKCS#8 format */ + DBGC ( key, "ECDSA %p is in PKCS#8 format\n", key ); + + /* Parse privateKeyAlgorithm */ + memcpy ( &curve, &cursor, sizeof ( curve ) ); + asn1_skip_any ( &cursor ); + + /* Enter privateKey */ + asn1_enter ( &cursor, ASN1_OCTET_STRING ); + + /* Enter ECPrivateKey */ + asn1_enter ( &cursor, ASN1_SEQUENCE ); + + /* Skip version */ + asn1_skip ( &cursor, ASN1_INTEGER ); + } + + /* Parse privateKey */ + memcpy ( &private, &cursor, sizeof ( private ) ); + asn1_enter ( &private, ASN1_OCTET_STRING ); + asn1_skip_any ( &cursor ); + + /* Parse parameters, if present */ + if ( asn1_type ( &cursor ) == ASN1_EXPLICIT_TAG ( 0 ) ) { + memcpy ( &curve, &cursor, sizeof ( curve ) ); + asn1_enter_any ( &curve ); + asn1_skip_any ( &cursor ); + } + + /* Enter publicKey */ + asn1_enter ( &cursor, ASN1_EXPLICIT_TAG ( 1 ) ); + + } else { + + /* Public key */ + is_private = 0; + + /* Parse algorithm */ + memcpy ( &curve, &cursor, sizeof ( curve ) ); + asn1_skip_any ( &cursor ); + } + + /* Enter publicKey */ + asn1_enter_bits ( &cursor, NULL ); + + /* Identify curve */ + if ( ( rc = asn1_curve_algorithm ( &curve, &ecpubkey_algorithm, + &algorithm ) ) != 0 ) { + DBGC ( key, "ECDSA %p unknown curve: %s\n", + key, strerror ( rc ) ); + DBGC_HDA ( key, 0, raw->data, raw->len ); + return rc; + } + key->curve = algorithm->curve; + DBGC ( key, "ECDSA %p is a %s (%s) %s key\n", key, algorithm->name, + key->curve->name, ( is_private ? "private" : "public" ) ); + + /* Check public key length */ + if ( cursor.len != ( sizeof ( *compression ) + + key->curve->pointsize ) ) { + DBGC ( key, "ECDSA %p invalid public key length %zd\n", + key, cursor.len ); + DBGC_HDA ( key, 0, raw->data, raw->len ); + return -EINVAL_POINTSIZE; + } + + /* Check that key is uncompressed */ + compression = cursor.data; + if ( *compression != ECDSA_UNCOMPRESSED ) { + DBGC ( key, "ECDSA %p invalid compression %#02x\n", + key, *compression ); + DBGC_HDA ( key, 0, raw->data, raw->len ); + return -EINVAL_COMPRESSION; + } + + /* Extract public curve point */ + key->public = ( cursor.data + sizeof ( *compression ) ); + DBGC ( key, "ECDSA %p public curve point:\n", key ); + DBGC_HDA ( key, 0, key->public, key->curve->pointsize ); + + /* Check that public key is not the point at infinity */ + if ( elliptic_is_infinity ( key->curve, key->public ) ) { + DBGC ( key, "ECDSA %p public curve point is infinity\n", key ); + return -EINVAL_INFINITY; + } + + /* Extract private key, if applicable */ + if ( is_private ) { + + /* Check private key length */ + if ( private.len != key->curve->keysize ) { + DBGC ( key, "ECDSA %p invalid private key length " + "%zd\n", key, private.len ); + DBGC_HDA ( key, 0, raw->data, raw->len ); + return -EINVAL_KEYSIZE; + } + + /* Extract private key */ + key->private = private.data; + DBGC ( key, "ECDSA %p private multiplier:\n", key ); + DBGC_HDA ( key, 0, key->private, key->curve->keysize ); + + } else { + + /* No private key */ + key->private = NULL; + } + + return 0; +} + +/** + * Parse ECDSA signature value + * + * @v ctx ECDSA context + * @v rs0 Element 0 of signature "r" or "s" value + * @v raw ASN.1 cursor + * @ret rc Return status code + */ +static int ecdsa_parse_signature ( struct ecdsa_context *ctx, + bigint_element_t *rs0, + const struct asn1_cursor *raw ) { + size_t keysize = ctx->key.curve->keysize; + unsigned int size = ctx->size; + bigint_t ( size ) __attribute__ (( may_alias )) *modulus = + ( ( void * ) ctx->modulus0 ); + bigint_t ( size ) __attribute__ (( may_alias )) *rs = + ( ( void * ) rs0 ); + struct asn1_cursor cursor; + int rc; + + /* Enter integer */ + memcpy ( &cursor, raw, sizeof ( cursor ) ); + if ( ( rc = asn1_enter_unsigned ( &cursor ) ) != 0 ) { + DBGC ( ctx, "ECDSA %p invalid integer:\n", ctx ); + DBGC_HDA ( ctx, 0, raw->data, raw->len ); + return rc; + } + + /* Extract value */ + if ( cursor.len > keysize ) { + DBGC ( ctx, "ECDSA %p invalid signature value:\n", ctx ); + DBGC_HDA ( ctx, 0, raw->data, raw->len ); + return -EINVAL_KEYSIZE; + } + bigint_init ( rs, cursor.data, cursor.len ); + + /* Check that value is within the required range */ + if ( bigint_is_zero ( rs ) || bigint_is_geq ( rs, modulus ) ) { + DBGC ( ctx, "ECDSA %p out-of-range signature value:\n", ctx ); + DBGC_HDA ( ctx, 0, raw->data, raw->len ); + return -ERANGE; + } + + return 0; +} + +/** + * Prepend ECDSA signature value + * + * @v ctx ECDSA context + * @v rs0 Element 0 of signature "r" or "s" value + * @v builder ASN.1 builder + * @ret rc Return status code + */ +static int ecdsa_prepend_signature ( struct ecdsa_context *ctx, + bigint_element_t *rs0, + struct asn1_builder *builder ) { + size_t keysize = ctx->key.curve->keysize; + unsigned int size = ctx->size; + bigint_t ( size ) __attribute__ (( may_alias )) *rs = + ( ( void * ) rs0 ); + uint8_t buf[ 1 /* potential sign byte */ + keysize ]; + uint8_t *data; + size_t len; + int rc; + + /* Construct value */ + buf[0] = 0; + bigint_done ( rs, &buf[1], keysize ); + + /* Strip leading zeros */ + data = buf; + len = sizeof ( buf ); + while ( ( len > 1 ) && ( data[0] == 0 ) && ( data[1] < 0x80 ) ) { + data++; + len--; + } + + /* Prepend integer */ + if ( ( rc = asn1_prepend ( builder, ASN1_INTEGER, data, len ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Allocate ECDSA context dynamic storage + * + * @v ctx ECDSA context + * @ret rc Return status code + */ +static int ecdsa_alloc ( struct ecdsa_context *ctx ) { + struct elliptic_curve *curve = ctx->key.curve; + size_t pointsize = curve->pointsize; + size_t keysize = curve->keysize; + unsigned int size = + bigint_required_size ( keysize + 1 /* for addition */ ); + struct { + bigint_t ( size ) modulus; + bigint_t ( size ) fermat; + bigint_t ( size ) square; + bigint_t ( size ) one; + bigint_t ( size ) z; + bigint_t ( size ) k; + bigint_t ( size ) r; + bigint_t ( size ) s; + bigint_t ( size ) temp; + bigint_t ( size * 2 ) product; + uint8_t point1[pointsize]; + uint8_t point2[pointsize]; + uint8_t scalar[keysize]; + struct hmac_drbg_state drbg; + } *dynamic; + + /* Allocate dynamic storage */ + dynamic = malloc ( sizeof ( *dynamic ) ); + if ( ! dynamic ) + return -ENOMEM; + + /* Populate context */ + ctx->size = size; + ctx->dynamic = dynamic; + ctx->modulus0 = dynamic->modulus.element; + ctx->fermat0 = dynamic->fermat.element; + ctx->square0 = dynamic->square.element; + ctx->one0 = dynamic->one.element; + ctx->z0 = dynamic->z.element; + ctx->k0 = dynamic->k.element; + ctx->r0 = dynamic->r.element; + ctx->s0 = dynamic->s.element; + ctx->temp0 = dynamic->temp.element; + ctx->product0 = dynamic->product.element; + ctx->point1 = dynamic->point1; + ctx->point2 = dynamic->point2; + ctx->scalar = dynamic->scalar; + ctx->drbg = &dynamic->drbg; + + return 0; +} + +/** + * Free ECDSA context dynamic storage + * + * @v ctx ECDSA context + */ +static void ecdsa_free ( struct ecdsa_context *ctx ) { + + /* Free dynamic storage */ + free ( ctx->dynamic ); +} + +/** + * Initialise ECDSA values + * + * @v ctx ECDSA context + * @v digest Digest algorithm + * @v value Digest value + */ +static void ecdsa_init_values ( struct ecdsa_context *ctx, + struct digest_algorithm *digest, + const void *value ) { + struct elliptic_curve *curve = ctx->key.curve; + unsigned int size = ctx->size; + bigint_t ( size ) __attribute__ (( may_alias )) *modulus = + ( ( void * ) ctx->modulus0 ); + bigint_t ( size ) __attribute__ (( may_alias )) *fermat = + ( ( void * ) ctx->fermat0 ); + bigint_t ( size ) __attribute__ (( may_alias )) *square = + ( ( void * ) ctx->square0 ); + bigint_t ( size ) __attribute__ (( may_alias )) *one = + ( ( void * ) ctx->one0 ); + bigint_t ( size ) __attribute__ (( may_alias )) *z = + ( ( void * ) ctx->z0 ); + bigint_t ( size * 2 ) __attribute__ (( may_alias )) *product = + ( ( void * ) ctx->product0 ); + static const uint8_t two_raw[] = { 2 }; + size_t zlen; + + /* Initialise modulus N */ + bigint_init ( modulus, curve->order, curve->keysize ); + DBGC2 ( ctx, "ECDSA %p N = %s\n", ctx, bigint_ntoa ( modulus ) ); + + /* Calculate N-2 (using Montgomery constant as temporary buffer) */ + bigint_copy ( modulus, fermat ); + bigint_init ( square, two_raw, sizeof ( two_raw ) ); + bigint_subtract ( square, fermat ); + + /* Calculate Montgomery constant */ + bigint_reduce ( modulus, square ); + DBGC2 ( ctx, "ECDSA %p R^2 = %s mod N\n", + ctx, bigint_ntoa ( square ) ); + + /* Construct one in Montgomery form */ + bigint_grow ( square, product ); + bigint_montgomery ( modulus, product, one ); + DBGC2 ( ctx, "ECDSA %p R = %s mod N\n", + ctx, bigint_ntoa ( one ) ); + + /* Initialise digest */ + ctx->digest = digest; + zlen = ctx->key.curve->keysize; + if ( zlen > digest->digestsize ) + zlen = digest->digestsize; + ctx->zlen = zlen; + bigint_init ( z, value, zlen ); + DBGC2 ( ctx, "ECDSA %p z = %s (%s)\n", + ctx, bigint_ntoa ( z ), digest->name ); +} + +/** + * Initialise ECDSA context + * + * @v ctx ECDSA context + * @v key Key + * @v digest Digest algorithm + * @v value Digest value + * @ret rc Return status code + */ +static int ecdsa_init ( struct ecdsa_context *ctx, + const struct asn1_cursor *key, + struct digest_algorithm *digest, + const void *value ) { + int rc; + + /* Parse key */ + if ( ( rc = ecdsa_parse_key ( &ctx->key, key ) ) != 0 ) + goto err_parse; + + /* Allocate dynamic storage */ + if ( ( rc = ecdsa_alloc ( ctx ) ) != 0 ) + goto err_alloc; + + /* Initialise values */ + ecdsa_init_values ( ctx, digest, value ); + + return 0; + + ecdsa_free ( ctx ); + err_alloc: + err_parse: + return rc; +} + +/** + * Invert ECDSA value + * + * @v ctx ECDSA context + * @v val0 Element 0 of value to invert + */ +static void ecdsa_invert ( struct ecdsa_context *ctx, + bigint_element_t *val0 ) { + unsigned int size = ctx->size; + bigint_t ( size ) __attribute__ (( may_alias )) *modulus = + ( ( void * ) ctx->modulus0 ); + bigint_t ( size ) __attribute__ (( may_alias )) *fermat = + ( ( void * ) ctx->fermat0 ); + bigint_t ( size ) __attribute__ (( may_alias )) *square = + ( ( void * ) ctx->square0 ); + bigint_t ( size ) __attribute__ (( may_alias )) *one = + ( ( void * ) ctx->one0 ); + bigint_t ( size ) __attribute__ (( may_alias )) *temp = + ( ( void * ) ctx->temp0 ); + bigint_t ( size * 2 ) __attribute__ (( may_alias )) *product = + ( ( void * ) ctx->product0 ); + bigint_t ( size ) __attribute__ (( may_alias )) *val = + ( ( void * ) val0 ); + + /* Convert value to Montgomery form */ + bigint_multiply ( val, square, product ); + bigint_montgomery ( modulus, product, temp ); + + /* Invert value via Fermat's little theorem */ + bigint_copy ( one, val ); + bigint_ladder ( val, temp, fermat, bigint_mod_exp_ladder, modulus, + product ); +} + +/** + * Generate ECDSA "r" and "s" values + * + * @v ctx ECDSA context + * @v sig Signature + * @ret rc Return status code + */ +static int ecdsa_sign_rs ( struct ecdsa_context *ctx ) { + struct digest_algorithm *digest = ctx->digest; + struct elliptic_curve *curve = ctx->key.curve; + size_t pointsize = curve->pointsize; + size_t keysize = curve->keysize; + unsigned int size = ctx->size; + bigint_t ( size ) __attribute__ (( may_alias )) *modulus = + ( ( void * ) ctx->modulus0 ); + bigint_t ( size ) __attribute__ (( may_alias )) *square = + ( ( void * ) ctx->square0 ); + bigint_t ( size ) __attribute__ (( may_alias )) *one = + ( ( void * ) ctx->one0 ); + bigint_t ( size ) __attribute__ (( may_alias )) *z = + ( ( void * ) ctx->z0 ); + bigint_t ( size ) __attribute__ (( may_alias )) *k = + ( ( void * ) ctx->k0 ); + bigint_t ( size ) __attribute__ (( may_alias )) *r = + ( ( void * ) ctx->r0 ); + bigint_t ( size ) __attribute__ (( may_alias )) *s = + ( ( void * ) ctx->s0 ); + bigint_t ( size ) __attribute__ (( may_alias )) *temp = + ( ( void * ) ctx->temp0 ); + bigint_t ( size * 2 ) __attribute__ (( may_alias )) *product = + ( ( void * ) ctx->product0 ); + bigint_t ( size ) __attribute__ (( may_alias )) *x1 = + ( ( void * ) temp ); + void *point1 = ctx->point1; + void *scalar = ctx->scalar; + int rc; + + /* Loop until a suitable signature is generated */ + while ( 1 ) { + + /* Generate pseudo-random data */ + if ( ( rc = hmac_drbg_generate ( digest, ctx->drbg, NULL, 0, + scalar, keysize ) ) != 0 ) { + DBGC ( ctx, "ECDSA %p could not generate: %s\n", + ctx, strerror ( rc ) ); + return rc; + } + + /* Check suitability of pseudo-random data */ + bigint_init ( k, scalar, keysize ); + DBGC2 ( ctx, "ECDSA %p k = %s\n", + ctx, bigint_ntoa ( k ) ); + if ( bigint_is_zero ( k ) ) + continue; + if ( bigint_is_geq ( k, modulus ) ) + continue; + + /* Calculate (x1,y1) = k*G */ + elliptic_multiply ( curve, curve->base, scalar, point1 ); + bigint_init ( x1, point1, ( pointsize / 2 ) ); + DBGC2 ( ctx, "ECDSA %p x1 = %s mod N\n", + ctx, bigint_ntoa ( x1 ) ); + + /* Calculate r = x1 mod N */ + bigint_multiply ( x1, one, product ); + bigint_montgomery ( modulus, product, r ); + DBGC2 ( ctx, "ECDSA %p r = %s\n", + ctx, bigint_ntoa ( r ) ); + + /* Check suitability of r */ + if ( bigint_is_zero ( r ) ) + continue; + + /* Calculate k^-1 mod N (in Montgomery form) */ + ecdsa_invert ( ctx, k->element ); + DBGC2 ( ctx, "ECDSA %p (k^-1)R = %s mod N\n", + ctx, bigint_ntoa ( k ) ); + + /* Calculate r * dA */ + bigint_init ( temp, ctx->key.private, keysize ); + DBGC2 ( ctx, "ECDSA %p dA = %s\n", + ctx, bigint_ntoa ( temp ) ); + bigint_multiply ( r, temp, product ); + bigint_montgomery ( modulus, product, temp ); + bigint_multiply ( temp, square, product ); + bigint_montgomery ( modulus, product, temp ); + DBGC2 ( ctx, "ECDSA %p r*dA = %s mod N\n", + ctx, bigint_ntoa ( temp ) ); + + /* Calculate k^-1 * (z + r*dA) */ + bigint_add ( z, temp ); + DBGC2 ( ctx, "ECDSA %p z+r*dA = %s mod N\n", + ctx, bigint_ntoa ( temp ) ); + bigint_multiply ( k, temp, product ); + bigint_montgomery ( modulus, product, s ); + DBGC2 ( ctx, "ECDSA %p s = %s\n", + ctx, bigint_ntoa ( s ) ); + + /* Check suitability of s */ + if ( bigint_is_zero ( s ) ) + continue; + + return 0; + } +} + +/** + * Verify ECDSA "r" and "s" values + * + * @v ctx ECDSA context + * @v sig Signature + * @ret rc Return status code + */ +static int ecdsa_verify_rs ( struct ecdsa_context *ctx ) { + struct elliptic_curve *curve = ctx->key.curve; + size_t pointsize = curve->pointsize; + size_t keysize = curve->keysize; + const void *public = ctx->key.public; + unsigned int size = ctx->size; + bigint_t ( size ) __attribute__ (( may_alias )) *modulus = + ( ( void * ) ctx->modulus0 ); + bigint_t ( size ) __attribute__ (( may_alias )) *one = + ( ( void * ) ctx->one0 ); + bigint_t ( size ) __attribute__ (( may_alias )) *z = + ( ( void * ) ctx->z0 ); + bigint_t ( size ) __attribute__ (( may_alias )) *r = + ( ( void * ) ctx->r0 ); + bigint_t ( size ) __attribute__ (( may_alias )) *s = + ( ( void * ) ctx->s0 ); + bigint_t ( size ) __attribute__ (( may_alias )) *temp = + ( ( void * ) ctx->temp0 ); + bigint_t ( size * 2 ) __attribute__ (( may_alias )) *product = + ( ( void * ) ctx->product0 ); + bigint_t ( size ) __attribute__ (( may_alias )) *u1 = + ( ( void * ) temp ); + bigint_t ( size ) __attribute__ (( may_alias )) *u2 = + ( ( void * ) temp ); + bigint_t ( size ) __attribute__ (( may_alias )) *x1 = + ( ( void * ) temp ); + void *point1 = ctx->point1; + void *point2 = ctx->point2; + void *scalar = ctx->scalar; + int valid; + int rc; + + DBGC2 ( ctx, "ECDSA %p r = %s\n", ctx, bigint_ntoa ( r ) ); + DBGC2 ( ctx, "ECDSA %p s = %s\n", ctx, bigint_ntoa ( s ) ); + + /* Calculate s^-1 mod N (in Montgomery form) */ + ecdsa_invert ( ctx, s->element ); + DBGC2 ( ctx, "ECDSA %p (s^-1)R = %s mod N\n", ctx, bigint_ntoa ( s ) ); + + /* Calculate u1 = (z * s^-1) mod N */ + bigint_multiply ( z, s, product ); + bigint_montgomery ( modulus, product, u1 ); + DBGC2 ( ctx, "ECDSA %p u1 = %s mod N\n", + ctx, bigint_ntoa ( u1 ) ); + bigint_done ( u1, scalar, keysize ); + + /* Calculate u1 * G */ + if ( ( rc = elliptic_multiply ( curve, curve->base, scalar, + point1 ) ) != 0 ) { + DBGC ( ctx, "ECDSA %p could not calculate u1*G: %s\n", + ctx, strerror ( rc ) ); + return rc; + } + + /* Calculate u2 = (r * s^-1) mod N */ + bigint_multiply ( r, s, product ); + bigint_montgomery ( modulus, product, u2 ); + bigint_done ( u2, scalar, keysize ); + DBGC2 ( ctx, "ECDSA %p u2 = %s mod N\n", + ctx, bigint_ntoa ( u2 ) ); + + /* Calculate u2 * Qa */ + if ( ( rc = elliptic_multiply ( curve, public, scalar, + point2 ) ) != 0 ) { + DBGC ( ctx, "ECDSA %p could not calculate u2*Qa: %s\n", + ctx, strerror ( rc ) ); + return rc; + } + + /* Calculate u1 * G + u2 * Qa */ + if ( ( rc = elliptic_add ( curve, point1, point2, point1 ) ) != 0 ) { + DBGC ( ctx, "ECDSA %p could not calculate u1*G+u2*Qa: %s\n", + ctx, strerror ( rc ) ); + return rc; + } + + /* Check that result is not the point at infinity */ + if ( elliptic_is_infinity ( curve, point1 ) ) { + DBGC ( ctx, "ECDSA %p result is point at infinity\n", ctx ); + return -EINVAL; + } + + /* Calculate x1 mod N */ + bigint_init ( x1, point1, ( pointsize / 2 ) ); + DBGC2 ( ctx, "ECDSA %p x1 = %s mod N\n", ctx, bigint_ntoa ( x1 ) ); + bigint_multiply ( x1, one, product ); + bigint_montgomery ( modulus, product, x1 ); + DBGC2 ( ctx, "ECDSA %p x1 = %s\n", ctx, bigint_ntoa ( x1 ) ); + + /* Check signature */ + bigint_subtract ( x1, r ); + valid = bigint_is_zero ( r ); + DBGC2 ( ctx, "ECDSA %p signature is%s valid\n", + ctx, ( valid ? "" : " not" ) ); + + return ( valid ? 0 : -EINVAL_SIGNATURE ); +} + +/** + * Encrypt using ECDSA + * + * @v key Key + * @v plaintext Plaintext + * @v ciphertext Ciphertext + * @ret rc Return status code + */ +static int ecdsa_encrypt ( const struct asn1_cursor *key __unused, + const struct asn1_cursor *plaintext __unused, + struct asn1_builder *ciphertext __unused ) { + + /* Not a defined operation for ECDSA */ + return -ENOTTY; +} + +/** + * Decrypt using ECDSA + * + * @v key Key + * @v ciphertext Ciphertext + * @v plaintext Plaintext + * @ret rc Return status code + */ +static int ecdsa_decrypt ( const struct asn1_cursor *key __unused, + const struct asn1_cursor *ciphertext __unused, + struct asn1_builder *plaintext __unused ) { + + /* Not a defined operation for ECDSA */ + return -ENOTTY; +} + +/** + * Sign digest value using ECDSA + * + * @v key Key + * @v digest Digest algorithm + * @v value Digest value + * @v signature Signature + * @ret rc Return status code + */ +static int ecdsa_sign ( const struct asn1_cursor *key, + struct digest_algorithm *digest, const void *value, + struct asn1_builder *signature ) { + struct ecdsa_context ctx; + int rc; + + /* Initialise context */ + if ( ( rc = ecdsa_init ( &ctx, key, digest, value ) ) != 0 ) + goto err_init; + + /* Fail unless we have a private key */ + if ( ! ctx.key.private ) { + rc = -ENOTTY; + goto err_no_key; + } + + /* Instantiate DRBG */ + hmac_drbg_instantiate ( digest, ctx.drbg, ctx.key.private, + ctx.key.curve->keysize, value, ctx.zlen ); + + /* Create signature */ + if ( ( rc = ecdsa_sign_rs ( &ctx ) ) != 0 ) + goto err_signature; + + /* Construct "r" and "s" values */ + if ( ( rc = ecdsa_prepend_signature ( &ctx, ctx.s0, signature ) ) != 0) + goto err_s; + if ( ( rc = ecdsa_prepend_signature ( &ctx, ctx.r0, signature ) ) != 0) + goto err_r; + if ( ( rc = asn1_wrap ( signature, ASN1_SEQUENCE ) ) != 0 ) + goto err_wrap; + + /* Free context */ + ecdsa_free ( &ctx ); + + return 0; + + err_wrap: + err_r: + err_s: + err_signature: + err_no_key: + ecdsa_free ( &ctx ); + err_init: + return rc; +} + +/** + * Verify signed digest using ECDSA + * + * @v key Key + * @v digest Digest algorithm + * @v value Digest value + * @v signature Signature + * @ret rc Return status code + */ +static int ecdsa_verify ( const struct asn1_cursor *key, + struct digest_algorithm *digest, const void *value, + const struct asn1_cursor *signature ) { + struct ecdsa_context ctx; + struct asn1_cursor cursor; + int rc; + + /* Initialise context */ + if ( ( rc = ecdsa_init ( &ctx, key, digest, value ) ) != 0 ) + goto err_init; + + /* Enter sequence */ + memcpy ( &cursor, signature, sizeof ( cursor ) ); + asn1_enter ( &cursor, ASN1_SEQUENCE ); + + /* Extract "r" and "s" values */ + if ( ( rc = ecdsa_parse_signature ( &ctx, ctx.r0, &cursor ) ) != 0 ) + goto err_r; + asn1_skip_any ( &cursor ); + if ( ( rc = ecdsa_parse_signature ( &ctx, ctx.s0, &cursor ) ) != 0 ) + goto err_s; + + /* Verify signature */ + if ( ( rc = ecdsa_verify_rs ( &ctx ) ) != 0 ) + goto err_verify; + + /* Free context */ + ecdsa_free ( &ctx ); + + return 0; + + err_verify: + err_s: + err_r: + ecdsa_free ( &ctx ); + err_init: + return rc; +} + +/** + * Check for matching ECDSA public/private key pair + * + * @v private_key Private key + * @v public_key Public key + * @ret rc Return status code + */ +static int ecdsa_match ( const struct asn1_cursor *private_key, + const struct asn1_cursor *public_key ) { + struct elliptic_curve *curve; + struct ecdsa_key privkey; + struct ecdsa_key pubkey; + int rc; + + /* Parse keys */ + if ( ( rc = ecdsa_parse_key ( &privkey, private_key ) ) != 0 ) + return rc; + if ( ( rc = ecdsa_parse_key ( &pubkey, public_key ) ) != 0 ) + return rc; + + /* Compare curves */ + if ( privkey.curve != pubkey.curve ) + return -ENOTTY; + curve = privkey.curve; + + /* Compare public curve points */ + if ( memcmp ( privkey.public, pubkey.public, curve->pointsize ) != 0 ) + return -ENOTTY; + + return 0; +} + +/** ECDSA public-key algorithm */ +struct pubkey_algorithm ecdsa_algorithm = { + .name = "ecdsa", + .encrypt = ecdsa_encrypt, + .decrypt = ecdsa_decrypt, + .sign = ecdsa_sign, + .verify = ecdsa_verify, + .match = ecdsa_match, +}; diff --git a/src/crypto/entropy.c b/src/crypto/entropy.c index 419007159..ac0e92c42 100644 --- a/src/crypto/entropy.c +++ b/src/crypto/entropy.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** @file * diff --git a/src/crypto/gcm.c b/src/crypto/gcm.c index b93925d07..b9c9d3a39 100644 --- a/src/crypto/gcm.c +++ b/src/crypto/gcm.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** @file * diff --git a/src/crypto/hash_df.c b/src/crypto/hash_df.c index dc0dc0ce8..ec4bcaebc 100644 --- a/src/crypto/hash_df.c +++ b/src/crypto/hash_df.c @@ -34,6 +34,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** @file * diff --git a/src/crypto/hmac.c b/src/crypto/hmac.c index 7109bbf6a..ed4cefaad 100644 --- a/src/crypto/hmac.c +++ b/src/crypto/hmac.c @@ -34,6 +34,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** * @file diff --git a/src/crypto/hmac_drbg.c b/src/crypto/hmac_drbg.c index 57bde4d1d..bd831e239 100644 --- a/src/crypto/hmac_drbg.c +++ b/src/crypto/hmac_drbg.c @@ -34,6 +34,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** @file * diff --git a/src/crypto/md4.c b/src/crypto/md4.c index dcd86a428..a9184aa57 100644 --- a/src/crypto/md4.c +++ b/src/crypto/md4.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** @file * diff --git a/src/crypto/md5.c b/src/crypto/md5.c index 5c62513e2..9418b006c 100644 --- a/src/crypto/md5.c +++ b/src/crypto/md5.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** @file * diff --git a/src/crypto/mishmash/cmd_md4.c b/src/crypto/mishmash/cmd_md4.c new file mode 100644 index 000000000..390a533db --- /dev/null +++ b/src/crypto/mishmash/cmd_md4.c @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( FORBIDDEN ); + +#include <ipxe/md4.h> +#include <hci/digest_cmd.h> + +static int md4sum_exec ( int argc, char **argv ) { + return digest_exec ( argc, argv, &md4_algorithm ); +} + +COMMAND ( md4sum, md4sum_exec ); diff --git a/src/crypto/mishmash/cmd_sha224.c b/src/crypto/mishmash/cmd_sha224.c new file mode 100644 index 000000000..fd8095937 --- /dev/null +++ b/src/crypto/mishmash/cmd_sha224.c @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); + +#include <ipxe/sha256.h> +#include <hci/digest_cmd.h> + +static int sha224sum_exec ( int argc, char **argv ) { + return digest_exec ( argc, argv, &sha224_algorithm ); +} + +COMMAND ( sha224sum, sha224sum_exec ); diff --git a/src/crypto/mishmash/cmd_sha256.c b/src/crypto/mishmash/cmd_sha256.c new file mode 100644 index 000000000..259ae3eac --- /dev/null +++ b/src/crypto/mishmash/cmd_sha256.c @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); + +#include <ipxe/sha256.h> +#include <hci/digest_cmd.h> + +static int sha256sum_exec ( int argc, char **argv ) { + return digest_exec ( argc, argv, &sha256_algorithm ); +} + +COMMAND ( sha256sum, sha256sum_exec ); diff --git a/src/crypto/mishmash/cmd_sha384.c b/src/crypto/mishmash/cmd_sha384.c new file mode 100644 index 000000000..c31154d24 --- /dev/null +++ b/src/crypto/mishmash/cmd_sha384.c @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); + +#include <ipxe/sha512.h> +#include <hci/digest_cmd.h> + +static int sha384sum_exec ( int argc, char **argv ) { + return digest_exec ( argc, argv, &sha384_algorithm ); +} + +COMMAND ( sha384sum, sha384sum_exec ); diff --git a/src/crypto/mishmash/cmd_sha512.c b/src/crypto/mishmash/cmd_sha512.c new file mode 100644 index 000000000..b6207f86d --- /dev/null +++ b/src/crypto/mishmash/cmd_sha512.c @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); + +#include <ipxe/sha512.h> +#include <hci/digest_cmd.h> + +static int sha512sum_exec ( int argc, char **argv ) { + return digest_exec ( argc, argv, &sha512_algorithm ); +} + +COMMAND ( sha512sum, sha512sum_exec ); diff --git a/src/crypto/mishmash/dhe_rsa_aes_cbc_sha1.c b/src/crypto/mishmash/dhe_rsa_aes_cbc_sha1.c index 05e409f7a..ec2155001 100644 --- a/src/crypto/mishmash/dhe_rsa_aes_cbc_sha1.c +++ b/src/crypto/mishmash/dhe_rsa_aes_cbc_sha1.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <byteswap.h> #include <ipxe/rsa.h> diff --git a/src/crypto/mishmash/dhe_rsa_aes_cbc_sha256.c b/src/crypto/mishmash/dhe_rsa_aes_cbc_sha256.c index 6ce428642..4e6226e87 100644 --- a/src/crypto/mishmash/dhe_rsa_aes_cbc_sha256.c +++ b/src/crypto/mishmash/dhe_rsa_aes_cbc_sha256.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <byteswap.h> #include <ipxe/rsa.h> diff --git a/src/crypto/mishmash/dhe_rsa_aes_gcm_sha256.c b/src/crypto/mishmash/dhe_rsa_aes_gcm_sha256.c index dc5cad9f8..6bbe4d00d 100644 --- a/src/crypto/mishmash/dhe_rsa_aes_gcm_sha256.c +++ b/src/crypto/mishmash/dhe_rsa_aes_gcm_sha256.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <byteswap.h> #include <ipxe/rsa.h> diff --git a/src/crypto/mishmash/dhe_rsa_aes_gcm_sha384.c b/src/crypto/mishmash/dhe_rsa_aes_gcm_sha384.c index 0448255f3..336feb195 100644 --- a/src/crypto/mishmash/dhe_rsa_aes_gcm_sha384.c +++ b/src/crypto/mishmash/dhe_rsa_aes_gcm_sha384.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <byteswap.h> #include <ipxe/rsa.h> diff --git a/src/crypto/mishmash/ecdhe_ecdsa_aes_cbc_sha1.c b/src/crypto/mishmash/ecdhe_ecdsa_aes_cbc_sha1.c new file mode 100644 index 000000000..0d9fcd15d --- /dev/null +++ b/src/crypto/mishmash/ecdhe_ecdsa_aes_cbc_sha1.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); + +#include <byteswap.h> +#include <ipxe/ecdsa.h> +#include <ipxe/aes.h> +#include <ipxe/sha1.h> +#include <ipxe/sha256.h> +#include <ipxe/tls.h> + +/** TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA cipher suite */ +struct tls_cipher_suite +tls_ecdhe_ecdsa_with_aes_128_cbc_sha __tls_cipher_suite ( 05 ) = { + .code = htons ( TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA ), + .key_len = ( 128 / 8 ), + .fixed_iv_len = 0, + .record_iv_len = AES_BLOCKSIZE, + .mac_len = SHA1_DIGEST_SIZE, + .exchange = &tls_ecdhe_exchange_algorithm, + .pubkey = &ecdsa_algorithm, + .cipher = &aes_cbc_algorithm, + .digest = &sha1_algorithm, + .handshake = &sha256_algorithm, +}; + +/** TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA cipher suite */ +struct tls_cipher_suite +tls_ecdhe_ecdsa_with_aes_256_cbc_sha __tls_cipher_suite ( 06 ) = { + .code = htons ( TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA ), + .key_len = ( 256 / 8 ), + .fixed_iv_len = 0, + .record_iv_len = AES_BLOCKSIZE, + .mac_len = SHA1_DIGEST_SIZE, + .exchange = &tls_ecdhe_exchange_algorithm, + .pubkey = &ecdsa_algorithm, + .cipher = &aes_cbc_algorithm, + .digest = &sha1_algorithm, + .handshake = &sha256_algorithm, +}; diff --git a/src/crypto/mishmash/ecdhe_ecdsa_aes_cbc_sha256.c b/src/crypto/mishmash/ecdhe_ecdsa_aes_cbc_sha256.c new file mode 100644 index 000000000..4b7cf1620 --- /dev/null +++ b/src/crypto/mishmash/ecdhe_ecdsa_aes_cbc_sha256.c @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); + +#include <byteswap.h> +#include <ipxe/ecdsa.h> +#include <ipxe/aes.h> +#include <ipxe/sha256.h> +#include <ipxe/tls.h> + +/** TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 cipher suite */ +struct tls_cipher_suite +tls_ecdhe_ecdsa_with_aes_128_cbc_sha256 __tls_cipher_suite ( 03 ) = { + .code = htons ( TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 ), + .key_len = ( 128 / 8 ), + .fixed_iv_len = 0, + .record_iv_len = AES_BLOCKSIZE, + .mac_len = SHA256_DIGEST_SIZE, + .exchange = &tls_ecdhe_exchange_algorithm, + .pubkey = &ecdsa_algorithm, + .cipher = &aes_cbc_algorithm, + .digest = &sha256_algorithm, + .handshake = &sha256_algorithm, +}; diff --git a/src/crypto/mishmash/ecdhe_ecdsa_aes_cbc_sha384.c b/src/crypto/mishmash/ecdhe_ecdsa_aes_cbc_sha384.c new file mode 100644 index 000000000..85373911a --- /dev/null +++ b/src/crypto/mishmash/ecdhe_ecdsa_aes_cbc_sha384.c @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); + +#include <byteswap.h> +#include <ipxe/ecdsa.h> +#include <ipxe/aes.h> +#include <ipxe/sha512.h> +#include <ipxe/tls.h> + +/** TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 cipher suite */ +struct tls_cipher_suite +tls_ecdhe_ecdsa_with_aes_256_cbc_sha384 __tls_cipher_suite ( 04 ) = { + .code = htons ( TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 ), + .key_len = ( 256 / 8 ), + .fixed_iv_len = 0, + .record_iv_len = AES_BLOCKSIZE, + .mac_len = SHA384_DIGEST_SIZE, + .exchange = &tls_ecdhe_exchange_algorithm, + .pubkey = &ecdsa_algorithm, + .cipher = &aes_cbc_algorithm, + .digest = &sha384_algorithm, + .handshake = &sha384_algorithm, +}; diff --git a/src/crypto/mishmash/ecdhe_ecdsa_aes_gcm_sha256.c b/src/crypto/mishmash/ecdhe_ecdsa_aes_gcm_sha256.c new file mode 100644 index 000000000..5aeb2f3d9 --- /dev/null +++ b/src/crypto/mishmash/ecdhe_ecdsa_aes_gcm_sha256.c @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); + +#include <byteswap.h> +#include <ipxe/ecdsa.h> +#include <ipxe/aes.h> +#include <ipxe/sha256.h> +#include <ipxe/tls.h> + +/** TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 cipher suite */ +struct tls_cipher_suite +tls_ecdhe_ecdsa_with_aes_128_gcm_sha256 __tls_cipher_suite ( 01 ) = { + .code = htons ( TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 ), + .key_len = ( 128 / 8 ), + .fixed_iv_len = 4, + .record_iv_len = 8, + .mac_len = 0, + .exchange = &tls_ecdhe_exchange_algorithm, + .pubkey = &ecdsa_algorithm, + .cipher = &aes_gcm_algorithm, + .digest = &sha256_algorithm, + .handshake = &sha256_algorithm, +}; diff --git a/src/crypto/mishmash/ecdhe_ecdsa_aes_gcm_sha384.c b/src/crypto/mishmash/ecdhe_ecdsa_aes_gcm_sha384.c new file mode 100644 index 000000000..3dc6149d7 --- /dev/null +++ b/src/crypto/mishmash/ecdhe_ecdsa_aes_gcm_sha384.c @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); + +#include <byteswap.h> +#include <ipxe/ecdsa.h> +#include <ipxe/aes.h> +#include <ipxe/sha512.h> +#include <ipxe/tls.h> + +/** TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 cipher suite */ +struct tls_cipher_suite +tls_ecdhe_ecdsa_with_aes_256_gcm_sha384 __tls_cipher_suite ( 02 ) = { + .code = htons ( TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 ), + .key_len = ( 256 / 8 ), + .fixed_iv_len = 4, + .record_iv_len = 8, + .mac_len = 0, + .exchange = &tls_ecdhe_exchange_algorithm, + .pubkey = &ecdsa_algorithm, + .cipher = &aes_gcm_algorithm, + .digest = &sha384_algorithm, + .handshake = &sha384_algorithm, +}; diff --git a/src/crypto/mishmash/ecdhe_rsa_aes_cbc_sha1.c b/src/crypto/mishmash/ecdhe_rsa_aes_cbc_sha1.c index c23f65cc0..46b42ac1e 100644 --- a/src/crypto/mishmash/ecdhe_rsa_aes_cbc_sha1.c +++ b/src/crypto/mishmash/ecdhe_rsa_aes_cbc_sha1.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <byteswap.h> #include <ipxe/rsa.h> diff --git a/src/crypto/mishmash/ecdhe_rsa_aes_cbc_sha256.c b/src/crypto/mishmash/ecdhe_rsa_aes_cbc_sha256.c index 431e2e304..dd524ec78 100644 --- a/src/crypto/mishmash/ecdhe_rsa_aes_cbc_sha256.c +++ b/src/crypto/mishmash/ecdhe_rsa_aes_cbc_sha256.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <byteswap.h> #include <ipxe/rsa.h> diff --git a/src/crypto/mishmash/ecdhe_rsa_aes_cbc_sha384.c b/src/crypto/mishmash/ecdhe_rsa_aes_cbc_sha384.c index c52976809..7524d1ccc 100644 --- a/src/crypto/mishmash/ecdhe_rsa_aes_cbc_sha384.c +++ b/src/crypto/mishmash/ecdhe_rsa_aes_cbc_sha384.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <byteswap.h> #include <ipxe/rsa.h> diff --git a/src/crypto/mishmash/ecdhe_rsa_aes_gcm_sha256.c b/src/crypto/mishmash/ecdhe_rsa_aes_gcm_sha256.c index 4f4e38c69..978be2a4c 100644 --- a/src/crypto/mishmash/ecdhe_rsa_aes_gcm_sha256.c +++ b/src/crypto/mishmash/ecdhe_rsa_aes_gcm_sha256.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <byteswap.h> #include <ipxe/rsa.h> diff --git a/src/crypto/mishmash/ecdhe_rsa_aes_gcm_sha384.c b/src/crypto/mishmash/ecdhe_rsa_aes_gcm_sha384.c index 0bc7c305f..5ca6f0457 100644 --- a/src/crypto/mishmash/ecdhe_rsa_aes_gcm_sha384.c +++ b/src/crypto/mishmash/ecdhe_rsa_aes_gcm_sha384.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <byteswap.h> #include <ipxe/rsa.h> diff --git a/src/crypto/mishmash/ecdsa_sha224.c b/src/crypto/mishmash/ecdsa_sha224.c new file mode 100644 index 000000000..92aa881cd --- /dev/null +++ b/src/crypto/mishmash/ecdsa_sha224.c @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); + +#include <ipxe/ecdsa.h> +#include <ipxe/sha256.h> +#include <ipxe/asn1.h> +#include <ipxe/tls.h> + +/** "ecdsa-with-SHA224" object identifier */ +static uint8_t oid_ecdsa_with_sha224[] = { ASN1_OID_ECDSA_WITH_SHA224 }; + +/** "ecdsa-with-SHA224" OID-identified algorithm */ +struct asn1_algorithm ecdsa_with_sha224_algorithm __asn1_algorithm = { + .name = "ecdsaWithSHA224", + .pubkey = &ecdsa_algorithm, + .digest = &sha224_algorithm, + .oid = ASN1_CURSOR ( oid_ecdsa_with_sha224 ), +}; + +/** ECDSA with SHA-224 signature hash algorithm */ +struct tls_signature_hash_algorithm +tls_ecdsa_sha224 __tls_sig_hash_algorithm = { + .code = { + .signature = TLS_ECDSA_ALGORITHM, + .hash = TLS_SHA224_ALGORITHM, + }, + .pubkey = &ecdsa_algorithm, + .digest = &sha224_algorithm, +}; diff --git a/src/crypto/mishmash/ecdsa_sha256.c b/src/crypto/mishmash/ecdsa_sha256.c new file mode 100644 index 000000000..025d6ec73 --- /dev/null +++ b/src/crypto/mishmash/ecdsa_sha256.c @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); + +#include <ipxe/ecdsa.h> +#include <ipxe/sha256.h> +#include <ipxe/asn1.h> +#include <ipxe/tls.h> + +/** "ecdsa-with-SHA256" object identifier */ +static uint8_t oid_ecdsa_with_sha256[] = { ASN1_OID_ECDSA_WITH_SHA256 }; + +/** "ecdsa-with-SHA256" OID-identified algorithm */ +struct asn1_algorithm ecdsa_with_sha256_algorithm __asn1_algorithm = { + .name = "ecdsaWithSHA256", + .pubkey = &ecdsa_algorithm, + .digest = &sha256_algorithm, + .oid = ASN1_CURSOR ( oid_ecdsa_with_sha256 ), +}; + +/** ECDSA with SHA-256 signature hash algorithm */ +struct tls_signature_hash_algorithm +tls_ecdsa_sha256 __tls_sig_hash_algorithm = { + .code = { + .signature = TLS_ECDSA_ALGORITHM, + .hash = TLS_SHA256_ALGORITHM, + }, + .pubkey = &ecdsa_algorithm, + .digest = &sha256_algorithm, +}; diff --git a/src/crypto/mishmash/ecdsa_sha384.c b/src/crypto/mishmash/ecdsa_sha384.c new file mode 100644 index 000000000..d7a0ca5d6 --- /dev/null +++ b/src/crypto/mishmash/ecdsa_sha384.c @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); + +#include <ipxe/ecdsa.h> +#include <ipxe/sha512.h> +#include <ipxe/asn1.h> +#include <ipxe/tls.h> + +/** "ecdsa-with-SHA384" object identifier */ +static uint8_t oid_ecdsa_with_sha384[] = { ASN1_OID_ECDSA_WITH_SHA384 }; + +/** "ecdsa-with-SHA384" OID-identified algorithm */ +struct asn1_algorithm ecdsa_with_sha384_algorithm __asn1_algorithm = { + .name = "ecdsaWithSHA384", + .pubkey = &ecdsa_algorithm, + .digest = &sha384_algorithm, + .oid = ASN1_CURSOR ( oid_ecdsa_with_sha384 ), +}; + +/** ECDSA with SHA-384 signature hash algorithm */ +struct tls_signature_hash_algorithm +tls_ecdsa_sha384 __tls_sig_hash_algorithm = { + .code = { + .signature = TLS_ECDSA_ALGORITHM, + .hash = TLS_SHA384_ALGORITHM, + }, + .pubkey = &ecdsa_algorithm, + .digest = &sha384_algorithm, +}; diff --git a/src/crypto/mishmash/ecdsa_sha512.c b/src/crypto/mishmash/ecdsa_sha512.c new file mode 100644 index 000000000..15391abf2 --- /dev/null +++ b/src/crypto/mishmash/ecdsa_sha512.c @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); + +#include <ipxe/ecdsa.h> +#include <ipxe/sha512.h> +#include <ipxe/asn1.h> +#include <ipxe/tls.h> + +/** "ecdsa-with-SHA512" object identifier */ +static uint8_t oid_ecdsa_with_sha512[] = { ASN1_OID_ECDSA_WITH_SHA512 }; + +/** "ecdsa-with-SHA512" OID-identified algorithm */ +struct asn1_algorithm ecdsa_with_sha512_algorithm __asn1_algorithm = { + .name = "ecdsaWithSHA512", + .pubkey = &ecdsa_algorithm, + .digest = &sha512_algorithm, + .oid = ASN1_CURSOR ( oid_ecdsa_with_sha512 ), +}; + +/** ECDSA with SHA-512 signature hash algorithm */ +struct tls_signature_hash_algorithm +tls_ecdsa_sha512 __tls_sig_hash_algorithm = { + .code = { + .signature = TLS_ECDSA_ALGORITHM, + .hash = TLS_SHA512_ALGORITHM, + }, + .pubkey = &ecdsa_algorithm, + .digest = &sha512_algorithm, +}; diff --git a/src/crypto/mishmash/oid_aes_cbc.c b/src/crypto/mishmash/oid_aes_cbc.c new file mode 100644 index 000000000..d5b81541a --- /dev/null +++ b/src/crypto/mishmash/oid_aes_cbc.c @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2024 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); + +#include <ipxe/aes.h> +#include <ipxe/asn1.h> + +/** "aes128-cbc" object identifier */ +static uint8_t oid_aes_128_cbc[] = { ASN1_OID_AES128_CBC }; + +/** "aes192-cbc" object identifier */ +static uint8_t oid_aes_192_cbc[] = { ASN1_OID_AES192_CBC }; + +/** "aes256-cbc" object identifier */ +static uint8_t oid_aes_256_cbc[] = { ASN1_OID_AES256_CBC }; + +/** "aes128-cbc" OID-identified algorithm */ +struct asn1_algorithm aes_128_cbc_algorithm __asn1_algorithm = { + .name = "aes128-cbc", + .cipher = &aes_cbc_algorithm, + .oid = ASN1_CURSOR ( oid_aes_128_cbc ), + .parse = asn1_parse_cbc, +}; + +/** "aes192-cbc" OID-identified algorithm */ +struct asn1_algorithm aes_192_cbc_algorithm __asn1_algorithm = { + .name = "aes192-cbc", + .cipher = &aes_cbc_algorithm, + .oid = ASN1_CURSOR ( oid_aes_192_cbc ), + .parse = asn1_parse_cbc, +}; + +/** "aes256-cbc" OID-identified algorithm */ +struct asn1_algorithm aes_256_cbc_algorithm __asn1_algorithm = { + .name = "aes256-cbc", + .cipher = &aes_cbc_algorithm, + .oid = ASN1_CURSOR ( oid_aes_256_cbc ), + .parse = asn1_parse_cbc, +}; diff --git a/src/crypto/mishmash/oid_aes_gcm.c b/src/crypto/mishmash/oid_aes_gcm.c new file mode 100644 index 000000000..6be1a132d --- /dev/null +++ b/src/crypto/mishmash/oid_aes_gcm.c @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2024 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); + +#include <ipxe/aes.h> +#include <ipxe/asn1.h> + +/** "aes128-gcm" object identifier */ +static uint8_t oid_aes_128_gcm[] = { ASN1_OID_AES128_GCM }; + +/** "aes192-gcm" object identifier */ +static uint8_t oid_aes_192_gcm[] = { ASN1_OID_AES192_GCM }; + +/** "aes256-gcm" object identifier */ +static uint8_t oid_aes_256_gcm[] = { ASN1_OID_AES256_GCM }; + +/** "aes128-gcm" OID-identified algorithm */ +struct asn1_algorithm aes_128_gcm_algorithm __asn1_algorithm = { + .name = "aes128-gcm", + .cipher = &aes_gcm_algorithm, + .oid = ASN1_CURSOR ( oid_aes_128_gcm ), + .parse = asn1_parse_gcm, +}; + +/** "aes192-gcm" OID-identified algorithm */ +struct asn1_algorithm aes_192_gcm_algorithm __asn1_algorithm = { + .name = "aes192-gcm", + .cipher = &aes_gcm_algorithm, + .oid = ASN1_CURSOR ( oid_aes_192_gcm ), + .parse = asn1_parse_gcm, +}; + +/** "aes256-gcm" OID-identified algorithm */ +struct asn1_algorithm aes_256_gcm_algorithm __asn1_algorithm = { + .name = "aes256-gcm", + .cipher = &aes_gcm_algorithm, + .oid = ASN1_CURSOR ( oid_aes_256_gcm ), + .parse = asn1_parse_gcm, +}; diff --git a/src/crypto/mishmash/oid_md4.c b/src/crypto/mishmash/oid_md4.c index d42f2df19..03b893d47 100644 --- a/src/crypto/mishmash/oid_md4.c +++ b/src/crypto/mishmash/oid_md4.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( FORBIDDEN ); #include <ipxe/md4.h> #include <ipxe/asn1.h> diff --git a/src/crypto/mishmash/oid_md5.c b/src/crypto/mishmash/oid_md5.c index f56dd8b8d..0095fbe0e 100644 --- a/src/crypto/mishmash/oid_md5.c +++ b/src/crypto/mishmash/oid_md5.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( FORBIDDEN ); #include <ipxe/md5.h> #include <ipxe/asn1.h> diff --git a/src/crypto/mishmash/oid_p256.c b/src/crypto/mishmash/oid_p256.c new file mode 100644 index 000000000..81ae1d11e --- /dev/null +++ b/src/crypto/mishmash/oid_p256.c @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); + +#include <byteswap.h> +#include <ipxe/p256.h> +#include <ipxe/asn1.h> +#include <ipxe/tls.h> + +/** "prime256v1" object identifier */ +static uint8_t oid_prime256v1[] = { ASN1_OID_PRIME256V1 }; + +/** "prime256v1" OID-identified algorithm */ +struct asn1_algorithm prime256v1_algorithm __asn1_algorithm = { + .name = "prime256v1", + .curve = &p256_curve, + .oid = ASN1_CURSOR ( oid_prime256v1 ), +}; + +/** P-256 named curve */ +struct tls_named_curve tls_secp256r1_named_curve __tls_named_curve ( 01 ) = { + .curve = &p256_curve, + .code = htons ( TLS_NAMED_CURVE_SECP256R1 ), + .format = TLS_POINT_FORMAT_UNCOMPRESSED, + .pre_master_secret_len = P256_LEN, +}; diff --git a/src/crypto/mishmash/oid_p384.c b/src/crypto/mishmash/oid_p384.c new file mode 100644 index 000000000..a7d36aee4 --- /dev/null +++ b/src/crypto/mishmash/oid_p384.c @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); + +#include <byteswap.h> +#include <ipxe/p384.h> +#include <ipxe/asn1.h> +#include <ipxe/tls.h> + +/** "secp384r1" object identifier */ +static uint8_t oid_secp384r1[] = { ASN1_OID_SECP384R1 }; + +/** "secp384r1" OID-identified algorithm */ +struct asn1_algorithm secp384r1_algorithm __asn1_algorithm = { + .name = "secp384r1", + .curve = &p384_curve, + .oid = ASN1_CURSOR ( oid_secp384r1 ), +}; + +/** P-384 named curve */ +struct tls_named_curve tls_secp384r1_named_curve __tls_named_curve ( 01 ) = { + .curve = &p384_curve, + .code = htons ( TLS_NAMED_CURVE_SECP384R1 ), + .format = TLS_POINT_FORMAT_UNCOMPRESSED, + .pre_master_secret_len = P384_LEN, +}; diff --git a/src/crypto/mishmash/oid_rsa.c b/src/crypto/mishmash/oid_rsa.c index 582022628..02bb59edb 100644 --- a/src/crypto/mishmash/oid_rsa.c +++ b/src/crypto/mishmash/oid_rsa.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/rsa.h> #include <ipxe/asn1.h> diff --git a/src/crypto/mishmash/oid_sha1.c b/src/crypto/mishmash/oid_sha1.c index 5dae6d27c..5ddd2aba8 100644 --- a/src/crypto/mishmash/oid_sha1.c +++ b/src/crypto/mishmash/oid_sha1.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/sha1.h> #include <ipxe/asn1.h> diff --git a/src/crypto/mishmash/oid_sha224.c b/src/crypto/mishmash/oid_sha224.c index ee7ed22e4..6658bda56 100644 --- a/src/crypto/mishmash/oid_sha224.c +++ b/src/crypto/mishmash/oid_sha224.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/sha256.h> #include <ipxe/asn1.h> diff --git a/src/crypto/mishmash/oid_sha256.c b/src/crypto/mishmash/oid_sha256.c index 963fddb63..8da40a70b 100644 --- a/src/crypto/mishmash/oid_sha256.c +++ b/src/crypto/mishmash/oid_sha256.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/sha256.h> #include <ipxe/asn1.h> diff --git a/src/crypto/mishmash/oid_sha384.c b/src/crypto/mishmash/oid_sha384.c index 81ff48bbf..57c1ab53b 100644 --- a/src/crypto/mishmash/oid_sha384.c +++ b/src/crypto/mishmash/oid_sha384.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/sha512.h> #include <ipxe/asn1.h> diff --git a/src/crypto/mishmash/oid_sha512.c b/src/crypto/mishmash/oid_sha512.c index 78bae48b4..73d7cb78f 100644 --- a/src/crypto/mishmash/oid_sha512.c +++ b/src/crypto/mishmash/oid_sha512.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/sha512.h> #include <ipxe/asn1.h> diff --git a/src/crypto/mishmash/oid_sha512_224.c b/src/crypto/mishmash/oid_sha512_224.c index 6f61f9cac..a6291097b 100644 --- a/src/crypto/mishmash/oid_sha512_224.c +++ b/src/crypto/mishmash/oid_sha512_224.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/sha512.h> #include <ipxe/asn1.h> diff --git a/src/crypto/mishmash/oid_sha512_256.c b/src/crypto/mishmash/oid_sha512_256.c index bce4762e4..d36199372 100644 --- a/src/crypto/mishmash/oid_sha512_256.c +++ b/src/crypto/mishmash/oid_sha512_256.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/sha512.h> #include <ipxe/asn1.h> diff --git a/src/crypto/mishmash/oid_x25519.c b/src/crypto/mishmash/oid_x25519.c index 2f8aa065b..2907eb461 100644 --- a/src/crypto/mishmash/oid_x25519.c +++ b/src/crypto/mishmash/oid_x25519.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <byteswap.h> #include <ipxe/x25519.h> @@ -42,4 +43,5 @@ struct asn1_algorithm x25519_algorithm __asn1_algorithm = { struct tls_named_curve tls_x25519_named_curve __tls_named_curve ( 01 ) = { .curve = &x25519_curve, .code = htons ( TLS_NAMED_CURVE_X25519 ), + .pre_master_secret_len = sizeof ( struct x25519_value ), }; diff --git a/src/crypto/mishmash/rsa_aes_cbc_sha1.c b/src/crypto/mishmash/rsa_aes_cbc_sha1.c index 0862fb5ac..35f5f6eb7 100644 --- a/src/crypto/mishmash/rsa_aes_cbc_sha1.c +++ b/src/crypto/mishmash/rsa_aes_cbc_sha1.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <byteswap.h> #include <ipxe/rsa.h> diff --git a/src/crypto/mishmash/rsa_aes_cbc_sha256.c b/src/crypto/mishmash/rsa_aes_cbc_sha256.c index e5928db82..22705df7e 100644 --- a/src/crypto/mishmash/rsa_aes_cbc_sha256.c +++ b/src/crypto/mishmash/rsa_aes_cbc_sha256.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <byteswap.h> #include <ipxe/rsa.h> diff --git a/src/crypto/mishmash/rsa_aes_gcm_sha256.c b/src/crypto/mishmash/rsa_aes_gcm_sha256.c index b18bbd844..d3fd00f1e 100644 --- a/src/crypto/mishmash/rsa_aes_gcm_sha256.c +++ b/src/crypto/mishmash/rsa_aes_gcm_sha256.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <byteswap.h> #include <ipxe/rsa.h> diff --git a/src/crypto/mishmash/rsa_aes_gcm_sha384.c b/src/crypto/mishmash/rsa_aes_gcm_sha384.c index 06558aaed..908db086a 100644 --- a/src/crypto/mishmash/rsa_aes_gcm_sha384.c +++ b/src/crypto/mishmash/rsa_aes_gcm_sha384.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <byteswap.h> #include <ipxe/rsa.h> diff --git a/src/crypto/mishmash/rsa_md5.c b/src/crypto/mishmash/rsa_md5.c index 051afe264..00808c23f 100644 --- a/src/crypto/mishmash/rsa_md5.c +++ b/src/crypto/mishmash/rsa_md5.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( FORBIDDEN ); #include <ipxe/rsa.h> #include <ipxe/md5.h> diff --git a/src/crypto/mishmash/rsa_sha1.c b/src/crypto/mishmash/rsa_sha1.c index 264f871f1..8907ac08a 100644 --- a/src/crypto/mishmash/rsa_sha1.c +++ b/src/crypto/mishmash/rsa_sha1.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/rsa.h> #include <ipxe/sha1.h> diff --git a/src/crypto/mishmash/rsa_sha224.c b/src/crypto/mishmash/rsa_sha224.c index 1465a033d..b676d41f3 100644 --- a/src/crypto/mishmash/rsa_sha224.c +++ b/src/crypto/mishmash/rsa_sha224.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/rsa.h> #include <ipxe/sha256.h> diff --git a/src/crypto/mishmash/rsa_sha256.c b/src/crypto/mishmash/rsa_sha256.c index 7283c3e29..8a6a7a5cf 100644 --- a/src/crypto/mishmash/rsa_sha256.c +++ b/src/crypto/mishmash/rsa_sha256.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/rsa.h> #include <ipxe/sha256.h> diff --git a/src/crypto/mishmash/rsa_sha384.c b/src/crypto/mishmash/rsa_sha384.c index 6f8c29b29..cc1878bd4 100644 --- a/src/crypto/mishmash/rsa_sha384.c +++ b/src/crypto/mishmash/rsa_sha384.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/rsa.h> #include <ipxe/sha512.h> diff --git a/src/crypto/mishmash/rsa_sha512.c b/src/crypto/mishmash/rsa_sha512.c index bb4463a5a..9c995e1c8 100644 --- a/src/crypto/mishmash/rsa_sha512.c +++ b/src/crypto/mishmash/rsa_sha512.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <ipxe/rsa.h> #include <ipxe/sha512.h> diff --git a/src/crypto/ntlm.c b/src/crypto/ntlm.c index fb120f8db..f9ce51bde 100644 --- a/src/crypto/ntlm.c +++ b/src/crypto/ntlm.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** @file * diff --git a/src/crypto/ocsp.c b/src/crypto/ocsp.c index cc957b40c..5d6acb605 100644 --- a/src/crypto/ocsp.c +++ b/src/crypto/ocsp.c @@ -18,6 +18,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER ); +FILE_SECBOOT ( PERMITTED ); #include <stdint.h> #include <stdlib.h> @@ -158,8 +159,8 @@ static int ocsp_request ( struct ocsp_check *ocsp ) { digest_final ( digest, digest_ctx, name_digest ); digest_init ( digest, digest_ctx ); digest_update ( digest, digest_ctx, - ocsp->issuer->subject.public_key.raw_bits.data, - ocsp->issuer->subject.public_key.raw_bits.len ); + ocsp->issuer->subject.public_key.value.data, + ocsp->issuer->subject.public_key.value.len ); digest_final ( digest, digest_ctx, pubkey_digest ); /* Construct request */ @@ -422,8 +423,8 @@ static int ocsp_compare_responder_key_hash ( struct ocsp_check *ocsp, /* Generate SHA1 hash of certificate's public key */ digest_init ( &sha1_algorithm, ctx ); digest_update ( &sha1_algorithm, ctx, - cert->subject.public_key.raw_bits.data, - cert->subject.public_key.raw_bits.len ); + cert->subject.public_key.value.data, + cert->subject.public_key.value.len ); digest_final ( &sha1_algorithm, ctx, digest ); /* Compare responder key hash with hash of certificate's public key */ @@ -701,7 +702,7 @@ static int ocsp_parse_basic_response ( struct ocsp_check *ocsp, const struct asn1_cursor *raw ) { struct ocsp_response *response = &ocsp->response; struct asn1_algorithm **algorithm = &response->algorithm; - struct asn1_bit_string *signature = &response->signature; + struct asn1_cursor *signature = &response->signature; struct asn1_cursor cursor; int rc; @@ -726,7 +727,8 @@ static int ocsp_parse_basic_response ( struct ocsp_check *ocsp, asn1_skip_any ( &cursor ); /* Parse signature */ - if ( ( rc = asn1_integral_bit_string ( &cursor, signature ) ) != 0 ) { + memcpy ( signature, &cursor, sizeof ( *signature ) ); + if ( ( rc = asn1_enter_bits ( signature, NULL ) ) != 0 ) { DBGC ( ocsp, "OCSP %p \"%s\" cannot parse signature: %s\n", ocsp, x509_name ( ocsp->cert ), strerror ( rc ) ); return rc; @@ -844,10 +846,9 @@ static int ocsp_check_signature ( struct ocsp_check *ocsp, struct ocsp_response *response = &ocsp->response; struct digest_algorithm *digest = response->algorithm->digest; struct pubkey_algorithm *pubkey = response->algorithm->pubkey; - struct x509_public_key *public_key = &signer->subject.public_key; + struct asn1_cursor *key = &signer->subject.public_key.raw; uint8_t digest_ctx[ digest->ctxsize ]; uint8_t digest_out[ digest->digestsize ]; - uint8_t pubkey_ctx[ pubkey->ctxsize ]; int rc; /* Generate digest */ @@ -856,30 +857,17 @@ static int ocsp_check_signature ( struct ocsp_check *ocsp, response->tbs.len ); digest_final ( digest, digest_ctx, digest_out ); - /* Initialise public-key algorithm */ - if ( ( rc = pubkey_init ( pubkey, pubkey_ctx, public_key->raw.data, - public_key->raw.len ) ) != 0 ) { - DBGC ( ocsp, "OCSP %p \"%s\" could not initialise public key: " - "%s\n", ocsp, x509_name ( ocsp->cert ), strerror ( rc )); - goto err_init; - } - /* Verify digest */ - if ( ( rc = pubkey_verify ( pubkey, pubkey_ctx, digest, digest_out, - response->signature.data, - response->signature.len ) ) != 0 ) { + if ( ( rc = pubkey_verify ( pubkey, key, digest, digest_out, + &response->signature ) ) != 0 ) { DBGC ( ocsp, "OCSP %p \"%s\" signature verification failed: " "%s\n", ocsp, x509_name ( ocsp->cert ), strerror ( rc )); - goto err_verify; + return rc; } DBGC2 ( ocsp, "OCSP %p \"%s\" signature is correct\n", ocsp, x509_name ( ocsp->cert ) ); - - err_verify: - pubkey_final ( pubkey, pubkey_ctx ); - err_init: - return rc; + return 0; } /** diff --git a/src/crypto/p256.c b/src/crypto/p256.c new file mode 100644 index 000000000..a513555b3 --- /dev/null +++ b/src/crypto/p256.c @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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_SECBOOT ( PERMITTED ); + +/** @file + * + * NIST P-256 elliptic curve + * + */ + +#include <ipxe/p256.h> + +/** P-256 field prime */ +static const uint8_t p256_prime[P256_LEN] = { + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; + +/** P-256 constant "a" */ +static const uint8_t p256_a[P256_LEN] = { + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc +}; + +/** P-256 constant "b" */ +static const uint8_t p256_b[P256_LEN] = { + 0x5a, 0xc6, 0x35, 0xd8, 0xaa, 0x3a, 0x93, 0xe7, 0xb3, 0xeb, 0xbd, + 0x55, 0x76, 0x98, 0x86, 0xbc, 0x65, 0x1d, 0x06, 0xb0, 0xcc, 0x53, + 0xb0, 0xf6, 0x3b, 0xce, 0x3c, 0x3e, 0x27, 0xd2, 0x60, 0x4b +}; + +/** P-256 base point */ +static const uint8_t p256_base[ P256_LEN * 2 ] = { + 0x6b, 0x17, 0xd1, 0xf2, 0xe1, 0x2c, 0x42, 0x47, 0xf8, 0xbc, 0xe6, + 0xe5, 0x63, 0xa4, 0x40, 0xf2, 0x77, 0x03, 0x7d, 0x81, 0x2d, 0xeb, + 0x33, 0xa0, 0xf4, 0xa1, 0x39, 0x45, 0xd8, 0x98, 0xc2, 0x96, 0x4f, + 0xe3, 0x42, 0xe2, 0xfe, 0x1a, 0x7f, 0x9b, 0x8e, 0xe7, 0xeb, 0x4a, + 0x7c, 0x0f, 0x9e, 0x16, 0x2b, 0xce, 0x33, 0x57, 0x6b, 0x31, 0x5e, + 0xce, 0xcb, 0xb6, 0x40, 0x68, 0x37, 0xbf, 0x51, 0xf5 +}; + +/** P-256 group order */ +static const uint8_t p256_order[P256_LEN] = { + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xbc, 0xe6, 0xfa, 0xad, 0xa7, 0x17, + 0x9e, 0x84, 0xf3, 0xb9, 0xca, 0xc2, 0xfc, 0x63, 0x25, 0x51 +}; + +/** P-256 elliptic curve */ +WEIERSTRASS_CURVE ( p256, p256_curve, P256_LEN, + p256_prime, p256_a, p256_b, p256_base, p256_order ); diff --git a/src/crypto/p384.c b/src/crypto/p384.c new file mode 100644 index 000000000..bdd23d460 --- /dev/null +++ b/src/crypto/p384.c @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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_SECBOOT ( PERMITTED ); + +/** @file + * + * NIST P-384 elliptic curve + * + */ + +#include <ipxe/p384.h> + +/** P-384 field prime */ +static const uint8_t p384_prime[P384_LEN] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff +}; + +/** P-384 constant "a" */ +static const uint8_t p384_a[P384_LEN] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xfc +}; + +/** P-384 constant "b" */ +static const uint8_t p384_b[P384_LEN] = { + 0xb3, 0x31, 0x2f, 0xa7, 0xe2, 0x3e, 0xe7, 0xe4, 0x98, 0x8e, 0x05, + 0x6b, 0xe3, 0xf8, 0x2d, 0x19, 0x18, 0x1d, 0x9c, 0x6e, 0xfe, 0x81, + 0x41, 0x12, 0x03, 0x14, 0x08, 0x8f, 0x50, 0x13, 0x87, 0x5a, 0xc6, + 0x56, 0x39, 0x8d, 0x8a, 0x2e, 0xd1, 0x9d, 0x2a, 0x85, 0xc8, 0xed, + 0xd3, 0xec, 0x2a, 0xef +}; + +/** P-384 base point */ +static const uint8_t p384_base[ P384_LEN * 2 ] = { + 0xaa, 0x87, 0xca, 0x22, 0xbe, 0x8b, 0x05, 0x37, 0x8e, 0xb1, 0xc7, + 0x1e, 0xf3, 0x20, 0xad, 0x74, 0x6e, 0x1d, 0x3b, 0x62, 0x8b, 0xa7, + 0x9b, 0x98, 0x59, 0xf7, 0x41, 0xe0, 0x82, 0x54, 0x2a, 0x38, 0x55, + 0x02, 0xf2, 0x5d, 0xbf, 0x55, 0x29, 0x6c, 0x3a, 0x54, 0x5e, 0x38, + 0x72, 0x76, 0x0a, 0xb7, 0x36, 0x17, 0xde, 0x4a, 0x96, 0x26, 0x2c, + 0x6f, 0x5d, 0x9e, 0x98, 0xbf, 0x92, 0x92, 0xdc, 0x29, 0xf8, 0xf4, + 0x1d, 0xbd, 0x28, 0x9a, 0x14, 0x7c, 0xe9, 0xda, 0x31, 0x13, 0xb5, + 0xf0, 0xb8, 0xc0, 0x0a, 0x60, 0xb1, 0xce, 0x1d, 0x7e, 0x81, 0x9d, + 0x7a, 0x43, 0x1d, 0x7c, 0x90, 0xea, 0x0e, 0x5f +}; + +/** P-384 group order */ +static const uint8_t p384_order[P384_LEN] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xc7, 0x63, 0x4d, 0x81, 0xf4, 0x37, 0x2d, 0xdf, 0x58, + 0x1a, 0x0d, 0xb2, 0x48, 0xb0, 0xa7, 0x7a, 0xec, 0xec, 0x19, 0x6a, + 0xcc, 0xc5, 0x29, 0x73 +}; + +/** P-384 elliptic curve */ +WEIERSTRASS_CURVE ( p384, p384_curve, P384_LEN, + p384_prime, p384_a, p384_b, p384_base, p384_order ); diff --git a/src/crypto/privkey.c b/src/crypto/privkey.c index c15edf130..c67a4400b 100644 --- a/src/crypto/privkey.c +++ b/src/crypto/privkey.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <stdint.h> #include <stdlib.h> @@ -53,7 +54,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /* Raw private key data */ extern char private_key_data[]; -extern char private_key_len[]; +extern size_t ABS_SYMBOL ( private_key_len ); __asm__ ( ".section \".rodata\", \"a\", " PROGBITS "\n\t" "\nprivate_key_data:\n\t" #ifdef PRIVATE_KEY @@ -68,7 +69,7 @@ struct private_key private_key = { .refcnt = REF_INIT ( ref_no_free ), .builder = { .data = private_key_data, - .len = ( ( size_t ) private_key_len ), + .len = ABS_VALUE_INIT ( private_key_len ), }, }; diff --git a/src/crypto/random_nz.c b/src/crypto/random_nz.c index 5fe576e05..96b12359c 100644 --- a/src/crypto/random_nz.c +++ b/src/crypto/random_nz.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** @file * diff --git a/src/crypto/rbg.c b/src/crypto/rbg.c index 4b45b3474..17914542e 100644 --- a/src/crypto/rbg.c +++ b/src/crypto/rbg.c @@ -34,6 +34,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** @file * @@ -75,6 +76,9 @@ static int rbg_startup ( void ) { int len; int rc; + /* Record that startup has been attempted (even if unsuccessful) */ + rbg.started = 1; + /* Try to obtain system UUID for use as personalisation * string, in accordance with ANS X9.82 Part 3-2007 Section * 8.5.2. If no UUID is available, proceed without a @@ -98,6 +102,33 @@ static int rbg_startup ( void ) { } /** + * Generate bits using RBG + * + * @v additional Additional input + * @v additional_len Length of additional input + * @v prediction_resist Prediction resistance is required + * @v data Output buffer + * @v len Length of output buffer + * @ret rc Return status code + * + * This is the RBG_Generate function defined in ANS X9.82 Part 4 + * (April 2011 Draft) Section 9.1.2.2. + */ +int rbg_generate ( const void *additional, size_t additional_len, + int prediction_resist, void *data, size_t len ) { + + /* Attempt startup, if not already attempted */ + if ( ! rbg.started ) + rbg_startup(); + + /* Generate bits. The DRBG will itself return an error if it + * is not valid (e.g. due to an instantiation failure). + */ + return drbg_generate ( &rbg.state, additional, additional_len, + prediction_resist, data, len ); +} + +/** * Shut down RBG * */ @@ -105,16 +136,21 @@ static void rbg_shutdown ( void ) { /* Uninstantiate DRBG */ drbg_uninstantiate ( &rbg.state ); + + /* Clear startup attempted flag */ + rbg.started = 0; } /** RBG startup function */ static void rbg_startup_fn ( void ) { - /* Start up RBG. There is no way to report an error at this - * stage, but a failed startup will result in an invalid DRBG - * that refuses to generate bits. + /* Start up RBG (if not already started on demand). There is + * no way to report an error at this stage, but a failed + * startup will result in an invalid DRBG that refuses to + * generate bits. */ - rbg_startup(); + if ( ! rbg.started ) + rbg_startup(); } /** RBG shutdown function */ diff --git a/src/crypto/rootcert.c b/src/crypto/rootcert.c index 0835ff071..6eb08256a 100644 --- a/src/crypto/rootcert.c +++ b/src/crypto/rootcert.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <stdlib.h> #include <ipxe/crypto.h> @@ -42,10 +43,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define FINGERPRINT_LEN SHA256_DIGEST_SIZE /* Allow trusted certificates to be overridden if not explicitly specified */ -#ifdef TRUSTED -#define ALLOW_TRUST_OVERRIDE 0 -#else -#define ALLOW_TRUST_OVERRIDE 1 +#ifndef ALLOW_TRUST_OVERRIDE + #ifdef TRUSTED + #define ALLOW_TRUST_OVERRIDE 0 + #else + #define ALLOW_TRUST_OVERRIDE 1 + #endif #endif /* Use iPXE root CA if no trusted certificates are explicitly specified */ @@ -58,6 +61,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); 0xed, 0x1a, #endif +/** Flag indicating if root of trust may be overridden at runtime */ +const int allow_trust_override = ALLOW_TRUST_OVERRIDE; + /** Root certificate fingerprints */ static const uint8_t fingerprints[] = { TRUSTED }; diff --git a/src/crypto/rsa.c b/src/crypto/rsa.c index 16c67d822..be055d881 100644 --- a/src/crypto/rsa.c +++ b/src/crypto/rsa.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <stdint.h> #include <stdlib.h> @@ -47,6 +48,28 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define EINFO_EACCES_VERIFY \ __einfo_uniqify ( EINFO_EACCES, 0x01, "RSA signature incorrect" ) +/** An RSA context */ +struct rsa_context { + /** Allocated memory */ + void *dynamic; + /** Modulus */ + bigint_element_t *modulus0; + /** Modulus size */ + unsigned int size; + /** Modulus length */ + size_t max_len; + /** Exponent */ + bigint_element_t *exponent0; + /** Exponent size */ + unsigned int exponent_size; + /** Input buffer */ + bigint_element_t *input0; + /** Output buffer */ + bigint_element_t *output0; + /** Temporary working space for modular exponentiation */ + void *tmp; +}; + /** * Identify RSA prefix * @@ -69,10 +92,9 @@ rsa_find_prefix ( struct digest_algorithm *digest ) { * * @v context RSA context */ -static void rsa_free ( struct rsa_context *context ) { +static inline void rsa_free ( struct rsa_context *context ) { free ( context->dynamic ); - context->dynamic = NULL; } /** @@ -88,8 +110,7 @@ static int rsa_alloc ( struct rsa_context *context, size_t modulus_len, unsigned int size = bigint_required_size ( modulus_len ); unsigned int exponent_size = bigint_required_size ( exponent_len ); bigint_t ( size ) *modulus; - bigint_t ( exponent_size ) *exponent; - size_t tmp_len = bigint_mod_exp_tmp_len ( modulus, exponent ); + size_t tmp_len = bigint_mod_exp_tmp_len ( modulus ); struct { bigint_t ( size ) modulus; bigint_t ( exponent_size ) exponent; @@ -98,9 +119,6 @@ static int rsa_alloc ( struct rsa_context *context, size_t modulus_len, uint8_t tmp[tmp_len]; } __attribute__ (( packed )) *dynamic; - /* Free any existing dynamic storage */ - rsa_free ( context ); - /* Allocate dynamic storage */ dynamic = malloc ( sizeof ( *dynamic ) ); if ( ! dynamic ) @@ -121,34 +139,6 @@ static int rsa_alloc ( struct rsa_context *context, size_t modulus_len, } /** - * Parse RSA integer - * - * @v integer Integer to fill in - * @v raw ASN.1 cursor - * @ret rc Return status code - */ -static int rsa_parse_integer ( struct asn1_cursor *integer, - const struct asn1_cursor *raw ) { - - /* Enter integer */ - memcpy ( integer, raw, sizeof ( *integer ) ); - asn1_enter ( integer, ASN1_INTEGER ); - - /* Skip initial sign byte if applicable */ - if ( ( integer->len > 1 ) && - ( *( ( uint8_t * ) integer->data ) == 0x00 ) ) { - integer->data++; - integer->len--; - } - - /* Fail if cursor or integer are invalid */ - if ( ! integer->len ) - return -EINVAL; - - return 0; -} - -/** * Parse RSA modulus and exponent * * @v modulus Modulus to fill in @@ -159,7 +149,6 @@ static int rsa_parse_integer ( struct asn1_cursor *integer, static int rsa_parse_mod_exp ( struct asn1_cursor *modulus, struct asn1_cursor *exponent, const struct asn1_cursor *raw ) { - struct asn1_bit_string bits; struct asn1_cursor cursor; int is_private; int rc; @@ -178,8 +167,8 @@ static int rsa_parse_mod_exp ( struct asn1_cursor *modulus, asn1_skip_any ( &cursor ); /* Enter privateKey, if present */ - if ( asn1_check_algorithm ( &cursor, - &rsa_encryption_algorithm ) == 0 ) { + if ( asn1_check_algorithm ( &cursor, &rsa_encryption_algorithm, + NULL ) == 0 ) { /* Skip privateKeyAlgorithm */ asn1_skip_any ( &cursor ); @@ -203,17 +192,15 @@ static int rsa_parse_mod_exp ( struct asn1_cursor *modulus, asn1_skip ( &cursor, ASN1_SEQUENCE ); /* Enter subjectPublicKey */ - if ( ( rc = asn1_integral_bit_string ( &cursor, &bits ) ) != 0 ) - return rc; - cursor.data = bits.data; - cursor.len = bits.len; + asn1_enter_bits ( &cursor, NULL ); /* Enter RSAPublicKey */ asn1_enter ( &cursor, ASN1_SEQUENCE ); } /* Extract modulus */ - if ( ( rc = rsa_parse_integer ( modulus, &cursor ) ) != 0 ) + memcpy ( modulus, &cursor, sizeof ( *modulus ) ); + if ( ( rc = asn1_enter_unsigned ( modulus ) ) != 0 ) return rc; asn1_skip_any ( &cursor ); @@ -222,7 +209,8 @@ static int rsa_parse_mod_exp ( struct asn1_cursor *modulus, asn1_skip ( &cursor, ASN1_INTEGER ); /* Extract publicExponent/privateExponent */ - if ( ( rc = rsa_parse_integer ( exponent, &cursor ) ) != 0 ) + memcpy ( exponent, &cursor, sizeof ( *exponent ) ); + if ( ( rc = asn1_enter_unsigned ( exponent ) ) != 0 ) return rc; return 0; @@ -231,29 +219,23 @@ static int rsa_parse_mod_exp ( struct asn1_cursor *modulus, /** * Initialise RSA cipher * - * @v ctx RSA context + * @v context RSA context * @v key Key - * @v key_len Length of key * @ret rc Return status code */ -static int rsa_init ( void *ctx, const void *key, size_t key_len ) { - struct rsa_context *context = ctx; +static int rsa_init ( struct rsa_context *context, + const struct asn1_cursor *key ) { struct asn1_cursor modulus; struct asn1_cursor exponent; - struct asn1_cursor cursor; int rc; /* Initialise context */ memset ( context, 0, sizeof ( *context ) ); - /* Initialise cursor */ - cursor.data = key; - cursor.len = key_len; - /* Parse modulus and exponent */ - if ( ( rc = rsa_parse_mod_exp ( &modulus, &exponent, &cursor ) ) != 0 ){ + if ( ( rc = rsa_parse_mod_exp ( &modulus, &exponent, key ) ) != 0 ){ DBGC ( context, "RSA %p invalid modulus/exponent:\n", context ); - DBGC_HDA ( context, 0, cursor.data, cursor.len ); + DBGC_HDA ( context, 0, key->data, key->len ); goto err_parse; } @@ -281,18 +263,6 @@ static int rsa_init ( void *ctx, const void *key, size_t key_len ) { } /** - * Calculate RSA maximum output length - * - * @v ctx RSA context - * @ret max_len Maximum output length - */ -static size_t rsa_max_len ( void *ctx ) { - struct rsa_context *context = ctx; - - return context->max_len; -} - -/** * Perform RSA cipher operation * * @v context RSA context @@ -320,111 +290,158 @@ static void rsa_cipher ( struct rsa_context *context, /** * Encrypt using RSA * - * @v ctx RSA context + * @v key Key * @v plaintext Plaintext - * @v plaintext_len Length of plaintext * @v ciphertext Ciphertext * @ret ciphertext_len Length of ciphertext, or negative error */ -static int rsa_encrypt ( void *ctx, const void *plaintext, - size_t plaintext_len, void *ciphertext ) { - struct rsa_context *context = ctx; +static int rsa_encrypt ( const struct asn1_cursor *key, + const struct asn1_cursor *plaintext, + struct asn1_builder *ciphertext ) { + struct rsa_context context; void *temp; uint8_t *encoded; - size_t max_len = ( context->max_len - 11 ); - size_t random_nz_len = ( max_len - plaintext_len + 8 ); + size_t max_len; + size_t random_nz_len; int rc; + DBGC ( &context, "RSA %p encrypting:\n", &context ); + DBGC_HDA ( &context, 0, plaintext->data, plaintext->len ); + + /* Initialise context */ + if ( ( rc = rsa_init ( &context, key ) ) != 0 ) + goto err_init; + + /* Calculate lengths */ + max_len = ( context.max_len - 11 ); + random_nz_len = ( max_len - plaintext->len + 8 ); + /* Sanity check */ - if ( plaintext_len > max_len ) { - DBGC ( context, "RSA %p plaintext too long (%zd bytes, max " - "%zd)\n", context, plaintext_len, max_len ); - return -ERANGE; + if ( plaintext->len > max_len ) { + DBGC ( &context, "RSA %p plaintext too long (%zd bytes, max " + "%zd)\n", &context, plaintext->len, max_len ); + rc = -ERANGE; + goto err_sanity; } - DBGC ( context, "RSA %p encrypting:\n", context ); - DBGC_HDA ( context, 0, plaintext, plaintext_len ); /* Construct encoded message (using the big integer output * buffer as temporary storage) */ - temp = context->output0; + temp = context.output0; encoded = temp; encoded[0] = 0x00; encoded[1] = 0x02; if ( ( rc = get_random_nz ( &encoded[2], random_nz_len ) ) != 0 ) { - DBGC ( context, "RSA %p could not generate random data: %s\n", - context, strerror ( rc ) ); - return rc; + DBGC ( &context, "RSA %p could not generate random data: %s\n", + &context, strerror ( rc ) ); + goto err_random; } encoded[ 2 + random_nz_len ] = 0x00; - memcpy ( &encoded[ context->max_len - plaintext_len ], - plaintext, plaintext_len ); + memcpy ( &encoded[ context.max_len - plaintext->len ], + plaintext->data, plaintext->len ); + + /* Create space for ciphertext */ + if ( ( rc = asn1_grow ( ciphertext, context.max_len ) ) != 0 ) + goto err_grow; /* Encipher the encoded message */ - rsa_cipher ( context, encoded, ciphertext ); - DBGC ( context, "RSA %p encrypted:\n", context ); - DBGC_HDA ( context, 0, ciphertext, context->max_len ); + rsa_cipher ( &context, encoded, ciphertext->data ); + DBGC ( &context, "RSA %p encrypted:\n", &context ); + DBGC_HDA ( &context, 0, ciphertext->data, context.max_len ); + + /* Free context */ + rsa_free ( &context ); - return context->max_len; + return 0; + + err_grow: + err_random: + err_sanity: + rsa_free ( &context ); + err_init: + return rc; } /** * Decrypt using RSA * - * @v ctx RSA context + * @v key Key * @v ciphertext Ciphertext - * @v ciphertext_len Ciphertext length * @v plaintext Plaintext - * @ret plaintext_len Plaintext length, or negative error + * @ret rc Return status code */ -static int rsa_decrypt ( void *ctx, const void *ciphertext, - size_t ciphertext_len, void *plaintext ) { - struct rsa_context *context = ctx; +static int rsa_decrypt ( const struct asn1_cursor *key, + const struct asn1_cursor *ciphertext, + struct asn1_builder *plaintext ) { + struct rsa_context context; void *temp; uint8_t *encoded; uint8_t *end; uint8_t *zero; uint8_t *start; - size_t plaintext_len; + size_t len; + int rc; + + DBGC ( &context, "RSA %p decrypting:\n", &context ); + DBGC_HDA ( &context, 0, ciphertext->data, ciphertext->len ); + + /* Initialise context */ + if ( ( rc = rsa_init ( &context, key ) ) != 0 ) + goto err_init; /* Sanity check */ - if ( ciphertext_len != context->max_len ) { - DBGC ( context, "RSA %p ciphertext incorrect length (%zd " + if ( ciphertext->len != context.max_len ) { + DBGC ( &context, "RSA %p ciphertext incorrect length (%zd " "bytes, should be %zd)\n", - context, ciphertext_len, context->max_len ); - return -ERANGE; + &context, ciphertext->len, context.max_len ); + rc = -ERANGE; + goto err_sanity; } - DBGC ( context, "RSA %p decrypting:\n", context ); - DBGC_HDA ( context, 0, ciphertext, ciphertext_len ); /* Decipher the message (using the big integer input buffer as * temporary storage) */ - temp = context->input0; + temp = context.input0; encoded = temp; - rsa_cipher ( context, ciphertext, encoded ); + rsa_cipher ( &context, ciphertext->data, encoded ); /* Parse the message */ - end = ( encoded + context->max_len ); - if ( ( encoded[0] != 0x00 ) || ( encoded[1] != 0x02 ) ) - goto invalid; + end = ( encoded + context.max_len ); + if ( ( encoded[0] != 0x00 ) || ( encoded[1] != 0x02 ) ) { + rc = -EINVAL; + goto err_invalid; + } zero = memchr ( &encoded[2], 0, ( end - &encoded[2] ) ); - if ( ! zero ) - goto invalid; + if ( ! zero ) { + DBGC ( &context, "RSA %p invalid decrypted message:\n", + &context ); + DBGC_HDA ( &context, 0, encoded, context.max_len ); + rc = -EINVAL; + goto err_invalid; + } start = ( zero + 1 ); - plaintext_len = ( end - start ); + len = ( end - start ); + + /* Create space for plaintext */ + if ( ( rc = asn1_grow ( plaintext, len ) ) != 0 ) + goto err_grow; /* Copy out message */ - memcpy ( plaintext, start, plaintext_len ); - DBGC ( context, "RSA %p decrypted:\n", context ); - DBGC_HDA ( context, 0, plaintext, plaintext_len ); + memcpy ( plaintext->data, start, len ); + DBGC ( &context, "RSA %p decrypted:\n", &context ); + DBGC_HDA ( &context, 0, plaintext->data, len ); - return plaintext_len; + /* Free context */ + rsa_free ( &context ); - invalid: - DBGC ( context, "RSA %p invalid decrypted message:\n", context ); - DBGC_HDA ( context, 0, encoded, context->max_len ); - return -EINVAL; + return 0; + + err_grow: + err_invalid: + err_sanity: + rsa_free ( &context ); + err_init: + return rc; } /** @@ -458,9 +475,9 @@ static int rsa_encode_digest ( struct rsa_context *context, /* Sanity check */ max_len = ( context->max_len - 11 ); if ( digestinfo_len > max_len ) { - DBGC ( context, "RSA %p %s digestInfo too long (%zd bytes, max" - "%zd)\n", - context, digest->name, digestinfo_len, max_len ); + DBGC ( context, "RSA %p %s digestInfo too long (%zd bytes, " + "max %zd)\n", context, digest->name, digestinfo_len, + max_len ); return -ERANGE; } DBGC ( context, "RSA %p encoding %s digest:\n", @@ -488,137 +505,149 @@ static int rsa_encode_digest ( struct rsa_context *context, /** * Sign digest value using RSA * - * @v ctx RSA context + * @v key Key * @v digest Digest algorithm * @v value Digest value * @v signature Signature - * @ret signature_len Signature length, or negative error + * @ret rc Return status code */ -static int rsa_sign ( void *ctx, struct digest_algorithm *digest, - const void *value, void *signature ) { - struct rsa_context *context = ctx; - void *temp; +static int rsa_sign ( const struct asn1_cursor *key, + struct digest_algorithm *digest, const void *value, + struct asn1_builder *signature ) { + struct rsa_context context; int rc; - DBGC ( context, "RSA %p signing %s digest:\n", context, digest->name ); - DBGC_HDA ( context, 0, value, digest->digestsize ); + DBGC ( &context, "RSA %p signing %s digest:\n", + &context, digest->name ); + DBGC_HDA ( &context, 0, value, digest->digestsize ); - /* Encode digest (using the big integer output buffer as - * temporary storage) - */ - temp = context->output0; - if ( ( rc = rsa_encode_digest ( context, digest, value, temp ) ) != 0 ) - return rc; + /* Initialise context */ + if ( ( rc = rsa_init ( &context, key ) ) != 0 ) + goto err_init; + + /* Create space for encoded digest and signature */ + if ( ( rc = asn1_grow ( signature, context.max_len ) ) != 0 ) + goto err_grow; + + /* Encode digest */ + if ( ( rc = rsa_encode_digest ( &context, digest, value, + signature->data ) ) != 0 ) + goto err_encode; /* Encipher the encoded digest */ - rsa_cipher ( context, temp, signature ); - DBGC ( context, "RSA %p signed %s digest:\n", context, digest->name ); - DBGC_HDA ( context, 0, signature, context->max_len ); + rsa_cipher ( &context, signature->data, signature->data ); + DBGC ( &context, "RSA %p signed %s digest:\n", &context, digest->name ); + DBGC_HDA ( &context, 0, signature->data, signature->len ); + + /* Free context */ + rsa_free ( &context ); + + return 0; - return context->max_len; + err_encode: + err_grow: + rsa_free ( &context ); + err_init: + return rc; } /** * Verify signed digest value using RSA * - * @v ctx RSA context + * @v key Key * @v digest Digest algorithm * @v value Digest value * @v signature Signature - * @v signature_len Signature length * @ret rc Return status code */ -static int rsa_verify ( void *ctx, struct digest_algorithm *digest, - const void *value, const void *signature, - size_t signature_len ) { - struct rsa_context *context = ctx; +static int rsa_verify ( const struct asn1_cursor *key, + struct digest_algorithm *digest, const void *value, + const struct asn1_cursor *signature ) { + struct rsa_context context; void *temp; void *expected; void *actual; int rc; + DBGC ( &context, "RSA %p verifying %s digest:\n", + &context, digest->name ); + DBGC_HDA ( &context, 0, value, digest->digestsize ); + DBGC_HDA ( &context, 0, signature->data, signature->len ); + + /* Initialise context */ + if ( ( rc = rsa_init ( &context, key ) ) != 0 ) + goto err_init; + /* Sanity check */ - if ( signature_len != context->max_len ) { - DBGC ( context, "RSA %p signature incorrect length (%zd " + if ( signature->len != context.max_len ) { + DBGC ( &context, "RSA %p signature incorrect length (%zd " "bytes, should be %zd)\n", - context, signature_len, context->max_len ); - return -ERANGE; + &context, signature->len, context.max_len ); + rc = -ERANGE; + goto err_sanity; } - DBGC ( context, "RSA %p verifying %s digest:\n", - context, digest->name ); - DBGC_HDA ( context, 0, value, digest->digestsize ); - DBGC_HDA ( context, 0, signature, signature_len ); /* Decipher the signature (using the big integer input buffer * as temporary storage) */ - temp = context->input0; + temp = context.input0; expected = temp; - rsa_cipher ( context, signature, expected ); - DBGC ( context, "RSA %p deciphered signature:\n", context ); - DBGC_HDA ( context, 0, expected, context->max_len ); + rsa_cipher ( &context, signature->data, expected ); + DBGC ( &context, "RSA %p deciphered signature:\n", &context ); + DBGC_HDA ( &context, 0, expected, context.max_len ); /* Encode digest (using the big integer output buffer as * temporary storage) */ - temp = context->output0; + temp = context.output0; actual = temp; - if ( ( rc = rsa_encode_digest ( context, digest, value, actual ) ) !=0 ) - return rc; + if ( ( rc = rsa_encode_digest ( &context, digest, value, + actual ) ) != 0 ) + goto err_encode; /* Verify the signature */ - if ( memcmp ( actual, expected, context->max_len ) != 0 ) { - DBGC ( context, "RSA %p signature verification failed\n", - context ); - return -EACCES_VERIFY; + if ( memcmp ( actual, expected, context.max_len ) != 0 ) { + DBGC ( &context, "RSA %p signature verification failed\n", + &context ); + rc = -EACCES_VERIFY; + goto err_verify; } - DBGC ( context, "RSA %p signature verified successfully\n", context ); - return 0; -} + /* Free context */ + rsa_free ( &context ); -/** - * Finalise RSA cipher - * - * @v ctx RSA context - */ -static void rsa_final ( void *ctx ) { - struct rsa_context *context = ctx; + DBGC ( &context, "RSA %p signature verified successfully\n", &context ); + return 0; - rsa_free ( context ); + err_verify: + err_encode: + err_sanity: + rsa_free ( &context ); + err_init: + return rc; } /** * Check for matching RSA public/private key pair * * @v private_key Private key - * @v private_key_len Private key length * @v public_key Public key - * @v public_key_len Public key length * @ret rc Return status code */ -static int rsa_match ( const void *private_key, size_t private_key_len, - const void *public_key, size_t public_key_len ) { +static int rsa_match ( const struct asn1_cursor *private_key, + const struct asn1_cursor *public_key ) { struct asn1_cursor private_modulus; struct asn1_cursor private_exponent; - struct asn1_cursor private_cursor; struct asn1_cursor public_modulus; struct asn1_cursor public_exponent; - struct asn1_cursor public_cursor; int rc; - /* Initialise cursors */ - private_cursor.data = private_key; - private_cursor.len = private_key_len; - public_cursor.data = public_key; - public_cursor.len = public_key_len; - /* Parse moduli and exponents */ if ( ( rc = rsa_parse_mod_exp ( &private_modulus, &private_exponent, - &private_cursor ) ) != 0 ) + private_key ) ) != 0 ) return rc; if ( ( rc = rsa_parse_mod_exp ( &public_modulus, &public_exponent, - &public_cursor ) ) != 0 ) + public_key ) ) != 0 ) return rc; /* Compare moduli */ @@ -631,14 +660,10 @@ static int rsa_match ( const void *private_key, size_t private_key_len, /** RSA public-key algorithm */ struct pubkey_algorithm rsa_algorithm = { .name = "rsa", - .ctxsize = RSA_CTX_SIZE, - .init = rsa_init, - .max_len = rsa_max_len, .encrypt = rsa_encrypt, .decrypt = rsa_decrypt, .sign = rsa_sign, .verify = rsa_verify, - .final = rsa_final, .match = rsa_match, }; diff --git a/src/crypto/sha1.c b/src/crypto/sha1.c index 8eecc75b3..023becec6 100644 --- a/src/crypto/sha1.c +++ b/src/crypto/sha1.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** @file * diff --git a/src/crypto/sha224.c b/src/crypto/sha224.c index e54a0abb0..7e0cfd34e 100644 --- a/src/crypto/sha224.c +++ b/src/crypto/sha224.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** @file * diff --git a/src/crypto/sha256.c b/src/crypto/sha256.c index c30300eb4..742393612 100644 --- a/src/crypto/sha256.c +++ b/src/crypto/sha256.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** @file * diff --git a/src/crypto/sha384.c b/src/crypto/sha384.c index f1af6fc6f..3e5e98a31 100644 --- a/src/crypto/sha384.c +++ b/src/crypto/sha384.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** @file * diff --git a/src/crypto/sha512.c b/src/crypto/sha512.c index d7d44b284..724cb71a5 100644 --- a/src/crypto/sha512.c +++ b/src/crypto/sha512.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** @file * diff --git a/src/crypto/sha512_224.c b/src/crypto/sha512_224.c index b6728726c..3b256a3b9 100644 --- a/src/crypto/sha512_224.c +++ b/src/crypto/sha512_224.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** @file * diff --git a/src/crypto/sha512_256.c b/src/crypto/sha512_256.c index 8163631e0..04df3f5bc 100644 --- a/src/crypto/sha512_256.c +++ b/src/crypto/sha512_256.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** @file * diff --git a/src/crypto/weierstrass.c b/src/crypto/weierstrass.c new file mode 100644 index 000000000..a64626c85 --- /dev/null +++ b/src/crypto/weierstrass.c @@ -0,0 +1,1027 @@ +/* + * Copyright (C) 2024 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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_SECBOOT ( PERMITTED ); + +/** @file + * + * Weierstrass elliptic curves + * + * The implementation is based upon Algorithm 1 from "Complete + * addition formulas for prime order elliptic curves" (Joost Renes, + * Craig Costello, and Lejla Batina), available from + * + * https://www.microsoft.com/en-us/research/wp-content/uploads/2016/06/complete-2.pdf + * + * The steps within the algorithm have been reordered and temporary + * variables shuffled to reduce stack usage, and calculations are + * carried out modulo small multiples of the field prime in order to + * elide reductions after intermediate addition and subtraction + * operations. + * + * The algorithm is encoded using a bytecode representation, since + * this substantially reduces the code size compared to direct + * implementation of the big integer operations. + */ + +#include <errno.h> +#include <ipxe/weierstrass.h> + +/** Big integer register names */ +enum weierstrass_register { + + /* + * Read-only registers + */ + + /* Curve constant "a" (for multiply), zero (for add/subtract) */ + WEIERSTRASS_a = 0, + /* Curve constant "3b" */ + WEIERSTRASS_3b, + /* Augend (x,y,z) co-ordinates */ + WEIERSTRASS_x1, + WEIERSTRASS_y1, + WEIERSTRASS_z1, + /* Addend (x,y,z) co-ordinates */ + WEIERSTRASS_x2, + WEIERSTRASS_y2, + WEIERSTRASS_z2, + + /* + * Read-write registers + */ + + /* Temporary working registers */ + WEIERSTRASS_Wt, + WEIERSTRASS_Wxy, + WEIERSTRASS_Wyz, + WEIERSTRASS_Wzx, + /* Low half of multiplication product */ + WEIERSTRASS_Wp, + /* Result (x,y,z) co-ordinates */ + WEIERSTRASS_x3, + WEIERSTRASS_y3, + WEIERSTRASS_z3, + + /* Number of registers */ + WEIERSTRASS_NUM_REGISTERS = 16 +}; + +/** Zero register (for add/subtract operations */ +#define WEIERSTRASS_zero WEIERSTRASS_a + +/** Construct big integer register index */ +#define WEIERSTRASS_REGISTER( name ) _C2 ( WEIERSTRASS_, name ) + +/** Bytecode operation codes */ +enum weierstrass_opcode { + /** Subtract big integers (and add nothing)*/ + WEIERSTRASS_OP_SUB_0N = 0, + /** Subtract big integers (and add 2N) */ + WEIERSTRASS_OP_SUB_2N = WEIERSTRASS_2N, + /** Subtract big integers (and add 4N) */ + WEIERSTRASS_OP_SUB_4N = WEIERSTRASS_4N, + /** Add big integers */ + WEIERSTRASS_OP_ADD, + /** Multiply big integers (and perform Montgomery reduction) */ + WEIERSTRASS_OP_MUL, +}; + +/** + * Define a bytecode operation + * + * @v opcode Operation code + * @v dest Destination big integer register name + * @v left Left source big integer register name + * @v right Right source big integer register name + */ +#define WEIERSTRASS_OP( opcode, dest, left, right ) \ + ( ( (opcode) << 12 ) | \ + ( WEIERSTRASS_REGISTER ( dest ) << 8 ) | \ + ( WEIERSTRASS_REGISTER ( left ) << 4 ) | \ + ( WEIERSTRASS_REGISTER ( right ) << 0 ) ) + +/** Extract bytecode operation code */ +#define WEIERSTRASS_OPCODE( op ) ( ( (op) >> 12 ) & 0xf ) + +/** Extract destination big integer register */ +#define WEIERSTRASS_DEST( op ) ( ( (op) >> 8 ) & 0xf ) + +/** Extract left source big integer register */ +#define WEIERSTRASS_LEFT( op ) ( ( (op) >> 4 ) & 0xf ) + +/** Extract right source big integer register */ +#define WEIERSTRASS_RIGHT( op ) ( ( (op) >> 0 ) & 0xf ) + +/** Define a three-argument addition operation */ +#define WEIERSTRASS_ADD3( dest, augend, addend ) \ + WEIERSTRASS_OP ( WEIERSTRASS_OP_ADD, dest, augend, addend ) + +/** Define a two-argument addition operation */ +#define WEIERSTRASS_ADD2( augend, addend ) \ + WEIERSTRASS_ADD3 ( augend, augend, addend ) + +/** Define a move operation */ +#define WEIERSTRASS_MOV( dest, source ) \ + WEIERSTRASS_ADD3( dest, source, zero ) + +/** Define a three-argument subtraction operation */ +#define WEIERSTRASS_SUB3( dest, minuend, subtrahend, multiple ) \ + WEIERSTRASS_OP ( _C2 ( WEIERSTRASS_OP_SUB_, multiple ), \ + dest, minuend, subtrahend ) + +/** Define a two-argument subtraction operation */ +#define WEIERSTRASS_SUB2( minuend, subtrahend, multiple ) \ + WEIERSTRASS_SUB3 ( minuend, minuend, subtrahend, multiple ) + +/** Define a stop operation */ +#define WEIERSTRASS_STOP WEIERSTRASS_SUB2 ( zero, zero, 0N ) + +/** Define a three-argument multiplication operation */ +#define WEIERSTRASS_MUL3( dest, multiplicand, multiplier ) \ + WEIERSTRASS_OP ( WEIERSTRASS_OP_MUL, dest, multiplicand, multiplier ) + +/** Define a two-argument multiplication operation */ +#define WEIERSTRASS_MUL2( multiplicand, multiplier ) \ + WEIERSTRASS_MUL3 ( multiplicand, multiplicand, multiplier ) + +/** + * Initialise curve + * + * @v curve Weierstrass curve + */ +static void weierstrass_init_curve ( struct weierstrass_curve *curve ) { + unsigned int size = curve->size; + bigint_t ( size ) __attribute__ (( may_alias )) *prime = + ( ( void * ) curve->prime[0] ); + bigint_t ( size ) __attribute__ (( may_alias )) *fermat = + ( ( void * ) curve->fermat ); + bigint_t ( size ) __attribute__ (( may_alias )) *square = + ( ( void * ) curve->square ); + bigint_t ( size ) __attribute__ (( may_alias )) *one = + ( ( void * ) curve->one ); + bigint_t ( size ) __attribute__ (( may_alias )) *a = + ( ( void * ) curve->a ); + bigint_t ( size ) __attribute__ (( may_alias )) *b3 = + ( ( void * ) curve->b3 ); + bigint_t ( size ) __attribute__ (( may_alias )) *mont = + ( ( void * ) curve->mont[0] ); + bigint_t ( size ) __attribute__ (( may_alias )) *temp = + ( ( void * ) curve->prime[1] ); + bigint_t ( size * 2 ) __attribute__ (( may_alias )) *product = + ( ( void * ) temp ); + bigint_t ( size ) __attribute__ (( may_alias )) *two = + ( ( void * ) temp ); + static const uint8_t one_raw[] = { 1 }; + static const uint8_t two_raw[] = { 2 }; + size_t len = curve->len; + unsigned int i; + + /* Initialise field prime */ + bigint_init ( prime, curve->prime_raw, len ); + DBGC ( curve, "WEIERSTRASS %s N = %s\n", + curve->name, bigint_ntoa ( prime ) ); + + /* Calculate Montgomery constant R^2 mod N */ + bigint_reduce ( prime, square ); + DBGC ( curve, "WEIERSTRASS %s R^2 = %s mod N\n", + curve->name, bigint_ntoa ( square ) ); + + /* Calculate constant "3b" */ + bigint_init ( b3, curve->b_raw, len ); + DBGC ( curve, "WEIERSTRASS %s b = %s\n", + curve->name, bigint_ntoa ( b3 ) ); + bigint_copy ( b3, a ); + bigint_add ( b3, b3 ); + bigint_add ( a, b3 ); + + /* Initialise "a" */ + bigint_init ( a, curve->a_raw, len ); + DBGC ( curve, "WEIERSTRASS %s a = %s\n", + curve->name, bigint_ntoa ( a ) ); + + /* Initialise "1" */ + bigint_init ( one, one_raw, sizeof ( one_raw ) ); + + /* Convert relevant constants to Montgomery form + * + * We rely on the fact that the prime multiples have not yet + * been calculated, and so can be used as a temporary buffer. + */ + for ( i = 0 ; i < WEIERSTRASS_NUM_MONT ; i++ ) { + static const char *names[] = { " ", " a", "3b" }; + bigint_multiply ( &mont[i], square, product ); + bigint_montgomery ( prime, product, &mont[i] ); + DBGC ( curve, "WEIERSTRASS %s %sR = %s mod N\n", + curve->name, names[i], bigint_ntoa ( &mont[i] ) ); + } + + /* Calculate constant "N-2" + * + * We rely on the fact that the prime multiples have not yet + * been calculated, and so can be used as a temporary buffer. + */ + bigint_copy ( prime, fermat ); + bigint_init ( two, two_raw, sizeof ( two_raw ) ); + bigint_subtract ( two, fermat ); + DBGC ( curve, "WEIERSTRASS %s N-2 = %s\n", + curve->name, bigint_ntoa ( fermat ) ); + + /* Calculate multiples of field prime */ + for ( i = 1 ; i < WEIERSTRASS_NUM_MULTIPLES ; i++ ) { + bigint_copy ( &prime[ i - 1 ], &prime[i] ); + bigint_add ( &prime[i], &prime[i] ); + DBGC ( curve, "WEIERSTRASS %s %dN = %s\n", + curve->name, ( 1 << i ), bigint_ntoa ( &prime[i] ) ); + } +} + +/** + * Execute bytecode instruction + * + * @v curve Weierstrass curve + * @v regs Registers + * @v size Big integer size + * @v op Operation + */ +static void weierstrass_exec ( const struct weierstrass_curve *curve, + void **regs, unsigned int size, + unsigned int op ) { + const bigint_t ( size ) __attribute__ (( may_alias )) + *prime = ( ( const void * ) curve->prime[0] ); + bigint_t ( size * 2 ) __attribute__ (( may_alias )) + *product = regs[WEIERSTRASS_Wp]; + bigint_t ( size ) __attribute__ (( may_alias )) *dest; + const bigint_t ( size ) __attribute__ (( may_alias )) *left; + const bigint_t ( size ) __attribute__ (( may_alias )) *right; + const bigint_t ( size ) __attribute__ (( may_alias )) *addend; + const bigint_t ( size ) __attribute__ (( may_alias )) *subtrahend; + unsigned int op_code; + unsigned int op_dest; + unsigned int op_left; + unsigned int op_right; + + /* Decode instruction */ + op_code = WEIERSTRASS_OPCODE ( op ); + op_dest = WEIERSTRASS_DEST ( op ); + op_left = WEIERSTRASS_LEFT ( op ); + op_right = WEIERSTRASS_RIGHT ( op ); + dest = regs[op_dest]; + left = regs[op_left]; + right = regs[op_right]; + + /* Check destination is a writable register */ + assert ( op_dest >= WEIERSTRASS_Wt ); + + /* Handle multiplications */ + if ( op_code == WEIERSTRASS_OP_MUL ) { + assert ( op_left != WEIERSTRASS_Wp ); + assert ( op_right != WEIERSTRASS_Wp ); + bigint_multiply ( left, right, product ); + bigint_montgomery_relaxed ( prime, product, dest ); + DBGCP ( curve, "WEIERSTRASS %s R%d := R%d x R%d = %s\n", + curve->name, op_dest, op_left, op_right, + bigint_ntoa ( dest ) ); + return; + } + + /* Copy left source, if required */ + if ( op_dest != op_left ) + bigint_copy ( left, dest ); + + /* Do nothing more if addend/subtrahend is zero */ + if ( ! op_right ) { + DBGCP ( curve, "WEIERSTRASS %s R%d := R%d = %s\n", + curve->name, op_dest, op_left, bigint_ntoa ( dest ) ); + return; + } + + /* Determine addend and subtrahend */ + addend = NULL; + subtrahend = NULL; + if ( op_code == WEIERSTRASS_OP_ADD ) { + DBGCP ( curve, "WEIERSTRASS %s R%d := R%d + R%d = ", + curve->name, op_dest, op_left, op_right ); + addend = ( ( const void * ) right ); + } else { + subtrahend = ( ( const void * ) right ); + if ( op_code > WEIERSTRASS_OP_SUB_0N ) { + DBGCP ( curve, "WEIERSTRASS %s R%d := R%d - R%d + " + "%dN = ", curve->name, op_dest, op_left, + op_right, ( 1 << op_code ) ); + addend = ( ( const void * ) curve->prime[op_code] ); + } else { + DBGCP ( curve, "WEIERSTRASS %s R%d := R%d - R%d = ", + curve->name, op_dest, op_left, op_right ); + } + } + + /* Perform addition and subtraction */ + if ( addend ) + bigint_add ( addend, dest ); + if ( subtrahend ) + bigint_subtract ( subtrahend, dest ); + DBGCP ( curve, "%s\n", bigint_ntoa ( dest ) ); +} + +/** + * Add points on curve + * + * @v curve Weierstrass curve + * @v augend0 Element 0 of point (x1,y1,z1) to be added + * @v addend0 Element 0 of point (x2,y2,z2) to be added + * @v result0 Element 0 of point (x3,y3,z3) to hold result + * + * Points are represented in projective coordinates, with all values + * in Montgomery form and in the range [0,4N) where N is the field + * prime. + * + * The augend may have the same value as the addend (i.e. this routine + * may be used to perform point doubling as well as point addition), + * and either or both may be the point at infinity. + * + * The result may overlap either input, since the inputs are fully + * consumed before the result is written. + */ +static void weierstrass_add_raw ( const struct weierstrass_curve *curve, + const bigint_element_t *augend0, + const bigint_element_t *addend0, + bigint_element_t *result0 ) { + unsigned int size = curve->size; + const bigint_t ( size ) __attribute__ (( may_alias )) + *prime = ( ( const void * ) curve->prime[0] ); + const bigint_t ( size ) __attribute__ (( may_alias )) + *a = ( ( const void * ) curve->a ); + const bigint_t ( size ) __attribute__ (( may_alias )) + *b3 = ( ( const void * ) curve->b3 ); + const weierstrass_t ( size ) __attribute__ (( may_alias )) + *augend = ( ( const void * ) augend0 ); + const weierstrass_t ( size ) __attribute__ (( may_alias )) + *addend = ( ( const void * ) addend0 ); + weierstrass_t ( size ) __attribute__ (( may_alias )) + *result = ( ( void * ) result0 ); + struct { + bigint_t ( size ) Wt; + bigint_t ( size ) Wxy; + bigint_t ( size ) Wyz; + bigint_t ( size ) Wzx; + bigint_t ( size * 2 ) Wp; + } temp; + void *regs[WEIERSTRASS_NUM_REGISTERS]; + unsigned int schedule; + const uint16_t *op; + unsigned int i; + + /* On entry, we assume that x1, x2, y1, y2, z1, z2 are all in + * the range [0,4N). Additions will extend the range. + * Subtractions will extend the range (and require an addition + * of a suitable multiple of the modulus to ensure that the + * result is a positive value). Relaxed Montgomery + * multiplications will reduce the range to [0,2N). The + * outputs x3, y3, z3 will be in the range [0,4N) and + * therefore usable as subsequent inputs. + */ + static const uint16_t ops[] = { + /* [Wxy] Qxy = (x1+y1)*(x2+y2) (mod 2N) */ + WEIERSTRASS_ADD3 ( Wt, x1, y1 ), + WEIERSTRASS_ADD3 ( Wxy, x2, y2 ), + WEIERSTRASS_MUL2 ( Wxy, Wt ), + /* [Wyz] Qyz = (y1+z1)*(y2+z2) (mod 2N) */ + WEIERSTRASS_ADD3 ( Wt, y1, z1 ), + WEIERSTRASS_ADD3 ( Wyz, y2, z2 ), + WEIERSTRASS_MUL2 ( Wyz, Wt ), + /* [Wzx] Qzx = (z1+x1)*(z2+x2) (mod 2N) */ + WEIERSTRASS_ADD3 ( Wt, z1, x1 ), + WEIERSTRASS_ADD3 ( Wzx, z2, x2 ), + WEIERSTRASS_MUL2 ( Wzx, Wt ), + /* [x3] Px = x1*x2 (mod 2N) */ + WEIERSTRASS_MUL3 ( x3, x1, x2 ), + /* [y3] Py = y1*y2 (mod 2N) */ + WEIERSTRASS_MUL3 ( y3, y1, y2 ), + /* [z3] Pz = z1*z2 (mod 2N) */ + WEIERSTRASS_MUL3 ( z3, z1, z2 ), + /* [Wxy] Rxy = Qxy - Px - Py (mod 6N) + * = (x1+y1)*(x2+y2) - x1*x2 - y1*y2 (mod 6N) + * = x1*y2 + x2*y1 (mod 6N) + */ + WEIERSTRASS_SUB2 ( Wxy, x3, 0N ), + WEIERSTRASS_SUB2 ( Wxy, y3, 4N ), + /* [Wyz] Ryz = Qyz - Py - Pz (mod 6N) + * = (y1+z1)*(y2+z2) - y1*y2 - z1*z2 (mod 6N) + * = y1*z2 + y2*z1 (mod 6N) + */ + WEIERSTRASS_SUB2 ( Wyz, y3, 0N ), + WEIERSTRASS_SUB2 ( Wyz, z3, 4N ), + /* [Wzx] Rzx = Qzx - Pz - Px (mod 6N) + * = (z1+x1)*(z2+x2) - z1*z2 - x1*x2 (mod 6N) + * = x1*z2 + x2*z1 (mod 6N) + */ + WEIERSTRASS_SUB2 ( Wzx, z3, 0N ), + WEIERSTRASS_SUB2 ( Wzx, x3, 4N ), + /* [Wt] aRzx = a * Rzx (mod 2N) + * = a * (x1*z2 + x2*z1) (mod 2N) + */ + WEIERSTRASS_MUL3 ( Wt, a, Wzx ), + /* [Wp] 3bPz = 3b * Pz (mod 2N) + * = 3b*z1*z2 (mod 2N) + */ + WEIERSTRASS_MUL3 ( Wp, 3b, z3 ), + /* [Wp] Sy = aRzx + 3bPz (mod 4N) + * = a*(x1*z2 + x2*z1) + 3b*z1*z2 (mod 4N) + */ + WEIERSTRASS_ADD2 ( Wp, Wt ), + /* [Wt] Syz = Py + Sy (mod 6N) + * = y1*y2 + a*(x1*z2 + x2*z1) + 3b*z1*z2 (mod 6N) + */ + WEIERSTRASS_ADD3 ( Wt, y3, Wp ), + /* [y3] Sxy = Py - Sy (mod 6N) + * = y1*y2 - a*(x1*z2 + x2*z1) - 3b*z1*z2 (mod 6N) + */ + WEIERSTRASS_SUB2 ( y3, Wp, 4N ), + /* [z3] aPz = a * Pz (mod 2N) + * = a * z1*z2 (mod 2N) + */ + WEIERSTRASS_MUL2 ( z3, a ), + /* [Wzx] 3bRzx = 3b * Rzx (mod 2N) + * = 3b * (x1*z2 + x2*z1) (mod 2N) + */ + WEIERSTRASS_MUL2 ( Wzx, 3b ), + /* [x3] aPzx' = Px - aPz (mod 4N) + * = x1*x2 - a*z1*z2 (mod 4N) + */ + WEIERSTRASS_SUB2 ( x3, z3, 2N ), + /* [Wp] Szx = a * aPzx' (mod 2N) + * = a * (x1*x2 - a*z1*z2) (mod 2N) + * = a*x1*x2 - (a^2)*z1*z2 (mod 2N) + */ + WEIERSTRASS_MUL3 ( Wp, a, x3 ), + /* [x3] Px = aPzx' + aPz (mod 6N) + * = x1*x2 - a*z1*z2 + a*z1*z2 (mod 6N) + * = x1*x2 (mod 6N) + */ + WEIERSTRASS_ADD2 ( x3, z3 ), + /* [Wzx] Tzx = 3bRzx + Szx (mod 4N) + * = a*x1*x2 + 3b*(x1*z2 + x2*z1) - + * (a^2)*z1*z2 (mod 4N) + */ + WEIERSTRASS_ADD2 ( Wzx, Wp ), + /* [z3] aPzx = Px + aPz (mod 8N) + * = x1*x2 + a*z1*z2 (mod 8N) + */ + WEIERSTRASS_ADD2 ( z3, x3 ), + /* [x3] 2Px = Px + Px (mod 12N) + * = 2*x1*x2 (mod 12N) + */ + WEIERSTRASS_ADD2 ( x3, x3 ), + /* [x3] Tyz = 2Px + aPzx (mod 20N) + * = 2*x1*x2 + x1*x2 + a*z1*z2 (mod 20N) + * = 3*x1*x2 + a*z1*z2 (mod 20N) + */ + WEIERSTRASS_ADD2 ( x3, z3 ), + /* [z3] Syz = Syz (mod 6N) + * = y1*y2 + a*(x1*z2 + x2*z1) + 3b*z1*z2 (mod 6N) + */ + WEIERSTRASS_MOV ( z3, Wt ), + /* [Wt] Tyz = Tyz (mod 20N) + * = 3*x1*x2 + a*z1*z2 (mod 20N) + */ + WEIERSTRASS_MOV ( Wt, x3 ), + /* [x3] Ux = Rxy * Sxy (mod 2N) + * = (x1*y2 + x2*y1) * + * (y1*y2 - a*(x1*z2 + x2*z1) - 3b*z1*z2) (mod 2N) + */ + WEIERSTRASS_MUL3 ( x3, Wxy, y3 ), + /* [y3] Uy = Syz * Sxy (mod 2N) + * = (y1*y2 + a*(x1*z2 + x2*z1) + 3b*z1*z2) * + * (y1*y2 - a*(x1*z2 + x2*z1) - 3b*z1*z2) (mod 2N) + */ + WEIERSTRASS_MUL2 ( y3, z3 ), + /* [z3] Uz = Ryz * Syz (mod 2N) + * = (y1*z2 + y2*z1) * + * (y1*y2 + a*(x1*z2 + x2*z1) + 3b*z1*z2) (mod 2N) + */ + WEIERSTRASS_MUL2 ( z3, Wyz ), + /* [Wp] Vx = Ryz * Tzx (mod 2N) + * = (y1*z2 + y2*z1) * + * (a*x1*x2 + 3b*(x1*z2 + x2*z1) - (a^2)*z1*z2) + * (mod 2N) + */ + WEIERSTRASS_MUL3 ( Wp, Wyz, Wzx ), + /* [x3] x3 = Ux - Vx (mod 4N) + * = ((x1*y2 + x2*y1) * + * (y1*y2 - a*(x1*z2 + x2*z1) - 3b*z1*z2)) - + * ((y1*z2 + y2*z1) * + * (a*x1*x2 + 3b*(x1*z2 + x2*z1) - (a^2)*z1*z2)) + * (mod 4N) + */ + WEIERSTRASS_SUB2 ( x3, Wp, 2N ), + /* [Wp] Vy = Tyz * Tzx (mod 2N) + * = (3*x1*x2 + a*z1*z2) * + * (a*x1*x2 + 3b*(x1*z2 + x2*z1) - (a^2)*z1*z2) + * (mod 2N) + */ + WEIERSTRASS_MUL3 ( Wp, Wt, Wzx ), + /* [y3] y3 = Vy + Uy (mod 4N) + * = ((3*x1*x2 + a*z1*z2) * + * (a*x1*x2 + 3b*(x1*z2 + x2*z1) - (a^2)*z1*z2)) + + * ((y1*y2 + a*(x1*z2 + x2*z1) + 3b*z1*z2) * + * (y1*y2 - a*(x1*z2 + x2*z1) - 3b*z1*z2)) + * (mod 4N) + */ + WEIERSTRASS_ADD2 ( y3, Wp ), + /* [Wp] Vz = Rxy * Tyz (mod 2N) + * = (x1*y2 + x2*y1) * (3*x1*x2 + a*z1*z2) (mod 2N) + */ + WEIERSTRASS_MUL3 ( Wp, Wxy, Wt ), + /* [z3] z3 = Uz + Vz (mod 4N) + * = ((y1*z2 + y2*z1) * + * (y1*y2 + a*(x1*z2 + x2*z1) + 3b*z1*z2)) + + * ((x1*y2 + x2*y1) * (3*x1*x2 + a*z1*z2)) + * (mod 4N) + */ + WEIERSTRASS_ADD2 ( z3, Wp ), + /* Stop */ + WEIERSTRASS_STOP + }; + + /* Initialise register list */ + regs[WEIERSTRASS_a] = ( ( void * ) a ); + regs[WEIERSTRASS_3b] = ( ( void * ) b3 ); + regs[WEIERSTRASS_x1] = ( ( void * ) &augend->x ); + regs[WEIERSTRASS_x2] = ( ( void * ) &addend->x ); + regs[WEIERSTRASS_x3] = ( ( void * ) &result->x ); + regs[WEIERSTRASS_Wt] = &temp; + schedule = ( ( ( 1 << WEIERSTRASS_NUM_REGISTERS ) - 1 ) + - ( 1 << WEIERSTRASS_a ) + - ( 1 << WEIERSTRASS_3b ) + - ( 1 << WEIERSTRASS_x1 ) + - ( 1 << WEIERSTRASS_x2 ) + - ( 1 << WEIERSTRASS_x3 ) + - ( 1 << WEIERSTRASS_Wt ) ); + for ( i = 0 ; schedule ; i++, schedule >>= 1 ) { + if ( schedule & 1 ) + regs[i] = ( regs[ i - 1 ] + sizeof ( *prime ) ); + } + DBGC2 ( curve, "WEIERSTRASS %s augend (%s,", + curve->name, bigint_ntoa ( &augend->x ) ); + DBGC2 ( curve, "%s,", bigint_ntoa ( &augend->y ) ); + DBGC2 ( curve, "%s)\n", bigint_ntoa ( &augend->z ) ); + DBGC2 ( curve, "WEIERSTRASS %s addend (%s,", + curve->name, bigint_ntoa ( &addend->x ) ); + DBGC2 ( curve, "%s,", bigint_ntoa ( &addend->y ) ); + DBGC2 ( curve, "%s)\n", bigint_ntoa ( &addend->z ) ); + + /* Sanity checks */ + assert ( regs[WEIERSTRASS_a] == a ); + assert ( regs[WEIERSTRASS_3b] == b3 ); + assert ( regs[WEIERSTRASS_x1] == &augend->x ); + assert ( regs[WEIERSTRASS_y1] == &augend->y ); + assert ( regs[WEIERSTRASS_z1] == &augend->z ); + assert ( regs[WEIERSTRASS_x2] == &addend->x ); + assert ( regs[WEIERSTRASS_y2] == &addend->y ); + assert ( regs[WEIERSTRASS_z2] == &addend->z ); + assert ( regs[WEIERSTRASS_x3] == &result->x ); + assert ( regs[WEIERSTRASS_y3] == &result->y ); + assert ( regs[WEIERSTRASS_z3] == &result->z ); + assert ( regs[WEIERSTRASS_Wt] == &temp.Wt ); + assert ( regs[WEIERSTRASS_Wxy] == &temp.Wxy ); + assert ( regs[WEIERSTRASS_Wyz] == &temp.Wyz ); + assert ( regs[WEIERSTRASS_Wzx] == &temp.Wzx ); + assert ( regs[WEIERSTRASS_Wp] == &temp.Wp ); + + /* Execute bytecode instruction sequence */ + for ( op = ops ; *op != WEIERSTRASS_STOP ; op++ ) + weierstrass_exec ( curve, regs, size, *op ); + DBGC2 ( curve, "WEIERSTRASS %s result (%s,", + curve->name, bigint_ntoa ( &result->x ) ); + DBGC2 ( curve, "%s,", bigint_ntoa ( &result->y ) ); + DBGC2 ( curve, "%s)\n", bigint_ntoa ( &result->z ) ); +} + +/** + * Add points on curve + * + * @v curve Weierstrass curve + * @v augend Point (x1,y1,z1) to be added + * @v addend Point (x2,y2,z2) to be added + * @v result0 Point (x3,y3,z3) to hold result + */ +#define weierstrass_add( curve, augend, addend, result ) do { \ + weierstrass_add_raw ( (curve), (augend)->all.element, \ + (addend)->all.element, \ + (result)->all.element ); \ + } while ( 0 ) + +/** + * Add points on curve as part of a Montgomery ladder + * + * @v operand Element 0 of first input operand (may overlap result) + * @v result Element 0 of second input operand and result + * @v size Number of elements in operands and result + * @v ctx Operation context + * @v tmp Temporary working space (not used) + */ +static void weierstrass_add_ladder ( const bigint_element_t *operand0, + bigint_element_t *result0, + unsigned int size, const void *ctx, + void *tmp __unused ) { + const struct weierstrass_curve *curve = ctx; + const weierstrass_t ( curve->size ) __attribute__ (( may_alias )) + *operand = ( ( const void * ) operand0 ); + weierstrass_t ( curve->size ) __attribute__ (( may_alias )) + *result = ( ( void * ) result0 ); + + /* Add curve points */ + assert ( size == bigint_size ( &operand->all ) ); + assert ( size == bigint_size ( &result->all ) ); + weierstrass_add ( curve, operand, result, result ); +} + +/** + * Verify freshly initialised point is on curve + * + * @v curve Weierstrass curve + * @v point0 Element 0 of point (x,y,z) to be verified + * @ret rc Return status code + * + * As with point addition, points are represented in projective + * coordinates, with all values in Montgomery form and in the range + * [0,4N) where N is the field prime. + * + * This verification logic is valid only for points that have been + * freshly constructed via weierstrass_init() (i.e. must either have + * z=1 or be the point at infinity (0,1,0)). + */ +static int weierstrass_verify_raw ( const struct weierstrass_curve *curve, + const bigint_element_t *point0 ) { + unsigned int size = curve->size; + const bigint_t ( size ) __attribute__ (( may_alias )) + *prime = ( ( const void * ) curve->prime[0] ); + const bigint_t ( size ) __attribute__ (( may_alias )) + *a = ( ( const void * ) curve->a ); + const bigint_t ( size ) __attribute__ (( may_alias )) + *b3 = ( ( const void * ) curve->b3 ); + const weierstrass_t ( size ) __attribute__ (( may_alias )) + *point = ( ( const void * ) point0 ); + struct { + bigint_t ( size ) Wt; + bigint_t ( size * 2 ) Wp; + } temp; + void *regs[WEIERSTRASS_NUM_REGISTERS]; + const uint16_t *op; + + /* Calculate 3*(x^3 + a*x + b - y^2) */ + static const uint16_t ops[] = { + /* [Wt] Tx = x^2 (mod 2N) */ + WEIERSTRASS_MUL3 ( Wt, x1, x1 ), + /* [Wt] Txa = Tx + a (mod 3N) + * = x^2 + a (mod 3N) + */ + WEIERSTRASS_MOV ( Wp, a ), + WEIERSTRASS_ADD2 ( Wt, Wp ), + /* [Wt] Txax = Txa * x (mod 2N) + * = (x^2 + a)*x (mod 2N) + * = x^3 + a*x (mod 2N) + */ + WEIERSTRASS_MUL2 ( Wt, x1 ), + /* [Wp] Ty = y^2 (mod 2N) */ + WEIERSTRASS_MUL3 ( Wp, y1, y1 ), + /* [Wt] Txaxy = Txax - Ty (mod 4N) + * = x^3 + a*x - y^2 (mod 4N) + */ + WEIERSTRASS_SUB2 ( Wt, Wp, 2N ), + /* [Wp] 2Txaxy = Txaxy + Txaxy (mod 8N) + * = 2*(x^3 + a*x - y^2) (mod 8N) + */ + WEIERSTRASS_ADD3 ( Wp, Wt, Wt ), + /* [Wt] 3Txaxy = 2Txaxy + Txaxy (mod 12N) + * = 3*(x^3 + a*x - y^2) (mod 12N) + */ + WEIERSTRASS_ADD2 ( Wt, Wp ), + /* [Wt] 3Txaxyb = 3Txaxy + 3b (mod 13N) + * = 3*(x^3 + a*x - y^2) + 3b (mod 13N) + * = 3*(x^3 + a*x + b - y^2) (mod 13N) + */ + WEIERSTRASS_ADD2 ( Wt, 3b ), + /* [Wt] check = 3Txaxyb * z (mod 2N) + * = 3*(x^3 + a*x + b - y^2) * z (mod 2N) + */ + WEIERSTRASS_MUL2 ( Wt, z1 ), + /* Stop */ + WEIERSTRASS_STOP + }; + + /* Initialise register list */ + regs[WEIERSTRASS_a] = ( ( void * ) a ); + regs[WEIERSTRASS_3b] = ( ( void * ) b3 ); + regs[WEIERSTRASS_x1] = ( ( void * ) &point->x ); + regs[WEIERSTRASS_y1] = ( ( void * ) &point->y ); + regs[WEIERSTRASS_z1] = ( ( void * ) &point->z ); + regs[WEIERSTRASS_Wt] = &temp.Wt; + regs[WEIERSTRASS_Wp] = &temp.Wp; + + /* Execute bytecode instruction sequence */ + for ( op = ops ; *op != WEIERSTRASS_STOP ; op++ ) + weierstrass_exec ( curve, regs, size, *op ); + + /* Check that result is zero (modulo the field prime) */ + bigint_grow ( &temp.Wt, &temp.Wp ); + bigint_montgomery ( prime, &temp.Wp, &temp.Wt ); + if ( ! bigint_is_zero ( &temp.Wt ) ) { + DBGC ( curve, "WEIERSTRASS %s base point is not on curve\n", + curve->name ); + return -EINVAL; + } + + return 0; +} + +/** + * Verify freshly initialised point is on curve + * + * @v curve Weierstrass curve + * @v point Point (x,y,z) to be verified + * @ret rc Return status code + */ +#define weierstrass_verify( curve, point ) ( { \ + weierstrass_verify_raw ( (curve), (point)->all.element ); \ + } ) + +/** + * Initialise curve point + * + * @v curve Weierstrass curve + * @v point0 Element 0 of point (x,y,z) to be filled in + * @v temp0 Element 0 of temporary point buffer + * @v data Raw curve point + * @ret rc Return status code + */ +static int weierstrass_init_raw ( struct weierstrass_curve *curve, + bigint_element_t *point0, + bigint_element_t *temp0, const void *data ) { + unsigned int size = curve->size; + size_t len = curve->len; + const bigint_t ( size ) __attribute__ (( may_alias )) *prime = + ( ( const void * ) curve->prime[0] ); + const bigint_t ( size ) __attribute__ (( may_alias )) *prime2 = + ( ( const void * ) curve->prime[WEIERSTRASS_2N] ); + const bigint_t ( size ) __attribute__ (( may_alias )) *square = + ( ( const void * ) curve->square ); + const bigint_t ( size ) __attribute__ (( may_alias )) *one = + ( ( const void * ) curve->one ); + weierstrass_t ( size ) __attribute__ (( may_alias )) + *point = ( ( void * ) point0 ); + union { + bigint_t ( size * 2 ) product; + weierstrass_t ( size ) point; + } __attribute__ (( may_alias )) *temp = ( ( void * ) temp0 ); + size_t offset; + int is_infinite; + unsigned int i; + int rc; + + /* Initialise curve, if not already done + * + * The least significant element of the field prime must be + * odd, and so the least significant element of the + * (initialised) first multiple of the field prime must be + * non-zero. + */ + if ( ! prime2->element[0] ) + weierstrass_init_curve ( curve ); + + /* Convert input to projective coordinates in Montgomery form */ + DBGC ( curve, "WEIERSTRASS %s point (", curve->name ); + for ( i = 0, offset = 0 ; i < WEIERSTRASS_AXES ; i++, offset += len ) { + bigint_init ( &point->axis[i], ( data + offset ), len ); + DBGC ( curve, "%s%s", ( i ? "," : "" ), + bigint_ntoa ( &point->axis[i] ) ); + bigint_multiply ( &point->axis[i], square, &temp->product ); + bigint_montgomery_relaxed ( prime, &temp->product, + &point->axis[i] ); + } + memset ( &point->z, 0, sizeof ( point->z ) ); + is_infinite = bigint_is_zero ( &point->xy ); + bigint_copy ( one, &point->axis[ is_infinite ? 1 : 2 ] ); + DBGC ( curve, ")\n" ); + + /* Verify point is on curve */ + if ( ( rc = weierstrass_verify ( curve, point ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Initialise curve point + * + * @v curve Weierstrass curve + * @v point Point (x,y,z) to be filled in + * @v temp Temporary point buffer + * @v data Raw curve point + * @ret rc Return status code + */ +#define weierstrass_init( curve, point, temp, data ) ( { \ + weierstrass_init_raw ( (curve), (point)->all.element, \ + (temp)->all.element, (data) ); \ + } ) + +/** + * Finalise curve point + * + * @v curve Weierstrass curve + * @v point0 Element 0 of point (x,y,z) + * @v temp0 Element 0 of temporary point buffer + * @v out Output buffer + */ +static void weierstrass_done_raw ( struct weierstrass_curve *curve, + bigint_element_t *point0, + bigint_element_t *temp0, void *out ) { + unsigned int size = curve->size; + size_t len = curve->len; + const bigint_t ( size ) __attribute__ (( may_alias )) *prime = + ( ( const void * ) curve->prime[0] ); + const bigint_t ( size ) __attribute__ (( may_alias )) *fermat = + ( ( const void * ) curve->fermat ); + const bigint_t ( size ) __attribute__ (( may_alias )) *one = + ( ( const void * ) curve->one ); + weierstrass_t ( size ) __attribute__ (( may_alias )) + *point = ( ( void * ) point0 ); + union { + bigint_t ( size * 2 ) product; + weierstrass_t ( size ) point; + } __attribute__ (( may_alias )) *temp = ( ( void * ) temp0 ); + size_t offset; + unsigned int i; + + /* Invert result Z co-ordinate (via Fermat's little theorem) */ + bigint_copy ( one, &temp->point.z ); + bigint_ladder ( &temp->point.z, &point->z, fermat, + bigint_mod_exp_ladder, prime, &temp->product ); + + /* Convert result back to affine co-ordinates */ + DBGC ( curve, "WEIERSTRASS %s result (", curve->name ); + for ( i = 0, offset = 0 ; i < WEIERSTRASS_AXES ; i++, offset += len ) { + bigint_multiply ( &point->axis[i], &temp->point.z, + &temp->product ); + bigint_montgomery_relaxed ( prime, &temp->product, + &point->axis[i] ); + bigint_grow ( &point->axis[i], &temp->product ); + bigint_montgomery ( prime, &temp->product, &point->axis[i] ); + DBGC ( curve, "%s%s", ( i ? "," : "" ), + bigint_ntoa ( &point->axis[i] ) ); + bigint_done ( &point->axis[i], ( out + offset ), len ); + } + DBGC ( curve, ")\n" ); +} + +/** + * Finalise curve point + * + * @v curve Weierstrass curve + * @v point Point (x,y,z) + * @v temp Temporary point buffer + * @v out Output buffer + * @ret rc Return status code + */ +#define weierstrass_done( curve, point, temp, out ) ( { \ + weierstrass_done_raw ( (curve), (point)->all.element, \ + (temp)->all.element, (out) ); \ + } ) + +/** + * Check if this is the point at infinity + * + * @v point Curve point + * @ret is_infinity This is the point at infinity + */ +int weierstrass_is_infinity ( struct weierstrass_curve *curve, + const void *point ) { + unsigned int size = curve->size; + size_t len = curve->len; + struct { + bigint_t ( size ) axis; + } temp; + size_t offset; + int is_finite = 0; + unsigned int i; + + /* We use all zeroes to represent the point at infinity */ + DBGC ( curve, "WEIERSTRASS %s point (", curve->name ); + for ( i = 0, offset = 0 ; i < WEIERSTRASS_AXES ; i++, offset += len ) { + bigint_init ( &temp.axis, ( point + offset ), len ); + DBGC ( curve, "%s%s", ( i ? "," : "" ), + bigint_ntoa ( &temp.axis ) ); + is_finite |= ( ! bigint_is_zero ( &temp.axis ) ); + } + DBGC ( curve, ") is%s infinity\n", ( is_finite ? " not" : "" ) ); + + return ( ! is_finite ); +} + +/** + * Multiply curve point by scalar + * + * @v curve Weierstrass curve + * @v base Base point + * @v scalar Scalar multiple + * @v result Result point to fill in + * @ret rc Return status code + */ +int weierstrass_multiply ( struct weierstrass_curve *curve, const void *base, + const void *scalar, void *result ) { + unsigned int size = curve->size; + size_t len = curve->len; + const bigint_t ( size ) __attribute__ (( may_alias )) *one = + ( ( const void * ) curve->one ); + struct { + weierstrass_t ( size ) result; + weierstrass_t ( size ) multiple; + bigint_t ( bigint_required_size ( len ) ) scalar; + } temp; + int rc; + + /* Convert input to projective coordinates in Montgomery form */ + if ( ( rc = weierstrass_init ( curve, &temp.multiple, &temp.result, + base ) ) != 0 ) { + return rc; + } + + /* Construct identity element (the point at infinity) */ + memset ( &temp.result, 0, sizeof ( temp.result ) ); + bigint_copy ( one, &temp.result.y ); + + /* Initialise scalar */ + bigint_init ( &temp.scalar, scalar, len ); + DBGC ( curve, "WEIERSTRASS %s scalar %s\n", + curve->name, bigint_ntoa ( &temp.scalar ) ); + + /* Perform multiplication via Montgomery ladder */ + bigint_ladder ( &temp.result.all, &temp.multiple.all, &temp.scalar, + weierstrass_add_ladder, curve, NULL ); + + /* Convert result back to affine co-ordinates */ + weierstrass_done ( curve, &temp.result, &temp.multiple, result ); + + return 0; +} + +/** + * Add curve points (as a one-off operation) + * + * @v curve Weierstrass curve + * @v addend Curve point to add + * @v augend Curve point to add + * @v result Curve point to hold result + * @ret rc Return status code + */ +int weierstrass_add_once ( struct weierstrass_curve *curve, + const void *addend, const void *augend, + void *result ) { + unsigned int size = curve->size; + struct { + weierstrass_t ( size ) addend; + weierstrass_t ( size ) augend; + weierstrass_t ( size ) result; + } temp; + int rc; + + /* Convert inputs to projective coordinates in Montgomery form */ + if ( ( rc = weierstrass_init ( curve, &temp.addend, &temp.result, + addend ) ) != 0 ) { + return rc; + } + if ( ( rc = weierstrass_init ( curve, &temp.augend, &temp.result, + augend ) ) != 0 ) { + return rc; + } + + /* Add curve points */ + weierstrass_add ( curve, &temp.augend, &temp.addend, &temp.result ); + + /* Convert result back to affine co-ordinates */ + weierstrass_done ( curve, &temp.result, &temp.addend, result ); + + return 0; +} diff --git a/src/crypto/x25519.c b/src/crypto/x25519.c index d58f7168c..95c42ea13 100644 --- a/src/crypto/x25519.c +++ b/src/crypto/x25519.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** @file * @@ -334,6 +335,7 @@ static void x25519_init_constants ( void ) { /** Initialisation function */ struct init_fn x25519_init_fn __init_fn ( INIT_NORMAL ) = { + .name = "x25519", .initialise = x25519_init_constants, }; @@ -563,8 +565,8 @@ void x25519_invert ( const union x25519_oct258 *invertend, * @v value Big integer to be subtracted from, if possible */ static void x25519_reduce_by ( const x25519_t *subtrahend, x25519_t *value ) { - unsigned int max_bit = ( ( 8 * sizeof ( *value ) ) - 1 ); x25519_t tmp; + int underflow; /* Conditionally subtract subtrahend * @@ -572,8 +574,8 @@ static void x25519_reduce_by ( const x25519_t *subtrahend, x25519_t *value ) { * time) if the subtraction underflows. */ bigint_copy ( value, &tmp ); - bigint_subtract ( subtrahend, value ); - bigint_swap ( value, &tmp, bigint_bit_is_set ( value, max_bit ) ); + underflow = bigint_subtract ( subtrahend, value ); + bigint_swap ( value, &tmp, underflow ); } /** @@ -783,16 +785,29 @@ static void x25519_reverse ( struct x25519_value *value ) { } /** + * Check if X25519 value is zero + * + * @v value Value to check + * @ret is_zero Value is zero + */ +int x25519_is_zero ( const struct x25519_value *value ) { + x25519_t point; + + /* Check if value is zero */ + bigint_init ( &point, value->raw, sizeof ( value->raw ) ); + return bigint_is_zero ( &point ); +} + +/** * Calculate X25519 key * * @v base Base point * @v scalar Scalar multiple * @v result Point to hold result (may overlap base point) - * @ret rc Return status code */ -int x25519_key ( const struct x25519_value *base, - const struct x25519_value *scalar, - struct x25519_value *result ) { +void x25519_key ( const struct x25519_value *base, + const struct x25519_value *scalar, + struct x25519_value *result ) { struct x25519_value *tmp = result; union x25519_quad257 point; @@ -813,15 +828,24 @@ int x25519_key ( const struct x25519_value *base, /* Reverse result */ bigint_done ( &point.value, result->raw, sizeof ( result->raw ) ); x25519_reverse ( result ); +} + +/** + * Check if this is the point at infinity + * + * @v point Curve point + * @ret is_infinity This is the point at infinity + */ +static int x25519_curve_is_infinity ( const void *point ) { - /* Fail if result was all zeros (as required by RFC8422) */ - return ( bigint_is_zero ( &point.value ) ? -EPERM : 0 ); + /* We use all zeroes for the point at infinity (as per RFC8422) */ + return x25519_is_zero ( point ); } /** * Multiply scalar by curve point * - * @v base Base point (or NULL to use generator) + * @v base Base point * @v scalar Scalar multiple * @v result Result point to fill in * @ret rc Return status code @@ -829,16 +853,32 @@ int x25519_key ( const struct x25519_value *base, static int x25519_curve_multiply ( const void *base, const void *scalar, void *result ) { - /* Use base point if applicable */ - if ( ! base ) - base = &x25519_generator; + x25519_key ( base, scalar, result ); + return 0; +} + +/** + * Add curve points (as a one-off operation) + * + * @v addend Curve point to add + * @v augend Curve point to add + * @v result Curve point to hold result + * @ret rc Return status code + */ +static int x25519_curve_add ( const void *addend __unused, + const void *augend __unused, + void *result __unused ) { - return x25519_key ( base, scalar, result ); + return -ENOTTY; } /** X25519 elliptic curve */ struct elliptic_curve x25519_curve = { .name = "x25519", + .pointsize = sizeof ( struct x25519_value ), .keysize = sizeof ( struct x25519_value ), + .base = x25519_generator.raw, + .is_infinity = x25519_curve_is_infinity, .multiply = x25519_curve_multiply, + .add = x25519_curve_add, }; diff --git a/src/crypto/x509.c b/src/crypto/x509.c index 92318093e..6a3fe423b 100644 --- a/src/crypto/x509.c +++ b/src/crypto/x509.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <stdlib.h> #include <string.h> @@ -38,6 +39,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/rsa.h> #include <ipxe/rootcert.h> #include <ipxe/certstore.h> +#include <ipxe/privkey.h> #include <ipxe/socket.h> #include <ipxe/in.h> #include <ipxe/image.h> @@ -231,7 +233,7 @@ static int x509_parse_serial ( struct x509_certificate *cert, cert, strerror ( rc ) ); return rc; } - DBGC2 ( cert, "X509 %p issuer is:\n", cert ); + DBGC2 ( cert, "X509 %p serial is:\n", cert ); DBGC2_HDA ( cert, 0, serial->raw.data, serial->raw.len ); return 0; @@ -391,7 +393,7 @@ static int x509_parse_public_key ( struct x509_certificate *cert, const struct asn1_cursor *raw ) { struct x509_public_key *public_key = &cert->subject.public_key; struct asn1_algorithm **algorithm = &public_key->algorithm; - struct asn1_bit_string *raw_bits = &public_key->raw_bits; + struct asn1_cursor *value = &public_key->value; struct asn1_cursor cursor; int rc; @@ -415,8 +417,9 @@ static int x509_parse_public_key ( struct x509_certificate *cert, cert, (*algorithm)->name ); asn1_skip_any ( &cursor ); - /* Parse bit string */ - if ( ( rc = asn1_bit_string ( &cursor, raw_bits ) ) != 0 ) { + /* Parse subjectPublicKey */ + memcpy ( value, &cursor, sizeof ( *value ) ); + if ( ( rc = asn1_enter_bits ( value, NULL ) ) != 0 ) { DBGC ( cert, "X509 %p could not parse public key bits: %s\n", cert, strerror ( rc ) ); return rc; @@ -497,8 +500,9 @@ static int x509_parse_basic_constraints ( struct x509_certificate *cert, static int x509_parse_key_usage ( struct x509_certificate *cert, const struct asn1_cursor *raw ) { struct x509_key_usage *usage = &cert->extensions.usage; - struct asn1_bit_string bit_string; + struct asn1_cursor cursor; const uint8_t *bytes; + unsigned int unused; size_t len; unsigned int i; int rc; @@ -506,16 +510,17 @@ static int x509_parse_key_usage ( struct x509_certificate *cert, /* Mark extension as present */ usage->present = 1; - /* Parse bit string */ - if ( ( rc = asn1_bit_string ( raw, &bit_string ) ) != 0 ) { + /* Enter bit string */ + memcpy ( &cursor, raw, sizeof ( cursor ) ); + if ( ( rc = asn1_enter_bits ( &cursor, &unused ) ) != 0 ) { DBGC ( cert, "X509 %p could not parse key usage: %s\n", cert, strerror ( rc ) ); return rc; } /* Parse key usage bits */ - bytes = bit_string.data; - len = bit_string.len; + bytes = cursor.data; + len = cursor.len; if ( len > sizeof ( usage->bits ) ) len = sizeof ( usage->bits ); for ( i = 0 ; i < len ; i++ ) { @@ -1004,7 +1009,7 @@ int x509_parse ( struct x509_certificate *cert, const struct asn1_cursor *raw ) { struct x509_signature *signature = &cert->signature; struct asn1_algorithm **signature_algorithm = &signature->algorithm; - struct asn1_bit_string *signature_value = &signature->value; + struct asn1_cursor *signature_value = &signature->value; struct asn1_cursor cursor; int rc; @@ -1032,8 +1037,8 @@ int x509_parse ( struct x509_certificate *cert, asn1_skip_any ( &cursor ); /* Parse signatureValue */ - if ( ( rc = asn1_integral_bit_string ( &cursor, - signature_value ) ) != 0 ) { + memcpy ( signature_value, &cursor, sizeof ( *signature_value ) ); + if ( ( rc = asn1_enter_bits ( signature_value, NULL ) ) != 0 ) { DBGC ( cert, "X509 %p could not parse signature value: %s\n", cert, strerror ( rc ) ); return rc; @@ -1078,7 +1083,7 @@ int x509_certificate ( const void *data, size_t len, asn1_shrink_any ( &cursor ); /* Return stored certificate, if present */ - if ( ( *cert = certstore_find ( &cursor ) ) != NULL ) { + if ( ( *cert = x509_find ( NULL, &cursor ) ) != NULL ) { /* Add caller's reference */ x509_get ( *cert ); @@ -1124,7 +1129,6 @@ static int x509_check_signature ( struct x509_certificate *cert, struct pubkey_algorithm *pubkey = algorithm->pubkey; uint8_t digest_ctx[ digest->ctxsize ]; uint8_t digest_out[ digest->digestsize ]; - uint8_t pubkey_ctx[ pubkey->ctxsize ]; int rc; /* Sanity check */ @@ -1148,15 +1152,8 @@ static int x509_check_signature ( struct x509_certificate *cert, } /* Verify signature using signer's public key */ - if ( ( rc = pubkey_init ( pubkey, pubkey_ctx, public_key->raw.data, - public_key->raw.len ) ) != 0 ) { - DBGC ( cert, "X509 %p \"%s\" cannot initialise public key: " - "%s\n", cert, x509_name ( cert ), strerror ( rc ) ); - goto err_pubkey_init; - } - if ( ( rc = pubkey_verify ( pubkey, pubkey_ctx, digest, digest_out, - signature->value.data, - signature->value.len ) ) != 0 ) { + if ( ( rc = pubkey_verify ( pubkey, &public_key->raw, digest, + digest_out, &signature->value ) ) != 0 ) { DBGC ( cert, "X509 %p \"%s\" signature verification failed: " "%s\n", cert, x509_name ( cert ), strerror ( rc ) ); goto err_pubkey_verify; @@ -1166,8 +1163,6 @@ static int x509_check_signature ( struct x509_certificate *cert, rc = 0; err_pubkey_verify: - pubkey_final ( pubkey, pubkey_ctx ); - err_pubkey_init: err_mismatch: return rc; } @@ -1331,9 +1326,9 @@ int x509_is_valid ( struct x509_certificate *cert, struct x509_root *root ) { * @v issuer Issuing X.509 certificate (or NULL) * @v root Root certificate list */ -static void x509_set_valid ( struct x509_certificate *cert, - struct x509_certificate *issuer, - struct x509_root *root ) { +void x509_set_valid ( struct x509_certificate *cert, + struct x509_certificate *issuer, + struct x509_root *root ) { unsigned int max_path_remaining; /* Sanity checks */ @@ -1642,11 +1637,17 @@ struct x509_chain * x509_alloc_chain ( void ) { */ int x509_append ( struct x509_chain *chain, struct x509_certificate *cert ) { struct x509_link *link; + int rc; + + /* Ensure allocation of link cannot invalidate certificate */ + x509_get ( cert ); /* Allocate link */ link = zalloc ( sizeof ( *link ) ); - if ( ! link ) - return -ENOMEM; + if ( ! link ) { + rc = -ENOMEM; + goto err_alloc; + } /* Add link to chain */ link->cert = x509_get ( cert ); @@ -1654,7 +1655,12 @@ int x509_append ( struct x509_chain *chain, struct x509_certificate *cert ) { DBGC ( chain, "X509 chain %p added X509 %p \"%s\"\n", chain, cert, x509_name ( cert ) ); - return 0; + /* Success */ + rc = 0; + + x509_put ( cert ); + err_alloc: + return rc; } /** @@ -1711,25 +1717,139 @@ void x509_truncate ( struct x509_chain *chain, struct x509_link *link ) { } /** + * Mark X.509 certificate as found + * + * @v store Certificate store + * @v cert X.509 certificate + * @ret cert X.509 certificate + */ +static struct x509_certificate * x509_found ( struct x509_chain *store, + struct x509_certificate *cert ) { + + /* Sanity check */ + assert ( store != NULL ); + + /* Mark as found, if applicable */ + if ( store->found ) + store->found ( store, cert ); + + return cert; +} + +/** + * Identify X.509 certificate by raw certificate data + * + * @v store Certificate store, or NULL to use default + * @v raw Raw certificate data + * @ret cert X.509 certificate, or NULL if not found + */ +struct x509_certificate * x509_find ( struct x509_chain *store, + const struct asn1_cursor *raw ) { + struct x509_link *link; + struct x509_certificate *cert; + + /* Use default certificate store if none specified */ + if ( ! store ) + store = &certstore; + + /* Search for certificate within store */ + list_for_each_entry ( link, &store->links, list ) { + + /* Check raw certificate data */ + cert = link->cert; + if ( asn1_compare ( raw, &cert->raw ) == 0 ) + return x509_found ( store, cert ); + } + + return NULL; +} + +/** * Identify X.509 certificate by subject * - * @v certs X.509 certificate list + * @v store Certificate store, or NULL to use default * @v subject Subject * @ret cert X.509 certificate, or NULL if not found */ -static struct x509_certificate * -x509_find_subject ( struct x509_chain *certs, +struct x509_certificate * +x509_find_subject ( struct x509_chain *store, const struct asn1_cursor *subject ) { struct x509_link *link; struct x509_certificate *cert; + /* Use default certificate store if none specified */ + if ( ! store ) + store = &certstore; + /* Scan through certificate list */ - list_for_each_entry ( link, &certs->links, list ) { + list_for_each_entry ( link, &store->links, list ) { /* Check subject */ cert = link->cert; if ( asn1_compare ( subject, &cert->subject.raw ) == 0 ) - return cert; + return x509_found ( store, cert ); + } + + return NULL; +} + +/** + * Identify X.509 certificate by issuer and serial number + * + * @v store Certificate store, or NULL to use default + * @v issuer Issuer + * @v serial Serial number + * @ret cert X.509 certificate, or NULL if not found + */ +struct x509_certificate * +x509_find_issuer_serial ( struct x509_chain *store, + const struct asn1_cursor *issuer, + const struct asn1_cursor *serial ) { + struct x509_link *link; + struct x509_certificate *cert; + + /* Use default certificate store if none specified */ + if ( ! store ) + store = &certstore; + + /* Scan through certificate list */ + list_for_each_entry ( link, &store->links, list ) { + + /* Check issuer and serial number */ + cert = link->cert; + if ( ( asn1_compare ( issuer, &cert->issuer.raw ) == 0 ) && + ( asn1_compare ( serial, &cert->serial.raw ) == 0 ) ) + return x509_found ( store, cert ); + } + + return NULL; +} + +/** + * Identify X.509 certificate by corresponding public key + * + * @v store Certificate store, or NULL to use default + * @v key Private key + * @ret cert X.509 certificate, or NULL if not found + */ +struct x509_certificate * x509_find_key ( struct x509_chain *store, + struct private_key *key ) { + struct x509_link *link; + struct x509_certificate *cert; + + /* Use default certificate store if none specified */ + if ( ! store ) + store = &certstore; + + /* Scan through certificate list */ + list_for_each_entry ( link, &store->links, list ) { + + /* Check public key */ + cert = link->cert; + if ( pubkey_match ( cert->signature_algorithm->pubkey, + privkey_cursor ( key ), + &cert->subject.public_key.raw ) == 0 ) + return x509_found ( store, cert ); } return NULL; @@ -1739,13 +1859,13 @@ x509_find_subject ( struct x509_chain *certs, * Append X.509 certificates to X.509 certificate chain * * @v chain X.509 certificate chain - * @v certs X.509 certificate list + * @v store Certificate store, or NULL to use default * @ret rc Return status code * * Certificates will be automatically appended to the chain based upon * the subject and issuer names. */ -int x509_auto_append ( struct x509_chain *chain, struct x509_chain *certs ) { +int x509_auto_append ( struct x509_chain *chain, struct x509_chain *store ) { struct x509_certificate *cert; struct x509_certificate *previous; int rc; @@ -1762,7 +1882,7 @@ int x509_auto_append ( struct x509_chain *chain, struct x509_chain *certs ) { /* Find issuing certificate */ previous = cert; - cert = x509_find_subject ( certs, &cert->issuer.raw ); + cert = x509_find_subject ( store, &cert->issuer.raw ); if ( ! cert ) break; if ( cert == previous ) @@ -1791,10 +1911,6 @@ int x509_validate_chain ( struct x509_chain *chain, time_t time, struct x509_link *link; int rc; - /* Use default certificate store if none specified */ - if ( ! store ) - store = &certstore; - /* Append any applicable certificates from the certificate store */ if ( ( rc = x509_auto_append ( chain, store ) ) != 0 ) return rc; |
