From 4e6c1092b8cf722434899fe90639c41bca437661 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Thu, 4 Mar 2021 20:56:54 +0300 Subject: [PATCH] core: add Neo.Crypto.CheckSig interop --- pkg/compiler/syscall_test.go | 1 + pkg/core/interop/crypto/ecdsa.go | 14 ++++++ pkg/core/interop/crypto/ecdsa_test.go | 61 ++++++++++++++++++++++++++ pkg/core/interop/crypto/interop.go | 2 + pkg/core/interop/interopnames/names.go | 2 + pkg/core/interops.go | 1 + pkg/interop/crypto/crypto.go | 6 +++ 7 files changed, 87 insertions(+) diff --git a/pkg/compiler/syscall_test.go b/pkg/compiler/syscall_test.go index 08e42b6ec..648267be1 100644 --- a/pkg/compiler/syscall_test.go +++ b/pkg/compiler/syscall_test.go @@ -93,6 +93,7 @@ func TestSyscallExecution(t *testing.T) { "crypto.ECDsaSecp256k1Verify": {interopnames.NeoCryptoVerifyWithECDsaSecp256k1, []string{b, pub, sig}, false}, "crypto.ECDSASecp256r1CheckMultisig": {interopnames.NeoCryptoCheckMultisigWithECDsaSecp256r1, []string{b, pubs, sigs}, false}, "crypto.ECDSASecp256k1CheckMultisig": {interopnames.NeoCryptoCheckMultisigWithECDsaSecp256k1, []string{b, pubs, sigs}, false}, + "crypto.CheckSig": {interopnames.NeoCryptoCheckSig, []string{pub, sig}, false}, } ic := &interop.Context{} core.SpawnVM(ic) // set Functions field diff --git a/pkg/core/interop/crypto/ecdsa.go b/pkg/core/interop/crypto/ecdsa.go index a31ca3e70..2c2b6931d 100644 --- a/pkg/core/interop/crypto/ecdsa.go +++ b/pkg/core/interop/crypto/ecdsa.go @@ -99,3 +99,17 @@ func getMessageHash(ic *interop.Context, item stackitem.Item) (util.Uint256, err } return hash.Sha256(msg), nil } + +// ECDSASecp256r1CheckSig checks ECDSA signature using Secp256r1 elliptic curve. +func ECDSASecp256r1CheckSig(ic *interop.Context) error { + hashToCheck := ic.Container.GetSignedHash() + keyb := ic.VM.Estack().Pop().Bytes() + signature := ic.VM.Estack().Pop().Bytes() + pkey, err := keys.NewPublicKeyFromBytes(keyb, elliptic.P256()) + if err != nil { + return err + } + res := pkey.Verify(signature, hashToCheck.BytesBE()) + ic.VM.Estack().PushVal(res) + return nil +} diff --git a/pkg/core/interop/crypto/ecdsa_test.go b/pkg/core/interop/crypto/ecdsa_test.go index 1939514f2..262f66f2b 100644 --- a/pkg/core/interop/crypto/ecdsa_test.go +++ b/pkg/core/interop/crypto/ecdsa_test.go @@ -279,3 +279,64 @@ func testCurveCHECKMULTISIGBad(t *testing.T, isR1 bool) { require.Error(t, v.Run()) }) } + +func TestCheckSig(t *testing.T) { + priv, err := keys.NewPrivateKey() + require.NoError(t, err) + + verifyFunc := ECDSASecp256r1CheckSig + d := dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet, false) + ic := &interop.Context{DAO: dao.NewCached(d)} + runCase := func(t *testing.T, isErr bool, result interface{}, args ...interface{}) { + ic.SpawnVM() + for i := range args { + ic.VM.Estack().PushVal(args[i]) + } + + var err error + func() { + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("panic: %v", r) + } + }() + err = verifyFunc(ic) + }() + + if isErr { + require.Error(t, err) + return + } + require.NoError(t, err) + require.Equal(t, 1, ic.VM.Estack().Len()) + require.Equal(t, result, ic.VM.Estack().Pop().Value().(bool)) + } + + tx := transaction.New(netmode.UnitTestNet, []byte{0, 1, 2}, 1) + msg := tx.GetSignedPart() + ic.Container = tx + + t.Run("success", func(t *testing.T) { + sign := priv.Sign(msg) + runCase(t, false, true, sign, priv.PublicKey().Bytes()) + }) + + t.Run("missing argument", func(t *testing.T) { + runCase(t, true, false) + sign := priv.Sign(msg) + runCase(t, true, false, sign) + }) + + t.Run("invalid signature", func(t *testing.T) { + sign := priv.Sign(msg) + sign[0] = ^sign[0] + runCase(t, false, false, sign, priv.PublicKey().Bytes()) + }) + + t.Run("invalid public key", func(t *testing.T) { + sign := priv.Sign(msg) + pub := priv.PublicKey().Bytes() + pub[0] = 0xFF // invalid prefix + runCase(t, true, false, sign, pub) + }) +} diff --git a/pkg/core/interop/crypto/interop.go b/pkg/core/interop/crypto/interop.go index 6d8172f99..d754d8411 100644 --- a/pkg/core/interop/crypto/interop.go +++ b/pkg/core/interop/crypto/interop.go @@ -10,6 +10,7 @@ var ( ecdsaSecp256k1VerifyID = interopnames.ToID([]byte(interopnames.NeoCryptoVerifyWithECDsaSecp256k1)) ecdsaSecp256r1CheckMultisigID = interopnames.ToID([]byte(interopnames.NeoCryptoCheckMultisigWithECDsaSecp256r1)) ecdsaSecp256k1CheckMultisigID = interopnames.ToID([]byte(interopnames.NeoCryptoCheckMultisigWithECDsaSecp256k1)) + neoCryptoCheckSigID = interopnames.ToID([]byte(interopnames.NeoCryptoCheckSig)) ) var cryptoInterops = []interop.Function{ @@ -17,6 +18,7 @@ var cryptoInterops = []interop.Function{ {ID: ecdsaSecp256k1VerifyID, Func: ECDSASecp256k1Verify}, {ID: ecdsaSecp256r1CheckMultisigID, Func: ECDSASecp256r1CheckMultisig}, {ID: ecdsaSecp256k1CheckMultisigID, Func: ECDSASecp256k1CheckMultisig}, + {ID: neoCryptoCheckSigID, Func: ECDSASecp256r1CheckSig}, } func init() { diff --git a/pkg/core/interop/interopnames/names.go b/pkg/core/interop/interopnames/names.go index 127d05121..d81787f55 100644 --- a/pkg/core/interop/interopnames/names.go +++ b/pkg/core/interop/interopnames/names.go @@ -41,6 +41,7 @@ const ( NeoCryptoVerifyWithECDsaSecp256k1 = "Neo.Crypto.VerifyWithECDsaSecp256k1" NeoCryptoCheckMultisigWithECDsaSecp256r1 = "Neo.Crypto.CheckMultisigWithECDsaSecp256r1" NeoCryptoCheckMultisigWithECDsaSecp256k1 = "Neo.Crypto.CheckMultisigWithECDsaSecp256k1" + NeoCryptoCheckSig = "Neo.Crypto.CheckSig" ) var names = []string{ @@ -83,4 +84,5 @@ var names = []string{ NeoCryptoVerifyWithECDsaSecp256k1, NeoCryptoCheckMultisigWithECDsaSecp256r1, NeoCryptoCheckMultisigWithECDsaSecp256k1, + NeoCryptoCheckSig, } diff --git a/pkg/core/interops.go b/pkg/core/interops.go index 3e871e239..28a33dc7d 100644 --- a/pkg/core/interops.go +++ b/pkg/core/interops.go @@ -81,6 +81,7 @@ var neoInterops = []interop.Function{ Price: fee.ECDSAVerifyPrice, ParamCount: 3}, {Name: interopnames.NeoCryptoCheckMultisigWithECDsaSecp256r1, Func: crypto.ECDSASecp256r1CheckMultisig, Price: 0, ParamCount: 3}, {Name: interopnames.NeoCryptoCheckMultisigWithECDsaSecp256k1, Func: crypto.ECDSASecp256k1CheckMultisig, Price: 0, ParamCount: 3}, + {Name: interopnames.NeoCryptoCheckSig, Func: crypto.ECDSASecp256r1CheckSig, Price: fee.ECDSAVerifyPrice, ParamCount: 2}, } // initIDinInteropsSlice initializes IDs from names in one given diff --git a/pkg/interop/crypto/crypto.go b/pkg/interop/crypto/crypto.go index f988632d0..d92bee275 100644 --- a/pkg/interop/crypto/crypto.go +++ b/pkg/interop/crypto/crypto.go @@ -31,3 +31,9 @@ func ECDSASecp256r1CheckMultisig(msg []byte, pubs []interop.PublicKey, sigs []in func ECDSASecp256k1CheckMultisig(msg []byte, pubs []interop.PublicKey, sigs []interop.Signature) bool { return neogointernal.Syscall3("Neo.Crypto.CheckMultisigWithECDsaSecp256k1", msg, pubs, sigs).(bool) } + +// CheckSig checks that sig is correct script-container's signature for a given pub +// (serialized public key). It uses `Neo.Crypto.CheckSig` syscall. +func CheckSig(pub interop.PublicKey, sig interop.Signature) bool { + return neogointernal.Syscall2("Neo.Crypto.CheckSig", pub, sig).(bool) +}