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