/* RSA.C - RSA routines for RSAREF
*/
/* Copyright (C) RSA Laboratories, a division of RSA Data Security,
Inc., created 1991. All rights reserved.
*/
#include "rsa.h"
#include <string.h>
#include <stdlib.h>
#define R_memset memset
#define R_memcpy memcpy
#define R_memcmp memcmp
static NN_DIGIT NN_AddDigitMult
(NN_DIGIT *, NN_DIGIT *, NN_DIGIT, NN_DIGIT *, unsigned int);
static NN_DIGIT NN_SubDigitMult
(NN_DIGIT *, NN_DIGIT *, NN_DIGIT, NN_DIGIT *, unsigned int);
static unsigned int NN_DigitBits(NN_DIGIT);
/* Decodes character string b into a, where character string is ordered
from most to least significant.
Lengths: a[digits], b[len].
Assumes b[i] = 0 for i < len - digits * NN_DIGIT_LEN. (Otherwise most
significant bytes are truncated.)
*/
void NN_Decode(NN_DIGIT *a,
unsigned int digits,
unsigned char *b,
unsigned int len)
{
NN_DIGIT t;
int j;
unsigned int i, u;
for (i = 0, j = len - 1; i < digits && j >= 0; i++)
{
t = 0;
for (u = 0; j >= 0 && u < NN_DIGIT_BITS; j--, u += 8)
t |= ((NN_DIGIT)b[j]) << u;
a[i] = t;
}
for (; i < digits; i++)
a[i] = 0;
}
/* Encodes b into character string a, where character string is ordered
from most to least significant.
Lengths: a[len], b[digits].
Assumes NN_Bits (b, digits) <= 8 * len. (Otherwise most significant
digits are truncated.)
*/
void NN_Encode(unsigned char *a,
unsigned int len,
NN_DIGIT *b,
unsigned int digits)
{
NN_DIGIT t;
int j;
unsigned int i, u;
for (i = 0, j = len - 1; i < digits && j >= 0; i++)
{
t = b[i];
for (u = 0; j >= 0 && u < NN_DIGIT_BITS; j--, u += 8)
a[j] = (unsigned char)(t >> u);
}
for (; j >= 0; j--)
a[j] = 0;
}
/* Assigns a = b.
Lengths: a[digits], b[digits].
*/
void NN_Assign(NN_DIGIT *a,
NN_DIGIT *b,
unsigned int digits)
{
unsigned int i;
for (i = 0; i < digits; i++)
a[i] = b[i];
}
/* Assigns a = 0.
Lengths: a[digits].
*/
void NN_AssignZero(NN_DIGIT *a,
unsigned int digits)
{
unsigned int i;
for (i = 0; i < digits; i++)
a[i] = 0;
}
/* Assigns a = 2^b.
Lengths: a[digits].
Requires b < digits * NN_DIGIT_BITS.
*/
void NN_Assign2Exp(NN_DIGIT *a,
unsigned int b,
unsigned int digits)
{
NN_AssignZero(a, digits);
if (b >= digits * NN_DIGIT_BITS)
return;
a[b / NN_DIGIT_BITS] = (NN_DIGIT)1 << (b % NN_DIGIT_BITS);
}
/* Computes a = b + c. Returns carry.
Lengths: a[digits], b[digits], c[digits].
*/
NN_DIGIT NN_Add(NN_DIGIT *a,
NN_DIGIT *b,
NN_DIGIT *c,
unsigned int digits)
{
NN_DIGIT ai, carry;
unsigned int i;
carry = 0;
for (i = 0; i < digits; i++)
{
if ((ai = b[i] + carry) < carry)
ai = c[i];
else if ((ai += c[i]) < c[i])
carry = 1;
else
carry = 0;
a[i] = ai;
}
return (carry);
}
/* Computes a = b - c. Returns borrow.
Lengths: a[digits], b[digits], c[digits].
*/
NN_DIGIT NN_Sub(NN_DIGIT *a,
NN_DIGIT *b,
NN_DIGIT * c,
unsigned int digits)
{
NN_DIGIT ai, borrow;
unsigned int i;
borrow = 0;
for (i = 0; i < digits; i++)
{
if ((ai = b[i] - borrow) >(MAX_NN_DIGIT - borrow))
ai = MAX_NN_DIGIT - c[i];
else if ((ai -= c[i]) > (MAX_NN_DIGIT - c[i]))
borrow = 1;
else
borrow = 0;
a[i] = ai;
}
return (borrow);
}
/* Returns sign of a - b.
Lengths: a[digits], b[digits].
*/
int NN_Cmp(NN_DIGIT *a,
NN_DIGIT *b,
unsigned int digits)
{
int i;
for (i = digits - 1; i >= 0; i--)
{
if (a[i] > b[i])
return (1);
if (a[i] < b[i])
return (-1);
}
return (0);
}
/* Returns nonzero iff a is zero.
Lengths: a[digits].
*/
int NN_Zero(NN_DIGIT *a,
unsigned int digits)
{
unsigned int i;
for (i = 0; i < digits; i++)
if (a[i])
return (0);
return (1);
}
/* Returns the significant length of a in digits.
Lengths: a[digits].
*/
unsigned int NN_Digits(NN_DIGIT *a,
unsigned int digits)
{
int i;
for (i = digits - 1; i >= 0; i--)
if (a[i])
break;
return (i + 1);
}
/* Returns the significant length of a in bits.
Lengths: a[digits].
*/
unsigned int NN_Bits(NN_DIGIT *a,
unsigned int digits)
{
if ((digits = NN_Digits(a, digits)) == 0)
return (0);
return ((digits - 1) * NN_DIGIT_BITS + NN_DigitBits(a[digits - 1]));
}
/* Computes a = b * c.
Lengths: a[2*digits], b[digits], c[digits].
Assumes digits < MAX_NN_DIGITS.
*/
void NN_Mult(NN_DIGIT *a,
NN_DIGIT *b,
NN_DIGIT *c,
unsigned int digits)
{
NN_DIGIT t[2 * MAX_NN_DIGITS];
unsigned int bDigits, cDigits, i;
NN_AssignZero(t, 2 * digits);
bDigits = NN_Digits(b, digits);
cDigits = NN_Digits(c, digits);
for (i = 0; i < bDigits; i++)
t[i + cDigits] += NN_AddDigitMult(&t[i], &t[i], b[i], c, cDigits);
NN_Assign(a, t, 2 * digits);
/* Zeroize potentially sensitive information.
*/
R_memset((POINTER)t, 0, sizeof(t));
}
/* Computes a = b * 2^c (i.e., shifts left c bits), returning carry.
Lengths: a[digits], b[digits].
Requires c < NN_DIGIT_BITS.
*/
NN_DIGIT NN_LShift(NN_DIGIT *a,
NN_DIGIT *b,
unsigned int c,
unsigned int digits)
{
NN_DIGIT bi, carry;
unsigned int i, t;
if (c >= NN_DIGIT_BITS)
return (0);
t = NN_DIGIT_BITS - c;
carry = 0;
for (i = 0; i < digits; i++)
{
bi = b[i];
a[i] = (bi << c) | carry;
carry = c ? (bi >> t) : 0;
}
return (carry);
}
/* Computes a = c div 2^c (i.e., shifts right c bits), returning carry.
Lengths: a[digits], b[digits].
Requires: c < NN_DIGIT_BITS.
*/
NN_DIGIT NN_RShift(NN_DIGIT *a,
NN_DIGIT *b,
unsigned int c,
unsigned int digits)
{
NN_DIGIT bi, carry;
int i;
unsigned int t;
if (c >= NN_DIGIT_BITS)
return (0);
t = NN_DIGIT_BITS - c;
carry = 0;
for (i = digits - 1; i >= 0; i--)
{
bi = b[i];
a[i] = (bi >> c) | carry;
carry = c ? (bi << t) : 0;
}
return (carry);
}
/* Computes a = b * c, where b and c are digits.
Lengths: a[2].
*/
void NN_DigitMult(NN_DIGIT a[2], NN_DIGIT b, NN_DIGIT c)
{
NN_DIGIT t, u;
NN_HALF_DIGIT bHigh, bLow, cHigh, cLow;
bHigh = (NN_HALF_DIGIT)HIGH_HALF(b);
bLow = (NN_HALF_DIGIT)LOW_HALF(b);
cHigh = (NN_HALF_DIGIT)HIGH_HALF(c);
cLow = (NN_HALF_DIGIT)LOW_HALF(c);
a[0] = (NN_DIGIT)bLow * (NN_DIGIT)cLow;
t = (NN_DIGIT)bLow * (NN_DIGIT)cHigh;
u = (NN_DIGIT)bHigh * (NN_DIGIT)cLow;
a[1] = (NN_DIGIT)bHigh * (NN_DIGIT)cHigh;
if ((t += u) < u)
a[1] += TO_HIGH_HALF(1);
u = TO_HIGH_HALF(t);
if ((a[0] += u) < u)
a[1]++;
a[1] += HIGH_HALF(t);
}
/* Sets a = b / c, where a and c are digits.
Lengths: b[2].
Assumes b[1] < c and HIGH_HALF (c) > 0. For efficiency, c should be
normalized.
*/
void NN_DigitDiv(NN_DIGIT *a, NN_DIGIT b[2], NN_DIGIT c)
{
NN_DIGIT t[2], u, v;
NN_HALF_DIGIT aHigh, aLow, cHigh, cLow;
cHigh = (NN_HALF_DIGIT)HIGH_HALF(c);
cLow = (NN_HALF_DIGIT)LOW_HALF(c);
t[0] = b[0];
t[1] = b[1];
/* Underestimate high half of quotient and subtract.
*/
if (cHigh == MAX_NN_HALF_DIGIT)
aHigh = (NN_HALF_DIGIT)HIGH_HALF(t[1]);
else
aHigh = (NN_HALF_DIGIT)(t[1] / (cHigh + 1));
u = (NN_DIGIT)aHigh * (NN_DIGIT)cLow;
v = (NN_DIGIT)aHigh * (NN_DIGIT)cHigh;
if ((t[0] -= TO_HIGH_HALF(u)) > (MAX_NN_DIGIT - TO_HIGH_HALF(u)))
t[1]--;
t[1] -= HIGH_HALF(u);
t[1] -= v;
/* Correct estimate.
*/
while ((t[1] > cHigh) ||
((t[1] == cHigh) && (t[0] >= TO_HIGH_HALF(cLow))))
{
if ((t[0] -= TO_HIGH_HALF(cLow)) > MAX_NN_DIGIT - TO_HIGH_HALF(cLow))
t[1]--;
t[1] -= cHigh;
aHigh++;
}
/* Underestimate low half of quotient and subtract.
*/
if (cHigh == MAX_NN_HALF_DIGIT)
aLow = (NN_HALF_DIGIT)LOW_HALF(t[1]);
else
aLow =
(NN_HALF_DIGIT)((TO_HIGH_HALF(t[1]) + HIGH_HALF(t[0])) / (cHigh + 1));
u = (NN_DIGIT)aLow * (NN_DIGIT)cLow;
v = (NN_DIGIT)aLow * (NN_DIGIT)cHigh;
if ((t[0] -= u) > (MAX_NN_DIGIT - u))
t[1]--;
if ((t[0] -= TO_HI