// 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 = "std")] use crate::crypto::Ss58Codec; use crate::crypto::{ ByteArray, CryptoType, CryptoTypeId, Derive, Public as TraitPublic, UncheckedFrom, VrfPublic, }; #[cfg(feature = "full_crypto")] use crate::crypto::{DeriveError, DeriveJunction, Pair as TraitPair, SecretStringError, VrfSecret}; use bandersnatch_vrfs::CanonicalSerialize; #[cfg(feature = "full_crypto")] use bandersnatch_vrfs::SecretKey; use codec::{Decode, Encode, EncodeLike, MaxEncodedLen}; use scale_info::TypeInfo; 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"band"); /// Context used to produce a plain signature without any VRF input/output. #[cfg(feature = "full_crypto")] pub const SIGNING_CTX: &[u8] = b"BandersnatchSigningContext"; // Max ring domain size. const RING_DOMAIN_SIZE: usize = 1024; #[cfg(feature = "full_crypto")] const SEED_SERIALIZED_LEN: usize = 32; // Short-Weierstrass form serialized sizes. const PUBLIC_SERIALIZED_LEN: usize = 33; const SIGNATURE_SERIALIZED_LEN: usize = 65; const RING_SIGNATURE_SERIALIZED_LEN: usize = 755; const PREOUT_SERIALIZED_LEN: usize = 33; // Max size of serialized ring-vrf context params. // // This size is dependent on the ring domain size and the actual value // is equal to the SCALE encoded size of the `KZG` backend. // // Some values: // ring_size → ~serialized_size // 512 → 74 KB // 1024 → 147 KB // 2048 → 295 KB // NOTE: This is quite big but looks like there is an upcoming fix // in the backend. const RING_CONTEXT_SERIALIZED_LEN: usize = 147748; /// Bandersnatch public key. #[cfg_attr(feature = "full_crypto", derive(Hash))] #[derive( Clone, Copy, PartialEq, Eq, PartialOrd, Ord, 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 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 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 {} 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.as_ref()), &s[0..8]) } #[cfg(not(feature = "std"))] fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { Ok(()) } } /// Bandersnatch signature. /// /// The signature is created via the [`VrfSecret::vrf_sign`] using [`SIGNING_CTX`] as transcript /// `label`. #[cfg_attr(feature = "full_crypto", derive(Hash))] #[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 { fn unchecked_from(raw: [u8; SIGNATURE_SERIALIZED_LEN]) -> Self { Signature(raw) } } 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 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; } 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 reconstruct the secret [`Pair`]. #[cfg(feature = "full_crypto")] type Seed = [u8; SEED_SERIALIZED_LEN]; /// Bandersnatch secret key. #[cfg(feature = "full_crypto")] #[derive(Clone)] pub struct Pair { secret: SecretKey, seed: Seed, } #[cfg(feature = "full_crypto")] impl Pair { /// Get the key seed. pub fn seed(&self) -> Seed { self.seed } } #[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 32 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 = [0; SEED_SERIALIZED_LEN]; 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_core_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_LEN]; 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. 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, 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() } } #[cfg(feature = "full_crypto")] 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/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 outputs. /// /// Can contain at most [`MAX_VRF_IOS`] elements. pub type VrfIosVec = BoundedVec>; /// VRF input to construct a [`VrfOutput`] 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 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("serialization length is constant and checked by test; qed"); bytes.encode() } } 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() } } /// 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 explaination 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 [`VrfOutput`]s. Both inputs and 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 `VrfOutput`s values. /// - *Vrf inputs* is some additional data which is used to produce *vrf outputs*. This data /// will contribute to the signature as well. #[derive(Clone)] pub struct VrfSignData { /// VRF inputs to be signed. pub inputs: VrfIosVec, /// Associated protocol transcript. pub transcript: Transcript, } 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 `outputs` generated from the /// [`VrfSignData::inputs`]. /// /// Refer to [`VrfSignData`] for more details. #[derive(Clone, Debug, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo)] pub struct VrfSignature { /// VRF (pre)outputs. pub outputs: VrfIosVec, /// Transcript signature. pub signature: Signature, } #[cfg(feature = "full_crypto")] impl VrfCrypto for Pair { type VrfInput = VrfInput; type VrfOutput = VrfOutput; 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_output(&self, input: &Self::VrfInput) -> Self::VrfOutput { let output = self.secret.vrf_preout(&input.0); VrfOutput(output) } } impl VrfCrypto for Public { type VrfInput = VrfInput; type VrfOutput = VrfOutput; 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 outputs_len = signature.outputs.len(); if outputs_len != data.inputs.len() { return false } // Workaround to overcome backend signature generic over the number of IOs. match 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| { let input = data.inputs[i].0.clone(); self.secret.vrf_inout(input) }); let thin_signature: ThinVrfSignature = self.secret.sign_thin_vrf(data.transcript.clone(), &ios); let outputs: Vec<_> = thin_signature.preouts.into_iter().map(VrfOutput).collect(); let outputs = VrfIosVec::truncate_from(outputs); let mut signature = VrfSignature { signature: Signature([0; SIGNATURE_SERIALIZED_LEN]), 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.clone()); inout.vrf_output_bytes(transcript) } } impl Public { fn vrf_verify_gen( &self, data: &VrfSignData, signature: &VrfSignature, ) -> bool { let Ok(public) = PublicKey::deserialize_compressed(self.as_slice()) else { return false }; let preouts: [bandersnatch_vrfs::VrfPreOut; N] = core::array::from_fn(|i| signature.outputs[i].0.clone()); // 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(signature.signature.as_ref()) .map(|s| s.proof) else { return false }; let signature = ThinVrfSignature { proof, preouts }; let inputs = data.inputs.iter().map(|i| i.0.clone()); public.verify_thin_vrf(data.transcript.clone(), inputs, &signature).is_ok() } } impl VrfOutput { /// 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.clone(), preoutput: self.0.clone() }; 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::{CanonicalDeserialize, PublicKey}; /// Context used to produce ring signatures. #[derive(Clone)] pub struct RingContext(KZG); impl RingContext { /// Build an dummy instance used for testing purposes. pub fn new_testing() -> Self { Self(KZG::testing_kzg_setup([0; 32], RING_DOMAIN_SIZE as u32)) } /// 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(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(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) } } impl Encode for RingContext { fn encode(&self) -> Vec { let mut buf = Box::new([0; RING_CONTEXT_SERIALIZED_LEN]); self.0 .serialize_compressed(buf.as_mut_slice()) .expect("serialization length is constant and checked by test; qed"); buf.encode() } } impl Decode for RingContext { fn decode(i: &mut R) -> Result { let buf = >::decode(i)?; let kzg = KZG::deserialize_compressed(buf.as_slice()).map_err(|_| "KZG decode error")?; Ok(RingContext(kzg)) } } impl EncodeLike for RingContext {} impl MaxEncodedLen for RingContext { fn max_encoded_len() -> usize { <[u8; RING_CONTEXT_SERIALIZED_LEN]>::max_encoded_len() } } impl TypeInfo for RingContext { type Identity = [u8; RING_CONTEXT_SERIALIZED_LEN]; fn type_info() -> scale_info::Type { Self::Identity::type_info() } } /// Ring VRF signature. #[derive(Clone, Debug, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo)] pub struct RingVrfSignature { /// VRF (pre)outputs. pub outputs: VrfIosVec, /// Ring signature. pub signature: [u8; RING_SIGNATURE_SERIALIZED_LEN], } #[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| { let input = data.inputs[i].0.clone(); self.secret.vrf_inout(input) }); let ring_signature: bandersnatch_vrfs::RingVrfSignature = bandersnatch_vrfs::RingProver { ring_prover: prover, secret: &self.secret } .sign_ring_vrf(data.transcript.clone(), &ios); let outputs: Vec<_> = ring_signature.preouts.into_iter().map(VrfOutput).collect(); let outputs = VrfIosVec::truncate_from(outputs); let mut signature = RingVrfSignature { outputs, signature: [0; RING_SIGNATURE_SERIALIZED_LEN] }; 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.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( self.signature.as_slice(), ) else { return false }; let preouts: [bandersnatch_vrfs::VrfPreOut; N] = core::array::from_fn(|i| self.outputs[i].0.clone()); let signature = bandersnatch_vrfs::RingVrfSignature { proof: vrf_signature.proof, preouts }; let inputs = data.inputs.iter().map(|i| i.0.clone()); 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_LEN] = &[0xcb; SEED_SERIALIZED_LEN]; #[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], RING_DOMAIN_SIZE as u32); assert_eq!(kzg.max_keyset_size(), RING_DOMAIN_SIZE - 257); assert_eq!(kzg.compressed_size(), RING_CONTEXT_SERIALIZED_LEN); 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_LEN); let input = VrfInput::new(b"foo", &[]); let preout = secret.vrf_preout(&input.0); assert_eq!(preout.compressed_size(), PREOUT_SERIALIZED_LEN); 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_LEN); 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_LEN); } #[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 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.outputs[0].make_bytes::<32>(b"ctx1", &i1); assert_eq!(o10, o11); let o20 = pair.make_bytes::<48>(b"ctx2", &i2); let o21 = signature.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_LEN + SIGNATURE_SERIALIZED_LEN + 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 = RingContext::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 = RingContext::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 = RingContext::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.outputs[0].make_bytes::<32>(b"ctx1", &i1); assert_eq!(o10, o11); let o20 = pair.make_bytes::<48>(b"ctx2", &i2); let o21 = signature.outputs[1].make_bytes::<48>(b"ctx2", &i2); assert_eq!(o20, o21); } #[test] fn encode_decode_ring_vrf_signature() { let ring_ctx = RingContext::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_LEN + RING_SIGNATURE_SERIALIZED_LEN + 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 = RingContext::new_testing(); let enc1 = ctx1.encode(); assert_eq!(enc1.len(), RingContext::max_encoded_len()); let ctx2 = RingContext::decode(&mut enc1.as_slice()).unwrap(); let enc2 = ctx2.encode(); assert_eq!(enc1, enc2); } }