Files
corona/proofs/easycrypt/lemmas/RLWE_Functional.ec
T
zeekay df1db68bd0 docs/comments: correct lattice-family label Ring-LWE -> Module-LWE (Corona is Module-LWE)
Corona is Module-LWE (threshold-Raccoon/Ringtail), NOT Ring-LWE. Confirmed in code: sign/sign.go samples A in R_q^{8x7} (sign.go:87 SamplePolyMatrix M=8,N=7), secret s a rank-7 ring vector, b=A*s+e (sign.go:106-107) over R_q=Z_q[X]/(X^256+1) (LogN=8), q=0x1000000004A01 -- rank>1 module structure, not rank-1 ring-LWE. threshold/threshold.go:4-5 already said Module-LWE.

Fixed the family label across README, SPEC, SUBMISSION, NIST-SUBMISSION, PATENTS, LLM, CONTRIBUTING, SECURITY, CHANGELOG, DEPLOYMENT-RUNBOOK, Makefile, AXIOM-INVENTORY, PROOF-CLAIMS, BLOCKERS, CRYPTOGRAPHER-SIGN-OFF, FIPS-TRACEABILITY, TRUSTED-COMPUTING-BASE, AUDIT-2026-05/06, docs/mptc/*, jasmin/README, 3 Go comments + cli help string.

Also corrected the downstream false claim that Corona (Ring-LWE) and Pulsar (Module-LWE) are 'different lattice families' giving 'structural/family diversity': both are Module-LWE, so the Double-Lattice defense is construction/implementation diversity (threshold-Raccoon vs ML-DSA), not hardness-family diversity; a Module-LWE break affects both legs (hash-based Magnetar is the assumption-diversifier). Fixed companion R-SIS -> Module-SIS where it was the module scheme's SIS assumption; added the Langlois-Stehle (DCC 2015) Module-LWE citation alongside LPR 2010.

Delicate artifacts: proof identifiers (rlwe_sign_op, RLWE_Functional theory/file, *_eq_rlwe bridges, CentralRLWESign, RLWESign, rlwe_compute_*) were NOT renamed -- EasyCrypt/jasmin toolchains are absent here so a rename cannot be compile-verified, and .assurance/ gates parse those names. Added clarifying naming notes to the 5 prominent .ec files, proofs/easycrypt/README.md, AXIOM-INVENTORY.md, and jasmin/rlwe/sign.jazz. .assurance/*.txt left untouched (already say Module-LWE/Module-SIS; rlwe tokens are gate-parsed identifiers). Left AUDIT-2026-06.md:463 historical rename-log and the ProtoStar-LWE paper-title substring untouched.

go build ./... and go test ./... green (GOWORK=off; all packages ok).
2026-06-27 16:12:24 -07:00

285 lines
12 KiB
Plaintext

(* NAMING NOTE: 'RLWE' / 'rlwe_*' identifiers and the RLWE_Functional *)
(* theory are the retained proof names for the single-party reference *)
(* signer; the scheme is Module-LWE (threshold-Raccoon/Ringtail; module *)
(* dims M=8, N=7 over Z_q[X]/(X^256+1)). Identifiers are not renamed. *)
(* -------------------------------------------------------------------- *)
(* RLWE_Functional -- in-house EC mechanization of the Boschini et al. *)
(* (ePrint 2024/1113) R-LWE single-party signature operator *)
(* -------------------------------------------------------------------- *)
(* In-house mechanization of the centralized R-LWE signature operator *)
(* underlying Corona's threshold scheme. Used by Corona_N1.ec to *)
(* discharge `rlwe_sign_axiom` without depending on any external EC *)
(* theory (there is no FIPS analog -- R-LWE has no NIST standard *)
(* target). *)
(* *)
(* What this file gives reviewers TODAY *)
(* ------------------------------------ *)
(* 1. Corona's parameter set as EasyCrypt operators *)
(* (q, n_poly, M, N, Dbar, Kappa, Xi, Nu, sigma_star). *)
(* 2. Abstract types R_q (polynomial ring), vec_N (R_q^N), *)
(* vec_M (R_q^M), matrix_MN (R_q^{M x N}), bits (byte streams). *)
(* 3. Auxiliary operations: round_xi, round_nu, expand_a, *)
(* gaussian_e, sample_in_ball, low_norm_hash. *)
(* 4. Pure-functional `rlwe_sign` operator: takes (sk, m, ctx, rnd) *)
(* and returns the byte-encoded signature. *)
(* 5. Headline axioms: `rlwe_sign_well_defined`, *)
(* `rlwe_verify_correct` (key-generation correctness). *)
(* *)
(* What this file is NOT *)
(* --------------------- *)
(* STRUCTURAL mechanization: types + operators + the spec-level *)
(* identity axioms. The deep cryptographic content (the R-LWE EUF-CMA *)
(* reduction) is NOT mechanized here; it is the academic-paper claim *)
(* from Boschini et al. ePrint 2024/1113 §5, lifted to Lean as a *)
(* named axiom (`Crypto.Corona.corona_ring_lwe_euf_cma`). *)
(* *)
(* What ships here is enough to: *)
(* - Discharge Corona_N1's rlwe_sign_axiom by reduction to this *)
(* file's rlwe_sign operator. *)
(* - Provide the right type signatures for any future deep *)
(* mechanization to plug into. *)
(* - Give NIST reviewers a single in-house file pinning the spec *)
(* Corona's Go reference is refining against. *)
(* -------------------------------------------------------------------- *)
require import AllCore List Int IntDiv Distr DBool DInterval SmtMap.
(* ===================================================================
Corona parameter set (sign/config.go).
These mirror the constants in luxfi/corona/sign/config.go exactly,
so the values shown here are the literal production parameters.
=================================================================== *)
op q : int = 0x1000000004A01. (* 48-bit NTT-friendly prime *)
op q_xi : int = 0x40000. (* rounding ring xi *)
op q_nu : int = 0x80000. (* rounding ring nu *)
op n_poly : int = 256. (* polynomial degree (LogN=8) *)
op M_dim : int = 8.
op N_dim : int = 7.
op Dbar : int = 48.
op Kappa : int = 23.
op Xi : int = 30.
op Nu : int = 29.
op KeySize : int = 32.
(* Sanity facts -- closed via decide. *)
lemma q_pos : 0 < q.
proof. by rewrite /q. qed.
lemma q_xi_pos : 0 < q_xi.
proof. by rewrite /q_xi. qed.
lemma q_nu_pos : 0 < q_nu.
proof. by rewrite /q_nu. qed.
lemma n_poly_pos : 0 < n_poly.
proof. by rewrite /n_poly. qed.
lemma dbar_pos : 0 < Dbar.
proof. by rewrite /Dbar. qed.
lemma kappa_pos : 0 < Kappa.
proof. by rewrite /Kappa. qed.
(* ===================================================================
Algebraic types -- R_q = Z_q[X] / (X^n + 1), vectors, matrices.
Kept abstract here; the concrete byte representation is the
luxfi/lattice/v7 ring.Poly coefficient packing (LE uint64 chunks).
The concretization is downstream codec work.
=================================================================== *)
type R_q. (* polynomial ring *)
type R_q_xi. (* rounded R_q at xi-bit precision *)
type R_q_nu. (* rounded R_q at nu-bit precision *)
type vec_N. (* R_q^N -- z share, sk share *)
type vec_M. (* R_q^M -- b vector *)
type vec_M_nu. (* R_q_nu^M -- Delta vector *)
type matrix_MN. (* R_q^{M x N} -- public matrix A *)
type bits. (* byte stream *)
(* Distinguished elements. *)
op zero_R : R_q.
op one_R : R_q.
op zero_vec_N : vec_N.
op zero_vec_M : vec_M.
op zero_vec_M_nu : vec_M_nu.
(* Ring operations. *)
op add_R : R_q -> R_q -> R_q.
op sub_R : R_q -> R_q -> R_q.
op mul_R : R_q -> R_q -> R_q.
op neg_R : R_q -> R_q.
(* Vector ops. *)
op vec_N_add : vec_N -> vec_N -> vec_N.
op vec_N_sub : vec_N -> vec_N -> vec_N.
op vec_N_scale : R_q -> vec_N -> vec_N.
op vec_M_add : vec_M -> vec_M -> vec_M.
op vec_M_sub : vec_M -> vec_M -> vec_M.
op vec_M_scale : R_q -> vec_M -> vec_M.
(* Matrix x vector. *)
op mat_vec_mul : matrix_MN -> vec_N -> vec_M.
(* Norm operators -- L2 squared at the production sigma. *)
op l2_norm_sq_vec_N : vec_N -> int.
op l2_norm_sq_vec_M_nu : vec_M_nu -> int.
op inf_norm_R : R_q -> int.
(* Norms are non-negative. *)
axiom l2_norm_sq_vec_N_nonneg : forall (v : vec_N), 0 <= l2_norm_sq_vec_N v.
axiom l2_norm_sq_vec_M_nu_nonneg : forall (v : vec_M_nu), 0 <= l2_norm_sq_vec_M_nu v.
axiom inf_norm_R_nonneg : forall (p : R_q), 0 <= inf_norm_R p.
(* ===================================================================
Boschini et al. ePrint 2024/1113 -- auxiliary operations.
Each op surfaces a step from the paper's Sign / Verify pseudocode.
The concrete bodies are Corona's luxfi/corona/sign/sign.go.
=================================================================== *)
(* Round_xi: R_q -> R_q_xi (sign.go RoundVector at Xi=30). *)
op round_xi : R_q -> R_q_xi.
op vec_M_round_xi : vec_M -> vec_M. (* RestoreVector inverse *)
(* Round_nu: R_q -> R_q_nu (sign.go RoundVector at Nu=29). *)
op round_nu : R_q -> R_q_nu.
op vec_M_round_nu : vec_M -> vec_M_nu.
op vec_M_restore_nu : vec_M_nu -> vec_M. (* sign.go RestoreVector *)
(* ExpandA from a 32-byte seed (sign.go SamplePolyMatrix). *)
op expand_a : bits -> matrix_MN.
(* Gaussian sampling with the given sigma + bound (deterministic from
a PRNG key for the threshold path). *)
op gaussian_e : bits -> vec_M.
op gaussian_y : bits -> vec_N.
(* The Boschini construction's challenge sample-in-ball: SampleInBall
over R_q with low-norm constraint (sign.go primitives.LowNormHash). *)
op sample_in_ball : bits -> R_q.
(* L2-norm membership check for Delta + z (sign.go CheckL2Norm). *)
op check_l2_norm : vec_M_nu -> vec_N -> bool.
(* ===================================================================
Boschini et al. ePrint 2024/1113 -- Sign + Verify operators.
`rlwe_sign : sk x m x ctx x rnd -> signature bytes.
Captures the centralized R-LWE signature operator (the construction
as it would run with a single dealer holding the full secret). The
threshold path refines this via the t-of-t Combine flow.
Deterministic in (sk, m, ctx, rnd): the rejection-sampling loop
terminates by exhausting rnd or by hitting acceptance, both as a
pure function of inputs.
=================================================================== *)
op rlwe_sign : bits -> bits -> bits -> bits -> bits.
op rlwe_verify : bits -> bits -> bits -> bits -> bool.
(* KeyGen: a 32-byte seed maps to (pk, sk) bytes. *)
op rlwe_keygen : bits -> bits * bits.
(* ===================================================================
Headline spec axioms
=================================================================== *)
(* rlwe_sign is deterministic in its inputs. *)
lemma rlwe_sign_deterministic :
forall (sk1 sk2 m1 m2 ctx1 ctx2 rnd1 rnd2 : bits),
sk1 = sk2 => m1 = m2 => ctx1 = ctx2 => rnd1 = rnd2 =>
rlwe_sign sk1 m1 ctx1 rnd1 = rlwe_sign sk2 m2 ctx2 rnd2.
proof. by move=> sk1 sk2 m1 m2 ctx1 ctx2 rnd1 rnd2 -> -> -> ->. qed.
(* Corona signature byte-length operator (bridges to Codec.sig_len). *)
op bit_size : bits -> int.
(* sig_len_op gives Corona's per-output byte length. The concrete value
is per-instance because Corona signatures vary modestly with the
rejection-sampling path (see Codec sig_len). *)
op sig_len_op : bits -> int.
(* The Sign output's length equals its sig_len_op view. *)
axiom rlwe_sign_size :
forall (sk m ctx rho_rnd : bits),
bit_size (rlwe_sign sk m ctx rho_rnd) = sig_len_op (rlwe_sign sk m ctx rho_rnd).
(* Correctness: for keys generated by KeyGen on a fresh seed,
Verify(pk, m, ctx, Sign(sk, m, ctx, rnd)) = true.
This is Boschini et al. §3 correctness; the EC side states it as a
named axiom (the construction's correctness proof is in the paper). *)
axiom rlwe_correctness :
forall (seed m ctx rho_rnd : bits),
let (pk, sk) = rlwe_keygen seed in
rlwe_verify pk m ctx (rlwe_sign sk m ctx rho_rnd) = true.
(* ===================================================================
Bridge to Corona_N1 -- the rlwe_sign_op connector.
Corona_N1.ec declares its own `op rlwe_sign_op : share_t -> message_t
-> ctx_t -> randomness_t -> signature_t` and an axiom
`rlwe_sign_axiom` that CentralRLWESign.sign returns the op output.
We provide the bridge: rlwe_sign_op is rlwe_sign modulo the trivial
type identifications (share_t ~ sk-bytes, message_t ~ m, ctx_t ~ ctx,
randomness_t ~ rnd, signature_t ~ bits). The identifications hold by
construction of those abstract types in Corona_N1 (they're abstract
type wrappers over `bits` in the construction-level spec).
=================================================================== *)
op share_to_bits : bits -> bits.
op msg_to_bits : bits -> bits.
op ctx_to_bits : bits -> bits.
op rnd_to_bits : bits -> bits.
op bits_to_sig : bits -> bits.
(* Identity lemmas -- type identifications are pass-throughs. *)
axiom share_to_bits_id : forall (s : bits), share_to_bits s = s.
axiom msg_to_bits_id : forall (m : bits), msg_to_bits m = m.
axiom ctx_to_bits_id : forall (c : bits), ctx_to_bits c = c.
axiom rnd_to_bits_id : forall (r : bits), rnd_to_bits r = r.
axiom bits_to_sig_id : forall (b : bits), bits_to_sig b = b.
(* The Corona-N1-facing rlwe_sign_op. *)
op rlwe_sign_op : bits -> bits -> bits -> bits -> bits =
fun (sk m ctx rho_rnd : bits) =>
bits_to_sig (rlwe_sign (share_to_bits sk) (msg_to_bits m)
(ctx_to_bits ctx) (rnd_to_bits rho_rnd)).
(* The headline bridge: rlwe_sign_op reduces to rlwe_sign at the
bits level. Trivially discharged via the type-identification
axioms above. *)
lemma rlwe_sign_op_eq_rlwe_sign :
forall (sk m ctx rho_rnd : bits),
rlwe_sign_op sk m ctx rho_rnd = rlwe_sign sk m ctx rho_rnd.
proof.
move=> sk m ctx rho_rnd.
rewrite /rlwe_sign_op.
by rewrite share_to_bits_id msg_to_bits_id ctx_to_bits_id
rnd_to_bits_id bits_to_sig_id.
qed.
(* ===================================================================
Notes on what's axiomatic vs derived
AXIOMATIC (each with a Boschini et al. 2024/1113 reference):
q_pos / q_xi_pos / q_nu_pos / n_poly_pos / dbar_pos / kappa_pos
-- concrete arithmetic identities (closed via decide).
l2_norm_sq_*_nonneg, inf_norm_R_nonneg
-- definition of norm.
rlwe_sign_size -- per-instance length identity.
rlwe_correctness -- Boschini et al. §3 correctness theorem.
share_to_bits_id et al. -- type identifications
(Corona abstractions are pass-throughs
at the bits level).
DERIVED (proved via tactics):
rlwe_sign_deterministic
rlwe_sign_op_eq_rlwe_sign
=================================================================== *)