github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/core/native/crypto_test.go (about)

     1  package native
     2  
     3  import (
     4  	"encoding/binary"
     5  	"encoding/hex"
     6  	"math"
     7  	"math/big"
     8  	"testing"
     9  
    10  	"github.com/consensys/gnark-crypto/ecc/bls12-381/fr"
    11  	"github.com/nspcc-dev/neo-go/pkg/core/interop"
    12  	"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
    13  	"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
    14  	"github.com/nspcc-dev/neo-go/pkg/vm"
    15  	"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
    16  	"github.com/stretchr/testify/require"
    17  )
    18  
    19  func TestSha256(t *testing.T) {
    20  	c := newCrypto()
    21  	ic := &interop.Context{VM: vm.New()}
    22  
    23  	t.Run("bad arg type", func(t *testing.T) {
    24  		require.Panics(t, func() {
    25  			c.sha256(ic, []stackitem.Item{stackitem.NewInterop(nil)})
    26  		})
    27  	})
    28  	t.Run("good", func(t *testing.T) {
    29  		// 0x0100 hashes to 47dc540c94ceb704a23875c11273e16bb0b8a87aed84de911f2133568115f254
    30  		require.Equal(t, "47dc540c94ceb704a23875c11273e16bb0b8a87aed84de911f2133568115f254", hex.EncodeToString(c.sha256(ic, []stackitem.Item{stackitem.NewByteArray([]byte{1, 0})}).Value().([]byte)))
    31  	})
    32  }
    33  
    34  // TestKeccak256_Compat is a C# node compatibility test with data taken from https://github.com/Jim8y/neo/blob/560d35783e428d31e3681eaa7ee9ed00a8a50d09/tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs#L340
    35  func TestKeccak256_Compat(t *testing.T) {
    36  	c := newCrypto()
    37  	ic := &interop.Context{VM: vm.New()}
    38  
    39  	t.Run("good", func(t *testing.T) {
    40  		testCases := []struct {
    41  			name         string
    42  			input        []byte
    43  			expectedHash string
    44  		}{
    45  			{"good", []byte{1, 0}, "628bf3596747d233f1e6533345700066bf458fa48daedaf04a7be6c392902476"},
    46  			{"hello world", []byte("Hello, World!"), "acaf3289d7b601cbd114fb36c4d29c85bbfd5e133f14cb355c3fd8d99367964f"},
    47  			{"keccak", []byte("Keccak"), "868c016b666c7d3698636ee1bd023f3f065621514ab61bf26f062c175fdbe7f2"},
    48  			{"cryptography", []byte("Cryptography"), "53d49d225dd2cfe77d8c5e2112bcc9efe77bea1c7aa5e5ede5798a36e99e2d29"},
    49  			{"testing123", []byte("Testing123"), "3f82db7b16b0818a1c6b2c6152e265f682d5ebcf497c9aad776ad38bc39cb6ca"},
    50  			{"long string", []byte("This is a longer string for Keccak256 testing purposes."), "24115e5c2359f85f6840b42acd2f7ea47bc239583e576d766fa173bf711bdd2f"},
    51  			{"blank string", []byte(""), "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"},
    52  		}
    53  		for _, tc := range testCases {
    54  			t.Run(tc.name, func(t *testing.T) {
    55  				result := c.keccak256(ic, []stackitem.Item{stackitem.NewByteArray(tc.input)}).Value().([]byte)
    56  				outputHashHex := hex.EncodeToString(result)
    57  				require.Equal(t, tc.expectedHash, outputHashHex)
    58  			})
    59  		}
    60  	})
    61  	t.Run("errors", func(t *testing.T) {
    62  		errCases := []struct {
    63  			name string
    64  			item stackitem.Item
    65  		}{
    66  			{
    67  				name: "Null item",
    68  				item: stackitem.Null{},
    69  			},
    70  			{
    71  				name: "not a byte array",
    72  				item: stackitem.NewArray([]stackitem.Item{stackitem.NewBool(true)}),
    73  			},
    74  		}
    75  
    76  		for _, tc := range errCases {
    77  			t.Run(tc.name, func(t *testing.T) {
    78  				require.Panics(t, func() {
    79  					_ = c.keccak256(ic, []stackitem.Item{tc.item})
    80  				}, "keccak256 should panic with incorrect argument types")
    81  			})
    82  		}
    83  	})
    84  }
    85  
    86  func TestRIPEMD160(t *testing.T) {
    87  	c := newCrypto()
    88  	ic := &interop.Context{VM: vm.New()}
    89  
    90  	t.Run("bad arg type", func(t *testing.T) {
    91  		require.Panics(t, func() {
    92  			c.ripemd160(ic, []stackitem.Item{stackitem.NewInterop(nil)})
    93  		})
    94  	})
    95  	t.Run("good", func(t *testing.T) {
    96  		// 0x0100 hashes to 213492c0c6fc5d61497cf17249dd31cd9964b8a3
    97  		require.Equal(t, "213492c0c6fc5d61497cf17249dd31cd9964b8a3", hex.EncodeToString(c.ripemd160(ic, []stackitem.Item{stackitem.NewByteArray([]byte{1, 0})}).Value().([]byte)))
    98  	})
    99  }
   100  
   101  func TestMurmur32(t *testing.T) {
   102  	c := newCrypto()
   103  	ic := &interop.Context{VM: vm.New()}
   104  
   105  	t.Run("bad arg type", func(t *testing.T) {
   106  		require.Panics(t, func() {
   107  			c.murmur32(ic, []stackitem.Item{stackitem.NewInterop(nil), stackitem.Make(5)})
   108  		})
   109  	})
   110  	t.Run("good", func(t *testing.T) {
   111  		// Example from the C# node:
   112  		// https://github.com/neo-project/neo/blob/2a64c1cc809d1ff4b3a573c7c22bffbbf69a738b/tests/neo.UnitTests/Cryptography/UT_Murmur32.cs#L18
   113  		data := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1}
   114  		seed := 10
   115  		expected := make([]byte, 4)
   116  		binary.LittleEndian.PutUint32(expected, 378574820)
   117  		require.Equal(t, expected, c.murmur32(ic, []stackitem.Item{stackitem.NewByteArray(data), stackitem.Make(seed)}).Value().([]byte))
   118  	})
   119  }
   120  
   121  func TestCryptoLibVerifyWithECDsa(t *testing.T) {
   122  	t.Run("R1 sha256", func(t *testing.T) {
   123  		testECDSAVerify(t, Secp256r1Sha256)
   124  	})
   125  	t.Run("K1 sha256", func(t *testing.T) {
   126  		testECDSAVerify(t, Secp256k1Sha256)
   127  	})
   128  	t.Run("R1 keccak256", func(t *testing.T) {
   129  		testECDSAVerify(t, Secp256r1Keccak256)
   130  	})
   131  	t.Run("K1 keccak256", func(t *testing.T) {
   132  		testECDSAVerify(t, Secp256k1Keccak256)
   133  	})
   134  }
   135  
   136  func testECDSAVerify(t *testing.T, curve NamedCurveHash) {
   137  	var (
   138  		priv   *keys.PrivateKey
   139  		err    error
   140  		c      = newCrypto()
   141  		ic     = &interop.Context{VM: vm.New()}
   142  		actual stackitem.Item
   143  		hasher HashFunc
   144  	)
   145  	switch curve {
   146  	case Secp256k1Sha256:
   147  		priv, err = keys.NewSecp256k1PrivateKey()
   148  		hasher = hash.Sha256
   149  	case Secp256r1Sha256:
   150  		priv, err = keys.NewPrivateKey()
   151  		hasher = hash.Sha256
   152  	case Secp256k1Keccak256:
   153  		priv, err = keys.NewSecp256k1PrivateKey()
   154  		hasher = Keccak256
   155  	case Secp256r1Keccak256:
   156  		priv, err = keys.NewPrivateKey()
   157  		hasher = Keccak256
   158  	default:
   159  		t.Fatal("unknown curve/hash")
   160  	}
   161  	require.NoError(t, err)
   162  
   163  	runCase := func(t *testing.T, isErr bool, result any, args ...any) {
   164  		argsArr := make([]stackitem.Item, len(args))
   165  		for i := range args {
   166  			argsArr[i] = stackitem.Make(args[i])
   167  		}
   168  		if isErr {
   169  			require.Panics(t, func() {
   170  				_ = c.verifyWithECDsa(ic, argsArr)
   171  			})
   172  		} else {
   173  			require.NotPanics(t, func() {
   174  				actual = c.verifyWithECDsa(ic, argsArr)
   175  			})
   176  			require.Equal(t, stackitem.Make(result), actual)
   177  		}
   178  	}
   179  
   180  	msg := []byte("test message")
   181  	sign := priv.SignHash(hasher(msg))
   182  
   183  	t.Run("bad message item", func(t *testing.T) {
   184  		runCase(t, true, false, stackitem.NewInterop("cheburek"), priv.PublicKey().Bytes(), sign, int64(curve))
   185  	})
   186  	t.Run("bad pubkey item", func(t *testing.T) {
   187  		runCase(t, true, false, msg, stackitem.NewInterop("cheburek"), sign, int64(curve))
   188  	})
   189  	t.Run("bad pubkey bytes", func(t *testing.T) {
   190  		runCase(t, true, false, msg, []byte{1, 2, 3}, sign, int64(curve))
   191  	})
   192  	t.Run("bad signature item", func(t *testing.T) {
   193  		runCase(t, true, false, msg, priv.PublicKey().Bytes(), stackitem.NewInterop("cheburek"), int64(curve))
   194  	})
   195  	t.Run("bad curve item", func(t *testing.T) {
   196  		runCase(t, true, false, msg, priv.PublicKey().Bytes(), sign, stackitem.NewInterop("cheburek"))
   197  	})
   198  	t.Run("bad curve value", func(t *testing.T) {
   199  		runCase(t, true, false, msg, priv.PublicKey().Bytes(), sign, new(big.Int).Add(big.NewInt(math.MaxInt64), big.NewInt(1)))
   200  	})
   201  	t.Run("unknown curve", func(t *testing.T) {
   202  		runCase(t, true, false, msg, priv.PublicKey().Bytes(), sign, int64(124))
   203  	})
   204  	t.Run("invalid signature", func(t *testing.T) {
   205  		s := priv.Sign(msg)
   206  		s[0] = ^s[0]
   207  		runCase(t, false, false, s, priv.PublicKey().Bytes(), msg, int64(curve))
   208  	})
   209  	t.Run("success", func(t *testing.T) {
   210  		runCase(t, false, true, msg, priv.PublicKey().Bytes(), sign, int64(curve))
   211  	})
   212  }
   213  
   214  func TestCryptolib_ScalarFromBytes_Compat(t *testing.T) {
   215  	r2Ref := &fr.Element{
   216  		0xc999_e990_f3f2_9c6d,
   217  		0x2b6c_edcb_8792_5c23,
   218  		0x05d3_1496_7254_398f,
   219  		0x0748_d9d9_9f59_ff11,
   220  	} // R2 Scalar representation taken from the https://github.com/neo-project/Neo.Cryptography.BLS12_381/blob/844bc3a4f7d8ba2c545ace90ca124f8ada4c8d29/src/Neo.Cryptography.BLS12_381/ScalarConstants.cs#L55
   221  
   222  	tcs := map[string]struct {
   223  		bytes      []byte
   224  		expected   *fr.Element
   225  		shouldFail bool
   226  	}{
   227  		"zero": {
   228  			bytes:    []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
   229  			expected: new(fr.Element).SetZero(),
   230  		},
   231  		"one": {
   232  			bytes:    []byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
   233  			expected: new(fr.Element).SetOne(),
   234  		},
   235  		"R2": {
   236  			bytes:    []byte{254, 255, 255, 255, 1, 0, 0, 0, 2, 72, 3, 0, 250, 183, 132, 88, 245, 79, 188, 236, 239, 79, 140, 153, 111, 5, 197, 172, 89, 177, 36, 24},
   237  			expected: r2Ref,
   238  		},
   239  		"negative": {
   240  			bytes: []byte{0, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115},
   241  		},
   242  		"modulus": {
   243  			bytes:      []byte{1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115},
   244  			shouldFail: true,
   245  		},
   246  		"larger than modulus": {
   247  			bytes:      []byte{2, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115},
   248  			shouldFail: true,
   249  		},
   250  		"larger than modulus 2": {
   251  			bytes:      []byte{1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, 216, 58, 51, 72, 125, 157, 41, 83, 167, 237, 115},
   252  			shouldFail: true,
   253  		},
   254  		"larger than modulus 3": {
   255  			bytes:      []byte{1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 116},
   256  			shouldFail: true,
   257  		},
   258  	}
   259  	for name, tc := range tcs {
   260  		t.Run(name, func(t *testing.T) {
   261  			actual, err := scalarFromBytes(tc.bytes, false)
   262  			if tc.shouldFail {
   263  				require.Error(t, err)
   264  			} else {
   265  				require.NoError(t, err)
   266  				if tc.expected != nil {
   267  					require.Equal(t, tc.expected, actual)
   268  				}
   269  			}
   270  		})
   271  	}
   272  }
   273  
   274  func TestKeccak256(t *testing.T) {
   275  	input := []byte("hello")
   276  	data := Keccak256(input)
   277  
   278  	expected := "1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8"
   279  	actual := hex.EncodeToString(data.BytesBE())
   280  
   281  	require.Equal(t, expected, actual)
   282  }