From 62dda703bad40d68422e97de0f35cf7597f39273 Mon Sep 17 00:00:00 2001 From: Blas Rodriguez Irizar Date: Mon, 17 Oct 2022 14:47:32 +0200 Subject: [PATCH 01/45] define CryptoProvider trait --- light-client-verifier/Cargo.toml | 4 ++++ light-client-verifier/src/host_functions.rs | 14 ++++++++++++++ light-client-verifier/src/lib.rs | 1 + 3 files changed, 19 insertions(+) create mode 100644 light-client-verifier/src/host_functions.rs diff --git a/light-client-verifier/Cargo.toml b/light-client-verifier/Cargo.toml index c041d401f..0db35a5ef 100644 --- a/light-client-verifier/Cargo.toml +++ b/light-client-verifier/Cargo.toml @@ -33,5 +33,9 @@ serde = { version = "1.0.106", default-features = false } time = { version = "0.3", default-features = false } flex-error = { version = "0.4.4", default-features = false } +k256 = { version = "0.11", default-features = false, features = ["ecdsa", "sha256"] } +digest = { version = "0.10", default-features = false } +ed25519 = { version = "1.3", default-features = false } + [dev-dependencies] tendermint-testgen = { path = "../testgen", default-features = false } diff --git a/light-client-verifier/src/host_functions.rs b/light-client-verifier/src/host_functions.rs new file mode 100644 index 000000000..6341b9a8d --- /dev/null +++ b/light-client-verifier/src/host_functions.rs @@ -0,0 +1,14 @@ +use digest::FixedOutput; +use digest::{consts::U32, Digest}; +use tendermint::signature::Signer; +use tendermint::signature::Verifier; + +pub trait CryptoProvider { + type Sha256: Digest + FixedOutput; + + type EcdsaSecp256k1Signer: Signer; + type EcdsaSecp256k1Verifier: Verifier; + + type Ed25519Signer: Signer; + type Ed25519Verifier: Verifier; +} diff --git a/light-client-verifier/src/lib.rs b/light-client-verifier/src/lib.rs index ce413cefd..499c2f82e 100644 --- a/light-client-verifier/src/lib.rs +++ b/light-client-verifier/src/lib.rs @@ -5,6 +5,7 @@ extern crate alloc; mod prelude; pub mod errors; +pub mod host_functions; pub mod operations; pub mod options; pub mod predicates; From b7920c3cf53b592ddcd2569845987e0915bb1174 Mon Sep 17 00:00:00 2001 From: Blas Rodriguez Irizar Date: Mon, 17 Oct 2022 21:35:58 +0200 Subject: [PATCH 02/45] wip --- light-client-verifier/Cargo.toml | 2 + light-client-verifier/src/host_functions.rs | 64 +++++++++++++++++++-- 2 files changed, 62 insertions(+), 4 deletions(-) diff --git a/light-client-verifier/Cargo.toml b/light-client-verifier/Cargo.toml index 0db35a5ef..43815ab70 100644 --- a/light-client-verifier/Cargo.toml +++ b/light-client-verifier/Cargo.toml @@ -39,3 +39,5 @@ ed25519 = { version = "1.3", default-features = false } [dev-dependencies] tendermint-testgen = { path = "../testgen", default-features = false } +sha2 = { version = "0.9", default-features = false } + diff --git a/light-client-verifier/src/host_functions.rs b/light-client-verifier/src/host_functions.rs index 6341b9a8d..16511f93f 100644 --- a/light-client-verifier/src/host_functions.rs +++ b/light-client-verifier/src/host_functions.rs @@ -6,9 +6,65 @@ use tendermint::signature::Verifier; pub trait CryptoProvider { type Sha256: Digest + FixedOutput; - type EcdsaSecp256k1Signer: Signer; - type EcdsaSecp256k1Verifier: Verifier; + // type EcdsaSecp256k1Signer: Signer; + // type EcdsaSecp256k1Verifier: Verifier; - type Ed25519Signer: Signer; - type Ed25519Verifier: Verifier; + // type Ed25519Signer: Signer; + // type Ed25519Verifier: Verifier; +} + +#[cfg(test)] +mod tests { + + use super::*; + struct SubstrateHostFunctionsManager; + + trait SubstrateHostFunctions: CryptoProvider { + fn sha2_256(preimage: &[u8]) -> [u8; 32]; + fn ed25519_verify(sig: &[u8], msg: &[u8], pub_key: &[u8]) -> Result<(), ()>; + fn secp256k1_verify(sig: &[u8], message: &[u8], public: &[u8]) -> Result<(), ()>; + } + + impl SubstrateHostFunctionsManager for SubstrateHostFunctions { + type Sha256 = sha2::Sha256; + + fn sha2_256(preimage: &[u8]) -> [u8; 32] { + unimplemented!() + } + fn ed25519_verify(sig: &[u8], msg: &[u8], pub_key: &[u8]) -> Result<(), ()> { + unimplemented!() + } + fn secp256k1_verify(sig: &[u8], message: &[u8], public: &[u8]) -> Result<(), ()> { + unimplemented!() + } + } + + // impl CryptoProvider for CryptoManager { + // fn sha2_256(preimage: &[u8]) -> [u8; 32] { + // sp_core::hashing::sha2_256(preimage) + // } + + // fn ed25519_verify(sig: &[u8], msg: &[u8], pub_key: &[u8]) -> Result<(), ()> { + // use sp_core::{ed25519, ByteArray, Pair}; + + // let signature = ed25519::Signature::from_slice(sig).ok_or(())?; + + // let public_key = ed25519::Public::from_slice(pub_key).map_err(|_| ())?; + // if ed25519::Pair::verify(&signature, msg, &public_key) { + // return Ok(()); + // } + // Err(()) + // } + + // fn secp256k1_verify(sig: &[u8], message: &[u8], public: &[u8]) -> Result<(), ()> { + // use sp_core::{ecdsa, ByteArray, Pair}; + + // let public = ecdsa::Public::from_slice(public).map_err(|_| ())?; + // if ecdsa::Pair::verify_weak(&sig, message, &public) { + // return Ok(()); + // } + + // Err(()) + // } + // } } From c4b52ded835ae051975b7de7372c9431420204ba Mon Sep 17 00:00:00 2001 From: Blas Rodriguez Irizar Date: Wed, 19 Oct 2022 13:14:25 +0200 Subject: [PATCH 03/45] implement CryptoProvider for Substrate (WIP) --- light-client-verifier/Cargo.toml | 1 + light-client-verifier/src/host_functions.rs | 108 ++++++++++++++------ 2 files changed, 75 insertions(+), 34 deletions(-) diff --git a/light-client-verifier/Cargo.toml b/light-client-verifier/Cargo.toml index 43815ab70..1d0f0a20a 100644 --- a/light-client-verifier/Cargo.toml +++ b/light-client-verifier/Cargo.toml @@ -36,6 +36,7 @@ flex-error = { version = "0.4.4", default-features = false } k256 = { version = "0.11", default-features = false, features = ["ecdsa", "sha256"] } digest = { version = "0.10", default-features = false } ed25519 = { version = "1.3", default-features = false } +signature = { version = "1", default-features = false } [dev-dependencies] tendermint-testgen = { path = "../testgen", default-features = false } diff --git a/light-client-verifier/src/host_functions.rs b/light-client-verifier/src/host_functions.rs index 16511f93f..8fdee922c 100644 --- a/light-client-verifier/src/host_functions.rs +++ b/light-client-verifier/src/host_functions.rs @@ -7,7 +7,7 @@ pub trait CryptoProvider { type Sha256: Digest + FixedOutput; // type EcdsaSecp256k1Signer: Signer; - // type EcdsaSecp256k1Verifier: Verifier; + type EcdsaSecp256k1Verifier: Verifier; // type Ed25519Signer: Signer; // type Ed25519Verifier: Verifier; @@ -16,55 +16,95 @@ pub trait CryptoProvider { #[cfg(test)] mod tests { + use core::marker::PhantomData; + + use signature::DigestVerifier; + use super::*; struct SubstrateHostFunctionsManager; + #[derive(Debug, Default)] + struct SubstrateSha256(sha2::Sha256); + #[derive(Debug)] + struct SubstrateSignatureVerifier { + inner: k256::ecdsa::VerifyingKey, + _d: PhantomData, + } + + impl SubstrateSignatureVerifier { + fn from_bytes(public_key: &[u8]) -> Result { + Ok(Self { + inner: k256::ecdsa::VerifyingKey::from_sec1_bytes(public_key)?, + _d: PhantomData::default(), + }) + } + } + + impl DigestVerifier for SubstrateSignatureVerifier { + fn verify_digest(&self, digest: D, signature: &S) -> Result<(), ed25519::Error> { + self.inner.verify_digest(digest, signature) + } + } + + impl tendermint::signature::Verifier + for SubstrateSignatureVerifier + { + fn verify(&self, msg: &[u8], signature: &S) -> Result<(), ed25519::Error> { + let mut hasher = D::new(); + hasher.update(msg); + self.verify_digest(hasher, signature) + } + } + + impl digest::OutputSizeUser for SubstrateSha256 { + type OutputSize = U32; + } + + impl digest::HashMarker for SubstrateSha256 {} + + impl digest::Update for SubstrateSha256 { + fn update(&mut self, data: &[u8]) { + use sha2::Digest; + self.0.update(data); + } + } + + impl digest::FixedOutput for SubstrateSha256 { + fn finalize_into(self, out: &mut digest::Output) { + use sha2::Digest; + self.0.finalize(); + } + } + trait SubstrateHostFunctions: CryptoProvider { fn sha2_256(preimage: &[u8]) -> [u8; 32]; fn ed25519_verify(sig: &[u8], msg: &[u8], pub_key: &[u8]) -> Result<(), ()>; fn secp256k1_verify(sig: &[u8], message: &[u8], public: &[u8]) -> Result<(), ()>; } - impl SubstrateHostFunctionsManager for SubstrateHostFunctions { - type Sha256 = sha2::Sha256; + impl CryptoProvider for SubstrateHostFunctionsManager { + type Sha256 = SubstrateSha256; + + type EcdsaSecp256k1Verifier = SubstrateSignatureVerifier; + } + impl SubstrateHostFunctions for SubstrateHostFunctionsManager { fn sha2_256(preimage: &[u8]) -> [u8; 32] { - unimplemented!() + let mut hasher = Self::Sha256::new(); + hasher.update(preimage); + let result = hasher.finalize().try_into().unwrap(); + result } fn ed25519_verify(sig: &[u8], msg: &[u8], pub_key: &[u8]) -> Result<(), ()> { unimplemented!() } fn secp256k1_verify(sig: &[u8], message: &[u8], public: &[u8]) -> Result<(), ()> { - unimplemented!() + // Self::secp256k1_verify(sig, message, public) + let verifier = + <::EcdsaSecp256k1Verifier>::from_bytes(public).unwrap(); + // TODO: probably should name the verifier properly - not sure how to do it better + let signature = k256::ecdsa::Signature::from_der(sig).unwrap(); + Ok(verifier.verify(message, &signature).unwrap()) } } - - // impl CryptoProvider for CryptoManager { - // fn sha2_256(preimage: &[u8]) -> [u8; 32] { - // sp_core::hashing::sha2_256(preimage) - // } - - // fn ed25519_verify(sig: &[u8], msg: &[u8], pub_key: &[u8]) -> Result<(), ()> { - // use sp_core::{ed25519, ByteArray, Pair}; - - // let signature = ed25519::Signature::from_slice(sig).ok_or(())?; - - // let public_key = ed25519::Public::from_slice(pub_key).map_err(|_| ())?; - // if ed25519::Pair::verify(&signature, msg, &public_key) { - // return Ok(()); - // } - // Err(()) - // } - - // fn secp256k1_verify(sig: &[u8], message: &[u8], public: &[u8]) -> Result<(), ()> { - // use sp_core::{ecdsa, ByteArray, Pair}; - - // let public = ecdsa::Public::from_slice(public).map_err(|_| ())?; - // if ecdsa::Pair::verify_weak(&sig, message, &public) { - // return Ok(()); - // } - - // Err(()) - // } - // } } From a35c105daf86fec215016d2e916380fd5bc9e787 Mon Sep 17 00:00:00 2001 From: Blas Rodriguez Irizar Date: Wed, 19 Oct 2022 13:15:30 +0200 Subject: [PATCH 04/45] comment issues --- light-client-verifier/src/host_functions.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/light-client-verifier/src/host_functions.rs b/light-client-verifier/src/host_functions.rs index 8fdee922c..f98f94e67 100644 --- a/light-client-verifier/src/host_functions.rs +++ b/light-client-verifier/src/host_functions.rs @@ -42,6 +42,16 @@ mod tests { impl DigestVerifier for SubstrateSignatureVerifier { fn verify_digest(&self, digest: D, signature: &S) -> Result<(), ed25519::Error> { + // TODO; having issues here + /* + error[E0277]: the trait bound `VerifyingKey: DigestVerifier` is not satisfied + --> light-client-verifier/src/host_functions.rs:46:38 + | + 46 | self.inner.verify_digest(digest, signature) + | ------------- ^^^^^^ the trait `DigestVerifier` is not implemented for `VerifyingKey` + | | + | required by a bound introduced by this call + */ self.inner.verify_digest(digest, signature) } } @@ -102,7 +112,6 @@ mod tests { // Self::secp256k1_verify(sig, message, public) let verifier = <::EcdsaSecp256k1Verifier>::from_bytes(public).unwrap(); - // TODO: probably should name the verifier properly - not sure how to do it better let signature = k256::ecdsa::Signature::from_der(sig).unwrap(); Ok(verifier.verify(message, &signature).unwrap()) } From 47b56bf987b1e251a97bc12a9c90f87041077fad Mon Sep 17 00:00:00 2001 From: Blas Rodriguez Irizar Date: Wed, 19 Oct 2022 14:40:24 +0200 Subject: [PATCH 05/45] ed25519_verify implementation for SubstrateHostFunctionsManager --- light-client-verifier/src/host_functions.rs | 42 ++++++++++----------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/light-client-verifier/src/host_functions.rs b/light-client-verifier/src/host_functions.rs index f98f94e67..3d1faa081 100644 --- a/light-client-verifier/src/host_functions.rs +++ b/light-client-verifier/src/host_functions.rs @@ -1,6 +1,5 @@ use digest::FixedOutput; use digest::{consts::U32, Digest}; -use tendermint::signature::Signer; use tendermint::signature::Verifier; pub trait CryptoProvider { @@ -22,6 +21,7 @@ mod tests { use super::*; struct SubstrateHostFunctionsManager; + use k256::ecdsa::VerifyingKey; #[derive(Debug, Default)] struct SubstrateSha256(sha2::Sha256); @@ -31,7 +31,7 @@ mod tests { _d: PhantomData, } - impl SubstrateSignatureVerifier { + impl> SubstrateSignatureVerifier { fn from_bytes(public_key: &[u8]) -> Result { Ok(Self { inner: k256::ecdsa::VerifyingKey::from_sec1_bytes(public_key)?, @@ -40,28 +40,24 @@ mod tests { } } - impl DigestVerifier for SubstrateSignatureVerifier { + impl, S: signature::Signature> DigestVerifier + for SubstrateSignatureVerifier + where + VerifyingKey: DigestVerifier, + { fn verify_digest(&self, digest: D, signature: &S) -> Result<(), ed25519::Error> { - // TODO; having issues here - /* - error[E0277]: the trait bound `VerifyingKey: DigestVerifier` is not satisfied - --> light-client-verifier/src/host_functions.rs:46:38 - | - 46 | self.inner.verify_digest(digest, signature) - | ------------- ^^^^^^ the trait `DigestVerifier` is not implemented for `VerifyingKey` - | | - | required by a bound introduced by this call - */ self.inner.verify_digest(digest, signature) } } - impl tendermint::signature::Verifier - for SubstrateSignatureVerifier + impl> + tendermint::signature::Verifier for SubstrateSignatureVerifier + where + VerifyingKey: DigestVerifier, { fn verify(&self, msg: &[u8], signature: &S) -> Result<(), ed25519::Error> { let mut hasher = D::new(); - hasher.update(msg); + Digest::update(&mut hasher, msg); self.verify_digest(hasher, signature) } } @@ -82,7 +78,7 @@ mod tests { impl digest::FixedOutput for SubstrateSha256 { fn finalize_into(self, out: &mut digest::Output) { use sha2::Digest; - self.0.finalize(); + *out = self.0.finalize(); } } @@ -106,14 +102,14 @@ mod tests { result } fn ed25519_verify(sig: &[u8], msg: &[u8], pub_key: &[u8]) -> Result<(), ()> { - unimplemented!() - } - fn secp256k1_verify(sig: &[u8], message: &[u8], public: &[u8]) -> Result<(), ()> { - // Self::secp256k1_verify(sig, message, public) let verifier = - <::EcdsaSecp256k1Verifier>::from_bytes(public).unwrap(); + <::EcdsaSecp256k1Verifier>::from_bytes(pub_key).unwrap(); let signature = k256::ecdsa::Signature::from_der(sig).unwrap(); - Ok(verifier.verify(message, &signature).unwrap()) + Ok(verifier.verify(msg, &signature).unwrap()) + } + + fn secp256k1_verify(sig: &[u8], message: &[u8], public: &[u8]) -> Result<(), ()> { + unimplemented!() } } } From 2d0fc38b7e5a31d8878c0f50fc37d9494360e125 Mon Sep 17 00:00:00 2001 From: Blas Rodriguez Irizar Date: Wed, 19 Oct 2022 14:57:14 +0200 Subject: [PATCH 06/45] introduce EcdsaSecp256k1Signer --- light-client-verifier/src/host_functions.rs | 24 ++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/light-client-verifier/src/host_functions.rs b/light-client-verifier/src/host_functions.rs index 3d1faa081..59ad859a2 100644 --- a/light-client-verifier/src/host_functions.rs +++ b/light-client-verifier/src/host_functions.rs @@ -1,11 +1,12 @@ use digest::FixedOutput; use digest::{consts::U32, Digest}; +use signature::Signer; use tendermint::signature::Verifier; pub trait CryptoProvider { type Sha256: Digest + FixedOutput; - // type EcdsaSecp256k1Signer: Signer; + type EcdsaSecp256k1Signer: Signer; type EcdsaSecp256k1Verifier: Verifier; // type Ed25519Signer: Signer; @@ -17,14 +18,19 @@ mod tests { use core::marker::PhantomData; - use signature::DigestVerifier; + use signature::{DigestSigner, DigestVerifier}; use super::*; struct SubstrateHostFunctionsManager; - use k256::ecdsa::VerifyingKey; + use k256::ecdsa::{SigningKey, VerifyingKey}; #[derive(Debug, Default)] struct SubstrateSha256(sha2::Sha256); + + struct SubstrateSigner { + inner: k256::ecdsa::SigningKey, + _d: PhantomData, + } #[derive(Debug)] struct SubstrateSignatureVerifier { inner: k256::ecdsa::VerifyingKey, @@ -82,6 +88,17 @@ mod tests { } } + impl Signer for SubstrateSigner + where + SigningKey: DigestSigner, + { + fn try_sign(&self, msg: &[u8]) -> Result { + let mut hasher = D::new(); + Digest::update(&mut hasher, msg); + self.inner.try_sign_digest(hasher) + } + } + trait SubstrateHostFunctions: CryptoProvider { fn sha2_256(preimage: &[u8]) -> [u8; 32]; fn ed25519_verify(sig: &[u8], msg: &[u8], pub_key: &[u8]) -> Result<(), ()>; @@ -91,6 +108,7 @@ mod tests { impl CryptoProvider for SubstrateHostFunctionsManager { type Sha256 = SubstrateSha256; + type EcdsaSecp256k1Signer = SubstrateSigner; type EcdsaSecp256k1Verifier = SubstrateSignatureVerifier; } From 6cbcbb3b7204557414eea5fe7a11fa38f18f1067 Mon Sep 17 00:00:00 2001 From: Blas Rodriguez Irizar Date: Wed, 19 Oct 2022 14:59:54 +0200 Subject: [PATCH 07/45] doc a bit --- light-client-verifier/src/host_functions.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/light-client-verifier/src/host_functions.rs b/light-client-verifier/src/host_functions.rs index 59ad859a2..8298133a4 100644 --- a/light-client-verifier/src/host_functions.rs +++ b/light-client-verifier/src/host_functions.rs @@ -16,8 +16,9 @@ pub trait CryptoProvider { #[cfg(test)] mod tests { + /// A draft for an imlpementation of the HostFunctionManager for a specific chain (i.e. Polkadot/Substrate) + /// that uses the [`CryptoProvider`] trait use core::marker::PhantomData; - use signature::{DigestSigner, DigestVerifier}; use super::*; From e6edade7c7b09f3bd3d0baceb68599bca8eb3947 Mon Sep 17 00:00:00 2001 From: Blas Rodriguez Irizar Date: Thu, 20 Oct 2022 02:23:09 +0200 Subject: [PATCH 08/45] move CryptoProvider to tendermint crate instead --- light-client-verifier/Cargo.toml | 8 +------- light-client-verifier/src/lib.rs | 1 - tendermint/Cargo.toml | 5 +++-- .../src/host_functions.rs | 16 ++++++++-------- tendermint/src/lib.rs | 1 + 5 files changed, 13 insertions(+), 18 deletions(-) rename {light-client-verifier => tendermint}/src/host_functions.rs (90%) diff --git a/light-client-verifier/Cargo.toml b/light-client-verifier/Cargo.toml index 1d0f0a20a..5e25c018c 100644 --- a/light-client-verifier/Cargo.toml +++ b/light-client-verifier/Cargo.toml @@ -33,12 +33,6 @@ serde = { version = "1.0.106", default-features = false } time = { version = "0.3", default-features = false } flex-error = { version = "0.4.4", default-features = false } -k256 = { version = "0.11", default-features = false, features = ["ecdsa", "sha256"] } -digest = { version = "0.10", default-features = false } -ed25519 = { version = "1.3", default-features = false } -signature = { version = "1", default-features = false } - [dev-dependencies] tendermint-testgen = { path = "../testgen", default-features = false } -sha2 = { version = "0.9", default-features = false } - +sha2 = { version = "0.9", default-features = false } \ No newline at end of file diff --git a/light-client-verifier/src/lib.rs b/light-client-verifier/src/lib.rs index 499c2f82e..ce413cefd 100644 --- a/light-client-verifier/src/lib.rs +++ b/light-client-verifier/src/lib.rs @@ -5,7 +5,6 @@ extern crate alloc; mod prelude; pub mod errors; -pub mod host_functions; pub mod operations; pub mod options; pub mod predicates; diff --git a/tendermint/Cargo.toml b/tendermint/Cargo.toml index 2c568a384..4a9568c68 100644 --- a/tendermint/Cargo.toml +++ b/tendermint/Cargo.toml @@ -32,6 +32,7 @@ rustdoc-args = ["--cfg", "docsrs"] [dependencies] async-trait = { version = "0.1", default-features = false } bytes = { version = "1.0", default-features = false, features = ["serde"] } +digest = { version = "0.10", default-features = false } ed25519 = { version = "1.3", default-features = false } ed25519-dalek = { version = "1", default-features = false, features = ["u64_backend"] } futures = { version = "0.3", default-features = false } @@ -51,14 +52,14 @@ tendermint-proto = { version = "0.26.0", default-features = false, path = "../pr time = { version = "0.3", default-features = false, features = ["macros", "parsing"] } zeroize = { version = "1.1", default-features = false, features = ["zeroize_derive", "alloc"] } flex-error = { version = "0.4.4", default-features = false } -k256 = { version = "0.11", optional = true, default-features = false, features = ["ecdsa", "sha256"] } +k256 = { version = "0.11", default-features = false, features = ["ecdsa", "sha256"] } ripemd160 = { version = "0.9", default-features = false, optional = true } [features] default = ["std"] std = ["flex-error/std", "flex-error/eyre_tracer", "clock"] clock = ["time/std"] -secp256k1 = ["k256", "ripemd160"] +secp256k1 = ["ripemd160"] [dev-dependencies] pretty_assertions = "1.3.0" diff --git a/light-client-verifier/src/host_functions.rs b/tendermint/src/host_functions.rs similarity index 90% rename from light-client-verifier/src/host_functions.rs rename to tendermint/src/host_functions.rs index 8298133a4..61ddcbc6b 100644 --- a/light-client-verifier/src/host_functions.rs +++ b/tendermint/src/host_functions.rs @@ -1,7 +1,7 @@ +use crate::signature::Verifier; use digest::FixedOutput; use digest::{consts::U32, Digest}; use signature::Signer; -use tendermint::signature::Verifier; pub trait CryptoProvider { type Sha256: Digest + FixedOutput; @@ -29,19 +29,19 @@ mod tests { struct SubstrateSha256(sha2::Sha256); struct SubstrateSigner { - inner: k256::ecdsa::SigningKey, + inner: SigningKey, _d: PhantomData, } #[derive(Debug)] struct SubstrateSignatureVerifier { - inner: k256::ecdsa::VerifyingKey, + inner: VerifyingKey, _d: PhantomData, } impl> SubstrateSignatureVerifier { fn from_bytes(public_key: &[u8]) -> Result { Ok(Self { - inner: k256::ecdsa::VerifyingKey::from_sec1_bytes(public_key)?, + inner: VerifyingKey::from_sec1_bytes(public_key)?, _d: PhantomData::default(), }) } @@ -57,8 +57,8 @@ mod tests { } } - impl> - tendermint::signature::Verifier for SubstrateSignatureVerifier + impl> Verifier + for SubstrateSignatureVerifier where VerifyingKey: DigestVerifier, { @@ -82,7 +82,7 @@ mod tests { } } - impl digest::FixedOutput for SubstrateSha256 { + impl FixedOutput for SubstrateSha256 { fn finalize_into(self, out: &mut digest::Output) { use sha2::Digest; *out = self.0.finalize(); @@ -127,7 +127,7 @@ mod tests { Ok(verifier.verify(msg, &signature).unwrap()) } - fn secp256k1_verify(sig: &[u8], message: &[u8], public: &[u8]) -> Result<(), ()> { + fn secp256k1_verify(_sig: &[u8], _message: &[u8], _public: &[u8]) -> Result<(), ()> { unimplemented!() } } diff --git a/tendermint/src/lib.rs b/tendermint/src/lib.rs index 74a554014..09c846933 100644 --- a/tendermint/src/lib.rs +++ b/tendermint/src/lib.rs @@ -36,6 +36,7 @@ pub mod consensus; pub mod evidence; pub mod genesis; pub mod hash; +pub mod host_functions; pub mod merkle; mod moniker; pub mod node; From bcd6be0db80cf193d1d48b3ea7e1155e208352df Mon Sep 17 00:00:00 2001 From: Blas Rodriguez Irizar Date: Mon, 31 Oct 2022 17:22:53 +0100 Subject: [PATCH 09/45] rename host_functions module to crypto --- tendermint/src/{host_functions.rs => crypto.rs} | 0 tendermint/src/lib.rs | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename tendermint/src/{host_functions.rs => crypto.rs} (100%) diff --git a/tendermint/src/host_functions.rs b/tendermint/src/crypto.rs similarity index 100% rename from tendermint/src/host_functions.rs rename to tendermint/src/crypto.rs diff --git a/tendermint/src/lib.rs b/tendermint/src/lib.rs index 09c846933..6fbab29c0 100644 --- a/tendermint/src/lib.rs +++ b/tendermint/src/lib.rs @@ -33,10 +33,10 @@ pub mod block; pub mod chain; pub mod channel; pub mod consensus; +pub mod crypto; pub mod evidence; pub mod genesis; pub mod hash; -pub mod host_functions; pub mod merkle; mod moniker; pub mod node; From 7f5e812fc2fc625eac9b1e4f0084b89bac0233ea Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Thu, 17 Nov 2022 15:42:13 +0200 Subject: [PATCH 10/45] tendermint: Remove ED25519_SIGNATURE_SIZE It has been deprecated for some time. --- tendermint/src/signature.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/tendermint/src/signature.rs b/tendermint/src/signature.rs index 43703c1c3..128f751c5 100644 --- a/tendermint/src/signature.rs +++ b/tendermint/src/signature.rs @@ -10,9 +10,6 @@ use tendermint_proto::Protobuf; use crate::{error::Error, prelude::*}; -#[deprecated(since = "0.23.2", note = "use Ed25519Signature::BYTE_SIZE instead")] -pub const ED25519_SIGNATURE_SIZE: usize = Ed25519Signature::BYTE_SIZE; - /// The expected length of all currently supported signatures, in bytes. pub const SIGNATURE_LENGTH: usize = 64; From 3fecdefb6de61d8077ccf1375f8bcc80b51a6ae0 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Thu, 17 Nov 2022 15:45:23 +0200 Subject: [PATCH 11/45] tendermint: clean up mod crypto --- tendermint/src/crypto.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tendermint/src/crypto.rs b/tendermint/src/crypto.rs index 61ddcbc6b..a005c097b 100644 --- a/tendermint/src/crypto.rs +++ b/tendermint/src/crypto.rs @@ -1,16 +1,12 @@ -use crate::signature::Verifier; +use crate::signature::{Signer, Verifier}; use digest::FixedOutput; use digest::{consts::U32, Digest}; -use signature::Signer; pub trait CryptoProvider { type Sha256: Digest + FixedOutput; type EcdsaSecp256k1Signer: Signer; type EcdsaSecp256k1Verifier: Verifier; - - // type Ed25519Signer: Signer; - // type Ed25519Verifier: Verifier; } #[cfg(test)] From 4a511c53037eeb509c0ec508928fbed911bd341f Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Thu, 17 Nov 2022 18:06:43 +0200 Subject: [PATCH 12/45] Disentangle CryptoProvider from k256 crate The API needs to be abstract, so make p256k1 signature an associated type of CryptoProvider. --- tendermint/Cargo.toml | 4 ++-- tendermint/src/crypto.rs | 12 +++++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/tendermint/Cargo.toml b/tendermint/Cargo.toml index 4a9568c68..5a2acb079 100644 --- a/tendermint/Cargo.toml +++ b/tendermint/Cargo.toml @@ -52,14 +52,14 @@ tendermint-proto = { version = "0.26.0", default-features = false, path = "../pr time = { version = "0.3", default-features = false, features = ["macros", "parsing"] } zeroize = { version = "1.1", default-features = false, features = ["zeroize_derive", "alloc"] } flex-error = { version = "0.4.4", default-features = false } -k256 = { version = "0.11", default-features = false, features = ["ecdsa", "sha256"] } +k256 = { version = "0.11", optional = true, default-features = false, features = ["ecdsa", "sha256"] } ripemd160 = { version = "0.9", default-features = false, optional = true } [features] default = ["std"] std = ["flex-error/std", "flex-error/eyre_tracer", "clock"] clock = ["time/std"] -secp256k1 = ["ripemd160"] +secp256k1 = ["k256", "ripemd160"] [dev-dependencies] pretty_assertions = "1.3.0" diff --git a/tendermint/src/crypto.rs b/tendermint/src/crypto.rs index a005c097b..403761e9b 100644 --- a/tendermint/src/crypto.rs +++ b/tendermint/src/crypto.rs @@ -1,12 +1,13 @@ -use crate::signature::{Signer, Verifier}; use digest::FixedOutput; use digest::{consts::U32, Digest}; +use signature::{Signature, Signer, Verifier}; pub trait CryptoProvider { type Sha256: Digest + FixedOutput; - type EcdsaSecp256k1Signer: Signer; - type EcdsaSecp256k1Verifier: Verifier; + type EcdsaSecp256k1Signature: Signature; + type EcdsaSecp256k1Signer: Signer; + type EcdsaSecp256k1Verifier: Verifier; } #[cfg(test)] @@ -43,7 +44,7 @@ mod tests { } } - impl, S: signature::Signature> DigestVerifier + impl, S: Signature> DigestVerifier for SubstrateSignatureVerifier where VerifyingKey: DigestVerifier, @@ -85,7 +86,7 @@ mod tests { } } - impl Signer for SubstrateSigner + impl Signer for SubstrateSigner where SigningKey: DigestSigner, { @@ -105,6 +106,7 @@ mod tests { impl CryptoProvider for SubstrateHostFunctionsManager { type Sha256 = SubstrateSha256; + type EcdsaSecp256k1Signature = k256::ecdsa::Signature; type EcdsaSecp256k1Signer = SubstrateSigner; type EcdsaSecp256k1Verifier = SubstrateSignatureVerifier; } From 95758521ac9115a028ffe03fdc95cd9e8f1d7c06 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Thu, 17 Nov 2022 22:02:45 +0200 Subject: [PATCH 13/45] Add DefaultCryptoProvider Guarded by the rust-crypto feature, this is an implementation of CryptoProvider with pure Rust crates. --- light-client-verifier/Cargo.toml | 2 +- p2p/Cargo.toml | 4 ++-- tendermint/Cargo.toml | 9 +++++---- tendermint/src/account.rs | 2 +- tendermint/src/crypto.rs | 19 +++++++++++++------ tendermint/src/crypto/default_provider.rs | 16 ++++++++++++++++ 6 files changed, 38 insertions(+), 14 deletions(-) create mode 100644 tendermint/src/crypto/default_provider.rs diff --git a/light-client-verifier/Cargo.toml b/light-client-verifier/Cargo.toml index 5e25c018c..d03b97464 100644 --- a/light-client-verifier/Cargo.toml +++ b/light-client-verifier/Cargo.toml @@ -35,4 +35,4 @@ flex-error = { version = "0.4.4", default-features = false } [dev-dependencies] tendermint-testgen = { path = "../testgen", default-features = false } -sha2 = { version = "0.9", default-features = false } \ No newline at end of file +sha2 = { version = "0.10", default-features = false } \ No newline at end of file diff --git a/p2p/Cargo.toml b/p2p/Cargo.toml index 941b6513c..f1c6c5348 100644 --- a/p2p/Cargo.toml +++ b/p2p/Cargo.toml @@ -31,11 +31,11 @@ chacha20poly1305 = { version = "0.8", default-features = false, features = ["red ed25519-dalek = { version = "1", default-features = false } eyre = { version = "0.6", default-features = false } flume = { version = "0.10.7", default-features = false } -hkdf = { version = "0.10.0", default-features = false } +hkdf = { version = "0.12.3", default-features = false } merlin = { version = "2", default-features = false } prost = { version = "0.11", default-features = false } rand_core = { version = "0.5", default-features = false, features = ["std"] } -sha2 = { version = "0.9", default-features = false } +sha2 = { version = "0.10", default-features = false } subtle = { version = "2", default-features = false } x25519-dalek = { version = "1.1", default-features = false } zeroize = { version = "1", default-features = false } diff --git a/tendermint/Cargo.toml b/tendermint/Cargo.toml index 5a2acb079..a5a9e569e 100644 --- a/tendermint/Cargo.toml +++ b/tendermint/Cargo.toml @@ -44,7 +44,7 @@ serde = { version = "1", default-features = false, features = ["derive"] } serde_json = { version = "1", default-features = false, features = ["alloc"] } serde_bytes = { version = "0.11", default-features = false } serde_repr = { version = "0.1", default-features = false } -sha2 = { version = "0.9", default-features = false } +sha2 = { version = "0.10", default-features = false } signature = { version = "1", default-features = false } subtle = { version = "2", default-features = false } subtle-encoding = { version = "0.5", default-features = false, features = ["bech32-preview"] } @@ -53,13 +53,14 @@ time = { version = "0.3", default-features = false, features = ["macros", "parsi zeroize = { version = "1.1", default-features = false, features = ["zeroize_derive", "alloc"] } flex-error = { version = "0.4.4", default-features = false } k256 = { version = "0.11", optional = true, default-features = false, features = ["ecdsa", "sha256"] } -ripemd160 = { version = "0.9", default-features = false, optional = true } +ripemd = { version = "0.1.3", default-features = false, optional = true } [features] -default = ["std"] +default = ["std", "rust-crypto"] std = ["flex-error/std", "flex-error/eyre_tracer", "clock"] clock = ["time/std"] -secp256k1 = ["k256", "ripemd160"] +secp256k1 = ["k256", "ripemd"] +rust-crypto = ["k256"] [dev-dependencies] pretty_assertions = "1.3.0" diff --git a/tendermint/src/account.rs b/tendermint/src/account.rs index e01d83074..b82a88d3c 100644 --- a/tendermint/src/account.rs +++ b/tendermint/src/account.rs @@ -7,7 +7,7 @@ use core::{ }; #[cfg(feature = "secp256k1")] -use ripemd160::Ripemd160; +use ripemd::Ripemd160; use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use sha2::{Digest, Sha256}; use subtle::{self, ConstantTimeEq}; diff --git a/tendermint/src/crypto.rs b/tendermint/src/crypto.rs index 403761e9b..0f527b33a 100644 --- a/tendermint/src/crypto.rs +++ b/tendermint/src/crypto.rs @@ -1,5 +1,10 @@ -use digest::FixedOutput; -use digest::{consts::U32, Digest}; +#[cfg(feature = "rust-crypto")] +mod default_provider; + +#[cfg(feature = "rust-crypto")] +pub use default_provider::DefaultCryptoProvider; + +use digest::{consts::U32, Digest, FixedOutput}; use signature::{Signature, Signer, Verifier}; pub trait CryptoProvider { @@ -18,7 +23,11 @@ mod tests { use core::marker::PhantomData; use signature::{DigestSigner, DigestVerifier}; - use super::*; + use digest::{consts::U32, Digest, FixedOutput}; + use signature::{Signature, Signer, Verifier}; + + use super::CryptoProvider; + struct SubstrateHostFunctionsManager; use k256::ecdsa::{SigningKey, VerifyingKey}; @@ -74,14 +83,12 @@ mod tests { impl digest::Update for SubstrateSha256 { fn update(&mut self, data: &[u8]) { - use sha2::Digest; - self.0.update(data); + digest::Update::update(&mut self.0, data) } } impl FixedOutput for SubstrateSha256 { fn finalize_into(self, out: &mut digest::Output) { - use sha2::Digest; *out = self.0.finalize(); } } diff --git a/tendermint/src/crypto/default_provider.rs b/tendermint/src/crypto/default_provider.rs new file mode 100644 index 000000000..5134f7e45 --- /dev/null +++ b/tendermint/src/crypto/default_provider.rs @@ -0,0 +1,16 @@ +use sha2::Sha256; + +use super::CryptoProvider; + +/// A batteries-included provider of cryptograpic functions. +pub struct DefaultCryptoProvider {} + +impl CryptoProvider for DefaultCryptoProvider { + type Sha256 = Sha256; + + type EcdsaSecp256k1Signature = k256::ecdsa::Signature; + + type EcdsaSecp256k1Signer = k256::ecdsa::SigningKey; + + type EcdsaSecp256k1Verifier = k256::ecdsa::VerifyingKey; +} From 3573ef04a25e80239b9a246f53ed1577a0166ff7 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Fri, 18 Nov 2022 14:07:36 +0200 Subject: [PATCH 14/45] tendermint: reorg mod crypto Move CryptoProvider definition out of the crypto module into the provider sub-module. Rename default_provider to default. --- tendermint/src/crypto.rs | 142 +----------------- .../{default_provider.rs => default.rs} | 0 tendermint/src/crypto/provider.rs | 133 ++++++++++++++++ 3 files changed, 139 insertions(+), 136 deletions(-) rename tendermint/src/crypto/{default_provider.rs => default.rs} (100%) create mode 100644 tendermint/src/crypto/provider.rs diff --git a/tendermint/src/crypto.rs b/tendermint/src/crypto.rs index 0f527b33a..23cecb461 100644 --- a/tendermint/src/crypto.rs +++ b/tendermint/src/crypto.rs @@ -1,139 +1,9 @@ -#[cfg(feature = "rust-crypto")] -mod default_provider; - -#[cfg(feature = "rust-crypto")] -pub use default_provider::DefaultCryptoProvider; - -use digest::{consts::U32, Digest, FixedOutput}; -use signature::{Signature, Signer, Verifier}; - -pub trait CryptoProvider { - type Sha256: Digest + FixedOutput; - - type EcdsaSecp256k1Signature: Signature; - type EcdsaSecp256k1Signer: Signer; - type EcdsaSecp256k1Verifier: Verifier; -} - -#[cfg(test)] -mod tests { - - /// A draft for an imlpementation of the HostFunctionManager for a specific chain (i.e. Polkadot/Substrate) - /// that uses the [`CryptoProvider`] trait - use core::marker::PhantomData; - use signature::{DigestSigner, DigestVerifier}; - - use digest::{consts::U32, Digest, FixedOutput}; - use signature::{Signature, Signer, Verifier}; - - use super::CryptoProvider; - - struct SubstrateHostFunctionsManager; - use k256::ecdsa::{SigningKey, VerifyingKey}; - - #[derive(Debug, Default)] - struct SubstrateSha256(sha2::Sha256); - - struct SubstrateSigner { - inner: SigningKey, - _d: PhantomData, - } - #[derive(Debug)] - struct SubstrateSignatureVerifier { - inner: VerifyingKey, - _d: PhantomData, - } +mod provider; - impl> SubstrateSignatureVerifier { - fn from_bytes(public_key: &[u8]) -> Result { - Ok(Self { - inner: VerifyingKey::from_sec1_bytes(public_key)?, - _d: PhantomData::default(), - }) - } - } +pub use provider::CryptoProvider; - impl, S: Signature> DigestVerifier - for SubstrateSignatureVerifier - where - VerifyingKey: DigestVerifier, - { - fn verify_digest(&self, digest: D, signature: &S) -> Result<(), ed25519::Error> { - self.inner.verify_digest(digest, signature) - } - } - - impl> Verifier - for SubstrateSignatureVerifier - where - VerifyingKey: DigestVerifier, - { - fn verify(&self, msg: &[u8], signature: &S) -> Result<(), ed25519::Error> { - let mut hasher = D::new(); - Digest::update(&mut hasher, msg); - self.verify_digest(hasher, signature) - } - } - - impl digest::OutputSizeUser for SubstrateSha256 { - type OutputSize = U32; - } - - impl digest::HashMarker for SubstrateSha256 {} - - impl digest::Update for SubstrateSha256 { - fn update(&mut self, data: &[u8]) { - digest::Update::update(&mut self.0, data) - } - } - - impl FixedOutput for SubstrateSha256 { - fn finalize_into(self, out: &mut digest::Output) { - *out = self.0.finalize(); - } - } - - impl Signer for SubstrateSigner - where - SigningKey: DigestSigner, - { - fn try_sign(&self, msg: &[u8]) -> Result { - let mut hasher = D::new(); - Digest::update(&mut hasher, msg); - self.inner.try_sign_digest(hasher) - } - } - - trait SubstrateHostFunctions: CryptoProvider { - fn sha2_256(preimage: &[u8]) -> [u8; 32]; - fn ed25519_verify(sig: &[u8], msg: &[u8], pub_key: &[u8]) -> Result<(), ()>; - fn secp256k1_verify(sig: &[u8], message: &[u8], public: &[u8]) -> Result<(), ()>; - } - - impl CryptoProvider for SubstrateHostFunctionsManager { - type Sha256 = SubstrateSha256; - - type EcdsaSecp256k1Signature = k256::ecdsa::Signature; - type EcdsaSecp256k1Signer = SubstrateSigner; - type EcdsaSecp256k1Verifier = SubstrateSignatureVerifier; - } - - impl SubstrateHostFunctions for SubstrateHostFunctionsManager { - fn sha2_256(preimage: &[u8]) -> [u8; 32] { - let mut hasher = Self::Sha256::new(); - hasher.update(preimage); - let result = hasher.finalize().try_into().unwrap(); - result - } - fn ed25519_verify(sig: &[u8], msg: &[u8], pub_key: &[u8]) -> Result<(), ()> { - let verifier = - <::EcdsaSecp256k1Verifier>::from_bytes(pub_key).unwrap(); - let signature = k256::ecdsa::Signature::from_der(sig).unwrap(); - Ok(verifier.verify(msg, &signature).unwrap()) - } +#[cfg(feature = "rust-crypto")] +mod default; - fn secp256k1_verify(_sig: &[u8], _message: &[u8], _public: &[u8]) -> Result<(), ()> { - unimplemented!() - } - } -} +#[cfg(feature = "rust-crypto")] +pub use default::DefaultCryptoProvider; diff --git a/tendermint/src/crypto/default_provider.rs b/tendermint/src/crypto/default.rs similarity index 100% rename from tendermint/src/crypto/default_provider.rs rename to tendermint/src/crypto/default.rs diff --git a/tendermint/src/crypto/provider.rs b/tendermint/src/crypto/provider.rs new file mode 100644 index 000000000..6d22e5941 --- /dev/null +++ b/tendermint/src/crypto/provider.rs @@ -0,0 +1,133 @@ +use digest::{consts::U32, Digest, FixedOutput}; +use signature::{Signature, Signer, Verifier}; + +pub trait CryptoProvider { + type Sha256: Digest + FixedOutput; + + type EcdsaSecp256k1Signature: Signature; + type EcdsaSecp256k1Signer: Signer; + type EcdsaSecp256k1Verifier: Verifier; +} + +#[cfg(test)] +mod tests { + + /// A draft for an imlpementation of the HostFunctionManager for a specific chain (i.e. Polkadot/Substrate) + /// that uses the [`CryptoProvider`] trait + use core::marker::PhantomData; + use signature::{DigestSigner, DigestVerifier}; + + use digest::{consts::U32, Digest, FixedOutput}; + use signature::{Signature, Signer, Verifier}; + + use super::CryptoProvider; + + struct SubstrateHostFunctionsManager; + use k256::ecdsa::{SigningKey, VerifyingKey}; + + #[derive(Debug, Default)] + struct SubstrateSha256(sha2::Sha256); + + struct SubstrateSigner { + inner: SigningKey, + _d: PhantomData, + } + #[derive(Debug)] + struct SubstrateSignatureVerifier { + inner: VerifyingKey, + _d: PhantomData, + } + + impl> SubstrateSignatureVerifier { + fn from_bytes(public_key: &[u8]) -> Result { + Ok(Self { + inner: VerifyingKey::from_sec1_bytes(public_key)?, + _d: PhantomData::default(), + }) + } + } + + impl, S: Signature> DigestVerifier + for SubstrateSignatureVerifier + where + VerifyingKey: DigestVerifier, + { + fn verify_digest(&self, digest: D, signature: &S) -> Result<(), ed25519::Error> { + self.inner.verify_digest(digest, signature) + } + } + + impl> Verifier + for SubstrateSignatureVerifier + where + VerifyingKey: DigestVerifier, + { + fn verify(&self, msg: &[u8], signature: &S) -> Result<(), ed25519::Error> { + let mut hasher = D::new(); + Digest::update(&mut hasher, msg); + self.verify_digest(hasher, signature) + } + } + + impl digest::OutputSizeUser for SubstrateSha256 { + type OutputSize = U32; + } + + impl digest::HashMarker for SubstrateSha256 {} + + impl digest::Update for SubstrateSha256 { + fn update(&mut self, data: &[u8]) { + digest::Update::update(&mut self.0, data) + } + } + + impl FixedOutput for SubstrateSha256 { + fn finalize_into(self, out: &mut digest::Output) { + *out = self.0.finalize(); + } + } + + impl Signer for SubstrateSigner + where + SigningKey: DigestSigner, + { + fn try_sign(&self, msg: &[u8]) -> Result { + let mut hasher = D::new(); + Digest::update(&mut hasher, msg); + self.inner.try_sign_digest(hasher) + } + } + + trait SubstrateHostFunctions: CryptoProvider { + fn sha2_256(preimage: &[u8]) -> [u8; 32]; + fn ed25519_verify(sig: &[u8], msg: &[u8], pub_key: &[u8]) -> Result<(), ()>; + fn secp256k1_verify(sig: &[u8], message: &[u8], public: &[u8]) -> Result<(), ()>; + } + + impl CryptoProvider for SubstrateHostFunctionsManager { + type Sha256 = SubstrateSha256; + + type EcdsaSecp256k1Signature = k256::ecdsa::Signature; + type EcdsaSecp256k1Signer = SubstrateSigner; + type EcdsaSecp256k1Verifier = SubstrateSignatureVerifier; + } + + impl SubstrateHostFunctions for SubstrateHostFunctionsManager { + fn sha2_256(preimage: &[u8]) -> [u8; 32] { + let mut hasher = Self::Sha256::new(); + hasher.update(preimage); + let result = hasher.finalize().try_into().unwrap(); + result + } + fn ed25519_verify(sig: &[u8], msg: &[u8], pub_key: &[u8]) -> Result<(), ()> { + let verifier = + <::EcdsaSecp256k1Verifier>::from_bytes(pub_key).unwrap(); + let signature = k256::ecdsa::Signature::from_der(sig).unwrap(); + Ok(verifier.verify(msg, &signature).unwrap()) + } + + fn secp256k1_verify(_sig: &[u8], _message: &[u8], _public: &[u8]) -> Result<(), ()> { + unimplemented!() + } + } +} From 2baed4f112f4a24ff85bf9b2b8fe05c3363207ed Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Fri, 18 Nov 2022 17:25:13 +0200 Subject: [PATCH 15/45] tendermint: generalize hash methods Add generic methods Header::hash_with and ValidatorSet::hash_with enabling a custom CryptoProvider to be plugged in for calculating hashes. The .hash() methods, implemented with the DefaultCryptoProvider, are feature-gated behind "rust-crypto". --- tendermint/src/block/header.rs | 15 ++++-- tendermint/src/crypto/provider.rs | 18 +++++-- tendermint/src/merkle.rs | 85 +++++++++++++++++-------------- tendermint/src/validator.rs | 16 ++++-- 4 files changed, 86 insertions(+), 48 deletions(-) diff --git a/tendermint/src/block/header.rs b/tendermint/src/block/header.rs index 5c4f73b93..19341426a 100644 --- a/tendermint/src/block/header.rs +++ b/tendermint/src/block/header.rs @@ -8,8 +8,7 @@ use tendermint_proto::{ }; use crate::{ - account, block, chain, merkle::simple_hash_from_byte_vectors, prelude::*, AppHash, Error, Hash, - Time, + account, block, chain, crypto::CryptoProvider, merkle, prelude::*, AppHash, Error, Hash, Time, }; /// Block `Header` values contain metadata about the block and about the @@ -164,8 +163,14 @@ impl From
for RawHeader { } impl Header { - /// Hash this header + /// Computes the hash of this header. + #[cfg(feature = "rust-crypto")] pub fn hash(&self) -> Hash { + self.hash_with::() + } + + /// Hash this header with a SHA256 hasher provided by a crypto provider. + pub fn hash_with(&self) -> Hash { // Note that if there is an encoding problem this will // panic (as the golang code would): // https://github.com/tendermint/tendermint/blob/134fe2896275bb926b49743c1e25493f6b24cc31/types/block.go#L393 @@ -194,7 +199,9 @@ impl Header { self.proposer_address.encode_vec().unwrap(), ]; - Hash::Sha256(simple_hash_from_byte_vectors(fields_bytes)) + Hash::Sha256(merkle::simple_hash_from_byte_vectors::( + &fields_bytes, + )) } } diff --git a/tendermint/src/crypto/provider.rs b/tendermint/src/crypto/provider.rs index 6d22e5941..5d443ec7a 100644 --- a/tendermint/src/crypto/provider.rs +++ b/tendermint/src/crypto/provider.rs @@ -1,8 +1,8 @@ -use digest::{consts::U32, Digest, FixedOutput}; +use digest::{consts::U32, Digest, FixedOutputReset}; use signature::{Signature, Signer, Verifier}; pub trait CryptoProvider { - type Sha256: Digest + FixedOutput; + type Sha256: Digest + FixedOutputReset; type EcdsaSecp256k1Signature: Signature; type EcdsaSecp256k1Signer: Signer; @@ -17,7 +17,7 @@ mod tests { use core::marker::PhantomData; use signature::{DigestSigner, DigestVerifier}; - use digest::{consts::U32, Digest, FixedOutput}; + use digest::{consts::U32, Digest, FixedOutput, FixedOutputReset, Reset}; use signature::{Signature, Signer, Verifier}; use super::CryptoProvider; @@ -87,6 +87,18 @@ mod tests { } } + impl Reset for SubstrateSha256 { + fn reset(&mut self) { + Reset::reset(&mut self.0) + } + } + + impl FixedOutputReset for SubstrateSha256 { + fn finalize_into_reset(&mut self, out: &mut digest::Output) { + *out = self.0.finalize_reset(); + } + } + impl Signer for SubstrateSigner where SigningKey: DigestSigner, diff --git a/tendermint/src/merkle.rs b/tendermint/src/merkle.rs index 23f0df8fd..14f72434a 100644 --- a/tendermint/src/merkle.rs +++ b/tendermint/src/merkle.rs @@ -4,7 +4,7 @@ pub mod proof; pub use proof::Proof; -use sha2::{Digest, Sha256}; +use digest::{consts::U32, Digest, FixedOutputReset}; use crate::prelude::*; @@ -17,21 +17,28 @@ pub type Hash = [u8; HASH_SIZE]; /// Compute a simple Merkle root from vectors of arbitrary byte vectors. /// The leaves of the tree are the bytes of the given byte vectors in /// the given order. -pub fn simple_hash_from_byte_vectors(byte_vecs: Vec>) -> Hash { - simple_hash_from_byte_slices_inner(byte_vecs.as_slice()) +pub fn simple_hash_from_byte_vectors + FixedOutputReset>( + byte_vecs: &[Vec], +) -> Hash { + let mut hasher = H::new(); + simple_hash_from_byte_vectors_inner(&mut hasher, byte_vecs) } -// recurse into subtrees -fn simple_hash_from_byte_slices_inner(byte_slices: &[Vec]) -> Hash { - let length = byte_slices.len(); +// Recurse into subtrees. +// Pre and post-conditions: the hasher is in the reset state before and after calling this function. +fn simple_hash_from_byte_vectors_inner + FixedOutputReset>( + hasher: &mut H, + byte_vecs: &[Vec], +) -> Hash { + let length = byte_vecs.len(); match length { - 0 => empty_hash(), - 1 => leaf_hash(byte_slices[0].as_slice()), + 0 => empty_hash(hasher), + 1 => leaf_hash(hasher, &byte_vecs[0]), _ => { let k = get_split_point(length); - let left = simple_hash_from_byte_slices_inner(&byte_slices[..k]); - let right = simple_hash_from_byte_slices_inner(&byte_slices[k..]); - inner_hash(&left, &right) + let left = simple_hash_from_byte_vectors_inner(hasher, &byte_vecs[..k]); + let right = simple_hash_from_byte_vectors_inner(hasher, &byte_vecs[k..]); + inner_hash(hasher, &left, &right) }, } } @@ -47,12 +54,10 @@ fn get_split_point(length: usize) -> usize { } // tmhash({}) -fn empty_hash() -> Hash { - // the empty string / byte slice - let empty = Vec::with_capacity(0); - - // hash it ! - let digest = Sha256::digest(&empty); +// Pre and post-conditions: the hasher is in the reset state before and after calling this function. +fn empty_hash + FixedOutputReset>(hasher: &mut H) -> Hash { + // Get the hash of an empty digest state + let digest = hasher.finalize_reset(); // copy the GenericArray out let mut hash_bytes = [0u8; HASH_SIZE]; @@ -61,14 +66,14 @@ fn empty_hash() -> Hash { } // tmhash(0x00 || leaf) -fn leaf_hash(bytes: &[u8]) -> Hash { - // make a new array starting with 0 and copy in the bytes - let mut leaf_bytes = Vec::with_capacity(bytes.len() + 1); - leaf_bytes.push(0x00); - leaf_bytes.extend_from_slice(bytes); +// Pre and post-conditions: the hasher is in the reset state before and after calling this function. +fn leaf_hash + FixedOutputReset>(hasher: &mut H, bytes: &[u8]) -> Hash { + // Feed the data to the hasher, prepended with 0x00 + Digest::update(hasher, &[0x00]); + Digest::update(hasher, bytes); - // hash it ! - let digest = Sha256::digest(&leaf_bytes); + // Finalize the digest, reset the hasher state + let digest = hasher.finalize_reset(); // copy the GenericArray out let mut hash_bytes = [0u8; HASH_SIZE]; @@ -77,15 +82,19 @@ fn leaf_hash(bytes: &[u8]) -> Hash { } // tmhash(0x01 || left || right) -fn inner_hash(left: &[u8], right: &[u8]) -> Hash { - // make a new array starting with 0x1 and copy in the bytes - let mut inner_bytes = Vec::with_capacity(left.len() + right.len() + 1); - inner_bytes.push(0x01); - inner_bytes.extend_from_slice(left); - inner_bytes.extend_from_slice(right); - - // hash it ! - let digest = Sha256::digest(&inner_bytes); +// Pre and post-conditions: the hasher is in the reset state before and after calling this function. +fn inner_hash + FixedOutputReset>( + hasher: &mut H, + left: &[u8], + right: &[u8], +) -> Hash { + // Feed the data to the hasher 0x1, then left and right data. + Digest::update(hasher, &[0x01]); + Digest::update(hasher, left); + Digest::update(hasher, right); + + // Finalize the digest, reset the hasher state + let digest = hasher.finalize_reset(); // copy the GenericArray out let mut hash_bytes = [0u8; HASH_SIZE]; @@ -95,6 +104,7 @@ fn inner_hash(left: &[u8], right: &[u8]) -> Hash { #[cfg(test)] mod tests { + use sha2::Sha256; use subtle_encoding::hex; use super::*; // TODO: use non-subtle ? @@ -120,7 +130,7 @@ mod tests { let empty_tree_root = &hex::decode(empty_tree_root_hex).unwrap(); let empty_tree: Vec> = vec![vec![]; 0]; - let root = simple_hash_from_byte_vectors(empty_tree); + let root = simple_hash_from_byte_vectors::(&empty_tree); assert_eq!(empty_tree_root, &root); } @@ -131,7 +141,7 @@ mod tests { let empty_leaf_root = &hex::decode(empty_leaf_root_hex).unwrap(); let one_empty_leaf: Vec> = vec![vec![]; 1]; - let root = simple_hash_from_byte_vectors(one_empty_leaf); + let root = simple_hash_from_byte_vectors::(&one_empty_leaf); assert_eq!(empty_leaf_root, &root); } @@ -143,7 +153,7 @@ mod tests { let leaf_root = &hex::decode(leaf_root_hex).unwrap(); let leaf_tree: Vec> = vec![leaf_string.as_bytes().to_vec(); 1]; - let root = simple_hash_from_byte_vectors(leaf_tree); + let root = simple_hash_from_byte_vectors::(&leaf_tree); assert_eq!(leaf_root, &root); } @@ -154,7 +164,8 @@ mod tests { let right_string = "N456"; let node_hash = &hex::decode(node_hash_hex).unwrap(); - let hash = inner_hash(left_string.as_bytes(), right_string.as_bytes()); + let mut hasher = Sha256::new(); + let hash = inner_hash(&mut hasher, left_string.as_bytes(), right_string.as_bytes()); assert_eq!(node_hash, &hash); } } diff --git a/tendermint/src/validator.rs b/tendermint/src/validator.rs index ad2993782..b6ec49432 100644 --- a/tendermint/src/validator.rs +++ b/tendermint/src/validator.rs @@ -13,8 +13,8 @@ use tendermint_proto::{ }; use crate::{ - account, hash::Hash, merkle, prelude::*, public_key::deserialize_public_key, vote, Error, - PublicKey, Signature, + account, crypto::CryptoProvider, hash::Hash, merkle, prelude::*, + public_key::deserialize_public_key, vote, Error, PublicKey, Signature, }; /// Validator set contains a vector of validators @@ -134,15 +134,23 @@ impl Set { .cloned() } - /// Compute the hash of this validator set + /// Compute the hash of this validator set. + #[cfg(feature = "rust-crypto")] pub fn hash(&self) -> Hash { + self.hash_with::() + } + + /// Hash this header with a SHA256 hasher provided by a crypto provider. + pub fn hash_with(&self) -> Hash { let validator_bytes: Vec> = self .validators() .iter() .map(|validator| validator.hash_bytes()) .collect(); - Hash::Sha256(merkle::simple_hash_from_byte_vectors(validator_bytes)) + Hash::Sha256(merkle::simple_hash_from_byte_vectors::( + &validator_bytes, + )) } } From a03236432763f2be9e9d7b6b12046c9d74073f87 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Fri, 18 Nov 2022 17:57:35 +0200 Subject: [PATCH 16/45] tendermint: eliminate get_split_point helper 2usize.next_power_of_two() / 1 == 1, and we have eliminated the other two explicitly matched cases at the call site, so the non-catchall match branches and the panics are dead code. --- tendermint/src/merkle.rs | 41 ++++++++++++++-------------------------- 1 file changed, 14 insertions(+), 27 deletions(-) diff --git a/tendermint/src/merkle.rs b/tendermint/src/merkle.rs index 14f72434a..516bcb004 100644 --- a/tendermint/src/merkle.rs +++ b/tendermint/src/merkle.rs @@ -35,24 +35,14 @@ fn simple_hash_from_byte_vectors_inner + FixedOutput 0 => empty_hash(hasher), 1 => leaf_hash(hasher, &byte_vecs[0]), _ => { - let k = get_split_point(length); - let left = simple_hash_from_byte_vectors_inner(hasher, &byte_vecs[..k]); - let right = simple_hash_from_byte_vectors_inner(hasher, &byte_vecs[k..]); + let split = length.next_power_of_two() / 2; + let left = simple_hash_from_byte_vectors_inner(hasher, &byte_vecs[..split]); + let right = simple_hash_from_byte_vectors_inner(hasher, &byte_vecs[split..]); inner_hash(hasher, &left, &right) }, } } -// returns the largest power of 2 less than length -fn get_split_point(length: usize) -> usize { - match length { - 0 => panic!("tree is empty!"), - 1 => panic!("tree has only one element!"), - 2 => 1, - _ => length.next_power_of_two() / 2, - } -} - // tmhash({}) // Pre and post-conditions: the hasher is in the reset state before and after calling this function. fn empty_hash + FixedOutputReset>(hasher: &mut H) -> Hash { @@ -109,20 +99,6 @@ mod tests { use super::*; // TODO: use non-subtle ? - #[test] - fn test_get_split_point() { - assert_eq!(get_split_point(2), 1); - assert_eq!(get_split_point(3), 2); - assert_eq!(get_split_point(4), 2); - assert_eq!(get_split_point(5), 4); - assert_eq!(get_split_point(10), 8); - assert_eq!(get_split_point(20), 16); - assert_eq!(get_split_point(100), 64); - assert_eq!(get_split_point(255), 128); - assert_eq!(get_split_point(256), 128); - assert_eq!(get_split_point(257), 256); - } - #[test] fn test_rfc6962_empty_tree() { let empty_tree_root_hex = @@ -168,4 +144,15 @@ mod tests { let hash = inner_hash(&mut hasher, left_string.as_bytes(), right_string.as_bytes()); assert_eq!(node_hash, &hash); } + + #[test] + fn test_rfc6962_tree_of_2() { + let node_hash_hex = "dc9a0536ff2e196d5a628a5bf377ab247bbddf83342be39699461c1e766e6646"; + let left = b"N123".to_vec(); + let right = b"N456".to_vec(); + + let node_hash = &hex::decode(node_hash_hex).unwrap(); + let hash = simple_hash_from_byte_vectors::(&[left, right]); + assert_eq!(node_hash, &hash); + } } From f26474250c5a9f112d6df85d0e6d994847b074d1 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Sun, 20 Nov 2022 17:53:01 +0200 Subject: [PATCH 17/45] light-client: eliminate Hasher The Hasher trait is obviated by CryptoProvider. Also, we could do with less dynamic dispatching. --- light-client-verifier/Cargo.toml | 3 +- light-client-verifier/src/operations.rs | 3 - .../src/operations/commit_validator.rs | 41 ++++++++---- .../src/operations/hasher.rs | 24 ------- light-client-verifier/src/predicates.rs | 66 ++++++------------- light-client-verifier/src/verifier.rs | 48 +++++--------- light-client/Cargo.toml | 3 +- light-client/src/builder/light_client.rs | 42 ++++++------ light-client/src/fork_detector.rs | 28 ++++---- light-client/src/light_client.rs | 13 +--- light-client/src/supervisor.rs | 6 +- light-client/tests/backward.rs | 3 - light-client/tests/light_client.rs | 3 - light-client/tests/supervisor.rs | 4 +- tendermint/src/crypto/default.rs | 3 +- tendermint/src/crypto/provider.rs | 6 +- 16 files changed, 116 insertions(+), 180 deletions(-) delete mode 100644 light-client-verifier/src/operations/hasher.rs diff --git a/light-client-verifier/Cargo.toml b/light-client-verifier/Cargo.toml index d03b97464..3b0b71def 100644 --- a/light-client-verifier/Cargo.toml +++ b/light-client-verifier/Cargo.toml @@ -23,7 +23,8 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [features] -default = ["flex-error/std", "flex-error/eyre_tracer"] +default = ["rust-crypto", "flex-error/std", "flex-error/eyre_tracer"] +rust-crypto = ["tendermint/rust-crypto"] [dependencies] tendermint = { version = "0.26.0", path = "../tendermint", default-features = false } diff --git a/light-client-verifier/src/operations.rs b/light-client-verifier/src/operations.rs index f7c43429a..e45ad2729 100644 --- a/light-client-verifier/src/operations.rs +++ b/light-client-verifier/src/operations.rs @@ -1,8 +1,5 @@ //! Crypto function traits allowing mocking out during testing -pub mod hasher; -pub use self::hasher::*; - pub mod voting_power; pub use self::voting_power::*; diff --git a/light-client-verifier/src/operations/commit_validator.rs b/light-client-verifier/src/operations/commit_validator.rs index 23c9b1790..d52ba93ce 100644 --- a/light-client-verifier/src/operations/commit_validator.rs +++ b/light-client-verifier/src/operations/commit_validator.rs @@ -1,10 +1,9 @@ //! Provides an interface and default implementation for the `CommitValidator` operation -use tendermint::block::CommitSig; +use tendermint::{block::CommitSig, crypto::CryptoProvider}; use crate::{ errors::VerificationError, - operations::{Hasher, ProdHasher}, types::{SignedHeader, ValidatorSet}, }; @@ -25,27 +24,43 @@ pub trait CommitValidator: Send + Sync { ) -> Result<(), VerificationError>; } -/// Production-ready implementation of a commit validator -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct ProdCommitValidator { - hasher: ProdHasher, +/// Generic implementation of a commit validator, with cryptographic primitives +/// provided via the [`CryptoProvider`] trait. +#[derive(Copy, Clone, Debug)] +pub struct ProvidedCommitValidator { + _provider: C, } -impl ProdCommitValidator { +/// Production-ready implementation of a commit validator. +#[cfg(feature = "rust-crypto")] +pub type ProdCommitValidator = ProvidedCommitValidator; + +impl ProvidedCommitValidator +where + C: CryptoProvider + Default, +{ /// Create a new commit validator using the given [`Hasher`] /// to compute the hash of headers and validator sets. - pub fn new(hasher: ProdHasher) -> Self { - Self { hasher } + pub fn new() -> Self { + Self { + _provider: Default::default(), + } } } -impl Default for ProdCommitValidator { +impl Default for ProvidedCommitValidator +where + C: CryptoProvider + Default, +{ fn default() -> Self { - Self::new(ProdHasher::default()) + Self::new() } } -impl CommitValidator for ProdCommitValidator { +impl CommitValidator for ProvidedCommitValidator +where + C: CryptoProvider + Send + Sync, +{ fn validate( &self, signed_header: &SignedHeader, @@ -96,7 +111,7 @@ impl CommitValidator for ProdCommitValidator { if validator_set.validator(*validator_address).is_none() { return Err(VerificationError::faulty_signer( *validator_address, - self.hasher.hash_validator_set(validator_set), + validator_set.hash_with::(), )); } } diff --git a/light-client-verifier/src/operations/hasher.rs b/light-client-verifier/src/operations/hasher.rs deleted file mode 100644 index 285defa2a..000000000 --- a/light-client-verifier/src/operations/hasher.rs +++ /dev/null @@ -1,24 +0,0 @@ -//! Provides an interface and default implementation for the `Hasher` operation - -use tendermint::Hash; - -use crate::types::{Header, ValidatorSet}; - -/// Hashing for headers and validator sets -pub trait Hasher: Send + Sync { - /// Hash the given header - fn hash_header(&self, header: &Header) -> Hash { - header.hash() - } - - /// Hash the given validator set - fn hash_validator_set(&self, validator_set: &ValidatorSet) -> Hash { - validator_set.hash() - } -} - -/// Default implementation of a hasher -#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] -pub struct ProdHasher; - -impl Hasher for ProdHasher {} diff --git a/light-client-verifier/src/predicates.rs b/light-client-verifier/src/predicates.rs index 0c8787d4f..888e2fc33 100644 --- a/light-client-verifier/src/predicates.rs +++ b/light-client-verifier/src/predicates.rs @@ -2,20 +2,23 @@ use core::time::Duration; -use tendermint::{block::Height, hash::Hash}; +use tendermint::{block::Height, crypto::CryptoProvider, hash::Hash}; use crate::{ errors::VerificationError, - operations::{CommitValidator, Hasher, VotingPowerCalculator}, + operations::{CommitValidator, VotingPowerCalculator}, prelude::*, types::{Header, SignedHeader, Time, TrustThreshold, ValidatorSet}, }; /// Production predicates, using the default implementation /// of the `VerificationPredicates` trait. +#[cfg(feature = "rust-crypto")] #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] pub struct ProdPredicates; -impl VerificationPredicates for ProdPredicates {} + +#[cfg(feature = "rust-crypto")] +impl VerificationPredicates for ProdPredicates {} /// Defines the various predicates used to validate and verify light blocks. /// @@ -23,16 +26,15 @@ impl VerificationPredicates for ProdPredicates {} /// /// This enables test implementations to only override a single method rather than /// have to re-define every predicate. -pub trait VerificationPredicates: Send + Sync { +pub trait VerificationPredicates: Send + Sync { /// Compare the provided validator_set_hash against the hash produced from hashing the validator /// set. fn validator_sets_match( &self, validators: &ValidatorSet, header_validators_hash: Hash, - hasher: &dyn Hasher, ) -> Result<(), VerificationError> { - let validators_hash = hasher.hash_validator_set(validators); + let validators_hash = validators.hash_with::(); if header_validators_hash == validators_hash { Ok(()) } else { @@ -48,9 +50,8 @@ pub trait VerificationPredicates: Send + Sync { &self, next_validators: &ValidatorSet, header_next_validators_hash: Hash, - hasher: &dyn Hasher, ) -> Result<(), VerificationError> { - let next_validators_hash = hasher.hash_validator_set(next_validators); + let next_validators_hash = next_validators.hash_with::(); if header_next_validators_hash == next_validators_hash { Ok(()) } else { @@ -66,9 +67,8 @@ pub trait VerificationPredicates: Send + Sync { &self, header: &Header, commit_hash: Hash, - hasher: &dyn Hasher, ) -> Result<(), VerificationError> { - let header_hash = hasher.hash_header(header); + let header_hash = header.hash_with::(); if header_hash == commit_hash { Ok(()) } else { @@ -203,7 +203,7 @@ pub trait VerificationPredicates: Send + Sync { } } -#[cfg(test)] +#[cfg(all(test, feature = "rust-crypto"))] mod tests { use core::{convert::TryInto, time::Duration}; @@ -216,9 +216,7 @@ mod tests { use crate::{ errors::{VerificationError, VerificationErrorDetail}, - operations::{ - Hasher, ProdCommitValidator, ProdHasher, ProdVotingPowerCalculator, VotingPowerTally, - }, + operations::{ProdCommitValidator, ProdVotingPowerCalculator, VotingPowerTally}, predicates::{ProdPredicates, VerificationPredicates}, prelude::*, types::{LightBlock, TrustThreshold}, @@ -348,14 +346,12 @@ mod tests { let bad_validator_set = ValidatorSet::new(vec!["bad-val"]).generate().unwrap(); let vp = ProdPredicates::default(); - let hasher = ProdHasher::default(); // Test positive case // 1. For predicate: validator_sets_match let val_sets_match_ok = vp.validator_sets_match( &light_block.validators, light_block.signed_header.header.validators_hash, - &hasher, ); assert!(val_sets_match_ok.is_ok()); @@ -364,7 +360,6 @@ mod tests { let next_val_sets_match_ok = vp.next_validators_match( &light_block.next_validators, light_block.signed_header.header.next_validators_hash, - &hasher, ); assert!(next_val_sets_match_ok.is_ok()); @@ -376,7 +371,6 @@ mod tests { let val_sets_match_err = vp.validator_sets_match( &light_block.validators, light_block.signed_header.header.validators_hash, - &hasher, ); match val_sets_match_err { @@ -385,10 +379,7 @@ mod tests { e.header_validators_hash, light_block.signed_header.header.validators_hash ); - assert_eq!( - e.validators_hash, - hasher.hash_validator_set(&light_block.validators) - ); + assert_eq!(e.validators_hash, light_block.validators.hash()); }, _ => panic!("expected InvalidValidatorSet error"), } @@ -398,7 +389,6 @@ mod tests { let next_val_sets_match_err = vp.next_validators_match( &light_block.next_validators, light_block.signed_header.header.next_validators_hash, - &hasher, ); match next_val_sets_match_err { @@ -407,10 +397,7 @@ mod tests { e.header_next_validators_hash, light_block.signed_header.header.next_validators_hash ); - assert_eq!( - e.next_validators_hash, - hasher.hash_validator_set(&light_block.next_validators) - ); + assert_eq!(e.next_validators_hash, light_block.next_validators.hash()); }, _ => panic!("expected InvalidNextValidatorSet error"), } @@ -424,14 +411,10 @@ mod tests { .signed_header; let vp = ProdPredicates::default(); - let hasher = ProdHasher::default(); // 1. ensure valid signed header verifies - let result_ok = vp.header_matches_commit( - &signed_header.header, - signed_header.commit.block_id.hash, - &hasher, - ); + let result_ok = + vp.header_matches_commit(&signed_header.header, signed_header.commit.block_id.hash); assert!(result_ok.is_ok()); @@ -440,14 +423,11 @@ mod tests { "15F15EF50BDE2018F4B129A827F90C18222C757770C8295EB8EE7BF50E761BC0" .parse() .unwrap(); - let result_err = vp.header_matches_commit( - &signed_header.header, - signed_header.commit.block_id.hash, - &hasher, - ); + let result_err = + vp.header_matches_commit(&signed_header.header, signed_header.commit.block_id.hash); // 3. ensure it fails with: VerificationVerificationError::InvalidCommitValue - let header_hash = hasher.hash_header(&signed_header.header); + let header_hash = signed_header.header.hash(); match result_err { Err(VerificationError(VerificationErrorDetail::InvalidCommitValue(e), _)) => { @@ -466,8 +446,7 @@ mod tests { let val_set = light_block.validators; let vp = ProdPredicates::default(); - let hasher = ProdHasher::default(); - let commit_validator = ProdCommitValidator::new(hasher); + let commit_validator = ProdCommitValidator::new(); // Test scenarios --> // 1. valid commit - must result "Ok" @@ -540,10 +519,7 @@ mod tests { .unwrap() ); - assert_eq!( - e.validator_set, - hasher.hash_validator_set(&val_set_with_faulty_signer) - ); + assert_eq!(e.validator_set, val_set_with_faulty_signer.hash()); }, _ => panic!("expected FaultySigner error"), } diff --git a/light-client-verifier/src/verifier.rs b/light-client-verifier/src/verifier.rs index 8525cf666..9206324b1 100644 --- a/light-client-verifier/src/verifier.rs +++ b/light-client-verifier/src/verifier.rs @@ -2,11 +2,12 @@ use preds::{ProdPredicates, VerificationPredicates}; use serde::{Deserialize, Serialize}; +use tendermint::crypto::CryptoProvider; use crate::{ errors::{ErrorExt, VerificationError, VerificationErrorDetail}, operations::{ - voting_power::VotingPowerTally, CommitValidator, Hasher, ProdCommitValidator, ProdHasher, + voting_power::VotingPowerTally, CommitValidator, ProdCommitValidator, ProdVotingPowerCalculator, VotingPowerCalculator, }, options::Options, @@ -70,55 +71,38 @@ macro_rules! verdict { /// Predicate verifier encapsulating components necessary to facilitate /// verification. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, Default, PartialEq, Eq)] pub struct PredicateVerifier { predicates: P, voting_power_calculator: C, commit_validator: V, - hasher: H, -} - -impl Default for PredicateVerifier -where - P: Default, - C: Default, - V: Default, - H: Default, -{ - fn default() -> Self { - Self { - predicates: P::default(), - voting_power_calculator: C::default(), - commit_validator: V::default(), - hasher: H::default(), - } - } + crypto_provider: H, } impl PredicateVerifier where - P: VerificationPredicates, + P: VerificationPredicates, C: VotingPowerCalculator, V: CommitValidator, - H: Hasher, + H: CryptoProvider, { /// Constructor. - pub fn new(predicates: P, voting_power_calculator: C, commit_validator: V, hasher: H) -> Self { + pub fn new(predicates: P, voting_power_calculator: C, commit_validator: V) -> Self { Self { predicates, voting_power_calculator, commit_validator, - hasher, + crypto_provider: Default::default(), } } } impl Verifier for PredicateVerifier where - P: VerificationPredicates, + P: VerificationPredicates, C: VotingPowerCalculator, V: CommitValidator, - H: Hasher, + H: CryptoProvider, { /// Validate the given light block state. /// @@ -159,7 +143,6 @@ where verdict!(self.predicates.validator_sets_match( untrusted.validators, untrusted.signed_header.header.validators_hash, - &self.hasher, )); // TODO(thane): Is this check necessary for IBC? @@ -168,7 +151,6 @@ where verdict!(self.predicates.next_validators_match( untrusted_next_validators, untrusted.signed_header.header.next_validators_hash, - &self.hasher, )); } @@ -176,7 +158,6 @@ where verdict!(self.predicates.header_matches_commit( &untrusted.signed_header.header, untrusted.signed_header.commit.block_id.hash, - &self.hasher, )); // Additional implementation specific validation @@ -228,6 +209,11 @@ where } } +#[cfg(feature = "rust-crypto")] /// The default production implementation of the [`PredicateVerifier`]. -pub type ProdVerifier = - PredicateVerifier; +pub type ProdVerifier = PredicateVerifier< + ProdPredicates, + ProdVotingPowerCalculator, + ProdCommitValidator, + tendermint::crypto::DefaultCryptoProvider, +>; diff --git a/light-client/Cargo.toml b/light-client/Cargo.toml index 514f4b6c0..4ee55e5eb 100644 --- a/light-client/Cargo.toml +++ b/light-client/Cargo.toml @@ -26,7 +26,8 @@ rustdoc-args = ["--cfg", "docsrs"] [features] default = ["rpc-client", "flex-error/std", "flex-error/eyre_tracer"] -rpc-client = ["tokio", "tendermint-rpc/http-client"] +rpc-client = ["tokio", "rust-crypto", "tendermint-rpc/http-client"] +rust-crypto = ["tendermint/rust-crypto", "tendermint-light-client-verifier/rust-crypto"] secp256k1 = ["tendermint/secp256k1", "tendermint-rpc/secp256k1"] lightstore-sled = ["sled"] unstable = [] diff --git a/light-client/src/builder/light_client.rs b/light-client/src/builder/light_client.rs index a428a5a2b..afacf8093 100644 --- a/light-client/src/builder/light_client.rs +++ b/light-client/src/builder/light_client.rs @@ -1,13 +1,15 @@ //! DSL for building a light client [`Instance`] -use tendermint::{block::Height, Hash}; +use tendermint::{block::Height, crypto::CryptoProvider, Hash}; + #[cfg(feature = "rpc-client")] use { crate::components::clock::SystemClock, crate::components::io::ProdIo, crate::components::scheduler, - crate::verifier::{operations::ProdHasher, predicates::ProdPredicates, ProdVerifier}, + crate::verifier::{predicates::ProdPredicates, ProdVerifier}, core::time::Duration, + tendermint::crypto::DefaultCryptoProvider, tendermint_rpc as rpc, }; @@ -23,7 +25,6 @@ use crate::{ store::LightStore, supervisor::Instance, verifier::{ - operations::Hasher, options::Options, predicates::VerificationPredicates, types::{LightBlock, PeerId, Status}, @@ -39,30 +40,28 @@ pub struct HasTrustedState; /// Builder for a light client [`Instance`] #[must_use] -pub struct LightClientBuilder { +pub struct LightClientBuilder { peer_id: PeerId, options: Options, io: Box, clock: Box, - hasher: Box, verifier: Box, scheduler: Box, - predicates: Box, + predicates: Box>, light_store: Box, #[allow(dead_code)] state: State, } -impl LightClientBuilder { +impl LightClientBuilder { /// Private method to move from one state to another - fn with_state(self, state: Next) -> LightClientBuilder { + fn with_state(self, state: Next) -> LightClientBuilder { LightClientBuilder { peer_id: self.peer_id, options: self.options, io: self.io, clock: self.clock, - hasher: self.hasher, verifier: self.verifier, scheduler: self.scheduler, predicates: self.predicates, @@ -72,9 +71,9 @@ impl LightClientBuilder { } } -impl LightClientBuilder { +#[cfg(feature = "rpc-client")] +impl LightClientBuilder { /// Initialize a builder for a production (non-mock) light client. - #[cfg(feature = "rpc-client")] pub fn prod( peer_id: PeerId, rpc_client: rpc::HttpClient, @@ -87,30 +86,30 @@ impl LightClientBuilder { options, light_store, Box::new(ProdIo::new(peer_id, rpc_client, timeout)), - Box::new(ProdHasher), Box::new(SystemClock), Box::new(ProdVerifier::default()), Box::new(scheduler::basic_bisecting_schedule), Box::new(ProdPredicates), ) } +} +impl LightClientBuilder { /// Initialize a builder for a custom light client, by providing all dependencies upfront. + // TODO: redesign this, it's a builder API! #[allow(clippy::too_many_arguments)] pub fn custom( peer_id: PeerId, options: Options, light_store: Box, io: Box, - hasher: Box, clock: Box, verifier: Box, scheduler: Box, - predicates: Box, + predicates: Box>, ) -> Self { Self { peer_id, - hasher, io, verifier, light_store, @@ -126,7 +125,7 @@ impl LightClientBuilder { fn trust_light_block( mut self, trusted_state: LightBlock, - ) -> Result, Error> { + ) -> Result, Error> { self.validate(&trusted_state)?; // TODO(liamsi, romac): it is unclear if this should be Trusted or only Verified @@ -137,7 +136,7 @@ impl LightClientBuilder { /// Keep using the latest verified or trusted block in the light store. /// Such a block must exists otherwise this will fail. - pub fn trust_from_store(self) -> Result, Error> { + pub fn trust_from_store(self) -> Result, Error> { let trusted_state = self .light_store .highest_trusted_or_verified() @@ -151,7 +150,7 @@ impl LightClientBuilder { self, trusted_height: Height, trusted_hash: Hash, - ) -> Result, Error> { + ) -> Result, Error> { let trusted_state = self .io .fetch_light_block(AtHeight::At(trusted_height)) @@ -164,7 +163,7 @@ impl LightClientBuilder { )); } - let header_hash = self.hasher.hash_header(&trusted_state.signed_header.header); + let header_hash = trusted_state.signed_header.header.hash_with::(); if header_hash != trusted_hash { return Err(Error::hash_mismatch(trusted_hash, header_hash)); @@ -189,7 +188,6 @@ impl LightClientBuilder { .validator_sets_match( &light_block.validators, light_block.signed_header.header.validators_hash, - &*self.hasher, ) .map_err(Error::invalid_light_block)?; @@ -197,7 +195,6 @@ impl LightClientBuilder { .next_validators_match( &light_block.next_validators, light_block.signed_header.header.next_validators_hash, - &*self.hasher, ) .map_err(Error::invalid_light_block)?; @@ -205,7 +202,7 @@ impl LightClientBuilder { } } -impl LightClientBuilder { +impl LightClientBuilder { /// Build the light client [`Instance`]. #[must_use] pub fn build(self) -> Instance { @@ -220,7 +217,6 @@ impl LightClientBuilder { self.clock, self.scheduler, self.verifier, - self.hasher, self.io, ); diff --git a/light-client/src/fork_detector.rs b/light-client/src/fork_detector.rs index 3788f4822..6876fcbcc 100644 --- a/light-client/src/fork_detector.rs +++ b/light-client/src/fork_detector.rs @@ -1,5 +1,7 @@ //! Fork detection data structures and implementation. +use tendermint::crypto::CryptoProvider; + use crate::{ errors::{Error, ErrorDetail}, state::State, @@ -7,7 +9,6 @@ use crate::{ supervisor::Instance, verifier::{ errors::ErrorExt, - operations::{Hasher, ProdHasher}, types::{LightBlock, PeerId, Status}, }, }; @@ -61,26 +62,29 @@ pub trait ForkDetector: Send + Sync { /// - If the verification succeeds, we have a real fork /// - If verification fails because of lack of trust, we have a potential fork. /// - If verification fails for any other reason, the witness is deemed faulty. -pub struct ProdForkDetector { - hasher: Box, +pub struct ProvidedForkDetector { + _crypto: H, } -impl ProdForkDetector { +#[cfg(feature = "rust-crypto")] +pub type ProdForkDetector = ProvidedForkDetector; + +impl ProvidedForkDetector { /// Construct a new fork detector that will use the given header hasher. - pub fn new(hasher: impl Hasher + 'static) -> Self { + pub fn new() -> Self { Self { - hasher: Box::new(hasher), + _crypto: Default::default(), } } } -impl Default for ProdForkDetector { +impl Default for ProvidedForkDetector { fn default() -> Self { - Self::new(ProdHasher) + Self::new() } } -impl ForkDetector for ProdForkDetector { +impl ForkDetector for ProvidedForkDetector { /// Perform fork detection. See the documentation `ProdForkDetector` for details. fn detect_forks( &self, @@ -88,9 +92,7 @@ impl ForkDetector for ProdForkDetector { trusted_block: &LightBlock, witnesses: Vec<&Instance>, ) -> Result { - let primary_hash = self - .hasher - .hash_header(&verified_block.signed_header.header); + let primary_hash = verified_block.signed_header.header.hash_with::(); let mut forks = Vec::with_capacity(witnesses.len()); @@ -101,7 +103,7 @@ impl ForkDetector for ProdForkDetector { .light_client .get_or_fetch_block(verified_block.height(), &mut state)?; - let witness_hash = self.hasher.hash_header(&witness_block.signed_header.header); + let witness_hash = witness_block.signed_header.header.hash_with::(); if primary_hash == witness_hash { // Hashes match, continue with next witness, if any. diff --git a/light-client/src/light_client.rs b/light-client/src/light_client.rs index 3ebb58498..7e94bcbc8 100644 --- a/light-client/src/light_client.rs +++ b/light-client/src/light_client.rs @@ -14,7 +14,6 @@ use crate::{ errors::Error, state::State, verifier::{ - operations::Hasher, types::{Height, LightBlock, PeerId, Status}, Verdict, Verifier, }, @@ -40,10 +39,6 @@ pub struct LightClient { scheduler: Box, verifier: Box, io: Box, - - // Only used in verify_backwards when "unstable" feature is enabled - #[allow(dead_code)] - hasher: Box, } impl fmt::Debug for LightClient { @@ -63,7 +58,6 @@ impl LightClient { clock: impl Clock + 'static, scheduler: impl Scheduler + 'static, verifier: impl Verifier + 'static, - hasher: impl Hasher + 'static, io: impl Io + 'static, ) -> Self { Self { @@ -72,7 +66,6 @@ impl LightClient { clock: Box::new(clock), scheduler: Box::new(scheduler), verifier: Box::new(verifier), - hasher: Box::new(hasher), io: Box::new(io), } } @@ -84,7 +77,6 @@ impl LightClient { clock: Box, scheduler: Box, verifier: Box, - hasher: Box, io: Box, ) -> Self { Self { @@ -94,7 +86,6 @@ impl LightClient { scheduler, verifier, io, - hasher, } } @@ -326,13 +317,14 @@ impl LightClient { for height in heights { let (current, _status) = self.get_or_fetch_block(height, state)?; + /* let latest_last_block_id = latest .signed_header .header .last_block_id .ok_or_else(|| Error::missing_last_block_id(latest.height()))?; - let current_hash = self.hasher.hash_header(¤t.signed_header.header); + let current_hash = current.signed_header.header.hash_with::(); if current_hash != latest_last_block_id.hash { return Err(Error::invalid_adjacent_headers( @@ -340,6 +332,7 @@ impl LightClient { latest_last_block_id.hash, )); } + */ // `latest` and `current` are linked together by `last_block_id`, // therefore it is not relevant which we verified first. diff --git a/light-client/src/supervisor.rs b/light-client/src/supervisor.rs index 297f8463d..dbd9e82df 100644 --- a/light-client/src/supervisor.rs +++ b/light-client/src/supervisor.rs @@ -451,7 +451,7 @@ mod tests { fork_detector::ProdForkDetector, store::{memory::MemoryStore, LightStore}, tests::{MockClock, MockEvidenceReporter, MockIo, TrustOptions}, - verifier::{operations::ProdHasher, options::Options, types::Time, ProdVerifier}, + verifier::{options::Options, types::Time, ProdVerifier}, }; trait IntoLightBlock { @@ -497,10 +497,8 @@ mod tests { let verifier = ProdVerifier::default(); let clock = MockClock { now }; let scheduler = scheduler::basic_bisecting_schedule; - let hasher = ProdHasher::default(); - let light_client = - LightClient::new(peer_id, options, clock, scheduler, verifier, hasher, io); + let light_client = LightClient::new(peer_id, options, clock, scheduler, verifier, io); Instance::new(light_client, state) } diff --git a/light-client/tests/backward.rs b/light-client/tests/backward.rs index 48c13a990..92d878e50 100644 --- a/light-client/tests/backward.rs +++ b/light-client/tests/backward.rs @@ -15,7 +15,6 @@ use tendermint_light_client::{ store::{memory::MemoryStore, LightStore}, tests::{MockClock, MockIo}, verifier::{ - operations::ProdHasher, options::Options, types::{Height, LightBlock, Status}, ProdVerifier, @@ -79,7 +78,6 @@ fn make(chain: LightChain, trusted_height: Height) -> (LightClient, State) { }; let verifier = ProdVerifier::default(); - let hasher = ProdHasher::default(); let light_client = LightClient::new( primary, @@ -87,7 +85,6 @@ fn make(chain: LightChain, trusted_height: Height) -> (LightClient, State) { clock, scheduler::basic_bisecting_schedule, verifier, - hasher, io, ); diff --git a/light-client/tests/light_client.rs b/light-client/tests/light_client.rs index 1c0612de0..0ad03c2b8 100644 --- a/light-client/tests/light_client.rs +++ b/light-client/tests/light_client.rs @@ -11,7 +11,6 @@ use tendermint_light_client::{ store::{memory::MemoryStore, LightStore}, tests::*, verifier::{ - operations::ProdHasher, options::Options, types::{LightBlock, Status}, ProdVerifier, @@ -64,7 +63,6 @@ fn run_test(tc: LightClientTest) -> BisectionTestResult { }; let verifier = ProdVerifier::default(); - let hasher = ProdHasher::default(); let mut light_client = LightClient::new( primary, @@ -72,7 +70,6 @@ fn run_test(tc: LightClientTest) -> BisectionTestResult { clock, scheduler::basic_bisecting_schedule, verifier, - hasher, io.clone(), ); diff --git a/light-client/tests/supervisor.rs b/light-client/tests/supervisor.rs index 1cd0182db..3726cb237 100644 --- a/light-client/tests/supervisor.rs +++ b/light-client/tests/supervisor.rs @@ -13,7 +13,6 @@ use tendermint_light_client::{ supervisor::{Handle, Instance, Supervisor}, tests::{LightClientTest, MockClock, MockEvidenceReporter, MockIo, TrustOptions}, verifier::{ - operations::ProdHasher, options::Options, types::{LightBlock, PeerId, Status, Time}, ProdVerifier, @@ -45,10 +44,9 @@ fn make_instance(peer_id: PeerId, trust_options: TrustOptions, io: MockIo, now: let clock = MockClock { now }; let verifier = ProdVerifier::default(); - let hasher = ProdHasher::default(); let scheduler = scheduler::basic_bisecting_schedule; - let light_client = LightClient::new(peer_id, options, clock, scheduler, verifier, hasher, io); + let light_client = LightClient::new(peer_id, options, clock, scheduler, verifier, io); Instance::new(light_client, state) } diff --git a/tendermint/src/crypto/default.rs b/tendermint/src/crypto/default.rs index 5134f7e45..d04af6025 100644 --- a/tendermint/src/crypto/default.rs +++ b/tendermint/src/crypto/default.rs @@ -3,7 +3,8 @@ use sha2::Sha256; use super::CryptoProvider; /// A batteries-included provider of cryptograpic functions. -pub struct DefaultCryptoProvider {} +#[derive(Copy, Clone, Debug, Default)] +pub struct DefaultCryptoProvider; impl CryptoProvider for DefaultCryptoProvider { type Sha256 = Sha256; diff --git a/tendermint/src/crypto/provider.rs b/tendermint/src/crypto/provider.rs index 5d443ec7a..fbda59d48 100644 --- a/tendermint/src/crypto/provider.rs +++ b/tendermint/src/crypto/provider.rs @@ -1,7 +1,7 @@ use digest::{consts::U32, Digest, FixedOutputReset}; use signature::{Signature, Signer, Verifier}; -pub trait CryptoProvider { +pub trait CryptoProvider: Default + Send + Sync { type Sha256: Digest + FixedOutputReset; type EcdsaSecp256k1Signature: Signature; @@ -22,9 +22,11 @@ mod tests { use super::CryptoProvider; - struct SubstrateHostFunctionsManager; use k256::ecdsa::{SigningKey, VerifyingKey}; + #[derive(Default)] + struct SubstrateHostFunctionsManager; + #[derive(Debug, Default)] struct SubstrateSha256(sha2::Sha256); From f0eee32af3c722c91efca7d0da2eb2d7637ad535 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Mon, 21 Nov 2022 17:10:17 +0200 Subject: [PATCH 18/45] fix no_std --- light-client-verifier/src/lib.rs | 5 ++++- light-client-verifier/src/verifier.rs | 14 ++++++++------ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/light-client-verifier/src/lib.rs b/light-client-verifier/src/lib.rs index ce413cefd..79bac7865 100644 --- a/light-client-verifier/src/lib.rs +++ b/light-client-verifier/src/lib.rs @@ -11,4 +11,7 @@ pub mod predicates; pub mod types; mod verifier; -pub use verifier::{PredicateVerifier, ProdVerifier, Verdict, Verifier}; +pub use verifier::{PredicateVerifier, Verdict, Verifier}; + +#[cfg(feature = "rust-crypto")] +pub use verifier::ProdVerifier; diff --git a/light-client-verifier/src/verifier.rs b/light-client-verifier/src/verifier.rs index 9206324b1..65aafc537 100644 --- a/light-client-verifier/src/verifier.rs +++ b/light-client-verifier/src/verifier.rs @@ -1,20 +1,22 @@ //! Provides an interface and default implementation of the `Verifier` component -use preds::{ProdPredicates, VerificationPredicates}; use serde::{Deserialize, Serialize}; use tendermint::crypto::CryptoProvider; use crate::{ errors::{ErrorExt, VerificationError, VerificationErrorDetail}, - operations::{ - voting_power::VotingPowerTally, CommitValidator, ProdCommitValidator, - ProdVotingPowerCalculator, VotingPowerCalculator, - }, + operations::{voting_power::VotingPowerTally, CommitValidator, VotingPowerCalculator}, options::Options, - predicates as preds, + predicates::VerificationPredicates, types::{Time, TrustedBlockState, UntrustedBlockState}, }; +#[cfg(feature = "rust-crypto")] +use crate::{ + operations::{ProdCommitValidator, ProdVotingPowerCalculator}, + predicates::ProdPredicates, +}; + /// Represents the result of the verification performed by the /// verifier component. #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] From ce146c851aa274b76c298822aa7e7ea3027fb051 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Wed, 23 Nov 2022 18:27:08 +0200 Subject: [PATCH 19/45] Fix the tools build --- tools/kvstore-test/Cargo.toml | 1 + tools/kvstore-test/tests/tendermint.rs | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/kvstore-test/Cargo.toml b/tools/kvstore-test/Cargo.toml index 4129dfcb9..682410cbd 100644 --- a/tools/kvstore-test/Cargo.toml +++ b/tools/kvstore-test/Cargo.toml @@ -10,6 +10,7 @@ edition = "2018" [dev-dependencies] futures = "0.3" +sha2 = "0.10" tendermint = { version = "0.26.0", path = "../../tendermint" } tendermint-light-client = { version = "0.26.0", path = "../../light-client", features = ["unstable"] } tendermint-rpc = { version = "0.26.0", path = "../../rpc", features = [ "http-client", "websocket-client" ] } diff --git a/tools/kvstore-test/tests/tendermint.rs b/tools/kvstore-test/tests/tendermint.rs index 83ca6ef63..7a35cd2ce 100644 --- a/tools/kvstore-test/tests/tendermint.rs +++ b/tools/kvstore-test/tests/tendermint.rs @@ -22,6 +22,7 @@ mod rpc { }; use futures::StreamExt; + use sha2::Sha256; use tendermint::{ abci::Code, Hash, @@ -110,8 +111,8 @@ mod rpc { // Check for empty merkle root. // See: https://github.com/informalsystems/tendermint-rs/issues/562 - let computed_data_hash = simple_hash_from_byte_vectors( - block_info.block.data.iter().map(|t| t.to_owned()).collect(), + let computed_data_hash = simple_hash_from_byte_vectors::( + &block_info.block.data.iter().map(|t| t.to_owned()).collect::>(), ); assert_eq!( computed_data_hash, From 4a656699e1b0c7800e8a4070d1bf2cb26aa3645b Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Wed, 23 Nov 2022 19:04:08 +0200 Subject: [PATCH 20/45] Fix wasm-light-client build --- light-client/Cargo.toml | 2 +- light-client/src/tests.rs | 19 +++++++++++++------ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/light-client/Cargo.toml b/light-client/Cargo.toml index 4ee55e5eb..65a206027 100644 --- a/light-client/Cargo.toml +++ b/light-client/Cargo.toml @@ -32,7 +32,7 @@ secp256k1 = ["tendermint/secp256k1", "tendermint-rpc/secp256k1"] lightstore-sled = ["sled"] unstable = [] # Enable to execute long-running model-based tests -mbt = [] +mbt = ["rust-crypto"] [dependencies] tendermint = { version = "0.26.0", path = "../tendermint", default-features = false } diff --git a/light-client/src/tests.rs b/light-client/src/tests.rs index eb64a13b6..26c9df702 100644 --- a/light-client/src/tests.rs +++ b/light-client/src/tests.rs @@ -1,6 +1,9 @@ //! Utilities and datatypes for use in tests. -use std::{collections::HashMap, time::Duration}; +use std::collections::HashMap; + +#[cfg(feature = "rust-crypto")] +use std::time::Duration; use contracts::contract_trait; use serde::{Deserialize, Serialize}; @@ -21,13 +24,14 @@ use crate::{ evidence::EvidenceReporter, light_client::LightClient, state::State, - verifier::{ - options::Options, - types::{Height, LightBlock, PeerId, SignedHeader, Time, TrustThreshold, ValidatorSet}, - ProdVerifier, Verdict, Verifier, + verifier::types::{ + Height, LightBlock, PeerId, SignedHeader, Time, TrustThreshold, ValidatorSet, }, }; +#[cfg(feature = "rust-crypto")] +use crate::verifier::{Verdict, Verifier}; + #[derive(Deserialize, Clone, Debug)] pub struct TestCases { pub batch_name: String, @@ -144,6 +148,7 @@ impl MockEvidenceReporter { } } +#[cfg(feature = "rust-crypto")] pub fn verify_single( trusted_block: LightBlock, input: LightBlock, @@ -152,7 +157,9 @@ pub fn verify_single( clock_drift: Duration, now: Time, ) -> Result { - let verifier = ProdVerifier::default(); + use crate::verifier::options::Options; + + let verifier = crate::verifier::ProdVerifier::default(); let options = Options { trust_threshold, From d39148d2a50e5d4ec60b1aedf814cbae179e292d Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Thu, 24 Nov 2022 16:10:46 +0200 Subject: [PATCH 21/45] kvstore-test: No need to copy vec-of-vecs --- tools/kvstore-test/tests/tendermint.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/kvstore-test/tests/tendermint.rs b/tools/kvstore-test/tests/tendermint.rs index 7a35cd2ce..094a32c65 100644 --- a/tools/kvstore-test/tests/tendermint.rs +++ b/tools/kvstore-test/tests/tendermint.rs @@ -112,7 +112,7 @@ mod rpc { // Check for empty merkle root. // See: https://github.com/informalsystems/tendermint-rs/issues/562 let computed_data_hash = simple_hash_from_byte_vectors::( - &block_info.block.data.iter().map(|t| t.to_owned()).collect::>(), + &block_info.block.data, ); assert_eq!( computed_data_hash, From ec3cf883426dde64a9ea8479f0203f91d1b78a26 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Thu, 1 Dec 2022 15:44:36 +0200 Subject: [PATCH 22/45] Break down CryptoProvider into functional traits Instead of a super-trait whose sole purpose is to bind down some associated types that provide the actual functionality, provide: - Sha256, a purpose-specific trait for SHA256 hashing that has a more human-friendly interface than rust-crypto. - Nothing else for signing and verifying! These are covered by the signature framework, and it's easy to plug into that as the alt_crypto test demonstrates. The crypto::default module, gated by the "rust-crypto" feature, provides aliases for pure Rust implementations. --- .../src/operations/commit_validator.rs | 26 ++-- light-client-verifier/src/predicates.rs | 17 +- light-client-verifier/src/verifier.rs | 23 +-- light-client/src/builder/light_client.rs | 25 ++- light-client/src/fork_detector.rs | 14 +- tendermint/Cargo.toml | 1 + tendermint/src/block/header.rs | 10 +- tendermint/src/crypto.rs | 17 +- tendermint/src/crypto/default.rs | 27 ++-- tendermint/src/crypto/provider.rs | 147 ------------------ tendermint/src/crypto/sha256.rs | 44 ++++++ tendermint/src/merkle.rs | 37 ++--- tendermint/src/validator.rs | 12 +- tendermint/tests/alt_crypto.rs | 140 +++++++++++++++++ 14 files changed, 284 insertions(+), 256 deletions(-) delete mode 100644 tendermint/src/crypto/provider.rs create mode 100644 tendermint/src/crypto/sha256.rs create mode 100644 tendermint/tests/alt_crypto.rs diff --git a/light-client-verifier/src/operations/commit_validator.rs b/light-client-verifier/src/operations/commit_validator.rs index d52ba93ce..0ebe9510e 100644 --- a/light-client-verifier/src/operations/commit_validator.rs +++ b/light-client-verifier/src/operations/commit_validator.rs @@ -1,6 +1,8 @@ //! Provides an interface and default implementation for the `CommitValidator` operation -use tendermint::{block::CommitSig, crypto::CryptoProvider}; +use core::marker::PhantomData; + +use tendermint::{block::CommitSig, crypto::Sha256}; use crate::{ errors::VerificationError, @@ -27,39 +29,39 @@ pub trait CommitValidator: Send + Sync { /// Generic implementation of a commit validator, with cryptographic primitives /// provided via the [`CryptoProvider`] trait. #[derive(Copy, Clone, Debug)] -pub struct ProvidedCommitValidator { - _provider: C, +pub struct ProvidedCommitValidator { + _crypto: PhantomData, } /// Production-ready implementation of a commit validator. #[cfg(feature = "rust-crypto")] -pub type ProdCommitValidator = ProvidedCommitValidator; +pub type ProdCommitValidator = ProvidedCommitValidator; -impl ProvidedCommitValidator +impl ProvidedCommitValidator where - C: CryptoProvider + Default, + H: Sha256, { /// Create a new commit validator using the given [`Hasher`] /// to compute the hash of headers and validator sets. pub fn new() -> Self { Self { - _provider: Default::default(), + _crypto: PhantomData, } } } -impl Default for ProvidedCommitValidator +impl Default for ProvidedCommitValidator where - C: CryptoProvider + Default, + H: Sha256, { fn default() -> Self { Self::new() } } -impl CommitValidator for ProvidedCommitValidator +impl CommitValidator for ProvidedCommitValidator where - C: CryptoProvider + Send + Sync, + H: Sha256, { fn validate( &self, @@ -111,7 +113,7 @@ where if validator_set.validator(*validator_address).is_none() { return Err(VerificationError::faulty_signer( *validator_address, - validator_set.hash_with::(), + validator_set.hash_with::(), )); } } diff --git a/light-client-verifier/src/predicates.rs b/light-client-verifier/src/predicates.rs index 888e2fc33..331e13ceb 100644 --- a/light-client-verifier/src/predicates.rs +++ b/light-client-verifier/src/predicates.rs @@ -2,7 +2,7 @@ use core::time::Duration; -use tendermint::{block::Height, crypto::CryptoProvider, hash::Hash}; +use tendermint::{block::Height, crypto::Sha256, hash::Hash}; use crate::{ errors::VerificationError, @@ -18,7 +18,9 @@ use crate::{ pub struct ProdPredicates; #[cfg(feature = "rust-crypto")] -impl VerificationPredicates for ProdPredicates {} +impl VerificationPredicates for ProdPredicates { + type Sha256 = tendermint::crypto::default::Sha256; +} /// Defines the various predicates used to validate and verify light blocks. /// @@ -26,7 +28,10 @@ impl VerificationPredicates for ProdP /// /// This enables test implementations to only override a single method rather than /// have to re-define every predicate. -pub trait VerificationPredicates: Send + Sync { +pub trait VerificationPredicates: Send + Sync { + /// The implementation of SHA256 digest + type Sha256: Sha256; + /// Compare the provided validator_set_hash against the hash produced from hashing the validator /// set. fn validator_sets_match( @@ -34,7 +39,7 @@ pub trait VerificationPredicates: Send + Sync { validators: &ValidatorSet, header_validators_hash: Hash, ) -> Result<(), VerificationError> { - let validators_hash = validators.hash_with::(); + let validators_hash = validators.hash_with::(); if header_validators_hash == validators_hash { Ok(()) } else { @@ -51,7 +56,7 @@ pub trait VerificationPredicates: Send + Sync { next_validators: &ValidatorSet, header_next_validators_hash: Hash, ) -> Result<(), VerificationError> { - let next_validators_hash = next_validators.hash_with::(); + let next_validators_hash = next_validators.hash_with::(); if header_next_validators_hash == next_validators_hash { Ok(()) } else { @@ -68,7 +73,7 @@ pub trait VerificationPredicates: Send + Sync { header: &Header, commit_hash: Hash, ) -> Result<(), VerificationError> { - let header_hash = header.hash_with::(); + let header_hash = header.hash_with::(); if header_hash == commit_hash { Ok(()) } else { diff --git a/light-client-verifier/src/verifier.rs b/light-client-verifier/src/verifier.rs index 9be4311f8..5b8430967 100644 --- a/light-client-verifier/src/verifier.rs +++ b/light-client-verifier/src/verifier.rs @@ -1,7 +1,6 @@ //! Provides an interface and default implementation of the `Verifier` component use serde::{Deserialize, Serialize}; -use tendermint::crypto::CryptoProvider; use crate::{ errors::{ErrorExt, VerificationError, VerificationErrorDetail}, @@ -83,19 +82,17 @@ macro_rules! ensure_verdict_success { /// Predicate verifier encapsulating components necessary to facilitate /// verification. #[derive(Debug, Clone, Default, PartialEq, Eq)] -pub struct PredicateVerifier { +pub struct PredicateVerifier { predicates: P, voting_power_calculator: C, commit_validator: V, - crypto_provider: H, } -impl PredicateVerifier +impl PredicateVerifier where - P: VerificationPredicates, + P: VerificationPredicates, C: VotingPowerCalculator, V: CommitValidator, - H: CryptoProvider, { /// Constructor. pub fn new(predicates: P, voting_power_calculator: C, commit_validator: V) -> Self { @@ -103,7 +100,6 @@ where predicates, voting_power_calculator, commit_validator, - crypto_provider: Default::default(), } } @@ -223,12 +219,11 @@ where } } -impl Verifier for PredicateVerifier +impl Verifier for PredicateVerifier where - P: VerificationPredicates, + P: VerificationPredicates, C: VotingPowerCalculator, V: CommitValidator, - H: CryptoProvider, { /// Validate the given light block state by performing the following checks -> /// @@ -269,9 +264,5 @@ where #[cfg(feature = "rust-crypto")] /// The default production implementation of the [`PredicateVerifier`]. -pub type ProdVerifier = PredicateVerifier< - ProdPredicates, - ProdVotingPowerCalculator, - ProdCommitValidator, - tendermint::crypto::DefaultCryptoProvider, ->; +pub type ProdVerifier = + PredicateVerifier; diff --git a/light-client/src/builder/light_client.rs b/light-client/src/builder/light_client.rs index afacf8093..d33e5c00d 100644 --- a/light-client/src/builder/light_client.rs +++ b/light-client/src/builder/light_client.rs @@ -1,6 +1,6 @@ //! DSL for building a light client [`Instance`] -use tendermint::{block::Height, crypto::CryptoProvider, Hash}; +use tendermint::{block::Height, crypto::Sha256, Hash}; #[cfg(feature = "rpc-client")] use { @@ -9,7 +9,6 @@ use { crate::components::scheduler, crate::verifier::{predicates::ProdPredicates, ProdVerifier}, core::time::Duration, - tendermint::crypto::DefaultCryptoProvider, tendermint_rpc as rpc, }; @@ -40,21 +39,21 @@ pub struct HasTrustedState; /// Builder for a light client [`Instance`] #[must_use] -pub struct LightClientBuilder { +pub struct LightClientBuilder { peer_id: PeerId, options: Options, io: Box, clock: Box, verifier: Box, scheduler: Box, - predicates: Box>, + predicates: Box>, light_store: Box, #[allow(dead_code)] state: State, } -impl LightClientBuilder { +impl LightClientBuilder { /// Private method to move from one state to another fn with_state(self, state: Next) -> LightClientBuilder { LightClientBuilder { @@ -72,7 +71,7 @@ impl LightClientBuilder { } #[cfg(feature = "rpc-client")] -impl LightClientBuilder { +impl LightClientBuilder { /// Initialize a builder for a production (non-mock) light client. pub fn prod( peer_id: PeerId, @@ -94,7 +93,7 @@ impl LightClientBuilder { } } -impl LightClientBuilder { +impl LightClientBuilder { /// Initialize a builder for a custom light client, by providing all dependencies upfront. // TODO: redesign this, it's a builder API! #[allow(clippy::too_many_arguments)] @@ -106,7 +105,7 @@ impl LightClientBuilder { clock: Box, verifier: Box, scheduler: Box, - predicates: Box>, + predicates: Box>, ) -> Self { Self { peer_id, @@ -125,7 +124,7 @@ impl LightClientBuilder { fn trust_light_block( mut self, trusted_state: LightBlock, - ) -> Result, Error> { + ) -> Result, Error> { self.validate(&trusted_state)?; // TODO(liamsi, romac): it is unclear if this should be Trusted or only Verified @@ -136,7 +135,7 @@ impl LightClientBuilder { /// Keep using the latest verified or trusted block in the light store. /// Such a block must exists otherwise this will fail. - pub fn trust_from_store(self) -> Result, Error> { + pub fn trust_from_store(self) -> Result, Error> { let trusted_state = self .light_store .highest_trusted_or_verified() @@ -150,7 +149,7 @@ impl LightClientBuilder { self, trusted_height: Height, trusted_hash: Hash, - ) -> Result, Error> { + ) -> Result, Error> { let trusted_state = self .io .fetch_light_block(AtHeight::At(trusted_height)) @@ -163,7 +162,7 @@ impl LightClientBuilder { )); } - let header_hash = trusted_state.signed_header.header.hash_with::(); + let header_hash = trusted_state.signed_header.header.hash_with::(); if header_hash != trusted_hash { return Err(Error::hash_mismatch(trusted_hash, header_hash)); @@ -202,7 +201,7 @@ impl LightClientBuilder { } } -impl LightClientBuilder { +impl LightClientBuilder { /// Build the light client [`Instance`]. #[must_use] pub fn build(self) -> Instance { diff --git a/light-client/src/fork_detector.rs b/light-client/src/fork_detector.rs index 6876fcbcc..24d617092 100644 --- a/light-client/src/fork_detector.rs +++ b/light-client/src/fork_detector.rs @@ -1,6 +1,8 @@ //! Fork detection data structures and implementation. -use tendermint::crypto::CryptoProvider; +use core::marker::PhantomData; + +use tendermint::crypto::Sha256; use crate::{ errors::{Error, ErrorDetail}, @@ -63,17 +65,17 @@ pub trait ForkDetector: Send + Sync { /// - If verification fails because of lack of trust, we have a potential fork. /// - If verification fails for any other reason, the witness is deemed faulty. pub struct ProvidedForkDetector { - _crypto: H, + _crypto: PhantomData, } #[cfg(feature = "rust-crypto")] -pub type ProdForkDetector = ProvidedForkDetector; +pub type ProdForkDetector = ProvidedForkDetector; -impl ProvidedForkDetector { +impl ProvidedForkDetector { /// Construct a new fork detector that will use the given header hasher. pub fn new() -> Self { Self { - _crypto: Default::default(), + _crypto: PhantomData, } } } @@ -84,7 +86,7 @@ impl Default for ProvidedForkDetector { } } -impl ForkDetector for ProvidedForkDetector { +impl ForkDetector for ProvidedForkDetector { /// Perform fork detection. See the documentation `ProdForkDetector` for details. fn detect_forks( &self, diff --git a/tendermint/Cargo.toml b/tendermint/Cargo.toml index 14d42da6d..bc8302a04 100644 --- a/tendermint/Cargo.toml +++ b/tendermint/Cargo.toml @@ -62,6 +62,7 @@ secp256k1 = ["k256", "ripemd"] rust-crypto = ["k256"] [dev-dependencies] +k256 = { version = "0.11" } pretty_assertions = "1.3.0" proptest = { version = "0.10.1", default-features = false, features = ["std"] } tendermint-pbt-gen = { path = "../pbt-gen", default-features = false, features = ["time"] } diff --git a/tendermint/src/block/header.rs b/tendermint/src/block/header.rs index 19341426a..d041271a0 100644 --- a/tendermint/src/block/header.rs +++ b/tendermint/src/block/header.rs @@ -8,7 +8,7 @@ use tendermint_proto::{ }; use crate::{ - account, block, chain, crypto::CryptoProvider, merkle, prelude::*, AppHash, Error, Hash, Time, + account, block, chain, crypto::Sha256, merkle, prelude::*, AppHash, Error, Hash, Time, }; /// Block `Header` values contain metadata about the block and about the @@ -166,11 +166,11 @@ impl Header { /// Computes the hash of this header. #[cfg(feature = "rust-crypto")] pub fn hash(&self) -> Hash { - self.hash_with::() + self.hash_with::() } /// Hash this header with a SHA256 hasher provided by a crypto provider. - pub fn hash_with(&self) -> Hash { + pub fn hash_with(&self) -> Hash { // Note that if there is an encoding problem this will // panic (as the golang code would): // https://github.com/tendermint/tendermint/blob/134fe2896275bb926b49743c1e25493f6b24cc31/types/block.go#L393 @@ -199,9 +199,7 @@ impl Header { self.proposer_address.encode_vec().unwrap(), ]; - Hash::Sha256(merkle::simple_hash_from_byte_vectors::( - &fields_bytes, - )) + Hash::Sha256(merkle::simple_hash_from_byte_vectors::(&fields_bytes)) } } diff --git a/tendermint/src/crypto.rs b/tendermint/src/crypto.rs index 23cecb461..ef578e1b4 100644 --- a/tendermint/src/crypto.rs +++ b/tendermint/src/crypto.rs @@ -1,9 +1,14 @@ -mod provider; +//! Cryptographic functionality for Tendermint. +//! +//! This module provides type aliases and utility traits that facilitate +//! use of interchangeable implementations of cryptographic routines used by +//! Tendermint. +//! +//! The abstract framework enabling this extensibility is provided by the +//! `digest` and `signature` crates. -pub use provider::CryptoProvider; +pub mod sha256; +pub use sha256::Sha256; #[cfg(feature = "rust-crypto")] -mod default; - -#[cfg(feature = "rust-crypto")] -pub use default::DefaultCryptoProvider; +pub mod default; diff --git a/tendermint/src/crypto/default.rs b/tendermint/src/crypto/default.rs index d04af6025..d15188b6f 100644 --- a/tendermint/src/crypto/default.rs +++ b/tendermint/src/crypto/default.rs @@ -1,17 +1,16 @@ -use sha2::Sha256; +//! Pure Rust implementations of the cryptographic traits. +//! +//! Most applications using this crate should use these implementations. +//! Alternative implementations can be useful on targets like wasm and +//! on-chain environments, where code size is at a premium and a faster +//! platform-native cryptographic API is available. -use super::CryptoProvider; +/// The default implementation of the [`Sha256`][sha256trait] trait. +/// +/// [sha256trait]: super::Sha256 +pub use sha2::Sha256; -/// A batteries-included provider of cryptograpic functions. -#[derive(Copy, Clone, Debug, Default)] -pub struct DefaultCryptoProvider; - -impl CryptoProvider for DefaultCryptoProvider { - type Sha256 = Sha256; - - type EcdsaSecp256k1Signature = k256::ecdsa::Signature; - - type EcdsaSecp256k1Signer = k256::ecdsa::SigningKey; - - type EcdsaSecp256k1Verifier = k256::ecdsa::VerifyingKey; +/// Types implementing the ECDSA algorithm using the Secp256k1 elliptic curve. +pub mod ecdsa_secp256 { + pub use k256::ecdsa::{Signature, SigningKey, VerifyingKey}; } diff --git a/tendermint/src/crypto/provider.rs b/tendermint/src/crypto/provider.rs deleted file mode 100644 index fbda59d48..000000000 --- a/tendermint/src/crypto/provider.rs +++ /dev/null @@ -1,147 +0,0 @@ -use digest::{consts::U32, Digest, FixedOutputReset}; -use signature::{Signature, Signer, Verifier}; - -pub trait CryptoProvider: Default + Send + Sync { - type Sha256: Digest + FixedOutputReset; - - type EcdsaSecp256k1Signature: Signature; - type EcdsaSecp256k1Signer: Signer; - type EcdsaSecp256k1Verifier: Verifier; -} - -#[cfg(test)] -mod tests { - - /// A draft for an imlpementation of the HostFunctionManager for a specific chain (i.e. Polkadot/Substrate) - /// that uses the [`CryptoProvider`] trait - use core::marker::PhantomData; - use signature::{DigestSigner, DigestVerifier}; - - use digest::{consts::U32, Digest, FixedOutput, FixedOutputReset, Reset}; - use signature::{Signature, Signer, Verifier}; - - use super::CryptoProvider; - - use k256::ecdsa::{SigningKey, VerifyingKey}; - - #[derive(Default)] - struct SubstrateHostFunctionsManager; - - #[derive(Debug, Default)] - struct SubstrateSha256(sha2::Sha256); - - struct SubstrateSigner { - inner: SigningKey, - _d: PhantomData, - } - #[derive(Debug)] - struct SubstrateSignatureVerifier { - inner: VerifyingKey, - _d: PhantomData, - } - - impl> SubstrateSignatureVerifier { - fn from_bytes(public_key: &[u8]) -> Result { - Ok(Self { - inner: VerifyingKey::from_sec1_bytes(public_key)?, - _d: PhantomData::default(), - }) - } - } - - impl, S: Signature> DigestVerifier - for SubstrateSignatureVerifier - where - VerifyingKey: DigestVerifier, - { - fn verify_digest(&self, digest: D, signature: &S) -> Result<(), ed25519::Error> { - self.inner.verify_digest(digest, signature) - } - } - - impl> Verifier - for SubstrateSignatureVerifier - where - VerifyingKey: DigestVerifier, - { - fn verify(&self, msg: &[u8], signature: &S) -> Result<(), ed25519::Error> { - let mut hasher = D::new(); - Digest::update(&mut hasher, msg); - self.verify_digest(hasher, signature) - } - } - - impl digest::OutputSizeUser for SubstrateSha256 { - type OutputSize = U32; - } - - impl digest::HashMarker for SubstrateSha256 {} - - impl digest::Update for SubstrateSha256 { - fn update(&mut self, data: &[u8]) { - digest::Update::update(&mut self.0, data) - } - } - - impl FixedOutput for SubstrateSha256 { - fn finalize_into(self, out: &mut digest::Output) { - *out = self.0.finalize(); - } - } - - impl Reset for SubstrateSha256 { - fn reset(&mut self) { - Reset::reset(&mut self.0) - } - } - - impl FixedOutputReset for SubstrateSha256 { - fn finalize_into_reset(&mut self, out: &mut digest::Output) { - *out = self.0.finalize_reset(); - } - } - - impl Signer for SubstrateSigner - where - SigningKey: DigestSigner, - { - fn try_sign(&self, msg: &[u8]) -> Result { - let mut hasher = D::new(); - Digest::update(&mut hasher, msg); - self.inner.try_sign_digest(hasher) - } - } - - trait SubstrateHostFunctions: CryptoProvider { - fn sha2_256(preimage: &[u8]) -> [u8; 32]; - fn ed25519_verify(sig: &[u8], msg: &[u8], pub_key: &[u8]) -> Result<(), ()>; - fn secp256k1_verify(sig: &[u8], message: &[u8], public: &[u8]) -> Result<(), ()>; - } - - impl CryptoProvider for SubstrateHostFunctionsManager { - type Sha256 = SubstrateSha256; - - type EcdsaSecp256k1Signature = k256::ecdsa::Signature; - type EcdsaSecp256k1Signer = SubstrateSigner; - type EcdsaSecp256k1Verifier = SubstrateSignatureVerifier; - } - - impl SubstrateHostFunctions for SubstrateHostFunctionsManager { - fn sha2_256(preimage: &[u8]) -> [u8; 32] { - let mut hasher = Self::Sha256::new(); - hasher.update(preimage); - let result = hasher.finalize().try_into().unwrap(); - result - } - fn ed25519_verify(sig: &[u8], msg: &[u8], pub_key: &[u8]) -> Result<(), ()> { - let verifier = - <::EcdsaSecp256k1Verifier>::from_bytes(pub_key).unwrap(); - let signature = k256::ecdsa::Signature::from_der(sig).unwrap(); - Ok(verifier.verify(msg, &signature).unwrap()) - } - - fn secp256k1_verify(_sig: &[u8], _message: &[u8], _public: &[u8]) -> Result<(), ()> { - unimplemented!() - } - } -} diff --git a/tendermint/src/crypto/sha256.rs b/tendermint/src/crypto/sha256.rs new file mode 100644 index 000000000..d930ecdc6 --- /dev/null +++ b/tendermint/src/crypto/sha256.rs @@ -0,0 +1,44 @@ +use digest::{consts::U32, Digest, FixedOutputReset}; + +/// Length of a SHA256 hash in bytes. +pub const HASH_SIZE: usize = 32; + +/// A SHA256 digest implementation. +/// +/// This trait is blanket-implemented, and it puts a more user-friendly face +/// over the APIs of the `digest` framework. +pub trait Sha256: Send + Sync { + fn new() -> Self; + fn update(&mut self, data: impl AsRef<[u8]>); + fn finalize(self) -> [u8; HASH_SIZE]; + fn finalize_reset(&mut self) -> [u8; HASH_SIZE]; +} + +impl Sha256 for H +where + H: Digest + FixedOutputReset + Send + Sync, +{ + fn new() -> Self { + Digest::new() + } + + fn update(&mut self, data: impl AsRef<[u8]>) { + Digest::update(self, data) + } + + fn finalize(self) -> [u8; HASH_SIZE] { + let digest = Digest::finalize(self); + // copy the GenericArray out + let mut hash = [0u8; HASH_SIZE]; + hash.copy_from_slice(&digest); + hash + } + + fn finalize_reset(&mut self) -> [u8; HASH_SIZE] { + let digest = Digest::finalize_reset(self); + // copy the GenericArray out + let mut hash = [0u8; HASH_SIZE]; + hash.copy_from_slice(&digest); + hash + } +} diff --git a/tendermint/src/merkle.rs b/tendermint/src/merkle.rs index 516bcb004..8efab6707 100644 --- a/tendermint/src/merkle.rs +++ b/tendermint/src/merkle.rs @@ -4,12 +4,11 @@ pub mod proof; pub use proof::Proof; -use digest::{consts::U32, Digest, FixedOutputReset}; - +use crate::crypto::Sha256; use crate::prelude::*; /// Size of Merkle root hash -pub const HASH_SIZE: usize = 32; +pub use crate::crypto::sha256::HASH_SIZE; /// Hash is the output of the cryptographic digest function pub type Hash = [u8; HASH_SIZE]; @@ -17,19 +16,14 @@ pub type Hash = [u8; HASH_SIZE]; /// Compute a simple Merkle root from vectors of arbitrary byte vectors. /// The leaves of the tree are the bytes of the given byte vectors in /// the given order. -pub fn simple_hash_from_byte_vectors + FixedOutputReset>( - byte_vecs: &[Vec], -) -> Hash { +pub fn simple_hash_from_byte_vectors(byte_vecs: &[Vec]) -> Hash { let mut hasher = H::new(); simple_hash_from_byte_vectors_inner(&mut hasher, byte_vecs) } // Recurse into subtrees. // Pre and post-conditions: the hasher is in the reset state before and after calling this function. -fn simple_hash_from_byte_vectors_inner + FixedOutputReset>( - hasher: &mut H, - byte_vecs: &[Vec], -) -> Hash { +fn simple_hash_from_byte_vectors_inner(hasher: &mut H, byte_vecs: &[Vec]) -> Hash { let length = byte_vecs.len(); match length { 0 => empty_hash(hasher), @@ -45,7 +39,7 @@ fn simple_hash_from_byte_vectors_inner + FixedOutput // tmhash({}) // Pre and post-conditions: the hasher is in the reset state before and after calling this function. -fn empty_hash + FixedOutputReset>(hasher: &mut H) -> Hash { +fn empty_hash(hasher: &mut H) -> Hash { // Get the hash of an empty digest state let digest = hasher.finalize_reset(); @@ -57,10 +51,10 @@ fn empty_hash + FixedOutputReset>(hasher: &mut H) -> // tmhash(0x00 || leaf) // Pre and post-conditions: the hasher is in the reset state before and after calling this function. -fn leaf_hash + FixedOutputReset>(hasher: &mut H, bytes: &[u8]) -> Hash { +fn leaf_hash(hasher: &mut H, bytes: &[u8]) -> Hash { // Feed the data to the hasher, prepended with 0x00 - Digest::update(hasher, &[0x00]); - Digest::update(hasher, bytes); + hasher.update([0x00]); + hasher.update(bytes); // Finalize the digest, reset the hasher state let digest = hasher.finalize_reset(); @@ -73,15 +67,11 @@ fn leaf_hash + FixedOutputReset>(hasher: &mut H, byt // tmhash(0x01 || left || right) // Pre and post-conditions: the hasher is in the reset state before and after calling this function. -fn inner_hash + FixedOutputReset>( - hasher: &mut H, - left: &[u8], - right: &[u8], -) -> Hash { - // Feed the data to the hasher 0x1, then left and right data. - Digest::update(hasher, &[0x01]); - Digest::update(hasher, left); - Digest::update(hasher, right); +fn inner_hash(hasher: &mut H, left: &[u8], right: &[u8]) -> Hash { + // Feed the data to the hasher: 0x1, then left and right data. + hasher.update([0x01]); + hasher.update(left); + hasher.update(right); // Finalize the digest, reset the hasher state let digest = hasher.finalize_reset(); @@ -98,6 +88,7 @@ mod tests { use subtle_encoding::hex; use super::*; // TODO: use non-subtle ? + use crate::crypto::Sha256 as _; #[test] fn test_rfc6962_empty_tree() { diff --git a/tendermint/src/validator.rs b/tendermint/src/validator.rs index b6ec49432..8b12544fc 100644 --- a/tendermint/src/validator.rs +++ b/tendermint/src/validator.rs @@ -13,8 +13,8 @@ use tendermint_proto::{ }; use crate::{ - account, crypto::CryptoProvider, hash::Hash, merkle, prelude::*, - public_key::deserialize_public_key, vote, Error, PublicKey, Signature, + account, crypto::Sha256, hash::Hash, merkle, prelude::*, public_key::deserialize_public_key, + vote, Error, PublicKey, Signature, }; /// Validator set contains a vector of validators @@ -137,20 +137,18 @@ impl Set { /// Compute the hash of this validator set. #[cfg(feature = "rust-crypto")] pub fn hash(&self) -> Hash { - self.hash_with::() + self.hash_with::() } /// Hash this header with a SHA256 hasher provided by a crypto provider. - pub fn hash_with(&self) -> Hash { + pub fn hash_with(&self) -> Hash { let validator_bytes: Vec> = self .validators() .iter() .map(|validator| validator.hash_bytes()) .collect(); - Hash::Sha256(merkle::simple_hash_from_byte_vectors::( - &validator_bytes, - )) + Hash::Sha256(merkle::simple_hash_from_byte_vectors::(&validator_bytes)) } } diff --git a/tendermint/tests/alt_crypto.rs b/tendermint/tests/alt_crypto.rs new file mode 100644 index 000000000..1bfaa6154 --- /dev/null +++ b/tendermint/tests/alt_crypto.rs @@ -0,0 +1,140 @@ +//! An imitation of alternative cryptographic function implementations +//! for a chain environment that provides its own cryptographic API. + +use digest::{consts::U32, Digest, FixedOutput, FixedOutputReset, Reset}; +use signature::{self, DigestSigner, DigestVerifier, Signer, Verifier}; + +use k256::ecdsa::{SigningKey, VerifyingKey}; + +#[derive(Debug, Default)] +struct SubstrateSha256(sha2::Sha256); + +pub use k256::ecdsa::Signature; + +struct SubstrateSigner { + inner: SigningKey, +} + +impl SubstrateSigner { + fn from_bytes(private_key: &[u8]) -> Result { + let inner = SigningKey::from_bytes(private_key)?; + Ok(Self { inner }) + } +} + +impl Signer for SubstrateSigner { + fn try_sign(&self, msg: &[u8]) -> Result { + let mut hasher = SubstrateSha256::new(); + hasher.update(msg); + self.inner.try_sign_digest(hasher) + } +} + +#[derive(Debug)] +struct SubstrateSignatureVerifier { + inner: VerifyingKey, +} + +impl SubstrateSignatureVerifier { + fn from_bytes(public_key: &[u8]) -> Result { + Ok(Self { + inner: VerifyingKey::from_sec1_bytes(public_key)?, + }) + } +} + +impl DigestVerifier for SubstrateSignatureVerifier { + fn verify_digest( + &self, + digest: SubstrateSha256, + signature: &Signature, + ) -> Result<(), signature::Error> { + self.inner.verify_digest(digest, signature) + } +} + +impl Verifier for SubstrateSignatureVerifier { + fn verify(&self, msg: &[u8], signature: &Signature) -> Result<(), signature::Error> { + let mut hasher = SubstrateSha256::new(); + Digest::update(&mut hasher, msg); + self.verify_digest(hasher, signature) + } +} + +impl digest::OutputSizeUser for SubstrateSha256 { + type OutputSize = U32; +} + +impl digest::HashMarker for SubstrateSha256 {} + +impl digest::Update for SubstrateSha256 { + fn update(&mut self, data: &[u8]) { + digest::Update::update(&mut self.0, data) + } +} + +impl FixedOutput for SubstrateSha256 { + fn finalize_into(self, out: &mut digest::Output) { + *out = self.0.finalize(); + } +} + +impl Reset for SubstrateSha256 { + fn reset(&mut self) { + Reset::reset(&mut self.0) + } +} + +impl FixedOutputReset for SubstrateSha256 { + fn finalize_into_reset(&mut self, out: &mut digest::Output) { + *out = self.0.finalize_reset(); + } +} + +mod tests { + use super::{SubstrateSha256, SubstrateSignatureVerifier, SubstrateSigner}; + use signature::{Signature, Signer, Verifier}; + use tendermint::crypto::Sha256; + + use subtle_encoding::hex; + + #[test] + fn sha256_can_hash() { + let mut hasher = SubstrateSha256::new(); + hasher.update(b"hello world"); + let hash = hasher.finalize(); + + let hash = String::from_utf8(hex::encode(&hash)).unwrap(); + assert_eq!( + hash, + "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9" + ); + } + + const SIGNING_KEY: &[u8] = b"59820654790d53a23d1017b50ddcdb31242e27c682a0a1372fc63c01dd48816a"; + const VERIFYING_KEY: &[u8] = + b"03cf7a110053a95b4b25266c3416ae342eba2ca3f4658fa1069fcf750f760b8c42"; + const MESSAGE: &[u8] = b"hello world"; + const SIGNATURE: &str = "684c3c183f76a79fc116dd4edd39fe40737cea51c6c1df47ff544c20d14a7a76754c43c51e0daa647e8e4164f254bb62dbf9bd5b2e2e03ffb8247dd92ce1e1e3"; + + #[test] + fn signer_can_sign() { + let key_bytes = hex::decode(SIGNING_KEY).unwrap(); + + let signer = SubstrateSigner::from_bytes(&key_bytes).unwrap(); + let signature = signer.sign(MESSAGE); + + let sig_hex = String::from_utf8(hex::encode(signature.as_bytes())).unwrap(); + assert_eq!(sig_hex, SIGNATURE); + } + + #[test] + fn verifier_can_verify() { + let key_bytes = hex::decode(VERIFYING_KEY).unwrap(); + let signature = hex::decode(SIGNATURE.as_bytes()).unwrap(); + let signature = Signature::from_bytes(&signature).unwrap(); + + let verifier = SubstrateSignatureVerifier::from_bytes(&key_bytes).unwrap(); + verifier.verify(MESSAGE, &signature).unwrap(); + } +} From 5a2dfe568a93d7c06ae5de5aded08369f1ec1168 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Thu, 1 Dec 2022 21:46:54 +0200 Subject: [PATCH 23/45] alt_crypto test: Roll our own signature type An alt implementation would not be able to reuse the signature type from k256. --- tendermint/tests/alt_crypto.rs | 35 ++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/tendermint/tests/alt_crypto.rs b/tendermint/tests/alt_crypto.rs index 1bfaa6154..289064f85 100644 --- a/tendermint/tests/alt_crypto.rs +++ b/tendermint/tests/alt_crypto.rs @@ -2,14 +2,28 @@ //! for a chain environment that provides its own cryptographic API. use digest::{consts::U32, Digest, FixedOutput, FixedOutputReset, Reset}; -use signature::{self, DigestSigner, DigestVerifier, Signer, Verifier}; +use signature::{self, DigestSigner, DigestVerifier, Signature, Signer, Verifier}; use k256::ecdsa::{SigningKey, VerifyingKey}; #[derive(Debug, Default)] struct SubstrateSha256(sha2::Sha256); -pub use k256::ecdsa::Signature; +#[derive(Debug)] +struct SubstrateSignature(k256::ecdsa::Signature); + +impl AsRef<[u8]> for SubstrateSignature { + fn as_ref(&self) -> &[u8] { + self.0.as_bytes() + } +} + +impl Signature for SubstrateSignature { + fn from_bytes(bytes: &[u8]) -> Result { + let inner = k256::ecdsa::Signature::from_bytes(bytes)?; + Ok(Self(inner)) + } +} struct SubstrateSigner { inner: SigningKey, @@ -22,11 +36,12 @@ impl SubstrateSigner { } } -impl Signer for SubstrateSigner { - fn try_sign(&self, msg: &[u8]) -> Result { +impl Signer for SubstrateSigner { + fn try_sign(&self, msg: &[u8]) -> Result { let mut hasher = SubstrateSha256::new(); hasher.update(msg); - self.inner.try_sign_digest(hasher) + let signature = self.inner.try_sign_digest(hasher)?; + Ok(SubstrateSignature(signature)) } } @@ -43,18 +58,18 @@ impl SubstrateSignatureVerifier { } } -impl DigestVerifier for SubstrateSignatureVerifier { +impl DigestVerifier for SubstrateSignatureVerifier { fn verify_digest( &self, digest: SubstrateSha256, - signature: &Signature, + signature: &SubstrateSignature, ) -> Result<(), signature::Error> { - self.inner.verify_digest(digest, signature) + self.inner.verify_digest(digest, &signature.0) } } -impl Verifier for SubstrateSignatureVerifier { - fn verify(&self, msg: &[u8], signature: &Signature) -> Result<(), signature::Error> { +impl Verifier for SubstrateSignatureVerifier { + fn verify(&self, msg: &[u8], signature: &SubstrateSignature) -> Result<(), signature::Error> { let mut hasher = SubstrateSha256::new(); Digest::update(&mut hasher, msg); self.verify_digest(hasher, signature) From 58c113abb1067093d3357a59697092bacc77d393 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Thu, 1 Dec 2022 21:50:48 +0200 Subject: [PATCH 24/45] Fix the wasm build --- tendermint/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tendermint/Cargo.toml b/tendermint/Cargo.toml index bc8302a04..c4455719e 100644 --- a/tendermint/Cargo.toml +++ b/tendermint/Cargo.toml @@ -51,7 +51,7 @@ tendermint-proto = { version = "0.27.0", default-features = false, path = "../pr time = { version = "0.3", default-features = false, features = ["macros", "parsing"] } zeroize = { version = "1.1", default-features = false, features = ["zeroize_derive", "alloc"] } flex-error = { version = "0.4.4", default-features = false } -k256 = { version = "0.11", optional = true, default-features = false, features = ["ecdsa", "sha256"] } +k256 = { version = "0.11", optional = true, default-features = false, features = ["ecdsa"] } ripemd = { version = "0.1.3", default-features = false, optional = true } [features] @@ -62,7 +62,7 @@ secp256k1 = ["k256", "ripemd"] rust-crypto = ["k256"] [dev-dependencies] -k256 = { version = "0.11" } +k256 = { version = "0.11", default-features = false, features = ["ecdsa"] } pretty_assertions = "1.3.0" proptest = { version = "0.10.1", default-features = false, features = ["std"] } tendermint-pbt-gen = { path = "../pbt-gen", default-features = false, features = ["time"] } From dcabd567fefd5d0df8f0eafb1cfc23e0ab3a734d Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Thu, 1 Dec 2022 21:52:43 +0200 Subject: [PATCH 25/45] Fix a clippy lint --- tendermint/src/account.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tendermint/src/account.rs b/tendermint/src/account.rs index b82a88d3c..c03713457 100644 --- a/tendermint/src/account.rs +++ b/tendermint/src/account.rs @@ -90,7 +90,7 @@ impl Debug for Id { #[cfg(feature = "secp256k1")] impl From for Id { fn from(pk: Secp256k1) -> Id { - let sha_digest = Sha256::digest(&pk.to_bytes()); + let sha_digest = Sha256::digest(pk.to_bytes()); let ripemd_digest = Ripemd160::digest(&sha_digest[..]); let mut bytes = [0u8; LENGTH]; bytes.copy_from_slice(&ripemd_digest[..LENGTH]); From 76a3fceddfcad1b226bad0db320c6f4e54e5dc19 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Thu, 1 Dec 2022 22:02:59 +0200 Subject: [PATCH 26/45] Fix a clippy lint in alt_crypto test --- tendermint/tests/alt_crypto.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tendermint/tests/alt_crypto.rs b/tendermint/tests/alt_crypto.rs index 289064f85..65b8f8c5e 100644 --- a/tendermint/tests/alt_crypto.rs +++ b/tendermint/tests/alt_crypto.rs @@ -119,7 +119,7 @@ mod tests { hasher.update(b"hello world"); let hash = hasher.finalize(); - let hash = String::from_utf8(hex::encode(&hash)).unwrap(); + let hash = String::from_utf8(hex::encode(hash)).unwrap(); assert_eq!( hash, "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9" From a9c86f21445f4c215cca7d6332fc0a68a0c014bb Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Fri, 2 Dec 2022 19:31:48 +0200 Subject: [PATCH 27/45] Disentangle CommitValidator from the hasher Remove the only purpose for using a hasher in the implementation by returning the set of validators in VerificationErrorDetail::FaultySigner. CommitValidator can now revert to being a (weird) default-implemented trait, with a ProdCommitValidator to anchor the implementation. --- light-client-verifier/src/errors.rs | 7 +- .../src/operations/commit_validator.rs | 70 +++---------------- light-client-verifier/src/predicates.rs | 4 +- 3 files changed, 14 insertions(+), 67 deletions(-) diff --git a/light-client-verifier/src/errors.rs b/light-client-verifier/src/errors.rs index db35506b0..efab57292 100644 --- a/light-client-verifier/src/errors.rs +++ b/light-client-verifier/src/errors.rs @@ -9,7 +9,7 @@ use tendermint::{account::Id, Error as TendermintError}; use crate::{ operations::voting_power::VotingPowerTally, prelude::*, - types::{Hash, Height, Time, Validator, ValidatorAddress}, + types::{Hash, Height, Time, Validator, ValidatorAddress, ValidatorSet}, }; define_error! { @@ -151,13 +151,12 @@ define_error! { FaultySigner { signer: Id, - validator_set: Hash + validator_set: ValidatorSet } | e | { format_args!( - "Found a faulty signer ({}) not present in the validator set ({})", + "Found a faulty signer ({}) not present in the validator set", e.signer, - e.validator_set ) }, diff --git a/light-client-verifier/src/operations/commit_validator.rs b/light-client-verifier/src/operations/commit_validator.rs index 0ebe9510e..315cd60d6 100644 --- a/light-client-verifier/src/operations/commit_validator.rs +++ b/light-client-verifier/src/operations/commit_validator.rs @@ -1,8 +1,6 @@ //! Provides an interface and default implementation for the `CommitValidator` operation -use core::marker::PhantomData; - -use tendermint::{block::CommitSig, crypto::Sha256}; +use tendermint::block::CommitSig; use crate::{ errors::VerificationError, @@ -12,57 +10,6 @@ use crate::{ /// Validates the commit associated with a header against a validator set pub trait CommitValidator: Send + Sync { /// Perform basic validation - fn validate( - &self, - signed_header: &SignedHeader, - validators: &ValidatorSet, - ) -> Result<(), VerificationError>; - - /// Perform full validation, only necessary if we do full verification (2/3) - fn validate_full( - &self, - signed_header: &SignedHeader, - validator_set: &ValidatorSet, - ) -> Result<(), VerificationError>; -} - -/// Generic implementation of a commit validator, with cryptographic primitives -/// provided via the [`CryptoProvider`] trait. -#[derive(Copy, Clone, Debug)] -pub struct ProvidedCommitValidator { - _crypto: PhantomData, -} - -/// Production-ready implementation of a commit validator. -#[cfg(feature = "rust-crypto")] -pub type ProdCommitValidator = ProvidedCommitValidator; - -impl ProvidedCommitValidator -where - H: Sha256, -{ - /// Create a new commit validator using the given [`Hasher`] - /// to compute the hash of headers and validator sets. - pub fn new() -> Self { - Self { - _crypto: PhantomData, - } - } -} - -impl Default for ProvidedCommitValidator -where - H: Sha256, -{ - fn default() -> Self { - Self::new() - } -} - -impl CommitValidator for ProvidedCommitValidator -where - H: Sha256, -{ fn validate( &self, signed_header: &SignedHeader, @@ -88,12 +35,7 @@ where Ok(()) } - // This check is only necessary if we do full verification (2/3) - // - // See https://github.com/informalsystems/tendermint-rs/issues/281 - // - // It returns `ImplementationSpecific` error if it detects a signer - // that is not present in the validator set + /// Perform full validation, only necessary if we do full verification (2/3) fn validate_full( &self, signed_header: &SignedHeader, @@ -113,7 +55,7 @@ where if validator_set.validator(*validator_address).is_none() { return Err(VerificationError::faulty_signer( *validator_address, - validator_set.hash_with::(), + validator_set.clone(), )); } } @@ -121,3 +63,9 @@ where Ok(()) } } + +/// Production-ready implementation of a commit validator. +#[derive(Copy, Clone, Default, Debug)] +pub struct ProdCommitValidator; + +impl CommitValidator for ProdCommitValidator {} diff --git a/light-client-verifier/src/predicates.rs b/light-client-verifier/src/predicates.rs index 331e13ceb..a8a732f68 100644 --- a/light-client-verifier/src/predicates.rs +++ b/light-client-verifier/src/predicates.rs @@ -451,7 +451,7 @@ mod tests { let val_set = light_block.validators; let vp = ProdPredicates::default(); - let commit_validator = ProdCommitValidator::new(); + let commit_validator = ProdCommitValidator::default(); // Test scenarios --> // 1. valid commit - must result "Ok" @@ -524,7 +524,7 @@ mod tests { .unwrap() ); - assert_eq!(e.validator_set, val_set_with_faulty_signer.hash()); + assert_eq!(e.validator_set, val_set_with_faulty_signer); }, _ => panic!("expected FaultySigner error"), } From 79af98ac793dbcad81a57f36de345e0b801f7427 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Fri, 2 Dec 2022 19:32:24 +0200 Subject: [PATCH 28/45] light-client: Enable shrinking in backward test --- light-client/tests/backward.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/light-client/tests/backward.rs b/light-client/tests/backward.rs index 92d878e50..2cdb774b2 100644 --- a/light-client/tests/backward.rs +++ b/light-client/tests/backward.rs @@ -205,7 +205,7 @@ fn tc_corrupted_hash(max: u32) -> impl Strategy { proptest! { #![proptest_config(ProptestConfig { cases: 20, - max_shrink_iters: 0, + max_shrink_iters: 100, ..Default::default() })] From c5ec5954c27e2752c82b5de4530d8c20c8ca860d Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Fri, 2 Dec 2022 22:28:58 +0200 Subject: [PATCH 29/45] light-client: update edition to rust2021 --- light-client/Cargo.toml | 2 +- light-client/src/light_client.rs | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/light-client/Cargo.toml b/light-client/Cargo.toml index 71d086abd..579dfd85c 100644 --- a/light-client/Cargo.toml +++ b/light-client/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "tendermint-light-client" version = "0.27.0" -edition = "2018" +edition = "2021" license = "Apache-2.0" readme = "README.md" keywords = ["blockchain", "bft", "consensus", "cosmos", "tendermint"] diff --git a/light-client/src/light_client.rs b/light-client/src/light_client.rs index 7e94bcbc8..ad20d404f 100644 --- a/light-client/src/light_client.rs +++ b/light-client/src/light_client.rs @@ -291,8 +291,6 @@ impl LightClient { target_height: Height, state: &mut State, ) -> Result { - use std::convert::TryFrom; - let root = state .light_store .highest_trusted_or_verified() From 4f73378ba577ff04be401cf76de8cd8b7040c4a9 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Fri, 2 Dec 2022 23:36:03 +0200 Subject: [PATCH 30/45] Recover LightClient::verify_backward A critical code block was hastily commented out in this "unstable" part of code. --- light-client/Cargo.toml | 2 +- light-client/src/light_client.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/light-client/Cargo.toml b/light-client/Cargo.toml index 579dfd85c..b106eb991 100644 --- a/light-client/Cargo.toml +++ b/light-client/Cargo.toml @@ -30,7 +30,7 @@ rpc-client = ["tokio", "rust-crypto", "tendermint-rpc/http-client"] rust-crypto = ["tendermint/rust-crypto", "tendermint-light-client-verifier/rust-crypto"] secp256k1 = ["tendermint/secp256k1", "tendermint-rpc/secp256k1"] lightstore-sled = ["sled"] -unstable = [] +unstable = ["rust-crypto"] # Enable to execute long-running model-based tests mbt = ["rust-crypto"] diff --git a/light-client/src/light_client.rs b/light-client/src/light_client.rs index ad20d404f..a70981189 100644 --- a/light-client/src/light_client.rs +++ b/light-client/src/light_client.rs @@ -291,6 +291,8 @@ impl LightClient { target_height: Height, state: &mut State, ) -> Result { + use tendermint::crypto::default::Sha256; + let root = state .light_store .highest_trusted_or_verified() @@ -315,14 +317,13 @@ impl LightClient { for height in heights { let (current, _status) = self.get_or_fetch_block(height, state)?; - /* let latest_last_block_id = latest .signed_header .header .last_block_id .ok_or_else(|| Error::missing_last_block_id(latest.height()))?; - let current_hash = current.signed_header.header.hash_with::(); + let current_hash = current.signed_header.header.hash_with::(); if current_hash != latest_last_block_id.hash { return Err(Error::invalid_adjacent_headers( @@ -330,7 +331,6 @@ impl LightClient { latest_last_block_id.hash, )); } - */ // `latest` and `current` are linked together by `last_block_id`, // therefore it is not relevant which we verified first. From 41efc0b7a2424d1328e1ec713dcc13ec683fe218 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Mon, 5 Dec 2022 13:27:23 +0200 Subject: [PATCH 31/45] Fix light-client-js build, record technical debt --- light-client-js/Cargo.toml | 2 +- light-client-js/src/lib.rs | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/light-client-js/Cargo.toml b/light-client-js/Cargo.toml index 0444d7cad..78259232e 100644 --- a/light-client-js/Cargo.toml +++ b/light-client-js/Cargo.toml @@ -25,7 +25,7 @@ serde_json = { version = "1.0", default-features = false } # TODO(thane): Remove once https://github.com/rustwasm/wasm-bindgen/issues/2508 is resolved syn = { version = "=1.0.65", default-features = false } tendermint = { version = "0.27.0", default-features = false, path = "../tendermint" } -tendermint-light-client-verifier = { version = "0.27.0", default-features = false, path = "../light-client-verifier" } +tendermint-light-client-verifier = { version = "0.27.0", features = ["rust-crypto"], default-features = false, path = "../light-client-verifier" } wasm-bindgen = { version = "0.2.63", default-features = false, features = [ "serde-serialize" ] } # The `console_error_panic_hook` crate provides better debugging of panics by diff --git a/light-client-js/src/lib.rs b/light-client-js/src/lib.rs index 32f38970d..bc37dc56e 100644 --- a/light-client-js/src/lib.rs +++ b/light-client-js/src/lib.rs @@ -18,10 +18,14 @@ use tendermint::Time; use tendermint_light_client_verifier::{ options::Options, types::{LightBlock, TrustThreshold}, - ProdVerifier, Verifier, + Verifier, }; use wasm_bindgen::{prelude::*, JsValue}; +// TODO: Use Web Crypto API for cryptographic routines. +// https://github.com/informalsystems/tendermint-rs/issues/1241 +use tendermint_light_client_verifier::ProdVerifier; + /// Check whether a given untrusted block can be trusted. #[wasm_bindgen] pub fn verify(untrusted: &JsValue, trusted: &JsValue, options: &JsValue, now: &JsValue) -> JsValue { From 6365d495502072628803e2653e2f94e29d9a8c0c Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Wed, 7 Dec 2022 23:01:11 +0200 Subject: [PATCH 32/45] tendermint: Make sha2 an optional dependency Use the Sha2 alias from crypto::default instead of the direct references to sha2 crate. All code that used Sha2 non-generically is feature-gated behind "rust-crypto". --- light-client/src/supervisor.rs | 2 +- light-client/tests/supervisor.rs | 2 ++ tendermint/Cargo.toml | 6 ++-- tendermint/src/account.rs | 58 +++++++++++++++++++++----------- tendermint/src/node/id.rs | 55 ++++++++++++++++-------------- tendermint/src/validator.rs | 13 +------ 6 files changed, 75 insertions(+), 61 deletions(-) diff --git a/light-client/src/supervisor.rs b/light-client/src/supervisor.rs index dbd9e82df..50dd7f401 100644 --- a/light-client/src/supervisor.rs +++ b/light-client/src/supervisor.rs @@ -421,7 +421,7 @@ impl Handle for SupervisorHandle { } } -#[cfg(test)] +#[cfg(all(test, feature = "rust-crypto"))] mod tests { use core::{ convert::{Into, TryFrom}, diff --git a/light-client/tests/supervisor.rs b/light-client/tests/supervisor.rs index 3726cb237..293ed9be1 100644 --- a/light-client/tests/supervisor.rs +++ b/light-client/tests/supervisor.rs @@ -1,3 +1,5 @@ +#![cfg(feature = "rust-crypto")] + use std::{collections::HashMap, time::Duration}; use tendermint_light_client::{ diff --git a/tendermint/Cargo.toml b/tendermint/Cargo.toml index c4455719e..e28d23ad7 100644 --- a/tendermint/Cargo.toml +++ b/tendermint/Cargo.toml @@ -43,7 +43,6 @@ serde = { version = "1", default-features = false, features = ["derive"] } serde_json = { version = "1", default-features = false, features = ["alloc"] } serde_bytes = { version = "0.11", default-features = false } serde_repr = { version = "0.1", default-features = false } -sha2 = { version = "0.10", default-features = false } signature = { version = "1", default-features = false } subtle = { version = "2", default-features = false } subtle-encoding = { version = "0.5", default-features = false, features = ["bech32-preview"] } @@ -51,15 +50,16 @@ tendermint-proto = { version = "0.27.0", default-features = false, path = "../pr time = { version = "0.3", default-features = false, features = ["macros", "parsing"] } zeroize = { version = "1.1", default-features = false, features = ["zeroize_derive", "alloc"] } flex-error = { version = "0.4.4", default-features = false } +sha2 = { version = "0.10", optional = true, default-features = false } k256 = { version = "0.11", optional = true, default-features = false, features = ["ecdsa"] } -ripemd = { version = "0.1.3", default-features = false, optional = true } +ripemd = { version = "0.1.3", optional = true, default-features = false } [features] default = ["std", "rust-crypto"] std = ["flex-error/std", "flex-error/eyre_tracer", "clock"] clock = ["time/std"] secp256k1 = ["k256", "ripemd"] -rust-crypto = ["k256"] +rust-crypto = ["sha2", "k256"] [dev-dependencies] k256 = { version = "0.11", default-features = false, features = ["ecdsa"] } diff --git a/tendermint/src/account.rs b/tendermint/src/account.rs index c03713457..8522040e9 100644 --- a/tendermint/src/account.rs +++ b/tendermint/src/account.rs @@ -9,14 +9,11 @@ use core::{ #[cfg(feature = "secp256k1")] use ripemd::Ripemd160; use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; -use sha2::{Digest, Sha256}; use subtle::{self, ConstantTimeEq}; use subtle_encoding::hex; use tendermint_proto::Protobuf; -#[cfg(feature = "secp256k1")] -use crate::public_key::Secp256k1; -use crate::{error::Error, prelude::*, public_key::Ed25519}; +use crate::{error::Error, prelude::*}; /// Size of an account ID in bytes pub const LENGTH: usize = 20; @@ -86,23 +83,43 @@ impl Debug for Id { } } -// RIPEMD160(SHA256(pk)) -#[cfg(feature = "secp256k1")] -impl From for Id { - fn from(pk: Secp256k1) -> Id { - let sha_digest = Sha256::digest(pk.to_bytes()); - let ripemd_digest = Ripemd160::digest(&sha_digest[..]); - let mut bytes = [0u8; LENGTH]; - bytes.copy_from_slice(&ripemd_digest[..LENGTH]); - Id(bytes) +#[cfg(feature = "rust-crypto")] +mod key_conversions { + use super::{Id, LENGTH}; + use crate::crypto::default::Sha256; + #[cfg(feature = "secp256k1")] + use crate::public_key::Secp256k1; + use crate::public_key::{Ed25519, PublicKey}; + use digest::Digest; + + // RIPEMD160(SHA256(pk)) + #[cfg(feature = "secp256k1")] + impl From for Id { + fn from(pk: Secp256k1) -> Id { + let sha_digest = Sha256::digest(pk.to_bytes()); + let ripemd_digest = Ripemd160::digest(&sha_digest[..]); + let mut bytes = [0u8; LENGTH]; + bytes.copy_from_slice(&ripemd_digest[..LENGTH]); + Id(bytes) + } + } + + // SHA256(pk)[:20] + impl From for Id { + fn from(pk: Ed25519) -> Id { + let digest = Sha256::digest(pk.as_bytes()); + Id(digest[..LENGTH].try_into().unwrap()) + } } -} -// SHA256(pk)[:20] -impl From for Id { - fn from(pk: Ed25519) -> Id { - let digest = Sha256::digest(pk.as_bytes()); - Id(digest[..LENGTH].try_into().unwrap()) + impl From for Id { + fn from(pub_key: PublicKey) -> Id { + match pub_key { + PublicKey::Ed25519(pk) => Id::from(pk), + #[cfg(feature = "secp256k1")] + PublicKey::Secp256k1(pk) => account::Id::from(pk), + } + } } } @@ -146,9 +163,10 @@ impl Serialize for Id { } } -#[cfg(test)] +#[cfg(all(test, feature = "rust-crypto"))] mod tests { use super::*; + use crate::public_key::Ed25519; #[test] fn test_ed25519_id() { diff --git a/tendermint/src/node/id.rs b/tendermint/src/node/id.rs index d1936384d..171fd0b7f 100644 --- a/tendermint/src/node/id.rs +++ b/tendermint/src/node/id.rs @@ -1,21 +1,17 @@ //! Tendermint node IDs use core::{ - convert::TryFrom, fmt::{self, Debug, Display}, str::FromStr, }; use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; -use sha2::{Digest, Sha256}; use subtle::{self, ConstantTimeEq}; use subtle_encoding::hex; -use crate::{ - error::Error, - prelude::*, - public_key::{Ed25519, PublicKey}, -}; +use crate::{error::Error, prelude::*}; + +#[allow(unused_imports)] /// Length of a Node ID in bytes pub const LENGTH: usize = 20; @@ -64,12 +60,33 @@ impl Debug for Id { } } -impl From for Id { - fn from(pk: Ed25519) -> Id { - let digest = Sha256::digest(pk.as_bytes()); - let mut bytes = [0u8; LENGTH]; - bytes.copy_from_slice(&digest[..LENGTH]); - Id(bytes) +#[cfg(feature = "rust-crypto")] +mod key_conversions { + use super::{Id, LENGTH}; + use crate::crypto::default::Sha256; + use crate::public_key::{Ed25519, PublicKey}; + use crate::Error; + use digest::Digest; + + impl From for Id { + fn from(pk: Ed25519) -> Id { + let digest = Sha256::digest(pk.as_bytes()); + let mut bytes = [0u8; LENGTH]; + bytes.copy_from_slice(&digest[..LENGTH]); + Id(bytes) + } + } + + impl TryFrom for Id { + type Error = Error; + + fn try_from(pk: PublicKey) -> Result { + match pk { + PublicKey::Ed25519(ed25519) => Ok(Id::from(ed25519)), + #[cfg(feature = "secp256k1")] + _ => Err(Error::unsupported_key_type()), + } + } } } @@ -99,18 +116,6 @@ impl PartialEq for Id { } } -impl TryFrom for Id { - type Error = Error; - - fn try_from(pk: PublicKey) -> Result { - match pk { - PublicKey::Ed25519(ed25519) => Ok(Id::from(ed25519)), - #[cfg(feature = "secp256k1")] - _ => Err(Error::unsupported_key_type()), - } - } -} - impl<'de> Deserialize<'de> for Id { fn deserialize(deserializer: D) -> Result where diff --git a/tendermint/src/validator.rs b/tendermint/src/validator.rs index 8b12544fc..f5dbe2a7b 100644 --- a/tendermint/src/validator.rs +++ b/tendermint/src/validator.rs @@ -214,19 +214,8 @@ impl Info { pub fn verify_signature(&self, sign_bytes: &[u8], signature: &Signature) -> Result<(), Error> { self.pub_key.verify(sign_bytes, signature) } -} - -impl From for account::Id { - fn from(pub_key: PublicKey) -> account::Id { - match pub_key { - PublicKey::Ed25519(pk) => account::Id::from(pk), - #[cfg(feature = "secp256k1")] - PublicKey::Secp256k1(pk) => account::Id::from(pk), - } - } -} -impl Info { + #[cfg(feature = "rust-crypto")] /// Create a new validator. pub fn new(pk: PublicKey, vp: vote::Power) -> Info { Info { From 58425b572f2be569e50c101890fa9dbf1e5921bd Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Tue, 13 Dec 2022 21:03:31 +0200 Subject: [PATCH 33/45] Redesign crypto::Sha256, add MerkleHash trait The host API for obtaining SHA256 digests on Substrate is a simple function, it cannot work incrementally. Change the Sha256 trait to match this, but provide a MerkleHash trait to retain incremental Merkle hashing with Rust-Crypto conformant digest implementations. The merkle::NonIncremental adapter type is provided to fit the lowest common denominator Sha256 API to a Merkle hash implementation, at the cost of some allocations and extra copying. --- light-client-verifier/src/predicates.rs | 4 +- light-client/src/builder/light_client.rs | 21 ++- light-client/src/fork_detector.rs | 8 +- tendermint/src/block/header.rs | 45 ++++-- tendermint/src/crypto/default.rs | 13 ++ tendermint/src/crypto/sha256.rs | 43 +----- tendermint/src/merkle.rs | 186 +++++++++++++++-------- tendermint/src/validator.rs | 182 +++++++++++----------- tendermint/tests/alt_crypto.rs | 54 ++----- 9 files changed, 300 insertions(+), 256 deletions(-) diff --git a/light-client-verifier/src/predicates.rs b/light-client-verifier/src/predicates.rs index a8a732f68..8cfb2f747 100644 --- a/light-client-verifier/src/predicates.rs +++ b/light-client-verifier/src/predicates.rs @@ -2,7 +2,7 @@ use core::time::Duration; -use tendermint::{block::Height, crypto::Sha256, hash::Hash}; +use tendermint::{block::Height, crypto::Sha256, hash::Hash, merkle::MerkleHash}; use crate::{ errors::VerificationError, @@ -30,7 +30,7 @@ impl VerificationPredicates for ProdPredicates { /// have to re-define every predicate. pub trait VerificationPredicates: Send + Sync { /// The implementation of SHA256 digest - type Sha256: Sha256; + type Sha256: MerkleHash + Sha256 + Default; /// Compare the provided validator_set_hash against the hash produced from hashing the validator /// set. diff --git a/light-client/src/builder/light_client.rs b/light-client/src/builder/light_client.rs index d33e5c00d..6a0ac3290 100644 --- a/light-client/src/builder/light_client.rs +++ b/light-client/src/builder/light_client.rs @@ -1,6 +1,6 @@ //! DSL for building a light client [`Instance`] -use tendermint::{block::Height, crypto::Sha256, Hash}; +use tendermint::{block::Height, crypto::Sha256, merkle::MerkleHash, Hash}; #[cfg(feature = "rpc-client")] use { @@ -39,7 +39,7 @@ pub struct HasTrustedState; /// Builder for a light client [`Instance`] #[must_use] -pub struct LightClientBuilder { +pub struct LightClientBuilder { peer_id: PeerId, options: Options, io: Box, @@ -53,9 +53,12 @@ pub struct LightClientBuilder { state: State, } -impl LightClientBuilder { +impl LightClientBuilder +where + H: MerkleHash + Sha256 + Default, +{ /// Private method to move from one state to another - fn with_state(self, state: Next) -> LightClientBuilder { + fn with_state(self, state: Next) -> LightClientBuilder { LightClientBuilder { peer_id: self.peer_id, options: self.options, @@ -93,7 +96,10 @@ impl LightClientBuilder { } } -impl LightClientBuilder { +impl LightClientBuilder +where + H: MerkleHash + Sha256 + Default, +{ /// Initialize a builder for a custom light client, by providing all dependencies upfront. // TODO: redesign this, it's a builder API! #[allow(clippy::too_many_arguments)] @@ -201,7 +207,10 @@ impl LightClientBuilder { } } -impl LightClientBuilder { +impl LightClientBuilder +where + H: MerkleHash + Sha256 + Default, +{ /// Build the light client [`Instance`]. #[must_use] pub fn build(self) -> Instance { diff --git a/light-client/src/fork_detector.rs b/light-client/src/fork_detector.rs index 24d617092..1bde5c70c 100644 --- a/light-client/src/fork_detector.rs +++ b/light-client/src/fork_detector.rs @@ -2,7 +2,7 @@ use core::marker::PhantomData; -use tendermint::crypto::Sha256; +use tendermint::{crypto::Sha256, merkle::MerkleHash}; use crate::{ errors::{Error, ErrorDetail}, @@ -86,7 +86,11 @@ impl Default for ProvidedForkDetector { } } -impl ForkDetector for ProvidedForkDetector { +impl ForkDetector for ProvidedForkDetector +where + // Sync + Send have to be added only because of forbid(unsafe_code) + H: MerkleHash + Sha256 + Default + Sync + Send, +{ /// Perform fork detection. See the documentation `ProdForkDetector` for details. fn detect_forks( &self, diff --git a/tendermint/src/block/header.rs b/tendermint/src/block/header.rs index d041271a0..ad747134b 100644 --- a/tendermint/src/block/header.rs +++ b/tendermint/src/block/header.rs @@ -8,7 +8,11 @@ use tendermint_proto::{ }; use crate::{ - account, block, chain, crypto::Sha256, merkle, prelude::*, AppHash, Error, Hash, Time, + account, block, chain, + crypto::Sha256, + merkle::{self, MerkleHash}, + prelude::*, + AppHash, Error, Hash, Time, }; /// Block `Header` values contain metadata about the block and about the @@ -169,8 +173,11 @@ impl Header { self.hash_with::() } - /// Hash this header with a SHA256 hasher provided by a crypto provider. - pub fn hash_with(&self) -> Hash { + /// Hash this header with a Merkle hasher provided by a crypto provider. + pub fn hash_with(&self) -> Hash + where + H: MerkleHash + Sha256 + Default, + { // Note that if there is an encoding problem this will // panic (as the golang code would): // https://github.com/tendermint/tendermint/blob/134fe2896275bb926b49743c1e25493f6b24cc31/types/block.go#L393 @@ -239,7 +246,7 @@ impl From for RawConsensusVersion { #[cfg(test)] mod tests { use super::Header; - use crate::{hash::Algorithm, test::test_serialization_roundtrip, Hash}; + use crate::test::test_serialization_roundtrip; #[test] fn serialization_roundtrip() { @@ -247,17 +254,23 @@ mod tests { test_serialization_roundtrip::
(json_data); } - #[test] - fn header_hashing() { - let expected_hash = Hash::from_hex_upper( - Algorithm::Sha256, - "F30A71F2409FB15AACAEDB6CC122DFA2525BEE9CAE521721B06BFDCA291B8D56", - ) - .unwrap(); - let header: Header = serde_json::from_str(include_str!( - "../../tests/support/serialization/block/header_with_known_hash.json" - )) - .unwrap(); - assert_eq!(expected_hash, header.hash()); + #[cfg(feature = "rust-crypto")] + mod crypto { + use super::*; + use crate::{hash::Algorithm, Hash}; + + #[test] + fn header_hashing() { + let expected_hash = Hash::from_hex_upper( + Algorithm::Sha256, + "F30A71F2409FB15AACAEDB6CC122DFA2525BEE9CAE521721B06BFDCA291B8D56", + ) + .unwrap(); + let header: Header = serde_json::from_str(include_str!( + "../../tests/support/serialization/block/header_with_known_hash.json" + )) + .unwrap(); + assert_eq!(expected_hash, header.hash()); + } } } diff --git a/tendermint/src/crypto/default.rs b/tendermint/src/crypto/default.rs index d15188b6f..66dfb943c 100644 --- a/tendermint/src/crypto/default.rs +++ b/tendermint/src/crypto/default.rs @@ -5,11 +5,24 @@ //! on-chain environments, where code size is at a premium and a faster //! platform-native cryptographic API is available. +use super::sha256::HASH_SIZE; +use digest::Digest; + /// The default implementation of the [`Sha256`][sha256trait] trait. /// /// [sha256trait]: super::Sha256 pub use sha2::Sha256; +impl super::Sha256 for Sha256 { + fn digest(data: impl AsRef<[u8]>) -> [u8; HASH_SIZE] { + let digest = ::digest(data); + // copy the GenericArray out + let mut hash = [0u8; HASH_SIZE]; + hash.copy_from_slice(&digest); + hash + } +} + /// Types implementing the ECDSA algorithm using the Secp256k1 elliptic curve. pub mod ecdsa_secp256 { pub use k256::ecdsa::{Signature, SigningKey, VerifyingKey}; diff --git a/tendermint/src/crypto/sha256.rs b/tendermint/src/crypto/sha256.rs index d930ecdc6..e4e2a4357 100644 --- a/tendermint/src/crypto/sha256.rs +++ b/tendermint/src/crypto/sha256.rs @@ -1,44 +1,11 @@ -use digest::{consts::U32, Digest, FixedOutputReset}; - /// Length of a SHA256 hash in bytes. pub const HASH_SIZE: usize = 32; /// A SHA256 digest implementation. /// -/// This trait is blanket-implemented, and it puts a more user-friendly face -/// over the APIs of the `digest` framework. -pub trait Sha256: Send + Sync { - fn new() -> Self; - fn update(&mut self, data: impl AsRef<[u8]>); - fn finalize(self) -> [u8; HASH_SIZE]; - fn finalize_reset(&mut self) -> [u8; HASH_SIZE]; -} - -impl Sha256 for H -where - H: Digest + FixedOutputReset + Send + Sync, -{ - fn new() -> Self { - Digest::new() - } - - fn update(&mut self, data: impl AsRef<[u8]>) { - Digest::update(self, data) - } - - fn finalize(self) -> [u8; HASH_SIZE] { - let digest = Digest::finalize(self); - // copy the GenericArray out - let mut hash = [0u8; HASH_SIZE]; - hash.copy_from_slice(&digest); - hash - } - - fn finalize_reset(&mut self) -> [u8; HASH_SIZE] { - let digest = Digest::finalize_reset(self); - // copy the GenericArray out - let mut hash = [0u8; HASH_SIZE]; - hash.copy_from_slice(&digest); - hash - } +/// This trait provides the most general possible interface that can be +/// implemented by host functions in popular on-chain smart contract +/// environments. As such, in can only do one-piece slice digests. +pub trait Sha256 { + fn digest(data: impl AsRef<[u8]>) -> [u8; HASH_SIZE]; } diff --git a/tendermint/src/merkle.rs b/tendermint/src/merkle.rs index 8efab6707..01731fc20 100644 --- a/tendermint/src/merkle.rs +++ b/tendermint/src/merkle.rs @@ -4,6 +4,10 @@ pub mod proof; pub use proof::Proof; +use core::marker::PhantomData; + +use digest::{consts::U32, Digest, FixedOutputReset}; + use crate::crypto::Sha256; use crate::prelude::*; @@ -16,79 +20,132 @@ pub type Hash = [u8; HASH_SIZE]; /// Compute a simple Merkle root from vectors of arbitrary byte vectors. /// The leaves of the tree are the bytes of the given byte vectors in /// the given order. -pub fn simple_hash_from_byte_vectors(byte_vecs: &[Vec]) -> Hash { - let mut hasher = H::new(); - simple_hash_from_byte_vectors_inner(&mut hasher, byte_vecs) +pub fn simple_hash_from_byte_vectors(byte_vecs: &[Vec]) -> Hash +where + H: MerkleHash + Default, +{ + let mut hasher = H::default(); + hasher.hash_byte_vectors(byte_vecs) } -// Recurse into subtrees. -// Pre and post-conditions: the hasher is in the reset state before and after calling this function. -fn simple_hash_from_byte_vectors_inner(hasher: &mut H, byte_vecs: &[Vec]) -> Hash { - let length = byte_vecs.len(); - match length { - 0 => empty_hash(hasher), - 1 => leaf_hash(hasher, &byte_vecs[0]), - _ => { - let split = length.next_power_of_two() / 2; - let left = simple_hash_from_byte_vectors_inner(hasher, &byte_vecs[..split]); - let right = simple_hash_from_byte_vectors_inner(hasher, &byte_vecs[split..]); - inner_hash(hasher, &left, &right) - }, +/// Implementation of Merkle tree hashing for Tendermint. +pub trait MerkleHash { + // tmhash({}) + // Pre and post-conditions: the hasher is in the reset state + // before and after calling this function. + fn empty_hash(&mut self) -> Hash; + + // tmhash(0x00 || leaf) + // Pre and post-conditions: the hasher is in the reset state + // before and after calling this function. + fn leaf_hash(&mut self, bytes: &[u8]) -> Hash; + + // tmhash(0x01 || left || right) + // Pre and post-conditions: the hasher is in the reset state + // before and after calling this function. + fn inner_hash(&mut self, left: Hash, right: Hash) -> Hash; + + // Implements recursion into subtrees. + // Pre and post-conditions: the hasher is in the reset state + // before and after calling this function. + fn hash_byte_vectors(&mut self, byte_vecs: &[Vec]) -> Hash { + let length = byte_vecs.len(); + match length { + 0 => self.empty_hash(), + 1 => self.leaf_hash(&byte_vecs[0]), + _ => { + let split = length.next_power_of_two() / 2; + let left = self.hash_byte_vectors(&byte_vecs[..split]); + let right = self.hash_byte_vectors(&byte_vecs[split..]); + self.inner_hash(left, right) + }, + } } } -// tmhash({}) -// Pre and post-conditions: the hasher is in the reset state before and after calling this function. -fn empty_hash(hasher: &mut H) -> Hash { - // Get the hash of an empty digest state - let digest = hasher.finalize_reset(); - - // copy the GenericArray out +// A helper to copy GenericArray into the human-friendly Hash type. +fn copy_to_hash(output: impl AsRef<[u8]>) -> Hash { let mut hash_bytes = [0u8; HASH_SIZE]; - hash_bytes.copy_from_slice(&digest); + hash_bytes.copy_from_slice(output.as_ref()); hash_bytes } -// tmhash(0x00 || leaf) -// Pre and post-conditions: the hasher is in the reset state before and after calling this function. -fn leaf_hash(hasher: &mut H, bytes: &[u8]) -> Hash { - // Feed the data to the hasher, prepended with 0x00 - hasher.update([0x00]); - hasher.update(bytes); +impl MerkleHash for H +where + H: Digest + FixedOutputReset, +{ + fn empty_hash(&mut self) -> Hash { + // Get the output of an empty digest state. + let digest = self.finalize_reset(); + copy_to_hash(&digest) + } + + fn leaf_hash(&mut self, bytes: &[u8]) -> Hash { + // Feed the data to the hasher, prepended with 0x00. + Digest::update(self, [0x00]); + Digest::update(self, bytes); - // Finalize the digest, reset the hasher state - let digest = hasher.finalize_reset(); + // Finalize the digest, reset the hasher state. + let digest = self.finalize_reset(); - // copy the GenericArray out - let mut hash_bytes = [0u8; HASH_SIZE]; - hash_bytes.copy_from_slice(&digest); - hash_bytes + copy_to_hash(digest) + } + + fn inner_hash(&mut self, left: Hash, right: Hash) -> Hash { + // Feed the data to the hasher: 0x1, then left and right data. + Digest::update(self, [0x01]); + Digest::update(self, left); + Digest::update(self, right); + + // Finalize the digest, reset the hasher state + let digest = self.finalize_reset(); + + copy_to_hash(digest) + } } -// tmhash(0x01 || left || right) -// Pre and post-conditions: the hasher is in the reset state before and after calling this function. -fn inner_hash(hasher: &mut H, left: &[u8], right: &[u8]) -> Hash { - // Feed the data to the hasher: 0x1, then left and right data. - hasher.update([0x01]); - hasher.update(left); - hasher.update(right); +/// A wrapper for platform-provided host functions which can't do incremental +/// hashing. One unfortunate example of such platform is Polkadot. +pub struct NonIncremental(PhantomData); - // Finalize the digest, reset the hasher state - let digest = hasher.finalize_reset(); +impl Default for NonIncremental { + fn default() -> Self { + Self(Default::default()) + } +} - // copy the GenericArray out - let mut hash_bytes = [0u8; HASH_SIZE]; - hash_bytes.copy_from_slice(&digest); - hash_bytes +impl MerkleHash for NonIncremental { + fn empty_hash(&mut self) -> Hash { + let digest = H::digest(&[]); + copy_to_hash(digest) + } + + fn leaf_hash(&mut self, bytes: &[u8]) -> Hash { + // This is why non-incremental digest APIs are daft. + let mut buf = Vec::with_capacity(1 + bytes.len()); + buf.push(0); + buf.extend_from_slice(bytes); + let digest = H::digest(buf); + copy_to_hash(digest) + } + + fn inner_hash(&mut self, left: Hash, right: Hash) -> Hash { + // This is why non-incremental digest APIs are daft. + let mut buf = [0u8; 1 + HASH_SIZE * 2]; + buf[0] = 1; + buf[1..HASH_SIZE + 1].copy_from_slice(&left); + buf[HASH_SIZE + 1..].copy_from_slice(&right); + let digest = H::digest(buf); + copy_to_hash(digest) + } } -#[cfg(test)] +#[cfg(all(test, feature = "rust-crypto"))] mod tests { use sha2::Sha256; use subtle_encoding::hex; use super::*; // TODO: use non-subtle ? - use crate::crypto::Sha256 as _; #[test] fn test_rfc6962_empty_tree() { @@ -124,18 +181,6 @@ mod tests { assert_eq!(leaf_root, &root); } - #[test] - fn test_rfc6962_node() { - let node_hash_hex = "aa217fe888e47007fa15edab33c2b492a722cb106c64667fc2b044444de66bbb"; - let left_string = "N123"; - let right_string = "N456"; - - let node_hash = &hex::decode(node_hash_hex).unwrap(); - let mut hasher = Sha256::new(); - let hash = inner_hash(&mut hasher, left_string.as_bytes(), right_string.as_bytes()); - assert_eq!(node_hash, &hash); - } - #[test] fn test_rfc6962_tree_of_2() { let node_hash_hex = "dc9a0536ff2e196d5a628a5bf377ab247bbddf83342be39699461c1e766e6646"; @@ -146,4 +191,19 @@ mod tests { let hash = simple_hash_from_byte_vectors::(&[left, right]); assert_eq!(node_hash, &hash); } + + mod non_incremental { + use super::*; + + #[test] + fn test_rfc6962_tree_of_2() { + let node_hash_hex = "dc9a0536ff2e196d5a628a5bf377ab247bbddf83342be39699461c1e766e6646"; + let left = b"N123".to_vec(); + let right = b"N456".to_vec(); + + let node_hash = &hex::decode(node_hash_hex).unwrap(); + let hash = simple_hash_from_byte_vectors::>(&[left, right]); + assert_eq!(node_hash, &hash); + } + } } diff --git a/tendermint/src/validator.rs b/tendermint/src/validator.rs index f5dbe2a7b..60eb79c37 100644 --- a/tendermint/src/validator.rs +++ b/tendermint/src/validator.rs @@ -13,7 +13,12 @@ use tendermint_proto::{ }; use crate::{ - account, crypto::Sha256, hash::Hash, merkle, prelude::*, public_key::deserialize_public_key, + account, + crypto::Sha256, + hash::Hash, + merkle::{self, MerkleHash}, + prelude::*, + public_key::deserialize_public_key, vote, Error, PublicKey, Signature, }; @@ -141,7 +146,10 @@ impl Set { } /// Hash this header with a SHA256 hasher provided by a crypto provider. - pub fn hash_with(&self) -> Hash { + pub fn hash_with(&self) -> Hash + where + H: MerkleHash + Sha256 + Default, + { let validator_bytes: Vec> = self .validators() .iter() @@ -357,92 +365,96 @@ impl TryFrom for Update { #[cfg(test)] mod tests { - use super::*; - // make a validator - fn make_validator(pk: Vec, vp: u64) -> Info { - let pk = PublicKey::from_raw_ed25519(&pk).unwrap(); - Info::new(pk, vote::Power::try_from(vp).unwrap()) - } + #[cfg(feature = "rust-crypto")] + mod crypto { + use super::*; - #[test] - fn test_validator_set() { - // test vector generated by Go code - // import ( - // "fmt" - // "github.com/tendermint/tendermint/crypto/ed25519" - // "github.com/tendermint/tendermint/types" - // "strings" - // ) - // func testValSet() { - // pk1 := ed25519.GenPrivKeyFromSecret([]byte{4, 211, 14, 157, 10, 0, 205, 9, 10, 116, 207, - // 161, 4, 211, 190, 37, 108, 88, 202, 168, 63, 135, 0, 141, 53, 55, 254, 57, 40, 184, 20, - // 242}) pk2 := ed25519.GenPrivKeyFromSecret([]byte{99, 231, 126, 151, 159, 236, 2, - // 229, 33, 44, 200, 248, 147, 176, 13, 127, 105, 76, 49, 83, 25, 101, 44, 57, 20, 215, 166, - // 188, 134, 94, 56, 165}) pk3 := ed25519.GenPrivKeyFromSecret([]byte{54, 253, 151, - // 16, 182, 114, 125, 12, 74, 101, 54, 253, 174, 153, 121, 74, 145, 180, 111, 16, 214, 48, - // 193, 109, 104, 134, 55, 162, 151, 16, 182, 114}) not_in_set := - // ed25519.GenPrivKeyFromSecret([]byte{121, 74, 145, 180, 111, 16, 214, 48, 193, 109, 35, - // 68, 19, 27, 173, 69, 92, 204, 127, 218, 234, 81, 232, 75, 204, 199, 48, 163, 55, 132, - // 231, 147}) fmt.Println("pk1: ", strings.Join(strings.Split(fmt.Sprintf("%v", - // pk1.PubKey().Bytes()), " "), ", ")) fmt.Println("pk2:", - // strings.Join(strings.Split(fmt.Sprintf("%v", pk2.PubKey().Bytes()), " "), ", ")) - // fmt.Println("pk3: ", strings.Join(strings.Split(fmt.Sprintf("%v", pk3.PubKey().Bytes()), - // " "), ", ")) fmt.Println("not_in_set: ", - // strings.Join(strings.Split(fmt.Sprintf("%v", not_in_set.PubKey().Bytes()), " "), ", ")) - // v1 := types.NewValidator(pk1.PubKey(), 148151478422287875) - // v2 := types.NewValidator(pk2.PubKey(), 158095448483785107) - // v3 := types.NewValidator(pk3.PubKey(), 770561664770006272) - // set := types.NewValidatorSet([]*types.Validator{v1, v2, v3}) - // fmt.Println("Hash:", strings.Join(strings.Split(fmt.Sprintf("%v", set.Hash()), " "), ", - // ")) } - let v1 = make_validator( - vec![ - 48, 163, 55, 132, 231, 147, 230, 163, 56, 158, 127, 218, 179, 139, 212, 103, 218, - 89, 122, 126, 229, 88, 84, 48, 32, 0, 185, 174, 63, 72, 203, 52, - ], - 148_151_478_422_287_875, - ); - let v2 = make_validator( - vec![ - 54, 253, 174, 153, 121, 74, 145, 180, 111, 16, 214, 48, 193, 109, 104, 134, 55, - 162, 151, 16, 182, 114, 125, 135, 32, 195, 236, 248, 64, 112, 74, 101, - ], - 158_095_448_483_785_107, - ); - let v3 = make_validator( - vec![ - 182, 205, 13, 86, 147, 27, 65, 49, 160, 118, 11, 180, 117, 35, 206, 35, 68, 19, 27, - 173, 69, 92, 204, 224, 200, 51, 249, 81, 105, 128, 112, 244, - ], - 770_561_664_770_006_272, - ); - let hash_expect = vec![ - 11, 64, 107, 4, 234, 81, 232, 75, 204, 199, 160, 114, 229, 97, 243, 95, 118, 213, 17, - 22, 57, 84, 71, 122, 200, 169, 192, 252, 41, 148, 223, 180, - ]; - - let val_set = Set::without_proposer(vec![v1.clone(), v2.clone(), v3.clone()]); - let hash = val_set.hash(); - assert_eq!(hash_expect, hash.as_bytes().to_vec()); - - let not_in_set = make_validator( - vec![ - 110, 147, 87, 120, 27, 218, 66, 209, 81, 4, 169, 153, 64, 163, 137, 89, 168, 97, - 219, 233, 42, 119, 24, 61, 47, 59, 76, 31, 182, 60, 13, 4, - ], - 10_000_000_000_000_000, - ); - - assert_eq!(val_set.validator(v1.address).unwrap(), v1); - assert_eq!(val_set.validator(v2.address).unwrap(), v2); - assert_eq!(val_set.validator(v3.address).unwrap(), v3); - assert_eq!(val_set.validator(not_in_set.address), None); - assert_eq!( - val_set.total_voting_power().value(), - 148_151_478_422_287_875 + 158_095_448_483_785_107 + 770_561_664_770_006_272 - ); + // make a validator + fn make_validator(pk: Vec, vp: u64) -> Info { + let pk = PublicKey::from_raw_ed25519(&pk).unwrap(); + Info::new(pk, vote::Power::try_from(vp).unwrap()) + } + + #[test] + fn test_validator_set() { + // test vector generated by Go code + // import ( + // "fmt" + // "github.com/tendermint/tendermint/crypto/ed25519" + // "github.com/tendermint/tendermint/types" + // "strings" + // ) + // func testValSet() { + // pk1 := ed25519.GenPrivKeyFromSecret([]byte{4, 211, 14, 157, 10, 0, 205, 9, 10, 116, 207, + // 161, 4, 211, 190, 37, 108, 88, 202, 168, 63, 135, 0, 141, 53, 55, 254, 57, 40, 184, 20, + // 242}) pk2 := ed25519.GenPrivKeyFromSecret([]byte{99, 231, 126, 151, 159, 236, 2, + // 229, 33, 44, 200, 248, 147, 176, 13, 127, 105, 76, 49, 83, 25, 101, 44, 57, 20, 215, 166, + // 188, 134, 94, 56, 165}) pk3 := ed25519.GenPrivKeyFromSecret([]byte{54, 253, 151, + // 16, 182, 114, 125, 12, 74, 101, 54, 253, 174, 153, 121, 74, 145, 180, 111, 16, 214, 48, + // 193, 109, 104, 134, 55, 162, 151, 16, 182, 114}) not_in_set := + // ed25519.GenPrivKeyFromSecret([]byte{121, 74, 145, 180, 111, 16, 214, 48, 193, 109, 35, + // 68, 19, 27, 173, 69, 92, 204, 127, 218, 234, 81, 232, 75, 204, 199, 48, 163, 55, 132, + // 231, 147}) fmt.Println("pk1: ", strings.Join(strings.Split(fmt.Sprintf("%v", + // pk1.PubKey().Bytes()), " "), ", ")) fmt.Println("pk2:", + // strings.Join(strings.Split(fmt.Sprintf("%v", pk2.PubKey().Bytes()), " "), ", ")) + // fmt.Println("pk3: ", strings.Join(strings.Split(fmt.Sprintf("%v", pk3.PubKey().Bytes()), + // " "), ", ")) fmt.Println("not_in_set: ", + // strings.Join(strings.Split(fmt.Sprintf("%v", not_in_set.PubKey().Bytes()), " "), ", ")) + // v1 := types.NewValidator(pk1.PubKey(), 148151478422287875) + // v2 := types.NewValidator(pk2.PubKey(), 158095448483785107) + // v3 := types.NewValidator(pk3.PubKey(), 770561664770006272) + // set := types.NewValidatorSet([]*types.Validator{v1, v2, v3}) + // fmt.Println("Hash:", strings.Join(strings.Split(fmt.Sprintf("%v", set.Hash()), " "), ", + // ")) } + let v1 = make_validator( + vec![ + 48, 163, 55, 132, 231, 147, 230, 163, 56, 158, 127, 218, 179, 139, 212, 103, + 218, 89, 122, 126, 229, 88, 84, 48, 32, 0, 185, 174, 63, 72, 203, 52, + ], + 148_151_478_422_287_875, + ); + let v2 = make_validator( + vec![ + 54, 253, 174, 153, 121, 74, 145, 180, 111, 16, 214, 48, 193, 109, 104, 134, 55, + 162, 151, 16, 182, 114, 125, 135, 32, 195, 236, 248, 64, 112, 74, 101, + ], + 158_095_448_483_785_107, + ); + let v3 = make_validator( + vec![ + 182, 205, 13, 86, 147, 27, 65, 49, 160, 118, 11, 180, 117, 35, 206, 35, 68, 19, + 27, 173, 69, 92, 204, 224, 200, 51, 249, 81, 105, 128, 112, 244, + ], + 770_561_664_770_006_272, + ); + let hash_expect = vec![ + 11, 64, 107, 4, 234, 81, 232, 75, 204, 199, 160, 114, 229, 97, 243, 95, 118, 213, + 17, 22, 57, 84, 71, 122, 200, 169, 192, 252, 41, 148, 223, 180, + ]; + + let val_set = Set::without_proposer(vec![v1.clone(), v2.clone(), v3.clone()]); + let hash = val_set.hash(); + assert_eq!(hash_expect, hash.as_bytes().to_vec()); + + let not_in_set = make_validator( + vec![ + 110, 147, 87, 120, 27, 218, 66, 209, 81, 4, 169, 153, 64, 163, 137, 89, 168, + 97, 219, 233, 42, 119, 24, 61, 47, 59, 76, 31, 182, 60, 13, 4, + ], + 10_000_000_000_000_000, + ); + + assert_eq!(val_set.validator(v1.address).unwrap(), v1); + assert_eq!(val_set.validator(v2.address).unwrap(), v2); + assert_eq!(val_set.validator(v3.address).unwrap(), v3); + assert_eq!(val_set.validator(not_in_set.address), None); + assert_eq!( + val_set.total_voting_power().value(), + 148_151_478_422_287_875 + 158_095_448_483_785_107 + 770_561_664_770_006_272 + ); + } } #[test] diff --git a/tendermint/tests/alt_crypto.rs b/tendermint/tests/alt_crypto.rs index 65b8f8c5e..462525e60 100644 --- a/tendermint/tests/alt_crypto.rs +++ b/tendermint/tests/alt_crypto.rs @@ -1,7 +1,9 @@ //! An imitation of alternative cryptographic function implementations //! for a chain environment that provides its own cryptographic API. -use digest::{consts::U32, Digest, FixedOutput, FixedOutputReset, Reset}; +use tendermint::crypto::{sha256::HASH_SIZE, Sha256}; + +use digest::Digest; use signature::{self, DigestSigner, DigestVerifier, Signature, Signer, Verifier}; use k256::ecdsa::{SigningKey, VerifyingKey}; @@ -38,7 +40,7 @@ impl SubstrateSigner { impl Signer for SubstrateSigner { fn try_sign(&self, msg: &[u8]) -> Result { - let mut hasher = SubstrateSha256::new(); + let mut hasher = sha2::Sha256::new(); hasher.update(msg); let signature = self.inner.try_sign_digest(hasher)?; Ok(SubstrateSignature(signature)) @@ -58,51 +60,17 @@ impl SubstrateSignatureVerifier { } } -impl DigestVerifier for SubstrateSignatureVerifier { - fn verify_digest( - &self, - digest: SubstrateSha256, - signature: &SubstrateSignature, - ) -> Result<(), signature::Error> { - self.inner.verify_digest(digest, &signature.0) - } -} - impl Verifier for SubstrateSignatureVerifier { fn verify(&self, msg: &[u8], signature: &SubstrateSignature) -> Result<(), signature::Error> { - let mut hasher = SubstrateSha256::new(); + let mut hasher = sha2::Sha256::new(); Digest::update(&mut hasher, msg); - self.verify_digest(hasher, signature) - } -} - -impl digest::OutputSizeUser for SubstrateSha256 { - type OutputSize = U32; -} - -impl digest::HashMarker for SubstrateSha256 {} - -impl digest::Update for SubstrateSha256 { - fn update(&mut self, data: &[u8]) { - digest::Update::update(&mut self.0, data) - } -} - -impl FixedOutput for SubstrateSha256 { - fn finalize_into(self, out: &mut digest::Output) { - *out = self.0.finalize(); - } -} - -impl Reset for SubstrateSha256 { - fn reset(&mut self) { - Reset::reset(&mut self.0) + self.inner.verify_digest(hasher, &signature.0) } } -impl FixedOutputReset for SubstrateSha256 { - fn finalize_into_reset(&mut self, out: &mut digest::Output) { - *out = self.0.finalize_reset(); +impl Sha256 for SubstrateSha256 { + fn digest(data: impl AsRef<[u8]>) -> [u8; HASH_SIZE] { + ::digest(data) } } @@ -115,9 +83,7 @@ mod tests { #[test] fn sha256_can_hash() { - let mut hasher = SubstrateSha256::new(); - hasher.update(b"hello world"); - let hash = hasher.finalize(); + let hash = SubstrateSha256::digest(b"hello world"); let hash = String::from_utf8(hex::encode(hash)).unwrap(); assert_eq!( From 1bb92a94fc4c01c0f0133e0b559ad712149702a0 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Wed, 14 Dec 2022 14:33:50 +0200 Subject: [PATCH 34/45] Fix all-features build --- tendermint/src/account.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tendermint/src/account.rs b/tendermint/src/account.rs index 8522040e9..6ce369a59 100644 --- a/tendermint/src/account.rs +++ b/tendermint/src/account.rs @@ -6,8 +6,6 @@ use core::{ str::FromStr, }; -#[cfg(feature = "secp256k1")] -use ripemd::Ripemd160; use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use subtle::{self, ConstantTimeEq}; use subtle_encoding::hex; @@ -96,6 +94,8 @@ mod key_conversions { #[cfg(feature = "secp256k1")] impl From for Id { fn from(pk: Secp256k1) -> Id { + use ripemd::Ripemd160; + let sha_digest = Sha256::digest(pk.to_bytes()); let ripemd_digest = Ripemd160::digest(&sha_digest[..]); let mut bytes = [0u8; LENGTH]; @@ -117,7 +117,7 @@ mod key_conversions { match pub_key { PublicKey::Ed25519(pk) => Id::from(pk), #[cfg(feature = "secp256k1")] - PublicKey::Secp256k1(pk) => account::Id::from(pk), + PublicKey::Secp256k1(pk) => Id::from(pk), } } } @@ -188,6 +188,8 @@ mod tests { #[test] #[cfg(feature = "secp256k1")] fn test_secp_id() { + use crate::public_key::Secp256k1; + // test vector for pubkey and id (address) let pubkey_hex = "02950E1CDFCB133D6024109FD489F734EEB4502418E538C28481F22BCE276F248C"; // SHA256: 034f706ac824dbb0d227c2ca30439e5be3766cfddc90f00bd530951d638b43a4 From 079ec4124dbfcb42b0b915ab7cea83ca53523bf8 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Wed, 14 Dec 2022 17:00:00 +0200 Subject: [PATCH 35/45] chore: fix clippy lints --- tendermint/src/merkle.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tendermint/src/merkle.rs b/tendermint/src/merkle.rs index 01731fc20..44611189e 100644 --- a/tendermint/src/merkle.rs +++ b/tendermint/src/merkle.rs @@ -77,7 +77,7 @@ where fn empty_hash(&mut self) -> Hash { // Get the output of an empty digest state. let digest = self.finalize_reset(); - copy_to_hash(&digest) + copy_to_hash(digest) } fn leaf_hash(&mut self, bytes: &[u8]) -> Hash { @@ -116,7 +116,7 @@ impl Default for NonIncremental { impl MerkleHash for NonIncremental { fn empty_hash(&mut self) -> Hash { - let digest = H::digest(&[]); + let digest = H::digest([]); copy_to_hash(digest) } From 1133fd939346adf884da33bbcc08eb0215172e4b Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Wed, 14 Dec 2022 17:27:34 +0200 Subject: [PATCH 36/45] tendermint: "rust-crypto" does not imply "k256" Also rename crypto::default::ecdsa_secp256 to ecdsa_secp256k1, to harmonize the naming with the feature that gates this module. --- tendermint/Cargo.toml | 2 +- tendermint/src/crypto/default.rs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tendermint/Cargo.toml b/tendermint/Cargo.toml index e28d23ad7..cf9c49dac 100644 --- a/tendermint/Cargo.toml +++ b/tendermint/Cargo.toml @@ -59,7 +59,7 @@ default = ["std", "rust-crypto"] std = ["flex-error/std", "flex-error/eyre_tracer", "clock"] clock = ["time/std"] secp256k1 = ["k256", "ripemd"] -rust-crypto = ["sha2", "k256"] +rust-crypto = ["sha2"] [dev-dependencies] k256 = { version = "0.11", default-features = false, features = ["ecdsa"] } diff --git a/tendermint/src/crypto/default.rs b/tendermint/src/crypto/default.rs index 66dfb943c..81ba318f1 100644 --- a/tendermint/src/crypto/default.rs +++ b/tendermint/src/crypto/default.rs @@ -24,6 +24,7 @@ impl super::Sha256 for Sha256 { } /// Types implementing the ECDSA algorithm using the Secp256k1 elliptic curve. -pub mod ecdsa_secp256 { +#[cfg(feature = "secp256k1")] +pub mod ecdsa_secp256k1 { pub use k256::ecdsa::{Signature, SigningKey, VerifyingKey}; } From 4a38c73a143dfe58714bb28816e7eca5301a497e Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Thu, 15 Dec 2022 14:23:36 +0200 Subject: [PATCH 37/45] rpc: require tendermint/rust-crypto Needed for NodeKey. --- config/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/Cargo.toml b/config/Cargo.toml index d162fa30e..f068ebaf7 100644 --- a/config/Cargo.toml +++ b/config/Cargo.toml @@ -25,7 +25,7 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [dependencies] -tendermint = { version = "0.27.0", default-features = false, path = "../tendermint" } +tendermint = { version = "0.27.0", default-features = false, features = ["rust-crypto"], path = "../tendermint" } flex-error = { version = "0.4.4", default-features = false } serde = { version = "1", features = ["derive"] } serde_json = "1" From adb6327cca2016db3df619941d18204935d81646 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Thu, 15 Dec 2022 15:53:55 +0200 Subject: [PATCH 38/45] Remove a bogus lint override --- tendermint/src/node/id.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/tendermint/src/node/id.rs b/tendermint/src/node/id.rs index 171fd0b7f..45117134f 100644 --- a/tendermint/src/node/id.rs +++ b/tendermint/src/node/id.rs @@ -11,8 +11,6 @@ use subtle_encoding::hex; use crate::{error::Error, prelude::*}; -#[allow(unused_imports)] - /// Length of a Node ID in bytes pub const LENGTH: usize = 20; From eac7b5a17f872c3af3b576fe485dc55e0526fa24 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Thu, 15 Dec 2022 15:59:04 +0200 Subject: [PATCH 39/45] Changelog entry for #1238 --- .../1238-crypto-provider-traits.md | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 .changelog/unreleased/breaking-changes/1238-crypto-provider-traits.md diff --git a/.changelog/unreleased/breaking-changes/1238-crypto-provider-traits.md b/.changelog/unreleased/breaking-changes/1238-crypto-provider-traits.md new file mode 100644 index 000000000..339036073 --- /dev/null +++ b/.changelog/unreleased/breaking-changes/1238-crypto-provider-traits.md @@ -0,0 +1,42 @@ +- `[tendermint]` Make implementations of cryptographic primitives replaceable + ([#1238](https://github.com/informalsystems/tendermint-rs/pull/1238)). + * Provide a `Sha256` trait in module `crypto` and make digest hashing + implementations available through it. + * The module `crypto::default` provides pure Rust implementations of the + cryptographic traits, mostly conformant to the framework given in + the `digest` and `signature` crates. The module is made available by a + new `rust-crypto` feature, enabled by default. + * `merkle::simple_hash_from_byte_vectors` is made generic over an + implementation of the new `MerkleHash` trait. Implementations for + Rust-Crypto conformant digest objects and the non-incremental + `crypto::Sha256` API are provided in the crate. + * The `Header::hash` and `ValidatorSet::hash` methods are gated by the + `rust-crypto` feature. Generic hashing methods not dependent on + the default crypto implementations are added for both types, + named `hash_with`. + * Conversions to `account::Id` and `node::Id` from `PublicKey` and + curve-specific key types are gated by the `rust-crypto` feature. + * The `validator::Info::new` method is gated by the `rust-crypto` feature. + * Remove a deprecated constant `signature::ED25519_SIGNATURE_SIZE`. + +- `[tendermint-light-client-verifier]` Changes for the new Tendermint crypto API + ([#1238](https://github.com/informalsystems/tendermint-rs/pull/1238)). + * The `rust-crypto` feature, enabled by default, guards the + batteries-included implementation types: `ProdVerifier`, `ProdPredicates`. + * Remove the `operations::hasher` API (`Hasher` and `ProdHasher`), + made unnecessary by the new crypto abstractions in the `tendermint` crate. + * The `VerificationPredicates` trait features a `Sha256` associated type + to represent the hasher implementation, replacing the `&dyn Hasher` + parameter passed to methods. + * Change the type of the `VerificationErrorDetail::FaultySigner` field + `validator_set` to `ValidatorSet`. This removes a hasher dependency from + `CommitValidator`, and `ProdCommitValidator` is now an empty dummy type. + +- `[tendermint-light-client]` Changes for the new Tendermint crypto API + ([#1238](https://github.com/informalsystems/tendermint-rs/pull/1238)). + * The `rust-crypto` feature enables the default crypto implementations, + and is required by the `rpc-client` and `unstable` features. + `ProdForkDetector` is guarded by this feature, and is made a specific + type alias to the hasher-generic `ProvidedForkDetector` type. + * `LightClientBuilder` gets another type parameter for the Merkle hasher. + Its generic constructors lose the `Hasher` parameter. From d525562420f5a5d25c09fbd5300eee9582c6fca8 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Mon, 23 Jan 2023 14:54:07 +0200 Subject: [PATCH 40/45] tendermint: crypto::signature::Verifier trait Define Verifier trait to abstract signature verification given a PublicKey. The ed25519-consensus dependency is made optional and gated by the "rust-crypto" feature. The Verifier implementation is provided for a dummy type crate::crypto::default::signature::Verifier, using ed25519-consensus. The Ed25519 key types in public_key and private_key module are redefined to in-crate newtypes. --- config/tests/mod.rs | 2 +- .../src/operations/voting_power.rs | 33 +++++++-- tendermint/Cargo.toml | 6 +- tendermint/src/crypto.rs | 3 + tendermint/src/crypto/default.rs | 2 + tendermint/src/crypto/default/signature.rs | 31 +++++++++ tendermint/src/crypto/ed25519.rs | 5 ++ tendermint/src/crypto/ed25519/signing_key.rs | 42 ++++++++++++ .../src/crypto/ed25519/verification_key.rs | 37 ++++++++++ tendermint/src/crypto/signature.rs | 35 ++++++++++ tendermint/src/lib.rs | 2 +- tendermint/src/private_key.rs | 7 +- tendermint/src/public_key.rs | 67 ++++--------------- tendermint/src/signature.rs | 1 + tendermint/src/validator.rs | 11 ++- testgen/src/helpers.rs | 7 +- testgen/src/validator.rs | 3 +- testgen/src/vote.rs | 1 + 18 files changed, 219 insertions(+), 76 deletions(-) create mode 100644 tendermint/src/crypto/default/signature.rs create mode 100644 tendermint/src/crypto/ed25519.rs create mode 100644 tendermint/src/crypto/ed25519/signing_key.rs create mode 100644 tendermint/src/crypto/ed25519/verification_key.rs create mode 100644 tendermint/src/crypto/signature.rs diff --git a/config/tests/mod.rs b/config/tests/mod.rs index 8d67c14dd..767a491a2 100644 --- a/config/tests/mod.rs +++ b/config/tests/mod.rs @@ -219,7 +219,7 @@ fn priv_validator_json_parser() { let raw_priv_validator_key = read_fixture("priv_validator_key.json"); let priv_validator_key = PrivValidatorKey::parse_json(raw_priv_validator_key).unwrap(); assert_eq!( - priv_validator_key.consensus_pubkey().to_hex(), + priv_validator_key.consensus_pubkey().public_key().to_hex(), "F26BF4B2A2E84CEB7A53C3F1AE77408779B20064782FBADBDF0E365959EE4534" ); } diff --git a/light-client-verifier/src/operations/voting_power.rs b/light-client-verifier/src/operations/voting_power.rs index 442db0510..720ecd92b 100644 --- a/light-client-verifier/src/operations/voting_power.rs +++ b/light-client-verifier/src/operations/voting_power.rs @@ -1,11 +1,12 @@ //! Provides an interface and default implementation for the `VotingPower` operation use alloc::collections::BTreeSet as HashSet; -use core::{convert::TryFrom, fmt}; +use core::{convert::TryFrom, fmt, marker::PhantomData}; use serde::{Deserialize, Serialize}; use tendermint::{ block::CommitSig, + crypto::signature, trust_threshold::TrustThreshold as _, vote::{SignedVote, ValidatorIndex, Vote}, }; @@ -98,11 +99,31 @@ pub trait VotingPowerCalculator: Send + Sync { ) -> Result; } -/// Default implementation of a `VotingPowerCalculator` -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] -pub struct ProdVotingPowerCalculator; +/// Default implementation of a `VotingPowerCalculator`, parameterized with +/// the signature verification trait. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct ProvidedVotingPowerCalculator { + _verifier: PhantomData, +} + +// Safety: the only member is phantom data +unsafe impl Send for ProvidedVotingPowerCalculator {} +unsafe impl Sync for ProvidedVotingPowerCalculator {} + +impl Default for ProvidedVotingPowerCalculator { + fn default() -> Self { + Self { + _verifier: PhantomData, + } + } +} + +/// Default implementation of a `VotingPowerCalculator`. +#[cfg(feature = "rust-crypto")] +pub type ProdVotingPowerCalculator = + ProvidedVotingPowerCalculator; -impl VotingPowerCalculator for ProdVotingPowerCalculator { +impl VotingPowerCalculator for ProvidedVotingPowerCalculator { fn voting_power_in( &self, signed_header: &SignedHeader, @@ -146,7 +167,7 @@ impl VotingPowerCalculator for ProdVotingPowerCalculator { // Check vote is valid let sign_bytes = signed_vote.sign_bytes(); if validator - .verify_signature(&sign_bytes, signed_vote.signature()) + .verify_signature::(&sign_bytes, signed_vote.signature()) .is_err() { return Err(VerificationError::invalid_signature( diff --git a/tendermint/Cargo.toml b/tendermint/Cargo.toml index 9905e1c08..53bf83e61 100644 --- a/tendermint/Cargo.toml +++ b/tendermint/Cargo.toml @@ -32,8 +32,7 @@ rustdoc-args = ["--cfg", "docsrs"] [dependencies] bytes = { version = "1.2", default-features = false, features = ["serde"] } digest = { version = "0.10", default-features = false } -ed25519 = { version = "1.3", default-features = false } -ed25519-consensus = { version = "2", default-features = false } +ed25519 = { version = "1.5", default-features = false } futures = { version = "0.3", default-features = false } num-traits = { version = "0.2", default-features = false } once_cell = { version = "1.3", default-features = false } @@ -50,6 +49,7 @@ tendermint-proto = { version = "0.28.0", default-features = false, path = "../pr time = { version = "0.3", default-features = false, features = ["macros", "parsing"] } zeroize = { version = "1.1", default-features = false, features = ["zeroize_derive", "alloc"] } flex-error = { version = "0.4.4", default-features = false } +ed25519-consensus = { version = "2", optional = true, default-features = false } sha2 = { version = "0.10", optional = true, default-features = false } k256 = { version = "0.11", optional = true, default-features = false, features = ["ecdsa"] } ripemd = { version = "0.1.3", optional = true, default-features = false } @@ -59,7 +59,7 @@ default = ["std", "rust-crypto"] std = ["flex-error/std", "flex-error/eyre_tracer", "clock"] clock = ["time/std"] secp256k1 = ["k256", "ripemd"] -rust-crypto = ["sha2"] +rust-crypto = ["sha2", "ed25519-consensus"] [dev-dependencies] k256 = { version = "0.11", default-features = false, features = ["ecdsa"] } diff --git a/tendermint/src/crypto.rs b/tendermint/src/crypto.rs index ef578e1b4..fb4a427a4 100644 --- a/tendermint/src/crypto.rs +++ b/tendermint/src/crypto.rs @@ -7,7 +7,10 @@ //! The abstract framework enabling this extensibility is provided by the //! `digest` and `signature` crates. +pub mod ed25519; pub mod sha256; +pub mod signature; + pub use sha256::Sha256; #[cfg(feature = "rust-crypto")] diff --git a/tendermint/src/crypto/default.rs b/tendermint/src/crypto/default.rs index 81ba318f1..441c35ad6 100644 --- a/tendermint/src/crypto/default.rs +++ b/tendermint/src/crypto/default.rs @@ -23,6 +23,8 @@ impl super::Sha256 for Sha256 { } } +pub mod signature; + /// Types implementing the ECDSA algorithm using the Secp256k1 elliptic curve. #[cfg(feature = "secp256k1")] pub mod ecdsa_secp256k1 { diff --git a/tendermint/src/crypto/default/signature.rs b/tendermint/src/crypto/default/signature.rs new file mode 100644 index 000000000..772221206 --- /dev/null +++ b/tendermint/src/crypto/default/signature.rs @@ -0,0 +1,31 @@ +//! The pure Rust implementation of signature verification functions. + +use crate::crypto::signature::Error; +use crate::{PublicKey, Signature}; + +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] +pub struct Verifier; + +impl crate::crypto::signature::Verifier for Verifier { + fn verify(pubkey: PublicKey, msg: &[u8], signature: &Signature) -> Result<(), Error> { + #[allow(unreachable_patterns)] + match pubkey { + PublicKey::Ed25519(pk) => { + let pubkey = ed25519_consensus::VerificationKey::try_from(pk) + .map_err(|_| Error::MalformedPublicKey)?; + let sig = ed25519_consensus::Signature::try_from(signature.as_bytes()) + .map_err(|_| Error::MalformedSignature)?; + pubkey + .verify(&sig, msg) + .map_err(|_| Error::VerificationFailed) + }, + #[cfg(feature = "secp256k1")] + PublicKey::Secp256k1(pk) => { + let signature = k256::ecdsa::Signature::try_from(signature.as_bytes()) + .map_err(|_| Error::MalformedSignature)?; + pk.verify(msg, &sig).map_err(|_| Error::VerificationFailed) + }, + _ => Err(Error::UnsupportedKeyType), + } + } +} diff --git a/tendermint/src/crypto/ed25519.rs b/tendermint/src/crypto/ed25519.rs new file mode 100644 index 000000000..78f9204f3 --- /dev/null +++ b/tendermint/src/crypto/ed25519.rs @@ -0,0 +1,5 @@ +mod signing_key; +mod verification_key; + +pub use signing_key::SigningKey; +pub use verification_key::VerificationKey; diff --git a/tendermint/src/crypto/ed25519/signing_key.rs b/tendermint/src/crypto/ed25519/signing_key.rs new file mode 100644 index 000000000..6351a2093 --- /dev/null +++ b/tendermint/src/crypto/ed25519/signing_key.rs @@ -0,0 +1,42 @@ +use super::VerificationKey; +use crate::Error; + +#[derive(Clone, Debug)] +pub struct SigningKey([u8; 32]); + +impl SigningKey { + pub fn as_bytes(&self) -> &[u8] { + &self.0 + } + + #[cfg(feature = "rust-crypto")] + pub fn verification_key(&self) -> VerificationKey { + let privkey = ed25519_consensus::SigningKey::from(self.0); + let pubkey = privkey.verification_key(); + let pubkey_bytes = pubkey.to_bytes(); + VerificationKey::new(pubkey_bytes) + } +} + +impl TryFrom<&'_ [u8]> for SigningKey { + type Error = Error; + + fn try_from(slice: &'_ [u8]) -> Result { + if slice.len() != 32 { + return Err(Error::invalid_key("invalid ed25519 key length".into())); + } + let mut bytes = [0u8; 32]; + bytes[..].copy_from_slice(slice); + Ok(Self(bytes)) + } +} + +#[cfg(feature = "rust-crypto")] +impl TryFrom for ed25519_consensus::SigningKey { + type Error = Error; + + fn try_from(src: SigningKey) -> Result { + ed25519_consensus::SigningKey::try_from(src.0) + .map_err(|_| Error::invalid_key("malformed Ed25519 private key".into())) + } +} diff --git a/tendermint/src/crypto/ed25519/verification_key.rs b/tendermint/src/crypto/ed25519/verification_key.rs new file mode 100644 index 000000000..562998df9 --- /dev/null +++ b/tendermint/src/crypto/ed25519/verification_key.rs @@ -0,0 +1,37 @@ +use crate::Error; + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct VerificationKey([u8; 32]); + +impl VerificationKey { + pub(super) fn new(bytes: [u8; 32]) -> Self { + Self(bytes) + } + + pub fn as_bytes(&self) -> &[u8] { + &self.0 + } +} + +impl TryFrom<&'_ [u8]> for VerificationKey { + type Error = Error; + + fn try_from(slice: &'_ [u8]) -> Result { + if slice.len() != 32 { + return Err(Error::invalid_key("invalid ed25519 key length".into())); + } + let mut bytes = [0u8; 32]; + bytes[..].copy_from_slice(slice); + Ok(Self(bytes)) + } +} + +#[cfg(feature = "rust-crypto")] +impl TryFrom for ed25519_consensus::VerificationKey { + type Error = Error; + + fn try_from(src: VerificationKey) -> Result { + ed25519_consensus::VerificationKey::try_from(src.0) + .map_err(|_| Error::invalid_key("malformed Ed25519 public key".into())) + } +} diff --git a/tendermint/src/crypto/signature.rs b/tendermint/src/crypto/signature.rs new file mode 100644 index 000000000..c27b4263a --- /dev/null +++ b/tendermint/src/crypto/signature.rs @@ -0,0 +1,35 @@ +use core::fmt::{self, Display}; + +use crate::{PublicKey, Signature}; + +/// Signature error. +/// +#[derive(Debug)] +pub enum Error { + /// This variant is deliberately opaque as to avoid side-channel leakage. + VerificationFailed, + /// The key used to verify a signature is not of a type supported by the implementation. + UnsupportedKeyType, + /// The encoding of the public key was malformed. + MalformedPublicKey, + /// The signature data was malformed. + MalformedSignature, +} + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::VerificationFailed => f.write_str("invalid signature"), + Error::UnsupportedKeyType => f.write_str("key type not supported"), + Error::MalformedPublicKey => f.write_str("malformed public key encoding"), + Error::MalformedSignature => f.write_str("malformed signature"), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for Error {} + +pub trait Verifier { + fn verify(pubkey: PublicKey, msg: &[u8], signature: &Signature) -> Result<(), Error>; +} diff --git a/tendermint/src/lib.rs b/tendermint/src/lib.rs index 4ee3a39f5..e106af29f 100644 --- a/tendermint/src/lib.rs +++ b/tendermint/src/lib.rs @@ -21,7 +21,7 @@ extern crate alloc; -#[cfg(test)] +#[cfg(any(feature = "std", test))] extern crate std; #[macro_use] diff --git a/tendermint/src/private_key.rs b/tendermint/src/private_key.rs index 0c0989f54..a1c89d6fe 100644 --- a/tendermint/src/private_key.rs +++ b/tendermint/src/private_key.rs @@ -1,10 +1,9 @@ //! Cryptographic private keys -pub use ed25519_consensus::SigningKey as Ed25519; - +pub use crate::crypto::ed25519::SigningKey as Ed25519; use crate::prelude::*; use crate::public_key::PublicKey; -use ed25519_consensus::VerificationKey; + use serde::{de, ser, Deserialize, Serialize}; use subtle_encoding::{Base64, Encoding}; use zeroize::Zeroizing; @@ -78,7 +77,7 @@ where // with the re-derived data. let signing_key = Ed25519::try_from(&keypair_bytes[0..32]) .map_err(|_| D::Error::custom("invalid signing key"))?; - let verification_key = VerificationKey::from(&signing_key); + let verification_key = signing_key.verification_key(); if &keypair_bytes[32..64] != verification_key.as_bytes() { return Err(D::Error::custom("keypair mismatch")); } diff --git a/tendermint/src/public_key.rs b/tendermint/src/public_key.rs index 286f005ee..80809a6ce 100644 --- a/tendermint/src/public_key.rs +++ b/tendermint/src/public_key.rs @@ -1,6 +1,5 @@ //! Public keys used in Tendermint networks -pub use ed25519_consensus::VerificationKey as Ed25519; #[cfg(feature = "secp256k1")] pub use k256::ecdsa::VerifyingKey as Secp256k1; @@ -11,7 +10,7 @@ pub use pub_key_request::PubKeyRequest; pub use pub_key_response::PubKeyResponse; use core::convert::TryFrom; -use core::{cmp::Ordering, fmt, ops::Deref, str::FromStr}; +use core::{cmp::Ordering, fmt, str::FromStr}; use serde::{de, ser, Deserialize, Deserializer, Serialize}; use serde_json::Value; use subtle_encoding::{base64, bech32, hex}; @@ -20,7 +19,8 @@ use tendermint_proto::{ Protobuf, }; -use crate::{error::Error, prelude::*, signature::Signature}; +pub use crate::crypto::ed25519::VerificationKey as Ed25519; +use crate::{error::Error, prelude::*}; #[cfg(feature = "secp256k1")] use signature::Verifier as _; @@ -132,8 +132,8 @@ impl TryFrom for PublicKey { .sum .ok_or_else(|| Error::invalid_key("empty sum".to_string()))?; if let Sum::Ed25519(b) = sum { - return Self::from_raw_ed25519(b) - .ok_or_else(|| Error::invalid_key("malformed ed25519 key".to_string())); + let key = Ed25519::try_from(&b[..])?; + return Ok(PublicKey::Ed25519(key)); } #[cfg(feature = "secp256k1")] if let Sum::Secp256k1(b) = sum { @@ -196,38 +196,6 @@ impl PublicKey { } } - /// Verify the given [`Signature`] using this public key - pub fn verify(&self, msg: &[u8], signature: &Signature) -> Result<(), Error> { - match self { - PublicKey::Ed25519(pk) => { - match ed25519_consensus::Signature::try_from(signature.as_bytes()) { - Ok(sig) => pk.verify(&sig, msg).map_err(|_| { - Error::signature_invalid( - "Ed25519 signature verification failed".to_string(), - ) - }), - Err(_) => Err(Error::signature_invalid( - "Could not parse Ed25519 signature".to_string(), - )), - } - }, - #[cfg(feature = "secp256k1")] - PublicKey::Secp256k1(pk) => { - match k256::ecdsa::Signature::try_from(signature.as_bytes()) { - Ok(sig) => pk.verify(msg, &sig).map_err(|_| { - Error::signature_invalid( - "Secp256k1 signature verification failed".to_string(), - ) - }), - Err(e) => Err(Error::signature_invalid(format!( - "invalid Secp256k1 signature: {}", - e - ))), - } - }, - } - } - /// Serialize this key as a byte vector. pub fn to_bytes(self) -> Vec { match self { @@ -338,15 +306,6 @@ impl TendermintKey { } } -// TODO(tarcieri): deprecate/remove this in favor of `TendermintKey::public_key` -impl Deref for TendermintKey { - type Target = PublicKey; - - fn deref(&self) -> &PublicKey { - self.public_key() - } -} - /// Public key algorithms #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum Algorithm { @@ -443,12 +402,10 @@ where #[cfg(test)] mod tests { - use core::convert::TryFrom; - use subtle_encoding::hex; use tendermint_proto::Protobuf; - use super::{PublicKey, Signature, TendermintKey}; + use super::{PublicKey, TendermintKey}; use crate::{prelude::*, public_key::PubKeyResponse}; const EXAMPLE_CONSENSUS_KEY: &str = @@ -476,7 +433,7 @@ mod tests { // fmt.Println(mustBech32ConsPub) // } assert_eq!( - example_key.to_bech32("cosmosvalconspub"), + example_key.public_key().to_bech32("cosmosvalconspub"), "cosmosvalconspub1zcjduepqfgjuveq2raetnjt4xwpffm63kmguxv2chdhvhf5lhslmtgeunh8qmf7exk" ); } @@ -502,7 +459,7 @@ mod tests { let pubkey: PublicKey = serde_json::from_str(json_string).unwrap(); assert_eq!( - pubkey.ed25519().unwrap().as_ref(), + pubkey.ed25519().unwrap().as_bytes(), [ 69, 185, 115, 48, 238, 34, 179, 146, 245, 133, 156, 250, 194, 142, 36, 61, 186, 109, 204, 236, 174, 123, 162, 211, 147, 143, 165, 62, 16, 245, 21, 25 @@ -703,8 +660,13 @@ mod tests { ], ]; + #[cfg(feature = "rust-crypto")] #[test] fn ed25519_test_vectors() { + use crate::crypto::default::signature::Verifier; + use crate::crypto::signature::Verifier as _; + use crate::Signature; + for (i, v) in ED25519_TEST_VECTORS.iter().enumerate() { let public_key = v[0]; let msg = v[1]; @@ -717,8 +679,7 @@ mod tests { _ => panic!("expected public key to be Ed25519: {:?}", public_key), } let sig = Signature::try_from(sig).unwrap(); - public_key - .verify(msg, &sig) + Verifier::verify(public_key, msg, &sig) .unwrap_or_else(|_| panic!("signature should be valid for test vector {}", i)); } } diff --git a/tendermint/src/signature.rs b/tendermint/src/signature.rs index 50d8a8e0a..4f6475c2f 100644 --- a/tendermint/src/signature.rs +++ b/tendermint/src/signature.rs @@ -88,6 +88,7 @@ impl From for Signature { } } +#[cfg(feature = "rust-crypto")] impl From for Signature { fn from(sig: ed25519_consensus::Signature) -> Signature { Self(sig.to_bytes().to_vec()) diff --git a/tendermint/src/validator.rs b/tendermint/src/validator.rs index f5c3c82f3..07022293f 100644 --- a/tendermint/src/validator.rs +++ b/tendermint/src/validator.rs @@ -14,6 +14,7 @@ use tendermint_proto::{ use crate::{ account, + crypto::signature::Verifier, crypto::Sha256, hash::Hash, merkle::{self, MerkleHash}, @@ -219,8 +220,12 @@ impl Info { /// Verify the given signature against the given sign_bytes using the validators /// public key. - pub fn verify_signature(&self, sign_bytes: &[u8], signature: &Signature) -> Result<(), Error> { - self.pub_key.verify(sign_bytes, signature) + pub fn verify_signature(&self, sign_bytes: &[u8], signature: &Signature) -> Result<(), Error> + where + V: Verifier, + { + V::verify(self.pub_key, sign_bytes, signature) + .map_err(|_| Error::signature_invalid("Ed25519 signature verification failed".into())) } #[cfg(feature = "rust-crypto")] @@ -276,7 +281,7 @@ impl From<&Info> for SimpleValidator { fn from(info: &Info) -> SimpleValidator { let sum = match &info.pub_key { PublicKey::Ed25519(pk) => Some(tendermint_proto::crypto::public_key::Sum::Ed25519( - pk.as_ref().to_vec(), + pk.as_bytes().to_vec(), )), #[cfg(feature = "secp256k1")] PublicKey::Secp256k1(pk) => Some(tendermint_proto::crypto::public_key::Sum::Secp256k1( diff --git a/testgen/src/helpers.rs b/testgen/src/helpers.rs index 63c0a5286..aba042389 100644 --- a/testgen/src/helpers.rs +++ b/testgen/src/helpers.rs @@ -45,9 +45,10 @@ pub fn get_vote_sign_bytes(chain_id: chain::Id, vote: &vote::Vote) -> Vec { signed_vote.sign_bytes() } -pub fn verify_signature(verifier: &public_key::Ed25519, msg: &[u8], signature: &Signature) -> bool { - let sig = ed25519_consensus::Signature::try_from(signature.as_bytes()); - sig.and_then(|sig| verifier.verify(&sig, msg)).is_ok() +pub fn verify_signature(pubkey: &public_key::Ed25519, msg: &[u8], signature: &Signature) -> bool { + let verifier = ed25519_consensus::VerificationKey::try_from(pubkey.as_bytes()).unwrap(); + let sig = ed25519_consensus::Signature::try_from(signature.as_bytes()).unwrap(); + verifier.verify(&sig, msg).is_ok() } pub fn get_time(abs: u64) -> Result { diff --git a/testgen/src/validator.rs b/testgen/src/validator.rs index 5e679b217..9a9f65173 100644 --- a/testgen/src/validator.rs +++ b/testgen/src/validator.rs @@ -1,4 +1,3 @@ -use ed25519_consensus::SigningKey as Ed25519SigningKey; use gumdrop::Options; use serde::{Deserialize, Serialize}; use simple_error::*; @@ -50,7 +49,7 @@ impl Validator { } bytes.extend(vec![0u8; 32 - bytes.len()].iter()); let signing_key = require_with!( - Ed25519SigningKey::try_from(&bytes[..]).ok(), + private_key::Ed25519::try_from(&bytes[..]).ok(), "failed to construct a seed from validator identifier" ); Ok(signing_key) diff --git a/testgen/src/vote.rs b/testgen/src/vote.rs index 2e136578d..a28a918e6 100644 --- a/testgen/src/vote.rs +++ b/testgen/src/vote.rs @@ -90,6 +90,7 @@ impl Generator for Vote { Some(h) => h, }; let signing_key = validator.get_private_key()?; + let signing_key = ed25519_consensus::SigningKey::try_from(signing_key).unwrap(); let block_validator = validator.generate()?; let block_header = header.generate()?; let block_id = if self.nil.is_some() { From 672db2df72386860c9d6a61925c03d507653bcb3 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Tue, 24 Jan 2023 13:52:02 +0200 Subject: [PATCH 41/45] tendermint: fix secp256k1 verification tests --- tendermint/src/crypto/default/signature.rs | 263 ++++++++++++++++++++- tendermint/src/public_key.rs | 263 +-------------------- 2 files changed, 263 insertions(+), 263 deletions(-) diff --git a/tendermint/src/crypto/default/signature.rs b/tendermint/src/crypto/default/signature.rs index 772221206..78c71d397 100644 --- a/tendermint/src/crypto/default/signature.rs +++ b/tendermint/src/crypto/default/signature.rs @@ -21,7 +21,9 @@ impl crate::crypto::signature::Verifier for Verifier { }, #[cfg(feature = "secp256k1")] PublicKey::Secp256k1(pk) => { - let signature = k256::ecdsa::Signature::try_from(signature.as_bytes()) + use signature::Verifier as _; + + let sig = k256::ecdsa::Signature::try_from(signature.as_bytes()) .map_err(|_| Error::MalformedSignature)?; pk.verify(msg, &sig).map_err(|_| Error::VerificationFailed) }, @@ -29,3 +31,262 @@ impl crate::crypto::signature::Verifier for Verifier { } } } + +#[cfg(test)] +mod tests { + use crate::crypto::default::signature::Verifier; + use crate::crypto::signature::Verifier as _; + use crate::{PublicKey, Signature}; + + // From https://datatracker.ietf.org/doc/html/rfc8032#section-7.1 + // Each test vector consists of: [public_key, message, signature]. + const ED25519_TEST_VECTORS: &[&[&[u8]]] = &[ + // Test 1 + &[ + &[ + 0xd7, 0x5a, 0x98, 0x01, 0x82, 0xb1, 0x0a, 0xb7, 0xd5, 0x4b, 0xfe, 0xd3, 0xc9, 0x64, + 0x07, 0x3a, 0x0e, 0xe1, 0x72, 0xf3, 0xda, 0xa6, 0x23, 0x25, 0xaf, 0x02, 0x1a, 0x68, + 0xf7, 0x07, 0x51, 0x1a, + ], + &[], + &[ + 0xe5, 0x56, 0x43, 0x00, 0xc3, 0x60, 0xac, 0x72, 0x90, 0x86, 0xe2, 0xcc, 0x80, 0x6e, + 0x82, 0x8a, 0x84, 0x87, 0x7f, 0x1e, 0xb8, 0xe5, 0xd9, 0x74, 0xd8, 0x73, 0xe0, 0x65, + 0x22, 0x49, 0x01, 0x55, 0x5f, 0xb8, 0x82, 0x15, 0x90, 0xa3, 0x3b, 0xac, 0xc6, 0x1e, + 0x39, 0x70, 0x1c, 0xf9, 0xb4, 0x6b, 0xd2, 0x5b, 0xf5, 0xf0, 0x59, 0x5b, 0xbe, 0x24, + 0x65, 0x51, 0x41, 0x43, 0x8e, 0x7a, 0x10, 0x0b, + ], + ], + // Test 2 + &[ + &[ + 0x3d, 0x40, 0x17, 0xc3, 0xe8, 0x43, 0x89, 0x5a, 0x92, 0xb7, 0x0a, 0xa7, 0x4d, 0x1b, + 0x7e, 0xbc, 0x9c, 0x98, 0x2c, 0xcf, 0x2e, 0xc4, 0x96, 0x8c, 0xc0, 0xcd, 0x55, 0xf1, + 0x2a, 0xf4, 0x66, 0x0c, + ], + &[0x72], + &[ + 0x92, 0xa0, 0x09, 0xa9, 0xf0, 0xd4, 0xca, 0xb8, 0x72, 0x0e, 0x82, 0x0b, 0x5f, 0x64, + 0x25, 0x40, 0xa2, 0xb2, 0x7b, 0x54, 0x16, 0x50, 0x3f, 0x8f, 0xb3, 0x76, 0x22, 0x23, + 0xeb, 0xdb, 0x69, 0xda, 0x08, 0x5a, 0xc1, 0xe4, 0x3e, 0x15, 0x99, 0x6e, 0x45, 0x8f, + 0x36, 0x13, 0xd0, 0xf1, 0x1d, 0x8c, 0x38, 0x7b, 0x2e, 0xae, 0xb4, 0x30, 0x2a, 0xee, + 0xb0, 0x0d, 0x29, 0x16, 0x12, 0xbb, 0x0c, 0x00, + ], + ], + // Test 3 + &[ + &[ + 0xfc, 0x51, 0xcd, 0x8e, 0x62, 0x18, 0xa1, 0xa3, 0x8d, 0xa4, 0x7e, 0xd0, 0x02, 0x30, + 0xf0, 0x58, 0x08, 0x16, 0xed, 0x13, 0xba, 0x33, 0x03, 0xac, 0x5d, 0xeb, 0x91, 0x15, + 0x48, 0x90, 0x80, 0x25, + ], + &[0xaf, 0x82], + &[ + 0x62, 0x91, 0xd6, 0x57, 0xde, 0xec, 0x24, 0x02, 0x48, 0x27, 0xe6, 0x9c, 0x3a, 0xbe, + 0x01, 0xa3, 0x0c, 0xe5, 0x48, 0xa2, 0x84, 0x74, 0x3a, 0x44, 0x5e, 0x36, 0x80, 0xd7, + 0xdb, 0x5a, 0xc3, 0xac, 0x18, 0xff, 0x9b, 0x53, 0x8d, 0x16, 0xf2, 0x90, 0xae, 0x67, + 0xf7, 0x60, 0x98, 0x4d, 0xc6, 0x59, 0x4a, 0x7c, 0x15, 0xe9, 0x71, 0x6e, 0xd2, 0x8d, + 0xc0, 0x27, 0xbe, 0xce, 0xea, 0x1e, 0xc4, 0x0a, + ], + ], + // Test 1024 + &[ + &[ + 0x27, 0x81, 0x17, 0xfc, 0x14, 0x4c, 0x72, 0x34, 0x0f, 0x67, 0xd0, 0xf2, 0x31, 0x6e, + 0x83, 0x86, 0xce, 0xff, 0xbf, 0x2b, 0x24, 0x28, 0xc9, 0xc5, 0x1f, 0xef, 0x7c, 0x59, + 0x7f, 0x1d, 0x42, 0x6e, + ], + &[ + 0x08, 0xb8, 0xb2, 0xb7, 0x33, 0x42, 0x42, 0x43, 0x76, 0x0f, 0xe4, 0x26, 0xa4, 0xb5, + 0x49, 0x08, 0x63, 0x21, 0x10, 0xa6, 0x6c, 0x2f, 0x65, 0x91, 0xea, 0xbd, 0x33, 0x45, + 0xe3, 0xe4, 0xeb, 0x98, 0xfa, 0x6e, 0x26, 0x4b, 0xf0, 0x9e, 0xfe, 0x12, 0xee, 0x50, + 0xf8, 0xf5, 0x4e, 0x9f, 0x77, 0xb1, 0xe3, 0x55, 0xf6, 0xc5, 0x05, 0x44, 0xe2, 0x3f, + 0xb1, 0x43, 0x3d, 0xdf, 0x73, 0xbe, 0x84, 0xd8, 0x79, 0xde, 0x7c, 0x00, 0x46, 0xdc, + 0x49, 0x96, 0xd9, 0xe7, 0x73, 0xf4, 0xbc, 0x9e, 0xfe, 0x57, 0x38, 0x82, 0x9a, 0xdb, + 0x26, 0xc8, 0x1b, 0x37, 0xc9, 0x3a, 0x1b, 0x27, 0x0b, 0x20, 0x32, 0x9d, 0x65, 0x86, + 0x75, 0xfc, 0x6e, 0xa5, 0x34, 0xe0, 0x81, 0x0a, 0x44, 0x32, 0x82, 0x6b, 0xf5, 0x8c, + 0x94, 0x1e, 0xfb, 0x65, 0xd5, 0x7a, 0x33, 0x8b, 0xbd, 0x2e, 0x26, 0x64, 0x0f, 0x89, + 0xff, 0xbc, 0x1a, 0x85, 0x8e, 0xfc, 0xb8, 0x55, 0x0e, 0xe3, 0xa5, 0xe1, 0x99, 0x8b, + 0xd1, 0x77, 0xe9, 0x3a, 0x73, 0x63, 0xc3, 0x44, 0xfe, 0x6b, 0x19, 0x9e, 0xe5, 0xd0, + 0x2e, 0x82, 0xd5, 0x22, 0xc4, 0xfe, 0xba, 0x15, 0x45, 0x2f, 0x80, 0x28, 0x8a, 0x82, + 0x1a, 0x57, 0x91, 0x16, 0xec, 0x6d, 0xad, 0x2b, 0x3b, 0x31, 0x0d, 0xa9, 0x03, 0x40, + 0x1a, 0xa6, 0x21, 0x00, 0xab, 0x5d, 0x1a, 0x36, 0x55, 0x3e, 0x06, 0x20, 0x3b, 0x33, + 0x89, 0x0c, 0xc9, 0xb8, 0x32, 0xf7, 0x9e, 0xf8, 0x05, 0x60, 0xcc, 0xb9, 0xa3, 0x9c, + 0xe7, 0x67, 0x96, 0x7e, 0xd6, 0x28, 0xc6, 0xad, 0x57, 0x3c, 0xb1, 0x16, 0xdb, 0xef, + 0xef, 0xd7, 0x54, 0x99, 0xda, 0x96, 0xbd, 0x68, 0xa8, 0xa9, 0x7b, 0x92, 0x8a, 0x8b, + 0xbc, 0x10, 0x3b, 0x66, 0x21, 0xfc, 0xde, 0x2b, 0xec, 0xa1, 0x23, 0x1d, 0x20, 0x6b, + 0xe6, 0xcd, 0x9e, 0xc7, 0xaf, 0xf6, 0xf6, 0xc9, 0x4f, 0xcd, 0x72, 0x04, 0xed, 0x34, + 0x55, 0xc6, 0x8c, 0x83, 0xf4, 0xa4, 0x1d, 0xa4, 0xaf, 0x2b, 0x74, 0xef, 0x5c, 0x53, + 0xf1, 0xd8, 0xac, 0x70, 0xbd, 0xcb, 0x7e, 0xd1, 0x85, 0xce, 0x81, 0xbd, 0x84, 0x35, + 0x9d, 0x44, 0x25, 0x4d, 0x95, 0x62, 0x9e, 0x98, 0x55, 0xa9, 0x4a, 0x7c, 0x19, 0x58, + 0xd1, 0xf8, 0xad, 0xa5, 0xd0, 0x53, 0x2e, 0xd8, 0xa5, 0xaa, 0x3f, 0xb2, 0xd1, 0x7b, + 0xa7, 0x0e, 0xb6, 0x24, 0x8e, 0x59, 0x4e, 0x1a, 0x22, 0x97, 0xac, 0xbb, 0xb3, 0x9d, + 0x50, 0x2f, 0x1a, 0x8c, 0x6e, 0xb6, 0xf1, 0xce, 0x22, 0xb3, 0xde, 0x1a, 0x1f, 0x40, + 0xcc, 0x24, 0x55, 0x41, 0x19, 0xa8, 0x31, 0xa9, 0xaa, 0xd6, 0x07, 0x9c, 0xad, 0x88, + 0x42, 0x5d, 0xe6, 0xbd, 0xe1, 0xa9, 0x18, 0x7e, 0xbb, 0x60, 0x92, 0xcf, 0x67, 0xbf, + 0x2b, 0x13, 0xfd, 0x65, 0xf2, 0x70, 0x88, 0xd7, 0x8b, 0x7e, 0x88, 0x3c, 0x87, 0x59, + 0xd2, 0xc4, 0xf5, 0xc6, 0x5a, 0xdb, 0x75, 0x53, 0x87, 0x8a, 0xd5, 0x75, 0xf9, 0xfa, + 0xd8, 0x78, 0xe8, 0x0a, 0x0c, 0x9b, 0xa6, 0x3b, 0xcb, 0xcc, 0x27, 0x32, 0xe6, 0x94, + 0x85, 0xbb, 0xc9, 0xc9, 0x0b, 0xfb, 0xd6, 0x24, 0x81, 0xd9, 0x08, 0x9b, 0xec, 0xcf, + 0x80, 0xcf, 0xe2, 0xdf, 0x16, 0xa2, 0xcf, 0x65, 0xbd, 0x92, 0xdd, 0x59, 0x7b, 0x07, + 0x07, 0xe0, 0x91, 0x7a, 0xf4, 0x8b, 0xbb, 0x75, 0xfe, 0xd4, 0x13, 0xd2, 0x38, 0xf5, + 0x55, 0x5a, 0x7a, 0x56, 0x9d, 0x80, 0xc3, 0x41, 0x4a, 0x8d, 0x08, 0x59, 0xdc, 0x65, + 0xa4, 0x61, 0x28, 0xba, 0xb2, 0x7a, 0xf8, 0x7a, 0x71, 0x31, 0x4f, 0x31, 0x8c, 0x78, + 0x2b, 0x23, 0xeb, 0xfe, 0x80, 0x8b, 0x82, 0xb0, 0xce, 0x26, 0x40, 0x1d, 0x2e, 0x22, + 0xf0, 0x4d, 0x83, 0xd1, 0x25, 0x5d, 0xc5, 0x1a, 0xdd, 0xd3, 0xb7, 0x5a, 0x2b, 0x1a, + 0xe0, 0x78, 0x45, 0x04, 0xdf, 0x54, 0x3a, 0xf8, 0x96, 0x9b, 0xe3, 0xea, 0x70, 0x82, + 0xff, 0x7f, 0xc9, 0x88, 0x8c, 0x14, 0x4d, 0xa2, 0xaf, 0x58, 0x42, 0x9e, 0xc9, 0x60, + 0x31, 0xdb, 0xca, 0xd3, 0xda, 0xd9, 0xaf, 0x0d, 0xcb, 0xaa, 0xaf, 0x26, 0x8c, 0xb8, + 0xfc, 0xff, 0xea, 0xd9, 0x4f, 0x3c, 0x7c, 0xa4, 0x95, 0xe0, 0x56, 0xa9, 0xb4, 0x7a, + 0xcd, 0xb7, 0x51, 0xfb, 0x73, 0xe6, 0x66, 0xc6, 0xc6, 0x55, 0xad, 0xe8, 0x29, 0x72, + 0x97, 0xd0, 0x7a, 0xd1, 0xba, 0x5e, 0x43, 0xf1, 0xbc, 0xa3, 0x23, 0x01, 0x65, 0x13, + 0x39, 0xe2, 0x29, 0x04, 0xcc, 0x8c, 0x42, 0xf5, 0x8c, 0x30, 0xc0, 0x4a, 0xaf, 0xdb, + 0x03, 0x8d, 0xda, 0x08, 0x47, 0xdd, 0x98, 0x8d, 0xcd, 0xa6, 0xf3, 0xbf, 0xd1, 0x5c, + 0x4b, 0x4c, 0x45, 0x25, 0x00, 0x4a, 0xa0, 0x6e, 0xef, 0xf8, 0xca, 0x61, 0x78, 0x3a, + 0xac, 0xec, 0x57, 0xfb, 0x3d, 0x1f, 0x92, 0xb0, 0xfe, 0x2f, 0xd1, 0xa8, 0x5f, 0x67, + 0x24, 0x51, 0x7b, 0x65, 0xe6, 0x14, 0xad, 0x68, 0x08, 0xd6, 0xf6, 0xee, 0x34, 0xdf, + 0xf7, 0x31, 0x0f, 0xdc, 0x82, 0xae, 0xbf, 0xd9, 0x04, 0xb0, 0x1e, 0x1d, 0xc5, 0x4b, + 0x29, 0x27, 0x09, 0x4b, 0x2d, 0xb6, 0x8d, 0x6f, 0x90, 0x3b, 0x68, 0x40, 0x1a, 0xde, + 0xbf, 0x5a, 0x7e, 0x08, 0xd7, 0x8f, 0xf4, 0xef, 0x5d, 0x63, 0x65, 0x3a, 0x65, 0x04, + 0x0c, 0xf9, 0xbf, 0xd4, 0xac, 0xa7, 0x98, 0x4a, 0x74, 0xd3, 0x71, 0x45, 0x98, 0x67, + 0x80, 0xfc, 0x0b, 0x16, 0xac, 0x45, 0x16, 0x49, 0xde, 0x61, 0x88, 0xa7, 0xdb, 0xdf, + 0x19, 0x1f, 0x64, 0xb5, 0xfc, 0x5e, 0x2a, 0xb4, 0x7b, 0x57, 0xf7, 0xf7, 0x27, 0x6c, + 0xd4, 0x19, 0xc1, 0x7a, 0x3c, 0xa8, 0xe1, 0xb9, 0x39, 0xae, 0x49, 0xe4, 0x88, 0xac, + 0xba, 0x6b, 0x96, 0x56, 0x10, 0xb5, 0x48, 0x01, 0x09, 0xc8, 0xb1, 0x7b, 0x80, 0xe1, + 0xb7, 0xb7, 0x50, 0xdf, 0xc7, 0x59, 0x8d, 0x5d, 0x50, 0x11, 0xfd, 0x2d, 0xcc, 0x56, + 0x00, 0xa3, 0x2e, 0xf5, 0xb5, 0x2a, 0x1e, 0xcc, 0x82, 0x0e, 0x30, 0x8a, 0xa3, 0x42, + 0x72, 0x1a, 0xac, 0x09, 0x43, 0xbf, 0x66, 0x86, 0xb6, 0x4b, 0x25, 0x79, 0x37, 0x65, + 0x04, 0xcc, 0xc4, 0x93, 0xd9, 0x7e, 0x6a, 0xed, 0x3f, 0xb0, 0xf9, 0xcd, 0x71, 0xa4, + 0x3d, 0xd4, 0x97, 0xf0, 0x1f, 0x17, 0xc0, 0xe2, 0xcb, 0x37, 0x97, 0xaa, 0x2a, 0x2f, + 0x25, 0x66, 0x56, 0x16, 0x8e, 0x6c, 0x49, 0x6a, 0xfc, 0x5f, 0xb9, 0x32, 0x46, 0xf6, + 0xb1, 0x11, 0x63, 0x98, 0xa3, 0x46, 0xf1, 0xa6, 0x41, 0xf3, 0xb0, 0x41, 0xe9, 0x89, + 0xf7, 0x91, 0x4f, 0x90, 0xcc, 0x2c, 0x7f, 0xff, 0x35, 0x78, 0x76, 0xe5, 0x06, 0xb5, + 0x0d, 0x33, 0x4b, 0xa7, 0x7c, 0x22, 0x5b, 0xc3, 0x07, 0xba, 0x53, 0x71, 0x52, 0xf3, + 0xf1, 0x61, 0x0e, 0x4e, 0xaf, 0xe5, 0x95, 0xf6, 0xd9, 0xd9, 0x0d, 0x11, 0xfa, 0xa9, + 0x33, 0xa1, 0x5e, 0xf1, 0x36, 0x95, 0x46, 0x86, 0x8a, 0x7f, 0x3a, 0x45, 0xa9, 0x67, + 0x68, 0xd4, 0x0f, 0xd9, 0xd0, 0x34, 0x12, 0xc0, 0x91, 0xc6, 0x31, 0x5c, 0xf4, 0xfd, + 0xe7, 0xcb, 0x68, 0x60, 0x69, 0x37, 0x38, 0x0d, 0xb2, 0xea, 0xaa, 0x70, 0x7b, 0x4c, + 0x41, 0x85, 0xc3, 0x2e, 0xdd, 0xcd, 0xd3, 0x06, 0x70, 0x5e, 0x4d, 0xc1, 0xff, 0xc8, + 0x72, 0xee, 0xee, 0x47, 0x5a, 0x64, 0xdf, 0xac, 0x86, 0xab, 0xa4, 0x1c, 0x06, 0x18, + 0x98, 0x3f, 0x87, 0x41, 0xc5, 0xef, 0x68, 0xd3, 0xa1, 0x01, 0xe8, 0xa3, 0xb8, 0xca, + 0xc6, 0x0c, 0x90, 0x5c, 0x15, 0xfc, 0x91, 0x08, 0x40, 0xb9, 0x4c, 0x00, 0xa0, 0xb9, + 0xd0, + ], + &[ + 0x0a, 0xab, 0x4c, 0x90, 0x05, 0x01, 0xb3, 0xe2, 0x4d, 0x7c, 0xdf, 0x46, 0x63, 0x32, + 0x6a, 0x3a, 0x87, 0xdf, 0x5e, 0x48, 0x43, 0xb2, 0xcb, 0xdb, 0x67, 0xcb, 0xf6, 0xe4, + 0x60, 0xfe, 0xc3, 0x50, 0xaa, 0x53, 0x71, 0xb1, 0x50, 0x8f, 0x9f, 0x45, 0x28, 0xec, + 0xea, 0x23, 0xc4, 0x36, 0xd9, 0x4b, 0x5e, 0x8f, 0xcd, 0x4f, 0x68, 0x1e, 0x30, 0xa6, + 0xac, 0x00, 0xa9, 0x70, 0x4a, 0x18, 0x8a, 0x03, + ], + ], + ]; + + #[test] + fn ed25519_test_vectors() { + for (i, v) in ED25519_TEST_VECTORS.iter().enumerate() { + let public_key = v[0]; + let msg = v[1]; + let sig = v[2]; + + let public_key = PublicKey::from_raw_ed25519(public_key).unwrap(); + match public_key { + PublicKey::Ed25519(_) => {}, + #[cfg(feature = "secp256k1")] + _ => panic!("expected public key to be Ed25519: {:?}", public_key), + } + let sig = Signature::try_from(sig).unwrap(); + Verifier::verify(public_key, msg, &sig) + .unwrap_or_else(|_| panic!("signature should be valid for test vector {}", i)); + } + } + + // Arbitrary "valid" tests taken from + // https://github.com/google/wycheproof/blob/2196000605e45d91097147c9c71f26b72af58003/testvectors/ecdsa_secp256k1_sha256_test.json + // + // Each test vector consists of: [public_key, message, signature]. + // + // NB: It appears as though all signatures in this test suite are + // DER-encoded. + #[cfg(feature = "secp256k1")] + const SECP256K1_TEST_VECTORS: &[&[&[u8]]] = &[ + // tcId 3 + &[ + &[ + 0x04, 0xb8, 0x38, 0xff, 0x44, 0xe5, 0xbc, 0x17, 0x7b, 0xf2, 0x11, 0x89, 0xd0, 0x76, + 0x60, 0x82, 0xfc, 0x9d, 0x84, 0x32, 0x26, 0x88, 0x7f, 0xc9, 0x76, 0x03, 0x71, 0x10, + 0x0b, 0x7e, 0xe2, 0x0a, 0x6f, 0xf0, 0xc9, 0xd7, 0x5b, 0xfb, 0xa7, 0xb3, 0x1a, 0x6b, + 0xca, 0x19, 0x74, 0x49, 0x6e, 0xeb, 0x56, 0xde, 0x35, 0x70, 0x71, 0x95, 0x5d, 0x83, + 0xc4, 0xb1, 0xba, 0xda, 0xa0, 0xb2, 0x18, 0x32, 0xe9, + ], + &[0x31, 0x32, 0x33, 0x34, 0x30, 0x30], + &[ + 0x30, 0x45, 0x02, 0x21, 0x00, 0x81, 0x3e, 0xf7, 0x9c, 0xce, 0xfa, 0x9a, 0x56, 0xf7, + 0xba, 0x80, 0x5f, 0x0e, 0x47, 0x85, 0x84, 0xfe, 0x5f, 0x0d, 0xd5, 0xf5, 0x67, 0xbc, + 0x09, 0xb5, 0x12, 0x3c, 0xcb, 0xc9, 0x83, 0x23, 0x65, 0x02, 0x20, 0x6f, 0xf1, 0x8a, + 0x52, 0xdc, 0xc0, 0x33, 0x6f, 0x7a, 0xf6, 0x24, 0x00, 0xa6, 0xdd, 0x9b, 0x81, 0x07, + 0x32, 0xba, 0xf1, 0xff, 0x75, 0x80, 0x00, 0xd6, 0xf6, 0x13, 0xa5, 0x56, 0xeb, 0x31, + 0xba, + ], + ], + // tcId 230 + &[ + &[ + 0x04, 0xb8, 0x38, 0xff, 0x44, 0xe5, 0xbc, 0x17, 0x7b, 0xf2, 0x11, 0x89, 0xd0, 0x76, + 0x60, 0x82, 0xfc, 0x9d, 0x84, 0x32, 0x26, 0x88, 0x7f, 0xc9, 0x76, 0x03, 0x71, 0x10, + 0x0b, 0x7e, 0xe2, 0x0a, 0x6f, 0xf0, 0xc9, 0xd7, 0x5b, 0xfb, 0xa7, 0xb3, 0x1a, 0x6b, + 0xca, 0x19, 0x74, 0x49, 0x6e, 0xeb, 0x56, 0xde, 0x35, 0x70, 0x71, 0x95, 0x5d, 0x83, + 0xc4, 0xb1, 0xba, 0xda, 0xa0, 0xb2, 0x18, 0x32, 0xe9, + ], + &[0x32, 0x35, 0x35, 0x38, 0x35], + &[ + 0x30, 0x45, 0x02, 0x21, 0x00, 0xdd, 0x1b, 0x7d, 0x09, 0xa7, 0xbd, 0x82, 0x18, 0x96, + 0x10, 0x34, 0xa3, 0x9a, 0x87, 0xfe, 0xcf, 0x53, 0x14, 0xf0, 0x0c, 0x4d, 0x25, 0xeb, + 0x58, 0xa0, 0x7a, 0xc8, 0x5e, 0x85, 0xea, 0xb5, 0x16, 0x02, 0x20, 0x35, 0x13, 0x8c, + 0x40, 0x1e, 0xf8, 0xd3, 0x49, 0x3d, 0x65, 0xc9, 0x00, 0x2f, 0xe6, 0x2b, 0x43, 0xae, + 0xe5, 0x68, 0x73, 0x1b, 0x74, 0x45, 0x48, 0x35, 0x89, 0x96, 0xd9, 0xcc, 0x42, 0x7e, + 0x06, + ], + ], + // tcId 231 + &[ + &[ + 0x04, 0xb8, 0x38, 0xff, 0x44, 0xe5, 0xbc, 0x17, 0x7b, 0xf2, 0x11, 0x89, 0xd0, 0x76, + 0x60, 0x82, 0xfc, 0x9d, 0x84, 0x32, 0x26, 0x88, 0x7f, 0xc9, 0x76, 0x03, 0x71, 0x10, + 0x0b, 0x7e, 0xe2, 0x0a, 0x6f, 0xf0, 0xc9, 0xd7, 0x5b, 0xfb, 0xa7, 0xb3, 0x1a, 0x6b, + 0xca, 0x19, 0x74, 0x49, 0x6e, 0xeb, 0x56, 0xde, 0x35, 0x70, 0x71, 0x95, 0x5d, 0x83, + 0xc4, 0xb1, 0xba, 0xda, 0xa0, 0xb2, 0x18, 0x32, 0xe9, + ], + &[0x34, 0x32, 0x36, 0x34, 0x37, 0x39, 0x37, 0x32, 0x34], + &[ + 0x30, 0x45, 0x02, 0x21, 0x00, 0x95, 0xc2, 0x92, 0x67, 0xd9, 0x72, 0xa0, 0x43, 0xd9, + 0x55, 0x22, 0x45, 0x46, 0x22, 0x2b, 0xba, 0x34, 0x3f, 0xc1, 0xd4, 0xdb, 0x0f, 0xec, + 0x26, 0x2a, 0x33, 0xac, 0x61, 0x30, 0x56, 0x96, 0xae, 0x02, 0x20, 0x6e, 0xdf, 0xe9, + 0x67, 0x13, 0xae, 0xd5, 0x6f, 0x8a, 0x28, 0xa6, 0x65, 0x3f, 0x57, 0xe0, 0xb8, 0x29, + 0x71, 0x2e, 0x5e, 0xdd, 0xc6, 0x7f, 0x34, 0x68, 0x2b, 0x24, 0xf0, 0x67, 0x6b, 0x26, + 0x40, + ], + ], + ]; + + #[cfg(feature = "secp256k1")] + #[test] + fn secp256k1_test_vectors() { + for (i, v) in SECP256K1_TEST_VECTORS.iter().enumerate() { + let public_key = v[0]; + let msg = v[1]; + let sig = v[2]; + + let public_key = PublicKey::from_raw_secp256k1(public_key).unwrap(); + match public_key { + PublicKey::Secp256k1(_) => {}, + _ => panic!("expected public key to be secp256k1: {:?}", public_key), + } + let der_sig = k256::ecdsa::Signature::from_der(sig).unwrap(); + let sig = der_sig.as_ref(); + let sig = Signature::try_from(sig).unwrap(); + Verifier::verify(public_key, msg, &sig) + .unwrap_or_else(|_| panic!("signature should be valid for test vector {}", i)); + } + } +} diff --git a/tendermint/src/public_key.rs b/tendermint/src/public_key.rs index 80809a6ce..ed7ab3124 100644 --- a/tendermint/src/public_key.rs +++ b/tendermint/src/public_key.rs @@ -22,9 +22,6 @@ use tendermint_proto::{ pub use crate::crypto::ed25519::VerificationKey as Ed25519; use crate::{error::Error, prelude::*}; -#[cfg(feature = "secp256k1")] -use signature::Verifier as _; - // Note:On the golang side this is generic in the sense that it could everything that implements // github.com/tendermint/tendermint/crypto.PubKey // While this is meant to be used with different key-types, it currently only uses a PubKeyEd25519 @@ -448,7 +445,7 @@ mod tests { .unwrap(), ); assert_eq!( - example_key.to_bech32("cosmospub"), + example_key.public_key().to_bech32("cosmospub"), "cosmospub1addwnpepq2skx090esq7h7md0r3e76r6ruyet330e904r6k3pgpwuzl92x6actrt4uq" ); } @@ -515,262 +512,4 @@ mod tests { assert_eq!(got, encoded); assert_eq!(PubKeyResponse::decode_vec(&encoded).unwrap(), msg); } - - // From https://datatracker.ietf.org/doc/html/rfc8032#section-7.1 - // Each test vector consists of: [public_key, message, signature]. - const ED25519_TEST_VECTORS: &[&[&[u8]]] = &[ - // Test 1 - &[ - &[ - 0xd7, 0x5a, 0x98, 0x01, 0x82, 0xb1, 0x0a, 0xb7, 0xd5, 0x4b, 0xfe, 0xd3, 0xc9, 0x64, - 0x07, 0x3a, 0x0e, 0xe1, 0x72, 0xf3, 0xda, 0xa6, 0x23, 0x25, 0xaf, 0x02, 0x1a, 0x68, - 0xf7, 0x07, 0x51, 0x1a, - ], - &[], - &[ - 0xe5, 0x56, 0x43, 0x00, 0xc3, 0x60, 0xac, 0x72, 0x90, 0x86, 0xe2, 0xcc, 0x80, 0x6e, - 0x82, 0x8a, 0x84, 0x87, 0x7f, 0x1e, 0xb8, 0xe5, 0xd9, 0x74, 0xd8, 0x73, 0xe0, 0x65, - 0x22, 0x49, 0x01, 0x55, 0x5f, 0xb8, 0x82, 0x15, 0x90, 0xa3, 0x3b, 0xac, 0xc6, 0x1e, - 0x39, 0x70, 0x1c, 0xf9, 0xb4, 0x6b, 0xd2, 0x5b, 0xf5, 0xf0, 0x59, 0x5b, 0xbe, 0x24, - 0x65, 0x51, 0x41, 0x43, 0x8e, 0x7a, 0x10, 0x0b, - ], - ], - // Test 2 - &[ - &[ - 0x3d, 0x40, 0x17, 0xc3, 0xe8, 0x43, 0x89, 0x5a, 0x92, 0xb7, 0x0a, 0xa7, 0x4d, 0x1b, - 0x7e, 0xbc, 0x9c, 0x98, 0x2c, 0xcf, 0x2e, 0xc4, 0x96, 0x8c, 0xc0, 0xcd, 0x55, 0xf1, - 0x2a, 0xf4, 0x66, 0x0c, - ], - &[0x72], - &[ - 0x92, 0xa0, 0x09, 0xa9, 0xf0, 0xd4, 0xca, 0xb8, 0x72, 0x0e, 0x82, 0x0b, 0x5f, 0x64, - 0x25, 0x40, 0xa2, 0xb2, 0x7b, 0x54, 0x16, 0x50, 0x3f, 0x8f, 0xb3, 0x76, 0x22, 0x23, - 0xeb, 0xdb, 0x69, 0xda, 0x08, 0x5a, 0xc1, 0xe4, 0x3e, 0x15, 0x99, 0x6e, 0x45, 0x8f, - 0x36, 0x13, 0xd0, 0xf1, 0x1d, 0x8c, 0x38, 0x7b, 0x2e, 0xae, 0xb4, 0x30, 0x2a, 0xee, - 0xb0, 0x0d, 0x29, 0x16, 0x12, 0xbb, 0x0c, 0x00, - ], - ], - // Test 3 - &[ - &[ - 0xfc, 0x51, 0xcd, 0x8e, 0x62, 0x18, 0xa1, 0xa3, 0x8d, 0xa4, 0x7e, 0xd0, 0x02, 0x30, - 0xf0, 0x58, 0x08, 0x16, 0xed, 0x13, 0xba, 0x33, 0x03, 0xac, 0x5d, 0xeb, 0x91, 0x15, - 0x48, 0x90, 0x80, 0x25, - ], - &[0xaf, 0x82], - &[ - 0x62, 0x91, 0xd6, 0x57, 0xde, 0xec, 0x24, 0x02, 0x48, 0x27, 0xe6, 0x9c, 0x3a, 0xbe, - 0x01, 0xa3, 0x0c, 0xe5, 0x48, 0xa2, 0x84, 0x74, 0x3a, 0x44, 0x5e, 0x36, 0x80, 0xd7, - 0xdb, 0x5a, 0xc3, 0xac, 0x18, 0xff, 0x9b, 0x53, 0x8d, 0x16, 0xf2, 0x90, 0xae, 0x67, - 0xf7, 0x60, 0x98, 0x4d, 0xc6, 0x59, 0x4a, 0x7c, 0x15, 0xe9, 0x71, 0x6e, 0xd2, 0x8d, - 0xc0, 0x27, 0xbe, 0xce, 0xea, 0x1e, 0xc4, 0x0a, - ], - ], - // Test 1024 - &[ - &[ - 0x27, 0x81, 0x17, 0xfc, 0x14, 0x4c, 0x72, 0x34, 0x0f, 0x67, 0xd0, 0xf2, 0x31, 0x6e, - 0x83, 0x86, 0xce, 0xff, 0xbf, 0x2b, 0x24, 0x28, 0xc9, 0xc5, 0x1f, 0xef, 0x7c, 0x59, - 0x7f, 0x1d, 0x42, 0x6e, - ], - &[ - 0x08, 0xb8, 0xb2, 0xb7, 0x33, 0x42, 0x42, 0x43, 0x76, 0x0f, 0xe4, 0x26, 0xa4, 0xb5, - 0x49, 0x08, 0x63, 0x21, 0x10, 0xa6, 0x6c, 0x2f, 0x65, 0x91, 0xea, 0xbd, 0x33, 0x45, - 0xe3, 0xe4, 0xeb, 0x98, 0xfa, 0x6e, 0x26, 0x4b, 0xf0, 0x9e, 0xfe, 0x12, 0xee, 0x50, - 0xf8, 0xf5, 0x4e, 0x9f, 0x77, 0xb1, 0xe3, 0x55, 0xf6, 0xc5, 0x05, 0x44, 0xe2, 0x3f, - 0xb1, 0x43, 0x3d, 0xdf, 0x73, 0xbe, 0x84, 0xd8, 0x79, 0xde, 0x7c, 0x00, 0x46, 0xdc, - 0x49, 0x96, 0xd9, 0xe7, 0x73, 0xf4, 0xbc, 0x9e, 0xfe, 0x57, 0x38, 0x82, 0x9a, 0xdb, - 0x26, 0xc8, 0x1b, 0x37, 0xc9, 0x3a, 0x1b, 0x27, 0x0b, 0x20, 0x32, 0x9d, 0x65, 0x86, - 0x75, 0xfc, 0x6e, 0xa5, 0x34, 0xe0, 0x81, 0x0a, 0x44, 0x32, 0x82, 0x6b, 0xf5, 0x8c, - 0x94, 0x1e, 0xfb, 0x65, 0xd5, 0x7a, 0x33, 0x8b, 0xbd, 0x2e, 0x26, 0x64, 0x0f, 0x89, - 0xff, 0xbc, 0x1a, 0x85, 0x8e, 0xfc, 0xb8, 0x55, 0x0e, 0xe3, 0xa5, 0xe1, 0x99, 0x8b, - 0xd1, 0x77, 0xe9, 0x3a, 0x73, 0x63, 0xc3, 0x44, 0xfe, 0x6b, 0x19, 0x9e, 0xe5, 0xd0, - 0x2e, 0x82, 0xd5, 0x22, 0xc4, 0xfe, 0xba, 0x15, 0x45, 0x2f, 0x80, 0x28, 0x8a, 0x82, - 0x1a, 0x57, 0x91, 0x16, 0xec, 0x6d, 0xad, 0x2b, 0x3b, 0x31, 0x0d, 0xa9, 0x03, 0x40, - 0x1a, 0xa6, 0x21, 0x00, 0xab, 0x5d, 0x1a, 0x36, 0x55, 0x3e, 0x06, 0x20, 0x3b, 0x33, - 0x89, 0x0c, 0xc9, 0xb8, 0x32, 0xf7, 0x9e, 0xf8, 0x05, 0x60, 0xcc, 0xb9, 0xa3, 0x9c, - 0xe7, 0x67, 0x96, 0x7e, 0xd6, 0x28, 0xc6, 0xad, 0x57, 0x3c, 0xb1, 0x16, 0xdb, 0xef, - 0xef, 0xd7, 0x54, 0x99, 0xda, 0x96, 0xbd, 0x68, 0xa8, 0xa9, 0x7b, 0x92, 0x8a, 0x8b, - 0xbc, 0x10, 0x3b, 0x66, 0x21, 0xfc, 0xde, 0x2b, 0xec, 0xa1, 0x23, 0x1d, 0x20, 0x6b, - 0xe6, 0xcd, 0x9e, 0xc7, 0xaf, 0xf6, 0xf6, 0xc9, 0x4f, 0xcd, 0x72, 0x04, 0xed, 0x34, - 0x55, 0xc6, 0x8c, 0x83, 0xf4, 0xa4, 0x1d, 0xa4, 0xaf, 0x2b, 0x74, 0xef, 0x5c, 0x53, - 0xf1, 0xd8, 0xac, 0x70, 0xbd, 0xcb, 0x7e, 0xd1, 0x85, 0xce, 0x81, 0xbd, 0x84, 0x35, - 0x9d, 0x44, 0x25, 0x4d, 0x95, 0x62, 0x9e, 0x98, 0x55, 0xa9, 0x4a, 0x7c, 0x19, 0x58, - 0xd1, 0xf8, 0xad, 0xa5, 0xd0, 0x53, 0x2e, 0xd8, 0xa5, 0xaa, 0x3f, 0xb2, 0xd1, 0x7b, - 0xa7, 0x0e, 0xb6, 0x24, 0x8e, 0x59, 0x4e, 0x1a, 0x22, 0x97, 0xac, 0xbb, 0xb3, 0x9d, - 0x50, 0x2f, 0x1a, 0x8c, 0x6e, 0xb6, 0xf1, 0xce, 0x22, 0xb3, 0xde, 0x1a, 0x1f, 0x40, - 0xcc, 0x24, 0x55, 0x41, 0x19, 0xa8, 0x31, 0xa9, 0xaa, 0xd6, 0x07, 0x9c, 0xad, 0x88, - 0x42, 0x5d, 0xe6, 0xbd, 0xe1, 0xa9, 0x18, 0x7e, 0xbb, 0x60, 0x92, 0xcf, 0x67, 0xbf, - 0x2b, 0x13, 0xfd, 0x65, 0xf2, 0x70, 0x88, 0xd7, 0x8b, 0x7e, 0x88, 0x3c, 0x87, 0x59, - 0xd2, 0xc4, 0xf5, 0xc6, 0x5a, 0xdb, 0x75, 0x53, 0x87, 0x8a, 0xd5, 0x75, 0xf9, 0xfa, - 0xd8, 0x78, 0xe8, 0x0a, 0x0c, 0x9b, 0xa6, 0x3b, 0xcb, 0xcc, 0x27, 0x32, 0xe6, 0x94, - 0x85, 0xbb, 0xc9, 0xc9, 0x0b, 0xfb, 0xd6, 0x24, 0x81, 0xd9, 0x08, 0x9b, 0xec, 0xcf, - 0x80, 0xcf, 0xe2, 0xdf, 0x16, 0xa2, 0xcf, 0x65, 0xbd, 0x92, 0xdd, 0x59, 0x7b, 0x07, - 0x07, 0xe0, 0x91, 0x7a, 0xf4, 0x8b, 0xbb, 0x75, 0xfe, 0xd4, 0x13, 0xd2, 0x38, 0xf5, - 0x55, 0x5a, 0x7a, 0x56, 0x9d, 0x80, 0xc3, 0x41, 0x4a, 0x8d, 0x08, 0x59, 0xdc, 0x65, - 0xa4, 0x61, 0x28, 0xba, 0xb2, 0x7a, 0xf8, 0x7a, 0x71, 0x31, 0x4f, 0x31, 0x8c, 0x78, - 0x2b, 0x23, 0xeb, 0xfe, 0x80, 0x8b, 0x82, 0xb0, 0xce, 0x26, 0x40, 0x1d, 0x2e, 0x22, - 0xf0, 0x4d, 0x83, 0xd1, 0x25, 0x5d, 0xc5, 0x1a, 0xdd, 0xd3, 0xb7, 0x5a, 0x2b, 0x1a, - 0xe0, 0x78, 0x45, 0x04, 0xdf, 0x54, 0x3a, 0xf8, 0x96, 0x9b, 0xe3, 0xea, 0x70, 0x82, - 0xff, 0x7f, 0xc9, 0x88, 0x8c, 0x14, 0x4d, 0xa2, 0xaf, 0x58, 0x42, 0x9e, 0xc9, 0x60, - 0x31, 0xdb, 0xca, 0xd3, 0xda, 0xd9, 0xaf, 0x0d, 0xcb, 0xaa, 0xaf, 0x26, 0x8c, 0xb8, - 0xfc, 0xff, 0xea, 0xd9, 0x4f, 0x3c, 0x7c, 0xa4, 0x95, 0xe0, 0x56, 0xa9, 0xb4, 0x7a, - 0xcd, 0xb7, 0x51, 0xfb, 0x73, 0xe6, 0x66, 0xc6, 0xc6, 0x55, 0xad, 0xe8, 0x29, 0x72, - 0x97, 0xd0, 0x7a, 0xd1, 0xba, 0x5e, 0x43, 0xf1, 0xbc, 0xa3, 0x23, 0x01, 0x65, 0x13, - 0x39, 0xe2, 0x29, 0x04, 0xcc, 0x8c, 0x42, 0xf5, 0x8c, 0x30, 0xc0, 0x4a, 0xaf, 0xdb, - 0x03, 0x8d, 0xda, 0x08, 0x47, 0xdd, 0x98, 0x8d, 0xcd, 0xa6, 0xf3, 0xbf, 0xd1, 0x5c, - 0x4b, 0x4c, 0x45, 0x25, 0x00, 0x4a, 0xa0, 0x6e, 0xef, 0xf8, 0xca, 0x61, 0x78, 0x3a, - 0xac, 0xec, 0x57, 0xfb, 0x3d, 0x1f, 0x92, 0xb0, 0xfe, 0x2f, 0xd1, 0xa8, 0x5f, 0x67, - 0x24, 0x51, 0x7b, 0x65, 0xe6, 0x14, 0xad, 0x68, 0x08, 0xd6, 0xf6, 0xee, 0x34, 0xdf, - 0xf7, 0x31, 0x0f, 0xdc, 0x82, 0xae, 0xbf, 0xd9, 0x04, 0xb0, 0x1e, 0x1d, 0xc5, 0x4b, - 0x29, 0x27, 0x09, 0x4b, 0x2d, 0xb6, 0x8d, 0x6f, 0x90, 0x3b, 0x68, 0x40, 0x1a, 0xde, - 0xbf, 0x5a, 0x7e, 0x08, 0xd7, 0x8f, 0xf4, 0xef, 0x5d, 0x63, 0x65, 0x3a, 0x65, 0x04, - 0x0c, 0xf9, 0xbf, 0xd4, 0xac, 0xa7, 0x98, 0x4a, 0x74, 0xd3, 0x71, 0x45, 0x98, 0x67, - 0x80, 0xfc, 0x0b, 0x16, 0xac, 0x45, 0x16, 0x49, 0xde, 0x61, 0x88, 0xa7, 0xdb, 0xdf, - 0x19, 0x1f, 0x64, 0xb5, 0xfc, 0x5e, 0x2a, 0xb4, 0x7b, 0x57, 0xf7, 0xf7, 0x27, 0x6c, - 0xd4, 0x19, 0xc1, 0x7a, 0x3c, 0xa8, 0xe1, 0xb9, 0x39, 0xae, 0x49, 0xe4, 0x88, 0xac, - 0xba, 0x6b, 0x96, 0x56, 0x10, 0xb5, 0x48, 0x01, 0x09, 0xc8, 0xb1, 0x7b, 0x80, 0xe1, - 0xb7, 0xb7, 0x50, 0xdf, 0xc7, 0x59, 0x8d, 0x5d, 0x50, 0x11, 0xfd, 0x2d, 0xcc, 0x56, - 0x00, 0xa3, 0x2e, 0xf5, 0xb5, 0x2a, 0x1e, 0xcc, 0x82, 0x0e, 0x30, 0x8a, 0xa3, 0x42, - 0x72, 0x1a, 0xac, 0x09, 0x43, 0xbf, 0x66, 0x86, 0xb6, 0x4b, 0x25, 0x79, 0x37, 0x65, - 0x04, 0xcc, 0xc4, 0x93, 0xd9, 0x7e, 0x6a, 0xed, 0x3f, 0xb0, 0xf9, 0xcd, 0x71, 0xa4, - 0x3d, 0xd4, 0x97, 0xf0, 0x1f, 0x17, 0xc0, 0xe2, 0xcb, 0x37, 0x97, 0xaa, 0x2a, 0x2f, - 0x25, 0x66, 0x56, 0x16, 0x8e, 0x6c, 0x49, 0x6a, 0xfc, 0x5f, 0xb9, 0x32, 0x46, 0xf6, - 0xb1, 0x11, 0x63, 0x98, 0xa3, 0x46, 0xf1, 0xa6, 0x41, 0xf3, 0xb0, 0x41, 0xe9, 0x89, - 0xf7, 0x91, 0x4f, 0x90, 0xcc, 0x2c, 0x7f, 0xff, 0x35, 0x78, 0x76, 0xe5, 0x06, 0xb5, - 0x0d, 0x33, 0x4b, 0xa7, 0x7c, 0x22, 0x5b, 0xc3, 0x07, 0xba, 0x53, 0x71, 0x52, 0xf3, - 0xf1, 0x61, 0x0e, 0x4e, 0xaf, 0xe5, 0x95, 0xf6, 0xd9, 0xd9, 0x0d, 0x11, 0xfa, 0xa9, - 0x33, 0xa1, 0x5e, 0xf1, 0x36, 0x95, 0x46, 0x86, 0x8a, 0x7f, 0x3a, 0x45, 0xa9, 0x67, - 0x68, 0xd4, 0x0f, 0xd9, 0xd0, 0x34, 0x12, 0xc0, 0x91, 0xc6, 0x31, 0x5c, 0xf4, 0xfd, - 0xe7, 0xcb, 0x68, 0x60, 0x69, 0x37, 0x38, 0x0d, 0xb2, 0xea, 0xaa, 0x70, 0x7b, 0x4c, - 0x41, 0x85, 0xc3, 0x2e, 0xdd, 0xcd, 0xd3, 0x06, 0x70, 0x5e, 0x4d, 0xc1, 0xff, 0xc8, - 0x72, 0xee, 0xee, 0x47, 0x5a, 0x64, 0xdf, 0xac, 0x86, 0xab, 0xa4, 0x1c, 0x06, 0x18, - 0x98, 0x3f, 0x87, 0x41, 0xc5, 0xef, 0x68, 0xd3, 0xa1, 0x01, 0xe8, 0xa3, 0xb8, 0xca, - 0xc6, 0x0c, 0x90, 0x5c, 0x15, 0xfc, 0x91, 0x08, 0x40, 0xb9, 0x4c, 0x00, 0xa0, 0xb9, - 0xd0, - ], - &[ - 0x0a, 0xab, 0x4c, 0x90, 0x05, 0x01, 0xb3, 0xe2, 0x4d, 0x7c, 0xdf, 0x46, 0x63, 0x32, - 0x6a, 0x3a, 0x87, 0xdf, 0x5e, 0x48, 0x43, 0xb2, 0xcb, 0xdb, 0x67, 0xcb, 0xf6, 0xe4, - 0x60, 0xfe, 0xc3, 0x50, 0xaa, 0x53, 0x71, 0xb1, 0x50, 0x8f, 0x9f, 0x45, 0x28, 0xec, - 0xea, 0x23, 0xc4, 0x36, 0xd9, 0x4b, 0x5e, 0x8f, 0xcd, 0x4f, 0x68, 0x1e, 0x30, 0xa6, - 0xac, 0x00, 0xa9, 0x70, 0x4a, 0x18, 0x8a, 0x03, - ], - ], - ]; - - #[cfg(feature = "rust-crypto")] - #[test] - fn ed25519_test_vectors() { - use crate::crypto::default::signature::Verifier; - use crate::crypto::signature::Verifier as _; - use crate::Signature; - - for (i, v) in ED25519_TEST_VECTORS.iter().enumerate() { - let public_key = v[0]; - let msg = v[1]; - let sig = v[2]; - - let public_key = PublicKey::from_raw_ed25519(public_key).unwrap(); - match public_key { - PublicKey::Ed25519(_) => {}, - #[cfg(feature = "secp256k1")] - _ => panic!("expected public key to be Ed25519: {:?}", public_key), - } - let sig = Signature::try_from(sig).unwrap(); - Verifier::verify(public_key, msg, &sig) - .unwrap_or_else(|_| panic!("signature should be valid for test vector {}", i)); - } - } - - // Arbitrary "valid" tests taken from - // https://github.com/google/wycheproof/blob/2196000605e45d91097147c9c71f26b72af58003/testvectors/ecdsa_secp256k1_sha256_test.json - // - // Each test vector consists of: [public_key, message, signature]. - // - // NB: It appears as though all signatures in this test suite are - // DER-encoded. - #[cfg(feature = "secp256k1")] - const SECP256K1_TEST_VECTORS: &[&[&[u8]]] = &[ - // tcId 3 - &[ - &[ - 0x04, 0xb8, 0x38, 0xff, 0x44, 0xe5, 0xbc, 0x17, 0x7b, 0xf2, 0x11, 0x89, 0xd0, 0x76, - 0x60, 0x82, 0xfc, 0x9d, 0x84, 0x32, 0x26, 0x88, 0x7f, 0xc9, 0x76, 0x03, 0x71, 0x10, - 0x0b, 0x7e, 0xe2, 0x0a, 0x6f, 0xf0, 0xc9, 0xd7, 0x5b, 0xfb, 0xa7, 0xb3, 0x1a, 0x6b, - 0xca, 0x19, 0x74, 0x49, 0x6e, 0xeb, 0x56, 0xde, 0x35, 0x70, 0x71, 0x95, 0x5d, 0x83, - 0xc4, 0xb1, 0xba, 0xda, 0xa0, 0xb2, 0x18, 0x32, 0xe9, - ], - &[0x31, 0x32, 0x33, 0x34, 0x30, 0x30], - &[ - 0x30, 0x45, 0x02, 0x21, 0x00, 0x81, 0x3e, 0xf7, 0x9c, 0xce, 0xfa, 0x9a, 0x56, 0xf7, - 0xba, 0x80, 0x5f, 0x0e, 0x47, 0x85, 0x84, 0xfe, 0x5f, 0x0d, 0xd5, 0xf5, 0x67, 0xbc, - 0x09, 0xb5, 0x12, 0x3c, 0xcb, 0xc9, 0x83, 0x23, 0x65, 0x02, 0x20, 0x6f, 0xf1, 0x8a, - 0x52, 0xdc, 0xc0, 0x33, 0x6f, 0x7a, 0xf6, 0x24, 0x00, 0xa6, 0xdd, 0x9b, 0x81, 0x07, - 0x32, 0xba, 0xf1, 0xff, 0x75, 0x80, 0x00, 0xd6, 0xf6, 0x13, 0xa5, 0x56, 0xeb, 0x31, - 0xba, - ], - ], - // tcId 230 - &[ - &[ - 0x04, 0xb8, 0x38, 0xff, 0x44, 0xe5, 0xbc, 0x17, 0x7b, 0xf2, 0x11, 0x89, 0xd0, 0x76, - 0x60, 0x82, 0xfc, 0x9d, 0x84, 0x32, 0x26, 0x88, 0x7f, 0xc9, 0x76, 0x03, 0x71, 0x10, - 0x0b, 0x7e, 0xe2, 0x0a, 0x6f, 0xf0, 0xc9, 0xd7, 0x5b, 0xfb, 0xa7, 0xb3, 0x1a, 0x6b, - 0xca, 0x19, 0x74, 0x49, 0x6e, 0xeb, 0x56, 0xde, 0x35, 0x70, 0x71, 0x95, 0x5d, 0x83, - 0xc4, 0xb1, 0xba, 0xda, 0xa0, 0xb2, 0x18, 0x32, 0xe9, - ], - &[0x32, 0x35, 0x35, 0x38, 0x35], - &[ - 0x30, 0x45, 0x02, 0x21, 0x00, 0xdd, 0x1b, 0x7d, 0x09, 0xa7, 0xbd, 0x82, 0x18, 0x96, - 0x10, 0x34, 0xa3, 0x9a, 0x87, 0xfe, 0xcf, 0x53, 0x14, 0xf0, 0x0c, 0x4d, 0x25, 0xeb, - 0x58, 0xa0, 0x7a, 0xc8, 0x5e, 0x85, 0xea, 0xb5, 0x16, 0x02, 0x20, 0x35, 0x13, 0x8c, - 0x40, 0x1e, 0xf8, 0xd3, 0x49, 0x3d, 0x65, 0xc9, 0x00, 0x2f, 0xe6, 0x2b, 0x43, 0xae, - 0xe5, 0x68, 0x73, 0x1b, 0x74, 0x45, 0x48, 0x35, 0x89, 0x96, 0xd9, 0xcc, 0x42, 0x7e, - 0x06, - ], - ], - // tcId 231 - &[ - &[ - 0x04, 0xb8, 0x38, 0xff, 0x44, 0xe5, 0xbc, 0x17, 0x7b, 0xf2, 0x11, 0x89, 0xd0, 0x76, - 0x60, 0x82, 0xfc, 0x9d, 0x84, 0x32, 0x26, 0x88, 0x7f, 0xc9, 0x76, 0x03, 0x71, 0x10, - 0x0b, 0x7e, 0xe2, 0x0a, 0x6f, 0xf0, 0xc9, 0xd7, 0x5b, 0xfb, 0xa7, 0xb3, 0x1a, 0x6b, - 0xca, 0x19, 0x74, 0x49, 0x6e, 0xeb, 0x56, 0xde, 0x35, 0x70, 0x71, 0x95, 0x5d, 0x83, - 0xc4, 0xb1, 0xba, 0xda, 0xa0, 0xb2, 0x18, 0x32, 0xe9, - ], - &[0x34, 0x32, 0x36, 0x34, 0x37, 0x39, 0x37, 0x32, 0x34], - &[ - 0x30, 0x45, 0x02, 0x21, 0x00, 0x95, 0xc2, 0x92, 0x67, 0xd9, 0x72, 0xa0, 0x43, 0xd9, - 0x55, 0x22, 0x45, 0x46, 0x22, 0x2b, 0xba, 0x34, 0x3f, 0xc1, 0xd4, 0xdb, 0x0f, 0xec, - 0x26, 0x2a, 0x33, 0xac, 0x61, 0x30, 0x56, 0x96, 0xae, 0x02, 0x20, 0x6e, 0xdf, 0xe9, - 0x67, 0x13, 0xae, 0xd5, 0x6f, 0x8a, 0x28, 0xa6, 0x65, 0x3f, 0x57, 0xe0, 0xb8, 0x29, - 0x71, 0x2e, 0x5e, 0xdd, 0xc6, 0x7f, 0x34, 0x68, 0x2b, 0x24, 0xf0, 0x67, 0x6b, 0x26, - 0x40, - ], - ], - ]; - - #[cfg(feature = "secp256k1")] - #[test] - fn secp256k1_test_vectors() { - for (i, v) in SECP256K1_TEST_VECTORS.iter().enumerate() { - let public_key = v[0]; - let msg = v[1]; - let sig = v[2]; - - let public_key = PublicKey::from_raw_secp256k1(public_key).unwrap(); - match public_key { - PublicKey::Secp256k1(_) => {}, - _ => panic!("expected public key to be secp256k1: {:?}", public_key), - } - let der_sig = k256::ecdsa::Signature::from_der(sig).unwrap(); - let sig = der_sig.as_ref(); - let sig = Signature::try_from(sig).unwrap(); - public_key - .verify(msg, &sig) - .unwrap_or_else(|_| panic!("signature should be valid for test vector {}", i)); - } - } } From 3d7ee0a89d85af8f764801f035f4799b7d021882 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Tue, 24 Jan 2023 14:17:40 +0200 Subject: [PATCH 42/45] Guard public key extraction with rust-crypto --- tendermint/src/crypto/ed25519/signing_key.rs | 2 ++ .../src/crypto/ed25519/verification_key.rs | 1 + tendermint/src/private_key.rs | 23 ++++++++++++++----- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/tendermint/src/crypto/ed25519/signing_key.rs b/tendermint/src/crypto/ed25519/signing_key.rs index 6351a2093..6abcc9492 100644 --- a/tendermint/src/crypto/ed25519/signing_key.rs +++ b/tendermint/src/crypto/ed25519/signing_key.rs @@ -1,4 +1,6 @@ +#[cfg(feature = "rust-crypto")] use super::VerificationKey; + use crate::Error; #[derive(Clone, Debug)] diff --git a/tendermint/src/crypto/ed25519/verification_key.rs b/tendermint/src/crypto/ed25519/verification_key.rs index 562998df9..52cc49dd2 100644 --- a/tendermint/src/crypto/ed25519/verification_key.rs +++ b/tendermint/src/crypto/ed25519/verification_key.rs @@ -4,6 +4,7 @@ use crate::Error; pub struct VerificationKey([u8; 32]); impl VerificationKey { + #[allow(dead_code)] pub(super) fn new(bytes: [u8; 32]) -> Self { Self(bytes) } diff --git a/tendermint/src/private_key.rs b/tendermint/src/private_key.rs index a1c89d6fe..f5b06a988 100644 --- a/tendermint/src/private_key.rs +++ b/tendermint/src/private_key.rs @@ -2,30 +2,39 @@ pub use crate::crypto::ed25519::SigningKey as Ed25519; use crate::prelude::*; + +#[cfg(feature = "rust-crypto")] use crate::public_key::PublicKey; +#[cfg(feature = "rust-crypto")] use serde::{de, ser, Deserialize, Serialize}; +#[cfg(feature = "rust-crypto")] use subtle_encoding::{Base64, Encoding}; +#[cfg(feature = "rust-crypto")] use zeroize::Zeroizing; pub const ED25519_KEYPAIR_SIZE: usize = 64; /// Private keys as parsed from configuration files -#[derive(Serialize, Deserialize)] +#[cfg_attr(feature = "rust-crypto", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "rust-crypto", serde(tag = "type", content = "value"))] // JSON custom serialization for priv_validator_key.json #[non_exhaustive] -#[serde(tag = "type", content = "value")] // JSON custom serialization for priv_validator_key.json pub enum PrivateKey { /// Ed25519 keys - #[serde( - rename = "tendermint/PrivKeyEd25519", - serialize_with = "serialize_ed25519_keypair", - deserialize_with = "deserialize_ed25519_keypair" + #[cfg_attr( + feature = "rust-crypto", + serde( + rename = "tendermint/PrivKeyEd25519", + serialize_with = "serialize_ed25519_keypair", + deserialize_with = "deserialize_ed25519_keypair" + ) )] Ed25519(Ed25519), } impl PrivateKey { /// Get the public key associated with this private key + #[cfg(feature = "rust-crypto")] pub fn public_key(&self) -> PublicKey { match self { PrivateKey::Ed25519(signing_key) => PublicKey::Ed25519(signing_key.verification_key()), @@ -41,6 +50,7 @@ impl PrivateKey { } /// Serialize an Ed25519 keypair as Base64 +#[cfg(feature = "rust-crypto")] fn serialize_ed25519_keypair(signing_key: &Ed25519, serializer: S) -> Result where S: ser::Serializer, @@ -55,6 +65,7 @@ where } /// Deserialize an Ed25519 keypair from Base64 +#[cfg(feature = "rust-crypto")] fn deserialize_ed25519_keypair<'de, D>(deserializer: D) -> Result where D: de::Deserializer<'de>, From 3acfa239fcb7bb69cff19317d20ed183ca7095ac Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Tue, 24 Jan 2023 20:39:12 +0200 Subject: [PATCH 43/45] tendermint: remove re-exports from signature crate As we don't currently use the signature framework, it makes no sense to provide its traits through the crate. --- tendermint/src/signature.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/tendermint/src/signature.rs b/tendermint/src/signature.rs index 4f6475c2f..d31901509 100644 --- a/tendermint/src/signature.rs +++ b/tendermint/src/signature.rs @@ -5,7 +5,6 @@ use core::convert::TryFrom; pub use ed25519::Signature as Ed25519Signature; #[cfg(feature = "secp256k1")] pub use k256::ecdsa::Signature as Secp256k1Signature; -pub use signature::{Signer, Verifier}; use tendermint_proto::Protobuf; use crate::{error::Error, prelude::*}; From 2b49f633db75fa3e65a80a4c6c4286dbd593fa85 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Tue, 24 Jan 2023 20:40:55 +0200 Subject: [PATCH 44/45] tendermint: rework signature in alt_crypto test Implement the new Verifier trait instead of the signature traits. --- tendermint/tests/alt_crypto.rs | 109 ++++++++++----------------------- 1 file changed, 33 insertions(+), 76 deletions(-) diff --git a/tendermint/tests/alt_crypto.rs b/tendermint/tests/alt_crypto.rs index 462525e60..24d5c2742 100644 --- a/tendermint/tests/alt_crypto.rs +++ b/tendermint/tests/alt_crypto.rs @@ -1,70 +1,37 @@ //! An imitation of alternative cryptographic function implementations //! for a chain environment that provides its own cryptographic API. +#![cfg(all(feature = "secp256k1", feature = "rust-crypto"))] -use tendermint::crypto::{sha256::HASH_SIZE, Sha256}; - +use ::signature::{DigestVerifier, Signature as _}; use digest::Digest; -use signature::{self, DigestSigner, DigestVerifier, Signature, Signer, Verifier}; -use k256::ecdsa::{SigningKey, VerifyingKey}; +use tendermint::crypto::signature::{self, Verifier}; +use tendermint::crypto::{sha256::HASH_SIZE, Sha256}; +use tendermint::{PublicKey, Signature}; #[derive(Debug, Default)] struct SubstrateSha256(sha2::Sha256); -#[derive(Debug)] -struct SubstrateSignature(k256::ecdsa::Signature); - -impl AsRef<[u8]> for SubstrateSignature { - fn as_ref(&self) -> &[u8] { - self.0.as_bytes() - } -} - -impl Signature for SubstrateSignature { - fn from_bytes(bytes: &[u8]) -> Result { - let inner = k256::ecdsa::Signature::from_bytes(bytes)?; - Ok(Self(inner)) - } -} - -struct SubstrateSigner { - inner: SigningKey, -} - -impl SubstrateSigner { - fn from_bytes(private_key: &[u8]) -> Result { - let inner = SigningKey::from_bytes(private_key)?; - Ok(Self { inner }) - } -} - -impl Signer for SubstrateSigner { - fn try_sign(&self, msg: &[u8]) -> Result { - let mut hasher = sha2::Sha256::new(); - hasher.update(msg); - let signature = self.inner.try_sign_digest(hasher)?; - Ok(SubstrateSignature(signature)) - } -} - -#[derive(Debug)] -struct SubstrateSignatureVerifier { - inner: VerifyingKey, -} - -impl SubstrateSignatureVerifier { - fn from_bytes(public_key: &[u8]) -> Result { - Ok(Self { - inner: VerifyingKey::from_sec1_bytes(public_key)?, - }) - } -} - -impl Verifier for SubstrateSignatureVerifier { - fn verify(&self, msg: &[u8], signature: &SubstrateSignature) -> Result<(), signature::Error> { - let mut hasher = sha2::Sha256::new(); - Digest::update(&mut hasher, msg); - self.inner.verify_digest(hasher, &signature.0) +#[derive(Debug, Default)] +struct SubstrateSignatureVerifier; + +impl Verifier for SubstrateSignatureVerifier { + fn verify( + pubkey: PublicKey, + msg: &[u8], + signature: &Signature, + ) -> Result<(), signature::Error> { + match pubkey { + PublicKey::Secp256k1(pk) => { + let sig = k256::ecdsa::Signature::from_bytes(signature.as_bytes()) + .map_err(|_| signature::Error::MalformedSignature)?; + let mut hasher = sha2::Sha256::new(); + Digest::update(&mut hasher, msg); + pk.verify_digest(hasher, &sig) + .map_err(|_| signature::Error::VerificationFailed) + }, + _ => Err(signature::Error::UnsupportedKeyType), + } } } @@ -75,9 +42,10 @@ impl Sha256 for SubstrateSha256 { } mod tests { - use super::{SubstrateSha256, SubstrateSignatureVerifier, SubstrateSigner}; - use signature::{Signature, Signer, Verifier}; + use super::{SubstrateSha256, SubstrateSignatureVerifier}; + use tendermint::crypto::signature::Verifier; use tendermint::crypto::Sha256; + use tendermint::{PublicKey, Signature}; use subtle_encoding::hex; @@ -92,30 +60,19 @@ mod tests { ); } - const SIGNING_KEY: &[u8] = b"59820654790d53a23d1017b50ddcdb31242e27c682a0a1372fc63c01dd48816a"; + //const SIGNING_KEY: &[u8] = b"59820654790d53a23d1017b50ddcdb31242e27c682a0a1372fc63c01dd48816a"; const VERIFYING_KEY: &[u8] = b"03cf7a110053a95b4b25266c3416ae342eba2ca3f4658fa1069fcf750f760b8c42"; const MESSAGE: &[u8] = b"hello world"; const SIGNATURE: &str = "684c3c183f76a79fc116dd4edd39fe40737cea51c6c1df47ff544c20d14a7a76754c43c51e0daa647e8e4164f254bb62dbf9bd5b2e2e03ffb8247dd92ce1e1e3"; - #[test] - fn signer_can_sign() { - let key_bytes = hex::decode(SIGNING_KEY).unwrap(); - - let signer = SubstrateSigner::from_bytes(&key_bytes).unwrap(); - let signature = signer.sign(MESSAGE); - - let sig_hex = String::from_utf8(hex::encode(signature.as_bytes())).unwrap(); - assert_eq!(sig_hex, SIGNATURE); - } - #[test] fn verifier_can_verify() { let key_bytes = hex::decode(VERIFYING_KEY).unwrap(); - let signature = hex::decode(SIGNATURE.as_bytes()).unwrap(); - let signature = Signature::from_bytes(&signature).unwrap(); + let public_key = PublicKey::from_raw_secp256k1(&key_bytes).unwrap(); + let sig_bytes = hex::decode(SIGNATURE.as_bytes()).unwrap(); + let signature = Signature::try_from(&sig_bytes[..]).unwrap(); - let verifier = SubstrateSignatureVerifier::from_bytes(&key_bytes).unwrap(); - verifier.verify(MESSAGE, &signature).unwrap(); + SubstrateSignatureVerifier::verify(public_key, MESSAGE, &signature).unwrap(); } } From 37418840b108373967a7bc042c3c10605b3adafc Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Wed, 25 Jan 2023 13:25:17 +0200 Subject: [PATCH 45/45] Updated the changelog entry for #1238 --- .../breaking-changes/1238-crypto-provider-traits.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.changelog/unreleased/breaking-changes/1238-crypto-provider-traits.md b/.changelog/unreleased/breaking-changes/1238-crypto-provider-traits.md index 339036073..57165c4d5 100644 --- a/.changelog/unreleased/breaking-changes/1238-crypto-provider-traits.md +++ b/.changelog/unreleased/breaking-changes/1238-crypto-provider-traits.md @@ -1,10 +1,13 @@ - `[tendermint]` Make implementations of cryptographic primitives replaceable ([#1238](https://github.com/informalsystems/tendermint-rs/pull/1238)). * Provide a `Sha256` trait in module `crypto` and make digest hashing - implementations available through it. + implementations available through it. + * Provide a `Verifier` trait in module `crypto::signature` to enable + alternative implementations of signature verification available through it. + An `Error` enum is defined in the same module, representing the error cases + that can arise in the implementation in a deliberately opaque way. * The module `crypto::default` provides pure Rust implementations of the - cryptographic traits, mostly conformant to the framework given in - the `digest` and `signature` crates. The module is made available by a + cryptographic traits. The module is made available by a new `rust-crypto` feature, enabled by default. * `merkle::simple_hash_from_byte_vectors` is made generic over an implementation of the new `MerkleHash` trait. Implementations for @@ -22,7 +25,8 @@ - `[tendermint-light-client-verifier]` Changes for the new Tendermint crypto API ([#1238](https://github.com/informalsystems/tendermint-rs/pull/1238)). * The `rust-crypto` feature, enabled by default, guards the - batteries-included implementation types: `ProdVerifier`, `ProdPredicates`. + batteries-included implementation types: `ProdVerifier`, `ProdPredicates`, + `ProdVotingPowerCalculator`. * Remove the `operations::hasher` API (`Hasher` and `ProdHasher`), made unnecessary by the new crypto abstractions in the `tendermint` crate. * The `VerificationPredicates` trait features a `Sha256` associated type