mirror of
https://github.com/taigrr/arduinolibs
synced 2025-01-18 04:33:12 -08:00
Unit tests and bug fixes for pack/unpack functions
This commit is contained in:
parent
6790ef99d3
commit
a936aa3e4a
@ -65,7 +65,7 @@ void BigNumberUtil::unpackLE(limb_t *limbs, size_t count,
|
|||||||
count *= sizeof(limb_t);
|
count *= sizeof(limb_t);
|
||||||
if (len < count) {
|
if (len < count) {
|
||||||
memcpy(limbs, bytes, len);
|
memcpy(limbs, bytes, len);
|
||||||
memset(limbs + len, 0, count - len);
|
memset(((uint8_t *)limbs) + len, 0, count - len);
|
||||||
} else {
|
} else {
|
||||||
memcpy(limbs, bytes, count);
|
memcpy(limbs, bytes, count);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,365 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Southern Storm Software, Pty Ltd.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* 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 OR COPYRIGHT HOLDERS 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
This example runs tests on the utility functions in BigNumberUtil.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Crypto.h>
|
||||||
|
#include <BigNumberUtil.h>
|
||||||
|
#include <utility/ProgMemUtil.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define NUM_SIZE_512BIT (64 / sizeof(limb_t))
|
||||||
|
#define LIMB_BITS (sizeof(limb_t) * 8)
|
||||||
|
|
||||||
|
// Convert a decimal string in program memory into a big number.
|
||||||
|
void fromString(limb_t *x, size_t xsize, const char *str)
|
||||||
|
{
|
||||||
|
uint8_t ch;
|
||||||
|
size_t posn;
|
||||||
|
memset(x, 0, sizeof(limb_t) * xsize);
|
||||||
|
while ((ch = pgm_read_byte((uint8_t *)str)) != '\0') {
|
||||||
|
if (ch >= '0' && ch <= '9') {
|
||||||
|
// Quick and simple method to multiply by 10 and add the new digit.
|
||||||
|
dlimb_t carry = ch - '0';
|
||||||
|
for (posn = 0; posn < xsize; ++posn) {
|
||||||
|
carry += ((dlimb_t)x[posn]) * 10U;
|
||||||
|
x[posn] = (limb_t)carry;
|
||||||
|
carry >>= LIMB_BITS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert a decimal string in program memory into a byte array.
|
||||||
|
void bytesFromString(uint8_t *x, size_t xsize, const char *str)
|
||||||
|
{
|
||||||
|
uint8_t ch;
|
||||||
|
size_t posn;
|
||||||
|
memset(x, 0, sizeof(limb_t) * xsize);
|
||||||
|
while ((ch = pgm_read_byte((uint8_t *)str)) != '\0') {
|
||||||
|
if (ch >= '0' && ch <= '9') {
|
||||||
|
// Quick and simple method to multiply by 10 and add the new digit.
|
||||||
|
uint16_t carry = ch - '0';
|
||||||
|
for (posn = 0; posn < xsize; ++posn) {
|
||||||
|
carry += ((uint16_t)x[posn]) * 10U;
|
||||||
|
x[posn] = (uint8_t)carry;
|
||||||
|
carry >>= 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare two big numbers. Returns -1, 0, or 1.
|
||||||
|
int compare(const limb_t *x, size_t xsize, const limb_t *y, size_t ysize)
|
||||||
|
{
|
||||||
|
limb_t a, b;
|
||||||
|
while (xsize > 0 || ysize > 0) {
|
||||||
|
if (xsize > ysize) {
|
||||||
|
--xsize;
|
||||||
|
a = x[xsize];
|
||||||
|
b = 0;
|
||||||
|
} else if (ysize > xsize) {
|
||||||
|
--ysize;
|
||||||
|
a = 0;
|
||||||
|
b = y[ysize];
|
||||||
|
} else {
|
||||||
|
--xsize;
|
||||||
|
--ysize;
|
||||||
|
a = x[xsize];
|
||||||
|
b = y[ysize];
|
||||||
|
}
|
||||||
|
if (a < b)
|
||||||
|
return -1;
|
||||||
|
else if (a > b)
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare two numbers where one is a decimal string. Returns -1, 0, or 1.
|
||||||
|
int compare(const limb_t *x, size_t xsize, const char *y)
|
||||||
|
{
|
||||||
|
limb_t val[NUM_SIZE_512BIT];
|
||||||
|
fromString(val, NUM_SIZE_512BIT, y);
|
||||||
|
return compare(x, xsize, val, NUM_SIZE_512BIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prints a number in hexadecimal.
|
||||||
|
void printNumber(const char *name, const limb_t *x, size_t xsize)
|
||||||
|
{
|
||||||
|
static const char hexchars[] = "0123456789ABCDEF";
|
||||||
|
Serial.print(name);
|
||||||
|
Serial.print(" = ");
|
||||||
|
for (size_t posn = 0; posn < xsize; ++posn) {
|
||||||
|
for (uint8_t bit = LIMB_BITS; bit > 0; ) {
|
||||||
|
bit -= 4;
|
||||||
|
Serial.print(hexchars[(x[xsize - 1 - posn] >> bit) & 0x0F]);
|
||||||
|
}
|
||||||
|
Serial.print(' ');
|
||||||
|
}
|
||||||
|
Serial.println();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Standard numbers that are useful in big number tests.
|
||||||
|
char const num_0[] PROGMEM = "0";
|
||||||
|
char const num_1[] PROGMEM = "1";
|
||||||
|
char const num_2[] PROGMEM = "2";
|
||||||
|
char const num_4[] PROGMEM = "4";
|
||||||
|
char const num_5[] PROGMEM = "5";
|
||||||
|
char const num_128[] PROGMEM = "128";
|
||||||
|
char const num_256[] PROGMEM = "256";
|
||||||
|
char const num_2_64_m7[] PROGMEM = "18446744073709551609"; // 2^64 - 7
|
||||||
|
char const num_2_129_m5[] PROGMEM = "680564733841876926926749214863536422907"; // 2^129 - 5
|
||||||
|
char const num_pi_77[] PROGMEM = "31415926535897932384626433832795028841971693993751058209749445923078164062862"; // 77 digits of pi
|
||||||
|
char const num_pi_154[] PROGMEM = "3141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647093844609550582231725359408128481"; // 154 digits of pi
|
||||||
|
char const num_2_255_m253[] PROGMEM = "57896044618658097711785492504343953926634992332820282019728792003956564819715"; // 2^255 - 253
|
||||||
|
char const num_2_255_m20[] PROGMEM = "57896044618658097711785492504343953926634992332820282019728792003956564819948"; // 2^255 - 20
|
||||||
|
char const num_2_255_m19[] PROGMEM = "57896044618658097711785492504343953926634992332820282019728792003956564819949"; // 2^255 - 19
|
||||||
|
char const num_2_255_m19_x2[] PROGMEM = "115792089237316195423570985008687907853269984665640564039457584007913129639898"; // (2^255 - 19) * 2
|
||||||
|
char const num_a24[] PROGMEM = "121665";
|
||||||
|
char const num_2_512_m19[] PROGMEM = "13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084077"; // (2^512 - 19)
|
||||||
|
|
||||||
|
// Table of useful numbers.
|
||||||
|
const char * const numbers[] = {
|
||||||
|
num_0,
|
||||||
|
num_1,
|
||||||
|
num_2,
|
||||||
|
num_4,
|
||||||
|
num_5,
|
||||||
|
num_128,
|
||||||
|
num_256,
|
||||||
|
num_2_64_m7,
|
||||||
|
num_2_129_m5,
|
||||||
|
num_pi_77,
|
||||||
|
num_2_255_m253,
|
||||||
|
num_2_255_m20,
|
||||||
|
num_pi_154,
|
||||||
|
num_2_512_m19,
|
||||||
|
0
|
||||||
|
};
|
||||||
|
#define numbers_count ((sizeof(numbers) / sizeof(numbers[0])) - 1)
|
||||||
|
|
||||||
|
#define foreach_number(var) \
|
||||||
|
const char *var = numbers[0]; \
|
||||||
|
for (unsigned index##var = 0; index##var < numbers_count; \
|
||||||
|
++index##var, var = numbers[index##var])
|
||||||
|
|
||||||
|
// Print a decimal string from program memory.
|
||||||
|
void printProgMem(const char *str)
|
||||||
|
{
|
||||||
|
uint8_t ch;
|
||||||
|
while ((ch = pgm_read_byte((uint8_t *)str)) != '\0') {
|
||||||
|
Serial.print((char)ch);
|
||||||
|
++str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine if an array consists of all zero bytes.
|
||||||
|
static bool isAllZero(const uint8_t *bytes, size_t size)
|
||||||
|
{
|
||||||
|
while (size > 0) {
|
||||||
|
if (*bytes++ != 0)
|
||||||
|
return false;
|
||||||
|
--size;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine if an array consists of all 0xBA bytes.
|
||||||
|
static bool isAllBad(const uint8_t *bytes, size_t size)
|
||||||
|
{
|
||||||
|
while (size > 0) {
|
||||||
|
if (*bytes++ != 0xBA)
|
||||||
|
return false;
|
||||||
|
--size;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !BIGNUMBER_LIMB_8BIT
|
||||||
|
|
||||||
|
static bool isAllBad(const limb_t *limbs, size_t size)
|
||||||
|
{
|
||||||
|
return isAllBad((uint8_t *)limbs, size * sizeof(limb_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Truncate the limb representation of a number to a specific byte length.
|
||||||
|
static void truncateNumber(limb_t *limbs, size_t bytes)
|
||||||
|
{
|
||||||
|
size_t posn = NUM_SIZE_512BIT * sizeof(limb_t);
|
||||||
|
size_t posn2;
|
||||||
|
limb_t mask;
|
||||||
|
while (posn > bytes) {
|
||||||
|
--posn;
|
||||||
|
posn2 = posn % sizeof(limb_t);
|
||||||
|
if (posn2 == 0)
|
||||||
|
mask = 0;
|
||||||
|
else if (posn2 == 1)
|
||||||
|
mask = 0x000000FF;
|
||||||
|
else if (posn2 == 2)
|
||||||
|
mask = 0x0000FFFF;
|
||||||
|
else
|
||||||
|
mask = 0x00FFFFFF;
|
||||||
|
limbs[posn / sizeof(limb_t)] &= mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test byte array pack and unpack operations.
|
||||||
|
void testPackUnpack(void)
|
||||||
|
{
|
||||||
|
limb_t num[NUM_SIZE_512BIT];
|
||||||
|
limb_t tnum[NUM_SIZE_512BIT];
|
||||||
|
limb_t limbs[NUM_SIZE_512BIT];
|
||||||
|
uint8_t bytes[64];
|
||||||
|
uint8_t expected[64];
|
||||||
|
size_t posn;
|
||||||
|
uint8_t temp;
|
||||||
|
|
||||||
|
foreach_number(x) {
|
||||||
|
// What number are we on?
|
||||||
|
Serial.print("pack ");
|
||||||
|
printProgMem(x);
|
||||||
|
Serial.print(": ");
|
||||||
|
Serial.flush();
|
||||||
|
bool ok = true;
|
||||||
|
|
||||||
|
// Convert the number into limbs and bytes in a simple way.
|
||||||
|
fromString(num, NUM_SIZE_512BIT, x);
|
||||||
|
bytesFromString(expected, sizeof(expected), x);
|
||||||
|
|
||||||
|
// Check packLE() and unpackLE() against the expected values.
|
||||||
|
for (posn = 0; posn < 64; ++posn) {
|
||||||
|
memset(bytes, 0xBA, sizeof(bytes));
|
||||||
|
BigNumberUtil::packLE(bytes, posn, num, NUM_SIZE_512BIT);
|
||||||
|
if (memcmp(bytes, expected, posn) != 0)
|
||||||
|
ok = false;
|
||||||
|
if (!isAllBad(bytes + posn, sizeof(bytes) - posn))
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
for (posn = 0; posn < NUM_SIZE_512BIT; ++posn) {
|
||||||
|
BigNumberUtil::packLE(bytes, sizeof(bytes), num, posn);
|
||||||
|
if (memcmp(bytes, expected, posn * sizeof(limb_t)) != 0)
|
||||||
|
ok = false;
|
||||||
|
if (!isAllZero(bytes + posn * sizeof(limb_t), sizeof(bytes) - posn * sizeof(limb_t)))
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
for (posn = 0; posn < NUM_SIZE_512BIT; ++posn) {
|
||||||
|
memset(limbs, 0xBA, sizeof(limbs));
|
||||||
|
BigNumberUtil::unpackLE(limbs, posn, expected, sizeof(expected));
|
||||||
|
if (memcmp(limbs, num, posn) != 0)
|
||||||
|
ok = false;
|
||||||
|
if (!isAllBad(limbs + posn, NUM_SIZE_512BIT - posn))
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
for (posn = 0; posn < 64; ++posn) {
|
||||||
|
memset(limbs, 0xBA, sizeof(limbs));
|
||||||
|
BigNumberUtil::unpackLE(limbs, NUM_SIZE_512BIT,
|
||||||
|
expected, sizeof(expected) - posn);
|
||||||
|
memcpy(tnum, num, sizeof(num));
|
||||||
|
truncateNumber(tnum, sizeof(expected) - posn);
|
||||||
|
if (memcmp(limbs, tnum, NUM_SIZE_512BIT) != 0)
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
for (posn = 0; posn < NUM_SIZE_512BIT; ++posn) {
|
||||||
|
memset(limbs, 0xBA, sizeof(limbs));
|
||||||
|
BigNumberUtil::unpackLE(limbs, posn, expected, sizeof(expected));
|
||||||
|
if (memcmp(limbs, num, posn * sizeof(limb_t)) != 0)
|
||||||
|
ok = false;
|
||||||
|
if (!isAllBad(limbs + posn, NUM_SIZE_512BIT - posn))
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap the expected byte array into big-endian order.
|
||||||
|
for (posn = 0; posn < 32; ++posn) {
|
||||||
|
temp = expected[posn];
|
||||||
|
expected[posn] = expected[63 - posn];
|
||||||
|
expected[63 - posn] = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check packBE() and unpackBE() against the expected values.
|
||||||
|
for (posn = 0; posn < 64; ++posn) {
|
||||||
|
memset(bytes, 0xBA, sizeof(bytes));
|
||||||
|
BigNumberUtil::packBE(bytes, posn, num, NUM_SIZE_512BIT);
|
||||||
|
if (memcmp(bytes, expected + 64 - posn, posn) != 0)
|
||||||
|
ok = false;
|
||||||
|
if (!isAllBad(bytes + posn, sizeof(bytes) - posn))
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
for (posn = 0; posn < NUM_SIZE_512BIT; ++posn) {
|
||||||
|
BigNumberUtil::packBE(bytes, sizeof(bytes), num, posn);
|
||||||
|
if (memcmp(bytes + 64 - posn * sizeof(limb_t),
|
||||||
|
expected + 64 - posn * sizeof(limb_t),
|
||||||
|
posn * sizeof(limb_t)) != 0)
|
||||||
|
ok = false;
|
||||||
|
if (!isAllZero(bytes, sizeof(bytes) - posn * sizeof(limb_t)))
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
for (posn = 0; posn < NUM_SIZE_512BIT; ++posn) {
|
||||||
|
memset(limbs, 0xBA, sizeof(limbs));
|
||||||
|
BigNumberUtil::unpackBE(limbs, posn, expected, sizeof(expected));
|
||||||
|
if (memcmp(limbs, num, posn) != 0)
|
||||||
|
ok = false;
|
||||||
|
if (!isAllBad(limbs + posn, NUM_SIZE_512BIT - posn))
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
for (posn = 0; posn < 64; ++posn) {
|
||||||
|
memset(limbs, 0xBA, sizeof(limbs));
|
||||||
|
BigNumberUtil::unpackBE(limbs, NUM_SIZE_512BIT,
|
||||||
|
expected + posn, sizeof(expected) - posn);
|
||||||
|
memcpy(tnum, num, sizeof(num));
|
||||||
|
truncateNumber(tnum, sizeof(expected) - posn);
|
||||||
|
if (memcmp(limbs, tnum, NUM_SIZE_512BIT) != 0)
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
for (posn = 0; posn < NUM_SIZE_512BIT; ++posn) {
|
||||||
|
memset(limbs, 0xBA, sizeof(limbs));
|
||||||
|
BigNumberUtil::unpackBE(limbs, posn, expected, sizeof(expected));
|
||||||
|
if (memcmp(limbs, num, posn * sizeof(limb_t)) != 0)
|
||||||
|
ok = false;
|
||||||
|
if (!isAllBad(limbs + posn, NUM_SIZE_512BIT - posn))
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Report the results.
|
||||||
|
if (ok)
|
||||||
|
Serial.println("ok");
|
||||||
|
else
|
||||||
|
Serial.println("failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
Serial.begin(9600);
|
||||||
|
|
||||||
|
testPackUnpack();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user