refactor(sigall): recompute signing digests from package contents - #947
Merged
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #947 +/- ##
==========================================
+ Coverage 95.70% 95.75% +0.05%
==========================================
Files 55 55
Lines 5797 5778 -19
Branches 1467 1463 -4
==========================================
- Hits 5548 5533 -15
+ Misses 105 103 -2
+ Partials 144 142 -2
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
signPackage now derives its digests from the package's own inputs, outputs and quote, so the digests field is redundant and has been removed from the transport format; packages from older builds still parse, with the field ignored. Digest names are version-anchored (v0 = unframed concatenation); the deprecated pre-v0 message builder is removed and signers emit one signature per supported format. Melt packages must carry a quote and witness signatures are shape-checked on deserialize.
robwoodgate
force-pushed
the
refactor/sigall-signing-package
branch
from
August 10, 2026 16:15
48c1ab6 to
897275d
Compare
Open
3 tasks
3 tasks
KvngMikey
added a commit
to KvngMikey/nuts
that referenced
this pull request
Aug 11, 2026
The digests object is computable from the rest of the package, so carrying it added nothing a signer could not derive itself. Worse, a signer that trusts a supplied digest signs a message it has never checked against the transaction it is approving. The legacy entry hashed the pre-amount-binding format (secret||C||B_), which NUT-11 no longer specifies. Without amount binding a signature commits to which blinded outputs exist but not to their values, letting amounts be reordered across outputs while the signature and the mint's balance check both still pass. Carrying that digest gave a superseded format renewed standing. The current entry hashed today's aggregation format, a label that goes stale the moment the message format changes (see cashubtc#404). Dropping both leaves the package independent of any aggregation scheme. Adds a normative requirement that signers derive the message to sign from the package contents themselves, and regenerates the sigallA vector without digests. Addresses review feedback from @robwoodgate. Serialized vector matches the one pinned in cashubtc/cashu-ts#947.
Collaborator
Author
|
Successfully created backport PR for |
robwoodgate
added a commit
that referenced
this pull request
Aug 11, 2026
# Description Backport of #947 to `v4-dev`.
75 tasks
KvngMikey
added a commit
to KvngMikey/nuts
that referenced
this pull request
Aug 12, 2026
The digests object is computable from the rest of the package, so carrying it added nothing a signer could not derive itself. Worse, a signer that trusts a supplied digest signs a message it has never checked against the transaction it is approving. The legacy entry hashed the pre-amount-binding format (secret||C||B_), which NUT-11 no longer specifies. Without amount binding a signature commits to which blinded outputs exist but not to their values, letting amounts be reordered across outputs while the signature and the mint's balance check both still pass. Carrying that digest gave a superseded format renewed standing. The current entry hashed today's aggregation format, a label that goes stale the moment the message format changes (see cashubtc#404). Dropping both leaves the package independent of any aggregation scheme. Adds a normative requirement that signers derive the message to sign from the package contents themselves, and regenerates the sigallA vector without digests. Also clarifies the witness encoding. NUT-11 defines Proof.witness as a serialized JSON string, but the package schema showed witness as a JSON object without saying which representation applied. An implementer could serialize it before putting it in the package, or pass the package's object straight into Proof.witness; both parse as valid JSON and fail only later. The object form is correct and is what the vector already encodes, so this documents the field rather than changing it. The accompanying note also states that inputs[].secret keeps the representation it has in Proof.secret, since documenting only witness invites the opposite mistake. Addresses review feedback from @robwoodgate and Copilot. Serialized vector matches the one pinned in cashubtc/cashu-ts#947.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
The SigAll signing package carried precomputed digests that
signPackagesigned directly. Recomputing them from the package's own inputs, outputs and quote is simpler and more robust: a signer always signs exactly what the package describes, and the transport no longer duplicates data it already contains. With that, thedigestsfield and thevalidateDigestoption are redundant and removed.The
sigallAprefix is kept: a package from an older build still parses, with itsdigestsfield ignored and stripped, and the fields that matter (inputs, outputs, quote, witness) are unchanged.Changes
signPackagerecomputes digests from package contents and emits one signature per supported format.SigAllSigningPackagedropsdigests;deserializePackagedrops thevalidateDigestoption and rebuilds the package from validated fields only. New guards: melt packages require a non-emptyquote(it is part of the signed transcript), andwitness.signaturesmust be a string array.SigAllDigestsis now{ v0 }, leaving room for the length-framedv1format proposed in NUT-11: length-frame the SIG_ALL message aggregation nuts#404.computeDigestsnow produce the v0 format only. Pre-v0 was only ever accepted by Nutshell <= 0.20.2 and CDK < 0.14.0.buildP2PKSigAllMessagerenamedbuildP2PKSigAllMessageV0(internal, not in the public rollup).Reviewer Notes
SigAllDigestsshape,deserializePackagesignature, no digests in emitted packages. Wallets on old mints that only accept the pre-v0 SIG_ALL format will no longer be able to spend SIG_ALL-locked proofs there.