From 4c1c8953ca77c843a72508a4c4c0eef994491ca8 Mon Sep 17 00:00:00 2001 From: Patrick Robertson Date: Wed, 29 Jan 2025 12:20:52 +0100 Subject: [PATCH] Add unit tests for timestamping_enricher --- .../timestamping_enricher.py | 25 +++++++---- tests/data/timestamp_token_digicert_com.crt | Bin 0 -> 5964 bytes tests/enrichers/test_timestamping_enricher.py | 41 ++++++++++++++++++ 3 files changed, 58 insertions(+), 8 deletions(-) create mode 100644 tests/data/timestamp_token_digicert_com.crt create mode 100644 tests/enrichers/test_timestamping_enricher.py diff --git a/src/auto_archiver/modules/timestamping_enricher/timestamping_enricher.py b/src/auto_archiver/modules/timestamping_enricher/timestamping_enricher.py index a7a0aee..4d5dd20 100644 --- a/src/auto_archiver/modules/timestamping_enricher/timestamping_enricher.py +++ b/src/auto_archiver/modules/timestamping_enricher/timestamping_enricher.py @@ -6,11 +6,11 @@ from importlib.metadata import version from asn1crypto.cms import ContentInfo from certvalidator import CertificateValidator, ValidationContext from asn1crypto import pem +from asn1crypto.core import Asn1Value import certifi from auto_archiver.core import Enricher from auto_archiver.core import Metadata, ArchivingContext, Media -from auto_archiver.core import Extractor class TimestampingEnricher(Enricher): @@ -45,13 +45,10 @@ class TimestampingEnricher(Enricher): from slugify import slugify for tsa_url in self.tsa_urls: try: - signing_settings = SigningSettings(tsp_server=tsa_url, digest_algorithm=DigestAlgorithm.SHA256) - signer = TSPSigner() message = bytes(data_to_sign, encoding='utf8') - # send TSQ and get TSR from the TSA server - signed = signer.sign(message=message, signing_settings=signing_settings) + signed = self.sign_data(tsa_url, message) # fail if there's any issue with the certificates, uses certifi list of trusted CAs - TSPVerifier(certifi.where()).verify(signed, message=message) + self.verify_signed(signed, message) # download and verify timestamping certificate cert_chain = self.download_and_verify_certificate(signed) # continue with saving the timestamp token @@ -72,9 +69,22 @@ class TimestampingEnricher(Enricher): else: logger.warning(f"No successful timestamps for {url=}") + def verify_signed(self, signed: bytes, message: bytes) -> None: + verifier = TSPVerifier(certifi.where()) + verifier.verify(signed, message=message) + + def sign_data(self, tsa_url: str, bytes_data: bytes) -> bytes: + signing_settings = SigningSettings(tsp_server=tsa_url, digest_algorithm=DigestAlgorithm.SHA256) + signer = TSPSigner() + # send TSQ and get TSR from the TSA server + return signer.sign(message=bytes_data, signing_settings=signing_settings) + + def load_tst_certs(self, signed: bytes) -> list[Asn1Value]: + return ContentInfo.load(signed)["content"]["certificates"] + def download_and_verify_certificate(self, signed: bytes) -> list[Media]: # returns the leaf certificate URL, fails if not set - tst = ContentInfo.load(signed) + certificates = self.load_tst_certs(signed) trust_roots = [] with open(certifi.where(), 'rb') as f: @@ -82,7 +92,6 @@ class TimestampingEnricher(Enricher): trust_roots.append(der_bytes) context = ValidationContext(trust_roots=trust_roots) - certificates = tst["content"]["certificates"] first_cert = certificates[0].dump() intermediate_certs = [] for i in range(1, len(certificates)): # cannot use list comprehension [1:] diff --git a/tests/data/timestamp_token_digicert_com.crt b/tests/data/timestamp_token_digicert_com.crt new file mode 100644 index 0000000000000000000000000000000000000000..592edf4ae0e96057379dd362f8671e6385a8a8d4 GIT binary patch literal 5964 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