]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[crypto] Add bigint_mod_invert() to calculate inverse modulo a power of two
authorMichael Brown <mcb30@ipxe.org>
Mon, 21 Oct 2024 15:09:44 +0000 (16:09 +0100)
committerMichael Brown <mcb30@ipxe.org>
Mon, 21 Oct 2024 16:24:53 +0000 (17:24 +0100)
Montgomery multiplication requires calculating the inverse of the
modulus modulo a larger power of two.

Add bigint_mod_invert() to calculate the inverse of any (odd) big
integer modulo an arbitrary power of two, using a lightly modified
version of the algorithm presented in "A New Algorithm for Inversion
mod p^k (Koç, 2017)".

The power of two is taken to be 2^k, where k is the number of bits
available in the big integer representation of the invertend.  The
inverse modulo any smaller power of two may be obtained simply by
masking off the relevant bits in the inverse.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/crypto/bigint.c
src/include/ipxe/bigint.h
src/tests/bigint_test.c

index a8b99ec3c07c0162291241960cb4b8f45ad8f419..3f5db8517bfffce5ba75816c32cf256c52bf7cde 100644 (file)
@@ -305,6 +305,60 @@ void bigint_reduce_raw ( const bigint_element_t *minuend0,
        profile_stop ( &bigint_mod_profiler );
 }
 
