diff options
author | Michael Brown <mcb30@ipxe.org> | 2024-09-19 16:23:32 +0100 |
---|---|---|
committer | Michael Brown <mcb30@ipxe.org> | 2024-09-23 13:19:58 +0100 |
commit | 3def13265d9475c861eed1a101584b761e97ae33 (patch) | |
tree | 5ce095cf73fa7fde7baf8ef65fb826a0881d80e2 /src/arch/arm32/include | |
parent | 59d123658bfe25402c4e89bbaf6eea83140d3c1f (diff) | |
download | ipxe-3def13265d9475c861eed1a101584b761e97ae33.tar.gz |
[crypto] Use constant-time big integer multiplication
Big integer multiplication currently performs immediate carry
propagation from each step of the long multiplication, relying on the
fact that the overall result has a known maximum value to minimise the
number of carries performed without ever needing to explicitly check
against the result buffer size.
This is not a constant-time algorithm, since the number of carries
performed will be a function of the input values. We could make it
constant-time by always continuing to propagate the carry until
reaching the end of the result buffer, but this would introduce a
large number of redundant zero carries.
Require callers of bigint_multiply() to provide a temporary carry
storage buffer, of the same size as the result buffer. This allows
the carry-out from the accumulation of each double-element product to
be accumulated in the temporary carry space, and then added in via a
single call to bigint_add() after the multiplication is complete.
Since the structure of big integer multiplication is identical across
all current CPU architectures, provide a single shared implementation
of bigint_multiply(). The architecture-specific operation then
becomes the multiplication of two big integer elements and the
accumulation of the double-element product.
Note that any intermediate carry arising from accumulating the lower
half of the double-element product may be added to the upper half of
the double-element product without risk of overflow, since the result
of multiplying two n-bit integers can never have all n bits set in its
upper half. This simplifies the carry calculations for architectures
such as RISC-V and LoongArch64 that do not have a carry flag.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/arch/arm32/include')
-rw-r--r-- | src/arch/arm32/include/bits/bigint.h | 34 |
1 files changed, 29 insertions, 5 deletions
diff --git a/src/arch/arm32/include/bits/bigint.h b/src/arch/arm32/include/bits/bigint.h index e4b511da7..0a368a0c0 100644 --- a/src/arch/arm32/include/bits/bigint.h +++ b/src/arch/arm32/include/bits/bigint.h @@ -309,10 +309,34 @@ bigint_done_raw ( const uint32_t *value0, unsigned int size __unused, *(--out_byte) = *(value_byte++); } -extern void bigint_multiply_raw ( const uint32_t *multiplicand0, - unsigned int multiplicand_size, - const uint32_t *multiplier0, - unsigned int multiplier_size, - uint32_t *value0 ); +/** + * Multiply big integer elements + * + * @v multiplicand Multiplicand element + * @v multiplier Multiplier element + * @v result Result element pair + * @v carry Carry element + */ +static inline __attribute__ (( always_inline )) void +bigint_multiply_one ( const uint32_t multiplicand, const uint32_t multiplier, + uint32_t *result, uint32_t *carry ) { + uint32_t discard_low; + uint32_t discard_high; + + __asm__ __volatile__ ( /* Perform multiplication */ + "umull %0, %1, %5, %6\n\t" + /* Accumulate result */ + "adds %2, %0\n\t" + "adcs %3, %1\n\t" + /* Accumulate carry (cannot overflow) */ + "adc %4, #0\n\t" + : "=r" ( discard_low ), + "=r" ( discard_high ), + "+r" ( result[0] ), + "+r" ( result[1] ), + "+r" ( *carry ) + : "r" ( multiplicand ), + "r" ( multiplier ) ); +} #endif /* _BITS_BIGINT_H */ |