ed25519.hpp 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. #pragma once
  2. #include <nall/hash/sha512.hpp>
  3. #if defined(EC_REFERENCE)
  4. #include <nall/elliptic-curve/modulo25519-reference.hpp>
  5. #else
  6. #include <nall/elliptic-curve/modulo25519-optimized.hpp>
  7. #endif
  8. namespace nall::EllipticCurve {
  9. static const uint256_t L = (1_u256 << 252) + 27742317777372353535851937790883648493_u256;
  10. struct Ed25519 {
  11. auto publicKey(uint256_t privateKey) const -> uint256_t {
  12. return compress(scalarMultiply(B, clamp(hash(privateKey)) % L));
  13. }
  14. auto sign(array_view<uint8_t> message, uint256_t privateKey) const -> uint512_t {
  15. uint512_t H = hash(privateKey);
  16. uint256_t a = clamp(H) % L;
  17. uint256_t A = compress(scalarMultiply(B, a));
  18. uint512_t r = hash(upper(H), message) % L;
  19. uint256_t R = compress(scalarMultiply(B, r));
  20. uint512_t k = hash(R, A, message) % L;
  21. uint256_t S = (k * a + r) % L;
  22. return uint512_t(S) << 256 | R;
  23. }
  24. auto verify(array_view<uint8_t> message, uint512_t signature, uint256_t publicKey) const -> bool {
  25. auto R = decompress(lower(signature));
  26. auto A = decompress(publicKey);
  27. if(!R || !A) return false;
  28. uint256_t S = upper(signature) % L;
  29. uint512_t r = hash(lower(signature), publicKey, message) % L;
  30. auto p = scalarMultiply(B, S);
  31. auto q = edwardsAdd(R(), scalarMultiply(A(), r));
  32. if(!onCurve(p) || !onCurve(q)) return false;
  33. if(p.x * q.z - q.x * p.z) return false;
  34. if(p.y * q.z - q.y * p.z) return false;
  35. return true;
  36. }
  37. private:
  38. using field = Modulo25519;
  39. struct point { field x, y, z, t; };
  40. const field D = -field(121665) * reciprocal(field(121666));
  41. const point B = *decompress((field(4) * reciprocal(field(5)))());
  42. const BarrettReduction<256> L = BarrettReduction<256>{EllipticCurve::L};
  43. auto input(Hash::SHA512&) const -> void {}
  44. template<typename... P> auto input(Hash::SHA512& hash, uint256_t value, P&&... p) const -> void {
  45. for(uint byte : range(32)) hash.input(uint8_t(value >> byte * 8));
  46. input(hash, forward<P>(p)...);
  47. }
  48. template<typename... P> auto input(Hash::SHA512& hash, array_view<uint8_t> value, P&&... p) const -> void {
  49. hash.input(value);
  50. input(hash, forward<P>(p)...);
  51. }
  52. template<typename... P> auto hash(P&&... p) const -> uint512_t {
  53. Hash::SHA512 hash;
  54. input(hash, forward<P>(p)...);
  55. uint512_t result;
  56. for(auto byte : reverse(hash.output())) result = result << 8 | byte;
  57. return result;
  58. }
  59. auto clamp(uint256_t p) const -> uint256_t {
  60. p &= (1_u256 << 254) - 8;
  61. p |= (1_u256 << 254);
  62. return p;
  63. }
  64. auto onCurve(point p) const -> bool {
  65. if(!p.z) return false;
  66. if(p.x * p.y - p.z * p.t) return false;
  67. if(square(p.y) - square(p.x) - square(p.z) - square(p.t) * D) return false;
  68. return true;
  69. }
  70. auto decompress(uint256_t c) const -> maybe<point> {
  71. field y = c & ~0_u256 >> 1;
  72. field x = squareRoot((square(y) - 1) * reciprocal(D * square(y) + 1));
  73. if(c >> 255) x = -x;
  74. point p{x, y, 1, x * y};
  75. if(!onCurve(p)) return nothing;
  76. return p;
  77. }
  78. auto compress(point p) const -> uint256_t {
  79. field r = reciprocal(p.z);
  80. field x = p.x * r;
  81. field y = p.y * r;
  82. return (x & 1) << 255 | (y & ~0_u256 >> 1);
  83. }
  84. auto edwardsDouble(point p) const -> point {
  85. field a = square(p.x);
  86. field b = square(p.y);
  87. field c = square(p.z);
  88. field d = -a;
  89. field e = square(p.x + p.y) - a - b;
  90. field g = d + b;
  91. field f = g - (c + c);
  92. field h = d - b;
  93. return {e * f, g * h, f * g, e * h};
  94. }
  95. auto edwardsAdd(point p, point q) const -> point {
  96. field a = (p.y - p.x) * (q.y - q.x);
  97. field b = (p.y + p.x) * (q.y + q.x);
  98. field c = (p.t + p.t) * q.t * D;
  99. field d = (p.z + p.z) * q.z;
  100. field e = b - a;
  101. field f = d - c;
  102. field g = d + c;
  103. field h = b + a;
  104. return {e * f, g * h, f * g, e * h};
  105. }
  106. auto scalarMultiply(point q, uint256_t exponent) const -> point {
  107. point p{0, 1, 1, 0}, c;
  108. for(uint bit : reverse(range(253))) {
  109. p = edwardsDouble(p);
  110. c = edwardsAdd(p, q);
  111. bool condition = exponent >> bit & 1;
  112. cmove(condition, p.x, c.x);
  113. cmove(condition, p.y, c.y);
  114. cmove(condition, p.z, c.z);
  115. cmove(condition, p.t, c.t);
  116. }
  117. return p;
  118. }
  119. };
  120. }