From 692b932ab69378f9515f352bf7cd4bee8531c950 Mon Sep 17 00:00:00 2001 From: Skalman Date: Thu, 10 Oct 2024 07:28:08 -0400 Subject: [PATCH 1/7] - Implement `ProofOfPossession` traits their derive macro. - Derive ProofOfPossession for all pubkey crypto type beside BLS. --- Cargo.lock | 14 ++ Cargo.toml | 4 + substrate/primitives/core/Cargo.toml | 1 + substrate/primitives/core/src/bandersnatch.rs | 6 +- substrate/primitives/core/src/bls.rs | 2 +- substrate/primitives/core/src/crypto.rs | 49 +++++++ substrate/primitives/core/src/ecdsa.rs | 9 +- substrate/primitives/core/src/ed25519.rs | 6 +- substrate/primitives/core/src/sr25519.rs | 6 +- .../primitives/crypto/pubkeycrypto/Cargo.toml | 45 +++++++ .../crypto/pubkeycrypto/proc-macro/Cargo.toml | 23 ++++ .../pubkeycrypto/proc-macro/src/impls.rs | 124 ++++++++++++++++++ .../crypto/pubkeycrypto/proc-macro/src/lib.rs | 42 ++++++ .../primitives/crypto/pubkeycrypto/src/lib.rs | 7 + 14 files changed, 329 insertions(+), 9 deletions(-) create mode 100644 substrate/primitives/crypto/pubkeycrypto/Cargo.toml create mode 100644 substrate/primitives/crypto/pubkeycrypto/proc-macro/Cargo.toml create mode 100644 substrate/primitives/crypto/pubkeycrypto/proc-macro/src/impls.rs create mode 100644 substrate/primitives/crypto/pubkeycrypto/proc-macro/src/lib.rs create mode 100644 substrate/primitives/crypto/pubkeycrypto/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index a572c37a40602..e6d30a17f39e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21844,6 +21844,7 @@ dependencies = [ "serde", "serde_json", "sp-crypto-hashing 0.1.0", + "sp-crypto-pubkeycrypto-proc-macro", "sp-debug-derive 14.0.0", "sp-externalities 0.25.0", "sp-runtime-interface 24.0.0", @@ -22109,6 +22110,19 @@ dependencies = [ "syn 2.0.79", ] +[[package]] +name = "sp-crypto-pubkeycrypto" +version = "28.0.0" + +[[package]] +name = "sp-crypto-pubkeycrypto-proc-macro" +version = "0.1.0" +dependencies = [ + "quote 1.0.37", + "sp-crypto-hashing 0.1.0", + "syn 2.0.79", +] + [[package]] name = "sp-database" version = "10.0.0" diff --git a/Cargo.toml b/Cargo.toml index e2c6d6c8dedc8..624aba96be239 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -469,6 +469,8 @@ members = [ "substrate/primitives/crypto/ec-utils", "substrate/primitives/crypto/hashing", "substrate/primitives/crypto/hashing/proc-macro", + "substrate/primitives/crypto/pubkeycrypto", + "substrate/primitives/crypto/pubkeycrypto/proc-macro", "substrate/primitives/database", "substrate/primitives/debug-derive", "substrate/primitives/externalities", @@ -1240,6 +1242,8 @@ sp-consensus-slots = { path = "substrate/primitives/consensus/slots", default-fe sp-core = { path = "substrate/primitives/core", default-features = false } sp-core-hashing = { default-features = false, path = "substrate/deprecated/hashing" } sp-core-hashing-proc-macro = { default-features = false, path = "substrate/deprecated/hashing/proc-macro" } +sp-crypto-pubkeycrypto = {default-features = false, path = "substrate/primitives/crypto/pubkeycrypto"} +sp-crypto-pubkeycrypto-proc-macro = {default-features = false, path = "substrate/primitives/crypto/pubkeycrypto/proc-macro"} sp-crypto-ec-utils = { default-features = false, path = "substrate/primitives/crypto/ec-utils" } sp-crypto-hashing = { path = "substrate/primitives/crypto/hashing", default-features = false } sp-crypto-hashing-proc-macro = { path = "substrate/primitives/crypto/hashing/proc-macro", default-features = false } diff --git a/substrate/primitives/core/Cargo.toml b/substrate/primitives/core/Cargo.toml index f6bc17bccacaf..953196c207101 100644 --- a/substrate/primitives/core/Cargo.toml +++ b/substrate/primitives/core/Cargo.toml @@ -42,6 +42,7 @@ sp-std = { workspace = true } sp-debug-derive = { workspace = true } sp-storage = { workspace = true } sp-externalities = { optional = true, workspace = true } +sp-crypto-pubkeycrypto-proc-macro= { workspace = true } futures = { optional = true, workspace = true } dyn-clonable = { optional = true, workspace = true } thiserror = { optional = true, workspace = true } diff --git a/substrate/primitives/core/src/bandersnatch.rs b/substrate/primitives/core/src/bandersnatch.rs index 25bf4657030fb..54bb86503e68f 100644 --- a/substrate/primitives/core/src/bandersnatch.rs +++ b/substrate/primitives/core/src/bandersnatch.rs @@ -24,8 +24,10 @@ use crate::crypto::VrfSecret; use crate::crypto::{ ByteArray, CryptoType, CryptoTypeId, DeriveError, DeriveJunction, Pair as TraitPair, - PublicBytes, SecretStringError, SignatureBytes, UncheckedFrom, VrfPublic, + ProofOfPossessionGenerator, ProofOfPossessionVerifier, PublicBytes, SecretStringError, + SignatureBytes, UncheckedFrom, VrfPublic, }; +use sp_crypto_pubkeycrypto_proc_macro::ProofOfPossession; use bandersnatch_vrfs::{CanonicalSerialize, SecretKey}; use codec::{Decode, Encode, EncodeLike, MaxEncodedLen}; @@ -75,7 +77,7 @@ impl CryptoType for Signature { type Seed = [u8; SEED_SERIALIZED_SIZE]; /// Bandersnatch secret key. -#[derive(Clone)] +#[derive(Clone, ProofOfPossession)] pub struct Pair { secret: SecretKey, seed: Seed, diff --git a/substrate/primitives/core/src/bls.rs b/substrate/primitives/core/src/bls.rs index f721a6ae08d18..40cf605fbb647 100644 --- a/substrate/primitives/core/src/bls.rs +++ b/substrate/primitives/core/src/bls.rs @@ -32,7 +32,7 @@ use alloc::vec::Vec; use w3f_bls::{ DoublePublicKey, DoublePublicKeyScheme, DoubleSignature, EngineBLS, Keypair, Message, - SecretKey, SerializableToBytes, TinyBLS381, + ProofOfPossession, SecretKey, SerializableToBytes, TinyBLS381, }; /// BLS-377 specialized types diff --git a/substrate/primitives/core/src/crypto.rs b/substrate/primitives/core/src/crypto.rs index fd7fe77672040..9c9c6696533f5 100644 --- a/substrate/primitives/core/src/crypto.rs +++ b/substrate/primitives/core/src/crypto.rs @@ -37,6 +37,9 @@ pub use ss58_registry::{from_known_address_format, Ss58AddressFormat, Ss58Addres /// Trait to zeroize a memory buffer. pub use zeroize::Zeroize; +use quote::quote; +use syn::{parse_macro_input, DeriveInput}; + pub use crate::{ address_uri::{AddressUri, Error as AddressUriError}, crypto_bytes::{CryptoBytes, PublicBytes, SignatureBytes}, @@ -960,6 +963,52 @@ pub trait Pair: CryptoType + Sized { fn to_raw_vec(&self) -> Vec; } +/// Pair which is able to generate proof of possession. This is implemented +/// in different trait to provide default behavoir +pub trait ProofOfPossessionGenerator: Pair +where + Self::Public: Encode, +{ + /// The proof of possession generator is supposed to + /// to produce a signature with unique hash context that should + /// never be used in other signatures. This proves that + /// that the secret key is known to the prover. While prevent + /// malicious actors to trick an honest party to sign their + /// public key to mount a rogue key attack (See: Section 4.3 of + /// - Ristenpart, T., & Yilek, S. (2007). The power of proofs-of-possession: Securing multiparty + /// signatures against rogue-key attacks. In , Annual {{International Conference}} on the + /// {{Theory}} and {{Applications}} of {{Cryptographic Techniques} (pp. 228–245). : Springer. + fn generate_proof_of_possession(&mut self) -> Self::Signature { + let pub_key_scaled = self.public().as_slice().encode(); + let pop_context_tag: &[u8] = b"POP_"; + let pop_statement = [pop_context_tag, pub_key_scaled.as_slice()].concat(); + self.sign(pop_statement.as_slice()) + } +} + +/// Pair which is able to generate proof of possession. While you don't need a keypair +/// to verify a proof of possession (you only need a public key) we constrain on Pair +/// to use the Public and Signature types associated to Pair. This is implemented +/// in different trait (than Public Key) to provide default behavoir +pub trait ProofOfPossessionVerifier: Pair +where + Self::Public: Encode, +{ + /// The proof of possession verifier is supposed to + /// to verify a signature with unique hash context that is + /// produced solely for this reason. This proves that + /// that the secret key is known to the prover. + fn verify_proof_of_possession( + proof_of_possesion: &Self::Signature, + allegedly_possessesd_pubkey: &Self::Public, + ) -> bool { + let pub_key_scaled = allegedly_possessesd_pubkey.encode(); + let pop_context_tag = b"POP_"; + let pop_statement = [pop_context_tag, pub_key_scaled.as_slice()].concat(); + Self::verify(proof_of_possesion, pop_statement, allegedly_possessesd_pubkey) + } +} + /// One type is wrapped by another. pub trait IsWrappedBy: From + Into { /// Get a reference to the inner from the outer. diff --git a/substrate/primitives/core/src/ecdsa.rs b/substrate/primitives/core/src/ecdsa.rs index d11811ff2af65..66752ca2f1400 100644 --- a/substrate/primitives/core/src/ecdsa.rs +++ b/substrate/primitives/core/src/ecdsa.rs @@ -18,10 +18,13 @@ //! Simple ECDSA secp256k1 API. use crate::crypto::{ - CryptoType, CryptoTypeId, DeriveError, DeriveJunction, Pair as TraitPair, PublicBytes, - SecretStringError, SignatureBytes, + CryptoType, CryptoTypeId, DeriveError, DeriveJunction, Pair as TraitPair, + ProofOfPossessionGenerator, ProofOfPossessionVerifier, PublicBytes, SecretStringError, + SignatureBytes, }; +use sp_crypto_pubkeycrypto_proc_macro::ProofOfPossession; + #[cfg(not(feature = "std"))] use alloc::vec::Vec; #[cfg(not(feature = "std"))] @@ -154,7 +157,7 @@ fn derive_hard_junction(secret_seed: &Seed, cc: &[u8; 32]) -> Seed { } /// A key pair. -#[derive(Clone)] +#[derive(Clone, ProofOfPossession)] pub struct Pair { public: Public, secret: SecretKey, diff --git a/substrate/primitives/core/src/ed25519.rs b/substrate/primitives/core/src/ed25519.rs index 401f9a39d5673..dc9edce796f9e 100644 --- a/substrate/primitives/core/src/ed25519.rs +++ b/substrate/primitives/core/src/ed25519.rs @@ -19,10 +19,12 @@ use crate::crypto::{ ByteArray, CryptoType, CryptoTypeId, DeriveError, DeriveJunction, Pair as TraitPair, - PublicBytes, SecretStringError, SignatureBytes, + ProofOfPossessionGenerator, ProofOfPossessionVerifier, PublicBytes, SecretStringError, + SignatureBytes, }; use ed25519_zebra::{SigningKey, VerificationKey}; +use sp_crypto_pubkeycrypto_proc_macro::ProofOfPossession; use alloc::vec::Vec; @@ -50,7 +52,7 @@ pub type Public = PublicBytes; pub type Signature = SignatureBytes; /// A key pair. -#[derive(Copy, Clone)] +#[derive(Copy, Clone, ProofOfPossession)] pub struct Pair { public: VerificationKey, secret: SigningKey, diff --git a/substrate/primitives/core/src/sr25519.rs b/substrate/primitives/core/src/sr25519.rs index 48780f2ccff93..d6e221870fdac 100644 --- a/substrate/primitives/core/src/sr25519.rs +++ b/substrate/primitives/core/src/sr25519.rs @@ -23,8 +23,11 @@ #[cfg(feature = "serde")] use crate::crypto::Ss58Codec; use crate::crypto::{ - CryptoBytes, DeriveError, DeriveJunction, Pair as TraitPair, SecretStringError, + CryptoBytes, DeriveError, DeriveJunction, Pair as TraitPair, ProofOfPossessionGenerator, + ProofOfPossessionVerifier, SecretStringError, }; +use sp_crypto_pubkeycrypto_proc_macro::ProofOfPossession; + use alloc::vec::Vec; #[cfg(feature = "full_crypto")] use schnorrkel::signing_context; @@ -145,6 +148,7 @@ impl From for Signature { } /// An Schnorrkel/Ristretto x25519 ("sr25519") key pair. +#[derive(ProofOfPossession)] pub struct Pair(Keypair); impl Clone for Pair { diff --git a/substrate/primitives/crypto/pubkeycrypto/Cargo.toml b/substrate/primitives/crypto/pubkeycrypto/Cargo.toml new file mode 100644 index 0000000000000..1efb5269e0cef --- /dev/null +++ b/substrate/primitives/crypto/pubkeycrypto/Cargo.toml @@ -0,0 +1,45 @@ +[package] +name = "sp-crypto-pubkeycrypto" +authors.workspace = true +edition.workspace = true +version = "28.0.0" +license = "Apache-2.0" +homepage.workspace = true +repository.workspace = true +description = "A crate for all implementations of public key cryptography in Substrate (currently in primitive/core.)" + +[lints] +workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] + +# full crypto + + +[dev-dependencies] + + + +[lib] +bench = false + + +[features] +default = ["std"] + +std = [] + +# Serde support without relying on std features. +serde = [ + +] + +# This feature enables all crypto primitives for `no_std` builds like microcontrollers +# or Intel SGX. +# For the regular wasm runtime builds this should not be used. +full_crypto = [ +] + diff --git a/substrate/primitives/crypto/pubkeycrypto/proc-macro/Cargo.toml b/substrate/primitives/crypto/pubkeycrypto/proc-macro/Cargo.toml new file mode 100644 index 0000000000000..089d875d3f864 --- /dev/null +++ b/substrate/primitives/crypto/pubkeycrypto/proc-macro/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "sp-crypto-pubkeycrypto-proc-macro" +version = "0.1.0" +authors.workspace = true +edition.workspace = true +license = "Apache-2.0" +homepage.workspace = true +repository.workspace = true +description = "Procedural macros for deriving public key crypto implementations." + +[lints] +workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[lib] +proc-macro = true + +[dependencies] +quote = { workspace = true } +syn = { features = ["full", "parsing"], workspace = true } +sp-crypto-hashing = { workspace = true } diff --git a/substrate/primitives/crypto/pubkeycrypto/proc-macro/src/impls.rs b/substrate/primitives/crypto/pubkeycrypto/proc-macro/src/impls.rs new file mode 100644 index 0000000000000..5eba7760b3de7 --- /dev/null +++ b/substrate/primitives/crypto/pubkeycrypto/proc-macro/src/impls.rs @@ -0,0 +1,124 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use quote::quote; +use syn::parse::{Parse, ParseStream}; + +use proc_macro::TokenStream; + +pub(super) struct InputBytes(pub Vec); + +pub(super) struct MultipleInputBytes(pub Vec>); + +impl MultipleInputBytes { + pub(super) fn concatenated(mut self) -> Vec { + if self.0.is_empty() { + Vec::new() + } else { + let mut result = core::mem::take(&mut self.0[0]); + for other in self.0[1..].iter_mut() { + result.append(other); + } + result + } + } +} + +impl Parse for InputBytes { + fn parse(input: ParseStream) -> syn::Result { + match syn::ExprArray::parse(input) { + Ok(array) => { + let mut bytes = Vec::::new(); + for expr in array.elems.iter() { + match expr { + syn::Expr::Lit(lit) => match &lit.lit { + syn::Lit::Int(b) => bytes.push(b.base10_parse()?), + syn::Lit::Byte(b) => bytes.push(b.value()), + _ => + return Err(syn::Error::new( + input.span(), + "Expected array of u8 elements.".to_string(), + )), + }, + _ => + return Err(syn::Error::new( + input.span(), + "Expected array of u8 elements.".to_string(), + )), + } + } + return Ok(InputBytes(bytes)) + }, + Err(_e) => (), + } + // use rust names as a vec of their utf8 bytecode. + match syn::Ident::parse(input) { + Ok(ident) => return Ok(InputBytes(ident.to_string().as_bytes().to_vec())), + Err(_e) => (), + } + Ok(InputBytes(syn::LitByteStr::parse(input)?.value())) + } +} + +impl Parse for MultipleInputBytes { + fn parse(input: ParseStream) -> syn::Result { + let elts = + syn::punctuated::Punctuated::::parse_terminated(input)?; + Ok(MultipleInputBytes(elts.into_iter().map(|elt| elt.0).collect())) + } +} + +pub(super) fn twox_64(bytes: Vec) -> TokenStream { + bytes_to_array(sp_crypto_hashing::twox_64(bytes.as_slice())) +} + +pub(super) fn twox_128(bytes: Vec) -> TokenStream { + bytes_to_array(sp_crypto_hashing::twox_128(bytes.as_slice())) +} + +pub(super) fn blake2b_512(bytes: Vec) -> TokenStream { + bytes_to_array(sp_crypto_hashing::blake2_512(bytes.as_slice())) +} + +pub(super) fn blake2b_256(bytes: Vec) -> TokenStream { + bytes_to_array(sp_crypto_hashing::blake2_256(bytes.as_slice())) +} + +pub(super) fn blake2b_64(bytes: Vec) -> TokenStream { + bytes_to_array(sp_crypto_hashing::blake2_64(bytes.as_slice())) +} + +pub(super) fn keccak_256(bytes: Vec) -> TokenStream { + bytes_to_array(sp_crypto_hashing::keccak_256(bytes.as_slice())) +} + +pub(super) fn keccak_512(bytes: Vec) -> TokenStream { + bytes_to_array(sp_crypto_hashing::keccak_512(bytes.as_slice())) +} + +pub(super) fn sha2_256(bytes: Vec) -> TokenStream { + bytes_to_array(sp_crypto_hashing::sha2_256(bytes.as_slice())) +} + +fn bytes_to_array(bytes: impl IntoIterator) -> TokenStream { + let bytes = bytes.into_iter(); + + quote!( + [ #( #bytes ),* ] + ) + .into() +} diff --git a/substrate/primitives/crypto/pubkeycrypto/proc-macro/src/lib.rs b/substrate/primitives/crypto/pubkeycrypto/proc-macro/src/lib.rs new file mode 100644 index 0000000000000..8a306011fb87c --- /dev/null +++ b/substrate/primitives/crypto/pubkeycrypto/proc-macro/src/lib.rs @@ -0,0 +1,42 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Macros to implement internals of public key crypto modules +//! +use proc_macro::{TokenStream}; +use quote::quote; +use syn::{parse_macro_input, DeriveInput}; + +/// Implement Proof of Possession Generation and Verificition for Crypto Pair type +#[proc_macro_derive(ProofOfPossession)] +pub fn derive_proof_of_possession(input: TokenStream) -> TokenStream { + // Parse the input tokens into a syntax tree + let input = parse_macro_input!(input as DeriveInput); + + // Get the name of the struct or enum to implement the trait for + let name = input.ident; + + // Generate the implementation for the trait + let expanded = quote! { + #[cfg(feature = "full_crypto")] + impl ProofOfPossessionGenerator for #name {} + impl ProofOfPossessionVerifier for #name {} + }; + + // Convert the quote output to a TokenStream + TokenStream::from(expanded) +} diff --git a/substrate/primitives/crypto/pubkeycrypto/src/lib.rs b/substrate/primitives/crypto/pubkeycrypto/src/lib.rs new file mode 100644 index 0000000000000..954dea7aad28d --- /dev/null +++ b/substrate/primitives/crypto/pubkeycrypto/src/lib.rs @@ -0,0 +1,7 @@ +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() {} +} From 946168b0e1b132c0b9573fd455b807994ca00dc0 Mon Sep 17 00:00:00 2001 From: Skalman Date: Wed, 16 Oct 2024 16:09:07 +0000 Subject: [PATCH 2/7] - Implement ProofOfPossessionGenerator for BLS12 keys with test. - Change PoP type to be &[u8] instead of signature so it works for BLS12. --- Cargo.lock | 5 +- Cargo.toml | 2 +- substrate/primitives/core/Cargo.toml | 2 + substrate/primitives/core/src/bls.rs | 77 +++++++++++++++++++++++-- substrate/primitives/core/src/crypto.rs | 19 +++--- 5 files changed, 90 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e6d30a17f39e7..6a1d9fd0e3da8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21843,6 +21843,7 @@ dependencies = [ "secrecy", "serde", "serde_json", + "sha2 0.10.8", "sp-crypto-hashing 0.1.0", "sp-crypto-pubkeycrypto-proc-macro", "sp-debug-derive 14.0.0", @@ -25815,9 +25816,9 @@ checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" [[package]] name = "w3f-bls" -version = "0.1.3" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7335e4c132c28cc43caef6adb339789e599e39adbe78da0c4d547fad48cbc331" +checksum = "6a48c48447120a85b0bdb897ba9426a7aa15b4229498a2e19103e8c9368dd4b2" dependencies = [ "ark-bls12-377", "ark-bls12-381", diff --git a/Cargo.toml b/Cargo.toml index 624aba96be239..cab7c80139830 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1353,7 +1353,7 @@ twox-hash = { version = "1.6.3", default-features = false } unsigned-varint = { version = "0.7.2" } url = { version = "2.4.0" } void = { version = "1.0.2" } -w3f-bls = { version = "0.1.3", default-features = false } +w3f-bls = { version = "0.1.6", default-features = false } wait-timeout = { version = "0.2" } walkdir = { version = "2.5.0" } wasm-bindgen-test = { version = "0.3.19" } diff --git a/substrate/primitives/core/Cargo.toml b/substrate/primitives/core/Cargo.toml index 953196c207101..3e704a6c95183 100644 --- a/substrate/primitives/core/Cargo.toml +++ b/substrate/primitives/core/Cargo.toml @@ -70,6 +70,8 @@ secp256k1 = { features = [ # bls crypto w3f-bls = { optional = true, workspace = true } +sha2 = { workspace = true } + # bandersnatch crypto bandersnatch_vrfs = { git = "https://github.com/w3f/ring-vrf", rev = "0fef826", default-features = false, features = [ "substrate-curves", diff --git a/substrate/primitives/core/src/bls.rs b/substrate/primitives/core/src/bls.rs index 40cf605fbb647..4be5d18089564 100644 --- a/substrate/primitives/core/src/bls.rs +++ b/substrate/primitives/core/src/bls.rs @@ -24,17 +24,22 @@ //! curve. use crate::crypto::{ - CryptoType, DeriveError, DeriveJunction, Pair as TraitPair, PublicBytes, SecretStringError, - SignatureBytes, UncheckedFrom, + CryptoType, DeriveError, DeriveJunction, Pair as TraitPair, ProofOfPossessionGenerator, + ProofOfPossessionVerifier, PublicBytes, SecretStringError, SignatureBytes, UncheckedFrom, }; use alloc::vec::Vec; use w3f_bls::{ - DoublePublicKey, DoublePublicKeyScheme, DoubleSignature, EngineBLS, Keypair, Message, - ProofOfPossession, SecretKey, SerializableToBytes, TinyBLS381, + BLSPoP, DoublePublicKey, DoublePublicKeyScheme, DoubleSignature, EngineBLS, Keypair, Message, + ProofOfPossession as BlsProofOfPossession, + ProofOfPossessionGenerator as BlsProofOfPossessionGenerator, SecretKey, SerializableToBytes, + TinyBLS381, }; +/// Required to generate PoP +use sha2::Sha256; + /// BLS-377 specialized types pub mod bls377 { pub use super::{PUBLIC_KEY_SERIALIZED_SIZE, SIGNATURE_SERIALIZED_SIZE}; @@ -99,6 +104,10 @@ pub const PUBLIC_KEY_SERIALIZED_SIZE: usize = pub const SIGNATURE_SERIALIZED_SIZE: usize = as SerializableToBytes>::SERIALIZED_BYTES_SIZE; +/// Signature serialized size +pub const POP_SERIALIZED_SIZE: usize = + as SerializableToBytes>::SERIALIZED_BYTES_SIZE; + /// A secret seed. /// /// It's not called a "secret key" because ring doesn't expose the secret keys @@ -227,6 +236,18 @@ impl TraitPair for Pair { } } +impl ProofOfPossessionGenerator for Pair { + fn generate_proof_of_possession(&mut self) -> Vec { + let r: [u8; POP_SERIALIZED_SIZE] = + as BlsProofOfPossessionGenerator>>::generate_pok( + &mut self.0, + ) + .to_bytes() + .try_into() + .expect("Signature serializer returns vectors of POP_SERIALIZED_SIZE size"); + r.to_vec() + } +} impl CryptoType for Pair { type Pair = Pair; } @@ -524,6 +545,7 @@ mod tests { fn signature_serialization_works_for_bls381() { signature_serialization_works::(); } + fn signature_serialization_doesnt_panic() { fn deserialize_signature( text: &str, @@ -544,4 +566,51 @@ mod tests { fn signature_serialization_doesnt_panic_for_bls381() { signature_serialization_doesnt_panic::(); } + + fn must_generate_proof_of_possession() { + let mut pair = Pair::::from_seed(b"12345678901234567890123456789012"); + pair.generate_proof_of_possession(); + } + + #[test] + fn must_generate_proof_of_possession_for_bls377() { + must_generate_proof_of_possession::(); + } + + // #[test] + // fn must_generate_proof_of_possession_for_bls381() { + // must_generate_proof_of_possession::(); + // } + + // fn good_proof_of_possession_must_verify() { + // let pair = Pair::::from_seed(b"12345678901234567890123456789012"); + // pair.generate_proof_of_possession(); + // } + + // #[test] + // fn good_proof_of_possession_must_verify_for_bls377() { + // good_proof_of_possession_must_verify::(); + // } + + // #[test] + // fn good_proof_of_possession_must_verify_for_bls381() { + // good_proof_of_possession_must_verify::(); + // } + + // fn proof_of_possession_must_fail_if_prover_does_not_possess_secret_key() { + // let pair = Pair::::from_seed(b"12345678901234567890123456789012"); + // let other_pair = Pair::::from_seed(b"23456789012345678901234567890123"); + // let pop = pair.generate_proof_of_possession(); + // assert_eq!(Pair::::verify(pop, other_pair.public()), false); + // } + + // #[test] + // fn proof_of_possession_must_fail_if_prover_does_not_possess_secret_key_for_bls377() { + // proof_of_possession_must_fail_if_prover_does_not_possess_secret_key::(); + // } + + // #[test] + // fn proof_of_possession_must_fail_if_prover_does_not_possess_secret_key_for_bls381() { + // proof_of_possession_must_fail_if_prover_does_not_possess_secret_key::(); + // } } diff --git a/substrate/primitives/core/src/crypto.rs b/substrate/primitives/core/src/crypto.rs index 9c9c6696533f5..28abd1cb717b4 100644 --- a/substrate/primitives/core/src/crypto.rs +++ b/substrate/primitives/core/src/crypto.rs @@ -37,9 +37,6 @@ pub use ss58_registry::{from_known_address_format, Ss58AddressFormat, Ss58Addres /// Trait to zeroize a memory buffer. pub use zeroize::Zeroize; -use quote::quote; -use syn::{parse_macro_input, DeriveInput}; - pub use crate::{ address_uri::{AddressUri, Error as AddressUriError}, crypto_bytes::{CryptoBytes, PublicBytes, SignatureBytes}, @@ -970,7 +967,7 @@ where Self::Public: Encode, { /// The proof of possession generator is supposed to - /// to produce a signature with unique hash context that should + /// to produce a "signature" with unique hash context that should /// never be used in other signatures. This proves that /// that the secret key is known to the prover. While prevent /// malicious actors to trick an honest party to sign their @@ -978,11 +975,11 @@ where /// - Ristenpart, T., & Yilek, S. (2007). The power of proofs-of-possession: Securing multiparty /// signatures against rogue-key attacks. In , Annual {{International Conference}} on the /// {{Theory}} and {{Applications}} of {{Cryptographic Techniques} (pp. 228–245). : Springer. - fn generate_proof_of_possession(&mut self) -> Self::Signature { + fn generate_proof_of_possession(&mut self) -> Vec { let pub_key_scaled = self.public().as_slice().encode(); let pop_context_tag: &[u8] = b"POP_"; let pop_statement = [pop_context_tag, pub_key_scaled.as_slice()].concat(); - self.sign(pop_statement.as_slice()) + self.sign(pop_statement.as_slice()).to_raw_vec() } } @@ -999,13 +996,19 @@ where /// produced solely for this reason. This proves that /// that the secret key is known to the prover. fn verify_proof_of_possession( - proof_of_possesion: &Self::Signature, + proof_of_possesion: &[u8], allegedly_possessesd_pubkey: &Self::Public, ) -> bool { let pub_key_scaled = allegedly_possessesd_pubkey.encode(); let pop_context_tag = b"POP_"; let pop_statement = [pop_context_tag, pub_key_scaled.as_slice()].concat(); - Self::verify(proof_of_possesion, pop_statement, allegedly_possessesd_pubkey) + let proof_of_possesion_as_signature: Option = + ::from_slice(proof_of_possesion).ok(); + + match proof_of_possesion_as_signature { + Some(signature) => Self::verify(&signature, pop_statement, allegedly_possessesd_pubkey), + _ => false, + } } } From f02da2c25b891c61903707fdd1470812363d78e4 Mon Sep 17 00:00:00 2001 From: Skalman Date: Wed, 16 Oct 2024 14:18:56 -0400 Subject: [PATCH 3/7] Implement ProofOfPossessionVerifier with test for bls crypto --- substrate/primitives/core/src/bls.rs | 99 ++++++++++++++++++---------- 1 file changed, 63 insertions(+), 36 deletions(-) diff --git a/substrate/primitives/core/src/bls.rs b/substrate/primitives/core/src/bls.rs index 4be5d18089564..bcd7adbdacf4a 100644 --- a/substrate/primitives/core/src/bls.rs +++ b/substrate/primitives/core/src/bls.rs @@ -248,6 +248,29 @@ impl ProofOfPossessionGenerator for Pair { r.to_vec() } } + +impl ProofOfPossessionVerifier for Pair { + fn verify_proof_of_possession( + proof_of_possession: &[u8], + allegedly_possessed_pubkey: &Self::Public, + ) -> bool { + let proof_of_possession = BLSPoP::::from_bytes(proof_of_possession); + let allegedly_possessed_pubkey_as_bls_pubkey = + match DoublePublicKey::::from_bytes(&allegedly_possessed_pubkey.0) { + Ok(pk) => pk, + Err(_) => return false, + }; + + match proof_of_possession.ok() { + Some(proof_of_possession) => BlsProofOfPossession::::verify( + &proof_of_possession, + &allegedly_possessed_pubkey_as_bls_pubkey, + ), + _ => false, + } + } +} + impl CryptoType for Pair { type Pair = Pair; } @@ -577,40 +600,44 @@ mod tests { must_generate_proof_of_possession::(); } - // #[test] - // fn must_generate_proof_of_possession_for_bls381() { - // must_generate_proof_of_possession::(); - // } - - // fn good_proof_of_possession_must_verify() { - // let pair = Pair::::from_seed(b"12345678901234567890123456789012"); - // pair.generate_proof_of_possession(); - // } - - // #[test] - // fn good_proof_of_possession_must_verify_for_bls377() { - // good_proof_of_possession_must_verify::(); - // } - - // #[test] - // fn good_proof_of_possession_must_verify_for_bls381() { - // good_proof_of_possession_must_verify::(); - // } - - // fn proof_of_possession_must_fail_if_prover_does_not_possess_secret_key() { - // let pair = Pair::::from_seed(b"12345678901234567890123456789012"); - // let other_pair = Pair::::from_seed(b"23456789012345678901234567890123"); - // let pop = pair.generate_proof_of_possession(); - // assert_eq!(Pair::::verify(pop, other_pair.public()), false); - // } - - // #[test] - // fn proof_of_possession_must_fail_if_prover_does_not_possess_secret_key_for_bls377() { - // proof_of_possession_must_fail_if_prover_does_not_possess_secret_key::(); - // } - - // #[test] - // fn proof_of_possession_must_fail_if_prover_does_not_possess_secret_key_for_bls381() { - // proof_of_possession_must_fail_if_prover_does_not_possess_secret_key::(); - // } + #[test] + fn must_generate_proof_of_possession_for_bls381() { + must_generate_proof_of_possession::(); + } + + fn good_proof_of_possession_must_verify() { + let mut pair = Pair::::from_seed(b"12345678901234567890123456789012"); + let pop = pair.generate_proof_of_possession(); + assert!(Pair::::verify_proof_of_possession(pop.as_slice(), &pair.public())); + } + + #[test] + fn good_proof_of_possession_must_verify_for_bls377() { + good_proof_of_possession_must_verify::(); + } + + #[test] + fn good_proof_of_possession_must_verify_for_bls381() { + good_proof_of_possession_must_verify::(); + } + + fn proof_of_possession_must_fail_if_prover_does_not_possess_secret_key() { + let mut pair = Pair::::from_seed(b"12345678901234567890123456789012"); + let other_pair = Pair::::from_seed(b"23456789012345678901234567890123"); + let pop = pair.generate_proof_of_possession(); + assert_eq!( + Pair::::verify_proof_of_possession(pop.as_slice(), &other_pair.public()), + false + ); + } + + #[test] + fn proof_of_possession_must_fail_if_prover_does_not_possess_secret_key_for_bls377() { + proof_of_possession_must_fail_if_prover_does_not_possess_secret_key::(); + } + + #[test] + fn proof_of_possession_must_fail_if_prover_does_not_possess_secret_key_for_bls381() { + proof_of_possession_must_fail_if_prover_does_not_possess_secret_key::(); + } } From 5737d8c60de1cacd14cbc0927904d2c761e385f5 Mon Sep 17 00:00:00 2001 From: Skalman Date: Wed, 16 Oct 2024 15:01:38 -0400 Subject: [PATCH 4/7] Add proof of possession test for ed25519, sr25519, ecdsa and bandersnatch --- substrate/primitives/core/src/bandersnatch.rs | 9 +++++++++ substrate/primitives/core/src/crypto.rs | 12 ++++++------ substrate/primitives/core/src/ecdsa.rs | 9 +++++++++ substrate/primitives/core/src/ed25519.rs | 9 +++++++++ substrate/primitives/core/src/sr25519.rs | 9 +++++++++ 5 files changed, 42 insertions(+), 6 deletions(-) diff --git a/substrate/primitives/core/src/bandersnatch.rs b/substrate/primitives/core/src/bandersnatch.rs index 54bb86503e68f..b4198cd58791e 100644 --- a/substrate/primitives/core/src/bandersnatch.rs +++ b/substrate/primitives/core/src/bandersnatch.rs @@ -1089,4 +1089,13 @@ mod tests { assert_eq!(enc1, enc2); } + + #[test] + fn good_proof_of_possession_should_work_bad_pop_should_fail() { + let mut pair = Pair::from_seed(b"12345678901234567890123456789012"); + let other_pair = Pair::from_seed(b"23456789012345678901234567890123"); + let pop = pair.generate_proof_of_possession(); + assert!(Pair::verify_proof_of_possession(pop.as_slice(), &pair.public())); + assert_eq!(Pair::verify_proof_of_possession(pop.as_slice(), &other_pair.public()), false); + } } diff --git a/substrate/primitives/core/src/crypto.rs b/substrate/primitives/core/src/crypto.rs index 28abd1cb717b4..b17e7e844dc94 100644 --- a/substrate/primitives/core/src/crypto.rs +++ b/substrate/primitives/core/src/crypto.rs @@ -964,7 +964,7 @@ pub trait Pair: CryptoType + Sized { /// in different trait to provide default behavoir pub trait ProofOfPossessionGenerator: Pair where - Self::Public: Encode, + Self::Public: CryptoType, { /// The proof of possession generator is supposed to /// to produce a "signature" with unique hash context that should @@ -976,9 +976,9 @@ where /// signatures against rogue-key attacks. In , Annual {{International Conference}} on the /// {{Theory}} and {{Applications}} of {{Cryptographic Techniques} (pp. 228–245). : Springer. fn generate_proof_of_possession(&mut self) -> Vec { - let pub_key_scaled = self.public().as_slice().encode(); + let pub_key_as_bytes = self.public().to_raw_vec(); let pop_context_tag: &[u8] = b"POP_"; - let pop_statement = [pop_context_tag, pub_key_scaled.as_slice()].concat(); + let pop_statement = [pop_context_tag, pub_key_as_bytes.as_slice()].concat(); self.sign(pop_statement.as_slice()).to_raw_vec() } } @@ -989,7 +989,7 @@ where /// in different trait (than Public Key) to provide default behavoir pub trait ProofOfPossessionVerifier: Pair where - Self::Public: Encode, + Self::Public: CryptoType, { /// The proof of possession verifier is supposed to /// to verify a signature with unique hash context that is @@ -999,9 +999,9 @@ where proof_of_possesion: &[u8], allegedly_possessesd_pubkey: &Self::Public, ) -> bool { - let pub_key_scaled = allegedly_possessesd_pubkey.encode(); + let pub_key_as_bytes = allegedly_possessesd_pubkey.to_raw_vec(); let pop_context_tag = b"POP_"; - let pop_statement = [pop_context_tag, pub_key_scaled.as_slice()].concat(); + let pop_statement = [pop_context_tag, pub_key_as_bytes.as_slice()].concat(); let proof_of_possesion_as_signature: Option = ::from_slice(proof_of_possesion).ok(); diff --git a/substrate/primitives/core/src/ecdsa.rs b/substrate/primitives/core/src/ecdsa.rs index 66752ca2f1400..ec8fc076b0397 100644 --- a/substrate/primitives/core/src/ecdsa.rs +++ b/substrate/primitives/core/src/ecdsa.rs @@ -632,4 +632,13 @@ mod test { let key = sig.recover_prehashed(&msg).unwrap(); assert_ne!(pair.public(), key); } + + #[test] + fn good_proof_of_possession_should_work_bad_pop_should_fail() { + let mut pair = Pair::from_seed(b"12345678901234567890123456789012"); + let other_pair = Pair::from_seed(b"23456789012345678901234567890123"); + let pop = pair.generate_proof_of_possession(); + assert!(Pair::verify_proof_of_possession(pop.as_slice(), &pair.public())); + assert_eq!(Pair::verify_proof_of_possession(pop.as_slice(), &other_pair.public()), false); + } } diff --git a/substrate/primitives/core/src/ed25519.rs b/substrate/primitives/core/src/ed25519.rs index dc9edce796f9e..4cf1a8c977c26 100644 --- a/substrate/primitives/core/src/ed25519.rs +++ b/substrate/primitives/core/src/ed25519.rs @@ -330,4 +330,13 @@ mod tests { // Poorly-sized assert!(deserialize_signature("\"abc123\"").is_err()); } + + #[test] + fn good_proof_of_possession_should_work_bad_pop_should_fail() { + let mut pair = Pair::from_seed(b"12345678901234567890123456789012"); + let other_pair = Pair::from_seed(b"23456789012345678901234567890123"); + let pop = pair.generate_proof_of_possession(); + assert!(Pair::verify_proof_of_possession(pop.as_slice(), &pair.public())); + assert_eq!(Pair::verify_proof_of_possession(pop.as_slice(), &other_pair.public()), false); + } } diff --git a/substrate/primitives/core/src/sr25519.rs b/substrate/primitives/core/src/sr25519.rs index d6e221870fdac..2291f53693a08 100644 --- a/substrate/primitives/core/src/sr25519.rs +++ b/substrate/primitives/core/src/sr25519.rs @@ -921,4 +921,13 @@ mod tests { assert!(public.vrf_verify(&data, &signature2)); assert_eq!(signature.pre_output, signature2.pre_output); } + + #[test] + fn good_proof_of_possession_should_work_bad_pop_should_fail() { + let mut pair = Pair::from_seed(b"12345678901234567890123456789012"); + let other_pair = Pair::from_seed(b"23456789012345678901234567890123"); + let pop = pair.generate_proof_of_possession(); + assert!(Pair::verify_proof_of_possession(pop.as_slice(), &pair.public())); + assert_eq!(Pair::verify_proof_of_possession(pop.as_slice(), &other_pair.public()), false); + } } From 8d83e975db6e14913a73b86959740974c042f920 Mon Sep 17 00:00:00 2001 From: Skalman Date: Wed, 16 Oct 2024 15:45:16 -0400 Subject: [PATCH 5/7] Implement ProofOfPossession for PairedCrypto type --- substrate/primitives/core/src/crypto.rs | 6 +- .../primitives/core/src/paired_crypto.rs | 76 ++++++++++++++++++- 2 files changed, 78 insertions(+), 4 deletions(-) diff --git a/substrate/primitives/core/src/crypto.rs b/substrate/primitives/core/src/crypto.rs index b17e7e844dc94..a36d01823205d 100644 --- a/substrate/primitives/core/src/crypto.rs +++ b/substrate/primitives/core/src/crypto.rs @@ -983,6 +983,9 @@ where } } +///The context which attached to pop message to attest its purpose +const POP_CONTEXT_TAG: &[u8; 4] = b"POP_"; + /// Pair which is able to generate proof of possession. While you don't need a keypair /// to verify a proof of possession (you only need a public key) we constrain on Pair /// to use the Public and Signature types associated to Pair. This is implemented @@ -1000,8 +1003,7 @@ where allegedly_possessesd_pubkey: &Self::Public, ) -> bool { let pub_key_as_bytes = allegedly_possessesd_pubkey.to_raw_vec(); - let pop_context_tag = b"POP_"; - let pop_statement = [pop_context_tag, pub_key_as_bytes.as_slice()].concat(); + let pop_statement = [POP_CONTEXT_TAG, pub_key_as_bytes.as_slice()].concat(); let proof_of_possesion_as_signature: Option = ::from_slice(proof_of_possesion).ok(); diff --git a/substrate/primitives/core/src/paired_crypto.rs b/substrate/primitives/core/src/paired_crypto.rs index bf5b26366571a..93cef7df766a2 100644 --- a/substrate/primitives/core/src/paired_crypto.rs +++ b/substrate/primitives/core/src/paired_crypto.rs @@ -20,8 +20,9 @@ use core::marker::PhantomData; use crate::crypto::{ - ByteArray, CryptoType, DeriveError, DeriveJunction, Pair as PairT, Public as PublicT, - PublicBytes, SecretStringError, Signature as SignatureT, SignatureBytes, UncheckedFrom, + ByteArray, CryptoType, DeriveError, DeriveJunction, Pair as PairT, ProofOfPossessionGenerator, + ProofOfPossessionVerifier, Public as PublicT, PublicBytes, SecretStringError, + Signature as SignatureT, SignatureBytes, UncheckedFrom, }; use alloc::vec::Vec; @@ -387,6 +388,68 @@ where } } +impl< + LeftPair: PairT + ProofOfPossessionGenerator, + RightPair: PairT + ProofOfPossessionGenerator, + const PUBLIC_KEY_LEN: usize, + const SIGNATURE_LEN: usize, + SubTag: PairedCryptoSubTagBound, + > ProofOfPossessionGenerator for Pair +where + Pair: CryptoType, + Public: PublicT, + Signature: SignatureT, + LeftPair::Seed: From + Into, + RightPair::Seed: From + Into, +{ + fn generate_proof_of_possession(&mut self) -> Vec { + [self.left.generate_proof_of_possession(), self.right.generate_proof_of_possession()] + .concat() + } +} + +///This requires that the PoP of LEFT is of LeftPair::Signature +///This is the case for current implemented cases but does not +///holds in general +impl< + LeftPair: PairT + ProofOfPossessionVerifier, + RightPair: PairT + ProofOfPossessionVerifier, + const PUBLIC_KEY_LEN: usize, + const SIGNATURE_LEN: usize, + SubTag: PairedCryptoSubTagBound, + > ProofOfPossessionVerifier for Pair +where + Pair: CryptoType, + Public: PublicT, + Signature: SignatureT, + LeftPair::Seed: From + Into, + RightPair::Seed: From + Into, +{ + fn verify_proof_of_possession( + proof_of_possession: &[u8], + allegedly_possessed_pubkey: &Self::Public, + ) -> bool { + if proof_of_possession.len() < LeftPair::Signature::LEN { + return false + }; + + let Ok(left_pub) = allegedly_possessed_pubkey.0[..LeftPair::Public::LEN].try_into() else { + return false + }; + let left_pop = &proof_of_possession[0..LeftPair::Signature::LEN]; + + if !LeftPair::verify_proof_of_possession(&left_pop, &left_pub) { + return false + } + + let Ok(right_pub) = allegedly_possessed_pubkey.0[LeftPair::Public::LEN..].try_into() else { + return false + }; + let right_pop = &proof_of_possession[LeftPair::Signature::LEN..]; + RightPair::verify_proof_of_possession(&right_pop, &right_pub) + } +} + // Test set exercising the (ECDSA,BLS12-377) implementation #[cfg(all(test, feature = "bls-experimental"))] mod tests { @@ -628,4 +691,13 @@ mod tests { let decoded_signature = Signature::decode(&mut encoded_signature.as_slice()).unwrap(); assert_eq!(signature, decoded_signature) } + + #[test] + fn good_proof_of_possession_should_work_bad_pop_should_fail() { + let mut pair = Pair::from_seed(b"12345678901234567890123456789012"); + let other_pair = Pair::from_seed(b"23456789012345678901234567890123"); + let pop = pair.generate_proof_of_possession(); + assert!(Pair::verify_proof_of_possession(pop.as_slice(), &pair.public())); + assert_eq!(Pair::verify_proof_of_possession(pop.as_slice(), &other_pair.public()), false); + } } From a72271c9c3a08a9dbe29f979f71b0f02239ec085 Mon Sep 17 00:00:00 2001 From: Skalman Date: Sun, 3 Nov 2024 06:58:51 -0500 Subject: [PATCH 6/7] ProofOfPossesion shall be a signature. BLS tests fails because we are enforcing conetxt --- Cargo.lock | 4 +- Cargo.toml | 2 +- substrate/primitives/core/src/bandersnatch.rs | 4 +- substrate/primitives/core/src/bls.rs | 60 ++++++++++--------- substrate/primitives/core/src/crypto.rs | 14 ++--- substrate/primitives/core/src/ecdsa.rs | 4 +- substrate/primitives/core/src/ed25519.rs | 4 +- .../primitives/core/src/paired_crypto.rs | 29 +++++---- substrate/primitives/core/src/sr25519.rs | 4 +- 9 files changed, 66 insertions(+), 59 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6a1d9fd0e3da8..0722ab236d8ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25816,9 +25816,9 @@ checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" [[package]] name = "w3f-bls" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a48c48447120a85b0bdb897ba9426a7aa15b4229498a2e19103e8c9368dd4b2" +checksum = "70a3028804c8bbae2a97a15b71ffc0e308c4b01a520994aafa77d56e94e19024" dependencies = [ "ark-bls12-377", "ark-bls12-381", diff --git a/Cargo.toml b/Cargo.toml index cab7c80139830..59c522ad909b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1353,7 +1353,7 @@ twox-hash = { version = "1.6.3", default-features = false } unsigned-varint = { version = "0.7.2" } url = { version = "2.4.0" } void = { version = "1.0.2" } -w3f-bls = { version = "0.1.6", default-features = false } +w3f-bls = { version = "0.1.8", default-features = false } wait-timeout = { version = "0.2" } walkdir = { version = "2.5.0" } wasm-bindgen-test = { version = "0.3.19" } diff --git a/substrate/primitives/core/src/bandersnatch.rs b/substrate/primitives/core/src/bandersnatch.rs index b4198cd58791e..2c08c4da62f1a 100644 --- a/substrate/primitives/core/src/bandersnatch.rs +++ b/substrate/primitives/core/src/bandersnatch.rs @@ -1095,7 +1095,7 @@ mod tests { let mut pair = Pair::from_seed(b"12345678901234567890123456789012"); let other_pair = Pair::from_seed(b"23456789012345678901234567890123"); let pop = pair.generate_proof_of_possession(); - assert!(Pair::verify_proof_of_possession(pop.as_slice(), &pair.public())); - assert_eq!(Pair::verify_proof_of_possession(pop.as_slice(), &other_pair.public()), false); + assert!(Pair::verify_proof_of_possession(&pop, &pair.public())); + assert_eq!(Pair::verify_proof_of_possession(&pop, &other_pair.public()), false); } } diff --git a/substrate/primitives/core/src/bls.rs b/substrate/primitives/core/src/bls.rs index bcd7adbdacf4a..8add5d4819632 100644 --- a/substrate/primitives/core/src/bls.rs +++ b/substrate/primitives/core/src/bls.rs @@ -31,8 +31,8 @@ use crate::crypto::{ use alloc::vec::Vec; use w3f_bls::{ - BLSPoP, DoublePublicKey, DoublePublicKeyScheme, DoubleSignature, EngineBLS, Keypair, Message, - ProofOfPossession as BlsProofOfPossession, + DoublePublicKey, DoublePublicKeyScheme, DoubleSignature, EngineBLS, Keypair, Message, + NuggetBLSnCPPoP, ProofOfPossession as BlsProofOfPossession, ProofOfPossessionGenerator as BlsProofOfPossessionGenerator, SecretKey, SerializableToBytes, TinyBLS381, }; @@ -106,7 +106,7 @@ pub const SIGNATURE_SERIALIZED_SIZE: usize = /// Signature serialized size pub const POP_SERIALIZED_SIZE: usize = - as SerializableToBytes>::SERIALIZED_BYTES_SIZE; + as SerializableToBytes>::SERIALIZED_BYTES_SIZE; /// A secret seed. /// @@ -237,37 +237,44 @@ impl TraitPair for Pair { } impl ProofOfPossessionGenerator for Pair { - fn generate_proof_of_possession(&mut self) -> Vec { - let r: [u8; POP_SERIALIZED_SIZE] = - as BlsProofOfPossessionGenerator>>::generate_pok( - &mut self.0, - ) - .to_bytes() - .try_into() - .expect("Signature serializer returns vectors of POP_SERIALIZED_SIZE size"); - r.to_vec() + fn generate_proof_of_possession(&mut self) -> Self::Signature { + let r: [u8; SIGNATURE_SERIALIZED_SIZE] = as BlsProofOfPossessionGenerator< + T, + Sha256, + DoublePublicKey, + NuggetBLSnCPPoP, + >>::generate_pok(&mut self.0) + .to_bytes() + .try_into() + .expect("NuggetBLSnCPPoP serializer returns vectors of SIGNATURE_SERIALIZED_SIZE size"); + Self::Signature::unchecked_from(r) } } -impl ProofOfPossessionVerifier for Pair { +impl ProofOfPossessionVerifier for Pair +where + Pair: TraitPair, +{ fn verify_proof_of_possession( - proof_of_possession: &[u8], + proof_of_possession: &Self::Signature, allegedly_possessed_pubkey: &Self::Public, ) -> bool { - let proof_of_possession = BLSPoP::::from_bytes(proof_of_possession); + let proof_of_possession = + match NuggetBLSnCPPoP::::from_bytes(proof_of_possession.as_ref()) { + Ok(s) => s, + Err(_) => return false, + }; + let allegedly_possessed_pubkey_as_bls_pubkey = - match DoublePublicKey::::from_bytes(&allegedly_possessed_pubkey.0) { + match DoublePublicKey::::from_bytes(allegedly_possessed_pubkey.as_ref()) { Ok(pk) => pk, Err(_) => return false, }; - match proof_of_possession.ok() { - Some(proof_of_possession) => BlsProofOfPossession::::verify( - &proof_of_possession, - &allegedly_possessed_pubkey_as_bls_pubkey, - ), - _ => false, - } + BlsProofOfPossession::::verify( + &proof_of_possession, + &allegedly_possessed_pubkey_as_bls_pubkey, + ) } } @@ -608,7 +615,7 @@ mod tests { fn good_proof_of_possession_must_verify() { let mut pair = Pair::::from_seed(b"12345678901234567890123456789012"); let pop = pair.generate_proof_of_possession(); - assert!(Pair::::verify_proof_of_possession(pop.as_slice(), &pair.public())); + assert!(Pair::::verify_proof_of_possession(&pop, &pair.public())); } #[test] @@ -625,10 +632,7 @@ mod tests { let mut pair = Pair::::from_seed(b"12345678901234567890123456789012"); let other_pair = Pair::::from_seed(b"23456789012345678901234567890123"); let pop = pair.generate_proof_of_possession(); - assert_eq!( - Pair::::verify_proof_of_possession(pop.as_slice(), &other_pair.public()), - false - ); + assert_eq!(Pair::::verify_proof_of_possession(&pop, &other_pair.public()), false); } #[test] diff --git a/substrate/primitives/core/src/crypto.rs b/substrate/primitives/core/src/crypto.rs index a36d01823205d..c954d889e7d0c 100644 --- a/substrate/primitives/core/src/crypto.rs +++ b/substrate/primitives/core/src/crypto.rs @@ -975,11 +975,11 @@ where /// - Ristenpart, T., & Yilek, S. (2007). The power of proofs-of-possession: Securing multiparty /// signatures against rogue-key attacks. In , Annual {{International Conference}} on the /// {{Theory}} and {{Applications}} of {{Cryptographic Techniques} (pp. 228–245). : Springer. - fn generate_proof_of_possession(&mut self) -> Vec { + fn generate_proof_of_possession(&mut self) -> Self::Signature { let pub_key_as_bytes = self.public().to_raw_vec(); let pop_context_tag: &[u8] = b"POP_"; let pop_statement = [pop_context_tag, pub_key_as_bytes.as_slice()].concat(); - self.sign(pop_statement.as_slice()).to_raw_vec() + self.sign(pop_statement.as_slice()) } } @@ -999,18 +999,12 @@ where /// produced solely for this reason. This proves that /// that the secret key is known to the prover. fn verify_proof_of_possession( - proof_of_possesion: &[u8], + proof_of_possession: &Self::Signature, allegedly_possessesd_pubkey: &Self::Public, ) -> bool { let pub_key_as_bytes = allegedly_possessesd_pubkey.to_raw_vec(); let pop_statement = [POP_CONTEXT_TAG, pub_key_as_bytes.as_slice()].concat(); - let proof_of_possesion_as_signature: Option = - ::from_slice(proof_of_possesion).ok(); - - match proof_of_possesion_as_signature { - Some(signature) => Self::verify(&signature, pop_statement, allegedly_possessesd_pubkey), - _ => false, - } + Self::verify(&proof_of_possession, pop_statement, allegedly_possessesd_pubkey) } } diff --git a/substrate/primitives/core/src/ecdsa.rs b/substrate/primitives/core/src/ecdsa.rs index ec8fc076b0397..52f365336c860 100644 --- a/substrate/primitives/core/src/ecdsa.rs +++ b/substrate/primitives/core/src/ecdsa.rs @@ -638,7 +638,7 @@ mod test { let mut pair = Pair::from_seed(b"12345678901234567890123456789012"); let other_pair = Pair::from_seed(b"23456789012345678901234567890123"); let pop = pair.generate_proof_of_possession(); - assert!(Pair::verify_proof_of_possession(pop.as_slice(), &pair.public())); - assert_eq!(Pair::verify_proof_of_possession(pop.as_slice(), &other_pair.public()), false); + assert!(Pair::verify_proof_of_possession(&pop, &pair.public())); + assert_eq!(Pair::verify_proof_of_possession(&pop, &other_pair.public()), false); } } diff --git a/substrate/primitives/core/src/ed25519.rs b/substrate/primitives/core/src/ed25519.rs index 4cf1a8c977c26..78eaaeed8fb46 100644 --- a/substrate/primitives/core/src/ed25519.rs +++ b/substrate/primitives/core/src/ed25519.rs @@ -336,7 +336,7 @@ mod tests { let mut pair = Pair::from_seed(b"12345678901234567890123456789012"); let other_pair = Pair::from_seed(b"23456789012345678901234567890123"); let pop = pair.generate_proof_of_possession(); - assert!(Pair::verify_proof_of_possession(pop.as_slice(), &pair.public())); - assert_eq!(Pair::verify_proof_of_possession(pop.as_slice(), &other_pair.public()), false); + assert!(Pair::verify_proof_of_possession(&pop, &pair.public())); + assert_eq!(Pair::verify_proof_of_possession(&pop, &other_pair.public()), false); } } diff --git a/substrate/primitives/core/src/paired_crypto.rs b/substrate/primitives/core/src/paired_crypto.rs index 93cef7df766a2..ab7201f9be078 100644 --- a/substrate/primitives/core/src/paired_crypto.rs +++ b/substrate/primitives/core/src/paired_crypto.rs @@ -402,9 +402,18 @@ where LeftPair::Seed: From + Into, RightPair::Seed: From + Into, { - fn generate_proof_of_possession(&mut self) -> Vec { - [self.left.generate_proof_of_possession(), self.right.generate_proof_of_possession()] + fn generate_proof_of_possession(&mut self) -> Self::Signature { + let mut raw: [u8; SIGNATURE_LEN] = [0u8; SIGNATURE_LEN]; + + raw.copy_from_slice( + [ + self.left.generate_proof_of_possession().to_raw_vec(), + self.right.generate_proof_of_possession().to_raw_vec(), + ] .concat() + .as_slice(), + ); + Self::Signature::unchecked_from(raw) } } @@ -426,17 +435,15 @@ where RightPair::Seed: From + Into, { fn verify_proof_of_possession( - proof_of_possession: &[u8], + proof_of_possession: &Self::Signature, allegedly_possessed_pubkey: &Self::Public, ) -> bool { - if proof_of_possession.len() < LeftPair::Signature::LEN { + let Ok(left_pub) = allegedly_possessed_pubkey.0[..LeftPair::Public::LEN].try_into() else { return false }; - - let Ok(left_pub) = allegedly_possessed_pubkey.0[..LeftPair::Public::LEN].try_into() else { + let Ok(left_pop) = proof_of_possession.0[0..LeftPair::Signature::LEN].try_into() else { return false }; - let left_pop = &proof_of_possession[0..LeftPair::Signature::LEN]; if !LeftPair::verify_proof_of_possession(&left_pop, &left_pub) { return false @@ -445,7 +452,9 @@ where let Ok(right_pub) = allegedly_possessed_pubkey.0[LeftPair::Public::LEN..].try_into() else { return false }; - let right_pop = &proof_of_possession[LeftPair::Signature::LEN..]; + let Ok(right_pop) = proof_of_possession.0[LeftPair::Signature::LEN..].try_into() else { + return false + }; RightPair::verify_proof_of_possession(&right_pop, &right_pub) } } @@ -697,7 +706,7 @@ mod tests { let mut pair = Pair::from_seed(b"12345678901234567890123456789012"); let other_pair = Pair::from_seed(b"23456789012345678901234567890123"); let pop = pair.generate_proof_of_possession(); - assert!(Pair::verify_proof_of_possession(pop.as_slice(), &pair.public())); - assert_eq!(Pair::verify_proof_of_possession(pop.as_slice(), &other_pair.public()), false); + assert!(Pair::verify_proof_of_possession(&pop, &pair.public())); + assert_eq!(Pair::verify_proof_of_possession(&pop, &other_pair.public()), false); } } diff --git a/substrate/primitives/core/src/sr25519.rs b/substrate/primitives/core/src/sr25519.rs index 2291f53693a08..dad94ab76ebf9 100644 --- a/substrate/primitives/core/src/sr25519.rs +++ b/substrate/primitives/core/src/sr25519.rs @@ -927,7 +927,7 @@ mod tests { let mut pair = Pair::from_seed(b"12345678901234567890123456789012"); let other_pair = Pair::from_seed(b"23456789012345678901234567890123"); let pop = pair.generate_proof_of_possession(); - assert!(Pair::verify_proof_of_possession(pop.as_slice(), &pair.public())); - assert_eq!(Pair::verify_proof_of_possession(pop.as_slice(), &other_pair.public()), false); + assert!(Pair::verify_proof_of_possession(&pop, &pair.public())); + assert_eq!(Pair::verify_proof_of_possession(&pop, &other_pair.public()), false); } } From 286ce0a1e2b54755374426e130389dfd71616310 Mon Sep 17 00:00:00 2001 From: Skalman Date: Tue, 5 Nov 2024 07:11:48 -0500 Subject: [PATCH 7/7] upgrade bls signature test vectors after enforcing ciphersuit context --- substrate/primitives/core/src/bls.rs | 11 +++++------ substrate/primitives/core/src/paired_crypto.rs | 4 ++-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/substrate/primitives/core/src/bls.rs b/substrate/primitives/core/src/bls.rs index 8add5d4819632..f6f2efc47ee20 100644 --- a/substrate/primitives/core/src/bls.rs +++ b/substrate/primitives/core/src/bls.rs @@ -352,8 +352,6 @@ mod tests { hex_expected_signature: &str, ) { let public = pair.public(); - let public_bytes: &[u8] = public.as_ref(); - println!("pub key is: {:?}", array_bytes::bytes2hex("", public_bytes)); assert_eq!( public, Public::unchecked_from(array_bytes::hex2array_unchecked(hex_expected_pub_key)) @@ -363,6 +361,7 @@ mod tests { let expected_signature = Signature::unchecked_from(expected_signature_bytes); let signature = pair.sign(&message[..]); + assert!(signature == expected_signature); assert!(Pair::verify(&signature, &message[..], &public)); } @@ -374,7 +373,7 @@ mod tests { )); test_vector_should_work(pair, "7a84ca8ce4c37c93c95ecee6a3c0c9a7b9c225093cf2f12dc4f69cbfb847ef9424a18f5755d5a742247d386ff2aabb806bcf160eff31293ea9616976628f77266c8a8cc1d8753be04197bd6cdd8c5c87a148f782c4c1568d599b48833fd539001e580cff64bbc71850605433fcd051f3afc3b74819786f815ffb5272030a8d03e5df61e6183f8fd8ea85f26defa83400", - "d1e3013161991e142d8751017d4996209c2ff8a9ee160f373733eda3b4b785ba6edce9f45f87104bbe07aa6aa6eb2780aa705efb2c13d3b317d6409d159d23bdc7cdd5c2a832d1551cf49d811d49c901495e527dbd532e3a462335ce2686009104aba7bc11c5b22be78f3198d2727a0b" + "124571b4bf23083b5d07e720fde0a984d4d592868156ece77487e97a1ba4b29397dbdc454f13e3aed1ad4b6a99af2501c68ab88ec0495f962a4f55c7c460275a8d356cfa344c27778ca4c641bd9a3604ce5c28f9ed566e1d29bf3b5d3591e46ae28be3ece035e8e4db53a40fc5826002" ) } @@ -385,7 +384,7 @@ mod tests { )); test_vector_should_work(pair, "88ff6c3a32542bc85f2adf1c490a929b7fcee50faeb95af9a036349390e9b3ea7326247c4fc4ebf88050688fd6265de0806284eec09ba0949f5df05dc93a787a14509749f36e4a0981bb748d953435483740907bb5c2fe8ffd97e8509e1a038b05fb08488db628ea0638b8d48c3ddf62ed437edd8b23d5989d6c65820fc70f80fb39b486a3766813e021124aec29a566", - "8c29473f44ac4f0a8ac4dc8c8da09adf9d2faa2dbe0cfdce3ce7c920714196a1b7bf48dc05048e453c161ebc2db9f44fae060b3be77e14e66d1a5262f14d3da0c3a18e650018761a7402b31abc7dd803d466bdcb71bc28c77eb73c610cbff53c00130b79116831e520a04a8ef6630e6f" + "8f4fe16cbb1b7f26ddbfbcde864a3c2f68802fbca5bd59920a135ed7e0f74cd9ba160e61c85e9acee3b4fe277862f226e60ac1958b57ed4487daf4673af420e8bf036ee8169190a927ede2e8eb3d6600633c69b2a84eb017473988fdfde082e150cbef05b77018c1f8ccc06da9e80421" ) } @@ -398,7 +397,7 @@ mod tests { .unwrap(); test_vector_should_work(pair, "7a84ca8ce4c37c93c95ecee6a3c0c9a7b9c225093cf2f12dc4f69cbfb847ef9424a18f5755d5a742247d386ff2aabb806bcf160eff31293ea9616976628f77266c8a8cc1d8753be04197bd6cdd8c5c87a148f782c4c1568d599b48833fd539001e580cff64bbc71850605433fcd051f3afc3b74819786f815ffb5272030a8d03e5df61e6183f8fd8ea85f26defa83400", - "d1e3013161991e142d8751017d4996209c2ff8a9ee160f373733eda3b4b785ba6edce9f45f87104bbe07aa6aa6eb2780aa705efb2c13d3b317d6409d159d23bdc7cdd5c2a832d1551cf49d811d49c901495e527dbd532e3a462335ce2686009104aba7bc11c5b22be78f3198d2727a0b" + "124571b4bf23083b5d07e720fde0a984d4d592868156ece77487e97a1ba4b29397dbdc454f13e3aed1ad4b6a99af2501c68ab88ec0495f962a4f55c7c460275a8d356cfa344c27778ca4c641bd9a3604ce5c28f9ed566e1d29bf3b5d3591e46ae28be3ece035e8e4db53a40fc5826002" ) } @@ -411,7 +410,7 @@ mod tests { .unwrap(); test_vector_should_work(pair, "88ff6c3a32542bc85f2adf1c490a929b7fcee50faeb95af9a036349390e9b3ea7326247c4fc4ebf88050688fd6265de0806284eec09ba0949f5df05dc93a787a14509749f36e4a0981bb748d953435483740907bb5c2fe8ffd97e8509e1a038b05fb08488db628ea0638b8d48c3ddf62ed437edd8b23d5989d6c65820fc70f80fb39b486a3766813e021124aec29a566", - "8c29473f44ac4f0a8ac4dc8c8da09adf9d2faa2dbe0cfdce3ce7c920714196a1b7bf48dc05048e453c161ebc2db9f44fae060b3be77e14e66d1a5262f14d3da0c3a18e650018761a7402b31abc7dd803d466bdcb71bc28c77eb73c610cbff53c00130b79116831e520a04a8ef6630e6f" + "8f4fe16cbb1b7f26ddbfbcde864a3c2f68802fbca5bd59920a135ed7e0f74cd9ba160e61c85e9acee3b4fe277862f226e60ac1958b57ed4487daf4673af420e8bf036ee8169190a927ede2e8eb3d6600633c69b2a84eb017473988fdfde082e150cbef05b77018c1f8ccc06da9e80421" ) } diff --git a/substrate/primitives/core/src/paired_crypto.rs b/substrate/primitives/core/src/paired_crypto.rs index ab7201f9be078..ac5f5526338e7 100644 --- a/substrate/primitives/core/src/paired_crypto.rs +++ b/substrate/primitives/core/src/paired_crypto.rs @@ -547,7 +547,7 @@ mod tests { ); let message = b""; let signature = - array_bytes::hex2array_unchecked("3dde91174bd9359027be59a428b8146513df80a2a3c7eda2194f64de04a69ab97b753169e94db6ffd50921a2668a48b94ca11e3d32c1ff19cfe88890aa7e8f3c00d1e3013161991e142d8751017d4996209c2ff8a9ee160f373733eda3b4b785ba6edce9f45f87104bbe07aa6aa6eb2780aa705efb2c13d3b317d6409d159d23bdc7cdd5c2a832d1551cf49d811d49c901495e527dbd532e3a462335ce2686009104aba7bc11c5b22be78f3198d2727a0b" + array_bytes::hex2array_unchecked("3dde91174bd9359027be59a428b8146513df80a2a3c7eda2194f64de04a69ab97b753169e94db6ffd50921a2668a48b94ca11e3d32c1ff19cfe88890aa7e8f3c00124571b4bf23083b5d07e720fde0a984d4d592868156ece77487e97a1ba4b29397dbdc454f13e3aed1ad4b6a99af2501c68ab88ec0495f962a4f55c7c460275a8d356cfa344c27778ca4c641bd9a3604ce5c28f9ed566e1d29bf3b5d3591e46ae28be3ece035e8e4db53a40fc5826002" ); let signature = Signature::unchecked_from(signature); assert!(pair.sign(&message[..]) == signature); @@ -571,7 +571,7 @@ mod tests { ); let message = b""; let signature = - array_bytes::hex2array_unchecked("3dde91174bd9359027be59a428b8146513df80a2a3c7eda2194f64de04a69ab97b753169e94db6ffd50921a2668a48b94ca11e3d32c1ff19cfe88890aa7e8f3c00d1e3013161991e142d8751017d4996209c2ff8a9ee160f373733eda3b4b785ba6edce9f45f87104bbe07aa6aa6eb2780aa705efb2c13d3b317d6409d159d23bdc7cdd5c2a832d1551cf49d811d49c901495e527dbd532e3a462335ce2686009104aba7bc11c5b22be78f3198d2727a0b" + array_bytes::hex2array_unchecked("3dde91174bd9359027be59a428b8146513df80a2a3c7eda2194f64de04a69ab97b753169e94db6ffd50921a2668a48b94ca11e3d32c1ff19cfe88890aa7e8f3c00124571b4bf23083b5d07e720fde0a984d4d592868156ece77487e97a1ba4b29397dbdc454f13e3aed1ad4b6a99af2501c68ab88ec0495f962a4f55c7c460275a8d356cfa344c27778ca4c641bd9a3604ce5c28f9ed566e1d29bf3b5d3591e46ae28be3ece035e8e4db53a40fc5826002" ); let signature = Signature::unchecked_from(signature); assert!(pair.sign(&message[..]) == signature);