From 5a30af2c75d39a8d3e21a4c06458b2d04846873d Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Wed, 18 Mar 2020 12:41:09 +0300 Subject: [PATCH] core: implement Neo.Crypto.ECDsaVerify interop --- pkg/core/interop/crypto/ecdsa.go | 31 ++++++++++++++++ pkg/core/interop_neo_test.go | 63 ++++++++++++++++++++++++++++++++ pkg/core/interops.go | 2 + 3 files changed, 96 insertions(+) create mode 100644 pkg/core/interop/crypto/ecdsa.go diff --git a/pkg/core/interop/crypto/ecdsa.go b/pkg/core/interop/crypto/ecdsa.go new file mode 100644 index 000000000..ab7f5b6b5 --- /dev/null +++ b/pkg/core/interop/crypto/ecdsa.go @@ -0,0 +1,31 @@ +package crypto + +import ( + "github.com/nspcc-dev/neo-go/pkg/core/interop" + "github.com/nspcc-dev/neo-go/pkg/crypto/hash" + "github.com/nspcc-dev/neo-go/pkg/crypto/keys" + "github.com/nspcc-dev/neo-go/pkg/vm" +) + +// ECDSAVerify checks ECDSA signature. +func ECDSAVerify(ic *interop.Context, v *vm.VM) error { + msg := getMessage(ic, v.Estack().Pop().Item()) + hashToCheck := hash.Sha256(msg).BytesBE() + keyb := v.Estack().Pop().Bytes() + signature := v.Estack().Pop().Bytes() + pkey, err := keys.NewPublicKeyFromBytes(keyb) + if err != nil { + return err + } + res := pkey.Verify(signature, hashToCheck) + v.Estack().PushVal(res) + return nil +} + +func getMessage(_ *interop.Context, item vm.StackItem) []byte { + msg, err := item.TryBytes() + if err != nil { + panic(err) + } + return msg +} diff --git a/pkg/core/interop_neo_test.go b/pkg/core/interop_neo_test.go index 26068690f..7253edb17 100644 --- a/pkg/core/interop_neo_test.go +++ b/pkg/core/interop_neo_test.go @@ -1,12 +1,14 @@ package core import ( + "fmt" "math/big" "testing" "github.com/nspcc-dev/neo-go/pkg/core/block" "github.com/nspcc-dev/neo-go/pkg/core/dao" "github.com/nspcc-dev/neo-go/pkg/core/interop" + "github.com/nspcc-dev/neo-go/pkg/core/interop/crypto" "github.com/nspcc-dev/neo-go/pkg/core/interop/enumerator" "github.com/nspcc-dev/neo-go/pkg/core/interop/iterator" "github.com/nspcc-dev/neo-go/pkg/core/state" @@ -231,6 +233,67 @@ func TestWitnessGetVerificationScript(t *testing.T) { require.Equal(t, witness.VerificationScript, value) } +func TestECDSAVerify(t *testing.T) { + priv, err := keys.NewPrivateKey() + require.NoError(t, err) + + chain := newTestChain(t) + defer chain.Close() + + ic := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore()), nil, nil) + runCase := func(t *testing.T, isErr bool, result interface{}, args ...interface{}) { + v := vm.New() + for i := range args { + v.Estack().PushVal(args[i]) + } + + var err error + func() { + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("panic: %v", r) + } + }() + err = crypto.ECDSAVerify(ic, v) + }() + + if isErr { + require.Error(t, err) + return + } + require.NoError(t, err) + require.Equal(t, 1, v.Estack().Len()) + require.Equal(t, result, v.Estack().Pop().Value().(bool)) + } + + msg := []byte("test message") + + t.Run("success", func(t *testing.T) { + sign := priv.Sign(msg) + runCase(t, false, true, sign, priv.PublicKey().Bytes(), msg) + }) + + t.Run("missing arguments", func(t *testing.T) { + runCase(t, true, false) + sign := priv.Sign(msg) + runCase(t, true, false, sign) + runCase(t, true, false, sign, priv.PublicKey().Bytes()) + }) + + t.Run("invalid signature", func(t *testing.T) { + sign := priv.Sign(msg) + sign[0] ^= sign[0] + runCase(t, false, false, sign, priv.PublicKey().Bytes(), msg) + }) + + t.Run("invalid public key", func(t *testing.T) { + sign := priv.Sign(msg) + pub := priv.PublicKey().Bytes() + pub = pub[10:] + runCase(t, true, false, sign, pub, msg) + }) +} + func TestPopInputFromVM(t *testing.T) { v, tx, _, chain := createVMAndTX(t) defer chain.Close() diff --git a/pkg/core/interops.go b/pkg/core/interops.go index edcdb3310..751035f7d 100644 --- a/pkg/core/interops.go +++ b/pkg/core/interops.go @@ -11,6 +11,7 @@ import ( "sort" "github.com/nspcc-dev/neo-go/pkg/core/interop" + "github.com/nspcc-dev/neo-go/pkg/core/interop/crypto" "github.com/nspcc-dev/neo-go/pkg/core/interop/enumerator" "github.com/nspcc-dev/neo-go/pkg/core/interop/iterator" "github.com/nspcc-dev/neo-go/pkg/smartcontract" @@ -137,6 +138,7 @@ var neoInterops = []interop.Function{ {Name: "Neo.Contract.GetStorageContext", Func: contractGetStorageContext, Price: 1}, {Name: "Neo.Contract.IsPayable", Func: contractIsPayable, Price: 1}, {Name: "Neo.Contract.Migrate", Func: contractMigrate, Price: 0}, + {Name: "Neo.Crypto.ECDsaVerify", Func: crypto.ECDSAVerify, Price: 1}, {Name: "Neo.Enumerator.Concat", Func: enumerator.Concat, Price: 1}, {Name: "Neo.Enumerator.Create", Func: enumerator.Create, Price: 1}, {Name: "Neo.Enumerator.Next", Func: enumerator.Next, Price: 1},