#pragma once #include #if defined(EC_REFERENCE) #include #else #include #endif namespace nall { namespace EllipticCurve { struct Ed25519 { Ed25519() { field y = field(4) * field(5).reciprocal(); field x = recoverX(y); point B{x, y, 1, x * y}; for(uint n : range(253)) { Bscalar[n] = B; B = edwardsDouble(B); } } auto publicKey(uint256_t privateKey) const -> uint256_t { auto H = uint512_t{Hash::SHA512(to_vector(privateKey)).output()}; auto a = clamp(H); auto A = compress(scalarMultiplyB(modL(a))); return A; } auto sign(const vector& message, uint256_t privateKey) const -> uint512_t { auto H = uint512_t{Hash::SHA512(to_vector(privateKey)).output()}; auto a = clamp(H); auto A = compress(scalarMultiplyB(modL(a))); Hash::SHA512 hash1; hash1.input(to_vector(upper(H))); hash1.input(message); auto r = uint512_t{hash1.output()}; auto R = compress(scalarMultiplyB(modL(r))); Hash::SHA512 hash2; hash2.input(to_vector(R)); hash2.input(to_vector(A)); hash2.input(message); uint512_t k = modL(uint512_t{hash2.output()}); uint256_t S = modL(k * a + r); return uint512_t(S) << 256 | R; } auto verify(const vector& message, uint512_t signature, uint256_t publicKey) const -> bool { auto R = decompress(lower(signature)); auto A = decompress(publicKey); if(!R || !A) return false; uint256_t S = upper(signature); Hash::SHA512 hash; hash.input(to_vector(lower(signature))); hash.input(to_vector(publicKey)); hash.input(message); auto r = uint512_t{hash.output()}; auto p = scalarMultiplyB(modL(S)); auto q = edwardsAdd(R(), scalarMultiply(modL(r), A())); if(!onCurve(p) || !onCurve(q)) return false; if(p.x * q.z - q.x * p.z) return false; if(p.y * q.z - q.y * p.z) return false; return true; } private: using field = Modulo25519; struct point { field x, y, z, t; }; point Bscalar[253]; const field D = -field(121665) * field(121666).reciprocal(); inline auto clamp(uint256_t p) const -> uint256_t { p &= ((0_u256 - 1) >> 2) - 7; p |= 1_u256 << 254; return p; } inline auto recoverX(field y) const -> field { field y2 = y.square(); field x = ((y2 - 1) * (D * y2 + 1).reciprocal()).squareRoot(); return x() & 1 ? -x : x; } inline auto onCurve(point p) const -> bool { if(!p.z) return false; if(p.x * p.y != p.z * p.t) return false; if(p.y.square() - p.x.square() - p.z.square() - p.t.square() * D) return false; return true; } inline auto decompress(uint256_t c) const -> maybe { field y = c & (1_u256 << 255) - 1; field x = recoverX(y); if(c >> 255) x = -x; point p{x, y, 1, x * y}; if(!onCurve(p)) return nothing; return p; } inline auto compress(point p) const -> uint256_t { field r = p.z.reciprocal(); field x = p.x * r; field y = p.y * r; return (x() & 1) << 255 | (y() & ((0_u256 - 1) >> 1)); } inline auto edwardsDouble(point p) const -> point { field a = p.x.square(); field b = p.y.square(); field c = p.z.square(); field d = -a; field e = (p.x + p.y).square() - a - b; field g = d + b; field f = g - (c + c); field h = d - b; return {e * f, g * h, f * g, e * h}; } inline auto edwardsAdd(point p, point q) const -> point { field a = (p.y - p.x) * (q.y - q.x); field b = (p.y + p.x) * (q.y + q.x); field c = (p.t + p.t) * q.t * D; field d = (p.z + p.z) * q.z; field e = b - a; field f = d - c; field g = d + c; field h = b + a; return {e * f, g * h, f * g, e * h}; } inline auto scalarMultiply(uint512_t e, point q) const -> point { point p{0, 1, 1, 0}, c; for(uint n : rrange(253)) { p = edwardsDouble(p); c = edwardsAdd(p, q); bool bit = e >> n & 1; cmove(bit, p.x, c.x); cmove(bit, p.y, c.y); cmove(bit, p.z, c.z); cmove(bit, p.t, c.t); } return p; } inline auto scalarMultiplyB(uint512_t e) const -> point { point p{0, 1, 1, 0}, c; for(uint n : rrange(253)) { bool bit = e >> n & 1; c = edwardsAdd(p, Bscalar[n]); cmove(bit, p.x, c.x); cmove(bit, p.y, c.y); cmove(bit, p.z, c.z); cmove(bit, p.t, c.t); } return p; } }; }}