OpenCorePkg/Library/OcCryptoLib/BigNumPrimitives.c
2019-11-06 19:47:14 +01:00

904 lines
25 KiB
C

/**
This library performs unsigned arbitrary precision arithmetic operations.
All results are returned into caller-provided buffers. The caller is
responsible to ensure the buffers can hold a value of the precision it
desires. Too large results will be truncated without further notification for
public APIs.
https://github.com/kokke/tiny-bignum-c has served as a template for several
algorithmic ideas.
This code is not to be considered general-purpose but solely to support
cryptographic operations such as RSA encryption. As such, there are arbitrary
limitations, such as requirement of equal precision, to limit the complexity
of the operations to the bare minimum required to support such use caes.
SECURITY: Currently, no security measures have been taken. This code is
vulnerable to both timing and side channel attacks for value
leakage. However, its current purpose is the verification of public
binaries with public certificates, for which this is perfectly
acceptable, especially in regards to performance.
Copyright (C) 2019, Download-Fritz. All rights reserved.
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#include <Base.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/OcCryptoLib.h>
#include <Library/OcGuardLib.h>
#include "BigNumLibInternal.h"
#define OC_BN_MAX_VAL ((OC_BN_WORD)0U - 1U)
OC_STATIC_ASSERT (
OC_BN_WORD_SIZE == sizeof (UINT32) || OC_BN_WORD_SIZE == sizeof (UINT64),
"OC_BN_WORD_SIZE and OC_BN_WORD_NUM_BITS usages must be adapted."
);
OC_BN_WORD
BigNumSwapWord (
IN OC_BN_WORD Word
)
{
if (OC_BN_WORD_SIZE == sizeof (UINT32)) {
return (OC_BN_WORD)SwapBytes32 ((UINT32)Word);
}
if (OC_BN_WORD_SIZE == sizeof (UINT64)) {
return (OC_BN_WORD)SwapBytes64 ((UINT64)Word);
}
ASSERT (FALSE);
}
/**
Shifts A left by Exponent Words.
@param[in,out] Result The buffer to return the result into.
@param[in] NumWordsResult The number of Words of Result.
@param[in,out] A The number to be word-shifted.
@param[in] NumWordsA The number of Words of A.
@param[in] Exponent The Word shift exponent.
**/
STATIC
VOID
BigNumLeftShiftWords (
IN OUT OC_BN_WORD *Result,
IN OC_BN_NUM_WORDS NumWordsResult,
IN CONST OC_BN_WORD *A,
IN OC_BN_NUM_WORDS NumWordsA,
IN UINTN Exponent
)
{
ASSERT (Result != NULL);
ASSERT (NumWordsResult > 0);
ASSERT (A != NULL);
ASSERT (NumWordsA > 0);
ASSERT (Exponent < NumWordsResult);
ASSERT (NumWordsResult - Exponent >= NumWordsA);
CopyMem (&Result[Exponent], A, (NumWordsResult - Exponent) * OC_BN_WORD_SIZE);
ZeroMem (Result, Exponent * OC_BN_WORD_SIZE);
if (NumWordsResult - Exponent > NumWordsA) {
ZeroMem (
&Result[NumWordsA + Exponent],
(NumWordsResult - Exponent - NumWordsA) * OC_BN_WORD_SIZE
);
}
}
/**
Shifts A left by Exponent Bits for 0 < Exponent < #Bits(Word).
@param[in,out] Result The buffer to return the result into.
@param[in] NumWordsResult The number of Words of Result.
@param[in] A The base.
@param[in] NumWordsA The number of Words of A.
@param[in] NumWords The Word shift exponent.
@param[in] NumBits The Bit shift exponent.
**/
STATIC
VOID
BigNumLeftShiftWordsAndBits (
IN OUT OC_BN_WORD *Result,
IN OC_BN_NUM_WORDS NumWordsResult,
IN CONST OC_BN_WORD *A,
IN OC_BN_NUM_WORDS NumWordsA,
IN UINTN NumWords,
IN UINT8 NumBits
)
{
UINTN Index;
ASSERT (Result != NULL);
ASSERT (NumWordsResult > 0);
ASSERT (A != NULL);
ASSERT (NumWordsA > 0);
ASSERT (NumWordsResult >= NumWords);
//
// NumBits must not be 0 because a shift of a Word by its Bit width or
// larger is Undefined Behaviour.
//
ASSERT (NumBits > 0);
ASSERT (NumBits < OC_BN_WORD_NUM_BITS);
//
// This, assuming below, is required to avoid overflows, which purely
// internal calls should never produce.
//
ASSERT (NumWordsResult - NumWords > NumWordsA);
//
// This is not an algorithmic requirement, but BigNumLeftShiftWords shall be
// called if TRUE.
//
ASSERT (NumWords > 0);
for (Index = (NumWordsA - 1); Index > 0; --Index) {
Result[Index + NumWords] = (A[Index] << NumBits) | (A[Index - 1] >> (OC_BN_WORD_NUM_BITS - NumBits));
}
//
// Handle the edge-cases at the beginning and the end of the value.
//
Result[NumWords] = A[0] << NumBits;
Result[NumWordsA + NumWords] = A[NumWordsA - 1] >> (OC_BN_WORD_NUM_BITS - NumBits);
//
// Zero everything outside of the previously set ranges.
//
ZeroMem (&Result[NumWordsA + NumWords + 1], (NumWordsResult - NumWords - NumWordsA - 1) * OC_BN_WORD_SIZE);
ZeroMem (Result, NumWords * OC_BN_WORD_SIZE);
}
/**
Shifts A left by Exponent Bits for 0 < Exponent < #Bits(Word).
@param[in,out] A The base.
@param[in] NumWords The number of Words of A.
@param[in] Exponent The Bit shift exponent.
**/
STATIC
VOID
BigNumLeftShiftBitsSmall (
IN OUT OC_BN_WORD *A,
IN OC_BN_NUM_WORDS NumWords,
IN UINT8 Exponent
)
{
UINTN Index;
ASSERT (A != NULL);
ASSERT (NumWords > 0);
//
// Exponent must not be 0 because a shift of a Word by its Bit width or
// larger is Undefined Behaviour.
//
ASSERT (Exponent > 0);
ASSERT (Exponent < OC_BN_WORD_NUM_BITS);
for (Index = (NumWords - 1); Index > 0; --Index) {
A[Index] = (A[Index] << Exponent) | (A[Index - 1] >> (OC_BN_WORD_NUM_BITS - Exponent));
}
A[0] <<= Exponent;
}
/**
Shifts A right by Exponent Bits for 0 < Exponent < #Bits(Word).
@param[in,out] A The base.
@param[in] NumWords The number of Words of A.
@param[in] Exponent The Bit shift exponent.
**/
STATIC
VOID
BigNumRightShiftBitsSmall (
IN OUT OC_BN_WORD *A,
IN OC_BN_NUM_WORDS NumWords,
IN UINT8 Exponent
)
{
UINTN Index;
ASSERT (A != NULL);
ASSERT (NumWords > 0);
//
// Exponent must not be 0 because a shift of a Word by its Bit width or
// larger is Undefined Behaviour.
//
ASSERT (Exponent > 0);
ASSERT (Exponent < OC_BN_WORD_NUM_BITS);
for (Index = 0; Index < (NumWords - 1U); ++Index) {
A[Index] = (A[Index] >> Exponent) | (A[Index + 1] << (OC_BN_WORD_NUM_BITS - Exponent));
}
A[Index] >>= Exponent;
}
OC_BN_WORD
BigNumWordMul64 (
OUT OC_BN_WORD *Hi,
IN OC_BN_WORD A,
IN OC_BN_WORD B
);
/**
Calculates the product of A and B.
@param[out] Hi Buffer in which the high Word of the result is returned.
@param[in] A The multiplicant.
@param[in] B The multiplier.
@returns The low Word of the result.
**/
OC_BN_WORD
BigNumWordMul (
OUT OC_BN_WORD *Hi,
IN OC_BN_WORD A,
IN OC_BN_WORD B
)
{
UINT64 Result64;
ASSERT (Hi != NULL);
if (OC_BN_WORD_SIZE == sizeof (UINT32)) {
Result64 = (UINT64)A * B;
//
// FIXME: The subtraction in the shift is a dirty hack to shut up MSVC.
//
*Hi = (OC_BN_WORD)(RShiftU64 (Result64, OC_BN_WORD_NUM_BITS));
return (OC_BN_WORD)Result64;
}
if (OC_BN_WORD_SIZE == sizeof (UINT64)) {
return BigNumWordMul64 (Hi, A, B);
}
}
/**
Assigns A to Result.
@param[in,out] Result The buffer to store the result in.
@param[in] NumWordsResult The number of Words of Result.
@param[in] A The number to assign.
@param[in] NumWordsA The number of Words of A.
**/
STATIC
VOID
BigNumAssign (
IN OUT OC_BN_WORD *Result,
IN OC_BN_NUM_WORDS NumWordsResult,
IN CONST OC_BN_WORD *A,
IN OC_BN_NUM_WORDS NumWordsA
)
{
UINTN NumWordsCopy;
ASSERT (Result != NULL);
ASSERT (NumWordsResult > 0);
ASSERT (A != NULL);
ASSERT (NumWordsA > 0);
if (NumWordsResult > NumWordsA) {
ZeroMem (
&Result[NumWordsA],
(NumWordsResult - NumWordsA) * OC_BN_WORD_SIZE
);
NumWordsCopy = NumWordsA;
} else {
NumWordsCopy = NumWordsResult;
}
CopyMem (Result, A, NumWordsCopy * OC_BN_WORD_SIZE);
}
VOID
BigNumAssign0 (
IN OUT OC_BN_WORD *A,
IN OC_BN_NUM_WORDS NumWords
)
{
ASSERT (A != NULL);
ASSERT (NumWords > 0);
ZeroMem (A, NumWords * OC_BN_WORD_SIZE);
}
VOID
BigNumSub (
IN OUT OC_BN_WORD *Result,
IN OC_BN_NUM_WORDS NumWords,
IN CONST OC_BN_WORD *A,
IN CONST OC_BN_WORD *B
)
{
OC_BN_WORD TmpResult;
OC_BN_WORD Tmp1;
OC_BN_WORD Tmp2;
UINTN Index;
UINT8 Borrow;
ASSERT (Result != NULL);
ASSERT (NumWords > 0);
ASSERT (A != NULL);
ASSERT (B != NULL);
//
// As the same indices are ever accessed at a step, the index is always
// increased per step, the preexisting values in c are unused and all are
// are set, it is safe to call this function with c = a or c = b
// ATTENTION: This might conflict with future "top" optimizations
//
Borrow = 0;
for (Index = 0; Index < NumWords; ++Index) {
Tmp1 = A[Index];
Tmp2 = B[Index] + Borrow;
TmpResult = (Tmp1 - Tmp2);
//
// When a subtraction wraps around, the result must be bigger than either
// operand.
//
Borrow = (Tmp2 < Borrow) | (Tmp1 < TmpResult);
Result[Index] = TmpResult;
}
}
/**
Propagates multiplicative Carry from the result at Index - 1 within Result.
@param[in,out] A The number to propagate Carry in.
@param[in] NumWords The number of Words of A.
@param[in] Index The index from which on to add Carry.
@param[in] Carry The carry from the multiplication of Index - 1.
**/
STATIC
VOID
BigNumMulPropagateCarry (
IN OUT OC_BN_WORD *A,
IN OC_BN_NUM_WORDS NumWords,
IN UINTN Index,
IN OC_BN_WORD Carry
)
{
OC_BN_WORD Tmp;
ASSERT (A != NULL);
ASSERT (NumWords > 0);
for (; Index < NumWords && Carry != 0; ++Index) {
Tmp = A[Index] + Carry;
//
// When an addition wraps around, the result must be smaller than either
// operand.
//
Carry = (Tmp < Carry);
A[Index] = Tmp;
}
}
/**
Returns the number of significant bits in a Word.
@param[in] Word The word to gather the number of significant bits of.
@returns The number of significant bits in Word.
**/
STATIC
UINT8
BigNumSignificantBitsWord (
IN OC_BN_WORD Word
)
{
UINT8 NumBits;
OC_BN_WORD Mask;
//
// The values we are receiving are very likely large, thus this approach
// should be reasonably fast.
//
NumBits = OC_BN_WORD_NUM_BITS;
Mask = (OC_BN_WORD)1U << (OC_BN_WORD_NUM_BITS - 1);
while ((Word & Mask) == 0) {
--NumBits;
Mask >>= 1U;
}
return NumBits;
}
/**
Returns the most significant word index of A.
@param[in] A The number to gather the most significant Word index of.
@param[in] NumWords The number of Words of A.
@returns The index of the most significant Word in A.
**/
STATIC
OC_BN_NUM_WORDS
BigNumMostSignificantWord (
IN CONST OC_BN_WORD *A,
IN OC_BN_NUM_WORDS NumWords
)
{
OC_BN_NUM_WORDS Index;
ASSERT (A != NULL);
ASSERT (NumWords > 0);
Index = NumWords;
do {
--Index;
if (A[Index] != 0) {
return Index;
}
} while (Index != 0);
return 0;
}
OC_BN_NUM_BITS
BigNumSignificantBits (
IN CONST OC_BN_WORD *A,
IN OC_BN_NUM_WORDS NumWords
)
{
OC_BN_NUM_BITS Index;
ASSERT (A != NULL);
ASSERT (NumWords > 0);
Index = BigNumMostSignificantWord (A, NumWords);
return ((Index * OC_BN_WORD_NUM_BITS) + BigNumSignificantBitsWord (A[Index]));
}
/**
Calculates the product of A and B.
@param[in,out] Result The buffer to store the result in.
@param[in] NumWordsResult The number of Words of Result.
@param[in] A The multiplicant.
@param[in] NumWordsA The number of Words of A.
@param[in] B The multiplier.
@param[in] NumWordsB The number of Words of B.
**/
STATIC
VOID
BigNumMul (
IN OUT OC_BN_WORD *Result,
IN OC_BN_NUM_WORDS NumWordsResult,
IN CONST OC_BN_WORD *A,
IN OC_BN_NUM_WORDS NumWordsA,
IN CONST OC_BN_WORD *B,
IN OC_BN_NUM_WORDS NumWordsB
)
{
//
// Given a better modulo function, this is subject for removal.
// This algorithm is based on a space-optimised version of the conventional
// Long Multiplication.
// https://en.wikipedia.org/wiki/Multiplication_algorithm#Optimizing_space_complexity
//
OC_BN_WORD CurWord;
OC_BN_WORD MulHi;
OC_BN_WORD MulLo;
OC_BN_WORD CurCarry;
UINTN LengthA;
UINTN LengthB;
UINTN LengthTmp;
UINTN IndexRes;
UINTN IndexA;
UINTN IndexB;
ASSERT (Result != NULL);
ASSERT (NumWordsResult > 0);
ASSERT (A != NULL);
ASSERT (NumWordsA > 0);
ASSERT (B != NULL);
ASSERT (NumWordsB > 0);
ASSERT (A != Result);
ASSERT (B != Result);
BigNumAssign0 (Result, NumWordsResult);
//
// These additions cannot overflow because NumWords fits UINTN.
//
LengthA = BigNumMostSignificantWord (A, NumWordsA) + 1;
LengthB = BigNumMostSignificantWord (B, NumWordsB) + 1;
//
// This cannot overflow for sane outputs due to the address space limitation.
//
ASSERT (LengthA + LengthB > LengthA);
//
// This is required to avoid overflows, which purely internal calls should
// never produce.
//
ASSERT (LengthA + LengthB - 1 < NumWordsResult);
CurCarry = 0;
for (IndexRes = 0; IndexRes < LengthA + LengthB - 1; ++IndexRes) {
//
// Add the carry from the last iteration.
//
CurWord = Result[IndexRes] + CurCarry;
CurCarry = (CurWord < CurCarry);
//
// When IndexB is out of bounds for B, the value at the requested position
// would be 0 and hence no calculation would be performed.
//
if (IndexRes < LengthB) {
IndexA = 0;
LengthTmp = IndexRes + 1;
IndexB = IndexRes;
} else {
IndexA = IndexRes - LengthB + 1;
LengthTmp = LengthA;
IndexB = LengthB - 1;
}
for (; IndexA < LengthTmp; ++IndexA, --IndexB) {
//
// No arithmetics need to be performed when both operands are 0.
//
if ((A[IndexA] | B[IndexB]) == 0) {
continue;
}
MulLo = BigNumWordMul (&MulHi, A[IndexA], B[IndexB]);
//
// FIXME:
// This is hard to read and probably not optimal, however during
// simplification of various operations, it turned out multiplication
// itself is only required as part of the current modulo implementation.
// Instead of cleaning and tweaking this algorithm, an optimised modulo
// algorithm that does not depend on this multiplication algorithm should
// be found and imported, rendering this function subject for removal.
//
CurCarry += MulHi;
if (CurCarry < MulHi) {
//
// If the current Carry overflows, propagate a carry of maximum value
// upwards and start counting anew.
//
BigNumMulPropagateCarry (
Result,
NumWordsResult,
IndexRes + 1,
OC_BN_MAX_VAL
);
++CurCarry;
}
CurWord += MulLo;
if (CurWord < MulLo) {
CurCarry += 1;
if (CurCarry < 1) {
//
// If the current Carry overflows, propagate a carry of maximum value
// upwards and start counting anew.
//
BigNumMulPropagateCarry (
Result,
NumWordsResult,
IndexRes + 1,
OC_BN_MAX_VAL
);
++CurCarry;
}
}
}
Result[IndexRes] = CurWord;
}
//
// Set the MSB to the carry of the last iteration.
//
Result[IndexRes] = CurCarry;
}
VOID
BigNumOrWord (
IN OUT OC_BN_WORD *A,
IN OC_BN_NUM_WORDS NumWords,
IN OC_BN_WORD Value,
IN UINTN Exponent
)
{
UINTN WordIndex;
UINT8 NumBits;
ASSERT (A != NULL);
ASSERT (NumWords > 0);
ASSERT (Exponent / OC_BN_WORD_NUM_BITS < NumWords);
WordIndex = Exponent / OC_BN_WORD_NUM_BITS;
if (WordIndex < NumWords) {
NumBits = Exponent % OC_BN_WORD_NUM_BITS;
A[WordIndex] |= (Value << NumBits);
}
}
INTN
BigNumCmp (
IN CONST OC_BN_WORD *A,
IN OC_BN_NUM_WORDS NumWords,
IN CONST OC_BN_WORD *B
)
{
UINTN Index;
ASSERT (A != NULL);
ASSERT (NumWords > 0);
ASSERT (B != NULL);
Index = NumWords;
do {
--Index;
if (A[Index] > B[Index]) {
return 1;
} else if (A[Index] < B[Index]) {
return -1;
}
} while (Index != 0);
return 0;
}
/**
Calculates the left-shift of A by Exponent Bits.
@param[in,out] Result The buffer to return the result into.
@param[in] NumWordsResult The number of Words of Result.
@param[in] A The number to shift.
@param[in] NumWordsA The number of Words of A.
@param[in] Exponent The amount of Bits to shift by.
**/
STATIC
VOID
BigNumLeftShift (
IN OUT OC_BN_WORD *Result,
IN OC_BN_NUM_WORDS NumWordsResult,
IN CONST OC_BN_WORD *A,
IN OC_BN_NUM_WORDS NumWordsA,
IN UINTN Exponent
)
{
UINTN NumWords;
UINT8 NumBits;
ASSERT (Result != NULL);
ASSERT (NumWordsResult > 0);
ASSERT (A != NULL);
ASSERT (NumWordsA > 0);
NumWords = Exponent / OC_BN_WORD_NUM_BITS;
NumBits = Exponent % OC_BN_WORD_NUM_BITS;
if (NumBits != 0) {
BigNumLeftShiftWordsAndBits (
Result,
NumWordsResult,
A,
NumWordsA,
NumWords,
NumBits
);
} else {
BigNumLeftShiftWords (Result, NumWordsResult, A, NumWordsA, NumWords);
}
}
/**
Calculates the quotient of A and B.
@param[in,out] Result The buffer to return the result into.
@param[in] NumWordsRest The number of Words of Result, A, DenonBuf and
DividenBuf.
@param[in] A The dividend.
@param[in] B The divisor.
@param[in] NumWordsB The number of Words of B.
@param[in] DenomBuf A temporary denominator buffer.
@param[in] DividendBuf A temporary dividend buffer.
@returns Whether the operation was completes successfully.
**/
STATIC
VOID
BigNumDiv (
IN OUT OC_BN_WORD *Result,
IN OC_BN_NUM_WORDS NumWordsRest,
IN CONST OC_BN_WORD *A,
IN CONST OC_BN_WORD *B,
IN OC_BN_NUM_WORDS NumWordsB,
IN OC_BN_WORD *DenomBuf,
IN OC_BN_WORD *DividendBuf
)
{
//
// As for multiplication, this is subject for removal.
//
UINTN CurBitIndex;
BOOLEAN Overflow;
UINT32 NumBitsA;
UINT32 NumBitsB;
ASSERT (Result != NULL);
ASSERT (NumWordsRest > 0);
ASSERT (A != NULL);
ASSERT (B != NULL);
ASSERT (NumWordsB > 0);
ASSERT (DenomBuf != NULL);
ASSERT (DividendBuf != NULL);
//
// Use an integer of natural size to store the current Bit index as 'current'
// is always a 2's potency. While a BIGNUM can theoretically hold a
// 2's potency eight times larger than what can represent as Bit index with a
// natural integer (Bytes vs Bits), this cannot happen within this function
// as 'a' aligned to the next 2's potency would need to be just as big for
// this to be the case. This cannot happen due to the address space
// limitation.
//
CurBitIndex = 0;
Overflow = FALSE;
//
// Shift b to the left so it has the same amount of significant bits as a.
// This would, without this speedup, be done on per-bit basis by the loop
// below.
//
NumBitsA = BigNumSignificantBits (A, NumWordsRest);
NumBitsB = BigNumSignificantBits (B, NumWordsB);
if (NumBitsA > NumBitsB) {
CurBitIndex = NumBitsA - NumBitsB; // int Current = 1 << (numBitsA - numBitsB);
BigNumLeftShift (DenomBuf, NumWordsRest, B, NumWordsB, CurBitIndex); // Denom = B << CurBitIndex
} else {
CurBitIndex = 0; // int Current = 1;
BigNumAssign (DenomBuf, NumWordsRest, B, NumWordsB); // Denom = B
}
while (BigNumCmp (DenomBuf, NumWordsRest, A) <= 0) { // while (Denom <= a) {
if (DenomBuf[NumWordsRest - 1] > (OC_BN_MAX_VAL / 2U)) {
Overflow = TRUE;
break;
}
++CurBitIndex; // Current <<= 1;
BigNumLeftShiftBitsSmall (DenomBuf, NumWordsRest, 1); // Denom <<= 1;
}
if (!Overflow) {
BigNumRightShiftBitsSmall (DenomBuf, NumWordsRest, 1); // Denom >>= 1;
--CurBitIndex; // Current >>= 1;
}
BigNumAssign0 (Result, NumWordsRest); // int Result = 0;
BigNumAssign (DividendBuf, NumWordsRest, A, NumWordsRest); // Dividend = A
//
// currentBitIndex cannot add-wraparound to reach this value as reasoned in
// the comment before.
//
while (CurBitIndex != (0ULL - 1ULL)) { // while (Current != 0)
if (BigNumCmp (DividendBuf, NumWordsRest, DenomBuf) >= 0) { // if (Dividend >= Denom)
BigNumSub (DividendBuf, NumWordsRest, DividendBuf, DenomBuf); // Dividend -= denom;
BigNumOrWord (Result, NumWordsRest, 1, CurBitIndex); // Result |= current;
}
--CurBitIndex; // Current >>= 1;
BigNumRightShiftBitsSmall (DenomBuf, NumWordsRest, 1); // Denom >>= 1;
} // return Result;
}
BOOLEAN
BigNumMod (
IN OUT OC_BN_WORD *Result,
IN OC_BN_NUM_WORDS NumWordsRest,
IN CONST OC_BN_WORD *A,
IN OC_BN_NUM_WORDS NumWordsA,
IN CONST OC_BN_WORD *B
)
{
//
// FIXME:
// The algorithm is rather expensive and slow. It utilitises the current
// suboptimal multiplication and division algorithms. An optimised algorithm
// should be imported and formerly mentioned functions be removed as they'd
// be dead code.
//
UINTN TempsBnSize;
VOID *Memory;
OC_BN_WORD *TmpDiv;
OC_BN_WORD *TmpMod;
OC_BN_WORD *TmpDenom;
OC_BN_WORD *TmpDividend;
ASSERT (Result != NULL);
ASSERT (NumWordsRest > 0);
ASSERT (A != NULL);
ASSERT (NumWordsA > 0);
ASSERT (B != NULL);
ASSERT (NumWordsA >= NumWordsRest);
OC_STATIC_ASSERT (
OC_BN_MAX_SIZE <= MAX_UINTN / 4,
"An overflow verification must be added"
);
TempsBnSize = NumWordsA * OC_BN_WORD_SIZE;
Memory = AllocatePool (4 * TempsBnSize);
if (Memory == NULL) {
return FALSE;
}
TmpDiv = (OC_BN_WORD *)Memory;
TmpMod = (OC_BN_WORD *)((UINTN)TmpDiv + TempsBnSize);
TmpDenom = (OC_BN_WORD *)((UINTN)TmpMod + TempsBnSize);
TmpDividend = (OC_BN_WORD *)((UINTN)TmpDenom + TempsBnSize);
BigNumDiv (TmpDiv, NumWordsA, A, B, NumWordsRest, TmpDenom, TmpDividend);
BigNumMul (TmpMod, NumWordsA, TmpDiv, NumWordsA, B, NumWordsRest);
BigNumSub (Result, NumWordsRest, A, TmpMod);
FreePool (Memory);
return TRUE;
}
VOID
BigNumParseBuffer (
IN OUT OC_BN_WORD *Result,
IN OC_BN_NUM_WORDS NumWords,
IN CONST UINT8 *Buffer,
IN UINTN BufferSize
)
{
UINTN Index;
OC_BN_WORD Tmp;
ASSERT (Result != NULL);
ASSERT (NumWords * OC_BN_WORD_SIZE == BufferSize);
ASSERT (Buffer != NULL);
ASSERT (BufferSize > 0);
ASSERT ((BufferSize % OC_BN_WORD_SIZE) == 0);
for (Index = OC_BN_WORD_SIZE; Index <= BufferSize; Index += OC_BN_WORD_SIZE) {
if (OC_BN_WORD_SIZE == sizeof (UINT32)) {
Tmp = (OC_BN_WORD)(
((UINT32)Buffer[(BufferSize - Index) + 0] << 24U) |
((UINT32)Buffer[(BufferSize - Index) + 1] << 16U) |
((UINT32)Buffer[(BufferSize - Index) + 2] << 8U) |
((UINT32)Buffer[(BufferSize - Index) + 3] << 0U));
} else if (OC_BN_WORD_SIZE == sizeof (UINT64)) {
Tmp = (OC_BN_WORD)(
((UINT64)Buffer[(BufferSize - Index) + 0] << 56U) |
((UINT64)Buffer[(BufferSize - Index) + 1] << 48U) |
((UINT64)Buffer[(BufferSize - Index) + 2] << 40U) |
((UINT64)Buffer[(BufferSize - Index) + 3] << 32U) |
((UINT64)Buffer[(BufferSize - Index) + 4] << 24U) |
((UINT64)Buffer[(BufferSize - Index) + 5] << 16U) |
((UINT64)Buffer[(BufferSize - Index) + 6] << 8U) |
((UINT64)Buffer[(BufferSize - Index) + 7] << 0U));
}
Result[(Index / OC_BN_WORD_SIZE) - 1] = Tmp;
}
}