Repository navigation
v0.3.0
v0.3.0 — whole-document integrity, TSA purpose validation, hardened signing
This release closes a multi-agent security/quality review (3× Claude Opus 5.5 + Codex gpt-5.6-sol, every finding reproduced or confirmed in code before fixing). It is a behaviour-changing release for verification: several documents that previously "passed" were not what was signed.
Verification — false accepts fixed
- Whole-document integrity.
SignatureReport::document_intactrequires the last valid signature or document timestamp to cover the entire file, or the tail to be a DSS-only incremental update.all_valid(),all_trusted()and the CLI exit code now require it, so the classic "append an incremental update after signing" attack is rejected.all_trusted()isfalsewithout a trust store. - Document timestamps are chain-validated against the roots, as TSAs, and report their own
chain_trusted. A later trusted archival timestamp anchors earlier TSAs (the B-LTA chain); the authenticated time actually used is exposed asVerifiedSignature::trusted_time. - A TSA must carry
id-kp-timeStamping(RFC 3161 §2.3) before itsgenTimeis used — an ordinary certificate under a trusted root can no longer forge a "trusted signing time". LeafkeyUsage(digitalSignature/nonRepudiation) is enforced for signers. - Revocation by date: an authenticated CRL/OCSP entry whose revocation date is at or before the validation time counts even if the evidence was issued later (RFC 3161 App. B) — previously the OCSP a B-LT signer embeds could never flag a revoked signer. OCSP
CertIDs are matched under the responder's hash algorithm; delegated responders must be valid. - RFC 5280 §6.1.3 extension processing: unknown critical or malformed/duplicated extensions fail the path;
pathLenConstraintcounts non-self-issued intermediates only; inner/outer signature algorithms must agree; ECDSA is verified under the declared digest. - Bounded path building (256 candidate evaluations, deduplicated pool): a hostile certificate pool in the CMS can no longer make verification run for hours.
- CMS profile checks:
content-type, singlemessage-digest, ESSsigning-certificate(-v2)binding, exactly oneSignerInfo, consistentsignatureAlgorithm(RSASSA-PSS explicitly rejected), Ed25519 + SHA-512. - A malformed
/ByteRangeor/Contentsis reported as an invalid entry instead of aborting the whole report; checked arithmetic throughout (der_total_lencould overflow on a crafted/Contents).
Signing — invalid outputs fixed
- Encrypted PDFs are refused (even with an empty user password: the update was written in clear and the document became unreadable). Documents certified with DocMDP
P=1are refused too. /DSSis merged with an existing one (dedup by DER,/VRIkept) instead of replaced — a second B-LT signature no longer downgrades the first to B-T./Info(and other trailer entries) are carried into the new trailer; Title/Author no longer vanish after signing./Reason,/Name,/Location,/ContactInfoare written as UTF-16BE text strings — "Aprovação" no longer renders as mojibake./Mis written by default and the CMSsigning-timeattribute is dropped (ETSI EN 319 142-1 baseline). Poppler shows the/Mtime.- Files with bytes before
%PDF-get header-relative xref offsets; xref-stream offsets widen past 4 GiB; the signature placeholder is located inside the signature object rather than by byte search. - CFF OpenType fonts embed as
/FontFile3,.ttccollections are rejected, CMYK JPEGs are handled, text is laid out beside the logo, an out-of-range page or NaN geometry is an error,/NeedAppearancesis cleared, andsign_pdf_filewrites atomically.
TSA client
genTimewith fractional seconds is accepted (many production TSAs emit it; signing at B-T+ against them failed before).- A nonce is sent and checked; the token's signature is verified before it is embedded; connect/overall timeouts and response-size limits on both the
httpsand plain-HTTP paths; strict status-line and chunked-encoding parsing.
CLI
--password-file <FILE>(-= stdin);KEY_PASSWORDis hidden from--help;--contact-info,--signature-capacity; appearance flags require--text;document_intactis printed and enforced.
Build / API
rust-version = "1.88"(the locked dependency set already required it).- New
testkitfeature gates the fixture builders and adds an in-process RFC 3161 mock TSA; B-T / B-LT / B-LTA are now tested offline. VerifiedSignaturegainedis_timestampandtrusted_time;SignatureReportgaineddocument_intact; both structs are#[non_exhaustive].- README commands fixed (
--features cli,-p), PKITS harness fails when its data is missing.
Full details in the commit message of cd993bb.