Skip to content

Commit

Permalink
[ec] Use windowed algorithm for base scalar mult
Browse files Browse the repository at this point in the history
Using a sliding window method with pre-computed values of multiples of
the generator point, obtain far more efficient performance for the
special case where G = P in the scalar multiplication kP.

By using a safe selection algorithm for pre-computed values and no
branches in the main loop, the algorithm leaks no less information about
its inputs than the current Montgomery ladder.
  • Loading branch information
Firobe committed Feb 13, 2024
1 parent 36bc72f commit 9f767a9
Showing 1 changed file with 67 additions and 5 deletions.
72 changes: 67 additions & 5 deletions ec/mirage_crypto_ec.ml
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,8 @@ module type Scalar = sig
val of_octets : string -> (scalar, error) result
val to_octets : scalar -> string
val scalar_mult : scalar -> point -> point

val scalar_mult_base : scalar -> point
end

module Make_scalar (Param : Parameters) (P : Point) : Scalar = struct
Expand All @@ -433,6 +435,63 @@ module Make_scalar (Param : Parameters) (P : Point) : Scalar = struct

let to_octets (Scalar buf) = rev_string buf

(* Use a sliding window optimization method for scalar multiplication
Hard-coded window size = 4
Implementation inspired from Go's crypto library
https://github.com/golang/go/blob/a5cd894318677359f6d07ee74f9004d28b4d164c/src/crypto/internal/nistec/p256.go#L317
*)
module Precomputed = struct
(* Pre-compute multiples of the generator point *)
let pre_compute_multiples () =
let len = Param.fe_length * 2 in
let one_table _ = Array.init 15 (fun _ -> P.at_infinity ()) in
let table = Array.init len one_table in
let base = ref P.params_g in
for i = 0 to len - 1 do
table.(i).(0) <- !base;
for j = 1 to 14 do
table.(i).(j) <- P.add !base table.(i).(j - 1)
done;
base := P.double !base;
base := P.double !base;
base := P.double !base;
base := P.double !base
done;
table

(* Select the n-th element of the table
without leaking information about [n] *)
let table_select table n =
let p = ref (P.at_infinity ()) in
for i = 1 to 15 do
let cond = not (Eqaf.bool_of_int (n - i)) in
p := P.select cond ~then_:table.(i - 1) ~else_:!p
done;
!p

(* Returns [kG] by decomposing [k] in binary form, and adding
[2^0G * k_0 + 2^1G * k_1 + ...] in constant time using
pre-computed values of 2^iG *)
let scalar_mult_base (Scalar k) tables =
let p = ref (P.at_infinity ()) in
let index = ref 0 in (* Index increases since k is big-endian *)
for i = 0 to String.length k - 1 do
let byte = String.get_uint8 k i in
let winValue = byte land 0b1111 in
p := P.add !p (table_select tables.(!index) winValue);
incr index;
let winValue = byte lsr 4 in
p := P.add !p (table_select tables.(!index) winValue);
incr index
done;
!p

let scalar_mult_base =
let tables = pre_compute_multiples () in
fun d -> scalar_mult_base d tables
end

(* Branchless Montgomery ladder method *)
let scalar_mult (Scalar s) p =
let r0 = ref (P.at_infinity ()) in
let r1 = ref p in
Expand All @@ -445,6 +504,9 @@ module Make_scalar (Param : Parameters) (P : Point) : Scalar = struct
r1 := P.select bit ~then_:r1_double ~else_:sum
done;
!r0

(* Specialization of [scalar_mult d p] when [p] is the generator *)
let scalar_mult_base = Precomputed.scalar_mult_base
end

module Make_dh (Param : Parameters) (P : Point) (S : Scalar) : Dh = struct
Expand All @@ -459,7 +521,7 @@ module Make_dh (Param : Parameters) (P : Point) (S : Scalar) : Dh = struct
type secret = scalar

let share ?(compress = false) private_key =
let public_key = S.scalar_mult private_key P.params_g in
let public_key = S.scalar_mult_base private_key in
point_to_octets ~compress public_key

let secret_of_octets ?compress s =
Expand Down Expand Up @@ -668,7 +730,7 @@ module Make_dsa (Param : Parameters) (F : Fn) (P : Point) (S : Scalar) (H : Mira
in
one ()
in
let q = S.scalar_mult d P.params_g in
let q = S.scalar_mult_base d in
(d, q)

let x_of_finite_point_mod_n p =
Expand All @@ -695,7 +757,7 @@ module Make_dsa (Param : Parameters) (F : Fn) (P : Point) (S : Scalar) (H : Mira
| Ok ksc -> ksc
| Error _ -> invalid_arg "k not in range" (* if no k is provided, this cannot happen since K_gen_*.gen already preserves the Scalar invariants *)
in
let point = S.scalar_mult ksc P.params_g in
let point = S.scalar_mult_base ksc in
match x_of_finite_point_mod_n point with
| None -> again ()
| Some r ->
Expand All @@ -719,7 +781,7 @@ module Make_dsa (Param : Parameters) (F : Fn) (P : Point) (S : Scalar) (H : Mira
let r, s = sign_octets ~key ?k:(Option.map Cstruct.to_string k) (Cstruct.to_string msg) in
Cstruct.of_string r, Cstruct.of_string s

let pub_of_priv priv = S.scalar_mult priv P.params_g
let pub_of_priv priv = S.scalar_mult_base priv

let verify_octets ~key (r, s) msg =
try
Expand All @@ -743,7 +805,7 @@ module Make_dsa (Param : Parameters) (F : Fn) (P : Point) (S : Scalar) (H : Mira
| Ok u1, Ok u2 ->
let point =
P.add
(S.scalar_mult u1 P.params_g)
(S.scalar_mult_base u1)
(S.scalar_mult u2 key)
in
begin match x_of_finite_point_mod_n point with
Expand Down

0 comments on commit 9f767a9

Please sign in to comment.