diff --git a/elgamal/ciphertext.go b/elgamal/ciphertext.go new file mode 100644 index 0000000..1be4864 --- /dev/null +++ b/elgamal/ciphertext.go @@ -0,0 +1,60 @@ +package elgamal + +import ( + ecc_tweds "github.com/consensys/gnark-crypto/ecc/twistededwards" + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/std/algebra/native/twistededwards" + "github.com/iden3/go-iden3-crypto/babyjub" +) + +type Ciphertext struct { + C1, C2 twistededwards.Point +} + +func NewCiphertext() *Ciphertext { + zero := babyjub.NewPoint() + return &Ciphertext{C1: twistededwards.Point{X: zero.X, Y: zero.Y}, C2: twistededwards.Point{X: zero.X, Y: zero.Y}} +} + +// Add sets z to the sum x+y and returns z. +// +// Panics if twistededwards curve init fails. +func (z *Ciphertext) Add(api frontend.API, x, y *Ciphertext) *Ciphertext { + curve, err := twistededwards.NewEdCurve(api, ecc_tweds.BN254) + if err != nil { + panic(err) + } + for _, p := range []twistededwards.Point{x.C1, x.C2, y.C1, y.C2} { + curve.AssertIsOnCurve(p) + } + z.C1 = curve.Add(x.C1, y.C1) + z.C2 = curve.Add(x.C2, y.C2) + return z +} + +// AssertIsEqual fails if any of the fields differ between z and x +func (z *Ciphertext) AssertIsEqual(api frontend.API, x *Ciphertext) { + api.AssertIsEqual(z.C1.X, z.C1.X) + api.AssertIsEqual(z.C1.Y, x.C1.Y) + api.AssertIsEqual(z.C2.X, x.C2.X) + api.AssertIsEqual(z.C2.Y, x.C2.Y) +} + +// Select if b is true, sets z = i1, else z = i2, and returns z +func (z *Ciphertext) Select(api frontend.API, b frontend.Variable, i1 *Ciphertext, i2 *Ciphertext) *Ciphertext { + z.C1.X = api.Select(b, i1.C1.X, i2.C1.X) + z.C1.Y = api.Select(b, i1.C1.Y, i2.C1.Y) + z.C2.X = api.Select(b, i1.C2.X, i2.C2.X) + z.C2.Y = api.Select(b, i1.C2.Y, i2.C2.Y) + return z +} + +// Serialize returns a slice with the C1.X, C1.Y, C2.X, C2.Y in order +func (z *Ciphertext) Serialize() []frontend.Variable { + return []frontend.Variable{ + z.C1.X, + z.C1.Y, + z.C2.X, + z.C2.Y, + } +} diff --git a/homomorphic/add_test.go b/elgamal/ciphertext_test.go similarity index 69% rename from homomorphic/add_test.go rename to elgamal/ciphertext_test.go index 0b1e252..d849d24 100644 --- a/homomorphic/add_test.go +++ b/elgamal/ciphertext_test.go @@ -1,4 +1,4 @@ -package hadd +package elgamal import ( "crypto/rand" @@ -18,34 +18,21 @@ import ( "github.com/vocdoni/vocdoni-z-sandbox/ecc/format" ) -type testHomomorphicAddCircuit struct { - A1 twistededwards.Point `gnark:"a1,public"` - A2 twistededwards.Point `gnark:"a2,public"` - B1 twistededwards.Point `gnark:"b1,public"` - B2 twistededwards.Point `gnark:"b2,public"` - C1 twistededwards.Point `gnark:"c1,public"` - C2 twistededwards.Point `gnark:"c2,public"` +type testElGamalAddCircuit struct { + A Ciphertext `gnark:",public"` + B Ciphertext `gnark:",public"` + Sum Ciphertext `gnark:",public"` } -func (c *testHomomorphicAddCircuit) Define(api frontend.API) error { - // calculate and check c1 - c1, err := Add(api, c.A1, c.B1) - if err != nil { - return err - } - api.AssertIsEqual(c.C1.X, c1.X) - api.AssertIsEqual(c.C1.Y, c1.Y) - // calculate and check c2 - c2, err := Add(api, c.A2, c.B2) - if err != nil { - return err - } - api.AssertIsEqual(c.C2.X, c2.X) - api.AssertIsEqual(c.C2.Y, c2.Y) +func (c *testElGamalAddCircuit) Define(api frontend.API) error { + // calculate and check sum + sum := &Ciphertext{} + sum.Add(api, &c.A, &c.B) + sum.AssertIsEqual(api, &c.Sum) return nil } -func TestHomomorphicAdd(t *testing.T) { +func TestElGamalAdd(t *testing.T) { // generate a public mocked key and a random k to encrypt first message _, pubKey := generateKeyPair() k1, err := randomK() @@ -80,40 +67,46 @@ func TestHomomorphicAdd(t *testing.T) { // profiling the circuit compilation p := profile.Start() now := time.Now() - _, _ = frontend.Compile(ecc.BN254.ScalarField(), r1cs.NewBuilder, &testHomomorphicAddCircuit{}) + _, _ = frontend.Compile(ecc.BN254.ScalarField(), r1cs.NewBuilder, &testElGamalAddCircuit{}) fmt.Println("elapsed", time.Since(now)) p.Stop() fmt.Println("constrains", p.NbConstraints()) // run the test to prove the homomorphic property assert := test.NewAssert(t) - inputs := &testHomomorphicAddCircuit{ - A1: twistededwards.Point{ - X: xA1RTE, - Y: yA1RTE, - }, - A2: twistededwards.Point{ - X: xA2RTE, - Y: yA2RTE, - }, - B1: twistededwards.Point{ - X: xB1RTE, - Y: yB1RTE, - }, - B2: twistededwards.Point{ - X: xB2RTE, - Y: yB2RTE, + inputs := &testElGamalAddCircuit{ + A: Ciphertext{ + C1: twistededwards.Point{ + X: xA1RTE, + Y: yA1RTE, + }, + C2: twistededwards.Point{ + X: xA2RTE, + Y: yA2RTE, + }, }, - C1: twistededwards.Point{ - X: xC1RTE, - Y: yC1RTE, + B: Ciphertext{ + C1: twistededwards.Point{ + X: xB1RTE, + Y: yB1RTE, + }, + C2: twistededwards.Point{ + X: xB2RTE, + Y: yB2RTE, + }, }, - C2: twistededwards.Point{ - X: xC2RTE, - Y: yC2RTE, + Sum: Ciphertext{ + C1: twistededwards.Point{ + X: xC1RTE, + Y: yC1RTE, + }, + C2: twistededwards.Point{ + X: xC2RTE, + Y: yC2RTE, + }, }, } now = time.Now() - assert.SolvingSucceeded(&testHomomorphicAddCircuit{}, inputs, test.WithCurves(ecc.BN254), test.WithBackends(backend.GROTH16)) + assert.SolvingSucceeded(&testElGamalAddCircuit{}, inputs, test.WithCurves(ecc.BN254), test.WithBackends(backend.GROTH16)) fmt.Println("elapsed", time.Since(now)) } diff --git a/homomorphic/add.go b/homomorphic/add.go deleted file mode 100644 index cfda373..0000000 --- a/homomorphic/add.go +++ /dev/null @@ -1,17 +0,0 @@ -package hadd - -import ( - ecc_tweds "github.com/consensys/gnark-crypto/ecc/twistededwards" - "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/std/algebra/native/twistededwards" -) - -func Add(api frontend.API, a, b twistededwards.Point) (twistededwards.Point, error) { - curve, err := twistededwards.NewEdCurve(api, ecc_tweds.BN254) - if err != nil { - return twistededwards.Point{}, err - } - curve.AssertIsOnCurve(a) - curve.AssertIsOnCurve(b) - return curve.Add(a, b), nil -}