HOL-Light: Add HOL Light poly_use_hint proofs for AArch64#1037
Conversation
2053888 to
0b42385
Compare
0b42385 to
c5c76af
Compare
CBMC Results (ML-DSA-87)Full Results (200 proofs)
|
CBMC Results (ML-DSA-65)Full Results (200 proofs)
|
CBMC Results (ML-DSA-44)Full Results (200 proofs)
|
mkannwischer
left a comment
There was a problem hiding this comment.
Thanks @jakemas. Please add the CBMC proofs.
e7ca229 to
e7a3a45
Compare
Thank you, added the harness as a single file with split functionality on GAMMA2 |
1ea7797 to
817aa3d
Compare
hanno-becker
left a comment
There was a problem hiding this comment.
Thank you @jakemas for working on this!
The specification seems far too weak to claim functional correctness? We seem to only say something about the bounds of the output.
40ec688 to
de080de
Compare
c97d731 to
e96df0d
Compare
f2f03df to
2c8a1e6
Compare
Update: FIPS 204-aligned specs for _32 (commit 2c8a1e6)Addressed the review comments for the _32 variant: FIPS 204 alignment (Hanno + Matthias)
Other fixes
Pending
|
67b89be to
a879424
Compare
|
woo got it! testing _88 in CI now then will merge shared code ( DIV_SANDWICH, ENSURES_STRENGTHEN_POST, and INT_MOD_RESIDUE) maybe go eat a div sandwich myself 🥪 |
3c1d5a1 to
999243b
Compare
mkannwischer
left a comment
There was a problem hiding this comment.
Thank you so much @jakemas for getting this to work so quickly! The use_hint and decompose specs now look great to me!
The only blocker left for me is that the SUBROUTINE_CORRECT_FIPS204 specs are missing the output bounds.
Also, can we rename SUBROUTINE_CORRECT_FIPS204 to SUBROUTINE_CORRECT for consistency with the other proofs and remove the old POLY_USE_HINT_32_AARCH64_ASM_SUBROUTINE_CORRECT? That is otherwise quite confusing to read.
999243b to
1e91386
Compare
mkannwischer
left a comment
There was a problem hiding this comment.
Thanks @jakemas for the updates.
The final specs now look great to me!
It would be nice if we could eliminate the CORRECT_CODE spec as there is no other proof in mlkem-native/mldsa-native that has multiple specs. I'd prefer it if we can keep this uniform. This can be done in a follow-up.
Two nits that should still be fixed regarding the CBMC contracts, but I'll take the liberty to fix those myself shortly.
Add formal verification proofs for poly_use_hint_32 and poly_use_hint_88
AArch64 NEON implementations using HOL Light.
Each proof file contains:
- Assembly-level functional correctness
- Subroutine correctness with code-aligned spec
- Constant-time and memory safety
- FIPS 204 equivalence proof via Barrett interval cascade
- FIPS 204-aligned subroutine correctness
FIPS 204 definitions (mldsa_cmod, mldsa_decompose, mldsa_use_hint) and
shared helpers (DIV_SANDWICH, INT_MOD_RESIDUE) are in mldsa_specs.ml.
ENSURES_STRENGTHEN_POST lives in aarch64_utils.ml.
The public SUBROUTINE_CORRECT theorem in each proof uses the FIPS 204
spec mldsa_use_hint_{32,88} in its postcondition and carries the output
bound (< 16 or < 44) as a corollary. Code-aligned intermediates use
*_CODE suffixes to mark them internal.
Signed-off-by: Jake Massimo <jakemas@amazon.com>
|
@jakemas This is the pattern I had in mind: You write the spec as you want it, but start the proof by massaging it into a shape that's better aligned to the code, including a) adding the automatic bounds, b) moving from FIPS204-aligned to code-aligned. Could you internalize this pattern, and open a follow-up PR removing let MLDSA_USE_HINT_88_EQUIV = prove(
`!r h. r < 8380417 /\ h <= 1
==> mldsa_use_hint_88 h r = mldsa_use_hint_88_code r h`,
(* FROM YOUR CODE *) );;
let POLY_USE_HINT_88_AARCH64_ASM_CORRECT = prove
(`!b a h x y pc.
nonoverlapping (word pc, LENGTH poly_use_hint_88_aarch64_asm_mc) (b, 1024) /\
nonoverlapping (b, 1024) (a, 1024) /\
nonoverlapping (b, 1024) (h, 1024)
==> ensures arm
(\s. aligned_bytes_loaded s (word pc) poly_use_hint_88_aarch64_asm_mc /\
read PC s = word pc /\
C_ARGUMENTS [b; a; h] s /\
(!i. i < 256 ==> val(x i) < 8380417) /\
(!i. i < 256 ==> val(y i) <= 1) /\
(!i. i < 256 ==>
read(memory :> bytes32(word_add a (word(4 * i)))) s = x i) /\
(!i. i < 256 ==>
read(memory :> bytes32(word_add h (word(4 * i)))) s = y i))
(\s. read PC s = word(pc + LENGTH poly_use_hint_88_aarch64_asm_mc - 4) /\
(!i. i < 256 ==>
read(memory :> bytes32(word_add b (word(4 * i)))) s =
word(mldsa_use_hint_88 (val(y i)) (val(x i)))) /\
(!i. i < 256 ==>
val(read(memory :> bytes32(word_add b (word(4 * i)))) s) < 44))
(MAYCHANGE_REGS_AND_FLAGS_PERMITTED_BY_ABI ,,
MAYCHANGE [memory :> bytes(b, 1024)])`,
(* First, globalize the variables and assumptions. In particular, this will
allow us to refer to the pure preconditions when rewriting the post condition. *)
MAP_EVERY X_GEN_TAC
[`b:int64`; `a:int64`; `h:int64`;
`x:num->int32`; `y:num->int32`; `pc:num`] THEN
REWRITE_TAC[MAYCHANGE_REGS_AND_FLAGS_PERMITTED_BY_ABI; C_ARGUMENTS;
NONOVERLAPPING_CLAUSES; ALL;
fst POLY_USE_HINT_88_AARCH64_ASM_EXEC] THEN
DISCH_THEN(REPEAT_TCL CONJUNCTS_THEN ASSUME_TAC) THEN
GLOBALIZE_PRECONDITION_TAC THEN
(* First, show that the bounds in the post-condition are automatic once the
functional description is established. *)
MATCH_MP_TAC ENSURES_STRENGTHEN_POST THEN
EXISTS_TAC
`\s. read PC s = word(pc + LENGTH poly_use_hint_88_aarch64_asm_mc - 4) /\
(!i. i < 256 ==>
read(memory :> bytes32(word_add b (word(4 * i)))) s =
word(mldsa_use_hint_88_code (val(x i:int32)) (val(y i:int32))))` THEN
CONJ_TAC THENL
[ (* Defer main proof *)
ALL_TAC;
(* Rewrite to code-aligned functional description *)
REPEAT (IMP_REWRITE_TAC[MLDSA_USE_HINT_88_EQUIV]) THEN
(* Show that bounds are automatic *)
REWRITE_TAC[fst POLY_USE_HINT_88_AARCH64_ASM_EXEC] THEN REPEAT STRIP_TAC THEN ASM_REWRITE_TAC[] THEN
FIRST_X_ASSUM(MP_TAC o SPEC `i:num`) THEN ASM_REWRITE_TAC[] THEN
DISCH_THEN SUBST1_TAC THEN REWRITE_TAC[VAL_WORD; DIMINDEX_32] THEN
CONV_TAC NUM_REDUCE_CONV THEN
MATCH_MP_TAC(ARITH_RULE `x < 44 ==> x MOD 4294967296 < 44`) THEN
REWRITE_TAC[mldsa_use_hint_88_code] THEN
CONV_TAC(TOP_DEPTH_CONV let_CONV) THEN
REPEAT(COND_CASES_TAC THEN ASM_REWRITE_TAC[]) THEN ASM_ARITH_TAC] THEN
(* Now do the actual proof -- literally as before *)
CONV_TAC(RATOR_CONV(LAND_CONV(ONCE_DEPTH_CONV EXPAND_CASES_CONV))) THEN
CONV_TAC NUM_REDUCE_CONV THEN
REPEAT STRIP_TAC THEN
REWRITE_TAC[SOME_FLAGS; MODIFIABLE_SIMD_REGS] THEN
ENSURES_INIT_TAC "s0" THEN
MEMORY_128_FROM_32_TAC "a" 0 64 THEN
ASM_REWRITE_TAC[WORD_ADD_0] THEN CONV_TAC WORD_REDUCE_CONV THEN
STRIP_TAC THEN
MEMORY_128_FROM_32_TAC "h" 0 64 THEN
ASM_REWRITE_TAC[WORD_ADD_0] THEN CONV_TAC WORD_REDUCE_CONV THEN
STRIP_TAC THEN
DISCARD_MATCHING_ASSUMPTIONS [`read (memory :> bytes32 a) s = x`] THEN
MAP_EVERY (fun n -> ARM_STEPS_TAC POLY_USE_HINT_88_AARCH64_ASM_EXEC [n] THEN
SIMD_SIMPLIFY_TAC[])
(1--1006) THEN
ENSURES_FINAL_STATE_TAC THEN ASM_REWRITE_TAC[] THEN
REPEAT(FIRST_X_ASSUM(STRIP_ASSUME_TAC o
CONV_RULE (SIMD_SIMPLIFY_CONV []) o
CONV_RULE(READ_MEMORY_SPLIT_CONV 2) o
check (can (term_match [] `read qqq s:int128 = xxx`) o concl))) THEN
CONV_TAC(TOP_DEPTH_CONV EXPAND_CASES_CONV) THEN
CONV_TAC(DEPTH_CONV NUM_MULT_CONV THENC DEPTH_CONV NUM_ADD_CONV) THEN
REWRITE_TAC[WORD_ADD_0] THEN
ASM_REWRITE_TAC[WORD_ADD_0] THEN ASM_REWRITE_TAC[] THEN
(* Push word_subword through SIMD ops to per-element form *)
REWRITE_TAC[WORD_SUBWORD_AND; WORD_SUBWORD_OR] THEN
let WSN_TAC = REWRITE_TAC(map (fun n -> prove(
subst [mk_small_numeral n, `n:num`]
`!x:int128. word_subword(word_not x) (n,32):int32 = word_not(word_subword x (n,32))`,
GEN_TAC THEN MATCH_MP_TAC WORD_SUBWORD_NOT THEN
REWRITE_TAC[DIMINDEX_32; DIMINDEX_128] THEN ARITH_TAC)) [0;32;64;96]) in
WSN_TAC THEN
CONV_TAC(DEPTH_CONV WORD_SIMPLE_SUBWORD_CONV) THEN
CONV_TAC(DEPTH_CONV WORD_NUM_RED_CONV) THEN
let EC_DEEP = CONV_RULE(DEPTH_CONV WORD_NUM_RED_CONV)
(CONV_RULE(DEPTH_CONV(INT_RED_CONV ORELSEC NUM_RED_CONV))
(CONV_RULE(TOP_DEPTH_CONV let_CONV)
(REWRITE_RULE[mldsa_use_hint_88_asm; word_2smulh; word_ishr_round;
DIMINDEX_32] ELEMENT_CORRECT_WORD_88))) in
let EC_OR = ONCE_REWRITE_RULE[WORD_OR_SYM] EC_DEEP in
REPEAT CONJ_TAC THEN
(MATCH_MP_TAC EC_OR ORELSE MATCH_MP_TAC EC_DEEP) THEN
CONJ_TAC THEN FIRST_X_ASSUM MATCH_MP_TAC THEN ARITH_TAC);; |
ok, will do! |
hanno-becker
left a comment
There was a problem hiding this comment.
Many thanks @jakemas !
|
I opened #1085 for @hanno-becker's suggestion so we don't forget. I will merge this PR shortly. |
Resolves:
poly_use_hint#486Summary
Proof times are decent
~15min for _32and~22min for _88.mldsa_poly_use_hint_32: GAMMA2=(Q-1)/32, for l=5,7 (s2n-bignum PR: ML-DSA Aarch64 HOL-Light proof poly_use_hint_32 awslabs/s2n-bignum#372)mldsa_poly_use_hint_88: GAMMA2=(Q-1)/88, for l=4 (s2n-bignum PR: ML-DSA Aarch64 HOL-Light proof poly_use_hint_88 awslabs/s2n-bignum#375)