From 1db8be91dbfe6d0b5913b1f6437a97190b9e4480 Mon Sep 17 00:00:00 2001 From: Patrick Robertson Date: Tue, 11 Mar 2025 11:08:52 +0000 Subject: [PATCH] Improved unit tests for timestamping --- .../timestamping_enricher.py | 44 +++++++++---- tests/data/timestamp_token_digicert_com.crt | Bin 5964 -> 0 bytes tests/data/timestamping/intermediate.crt | 44 +++++++++++++ tests/data/timestamping/leaf.crt | 39 +++++++++++ .../timestamping/rfc3161-client-issue-104.tsr | Bin 0 -> 4775 bytes tests/data/timestamping/root.crt | 31 +++++++++ .../data/timestamping/timestamp_response.tsr | Bin 0 -> 4776 bytes tests/enrichers/test_timestamping_enricher.py | 61 +++++++++++------- 8 files changed, 184 insertions(+), 35 deletions(-) delete mode 100644 tests/data/timestamp_token_digicert_com.crt create mode 100644 tests/data/timestamping/intermediate.crt create mode 100644 tests/data/timestamping/leaf.crt create mode 100644 tests/data/timestamping/rfc3161-client-issue-104.tsr create mode 100644 tests/data/timestamping/root.crt create mode 100644 tests/data/timestamping/timestamp_response.tsr diff --git a/src/auto_archiver/modules/timestamping_enricher/timestamping_enricher.py b/src/auto_archiver/modules/timestamping_enricher/timestamping_enricher.py index b29d338..9742587 100644 --- a/src/auto_archiver/modules/timestamping_enricher/timestamping_enricher.py +++ b/src/auto_archiver/modules/timestamping_enricher/timestamping_enricher.py @@ -81,7 +81,7 @@ class TimestampingEnricher(Enricher): raise ValueError(f"No valid root certificate found for {tsa_url=}. Are you sure it's a trusted TSA? Or define an alternative trusted root with `cert_authorities`.") # save the timestamping certificate - cert_chain = self.save_certificate(signed) + cert_chain = self.save_certificate(signed, root_cert) # continue with saving the timestamp token tst_fn = os.path.join(self.tmp_dir, f"timestamp_token_{slugify(tsa_url)}") @@ -102,9 +102,19 @@ class TimestampingEnricher(Enricher): else: logger.warning(f"No successful timestamps for {url=}") - def verify_signed(self, timestamp_response: TimeStampResponse, message: bytes) -> None: + def verify_signed(self, timestamp_response: TimeStampResponse, message: bytes) -> x509.Certificate: """ - Verify a Signed Timestamp using the TSA provided by the Trusted Root. + Verify a Signed Timestamp Response is trusted by a known Certificate Authority. + + Args: + timestamp_response (TimeStampResponse): The signed timestamp response. + message (bytes): The message that was timestamped. + + Returns: + x509.Certificate: A valid root certificate that was used to sign the timestamp response, or None + + Raises: + ValueError: If no valid root certificate was found in the trusted root store. """ trusted_root_path = self.cert_authorities or certifi.where() @@ -148,7 +158,7 @@ class TimestampingEnricher(Enricher): logger.debug(f"Unable to verify Timestamp with CA {certificate.subject}: {e}") continue - return None + raise ValueError(f"No valid root certificate found in {trusted_root_path}.") def sign_data(self, tsa_url: str, bytes_data: bytes) -> TimeStampResponse: # see https://github.com/sigstore/sigstore-python/blob/99948d5b80525a5a104e904ffea58169dc6e0629/sigstore/_internal/timestamp.py#L84-L121 @@ -173,19 +183,31 @@ class TimestampingEnricher(Enricher): def tst_certs(self, tsp_response: TimeStampResponse): signed_data: SignedData = tsp_response.signed_data - return [x509.load_der_x509_certificate(c) for c in signed_data.certificates] + certs = [x509.load_der_x509_certificate(c) for c in signed_data.certificates] + # reorder the certs to be in the correct order + ordered_certs = [] + while(len(ordered_certs) < len(certs)): + if len(ordered_certs) == 0: + for cert in certs: + if not [c for c in certs if c.subject == cert.issuer]: + ordered_certs.append(cert) + break + else: + for cert in certs: + if cert.issuer == ordered_certs[-1].subject: + ordered_certs.append(cert) + break + return ordered_certs - - def save_certificate(self, tsp_response: TimeStampResponse) -> list[Media]: + def save_certificate(self, tsp_response: TimeStampResponse, verified_root_cert: x509.Certificate) -> list[Media]: # returns the leaf certificate URL, fails if not set - certificates = self.tst_certs(tsp_response) + certificates = self.tst_certs(tsp_response) + [verified_root_cert] cert_chain = [] - for cert in certificates: - cert_fn = os.path.join(self.tmp_dir, f"{str(cert.serial_number)[:20]}.crt") - print(cert_fn) + for i, cert in enumerate(certificates): + cert_fn = os.path.join(self.tmp_dir, f"{i+1} – {str(cert.serial_number)[:20]}.crt") with open(cert_fn, "wb") as f: f.write(cert.public_bytes(encoding=serialization.Encoding.PEM)) cert_chain.append(Media(filename=cert_fn).set("subject", cert.subject.get_attributes_for_oid(x509.NameOID.COMMON_NAME)[0].value)) diff --git a/tests/data/timestamp_token_digicert_com.crt b/tests/data/timestamp_token_digicert_com.crt deleted file mode 100644 index 592edf4ae0e96057379dd362f8671e6385a8a8d4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5964 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 TimeStampResponse: + with open("tests/data/timestamping/timestamp_response.tsr", "rb") as f: + return decode_timestamp_response(f.read()) + +@pytest.fixture +def wrong_order_timestamp_response() -> TimeStampResponse: + with open("tests/data/timestamping/rfc3161-client-issue-104.tsr", "rb") as f: + return decode_timestamp_response(f.read()) @pytest.mark.download -def test_sign_data(setup_module): +def test_download_tsr(setup_module): tsa_url = "http://timestamp.identrust.com" tsp: TimestampingEnricher = setup_module("timestamping_enricher") @@ -22,27 +26,36 @@ def test_sign_data(setup_module): result: TimeStampResponse = tsp.sign_data(tsa_url, data) assert isinstance(result, TimeStampResponse) - root_cert: x509.Certificate = tsp.verify_signed(result, data) - assert root_cert.subject.rfc4514_string() == "CN=IdenTrust Commercial Root CA 1,O=IdenTrust,C=US" + verified_root_cert = tsp.verify_signed(result, data) + assert verified_root_cert.subject.rfc4514_string() == "CN=IdenTrust Commercial Root CA 1,O=IdenTrust,C=US" # test downloading the cert - cert_chain = tsp.save_certificate(result) - assert len(cert_chain) == 2 - -def test_tsp_enricher_download_syndication(setup_module, digicert): - tsp: TimestampingEnricher = setup_module("timestamping_enricher") - - cert_chain = tsp.download_and_verify_certificate(digicert) + cert_chain = tsp.save_certificate(result, verified_root_cert) assert len(cert_chain) == 3 - assert cert_chain[0].filename == f"{tsp.tmp_dir}/74515005589773707779.crt" - assert cert_chain[1].filename == f"{tsp.tmp_dir}/95861100433808324400.crt" - assert cert_chain[2].filename == f"{tsp.tmp_dir}/15527051335772373346.crt" - -def test_tst_cert_valid(setup_module, digicert): +def test_verify_save(setup_module, timestamp_response): tsp: TimestampingEnricher = setup_module("timestamping_enricher") - - try: - tsp.verify_signed(digicert, b"4b7b4e39f12b8c725e6e603e6d4422500316df94211070682ef10260ff5759ef") - except Exception as e: - pytest.fail(f"Verification failed: {e}") \ No newline at end of file + + verified_root_cert = tsp.verify_signed(timestamp_response, b"4b7b4e39f12b8c725e6e603e6d4422500316df94211070682ef10260ff5759ef") + assert verified_root_cert.subject.rfc4514_string() == "CN=IdenTrust Commercial Root CA 1,O=IdenTrust,C=US" + + cert_chain = tsp.save_certificate(timestamp_response, verified_root_cert) + assert len(cert_chain) == 3 + + assert cert_chain[0].filename == f"{tsp.tmp_dir}/1 – 85078371663472981624.crt" + assert cert_chain[1].filename == f"{tsp.tmp_dir}/2 – 85078758028491331763.crt" + assert cert_chain[2].filename == f"{tsp.tmp_dir}/3 – 13298821034946342390.crt" + + +def test_order_crt_correctly(setup_module, wrong_order_timestamp_response): + # reference: https://github.com/trailofbits/rfc3161-client/issues/104#issuecomment-2711244010 + tsp: TimestampingEnricher = setup_module("timestamping_enricher") + + # get the certificates, make sure the reordering is working: + + ordered_certs = tsp.tst_certs(wrong_order_timestamp_response) + assert len(ordered_certs) == 2 + assert ordered_certs[0].subject.rfc4514_string() == "CN=TrustID Timestamping CA 3,O=IdenTrust,C=US" + assert ordered_certs[1].subject.rfc4514_string() == "CN=TrustID Timestamp Authority,O=IdenTrust,C=US" + +