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  }