arnaucube
arnaucube/gnark-plonky2-verifierPublic Self-hosted repository
gnark-plonky2-verifier/edwards_curve/eddsa25519.go
edwards_curve/eddsa25519.goRaw
  1package edwards_curve
  2
  3
  4// This file is little-endian
  5
  6import (
  7	"math/big"
  8	"github.com/consensys/gnark/frontend"
  9	"github.com/consensys/gnark/std/math/emulated"
 10	"gnark-ed25519/sha512"
 11)
 12
 13
 14func H(api frontend.API, m []frontend.Variable) []frontend.Variable {
 15	rawResult := sha512.Sha512(api, swapByteEndianness(m))
 16	sResult := swapByteEndianness(rawResult[:])
 17	return sResult
 18}
 19
 20func pow2(n uint) *big.Int {
 21	result := big.NewInt(1)
 22	result.Lsh(result, n)
 23	return result
 24}
 25
 26type EdCurve = Curve[Ed25519, Ed25519Scalars]
 27type EdPoint = AffinePoint[Ed25519]
 28type EdCoordinate = emulated.Element[Ed25519]
 29type EdScalar = emulated.Element[Ed25519Scalars]
 30
 31func bits_to_scalar(c *EdCurve, s []frontend.Variable) EdCoordinate {
 32	if len(s) != 256 { panic("bad length") }
 33	elt := emulated.NewElement[Ed25519](0)
 34	if len(elt.Limbs) != 4 { panic("bad length") }
 35	i := 0
 36	for k := 0; k < 4; k++ {
 37		elt.Limbs[k] = c.api.FromBinary(s[i:i+64]...)
 38		i += 64
 39	}
 40	if i != len(s) { panic("bad length") }
 41	return elt
 42}
 43
 44// func bits_to_clamped_scalar(c *EdCurve, input []frontend.Variable) EdScalar {
 45// 	if len(input) != 256 { panic("bad length") }
 46// 	s := make([]frontend.Variable, len(input))
 47// 	copy(s, input)
 48// 	s[0] = 0
 49// 	s[1] = 0
 50// 	s[2] = 0
 51// 	s[254] = 1
 52// 	return bits_to_scalar[Ed25519Scalars](c, s)
 53// }
 54
 55func bits_to_element(c *EdCurve, input []frontend.Variable) EdPoint {
 56	// L := emulated.NewElement[Ed25519Scalars](rEd25519)
 57	unchecked_point := decodepoint(c, input)
 58
 59	// // TODO: https://github.com/warner/python-pure25519 says this check is not necessary:
 60	// //
 61	// // > This library is conservative, and performs full subgroup-membership checks on decoded
 62	// // > points, which adds considerable overhead. The Curve25519/Ed25519 algorithms were
 63	// // > designed to not require these checks, so a careful application might be able to
 64	// // > improve on this slightly (Ed25519 verify down to 6.2ms, DH-finish to 3.2ms).
 65	// c.AssertIsZero(c.ScalarMul(unchecked_point, L))
 66
 67	return unchecked_point
 68}
 69
 70// func publickey(c *EdCurve, seed []frontend.Variable) EdPoint {
 71// 	if len(seed) != 32 { panic("bad length") }
 72// 	a := bits_to_clamped_scalar(c, H(c.api, seed)[:256])
 73// 	return c.ScalarMul(c.g, a)
 74// }
 75
 76func CheckValid(c *EdCurve, s, m, pk []frontend.Variable) {
 77	if len(s) != 512 { panic("bad signature length") }
 78	if len(pk) != 256 { panic("bad public key length") }
 79	if len(m) % 8 != 0 { panic("bad message length") }
 80	R := bits_to_element(c, s[:256])
 81	A := bits_to_element(c, pk)
 82	h := H(c.api, concat(s[:256], pk, m))
 83	v1 := c.ScalarMulBinary(c.g, s[256:])
 84	v2 := c.Add(R, c.ScalarMulBinary(A, h))
 85	c.AssertIsEqual(v1, v2)
 86}
 87
 88func reverse[T interface{}](arr []T) []T {
 89	result := make([]T, len(arr))
 90	for i, v := range arr {
 91		result[len(result)-i-1] = v
 92	}
 93	return result
 94}
 95
 96func concat(args ...[]frontend.Variable) []frontend.Variable {
 97	result := []frontend.Variable{}
 98	for _, v := range args {
 99		result = append(result, v...)
100	}
101	return result
102}
103
104func decodepoint(c *EdCurve, unclamped []frontend.Variable) EdPoint {
105	if len(unclamped) != 256 { panic("bad length") }
106
107	s := make([]frontend.Variable, len(unclamped))
108	copy(s, unclamped)
109	s[255] = 0
110	y := bits_to_scalar(c, s)
111//     unclamped = int(binascii.hexlify(s[:32][::-1]), 16)
112//     clamp = (1 << 255) - 1
113//     y = unclamped & clamp # clear MSB
114
115	x := xrecover(c, y)
116//     x = xrecover(y)
117
118	xbits := c.baseApi.ToBinary(x)
119	if len(xbits) != 256 { panic("bad length") }
120	mismatch := c.api.Xor(xbits[0], unclamped[255])
121	x = c.baseApi.Select(mismatch, c.baseApi.Neg(x), x).(EdCoordinate)
122//     if bool(x & 1) != bool(unclamped & (1<<255)): x = Q-x
123
124	P := AffinePoint[Ed25519]{
125		X: x,
126		Y: y,
127	}
128//     P = [x,y]
129
130	c.AssertIsOnCurve(P)
131//     if not isoncurve(P): raise NotOnCurve("decoding point that is not on curve")
132
133	return P
134}
135
136func toValue(s EdCoordinate) *big.Int {
137	result := big.NewInt(0)
138	placeValue := big.NewInt(1)
139	for _, v := range s.Limbs {
140		q := new(big.Int).Mul(placeValue, v.(*big.Int))
141		result.Add(result, q)
142		placeValue.Lsh(placeValue, Ed25519{}.BitsPerLimb())
143	}
144	return result
145}
146
147func _const(x int64) EdCoordinate {
148	return emulated.NewElement[Ed25519](big.NewInt(x))
149}
150
151// Q = 2**255 - 19
152// L = 2**252 + 27742317777372353535851937790883648493
153// def inv(x):
154//     return pow(x, Q-2, Q)
155// d = -121665 * inv(121666)
156// I = pow(2,(Q-1)//4,Q)
157
158func xrecover(c *EdCurve, y EdCoordinate) EdCoordinate {	
159	Q := Ed25519{}.Modulus()
160	I := emulated.NewElement[Ed25519](newBigInt("2b8324804fc1df0b2b4d00993dfbd7a72f431806ad2fe478c4ee1b274a0ea0b0"))
161
162	yy := c.baseApi.Mul(y, y)
163	xx := c.baseApi.Div(
164		c.baseApi.Sub(yy, _const(1)),
165		c.baseApi.Add(c.baseApi.Mul(c.d, yy), _const(1)),
166	).(EdCoordinate)
167	// xx = (y*y-1) * inv(d*y*y+1)
168
169	power := new(big.Int).Add(Q, big.NewInt(3))
170	power.Rsh(power, 3)
171	x := pow(c, xx, power)
172	// x = pow(xx,(Q+3)//8,Q)
173
174	matches := c.baseApi.IsZero(c.baseApi.Sub(
175		c.baseApi.Mul(x, x),
176		xx,
177	))
178	x = c.baseApi.Select(matches, x, c.baseApi.Mul(x, emulated.NewElement[Ed25519](I))).(EdCoordinate)
179	// if (x*x - xx) % Q != 0: x = (x*I) % Q
180
181	odd := c.baseApi.ToBinary(x)[0]
182	x = c.baseApi.Select(odd, c.baseApi.Neg(x), x).(EdCoordinate)
183	// if x % 2 != 0: x = Q-x
184
185	return x
186}
187
188func pow(c *EdCurve, base EdCoordinate, exponent *big.Int) EdCoordinate {
189	mul := base
190	result := _const(1)
191	for exponent.Sign() > 0 {
192		if exponent.Bit(0) != 0 {
193			result = c.baseApi.Mul(result, mul).(EdCoordinate)
194		}
195		mul = c.baseApi.Mul(mul, mul).(EdCoordinate)
196		exponent.Rsh(exponent, 1)
197	}
198	return result
199}
200
201func swapByteEndianness(in []frontend.Variable) []frontend.Variable {
202	if len(in) % 8 != 0 { panic("must be a multiple of 8 bits") }
203	result := make([]frontend.Variable, len(in))
204	for i := 0; i < len(in); i += 8 {
205		for j := 0; j < 8; j++ {
206			result[i+j] = in[i+7-j]
207		}
208	}
209	return result
210}
211
212// def checkvalid(s, m, pk):
213//     if len(s) != 64: raise Exception("signature length is wrong")
214//     if len(pk) != 32: raise Exception("public-key length is wrong")
215//     R = bytes_to_element(s[:32])
216//     A = bytes_to_element(pk)
217//     S = bytes_to_scalar(s[32:])
218//     h = Hint(s[:32] + pk + m)
219//     v1 = Base.scalarmult(S)
220//     v2 = R.add(A.scalarmult(h))
221//     return v1==v2
222
223// def publickey(seed):
224//     # turn first half of SHA512(seed) into scalar, then into point
225//     assert len(seed) == 32
226//     a = bytes_to_clamped_scalar(H(seed)[:32])
227//     A = Base.scalarmult(a)
228//     return A.to_bytes()
229
230// def bytes_to_scalar(s):
231//     assert len(s) == 32, len(s)
232//     return int(binascii.hexlify(s[::-1]), 16)
233
234
235// from pure25519.basic import (bytes_to_clamped_scalar,
236//                              bytes_to_scalar, scalar_to_bytes,
237//                              bytes_to_element, Base)
238// import hashlib, binascii
239
240// def H(m):
241//     return hashlib.sha512(m).digest()
242
243// def Hint(m):
244//     h = H(m)
245//     return int(binascii.hexlify(h[::-1]), 16)
246
247// def signature(m,sk,pk):
248//     assert len(sk) == 32 # seed
249//     assert len(pk) == 32
250//     h = H(sk[:32])
251//     a_bytes, inter = h[:32], h[32:]
252//     a = bytes_to_clamped_scalar(a_bytes)
253//     r = Hint(inter + m)
254//     R = Base.scalarmult(r)
255//     R_bytes = R.to_bytes()
256//     S = r + Hint(R_bytes + pk + m) * a
257//     return R_bytes + scalar_to_bytes(S)
258
259// def checkvalid(s, m, pk):
260//     if len(s) != 64: raise Exception("signature length is wrong")
261//     if len(pk) != 32: raise Exception("public-key length is wrong")
262//     R = bytes_to_element(s[:32])
263//     A = bytes_to_element(pk)
264//     S = bytes_to_scalar(s[32:])
265//     h = Hint(s[:32] + pk + m)
266//     v1 = Base.scalarmult(S)
267//     v2 = R.add(A.scalarmult(h))
268//     return v1==v2
269
270// # wrappers
271
272// import os
273
274// def create_signing_key():
275//     seed = os.urandom(32)
276//     return seed
277// def create_verifying_key(signing_key):
278//     return publickey(signing_key)
279
280// def sign(skbytes, msg):
281//     """Return just the signature, given the message and just the secret
282//     key."""
283//     if len(skbytes) != 32:
284//         raise ValueError("Bad signing key length %d" % len(skbytes))
285//     vkbytes = create_verifying_key(skbytes)
286//     sig = signature(msg, skbytes, vkbytes)
287//     return sig
288
289// def verify(vkbytes, sig, msg):
290//     if len(vkbytes) != 32:
291//         raise ValueError("Bad verifying key length %d" % len(vkbytes))
292//     if len(sig) != 64:
293//         raise ValueError("Bad signature length %d" % len(sig))
294//     rc = checkvalid(sig, msg, vkbytes)
295//     if not rc:
296//         raise ValueError("rc != 0", rc)
297//     return True