+/**
+ * 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
+ * @v tmp              Temporary working space
+ */
+void bigint_mod_invert_raw ( const bigint_element_t *invertend0,
+                            bigint_element_t *inverse0,
+                            unsigned int size, void *tmp ) {
+       const bigint_t ( size ) __attribute__ (( may_alias ))
+               *invertend = ( ( const void * ) invertend0 );
+       bigint_t ( size ) __attribute__ (( may_alias ))
+               *inverse = ( ( void * ) inverse0 );
+       struct {
+               bigint_t ( size ) residue;
+       } *temp = tmp;
+       const unsigned int width = ( 8 * sizeof ( bigint_element_t ) );
+       unsigned int i;
+
+       /* Sanity check */
+       assert ( invertend->element[0] & 1 );
+
+       /* Initialise temporary working space and output value */
+       memset ( &temp->residue, 0xff, sizeof ( temp->residue ) );
+       memset ( inverse, 0, 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 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).
+        *
+        * 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 = 0 ; i < ( 8 * sizeof ( *inverse ) ) ; i++ ) {
+               if ( temp->residue.element[0] & 1 ) {
+                       inverse->element[ i / width ] |=
+                               ( 1UL << ( i % width ) );
+                       bigint_add ( invertend, &temp->residue );
+               }
+               bigint_shr ( &temp->residue );
+       }
+}
+
 /**
  * Perform modular multiplication of big integers
  *
index c56b2155f1770d2741d46a3c5a479ef344fcc292..f7900e0e6450dd2587c0e4ddd15b5755ee376c07 100644 (file)
@@ -246,6 +246,31 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
                bigint_t ( size ) temp_modulus;                         \
        } ); } )
 
+/**
+ * Compute inverse of odd big integer modulo its own size
+ *
+ * @v invertend                Odd big integer to be inverted
+ * @v inverse          Big integer to hold result
+ * @v tmp              Temporary working space
+ */
+#define bigint_mod_invert( invertend, inverse, tmp ) do {              \
+       unsigned int size = bigint_size (invertend);                    \
+       bigint_mod_invert_raw ( (invertend)->element,                   \
+                               (inverse)->element, size, tmp );        \
+       } while ( 0 )
+
+/**
+ * Calculate temporary working space required for modular inversion
+ *
+ * @v invertend                Odd big integer to be inverted
+ * @ret len            Length of temporary working space
+ */
+#define bigint_mod_invert_tmp_len( invertend ) ( {                     \
+       unsigned int size = bigint_size (invertend);                    \
+       sizeof ( struct {                                               \
+               bigint_t ( size ) temp_residue;                         \
+       } ); } )
+
 /**
  * Perform modular multiplication of big integers
  *
@@ -373,6 +398,9 @@ void bigint_reduce_raw ( const bigint_element_t *minuend0,
                         const bigint_element_t *modulus0,
                         unsigned int modulus_size,
                         bigint_element_t *result0, void *tmp );
+void bigint_mod_invert_raw ( const bigint_element_t *invertend0,
+                            bigint_element_t *inverse0,
+                            unsigned int size, void *tmp );
 void bigint_mod_multiply_raw ( const bigint_element_t *multiplicand0,
                               const bigint_element_t *multiplier0,
                               const bigint_element_t *modulus0,
index 104e1f362cc1ead2c4b1056074f32493d6d51669..166c771b9711a7024f74edc2043810f43d5a297e 100644 (file)
@@ -200,6 +200,17 @@ void bigint_reduce_sample ( const bigint_element_t *minuend0,
        bigint_reduce ( minuend, modulus, result, tmp );
 }
 
+void bigint_mod_invert_sample ( const bigint_element_t *invertend0,
+                               bigint_element_t *inverse0,
+                               unsigned int size, void *tmp ) {
+       const bigint_t ( size ) __attribute__ (( may_alias ))
+               *invertend = ( ( const void * ) invertend0 );
+       bigint_t ( size ) __attribute__ (( may_alias ))
+               *inverse = ( ( void * ) inverse0 );
+
+       bigint_mod_invert ( invertend, inverse, tmp );
+}
+
 void bigint_mod_multiply_sample ( const bigint_element_t *multiplicand0,
                                  const bigint_element_t *multiplier0,
                                  const bigint_element_t *modulus0,
@@ -573,6 +584,39 @@ void bigint_mod_exp_sample ( const bigint_element_t *base0,
                      sizeof ( result_raw ) ) == 0 );                   \
        } while ( 0 )
 
+/**
+ * Report result of big integer modular inversion test
+ *
+ * @v invertend                Big integer to be inverted
+ * @v expected         Big integer expected result
+ */
+#define bigint_mod_invert_ok( invertend, expected ) do {               \
+       static const uint8_t invertend_raw[] = invertend;               \
+       static const uint8_t expected_raw[] = expected;                 \
+       uint8_t inverse_raw[ sizeof ( expected_raw ) ];                 \
+       unsigned int size =                                             \
+               bigint_required_size ( sizeof ( invertend_raw ) );      \
+       bigint_t ( size ) invertend_temp;                               \
+       bigint_t ( size ) inverse_temp;                                 \
+       size_t tmp_len = bigint_mod_invert_tmp_len ( &invertend_temp ); \
+       uint8_t tmp[tmp_len];                                           \
+       {} /* Fix emacs alignment */                                    \
+                                                                       \
+       assert ( bigint_size ( &invertend_temp ) ==                     \
+                bigint_size ( &inverse_temp ) );                       \
+       bigint_init ( &invertend_temp, invertend_raw,                   \
+                     sizeof ( invertend_raw ) );                       \
+       DBG ( "Modular invert:\n" );                                    \
+       DBG_HDA ( 0, &invertend_temp, sizeof ( invertend_temp ) );      \
+       bigint_mod_invert ( &invertend_temp, &inverse_temp, tmp );      \
+       DBG_HDA ( 0, &inverse_temp, sizeof ( inverse_temp ) );          \
+       bigint_done ( &inverse_temp, inverse_raw,                       \
+                     sizeof ( inverse_raw ) );                         \
+                                                                       \
+       ok ( memcmp ( inverse_raw, expected_raw,                        \
+                     sizeof ( inverse_raw ) ) == 0 );                  \
+       } while ( 0 )
+
 /**
  * Report result of big integer modular multiplication test
  *
@@ -1760,6 +1804,23 @@ static void bigint_test_exec ( void ) {
                                    0xfb, 0x5d, 0x55 ),
                           BIGINT ( 0x27, 0x31, 0x49, 0xc3, 0xf5, 0x06, 0x1f,
                                    0x3c, 0x7c, 0xd5 ) );
+       bigint_mod_invert_ok ( BIGINT ( 0x01 ), BIGINT ( 0x01 ) );
+       bigint_mod_invert_ok ( BIGINT ( 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                                       0xff, 0xff ),
+                              BIGINT ( 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                                       0xff, 0xff ) );
+       bigint_mod_invert_ok ( BIGINT ( 0x95, 0x6a, 0xc5, 0xe7, 0x2e, 0x5b,
+                                       0x44, 0xed, 0xbf, 0x7e, 0xfe, 0x8d,
+                                       0xf4, 0x5a, 0x48, 0xc1 ),
+                              BIGINT ( 0xad, 0xb8, 0x3d, 0x85, 0x10, 0xdf,
+                                       0xea, 0x70, 0x71, 0x2c, 0x80, 0xf4,
+                                       0x6e, 0x66, 0x47, 0x41 ) );
+       bigint_mod_invert_ok ( BIGINT ( 0x35, 0xe4, 0x80, 0x48, 0xdd, 0xa1,
+                                       0x46, 0xc0, 0x84, 0x63, 0xc1, 0xe4,
+                                       0xf7, 0xbf, 0xb3, 0x05 ),
+                              BIGINT ( 0xf2, 0x9c, 0x63, 0x29, 0xfa, 0xe4,
+                                       0xbf, 0x90, 0xa6, 0x9a, 0xec, 0xcf,
+                                       0x5f, 0xe2, 0x21, 0xcd ) );
        bigint_mod_multiply_ok ( BIGINT ( 0x37 ),
                                 BIGINT ( 0x67 ),
                                 BIGINT ( 0x3f ),