/* $Id: t_bdTest.c $ */ /***** BEGIN LICENSE BLOCK ***** * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright (c) 2001-16 David Ireland, D.I. Management Services Pty Limited * . All rights reserved. * ***** END LICENSE BLOCK *****/ /* * Last updated: * $Date: 2016-03-31 09:51:00 $ * $Revision: 2.6.1 $ * $Author: dai $ */ /* Various tests of "bd" functions, not exhaustive */ #if _MSC_VER >= 1100 /* Detect memory leaks in MSVC++ */ #define _CRTDBG_MAP_ALLOC #include #include #else #include #endif #ifdef NDEBUG #undef NDEBUG #endif #include #include #include #include #include "bigd.h" #include "bigdRand.h" static void pr_bytesmsg(const char *msg, unsigned char *bytes, size_t nbytes) /* Display a message followed by the hex values of a byte array */ { size_t i; printf("%s", msg); for (i = 0; i < nbytes; i++) printf("%02x", bytes[i]); printf("\n"); } int my_rand(unsigned char *bytes, size_t nbytes, const unsigned char *seed, size_t seedlen) /* Our own (very insecure) random generator call-back function using good old rand() This demonstrates the required format for BD_RANDFUNC -- replace this in practice with your own cryptographically-secure function. */ { unsigned int myseed; size_t i; int offset; /* Use time - then blend in seed, if any */ myseed = (unsigned)time(NULL); if (seed) { for (offset = 0, i = 0; i < seedlen; i++, offset = (offset + 1) % sizeof(unsigned)) myseed ^= ((unsigned int)seed[i] << (offset * 8)); } srand(myseed); while (nbytes--) { *bytes++ = rand() & 0xFF; } return 0; } int fermat_test(BIGD n) { /* Carries out a quick Fermat primality test on n > 4. Returns 1 if n is prime and 0 if composite or -1 if n < 5. This is not foolproof but we use it as a check on our main bdIsPrime function. It will return a false positive for Fermat Liars, which are very rare. */ BIGD a, e, r; int isprime = 1; /* For any integer a, 1<=a<=n-1, if a^(n-1) mod n != 1 then n is composite. */ if (bdShortCmp(n, 5) < 0) return -1; a = bdNew(); e = bdNew(); r = bdNew(); /* e = n -1 */ bdSetEqual(e, n); bdDecrement(e); /* Set a = 2 and compute a^(n-1) mod n */ bdSetShort(a, 2); bdModExp(r, a, e, n); if (bdShortCmp(r, 1) != 0) isprime = 0; /* a = 3 */ bdSetShort(a, 3); bdModExp(r, a, e, n); if (bdShortCmp(r, 1) != 0) isprime = 0; /* a = 5 */ bdSetShort(a, 5); bdModExp(r, a, e, n); if (bdShortCmp(r, 1) != 0) isprime = 0; bdFree(&a); bdFree(&e); bdFree(&r); return isprime; } /** Compute w = u - v using cutoff subtraction so `w >= 0` always. * @return 1 if subtraction underflowed or 0 for normal result */ int cutoff_subtraction(BIGD w, BIGD u, BIGD v) { int carry = 0; if (bdCompare(u, v) < 0) { bdSetZero(w); carry = 1; } else { bdSubtract(w, u, v); } return carry; } int main(void) { BIGD u, v, w, q, r, p, b, a; BIGD n, e, m, c, z, mz, cz, t; BIGD x, y; bdigit_t overflow, d; int cmp, res; unsigned char ff[64], bytes[16]; int i, mc, jac; size_t nbytes, nbits; char s[128]; /* MSVC memory leak checking stuff */ #if _MSC_VER >= 1100 _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE ); _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDOUT ); _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE ); _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDOUT ); #endif printf("Tests for BIGD functions\n"); /* Initialize - new way in [v2.6]*/ bdNewVars(&u, &v, &w, &p, &q, &r, &x, &y); //u = bdNew(); //v = bdNew(); //w = bdNew(); //p = bdNew(); //q = bdNew(); //r = bdNew(); //x = bdNew(); //y = bdNew(); printf("\nSIMPLE ARITHMETIC OPERATIONS...\n"); bdPrintDecimal("At start w=", w, "\n"); assert(bdIsZero(w)); /* Addition */ /* 1 + 1 = 2 */ bdSetShort(u, 1); bdSetShort(v, 1); bdAdd(w, u, v); bdPrintDecimal("1+1=", w, "\n"); assert(bdShortCmp(w, 2) == 0); /* Add with digit overflow */ bdSetShort(u, 0xffffffff); bdSetShort(v, 0xffffffff); bdAdd(w, u, v); bdPrintHex("ffffffff+ffffffff=", w, "\n"); /* ffffffff+ffffffff=00000001 fffffffe */ assert(bdSizeof(w) == 2); /* Bigger random digits */ bdSetRandTest(u, 10); bdSetRandTest(v, 10); bdAdd(w, u, v); bdPrintHex("u=\n", u, "\n"); bdPrintHex("v=\n", v, "\n"); bdPrintHex("w=u+v=\n", w, "\n"); /* Subtract */ bdSubtract(r, w, v); bdPrintHex("w-v=\n", r, "\n"); assert(bdCompare(r, u) == 0); /* Multiplication */ /* 2 * 3 = 6 */ bdSetShort(u, 2); bdSetShort(v, 3); bdMultiply(w, u, v); bdPrintDecimal("2 * 3=", w, "\n"); assert(bdShortCmp(w, 6) == 0); bdSetShort(u, 0xffffffff); bdSetShort(v, 0xffffffff); bdMultiply(w, u, v); bdPrintHex("ffffffff*ffffffff=", w, "\n"); /* ffffffff*ffffffff=fffffffe 00000001 */ assert(bdGetBit(w, 0) == 1); /* Use ShortMult */ bdShortMult(p, u, 0xffffffff); bdPrintHex("(Short)ffffffff*ffffffff=", p, "\n"); assert(bdCompare(p, w) == 0); /* Use larger random u, v */ bdSetRandTest(u, 10); bdSetRandTest(v, 20); bdPrintHex("u=\n", u, "\n"); bdPrintHex("v=\n", v, "\n"); /* Use ShortMult by one: w = v * 1 */ bdShortMult(w, v, 1); bdPrintHex("(Short)v*1=\n", w, "\n"); assert(bdCompare(w, v) == 0); /* Use ShortMult by two */ bdShortMult(w, u, 2); bdPrintHex("(Short)u*2=\n", w, "\n"); assert(bdIsEven(w)); /* Big multiplication */ bdMultiply(w, u, v); bdPrintHex("w=u*v=\n", w, "\n"); /* Divide expecting q = w/v == u and r = w%v == 0 */ bdDivide(q, r, w, v); bdPrintHex("w/v=\n", q, "\n"); bdPrintHex("w%v=", r, "\n"); assert(bdIsEqual(q, u)); assert(bdShortCmp(r, 0) == 0); /* Modulo */ bdIncrement(w); bdModulo(r, w, v); bdPrintHex("w+1 mod v=", r, "\n"); assert(bdShortCmp(r, 1) == 0); bdDecrement(w); bdModulo(r, w, u); bdPrintHex("w+1-1 mod u=", r, "\n"); assert(bdShortCmp(r, 0) == 0); /* Use a short divisor q=w/2 */ bdShortDiv(q, r, w, 2); bdPrintHex("(ShortDiv)w/2=\n", q, "\n"); bdPrintHex("(ShortDiv)w%2=", r, "\n"); printf("w is %s\n", bdIsOdd(w) ? "ODD" : "EVEN"); bdShortMod(r, w, 2); bdPrintHex("w mod 2=", r, "\n"); bdIncrement(w); printf("w+1 is %s\n", bdIsOdd(w) ? "ODD" : "EVEN"); bdShortMod(r, w, 2); bdPrintHex("++w mod 2=", r, "\n"); bdDecrement(w); /* Check with short mult u = q*2 */ bdShortMult(u, q, 2); bdPrintHex("(ShortMult) (w/2) * 2=\n", u, "\n"); printf("\nSQUARE AND SQUARE ROOT...\n"); /* Square a random number w = u^2 */ bdSetRandTest(u, 10); bdPrintHex("random u=\n", u, "\n"); bdSquare(w, u); bdPrintHex("(Square) u^2=", w, "\n"); /* check against product p = u * u */ bdMultiply(p, u, u); bdPrintHex("u*u=", p, "\n"); assert(bdIsEqual(p, w)); /* Compute integer square root [new in v2.1] */ bdSqrt(p, w); bdPrintHex("sqrt(w)=", p, "\n"); assert(bdIsEqual(p, u)); printf("\nMODULAR ARITHMETIC...\n"); /* Check that (u mod n + v mod n) mod n == (u+v) mod n */ bdSetRandTest(u, 10); bdSetRandTest(v, 10); bdSetRandTest(w, 10); bdPrintHex("(Modulo)\nNew random u=", u, "\n"); bdPrintHex("v=", v, "\n"); bdPrintHex("w=", w, "\n"); /* r = u mod w */ bdModulo(r, u, w); /* q = v mod w */ bdModulo(q, v, w); /* q = u mod n + v mod n */ bdAdd(q, q, r); /* r = (u mod w + v mod w) mod w */ bdModulo(r, q, w); bdPrintHex("(u mod w + v mod w) mod w=\n", r, "\n"); /* q = (u+v) mod w */ bdSetEqual(p, u); bdAdd(p, p, v); bdModulo(q, p, w); bdPrintHex("(u+v) mod w=\n", q, "\n"); assert(bdIsEqual(r, q)); /* ModMult and ModSquare */ bdModMult(p, u, v, w); bdPrintHex("u * v mod w=\n", p, "\n"); bdModMult(p, u, u, w); bdPrintHex("u * u mod w=\n", p, "\n"); bdModSquare(q, u, w); bdPrintHex("u^2 mod w=\n", q, "\n"); assert(bdIsEqual(p, q)); /* Clear and start again */ bdSetZero(u); bdSetZero(v); bdSetZero(w); printf("\nARITHMETIC BOUNDARY CONDITIONS...\n"); /* This causes Divide algorithm to `Add Back' with 32-bit digits See Knuth 4.3.1 Algorithm D Step D6 */ printf("Check `Add Back' in Divide algorithm\n"); bdConvFromHex(u, "7fffffff 80000001 00000000 00000000"); bdConvFromHex(v, "00000000 80000000 80000002 00000005"); bdDivide(q, r, u, v); bdPrintHex("u=", u, "\n"); bdPrintHex("v=", v, "\n"); bdPrintHex("q=u/v=", q, "\n"); bdPrintHex("r=u mod v=", r, "\n"); /* check that qv + r = u */ bdMultiply(w, q, v); bdAdd(w, w, r); bdPrintHex("qv+r=", w, "\n"); assert(bdIsEqual(w, u)); /* And check that r = u mod v >= (1-2/b)v See Knuth 4.3.1 Exercise 21 */ bdConvFromHex(p, "1 00000000"); bdSetEqual(w, p); bdShortSub(w, w, 2); bdMultiply(u, w, v); bdDivide(q, w, u, p); bdPrintHex("(1-2/b)=", q, "\n"); cmp = bdCompare(r, q); printf("u mod v %s (1-2/b)v\n", (cmp >= 0 ? ">=" : "<")); printf("\nCope, sort of, with `negative` numbers...\n"); bdSetZero(u); overflow = bdShortSub(w, u, 2); bdPrintHex("w=0-2=", w, "\n"); printf("overflow=%" PRIuBIGD "\n", overflow); overflow = bdIncrement(w); bdPrintHex("w+1=", w, "\n"); printf("overflow=%" PRIuBIGD "\n", overflow); overflow = bdIncrement(w); bdPrintHex("(Note error!) w+1=", w, "\n"); printf("overflow=%" PRIuBIGD "\n", overflow); printf("\nUse cut-off subtraction...\n"); bdSetShort(u, 3); bdSetShort(v, 7); cutoff_subtraction(w, u, v); bdPrintDecimal("3-7=", w, "\n"); // But OK if no underflow cutoff_subtraction(w, v, u); bdPrintDecimal("7-3=", w, "\n"); printf("\nCatch problems working close to zero...\n"); bdSetZero(u); overflow = bdShortAdd(u, u, 1); bdPrintHex("u=0+1=", u, "\n"); printf("overflow=%" PRIuBIGD " (expected 0)\n", overflow); printf("Compute w = 0 + 0...\n"); bdSetZero(u); bdSetZero(v); bdAdd(w, u, v); printf("bdPrint(w)="); bdPrint(w, 0x1); bdPrintDecimal("w=0+0=", w, "\n"); bdPrintHex("w=0+0=0x", w, "\n"); bdPrintBits("w=0+0='", w, "'b\n"); assert(bdIsZero(w)); bdAdd(u, w, v); bdPrintHex("w+0=", u, "\n"); bdAdd(w, w, v); bdPrintHex("w+=0=", w, "\n"); /* Set u = v = ffff...ffff */ for (i = 0; i < sizeof(ff); i++) ff[i] = 0xff; bdConvFromOctets(u, ff, sizeof(ff)); bdConvFromOctets(v, ff, sizeof(ff)); bdPrintHex("u=", u,"\n"); bdPrintHex("v=", v,"\n"); /* Compute u * v */ bdMultiply(w, u, v); bdPrintHex("w = u * v =\n", w,"\n"); /* Set v = w / u */ bdDivide(v, r, w, u); bdPrintHex("v = w / u =\n", v,"\n"); bdPrintHex("rem=", r,"\n"); assert(bdIsEqual(u, v)); assert(bdShortCmp(r, 0) == 0); bdIncrement(u); bdDecrement(v); bdMultiply(w, u, v); bdPrintHex("w = ++u * --v =\n", w,"\n"); bdDivide(w, r, u, v); bdPrintHex("q = u / v =", w,"\n"); bdPrintHex("rem=", r,"\n"); assert(bdShortCmp(w, 1) == 0); assert(bdShortCmp(r, 2) == 0); /* Clear and start again */ bdSetZero(u); bdSetZero(v); bdSetZero(w); bdSetShort(u, 0); /* Fixed [v2.3]: This used to cause an error */ bdPrintDecimal("bdSetShort(u, 0)=> u=", u, "\n"); bdSetShort(v, 3); bdModulo(w, u, v); bdPrintDecimal("0 mod 3=", w, "\n"); /* Set a new random value for v */ bdSetRandTest(v, 10); bdPrintHex("Random v=\n", v,"\n"); bdShortAdd(u, v, 1); bdPrintHex("u = v + 1 =\n", u,"\n"); cmp = bdCompare(u, v); printf("bdCompare(u-v) = %d\n", cmp); assert(cmp > 0); cmp = bdCompare(v, u); printf("bdCompare(v-u) = %d\n", cmp); assert(cmp < 0); cmp = bdCompare(u, u); printf("bdCompare(u-u) = %d\n", cmp); assert(cmp == 0); /* Compare with short */ bdSetShort(w, 0xfffffffe); bdPrintHex("w=", w,"\n"); cmp = bdShortCmp(w, 1); assert(cmp > 0); printf("bdShortCmp(w-1) = %d\n", cmp); cmp = bdShortCmp(w, 0xffffffff); assert(cmp < 0); printf("bdShortCmp(w-0xffffffff) = %d\n", cmp); cmp = bdShortCmp(w, 0xfffffffd); printf("bdShortCmp(w-0xfffffffd) = %d\n", cmp); assert(cmp > 0); /* Convert w back to a short and compare */ d = bdToShort(w); /* NB This will truncate if w is too big */ printf("d=bdToShort(w)=%" PRIxBIGD "\n", d); cmp = bdShortIsEqual(w, d); printf("bdShortIsEqual(w, d)=%d\n", cmp); assert(cmp == 1); printf("\nBIT SHIFT OPERATIONS...\n"); /* Try shifting bits */ bdPrintHex("w = ", w,"\n"); bdFree(&u); u = bdNew(); bdShiftLeft(u, w, 2); bdPrintHex("w << 2 = ", u,"\n"); assert(bdGetBit(u, 33) == 1); bdShiftRight(v, u, 3); bdPrintHex("(w << 2) >> 3 = ", v,"\n"); assert(bdGetBit(v, 31) == 0); /* Bigger shift */ bdShiftLeft(u, w, 128); bdPrintHex("w << 128 = ", u,"\n"); assert(bdGetBit(u, 159) == 1); bdShiftRight(v, u, 129); bdPrintHex("(w << 128) >> 129 = ", v,"\n"); assert(bdGetBit(v, 31) == 0); /* Use a new variable */ b = bdNew(); bdSetZero(b); /* overkill */ bdSetBit(b, 510, 1); bdSetBit(b, 477, 1); bdSetBit(b, 31, 1); bdSetBit(b, 0, 1); bdPrintHex("b=\n", b,"\n"); bdSetBit(b, 512, 1); bdSetBit(b, 31, 0); bdPrintHex("b'=\n", b,"\n"); printf("Bit 512 is %d\n", bdGetBit(b, 512)); assert(bdGetBit(b, 512) == 1); printf("Bit 511 is %d\n", bdGetBit(b, 511)); assert(bdGetBit(b, 511) == 0); assert(bdGetBit(b, 477) == 1); /* NB one-based bit lengths so expecting 513 */ printf("Bit Length = %u\n", bdBitLength(b)); assert(bdBitLength(b) == 513); bdPrintHex("b'=\n", b,"\n"); /* Destroy b */ bdFree(&b); printf("\nRANDOM NUMBER GENERATION...\n"); /* Create a random number using `my_rand' callback function */ bdRandomSeeded(r, 508, (const unsigned char*)"", 0, my_rand); bdPrintHex("random=\n", r,"\n"); /* NB bit length may be less than 508 */ printf("%u bits\n", bdBitLength(r)); assert(bdBitLength(r) <= 508); /* Create a random number using the internal RNG */ bdRandomBits(r, 509); bdPrintHex("random=\n", r,"\n"); printf("%u bits\n", bdBitLength(r)); assert(bdBitLength(r) <= 509); printf("\nPRIME NUMBERS...\n"); /* Generate two random primes */ printf("Generating two primes...\n"); bdGeneratePrime(p, 128, 5, (const unsigned char*)"1", 1, my_rand); bdPrintHex("prime p=\n", p,"\n"); bdGeneratePrime(q, 128, 5, (const unsigned char*)"abcdef", 6, my_rand); bdPrintHex("prime q=\n", q,"\n"); /* Check primality with more tests (64 vs 5) */ /* NB vv small chance this could fail */ printf("Checking prime: p "); res = bdIsPrime(p, 64); printf("%s\n", (res ? "is OK" : "is NOT prime!!")); /* And again using Fermat test */ res = fermat_test(p); if (res == 0) printf("WARNING: p failed Fermat test.\n"); /* w = product pq */ bdMultiply(w, p, q); bdPrintHex("pq=\n", w,"\n"); /* Product pq should not be prime */ res = bdIsPrime(w, 5); printf("Composite pq %s\n", (res ? "IS prime" : "is NOT prime")); assert(res == 0); /* Check GCD: gcd(pq, p) == p */ bdGcd(r, w, p); bdPrintHex("gcd(pq, p) =\n", r,"\n"); assert(bdIsEqual(r, p)); /* --Modular inversion-- */ printf("\nMODULAR INVERSION...\n"); /* set v to be a small random multiplier */ bdSetRandTest(v, 1); bdIncrement(v); /* Make sure not zero */ bdPrintHex("multiplier, v=", v,"\n"); /* set u = vp - 1 */ bdMultiply(u, v, p); bdDecrement(u); bdPrintHex("p=\n", p,"\n"); bdPrintHex("u = vp - 1 =\n", u,"\n"); /* compute w = u^-1 mod p */ res = bdModInv(w, u, p); bdPrintHex("w = u^-1 mod p=\n", w,"\n"); assert(res == 0); /* check that wu mod p == 1 */ bdModMult(r, w, u, p); bdPrintHex("wu mod p = ", r,"\n"); assert(bdShortCmp(r, 1) == 0); /* Try mod inversion that should fail */ /* Set u = pq so that gcd(u, p) != 1 */ bdMultiply(u, p, q); res = bdModInv(w, u, p); printf("w = (pq)^-1 mod p returns (expected) error %d\n", res); assert(res != 0); bdPrintHex("output, w=", w,"\n"); /* Do some bit string operations */ printf("\nBIT STRING OPERATIONS...\n"); /* Use XOR to swop two variables without a temp variable, i.e. a = a XOR b; b = a XOR b; a = a XOR b; */ printf("Swop u and w:\n"); /* Generate 2 random numbers (u,w) each 144 bits long */ bdRandomBits(u, 144); bdRandomBits(w, 144); bdPrintHex("u= ", u,"\n"); bdPrintHex("w= ", w,"\n"); /* remember these for later (p=u, q=w) */ bdSetEqual(p, u); bdSetEqual(q, w); /* Swop using XOR */ bdXorBits(u, u, w); bdXorBits(w, u, w); bdXorBits(u, u, w); bdPrintHex("u'=", u,"\n"); bdPrintHex("w'=", w,"\n"); /* check they have swopped accurately */ assert(bdIsEqual(u, q)); assert(bdIsEqual(w, p)); /* Try some simple AND and OR ops */ printf("AND and OR ops:\n"); /* Set every even bit in u and every odd bit in v */ bdSetZero(u); bdSetZero(v); for (i = 0; i < 144 / 2; i++) { bdSetBit(u, (i * 2), 1); bdSetBit(v, (i * 2) + 1, 1); } bdPrintHex("u= ", u,"\n"); bdPrintHex("v= ", v,"\n"); bdAndBits(w, u, v); bdPrintHex("u AND v=", w,"\n"); /* expected answer = zero */ assert(bdIsZero(w)); bdOrBits(w, u, v); bdPrintHex("u OR v= ", w,"\n"); /* expected answer p = 2^144 - 1 */ bdSetZero(p); bdSetBit(p, 144, 1); bdDecrement(p); bdPrintHex("2^144-1=", p,"\n"); assert(bdIsEqual(p, w)); /* Invert all bits */ bdSetZero(p); bdSetZero(q); bdSetShort(p, 0xfffe); bdShiftLeft(p, p, 32); bdShortAdd(p, p, 0xfffe); bdPrintHex("p= ", p,"\n"); bdNotBits(q, p); bdPrintHex("NOT p=", q,"\n"); /* check p AND (NOT p) == 0 */ bdAndBits(w, p, q); bdPrintHex("p AND (NOT p)=", w,"\n"); assert(bdIsZero(w)); /* Compose a 400-bit string by concatenating random 160-bit sections and then truncating the result */ printf("\nBit string concatenation and truncation:\n"); /* ALGORITHM: (based on a similar one in RFC 2631/ANSI X.42) Set m' = m/160 where / represents integer division with rounding upwards Set U = 0 For i = 0 to m' - 1 Set R = FUNC_160 and V = FUNC_160 where FUNC_160 generates a unique 160-bit value U = U + (R XOR V) * 2^(160 * i) Form q from U by computing U mod (2^m) and setting the most significant bit (the 2^(m-1) bit) and the least significant bit to 1. In terms of boolean operations, q = U OR 2^(m-1) OR 1. Note that 2^(m-1) < q < 2^m */ /* In this example, m=400 and m'=3, and we just generate 160-bit random values */ mc = 3; bdSetZero(u); /* Set U = 0 */ for (i = 0; i < mc; i++) { bdRandomBits(r, 160); /* R = FUNC_160 */ bdRandomBits(v, 160); /* V = FUNC_160 */ bdXorBits(p, r, v); /* p = (R XOR V) */ /* w = p * 2^160i, i.e. shift p left by 160i bits */ bdShiftLeft(w, p, (160 * i)); bdOrBits(u, u, w); /* q = q + w */ printf("w(%d)=", i); bdPrintHex("", w,"\n"); } bdPrintHex("u = w(0)+w(1)+w(2) =\n", u,"\n"); bdModPowerOf2(u, 400); /* U = U mod (2^m) */ bdPrintHex("u = u mod (2^m) = // `ModPowerOf2' \n", u,"\n"); /* q = U OR 2^(m-1) OR 1 */ bdSetEqual(q, u); /* q = U */ bdSetBit(q, 400-1, 1); /* q = q OR 2^(m-1) */ bdSetBit(q, 0, 1); /* q = q OR 1 */ bdPrintHex("q = u OR 2^(m-1) OR 1 =\n", q,"\n"); /* Check that 2^(m-1) < q < 2^m */ printf("BitLength(q)=%d\n", bdBitLength(q)); bdSetZero(p); bdSetBit(p, 400-1, 1); /* p = 2^(m-1) */ assert(bdCompare(p, q) < 1); /* p < q */ bdSetZero(r); bdSetBit(r, 400, 1); /* r = 2^m */ assert(bdCompare(q, r) < 1); /* q < r */ printf("OK, checked that 2^(m-1) < q < 2^m\n"); /* Create a new set for Modular exponentiation test */ printf("\nMODULAR EXPONENTIATION...\n"); n = bdNew(); e = bdNew(); m = bdNew(); c = bdNew(); z = bdNew(); mz = bdNew(); cz = bdNew(); t = bdNew(); /* n is a prime modulus */ printf("Generating a prime...\n"); bdGeneratePrime(n, 513, 5, NULL, 0, my_rand); bdPrintHex("prime n=\n", n,"\n"); printf("bdBitLength(n)=%u bdSizeof(n)=%u\n", bdBitLength(n), bdSizeof(n)); /* Check primality using Fermat test (NB could fail!) */ res = fermat_test(n); if (res == 0) printf("WARNING: n failed Fermat test (this can happen!).\n"); /* exponent e and base m are some random numbers < n */ bdSetRandTest(e, bdSizeof(n) - 1); bdPrintHex("e=", e,"\n"); bdSetRandTest(m, bdSizeof(n) - 1); bdPrintHex("random m=\n", m,"\n"); /* Compute c = m^e mod n */ bdModExp(c, m, e, n); bdPrintHex("c = m^e mod n =\n", c,"\n"); /* Now use a random z and check that c.z^e == (m.z)^e (mod n) */ bdSetRandTest(z, bdSizeof(n)); bdPrintHex("random z=\n", z,"\n"); /* Compute cz = c.z^e mod n (Note use of temp t) */ bdModExp(t, z, e, n); bdModMult(cz, c, t, n); bdPrintHex("c.z^e mod n=\n", cz,"\n"); /* Compute mz = (m.z)^e mod n */ bdModMult(t, m, z, n); bdModExp(mz, t, e, n); bdPrintHex("(m.z)^e mod n==\n", mz,"\n"); if (!bdIsEqual(mz, cz)) printf("ERROR: (c.z^e == (m.z)^e (mod n) DOES NOT MATCH m'\n"); else printf("c.z^e == (m.z)^e (mod n) checks OK\n"); assert(bdIsEqual(mz, cz)); /* Repeat using constant-time exponentiation */ printf("Using constant-time exponentiation...\n"); /* Compute cz = c.z^e mod n (Note use of temp t) */ bdModExp_ct(t, z, e, n); bdModMult(cz, c, t, n); bdPrintHex("c.z^e mod n=\n", cz,"\n"); /* Compute mz = (m.z)^e mod n */ bdModMult(t, m, z, n); bdModExp_ct(mz, t, e, n); bdPrintHex("(m.z)^e mod n==\n", mz,"\n"); if (!bdIsEqual(mz, cz)) printf("ERROR: (c.z^e == (m.z)^e (mod n) DOES NOT MATCH m'\n"); else printf("c.z^e == (m.z)^e (mod n) checks OK\n"); assert(bdIsEqual(mz, cz)); printf("\nCONVERSIONS WITH HEX AND DECIMAL...\n"); /* Do some conversions with hex and decimal strings */ bdSetZero(u); bdConvFromHex(u, "DeadBeefCafeBabeBeddedDefacedDeafBeadFacade"); bdPrintHex("From Hex: ", u,"\n"); bdConvToHex(u, s, sizeof(s)); printf("To Hex: %s\n", s); bdConvFromDecimal(u, "1234567890123456789012345678901234567890"); bdPrintHex("From Decimal: ", u,"\n"); bdConvToDecimal(u, s, sizeof(s)); printf("To Decimal: %s\n", s); cmp = strcmp(s, "1234567890123456789012345678901234567890"); assert(cmp == 0); /* 987654321 x 81 = 80000000001 */ bdConvFromDecimal(u, "987654321"); bdShortMult(v, u, 81); bdConvToDecimal(v, s, sizeof(s)); printf("987654321 x 81 = %s\n", s); cmp = strcmp(s, "80000000001"); assert(cmp == 0); /* 123456789 x 9 + 10 = 1111111111 */ bdConvFromDecimal(u, "123456789"); bdShortMult(v, u, 9); bdShortAdd(w, v, 10); bdConvToDecimal(w, s, sizeof(s)); printf("123456789 x 9 + 10 = %s\n", s); cmp = strcmp(s, "1111111111"); assert(cmp == 0); /* Convert an empty decimal string to a BIGD */ bdSetShort(u, 0xfdfdfdfd); bdConvFromDecimal(u, ""); bdPrintHex("bdConvFromDecimal(u, '')=", u,"\n"); assert(bdIsZero(u)); /* ditto an empty hex string */ bdSetShort(u, 0xfdfdfdfd); bdConvFromHex(u, ""); bdPrintHex("bdConvFromHex(u, '')=", u,"\n"); assert(bdIsZero(u)); /* Convert zero BIGD value to a decimal string */ bdSetZero(u); bdConvToDecimal(u, s, sizeof(s)); printf("Decimal zero = '%s'\n", s); cmp = strcmp(s, "0"); assert(cmp == 0); /* ditto to a hex string */ bdConvToHex(u, s, sizeof(s)); printf("Hex zero = '%s'\n", s); cmp = strcmp(s, "0"); assert(cmp == 0); /* Convert a zero BIGD to an array of octets */ memset(bytes, 0xdf, sizeof(bytes)); bdSetZero(u); nbytes = bdConvToOctets(u, bytes, sizeof(bytes)); printf("bdConvToOctets returns %d: ", nbytes); /* show that all bytes are zero */ pr_bytesmsg("", bytes, sizeof(bytes)); assert(0 == bytes[sizeof(bytes)-1] && 0 == bytes[0]); /* Convert an zero-length array of octets to a BIGD */ bdSetShort(u, 0xfdfdfdfd); bdConvFromOctets(u, bytes, 0); bdPrintHex("bdConvFromOctets(nbytes=0)=", u,"\n"); assert(bdIsZero(u)); bdFree(&n); printf("\nJACOBI AND LEGENDRE SYMBOLS...\n"); /* Try computing some Jacobi and Legendre symbol values */ a = bdNew(); n = bdNew(); bdSetShort(a, 158); bdSetShort(n, 235); jac = bdJacobi(a, n); printf("Jacobi(158, 235)=%d\n", jac); assert(-1 == jac); bdSetShort(a, 2183); bdSetShort(n, 9907); jac = bdJacobi(a, n); printf("Jacobi(2183, 9907)=%d\n", jac); assert(1 == jac); bdSetShort(a, 1001); jac = bdJacobi(a, n); printf("Jacobi(1001, 9907)=%d\n", jac); assert(-1 == jac); bdShortMult(a, n, 10000); jac = bdJacobi(a, n); printf("Jacobi(10000 * 9907, 9907)=%d\n", jac); assert(0 == jac); printf("\nSQUARE AND SQUARE ROOT...\n"); /* Square a random number w = u^2 */ bdSetRandTest(u, 10); bdPrintHex("random u=\n", u, "\n"); bdSquare(w, u); bdPrintHex("(Square) u^2=", w, "\n"); /* check against product p = u * u */ bdMultiply(p, u, u); bdPrintHex("u*u=", p, "\n"); assert(bdIsEqual(p, w)); /* and check against power function */ bdPower(p, u, 2); bdPrintHex("u power 2=", p, "\n"); assert(bdIsEqual(p, w)); /* Compute integer square root [new in v2.1] */ bdSqrt(p, w); bdPrintHex("sqrt(u^2)=", p, "\n"); assert(bdIsEqual(p, u)); /* Now test sqrt(u^2 - 1) */ bdDecrement(w); bdPrintHex("u^2-1=", w, "\n"); bdSqrt(r, w); bdPrintHex("sqrt(u^2-1)=", r, "\n"); /* Check difference is 1 */ bdSubtract(q, p, r); bdPrintHex("difference=", q, " (expected 1)\n"); assert(bdShortCmp(q, 1) == 0); printf("\nCUBE AND CUBE ROOT...\n"); /* Cube a random number w = u^2 */ bdPrintHex("u=", u, "\n"); bdSquare(v, u); bdMultiply(w, v, u); bdPrintHex("(Cube) u^3=", w, "\n"); /* Check that power function works */ bdPower(v, u, 0); bdPrintHex("u^0=", v, "\n"); bdPower(v, u, 1); bdPrintHex("u^1=", v, "\n"); bdPower(v, u, 3); bdPrintHex("(Power) u^3=", v, "\n"); assert(bdIsEqual(v, w)); /* Compute integer cube root (new in [v2.3]) */ bdCubeRoot(p, w); bdPrintHex("cuberoot(u^3)=", p, "\n"); assert(bdIsEqual(p, u)); /* Now test cuberoot(u^2 - 1) */ bdDecrement(w); bdPrintHex("u^3-1=", w, "\n"); bdCubeRoot(r, w); bdPrintHex("cuberoot(u^3-1)=", r, "\n"); /* Check difference is 1 */ bdSubtract(q, p, r); bdPrintHex("difference=", q, " (expected 1)\n"); assert(bdShortCmp(q, 1) == 0); printf("\nQUICK-AND-DIRTY RANDOM NUMBERS (bitlen <= nbits)...\n"); for (i = 0; i < 5; i++) { int nbits = 128 + i; bdQuickRandBits(a, nbits); printf("q-rand(%d)=", nbits); bdPrintHex("", a, ""); printf("\t%d bits\n", bdBitLength(a)); } printf("\nPRINT BITS...\n"); bdSetShort(u, 0xdeadbeef); bdPrintHex("u=0x", u, " => "); bdPrintBits("'", u, "'b\n"); printf("(correct)= %s\n", "11011110101011011011111011101111"); bdSetShort(u, 0); bdPrintHex("u=0x", u, " => "); bdPrintBits("'", u, "'b\n"); bdSetShort(u, 1); bdPrintHex("u=0x", u, " => "); bdPrintBits("'", u, "'b\n"); printf("\nMODULAR SQUARE ROOT...\n"); printf("Select a 192-bit prime number...\n"); bdConvFromHex(p, "fffffffffffffffffffffffffffffffeffffffffffffffff"); bdPrintHex("p=0x", p, "\n"); bdPrintDecimal("p=", p, "\n"); res = bdIsPrime(p, 50); printf("bdIsPrime(p)=%d\n", res); assert(r != 0); nbits = bdBitLength(p); printf("p is %d bits\n", nbits); // Pick a number just smaller than p bdQuickRandBits(u, nbits - 1); bdPrintDecimal("u= ", u, "\n"); // Square it bdModSquare(w, u, p); bdPrintDecimal("w=u^2(mod p)= ", w, "\n"); /* Check we have a quadratic residue */ jac = bdJacobi(w, p); printf("Legendre symbol (w|p)=%d (expected 1)\n", jac); assert(jac == 1); /* Compute one modular square root */ bdModSqrt(x, w, p); bdPrintDecimal("x=sqrt(w)(mod p)=", x, "\n"); /* and the other */ bdSubtract(y, p, x); bdPrintDecimal("other root y=p-x=", y, "\n"); assert(bdIsEqual(x, u) || bdIsEqual(y, u)); if (bdIsEqual(x, u)) { printf("u == x => OK\n"); } else { printf("u == p-x => OK\n"); } printf("\nFind a number that is not a quadratic residue mod p...\n"); do { bdQuickRandBits(v, nbits - 1); } while (bdJacobi(v, p) != -1); bdPrintDecimal("v=", v, "\n"); printf("Legendre symbol (v|p)=%d (expected -1)\n", bdJacobi(v, p)); res = bdModSqrt(x, v, p); printf("bdModSqrt(v) returns %d (-1 => square root does not exist)\n", res); assert(res != 0); printf("\nADD AND SUBTRACT MODULO M ...\n"); /* Add two random numbers w = u + v mod p */ bdQuickRandBits(u, nbits - 1); bdQuickRandBits(v, nbits - 1); bdPrintDecimal("u= ", u, "\n"); bdPrintDecimal("v= ", v, "\n"); bdModAdd(w, u, v, p); bdPrintDecimal("w=u+v(mod p)=", w, "\n"); /* y = w - v */ bdModSub(y, w, v, p); bdPrintDecimal("y=w-v(mod p)=", y, "\n"); /* Check y == u */ printf("y == u => %s\n", (bdIsEqual(y, u) ? "OK" : "ERROR")); assert(bdIsEqual(y, u)); printf("\nDivide an integer by 2 modulo p using bdModHalve()...\n"); bdSetEqual(w, u); bdPrintDecimal("w= ", u, "\n"); bdModHalve(w, w, p); bdPrintDecimal("w=w/2(mod p)=", w, "\n"); // Check result bdModAdd(v, w, w, p); bdPrintDecimal("v=w+w(mod p)=", v, "\n"); printf("v == u => %s\n", (bdIsEqual(v, u) ? "OK" : "ERROR")); assert(bdIsEqual(v, u)); /* Finally, show the current version number */ printf("\nVERSION=%d\n", bdVersion()); printf("Compiled on [%s]\n", bdCompileTime()); /* Clear up */ bdFree(&a); bdFree(&n); bdFree(&e); bdFree(&m); bdFree(&c); bdFree(&z); bdFree(&mz); bdFree(&cz); bdFree(&t); /* Old method... */ //bdFree(&u); //bdFree(&v); //bdFree(&w); //bdFree(&q); //bdFree(&r); //bdFree(&p); //bdFree(&x); //bdFree(&y); /* New way in [v2.6] */ bdFreeVars(&u, &v, &w, &p, &q, &r, &x, &y); printf("OK, successfully completed tests.\n"); return 0; }