1use anyhow::{anyhow, Context};
4use async_trait::async_trait;
5use hex::ToHex;
6use slog::{debug, Logger};
7use std::sync::Arc;
8use thiserror::Error;
9
10use super::CertificateRetriever;
11use crate::crypto_helper::{
12 ProtocolAggregateVerificationKey, ProtocolGenesisError, ProtocolGenesisVerificationKey,
13 ProtocolMultiSignature,
14};
15use crate::entities::{
16 Certificate, CertificateSignature, ProtocolMessagePartKey, ProtocolParameters,
17};
18use crate::logging::LoggerExtensions;
19use crate::StdResult;
20
21#[cfg(test)]
22use mockall::automock;
23
24#[derive(Error, Debug)]
26pub enum CertificateVerifierError {
27 #[error("multi signature verification failed: '{0}'")]
29 VerifyMultiSignature(String),
30
31 #[error("certificate genesis error")]
33 CertificateGenesis(#[from] ProtocolGenesisError),
34
35 #[error("certificate hash unmatch error")]
37 CertificateHashUnmatch,
38
39 #[error("certificate chain previous hash unmatch error")]
42 CertificateChainPreviousHashUnmatch,
43
44 #[error("certificate protocol message unmatch error")]
47 CertificateProtocolMessageUnmatch,
48
49 #[error("certificate chain AVK unmatch error")]
54 CertificateChainAVKUnmatch,
55
56 #[error("certificate chain protocol parameters unmatch error")]
61 CertificateChainProtocolParametersUnmatch,
62
63 #[error("certificate epoch unmatch error")]
66 CertificateEpochUnmatch,
67
68 #[error("certificate chain missing epoch error")]
71 CertificateChainMissingEpoch,
72
73 #[error("certificate chain infinite loop error")]
75 CertificateChainInfiniteLoop,
76
77 #[error("can't validate genesis certificate: given certificate isn't a genesis certificate")]
80 InvalidGenesisCertificateProvided,
81
82 #[error("can't validate standard certificate: given certificate isn't a standard certificate")]
85 InvalidStandardCertificateProvided,
86}
87
88#[cfg_attr(test, automock)]
91#[cfg_attr(target_family = "wasm", async_trait(?Send))]
92#[cfg_attr(not(target_family = "wasm"), async_trait)]
93pub trait CertificateVerifier: Send + Sync {
94 async fn verify_genesis_certificate(
96 &self,
97 genesis_certificate: &Certificate,
98 genesis_verification_key: &ProtocolGenesisVerificationKey,
99 ) -> StdResult<()>;
100
101 async fn verify_standard_certificate(
103 &self,
104 certificate: &Certificate,
105 previous_certificate: &Certificate,
106 ) -> StdResult<()>;
107
108 async fn verify_certificate(
110 &self,
111 certificate: &Certificate,
112 genesis_verification_key: &ProtocolGenesisVerificationKey,
113 ) -> StdResult<Option<Certificate>>;
114
115 async fn verify_certificate_chain(
117 &self,
118 certificate: Certificate,
119 genesis_verification_key: &ProtocolGenesisVerificationKey,
120 ) -> StdResult<()> {
121 let mut certificate = certificate;
122 while let Some(previous_certificate) = self
123 .verify_certificate(&certificate, genesis_verification_key)
124 .await?
125 {
126 certificate = previous_certificate;
127 }
128
129 Ok(())
130 }
131}
132
133pub struct MithrilCertificateVerifier {
135 logger: Logger,
136 certificate_retriever: Arc<dyn CertificateRetriever>,
137}
138
139impl MithrilCertificateVerifier {
140 pub fn new(logger: Logger, certificate_retriever: Arc<dyn CertificateRetriever>) -> Self {
142 debug!(logger, "New MithrilCertificateVerifier created");
143 Self {
144 logger: logger.new_with_component_name::<Self>(),
145 certificate_retriever,
146 }
147 }
148
149 async fn fetch_previous_certificate(
150 &self,
151 certificate: &Certificate,
152 ) -> StdResult<Certificate> {
153 self.certificate_retriever
154 .get_certificate_details(&certificate.previous_hash)
155 .await
156 .map_err(|e| anyhow!(e))
157 .with_context(|| "Can not retrieve previous certificate during verification")
158 }
159
160 fn verify_multi_signature(
161 &self,
162 message: &[u8],
163 multi_signature: &ProtocolMultiSignature,
164 aggregate_verification_key: &ProtocolAggregateVerificationKey,
165 protocol_parameters: &ProtocolParameters,
166 ) -> Result<(), CertificateVerifierError> {
167 debug!(
168 self.logger,
169 "Verify multi signature for {:?}",
170 message.encode_hex::<String>()
171 );
172
173 multi_signature
174 .verify(
175 message,
176 aggregate_verification_key,
177 &protocol_parameters.to_owned().into(),
178 )
179 .map_err(|e| CertificateVerifierError::VerifyMultiSignature(e.to_string()))
180 }
181
182 fn verify_is_not_in_infinite_loop(&self, certificate: &Certificate) -> StdResult<()> {
183 if certificate.is_chaining_to_itself() {
184 return Err(anyhow!(
185 CertificateVerifierError::CertificateChainInfiniteLoop
186 ));
187 }
188
189 Ok(())
190 }
191
192 fn verify_hash_matches_content(&self, certificate: &Certificate) -> StdResult<()> {
193 if certificate.compute_hash() != certificate.hash {
194 return Err(anyhow!(CertificateVerifierError::CertificateHashUnmatch));
195 }
196
197 Ok(())
198 }
199
200 fn verify_previous_hash_matches_previous_certificate_hash(
201 &self,
202 certificate: &Certificate,
203 previous_certificate: &Certificate,
204 ) -> StdResult<()> {
205 if previous_certificate.hash != certificate.previous_hash {
206 return Err(anyhow!(
207 CertificateVerifierError::CertificateChainPreviousHashUnmatch
208 ));
209 }
210
211 Ok(())
212 }
213
214 fn verify_signed_message_matches_hashed_protocol_message(
215 &self,
216 certificate: &Certificate,
217 ) -> StdResult<()> {
218 if certificate.protocol_message.compute_hash() != certificate.signed_message {
219 return Err(anyhow!(
220 CertificateVerifierError::CertificateProtocolMessageUnmatch
221 ));
222 }
223
224 Ok(())
225 }
226
227 fn verify_epoch_matches_protocol_message(&self, certificate: &Certificate) -> StdResult<()> {
228 if let Some(signed_epoch) = &certificate
229 .protocol_message
230 .get_message_part(&ProtocolMessagePartKey::CurrentEpoch)
231 {
232 if **signed_epoch == certificate.epoch.to_string() {
233 return Ok(());
234 }
235 }
236
237 Err(anyhow!(CertificateVerifierError::CertificateEpochUnmatch))
238 }
239
240 fn verify_epoch_chaining(
241 &self,
242 certificate: &Certificate,
243 previous_certificate: &Certificate,
244 ) -> StdResult<()> {
245 if certificate.epoch.has_gap_with(&previous_certificate.epoch) {
246 return Err(anyhow!(
247 CertificateVerifierError::CertificateChainMissingEpoch
248 ));
249 }
250
251 Ok(())
252 }
253
254 fn verify_aggregate_verification_key_chaining(
255 &self,
256 certificate: &Certificate,
257 previous_certificate: &Certificate,
258 ) -> StdResult<()> {
259 let previous_certificate_has_same_epoch = previous_certificate.epoch == certificate.epoch;
260 let certificate_has_valid_aggregate_verification_key =
261 if previous_certificate_has_same_epoch {
262 previous_certificate.aggregate_verification_key
263 == certificate.aggregate_verification_key
264 } else {
265 match &previous_certificate
266 .protocol_message
267 .get_message_part(&ProtocolMessagePartKey::NextAggregateVerificationKey)
268 {
269 Some(previous_certificate_next_aggregate_verification_key) => {
270 **previous_certificate_next_aggregate_verification_key
271 == certificate
272 .aggregate_verification_key
273 .to_json_hex()
274 .with_context(|| {
275 format!(
276 "aggregate verification key to string conversion error for certificate: `{}`",
277 certificate.hash
278 )
279 })?
280 }
281 None => false,
282 }
283 };
284 if !certificate_has_valid_aggregate_verification_key {
285 debug!(
286 self.logger,
287 "Previous certificate {:#?}", previous_certificate
288 );
289 return Err(anyhow!(
290 CertificateVerifierError::CertificateChainAVKUnmatch
291 ));
292 }
293
294 Ok(())
295 }
296
297 fn verify_protocol_parameters_chaining(
298 &self,
299 certificate: &Certificate,
300 previous_certificate: &Certificate,
301 ) -> StdResult<()> {
302 let previous_certificate_has_same_epoch = previous_certificate.epoch == certificate.epoch;
303 let certificate_has_valid_protocol_parameters = if previous_certificate_has_same_epoch {
304 previous_certificate.metadata.protocol_parameters
305 == certificate.metadata.protocol_parameters
306 } else {
307 match &previous_certificate
308 .protocol_message
309 .get_message_part(&ProtocolMessagePartKey::NextProtocolParameters)
310 {
311 Some(previous_certificate_next_protocol_parameters) => {
312 **previous_certificate_next_protocol_parameters
313 == certificate.metadata.protocol_parameters.compute_hash()
314 }
315 None => false,
316 }
317 };
318 if !certificate_has_valid_protocol_parameters {
319 debug!(
320 self.logger,
321 "Previous certificate {:#?}", previous_certificate
322 );
323 return Err(anyhow!(
324 CertificateVerifierError::CertificateChainProtocolParametersUnmatch
325 ));
326 }
327
328 Ok(())
329 }
330}
331
332#[cfg_attr(target_family = "wasm", async_trait(?Send))]
333#[cfg_attr(not(target_family = "wasm"), async_trait)]
334impl CertificateVerifier for MithrilCertificateVerifier {
335 async fn verify_genesis_certificate(
336 &self,
337 genesis_certificate: &Certificate,
338 genesis_verification_key: &ProtocolGenesisVerificationKey,
339 ) -> StdResult<()> {
340 let genesis_signature = match &genesis_certificate.signature {
341 CertificateSignature::GenesisSignature(signature) => Ok(signature),
342 _ => Err(CertificateVerifierError::InvalidGenesisCertificateProvided),
343 }?;
344 self.verify_hash_matches_content(genesis_certificate)?;
345 self.verify_signed_message_matches_hashed_protocol_message(genesis_certificate)?;
346 genesis_verification_key
347 .verify(
348 genesis_certificate.signed_message.as_bytes(),
349 genesis_signature,
350 )
351 .with_context(|| "Certificate verifier failed verifying a genesis certificate")?;
352 self.verify_epoch_matches_protocol_message(genesis_certificate)?;
353
354 Ok(())
355 }
356
357 async fn verify_standard_certificate(
358 &self,
359 certificate: &Certificate,
360 previous_certificate: &Certificate,
361 ) -> StdResult<()> {
362 let multi_signature = match &certificate.signature {
363 CertificateSignature::MultiSignature(_, signature) => Ok(signature),
364 _ => Err(CertificateVerifierError::InvalidStandardCertificateProvided),
365 }?;
366 self.verify_is_not_in_infinite_loop(certificate)?;
367 self.verify_hash_matches_content(certificate)?;
368 self.verify_signed_message_matches_hashed_protocol_message(certificate)?;
369 self.verify_multi_signature(
370 certificate.signed_message.as_bytes(),
371 multi_signature,
372 &certificate.aggregate_verification_key,
373 &certificate.metadata.protocol_parameters,
374 )?;
375 self.verify_epoch_matches_protocol_message(certificate)?;
376 self.verify_epoch_chaining(certificate, previous_certificate)?;
377 self.verify_previous_hash_matches_previous_certificate_hash(
378 certificate,
379 previous_certificate,
380 )?;
381 self.verify_aggregate_verification_key_chaining(certificate, previous_certificate)?;
382 self.verify_protocol_parameters_chaining(certificate, previous_certificate)?;
383
384 Ok(())
385 }
386
387 async fn verify_certificate(
389 &self,
390 certificate: &Certificate,
391 genesis_verification_key: &ProtocolGenesisVerificationKey,
392 ) -> StdResult<Option<Certificate>> {
393 debug!(
394 self.logger, "Verifying certificate";
395 "certificate_hash" => &certificate.hash,
396 "certificate_previous_hash" => &certificate.previous_hash,
397 "certificate_epoch" => ?certificate.epoch,
398 "certificate_signed_entity_type" => ?certificate.signed_entity_type(),
399 );
400
401 match &certificate.signature {
402 CertificateSignature::GenesisSignature(_) => {
403 self.verify_genesis_certificate(certificate, genesis_verification_key)
404 .await?;
405
406 Ok(None)
407 }
408 CertificateSignature::MultiSignature(_, _) => {
409 let previous_certificate = self.fetch_previous_certificate(certificate).await?;
410 self.verify_standard_certificate(certificate, &previous_certificate)
411 .await?;
412
413 Ok(Some(previous_certificate))
414 }
415 }
416 }
417}
418
419#[cfg(test)]
420mod tests {
421 use std::collections::HashMap;
422
423 use async_trait::async_trait;
424 use mockall::mock;
425 use tokio::sync::Mutex;
426
427 use super::CertificateRetriever;
428 use super::*;
429
430 use crate::certificate_chain::{CertificateRetrieverError, FakeCertificaterRetriever};
431 use crate::crypto_helper::{tests_setup::*, ProtocolClerk};
432 use crate::test_utils::{
433 CertificateChainBuilder, CertificateChainBuilderContext, MithrilFixtureBuilder, TestLogger,
434 };
435
436 macro_rules! assert_error_matches {
437 ( $expected_error:path, $error:expr ) => {{
438 let error = $error
439 .downcast_ref::<CertificateVerifierError>()
440 .expect("Can not downcast to `CertificateVerifierError`.");
441 let expected_error = $expected_error;
442
443 assert!(
444 matches!(error, $expected_error),
445 "unexpected error type: got {error:?}, want {expected_error:?}"
446 );
447 }};
448 }
449
450 mock! {
451 pub CertificateRetrieverImpl { }
452
453 #[async_trait]
454 impl CertificateRetriever for CertificateRetrieverImpl {
455
456 async fn get_certificate_details(
457 &self,
458 certificate_hash: &str,
459 ) -> Result<Certificate, CertificateRetrieverError>;
460 }
461 }
462
463 struct MockDependencyInjector {
464 mock_certificate_retriever: MockCertificateRetrieverImpl,
465 }
466
467 impl MockDependencyInjector {
468 fn new() -> MockDependencyInjector {
469 MockDependencyInjector {
470 mock_certificate_retriever: MockCertificateRetrieverImpl::new(),
471 }
472 }
473
474 fn build_certificate_verifier(self) -> MithrilCertificateVerifier {
475 MithrilCertificateVerifier::new(
476 TestLogger::stdout(),
477 Arc::new(self.mock_certificate_retriever),
478 )
479 }
480 }
481
482 #[test]
483 fn verify_multi_signature_success() {
484 let protocol_parameters = setup_protocol_parameters();
485 let fixture = MithrilFixtureBuilder::default()
486 .with_signers(5)
487 .with_protocol_parameters(protocol_parameters.into())
488 .build();
489 let signers = fixture.signers_fixture();
490 let message_hash = setup_message().compute_hash().as_bytes().to_vec();
491
492 let single_signatures = signers
493 .iter()
494 .filter_map(|s| s.protocol_signer.sign(&message_hash))
495 .collect::<Vec<_>>();
496
497 let first_signer = &signers[0].protocol_signer;
498 let clerk = ProtocolClerk::from_signer(first_signer);
499 let aggregate_verification_key = clerk.compute_avk().into();
500 let multi_signature = clerk
501 .aggregate(&single_signatures, &message_hash)
502 .unwrap()
503 .into();
504
505 let verifier = MithrilCertificateVerifier::new(
506 TestLogger::stdout(),
507 Arc::new(MockCertificateRetrieverImpl::new()),
508 );
509 let message_tampered = message_hash[1..].to_vec();
510 assert!(
511 verifier
512 .verify_multi_signature(
513 &message_tampered,
514 &multi_signature,
515 &aggregate_verification_key,
516 &fixture.protocol_parameters(),
517 )
518 .is_err(),
519 "multi signature verification should have failed"
520 );
521 verifier
522 .verify_multi_signature(
523 &message_hash,
524 &multi_signature,
525 &aggregate_verification_key,
526 &fixture.protocol_parameters(),
527 )
528 .expect("multi signature verification should have succeeded");
529 }
530
531 #[tokio::test]
532 async fn verify_genesis_certificate_success() {
533 let (total_certificates, certificates_per_epoch) = (5, 1);
534 let (fake_certificates, genesis_verifier) =
535 setup_certificate_chain(total_certificates, certificates_per_epoch);
536 let verifier = MockDependencyInjector::new().build_certificate_verifier();
537 let genesis_certificate = fake_certificates.last().unwrap().clone();
538
539 let verify = verifier
540 .verify_genesis_certificate(
541 &genesis_certificate,
542 &genesis_verifier.to_verification_key(),
543 )
544 .await;
545
546 verify.expect("verify_genesis_certificate should not fail");
547 }
548
549 #[tokio::test]
550 async fn verify_genesis_certificate_fails_if_is_not_genesis() {
551 let (total_certificates, certificates_per_epoch) = (5, 1);
552 let (fake_certificates, genesis_verifier) =
553 setup_certificate_chain(total_certificates, certificates_per_epoch);
554 let verifier = MockDependencyInjector::new().build_certificate_verifier();
555 let standard_certificate = fake_certificates[0].clone();
556 let mut genesis_certificate = fake_certificates.last().unwrap().clone();
557 genesis_certificate.signature = standard_certificate.signature.clone();
558 genesis_certificate.hash = genesis_certificate.compute_hash();
559
560 let error = verifier
561 .verify_genesis_certificate(
562 &genesis_certificate,
563 &genesis_verifier.to_verification_key(),
564 )
565 .await
566 .expect_err("verify_genesis_certificate should fail");
567
568 assert_error_matches!(
569 CertificateVerifierError::InvalidGenesisCertificateProvided,
570 error
571 )
572 }
573
574 #[tokio::test]
575 async fn verify_genesis_certificate_fails_if_hash_unmatch() {
576 let (total_certificates, certificates_per_epoch) = (5, 1);
577 let (fake_certificates, genesis_verifier) =
578 setup_certificate_chain(total_certificates, certificates_per_epoch);
579 let verifier = MockDependencyInjector::new().build_certificate_verifier();
580 let mut genesis_certificate = fake_certificates.last().unwrap().clone();
581 genesis_certificate.hash = "another-hash".to_string();
582
583 let error = verifier
584 .verify_genesis_certificate(
585 &genesis_certificate,
586 &genesis_verifier.to_verification_key(),
587 )
588 .await
589 .expect_err("verify_genesis_certificate should fail");
590
591 assert_error_matches!(CertificateVerifierError::CertificateHashUnmatch, error)
592 }
593
594 #[tokio::test]
595 async fn verify_genesis_certificate_fails_if_protocol_message_unmatch() {
596 let (total_certificates, certificates_per_epoch) = (5, 1);
597 let (fake_certificates, genesis_verifier) =
598 setup_certificate_chain(total_certificates, certificates_per_epoch);
599 let verifier = MockDependencyInjector::new().build_certificate_verifier();
600 let mut genesis_certificate = fake_certificates.last().unwrap().clone();
601 genesis_certificate.protocol_message.set_message_part(
602 ProtocolMessagePartKey::CurrentEpoch,
603 "another-value".to_string(),
604 );
605 genesis_certificate.hash = genesis_certificate.compute_hash();
606
607 let error = verifier
608 .verify_genesis_certificate(
609 &genesis_certificate,
610 &genesis_verifier.to_verification_key(),
611 )
612 .await
613 .expect_err("verify_genesis_certificate should fail");
614
615 assert_error_matches!(
616 CertificateVerifierError::CertificateProtocolMessageUnmatch,
617 error
618 )
619 }
620
621 #[tokio::test]
622 async fn verify_genesis_certificate_fails_if_epoch_unmatch() {
623 let (total_certificates, certificates_per_epoch) = (5, 1);
624 let (fake_certificates, genesis_verifier) =
625 setup_certificate_chain(total_certificates, certificates_per_epoch);
626 let verifier = MockDependencyInjector::new().build_certificate_verifier();
627 let mut genesis_certificate = fake_certificates.last().unwrap().clone();
628 genesis_certificate.epoch -= 1;
629 genesis_certificate.hash = genesis_certificate.compute_hash();
630
631 let error = verifier
632 .verify_genesis_certificate(
633 &genesis_certificate,
634 &genesis_verifier.to_verification_key(),
635 )
636 .await
637 .expect_err("verify_genesis_certificate should fail");
638
639 assert_error_matches!(CertificateVerifierError::CertificateEpochUnmatch, error)
640 }
641
642 #[tokio::test]
643 async fn verify_standard_certificate_success_with_different_epochs_as_previous() {
644 let (total_certificates, certificates_per_epoch) = (5, 1);
645 let (fake_certificates, _) =
646 setup_certificate_chain(total_certificates, certificates_per_epoch);
647 let verifier = MockDependencyInjector::new().build_certificate_verifier();
648 let certificate = fake_certificates[0].clone();
649 let previous_certificate = fake_certificates[1].clone();
650
651 let verify = verifier
652 .verify_standard_certificate(&certificate, &previous_certificate)
653 .await;
654
655 verify.expect("verify_standard_certificate should not fail");
656 }
657
658 #[tokio::test]
659 async fn verify_standard_certificate_success_with_same_epoch_as_previous() {
660 let (total_certificates, certificates_per_epoch) = (5, 2);
661 let (fake_certificates, _) =
662 setup_certificate_chain(total_certificates, certificates_per_epoch);
663 let verifier = MockDependencyInjector::new().build_certificate_verifier();
664 let certificate = fake_certificates[0].clone();
665 let previous_certificate = fake_certificates[1].clone();
666
667 let verify = verifier
668 .verify_standard_certificate(&certificate, &previous_certificate)
669 .await;
670
671 verify.expect("verify_standard_certificate should not fail");
672 }
673
674 #[tokio::test]
675 async fn verify_standard_certificate_fails_if_is_not_genesis() {
676 let (total_certificates, certificates_per_epoch) = (5, 1);
677 let (fake_certificates, _) =
678 setup_certificate_chain(total_certificates, certificates_per_epoch);
679 let verifier = MockDependencyInjector::new().build_certificate_verifier();
680 let genesis_certificate = fake_certificates.last().unwrap().clone();
681 let mut standard_certificate = fake_certificates[0].clone();
682 standard_certificate.signature = genesis_certificate.signature.clone();
683 standard_certificate.hash = standard_certificate.compute_hash();
684 let standard_previous_certificate = fake_certificates[1].clone();
685
686 let error = verifier
687 .verify_standard_certificate(&standard_certificate, &standard_previous_certificate)
688 .await
689 .expect_err("verify_standard_certificate should fail");
690
691 assert_error_matches!(
692 CertificateVerifierError::InvalidStandardCertificateProvided,
693 error
694 )
695 }
696
697 #[tokio::test]
698 async fn verify_standard_certificate_fails_if_infinite_loop() {
699 let (total_certificates, certificates_per_epoch) = (5, 1);
700 let (fake_certificates, _) =
701 setup_certificate_chain(total_certificates, certificates_per_epoch);
702 let verifier = MockDependencyInjector::new().build_certificate_verifier();
703 let mut certificate = fake_certificates[0].clone();
704 certificate.previous_hash = certificate.hash.clone();
705 let previous_certificate = fake_certificates[1].clone();
706
707 let error = verifier
708 .verify_standard_certificate(&certificate, &previous_certificate)
709 .await
710 .expect_err("verify_standard_certificate should fail");
711
712 assert_error_matches!(
713 CertificateVerifierError::CertificateChainInfiniteLoop,
714 error
715 )
716 }
717
718 #[tokio::test]
719 async fn verify_standard_certificate_fails_if_hash_unmatch() {
720 let (total_certificates, certificates_per_epoch) = (5, 1);
721 let (fake_certificates, _) =
722 setup_certificate_chain(total_certificates, certificates_per_epoch);
723 let verifier = MockDependencyInjector::new().build_certificate_verifier();
724 let mut certificate = fake_certificates[0].clone();
725 certificate.hash = "another-hash".to_string();
726 let previous_certificate = fake_certificates[1].clone();
727
728 let error = verifier
729 .verify_standard_certificate(&certificate, &previous_certificate)
730 .await
731 .expect_err("verify_standard_certificate should fail");
732
733 assert_error_matches!(CertificateVerifierError::CertificateHashUnmatch, error)
734 }
735
736 #[tokio::test]
737 async fn verify_standard_certificate_fails_if_protocol_message_unmatch() {
738 let (total_certificates, certificates_per_epoch) = (5, 1);
739 let (fake_certificates, _) =
740 setup_certificate_chain(total_certificates, certificates_per_epoch);
741 let verifier = MockDependencyInjector::new().build_certificate_verifier();
742 let mut certificate = fake_certificates[0].clone();
743 certificate.protocol_message.set_message_part(
744 ProtocolMessagePartKey::CurrentEpoch,
745 "another-value".to_string(),
746 );
747 certificate.hash = certificate.compute_hash();
748 let previous_certificate = fake_certificates[1].clone();
749
750 let error = verifier
751 .verify_standard_certificate(&certificate, &previous_certificate)
752 .await
753 .expect_err("verify_standard_certificate should fail");
754
755 assert_error_matches!(
756 CertificateVerifierError::CertificateProtocolMessageUnmatch,
757 error
758 )
759 }
760
761 #[tokio::test]
762 async fn verify_standard_certificate_fails_if_epoch_unmatch() {
763 let (total_certificates, certificates_per_epoch) = (5, 1);
764 let (fake_certificates, _) =
765 setup_certificate_chain(total_certificates, certificates_per_epoch);
766 let verifier = MockDependencyInjector::new().build_certificate_verifier();
767 let mut certificate = fake_certificates[0].clone();
768 certificate.epoch -= 1;
769 certificate.hash = certificate.compute_hash();
770 let previous_certificate = fake_certificates[1].clone();
771
772 let error = verifier
773 .verify_standard_certificate(&certificate, &previous_certificate)
774 .await
775 .expect_err("verify_standard_certificate should fail");
776
777 assert_error_matches!(CertificateVerifierError::CertificateEpochUnmatch, error)
778 }
779
780 #[tokio::test]
781 async fn verify_standard_certificate_fails_if_has_missing_epoch() {
782 fn create_epoch_gap_certificate(
783 certificate: Certificate,
784 context: &CertificateChainBuilderContext,
785 ) -> Certificate {
786 let fixture = context.fixture;
787 let modified_epoch = certificate.epoch + 1;
788 let mut protocol_message = certificate.protocol_message.to_owned();
789 protocol_message.set_message_part(
790 ProtocolMessagePartKey::CurrentEpoch,
791 modified_epoch.to_string(),
792 );
793 let signed_message = protocol_message.compute_hash();
794 let mut modified_certificate = certificate;
795 modified_certificate.epoch = modified_epoch;
796 modified_certificate.protocol_message = protocol_message;
797 modified_certificate.signed_message = signed_message.clone();
798 let single_signatures = fixture
799 .signers_fixture()
800 .iter()
801 .filter_map(|s| s.protocol_signer.sign(signed_message.as_bytes()))
802 .collect::<Vec<_>>();
803 let clerk = ProtocolClerk::from_signer(&fixture.signers_fixture()[0].protocol_signer);
804 let modified_multi_signature = clerk
805 .aggregate(&single_signatures, signed_message.as_bytes())
806 .unwrap();
807 modified_certificate.signature = CertificateSignature::MultiSignature(
808 modified_certificate.signed_entity_type(),
809 modified_multi_signature.into(),
810 );
811
812 modified_certificate
813 }
814
815 let (total_certificates, certificates_per_epoch) = (5, 1);
816 let (fake_certificates, _) = CertificateChainBuilder::new()
817 .with_total_certificates(total_certificates)
818 .with_certificates_per_epoch(certificates_per_epoch)
819 .with_standard_certificate_processor(&|certificate, context| {
820 if context.is_last_certificate() {
821 create_epoch_gap_certificate(certificate, context)
822 } else {
823 certificate
824 }
825 })
826 .build();
827 let verifier = MockDependencyInjector::new().build_certificate_verifier();
828 let certificate = fake_certificates[0].clone();
829 let previous_certificate = fake_certificates[1].clone();
830
831 let error = verifier
832 .verify_standard_certificate(&certificate, &previous_certificate)
833 .await
834 .expect_err("verify_standard_certificate should fail");
835
836 assert_error_matches!(
837 CertificateVerifierError::CertificateChainMissingEpoch,
838 error
839 )
840 }
841
842 #[tokio::test]
843 async fn verify_standard_certificate_fails_if_certificate_previous_hash_unmatch() {
844 let (total_certificates, certificates_per_epoch) = (5, 1);
845 let (fake_certificates, _) =
846 setup_certificate_chain(total_certificates, certificates_per_epoch);
847 let verifier = MockDependencyInjector::new().build_certificate_verifier();
848 let certificate = fake_certificates[0].clone();
849 let mut previous_certificate = fake_certificates[1].clone();
850 previous_certificate.previous_hash = "another-hash".to_string();
851 previous_certificate.hash = previous_certificate.compute_hash();
852
853 let error = verifier
854 .verify_standard_certificate(&certificate, &previous_certificate)
855 .await
856 .expect_err("verify_standard_certificate should fail");
857
858 assert_error_matches!(
859 CertificateVerifierError::CertificateChainPreviousHashUnmatch,
860 error
861 )
862 }
863
864 #[tokio::test]
865 async fn verify_standard_certificate_fails_if_certificate_chain_avk_unmatch() {
866 let (total_certificates, certificates_per_epoch) = (5, 1);
867 let (fake_certificates, _) =
868 setup_certificate_chain(total_certificates, certificates_per_epoch);
869 let verifier = MockDependencyInjector::new().build_certificate_verifier();
870 let mut certificate = fake_certificates[0].clone();
871 let mut previous_certificate = fake_certificates[1].clone();
872 previous_certificate.protocol_message.set_message_part(
873 ProtocolMessagePartKey::NextAggregateVerificationKey,
874 "another-avk".to_string(),
875 );
876 previous_certificate.hash = previous_certificate.compute_hash();
877 certificate
878 .previous_hash
879 .clone_from(&previous_certificate.hash);
880 certificate.hash = certificate.compute_hash();
881
882 let error = verifier
883 .verify_standard_certificate(&certificate, &previous_certificate)
884 .await
885 .expect_err("verify_standard_certificate should fail");
886
887 assert_error_matches!(CertificateVerifierError::CertificateChainAVKUnmatch, error)
888 }
889
890 #[tokio::test]
891 async fn verify_standard_certificate_fails_if_certificate_chain_protocol_parameters_unmatch() {
892 let (total_certificates, certificates_per_epoch) = (5, 1);
893 let (fake_certificates, _) =
894 setup_certificate_chain(total_certificates, certificates_per_epoch);
895 let verifier = MockDependencyInjector::new().build_certificate_verifier();
896 let mut certificate = fake_certificates[0].clone();
897 let mut previous_certificate = fake_certificates[1].clone();
898 previous_certificate.protocol_message.set_message_part(
899 ProtocolMessagePartKey::NextProtocolParameters,
900 "protocol-params-hash-123".to_string(),
901 );
902 previous_certificate.hash = previous_certificate.compute_hash();
903 certificate
904 .previous_hash
905 .clone_from(&previous_certificate.hash);
906 certificate.hash = certificate.compute_hash();
907
908 let error = verifier
909 .verify_standard_certificate(&certificate, &previous_certificate)
910 .await
911 .expect_err("verify_standard_certificate should fail");
912
913 assert_error_matches!(
914 CertificateVerifierError::CertificateChainProtocolParametersUnmatch,
915 error
916 )
917 }
918
919 #[tokio::test]
920 async fn verify_certificate_success_when_certificate_is_genesis_and_valid() {
921 let (total_certificates, certificates_per_epoch) = (5, 1);
922 let (fake_certificates, genesis_verifier) =
923 setup_certificate_chain(total_certificates, certificates_per_epoch);
924 let genesis_certificate = fake_certificates.last().unwrap().clone();
925 let mock_container = MockDependencyInjector::new();
926 let verifier = mock_container.build_certificate_verifier();
927
928 let verify = verifier
929 .verify_certificate(
930 &genesis_certificate,
931 &genesis_verifier.to_verification_key(),
932 )
933 .await;
934
935 verify.expect("verify_certificate should not fail");
936 }
937
938 #[tokio::test]
939 async fn verify_certificate_success_when_certificate_is_standard_and_valid() {
940 let (total_certificates, certificates_per_epoch) = (5, 1);
941 let (fake_certificates, genesis_verifier) =
942 setup_certificate_chain(total_certificates, certificates_per_epoch);
943 let certificate = fake_certificates[0].clone();
944 let previous_certificate = fake_certificates[1].clone();
945 let mut mock_container = MockDependencyInjector::new();
946 mock_container
947 .mock_certificate_retriever
948 .expect_get_certificate_details()
949 .returning(move |_| Ok(previous_certificate.clone()))
950 .times(1);
951 let verifier = mock_container.build_certificate_verifier();
952
953 let verify = verifier
954 .verify_certificate(&certificate, &genesis_verifier.to_verification_key())
955 .await;
956
957 verify.expect("verify_certificate should not fail");
958 }
959
960 #[tokio::test]
961 async fn verify_certificate_chain_verifies_all_chained_certificates() {
962 struct CertificateVerifierTest {
963 certificates_unverified: Mutex<HashMap<String, Certificate>>,
964 }
965
966 impl CertificateVerifierTest {
967 fn from_certificates(certificates: &[Certificate]) -> Self {
968 Self {
969 certificates_unverified: Mutex::new(HashMap::from_iter(
970 certificates
971 .iter()
972 .map(|c| (c.hash.to_owned(), c.to_owned())),
973 )),
974 }
975 }
976
977 async fn has_unverified_certificates(&self) -> bool {
978 !self.certificates_unverified.lock().await.is_empty()
979 }
980 }
981
982 #[async_trait]
983 impl CertificateVerifier for CertificateVerifierTest {
984 async fn verify_genesis_certificate(
985 &self,
986 _genesis_certificate: &Certificate,
987 _genesis_verification_key: &ProtocolGenesisVerificationKey,
988 ) -> StdResult<()> {
989 unimplemented!()
990 }
991
992 async fn verify_standard_certificate(
993 &self,
994 _certificate: &Certificate,
995 _previous_certificate: &Certificate,
996 ) -> StdResult<()> {
997 unimplemented!()
998 }
999
1000 async fn verify_certificate(
1001 &self,
1002 certificate: &Certificate,
1003 _genesis_verification_key: &ProtocolGenesisVerificationKey,
1004 ) -> StdResult<Option<Certificate>> {
1005 let mut certificates_unverified = self.certificates_unverified.lock().await;
1006 let _verified_certificate = (*certificates_unverified).remove(&certificate.hash);
1007 let previous_certificate = (*certificates_unverified)
1008 .get(&certificate.previous_hash)
1009 .cloned();
1010
1011 Ok(previous_certificate)
1012 }
1013 }
1014
1015 let (total_certificates, certificates_per_epoch) = (10, 1);
1016 let (fake_certificates, genesis_verifier) =
1017 setup_certificate_chain(total_certificates, certificates_per_epoch);
1018 let fake_certificate_to_verify = fake_certificates[0].clone();
1019 let verifier = CertificateVerifierTest::from_certificates(&fake_certificates);
1020 assert!(verifier.has_unverified_certificates().await);
1021
1022 let verify = verifier
1023 .verify_certificate_chain(
1024 fake_certificate_to_verify,
1025 &genesis_verifier.to_verification_key(),
1026 )
1027 .await;
1028
1029 verify.expect("verify_certificate_chain should not fail");
1030 assert!(!verifier.has_unverified_certificates().await);
1031 }
1032
1033 #[tokio::test]
1034 async fn verify_certificate_chain_success_when_chain_is_valid() {
1035 let (total_certificates, certificates_per_epoch) = (7, 2);
1036 let (fake_certificates, genesis_verifier) =
1037 setup_certificate_chain(total_certificates, certificates_per_epoch);
1038 let certificate_retriever =
1039 FakeCertificaterRetriever::from_certificates(&fake_certificates);
1040 let verifier =
1041 MithrilCertificateVerifier::new(TestLogger::stdout(), Arc::new(certificate_retriever));
1042 let certificate_to_verify = fake_certificates[0].clone();
1043
1044 let verify = verifier
1045 .verify_certificate_chain(
1046 certificate_to_verify,
1047 &genesis_verifier.to_verification_key(),
1048 )
1049 .await;
1050 verify.expect("verify_certificate_chain should not fail");
1051 }
1052
1053 #[tokio::test]
1054 async fn verify_certificate_chain_fails_when_chain_is_tampered() {
1055 let (total_certificates, certificates_per_epoch) = (7, 2);
1056 let (mut fake_certificates, genesis_verifier) =
1057 setup_certificate_chain(total_certificates, certificates_per_epoch);
1058 let index_certificate_fail = (total_certificates / 2) as usize;
1059 fake_certificates[index_certificate_fail].signed_message = "tampered-message".to_string();
1060 let certificate_retriever =
1061 FakeCertificaterRetriever::from_certificates(&fake_certificates);
1062 let verifier =
1063 MithrilCertificateVerifier::new(TestLogger::stdout(), Arc::new(certificate_retriever));
1064 let certificate_to_verify = fake_certificates[0].clone();
1065
1066 let error = verifier
1067 .verify_certificate_chain(
1068 certificate_to_verify,
1069 &genesis_verifier.to_verification_key(),
1070 )
1071 .await
1072 .expect_err("verify_certificate_chain should fail");
1073
1074 assert_error_matches!(CertificateVerifierError::CertificateHashUnmatch, error)
1075 }
1076
1077 #[tokio::test]
1078 async fn verify_certificate_chain_fails_when_adversarial_with_registered_signer_forgery_through_protocol_parameters(
1079 ) {
1080 fn forge_certificate(
1086 certificate: Certificate,
1087 context: &CertificateChainBuilderContext,
1088 ) -> Certificate {
1089 assert_ne!(
1090 1.0, certificate.metadata.protocol_parameters.phi_f,
1091 "Adversarial protocol parameters phi_f should be different from 1.0"
1092 );
1093 let fixture = context.fixture;
1094 let signed_message = certificate.signed_message.to_owned();
1095 let mut forged_certificate = certificate;
1096 let mut forged_protocol_parameters = fixture.protocol_parameters();
1097 forged_protocol_parameters.phi_f = 1.0;
1098 let forged_single_signatures = fixture
1099 .signers_fixture()
1100 .iter()
1101 .take(1)
1102 .filter_map(|s| {
1103 let s_adversary = s
1104 .to_owned()
1105 .try_new_with_protocol_parameters(forged_protocol_parameters.clone())
1106 .unwrap();
1107 let signature = s_adversary.protocol_signer.sign(signed_message.as_bytes());
1108
1109 signature
1110 })
1111 .collect::<Vec<_>>();
1112 let forged_clerk = ProtocolClerk::from_registration(
1113 &forged_protocol_parameters.clone().into(),
1114 &fixture.signers_fixture()[0].protocol_closed_key_registration,
1115 );
1116 let forged_multi_signature = forged_clerk
1117 .aggregate(&forged_single_signatures, signed_message.as_bytes())
1118 .unwrap();
1119 forged_certificate.signature = CertificateSignature::MultiSignature(
1120 forged_certificate.signed_entity_type(),
1121 forged_multi_signature.into(),
1122 );
1123 forged_certificate.metadata.protocol_parameters = forged_protocol_parameters;
1124
1125 forged_certificate
1126 }
1127
1128 let (total_certificates, certificates_per_epoch) = (7, 2);
1129 let (fake_certificates, genesis_verifier) = CertificateChainBuilder::new()
1130 .with_total_certificates(total_certificates)
1131 .with_certificates_per_epoch(certificates_per_epoch)
1132 .with_standard_certificate_processor(&|certificate, context| {
1133 if context.is_last_certificate() {
1134 forge_certificate(certificate, context)
1135 } else {
1136 certificate
1137 }
1138 })
1139 .build();
1140 let certificate_to_verify = fake_certificates[0].clone();
1141 let mock_container = MockDependencyInjector::new();
1142 let mut verifier = mock_container.build_certificate_verifier();
1143 verifier.certificate_retriever = Arc::new(FakeCertificaterRetriever::from_certificates(
1144 &fake_certificates,
1145 ));
1146
1147 let error = verifier
1148 .verify_certificate(
1149 &certificate_to_verify,
1150 &genesis_verifier.to_verification_key(),
1151 )
1152 .await
1153 .expect_err("verify_certificate_chain should fail");
1154
1155 assert_error_matches!(
1156 CertificateVerifierError::CertificateChainProtocolParametersUnmatch,
1157 error
1158 )
1159 }
1160}