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