// 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. //! VRFs backed by [Bandersnatch](https://neuromancer.sk/std/bls/Bandersnatch), //! an elliptic curve built over BLS12-381 scalar field. //! //! The primitive can operate both as a regular VRF or as an anonymized Ring VRF. #[cfg(feature = "full_crypto")] use crate::crypto::VrfSecret; use crate::crypto::{ ByteArray, CryptoType, CryptoTypeId, DeriveError, DeriveJunction, Pair as TraitPair, PublicBytes, SecretStringError, SignatureBytes, UncheckedFrom, VrfPublic, }; use bandersnatch_vrfs::{CanonicalSerialize, SecretKey}; use codec::{Decode, Encode, EncodeLike, MaxEncodedLen}; use scale_info::TypeInfo; use sp_std::{vec, vec::Vec}; /// Identifier used to match public keys against bandersnatch-vrf keys. pub const CRYPTO_ID: CryptoTypeId = CryptoTypeId(*b"band"); /// Context used to produce a plain signature without any VRF input/output. pub const SIGNING_CTX: &[u8] = b"BandersnatchSigningContext"; /// The byte length of secret key seed. pub const SEED_SERIALIZED_SIZE: usize = 32; /// The byte length of serialized public key. pub const PUBLIC_SERIALIZED_SIZE: usize = 33; /// The byte length of serialized signature. pub const SIGNATURE_SERIALIZED_SIZE: usize = 65; /// The byte length of serialized pre-output. pub const PREOUT_SERIALIZED_SIZE: usize = 33; #[doc(hidden)] pub struct BandersnatchTag; /// Bandersnatch public key. pub type Public = PublicBytes; impl CryptoType for Public { type Pair = Pair; } /// Bandersnatch signature. /// /// The signature is created via the [`VrfSecret::vrf_sign`] using [`SIGNING_CTX`] as transcript /// `label`. pub type Signature = SignatureBytes; impl CryptoType for Signature { type Pair = Pair; } /// The raw secret seed, which can be used to reconstruct the secret [`Pair`]. type Seed = [u8; SEED_SERIALIZED_SIZE]; /// Bandersnatch secret key. #[derive(Clone)] pub struct Pair { secret: SecretKey, seed: Seed, } impl Pair { /// Get the key seed. pub fn seed(&self) -> Seed { self.seed } } 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 32 bytes long or it will return an error. fn from_seed_slice(seed_slice: &[u8]) -> Result { if seed_slice.len() != SEED_SERIALIZED_SIZE { return Err(SecretStringError::InvalidSeedLength) } let mut seed = [0; SEED_SERIALIZED_SIZE]; seed.copy_from_slice(seed_slice); let secret = SecretKey::from_seed(&seed); Ok(Pair { secret, seed }) } /// 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> { let derive_hard = |seed, cc| -> Seed { ("bandersnatch-vrf-HDKD", seed, cc).using_encoded(sp_crypto_hashing::blake2_256) }; let mut seed = self.seed(); for p in path { if let DeriveJunction::Hard(cc) = p { seed = derive_hard(seed, cc); } else { return Err(DeriveError::SoftKeyInPath) } } Ok((Self::from_seed(&seed), Some(seed))) } fn public(&self) -> Public { let public = self.secret.to_public(); let mut raw = [0; PUBLIC_SERIALIZED_SIZE]; public .serialize_compressed(raw.as_mut_slice()) .expect("serialization length is constant and checked by test; qed"); Public::unchecked_from(raw) } /// Sign a message. /// /// In practice this produce a Schnorr signature of a transcript composed by /// the constant label [`SIGNING_CTX`] and `data` without any additional data. /// /// See [`vrf::VrfSignData`] for additional details. #[cfg(feature = "full_crypto")] fn sign(&self, data: &[u8]) -> Signature { let data = vrf::VrfSignData::new_unchecked(SIGNING_CTX, &[data], None); self.vrf_sign(&data).signature } fn verify>(signature: &Signature, data: M, public: &Public) -> bool { let data = vrf::VrfSignData::new_unchecked(SIGNING_CTX, &[data.as_ref()], None); let signature = vrf::VrfSignature { signature: *signature, pre_outputs: vrf::VrfIosVec::default() }; public.vrf_verify(&data, &signature) } /// Return a vector filled with the seed (32 bytes). fn to_raw_vec(&self) -> Vec { self.seed().to_vec() } } impl CryptoType for Pair { type Pair = Pair; } /// Bandersnatch VRF types and operations. pub mod vrf { use super::*; use crate::{bounded::BoundedVec, crypto::VrfCrypto, ConstU32}; use bandersnatch_vrfs::{ CanonicalDeserialize, CanonicalSerialize, IntoVrfInput, Message, PublicKey, ThinVrfSignature, Transcript, }; /// Max number of inputs/pre-outputs which can be handled by the VRF signing procedures. /// /// The number is quite arbitrary and chosen to fulfill the use cases found so far. /// If required it can be extended in the future. pub const MAX_VRF_IOS: u32 = 3; /// Bounded vector used for VRF inputs and pre-outputs. /// /// Can contain at most [`MAX_VRF_IOS`] elements. pub type VrfIosVec = BoundedVec>; /// VRF input to construct a [`VrfPreOutput`] instance and embeddable in [`VrfSignData`]. #[derive(Clone, Debug)] pub struct VrfInput(pub(super) bandersnatch_vrfs::VrfInput); impl VrfInput { /// Construct a new VRF input. pub fn new(domain: impl AsRef<[u8]>, data: impl AsRef<[u8]>) -> Self { let msg = Message { domain: domain.as_ref(), message: data.as_ref() }; VrfInput(msg.into_vrf_input()) } } /// VRF pre-output derived from [`VrfInput`] using a [`VrfSecret`]. /// /// This object is used to produce an arbitrary number of verifiable pseudo random /// bytes and is often called pre-output to emphasize that this is not the actual /// output of the VRF but an object capable of generating the output. #[derive(Clone, Debug, PartialEq, Eq)] pub struct VrfPreOutput(pub(super) bandersnatch_vrfs::VrfPreOut); impl Encode for VrfPreOutput { fn encode(&self) -> Vec { let mut bytes = [0; PREOUT_SERIALIZED_SIZE]; self.0 .serialize_compressed(bytes.as_mut_slice()) .expect("serialization length is constant and checked by test; qed"); bytes.encode() } } impl Decode for VrfPreOutput { fn decode(i: &mut R) -> Result { let buf = <[u8; PREOUT_SERIALIZED_SIZE]>::decode(i)?; let preout = bandersnatch_vrfs::VrfPreOut::deserialize_compressed_unchecked(buf.as_slice()) .map_err(|_| "vrf-preout decode error: bad preout")?; Ok(VrfPreOutput(preout)) } } impl EncodeLike for VrfPreOutput {} impl MaxEncodedLen for VrfPreOutput { fn max_encoded_len() -> usize { <[u8; PREOUT_SERIALIZED_SIZE]>::max_encoded_len() } } impl TypeInfo for VrfPreOutput { type Identity = [u8; PREOUT_SERIALIZED_SIZE]; fn type_info() -> scale_info::Type { Self::Identity::type_info() } } /// Data to be signed via one of the two provided vrf flavors. /// /// The object contains a transcript and a sequence of [`VrfInput`]s ready to be signed. /// /// The `transcript` summarizes a set of messages which are defining a particular /// protocol by automating the Fiat-Shamir transform for challenge generation. /// A good explanation of the topic can be found in Merlin [docs](https://merlin.cool/) /// /// The `inputs` is a sequence of [`VrfInput`]s which, during the signing procedure, are /// first transformed to [`VrfPreOutput`]s. Both inputs and pre-outputs are then appended to /// the transcript before signing the Fiat-Shamir transform result (the challenge). /// /// In practice, as a user, all these technical details can be easily ignored. /// What is important to remember is: /// - *Transcript* is an object defining the protocol and used to produce the signature. This /// object doesn't influence the `VrfPreOutput`s values. /// - *Vrf inputs* is some additional data which is used to produce *vrf pre-outputs*. This data /// will contribute to the signature as well. #[derive(Clone)] pub struct VrfSignData { /// Associated protocol transcript. pub transcript: Transcript, /// VRF inputs to be signed. pub inputs: VrfIosVec, } impl VrfSignData { /// Construct a new data to be signed. /// /// Fails if the `inputs` iterator yields more elements than [`MAX_VRF_IOS`] /// /// Refer to [`VrfSignData`] for details about transcript and inputs. pub fn new( transcript_label: &'static [u8], transcript_data: impl IntoIterator>, inputs: impl IntoIterator, ) -> Result { let inputs: Vec = inputs.into_iter().collect(); if inputs.len() > MAX_VRF_IOS as usize { return Err(()) } Ok(Self::new_unchecked(transcript_label, transcript_data, inputs)) } /// Construct a new data to be signed. /// /// At most the first [`MAX_VRF_IOS`] elements of `inputs` are used. /// /// Refer to [`VrfSignData`] for details about transcript and inputs. pub fn new_unchecked( transcript_label: &'static [u8], transcript_data: impl IntoIterator>, inputs: impl IntoIterator, ) -> Self { let inputs: Vec = inputs.into_iter().collect(); let inputs = VrfIosVec::truncate_from(inputs); let mut transcript = Transcript::new_labeled(transcript_label); transcript_data.into_iter().for_each(|data| transcript.append(data.as_ref())); VrfSignData { transcript, inputs } } /// Append a message to the transcript. pub fn push_transcript_data(&mut self, data: &[u8]) { self.transcript.append(data); } /// Tries to append a [`VrfInput`] to the vrf inputs list. /// /// On failure, returns back the [`VrfInput`] parameter. pub fn push_vrf_input(&mut self, input: VrfInput) -> Result<(), VrfInput> { self.inputs.try_push(input) } /// Get the challenge associated to the `transcript` contained within the signing data. /// /// Ignores the vrf inputs and outputs. pub fn challenge(&self) -> [u8; N] { let mut output = [0; N]; let mut transcript = self.transcript.clone(); let mut reader = transcript.challenge(b"bandersnatch challenge"); reader.read_bytes(&mut output); output } } /// VRF signature. /// /// Includes both the transcript `signature` and the `pre-outputs` generated from the /// [`VrfSignData::inputs`]. /// /// Refer to [`VrfSignData`] for more details. #[derive(Clone, Debug, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo)] pub struct VrfSignature { /// Transcript signature. pub signature: Signature, /// VRF pre-outputs. pub pre_outputs: VrfIosVec, } #[cfg(feature = "full_crypto")] impl VrfCrypto for Pair { type VrfInput = VrfInput; type VrfPreOutput = VrfPreOutput; type VrfSignData = VrfSignData; type VrfSignature = VrfSignature; } #[cfg(feature = "full_crypto")] impl VrfSecret for Pair { fn vrf_sign(&self, data: &Self::VrfSignData) -> Self::VrfSignature { const _: () = assert!(MAX_VRF_IOS == 3, "`MAX_VRF_IOS` expected to be 3"); // Workaround to overcome backend signature generic over the number of IOs. match data.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), _ => unreachable!(), } } fn vrf_pre_output(&self, input: &Self::VrfInput) -> Self::VrfPreOutput { let pre_output = self.secret.vrf_preout(&input.0); VrfPreOutput(pre_output) } } impl VrfCrypto for Public { type VrfInput = VrfInput; type VrfPreOutput = VrfPreOutput; type VrfSignData = VrfSignData; type VrfSignature = VrfSignature; } impl VrfPublic for Public { fn vrf_verify(&self, data: &Self::VrfSignData, signature: &Self::VrfSignature) -> bool { const _: () = assert!(MAX_VRF_IOS == 3, "`MAX_VRF_IOS` expected to be 3"); let pre_outputs_len = signature.pre_outputs.len(); if pre_outputs_len != data.inputs.len() { return false } // Workaround to overcome backend signature generic over the number of IOs. match pre_outputs_len { 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), _ => unreachable!(), } } } #[cfg(feature = "full_crypto")] impl Pair { fn vrf_sign_gen(&self, data: &VrfSignData) -> VrfSignature { let ios = core::array::from_fn(|i| self.secret.vrf_inout(data.inputs[i].0)); let thin_signature: ThinVrfSignature = self.secret.sign_thin_vrf(data.transcript.clone(), &ios); let pre_outputs: Vec<_> = thin_signature.preouts.into_iter().map(VrfPreOutput).collect(); let pre_outputs = VrfIosVec::truncate_from(pre_outputs); let mut signature = VrfSignature { signature: Signature::default(), pre_outputs }; thin_signature .proof .serialize_compressed(signature.signature.0.as_mut_slice()) .expect("serialization length is constant and checked by test; qed"); signature } /// Generate an arbitrary number of bytes from the given `context` and VRF `input`. pub fn make_bytes( &self, context: &'static [u8], input: &VrfInput, ) -> [u8; N] { let transcript = Transcript::new_labeled(context); let inout = self.secret.vrf_inout(input.0); inout.vrf_output_bytes(transcript) } } impl Public { fn vrf_verify_gen( &self, data: &VrfSignData, signature: &VrfSignature, ) -> bool { let Ok(public) = PublicKey::deserialize_compressed_unchecked(self.as_slice()) else { return false }; let preouts: [bandersnatch_vrfs::VrfPreOut; N] = core::array::from_fn(|i| signature.pre_outputs[i].0); // 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. let Ok(proof) = ThinVrfSignature::<0>::deserialize_compressed_unchecked( signature.signature.as_slice(), ) .map(|s| s.proof) else { return false }; let signature = ThinVrfSignature { proof, preouts }; let inputs = data.inputs.iter().map(|i| i.0); public.verify_thin_vrf(data.transcript.clone(), inputs, &signature).is_ok() } } impl VrfPreOutput { /// Generate an arbitrary number of bytes from the given `context` and VRF `input`. pub fn make_bytes( &self, context: &'static [u8], input: &VrfInput, ) -> [u8; N] { let transcript = Transcript::new_labeled(context); let inout = bandersnatch_vrfs::VrfInOut { input: input.0, preoutput: self.0 }; inout.vrf_output_bytes(transcript) } } } /// Bandersnatch Ring-VRF types and operations. pub mod ring_vrf { use super::{vrf::*, *}; pub use bandersnatch_vrfs::ring::{RingProof, RingProver, RingVerifier, KZG}; use bandersnatch_vrfs::{ring::VerifierKey, CanonicalDeserialize, PublicKey}; /// Overhead in the domain size with respect to the supported ring size. /// /// Some bits of the domain are reserved for the zk-proof to work. pub const RING_DOMAIN_OVERHEAD: u32 = 257; // Max size of serialized ring-vrf context given `domain_len`. pub(crate) const fn ring_context_serialized_size(domain_len: u32) -> usize { // const G1_POINT_COMPRESSED_SIZE: usize = 48; // const G2_POINT_COMPRESSED_SIZE: usize = 96; const G1_POINT_UNCOMPRESSED_SIZE: usize = 96; const G2_POINT_UNCOMPRESSED_SIZE: usize = 192; const OVERHEAD_SIZE: usize = 20; const G2_POINTS_NUM: usize = 2; let g1_points_num = 3 * domain_len as usize + 1; OVERHEAD_SIZE + g1_points_num * G1_POINT_UNCOMPRESSED_SIZE + G2_POINTS_NUM * G2_POINT_UNCOMPRESSED_SIZE } pub(crate) const RING_VERIFIER_DATA_SERIALIZED_SIZE: usize = 388; pub(crate) const RING_SIGNATURE_SERIALIZED_SIZE: usize = 755; /// remove as soon as soon as serialization is implemented by the backend pub struct RingVerifierData { /// Domain size. pub domain_size: u32, /// Verifier key. pub verifier_key: VerifierKey, } impl From for RingVerifier { fn from(vd: RingVerifierData) -> RingVerifier { bandersnatch_vrfs::ring::make_ring_verifier(vd.verifier_key, vd.domain_size as usize) } } impl Encode for RingVerifierData { fn encode(&self) -> Vec { const ERR_STR: &str = "serialization length is constant and checked by test; qed"; let mut buf = [0; RING_VERIFIER_DATA_SERIALIZED_SIZE]; self.domain_size.serialize_compressed(&mut buf[..4]).expect(ERR_STR); self.verifier_key.serialize_compressed(&mut buf[4..]).expect(ERR_STR); buf.encode() } } impl Decode for RingVerifierData { fn decode(i: &mut R) -> Result { const ERR_STR: &str = "serialization length is constant and checked by test; qed"; let buf = <[u8; RING_VERIFIER_DATA_SERIALIZED_SIZE]>::decode(i)?; let domain_size = ::deserialize_compressed_unchecked(&mut &buf[..4]) .expect(ERR_STR); let verifier_key = ::deserialize_compressed_unchecked(&mut &buf[4..]).expect(ERR_STR); Ok(RingVerifierData { domain_size, verifier_key }) } } impl EncodeLike for RingVerifierData {} impl MaxEncodedLen for RingVerifierData { fn max_encoded_len() -> usize { <[u8; RING_VERIFIER_DATA_SERIALIZED_SIZE]>::max_encoded_len() } } impl TypeInfo for RingVerifierData { type Identity = [u8; RING_VERIFIER_DATA_SERIALIZED_SIZE]; fn type_info() -> scale_info::Type { Self::Identity::type_info() } } /// Context used to construct ring prover and verifier. /// /// Generic parameter `D` represents the ring domain size and drives /// the max number of supported ring members [`RingContext::max_keyset_size`] /// which is equal to `D - RING_DOMAIN_OVERHEAD`. #[derive(Clone)] pub struct RingContext(KZG); impl RingContext { /// Build an dummy instance for testing purposes. pub fn new_testing() -> Self { Self(KZG::testing_kzg_setup([0; 32], D)) } /// Get the keyset max size. pub fn max_keyset_size(&self) -> usize { self.0.max_keyset_size() } /// Get ring prover for the key at index `public_idx` in the `public_keys` set. pub fn prover(&self, public_keys: &[Public], public_idx: usize) -> Option { let mut pks = Vec::with_capacity(public_keys.len()); for public_key in public_keys { let pk = PublicKey::deserialize_compressed_unchecked(public_key.as_slice()).ok()?; pks.push(pk.0.into()); } let prover_key = self.0.prover_key(pks); let ring_prover = self.0.init_ring_prover(prover_key, public_idx); Some(ring_prover) } /// Get ring verifier for the `public_keys` set. pub fn verifier(&self, public_keys: &[Public]) -> Option { let mut pks = Vec::with_capacity(public_keys.len()); for public_key in public_keys { let pk = PublicKey::deserialize_compressed_unchecked(public_key.as_slice()).ok()?; pks.push(pk.0.into()); } let verifier_key = self.0.verifier_key(pks); let ring_verifier = self.0.init_ring_verifier(verifier_key); Some(ring_verifier) } /// Information required for a lazy construction of a ring verifier. pub fn verifier_data(&self, public_keys: &[Public]) -> Option { let mut pks = Vec::with_capacity(public_keys.len()); for public_key in public_keys { let pk = PublicKey::deserialize_compressed_unchecked(public_key.as_slice()).ok()?; pks.push(pk.0.into()); } Some(RingVerifierData { verifier_key: self.0.verifier_key(pks), domain_size: self.0.domain_size, }) } } impl Encode for RingContext { fn encode(&self) -> Vec { let mut buf = vec![0; ring_context_serialized_size(D)]; self.0 .serialize_uncompressed(buf.as_mut_slice()) .expect("serialization length is constant and checked by test; qed"); buf } } impl Decode for RingContext { fn decode(input: &mut R) -> Result { let mut buf = vec![0; ring_context_serialized_size(D)]; input.read(&mut buf[..])?; let kzg = KZG::deserialize_uncompressed_unchecked(buf.as_slice()) .map_err(|_| "KZG decode error")?; Ok(RingContext(kzg)) } } impl EncodeLike for RingContext {} impl MaxEncodedLen for RingContext { fn max_encoded_len() -> usize { ring_context_serialized_size(D) } } impl TypeInfo for RingContext { type Identity = Self; fn type_info() -> scale_info::Type { let path = scale_info::Path::new("RingContext", module_path!()); let array_type_def = scale_info::TypeDefArray { len: ring_context_serialized_size(D) as u32, type_param: scale_info::MetaType::new::(), }; let type_def = scale_info::TypeDef::Array(array_type_def); scale_info::Type { path, type_params: Vec::new(), type_def, docs: Vec::new() } } } /// Ring VRF signature. #[derive(Clone, Debug, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo)] pub struct RingVrfSignature { /// Ring signature. pub signature: [u8; RING_SIGNATURE_SERIALIZED_SIZE], /// VRF pre-outputs. pub pre_outputs: VrfIosVec, } #[cfg(feature = "full_crypto")] impl Pair { /// Produce a ring-vrf signature. /// /// The ring signature is verifiable if the public key corresponding to the /// signing [`Pair`] is part of the ring from which the [`RingProver`] has /// been constructed. If not, the produced signature is just useless. pub fn ring_vrf_sign(&self, data: &VrfSignData, prover: &RingProver) -> RingVrfSignature { const _: () = assert!(MAX_VRF_IOS == 3, "`MAX_VRF_IOS` expected to be 3"); // Workaround to overcome backend signature generic over the number of IOs. match data.inputs.len() { 0 => self.ring_vrf_sign_gen::<0>(data, prover), 1 => self.ring_vrf_sign_gen::<1>(data, prover), 2 => self.ring_vrf_sign_gen::<2>(data, prover), 3 => self.ring_vrf_sign_gen::<3>(data, prover), _ => unreachable!(), } } fn ring_vrf_sign_gen( &self, data: &VrfSignData, prover: &RingProver, ) -> RingVrfSignature { let ios = core::array::from_fn(|i| self.secret.vrf_inout(data.inputs[i].0)); let ring_signature: bandersnatch_vrfs::RingVrfSignature = bandersnatch_vrfs::RingProver { ring_prover: prover, secret: &self.secret } .sign_ring_vrf(data.transcript.clone(), &ios); let pre_outputs: Vec<_> = ring_signature.preouts.into_iter().map(VrfPreOutput).collect(); let pre_outputs = VrfIosVec::truncate_from(pre_outputs); let mut signature = RingVrfSignature { pre_outputs, signature: [0; RING_SIGNATURE_SERIALIZED_SIZE] }; ring_signature .proof .serialize_compressed(signature.signature.as_mut_slice()) .expect("serialization length is constant and checked by test; qed"); signature } } impl RingVrfSignature { /// Verify a ring-vrf signature. /// /// The signature is verifiable if it has been produced by a member of the ring /// from which the [`RingVerifier`] has been constructed. pub fn ring_vrf_verify(&self, data: &VrfSignData, verifier: &RingVerifier) -> bool { const _: () = assert!(MAX_VRF_IOS == 3, "`MAX_VRF_IOS` expected to be 3"); let preouts_len = self.pre_outputs.len(); if preouts_len != data.inputs.len() { return false } // Workaround to overcome backend signature generic over the number of IOs. match preouts_len { 0 => self.ring_vrf_verify_gen::<0>(data, verifier), 1 => self.ring_vrf_verify_gen::<1>(data, verifier), 2 => self.ring_vrf_verify_gen::<2>(data, verifier), 3 => self.ring_vrf_verify_gen::<3>(data, verifier), _ => unreachable!(), } } fn ring_vrf_verify_gen( &self, data: &VrfSignData, verifier: &RingVerifier, ) -> bool { let Ok(vrf_signature) = bandersnatch_vrfs::RingVrfSignature::<0>::deserialize_compressed_unchecked( self.signature.as_slice(), ) else { return false }; let preouts: [bandersnatch_vrfs::VrfPreOut; N] = core::array::from_fn(|i| self.pre_outputs[i].0); let signature = bandersnatch_vrfs::RingVrfSignature { proof: vrf_signature.proof, preouts }; let inputs = data.inputs.iter().map(|i| i.0); bandersnatch_vrfs::RingVerifier(verifier) .verify_ring_vrf(data.transcript.clone(), inputs, &signature) .is_ok() } } } #[cfg(test)] mod tests { use super::{ring_vrf::*, vrf::*, *}; use crate::crypto::{VrfPublic, VrfSecret, DEV_PHRASE}; const DEV_SEED: &[u8; SEED_SERIALIZED_SIZE] = &[0xcb; SEED_SERIALIZED_SIZE]; const TEST_DOMAIN_SIZE: u32 = 1024; type TestRingContext = RingContext; #[allow(unused)] fn b2h(bytes: &[u8]) -> String { array_bytes::bytes2hex("", bytes) } fn h2b(hex: &str) -> Vec { array_bytes::hex2bytes_unchecked(hex) } #[test] fn backend_assumptions_sanity_check() { let kzg = KZG::testing_kzg_setup([0; 32], TEST_DOMAIN_SIZE); assert_eq!(kzg.max_keyset_size() as u32, TEST_DOMAIN_SIZE - RING_DOMAIN_OVERHEAD); assert_eq!(kzg.uncompressed_size(), ring_context_serialized_size(TEST_DOMAIN_SIZE)); let pks: Vec<_> = (0..16) .map(|i| SecretKey::from_seed(&[i as u8; 32]).to_public().0.into()) .collect(); let secret = SecretKey::from_seed(&[0u8; 32]); let public = secret.to_public(); assert_eq!(public.compressed_size(), PUBLIC_SERIALIZED_SIZE); let input = VrfInput::new(b"foo", &[]); let preout = secret.vrf_preout(&input.0); assert_eq!(preout.compressed_size(), PREOUT_SERIALIZED_SIZE); let verifier_key = kzg.verifier_key(pks.clone()); assert_eq!(verifier_key.compressed_size() + 4, RING_VERIFIER_DATA_SERIALIZED_SIZE); let prover_key = kzg.prover_key(pks); let ring_prover = kzg.init_ring_prover(prover_key, 0); let data = VrfSignData::new_unchecked(b"mydata", &[b"tdata"], None); let thin_signature: bandersnatch_vrfs::ThinVrfSignature<0> = secret.sign_thin_vrf(data.transcript.clone(), &[]); assert_eq!(thin_signature.compressed_size(), SIGNATURE_SERIALIZED_SIZE); let ring_signature: bandersnatch_vrfs::RingVrfSignature<0> = bandersnatch_vrfs::RingProver { ring_prover: &ring_prover, secret: &secret } .sign_ring_vrf(data.transcript.clone(), &[]); assert_eq!(ring_signature.compressed_size(), RING_SIGNATURE_SERIALIZED_SIZE); } #[test] fn max_vrf_ios_bound_respected() { let inputs: Vec<_> = (0..MAX_VRF_IOS - 1).map(|_| VrfInput::new(b"", &[])).collect(); let mut sign_data = VrfSignData::new(b"", &[b""], inputs).unwrap(); let res = sign_data.push_vrf_input(VrfInput::new(b"", b"")); assert!(res.is_ok()); let res = sign_data.push_vrf_input(VrfInput::new(b"", b"")); assert!(res.is_err()); let inputs: Vec<_> = (0..MAX_VRF_IOS + 1).map(|_| VrfInput::new(b"", b"")).collect(); let res = VrfSignData::new(b"mydata", &[b"tdata"], inputs); assert!(res.is_err()); } #[test] fn derive_works() { let pair = Pair::from_string(&format!("{}//Alice//Hard", DEV_PHRASE), None).unwrap(); let known = h2b("2b340c18b94dc1916979cb83daf3ed4ac106742ddc06afc42cf26be3b18a523f80"); assert_eq!(pair.public().as_ref(), known); // Soft derivation not supported let res = Pair::from_string(&format!("{}//Alice/Soft", DEV_PHRASE), None); assert!(res.is_err()); } #[test] fn generate_with_phrase_should_be_recoverable_with_from_string() { let (pair, phrase, seed) = Pair::generate_with_phrase(None); let repair_seed = Pair::from_seed_slice(seed.as_ref()).expect("seed slice is valid"); assert_eq!(pair.public(), repair_seed.public()); let (repair_phrase, reseed) = Pair::from_phrase(phrase.as_ref(), None).expect("seed slice is valid"); assert_eq!(seed, reseed); assert_eq!(pair.public(), repair_phrase.public()); let repair_string = Pair::from_string(phrase.as_str(), None).expect("seed slice is valid"); assert_eq!(pair.public(), repair_string.public()); } #[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 vrf_sign_verify() { let pair = Pair::from_seed(DEV_SEED); let public = pair.public(); let i1 = VrfInput::new(b"dom1", b"foo"); let i2 = VrfInput::new(b"dom2", b"bar"); let i3 = VrfInput::new(b"dom3", b"baz"); let data = VrfSignData::new_unchecked(b"mydata", &[b"tdata"], [i1, i2, i3]); let signature = pair.vrf_sign(&data); assert!(public.vrf_verify(&data, &signature)); } #[test] fn vrf_sign_verify_bad_inputs() { let pair = Pair::from_seed(DEV_SEED); let public = pair.public(); let i1 = VrfInput::new(b"dom1", b"foo"); let i2 = VrfInput::new(b"dom2", b"bar"); let data = VrfSignData::new_unchecked(b"mydata", &[b"aaaa"], [i1.clone(), i2.clone()]); let signature = pair.vrf_sign(&data); let data = VrfSignData::new_unchecked(b"mydata", &[b"bbb"], [i1, i2.clone()]); assert!(!public.vrf_verify(&data, &signature)); let data = VrfSignData::new_unchecked(b"mydata", &[b"aaa"], [i2]); assert!(!public.vrf_verify(&data, &signature)); } #[test] fn vrf_make_bytes_matches() { let pair = Pair::from_seed(DEV_SEED); let i1 = VrfInput::new(b"dom1", b"foo"); let i2 = VrfInput::new(b"dom2", b"bar"); let data = VrfSignData::new_unchecked(b"mydata", &[b"tdata"], [i1.clone(), i2.clone()]); let signature = pair.vrf_sign(&data); let o10 = pair.make_bytes::<32>(b"ctx1", &i1); let o11 = signature.pre_outputs[0].make_bytes::<32>(b"ctx1", &i1); assert_eq!(o10, o11); let o20 = pair.make_bytes::<48>(b"ctx2", &i2); let o21 = signature.pre_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"dom1", b"foo"); let i2 = VrfInput::new(b"dom2", b"bar"); let data = VrfSignData::new_unchecked(b"mydata", &[b"tdata"], [i1.clone(), i2.clone()]); let expected = pair.vrf_sign(&data); let bytes = expected.encode(); let expected_len = data.inputs.len() * PREOUT_SERIALIZED_SIZE + SIGNATURE_SERIALIZED_SIZE + 1; assert_eq!(bytes.len(), expected_len); let decoded = VrfSignature::decode(&mut bytes.as_slice()).unwrap(); assert_eq!(expected, decoded); let data = VrfSignData::new_unchecked(b"mydata", &[b"tdata"], []); let expected = pair.vrf_sign(&data); let bytes = expected.encode(); let decoded = VrfSignature::decode(&mut bytes.as_slice()).unwrap(); assert_eq!(expected, decoded); } #[test] fn ring_vrf_sign_verify() { let ring_ctx = TestRingContext::new_testing(); let mut pks: Vec<_> = (0..16).map(|i| Pair::from_seed(&[i as u8; 32]).public()).collect(); assert!(pks.len() <= ring_ctx.max_keyset_size()); let pair = Pair::from_seed(DEV_SEED); // Just pick one index to patch with the actual public key let prover_idx = 3; pks[prover_idx] = pair.public(); let i1 = VrfInput::new(b"dom1", b"foo"); let i2 = VrfInput::new(b"dom2", b"bar"); let i3 = VrfInput::new(b"dom3", b"baz"); let data = VrfSignData::new_unchecked(b"mydata", &[b"tdata"], [i1, i2, i3]); let prover = ring_ctx.prover(&pks, prover_idx).unwrap(); let signature = pair.ring_vrf_sign(&data, &prover); let verifier = ring_ctx.verifier(&pks).unwrap(); assert!(signature.ring_vrf_verify(&data, &verifier)); } #[test] fn ring_vrf_sign_verify_with_out_of_ring_key() { let ring_ctx = TestRingContext::new_testing(); let pks: Vec<_> = (0..16).map(|i| Pair::from_seed(&[i as u8; 32]).public()).collect(); let pair = Pair::from_seed(DEV_SEED); // Just pick one index to patch with the actual public key let i1 = VrfInput::new(b"dom1", b"foo"); let data = VrfSignData::new_unchecked(b"mydata", Some(b"tdata"), Some(i1)); // pair.public != pks[0] let prover = ring_ctx.prover(&pks, 0).unwrap(); let signature = pair.ring_vrf_sign(&data, &prover); let verifier = ring_ctx.verifier(&pks).unwrap(); assert!(!signature.ring_vrf_verify(&data, &verifier)); } #[test] fn ring_vrf_make_bytes_matches() { let ring_ctx = TestRingContext::new_testing(); let mut pks: Vec<_> = (0..16).map(|i| Pair::from_seed(&[i as u8; 32]).public()).collect(); assert!(pks.len() <= ring_ctx.max_keyset_size()); let pair = Pair::from_seed(DEV_SEED); // Just pick one index to patch with the actual public key let prover_idx = 3; pks[prover_idx] = pair.public(); let i1 = VrfInput::new(b"dom1", b"foo"); let i2 = VrfInput::new(b"dom2", b"bar"); let data = VrfSignData::new_unchecked(b"mydata", &[b"tdata"], [i1.clone(), i2.clone()]); let prover = ring_ctx.prover(&pks, prover_idx).unwrap(); let signature = pair.ring_vrf_sign(&data, &prover); let o10 = pair.make_bytes::<32>(b"ctx1", &i1); let o11 = signature.pre_outputs[0].make_bytes::<32>(b"ctx1", &i1); assert_eq!(o10, o11); let o20 = pair.make_bytes::<48>(b"ctx2", &i2); let o21 = signature.pre_outputs[1].make_bytes::<48>(b"ctx2", &i2); assert_eq!(o20, o21); } #[test] fn encode_decode_ring_vrf_signature() { let ring_ctx = TestRingContext::new_testing(); let mut pks: Vec<_> = (0..16).map(|i| Pair::from_seed(&[i as u8; 32]).public()).collect(); assert!(pks.len() <= ring_ctx.max_keyset_size()); let pair = Pair::from_seed(DEV_SEED); // Just pick one... let prover_idx = 3; pks[prover_idx] = pair.public(); let i1 = VrfInput::new(b"dom1", b"foo"); let i2 = VrfInput::new(b"dom2", b"bar"); let i3 = VrfInput::new(b"dom3", b"baz"); let data = VrfSignData::new_unchecked(b"mydata", &[b"tdata"], [i1, i2, i3]); let prover = ring_ctx.prover(&pks, prover_idx).unwrap(); let expected = pair.ring_vrf_sign(&data, &prover); let bytes = expected.encode(); let expected_len = data.inputs.len() * PREOUT_SERIALIZED_SIZE + RING_SIGNATURE_SERIALIZED_SIZE + 1; assert_eq!(bytes.len(), expected_len); let decoded = RingVrfSignature::decode(&mut bytes.as_slice()).unwrap(); assert_eq!(expected, decoded); } #[test] fn encode_decode_ring_vrf_context() { let ctx1 = TestRingContext::new_testing(); let enc1 = ctx1.encode(); let _ti = ::type_info(); assert_eq!(enc1.len(), ring_context_serialized_size(TEST_DOMAIN_SIZE)); assert_eq!(enc1.len(), TestRingContext::max_encoded_len()); let ctx2 = TestRingContext::decode(&mut enc1.as_slice()).unwrap(); let enc2 = ctx2.encode(); assert_eq!(enc1, enc2); } #[test] fn encode_decode_verifier_data() { let ring_ctx = TestRingContext::new_testing(); let pks: Vec<_> = (0..16).map(|i| Pair::from_seed(&[i as u8; 32]).public()).collect(); assert!(pks.len() <= ring_ctx.max_keyset_size()); let verifier_data = ring_ctx.verifier_data(&pks).unwrap(); let enc1 = verifier_data.encode(); assert_eq!(enc1.len(), RING_VERIFIER_DATA_SERIALIZED_SIZE); assert_eq!(RingVerifierData::max_encoded_len(), RING_VERIFIER_DATA_SERIALIZED_SIZE); let vd2 = RingVerifierData::decode(&mut enc1.as_slice()).unwrap(); let enc2 = vd2.encode(); assert_eq!(enc1, enc2); } }