jam_std_common/crypto/
bandersnatch.rs

1//! VRFs backed by [Bandersnatch](https://neuromancer.sk/std/bls/Bandersnatch),
2//! an elliptic curve built over BLS12-381 scalar field.
3//!
4//! The primitive can operate both as a regular VRF or as an anonymized Ring VRF.
5
6use super::concat;
7use crate::{simple::TrancheIndex, Entropy, TicketId};
8use ark_vrf::{
9	reexports::ark_serialize::{CanonicalDeserialize, CanonicalSerialize},
10	suites::bandersnatch::{self, AffinePoint, Secret as SecretImpl},
11};
12use codec::{Decode, Encode, MaxEncodedLen};
13use jam_types::{AnyVec, OpaqueBandersnatchPublic, TicketAttempt};
14
15pub use ark_vrf::Error;
16
17const SCALAR_SERIALIZED_SIZE: usize = 32;
18const POINT_SERIALIZED_SIZE: usize = 32;
19
20pub const PUBLIC_SERIALIZED_SIZE: usize = POINT_SERIALIZED_SIZE;
21pub const PREOUT_SERIALIZED_SIZE: usize = POINT_SERIALIZED_SIZE;
22
23const ERR_MSG: &str = "object length is constant and checked by test; qed";
24
25/// The raw secret seed, which can be used to reconstruct the secret [`Secret`].
26type Seed = [u8; SCALAR_SERIALIZED_SIZE];
27
28/// Bandersnatch public key.
29#[derive(Clone, Copy, PartialEq, Eq, Hash, Encode, Decode, MaxEncodedLen, Default)]
30pub struct Public(pub [u8; PUBLIC_SERIALIZED_SIZE]);
31
32impl Public {
33	pub fn is_valid(&self) -> bool {
34		AffinePoint::deserialize_compressed_unchecked(&self.0[..]).is_ok()
35	}
36}
37
38impl std::fmt::Debug for Public {
39	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40		write!(f, "{:?}", AnyVec(self.0.to_vec()))
41	}
42}
43
44impl std::fmt::Display for Public {
45	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46		write!(f, "{}", AnyVec(self.0.to_vec()))
47	}
48}
49
50impl From<OpaqueBandersnatchPublic> for Public {
51	fn from(opaque: OpaqueBandersnatchPublic) -> Self {
52		Self(opaque.0)
53	}
54}
55impl From<Public> for OpaqueBandersnatchPublic {
56	fn from(public: Public) -> Self {
57		Self(public.0)
58	}
59}
60
61/// Bandersnatch secret key.
62#[derive(Clone)]
63pub struct Secret(SecretImpl);
64
65impl Secret {
66	/// Construct a new secret from the given `rng`.
67	pub fn new(rng: &mut impl rand::CryptoRng) -> Self {
68		Self::from_seed(rand::Rng::random(rng))
69	}
70
71	/// Construct from seed.
72	pub fn from_seed(seed: Seed) -> Self {
73		Self(SecretImpl::from_seed(&seed))
74	}
75
76	/// Get public key component.
77	pub fn public(&self) -> Public {
78		let public = self.0.public();
79		let mut raw = [0; PUBLIC_SERIALIZED_SIZE];
80		public.serialize_compressed(raw.as_mut_slice()).expect(ERR_MSG);
81		Public(raw)
82	}
83
84	/// Generate from system entropy.
85	pub fn generate() -> Self {
86		Self::new(&mut rand::rng())
87	}
88}
89
90/// A message signed by a node's Bandersnatch key.
91pub enum Message {
92	/// RingVRF ticket generation and regular block seal.
93	TicketSeal(Entropy, TicketAttempt),
94	/// Fallback block seal.
95	FallbackSeal(Entropy),
96	/// On-chain entropy generation.
97	Entropy(TicketId),
98	// Audit selection entropy for initial tranche
99	AuditInitial(Entropy),
100	// Audit selection entropy for subsequent tranches
101	AuditSubsequent(Entropy, jam_types::WorkReportHash, TrancheIndex),
102}
103
104impl Message {
105	/// Call `f` with the encoded message.
106	pub fn using_encoded<R>(&self, f: impl FnOnce(&[u8]) -> R) -> R {
107		match self {
108			Self::TicketSeal(entropy, attempt) =>
109				f(concat(&mut [0; 15 + 32 + 1], &[b"jam_ticket_seal", &entropy.0, &[*attempt]])),
110			Self::FallbackSeal(entropy) =>
111				f(concat(&mut [0; 17 + 32], &[b"jam_fallback_seal", &entropy.0])),
112			Self::Entropy(ticket_id) =>
113				f(concat(&mut [0; 11 + 32], &[b"jam_entropy", &ticket_id.0])),
114			Self::AuditInitial(entropy_source) =>
115				f(concat(&mut [0; 9 + 32], &[b"jam_audit", &entropy_source.0])),
116			Self::AuditSubsequent(entropy_source, report, index) => f(concat(
117				&mut [0; 9 + 64 + 1],
118				&[b"jam_audit", &entropy_source.0, &report.0, &[*index]],
119			)),
120		}
121	}
122}
123
124/// Generate VRF input point from raw bytes.
125#[inline(always)]
126fn vrf_input(message: &Message) -> bandersnatch::Input {
127	message
128		.using_encoded(|enc| bandersnatch::Input::new(enc).expect("Elligator2 H2C is infallible"))
129}
130
131/// Bandersnatch VRF types and operations.
132pub mod vrf {
133	use super::*;
134	use ark_vrf::ietf::{Prover, Verifier};
135	use codec::ConstEncodedLen;
136
137	pub(crate) const PROOF_SERIALIZED_SIZE: usize = 64;
138	pub const SIGNATURE_SERIALIZED_SIZE: usize = PREOUT_SERIALIZED_SIZE + PROOF_SERIALIZED_SIZE;
139
140	/// VRF signature.
141	#[derive(Clone, Debug, PartialEq, Eq, Encode, Decode, MaxEncodedLen)]
142	pub struct Signature(pub [u8; SIGNATURE_SERIALIZED_SIZE]);
143
144	impl ConstEncodedLen for Signature {}
145
146	impl Signature {
147		/// Generate VRF output bytes from the signature.
148		pub fn vrf_output(&self) -> Entropy {
149			bandersnatch::Output::deserialize_compressed_unchecked(
150				&self.0[..PREOUT_SERIALIZED_SIZE],
151			)
152			.map(|p| {
153				let mut raw = [0u8; 32];
154				raw.copy_from_slice(&p.hash()[..32]);
155				Entropy(raw)
156			})
157			.unwrap_or_default()
158		}
159
160		/// Verify the signature.
161		pub fn vrf_verify(
162			&self,
163			message: &Message,
164			aux_data: &[u8],
165			public: &Public,
166		) -> Result<(), Error> {
167			public.vrf_verify(message, aux_data, self)
168		}
169	}
170
171	impl Secret {
172		/// Creates a VRF signature of `message` and `aux_data`.
173		///
174		/// Auxiliary data (`aux_data`) is signed but doesn't contribute to the VRF output.
175		pub fn vrf_sign(&self, message: &Message, aux_data: &[u8]) -> Signature {
176			let input = vrf_input(message);
177			let output = self.0.output(input);
178			let proof = self.0.prove(input, output, aux_data);
179			let mut raw = [0_u8; SIGNATURE_SERIALIZED_SIZE];
180			output.serialize_compressed(&mut raw[..PREOUT_SERIALIZED_SIZE]).expect(ERR_MSG);
181			proof.serialize_compressed(&mut raw[PREOUT_SERIALIZED_SIZE..]).expect(ERR_MSG);
182			Signature(raw)
183		}
184
185		/// Generate VRF output from the given `message`.
186		pub fn vrf_output(&self, message: &Message) -> Entropy {
187			let input = vrf_input(message);
188			let hash = self.0.output(input).hash();
189			let mut raw = [0u8; 32];
190			raw.copy_from_slice(&hash[..32]);
191			Entropy(raw)
192		}
193	}
194
195	impl Public {
196		/// Verify a VRF signature.
197		pub fn vrf_verify(
198			&self,
199			message: &Message,
200			aux_data: &[u8],
201			signature: &Signature,
202		) -> Result<(), Error> {
203			let public = bandersnatch::Public::deserialize_compressed_unchecked(self.0.as_ref())
204				.map_err(|_| Error::InvalidData)?;
205			let output = bandersnatch::Output::deserialize_compressed_unchecked(
206				&signature.0[..PREOUT_SERIALIZED_SIZE],
207			)
208			.map_err(|_| Error::InvalidData)?;
209			let proof = bandersnatch::IetfProof::deserialize_compressed_unchecked(
210				&signature.0[PREOUT_SERIALIZED_SIZE..],
211			)
212			.map_err(|_| Error::InvalidData)?;
213			let input = vrf_input(message);
214			public.verify(input, output, aux_data, &proof)
215		}
216	}
217
218	impl AsRef<[u8]> for Public {
219		fn as_ref(&self) -> &[u8] {
220			&self.0
221		}
222	}
223}
224
225pub mod ring_vrf {
226	use super::*;
227	use ark_vrf::ring::{Prover, Verifier};
228	use bandersnatch::{RingCommitment as RingCommitmentImpl, RingProofParams};
229	pub use bandersnatch::{RingProver, RingVerifier};
230
231	pub const RING_COMMITMENT_SERIALIZED_SIZE: usize = 144;
232
233	pub const RING_PROOF_SERIALIZED_SIZE: usize = 752;
234
235	pub const RING_SIGNATURE_SERIALIZED_SIZE: usize =
236		PREOUT_SERIALIZED_SIZE + RING_PROOF_SERIALIZED_SIZE;
237
238	#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen)]
239	pub struct RingCommitment(pub [u8; RING_COMMITMENT_SERIALIZED_SIZE]);
240
241	/// Ring VRF signature.
242	#[derive(Clone, Debug, PartialEq, Eq, Encode, Decode, MaxEncodedLen)]
243	pub struct Signature(pub [u8; RING_SIGNATURE_SERIALIZED_SIZE]);
244
245	impl Signature {
246		/// Verify a ring-vrf signature.
247		///
248		/// The signature is verifiable if it has been produced by a member of the ring
249		/// from which the [`RingVerifier`] has been constructed.
250		pub fn ring_vrf_verify(
251			&self,
252			message: &Message,
253			aux_data: &[u8],
254			verifier: &RingVerifier,
255		) -> Result<(), Error> {
256			let output = bandersnatch::Output::deserialize_compressed_unchecked(
257				&self.0[..PREOUT_SERIALIZED_SIZE],
258			)
259			.map_err(|_| Error::InvalidData)?;
260			let proof = bandersnatch::RingProof::deserialize_compressed_unchecked(
261				&self.0[PREOUT_SERIALIZED_SIZE..],
262			)
263			.map_err(|_| Error::InvalidData)?;
264			let input = vrf_input(message);
265			bandersnatch::Public::verify(input, output, aux_data, &proof, verifier)
266		}
267
268		/// Generate VRF output bytes from the signature.
269		pub fn vrf_output(&self) -> Entropy {
270			bandersnatch::Output::deserialize_compressed_unchecked(
271				&self.0[..PREOUT_SERIALIZED_SIZE],
272			)
273			.map(|p| {
274				let mut raw = [0u8; 32];
275				raw.copy_from_slice(&p.hash()[..32]);
276				Entropy(raw)
277			})
278			.unwrap_or_default()
279		}
280	}
281
282	impl Secret {
283		/// Construct a Ring VRF signature.
284		///
285		/// Auxiliary data (`aux_data`) is signed but doesn't contribute to the VRF output.
286		pub fn ring_vrf_sign(
287			&self,
288			message: &Message,
289			aux_data: &[u8],
290			prover: &RingProver,
291		) -> Signature {
292			let input = vrf_input(message);
293			let output = self.0.output(input);
294			let proof = self.0.prove(input, output, aux_data, prover);
295			let mut raw = [0_u8; RING_SIGNATURE_SERIALIZED_SIZE];
296			output.serialize_compressed(&mut raw[..PREOUT_SERIALIZED_SIZE]).expect(ERR_MSG);
297			proof.serialize_compressed(&mut raw[PREOUT_SERIALIZED_SIZE..]).expect(ERR_MSG);
298			Signature(raw)
299		}
300	}
301
302	/// Ring context.
303	#[derive(Clone)]
304	pub struct RingContext;
305
306	impl RingContext {
307		/// Get a reference to the ring context instance.
308		pub fn params() -> &'static RingProofParams {
309			use std::sync::OnceLock;
310			static PARAMS: OnceLock<RingProofParams> = OnceLock::new();
311			PARAMS.get_or_init(|| {
312				use bandersnatch::PcsParams;
313				// Zcash SRS file derived from (https://zfnd.org/conclusion-of-the-powers-of-tau-ceremony).
314				let buf = include_bytes!(concat!(
315					env!("CARGO_MANIFEST_DIR"),
316					"/data/zcash-srs-2-11-uncompressed.bin"
317				));
318				let pcs_params = PcsParams::deserialize_uncompressed_unchecked(&mut &buf[..])
319					.expect("Error deserializing PcsParam");
320				RingProofParams::from_pcs_params(jam_types::val_count() as usize, pcs_params)
321					.expect("Error constructing RingProofParams from PcsParams")
322			})
323		}
324
325		/// Get ring prover for the key at index `public_index` in the `public_keys` sequence.
326		///
327		/// Public keys sequence order matters.
328		pub fn prover(public_keys: &[Public], public_index: usize) -> RingProver {
329			let params = Self::params();
330			let pks = public_to_affine(public_keys);
331			let prover_key = params.prover_key(&pks);
332			params.prover(prover_key, public_index)
333		}
334
335		/// Get ring verifier for the `public_keys` sequence.
336		///
337		/// Public keys sequence order matters.
338		pub fn verifier(public_keys: &[Public]) -> RingVerifier {
339			let params = Self::params();
340			let pks = public_to_affine(public_keys);
341			let verifier_key = params.verifier_key(&pks);
342			params.verifier(verifier_key)
343		}
344
345		/// Get ring commitment which may be used for lazy ring verifier construction.
346		///
347		/// Public keys sequence order matters.
348		pub fn commitment(public_keys: &[Public]) -> RingCommitment {
349			let params = Self::params();
350			let pks = public_to_affine(public_keys);
351			let verifier_key = params.verifier_key(&pks);
352			let commitment = verifier_key.commitment();
353			let mut raw = [0; RING_COMMITMENT_SERIALIZED_SIZE];
354			commitment.serialize_compressed(&mut raw[..]).expect(ERR_MSG);
355			RingCommitment(raw)
356		}
357
358		/// Construct a ring verifier using the ring commitment.
359		pub fn verifier_from_commitment(commitment: RingCommitment) -> Result<RingVerifier, Error> {
360			let params = Self::params();
361			let mut raw = &commitment.0[..];
362			let commitment = RingCommitmentImpl::deserialize_compressed_unchecked(&mut raw)
363				.map_err(|_| Error::InvalidData)?;
364			let verifier_key = params.verifier_key_from_commitment(commitment);
365			Ok(params.verifier(verifier_key))
366		}
367	}
368
369	/// Maps a sequence of public keys to affine points on the Bandersnatch curve.
370	///
371	/// Any Public that cannot be mapped to a specific point will have the padding point used in its
372	/// place.
373	#[inline(always)]
374	fn public_to_affine(pks: &[Public]) -> Vec<AffinePoint> {
375		pks.iter()
376			.map(|pk| {
377				AffinePoint::deserialize_compressed_unchecked(&pk.0[..])
378					.unwrap_or(RingProofParams::padding_point())
379			})
380			.collect()
381	}
382}
383
384#[cfg(all(test, feature = "full-test-suite"))]
385mod tests {
386	#![allow(clippy::unwrap_used)]
387	use super::{ring_vrf::*, vrf::*, *};
388	use crate::NewNull;
389	use ring_vrf::RING_COMMITMENT_SERIALIZED_SIZE;
390
391	const SEED_SERIALIZED_SIZE: usize = 32;
392	const RING_KEYSET_SIZE: usize = 1023;
393
394	const DEV_SEED: [u8; SEED_SERIALIZED_SIZE] = [0xcb; SEED_SERIALIZED_SIZE];
395
396	#[allow(dead_code)]
397	fn serialize<T: CanonicalSerialize>(obj: &T) -> Vec<u8> {
398		let mut buf = Vec::new();
399		obj.serialize_compressed(&mut buf).unwrap();
400		buf
401	}
402
403	#[test]
404	fn backend_assumptions_check() {
405		use ark_vrf::reexports::ark_std;
406		use bandersnatch::RingProofParams;
407		const EXPECTED_DOMAIN_OVERHEAD: usize = 257;
408
409		let ctx = RingProofParams::from_seed(RING_KEYSET_SIZE, [0; 32]);
410
411		let expected_domain_size = 1 << ark_std::log2(RING_KEYSET_SIZE + EXPECTED_DOMAIN_OVERHEAD);
412		assert_eq!(ctx.max_ring_size(), expected_domain_size - EXPECTED_DOMAIN_OVERHEAD);
413
414		let pks: Vec<_> =
415			(0..16).map(|i| SecretImpl::from_seed(&[i as u8; 32]).public().0).collect();
416
417		let secret = SecretImpl::from_seed(&[0u8; 32]);
418
419		let public = secret.public();
420		assert_eq!(public.compressed_size(), PUBLIC_SERIALIZED_SIZE);
421
422		let input = vrf_input(&Message::FallbackSeal(Default::default()));
423		let output = secret.output(input);
424		assert_eq!(output.compressed_size(), PREOUT_SERIALIZED_SIZE);
425
426		let prover_key = ctx.prover_key(&pks);
427		let prover = ctx.prover(prover_key, 0);
428
429		let verifier_key = ctx.verifier_key(&pks);
430		let commitment = verifier_key.commitment();
431		assert_eq!(commitment.compressed_size(), RING_COMMITMENT_SERIALIZED_SIZE);
432
433		let ietf_proof = {
434			use ark_vrf::ietf::Prover;
435			secret.prove(input, output, [])
436		};
437		assert_eq!(ietf_proof.compressed_size(), PROOF_SERIALIZED_SIZE);
438
439		let ring_proof = {
440			use ark_vrf::ring::Prover;
441			secret.prove(input, output, [], &prover)
442		};
443		assert_eq!(ring_proof.compressed_size(), RING_PROOF_SERIALIZED_SIZE);
444
445		// Well known Bandersnatch padding point defined within the Bandersnatch-EC-VRFs
446		// specification: https://github.com/davxy/bandersnatch-vrfs-spec
447		let padding_raw = [
448			0x92, 0xca, 0x79, 0xe6, 0x1d, 0xd9, 0x0c, 0x15, 0x73, 0xa8, 0x69, 0x3f, 0x19, 0x9b,
449			0xf6, 0xe1, 0xe8, 0x68, 0x35, 0xcc, 0x71, 0x5c, 0xdc, 0xf9, 0x3f, 0x5e, 0xf2, 0x22,
450			0x56, 0x00, 0x23, 0xaa,
451		];
452		let padding_point = AffinePoint::deserialize_compressed(&padding_raw[..]).unwrap();
453		assert_eq!(RingProofParams::padding_point(), padding_point);
454	}
455
456	fn encode_decode<T: Encode + Decode + PartialEq + std::fmt::Debug>(expected_len: usize) {
457		let obj = T::new_null();
458		let buf = obj.encode();
459		assert_eq!(buf.len(), expected_len);
460		let dec = T::decode(&mut buf.as_slice()).unwrap();
461		assert_eq!(obj, dec);
462	}
463
464	#[test]
465	fn codec_works() {
466		encode_decode::<Public>(PUBLIC_SERIALIZED_SIZE);
467		encode_decode::<vrf::Signature>(SIGNATURE_SERIALIZED_SIZE);
468		encode_decode::<ring_vrf::Signature>(RING_SIGNATURE_SERIALIZED_SIZE);
469	}
470
471	#[test]
472	fn vrf_sign_verify() {
473		let secret = Secret::from_seed(DEV_SEED);
474		let public = secret.public();
475
476		let input = Message::FallbackSeal(Default::default());
477		let ad = b"data";
478
479		let signature = secret.vrf_sign(&input, ad);
480
481		assert!(public.vrf_verify(&input, ad, &signature).is_ok());
482
483		// Fail with bad additional data
484		assert!(public.vrf_verify(&input, b"bad", &signature).is_err());
485
486		// Fail with bad input
487		assert!(public
488			.vrf_verify(&Message::FallbackSeal([1; 32].into()), ad, &signature)
489			.is_err());
490	}
491
492	#[test]
493	fn vrf_output_hash_matches() {
494		let secret = Secret::from_seed(DEV_SEED);
495
496		let input = Message::FallbackSeal(Default::default());
497		let signature = secret.vrf_sign(&input, b"data");
498
499		let output1 = secret.vrf_output(&input);
500		let output2 = signature.vrf_output();
501		assert_eq!(output1, output2);
502	}
503
504	#[test]
505	fn ring_vrf_sign_verify() {
506		let mut pks: Vec<_> = (0..16).map(|i| Secret::from_seed([i as u8; 32]).public()).collect();
507		assert!(pks.len() <= RING_KEYSET_SIZE);
508
509		let secret = Secret::from_seed(DEV_SEED);
510
511		let input = Message::FallbackSeal(Default::default());
512		let ad = b"data";
513
514		// Just pick one index to patch with the used public key
515		let prover_index = 3;
516		pks[prover_index] = secret.public();
517
518		let prover = RingContext::prover(&pks, prover_index);
519		let signature = secret.ring_vrf_sign(&input, ad, &prover);
520
521		let verifier = RingContext::verifier(&pks);
522		assert!(signature.ring_vrf_verify(&input, ad, &verifier).is_ok());
523
524		// Fail with bad additional data
525		assert!(signature.ring_vrf_verify(&input, b"bad", &verifier).is_err());
526
527		// Fail with bad input
528		assert!(signature
529			.ring_vrf_verify(&Message::FallbackSeal([1; 32].into()), ad, &verifier)
530			.is_err());
531	}
532
533	#[test]
534	fn ring_vrf_sign_verify_with_out_of_ring_key() {
535		let pks: Vec<_> = (0..16).map(|i| Secret::from_seed([i as u8; 32]).public()).collect();
536		let secret = Secret::from_seed(DEV_SEED);
537
538		let input = Message::FallbackSeal(Default::default());
539		let ad = b"data";
540
541		// Fair assuption that secret.public() != pks[0]
542		let prover = RingContext::prover(&pks, 0);
543		let signature = secret.ring_vrf_sign(&input, ad, &prover);
544
545		let verifier = RingContext::verifier(&pks);
546		assert!(signature.ring_vrf_verify(&input, ad, &verifier).is_err());
547	}
548
549	#[test]
550	fn ring_vrf_make_bytes_matches() {
551		let mut pks: Vec<_> = (0..16).map(|i| Secret::from_seed([i as u8; 32]).public()).collect();
552		assert!(pks.len() <= RING_KEYSET_SIZE);
553
554		let secret = Secret::from_seed(DEV_SEED);
555
556		// Just pick one index to patch with the used public key
557		let prover_index = 3;
558		pks[prover_index] = secret.public();
559
560		let input = Message::FallbackSeal(Default::default());
561
562		let prover = RingContext::prover(&pks, prover_index);
563		let signature = secret.ring_vrf_sign(&input, b"data", &prover);
564
565		let output1 = secret.vrf_output(&input);
566		let output2 = signature.vrf_output();
567		assert_eq!(output1, output2);
568	}
569}