1
0
mirror of https://github.com/taigrr/arduinolibs synced 2025-01-18 04:33:12 -08:00

An implementation of Curve25519

This commit is contained in:
Rhys Weatherley
2015-03-12 18:59:55 +10:00
parent ccffa1ec87
commit 0f975de733
7 changed files with 1784 additions and 4 deletions

View File

@@ -0,0 +1,221 @@
/*
* 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 Curve25519 algorithm.
*/
#include <Crypto.h>
#include <Curve25519.h>
#include <RNG.h>
#include <string.h>
void printNumber(const char *name, const uint8_t *x)
{
static const char hexchars[] = "0123456789ABCDEF";
Serial.print(name);
Serial.print(" = ");
for (uint8_t posn = 0; posn < 32; ++posn) {
Serial.print(hexchars[(x[posn] >> 4) & 0x0F]);
Serial.print(hexchars[x[posn] & 0x0F]);
}
Serial.println();
}
// Check the eval() function using the test vectors from:
// https://tools.ietf.org/html/draft-turner-thecurve25519function-01
void testEval()
{
static uint8_t alice_private[32] = {
0x77, 0x07, 0x6d, 0x0a, 0x73, 0x18, 0xa5, 0x7d,
0x3c, 0x16, 0xc1, 0x72, 0x51, 0xb2, 0x66, 0x45,
0xdf, 0x4c, 0x2f, 0x87, 0xeb, 0xc0, 0x99, 0x2a,
0xb1, 0x77, 0xfb, 0xa5, 0x1d, 0xb9, 0x2c, 0x2a
};
static uint8_t const alice_public[32] = {
0x85, 0x20, 0xf0, 0x09, 0x89, 0x30, 0xa7, 0x54,
0x74, 0x8b, 0x7d, 0xdc, 0xb4, 0x3e, 0xf7, 0x5a,
0x0d, 0xbf, 0x3a, 0x0d, 0x26, 0x38, 0x1a, 0xf4,
0xeb, 0xa4, 0xa9, 0x8e, 0xaa, 0x9b, 0x4e, 0x6a
};
static uint8_t bob_private[32] = {
0x5d, 0xab, 0x08, 0x7e, 0x62, 0x4a, 0x8a, 0x4b,
0x79, 0xe1, 0x7f, 0x8b, 0x83, 0x80, 0x0e, 0xe6,
0x6f, 0x3b, 0xb1, 0x29, 0x26, 0x18, 0xb6, 0xfd,
0x1c, 0x2f, 0x8b, 0x27, 0xff, 0x88, 0xe0, 0xeb
};
static uint8_t const bob_public[32] = {
0xde, 0x9e, 0xdb, 0x7d, 0x7b, 0x7d, 0xc1, 0xb4,
0xd3, 0x5b, 0x61, 0xc2, 0xec, 0xe4, 0x35, 0x37,
0x3f, 0x83, 0x43, 0xc8, 0x5b, 0x78, 0x67, 0x4d,
0xad, 0xfc, 0x7e, 0x14, 0x6f, 0x88, 0x2b, 0x4f
};
static uint8_t const shared_secret[32] = {
0x4a, 0x5d, 0x9d, 0x5b, 0xa4, 0xce, 0x2d, 0xe1,
0x72, 0x8e, 0x3b, 0xf4, 0x80, 0x35, 0x0f, 0x25,
0xe0, 0x7e, 0x21, 0xc9, 0x47, 0xd1, 0x9e, 0x33,
0x76, 0xf0, 0x9b, 0x3c, 0x1e, 0x16, 0x17, 0x42
};
// Fix up the private keys by applying the standard masks.
alice_private[0] &= 0xF8;
alice_private[31] = (alice_private[31] & 0x7F) | 0x40;
bob_private[0] &= 0xF8;
bob_private[31] = (bob_private[31] & 0x7F) | 0x40;
// Evaluate the curve function and check the public keys.
uint8_t result[32];
Serial.println("Fixed test vectors:");
Serial.print("Computing Alice's public key ... ");
Serial.flush();
unsigned long start = micros();
Curve25519::eval(result, alice_private, 0);
unsigned long elapsed = micros() - start;
if (memcmp(result, alice_public, 32) == 0) {
Serial.print("ok");
} else {
Serial.println("failed");
printNumber("actual ", result);
printNumber("expected", alice_public);
}
Serial.print(" (elapsed ");
Serial.print(elapsed);
Serial.println(" us)");
Serial.print("Computing Bob's public key ... ");
Serial.flush();
start = micros();
Curve25519::eval(result, bob_private, 0);
elapsed = micros() - start;
if (memcmp(result, bob_public, 32) == 0) {
Serial.print("ok");
} else {
Serial.println("failed");
printNumber("actual ", result);
printNumber("expected", bob_public);
}
Serial.print(" (elapsed ");
Serial.print(elapsed);
Serial.println(" us)");
// Compute the shared secret from each side.
Serial.print("Computing Alice's shared secret ... ");
Serial.flush();
start = micros();
Curve25519::eval(result, alice_private, bob_public);
elapsed = micros() - start;
if (memcmp(result, shared_secret, 32) == 0) {
Serial.print("ok");
} else {
Serial.println("failed");
printNumber("actual ", result);
printNumber("expected", shared_secret);
}
Serial.print(" (elapsed ");
Serial.print(elapsed);
Serial.println(" us)");
Serial.print("Computing Bob's shared secret ... ");
Serial.flush();
start = micros();
Curve25519::eval(result, bob_private, alice_public);
elapsed = micros() - start;
if (memcmp(result, shared_secret, 32) == 0) {
Serial.print("ok");
} else {
Serial.println("failed");
printNumber("actual ", result);
printNumber("expected", shared_secret);
}
Serial.print(" (elapsed ");
Serial.print(elapsed);
Serial.println(" us)");
}
void testDH()
{
static uint8_t alice_k[32];
static uint8_t alice_f[32];
static uint8_t bob_k[32];
static uint8_t bob_f[32];
Serial.println("Diffie-Hellman key exchange:");
Serial.print("Generate random k/f for Alice ... ");
Serial.flush();
unsigned long start = micros();
Curve25519::dh1(alice_k, alice_f);
unsigned long elapsed = micros() - start;
Serial.print("elapsed ");
Serial.print(elapsed);
Serial.println(" us");
Serial.print("Generate random k/f for Bob ... ");
Serial.flush();
start = micros();
Curve25519::dh1(bob_k, bob_f);
elapsed = micros() - start;
Serial.print("elapsed ");
Serial.print(elapsed);
Serial.println(" us");
Serial.print("Generate shared secret for Alice ... ");
Serial.flush();
start = micros();
Curve25519::dh2(bob_k, alice_f);
elapsed = micros() - start;
Serial.print("elapsed ");
Serial.print(elapsed);
Serial.println(" us");
Serial.print("Generate shared secret for Bob ... ");
Serial.flush();
start = micros();
Curve25519::dh2(alice_k, bob_f);
elapsed = micros() - start;
Serial.print("elapsed ");
Serial.print(elapsed);
Serial.println(" us");
Serial.print("Check that the shared secrets match ... ");
if (memcmp(alice_k, bob_k, 32) == 0)
Serial.println("ok");
else
Serial.println("failed");
}
void setup()
{
Serial.begin(9600);
// Start the random number generator. We don't initialise a noise
// source here because we don't need one for testing purposes.
// Real DH applications should of course use a proper noise source.
RNG.begin("TestCurve25519 1.0", 500);
// Perform the tests.
testEval();
Serial.println();
testDH();
Serial.println();
}
void loop()
{
}

