Skip to content

Audit metadata is not cryptographically bound to the token request anchor #2110

Description

@adecaro

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-action ActionID positional 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

  1. added this to the Q3/26 milestone on Aug 3, 2026
  2. self-assigned this
    on Aug 3, 2026
  3. added theissue type on Aug 3, 2026
  4. removed their assignment
    on Aug 3, 2026
  5. KElkhiyaoui commented on Aug 4, 2026

    @KElkhiyaoui
    Contributor

    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.

  6. SurbhiAgarwal1 commented on Aug 6, 2026

    @SurbhiAgarwal1
    Contributor

    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 Anchor can be done on the creator/sender side when the transaction is assembled online:

    1. Include a cryptographic hash of TokenRequestMetadata inside MarshalToAudit() (or MarshalToSign()).
    2. This way, the transaction creator (who is already online and knows the fresh Anchor) commits the specific TokenRequestMetadata to that Anchor under the signed message body.
    3. During audit validation, the auditor verifies that the submitted metadata matches H(Metadata) in the signed request body for that Anchor, 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?

  7. AkramBitar commented on Sep 22, 2026

    @AkramBitar
    Contributor

    @SurbhiAgarwal1
    Assigned. Please let me know if you have time to work on it.
    Thanks a lot,
    Akram

  8. SurbhiAgarwal1 commented on Sep 22, 2026

    @SurbhiAgarwal1
    Contributor

    Hi @AkramBitar , I will raise pr soon on this. Thanks

  9. modified the milestones: Q3/26, Q4/26 on Oct 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions