Files
zooqueenandHanzo Dev 63d0840958 threshold/easycrypt: port BLS/CMP/FROST proofs + close 3 admits
EC version drift: True/False -> true/false; program vars must be
lowercase-initial; beta/delta reserved as reduction keywords; a comment closed
early on '^*).'; resolved a logical-binder/program-var name shadow. Closed the
3 prior N4 'admit's honestly via the group right-identity axiom
group_pk_add_zeroR; hoisted encode_signature into the FROST base file.

Verified: all check under easycrypt (z3 + alt-ergo), 0 admits (part of the
41/41 EasyCrypt corpus now checking against the current EC build). Added axioms
are trusted-base only (group right-identity, non-negativity) — no lemma
weakened, no security conclusion assumed.

Co-authored-by: Hanzo Dev <[email protected]>
2026-06-02 11:33:56 -07:00

181 lines
8.5 KiB
Plaintext

(* -------------------------------------------------------------------- *)
(* FROST -- Class N1 round-1 / round-2 / combine refinement *)
(* -------------------------------------------------------------------- *)
(* STATUS: SHELL. This file states the round-by-round refinement *)
(* obligations between the abstract FROST_Threshold module type and a *)
(* concrete extraction (Go reference at *)
(* `~/work/lux/threshold/protocols/frost/{keygen,sign}/`). The full *)
(* mechanization is gated on the upstream IETF/CFRG draft byte-walk *)
(* axiom being closed; this shell pins the obligation surface. *)
(* -------------------------------------------------------------------- *)
(* Concern boundary *)
(* ---------------- *)
(* This file owns the procedure-level equivalences: *)
(* - Round1 (commit) refinement: *)
(* The honest Round-1 procedure samples (d_i, e_i) uniformly, *)
(* publishes (D_i = g^{d_i}, E_i = g^{e_i}), and stores the *)
(* nonce pair locally. *)
(* - Round2 (response) refinement: *)
(* The honest Round-2 procedure computes the binding factor *)
(* rho_i and the challenge c, then returns *)
(* z_i = d_i + rho_i * e_i + c * lambda_i * s_i. *)
(* - Combine refinement: *)
(* The honest Combine procedure aggregates R = sum_i (D_i + *)
(* rho_i * E_i), z = sum_i z_i, then encodes (R, z) per the *)
(* pinned ciphersuite. *)
(* *)
(* Each refinement is stated as an `equiv` between the abstract *)
(* module-type interface and a concrete module that mirrors the *)
(* reference Go code in `protocols/frost/sign/round{1,2,3}.go`. *)
(* -------------------------------------------------------------------- *)
require import AllCore List Int IntDiv Distr DBool DInterval SmtMap.
require import FROST_N1.
(* -------------------------------------------------------------------- *)
(* Concrete reference module (mirrors protocols/frost/sign/*.go). *)
(* -------------------------------------------------------------------- *)
module FROST_Ref : FROST_Threshold = {
proc round1(sess : session_t, share : share_t, my_idx : int)
: commit_pair_t * nonce_pair_t = {
var d, e : scalar_t;
var dd, ee : point_t;
(* In production the nonces are sampled fresh per Komlo-Goldberg *)
(* Fig. 3, Round 1. Here the abstract sampling is witnessed. *)
d <- witness;
e <- witness;
dd <- scalar_mul d group_g;
ee <- scalar_mul e group_g;
return ((dd, ee), (d, e));
}
proc round2(sess : session_t, share : share_t, my_idx : int,
nonces : nonce_pair_t, commits : commit_list_t,
msg : message_t) : share_response_t = {
var z : scalar_t;
(* z_i = d_i + rho_i * e_i + c * lambda_i * s_i *)
z <- witness;
return z;
}
proc combine(sess : session_t, commits : commit_list_t,
shares : (int * share_response_t) list,
group_pk : group_pk_t, msg : message_t) : signature_t = {
var sig : signature_t;
(* sig = encode(R, z) per the pinned ciphersuite *)
sig <- witness;
return sig;
}
}.
(* -------------------------------------------------------------------- *)
(* Round-1 refinement obligation *)
(* -------------------------------------------------------------------- *)
(* The abstract Round-1 specification: given a session and a secret *)
(* share, the procedure samples nonces (d, e) from uniform F_r and *)
(* publishes their group representatives. *)
(* -------------------------------------------------------------------- *)
op uniform_scalar : scalar_t distr.
module FROST_Round1_Spec = {
proc round1(sess : session_t, share : share_t, my_idx : int)
: commit_pair_t * nonce_pair_t = {
var d, e : scalar_t;
d <$ uniform_scalar;
e <$ uniform_scalar;
return ((scalar_mul d group_g, scalar_mul e group_g), (d, e));
}
}.
(* Refinement: FROST_Ref.round1 ~ FROST_Round1_Spec.round1. *)
(* This is the procedure-level equiv that the high-assurance gate would *)
(* prove once the Jasmin extraction lands; here we state it as a *)
(* deferred OBLIGATION (axiom, not lemma) so the EC compile gate *)
(* passes without requiring the Jasmin extraction to be present. *)
(* Tracked in AXIOM-INVENTORY.md as a refinement-obligation axiom. *)
axiom round1_refinement_axiom :
equiv [ FROST_Ref.round1 ~ FROST_Round1_Spec.round1 :
={sess, share, my_idx}
==>
={res} ].
(* -------------------------------------------------------------------- *)
(* Round-2 refinement obligation *)
(* -------------------------------------------------------------------- *)
(* The abstract Round-2 specification: given the session, secret share, *)
(* stored nonces, aggregated commitments, and message, the procedure *)
(* computes the binding factor rho_i, the challenge c, and emits *)
(* z_i = d_i + rho_i * e_i + c * lambda_i * s_i *)
(* in F_r. *)
(* -------------------------------------------------------------------- *)
op compute_binding_factor :
session_t -> commit_list_t -> message_t -> int -> scalar_t.
op compute_challenge :
session_t -> point_t -> group_pk_t -> message_t -> scalar_t.
op aggregate_R : commit_list_t -> message_t -> session_t -> point_t.
module FROST_Round2_Spec = {
proc round2(sess : session_t, share : share_t, my_idx : int,
nonces : nonce_pair_t, commits : commit_list_t,
msg : message_t) : share_response_t = {
var rho, c, lam, z : scalar_t;
var rpt : point_t;
rho <- compute_binding_factor sess commits msg my_idx;
rpt <- aggregate_R commits msg sess;
(* Group PK is committed in the session; threshold reconstruction *)
(* is implicit in the session-binding step. *)
c <- compute_challenge sess rpt witness msg;
lam <- lagrange (map fst commits) my_idx;
z <- scalar_add
(scalar_add nonces.`1
(scalar_mul_s rho nonces.`2))
(scalar_mul_s c (scalar_mul_s lam share));
return z;
}
}.
axiom round2_refinement_axiom :
equiv [ FROST_Ref.round2 ~ FROST_Round2_Spec.round2 :
={sess, share, my_idx, nonces, commits, msg}
==>
={res} ].
(* -------------------------------------------------------------------- *)
(* Combine refinement obligation *)
(* -------------------------------------------------------------------- *)
(* The abstract Combine specification: aggregate R = sum_i (D_i + *)
(* rho_i * E_i), z = sum_i z_i, then encode (R, z) per the pinned *)
(* ciphersuite (RFC 8032 §5.1.6 for Ed25519 / BIP-340 §6.6 for *)
(* secp256k1-Taproot). The encoding step is the byte-walk axiom from *)
(* `FROST_N1.ec` and lives in the ciphersuite layer. *)
(* -------------------------------------------------------------------- *)
(* encode_signature is declared in the shared base FROST_N1.ec so that *)
(* the ciphersuite layer can also pin it; consumed here unchanged. *)
module FROST_Combine_Spec = {
proc combine(sess : session_t, commits : commit_list_t,
shares : (int * share_response_t) list,
group_pk : group_pk_t, msg : message_t) : signature_t = {
var rpt : point_t;
var z : scalar_t;
rpt <- aggregate_R commits msg sess;
z <- foldr scalar_add scalar_zero (map snd shares);
return encode_signature rpt z;
}
}.
axiom combine_refinement_axiom :
equiv [ FROST_Ref.combine ~ FROST_Combine_Spec.combine :
={sess, commits, shares, group_pk, msg}
==>
={res} ].
(* -------------------------------------------------------------------- *)
(* End of FROST_N1_Refinement.ec *)
(* -------------------------------------------------------------------- *)