Timestamp Authority
On this page
Timestamp Authority is a Rust service that answers one request with one signed statement: this exact fingerprint of a document existed at this second. It implements RFC 3161 time-stamping for signed PDFs, and it is also a small certificate authority of its own — it issues, stores, and renews the certificate it signs with, publishes revocation information for it, and records every token it has ever produced.
I built it because a digital signature only proves who signed; it does not prove when. A signer's certificate expires after a few years and can be revoked tomorrow, and without trusted evidence of the signing time a reader cannot tell whether a signature was made while the certificate was still good. A timestamp from an independent authority, embedded in the PDF, is that evidence, and it keeps a signature verifiable long after the certificate behind it has gone.
At a glance
| Language & framework | Rust, with actix-web, sqlx on PostgreSQL, and OpenSSL's time-stamping engine through its C interface |
|---|---|
| Architecture | Flat Layered |
| Version | 2.11.2 |
| Status | Stable, in production |
| Form | A standalone HTTP service: public time-stamping and revocation endpoints, and an authenticated API for administration |
| Source code | Private |
What it does
A. Stamps a document's fingerprint with the time
A signing application sends a time-stamp request that carries only a hash of the document, never the document itself. The service answers with a signed token that binds that hash to the current time, a unique serial number, and the client's nonce, and the application embeds the token in the PDF next to the signature.
B. Issues and renews its own signing certificate
The certificate that signs every token is created by the service itself, under its own intermediate CA, at the first start. When it nears the end of its validity, the next request renews it before signing, so the service never stamps with an expired certificate and nobody has to remember to replace one.
C. Lets anyone check its certificates
A validator that reads a timestamp years later must be able to trust the certificate that signed it. The service publishes its intermediate CA certificate, a certificate revocation list, and an OCSP responder, all without authentication, at the addresses written into its certificates.
D. Keeps a record of every token
Every response is stored with its time, policy, hash algorithm, document fingerprint, nonce, and the signing certificate's serial number, so any token the service ever issued can be traced back to the request that produced it.
E. Controls administration
Time-stamping and revocation checks are public; revoking a certificate and managing users, roles, and access tokens require a signed token with the right role.
Interface
| Capability | Access |
|---|---|
| Time-stamp a hash: an RFC 3161 request in, a signed response out | public |
| The intermediate CA's certificate, in DER, at the address certificates point to | public |
| The certificate revocation list | public |
| OCSP, by GET and by POST | public |
| Readiness, covering the database and the root CA | public |
| Revoke a certificate with an RFC 5280 reason | token |
| Log in; manage users, roles, passwords, and access tokens | token, by role |
Any RFC 3161 client can use it. Here is the round trip with OpenSSL, against a placeholder host:
Standards
| Standard | What it guarantees here |
|---|---|
| RFC 3161, Time-Stamp Protocol | requests and tokens that PDF signers and validators already understand |
| CMS SignedData (RFC 5652) | a token signed in the same envelope format as the signature it protects |
| X.509 v3 and RFC 5280 | a signing certificate restricted to time-stamping, and a revocation list any validator can read |
| RFC 6960, OCSP | revocation answers for the signing certificate, by GET or POST |
| Authority Information Access and CRL Distribution Points | certificates that tell a validator where to find the issuer and the revocation status |
| JSON Web Tokens with RS256 | access tokens for administration, signed with a key the service can rotate |
| W3C Trace Context and OpenTelemetry | traces, logs, and metrics that continue the caller's trace |
Inside
The service follows the same Flat Layered shape as the certificate authority it was branched from: routes, controllers, services, repositories, and helpers side by side, one level deep, with one file per operation — answer a time-stamp request, create the signing certificate, answer an OCSP query, build a revocation list. The two services shared their code up to the point where they branched; the time-stamping engine, the signer's lifecycle, and the token record were built after it, and fixes made later in one were deliberately carried over to the other.
Design decisions
The cryptography is OpenSSL's, and only the decisions are mine. Time-stamping has many fields to get exactly right, and OpenSSL already has a time-stamp response engine that produces them, so I drive that engine through bindings generated for a narrow list of functions instead of assembling the structure by hand. Two values are not left to OpenSSL. The serial number of each token comes from a database sequence of its own, separate from certificate serial numbers, and is handed to the engine through a callback. The time is read once per request from the host's clock, which is kept synchronised with NTP, and handed over the same way, so the time in the token and the time in the record are the same second.
A token must be readable by the tools that will check it. In the first version, a widely used Python PDF-signing library received responses with a status but no token inside. The cause was in how the request reached OpenSSL — the engine was given the original request instead of the adjusted one — and the fix changed four things together: the request is re-encoded before it is passed on, the signing certificate is always included in the response — also when the request does not ask for it — the digest used matches the length of the fingerprint the client sent, and a response that would carry no token fails immediately instead of being returned as if it were an answer.
The chain stops below the root. Each token carries the signing certificate and the intermediate CA, so a validator can build the path, but not the root: the root is a trust anchor that the validator must already hold, and including it only makes every token larger. An early version included it; I removed it again.
The signing certificate is made inside, never requested from outside. The only certificate this service signs tokens with is one it creates itself. Its profile is fixed in code rather than configuration — not a CA, key usage critical and limited to digital signature and non-repudiation, extended key usage critical and limited to time-stamping — because two incidents showed what a profile left loose can do: one certificate was issued without the time-stamping purpose, and an earlier batch was issued as version 1 despite carrying version 3 extensions. The service creates the certificate at the first start, refuses to start if it cannot, and renews it inside the request path before it can expire.
Every token leaves evidence, and the evidence holds no document. The service never receives the document, only its hash, so it can log and store the fingerprint in full without holding anything confidential. Each response is stored with the time, policy, algorithm, fingerprint, nonce, signing certificate, and request id, and each request emits telemetry attributes for non-repudiation — who asked, from where, and what was stamped — with the raw chain of forwarding addresses kept as evidence only. Responses are sent with no-store, because a cached timestamp is a wrong timestamp.
Revocation for the signing certificate works the same way as in the certificate authority. The OCSP responder has a signer of its own, echoes nonces, includes the intermediate CA, and backdates the start of each answer slightly for clients with slow clocks; the revocation list is version 2, built through OpenSSL's C interface. The addresses in every certificate use plain HTTP, because checking revocation over TLS would require checking the TLS certificate first, and the responder's certificate points to its issuer, never to itself.
Trust starts from the root, and operations follow the certificate authority's lessons. The intermediate CA is verified against the root at every start. Administration uses signed tokens with key identifiers and revocable token ids; telemetry goes through OpenTelemetry with delta metrics; readiness requires both the database and the root CA; and migrations travel inside the image, because applying them only to an empty database had left later schema changes out of staging and production.
Specifications
| Inputs | RFC 3161 time-stamp requests carrying a document hash and an optional nonce; OCSP queries and list downloads from any validator |
|---|---|
| Outputs | Signed time-stamp responses, OCSP answers, version 2 revocation lists, and the intermediate CA certificate |
| Time | The host's NTP-synchronised clock, read once per request; accuracy of one second |
| Keys | RSA 4096 for the signing certificate and the responder |
| Storage | PostgreSQL, holding every issued token and certificate, with migrations shipped inside the image |
| Verified with | OpenSSL's time-stamp tools and a PDF-signing library that embeds the tokens |
| Runs as | A container image, as a non-root user, with configuration mounted from a volume |
Related
The certificate authority this service was branched from, which shares its revocation and access design.
The toolkit that applies the service's migrations when a release reaches production.
Where the service's audit attributes, traces, and metrics go once they leave it.
Terms
- Timestamp authority
- A trusted service that signs a statement that a given piece of data existed at a given time.
- Message imprint
- The hash of the document that a time-stamp request carries in place of the document itself.
- Nonce
- A random number a client puts in a request and expects back in the answer, so an old answer cannot be replayed.
- Intermediate CA
- A certificate authority whose certificate is signed by a root, and which signs the certificates that do the actual work.
- OCSP
- Online Certificate Status Protocol: a client asks whether one certificate is still valid and receives a signed answer.
- Trust anchor
- A root certificate a validator already trusts, at which every chain it checks must end.
Technologies covered
- Rust
- PostgreSQL
- OpenTelemetry
- OCSP
- X.509