Skip to content

Commit 21f3fc5

Browse files
committed
Add constant time spec for point addition
1 parent d00cba0 commit 21f3fc5

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),
@@ -142,8 +141,7 @@ Qed.
142141
fnspec! "p256_precompute_multiples" p_table p_P / out (P : point) R,
143142
{ requires t m :=
144143
m =* out$@p_table * P$@p_P * R /\
145-
length out = (sizeof_point * 17)%nat /\
146-
~ Jacobian.iszero P;
144+
length out = (sizeof_point * 17)%nat;
147145
ensures t' m := t' = t /\ exists out,
148146
m =* pointarray p_table out * P$@p_P * R /\
149147
List.Forall2 W.eq (map to_affine out) (W.multiples 17 (to_affine P))
@@ -421,7 +419,8 @@ Proof.
421419
{ etransitivity; [eassumption|]. repeat Morphisms.f_equiv. lia. }
422420
Qed.
423421

424-
Lemma p256_precompute_multiples_ok : program_logic_goal_for_function! p256_precompute_multiples.
422+
Lemma p256_precompute_multiples_ok :
423+
let spec := spec_of_p256_point_add_constant_time in program_logic_goal_for_function! p256_precompute_multiples.
425424
Proof.
426425
repeat straightline.
427426

@@ -489,7 +488,7 @@ Proof.
489488
rewrite ?map.of_list_word_nil in *.
490489
seprewrite_in @sepclause_of_map_empty ltac:(hyp_containing m0).
491490
eexists; ssplit; [ecancel_assumption|].
492-
eqapply H12. f_equal. ZnWords.
491+
eqapply H11. f_equal. ZnWords.
493492
}
494493

495494
(* Loop invariant is preserved. *)
@@ -530,38 +529,26 @@ Proof.
530529
eapply ListUtil.Forall2_forall_iff'; [solve_num|assumption|solve_num].
531530
}
532531

533-
(* p256_point_add_nz_nz_neq *)
532+
(* Point addition in constant time. *)
534533
straightline_call; ssplit.
535534
{ use_sep_assumption; cancel.
536535
(* Set Ltac Profiling. *)
537536
cancel_seps_at_indices 3%nat 0%nat; [match_up_pointers; exact eq_refl|]. (* 0.4s *)
538537
cancel_seps_at_indices 1%nat 0%nat; [match_up_pointers; exact eq_refl|]. (* 0.5s *)
539538
cbv [seps]; ecancel. }
540539
{ solve_num. }
540+
{ rewrite <-(ScalarMult.scalarmult_1_l (eq:=W.eq) (to_affine P)).
541+
rewrite Hvm1P. intros Hzero.
542+
rewrite p256_mul_mod_n. {
543+
cbv [p256_group_order]; PreOmega.Z.div_mod_to_equations. lia. }
544+
{ intros HPzero. apply Hzero. rewrite HPzero.
545+
rewrite ScalarMult.scalarmult_zero_r, ScalarMult.scalarmult_1_l.
546+
split; reflexivity. } }
541547

542548
repeat straightline.
543549

544-
let h := hyp_containing @Forall2 in rename h into Hf2.
545-
546-
unshelve (match goal with H : ~ Jacobian.iszero ?P -> _ |- _ =>
547-
repeat (let HH := fresh in assert (~ Jacobian.iszero P);
548-
[shelve|specialize (H HH)]); trivial;
549-
rename H into Hadd end).
550-
{ rewrite Jacobian.iszero_iff. rewrite Hvm1P.
551-
rewrite <- (ScalarMult.scalarmult_0_l (eq:=W.eq) (Jacobian.to_affine P)), p256_mul_mod_n.
552-
cbv [p256_group_order]; PreOmega.Z.div_mod_to_equations; lia. }
553-
554-
destruct Hadd as [(?&?&?)|[? Hadd] ]; trivial; [|contradict Hadd]; cycle 1.
555-
{ (* no doubling *)
556-
rewrite Jacobian.eq_iff. rewrite Hvm1P.
557-
rewrite <- (ScalarMult.scalarmult_1_l (eq:=W.eq) (to_affine P)) at 2.
558-
rewrite p256_mul_mod_n.
559-
cbv [p256_group_order]; PreOmega.Z.div_mod_to_equations; lia. }
560-
561-
repeat straightline.
562-
563-
eexists (S v); split.
564-
{ subst i. subst x0.
550+
eexists (S v); split.
551+
{ subst i.
565552
eexists (multiples ++ [_]), (skipn sizeof_point todo); ssplit.
566553
{ rewrite (pointlist_firstn_nth_skipn multiples (v - 1)) by solve_num.
567554
repeat seprewrite @array_append; repeat seprewrite @array_cons;
@@ -575,7 +562,8 @@ Proof.
575562
apply Forall2_app.
576563
{ assumption. }
577564
repeat constructor.
578-
rewrite Jacobian.to_affine_add_inequal_nz_nz by assumption.
565+
let H := hyp_containing (add vm1P P) in rewrite H.
566+
rewrite Jacobian.to_affine_add.
579567
rewrite Hvm1P.
580568
rewrite <- (ScalarMult.scalarmult_1_l (mul:=W.mul) (eq:=W.eq) (Jacobian.to_affine P)) at 2.
581569
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)