The Pedersen commitment scheme #
A transliteration of EasyCrypt's examples/Pedersen.ec:
- EC's
clone DLog(cyclic groupgroupwith generatorg, prime exponent fieldexp) becomes the classPedersenGroup: the operations the programs mention, plus the laws the proofs turned out to need (f_commring,gpow_add,gpow_mul— the last two are EC'sexpD/expM). - EC's
PedersenTypes+ theCommitmentclone become theCommitmentTypesinstance (value/commitment = group, message/openingkey = exponent). module Pedersen : CommitmentSchemebecomes amodule Pedersen : CommitmentScheme { … }declaration — EC's own syntax, near enough, now that themodulecommand exists.- Correctness is stated as EC states it (
hoare[Correctness(Pedersen).main : true ==> res]): the output distribution puts no mass onres = false. Proven (pedersen_correctness), on standard axioms only, entirely on themodulecommand's generated lemmas — this file declares nothing of its own for it beyond the per-procedurewp_*lemmas.
The group setup (EC's DLog clone) #
A cyclic group G with generator g and exponent type F (EC's group/exp).
Multiplication and exponentiation, what the programs genuinely need of the types — a default
element (Inhabited, so local variables can be declared) and Fintype F (real content:
SubProbability.uniform samples it, and the wp lemmas sum over it) — and the algebraic laws
the proofs need, which are the three fields documented individually below. Correctness needs
none of the laws; they are all for Hiding.lean.
Decidable equality is not a field. verify does compare ($c == $c'), and == is BEq
derived from DecidableEq — but these programs are never executed: every proc is
noncomputable and the semantics is a measure, so the comparison only ever has to denote a
Bool, not compute one. Classical.decEq supplies that for free, which is why the instances
below are classical and the class is two fields shorter. Nothing in the proofs cares: they
reason about =, and beq_self_eq_true and friends hold for any DecidableEq witness.
- G : Type
- F : Type
- g : G
EC's exponent type is the prime field
DL.GP.ZModE; the hiding proof formsd + x * mand its inversed - x * m, so the additive group and the multiplication are both needed. (Only the ring structure is required — nothing here uses inverses.)EC's
expD: exponentiation turns addition into multiplication.EC's
expM: iterated exponentiation multiplies the exponents.
Instances
gpow_add at the ^/* notation. The class fields have to be stated with the raw
gmul/gpow (the instances below them do not exist yet), but every use site sees the
notation, and rw matches on the notation's head — not the raw field.
EC's PedersenTypes + clone Commitment with …: value/commitment are group elements,
message/openingkey are exponents.
Equations
- One or more equations did not get rendered due to their size.
The scheme #
EC's
module Pedersen : CommitmentScheme = {
proc gen() : value = { x <$ dt; h <- g ^ x; return h; }
proc commit(h, m) = { d <$ dt; c <- (g ^ d) * (h ^ m); return (c, d); }
proc verify(h, m, c, d) = { c' <- (g ^ d) * (h ^ m); return (c = c'); }
}.
transcribes directly with the module command. It declares, per procedure f, both the body
Pedersen.f.procedure and the module Pedersen.f : Module.Proc …, and assembles them into
Pedersen : CommitmentScheme with CommitmentScheme.mk (the field names match the moduletype's,
so the record constructor is used rather than a nest of Module.pairs). Pedersen has no
module parameters, so it is the scheme — there is nothing to apply it to, and hence no
Pedersen.apply_simp.
Equations
Instances For
Equations
Instances For
Equations
Instances For
Equations
- One or more equations did not get rendered due to their size.
Instances For
Equations
- One or more equations did not get rendered due to their size.
Instances For
Equations
- One or more equations did not get rendered due to their size.
Instances For
Equations
- One or more equations did not get rendered due to their size.
Instances For
Correctness #
To run an applied functor module we extract its procedure: a normal closed module
expression of procedure type is a .proc node (proc_type_is_proc / Module.procedure,
now in Language/Modules.lean).
Correctness Pedersen β/δ-normalizes to Correctness.main with Pedersen's procedures in the
holes. That reduction used to be done by hand here, by a functorApp_procedure bridge lemma, a
functor_procedure tactic, a Pedersen_expression record equation and a pedersenInst naming
the hole filling. None of it is needed any more, and all of it is gone (see the history of this
file if you want it back): the module/moduletype commands emit @[simp] apply_simp lemmas
and tag their accessors @[module_accessor], and those do the whole reduction inline in
pedersen_correctness.
Per-procedure wp lemmas (EC's inline+auto steps, done once per procedure) #
All three are stated at the CommitmentTypes-spelled signature the instantiated game carries,
not at G/F. The two are definitionally equal, but Eq carries its type as an index, so the
spelling is what makes them the same proposition as the goal — see the ⚠ below.
Reducing the applied functor #
Correctness.main.procedure.apply_simp fills Correctness.main's holes with the callees they
were made from — which, since Correctness's body calls S.gen, are the moduletype accessors
CommitmentScheme.gen Pedersen and friends. The wp_* lemmas above are stated at
Pedersen.gen.procedure, a separate definition the module command emits. Adding
module_accessor (the simp set the accessors are tagged with), Pedersen, and the
Module.proc/Module.procedure_proc round-trip to the main simp call is all it takes to close
that gap. So the whole reduction is the commands' own lemmas plus one simp set: no bridge
lemma, no hand-written hole instantiation, nothing declared for the purpose.
(Module.procedure_proc has to be paired with unfolding Module.proc: the library states the
round-trip with Module.proc already unfolded, as (ModuleExpression.proc p).toModule (.proc p),
so on its own it never fires against the folded Module.proc that X.<f>.apply_simp emits.)
⚠ One thing to know before touching this: CommitmentScheme.gen Pedersen and Pedersen.gen
both print as Pedersen.gen (dot-notation collision) and are not defeq — the accessor is a
chain of Module.fst'/Module.snd' through Pedersen's expression. So a lemma or rewrite
aimed at the wrong one of the two fails with the two sides displaying identically, or with "did
not find an occurrence of the pattern" against a goal in which the pattern is apparently right
there. set_option pp.explicit true is what tells them apart. A second, similar trap: signature
spellings must be CommitmentTypes.*, not G/F — those are defeq, but Eq carries its type
as an index, so the two are different propositions and exact rejects the mismatch, again
printing identically (convert … using 2 exposes that one). The wp_* lemmas above are spelled
CommitmentTypes.* for exactly this reason.
Correctness of Pedersen — EC's
hoare[Correctness(Pedersen).main : true ==> res]: from any initial state, the
correctness game never returns false.