Skip to content

Exp distr - #25

Merged
arthuraa merged 10 commits into
masterfrom
exp-distr
Sep 24, 2026
Merged

arthuraa merged 10 commits into
masterfrom
exp-distr

Conversation

@arthuraa

Copy link
Copy Markdown
Owner

No description provided.

arthuraa and others added 10 commits September 24, 2026 14:44
…es, examples.

Stages 4-6 of enabling (a*b)^x = a^x * b^x: core/public.v, the HeapLang
primitives, lib/dh.v and the case studies.

Lemmas whose base must now be an atom gained negb (is_mul _) / negb (is_inv _)
premises; public_TExp, TExpA, TExpK, TExp_TMulN, TExp_TInv, minted_base_exps
and twp_texp stay unconditional.

New factor-level API, for protocols that exponentiate an attacker-supplied
value and so cannot assume an atomic base:

  algebra.v   is_inv_TExp, TExp_injl, invs_canceled_TExp, factors_TExp
  subterms.v  subterm_factors, subterm_TExp_factors
  public.v    public_TExp_factors

The OPAQUE server exercises all of them.  It also needs a group-membership
check it never had: 1 ^ x = 1 drops the server's ephemeral secret out of the
session key, so Server.session now rejects an identity X_u -- the check its
own TODO was already asking for.

CLAUDE.md records the endomorphism law, the atomic-base wf invariant, and both
downstream consequences.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E8khwa8zca1fMdMbwHLikG
PTMul is replaced by PTN of term_opN & list pre_term, mirroring PT0/PT1/PT2.
term_opN currently has the single constructor ONMul, with the same derived
eqType/choiceType/countType/orderType instances as the other operation types,
and PTMul ts becomes a Notation for PTN ONMul ts (alongside PTInv/PTExp).

Keeping PTMul as a notation means product-specific matches read as before and,
once term_opN gains a constructor, become non-exhaustiveness errors rather
than silently taking the product branch.