View File

@@ -0,0 +1,622 @@
/*
* 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 Curve25519 field mathematics independent
of the full curve operation itself.
*/
// Enable access to the internals of Curve25519 to test the raw field ops.
#define TEST_CURVE25519_FIELD_OPS 1
#include <Crypto.h>
#include <Curve25519.h>
#include <utility/ProgMemUtil.h>
#include <string.h>
// Copy some definitions from the Curve25519 class for convenience.
#define NUM_LIMBS (32 / sizeof(Curve25519::limb_t))
#define LIMB_BITS (8 * sizeof(Curve25519::limb_t))
#define limb_t Curve25519::limb_t
#define dlimb_t Curve25519::dlimb_t
#define INVERSE_LIMB (~((limb_t)0))
// For simpleMod() below we need a type that is 4 times the size of limb_t.
#if CURVE25519_LIMB_8BIT
#define qlimb_t uint32_t
#elif CURVE25519_LIMB_16BIT
#define qlimb_t uint64_t
#else
#define CURVE25519_NO_QLIMB 1
#endif
limb_t arg1[NUM_LIMBS];
limb_t arg2[NUM_LIMBS];
limb_t result[NUM_LIMBS];
limb_t result2[NUM_LIMBS * 2 + 1];
limb_t temp[NUM_LIMBS];
// Convert a decimal string in program memory into a number.
void fromString(limb_t *x, uint8_t size, const char *str)
{
uint8_t ch, posn;
memset(x, 0, sizeof(limb_t) * size);
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 < size; ++posn) {
carry += ((dlimb_t)x[posn]) * 10U;
x[posn] = (limb_t)carry;
carry >>= LIMB_BITS;
}
}
++str;
}
}
// Compare two numbers of NUM_LIMBS in length. Returns -1, 0, or 1.
int compare(const limb_t *x, const limb_t *y)
{
for (uint8_t posn = NUM_LIMBS; posn > 0; --posn) {
limb_t a = x[posn - 1];
limb_t b = y[posn - 1];
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, const char *y)
{
limb_t val[NUM_LIMBS];
fromString(val, NUM_LIMBS, y);
return compare(x, val);
}
void printNumber(const char *name, const limb_t *x)
{
static const char hexchars[] = "0123456789ABCDEF";
Serial.print(name);
Serial.print(" = ");
for (uint8_t posn = 0; posn < NUM_LIMBS; ++posn) {
for (uint8_t bit = LIMB_BITS; bit > 0; ) {
bit -= 4;
Serial.print(hexchars[(x[posn] >> bit) & 0x0F]);
}
Serial.print(' ');
}
Serial.println();
}
// Standard numbers that are useful in field operation 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[] PROGMEM = "31415926535897932384626433832795028841971693993751058209749445923078164062862"; // 77 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";
// Table of useful numbers less than 2^255 - 19.
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,
num_2_255_m253,
num_2_255_m20,
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])
void printProgMem(const char *str)
{
uint8_t ch;
while ((ch = pgm_read_byte((uint8_t *)str)) != '\0') {
Serial.print((char)ch);
++str;
}
}
// Simple implementation of modular addition to cross-check the library.
void simpleAdd(limb_t *result, const limb_t *x, const limb_t *y)
{
uint8_t posn;
dlimb_t carry = 0;
for (posn = 0; posn < NUM_LIMBS; ++posn) {
carry += x[posn];
carry += y[posn];
result[posn] = (limb_t)carry;
carry >>= LIMB_BITS;
}
if (compare(result, num_2_255_m19) >= 0) {
// Subtract 2^255 - 19 to get the final result.
// Same as add 19 and then subtract 2^255.
carry = 19;
for (posn = 0; posn < NUM_LIMBS; ++posn) {
carry += result[posn];
result[posn] = (limb_t)carry;
carry >>= LIMB_BITS;
}
result[NUM_LIMBS - 1] -= ((limb_t)1) << (LIMB_BITS - 1);
}
}
// Simple implementation of subtraction to cross-check the library.
// Note: this does not reduce the result modulo 2^255 - 19 and we
// assume that x is greater than or equal to y.
void simpleSub(limb_t *result, const limb_t *x, const limb_t *y)
{
uint8_t posn;
dlimb_t borrow = 0;
for (posn = 0; posn < NUM_LIMBS; ++posn) {
borrow = ((dlimb_t)x[posn]) - y[posn] - borrow;
result[posn] = (limb_t)borrow;
borrow = (borrow >> LIMB_BITS) != 0;
}
}
// Simple implementation of multiplication to cross-check the library.
// Note: this does not reduce the result modulo 2^255 - 19.
// The "result" buffer must contain at least NUM_LIMBS * 2 limbs.
void simpleMul(limb_t *result, const limb_t *x, const limb_t *y)
{
memset(result, 0, NUM_LIMBS * 2 * sizeof(limb_t));
for (uint8_t i = 0; i < NUM_LIMBS; ++i) {
for (uint8_t j = 0; j < NUM_LIMBS; ++j) {
uint8_t n = i + j;
dlimb_t carry =
((dlimb_t)x[i]) * y[j] + result[n];
result[n] = (limb_t)carry;
carry >>= LIMB_BITS;
++n;
while (carry != 0 && n < (NUM_LIMBS * 2)) {
carry += result[n];
result[n] = (limb_t)carry;
carry >>= LIMB_BITS;
++n;
}
}
}
}
#if defined(CURVE25519_NO_QLIMB)
// Quick check to correct the estimate on a quotient word.
static inline limb_t correctEstimate
(limb_t q, limb_t y1, limb_t y2, dlimb_t x01, limb_t x2)
{
// Algorithm D from section 4.3.1 of "The Art Of Computer Programming",
// D. Knuth, Volume 2, "Seminumerical Algorithms", Second Edition, 1981.
//
// We want to check if (y2 * q) > ((x01 - y1 * q) * b + x2) where
// b is (1 << LIMB_BITS). If it is, then q must be reduced by 1.
//
// One wrinkle that isn't obvious from Knuth's description is that it
// is possible for (x01 - y1 * q) >= b, especially in the case where
// x0 = y1 and q = b - 1. This will cause an overflow of the intermediate
// double-word result ((x01 - y1 * q) * b).
//
// In assembly language, we could use the carry flag to detect when
// (x01 - y1 * q) * b overflows, but we can't access the carry flag
// in C++. So we have to account for the carry in a different way here.
// Calculate the remainder using the estimated quotient.
dlimb_t r = x01 - ((dlimb_t)y1) * q;
// If there will be a double-word carry when we calculate (r * b),
// then (y2 * q) is obviously going to be less than (r * b), so we
// can stop here. The estimated quotient is correct.
if (r & (((dlimb_t)INVERSE_LIMB) << LIMB_BITS))
return q;
// Bail out if (y2 * q) <= (r * b + x2). The estimate is correct.
dlimb_t y2q = ((dlimb_t)y2) * q;
if (y2q <= ((r << LIMB_BITS) + x2))
return q;
// Correct for the estimated quotient being off by 1.
--q;
// Now repeat the check to correct for q values that are off by 2.
r += y1; // r' = (x01 - y1 * (q - 1)) = (x01 - y1 * q + y2) = r + y1
if (r & (((dlimb_t)INVERSE_LIMB) << LIMB_BITS))
return q;
// y2q' = (y2 * (q - 1)) = (y2 * q - y2) = y2q - y2
if ((y2q - y2) <= ((r << LIMB_BITS) + x2))
return q;
// Perform the final correction for q values that are off by 2.
return q - 1;
}
#endif
// Simple implementation of modular division to cross-check the library.
// Calling this "simple" is a bit of a misnomer. It is a full implementation
// of Algorithm D from section 4.3.1 of "The Art Of Computer Programming",
// D. Knuth, Volume 2, "Seminumerical Algorithms", Second Edition, 1981.
// This is quite slow on embedded platforms, but it should be correct.
// Note: "x" is assumed to be (NUM_LIMBS * 2 + 1) limbs in size because
// we need a limb for the extra leading zero word added by step D1.
void simpleMod(limb_t *x)
{
limb_t divisor[NUM_LIMBS];
uint8_t j, k;
// Step D1. Normalize.
// The divisor (2^255 - 19) and "x" need to be shifted left until
// the top-most bit of the divisor is 1. Since we know that the
// next-to-top-most bit of (2^255 - 19) is already 1 and the top-most
// bit of "x" is zero, shifting everything into place is pretty easy.
fromString(divisor, NUM_LIMBS, num_2_255_m19_x2);
for (j = (NUM_LIMBS * 2); j > 1; --j) {
x[j - 1] = (x[j - 1] << 1) | (x[j - 2] >> (LIMB_BITS - 1));
}
x[0] <<= 1;
x[NUM_LIMBS * 2] = 0; // Extra leading word.
// Step D2/D7. Loop on j
for (j = 0; j <= NUM_LIMBS; ++j) {
// Step D3. Calculate an estimate of the top-most quotient word.
limb_t *u = x + NUM_LIMBS * 2 - 2 - j;
limb_t *v = divisor + NUM_LIMBS - 2;
limb_t q;
dlimb_t uword = ((((dlimb_t)u[2]) << LIMB_BITS) + u[1]);
if (u[2] == v[1])
q = ~((limb_t)0);
else
q = (limb_t)(uword / v[1]);
// Step D3, part 2. Correct the estimate downwards by 1 or 2.
// One subtlety of Knuth's algorithm is that it looks like the test
// is working with double-word quantities but it is actually using
// double-word plus a carry bit. So we need to use qlimb_t for this.
#if !defined(CURVE25519_NO_QLIMB)
qlimb_t test = ((((qlimb_t)uword) - ((dlimb_t)q) * v[1]) << LIMB_BITS) + u[0];
if ((((dlimb_t)q) * v[0]) > test) {
--q;
test = ((((qlimb_t)uword) - ((dlimb_t)q) * v[1]) << LIMB_BITS) + u[0];
if ((((dlimb_t)q) * v[0]) > test)
--q;
}
#else
// 32-bit platform - we don't have a 128-bit numeric type so we have
// to calculate the estimate in another way to preserve the carry bit.
q = correctEstimate(q, v[0], v[1], uword, u[0]);
#endif
// Step D4. Multiply and subtract.
u = x + (NUM_LIMBS - j);
v = divisor;
dlimb_t carry = 0;
dlimb_t borrow = 0;
for (k = 0; k < NUM_LIMBS; ++k) {
carry += ((dlimb_t)v[k]) * q;
borrow = ((dlimb_t)u[k]) - ((limb_t)carry) - borrow;
u[k] = (dlimb_t)borrow;
carry >>= LIMB_BITS;
borrow = ((borrow >> LIMB_BITS) != 0);
}
borrow = ((dlimb_t)u[k]) - ((limb_t)carry) - borrow;
u[k] = (dlimb_t)borrow;
// Step D5. Test remainder. Nothing further to do if no borrow.
if ((borrow >> LIMB_BITS) == 0)
continue;
// Step D6. Borrow occurred: add back.
carry = 0;
for (k = 0; k < NUM_LIMBS; ++k) {
carry += u[k];
carry += v[k];
u[k] = (limb_t)carry;
carry >>= LIMB_BITS;
}
u[k] += (limb_t)carry;
}
// Step D8. Unnormalize.
// Shift the remainder right by 1 bit to undo the earlier left shift.
for (j = 0; j < (NUM_LIMBS - 1); ++j) {
x[j] = (x[j] >> 1) | (x[j + 1] << (LIMB_BITS - 1));
}
x[NUM_LIMBS - 1] >>= 1;
}
void testAdd(const char *x, const char *y)
{
printProgMem(x);
Serial.print(" + ");
printProgMem(y);
Serial.print(": ");
Serial.flush();
fromString(arg1, NUM_LIMBS, x);
fromString(arg2, NUM_LIMBS, y);
Curve25519::add(result, arg1, arg2);
simpleAdd(result2, arg1, arg2);
if (compare(result, result2) == 0) {
Serial.println("ok");
} else {
Serial.println("failed");
printNumber("actual ", result);
printNumber("expected", result2);
}
}
void testAdd()
{
Serial.println("Addition:");
foreach_number (x) {
foreach_number (y) {
testAdd(x, y);
}
}
Serial.println();
}
void testSub(const char *x, const char *y)
{
printProgMem(x);
Serial.print(" - ");
printProgMem(y);
Serial.print(": ");
Serial.flush();
fromString(arg1, NUM_LIMBS, x);
fromString(arg2, NUM_LIMBS, y);
Curve25519::sub(result, arg1, arg2);
if (compare(arg1, arg2) >= 0) {
// First argument is larger than the second.
simpleSub(result2, arg1, arg2);
} else {
// First argument is smaller than the second.
// Compute arg1 + (2^255 - 19 - arg2).
fromString(temp, NUM_LIMBS, num_2_255_m19);
simpleSub(result2, temp, arg2);
simpleAdd(result2, arg1, result2);
}
if (compare(result, result2) == 0) {
Serial.println("ok");
} else {
Serial.println("failed");
printNumber("actual ", result);
printNumber("expected", result2);
}
}
void testSub()
{
Serial.println("Subtraction:");
foreach_number (x) {
foreach_number (y) {
testSub(x, y);
}
}
Serial.println();
}
void testMul(const char *x, const char *y)
{
printProgMem(x);
Serial.print(" * ");
printProgMem(y);
Serial.print(": ");
Serial.flush();
fromString(arg1, NUM_LIMBS, x);
fromString(arg2, NUM_LIMBS, y);
if (compare(arg1, arg2) != 0)
Curve25519::mul(result, arg1, arg2);
else
Curve25519::square(result, arg1);
simpleMul(result2, arg1, arg2);
simpleMod(result2);
if (compare(result, result2) == 0) {
Serial.println("ok");
} else {
Serial.println("failed");
printNumber("actual ", result);
printNumber("expected", result2);
}
}
void testMul()
{
Serial.println("Multiplication:");
foreach_number (x) {
foreach_number (y) {
testMul(x, y);
}
}
Serial.println();
}
void testMulA24(const char *x)
{
printProgMem(x);
Serial.print(" * ");
printProgMem(num_a24);
Serial.print(": ");
Serial.flush();
fromString(arg1, NUM_LIMBS, x);
fromString(arg2, NUM_LIMBS, num_a24);
Curve25519::mulA24(result, arg1);
simpleMul(result2, arg1, arg2);
simpleMod(result2);
if (compare(result, result2) == 0) {
Serial.println("ok");
} else {
Serial.println("failed");
printNumber("actual ", result);
printNumber("expected", result2);
}
}
void testMulA24()
{
Serial.println("Multiplication by a24:");
foreach_number (x) {
testMulA24(x);
}
Serial.println();
}
void testSwap(const char *x, const char *y, uint8_t select)
{
printProgMem(x);
Serial.print(" <-> ");
printProgMem(y);
Serial.print(": ");
Serial.flush();
fromString(arg1, NUM_LIMBS, x);
fromString(arg2, NUM_LIMBS, y);
memcpy(result, arg1, NUM_LIMBS * sizeof(limb_t));
memcpy(result2, arg2, NUM_LIMBS * sizeof(limb_t));
// Swap the values using the selection bit.
Curve25519::cswap(select, result, result2);
bool ok = compare(result, arg2) == 0 && compare(result2, arg1) == 0;
// Don't swap the values back yet.
Curve25519::cswap(0, result, result2);
if (ok)
ok = compare(result, arg2) == 0 && compare(result2, arg1) == 0;
// Swap the values back.
Curve25519::cswap(select, result, result2);
if (ok)
ok = compare(result, arg1) == 0 && compare(result2, arg2) == 0;
// No swap.
Curve25519::cswap(0, result, result2);
if (ok)
ok = compare(result, arg1) == 0 && compare(result2, arg2) == 0;
if (ok) {
Serial.println("ok");
} else {
Serial.println("failed");
}
}
void testSwap()
{
Serial.println("Swap:");
uint8_t bit = 0;
foreach_number (x) {
foreach_number (y) {
testSwap(x, y, ((uint8_t)1) << bit);
bit = (bit + 1) % 8;
}
}
Serial.println();
}
void testRecip(const char *x)
{
printProgMem(x);
Serial.print("^-1");
Serial.print(": ");
Serial.flush();
fromString(arg1, NUM_LIMBS, x);
Curve25519::recip(result, arg1);
bool ok;
if (compare(arg1, num_0) == 0) {
// 0^-1 = 0
ok = (compare(result, num_0) == 0);
} else {
// Multiply the result with arg1 - we expect 1 as the result.
Curve25519::mul(result2, result, arg1);
ok = (compare(result2, num_1) == 0);
}
if (ok) {
Serial.println("ok");
} else {
Serial.println("failed");
printNumber("actual", result);
}
}
void testRecip()
{
Serial.println("Reciprocal:");
foreach_number (x) {
testRecip(x);
}
Serial.println();
}
void setup()
{
Serial.begin(9600);
testAdd();
testSub();
testMul();
testMulA24();
testSwap();
testRecip();
}
void loop()
{
}