CVE-2025-52556
Description
rfc3161-client is a Python library implementing the Time-Stamp Protocol (TSP) described in RFC 3161. Prior to version 1.0.3, there is a flaw in the timestamp response signature verification logic. In particular, chain verification is performed against the TSR's embedded certificates up to the trusted root(s), but fails to verify the TSR's own signature against the timestamping leaf certificates. Consequently, vulnerable versions perform insufficient signature validation to properly consider a TSR verified, as the attacker can introduce any TSR signature so long as the embedded leaf chains up to some root TSA. This issue has been patched in version 1.0.3. There is no workaround for this issue.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
rfc3161-clientPyPI | < 1.0.3 | 1.0.3 |
Patches
2a9a5588e3b67724a184f953eMerge commit from fork
5 files changed · +69 −31
rust/src/lib.rs+14 −29 modified@@ -738,43 +738,28 @@ fn pkcs7_verify( )) })?; } - b.build() - }; - let certs = openssl::stack::Stack::new().map_err(|e| { - pyo3::exceptions::PyValueError::new_err(format!("Unable to create certs stack: {:?}", e)) - })?; - - let signers = p7.signers(&certs, flags).map_err(|e| { - pyo3::exceptions::PyValueError::new_err(format!("Unable to create signers: {:?}", e)) - })?; - if signers.len() == 0 { - return Err(pyo3::exceptions::PyValueError::new_err("No signers found")); - } - for signer in signers { - let mut store_ctx = openssl::x509::X509StoreContext::new().map_err(|e| { - pyo3::exceptions::PyValueError::new_err(format!( - "Unable to create store context. {:?}", - e - )) - })?; - let is_valid = store_ctx - .init(&store, &signer, &certs, |ctx| ctx.verify_cert()) + b.set_purpose(openssl::x509::X509PurposeId::TIMESTAMP_SIGN) .map_err(|e| { pyo3::exceptions::PyValueError::new_err(format!( - "Unable to create verification context. {:?}", + "Unable to set purpose for store: {:?}", e )) })?; - if !is_valid { - return Err(pyo3::exceptions::PyValueError::new_err( - "Unable to verify certificate", - )); - } - } + b.build() + }; - Ok(()) + // NOTE(ww): Passing the store's certificates as the first argument + // is superfluous here, but rust-openssl requires _something_ + // here where the underlying `PKCS7_verify` allows it to be NULL. + p7.verify(&store.all_certificates(), &store, None, None, flags) + .map_err(|e| { + pyo3::exceptions::PyValueError::new_err(format!( + "Unable to verify pkcs7 signature: {:?}", + e + )) + }) } /// A Python module implemented in Rust.
rust/src/util.rs+2 −2 modified@@ -98,8 +98,8 @@ pub static HASH_ALGORITHM: LazyPyImport = LazyPyImport::new("rfc3161_client.base", &["HashAlgorithm"]); pub fn generate_random_bytes_for_asn1_biguint() -> Vec<u8> { - let mut rng = rand::thread_rng(); - let nonce_random: u64 = rng.gen_range(0..u64::MAX); + let mut rng = rand::rng(); + let nonce_random: u64 = rng.random_range(0..u64::MAX); let nonce_bytes = nonce_random.to_be_bytes(); // Remove leading 0
test/fixtures/sigstage/response-invalid-signature.tsr+0 −0 addedtest/fixtures/sigstage/response-no-embedded-cert.tsr+0 −0 addedtest/test_verify.py+53 −0 modified@@ -441,3 +441,56 @@ def test_verify_succeeds_when_leaf_cert_is_not_first() -> None: message = digest.finalize() assert verifier.verify(ts_response, message) + + +def test_verify_succeeds_without_embedded_cert() -> None: + """Ensure that a timestamp is considered valid even if it does not + contain any embedded certificates (as long as the full certificate + chain is provided to the verifier). + + The test asset was produced with timestamp-cli from sigstore/timestamp-authority: + + $ echo -n "hello > artifact + $ timestamp-cli --timestamp_server https://timestamp.sigstage.dev \ + timestamp --artifact artifact --certificate=false + + https://github.com/trailofbits/rfc3161-client/issues/162 + """ + cert_path = _FIXTURE / "sigstage" / "ts_chain.pem" + tsr_path = _FIXTURE / "sigstage" / "response-no-embedded-cert.tsr" + + certificates = cryptography.x509.load_pem_x509_certificates(cert_path.read_bytes()) + verifier = ( + VerifierBuilder() + .add_root_certificate(certificates[-1]) + .tsa_certificate(certificates[0]) + .build() + ) + + ts_response = decode_timestamp_response(tsr_path.read_bytes()) + + assert verifier.verify_message(ts_response, b"hello") + + +def test_verify_fails_invalid_tsr_signature() -> None: + """Ensure that a TSR is rejected if it has an invalid signature, + even if the certificate chain is valid. + + This test asset was produced by taking `response-sha256.tsr` + and twiddling the signature bytes to make it invalid. + """ + cert_path = _FIXTURE / "sigstage" / "ts_chain.pem" + tsr_path = _FIXTURE / "sigstage" / "response-invalid-signature.tsr" + + certificates = cryptography.x509.load_pem_x509_certificates(cert_path.read_bytes()) + verifier = ( + VerifierBuilder() + .add_root_certificate(certificates[-1]) + .tsa_certificate(certificates[0]) + .build() + ) + + ts_response = decode_timestamp_response(tsr_path.read_bytes()) + + with pytest.raises(VerificationError, match="signature failure"): + verifier.verify_message(ts_response, b"hello")
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4News mentions
0No linked articles in our index yet.