1083 lines
23 KiB
C
1083 lines
23 KiB
C
/****************************************************************************
|
|
* crypto/bn.c
|
|
* This is free and unencumbered software released into the public domain.
|
|
* Anyone is free to copy, modify, publish, use, compile, sell, or
|
|
* distribute this software, either in source code form or as a compiled
|
|
* binary, for any purpose, commercial or non-commercial, and by any
|
|
* means.
|
|
* In jurisdictions that recognize copyright laws, the author or authors
|
|
* of this software dedicate any and all copyright interest in the
|
|
* software to the public domain. We make this dedication for the benefit
|
|
* of the public at large and to the detriment of our heirs and
|
|
* successors. We intend this dedication to be an overt act of
|
|
* relinquishment in perpetuity of all present and future rights to this
|
|
* software under copyright law.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
* OTHER DEALINGS IN THE SOFTWARE.
|
|
*
|
|
* For more information, please refer to <https://unlicense.org>
|
|
****************************************************************************/
|
|
|
|
/* Big number library - arithmetic on multiple-precision unsigned integers.
|
|
*
|
|
* This library is an implementation of arithmetic on arbitrarily large
|
|
* integers.
|
|
*
|
|
* The difference between this and other implementations, is that the data
|
|
* structure has optimal memory utilization (i.e. a 1024 bit integer takes up
|
|
* 128 bytes RAM), and all memory is allocated statically: no dynamic
|
|
* allocation for better or worse.
|
|
*
|
|
* Primary goals are correctness, clarity of code and clean, portable
|
|
* implementation. Secondary goal is a memory footprint small enough to make
|
|
* it suitable for use in embedded applications.
|
|
*
|
|
*
|
|
* The current state is correct functionality and adequate performance.
|
|
* There may well be room for performance-optimizations and improvements.
|
|
*/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <stdio.h>
|
|
#include <stdbool.h>
|
|
#include <assert.h>
|
|
#include <crypto/bn.h>
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
/* Custom assert macro - easy to disable */
|
|
|
|
#define require(p, msg) ASSERT(p && msg)
|
|
|
|
/****************************************************************************
|
|
* Private Functions Prototype
|
|
****************************************************************************/
|
|
|
|
/* Functions for shifting number in-place. */
|
|
|
|
static void lshift_one_bit(FAR struct bn *a);
|
|
static void rshift_one_bit(FAR struct bn *a);
|
|
static void lshift_word(FAR struct bn *a, int nwords);
|
|
static void rshift_word(FAR struct bn *a, int nwords);
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/* Private / Static functions. */
|
|
|
|
static void rshift_word(FAR struct bn *a, int nwords)
|
|
{
|
|
/* Naive method: */
|
|
|
|
int i;
|
|
|
|
require(a, "a is null");
|
|
require(nwords >= 0, "no negative shifts");
|
|
|
|
if (nwords >= BN_ARRAY_SIZE)
|
|
{
|
|
for (i = 0; i < BN_ARRAY_SIZE; ++i)
|
|
{
|
|
a->array[i] = 0;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < BN_ARRAY_SIZE - nwords; ++i)
|
|
{
|
|
a->array[i] = a->array[i + nwords];
|
|
}
|
|
|
|
for (; i < BN_ARRAY_SIZE; ++i)
|
|
{
|
|
a->array[i] = 0;
|
|
}
|
|
}
|
|
|
|
static void lshift_word(FAR struct bn *a, int nwords)
|
|
{
|
|
int i;
|
|
|
|
require(a, "a is null");
|
|
require(nwords >= 0, "no negative shifts");
|
|
|
|
/* Shift whole words */
|
|
|
|
for (i = (BN_ARRAY_SIZE - 1); i >= nwords; --i)
|
|
{
|
|
a->array[i] = a->array[i - nwords];
|
|
}
|
|
|
|
/* Zero pad shifted words. */
|
|
|
|
for (; i >= 0; --i)
|
|
{
|
|
a->array[i] = 0;
|
|
}
|
|
}
|
|
|
|
static void lshift_one_bit(FAR struct bn *a)
|
|
{
|
|
int i;
|
|
|
|
require(a, "a is null");
|
|
|
|
for (i = (BN_ARRAY_SIZE - 1); i > 0; --i)
|
|
{
|
|
a->array[i] = (a->array[i] << 1) |
|
|
(a->array[i - 1] >> ((8 * WORD_SIZE) - 1));
|
|
}
|
|
|
|
a->array[0] <<= 1;
|
|
}
|
|
|
|
static void rshift_one_bit(FAR struct bn *a)
|
|
{
|
|
int i;
|
|
|
|
require(a, "a is null");
|
|
|
|
for (i = 0; i < (BN_ARRAY_SIZE - 1); ++i)
|
|
{
|
|
a->array[i] = (a->array[i] >> 1) |
|
|
(a->array[i + 1] << ((8 * WORD_SIZE) - 1));
|
|
}
|
|
|
|
a->array[BN_ARRAY_SIZE - 1] >>= 1;
|
|
}
|
|
|
|
static
|
|
void bignum_add_sub(struct bn *a, struct bn *b, struct bn *c, int flip)
|
|
{
|
|
int cmp;
|
|
int s;
|
|
|
|
require(a, "a is null");
|
|
require(b, "b is null");
|
|
require(c, "c is null");
|
|
|
|
s = a->s;
|
|
if (a->s * b->s * flip < 0)
|
|
{
|
|
cmp = bignum_cmp_abs(a, b);
|
|
if (cmp >= 0)
|
|
{
|
|
bignum_sub_abs(a, b, c);
|
|
c->s = cmp == 0 ? 1 : s;
|
|
}
|
|
else
|
|
{
|
|
bignum_sub_abs(b, a, c);
|
|
c->s = -s;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bignum_add_abs(a, b, c);
|
|
c->s = s;
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/* Public / Exported functions. */
|
|
|
|
void bignum_init(FAR struct bn *n)
|
|
{
|
|
int i;
|
|
|
|
require(n, "n is null");
|
|
|
|
for (i = 0; i < BN_ARRAY_SIZE; ++i)
|
|
{
|
|
n->array[i] = 0;
|
|
}
|
|
|
|
n->s = 1;
|
|
}
|
|
|
|
void bignum_from_int(FAR struct bn *n, DTYPE_TMP i)
|
|
{
|
|
require(n, "n is null");
|
|
|
|
bignum_init(n);
|
|
|
|
/* Endianness issue if machine is not little-endian? */
|
|
|
|
#ifdef WORD_SIZE
|
|
# if (WORD_SIZE == 1)
|
|
n->array[0] = (i & 0x000000ff);
|
|
n->array[1] = (i & 0x0000ff00) >> 8;
|
|
n->array[2] = (i & 0x00ff0000) >> 16;
|
|
n->array[3] = (i & 0xff000000) >> 24;
|
|
# elif (WORD_SIZE == 2)
|
|
n->array[0] = (i & 0x0000ffff);
|
|
n->array[1] = (i & 0xffff0000) >> 16;
|
|
# elif (WORD_SIZE == 4)
|
|
n->array[0] = i;
|
|
DTYPE_TMP num_32 = 32;
|
|
DTYPE_TMP tmp = i >> num_32; /* bit-shift with U64 operands to force
|
|
* 64-bit results */
|
|
n->array[1] = tmp;
|
|
# endif
|
|
#endif
|
|
}
|
|
|
|
int bignum_to_int(FAR struct bn *n)
|
|
{
|
|
int ret = 0;
|
|
|
|
require(n, "n is null");
|
|
|
|
/* Endianness issue if machine is not little-endian? */
|
|
|
|
#if (WORD_SIZE == 1)
|
|
ret += n->array[0];
|
|
ret += n->array[1] << 8;
|
|
ret += n->array[2] << 16;
|
|
ret += n->array[3] << 24;
|
|
#elif (WORD_SIZE == 2)
|
|
ret += n->array[0];
|
|
ret += n->array[1] << 16;
|
|
#elif (WORD_SIZE == 4)
|
|
ret += n->array[0];
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
void bignum_from_string(FAR struct bn *n, FAR char *str, int nbytes)
|
|
{
|
|
DTYPE tmp; /* DTYPE is defined in bn.h -
|
|
* uint{8,16,32,64}_t */
|
|
int i = nbytes - (2 * WORD_SIZE); /* index into string */
|
|
int j = 0; /* index into array */
|
|
|
|
require(n, "n is null");
|
|
require(str, "str is null");
|
|
require(nbytes > 0, "nbytes must be positive");
|
|
require((nbytes & 1) == 0,
|
|
"string format must be in hex -> equal number of bytes");
|
|
require((nbytes % (sizeof(DTYPE) * 2)) == 0,
|
|
"string length must be a multiple of (sizeof(DTYPE) * 2) characters");
|
|
|
|
bignum_init(n);
|
|
|
|
/* reading last hex-byte "MSB" from string first -> big endian
|
|
* MSB ~= most significant byte / block ? :)
|
|
*/
|
|
|
|
while (i >= 0)
|
|
{
|
|
tmp = 0;
|
|
sscanf(&str[i], SSCANF_FORMAT_STR, &tmp);
|
|
n->array[j] = tmp;
|
|
i -= (2 * WORD_SIZE); /* step WORD_SIZE hex-byte(s) back in
|
|
* the string. */
|
|
j += 1; /* step one element forward in the
|
|
* array. */
|
|
}
|
|
}
|
|
|
|
void bignum_to_string(FAR struct bn *n, FAR char *str, int nbytes)
|
|
{
|
|
int j = BN_ARRAY_SIZE - 1; /* index into array - reading "MSB" first
|
|
* -> big-endian */
|
|
int i = 0; /* index into string representation. */
|
|
|
|
require(n, "n is null");
|
|
require(str, "str is null");
|
|
require(nbytes > 0, "nbytes must be positive");
|
|
require((nbytes & 1) == 0,
|
|
"string format must be in hex -> equal number of bytes");
|
|
|
|
/* reading last array-element "MSB" first -> big endian */
|
|
|
|
while ((j >= 0) && (nbytes > (i + 1)))
|
|
{
|
|
sprintf(&str[i], SPRINTF_FORMAT_STR, n->array[j]);
|
|
i += (2 * WORD_SIZE); /* step WORD_SIZE hex-byte(s) forward in the
|
|
* string. */
|
|
j -= 1; /* step one element back in the array. */
|
|
}
|
|
|
|
/* Count leading zeros: */
|
|
|
|
j = 0;
|
|
while (str[j] == '0')
|
|
{
|
|
j += 1;
|
|
}
|
|
|
|
/* Move string j places ahead, effectively skipping leading zeros */
|
|
|
|
for (i = 0; i < (nbytes - j); ++i)
|
|
{
|
|
str[i] = str[i + j];
|
|
}
|
|
|
|
/* Zero-terminate string */
|
|
|
|
str[i] = 0;
|
|
}
|
|
|
|
void bignum_dec(FAR struct bn *n)
|
|
{
|
|
DTYPE tmp; /* copy of n */
|
|
DTYPE res;
|
|
int i;
|
|
|
|
require(n, "n is null");
|
|
|
|
for (i = 0; i < BN_ARRAY_SIZE; ++i)
|
|
{
|
|
tmp = n->array[i];
|
|
res = tmp - 1;
|
|
n->array[i] = res;
|
|
|
|
if (!(res > tmp))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void bignum_inc(FAR struct bn *n)
|
|
{
|
|
DTYPE res;
|
|
DTYPE_TMP tmp; /* copy of n */
|
|
int i;
|
|
|
|
require(n, "n is null");
|
|
|
|
for (i = 0; i < BN_ARRAY_SIZE; ++i)
|
|
{
|
|
tmp = n->array[i];
|
|
res = tmp + 1;
|
|
n->array[i] = res;
|
|
|
|
if (res > tmp)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void bignum_add(FAR struct bn *a, FAR struct bn *b, FAR struct bn *c)
|
|
{
|
|
bignum_add_sub(a, b, c, 1);
|
|
}
|
|
|
|
void bignum_add_abs(FAR struct bn *a, FAR struct bn *b, FAR struct bn *c)
|
|
{
|
|
DTYPE_TMP tmp;
|
|
int carry = 0;
|
|
int i;
|
|
|
|
require(a, "a is null");
|
|
require(b, "b is null");
|
|
require(c, "c is null");
|
|
|
|
for (i = 0; i < BN_ARRAY_SIZE; ++i)
|
|
{
|
|
tmp = (DTYPE_TMP)a->array[i] + b->array[i] + carry;
|
|
carry = (tmp > MAX_VAL);
|
|
c->array[i] = (tmp & MAX_VAL);
|
|
}
|
|
}
|
|
|
|
void bignum_sub(FAR struct bn *a, FAR struct bn *b, FAR struct bn *c)
|
|
{
|
|
bignum_add_sub(a, b, c, -1);
|
|
}
|
|
|
|
void bignum_sub_abs(FAR struct bn *a, FAR struct bn *b, FAR struct bn *c)
|
|
{
|
|
DTYPE_TMP res;
|
|
DTYPE_TMP tmp1;
|
|
DTYPE_TMP tmp2;
|
|
int borrow = 0;
|
|
int i;
|
|
|
|
require(a, "a is null");
|
|
require(b, "b is null");
|
|
require(c, "c is null");
|
|
|
|
for (i = 0; i < BN_ARRAY_SIZE; ++i)
|
|
{
|
|
tmp1 = (DTYPE_TMP)a->array[i] + (MAX_VAL + 1); /* + number_base */
|
|
tmp2 = (DTYPE_TMP)b->array[i] + borrow;
|
|
res = (tmp1 - tmp2);
|
|
|
|
/* "modulo number_base" == "% (number_base - 1)"
|
|
* if number_base is 2^N
|
|
*/
|
|
|
|
c->array[i] = (DTYPE)(res & MAX_VAL);
|
|
borrow = (res <= MAX_VAL);
|
|
}
|
|
}
|
|
|
|
void bignum_mul(FAR struct bn *a, FAR struct bn *b, FAR struct bn *c)
|
|
{
|
|
struct bn row;
|
|
struct bn tmp;
|
|
int i;
|
|
int j;
|
|
|
|
require(a, "a is null");
|
|
require(b, "b is null");
|
|
require(c, "c is null");
|
|
|
|
bignum_init(c);
|
|
|
|
for (i = 0; i < BN_ARRAY_SIZE; ++i)
|
|
{
|
|
bignum_init(&row);
|
|
|
|
for (j = 0; j < BN_ARRAY_SIZE; ++j)
|
|
{
|
|
if (i + j < BN_ARRAY_SIZE)
|
|
{
|
|
bignum_init(&tmp);
|
|
DTYPE_TMP intermediate =
|
|
((DTYPE_TMP)a->array[i] * (DTYPE_TMP)b->array[j]);
|
|
bignum_from_int(&tmp, intermediate);
|
|
lshift_word(&tmp, i + j);
|
|
bignum_add_abs(&tmp, &row, &row);
|
|
}
|
|
}
|
|
|
|
bignum_add_abs(c, &row, c);
|
|
}
|
|
|
|
if (bignum_is_zero(c) != 0)
|
|
{
|
|
c->s = 1;
|
|
}
|
|
else
|
|
{
|
|
c->s = a->s * b->s;
|
|
}
|
|
}
|
|
|
|
void bignum_div(FAR struct bn *a, FAR struct bn *b, FAR struct bn *c)
|
|
{
|
|
struct bn current;
|
|
struct bn denom;
|
|
struct bn tmp;
|
|
const DTYPE_TMP half_max = 1 + (DTYPE_TMP)(MAX_VAL / 2);
|
|
bool overflow = false;
|
|
|
|
require(a, "a is null");
|
|
require(b, "b is null");
|
|
require(c, "c is null");
|
|
|
|
bignum_from_int(¤t, 1); /* int current = 1; */
|
|
bignum_assign(&denom, b); /* denom = b */
|
|
bignum_assign(&tmp, a); /* tmp = a */
|
|
|
|
while (bignum_cmp_abs(&denom, &tmp) != LARGER) /* while (denom <= a) { */
|
|
{
|
|
if (denom.array[BN_ARRAY_SIZE - 1] >= half_max)
|
|
{
|
|
overflow = true;
|
|
break;
|
|
}
|
|
|
|
lshift_one_bit(¤t); /* current <<= 1; */
|
|
lshift_one_bit(&denom); /* denom <<= 1; */
|
|
}
|
|
|
|
if (!overflow)
|
|
{
|
|
rshift_one_bit(&denom); /* denom >>= 1; */
|
|
rshift_one_bit(¤t); /* current >>= 1; */
|
|
}
|
|
|
|
bignum_init(c); /* int answer = 0; */
|
|
|
|
while (!bignum_is_zero(¤t)) /* while (current != 0) */
|
|
{
|
|
/* if (dividend >= denom) */
|
|
|
|
if (bignum_cmp_abs(&tmp, &denom) != SMALLER)
|
|
{
|
|
bignum_sub_abs(&tmp, &denom, &tmp); /* dividend -= denom; */
|
|
bignum_or(c, ¤t, c); /* answer |= current; */
|
|
}
|
|
|
|
rshift_one_bit(¤t); /* current >>= 1; */
|
|
rshift_one_bit(&denom); /* denom >>= 1; */
|
|
}
|
|
|
|
c->s = a->s * b->s;
|
|
|
|
/* return answer; */
|
|
}
|
|
|
|
void bignum_lshift(FAR struct bn *a, FAR struct bn *b, int nbits)
|
|
{
|
|
const int nbits_pr_word = (WORD_SIZE * 8);
|
|
int nwords = nbits / nbits_pr_word;
|
|
|
|
require(a, "a is null");
|
|
require(b, "b is null");
|
|
require(nbits >= 0, "no negative shifts");
|
|
|
|
bignum_assign(b, a);
|
|
|
|
/* Handle shift in multiples of word-size */
|
|
|
|
if (nwords != 0)
|
|
{
|
|
lshift_word(b, nwords);
|
|
nbits -= (nwords * nbits_pr_word);
|
|
}
|
|
|
|
if (nbits != 0)
|
|
{
|
|
int i;
|
|
for (i = (BN_ARRAY_SIZE - 1); i > 0; --i)
|
|
{
|
|
b->array[i] = (b->array[i] << nbits) |
|
|
(b->array[i - 1] >> ((8 * WORD_SIZE) - nbits));
|
|
}
|
|
|
|
b->array[i] <<= nbits;
|
|
}
|
|
}
|
|
|
|
void bignum_rshift(FAR struct bn *a, FAR struct bn *b, int nbits)
|
|
{
|
|
const int nbits_pr_word = (WORD_SIZE * 8);
|
|
int nwords = nbits / nbits_pr_word;
|
|
|
|
require(a, "a is null");
|
|
require(b, "b is null");
|
|
require(nbits >= 0, "no negative shifts");
|
|
|
|
bignum_assign(b, a);
|
|
|
|
/* Handle shift in multiples of word-size */
|
|
|
|
if (nwords != 0)
|
|
{
|
|
rshift_word(b, nwords);
|
|
nbits -= (nwords * nbits_pr_word);
|
|
}
|
|
|
|
if (nbits != 0)
|
|
{
|
|
int i;
|
|
for (i = 0; i < (BN_ARRAY_SIZE - 1); ++i)
|
|
{
|
|
b->array[i] = (b->array[i] >> nbits) |
|
|
(b->array[i + 1] << ((8 * WORD_SIZE) - nbits));
|
|
}
|
|
|
|
b->array[i] >>= nbits;
|
|
}
|
|
}
|
|
|
|
void bignum_mod(FAR struct bn *a, FAR struct bn *b, FAR struct bn *c)
|
|
{
|
|
/* Take divmod and throw away div part */
|
|
|
|
struct bn tmp;
|
|
|
|
require(a, "a is null");
|
|
require(b, "b is null");
|
|
require(c, "c is null");
|
|
|
|
bignum_divmod(a, b, &tmp, c);
|
|
}
|
|
|
|
void bignum_divmod(FAR struct bn *a, FAR struct bn *b,
|
|
FAR struct bn *c, FAR struct bn *d)
|
|
{
|
|
/* Puts a%b in d
|
|
* and a/b in c
|
|
*
|
|
* mod(a,b) = a - ((a / b) * b)
|
|
*
|
|
* example:
|
|
* mod(8, 3) = 8 - ((8 / 3) * 3) = 2
|
|
*/
|
|
|
|
struct bn tmp;
|
|
|
|
require(a, "a is null");
|
|
require(b, "b is null");
|
|
require(c, "c is null");
|
|
|
|
/* c = (a / b) */
|
|
|
|
bignum_div(a, b, c);
|
|
|
|
/* tmp = (c * b) */
|
|
|
|
bignum_mul(c, b, &tmp);
|
|
|
|
/* c = a - tmp */
|
|
|
|
bignum_sub(a, &tmp, d);
|
|
|
|
while (d->s < 0)
|
|
{
|
|
bignum_add(d, b, d);
|
|
}
|
|
}
|
|
|
|
void bignum_and(FAR struct bn *a, FAR struct bn *b, FAR struct bn *c)
|
|
{
|
|
int i;
|
|
|
|
require(a, "a is null");
|
|
require(b, "b is null");
|
|
require(c, "c is null");
|
|
|
|
for (i = 0; i < BN_ARRAY_SIZE; ++i)
|
|
{
|
|
c->array[i] = (a->array[i] & b->array[i]);
|
|
}
|
|
}
|
|
|
|
void bignum_or(FAR struct bn *a, FAR struct bn *b, FAR struct bn *c)
|
|
{
|
|
int i;
|
|
|
|
require(a, "a is null");
|
|
require(b, "b is null");
|
|
require(c, "c is null");
|
|
|
|
for (i = 0; i < BN_ARRAY_SIZE; ++i)
|
|
{
|
|
c->array[i] = (a->array[i] | b->array[i]);
|
|
}
|
|
}
|
|
|
|
void bignum_xor(FAR struct bn *a, FAR struct bn *b, FAR struct bn *c)
|
|
{
|
|
int i;
|
|
|
|
require(a, "a is null");
|
|
require(b, "b is null");
|
|
require(c, "c is null");
|
|
|
|
for (i = 0; i < BN_ARRAY_SIZE; ++i)
|
|
{
|
|
c->array[i] = (a->array[i] ^ b->array[i]);
|
|
}
|
|
}
|
|
|
|
int bignum_cmp(FAR struct bn *a, FAR struct bn *b)
|
|
{
|
|
int i = BN_ARRAY_SIZE;
|
|
|
|
require(a, "a is null");
|
|
require(b, "b is null");
|
|
|
|
if (a->s > 0 && b->s < 0)
|
|
{
|
|
return LARGER;
|
|
}
|
|
|
|
if (b->s > 0 && a->s < 0)
|
|
{
|
|
return SMALLER;
|
|
}
|
|
|
|
do
|
|
{
|
|
i -= 1; /* Decrement first, to start with last array element */
|
|
if (a->array[i] > b->array[i])
|
|
{
|
|
return a->s;
|
|
}
|
|
else if (a->array[i] < b->array[i])
|
|
{
|
|
return -a->s;
|
|
}
|
|
}
|
|
while (i != 0);
|
|
|
|
return EQUAL;
|
|
}
|
|
|
|
int bignum_cmp_abs(FAR struct bn *a, FAR struct bn *b)
|
|
{
|
|
int i = BN_ARRAY_SIZE;
|
|
|
|
require(a, "a is null");
|
|
require(b, "b is null");
|
|
|
|
do
|
|
{
|
|
i -= 1; /* Decrement first, to start with last array element */
|
|
if (a->array[i] > b->array[i])
|
|
{
|
|
return LARGER;
|
|
}
|
|
else if (a->array[i] < b->array[i])
|
|
{
|
|
return SMALLER;
|
|
}
|
|
}
|
|
while (i != 0);
|
|
|
|
return EQUAL;
|
|
}
|
|
|
|
int bignum_is_zero(FAR struct bn *n)
|
|
{
|
|
int i;
|
|
|
|
require(n, "n is null");
|
|
|
|
for (i = 0; i < BN_ARRAY_SIZE; ++i)
|
|
{
|
|
if (n->array[i])
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void bignum_pow(FAR struct bn *a, FAR struct bn *b, FAR struct bn *c)
|
|
{
|
|
struct bn tmp;
|
|
|
|
require(a, "a is null");
|
|
require(b, "b is null");
|
|
require(c, "c is null");
|
|
|
|
bignum_init(c);
|
|
|
|
if (bignum_cmp(b, c) == EQUAL)
|
|
{
|
|
/* Return 1 when exponent is 0 -- n^0 = 1 */
|
|
|
|
bignum_inc(c);
|
|
}
|
|
else
|
|
{
|
|
struct bn bcopy;
|
|
bignum_assign(&bcopy, b);
|
|
|
|
/* Copy a -> tmp */
|
|
|
|
bignum_assign(&tmp, a);
|
|
|
|
bignum_dec(&bcopy);
|
|
|
|
/* Begin summing products: */
|
|
|
|
while (!bignum_is_zero(&bcopy))
|
|
{
|
|
/* c = tmp * tmp */
|
|
|
|
bignum_mul(&tmp, a, c);
|
|
|
|
/* Decrement b by one */
|
|
|
|
bignum_dec(&bcopy);
|
|
|
|
bignum_assign(&tmp, c);
|
|
}
|
|
|
|
/* c = tmp */
|
|
|
|
bignum_assign(c, &tmp);
|
|
}
|
|
}
|
|
|
|
void bignum_isqrt(FAR struct bn *a, FAR struct bn *b)
|
|
{
|
|
struct bn low;
|
|
struct bn high;
|
|
struct bn mid;
|
|
struct bn tmp;
|
|
|
|
require(a, "a is null");
|
|
require(b, "b is null");
|
|
|
|
bignum_init(&low);
|
|
bignum_assign(&high, a);
|
|
bignum_rshift(&high, &mid, 1);
|
|
bignum_inc(&mid);
|
|
|
|
while (bignum_cmp(&high, &low) > 0)
|
|
{
|
|
bignum_mul(&mid, &mid, &tmp);
|
|
if (bignum_cmp(&tmp, a) > 0)
|
|
{
|
|
bignum_assign(&high, &mid);
|
|
bignum_dec(&high);
|
|
}
|
|
else
|
|
{
|
|
bignum_assign(&low, &mid);
|
|
}
|
|
|
|
bignum_sub_abs(&high, &low, &mid);
|
|
rshift_one_bit(&mid);
|
|
bignum_add_abs(&low, &mid, &mid);
|
|
bignum_inc(&mid);
|
|
}
|
|
|
|
bignum_assign(b, &low);
|
|
}
|
|
|
|
void bignum_assign(FAR struct bn *dst, FAR struct bn *src)
|
|
{
|
|
int i;
|
|
|
|
require(dst, "dst is null");
|
|
require(src, "src is null");
|
|
|
|
for (i = 0; i < BN_ARRAY_SIZE; ++i)
|
|
{
|
|
dst->array[i] = src->array[i];
|
|
}
|
|
|
|
dst->s = src->s;
|
|
}
|
|
|
|
void pow_mod_faster(FAR struct bn *a, FAR struct bn *b,
|
|
FAR struct bn *n, FAR struct bn *res)
|
|
{
|
|
struct bn tmpa;
|
|
struct bn tmpb;
|
|
struct bn tmp;
|
|
bignum_assign(&tmpa, a);
|
|
bignum_assign(&tmpb, b);
|
|
|
|
bignum_from_int(res, 1); /* r = 1 */
|
|
|
|
while (1)
|
|
{
|
|
/* if (b % 2) */
|
|
|
|
if (tmpb.array[0] & 1)
|
|
{
|
|
bignum_mul(res, &tmpa, &tmp); /* r = r * a % m */
|
|
bignum_mod(&tmp, n, res);
|
|
}
|
|
|
|
bignum_rshift(&tmpb, &tmp, 1); /* b /= 2 */
|
|
bignum_assign(&tmpb, &tmp);
|
|
|
|
if (bignum_is_zero(&tmpb))
|
|
{
|
|
break;
|
|
}
|
|
|
|
bignum_mul(&tmpa, &tmpa, &tmp);
|
|
bignum_mod(&tmp, n, &tmpa);
|
|
}
|
|
}
|
|
|
|
int bignum_lsb(FAR struct bn *a)
|
|
{
|
|
int i;
|
|
int j;
|
|
int count;
|
|
|
|
require(a, "n is null");
|
|
|
|
for (i = 0, count = 0; i < BN_ARRAY_SIZE; i++)
|
|
{
|
|
for (j = 0; j < WORD_SIZE * 8; j++, count++)
|
|
{
|
|
if (((a->array[i] >> j) & 1) != 0)
|
|
{
|
|
return count;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void bignum_gcd(FAR struct bn *a, FAR struct bn *b, FAR struct bn *g)
|
|
{
|
|
size_t lz;
|
|
size_t lzt;
|
|
struct bn ta;
|
|
struct bn tb;
|
|
|
|
require(a, "a is null");
|
|
require(b, "b is null");
|
|
require(g, "g is null");
|
|
|
|
bignum_init(&ta);
|
|
bignum_init(&tb);
|
|
|
|
bignum_assign(&ta, a);
|
|
bignum_assign(&tb, b);
|
|
|
|
lz = bignum_lsb(&ta);
|
|
lzt = bignum_lsb(&tb);
|
|
|
|
if (lzt == 0 && (tb.array[0] & 1) == 0)
|
|
{
|
|
bignum_assign(g, a);
|
|
return;
|
|
}
|
|
|
|
if (lzt < lz)
|
|
{
|
|
lz = lzt;
|
|
}
|
|
|
|
ta.s = tb.s = 1;
|
|
|
|
while (bignum_is_zero(&ta) != 1)
|
|
{
|
|
bignum_rshift(&ta, &ta, bignum_lsb(&ta));
|
|
bignum_rshift(&tb, &tb, bignum_lsb(&tb));
|
|
|
|
if (bignum_cmp(&ta, &tb) >= 0)
|
|
{
|
|
bignum_sub(&ta, &tb, &ta);
|
|
bignum_rshift(&ta, &ta, 1);
|
|
}
|
|
else
|
|
{
|
|
bignum_sub(&tb, &ta, &tb);
|
|
bignum_rshift(&tb, &tb, 1);
|
|
}
|
|
}
|
|
|
|
bignum_lshift(&tb, &tb, lz);
|
|
bignum_assign(g, &tb);
|
|
}
|
|
|
|
int bignum_inv_mod(FAR struct bn *a, FAR struct bn *n, FAR struct bn *c)
|
|
{
|
|
struct bn g;
|
|
struct bn ta;
|
|
struct bn tu;
|
|
struct bn u1;
|
|
struct bn u2;
|
|
struct bn tb;
|
|
struct bn tv;
|
|
struct bn v1;
|
|
struct bn v2;
|
|
struct bn num_one;
|
|
struct bn num_zero;
|
|
|
|
require(a, "a is null");
|
|
require(n, "n is null");
|
|
require(c, "c is null");
|
|
|
|
bignum_init(&g);
|
|
bignum_init(&ta);
|
|
bignum_init(&tu);
|
|
bignum_init(&u1);
|
|
bignum_init(&u2);
|
|
bignum_init(&tb);
|
|
bignum_init(&tv);
|
|
bignum_init(&v1);
|
|
bignum_init(&v2);
|
|
bignum_init(&num_one);
|
|
bignum_init(&num_zero);
|
|
bignum_from_int(&num_one, 1);
|
|
|
|
if (bignum_cmp(n, &num_one) <= 0)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
bignum_gcd(a, n, &g);
|
|
|
|
if (bignum_cmp(&g, &num_one) != 0)
|
|
{
|
|
return -EFAULT;
|
|
}
|
|
|
|
bignum_mod(a, n, &ta);
|
|
bignum_assign(&tu, &ta);
|
|
bignum_assign(&tb, n);
|
|
bignum_assign(&tv, n);
|
|
bignum_from_int(&u1, 1);
|
|
bignum_from_int(&u2, 0);
|
|
bignum_from_int(&v1, 0);
|
|
bignum_from_int(&v2, 1);
|
|
|
|
do
|
|
{
|
|
while ((tu.array[0] & 1) == 0)
|
|
{
|
|
bignum_rshift(&tu, &tu, 1);
|
|
|
|
if ((u1.array[0] & 1) != 0 || (u2.array[0] & 1) != 0)
|
|
{
|
|
bignum_add(&u1, &tb, &u1);
|
|
bignum_sub(&u2, &ta, &u2);
|
|
}
|
|
|
|
bignum_rshift(&u1, &u1, 1);
|
|
bignum_rshift(&u2, &u2, 1);
|
|
}
|
|
|
|
while ((tv.array[0] & 1) == 0)
|
|
{
|
|
bignum_rshift(&tv, &tv, 1);
|
|
if ((v1.array[0] & 1) != 0 || (v2.array[0] & 1) != 0)
|
|
{
|
|
bignum_add(&v1, &tb, &v1);
|
|
bignum_sub(&v2, &ta, &v2);
|
|
}
|
|
|
|
bignum_rshift(&v1, &v1, 1);
|
|
bignum_rshift(&v2, &v2, 1);
|
|
}
|
|
|
|
if (bignum_cmp(&tu, &tv) >= 0)
|
|
{
|
|
bignum_sub(&tu, &tv, &tu);
|
|
bignum_sub(&u1, &v1, &u1);
|
|
bignum_sub(&u2, &v2, &u2);
|
|
}
|
|
else
|
|
{
|
|
bignum_sub(&tv, &tu, &tv);
|
|
bignum_sub(&v1, &u1, &v1);
|
|
bignum_sub(&v2, &u2, &v2);
|
|
}
|
|
}
|
|
while (bignum_is_zero(&tu) != 1);
|
|
|
|
while (bignum_cmp(&v1, &num_zero) < 0)
|
|
{
|
|
bignum_add(&v1, n, &v1);
|
|
}
|
|
|
|
while (bignum_cmp(&v1, n) >= 0)
|
|
{
|
|
bignum_sub(&v1, n, &v1);
|
|
}
|
|
|
|
bignum_assign(c, &v1);
|
|
return 0;
|
|
}
|