diff --git a/client/consensus/babe/src/authorship.rs b/client/consensus/babe/src/authorship.rs index dc7cd10867745..758d5321a94c5 100644 --- a/client/consensus/babe/src/authorship.rs +++ b/client/consensus/babe/src/authorship.rs @@ -24,7 +24,7 @@ use sc_consensus_epochs::Epoch as EpochT; use sp_application_crypto::AppCrypto; use sp_consensus_babe::{ digests::{PreDigest, PrimaryPreDigest, SecondaryPlainPreDigest, SecondaryVRFPreDigest}, - make_transcript, AuthorityId, BabeAuthorityWeight, Randomness, Slot, + make_vrf_sign_data, AuthorityId, BabeAuthorityWeight, Randomness, Slot, }; use sp_core::{ blake2_256, @@ -148,9 +148,9 @@ fn claim_secondary_slot( for (authority_id, authority_index) in keys { if authority_id == expected_author { let pre_digest = if author_secondary_vrf { - let transcript = make_transcript(randomness, slot, epoch_index); + let data = make_vrf_sign_data(randomness, slot, epoch_index); let result = - keystore.sr25519_vrf_sign(AuthorityId::ID, authority_id.as_ref(), &transcript); + keystore.sr25519_vrf_sign(AuthorityId::ID, authority_id.as_ref(), &data); if let Ok(Some(vrf_signature)) = result { Some(PreDigest::SecondaryVRF(SecondaryVRFPreDigest { slot, @@ -239,18 +239,18 @@ fn claim_primary_slot( epoch_index = epoch.clone_for_slot(slot).epoch_index; } - let transcript = make_transcript(randomness, slot, epoch_index); + let data = make_vrf_sign_data(randomness, slot, epoch_index); for (authority_id, authority_index) in keys { - let result = keystore.sr25519_vrf_sign(AuthorityId::ID, authority_id.as_ref(), &transcript); + let result = keystore.sr25519_vrf_sign(AuthorityId::ID, authority_id.as_ref(), &data); if let Ok(Some(vrf_signature)) = result { let threshold = calculate_primary_threshold(c, authorities, *authority_index); let can_claim = authority_id .as_inner_ref() - .make_bytes::<[u8; AUTHORING_SCORE_LENGTH]>( + .make_bytes::( AUTHORING_SCORE_VRF_CONTEXT, - &transcript, + &data.as_ref(), &vrf_signature.output, ) .map(|bytes| u128::from_le_bytes(bytes) < threshold) diff --git a/client/consensus/babe/src/tests.rs b/client/consensus/babe/src/tests.rs index 740ce63676374..aa3e32fa09d0a 100644 --- a/client/consensus/babe/src/tests.rs +++ b/client/consensus/babe/src/tests.rs @@ -29,7 +29,7 @@ use sc_network_test::{Block as TestBlock, *}; use sp_application_crypto::key_types::BABE; use sp_consensus::{DisableProofRecording, NoNetwork as DummyOracle, Proposal}; use sp_consensus_babe::{ - inherents::InherentDataProvider, make_transcript, AllowedSlots, AuthorityId, AuthorityPair, + inherents::InherentDataProvider, make_vrf_sign_data, AllowedSlots, AuthorityId, AuthorityPair, Slot, }; use sp_consensus_slots::SlotDuration; @@ -630,11 +630,8 @@ fn claim_vrf_check() { PreDigest::Primary(d) => d, v => panic!("Unexpected pre-digest variant {:?}", v), }; - let transcript = make_transcript(&epoch.randomness.clone(), 0.into(), epoch.epoch_index); - let sign = keystore - .sr25519_vrf_sign(AuthorityId::ID, &public, &transcript) - .unwrap() - .unwrap(); + let data = make_vrf_sign_data(&epoch.randomness.clone(), 0.into(), epoch.epoch_index); + let sign = keystore.sr25519_vrf_sign(AuthorityId::ID, &public, &data).unwrap().unwrap(); assert_eq!(pre_digest.vrf_signature.output, sign.output); // We expect a SecondaryVRF claim for slot 1 @@ -642,11 +639,8 @@ fn claim_vrf_check() { PreDigest::SecondaryVRF(d) => d, v => panic!("Unexpected pre-digest variant {:?}", v), }; - let transcript = make_transcript(&epoch.randomness.clone(), 1.into(), epoch.epoch_index); - let sign = keystore - .sr25519_vrf_sign(AuthorityId::ID, &public, &transcript) - .unwrap() - .unwrap(); + let data = make_vrf_sign_data(&epoch.randomness.clone(), 1.into(), epoch.epoch_index); + let sign = keystore.sr25519_vrf_sign(AuthorityId::ID, &public, &data).unwrap().unwrap(); assert_eq!(pre_digest.vrf_signature.output, sign.output); // Check that correct epoch index has been used if epochs are skipped (primary VRF) @@ -656,11 +650,8 @@ fn claim_vrf_check() { v => panic!("Unexpected claim variant {:?}", v), }; let fixed_epoch = epoch.clone_for_slot(slot); - let transcript = make_transcript(&epoch.randomness.clone(), slot, fixed_epoch.epoch_index); - let sign = keystore - .sr25519_vrf_sign(AuthorityId::ID, &public, &transcript) - .unwrap() - .unwrap(); + let data = make_vrf_sign_data(&epoch.randomness.clone(), slot, fixed_epoch.epoch_index); + let sign = keystore.sr25519_vrf_sign(AuthorityId::ID, &public, &data).unwrap().unwrap(); assert_eq!(fixed_epoch.epoch_index, 11); assert_eq!(claim.vrf_signature.output, sign.output); @@ -671,11 +662,8 @@ fn claim_vrf_check() { v => panic!("Unexpected claim variant {:?}", v), }; let fixed_epoch = epoch.clone_for_slot(slot); - let transcript = make_transcript(&epoch.randomness.clone(), slot, fixed_epoch.epoch_index); - let sign = keystore - .sr25519_vrf_sign(AuthorityId::ID, &public, &transcript) - .unwrap() - .unwrap(); + let data = make_vrf_sign_data(&epoch.randomness.clone(), slot, fixed_epoch.epoch_index); + let sign = keystore.sr25519_vrf_sign(AuthorityId::ID, &public, &data).unwrap().unwrap(); assert_eq!(fixed_epoch.epoch_index, 11); assert_eq!(pre_digest.vrf_signature.output, sign.output); } diff --git a/client/consensus/babe/src/verification.rs b/client/consensus/babe/src/verification.rs index cadceb6a57510..3de5eacc2c519 100644 --- a/client/consensus/babe/src/verification.rs +++ b/client/consensus/babe/src/verification.rs @@ -30,11 +30,11 @@ use sp_consensus_babe::{ CompatibleDigestItem, PreDigest, PrimaryPreDigest, SecondaryPlainPreDigest, SecondaryVRFPreDigest, }, - make_transcript, AuthorityId, AuthorityPair, AuthoritySignature, + make_vrf_sign_data, AuthorityId, AuthorityPair, AuthoritySignature, }; use sp_consensus_slots::Slot; use sp_core::{ - crypto::{VrfVerifier, Wraps}, + crypto::{VrfPublic, Wraps}, Pair, }; use sp_runtime::{traits::Header, DigestItem}; @@ -171,9 +171,9 @@ fn check_primary_header( return Err(babe_err(Error::BadSignature(pre_hash))) } - let transcript = make_transcript(&epoch.randomness, pre_digest.slot, epoch_index); + let data = make_vrf_sign_data(&epoch.randomness, pre_digest.slot, epoch_index); - if !authority_id.as_inner_ref().vrf_verify(&transcript, &pre_digest.vrf_signature) { + if !authority_id.as_inner_ref().vrf_verify(&data, &pre_digest.vrf_signature) { return Err(babe_err(Error::VrfVerificationFailed)) } @@ -182,9 +182,9 @@ fn check_primary_header( let score = authority_id .as_inner_ref() - .make_bytes::<[u8; AUTHORING_SCORE_LENGTH]>( + .make_bytes::( AUTHORING_SCORE_VRF_CONTEXT, - &transcript, + &data.as_ref(), &pre_digest.vrf_signature.output, ) .map(u128::from_le_bytes) @@ -253,9 +253,9 @@ fn check_secondary_vrf_header( return Err(Error::BadSignature(pre_hash)) } - let transcript = make_transcript(&epoch.randomness, pre_digest.slot, epoch_index); + let data = make_vrf_sign_data(&epoch.randomness, pre_digest.slot, epoch_index); - if !author.as_inner_ref().vrf_verify(&transcript, &pre_digest.vrf_signature) { + if !author.as_inner_ref().vrf_verify(&data, &pre_digest.vrf_signature) { return Err(Error::VrfVerificationFailed) } diff --git a/client/keystore/src/local.rs b/client/keystore/src/local.rs index 3551623f332a2..1e785113a4f03 100644 --- a/client/keystore/src/local.rs +++ b/client/keystore/src/local.rs @@ -20,7 +20,7 @@ use parking_lot::RwLock; use sp_application_crypto::{AppCrypto, AppPair, IsWrappedBy}; use sp_core::{ - crypto::{ByteArray, ExposeSecret, KeyTypeId, Pair as CorePair, SecretString, VrfSigner}, + crypto::{ByteArray, ExposeSecret, KeyTypeId, Pair as CorePair, SecretString, VrfSecret}, ecdsa, ed25519, sr25519, }; use sp_keystore::{Error as TraitError, Keystore, KeystorePtr}; @@ -98,19 +98,33 @@ impl LocalKeystore { Ok(signature) } - fn vrf_sign( + fn vrf_sign( &self, key_type: KeyTypeId, public: &T::Public, - transcript: &T::VrfInput, + data: &T::VrfSignData, ) -> std::result::Result, TraitError> { let sig = self .0 .read() .key_pair_by_type::(public, key_type)? - .map(|pair| pair.vrf_sign(transcript)); + .map(|pair| pair.vrf_sign(data)); Ok(sig) } + + fn vrf_output( + &self, + key_type: KeyTypeId, + public: &T::Public, + input: &T::VrfInput, + ) -> std::result::Result, TraitError> { + let preout = self + .0 + .read() + .key_pair_by_type::(public, key_type)? + .map(|pair| pair.vrf_output(input)); + Ok(preout) + } } impl Keystore for LocalKeystore { @@ -142,9 +156,18 @@ impl Keystore for LocalKeystore { &self, key_type: KeyTypeId, public: &sr25519::Public, - transcript: &sr25519::vrf::VrfTranscript, + data: &sr25519::vrf::VrfSignData, ) -> std::result::Result, TraitError> { - self.vrf_sign::(key_type, public, transcript) + self.vrf_sign::(key_type, public, data) + } + + fn sr25519_vrf_output( + &self, + key_type: KeyTypeId, + public: &sr25519::Public, + input: &sr25519::vrf::VrfInput, + ) -> std::result::Result, TraitError> { + self.vrf_output::(key_type, public, input) } fn ed25519_public_keys(&self, key_type: KeyTypeId) -> Vec { diff --git a/frame/babe/src/lib.rs b/frame/babe/src/lib.rs index e3ead424e7f6b..15580457c9810 100644 --- a/frame/babe/src/lib.rs +++ b/frame/babe/src/lib.rs @@ -357,12 +357,12 @@ pub mod pallet { ); } - if let Some(vrf_signature) = pre_digest.vrf_signature() { + if let Some(signature) = pre_digest.vrf_signature() { let randomness: Option = Authorities::::get() .get(authority_index as usize) .and_then(|(authority, _)| { let public = authority.as_inner_ref(); - let transcript = sp_consensus_babe::make_transcript( + let transcript = sp_consensus_babe::make_vrf_transcript( &Self::randomness(), CurrentSlot::::get(), EpochIndex::::get(), @@ -372,16 +372,12 @@ pub mod pallet { // execution. We don't run the verification again here to avoid slowing // down the runtime. debug_assert!({ - use sp_core::crypto::VrfVerifier; - public.vrf_verify(&transcript, &vrf_signature) + use sp_core::crypto::VrfPublic; + public.vrf_verify(&transcript.clone().into_sign_data(), &signature) }); public - .make_bytes( - RANDOMNESS_VRF_CONTEXT, - &transcript, - &vrf_signature.output, - ) + .make_bytes(RANDOMNESS_VRF_CONTEXT, &transcript, &signature.output) .ok() }); diff --git a/frame/babe/src/mock.rs b/frame/babe/src/mock.rs index bccdb42c71ac3..96ebd818bce2c 100644 --- a/frame/babe/src/mock.rs +++ b/frame/babe/src/mock.rs @@ -27,7 +27,7 @@ use frame_support::{ use pallet_session::historical as pallet_session_historical; use sp_consensus_babe::{AuthorityId, AuthorityPair, Randomness, Slot, VrfSignature}; use sp_core::{ - crypto::{KeyTypeId, Pair, VrfSigner}, + crypto::{KeyTypeId, Pair, VrfSecret}, H256, U256, }; use sp_io; @@ -314,17 +314,16 @@ pub fn make_secondary_vrf_pre_digest( Digest { logs: vec![log] } } -pub fn make_vrf_output( +pub fn make_vrf_signature_and_randomness( slot: Slot, pair: &sp_consensus_babe::AuthorityPair, ) -> (VrfSignature, Randomness) { - let transcript = sp_consensus_babe::make_transcript(&Babe::randomness(), slot, 0); + let transcript = sp_consensus_babe::make_vrf_transcript(&Babe::randomness(), slot, 0); - let signature = pair.as_ref().vrf_sign(&transcript); + let randomness = + pair.as_ref().make_bytes(sp_consensus_babe::RANDOMNESS_VRF_CONTEXT, &transcript); - let randomness = pair - .as_ref() - .make_bytes::(sp_consensus_babe::RANDOMNESS_VRF_CONTEXT, &transcript); + let signature = pair.as_ref().vrf_sign(&transcript.into()); (signature, randomness) } diff --git a/frame/babe/src/tests.rs b/frame/babe/src/tests.rs index 8a9aa6fcc03d2..38edc2af7f272 100644 --- a/frame/babe/src/tests.rs +++ b/frame/babe/src/tests.rs @@ -63,7 +63,8 @@ fn first_block_epoch_zero_start() { ext.execute_with(|| { let genesis_slot = Slot::from(100); - let (vrf_signature, vrf_randomness) = make_vrf_output(genesis_slot, &pairs[0]); + let (vrf_signature, vrf_randomness) = + make_vrf_signature_and_randomness(genesis_slot, &pairs[0]); let pre_digest = make_primary_pre_digest(0, genesis_slot, vrf_signature); @@ -111,7 +112,8 @@ fn current_slot_is_processed_on_initialization() { ext.execute_with(|| { let genesis_slot = Slot::from(10); - let (vrf_signature, vrf_randomness) = make_vrf_output(genesis_slot, &pairs[0]); + let (vrf_signature, vrf_randomness) = + make_vrf_signature_and_randomness(genesis_slot, &pairs[0]); let pre_digest = make_primary_pre_digest(0, genesis_slot, vrf_signature); System::reset_events(); @@ -140,7 +142,8 @@ where ext.execute_with(|| { let genesis_slot = Slot::from(10); - let (vrf_signature, vrf_randomness) = make_vrf_output(genesis_slot, &pairs[0]); + let (vrf_signature, vrf_randomness) = + make_vrf_signature_and_randomness(genesis_slot, &pairs[0]); let pre_digest = make_pre_digest(0, genesis_slot, vrf_signature); System::reset_events(); diff --git a/primitives/consensus/babe/src/lib.rs b/primitives/consensus/babe/src/lib.rs index 37cf0c3f0b677..dc161525a8511 100644 --- a/primitives/consensus/babe/src/lib.rs +++ b/primitives/consensus/babe/src/lib.rs @@ -32,7 +32,9 @@ use sp_std::vec::Vec; use crate::digests::{NextConfigDescriptor, NextEpochDescriptor}; -pub use sp_core::sr25519::vrf::{VrfOutput, VrfProof, VrfSignature, VrfTranscript}; +pub use sp_core::sr25519::vrf::{ + VrfInput, VrfOutput, VrfProof, VrfSignData, VrfSignature, VrfTranscript, +}; /// Key type for BABE module. pub const KEY_TYPE: sp_core::crypto::KeyTypeId = sp_application_crypto::key_types::BABE; @@ -94,9 +96,9 @@ pub type BabeAuthorityWeight = u64; /// of 0 (regardless of whether they are plain or vrf secondary blocks). pub type BabeBlockWeight = u32; -/// Make a VRF transcript data container -pub fn make_transcript(randomness: &Randomness, slot: Slot, epoch: u64) -> VrfTranscript { - VrfTranscript::new( +/// Make VRF input suitable for BABE's randomness generation. +pub fn make_vrf_transcript(randomness: &Randomness, slot: Slot, epoch: u64) -> VrfInput { + VrfInput::new( &BABE_ENGINE_ID, &[ (b"slot number", &slot.to_le_bytes()), @@ -106,6 +108,11 @@ pub fn make_transcript(randomness: &Randomness, slot: Slot, epoch: u64) -> VrfTr ) } +/// Make VRF signing data suitable for BABE's protocol. +pub fn make_vrf_sign_data(randomness: &Randomness, slot: Slot, epoch: u64) -> VrfSignData { + make_vrf_transcript(randomness, slot, epoch).into() +} + /// An consensus log item for BABE. #[derive(Decode, Encode, Clone, PartialEq, Eq)] pub enum ConsensusLog { diff --git a/primitives/core/src/crypto.rs b/primitives/core/src/crypto.rs index 9d63cbc518936..27b24057b0e80 100644 --- a/primitives/core/src/crypto.rs +++ b/primitives/core/src/crypto.rs @@ -1094,23 +1094,29 @@ impl<'a> TryFrom<&'a str> for KeyTypeId { /// Trait grouping types shared by a VRF signer and verifiers. pub trait VrfCrypto { - /// Associated signature type. - type VrfSignature; - - /// Vrf input data. Generally some form of transcript. + /// VRF input. type VrfInput; + /// VRF output. + type VrfOutput; + /// VRF signing data. + type VrfSignData; + /// VRF signature. + type VrfSignature; } -/// VRF Signer. -pub trait VrfSigner: VrfCrypto { - /// Sign input data. - fn vrf_sign(&self, data: &Self::VrfInput) -> Self::VrfSignature; +/// VRF Secret Key. +pub trait VrfSecret: VrfCrypto { + /// Get VRF-specific output . + fn vrf_output(&self, data: &Self::VrfInput) -> Self::VrfOutput; + + /// Sign VRF-specific data. + fn vrf_sign(&self, input: &Self::VrfSignData) -> Self::VrfSignature; } -/// VRF Verifier. -pub trait VrfVerifier: VrfCrypto { +/// VRF Public Key. +pub trait VrfPublic: VrfCrypto { /// Verify input data signature. - fn vrf_verify(&self, data: &Self::VrfInput, signature: &Self::VrfSignature) -> bool; + fn vrf_verify(&self, data: &Self::VrfSignData, signature: &Self::VrfSignature) -> bool; } /// An identifier for a specific cryptographic algorithm used by a key pair diff --git a/primitives/core/src/sr25519.rs b/primitives/core/src/sr25519.rs index 9b1c82205de10..fead9e2a09df3 100644 --- a/primitives/core/src/sr25519.rs +++ b/primitives/core/src/sr25519.rs @@ -537,32 +537,86 @@ impl CryptoType for Pair { pub mod vrf { use super::*; #[cfg(feature = "full_crypto")] - use crate::crypto::VrfSigner; - use crate::crypto::{VrfCrypto, VrfVerifier}; + use crate::crypto::VrfSecret; + use crate::crypto::{VrfCrypto, VrfPublic}; use schnorrkel::{ errors::MultiSignatureStage, vrf::{VRF_OUTPUT_LENGTH, VRF_PROOF_LENGTH}, SignatureError, }; - /// VRF transcript ready to be used for VRF sign/verify operations. + const DEFAULT_EXTRA_DATA_LABEL: &[u8] = b"VRF"; + + /// Transcript ready to be used for VRF related operations. + #[derive(Clone)] pub struct VrfTranscript(pub merlin::Transcript); impl VrfTranscript { - /// Build a new transcript ready to be used by a VRF signer/verifier. + /// Build a new transcript instance. + /// + /// Each `data` element is a tuple `(domain, message)` composing the transcipt. pub fn new(label: &'static [u8], data: &[(&'static [u8], &[u8])]) -> Self { let mut transcript = merlin::Transcript::new(label); data.iter().for_each(|(l, b)| transcript.append_message(l, b)); VrfTranscript(transcript) } + + /// Map transcript to `VrfSignData`. + pub fn into_sign_data(self) -> VrfSignData { + self.into() + } + } + + /// VRF input. + /// + /// Technically a transcript used by the Fiat-Shamir transform. + pub type VrfInput = VrfTranscript; + + /// VRF input ready to be used for VRF sign and verify operations. + #[derive(Clone)] + pub struct VrfSignData { + /// Transcript data contributing to VRF output. + pub(super) transcript: VrfTranscript, + /// Extra transcript data to be signed by the VRF. + pub(super) extra: Option, + } + + impl From for VrfSignData { + fn from(transcript: VrfInput) -> Self { + VrfSignData { transcript, extra: None } + } + } + + // Get a reference to the inner VRF input. + impl AsRef for VrfSignData { + fn as_ref(&self) -> &VrfInput { + &self.transcript + } + } + + impl VrfSignData { + /// Build a new instance ready to be used for VRF signer and verifier. + /// + /// `input` will contribute to the VRF output bytes. + pub fn new(input: VrfTranscript) -> Self { + input.into() + } + + /// Add some extra data to be signed. + /// + /// `extra` will not contribute to the VRF output bytes. + pub fn with_extra(mut self, extra: VrfTranscript) -> Self { + self.extra = Some(extra); + self + } } /// VRF signature data #[derive(Clone, Debug, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo)] pub struct VrfSignature { - /// The initial VRF configuration + /// VRF output. pub output: VrfOutput, - /// The calculated VRF proof + /// VRF proof. pub proof: VrfProof, } @@ -630,30 +684,58 @@ pub mod vrf { #[cfg(feature = "full_crypto")] impl VrfCrypto for Pair { - type VrfSignature = VrfSignature; type VrfInput = VrfTranscript; + type VrfOutput = VrfOutput; + type VrfSignData = VrfSignData; + type VrfSignature = VrfSignature; } #[cfg(feature = "full_crypto")] - impl VrfSigner for Pair { - fn vrf_sign(&self, transcript: &Self::VrfInput) -> Self::VrfSignature { - let (inout, proof, _) = self.0.vrf_sign(transcript.0.clone()); + impl VrfSecret for Pair { + fn vrf_sign(&self, data: &Self::VrfSignData) -> Self::VrfSignature { + let inout = self.0.vrf_create_hash(data.transcript.0.clone()); + + let extra = data + .extra + .as_ref() + .map(|e| e.0.clone()) + .unwrap_or_else(|| merlin::Transcript::new(DEFAULT_EXTRA_DATA_LABEL)); + + let proof = self.0.dleq_proove(extra, &inout, true).0; + VrfSignature { output: VrfOutput(inout.to_output()), proof: VrfProof(proof) } } + + fn vrf_output(&self, input: &Self::VrfInput) -> Self::VrfOutput { + let output = self.0.vrf_create_hash(input.0.clone()).to_output(); + VrfOutput(output) + } } impl VrfCrypto for Public { - type VrfSignature = VrfSignature; type VrfInput = VrfTranscript; + type VrfOutput = VrfOutput; + type VrfSignData = VrfSignData; + type VrfSignature = VrfSignature; } - impl VrfVerifier for Public { - fn vrf_verify(&self, transcript: &Self::VrfInput, signature: &Self::VrfSignature) -> bool { - schnorrkel::PublicKey::from_bytes(self) - .and_then(|public| { - public.vrf_verify(transcript.0.clone(), &signature.output.0, &signature.proof.0) - }) - .is_ok() + impl VrfPublic for Public { + fn vrf_verify(&self, data: &Self::VrfSignData, signature: &Self::VrfSignature) -> bool { + let do_verify = || { + let public = schnorrkel::PublicKey::from_bytes(self)?; + + let inout = + signature.output.0.attach_input_hash(&public, data.transcript.0.clone())?; + + let extra = data + .extra + .as_ref() + .map(|e| e.0.clone()) + .unwrap_or_else(|| merlin::Transcript::new(DEFAULT_EXTRA_DATA_LABEL)); + + public.dleq_verify(extra, &inout, &signature.proof.0, true) + }; + do_verify().is_ok() } } @@ -690,39 +772,54 @@ 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 { - let inout = self.0.vrf_create_hash(transcript.0.clone()); - inout.make_bytes::(context) + /// Generate output bytes from the given VRF configuration. + pub fn make_bytes(&self, context: &[u8], input: &VrfInput) -> [u8; N] + where + [u8; N]: Default, + { + let inout = self.0.vrf_create_hash(input.0.clone()); + inout.make_bytes::<[u8; N]>(context) } } impl Public { - /// Generate bytes from the given VRF configuration. - pub fn make_bytes>( + /// Generate output bytes from the given VRF configuration. + pub fn make_bytes( &self, context: &[u8], - transcript: &VrfTranscript, + input: &VrfInput, output: &VrfOutput, - ) -> Result { + ) -> Result<[u8; N], codec::Error> + where + [u8; N]: Default, + { 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)) + let inout = + output.0.attach_input_hash(&pubkey, input.0.clone()).map_err(convert_error)?; + Ok(inout.make_bytes::<[u8; N]>(context)) + } + } + + impl VrfOutput { + /// Generate output bytes from the given VRF configuration. + pub fn make_bytes( + &self, + context: &[u8], + input: &VrfInput, + public: &Public, + ) -> Result<[u8; N], codec::Error> + where + [u8; N]: Default, + { + public.make_bytes(context, input, self) } } } #[cfg(test)] mod tests { - use super::*; - use crate::crypto::{Ss58Codec, DEV_ADDRESS, DEV_PHRASE}; + use super::{vrf::*, *}; + use crate::crypto::{Ss58Codec, VrfPublic, VrfSecret, DEV_ADDRESS, DEV_PHRASE}; use serde_json; #[test] @@ -951,19 +1048,86 @@ mod tests { assert!(deserialize_signature("\"abc123\"").is_err()); } + #[test] + fn vrf_sign_verify() { + let pair = Pair::from_seed(b"12345678901234567890123456789012"); + let public = pair.public(); + + let data = VrfTranscript::new(b"label", &[(b"domain1", b"data1")]).into(); + + let signature = pair.vrf_sign(&data); + + assert!(public.vrf_verify(&data, &signature)); + } + + #[test] + fn vrf_sign_verify_with_extra() { + let pair = Pair::from_seed(b"12345678901234567890123456789012"); + let public = pair.public(); + + let extra = VrfTranscript::new(b"extra", &[(b"domain2", b"data2")]); + let data = VrfTranscript::new(b"label", &[(b"domain1", b"data1")]) + .into_sign_data() + .with_extra(extra); + + let signature = pair.vrf_sign(&data); + + assert!(public.vrf_verify(&data, &signature)); + } + #[test] fn vrf_make_bytes_matches() { - use super::vrf::*; - use crate::crypto::VrfSigner; let pair = Pair::from_seed(b"12345678901234567890123456789012"); let public = pair.public(); - let transcript = VrfTranscript::new(b"test", &[(b"foo", b"bar")]); + let ctx = b"vrfbytes"; + + let input = VrfTranscript::new(b"label", &[(b"domain1", b"data1")]); - let signature = pair.vrf_sign(&transcript); + let output = pair.vrf_output(&input); - let ctx = b"randbytes"; - let b1 = pair.make_bytes::<[u8; 32]>(ctx, &transcript); - let b2 = public.make_bytes::<[u8; 32]>(ctx, &transcript, &signature.output).unwrap(); - assert_eq!(b1, b2); + let out1 = pair.make_bytes::<32>(ctx, &input); + let out2 = output.make_bytes::<32>(ctx, &input, &public).unwrap(); + assert_eq!(out1, out2); + + let extra = VrfTranscript::new(b"extra", &[(b"domain2", b"data2")]); + let data = input.clone().into_sign_data().with_extra(extra); + let signature = pair.vrf_sign(&data); + assert!(public.vrf_verify(&data, &signature)); + + let out3 = public.make_bytes::<32>(ctx, &input, &signature.output).unwrap(); + assert_eq!(out2, out3); + } + + #[test] + fn vrf_backend_compat() { + let pair = Pair::from_seed(b"12345678901234567890123456789012"); + let public = pair.public(); + let ctx = b"vrfbytes"; + + let input = VrfInput::new(b"label", &[(b"domain1", b"data1")]); + let extra = VrfTranscript::new(b"extra", &[(b"domain2", b"data2")]); + + let data = input.clone().into_sign_data().with_extra(extra.clone()); + let signature = pair.vrf_sign(&data); + assert!(public.vrf_verify(&data, &signature)); + + let out1 = pair.make_bytes::<32>(ctx, &input); + let out2 = public.make_bytes::<32>(ctx, &input, &signature.output).unwrap(); + assert_eq!(out1, out2); + + // Direct call to backend version of sign after check with extra params + let (inout, proof, _) = pair + .0 + .vrf_sign_extra_after_check(input.0.clone(), |inout| { + let out3 = inout.make_bytes::<[u8; 32]>(ctx); + assert_eq!(out2, out3); + Some(extra.0.clone()) + }) + .unwrap(); + let signature2 = + VrfSignature { output: VrfOutput(inout.to_output()), proof: VrfProof(proof) }; + + assert!(public.vrf_verify(&data, &signature2)); + assert_eq!(signature.output, signature2.output); } } diff --git a/primitives/keystore/src/lib.rs b/primitives/keystore/src/lib.rs index 5b41f3b80043d..d6165088f6b46 100644 --- a/primitives/keystore/src/lib.rs +++ b/primitives/keystore/src/lib.rs @@ -72,23 +72,34 @@ pub trait Keystore: Send + Sync { msg: &[u8], ) -> Result, Error>; - /// Generate an sr25519 VRF signature for a given transcript data. + /// Generate an sr25519 VRF signature for the given data. /// /// Receives [`KeyTypeId`] and an [`sr25519::Public`] key to be able to map /// them to a private key that exists in the keystore. /// - /// Returns a result containing the signature data. - /// Namely, VRFOutput and VRFProof which are returned inside the `VRFSignature` - /// container struct. /// Returns `None` if the given `key_type` and `public` combination doesn't /// exist in the keystore or an `Err` when something failed. fn sr25519_vrf_sign( &self, key_type: KeyTypeId, public: &sr25519::Public, - transcript: &sr25519::vrf::VrfTranscript, + data: &sr25519::vrf::VrfSignData, ) -> Result, Error>; + /// Generate an sr25519 VRF output for a given input data. + /// + /// Receives [`KeyTypeId`] and an [`sr25519::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 sr25519_vrf_output( + &self, + key_type: KeyTypeId, + public: &sr25519::Public, + input: &sr25519::vrf::VrfInput, + ) -> Result, Error>; + /// Returns all ed25519 public keys for the given key type. fn ed25519_public_keys(&self, key_type: KeyTypeId) -> Vec; diff --git a/primitives/keystore/src/testing.rs b/primitives/keystore/src/testing.rs index a6fcd6e26abe3..dd3254e9371c4 100644 --- a/primitives/keystore/src/testing.rs +++ b/primitives/keystore/src/testing.rs @@ -20,7 +20,7 @@ use crate::{Error, Keystore, KeystorePtr}; use sp_core::{ - crypto::{ByteArray, KeyTypeId, Pair, VrfSigner}, + crypto::{ByteArray, KeyTypeId, Pair, VrfSecret}, ecdsa, ed25519, sr25519, }; @@ -99,15 +99,25 @@ impl MemoryKeystore { Ok(sig) } - fn vrf_sign( + fn vrf_sign( &self, key_type: KeyTypeId, public: &T::Public, - transcript: &T::VrfInput, + data: &T::VrfSignData, ) -> Result, Error> { - let sig = self.pair::(key_type, public).map(|pair| pair.vrf_sign(transcript)); + let sig = self.pair::(key_type, public).map(|pair| pair.vrf_sign(data)); Ok(sig) } + + fn vrf_output( + &self, + key_type: KeyTypeId, + public: &T::Public, + input: &T::VrfInput, + ) -> Result, Error> { + let preout = self.pair::(key_type, public).map(|pair| pair.vrf_output(input)); + Ok(preout) + } } impl Keystore for MemoryKeystore { @@ -136,9 +146,18 @@ impl Keystore for MemoryKeystore { &self, key_type: KeyTypeId, public: &sr25519::Public, - transcript: &sr25519::vrf::VrfTranscript, + data: &sr25519::vrf::VrfSignData, ) -> Result, Error> { - self.vrf_sign::(key_type, public, transcript) + self.vrf_sign::(key_type, public, data) + } + + fn sr25519_vrf_output( + &self, + key_type: KeyTypeId, + public: &sr25519::Public, + input: &sr25519::vrf::VrfInput, + ) -> Result, Error> { + self.vrf_output::(key_type, public, input) } fn ed25519_public_keys(&self, key_type: KeyTypeId) -> Vec { @@ -267,27 +286,57 @@ mod tests { let secret_uri = "//Alice"; let key_pair = sr25519::Pair::from_string(secret_uri, None).expect("Generates key pair"); - let transcript = sr25519::vrf::VrfTranscript::new( + let data = sr25519::vrf::VrfInput::new( b"Test", &[ (b"one", &1_u64.to_le_bytes()), (b"two", &2_u64.to_le_bytes()), (b"three", "test".as_bytes()), ], - ); + ) + .into_sign_data(); - let result = store.sr25519_vrf_sign(SR25519, &key_pair.public(), &transcript); + let result = store.sr25519_vrf_sign(SR25519, &key_pair.public(), &data); assert!(result.unwrap().is_none()); store .insert(SR25519, secret_uri, key_pair.public().as_ref()) .expect("Inserts unknown key"); - let result = store.sr25519_vrf_sign(SR25519, &key_pair.public(), &transcript); + let result = store.sr25519_vrf_sign(SR25519, &key_pair.public(), &data); assert!(result.unwrap().is_some()); } + #[test] + fn vrf_output() { + let store = MemoryKeystore::new(); + + let secret_uri = "//Alice"; + let pair = sr25519::Pair::from_string(secret_uri, None).expect("Generates key pair"); + + let input = sr25519::vrf::VrfInput::new( + b"Test", + &[ + (b"one", &1_u64.to_le_bytes()), + (b"two", &2_u64.to_le_bytes()), + (b"three", "test".as_bytes()), + ], + ); + + let result = store.sr25519_vrf_output(SR25519, &pair.public(), &input); + assert!(result.unwrap().is_none()); + + store + .insert(SR25519, secret_uri, pair.public().as_ref()) + .expect("Inserts unknown key"); + + let preout = store.sr25519_vrf_output(SR25519, &pair.public(), &input).unwrap().unwrap(); + + let result = preout.make_bytes::<32>(b"rand", &input, &pair.public()); + assert!(result.is_ok()); + } + #[test] fn ecdsa_sign_prehashed_works() { let store = MemoryKeystore::new();