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 crate::test::{
426 TestLogger,
427 builder::{CertificateChainBuilder, CertificateChainBuilderContext, MithrilFixtureBuilder},
428 crypto_helper::{setup_certificate_chain, setup_message, setup_protocol_parameters},
429 double::FakeCertificaterRetriever,
430 };
431 use crate::{
432 certificate_chain::certificate_retriever::MockCertificateRetriever,
433 crypto_helper::ProtocolClerk,
434 };
435
436 use super::*;
437
438 macro_rules! assert_error_matches {
439 ( $expected_error:path, $error:expr ) => {{
440 let error = $error
441 .downcast_ref::<CertificateVerifierError>()
442 .expect("Can not downcast to `CertificateVerifierError`.");
443 let expected_error = $expected_error;
444
445 assert!(
446 matches!(error, $expected_error),
447 "unexpected error type: got {error:?}, want {expected_error:?}"
448 );
449 }};
450 }
451
452 struct MockDependencyInjector {
453 mock_certificate_retriever: MockCertificateRetriever,
454 }
455
456 impl MockDependencyInjector {
457 fn new() -> MockDependencyInjector {
458 MockDependencyInjector {
459 mock_certificate_retriever: MockCertificateRetriever::new(),
460 }
461 }
462
463 fn build_certificate_verifier(self) -> MithrilCertificateVerifier {
464 MithrilCertificateVerifier::new(
465 TestLogger::stdout(),
466 Arc::new(self.mock_certificate_retriever),
467 )
468 }
469 }
470
471 #[test]
472 fn verify_multi_signature_success() {
473 let protocol_parameters = setup_protocol_parameters();
474 let fixture = MithrilFixtureBuilder::default()
475 .with_signers(5)
476 .with_protocol_parameters(protocol_parameters.into())
477 .build();
478 let signers = fixture.signers_fixture();
479 let message_hash = setup_message().compute_hash().as_bytes().to_vec();
480
481 let single_signatures = signers
482 .iter()
483 .filter_map(|s| s.protocol_signer.sign(&message_hash))
484 .collect::<Vec<_>>();
485
486 let first_signer = &signers[0].protocol_signer;
487 let clerk = ProtocolClerk::new_clerk_from_signer(first_signer);
488 let aggregate_verification_key = clerk.compute_aggregate_verification_key().into();
489 let multi_signature = clerk
490 .aggregate_signatures(&single_signatures, &message_hash)
491 .unwrap()
492 .into();
493
494 let verifier = MithrilCertificateVerifier::new(
495 TestLogger::stdout(),
496 Arc::new(MockCertificateRetriever::new()),
497 );
498 let message_tampered = message_hash[1..].to_vec();
499 assert!(
500 verifier
501 .verify_multi_signature(
502 &message_tampered,
503 &multi_signature,
504 &aggregate_verification_key,
505 &fixture.protocol_parameters(),
506 )
507 .is_err(),
508 "multi signature verification should have failed"
509 );
510 verifier
511 .verify_multi_signature(
512 &message_hash,
513 &multi_signature,
514 &aggregate_verification_key,
515 &fixture.protocol_parameters(),
516 )
517 .expect("multi signature verification should have succeeded");
518 }
519
520 #[tokio::test]
521 async fn verify_genesis_certificate_success() {
522 let (total_certificates, certificates_per_epoch) = (5, 1);
523 let fake_certificates = setup_certificate_chain(total_certificates, certificates_per_epoch);
524 let verifier = MockDependencyInjector::new().build_certificate_verifier();
525 let genesis_certificate = fake_certificates.genesis_certificate();
526
527 let verify = verifier
528 .verify_genesis_certificate(
529 genesis_certificate,
530 &fake_certificates.genesis_verifier.to_verification_key(),
531 )
532 .await;
533
534 verify.expect("verify_genesis_certificate should not fail");
535 }
536
537 #[tokio::test]
538 async fn verify_genesis_certificate_fails_if_is_not_genesis() {
539 let (total_certificates, certificates_per_epoch) = (5, 1);
540 let fake_certificates = setup_certificate_chain(total_certificates, certificates_per_epoch);
541 let verifier = MockDependencyInjector::new().build_certificate_verifier();
542 let standard_certificate = fake_certificates[0].clone();
543 let mut genesis_certificate = fake_certificates.genesis_certificate().clone();
544 genesis_certificate.signature = standard_certificate.signature.clone();
545 genesis_certificate.hash = genesis_certificate.compute_hash();
546
547 let error = verifier
548 .verify_genesis_certificate(
549 &genesis_certificate,
550 &fake_certificates.genesis_verifier.to_verification_key(),
551 )
552 .await
553 .expect_err("verify_genesis_certificate should fail");
554
555 assert_error_matches!(
556 CertificateVerifierError::InvalidGenesisCertificateProvided,
557 error
558 )
559 }
560
561 #[tokio::test]
562 async fn verify_genesis_certificate_fails_if_hash_unmatch() {
563 let (total_certificates, certificates_per_epoch) = (5, 1);
564 let fake_certificates = setup_certificate_chain(total_certificates, certificates_per_epoch);
565 let verifier = MockDependencyInjector::new().build_certificate_verifier();
566 let mut genesis_certificate = fake_certificates.genesis_certificate().clone();
567 genesis_certificate.hash = "another-hash".to_string();
568
569 let error = verifier
570 .verify_genesis_certificate(
571 &genesis_certificate,
572 &fake_certificates.genesis_verifier.to_verification_key(),
573 )
574 .await
575 .expect_err("verify_genesis_certificate should fail");
576
577 assert_error_matches!(CertificateVerifierError::CertificateHashUnmatch, error)
578 }
579
580 #[tokio::test]
581 async fn verify_genesis_certificate_fails_if_protocol_message_unmatch() {
582 let (total_certificates, certificates_per_epoch) = (5, 1);
583 let fake_certificates = setup_certificate_chain(total_certificates, certificates_per_epoch);
584 let verifier = MockDependencyInjector::new().build_certificate_verifier();
585 let mut genesis_certificate = fake_certificates.genesis_certificate().clone();
586 genesis_certificate.protocol_message.set_message_part(
587 ProtocolMessagePartKey::CurrentEpoch,
588 "another-value".to_string(),
589 );
590 genesis_certificate.hash = genesis_certificate.compute_hash();
591
592 let error = verifier
593 .verify_genesis_certificate(
594 &genesis_certificate,
595 &fake_certificates.genesis_verifier.to_verification_key(),
596 )
597 .await
598 .expect_err("verify_genesis_certificate should fail");
599
600 assert_error_matches!(
601 CertificateVerifierError::CertificateProtocolMessageUnmatch,
602 error
603 )
604 }
605
606 #[tokio::test]
607 async fn verify_genesis_certificate_fails_if_epoch_unmatch() {
608 let (total_certificates, certificates_per_epoch) = (5, 1);
609 let fake_certificates = setup_certificate_chain(total_certificates, certificates_per_epoch);
610 let verifier = MockDependencyInjector::new().build_certificate_verifier();
611 let mut genesis_certificate = fake_certificates.genesis_certificate().clone();
612 genesis_certificate.epoch -= 1;
613 genesis_certificate.hash = genesis_certificate.compute_hash();
614
615 let error = verifier
616 .verify_genesis_certificate(
617 &genesis_certificate,
618 &fake_certificates.genesis_verifier.to_verification_key(),
619 )
620 .await
621 .expect_err("verify_genesis_certificate should fail");
622
623 assert_error_matches!(CertificateVerifierError::CertificateEpochUnmatch, error)
624 }
625
626 #[tokio::test]
627 async fn verify_standard_certificate_success_with_different_epochs_as_previous() {
628 let (total_certificates, certificates_per_epoch) = (5, 1);
629 let fake_certificates = setup_certificate_chain(total_certificates, certificates_per_epoch);
630 let verifier = MockDependencyInjector::new().build_certificate_verifier();
631 let certificate = fake_certificates[0].clone();
632 let previous_certificate = fake_certificates[1].clone();
633
634 let verify = verifier
635 .verify_standard_certificate(&certificate, &previous_certificate)
636 .await;
637
638 verify.expect("verify_standard_certificate should not fail");
639 }
640
641 #[tokio::test]
642 async fn verify_standard_certificate_success_with_same_epoch_as_previous() {
643 let (total_certificates, certificates_per_epoch) = (5, 2);
644 let fake_certificates = setup_certificate_chain(total_certificates, certificates_per_epoch);
645 let verifier = MockDependencyInjector::new().build_certificate_verifier();
646 let certificate = fake_certificates[0].clone();
647 let previous_certificate = fake_certificates[1].clone();
648
649 let verify = verifier
650 .verify_standard_certificate(&certificate, &previous_certificate)
651 .await;
652
653 verify.expect("verify_standard_certificate should not fail");
654 }
655
656 #[tokio::test]
657 async fn verify_standard_certificate_fails_if_is_not_genesis() {
658 let (total_certificates, certificates_per_epoch) = (5, 1);
659 let fake_certificates = setup_certificate_chain(total_certificates, certificates_per_epoch);
660 let verifier = MockDependencyInjector::new().build_certificate_verifier();
661 let genesis_certificate = fake_certificates.genesis_certificate();
662 let mut standard_certificate = fake_certificates[0].clone();
663 standard_certificate.signature = genesis_certificate.signature.clone();
664 standard_certificate.hash = standard_certificate.compute_hash();
665 let standard_previous_certificate = fake_certificates[1].clone();
666
667 let error = verifier
668 .verify_standard_certificate(&standard_certificate, &standard_previous_certificate)
669 .await
670 .expect_err("verify_standard_certificate should fail");
671
672 assert_error_matches!(
673 CertificateVerifierError::InvalidStandardCertificateProvided,
674 error
675 )
676 }
677
678 #[tokio::test]
679 async fn verify_standard_certificate_fails_if_infinite_loop() {
680 let (total_certificates, certificates_per_epoch) = (5, 1);
681 let fake_certificates = setup_certificate_chain(total_certificates, certificates_per_epoch);
682 let verifier = MockDependencyInjector::new().build_certificate_verifier();
683 let mut certificate = fake_certificates[0].clone();
684 certificate.previous_hash = certificate.hash.clone();
685 let previous_certificate = fake_certificates[1].clone();
686
687 let error = verifier
688 .verify_standard_certificate(&certificate, &previous_certificate)
689 .await
690 .expect_err("verify_standard_certificate should fail");
691
692 assert_error_matches!(
693 CertificateVerifierError::CertificateChainInfiniteLoop,
694 error
695 )
696 }
697
698 #[tokio::test]
699 async fn verify_standard_certificate_fails_if_hash_unmatch() {
700 let (total_certificates, certificates_per_epoch) = (5, 1);
701 let fake_certificates = 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.hash = "another-hash".to_string();
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!(CertificateVerifierError::CertificateHashUnmatch, error)
713 }
714
715 #[tokio::test]
716 async fn verify_standard_certificate_fails_if_protocol_message_unmatch() {
717 let (total_certificates, certificates_per_epoch) = (5, 1);
718 let fake_certificates = setup_certificate_chain(total_certificates, certificates_per_epoch);
719 let verifier = MockDependencyInjector::new().build_certificate_verifier();
720 let mut certificate = fake_certificates[0].clone();
721 certificate.protocol_message.set_message_part(
722 ProtocolMessagePartKey::CurrentEpoch,
723 "another-value".to_string(),
724 );
725 certificate.hash = certificate.compute_hash();
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!(
734 CertificateVerifierError::CertificateProtocolMessageUnmatch,
735 error
736 )
737 }
738
739 #[tokio::test]
740 async fn verify_standard_certificate_fails_if_epoch_unmatch() {
741 let (total_certificates, certificates_per_epoch) = (5, 1);
742 let fake_certificates = setup_certificate_chain(total_certificates, certificates_per_epoch);
743 let verifier = MockDependencyInjector::new().build_certificate_verifier();
744 let mut certificate = fake_certificates[0].clone();
745 certificate.epoch -= 1;
746 certificate.hash = certificate.compute_hash();
747 let previous_certificate = fake_certificates[1].clone();
748
749 let error = verifier
750 .verify_standard_certificate(&certificate, &previous_certificate)
751 .await
752 .expect_err("verify_standard_certificate should fail");
753
754 assert_error_matches!(CertificateVerifierError::CertificateEpochUnmatch, error)
755 }
756
757 #[tokio::test]
758 async fn verify_standard_certificate_fails_if_has_missing_epoch() {
759 fn create_epoch_gap_certificate(
760 certificate: Certificate,
761 context: &CertificateChainBuilderContext,
762 ) -> Certificate {
763 let fixture = context.fixture;
764 let modified_epoch = certificate.epoch + 1;
765 let mut protocol_message = certificate.protocol_message.to_owned();
766 protocol_message.set_message_part(
767 ProtocolMessagePartKey::CurrentEpoch,
768 modified_epoch.to_string(),
769 );
770 let signed_message = protocol_message.compute_hash();
771 let mut modified_certificate = certificate;
772 modified_certificate.epoch = modified_epoch;
773 modified_certificate.protocol_message = protocol_message;
774 modified_certificate.signed_message = signed_message.clone();
775 let single_signatures = fixture
776 .signers_fixture()
777 .iter()
778 .filter_map(|s| s.protocol_signer.sign(signed_message.as_bytes()))
779 .collect::<Vec<_>>();
780 let clerk =
781 ProtocolClerk::new_clerk_from_signer(&fixture.signers_fixture()[0].protocol_signer);
782 let modified_multi_signature = clerk
783 .aggregate_signatures(&single_signatures, signed_message.as_bytes())
784 .unwrap();
785 modified_certificate.signature = CertificateSignature::MultiSignature(
786 modified_certificate.signed_entity_type(),
787 modified_multi_signature.into(),
788 );
789
790 modified_certificate
791 }
792
793 let (total_certificates, certificates_per_epoch) = (5, 1);
794 let fake_certificates = CertificateChainBuilder::new()
795 .with_total_certificates(total_certificates)
796 .with_certificates_per_epoch(certificates_per_epoch)
797 .with_standard_certificate_processor(&|certificate, context| {
798 if context.is_last_certificate() {
799 create_epoch_gap_certificate(certificate, context)
800 } else {
801 certificate
802 }
803 })
804 .build();
805 let verifier = MockDependencyInjector::new().build_certificate_verifier();
806 let certificate = fake_certificates[0].clone();
807 let previous_certificate = fake_certificates[1].clone();
808
809 let error = verifier
810 .verify_standard_certificate(&certificate, &previous_certificate)
811 .await
812 .expect_err("verify_standard_certificate should fail");
813
814 assert_error_matches!(
815 CertificateVerifierError::CertificateChainMissingEpoch,
816 error
817 )
818 }
819
820 #[tokio::test]
821 async fn verify_standard_certificate_fails_if_certificate_previous_hash_unmatch() {
822 let (total_certificates, certificates_per_epoch) = (5, 1);
823 let fake_certificates = setup_certificate_chain(total_certificates, certificates_per_epoch);
824 let verifier = MockDependencyInjector::new().build_certificate_verifier();
825 let certificate = fake_certificates[0].clone();
826 let mut previous_certificate = fake_certificates[1].clone();
827 previous_certificate.previous_hash = "another-hash".to_string();
828 previous_certificate.hash = previous_certificate.compute_hash();
829
830 let error = verifier
831 .verify_standard_certificate(&certificate, &previous_certificate)
832 .await
833 .expect_err("verify_standard_certificate should fail");
834
835 assert_error_matches!(
836 CertificateVerifierError::CertificateChainPreviousHashUnmatch,
837 error
838 )
839 }
840
841 #[tokio::test]
842 async fn verify_standard_certificate_fails_if_certificate_chain_avk_unmatch() {
843 let (total_certificates, certificates_per_epoch) = (5, 1);
844 let fake_certificates = setup_certificate_chain(total_certificates, certificates_per_epoch);
845 let verifier = MockDependencyInjector::new().build_certificate_verifier();
846 let mut certificate = fake_certificates[0].clone();
847 let mut previous_certificate = fake_certificates[1].clone();
848 previous_certificate.protocol_message.set_message_part(
849 ProtocolMessagePartKey::NextAggregateVerificationKey,
850 "another-avk".to_string(),
851 );
852 previous_certificate.hash = previous_certificate.compute_hash();
853 certificate.previous_hash.clone_from(&previous_certificate.hash);
854 certificate.hash = certificate.compute_hash();
855
856 let error = verifier
857 .verify_standard_certificate(&certificate, &previous_certificate)
858 .await
859 .expect_err("verify_standard_certificate should fail");
860
861 assert_error_matches!(CertificateVerifierError::CertificateChainAVKUnmatch, error)
862 }
863
864 #[tokio::test]
865 async fn verify_standard_certificate_fails_if_certificate_chain_protocol_parameters_unmatch() {
866 let (total_certificates, certificates_per_epoch) = (5, 1);
867 let fake_certificates = setup_certificate_chain(total_certificates, certificates_per_epoch);
868 let verifier = MockDependencyInjector::new().build_certificate_verifier();
869 let mut certificate = fake_certificates[0].clone();
870 let mut previous_certificate = fake_certificates[1].clone();
871 previous_certificate.protocol_message.set_message_part(
872 ProtocolMessagePartKey::NextProtocolParameters,
873 "protocol-params-hash-123".to_string(),
874 );
875 previous_certificate.hash = previous_certificate.compute_hash();
876 certificate.previous_hash.clone_from(&previous_certificate.hash);
877 certificate.hash = certificate.compute_hash();
878
879 let error = verifier
880 .verify_standard_certificate(&certificate, &previous_certificate)
881 .await
882 .expect_err("verify_standard_certificate should fail");
883
884 assert_error_matches!(
885 CertificateVerifierError::CertificateChainProtocolParametersUnmatch,
886 error
887 )
888 }
889
890 #[tokio::test]
891 async fn verify_certificate_success_when_certificate_is_genesis_and_valid() {
892 let (total_certificates, certificates_per_epoch) = (5, 1);
893 let fake_certificates = setup_certificate_chain(total_certificates, certificates_per_epoch);
894 let genesis_certificate = fake_certificates.genesis_certificate();
895 let mock_container = MockDependencyInjector::new();
896 let verifier = mock_container.build_certificate_verifier();
897
898 let verify = verifier
899 .verify_certificate(
900 genesis_certificate,
901 &fake_certificates.genesis_verifier.to_verification_key(),
902 )
903 .await;
904
905 verify.expect("verify_certificate should not fail");
906 }
907
908 #[tokio::test]
909 async fn verify_certificate_success_when_certificate_is_standard_and_valid() {
910 let (total_certificates, certificates_per_epoch) = (5, 1);
911 let fake_certificates = setup_certificate_chain(total_certificates, certificates_per_epoch);
912 let certificate = fake_certificates[0].clone();
913 let previous_certificate = fake_certificates[1].clone();
914 let mut mock_container = MockDependencyInjector::new();
915 mock_container
916 .mock_certificate_retriever
917 .expect_get_certificate_details()
918 .returning(move |_| Ok(previous_certificate.clone()))
919 .times(1);
920 let verifier = mock_container.build_certificate_verifier();
921
922 let verify = verifier
923 .verify_certificate(
924 &certificate,
925 &fake_certificates.genesis_verifier.to_verification_key(),
926 )
927 .await;
928
929 verify.expect("verify_certificate should not fail");
930 }
931
932 #[tokio::test]
933 async fn verify_certificate_chain_verifies_all_chained_certificates() {
934 struct CertificateVerifierTest {
935 certificates_unverified: Mutex<HashMap<String, Certificate>>,
936 }
937
938 impl CertificateVerifierTest {
939 fn from_certificates(certificates: &[Certificate]) -> Self {
940 Self {
941 certificates_unverified: Mutex::new(HashMap::from_iter(
942 certificates.iter().map(|c| (c.hash.to_owned(), c.to_owned())),
943 )),
944 }
945 }
946
947 async fn has_unverified_certificates(&self) -> bool {
948 !self.certificates_unverified.lock().await.is_empty()
949 }
950 }
951
952 #[async_trait]
953 impl CertificateVerifier for CertificateVerifierTest {
954 async fn verify_genesis_certificate(
955 &self,
956 _genesis_certificate: &Certificate,
957 _genesis_verification_key: &ProtocolGenesisVerificationKey,
958 ) -> StdResult<()> {
959 unimplemented!()
960 }
961
962 async fn verify_standard_certificate(
963 &self,
964 _certificate: &Certificate,
965 _previous_certificate: &Certificate,
966 ) -> StdResult<()> {
967 unimplemented!()
968 }
969
970 async fn verify_certificate(
971 &self,
972 certificate: &Certificate,
973 _genesis_verification_key: &ProtocolGenesisVerificationKey,
974 ) -> StdResult<Option<Certificate>> {
975 let mut certificates_unverified = self.certificates_unverified.lock().await;
976 let _verified_certificate = (*certificates_unverified).remove(&certificate.hash);
977 let previous_certificate =
978 (*certificates_unverified).get(&certificate.previous_hash).cloned();
979
980 Ok(previous_certificate)
981 }
982 }
983
984 let (total_certificates, certificates_per_epoch) = (10, 1);
985 let fake_certificates = setup_certificate_chain(total_certificates, certificates_per_epoch);
986 let fake_certificate_to_verify = fake_certificates[0].clone();
987 let verifier = CertificateVerifierTest::from_certificates(&fake_certificates);
988 assert!(verifier.has_unverified_certificates().await);
989
990 let verify = verifier
991 .verify_certificate_chain(
992 fake_certificate_to_verify,
993 &fake_certificates.genesis_verifier.to_verification_key(),
994 )
995 .await;
996
997 verify.expect("verify_certificate_chain should not fail");
998 assert!(!verifier.has_unverified_certificates().await);
999 }
1000
1001 #[tokio::test]
1002 async fn verify_certificate_chain_success_when_chain_is_valid() {
1003 let (total_certificates, certificates_per_epoch) = (7, 2);
1004 let fake_certificates = setup_certificate_chain(total_certificates, certificates_per_epoch);
1005 let certificate_retriever =
1006 FakeCertificaterRetriever::from_certificates(&fake_certificates);
1007 let verifier =
1008 MithrilCertificateVerifier::new(TestLogger::stdout(), Arc::new(certificate_retriever));
1009 let certificate_to_verify = fake_certificates[0].clone();
1010
1011 let verify = verifier
1012 .verify_certificate_chain(
1013 certificate_to_verify,
1014 &fake_certificates.genesis_verifier.to_verification_key(),
1015 )
1016 .await;
1017 verify.expect("verify_certificate_chain should not fail");
1018 }
1019
1020 #[tokio::test]
1021 async fn verify_certificate_chain_fails_when_chain_is_tampered() {
1022 let (total_certificates, certificates_per_epoch) = (7, 2);
1023 let mut fake_certificates =
1024 setup_certificate_chain(total_certificates, certificates_per_epoch);
1025 let index_certificate_fail = (total_certificates / 2) as usize;
1026 fake_certificates[index_certificate_fail].signed_message = "tampered-message".to_string();
1027 let certificate_retriever =
1028 FakeCertificaterRetriever::from_certificates(&fake_certificates);
1029 let verifier =
1030 MithrilCertificateVerifier::new(TestLogger::stdout(), Arc::new(certificate_retriever));
1031 let certificate_to_verify = fake_certificates[0].clone();
1032
1033 let error = verifier
1034 .verify_certificate_chain(
1035 certificate_to_verify,
1036 &fake_certificates.genesis_verifier.to_verification_key(),
1037 )
1038 .await
1039 .expect_err("verify_certificate_chain should fail");
1040
1041 assert_error_matches!(CertificateVerifierError::CertificateHashUnmatch, error)
1042 }
1043
1044 #[tokio::test]
1045 async fn verify_certificate_chain_fails_when_adversarial_with_registered_signer_forgery_through_protocol_parameters()
1046 {
1047 fn forge_certificate(
1053 certificate: Certificate,
1054 context: &CertificateChainBuilderContext,
1055 ) -> Certificate {
1056 assert_ne!(
1057 1.0, certificate.metadata.protocol_parameters.phi_f,
1058 "Adversarial protocol parameters phi_f should be different from 1.0"
1059 );
1060 let fixture = context.fixture;
1061 let signed_message = certificate.signed_message.to_owned();
1062 let mut forged_certificate = certificate;
1063 let mut forged_protocol_parameters = fixture.protocol_parameters();
1064 forged_protocol_parameters.phi_f = 1.0;
1065 let forged_single_signatures = fixture
1066 .signers_fixture()
1067 .iter()
1068 .take(1)
1069 .filter_map(|s| {
1070 let s_adversary = s
1071 .to_owned()
1072 .try_new_with_protocol_parameters(forged_protocol_parameters.clone())
1073 .unwrap();
1074
1075 s_adversary.protocol_signer.sign(signed_message.as_bytes())
1076 })
1077 .collect::<Vec<_>>();
1078 let forged_clerk = ProtocolClerk::new_clerk_from_closed_key_registration(
1079 &forged_protocol_parameters.clone().into(),
1080 &fixture.signers_fixture()[0].protocol_closed_key_registration,
1081 );
1082 let forged_multi_signature = forged_clerk
1083 .aggregate_signatures(&forged_single_signatures, signed_message.as_bytes())
1084 .unwrap();
1085 forged_certificate.signature = CertificateSignature::MultiSignature(
1086 forged_certificate.signed_entity_type(),
1087 forged_multi_signature.into(),
1088 );
1089 forged_certificate.metadata.protocol_parameters = forged_protocol_parameters;
1090
1091 forged_certificate
1092 }
1093
1094 let (total_certificates, certificates_per_epoch) = (7, 2);
1095 let fake_certificates = CertificateChainBuilder::new()
1096 .with_total_certificates(total_certificates)
1097 .with_certificates_per_epoch(certificates_per_epoch)
1098 .with_standard_certificate_processor(&|certificate, context| {
1099 if context.is_last_certificate() {
1100 forge_certificate(certificate, context)
1101 } else {
1102 certificate
1103 }
1104 })
1105 .build();
1106 let certificate_to_verify = fake_certificates[0].clone();
1107 let mock_container = MockDependencyInjector::new();
1108 let mut verifier = mock_container.build_certificate_verifier();
1109 verifier.certificate_retriever = Arc::new(FakeCertificaterRetriever::from_certificates(
1110 &fake_certificates,
1111 ));
1112
1113 let error = verifier
1114 .verify_certificate(
1115 &certificate_to_verify,
1116 &fake_certificates.genesis_verifier.to_verification_key(),
1117 )
1118 .await
1119 .expect_err("verify_certificate_chain should fail");
1120
1121 assert_error_matches!(
1122 CertificateVerifierError::CertificateChainProtocolParametersUnmatch,
1123 error
1124 )
1125 }
1126}