From 786e52f923ee4930e8d0bc6f43a0a2f2b1c21f4d Mon Sep 17 00:00:00 2001 From: Rhys Weatherley Date: Sat, 18 Apr 2015 08:42:37 +1000 Subject: [PATCH] Ed25519 signature algorithm --- doc/crypto.dox | 5 +- doc/mainpage.dox | 2 +- libraries/Crypto/BigNumberUtil.cpp | 271 ++++++++ libraries/Crypto/BigNumberUtil.h | 18 + libraries/Crypto/Curve25519.cpp | 398 +++++++---- libraries/Crypto/Curve25519.h | 13 +- libraries/Crypto/Ed25519.cpp | 643 ++++++++++++++++++ libraries/Crypto/Ed25519.h | 71 ++ libraries/Crypto/Poly1305.cpp | 7 +- libraries/Crypto/SHA512.h | 4 + .../TestCurve25519Math/TestCurve25519Math.ino | 67 +- .../examples/TestEd25519/TestEd25519.ino | 241 +++++++ libraries/Crypto/utility/LimbUtil.h | 59 ++ 13 files changed, 1672 insertions(+), 127 deletions(-) create mode 100644 libraries/Crypto/Ed25519.cpp create mode 100644 libraries/Crypto/Ed25519.h create mode 100644 libraries/Crypto/examples/TestEd25519/TestEd25519.ino create mode 100644 libraries/Crypto/utility/LimbUtil.h 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()3119msRaw curve evaluation Curve25519::dh1()3121msFirst half of Diffie-Hellman key agreement Curve25519::dh2()3120msSecond half of Diffie-Hellman key agreement +Ed25519::sign()5688msDigital signature generation +Ed25519::verify()9030msDigital signature verification +Ed25519::derivePublicKey()5642msDerive 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