Module: Causalontology::Ed25519
- Defined in:
- lib/causalontology/ed25519.rb
Constant Summary collapse
- P =
2**255 - 19
- Q =
2**252 + 27742317777372353535851937790883648493
- CURVE_D =
The curve constant d = -121665 / 121666 (mod p).
-121665 * modp_inv(121666) % P
- SQRT_M1 =
A square root of -1 (mod p), used in point decompression.
2.pow((P - 1) / 4, P)
- BASE_Y =
The base point G.
4 * modp_inv(5) % P
- BASE_X =
recover_x(BASE_Y, 0)
- BASE_POINT =
[BASE_X, BASE_Y, 1, BASE_X * BASE_Y % P].freeze
Class Method Summary collapse
-
.int_to_le32(n) ⇒ Object
A 32-byte little-endian string for a non-negative integer below 2**256.
-
.le_to_int(s) ⇒ Object
The non-negative integer encoded by a little-endian byte string.
- .modp_inv(x) ⇒ Object
-
.point_add(pt, qt) ⇒ Object
Points are [x, y, z, t] in extended homogeneous coordinates.
- .point_compress(pt) ⇒ Object
- .point_decompress(s) ⇒ Object
- .point_equal(pt, qt) ⇒ Object
- .point_mul(s, pt) ⇒ Object
- .recover_x(y, sign) ⇒ Object
- .secret_expand(secret) ⇒ Object
-
.secret_to_public(secret) ⇒ Object
The 32-byte public key for a 32-byte secret key.
- .sha512(s) ⇒ Object
- .sha512_modq(s) ⇒ Object
-
.sign(secret, msg) ⇒ Object
The 64-byte Ed25519 signature of msg under the 32-byte secret key.
-
.verify(public_key, msg, signature) ⇒ Object
True iff signature is a valid Ed25519 signature of msg under public.
Class Method Details
.int_to_le32(n) ⇒ Object
A 32-byte little-endian string for a non-negative integer below 2**256.
33 34 35 36 |
# File 'lib/causalontology/ed25519.rb', line 33 def int_to_le32(n) hex = n.to_s(16).rjust(64, "0") [hex].pack("H*").reverse end |
.le_to_int(s) ⇒ Object
The non-negative integer encoded by a little-endian byte string.
39 40 41 |
# File 'lib/causalontology/ed25519.rb', line 39 def le_to_int(s) s.dup.force_encoding(Encoding::BINARY).reverse.unpack1("H*").to_i(16) end |
.modp_inv(x) ⇒ Object
28 29 30 |
# File 'lib/causalontology/ed25519.rb', line 28 def modp_inv(x) x.pow(P - 2, P) end |
.point_add(pt, qt) ⇒ Object
Points are [x, y, z, t] in extended homogeneous coordinates.
49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/causalontology/ed25519.rb', line 49 def point_add(pt, qt) a = (pt[1] - pt[0]) * (qt[1] - qt[0]) % P b = (pt[1] + pt[0]) * (qt[1] + qt[0]) % P c = 2 * pt[3] * qt[3] * CURVE_D % P d = 2 * pt[2] * qt[2] % P e = b - a f = d - c g = d + c h = b + a [e * f % P, g * h % P, f * g % P, e * h % P] end |
.point_compress(pt) ⇒ Object
93 94 95 96 97 98 |
# File 'lib/causalontology/ed25519.rb', line 93 def point_compress(pt) zinv = modp_inv(pt[2]) x = pt[0] * zinv % P y = pt[1] * zinv % P int_to_le32(y | ((x & 1) << 255)) end |
.point_decompress(s) ⇒ Object
100 101 102 103 104 105 106 107 108 |
# File 'lib/causalontology/ed25519.rb', line 100 def point_decompress(s) return nil if s.bytesize != 32 y = le_to_int(s) sign = y >> 255 y &= (1 << 255) - 1 x = recover_x(y, sign) return nil if x.nil? [x, y, 1, x * y % P] end |
.point_equal(pt, qt) ⇒ Object
71 72 73 74 75 |
# File 'lib/causalontology/ed25519.rb', line 71 def point_equal(pt, qt) return false if (pt[0] * qt[2] - qt[0] * pt[2]) % P != 0 return false if (pt[1] * qt[2] - qt[1] * pt[2]) % P != 0 true end |
.point_mul(s, pt) ⇒ Object
61 62 63 64 65 66 67 68 69 |
# File 'lib/causalontology/ed25519.rb', line 61 def point_mul(s, pt) q = [0, 1, 1, 0] # the neutral element while s > 0 q = point_add(q, pt) if s & 1 == 1 pt = point_add(pt, pt) s >>= 1 end q end |
.recover_x(y, sign) ⇒ Object
77 78 79 80 81 82 83 84 85 86 |
# File 'lib/causalontology/ed25519.rb', line 77 def recover_x(y, sign) return nil if y >= P x2 = (y * y - 1) * modp_inv(CURVE_D * y * y + 1) % P return (sign == 1 ? nil : 0) if x2 == 0 x = x2.pow((P + 3) / 8, P) x = x * SQRT_M1 % P if (x * x - x2) % P != 0 return nil if (x * x - x2) % P != 0 x = P - x if (x & 1) != sign x end |
.secret_expand(secret) ⇒ Object
110 111 112 113 114 115 116 117 |
# File 'lib/causalontology/ed25519.rb', line 110 def (secret) raise ArgumentError, "secret key must be 32 bytes" if secret.bytesize != 32 h = sha512(secret) a = le_to_int(h.byteslice(0, 32)) a &= (1 << 254) - 8 a |= (1 << 254) [a, h.byteslice(32, 32)] end |
.secret_to_public(secret) ⇒ Object
The 32-byte public key for a 32-byte secret key.
124 125 126 127 |
# File 'lib/causalontology/ed25519.rb', line 124 def secret_to_public(secret) a, _prefix = (secret) point_compress(point_mul(a, BASE_POINT)) end |
.sha512(s) ⇒ Object
24 25 26 |
# File 'lib/causalontology/ed25519.rb', line 24 def sha512(s) Digest::SHA512.digest(s) end |
.sha512_modq(s) ⇒ Object
119 120 121 |
# File 'lib/causalontology/ed25519.rb', line 119 def sha512_modq(s) le_to_int(sha512(s)) % Q end |
.sign(secret, msg) ⇒ Object
The 64-byte Ed25519 signature of msg under the 32-byte secret key.
130 131 132 133 134 135 136 137 138 139 |
# File 'lib/causalontology/ed25519.rb', line 130 def sign(secret, msg) msg = msg.dup.force_encoding(Encoding::BINARY) a, prefix = (secret) public_key = point_compress(point_mul(a, BASE_POINT)) r = sha512_modq(prefix + msg) rs = point_compress(point_mul(r, BASE_POINT)) h = sha512_modq(rs + public_key + msg) s = (r + h * a) % Q rs + int_to_le32(s) end |
.verify(public_key, msg, signature) ⇒ Object
True iff signature is a valid Ed25519 signature of msg under public.
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
# File 'lib/causalontology/ed25519.rb', line 142 def verify(public_key, msg, signature) msg = msg.dup.force_encoding(Encoding::BINARY) return false if public_key.bytesize != 32 || signature.bytesize != 64 a_point = point_decompress(public_key) return false if a_point.nil? rs = signature.byteslice(0, 32) r_point = point_decompress(rs) return false if r_point.nil? s = le_to_int(signature.byteslice(32, 32)) return false if s >= Q h = sha512_modq(rs + public_key + msg) sb = point_mul(s, BASE_POINT) ha = point_mul(h, a_point) point_equal(sb, point_add(r_point, ha)) end |