diff --git a/doc/crypto.dox b/doc/crypto.dox
index bcbf3774..2aef5ed6 100644
--- a/doc/crypto.dox
+++ b/doc/crypto.dox
@@ -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:
Curve25519::eval() | 3119ms | Raw curve evaluation |
Curve25519::dh1() | 3121ms | First half of Diffie-Hellman key agreement |
Curve25519::dh2() | 3120ms | Second half of Diffie-Hellman key agreement |
+Ed25519::sign() | 5688ms | Digital signature generation |
+Ed25519::verify() | 9030ms | Digital signature verification |
+Ed25519::derivePublicKey() | 5642ms | Derive a public key from a private key |
Where a cipher supports more than one key size (such as ChaCha), the values
diff --git a/doc/mainpage.dox b/doc/mainpage.dox
index 0f31b58c..8b2ba5f7 100644
--- a/doc/mainpage.dox
+++ b/doc/mainpage.dox
@@ -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.
diff --git a/libraries/Crypto/BigNumberUtil.cpp b/libraries/Crypto/BigNumberUtil.cpp
index 3a56c2d4..1c413cb4 100644
--- a/libraries/Crypto/BigNumberUtil.cpp
+++ b/libraries/Crypto/BigNumberUtil.cpp
@@ -22,6 +22,7 @@
#include "BigNumberUtil.h"
#include "utility/EndianUtil.h"
+#include "utility/LimbUtil.h"
#include
/**
@@ -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;
+ }
+}
diff --git a/libraries/Crypto/BigNumberUtil.h b/libraries/Crypto/BigNumberUtil.h
index 575b57ca..286ebd81 100644
--- a/libraries/Crypto/BigNumberUtil.h
+++ b/libraries/Crypto/BigNumberUtil.h
@@ -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() {}
diff --git a/libraries/Crypto/Curve25519.cpp b/libraries/Crypto/Curve25519.cpp
index 9f8986e4..41165478 100644
--- a/libraries/Crypto/Curve25519.cpp
+++ b/libraries/Crypto/Curve25519.cpp
@@ -23,7 +23,7 @@
#include "Curve25519.h"
#include "Crypto.h"
#include "RNG.h"
-#include "utility/ProgMemUtil.h"
+#include "utility/LimbUtil.h"
#include
/**
@@ -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;
}
diff --git a/libraries/Crypto/Curve25519.h b/libraries/Crypto/Curve25519.h
index c88301db..b95a9fae 100644
--- a/libraries/Crypto/Curve25519.h
+++ b/libraries/Crypto/Curve25519.h
@@ -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
diff --git a/libraries/Crypto/Ed25519.cpp b/libraries/Crypto/Ed25519.cpp
new file mode 100644
index 00000000..004d225a
--- /dev/null
+++ b/libraries/Crypto/Ed25519.cpp
@@ -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
+
+/**
+ * \class Ed25519 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);
+}
diff --git a/libraries/Crypto/Ed25519.h b/libraries/Crypto/Ed25519.h
new file mode 100644
index 00000000..bbdd9f20
--- /dev/null
+++ b/libraries/Crypto/Ed25519.h
@@ -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
diff --git a/libraries/Crypto/Poly1305.cpp b/libraries/Crypto/Poly1305.cpp
index 7d6524e5..0cfbfda0 100644
--- a/libraries/Crypto/Poly1305.cpp
+++ b/libraries/Crypto/Poly1305.cpp
@@ -23,6 +23,7 @@
#include "Poly1305.h"
#include "Crypto.h"
#include "utility/EndianUtil.h"
+#include "utility/LimbUtil.h"
#include
/**
@@ -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
diff --git a/libraries/Crypto/SHA512.h b/libraries/Crypto/SHA512.h
index 1530d548..d1a069f7 100644
--- a/libraries/Crypto/SHA512.h
+++ b/libraries/Crypto/SHA512.h
@@ -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
diff --git a/libraries/Crypto/examples/TestCurve25519Math/TestCurve25519Math.ino b/libraries/Crypto/examples/TestCurve25519Math/TestCurve25519Math.ino
index 9bb30dfd..76c09b84 100644
--- a/libraries/Crypto/examples/TestCurve25519Math/TestCurve25519Math.ino
+++ b/libraries/Crypto/examples/TestCurve25519Math/TestCurve25519Math.ino
@@ -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()
diff --git a/libraries/Crypto/examples/TestEd25519/TestEd25519.ino b/libraries/Crypto/examples/TestEd25519/TestEd25519.ino
new file mode 100644
index 00000000..2ce0190c
--- /dev/null
+++ b/libraries/Crypto/examples/TestEd25519/TestEd25519.ino
@@ -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
+#include
+#include
+#include
+#include
+
+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()
+{
+}
diff --git a/libraries/Crypto/utility/LimbUtil.h b/libraries/Crypto/utility/LimbUtil.h
new file mode 100644
index 00000000..410b0f6b
--- /dev/null
+++ b/libraries/Crypto/utility/LimbUtil.h
@@ -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