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

Ed25519 signature algorithm

This commit is contained in:
Rhys Weatherley 2015-04-18 08:42:37 +10:00
parent a936aa3e4a
commit 786e52f923
13 changed files with 1672 additions and 127 deletions

View File

@ -32,7 +32,7 @@
\li Authenticated encryption with associated data (AEAD): ChaChaPoly, GCM
\li Hash algorithms: SHA1, SHA256, SHA512, SHA3_256, SHA3_512, BLAKE2s, BLAKE2b (regular and HMAC modes)
\li Message authenticators: Poly1305, GHASH
\li Public key algorithms: Curve25519
\li Public key algorithms: Curve25519, Ed25519
\li Random number generation: \link RNGClass RNG\endlink, TransistorNoiseSource, RingOscillatorNoiseSource
All cryptographic algorithms have been optimized for 8-bit Arduino platforms
@ -96,6 +96,9 @@ Ardunino Mega 2560 running at 16 MHz are similar:
<tr><td>Curve25519::eval()</td><td align="right">3119ms</td><td colspan="3">Raw curve evaluation</td></tr>
<tr><td>Curve25519::dh1()</td><td align="right">3121ms</td><td colspan="3">First half of Diffie-Hellman key agreement</td></tr>
<tr><td>Curve25519::dh2()</td><td align="right">3120ms</td><td colspan="3">Second half of Diffie-Hellman key agreement</td></tr>
<tr><td>Ed25519::sign()</td><td align="right">5688ms</td><td colspan="3">Digital signature generation</td></tr>
<tr><td>Ed25519::verify()</td><td align="right">9030ms</td><td colspan="3">Digital signature verification</td></tr>
<tr><td>Ed25519::derivePublicKey()</td><td align="right">5642ms</td><td colspan="3">Derive a public key from a private key</td></tr>
</table>
Where a cipher supports more than one key size (such as ChaCha), the values

View File

@ -97,7 +97,7 @@ realtime clock and the LCD library to implement an alarm clock.
\li Authenticated encryption with associated data (AEAD): ChaChaPoly, GCM
\li Hash algorithms: SHA1, SHA256, SHA512, SHA3_256, SHA3_512, BLAKE2s, BLAKE2b (regular and HMAC modes)
\li Message authenticators: Poly1305, GHASH
\li Public key algorithms: Curve25519
\li Public key algorithms: Curve25519, Ed25519
\li Random number generation: \link RNGClass RNG\endlink, TransistorNoiseSource, RingOscillatorNoiseSource
More information can be found on the \ref crypto "Cryptographic Library" page.

View File

@ -22,6 +22,7 @@
#include "BigNumberUtil.h"
#include "utility/EndianUtil.h"
#include "utility/LimbUtil.h"
#include <string.h>
/**
@ -363,3 +364,273 @@ void BigNumberUtil::packBE(uint8_t *bytes, size_t len,
}
#endif
}
/**
* \brief Adds two big numbers.
*
* \param result The result of the addition. This can be the same
* as either \a x or \a y.
* \param x The first big number.
* \param y The second big number.
* \param size The size of the values in limbs.
*
* \return Returns 1 if there was a carry out or 0 if there was no carry out.
*
* \sa sub(), mul()
*/
limb_t BigNumberUtil::add(limb_t *result, const limb_t *x,
const limb_t *y, size_t size)
{
dlimb_t carry = 0;
while (size > 0) {
carry += *x++;
carry += *y++;
*result++ = (limb_t)carry;
carry >>= LIMB_BITS;
--size;
}
return (limb_t)carry;
}
/**
* \brief Subtracts one big number from another.
*
* \param result The result of the subtraction. This can be the same
* as either \a x or \a y.
* \param x The first big number.
* \param y The second big number to subtract from \a x.
* \param size The size of the values in limbs.
*
* \return Returns 1 if there was a borrow, or 0 if there was no borrow.
*
* \sa add(), mul()
*/
limb_t BigNumberUtil::sub(limb_t *result, const limb_t *x,
const limb_t *y, size_t size)
{
dlimb_t borrow = 0;
while (size > 0) {
borrow = ((dlimb_t)(*x++)) - (*y++) - ((borrow >> LIMB_BITS) & 0x01);
*result++ = (limb_t)borrow;
--size;
}
return ((limb_t)(borrow >> LIMB_BITS)) & 0x01;
}
/**
* \brief Multiplies two big numbers.
*
* \param result The result of the multiplication. The array must be
* \a xcount + \a ycount limbs in size.
* \param x Points to the first value to multiply.
* \param xcount The number of limbs in \a x.
* \param y Points to the second value to multiply.
* \param ycount The number of limbs in \a y.
*
* \sa mul_P()
*/
void BigNumberUtil::mul(limb_t *result, const limb_t *x, size_t xcount,
const limb_t *y, size_t ycount)
{
size_t i, j;
dlimb_t carry;
limb_t word;
const limb_t *xx;
limb_t *rr;
// Multiply the lowest limb of y by x.
carry = 0;
word = y[0];
xx = x;
rr = result;
for (i = 0; i < xcount; ++i) {
carry += ((dlimb_t)(*xx++)) * word;
*rr++ = (limb_t)carry;
carry >>= LIMB_BITS;
}
*rr = (limb_t)carry;
// Multiply and add the remaining limbs of y by x.
for (i = 1; i < ycount; ++i) {
word = y[i];
carry = 0;
xx = x;
rr = result + i;
for (j = 0; j < xcount; ++j) {
carry += ((dlimb_t)(*xx++)) * word;
carry += *rr;
*rr++ = (limb_t)carry;
carry >>= LIMB_BITS;
}
*rr = (limb_t)carry;
}
}
/**
* \brief Reduces \a x modulo \a y using subtraction.
*
* \param result The result of the reduction. This can be the
* same as \a x.
* \param x The number to be reduced.
* \param y The base to use for the modulo reduction.
* \param size The size of the values in limbs.
*
* It is assumed that \a x is less than \a y * 2 so that a single
* conditional subtraction will bring it down below \a y. The reduction
* is performed in constant time.
*
* \sa reduceQuick_P()
*/
void BigNumberUtil::reduceQuick(limb_t *result, const limb_t *x,
const limb_t *y, size_t size)
{
// Subtract "y" from "x" and turn the borrow into an AND mask.
limb_t mask = sub(result, x, y, size);
mask = (~mask) + 1;
// Add "y" back to the result if the mask is non-zero.
dlimb_t carry = 0;
while (size > 0) {
carry += *result;
carry += (*y++ & mask);
*result++ = (limb_t)carry;
carry >>= LIMB_BITS;
--size;
}
}
/**
* \brief Adds two big numbers where one of them is in program memory.
*
* \param result The result of the addition. This can be the same as \a x.
* \param x The first big number.
* \param y The second big number. This must point into program memory.
* \param size The size of the values in limbs.
*
* \return Returns 1 if there was a carry out or 0 if there was no carry out.
*
* \sa sub_P(), mul_P()
*/
limb_t BigNumberUtil::add_P(limb_t *result, const limb_t *x,
const limb_t *y, size_t size)
{
dlimb_t carry = 0;
while (size > 0) {
carry += *x++;
carry += pgm_read_limb(y++);
*result++ = (limb_t)carry;
carry >>= LIMB_BITS;
--size;
}
return (limb_t)carry;
}
/**
* \brief Subtracts one big number from another where one is in program memory.
*
* \param result The result of the subtraction. This can be the same as \a x.
* \param x The first big number.
* \param y The second big number to subtract from \a x. This must point
* into program memory.
* \param size The size of the values in limbs.
*
* \return Returns 1 if there was a borrow, or 0 if there was no borrow.
*
* \sa add_P(), mul_P()
*/
limb_t BigNumberUtil::sub_P(limb_t *result, const limb_t *x,
const limb_t *y, size_t size)
{
dlimb_t borrow = 0;
while (size > 0) {
borrow = ((dlimb_t)(*x++)) - pgm_read_limb(y++) - ((borrow >> LIMB_BITS) & 0x01);
*result++ = (limb_t)borrow;
--size;
}
return ((limb_t)(borrow >> LIMB_BITS)) & 0x01;
}
/**
* \brief Multiplies two big numbers where one is in program memory.
*
* \param result The result of the multiplication. The array must be
* \a xcount + \a ycount limbs in size.
* \param x Points to the first value to multiply.
* \param xcount The number of limbs in \a x.
* \param y Points to the second value to multiply. This must point
* into program memory.
* \param ycount The number of limbs in \a y.
*
* \sa mul()
*/
void BigNumberUtil::mul_P(limb_t *result, const limb_t *x, size_t xcount,
const limb_t *y, size_t ycount)
{
size_t i, j;
dlimb_t carry;
limb_t word;
const limb_t *xx;
limb_t *rr;
// Multiply the lowest limb of y by x.
carry = 0;
word = pgm_read_limb(&(y[0]));
xx = x;
rr = result;
for (i = 0; i < xcount; ++i) {
carry += ((dlimb_t)(*xx++)) * word;
*rr++ = (limb_t)carry;
carry >>= LIMB_BITS;
}
*rr = (limb_t)carry;
// Multiply and add the remaining limb of y by x.
for (i = 1; i < ycount; ++i) {
word = pgm_read_limb(&(y[i]));
carry = 0;
xx = x;
rr = result + i;
for (j = 0; j < xcount; ++j) {
carry += ((dlimb_t)(*xx++)) * word;
carry += *rr;
*rr++ = (limb_t)carry;
carry >>= LIMB_BITS;
}
*rr = (limb_t)carry;
}
}
/**
* \brief Reduces \a x modulo \a y using subtraction where \a y is
* in program memory.
*
* \param result The result of the reduction. This can be the
* same as \a x.
* \param x The number to be reduced.
* \param y The base to use for the modulo reduction. This must point
* into program memory.
* \param size The size of the values in limbs.
*
* It is assumed that \a x is less than \a y * 2 so that a single
* conditional subtraction will bring it down below \a y. The reduction
* is performed in constant time.
*
* \sa reduceQuick()
*/
void BigNumberUtil::reduceQuick_P(limb_t *result, const limb_t *x,
const limb_t *y, size_t size)
{
// Subtract "y" from "x" and turn the borrow into an AND mask.
limb_t mask = sub_P(result, x, y, size);
mask = (~mask) + 1;
// Add "y" back to the result if the mask is non-zero.
dlimb_t carry = 0;
while (size > 0) {
carry += *result;
carry += (pgm_read_limb(y++) & mask);
*result++ = (limb_t)carry;
carry >>= LIMB_BITS;
--size;
}
}

