Skip to content

seal: a timestamp's own certificate is never checked, and it now decides verdicts #331

Description

@LKSNDRTMLKV

attested_sealing_time reads the signature timestamp out of a seal and checks two things: the token's own signature, and that its messageImprint covers this signature (EN 319 122-1 clause 5.3). Both were added in #322, and together they mean a token cannot be lifted from another seal.

What is never checked is the timestamp authority's own certificate. Not its validity window at the moment it claims to have stamped, not its chain, not its revocation. A token signed by a certificate that had expired years before genTime is accepted, and so is one signed by a certificate the signer minted themselves.

Why that now decides verdicts

It used to be a reporting question — attestedSealedAt was a field a reader could weigh. #322 made it load-bearing:

SealValidationStatus::of uses judgedAt.attested to decide whether a certificate finding is a failure or an open question. With an attested moment, a certificate outside its window is totalFailed / expired; without one it is indeterminate / outOfBoundsNoPoe. The same for revocation.

So the permissive direction is the problem. Consider a seal whose certificate had expired when it was made — the case the window check exists to catch:

  • with no timestamp, the moment is this node's clock, the certificate reads expired now, and the verdict is outOfBoundsNoPoe: unproven, visible, not dismissed;
  • attach a timestamp saying a moment inside the window, signed by any key at all, and the window check passes. The verdict improves to indeterminate with no sub-indication — the same answer a sound seal gets.

The imprint check does not prevent this: it binds the token to this signature, and anyone who can edit the stored seal can compute that imprint. They cannot steal someone else's token; they can make their own.

What a real check needs

EN 319 102-1's timestamp validation building block wants the token's signing certificate validated at genTime, and Art. 42 makes a qualified timestamp a QTSP service — a Trusted List question about the TSA/QTST service type, which #324 is the prerequisite for.

Those are different amounts of work, and the first is worth having on its own:

  1. The window — read the TSA certificate's notBefore/notAfter and refuse a token stamped outside it. Purely local, no list needed, and it removes the self-made-token escalation above for any certificate that is not freshly minted.
  2. The chain and the list — whether the authority is one anybody trusts. Needs #324, and until then the honest report is that the time is attested but the attester is unknown, which the seal route's documentation already says.

A wrinkle worth deciding

This node's own local backend runs a self-signed TSA whose certificate says NOT A QUALIFIED TIMESTAMP. Under (1) its tokens still pass — the certificate is valid, it is simply nobody's. Under (2) they would not, and the local development path would have to keep reporting an attested time or every B-T-and-above local seal becomes outOfBoundsNoPoe the moment its certificate ages out. Worth settling before (2), not after.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    surface/sealMoves sealing, trusted lists or trust-service procurementtype/defectSomething published or encoded here is wrong or unbackable nowurgency/nextBlocks work already scheduled

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions