From 9375040d76f5b358d1b8931af0d0e819edadbf8b Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Tue, 11 Apr 2023 10:18:44 +0200 Subject: [PATCH 01/22] Introduce bandersnatch-vrfs (temporary) --- Cargo.lock | 200 +++++++++++++++- Cargo.toml | 3 + primitives/core/Cargo.toml | 4 +- primitives/core/src/bandersnatch.rs | 359 ++++++++++++++++++++++++++++ primitives/core/src/crypto.rs | 24 +- primitives/core/src/lib.rs | 3 +- 6 files changed, 573 insertions(+), 20 deletions(-) create mode 100644 primitives/core/src/bandersnatch.rs diff --git a/Cargo.lock b/Cargo.lock index 7fd0122f3cc0f..3af1414088948 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -230,6 +230,136 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" +[[package]] +name = "ark-bls12-381" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c775f0d12169cba7aae4caeb547bb6a50781c7449a8aa53793827c9ec4abf488" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-serialize", + "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-ed-on-bls12-381-bandersnatch" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9cde0f2aa063a2a5c28d39b47761aa102bda7c13c84fc118a61b87c7b2f785c" +dependencies = [ + "ark-bls12-381", + "ark-ec", + "ark-ff", + "ark-std", +] + +[[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 0.10.6", + "itertools", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.4.0", + "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", +] + +[[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", +] + +[[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 0.10.6", + "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", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + [[package]] name = "array-bytes" version = "4.2.0" @@ -472,6 +602,23 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "bandersnatch_vrfs" +version = "0.0.1" +dependencies = [ + "ark-bls12-381", + "ark-ec", + "ark-ed-on-bls12-381-bandersnatch", + "ark-ff", + "ark-serialize", + "ark-serialize-derive", + "ark-std", + "dleq_vrf", + "merlin 3.0.0", + "rand_core 0.6.4", + "sha2 0.10.6", +] + [[package]] name = "base-x" version = "0.2.11" @@ -1639,6 +1786,17 @@ dependencies = [ "rusticata-macros", ] +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "derive-syn-parse" version = "0.1.5" @@ -1791,6 +1949,23 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "210ec60ae7d710bed8683e333e9d2855a8a56a3e9892b38bad3bb0d4d29b0d5e" +[[package]] +name = "dleq_vrf" +version = "0.0.1" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-serialize-derive", + "ark-std", + "arrayref", + "arrayvec 0.7.2", + "merlin 3.0.0", + "rand_chacha 0.3.1", + "rand_core 0.6.4", + "zeroize", +] + [[package]] name = "doc-comment" version = "0.3.3" @@ -4584,6 +4759,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "merlin" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" +dependencies = [ + "byteorder", + "keccak", + "rand_core 0.6.4", + "zeroize", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -8505,7 +8692,7 @@ dependencies = [ "fork-tree", "futures", "log", - "merlin", + "merlin 2.0.1", "num-bigint", "num-rational", "num-traits", @@ -9678,7 +9865,7 @@ dependencies = [ "arrayvec 0.5.2", "curve25519-dalek 2.1.3", "getrandom 0.1.16", - "merlin", + "merlin 2.0.1", "rand 0.7.3", "rand_core 0.5.1", "sha2 0.8.2", @@ -10259,7 +10446,7 @@ name = "sp-consensus-babe" version = "0.10.0-dev" dependencies = [ "async-trait", - "merlin", + "merlin 2.0.1", "parity-scale-codec", "scale-info", "serde", @@ -10352,6 +10539,7 @@ name = "sp-core" version = "7.0.0" dependencies = [ "array-bytes", + "bandersnatch_vrfs", "bitflags", "blake2", "bounded-collections", @@ -10366,7 +10554,7 @@ dependencies = [ "lazy_static", "libsecp256k1", "log", - "merlin", + "merlin 3.0.0", "parity-scale-codec", "parking_lot 0.12.1", "primitive-types", @@ -10498,7 +10686,7 @@ name = "sp-keystore" version = "0.13.0" dependencies = [ "futures", - "merlin", + "merlin 2.0.1", "parity-scale-codec", "parking_lot 0.12.1", "rand 0.7.3", @@ -11989,7 +12177,7 @@ checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ "cfg-if", "digest 0.10.6", - "rand 0.8.5", + "rand 0.7.3", "static_assertions", ] diff --git a/Cargo.toml b/Cargo.toml index de562ad79e47e..dc2b736c8814c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -326,3 +326,6 @@ inherits = "release" lto = "fat" # https://doc.rust-lang.org/rustc/codegen-options/index.html#codegen-units codegen-units = 1 + +[patch.crates-io] +dleq_vrf = { path = "/mnt/ssd/users/develop/w3f/ring-vrf/dleq_vrf" } diff --git a/primitives/core/Cargo.toml b/primitives/core/Cargo.toml index 9b253fd154675..101beb9870498 100644 --- a/primitives/core/Cargo.toml +++ b/primitives/core/Cargo.toml @@ -13,6 +13,8 @@ documentation = "https://docs.rs/sp-core" targets = ["x86_64-unknown-linux-gnu"] [dependencies] +# bandersnatch_vrfs = { git = "https://github.com/w3f/ring-vrf" } +bandersnatch_vrfs = { path = "/mnt/ssd/users/develop/w3f/ring-vrf/bandersnatch_vrfs" } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive","max-encoded-len"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } @@ -46,7 +48,7 @@ ed25519-zebra = { version = "3.1.0", default-features = false, optional = true } blake2 = { version = "0.10.4", default-features = false, optional = true } schnorrkel = { version = "0.9.1", features = ["preaudit_deprecated", "u64_backend"], default-features = false, optional = true } libsecp256k1 = { version = "0.7", default-features = false, features = ["static-context"], optional = true } -merlin = { version = "2.0", default-features = false, optional = true } +merlin = { version = "3.0", default-features = false, optional = true } secp256k1 = { version = "0.24.0", default-features = false, features = ["recovery", "alloc"], optional = true } ss58-registry = { version = "1.34.0", default-features = false } sp-core-hashing = { version = "5.0.0", path = "./hashing", default-features = false, optional = true } diff --git a/primitives/core/src/bandersnatch.rs b/primitives/core/src/bandersnatch.rs new file mode 100644 index 0000000000000..92412e33bd564 --- /dev/null +++ b/primitives/core/src/bandersnatch.rs @@ -0,0 +1,359 @@ +// 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. + +//! TODO DOCS. + +#![allow(unused)] + +#[cfg(feature = "std")] +use crate::crypto::Ss58Codec; +#[cfg(feature = "full_crypto")] +use crate::crypto::{DeriveError, DeriveJunction, Pair as TraitPair, SecretStringError}; +use bandersnatch_vrfs::{PublicKey, SecretKey, ThinVrfSignature}; +#[cfg(feature = "full_crypto")] +use sp_std::vec::Vec; + +use crate::{ + crypto::{ByteArray, CryptoType, CryptoTypeId, Derive, Public as TraitPublic, UncheckedFrom}, + hash::{H256, H512}, +}; +use codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; +use sp_std::ops::Deref; + +#[cfg(feature = "std")] +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; +use sp_runtime_interface::pass_by::PassByInner; + +/// Identifier used to match public keys against bandersnatch-vrf keys. +pub const CRYPTO_ID: CryptoTypeId = CryptoTypeId(*b"bs38"); + +const SEED_SERIALIZED_LEN: usize = 32; +const PUBLIC_SERIALIZED_LEN: usize = 32; +const SIGNATURE_SERIALIZED_LEN: usize = 64; + +/// XXX. +#[cfg_attr(feature = "full_crypto", derive(Hash))] +#[derive( + PartialEq, + Eq, + PartialOrd, + Ord, + Clone, + Copy, + Encode, + Decode, + PassByInner, + MaxEncodedLen, + TypeInfo, +)] +pub struct Public(pub [u8; PUBLIC_SERIALIZED_LEN]); + +impl UncheckedFrom<[u8; PUBLIC_SERIALIZED_LEN]> for Public { + fn unchecked_from(raw: [u8; PUBLIC_SERIALIZED_LEN]) -> Self { + Public(raw) + } +} + +// impl AsRef<[u8; PUBLIC_SERIALIZED_LEN]> for Public { +// fn as_ref(&self) -> &[u8; PUBLIC_SERIALIZED_LEN] { +// &self.0 +// } +// } + +impl AsRef<[u8]> for Public { + fn as_ref(&self) -> &[u8] { + &self.0[..] + } +} + +impl AsMut<[u8]> for Public { + fn as_mut(&mut self) -> &mut [u8] { + &mut self.0[..] + } +} + +impl Deref for Public { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl TryFrom<&[u8]> for Public { + type Error = (); + + fn try_from(data: &[u8]) -> Result { + if data.len() != PUBLIC_SERIALIZED_LEN { + return Err(()) + } + let mut r = [0u8; PUBLIC_SERIALIZED_LEN]; + r.copy_from_slice(data); + Ok(Self::unchecked_from(r)) + } +} + +// impl From for [u8; PUBLIC_SERIALIZED_LEN] { +// fn from(x: Public) -> [u8; PUBLIC_SERIALIZED_LEN] { +// x.0 +// } +// } + +// impl From for H256 { +// fn from(x: Public) -> H256 { +// x.0.into() +// } +// } + +// #[cfg(feature = "std")] +// impl std::str::FromStr for Public { +// type Err = crate::crypto::PublicError; + +// fn from_str(s: &str) -> Result { +// Self::from_ss58check(s) +// } +// } + +// impl UncheckedFrom for Public { +// fn unchecked_from(x: H256) -> Self { +// Public::from_h256(x) +// } +// } + +// #[cfg(feature = "std")] +// impl std::fmt::Display for Public { +// fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { +// write!(f, "{}", self.to_ss58check()) +// } +// } + +// impl sp_std::fmt::Debug for Public { +// #[cfg(feature = "std")] +// fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { +// let s = self.to_ss58check(); +// write!(f, "{} ({}...)", crate::hexdisplay::HexDisplay::from(&self.0), &s[0..8]) +// } + +// #[cfg(not(feature = "std"))] +// fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { +// Ok(()) +// } +// } + +// #[cfg(feature = "std")] +// impl Serialize for Public { +// fn serialize(&self, serializer: S) -> Result +// where +// S: Serializer, +// { +// serializer.serialize_str(&self.to_ss58check()) +// } +// } + +// #[cfg(feature = "std")] +// impl<'de> Deserialize<'de> for Public { +// fn deserialize(deserializer: D) -> Result +// where +// D: Deserializer<'de>, +// { +// Public::from_ss58check(&String::deserialize(deserializer)?) +// .map_err(|e| de::Error::custom(format!("{:?}", e))) +// } +// } + +impl ByteArray for Public { + const LEN: usize = PUBLIC_SERIALIZED_LEN; +} + +impl TraitPublic for Public {} + +impl CryptoType for Public { + #[cfg(feature = "full_crypto")] + type Pair = Pair; +} + +impl Derive for Public {} + +/// DAVXY TODO: DOCS +#[cfg_attr(feature = "full_crypto", derive(Hash))] +#[derive(Encode, Decode, PartialEq, Eq, PassByInner, MaxEncodedLen, TypeInfo)] +pub struct Signature(pub [u8; SIGNATURE_SERIALIZED_LEN]); + +// impl AsRef<[u8; 64]> for Signature { +// fn as_ref(&self) -> &[u8; 64] { +// &self.0 +// } +// } + +impl AsRef<[u8]> for Signature { + fn as_ref(&self) -> &[u8] { + &self.0[..] + } +} + +impl AsMut<[u8]> for Signature { + fn as_mut(&mut self) -> &mut [u8] { + &mut self.0[..] + } +} + +impl CryptoType for Signature { + #[cfg(feature = "full_crypto")] + type Pair = Pair; +} + +/// The raw secret seed, which can be used to recreate the `Pair`. +#[cfg(feature = "full_crypto")] +type Seed = [u8; SEED_SERIALIZED_LEN]; + +use bandersnatch_vrfs::SigningTranscript; +use merlin::Transcript; + +/// DAVXY TODO: DOCS +#[cfg(feature = "full_crypto")] +#[derive(Clone)] +pub struct Pair(SecretKey); + +#[cfg(feature = "full_crypto")] +impl TraitPair for Pair { + type Seed = Seed; + type Public = Public; + type Signature = Signature; + + /// Make a new key pair from secret seed material. + /// + /// The slice must be 64 bytes long or it will return an error. + fn from_seed_slice(seed_slice: &[u8]) -> Result { + if seed_slice.len() != SEED_SERIALIZED_LEN { + return Err(SecretStringError::InvalidSeedLength) + } + let mut seed_raw = [0; SEED_SERIALIZED_LEN]; + seed_raw.copy_from_slice(seed_slice); + let secret = SecretKey::from_seed(seed_raw); + Ok(Pair(secret)) + } + + /// Derive a child key from a series of given (hard) junctions. + /// + /// Soft junctions are not supported. + fn derive>( + &self, + path: Iter, + _seed: Option, + ) -> Result<(Pair, Option), DeriveError> { + // TODO DAVXY: is this good? + let derive_hard_junction = |secret_seed, cc| -> Seed { + ("bandersnatch-vrf-HDKD", secret_seed, cc).using_encoded(sp_core_hashing::blake2_256) + }; + + let mut acc = [0; SEED_SERIALIZED_LEN]; + for j in path { + match j { + DeriveJunction::Soft(_cc) => return Err(DeriveError::SoftKeyInPath), + DeriveJunction::Hard(cc) => acc = derive_hard_junction(acc, cc), + } + } + Ok((Self::from_seed(&acc), Some(acc))) + } + + /// Get the public key. + fn public(&self) -> Public { + let public = self.0.to_public(); + let mut raw = [0; PUBLIC_SERIALIZED_LEN]; + public.0.serialize(raw.as_mut_slice()).expect("key buffer length is good; qed"); + Public::unchecked_from(raw) + } + + /// Sign a message. + fn sign(&self, message: &[u8]) -> Signature { + let mut t = Transcript::new(b"SigningContext"); + t.append_message(b"", message); + // // TODO DAVXY: looks like we require to clone to call sign... is this required? + let sign: ThinVrfSignature<0> = self.0.clone().sign_thin_vrf(t, &[]); + let mut raw = [0; SIGNATURE_SERIALIZED_LEN]; + sign.signature.serialize(raw.as_mut_slice()); + Signature(raw) + } + + /// Verify a signature on a message. Returns true if the signature is good. + fn verify>(sig: &Self::Signature, message: M, pubkey: &Self::Public) -> bool { + // match sig.recover(message) { + // Some(actual) => actual == *pubkey, + // None => false, + // } + // DAVXY TODO + false + } + + /// Verify a signature on a message. Returns true if the signature is good. + /// + /// This doesn't use the type system to ensure that `sig` and `pubkey` are the correct + /// size. Use it only if you're coming from byte buffers and need the speed. + fn verify_weak, M: AsRef<[u8]>>(sig: &[u8], message: M, pubkey: P) -> bool { + // match Signature::from_slice(sig).and_then(|sig| sig.recover(message)) { + // Some(actual) => actual.as_ref() == pubkey.as_ref(), + // None => false, + // } + // DAVXY TODO + false + } + + /// Return a vec filled with seed raw data. + fn to_raw_vec(&self) -> Vec { + // DAVXY TODO: should we maintain the seed here? + // Can't we extract it from inner secret key somehow? Maybe not... + vec![] + } +} + +#[cfg(feature = "full_crypto")] +impl CryptoType for Pair { + type Pair = Pair; +} + +// use ark_serialize::CanonicalSerialize; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn assumptions_check() { + let pair = SecretKey::from_seed([0; SEED_SERIALIZED_LEN]); + let public = pair.to_public(); + assert_eq!(public.0.size_of_serialized(), PUBLIC_SERIALIZED_LEN); + } + + #[test] + fn tmp_construct_public() { + let pair = Pair::from_seed(&[0; SEED_SERIALIZED_LEN]); + let public = pair.public(); + let raw = public.to_vec(); + println!("{:?}", raw); + } + + #[test] + fn sign_verify() { + let pair = Pair::from_seed(&[0; SEED_SERIALIZED_LEN]); + let public = pair.public(); + + let signature = pair.sign(b"hello"); + + println!("{:?}", signature.0); + } +} diff --git a/primitives/core/src/crypto.rs b/primitives/core/src/crypto.rs index f77e952d84546..ecbca671c6745 100644 --- a/primitives/core/src/crypto.rs +++ b/primitives/core/src/crypto.rs @@ -19,7 +19,7 @@ //! Cryptographic utilities. // end::description[] -use crate::{ed25519, sr25519}; +// use crate::{ed25519, sr25519}; #[cfg(feature = "std")] use bip39::{Language, Mnemonic, MnemonicType}; use codec::{Decode, Encode, MaxEncodedLen}; @@ -562,17 +562,17 @@ impl From for [u8; 32] { } } -impl From for AccountId32 { - fn from(k: sr25519::Public) -> Self { - k.0.into() - } -} - -impl From for AccountId32 { - fn from(k: ed25519::Public) -> Self { - k.0.into() - } -} +// impl From for AccountId32 { +// fn from(k: sr25519::Public) -> Self { +// k.0.into() +// } +// } + +// impl From for AccountId32 { +// fn from(k: ed25519::Public) -> Self { +// k.0.into() +// } +// } #[cfg(feature = "std")] impl std::fmt::Display for AccountId32 { diff --git a/primitives/core/src/lib.rs b/primitives/core/src/lib.rs index efccd0378e95a..f29bc8bb723f6 100644 --- a/primitives/core/src/lib.rs +++ b/primitives/core/src/lib.rs @@ -54,6 +54,7 @@ pub use hashing::{blake2_128, blake2_256, keccak_256, twox_128, twox_256, twox_6 pub mod crypto; pub mod hexdisplay; +pub mod bandersnatch; pub mod defer; pub mod ecdsa; pub mod ed25519; @@ -61,7 +62,7 @@ pub mod hash; #[cfg(feature = "std")] mod hasher; pub mod offchain; -pub mod sr25519; +// pub mod sr25519; pub mod testing; #[cfg(feature = "std")] pub mod traits; From 74e1aca975db88ff7c56ae15cab0d111f68af3fb Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Wed, 19 Apr 2023 16:28:27 +0200 Subject: [PATCH 02/22] Align to last bandersnatch-vrf crate iteration --- Cargo.lock | 38 ++++++++++++----------------- Cargo.toml | 3 ++- primitives/core/Cargo.toml | 4 +-- primitives/core/src/bandersnatch.rs | 13 +++++----- primitives/core/src/crypto.rs | 26 +++++++++----------- primitives/core/src/lib.rs | 2 +- 6 files changed, 39 insertions(+), 47 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 63e3600c06ff4..546198e5ff16f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -366,6 +366,17 @@ dependencies = [ "rand 0.8.5", ] +[[package]] +name = "ark-transcript" +version = "0.0.2" +dependencies = [ + "ark-ff", + "ark-serialize", + "ark-std", + "rand_core 0.6.4", + "sha3", +] + [[package]] name = "array-bytes" version = "4.2.0" @@ -618,10 +629,8 @@ dependencies = [ "ark-ed-on-bls12-381-bandersnatch", "ark-ff", "ark-serialize", - "ark-serialize-derive", "ark-std", "dleq_vrf", - "merlin 3.0.0", "rand_core 0.6.4", "sha2 0.10.6", ] @@ -1958,17 +1967,14 @@ checksum = "210ec60ae7d710bed8683e333e9d2855a8a56a3e9892b38bad3bb0d4d29b0d5e" [[package]] name = "dleq_vrf" -version = "0.0.1" +version = "0.0.2" dependencies = [ "ark-ec", "ark-ff", "ark-serialize", - "ark-serialize-derive", "ark-std", - "arrayref", + "ark-transcript", "arrayvec 0.7.2", - "merlin 3.0.0", - "rand_chacha 0.3.1", "rand_core 0.6.4", "zeroize", ] @@ -4786,18 +4792,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "merlin" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" -dependencies = [ - "byteorder", - "keccak", - "rand_core 0.6.4", - "zeroize", -] - [[package]] name = "minimal-lexical" version = "0.2.1" @@ -9951,7 +9945,7 @@ dependencies = [ "arrayvec 0.5.2", "curve25519-dalek 2.1.3", "getrandom 0.1.16", - "merlin 2.0.1", + "merlin", "rand 0.7.3", "rand_core 0.5.1", "sha2 0.8.2", @@ -10638,7 +10632,7 @@ dependencies = [ "lazy_static", "libsecp256k1", "log", - "merlin 2.0.1", + "merlin", "parity-scale-codec", "parking_lot 0.12.1", "primitive-types", @@ -12331,7 +12325,7 @@ checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ "cfg-if", "digest 0.10.6", - "rand 0.8.5", + "rand 0.7.3", "static_assertions", ] diff --git a/Cargo.toml b/Cargo.toml index fcc40d3a5b2dd..8f68ed5031b44 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -328,5 +328,6 @@ lto = "fat" # https://doc.rust-lang.org/rustc/codegen-options/index.html#codegen-units codegen-units = 1 -[patch.crates-io] +[patch."https://github.com/w3f/ring-vrf"] +bandersnatch_vrfs = { path = "/mnt/ssd/users/develop/w3f/ring-vrf/bandersnatch_vrfs" } dleq_vrf = { path = "/mnt/ssd/users/develop/w3f/ring-vrf/dleq_vrf" } diff --git a/primitives/core/Cargo.toml b/primitives/core/Cargo.toml index 47df2a385e626..4c349cbddad79 100644 --- a/primitives/core/Cargo.toml +++ b/primitives/core/Cargo.toml @@ -13,8 +13,7 @@ documentation = "https://docs.rs/sp-core" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -# bandersnatch_vrfs = { git = "https://github.com/w3f/ring-vrf" } -bandersnatch_vrfs = { path = "/mnt/ssd/users/develop/w3f/ring-vrf/bandersnatch_vrfs" } +bandersnatch_vrfs = { git = "https://github.com/w3f/ring-vrf", default-features = false } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive","max-encoded-len"] } scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } @@ -77,6 +76,7 @@ std = [ "thiserror", "lazy_static", "parking_lot", + "bandersnatch_vrfs/getrandom", "bounded-collections/std", "primitive-types/std", "primitive-types/serde", diff --git a/primitives/core/src/bandersnatch.rs b/primitives/core/src/bandersnatch.rs index 92412e33bd564..a8aa924374096 100644 --- a/primitives/core/src/bandersnatch.rs +++ b/primitives/core/src/bandersnatch.rs @@ -23,7 +23,7 @@ use crate::crypto::Ss58Codec; #[cfg(feature = "full_crypto")] use crate::crypto::{DeriveError, DeriveJunction, Pair as TraitPair, SecretStringError}; -use bandersnatch_vrfs::{PublicKey, SecretKey, ThinVrfSignature}; +use bandersnatch_vrfs::{CanonicalSerialize, PublicKey, SecretKey, ThinVrfSignature}; #[cfg(feature = "full_crypto")] use sp_std::vec::Vec; @@ -221,8 +221,7 @@ impl CryptoType for Signature { #[cfg(feature = "full_crypto")] type Seed = [u8; SEED_SERIALIZED_LEN]; -use bandersnatch_vrfs::SigningTranscript; -use merlin::Transcript; +use bandersnatch_vrfs::Transcript; /// DAVXY TODO: DOCS #[cfg(feature = "full_crypto")] @@ -244,7 +243,7 @@ impl TraitPair for Pair { } let mut seed_raw = [0; SEED_SERIALIZED_LEN]; seed_raw.copy_from_slice(seed_slice); - let secret = SecretKey::from_seed(seed_raw); + let secret = SecretKey::from_seed(&seed_raw); Ok(Pair(secret)) } @@ -282,11 +281,11 @@ impl TraitPair for Pair { /// Sign a message. fn sign(&self, message: &[u8]) -> Signature { let mut t = Transcript::new(b"SigningContext"); - t.append_message(b"", message); + t.append_slice(message); // // TODO DAVXY: looks like we require to clone to call sign... is this required? let sign: ThinVrfSignature<0> = self.0.clone().sign_thin_vrf(t, &[]); let mut raw = [0; SIGNATURE_SERIALIZED_LEN]; - sign.signature.serialize(raw.as_mut_slice()); + sign.serialize_compressed(raw.as_mut_slice()); Signature(raw) } @@ -334,7 +333,7 @@ mod tests { #[test] fn assumptions_check() { - let pair = SecretKey::from_seed([0; SEED_SERIALIZED_LEN]); + let pair = SecretKey::from_seed(&[0; SEED_SERIALIZED_LEN]); let public = pair.to_public(); assert_eq!(public.0.size_of_serialized(), PUBLIC_SERIALIZED_LEN); } diff --git a/primitives/core/src/crypto.rs b/primitives/core/src/crypto.rs index 2049759f5939e..940281b56eb0d 100644 --- a/primitives/core/src/crypto.rs +++ b/primitives/core/src/crypto.rs @@ -15,11 +15,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -// tag::description[] //! Cryptographic utilities. -// end::description[] -// use crate::{ed25519, sr25519}; +use crate::{ed25519, sr25519}; #[cfg(feature = "std")] use bip39::{Language, Mnemonic, MnemonicType}; use codec::{Decode, Encode, MaxEncodedLen}; @@ -559,17 +557,17 @@ impl From for [u8; 32] { } } -// impl From for AccountId32 { -// fn from(k: sr25519::Public) -> Self { -// k.0.into() -// } -// } - -// impl From for AccountId32 { -// fn from(k: ed25519::Public) -> Self { -// k.0.into() -// } -// } +impl From for AccountId32 { + fn from(k: sr25519::Public) -> Self { + k.0.into() + } +} + +impl From for AccountId32 { + fn from(k: ed25519::Public) -> Self { + k.0.into() + } +} #[cfg(feature = "std")] impl std::fmt::Display for AccountId32 { diff --git a/primitives/core/src/lib.rs b/primitives/core/src/lib.rs index f29bc8bb723f6..503eff3da8978 100644 --- a/primitives/core/src/lib.rs +++ b/primitives/core/src/lib.rs @@ -62,7 +62,7 @@ pub mod hash; #[cfg(feature = "std")] mod hasher; pub mod offchain; -// pub mod sr25519; +pub mod sr25519; pub mod testing; #[cfg(feature = "std")] pub mod traits; From 9273581edca31d18011d8af093727a328aee89de Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Thu, 20 Apr 2023 20:34:32 +0200 Subject: [PATCH 03/22] Implementation of sign/verify methods --- Cargo.lock | 1 + primitives/core/src/bandersnatch.rs | 199 +++++++++++++++++++++------- 2 files changed, 155 insertions(+), 45 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 546198e5ff16f..4c586bf591715 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -633,6 +633,7 @@ dependencies = [ "dleq_vrf", "rand_core 0.6.4", "sha2 0.10.6", + "zeroize", ] [[package]] diff --git a/primitives/core/src/bandersnatch.rs b/primitives/core/src/bandersnatch.rs index a8aa924374096..ff837edf6879d 100644 --- a/primitives/core/src/bandersnatch.rs +++ b/primitives/core/src/bandersnatch.rs @@ -23,7 +23,10 @@ use crate::crypto::Ss58Codec; #[cfg(feature = "full_crypto")] use crate::crypto::{DeriveError, DeriveJunction, Pair as TraitPair, SecretStringError}; -use bandersnatch_vrfs::{CanonicalSerialize, PublicKey, SecretKey, ThinVrfSignature}; +use bandersnatch_vrfs::{ + CanonicalDeserialize, CanonicalSerialize, IntoVrfInput, Message, PublicKey, SecretKey, + ThinVrfSignature, Transcript, VrfInput, +}; #[cfg(feature = "full_crypto")] use sp_std::vec::Vec; @@ -69,12 +72,6 @@ impl UncheckedFrom<[u8; PUBLIC_SERIALIZED_LEN]> for Public { } } -// impl AsRef<[u8; PUBLIC_SERIALIZED_LEN]> for Public { -// fn as_ref(&self) -> &[u8; PUBLIC_SERIALIZED_LEN] { -// &self.0 -// } -// } - impl AsRef<[u8]> for Public { fn as_ref(&self) -> &[u8] { &self.0[..] @@ -87,13 +84,13 @@ impl AsMut<[u8]> for Public { } } -impl Deref for Public { - type Target = [u8]; +// impl Deref for Public { +// type Target = [u8]; - fn deref(&self) -> &Self::Target { - &self.0 - } -} +// fn deref(&self) -> &Self::Target { +// &self.0 +// } +// } impl TryFrom<&[u8]> for Public { type Error = (); @@ -192,13 +189,13 @@ impl Derive for Public {} /// DAVXY TODO: DOCS #[cfg_attr(feature = "full_crypto", derive(Hash))] #[derive(Encode, Decode, PartialEq, Eq, PassByInner, MaxEncodedLen, TypeInfo)] -pub struct Signature(pub [u8; SIGNATURE_SERIALIZED_LEN]); +pub struct Signature([u8; SIGNATURE_SERIALIZED_LEN]); -// impl AsRef<[u8; 64]> for Signature { -// fn as_ref(&self) -> &[u8; 64] { -// &self.0 -// } -// } +impl UncheckedFrom<[u8; SIGNATURE_SERIALIZED_LEN]> for Signature { + fn unchecked_from(raw: [u8; SIGNATURE_SERIALIZED_LEN]) -> Self { + Signature(raw) + } +} impl AsRef<[u8]> for Signature { fn as_ref(&self) -> &[u8] { @@ -212,6 +209,23 @@ impl AsMut<[u8]> for Signature { } } +impl TryFrom<&[u8]> for Signature { + type Error = (); + + fn try_from(data: &[u8]) -> Result { + if data.len() != SIGNATURE_SERIALIZED_LEN { + return Err(()) + } + let mut r = [0u8; SIGNATURE_SERIALIZED_LEN]; + r.copy_from_slice(data); + Ok(Self::unchecked_from(r)) + } +} + +impl ByteArray for Signature { + const LEN: usize = SIGNATURE_SERIALIZED_LEN; +} + impl CryptoType for Signature { #[cfg(feature = "full_crypto")] type Pair = Pair; @@ -221,8 +235,6 @@ impl CryptoType for Signature { #[cfg(feature = "full_crypto")] type Seed = [u8; SEED_SERIALIZED_LEN]; -use bandersnatch_vrfs::Transcript; - /// DAVXY TODO: DOCS #[cfg(feature = "full_crypto")] #[derive(Clone)] @@ -282,7 +294,8 @@ impl TraitPair for Pair { fn sign(&self, message: &[u8]) -> Signature { let mut t = Transcript::new(b"SigningContext"); t.append_slice(message); - // // TODO DAVXY: looks like we require to clone to call sign... is this required? + // TODO DAVXY: looks like we require to clone the secret to call sign... + // Is this required?!? let sign: ThinVrfSignature<0> = self.0.clone().sign_thin_vrf(t, &[]); let mut raw = [0; SIGNATURE_SERIALIZED_LEN]; sign.serialize_compressed(raw.as_mut_slice()); @@ -290,25 +303,33 @@ impl TraitPair for Pair { } /// Verify a signature on a message. Returns true if the signature is good. - fn verify>(sig: &Self::Signature, message: M, pubkey: &Self::Public) -> bool { - // match sig.recover(message) { - // Some(actual) => actual == *pubkey, - // None => false, - // } - // DAVXY TODO - false + fn verify>( + signature: &Self::Signature, + message: M, + public: &Self::Public, + ) -> bool { + let Ok(public) = PublicKey::deserialize_compressed(public.as_ref()) else { + return false + }; + let Ok(signature) = ThinVrfSignature::<0>::deserialize_compressed(signature.as_ref()) else { + return false + }; + let mut t = Transcript::new(b"SigningContext"); + t.append_slice(message.as_ref()); + + let inputs: Vec = Vec::new(); + match signature.verify_thin_vrf(t, inputs, &public) { + Ok(ios) => true, + Err(e) => false, + } } /// Verify a signature on a message. Returns true if the signature is good. /// - /// This doesn't use the type system to ensure that `sig` and `pubkey` are the correct + /// This doesn't use the type system to ensure that `sig` and `public` are the correct /// size. Use it only if you're coming from byte buffers and need the speed. - fn verify_weak, M: AsRef<[u8]>>(sig: &[u8], message: M, pubkey: P) -> bool { - // match Signature::from_slice(sig).and_then(|sig| sig.recover(message)) { - // Some(actual) => actual.as_ref() == pubkey.as_ref(), - // None => false, - // } - // DAVXY TODO + fn verify_weak, M: AsRef<[u8]>>(sig: &[u8], message: M, public: P) -> bool { + // DAVXY TODO : makes sense??? false } @@ -325,34 +346,122 @@ impl CryptoType for Pair { type Pair = Pair; } -// use ark_serialize::CanonicalSerialize; +/// VRF related types and operations. +mod vrf { + use super::*; + #[cfg(feature = "full_crypto")] + use crate::crypto::VrfSigner; + use crate::crypto::{VrfCrypto, VrfVerifier}; + + /// VRF transcript ready to be used for VRF sign/verify operations. + pub struct VrfTranscript(Transcript); + + impl VrfTranscript { + /// Build a new transcript ready to be used by a VRF signer/verifier. + pub fn new(label: &'static [u8], data: &[&[u8]]) -> Self { + let mut transcript = Transcript::new(label); + data.iter().for_each(|b| transcript.append_slice(b)); + VrfTranscript(transcript) + } + } + + pub type VrfSignature = super::Signature; + + #[cfg(feature = "full_crypto")] + impl VrfCrypto for Pair { + type VrfSignature = VrfSignature; + type VrfInput = VrfTranscript; + } + + #[cfg(feature = "full_crypto")] + impl VrfSigner for Pair { + fn vrf_sign(&self, transcript: &Self::VrfInput) -> Self::VrfSignature { + let sign: ThinVrfSignature<0> = self.0.clone().sign_thin_vrf(transcript.0.clone(), &[]); + let mut raw = [0; SIGNATURE_SERIALIZED_LEN]; + sign.serialize_compressed(raw.as_mut_slice()); + Signature(raw) + } + } + + impl VrfCrypto for Public { + type VrfSignature = VrfSignature; + type VrfInput = VrfTranscript; + } + + impl VrfVerifier for Public { + fn vrf_verify(&self, transcript: &Self::VrfInput, signature: &Self::VrfSignature) -> bool { + let Ok(public) = PublicKey::deserialize_compressed(self.as_ref()) else { + return false + }; + let Ok(signature) = ThinVrfSignature::<0>::deserialize_compressed(signature.as_ref()) else { + return false + }; + + let inputs: Vec = Vec::new(); + match signature.verify_thin_vrf(transcript.0.clone(), inputs, &public) { + Ok(ios) => true, + Err(e) => false, + } + } + } +} #[cfg(test)] mod tests { use super::*; + use crate::crypto::DEV_PHRASE; + const DEV_SEED: &[u8; SEED_SERIALIZED_LEN] = &[0; SEED_SERIALIZED_LEN]; + + fn b2h(bytes: &[u8]) -> String { + array_bytes::bytes2hex("", bytes) + } + + fn h2b(hex: &str) -> Vec { + array_bytes::hex2bytes_unchecked(hex) + } #[test] - fn assumptions_check() { - let pair = SecretKey::from_seed(&[0; SEED_SERIALIZED_LEN]); + fn backend_assumptions_check() { + let pair = SecretKey::from_seed(DEV_SEED); let public = pair.to_public(); + assert_eq!(public.0.size_of_serialized(), PUBLIC_SERIALIZED_LEN); } + #[test] + fn verify_known_signature() { + let pair = Pair::from_seed(DEV_SEED); + let public = pair.public(); + + let signature_raw = h2b("524b0cbc4eb9579e2cd115fe55e2625e8265b3ea599ac903e67b08c2c669780cf43ca9c1e0a8a63c1dba121a606f95d3466cfe1880acc502c2792775125a7fcc"); + let signature = Signature::from_slice(&signature_raw).unwrap(); + + assert!(Pair::verify(&signature, b"hello", &public)); + } + + #[test] + fn derive_hard_known_pair() { + let pair = Pair::from_string(&format!("{}//Alice", DEV_PHRASE), None).unwrap(); + // known address of DEV_PHRASE with 1.1 + let known = h2b("b0d3648bd5a3542afa16c06fee04cba37cc55c83a8894d36d87897bda0c65eec"); + assert_eq!(pair.public().as_ref(), known); + } + #[test] fn tmp_construct_public() { - let pair = Pair::from_seed(&[0; SEED_SERIALIZED_LEN]); + let pair = Pair::from_seed(DEV_SEED); let public = pair.public(); - let raw = public.to_vec(); - println!("{:?}", raw); + let raw = public.to_raw_vec(); } #[test] fn sign_verify() { - let pair = Pair::from_seed(&[0; SEED_SERIALIZED_LEN]); + let pair = Pair::from_seed(DEV_SEED); let public = pair.public(); + let msg = b"hello"; - let signature = pair.sign(b"hello"); + let signature = pair.sign(msg); - println!("{:?}", signature.0); + assert!(Pair::verify(&signature, msg, &public)); } } From 09d2d41eab9f2565695c935939b6af8885159b33 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Fri, 21 Apr 2023 09:40:38 +0200 Subject: [PATCH 04/22] Bandersnatch keystore integration --- client/keystore/src/local.rs | 31 ++++++++++++++++++++++++++++ primitives/core/src/bandersnatch.rs | 3 ++- primitives/keystore/src/lib.rs | 32 +++++++++++++++++++++++++++++ primitives/keystore/src/testing.rs | 31 ++++++++++++++++++++++++++++ 4 files changed, 96 insertions(+), 1 deletion(-) diff --git a/client/keystore/src/local.rs b/client/keystore/src/local.rs index 3551623f332a2..d541807d1fd45 100644 --- a/client/keystore/src/local.rs +++ b/client/keystore/src/local.rs @@ -20,6 +20,7 @@ use parking_lot::RwLock; use sp_application_crypto::{AppCrypto, AppPair, IsWrappedBy}; use sp_core::{ + bandersnatch, crypto::{ByteArray, ExposeSecret, KeyTypeId, Pair as CorePair, SecretString, VrfSigner}, ecdsa, ed25519, sr25519, }; @@ -209,6 +210,36 @@ impl Keystore for LocalKeystore { Ok(sig) } + fn bandersnatch_public_keys(&self, key_type: KeyTypeId) -> Vec { + self.public_keys::(key_type) + } + + fn bandersnatch_generate_new( + &self, + key_type: KeyTypeId, + seed: Option<&str>, + ) -> std::result::Result { + self.generate_new::(key_type, seed) + } + + fn bandersnatch_sign( + &self, + key_type: KeyTypeId, + public: &bandersnatch::Public, + msg: &[u8], + ) -> std::result::Result, TraitError> { + self.sign::(key_type, public, msg) + } + + fn bandersnatch_vrf_sign( + &self, + key_type: KeyTypeId, + public: &bandersnatch::Public, + transcript: &bandersnatch::vrf::VrfTranscript, + ) -> std::result::Result, TraitError> { + self.vrf_sign::(key_type, public, transcript) + } + fn insert( &self, key_type: KeyTypeId, diff --git a/primitives/core/src/bandersnatch.rs b/primitives/core/src/bandersnatch.rs index ff837edf6879d..fd17bd7356f25 100644 --- a/primitives/core/src/bandersnatch.rs +++ b/primitives/core/src/bandersnatch.rs @@ -347,7 +347,7 @@ impl CryptoType for Pair { } /// VRF related types and operations. -mod vrf { +pub mod vrf { use super::*; #[cfg(feature = "full_crypto")] use crate::crypto::VrfSigner; @@ -365,6 +365,7 @@ mod vrf { } } + /// TODO DAVXY pub type VrfSignature = super::Signature; #[cfg(feature = "full_crypto")] diff --git a/primitives/keystore/src/lib.rs b/primitives/keystore/src/lib.rs index 5b41f3b80043d..087efcfc261d7 100644 --- a/primitives/keystore/src/lib.rs +++ b/primitives/keystore/src/lib.rs @@ -20,6 +20,7 @@ pub mod testing; use sp_core::{ + bandersnatch, crypto::{ByteArray, CryptoTypeId, KeyTypeId}, ecdsa, ed25519, sr25519, }; @@ -160,6 +161,32 @@ pub trait Keystore: Send + Sync { msg: &[u8; 32], ) -> Result, Error>; + /// DAVXY TODO + fn bandersnatch_public_keys(&self, key_type: KeyTypeId) -> Vec; + + /// DAVXY TODO + fn bandersnatch_generate_new( + &self, + key_type: KeyTypeId, + seed: Option<&str>, + ) -> Result; + + /// DAVXY TODO + fn bandersnatch_sign( + &self, + key_type: KeyTypeId, + public: &bandersnatch::Public, + msg: &[u8], + ) -> Result, Error>; + + /// DAVXY TODO + fn bandersnatch_vrf_sign( + &self, + key_type: KeyTypeId, + public: &bandersnatch::Public, + transcript: &bandersnatch::vrf::VrfTranscript, + ) -> Result, Error>; + /// Insert a new secret key. fn insert(&self, key_type: KeyTypeId, suri: &str, public: &[u8]) -> Result<(), ()>; @@ -208,6 +235,11 @@ pub trait Keystore: Send + Sync { .map_err(|_| Error::ValidationError("Invalid public key format".into()))?; self.ecdsa_sign(id, &public, msg)?.map(|s| s.encode()) }, + bandersnatch::CRYPTO_ID => { + let public = bandersnatch::Public::from_slice(public) + .map_err(|_| Error::ValidationError("Invalid public key format".into()))?; + self.bandersnatch_sign(id, &public, msg)?.map(|s| s.encode()) + }, _ => return Err(Error::KeyNotSupported(id)), }; Ok(signature) diff --git a/primitives/keystore/src/testing.rs b/primitives/keystore/src/testing.rs index a6fcd6e26abe3..0b8304c0184bb 100644 --- a/primitives/keystore/src/testing.rs +++ b/primitives/keystore/src/testing.rs @@ -20,6 +20,7 @@ use crate::{Error, Keystore, KeystorePtr}; use sp_core::{ + bandersnatch, crypto::{ByteArray, KeyTypeId, Pair, VrfSigner}, ecdsa, ed25519, sr25519, }; @@ -193,6 +194,36 @@ impl Keystore for MemoryKeystore { Ok(sig) } + fn bandersnatch_public_keys(&self, key_type: KeyTypeId) -> Vec { + self.public_keys::(key_type) + } + + fn bandersnatch_generate_new( + &self, + key_type: KeyTypeId, + seed: Option<&str>, + ) -> Result { + self.generate_new::(key_type, seed) + } + + fn bandersnatch_sign( + &self, + key_type: KeyTypeId, + public: &bandersnatch::Public, + msg: &[u8], + ) -> Result, Error> { + self.sign::(key_type, public, msg) + } + + fn bandersnatch_vrf_sign( + &self, + key_type: KeyTypeId, + public: &bandersnatch::Public, + transcript: &bandersnatch::vrf::VrfTranscript, + ) -> Result, Error> { + self.vrf_sign::(key_type, public, transcript) + } + fn insert(&self, key_type: KeyTypeId, suri: &str, public: &[u8]) -> Result<(), ()> { self.keys .write() From fbd3e7a19dc2cc275c0319dc1aa5706a1da4caf9 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Fri, 21 Apr 2023 14:47:25 +0200 Subject: [PATCH 05/22] Application crypto --- .../application-crypto/src/bandersnatch.rs | 28 ++++ primitives/application-crypto/src/lib.rs | 8 +- primitives/core/src/bandersnatch.rs | 146 ++++++------------ primitives/core/src/testing.rs | 4 +- 4 files changed, 79 insertions(+), 107 deletions(-) create mode 100644 primitives/application-crypto/src/bandersnatch.rs diff --git a/primitives/application-crypto/src/bandersnatch.rs b/primitives/application-crypto/src/bandersnatch.rs new file mode 100644 index 0000000000000..8e385e781bb92 --- /dev/null +++ b/primitives/application-crypto/src/bandersnatch.rs @@ -0,0 +1,28 @@ +// 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. + +//! Bandersnatch VRF application crypto types. + +pub use sp_core::bandersnatch::*; + +mod app { + crate::app_crypto!(super, sp_core::testing::BANDERSNATCH); +} + +#[cfg(feature = "full_crypto")] +pub use app::Pair as AppPair; +pub use app::{Public as AppPublic, Signature as AppSignature}; diff --git a/primitives/application-crypto/src/lib.rs b/primitives/application-crypto/src/lib.rs index 3f12e06e11ec3..8c407f06fed02 100644 --- a/primitives/application-crypto/src/lib.rs +++ b/primitives/application-crypto/src/lib.rs @@ -41,12 +41,14 @@ pub use serde; #[doc(hidden)] pub use sp_std::{ops::Deref, vec::Vec}; +pub use traits::*; + +mod traits; + +pub mod bandersnatch; pub mod ecdsa; pub mod ed25519; pub mod sr25519; -mod traits; - -pub use traits::*; /// Declares `Public`, `Pair` and `Signature` types which are functionally equivalent /// to the corresponding types defined by `$module` but are new application-specific diff --git a/primitives/core/src/bandersnatch.rs b/primitives/core/src/bandersnatch.rs index fd17bd7356f25..108bb8d38f369 100644 --- a/primitives/core/src/bandersnatch.rs +++ b/primitives/core/src/bandersnatch.rs @@ -24,10 +24,9 @@ use crate::crypto::Ss58Codec; #[cfg(feature = "full_crypto")] use crate::crypto::{DeriveError, DeriveJunction, Pair as TraitPair, SecretStringError}; use bandersnatch_vrfs::{ - CanonicalDeserialize, CanonicalSerialize, IntoVrfInput, Message, PublicKey, SecretKey, - ThinVrfSignature, Transcript, VrfInput, + CanonicalDeserialize, CanonicalSerialize, IntoVrfInput, PublicKey, SecretKey, ThinVrfSignature, + Transcript, VrfInput, }; -#[cfg(feature = "full_crypto")] use sp_std::vec::Vec; use crate::{ @@ -52,12 +51,12 @@ const SIGNATURE_SERIALIZED_LEN: usize = 64; /// XXX. #[cfg_attr(feature = "full_crypto", derive(Hash))] #[derive( + Clone, + Copy, PartialEq, Eq, PartialOrd, Ord, - Clone, - Copy, Encode, Decode, PassByInner, @@ -84,14 +83,6 @@ impl AsMut<[u8]> for Public { } } -// impl Deref for Public { -// type Target = [u8]; - -// fn deref(&self) -> &Self::Target { -// &self.0 -// } -// } - impl TryFrom<&[u8]> for Public { type Error = (); @@ -105,74 +96,6 @@ impl TryFrom<&[u8]> for Public { } } -// impl From for [u8; PUBLIC_SERIALIZED_LEN] { -// fn from(x: Public) -> [u8; PUBLIC_SERIALIZED_LEN] { -// x.0 -// } -// } - -// impl From for H256 { -// fn from(x: Public) -> H256 { -// x.0.into() -// } -// } - -// #[cfg(feature = "std")] -// impl std::str::FromStr for Public { -// type Err = crate::crypto::PublicError; - -// fn from_str(s: &str) -> Result { -// Self::from_ss58check(s) -// } -// } - -// impl UncheckedFrom for Public { -// fn unchecked_from(x: H256) -> Self { -// Public::from_h256(x) -// } -// } - -// #[cfg(feature = "std")] -// impl std::fmt::Display for Public { -// fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { -// write!(f, "{}", self.to_ss58check()) -// } -// } - -// impl sp_std::fmt::Debug for Public { -// #[cfg(feature = "std")] -// fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { -// let s = self.to_ss58check(); -// write!(f, "{} ({}...)", crate::hexdisplay::HexDisplay::from(&self.0), &s[0..8]) -// } - -// #[cfg(not(feature = "std"))] -// fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { -// Ok(()) -// } -// } - -// #[cfg(feature = "std")] -// impl Serialize for Public { -// fn serialize(&self, serializer: S) -> Result -// where -// S: Serializer, -// { -// serializer.serialize_str(&self.to_ss58check()) -// } -// } - -// #[cfg(feature = "std")] -// impl<'de> Deserialize<'de> for Public { -// fn deserialize(deserializer: D) -> Result -// where -// D: Deserializer<'de>, -// { -// Public::from_ss58check(&String::deserialize(deserializer)?) -// .map_err(|e| de::Error::custom(format!("{:?}", e))) -// } -// } - impl ByteArray for Public { const LEN: usize = PUBLIC_SERIALIZED_LEN; } @@ -186,9 +109,22 @@ impl CryptoType for Public { impl Derive for Public {} +impl sp_std::fmt::Debug for Public { + #[cfg(feature = "std")] + fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { + let s = self.to_ss58check(); + write!(f, "{} ({}...)", crate::hexdisplay::HexDisplay::from(&self.0), &s[0..8]) + } + + #[cfg(not(feature = "std"))] + fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { + Ok(()) + } +} + /// DAVXY TODO: DOCS #[cfg_attr(feature = "full_crypto", derive(Hash))] -#[derive(Encode, Decode, PartialEq, Eq, PassByInner, MaxEncodedLen, TypeInfo)] +#[derive(Clone, Copy, PartialEq, Eq, Encode, Decode, PassByInner, MaxEncodedLen, TypeInfo)] pub struct Signature([u8; SIGNATURE_SERIALIZED_LEN]); impl UncheckedFrom<[u8; SIGNATURE_SERIALIZED_LEN]> for Signature { @@ -231,6 +167,18 @@ impl CryptoType for Signature { type Pair = Pair; } +impl sp_std::fmt::Debug for Signature { + #[cfg(feature = "std")] + fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { + write!(f, "{}", crate::hexdisplay::HexDisplay::from(&self.0)) + } + + #[cfg(not(feature = "std"))] + fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { + Ok(()) + } +} + /// The raw secret seed, which can be used to recreate the `Pair`. #[cfg(feature = "full_crypto")] type Seed = [u8; SEED_SERIALIZED_LEN]; @@ -314,11 +262,11 @@ impl TraitPair for Pair { let Ok(signature) = ThinVrfSignature::<0>::deserialize_compressed(signature.as_ref()) else { return false }; - let mut t = Transcript::new(b"SigningContext"); - t.append_slice(message.as_ref()); + let mut transcript = Transcript::new(b"SigningContext"); + transcript.append_slice(message.as_ref()); - let inputs: Vec = Vec::new(); - match signature.verify_thin_vrf(t, inputs, &public) { + let inputs: Vec = Vec::new(); + match signature.verify_thin_vrf(transcript, inputs, &public) { Ok(ios) => true, Err(e) => false, } @@ -398,7 +346,7 @@ pub mod vrf { return false }; - let inputs: Vec = Vec::new(); + let inputs: Vec = Vec::new(); match signature.verify_thin_vrf(transcript.0.clone(), inputs, &public) { Ok(ios) => true, Err(e) => false, @@ -440,29 +388,21 @@ mod tests { assert!(Pair::verify(&signature, b"hello", &public)); } - #[test] - fn derive_hard_known_pair() { - let pair = Pair::from_string(&format!("{}//Alice", DEV_PHRASE), None).unwrap(); - // known address of DEV_PHRASE with 1.1 - let known = h2b("b0d3648bd5a3542afa16c06fee04cba37cc55c83a8894d36d87897bda0c65eec"); - assert_eq!(pair.public().as_ref(), known); - } - - #[test] - fn tmp_construct_public() { - let pair = Pair::from_seed(DEV_SEED); - let public = pair.public(); - let raw = public.to_raw_vec(); - } - #[test] fn sign_verify() { let pair = Pair::from_seed(DEV_SEED); let public = pair.public(); let msg = b"hello"; - let signature = pair.sign(msg); assert!(Pair::verify(&signature, msg, &public)); } + + #[test] + fn derive_hard_known_pair() { + let pair = Pair::from_string(&format!("{}//Alice", DEV_PHRASE), None).unwrap(); + // known address of DEV_PHRASE with 1.1 + let known = h2b("b0d3648bd5a3542afa16c06fee04cba37cc55c83a8894d36d87897bda0c65eec"); + assert_eq!(pair.public().as_ref(), known); + } } diff --git a/primitives/core/src/testing.rs b/primitives/core/src/testing.rs index 756275aed6f41..b358e5153c6cb 100644 --- a/primitives/core/src/testing.rs +++ b/primitives/core/src/testing.rs @@ -21,10 +21,12 @@ use crate::crypto::KeyTypeId; /// Key type for generic Ed25519 key. pub const ED25519: KeyTypeId = KeyTypeId(*b"ed25"); -/// Key type for generic Sr 25519 key. +/// Key type for generic Sr25519 key. pub const SR25519: KeyTypeId = KeyTypeId(*b"sr25"); /// Key type for generic ECDSA key. pub const ECDSA: KeyTypeId = KeyTypeId(*b"ecds"); +/// Key type for generic Bandersnatch key. +pub const BANDERSNATCH: KeyTypeId = KeyTypeId(*b"bach"); /// Macro for exporting functions from wasm in with the expected signature for using it with the /// wasm executor. This is useful for tests where you need to call a function in wasm. From b038d9e08368f458f399fb76dc0011db7e16117f Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Fri, 21 Apr 2023 15:32:51 +0200 Subject: [PATCH 06/22] Global const signing context --- primitives/core/src/bandersnatch.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/primitives/core/src/bandersnatch.rs b/primitives/core/src/bandersnatch.rs index 108bb8d38f369..df9b7007a9c55 100644 --- a/primitives/core/src/bandersnatch.rs +++ b/primitives/core/src/bandersnatch.rs @@ -44,6 +44,8 @@ use sp_runtime_interface::pass_by::PassByInner; /// Identifier used to match public keys against bandersnatch-vrf keys. pub const CRYPTO_ID: CryptoTypeId = CryptoTypeId(*b"bs38"); +const SIGNING_CTX: &[u8] = b"SigningContext"; + const SEED_SERIALIZED_LEN: usize = 32; const PUBLIC_SERIALIZED_LEN: usize = 32; const SIGNATURE_SERIALIZED_LEN: usize = 64; @@ -240,17 +242,17 @@ impl TraitPair for Pair { /// Sign a message. fn sign(&self, message: &[u8]) -> Signature { - let mut t = Transcript::new(b"SigningContext"); - t.append_slice(message); - // TODO DAVXY: looks like we require to clone the secret to call sign... - // Is this required?!? - let sign: ThinVrfSignature<0> = self.0.clone().sign_thin_vrf(t, &[]); + let mut transcript = Transcript::new(SIGNING_CTX); + transcript.append_slice(message); + let sign: ThinVrfSignature<0> = self.0.sign_thin_vrf(transcript, &[]); let mut raw = [0; SIGNATURE_SERIALIZED_LEN]; sign.serialize_compressed(raw.as_mut_slice()); Signature(raw) } - /// Verify a signature on a message. Returns true if the signature is good. + /// Verify a signature on a message. + /// + /// Returns true if the signature is good. fn verify>( signature: &Self::Signature, message: M, @@ -262,7 +264,7 @@ impl TraitPair for Pair { let Ok(signature) = ThinVrfSignature::<0>::deserialize_compressed(signature.as_ref()) else { return false }; - let mut transcript = Transcript::new(b"SigningContext"); + let mut transcript = Transcript::new(SIGNING_CTX); transcript.append_slice(message.as_ref()); let inputs: Vec = Vec::new(); @@ -325,7 +327,7 @@ pub mod vrf { #[cfg(feature = "full_crypto")] impl VrfSigner for Pair { fn vrf_sign(&self, transcript: &Self::VrfInput) -> Self::VrfSignature { - let sign: ThinVrfSignature<0> = self.0.clone().sign_thin_vrf(transcript.0.clone(), &[]); + let sign: ThinVrfSignature<0> = self.0.sign_thin_vrf(transcript.0.clone(), &[]); let mut raw = [0; SIGNATURE_SERIALIZED_LEN]; sign.serialize_compressed(raw.as_mut_slice()); Signature(raw) From 7b31bcd4436dfbaaadbca143176b27fa02454601 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Fri, 21 Apr 2023 17:05:02 +0200 Subject: [PATCH 07/22] make_bytes skeleton --- primitives/core/src/bandersnatch.rs | 80 ++++++++++++++++++++++++++--- 1 file changed, 72 insertions(+), 8 deletions(-) diff --git a/primitives/core/src/bandersnatch.rs b/primitives/core/src/bandersnatch.rs index df9b7007a9c55..e97f3c12b9197 100644 --- a/primitives/core/src/bandersnatch.rs +++ b/primitives/core/src/bandersnatch.rs @@ -25,7 +25,7 @@ use crate::crypto::Ss58Codec; use crate::crypto::{DeriveError, DeriveJunction, Pair as TraitPair, SecretStringError}; use bandersnatch_vrfs::{ CanonicalDeserialize, CanonicalSerialize, IntoVrfInput, PublicKey, SecretKey, ThinVrfSignature, - Transcript, VrfInput, + Transcript, VrfInput, VrfPreOut, }; use sp_std::vec::Vec; @@ -355,6 +355,40 @@ pub mod vrf { } } } + + #[cfg(feature = "full_crypto")] + impl Pair { + /// Generate bytes from the given VRF configuration. + pub fn make_bytes>( + &self, + _context: &[u8], + _transcript: &VrfTranscript, + ) -> B { + // TODO davxy: the following is for schnorrkel + // let inout = self.0.vrf_create_hash(transcript.0.clone()); + // inout.make_bytes::(context) + B::default() + } + } + + impl Public { + /// Generate bytes from the given VRF configuration. + pub fn make_bytes>( + &self, + _context: &[u8], + _transcript: &VrfTranscript, + _output: &VrfPreOut, + ) -> Result { + // TODO davxy: the following is for schnorrkel + // let pubkey = schnorrkel::PublicKey::from_bytes(&self.0).map_err(convert_error)?; + // let inout = output + // .0 + // .attach_input_hash(&pubkey, transcript.0.clone()) + // .map_err(convert_error)?; + // Ok(inout.make_bytes::(context)) + Ok(B::default()) + } + } } #[cfg(test)] @@ -379,12 +413,22 @@ mod tests { assert_eq!(public.0.size_of_serialized(), PUBLIC_SERIALIZED_LEN); } + #[test] + fn derive_hard_known_pair() { + let pair = Pair::from_string(&format!("{}//Alice", DEV_PHRASE), None).unwrap(); + // known address of DEV_PHRASE with 1.1 + let known = h2b("b0d3648bd5a3542afa16c06fee04cba37cc55c83a8894d36d87897bda0c65eec"); + assert_eq!(pair.public().as_ref(), known); + } + #[test] fn verify_known_signature() { let pair = Pair::from_seed(DEV_SEED); let public = pair.public(); - let signature_raw = h2b("524b0cbc4eb9579e2cd115fe55e2625e8265b3ea599ac903e67b08c2c669780cf43ca9c1e0a8a63c1dba121a606f95d3466cfe1880acc502c2792775125a7fcc"); + let signature_raw = + h2b("524b0cbc4eb9579e2cd115fe55e2625e8265b3ea599ac903e67b08c2c669780cf43ca9c1e0a8a63c1dba121a606f95d3466cfe1880acc502c2792775125a7fcc" + ); let signature = Signature::from_slice(&signature_raw).unwrap(); assert!(Pair::verify(&signature, b"hello", &public)); @@ -395,16 +439,36 @@ mod tests { let pair = Pair::from_seed(DEV_SEED); let public = pair.public(); let msg = b"hello"; - let signature = pair.sign(msg); + let signature = pair.sign(msg); assert!(Pair::verify(&signature, msg, &public)); } #[test] - fn derive_hard_known_pair() { - let pair = Pair::from_string(&format!("{}//Alice", DEV_PHRASE), None).unwrap(); - // known address of DEV_PHRASE with 1.1 - let known = h2b("b0d3648bd5a3542afa16c06fee04cba37cc55c83a8894d36d87897bda0c65eec"); - assert_eq!(pair.public().as_ref(), known); + fn vrf_sign_verify() { + use super::vrf::*; + use crate::crypto::{VrfSigner, VrfVerifier}; + + let pair = Pair::from_seed(DEV_SEED); + let public = pair.public(); + let transcript = VrfTranscript::new(b"test", &[b"foo", b"bar"]); + + let signature = pair.vrf_sign(&transcript); + + println!("{}", b2h(signature.as_ref())); + + assert!(public.vrf_verify(&transcript, &signature)); } + + // #[test] + // fn vrf_make_bytes_matches() { + // use super::vrf::*; + // use crate::crypto::VrfSigner; + // let pair = Pair::from_seed(DEV_SEED); + // let public = pair.public(); + // let transcript = VrfTranscript::new(b"test", &[b"foo", b"bar"]); + + // let ctx = b"randbytes"; + // let b1 = pair.make_bytes::<[u8; 32]>(ctx, &transcript); + // } } From e258c1cd46023db371ff9e71ea6fdc17b8f41d13 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Fri, 21 Apr 2023 17:09:19 +0200 Subject: [PATCH 08/22] Remove cruft --- primitives/core/src/bandersnatch.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/primitives/core/src/bandersnatch.rs b/primitives/core/src/bandersnatch.rs index e97f3c12b9197..215c9b9ad9f1a 100644 --- a/primitives/core/src/bandersnatch.rs +++ b/primitives/core/src/bandersnatch.rs @@ -124,7 +124,7 @@ impl sp_std::fmt::Debug for Public { } } -/// DAVXY TODO: DOCS +/// TODO davxy: DOCS #[cfg_attr(feature = "full_crypto", derive(Hash))] #[derive(Clone, Copy, PartialEq, Eq, Encode, Decode, PassByInner, MaxEncodedLen, TypeInfo)] pub struct Signature([u8; SIGNATURE_SERIALIZED_LEN]); @@ -185,7 +185,7 @@ impl sp_std::fmt::Debug for Signature { #[cfg(feature = "full_crypto")] type Seed = [u8; SEED_SERIALIZED_LEN]; -/// DAVXY TODO: DOCS +/// TODO davxy: DOCS #[cfg(feature = "full_crypto")] #[derive(Clone)] pub struct Pair(SecretKey); @@ -217,7 +217,7 @@ impl TraitPair for Pair { path: Iter, _seed: Option, ) -> Result<(Pair, Option), DeriveError> { - // TODO DAVXY: is this good? + // TODO davxy is this good? let derive_hard_junction = |secret_seed, cc| -> Seed { ("bandersnatch-vrf-HDKD", secret_seed, cc).using_encoded(sp_core_hashing::blake2_256) }; @@ -279,15 +279,15 @@ impl TraitPair for Pair { /// This doesn't use the type system to ensure that `sig` and `public` are the correct /// size. Use it only if you're coming from byte buffers and need the speed. fn verify_weak, M: AsRef<[u8]>>(sig: &[u8], message: M, public: P) -> bool { - // DAVXY TODO : makes sense??? + // TODO davxy : makes sense??? + unimplemented!() false } /// Return a vec filled with seed raw data. fn to_raw_vec(&self) -> Vec { - // DAVXY TODO: should we maintain the seed here? - // Can't we extract it from inner secret key somehow? Maybe not... - vec![] + // TODO davxy: makes sense??? + unimplemented!() } } @@ -315,7 +315,7 @@ pub mod vrf { } } - /// TODO DAVXY + /// TODO davxy: docs pub type VrfSignature = super::Signature; #[cfg(feature = "full_crypto")] From 95c03584245dfbc340ee14ba754566de1765707c Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Sat, 22 Apr 2023 08:37:05 +0200 Subject: [PATCH 09/22] Fix after master merge --- primitives/core/src/bandersnatch.rs | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/primitives/core/src/bandersnatch.rs b/primitives/core/src/bandersnatch.rs index 215c9b9ad9f1a..ab10eaeffbf1f 100644 --- a/primitives/core/src/bandersnatch.rs +++ b/primitives/core/src/bandersnatch.rs @@ -274,19 +274,10 @@ impl TraitPair for Pair { } } - /// Verify a signature on a message. Returns true if the signature is good. - /// - /// This doesn't use the type system to ensure that `sig` and `public` are the correct - /// size. Use it only if you're coming from byte buffers and need the speed. - fn verify_weak, M: AsRef<[u8]>>(sig: &[u8], message: M, public: P) -> bool { - // TODO davxy : makes sense??? - unimplemented!() - false - } - /// Return a vec filled with seed raw data. fn to_raw_vec(&self) -> Vec { - // TODO davxy: makes sense??? + // TODO davxy: makes sense??? Should we returne the seed or serialized secret key? + // If we return the serialized secret there is no method to reconstruct if ... unimplemented!() } } From 7d8c71a5d5cf5900412d59d7e949130de45a78a4 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Tue, 25 Apr 2023 19:46:36 +0200 Subject: [PATCH 10/22] Manage multiple VRF inputs --- Cargo.lock | 1 + client/keystore/src/local.rs | 6 +- primitives/core/Cargo.toml | 1 + primitives/core/src/bandersnatch.rs | 312 ++++++++++++++++++---------- primitives/keystore/src/lib.rs | 2 +- primitives/keystore/src/testing.rs | 4 +- 6 files changed, 212 insertions(+), 114 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e8514404effb8..96e5d15bbe179 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10668,6 +10668,7 @@ name = "sp-core" version = "7.0.0" dependencies = [ "array-bytes", + "arrayvec 0.7.2", "bandersnatch_vrfs", "bitflags", "blake2", diff --git a/client/keystore/src/local.rs b/client/keystore/src/local.rs index d541807d1fd45..e343bfdaafacb 100644 --- a/client/keystore/src/local.rs +++ b/client/keystore/src/local.rs @@ -231,13 +231,15 @@ impl Keystore for LocalKeystore { self.sign::(key_type, public, msg) } + // Maybe we can expose just one bandersnatch sign here (the above one reduces to this when input + // len = 0) fn bandersnatch_vrf_sign( &self, key_type: KeyTypeId, public: &bandersnatch::Public, - transcript: &bandersnatch::vrf::VrfTranscript, + input: &bandersnatch::vrf::VrfInput, ) -> std::result::Result, TraitError> { - self.vrf_sign::(key_type, public, transcript) + self.vrf_sign::(key_type, public, input) } fn insert( diff --git a/primitives/core/Cargo.toml b/primitives/core/Cargo.toml index 4c349cbddad79..064f196f2aa2a 100644 --- a/primitives/core/Cargo.toml +++ b/primitives/core/Cargo.toml @@ -14,6 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] bandersnatch_vrfs = { git = "https://github.com/w3f/ring-vrf", default-features = false } +arrayvec = { version = "0.7.2" } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive","max-encoded-len"] } scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } diff --git a/primitives/core/src/bandersnatch.rs b/primitives/core/src/bandersnatch.rs index ab10eaeffbf1f..adb07c9312e71 100644 --- a/primitives/core/src/bandersnatch.rs +++ b/primitives/core/src/bandersnatch.rs @@ -17,29 +17,23 @@ //! TODO DOCS. -#![allow(unused)] +// #![allow(unused)] #[cfg(feature = "std")] use crate::crypto::Ss58Codec; -#[cfg(feature = "full_crypto")] -use crate::crypto::{DeriveError, DeriveJunction, Pair as TraitPair, SecretStringError}; -use bandersnatch_vrfs::{ - CanonicalDeserialize, CanonicalSerialize, IntoVrfInput, PublicKey, SecretKey, ThinVrfSignature, - Transcript, VrfInput, VrfPreOut, +use crate::crypto::{ + ByteArray, CryptoType, CryptoTypeId, Derive, Public as TraitPublic, UncheckedFrom, VrfVerifier, }; -use sp_std::vec::Vec; +#[cfg(feature = "full_crypto")] +use crate::crypto::{DeriveError, DeriveJunction, Pair as TraitPair, SecretStringError, VrfSigner}; -use crate::{ - crypto::{ByteArray, CryptoType, CryptoTypeId, Derive, Public as TraitPublic, UncheckedFrom}, - hash::{H256, H512}, -}; +#[cfg(feature = "full_crypto")] +use bandersnatch_vrfs::SecretKey; use codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; -use sp_std::ops::Deref; -#[cfg(feature = "std")] -use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use sp_runtime_interface::pass_by::PassByInner; +use sp_std::{boxed::Box, vec::Vec}; /// Identifier used to match public keys against bandersnatch-vrf keys. pub const CRYPTO_ID: CryptoTypeId = CryptoTypeId(*b"bs38"); @@ -240,45 +234,29 @@ impl TraitPair for Pair { Public::unchecked_from(raw) } - /// Sign a message. - fn sign(&self, message: &[u8]) -> Signature { - let mut transcript = Transcript::new(SIGNING_CTX); - transcript.append_slice(message); - let sign: ThinVrfSignature<0> = self.0.sign_thin_vrf(transcript, &[]); - let mut raw = [0; SIGNATURE_SERIALIZED_LEN]; - sign.serialize_compressed(raw.as_mut_slice()); - Signature(raw) + /// Sign raw data. + fn sign(&self, data: &[u8]) -> Signature { + let input = vrf::VrfInput::new(SIGNING_CTX, &[data], &[]) + .expect("less than max input messages; qed"); + self.vrf_sign(&input).signature } /// Verify a signature on a message. /// /// Returns true if the signature is good. - fn verify>( - signature: &Self::Signature, - message: M, - public: &Self::Public, - ) -> bool { - let Ok(public) = PublicKey::deserialize_compressed(public.as_ref()) else { - return false - }; - let Ok(signature) = ThinVrfSignature::<0>::deserialize_compressed(signature.as_ref()) else { - return false - }; - let mut transcript = Transcript::new(SIGNING_CTX); - transcript.append_slice(message.as_ref()); - - let inputs: Vec = Vec::new(); - match signature.verify_thin_vrf(transcript, inputs, &public) { - Ok(ios) => true, - Err(e) => false, - } + fn verify>(signature: &Self::Signature, data: M, public: &Self::Public) -> bool { + let input = vrf::VrfInput::new(SIGNING_CTX, &[data.as_ref()], &[]) + .expect("less than max input messages; qed"); + let signature = vrf::VrfSignature { signature: signature.clone(), preouts: Box::new([]) }; + public.vrf_verify(&input, &signature) } /// Return a vec filled with seed raw data. fn to_raw_vec(&self) -> Vec { // TODO davxy: makes sense??? Should we returne the seed or serialized secret key? // If we return the serialized secret there is no method to reconstruct if ... - unimplemented!() + // unimplemented!() + panic!() } } @@ -290,94 +268,177 @@ impl CryptoType for Pair { /// VRF related types and operations. pub mod vrf { use super::*; + use crate::crypto::VrfCrypto; #[cfg(feature = "full_crypto")] - use crate::crypto::VrfSigner; - use crate::crypto::{VrfCrypto, VrfVerifier}; - - /// VRF transcript ready to be used for VRF sign/verify operations. - pub struct VrfTranscript(Transcript); - - impl VrfTranscript { - /// Build a new transcript ready to be used by a VRF signer/verifier. - pub fn new(label: &'static [u8], data: &[&[u8]]) -> Self { + use bandersnatch_vrfs::CanonicalSerialize; + use bandersnatch_vrfs::{ + CanonicalDeserialize, IntoVrfInput, PublicKey, ThinVrfSignature, Transcript, + }; + + const MAX_VRF_INPUT_MESSAGES: usize = 3; + + /// Input to be used for VRF sign and verify operations. + pub struct VrfInput { + /// Associated Fiat-Shamir transcript + pub(super) transcript: Transcript, + /// VRF input messages + pub(super) messages: Box<[bandersnatch_vrfs::VrfInput]>, + } + + impl VrfInput { + /// Build a new VRF input ready to be used by VRF sign and verify operations. + /// + /// Returns `None` if `messages.len() > MAX_VRF_INPUT_MESSAGES`. + pub fn new( + label: &'static [u8], + transcript_data: &[&[u8]], + messages: &[(&[u8], &[u8])], + ) -> Option { + if messages.len() > MAX_VRF_INPUT_MESSAGES { + return None + } let mut transcript = Transcript::new(label); - data.iter().for_each(|b| transcript.append_slice(b)); - VrfTranscript(transcript) + transcript_data.iter().for_each(|b| transcript.append_slice(b)); + let messages = messages + .into_iter() + .map(|(domain, message)| { + bandersnatch_vrfs::Message { domain, message }.into_vrf_input() + }) + .collect(); + Some(VrfInput { transcript, messages }) } } /// TODO davxy: docs - pub type VrfSignature = super::Signature; + pub struct VrfSignature { + /// VRF signature + pub(super) signature: Signature, + /// VRF pre-outputs + pub(super) preouts: Box<[bandersnatch_vrfs::VrfPreOut]>, + } #[cfg(feature = "full_crypto")] impl VrfCrypto for Pair { type VrfSignature = VrfSignature; - type VrfInput = VrfTranscript; + type VrfInput = VrfInput; } #[cfg(feature = "full_crypto")] impl VrfSigner for Pair { - fn vrf_sign(&self, transcript: &Self::VrfInput) -> Self::VrfSignature { - let sign: ThinVrfSignature<0> = self.0.sign_thin_vrf(transcript.0.clone(), &[]); - let mut raw = [0; SIGNATURE_SERIALIZED_LEN]; - sign.serialize_compressed(raw.as_mut_slice()); - Signature(raw) + fn vrf_sign(&self, input: &Self::VrfInput) -> Self::VrfSignature { + // Hack used because backend signature type is generic over the number of ios + // @burdges can we provide a vec or boxed version? + match input.messages.len() { + 0 => self.vrf_sign_gen::<0>(input), + 1 => self.vrf_sign_gen::<1>(input), + 2 => self.vrf_sign_gen::<2>(input), + 3 => self.vrf_sign_gen::<3>(input), + _ => panic!("Max VRF input messages is set to: {}", MAX_VRF_INPUT_MESSAGES), + } } } impl VrfCrypto for Public { type VrfSignature = VrfSignature; - type VrfInput = VrfTranscript; + type VrfInput = VrfInput; } impl VrfVerifier for Public { - fn vrf_verify(&self, transcript: &Self::VrfInput, signature: &Self::VrfSignature) -> bool { - let Ok(public) = PublicKey::deserialize_compressed(self.as_ref()) else { - return false - }; - let Ok(signature) = ThinVrfSignature::<0>::deserialize_compressed(signature.as_ref()) else { + fn vrf_verify(&self, input: &Self::VrfInput, signature: &Self::VrfSignature) -> bool { + let preouts_len = signature.preouts.len(); + if preouts_len != input.messages.len() { return false - }; - - let inputs: Vec = Vec::new(); - match signature.verify_thin_vrf(transcript.0.clone(), inputs, &public) { - Ok(ios) => true, - Err(e) => false, + } + // Hack used because backend signature type is generic over the number of ios + // @burdges can we provide a vec or boxed version? + match preouts_len { + 0 => self.vrf_verify_gen::<0>(input, signature), + 1 => self.vrf_verify_gen::<1>(input, signature), + 2 => self.vrf_verify_gen::<2>(input, signature), + 3 => self.vrf_verify_gen::<3>(input, signature), + _ => panic!("Max VRF input messages is set to: {}", MAX_VRF_INPUT_MESSAGES), } } } #[cfg(feature = "full_crypto")] impl Pair { - /// Generate bytes from the given VRF configuration. - pub fn make_bytes>( + fn vrf_sign_gen(&self, input: &VrfInput) -> VrfSignature { + let ios: Vec<_> = + input.messages.iter().map(|i| self.0.clone().0.vrf_inout(i.clone())).collect(); + + let sign: ThinVrfSignature = + self.0.sign_thin_vrf(input.transcript.clone(), ios.as_slice()); + + let mut sign_bytes = [0; SIGNATURE_SERIALIZED_LEN]; + // TODO davxy: use ark-scale??? + sign.signature + .serialize_compressed(sign_bytes.as_mut_slice()) + .expect("serialization can't fail"); + VrfSignature { signature: Signature(sign_bytes), preouts: Box::new(sign.preoutputs) } + } + + /// Generate output bytes from the given VRF input. + /// + /// Index is relative to one of the `VrfInput` messages used during construction. + pub fn output_bytes( &self, - _context: &[u8], - _transcript: &VrfTranscript, - ) -> B { - // TODO davxy: the following is for schnorrkel - // let inout = self.0.vrf_create_hash(transcript.0.clone()); - // inout.make_bytes::(context) - B::default() + input: &VrfInput, + index: usize, + ) -> Option<[u8; N]> { + let msg = input.messages.get(index)?.clone(); + let inout = self.0.clone().0.vrf_inout(msg); + let bytes = inout.vrf_output_bytes(input.transcript.clone()); + Some(bytes) } } impl Public { - /// Generate bytes from the given VRF configuration. - pub fn make_bytes>( + fn vrf_verify_gen( &self, - _context: &[u8], - _transcript: &VrfTranscript, - _output: &VrfPreOut, - ) -> Result { - // TODO davxy: the following is for schnorrkel - // let pubkey = schnorrkel::PublicKey::from_bytes(&self.0).map_err(convert_error)?; - // let inout = output - // .0 - // .attach_input_hash(&pubkey, transcript.0.clone()) - // .map_err(convert_error)?; - // Ok(inout.make_bytes::(context)) - Ok(B::default()) + input: &VrfInput, + signature: &VrfSignature, + ) -> bool { + let Ok(public) = PublicKey::deserialize_compressed(self.as_ref()) else { + return false + }; + + let Ok(preouts) = signature + .preouts + .iter() + .map(|o| o.clone()) + .collect::>() + .into_inner() else { + return false + }; + + // Deserialize only the proof, the rest has already been deserialized + // This is another hack used because backend signature type is generic over the number + // of ios. @burdges can we provide a vec or boxed version? + let Ok(signature) = ThinVrfSignature::<0>::deserialize_compressed(signature.signature.as_ref()).map(|s| s.signature) else { + return false + }; + let signature = ThinVrfSignature { signature, preoutputs: preouts }; + + let inputs = input.messages.clone().into_vec(); + + signature.verify_thin_vrf(input.transcript.clone(), inputs, &public).is_ok() + } + } + + impl VrfSignature { + /// Generate output bytes for the given VRF input. + pub fn output_bytes( + &self, + input: &VrfInput, + index: usize, + ) -> Option<[u8; N]> { + let inout = bandersnatch_vrfs::VrfInOut { + input: input.messages.get(index)?.clone().into_vrf_input(), + preoutput: self.preouts.get(index)?.clone(), + }; + let bytes = inout.vrf_output_bytes(input.transcript.clone()); + Some(bytes) } } } @@ -442,24 +503,57 @@ mod tests { let pair = Pair::from_seed(DEV_SEED); let public = pair.public(); - let transcript = VrfTranscript::new(b"test", &[b"foo", b"bar"]); + // let input = VrfInput::new(b"test", &[b"foo", b"bar"], &[]); + let input = VrfInput::new(b"test", &[b"foo", b"bar"], &[(b"msg1", b"hello")]).unwrap(); - let signature = pair.vrf_sign(&transcript); + let signature = pair.vrf_sign(&input); - println!("{}", b2h(signature.as_ref())); + assert!(public.vrf_verify(&input, &signature)); + } - assert!(public.vrf_verify(&transcript, &signature)); + #[test] + fn vrf_sign_verify_bad_inputs() { + use super::vrf::*; + use crate::crypto::{VrfSigner, VrfVerifier}; + + let pair = Pair::from_seed(DEV_SEED); + let public = pair.public(); + + let input = VrfInput::new(b"test", &[b"foo", b"bar"], &[(b"msg1", b"hello")]).unwrap(); + + let signature = pair.vrf_sign(&input); + + let input = VrfInput::new(b"test", &[b"foo", b"bar"], &[]).unwrap(); + assert!(!public.vrf_verify(&input, &signature)); } - // #[test] - // fn vrf_make_bytes_matches() { - // use super::vrf::*; - // use crate::crypto::VrfSigner; - // let pair = Pair::from_seed(DEV_SEED); - // let public = pair.public(); - // let transcript = VrfTranscript::new(b"test", &[b"foo", b"bar"]); + #[test] + fn vrf_make_bytes_matches() { + use super::vrf::*; + let pair = Pair::from_seed(DEV_SEED); - // let ctx = b"randbytes"; - // let b1 = pair.make_bytes::<[u8; 32]>(ctx, &transcript); - // } + let input = VrfInput::new( + b"test", + &[b"proto", b"foo"], + &[(b"dom1", b"dat1"), (b"dom2", b"dat2"), (b"dom3", b"dat3")], + ) + .unwrap(); + + let sign = pair.vrf_sign(&input); + + let out0 = pair.output_bytes::<32>(&input, 0).unwrap(); + let out1 = sign.output_bytes::<32>(&input, 0).unwrap(); + assert_eq!(out0, out1); + + let out0 = pair.output_bytes::<48>(&input, 1).unwrap(); + let out1 = sign.output_bytes::<48>(&input, 1).unwrap(); + assert_eq!(out0, out1); + + let out0 = pair.output_bytes::<64>(&input, 2).unwrap(); + let out1 = sign.output_bytes::<64>(&input, 2).unwrap(); + assert_eq!(out0, out1); + + assert!(pair.output_bytes::<8>(&input, 3).is_none()); + assert!(sign.output_bytes::<8>(&input, 3).is_none()); + } } diff --git a/primitives/keystore/src/lib.rs b/primitives/keystore/src/lib.rs index 087efcfc261d7..23fe2f693f5e5 100644 --- a/primitives/keystore/src/lib.rs +++ b/primitives/keystore/src/lib.rs @@ -184,7 +184,7 @@ pub trait Keystore: Send + Sync { &self, key_type: KeyTypeId, public: &bandersnatch::Public, - transcript: &bandersnatch::vrf::VrfTranscript, + input: &bandersnatch::vrf::VrfInput, ) -> Result, Error>; /// Insert a new secret key. diff --git a/primitives/keystore/src/testing.rs b/primitives/keystore/src/testing.rs index 0b8304c0184bb..c67a4f3c9b60f 100644 --- a/primitives/keystore/src/testing.rs +++ b/primitives/keystore/src/testing.rs @@ -219,9 +219,9 @@ impl Keystore for MemoryKeystore { &self, key_type: KeyTypeId, public: &bandersnatch::Public, - transcript: &bandersnatch::vrf::VrfTranscript, + input: &bandersnatch::vrf::VrfInput, ) -> Result, Error> { - self.vrf_sign::(key_type, public, transcript) + self.vrf_sign::(key_type, public, input) } fn insert(&self, key_type: KeyTypeId, suri: &str, public: &[u8]) -> Result<(), ()> { From 1b2da2cb65f30424990a7e2798a94aa5f02aa192 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Thu, 27 Apr 2023 10:10:28 +0200 Subject: [PATCH 11/22] Disable std feature --- primitives/core/Cargo.toml | 3 ++- primitives/core/src/bandersnatch.rs | 10 +++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/primitives/core/Cargo.toml b/primitives/core/Cargo.toml index 064f196f2aa2a..a496ded2bab67 100644 --- a/primitives/core/Cargo.toml +++ b/primitives/core/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] bandersnatch_vrfs = { git = "https://github.com/w3f/ring-vrf", default-features = false } -arrayvec = { version = "0.7.2" } +arrayvec = { version = "0.7.2", default-features = false } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive","max-encoded-len"] } scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } @@ -71,6 +71,7 @@ bench = false [features] default = ["std"] std = [ + "arrayvec/std", "merlin/std", "full_crypto", "log/std", diff --git a/primitives/core/src/bandersnatch.rs b/primitives/core/src/bandersnatch.rs index adb07c9312e71..8d937c9cb721b 100644 --- a/primitives/core/src/bandersnatch.rs +++ b/primitives/core/src/bandersnatch.rs @@ -367,15 +367,19 @@ pub mod vrf { let ios: Vec<_> = input.messages.iter().map(|i| self.0.clone().0.vrf_inout(i.clone())).collect(); - let sign: ThinVrfSignature = + let signature: ThinVrfSignature = self.0.sign_thin_vrf(input.transcript.clone(), ios.as_slice()); let mut sign_bytes = [0; SIGNATURE_SERIALIZED_LEN]; // TODO davxy: use ark-scale??? - sign.signature + signature + .signature .serialize_compressed(sign_bytes.as_mut_slice()) .expect("serialization can't fail"); - VrfSignature { signature: Signature(sign_bytes), preouts: Box::new(sign.preoutputs) } + VrfSignature { + signature: Signature(sign_bytes), + preouts: Box::new(signature.preoutputs), + } } /// Generate output bytes from the given VRF input. From 5ef6ed906a2f5385727492512a3534e2d206de20 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Thu, 4 May 2023 18:12:27 +0200 Subject: [PATCH 12/22] Fix after master merge --- Cargo.lock | 109 +++++++++++++++--- client/keystore/src/local.rs | 9 +- primitives/application-crypto/src/traits.rs | 2 +- primitives/core/src/bandersnatch.rs | 119 ++++++++++++++------ primitives/core/src/crypto.rs | 4 +- primitives/keystore/src/lib.rs | 2 +- primitives/keystore/src/testing.rs | 4 +- 7 files changed, 184 insertions(+), 65 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0c1c8bc67cca4..4c8df468ffac2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -382,6 +382,20 @@ dependencies = [ "hashbrown 0.13.2", ] +[[package]] +name = "ark-secret-scalar" +version = "0.0.2" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", + "ark-transcript", + "digest 0.10.6", + "rand_core 0.6.4", + "zeroize", +] + [[package]] name = "ark-serialize" version = "0.4.2" @@ -422,6 +436,7 @@ dependencies = [ "ark-ff", "ark-serialize", "ark-std", + "digest 0.10.6", "rand_core 0.6.4", "sha3", ] @@ -691,7 +706,10 @@ dependencies = [ "ark-serialize", "ark-std", "dleq_vrf", + "fflonk", + "merlin 3.0.0", "rand_core 0.6.4", + "ring 0.1.0", "sha2 0.10.6", "zeroize", ] @@ -1323,6 +1341,20 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "common" +version = "0.1.0" +source = "git+https://github.com/w3f/ring-proof#56ca3cb17f2e9ea5f9c675327689aa1136aa7b9f" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "fflonk", + "merlin 3.0.0", +] + [[package]] name = "concurrent-queue" version = "2.1.0" @@ -2043,6 +2075,7 @@ version = "0.0.2" dependencies = [ "ark-ec", "ark-ff", + "ark-secret-scalar", "ark-serialize", "ark-std", "ark-transcript", @@ -2384,6 +2417,19 @@ dependencies = [ "subtle", ] +[[package]] +name = "fflonk" +version = "0.1.0" +source = "git+https://github.com/w3f/fflonk#cbcfc4524779872fb9883110f07890cdc46633ea" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "merlin 3.0.0", +] + [[package]] name = "fiat-crypto" version = "0.1.17" @@ -4467,7 +4513,7 @@ dependencies = [ "futures-rustls", "libp2p-core 0.39.0", "rcgen 0.10.0", - "ring", + "ring 0.16.20", "rustls 0.20.8", "thiserror", "webpki 0.22.0", @@ -4889,6 +4935,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "merlin" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" +dependencies = [ + "byteorder", + "keccak", + "rand_core 0.6.4", + "zeroize", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -8075,7 +8133,7 @@ checksum = "72ef4ced82a24bb281af338b9e8f94429b6eca01b4e66d899f40031f074e74c9" dependencies = [ "bytes", "rand 0.8.5", - "ring", + "ring 0.16.20", "rustc-hash", "rustls 0.20.8", "slab", @@ -8225,7 +8283,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6413f3de1edee53342e6138e75b56d32e7bc6e332b3bd62d497b1929d4cfbcdd" dependencies = [ "pem", - "ring", + "ring 0.16.20", "time 0.3.20", "x509-parser 0.13.2", "yasna", @@ -8238,7 +8296,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffbe84efe2f38dea12e9bfc1f65377fdf03e53a18cb3b995faedf7934c7e785b" dependencies = [ "pem", - "ring", + "ring 0.16.20", "time 0.3.20", "yasna", ] @@ -8373,6 +8431,21 @@ dependencies = [ "subtle", ] +[[package]] +name = "ring" +version = "0.1.0" +source = "git+https://github.com/w3f/ring-proof#56ca3cb17f2e9ea5f9c675327689aa1136aa7b9f" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "common", + "fflonk", + "merlin 3.0.0", +] + [[package]] name = "ring" version = "0.16.20" @@ -8554,7 +8627,7 @@ checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" dependencies = [ "base64 0.13.1", "log", - "ring", + "ring 0.16.20", "sct 0.6.1", "webpki 0.21.4", ] @@ -8566,7 +8639,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" dependencies = [ "log", - "ring", + "ring 0.16.20", "sct 0.7.0", "webpki 0.22.0", ] @@ -10155,7 +10228,7 @@ dependencies = [ "arrayvec 0.5.2", "curve25519-dalek 2.1.3", "getrandom 0.1.16", - "merlin", + "merlin 2.0.1", "rand 0.7.3", "rand_core 0.5.1", "sha2 0.8.2", @@ -10181,7 +10254,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" dependencies = [ - "ring", + "ring 0.16.20", "untrusted", ] @@ -10191,7 +10264,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" dependencies = [ - "ring", + "ring 0.16.20", "untrusted", ] @@ -10529,7 +10602,7 @@ dependencies = [ "chacha20poly1305", "curve25519-dalek 4.0.0-rc.1", "rand_core 0.6.4", - "ring", + "ring 0.16.20", "rustc_version 0.4.0", "sha2 0.10.6", "subtle", @@ -10843,7 +10916,7 @@ dependencies = [ "lazy_static", "libsecp256k1", "log", - "merlin", + "merlin 2.0.1", "parity-scale-codec", "parking_lot 0.12.1", "paste", @@ -11535,7 +11608,7 @@ dependencies = [ "lazy_static", "md-5", "rand 0.8.5", - "ring", + "ring 0.16.20", "subtle", "thiserror", "tokio", @@ -12540,7 +12613,7 @@ dependencies = [ "log", "md-5", "rand 0.8.5", - "ring", + "ring 0.16.20", "stun", "thiserror", "tokio", @@ -13215,7 +13288,7 @@ version = "0.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" dependencies = [ - "ring", + "ring 0.16.20", "untrusted", ] @@ -13225,7 +13298,7 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" dependencies = [ - "ring", + "ring 0.16.20", "untrusted", ] @@ -13254,7 +13327,7 @@ dependencies = [ "rand 0.8.5", "rcgen 0.9.3", "regex", - "ring", + "ring 0.16.20", "rtcp", "rtp", "rustls 0.19.1", @@ -13319,7 +13392,7 @@ dependencies = [ "rand 0.8.5", "rand_core 0.6.4", "rcgen 0.9.3", - "ring", + "ring 0.16.20", "rustls 0.19.1", "sec1 0.3.0", "serde", @@ -13769,7 +13842,7 @@ dependencies = [ "lazy_static", "nom", "oid-registry 0.4.0", - "ring", + "ring 0.16.20", "rusticata-macros", "thiserror", "time 0.3.20", diff --git a/client/keystore/src/local.rs b/client/keystore/src/local.rs index c766fd508ccb4..b64e7036e40e6 100644 --- a/client/keystore/src/local.rs +++ b/client/keystore/src/local.rs @@ -254,15 +254,16 @@ impl Keystore for LocalKeystore { self.sign::(key_type, public, msg) } - // Maybe we can expose just one bandersnatch sign here (the above one reduces to this when input - // len = 0) + // TODO DAVXY + // Maybe we can expose just this bandersnatch sign (the above one reduces to this with + // input len = 0) fn bandersnatch_vrf_sign( &self, key_type: KeyTypeId, public: &bandersnatch::Public, - input: &bandersnatch::vrf::VrfInput, + data: &bandersnatch::vrf::VrfSignData, ) -> std::result::Result, TraitError> { - self.vrf_sign::(key_type, public, input) + self.vrf_sign::(key_type, public, data) } fn insert( diff --git a/primitives/application-crypto/src/traits.rs b/primitives/application-crypto/src/traits.rs index 88d4bf36915d0..d8869f19d0dab 100644 --- a/primitives/application-crypto/src/traits.rs +++ b/primitives/application-crypto/src/traits.rs @@ -31,7 +31,7 @@ use sp_std::{fmt::Debug, vec::Vec}; /// Typically, the implementers of this trait are its associated types themselves. /// This provides a convenient way to access generic information about the scheme /// given any of the associated types. -pub trait AppCrypto: 'static + Send + Sync + Sized + CryptoType + Clone { +pub trait AppCrypto: 'static + Send + Sized + CryptoType + Clone { /// Identifier for application-specific key type. const ID: KeyTypeId; diff --git a/primitives/core/src/bandersnatch.rs b/primitives/core/src/bandersnatch.rs index 8d937c9cb721b..b5db9d4e3211b 100644 --- a/primitives/core/src/bandersnatch.rs +++ b/primitives/core/src/bandersnatch.rs @@ -22,10 +22,10 @@ #[cfg(feature = "std")] use crate::crypto::Ss58Codec; use crate::crypto::{ - ByteArray, CryptoType, CryptoTypeId, Derive, Public as TraitPublic, UncheckedFrom, VrfVerifier, + ByteArray, CryptoType, CryptoTypeId, Derive, Public as TraitPublic, UncheckedFrom, VrfPublic, }; #[cfg(feature = "full_crypto")] -use crate::crypto::{DeriveError, DeriveJunction, Pair as TraitPair, SecretStringError, VrfSigner}; +use crate::crypto::{DeriveError, DeriveJunction, Pair as TraitPair, SecretStringError, VrfSecret}; #[cfg(feature = "full_crypto")] use bandersnatch_vrfs::SecretKey; @@ -238,7 +238,7 @@ impl TraitPair for Pair { fn sign(&self, data: &[u8]) -> Signature { let input = vrf::VrfInput::new(SIGNING_CTX, &[data], &[]) .expect("less than max input messages; qed"); - self.vrf_sign(&input).signature + self.vrf_sign(&input.into()).signature } /// Verify a signature on a message. @@ -247,8 +247,11 @@ impl TraitPair for Pair { fn verify>(signature: &Self::Signature, data: M, public: &Self::Public) -> bool { let input = vrf::VrfInput::new(SIGNING_CTX, &[data.as_ref()], &[]) .expect("less than max input messages; qed"); - let signature = vrf::VrfSignature { signature: signature.clone(), preouts: Box::new([]) }; - public.vrf_verify(&input, &signature) + let signature = vrf::VrfSignature { + signature: signature.clone(), + output: vrf::VrfOutput { preouts: Box::new([]) }, + }; + public.vrf_verify(&input.into(), &signature) } /// Return a vec filled with seed raw data. @@ -278,6 +281,7 @@ pub mod vrf { const MAX_VRF_INPUT_MESSAGES: usize = 3; /// Input to be used for VRF sign and verify operations. + #[derive(Clone)] pub struct VrfInput { /// Associated Fiat-Shamir transcript pub(super) transcript: Transcript, @@ -297,7 +301,7 @@ pub mod vrf { if messages.len() > MAX_VRF_INPUT_MESSAGES { return None } - let mut transcript = Transcript::new(label); + let mut transcript = Transcript::new_labeled(label); transcript_data.iter().for_each(|b| transcript.append_slice(b)); let messages = messages .into_iter() @@ -307,6 +311,34 @@ pub mod vrf { .collect(); Some(VrfInput { transcript, messages }) } + + /// Map to `VrfSignData` + pub fn into_sign_data(self) -> VrfSignData { + self.into() + } + } + + /// TODO davxy docs + pub struct VrfOutput { + /// VRF pre-outputs + pub(super) preouts: Box<[bandersnatch_vrfs::VrfPreOut]>, + } + + /// TODO davxy docs + pub struct VrfSignData { + pub(super) input: VrfInput, + } + + impl From for VrfSignData { + fn from(input: VrfInput) -> Self { + VrfSignData { input } + } + } + + impl AsRef for VrfSignData { + fn as_ref(&self) -> &VrfInput { + &self.input + } } /// TODO davxy: docs @@ -314,20 +346,23 @@ pub mod vrf { /// VRF signature pub(super) signature: Signature, /// VRF pre-outputs - pub(super) preouts: Box<[bandersnatch_vrfs::VrfPreOut]>, + pub(super) output: VrfOutput, } #[cfg(feature = "full_crypto")] impl VrfCrypto for Pair { - type VrfSignature = VrfSignature; type VrfInput = VrfInput; + type VrfOutput = VrfOutput; + type VrfSignData = VrfSignData; + type VrfSignature = VrfSignature; } #[cfg(feature = "full_crypto")] - impl VrfSigner for Pair { - fn vrf_sign(&self, input: &Self::VrfInput) -> Self::VrfSignature { + impl VrfSecret for Pair { + fn vrf_sign(&self, data: &Self::VrfSignData) -> Self::VrfSignature { // Hack used because backend signature type is generic over the number of ios // @burdges can we provide a vec or boxed version? + let input = &data.input; match input.messages.len() { 0 => self.vrf_sign_gen::<0>(input), 1 => self.vrf_sign_gen::<1>(input), @@ -336,21 +371,35 @@ pub mod vrf { _ => panic!("Max VRF input messages is set to: {}", MAX_VRF_INPUT_MESSAGES), } } + + fn vrf_output(&self, input: &Self::VrfInput) -> Self::VrfOutput { + let ios: Vec<_> = + input.messages.iter().map(|i| self.0.clone().0.vrf_inout(i.clone())).collect(); + + let preout = bandersnatch_vrfs::vrf::collect_preoutputs_vec(&ios); + let preouts = preout.into_boxed_slice(); + + let output = VrfOutput { preouts }; + output + } } impl VrfCrypto for Public { - type VrfSignature = VrfSignature; type VrfInput = VrfInput; + type VrfOutput = VrfOutput; + type VrfSignData = VrfSignData; + type VrfSignature = VrfSignature; } - impl VrfVerifier for Public { - fn vrf_verify(&self, input: &Self::VrfInput, signature: &Self::VrfSignature) -> bool { - let preouts_len = signature.preouts.len(); - if preouts_len != input.messages.len() { + impl VrfPublic for Public { + fn vrf_verify(&self, data: &Self::VrfSignData, signature: &Self::VrfSignature) -> bool { + let preouts_len = signature.output.preouts.len(); + if preouts_len != data.input.messages.len() { return false } // Hack used because backend signature type is generic over the number of ios // @burdges can we provide a vec or boxed version? + let input = &data.input; match preouts_len { 0 => self.vrf_verify_gen::<0>(input, signature), 1 => self.vrf_verify_gen::<1>(input, signature), @@ -376,10 +425,8 @@ pub mod vrf { .signature .serialize_compressed(sign_bytes.as_mut_slice()) .expect("serialization can't fail"); - VrfSignature { - signature: Signature(sign_bytes), - preouts: Box::new(signature.preoutputs), - } + let output = VrfOutput { preouts: Box::new(signature.preoutputs) }; + VrfSignature { signature: Signature(sign_bytes), output } } /// Generate output bytes from the given VRF input. @@ -408,6 +455,7 @@ pub mod vrf { }; let Ok(preouts) = signature + .output .preouts .iter() .map(|o| o.clone()) @@ -439,7 +487,7 @@ pub mod vrf { ) -> Option<[u8; N]> { let inout = bandersnatch_vrfs::VrfInOut { input: input.messages.get(index)?.clone().into_vrf_input(), - preoutput: self.preouts.get(index)?.clone(), + preoutput: self.output.preouts.get(index)?.clone(), }; let bytes = inout.vrf_output_bytes(input.transcript.clone()); Some(bytes) @@ -449,8 +497,8 @@ pub mod vrf { #[cfg(test)] mod tests { - use super::*; - use crate::crypto::DEV_PHRASE; + use super::{vrf::*, *}; + use crate::crypto::{VrfPublic, VrfSecret, DEV_PHRASE}; const DEV_SEED: &[u8; SEED_SERIALIZED_LEN] = &[0; SEED_SERIALIZED_LEN]; fn b2h(bytes: &[u8]) -> String { @@ -502,38 +550,35 @@ mod tests { #[test] fn vrf_sign_verify() { - use super::vrf::*; - use crate::crypto::{VrfSigner, VrfVerifier}; - let pair = Pair::from_seed(DEV_SEED); let public = pair.public(); // let input = VrfInput::new(b"test", &[b"foo", b"bar"], &[]); - let input = VrfInput::new(b"test", &[b"foo", b"bar"], &[(b"msg1", b"hello")]).unwrap(); + let data = VrfInput::new(b"test", &[b"foo", b"bar"], &[(b"msg1", b"hello")]) + .unwrap() + .into(); - let signature = pair.vrf_sign(&input); + let signature = pair.vrf_sign(&data); - assert!(public.vrf_verify(&input, &signature)); + assert!(public.vrf_verify(&data, &signature)); } #[test] fn vrf_sign_verify_bad_inputs() { - use super::vrf::*; - use crate::crypto::{VrfSigner, VrfVerifier}; - let pair = Pair::from_seed(DEV_SEED); let public = pair.public(); - let input = VrfInput::new(b"test", &[b"foo", b"bar"], &[(b"msg1", b"hello")]).unwrap(); + let data = VrfInput::new(b"test", &[b"foo", b"bar"], &[(b"msg1", b"hello")]) + .unwrap() + .into(); - let signature = pair.vrf_sign(&input); + let signature = pair.vrf_sign(&data); - let input = VrfInput::new(b"test", &[b"foo", b"bar"], &[]).unwrap(); - assert!(!public.vrf_verify(&input, &signature)); + let data = VrfInput::new(b"test", &[b"foo", b"bar"], &[]).unwrap().into(); + assert!(!public.vrf_verify(&data, &signature)); } #[test] fn vrf_make_bytes_matches() { - use super::vrf::*; let pair = Pair::from_seed(DEV_SEED); let input = VrfInput::new( @@ -543,7 +588,7 @@ mod tests { ) .unwrap(); - let sign = pair.vrf_sign(&input); + let sign = pair.vrf_sign(&input.clone().into()); let out0 = pair.output_bytes::<32>(&input, 0).unwrap(); let out1 = sign.output_bytes::<32>(&input, 0).unwrap(); diff --git a/primitives/core/src/crypto.rs b/primitives/core/src/crypto.rs index ccdeb760413b2..ae4b067b2c192 100644 --- a/primitives/core/src/crypto.rs +++ b/primitives/core/src/crypto.rs @@ -478,7 +478,7 @@ pub trait ByteArray: AsRef<[u8]> + AsMut<[u8]> + for<'a> TryFrom<&'a [u8], Error } /// Trait suitable for typical cryptographic key public type. -pub trait Public: ByteArray + Derive + CryptoType + PartialEq + Eq + Clone + Send + Sync {} +pub trait Public: ByteArray + Derive + CryptoType + PartialEq + Eq + Clone + Send {} /// An opaque 32-byte cryptographic identifier. #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, MaxEncodedLen, TypeInfo)] @@ -826,7 +826,7 @@ impl sp_std::str::FromStr for SecretUri { /// /// For now it just specifies how to create a key from a phrase and derivation path. #[cfg(feature = "full_crypto")] -pub trait Pair: CryptoType + Sized + Clone + Send + Sync + 'static { +pub trait Pair: CryptoType + Sized + Clone + Send + 'static { /// The type which is used to encode a public key. type Public: Public + Hash; diff --git a/primitives/keystore/src/lib.rs b/primitives/keystore/src/lib.rs index 37a6cedbd5b0d..30829d1c2a2a0 100644 --- a/primitives/keystore/src/lib.rs +++ b/primitives/keystore/src/lib.rs @@ -195,7 +195,7 @@ pub trait Keystore: Send + Sync { &self, key_type: KeyTypeId, public: &bandersnatch::Public, - input: &bandersnatch::vrf::VrfInput, + input: &bandersnatch::vrf::VrfSignData, ) -> Result, Error>; /// Insert a new secret key. diff --git a/primitives/keystore/src/testing.rs b/primitives/keystore/src/testing.rs index 38c5f61d69e57..0d691fa9335f2 100644 --- a/primitives/keystore/src/testing.rs +++ b/primitives/keystore/src/testing.rs @@ -238,9 +238,9 @@ impl Keystore for MemoryKeystore { &self, key_type: KeyTypeId, public: &bandersnatch::Public, - input: &bandersnatch::vrf::VrfInput, + data: &bandersnatch::vrf::VrfSignData, ) -> Result, Error> { - self.vrf_sign::(key_type, public, input) + self.vrf_sign::(key_type, public, data) } fn insert(&self, key_type: KeyTypeId, suri: &str, public: &[u8]) -> Result<(), ()> { From 80c9f0a984c277d10310d3377faa8a41440964ff Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Thu, 4 May 2023 19:16:26 +0200 Subject: [PATCH 13/22] Patch no-std compatible w3f crates --- Cargo.lock | 3 --- Cargo.toml | 8 ++++++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4c8df468ffac2..18ff2104b2118 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1344,7 +1344,6 @@ dependencies = [ [[package]] name = "common" version = "0.1.0" -source = "git+https://github.com/w3f/ring-proof#56ca3cb17f2e9ea5f9c675327689aa1136aa7b9f" dependencies = [ "ark-ec", "ark-ff", @@ -2420,7 +2419,6 @@ dependencies = [ [[package]] name = "fflonk" version = "0.1.0" -source = "git+https://github.com/w3f/fflonk#cbcfc4524779872fb9883110f07890cdc46633ea" dependencies = [ "ark-ec", "ark-ff", @@ -8434,7 +8432,6 @@ dependencies = [ [[package]] name = "ring" version = "0.1.0" -source = "git+https://github.com/w3f/ring-proof#56ca3cb17f2e9ea5f9c675327689aa1136aa7b9f" dependencies = [ "ark-ec", "ark-ff", diff --git a/Cargo.toml b/Cargo.toml index 35e411911c654..e6becaae429ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -337,3 +337,11 @@ codegen-units = 1 [patch."https://github.com/w3f/ring-vrf"] bandersnatch_vrfs = { path = "/mnt/ssd/users/develop/w3f/ring-vrf/bandersnatch_vrfs" } dleq_vrf = { path = "/mnt/ssd/users/develop/w3f/ring-vrf/dleq_vrf" } + +# https://github.com/w3f/fflonk/pull/31 +[patch."https://github.com/w3f/fflonk"] +fflonk = { path = "/mnt/ssd/users/develop/w3f/fflonk" } + +# https://github.com/w3f/ring-proof/pull/5 +[patch."https://github.com/w3f/ring-proof"] +ring = { path = "/mnt/ssd/users/develop/w3f/ring-proof/ring" } From dc324db228d89c4e05af59f0aca4356aa7797bf7 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Thu, 11 May 2023 15:57:07 +0200 Subject: [PATCH 14/22] put bandersnatch under experimental flag --- client/keystore/src/local.rs | 11 ++++++++--- primitives/application-crypto/src/lib.rs | 1 + primitives/core/src/lib.rs | 1 + primitives/keystore/src/lib.rs | 12 +++++++++--- primitives/keystore/src/testing.rs | 11 ++++++++--- 5 files changed, 27 insertions(+), 9 deletions(-) diff --git a/client/keystore/src/local.rs b/client/keystore/src/local.rs index a3e7486399686..6b01ce6b80330 100644 --- a/client/keystore/src/local.rs +++ b/client/keystore/src/local.rs @@ -19,13 +19,14 @@ use parking_lot::RwLock; use sp_application_crypto::{AppCrypto, AppPair, IsWrappedBy}; +#[cfg(feature = "bandersnatch-experimental")] +use sp_core::bandersnatch; +#[cfg(feature = "bls-experimental")] +use sp_core::{bls377, bls381}; use sp_core::{ - bandersnatch, crypto::{ByteArray, ExposeSecret, KeyTypeId, Pair as CorePair, SecretString, VrfSecret}, ecdsa, ed25519, sr25519, }; -#[cfg(feature = "bls-experimental")] -use sp_core::{bls377, bls381}; use sp_keystore::{Error as TraitError, Keystore, KeystorePtr}; use std::{ collections::HashMap, @@ -235,10 +236,12 @@ impl Keystore for LocalKeystore { Ok(sig) } + #[cfg(feature = "bandersnatch-experimental")] fn bandersnatch_public_keys(&self, key_type: KeyTypeId) -> Vec { self.public_keys::(key_type) } + #[cfg(feature = "bandersnatch-experimental")] fn bandersnatch_generate_new( &self, key_type: KeyTypeId, @@ -247,6 +250,7 @@ impl Keystore for LocalKeystore { self.generate_new::(key_type, seed) } + #[cfg(feature = "bandersnatch-experimental")] fn bandersnatch_sign( &self, key_type: KeyTypeId, @@ -259,6 +263,7 @@ impl Keystore for LocalKeystore { // TODO DAVXY // Maybe we can expose just this bandersnatch sign (the above one reduces to this with // input len = 0) + #[cfg(feature = "bandersnatch-experimental")] fn bandersnatch_vrf_sign( &self, key_type: KeyTypeId, diff --git a/primitives/application-crypto/src/lib.rs b/primitives/application-crypto/src/lib.rs index 2c4f4835bd20a..bb0df705bf108 100644 --- a/primitives/application-crypto/src/lib.rs +++ b/primitives/application-crypto/src/lib.rs @@ -45,6 +45,7 @@ pub use traits::*; mod traits; +#[cfg(feature = "bandersnatch-experimental")] pub mod bandersnatch; #[cfg(feature = "bls-experimental")] pub mod bls377; diff --git a/primitives/core/src/lib.rs b/primitives/core/src/lib.rs index d00bc4dd70d5f..9bdebf23bac8f 100644 --- a/primitives/core/src/lib.rs +++ b/primitives/core/src/lib.rs @@ -55,6 +55,7 @@ pub mod crypto; pub mod hexdisplay; pub use paste; +#[cfg(feature = "bandersnatch-experimental")] pub mod bandersnatch; #[cfg(feature = "bls-experimental")] pub mod bls; diff --git a/primitives/keystore/src/lib.rs b/primitives/keystore/src/lib.rs index c115e716e20d5..71bf2e192f510 100644 --- a/primitives/keystore/src/lib.rs +++ b/primitives/keystore/src/lib.rs @@ -19,13 +19,14 @@ pub mod testing; +#[cfg(feature = "bandersnatch-experimental")] +use sp_core::bandersnatch; +#[cfg(feature = "bls-experimental")] +use sp_core::{bls377, bls381}; use sp_core::{ - bandersnatch, crypto::{ByteArray, CryptoTypeId, KeyTypeId}, ecdsa, ed25519, sr25519, }; -#[cfg(feature = "bls-experimental")] -use sp_core::{bls377, bls381}; use std::sync::Arc; @@ -175,9 +176,11 @@ pub trait Keystore: Send + Sync { msg: &[u8; 32], ) -> Result, Error>; + #[cfg(feature = "bandersnatch-experimental")] /// DAVXY TODO fn bandersnatch_public_keys(&self, key_type: KeyTypeId) -> Vec; + #[cfg(feature = "bandersnatch-experimental")] /// DAVXY TODO fn bandersnatch_generate_new( &self, @@ -185,6 +188,7 @@ pub trait Keystore: Send + Sync { seed: Option<&str>, ) -> Result; + #[cfg(feature = "bandersnatch-experimental")] /// DAVXY TODO fn bandersnatch_sign( &self, @@ -193,6 +197,7 @@ pub trait Keystore: Send + Sync { msg: &[u8], ) -> Result, Error>; + #[cfg(feature = "bandersnatch-experimental")] /// DAVXY TODO fn bandersnatch_vrf_sign( &self, @@ -319,6 +324,7 @@ pub trait Keystore: Send + Sync { self.ecdsa_sign(id, &public, msg)?.map(|s| s.encode()) }, + #[cfg(feature = "bandersnatch-experimental")] bandersnatch::CRYPTO_ID => { let public = bandersnatch::Public::from_slice(public) .map_err(|_| Error::ValidationError("Invalid public key format".into()))?; diff --git a/primitives/keystore/src/testing.rs b/primitives/keystore/src/testing.rs index ff6040d775c1a..ccdb1672eed44 100644 --- a/primitives/keystore/src/testing.rs +++ b/primitives/keystore/src/testing.rs @@ -19,13 +19,14 @@ use crate::{Error, Keystore, KeystorePtr}; +#[cfg(feature = "bandersnatch-experimental")] +use sp_core::bandersnatch; +#[cfg(feature = "bls-experimental")] +use sp_core::{bls377, bls381}; use sp_core::{ - bandersnatch, crypto::{ByteArray, KeyTypeId, Pair, VrfSecret}, ecdsa, ed25519, sr25519, }; -#[cfg(feature = "bls-experimental")] -use sp_core::{bls377, bls381}; use parking_lot::RwLock; use std::{collections::HashMap, sync::Arc}; @@ -215,10 +216,12 @@ impl Keystore for MemoryKeystore { Ok(sig) } + #[cfg(feature = "bandersnatch-experimental")] fn bandersnatch_public_keys(&self, key_type: KeyTypeId) -> Vec { self.public_keys::(key_type) } + #[cfg(feature = "bandersnatch-experimental")] fn bandersnatch_generate_new( &self, key_type: KeyTypeId, @@ -227,6 +230,7 @@ impl Keystore for MemoryKeystore { self.generate_new::(key_type, seed) } + #[cfg(feature = "bandersnatch-experimental")] fn bandersnatch_sign( &self, key_type: KeyTypeId, @@ -236,6 +240,7 @@ impl Keystore for MemoryKeystore { self.sign::(key_type, public, msg) } + #[cfg(feature = "bandersnatch-experimental")] fn bandersnatch_vrf_sign( &self, key_type: KeyTypeId, From b02fc4b20558c819dbca47a1d3af78ef6808f6f9 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Thu, 11 May 2023 17:13:07 +0200 Subject: [PATCH 15/22] point to patched repo --- Cargo.lock | 7 +++++++ Cargo.toml | 12 ++---------- primitives/core/Cargo.toml | 7 ++++++- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e6c19894eb250..0e60c6ba1b84a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -399,6 +399,7 @@ dependencies = [ [[package]] name = "ark-secret-scalar" version = "0.0.2" +source = "git+https://github.com/davxy/ring-vrf?branch=davxy-expose-some-stuff#c54be13d0b42cf239255482a78c19a703b903de9" dependencies = [ "ark-ec", "ark-ff", @@ -446,6 +447,7 @@ dependencies = [ [[package]] name = "ark-transcript" version = "0.0.2" +source = "git+https://github.com/davxy/ring-vrf?branch=davxy-expose-some-stuff#c54be13d0b42cf239255482a78c19a703b903de9" dependencies = [ "ark-ff", "ark-serialize", @@ -717,6 +719,7 @@ dependencies = [ [[package]] name = "bandersnatch_vrfs" version = "0.0.1" +source = "git+https://github.com/davxy/ring-vrf?branch=davxy-expose-some-stuff#c54be13d0b42cf239255482a78c19a703b903de9" dependencies = [ "ark-bls12-381", "ark-ec", @@ -1364,6 +1367,7 @@ dependencies = [ [[package]] name = "common" version = "0.1.0" +source = "git+https://github.com/w3f/ring-proof#1e42bb632263f4dff86b400ec9a13af21db72360" dependencies = [ "ark-ec", "ark-ff", @@ -2111,6 +2115,7 @@ checksum = "210ec60ae7d710bed8683e333e9d2855a8a56a3e9892b38bad3bb0d4d29b0d5e" [[package]] name = "dleq_vrf" version = "0.0.2" +source = "git+https://github.com/davxy/ring-vrf?branch=davxy-expose-some-stuff#c54be13d0b42cf239255482a78c19a703b903de9" dependencies = [ "ark-ec", "ark-ff", @@ -2455,6 +2460,7 @@ dependencies = [ [[package]] name = "fflonk" version = "0.1.0" +source = "git+https://github.com/w3f/fflonk#f60bc946e2a4340b1c2d00d30c654e82a5887983" dependencies = [ "ark-ec", "ark-ff", @@ -8575,6 +8581,7 @@ dependencies = [ [[package]] name = "ring" version = "0.1.0" +source = "git+https://github.com/w3f/ring-proof#1e42bb632263f4dff86b400ec9a13af21db72360" dependencies = [ "ark-ec", "ark-ff", diff --git a/Cargo.toml b/Cargo.toml index af8133f9af482..fd26f903a448d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -336,13 +336,5 @@ lto = "fat" codegen-units = 1 [patch."https://github.com/w3f/ring-vrf"] -bandersnatch_vrfs = { path = "/mnt/ssd/users/develop/w3f/ring-vrf/bandersnatch_vrfs" } -dleq_vrf = { path = "/mnt/ssd/users/develop/w3f/ring-vrf/dleq_vrf" } - -# https://github.com/w3f/fflonk/pull/31 -[patch."https://github.com/w3f/fflonk"] -fflonk = { path = "/mnt/ssd/users/develop/w3f/fflonk" } - -# https://github.com/w3f/ring-proof/pull/5 -[patch."https://github.com/w3f/ring-proof"] -ring = { path = "/mnt/ssd/users/develop/w3f/ring-proof/ring" } +bandersnatch_vrfs = { git = "https://github.com/davxy/ring-vrf", branch = "davxy-expose-some-stuff" } +# bandersnatch_vrfs = { path = "/mnt/ssd/users/develop/w3f/ring-vrf/bandersnatch_vrfs" } diff --git a/primitives/core/Cargo.toml b/primitives/core/Cargo.toml index 971834c2a80a1..23db60060090c 100644 --- a/primitives/core/Cargo.toml +++ b/primitives/core/Cargo.toml @@ -13,7 +13,7 @@ documentation = "https://docs.rs/sp-core" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -bandersnatch_vrfs = { git = "https://github.com/w3f/ring-vrf", default-features = false } +bandersnatch_vrfs = { git = "https://github.com/w3f/ring-vrf", default-features = false, optional = true } arrayvec = { version = "0.7.2", default-features = false } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive","max-encoded-len"] } scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } @@ -136,3 +136,8 @@ full_crypto = [ # This feature adds BLS crypto primitives. It should not be used in production since # the BLS implementation and interface may still be subject to significant change. bls-experimental = ["w3f-bls"] + +# This feature adds bandernatch-vrf crypto primitive. +# It should not be used in production since the implementation and interface +# may still be subject to significant change. +bandersnatch-experimental = ["bandersnatch_vrfs"] From dd9b7f89865cc1a329a9fbb899df81bc162e8881 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Thu, 18 May 2023 19:58:58 +0200 Subject: [PATCH 16/22] Vrf input and sign-data separation --- Cargo.lock | 8 +- Cargo.toml | 3 +- client/keystore/src/local.rs | 20 +- .../application-crypto/src/bandersnatch.rs | 29 ++ primitives/application-crypto/src/lib.rs | 1 - primitives/consensus/babe/src/lib.rs | 1 + primitives/core/Cargo.toml | 7 +- primitives/core/src/bandersnatch.rs | 356 +++++++++++------- primitives/core/src/lib.rs | 1 - primitives/io/src/lib.rs | 14 + primitives/keyring/src/bandersnatch.rs | 241 ++++++++++++ primitives/keyring/src/lib.rs | 4 + primitives/keystore/src/lib.rs | 20 +- primitives/keystore/src/testing.rs | 20 +- 14 files changed, 550 insertions(+), 175 deletions(-) create mode 100644 primitives/keyring/src/bandersnatch.rs diff --git a/Cargo.lock b/Cargo.lock index 45e09afa22218..6228ef0a6ddcb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -399,7 +399,7 @@ dependencies = [ [[package]] name = "ark-secret-scalar" version = "0.0.2" -source = "git+https://github.com/davxy/ring-vrf?branch=davxy-expose-some-stuff#c54be13d0b42cf239255482a78c19a703b903de9" +source = "git+https://github.com/w3f/ring-vrf#4957177a717c7555c8df2869012201017b62e66b" dependencies = [ "ark-ec", "ark-ff", @@ -447,7 +447,7 @@ dependencies = [ [[package]] name = "ark-transcript" version = "0.0.2" -source = "git+https://github.com/davxy/ring-vrf?branch=davxy-expose-some-stuff#c54be13d0b42cf239255482a78c19a703b903de9" +source = "git+https://github.com/w3f/ring-vrf#4957177a717c7555c8df2869012201017b62e66b" dependencies = [ "ark-ff", "ark-serialize", @@ -719,7 +719,7 @@ dependencies = [ [[package]] name = "bandersnatch_vrfs" version = "0.0.1" -source = "git+https://github.com/davxy/ring-vrf?branch=davxy-expose-some-stuff#c54be13d0b42cf239255482a78c19a703b903de9" +source = "git+https://github.com/w3f/ring-vrf#4957177a717c7555c8df2869012201017b62e66b" dependencies = [ "ark-bls12-381", "ark-ec", @@ -2115,7 +2115,7 @@ checksum = "210ec60ae7d710bed8683e333e9d2855a8a56a3e9892b38bad3bb0d4d29b0d5e" [[package]] name = "dleq_vrf" version = "0.0.2" -source = "git+https://github.com/davxy/ring-vrf?branch=davxy-expose-some-stuff#c54be13d0b42cf239255482a78c19a703b903de9" +source = "git+https://github.com/w3f/ring-vrf#4957177a717c7555c8df2869012201017b62e66b" dependencies = [ "ark-ec", "ark-ff", diff --git a/Cargo.toml b/Cargo.toml index fd26f903a448d..06740f2639d05 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -335,6 +335,5 @@ lto = "fat" # https://doc.rust-lang.org/rustc/codegen-options/index.html#codegen-units codegen-units = 1 -[patch."https://github.com/w3f/ring-vrf"] -bandersnatch_vrfs = { git = "https://github.com/davxy/ring-vrf", branch = "davxy-expose-some-stuff" } +#[patch."https://github.com/w3f/ring-vrf"] # bandersnatch_vrfs = { path = "/mnt/ssd/users/develop/w3f/ring-vrf/bandersnatch_vrfs" } diff --git a/client/keystore/src/local.rs b/client/keystore/src/local.rs index 6b01ce6b80330..0fdbb24bf70b7 100644 --- a/client/keystore/src/local.rs +++ b/client/keystore/src/local.rs @@ -19,14 +19,13 @@ use parking_lot::RwLock; use sp_application_crypto::{AppCrypto, AppPair, IsWrappedBy}; -#[cfg(feature = "bandersnatch-experimental")] -use sp_core::bandersnatch; -#[cfg(feature = "bls-experimental")] -use sp_core::{bls377, bls381}; use sp_core::{ + bandersnatch, crypto::{ByteArray, ExposeSecret, KeyTypeId, Pair as CorePair, SecretString, VrfSecret}, ecdsa, ed25519, sr25519, }; +#[cfg(feature = "bls-experimental")] +use sp_core::{bls377, bls381}; use sp_keystore::{Error as TraitError, Keystore, KeystorePtr}; use std::{ collections::HashMap, @@ -236,12 +235,10 @@ impl Keystore for LocalKeystore { Ok(sig) } - #[cfg(feature = "bandersnatch-experimental")] fn bandersnatch_public_keys(&self, key_type: KeyTypeId) -> Vec { self.public_keys::(key_type) } - #[cfg(feature = "bandersnatch-experimental")] fn bandersnatch_generate_new( &self, key_type: KeyTypeId, @@ -250,7 +247,6 @@ impl Keystore for LocalKeystore { self.generate_new::(key_type, seed) } - #[cfg(feature = "bandersnatch-experimental")] fn bandersnatch_sign( &self, key_type: KeyTypeId, @@ -263,7 +259,6 @@ impl Keystore for LocalKeystore { // TODO DAVXY // Maybe we can expose just this bandersnatch sign (the above one reduces to this with // input len = 0) - #[cfg(feature = "bandersnatch-experimental")] fn bandersnatch_vrf_sign( &self, key_type: KeyTypeId, @@ -273,6 +268,15 @@ impl Keystore for LocalKeystore { self.vrf_sign::(key_type, public, data) } + fn bandersnatch_vrf_output( + &self, + key_type: KeyTypeId, + public: &bandersnatch::Public, + input: &bandersnatch::vrf::VrfInput, + ) -> std::result::Result, TraitError> { + self.vrf_output::(key_type, public, input) + } + #[cfg(feature = "bls-experimental")] fn bls381_public_keys(&self, key_type: KeyTypeId) -> Vec { self.public_keys::(key_type) diff --git a/primitives/application-crypto/src/bandersnatch.rs b/primitives/application-crypto/src/bandersnatch.rs index 8e385e781bb92..68adee8061c00 100644 --- a/primitives/application-crypto/src/bandersnatch.rs +++ b/primitives/application-crypto/src/bandersnatch.rs @@ -17,7 +17,9 @@ //! Bandersnatch VRF application crypto types. +use crate::{KeyTypeId, RuntimePublic}; pub use sp_core::bandersnatch::*; +use sp_std::vec::Vec; mod app { crate::app_crypto!(super, sp_core::testing::BANDERSNATCH); @@ -26,3 +28,30 @@ mod app { #[cfg(feature = "full_crypto")] pub use app::Pair as AppPair; pub use app::{Public as AppPublic, Signature as AppSignature}; + +impl RuntimePublic for Public { + type Signature = Signature; + + fn all(_key_type: KeyTypeId) -> Vec { + // sp_io::crypto::bandersnatch_public_keys(key_type) + unimplemented!() + } + + fn generate_pair(key_type: KeyTypeId, seed: Option>) -> Self { + sp_io::crypto::bandersnatch_generate(key_type, seed) + } + + fn sign>(&self, _key_type: KeyTypeId, _msg: &M) -> Option { + // sp_io::crypto::bandersnatch_sign(key_type, self, msg.as_ref()) + unimplemented!() + } + + fn verify>(&self, _msg: &M, _signature: &Self::Signature) -> bool { + // sp_io::crypto::bandersnatch_verify(signature, msg.as_ref(), self) + unimplemented!() + } + + fn to_raw_vec(&self) -> Vec { + sp_core::crypto::ByteArray::to_raw_vec(self) + } +} diff --git a/primitives/application-crypto/src/lib.rs b/primitives/application-crypto/src/lib.rs index bb0df705bf108..2c4f4835bd20a 100644 --- a/primitives/application-crypto/src/lib.rs +++ b/primitives/application-crypto/src/lib.rs @@ -45,7 +45,6 @@ pub use traits::*; mod traits; -#[cfg(feature = "bandersnatch-experimental")] pub mod bandersnatch; #[cfg(feature = "bls-experimental")] pub mod bls377; diff --git a/primitives/consensus/babe/src/lib.rs b/primitives/consensus/babe/src/lib.rs index dc161525a8511..397eafc882241 100644 --- a/primitives/consensus/babe/src/lib.rs +++ b/primitives/consensus/babe/src/lib.rs @@ -324,6 +324,7 @@ where /// sure that all usages of `OpaqueKeyOwnershipProof` refer to the same type. #[derive(Decode, Encode, PartialEq, TypeInfo)] pub struct OpaqueKeyOwnershipProof(Vec); + impl OpaqueKeyOwnershipProof { /// Create a new `OpaqueKeyOwnershipProof` using the given encoded /// representation. diff --git a/primitives/core/Cargo.toml b/primitives/core/Cargo.toml index 23db60060090c..971834c2a80a1 100644 --- a/primitives/core/Cargo.toml +++ b/primitives/core/Cargo.toml @@ -13,7 +13,7 @@ documentation = "https://docs.rs/sp-core" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -bandersnatch_vrfs = { git = "https://github.com/w3f/ring-vrf", default-features = false, optional = true } +bandersnatch_vrfs = { git = "https://github.com/w3f/ring-vrf", default-features = false } arrayvec = { version = "0.7.2", default-features = false } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive","max-encoded-len"] } scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } @@ -136,8 +136,3 @@ full_crypto = [ # This feature adds BLS crypto primitives. It should not be used in production since # the BLS implementation and interface may still be subject to significant change. bls-experimental = ["w3f-bls"] - -# This feature adds bandernatch-vrf crypto primitive. -# It should not be used in production since the implementation and interface -# may still be subject to significant change. -bandersnatch-experimental = ["bandersnatch_vrfs"] diff --git a/primitives/core/src/bandersnatch.rs b/primitives/core/src/bandersnatch.rs index b5db9d4e3211b..f0af2ae705911 100644 --- a/primitives/core/src/bandersnatch.rs +++ b/primitives/core/src/bandersnatch.rs @@ -33,13 +33,14 @@ use codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; use sp_runtime_interface::pass_by::PassByInner; -use sp_std::{boxed::Box, vec::Vec}; +use sp_std::vec::Vec; /// Identifier used to match public keys against bandersnatch-vrf keys. pub const CRYPTO_ID: CryptoTypeId = CryptoTypeId(*b"bs38"); +#[cfg(feature = "full_crypto")] const SIGNING_CTX: &[u8] = b"SigningContext"; - +#[cfg(feature = "full_crypto")] const SEED_SERIALIZED_LEN: usize = 32; const PUBLIC_SERIALIZED_LEN: usize = 32; const SIGNATURE_SERIALIZED_LEN: usize = 64; @@ -67,6 +68,12 @@ impl UncheckedFrom<[u8; PUBLIC_SERIALIZED_LEN]> for Public { } } +impl AsRef<[u8; PUBLIC_SERIALIZED_LEN]> for Public { + fn as_ref(&self) -> &[u8; PUBLIC_SERIALIZED_LEN] { + &self.0 + } +} + impl AsRef<[u8]> for Public { fn as_ref(&self) -> &[u8] { &self.0[..] @@ -236,22 +243,20 @@ impl TraitPair for Pair { /// Sign raw data. fn sign(&self, data: &[u8]) -> Signature { - let input = vrf::VrfInput::new(SIGNING_CTX, &[data], &[]) - .expect("less than max input messages; qed"); - self.vrf_sign(&input.into()).signature + let data = vrf::VrfSignData::new(SIGNING_CTX, &[data], vrf::VrfIosVec::default()); + self.vrf_sign(&data).signature } /// Verify a signature on a message. /// /// Returns true if the signature is good. fn verify>(signature: &Self::Signature, data: M, public: &Self::Public) -> bool { - let input = vrf::VrfInput::new(SIGNING_CTX, &[data.as_ref()], &[]) - .expect("less than max input messages; qed"); + let data = vrf::VrfSignData::new(SIGNING_CTX, &[data.as_ref()], vrf::VrfIosVec::default()); let signature = vrf::VrfSignature { signature: signature.clone(), - output: vrf::VrfOutput { preouts: Box::new([]) }, + vrf_outputs: vrf::VrfIosVec::default(), }; - public.vrf_verify(&input.into(), &signature) + public.vrf_verify(&data, &signature) } /// Return a vec filled with seed raw data. @@ -271,82 +276,145 @@ impl CryptoType for Pair { /// VRF related types and operations. pub mod vrf { use super::*; - use crate::crypto::VrfCrypto; - #[cfg(feature = "full_crypto")] - use bandersnatch_vrfs::CanonicalSerialize; + use crate::{bounded::BoundedVec, crypto::VrfCrypto, ConstU32}; use bandersnatch_vrfs::{ - CanonicalDeserialize, IntoVrfInput, PublicKey, ThinVrfSignature, Transcript, + CanonicalDeserialize, CanonicalSerialize, IntoVrfInput, Message, PublicKey, + ThinVrfSignature, Transcript, }; - const MAX_VRF_INPUT_MESSAGES: usize = 3; + const PREOUT_SERIALIZED_LEN: usize = 32; + + /// Max number of VRF inputs/outputs + pub const MAX_VRF_IOS: u32 = 3; + + pub(super) type VrfIosVec = BoundedVec>; /// Input to be used for VRF sign and verify operations. #[derive(Clone)] - pub struct VrfInput { - /// Associated Fiat-Shamir transcript - pub(super) transcript: Transcript, - /// VRF input messages - pub(super) messages: Box<[bandersnatch_vrfs::VrfInput]>, - } + pub struct VrfInput(pub(super) bandersnatch_vrfs::VrfInput); impl VrfInput { - /// Build a new VRF input ready to be used by VRF sign and verify operations. + /// Build a new VRF input. /// - /// Returns `None` if `messages.len() > MAX_VRF_INPUT_MESSAGES`. - pub fn new( - label: &'static [u8], - transcript_data: &[&[u8]], - messages: &[(&[u8], &[u8])], - ) -> Option { - if messages.len() > MAX_VRF_INPUT_MESSAGES { - return None - } - let mut transcript = Transcript::new_labeled(label); - transcript_data.iter().for_each(|b| transcript.append_slice(b)); - let messages = messages - .into_iter() - .map(|(domain, message)| { - bandersnatch_vrfs::Message { domain, message }.into_vrf_input() - }) - .collect(); - Some(VrfInput { transcript, messages }) + /// Each message tuple has the form: (domain, data). + // TODO: Maybe we should access directly the transcript. + // I see a commented method in bandersnatch_vrfs crate that fullfil what we need... + pub fn new(label: &'static [u8], messages: &[(&[u8], &[u8])]) -> Self { + let _ = label; + let mut buf = Vec::new(); + messages.into_iter().for_each(|(domain, message)| { + buf.extend_from_slice(domain); + buf.extend_from_slice(message); + }); + let msg = Message { domain: b"TODO-DAVXY-FIXME", message: buf.as_slice() }; + VrfInput(msg.into_vrf_input()) } + } - /// Map to `VrfSignData` - pub fn into_sign_data(self) -> VrfSignData { - self.into() + /// TODO davxy docs + #[derive(Clone, Debug, PartialEq, Eq)] + pub struct VrfOutput(pub(super) bandersnatch_vrfs::VrfPreOut); + + impl Encode for VrfOutput { + fn encode(&self) -> Vec { + let mut bytes = [0; PREOUT_SERIALIZED_LEN]; + self.0 + .serialize_compressed(bytes.as_mut_slice()) + .expect("preout serialization can't fail"); + bytes.encode() } } - /// TODO davxy docs - pub struct VrfOutput { - /// VRF pre-outputs - pub(super) preouts: Box<[bandersnatch_vrfs::VrfPreOut]>, + impl Decode for VrfOutput { + fn decode(i: &mut R) -> Result { + let buf = <[u8; PREOUT_SERIALIZED_LEN]>::decode(i)?; + let preout = bandersnatch_vrfs::VrfPreOut::deserialize_compressed(buf.as_slice()) + .map_err(|_| "vrf-preout decode error: bad preout")?; + Ok(VrfOutput(preout)) + } + } + + impl MaxEncodedLen for VrfOutput { + fn max_encoded_len() -> usize { + <[u8; PREOUT_SERIALIZED_LEN]>::max_encoded_len() + } + } + + impl TypeInfo for VrfOutput { + type Identity = [u8; PREOUT_SERIALIZED_LEN]; + + fn type_info() -> scale_info::Type { + Self::Identity::type_info() + } } /// TODO davxy docs pub struct VrfSignData { - pub(super) input: VrfInput, + /// Associated Fiat-Shamir transcript + pub transcript: Transcript, + /// VRF inputs to be signed. + pub vrf_inputs: VrfIosVec, } - impl From for VrfSignData { - fn from(input: VrfInput) -> Self { - VrfSignData { input } + impl VrfSignData { + /// Construct a new data to be signed. + pub fn new>>( + label: &'static [u8], + transcript_data: &[&[u8]], + vrf_inputs: T, + ) -> Self { + let mut transcript = Transcript::new_labeled(label); + transcript_data.iter().for_each(|data| transcript.append_slice(data)); + VrfSignData { transcript, vrf_inputs: vrf_inputs.into() } + } + + /// Construct a new data to be signed from an iterator of `VrfInputs`. + /// + /// Returns `Err` if the `vrf_inputs` yields more elements than `MAX_VRF_IOS` + pub fn from_iter>( + label: &'static [u8], + transcript_data: &[&[u8]], + vrf_inputs: T, + ) -> Result { + let vrf_inputs: Vec = vrf_inputs.into_iter().collect(); + let bounded = VrfIosVec::try_from(vrf_inputs).map_err(|_| ())?; + Ok(Self::new(label, transcript_data, bounded)) + } + + /// Appends a message to the transcript + pub fn push_transcript_data( + &mut self, + data: &[u8], + ) { + self.transcript.append_slice(data); } - } - impl AsRef for VrfSignData { - fn as_ref(&self) -> &VrfInput { - &self.input + /// Appends a `VrfInput` to the vrf inputs to be signed. + /// On failure, returns the `VrfInput`. + pub fn push_vrf_input( + &mut self, + vrf_input: VrfInput, + ) -> Result<(), VrfInput> { + self.vrf_inputs.try_push(vrf_input) + } + + /// Create challenge from input transcript within the signing data. + pub fn challenge(&self) -> [u8; N] { + let mut output = [0; N]; + let mut t = self.transcript.clone(); + let mut reader = t.challenge(b"Prehashed for Ed25519"); + reader.read_bytes(&mut output); + output } } - /// TODO davxy: docs + /// VRF signature. + #[derive(Clone, Debug, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo)] pub struct VrfSignature { /// VRF signature - pub(super) signature: Signature, + pub signature: Signature, /// VRF pre-outputs - pub(super) output: VrfOutput, + pub vrf_outputs: VrfIosVec, } #[cfg(feature = "full_crypto")] @@ -362,25 +430,18 @@ pub mod vrf { fn vrf_sign(&self, data: &Self::VrfSignData) -> Self::VrfSignature { // Hack used because backend signature type is generic over the number of ios // @burdges can we provide a vec or boxed version? - let input = &data.input; - match input.messages.len() { - 0 => self.vrf_sign_gen::<0>(input), - 1 => self.vrf_sign_gen::<1>(input), - 2 => self.vrf_sign_gen::<2>(input), - 3 => self.vrf_sign_gen::<3>(input), - _ => panic!("Max VRF input messages is set to: {}", MAX_VRF_INPUT_MESSAGES), + match data.vrf_inputs.len() { + 0 => self.vrf_sign_gen::<0>(data), + 1 => self.vrf_sign_gen::<1>(data), + 2 => self.vrf_sign_gen::<2>(data), + 3 => self.vrf_sign_gen::<3>(data), + _ => panic!("Max VRF inputs is set to: {}", MAX_VRF_IOS), } } fn vrf_output(&self, input: &Self::VrfInput) -> Self::VrfOutput { - let ios: Vec<_> = - input.messages.iter().map(|i| self.0.clone().0.vrf_inout(i.clone())).collect(); - - let preout = bandersnatch_vrfs::vrf::collect_preoutputs_vec(&ios); - let preouts = preout.into_boxed_slice(); - - let output = VrfOutput { preouts }; - output + let output = self.0 .0.vrf_preout(&input.0); + VrfOutput(output) } } @@ -393,61 +454,63 @@ pub mod vrf { impl VrfPublic for Public { fn vrf_verify(&self, data: &Self::VrfSignData, signature: &Self::VrfSignature) -> bool { - let preouts_len = signature.output.preouts.len(); - if preouts_len != data.input.messages.len() { + let preouts_len = signature.vrf_outputs.len(); + if preouts_len != data.vrf_inputs.len() { return false } // Hack used because backend signature type is generic over the number of ios // @burdges can we provide a vec or boxed version? - let input = &data.input; match preouts_len { - 0 => self.vrf_verify_gen::<0>(input, signature), - 1 => self.vrf_verify_gen::<1>(input, signature), - 2 => self.vrf_verify_gen::<2>(input, signature), - 3 => self.vrf_verify_gen::<3>(input, signature), - _ => panic!("Max VRF input messages is set to: {}", MAX_VRF_INPUT_MESSAGES), + 0 => self.vrf_verify_gen::<0>(data, signature), + 1 => self.vrf_verify_gen::<1>(data, signature), + 2 => self.vrf_verify_gen::<2>(data, signature), + 3 => self.vrf_verify_gen::<3>(data, signature), + _ => panic!("Max VRF input messages is set to: {}", MAX_VRF_IOS), } } } #[cfg(feature = "full_crypto")] impl Pair { - fn vrf_sign_gen(&self, input: &VrfInput) -> VrfSignature { - let ios: Vec<_> = - input.messages.iter().map(|i| self.0.clone().0.vrf_inout(i.clone())).collect(); + fn vrf_sign_gen(&self, data: &VrfSignData) -> VrfSignature { + let ios: Vec<_> = data + .vrf_inputs + .iter() + .map(|i| self.0.clone().0.vrf_inout(i.0.clone())) + .collect(); let signature: ThinVrfSignature = - self.0.sign_thin_vrf(input.transcript.clone(), ios.as_slice()); + self.0.sign_thin_vrf(data.transcript.clone(), ios.as_slice()); let mut sign_bytes = [0; SIGNATURE_SERIALIZED_LEN]; - // TODO davxy: use ark-scale??? signature .signature .serialize_compressed(sign_bytes.as_mut_slice()) .expect("serialization can't fail"); - let output = VrfOutput { preouts: Box::new(signature.preoutputs) }; - VrfSignature { signature: Signature(sign_bytes), output } + + let outputs: Vec<_> = signature.preoutputs.into_iter().map(VrfOutput).collect(); + let outputs = VrfIosVec::truncate_from(outputs); + VrfSignature { signature: Signature(sign_bytes), vrf_outputs: outputs } } /// Generate output bytes from the given VRF input. /// /// Index is relative to one of the `VrfInput` messages used during construction. - pub fn output_bytes( + pub fn make_bytes( &self, + context: &'static [u8], input: &VrfInput, - index: usize, - ) -> Option<[u8; N]> { - let msg = input.messages.get(index)?.clone(); - let inout = self.0.clone().0.vrf_inout(msg); - let bytes = inout.vrf_output_bytes(input.transcript.clone()); - Some(bytes) + ) -> [u8; N] { + let transcript = Transcript::new_labeled(context); + let inout = self.0.clone().0.vrf_inout(input.0.clone()); + inout.vrf_output_bytes(transcript) } } impl Public { fn vrf_verify_gen( &self, - input: &VrfInput, + data: &VrfSignData, signature: &VrfSignature, ) -> bool { let Ok(public) = PublicKey::deserialize_compressed(self.as_ref()) else { @@ -455,10 +518,9 @@ pub mod vrf { }; let Ok(preouts) = signature - .output - .preouts + .vrf_outputs .iter() - .map(|o| o.clone()) + .map(|o| o.0.clone()) .collect::>() .into_inner() else { return false @@ -472,25 +534,23 @@ pub mod vrf { }; let signature = ThinVrfSignature { signature, preoutputs: preouts }; - let inputs = input.messages.clone().into_vec(); + let inputs = data.vrf_inputs.iter().map(|i| i.0.clone()); - signature.verify_thin_vrf(input.transcript.clone(), inputs, &public).is_ok() + signature.verify_thin_vrf(data.transcript.clone(), inputs, &public).is_ok() } } - impl VrfSignature { + impl VrfOutput { /// Generate output bytes for the given VRF input. - pub fn output_bytes( + pub fn make_bytes( &self, + context: &'static [u8], input: &VrfInput, - index: usize, - ) -> Option<[u8; N]> { - let inout = bandersnatch_vrfs::VrfInOut { - input: input.messages.get(index)?.clone().into_vrf_input(), - preoutput: self.output.preouts.get(index)?.clone(), - }; - let bytes = inout.vrf_output_bytes(input.transcript.clone()); - Some(bytes) + ) -> [u8; N] { + let transcript = Transcript::new_labeled(context); + let inout = + bandersnatch_vrfs::VrfInOut { input: input.0.clone(), preoutput: self.0.clone() }; + inout.vrf_output_bytes(transcript) } } } @@ -501,6 +561,7 @@ mod tests { use crate::crypto::{VrfPublic, VrfSecret, DEV_PHRASE}; const DEV_SEED: &[u8; SEED_SERIALIZED_LEN] = &[0; SEED_SERIALIZED_LEN]; + #[allow(unused)] fn b2h(bytes: &[u8]) -> String { array_bytes::bytes2hex("", bytes) } @@ -552,10 +613,12 @@ mod tests { fn vrf_sign_verify() { let pair = Pair::from_seed(DEV_SEED); let public = pair.public(); - // let input = VrfInput::new(b"test", &[b"foo", b"bar"], &[]); - let data = VrfInput::new(b"test", &[b"foo", b"bar"], &[(b"msg1", b"hello")]) - .unwrap() - .into(); + + let i1 = VrfInput::new(b"in1", &[(b"dom1", b"foo"), (b"dom2", b"bar")]); + let i2 = VrfInput::new(b"in2", &[(b"domx", b"hello")]); + let i3 = VrfInput::new(b"in3", &[(b"domy", b"yay"), (b"domz", b"nay")]); + + let data = VrfSignData::from_iter(b"mydata", &[b"tdata"], [i1, i2, i3]).unwrap(); let signature = pair.vrf_sign(&data); @@ -567,13 +630,18 @@ mod tests { let pair = Pair::from_seed(DEV_SEED); let public = pair.public(); - let data = VrfInput::new(b"test", &[b"foo", b"bar"], &[(b"msg1", b"hello")]) - .unwrap() - .into(); + let i1 = VrfInput::new(b"in1", &[(b"dom1", b"foo"), (b"dom2", b"bar")]); + let i2 = VrfInput::new(b"in2", &[(b"domx", b"hello")]); + + let data = + VrfSignData::from_iter(b"mydata", &[b"tdata"], [i1.clone(), i2.clone()]).unwrap(); let signature = pair.vrf_sign(&data); - let data = VrfInput::new(b"test", &[b"foo", b"bar"], &[]).unwrap().into(); + let data = VrfSignData::from_iter(b"mydata", &[b"data"], [i1, i2.clone()]).unwrap(); + assert!(!public.vrf_verify(&data, &signature)); + + let data = VrfSignData::from_iter(b"mydata", &[b"tdata"], [i2]).unwrap(); assert!(!public.vrf_verify(&data, &signature)); } @@ -581,28 +649,44 @@ mod tests { fn vrf_make_bytes_matches() { let pair = Pair::from_seed(DEV_SEED); - let input = VrfInput::new( - b"test", - &[b"proto", b"foo"], - &[(b"dom1", b"dat1"), (b"dom2", b"dat2"), (b"dom3", b"dat3")], - ) - .unwrap(); + let i1 = VrfInput::new(b"in1", &[(b"dom1", b"foo"), (b"dom2", b"bar")]); + let i2 = VrfInput::new(b"in2", &[(b"domx", b"hello")]); + let data = + VrfSignData::from_iter(b"mydata", &[b"tdata"], [i1.clone(), i2.clone()]).unwrap(); + let signature = pair.vrf_sign(&data); + + let o10 = pair.make_bytes::<32>(b"ctx1", &i1); + let o11 = signature.vrf_outputs[0].make_bytes::<32>(b"ctx1", &i1); + assert_eq!(o10, o11); + + let o20 = pair.make_bytes::<48>(b"ctx2", &i2); + let o21 = signature.vrf_outputs[1].make_bytes::<48>(b"ctx2", &i2); + assert_eq!(o20, o21); + } + + #[test] + fn encode_decode_vrf_signature() { + // Transcript data is hashed together and signed. + // It doesn't contribute to serialized length. + let pair = Pair::from_seed(DEV_SEED); + + let i1 = VrfInput::new(b"in1", &[(b"dom1", b"foo"), (b"dom2", b"bar")]); + let i2 = VrfInput::new(b"in2", &[(b"domx", b"hello")]); + let data = + VrfSignData::from_iter(b"mydata", &[b"tdata"], [i1.clone(), i2.clone()]).unwrap(); + let expected = pair.vrf_sign(&data); - let sign = pair.vrf_sign(&input.clone().into()); + let bytes = expected.encode(); - let out0 = pair.output_bytes::<32>(&input, 0).unwrap(); - let out1 = sign.output_bytes::<32>(&input, 0).unwrap(); - assert_eq!(out0, out1); + let decoded = VrfSignature::decode(&mut &bytes[..]).unwrap(); + assert_eq!(expected, decoded); - let out0 = pair.output_bytes::<48>(&input, 1).unwrap(); - let out1 = sign.output_bytes::<48>(&input, 1).unwrap(); - assert_eq!(out0, out1); + let data = VrfSignData::from_iter(b"mydata", &[b"tdata"], []).unwrap(); + let expected = pair.vrf_sign(&data); - let out0 = pair.output_bytes::<64>(&input, 2).unwrap(); - let out1 = sign.output_bytes::<64>(&input, 2).unwrap(); - assert_eq!(out0, out1); + let bytes = expected.encode(); - assert!(pair.output_bytes::<8>(&input, 3).is_none()); - assert!(sign.output_bytes::<8>(&input, 3).is_none()); + let decoded = VrfSignature::decode(&mut &bytes[..]).unwrap(); + assert_eq!(expected, decoded); } } diff --git a/primitives/core/src/lib.rs b/primitives/core/src/lib.rs index 9bdebf23bac8f..d00bc4dd70d5f 100644 --- a/primitives/core/src/lib.rs +++ b/primitives/core/src/lib.rs @@ -55,7 +55,6 @@ pub mod crypto; pub mod hexdisplay; pub use paste; -#[cfg(feature = "bandersnatch-experimental")] pub mod bandersnatch; #[cfg(feature = "bls-experimental")] pub mod bls; diff --git a/primitives/io/src/lib.rs b/primitives/io/src/lib.rs index 750b5d5924637..050effbb1a003 100644 --- a/primitives/io/src/lib.rs +++ b/primitives/io/src/lib.rs @@ -45,6 +45,7 @@ use sp_core::{ use sp_keystore::KeystoreExt; use sp_core::{ + bandersnatch, crypto::KeyTypeId, ecdsa, ed25519, offchain::{ @@ -1140,6 +1141,19 @@ pub trait Crypto { .map_err(|_| EcdsaVerifyError::BadSignature)?; Ok(pubkey.serialize()) } + + /// DAVXY + fn bandersnatch_generate( + &mut self, + id: KeyTypeId, + seed: Option>, + ) -> bandersnatch::Public { + let seed = seed.as_ref().map(|s| std::str::from_utf8(s).expect("Seed is valid utf8!")); + self.extension::() + .expect("No `keystore` associated for the current context!") + .bandersnatch_generate_new(id, seed) + .expect("`bandernatch_generate` failed") + } } /// Interface that provides functions for hashing with different algorithms. diff --git a/primitives/keyring/src/bandersnatch.rs b/primitives/keyring/src/bandersnatch.rs new file mode 100644 index 0000000000000..a61e9dafef877 --- /dev/null +++ b/primitives/keyring/src/bandersnatch.rs @@ -0,0 +1,241 @@ +// 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. + +//! Support code for the runtime. A set of test accounts. + +pub use sp_core::bandersnatch; +use sp_core::{ + bandersnatch::{Pair, Public, Signature}, + crypto::UncheckedFrom, + ByteArray, Pair as PairT, H256, +}; +use sp_runtime::AccountId32; + +use lazy_static::lazy_static; +use std::{collections::HashMap, ops::Deref, sync::Mutex}; + +/// Set of test accounts. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, strum::Display, strum::EnumIter)] +pub enum Keyring { + Alice, + Bob, + Charlie, + Dave, + Eve, + Ferdie, + One, + Two, +} + +impl Keyring { + pub fn from_public(who: &Public) -> Option { + Self::iter().find(|&k| &Public::from(k) == who) + } + + pub fn from_account_id(who: &AccountId32) -> Option { + Self::iter().find(|&k| &k.to_account_id() == who) + } + + pub fn from_raw_public(who: [u8; 32]) -> Option { + Self::from_public(&Public::unchecked_from(who)) + } + + pub fn to_raw_public(self) -> [u8; 32] { + *Public::from(self).as_ref() + } + + pub fn from_h256_public(who: H256) -> Option { + Self::from_public(&Public::unchecked_from(who.into())) + } + + pub fn to_h256_public(self) -> H256 { + AsRef::<[u8; 32]>::as_ref(&Public::from(self)).into() + } + + pub fn to_raw_public_vec(self) -> Vec { + Public::from(self).to_raw_vec() + } + + pub fn to_account_id(self) -> AccountId32 { + self.to_raw_public().into() + } + + pub fn sign(self, msg: &[u8]) -> Signature { + Pair::from(self).sign(msg) + } + + pub fn pair(self) -> Pair { + Pair::from_string(&format!("//{}", <&'static str>::from(self)), None) + .expect("static values are known good; qed") + } + + /// Returns an iterator over all test accounts. + pub fn iter() -> impl Iterator { + ::iter() + } + + pub fn public(self) -> Public { + self.pair().public() + } + + pub fn to_seed(self) -> String { + format!("//{}", self) + } + + /// Create a crypto `Pair` from a numeric value. + pub fn numeric(idx: usize) -> Pair { + Pair::from_string(&format!("//{}", idx), None).expect("numeric values are known good; qed") + } + + /// Get account id of a `numeric` account. + pub fn numeric_id(idx: usize) -> AccountId32 { + (*AsRef::<[u8; 32]>::as_ref(&Self::numeric(idx).public())).into() + } +} + +impl From for &'static str { + fn from(k: Keyring) -> Self { + match k { + Keyring::Alice => "Alice", + Keyring::Bob => "Bob", + Keyring::Charlie => "Charlie", + Keyring::Dave => "Dave", + Keyring::Eve => "Eve", + Keyring::Ferdie => "Ferdie", + Keyring::One => "One", + Keyring::Two => "Two", + } + } +} + +#[derive(Debug)] +pub struct ParseKeyringError; + +impl std::fmt::Display for ParseKeyringError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "ParseKeyringError") + } +} + +impl std::str::FromStr for Keyring { + type Err = ParseKeyringError; + + fn from_str(s: &str) -> Result::Err> { + match s { + "Alice" => Ok(Keyring::Alice), + "Bob" => Ok(Keyring::Bob), + "Charlie" => Ok(Keyring::Charlie), + "Dave" => Ok(Keyring::Dave), + "Eve" => Ok(Keyring::Eve), + "Ferdie" => Ok(Keyring::Ferdie), + "One" => Ok(Keyring::One), + "Two" => Ok(Keyring::Two), + _ => Err(ParseKeyringError), + } + } +} + +lazy_static! { + static ref PRIVATE_KEYS: Mutex> = + Mutex::new(Keyring::iter().map(|who| (who, who.pair())).collect()); + static ref PUBLIC_KEYS: HashMap = PRIVATE_KEYS + .lock() + .unwrap() + .iter() + .map(|(&who, pair)| (who, pair.public())) + .collect(); +} + +impl From for AccountId32 { + fn from(k: Keyring) -> Self { + k.to_account_id() + } +} + +impl From for Public { + fn from(k: Keyring) -> Self { + *(*PUBLIC_KEYS).get(&k).unwrap() + } +} + +impl From for Pair { + fn from(k: Keyring) -> Self { + k.pair() + } +} + +impl From for [u8; 32] { + fn from(k: Keyring) -> Self { + *(*PUBLIC_KEYS).get(&k).unwrap().as_ref() + } +} + +impl From for H256 { + fn from(k: Keyring) -> Self { + AsRef::<[u8; 32]>::as_ref(PUBLIC_KEYS.get(&k).unwrap()).into() + } +} + +impl From for &'static [u8; 32] { + fn from(k: Keyring) -> Self { + PUBLIC_KEYS.get(&k).unwrap().as_ref() + } +} + +impl AsRef<[u8; 32]> for Keyring { + fn as_ref(&self) -> &[u8; 32] { + PUBLIC_KEYS.get(self).unwrap().as_ref() + } +} + +impl AsRef for Keyring { + fn as_ref(&self) -> &Public { + PUBLIC_KEYS.get(self).unwrap() + } +} + +impl Deref for Keyring { + type Target = [u8; 32]; + fn deref(&self) -> &[u8; 32] { + PUBLIC_KEYS.get(self).unwrap().as_ref() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use sp_core::{bandersnatch::Pair, Pair as PairT}; + + #[test] + fn should_work() { + assert!(Pair::verify( + &Keyring::Alice.sign(b"I am Alice!"), + b"I am Alice!", + &Keyring::Alice.public(), + )); + assert!(!Pair::verify( + &Keyring::Alice.sign(b"I am Alice!"), + b"I am Bob!", + &Keyring::Alice.public(), + )); + assert!(!Pair::verify( + &Keyring::Alice.sign(b"I am Alice!"), + b"I am Alice!", + &Keyring::Bob.public(), + )); + } +} diff --git a/primitives/keyring/src/lib.rs b/primitives/keyring/src/lib.rs index 7432aff12544a..fba15a121e818 100644 --- a/primitives/keyring/src/lib.rs +++ b/primitives/keyring/src/lib.rs @@ -23,11 +23,15 @@ pub mod sr25519; /// Test account crypto for ed25519. pub mod ed25519; +/// Test account crypto for bandersnatch. +pub mod bandersnatch; + /// Convenience export: Sr25519's Keyring is exposed as `AccountKeyring`, /// since it tends to be used for accounts (although it may also be used /// by authorities). pub use sr25519::Keyring as AccountKeyring; +pub use bandersnatch::Keyring as BandersnatchKeyring; pub use ed25519::Keyring as Ed25519Keyring; pub use sr25519::Keyring as Sr25519Keyring; diff --git a/primitives/keystore/src/lib.rs b/primitives/keystore/src/lib.rs index 71bf2e192f510..d67f2cb2d5c69 100644 --- a/primitives/keystore/src/lib.rs +++ b/primitives/keystore/src/lib.rs @@ -19,14 +19,13 @@ pub mod testing; -#[cfg(feature = "bandersnatch-experimental")] -use sp_core::bandersnatch; -#[cfg(feature = "bls-experimental")] -use sp_core::{bls377, bls381}; use sp_core::{ + bandersnatch, crypto::{ByteArray, CryptoTypeId, KeyTypeId}, ecdsa, ed25519, sr25519, }; +#[cfg(feature = "bls-experimental")] +use sp_core::{bls377, bls381}; use std::sync::Arc; @@ -176,11 +175,9 @@ pub trait Keystore: Send + Sync { msg: &[u8; 32], ) -> Result, Error>; - #[cfg(feature = "bandersnatch-experimental")] /// DAVXY TODO fn bandersnatch_public_keys(&self, key_type: KeyTypeId) -> Vec; - #[cfg(feature = "bandersnatch-experimental")] /// DAVXY TODO fn bandersnatch_generate_new( &self, @@ -188,7 +185,6 @@ pub trait Keystore: Send + Sync { seed: Option<&str>, ) -> Result; - #[cfg(feature = "bandersnatch-experimental")] /// DAVXY TODO fn bandersnatch_sign( &self, @@ -197,7 +193,6 @@ pub trait Keystore: Send + Sync { msg: &[u8], ) -> Result, Error>; - #[cfg(feature = "bandersnatch-experimental")] /// DAVXY TODO fn bandersnatch_vrf_sign( &self, @@ -206,6 +201,14 @@ pub trait Keystore: Send + Sync { input: &bandersnatch::vrf::VrfSignData, ) -> Result, Error>; + /// DAVXY TODO + fn bandersnatch_vrf_output( + &self, + key_type: KeyTypeId, + public: &bandersnatch::Public, + input: &bandersnatch::vrf::VrfInput, + ) -> Result, Error>; + #[cfg(feature = "bls-experimental")] /// Returns all bls12-381 public keys for the given key type. fn bls381_public_keys(&self, id: KeyTypeId) -> Vec; @@ -324,7 +327,6 @@ pub trait Keystore: Send + Sync { self.ecdsa_sign(id, &public, msg)?.map(|s| s.encode()) }, - #[cfg(feature = "bandersnatch-experimental")] bandersnatch::CRYPTO_ID => { let public = bandersnatch::Public::from_slice(public) .map_err(|_| Error::ValidationError("Invalid public key format".into()))?; diff --git a/primitives/keystore/src/testing.rs b/primitives/keystore/src/testing.rs index ccdb1672eed44..b9c685397fb6f 100644 --- a/primitives/keystore/src/testing.rs +++ b/primitives/keystore/src/testing.rs @@ -19,14 +19,13 @@ use crate::{Error, Keystore, KeystorePtr}; -#[cfg(feature = "bandersnatch-experimental")] -use sp_core::bandersnatch; -#[cfg(feature = "bls-experimental")] -use sp_core::{bls377, bls381}; use sp_core::{ + bandersnatch, crypto::{ByteArray, KeyTypeId, Pair, VrfSecret}, ecdsa, ed25519, sr25519, }; +#[cfg(feature = "bls-experimental")] +use sp_core::{bls377, bls381}; use parking_lot::RwLock; use std::{collections::HashMap, sync::Arc}; @@ -216,12 +215,10 @@ impl Keystore for MemoryKeystore { Ok(sig) } - #[cfg(feature = "bandersnatch-experimental")] fn bandersnatch_public_keys(&self, key_type: KeyTypeId) -> Vec { self.public_keys::(key_type) } - #[cfg(feature = "bandersnatch-experimental")] fn bandersnatch_generate_new( &self, key_type: KeyTypeId, @@ -230,7 +227,6 @@ impl Keystore for MemoryKeystore { self.generate_new::(key_type, seed) } - #[cfg(feature = "bandersnatch-experimental")] fn bandersnatch_sign( &self, key_type: KeyTypeId, @@ -240,7 +236,6 @@ impl Keystore for MemoryKeystore { self.sign::(key_type, public, msg) } - #[cfg(feature = "bandersnatch-experimental")] fn bandersnatch_vrf_sign( &self, key_type: KeyTypeId, @@ -250,6 +245,15 @@ impl Keystore for MemoryKeystore { self.vrf_sign::(key_type, public, data) } + fn bandersnatch_vrf_output( + &self, + key_type: KeyTypeId, + public: &bandersnatch::Public, + input: &bandersnatch::vrf::VrfInput, + ) -> Result, Error> { + self.vrf_output::(key_type, public, input) + } + #[cfg(feature = "bls-experimental")] fn bls381_public_keys(&self, key_type: KeyTypeId) -> Vec { self.public_keys::(key_type) From a6b09d9e350f6deeacaf573d6b7677540f38495a Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Thu, 18 May 2023 20:04:17 +0200 Subject: [PATCH 17/22] rust fmt --- primitives/core/src/bandersnatch.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/primitives/core/src/bandersnatch.rs b/primitives/core/src/bandersnatch.rs index f0af2ae705911..32a15ffc2f760 100644 --- a/primitives/core/src/bandersnatch.rs +++ b/primitives/core/src/bandersnatch.rs @@ -382,19 +382,13 @@ pub mod vrf { } /// Appends a message to the transcript - pub fn push_transcript_data( - &mut self, - data: &[u8], - ) { + pub fn push_transcript_data(&mut self, data: &[u8]) { self.transcript.append_slice(data); } /// Appends a `VrfInput` to the vrf inputs to be signed. /// On failure, returns the `VrfInput`. - pub fn push_vrf_input( - &mut self, - vrf_input: VrfInput, - ) -> Result<(), VrfInput> { + pub fn push_vrf_input(&mut self, vrf_input: VrfInput) -> Result<(), VrfInput> { self.vrf_inputs.try_push(vrf_input) } From d69cef7eec067eb92e7c83935ed042d7d4a22256 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Thu, 18 May 2023 20:20:57 +0200 Subject: [PATCH 18/22] another clippy warning... --- primitives/core/src/bandersnatch.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/primitives/core/src/bandersnatch.rs b/primitives/core/src/bandersnatch.rs index 32a15ffc2f760..1f8d0bb7682c3 100644 --- a/primitives/core/src/bandersnatch.rs +++ b/primitives/core/src/bandersnatch.rs @@ -252,10 +252,8 @@ impl TraitPair for Pair { /// Returns true if the signature is good. fn verify>(signature: &Self::Signature, data: M, public: &Self::Public) -> bool { let data = vrf::VrfSignData::new(SIGNING_CTX, &[data.as_ref()], vrf::VrfIosVec::default()); - let signature = vrf::VrfSignature { - signature: signature.clone(), - vrf_outputs: vrf::VrfIosVec::default(), - }; + let signature = + vrf::VrfSignature { signature: *signature, vrf_outputs: vrf::VrfIosVec::default() }; public.vrf_verify(&data, &signature) } From 93cb275389b8b3d7f1e463f95bb7df4cd6ed4829 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Mon, 22 May 2023 14:36:28 +0200 Subject: [PATCH 19/22] Bandersnatch VRFs behind "bsnvrf-experimental" flag --- client/keystore/Cargo.toml | 17 +++- client/keystore/src/local.rs | 66 ++++++++------- primitives/core/Cargo.toml | 14 +++- .../core/src/{bandersnatch.rs => bsnvrf.rs} | 0 primitives/core/src/lib.rs | 3 +- primitives/io/Cargo.toml | 8 ++ primitives/io/src/lib.rs | 23 ++++-- primitives/keystore/Cargo.toml | 10 ++- primitives/keystore/src/lib.rs | 82 +++++++++++++------ primitives/keystore/src/testing.rs | 50 ++++++----- 10 files changed, 177 insertions(+), 96 deletions(-) rename primitives/core/src/{bandersnatch.rs => bsnvrf.rs} (100%) diff --git a/client/keystore/Cargo.toml b/client/keystore/Cargo.toml index 81e047e90ab31..be62b948b703d 100644 --- a/client/keystore/Cargo.toml +++ b/client/keystore/Cargo.toml @@ -26,9 +26,18 @@ sp-keystore = { version = "0.13.0", path = "../../primitives/keystore" } tempfile = "3.1.0" [features] -# This feature adds BLS crypto primitives. It should not be used in production since -# the BLS implementation and interface may still be subject to significant change. +# This feature adds BLS crypto primitives. +# It should not be used in production since the implementation +# and interface may still be subject to significant change. bls-experimental = [ - "sp-core/bls-experimental", - "sp-keystore/bls-experimental", + "sp-core/bls-experimental", + "sp-keystore/bls-experimental", +] + +# This feature adds Bandersnatch-VRFs crypto primitives. +# It should not be used in production since the implementation +# and interface may still be subject to significant change. +bsnvrf-experimental = [ + "sp-core/bsnvrf-experimental", + "sp-keystore/bsnvrf-experimental", ] diff --git a/client/keystore/src/local.rs b/client/keystore/src/local.rs index 0fdbb24bf70b7..3eadb31845984 100644 --- a/client/keystore/src/local.rs +++ b/client/keystore/src/local.rs @@ -19,13 +19,14 @@ use parking_lot::RwLock; use sp_application_crypto::{AppCrypto, AppPair, IsWrappedBy}; +#[cfg(feature = "bsnvrf-experimental")] +use sp_core::bsnvrf; +#[cfg(feature = "bls-experimental")] +use sp_core::{bls377, bls381}; use sp_core::{ - bandersnatch, crypto::{ByteArray, ExposeSecret, KeyTypeId, Pair as CorePair, SecretString, VrfSecret}, ecdsa, ed25519, sr25519, }; -#[cfg(feature = "bls-experimental")] -use sp_core::{bls377, bls381}; use sp_keystore::{Error as TraitError, Keystore, KeystorePtr}; use std::{ collections::HashMap, @@ -135,7 +136,7 @@ impl Keystore for LocalKeystore { self.public_keys::(key_type) } - /// Generate a new pair compatible with the 'ed25519' signature scheme. + /// Generate a new key pair. /// /// If `[seed]` is `Some` then the key will be ephemeral and stored in memory. fn sr25519_generate_new( @@ -177,7 +178,7 @@ impl Keystore for LocalKeystore { self.public_keys::(key_type) } - /// Generate a new pair compatible with the 'sr25519' signature scheme. + /// Generate a new key pair. /// /// If `[seed]` is `Some` then the key will be ephemeral and stored in memory. fn ed25519_generate_new( @@ -201,7 +202,7 @@ impl Keystore for LocalKeystore { self.public_keys::(key_type) } - /// Generate a new pair compatible with the 'ecdsa' signature scheme. + /// Generate a new key pair. /// /// If `[seed]` is `Some` then the key will be ephemeral and stored in memory. fn ecdsa_generate_new( @@ -235,46 +236,51 @@ impl Keystore for LocalKeystore { Ok(sig) } - fn bandersnatch_public_keys(&self, key_type: KeyTypeId) -> Vec { - self.public_keys::(key_type) + #[cfg(feature = "bsnvrf-experimental")] + fn bsnvrf_public_keys(&self, key_type: KeyTypeId) -> Vec { + self.public_keys::(key_type) } - fn bandersnatch_generate_new( + #[cfg(feature = "bsnvrf-experimental")] + /// Generate a new key pair. + /// + /// If `[seed]` is `Some` then the key will be ephemeral and stored in memory. + fn bsnvrf_generate_new( &self, key_type: KeyTypeId, seed: Option<&str>, - ) -> std::result::Result { - self.generate_new::(key_type, seed) + ) -> std::result::Result { + self.generate_new::(key_type, seed) } - fn bandersnatch_sign( + #[cfg(feature = "bsnvrf-experimental")] + fn bsnvrf_sign( &self, key_type: KeyTypeId, - public: &bandersnatch::Public, + public: &bsnvrf::Public, msg: &[u8], - ) -> std::result::Result, TraitError> { - self.sign::(key_type, public, msg) + ) -> std::result::Result, TraitError> { + self.sign::(key_type, public, msg) } - // TODO DAVXY - // Maybe we can expose just this bandersnatch sign (the above one reduces to this with - // input len = 0) - fn bandersnatch_vrf_sign( + #[cfg(feature = "bsnvrf-experimental")] + fn bsnvrf_vrf_sign( &self, key_type: KeyTypeId, - public: &bandersnatch::Public, - data: &bandersnatch::vrf::VrfSignData, - ) -> std::result::Result, TraitError> { - self.vrf_sign::(key_type, public, data) + public: &bsnvrf::Public, + data: &bsnvrf::vrf::VrfSignData, + ) -> std::result::Result, TraitError> { + self.vrf_sign::(key_type, public, data) } - fn bandersnatch_vrf_output( + #[cfg(feature = "bsnvrf-experimental")] + fn bsnvrf_vrf_output( &self, key_type: KeyTypeId, - public: &bandersnatch::Public, - input: &bandersnatch::vrf::VrfInput, - ) -> std::result::Result, TraitError> { - self.vrf_output::(key_type, public, input) + public: &bsnvrf::Public, + input: &bsnvrf::vrf::VrfInput, + ) -> std::result::Result, TraitError> { + self.vrf_output::(key_type, public, input) } #[cfg(feature = "bls-experimental")] @@ -283,7 +289,7 @@ impl Keystore for LocalKeystore { } #[cfg(feature = "bls-experimental")] - /// Generate a new pair compatible with the 'bls381' signature scheme. + /// Generate a new key pair. /// /// If `[seed]` is `Some` then the key will be ephemeral and stored in memory. fn bls381_generate_new( @@ -310,7 +316,7 @@ impl Keystore for LocalKeystore { } #[cfg(feature = "bls-experimental")] - /// Generate a new pair compatible with the 'bls377' signature scheme. + /// Generate a new key-pair. /// /// If `[seed]` is `Some` then the key will be ephemeral and stored in memory. fn bls377_generate_new( diff --git a/primitives/core/Cargo.toml b/primitives/core/Cargo.toml index 971834c2a80a1..e39f9377b69ad 100644 --- a/primitives/core/Cargo.toml +++ b/primitives/core/Cargo.toml @@ -13,7 +13,6 @@ documentation = "https://docs.rs/sp-core" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -bandersnatch_vrfs = { git = "https://github.com/w3f/ring-vrf", default-features = false } arrayvec = { version = "0.7.2", default-features = false } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive","max-encoded-len"] } scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } @@ -57,6 +56,9 @@ sp-runtime-interface = { version = "7.0.0", default-features = false, path = ".. # bls crypto w3f-bls = { version = "0.1.3", default-features = false, optional = true} +# bandersnatch vrfs +bandersnatch_vrfs = { git = "https://github.com/w3f/ring-vrf", default-features = false, optional = true } + [dev-dependencies] sp-serializer = { version = "4.0.0-dev", path = "../serializer" } rand = "0.8.5" @@ -133,6 +135,12 @@ full_crypto = [ "sp-runtime-interface/disable_target_static_assertions", ] -# This feature adds BLS crypto primitives. It should not be used in production since -# the BLS implementation and interface may still be subject to significant change. +# This feature adds BLS crypto primitives. +# It should not be used in production since the implementation +# and interface may still be subject to significant change. bls-experimental = ["w3f-bls"] + +# This feature adds Bandersnatch-VRFs crypto primitives. +# It should not be used in production since the implementation +# and interface may still be subject to significant change. +bsnvrf-experimental = ["bandersnatch_vrfs"] diff --git a/primitives/core/src/bandersnatch.rs b/primitives/core/src/bsnvrf.rs similarity index 100% rename from primitives/core/src/bandersnatch.rs rename to primitives/core/src/bsnvrf.rs diff --git a/primitives/core/src/lib.rs b/primitives/core/src/lib.rs index d00bc4dd70d5f..2687058283249 100644 --- a/primitives/core/src/lib.rs +++ b/primitives/core/src/lib.rs @@ -55,9 +55,10 @@ pub mod crypto; pub mod hexdisplay; pub use paste; -pub mod bandersnatch; #[cfg(feature = "bls-experimental")] pub mod bls; +#[cfg(feature = "bsnvrf-experimental")] +pub mod bsnvrf; pub mod defer; pub mod ecdsa; pub mod ed25519; diff --git a/primitives/io/Cargo.toml b/primitives/io/Cargo.toml index c6e716396aea4..05e3faa1a87ca 100644 --- a/primitives/io/Cargo.toml +++ b/primitives/io/Cargo.toml @@ -94,3 +94,11 @@ disable_allocator = [] # host function to be supported by the host. Do *not* enable it for your # runtime without first upgrading your host client! improved_panic_error_reporting = [] + +# This feature adds Bandersnatch-VRF crypto primitives. +# It should not be used in production since the implementation +# and interface may still be subject to significant change. +bsnvrf-experimental = [ + "sp-core/bsnvrf-experimental", + "sp-keystore/bsnvrf-experimental", +] diff --git a/primitives/io/src/lib.rs b/primitives/io/src/lib.rs index 050effbb1a003..0185ae37fbb08 100644 --- a/primitives/io/src/lib.rs +++ b/primitives/io/src/lib.rs @@ -44,8 +44,9 @@ use sp_core::{ #[cfg(feature = "std")] use sp_keystore::KeystoreExt; +#[cfg(feature = "bsnvrf-experimental")] +use sp_core::bsnvrf; use sp_core::{ - bandersnatch, crypto::KeyTypeId, ecdsa, ed25519, offchain::{ @@ -1141,17 +1142,23 @@ pub trait Crypto { .map_err(|_| EcdsaVerifyError::BadSignature)?; Ok(pubkey.serialize()) } +} - /// DAVXY - fn bandersnatch_generate( - &mut self, - id: KeyTypeId, - seed: Option>, - ) -> bandersnatch::Public { +/// Temporary experimental interface that provides required Bandersnatch-VRFs functions. +#[cfg(feature = "bsnvrf-experimental")] +#[runtime_interface] +pub trait BsnVrfExperimental { + /// Generate a `bsnvrf` key for the given key type using an optional `seed` and + /// store it in the keystore. + /// + /// The `seed` needs to be a valid utf8. + /// + /// Returns the public key. + fn bsnvrf_generate(&mut self, id: KeyTypeId, seed: Option>) -> bsnvrf::Public { let seed = seed.as_ref().map(|s| std::str::from_utf8(s).expect("Seed is valid utf8!")); self.extension::() .expect("No `keystore` associated for the current context!") - .bandersnatch_generate_new(id, seed) + .bsnvrf_generate_new(id, seed) .expect("`bandernatch_generate` failed") } } diff --git a/primitives/keystore/Cargo.toml b/primitives/keystore/Cargo.toml index a749b95a483b1..a6e44ef3b17cd 100644 --- a/primitives/keystore/Cargo.toml +++ b/primitives/keystore/Cargo.toml @@ -34,6 +34,12 @@ std = [ "sp-externalities/std", ] -# This feature adds BLS crypto primitives. It should not be used in production since -# the BLS implementation and interface may still be subject to significant change. +# This feature adds BLS crypto primitives. +# It should not be used in production since the implementation +# and interface may still be subject to significant change. bls-experimental = ["sp-core/bls-experimental"] + +# This feature adds Bandersnatch-VRF crypto primitives. +# It should not be used in production since the implementation +# and interface may still be subject to significant change. +bsnvrf-experimental = ["sp-core/bsnvrf-experimental"] diff --git a/primitives/keystore/src/lib.rs b/primitives/keystore/src/lib.rs index d67f2cb2d5c69..7e6a5538fdf2d 100644 --- a/primitives/keystore/src/lib.rs +++ b/primitives/keystore/src/lib.rs @@ -19,13 +19,14 @@ pub mod testing; +#[cfg(feature = "bsnvrf-experimental")] +use sp_core::bsnvrf; +#[cfg(feature = "bls-experimental")] +use sp_core::{bls377, bls381}; use sp_core::{ - bandersnatch, crypto::{ByteArray, CryptoTypeId, KeyTypeId}, ecdsa, ed25519, sr25519, }; -#[cfg(feature = "bls-experimental")] -use sp_core::{bls377, bls381}; use std::sync::Arc; @@ -175,39 +176,67 @@ pub trait Keystore: Send + Sync { msg: &[u8; 32], ) -> Result, Error>; - /// DAVXY TODO - fn bandersnatch_public_keys(&self, key_type: KeyTypeId) -> Vec; + #[cfg(feature = "bsnvrf-experimental")] + /// Returns all bsnvrf public keys for the given key type. + fn bsnvrf_public_keys(&self, key_type: KeyTypeId) -> Vec; - /// DAVXY TODO - fn bandersnatch_generate_new( + #[cfg(feature = "bsnvrf-experimental")] + /// Generate a new bsnvrf key pair for the given key type and an optional seed. + /// + /// Returns an `ed25519::Public` key of the generated key pair or an `Err` if + /// something failed during key generation. + fn bsnvrf_generate_new( &self, key_type: KeyTypeId, seed: Option<&str>, - ) -> Result; + ) -> Result; - /// DAVXY TODO - fn bandersnatch_sign( + #[cfg(feature = "bsnvrf-experimental")] + /// Generate an bsnvrf signature for the given data. + /// + /// Receives [`KeyTypeId`] and an [`bsnvrf::Public`] key to be able to map + /// them to a private key that exists in the keystore. + /// + /// Returns `None` if the given `key_type` and `public` combination doesn't + /// exist in the keystore or an `Err` when something failed. + // TODO DAVXY: maybe we can remove this and just pass through the `vrf_sign` + // as this reduces to the other with a sign-data with 0 vrf-inputs. + fn bsnvrf_sign( &self, key_type: KeyTypeId, - public: &bandersnatch::Public, + public: &bsnvrf::Public, msg: &[u8], - ) -> Result, Error>; + ) -> Result, Error>; - /// DAVXY TODO - fn bandersnatch_vrf_sign( + #[cfg(feature = "bsnvrf-experimental")] + /// Generate a bsnvrf VRF signature for the given data. + /// + /// Receives [`KeyTypeId`] and an [`bsnvrf::Public`] key to be able to map + /// them to a private key that exists in the keystore. + /// + /// Returns `None` if the given `key_type` and `public` combination doesn't + /// exist in the keystore or an `Err` when something failed. + fn bsnvrf_vrf_sign( &self, key_type: KeyTypeId, - public: &bandersnatch::Public, - input: &bandersnatch::vrf::VrfSignData, - ) -> Result, Error>; + public: &bsnvrf::Public, + input: &bsnvrf::vrf::VrfSignData, + ) -> Result, Error>; - /// DAVXY TODO - fn bandersnatch_vrf_output( + #[cfg(feature = "bsnvrf-experimental")] + /// Generate a bsnvrf VRF output for a given input data. + /// + /// Receives [`KeyTypeId`] and an [`bsnvrf::Public`] key to be able to map + /// them to a private key that exists in the keystore. + /// + /// Returns `None` if the given `key_type` and `public` combination doesn't + /// exist in the keystore or an `Err` when something failed. + fn bsnvrf_vrf_output( &self, key_type: KeyTypeId, - public: &bandersnatch::Public, - input: &bandersnatch::vrf::VrfInput, - ) -> Result, Error>; + public: &bsnvrf::Public, + input: &bsnvrf::vrf::VrfInput, + ) -> Result, Error>; #[cfg(feature = "bls-experimental")] /// Returns all bls12-381 public keys for the given key type. @@ -293,7 +322,7 @@ pub trait Keystore: Send + Sync { /// - sr25519 /// - ed25519 /// - ecdsa - /// - bandersnatch + /// - bsnvrf /// - bls381 /// - bls377 /// @@ -327,10 +356,11 @@ pub trait Keystore: Send + Sync { self.ecdsa_sign(id, &public, msg)?.map(|s| s.encode()) }, - bandersnatch::CRYPTO_ID => { - let public = bandersnatch::Public::from_slice(public) + #[cfg(feature = "bsnvrf-experimental")] + bsnvrf::CRYPTO_ID => { + let public = bsnvrf::Public::from_slice(public) .map_err(|_| Error::ValidationError("Invalid public key format".into()))?; - self.bandersnatch_sign(id, &public, msg)?.map(|s| s.encode()) + self.bsnvrf_sign(id, &public, msg)?.map(|s| s.encode()) }, #[cfg(feature = "bls-experimental")] bls381::CRYPTO_ID => { diff --git a/primitives/keystore/src/testing.rs b/primitives/keystore/src/testing.rs index b9c685397fb6f..95fe2d4325bdb 100644 --- a/primitives/keystore/src/testing.rs +++ b/primitives/keystore/src/testing.rs @@ -19,13 +19,14 @@ use crate::{Error, Keystore, KeystorePtr}; +#[cfg(feature = "bsnvrf-experimental")] +use sp_core::bsnvrf; +#[cfg(feature = "bls-experimental")] +use sp_core::{bls377, bls381}; use sp_core::{ - bandersnatch, crypto::{ByteArray, KeyTypeId, Pair, VrfSecret}, ecdsa, ed25519, sr25519, }; -#[cfg(feature = "bls-experimental")] -use sp_core::{bls377, bls381}; use parking_lot::RwLock; use std::{collections::HashMap, sync::Arc}; @@ -215,43 +216,48 @@ impl Keystore for MemoryKeystore { Ok(sig) } - fn bandersnatch_public_keys(&self, key_type: KeyTypeId) -> Vec { - self.public_keys::(key_type) + #[cfg(feature = "bsnvrf-experimental")] + fn bsnvrf_public_keys(&self, key_type: KeyTypeId) -> Vec { + self.public_keys::(key_type) } - fn bandersnatch_generate_new( + #[cfg(feature = "bsnvrf-experimental")] + fn bsnvrf_generate_new( &self, key_type: KeyTypeId, seed: Option<&str>, - ) -> Result { - self.generate_new::(key_type, seed) + ) -> Result { + self.generate_new::(key_type, seed) } - fn bandersnatch_sign( + #[cfg(feature = "bsnvrf-experimental")] + fn bsnvrf_sign( &self, key_type: KeyTypeId, - public: &bandersnatch::Public, + public: &bsnvrf::Public, msg: &[u8], - ) -> Result, Error> { - self.sign::(key_type, public, msg) + ) -> Result, Error> { + self.sign::(key_type, public, msg) } - fn bandersnatch_vrf_sign( + #[cfg(feature = "bsnvrf-experimental")] + fn bsnvrf_vrf_sign( &self, key_type: KeyTypeId, - public: &bandersnatch::Public, - data: &bandersnatch::vrf::VrfSignData, - ) -> Result, Error> { - self.vrf_sign::(key_type, public, data) + public: &bsnvrf::Public, + data: &bsnvrf::vrf::VrfSignData, + ) -> Result, Error> { + self.vrf_sign::(key_type, public, data) } - fn bandersnatch_vrf_output( + #[cfg(feature = "bsnvrf-experimental")] + fn bsnvrf_vrf_output( &self, key_type: KeyTypeId, - public: &bandersnatch::Public, - input: &bandersnatch::vrf::VrfInput, - ) -> Result, Error> { - self.vrf_output::(key_type, public, input) + public: &bsnvrf::Public, + input: &bsnvrf::vrf::VrfInput, + ) -> Result, Error> { + self.vrf_output::(key_type, public, input) } #[cfg(feature = "bls-experimental")] From 2711eb6e871234f22deaecbdc8cf23c38007aa08 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Mon, 22 May 2023 16:07:29 +0200 Subject: [PATCH 20/22] Some more docs --- primitives/core/src/bsnvrf.rs | 48 ++++++++++++++++++++-------------- primitives/keystore/src/lib.rs | 2 +- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/primitives/core/src/bsnvrf.rs b/primitives/core/src/bsnvrf.rs index 1f8d0bb7682c3..1304c418047f0 100644 --- a/primitives/core/src/bsnvrf.rs +++ b/primitives/core/src/bsnvrf.rs @@ -15,9 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! TODO DOCS. - -// #![allow(unused)] +//! VRF defined using Bandersnatch, an elliptic curve built over the BLS12-381 scalar field. #[cfg(feature = "std")] use crate::crypto::Ss58Codec; @@ -125,7 +123,7 @@ impl sp_std::fmt::Debug for Public { } } -/// TODO davxy: DOCS +/// Signature without attached VRF pre-out. #[cfg_attr(feature = "full_crypto", derive(Hash))] #[derive(Clone, Copy, PartialEq, Eq, Encode, Decode, PassByInner, MaxEncodedLen, TypeInfo)] pub struct Signature([u8; SIGNATURE_SERIALIZED_LEN]); @@ -186,7 +184,7 @@ impl sp_std::fmt::Debug for Signature { #[cfg(feature = "full_crypto")] type Seed = [u8; SEED_SERIALIZED_LEN]; -/// TODO davxy: DOCS +/// Keypair #[cfg(feature = "full_crypto")] #[derive(Clone)] pub struct Pair(SecretKey); @@ -282,12 +280,14 @@ pub mod vrf { const PREOUT_SERIALIZED_LEN: usize = 32; - /// Max number of VRF inputs/outputs + /// Max number of VRF inputs/outputs per sign operation. + /// In other words, the max number of `VrfInput`s and `VrfOutput`s embeddable + /// in `VrfSignData` and `VrfSignature`, respectively. pub const MAX_VRF_IOS: u32 = 3; pub(super) type VrfIosVec = BoundedVec>; - /// Input to be used for VRF sign and verify operations. + /// Input used for VRF operations. #[derive(Clone)] pub struct VrfInput(pub(super) bandersnatch_vrfs::VrfInput); @@ -295,21 +295,27 @@ pub mod vrf { /// Build a new VRF input. /// /// Each message tuple has the form: (domain, data). - // TODO: Maybe we should access directly the transcript. - // I see a commented method in bandersnatch_vrfs crate that fullfil what we need... + // TODO davxy (temporary hack) + // `bandersnatch_vrfs::Message` maps to a single (domain, data) tuple. + // We need something to push multiple messages together with a `label`. + // One solution is to construct the labeled Transcript here and then use + // `dleq_vrf::into_vrf_input()`. + // But has been commented out: + // https://github.com/w3f/ring-vrf/blob/8ab7b7b56e844f80b76afb1742b201fd69fb6046/dleq_vrf/src/vrf.rs#L38-L55 pub fn new(label: &'static [u8], messages: &[(&[u8], &[u8])]) -> Self { - let _ = label; let mut buf = Vec::new(); messages.into_iter().for_each(|(domain, message)| { buf.extend_from_slice(domain); buf.extend_from_slice(message); }); - let msg = Message { domain: b"TODO-DAVXY-FIXME", message: buf.as_slice() }; + let msg = Message { domain: label, message: buf.as_slice() }; VrfInput(msg.into_vrf_input()) } } - /// TODO davxy docs + /// VRF Pre-output derived from some `VrfInput` using a `VrfSecret`. + /// + /// This can be used to generate an arbitrary number of bytes va the `make_bytes` method. #[derive(Clone, Debug, PartialEq, Eq)] pub struct VrfOutput(pub(super) bandersnatch_vrfs::VrfPreOut); @@ -346,7 +352,7 @@ pub mod vrf { } } - /// TODO davxy docs + /// A Fiat-Shamir transcript and a sequence of `VrfInput`s ready to be signed. pub struct VrfSignData { /// Associated Fiat-Shamir transcript pub transcript: Transcript, @@ -355,7 +361,7 @@ pub mod vrf { } impl VrfSignData { - /// Construct a new data to be signed. + /// Construct a new data ready to be signed or verified. pub fn new>>( label: &'static [u8], transcript_data: &[&[u8]], @@ -420,8 +426,9 @@ pub mod vrf { #[cfg(feature = "full_crypto")] impl VrfSecret for Pair { fn vrf_sign(&self, data: &Self::VrfSignData) -> Self::VrfSignature { - // Hack used because backend signature type is generic over the number of ios - // @burdges can we provide a vec or boxed version? + // TODO davxy: + // (maybe temporary) Hack used because backend signature type is generic over the number + // of ios @burdges can we provide a vec or boxed version? match data.vrf_inputs.len() { 0 => self.vrf_sign_gen::<0>(data), 1 => self.vrf_sign_gen::<1>(data), @@ -450,8 +457,9 @@ pub mod vrf { if preouts_len != data.vrf_inputs.len() { return false } - // Hack used because backend signature type is generic over the number of ios - // @burdges can we provide a vec or boxed version? + // TODO davxy: + // (maybe temporary) Hack used because backend signature type is generic over the number + // of ios @burdges can we provide a vec or boxed version? match preouts_len { 0 => self.vrf_verify_gen::<0>(data, signature), 1 => self.vrf_verify_gen::<1>(data, signature), @@ -519,8 +527,8 @@ pub mod vrf { }; // Deserialize only the proof, the rest has already been deserialized - // This is another hack used because backend signature type is generic over the number - // of ios. @burdges can we provide a vec or boxed version? + // TODO davxy. This is another hack used because backend signature type is generic over + // the number of ios. @burdges can you provide a vec version? let Ok(signature) = ThinVrfSignature::<0>::deserialize_compressed(signature.signature.as_ref()).map(|s| s.signature) else { return false }; diff --git a/primitives/keystore/src/lib.rs b/primitives/keystore/src/lib.rs index 7e6a5538fdf2d..e2c28011ef8a2 100644 --- a/primitives/keystore/src/lib.rs +++ b/primitives/keystore/src/lib.rs @@ -199,7 +199,7 @@ pub trait Keystore: Send + Sync { /// /// Returns `None` if the given `key_type` and `public` combination doesn't /// exist in the keystore or an `Err` when something failed. - // TODO DAVXY: maybe we can remove this and just pass through the `vrf_sign` + // TODO davxy: maybe we can remove this and just pass through the `vrf_sign` // as this reduces to the other with a sign-data with 0 vrf-inputs. fn bsnvrf_sign( &self, From 5d2e2bbf17bb7f375d6d24bfe696be2c6e54aa2a Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Mon, 22 May 2023 16:32:27 +0200 Subject: [PATCH 21/22] Fix old name references --- primitives/application-crypto/Cargo.toml | 8 ++++++++ .../src/{bandersnatch.rs => bsnvrf.rs} | 10 +++++----- primitives/application-crypto/src/lib.rs | 3 ++- primitives/io/src/lib.rs | 6 +----- primitives/keyring/Cargo.toml | 5 +++++ primitives/keyring/src/{bandersnatch.rs => bsnvrf.rs} | 6 +++--- primitives/keyring/src/lib.rs | 8 +++++--- 7 files changed, 29 insertions(+), 17 deletions(-) rename primitives/application-crypto/src/{bandersnatch.rs => bsnvrf.rs} (84%) rename primitives/keyring/src/{bandersnatch.rs => bsnvrf.rs} (97%) diff --git a/primitives/application-crypto/Cargo.toml b/primitives/application-crypto/Cargo.toml index 37b58bc2bf649..3f5f2379b3430 100644 --- a/primitives/application-crypto/Cargo.toml +++ b/primitives/application-crypto/Cargo.toml @@ -44,3 +44,11 @@ full_crypto = [ "sp-io/disable_panic_handler", "sp-io/disable_oom", ] + +# This feature adds Bandersnatch-VRFs crypto primitives. +# It should not be used in production since the implementation +# and interface may still be subject to significant change. +bsnvrf-experimental = [ + "sp-core/bsnvrf-experimental", + "sp-io/bsnvrf-experimental", +] diff --git a/primitives/application-crypto/src/bandersnatch.rs b/primitives/application-crypto/src/bsnvrf.rs similarity index 84% rename from primitives/application-crypto/src/bandersnatch.rs rename to primitives/application-crypto/src/bsnvrf.rs index 68adee8061c00..9f49299ca16ad 100644 --- a/primitives/application-crypto/src/bandersnatch.rs +++ b/primitives/application-crypto/src/bsnvrf.rs @@ -18,7 +18,7 @@ //! Bandersnatch VRF application crypto types. use crate::{KeyTypeId, RuntimePublic}; -pub use sp_core::bandersnatch::*; +pub use sp_core::bsnvrf::*; use sp_std::vec::Vec; mod app { @@ -33,21 +33,21 @@ impl RuntimePublic for Public { type Signature = Signature; fn all(_key_type: KeyTypeId) -> Vec { - // sp_io::crypto::bandersnatch_public_keys(key_type) + // sp_io::crypto::bsnvrf_public_keys(key_type) unimplemented!() } fn generate_pair(key_type: KeyTypeId, seed: Option>) -> Self { - sp_io::crypto::bandersnatch_generate(key_type, seed) + sp_io::crypto::bsnvrf_generate(key_type, seed) } fn sign>(&self, _key_type: KeyTypeId, _msg: &M) -> Option { - // sp_io::crypto::bandersnatch_sign(key_type, self, msg.as_ref()) + // sp_io::crypto::bsnvrf_sign(key_type, self, msg.as_ref()) unimplemented!() } fn verify>(&self, _msg: &M, _signature: &Self::Signature) -> bool { - // sp_io::crypto::bandersnatch_verify(signature, msg.as_ref(), self) + // sp_io::crypto::bsnvrf_verify(signature, msg.as_ref(), self) unimplemented!() } diff --git a/primitives/application-crypto/src/lib.rs b/primitives/application-crypto/src/lib.rs index 2c4f4835bd20a..281e97a549dee 100644 --- a/primitives/application-crypto/src/lib.rs +++ b/primitives/application-crypto/src/lib.rs @@ -45,11 +45,12 @@ pub use traits::*; mod traits; -pub mod bandersnatch; #[cfg(feature = "bls-experimental")] pub mod bls377; #[cfg(feature = "bls-experimental")] pub mod bls381; +#[cfg(feature = "bsnvrf-experimental")] +pub mod bsnvrf; pub mod ecdsa; pub mod ed25519; pub mod sr25519; diff --git a/primitives/io/src/lib.rs b/primitives/io/src/lib.rs index 0185ae37fbb08..ab7b241a21150 100644 --- a/primitives/io/src/lib.rs +++ b/primitives/io/src/lib.rs @@ -1142,12 +1142,8 @@ pub trait Crypto { .map_err(|_| EcdsaVerifyError::BadSignature)?; Ok(pubkey.serialize()) } -} -/// Temporary experimental interface that provides required Bandersnatch-VRFs functions. -#[cfg(feature = "bsnvrf-experimental")] -#[runtime_interface] -pub trait BsnVrfExperimental { + #[cfg(feature = "bsnvrf-experimental")] /// Generate a `bsnvrf` key for the given key type using an optional `seed` and /// store it in the keystore. /// diff --git a/primitives/keyring/Cargo.toml b/primitives/keyring/Cargo.toml index db3a8de2b2433..219436b225fa3 100644 --- a/primitives/keyring/Cargo.toml +++ b/primitives/keyring/Cargo.toml @@ -18,3 +18,8 @@ lazy_static = "1.4.0" strum = { version = "0.24.1", features = ["derive"], default-features = false } sp-core = { version = "7.0.0", path = "../core" } sp-runtime = { version = "7.0.0", path = "../runtime" } + +[features] +bsnvrf-experimental = [ + "sp-core/bsnvrf-experimental", +] diff --git a/primitives/keyring/src/bandersnatch.rs b/primitives/keyring/src/bsnvrf.rs similarity index 97% rename from primitives/keyring/src/bandersnatch.rs rename to primitives/keyring/src/bsnvrf.rs index a61e9dafef877..df0538c67cd05 100644 --- a/primitives/keyring/src/bandersnatch.rs +++ b/primitives/keyring/src/bsnvrf.rs @@ -17,9 +17,9 @@ //! Support code for the runtime. A set of test accounts. -pub use sp_core::bandersnatch; +pub use sp_core::bsnvrf; use sp_core::{ - bandersnatch::{Pair, Public, Signature}, + bsnvrf::{Pair, Public, Signature}, crypto::UncheckedFrom, ByteArray, Pair as PairT, H256, }; @@ -218,7 +218,7 @@ impl Deref for Keyring { #[cfg(test)] mod tests { use super::*; - use sp_core::{bandersnatch::Pair, Pair as PairT}; + use sp_core::{bsnvrf::Pair, Pair as PairT}; #[test] fn should_work() { diff --git a/primitives/keyring/src/lib.rs b/primitives/keyring/src/lib.rs index fba15a121e818..69df7fc74907e 100644 --- a/primitives/keyring/src/lib.rs +++ b/primitives/keyring/src/lib.rs @@ -23,15 +23,17 @@ pub mod sr25519; /// Test account crypto for ed25519. pub mod ed25519; -/// Test account crypto for bandersnatch. -pub mod bandersnatch; +#[cfg(feature = "bsnvrf-experimental")] +/// Test account crypto for Bandersnatch-Vrf +pub mod bsnvrf; /// Convenience export: Sr25519's Keyring is exposed as `AccountKeyring`, /// since it tends to be used for accounts (although it may also be used /// by authorities). pub use sr25519::Keyring as AccountKeyring; -pub use bandersnatch::Keyring as BandersnatchKeyring; +#[cfg(feature = "bsnvrf-experimental")] +pub use bsnvrf::Keyring as BsnVrfKeyring; pub use ed25519::Keyring as Ed25519Keyring; pub use sr25519::Keyring as Sr25519Keyring; From 5a900da2962924f54086d116081d50e35c41db86 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Mon, 22 May 2023 17:50:16 +0200 Subject: [PATCH 22/22] Dummy bsnvrf application-crypto implementations --- primitives/application-crypto/src/bsnvrf.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/primitives/application-crypto/src/bsnvrf.rs b/primitives/application-crypto/src/bsnvrf.rs index 9f49299ca16ad..86a9b11eed0b5 100644 --- a/primitives/application-crypto/src/bsnvrf.rs +++ b/primitives/application-crypto/src/bsnvrf.rs @@ -33,8 +33,7 @@ impl RuntimePublic for Public { type Signature = Signature; fn all(_key_type: KeyTypeId) -> Vec { - // sp_io::crypto::bsnvrf_public_keys(key_type) - unimplemented!() + vec![] } fn generate_pair(key_type: KeyTypeId, seed: Option>) -> Self { @@ -42,13 +41,11 @@ impl RuntimePublic for Public { } fn sign>(&self, _key_type: KeyTypeId, _msg: &M) -> Option { - // sp_io::crypto::bsnvrf_sign(key_type, self, msg.as_ref()) - unimplemented!() + None } fn verify>(&self, _msg: &M, _signature: &Self::Signature) -> bool { - // sp_io::crypto::bsnvrf_verify(signature, msg.as_ref(), self) - unimplemented!() + false } fn to_raw_vec(&self) -> Vec {