mithril_common/certificate_chain/
certificate_verifier.rs

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