Skip to content

Commit

Permalink
Make signature verification customizable in the host module (#2955)
Browse files Browse the repository at this point in the history
cc @xlc 
After looking at #2952, I
thought that the approach in this PR is a better idea.

As shown in the changes to `runtime_host.rs` and
`read_only_runtime_host.rs`, you now have a new variant called
`SignatureVerification` that allows you to overwrite the verification if
you so desire.

As I was implemented this PR, I noticed that the implementation of
`ext_crypto_ecdsa_verify` loads the message from the first parameter,
which is actually the signature. Oops. This is fixed at the same time.

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
tomaka and mergify[bot] authored Nov 1, 2022
1 parent 65fc2f3 commit 377520f
Show file tree
Hide file tree
Showing 10 changed files with 589 additions and 193 deletions.
3 changes: 3 additions & 0 deletions bin/light-base/src/json_rpc_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1588,6 +1588,9 @@ impl<TPlat: Platform> Background<TPlat> {
read_only_runtime_host::RuntimeHostVm::StorageRoot(storage_root) => {
runtime_call = storage_root.resume(runtime_call_lock.block_storage_root());
}
read_only_runtime_host::RuntimeHostVm::SignatureVerification(sig) => {
runtime_call = sig.verify_and_resume();
}
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions bin/light-base/src/json_rpc_service/chain_head.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,9 @@ impl<TPlat: Platform> Background<TPlat> {
}
.to_json_call_object_parameters(None);
}
runtime_host::RuntimeHostVm::SignatureVerification(sig) => {
runtime_call = sig.verify_and_resume();
}
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions bin/light-base/src/sync_service/parachain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1162,6 +1162,9 @@ async fn parahead<TPlat: Platform>(
read_only_runtime_host::RuntimeHostVm::StorageRoot(storage_root) => {
runtime_call = storage_root.resume(runtime_call_lock.block_storage_root());
}
read_only_runtime_host::RuntimeHostVm::SignatureVerification(sig) => {
runtime_call = sig.verify_and_resume();
}
}
};

Expand Down
4 changes: 4 additions & 0 deletions bin/wasm-node/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### Fixed

- Fix the `ext_crypto_ecdsa_verify_version_1` and `ext_crypto_ecdsa_verify_prehashed_version_1` host functions mixing their parameters and thus always failing. ([#2955](https://github.com/paritytech/smoldot/pull/2955))

## 0.7.5 - 2022-10-31

### Fixed
Expand Down
3 changes: 3 additions & 0 deletions src/chain/chain_information/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,9 @@ impl ChainInformationBuild {
call, inner,
)))
}
read_only_runtime_host::RuntimeHostVm::SignatureVerification(sig) => {
call = sig.verify_and_resume();
}
}
}
}
Expand Down
393 changes: 284 additions & 109 deletions src/executor/host.rs

Large diffs are not rendered by default.

94 changes: 94 additions & 0 deletions src/executor/read_only_runtime_host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ pub enum RuntimeHostVm {
NextKey(NextKey),
/// Fetching the storage trie root is required in order to continue.
StorageRoot(StorageRoot),
/// Verifying whether a signature is correct is required in order to continue.
SignatureVerification(SignatureVerification),
}

impl RuntimeHostVm {
Expand All @@ -130,6 +132,7 @@ impl RuntimeHostVm {
RuntimeHostVm::StorageGet(inner) => inner.inner.vm.into_prototype(),
RuntimeHostVm::NextKey(inner) => inner.inner.vm.into_prototype(),
RuntimeHostVm::StorageRoot(inner) => inner.inner.vm.into_prototype(),
RuntimeHostVm::SignatureVerification(inner) => inner.inner.vm.into_prototype(),
}
}
}
Expand Down Expand Up @@ -247,6 +250,90 @@ impl StorageRoot {
}
}

/// Verifying whether a signature is correct is required in order to continue.
#[must_use]
pub struct SignatureVerification {
inner: Inner,
}

