diff --git a/std/signature/ecdsa/ecdsa.go b/std/signature/ecdsa/ecdsa.go index 201a802323..3f28d995ec 100644 --- a/std/signature/ecdsa/ecdsa.go +++ b/std/signature/ecdsa/ecdsa.go @@ -19,6 +19,28 @@ type PublicKey[Base, Scalar emulated.FieldParams] sw_emulated.AffinePoint[Base] // // We assume that the message msg is already hashed to the scalar field. func (pk PublicKey[T, S]) Verify(api frontend.API, params sw_emulated.CurveParams, msg *emulated.Element[S], sig *Signature[S]) { + qxBits, rbits := pk.checkParams(api, params, msg, sig) + for i := range rbits { + api.AssertIsEqual(rbits[i], qxBits[i]) + } +} + +// IsVerified returns whether the signature sig verifies for the message msg and public +// key pk. The curve parameters params define the elliptic curve. +// We assume that the message msg is already hashed to the scalar field. +// If the signature is valid, it returns 1; otherwise, it returns 0 +func (pk PublicKey[T, S]) IsVerified(api frontend.API, params sw_emulated.CurveParams, msg *emulated.Element[S], sig *Signature[S]) frontend.Variable { + qxBits, rbits := pk.checkParams(api, params, msg, sig) + verified := frontend.Variable(1) + for i := range rbits { + res := api.IsZero(api.Sub(rbits[i], qxBits[i])) + verified = api.And(verified, res) + } + return verified + +} + +func (pk PublicKey[T, S]) checkParams(api frontend.API, params sw_emulated.CurveParams, msg *emulated.Element[S], sig *Signature[S]) ([]frontend.Variable, []frontend.Variable) { cr, err := sw_emulated.New[T, S](api, params) if err != nil { panic(err) @@ -43,7 +65,5 @@ func (pk PublicKey[T, S]) Verify(api frontend.API, params sw_emulated.CurveParam if len(rbits) != len(qxBits) { panic("non-equal lengths") } - for i := range rbits { - api.AssertIsEqual(rbits[i], qxBits[i]) - } + return qxBits, rbits } diff --git a/std/signature/ecdsa/ecdsa_test.go b/std/signature/ecdsa/ecdsa_test.go index 57ff1a4406..a62b9733e6 100644 --- a/std/signature/ecdsa/ecdsa_test.go +++ b/std/signature/ecdsa/ecdsa_test.go @@ -158,3 +158,111 @@ func ExamplePublicKey_Verify_create() { // can continue in the PublicKey Verify example _, _, _, _, _ = sig.R, sig.S, msg, pubx, puby } + +type EcdsaCircuitV1[T, S emulated.FieldParams] struct { + Sig Signature[S] + Msg emulated.Element[S] + Pub PublicKey[T, S] +} + +func (c *EcdsaCircuitV1[T, S]) Define(api frontend.API) error { + verified := c.Pub.IsVerified(api, sw_emulated.GetCurveParams[T](), &c.Msg, &c.Sig) + api.AssertIsEqual(verified, 1) + return nil +} + +func TestEcdsaPublicKeyIsVerifiedOK(t *testing.T) { + + // generate parameters + privKey, _ := ecdsa.GenerateKey(rand.Reader) + publicKey := privKey.PublicKey + + // sign + msg := []byte("testing ECDSA (pre-hashed)") + sigBin, _ := privKey.Sign(msg, nil) + + // check that the signature is correct + flag, _ := publicKey.Verify(sigBin, msg, nil) + if !flag { + t.Errorf("can't verify signature") + } + + // unmarshal signature + var sig ecdsa.Signature + sig.SetBytes(sigBin) + r, s := new(big.Int), new(big.Int) + r.SetBytes(sig.R[:32]) + s.SetBytes(sig.S[:32]) + + hash := ecdsa.HashToInt(msg) + + circuit := EcdsaCircuitV1[emulated.Secp256k1Fp, emulated.Secp256k1Fr]{} + witness := EcdsaCircuitV1[emulated.Secp256k1Fp, emulated.Secp256k1Fr]{ + Sig: Signature[emulated.Secp256k1Fr]{ + R: emulated.ValueOf[emulated.Secp256k1Fr](r), + S: emulated.ValueOf[emulated.Secp256k1Fr](s), + }, + Msg: emulated.ValueOf[emulated.Secp256k1Fr](hash), + Pub: PublicKey[emulated.Secp256k1Fp, emulated.Secp256k1Fr]{ + X: emulated.ValueOf[emulated.Secp256k1Fp](privKey.PublicKey.A.X), + Y: emulated.ValueOf[emulated.Secp256k1Fp](privKey.PublicKey.A.Y), + }, + } + assert := test.NewAssert(t) + err := test.IsSolved(&circuit, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} + +type EcdsaCircuitV2[T, S emulated.FieldParams] struct { + Sig Signature[S] + Msg emulated.Element[S] + Pub PublicKey[T, S] +} + +func (c *EcdsaCircuitV2[T, S]) Define(api frontend.API) error { + verified := c.Pub.IsVerified(api, sw_emulated.GetCurveParams[T](), &c.Msg, &c.Sig) + api.AssertIsEqual(verified, 0) + return nil +} + +func TestEcdsaPublicKeyIsVerifiedNegative(t *testing.T) { + + // generate parameters + privKey, _ := ecdsa.GenerateKey(rand.Reader) + publicKey := privKey.PublicKey + + // sign + msg := []byte("testing ECDSA (pre-hashed)") + sigBin, _ := privKey.Sign(msg, nil) + + // check that the signature is correct + flag, _ := publicKey.Verify(sigBin, msg, nil) + if !flag { + t.Errorf("can't verify signature") + } + + // unmarshal signature + var sig ecdsa.Signature + sig.SetBytes(sigBin) + r, s := new(big.Int), new(big.Int) + r.SetBytes(sig.R[:32]) + s.SetBytes(sig.S[:32]) + + hash := ecdsa.HashToInt(msg) + + circuit := EcdsaCircuitV2[emulated.Secp256k1Fp, emulated.Secp256k1Fr]{} + witness := EcdsaCircuitV2[emulated.Secp256k1Fp, emulated.Secp256k1Fr]{ + Sig: Signature[emulated.Secp256k1Fr]{ + R: emulated.ValueOf[emulated.Secp256k1Fr](r), + S: emulated.ValueOf[emulated.Secp256k1Fr](s), + }, + Msg: emulated.ValueOf[emulated.Secp256k1Fr](hash), + Pub: PublicKey[emulated.Secp256k1Fp, emulated.Secp256k1Fr]{ + X: emulated.ValueOf[emulated.Secp256k1Fp](privKey.PublicKey.A.X), + Y: emulated.ValueOf[emulated.Secp256k1Fp](privKey.PublicKey.A.Y), + }, + } + assert := test.NewAssert(t) + err := test.IsSolved(&circuit, &witness, ecc.BN254.ScalarField()) + assert.Error(err) +}