From 5c7c5270de0325c9b520bc7f447a2ee9b3ba1e05 Mon Sep 17 00:00:00 2001 From: moshababo Date: Sun, 22 Oct 2023 20:18:53 -0500 Subject: [PATCH 01/32] Implement bn254 --- node/Cargo.lock | 48 ++++++++ node/Cargo.toml | 1 + node/libs/crypto/Cargo.toml | 1 + node/libs/crypto/src/bn254/error.rs | 15 +++ node/libs/crypto/src/bn254/mod.rs | 176 ++++++++++++++++++++++++++++ node/libs/crypto/src/bn254/tests.rs | 92 +++++++++++++++ node/libs/crypto/src/lib.rs | 5 +- 7 files changed, 336 insertions(+), 2 deletions(-) create mode 100644 node/libs/crypto/src/bn254/error.rs create mode 100644 node/libs/crypto/src/bn254/mod.rs create mode 100644 node/libs/crypto/src/bn254/tests.rs diff --git a/node/Cargo.lock b/node/Cargo.lock index 3755a046..63db74ae 100644 --- a/node/Cargo.lock +++ b/node/Cargo.lock @@ -244,6 +244,19 @@ dependencies = [ "zeroize", ] +[[package]] +name = "bn254" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694a2df5ffc5f2e385503b2987d281abeb80e02f3c30acd7de0d3db18794e73" +dependencies = [ + "byteorder", + "rand", + "sha2", + "substrate-bn", + "thiserror", +] + [[package]] name = "byteorder" version = "1.4.3" @@ -439,12 +452,19 @@ dependencies = [ "libc", ] +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "crypto" version = "0.1.0" dependencies = [ "anyhow", "blst", + "bn254", "ed25519-dalek", "hex", "rand", @@ -946,6 +966,9 @@ name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin", +] [[package]] name = "lazycell" @@ -1667,6 +1690,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + [[package]] name = "rustc_version" version = "0.4.0" @@ -1871,6 +1900,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + [[package]] name = "spki" version = "0.7.2" @@ -1907,6 +1942,19 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "substrate-bn" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b5bbfa79abbae15dd642ea8176a21a635ff3c00059961d1ea27ad04e5b441c" +dependencies = [ + "byteorder", + "crunchy", + "lazy_static", + "rand", + "rustc-hex", +] + [[package]] name = "subtle" version = "2.5.0" diff --git a/node/Cargo.toml b/node/Cargo.toml index 4ef28062..7b768de4 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -26,6 +26,7 @@ assert_matches = "1.5.0" async-trait = "0.1.71" bit-vec = "0.6" blst = "0.3.10" +bn254 = "0.0.1" clap = { version = "4.3.3", features = ["derive"] } ed25519-dalek = { version = "2.0.0", features = ["serde", "rand_core"] } futures = "0.3.28" diff --git a/node/libs/crypto/Cargo.toml b/node/libs/crypto/Cargo.toml index 93d7ec24..c2daa007 100644 --- a/node/libs/crypto/Cargo.toml +++ b/node/libs/crypto/Cargo.toml @@ -9,6 +9,7 @@ license.workspace = true [dependencies] anyhow.workspace = true blst.workspace = true +bn254.workspace = true ed25519-dalek.workspace = true hex.workspace = true rand.workspace = true diff --git a/node/libs/crypto/src/bn254/error.rs b/node/libs/crypto/src/bn254/error.rs new file mode 100644 index 00000000..3a12ca9a --- /dev/null +++ b/node/libs/crypto/src/bn254/error.rs @@ -0,0 +1,15 @@ +#[derive(Debug, thiserror::Error)] +pub enum Error { + /// Signature verification failure + #[error("Failed to decode secret key: TODO(moshababo)")] + DecodeFailure, + /// Signature verification failure + #[error("Signature verification failure: {0:?}")] + SignatureVerification(bn254::Error), + /// Aggregate signature verification failure + #[error("Aggregate signature verification failure:")] + AggregateSignatureVerification, + /// Error aggregating signatures + #[error("Error aggregating signatures:")] + SignatureAggregation, +} diff --git a/node/libs/crypto/src/bn254/mod.rs b/node/libs/crypto/src/bn254/mod.rs new file mode 100644 index 00000000..647ad712 --- /dev/null +++ b/node/libs/crypto/src/bn254/mod.rs @@ -0,0 +1,176 @@ +//! BLS signature scheme for BN254 curve. + +use std::collections::BTreeMap; + +use anyhow::anyhow; +use bn254::{ECDSA, PrivateKey}; +use rand::Rng; + +use crate::bn254::error::Error; +use crate::ByteFmt; + +mod error; + +#[cfg(test)] +mod tests; + +pub struct SecretKey(PrivateKey); + +impl SecretKey { + /// Generates a random secret key + pub fn random(rng: &mut R) -> Self { + let private_key = PrivateKey::random(rng); + return Self(private_key); + } + + /// Produces a signature using this [`SecretKey`] + pub fn sign(&self, msg: &[u8]) -> Signature { + let sig = ECDSA::sign(&msg, &self.0).unwrap(); + Signature(sig) + } + + /// Gets the corresponding [`PublicKey`] for this [`SecretKey`] + pub fn public(&self) -> PublicKey { + let pk = bn254::PublicKey::from_private_key(&self.0); + PublicKey(pk) + } +} + + +impl ByteFmt for SecretKey { + fn decode(bytes: &[u8]) -> anyhow::Result { + bn254::PrivateKey::try_from(bytes) + .map(Self) + .map_err(|e| anyhow!("Failed to decode secret key: {e:?}")) + } + + fn encode(&self) -> Vec { + self.0.to_bytes().unwrap() + } +} + +/// Type safety wrapper around a `bn254` public key. +#[derive(Clone)] +pub struct PublicKey(bn254::PublicKey); + +impl PartialEq for PublicKey { + fn eq(&self, other: &Self) -> bool { + ByteFmt::encode(self).eq(&ByteFmt::encode(other)) + } +} + +impl Eq for PublicKey {} + +impl ByteFmt for PublicKey { + fn decode(bytes: &[u8]) -> anyhow::Result { + bn254::PublicKey::from_compressed(bytes) + .map(Self) + .map_err(|err| anyhow!("Error decoding public key: {err:?}")) + } + + fn encode(&self) -> Vec { + self.0.to_compressed().unwrap() + } +} + +impl std::hash::Hash for PublicKey { + fn hash(&self, state: &mut H) { + state.write(&self.0.to_compressed().unwrap()) + } +} + +impl PartialOrd for PublicKey { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for PublicKey { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + ByteFmt::encode(self).cmp(&ByteFmt::encode(other)) + } +} + +#[derive(Clone, Debug)] +pub struct Signature(bn254::Signature); + +impl Signature { + pub fn verify(&self, msg: &[u8], pk: &PublicKey) -> Result<(), Error> { + let result = ECDSA::verify(msg, &self.0, &pk.0); + match result { + Ok(()) => Ok(()), + Err(err) => Err(Error::SignatureVerification(err)), + } + } +} + +impl PartialEq for Signature { + fn eq(&self, other: &Self) -> bool { + ByteFmt::encode(self).eq(&ByteFmt::encode(other)) + } +} + +impl Eq for Signature {} + +impl ByteFmt for Signature { + fn decode(bytes: &[u8]) -> anyhow::Result { + bn254::Signature::from_compressed(bytes) + .map(Self) + .map_err(|err| anyhow!("Error decoding signature: {err:?}")) + } + fn encode(&self) -> Vec { + self.0.to_compressed().unwrap() } +} + +impl PartialOrd for Signature { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Signature { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + ByteFmt::encode(self).cmp(&ByteFmt::encode(other)) + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct AggregateSignature(Signature); + +impl AggregateSignature { + pub fn aggregate<'a>(sigs: impl IntoIterator) -> Self { + let sigs: Vec = sigs.into_iter().map(|s| s.0).collect(); + let mut agg = sigs[0]; + for i in 1..sigs.len() { + agg = agg + sigs[i]; + } + + AggregateSignature(Signature(agg)) + } + + pub fn verify<'a>( + &self, + msgs_and_pks: impl Iterator, + ) -> Result<(), Error> { + // Aggregate public keys if they are signing the same hash. Each public key aggregated + // is one fewer pairing to calculate. + let mut tree_map: BTreeMap<_, PublicKey> = BTreeMap::new(); + + for (msg, pk) in msgs_and_pks { + if let Some(existing_pk) = tree_map.get_mut(msg) { + let agg = PublicKey(existing_pk.0 + pk.0); + tree_map.insert(msg, agg); + } else { + tree_map.insert(msg, (*pk).clone()); + } + } + + for (msg, pk) in tree_map { + if let Err(err) = self.0.verify(msg, &pk) { + return Err(err); + } + } + + Ok(()) + } +} diff --git a/node/libs/crypto/src/bn254/tests.rs b/node/libs/crypto/src/bn254/tests.rs new file mode 100644 index 00000000..3d145b4a --- /dev/null +++ b/node/libs/crypto/src/bn254/tests.rs @@ -0,0 +1,92 @@ +use crate::bn254::{AggregateSignature, PublicKey, SecretKey, Signature, ByteFmt}; +use rand::{rngs::StdRng, Rng, SeedableRng}; +use std::iter::repeat_with; + +#[test] +fn signature_smoke() { + let mut rng = StdRng::seed_from_u64(29483920); + let sk = SecretKey::decode(&rng.gen::<[u8; 32]>()).unwrap(); + let pk = sk.public(); + + let msg: [u8; 32] = rng.gen(); + let sig = sk.sign(&msg); + + sig.verify(&msg, &pk).unwrap() +} + +#[test] +fn signature_failure_smoke() { + let mut rng = StdRng::seed_from_u64(29483920); + + let sk1 = SecretKey::decode(&rng.gen::<[u8; 32]>()).unwrap(); + let sk2 = SecretKey::decode(&rng.gen::<[u8; 32]>()).unwrap(); + let pk2 = sk2.public(); + let msg: [u8; 32] = rng.gen(); + let sig = sk1.sign(&msg); + + assert!(sig.verify(&msg, &pk2).is_err()) +} + +#[test] +fn aggregate_signature_smoke() { + let mut rng = StdRng::seed_from_u64(29483920); + + // Use an arbitrary 5 keys for the smoke test + let sks: Vec = repeat_with(|| SecretKey::random(&mut rng)) + .take(5) + .collect(); + let pks: Vec = sks.iter().map(|k| k.public()).collect(); + let msg: [u8; 32] = rng.gen(); + + let sigs: Vec = sks.iter().map(|k| k.sign(&msg)).collect(); + let agg_sig = AggregateSignature::aggregate(&sigs); + + agg_sig.verify(pks.iter().map(|pk| (&msg[..], pk))).unwrap() +} + +#[test] +fn aggregate_signature_failure_smoke() { + let mut rng = StdRng::seed_from_u64(29483920); + + // Use an arbitrary 5 keys for the smoke test + let sks: Vec = repeat_with(|| SecretKey::random(&mut rng)) + .take(5) + .collect(); + let pks: Vec = sks.iter().map(|k| k.public()).collect(); + let msg: [u8; 32] = rng.gen(); + + // Take only three signatures for the aggregate + let sigs: Vec = sks.iter().take(3).map(|k| k.sign(&msg)).collect(); + + let agg_sig = AggregateSignature::aggregate(&sigs); + + assert!(agg_sig.verify(pks.iter().map(|pk| (&msg[..], pk))).is_err()) +} + + +#[test] +fn aggregate_signature_distinct_messages() { + let mut rng = StdRng::seed_from_u64(29483920); + let num_keys = 6; + let num_distinct = 2; + + // Use an arbitrary 5 keys for the smoke test + let sks: Vec = repeat_with(|| SecretKey::random(&mut rng)) + .take(num_keys) + .collect(); + let pks: Vec = sks.iter().map(|k| k.public()).collect(); + // Create 2 distinct messages + let msgs: Vec<[u8; 32]> = repeat_with(|| rng.gen()).take(num_distinct).collect(); + + let mut sigs: Vec = Vec::new(); + let mut pairs: Vec<(&[u8], &PublicKey)> = Vec::new(); + for (i, sk) in sks.iter().enumerate() { + let msg = &msgs[i % num_distinct]; + sigs.push(sk.sign(msg)); + pairs.push((msg, &pks[i])) + } + + let agg_sig = AggregateSignature::aggregate(&sigs); + + agg_sig.verify(pairs.into_iter()).unwrap() +} diff --git a/node/libs/crypto/src/lib.rs b/node/libs/crypto/src/lib.rs index b777892a..927d77ed 100644 --- a/node/libs/crypto/src/lib.rs +++ b/node/libs/crypto/src/lib.rs @@ -1,8 +1,9 @@ //! Collection of cryptographic primitives used in zksync-bft repository. +pub use fmt::*; + pub mod bls12_381; +pub mod bn254; pub mod ed25519; mod fmt; pub mod sha256; - -pub use fmt::*; From cae12915d4e6c3c4ff62361a9cf5846f95299cf4 Mon Sep 17 00:00:00 2001 From: moshababo Date: Mon, 23 Oct 2023 05:32:58 -0500 Subject: [PATCH 02/32] Replace bls12_381 --- node/actors/consensus/src/leader/error.rs | 4 +-- node/actors/consensus/src/replica/error.rs | 4 +-- .../network/src/consensus/handshake/mod.rs | 4 +-- node/libs/crypto/src/bn254/error.rs | 3 -- node/libs/crypto/src/bn254/mod.rs | 32 ++++++++++++++--- node/libs/crypto/src/bn254/testonly.rs | 35 +++++++++++++++++++ node/libs/crypto/src/bn254/tests.rs | 5 ++- node/libs/crypto/src/lib.rs | 1 - .../src/validator/keys/aggregate_signature.rs | 23 ++++++------ node/libs/roles/src/validator/keys/mod.rs | 2 +- .../roles/src/validator/keys/public_key.rs | 8 ++--- .../roles/src/validator/keys/secret_key.rs | 13 +++---- .../roles/src/validator/keys/signature.rs | 8 ++--- .../roles/src/validator/messages/consensus.rs | 4 +-- node/libs/roles/src/validator/messages/msg.rs | 2 +- node/tools/src/bin/keys.rs | 2 +- 16 files changed, 103 insertions(+), 47 deletions(-) create mode 100644 node/libs/crypto/src/bn254/testonly.rs diff --git a/node/actors/consensus/src/leader/error.rs b/node/actors/consensus/src/leader/error.rs index 32b05713..c3e37acd 100644 --- a/node/actors/consensus/src/leader/error.rs +++ b/node/actors/consensus/src/leader/error.rs @@ -40,9 +40,9 @@ pub(crate) enum Error { current_view: validator::ViewNumber, }, #[error("received replica commit message with invalid signature")] - ReplicaCommitInvalidSignature(#[source] crypto::bls12_381::Error), + ReplicaCommitInvalidSignature(#[source] crypto::bn254::Error), #[error("received replica prepare message with invalid signature")] - ReplicaPrepareInvalidSignature(#[source] crypto::bls12_381::Error), + ReplicaPrepareInvalidSignature(#[source] crypto::bn254::Error), #[error("received replica prepare message with invalid high QC")] ReplicaPrepareInvalidHighQC(#[source] anyhow::Error), } diff --git a/node/actors/consensus/src/replica/error.rs b/node/actors/consensus/src/replica/error.rs index 4e16cd2f..06c342de 100644 --- a/node/actors/consensus/src/replica/error.rs +++ b/node/actors/consensus/src/replica/error.rs @@ -26,9 +26,9 @@ pub(crate) enum Error { current_phase: validator::Phase, }, #[error("received leader commit message with invalid signature")] - LeaderCommitInvalidSignature(#[source] crypto::bls12_381::Error), + LeaderCommitInvalidSignature(#[source] crypto::bn254::Error), #[error("received leader prepare message with invalid signature")] - LeaderPrepareInvalidSignature(#[source] crypto::bls12_381::Error), + LeaderPrepareInvalidSignature(#[source] crypto::bn254::Error), #[error("received leader commit message with invalid justification")] LeaderCommitInvalidJustification(#[source] anyhow::Error), #[error("received leader prepare message with empty map in the justification")] diff --git a/node/actors/network/src/consensus/handshake/mod.rs b/node/actors/network/src/consensus/handshake/mod.rs index d0eabab9..4128018e 100644 --- a/node/actors/network/src/consensus/handshake/mod.rs +++ b/node/actors/network/src/consensus/handshake/mod.rs @@ -1,7 +1,7 @@ use crate::{frame, noise}; use anyhow::Context as _; use concurrency::{ctx, time}; -use crypto::{bls12_381, ByteFmt}; +use crypto::{bn254, ByteFmt}; use roles::{node, validator}; use schema::{proto::network::consensus as proto, read_required, ProtoFmt}; @@ -43,7 +43,7 @@ pub(super) enum Error { #[error("unexpected peer")] PeerMismatch, #[error("validator signature {0}")] - Signature(#[from] bls12_381::Error), + Signature(#[from] bn254::Error), #[error("stream {0}")] Stream(#[source] anyhow::Error), } diff --git a/node/libs/crypto/src/bn254/error.rs b/node/libs/crypto/src/bn254/error.rs index 3a12ca9a..a89268e9 100644 --- a/node/libs/crypto/src/bn254/error.rs +++ b/node/libs/crypto/src/bn254/error.rs @@ -1,8 +1,5 @@ #[derive(Debug, thiserror::Error)] pub enum Error { - /// Signature verification failure - #[error("Failed to decode secret key: TODO(moshababo)")] - DecodeFailure, /// Signature verification failure #[error("Signature verification failure: {0:?}")] SignatureVerification(bn254::Error), diff --git a/node/libs/crypto/src/bn254/mod.rs b/node/libs/crypto/src/bn254/mod.rs index 647ad712..37e93956 100644 --- a/node/libs/crypto/src/bn254/mod.rs +++ b/node/libs/crypto/src/bn254/mod.rs @@ -3,14 +3,15 @@ use std::collections::BTreeMap; use anyhow::anyhow; -use bn254::{ECDSA, PrivateKey}; +use bn254::{PrivateKey, ECDSA}; use rand::Rng; -use crate::bn254::error::Error; +pub use crate::bn254::error::Error; use crate::ByteFmt; mod error; +mod testonly; #[cfg(test)] mod tests; @@ -36,7 +37,6 @@ impl SecretKey { } } - impl ByteFmt for SecretKey { fn decode(bytes: &[u8]) -> anyhow::Result { bn254::PrivateKey::try_from(bytes) @@ -119,7 +119,8 @@ impl ByteFmt for Signature { .map_err(|err| anyhow!("Error decoding signature: {err:?}")) } fn encode(&self) -> Vec { - self.0.to_compressed().unwrap() } + self.0.to_compressed().unwrap() + } } impl PartialOrd for Signature { @@ -174,3 +175,26 @@ impl AggregateSignature { Ok(()) } } + +impl ByteFmt for AggregateSignature { + fn decode(bytes: &[u8]) -> anyhow::Result { + let signature = Signature::decode(bytes)?; + Ok(AggregateSignature(signature)) + } + + fn encode(&self) -> Vec { + Signature::encode(&self.0) + } +} + +impl PartialOrd for AggregateSignature { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for AggregateSignature { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + ByteFmt::encode(self).cmp(&ByteFmt::encode(other)) + } +} diff --git a/node/libs/crypto/src/bn254/testonly.rs b/node/libs/crypto/src/bn254/testonly.rs new file mode 100644 index 00000000..a516579e --- /dev/null +++ b/node/libs/crypto/src/bn254/testonly.rs @@ -0,0 +1,35 @@ +//! Random key generation, intended for use in testing + +use super::{AggregateSignature, ByteFmt, PublicKey, SecretKey, Signature}; +use rand::{distributions::Standard, prelude::Distribution, Rng}; + +/// Generates a random SecretKey. This is meant for testing purposes. +impl Distribution for Standard { + fn sample(&self, rng: &mut R) -> SecretKey { + SecretKey::decode(&rng.gen::<[u8; 32]>()).unwrap() + } +} + +/// Generates a random Signature. This is meant for testing purposes. +impl Distribution for Standard { + fn sample(&self, rng: &mut R) -> Signature { + let key = rng.gen::(); + let msg = rng.gen::<[u8; 4]>(); + key.sign(&msg) + } +} + +/// Generates a random AggregateSignature. This is meant for testing purposes. +impl Distribution for Standard { + fn sample(&self, rng: &mut R) -> AggregateSignature { + let sig: Signature = self.sample(rng); + AggregateSignature(sig) + } +} + +/// Generates a random PublicKey. This is meant for testing purposes. +impl Distribution for Standard { + fn sample(&self, rng: &mut R) -> PublicKey { + rng.gen::().public() + } +} diff --git a/node/libs/crypto/src/bn254/tests.rs b/node/libs/crypto/src/bn254/tests.rs index 3d145b4a..273f7afb 100644 --- a/node/libs/crypto/src/bn254/tests.rs +++ b/node/libs/crypto/src/bn254/tests.rs @@ -1,4 +1,4 @@ -use crate::bn254::{AggregateSignature, PublicKey, SecretKey, Signature, ByteFmt}; +use crate::bn254::{AggregateSignature, ByteFmt, PublicKey, SecretKey, Signature}; use rand::{rngs::StdRng, Rng, SeedableRng}; use std::iter::repeat_with; @@ -63,11 +63,10 @@ fn aggregate_signature_failure_smoke() { assert!(agg_sig.verify(pks.iter().map(|pk| (&msg[..], pk))).is_err()) } - #[test] fn aggregate_signature_distinct_messages() { let mut rng = StdRng::seed_from_u64(29483920); - let num_keys = 6; + let num_keys = 5; let num_distinct = 2; // Use an arbitrary 5 keys for the smoke test diff --git a/node/libs/crypto/src/lib.rs b/node/libs/crypto/src/lib.rs index 927d77ed..d314b7bb 100644 --- a/node/libs/crypto/src/lib.rs +++ b/node/libs/crypto/src/lib.rs @@ -2,7 +2,6 @@ pub use fmt::*; -pub mod bls12_381; pub mod bn254; pub mod ed25519; mod fmt; diff --git a/node/libs/roles/src/validator/keys/aggregate_signature.rs b/node/libs/roles/src/validator/keys/aggregate_signature.rs index a6b8743a..a19d84be 100644 --- a/node/libs/roles/src/validator/keys/aggregate_signature.rs +++ b/node/libs/roles/src/validator/keys/aggregate_signature.rs @@ -1,20 +1,21 @@ -use super::{Error, PublicKey, Signature}; -use crate::validator::messages::{Msg, MsgHash}; -use crypto::{bls12_381, ByteFmt, Text, TextFmt}; use std::fmt; + +use crypto::{bn254, ByteFmt, Text, TextFmt}; use utils::enum_util::Variant; +use crate::validator::messages::{Msg, MsgHash}; + +use super::{Error, PublicKey, Signature}; + /// An aggregate signature from a validator. #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] -pub struct AggregateSignature(pub(crate) bls12_381::AggregateSignature); +pub struct AggregateSignature(pub(crate) bn254::AggregateSignature); impl AggregateSignature { /// Generate a new aggregate signature from a list of signatures. - pub fn aggregate<'a>(sigs: impl IntoIterator) -> anyhow::Result { - Ok(AggregateSignature( - bls12_381::AggregateSignature::aggregate( - sigs.into_iter().map(|sig| &sig.0).collect::>(), - )?, + pub fn aggregate<'a>(sigs: impl IntoIterator) -> Self { + AggregateSignature(bn254::AggregateSignature::aggregate( + sigs.into_iter().map(|sig| &sig.0).collect::>(), )) } @@ -55,12 +56,12 @@ impl ByteFmt for AggregateSignature { impl TextFmt for AggregateSignature { fn encode(&self) -> String { format!( - "validator:aggregate_signature:bls12_381:{}", + "validator:aggregate_signature:bn254:{}", hex::encode(ByteFmt::encode(&self.0)) ) } fn decode(text: Text) -> anyhow::Result { - text.strip("validator:aggregate_signature:bls12_381:")? + text.strip("validator:aggregate_signature:bn254:")? .decode_hex() .map(Self) } diff --git a/node/libs/roles/src/validator/keys/mod.rs b/node/libs/roles/src/validator/keys/mod.rs index 36b17bff..77e23ff9 100644 --- a/node/libs/roles/src/validator/keys/mod.rs +++ b/node/libs/roles/src/validator/keys/mod.rs @@ -11,4 +11,4 @@ pub use secret_key::SecretKey; pub use signature::Signature; /// Error type returned by validator key operations. -pub type Error = crypto::bls12_381::Error; +pub type Error = crypto::bn254::Error; diff --git a/node/libs/roles/src/validator/keys/public_key.rs b/node/libs/roles/src/validator/keys/public_key.rs index fb668ac1..40f8edfc 100644 --- a/node/libs/roles/src/validator/keys/public_key.rs +++ b/node/libs/roles/src/validator/keys/public_key.rs @@ -1,9 +1,9 @@ -use crypto::{bls12_381, ByteFmt, Text, TextFmt}; +use crypto::{bn254, ByteFmt, Text, TextFmt}; use std::fmt; /// A public key for a validator. #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct PublicKey(pub(crate) bls12_381::PublicKey); +pub struct PublicKey(pub(crate) bn254::PublicKey); impl ByteFmt for PublicKey { fn encode(&self) -> Vec { @@ -17,12 +17,12 @@ impl ByteFmt for PublicKey { impl TextFmt for PublicKey { fn encode(&self) -> String { format!( - "validator:public:bls12_381:{}", + "validator:public:bn254:{}", hex::encode(ByteFmt::encode(&self.0)) ) } fn decode(text: Text) -> anyhow::Result { - text.strip("validator:public:bls12_381:")? + text.strip("validator:public:bn254:")? .decode_hex() .map(Self) } diff --git a/node/libs/roles/src/validator/keys/secret_key.rs b/node/libs/roles/src/validator/keys/secret_key.rs index db1b8f4b..d16e3a55 100644 --- a/node/libs/roles/src/validator/keys/secret_key.rs +++ b/node/libs/roles/src/validator/keys/secret_key.rs @@ -1,6 +1,7 @@ use super::{PublicKey, Signature}; use crate::validator::messages::{Msg, MsgHash, Signed}; -use crypto::{bls12_381, ByteFmt, Text, TextFmt}; +use crypto::{bn254, ByteFmt, Text, TextFmt}; +use rand::Rng; use std::{fmt, sync::Arc}; use utils::enum_util::Variant; @@ -8,12 +9,12 @@ use utils::enum_util::Variant; /// SecretKey is put into an Arc, so that we can clone it, /// without copying the secret all over the RAM. #[derive(Clone)] -pub struct SecretKey(pub(crate) Arc); +pub struct SecretKey(pub(crate) Arc); impl SecretKey { /// Generate a new secret key. - pub fn generate(ikm: [u8; 32]) -> Self { - Self(Arc::new(bls12_381::SecretKey::generate(ikm))) + pub fn generate(rng: &mut R) -> Self { + Self(Arc::new(bn254::SecretKey::random(rng))) } /// Public key corresponding to this secret key. @@ -50,13 +51,13 @@ impl ByteFmt for SecretKey { impl TextFmt for SecretKey { fn encode(&self) -> String { format!( - "validator:secret:bls12_381:{}", + "validator:secret:bn254:{}", hex::encode(ByteFmt::encode(&*self.0)) ) } fn decode(text: Text) -> anyhow::Result { - text.strip("validator:secret:bls12_381:")? + text.strip("validator:secret:bn254:")? .decode_hex() .map(Arc::new) .map(Self) diff --git a/node/libs/roles/src/validator/keys/signature.rs b/node/libs/roles/src/validator/keys/signature.rs index 21e0ddf4..d35ec6d6 100644 --- a/node/libs/roles/src/validator/keys/signature.rs +++ b/node/libs/roles/src/validator/keys/signature.rs @@ -1,11 +1,11 @@ use super::{Error, PublicKey}; use crate::validator::messages::{Msg, MsgHash}; -use crypto::{bls12_381, ByteFmt, Text, TextFmt}; +use crypto::{bn254, ByteFmt, Text, TextFmt}; use std::fmt; /// A signature from a validator. #[derive(Clone, PartialEq, Eq)] -pub struct Signature(pub(crate) bls12_381::Signature); +pub struct Signature(pub(crate) bn254::Signature); impl Signature { /// Verify a message against a public key. @@ -31,12 +31,12 @@ impl ByteFmt for Signature { impl TextFmt for Signature { fn encode(&self) -> String { format!( - "validator:signature:bls12_381:{}", + "validator:signature:bn254:{}", hex::encode(ByteFmt::encode(&self.0)) ) } fn decode(text: Text) -> anyhow::Result { - text.strip("validator:signature:bls12_381:")? + text.strip("validator:signature:bn254:")? .decode_hex() .map(Self) } diff --git a/node/libs/roles/src/validator/messages/consensus.rs b/node/libs/roles/src/validator/messages/consensus.rs index e08b299b..165a3bb0 100644 --- a/node/libs/roles/src/validator/messages/consensus.rs +++ b/node/libs/roles/src/validator/messages/consensus.rs @@ -177,7 +177,7 @@ impl PrepareQC { // Aggregate the signatures. let signature = - validator::AggregateSignature::aggregate(signed_messages.iter().map(|v| &v.sig))?; + validator::AggregateSignature::aggregate(signed_messages.iter().map(|v| &v.sig)); Ok(Self { map, signature }) } @@ -297,7 +297,7 @@ impl CommitQC { .collect(); // Aggregate the signatures. - let signature = validator::AggregateSignature::aggregate(msg_map.values().copied())?; + let signature = validator::AggregateSignature::aggregate(msg_map.values().copied()); Ok(Self { message, signers: Signers(bit_vec), diff --git a/node/libs/roles/src/validator/messages/msg.rs b/node/libs/roles/src/validator/messages/msg.rs index 51068c04..e38fac2b 100644 --- a/node/libs/roles/src/validator/messages/msg.rs +++ b/node/libs/roles/src/validator/messages/msg.rs @@ -2,7 +2,7 @@ use super::{ConsensusMsg, NetAddress}; use crate::{node::SessionId, validator}; -use crypto::{bls12_381::Error, sha256, ByteFmt, Text, TextFmt}; +use crypto::{bn254::Error, sha256, ByteFmt, Text, TextFmt}; use std::fmt; use utils::enum_util::{ErrBadVariant, Variant}; diff --git a/node/tools/src/bin/keys.rs b/node/tools/src/bin/keys.rs index 2355b58b..25d08aaf 100644 --- a/node/tools/src/bin/keys.rs +++ b/node/tools/src/bin/keys.rs @@ -6,7 +6,7 @@ use roles::validator; /// This tool generates a validator key pair and prints it to stdout. fn main() { - let key = validator::SecretKey::generate(rand::rngs::OsRng.gen()); + let key = validator::SecretKey::generate(&mut rand::rngs::OsRng); let encoded_pk = crypto::TextFmt::encode(&key.public()); let encoded_sk = crypto::TextFmt::encode(&key); println!("Generating keypair:"); From b35e202f932995e994e9d0ee511eff9b62013ee5 Mon Sep 17 00:00:00 2001 From: moshababo Date: Thu, 26 Oct 2023 21:10:21 -0500 Subject: [PATCH 03/32] [WIP] Use ark_bn254 --- node/Cargo.lock | 245 ++++++++++++++++++++--- node/Cargo.toml | 7 +- node/libs/crypto/Cargo.toml | 3 + node/libs/crypto/src/bn254/mod.rs | 3 +- node/libs/crypto/src/bn254_2/error.rs | 9 + node/libs/crypto/src/bn254_2/hash.rs | 25 +++ node/libs/crypto/src/bn254_2/mod.rs | 238 ++++++++++++++++++++++ node/libs/crypto/src/bn254_2/testonly.rs | 35 ++++ node/libs/crypto/src/bn254_2/tests.rs | 91 +++++++++ node/libs/crypto/src/lib.rs | 1 + 10 files changed, 631 insertions(+), 26 deletions(-) create mode 100644 node/libs/crypto/src/bn254_2/error.rs create mode 100644 node/libs/crypto/src/bn254_2/hash.rs create mode 100644 node/libs/crypto/src/bn254_2/mod.rs create mode 100644 node/libs/crypto/src/bn254_2/testonly.rs create mode 100644 node/libs/crypto/src/bn254_2/tests.rs diff --git a/node/Cargo.lock b/node/Cargo.lock index 63db74ae..d7580e4a 100644 --- a/node/Cargo.lock +++ b/node/Cargo.lock @@ -52,6 +52,18 @@ dependencies = [ "subtle", ] +[[package]] +name = "ahash" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.0.4" @@ -116,6 +128,123 @@ version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +[[package]] +name = "ark-bn254" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a22f4561524cd949590d78d7d4c5df8f592430d221f7f3c9497bbafd8972120f" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-std", +] + +[[package]] +name = "ark-ec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" +dependencies = [ + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", + "itertools", + "num-traits", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "derivative", + "digest", + "itertools", + "num-bigint", + "num-traits", + "paste", + "rustc_version", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-poly" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" +dependencies = [ + "ark-ff", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-serialize-derive", + "ark-std", + "digest", + "num-bigint", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand", +] + [[package]] name = "assert_matches" version = "1.5.0" @@ -130,7 +259,7 @@ checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -184,7 +313,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -382,7 +511,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -463,10 +592,13 @@ name = "crypto" version = "0.1.0" dependencies = [ "anyhow", + "ark-bn254", + "ark-ec", "blst", "bn254", "ed25519-dalek", "hex", + "num-traits", "rand", "sha2", "thiserror", @@ -516,7 +648,7 @@ checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -535,6 +667,17 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7684a49fb1af197853ef7b2ee694bc1f5b4179556f1e5710e1760c5db6f5e929" +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "diff" version = "0.1.13" @@ -725,7 +868,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -826,6 +969,15 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + [[package]] name = "heck" version = "0.4.1" @@ -923,7 +1075,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", ] [[package]] @@ -1036,7 +1188,7 @@ checksum = "bc28438cad73dcc90ff3466fc329a9252b1b8ba668eb0d5668ba97088cf4eef0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -1163,11 +1315,32 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + [[package]] name = "num-traits" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", ] @@ -1241,6 +1414,12 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + [[package]] name = "peeking_take_while" version = "0.1.2" @@ -1274,7 +1453,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -1367,7 +1546,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c64d9ba0963cdcea2e1b2230fbae2bab30eb25a174be395c41e764bfb65dd62" dependencies = [ "proc-macro2", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -1399,7 +1578,7 @@ checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -1480,7 +1659,7 @@ checksum = "7718375aa8f966df66e583b608a305a45bc87eeb1ffd5db87fae673bea17a7e4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -1741,7 +1920,7 @@ dependencies = [ "rand", "serde", "serde_json", - "syn 2.0.29", + "syn 2.0.32", "tokio", ] @@ -1784,7 +1963,7 @@ checksum = "aafe972d60b0b9bee71a91b92fee2d4fb3c9d7e8f6b179aa99f27203d99a4816" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -1974,9 +2153,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.29" +version = "2.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" +checksum = "239814284fd6f1a4ffe4ca893952cdd93c224b6a1571c9a9eadd670295c0c9e2" dependencies = [ "proc-macro2", "quote", @@ -2032,7 +2211,7 @@ checksum = "b4dc4744280091c8760f456b14c5598f5f7afe96851b4da30fe0933725dae0d3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -2052,7 +2231,7 @@ checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -2118,7 +2297,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -2177,7 +2356,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -2310,7 +2489,7 @@ source = "git+https://github.com/matter-labs/vise.git?rev=8322ddc4bb115a7d111276 dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -2433,6 +2612,26 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" +[[package]] +name = "zerocopy" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81ba595b9f2772fbee2312de30eeb80ec773b4cb2f1e8098db024afadda6c06f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "772666c41fb6dceaf520b564b962d738a8e1a83b41bd48945f50837aed78bb1d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.32", +] + [[package]] name = "zeroize" version = "1.6.0" @@ -2450,7 +2649,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] diff --git a/node/Cargo.toml b/node/Cargo.toml index 7b768de4..9e745430 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -27,17 +27,20 @@ async-trait = "0.1.71" bit-vec = "0.6" blst = "0.3.10" bn254 = "0.0.1" +ark-bn254 = "0.4.0" +ark-ec = "0.4.2" +num-traits = "0.2.17" clap = { version = "4.3.3", features = ["derive"] } ed25519-dalek = { version = "2.0.0", features = ["serde", "rand_core"] } futures = "0.3.28" hex = "0.4.3" -hyper = { version = "0.14.27", features = ["http1", "http2","server","tcp"] } +hyper = { version = "0.14.27", features = ["http1", "http2", "server", "tcp"] } im = "15.1.0" once_cell = "1.17.1" pin-project = "1.1.0" prost = "0.11.0" prost-build = "0.11.0" -prost-reflect = { version = "0.11.0", features = ["derive","serde"] } +prost-reflect = { version = "0.11.0", features = ["derive", "serde"] } prost-reflect-build = "0.11.0" protoc-bin-vendored = "3.0.0" prettyplease = "0.2.6" diff --git a/node/libs/crypto/Cargo.toml b/node/libs/crypto/Cargo.toml index c2daa007..ac842036 100644 --- a/node/libs/crypto/Cargo.toml +++ b/node/libs/crypto/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true anyhow.workspace = true blst.workspace = true bn254.workspace = true +ark-bn254.workspace = true +ark-ec.workspace = true +num-traits.workspace = true ed25519-dalek.workspace = true hex.workspace = true rand.workspace = true diff --git a/node/libs/crypto/src/bn254/mod.rs b/node/libs/crypto/src/bn254/mod.rs index 37e93956..79e8f323 100644 --- a/node/libs/crypto/src/bn254/mod.rs +++ b/node/libs/crypto/src/bn254/mod.rs @@ -3,10 +3,11 @@ use std::collections::BTreeMap; use anyhow::anyhow; -use bn254::{PrivateKey, ECDSA}; +use bn254::{ECDSA, PrivateKey}; use rand::Rng; pub use crate::bn254::error::Error; +use crate::bn254_2::hash; use crate::ByteFmt; mod error; diff --git a/node/libs/crypto/src/bn254_2/error.rs b/node/libs/crypto/src/bn254_2/error.rs new file mode 100644 index 00000000..bb97bacd --- /dev/null +++ b/node/libs/crypto/src/bn254_2/error.rs @@ -0,0 +1,9 @@ +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("Signature verification failure")] + SignatureVerificationFailure, + #[error("Aggregate signature verification failure")] + AggregateSignatureVerificationFailure, + #[error("Signature aggregation failure")] + SignatureAggregationFailure, +} diff --git a/node/libs/crypto/src/bn254_2/hash.rs b/node/libs/crypto/src/bn254_2/hash.rs new file mode 100644 index 00000000..2610a279 --- /dev/null +++ b/node/libs/crypto/src/bn254_2/hash.rs @@ -0,0 +1,25 @@ +use sha2::Digest as _; +use ark_ec::AffineRepr as _; +use ark_bn254::{G1Affine, G1Projective}; + +/// H(m) as a point in G1. +pub(crate) fn hash_to_g1(msg: &[u8]) -> G1Projective { + for i in 0..100 { + // Hash the message with the index as suffix. + let bytes: [u8; 32] = sha2::Sha256::new() + .chain_update(&msg) + .chain_update((i as u32).to_be_bytes()) + .finalize() + .into(); + + // Try to get a G1 point from the hash. + let p = G1Affine::from_random_bytes(&bytes); + + if let Some(p) = p { + return p.into() + + } + } + // It should be statistically infeasible to finish the loop without finding a point. + unreachable!() +} \ No newline at end of file diff --git a/node/libs/crypto/src/bn254_2/mod.rs b/node/libs/crypto/src/bn254_2/mod.rs new file mode 100644 index 00000000..a45ef744 --- /dev/null +++ b/node/libs/crypto/src/bn254_2/mod.rs @@ -0,0 +1,238 @@ +//! BLS signature scheme for BN254 curve. + +use std::collections::BTreeMap; + +use ark_bn254::{Bn254, Fr, G1Projective as G1, G2Projective as G2}; +use ark_ec::AffineRepr as _; +use ark_ec::bn::Bn; +use ark_ec::Group as _; +use ark_ec::pairing::Pairing as _; +use ark_ec::pairing::PairingOutput; +use num_traits::Zero as _; +use rand::Rng; + +pub use crate::bn254_2::error::Error; +use crate::ByteFmt; + +mod error; + +mod testonly; +#[cfg(test)] +mod tests; +pub mod hash; + +pub struct SecretKey(pub Fr); + +impl SecretKey { + /// Generates a random secret key + pub fn random(rng: &mut R) -> Self { + let scalar = Fr::new(rng.gen()); + SecretKey(scalar) + } + + /// Gets the corresponding [`PublicKey`] for this [`SecretKey`] + pub fn public(&self) -> PublicKey { + let p = G2::generator() * self.0; + PublicKey(p) + } + + /// Produces a signature using this [`SecretKey`] + pub fn sign(&self, msg: &[u8]) -> Signature { + let hash_point = hash::hash_to_g1(msg); + let sig = hash_point * self.0; + Signature(sig) + } +} + +impl ByteFmt for SecretKey { + fn decode(bytes: &[u8]) -> anyhow::Result { + panic!("implement"); + // bn254::PrivateKey::try_from(bytes) + // .map(Self) + // .map_err(|e| anyhow!("Failed to decode secret key: {e:?}")) + } + + fn encode(&self) -> Vec { + panic!("implement"); + // self.0.to_bytes().unwrap() + } +} + +/// Type safety wrapper around a `bn254` public key. +#[derive(Clone)] +pub struct PublicKey(pub G2); + +impl PartialEq for PublicKey { + fn eq(&self, other: &Self) -> bool { + ByteFmt::encode(self).eq(&ByteFmt::encode(other)) + } +} + +impl Eq for PublicKey {} + +impl ByteFmt for PublicKey { + fn decode(bytes: &[u8]) -> anyhow::Result { + panic!("implement"); + // bn254::PublicKey::from_compressed(bytes) + // .map(Self) + // .map_err(|err| anyhow!("Error decoding public key: {err:?}")) + } + + fn encode(&self) -> Vec { + panic!("implement"); + // self.0.to_compressed().unwrap() + } +} + +impl std::hash::Hash for PublicKey { + fn hash(&self, state: &mut H) { + panic!("implement"); + // state.write(&self.0.to_compressed().unwrap()) + } +} + +impl PartialOrd for PublicKey { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for PublicKey { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + ByteFmt::encode(self).cmp(&ByteFmt::encode(other)) + } +} + +#[derive(Clone, Debug)] +pub struct Signature(pub G1); + +impl Signature { + pub fn verify(&self, msg: &[u8], pk: &PublicKey) -> Result<(), Error> { + let hash_point = hash::hash_to_g1(msg); + + let mut vals = Vec::new(); + // First pair: e(H(m): G1, pk: G2) + vals.push((hash_point, pk.0)); + // Second pair: e(sig: G1, -generator: G2) + vals.push((self.0, -G2::generator())); + + let output = multi_pairing(&vals); + // println!("{:?}", output); + // let a: PairingOutput = PairingOutput::zero(); + // println!("{:?}", a); + if output == PairingOutput::zero() { + Ok(()) + } else { + Err(Error::SignatureVerificationFailure) + } + } +} + +fn multi_pairing(pairs: &[(G1, G2)]) -> PairingOutput { + let mut g1: Vec = Vec::new(); + let mut g2: Vec = Vec::new(); + for (p, q) in pairs { + g1.push(p.clone()); + g2.push(q.clone()); + } + + Bn254::multi_pairing(g1, g2) +} + +impl PartialEq for Signature { + fn eq(&self, other: &Self) -> bool { + ByteFmt::encode(self).eq(&ByteFmt::encode(other)) + } +} + +impl Eq for Signature {} + +impl ByteFmt for Signature { + fn decode(bytes: &[u8]) -> anyhow::Result { + panic!("implement"); + // bn254::Signature::from_compressed(bytes) + // .map(Self) + // .map_err(|err| anyhow!("Error decoding signature: {err:?}")) + } + fn encode(&self) -> Vec { + panic!() + // self.0.to_compressed().unwrap() + } +} + +impl PartialOrd for Signature { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Signature { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + ByteFmt::encode(self).cmp(&ByteFmt::encode(other)) + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct AggregateSignature(Signature); + +impl AggregateSignature { + pub fn aggregate<'a>(sigs: impl IntoIterator) -> Self { + panic!("implement"); + // let sigs: Vec = sigs.into_iter().map(|s| s.0).collect(); + // let mut agg = sigs[0]; + // for i in 1..sigs.len() { + // agg = agg + sigs[i]; + // } + // + // AggregateSignature(Signature(agg)) + } + + pub fn verify<'a>( + &self, + msgs_and_pks: impl Iterator, + ) -> Result<(), Error> { + // Aggregate public keys if they are signing the same hash. Each public key aggregated + // is one fewer pairing to calculate. + let mut tree_map: BTreeMap<_, PublicKey> = BTreeMap::new(); + + for (msg, pk) in msgs_and_pks { + if let Some(existing_pk) = tree_map.get_mut(msg) { + let agg = PublicKey(existing_pk.0 + pk.0); + tree_map.insert(msg, agg); + } else { + tree_map.insert(msg, (*pk).clone()); + } + } + + for (msg, pk) in tree_map { + if let Err(err) = self.0.verify(msg, &pk) { + return Err(err); + } + } + + Ok(()) + } +} + +impl ByteFmt for AggregateSignature { + fn decode(bytes: &[u8]) -> anyhow::Result { + let signature = Signature::decode(bytes)?; + Ok(AggregateSignature(signature)) + } + + fn encode(&self) -> Vec { + Signature::encode(&self.0) + } +} + +impl PartialOrd for AggregateSignature { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for AggregateSignature { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + ByteFmt::encode(self).cmp(&ByteFmt::encode(other)) + } +} diff --git a/node/libs/crypto/src/bn254_2/testonly.rs b/node/libs/crypto/src/bn254_2/testonly.rs new file mode 100644 index 00000000..a516579e --- /dev/null +++ b/node/libs/crypto/src/bn254_2/testonly.rs @@ -0,0 +1,35 @@ +//! Random key generation, intended for use in testing + +use super::{AggregateSignature, ByteFmt, PublicKey, SecretKey, Signature}; +use rand::{distributions::Standard, prelude::Distribution, Rng}; + +/// Generates a random SecretKey. This is meant for testing purposes. +impl Distribution for Standard { + fn sample(&self, rng: &mut R) -> SecretKey { + SecretKey::decode(&rng.gen::<[u8; 32]>()).unwrap() + } +} + +/// Generates a random Signature. This is meant for testing purposes. +impl Distribution for Standard { + fn sample(&self, rng: &mut R) -> Signature { + let key = rng.gen::(); + let msg = rng.gen::<[u8; 4]>(); + key.sign(&msg) + } +} + +/// Generates a random AggregateSignature. This is meant for testing purposes. +impl Distribution for Standard { + fn sample(&self, rng: &mut R) -> AggregateSignature { + let sig: Signature = self.sample(rng); + AggregateSignature(sig) + } +} + +/// Generates a random PublicKey. This is meant for testing purposes. +impl Distribution for Standard { + fn sample(&self, rng: &mut R) -> PublicKey { + rng.gen::().public() + } +} diff --git a/node/libs/crypto/src/bn254_2/tests.rs b/node/libs/crypto/src/bn254_2/tests.rs new file mode 100644 index 00000000..c7b46f46 --- /dev/null +++ b/node/libs/crypto/src/bn254_2/tests.rs @@ -0,0 +1,91 @@ +use crate::bn254_2::{AggregateSignature, PublicKey, SecretKey, Signature}; +use rand::{rngs::StdRng, Rng, SeedableRng}; +use std::iter::repeat_with; + +#[test] +fn signature_smoke() { + let mut rng = StdRng::seed_from_u64(29483920); + let sk = SecretKey::random(&mut rng); + let pk = sk.public(); + + let msg: [u8; 32] = rng.gen(); + let sig = sk.sign(&msg); + + sig.verify(&msg, &pk).unwrap() +} + +#[test] +fn signature_failure_smoke() { + let mut rng = StdRng::seed_from_u64(29483920); + + let sk1 = SecretKey::random(&mut rng); + let sk2 = SecretKey::random(&mut rng); + let pk2 = sk2.public(); + let msg: [u8; 32] = rng.gen(); + let sig = sk1.sign(&msg); + + assert!(sig.verify(&msg, &pk2).is_err()) +} + +#[test] +fn aggregate_signature_smoke() { + let mut rng = StdRng::seed_from_u64(29483920); + + // Use an arbitrary 5 keys for the smoke test + let sks: Vec = repeat_with(|| SecretKey::random(&mut rng)) + .take(5) + .collect(); + let pks: Vec = sks.iter().map(|k| k.public()).collect(); + let msg: [u8; 32] = rng.gen(); + + let sigs: Vec = sks.iter().map(|k| k.sign(&msg)).collect(); + let agg_sig = AggregateSignature::aggregate(&sigs); + + agg_sig.verify(pks.iter().map(|pk| (&msg[..], pk))).unwrap() +} + +#[test] +fn aggregate_signature_failure_smoke() { + let mut rng = StdRng::seed_from_u64(29483920); + + // Use an arbitrary 5 keys for the smoke test + let sks: Vec = repeat_with(|| SecretKey::random(&mut rng)) + .take(5) + .collect(); + let pks: Vec = sks.iter().map(|k| k.public()).collect(); + let msg: [u8; 32] = rng.gen(); + + // Take only three signatures for the aggregate + let sigs: Vec = sks.iter().take(3).map(|k| k.sign(&msg)).collect(); + + let agg_sig = AggregateSignature::aggregate(&sigs); + + assert!(agg_sig.verify(pks.iter().map(|pk| (&msg[..], pk))).is_err()) +} + +#[test] +fn aggregate_signature_distinct_messages() { + let mut rng = StdRng::seed_from_u64(29483920); + let num_keys = 5; + let num_distinct = 2; + + // Use an arbitrary 5 keys for the smoke test + let sks: Vec = repeat_with(|| SecretKey::random(&mut rng)) + .take(num_keys) + .collect(); + let pks: Vec = sks.iter().map(|k| k.public()).collect(); + // Create 2 distinct messages + let msgs: Vec<[u8; 32]> = repeat_with(|| rng.gen()).take(num_distinct).collect(); + + let mut sigs: Vec = Vec::new(); + let mut pairs: Vec<(&[u8], &PublicKey)> = Vec::new(); + for (i, sk) in sks.iter().enumerate() { + let msg = &msgs[i % num_distinct]; + sigs.push(sk.sign(msg)); + pairs.push((msg, &pks[i])) + } + + let agg_sig = AggregateSignature::aggregate(&sigs); + + agg_sig.verify(pairs.into_iter()).unwrap() +} diff --git a/node/libs/crypto/src/lib.rs b/node/libs/crypto/src/lib.rs index d314b7bb..b80a9cdd 100644 --- a/node/libs/crypto/src/lib.rs +++ b/node/libs/crypto/src/lib.rs @@ -6,3 +6,4 @@ pub mod bn254; pub mod ed25519; mod fmt; pub mod sha256; +mod bn254_2; From 972b7d63ff4a03002bd33fca82269ac18661e943 Mon Sep 17 00:00:00 2001 From: moshababo Date: Fri, 27 Oct 2023 12:33:38 -0500 Subject: [PATCH 04/32] Don't use multi pairing on non-aggregated verify --- node/libs/crypto/src/bn254_2/mod.rs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/node/libs/crypto/src/bn254_2/mod.rs b/node/libs/crypto/src/bn254_2/mod.rs index a45ef744..661885d6 100644 --- a/node/libs/crypto/src/bn254_2/mod.rs +++ b/node/libs/crypto/src/bn254_2/mod.rs @@ -110,17 +110,12 @@ impl Signature { pub fn verify(&self, msg: &[u8], pk: &PublicKey) -> Result<(), Error> { let hash_point = hash::hash_to_g1(msg); - let mut vals = Vec::new(); // First pair: e(H(m): G1, pk: G2) - vals.push((hash_point, pk.0)); - // Second pair: e(sig: G1, -generator: G2) - vals.push((self.0, -G2::generator())); - - let output = multi_pairing(&vals); - // println!("{:?}", output); - // let a: PairingOutput = PairingOutput::zero(); - // println!("{:?}", a); - if output == PairingOutput::zero() { + let first = Bn254::pairing(hash_point, pk.0); + // Second pair: e(sig: G1, generator: G2) + let second = Bn254::pairing(self.0, G2::generator()); + + if first == second { Ok(()) } else { Err(Error::SignatureVerificationFailure) From 47b624f4e88511cdd678ea05efacc402113ddb8f Mon Sep 17 00:00:00 2001 From: moshababo Date: Sun, 29 Oct 2023 14:23:20 -0500 Subject: [PATCH 05/32] Implement signatures aggregation --- node/Cargo.lock | 1 + node/Cargo.toml | 1 + node/libs/crypto/Cargo.toml | 1 + node/libs/crypto/src/bn254/error.rs | 13 +- .../crypto/src/{bn254_2 => bn254}/hash.rs | 9 +- node/libs/crypto/src/bn254/mod.rs | 134 ++++++---- node/libs/crypto/src/bn254/testonly.rs | 21 +- node/libs/crypto/src/bn254/tests.rs | 64 ++--- node/libs/crypto/src/bn254_2/error.rs | 9 - node/libs/crypto/src/bn254_2/mod.rs | 233 ------------------ node/libs/crypto/src/bn254_2/testonly.rs | 35 --- node/libs/crypto/src/bn254_2/tests.rs | 91 ------- node/libs/crypto/src/lib.rs | 1 - .../roles/src/validator/keys/secret_key.rs | 2 +- node/libs/roles/src/validator/tests.rs | 10 +- node/tools/src/bin/keys.rs | 1 - 16 files changed, 141 insertions(+), 485 deletions(-) rename node/libs/crypto/src/{bn254_2 => bn254}/hash.rs (95%) delete mode 100644 node/libs/crypto/src/bn254_2/error.rs delete mode 100644 node/libs/crypto/src/bn254_2/mod.rs delete mode 100644 node/libs/crypto/src/bn254_2/testonly.rs delete mode 100644 node/libs/crypto/src/bn254_2/tests.rs diff --git a/node/Cargo.lock b/node/Cargo.lock index d7580e4a..438eaa49 100644 --- a/node/Cargo.lock +++ b/node/Cargo.lock @@ -594,6 +594,7 @@ dependencies = [ "anyhow", "ark-bn254", "ark-ec", + "ark-serialize", "blst", "bn254", "ed25519-dalek", diff --git a/node/Cargo.toml b/node/Cargo.toml index 9e745430..0c9d671b 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -29,6 +29,7 @@ blst = "0.3.10" bn254 = "0.0.1" ark-bn254 = "0.4.0" ark-ec = "0.4.2" +ark-serialize = "0.4.2" num-traits = "0.2.17" clap = { version = "4.3.3", features = ["derive"] } ed25519-dalek = { version = "2.0.0", features = ["serde", "rand_core"] } diff --git a/node/libs/crypto/Cargo.toml b/node/libs/crypto/Cargo.toml index ac842036..55c83920 100644 --- a/node/libs/crypto/Cargo.toml +++ b/node/libs/crypto/Cargo.toml @@ -12,6 +12,7 @@ blst.workspace = true bn254.workspace = true ark-bn254.workspace = true ark-ec.workspace = true +ark-serialize.workspace = true num-traits.workspace = true ed25519-dalek.workspace = true hex.workspace = true diff --git a/node/libs/crypto/src/bn254/error.rs b/node/libs/crypto/src/bn254/error.rs index a89268e9..5a91b019 100644 --- a/node/libs/crypto/src/bn254/error.rs +++ b/node/libs/crypto/src/bn254/error.rs @@ -1,12 +1,7 @@ #[derive(Debug, thiserror::Error)] pub enum Error { - /// Signature verification failure - #[error("Signature verification failure: {0:?}")] - SignatureVerification(bn254::Error), - /// Aggregate signature verification failure - #[error("Aggregate signature verification failure:")] - AggregateSignatureVerification, - /// Error aggregating signatures - #[error("Error aggregating signatures:")] - SignatureAggregation, + #[error("Signature verification failure")] + SignatureVerificationFailure, + #[error("Aggregate signature verification failure")] + AggregateSignatureVerificationFailure, } diff --git a/node/libs/crypto/src/bn254_2/hash.rs b/node/libs/crypto/src/bn254/hash.rs similarity index 95% rename from node/libs/crypto/src/bn254_2/hash.rs rename to node/libs/crypto/src/bn254/hash.rs index 2610a279..7aa9b399 100644 --- a/node/libs/crypto/src/bn254_2/hash.rs +++ b/node/libs/crypto/src/bn254/hash.rs @@ -1,6 +1,6 @@ -use sha2::Digest as _; -use ark_ec::AffineRepr as _; use ark_bn254::{G1Affine, G1Projective}; +use ark_ec::AffineRepr as _; +use sha2::Digest as _; /// H(m) as a point in G1. pub(crate) fn hash_to_g1(msg: &[u8]) -> G1Projective { @@ -16,10 +16,9 @@ pub(crate) fn hash_to_g1(msg: &[u8]) -> G1Projective { let p = G1Affine::from_random_bytes(&bytes); if let Some(p) = p { - return p.into() - + return p.into(); } } // It should be statistically infeasible to finish the loop without finding a point. unreachable!() -} \ No newline at end of file +} diff --git a/node/libs/crypto/src/bn254/mod.rs b/node/libs/crypto/src/bn254/mod.rs index 79e8f323..973c334b 100644 --- a/node/libs/crypto/src/bn254/mod.rs +++ b/node/libs/crypto/src/bn254/mod.rs @@ -1,58 +1,61 @@ -//! BLS signature scheme for BN254 curve. +//! BLS signature scheme for the BN254 curve. use std::collections::BTreeMap; +use std::fmt::Debug; +use std::hash::Hasher; use anyhow::anyhow; -use bn254::{ECDSA, PrivateKey}; -use rand::Rng; +use ark_bn254::{Bn254, Fr, G1Projective as G1, G2Projective as G2}; +use ark_ec::pairing::Pairing as _; +use ark_ec::pairing::PairingOutput; +use ark_ec::Group as _; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use num_traits::Zero as _; -pub use crate::bn254::error::Error; -use crate::bn254_2::hash; -use crate::ByteFmt; +pub use error::Error; -mod error; +use crate::ByteFmt; +pub mod error; +pub mod hash; mod testonly; #[cfg(test)] mod tests; -pub struct SecretKey(PrivateKey); +pub struct SecretKey(pub Fr); impl SecretKey { - /// Generates a random secret key - pub fn random(rng: &mut R) -> Self { - let private_key = PrivateKey::random(rng); - return Self(private_key); + /// Gets the corresponding [`PublicKey`] for this [`SecretKey`] + pub fn public(&self) -> PublicKey { + let p = G2::generator() * self.0; + PublicKey(p) } /// Produces a signature using this [`SecretKey`] pub fn sign(&self, msg: &[u8]) -> Signature { - let sig = ECDSA::sign(&msg, &self.0).unwrap(); + let hash_point = hash::hash_to_g1(msg); + let sig = hash_point * self.0; Signature(sig) } - - /// Gets the corresponding [`PublicKey`] for this [`SecretKey`] - pub fn public(&self) -> PublicKey { - let pk = bn254::PublicKey::from_private_key(&self.0); - PublicKey(pk) - } } impl ByteFmt for SecretKey { fn decode(bytes: &[u8]) -> anyhow::Result { - bn254::PrivateKey::try_from(bytes) + Fr::deserialize_compressed(bytes) .map(Self) - .map_err(|e| anyhow!("Failed to decode secret key: {e:?}")) + .map_err(|e| anyhow!("failed to decode secret key: {e:?}")) } fn encode(&self) -> Vec { - self.0.to_bytes().unwrap() + let mut buf = Vec::new(); + self.0.serialize_compressed(&mut buf).unwrap(); + buf } } /// Type safety wrapper around a `bn254` public key. #[derive(Clone)] -pub struct PublicKey(bn254::PublicKey); +pub struct PublicKey(pub G2); impl PartialEq for PublicKey { fn eq(&self, other: &Self) -> bool { @@ -64,19 +67,21 @@ impl Eq for PublicKey {} impl ByteFmt for PublicKey { fn decode(bytes: &[u8]) -> anyhow::Result { - bn254::PublicKey::from_compressed(bytes) + G2::deserialize_compressed(bytes) .map(Self) - .map_err(|err| anyhow!("Error decoding public key: {err:?}")) + .map_err(|e| anyhow!("failed to decode public key: {e:?}")) } fn encode(&self) -> Vec { - self.0.to_compressed().unwrap() + let mut buf = Vec::new(); + self.0.serialize_compressed(&mut buf).unwrap(); + buf } } impl std::hash::Hash for PublicKey { - fn hash(&self, state: &mut H) { - state.write(&self.0.to_compressed().unwrap()) + fn hash(&self, state: &mut H) { + self.0.hash(state); } } @@ -93,14 +98,21 @@ impl Ord for PublicKey { } #[derive(Clone, Debug)] -pub struct Signature(bn254::Signature); +pub struct Signature(pub G1); impl Signature { pub fn verify(&self, msg: &[u8], pk: &PublicKey) -> Result<(), Error> { - let result = ECDSA::verify(msg, &self.0, &pk.0); - match result { - Ok(()) => Ok(()), - Err(err) => Err(Error::SignatureVerification(err)), + let hash_point = hash::hash_to_g1(msg); + + // First pair: e(H(m): G1, pk: G2) + let a = Bn254::pairing(hash_point, pk.0); + // Second pair: e(sig: G1, generator: G2) + let b = Bn254::pairing(self.0, G2::generator()); + + if a == b { + Ok(()) + } else { + Err(Error::SignatureVerificationFailure) } } } @@ -115,12 +127,14 @@ impl Eq for Signature {} impl ByteFmt for Signature { fn decode(bytes: &[u8]) -> anyhow::Result { - bn254::Signature::from_compressed(bytes) + G1::deserialize_compressed(bytes) .map(Self) - .map_err(|err| anyhow!("Error decoding signature: {err:?}")) + .map_err(|e| anyhow!("failed to decode signature: {e:?}")) } fn encode(&self) -> Vec { - self.0.to_compressed().unwrap() + let mut buf = Vec::new(); + self.0.serialize_compressed(&mut buf).unwrap(); + buf } } @@ -141,10 +155,9 @@ pub struct AggregateSignature(Signature); impl AggregateSignature { pub fn aggregate<'a>(sigs: impl IntoIterator) -> Self { - let sigs: Vec = sigs.into_iter().map(|s| s.0).collect(); - let mut agg = sigs[0]; - for i in 1..sigs.len() { - agg = agg + sigs[i]; + let mut agg = G1::zero(); + for sig in sigs { + agg = agg + sig.0 } AggregateSignature(Signature(agg)) @@ -154,33 +167,48 @@ impl AggregateSignature { &self, msgs_and_pks: impl Iterator, ) -> Result<(), Error> { + let msgs_and_pks = Self::reduce(msgs_and_pks); + + // First pair: e(sig: G1, generator: G2) + let a = Bn254::pairing(self.0 .0, G2::generator()); + + // Second pair: e(H(m1): G1, pk1: G2) * ... * (H(m1000): G1, pk1000: G2) + let mut b = PairingOutput::zero(); + for (msg, pk) in msgs_and_pks { + let hash_point = hash::hash_to_g1(&msg); + b += Bn254::pairing(hash_point, pk.0); + } + + if a == b { + Ok(()) + } else { + Err(Error::AggregateSignatureVerificationFailure) + } + } + + fn reduce<'a>( + msgs_and_pks: impl Iterator, + ) -> impl Iterator { // Aggregate public keys if they are signing the same hash. Each public key aggregated // is one fewer pairing to calculate. - let mut tree_map: BTreeMap<_, PublicKey> = BTreeMap::new(); + let mut tree_map: BTreeMap<&[u8], PublicKey> = BTreeMap::new(); for (msg, pk) in msgs_and_pks { if let Some(existing_pk) = tree_map.get_mut(msg) { - let agg = PublicKey(existing_pk.0 + pk.0); - tree_map.insert(msg, agg); + existing_pk.0 += pk.0; } else { - tree_map.insert(msg, (*pk).clone()); - } - } - - for (msg, pk) in tree_map { - if let Err(err) = self.0.verify(msg, &pk) { - return Err(err); + tree_map.insert(msg, pk.clone()); } } - Ok(()) + tree_map.into_iter() } } impl ByteFmt for AggregateSignature { fn decode(bytes: &[u8]) -> anyhow::Result { - let signature = Signature::decode(bytes)?; - Ok(AggregateSignature(signature)) + let sig = Signature::decode(bytes)?; + Ok(AggregateSignature(sig)) } fn encode(&self) -> Vec { diff --git a/node/libs/crypto/src/bn254/testonly.rs b/node/libs/crypto/src/bn254/testonly.rs index a516579e..9f6b6296 100644 --- a/node/libs/crypto/src/bn254/testonly.rs +++ b/node/libs/crypto/src/bn254/testonly.rs @@ -1,12 +1,20 @@ //! Random key generation, intended for use in testing -use super::{AggregateSignature, ByteFmt, PublicKey, SecretKey, Signature}; +use super::{AggregateSignature, PublicKey, SecretKey, Signature}; use rand::{distributions::Standard, prelude::Distribution, Rng}; /// Generates a random SecretKey. This is meant for testing purposes. impl Distribution for Standard { fn sample(&self, rng: &mut R) -> SecretKey { - SecretKey::decode(&rng.gen::<[u8; 32]>()).unwrap() + let rand = ark_bn254::Fr::new(rng.gen()); + SecretKey(rand) + } +} + +/// Generates a random PublicKey. This is meant for testing purposes. +impl Distribution for Standard { + fn sample(&self, rng: &mut R) -> PublicKey { + rng.gen::().public() } } @@ -22,14 +30,7 @@ impl Distribution for Standard { /// Generates a random AggregateSignature. This is meant for testing purposes. impl Distribution for Standard { fn sample(&self, rng: &mut R) -> AggregateSignature { - let sig: Signature = self.sample(rng); + let sig = rng.gen(); AggregateSignature(sig) } } - -/// Generates a random PublicKey. This is meant for testing purposes. -impl Distribution for Standard { - fn sample(&self, rng: &mut R) -> PublicKey { - rng.gen::().public() - } -} diff --git a/node/libs/crypto/src/bn254/tests.rs b/node/libs/crypto/src/bn254/tests.rs index 273f7afb..d950d0c8 100644 --- a/node/libs/crypto/src/bn254/tests.rs +++ b/node/libs/crypto/src/bn254/tests.rs @@ -1,14 +1,14 @@ -use crate::bn254::{AggregateSignature, ByteFmt, PublicKey, SecretKey, Signature}; +use crate::bn254::{AggregateSignature, PublicKey, SecretKey, Signature}; use rand::{rngs::StdRng, Rng, SeedableRng}; use std::iter::repeat_with; #[test] fn signature_smoke() { let mut rng = StdRng::seed_from_u64(29483920); - let sk = SecretKey::decode(&rng.gen::<[u8; 32]>()).unwrap(); + let sk = rng.gen::(); let pk = sk.public(); - let msg: [u8; 32] = rng.gen(); + let msg = rng.gen::<[u8; 32]>(); let sig = sk.sign(&msg); sig.verify(&msg, &pk).unwrap() @@ -18,10 +18,10 @@ fn signature_smoke() { fn signature_failure_smoke() { let mut rng = StdRng::seed_from_u64(29483920); - let sk1 = SecretKey::decode(&rng.gen::<[u8; 32]>()).unwrap(); - let sk2 = SecretKey::decode(&rng.gen::<[u8; 32]>()).unwrap(); + let sk1 = rng.gen::(); + let sk2 = rng.gen::(); let pk2 = sk2.public(); - let msg: [u8; 32] = rng.gen(); + let msg = rng.gen::<[u8; 32]>(); let sig = sk1.sign(&msg); assert!(sig.verify(&msg, &pk2).is_err()) @@ -32,35 +32,16 @@ fn aggregate_signature_smoke() { let mut rng = StdRng::seed_from_u64(29483920); // Use an arbitrary 5 keys for the smoke test - let sks: Vec = repeat_with(|| SecretKey::random(&mut rng)) + let sks: Vec = repeat_with(|| rng.gen::()) .take(5) .collect(); let pks: Vec = sks.iter().map(|k| k.public()).collect(); - let msg: [u8; 32] = rng.gen(); + let msg = rng.gen::<[u8; 32]>(); let sigs: Vec = sks.iter().map(|k| k.sign(&msg)).collect(); - let agg_sig = AggregateSignature::aggregate(&sigs); + let agg = AggregateSignature::aggregate(&sigs); - agg_sig.verify(pks.iter().map(|pk| (&msg[..], pk))).unwrap() -} - -#[test] -fn aggregate_signature_failure_smoke() { - let mut rng = StdRng::seed_from_u64(29483920); - - // Use an arbitrary 5 keys for the smoke test - let sks: Vec = repeat_with(|| SecretKey::random(&mut rng)) - .take(5) - .collect(); - let pks: Vec = sks.iter().map(|k| k.public()).collect(); - let msg: [u8; 32] = rng.gen(); - - // Take only three signatures for the aggregate - let sigs: Vec = sks.iter().take(3).map(|k| k.sign(&msg)).collect(); - - let agg_sig = AggregateSignature::aggregate(&sigs); - - assert!(agg_sig.verify(pks.iter().map(|pk| (&msg[..], pk))).is_err()) + agg.verify(pks.iter().map(|pk| (&msg[..], pk))).unwrap() } #[test] @@ -70,7 +51,7 @@ fn aggregate_signature_distinct_messages() { let num_distinct = 2; // Use an arbitrary 5 keys for the smoke test - let sks: Vec = repeat_with(|| SecretKey::random(&mut rng)) + let sks: Vec = repeat_with(|| rng.gen::()) .take(num_keys) .collect(); let pks: Vec = sks.iter().map(|k| k.public()).collect(); @@ -85,7 +66,26 @@ fn aggregate_signature_distinct_messages() { pairs.push((msg, &pks[i])) } - let agg_sig = AggregateSignature::aggregate(&sigs); + let agg = AggregateSignature::aggregate(&sigs); + + agg.verify(pairs.into_iter()).unwrap() +} + +#[test] +fn aggregate_signature_failure_smoke() { + let mut rng = StdRng::seed_from_u64(29483920); + + // Use an arbitrary 5 keys for the smoke test + let sks: Vec = repeat_with(|| rng.gen::()) + .take(5) + .collect(); + let pks: Vec = sks.iter().map(|k| k.public()).collect(); + let msg = rng.gen::<[u8; 32]>(); + + // Take only three signatures for the aggregate + let sigs: Vec = sks.iter().take(3).map(|k| k.sign(&msg)).collect(); + + let agg = AggregateSignature::aggregate(&sigs); - agg_sig.verify(pairs.into_iter()).unwrap() + assert!(agg.verify(pks.iter().map(|pk| (&msg[..], pk))).is_err()) } diff --git a/node/libs/crypto/src/bn254_2/error.rs b/node/libs/crypto/src/bn254_2/error.rs deleted file mode 100644 index bb97bacd..00000000 --- a/node/libs/crypto/src/bn254_2/error.rs +++ /dev/null @@ -1,9 +0,0 @@ -#[derive(Debug, thiserror::Error)] -pub enum Error { - #[error("Signature verification failure")] - SignatureVerificationFailure, - #[error("Aggregate signature verification failure")] - AggregateSignatureVerificationFailure, - #[error("Signature aggregation failure")] - SignatureAggregationFailure, -} diff --git a/node/libs/crypto/src/bn254_2/mod.rs b/node/libs/crypto/src/bn254_2/mod.rs deleted file mode 100644 index 661885d6..00000000 --- a/node/libs/crypto/src/bn254_2/mod.rs +++ /dev/null @@ -1,233 +0,0 @@ -//! BLS signature scheme for BN254 curve. - -use std::collections::BTreeMap; - -use ark_bn254::{Bn254, Fr, G1Projective as G1, G2Projective as G2}; -use ark_ec::AffineRepr as _; -use ark_ec::bn::Bn; -use ark_ec::Group as _; -use ark_ec::pairing::Pairing as _; -use ark_ec::pairing::PairingOutput; -use num_traits::Zero as _; -use rand::Rng; - -pub use crate::bn254_2::error::Error; -use crate::ByteFmt; - -mod error; - -mod testonly; -#[cfg(test)] -mod tests; -pub mod hash; - -pub struct SecretKey(pub Fr); - -impl SecretKey { - /// Generates a random secret key - pub fn random(rng: &mut R) -> Self { - let scalar = Fr::new(rng.gen()); - SecretKey(scalar) - } - - /// Gets the corresponding [`PublicKey`] for this [`SecretKey`] - pub fn public(&self) -> PublicKey { - let p = G2::generator() * self.0; - PublicKey(p) - } - - /// Produces a signature using this [`SecretKey`] - pub fn sign(&self, msg: &[u8]) -> Signature { - let hash_point = hash::hash_to_g1(msg); - let sig = hash_point * self.0; - Signature(sig) - } -} - -impl ByteFmt for SecretKey { - fn decode(bytes: &[u8]) -> anyhow::Result { - panic!("implement"); - // bn254::PrivateKey::try_from(bytes) - // .map(Self) - // .map_err(|e| anyhow!("Failed to decode secret key: {e:?}")) - } - - fn encode(&self) -> Vec { - panic!("implement"); - // self.0.to_bytes().unwrap() - } -} - -/// Type safety wrapper around a `bn254` public key. -#[derive(Clone)] -pub struct PublicKey(pub G2); - -impl PartialEq for PublicKey { - fn eq(&self, other: &Self) -> bool { - ByteFmt::encode(self).eq(&ByteFmt::encode(other)) - } -} - -impl Eq for PublicKey {} - -impl ByteFmt for PublicKey { - fn decode(bytes: &[u8]) -> anyhow::Result { - panic!("implement"); - // bn254::PublicKey::from_compressed(bytes) - // .map(Self) - // .map_err(|err| anyhow!("Error decoding public key: {err:?}")) - } - - fn encode(&self) -> Vec { - panic!("implement"); - // self.0.to_compressed().unwrap() - } -} - -impl std::hash::Hash for PublicKey { - fn hash(&self, state: &mut H) { - panic!("implement"); - // state.write(&self.0.to_compressed().unwrap()) - } -} - -impl PartialOrd for PublicKey { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for PublicKey { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - ByteFmt::encode(self).cmp(&ByteFmt::encode(other)) - } -} - -#[derive(Clone, Debug)] -pub struct Signature(pub G1); - -impl Signature { - pub fn verify(&self, msg: &[u8], pk: &PublicKey) -> Result<(), Error> { - let hash_point = hash::hash_to_g1(msg); - - // First pair: e(H(m): G1, pk: G2) - let first = Bn254::pairing(hash_point, pk.0); - // Second pair: e(sig: G1, generator: G2) - let second = Bn254::pairing(self.0, G2::generator()); - - if first == second { - Ok(()) - } else { - Err(Error::SignatureVerificationFailure) - } - } -} - -fn multi_pairing(pairs: &[(G1, G2)]) -> PairingOutput { - let mut g1: Vec = Vec::new(); - let mut g2: Vec = Vec::new(); - for (p, q) in pairs { - g1.push(p.clone()); - g2.push(q.clone()); - } - - Bn254::multi_pairing(g1, g2) -} - -impl PartialEq for Signature { - fn eq(&self, other: &Self) -> bool { - ByteFmt::encode(self).eq(&ByteFmt::encode(other)) - } -} - -impl Eq for Signature {} - -impl ByteFmt for Signature { - fn decode(bytes: &[u8]) -> anyhow::Result { - panic!("implement"); - // bn254::Signature::from_compressed(bytes) - // .map(Self) - // .map_err(|err| anyhow!("Error decoding signature: {err:?}")) - } - fn encode(&self) -> Vec { - panic!() - // self.0.to_compressed().unwrap() - } -} - -impl PartialOrd for Signature { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for Signature { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - ByteFmt::encode(self).cmp(&ByteFmt::encode(other)) - } -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct AggregateSignature(Signature); - -impl AggregateSignature { - pub fn aggregate<'a>(sigs: impl IntoIterator) -> Self { - panic!("implement"); - // let sigs: Vec = sigs.into_iter().map(|s| s.0).collect(); - // let mut agg = sigs[0]; - // for i in 1..sigs.len() { - // agg = agg + sigs[i]; - // } - // - // AggregateSignature(Signature(agg)) - } - - pub fn verify<'a>( - &self, - msgs_and_pks: impl Iterator, - ) -> Result<(), Error> { - // Aggregate public keys if they are signing the same hash. Each public key aggregated - // is one fewer pairing to calculate. - let mut tree_map: BTreeMap<_, PublicKey> = BTreeMap::new(); - - for (msg, pk) in msgs_and_pks { - if let Some(existing_pk) = tree_map.get_mut(msg) { - let agg = PublicKey(existing_pk.0 + pk.0); - tree_map.insert(msg, agg); - } else { - tree_map.insert(msg, (*pk).clone()); - } - } - - for (msg, pk) in tree_map { - if let Err(err) = self.0.verify(msg, &pk) { - return Err(err); - } - } - - Ok(()) - } -} - -impl ByteFmt for AggregateSignature { - fn decode(bytes: &[u8]) -> anyhow::Result { - let signature = Signature::decode(bytes)?; - Ok(AggregateSignature(signature)) - } - - fn encode(&self) -> Vec { - Signature::encode(&self.0) - } -} - -impl PartialOrd for AggregateSignature { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for AggregateSignature { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - ByteFmt::encode(self).cmp(&ByteFmt::encode(other)) - } -} diff --git a/node/libs/crypto/src/bn254_2/testonly.rs b/node/libs/crypto/src/bn254_2/testonly.rs deleted file mode 100644 index a516579e..00000000 --- a/node/libs/crypto/src/bn254_2/testonly.rs +++ /dev/null @@ -1,35 +0,0 @@ -//! Random key generation, intended for use in testing - -use super::{AggregateSignature, ByteFmt, PublicKey, SecretKey, Signature}; -use rand::{distributions::Standard, prelude::Distribution, Rng}; - -/// Generates a random SecretKey. This is meant for testing purposes. -impl Distribution for Standard { - fn sample(&self, rng: &mut R) -> SecretKey { - SecretKey::decode(&rng.gen::<[u8; 32]>()).unwrap() - } -} - -/// Generates a random Signature. This is meant for testing purposes. -impl Distribution for Standard { - fn sample(&self, rng: &mut R) -> Signature { - let key = rng.gen::(); - let msg = rng.gen::<[u8; 4]>(); - key.sign(&msg) - } -} - -/// Generates a random AggregateSignature. This is meant for testing purposes. -impl Distribution for Standard { - fn sample(&self, rng: &mut R) -> AggregateSignature { - let sig: Signature = self.sample(rng); - AggregateSignature(sig) - } -} - -/// Generates a random PublicKey. This is meant for testing purposes. -impl Distribution for Standard { - fn sample(&self, rng: &mut R) -> PublicKey { - rng.gen::().public() - } -} diff --git a/node/libs/crypto/src/bn254_2/tests.rs b/node/libs/crypto/src/bn254_2/tests.rs deleted file mode 100644 index c7b46f46..00000000 --- a/node/libs/crypto/src/bn254_2/tests.rs +++ /dev/null @@ -1,91 +0,0 @@ -use crate::bn254_2::{AggregateSignature, PublicKey, SecretKey, Signature}; -use rand::{rngs::StdRng, Rng, SeedableRng}; -use std::iter::repeat_with; - -#[test] -fn signature_smoke() { - let mut rng = StdRng::seed_from_u64(29483920); - let sk = SecretKey::random(&mut rng); - let pk = sk.public(); - - let msg: [u8; 32] = rng.gen(); - let sig = sk.sign(&msg); - - sig.verify(&msg, &pk).unwrap() -} - -#[test] -fn signature_failure_smoke() { - let mut rng = StdRng::seed_from_u64(29483920); - - let sk1 = SecretKey::random(&mut rng); - let sk2 = SecretKey::random(&mut rng); - let pk2 = sk2.public(); - let msg: [u8; 32] = rng.gen(); - let sig = sk1.sign(&msg); - - assert!(sig.verify(&msg, &pk2).is_err()) -} - -#[test] -fn aggregate_signature_smoke() { - let mut rng = StdRng::seed_from_u64(29483920); - - // Use an arbitrary 5 keys for the smoke test - let sks: Vec = repeat_with(|| SecretKey::random(&mut rng)) - .take(5) - .collect(); - let pks: Vec = sks.iter().map(|k| k.public()).collect(); - let msg: [u8; 32] = rng.gen(); - - let sigs: Vec = sks.iter().map(|k| k.sign(&msg)).collect(); - let agg_sig = AggregateSignature::aggregate(&sigs); - - agg_sig.verify(pks.iter().map(|pk| (&msg[..], pk))).unwrap() -} - -#[test] -fn aggregate_signature_failure_smoke() { - let mut rng = StdRng::seed_from_u64(29483920); - - // Use an arbitrary 5 keys for the smoke test - let sks: Vec = repeat_with(|| SecretKey::random(&mut rng)) - .take(5) - .collect(); - let pks: Vec = sks.iter().map(|k| k.public()).collect(); - let msg: [u8; 32] = rng.gen(); - - // Take only three signatures for the aggregate - let sigs: Vec = sks.iter().take(3).map(|k| k.sign(&msg)).collect(); - - let agg_sig = AggregateSignature::aggregate(&sigs); - - assert!(agg_sig.verify(pks.iter().map(|pk| (&msg[..], pk))).is_err()) -} - -#[test] -fn aggregate_signature_distinct_messages() { - let mut rng = StdRng::seed_from_u64(29483920); - let num_keys = 5; - let num_distinct = 2; - - // Use an arbitrary 5 keys for the smoke test - let sks: Vec = repeat_with(|| SecretKey::random(&mut rng)) - .take(num_keys) - .collect(); - let pks: Vec = sks.iter().map(|k| k.public()).collect(); - // Create 2 distinct messages - let msgs: Vec<[u8; 32]> = repeat_with(|| rng.gen()).take(num_distinct).collect(); - - let mut sigs: Vec = Vec::new(); - let mut pairs: Vec<(&[u8], &PublicKey)> = Vec::new(); - for (i, sk) in sks.iter().enumerate() { - let msg = &msgs[i % num_distinct]; - sigs.push(sk.sign(msg)); - pairs.push((msg, &pks[i])) - } - - let agg_sig = AggregateSignature::aggregate(&sigs); - - agg_sig.verify(pairs.into_iter()).unwrap() -} diff --git a/node/libs/crypto/src/lib.rs b/node/libs/crypto/src/lib.rs index b80a9cdd..d314b7bb 100644 --- a/node/libs/crypto/src/lib.rs +++ b/node/libs/crypto/src/lib.rs @@ -6,4 +6,3 @@ pub mod bn254; pub mod ed25519; mod fmt; pub mod sha256; -mod bn254_2; diff --git a/node/libs/roles/src/validator/keys/secret_key.rs b/node/libs/roles/src/validator/keys/secret_key.rs index d16e3a55..4029ea23 100644 --- a/node/libs/roles/src/validator/keys/secret_key.rs +++ b/node/libs/roles/src/validator/keys/secret_key.rs @@ -14,7 +14,7 @@ pub struct SecretKey(pub(crate) Arc); impl SecretKey { /// Generate a new secret key. pub fn generate(rng: &mut R) -> Self { - Self(Arc::new(bn254::SecretKey::random(rng))) + Self(Arc::new(rng.gen())) } /// Public key corresponding to this secret key. diff --git a/node/libs/roles/src/validator/tests.rs b/node/libs/roles/src/validator/tests.rs index 7bc23605..1d39454c 100644 --- a/node/libs/roles/src/validator/tests.rs +++ b/node/libs/roles/src/validator/tests.rs @@ -114,8 +114,8 @@ fn test_signature_verify() { let msg1: MsgHash = rng.gen(); let msg2: MsgHash = rng.gen(); - let key1 = SecretKey::generate(rng.gen()); - let key2 = SecretKey::generate(rng.gen()); + let key1 = SecretKey::generate(rng); + let key2 = SecretKey::generate(rng); let sig1 = key1.sign_hash(&msg1); @@ -137,13 +137,13 @@ fn test_agg_signature_verify() { let msg1: MsgHash = rng.gen(); let msg2: MsgHash = rng.gen(); - let key1 = SecretKey::generate(rng.gen()); - let key2 = SecretKey::generate(rng.gen()); + let key1 = SecretKey::generate(rng); + let key2 = SecretKey::generate(rng); let sig1 = key1.sign_hash(&msg1); let sig2 = key2.sign_hash(&msg2); - let agg_sig = AggregateSignature::aggregate(vec![&sig1, &sig2]).unwrap(); + let agg_sig = AggregateSignature::aggregate(vec![&sig1, &sig2]); // Matching key and message. assert!(agg_sig diff --git a/node/tools/src/bin/keys.rs b/node/tools/src/bin/keys.rs index 25d08aaf..63d5ec9e 100644 --- a/node/tools/src/bin/keys.rs +++ b/node/tools/src/bin/keys.rs @@ -1,7 +1,6 @@ //! This tool generates a validator key pair and prints it to stdout. #![allow(clippy::print_stdout)] -use rand::Rng; use roles::validator; /// This tool generates a validator key pair and prints it to stdout. From 1cfbeb36925a087d09f50fbb58b845c892d6c208 Mon Sep 17 00:00:00 2001 From: moshababo Date: Sun, 29 Oct 2023 14:55:49 -0500 Subject: [PATCH 06/32] Post-merge fixes --- node/actors/consensus/src/leader/replica_commit.rs | 2 +- node/actors/consensus/src/leader/replica_prepare.rs | 2 +- node/actors/consensus/src/replica/leader_commit.rs | 2 +- node/actors/consensus/src/replica/leader_prepare.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/node/actors/consensus/src/leader/replica_commit.rs b/node/actors/consensus/src/leader/replica_commit.rs index 870da2b5..126cf529 100644 --- a/node/actors/consensus/src/leader/replica_commit.rs +++ b/node/actors/consensus/src/leader/replica_commit.rs @@ -41,7 +41,7 @@ pub(crate) enum Error { }, /// Invalid message signature. #[error("invalid signature: {0:#}")] - InvalidSignature(#[source] crypto::bls12_381::Error), + InvalidSignature(#[source] crypto::bn254::Error), } impl StateMachine { diff --git a/node/actors/consensus/src/leader/replica_prepare.rs b/node/actors/consensus/src/leader/replica_prepare.rs index 67519427..4c0ef691 100644 --- a/node/actors/consensus/src/leader/replica_prepare.rs +++ b/node/actors/consensus/src/leader/replica_prepare.rs @@ -50,7 +50,7 @@ pub(crate) enum Error { }, /// Invalid message signature. #[error("invalid signature: {0:#}")] - InvalidSignature(#[source] crypto::bls12_381::Error), + InvalidSignature(#[source] crypto::bn254::Error), /// Invalid `HighQC` message. #[error("invalid high QC: {0:#}")] InvalidHighQC(#[source] anyhow::Error), diff --git a/node/actors/consensus/src/replica/leader_commit.rs b/node/actors/consensus/src/replica/leader_commit.rs index bc73a82b..6fe4952e 100644 --- a/node/actors/consensus/src/replica/leader_commit.rs +++ b/node/actors/consensus/src/replica/leader_commit.rs @@ -28,7 +28,7 @@ pub(crate) enum Error { }, /// Invalid message signature. #[error("invalid signature: {0:#}")] - InvalidSignature(#[source] crypto::bls12_381::Error), + InvalidSignature(#[source] crypto::bn254::Error), /// Invalid justification for the message. #[error("invalid justification: {0:#}")] InvalidJustification(#[source] anyhow::Error), diff --git a/node/actors/consensus/src/replica/leader_prepare.rs b/node/actors/consensus/src/replica/leader_prepare.rs index ec2c3b8e..2c7ed3e8 100644 --- a/node/actors/consensus/src/replica/leader_prepare.rs +++ b/node/actors/consensus/src/replica/leader_prepare.rs @@ -32,7 +32,7 @@ pub(crate) enum Error { }, /// Invalid message signature. #[error("invalid signature: {0:#}")] - InvalidSignature(#[source] crypto::bls12_381::Error), + InvalidSignature(#[source] crypto::bn254::Error), /// Invalid `PrepareQC` message. #[error("invalid PrepareQC: {0:#}")] InvalidPrepareQC(#[source] anyhow::Error), From e9f04e854841bf80364bfa03bc813d9f01d69b99 Mon Sep 17 00:00:00 2001 From: moshababo Date: Sun, 29 Oct 2023 17:23:02 -0500 Subject: [PATCH 07/32] Downgrade multiple-versions "deny" to "warn" --- node/deny.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/deny.toml b/node/deny.toml index dd10be9e..8df25cfe 100644 --- a/node/deny.toml +++ b/node/deny.toml @@ -44,7 +44,7 @@ allow = [ [bans] # Lint level for when multiple versions of the same crate are detected -multiple-versions = "deny" +multiple-versions = "warn" # Certain crates/versions that will be skipped when doing duplicate detection. skip = [ # Old versions required by prost. From cbb9737ae14e37f9bef1635a2217e5ec2972df29 Mon Sep 17 00:00:00 2001 From: moshababo Date: Sun, 29 Oct 2023 17:24:32 -0500 Subject: [PATCH 08/32] cargo fmt --- node/libs/crypto/src/bn254/tests.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/node/libs/crypto/src/bn254/tests.rs b/node/libs/crypto/src/bn254/tests.rs index d950d0c8..d07afbfa 100644 --- a/node/libs/crypto/src/bn254/tests.rs +++ b/node/libs/crypto/src/bn254/tests.rs @@ -32,9 +32,7 @@ fn aggregate_signature_smoke() { let mut rng = StdRng::seed_from_u64(29483920); // Use an arbitrary 5 keys for the smoke test - let sks: Vec = repeat_with(|| rng.gen::()) - .take(5) - .collect(); + let sks: Vec = repeat_with(|| rng.gen::()).take(5).collect(); let pks: Vec = sks.iter().map(|k| k.public()).collect(); let msg = rng.gen::<[u8; 32]>(); @@ -76,9 +74,7 @@ fn aggregate_signature_failure_smoke() { let mut rng = StdRng::seed_from_u64(29483920); // Use an arbitrary 5 keys for the smoke test - let sks: Vec = repeat_with(|| rng.gen::()) - .take(5) - .collect(); + let sks: Vec = repeat_with(|| rng.gen::()).take(5).collect(); let pks: Vec = sks.iter().map(|k| k.public()).collect(); let msg = rng.gen::<[u8; 32]>(); From 540c4f2f202e90f3cc9e20a825c8a3fa123e796d Mon Sep 17 00:00:00 2001 From: moshababo Date: Sun, 29 Oct 2023 17:45:47 -0500 Subject: [PATCH 09/32] cargo cranky --- node/libs/crypto/src/bn254/error.rs | 2 ++ node/libs/crypto/src/bn254/hash.rs | 4 +++- node/libs/crypto/src/bn254/mod.rs | 26 ++++++++++++++++++-------- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/node/libs/crypto/src/bn254/error.rs b/node/libs/crypto/src/bn254/error.rs index 5a91b019..985ae8a0 100644 --- a/node/libs/crypto/src/bn254/error.rs +++ b/node/libs/crypto/src/bn254/error.rs @@ -1,4 +1,6 @@ +/// Error type for generating and interacting with bn254. #[derive(Debug, thiserror::Error)] +#[non_exhaustive] pub enum Error { #[error("Signature verification failure")] SignatureVerificationFailure, diff --git a/node/libs/crypto/src/bn254/hash.rs b/node/libs/crypto/src/bn254/hash.rs index 7aa9b399..9940a4ef 100644 --- a/node/libs/crypto/src/bn254/hash.rs +++ b/node/libs/crypto/src/bn254/hash.rs @@ -1,3 +1,5 @@ +//! Hash operations. + use ark_bn254::{G1Affine, G1Projective}; use ark_ec::AffineRepr as _; use sha2::Digest as _; @@ -7,7 +9,7 @@ pub(crate) fn hash_to_g1(msg: &[u8]) -> G1Projective { for i in 0..100 { // Hash the message with the index as suffix. let bytes: [u8; 32] = sha2::Sha256::new() - .chain_update(&msg) + .chain_update(msg) .chain_update((i as u32).to_be_bytes()) .finalize() .into(); diff --git a/node/libs/crypto/src/bn254/mod.rs b/node/libs/crypto/src/bn254/mod.rs index 973c334b..15d76638 100644 --- a/node/libs/crypto/src/bn254/mod.rs +++ b/node/libs/crypto/src/bn254/mod.rs @@ -16,12 +16,16 @@ pub use error::Error; use crate::ByteFmt; +#[doc(hidden)] pub mod error; -pub mod hash; -mod testonly; + #[cfg(test)] mod tests; +pub mod hash; +mod testonly; + +/// Type safety wrapper around a scalar value. pub struct SecretKey(pub Fr); impl SecretKey { @@ -53,7 +57,7 @@ impl ByteFmt for SecretKey { } } -/// Type safety wrapper around a `bn254` public key. +/// Type safety wrapper around G2. #[derive(Clone)] pub struct PublicKey(pub G2); @@ -97,10 +101,12 @@ impl Ord for PublicKey { } } +/// Type safety wrapper around a G1 value. #[derive(Clone, Debug)] pub struct Signature(pub G1); impl Signature { + /// Verifies a signature against the provided public key pub fn verify(&self, msg: &[u8], pk: &PublicKey) -> Result<(), Error> { let hash_point = hash::hash_to_g1(msg); @@ -149,25 +155,29 @@ impl Ord for Signature { ByteFmt::encode(self).cmp(&ByteFmt::encode(other)) } } - +/// Type safety wrapper around [Signature] indicating that it is an aggregated signature. #[derive(Clone, Debug, PartialEq, Eq)] pub struct AggregateSignature(Signature); impl AggregateSignature { + /// Generates an aggregate signature from a list of signatures. pub fn aggregate<'a>(sigs: impl IntoIterator) -> Self { let mut agg = G1::zero(); for sig in sigs { - agg = agg + sig.0 + agg += sig.0 } AggregateSignature(Signature(agg)) } + /// Verifies an aggregated signature for multiple messages against the provided list of public keys. + /// This method expects one public key per message, otherwise it will fail. Note however that + /// If there are any duplicate messages, the public keys will be aggregated before verification. pub fn verify<'a>( &self, msgs_and_pks: impl Iterator, ) -> Result<(), Error> { - let msgs_and_pks = Self::reduce(msgs_and_pks); + let msgs_and_pks = Self::aggregate_pk(msgs_and_pks); // First pair: e(sig: G1, generator: G2) let a = Bn254::pairing(self.0 .0, G2::generator()); @@ -175,7 +185,7 @@ impl AggregateSignature { // Second pair: e(H(m1): G1, pk1: G2) * ... * (H(m1000): G1, pk1000: G2) let mut b = PairingOutput::zero(); for (msg, pk) in msgs_and_pks { - let hash_point = hash::hash_to_g1(&msg); + let hash_point = hash::hash_to_g1(msg); b += Bn254::pairing(hash_point, pk.0); } @@ -186,7 +196,7 @@ impl AggregateSignature { } } - fn reduce<'a>( + fn aggregate_pk<'a>( msgs_and_pks: impl Iterator, ) -> impl Iterator { // Aggregate public keys if they are signing the same hash. Each public key aggregated From a462c6a9805fa0daa2c3bf3028d0e35cf82c1fde Mon Sep 17 00:00:00 2001 From: Moshe Shababo Date: Mon, 30 Oct 2023 16:11:01 -0500 Subject: [PATCH 10/32] Update node/Cargo.toml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bruno França --- node/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/node/Cargo.toml b/node/Cargo.toml index 0c9d671b..7e7f8a23 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -26,7 +26,6 @@ assert_matches = "1.5.0" async-trait = "0.1.71" bit-vec = "0.6" blst = "0.3.10" -bn254 = "0.0.1" ark-bn254 = "0.4.0" ark-ec = "0.4.2" ark-serialize = "0.4.2" From 59602fdeb91b2744008ea856533de7bc38350a67 Mon Sep 17 00:00:00 2001 From: Moshe Shababo <17073733+moshababo@users.noreply.github.com> Date: Mon, 30 Oct 2023 17:58:39 -0500 Subject: [PATCH 11/32] Skip hashbrown version discrepancy --- node/deny.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/node/deny.toml b/node/deny.toml index 8df25cfe..6fdd9bb5 100644 --- a/node/deny.toml +++ b/node/deny.toml @@ -44,7 +44,7 @@ allow = [ [bans] # Lint level for when multiple versions of the same crate are detected -multiple-versions = "warn" +multiple-versions = "deny" # Certain crates/versions that will be skipped when doing duplicate detection. skip = [ # Old versions required by prost. @@ -60,6 +60,7 @@ skip = [ # Old versions required by hyper. { name = "socket2", version = "=0.4.9" }, + { name = "hashbrown", version = "=0.12.3" }, # (hyper -> h2 -> indexmap -> hashbrown) ] [sources] From 09dc83e8825f4007b8c38cf5f450a7b4a5d6e28b Mon Sep 17 00:00:00 2001 From: Moshe Shababo <17073733+moshababo@users.noreply.github.com> Date: Mon, 30 Oct 2023 18:05:38 -0500 Subject: [PATCH 12/32] Finalize removal of bn254 crate dep --- node/Cargo.lock | 48 ------------------------------------- node/libs/crypto/Cargo.toml | 1 - 2 files changed, 49 deletions(-) diff --git a/node/Cargo.lock b/node/Cargo.lock index c1e2b0a0..57ac2955 100644 --- a/node/Cargo.lock +++ b/node/Cargo.lock @@ -373,19 +373,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "bn254" -version = "0.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694a2df5ffc5f2e385503b2987d281abeb80e02f3c30acd7de0d3db18794e73" -dependencies = [ - "byteorder", - "rand", - "sha2", - "substrate-bn", - "thiserror", -] - [[package]] name = "byteorder" version = "1.4.3" @@ -581,12 +568,6 @@ dependencies = [ "libc", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto" version = "0.1.0" @@ -596,7 +577,6 @@ dependencies = [ "ark-ec", "ark-serialize", "blst", - "bn254", "ed25519-dalek", "hex", "num-traits", @@ -1119,9 +1099,6 @@ name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -dependencies = [ - "spin", -] [[package]] name = "lazycell" @@ -1870,12 +1847,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" -[[package]] -name = "rustc-hex" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" - [[package]] name = "rustc_version" version = "0.4.0" @@ -2081,12 +2052,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - [[package]] name = "spki" version = "0.7.2" @@ -2128,19 +2093,6 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" -[[package]] -name = "substrate-bn" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b5bbfa79abbae15dd642ea8176a21a635ff3c00059961d1ea27ad04e5b441c" -dependencies = [ - "byteorder", - "crunchy", - "lazy_static", - "rand", - "rustc-hex", -] - [[package]] name = "subtle" version = "2.5.0" diff --git a/node/libs/crypto/Cargo.toml b/node/libs/crypto/Cargo.toml index 55c83920..8dcf402e 100644 --- a/node/libs/crypto/Cargo.toml +++ b/node/libs/crypto/Cargo.toml @@ -9,7 +9,6 @@ license.workspace = true [dependencies] anyhow.workspace = true blst.workspace = true -bn254.workspace = true ark-bn254.workspace = true ark-ec.workspace = true ark-serialize.workspace = true From 8e7012778a5b8ce8cf36c4ec7ed506f8921ed914 Mon Sep 17 00:00:00 2001 From: Moshe Shababo <17073733+moshababo@users.noreply.github.com> Date: Mon, 30 Oct 2023 18:09:49 -0500 Subject: [PATCH 13/32] Update node/libs/crypto/src/bn254/hash.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bruno França --- node/libs/crypto/src/bn254/hash.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/libs/crypto/src/bn254/hash.rs b/node/libs/crypto/src/bn254/hash.rs index 9940a4ef..44cba17c 100644 --- a/node/libs/crypto/src/bn254/hash.rs +++ b/node/libs/crypto/src/bn254/hash.rs @@ -4,7 +4,7 @@ use ark_bn254::{G1Affine, G1Projective}; use ark_ec::AffineRepr as _; use sha2::Digest as _; -/// H(m) as a point in G1. +/// Hashes an arbitrary message and maps it to an elliptic curve point in G1. pub(crate) fn hash_to_g1(msg: &[u8]) -> G1Projective { for i in 0..100 { // Hash the message with the index as suffix. From 6127656bab34006232609d698285625fe609d84d Mon Sep 17 00:00:00 2001 From: Moshe Shababo <17073733+moshababo@users.noreply.github.com> Date: Mon, 30 Oct 2023 18:12:48 -0500 Subject: [PATCH 14/32] Update node/libs/crypto/src/bn254/hash.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bruno França --- node/libs/crypto/src/bn254/hash.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/libs/crypto/src/bn254/hash.rs b/node/libs/crypto/src/bn254/hash.rs index 44cba17c..8540ee58 100644 --- a/node/libs/crypto/src/bn254/hash.rs +++ b/node/libs/crypto/src/bn254/hash.rs @@ -14,7 +14,7 @@ pub(crate) fn hash_to_g1(msg: &[u8]) -> G1Projective { .finalize() .into(); - // Try to get a G1 point from the hash. + // Try to get a G1 point from the hash. The probability that this works is around 1/8. let p = G1Affine::from_random_bytes(&bytes); if let Some(p) = p { From ba5823f4393dde70a505ce543b25993a52734b4f Mon Sep 17 00:00:00 2001 From: Moshe Shababo <17073733+moshababo@users.noreply.github.com> Date: Mon, 30 Oct 2023 18:41:26 -0500 Subject: [PATCH 15/32] Derive PartialEq, Eq --- node/libs/crypto/src/bn254/mod.rs | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/node/libs/crypto/src/bn254/mod.rs b/node/libs/crypto/src/bn254/mod.rs index 15d76638..23458f82 100644 --- a/node/libs/crypto/src/bn254/mod.rs +++ b/node/libs/crypto/src/bn254/mod.rs @@ -58,17 +58,9 @@ impl ByteFmt for SecretKey { } /// Type safety wrapper around G2. -#[derive(Clone)] +#[derive(Clone, PartialEq, Eq)] pub struct PublicKey(pub G2); -impl PartialEq for PublicKey { - fn eq(&self, other: &Self) -> bool { - ByteFmt::encode(self).eq(&ByteFmt::encode(other)) - } -} - -impl Eq for PublicKey {} - impl ByteFmt for PublicKey { fn decode(bytes: &[u8]) -> anyhow::Result { G2::deserialize_compressed(bytes) @@ -102,7 +94,7 @@ impl Ord for PublicKey { } /// Type safety wrapper around a G1 value. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct Signature(pub G1); impl Signature { @@ -123,14 +115,6 @@ impl Signature { } } -impl PartialEq for Signature { - fn eq(&self, other: &Self) -> bool { - ByteFmt::encode(self).eq(&ByteFmt::encode(other)) - } -} - -impl Eq for Signature {} - impl ByteFmt for Signature { fn decode(bytes: &[u8]) -> anyhow::Result { G1::deserialize_compressed(bytes) From d262d818de16203556cac3512624c37f45912da4 Mon Sep 17 00:00:00 2001 From: Moshe Shababo <17073733+moshababo@users.noreply.github.com> Date: Mon, 30 Oct 2023 18:42:03 -0500 Subject: [PATCH 16/32] AggregateSignature to store G1 directly --- node/libs/crypto/src/bn254/mod.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/node/libs/crypto/src/bn254/mod.rs b/node/libs/crypto/src/bn254/mod.rs index 23458f82..0aff49fe 100644 --- a/node/libs/crypto/src/bn254/mod.rs +++ b/node/libs/crypto/src/bn254/mod.rs @@ -141,7 +141,7 @@ impl Ord for Signature { } /// Type safety wrapper around [Signature] indicating that it is an aggregated signature. #[derive(Clone, Debug, PartialEq, Eq)] -pub struct AggregateSignature(Signature); +pub struct AggregateSignature(pub G1); impl AggregateSignature { /// Generates an aggregate signature from a list of signatures. @@ -151,7 +151,7 @@ impl AggregateSignature { agg += sig.0 } - AggregateSignature(Signature(agg)) + AggregateSignature(agg) } /// Verifies an aggregated signature for multiple messages against the provided list of public keys. @@ -164,7 +164,7 @@ impl AggregateSignature { let msgs_and_pks = Self::aggregate_pk(msgs_and_pks); // First pair: e(sig: G1, generator: G2) - let a = Bn254::pairing(self.0 .0, G2::generator()); + let a = Bn254::pairing(self.0, G2::generator()); // Second pair: e(H(m1): G1, pk1: G2) * ... * (H(m1000): G1, pk1000: G2) let mut b = PairingOutput::zero(); @@ -201,12 +201,15 @@ impl AggregateSignature { impl ByteFmt for AggregateSignature { fn decode(bytes: &[u8]) -> anyhow::Result { - let sig = Signature::decode(bytes)?; - Ok(AggregateSignature(sig)) + G1::deserialize_compressed(bytes) + .map(Self) + .map_err(|e| anyhow!("failed to decode aggregate signature: {e:?}")) } fn encode(&self) -> Vec { - Signature::encode(&self.0) + let mut buf = Vec::new(); + self.0.serialize_compressed(&mut buf).unwrap(); + buf } } From 2345e62f33599d608f61991cce71792a30998ad0 Mon Sep 17 00:00:00 2001 From: Moshe Shababo <17073733+moshababo@users.noreply.github.com> Date: Mon, 30 Oct 2023 19:44:59 -0500 Subject: [PATCH 17/32] Generate random G1/G2 points directly --- node/libs/crypto/src/bn254/testonly.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/node/libs/crypto/src/bn254/testonly.rs b/node/libs/crypto/src/bn254/testonly.rs index 9f6b6296..9304b530 100644 --- a/node/libs/crypto/src/bn254/testonly.rs +++ b/node/libs/crypto/src/bn254/testonly.rs @@ -1,8 +1,9 @@ //! Random key generation, intended for use in testing -use super::{AggregateSignature, PublicKey, SecretKey, Signature}; use rand::{distributions::Standard, prelude::Distribution, Rng}; +use super::{AggregateSignature, PublicKey, SecretKey, Signature}; + /// Generates a random SecretKey. This is meant for testing purposes. impl Distribution for Standard { fn sample(&self, rng: &mut R) -> SecretKey { @@ -14,23 +15,20 @@ impl Distribution for Standard { /// Generates a random PublicKey. This is meant for testing purposes. impl Distribution for Standard { fn sample(&self, rng: &mut R) -> PublicKey { - rng.gen::().public() + rng.gen() } } /// Generates a random Signature. This is meant for testing purposes. impl Distribution for Standard { fn sample(&self, rng: &mut R) -> Signature { - let key = rng.gen::(); - let msg = rng.gen::<[u8; 4]>(); - key.sign(&msg) + rng.gen() } } /// Generates a random AggregateSignature. This is meant for testing purposes. impl Distribution for Standard { fn sample(&self, rng: &mut R) -> AggregateSignature { - let sig = rng.gen(); - AggregateSignature(sig) + rng.gen() } } From cf0a547c7bbe9ce6dba0970a87f194fe048e868f Mon Sep 17 00:00:00 2001 From: Moshe Shababo <17073733+moshababo@users.noreply.github.com> Date: Mon, 30 Oct 2023 19:51:12 -0500 Subject: [PATCH 18/32] Undo bls12_381 mod removal --- node/libs/crypto/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/node/libs/crypto/src/lib.rs b/node/libs/crypto/src/lib.rs index d314b7bb..5c22086c 100644 --- a/node/libs/crypto/src/lib.rs +++ b/node/libs/crypto/src/lib.rs @@ -2,6 +2,9 @@ pub use fmt::*; +/// Currently replaced by [bn254] and unused. +pub mod bls12_381; + pub mod bn254; pub mod ed25519; mod fmt; From d7c8bb2e557f3fc748f1ec28b06787ace0de862f Mon Sep 17 00:00:00 2001 From: Moshe Shababo <17073733+moshababo@users.noreply.github.com> Date: Mon, 30 Oct 2023 22:43:08 -0500 Subject: [PATCH 19/32] Add benches --- node/Cargo.lock | 295 ++++++++++++++++++++++++++++++ node/libs/crypto/Cargo.toml | 7 + node/libs/crypto/benches/bench.rs | 48 +++++ 3 files changed, 350 insertions(+) create mode 100644 node/libs/crypto/benches/bench.rs diff --git a/node/Cargo.lock b/node/Cargo.lock index 271ab245..400544aa 100644 --- a/node/Cargo.lock +++ b/node/Cargo.lock @@ -73,6 +73,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + [[package]] name = "anstream" version = "0.3.2" @@ -373,6 +379,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + [[package]] name = "byteorder" version = "1.4.3" @@ -396,6 +408,12 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + [[package]] name = "cc" version = "1.0.82" @@ -446,6 +464,33 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ciborium" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656" + +[[package]] +name = "ciborium-ll" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b" +dependencies = [ + "ciborium-io", + "half", +] + [[package]] name = "cipher" version = "0.3.0" @@ -566,6 +611,75 @@ dependencies = [ "libc", ] +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "is-terminal", + "itertools", + "num-traits", + "once_cell", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + [[package]] name = "crypto" version = "0.1.0" @@ -575,6 +689,7 @@ dependencies = [ "ark-ec", "ark-serialize", "blst", + "criterion", "ed25519-dalek", "hex", "num-traits", @@ -918,6 +1033,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "half" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" + [[package]] name = "hashbrown" version = "0.12.3" @@ -1067,6 +1188,15 @@ dependencies = [ "libc", ] +[[package]] +name = "js-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -1189,6 +1319,15 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1321,6 +1460,12 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + [[package]] name = "opaque-debug" version = "0.3.0" @@ -1441,6 +1586,34 @@ version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3d7ddaed09e0eb771a79ab0fd64609ba0afb0a8366421957936ad14cbd13630" +[[package]] +name = "plotters" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" + +[[package]] +name = "plotters-svg" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" +dependencies = [ + "plotters-backend", +] + [[package]] name = "poly1305" version = "0.7.2" @@ -1729,6 +1902,26 @@ dependencies = [ "rand_core", ] +[[package]] +name = "rayon" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "redox_syscall" version = "0.3.5" @@ -1848,6 +2041,15 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "schema" version = "0.1.0" @@ -2203,6 +2405,16 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "tokio" version = "1.32.0" @@ -2416,6 +2628,16 @@ dependencies = [ "syn 2.0.32", ] +[[package]] +name = "walkdir" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "want" version = "0.3.1" @@ -2431,6 +2653,70 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.32", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.32", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + +[[package]] +name = "web-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "which" version = "4.4.0" @@ -2458,6 +2744,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/node/libs/crypto/Cargo.toml b/node/libs/crypto/Cargo.toml index 8dcf402e..6a827f90 100644 --- a/node/libs/crypto/Cargo.toml +++ b/node/libs/crypto/Cargo.toml @@ -18,3 +18,10 @@ hex.workspace = true rand.workspace = true sha2.workspace = true thiserror.workspace = true + +[dev-dependencies] +criterion = "0.5.1" + +[[bench]] +name = "bench" +harness = false \ No newline at end of file diff --git a/node/libs/crypto/benches/bench.rs b/node/libs/crypto/benches/bench.rs new file mode 100644 index 00000000..99d6c477 --- /dev/null +++ b/node/libs/crypto/benches/bench.rs @@ -0,0 +1,48 @@ +#![allow(clippy::missing_docs_in_private_items)] +#![allow(missing_docs)] + +extern crate crypto; + +use criterion::{criterion_group, criterion_main, Criterion}; +use rand::Rng; +use std::iter::repeat_with; + +fn bench_bn254(c: &mut Criterion) { + use crypto::bn254::{AggregateSignature, PublicKey, SecretKey, Signature}; + let mut rng = rand::thread_rng(); + let mut group = c.benchmark_group("bn254"); + group.bench_function("100 sig aggregation", |b| { + b.iter(|| { + let sks: Vec = repeat_with(|| rng.gen::()).take(100).collect(); + let pks: Vec = sks.iter().map(|k| k.public()).collect(); + let msg = rng.gen::<[u8; 32]>(); + let sigs: Vec = sks.iter().map(|k| k.sign(&msg)).collect(); + let agg = AggregateSignature::aggregate(&sigs); + agg.verify(pks.iter().map(|pk| (&msg[..], pk))).unwrap() + }); + }); + + group.finish(); +} + +#[allow(missing_docs)] +fn bench_bls12_381(c: &mut Criterion) { + use crypto::bls12_381::{AggregateSignature, PublicKey, SecretKey, Signature}; + let mut rng = rand::thread_rng(); + let mut group = c.benchmark_group("bls12_381"); + group.bench_function("100 sig aggregation", |b| { + b.iter(|| { + let sks: Vec = repeat_with(|| rng.gen::()).take(100).collect(); + let pks: Vec = sks.iter().map(|k| k.public()).collect(); + let msg = rng.gen::<[u8; 32]>(); + let sigs: Vec = sks.iter().map(|k| k.sign(&msg)).collect(); + let agg = AggregateSignature::aggregate(&sigs)?; + agg.verify(pks.iter().map(|pk| (&msg[..], pk))) + }); + }); + + group.finish(); +} + +criterion_group!(benches, bench_bls12_381, bench_bn254); +criterion_main!(benches); From e23724f89b19f2fc0631da151f8275cfd1242f3f Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Tue, 31 Oct 2023 11:26:36 +0100 Subject: [PATCH 20/32] opt=3 for crypto --- node/Cargo.toml | 3 +++ node/actors/consensus/src/leader/replica_commit.rs | 2 +- node/actors/consensus/src/leader/replica_prepare.rs | 2 +- node/actors/consensus/src/replica/leader_commit.rs | 2 +- node/actors/consensus/src/replica/leader_prepare.rs | 2 +- node/actors/network/src/consensus/handshake/mod.rs | 4 ++-- node/libs/roles/src/validator/messages/msg.rs | 4 ++-- 7 files changed, 11 insertions(+), 8 deletions(-) diff --git a/node/Cargo.toml b/node/Cargo.toml index 4c3e613e..f90666f2 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -80,6 +80,9 @@ panic = 'abort' [profile.release] panic = 'abort' +[profile.dev.package.crypto] +opt-level = 3 + # Compile all the external dependencies with optimizations, because # some of them (especially the cryptographic primitives) are extremely # slow when compiled without optimizations, and make the tests run slow. diff --git a/node/actors/consensus/src/leader/replica_commit.rs b/node/actors/consensus/src/leader/replica_commit.rs index 126cf529..dd676f8c 100644 --- a/node/actors/consensus/src/leader/replica_commit.rs +++ b/node/actors/consensus/src/leader/replica_commit.rs @@ -41,7 +41,7 @@ pub(crate) enum Error { }, /// Invalid message signature. #[error("invalid signature: {0:#}")] - InvalidSignature(#[source] crypto::bn254::Error), + InvalidSignature(#[source] validator::Error), } impl StateMachine { diff --git a/node/actors/consensus/src/leader/replica_prepare.rs b/node/actors/consensus/src/leader/replica_prepare.rs index 4c0ef691..275f31f8 100644 --- a/node/actors/consensus/src/leader/replica_prepare.rs +++ b/node/actors/consensus/src/leader/replica_prepare.rs @@ -50,7 +50,7 @@ pub(crate) enum Error { }, /// Invalid message signature. #[error("invalid signature: {0:#}")] - InvalidSignature(#[source] crypto::bn254::Error), + InvalidSignature(#[source] validator::Error), /// Invalid `HighQC` message. #[error("invalid high QC: {0:#}")] InvalidHighQC(#[source] anyhow::Error), diff --git a/node/actors/consensus/src/replica/leader_commit.rs b/node/actors/consensus/src/replica/leader_commit.rs index 6fe4952e..f5bcd1da 100644 --- a/node/actors/consensus/src/replica/leader_commit.rs +++ b/node/actors/consensus/src/replica/leader_commit.rs @@ -28,7 +28,7 @@ pub(crate) enum Error { }, /// Invalid message signature. #[error("invalid signature: {0:#}")] - InvalidSignature(#[source] crypto::bn254::Error), + InvalidSignature(#[source] validator::Error), /// Invalid justification for the message. #[error("invalid justification: {0:#}")] InvalidJustification(#[source] anyhow::Error), diff --git a/node/actors/consensus/src/replica/leader_prepare.rs b/node/actors/consensus/src/replica/leader_prepare.rs index 2c7ed3e8..74501136 100644 --- a/node/actors/consensus/src/replica/leader_prepare.rs +++ b/node/actors/consensus/src/replica/leader_prepare.rs @@ -32,7 +32,7 @@ pub(crate) enum Error { }, /// Invalid message signature. #[error("invalid signature: {0:#}")] - InvalidSignature(#[source] crypto::bn254::Error), + InvalidSignature(#[source] validator::Error), /// Invalid `PrepareQC` message. #[error("invalid PrepareQC: {0:#}")] InvalidPrepareQC(#[source] anyhow::Error), diff --git a/node/actors/network/src/consensus/handshake/mod.rs b/node/actors/network/src/consensus/handshake/mod.rs index b041cc5e..ccf8254f 100644 --- a/node/actors/network/src/consensus/handshake/mod.rs +++ b/node/actors/network/src/consensus/handshake/mod.rs @@ -1,7 +1,7 @@ use crate::{frame, noise}; use anyhow::Context as _; use concurrency::{ctx, time}; -use crypto::{bn254, ByteFmt}; +use crypto::{ByteFmt}; use roles::{node, validator}; use schema::{proto::network::consensus as proto, read_required, ProtoFmt}; @@ -43,7 +43,7 @@ pub(super) enum Error { #[error("unexpected peer")] PeerMismatch, #[error("validator signature {0}")] - Signature(#[from] bn254::Error), + Signature(#[from] validator::Error), #[error("stream {0}")] Stream(#[source] anyhow::Error), } diff --git a/node/libs/roles/src/validator/messages/msg.rs b/node/libs/roles/src/validator/messages/msg.rs index c722d47a..d4f66d05 100644 --- a/node/libs/roles/src/validator/messages/msg.rs +++ b/node/libs/roles/src/validator/messages/msg.rs @@ -1,8 +1,8 @@ //! Generic message types. use super::{ConsensusMsg, NetAddress}; -use crate::{node::SessionId, validator}; -use crypto::{bn254::Error, sha256, ByteFmt, Text, TextFmt}; +use crate::{validator::Error, node::SessionId, validator}; +use crypto::{sha256, ByteFmt, Text, TextFmt}; use std::fmt; use utils::enum_util::{BadVariantError, Variant}; From 43f4deceb0bb346eab8c81abb89832a489060f4c Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Tue, 31 Oct 2023 13:05:10 +0100 Subject: [PATCH 21/32] <4s --- node/Cargo.lock | 1 + node/actors/consensus/src/replica/tests.rs | 1 + .../network/src/consensus/handshake/mod.rs | 2 +- node/libs/crypto/Cargo.toml | 3 +- node/libs/crypto/src/bn254/hash.rs | 2 + node/libs/crypto/src/bn254/mod.rs | 65 ++++++++----------- node/libs/crypto/src/bn254/testonly.rs | 9 ++- .../src/validator/keys/aggregate_signature.rs | 9 +-- node/libs/roles/src/validator/messages/msg.rs | 2 +- 9 files changed, 41 insertions(+), 53 deletions(-) diff --git a/node/Cargo.lock b/node/Cargo.lock index 400544aa..fedbc7d4 100644 --- a/node/Cargo.lock +++ b/node/Cargo.lock @@ -696,6 +696,7 @@ dependencies = [ "rand", "sha2", "thiserror", + "tracing", ] [[package]] diff --git a/node/actors/consensus/src/replica/tests.rs b/node/actors/consensus/src/replica/tests.rs index 775929e8..09fd17b7 100644 --- a/node/actors/consensus/src/replica/tests.rs +++ b/node/actors/consensus/src/replica/tests.rs @@ -6,6 +6,7 @@ use roles::validator::{self, ViewNumber}; #[tokio::test] async fn start_new_view_not_leader() { + concurrency::testonly::abort_on_panic(); let ctx = &ctx::test_root(&ctx::ManualClock::new()); let rng = &mut ctx.rng(); diff --git a/node/actors/network/src/consensus/handshake/mod.rs b/node/actors/network/src/consensus/handshake/mod.rs index ccf8254f..5d6d309f 100644 --- a/node/actors/network/src/consensus/handshake/mod.rs +++ b/node/actors/network/src/consensus/handshake/mod.rs @@ -1,7 +1,7 @@ use crate::{frame, noise}; use anyhow::Context as _; use concurrency::{ctx, time}; -use crypto::{ByteFmt}; +use crypto::ByteFmt; use roles::{node, validator}; use schema::{proto::network::consensus as proto, read_required, ProtoFmt}; diff --git a/node/libs/crypto/Cargo.toml b/node/libs/crypto/Cargo.toml index 6a827f90..4b8f96f3 100644 --- a/node/libs/crypto/Cargo.toml +++ b/node/libs/crypto/Cargo.toml @@ -18,10 +18,11 @@ hex.workspace = true rand.workspace = true sha2.workspace = true thiserror.workspace = true +tracing.workspace = true [dev-dependencies] criterion = "0.5.1" [[bench]] name = "bench" -harness = false \ No newline at end of file +harness = false diff --git a/node/libs/crypto/src/bn254/hash.rs b/node/libs/crypto/src/bn254/hash.rs index 8540ee58..8d8aaa69 100644 --- a/node/libs/crypto/src/bn254/hash.rs +++ b/node/libs/crypto/src/bn254/hash.rs @@ -3,8 +3,10 @@ use ark_bn254::{G1Affine, G1Projective}; use ark_ec::AffineRepr as _; use sha2::Digest as _; +use tracing::instrument; /// Hashes an arbitrary message and maps it to an elliptic curve point in G1. +#[instrument(level = "trace", skip_all)] pub(crate) fn hash_to_g1(msg: &[u8]) -> G1Projective { for i in 0..100 { // Hash the message with the index as suffix. diff --git a/node/libs/crypto/src/bn254/mod.rs b/node/libs/crypto/src/bn254/mod.rs index 0aff49fe..f0c7b551 100644 --- a/node/libs/crypto/src/bn254/mod.rs +++ b/node/libs/crypto/src/bn254/mod.rs @@ -1,20 +1,16 @@ //! BLS signature scheme for the BN254 curve. -use std::collections::BTreeMap; -use std::fmt::Debug; -use std::hash::Hasher; - +use crate::ByteFmt; use anyhow::anyhow; use ark_bn254::{Bn254, Fr, G1Projective as G1, G2Projective as G2}; -use ark_ec::pairing::Pairing as _; -use ark_ec::pairing::PairingOutput; -use ark_ec::Group as _; +use ark_ec::{ + pairing::{Pairing as _, PairingOutput}, + Group as _, +}; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; -use num_traits::Zero as _; - pub use error::Error; - -use crate::ByteFmt; +use num_traits::Zero as _; +use std::{collections::HashMap, fmt::Debug, hash::Hasher}; #[doc(hidden)] pub mod error; @@ -99,6 +95,7 @@ pub struct Signature(pub G1); impl Signature { /// Verifies a signature against the provided public key + #[inline(never)] pub fn verify(&self, msg: &[u8], pk: &PublicKey) -> Result<(), Error> { let hash_point = hash::hash_to_g1(msg); @@ -154,23 +151,21 @@ impl AggregateSignature { AggregateSignature(agg) } - /// Verifies an aggregated signature for multiple messages against the provided list of public keys. - /// This method expects one public key per message, otherwise it will fail. Note however that - /// If there are any duplicate messages, the public keys will be aggregated before verification. - pub fn verify<'a>( - &self, - msgs_and_pks: impl Iterator, - ) -> Result<(), Error> { - let msgs_and_pks = Self::aggregate_pk(msgs_and_pks); - + #[inline(never)] + fn verify_raw(&self, msgs_and_pks: &[(&[u8], &PublicKey)]) -> Result<(), Error> { + // Aggregate public keys if they are signing the same hash. Each public key aggregated + // is one fewer pairing to calculate. + let mut pairs: HashMap<&[u8], G2> = HashMap::new(); + for (msg, pk) in msgs_and_pks { + *pairs.entry(msg).or_default() += pk.0; + } // First pair: e(sig: G1, generator: G2) let a = Bn254::pairing(self.0, G2::generator()); - // Second pair: e(H(m1): G1, pk1: G2) * ... * (H(m1000): G1, pk1000: G2) + // Second pair: e(H(m1): G1, pk1: G2) * ... * e(H(m1000): G1, pk1000: G2) let mut b = PairingOutput::zero(); - for (msg, pk) in msgs_and_pks { - let hash_point = hash::hash_to_g1(msg); - b += Bn254::pairing(hash_point, pk.0); + for (msg, pk) in pairs { + b += Bn254::pairing(hash::hash_to_g1(msg), pk); } if a == b { @@ -180,22 +175,14 @@ impl AggregateSignature { } } - fn aggregate_pk<'a>( + /// Verifies an aggregated signature for multiple messages against the provided list of public keys. + /// This method expects one public key per message, otherwise it will fail. Note however that + /// If there are any duplicate messages, the public keys will be aggregated before verification. + pub fn verify<'a>( + &self, msgs_and_pks: impl Iterator, - ) -> impl Iterator { - // Aggregate public keys if they are signing the same hash. Each public key aggregated - // is one fewer pairing to calculate. - let mut tree_map: BTreeMap<&[u8], PublicKey> = BTreeMap::new(); - - for (msg, pk) in msgs_and_pks { - if let Some(existing_pk) = tree_map.get_mut(msg) { - existing_pk.0 += pk.0; - } else { - tree_map.insert(msg, pk.clone()); - } - } - - tree_map.into_iter() + ) -> Result<(), Error> { + self.verify_raw(&msgs_and_pks.collect::>()[..]) } } diff --git a/node/libs/crypto/src/bn254/testonly.rs b/node/libs/crypto/src/bn254/testonly.rs index 9304b530..a4af1c1d 100644 --- a/node/libs/crypto/src/bn254/testonly.rs +++ b/node/libs/crypto/src/bn254/testonly.rs @@ -1,8 +1,7 @@ //! Random key generation, intended for use in testing -use rand::{distributions::Standard, prelude::Distribution, Rng}; - use super::{AggregateSignature, PublicKey, SecretKey, Signature}; +use rand::{distributions::Standard, prelude::Distribution, Rng}; /// Generates a random SecretKey. This is meant for testing purposes. impl Distribution for Standard { @@ -15,20 +14,20 @@ impl Distribution for Standard { /// Generates a random PublicKey. This is meant for testing purposes. impl Distribution for Standard { fn sample(&self, rng: &mut R) -> PublicKey { - rng.gen() + PublicKey(rng.gen()) } } /// Generates a random Signature. This is meant for testing purposes. impl Distribution for Standard { fn sample(&self, rng: &mut R) -> Signature { - rng.gen() + Signature(rng.gen()) } } /// Generates a random AggregateSignature. This is meant for testing purposes. impl Distribution for Standard { fn sample(&self, rng: &mut R) -> AggregateSignature { - rng.gen() + AggregateSignature(rng.gen()) } } diff --git a/node/libs/roles/src/validator/keys/aggregate_signature.rs b/node/libs/roles/src/validator/keys/aggregate_signature.rs index a19d84be..7f1d6ee8 100644 --- a/node/libs/roles/src/validator/keys/aggregate_signature.rs +++ b/node/libs/roles/src/validator/keys/aggregate_signature.rs @@ -1,12 +1,9 @@ -use std::fmt; - +use super::{Error, PublicKey, Signature}; +use crate::validator::messages::{Msg, MsgHash}; use crypto::{bn254, ByteFmt, Text, TextFmt}; +use std::fmt; use utils::enum_util::Variant; -use crate::validator::messages::{Msg, MsgHash}; - -use super::{Error, PublicKey, Signature}; - /// An aggregate signature from a validator. #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct AggregateSignature(pub(crate) bn254::AggregateSignature); diff --git a/node/libs/roles/src/validator/messages/msg.rs b/node/libs/roles/src/validator/messages/msg.rs index d4f66d05..ac0e3bf8 100644 --- a/node/libs/roles/src/validator/messages/msg.rs +++ b/node/libs/roles/src/validator/messages/msg.rs @@ -1,7 +1,7 @@ //! Generic message types. use super::{ConsensusMsg, NetAddress}; -use crate::{validator::Error, node::SessionId, validator}; +use crate::{node::SessionId, validator, validator::Error}; use crypto::{sha256, ByteFmt, Text, TextFmt}; use std::fmt; use utils::enum_util::{BadVariantError, Variant}; From efd701a7cce01fb2207761fab5430f6f27cb56ee Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Tue, 31 Oct 2023 13:24:38 +0100 Subject: [PATCH 22/32] removed clock speedup --- node/actors/consensus/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/actors/consensus/src/tests.rs b/node/actors/consensus/src/tests.rs index eead7bca..41723316 100644 --- a/node/actors/consensus/src/tests.rs +++ b/node/actors/consensus/src/tests.rs @@ -6,7 +6,7 @@ use concurrency::ctx; async fn run_test(behavior: Behavior, network: Network) { concurrency::testonly::abort_on_panic(); - let ctx = &ctx::test_root(&ctx::AffineClock::new(4.)); + let ctx = &ctx::test_root(&ctx::AffineClock::new(1.)); const NODES: usize = 11; let mut nodes = vec![behavior; NODES]; From 71a1f76c0f3711cceafaf81c6c6220ea6d02796e Mon Sep 17 00:00:00 2001 From: Moshe Shababo <17073733+moshababo@users.noreply.github.com> Date: Tue, 31 Oct 2023 21:42:32 -0500 Subject: [PATCH 23/32] Remove bls12_381 from protobuf schemas --- node/libs/roles/src/validator/conv.rs | 12 ++++++------ node/libs/schema/proto/executor/config.proto | 4 ++-- node/libs/schema/proto/roles/validator.proto | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/node/libs/roles/src/validator/conv.rs b/node/libs/roles/src/validator/conv.rs index 0b65e63d..c96fc2f7 100644 --- a/node/libs/roles/src/validator/conv.rs +++ b/node/libs/roles/src/validator/conv.rs @@ -354,12 +354,12 @@ impl ProtoFmt for PublicKey { type Proto = proto::PublicKey; fn read(r: &Self::Proto) -> anyhow::Result { - Ok(Self(ByteFmt::decode(required(&r.bls12381)?)?)) + Ok(Self(ByteFmt::decode(required(&r.bn254)?)?)) } fn build(&self) -> Self::Proto { Self::Proto { - bls12381: Some(self.0.encode()), + bn254: Some(self.0.encode()), } } } @@ -368,12 +368,12 @@ impl ProtoFmt for Signature { type Proto = proto::Signature; fn read(r: &Self::Proto) -> anyhow::Result { - Ok(Self(ByteFmt::decode(required(&r.bls12381)?)?)) + Ok(Self(ByteFmt::decode(required(&r.bn254)?)?)) } fn build(&self) -> Self::Proto { Self::Proto { - bls12381: Some(self.0.encode()), + bn254: Some(self.0.encode()), } } } @@ -382,12 +382,12 @@ impl ProtoFmt for AggregateSignature { type Proto = proto::AggregateSignature; fn read(r: &Self::Proto) -> anyhow::Result { - Ok(Self(ByteFmt::decode(required(&r.bls12381)?)?)) + Ok(Self(ByteFmt::decode(required(&r.bn254)?)?)) } fn build(&self) -> Self::Proto { Self::Proto { - bls12381: Some(self.0.encode()), + bn254: Some(self.0.encode()), } } } diff --git a/node/libs/schema/proto/executor/config.proto b/node/libs/schema/proto/executor/config.proto index 87baafd0..f273a039 100644 --- a/node/libs/schema/proto/executor/config.proto +++ b/node/libs/schema/proto/executor/config.proto @@ -21,8 +21,8 @@ // examples: "203.0.113.7:3456", "[2001:DB8::1]:4567" // // ValidatorPublicKey - public key of the validator (consensus participant) of the form "validator:public::" -// Currently only bls12_381 signature scheme is supported for validators. -// example: "validator:public:bls12_381:ad5dae0f91d5bcdd1a0481a6f719bc7188b25b623c51b1988321fe911596c94d0a3717f028d4d9d226a8afe087d7f2ca" +// Currently only bn254 signature scheme is supported for validators. +// example: "validator:public:bn254:4b0c4697f0a35eab30f63684ae4611f3c1d631eecfd97237e2345a9b3d0c472dbb16c49b793beceaab0cdd89cda6ff1099bd1aaf1ad6cabde9a15793cc09b407" // // NodePublicKey - public key of the node (gossip network participant) of the form "node:public::" // Currently only ed25519 signature scheme is supported for nodes. diff --git a/node/libs/schema/proto/roles/validator.proto b/node/libs/schema/proto/roles/validator.proto index 8d02e1a7..f622b06d 100644 --- a/node/libs/schema/proto/roles/validator.proto +++ b/node/libs/schema/proto/roles/validator.proto @@ -147,13 +147,13 @@ message Signed { } message PublicKey { - optional bytes bls12381 = 1; // required + optional bytes bn254 = 1; // required } message Signature { - optional bytes bls12381 = 1; // required + optional bytes bn254 = 1; // required } message AggregateSignature { - optional bytes bls12381 = 1; // required + optional bytes bn254 = 1; // required } From 96f1cba8d9a19b2e6eb0b3d2312e436b08397f87 Mon Sep 17 00:00:00 2001 From: Moshe Shababo <17073733+moshababo@users.noreply.github.com> Date: Wed, 1 Nov 2023 12:01:51 -0500 Subject: [PATCH 24/32] Make wrapped scalar/group elements private --- node/libs/crypto/src/bn254/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/node/libs/crypto/src/bn254/mod.rs b/node/libs/crypto/src/bn254/mod.rs index f0c7b551..0c24ac7a 100644 --- a/node/libs/crypto/src/bn254/mod.rs +++ b/node/libs/crypto/src/bn254/mod.rs @@ -22,7 +22,7 @@ pub mod hash; mod testonly; /// Type safety wrapper around a scalar value. -pub struct SecretKey(pub Fr); +pub struct SecretKey(Fr); impl SecretKey { /// Gets the corresponding [`PublicKey`] for this [`SecretKey`] @@ -55,7 +55,7 @@ impl ByteFmt for SecretKey { /// Type safety wrapper around G2. #[derive(Clone, PartialEq, Eq)] -pub struct PublicKey(pub G2); +pub struct PublicKey(G2); impl ByteFmt for PublicKey { fn decode(bytes: &[u8]) -> anyhow::Result { @@ -91,7 +91,7 @@ impl Ord for PublicKey { /// Type safety wrapper around a G1 value. #[derive(Clone, Debug, PartialEq, Eq)] -pub struct Signature(pub G1); +pub struct Signature(G1); impl Signature { /// Verifies a signature against the provided public key @@ -138,7 +138,7 @@ impl Ord for Signature { } /// Type safety wrapper around [Signature] indicating that it is an aggregated signature. #[derive(Clone, Debug, PartialEq, Eq)] -pub struct AggregateSignature(pub G1); +pub struct AggregateSignature(G1); impl AggregateSignature { /// Generates an aggregate signature from a list of signatures. From 40faa1f061bb76b2305b477e448d538ab48efc80 Mon Sep 17 00:00:00 2001 From: Moshe Shababo <17073733+moshababo@users.noreply.github.com> Date: Wed, 1 Nov 2023 16:01:03 -0500 Subject: [PATCH 25/32] PublicKey to derive Hash --- node/libs/crypto/src/bn254/mod.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/node/libs/crypto/src/bn254/mod.rs b/node/libs/crypto/src/bn254/mod.rs index 0c24ac7a..daa16e66 100644 --- a/node/libs/crypto/src/bn254/mod.rs +++ b/node/libs/crypto/src/bn254/mod.rs @@ -10,7 +10,7 @@ use ark_ec::{ use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; pub use error::Error; use num_traits::Zero as _; -use std::{collections::HashMap, fmt::Debug, hash::Hasher}; +use std::{collections::HashMap, fmt::Debug}; #[doc(hidden)] pub mod error; @@ -54,7 +54,7 @@ impl ByteFmt for SecretKey { } /// Type safety wrapper around G2. -#[derive(Clone, PartialEq, Eq)] +#[derive(Clone, PartialEq, Eq, Hash)] pub struct PublicKey(G2); impl ByteFmt for PublicKey { @@ -71,12 +71,6 @@ impl ByteFmt for PublicKey { } } -impl std::hash::Hash for PublicKey { - fn hash(&self, state: &mut H) { - self.0.hash(state); - } -} - impl PartialOrd for PublicKey { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) From 93999ce4db905522754ba2f0bf8d719587a39ce0 Mon Sep 17 00:00:00 2001 From: Moshe Shababo <17073733+moshababo@users.noreply.github.com> Date: Wed, 1 Nov 2023 16:02:54 -0500 Subject: [PATCH 26/32] Remove unused import --- node/libs/crypto/src/bn254/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/libs/crypto/src/bn254/mod.rs b/node/libs/crypto/src/bn254/mod.rs index daa16e66..44394969 100644 --- a/node/libs/crypto/src/bn254/mod.rs +++ b/node/libs/crypto/src/bn254/mod.rs @@ -10,7 +10,7 @@ use ark_ec::{ use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; pub use error::Error; use num_traits::Zero as _; -use std::{collections::HashMap, fmt::Debug}; +use std::{collections::HashMap}; #[doc(hidden)] pub mod error; From c352cc8cf4d10a16a78200f50f3e1d6325fb95c3 Mon Sep 17 00:00:00 2001 From: Moshe Shababo <17073733+moshababo@users.noreply.github.com> Date: Wed, 1 Nov 2023 20:45:33 -0500 Subject: [PATCH 27/32] cargo fmt --- node/libs/crypto/src/bn254/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/libs/crypto/src/bn254/mod.rs b/node/libs/crypto/src/bn254/mod.rs index 44394969..99dbfa77 100644 --- a/node/libs/crypto/src/bn254/mod.rs +++ b/node/libs/crypto/src/bn254/mod.rs @@ -10,7 +10,7 @@ use ark_ec::{ use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; pub use error::Error; use num_traits::Zero as _; -use std::{collections::HashMap}; +use std::collections::HashMap; #[doc(hidden)] pub mod error; From 8c3aa70e1385c7fbc3b2feb96f64a8f1366ce15f Mon Sep 17 00:00:00 2001 From: Moshe Shababo <17073733+moshababo@users.noreply.github.com> Date: Wed, 1 Nov 2023 20:51:08 -0500 Subject: [PATCH 28/32] Increase range --- node/libs/crypto/src/bn254/hash.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/libs/crypto/src/bn254/hash.rs b/node/libs/crypto/src/bn254/hash.rs index 8d8aaa69..4e0eec26 100644 --- a/node/libs/crypto/src/bn254/hash.rs +++ b/node/libs/crypto/src/bn254/hash.rs @@ -8,7 +8,7 @@ use tracing::instrument; /// Hashes an arbitrary message and maps it to an elliptic curve point in G1. #[instrument(level = "trace", skip_all)] pub(crate) fn hash_to_g1(msg: &[u8]) -> G1Projective { - for i in 0..100 { + for i in 0..256 { // Hash the message with the index as suffix. let bytes: [u8; 32] = sha2::Sha256::new() .chain_update(msg) From e89656f8d60f243cfcac4ee7df72d8c0479c3266 Mon Sep 17 00:00:00 2001 From: Moshe Shababo <17073733+moshababo@users.noreply.github.com> Date: Thu, 2 Nov 2023 12:11:37 -0500 Subject: [PATCH 29/32] Remove instrumentation --- node/libs/crypto/src/bn254/hash.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/node/libs/crypto/src/bn254/hash.rs b/node/libs/crypto/src/bn254/hash.rs index 4e0eec26..ad30f95d 100644 --- a/node/libs/crypto/src/bn254/hash.rs +++ b/node/libs/crypto/src/bn254/hash.rs @@ -6,7 +6,6 @@ use sha2::Digest as _; use tracing::instrument; /// Hashes an arbitrary message and maps it to an elliptic curve point in G1. -#[instrument(level = "trace", skip_all)] pub(crate) fn hash_to_g1(msg: &[u8]) -> G1Projective { for i in 0..256 { // Hash the message with the index as suffix. From 91b4f64076f91804d7810f05cd9daa4773fbc9d4 Mon Sep 17 00:00:00 2001 From: Moshe Shababo <17073733+moshababo@users.noreply.github.com> Date: Thu, 2 Nov 2023 12:28:08 -0500 Subject: [PATCH 30/32] Use anyhow::Context --- node/Cargo.toml | 2 +- node/libs/crypto/src/bn254/hash.rs | 1 - node/libs/crypto/src/bn254/mod.rs | 10 +++++----- node/libs/crypto/src/bn254/tests.rs | 6 ++++-- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/node/Cargo.toml b/node/Cargo.toml index f90666f2..b22a7fb6 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -28,7 +28,7 @@ bit-vec = "0.6" blst = "0.3.10" ark-bn254 = "0.4.0" ark-ec = "0.4.2" -ark-serialize = "0.4.2" +ark-serialize = { version = "0.4.2", features = ["std"] } num-traits = "0.2.17" clap = { version = "4.3.3", features = ["derive"] } ed25519-dalek = { version = "2.0.0", features = ["serde", "rand_core"] } diff --git a/node/libs/crypto/src/bn254/hash.rs b/node/libs/crypto/src/bn254/hash.rs index ad30f95d..dd7a7b62 100644 --- a/node/libs/crypto/src/bn254/hash.rs +++ b/node/libs/crypto/src/bn254/hash.rs @@ -3,7 +3,6 @@ use ark_bn254::{G1Affine, G1Projective}; use ark_ec::AffineRepr as _; use sha2::Digest as _; -use tracing::instrument; /// Hashes an arbitrary message and maps it to an elliptic curve point in G1. pub(crate) fn hash_to_g1(msg: &[u8]) -> G1Projective { diff --git a/node/libs/crypto/src/bn254/mod.rs b/node/libs/crypto/src/bn254/mod.rs index 99dbfa77..b70bcd72 100644 --- a/node/libs/crypto/src/bn254/mod.rs +++ b/node/libs/crypto/src/bn254/mod.rs @@ -1,7 +1,7 @@ //! BLS signature scheme for the BN254 curve. use crate::ByteFmt; -use anyhow::anyhow; +use anyhow::Context as _; use ark_bn254::{Bn254, Fr, G1Projective as G1, G2Projective as G2}; use ark_ec::{ pairing::{Pairing as _, PairingOutput}, @@ -43,7 +43,7 @@ impl ByteFmt for SecretKey { fn decode(bytes: &[u8]) -> anyhow::Result { Fr::deserialize_compressed(bytes) .map(Self) - .map_err(|e| anyhow!("failed to decode secret key: {e:?}")) + .context("failed to decode secret key") } fn encode(&self) -> Vec { @@ -61,7 +61,7 @@ impl ByteFmt for PublicKey { fn decode(bytes: &[u8]) -> anyhow::Result { G2::deserialize_compressed(bytes) .map(Self) - .map_err(|e| anyhow!("failed to decode public key: {e:?}")) + .context("failed to decode public key") } fn encode(&self) -> Vec { @@ -110,7 +110,7 @@ impl ByteFmt for Signature { fn decode(bytes: &[u8]) -> anyhow::Result { G1::deserialize_compressed(bytes) .map(Self) - .map_err(|e| anyhow!("failed to decode signature: {e:?}")) + .context("failed to decode signature") } fn encode(&self) -> Vec { let mut buf = Vec::new(); @@ -184,7 +184,7 @@ impl ByteFmt for AggregateSignature { fn decode(bytes: &[u8]) -> anyhow::Result { G1::deserialize_compressed(bytes) .map(Self) - .map_err(|e| anyhow!("failed to decode aggregate signature: {e:?}")) + .context("failed to decode aggregate signature") } fn encode(&self) -> Vec { diff --git a/node/libs/crypto/src/bn254/tests.rs b/node/libs/crypto/src/bn254/tests.rs index d07afbfa..933b914b 100644 --- a/node/libs/crypto/src/bn254/tests.rs +++ b/node/libs/crypto/src/bn254/tests.rs @@ -1,7 +1,9 @@ -use crate::bn254::{AggregateSignature, PublicKey, SecretKey, Signature}; -use rand::{rngs::StdRng, Rng, SeedableRng}; use std::iter::repeat_with; +use rand::{rngs::StdRng, Rng, SeedableRng}; + +use crate::bn254::{AggregateSignature, PublicKey, SecretKey, Signature}; + #[test] fn signature_smoke() { let mut rng = StdRng::seed_from_u64(29483920); From 28526fd2bb54d833e1928ce6cbe95d93671a4e42 Mon Sep 17 00:00:00 2001 From: Moshe Shababo <17073733+moshababo@users.noreply.github.com> Date: Thu, 2 Nov 2023 13:16:53 -0500 Subject: [PATCH 31/32] Add comment about non-generic no-inline functions --- node/libs/crypto/src/bn254/mod.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/node/libs/crypto/src/bn254/mod.rs b/node/libs/crypto/src/bn254/mod.rs index b70bcd72..49f5d37c 100644 --- a/node/libs/crypto/src/bn254/mod.rs +++ b/node/libs/crypto/src/bn254/mod.rs @@ -88,7 +88,10 @@ impl Ord for PublicKey { pub struct Signature(G1); impl Signature { - /// Verifies a signature against the provided public key + /// Verifies a signature against the provided public key. + /// + /// This function is intentionally non-generic and disallow inlining to ensure that compilation optimizations can be effectively applied. + /// This optimization is needed for ensuring that tests can run within a reasonable time frame. #[inline(never)] pub fn verify(&self, msg: &[u8], pk: &PublicKey) -> Result<(), Error> { let hash_point = hash::hash_to_g1(msg); @@ -145,6 +148,8 @@ impl AggregateSignature { AggregateSignature(agg) } + /// This function is intentionally non-generic and disallow inlining to ensure that compilation optimizations can be effectively applied. + /// This optimization is needed for ensuring that tests can run within a reasonable time frame. #[inline(never)] fn verify_raw(&self, msgs_and_pks: &[(&[u8], &PublicKey)]) -> Result<(), Error> { // Aggregate public keys if they are signing the same hash. Each public key aggregated From 4959dd5c6b20254a30b22c44c66a8281f2d44dff Mon Sep 17 00:00:00 2001 From: Moshe Shababo <17073733+moshababo@users.noreply.github.com> Date: Thu, 2 Nov 2023 20:18:51 -0500 Subject: [PATCH 32/32] Add disclaimer regarding side-channel attacks --- node/libs/crypto/src/bn254/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/node/libs/crypto/src/bn254/mod.rs b/node/libs/crypto/src/bn254/mod.rs index 49f5d37c..533509e9 100644 --- a/node/libs/crypto/src/bn254/mod.rs +++ b/node/libs/crypto/src/bn254/mod.rs @@ -1,4 +1,7 @@ //! BLS signature scheme for the BN254 curve. +//! +//! Disclaimer: the implementation of the pairing-friendly elliptic curve does not run in constant time, +//! hence it does not protect the secret key from side-channel attacks. use crate::ByteFmt; use anyhow::Context as _;