View File

@ -61,6 +61,24 @@ public:
static void packBE(uint8_t *bytes, size_t len,
const limb_t *limbs, size_t count);
static limb_t add(limb_t *result, const limb_t *x,
const limb_t *y, size_t size);
static limb_t sub(limb_t *result, const limb_t *x,
const limb_t *y, size_t size);
static void mul(limb_t *result, const limb_t *x, size_t xcount,
const limb_t *y, size_t ycount);
static void reduceQuick(limb_t *result, const limb_t *x,
const limb_t *y, size_t size);
static limb_t add_P(limb_t *result, const limb_t *x,
const limb_t *y, size_t size);
static limb_t sub_P(limb_t *result, const limb_t *x,
const limb_t *y, size_t size);
static void mul_P(limb_t *result, const limb_t *x, size_t xcount,
const limb_t *y, size_t ycount);
static void reduceQuick_P(limb_t *result, const limb_t *x,
const limb_t *y, size_t size);
private:
// Constructor and destructor are private - cannot instantiate this class.
BigNumberUtil() {}

View File

@ -23,7 +23,7 @@
#include "Curve25519.h"
#include "Crypto.h"
#include "RNG.h"
#include "utility/ProgMemUtil.h"
#include "utility/LimbUtil.h"
#include <string.h>
/**
@ -37,15 +37,10 @@
*
* References: http://cr.yp.to/ecdh.html
* https://tools.ietf.org/html/draft-irtf-cfrg-curves-02
*
* \sa Ed25519
*/
// Number of limbs in a value from the field modulo 2^255 - 19.
// We assume that sizeof(limb_t) is a power of 2: 1, 2, 4, etc.
#define NUM_LIMBS (32 / sizeof(limb_t))
// Number of bits in limb_t.
#define LIMB_BITS (8 * sizeof(limb_t))
// The overhead of clean() calls in mul(), reduceQuick(), etc can
// add up to a lot of processing time during eval(). Only do such
// cleanups if strict mode has been enabled. Other implementations
@ -78,20 +73,20 @@
*/
bool Curve25519::eval(uint8_t result[32], const uint8_t s[32], const uint8_t x[32])
{
limb_t x_1[NUM_LIMBS];
limb_t x_2[NUM_LIMBS];
limb_t x_3[NUM_LIMBS];
limb_t z_2[NUM_LIMBS];
limb_t z_3[NUM_LIMBS];
limb_t A[NUM_LIMBS];
limb_t B[NUM_LIMBS];
limb_t C[NUM_LIMBS];
limb_t D[NUM_LIMBS];
limb_t E[NUM_LIMBS];
limb_t AA[NUM_LIMBS];
limb_t BB[NUM_LIMBS];
limb_t DA[NUM_LIMBS];
limb_t CB[NUM_LIMBS];
limb_t x_1[NUM_LIMBS_256BIT];
limb_t x_2[NUM_LIMBS_256BIT];
limb_t x_3[NUM_LIMBS_256BIT];
limb_t z_2[NUM_LIMBS_256BIT];
limb_t z_3[NUM_LIMBS_256BIT];
limb_t A[NUM_LIMBS_256BIT];
limb_t B[NUM_LIMBS_256BIT];
limb_t C[NUM_LIMBS_256BIT];
limb_t D[NUM_LIMBS_256BIT];
limb_t E[NUM_LIMBS_256BIT];
limb_t AA[NUM_LIMBS_256BIT];
limb_t BB[NUM_LIMBS_256BIT];
limb_t DA[NUM_LIMBS_256BIT];
limb_t CB[NUM_LIMBS_256BIT];
uint8_t mask;
uint8_t sposn;
uint8_t select;
@ -102,8 +97,8 @@ bool Curve25519::eval(uint8_t result[32], const uint8_t s[32], const uint8_t x[3
// which also masks off the high bit. NULL means 9.
if (x) {
// x1 = x
BigNumberUtil::unpackLE(x_1, NUM_LIMBS, x, 32);
x_1[NUM_LIMBS - 1] &= ((((limb_t)1) << (LIMB_BITS - 1)) - 1);
BigNumberUtil::unpackLE(x_1, NUM_LIMBS_256BIT, x, 32);
x_1[NUM_LIMBS_256BIT - 1] &= ((((limb_t)1) << (LIMB_BITS - 1)) - 1);
} else {
memset(x_1, 0, sizeof(x_1)); // x_1 = 9
x_1[0] = 9;
@ -178,7 +173,7 @@ bool Curve25519::eval(uint8_t result[32], const uint8_t s[32], const uint8_t x[3
mul(x_2, x_2, z_3);
// Pack the result into the return array.
BigNumberUtil::packLE(result, 32, x_2, NUM_LIMBS);
BigNumberUtil::packLE(result, 32, x_2, NUM_LIMBS_256BIT);
// Clean up and exit.
clean(x_1);
@ -354,13 +349,13 @@ uint8_t Curve25519::isWeakPoint(const uint8_t k[32])
* \brief Reduces a number modulo 2^255 - 19.
*
* \param result The array that will contain the result when the
* function exits. Must be NUM_LIMBS limbs in size.
* \param x The number to be reduced, which must be NUM_LIMBS * 2 limbs in
* size and less than or equal to square(2^255 - 19 - 1). This array will
* be modified by the reduction process.
* function exits. Must be NUM_LIMBS_256BIT limbs in size.
* \param x The number to be reduced, which must be NUM_LIMBS_512BIT
* limbs in size and less than or equal to square(2^255 - 19 - 1).
* This array will be modified by the reduction process.
* \param size The size of the high order half of \a x. This indicates
* the size of \a x in limbs. If it is shorter than NUM_LIMBS then the
* reduction can be performed quicker.
* the size of \a x in limbs. If it is shorter than NUM_LIMBS_256BIT
* then the reduction can be performed quicker.
*/
void Curve25519::reduce(limb_t *result, limb_t *x, uint8_t size)
{
@ -411,18 +406,18 @@ void Curve25519::reduce(limb_t *result, limb_t *x, uint8_t size)
// Calculate (x mod 2^255) + ((x / 2^255) * 19) which will
// either produce the answer we want or it will produce a
// value of the form "answer + j * (2^255 - 19)".
carry = ((dlimb_t)(x[NUM_LIMBS - 1] >> (LIMB_BITS - 1))) * 19U;
x[NUM_LIMBS - 1] &= ((((limb_t)1) << (LIMB_BITS - 1)) - 1);
carry = ((dlimb_t)(x[NUM_LIMBS_256BIT - 1] >> (LIMB_BITS - 1))) * 19U;
x[NUM_LIMBS_256BIT - 1] &= ((((limb_t)1) << (LIMB_BITS - 1)) - 1);
for (posn = 0; posn < size; ++posn) {
carry += ((dlimb_t)(x[posn + NUM_LIMBS])) * 38U;
carry += ((dlimb_t)(x[posn + NUM_LIMBS_256BIT])) * 38U;
carry += x[posn];
x[posn] = (limb_t)carry;
carry >>= LIMB_BITS;
}
if (size < NUM_LIMBS) {
if (size < NUM_LIMBS_256BIT) {
// The high order half of the number is short; e.g. for mulA24().
// Propagate the carry through the rest of the low order part.
for (posn = size; posn < NUM_LIMBS; ++posn) {
for (posn = size; posn < NUM_LIMBS_256BIT; ++posn) {
carry += x[posn];
x[posn] = (limb_t)carry;
carry >>= LIMB_BITS;
@ -434,9 +429,9 @@ void Curve25519::reduce(limb_t *result, limb_t *x, uint8_t size)
// then this won't do any harm but we must still do the calculation
// to preserve the overall timing.
carry *= 38U;
carry += ((dlimb_t)(x[NUM_LIMBS - 1] >> (LIMB_BITS - 1))) * 19U;
x[NUM_LIMBS - 1] &= ((((limb_t)1) << (LIMB_BITS - 1)) - 1);
for (posn = 0; posn < NUM_LIMBS; ++posn) {
carry += ((dlimb_t)(x[NUM_LIMBS_256BIT - 1] >> (LIMB_BITS - 1))) * 19U;
x[NUM_LIMBS_256BIT - 1] &= ((((limb_t)1) << (LIMB_BITS - 1)) - 1);
for (posn = 0; posn < NUM_LIMBS_256BIT; ++posn) {
carry += x[posn];
x[posn] = (limb_t)carry;
carry >>= LIMB_BITS;
@ -448,9 +443,9 @@ void Curve25519::reduce(limb_t *result, limb_t *x, uint8_t size)
// trial answer into the top-most limbs of the original "x" array.
// We add 19 here; the subtraction of 2^255 occurs in the next step.
carry = 19U;
for (posn = 0; posn < NUM_LIMBS; ++posn) {
for (posn = 0; posn < NUM_LIMBS_256BIT; ++posn) {
carry += x[posn];
x[posn + NUM_LIMBS] = (limb_t)carry;
x[posn + NUM_LIMBS_256BIT] = (limb_t)carry;
carry >>= LIMB_BITS;
}
@ -460,22 +455,22 @@ void Curve25519::reduce(limb_t *result, limb_t *x, uint8_t size)
// it in a way that instruction timing will not reveal which value
// was selected. Borrow will occur if the high bit of the previous
// result is 0: turn the high bit into a selection mask.
limb_t mask = (limb_t)(((slimb_t)(x[NUM_LIMBS * 2 - 1])) >> (LIMB_BITS - 1));
limb_t mask = (limb_t)(((slimb_t)(x[NUM_LIMBS_512BIT - 1])) >> (LIMB_BITS - 1));
limb_t nmask = ~mask;
x[NUM_LIMBS * 2 - 1] &= ((((limb_t)1) << (LIMB_BITS - 1)) - 1);
for (posn = 0; posn < NUM_LIMBS; ++posn) {
result[posn] = (x[posn] & nmask) | (x[posn + NUM_LIMBS] & mask);
x[NUM_LIMBS_512BIT - 1] &= ((((limb_t)1) << (LIMB_BITS - 1)) - 1);
for (posn = 0; posn < NUM_LIMBS_256BIT; ++posn) {
result[posn] = (x[posn] & nmask) | (x[posn + NUM_LIMBS_256BIT] & mask);
}
}
/**
* \brief Quickly reduces a number modulo 2^255 - 19.
*
* \param x The number to be reduced, which must be NUM_LIMBS limbs in size
* and less than or equal to 2 * (2^255 - 19 - 1).
* \param x The number to be reduced, which must be NUM_LIMBS_256BIT
( limbs in size and less than or equal to 2 * (2^255 - 19 - 1).
* \return Zero if \a x was greater than or equal to (2^255 - 19).
*
* The answer is also put into \a x and will consist of NUM_LIMBS limbs.
* The answer is also put into \a x and will consist of NUM_LIMBS_256BIT limbs.
*
* This function is intended for reducing the result of additions where
* the caller knows that \a x is within the described range. A single
@ -483,7 +478,7 @@ void Curve25519::reduce(limb_t *result, limb_t *x, uint8_t size)
*/
limb_t Curve25519::reduceQuick(limb_t *x)
{
limb_t temp[NUM_LIMBS];
limb_t temp[NUM_LIMBS_256BIT];
dlimb_t carry;
uint8_t posn;
limb_t *xx;
@ -495,7 +490,7 @@ limb_t Curve25519::reduceQuick(limb_t *x)
carry = 19U;
xx = x;
tt = temp;
for (posn = 0; posn < NUM_LIMBS; ++posn) {
for (posn = 0; posn < NUM_LIMBS_256BIT; ++posn) {
carry += *xx++;
*tt++ = (limb_t)carry;
carry >>= LIMB_BITS;
@ -506,12 +501,12 @@ limb_t Curve25519::reduceQuick(limb_t *x)
// correct answer but do it in a way that instruction timing will not
// reveal which value was selected. Borrow will occur if the high bit
// of "temp" is 0: turn the high bit into a selection mask.
limb_t mask = (limb_t)(((slimb_t)(temp[NUM_LIMBS - 1])) >> (LIMB_BITS - 1));
limb_t mask = (limb_t)(((slimb_t)(temp[NUM_LIMBS_256BIT - 1])) >> (LIMB_BITS - 1));
limb_t nmask = ~mask;
temp[NUM_LIMBS - 1] &= ((((limb_t)1) << (LIMB_BITS - 1)) - 1);
temp[NUM_LIMBS_256BIT - 1] &= ((((limb_t)1) << (LIMB_BITS - 1)) - 1);
xx = x;
tt = temp;
for (posn = 0; posn < NUM_LIMBS; ++posn) {
for (posn = 0; posn < NUM_LIMBS_256BIT; ++posn) {
*xx = ((*xx) & nmask) | ((*tt++) & mask);
++xx;
}
@ -524,53 +519,68 @@ limb_t Curve25519::reduceQuick(limb_t *x)
}
/**
* \brief Multiplies two values and then reduces the result modulo 2^255 - 19.
* \brief Multiplies two 256-bit values to produce a 512-bit result.
*
* \param result The result, which must be NUM_LIMBS limbs in size and can
* be the same array as \a x or \a y.
* \param x The first value to multiply, which must be NUM_LIMBS limbs in size
* and less than 2^255 - 19.
* \param y The second value to multiply, which must be NUM_LIMBS limbs in size
* and less than 2^255 - 19. This can be the same array as \a x.
* \param result The result, which must be NUM_LIMBS_512BIT limbs in size
* and must not overlap with \a x or \a y.
* \param x The first value to multiply, which must be NUM_LIMBS_256BIT
* limbs in size.
* \param y The second value to multiply, which must be NUM_LIMBS_256BIT
* limbs in size.
*
* \sa mul()
*/
void Curve25519::mul(limb_t *result, const limb_t *x, const limb_t *y)
void Curve25519::mulNoReduce(limb_t *result, const limb_t *x, const limb_t *y)
{
limb_t temp[NUM_LIMBS * 2];
uint8_t i, j;
dlimb_t carry;
limb_t word;
const limb_t *yy;
limb_t *tt;
limb_t *rr;
// Multiply the lowest word of x by y.
carry = 0;
word = x[0];
yy = y;
tt = temp;
for (i = 0; i < NUM_LIMBS; ++i) {
rr = result;
for (i = 0; i < NUM_LIMBS_256BIT; ++i) {
carry += ((dlimb_t)(*yy++)) * word;
*tt++ = (limb_t)carry;
*rr++ = (limb_t)carry;
carry >>= LIMB_BITS;
}
*tt = (limb_t)carry;
*rr = (limb_t)carry;
// Multiply and add the remaining words of x by y.
for (i = 1; i < NUM_LIMBS; ++i) {
for (i = 1; i < NUM_LIMBS_256BIT; ++i) {
word = x[i];
carry = 0;
yy = y;
tt = temp + i;
for (j = 0; j < NUM_LIMBS; ++j) {
rr = result + i;
for (j = 0; j < NUM_LIMBS_256BIT; ++j) {
carry += ((dlimb_t)(*yy++)) * word;
carry += *tt;
*tt++ = (limb_t)carry;
carry += *rr;
*rr++ = (limb_t)carry;
carry >>= LIMB_BITS;
}
*tt = (limb_t)carry;
*rr = (limb_t)carry;
}
}
// Reduce the intermediate result modulo 2^255 - 19.
reduce(result, temp, NUM_LIMBS);
/**
* \brief Multiplies two values and then reduces the result modulo 2^255 - 19.
*
* \param result The result, which must be NUM_LIMBS_256BIT limbs in size
* and can be the same array as \a x or \a y.
* \param x The first value to multiply, which must be NUM_LIMBS_256BIT limbs
* in size and less than 2^255 - 19.
* \param y The second value to multiply, which must be NUM_LIMBS_256BIT limbs
* in size and less than 2^255 - 19. This can be the same array as \a x.
*/
void Curve25519::mul(limb_t *result, const limb_t *x, const limb_t *y)
{
limb_t temp[NUM_LIMBS_512BIT];
mulNoReduce(temp, x, y);
reduce(result, temp, NUM_LIMBS_256BIT);
strict_clean(temp);
}
@ -578,9 +588,9 @@ void Curve25519::mul(limb_t *result, const limb_t *x, const limb_t *y)
* \fn void Curve25519::square(limb_t *result, const limb_t *x)
* \brief Squares a value and then reduces it modulo 2^255 - 19.
*
* \param result The result, which must be NUM_LIMBS limbs in size and
* \param result The result, which must be NUM_LIMBS_256BIT limbs in size and
* can be the same array as \a x.
* \param x The value to square, which must be NUM_LIMBS limbs in size
* \param x The value to square, which must be NUM_LIMBS_256BIT limbs in size
* and less than 2^255 - 19.
*/
@ -588,36 +598,33 @@ void Curve25519::mul(limb_t *result, const limb_t *x, const limb_t *y)
* \brief Multiplies a value by the a24 constant and then reduces the result
* modulo 2^255 - 19.
*
* \param result The result, which must be NUM_LIMBS limbs in size and can
* be the same array as \a x.
* \param x The value to multiply by a24, which must be NUM_LIMBS limbs in size
* and less than 2^255 - 19.
* \param result The result, which must be NUM_LIMBS_256BIT limbs in size
* and can be the same array as \a x.
* \param x The value to multiply by a24, which must be NUM_LIMBS_256BIT
* limbs in size and less than 2^255 - 19.
*/
void Curve25519::mulA24(limb_t *result, const limb_t *x)
{
// The constant a24 = 121665 (0x1DB41) as a limb array.
#if BIGNUMBER_LIMB_8BIT
static limb_t const a24[3] PROGMEM = {0x41, 0xDB, 0x01};
#define pgm_read_a24(index) (pgm_read_byte(&(a24[(index)])))
#elif BIGNUMBER_LIMB_16BIT
static limb_t const a24[2] PROGMEM = {0xDB41, 0x0001};
#define pgm_read_a24(index) (pgm_read_word(&(a24[(index)])))
#elif BIGNUMBER_LIMB_32BIT
static limb_t const a24[1] PROGMEM = {0x0001DB41};
#define pgm_read_a24(index) (pgm_read_dword(&(a24[(index)])))
#else
#error "limb_t must be 8, 16, or 32 bits in size"
#endif
#define NUM_A24_LIMBS (sizeof(a24) / sizeof(limb_t))
// Multiply the lowest limb of a24 by x and zero-extend into the result.
limb_t temp[NUM_LIMBS * 2];
limb_t temp[NUM_LIMBS_512BIT];
uint8_t i, j;
dlimb_t carry = 0;
limb_t word = pgm_read_a24(0);
limb_t word = pgm_read_limb(&(a24[0]));
const limb_t *xx = x;
limb_t *tt = temp;
for (i = 0; i < NUM_LIMBS; ++i) {
for (i = 0; i < NUM_LIMBS_256BIT; ++i) {
carry += ((dlimb_t)(*xx++)) * word;
*tt++ = (limb_t)carry;
carry >>= LIMB_BITS;
@ -626,11 +633,11 @@ void Curve25519::mulA24(limb_t *result, const limb_t *x)
// Multiply and add the remaining limbs of a24.
for (i = 1; i < NUM_A24_LIMBS; ++i) {
word = pgm_read_a24(i);
word = pgm_read_limb(&(a24[i]));
carry = 0;
xx = x;
tt = temp + i;
for (j = 0; j < NUM_LIMBS; ++j) {
for (j = 0; j < NUM_LIMBS_256BIT; ++j) {
carry += ((dlimb_t)(*xx++)) * word;
carry += *tt;
*tt++ = (limb_t)carry;
@ -644,15 +651,69 @@ void Curve25519::mulA24(limb_t *result, const limb_t *x)
strict_clean(temp);
}
/**
* \brief Multiplies two values and then reduces the result modulo 2^255 - 19,
* where one of the values is in program memory.
*
* \param result The result, which must be NUM_LIMBS_256BIT limbs in size
* and can be the same array as \a x or \a y.
* \param x The first value to multiply, which must be NUM_LIMBS_256BIT limbs
* in size and less than 2^255 - 19.
* \param y The second value to multiply, which must be NUM_LIMBS_256BIT limbs
* in size and less than 2^255 - 19. This array must be in program memory.
*/
void Curve25519::mul_P(limb_t *result, const limb_t *x, const limb_t *y)
{
limb_t temp[NUM_LIMBS_512BIT];
uint8_t i, j;
dlimb_t carry;
limb_t word;
const limb_t *yy;
limb_t *tt;
// Multiply the lowest word of x by y.
carry = 0;
word = x[0];
yy = y;
tt = temp;
for (i = 0; i < NUM_LIMBS_256BIT; ++i) {
carry += ((dlimb_t)(pgm_read_limb(yy))) * word;
*tt++ = (limb_t)carry;
carry >>= LIMB_BITS;
++yy;
}
*tt = (limb_t)carry;
// Multiply and add the remaining words of x by y.
for (i = 1; i < NUM_LIMBS_256BIT; ++i) {
word = x[i];
carry = 0;
yy = y;
tt = temp + i;
for (j = 0; j < NUM_LIMBS_256BIT; ++j) {
carry += ((dlimb_t)(pgm_read_limb(yy))) * word;
carry += *tt;
*tt++ = (limb_t)carry;
carry >>= LIMB_BITS;
++yy;
}
*tt = (limb_t)carry;
}
// Reduce the intermediate result modulo 2^255 - 19.
reduce(result, temp, NUM_LIMBS_256BIT);
strict_clean(temp);
}
/**
* \brief Adds two values and then reduces the result modulo 2^255 - 19.
*
* \param result The result, which must be NUM_LIMBS limbs in size and can
* be the same array as \a x or \a y.
* \param x The first value to multiply, which must be NUM_LIMBS limbs in size
* and less than 2^255 - 19.
* \param y The second value to multiply, which must be NUM_LIMBS limbs in size
* and less than 2^255 - 19.
* \param result The result, which must be NUM_LIMBS_256BIT limbs in size
* and can be the same array as \a x or \a y.
* \param x The first value to multiply, which must be NUM_LIMBS_256BIT
* limbs in size and less than 2^255 - 19.
* \param y The second value to multiply, which must be NUM_LIMBS_256BIT
* limbs in size and less than 2^255 - 19.
*/
void Curve25519::add(limb_t *result, const limb_t *x, const limb_t *y)
{
@ -661,7 +722,7 @@ void Curve25519::add(limb_t *result, const limb_t *x, const limb_t *y)
limb_t *rr = result;
// Add the two arrays to obtain the intermediate result.
for (posn = 0; posn < NUM_LIMBS; ++posn) {
for (posn = 0; posn < NUM_LIMBS_256BIT; ++posn) {
carry += *x++;
carry += *y++;
*rr++ = (limb_t)carry;
@ -675,12 +736,12 @@ void Curve25519::add(limb_t *result, const limb_t *x, const limb_t *y)
/**
* \brief Subtracts two values and then reduces the result modulo 2^255 - 19.
*
* \param result The result, which must be NUM_LIMBS limbs in size and can
* be the same array as \a x or \a y.
* \param x The first value to multiply, which must be NUM_LIMBS limbs in size
* and less than 2^255 - 19.
* \param y The second value to multiply, which must be NUM_LIMBS limbs in size
* and less than 2^255 - 19.
* \param result The result, which must be NUM_LIMBS_256BIT limbs in size
* and can be the same array as \a x or \a y.
* \param x The first value to multiply, which must be NUM_LIMBS_256BIT
* limbs in size and less than 2^255 - 19.
* \param y The second value to multiply, which must be NUM_LIMBS_256BIT
* limbs in size and less than 2^255 - 19.
*/
void Curve25519::sub(limb_t *result, const limb_t *x, const limb_t *y)
{
@ -690,7 +751,7 @@ void Curve25519::sub(limb_t *result, const limb_t *x, const limb_t *y)
// Subtract y from x to generate the intermediate result.
borrow = 0;
for (posn = 0; posn < NUM_LIMBS; ++posn) {
for (posn = 0; posn < NUM_LIMBS_256BIT; ++posn) {
borrow = ((dlimb_t)(*x++)) - (*y++) - ((borrow >> LIMB_BITS) & 0x01);
*rr++ = (limb_t)borrow;
}
@ -704,7 +765,7 @@ void Curve25519::sub(limb_t *result, const limb_t *x, const limb_t *y)
borrow = (borrow >> LIMB_BITS) & 19U;
borrow = ((dlimb_t)(*rr)) - borrow;
*rr++ = (limb_t)borrow;
for (posn = 1; posn < NUM_LIMBS; ++posn) {
for (posn = 1; posn < NUM_LIMBS_256BIT; ++posn) {
borrow = ((dlimb_t)(*rr)) - ((borrow >> LIMB_BITS) & 0x01);
*rr++ = (limb_t)borrow;
}
@ -720,8 +781,10 @@ void Curve25519::sub(limb_t *result, const limb_t *x, const limb_t *y)
*
* The swap is performed in a way that it should take the same amount of
* time irrespective of the value of \a select.
*
* \sa cmove()
*/
void Curve25519::cswap(uint8_t select, limb_t *x, limb_t *y)
void Curve25519::cswap(limb_t select, limb_t *x, limb_t *y)
{
uint8_t posn;
limb_t dummy;
@ -734,7 +797,7 @@ void Curve25519::cswap(uint8_t select, limb_t *x, limb_t *y)
// Swap the two values based on "select". Algorithm from:
// https://tools.ietf.org/html/draft-irtf-cfrg-curves-02
for (posn = 0; posn < NUM_LIMBS; ++posn) {
for (posn = 0; posn < NUM_LIMBS_256BIT; ++posn) {
dummy = sel & (x[posn] ^ y[posn]);
x[posn] ^= dummy;
y[posn] ^= dummy;
@ -742,20 +805,48 @@ void Curve25519::cswap(uint8_t select, limb_t *x, limb_t *y)
}
/**
* \brief Computes the reciprocal of a number modulo 2^255 - 19.
* \brief Conditionally moves \a y into \a x if a selection value is non-zero.
*
* \param result The result as a array of NUM_LIMBS limbs in size. This can
* be the same array as \a x.
* \param x The number to compute the reciprocal for.
* \param select Non-zero to move \a y into \a x, zero to leave \a x unchanged.
* \param x The destination to move into.
* \param y The value to conditionally move.
*
* The move is performed in a way that it should take the same amount of
* time irrespective of the value of \a select.
*
* \sa cswap()
*/
void Curve25519::recip(limb_t *result, const limb_t *x)
void Curve25519::cmove(limb_t select, limb_t *x, const limb_t *y)
{
limb_t t1[NUM_LIMBS];
uint8_t posn;
limb_t dummy;
limb_t sel;
// Turn "select" into an all-zeroes or all-ones mask. We don't care
// which bit or bits is set in the original "select" value.
sel = (limb_t)(((((dlimb_t)1) << LIMB_BITS) - select) >> LIMB_BITS);
--sel;
// Move y into x based on "select". Similar to conditional swap above.
for (posn = 0; posn < NUM_LIMBS_256BIT; ++posn) {
dummy = sel & (x[posn] ^ y[posn]);
x[posn] ^= dummy;
}
}
/**
* \brief Raise x to the power of (2^250 - 1).
*
* \param result The result array, which must be NUM_LIMBS_256BIT limbs in size.
* \param x The value to raise.
*/
void Curve25519::pow250(limb_t *result, const limb_t *x)
{
limb_t t1[NUM_LIMBS_256BIT];
uint8_t i, j;
// The reciprocal is the same as x ^ (p - 2) where p = 2^255 - 19.
// The big-endian hexadecimal expansion of (p - 2) is:
// 7FFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFEB
// The big-endian hexadecimal expansion of (2^250 - 1) is:
// 03FFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF
//
// The naive implementation needs to do 2 multiplications per 1 bit and
// 1 multiplication per 0 bit. We can improve upon this by creating a
@ -786,6 +877,25 @@ void Curve25519::recip(limb_t *result, const limb_t *x)
mul(result, result, t1);
}
// Clean up and exit.
clean(t1);
}
/**
* \brief Computes the reciprocal of a number modulo 2^255 - 19.
*
* \param result The result as a array of NUM_LIMBS_256BIT limbs in size.
* This cannot be the same array as \a x.
* \param x The number to compute the reciprocal for.
*/
void Curve25519::recip(limb_t *result, const limb_t *x)
{
// The reciprocal is the same as x ^ (p - 2) where p = 2^255 - 19.
// The big-endian hexadecimal expansion of (p - 2) is:
// 7FFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFEB
// Start with the 250 upper bits of the expansion of (p - 2).
pow250(result, x);
// Deal with the 5 lowest bits of (p - 2), 01011, from highest to lowest.
square(result, result);
square(result, result);
@ -795,7 +905,59 @@ void Curve25519::recip(limb_t *result, const limb_t *x)
mul(result, result, x);
square(result, result);
mul(result, result, x);
// Clean up and exit.
clean(t1);
}
/**
* \brief Computes the square root of a number modulo 2^255 - 19.
*
* \param result The result as a array of NUM_LIMBS_256BIT limbs in size.
* This must not overlap with \a x.
* \param x The number to compute the square root for.
*
* For any number \a x, there are two square roots: positive and negative.
* For example, both 2 and -2 are square roots of 4 because 2 * 2 = -2 * -2.
* This function will return one or the other. Callers must determine which
* square root they are interested in and invert the result as necessary.
*
* \note This function is not constant time so it should only be used
* on publicly-known values.
*/
bool Curve25519::sqrt(limb_t *result, const limb_t *x)
{
// sqrt(-1) mod (2^255 - 19).
static limb_t const numSqrtM1[NUM_LIMBS_256BIT] PROGMEM = {
LIMB(0x4A0EA0B0), LIMB(0xC4EE1B27), LIMB(0xAD2FE478), LIMB(0x2F431806),
LIMB(0x3DFBD7A7), LIMB(0x2B4D0099), LIMB(0x4FC1DF0B), LIMB(0x2B832480)
};
limb_t y[NUM_LIMBS_256BIT];
// Algorithm from:
// https://tools.ietf.org/id/draft-josefsson-eddsa-ed25519-02.txt
// Compute a candidate root: result = x^((p + 3) / 8) mod p.
// (p + 3) / 8 = (2^252 - 2) which is 251 one bits followed by a zero:
// 0FFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE
pow250(result, x);
square(result, result);
mul(result, result, x);
square(result, result);
// Did we get the square root immediately?
square(y, result);
if (memcmp(x, y, sizeof(y)) == 0) {
clean(y);
return true;
}
// Multiply the result by sqrt(-1) and check again.
mul_P(result, result, numSqrtM1);
square(y, result);
if (memcmp(x, y, sizeof(y)) == 0) {
clean(y);
return true;
}
// The number does not have a square root.
clean(y);
return false;
}

View File

@ -25,6 +25,8 @@
#include "BigNumberUtil.h"
class Ed25519;
class Curve25519
{
public:
@ -43,6 +45,8 @@ private:
static void reduce(limb_t *result, limb_t *x, uint8_t size);
static limb_t reduceQuick(limb_t *x);
static void mulNoReduce(limb_t *result, const limb_t *x, const limb_t *y);
static void mul(limb_t *result, const limb_t *x, const limb_t *y);
static void square(limb_t *result, const limb_t *x)
{
@ -51,16 +55,23 @@ private:
static void mulA24(limb_t *result, const limb_t *x);
static void mul_P(limb_t *result, const limb_t *x, const limb_t *y);
static void add(limb_t *result, const limb_t *x, const limb_t *y);
static void sub(limb_t *result, const limb_t *x, const limb_t *y);
static void cswap(uint8_t select, limb_t *x, limb_t *y);
static void cswap(limb_t select, limb_t *x, limb_t *y);
static void cmove(limb_t select, limb_t *x, const limb_t *y);
static void pow250(limb_t *result, const limb_t *x);
static void recip(limb_t *result, const limb_t *x);
static bool sqrt(limb_t *result, const limb_t *x);
// Constructor and destructor are private - cannot instantiate this class.
Curve25519() {}
~Curve25519() {}
friend class Ed25519;
};
#endif

View File

@ -0,0 +1,643 @@
/*
* 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.
*/
#include "Ed25519.h"
#include "Curve25519.h"
#include "Crypto.h"
#include "RNG.h"
#include "utility/LimbUtil.h"
#include <string.h>
/**
* \class Ed25519 Ed25519.h <Ed25519.h>
* \brief Digital signatures based on the elliptic curve modulo 2^255 - 19.
*
* The first step in creating a digital signature with Ed25519 is to
* generate a key pair:
*
* \code
* uint8_t privateKey[32];
* uint8_t publicKey[32];
*
* Ed25519::generatePrivateKey(privateKey);
* Ed25519::derivePublicKey(publicKey, privateKey);
* \endcode
*
* The application can store both the private and public key for later
* signing operations. Or it can store just the private key and then
* derive the public key at the point where signing is to occur.
*
* Message signing produces a 64-byte signature as follows:
*
* \code
* uint8_t message[N];
* uint8_t signature[64];
*
* Ed25519::sign(signature, privateKey, publicKey, message, N);
* \endcode
*
* And then to verify the signature:
*
* \code
* if (!Ed25519::verify(signature, publicKey, message, N)) {
* // The signature is invalid.
* ...
* }
* \endcode
*
* \note The public functions in this class need a substantial amount of
* stack space to store intermediate results while the curve function is
* being evaluated. About 1.5k of free stack space is recommended for safety.
*
* References: https://tools.ietf.org/id/draft-josefsson-eddsa-ed25519-02.txt
*
* \sa Curve25519
*/
/** @cond */
// 37095705934669439343138083508754565189542113879843219016388785533085940283555
static limb_t const numD[NUM_LIMBS_256BIT] PROGMEM = {
LIMB(0x135978A3), LIMB(0x75EB4DCA), LIMB(0x4141D8AB), LIMB(0x00700A4D),
LIMB(0x7779E898), LIMB(0x8CC74079), LIMB(0x2B6FFE73), LIMB(0x52036CEE)
};
// d * 2
static limb_t const numDx2[NUM_LIMBS_256BIT] PROGMEM = {
LIMB(0x26B2F159), LIMB(0xEBD69B94), LIMB(0x8283B156), LIMB(0x00E0149A),
LIMB(0xEEF3D130), LIMB(0x198E80F2), LIMB(0x56DFFCE7), LIMB(0x2406D9DC)
};
// Extended homogenous co-ordinates for the base point.
static limb_t const numBx[NUM_LIMBS_256BIT] PROGMEM = {
LIMB(0x8F25D51A), LIMB(0xC9562D60), LIMB(0x9525A7B2), LIMB(0x692CC760),
LIMB(0xFDD6DC5C), LIMB(0xC0A4E231), LIMB(0xCD6E53FE), LIMB(0x216936D3)
};
static limb_t const numBy[NUM_LIMBS_256BIT] PROGMEM = {
LIMB(0x66666658), LIMB(0x66666666), LIMB(0x66666666), LIMB(0x66666666),
LIMB(0x66666666), LIMB(0x66666666), LIMB(0x66666666), LIMB(0x66666666)
};
static limb_t const numBz[NUM_LIMBS_256BIT] PROGMEM = {
LIMB(0x00000001), LIMB(0x00000000), LIMB(0x00000000), LIMB(0x00000000),
LIMB(0x00000000), LIMB(0x00000000), LIMB(0x00000000), LIMB(0x00000000)
};
static limb_t const numBt[NUM_LIMBS_256BIT] PROGMEM = {
LIMB(0xA5B7DDA3), LIMB(0x6DDE8AB3), LIMB(0x775152F5), LIMB(0x20F09F80),
LIMB(0x64ABE37D), LIMB(0x66EA4E8E), LIMB(0xD78B7665), LIMB(0x67875F0F)
};
// 2^252 + 27742317777372353535851937790883648493
static limb_t const numQ[NUM_LIMBS_256BIT] PROGMEM = {
LIMB(0x5CF5D3ED), LIMB(0x5812631A), LIMB(0xA2F79CD6), LIMB(0x14DEF9DE),
LIMB(0x00000000), LIMB(0x00000000), LIMB(0x00000000), LIMB(0x10000000)
};
/** @endcond */
/**
* \brief Signs a message using a specific Ed25519 private key.
*
* \param signature The signature value.
* \param privateKey The private key to use to sign the message.
* \param publicKey The public key corresponding to \a privateKey.
* \param message Points to the message to be signed.
* \param len The length of the \a message to be signed.
*
* \sa verify(), derivePublicKey()
*/
void Ed25519::sign(uint8_t signature[64], const uint8_t privateKey[32],
const uint8_t publicKey[32], const void *message, size_t len)
{
SHA512 hash;
uint8_t *buf = (uint8_t *)(hash.state.w); // Reuse hash buffer to save memory.
limb_t a[NUM_LIMBS_256BIT];
limb_t r[NUM_LIMBS_256BIT];
limb_t k[NUM_LIMBS_256BIT];
limb_t t[NUM_LIMBS_512BIT + 1];
Point rB;
// Derive the secret scalar a and the message prefix from the private key.
deriveKeys(&hash, a, privateKey);
// Hash the prefix and the message to derive r.
hash.reset();
hash.update(buf + 32, 32);
hash.update(message, len);
hash.finalize(buf, 0);
reduceQFromBuffer(r, buf, t);
// Encode rB into the first half of the signature buffer as R.
mul(rB, r);
encodePoint(signature, rB);
// Hash R, A, and the message to get k.
hash.reset();
hash.update(signature, 32); // R
hash.update(publicKey, 32); // A
hash.update(message, len);
hash.finalize(buf, 0);
reduceQFromBuffer(k, buf, t);
// Compute s = (r + k * a) mod q.
Curve25519::mulNoReduce(t, k, a);
t[NUM_LIMBS_512BIT] = 0;
reduceQ(t, t);
BigNumberUtil::add(t, t, r, NUM_LIMBS_256BIT);
BigNumberUtil::reduceQuick_P(t, t, numQ, NUM_LIMBS_256BIT);
BigNumberUtil::packLE(signature + 32, 32, t, NUM_LIMBS_256BIT);
// Clean up.
clean(a);
clean(r);
clean(k);
clean(t);
clean(rB);
}
/**
* \brief Verifies a signature using a specific Ed25519 public key.
*
* \param signature The signature value to be verified.
* \param publicKey The public key to use to verify the signature.
* \param message The message whose signature is to be verified.
* \param len The length of the \a message to be verified.
*
* \return Returns true if the \a signature is valid for \a message;
* or false if the \a signature is not valid.
*
* \sa sign()
*/
bool Ed25519::verify(const uint8_t signature[64], const uint8_t publicKey[32],
const void *message, size_t len)
{
SHA512 hash;
Point A;
Point R;
Point sB;
Point kA;
uint8_t *k = (uint8_t *)(hash.state.w); // Reuse hash buffer to save memory.
bool result = false;
// Decode the public key and the R component of the signature.
if (decodePoint(A, publicKey) && decodePoint(R, signature)) {
// Reconstruct the k value from the signing step.
hash.reset();
hash.update(signature, 32);
hash.update(publicKey, 32);
hash.update(message, len);
hash.finalize(k, 0);
// Calculate s * B. The s value is stored temporarily in kA.t.
BigNumberUtil::unpackLE(kA.t, NUM_LIMBS_256BIT, signature + 32, 32);
mul(sB, kA.t, false);
// Calculate R + k * A. We don't need sB.t in equal() below,
// so we reuse that as a temporary buffer when reducing k.
reduceQFromBuffer(sB.t, k, kA.x);
mul(kA, sB.t, A, false);
add(R, kA);
// Compare s * B and R + k * A for equality.
result = equal(sB, R);
}
// Clean up and exit.
clean(A);
clean(R);
clean(sB);
clean(kA);
return result;
}
/**
* \brief Generates a private key for Ed25519 signing operations.
*
* \param privateKey The resulting private key.
*
* The private key is generated with \link RNGClass::rand() RNG.rand()\endlink.
* It is the caller's responsibility to ensure that the global random number
* pool has sufficient entropy to generate the 32 bytes of the key safely
* before calling this function.
*
* \sa derivePublicKey()
*/
void Ed25519::generatePrivateKey(uint8_t privateKey[32])
{
RNG.rand(privateKey, 32);
}
/**
* \brief Derives the public key from a private key.
*
* \param publicKey The public key.
* \param privateKey The private key.
*
* \sa generatePrivateKey()
*/
void Ed25519::derivePublicKey(uint8_t publicKey[32], const uint8_t privateKey[32])
{
SHA512 hash;
uint8_t *buf = (uint8_t *)(hash.state.w);
limb_t a[NUM_LIMBS_256BIT];
Point ptA;
// Derive the secret scalar a from the private key.
deriveKeys(&hash, a, privateKey);
// Compute the point A = aB and encode it.
mul(ptA, a);
encodePoint(publicKey, ptA);
// Clean up and exit.
clean(a);
clean(ptA);
}
/**
* \brief Reduces a number modulo q that was specified in a 512 bit buffer.
*
* \param result The result array, which must be NUM_LIMBS_256BIT limbs in size.
* \param buf The buffer containing the value to reduce in little-endian order.
* \param temp A temporary buffer of at least NUM_LIMBS_512BIT + 1 in size.
*
* \sa reduceQ()
*/
void Ed25519::reduceQFromBuffer(limb_t *result, const uint8_t buf[64], limb_t *temp)
{
BigNumberUtil::unpackLE(temp, NUM_LIMBS_512BIT, buf, 64);
temp[NUM_LIMBS_512BIT] = 0;
reduceQ(result, temp);
}
/**
* \brief Reduces a number modulo q.
*
* \param result The result array, which must be NUM_LIMBS_256BIT limbs in size.
* \param r The value to reduce, which must be NUM_LIMBS_512BIT + 1
* limbs in size.
*
* The \a r array will be modified by this function as a side effect of
* the division. It is allowed for \a result to be the same as \a r.
*
* \sa reduceQFromBuffer()
*/
void Ed25519::reduceQ(limb_t *result, limb_t *r)
{
// Algorithm from: http://en.wikipedia.org/wiki/Barrett_reduction
//
// We assume that r is less than or equal to (q - 1)^2.
//
// We want to compute result = r mod q. Find the smallest k such
// that 2^k > q. In our case, k = 253. Then set m = floor(4^k / q)
// and let r = r - q * floor(m * r / 4^k). This will be the result
// or it will be at most one subtraction of q away from the result.
//
// Note: 4^k = 4^253 = 2^506 = 2^512/2^6. We can more easily compute
// the result we want if we set m = floor(4^k * 2^6 / q) instead and
// then r = r - q * floor(m * r / 2^512). Because the slight extra
// precision in m, r is at most two subtractions of q away from the
// final result.
static limb_t const numM[NUM_LIMBS_256BIT + 1] PROGMEM = {
LIMB(0x0A2C131B), LIMB(0xED9CE5A3), LIMB(0x086329A7), LIMB(0x2106215D),
LIMB(0xFFFFFFEB), LIMB(0xFFFFFFFF), LIMB(0xFFFFFFFF), LIMB(0xFFFFFFFF),
0x0F
};
limb_t temp[NUM_LIMBS_512BIT + NUM_LIMBS_256BIT + 1];
// Multiply r by m.
BigNumberUtil::mul_P(temp, r, NUM_LIMBS_512BIT, numM, NUM_LIMBS_256BIT + 1);
// Multiply (m * r) / 2^512 by q and subtract it from r.
// We can ignore the high words of the subtraction result
// because they will all turn into zero after the subtraction.
BigNumberUtil::mul_P(temp, temp + NUM_LIMBS_512BIT, NUM_LIMBS_256BIT + 1,
numQ, NUM_LIMBS_256BIT);
BigNumberUtil::sub(r, r, temp, NUM_LIMBS_256BIT);
// Perform two subtractions of q from the result to reduce it.
BigNumberUtil::reduceQuick_P(result, r, numQ, NUM_LIMBS_256BIT);
BigNumberUtil::reduceQuick_P(result, result, numQ, NUM_LIMBS_256BIT);
// Clean up and exit.
clean(temp);
}
/**
* \brief Multiplies a value by a curve point.
*
* \param result The result of the multiplication.
* \param s The value, which must be NUM_LIMBS_256BIT limbs in size.
* \param p The curve point, which will be modified by this function.
* \param constTime Set to true if the evaluation must be constant-time
* because \a s is a secret value.
*/
void Ed25519::mul(Point &result, const limb_t *s, Point &p, bool constTime)
{
Point q;
limb_t A[NUM_LIMBS_256BIT];
limb_t B[NUM_LIMBS_256BIT];
limb_t C[NUM_LIMBS_256BIT];
limb_t D[NUM_LIMBS_256BIT];
limb_t mask, select;
uint8_t sposn, t;
// Initialize the result to (0, 1, 1, 0).
memset(&result, 0, sizeof(Point));
result.y[0] = 1;
result.z[0] = 1;
// Iterate over the 255 bits of "s" to calculate "s * p".
mask = 1;
sposn = 0;
for (t = 255; t > 0; --t) {
// Add p to the result to produce q. The specification refers
// to temporary variables A to H. We can dispense with E to H
// by using B, D, q.z, and q.t to hold those values temporarily.
select = s[sposn] & mask;
if (constTime || select) {
Curve25519::sub(A, result.y, result.x);
Curve25519::sub(C, p.y, p.x);
Curve25519::mul(A, A, C);
Curve25519::add(B, result.y, result.x);
Curve25519::add(C, p.y, p.x);
Curve25519::mul(B, B, C);
Curve25519::mul(C, result.t, p.t);
Curve25519::mul_P(C, C, numDx2);
Curve25519::mul(D, result.z, p.z);
Curve25519::add(D, D, D);
Curve25519::sub(q.t, B, A); // E = B - A
Curve25519::sub(q.z, D, C); // F = D - C
Curve25519::add(D, D, C); // G = D + C
Curve25519::add(B, B, A); // H = B + A
if (constTime) {
// Put the intermediate value into q.
Curve25519::mul(q.x, q.t, q.z); // q.x = E * F
Curve25519::mul(q.y, D, B); // q.y = G * H
Curve25519::mul(q.z, q.z, D); // q.z = F * G
Curve25519::mul(q.t, q.t, B); // q.t = E * H
// Copy q into the result if the current bit of s is 1.
Curve25519::cmove(select, result.x, q.x);
Curve25519::cmove(select, result.y, q.y);
Curve25519::cmove(select, result.z, q.z);
Curve25519::cmove(select, result.t, q.t);
} else {
// Put the intermediate value directly into the result.
Curve25519::mul(result.x, q.t, q.z); // q.x = E * F
Curve25519::mul(result.y, D, B); // q.y = G * H
Curve25519::mul(result.z, q.z, D); // q.z = F * G
Curve25519::mul(result.t, q.t, B); // q.t = E * H
}
}
// Double p for the next iteration.
Curve25519::sub(A, p.y, p.x);
Curve25519::square(A, A);
Curve25519::add(B, p.y, p.x);
Curve25519::square(B, B);
Curve25519::square(C, p.t);
Curve25519::mul_P(C, C, numDx2);
Curve25519::square(D, p.z);
Curve25519::add(D, D, D);
Curve25519::sub(p.t, B, A); // E = B - A
Curve25519::sub(p.z, D, C); // F = D - C
Curve25519::add(D, D, C); // G = D + C
Curve25519::add(B, B, A); // H = B + A
Curve25519::mul(p.x, p.t, p.z); // p.x = E * F
Curve25519::mul(p.y, D, B); // p.y = G * H
Curve25519::mul(p.z, p.z, D); // p.z = F * G
Curve25519::mul(p.t, p.t, B); // p.t = E * H
// Move onto the next bit of s from lowest to highest.
if (mask != (((limb_t)1) << (LIMB_BITS - 1))) {
mask <<= 1;
} else {
++sposn;
mask = 1;
}
}
// Clean up.
clean(q);
clean(A);
clean(B);
clean(C);
clean(D);
}
/**
* \brief Multiplies a value by the base point of the curve.
*
* \param result The result of the multiplication.
* \param s The value, which must be NUM_LIMBS_256BIT limbs in size.
* \param constTime Set to true if the evaluation must be constant-time
* because \a s is a secret values.
*/
void Ed25519::mul(Point &result, const limb_t *s, bool constTime)
{
Point P;
memcpy_P(P.x, numBx, sizeof(P.x));
memcpy_P(P.y, numBy, sizeof(P.y));
memcpy_P(P.z, numBz, sizeof(P.z));
memcpy_P(P.t, numBt, sizeof(P.t));
mul(result, s, P, constTime);
clean(P);
}
/**
* \brief Adds two curve points.
*
* \param p The first point and the result.
* \param q The second point.
*/
void Ed25519::add(Point &p, const Point &q)
{
limb_t A[NUM_LIMBS_256BIT];
limb_t B[NUM_LIMBS_256BIT];
limb_t C[NUM_LIMBS_256BIT];
limb_t D[NUM_LIMBS_256BIT];
Curve25519::sub(A, p.y, p.x);
Curve25519::sub(C, q.y, q.x);
Curve25519::mul(A, A, C);
Curve25519::add(B, p.y, p.x);
Curve25519::add(C, q.y, q.x);
Curve25519::mul(B, B, C);
Curve25519::mul(C, p.t, q.t);
Curve25519::mul_P(C, C, numDx2);
Curve25519::mul(D, p.z, q.z);
Curve25519::add(D, D, D);
Curve25519::sub(p.t, B, A); // E = B - A
Curve25519::sub(p.z, D, C); // F = D - C
Curve25519::add(D, D, C); // G = D + C
Curve25519::add(B, B, A); // H = B + A
Curve25519::mul(p.x, p.t, p.z); // p.x = E * F
Curve25519::mul(p.y, D, B); // p.y = G * H
Curve25519::mul(p.z, p.z, D); // p.z = F * G
Curve25519::mul(p.t, p.t, B); // p.t = E * H
clean(A);
clean(B);
clean(C);
clean(D);
}
/**
* \brief Determine if two curve points are equal.
*
* \param p The first curve point.
* \param q The second curve point.
*
* \return Returns true if \a p and \a q are equal; false otherwise.
*/
bool Ed25519::equal(const Point &p, const Point &q)
{
limb_t a[NUM_LIMBS_256BIT];
limb_t b[NUM_LIMBS_256BIT];
bool result = true;
Curve25519::mul(a, p.x, q.z);
Curve25519::mul(b, q.x, p.z);
result &= secure_compare(a, b, sizeof(a));
Curve25519::mul(a, p.y, q.z);
Curve25519::mul(b, q.y, p.z);
result &= secure_compare(a, b, sizeof(a));
clean(a);
clean(b);
return result;
}
/**
* \brief Encodes a curve point into a 32-byte buffer.
*
* \param buf The buffer to encode into.
* \param point The curve point to encode. This value will be modified
* the function and effectively destroyed.
*
* \sa decodePoint()
*/
void Ed25519::encodePoint(uint8_t *buf, Point &point)
{
// Convert the homogeneous coordinates into plain (x, y) coordinates:
// zinv = z^(-1) mod p
// x = x * zinv mod p
// y = y * zinv mod p
// We don't need the t coordinate, so use that to store zinv temporarily.
Curve25519::recip(point.t, point.z);
Curve25519::mul(point.x, point.x, point.t);
Curve25519::mul(point.y, point.y, point.t);
// Copy the lowest bit of x to the highest bit of y.
point.y[NUM_LIMBS_256BIT - 1] |= (point.x[0] << (LIMB_BITS - 1));
// Convert y into little-endian in the return buffer.
BigNumberUtil::packLE(buf, 32, point.y, NUM_LIMBS_256BIT);
}
/**
* \brief Decodes a curve point from a 32-byte buffer.
*
* \param point The curve point that was decoded from the buffer.
* \param buf The buffer to decode.
*
* \return Returns true if the point was decoded or false if the contents
* of the buffer do not correspond to a legitimate curve point.
*
* \note This function is not constant time so it should only be used
* on publicly-known values.
*/
bool Ed25519::decodePoint(Point &point, const uint8_t *buf)
{
limb_t temp[NUM_LIMBS_256BIT];
// Convert the input buffer from little-endian into the limbs of y.
BigNumberUtil::unpackLE(point.y, NUM_LIMBS_256BIT, buf, 32);
// The high bit of y is the sign bit for x.
limb_t sign = point.y[NUM_LIMBS_256BIT - 1] >> (LIMB_BITS - 1);
point.y[NUM_LIMBS_256BIT - 1] &= ~(((limb_t)1) << (LIMB_BITS - 1));
// Set z to 1.
memcpy_P(point.z, numBz, sizeof(point.z));
// Compute t = (y * y - 1) * modinv(d * y * y + 1).
Curve25519::square(point.t, point.y);
Curve25519::sub(point.x, point.t, point.z);
Curve25519::mul_P(point.t, point.t, numD);
Curve25519::add(point.t, point.t, point.z);
Curve25519::recip(temp, point.t);
Curve25519::mul(point.t, point.x, temp);
clean(temp);
// Check for t = 0.
limb_t check = point.t[0];
for (uint8_t posn = 1; posn < NUM_LIMBS_256BIT; ++posn)
check |= point.t[posn];
if (!check) {
// If the sign bit is set, then decoding has failed.
// Otherwise x is zero and we're done.
if (sign)
return false;
memset(point.x, 0, sizeof(point.x));
return true;
}
// Recover x by taking the sqrt of t and flipping the sign if necessary.
if (!Curve25519::sqrt(point.x, point.t))
return false;
if (sign != (point.x[0] & ((limb_t)1))) {
// The signs are different so we want the other square root.
memset(point.t, 0, sizeof(point.t));
Curve25519::sub(point.x, point.t, point.x);
}
// Finally, t = x * y.
Curve25519::mul(point.t, point.x, point.y);
return true;
}
/**
* \brief Derive key material from a 32-byte private key.
*
* \param hash SHA512 hash object from the caller for use in this function.
* The 64-byte output buffer within this hash object will contain the
* hash prefix on exit.
* \param a The secret scalar derived from \a privateKey. This must be
* NUM_LIMBS_256BIT limbs in size.
* \param privateKey The 32-byte private key to derive all other values from.
*/
void Ed25519::deriveKeys(SHA512 *hash, limb_t *a, const uint8_t privateKey[32])
{
// Hash the private key to get the "a" scalar and the message prefix.
uint8_t *buf = (uint8_t *)(hash->state.w); // Reuse hash buffer to save memory.
hash->reset();
hash->update(privateKey, 32);
hash->finalize(buf, 0);
buf[0] &= 0xF8;
buf[31] &= 0x7F;
buf[31] |= 0x40;
// Unpack the first half of the hash value into "a".
BigNumberUtil::unpackLE(a, NUM_LIMBS_256BIT, buf, 32);
}

View File

@ -0,0 +1,71 @@
/*
* 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.
*/
#ifndef CRYPTO_ED25519_h
#define CRYPTO_ED25519_h
#include "BigNumberUtil.h"
#include "SHA512.h"
class Ed25519
{
public:
static void sign(uint8_t signature[64], const uint8_t privateKey[32],
const uint8_t publicKey[32], const void *message,
size_t len);
static bool verify(const uint8_t signature[64], const uint8_t publicKey[32],
const void *message, size_t len);
static void generatePrivateKey(uint8_t privateKey[32]);
static void derivePublicKey(uint8_t publicKey[32], const uint8_t privateKey[32]);
private:
// Constructor and destructor are private - cannot instantiate this class.
Ed25519();
~Ed25519();
// Curve point represented in extended homogeneous coordinates.
struct Point
{
limb_t x[32 / sizeof(limb_t)];
limb_t y[32 / sizeof(limb_t)];
limb_t z[32 / sizeof(limb_t)];
limb_t t[32 / sizeof(limb_t)];
};
static void reduceQFromBuffer(limb_t *result, const uint8_t buf[64], limb_t *temp);
static void reduceQ(limb_t *result, limb_t *r);
static void mul(Point &result, const limb_t *s, Point &p, bool constTime = true);
static void mul(Point &result, const limb_t *s, bool constTime = true);
static void add(Point &p, const Point &q);
static bool equal(const Point &p, const Point &q);
static void encodePoint(uint8_t *buf, Point &point);
static bool decodePoint(Point &point, const uint8_t *buf);
static void deriveKeys(SHA512 *hash, limb_t *a, const uint8_t privateKey[32]);
};
#endif

View File

@ -23,6 +23,7 @@
#include "Poly1305.h"
#include "Crypto.h"
#include "utility/EndianUtil.h"
#include "utility/LimbUtil.h"
#include <string.h>
/**
@ -58,10 +59,8 @@
* http://cr.yp.to/mac.html
*/
// Useful sizes for limb array and word manipulation.
#define NUM_LIMBS_128BIT (16 / sizeof(limb_t))
#define NUM_LIMBS_130BIT ((16 / sizeof(limb_t)) + 1)
#define LIMB_BITS (sizeof(limb_t) * 8)
// Limb array with enough space for 130 bits.
#define NUM_LIMBS_130BIT (NUM_LIMBS_128BIT + 1)
// Endian helper macros for limbs and arrays of limbs.
#if BIGNUMBER_LIMB_8BIT

View File

@ -25,6 +25,8 @@
#include "Hash.h"
class Ed25519;
class SHA512 : public Hash
{
public:
@ -53,6 +55,8 @@ private:
} state;
void processChunk();
friend class Ed25519;
};
#endif

View File

@ -99,10 +99,10 @@ 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 posn = NUM_LIMBS; posn > 0; --posn) {
for (uint8_t bit = LIMB_BITS; bit > 0; ) {
bit -= 4;
Serial.print(hexchars[(x[posn] >> bit) & 0x0F]);
Serial.print(hexchars[(x[posn - 1] >> bit) & 0x0F]);
}
Serial.print(' ');
}
@ -603,6 +603,68 @@ void testRecip()
Serial.println();
}
void testSqrt(const char *x)
{
Serial.print("sqrt(");
printProgMem(x);
Serial.print("^2): ");
Serial.flush();
fromString(arg1, NUM_LIMBS, x);
Curve25519::square(arg2, arg1);
bool ok = Curve25519::sqrt(result, arg2);
if (ok) {
ok = (compare(result, arg1) == 0);
if (!ok) {
// Check the negation of arg1 as well because we could
// have ended up with the inverse of the original value.
memset(temp, 0, sizeof(temp));
Curve25519::sub(temp, temp, arg1);
ok = (compare(result, temp) == 0);
}
} else {
Serial.println("no sqrt ... ");
}
if (ok) {
Serial.println("ok");
} else {
Serial.println("failed");
printNumber("actual", result);
printNumber("expected", arg1);
}
}
void testNoSqrt(const char *x)
{
Serial.print("no sqrt(");
printProgMem(x);
Serial.print("): ");
Serial.flush();
fromString(arg1, NUM_LIMBS, x);
bool ok = !Curve25519::sqrt(result, arg1);
if (ok) {
Serial.println("ok");
} else {
Serial.println("failed");
printNumber("actual", result);
}
}
void testSqrt()
{
Serial.println("Square root:");
foreach_number (x) {
testSqrt(x);
}
testNoSqrt(num_128);
testNoSqrt(num_pi);
Serial.println();
}
void setup()
{
Serial.begin(9600);
@ -613,6 +675,7 @@ void setup()
testMulA24();
testSwap();
testRecip();
testSqrt();
}
void loop()

View File

@ -0,0 +1,241 @@
/*
* 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 Ed25519 algorithm.
*/
#include <Crypto.h>
#include <Ed25519.h>
#include <RNG.h>
#include <utility/ProgMemUtil.h>
#include <string.h>
struct TestVector
{
const char *name;
uint8_t privateKey[32];
uint8_t publicKey[32];
uint8_t message[2];
size_t len;
uint8_t signature[64];
};
// Test vectors for Ed25519 from:
// https://tools.ietf.org/id/draft-josefsson-eddsa-ed25519-02.txt
static TestVector const testVectorEd25519_1 PROGMEM = {
.name = "Ed25519 #1",
.privateKey = {0x9d, 0x61, 0xb1, 0x9d, 0xef, 0xfd, 0x5a, 0x60,
0xba, 0x84, 0x4a, 0xf4, 0x92, 0xec, 0x2c, 0xc4,
0x44, 0x49, 0xc5, 0x69, 0x7b, 0x32, 0x69, 0x19,
0x70, 0x3b, 0xac, 0x03, 0x1c, 0xae, 0x7f, 0x60},
.publicKey = {0xd7, 0x5a, 0x98, 0x01, 0x82, 0xb1, 0x0a, 0xb7,
0xd5, 0x4b, 0xfe, 0xd3, 0xc9, 0x64, 0x07, 0x3a,
0x0e, 0xe1, 0x72, 0xf3, 0xda, 0xa6, 0x23, 0x25,
0xaf, 0x02, 0x1a, 0x68, 0xf7, 0x07, 0x51, 0x1a},
.message = {0x00, 0x00},
.len = 0,
.signature = {0xe5, 0x56, 0x43, 0x00, 0xc3, 0x60, 0xac, 0x72,
0x90, 0x86, 0xe2, 0xcc, 0x80, 0x6e, 0x82, 0x8a,
0x84, 0x87, 0x7f, 0x1e, 0xb8, 0xe5, 0xd9, 0x74,
0xd8, 0x73, 0xe0, 0x65, 0x22, 0x49, 0x01, 0x55,
0x5f, 0xb8, 0x82, 0x15, 0x90, 0xa3, 0x3b, 0xac,
0xc6, 0x1e, 0x39, 0x70, 0x1c, 0xf9, 0xb4, 0x6b,
0xd2, 0x5b, 0xf5, 0xf0, 0x59, 0x5b, 0xbe, 0x24,
0x65, 0x51, 0x41, 0x43, 0x8e, 0x7a, 0x10, 0x0b}
};
static TestVector const testVectorEd25519_2 PROGMEM = {
.name = "Ed25519 #2",
.privateKey = {0x4c, 0xcd, 0x08, 0x9b, 0x28, 0xff, 0x96, 0xda,
0x9d, 0xb6, 0xc3, 0x46, 0xec, 0x11, 0x4e, 0x0f,
0x5b, 0x8a, 0x31, 0x9f, 0x35, 0xab, 0xa6, 0x24,
0xda, 0x8c, 0xf6, 0xed, 0x4f, 0xb8, 0xa6, 0xfb},
.publicKey = {0x3d, 0x40, 0x17, 0xc3, 0xe8, 0x43, 0x89, 0x5a,
0x92, 0xb7, 0x0a, 0xa7, 0x4d, 0x1b, 0x7e, 0xbc,
0x9c, 0x98, 0x2c, 0xcf, 0x2e, 0xc4, 0x96, 0x8c,
0xc0, 0xcd, 0x55, 0xf1, 0x2a, 0xf4, 0x66, 0x0c},
.message = {0x72, 0x00},
.len = 1,
.signature = {0x92, 0xa0, 0x09, 0xa9, 0xf0, 0xd4, 0xca, 0xb8,
0x72, 0x0e, 0x82, 0x0b, 0x5f, 0x64, 0x25, 0x40,
0xa2, 0xb2, 0x7b, 0x54, 0x16, 0x50, 0x3f, 0x8f,
0xb3, 0x76, 0x22, 0x23, 0xeb, 0xdb, 0x69, 0xda,
0x08, 0x5a, 0xc1, 0xe4, 0x3e, 0x15, 0x99, 0x6e,
0x45, 0x8f, 0x36, 0x13, 0xd0, 0xf1, 0x1d, 0x8c,
0x38, 0x7b, 0x2e, 0xae, 0xb4, 0x30, 0x2a, 0xee,
0xb0, 0x0d, 0x29, 0x16, 0x12, 0xbb, 0x0c, 0x00}
};
static TestVector testVector;
void printNumber(const char *name, const uint8_t *x, uint8_t len)
{
static const char hexchars[] = "0123456789ABCDEF";
Serial.print(name);
Serial.print(" = ");
for (uint8_t posn = 0; posn < len; ++posn) {
Serial.print(hexchars[(x[posn] >> 4) & 0x0F]);
Serial.print(hexchars[x[posn] & 0x0F]);
}
Serial.println();
}
void testFixedVectors(const struct TestVector *test)
{
// Copy the test vector out of program memory.
memcpy_P(&testVector, test, sizeof(TestVector));
test = &testVector;
// Sign using the test vector.
uint8_t signature[64];
Serial.print(test->name);
Serial.print(" sign ... ");
Serial.flush();
unsigned long start = micros();
Ed25519::sign(signature, test->privateKey, test->publicKey,
test->message, test->len);
unsigned long elapsed = micros() - start;
if (memcmp(signature, test->signature, 64) == 0) {
Serial.print("ok");
} else {
Serial.println("failed");
printNumber("actual ", signature, 64);
printNumber("expected", test->signature, 64);
}
Serial.print(" (elapsed ");
Serial.print(elapsed);
Serial.println(" us)");
// Verify using the test vector.
Serial.print(test->name);
Serial.print(" verify ... ");
Serial.flush();
start = micros();
bool verified = Ed25519::verify(signature, test->publicKey, test->message, test->len);
elapsed = micros() - start;
if (verified) {
Serial.print("ok");
} else {
Serial.println("failed");
}
Serial.print(" (elapsed ");
Serial.print(elapsed);
Serial.println(" us)");
// Check derivation of the public key from the private key.
Serial.print(test->name);
Serial.print(" derive public key ... ");
Serial.flush();
start = micros();
Ed25519::derivePublicKey(signature, test->privateKey);
elapsed = micros() - start;
if (memcmp(signature, test->publicKey, 32) == 0) {
Serial.print("ok");
} else {
Serial.println("failed");
printNumber("actual ", signature, 32);
printNumber("expected", test->publicKey, 32);
}
Serial.print(" (elapsed ");
Serial.print(elapsed);
Serial.println(" us)");
}
void testFixedVectors()
{
//Serial.println("Fixed test vectors:");
testFixedVectors(&testVectorEd25519_1);
testFixedVectors(&testVectorEd25519_2);
}
/*
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 applications should of course use a proper noise source.
RNG.begin("TestEd25519 1.0", 500);
// Perform the tests.
testFixedVectors();
Serial.println();
//testDH();
//Serial.println();
}
void loop()
{
}

View File

@ -0,0 +1,59 @@
/*
* 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.
*/
#ifndef CRYPTO_LIMBUTIL_H
#define CRYPTO_LIMBUTIL_H
#include "ProgMemUtil.h"
// Number of limbs in a big number value of various sizes.
#define NUM_LIMBS_128BIT (16 / sizeof(limb_t))
#define NUM_LIMBS_256BIT (32 / sizeof(limb_t))
#define NUM_LIMBS_512BIT (64 / sizeof(limb_t))
// The number of bits in a limb.
#define LIMB_BITS (8 * sizeof(limb_t))
// Read a limb-sized quantity from program memory.
#if BIGNUMBER_LIMB_8BIT
#define pgm_read_limb(x) (pgm_read_byte((x)))
#elif BIGNUMBER_LIMB_16BIT
#define pgm_read_limb(x) (pgm_read_word((x)))
#elif BIGNUMBER_LIMB_32BIT
#define pgm_read_limb(x) (pgm_read_dword((x)))
#endif
// Expand a 32-bit value into a set of limbs depending upon the limb size.
// This is used when initializing constant big number values in the code.
#if BIGNUMBER_LIMB_8BIT
#define LIMB(value) ((uint8_t)(value)), \
((uint8_t)((value) >> 8)), \
((uint8_t)((value) >> 16)), \
((uint8_t)((value) >> 24))
#elif BIGNUMBER_LIMB_16BIT
#define LIMB(value) ((uint16_t)(value)), \
((uint16_t)((value) >> 16))
#elif BIGNUMBER_LIMB_32BIT
#define LIMB(value) (value)
#endif
#endif