An RFC 3161 timestamp token: a time-stamping authority's assertion that some bytes existed when it saw them.
PdfElixide.Signature.timestamp/1 reaches the one a signature carries, and
parse/1 takes a token from anywhere else. A signature's own :signing_time
is the signer's unverifiable claim; a timestamp is a third party's, and
verify/1 is what checks the authority actually made it — while
PdfElixide.Signature.verify_timestamp/2 checks that it was made over the
signature carrying it, which is a separate question with a separate answer:
{:ok, [signature]} = PdfElixide.Signature.list(doc)
{:ok, timestamp} = PdfElixide.Signature.timestamp(signature)
PdfElixide.Signature.Timestamp.verify(timestamp)
#=> {:ok, :valid}The "Timestamps ask three separate questions" section of the Signatures guide sets out which call answers which.
What a verdict proves
verify/1 checks one thing: that the token carries an authentic signature
from the certificate embedded in it, over the time and imprint it states. Four
things it does not establish:
- That the timestamp covers anything in particular.
:message_imprintis a digest of whatever was timestamped, and it is compared here to nothing at all: until it is matched against something, a:validtimestamp is a valid timestamp over unknown bytes.PdfElixide.Signature.verify_timestamp/2performs that match for a token reached from a signature. - That the authority is who the token says. No certificate is chained to
a root, checked against a revocation list, or compared to any list of
trusted authorities.
:tsa_nameis the token's own claim and is not checked against the certificate that signed it. - That the stated time is right. A timestamp authority that signs a time of its choosing produces a token that verifies.
- That the certificate was valid then. Its validity window is compared to nothing, so an expired or not-yet-valid certificate verifies exactly like a current one.
Tokens that cannot be verified
A token comes in two shapes, and parse/1 reads both: a full CMS-wrapped
TimeStampToken, and a bare TSTInfo. Only the first carries an authority's
signature, so verify/1 reports %PdfElixide.Error{reason: :invalid_pdf} for
a bare one rather than a verdict — there is nothing there to check. The same
reason covers a token whose authority signed with an algorithm this library
cannot verify, which is why the answer is :valid or :invalid and never the
third state PdfElixide.Signature.verify/2 reports.
A timestamp is a plain value with no handle behind it, so it survives
PdfElixide.Document.close/1 and can be passed between processes freely.
Summary
Functions
Parses a DER-encoded RFC 3161 timestamp token.
Same as parse/1, but raises PdfElixide.Error on failure.
Verifies the timestamp authority's signature over the token.
Same as verify/1, but raises PdfElixide.Error on failure.
Types
@type hash_algorithm() :: :sha1 | :sha256 | :sha384 | :sha512 | :unknown
The digest algorithm the timestamped imprint was made with.
:unknown is an algorithm identifier this library does not recognize, which
leaves :message_imprint uninterpretable rather than wrong.
@type t() :: %PdfElixide.Signature.Timestamp{ hash_algorithm: hash_algorithm(), message_imprint: binary(), policy_oid: String.t(), serial: String.t(), time: DateTime.t(), token: binary(), tsa_name: String.t() | nil }
One parsed timestamp token.
:token— the DER the timestamp was parsed from, with any trailing padding removed.verify/1reads it, and it is what to hand to another decoder or to whatever embeds it; nothing here writes one back into a document.:time— the time the authority says it issued the token. UnlikePdfElixide.Signature's:signing_time, which is a raw PDF date string, this is aDateTimein UTC: a timestamp's generation time is a parsed value where a signature's/Mis a string the document supplied. Resolution is one second.:serial— the authority's serial number for this token, as uppercase hexadecimal with no0xprefix and no fixed width.:policy_oid— the timestamping policy the token was issued under, in dotted-decimal form.:tsa_name— the name the authority gave itself in the token. A distinguished name for the usual directory-name form, otherwise the raw URI, DNS or email value.nilcovers two cases that cannot be told apart here: a token naming no authority at all, and one naming it in a form this library does not render — anotherName, anediPartyName, an IP address or a registered identifier. Reaching the certificate that actually signed the token is possible only for a document timestamp, whose blob is the token:PdfElixide.Signature.certificate/1returns it for an:rfc3161signature, and for one carried in a signature's unsigned attributes returns that signature's signer instead.:hash_algorithm,:message_imprint— the digest that was timestamped and the algorithm that produced it.verify/1does not compare it to content; see "What a verdict proves".
@type verdict() :: :valid | :invalid
What a verify/1 call concluded. "What a verdict proves" in the module
documentation says what :valid does and does not establish.
Functions
@spec parse(binary()) :: {:ok, t()} | {:error, PdfElixide.Error.t()}
Parses a DER-encoded RFC 3161 timestamp token.
Reads both a full CMS-wrapped TimeStampToken and a bare TSTInfo, and
tolerates the zero padding a token stored in a PDF carries — so the
:contents of an :rfc3161 signature can be passed straight in, though
PdfElixide.Signature.timestamp/1 is the shorter way to the same place.
{:ok, timestamp} = PdfElixide.Signature.Timestamp.parse(der)
timestamp.time
#=> ~U[2026-08-23 07:50:03Z]Reports %PdfElixide.Error{reason: :invalid_pdf} when the bytes are neither
shape, carry trailing bytes other than zero padding, or state a generation
time no date can represent.
Same as parse/1, but raises PdfElixide.Error on failure.
@spec verify(t()) :: {:ok, verdict()} | {:error, PdfElixide.Error.t()}
Verifies the timestamp authority's signature over the token.
{:ok, :valid} means the token is authentically the authority's and has not
been altered since. It says nothing about what was timestamped: see "What a
verdict proves" in the module documentation, which is also where the trust,
time and certificate-validity limits are.
PdfElixide.Signature.Timestamp.verify(timestamp)
#=> {:ok, :valid}Reports %PdfElixide.Error{reason: :invalid_pdf} rather than a verdict when
the token is a bare TSTInfo and so carries no signature to check, and when
the authority signed it with an algorithm this library cannot verify.
Same as verify/1, but raises PdfElixide.Error on failure.