diff --git a/lib/runtime/constants.go b/lib/runtime/constants.go index fb63ab3917..db4c7d96dc 100644 --- a/lib/runtime/constants.go +++ b/lib/runtime/constants.go @@ -47,7 +47,7 @@ const ( // v0.8 test API wasm HOST_API_TEST_RUNTIME = "hostapi_runtime" HOST_API_TEST_RUNTIME_FP = "hostapi_runtime.compact.wasm" - HOST_API_TEST_RUNTIME_URL = "https://github.com/ChainSafe/polkadot-spec/blob/f9f8c94397d155c4f2edc9c59828dc4ef2c62dd3/test/hostapi_runtime.compact.wasm?raw=true" + HOST_API_TEST_RUNTIME_URL = "https://github.com/ChainSafe/polkadot-spec/blob/80fa2be272820731b5159e9dc2a3eec3cca02b4d/test/hostapi_runtime.compact.wasm?raw=true" // v0.8 substrate runtime with modified name and babe C=(1, 1) DEV_RUNTIME = "dev_runtime" diff --git a/lib/runtime/wasmer/imports.go b/lib/runtime/wasmer/imports.go index 584d5d26ba..69496b3c32 100644 --- a/lib/runtime/wasmer/imports.go +++ b/lib/runtime/wasmer/imports.go @@ -38,6 +38,7 @@ package wasmer // extern int64_t ext_crypto_secp256k1_ecdsa_recover_version_2(void *context, int32_t a, int32_t b); // extern int64_t ext_crypto_secp256k1_ecdsa_recover_compressed_version_1(void *context, int32_t a, int32_t b); // extern int64_t ext_crypto_secp256k1_ecdsa_recover_compressed_version_2(void *context, int32_t a, int32_t b); +// extern int32_t ext_crypto_ecdsa_verify_version_2(void *context, int32_t a, int64_t b, int32_t c); // extern int32_t ext_crypto_sr25519_generate_version_1(void *context, int32_t a, int64_t b); // extern int64_t ext_crypto_sr25519_public_keys_version_1(void *context, int32_t a); // extern int64_t ext_crypto_sr25519_sign_version_1(void *context, int32_t a, int32_t b, int64_t c); @@ -449,6 +450,56 @@ func ext_crypto_secp256k1_ecdsa_recover_version_2(context unsafe.Pointer, sig, m return ext_crypto_secp256k1_ecdsa_recover_version_1(context, sig, msg) } +//export ext_crypto_ecdsa_verify_version_2 +func ext_crypto_ecdsa_verify_version_2(context unsafe.Pointer, sig C.int32_t, msg C.int64_t, key C.int32_t) C.int32_t { + logger.Trace("executing...") + + instanceContext := wasm.IntoInstanceContext(context) + memory := instanceContext.Memory().Data() + sigVerifier := instanceContext.Data().(*runtime.Context).SigVerifier + + message := asMemorySlice(instanceContext, msg) + signature := memory[sig : sig+64] + pubKey := memory[key : key+33] + + pub := new(secp256k1.PublicKey) + err := pub.Decode(pubKey) + if err != nil { + logger.Error("failed to decode public key", "error", err) + return C.int32_t(0) + } + + logger.Debug("", "pub", pub.Hex(), + "message", fmt.Sprintf("0x%x", message), + "signature", fmt.Sprintf("0x%x", signature), + ) + + hash, err := common.Blake2bHash(message) + if err != nil { + logger.Error("failed to hash message", "error", err) + return C.int32_t(0) + } + + if sigVerifier.IsStarted() { + signature := runtime.Signature{ + PubKey: pub.Encode(), + Sign: signature, + Msg: hash[:], + KeyTypeID: crypto.Secp256k1Type, + } + sigVerifier.Add(&signature) + return C.int32_t(1) + } + + if ok, err := pub.Verify(hash[:], signature); err != nil || !ok { + logger.Error("failed to validate signature", "error", err) + return C.int32_t(0) + } + + logger.Debug("validated signature") + return C.int32_t(1) +} + //export ext_crypto_secp256k1_ecdsa_recover_compressed_version_1 func ext_crypto_secp256k1_ecdsa_recover_compressed_version_1(context unsafe.Pointer, sig, msg C.int32_t) C.int64_t { logger.Trace("[ext_crypto_secp256k1_ecdsa_recover_compressed_version_1] executing...") @@ -2102,6 +2153,10 @@ func ImportsNodeRuntime() (*wasm.Imports, error) { //nolint if err != nil { return nil, err } + _, err = imports.Append("ext_crypto_ecdsa_verify_version_2", ext_crypto_ecdsa_verify_version_2, C.ext_crypto_ecdsa_verify_version_2) + if err != nil { + return nil, err + } _, err = imports.Append("ext_crypto_start_batch_verify_version_1", ext_crypto_start_batch_verify_version_1, C.ext_crypto_start_batch_verify_version_1) if err != nil { return nil, err diff --git a/lib/runtime/wasmer/imports_test.go b/lib/runtime/wasmer/imports_test.go index 831f309dee..865dfce136 100644 --- a/lib/runtime/wasmer/imports_test.go +++ b/lib/runtime/wasmer/imports_test.go @@ -36,7 +36,9 @@ import ( "github.com/ChainSafe/gossamer/lib/trie" "github.com/ChainSafe/gossamer/pkg/scale" log "github.com/ChainSafe/log15" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/wasmerio/go-ext-wasm/wasmer" ) var testChildKey = []byte("childKey") @@ -629,6 +631,99 @@ func Test_ext_crypto_ed25519_verify_version_1(t *testing.T) { require.True(t, read.Exists()) } +func Test_ext_crypto_ecdsa_verify_version_2(t *testing.T) { + t.Parallel() + + inst := NewTestInstance(t, runtime.HOST_API_TEST_RUNTIME) + + kp, err := secp256k1.GenerateKeypair() + require.NoError(t, err) + + pubKeyData := kp.Public().Encode() + encPubKey, err := scale.Marshal(pubKeyData) + require.NoError(t, err) + + msgData := []byte("Hello world!") + encMsg, err := scale.Marshal(msgData) + require.NoError(t, err) + + msgHash, err := common.Blake2bHash(msgData) + require.NoError(t, err) + + sig, err := kp.Private().Sign(msgHash[:]) + require.NoError(t, err) + + encSig, err := scale.Marshal(sig) + require.NoError(t, err) + + ret, err := inst.Exec("rtm_ext_crypto_ecdsa_verify_version_2", append(append(encSig, encMsg...), encPubKey...)) + require.NoError(t, err) + + buf := bytes.NewBuffer(ret) + + read, err := new(optional.Bytes).Decode(buf) + require.NoError(t, err) + + require.True(t, read.Exists()) +} + +func Test_ext_crypto_ecdsa_verify_version_2_Table(t *testing.T) { + testCases := map[string]struct { + sig []byte + msg []byte + key []byte + expected []byte + err error + }{ + "valid signature": { + sig: []byte{5, 1, 187, 179, 88, 183, 46, 115, 242, 32, 9, 54, 141, 207, 44, 15, 238, 42, 217, 196, 111, 173, 239, 204, 128, 93, 49, 179, 137, 150, 162, 125, 226, 225, 28, 145, 122, 127, 15, 154, 185, 11, 3, 66, 27, 187, 204, 242, 107, 68, 26, 111, 245, 30, 115, 141, 85, 74, 158, 211, 161, 217, 43, 151, 120, 125, 1}, + msg: []byte{48, 72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33}, + key: []byte{132, 2, 39, 206, 55, 134, 131, 142, 43, 100, 63, 134, 96, 14, 253, 15, 222, 119, 154, 110, 188, 20, 159, 62, 125, 42, 59, 127, 19, 16, 0, 161, 236, 109}, + expected: []byte{1, 0, 0, 0}, + }, + "invalid signature": { + sig: []byte{5, 1, 187, 0, 0, 183, 46, 115, 242, 32, 9, 54, 141, 207, 44, 15, 238, 42, 217, 196, 111, 173, 239, 204, 128, 93, 49, 179, 137, 150, 162, 125, 226, 225, 28, 145, 122, 127, 15, 154, 185, 11, 3, 66, 27, 187, 204, 242, 107, 68, 26, 111, 245, 30, 115, 141, 85, 74, 158, 211, 161, 217, 43, 151, 120, 125, 1}, + msg: []byte{48, 72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33}, + key: []byte{132, 2, 39, 206, 55, 134, 131, 142, 43, 100, 63, 134, 96, 14, 253, 15, 222, 119, 154, 110, 188, 20, 159, 62, 125, 42, 59, 127, 19, 16, 0, 161, 236, 109}, + expected: []byte{0, 0, 0, 0}, + }, + "wrong key": { + sig: []byte{5, 1, 187, 0, 0, 183, 46, 115, 242, 32, 9, 54, 141, 207, 44, 15, 238, 42, 217, 196, 111, 173, 239, 204, 128, 93, 49, 179, 137, 150, 162, 125, 226, 225, 28, 145, 122, 127, 15, 154, 185, 11, 3, 66, 27, 187, 204, 242, 107, 68, 26, 111, 245, 30, 115, 141, 85, 74, 158, 211, 161, 217, 43, 151, 120, 125, 1}, + msg: []byte{48, 72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33}, + key: []byte{132, 2, 39, 0, 55, 134, 131, 142, 43, 100, 63, 134, 96, 14, 253, 15, 222, 119, 154, 110, 188, 20, 159, 62, 125, 42, 59, 127, 19, 16, 0, 161, 236, 109}, + expected: []byte{0, 0, 0, 0}, + }, + "invalid key": { + sig: []byte{5, 1, 187, 0, 0, 183, 46, 115, 242, 32, 9, 54, 141, 207, 44, 15, 238, 42, 217, 196, 111, 173, 239, 204, 128, 93, 49, 179, 137, 150, 162, 125, 226, 225, 28, 145, 122, 127, 15, 154, 185, 11, 3, 66, 27, 187, 204, 242, 107, 68, 26, 111, 245, 30, 115, 141, 85, 74, 158, 211, 161, 217, 43, 151, 120, 125, 1}, + msg: []byte{48, 72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33}, + key: []byte{132, 2, 39, 55, 134, 131, 142, 43, 100, 63, 134, 96, 14, 253, 15, 222, 119, 154, 110, 188, 20, 159, 62, 125, 42, 59, 127, 19, 16, 0, 161, 236, 109}, + err: wasmer.NewExportedFunctionError("rtm_ext_crypto_ecdsa_verify_version_2", "Failed to call the `%s` exported function."), + }, + "invalid message": { + sig: []byte{5, 1, 187, 179, 88, 183, 46, 115, 242, 32, 9, 54, 141, 207, 44, 15, 238, 42, 217, 196, 111, 173, 239, 204, 128, 93, 49, 179, 137, 150, 162, 125, 226, 225, 28, 145, 122, 127, 15, 154, 185, 11, 3, 66, 27, 187, 204, 242, 107, 68, 26, 111, 245, 30, 115, 141, 85, 74, 158, 211, 161, 217, 43, 151, 120, 125, 1}, + msg: []byte{48, 72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100}, + key: []byte{132, 2, 39, 206, 55, 134, 131, 142, 43, 100, 63, 134, 96, 14, 253, 15, 222, 119, 154, 110, 188, 20, 159, 62, 125, 42, 59, 127, 19, 16, 0, 161, 236, 109}, + err: wasmer.NewExportedFunctionError("rtm_ext_crypto_ecdsa_verify_version_2", "Failed to call the `%s` exported function."), + }, + } + for name, tc := range testCases { + tc := tc + t.Run(name, func(t *testing.T) { + t.Parallel() + + inst := NewTestInstance(t, runtime.HOST_API_TEST_RUNTIME) + + ret, err := inst.Exec("rtm_ext_crypto_ecdsa_verify_version_2", append(append(tc.sig, tc.msg...), tc.key...)) + assert.Equal(t, tc.expected, ret) + if tc.err != nil { + assert.EqualError(t, err, tc.err.Error()) + return + } + assert.NoError(t, err) + }) + } +} + func Test_ext_crypto_sr25519_generate_version_1(t *testing.T) { inst := NewTestInstance(t, runtime.HOST_API_TEST_RUNTIME)