The HeapLang encoding follows the arities too: outer TOpN_tag = 3, inner
TMul_tag = 0, PTN o ts represented as (#TOpN_tag, (repr o, repr_list ...)).
eq_term, leq_term, hl_is_mul, hl_one, hl_factors and hl_mul_aux are updated
accordingly, with eq_term_opN / leq_term_opN and their specs.

Order plumbing: leqE's PTN case compares the operation first, as PT1/PT2 do;
with_stdpp.v gains int_of_term_opN / opN_le / opN_leE, and pt_order_mul
becomes pt_order_N, quantified over the operation.

Case patterns in the n-ary branch now destruct the operation -- [] rather than
a binder, so is_mul and friends keep reducing as before, and a second n-ary
operation fails loudly.  Structural functions that ignore the operation
(height, tsize, nonces_of_pre_term) match on PTN _ ts; product-specific ones
(is_mul, factors, wf, normalize) stay on PTMul.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E8khwa8zca1fMdMbwHLikG
`TMulN`/`TInv` served both as the Diffie-Hellman group operation (in the
base of `TExp`) and as the exponent operation.  Those two roles cannot
share a carrier: with one structure, `a^1 = a`, `(a*b)^x = a^x*b^x` and
`(a^x)^y = a^(x*y)` together say that `x |-> (_^x)` is a monoid
homomorphism `(G,*) -> End(G) = (Z_n,x)`, i.e. a group homomorphism
`psi : G -> Z_n*`.  Its image has order dividing `gcd(n, phi(n))`, and
`psi` is trivial on any large prime-order subgroup -- so in any group
where DLP is hard, the axioms force `a^x = a` and exponentiation
degenerates to the identity.

So give the two roles separate operations:

  - `GMul`/`GInv` (`O1GInv`, `ONGMul`; `TGMulN`, `TGMul`, `TGInv`) --
    the DH group, a symbolic model of a prime-order elliptic-curve
    group.  Validates `(a*b)^x = a^x*b^x`, `1^x = 1`,
    `(a^-1)^x = (a^x)^-1`, and the abelian-group laws.
  - `Mul`/`Inv` -- keep their names and the exponent laws:
    `(g^a)^b = g^(a*b)`, `g^1 = g`, `(g^a)^(a^-1) = g`.

`TExp : G -> E -> G` now takes a group element and a scalar, so the
degeneracy argument no longer applies.  A scalar product or scalar
inverse in a base is an atom: `(a*b)^x` does not distribute.

The group stack is a parallel copy of the exponent stack at the other
operation pair, reusing `SMS` at `(pt_order, ginv_aux)`.  `exp` is
retargeted rather than rewritten: it distributes over `gfactors` and
pushes through `PTGInv`, while still merging exponents with the scalar
`mul`.  `is_non_free` splits in two: the five heads `TNonFree`
represents, and the three `wf` forbids in an exponential base
(`is_gnon_free`).  `is_mul_base`/`is_inv_base` become false and are
replaced by `is_gmul_base`/`is_ginv_base`, since `wf (PTExp (PTMul ts) e)`
is now legal.

`public` treats the group product and inverse exactly as it treats the
exponent ones: `decompose` gains `DGInv`, `public_pre_aux` gains an
`is_gmul` disjunct, and `public_TGInv`/`public_TGMulN`/`public_gfactors`
mirror their scalar counterparts.  `primitives/attacker.v` exposes
`add_gmul`, `add_ginv` and `add_gmul_unit`, so the symbolic attacker can
build group products and inverses.

In OPAQUE, the identity the server rejects is now the *group* identity.

Four behavioural checks record what the split buys: `TExp_TGMulN`,
`TExp_TGInv`, `TExp_gunit`, and -- the negative one -- `tsize_TExp_TMulN`,
which says a scalar product in a base is a genuine atom.

All ten `*_secure` / `game_secure` theorems still report "Closed under
the global context".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E8khwa8zca1fMdMbwHLikG
`examples/opaque/impl.v` carried a `TODO: use the key exchange formula
from the OPAQUE paper`; `KE` hashed two independent DH values.  OPAQUE
(Jarecki, Krawczyk and Xu, Eurocrypt 2018) instantiates its AKE with
HMQV, whose key is a single group element mixing static and ephemeral
keys:

  d = H(X_u, IdS)                  e = H(X_s, IdU)
  client:  K = (X_s · P_s^e)^(x_u + d·p_u)
  server:  K = (X_u · P_u^d)^(x_s + e·p_s)

That needs a group product, which the term algebra only gained with the
GMul/GInv split, and an exponent sum, which it does not have.  The sum
is avoidable: exponentiation distributes over the group product, so
`Y^(x + c)` becomes `Y^x · Y^c`.  `KE` now takes the two multipliers as
parameters, so one definition serves both roles -- the client passes
`(d, e)` and the server `(e, d)` -- and `d`/`e` bind the ephemeral to the
peer's static public key, standing in for the paper's identities.

`hmqv_K_sym` records that both roles compute the same group element; the
expansion is the four-factor product

  g^(x_s·x_u) · g^(p_s·e·x_u) · g^(x_s·d·p_u) · g^(p_s·e·d·p_u).

Secrecy.  `public_gfactors` makes a group product public exactly when all
its group factors are, so one secret factor suffices.  Both roles use the
static-static factor: the client has `opaque_public_private_pair p_u P_s`
from the envelope, the server `opaque_public_private_pair p_s P_u` from
its file.  Two new pieces make that work:

  - `gcount_TExp_eq0` (core/term/tsize.v) -- the occurs check.  If an
    exponent of `w` is bigger than all of `X`, no group factor of `X^c`
    is `w` or its inverse.  This is HMQV's own argument: the multiplier
    `m_b` is a hash of the peer's ephemeral `X_b`, hence strictly bigger,
    so `X_b` cannot contain it, and the attacker cannot cancel the
    static-static factor out.
  - `public_dh_secret_gen` (examples/iso_dh/proofs/base.v) -- the
    `exps`-membership form of `public_dh_secret2`.  The static-static
    factor has four exponents, two of them public hash multipliers, so a
    lemma concluding "some exponent is public" would be vacuous;
    `exp_pred_inv_gen` already takes an arbitrary sublist of `exps`, so
    instantiating at `[p_u; p_s]` skips the hashes.

`hmqv_key_gfactors` packages both group-factor memberships and the two
`exps` memberships the roles need.  The server's freshness argument rides
on the second factor, `g^(p_u·d·x_s)`, by the same occurs check.

Both roles still prove exactly today's `SK_result`: `public SK <-> |>[]False`
and `SK` not in the fresh set.  `game.v` is unchanged.

In OPAQUE the identity the server rejects stays the group identity, but
the check is no longer load-bearing -- the static-static factor survives
any `X_u`.

Also adds `all_minted_TMulN` / `all_minted_TGMulN` (with the `nonces`
subset lemmas they need) and moves `mem_factors_TMulN2` from `public.v`
to `algebra.v`, where `tsize.v` can reach it.

All ten `*_secure` / `game_secure` theorems and OPAQUE's `wp_game` still
report "Closed under the global context".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E8khwa8zca1fMdMbwHLikG
@arthuraa
arthuraa merged commit a97491b into master Sep 24, 2026
4 checks passed
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.

1 participant