Skip to content

Commit abc436b

Browse files
miriampolzerandres-erbsen
authored andcommitted
Add constant time spec for point addition
1 parent e370a72 commit abc436b

5 files changed

Lines changed: 91 additions & 64 deletions

File tree

src/Bedrock/P256.v

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ Proof.
148148
pose_correctness p256_point_add_nz_nz_neq_ok.
149149

150150
pose_correctness p256_point_add_vartime_if_doubling_ok.
151+
pose_correctness p256_point_add_constant_time_ok.
151152

152153
pose_correctness p256_point_add_affine_nz_nz_neq_ok.
153154
pose_correctness p256_point_add_affinenz_conditional_vartime_if_doubling_ok.

src/Bedrock/P256/Jacobian.v

Lines changed: 49 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ Local Open Scope list_scope.
4141

4242
Import (notations) coqutil.Map.Memory.
4343

44-
4544
Import Coq.micromega.Lia.
4645
Import coqutil.Tactics.Tactics.
4746

@@ -305,8 +304,6 @@ rewrite ?app_length, ?length_coord in *.
305304
trivial. }
306305
Qed.
307306

308-
Import coqutil.Tactics.Tactics.
309-
310307
Definition p256_point_add_vartime_if_doubling := func!(p_out, p_P, p_Q) {
311308
unpack! zeroP = p256_point_iszero(p_P);
312309
unpack! zeroQ = p256_point_iszero(p_Q);
@@ -322,66 +319,72 @@ Definition p256_point_add_vartime_if_doubling := func!(p_out, p_P, p_Q) {
322319
br_memcpy(p_out, p_sel, $(3*32))
323320
}.
324321

322+
#[local] Ltac ensure_map m := lazymatch type of m with | @Interface.map.rep _ _ _ => true | _ => false end.
323+
#[local] Ltac newest_memory_hyp := match goal with | H: ?G ?m |- _ =>
324+
match (ensure_map m) with true => H | false => fail end end.
325+
325326
Import memcpy.
326-
Lemma p256_point_add_vartime_if_doubling_ok : program_logic_goal_for_function! p256_point_add_vartime_if_doubling.
327+
Lemma p256_point_add_vartime_if_doubling_ok :
328+
let spec := spec_of_p256_point_add_vartime_if_doubling in
329+
program_logic_goal_for_function! p256_point_add_vartime_if_doubling.
327330
Proof.
328331
cbv [spec_of_p256_point_add_vartime_if_doubling].
329332
repeat straightline.
333+
pose proof (length_point P) as HlengthP. pose proof (length_point Q) as HlengthQ.
330334
straightline_call; repeat straightline. (*iszero*)
331-
{ letexists. ecancel_assumption. }
335+
{ eexists. ecancel_assumption. }
332336
straightline_call; repeat straightline. (*iszero*)
333-
{ letexists. ecancel_assumption. }
337+
{ eexists. ecancel_assumption. }
334338
(* stackalloc *)
335-
seprewrite_in_by (@Array.array1_iff_eq_of_list_word_at) H9 ltac:(lia).
339+
seprewrite_in_by (@Array.array1_iff_eq_of_list_word_at) ltac:(newest_memory_hyp) ltac:(lia).
336340
straightline_call; ssplit. (*add*)
337341
{ ecancel_assumption. }
338-
{ rewrite length_point; lia. }
342+
{ lia. }
339343
repeat straightline.
340344
straightline_call; repeat straightline (* br_declassify *).
341345
(* stackalloc *)
342-
seprewrite_in_by (@Array.array1_iff_eq_of_list_word_at) H17 ltac:(lia).
346+
seprewrite_in_by (@Array.array1_iff_eq_of_list_word_at) ltac:(newest_memory_hyp) ltac:(lia).
343347
straightline_call; ssplit. (* memset *)
344348
{ ecancel_assumption. }
345-
{ ZnWords.ZnWords. }
349+
{ ZnWords. }
346350
repeat straightline.
347351
straightline_call; repeat straightline; ssplit (* memcxor *).
348352
{ ecancel_assumption. }
349353
{ rewrite ?repeat_length; trivial. }
350-
{ rewrite H18, length_point; trivial. }
354+
{ ZnWords. }
351355
straightline_call; repeat straightline; ssplit (* memcxor *).
352356
{ ecancel_assumption. }
353357
{ rewrite ?repeat_length; trivial. }
354-
{ rewrite length_point; trivial. }
358+
{ ZnWords. }
355359
straightline_call; repeat straightline; ssplit (* memcxor *).
356360
{ ecancel_assumption. }
357361
{ rewrite ?repeat_length; trivial. }
358-
{ rewrite length_point; trivial. }
359-
362+
{ ZnWords. }
360363
subst x x0 x3.
361-
letexists; ssplit; repeat straightline; subst v (* if ok *).
364+
eexists; ssplit; repeat straightline. (* if ok *)
362365
{ straightline_call; repeat straightline; ssplit (* memcpy *).
363366
{ ecancel_assumption. }
364-
{ rewrite H10, length_point; trivial. }
367+
{ ZnWords. }
365368
{ trivial. }
366-
{ clear; ZnWords.ZnWords. }
369+
{ clear; ZnWords. }
367370
repeat straightline.
368371
(* stackdealloc *)
369-
progress repeat seprewrite_in_by (symmetry! @Array.array1_iff_eq_of_list_word_at) H42 ltac:(rewrite ?length_point in *; lia || ZnWords.ZnWords).
370-
progress repeat match type of H42 with context [Array.array ptsto _ _ (point.to_bytes ?x)] =>
371-
unique pose proof (length_point x) end.
372+
progress repeat seprewrite_in_by (symmetry! @Array.array1_iff_eq_of_list_word_at) ltac:(newest_memory_hyp)
373+
ltac:(rewrite ?length_point in *; lia || ZnWords).
372374
assert (Datatypes.length x6 = 96%nat) by ZnWords.ZnWords.
373375
repeat straightline.
374-
progress repeat seprewrite_in_by (@Array.array1_iff_eq_of_list_word_at) H42 ltac:(rewrite ?length_point in *; lia || ZnWords.ZnWords).
375-
376-
rewrite <-word.unsigned_of_Z_0, !word.unsigned_inj_iff in H27 by exact _.
377-
rewrite !word.lor_0_iff, !word.broadcast_0_iff in H27.
376+
progress repeat seprewrite_in_by (@Array.array1_iff_eq_of_list_word_at)
377+
ltac:(newest_memory_hyp) ltac:(lia || ZnWords.ZnWords).
378+
let Hzero := match goal with H: _ <> 0 |- _ => H end in
379+
rewrite <-word.unsigned_of_Z_0, !word.unsigned_inj_iff in Hzero by exact _;
380+
rewrite !word.lor_0_iff, !word.broadcast_0_iff in Hzero.
378381
destruct (iszero P) eqn:HP, (iszero Q) eqn:HQ in *; try intuition discriminate;
379382
repeat match goal with
380383
| H : _ = _ -> _ |- _ => specialize (H eq_refl)
381384
| H : ?x = ?y -> _ |- _ => assert (x = y -> False) as _ by inversion 1; clear H
382385
end;
383386
subst x4; subst x5; subst x6;
384-
rewrite ?Byte.map_xor_0_l in * by (rewrite ?length_point; ZnWords.ZnWords).
387+
rewrite ?Byte.map_xor_0_l in * by (ZnWords).
385388
{ (* 0 + 0 *)
386389
eexists (exist _ (0,0,0)%F I); split.
387390
{ use_sep_assumption; cancel. reflexivity. }
@@ -393,7 +396,6 @@ Proof.
393396
eexists; split. { ecancel_assumption. }
394397
apply Decidable.dec_bool, Jacobian.iszero_iff in HP.
395398
rewrite Jacobian.eq_iff, Jacobian.to_affine_add, HP.
396-
Import Curves.Weierstrass.AffineProofs.
397399
symmetry.
398400
eapply Hierarchy.left_identity. }
399401
{ (* P + 0 *)
@@ -406,7 +408,8 @@ Import Curves.Weierstrass.AffineProofs.
406408
rewrite <-Bool.not_true_iff_false in HP, HQ.
407409
(* Decidable.dec_iff? *)
408410
cbv [iszero] in HP, HQ; case Decidable.dec in HP; case Decidable.dec in HQ; try congruence.
409-
destruct (H19 ltac:(trivial) ltac:(trivial)) as [HE|]; [|intuition fail].
411+
match goal with H : ~ Jacobian.iszero ?P -> _ |- _ =>
412+
destruct (H ltac:(trivial) ltac:(trivial)) as [HE|]; [|intuition fail] end.
410413
case HE as [_ (?&HE)].
411414
repeat straightline_cleanup.
412415
eexists; split; [ecancel_assumption|].
@@ -428,7 +431,7 @@ Import Curves.Weierstrass.AffineProofs.
428431
straightline_call; repeat straightline.
429432
{ split. { ecancel_assumption. }
430433
rewrite ?map_length, ?combine_length, ?repeat_length.
431-
rewrite H18, length_point. clear; ZnWords.ZnWords. }
434+
rewrite H18, length_point. clear; reflexivity. }
432435

433436
straightline_call; repeat straightline; ssplit (* memcpy *).
434437
{ ecancel_assumption. }
@@ -437,18 +440,34 @@ Import Curves.Weierstrass.AffineProofs.
437440
{ clear; ZnWords.ZnWords. }
438441
repeat straightline.
439442
(* stackdealloc *)
440-
progress repeat seprewrite_in_by (symmetry! @Array.array1_iff_eq_of_list_word_at) H31 ltac:(rewrite ?length_point in *; lia || ZnWords.ZnWords).
443+
progress repeat seprewrite_in_by (symmetry! @Array.array1_iff_eq_of_list_word_at)
444+
ltac:(newest_memory_hyp) ltac:(rewrite ?length_point in *; lia || ZnWords.ZnWords).
441445
progress repeat match type of H31 with context [Array.array ptsto _ _ (point.to_bytes ?x)] =>
442446
unique pose proof (length_point x) end.
443447
repeat straightline.
444-
progress repeat seprewrite_in_by (@Array.array1_iff_eq_of_list_word_at) H31 ltac:(rewrite ?length_point in *; lia || ZnWords.ZnWords).
448+
progress repeat seprewrite_in_by (@Array.array1_iff_eq_of_list_word_at)
449+
ltac:(newest_memory_hyp) ltac:(rewrite ?length_point in *; lia || ZnWords.ZnWords).
445450

446451
eexists; ssplit. { ecancel_assumption. }
447452
rewrite <-HE, <-Jacobian.double_minus_3_eq_double.
448453
rewrite Jacobian.eq_iff, Jacobian.to_affine_double, Jacobian.to_affine_add.
449454
reflexivity. }
450455
Qed.
451456

457+
Lemma p256_point_add_constant_time_ok :
458+
let spec := spec_of_p256_point_add_constant_time in
459+
program_logic_goal_for_function! p256_point_add_vartime_if_doubling.
460+
Proof.
461+
unfold spec_of_p256_point_add_constant_time.
462+
pose proof p256_point_add_vartime_if_doubling_ok as Hvartime.
463+
unfold spec_of_p256_point_add_vartime_if_doubling in Hvartime.
464+
cbv [program_logic_goal_for] in *.
465+
do 21 intros ?. intros (?, (?, ?)).
466+
eapply Semantics.weaken_call.
467+
{ eapply Hvartime; try trivial. split; eassumption. }
468+
cbv [id]. intros. assumption.
469+
Qed.
470+
452471
Definition p256_point_double := func!(out, in1) {
453472
stackalloc 32 as D;
454473
stackalloc 32 as A;

src/Bedrock/P256/PrecomputedMultiples.v

Lines changed: 18 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,11 @@ Definition p256_precompute_multiples := func! (p_table, p_P) {
5858
i = $2;
5959
while ($17 - i) {
6060
if (i & $1) {
61-
unpack! ok = p256_point_add_nz_nz_neq(
61+
p256_point_add_vartime_if_doubling(
6262
p_table + ($sizeof_point * i),
6363
p_table + ($sizeof_point * (i - $1)),
6464
p_P
65-
);
66-
$(cmd.unset "ok")
65+
)
6766
} else {
6867
p256_point_double(
6968
p_table + ($sizeof_point * i),
@@ -143,8 +142,7 @@ Qed.
143142
fnspec! "p256_precompute_multiples" p_table p_P / out (P : point) R,
144143
{ requires t m :=
145144
m =* out$@p_table * P$@p_P * R /\
146-
length out = (sizeof_point * 17)%nat /\
147-
~ Jacobian.iszero P;
145+
length out = (sizeof_point * 17)%nat;
148146
ensures t' m := t' = t /\ exists out,
149147
m =* pointarray p_table out * P$@p_P * R /\
150148
List.Forall2 W.eq (map to_affine out) (W.multiples 17 (to_affine P))
@@ -430,7 +428,8 @@ Proof.
430428
{ etransitivity; [eassumption|]. repeat Morphisms.f_equiv. lia. }
431429
Qed.
432430

433-
Lemma p256_precompute_multiples_ok : program_logic_goal_for_function! p256_precompute_multiples.
431+
Lemma p256_precompute_multiples_ok :
432+
let spec := spec_of_p256_point_add_constant_time in program_logic_goal_for_function! p256_precompute_multiples.
434433
Proof.
435434
repeat straightline.
436435

@@ -498,7 +497,7 @@ Proof.
498497
rewrite ?map.of_list_word_nil in *.
499498
seprewrite_in @sepclause_of_map_empty ltac:(hyp_containing m0).
500499
eexists; ssplit; [ecancel_assumption|].
501-
eqapply H12. f_equal. ZnWords.
500+
eqapply H11. f_equal. ZnWords.
502501
}
503502

504503
(* Loop invariant is preserved. *)
@@ -539,38 +538,26 @@ Proof.
539538
eapply ListUtil.Forall2_forall_iff'; [solve_num|assumption|solve_num].
540539
}
541540

542-
(* p256_point_add_nz_nz_neq *)
541+
(* Point addition in constant time. *)
543542
straightline_call; ssplit.
544543
{ use_sep_assumption; cancel.
545544
(* Set Ltac Profiling. *)
546545
cancel_seps_at_indices 3%nat 0%nat; [match_up_pointers; exact eq_refl|]. (* 0.4s *)
547546
cancel_seps_at_indices 1%nat 0%nat; [match_up_pointers; exact eq_refl|]. (* 0.5s *)
548547
cbv [seps]; ecancel. }
549548
{ solve_num. }
549+
{ rewrite <-(ScalarMult.scalarmult_1_l (eq:=W.eq) (to_affine P)).
550+
rewrite Hvm1P. intros Hzero.
551+
rewrite p256_mul_mod_n. {
552+
cbv [p256_group_order]; PreOmega.Z.div_mod_to_equations. lia. }
553+
{ intros HPzero. apply Hzero. rewrite HPzero.
554+
rewrite ScalarMult.scalarmult_zero_r, ScalarMult.scalarmult_1_l.
555+
split; reflexivity. } }
550556

551557
repeat straightline.
552558

553-
let h := hyp_containing @Forall2 in rename h into Hf2.
554-
555-
unshelve (match goal with H : ~ Jacobian.iszero ?P -> _ |- _ =>
556-
repeat (let HH := fresh in assert (~ Jacobian.iszero P);
557-
[shelve|specialize (H HH)]); trivial;
558-
rename H into Hadd end).
559-
{ rewrite Jacobian.iszero_iff. rewrite Hvm1P.
560-
rewrite <- (ScalarMult.scalarmult_0_l (eq:=W.eq) (Jacobian.to_affine P)), p256_mul_mod_n.
561-
cbv [p256_group_order]; PreOmega.Z.div_mod_to_equations; lia. }
562-
563-
destruct Hadd as [(?&?&?)|[? Hadd] ]; trivial; [|contradict Hadd]; cycle 1.
564-
{ (* no doubling *)
565-
rewrite Jacobian.eq_iff. rewrite Hvm1P.
566-
rewrite <- (ScalarMult.scalarmult_1_l (eq:=W.eq) (to_affine P)) at 2.
567-
rewrite p256_mul_mod_n.
568-
cbv [p256_group_order]; PreOmega.Z.div_mod_to_equations; lia. }
569-
570-
repeat straightline.
571-
572-
eexists (S v); split.
573-
{ subst i. subst x0.
559+
eexists (S v); split.
560+
{ subst i.
574561
eexists (multiples ++ [_]), (skipn sizeof_point todo); ssplit.
575562
{ rewrite (pointlist_firstn_nth_skipn multiples (v - 1)) by solve_num.
576563
repeat seprewrite @array_append; repeat seprewrite @array_cons;
@@ -584,7 +571,8 @@ Proof.
584571
apply Forall2_app.
585572
{ assumption. }
586573
repeat constructor.
587-
rewrite Jacobian.to_affine_add_inequal_nz_nz by assumption.
574+
let H := hyp_containing (add vm1P P) in rewrite H.
575+
rewrite Jacobian.to_affine_add.
588576
rewrite Hvm1P.
589577
rewrite <- (ScalarMult.scalarmult_1_l (mul:=W.mul) (eq:=W.eq) (Jacobian.to_affine P)) at 2.
590578
rewrite <-ScalarMult.scalarmult_add_l.

src/Bedrock/P256/Specs.v

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -313,13 +313,31 @@ Context {ext_spec : Semantics.ExtSpec}.
313313
m =* out$@p_out * P$@p_P * R
314314
}%sep.
315315

316-
#[export] Instance spec_of_p256_point_add_vartime_if_doubling : spec_of "p256_point_add_vartime_if_doubling" :=
317-
fnspec! "p256_point_add_vartime_if_doubling" p_out p_P p_Q / out (P Q : point),
318-
{ requires t m := m =* out$@p_out * P$@p_P * Q$@p_Q /\ length out = length P;
316+
(* Two specs exist as Definitions for p256_point_add_vartime_if_doubling:
317+
1. General: Variable-time, supports equal points.
318+
2. Constant-time: Requires points equal only if both are zero.
319+
Select the constant-time spec to enforce its preconditions. Note:
320+
This ensures conditional safety, not a proof of constant-time execution. *)
321+
322+
#[global] Definition spec_of_p256_point_add_vartime_if_doubling : spec_of "p256_point_add_vartime_if_doubling" :=
323+
fnspec! "p256_point_add_vartime_if_doubling" p_out p_P p_Q / out (P Q : point) R,
324+
{ requires t m := m =* out$@p_out * P$@p_P * Q$@p_Q * R /\ length out = length P;
319325
ensures t' m := t' = t /\ exists out : point,
320-
m =* out$@p_out * P$@p_P * Q$@p_Q /\ Jacobian.eq out (Jacobian.add P Q)
326+
m =* out$@p_out * P$@p_P * Q$@p_Q * R /\ Jacobian.eq out (Jacobian.add P Q)
321327
}%sep.
322328

329+
#[global] Definition spec_of_p256_point_add_constant_time : spec_of "p256_point_add_vartime_if_doubling" :=
330+
fnspec! "p256_point_add_vartime_if_doubling" p_out p_P p_Q / out (P Q : point) R,
331+
{ requires t m :=
332+
m =* out$@p_out * P$@p_P * Q$@p_Q * R /\
333+
length out = length P /\
334+
(~ (W.eq (Jacobian.to_affine P) W.zero /\ W.eq (Jacobian.to_affine Q) W.zero) ->
335+
~ W.eq (Jacobian.to_affine P) (Jacobian.to_affine Q));
336+
ensures T M := T = t /\ exists (out : point),
337+
M =* out$@p_out * P$@p_P * Q$@p_Q * R /\
338+
Jacobian.eq out (Jacobian.add P Q)
339+
}.
340+
323341
#[export] Instance spec_of_p256_point_add_affine_nz_nz_neq : spec_of "p256_point_add_affine_nz_nz_neq" :=
324342
fnspec! "p256_point_add_affine_nz_nz_neq" p_out p_P p_Q / out (P : point) (Q : affine_point) R ~> ok,
325343
{ requires t m := m =* out$@p_out * P$@p_P * (Jacobian.of_affine Q)$@p_Q * R /\ length out = length P;

src/Curves/Weierstrass/P256.v

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ Proof.
7979
Qed.
8080

8181
Lemma p256_mul_mod_n (a b : Z) (P : Wpoint) :
82+
~ W.eq P W.zero ->
8283
W.eq (W.mul a P) (W.mul b P) <->
8384
a mod p256_group_order = b mod p256_group_order.
8485
Proof. Admitted.

0 commit comments

Comments
 (0)