iBoot/lib/pki/libGiants/giantMod.c

723 lines
14 KiB
C

/* Copyright (c) 2005-2007 Apple Inc. All Rights Reserved. */
/*
* giantMod.c - modular arithmetic function declarations.
*
* Created Nov. 29 2005 by Doug Mitchell.
*/
#include <libGiants/giantMod.h>
#include <libGiants/giantDebug.h>
#ifndef GI_SMALL_CRT_ENABLE
#error Please define GI_SMALL_CRT_ENABLE.
#endif
#if GI_SMALL_CRT_ENABLE
/*
* Custom version of the tgiant for GI_SMALL_CRT_ENABLE use.
* Normally a tgiant has 2n+2 digits. The corresponding "small tgiant"
* has n+2 digits.
*/
#define GIANT_SMALL_TRIPLE_DIGITS (MAX_PRIME_SIZE_DIGITS + 2)
typedef struct {
giantStruct g;
giantDigit _n_[GIANT_SMALL_TRIPLE_DIGITS];
} smallTGiantStruct;
typedef smallTGiantStruct stgiant;
static GI_INLINE void localSmallTGiantAlloc(stgiant *g)
{
initGiant(&g->g, GIANT_SMALL_TRIPLE_DIGITS, g->_n_);
}
static void rsquareSmall(
giant x,
giant n,
giant recip);
static void rmulgSmall(
giant x,
giant y,
giant n,
giant recip);
static void modg_via_recipSmall(
giant d,
giant r,
giant n);
static void powermodgSmall(
giant base,
giant expo,
giant n,
giant recip,
giant result);
#endif
/* for lots of grungy low-level debugging */
#define DEBUG_MODG 0
#if DEBUG_MODG
#include <stdio.h>
#include <libGiantsUtils/printGiant.h>
static void dispGiant(
const char *label,
giant g)
{
printf("--- %s : ", label);
printGiant(g);
}
static void dispInt(
const char *label,
unsigned i)
{
printf("--- %s : %u\n", label, i);
}
static void dispHeader(
const char *label)
{
printf("--- %s ---\n", label);
}
static void dispDelim(void)
{
printf("-------------\n");
}
#else /* DEBUG_MODG */
#define dispGiant(l, g)
#define dispInt(l, i)
#define dispDelim()
#define dispHeader(l)
#endif /* DEBUG_MODG */
#if GI_MODG_ENABLE
/* returns 1 if g has exactly one bit set, else returns zero */
static int isPowerOfTwo(
giant g)
{
int foundBit = 0;
gi_uint16 dex;
giantDigit currDigit;
giantDigit mask;
GI_CHECK_SP("isPowerOfTwo");
if(isZero(g)) {
return 0;
}
/* see if m.s. digit has exactly one bit set */
currDigit = g->n[g->sign - 1];
/* mask shifts out to zero when done */
for(mask=0x01; mask != 0; mask <<= 1) {
if(currDigit & mask) {
if(foundBit) {
/* found second bit set: done */
return 0;
}
else {
/* first '1' bit seen */
foundBit = 1;
}
}
}
GIASSERT(foundBit != 0);
/* now we return true iff all the rest of the digits are zero */
for(dex=0; dex<g->sign-1; dex++) {
if(g->n[dex] != 0) {
return 0;
}
}
return 1;
}
/*
* Calculate the reciprocal of a demonimator.
*/
void make_recip(
giant d,
giant recip)
/* r becomes the steady-state reciprocal
2^(2b)/d, where b = bit-length of d-1. */
{
int b;
tgiant tmp1;
bgiant tmp2;
tgiant tmp3;
GI_CHECK_SP("make_recip (1)");
if (isZero(d)) {
GIASSERT(0);
return;
}
if(isPowerOfTwo(d)) {
gtog(d, recip);
return;
}
localTriGiantAlloc(&tmp1);
localBigGiantAlloc(&tmp2);
localTriGiantAlloc(&tmp3);
//localGiantAlloc(&tmpRecip);
GI_CHECK_SP("make_recip (2)");
b = bitlen(d);
int_to_giant(1, recip);
if((b & (GIANT_BITS_PER_DIGIT - 1)) == 0) {
/* digit-aligned */
gshiftwordsleft(b >> GI_GIANT_LOG2_BITS_PER_DIGIT, recip);
}
else {
gshiftleft(b, recip);
}
gtog(recip, &tmp2.g);
while(1) {
grammarSquare_common(recip, &tmp1.g);
gshiftright(b, &tmp1.g);
mulg_common(d, &tmp1.g, &tmp3.g);
gshiftright(b, &tmp3.g);
addg(recip, recip);
normal_subg(&tmp3.g, recip);
if(gcompg(recip, &tmp2.g) <= 0) {
break;
}
gtog(recip, &tmp2.g);
}
mulg_common(recip, d, &tmp1.g);
int_to_giant(1, &tmp2.g);
while(bitlen(&tmp1.g) > (2 * b)) {
normal_subg(&tmp2.g, recip);
normal_subg(d, &tmp1.g);
}
CHECK_GIANT_OFLOW(recip);
}
#define MODG_CTR_LIMIT 3
/*
* Optimized modg.
* n := n % d, where r is the precalculated steady-state reciprocal of d.
*/
void modg_via_recip(
giant d,
giant r,
giant n)
{
int s = bitlen(r)-1;
bgiant tmp1;
tgiant tmp2;
//dispGiant("d", d);
//dispGiant("r", r);
//dispGiant("n", n);
GI_CHECK_SP("modg_via_recip(1)");
GIASSERT(!isZero(r));
if(s == 0) {
int_to_giant(0, n);
return;
}
if(gcompg(n, d) < 0) {
/* this mod is a nop: done. */
return;
}
localBigGiantAlloc(&tmp1);
localTriGiantAlloc(&tmp2);
do {
unsigned ct = 0;
gtog(n, &tmp1.g);
gshiftright(s-1, &tmp1.g);
mulg_common(r, &tmp1.g, &tmp2.g);
gshiftright(s+1, &tmp2.g);
mulg_common(d, &tmp2.g, &tmp1.g);
normal_subg(&tmp1.g, n);
while(++ct <= MODG_CTR_LIMIT) {
if(gcompg(n,d) >= 0) {
normal_subg(d,n);
}
else {
return;
}
}
} while(1);
}
/* Both n and div are written by this function, with new values:
n := n mod d,
div := floor(n/d).
*/
void divmodg_via_recip(
giant d,
giant r,
giant n,
giant dv)
{
int s = bitlen(r)-1;
tgiant tmp1;
tgiant tmp2;
GI_CHECK_SP("divmodg_via_recip(1)");
GIASSERT(!isZero(r));
if(s == 0) {
int_to_giant(0, n);
gtog(n, dv);
return;
}
int_to_giant(0, dv);
if(gcompg(n, d) < 0) {
/* this mod is a nop: done. */
return;
}
localTriGiantAlloc(&tmp1);
localTriGiantAlloc(&tmp2);
GI_CHECK_SP("divmodg_via_recip(2)");
do {
unsigned ct = 0;
gtog(n, &tmp1.g);
gshiftright(s-1, &tmp1.g);
mulg_common(r, &tmp1.g, &tmp2.g);
gshiftright(s+1, &tmp2.g);
mulg_common(d, &tmp2.g, &tmp1.g);
normal_subg(&tmp1.g, n);
addg(&tmp2.g, dv);
while(++ct <= 3) {
if(gcompg(n,d) >= 0) {
normal_subg(d,n);
iaddg(1, dv);
}
else {
return;
}
}
} while(1);
}
/* x := (x^2) mod n with known reciprocal of n */
void rsquare(
giant x,
giant n,
giant recip)
{
bgiant sq;
GI_CHECK_SP("rsquare(1)");
localBigGiantAlloc(&sq);
GI_CHECK_SP("rsquare(2)");
grammarSquare_common(x, &sq.g);
modg_via_recip(n, recip, &sq.g);
gtog(&sq.g, x);
}
/* y := (x * y) mod n with known reciprocal of n */
void rmulg(
giant x,
giant y,
giant n,
giant recip)
{
bgiant prod;
GI_CHECK_SP("rmulg");
localBigGiantAlloc(&prod);
mulg_common(x, y, &prod.g);
modg_via_recip(n, recip, &prod.g);
gtog(&prod.g, y);
}
/*
* result becomes base^expo (mod n).
* Caller knows and gives us reciprocal of n as recip.
* base and result can share the same giant.
*/
void powermodg(
giant base,
giant expo,
giant n,
giant recip,
giant result)
{
int len, pos;
lgiant bmod; /* saved base mod n */
GI_CHECK_SP("powermodg");
GI_LOG_SP("top of powermodg");
if(isZero(expo)) {
int_to_giant(1, result);
return;
}
localGiantAlloc(&bmod);
if(base != result) {
gtog(base, result);
}
if(gcompg(result, n) >= 0) {
modg_via_recip(n, recip, result);
}
gtog(result, &bmod.g);
len = bitlen(expo);
if(len == 1) {
return;
}
for(pos = len-2; pos >= 0; pos--) {
rsquare(result, n, recip); // New ring-square.
if (bitval(expo, pos)) {
rmulg(&bmod.g, result, n, recip);
}
}
return;
}
void modg(
giant d,
giant n)
/* n becomes n%d. n is arbitrary, but the denominator d must be positive! */
{
lgiant recip;
GI_CHECK_SP("modg");
localGiantAlloc(&recip);
make_recip(d, &recip.g);
modg_via_recip(d, &recip.g, n);
}
/*
* power/mod using Chinese Remainder Theorem.
*
* result becomes base^d (mod n).
*
* ...but we don't know or need d or n. What we have are
*
* p, q such that n = p*q
* reciprocals of p, q
* dp = d mod (p-1)
* dq = d mod (q-1)
* qinv = q^(1) mod p
*/
void powermodCRT(
giant x, /* base (plaintext/ciphertext) */
giant p, /* p*q = n */
giant pRecip, /* reciprocal of p */
giant q,
giant qRecip, /* reciprocal of p */
giant dp, /* d mod (p-1) */
giant dq, /* d mod (q-1) */
giant qinv,
giant sq, /* scratch, provided and init'd by caller */
giant result) /* OUTPUT */
{
bgiant sp;
int m;
GI_CHECK_SP("powermodCRT");
localBigGiantAlloc(&sp);
GIASSERT(sq->capacity >= GIANT_LITTLE_DIGITS);
/*
* sp := x^dp mod p;
* sq := x^dq mod q;
*/
#if GI_SMALL_CRT_ENABLE
powermodgSmall(x, dp, p, pRecip, &sp.g);
powermodgSmall(x, dq, q, qRecip, sq);
#else
powermodg(x, dp, p, pRecip, &sp.g);
powermodg(x, dq, q, qRecip, sq);
#endif
/*
* m = gcompg(sp, sq);
* if(m > = 0)
* result = sp-sq;
* else
* result = sq-sp; // Note unsigned subtract always;
*/
m = gcompg(&sp.g, sq);
if(m >= 0) {
gtog(&sp.g, result);
normal_subg(sq, result);
}
else {
gtog(sq, result);
normal_subg(&sp.g, result);
}
/*
* result = (qinv * result) mod p;
* done with sp, use as scratch now...
*/
mulg_common(result, qinv, &sp.g);
modg_via_recip(p, pRecip, &sp.g);
gtog(&sp.g, result);
/*
* if( ! isZero(result)) if(m < 0) result = p - result;
*/
if(!isZero(result)) {
if(m < 0) {
gtog(p, &sp.g);
normal_subg(result, &sp.g);
gtog(&sp.g, result);
}
}
/*
* result *= q;
* result += sq;
*/
mulg_common(result, q, &sp.g);
addg(sq, &sp.g);
gtog(&sp.g, result);
}
#endif /* GI_MODG_ENABLE */
#if GI_INVERSE_MOD_ENABLE
/*
* Given x, y coprime with x > 0, y > 1, this routine forces
* x := x^(-1) mod modulus.
*/
void ginverseMod(
giant modulus,
giant recip, /* reciprocal of modulus */
giant x) /* IN/OUT */
{
lgiant u, v, a, q, tmp;
unsigned int k = 0;
lgiant uRecip;
GI_CHECK_SP("ginverseMod");
localGiantAlloc(&u);
localGiantAlloc(&v);
localGiantAlloc(&a);
localGiantAlloc(&q);
localGiantAlloc(&tmp);
localGiantAlloc(&uRecip);
dispHeader("ginverseMod top");
dispGiant("x", x);
dispGiant("modulus", modulus);
dispGiant("recip", recip);
/*
* 1) (u, v, a, x, k) = (x mod modulus, modulus, 0, 1, 0);
*/
gtog(x, &u.g);
modg_via_recip(modulus, recip, &u.g);
gtog(modulus, &v.g);
/* a is already zero */
int_to_giant(1, x);
dispHeader("ginverseMod before loop");
dispGiant("x", x);
dispGiant("u", &u.g);
dispGiant("v", &v.g);
dispGiant("a", &a.g);
dispInt("k", k);
/*
* 2) while(u > 1) {
* q = v div u;
* (u, v, a, x, k) = (v mod u, u, x, (a + q*x) mod y, k + 1);
* }
*/
while(!isZero(&u.g) && !isone(&u.g)) {
/* (q,u) := (v div u, v mod u */
make_recip(&u.g, &uRecip.g);
gtog(&u.g, &tmp.g); /* for a const modulus in divmodg_via_recip() */
gtog(&v.g, &u.g); /* divisor, then result of mod */
divmodg_via_recip(&tmp.g, &uRecip.g, &u.g, &q.g);
/* v := old u */
gtog(&tmp.g, &v.g);
/* intermediate: *old* x */
gtog(x, &tmp.g);
/* calc new x := a + q*x mod y */
rmulg(&q.g, x, modulus, recip);
addg(&a.g, x);
modg_via_recip(modulus, recip, x); /* just in case */
/* new a := old x */
gtog(&tmp.g, &a.g);
k++;
dispDelim();
dispGiant("x", x);
dispGiant("q", &q.g);
dispGiant("u", &u.g);
dispGiant("v", &v.g);
dispGiant("a", &a.g);
dispInt("k", k);
}
/*
* 3) if(k odd) x = y - x; // Unsigned (normal_subg) subtract.
*/
if(k & 1) {
gtog(modulus, &u.g);
normal_subg(x, &u.g);
gtog(&u.g, x);
}
}
#endif /* GI_INVERSE_MOD_ENABLE */
#if GI_SMALL_CRT_ENABLE
/*
* Optional "small giant" routines used only by powermodCRT when
* performing its initial powermodg operations with p and q as the
* modulus; p and q are half the size of MAX_PRIME_SIZE_DIGITS, so
* we implement the following functions with corresponding small
* local giants.
* On PPC, this saves 384 bytes of stack space, at the cost of
* 1407 extra bytes of code.
*/
static void rsquareSmall(
giant x,
giant n,
giant recip)
{
stgiant sq;
GI_CHECK_SP("rsquareSmall(1)");
localSmallTGiantAlloc(&sq);
GI_CHECK_SP("rsquareSmall(2)");
grammarSquare_common(x, &sq.g);
modg_via_recipSmall(n, recip, &sq.g);
gtog(&sq.g, x);
}
/* y := (x * y) mod n with known reciprocal of n */
static void rmulgSmall(
giant x,
giant y,
giant n,
giant recip)
{
stgiant prod;
GI_CHECK_SP("rmulgSmall");
localSmallTGiantAlloc(&prod);
mulg_common(x, y, &prod.g);
modg_via_recipSmall(n, recip, &prod.g);
gtog(&prod.g, y);
}
/*
* Optimized modg.
* n := n % d, where r is the precalculated steady-state reciprocal of d.
*/
static void modg_via_recipSmall(
giant d,
giant r,
giant n)
{
int s = bitlen(r)-1;
stgiant tmp1;
stgiant tmp2;
GI_CHECK_SP("modg_via_recipSmall(1)");
GIASSERT(!isZero(r));
if(s == 0) {
int_to_giant(0, n);
return;
}
if(gcompg(n, d) < 0) {
/* this mod is a nop: done. */
return;
}
localSmallTGiantAlloc(&tmp1);
localSmallTGiantAlloc(&tmp2);
do {
unsigned ct = 0;
gtog(n, &tmp1.g);
gshiftright(s-1, &tmp1.g);
mulg_common(r, &tmp1.g, &tmp2.g);
gshiftright(s+1, &tmp2.g);
mulg_common(d, &tmp2.g, &tmp1.g);
normal_subg(&tmp1.g, n);
while(++ct <= MODG_CTR_LIMIT) {
if(gcompg(n,d) >= 0) {
normal_subg(d,n);
}
else {
return;
}
}
} while(1);
}
/*
* result becomes base^expo (mod n).
* Caller knows and gives us reciprocal of n as recip.
* base and result can share the same giant.
*
* This is only used by powermodCRT, which conveniently hands us
* two initialized scratch giants, one bgiant and one stgiant
* so neither we nor any of our callers have to allocate any
* local giants.
*/
static void powermodgSmall(
giant base,
giant expo,
giant n,
giant recip,
giant result)
{
int len, pos;
lgiant bmod; /* saved base mod n */
GI_CHECK_SP("powermodgSmall");
GI_LOG_SP("top of powermodgSmall");
if(isZero(expo)) {
int_to_giant(1, result);
return;
}
localGiantAlloc(&bmod);
if(base != result) {
gtog(base, result);
}
if(gcompg(result, n) >= 0) {
modg_via_recipSmall(n, recip, result);
}
gtog(result, &bmod.g);
len = bitlen(expo);
if(len == 1) {
return;
}
for(pos = len-2; pos >= 0; pos--) {
rsquareSmall(result, n, recip); // New ring-square.
if (bitval(expo, pos)) {
rmulgSmall(&bmod.g, result, n, recip);
}
}
return;
}
#endif /* GI_SMALL_CRT_ENABLE */