impl SignatureVerification {
/// Returns the message that the signature is expected to sign.
pub fn message(&'_ self) -> impl AsRef<[u8]> + '_ {
match self.inner.vm {
host::HostVm::SignatureVerification(ref sig) => sig.message(),
_ => unreachable!(),
}
}

/// Returns the signature.
///
/// > **Note**: Be aware that this signature is untrusted input and might not be part of the
/// > set of valid signatures.
pub fn signature(&'_ self) -> impl AsRef<[u8]> + '_ {
match self.inner.vm {
host::HostVm::SignatureVerification(ref sig) => sig.signature(),
_ => unreachable!(),
}
}

/// Returns the public key the signature is against.
///
/// > **Note**: Be aware that this public key is untrusted input and might not be part of the
/// > set of valid public keys.
pub fn public_key(&'_ self) -> impl AsRef<[u8]> + '_ {
match self.inner.vm {
host::HostVm::SignatureVerification(ref sig) => sig.public_key(),
_ => unreachable!(),
}
}

/// Verify the signature. Returns `true` if it is valid.
pub fn is_valid(&self) -> bool {
match self.inner.vm {
host::HostVm::SignatureVerification(ref sig) => sig.is_valid(),
_ => unreachable!(),
}
}

/// Verify the signature and resume execution.
pub fn verify_and_resume(mut self) -> RuntimeHostVm {
match self.inner.vm {
host::HostVm::SignatureVerification(sig) => self.inner.vm = sig.verify_and_resume(),
_ => unreachable!(),
}

self.inner.run()
}

/// Resume the execution assuming that the signature is valid.
///
/// > **Note**: You are strongly encouraged to call
/// > [`SignatureVerification::verify_and_resume`]. This function is meant to be
/// > used only in debugging situations.
pub fn resume_success(mut self) -> RuntimeHostVm {
match self.inner.vm {
host::HostVm::SignatureVerification(sig) => self.inner.vm = sig.resume_success(),
_ => unreachable!(),
}

self.inner.run()
}

/// Resume the execution assuming that the signature is invalid.
///
/// > **Note**: You are strongly encouraged to call
/// > [`SignatureVerification::verify_and_resume`]. This function is meant to be
/// > used only in debugging situations.
pub fn resume_failed(mut self) -> RuntimeHostVm {
match self.inner.vm {
host::HostVm::SignatureVerification(sig) => self.inner.vm = sig.resume_failed(),
_ => unreachable!(),
}

self.inner.run()
}
}

/// Implementation detail of the execution. Shared by all the variants of [`RuntimeHostVm`]
/// other than [`RuntimeHostVm::Finished`].
struct Inner {
Expand Down Expand Up @@ -290,6 +377,13 @@ impl Inner {
return RuntimeHostVm::NextKey(NextKey { inner: self });
}

host::HostVm::SignatureVerification(req) => {
self.vm = req.into();
return RuntimeHostVm::SignatureVerification(SignatureVerification {
inner: self,
});
}

host::HostVm::CallRuntimeVersion(req) => {
// TODO: make the user execute this ; see https://github.com/paritytech/smoldot/issues/144
// The code below compiles the provided WebAssembly runtime code, which is a
Expand Down
94 changes: 94 additions & 0 deletions src/executor/runtime_host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,8 @@ pub enum RuntimeHostVm {
PrefixKeys(PrefixKeys),
/// Fetching the key that follows a given one is required in order to continue.
NextKey(NextKey),
/// Verifying whether a signature is correct is required in order to continue.
SignatureVerification(SignatureVerification),
}

impl RuntimeHostVm {
Expand All @@ -177,6 +179,7 @@ impl RuntimeHostVm {
RuntimeHostVm::StorageGet(inner) => inner.inner.vm.into_prototype(),
RuntimeHostVm::PrefixKeys(inner) => inner.inner.vm.into_prototype(),
RuntimeHostVm::NextKey(inner) => inner.inner.vm.into_prototype(),
RuntimeHostVm::SignatureVerification(inner) => inner.inner.vm.into_prototype(),
}
}
}
Expand Down Expand Up @@ -464,6 +467,90 @@ impl NextKey {
}
}

/// Verifying whether a signature is correct is required in order to continue.
#[must_use]
pub struct SignatureVerification {
inner: Inner,
}

impl SignatureVerification {
/// Returns the message that the signature is expected to sign.
pub fn message(&'_ self) -> impl AsRef<[u8]> + '_ {
match self.inner.vm {
host::HostVm::SignatureVerification(ref sig) => sig.message(),
_ => unreachable!(),
}
}

/// Returns the signature.
///
/// > **Note**: Be aware that this signature is untrusted input and might not be part of the
/// > set of valid signatures.
pub fn signature(&'_ self) -> impl AsRef<[u8]> + '_ {
match self.inner.vm {
host::HostVm::SignatureVerification(ref sig) => sig.signature(),
_ => unreachable!(),
}
}

/// Returns the public key the signature is against.
///
/// > **Note**: Be aware that this public key is untrusted input and might not be part of the
/// > set of valid public keys.
pub fn public_key(&'_ self) -> impl AsRef<[u8]> + '_ {
match self.inner.vm {
host::HostVm::SignatureVerification(ref sig) => sig.public_key(),
_ => unreachable!(),
}
}

/// Verify the signature. Returns `true` if it is valid.
pub fn is_valid(&self) -> bool {
match self.inner.vm {
host::HostVm::SignatureVerification(ref sig) => sig.is_valid(),
_ => unreachable!(),
}
}

/// Verify the signature and resume execution.
pub fn verify_and_resume(mut self) -> RuntimeHostVm {
match self.inner.vm {
host::HostVm::SignatureVerification(sig) => self.inner.vm = sig.verify_and_resume(),
_ => unreachable!(),
}

self.inner.run()
}

/// Resume the execution assuming that the signature is valid.
///
/// > **Note**: You are strongly encouraged to call
/// > [`SignatureVerification::verify_and_resume`]. This function is meant to be
/// > used only in debugging situations.
pub fn resume_success(mut self) -> RuntimeHostVm {
match self.inner.vm {
host::HostVm::SignatureVerification(sig) => self.inner.vm = sig.resume_success(),
_ => unreachable!(),
}

self.inner.run()
}

/// Resume the execution assuming that the signature is invalid.
///
/// > **Note**: You are strongly encouraged to call
/// > [`SignatureVerification::verify_and_resume`]. This function is meant to be
/// > used only in debugging situations.
pub fn resume_failed(mut self) -> RuntimeHostVm {
match self.inner.vm {
host::HostVm::SignatureVerification(sig) => self.inner.vm = sig.resume_failed(),
_ => unreachable!(),
}

self.inner.run()
}
}

/// Implementation detail of the execution. Shared by all the variants of [`RuntimeHostVm`]
/// other than [`RuntimeHostVm::Finished`].
struct Inner {
Expand Down Expand Up @@ -654,6 +741,13 @@ impl Inner {
self.vm = req.resume();
}

host::HostVm::SignatureVerification(req) => {
self.vm = req.into();
return RuntimeHostVm::SignatureVerification(SignatureVerification {
inner: self,
});
}

host::HostVm::CallRuntimeVersion(req) => {
// TODO: make the user execute this ; see https://github.com/paritytech/smoldot/issues/144
// The code below compiles the provided WebAssembly runtime code, which is a
Expand Down
Loading

0 comments on commit 377520f

Please sign in to comment.