mithril_common/certificate_chain/
certificate_verifier.rs

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