From 6cc800c07ad2be6c202bb5efbf92b7c05abad666 Mon Sep 17 00:00:00 2001 From: Jake Hemmerle Date: Sat, 8 May 2021 17:54:06 -0400 Subject: [PATCH 1/5] swap ed25519-dalek for ed25519-zebra; no batch verificaiton --- Cargo.lock | 16 ++++++++++- Cargo.toml | 2 +- primitives/core/Cargo.toml | 6 ++-- primitives/core/src/ed25519.rs | 51 +++++++++++++++++++--------------- 4 files changed, 47 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b7e22d9d2cf33..efc5d392b3187 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1524,6 +1524,19 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ed25519-zebra" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a128b76af6dd4b427e34a6fd43dc78dbfe73672ec41ff615a2414c1a0ad0409" +dependencies = [ + "curve25519-dalek 3.0.2", + "hex", + "rand_core 0.5.1", + "sha2 0.9.3", + "thiserror", +] + [[package]] name = "either" version = "1.6.1" @@ -8944,8 +8957,9 @@ dependencies = [ "byteorder", "criterion", "dyn-clonable", - "ed25519-dalek", + "ed25519-zebra", "futures 0.3.15", + "ed25519-zebra", "hash-db", "hash256-std-hasher", "hex", diff --git a/Cargo.toml b/Cargo.toml index 03115fe5593f1..828ffb8331708 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -235,7 +235,7 @@ crossbeam-deque = { opt-level = 3 } crossbeam-queue = { opt-level = 3 } crypto-mac = { opt-level = 3 } curve25519-dalek = { opt-level = 3 } -ed25519-dalek = { opt-level = 3 } +ed25519-zebra = { opt-level = 3 } flate2 = { opt-level = 3 } futures-channel = { opt-level = 3 } hashbrown = { opt-level = 3 } diff --git a/primitives/core/Cargo.toml b/primitives/core/Cargo.toml index 711fcc37e8556..eb2e28831761e 100644 --- a/primitives/core/Cargo.toml +++ b/primitives/core/Cargo.toml @@ -42,7 +42,7 @@ dyn-clonable = { version = "0.9.0", optional = true } thiserror = { version = "1.0.21", optional = true } # full crypto -ed25519-dalek = { version = "1.0.1", default-features = false, features = ["u64_backend", "alloc"], optional = true } +ed25519-zebra = { version = "2.2.0", default-features = false, optional = true} blake2-rfc = { version = "0.2.18", default-features = false, optional = true } tiny-keccak = { version = "2.0.1", features = ["keccak"], optional = true } schnorrkel = { version = "0.9.1", features = ["preaudit_deprecated", "u64_backend"], default-features = false, optional = true } @@ -91,7 +91,7 @@ std = [ "serde", "twox-hash/std", "blake2-rfc/std", - "ed25519-dalek/std", + "ed25519-zebra", "hex/std", "base58", "substrate-bip39", @@ -119,7 +119,7 @@ std = [ # or Intel SGX. # For the regular wasm runtime builds this should not be used. full_crypto = [ - "ed25519-dalek", + "ed25519-zebra", "blake2-rfc", "tiny-keccak", "schnorrkel", diff --git a/primitives/core/src/ed25519.rs b/primitives/core/src/ed25519.rs index be70da31e641d..aa4edcda1cf7a 100644 --- a/primitives/core/src/ed25519.rs +++ b/primitives/core/src/ed25519.rs @@ -37,7 +37,7 @@ use bip39::{Language, Mnemonic, MnemonicType}; #[cfg(feature = "full_crypto")] use core::convert::TryFrom; #[cfg(feature = "full_crypto")] -use ed25519_dalek::{Signer as _, Verifier as _}; +use ed25519_zebra::{SigningKey, VerificationKey}; #[cfg(feature = "std")] use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use sp_runtime_interface::pass_by::PassByInner; @@ -63,16 +63,18 @@ pub struct Public(pub [u8; 32]); /// A key pair. #[cfg(feature = "full_crypto")] -pub struct Pair(ed25519_dalek::Keypair); +pub struct Pair { + public: VerificationKey, + secret: SigningKey, +} #[cfg(feature = "full_crypto")] impl Clone for Pair { fn clone(&self) -> Self { - Pair(ed25519_dalek::Keypair { - public: self.0.public, - secret: ed25519_dalek::SecretKey::from_bytes(self.0.secret.as_bytes()) - .expect("key is always the correct size; qed"), - }) + Pair { + public: self.public.clone(), + secret: self.secret.clone(), + } } } @@ -484,10 +486,11 @@ impl TraitPair for Pair { /// /// You should never need to use this; generate(), generate_with_phrase fn from_seed_slice(seed_slice: &[u8]) -> Result { - let secret = ed25519_dalek::SecretKey::from_bytes(seed_slice) + // does try_into consume the seed? can I consume seed_slice here? I think not right? + let secret = SigningKey::try_from(seed_slice) .map_err(|_| SecretStringError::InvalidSeedLength)?; - let public = ed25519_dalek::PublicKey::from(&secret); - Ok(Pair(ed25519_dalek::Keypair { secret, public })) + let public = VerificationKey::from(&secret); + Ok(Pair {secret, public}) } /// Derive a child key from a series of given junctions. @@ -496,7 +499,7 @@ impl TraitPair for Pair { path: Iter, _seed: Option, ) -> Result<(Pair, Option), DeriveError> { - let mut acc = self.0.secret.to_bytes(); + let mut acc: [u8; 32] = self.secret.into(); for j in path { match j { DeriveJunction::Soft(_cc) => return Err(DeriveError::SoftKeyInPath), @@ -508,15 +511,14 @@ impl TraitPair for Pair { /// Get the public key. fn public(&self) -> Public { - let mut r = [0u8; 32]; - let pk = self.0.public.as_bytes(); - r.copy_from_slice(pk); - Public(r) + // does this consume public? Is that why we used copy_from_slice? + let pk: [u8; 32] = self.public.into(); + Public(pk) } /// Sign a message. fn sign(&self, message: &[u8]) -> Signature { - let r = self.0.sign(message).to_bytes(); + let r: [u8; 64] = self.secret.sign(message).into(); Signature::from_raw(r) } @@ -530,17 +532,20 @@ impl TraitPair for Pair { /// This doesn't use the type system to ensure that `sig` and `pubkey` are the correct /// size. Use it only if you're coming from byte buffers and need the speed. fn verify_weak, M: AsRef<[u8]>>(sig: &[u8], message: M, pubkey: P) -> bool { - let public_key = match ed25519_dalek::PublicKey::from_bytes(pubkey.as_ref()) { + let public_key = match VerificationKey::try_from(pubkey.as_ref()) { Ok(pk) => pk, Err(_) => return false, }; - let sig = match ed25519_dalek::Signature::try_from(sig) { + let sig = match ed25519_zebra::Signature::try_from(sig) { Ok(s) => s, Err(_) => return false, }; - public_key.verify(message.as_ref(), &sig).is_ok() + match public_key.verify(&sig, message.as_ref()) { + Ok(_) => true, + _ => false, + } } /// Return a vec filled with raw data. @@ -552,8 +557,8 @@ impl TraitPair for Pair { #[cfg(feature = "full_crypto")] impl Pair { /// Get the seed for this key. - pub fn seed(&self) -> &Seed { - self.0.secret.as_bytes() + pub fn seed(&self) -> Seed { + self.secret.into() } /// Exactly as `from_string` except that if no matches are found then, the the first 32 @@ -605,12 +610,12 @@ mod test { fn seed_and_derive_should_work() { let seed = hex!("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60"); let pair = Pair::from_seed(&seed); - assert_eq!(pair.seed(), &seed); + assert_eq!(pair.seed(), seed); let path = vec![DeriveJunction::Hard([0u8; 32])]; let derived = pair.derive(path.into_iter(), None).ok().unwrap().0; assert_eq!( derived.seed(), - &hex!("ede3354e133f9c8e337ddd6ee5415ed4b4ffe5fc7d21e933f4930a3730e5b21c") + hex!("ede3354e133f9c8e337ddd6ee5415ed4b4ffe5fc7d21e933f4930a3730e5b21c") ); } From f69baaf7be7d675854775a2891958cc5991e29fe Mon Sep 17 00:00:00 2001 From: Jake Hemmerle Date: Sun, 23 May 2021 15:05:29 -0400 Subject: [PATCH 2/5] fixed batch verificaiton tests removed additional zero verificaiton tests --- Cargo.lock | 1 - primitives/io/src/lib.rs | 30 +++++++++++++++--------------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index efc5d392b3187..8113520a86115 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8959,7 +8959,6 @@ dependencies = [ "dyn-clonable", "ed25519-zebra", "futures 0.3.15", - "ed25519-zebra", "hash-db", "hash256-std-hasher", "hex", diff --git a/primitives/io/src/lib.rs b/primitives/io/src/lib.rs index 8ecbd1722017c..46eb82e33c391 100644 --- a/primitives/io/src/lib.rs +++ b/primitives/io/src/lib.rs @@ -1560,6 +1560,7 @@ mod tests { ext.register_extension(TaskExecutorExt::new(TaskExecutor::new())); ext.execute_with(|| { let pair = sr25519::Pair::generate_with_phrase(None).0; + let pair_unused = sr25519::Pair::generate_with_phrase(None).0; crypto::start_batch_verify(); for it in 0..70 { let msg = format!("Schnorrkel {}!", it); @@ -1567,8 +1568,10 @@ mod tests { crypto::sr25519_batch_verify(&signature, msg.as_bytes(), &pair.public()); } - // push invlaid - crypto::sr25519_batch_verify(&Default::default(), &Vec::new(), &Default::default()); + // push invalid + let msg = format!("asdf!"); + let signature = pair.sign(msg.as_bytes()); + crypto::sr25519_batch_verify(&signature, msg.as_bytes(), &pair_unused.public()); assert!(!crypto::finish_batch_verify()); crypto::start_batch_verify(); @@ -1586,11 +1589,6 @@ mod tests { let mut ext = BasicExternalities::default(); ext.register_extension(TaskExecutorExt::new(TaskExecutor::new())); ext.execute_with(|| { - // invalid ed25519 signature - crypto::start_batch_verify(); - crypto::ed25519_batch_verify(&Default::default(), &Vec::new(), &Default::default()); - assert!(!crypto::finish_batch_verify()); - // 2 valid ed25519 signatures crypto::start_batch_verify(); @@ -1609,12 +1607,13 @@ mod tests { // 1 valid, 1 invalid ed25519 signature crypto::start_batch_verify(); - let pair = ed25519::Pair::generate_with_phrase(None).0; + let pair1 = ed25519::Pair::generate_with_phrase(None).0; + let pair2 = ed25519::Pair::generate_with_phrase(None).0; let msg = b"Important message"; - let signature = pair.sign(msg); - crypto::ed25519_batch_verify(&signature, msg, &pair.public()); + let signature = pair1.sign(msg); - crypto::ed25519_batch_verify(&Default::default(), &Vec::new(), &Default::default()); + crypto::ed25519_batch_verify(&signature, msg, &pair1.public()); + crypto::ed25519_batch_verify(&signature, msg, &pair2.public()); assert!(!crypto::finish_batch_verify()); @@ -1641,12 +1640,13 @@ mod tests { // 1 valid sr25519, 1 invalid sr25519 crypto::start_batch_verify(); - let pair = sr25519::Pair::generate_with_phrase(None).0; + let pair1 = sr25519::Pair::generate_with_phrase(None).0; + let pair2 = sr25519::Pair::generate_with_phrase(None).0; let msg = b"Schnorrkcel!"; let signature = pair.sign(msg); - crypto::sr25519_batch_verify(&signature, msg, &pair.public()); - - crypto::sr25519_batch_verify(&Default::default(), &Vec::new(), &Default::default()); + + crypto::sr25519_batch_verify(&signature, msg, &pair1.public()); + crypto::sr25519_batch_verify(&signature, msg, &pair2.public()); assert!(!crypto::finish_batch_verify()); }); From 8abe5d29a2d3303e6d0b28d29ba70fdb36347bc2 Mon Sep 17 00:00:00 2001 From: Giles Cope Date: Fri, 3 Sep 2021 07:33:13 +0100 Subject: [PATCH 3/5] Fixing Cargo.lock --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 28679ea018804..a0577dedf67d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8978,7 +8978,7 @@ dependencies = [ "sp-core", "sp-runtime", "sp-std", -6 +] [[package]] name = "sp-core" From 4970c66e0ccbee8862c4632b7ed7fb7ab3f0c539 Mon Sep 17 00:00:00 2001 From: Jake Hemmerle Date: Tue, 7 Sep 2021 18:13:22 -0700 Subject: [PATCH 4/5] Update primitives/core/src/ed25519.rs Co-authored-by: Squirrel --- primitives/core/src/ed25519.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/primitives/core/src/ed25519.rs b/primitives/core/src/ed25519.rs index aa4edcda1cf7a..37409fbd00edd 100644 --- a/primitives/core/src/ed25519.rs +++ b/primitives/core/src/ed25519.rs @@ -542,10 +542,7 @@ impl TraitPair for Pair { Err(_) => return false, }; - match public_key.verify(&sig, message.as_ref()) { - Ok(_) => true, - _ => false, - } + public_key.verify(&sig, message.as_ref()).is_ok() } /// Return a vec filled with raw data. From f75dd2692d16109e8bf53a33e51f5c8043c17ae3 Mon Sep 17 00:00:00 2001 From: Jake Hemmerle Date: Wed, 8 Sep 2021 14:19:22 -0400 Subject: [PATCH 5/5] removed comments, fixed test bug, added #[derive(Clone)] --- primitives/core/src/ed25519.rs | 13 +------------ primitives/io/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/primitives/core/src/ed25519.rs b/primitives/core/src/ed25519.rs index aa4edcda1cf7a..da0fa0047d671 100644 --- a/primitives/core/src/ed25519.rs +++ b/primitives/core/src/ed25519.rs @@ -63,21 +63,12 @@ pub struct Public(pub [u8; 32]); /// A key pair. #[cfg(feature = "full_crypto")] +#[derive(Clone)] pub struct Pair { public: VerificationKey, secret: SigningKey, } -#[cfg(feature = "full_crypto")] -impl Clone for Pair { - fn clone(&self) -> Self { - Pair { - public: self.public.clone(), - secret: self.secret.clone(), - } - } -} - impl AsRef<[u8; 32]> for Public { fn as_ref(&self) -> &[u8; 32] { &self.0 @@ -486,7 +477,6 @@ impl TraitPair for Pair { /// /// You should never need to use this; generate(), generate_with_phrase fn from_seed_slice(seed_slice: &[u8]) -> Result { - // does try_into consume the seed? can I consume seed_slice here? I think not right? let secret = SigningKey::try_from(seed_slice) .map_err(|_| SecretStringError::InvalidSeedLength)?; let public = VerificationKey::from(&secret); @@ -511,7 +501,6 @@ impl TraitPair for Pair { /// Get the public key. fn public(&self) -> Public { - // does this consume public? Is that why we used copy_from_slice? let pk: [u8; 32] = self.public.into(); Public(pk) } diff --git a/primitives/io/src/lib.rs b/primitives/io/src/lib.rs index 46eb82e33c391..f4bbb103b2deb 100644 --- a/primitives/io/src/lib.rs +++ b/primitives/io/src/lib.rs @@ -1643,7 +1643,7 @@ mod tests { let pair1 = sr25519::Pair::generate_with_phrase(None).0; let pair2 = sr25519::Pair::generate_with_phrase(None).0; let msg = b"Schnorrkcel!"; - let signature = pair.sign(msg); + let signature = pair1.sign(msg); crypto::sr25519_batch_verify(&signature, msg, &pair1.public()); crypto::sr25519_batch_verify(&signature, msg, &pair2.public());