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 }