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