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