Skip to content

Upstream: challenge_digest binding (4.1.3) is implemented by no one #8

Description

@OR13

draft-jms-mole-protocols-00 §4.1.3 requires that "Present MUST bind challenge_digest into the proof transcript" and that "Verify MUST fail when given any other challenge_digest". The crypto draft's verification procedure does not mention challenge_digest anywhere.

Structural constraint: the endorsement proof $(e, a, b, r)$ is fixed at issuance. It cannot bind a value that is only known at presentation. So the binding has to happen somewhere else.

What this crate chose: challenge_digest is committed in the OR-proof transcript, which is the only part of a presentation computed at presentation time. Verify then fails on a different digest because the OR proof does not verify — asserted by tests/exp_ihat_profile.rs::verify_fails_against_a_different_challenge_digest.

I believe this is forced rather than chosen — there is nowhere else it can go — but the draft does not say it, so it is recorded as a guess.

Draft text: https://github.com/Moderation-of-unLinkable-Endorsements/internet-drafts/blob/main/draft-authors-mole-crypto.md


Update — upstream PR #39 (open, unmerged): confirmed missing, not merely unstated

PR #39 now defines the full presentation structure and every challenge in the scheme. challenge_digest appears in none of them:

  • EndorsementPresentation has fields Xhat, Zhat, m, e, a, b, r, proof — no challenge_digest.
  • The partial-endorsement challenge is e = H(Y, Zhat, T1, T2, C) — absent.
  • The OR-proof challenge is challenge = H(params, Xi, C) — absent.
  • The verification procedure ("Putting it together") checks the partial endorsement, that Xhat is not the identity, and the OR proof. It never mentions challenge_digest.

Meanwhile draft-jms-mole-protocols-00 §4.1.3 states two MUSTs:

Present MUST bind challenge_digest into the proof transcript.

Verify MUST fail when given any other challenge_digest.

Both are unsatisfiable against the crypto draft's presentation as specified. Nothing in it depends on challenge_digest, so nothing about it can fail when the digest changes. Without this, an endorsement presentation is replayable across challenges, which is the property §4.1.3 exists to prevent.

This upgrades the issue from "the draft does not say where the digest binds" to "the draft specifies a presentation that cannot bind it."

Our choice is unchanged and now better supported. crypto::exp_ihat folds challenge_digest into the OR-proof transcript, because the partial endorsement (e, a, b, r) is fixed at issuance and the OR proof is the only component computed at presentation time. PR #39's structure confirms that division: everything except proof is issuance-time. So the OR transcript is not merely a place the digest can bind — it is the only one, and upstream needs to put it there.


Update — confirmed by the reference implementation, and now the sharpest finding here

ihat-rs has no challenge_digest anywhere. Not in a structure, not in
a transcript, not in its API:

let pres = issued.show(&accepted, 2, &mut rng);
assert!(pres.verify(&pp, &accepted));

Its own verifier documentation is explicit about the consequence:

this is a pure predicate with no replay or double-spend protection — the same
Presentation verifies every time

So protocols §4.1.3's two MUSTs — Present MUST bind challenge_digest, Verify
MUST fail on any other — are satisfied by no implementation, including the
authors' own
. This is no longer "the draft omits it"; it is "the reference
implementation omits it too", which makes it a live gap rather than an editorial one.

What this crate does about it

Byte-compatibility with ihat-rs and compliance with §4.1.3 are mutually exclusive,
so crypto::exp_ihat refuses to pick silently. ChallengeBinding:

Mode Binds the digest Interoperates Satisfies §4.1.3
IhatRsCompatible ❌ — replayable
MoleBound (default) ✅, into the OR transcript

Both are tested, including the_two_binding_modes_do_not_cross_verify and
ihat_rs_compatible_mode_cannot_bind_the_challenge, which asserts that a compat-mode
presentation is accepted under a completely unrelated digest. That test exists to
make the deficiency visible rather than latent.

The OR transcript is the only place the digest can go: the partial endorsement
(e, a, b, r) is fixed at issuance, and PR #39's structure confirms everything except
proof is issuance-time.

This is the top upstream report. Either §4.1.3 relaxes, or the crypto draft and
ihat-rs gain the field. Until then MoLE's replay protection for endorsement
presentation is specified but unimplemented.

Metadata

Metadata

Assignees

No one assigned

    Labels

    exp-ihatConcerns the experimental IHAT profileguessed-parameterA choice this crate invented because the drafts do not specify it

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions