1use super::{
2 encoder::{
3 finalize_namespace, notarize_namespace, nullify_message, nullify_namespace,
4 proposal_message, seed_message, seed_namespace,
5 },
6 wire, View,
7};
8use crate::Proof;
9use bytes::{Buf, BufMut};
10use commonware_cryptography::bls12381::primitives::{
11 group::{self, Element},
12 ops,
13 poly::{self, Eval},
14};
15use commonware_utils::{Array, SizedSerialize};
16use std::marker::PhantomData;
17
18type Callback = Box<dyn Fn(&poly::Poly<group::Public>) -> Option<u32>>;
19
20pub struct Verifier {
21 callback: Callback,
22}
23
24impl Verifier {
25 fn new<F>(callback: F) -> Self
26 where
27 F: Fn(&poly::Poly<group::Public>) -> Option<u32> + 'static,
28 {
29 Self {
30 callback: Box::new(callback),
31 }
32 }
33
34 pub fn verify(self, identity: &poly::Poly<group::Public>) -> Option<u32> {
35 (self.callback)(identity)
36 }
37}
38
39#[derive(Clone)]
44pub struct Prover<D: Array> {
45 public: group::Public,
46
47 seed_namespace: Vec<u8>,
48 notarize_namespace: Vec<u8>,
49 nullify_namespace: Vec<u8>,
50 finalize_namespace: Vec<u8>,
51
52 _digest: PhantomData<D>,
53}
54
55impl<D: Array> Prover<D> {
60 pub fn new(public: group::Public, namespace: &[u8]) -> Self {
62 Self {
63 public,
64
65 seed_namespace: seed_namespace(namespace),
66 notarize_namespace: notarize_namespace(namespace),
67 nullify_namespace: nullify_namespace(namespace),
68 finalize_namespace: finalize_namespace(namespace),
69
70 _digest: PhantomData,
71 }
72 }
73
74 pub fn serialize_proposal(proposal: &wire::Proposal, partial_signature: &[u8]) -> Proof {
76 let len = u64::SERIALIZED_LEN
78 + u64::SERIALIZED_LEN
79 + D::SERIALIZED_LEN
80 + poly::PARTIAL_SIGNATURE_LENGTH;
81
82 let mut proof = Vec::with_capacity(len);
84 proof.put_u64(proposal.view);
85 proof.put_u64(proposal.parent);
86 proof.extend_from_slice(&proposal.payload);
87 proof.extend_from_slice(partial_signature);
88 proof.into()
89 }
90
91 fn deserialize_proposal(
93 &self,
94 mut proof: Proof,
95 namespace: &[u8],
96 ) -> Option<(View, View, D, Verifier)> {
97 let expected_len = u64::SERIALIZED_LEN
99 + u64::SERIALIZED_LEN
100 + D::SERIALIZED_LEN
101 + poly::PARTIAL_SIGNATURE_LENGTH;
102 if proof.len() != expected_len {
103 return None;
104 }
105
106 let view = proof.get_u64();
108 let parent = proof.get_u64();
109 let payload = D::read_from(&mut proof).ok()?;
110 let signature = proof.copy_to_bytes(poly::PARTIAL_SIGNATURE_LENGTH);
111 let signature = poly::Eval::deserialize(&signature)?;
112
113 let proposal_message = proposal_message(view, parent, &payload);
115 let namespace = namespace.to_vec();
116 let callback = move |identity: &poly::Poly<group::Public>| -> Option<u32> {
117 if ops::partial_verify_message(
118 identity,
119 Some(&namespace),
120 &proposal_message,
121 &signature,
122 )
123 .is_err()
124 {
125 return None;
126 }
127 Some(signature.index)
128 };
129 Some((view, parent, payload, Verifier::new(callback)))
130 }
131
132 pub fn serialize_threshold(proposal: &wire::Proposal, signature: &[u8], seed: &[u8]) -> Proof {
134 let len = u64::SERIALIZED_LEN
136 + u64::SERIALIZED_LEN
137 + D::SERIALIZED_LEN
138 + group::SIGNATURE_LENGTH
139 + group::SIGNATURE_LENGTH;
140
141 let mut proof = Vec::with_capacity(len);
143 proof.put_u64(proposal.view);
144 proof.put_u64(proposal.parent);
145 proof.extend_from_slice(&proposal.payload);
146 proof.extend_from_slice(signature);
147 proof.extend_from_slice(seed);
148 proof.into()
149 }
150
151 fn deserialize_threshold(
153 &self,
154 mut proof: Proof,
155 namespace: &[u8],
156 ) -> Option<(View, View, D, group::Signature, group::Signature)> {
157 let expected_len = u64::SERIALIZED_LEN
159 + u64::SERIALIZED_LEN
160 + D::SERIALIZED_LEN
161 + group::SIGNATURE_LENGTH
162 + group::SIGNATURE_LENGTH;
163 if proof.len() != expected_len {
164 return None;
165 }
166
167 let view = proof.get_u64();
169 let parent = proof.get_u64();
170 let payload = D::read_from(&mut proof).ok()?;
171 let message = proposal_message(view, parent, &payload);
172 let signature = proof.copy_to_bytes(group::SIGNATURE_LENGTH);
173 let signature = group::Signature::deserialize(&signature)?;
174 if ops::verify_message(&self.public, Some(namespace), &message, &signature).is_err() {
175 return None;
176 }
177
178 let message = seed_message(view);
180 let seed = proof.copy_to_bytes(group::SIGNATURE_LENGTH);
181 let seed = group::Signature::deserialize(&seed)?;
182 if ops::verify_message(&self.public, Some(&self.seed_namespace), &message, &seed).is_err() {
183 return None;
184 }
185 Some((view, parent, payload, signature, seed))
186 }
187
188 pub fn deserialize_notarize(&self, proof: Proof) -> Option<(View, View, D, Verifier)> {
190 Self::deserialize_proposal(self, proof, &self.notarize_namespace)
191 }
192
193 pub fn deserialize_notarization(
195 &self,
196 proof: Proof,
197 ) -> Option<(View, View, D, group::Signature, group::Signature)> {
198 self.deserialize_threshold(proof, &self.notarize_namespace)
199 }
200
201 pub fn deserialize_finalize(&self, proof: Proof) -> Option<(View, View, D, Verifier)> {
203 self.deserialize_proposal(proof, &self.finalize_namespace)
204 }
205
206 pub fn deserialize_finalization(
208 &self,
209 proof: Proof,
210 ) -> Option<(View, View, D, group::Signature, group::Signature)> {
211 self.deserialize_threshold(proof, &self.finalize_namespace)
212 }
213
214 #[allow(clippy::too_many_arguments)]
215 pub fn serialize_conflicting_proposal(
216 view: View,
217 parent_1: View,
218 payload_1: &D,
219 signature_1: &[u8],
220 parent_2: View,
221 payload_2: &D,
222 signature_2: &[u8],
223 ) -> Proof {
224 let len = u64::SERIALIZED_LEN
226 + u64::SERIALIZED_LEN
227 + D::SERIALIZED_LEN
228 + poly::PARTIAL_SIGNATURE_LENGTH
229 + u64::SERIALIZED_LEN
230 + D::SERIALIZED_LEN
231 + poly::PARTIAL_SIGNATURE_LENGTH;
232
233 let mut proof = Vec::with_capacity(len);
235 proof.put_u64(view);
236 proof.put_u64(parent_1);
237 proof.extend_from_slice(payload_1);
238 proof.extend_from_slice(signature_1);
239 proof.put_u64(parent_2);
240 proof.extend_from_slice(payload_2);
241 proof.extend_from_slice(signature_2);
242 proof.into()
243 }
244
245 fn deserialize_conflicting_proposal(
246 &self,
247 mut proof: Proof,
248 namespace: &[u8],
249 ) -> Option<(View, Verifier)> {
250 let expected_len = u64::SERIALIZED_LEN
252 + u64::SERIALIZED_LEN
253 + D::SERIALIZED_LEN
254 + poly::PARTIAL_SIGNATURE_LENGTH
255 + u64::SERIALIZED_LEN
256 + D::SERIALIZED_LEN
257 + poly::PARTIAL_SIGNATURE_LENGTH;
258 if proof.len() != expected_len {
259 return None;
260 }
261
262 let view = proof.get_u64();
264 let parent_1 = proof.get_u64();
265 let payload_1 = D::read_from(&mut proof).ok()?;
266 let signature_1 = proof.copy_to_bytes(poly::PARTIAL_SIGNATURE_LENGTH);
267 let signature_1 = Eval::deserialize(&signature_1)?;
268 let parent_2 = proof.get_u64();
269 let payload_2 = D::read_from(&mut proof).ok()?;
270 let signature_2 = proof.copy_to_bytes(poly::PARTIAL_SIGNATURE_LENGTH);
271 let signature_2 = Eval::deserialize(&signature_2)?;
272 if signature_1.index != signature_2.index {
273 return None;
274 }
275
276 let namespace = namespace.to_vec();
278 let callback = move |identity: &poly::Poly<group::Public>| -> Option<u32> {
279 if ops::partial_verify_message(
280 identity,
281 Some(&namespace),
282 &proposal_message(view, parent_1, &payload_1),
283 &signature_1,
284 )
285 .is_err()
286 {
287 return None;
288 }
289 if ops::partial_verify_message(
290 identity,
291 Some(&namespace),
292 &proposal_message(view, parent_2, &payload_2),
293 &signature_2,
294 )
295 .is_err()
296 {
297 return None;
298 }
299 Some(signature_1.index)
300 };
301 Some((view, Verifier::new(callback)))
302 }
303
304 #[allow(clippy::too_many_arguments)]
306 pub fn serialize_conflicting_notarize(
307 view: View,
308 parent_1: View,
309 payload_1: &D,
310 signature_1: &[u8],
311 parent_2: View,
312 payload_2: &D,
313 signature_2: &[u8],
314 ) -> Proof {
315 Self::serialize_conflicting_proposal(
316 view,
317 parent_1,
318 payload_1,
319 signature_1,
320 parent_2,
321 payload_2,
322 signature_2,
323 )
324 }
325
326 pub fn deserialize_conflicting_notarize(&self, proof: Proof) -> Option<(View, Verifier)> {
328 self.deserialize_conflicting_proposal(proof, &self.notarize_namespace)
329 }
330
331 #[allow(clippy::too_many_arguments)]
333 pub fn serialize_conflicting_finalize(
334 view: View,
335 parent_1: View,
336 payload_1: &D,
337 signature_1: &[u8],
338 parent_2: View,
339 payload_2: &D,
340 signature_2: &[u8],
341 ) -> Proof {
342 Self::serialize_conflicting_proposal(
343 view,
344 parent_1,
345 payload_1,
346 signature_1,
347 parent_2,
348 payload_2,
349 signature_2,
350 )
351 }
352
353 pub fn deserialize_conflicting_finalize(&self, proof: Proof) -> Option<(View, Verifier)> {
355 self.deserialize_conflicting_proposal(proof, &self.finalize_namespace)
356 }
357
358 pub fn serialize_nullify_finalize(
360 view: View,
361 parent: View,
362 payload: &D,
363 signature_finalize: &[u8],
364 signature_null: &[u8],
365 ) -> Proof {
366 let len = u64::SERIALIZED_LEN
368 + u64::SERIALIZED_LEN
369 + D::SERIALIZED_LEN
370 + poly::PARTIAL_SIGNATURE_LENGTH
371 + poly::PARTIAL_SIGNATURE_LENGTH;
372
373 let mut proof = Vec::with_capacity(len);
375 proof.put_u64(view);
376 proof.put_u64(parent);
377 proof.extend_from_slice(payload);
378 proof.extend_from_slice(signature_finalize);
379 proof.extend_from_slice(signature_null);
380 proof.into()
381 }
382
383 pub fn deserialize_nullify_finalize(&self, mut proof: Proof) -> Option<(View, Verifier)> {
385 let expected_len = u64::SERIALIZED_LEN
387 + u64::SERIALIZED_LEN
388 + D::SERIALIZED_LEN
389 + poly::PARTIAL_SIGNATURE_LENGTH
390 + poly::PARTIAL_SIGNATURE_LENGTH;
391 if proof.len() != expected_len {
392 return None;
393 }
394
395 let view = proof.get_u64();
397 let parent = proof.get_u64();
398 let payload = D::read_from(&mut proof).ok()?;
399 let signature_finalize = proof.copy_to_bytes(poly::PARTIAL_SIGNATURE_LENGTH);
400 let signature_finalize = Eval::deserialize(&signature_finalize)?;
401 let signature_null = proof.copy_to_bytes(poly::PARTIAL_SIGNATURE_LENGTH);
402 let signature_null = Eval::deserialize(&signature_null)?;
403 if signature_finalize.index != signature_null.index {
404 return None;
405 }
406
407 let finalize_namespace = self.finalize_namespace.clone();
409 let nullify_namespace = self.nullify_namespace.clone();
410 let callback = move |identity: &poly::Poly<group::Public>| -> Option<u32> {
411 if ops::partial_verify_message(
412 identity,
413 Some(&finalize_namespace),
414 &proposal_message(view, parent, &payload),
415 &signature_finalize,
416 )
417 .is_err()
418 {
419 return None;
420 }
421 if ops::partial_verify_message(
422 identity,
423 Some(&nullify_namespace),
424 &nullify_message(view),
425 &signature_null,
426 )
427 .is_err()
428 {
429 return None;
430 }
431 Some(signature_finalize.index)
432 };
433 Some((view, Verifier::new(callback)))
434 }
435}
436
437#[cfg(test)]
438mod tests {
439 use super::*;
440 use commonware_cryptography::{
441 bls12381::{
442 dkg::ops::generate_shares,
443 primitives::group::{self, Share},
444 },
445 sha256::Digest as Sha256Digest,
446 Hasher, Sha256,
447 };
448 use ops::{keypair, partial_sign_message, sign_message};
449 use rand::{rngs::StdRng, SeedableRng};
450
451 fn generate_threshold() -> (group::Public, poly::Public, Vec<Share>) {
452 let mut sampler = StdRng::seed_from_u64(0);
453 let (public, shares) = generate_shares(&mut sampler, None, 4, 3);
454 (poly::public(&public), public, shares)
455 }
456
457 fn generate_keypair() -> (group::Private, group::Public) {
458 let mut sampler = StdRng::seed_from_u64(0);
459 keypair(&mut sampler)
460 }
461
462 fn test_digest(value: u8) -> Sha256Digest {
463 let mut hasher = Sha256::new();
464 hasher.update(&[value]);
465 hasher.finalize()
466 }
467
468 #[test]
469 fn test_deserialize_proposal() {
470 let (public, poly, shares) = generate_threshold();
472 let prover = Prover::<Sha256Digest>::new(public, b"test");
473 let payload = test_digest(0);
474 let signature = partial_sign_message(
475 &shares[0],
476 Some(&prover.seed_namespace),
477 &proposal_message(1, 0, &payload),
478 )
479 .serialize();
480
481 let mut proof = Vec::new();
483 proof.put_u64(1); proof.put_u64(0); proof.extend_from_slice(&payload); proof.extend_from_slice(&signature); let (_, _, _, verifier) = prover
490 .deserialize_proposal(proof.into(), &prover.notarize_namespace)
491 .unwrap();
492 assert!(verifier.verify(&poly).is_none());
493 }
494
495 #[test]
496 fn test_deserialize_proposal_invalid() {
497 let (public, poly, shares) = generate_threshold();
499 let prover = Prover::<Sha256Digest>::new(public, b"test");
500 let payload = test_digest(0);
501 let signature = partial_sign_message(
502 &shares[0],
503 Some(&prover.seed_namespace),
504 &proposal_message(1, 1, &payload),
505 )
506 .serialize();
507
508 let mut proof = Vec::new();
510 proof.put_u64(1); proof.put_u64(0); proof.extend_from_slice(&payload); proof.extend_from_slice(&signature); let (_, _, _, verifier) = prover
517 .deserialize_proposal(proof.into(), &prover.notarize_namespace)
518 .unwrap();
519 assert!(verifier.verify(&poly).is_none());
520 }
521
522 #[test]
523 fn test_deserialize_proposal_underflow() {
524 let (public, _, shares) = generate_threshold();
526 let prover = Prover::<Sha256Digest>::new(public, b"test");
527 let payload = test_digest(0);
528 let signature = partial_sign_message(
529 &shares[0],
530 Some(&prover.seed_namespace),
531 &proposal_message(1, 0, &payload),
532 )
533 .serialize();
534
535 let signature = signature[0..group::SIGNATURE_LENGTH - 1].to_vec();
537
538 let mut proof = Vec::new();
540 proof.put_u64(1); proof.put_u64(0); proof.extend_from_slice(&payload); proof.extend_from_slice(&signature); let result = prover.deserialize_proposal(proof.into(), &prover.notarize_namespace);
547 assert!(result.is_none());
548 }
549
550 #[test]
551 fn test_deserialize_proposal_overflow() {
552 let (public, _, shares) = generate_threshold();
554 let prover = Prover::<Sha256Digest>::new(public, b"test");
555 let payload = test_digest(0);
556 let signature = partial_sign_message(
557 &shares[0],
558 Some(&prover.seed_namespace),
559 &proposal_message(1, 0, &payload),
560 )
561 .serialize();
562
563 let signature = [signature, vec![0; 1]].concat();
565
566 let mut proof = Vec::new();
568 proof.put_u64(1); proof.put_u64(0); proof.extend_from_slice(&payload); proof.extend_from_slice(&signature); let result = prover.deserialize_proposal(proof.into(), &prover.notarize_namespace);
575 assert!(result.is_none());
576 }
577
578 #[test]
579 fn test_deserialize_threshold() {
580 let (private, public) = generate_keypair();
582 let prover = Prover::<Sha256Digest>::new(public, b"test");
583
584 let payload = test_digest(0);
586 let proposal_signature = sign_message(
587 &private,
588 Some(&prover.notarize_namespace),
589 &proposal_message(1, 0, &payload),
590 )
591 .serialize();
592 let seed_signature =
593 sign_message(&private, Some(&prover.seed_namespace), &seed_message(1)).serialize();
594
595 let mut proof = Vec::new();
597 proof.put_u64(1); proof.put_u64(0); proof.extend_from_slice(&payload); proof.extend_from_slice(&proposal_signature); proof.extend_from_slice(&seed_signature); let result = prover.deserialize_threshold(proof.into(), &prover.notarize_namespace);
605 assert!(result.is_some());
606 }
607
608 #[test]
609 fn test_deserialize_threshold_invalid() {
610 let (private, public) = generate_keypair();
612 let prover = Prover::<Sha256Digest>::new(public, b"test");
613
614 let payload = test_digest(0);
616 let proposal_signature = sign_message(
617 &private,
618 Some(&prover.notarize_namespace),
619 &proposal_message(1, 0, &payload),
620 )
621 .serialize();
622 let seed_signature =
623 sign_message(&private, Some(&prover.seed_namespace), &seed_message(2)).serialize();
624
625 let mut proof = Vec::new();
627 proof.put_u64(1); proof.put_u64(0); proof.extend_from_slice(&payload); proof.extend_from_slice(&proposal_signature); proof.extend_from_slice(&seed_signature); let result = prover.deserialize_threshold(proof.into(), &prover.notarize_namespace);
635 assert!(result.is_none());
636 }
637
638 #[test]
639 fn test_deserialize_threshold_underflow() {
640 let (private, public) = generate_keypair();
642 let prover = Prover::<Sha256Digest>::new(public, b"test");
643
644 let payload = test_digest(0);
646 let proposal_signature = sign_message(
647 &private,
648 Some(&prover.notarize_namespace),
649 &proposal_message(1, 0, &payload),
650 )
651 .serialize();
652 let seed_signature =
653 sign_message(&private, Some(&prover.seed_namespace), &seed_message(1)).serialize();
654
655 let seed_signature = seed_signature[0..group::SIGNATURE_LENGTH - 1].to_vec();
657
658 let mut proof = Vec::new();
660 proof.put_u64(1); proof.put_u64(0); proof.extend_from_slice(&payload); proof.extend_from_slice(&proposal_signature); proof.extend_from_slice(&seed_signature); let result = prover.deserialize_threshold(proof.into(), &prover.notarize_namespace);
668 assert!(result.is_none());
669 }
670
671 #[test]
672 fn test_deserialize_threshold_overflow() {
673 let (private, public) = generate_keypair();
675 let prover = Prover::<Sha256Digest>::new(public, b"test");
676
677 let payload = test_digest(0);
679 let proposal_signature = sign_message(
680 &private,
681 Some(&prover.notarize_namespace),
682 &proposal_message(1, 0, &payload),
683 )
684 .serialize();
685 let seed_signature =
686 sign_message(&private, Some(&prover.seed_namespace), &seed_message(1)).serialize();
687
688 let seed_signature = [seed_signature, vec![0; 1]].concat();
690
691 let mut proof = Vec::new();
693 proof.put_u64(1); proof.put_u64(0); proof.extend_from_slice(&payload); proof.extend_from_slice(&proposal_signature); proof.extend_from_slice(&seed_signature); let result = prover.deserialize_threshold(proof.into(), &prover.notarize_namespace);
701 assert!(result.is_none());
702 }
703}