Repository navigation
Audit metadata is not cryptographically bound to the token request anchor #2110
Description
Summary
During auditor approval, TokenRequestMetadata is checked for structural/cryptographic consistency with the accompanying TokenRequest.Actions, but it is never cryptographically bound to the TokenRequestAnchor (the transaction identifier). Only Actions are covered by the anchor-bound signing scheme; Metadata is not.
Details
The audit-approval pipeline validates that metadata matches the actions it is submitted alongside:
common.Auditor.Check→ValidateStructure(token/core/common/auditor.go) checks action-count parity, per-actionActionIDpositional alignment, and action-type/metadata-type alignment.IssueMetadata.Match(action)/TransferMetadata.Match(action)(token/driver/request.go) check structural correspondence (input/output counts, extra signers, issuer identity) between metadata and the deserialized action.- In the zkatdlog driver,
InspectOutput/InspectIdentity(token/core/zkatdlog/nogh/v1/audit/auditor.go) recompute Pedersen commitments from the cleartext value/type/blinding-factor in the metadata and check them against the action's on-the-wire commitments, and match audit info against identities.
None of these checks take the transaction Anchor into account. The Anchor/txID value is threaded through the entire AuditContext/Context call chain, but only ever appears inside log statements and error-message strings — never in a comparison.
Separately, the only part of a token request that is ever cryptographically signed — by participants (MarshalToSign) or by the auditor (MarshalToAudit) — is Actions bound to Anchor via TokenRequest.MarshalToMessageToSign (token/driver/request.go). Its own doc comment states: "The anchor MUST be unique per transaction to prevent signature reuse." TokenRequestMetadata has no signature, hash, or MAC field at all, and is explicitly excluded from every signing path:
Request.MarshalToAudit()(token/request.go): "serializes the request for auditor signatures (excludes metadata)"Request.MarshalToSign()(token/request.go): "serializes the request for participant signatures" — same exclusion.
docs/driverapi.md states that the Auditor Service allows auditors "to inspect token requests and their associated metadata against a provided anchor to ensure compliance" — this is not what the implementation does today.
Impact
Because metadata is validated only for structural consistency with a set of actions — and never for having been produced specifically for the anchor it's submitted with — metadata that was legitimately generated for one transaction can be resubmitted paired with a different, structurally-compatible action set under a different anchor, and every metadata check (ValidateStructure, Match, InspectOutput, InspectIdentity) will pass. The auditor's own signature, which is anchor-bound, is computed strictly after this (already-succeeded) metadata check and never covers metadata, so it provides no retroactive protection against this scenario. The legitimate parties for the original transaction have no way to detect that their metadata was reused elsewhere.
Suggested direction
Bind TokenRequestMetadata to the Anchor before/while it is validated during audit — e.g., include the anchor in the metadata's structural validation, or require metadata to carry (or be accompanied by) something that ties it to the specific anchor it was generated for, so metadata generated for transaction A cannot be replayed as valid metadata for transaction B.
Activity
KElkhiyaoui commentedon Aug 4, 2026
If we bind the audit metadata to the token request anchor, then the recipient will be required to always be online to receive tokens. This change will amount to a signature by the recipient on the token request anchor. Currently, there is the possibility to have recipient audit information computed offline and stored at a third party (intermediary, for example). We should consider the tradeoffs.
SurbhiAgarwal1 commentedon Aug 6, 2026
Hi @adecaro @KElkhiyaoui,
To address @KElkhiyaoui's point on keeping recipient audit info offline-friendly: we don't need the recipient to sign the anchor dynamically. The recipient's audit info/metadata can still be pre-generated offline and provided to the transaction creator or intermediary.
Instead, the binding to
Anchorcan be done on the creator/sender side when the transaction is assembled online:TokenRequestMetadatainsideMarshalToAudit()(orMarshalToSign()).Anchor) commits the specificTokenRequestMetadatato thatAnchorunder the signed message body.H(Metadata)in the signed request body for thatAnchor, preventing metadata replay across different anchors without requiring any recipient online interaction.I'd work on this issue and implement this binding. Could you please assign this issue to me?
AkramBitar commentedon Sep 22, 2026
@SurbhiAgarwal1
Assigned. Please let me know if you have time to work on it.
Thanks a lot,
Akram
SurbhiAgarwal1 commentedon Sep 22, 2026
Hi @AkramBitar , I will raise pr soon on this. Thanks