Arduino Cryptography Library
Ed25519.cpp
1 /*
2  * Copyright (C) 2015 Southern Storm Software, Pty Ltd.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included
12  * in all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20  * DEALINGS IN THE SOFTWARE.
21  */
22 
23 #include "Ed25519.h"
24 #include "Curve25519.h"
25 #include "Crypto.h"
26 #include "RNG.h"
27 #include "utility/LimbUtil.h"
28 #include <string.h>
29 
78 // 37095705934669439343138083508754565189542113879843219016388785533085940283555
79 static limb_t const numD[NUM_LIMBS_256BIT] PROGMEM = {
80  LIMB_PAIR(0x135978A3, 0x75EB4DCA), LIMB_PAIR(0x4141D8AB, 0x00700A4D),
81  LIMB_PAIR(0x7779E898, 0x8CC74079), LIMB_PAIR(0x2B6FFE73, 0x52036CEE)
82 };
83 
84 // d * 2
85 static limb_t const numDx2[NUM_LIMBS_256BIT] PROGMEM = {
86  LIMB_PAIR(0x26B2F159, 0xEBD69B94), LIMB_PAIR(0x8283B156, 0x00E0149A),
87  LIMB_PAIR(0xEEF3D130, 0x198E80F2), LIMB_PAIR(0x56DFFCE7, 0x2406D9DC)
88 };
89 
90 // Extended homogenous co-ordinates for the base point.
91 static limb_t const numBx[NUM_LIMBS_256BIT] PROGMEM = {
92  LIMB_PAIR(0x8F25D51A, 0xC9562D60), LIMB_PAIR(0x9525A7B2, 0x692CC760),
93  LIMB_PAIR(0xFDD6DC5C, 0xC0A4E231), LIMB_PAIR(0xCD6E53FE, 0x216936D3)
94 };
95 static limb_t const numBy[NUM_LIMBS_256BIT] PROGMEM = {
96  LIMB_PAIR(0x66666658, 0x66666666), LIMB_PAIR(0x66666666, 0x66666666),
97  LIMB_PAIR(0x66666666, 0x66666666), LIMB_PAIR(0x66666666, 0x66666666)
98 };
99 static limb_t const numBz[NUM_LIMBS_256BIT] PROGMEM = {
100  LIMB_PAIR(0x00000001, 0x00000000), LIMB_PAIR(0x00000000, 0x00000000),
101  LIMB_PAIR(0x00000000, 0x00000000), LIMB_PAIR(0x00000000, 0x00000000)
102 };
103 static limb_t const numBt[NUM_LIMBS_256BIT] PROGMEM = {
104  LIMB_PAIR(0xA5B7DDA3, 0x6DDE8AB3), LIMB_PAIR(0x775152F5, 0x20F09F80),
105  LIMB_PAIR(0x64ABE37D, 0x66EA4E8E), LIMB_PAIR(0xD78B7665, 0x67875F0F)
106 };
107 
108 // 2^252 + 27742317777372353535851937790883648493
109 static limb_t const numQ[NUM_LIMBS_256BIT] PROGMEM = {
110  LIMB_PAIR(0x5CF5D3ED, 0x5812631A), LIMB_PAIR(0xA2F79CD6, 0x14DEF9DE),
111  LIMB_PAIR(0x00000000, 0x00000000), LIMB_PAIR(0x00000000, 0x10000000)
112 };
113 
127 void Ed25519::sign(uint8_t signature[64], const uint8_t privateKey[32],
128  const uint8_t publicKey[32], const void *message, size_t len)
129 {
130  SHA512 hash;
131  uint8_t *buf = (uint8_t *)(hash.state.w); // Reuse hash buffer to save memory.
132  limb_t a[NUM_LIMBS_256BIT];
133  limb_t r[NUM_LIMBS_256BIT];
134  limb_t k[NUM_LIMBS_256BIT];
135  limb_t t[NUM_LIMBS_512BIT + 1];
136  Point rB;
137 
138  // Derive the secret scalar a and the message prefix from the private key.
139  deriveKeys(&hash, a, privateKey);
140 
141  // Hash the prefix and the message to derive r.
142  hash.reset();
143  hash.update(buf + 32, 32);
144  hash.update(message, len);
145  hash.finalize(buf, 0);
146  reduceQFromBuffer(r, buf, t);
147 
148  // Encode rB into the first half of the signature buffer as R.
149  mul(rB, r);
150  encodePoint(signature, rB);
151 
152  // Hash R, A, and the message to get k.
153  hash.reset();
154  hash.update(signature, 32); // R
155  hash.update(publicKey, 32); // A
156  hash.update(message, len);
157  hash.finalize(buf, 0);
158  reduceQFromBuffer(k, buf, t);
159 
160  // Compute s = (r + k * a) mod q.
161  Curve25519::mulNoReduce(t, k, a);
162  t[NUM_LIMBS_512BIT] = 0;
163  reduceQ(t, t);
164  BigNumberUtil::add(t, t, r, NUM_LIMBS_256BIT);
165  BigNumberUtil::reduceQuick_P(t, t, numQ, NUM_LIMBS_256BIT);
166  BigNumberUtil::packLE(signature + 32, 32, t, NUM_LIMBS_256BIT);
167 
168  // Clean up.
169  clean(a);
170  clean(r);
171  clean(k);
172  clean(t);
173  clean(rB);
174 }
175 
189 bool Ed25519::verify(const uint8_t signature[64], const uint8_t publicKey[32],
190  const void *message, size_t len)
191 {
192  SHA512 hash;
193  Point A;
194  Point R;
195  Point sB;
196  Point kA;
197  uint8_t *k = (uint8_t *)(hash.state.w); // Reuse hash buffer to save memory.
198  bool result = false;
199 
200  // Decode the public key and the R component of the signature.
201  if (decodePoint(A, publicKey) && decodePoint(R, signature)) {
202  // Reconstruct the k value from the signing step.
203  hash.reset();
204  hash.update(signature, 32);
205  hash.update(publicKey, 32);
206  hash.update(message, len);
207  hash.finalize(k, 0);
208 
209  // Calculate s * B. The s value is stored temporarily in kA.t.
210  BigNumberUtil::unpackLE(kA.t, NUM_LIMBS_256BIT, signature + 32, 32);
211  mul(sB, kA.t, false);
212 
213  // Calculate R + k * A. We don't need sB.t in equal() below,
214  // so we reuse that as a temporary buffer when reducing k.
215  reduceQFromBuffer(sB.t, k, kA.x);
216  mul(kA, sB.t, A, false);
217  add(R, kA);
218 
219  // Compare s * B and R + k * A for equality.
220  result = equal(sB, R);
221  }
222 
223  // Clean up and exit.
224  clean(A);
225  clean(R);
226  clean(sB);
227  clean(kA);
228  return result;
229 }
230 
243 void Ed25519::generatePrivateKey(uint8_t privateKey[32])
244 {
245  RNG.rand(privateKey, 32);
246 }
247 
256 void Ed25519::derivePublicKey(uint8_t publicKey[32], const uint8_t privateKey[32])
257 {
258  SHA512 hash;
259  limb_t a[NUM_LIMBS_256BIT];
260  Point ptA;
261 
262  // Derive the secret scalar a from the private key.
263  deriveKeys(&hash, a, privateKey);
264 
265  // Compute the point A = aB and encode it.
266  mul(ptA, a);
267  encodePoint(publicKey, ptA);
268 
269  // Clean up and exit.
270  clean(a);
271  clean(ptA);
272 }
273 
283 void Ed25519::reduceQFromBuffer(limb_t *result, const uint8_t buf[64], limb_t *temp)
284 {
285  BigNumberUtil::unpackLE(temp, NUM_LIMBS_512BIT, buf, 64);
286  temp[NUM_LIMBS_512BIT] = 0;
287  reduceQ(result, temp);
288 }
289 
302 void Ed25519::reduceQ(limb_t *result, limb_t *r)
303 {
304  // Algorithm from: http://en.wikipedia.org/wiki/Barrett_reduction
305  //
306  // We assume that r is less than or equal to (q - 1)^2.
307  //
308  // We want to compute result = r mod q. Find the smallest k such
309  // that 2^k > q. In our case, k = 253. Then set m = floor(4^k / q)
310  // and let r = r - q * floor(m * r / 4^k). This will be the result
311  // or it will be at most one subtraction of q away from the result.
312  //
313  // Note: 4^k = 4^253 = 2^506 = 2^512/2^6. We can more easily compute
314  // the result we want if we set m = floor(4^k * 2^6 / q) instead and
315  // then r = r - q * floor(m * r / 2^512). Because the slight extra
316  // precision in m, r is at most two subtractions of q away from the
317  // final result.
318  static limb_t const numM[NUM_LIMBS_256BIT + 1] PROGMEM = {
319  LIMB_PAIR(0x0A2C131B, 0xED9CE5A3), LIMB_PAIR(0x086329A7, 0x2106215D),
320  LIMB_PAIR(0xFFFFFFEB, 0xFFFFFFFF), LIMB_PAIR(0xFFFFFFFF, 0xFFFFFFFF),
321  0x0F
322  };
323  limb_t temp[NUM_LIMBS_512BIT + NUM_LIMBS_256BIT + 1];
324 
325  // Multiply r by m.
326  BigNumberUtil::mul_P(temp, r, NUM_LIMBS_512BIT, numM, NUM_LIMBS_256BIT + 1);
327 
328  // Multiply (m * r) / 2^512 by q and subtract it from r.
329  // We can ignore the high words of the subtraction result
330  // because they will all turn into zero after the subtraction.
331  BigNumberUtil::mul_P(temp, temp + NUM_LIMBS_512BIT, NUM_LIMBS_256BIT + 1,
332  numQ, NUM_LIMBS_256BIT);
333  BigNumberUtil::sub(r, r, temp, NUM_LIMBS_256BIT);
334 
335  // Perform two subtractions of q from the result to reduce it.
336  BigNumberUtil::reduceQuick_P(result, r, numQ, NUM_LIMBS_256BIT);
337  BigNumberUtil::reduceQuick_P(result, result, numQ, NUM_LIMBS_256BIT);
338 
339  // Clean up and exit.
340  clean(temp);
341 }
342 
352 void Ed25519::mul(Point &result, const limb_t *s, Point &p, bool constTime)
353 {
354  Point q;
355  limb_t A[NUM_LIMBS_256BIT];
356  limb_t B[NUM_LIMBS_256BIT];
357  limb_t C[NUM_LIMBS_256BIT];
358  limb_t D[NUM_LIMBS_256BIT];
359  limb_t mask, select;
360  uint8_t sposn, t;
361 
362  // Initialize the result to (0, 1, 1, 0).
363  memset(&result, 0, sizeof(Point));
364  result.y[0] = 1;
365  result.z[0] = 1;
366 
367  // Iterate over the 255 bits of "s" to calculate "s * p".
368  mask = 1;
369  sposn = 0;
370  for (t = 255; t > 0; --t) {
371  // Add p to the result to produce q. The specification refers
372  // to temporary variables A to H. We can dispense with E to H
373  // by using B, D, q.z, and q.t to hold those values temporarily.
374  select = s[sposn] & mask;
375  if (constTime || select) {
376  Curve25519::sub(A, result.y, result.x);
377  Curve25519::sub(C, p.y, p.x);
378  Curve25519::mul(A, A, C);
379  Curve25519::add(B, result.y, result.x);
380  Curve25519::add(C, p.y, p.x);
381  Curve25519::mul(B, B, C);
382  Curve25519::mul(C, result.t, p.t);
383  Curve25519::mul_P(C, C, numDx2);
384  Curve25519::mul(D, result.z, p.z);
385  Curve25519::add(D, D, D);
386  Curve25519::sub(q.t, B, A); // E = B - A
387  Curve25519::sub(q.z, D, C); // F = D - C
388  Curve25519::add(D, D, C); // G = D + C
389  Curve25519::add(B, B, A); // H = B + A
390  if (constTime) {
391  // Put the intermediate value into q.
392  Curve25519::mul(q.x, q.t, q.z); // q.x = E * F
393  Curve25519::mul(q.y, D, B); // q.y = G * H
394  Curve25519::mul(q.z, q.z, D); // q.z = F * G
395  Curve25519::mul(q.t, q.t, B); // q.t = E * H
396 
397  // Copy q into the result if the current bit of s is 1.
398  Curve25519::cmove(select, result.x, q.x);
399  Curve25519::cmove(select, result.y, q.y);
400  Curve25519::cmove(select, result.z, q.z);
401  Curve25519::cmove(select, result.t, q.t);
402  } else {
403  // Put the intermediate value directly into the result.
404  Curve25519::mul(result.x, q.t, q.z); // q.x = E * F
405  Curve25519::mul(result.y, D, B); // q.y = G * H
406  Curve25519::mul(result.z, q.z, D); // q.z = F * G
407  Curve25519::mul(result.t, q.t, B); // q.t = E * H
408  }
409  }
410 
411  // Double p for the next iteration.
412  Curve25519::sub(A, p.y, p.x);
413  Curve25519::square(A, A);
414  Curve25519::add(B, p.y, p.x);
415  Curve25519::square(B, B);
416  Curve25519::square(C, p.t);
417  Curve25519::mul_P(C, C, numDx2);
418  Curve25519::square(D, p.z);
419  Curve25519::add(D, D, D);
420  Curve25519::sub(p.t, B, A); // E = B - A
421  Curve25519::sub(p.z, D, C); // F = D - C
422  Curve25519::add(D, D, C); // G = D + C
423  Curve25519::add(B, B, A); // H = B + A
424  Curve25519::mul(p.x, p.t, p.z); // p.x = E * F
425  Curve25519::mul(p.y, D, B); // p.y = G * H
426  Curve25519::mul(p.z, p.z, D); // p.z = F * G
427  Curve25519::mul(p.t, p.t, B); // p.t = E * H
428 
429  // Move onto the next bit of s from lowest to highest.
430  if (mask != (((limb_t)1) << (LIMB_BITS - 1))) {
431  mask <<= 1;
432  } else {
433  ++sposn;
434  mask = 1;
435  }
436  }
437 
438  // Clean up.
439  clean(q);
440  clean(A);
441  clean(B);
442  clean(C);
443  clean(D);
444 }
445 
454 void Ed25519::mul(Point &result, const limb_t *s, bool constTime)
455 {
456  Point P;
457  memcpy_P(P.x, numBx, sizeof(P.x));
458  memcpy_P(P.y, numBy, sizeof(P.y));
459  memcpy_P(P.z, numBz, sizeof(P.z));
460  memcpy_P(P.t, numBt, sizeof(P.t));
461  mul(result, s, P, constTime);
462  clean(P);
463 }
464 
471 void Ed25519::add(Point &p, const Point &q)
472 {
473  limb_t A[NUM_LIMBS_256BIT];
474  limb_t B[NUM_LIMBS_256BIT];
475  limb_t C[NUM_LIMBS_256BIT];
476  limb_t D[NUM_LIMBS_256BIT];
477 
478  Curve25519::sub(A, p.y, p.x);
479  Curve25519::sub(C, q.y, q.x);
480  Curve25519::mul(A, A, C);
481  Curve25519::add(B, p.y, p.x);
482  Curve25519::add(C, q.y, q.x);
483  Curve25519::mul(B, B, C);
484  Curve25519::mul(C, p.t, q.t);
485  Curve25519::mul_P(C, C, numDx2);
486  Curve25519::mul(D, p.z, q.z);
487  Curve25519::add(D, D, D);
488  Curve25519::sub(p.t, B, A); // E = B - A
489  Curve25519::sub(p.z, D, C); // F = D - C
490  Curve25519::add(D, D, C); // G = D + C
491  Curve25519::add(B, B, A); // H = B + A
492  Curve25519::mul(p.x, p.t, p.z); // p.x = E * F
493  Curve25519::mul(p.y, D, B); // p.y = G * H
494  Curve25519::mul(p.z, p.z, D); // p.z = F * G
495  Curve25519::mul(p.t, p.t, B); // p.t = E * H
496 
497  clean(A);
498  clean(B);
499  clean(C);
500  clean(D);
501 }
502 
511 bool Ed25519::equal(const Point &p, const Point &q)
512 {
513  limb_t a[NUM_LIMBS_256BIT];
514  limb_t b[NUM_LIMBS_256BIT];
515  bool result = true;
516 
517  Curve25519::mul(a, p.x, q.z);
518  Curve25519::mul(b, q.x, p.z);
519  result &= secure_compare(a, b, sizeof(a));
520 
521  Curve25519::mul(a, p.y, q.z);
522  Curve25519::mul(b, q.y, p.z);
523  result &= secure_compare(a, b, sizeof(a));
524 
525  clean(a);
526  clean(b);
527  return result;
528 }
529 
539 void Ed25519::encodePoint(uint8_t *buf, Point &point)
540 {
541  // Convert the homogeneous coordinates into plain (x, y) coordinates:
542  // zinv = z^(-1) mod p
543  // x = x * zinv mod p
544  // y = y * zinv mod p
545  // We don't need the t coordinate, so use that to store zinv temporarily.
546  Curve25519::recip(point.t, point.z);
547  Curve25519::mul(point.x, point.x, point.t);
548  Curve25519::mul(point.y, point.y, point.t);
549 
550  // Copy the lowest bit of x to the highest bit of y.
551  point.y[NUM_LIMBS_256BIT - 1] |= (point.x[0] << (LIMB_BITS - 1));
552 
553  // Convert y into little-endian in the return buffer.
554  BigNumberUtil::packLE(buf, 32, point.y, NUM_LIMBS_256BIT);
555 }
556 
569 bool Ed25519::decodePoint(Point &point, const uint8_t *buf)
570 {
571  limb_t temp[NUM_LIMBS_256BIT];
572 
573  // Convert the input buffer from little-endian into the limbs of y.
574  BigNumberUtil::unpackLE(point.y, NUM_LIMBS_256BIT, buf, 32);
575 
576  // The high bit of y is the sign bit for x.
577  limb_t sign = point.y[NUM_LIMBS_256BIT - 1] >> (LIMB_BITS - 1);
578  point.y[NUM_LIMBS_256BIT - 1] &= ~(((limb_t)1) << (LIMB_BITS - 1));
579 
580  // Set z to 1.
581  memcpy_P(point.z, numBz, sizeof(point.z));
582 
583  // Compute t = (y * y - 1) * modinv(d * y * y + 1).
584  Curve25519::square(point.t, point.y);
585  Curve25519::sub(point.x, point.t, point.z);
586  Curve25519::mul_P(point.t, point.t, numD);
587  Curve25519::add(point.t, point.t, point.z);
588  Curve25519::recip(temp, point.t);
589  Curve25519::mul(point.t, point.x, temp);
590  clean(temp);
591 
592  // Check for t = 0.
593  limb_t check = point.t[0];
594  for (uint8_t posn = 1; posn < NUM_LIMBS_256BIT; ++posn)
595  check |= point.t[posn];
596  if (!check) {
597  // If the sign bit is set, then decoding has failed.
598  // Otherwise x is zero and we're done.
599  if (sign)
600  return false;
601  memset(point.x, 0, sizeof(point.x));
602  return true;
603  }
604 
605  // Recover x by taking the sqrt of t and flipping the sign if necessary.
606  if (!Curve25519::sqrt(point.x, point.t))
607  return false;
608  if (sign != (point.x[0] & ((limb_t)1))) {
609  // The signs are different so we want the other square root.
610  memset(point.t, 0, sizeof(point.t));
611  Curve25519::sub(point.x, point.t, point.x);
612  }
613 
614  // Finally, t = x * y.
615  Curve25519::mul(point.t, point.x, point.y);
616  return true;
617 }
618 
629 void Ed25519::deriveKeys(SHA512 *hash, limb_t *a, const uint8_t privateKey[32])
630 {
631  // Hash the private key to get the "a" scalar and the message prefix.
632  uint8_t *buf = (uint8_t *)(hash->state.w); // Reuse hash buffer to save memory.
633  hash->reset();
634  hash->update(privateKey, 32);
635  hash->finalize(buf, 0);
636  buf[0] &= 0xF8;
637  buf[31] &= 0x7F;
638  buf[31] |= 0x40;
639 
640  // Unpack the first half of the hash value into "a".
641  BigNumberUtil::unpackLE(a, NUM_LIMBS_256BIT, buf, 32);
642 }
static void reduceQuick_P(limb_t *result, const limb_t *x, const limb_t *y, size_t size)
Reduces x modulo y using subtraction where y is in program memory.
static void unpackLE(limb_t *limbs, size_t count, const uint8_t *bytes, size_t len)
Unpacks the little-endian byte representation of a big number into a limb array.
static limb_t sub(limb_t *result, const limb_t *x, const limb_t *y, size_t size)
Subtracts one big number from another.
static void packLE(uint8_t *bytes, size_t len, const limb_t *limbs, size_t count)
Packs the little-endian byte representation of a big number into a byte array.
static limb_t add(limb_t *result, const limb_t *x, const limb_t *y, size_t size)
Adds two big numbers.
static void mul_P(limb_t *result, const limb_t *x, size_t xcount, const limb_t *y, size_t ycount)
Multiplies two big numbers where one is in program memory.
static void sign(uint8_t signature[64], const uint8_t privateKey[32], const uint8_t publicKey[32], const void *message, size_t len)
Signs a message using a specific Ed25519 private key.
Definition: Ed25519.cpp:127
static void derivePublicKey(uint8_t publicKey[32], const uint8_t privateKey[32])
Derives the public key from a private key.
Definition: Ed25519.cpp:256
static void generatePrivateKey(uint8_t privateKey[32])
Generates a private key for Ed25519 signing operations.
Definition: Ed25519.cpp:243
static bool verify(const uint8_t signature[64], const uint8_t publicKey[32], const void *message, size_t len)
Verifies a signature using a specific Ed25519 public key.
Definition: Ed25519.cpp:189
void rand(uint8_t *data, size_t len)
Generates random bytes into a caller-supplied buffer.
Definition: RNG.cpp:660
SHA-512 hash algorithm.
Definition: SHA512.h:31
void reset()
Resets the hash ready for a new hashing process.
Definition: SHA512.cpp:76
void update(const void *data, size_t len)
Updates the hash with more data.
Definition: SHA512.cpp:89
void finalize(void *hash, size_t len)
Finalizes the hashing process and returns the hash.
Definition: SHA512.cpp:115