diff options
author | Michael Brown <mcb30@ipxe.org> | 2024-11-27 13:25:18 +0000 |
---|---|---|
committer | Michael Brown <mcb30@ipxe.org> | 2024-11-27 13:25:18 +0000 |
commit | 4f7dd7fbba205d413cf9b989f7cdc928fa02caf2 (patch) | |
tree | ab8254a6e9acf2324523b6ae1f66beb967f92886 | |
parent | 96f385d7a48ffe259295991043a86b2cefce1891 (diff) | |
download | ipxe-4f7dd7fbba205d413cf9b989f7cdc928fa02caf2.tar.gz |
[crypto] Add bigint_montgomery() to perform Montgomery reduction
Montgomery reduction is substantially faster than direct reduction,
and is better suited for modular exponentiation operations.
Add bigint_montgomery() to perform the Montgomery reduction operation
(often referred to as "REDC"), along with some test vectors.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
-rw-r--r-- | src/crypto/bigint.c | 77 | ||||
-rw-r--r-- | src/include/ipxe/bigint.h | 21 | ||||
-rw-r--r-- | src/tests/bigint_test.c | 76 |
3 files changed, 174 insertions, 0 deletions
diff --git a/src/crypto/bigint.c b/src/crypto/bigint.c index 735fcdf61..6d75fbe9b 100644 --- a/src/crypto/bigint.c +++ b/src/crypto/bigint.c @@ -355,6 +355,83 @@ void bigint_mod_invert_raw ( const bigint_element_t *invertend0, } /** + * Perform Montgomery reduction (REDC) of a big integer product + * + * @v modulus0 Element 0 of big integer modulus + * @v modinv0 Element 0 of the inverse of the modulus modulo 2^k + * @v mont0 Element 0 of big integer Montgomery product + * @v result0 Element 0 of big integer to hold result + * @v size Number of elements in modulus and result + * + * Note that only the least significant element of the inverse modulo + * 2^k is required, and that the Montgomery product will be + * overwritten. + */ +void bigint_montgomery_raw ( const bigint_element_t *modulus0, + const bigint_element_t *modinv0, + bigint_element_t *mont0, + bigint_element_t *result0, unsigned int size ) { + const bigint_t ( size ) __attribute__ (( may_alias )) + *modulus = ( ( const void * ) modulus0 ); + const bigint_t ( 1 ) __attribute__ (( may_alias )) + *modinv = ( ( const void * ) modinv0 ); + union { + bigint_t ( size * 2 ) full; + struct { + bigint_t ( size ) low; + bigint_t ( size ) high; + } __attribute__ (( packed )); + } __attribute__ (( may_alias )) *mont = ( ( void * ) mont0 ); + bigint_t ( size ) __attribute__ (( may_alias )) + *result = ( ( void * ) result0 ); + bigint_element_t negmodinv = -modinv->element[0]; + bigint_element_t multiple; + bigint_element_t carry; + unsigned int i; + unsigned int j; + int overflow; + int underflow; + + /* Sanity checks */ + assert ( bigint_bit_is_set ( modulus, 0 ) ); + + /* Perform multiprecision Montgomery reduction */ + for ( i = 0 ; i < size ; i++ ) { + + /* Determine scalar multiple for this round */ + multiple = ( mont->low.element[i] * negmodinv ); + + /* 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], + &mont->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. We can store the multiplication carry out + * in this element, avoiding the need to immediately + * propagate the carry through the remaining elements. + */ + assert ( mont->low.element[i] == 0 ); + mont->low.element[i] = carry; + } + + /* Add the accumulated carries */ + overflow = bigint_add ( &mont->low, &mont->high ); + + /* Conditionally subtract the modulus once */ + memcpy ( result, &mont->high, sizeof ( *result ) ); + underflow = bigint_subtract ( modulus, result ); + bigint_swap ( result, &mont->high, ( underflow & ~overflow ) ); + + /* Sanity check */ + assert ( ! bigint_is_geq ( result, modulus ) ); +} + +/** * Perform modular multiplication of big integers * * @v multiplicand0 Element 0 of big integer to be multiplied diff --git a/src/include/ipxe/bigint.h b/src/include/ipxe/bigint.h index 14f3c5f28..6c9730252 100644 --- a/src/include/ipxe/bigint.h +++ b/src/include/ipxe/bigint.h @@ -254,6 +254,23 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); } while ( 0 ) /** + * Perform Montgomery reduction (REDC) of a big integer product + * + * @v modulus Big integer modulus + * @v modinv Big integer inverse of the modulus modulo 2^k + * @v mont Big integer Montgomery product + * @v result Big integer to hold result + * + * Note that the Montgomery product will be overwritten. + */ +#define bigint_montgomery( modulus, modinv, mont, result ) do { \ + unsigned int size = bigint_size (modulus); \ + bigint_montgomery_raw ( (modulus)->element, (modinv)->element, \ + (mont)->element, (result)->element, \ + size ); \ + } while ( 0 ) + +/** * Perform modular multiplication of big integers * * @v multiplicand Big integer to be multiplied @@ -396,6 +413,10 @@ void bigint_reduce_raw ( bigint_element_t *modulus0, bigint_element_t *value0, unsigned int size ); void bigint_mod_invert_raw ( const bigint_element_t *invertend0, bigint_element_t *inverse0, unsigned int size ); +void bigint_montgomery_raw ( const bigint_element_t *modulus0, + const bigint_element_t *modinv0, + bigint_element_t *mont0, + bigint_element_t *result0, unsigned int size ); void bigint_mod_multiply_raw ( const bigint_element_t *multiplicand0, const bigint_element_t *multiplier0, const bigint_element_t *modulus0, diff --git a/src/tests/bigint_test.c b/src/tests/bigint_test.c index bee36de25..1f2f5f244 100644 --- a/src/tests/bigint_test.c +++ b/src/tests/bigint_test.c @@ -206,6 +206,23 @@ void bigint_mod_invert_sample ( const bigint_element_t *invertend0, bigint_mod_invert ( invertend, inverse ); } +void bigint_montgomery_sample ( const bigint_element_t *modulus0, + const bigint_element_t *modinv0, + bigint_element_t *mont0, + bigint_element_t *result0, + unsigned int size ) { + const bigint_t ( size ) __attribute__ (( may_alias )) + *modulus = ( ( const void * ) modulus0 ); + const bigint_t ( 1 ) __attribute__ (( may_alias )) + *modinv = ( ( const void * ) modinv0 ); + bigint_t ( 2 * size ) __attribute__ (( may_alias )) + *mont = ( ( void * ) mont0 ); + bigint_t ( size ) __attribute__ (( may_alias )) + *result = ( ( void * ) result0 ); + + bigint_montgomery ( modulus, modinv, mont, result ); +} + void bigint_mod_multiply_sample ( const bigint_element_t *multiplicand0, const bigint_element_t *multiplier0, const bigint_element_t *modulus0, @@ -618,6 +635,46 @@ void bigint_mod_exp_sample ( const bigint_element_t *base0, } while ( 0 ) /** + * Report result of Montgomery reduction (REDC) test + * + * @v modulus Big integer modulus + * @v mont Big integer Montgomery product + * @v expected Big integer expected result + */ +#define bigint_montgomery_ok( modulus, mont, expected ) do { \ + static const uint8_t modulus_raw[] = modulus; \ + static const uint8_t mont_raw[] = mont; \ + static const uint8_t expected_raw[] = expected; \ + uint8_t result_raw[ sizeof ( expected_raw ) ]; \ + unsigned int size = \ + bigint_required_size ( sizeof ( modulus_raw ) ); \ + bigint_t ( size ) modulus_temp; \ + bigint_t ( 1 ) modinv_temp; \ + bigint_t ( 2 * size ) mont_temp; \ + bigint_t ( size ) result_temp; \ + {} /* Fix emacs alignment */ \ + \ + assert ( ( sizeof ( modulus_raw ) % \ + sizeof ( bigint_element_t ) ) == 0 ); \ + bigint_init ( &modulus_temp, modulus_raw, \ + sizeof ( modulus_raw ) ); \ + bigint_init ( &mont_temp, mont_raw, sizeof ( mont_raw ) ); \ + bigint_mod_invert ( &modulus_temp, &modinv_temp ); \ + DBG ( "Montgomery:\n" ); \ + DBG_HDA ( 0, &modulus_temp, sizeof ( modulus_temp ) ); \ + DBG_HDA ( 0, &modinv_temp, sizeof ( modinv_temp ) ); \ + DBG_HDA ( 0, &mont_temp, sizeof ( mont_temp ) ); \ + bigint_montgomery ( &modulus_temp, &modinv_temp, &mont_temp, \ + &result_temp ); \ + DBG_HDA ( 0, &result_temp, sizeof ( result_temp ) ); \ + bigint_done ( &result_temp, result_raw, \ + sizeof ( result_raw ) ); \ + \ + ok ( memcmp ( result_raw, expected_raw, \ + sizeof ( result_raw ) ) == 0 ); \ + } while ( 0 ) + +/** * Report result of big integer modular multiplication test * * @v multiplicand Big integer to be multiplied @@ -1858,6 +1915,25 @@ static void bigint_test_exec ( void ) { 0x2e, 0xe6, 0x22, 0x07 ), BIGINT ( 0x7b, 0xd1, 0x0f, 0x78, 0x0c, 0x65, 0xab, 0xb7 ) ); + bigint_montgomery_ok ( BIGINT ( 0x74, 0xdf, 0xd1, 0xb8, 0x14, 0xf7, + 0x05, 0x83 ), + BIGINT ( 0x50, 0x6a, 0x38, 0x55, 0x9f, 0xb9, + 0x9d, 0xba, 0xff, 0x23, 0x86, 0x65, + 0xe3, 0x2c, 0x3f, 0x17 ), + BIGINT ( 0x45, 0x1f, 0x51, 0x44, 0x6f, 0x3c, + 0x09, 0x6b ) ); + bigint_montgomery_ok ( BIGINT ( 0x2e, 0x32, 0x90, 0x69, 0x6e, 0xa8, + 0x47, 0x4c, 0xad, 0xe4, 0xe7, 0x4c, + 0x03, 0xcb, 0xe6, 0x55 ), + BIGINT ( 0x1e, 0x43, 0xf9, 0xc2, 0x61, 0xdd, + 0xe8, 0xbf, 0xb8, 0xea, 0xe0, 0xdb, + 0xed, 0x66, 0x80, 0x1e, 0xe8, 0xf8, + 0xd1, 0x1d, 0xca, 0x8d, 0x45, 0xe9, + 0xc5, 0xeb, 0x77, 0x21, 0x34, 0xe0, + 0xf5, 0x5a ), + BIGINT ( 0x03, 0x38, 0xfb, 0xb6, 0xf3, 0x80, + 0x91, 0xb2, 0x68, 0x2f, 0x81, 0x44, + 0xbf, 0x43, 0x0a, 0x4e ) ); bigint_mod_multiply_ok ( BIGINT ( 0x37 ), BIGINT ( 0x67 ), BIGINT ( 0x3f ), |