/* * 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; 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); }