mirror of
https://github.com/ipxe/ipxe
synced 2026-01-31 11:39:46 +03:00
[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>
This commit is contained in:
@@ -1,106 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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 uint32_t *multiplicand0,
|
||||
unsigned int multiplicand_size,
|
||||
const uint32_t *multiplier0,
|
||||
unsigned int multiplier_size,
|
||||
uint32_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 );
|
||||
unsigned int i;
|
||||
unsigned int j;
|
||||
uint32_t multiplicand_element;
|
||||
uint32_t multiplier_element;
|
||||
uint32_t *result_elements;
|
||||
uint32_t discard_low;
|
||||
uint32_t discard_high;
|
||||
uint32_t discard_temp;
|
||||
|
||||
/* 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__ ( "umull %1, %2, %5, %6\n\t"
|
||||
"ldr %3, [%0]\n\t"
|
||||
"adds %3, %1\n\t"
|
||||
"stmia %0!, {%3}\n\t"
|
||||
"ldr %3, [%0]\n\t"
|
||||
"adcs %3, %2\n\t"
|
||||
"stmia %0!, {%3}\n\t"
|
||||
"bcc 2f\n\t"
|
||||
"\n1:\n\t"
|
||||
"ldr %3, [%0]\n\t"
|
||||
"adcs %3, #0\n\t"
|
||||
"stmia %0!, {%3}\n\t"
|
||||
"bcs 1b\n\t"
|
||||
"\n2:\n\t"
|
||||
: "+l" ( result_elements ),
|
||||
"=l" ( discard_low ),
|
||||
"=l" ( discard_high ),
|
||||
"=l" ( discard_temp ),
|
||||
"+m" ( *result )
|
||||
: "l" ( multiplicand_element ),
|
||||
"l" ( multiplier_element )
|
||||
: "cc" );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user