From 657339765593920873e30a890c95488ee86ee27f Mon Sep 17 00:00:00 2001 From: Jacob Jackson Date: Wed, 5 Oct 2022 18:09:59 +0000 Subject: [PATCH 1/5] finish eddsa draft --- edwards_curve/eddsa25519.go | 257 ++++++++++++++++++++++++++++++++++++ edwards_curve/edparams.go | 6 +- edwards_curve/edpoint.go | 10 +- sha512/sha512.go | 13 +- sha512/sha_test.go | 2 +- 5 files changed, 280 insertions(+), 8 deletions(-) create mode 100644 edwards_curve/eddsa25519.go diff --git a/edwards_curve/eddsa25519.go b/edwards_curve/eddsa25519.go new file mode 100644 index 0000000..0a829ac --- /dev/null +++ b/edwards_curve/eddsa25519.go @@ -0,0 +1,257 @@ +package edwards_curve + + +// This file is little-endian + +import ( + "math/big" + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/std/math/emulated" + "gnark-ed25519/sha512" +) + + +func H(api frontend.API, m []frontend.Variable) []frontend.Variable { + result := sha512.Sha512Bytes(api, m) + return result[:] +} + +func pow2(n uint) *big.Int { + result := big.NewInt(1) + result.Lsh(result, n) + return result +} + +type EdCurve = Curve[Ed25519, Ed25519Scalars] +type EdPoint = AffinePoint[Ed25519] +type EdCoordinate = emulated.Element[Ed25519] +type EdScalar = emulated.Element[Ed25519Scalars] + +func bits_to_scalar(c *EdCurve, s []frontend.Variable) EdCoordinate { + if len(s) != 256 { panic("bad length") } + elt := emulated.NewElement[Ed25519](0) + if len(elt.Limbs) != 4 { panic("bad length") } + i := 0 + elt.Limbs[0] = c.api.FromBinary(s[i:i+64]); i += 64 + elt.Limbs[1] = c.api.FromBinary(s[i:i+64]); i += 64 + elt.Limbs[2] = c.api.FromBinary(s[i:i+64]); i += 64 + elt.Limbs[3] = c.api.FromBinary(s[i:i+64]); i += 64 + if i != len(s) { panic("bad length") } + return elt +} + +// func bits_to_clamped_scalar(c *EdCurve, input []frontend.Variable) EdScalar { +// if len(input) != 256 { panic("bad length") } +// s := make([]frontend.Variable, len(input)) +// copy(s, input) +// s[0] = 0 +// s[1] = 0 +// s[2] = 0 +// s[254] = 1 +// return bits_to_scalar[Ed25519Scalars](c, s) +// } + +func bits_to_element(c *EdCurve, input []frontend.Variable) EdPoint { + L := emulated.NewElement[Ed25519Scalars](rEd25519) + unchecked_point := decodepoint(c, input) + c.AssertIsZero(c.ScalarMul(unchecked_point, L)) + return unchecked_point +} + +// func publickey(c *EdCurve, seed []frontend.Variable) EdPoint { +// if len(seed) != 32 { panic("bad length") } +// a := bits_to_clamped_scalar(c, H(c.api, seed)[:256]) +// return c.ScalarMul(c.g, a) +// } + +func checkvalid(c *EdCurve, s, m, pk []frontend.Variable) { + if len(s) != 512 { panic("bad signature length") } + if len(pk) != 256 { panic("bad public key length") } + R := bits_to_element(c, s[:256]) + A := bits_to_element(c, pk) + h := H(c.api, concat(s[:256], pk, m)) + v1 := c.ScalarMulBinary(c.g, s[256:]) + v2 := c.Add(R, c.ScalarMulBinary(A, h)) + c.AssertIsEqual(v1, v2) +} + +func concat(args ...[]frontend.Variable) []frontend.Variable { + result := []frontend.Variable{} + for _, v := range args { + result = append(result, v...) + } + return result +} + +func decodepoint(c *EdCurve, input []frontend.Variable) EdPoint { + if len(input) != 256 { panic("bad length") } + + s := make([]frontend.Variable, len(input)) + copy(s, input) + s[255] = 0 + y := bits_to_scalar(c, s) +// unclamped = int(binascii.hexlify(s[:32][::-1]), 16) +// clamp = (1 << 255) - 1 +// y = unclamped & clamp # clear MSB + + x := xrecover(c, y) +// x = xrecover(y) + + xbits := c.baseApi.ToBinary(x) + if len(xbits) != 256 { panic("bad length") } + mismatch := c.api.Xor(xbits[0], xbits[255]) + x = c.baseApi.Select(mismatch, c.baseApi.Neg(x), x).(EdCoordinate) +// if bool(x & 1) != bool(unclamped & (1<<255)): x = Q-x + + P := AffinePoint[Ed25519]{ + X: x, + Y: y, + } +// P = [x,y] + + c.AssertIsOnCurve(P) +// if not isoncurve(P): raise NotOnCurve("decoding point that is not on curve") + + return P +} + +func _const(x int64) EdCoordinate { + return emulated.NewElement[Ed25519](big.NewInt(x)) +} + +// Q = 2**255 - 19 +// L = 2**252 + 27742317777372353535851937790883648493 +// def inv(x): +// return pow(x, Q-2, Q) +// d = -121665 * inv(121666) +// I = pow(2,(Q-1)//4,Q) + +func xrecover(c *EdCurve, y EdCoordinate) EdCoordinate { + Q := Ed25519{}.Modulus() + I := emulated.NewElement[Ed25519](newBigInt("2b8324804fc1df0b2b4d00993dfbd7a72f431806ad2fe478c4ee1b274a0ea0b0")) + + yy := c.baseApi.Mul(y, y) + xx := c.baseApi.Div( + c.baseApi.Sub(yy, _const(1)), + c.baseApi.Add(c.baseApi.Mul(c.d, yy), _const(1)), + ).(EdCoordinate) + // xx = (y*y-1) * inv(d*y*y+1) + + power := new(big.Int).Add(Q, big.NewInt(3)) + power.Rsh(power, 3) + x := pow(c, xx, power) + // x = pow(xx,(Q+3)//8,Q) + + matches := c.baseApi.IsZero(c.baseApi.Sub( + c.baseApi.Mul(x, x), + xx, + )) + x = c.baseApi.Select(matches, x, c.baseApi.Mul(x, emulated.NewElement[Ed25519](I))).(EdCoordinate) + // if (x*x - xx) % Q != 0: x = (x*I) % Q + + even := c.baseApi.ToBinary(x)[0] + x = c.baseApi.Select(even, x, c.baseApi.Neg(x)).(EdCoordinate) + // if x % 2 != 0: x = Q-x + + return x +} + +func pow(c *EdCurve, base EdCoordinate, exponent *big.Int) EdCoordinate { + mul := base + result := _const(1) + for exponent.Sign() > 0 { + if exponent.Bit(0) != 0 { + result = c.baseApi.Mul(result, mul).(EdCoordinate) + } + mul = c.baseApi.Mul(mul, mul).(EdCoordinate) + exponent.Rsh(exponent, 1) + } + return result +} + +// def checkvalid(s, m, pk): +// if len(s) != 64: raise Exception("signature length is wrong") +// if len(pk) != 32: raise Exception("public-key length is wrong") +// R = bytes_to_element(s[:32]) +// A = bytes_to_element(pk) +// S = bytes_to_scalar(s[32:]) +// h = Hint(s[:32] + pk + m) +// v1 = Base.scalarmult(S) +// v2 = R.add(A.scalarmult(h)) +// return v1==v2 + +// def publickey(seed): +// # turn first half of SHA512(seed) into scalar, then into point +// assert len(seed) == 32 +// a = bytes_to_clamped_scalar(H(seed)[:32]) +// A = Base.scalarmult(a) +// return A.to_bytes() + +// def bytes_to_scalar(s): +// assert len(s) == 32, len(s) +// return int(binascii.hexlify(s[::-1]), 16) + + +// from pure25519.basic import (bytes_to_clamped_scalar, +// bytes_to_scalar, scalar_to_bytes, +// bytes_to_element, Base) +// import hashlib, binascii + +// def H(m): +// return hashlib.sha512(m).digest() + +// def Hint(m): +// h = H(m) +// return int(binascii.hexlify(h[::-1]), 16) + +// def signature(m,sk,pk): +// assert len(sk) == 32 # seed +// assert len(pk) == 32 +// h = H(sk[:32]) +// a_bytes, inter = h[:32], h[32:] +// a = bytes_to_clamped_scalar(a_bytes) +// r = Hint(inter + m) +// R = Base.scalarmult(r) +// R_bytes = R.to_bytes() +// S = r + Hint(R_bytes + pk + m) * a +// return R_bytes + scalar_to_bytes(S) + +// def checkvalid(s, m, pk): +// if len(s) != 64: raise Exception("signature length is wrong") +// if len(pk) != 32: raise Exception("public-key length is wrong") +// R = bytes_to_element(s[:32]) +// A = bytes_to_element(pk) +// S = bytes_to_scalar(s[32:]) +// h = Hint(s[:32] + pk + m) +// v1 = Base.scalarmult(S) +// v2 = R.add(A.scalarmult(h)) +// return v1==v2 + +// # wrappers + +// import os + +// def create_signing_key(): +// seed = os.urandom(32) +// return seed +// def create_verifying_key(signing_key): +// return publickey(signing_key) + +// def sign(skbytes, msg): +// """Return just the signature, given the message and just the secret +// key.""" +// if len(skbytes) != 32: +// raise ValueError("Bad signing key length %d" % len(skbytes)) +// vkbytes = create_verifying_key(skbytes) +// sig = signature(msg, skbytes, vkbytes) +// return sig + +// def verify(vkbytes, sig, msg): +// if len(vkbytes) != 32: +// raise ValueError("Bad verifying key length %d" % len(vkbytes)) +// if len(sig) != 64: +// raise ValueError("Bad signature length %d" % len(sig)) +// rc = checkvalid(sig, msg, vkbytes) +// if not rc: +// raise ValueError("rc != 0", rc) +// return True diff --git a/edwards_curve/edparams.go b/edwards_curve/edparams.go index 107dba3..47bbca7 100644 --- a/edwards_curve/edparams.go +++ b/edwards_curve/edparams.go @@ -11,11 +11,7 @@ var ( func init() { // https://neuromancer.sk/std/other/Ed25519 qEd25519 = newBigInt("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed") - n := newBigInt("1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed") - // TODO: is this ok? - // h := big.NewInt(8) - // rEd25519 = new(big.Int).Mul(n, h) - rEd25519 = n + rEd25519 = newBigInt("1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed") } type Ed25519 struct{} diff --git a/edwards_curve/edpoint.go b/edwards_curve/edpoint.go index 17a7c1f..b99733a 100644 --- a/edwards_curve/edpoint.go +++ b/edwards_curve/edpoint.go @@ -112,6 +112,11 @@ func (c *Curve[T, S]) AssertIsOnCurve(p AffinePoint[T]) { c.baseApi.AssertIsEqual(lhs, rhs) } +func (c *Curve[T, S]) AssertIsZero(p AffinePoint[T]) { + c.baseApi.AssertIsEqual(p.X, 0) + c.baseApi.AssertIsEqual(p.Y, 1) +} + func (c *Curve[T, S]) Add(q, r AffinePoint[T]) AffinePoint[T] { // u = (x1 + y1) * (x2 + y2) u1 := c.baseApi.Mul(q.X, c.a) @@ -176,10 +181,13 @@ func (c *Curve[T, S]) Select(b frontend.Variable, p, q AffinePoint[T]) AffinePoi } func (c *Curve[T, S]) ScalarMul(p AffinePoint[T], s emulated.Element[S]) AffinePoint[T] { + return c.ScalarMulBinary(p, c.scalarApi.ToBinary(s)) +} + +func (c *Curve[T, S]) ScalarMulBinary(p AffinePoint[T], sBits []frontend.Variable) AffinePoint[T] { res := p acc := c.Double(p) - sBits := c.scalarApi.ToBinary(s) for i := 1; i < len(sBits); i++ { tmp := c.Add(res, acc) res = c.Select(sBits[i], tmp, res) diff --git a/sha512/sha512.go b/sha512/sha512.go index fc1358e..b9be0fd 100644 --- a/sha512/sha512.go +++ b/sha512/sha512.go @@ -15,7 +15,18 @@ func _right_rotate(n [64]frontend.Variable, bits int) [64]frontend.Variable { return result } -func Sha512(api frontend.API, in [] frontend.Variable) ([512] frontend.Variable) { +func Sha512Bytes(api frontend.API, in []frontend.Variable) ([512]frontend.Variable) { + bits := []frontend.Variable{} + for _, v := range in { + b := api.ToBinary(v, 8) + for i := 0; i < 8; i++ { + bits = append(bits, b[7-i]) + } + } + return Sha512Bits(api, bits) +} + +func Sha512Bits(api frontend.API, in []frontend.Variable) ([512]frontend.Variable) { _not := func(x [64]frontend.Variable) [64]frontend.Variable { return not(api, x) } diff --git a/sha512/sha_test.go b/sha512/sha_test.go index c1ef941..66c4fe4 100644 --- a/sha512/sha_test.go +++ b/sha512/sha_test.go @@ -15,7 +15,7 @@ type Sha512Circuit struct { } func (circuit *Sha512Circuit) Define(api frontend.API) error { - res := Sha512(api, circuit.in) + res := Sha512Bits(api, circuit.in) if len(res) != 512 { panic("bad length") } for i := 0; i < 512; i++ { api.AssertIsEqual(res[i], circuit.out[i]) From 06582cb3ee13a11109659615fd968175a975841d Mon Sep 17 00:00:00 2001 From: Jacob Jackson Date: Wed, 5 Oct 2022 22:04:22 +0000 Subject: [PATCH 2/5] finish eddsa --- edwards_curve/eddsa25519.go | 80 ++++++++++++++++++++++++++------ edwards_curve/eddsa25519_test.go | 65 ++++++++++++++++++++++++++ edwards_curve/edpoint.go | 11 +++-- sha512/sha512.go | 13 +----- sha512/sha_test.go | 18 ++++--- 5 files changed, 149 insertions(+), 38 deletions(-) create mode 100644 edwards_curve/eddsa25519_test.go diff --git a/edwards_curve/eddsa25519.go b/edwards_curve/eddsa25519.go index 0a829ac..daa1ab6 100644 --- a/edwards_curve/eddsa25519.go +++ b/edwards_curve/eddsa25519.go @@ -4,6 +4,7 @@ package edwards_curve // This file is little-endian import ( + "fmt" "math/big" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/std/math/emulated" @@ -12,8 +13,11 @@ import ( func H(api frontend.API, m []frontend.Variable) []frontend.Variable { - result := sha512.Sha512Bytes(api, m) - return result[:] + fmt.Println("sha input", m) + rawResult := sha512.Sha512(api, swapByteEndianness(m)) + sResult := swapByteEndianness(rawResult[:]) + fmt.Println("sha output", sResult) + return sResult } func pow2(n uint) *big.Int { @@ -32,10 +36,10 @@ func bits_to_scalar(c *EdCurve, s []frontend.Variable) EdCoordinate { elt := emulated.NewElement[Ed25519](0) if len(elt.Limbs) != 4 { panic("bad length") } i := 0 - elt.Limbs[0] = c.api.FromBinary(s[i:i+64]); i += 64 - elt.Limbs[1] = c.api.FromBinary(s[i:i+64]); i += 64 - elt.Limbs[2] = c.api.FromBinary(s[i:i+64]); i += 64 - elt.Limbs[3] = c.api.FromBinary(s[i:i+64]); i += 64 + elt.Limbs[0] = c.api.FromBinary(s[i:i+64]...); i += 64 + elt.Limbs[1] = c.api.FromBinary(s[i:i+64]...); i += 64 + elt.Limbs[2] = c.api.FromBinary(s[i:i+64]...); i += 64 + elt.Limbs[3] = c.api.FromBinary(s[i:i+64]...); i += 64 if i != len(s) { panic("bad length") } return elt } @@ -54,7 +58,15 @@ func bits_to_scalar(c *EdCurve, s []frontend.Variable) EdCoordinate { func bits_to_element(c *EdCurve, input []frontend.Variable) EdPoint { L := emulated.NewElement[Ed25519Scalars](rEd25519) unchecked_point := decodepoint(c, input) + + // TODO: https://github.com/warner/python-pure25519 says this check is not necessary: + // + // > This library is conservative, and performs full subgroup-membership checks on decoded + // > points, which adds considerable overhead. The Curve25519/Ed25519 algorithms were + // > designed to not require these checks, so a careful application might be able to + // > improve on this slightly (Ed25519 verify down to 6.2ms, DH-finish to 3.2ms). c.AssertIsZero(c.ScalarMul(unchecked_point, L)) + return unchecked_point } @@ -64,17 +76,31 @@ func bits_to_element(c *EdCurve, input []frontend.Variable) EdPoint { // return c.ScalarMul(c.g, a) // } -func checkvalid(c *EdCurve, s, m, pk []frontend.Variable) { +func CheckValid(c *EdCurve, s, m, pk []frontend.Variable) { if len(s) != 512 { panic("bad signature length") } if len(pk) != 256 { panic("bad public key length") } + if len(m) % 8 != 0 { panic("bad message length") } R := bits_to_element(c, s[:256]) A := bits_to_element(c, pk) h := H(c.api, concat(s[:256], pk, m)) + fmt.Println("h", h) + fmt.Println("g", dbg(c.g.X), dbg(c.g.Y)) + fmt.Println("s last half", s[256:]) v1 := c.ScalarMulBinary(c.g, s[256:]) + fmt.Println("v1", dbg(v1.X), dbg(v1.Y)) v2 := c.Add(R, c.ScalarMulBinary(A, h)) + fmt.Println("v2", dbg(v2.X), dbg(v2.Y)) c.AssertIsEqual(v1, v2) } +func reverse[T interface{}](arr []T) []T { + result := make([]T, len(arr)) + for i, v := range arr { + result[len(result)-i-1] = v + } + return result +} + func concat(args ...[]frontend.Variable) []frontend.Variable { result := []frontend.Variable{} for _, v := range args { @@ -83,11 +109,11 @@ func concat(args ...[]frontend.Variable) []frontend.Variable { return result } -func decodepoint(c *EdCurve, input []frontend.Variable) EdPoint { - if len(input) != 256 { panic("bad length") } +func decodepoint(c *EdCurve, unclamped []frontend.Variable) EdPoint { + if len(unclamped) != 256 { panic("bad length") } - s := make([]frontend.Variable, len(input)) - copy(s, input) + s := make([]frontend.Variable, len(unclamped)) + copy(s, unclamped) s[255] = 0 y := bits_to_scalar(c, s) // unclamped = int(binascii.hexlify(s[:32][::-1]), 16) @@ -99,7 +125,7 @@ func decodepoint(c *EdCurve, input []frontend.Variable) EdPoint { xbits := c.baseApi.ToBinary(x) if len(xbits) != 256 { panic("bad length") } - mismatch := c.api.Xor(xbits[0], xbits[255]) + mismatch := c.api.Xor(xbits[0], unclamped[255]) x = c.baseApi.Select(mismatch, c.baseApi.Neg(x), x).(EdCoordinate) // if bool(x & 1) != bool(unclamped & (1<<255)): x = Q-x @@ -115,6 +141,21 @@ func decodepoint(c *EdCurve, input []frontend.Variable) EdPoint { return P } +func toValue(s EdCoordinate) *big.Int { + result := big.NewInt(0) + placeValue := big.NewInt(1) + for _, v := range s.Limbs { + q := new(big.Int).Mul(placeValue, v.(*big.Int)) + result.Add(result, q) + placeValue.Lsh(placeValue, Ed25519{}.BitsPerLimb()) + } + return result +} + +func dbg(s EdCoordinate) string { + return toValue(s).Text(16) +} + func _const(x int64) EdCoordinate { return emulated.NewElement[Ed25519](big.NewInt(x)) } @@ -149,8 +190,8 @@ func xrecover(c *EdCurve, y EdCoordinate) EdCoordinate { x = c.baseApi.Select(matches, x, c.baseApi.Mul(x, emulated.NewElement[Ed25519](I))).(EdCoordinate) // if (x*x - xx) % Q != 0: x = (x*I) % Q - even := c.baseApi.ToBinary(x)[0] - x = c.baseApi.Select(even, x, c.baseApi.Neg(x)).(EdCoordinate) + odd := c.baseApi.ToBinary(x)[0] + x = c.baseApi.Select(odd, c.baseApi.Neg(x), x).(EdCoordinate) // if x % 2 != 0: x = Q-x return x @@ -169,6 +210,17 @@ func pow(c *EdCurve, base EdCoordinate, exponent *big.Int) EdCoordinate { return result } +func swapByteEndianness(in []frontend.Variable) []frontend.Variable { + if len(in) % 8 != 0 { panic("must be a multiple of 8 bits") } + result := make([]frontend.Variable, len(in)) + for i := 0; i < len(in); i += 8 { + for j := 0; j < 8; j++ { + result[i+j] = in[i+7-j] + } + } + return result +} + // def checkvalid(s, m, pk): // if len(s) != 64: raise Exception("signature length is wrong") // if len(pk) != 32: raise Exception("public-key length is wrong") diff --git a/edwards_curve/eddsa25519_test.go b/edwards_curve/eddsa25519_test.go new file mode 100644 index 0000000..d3cbf5f --- /dev/null +++ b/edwards_curve/eddsa25519_test.go @@ -0,0 +1,65 @@ +package edwards_curve + +import ( + "testing" + "encoding/hex" + + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/test" +) + +type Eddsa25519Circuit struct { + m []frontend.Variable + pk []frontend.Variable + sig []frontend.Variable +} + +func (circuit *Eddsa25519Circuit) Define(api frontend.API) error { + c, err := New[Ed25519, Ed25519Scalars](api) + if err != nil { + return err + } + CheckValid(c, circuit.sig, circuit.m, circuit.pk) + return nil +} + +func TestEddsa25519(t *testing.T) { + assert := test.NewAssert(t) + + m := "53756363696e6374204c616273" + pk := "f7ec1c43f4de9d49556de87b86b26a98942cb078486fdb44de38b80864c39731" + sig := "35c323757c20640a294345c89c0bfcebe3d554fdb0c7b7a0bdb72222c531b1ec849fed99a053e0f5b02dd9a25bb6eb018885526d9f583cdbde0b1e9f6329da09" + + circuit := Eddsa25519Circuit { + m: hexToBits(m), + pk: hexToBits(pk), + sig: hexToBits(sig), + } + witness := Eddsa25519Circuit { + m: hexToBits(m), + pk: hexToBits(pk), + sig: hexToBits(sig), + } + + err := test.IsSolved(&circuit, &witness, testCurve.ScalarField()) + assert.NoError(err) +} + +func hexToBits(h string) []frontend.Variable { + b, err := hex.DecodeString(h) + if err != nil { + panic(err) + } + result := make([]frontend.Variable, len(b) * 8) + for i, v := range b { + for j := 0; j < 8; j++ { + if (v & (1 << j)) != 0 { + result[i*8+j] = 1 + } else { + result[i*8+j] = 0 + } + } + } + return result +} + diff --git a/edwards_curve/edpoint.go b/edwards_curve/edpoint.go index b99733a..d1930bc 100644 --- a/edwards_curve/edpoint.go +++ b/edwards_curve/edpoint.go @@ -185,16 +185,17 @@ func (c *Curve[T, S]) ScalarMul(p AffinePoint[T], s emulated.Element[S]) AffineP } func (c *Curve[T, S]) ScalarMulBinary(p AffinePoint[T], sBits []frontend.Variable) AffinePoint[T] { - res := p - acc := c.Double(p) + res := AffinePoint[T]{ + X: emulated.NewElement[T](0), + Y: emulated.NewElement[T](1), + } + acc := p - for i := 1; i < len(sBits); i++ { + for i := 0; i < len(sBits); i++ { tmp := c.Add(res, acc) res = c.Select(sBits[i], tmp, res) acc = c.Double(acc) } - tmp := c.Add(res, c.Neg(p)) - res = c.Select(sBits[0], res, tmp) return res } diff --git a/sha512/sha512.go b/sha512/sha512.go index b9be0fd..f9a8da6 100644 --- a/sha512/sha512.go +++ b/sha512/sha512.go @@ -15,18 +15,7 @@ func _right_rotate(n [64]frontend.Variable, bits int) [64]frontend.Variable { return result } -func Sha512Bytes(api frontend.API, in []frontend.Variable) ([512]frontend.Variable) { - bits := []frontend.Variable{} - for _, v := range in { - b := api.ToBinary(v, 8) - for i := 0; i < 8; i++ { - bits = append(bits, b[7-i]) - } - } - return Sha512Bits(api, bits) -} - -func Sha512Bits(api frontend.API, in []frontend.Variable) ([512]frontend.Variable) { +func Sha512(api frontend.API, in []frontend.Variable) ([512]frontend.Variable) { _not := func(x [64]frontend.Variable) [64]frontend.Variable { return not(api, x) } diff --git a/sha512/sha_test.go b/sha512/sha_test.go index 66c4fe4..a5d0acb 100644 --- a/sha512/sha_test.go +++ b/sha512/sha_test.go @@ -15,7 +15,7 @@ type Sha512Circuit struct { } func (circuit *Sha512Circuit) Define(api frontend.API) error { - res := Sha512Bits(api, circuit.in) + res := Sha512(api, circuit.in) if len(res) != 512 { panic("bad length") } for i := 0; i < 512; i++ { api.AssertIsEqual(res[i], circuit.out[i]) @@ -28,8 +28,7 @@ var testCurve = ecc.BN254 func TestSha512(t *testing.T) { assert := test.NewAssert(t) - testCase := func(input, output string) { - in := toBytes(input) + testCase := func(in []byte, output string) { out, err := hex.DecodeString(output) if err != nil { panic(err) } if len(out) != 512 / 8 { panic("bad output length") } @@ -46,8 +45,9 @@ func TestSha512(t *testing.T) { assert.NoError(err) } - testCase("", "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e") - testCase("Succinct Labs", "503ace098aa03f6feec1b5df0a38aee923f744a775508bc81f2b94ad139be297c2e8cd8c44af527b5d3f017a7fc929892c896604047e52e3f518924f52bff0dc") + testCase([]byte(""), "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e") + testCase([]byte("Succinct Labs"), "503ace098aa03f6feec1b5df0a38aee923f744a775508bc81f2b94ad139be297c2e8cd8c44af527b5d3f017a7fc929892c896604047e52e3f518924f52bff0dc") + testCase(decode("35c323757c20640a294345c89c0bfcebe3d554fdb0c7b7a0bdb72222c531b1ecf7ec1c43f4de9d49556de87b86b26a98942cb078486fdb44de38b80864c3973153756363696e6374204c616273"), "4388243c4452274402673de881b2f942ff5730fd2c7d8ddb94c3e3d789fb3754380cba8faa40554d9506a0730a681e88ab348a04bc5c41d18926f140b59aed39") } func toBits(arr []byte) []frontend.Variable { @@ -64,6 +64,10 @@ func toBits(arr []byte) []frontend.Variable { return result } -func toBytes(s string) []byte { - return []byte(s) +func decode(s string) []byte { + result, err := hex.DecodeString(s) + if err != nil { + panic(err) + } + return result } From ad21fd775fa2b1188c1e36458c45aac8248f8f05 Mon Sep 17 00:00:00 2001 From: Jacob Jackson Date: Wed, 5 Oct 2022 22:06:46 +0000 Subject: [PATCH 3/5] minor --- edwards_curve/edpoint.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edwards_curve/edpoint.go b/edwards_curve/edpoint.go index d1930bc..75c9300 100644 --- a/edwards_curve/edpoint.go +++ b/edwards_curve/edpoint.go @@ -156,7 +156,7 @@ func (c *Curve[T, S]) Double(p AffinePoint[T]) AffinePoint[T] { v := c.baseApi.Mul(p.X, p.X) w := c.baseApi.Mul(p.Y, p.Y) - n1 := c.baseApi.Mul(2, u) + n1 := c.baseApi.Add(u, u) av := c.baseApi.Mul(v, c.a) n2 := c.baseApi.Sub(w, av) d1 := c.baseApi.Add(w, av) From d61e54c60e634fbe112d7cd1f1222ceb4d543cad Mon Sep 17 00:00:00 2001 From: Jacob Jackson Date: Wed, 5 Oct 2022 22:08:11 +0000 Subject: [PATCH 4/5] remove check that they say is unnecessary --- edwards_curve/eddsa25519.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/edwards_curve/eddsa25519.go b/edwards_curve/eddsa25519.go index daa1ab6..6d640a5 100644 --- a/edwards_curve/eddsa25519.go +++ b/edwards_curve/eddsa25519.go @@ -56,16 +56,16 @@ func bits_to_scalar(c *EdCurve, s []frontend.Variable) EdCoordinate { // } func bits_to_element(c *EdCurve, input []frontend.Variable) EdPoint { - L := emulated.NewElement[Ed25519Scalars](rEd25519) + // L := emulated.NewElement[Ed25519Scalars](rEd25519) unchecked_point := decodepoint(c, input) - // TODO: https://github.com/warner/python-pure25519 says this check is not necessary: - // - // > This library is conservative, and performs full subgroup-membership checks on decoded - // > points, which adds considerable overhead. The Curve25519/Ed25519 algorithms were - // > designed to not require these checks, so a careful application might be able to - // > improve on this slightly (Ed25519 verify down to 6.2ms, DH-finish to 3.2ms). - c.AssertIsZero(c.ScalarMul(unchecked_point, L)) + // // TODO: https://github.com/warner/python-pure25519 says this check is not necessary: + // // + // // > This library is conservative, and performs full subgroup-membership checks on decoded + // // > points, which adds considerable overhead. The Curve25519/Ed25519 algorithms were + // // > designed to not require these checks, so a careful application might be able to + // // > improve on this slightly (Ed25519 verify down to 6.2ms, DH-finish to 3.2ms). + // c.AssertIsZero(c.ScalarMul(unchecked_point, L)) return unchecked_point } From 52dade9335211ffe2fa83e93bd723035ffc14194 Mon Sep 17 00:00:00 2001 From: Jacob Jackson Date: Fri, 7 Oct 2022 06:19:24 +0000 Subject: [PATCH 5/5] get signature working --- cubic.go | 106 ++++++++++----------- ed25519.go | 180 +++++++++++++++++++++++++++++------- edwards_curve/eddsa25519.go | 20 +--- 3 files changed, 205 insertions(+), 101 deletions(-) diff --git a/cubic.go b/cubic.go index 0e793b9..e4504e8 100644 --- a/cubic.go +++ b/cubic.go @@ -14,61 +14,61 @@ package main -import ( - "fmt" - "os" - "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark/frontend/cs/r1cs" - "github.com/consensys/gnark/backend/groth16" - _ "gnark-ed25519/edwards_curve" - _ "gnark-ed25519/sha512" -) +// import ( +// "fmt" +// "os" +// "github.com/consensys/gnark/frontend" +// "github.com/consensys/gnark-crypto/ecc" +// "github.com/consensys/gnark/frontend/cs/r1cs" +// "github.com/consensys/gnark/backend/groth16" +// _ "gnark-ed25519/edwards_curve" +// _ "gnark-ed25519/sha512" +// ) -// Circuit defines a simple circuit -// x**3 + x + 5 == y -type Circuit struct { - // struct tags on a variable is optional - // default uses variable name and secret visibility. - X frontend.Variable `gnark:"x"` - Y frontend.Variable `gnark:",public"` -} +// // Circuit defines a simple circuit +// // x**3 + x + 5 == y +// type Circuit struct { +// // struct tags on a variable is optional +// // default uses variable name and secret visibility. +// X frontend.Variable `gnark:"x"` +// Y frontend.Variable `gnark:",public"` +// } -// Define declares the circuit constraints -// x**3 + x + 5 == y -func (circuit *Circuit) Define(api frontend.API) error { - x3 := api.Mul(circuit.X, circuit.X, circuit.X) - api.AssertIsEqual(circuit.Y, api.Add(x3, circuit.X, 5)) - return nil -} +// // Define declares the circuit constraints +// // x**3 + x + 5 == y +// func (circuit *Circuit) Define(api frontend.API) error { +// x3 := api.Mul(circuit.X, circuit.X, circuit.X) +// api.AssertIsEqual(circuit.Y, api.Add(x3, circuit.X, 5)) +// return nil +// } -func main() { - err := mainImpl() - if err != nil { - fmt.Println(err) - os.Exit(1) - } -} +// func main() { +// err := mainImpl() +// if err != nil { +// fmt.Println(err) +// os.Exit(1) +// } +// } -func mainImpl() error { - var myCircuit Circuit - r1cs, err := frontend.Compile(ecc.BN254.ScalarField(), r1cs.NewBuilder, &myCircuit) - if err != nil { - return err - } +// func mainImpl() error { +// var myCircuit Circuit +// r1cs, err := frontend.Compile(ecc.BN254.ScalarField(), r1cs.NewBuilder, &myCircuit) +// if err != nil { +// return err +// } - assignment := &Circuit{ - X: "2", - Y: "15", - } - witness, _ := frontend.NewWitness(assignment, ecc.BN254.ScalarField()) - publicWitness, _ := witness.Public() - pk, vk, err := groth16.Setup(r1cs) - proof, err := groth16.Prove(r1cs, pk, witness) - err = groth16.Verify(proof, vk, publicWitness) - if err != nil { - return err - } - fmt.Println(proof) - return nil -} +// assignment := &Circuit{ +// X: "2", +// Y: "15", +// } +// witness, _ := frontend.NewWitness(assignment, ecc.BN254.ScalarField()) +// publicWitness, _ := witness.Public() +// pk, vk, err := groth16.Setup(r1cs) +// proof, err := groth16.Prove(r1cs, pk, witness) +// err = groth16.Verify(proof, vk, publicWitness) +// if err != nil { +// return err +// } +// fmt.Println(proof) +// return nil +// } diff --git a/ed25519.go b/ed25519.go index 0dbc218..9d96483 100644 --- a/ed25519.go +++ b/ed25519.go @@ -14,43 +14,159 @@ package main -// import ( -// "fmt" -// "os" -// "crypto/ed25519" -// "crypto/rand" -// "github.com/consensys/gnark/std/math/emulated" -// ) +import ( + "time" + "fmt" + "os" + "encoding/hex" + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark/frontend/cs/r1cs" + "github.com/consensys/gnark/backend/groth16" + "gnark-ed25519/edwards_curve" + "gnark-ed25519/sha512" +) +type Eddsa25519Circuit struct { + M []frontend.Variable + Pk []frontend.Variable + Sig []frontend.Variable +} -// func main() { -// err := mainImpl() +func (circuit *Eddsa25519Circuit) Define(api frontend.API) error { + c, err := edwards_curve.New[edwards_curve.Ed25519, edwards_curve.Ed25519Scalars](api) + if err != nil { + return err + } + edwards_curve.CheckValid(c, circuit.Sig, circuit.M, circuit.Pk) + return nil +} + + +type Sha512Circuit struct { + in []frontend.Variable `gnark:"in"` + out []frontend.Variable `gnark:"out"` +} + +func (circuit *Sha512Circuit) Define(api frontend.API) error { + res := sha512.Sha512(api, circuit.in) + if len(res) != 512 { panic("bad length") } + for i := 0; i < 512; i++ { + api.AssertIsEqual(res[i], circuit.out[i]) + } + return nil +} + + + +func main() { + err := mainImpl() + if err != nil { + fmt.Println(err) + os.Exit(1) + } +} + +// func mainImpl() error { +// in := bytesToBits([]byte("Succinct Labs")) +// out := hexToBits("503ace098aa03f6feec1b5df0a38aee923f744a775508bc81f2b94ad139be297c2e8cd8c44af527b5d3f017a7fc929892c896604047e52e3f518924f52bff0dc") + +// myCircuit := Sha512Circuit{ +// in, +// out, +// } +// fmt.Println(time.Now(), "compiling...") +// r1cs, err := frontend.Compile(ecc.BN254.ScalarField(), r1cs.NewBuilder, &myCircuit) // if err != nil { -// fmt.Println(err) -// os.Exit(1) +// return err // } + +// assignment := &Sha512Circuit{ +// in, +// out, +// } +// fmt.Println(time.Now(), "generating witness...") +// witness, _ := frontend.NewWitness(assignment, ecc.BN254.ScalarField()) +// publicWitness, _ := witness.Public() +// fmt.Println(time.Now(), "groth setup...") +// pk, vk, err := groth16.Setup(r1cs) +// fmt.Println(time.Now(), "groth prove...") +// proof, err := groth16.Prove(r1cs, pk, witness) +// fmt.Println(time.Now(), "groth verify...") +// err = groth16.Verify(proof, vk, publicWitness) +// if err != nil { +// return err +// } +// fmt.Println(proof) +// return nil // } -// func mainImpl() error { -// pubKey, privKey, err := ed25519.GenerateKey(rand.Reader) -// fmt.Println(pubKey) -// fmt.Println(privKey) -// message := []byte("string") -// sig := ed25519.Sign(privKey, message) -// fmt.Println(sig) -// verified := ed25519.Verify(pubKey, message, sig) -// fmt.Println(verified) - -// verifiedFalse := ed25519.Verify(pubKey, []byte("string1"), sig) -// fmt.Println(verifiedFalse) - -// ele := emulated.NewElement[emulated.BN254Fp](1) -// fmt.Println(ele) - -// if err != nil { -// return err -// } -// return nil +func mainImpl() error { + M := "53756363696e6374204c616273" + Pk := "f7ec1c43f4de9d49556de87b86b26a98942cb078486fdb44de38b80864c39731" + Sig := "35c323757c20640a294345c89c0bfcebe3d554fdb0c7b7a0bdb72222c531b1ec849fed99a053e0f5b02dd9a25bb6eb018885526d9f583cdbde0b1e9f6329da09" -// } + myCircuit := Eddsa25519Circuit{ + M: hexToBits(M), + Pk: hexToBits(Pk), + Sig: hexToBits(Sig), + } + fmt.Println(time.Now(), "compiling...") + r1cs, err := frontend.Compile(ecc.BN254.ScalarField(), r1cs.NewBuilder, &myCircuit) + if err != nil { + return err + } + + assignment := &Eddsa25519Circuit{ + M: hexToBits(M), + Pk: hexToBits(Pk), + Sig: hexToBits(Sig), + } + fmt.Println(time.Now(), "generating witness...") + witness, _ := frontend.NewWitness(assignment, ecc.BN254.ScalarField()) + publicWitness, _ := witness.Public() + fmt.Println(time.Now(), "groth setup...") + pk, vk, err := groth16.Setup(r1cs) + fmt.Println(time.Now(), "groth prove...") + proof, err := groth16.Prove(r1cs, pk, witness) + fmt.Println(time.Now(), "groth verify...") + err = groth16.Verify(proof, vk, publicWitness) + if err != nil { + return err + } + fmt.Println(proof) + return nil +} + +func hexToBits(h string) []frontend.Variable { + b, err := hex.DecodeString(h) + if err != nil { + panic(err) + } + result := make([]frontend.Variable, len(b) * 8) + for i, v := range b { + for j := 0; j < 8; j++ { + if (v & (1 << j)) != 0 { + result[i*8+j] = 1 + } else { + result[i*8+j] = 0 + } + } + } + return result +} + +func bytesToBits(arr []byte) []frontend.Variable { + result := make([]frontend.Variable, len(arr) * 8) + for i, v := range arr { + for j := 0; j < 8; j++ { + if (v & (1 << (7-j))) != 0 { + result[i*8+j] = 1 + } else { + result[i*8+j] = 0 + } + } + } + return result +} diff --git a/edwards_curve/eddsa25519.go b/edwards_curve/eddsa25519.go index 6d640a5..e15de5b 100644 --- a/edwards_curve/eddsa25519.go +++ b/edwards_curve/eddsa25519.go @@ -4,7 +4,6 @@ package edwards_curve // This file is little-endian import ( - "fmt" "math/big" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/std/math/emulated" @@ -13,10 +12,8 @@ import ( func H(api frontend.API, m []frontend.Variable) []frontend.Variable { - fmt.Println("sha input", m) rawResult := sha512.Sha512(api, swapByteEndianness(m)) sResult := swapByteEndianness(rawResult[:]) - fmt.Println("sha output", sResult) return sResult } @@ -36,10 +33,10 @@ func bits_to_scalar(c *EdCurve, s []frontend.Variable) EdCoordinate { elt := emulated.NewElement[Ed25519](0) if len(elt.Limbs) != 4 { panic("bad length") } i := 0 - elt.Limbs[0] = c.api.FromBinary(s[i:i+64]...); i += 64 - elt.Limbs[1] = c.api.FromBinary(s[i:i+64]...); i += 64 - elt.Limbs[2] = c.api.FromBinary(s[i:i+64]...); i += 64 - elt.Limbs[3] = c.api.FromBinary(s[i:i+64]...); i += 64 + for k := 0; k < 4; k++ { + elt.Limbs[k] = c.api.FromBinary(s[i:i+64]...) + i += 64 + } if i != len(s) { panic("bad length") } return elt } @@ -83,13 +80,8 @@ func CheckValid(c *EdCurve, s, m, pk []frontend.Variable) { R := bits_to_element(c, s[:256]) A := bits_to_element(c, pk) h := H(c.api, concat(s[:256], pk, m)) - fmt.Println("h", h) - fmt.Println("g", dbg(c.g.X), dbg(c.g.Y)) - fmt.Println("s last half", s[256:]) v1 := c.ScalarMulBinary(c.g, s[256:]) - fmt.Println("v1", dbg(v1.X), dbg(v1.Y)) v2 := c.Add(R, c.ScalarMulBinary(A, h)) - fmt.Println("v2", dbg(v2.X), dbg(v2.Y)) c.AssertIsEqual(v1, v2) } @@ -152,10 +144,6 @@ func toValue(s EdCoordinate) *big.Int { return result } -func dbg(s EdCoordinate) string { - return toValue(s).Text(16) -} - func _const(x int64) EdCoordinate { return emulated.NewElement[Ed25519](big.NewInt(x)) }