mithril_common/certificate_chain/
certificate_verifier.rs

1//! A module used to validate the Certificate Chain created by an aggregator
2//!
3use 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/// [CertificateVerifier] related errors.
25#[derive(Error, Debug)]
26pub enum CertificateVerifierError {
27    /// Error raised when the multi signatures verification fails.
28    #[error("multi signature verification failed: '{0}'")]
29    VerifyMultiSignature(String),
30
31    /// Error raised when the Genesis Signature stored in a [Certificate] is invalid.
32    #[error("certificate genesis error")]
33    CertificateGenesis(#[from] ProtocolGenesisError),
34
35    /// Error raised when the hash stored in a [Certificate] doesn't match a recomputed hash.
36    #[error("certificate hash unmatch error")]
37    CertificateHashUnmatch,
38
39    /// Error raised when validating the certificate chain if a previous [Certificate] hash isn't
40    /// equal to the current certificate `previous_hash`.
41    #[error("certificate chain previous hash unmatch error")]
42    CertificateChainPreviousHashUnmatch,
43
44    /// Error raised when validating the certificate chain if the current [Certificate]
45    /// `signed_message` doesn't match the hash of the `protocol_message` of the current certificate
46    #[error("certificate protocol message unmatch error")]
47    CertificateProtocolMessageUnmatch,
48
49    /// Error raised when validating the certificate chain if the current [Certificate]
50    /// `aggregate_verification_key` doesn't match the signed `next_aggregate_verification_key` of the previous certificate
51    /// (if the certificates are on different epoch) or the `aggregate_verification_key` of the previous certificate
52    /// (if the certificates are on the same epoch).
53    #[error("certificate chain AVK unmatch error")]
54    CertificateChainAVKUnmatch,
55
56    /// Error raised when validating the certificate chain if the current [Certificate]
57    /// `protocol_parameters` don't match the signed `next_protocol_parameters` of the previous certificate
58    /// (if the certificates are on different epoch) or the `protocol_parameters` of the previous certificate
59    /// (if the certificates are on the same epoch).
60    #[error("certificate chain protocol parameters unmatch error")]
61    CertificateChainProtocolParametersUnmatch,
62
63    /// Error raised when validating the certificate chain if the current [Certificate]
64    /// `epoch` doesn't match the signed `current_epoch` of the current certificate
65    #[error("certificate epoch unmatch error")]
66    CertificateEpochUnmatch,
67
68    /// Error raised when validating the certificate chain if the current [Certificate]
69    /// `epoch` is neither equal to the previous certificate `epoch` nor to the previous certificate `epoch - 1`
70    #[error("certificate chain missing epoch error")]
71    CertificateChainMissingEpoch,
72
73    /// Error raised when validating the certificate chain if the chain loops.
74    #[error("certificate chain infinite loop error")]
75    CertificateChainInfiniteLoop,
76
77    /// Error raised when [CertificateVerifier::verify_genesis_certificate] was called with a
78    /// certificate that's not a genesis certificate.
79    #[error("can't validate genesis certificate: given certificate isn't a genesis certificate")]
80    InvalidGenesisCertificateProvided,
81
82    /// Error raised when [CertificateVerifier::verify_standard_certificate] was called with a
83    /// certificate that's not a standard certificate.
84    #[error("can't validate standard certificate: given certificate isn't a standard certificate")]
85    InvalidStandardCertificateProvided,
86}
87
88/// CertificateVerifier is the cryptographic engine in charge of verifying multi signatures and
89/// [certificates](Certificate)
90#[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    /// Verify Genesis certificate
95    async fn verify_genesis_certificate(
96        &self,
97        genesis_certificate: &Certificate,
98        genesis_verification_key: &ProtocolGenesisVerificationKey,
99    ) -> StdResult<()>;
100
101    /// Verify Standard certificate
102    async fn verify_standard_certificate(
103        &self,
104        certificate: &Certificate,
105        previous_certificate: &Certificate,
106    ) -> StdResult<()>;
107
108    /// Verify if a Certificate is valid and returns the previous Certificate in the chain if exists
109    async fn verify_certificate(
110        &self,
111        certificate: &Certificate,
112        genesis_verification_key: &ProtocolGenesisVerificationKey,
113    ) -> StdResult<Option<Certificate>>;
114
115    /// Verify that the Certificate Chain associated to a Certificate is valid
116    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
133/// MithrilCertificateVerifier is an implementation of the CertificateVerifier
134pub struct MithrilCertificateVerifier {
135    logger: Logger,
136    certificate_retriever: Arc<dyn CertificateRetriever>,
137}
138
139impl MithrilCertificateVerifier {
140    /// MithrilCertificateVerifier factory
141    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    /// Verify a certificate
387    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        // Create an adversarial certificate with a forged multi signature:
1058        // - with the valid signed message
1059        // - with the valid aggregate verification key (valid stake distribution)
1060        // - by a legit adversarial registered signer in the signing stake distribution
1061        // - with adversarial protocol parameters (phi_f = 1.0, i.e. all lotteries are won by the adversarial signer and quorum is reached)
1062        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}