Files
wolfssl/wolfcrypt/src/sp_int.c

751 lines
16 KiB
C

/* sp_int.c
*
* Copyright (C) 2006-2017 wolfSSL Inc.
*
* This file is part of wolfSSL.
*
* wolfSSL 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
* (at your option) any later version.
*
* wolfSSL 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-1335, USA
*/
/* Implementation by Sean Parkinson. */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <wolfssl/wolfcrypt/settings.h>
#include <wolfssl/wolfcrypt/error-crypt.h>
#ifdef NO_INLINE
#include <wolfssl/wolfcrypt/misc.h>
#else
#define WOLFSSL_MISC_INCLUDED
#include <wolfcrypt/src/misc.c>
#endif
#ifdef WOLFSSL_SP_MATH
#include <wolfssl/wolfcrypt/sp_int.h>
/* Initialize the big number to be zero.
*
* a SP integer.
* returns MP_OKAY always.
*/
int sp_init(sp_int* a)
{
a->used = 0;
a->size = SP_INT_DIGITS;
return MP_OKAY;
}
#if !defined(WOLFSSL_RSA_PUBLIC_ONLY) || (!defined(NO_DH) || defined(HAVE_ECC))
/* Initialize up to six big numbers to be zero.
*
* a SP integer.
* b SP integer.
* c SP integer.
* d SP integer.
* e SP integer.
* f SP integer.
* returns MP_OKAY always.
*/
int sp_init_multi(sp_int* a, sp_int* b, sp_int* c, sp_int* d, sp_int* e,
sp_int* f)
{
if (a != NULL) {
a->used = 0;
a->size = SP_INT_DIGITS;
}
if (b != NULL) {
b->used = 0;
b->size = SP_INT_DIGITS;
}
if (c != NULL) {
c->used = 0;
c->size = SP_INT_DIGITS;
}
if (d != NULL) {
d->used = 0;
d->size = SP_INT_DIGITS;
}
if (e != NULL) {
e->used = 0;
e->size = SP_INT_DIGITS;
}
if (f != NULL) {
f->used = 0;
f->size = SP_INT_DIGITS;
}
return MP_OKAY;
}
#endif
/* Clear the data from the big number and set to zero.
*
* a SP integer.
*/
void sp_clear(sp_int* a)
{
if (a != NULL) {
int i;
for (i=0; i<a->used; i++)
a->dp[i] = 0;
a->used = 0;
}
}
/* Calculate the number of 8-bit values required to represent the big number.
*
* a SP integer.
* returns the count.
*/
int sp_unsigned_bin_size(sp_int* a)
{
int size = sp_count_bits(a);
return (size + 7) / 8;
}
/* Convert a number as an array of bytes in big-endian format to a big number.
*
* a SP integer.
* in Array of bytes.
* inSz Number of data bytes in array.
* returns MP_OKAY always.
*/
int sp_read_unsigned_bin(sp_int* a, const byte* in, word32 inSz)
{
int i, j = 0, s = 0;
a->dp[0] = 0;
for (i = inSz-1; i >= 0; i--) {
a->dp[j] |= ((sp_int_digit)in[i]) << s;
if (s == DIGIT_BIT - 8) {
a->dp[++j] = 0;
s = 0;
}
else if (s > DIGIT_BIT - 8) {
s = DIGIT_BIT - s;
if (j + 1 >= a->size)
break;
a->dp[++j] = in[i] >> s;
s = 8 - s;
}
else
s += 8;
}
a->used = j + 1;
if (a->dp[j] == 0)
a->used--;
for (j++; j < a->size; j++)
a->dp[j] = 0;
return MP_OKAY;
}
#ifdef HAVE_ECC
/* Convert a number as string in big-endian format to a big number.
* Only supports base-16 (hexadecimal).
* Negative values not supported.
*
* a SP integer.
* in NUL terminated string.
* radix Number of values in a digit.
* returns BAD_FUNC_ARG when radix not supported or value is negative, MP_VAL
* when a character is not valid and MP_OKAY otherwise.
*/
int sp_read_radix(sp_int* a, const char* in, int radix)
{
int i, j, k;
char ch;
if (radix != 16)
return BAD_FUNC_ARG;
if (*in == '-') {
return BAD_FUNC_ARG;
}
j = 0;
k = 0;
a->dp[0] = 0;
for (i = (int)(XSTRLEN(in) - 1); i >= 0; i--) {
ch = in[i];
if (ch >= '0' && ch <= '9')
ch -= '0';
else if (ch >= 'A' && ch <= 'F')
ch -= 'A' - 10;
else if (ch >= 'a' && ch <= 'f')
ch -= 'a' - 10;
else
return MP_VAL;
a->dp[k] |= ((sp_int_digit)ch) << j;
j += 4;
if (j == DIGIT_BIT && k < SP_INT_DIGITS)
a->dp[++k] = 0;
j &= DIGIT_BIT - 1;
}
a->used = k + 1;
if (a->dp[k] == 0)
a->used--;
for (k++; k < a->size; k++)
a->dp[k] = 0;
return MP_OKAY;
}
#endif
/* Compare two big numbers.
*
* a SP integer.
* b SP integer.
* returns MP_GT if a is greater than b, MP_LT if a is less than b and MP_EQ
* when a equals b.
*/
int sp_cmp(sp_int* a, sp_int* b)
{
int i;
if (a->used > b->used)
return MP_GT;
else if (a->used < b->used)
return MP_LT;
for (i = a->used - 1; i >= 0; i--) {
if (a->dp[i] > b->dp[i])
return MP_GT;
else if (a->dp[i] < b->dp[i])
return MP_LT;
}
return MP_EQ;
}
/* Count the number of bits in the big number.
*
* a SP integer.
* returns the number of bits.
*/
int sp_count_bits(sp_int* a)
{
int r = 0;
sp_int_digit d;
r = a->used - 1;
while (r >= 0 && a->dp[r] == 0)
r--;
if (r < 0)
r = 0;
else {
d = a->dp[r];
r *= DIGIT_BIT;
while (d != 0) {
r++;
d >>= 1;
}
}
return r;
}
/* Determine if the most significant byte of the encoded big number as the top
* bit set.
*
* a SP integer.
* returns 1 when the top bit is set and 0 otherwise.
*/
int sp_leading_bit(sp_int* a)
{
int bit = 0;
sp_int_digit d;
if (a->used > 0) {
d = a->dp[a->used - 1];
while (d > (sp_int_digit)0xff)
d >>= 8;
bit = (int)(d >> 7);
}
return bit;
}
#if !defined(WOLFSSL_RSA_VERIFY_ONLY) || (!defined(NO_DH) || defined(HAVE_ECC))
/* Convert the big number to an array of bytes in big-endian format.
* The array must be large enough for encoded number - use mp_unsigned_bin_size
* to calculate the number of bytes required.
*
* a SP integer.
* out Array to put encoding into.
* returns MP_OKAY always.
*/
int sp_to_unsigned_bin(sp_int* a, byte* out)
{
int i, j, b;
j = sp_unsigned_bin_size(a) - 1;
for (i=0; j>=0; i++) {
for (b = 0; b < SP_WORD_SIZE; b += 8) {
out[j--] = a->dp[i] >> b;
if (j < 0)
break;
}
}
return MP_OKAY;
}
#endif
/* Convert the big number to an array of bytes in big-endian format.
* The array must be large enough for encoded number - use mp_unsigned_bin_size
* to calculate the number of bytes required.
* Front-pads the output array with zeros make number the size of the array.
*
* a SP integer.
* out Array to put encoding into.
* outSz Size of the array.
* returns MP_OKAY always.
*/
int sp_to_unsigned_bin_len(sp_int* a, byte* out, int outSz)
{
int i, j, b;
j = outSz - 1;
for (i=0; j>=0; i++) {
for (b = 0; b < SP_WORD_SIZE; b += 8) {
out[j--] = a->dp[i] >> b;
if (j < 0)
break;
}
}
return MP_OKAY;
}
#if !defined(WOLFSSL_RSA_PUBLIC_ONLY) || (!defined(NO_DH) || defined(HAVE_ECC))
/* Ensure the data in the big number is zeroed.
*
* a SP integer.
*/
void sp_forcezero(sp_int* a)
{
ForceZero(a->dp, a->used * sizeof(sp_int_digit));
a->used = 0;
}
#endif
#if !defined(WOLFSSL_RSA_VERIFY_ONLY) || (!defined(NO_DH) || defined(HAVE_ECC))
/* Copy value of big number a into b.
*
* a SP integer.
* b SP integer.
* returns MP_OKAY always.
*/
int sp_copy(sp_int* a, sp_int* b)
{
if (a != b) {
XMEMCPY(b->dp, a->dp, a->used * sizeof(sp_int_digit));
b->used = a->used;
}
return MP_OKAY;
}
#endif
/* Set the big number to be the value of the digit.
*
* a SP integer.
* d Digit to be set.
* returns MP_OKAY always.
*/
int sp_set(sp_int* a, sp_int_digit d)
{
a->dp[0] = d;
a->used = 1;
return MP_OKAY;
}
#if defined(WC_MP_TO_RADIX) || !defined(NO_DH) || defined(HAVE_ECC)
/* Checks whether the value of the big number is zero.
*
* a SP integer.
* returns 1 when value is zero and 0 otherwise.
*/
int sp_iszero(sp_int* a)
{
return a->used == 0;
}
#endif
#if !defined(WOLFSSL_RSA_VERIFY_ONLY) || (!defined(NO_DH) || defined(HAVE_ECC))
/* Recalculate the number of digits used.
*
* a SP integer.
*/
void sp_clamp(sp_int* a)
{
int i;
for (i = a->used - 1; i >= 0 && a->dp[i] == 0; i--) {
}
a->used = i + 1;
}
/* Grow big number to be able to hold l digits.
* This function does nothing as the number of digits is fixed.
*
* a SP integer.
* l Number of digits.
* retuns MP_MEM if the number of digits requested is more than available and
* MP_OKAY otherwise.
*/
int sp_grow(sp_int* a, int l)
{
if (l > a->size)
return MP_MEM;
(void)a;
(void)l;
return MP_OKAY;
}
/* Sub a one digit number from the big number.
*
* a SP integer.
* d Digit to subtract.
* r SP integer - result.
* returns MP_OKAY always.
*/
int sp_sub_d(sp_int* a, sp_int_digit d, sp_int* r)
{
int i = 0;
r->used = a->used;
r->dp[0] = a->dp[0] - d;
if (r->dp[i] > a->dp[i]) {
for (; i < a->used; i++) {
r->dp[i] = a->dp[i] - 1;
if (r->dp[i] != (sp_int_digit)-1)
break;
}
}
for (; i < a->used; i++)
r->dp[i] = a->dp[i];
return MP_OKAY;
}
#endif
/* Compare a one digit number with a big number.
*
* a SP integer.
* d Digit to compare with.
* returns MP_GT if a is greater than d, MP_LT if a is less than d and MP_EQ
* when a equals d.
*/
int sp_cmp_d(sp_int *a, sp_int_digit d)
{
/* special case for zero*/
if (a->used == 0) {
if (d == 0)
return MP_EQ;
else
return MP_LT;
}
else if (a->used > 1)
return MP_GT;
/* compare the only digit of a to d */
if (a->dp[0] > d)
return MP_GT;
else if (a->dp[0] < d)
return MP_LT;
return MP_EQ;
}
#if !defined(WOLFSSL_RSA_VERIFY_ONLY) || (!defined(NO_DH) || defined(HAVE_ECC))
/* Left shift the number by number of bits.
* Bits may be larger than the word size.
*
* a SP integer.
* n Number of bits to shift.
* returns MP_OKAY always.
*/
static int sp_lshb(sp_int* a, int n)
{
int i;
if (n >= SP_WORD_SIZE) {
sp_lshd(a, n / SP_WORD_SIZE);
n %= SP_WORD_SIZE;
}
if (n == 0)
return MP_OKAY;
a->dp[a->used] = 0;
for (i = a->used - 1; i >= 0; i--) {
a->dp[i+1] |= a->dp[i] >> (SP_WORD_SIZE - n);
a->dp[i] = a->dp[i] << n;
}
if (a->dp[a->used] != 0)
a->used++;
return MP_OKAY;
}
/* Subtract two large numbers into result: r = a - b
* a must be greater than b.
*
* a SP integer.
* b SP integer.
* r SP integer.
* returns MP_OKAY always.
*/
static int sp_sub(sp_int* a, sp_int* b, sp_int* r)
{
int i;
sp_int_digit c = 0;
sp_int_digit t;
for (i = 0; i < a->used && i < b->used; i++) {
t = a->dp[i] - b->dp[i] - c;
if (c == 0)
c = t > a->dp[i];
else
c = t >= a->dp[i];
r->dp[i] = t;
}
for (; i < a->used; i++) {
r->dp[i] = a->dp[i] - c;
c = r->dp[i] == (sp_int_digit)-1;
}
r->used = i;
sp_clamp(r);
return MP_OKAY;
}
/* Calculate the r = a mod m.
*
* a SP integer.
* m SP integer.
* r SP integer.
* returns MP_OKAY always.
*/
int sp_mod(sp_int* a, sp_int* m, sp_int* r)
{
sp_int t;
int mBits = sp_count_bits(m);
int rBits;
if (a != r)
sp_copy(a, r);
sp_init(&t);
rBits = sp_count_bits(r);
while (rBits > mBits) {
sp_copy(m, &t);
sp_lshb(&t, rBits - mBits);
if (sp_cmp(&t, r) == MP_GT) {
sp_copy(m, &t);
sp_lshb(&t, rBits - mBits - 1);
}
sp_sub(r, &t, r);
rBits = sp_count_bits(r);
}
if (sp_cmp(r, m) != MP_LT)
sp_sub(r, m, r);
return MP_OKAY;
}
#endif
/* Clear all data in the big number and sets value to zero.
*
* a SP integer.
*/
void sp_zero(sp_int* a)
{
XMEMSET(a->dp, 0, a->size * sizeof(*a->dp));
a->used = 0;
}
/* Add a one digit number to the big number.
*
* a SP integer.
* d Digit to add.
* r SP integer - result.
* returns MP_OKAY always.
*/
int sp_add_d(sp_int* a, sp_int_digit d, sp_int* r)
{
int i = 0;
r->used = a->used;
r->dp[0] = a->dp[0] + d;
if (r->dp[i] < a->dp[i]) {
for (; i < a->used; i++) {
r->dp[i] = a->dp[i] + 1;
if (r->dp[i] != 0)
break;
}
if (i == a->used) {
r->used++;
r->dp[i] = 1;
}
}
for (; i < a->used; i++)
r->dp[i] = a->dp[i];
return MP_OKAY;
}
#if !defined(WOLFSSL_RSA_VERIFY_ONLY) || (!defined(NO_DH) || defined(HAVE_ECC))
/* Left shift the big number by a number of digits.
* WIll chop off digits overflowing maximum size.
*
* a SP integer.
* s Number of digits to shift.
* returns MP_OKAY always.
*/
int sp_lshd(sp_int* a, int s)
{
if (a->used + s > a->size)
a->used = a->size - s;
XMEMMOVE(a->dp + s, a->dp, a->used * sizeof(sp_int_digit));
a->used += s;
XMEMSET(a->dp, 0, s * sizeof(sp_int_digit));
return MP_OKAY;
}
#ifndef NO_PWDBASED
/* Add two large numbers into result: r = a + b
*
* a SP integer.
* b SP integer.
* r SP integer.
* returns MP_OKAY always.
*/
int sp_add(sp_int* a, sp_int* b, sp_int* r)
{
int i;
sp_digit c = 0;
sp_digit t;
for (i = 0; i < a->used && i < b->used; i++) {
t = a->dp[i] + b->dp[i] + c;
if (c == 0)
c = t < a->dp[i];
else
c = t <= a->dp[i];
r->dp[i] = t;
}
for (; i < a->used; i++) {
r->dp[i] = a->dp[i] + c;
c = r->dp[i] == 0;
}
for (; i < b->used; i++) {
r->dp[i] = b->dp[i] + c;
c = r->dp[i] == 0;
}
r->dp[i] = c;
r->used = (int)(i + c);
return MP_OKAY;
}
#endif /* NO_PWDBASED */
#endif /* !WOLFSSL_RSA_VERIFY_ONLY || (!NO_DH || HAVE_ECC) */
#ifndef NO_RSA
/* Set a number into the big number.
*
* a SP integer.
* b Value to set.
* returns MP_OKAY always.
*/
int sp_set_int(sp_int* a, unsigned long b)
{
a->used = 1;
a->dp[0] = b;
return MP_OKAY;
}
#endif /* !NO_RSA */
#ifdef WC_MP_TO_RADIX
/* Hex string characters. */
static const char sp_hex_char[16] = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
};
/* Put the hex string version, big-endian, of a in str.
*
* a SP integer.
* str Hex string is stored here.
* returns MP_OKAY always.
*/
int sp_tohex(sp_int* a, char* str)
{
int i, j;
/* quick out if its zero */
if (sp_iszero(a) == MP_YES) {
*str++ = '0';
*str = '\0';
return MP_OKAY;
}
i = a->used - 1;
for (j = SP_WORD_SIZE - 4; j >= 0; j -= 4) {
if (((a->dp[i] >> j) & 0xf) != 0)
break;
}
for (; j >= 0; j -= 4)
*(str++) = sp_hex_char[(a->dp[i] >> j) & 0xf];
for (--i; i >= 0; i--) {
for (j = SP_WORD_SIZE - 4; j >= 0; j -= 4)
*(str++) = sp_hex_char[(a->dp[i] >> j) & 0xf];
}
*str = '\0';
return MP_OKAY;
}
#endif /* WC_MP_TO_RADIX */
#if !defined(USE_FAST_MATH)
/* Returns the run time settings.
*
* returns the settings value.
*/
word32 CheckRunTimeSettings(void)
{
return CTC_SETTINGS;
}
#endif /* !USE_FAST_MATH */
#endif /* WOLFSSL_SP_MATH */