github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/core/interop/crypto/ecdsa_test.go (about) 1 package crypto 2 3 import ( 4 "encoding/binary" 5 "fmt" 6 "testing" 7 8 "github.com/nspcc-dev/neo-go/internal/fakechain" 9 "github.com/nspcc-dev/neo-go/pkg/config/netmode" 10 "github.com/nspcc-dev/neo-go/pkg/core/dao" 11 "github.com/nspcc-dev/neo-go/pkg/core/fee" 12 "github.com/nspcc-dev/neo-go/pkg/core/interop" 13 "github.com/nspcc-dev/neo-go/pkg/core/native" 14 "github.com/nspcc-dev/neo-go/pkg/core/storage" 15 "github.com/nspcc-dev/neo-go/pkg/core/transaction" 16 "github.com/nspcc-dev/neo-go/pkg/crypto/hash" 17 "github.com/nspcc-dev/neo-go/pkg/crypto/keys" 18 "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" 19 "github.com/nspcc-dev/neo-go/pkg/util" 20 "github.com/nspcc-dev/neo-go/pkg/vm" 21 "github.com/nspcc-dev/neo-go/pkg/vm/opcode" 22 "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" 23 "github.com/stretchr/testify/assert" 24 "github.com/stretchr/testify/require" 25 ) 26 27 func initCHECKMULTISIG(msgHash util.Uint256, n int) ([]stackitem.Item, []stackitem.Item, map[string]*keys.PublicKey, error) { 28 var err error 29 30 keyMap := make(map[string]*keys.PublicKey) 31 pkeys := make([]*keys.PrivateKey, n) 32 pubs := make([]stackitem.Item, n) 33 for i := range pubs { 34 pkeys[i], err = keys.NewPrivateKey() 35 if err != nil { 36 return nil, nil, nil, err 37 } 38 39 pk := pkeys[i].PublicKey() 40 data := pk.Bytes() 41 pubs[i] = stackitem.NewByteArray(data) 42 keyMap[string(data)] = pk 43 } 44 45 sigs := make([]stackitem.Item, n) 46 for i := range sigs { 47 sig := pkeys[i].SignHash(msgHash) 48 sigs[i] = stackitem.NewByteArray(sig) 49 } 50 51 return pubs, sigs, keyMap, nil 52 } 53 54 func subSlice(arr []stackitem.Item, indices []int) []stackitem.Item { 55 if indices == nil { 56 return arr 57 } 58 59 result := make([]stackitem.Item, len(indices)) 60 for i, j := range indices { 61 result[i] = arr[j] 62 } 63 64 return result 65 } 66 67 func initCheckMultisigVMNoArgs(container *transaction.Transaction) *vm.VM { 68 buf := make([]byte, 5) 69 buf[0] = byte(opcode.SYSCALL) 70 binary.LittleEndian.PutUint32(buf[1:], neoCryptoCheckMultisigID) 71 72 ic := interop.NewContext( 73 trigger.Verification, 74 fakechain.NewFakeChain(), 75 dao.NewSimple(storage.NewMemoryStore(), false), 76 interop.DefaultBaseExecFee, native.DefaultStoragePrice, nil, nil, nil, nil, 77 container, 78 nil) 79 ic.Container = container 80 ic.Functions = Interops 81 v := ic.SpawnVM() 82 v.LoadScript(buf) 83 return v 84 } 85 86 func initCHECKMULTISIGVM(t *testing.T, n int, ik, is []int) *vm.VM { 87 tx := transaction.New([]byte("NEO - An Open Network For Smart Economy"), 10) 88 tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}} 89 tx.Scripts = []transaction.Witness{{}} 90 91 v := initCheckMultisigVMNoArgs(tx) 92 93 pubs, sigs, _, err := initCHECKMULTISIG(hash.NetSha256(uint32(netmode.UnitTestNet), tx), n) 94 require.NoError(t, err) 95 96 pubs = subSlice(pubs, ik) 97 sigs = subSlice(sigs, is) 98 99 v.Estack().PushVal(sigs) 100 v.Estack().PushVal(pubs) 101 102 return v 103 } 104 105 func testCHECKMULTISIGGood(t *testing.T, n int, is []int) { 106 v := initCHECKMULTISIGVM(t, n, nil, is) 107 108 require.NoError(t, v.Run()) 109 assert.Equal(t, 1, v.Estack().Len()) 110 assert.True(t, v.Estack().Pop().Bool()) 111 } 112 113 func TestECDSASecp256r1CheckMultisigGood(t *testing.T) { 114 testCurveCHECKMULTISIGGood(t) 115 } 116 117 func testCurveCHECKMULTISIGGood(t *testing.T) { 118 t.Run("3_1", func(t *testing.T) { testCHECKMULTISIGGood(t, 3, []int{1}) }) 119 t.Run("2_2", func(t *testing.T) { testCHECKMULTISIGGood(t, 2, []int{0, 1}) }) 120 t.Run("3_3", func(t *testing.T) { testCHECKMULTISIGGood(t, 3, []int{0, 1, 2}) }) 121 t.Run("3_2", func(t *testing.T) { testCHECKMULTISIGGood(t, 3, []int{0, 2}) }) 122 t.Run("4_2", func(t *testing.T) { testCHECKMULTISIGGood(t, 4, []int{0, 2}) }) 123 t.Run("10_7", func(t *testing.T) { testCHECKMULTISIGGood(t, 10, []int{2, 3, 4, 5, 6, 8, 9}) }) 124 t.Run("12_9", func(t *testing.T) { testCHECKMULTISIGGood(t, 12, []int{0, 1, 4, 5, 6, 7, 8, 9}) }) 125 } 126 127 func testCHECKMULTISIGBad(t *testing.T, isErr bool, n int, ik, is []int) { 128 v := initCHECKMULTISIGVM(t, n, ik, is) 129 130 if isErr { 131 require.Error(t, v.Run()) 132 return 133 } 134 require.NoError(t, v.Run()) 135 assert.Equal(t, 1, v.Estack().Len()) 136 assert.False(t, v.Estack().Pop().Bool()) 137 } 138 139 func TestECDSASecp256r1CheckMultisigBad(t *testing.T) { 140 testCurveCHECKMULTISIGBad(t) 141 } 142 143 func testCurveCHECKMULTISIGBad(t *testing.T) { 144 t.Run("1_1 wrong signature", func(t *testing.T) { testCHECKMULTISIGBad(t, false, 2, []int{0}, []int{1}) }) 145 t.Run("3_2 wrong order", func(t *testing.T) { testCHECKMULTISIGBad(t, false, 3, []int{0, 2}, []int{2, 0}) }) 146 t.Run("3_2 duplicate sig", func(t *testing.T) { testCHECKMULTISIGBad(t, false, 3, nil, []int{0, 0}) }) 147 t.Run("1_2 too many signatures", func(t *testing.T) { testCHECKMULTISIGBad(t, true, 2, []int{0}, []int{0, 1}) }) 148 t.Run("gas limit exceeded", func(t *testing.T) { 149 v := initCHECKMULTISIGVM(t, 1, []int{0}, []int{0}) 150 v.GasLimit = fee.ECDSAVerifyPrice - 1 151 require.Error(t, v.Run()) 152 }) 153 154 msg := []byte("NEO - An Open Network For Smart Economy") 155 pubs, sigs, _, err := initCHECKMULTISIG(hash.Sha256(msg), 1) 156 require.NoError(t, err) 157 arr := stackitem.NewArray([]stackitem.Item{stackitem.NewArray(nil)}) 158 tx := transaction.New([]byte("NEO - An Open Network For Smart Economy"), 10) 159 tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}} 160 tx.Scripts = []transaction.Witness{{}} 161 162 t.Run("invalid public keys", func(t *testing.T) { 163 v := initCheckMultisigVMNoArgs(tx) 164 v.Estack().PushVal(sigs) 165 v.Estack().PushVal(arr) 166 require.Error(t, v.Run()) 167 }) 168 t.Run("invalid signatures", func(t *testing.T) { 169 v := initCheckMultisigVMNoArgs(tx) 170 v.Estack().PushVal(arr) 171 v.Estack().PushVal(pubs) 172 require.Error(t, v.Run()) 173 }) 174 } 175 176 func TestCheckSig(t *testing.T) { 177 priv, err := keys.NewPrivateKey() 178 require.NoError(t, err) 179 180 verifyFunc := ECDSASecp256r1CheckSig 181 d := dao.NewSimple(storage.NewMemoryStore(), false) 182 ic := &interop.Context{Network: uint32(netmode.UnitTestNet), DAO: d} 183 runCase := func(t *testing.T, isErr bool, result any, args ...any) { 184 ic.SpawnVM() 185 for i := range args { 186 ic.VM.Estack().PushVal(args[i]) 187 } 188 189 var err error 190 func() { 191 defer func() { 192 if r := recover(); r != nil { 193 err = fmt.Errorf("panic: %v", r) 194 } 195 }() 196 err = verifyFunc(ic) 197 }() 198 199 if isErr { 200 require.Error(t, err) 201 return 202 } 203 require.NoError(t, err) 204 require.Equal(t, 1, ic.VM.Estack().Len()) 205 require.Equal(t, result, ic.VM.Estack().Pop().Value().(bool)) 206 } 207 208 tx := transaction.New([]byte{0, 1, 2}, 1) 209 ic.Container = tx 210 211 t.Run("success", func(t *testing.T) { 212 sign := priv.SignHashable(uint32(netmode.UnitTestNet), tx) 213 runCase(t, false, true, sign, priv.PublicKey().Bytes()) 214 }) 215 216 t.Run("missing argument", func(t *testing.T) { 217 runCase(t, true, false) 218 sign := priv.SignHashable(uint32(netmode.UnitTestNet), tx) 219 runCase(t, true, false, sign) 220 }) 221 222 t.Run("invalid signature", func(t *testing.T) { 223 sign := priv.SignHashable(uint32(netmode.UnitTestNet), tx) 224 sign[0] = ^sign[0] 225 runCase(t, false, false, sign, priv.PublicKey().Bytes()) 226 }) 227 228 t.Run("invalid public key", func(t *testing.T) { 229 sign := priv.SignHashable(uint32(netmode.UnitTestNet), tx) 230 pub := priv.PublicKey().Bytes() 231 pub[0] = 0xFF // invalid prefix 232 runCase(t, true, false, sign, pub) 233 }) 234 }