From 377e7f2e075284f05fb3731e574c56b1c1c83f7a Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Thu, 27 Apr 2023 14:09:14 +0200 Subject: [PATCH 01/16] Allow extra signing data --- primitives/core/src/crypto.rs | 18 ++- primitives/core/src/sr25519.rs | 199 ++++++++++++++++++++++++++------- 2 files changed, 173 insertions(+), 44 deletions(-) diff --git a/primitives/core/src/crypto.rs b/primitives/core/src/crypto.rs index dd0d9e60529f2..f9145f4cbde06 100644 --- a/primitives/core/src/crypto.rs +++ b/primitives/core/src/crypto.rs @@ -1094,17 +1094,25 @@ impl<'a> TryFrom<&'a str> for KeyTypeId { /// Trait grouping types shared by a VRF signer and verifiers. pub trait VrfCrypto { + /// VRF input data. + type VrfInput; + /// VRF pre-output generated from input data. + type VrfPreOutput; /// Associated signature type. type VrfSignature; - - /// Vrf input data. Generally some form of transcript. - type VrfInput; } /// VRF Signer. pub trait VrfSigner: VrfCrypto { - /// Sign input data. - fn vrf_sign(&self, data: &Self::VrfInput) -> Self::VrfSignature; + /// Get VRF pre-output. + fn vrf_preout(&self, data: &Self::VrfInput) -> Self::VrfPreOutput; + + /// Sign input data with optional pre-computed pre-output. + fn vrf_sign( + &self, + input: &Self::VrfInput, + preout: Option, + ) -> Self::VrfSignature; } /// VRF Verifier. diff --git a/primitives/core/src/sr25519.rs b/primitives/core/src/sr25519.rs index 9b1c82205de10..c2b8d16a33304 100644 --- a/primitives/core/src/sr25519.rs +++ b/primitives/core/src/sr25519.rs @@ -545,51 +545,63 @@ pub mod vrf { SignatureError, }; - /// VRF transcript ready to be used for VRF sign/verify operations. - pub struct VrfTranscript(pub merlin::Transcript); + /// VRF input ready to be used for VRF sign and verify operations. + pub struct VrfInput { + /// VRF input data. + pub data: merlin::Transcript, + /// Extra non-input data signed by the VRF. Doesn't contribute to VRF output. + pub extra: Option, + } - impl VrfTranscript { - /// Build a new transcript ready to be used by a VRF signer/verifier. + impl VrfInput { + /// Build a new input ready to be used by a VRF signer/verifier. 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) + VrfInput { data: transcript, extra: None } + } + + pub fn extra(mut self, 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)); + self.extra = Some(transcript); + self } } /// VRF signature data #[derive(Clone, Debug, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo)] pub struct VrfSignature { - /// The initial VRF configuration - pub output: VrfOutput, - /// The calculated VRF proof + /// Initial pre-output configuration. + pub preout: VrfPreOutput, + /// Calculated proof. pub proof: VrfProof, } /// VRF output type suitable for schnorrkel operations. #[derive(Clone, Debug, PartialEq, Eq)] - pub struct VrfOutput(pub schnorrkel::vrf::VRFOutput); + pub struct VrfPreOutput(pub schnorrkel::vrf::VRFOutput); - impl Encode for VrfOutput { + impl Encode for VrfPreOutput { fn encode(&self) -> Vec { self.0.as_bytes().encode() } } - impl Decode for VrfOutput { + impl Decode for VrfPreOutput { fn decode(i: &mut R) -> Result { let decoded = <[u8; VRF_OUTPUT_LENGTH]>::decode(i)?; Ok(Self(schnorrkel::vrf::VRFOutput::from_bytes(&decoded).map_err(convert_error)?)) } } - impl MaxEncodedLen for VrfOutput { + impl MaxEncodedLen for VrfPreOutput { fn max_encoded_len() -> usize { <[u8; VRF_OUTPUT_LENGTH]>::max_encoded_len() } } - impl TypeInfo for VrfOutput { + impl TypeInfo for VrfPreOutput { type Identity = [u8; VRF_OUTPUT_LENGTH]; fn type_info() -> scale_info::Type { @@ -630,28 +642,53 @@ pub mod vrf { #[cfg(feature = "full_crypto")] impl VrfCrypto for Pair { + type VrfInput = VrfInput; + type VrfPreOutput = VrfPreOutput; type VrfSignature = VrfSignature; - type VrfInput = VrfTranscript; } #[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()); - VrfSignature { output: VrfOutput(inout.to_output()), proof: VrfProof(proof) } + fn vrf_sign( + &self, + input: &Self::VrfInput, + preout: Option, + ) -> Self::VrfSignature { + let transcript = input.data.clone(); + let (inout, proof, _) = match input.extra { + Some(ref extra) => self.0.vrf_sign_extra(transcript, extra.clone()), + None => self.0.vrf_sign(transcript), + }; + let preout = preout.unwrap_or_else(|| VrfPreOutput(inout.to_output())); + VrfSignature { preout, proof: VrfProof(proof) } + } + + fn vrf_preout(&self, input: &Self::VrfInput) -> Self::VrfPreOutput { + let preout = self.0.vrf_create_hash(input.data.clone()).to_output(); + VrfPreOutput(preout) } } impl VrfCrypto for Public { + type VrfInput = VrfInput; + type VrfPreOutput = VrfPreOutput; type VrfSignature = VrfSignature; - type VrfInput = VrfTranscript; } impl VrfVerifier for Public { - fn vrf_verify(&self, transcript: &Self::VrfInput, signature: &Self::VrfSignature) -> bool { + fn vrf_verify(&self, input: &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) + let data = input.data.clone(); + match input.extra { + Some(ref extra) => public.vrf_verify_extra( + data, + &signature.preout.0, + &signature.proof.0, + extra.clone(), + ), + None => public.vrf_verify(data, &signature.preout.0, &signature.proof.0), + } }) .is_ok() } @@ -691,38 +728,48 @@ pub mod vrf { #[cfg(feature = "full_crypto")] impl Pair { /// Generate bytes from the given VRF configuration. - pub fn make_bytes>( + pub fn output_bytes>( &self, context: &[u8], - transcript: &VrfTranscript, + input: &VrfInput, ) -> B { - let inout = self.0.vrf_create_hash(transcript.0.clone()); + let inout = self.0.vrf_create_hash(input.data.clone()); inout.make_bytes::(context) } } impl Public { /// Generate bytes from the given VRF configuration. - pub fn make_bytes>( + pub fn output_bytes>( &self, context: &[u8], - transcript: &VrfTranscript, - output: &VrfOutput, + input: &VrfInput, + preout: &VrfPreOutput, ) -> Result { 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)?; + let inout = + preout.0.attach_input_hash(&pubkey, input.data.clone()).map_err(convert_error)?; Ok(inout.make_bytes::(context)) } } + + impl VrfPreOutput { + /// Generate bytes from the given VRF configuration. + pub fn output_bytes>( + &self, + context: &[u8], + input: &VrfInput, + public: &Public, + ) -> Result { + public.output_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, VrfSigner, VrfVerifier, DEV_ADDRESS, DEV_PHRASE}; use serde_json; #[test] @@ -951,19 +998,93 @@ 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 input = VrfInput::new(b"label", &[(b"dom1", b"dat1")]); + + let signature = pair.vrf_sign(&input, None); + + assert!(public.vrf_verify(&input, &signature)); + } + + #[test] + fn vrf_sign_verify_with_extra() { + let pair = Pair::from_seed(b"12345678901234567890123456789012"); + let public = pair.public(); + + let input = + VrfInput::new(b"data", &[(b"dom1", b"dat1")]).extra(b"extra", &[(b"dom2", b"dat2")]); + + let signature = pair.vrf_sign(&input, None); + + assert!(public.vrf_verify(&input, &signature)); + } + + #[test] + fn vrf_sign_verify_with_preout() { + let pair = Pair::from_seed(b"12345678901234567890123456789012"); + let public = pair.public(); + let ctx = b"vrfbytes"; + + let input = + VrfInput::new(b"data", &[(b"dom1", b"dat1")]).extra(b"extra", &[(b"dom2", b"dat2")]); + + let preout = pair.vrf_preout(&input); + + let out1 = pair.output_bytes::<[u8; 32]>(ctx, &input); + let out2 = preout.output_bytes::<[u8; 32]>(ctx, &input, &public).unwrap(); + assert_eq!(out1, out2); + + let signature = pair.vrf_sign(&input, Some(preout)); + + assert!(public.vrf_verify(&input, &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 input = VrfInput::new(b"test", &[(b"foo", b"bar")]); + let ctx = b"vrfbytes"; - let signature = pair.vrf_sign(&transcript); + let signature = pair.vrf_sign(&input, None); - 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(); + let b1 = pair.output_bytes::<[u8; 32]>(ctx, &input); + let b2 = public.output_bytes::<[u8; 32]>(ctx, &input, &signature.preout).unwrap(); assert_eq!(b1, b2); } + + #[test] + fn vrf_backend_compat() { + let pair = Pair::from_seed(b"12345678901234567890123456789012"); + let public = pair.public(); + let input = + VrfInput::new(b"data", &[(b"dom1", b"dat1")]).extra(b"extra", &[(b"dom2", b"dat2")]); + let ctx = b"vrfbytes"; + + let signature = pair.vrf_sign(&input, None); + assert!(public.vrf_verify(&input, &signature)); + + let out1 = pair.output_bytes::<[u8; 32]>(ctx, &input); + let out2 = public.output_bytes::<[u8; 32]>(ctx, &input, &signature.preout).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.data.clone(), |inout| { + let out3 = inout.make_bytes::<[u8; 32]>(ctx); + assert_eq!(out2, out3); + input.extra.clone() + }) + .unwrap(); + let signature2 = + VrfSignature { preout: VrfPreOutput(inout.to_output()), proof: VrfProof(proof) }; + + assert!(public.vrf_verify(&input, &signature2)); + assert_eq!(signature.preout, signature2.preout); + } } From e1c037d9691ce2feb5186c459d6d299faf944da0 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Thu, 27 Apr 2023 14:43:48 +0200 Subject: [PATCH 02/16] Fix tests after renaming --- client/consensus/babe/src/authorship.rs | 16 ++++++++-------- client/consensus/babe/src/tests.rs | 19 +++++++++---------- client/consensus/babe/src/verification.rs | 16 ++++++++-------- client/keystore/src/local.rs | 8 ++++---- frame/babe/src/lib.rs | 10 +++++----- frame/babe/src/mock.rs | 8 ++++---- frame/babe/src/tests.rs | 9 ++++++--- primitives/consensus/babe/src/lib.rs | 6 +++--- primitives/core/src/sr25519.rs | 4 +++- primitives/keystore/src/lib.rs | 4 ++-- primitives/keystore/src/testing.rs | 14 +++++++------- 11 files changed, 59 insertions(+), 55 deletions(-) diff --git a/client/consensus/babe/src/authorship.rs b/client/consensus/babe/src/authorship.rs index dc7cd10867745..e287055e7c7dd 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_input, 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 vrf_input = make_vrf_input(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(), &vrf_input); if let Ok(Some(vrf_signature)) = result { Some(PreDigest::SecondaryVRF(SecondaryVRFPreDigest { slot, @@ -239,19 +239,19 @@ fn claim_primary_slot( epoch_index = epoch.clone_for_slot(slot).epoch_index; } - let transcript = make_transcript(randomness, slot, epoch_index); + let vrf_input = make_vrf_input(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(), &vrf_input); 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]>( + .output_bytes::<[u8; AUTHORING_SCORE_LENGTH]>( AUTHORING_SCORE_VRF_CONTEXT, - &transcript, - &vrf_signature.output, + &vrf_input, + &vrf_signature.preout, ) .map(|bytes| u128::from_le_bytes(bytes) < threshold) .unwrap_or_default(); diff --git a/client/consensus/babe/src/tests.rs b/client/consensus/babe/src/tests.rs index 740ce63676374..414ae8fc9b1c1 100644 --- a/client/consensus/babe/src/tests.rs +++ b/client/consensus/babe/src/tests.rs @@ -29,8 +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, - Slot, + inherents::InherentDataProvider, make_vrf_input, AllowedSlots, AuthorityId, AuthorityPair, Slot, }; use sp_consensus_slots::SlotDuration; use sp_core::crypto::Pair; @@ -630,24 +629,24 @@ 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 transcript = make_vrf_input(&epoch.randomness.clone(), 0.into(), epoch.epoch_index); let sign = keystore .sr25519_vrf_sign(AuthorityId::ID, &public, &transcript) .unwrap() .unwrap(); - assert_eq!(pre_digest.vrf_signature.output, sign.output); + assert_eq!(pre_digest.vrf_signature.preout, sign.preout); // We expect a SecondaryVRF claim for slot 1 let pre_digest = match claim_slot(1.into(), &epoch, &keystore).unwrap().0 { PreDigest::SecondaryVRF(d) => d, v => panic!("Unexpected pre-digest variant {:?}", v), }; - let transcript = make_transcript(&epoch.randomness.clone(), 1.into(), epoch.epoch_index); + let transcript = make_vrf_input(&epoch.randomness.clone(), 1.into(), epoch.epoch_index); let sign = keystore .sr25519_vrf_sign(AuthorityId::ID, &public, &transcript) .unwrap() .unwrap(); - assert_eq!(pre_digest.vrf_signature.output, sign.output); + assert_eq!(pre_digest.vrf_signature.preout, sign.preout); // Check that correct epoch index has been used if epochs are skipped (primary VRF) let slot = Slot::from(103); @@ -656,13 +655,13 @@ 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 transcript = make_vrf_input(&epoch.randomness.clone(), slot, fixed_epoch.epoch_index); let sign = keystore .sr25519_vrf_sign(AuthorityId::ID, &public, &transcript) .unwrap() .unwrap(); assert_eq!(fixed_epoch.epoch_index, 11); - assert_eq!(claim.vrf_signature.output, sign.output); + assert_eq!(claim.vrf_signature.preout, sign.preout); // Check that correct epoch index has been used if epochs are skipped (secondary VRF) let slot = Slot::from(100); @@ -671,13 +670,13 @@ 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 transcript = make_vrf_input(&epoch.randomness.clone(), slot, fixed_epoch.epoch_index); let sign = keystore .sr25519_vrf_sign(AuthorityId::ID, &public, &transcript) .unwrap() .unwrap(); assert_eq!(fixed_epoch.epoch_index, 11); - assert_eq!(pre_digest.vrf_signature.output, sign.output); + assert_eq!(pre_digest.vrf_signature.preout, sign.preout); } // Propose and import a new BABE block on top of the given parent. diff --git a/client/consensus/babe/src/verification.rs b/client/consensus/babe/src/verification.rs index cadceb6a57510..0ea2a7735232c 100644 --- a/client/consensus/babe/src/verification.rs +++ b/client/consensus/babe/src/verification.rs @@ -30,7 +30,7 @@ use sp_consensus_babe::{ CompatibleDigestItem, PreDigest, PrimaryPreDigest, SecondaryPlainPreDigest, SecondaryVRFPreDigest, }, - make_transcript, AuthorityId, AuthorityPair, AuthoritySignature, + make_vrf_input, AuthorityId, AuthorityPair, AuthoritySignature, }; use sp_consensus_slots::Slot; use sp_core::{ @@ -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 vrf_input = make_vrf_input(&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(&vrf_input, &pre_digest.vrf_signature) { return Err(babe_err(Error::VrfVerificationFailed)) } @@ -182,10 +182,10 @@ fn check_primary_header( let score = authority_id .as_inner_ref() - .make_bytes::<[u8; AUTHORING_SCORE_LENGTH]>( + .output_bytes::<[u8; AUTHORING_SCORE_LENGTH]>( AUTHORING_SCORE_VRF_CONTEXT, - &transcript, - &pre_digest.vrf_signature.output, + &vrf_input, + &pre_digest.vrf_signature.preout, ) .map(u128::from_le_bytes) .map_err(|_| babe_err(Error::VrfVerificationFailed))?; @@ -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 vrf_input = make_vrf_input(&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(&vrf_input, &pre_digest.vrf_signature) { return Err(Error::VrfVerificationFailed) } diff --git a/client/keystore/src/local.rs b/client/keystore/src/local.rs index 3551623f332a2..ff1b4c0691e09 100644 --- a/client/keystore/src/local.rs +++ b/client/keystore/src/local.rs @@ -102,13 +102,13 @@ impl LocalKeystore { &self, key_type: KeyTypeId, public: &T::Public, - transcript: &T::VrfInput, + input: &T::VrfInput, ) -> 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(input, None)); Ok(sig) } } @@ -142,9 +142,9 @@ impl Keystore for LocalKeystore { &self, key_type: KeyTypeId, public: &sr25519::Public, - transcript: &sr25519::vrf::VrfTranscript, + input: &sr25519::vrf::VrfInput, ) -> std::result::Result, TraitError> { - self.vrf_sign::(key_type, public, transcript) + self.vrf_sign::(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..81efa1e24da6b 100644 --- a/frame/babe/src/lib.rs +++ b/frame/babe/src/lib.rs @@ -362,7 +362,7 @@ pub mod pallet { .get(authority_index as usize) .and_then(|(authority, _)| { let public = authority.as_inner_ref(); - let transcript = sp_consensus_babe::make_transcript( + let vrf_input = sp_consensus_babe::make_vrf_input( &Self::randomness(), CurrentSlot::::get(), EpochIndex::::get(), @@ -373,14 +373,14 @@ pub mod pallet { // down the runtime. debug_assert!({ use sp_core::crypto::VrfVerifier; - public.vrf_verify(&transcript, &vrf_signature) + public.vrf_verify(&vrf_input, &vrf_signature) }); public - .make_bytes( + .output_bytes( RANDOMNESS_VRF_CONTEXT, - &transcript, - &vrf_signature.output, + &vrf_input, + &vrf_signature.preout, ) .ok() }); diff --git a/frame/babe/src/mock.rs b/frame/babe/src/mock.rs index bccdb42c71ac3..3fbd858f876f1 100644 --- a/frame/babe/src/mock.rs +++ b/frame/babe/src/mock.rs @@ -314,17 +314,17 @@ pub fn make_secondary_vrf_pre_digest( Digest { logs: vec![log] } } -pub fn make_vrf_output( +pub fn make_vrf_signature_and_output( slot: Slot, pair: &sp_consensus_babe::AuthorityPair, ) -> (VrfSignature, Randomness) { - let transcript = sp_consensus_babe::make_transcript(&Babe::randomness(), slot, 0); + let vrf_input = sp_consensus_babe::make_vrf_input(&Babe::randomness(), slot, 0); - let signature = pair.as_ref().vrf_sign(&transcript); + let signature = pair.as_ref().vrf_sign(&vrf_input, None); let randomness = pair .as_ref() - .make_bytes::(sp_consensus_babe::RANDOMNESS_VRF_CONTEXT, &transcript); + .output_bytes::(sp_consensus_babe::RANDOMNESS_VRF_CONTEXT, &vrf_input); (signature, randomness) } diff --git a/frame/babe/src/tests.rs b/frame/babe/src/tests.rs index 8a9aa6fcc03d2..102ceffc3647b 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_output(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_output(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_output(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..2a91e59661842 100644 --- a/primitives/consensus/babe/src/lib.rs +++ b/primitives/consensus/babe/src/lib.rs @@ -32,7 +32,7 @@ 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, VrfPreOutput, VrfProof, VrfSignature}; /// Key type for BABE module. pub const KEY_TYPE: sp_core::crypto::KeyTypeId = sp_application_crypto::key_types::BABE; @@ -95,8 +95,8 @@ pub type BabeAuthorityWeight = u64; pub type BabeBlockWeight = u32; /// Make a VRF transcript data container -pub fn make_transcript(randomness: &Randomness, slot: Slot, epoch: u64) -> VrfTranscript { - VrfTranscript::new( +pub fn make_vrf_input(randomness: &Randomness, slot: Slot, epoch: u64) -> VrfInput { + VrfInput::new( &BABE_ENGINE_ID, &[ (b"slot number", &slot.to_le_bytes()), diff --git a/primitives/core/src/sr25519.rs b/primitives/core/src/sr25519.rs index c2b8d16a33304..755d519466785 100644 --- a/primitives/core/src/sr25519.rs +++ b/primitives/core/src/sr25519.rs @@ -549,7 +549,7 @@ pub mod vrf { pub struct VrfInput { /// VRF input data. pub data: merlin::Transcript, - /// Extra non-input data signed by the VRF. Doesn't contribute to VRF output. + /// Extra data to be signed by the VRF. pub extra: Option, } @@ -561,6 +561,8 @@ pub mod vrf { VrfInput { data: transcript, extra: None } } + /// Add extra non-input data to be signed by the VRF. + /// Extra data doesn't contribute to the VRF output. pub fn extra(mut self, 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)); diff --git a/primitives/keystore/src/lib.rs b/primitives/keystore/src/lib.rs index 5b41f3b80043d..0e49bb17d22f6 100644 --- a/primitives/keystore/src/lib.rs +++ b/primitives/keystore/src/lib.rs @@ -72,7 +72,7 @@ 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 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. @@ -86,7 +86,7 @@ pub trait Keystore: Send + Sync { &self, key_type: KeyTypeId, public: &sr25519::Public, - transcript: &sr25519::vrf::VrfTranscript, + input: &sr25519::vrf::VrfInput, ) -> Result, Error>; /// Returns all ed25519 public keys for the given key type. diff --git a/primitives/keystore/src/testing.rs b/primitives/keystore/src/testing.rs index a6fcd6e26abe3..7328939e1553a 100644 --- a/primitives/keystore/src/testing.rs +++ b/primitives/keystore/src/testing.rs @@ -103,9 +103,9 @@ impl MemoryKeystore { &self, key_type: KeyTypeId, public: &T::Public, - transcript: &T::VrfInput, + input: &T::VrfInput, ) -> 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(input, None)); Ok(sig) } } @@ -136,9 +136,9 @@ impl Keystore for MemoryKeystore { &self, key_type: KeyTypeId, public: &sr25519::Public, - transcript: &sr25519::vrf::VrfTranscript, + input: &sr25519::vrf::VrfInput, ) -> Result, Error> { - self.vrf_sign::(key_type, public, transcript) + self.vrf_sign::(key_type, public, input) } fn ed25519_public_keys(&self, key_type: KeyTypeId) -> Vec { @@ -267,7 +267,7 @@ 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 input = sr25519::vrf::VrfInput::new( b"Test", &[ (b"one", &1_u64.to_le_bytes()), @@ -276,14 +276,14 @@ mod tests { ], ); - let result = store.sr25519_vrf_sign(SR25519, &key_pair.public(), &transcript); + let result = store.sr25519_vrf_sign(SR25519, &key_pair.public(), &input); 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(), &input); assert!(result.unwrap().is_some()); } From dc10d8d1e8d2d26a7009b0a574c488b14a258a79 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Thu, 27 Apr 2023 14:54:18 +0200 Subject: [PATCH 03/16] Rename VrfSecret/VrfVerifier to VrfSecret/VrfPublic --- client/consensus/babe/src/verification.rs | 2 +- client/keystore/src/local.rs | 4 ++-- frame/babe/src/lib.rs | 2 +- frame/babe/src/mock.rs | 2 +- primitives/core/src/crypto.rs | 8 ++++---- primitives/core/src/sr25519.rs | 10 +++++----- primitives/keystore/src/testing.rs | 4 ++-- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/client/consensus/babe/src/verification.rs b/client/consensus/babe/src/verification.rs index 0ea2a7735232c..982de51881f5f 100644 --- a/client/consensus/babe/src/verification.rs +++ b/client/consensus/babe/src/verification.rs @@ -34,7 +34,7 @@ use sp_consensus_babe::{ }; use sp_consensus_slots::Slot; use sp_core::{ - crypto::{VrfVerifier, Wraps}, + crypto::{VrfPublic, Wraps}, Pair, }; use sp_runtime::{traits::Header, DigestItem}; diff --git a/client/keystore/src/local.rs b/client/keystore/src/local.rs index ff1b4c0691e09..69147a4adc3dd 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,7 +98,7 @@ impl LocalKeystore { Ok(signature) } - fn vrf_sign( + fn vrf_sign( &self, key_type: KeyTypeId, public: &T::Public, diff --git a/frame/babe/src/lib.rs b/frame/babe/src/lib.rs index 81efa1e24da6b..2fce068b62a35 100644 --- a/frame/babe/src/lib.rs +++ b/frame/babe/src/lib.rs @@ -372,7 +372,7 @@ 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; + use sp_core::crypto::VrfPublic; public.vrf_verify(&vrf_input, &vrf_signature) }); diff --git a/frame/babe/src/mock.rs b/frame/babe/src/mock.rs index 3fbd858f876f1..c2b7f05d6da43 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; diff --git a/primitives/core/src/crypto.rs b/primitives/core/src/crypto.rs index f9145f4cbde06..72db088698480 100644 --- a/primitives/core/src/crypto.rs +++ b/primitives/core/src/crypto.rs @@ -1102,8 +1102,8 @@ pub trait VrfCrypto { type VrfSignature; } -/// VRF Signer. -pub trait VrfSigner: VrfCrypto { +/// VRF Secret Key. +pub trait VrfSecret: VrfCrypto { /// Get VRF pre-output. fn vrf_preout(&self, data: &Self::VrfInput) -> Self::VrfPreOutput; @@ -1115,8 +1115,8 @@ pub trait VrfSigner: VrfCrypto { ) -> 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; } diff --git a/primitives/core/src/sr25519.rs b/primitives/core/src/sr25519.rs index 755d519466785..172a186c92112 100644 --- a/primitives/core/src/sr25519.rs +++ b/primitives/core/src/sr25519.rs @@ -537,8 +537,8 @@ 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}, @@ -650,7 +650,7 @@ pub mod vrf { } #[cfg(feature = "full_crypto")] - impl VrfSigner for Pair { + impl VrfSecret for Pair { fn vrf_sign( &self, input: &Self::VrfInput, @@ -677,7 +677,7 @@ pub mod vrf { type VrfSignature = VrfSignature; } - impl VrfVerifier for Public { + impl VrfPublic for Public { fn vrf_verify(&self, input: &Self::VrfInput, signature: &Self::VrfSignature) -> bool { schnorrkel::PublicKey::from_bytes(self) .and_then(|public| { @@ -771,7 +771,7 @@ pub mod vrf { #[cfg(test)] mod tests { use super::{vrf::*, *}; - use crate::crypto::{Ss58Codec, VrfSigner, VrfVerifier, DEV_ADDRESS, DEV_PHRASE}; + use crate::crypto::{Ss58Codec, VrfPublic, VrfSecret, DEV_ADDRESS, DEV_PHRASE}; use serde_json; #[test] diff --git a/primitives/keystore/src/testing.rs b/primitives/keystore/src/testing.rs index 7328939e1553a..32eb9083b9c15 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,7 +99,7 @@ impl MemoryKeystore { Ok(sig) } - fn vrf_sign( + fn vrf_sign( &self, key_type: KeyTypeId, public: &T::Public, From fc880b625cb6aef9e596f620513732bd5f6054a7 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Thu, 27 Apr 2023 14:57:46 +0200 Subject: [PATCH 04/16] Further encrapsulation of 'transcript' type to the sr25519 implementation --- client/consensus/babe/src/tests.rs | 16 ++++++++-------- primitives/consensus/babe/src/lib.rs | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/client/consensus/babe/src/tests.rs b/client/consensus/babe/src/tests.rs index 414ae8fc9b1c1..2f312a3878087 100644 --- a/client/consensus/babe/src/tests.rs +++ b/client/consensus/babe/src/tests.rs @@ -629,9 +629,9 @@ fn claim_vrf_check() { PreDigest::Primary(d) => d, v => panic!("Unexpected pre-digest variant {:?}", v), }; - let transcript = make_vrf_input(&epoch.randomness.clone(), 0.into(), epoch.epoch_index); + let vrf_input = make_vrf_input(&epoch.randomness.clone(), 0.into(), epoch.epoch_index); let sign = keystore - .sr25519_vrf_sign(AuthorityId::ID, &public, &transcript) + .sr25519_vrf_sign(AuthorityId::ID, &public, &vrf_input) .unwrap() .unwrap(); assert_eq!(pre_digest.vrf_signature.preout, sign.preout); @@ -641,9 +641,9 @@ fn claim_vrf_check() { PreDigest::SecondaryVRF(d) => d, v => panic!("Unexpected pre-digest variant {:?}", v), }; - let transcript = make_vrf_input(&epoch.randomness.clone(), 1.into(), epoch.epoch_index); + let vrf_input = make_vrf_input(&epoch.randomness.clone(), 1.into(), epoch.epoch_index); let sign = keystore - .sr25519_vrf_sign(AuthorityId::ID, &public, &transcript) + .sr25519_vrf_sign(AuthorityId::ID, &public, &vrf_input) .unwrap() .unwrap(); assert_eq!(pre_digest.vrf_signature.preout, sign.preout); @@ -655,9 +655,9 @@ fn claim_vrf_check() { v => panic!("Unexpected claim variant {:?}", v), }; let fixed_epoch = epoch.clone_for_slot(slot); - let transcript = make_vrf_input(&epoch.randomness.clone(), slot, fixed_epoch.epoch_index); + let vrf_input = make_vrf_input(&epoch.randomness.clone(), slot, fixed_epoch.epoch_index); let sign = keystore - .sr25519_vrf_sign(AuthorityId::ID, &public, &transcript) + .sr25519_vrf_sign(AuthorityId::ID, &public, &vrf_input) .unwrap() .unwrap(); assert_eq!(fixed_epoch.epoch_index, 11); @@ -670,9 +670,9 @@ fn claim_vrf_check() { v => panic!("Unexpected claim variant {:?}", v), }; let fixed_epoch = epoch.clone_for_slot(slot); - let transcript = make_vrf_input(&epoch.randomness.clone(), slot, fixed_epoch.epoch_index); + let vrf_input = make_vrf_input(&epoch.randomness.clone(), slot, fixed_epoch.epoch_index); let sign = keystore - .sr25519_vrf_sign(AuthorityId::ID, &public, &transcript) + .sr25519_vrf_sign(AuthorityId::ID, &public, &vrf_input) .unwrap() .unwrap(); assert_eq!(fixed_epoch.epoch_index, 11); diff --git a/primitives/consensus/babe/src/lib.rs b/primitives/consensus/babe/src/lib.rs index 2a91e59661842..043a978c3cf81 100644 --- a/primitives/consensus/babe/src/lib.rs +++ b/primitives/consensus/babe/src/lib.rs @@ -94,7 +94,7 @@ 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 +/// Make VRF input suitable for BABE's randomness generation. pub fn make_vrf_input(randomness: &Randomness, slot: Slot, epoch: u64) -> VrfInput { VrfInput::new( &BABE_ENGINE_ID, From 521191bd5309041496226c9167324bc69ed65085 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Thu, 27 Apr 2023 17:02:00 +0200 Subject: [PATCH 05/16] Keystore sr25519 pre-output --- client/keystore/src/local.rs | 23 ++++++++++++++ primitives/core/src/sr25519.rs | 4 +-- primitives/keystore/src/lib.rs | 17 +++++++++-- primitives/keystore/src/testing.rs | 48 ++++++++++++++++++++++++++++++ 4 files changed, 87 insertions(+), 5 deletions(-) diff --git a/client/keystore/src/local.rs b/client/keystore/src/local.rs index 69147a4adc3dd..d2ec52e20795d 100644 --- a/client/keystore/src/local.rs +++ b/client/keystore/src/local.rs @@ -111,6 +111,20 @@ impl LocalKeystore { .map(|pair| pair.vrf_sign(input, None)); Ok(sig) } + + fn vrf_preout( + &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_preout(input)); + Ok(preout) + } } impl Keystore for LocalKeystore { @@ -147,6 +161,15 @@ impl Keystore for LocalKeystore { self.vrf_sign::(key_type, public, input) } + fn sr25519_vrf_preout( + &self, + key_type: KeyTypeId, + public: &sr25519::Public, + input: &sr25519::vrf::VrfInput, + ) -> std::result::Result, TraitError> { + self.vrf_preout::(key_type, public, input) + } + fn ed25519_public_keys(&self, key_type: KeyTypeId) -> Vec { self.public_keys::(key_type) } diff --git a/primitives/core/src/sr25519.rs b/primitives/core/src/sr25519.rs index 172a186c92112..a1cecba7424c1 100644 --- a/primitives/core/src/sr25519.rs +++ b/primitives/core/src/sr25519.rs @@ -574,9 +574,9 @@ pub mod vrf { /// VRF signature data #[derive(Clone, Debug, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo)] pub struct VrfSignature { - /// Initial pre-output configuration. + /// VRF pre-output. pub preout: VrfPreOutput, - /// Calculated proof. + /// VRF input proof. pub proof: VrfProof, } diff --git a/primitives/keystore/src/lib.rs b/primitives/keystore/src/lib.rs index 0e49bb17d22f6..fe6bdb4c2e62a 100644 --- a/primitives/keystore/src/lib.rs +++ b/primitives/keystore/src/lib.rs @@ -77,9 +77,6 @@ pub trait Keystore: Send + Sync { /// 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( @@ -89,6 +86,20 @@ pub trait Keystore: Send + Sync { input: &sr25519::vrf::VrfInput, ) -> Result, Error>; + /// Generate an sr25519 VRF pre-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_preout( + &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 32eb9083b9c15..177e47403e8d6 100644 --- a/primitives/keystore/src/testing.rs +++ b/primitives/keystore/src/testing.rs @@ -108,6 +108,16 @@ impl MemoryKeystore { let sig = self.pair::(key_type, public).map(|pair| pair.vrf_sign(input, None)); Ok(sig) } + + fn vrf_preout( + &self, + key_type: KeyTypeId, + public: &T::Public, + input: &T::VrfInput, + ) -> Result, Error> { + let preout = self.pair::(key_type, public).map(|pair| pair.vrf_preout(input)); + Ok(preout) + } } impl Keystore for MemoryKeystore { @@ -141,6 +151,15 @@ impl Keystore for MemoryKeystore { self.vrf_sign::(key_type, public, input) } + fn sr25519_vrf_preout( + &self, + key_type: KeyTypeId, + public: &sr25519::Public, + input: &sr25519::vrf::VrfInput, + ) -> Result, Error> { + self.vrf_preout::(key_type, public, input) + } + fn ed25519_public_keys(&self, key_type: KeyTypeId) -> Vec { self.public_keys::(key_type) } @@ -288,6 +307,35 @@ mod tests { assert!(result.unwrap().is_some()); } + #[test] + fn vrf_preout() { + 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_preout(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_preout(SR25519, &pair.public(), &input).unwrap().unwrap(); + + let result = preout.output_bytes::<[u8; 32]>(b"rand", &input, &pair.public()); + assert!(result.is_ok()); + } + #[test] fn ecdsa_sign_prehashed_works() { let store = MemoryKeystore::new(); From 7aac66e012c4fa073486c114d2e0bd2dbec8ec03 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Thu, 27 Apr 2023 19:08:19 +0200 Subject: [PATCH 06/16] Leave additional custom input field hidden in the associated VrfInput type --- client/keystore/src/local.rs | 2 +- primitives/core/src/crypto.rs | 6 +--- primitives/core/src/sr25519.rs | 48 +++++++++++++++++------------- primitives/keystore/src/testing.rs | 2 +- 4 files changed, 30 insertions(+), 28 deletions(-) diff --git a/client/keystore/src/local.rs b/client/keystore/src/local.rs index d2ec52e20795d..4efc8a4fb04b3 100644 --- a/client/keystore/src/local.rs +++ b/client/keystore/src/local.rs @@ -108,7 +108,7 @@ impl LocalKeystore { .0 .read() .key_pair_by_type::(public, key_type)? - .map(|pair| pair.vrf_sign(input, None)); + .map(|pair| pair.vrf_sign(input)); Ok(sig) } diff --git a/primitives/core/src/crypto.rs b/primitives/core/src/crypto.rs index 72db088698480..f3a429d15c615 100644 --- a/primitives/core/src/crypto.rs +++ b/primitives/core/src/crypto.rs @@ -1108,11 +1108,7 @@ pub trait VrfSecret: VrfCrypto { fn vrf_preout(&self, data: &Self::VrfInput) -> Self::VrfPreOutput; /// Sign input data with optional pre-computed pre-output. - fn vrf_sign( - &self, - input: &Self::VrfInput, - preout: Option, - ) -> Self::VrfSignature; + fn vrf_sign(&self, input: &Self::VrfInput) -> Self::VrfSignature; } /// VRF Public Key. diff --git a/primitives/core/src/sr25519.rs b/primitives/core/src/sr25519.rs index a1cecba7424c1..66e9b234c39dc 100644 --- a/primitives/core/src/sr25519.rs +++ b/primitives/core/src/sr25519.rs @@ -546,11 +546,14 @@ pub mod vrf { }; /// VRF input ready to be used for VRF sign and verify operations. + // Note: here we may potentially add a closure to check-before-sign... pub struct VrfInput { /// VRF input data. pub data: merlin::Transcript, /// Extra data to be signed by the VRF. pub extra: Option, + /// Optional pre-computed pre-output + pub preout: Option, } impl VrfInput { @@ -558,17 +561,23 @@ pub mod vrf { 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)); - VrfInput { data: transcript, extra: None } + VrfInput { data: transcript, extra: None, preout: None } } /// Add extra non-input data to be signed by the VRF. /// Extra data doesn't contribute to the VRF output. - pub fn extra(mut self, label: &'static [u8], data: &[(&'static [u8], &[u8])]) -> Self { + pub fn with_extra(mut self, 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)); self.extra = Some(transcript); self } + + /// Add pre-output corresponding to `data` to be used during `sign` operation. + pub fn with_preout(mut self, preout: VrfPreOutput) -> Self { + self.preout = Some(preout); + self + } } /// VRF signature data @@ -651,17 +660,13 @@ pub mod vrf { #[cfg(feature = "full_crypto")] impl VrfSecret for Pair { - fn vrf_sign( - &self, - input: &Self::VrfInput, - preout: Option, - ) -> Self::VrfSignature { + fn vrf_sign(&self, input: &Self::VrfInput) -> Self::VrfSignature { let transcript = input.data.clone(); let (inout, proof, _) = match input.extra { Some(ref extra) => self.0.vrf_sign_extra(transcript, extra.clone()), None => self.0.vrf_sign(transcript), }; - let preout = preout.unwrap_or_else(|| VrfPreOutput(inout.to_output())); + let preout = input.preout.clone().unwrap_or_else(|| VrfPreOutput(inout.to_output())); VrfSignature { preout, proof: VrfProof(proof) } } @@ -1005,9 +1010,9 @@ mod tests { let pair = Pair::from_seed(b"12345678901234567890123456789012"); let public = pair.public(); - let input = VrfInput::new(b"label", &[(b"dom1", b"dat1")]); + let input = VrfInput::new(b"label", &[(b"domain1", b"data1")]); - let signature = pair.vrf_sign(&input, None); + let signature = pair.vrf_sign(&input); assert!(public.vrf_verify(&input, &signature)); } @@ -1017,10 +1022,10 @@ mod tests { let pair = Pair::from_seed(b"12345678901234567890123456789012"); let public = pair.public(); - let input = - VrfInput::new(b"data", &[(b"dom1", b"dat1")]).extra(b"extra", &[(b"dom2", b"dat2")]); + let input = VrfInput::new(b"label", &[(b"domain1", b"data1")]) + .with_extra(b"extra", &[(b"domain2", b"data2")]); - let signature = pair.vrf_sign(&input, None); + let signature = pair.vrf_sign(&input); assert!(public.vrf_verify(&input, &signature)); } @@ -1031,8 +1036,8 @@ mod tests { let public = pair.public(); let ctx = b"vrfbytes"; - let input = - VrfInput::new(b"data", &[(b"dom1", b"dat1")]).extra(b"extra", &[(b"dom2", b"dat2")]); + let input = VrfInput::new(b"label", &[(b"domain1", b"data1")]) + .with_extra(b"extra", &[(b"domain2", b"data2")]); let preout = pair.vrf_preout(&input); @@ -1040,7 +1045,8 @@ mod tests { let out2 = preout.output_bytes::<[u8; 32]>(ctx, &input, &public).unwrap(); assert_eq!(out1, out2); - let signature = pair.vrf_sign(&input, Some(preout)); + let input = input.with_preout(preout); + let signature = pair.vrf_sign(&input); assert!(public.vrf_verify(&input, &signature)); } @@ -1049,10 +1055,10 @@ mod tests { fn vrf_make_bytes_matches() { let pair = Pair::from_seed(b"12345678901234567890123456789012"); let public = pair.public(); - let input = VrfInput::new(b"test", &[(b"foo", b"bar")]); + let input = VrfInput::new(b"label", &[(b"domain", b"data")]); let ctx = b"vrfbytes"; - let signature = pair.vrf_sign(&input, None); + let signature = pair.vrf_sign(&input); let b1 = pair.output_bytes::<[u8; 32]>(ctx, &input); let b2 = public.output_bytes::<[u8; 32]>(ctx, &input, &signature.preout).unwrap(); @@ -1063,11 +1069,11 @@ mod tests { fn vrf_backend_compat() { let pair = Pair::from_seed(b"12345678901234567890123456789012"); let public = pair.public(); - let input = - VrfInput::new(b"data", &[(b"dom1", b"dat1")]).extra(b"extra", &[(b"dom2", b"dat2")]); + let input = VrfInput::new(b"label", &[(b"domain1", b"data1")]) + .with_extra(b"extra", &[(b"domain2", b"data2")]); let ctx = b"vrfbytes"; - let signature = pair.vrf_sign(&input, None); + let signature = pair.vrf_sign(&input); assert!(public.vrf_verify(&input, &signature)); let out1 = pair.output_bytes::<[u8; 32]>(ctx, &input); diff --git a/primitives/keystore/src/testing.rs b/primitives/keystore/src/testing.rs index 177e47403e8d6..538c7910b771e 100644 --- a/primitives/keystore/src/testing.rs +++ b/primitives/keystore/src/testing.rs @@ -105,7 +105,7 @@ impl MemoryKeystore { public: &T::Public, input: &T::VrfInput, ) -> Result, Error> { - let sig = self.pair::(key_type, public).map(|pair| pair.vrf_sign(input, None)); + let sig = self.pair::(key_type, public).map(|pair| pair.vrf_sign(input)); Ok(sig) } From d36ae199931c65910d8397a7d665910f72830ecc Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Thu, 27 Apr 2023 19:36:53 +0200 Subject: [PATCH 07/16] Fix test --- frame/babe/src/mock.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/babe/src/mock.rs b/frame/babe/src/mock.rs index c2b7f05d6da43..d982be4d5480c 100644 --- a/frame/babe/src/mock.rs +++ b/frame/babe/src/mock.rs @@ -320,7 +320,7 @@ pub fn make_vrf_signature_and_output( ) -> (VrfSignature, Randomness) { let vrf_input = sp_consensus_babe::make_vrf_input(&Babe::randomness(), slot, 0); - let signature = pair.as_ref().vrf_sign(&vrf_input, None); + let signature = pair.as_ref().vrf_sign(&vrf_input); let randomness = pair .as_ref() From 15a05ce95df38fb2f3ac8fa8488cf1a73a92fb00 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Thu, 27 Apr 2023 19:52:01 +0200 Subject: [PATCH 08/16] More ergonomic output_bytes --- client/consensus/babe/src/authorship.rs | 2 +- client/consensus/babe/src/verification.rs | 2 +- frame/babe/src/mock.rs | 2 +- primitives/core/src/sr25519.rs | 39 +++++++++++++---------- primitives/keystore/src/testing.rs | 2 +- 5 files changed, 26 insertions(+), 21 deletions(-) diff --git a/client/consensus/babe/src/authorship.rs b/client/consensus/babe/src/authorship.rs index e287055e7c7dd..93897310add7e 100644 --- a/client/consensus/babe/src/authorship.rs +++ b/client/consensus/babe/src/authorship.rs @@ -248,7 +248,7 @@ fn claim_primary_slot( let can_claim = authority_id .as_inner_ref() - .output_bytes::<[u8; AUTHORING_SCORE_LENGTH]>( + .output_bytes::( AUTHORING_SCORE_VRF_CONTEXT, &vrf_input, &vrf_signature.preout, diff --git a/client/consensus/babe/src/verification.rs b/client/consensus/babe/src/verification.rs index 982de51881f5f..e9f546e91a114 100644 --- a/client/consensus/babe/src/verification.rs +++ b/client/consensus/babe/src/verification.rs @@ -182,7 +182,7 @@ fn check_primary_header( let score = authority_id .as_inner_ref() - .output_bytes::<[u8; AUTHORING_SCORE_LENGTH]>( + .output_bytes::( AUTHORING_SCORE_VRF_CONTEXT, &vrf_input, &pre_digest.vrf_signature.preout, diff --git a/frame/babe/src/mock.rs b/frame/babe/src/mock.rs index d982be4d5480c..b71c20015632a 100644 --- a/frame/babe/src/mock.rs +++ b/frame/babe/src/mock.rs @@ -324,7 +324,7 @@ pub fn make_vrf_signature_and_output( let randomness = pair .as_ref() - .output_bytes::(sp_consensus_babe::RANDOMNESS_VRF_CONTEXT, &vrf_input); + .output_bytes(sp_consensus_babe::RANDOMNESS_VRF_CONTEXT, &vrf_input); (signature, randomness) } diff --git a/primitives/core/src/sr25519.rs b/primitives/core/src/sr25519.rs index 66e9b234c39dc..efe5dfdf20eb2 100644 --- a/primitives/core/src/sr25519.rs +++ b/primitives/core/src/sr25519.rs @@ -735,39 +735,44 @@ pub mod vrf { #[cfg(feature = "full_crypto")] impl Pair { /// Generate bytes from the given VRF configuration. - pub fn output_bytes>( - &self, - context: &[u8], - input: &VrfInput, - ) -> B { + pub fn output_bytes(&self, context: &[u8], input: &VrfInput) -> [u8; N] + where + [u8; N]: Default, + { let inout = self.0.vrf_create_hash(input.data.clone()); - inout.make_bytes::(context) + inout.make_bytes::<[u8; N]>(context) } } impl Public { /// Generate bytes from the given VRF configuration. - pub fn output_bytes>( + pub fn output_bytes( &self, context: &[u8], input: &VrfInput, preout: &VrfPreOutput, - ) -> Result { + ) -> Result<[u8; N], codec::Error> + where + [u8; N]: Default, + { let pubkey = schnorrkel::PublicKey::from_bytes(&self.0).map_err(convert_error)?; let inout = preout.0.attach_input_hash(&pubkey, input.data.clone()).map_err(convert_error)?; - Ok(inout.make_bytes::(context)) + Ok(inout.make_bytes::<[u8; N]>(context)) } } impl VrfPreOutput { /// Generate bytes from the given VRF configuration. - pub fn output_bytes>( + pub fn output_bytes( &self, context: &[u8], input: &VrfInput, public: &Public, - ) -> Result { + ) -> Result<[u8; N], codec::Error> + where + [u8; N]: Default, + { public.output_bytes(context, input, self) } } @@ -1041,8 +1046,8 @@ mod tests { let preout = pair.vrf_preout(&input); - let out1 = pair.output_bytes::<[u8; 32]>(ctx, &input); - let out2 = preout.output_bytes::<[u8; 32]>(ctx, &input, &public).unwrap(); + let out1 = pair.output_bytes::<32>(ctx, &input); + let out2 = preout.output_bytes::<32>(ctx, &input, &public).unwrap(); assert_eq!(out1, out2); let input = input.with_preout(preout); @@ -1060,8 +1065,8 @@ mod tests { let signature = pair.vrf_sign(&input); - let b1 = pair.output_bytes::<[u8; 32]>(ctx, &input); - let b2 = public.output_bytes::<[u8; 32]>(ctx, &input, &signature.preout).unwrap(); + let b1 = pair.output_bytes::<32>(ctx, &input); + let b2 = public.output_bytes::<32>(ctx, &input, &signature.preout).unwrap(); assert_eq!(b1, b2); } @@ -1076,8 +1081,8 @@ mod tests { let signature = pair.vrf_sign(&input); assert!(public.vrf_verify(&input, &signature)); - let out1 = pair.output_bytes::<[u8; 32]>(ctx, &input); - let out2 = public.output_bytes::<[u8; 32]>(ctx, &input, &signature.preout).unwrap(); + let out1 = pair.output_bytes::<32>(ctx, &input); + let out2 = public.output_bytes::<32>(ctx, &input, &signature.preout).unwrap(); assert_eq!(out1, out2); // Direct call to backend version of sign after check with extra params diff --git a/primitives/keystore/src/testing.rs b/primitives/keystore/src/testing.rs index 538c7910b771e..1138015abf055 100644 --- a/primitives/keystore/src/testing.rs +++ b/primitives/keystore/src/testing.rs @@ -332,7 +332,7 @@ mod tests { let preout = store.sr25519_vrf_preout(SR25519, &pair.public(), &input).unwrap().unwrap(); - let result = preout.output_bytes::<[u8; 32]>(b"rand", &input, &pair.public()); + let result = preout.output_bytes::<32>(b"rand", &input, &pair.public()); assert!(result.is_ok()); } From 468ef22c23108fe8f5e779e02f8059dd143afa95 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Thu, 27 Apr 2023 20:08:40 +0200 Subject: [PATCH 09/16] Trigger pipeline From f806adf7468456c16f5ffed6fd66791359f47813 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Sat, 29 Apr 2023 11:51:53 +0200 Subject: [PATCH 10/16] Define a separated type for vrf signature data --- client/consensus/babe/src/authorship.rs | 16 +- client/consensus/babe/src/tests.rs | 39 ++-- client/consensus/babe/src/verification.rs | 16 +- client/keystore/src/local.rs | 20 +- frame/babe/src/lib.rs | 12 +- frame/babe/src/mock.rs | 11 +- frame/babe/src/tests.rs | 6 +- primitives/consensus/babe/src/lib.rs | 11 +- primitives/core/src/crypto.rs | 20 +- primitives/core/src/sr25519.rs | 237 +++++++++++++--------- primitives/keystore/src/lib.rs | 10 +- primitives/keystore/src/testing.rs | 37 ++-- 12 files changed, 239 insertions(+), 196 deletions(-) diff --git a/client/consensus/babe/src/authorship.rs b/client/consensus/babe/src/authorship.rs index 93897310add7e..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_vrf_input, 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 vrf_input = make_vrf_input(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(), &vrf_input); + keystore.sr25519_vrf_sign(AuthorityId::ID, authority_id.as_ref(), &data); if let Ok(Some(vrf_signature)) = result { Some(PreDigest::SecondaryVRF(SecondaryVRFPreDigest { slot, @@ -239,19 +239,19 @@ fn claim_primary_slot( epoch_index = epoch.clone_for_slot(slot).epoch_index; } - let vrf_input = make_vrf_input(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(), &vrf_input); + 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() - .output_bytes::( + .make_bytes::( AUTHORING_SCORE_VRF_CONTEXT, - &vrf_input, - &vrf_signature.preout, + &data.as_ref(), + &vrf_signature.output, ) .map(|bytes| u128::from_le_bytes(bytes) < threshold) .unwrap_or_default(); diff --git a/client/consensus/babe/src/tests.rs b/client/consensus/babe/src/tests.rs index 2f312a3878087..aa3e32fa09d0a 100644 --- a/client/consensus/babe/src/tests.rs +++ b/client/consensus/babe/src/tests.rs @@ -29,7 +29,8 @@ 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_vrf_input, AllowedSlots, AuthorityId, AuthorityPair, Slot, + inherents::InherentDataProvider, make_vrf_sign_data, AllowedSlots, AuthorityId, AuthorityPair, + Slot, }; use sp_consensus_slots::SlotDuration; use sp_core::crypto::Pair; @@ -629,24 +630,18 @@ fn claim_vrf_check() { PreDigest::Primary(d) => d, v => panic!("Unexpected pre-digest variant {:?}", v), }; - let vrf_input = make_vrf_input(&epoch.randomness.clone(), 0.into(), epoch.epoch_index); - let sign = keystore - .sr25519_vrf_sign(AuthorityId::ID, &public, &vrf_input) - .unwrap() - .unwrap(); - assert_eq!(pre_digest.vrf_signature.preout, sign.preout); + 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 let pre_digest = match claim_slot(1.into(), &epoch, &keystore).unwrap().0 { PreDigest::SecondaryVRF(d) => d, v => panic!("Unexpected pre-digest variant {:?}", v), }; - let vrf_input = make_vrf_input(&epoch.randomness.clone(), 1.into(), epoch.epoch_index); - let sign = keystore - .sr25519_vrf_sign(AuthorityId::ID, &public, &vrf_input) - .unwrap() - .unwrap(); - assert_eq!(pre_digest.vrf_signature.preout, sign.preout); + 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) let slot = Slot::from(103); @@ -655,13 +650,10 @@ fn claim_vrf_check() { v => panic!("Unexpected claim variant {:?}", v), }; let fixed_epoch = epoch.clone_for_slot(slot); - let vrf_input = make_vrf_input(&epoch.randomness.clone(), slot, fixed_epoch.epoch_index); - let sign = keystore - .sr25519_vrf_sign(AuthorityId::ID, &public, &vrf_input) - .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.preout, sign.preout); + assert_eq!(claim.vrf_signature.output, sign.output); // Check that correct epoch index has been used if epochs are skipped (secondary VRF) let slot = Slot::from(100); @@ -670,13 +662,10 @@ fn claim_vrf_check() { v => panic!("Unexpected claim variant {:?}", v), }; let fixed_epoch = epoch.clone_for_slot(slot); - let vrf_input = make_vrf_input(&epoch.randomness.clone(), slot, fixed_epoch.epoch_index); - let sign = keystore - .sr25519_vrf_sign(AuthorityId::ID, &public, &vrf_input) - .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.preout, sign.preout); + assert_eq!(pre_digest.vrf_signature.output, sign.output); } // Propose and import a new BABE block on top of the given parent. diff --git a/client/consensus/babe/src/verification.rs b/client/consensus/babe/src/verification.rs index e9f546e91a114..3de5eacc2c519 100644 --- a/client/consensus/babe/src/verification.rs +++ b/client/consensus/babe/src/verification.rs @@ -30,7 +30,7 @@ use sp_consensus_babe::{ CompatibleDigestItem, PreDigest, PrimaryPreDigest, SecondaryPlainPreDigest, SecondaryVRFPreDigest, }, - make_vrf_input, AuthorityId, AuthorityPair, AuthoritySignature, + make_vrf_sign_data, AuthorityId, AuthorityPair, AuthoritySignature, }; use sp_consensus_slots::Slot; use sp_core::{ @@ -171,9 +171,9 @@ fn check_primary_header( return Err(babe_err(Error::BadSignature(pre_hash))) } - let vrf_input = make_vrf_input(&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(&vrf_input, &pre_digest.vrf_signature) { + if !authority_id.as_inner_ref().vrf_verify(&data, &pre_digest.vrf_signature) { return Err(babe_err(Error::VrfVerificationFailed)) } @@ -182,10 +182,10 @@ fn check_primary_header( let score = authority_id .as_inner_ref() - .output_bytes::( + .make_bytes::( AUTHORING_SCORE_VRF_CONTEXT, - &vrf_input, - &pre_digest.vrf_signature.preout, + &data.as_ref(), + &pre_digest.vrf_signature.output, ) .map(u128::from_le_bytes) .map_err(|_| babe_err(Error::VrfVerificationFailed))?; @@ -253,9 +253,9 @@ fn check_secondary_vrf_header( return Err(Error::BadSignature(pre_hash)) } - let vrf_input = make_vrf_input(&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(&vrf_input, &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 4efc8a4fb04b3..1e785113a4f03 100644 --- a/client/keystore/src/local.rs +++ b/client/keystore/src/local.rs @@ -102,27 +102,27 @@ impl LocalKeystore { &self, key_type: KeyTypeId, public: &T::Public, - input: &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(input)); + .map(|pair| pair.vrf_sign(data)); Ok(sig) } - fn vrf_preout( + fn vrf_output( &self, key_type: KeyTypeId, public: &T::Public, input: &T::VrfInput, - ) -> std::result::Result, TraitError> { + ) -> std::result::Result, TraitError> { let preout = self .0 .read() .key_pair_by_type::(public, key_type)? - .map(|pair| pair.vrf_preout(input)); + .map(|pair| pair.vrf_output(input)); Ok(preout) } } @@ -156,18 +156,18 @@ impl Keystore for LocalKeystore { &self, key_type: KeyTypeId, public: &sr25519::Public, - input: &sr25519::vrf::VrfInput, + data: &sr25519::vrf::VrfSignData, ) -> std::result::Result, TraitError> { - self.vrf_sign::(key_type, public, input) + self.vrf_sign::(key_type, public, data) } - fn sr25519_vrf_preout( + fn sr25519_vrf_output( &self, key_type: KeyTypeId, public: &sr25519::Public, input: &sr25519::vrf::VrfInput, - ) -> std::result::Result, TraitError> { - self.vrf_preout::(key_type, public, input) + ) -> 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 2fce068b62a35..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 vrf_input = sp_consensus_babe::make_vrf_input( + let transcript = sp_consensus_babe::make_vrf_transcript( &Self::randomness(), CurrentSlot::::get(), EpochIndex::::get(), @@ -373,15 +373,11 @@ pub mod pallet { // down the runtime. debug_assert!({ use sp_core::crypto::VrfPublic; - public.vrf_verify(&vrf_input, &vrf_signature) + public.vrf_verify(&transcript.clone().into_sign_data(), &signature) }); public - .output_bytes( - RANDOMNESS_VRF_CONTEXT, - &vrf_input, - &vrf_signature.preout, - ) + .make_bytes(RANDOMNESS_VRF_CONTEXT, &transcript, &signature.output) .ok() }); diff --git a/frame/babe/src/mock.rs b/frame/babe/src/mock.rs index b71c20015632a..96ebd818bce2c 100644 --- a/frame/babe/src/mock.rs +++ b/frame/babe/src/mock.rs @@ -314,17 +314,16 @@ pub fn make_secondary_vrf_pre_digest( Digest { logs: vec![log] } } -pub fn make_vrf_signature_and_output( +pub fn make_vrf_signature_and_randomness( slot: Slot, pair: &sp_consensus_babe::AuthorityPair, ) -> (VrfSignature, Randomness) { - let vrf_input = sp_consensus_babe::make_vrf_input(&Babe::randomness(), slot, 0); + let transcript = sp_consensus_babe::make_vrf_transcript(&Babe::randomness(), slot, 0); - let signature = pair.as_ref().vrf_sign(&vrf_input); + let randomness = + pair.as_ref().make_bytes(sp_consensus_babe::RANDOMNESS_VRF_CONTEXT, &transcript); - let randomness = pair - .as_ref() - .output_bytes(sp_consensus_babe::RANDOMNESS_VRF_CONTEXT, &vrf_input); + 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 102ceffc3647b..38edc2af7f272 100644 --- a/frame/babe/src/tests.rs +++ b/frame/babe/src/tests.rs @@ -64,7 +64,7 @@ fn first_block_epoch_zero_start() { ext.execute_with(|| { let genesis_slot = Slot::from(100); let (vrf_signature, vrf_randomness) = - make_vrf_signature_and_output(genesis_slot, &pairs[0]); + make_vrf_signature_and_randomness(genesis_slot, &pairs[0]); let pre_digest = make_primary_pre_digest(0, genesis_slot, vrf_signature); @@ -113,7 +113,7 @@ fn current_slot_is_processed_on_initialization() { ext.execute_with(|| { let genesis_slot = Slot::from(10); let (vrf_signature, vrf_randomness) = - make_vrf_signature_and_output(genesis_slot, &pairs[0]); + make_vrf_signature_and_randomness(genesis_slot, &pairs[0]); let pre_digest = make_primary_pre_digest(0, genesis_slot, vrf_signature); System::reset_events(); @@ -143,7 +143,7 @@ where ext.execute_with(|| { let genesis_slot = Slot::from(10); let (vrf_signature, vrf_randomness) = - make_vrf_signature_and_output(genesis_slot, &pairs[0]); + 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 043a978c3cf81..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::{VrfInput, VrfPreOutput, VrfProof, VrfSignature}; +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; @@ -95,7 +97,7 @@ pub type BabeAuthorityWeight = u64; pub type BabeBlockWeight = u32; /// Make VRF input suitable for BABE's randomness generation. -pub fn make_vrf_input(randomness: &Randomness, slot: Slot, epoch: u64) -> VrfInput { +pub fn make_vrf_transcript(randomness: &Randomness, slot: Slot, epoch: u64) -> VrfInput { VrfInput::new( &BABE_ENGINE_ID, &[ @@ -106,6 +108,11 @@ pub fn make_vrf_input(randomness: &Randomness, slot: Slot, epoch: u64) -> VrfInp ) } +/// 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 f3a429d15c615..e6fa331b308fc 100644 --- a/primitives/core/src/crypto.rs +++ b/primitives/core/src/crypto.rs @@ -1094,27 +1094,29 @@ impl<'a> TryFrom<&'a str> for KeyTypeId { /// Trait grouping types shared by a VRF signer and verifiers. pub trait VrfCrypto { - /// VRF input data. + /// VRF input. type VrfInput; - /// VRF pre-output generated from input data. - type VrfPreOutput; - /// Associated signature type. + /// VRF output generated from input data. + type VrfOutput; + /// VRF Auxcontext specific aux data. + type VrfSignData; + /// VRF signature. type VrfSignature; } /// VRF Secret Key. pub trait VrfSecret: VrfCrypto { - /// Get VRF pre-output. - fn vrf_preout(&self, data: &Self::VrfInput) -> Self::VrfPreOutput; + /// Get VRF-specific output . + fn vrf_output(&self, data: &Self::VrfInput) -> Self::VrfOutput; - /// Sign input data with optional pre-computed pre-output. - fn vrf_sign(&self, input: &Self::VrfInput) -> Self::VrfSignature; + /// Sign VRF-specific data. + fn vrf_sign(&self, input: &Self::VrfSignData) -> Self::VrfSignature; } /// 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 efe5dfdf20eb2..a155165ff0655 100644 --- a/primitives/core/src/sr25519.rs +++ b/primitives/core/src/sr25519.rs @@ -545,37 +545,75 @@ pub mod vrf { SignatureError, }; + /// Transcript ready to be used for VRF related operations. + #[derive(Clone)] + pub struct VrfTranscript(pub merlin::Transcript); + + impl VrfTranscript { + /// 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. // Note: here we may potentially add a closure to check-before-sign... - pub struct VrfInput { - /// VRF input data. - pub data: merlin::Transcript, - /// Extra data to be signed by the VRF. - pub extra: Option, - /// Optional pre-computed pre-output - pub preout: Option, + #[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, + /// Optional pre-computed output + pub(super) output: Option, + } + + impl From for VrfSignData { + fn from(transcript: VrfInput) -> Self { + VrfSignData { transcript, extra: None, output: None } + } } - impl VrfInput { - /// Build a new input ready to be used by a VRF signer/verifier. - 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)); - VrfInput { data: transcript, extra: None, preout: 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. + pub fn new(transcript: VrfTranscript) -> Self { + transcript.into() } - /// Add extra non-input data to be signed by the VRF. - /// Extra data doesn't contribute to the VRF output. - pub fn with_extra(mut self, 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)); + /// Add extra non-input data to be signed by the VRF signer. + pub fn with_extra(mut self, transcript: VrfTranscript) -> Self { self.extra = Some(transcript); self } - /// Add pre-output corresponding to `data` to be used during `sign` operation. - pub fn with_preout(mut self, preout: VrfPreOutput) -> Self { - self.preout = Some(preout); + /// Add pre-computed output to be used during `sign` operation. + /// + /// This output is computed by the main transcript. + /// If an output doesn't correspond to the main sign data `transcript` then + /// the signature will be invalid and its verification fails. + pub fn with_output(mut self, output: VrfOutput) -> Self { + self.output = Some(output); self } } @@ -583,36 +621,36 @@ pub mod vrf { /// VRF signature data #[derive(Clone, Debug, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo)] pub struct VrfSignature { - /// VRF pre-output. - pub preout: VrfPreOutput, + /// VRF output. + pub output: VrfOutput, /// VRF input proof. pub proof: VrfProof, } /// VRF output type suitable for schnorrkel operations. #[derive(Clone, Debug, PartialEq, Eq)] - pub struct VrfPreOutput(pub schnorrkel::vrf::VRFOutput); + pub struct VrfOutput(pub schnorrkel::vrf::VRFOutput); - impl Encode for VrfPreOutput { + impl Encode for VrfOutput { fn encode(&self) -> Vec { self.0.as_bytes().encode() } } - impl Decode for VrfPreOutput { + impl Decode for VrfOutput { fn decode(i: &mut R) -> Result { let decoded = <[u8; VRF_OUTPUT_LENGTH]>::decode(i)?; Ok(Self(schnorrkel::vrf::VRFOutput::from_bytes(&decoded).map_err(convert_error)?)) } } - impl MaxEncodedLen for VrfPreOutput { + impl MaxEncodedLen for VrfOutput { fn max_encoded_len() -> usize { <[u8; VRF_OUTPUT_LENGTH]>::max_encoded_len() } } - impl TypeInfo for VrfPreOutput { + impl TypeInfo for VrfOutput { type Identity = [u8; VRF_OUTPUT_LENGTH]; fn type_info() -> scale_info::Type { @@ -653,48 +691,52 @@ pub mod vrf { #[cfg(feature = "full_crypto")] impl VrfCrypto for Pair { - type VrfInput = VrfInput; - type VrfPreOutput = VrfPreOutput; + type VrfInput = VrfTranscript; + type VrfOutput = VrfOutput; + type VrfSignData = VrfSignData; type VrfSignature = VrfSignature; } #[cfg(feature = "full_crypto")] impl VrfSecret for Pair { - fn vrf_sign(&self, input: &Self::VrfInput) -> Self::VrfSignature { - let transcript = input.data.clone(); - let (inout, proof, _) = match input.extra { - Some(ref extra) => self.0.vrf_sign_extra(transcript, extra.clone()), + fn vrf_sign(&self, data: &Self::VrfSignData) -> Self::VrfSignature { + let transcript = data.transcript.0.clone(); + + let (inout, proof, _) = match data.extra { + Some(ref extra) => self.0.vrf_sign_extra(transcript, extra.0.clone()), None => self.0.vrf_sign(transcript), }; - let preout = input.preout.clone().unwrap_or_else(|| VrfPreOutput(inout.to_output())); - VrfSignature { preout, proof: VrfProof(proof) } + let output = data.output.clone().unwrap_or_else(|| VrfOutput(inout.to_output())); + VrfSignature { output, proof: VrfProof(proof) } } - fn vrf_preout(&self, input: &Self::VrfInput) -> Self::VrfPreOutput { - let preout = self.0.vrf_create_hash(input.data.clone()).to_output(); - VrfPreOutput(preout) + 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 VrfInput = VrfInput; - type VrfPreOutput = VrfPreOutput; + type VrfInput = VrfTranscript; + type VrfOutput = VrfOutput; + type VrfSignData = VrfSignData; type VrfSignature = VrfSignature; } impl VrfPublic for Public { - fn vrf_verify(&self, input: &Self::VrfInput, signature: &Self::VrfSignature) -> bool { + fn vrf_verify(&self, data: &Self::VrfSignData, signature: &Self::VrfSignature) -> bool { schnorrkel::PublicKey::from_bytes(self) .and_then(|public| { - let data = input.data.clone(); - match input.extra { + let transcript = data.transcript.0.clone(); + match data.extra { Some(ref extra) => public.vrf_verify_extra( - data, - &signature.preout.0, + transcript, + &signature.output.0, &signature.proof.0, - extra.clone(), + extra.0.clone(), ), - None => public.vrf_verify(data, &signature.preout.0, &signature.proof.0), + None => + public.vrf_verify(transcript, &signature.output.0, &signature.proof.0), } }) .is_ok() @@ -734,37 +776,37 @@ pub mod vrf { #[cfg(feature = "full_crypto")] impl Pair { - /// Generate bytes from the given VRF configuration. - pub fn output_bytes(&self, context: &[u8], input: &VrfInput) -> [u8; N] + /// 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.data.clone()); + 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 output_bytes( + /// Generate output bytes from the given VRF configuration. + pub fn make_bytes( &self, context: &[u8], input: &VrfInput, - preout: &VrfPreOutput, + output: &VrfOutput, ) -> Result<[u8; N], codec::Error> where [u8; N]: Default, { let pubkey = schnorrkel::PublicKey::from_bytes(&self.0).map_err(convert_error)?; let inout = - preout.0.attach_input_hash(&pubkey, input.data.clone()).map_err(convert_error)?; + output.0.attach_input_hash(&pubkey, input.0.clone()).map_err(convert_error)?; Ok(inout.make_bytes::<[u8; N]>(context)) } } - impl VrfPreOutput { - /// Generate bytes from the given VRF configuration. - pub fn output_bytes( + impl VrfOutput { + /// Generate output bytes from the given VRF configuration. + pub fn make_bytes( &self, context: &[u8], input: &VrfInput, @@ -773,7 +815,7 @@ pub mod vrf { where [u8; N]: Default, { - public.output_bytes(context, input, self) + public.make_bytes(context, input, self) } } } @@ -1015,11 +1057,11 @@ mod tests { let pair = Pair::from_seed(b"12345678901234567890123456789012"); let public = pair.public(); - let input = VrfInput::new(b"label", &[(b"domain1", b"data1")]); + let data = VrfTranscript::new(b"label", &[(b"domain1", b"data1")]).into(); - let signature = pair.vrf_sign(&input); + let signature = pair.vrf_sign(&data); - assert!(public.vrf_verify(&input, &signature)); + assert!(public.vrf_verify(&data, &signature)); } #[test] @@ -1027,77 +1069,84 @@ mod tests { let pair = Pair::from_seed(b"12345678901234567890123456789012"); let public = pair.public(); - let input = VrfInput::new(b"label", &[(b"domain1", b"data1")]) - .with_extra(b"extra", &[(b"domain2", b"data2")]); + 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(&input); + let signature = pair.vrf_sign(&data); - assert!(public.vrf_verify(&input, &signature)); + assert!(public.vrf_verify(&data, &signature)); } #[test] - fn vrf_sign_verify_with_preout() { + fn vrf_sign_verify_with_output() { 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")]) - .with_extra(b"extra", &[(b"domain2", b"data2")]); + let input = VrfTranscript::new(b"label", &[(b"domain1", b"data1")]); + let output = pair.vrf_output(&input); - let preout = pair.vrf_preout(&input); + let data = input.into_sign_data().with_output(output); - let out1 = pair.output_bytes::<32>(ctx, &input); - let out2 = preout.output_bytes::<32>(ctx, &input, &public).unwrap(); - assert_eq!(out1, out2); + let signature = pair.vrf_sign(&data); - let input = input.with_preout(preout); - let signature = pair.vrf_sign(&input); - - assert!(public.vrf_verify(&input, &signature)); + assert!(public.vrf_verify(&data, &signature)); } #[test] fn vrf_make_bytes_matches() { let pair = Pair::from_seed(b"12345678901234567890123456789012"); let public = pair.public(); - let input = VrfInput::new(b"label", &[(b"domain", b"data")]); let ctx = b"vrfbytes"; - let signature = pair.vrf_sign(&input); + let input = VrfTranscript::new(b"label", &[(b"domain1", b"data1")]); + + let output = pair.vrf_output(&input); + + let out1 = pair.make_bytes::<32>(ctx, &input); + let out2 = output.make_bytes::<32>(ctx, &input, &public).unwrap(); + assert_eq!(out1, out2); - let b1 = pair.output_bytes::<32>(ctx, &input); - let b2 = public.output_bytes::<32>(ctx, &input, &signature.preout).unwrap(); - assert_eq!(b1, b2); + let extra = VrfTranscript::new(b"extra", &[(b"domain2", b"data2")]); + let data = input.clone().into_sign_data().with_extra(extra).with_output(output); + 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 input = VrfInput::new(b"label", &[(b"domain1", b"data1")]) - .with_extra(b"extra", &[(b"domain2", b"data2")]); let ctx = b"vrfbytes"; - let signature = pair.vrf_sign(&input); - assert!(public.vrf_verify(&input, &signature)); + 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.output_bytes::<32>(ctx, &input); - let out2 = public.output_bytes::<32>(ctx, &input, &signature.preout).unwrap(); + 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.data.clone(), |inout| { + .vrf_sign_extra_after_check(input.0.clone(), |inout| { let out3 = inout.make_bytes::<[u8; 32]>(ctx); assert_eq!(out2, out3); - input.extra.clone() + Some(extra.0.clone()) }) .unwrap(); let signature2 = - VrfSignature { preout: VrfPreOutput(inout.to_output()), proof: VrfProof(proof) }; + VrfSignature { output: VrfOutput(inout.to_output()), proof: VrfProof(proof) }; - assert!(public.vrf_verify(&input, &signature2)); - assert_eq!(signature.preout, signature2.preout); + 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 fe6bdb4c2e62a..d6165088f6b46 100644 --- a/primitives/keystore/src/lib.rs +++ b/primitives/keystore/src/lib.rs @@ -72,7 +72,7 @@ pub trait Keystore: Send + Sync { msg: &[u8], ) -> Result, Error>; - /// Generate an sr25519 VRF signature for a given input 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. @@ -83,22 +83,22 @@ pub trait Keystore: Send + Sync { &self, key_type: KeyTypeId, public: &sr25519::Public, - input: &sr25519::vrf::VrfInput, + data: &sr25519::vrf::VrfSignData, ) -> Result, Error>; - /// Generate an sr25519 VRF pre-output for a given input data. + /// 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_preout( + fn sr25519_vrf_output( &self, key_type: KeyTypeId, public: &sr25519::Public, input: &sr25519::vrf::VrfInput, - ) -> Result, Error>; + ) -> 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 1138015abf055..dd3254e9371c4 100644 --- a/primitives/keystore/src/testing.rs +++ b/primitives/keystore/src/testing.rs @@ -103,19 +103,19 @@ impl MemoryKeystore { &self, key_type: KeyTypeId, public: &T::Public, - input: &T::VrfInput, + data: &T::VrfSignData, ) -> Result, Error> { - let sig = self.pair::(key_type, public).map(|pair| pair.vrf_sign(input)); + let sig = self.pair::(key_type, public).map(|pair| pair.vrf_sign(data)); Ok(sig) } - fn vrf_preout( + 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_preout(input)); + ) -> Result, Error> { + let preout = self.pair::(key_type, public).map(|pair| pair.vrf_output(input)); Ok(preout) } } @@ -146,18 +146,18 @@ impl Keystore for MemoryKeystore { &self, key_type: KeyTypeId, public: &sr25519::Public, - input: &sr25519::vrf::VrfInput, + data: &sr25519::vrf::VrfSignData, ) -> Result, Error> { - self.vrf_sign::(key_type, public, input) + self.vrf_sign::(key_type, public, data) } - fn sr25519_vrf_preout( + fn sr25519_vrf_output( &self, key_type: KeyTypeId, public: &sr25519::Public, input: &sr25519::vrf::VrfInput, - ) -> Result, Error> { - self.vrf_preout::(key_type, public, input) + ) -> Result, Error> { + self.vrf_output::(key_type, public, input) } fn ed25519_public_keys(&self, key_type: KeyTypeId) -> Vec { @@ -286,29 +286,30 @@ mod tests { let secret_uri = "//Alice"; let key_pair = sr25519::Pair::from_string(secret_uri, None).expect("Generates key pair"); - let input = sr25519::vrf::VrfInput::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(), &input); + 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(), &input); + let result = store.sr25519_vrf_sign(SR25519, &key_pair.public(), &data); assert!(result.unwrap().is_some()); } #[test] - fn vrf_preout() { + fn vrf_output() { let store = MemoryKeystore::new(); let secret_uri = "//Alice"; @@ -323,16 +324,16 @@ mod tests { ], ); - let result = store.sr25519_vrf_preout(SR25519, &pair.public(), &input); + 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_preout(SR25519, &pair.public(), &input).unwrap().unwrap(); + let preout = store.sr25519_vrf_output(SR25519, &pair.public(), &input).unwrap().unwrap(); - let result = preout.output_bytes::<32>(b"rand", &input, &pair.public()); + let result = preout.make_bytes::<32>(b"rand", &input, &pair.public()); assert!(result.is_ok()); } From 4d0cb2eade4c7cea458c2d2bd42e589bba1c66be Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Sat, 29 Apr 2023 12:57:38 +0200 Subject: [PATCH 11/16] Fix docs --- primitives/core/src/crypto.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/primitives/core/src/crypto.rs b/primitives/core/src/crypto.rs index e6fa331b308fc..a218a1bf36343 100644 --- a/primitives/core/src/crypto.rs +++ b/primitives/core/src/crypto.rs @@ -1096,9 +1096,9 @@ impl<'a> TryFrom<&'a str> for KeyTypeId { pub trait VrfCrypto { /// VRF input. type VrfInput; - /// VRF output generated from input data. + /// VRF output. type VrfOutput; - /// VRF Auxcontext specific aux data. + /// VRF signing data. type VrfSignData; /// VRF signature. type VrfSignature; From eeb6ddc7c3b57a18f05dd5a7e908b3a18acc07ca Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Sat, 29 Apr 2023 13:11:07 +0200 Subject: [PATCH 12/16] Fix doc --- primitives/core/src/sr25519.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/primitives/core/src/sr25519.rs b/primitives/core/src/sr25519.rs index a155165ff0655..277937fbc0f8b 100644 --- a/primitives/core/src/sr25519.rs +++ b/primitives/core/src/sr25519.rs @@ -623,7 +623,7 @@ pub mod vrf { pub struct VrfSignature { /// VRF output. pub output: VrfOutput, - /// VRF input proof. + /// VRF proof. pub proof: VrfProof, } From 19d9c7aea5708a015b1dd718da5eea5e32fd3b3d Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Wed, 3 May 2023 19:26:01 +0200 Subject: [PATCH 13/16] Remove annotation --- primitives/core/src/sr25519.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/primitives/core/src/sr25519.rs b/primitives/core/src/sr25519.rs index 277937fbc0f8b..f3499dc85ac12 100644 --- a/primitives/core/src/sr25519.rs +++ b/primitives/core/src/sr25519.rs @@ -571,7 +571,6 @@ pub mod vrf { pub type VrfInput = VrfTranscript; /// VRF input ready to be used for VRF sign and verify operations. - // Note: here we may potentially add a closure to check-before-sign... #[derive(Clone)] pub struct VrfSignData { /// Transcript data contributing to VRF output. From 5af19b853d456b6a30594b49946f4bb1bf9afa4c Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Wed, 3 May 2023 20:02:23 +0200 Subject: [PATCH 14/16] Directly use dleq_proove and dleq_verify in sr25519 --- primitives/core/src/sr25519.rs | 49 +++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/primitives/core/src/sr25519.rs b/primitives/core/src/sr25519.rs index f3499dc85ac12..b697842cb918c 100644 --- a/primitives/core/src/sr25519.rs +++ b/primitives/core/src/sr25519.rs @@ -545,6 +545,8 @@ pub mod vrf { SignatureError, }; + 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); @@ -699,14 +701,17 @@ pub mod vrf { #[cfg(feature = "full_crypto")] impl VrfSecret for Pair { fn vrf_sign(&self, data: &Self::VrfSignData) -> Self::VrfSignature { - let transcript = data.transcript.0.clone(); + let inout = self.0.vrf_create_hash(data.transcript.0.clone()); - let (inout, proof, _) = match data.extra { - Some(ref extra) => self.0.vrf_sign_extra(transcript, extra.0.clone()), - None => self.0.vrf_sign(transcript), - }; - let output = data.output.clone().unwrap_or_else(|| VrfOutput(inout.to_output())); - VrfSignature { output, proof: VrfProof(proof) } + 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 { @@ -724,21 +729,21 @@ pub mod vrf { impl VrfPublic for Public { fn vrf_verify(&self, data: &Self::VrfSignData, signature: &Self::VrfSignature) -> bool { - schnorrkel::PublicKey::from_bytes(self) - .and_then(|public| { - let transcript = data.transcript.0.clone(); - match data.extra { - Some(ref extra) => public.vrf_verify_extra( - transcript, - &signature.output.0, - &signature.proof.0, - extra.0.clone(), - ), - None => - public.vrf_verify(transcript, &signature.output.0, &signature.proof.0), - } - }) - .is_ok() + 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() } } From 7f5e7e048a08e48503c9823dbee0000b042d5260 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Thu, 4 May 2023 12:18:08 +0200 Subject: [PATCH 15/16] Trigger CI From 4f1342b3b89f456e5f9207b49f5db985918b47d0 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Thu, 4 May 2023 12:57:26 +0200 Subject: [PATCH 16/16] Remove cruft before merge --- primitives/core/src/sr25519.rs | 43 ++++++++-------------------------- 1 file changed, 10 insertions(+), 33 deletions(-) diff --git a/primitives/core/src/sr25519.rs b/primitives/core/src/sr25519.rs index b697842cb918c..fead9e2a09df3 100644 --- a/primitives/core/src/sr25519.rs +++ b/primitives/core/src/sr25519.rs @@ -579,13 +579,11 @@ pub mod vrf { pub(super) transcript: VrfTranscript, /// Extra transcript data to be signed by the VRF. pub(super) extra: Option, - /// Optional pre-computed output - pub(super) output: Option, } impl From for VrfSignData { fn from(transcript: VrfInput) -> Self { - VrfSignData { transcript, extra: None, output: None } + VrfSignData { transcript, extra: None } } } @@ -598,23 +596,17 @@ pub mod vrf { impl VrfSignData { /// Build a new instance ready to be used for VRF signer and verifier. - pub fn new(transcript: VrfTranscript) -> Self { - transcript.into() - } - - /// Add extra non-input data to be signed by the VRF signer. - pub fn with_extra(mut self, transcript: VrfTranscript) -> Self { - self.extra = Some(transcript); - self + /// + /// `input` will contribute to the VRF output bytes. + pub fn new(input: VrfTranscript) -> Self { + input.into() } - /// Add pre-computed output to be used during `sign` operation. + /// Add some extra data to be signed. /// - /// This output is computed by the main transcript. - /// If an output doesn't correspond to the main sign data `transcript` then - /// the signature will be invalid and its verification fails. - pub fn with_output(mut self, output: VrfOutput) -> Self { - self.output = Some(output); + /// `extra` will not contribute to the VRF output bytes. + pub fn with_extra(mut self, extra: VrfTranscript) -> Self { + self.extra = Some(extra); self } } @@ -1083,21 +1075,6 @@ mod tests { assert!(public.vrf_verify(&data, &signature)); } - #[test] - fn vrf_sign_verify_with_output() { - let pair = Pair::from_seed(b"12345678901234567890123456789012"); - let public = pair.public(); - - let input = VrfTranscript::new(b"label", &[(b"domain1", b"data1")]); - let output = pair.vrf_output(&input); - - let data = input.into_sign_data().with_output(output); - - let signature = pair.vrf_sign(&data); - - assert!(public.vrf_verify(&data, &signature)); - } - #[test] fn vrf_make_bytes_matches() { let pair = Pair::from_seed(b"12345678901234567890123456789012"); @@ -1113,7 +1090,7 @@ mod tests { assert_eq!(out1, out2); let extra = VrfTranscript::new(b"extra", &[(b"domain2", b"data2")]); - let data = input.clone().into_sign_data().with_extra(extra).with_output(output); + let data = input.clone().into_sign_data().with_extra(extra); let signature = pair.vrf_sign(&data); assert!(public.vrf_verify(&data, &signature));