Skip to content

Use note-style formatting for SubtreeSignature - #281

Merged
AlCutter merged 2 commits into
transparency-dev:mainfrom
AlCutter:note_subtree_sig
Sep 16, 2026
Merged

AlCutter merged 2 commits into
transparency-dev:mainfrom
AlCutter:note_subtree_sig

Conversation

@AlCutter

Copy link
Copy Markdown
Collaborator

This PR updates the support for subtree signatures to return/consume note-style signature lines.

Adds a small inter-op test with a subtree signature created by torchwood to check compatibility.

@AlCutter
AlCutter requested a review from phbnf September 15, 2026 18:42
@AlCutter
AlCutter requested a review from a team as a code owner September 15, 2026 18:42
@codecov-commenter

codecov-commenter commented Sep 15, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 75.38462% with 16 lines in your changes missing coverage. Please review.
✅ Project coverage is 87.32%. Comparing base (fa00c16) to head (41ac158).
⚠️ Report is 192 commits behind head on main.

Files with missing lines Patch % Lines
note/note_cosigv1.go 75.38% 16 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #281      +/-   ##
==========================================
+ Coverage   82.57%   87.32%   +4.75%     
==========================================
  Files           5       10       +5     
  Lines         241      994     +753     
==========================================
+ Hits          199      868     +669     
- Misses         30      125      +95     
+ Partials       12        1      -11     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Comment thread note/note_cosigv1.go Outdated
func SubtreeTimestamp(s []byte) (time.Time, error) {
sigLine, ok := strings.CutPrefix(string(s), "— ")
if !ok {
return time.UnixMilli(0), errMalformedSig

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit, should this be time.UnixMilli(0).UTC()?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've dinged this func entirely, with the other changes the timestamp will always be zero here anyway.

However, I regret having time.Time involved here because of exactly this abiguity, so I've deprecated CoSigV1Timestamp (I could actually just flat out remove it - we're still at v0) and replaced with CosignatureTimestamp which returns the underlying uint64 instead, much cleaner.

Comment thread note/note_cosigv1.go Outdated
type SubtreeSigner interface {
note.Signer
// SignSubtree returns a note-style signature line over the subtree described by the provided arguments.
SignSubtree(timestamp uint64, logOrigin string, start, end uint64, root []byte) ([]byte, error)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a different PR, should we remove "timestamp" from the argument list? Is there a spec allowed use case at the moment for signing subtrees with a non-0 timestamp?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've rolled it in here, may as well tidy that up while I'm here!

Comment thread note/note_cosigv1.go Outdated
}
sigRaw = sigRaw[keyHashSize:]
t := binary.BigEndian.Uint64(sigRaw[:timestampSize])
// Timestamp must be zero if start > 0.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From https://c2sp.org/tlog-witness@main#sign-subtree:

If the cosignature format supports timestamps, the timestamp MUST be zero.

So maybe this should always be 0? I can see an argument for keeping the current behaviour to make it possible to call VerifySubtree on full tree signatures. But in practice, will VerifySubtree be ever called on a checkpoint signature?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've left it as is for now, mainly because:

Semantically, a v1 subtree cosignature is a statement that the subtree with the specified root hash is consistent with all other historical views observed by the cosigner of the log identified by the origin line. If the timestamp is not zero, it is also a statement that, as of the specified time, this is the largest consistent tree the cosigner has observed for the log.

Practically, you're probably right that nobody would ever use this path to verify a checkpoint sig, but technically we'd be refusing to validate a signature which was actually valid, which feels wrong.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense! Maybe leave a comment then to explain why then?

Comment thread note/note_cosigv1.go Outdated
if timestamp > math.MaxInt64 {
return nil, errInvalidTimestamp
}
if start > end {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is good, but still leaves the door open for invalid subtrees.

Specs say The half-open interval [start, end) MUST be a valid subtree per [draft-ietf-plants-merkle-tree-certs-03](https://datatracker.ietf.org/doc/html/draft-ietf-plants-merkle-tree-certs-03), Section 4.1, and end MUST be less than or equal to the checkpoint size.

So in the interest of clarity, I'd say that either we should remove this check, or we should do a full check with something along the lines of https://github.com/transparency-dev/merkle/blob/a490ef305a5bc3e556495fd824681d090d832d27/proof/proof.go#L311.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, fair; I've added the full check.

Comment thread note/note_cosigv1.go Outdated
}
sigRaw = sigRaw[keyHashSize:]
t := binary.BigEndian.Uint64(sigRaw[:timestampSize])
// Timestamp must be zero if start > 0.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense! Maybe leave a comment then to explain why then?

@AlCutter
AlCutter merged commit 091ce41 into transparency-dev:main Sep 16, 2026
14 checks passed
@AlCutter
AlCutter deleted the note_subtree_sig branch September 16, 2026 15:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants