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/riscv | |
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/riscv')
-rw-r--r-- | src/arch/riscv/core/riscv_bigint.c | 112 | ||||
-rw-r--r-- | src/arch/riscv/include/bits/bigint.h | 43 |
2 files changed, 38 insertions, 117 deletions
diff --git a/src/arch/riscv/core/riscv_bigint.c b/src/arch/riscv/core/riscv_bigint.c deleted file mode 100644 index cbc5631a3..000000000 --- a/src/arch/riscv/core/riscv_bigint.c +++ /dev/null @@ -1,112 +0,0 @@ -/* - * 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 ); - -#include <stdint.h> -#include <string.h> -#include <ipxe/bigint.h> - -/** @file - * - * Big integer support - */ - -/** - * 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 unsigned long *multiplicand0, - unsigned int multiplicand_size, - const unsigned long *multiplier0, - unsigned int multiplier_size, - unsigned long *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 ); - unsigned int i; - unsigned int j; - unsigned long multiplicand_element; - unsigned long multiplier_element; - unsigned long *result_elements; - unsigned long discard_low; - unsigned long discard_high; - unsigned long discard_temp; - unsigned long discard_carry; - - /* Zero result */ - memset ( result, 0, sizeof ( *result ) ); - - /* Multiply integers one element at a time */ - for ( i = 0 ; i < multiplicand_size ; i++ ) { - multiplicand_element = multiplicand->element[i]; - for ( j = 0 ; j < multiplier_size ; j++ ) { - multiplier_element = multiplier->element[j]; - result_elements = &result->element[ i + j ]; - /* Perform a single multiply, and add the - * resulting double-element into the result, - * carrying as necessary. The carry can - * never overflow beyond the end of the - * result, since: - * - * a < 2^{n}, b < 2^{m} => ab < 2^{n+m} - */ - __asm__ __volatile__ ( /* Perform multiplication */ - "mulhu %2, %6, %7\n\t" - "mul %1, %6, %7\n\t" - /* Accumulate low half */ - LOADN " %3, (%0)\n\t" - "add %3, %3, %1\n\t" - "sltu %4, %3, %1\n\t" - STOREN " %3, 0(%0)\n\t" - /* Carry into high half */ - "add %4, %4, %2\n\t" - /* Propagate as necessary */ - "\n1:\n\t" - "addi %0, %0, %8\n\t" - LOADN " %3, 0(%0)\n\t" - "add %3, %3, %4\n\t" - "sltu %4, %3, %4\n\t" - STOREN " %3, 0(%0)\n\t" - "bnez %4, 1b\n\t" - : "+r" ( result_elements ), - "=r" ( discard_low ), - "=r" ( discard_high ), - "=r" ( discard_temp ), - "=r" ( discard_carry ), - "+m" ( *result ) - : "r" ( multiplicand_element ), - "r" ( multiplier_element ), - "i" ( sizeof ( *result0 ) ) ); - } - } -} diff --git a/src/arch/riscv/include/bits/bigint.h b/src/arch/riscv/include/bits/bigint.h index fcc0d51c3..c58233469 100644 --- a/src/arch/riscv/include/bits/bigint.h +++ b/src/arch/riscv/include/bits/bigint.h @@ -353,10 +353,43 @@ bigint_done_raw ( const unsigned long *value0, unsigned int size __unused, *(--out_byte) = *(value_byte++); } -extern void bigint_multiply_raw ( const unsigned long *multiplicand0, - unsigned int multiplicand_size, - const unsigned long *multiplier0, - unsigned int multiplier_size, - unsigned long *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 unsigned long multiplicand, + const unsigned long multiplier, + unsigned long *result, unsigned long *carry ) { + unsigned long discard_low; + unsigned long discard_high; + unsigned long discard_carry; + + __asm__ __volatile__ ( /* Perform multiplication */ + "mulhu %1, %6, %7\n\t" + "mul %0, %6, %7\n\t" + /* Accumulate low half */ + "add %3, %3, %0\n\t" + "sltu %2, %3, %0\n\t" + /* Add carry to high half (cannot overflow) */ + "add %1, %1, %2\n\t" + /* Accumulate high half */ + "add %4, %4, %1\n\t" + "sltu %2, %4, %1\n\t" + /* Accumulate carry (cannot overflow) */ + "add %5, %5, %2\n\t" + : "=r" ( discard_low ), + "=&r" ( discard_high ), + "=r" ( discard_carry ), + "+r" ( result[0] ), + "+r" ( result[1] ), + "+r" ( *carry ) + : "r" ( multiplicand ), + "r" ( multiplier ) ); +} #endif /* _BITS_BIGINT_H */ |