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