Skip to content

Commit a7b60c5

Browse files
authored
[ec] Handle K_gen correctly for bitlen mod 8 <> 0 (#230)
* [ec] Handle K_gen correctly for bitlen mod 8 <> 0
1 parent 38bde3a commit a7b60c5

File tree

3 files changed

+39
-7
lines changed

3 files changed

+39
-7
lines changed

ec/mirage_crypto_ec.ml

+32-3
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ module type Dsa = sig
5151
type priv
5252
type pub
5353
val byte_length : int
54+
val bit_length : int
5455
val priv_of_octets : string -> (priv, error) result
5556
val priv_to_octets : priv -> string
5657
val pub_of_octets : string -> (pub, error) result
@@ -85,6 +86,7 @@ module type Parameters = sig
8586
val n : field_element
8687
val pident: string
8788
val byte_length : int
89+
val bit_length : int
8890
val fe_length : int
8991
val first_byte_bits : int option
9092
end
@@ -600,6 +602,8 @@ module Make_dsa (Param : Parameters) (F : Fn) (P : Point) (S : Scalar) (H : Dige
600602

601603
let byte_length = Param.byte_length
602604

605+
let bit_length = Param.bit_length
606+
603607
let priv_of_octets= S.of_octets
604608

605609
let priv_to_octets = S.to_octets
@@ -628,14 +632,36 @@ module Make_dsa (Param : Parameters) (F : Fn) (P : Point) (S : Scalar) (H : Dige
628632

629633
let g ~key msg =
630634
let g = Mirage_crypto_rng.create ~strict:true drbg in
631-
Mirage_crypto_rng.reseed ~g
632-
(S.to_octets key ^ msg);
635+
Mirage_crypto_rng.reseed ~g (S.to_octets key ^ msg);
633636
g
634637

638+
(* Defined in RFC 6979 sec 2.3.2 with
639+
- blen = 8 * Param.byte_length
640+
- qlen = Param.bit_length *)
641+
let bits2int r =
642+
(* keep qlen *leftmost* bits *)
643+
let shift = (8 * Param.byte_length) - Param.bit_length in
644+
if shift = 0 then
645+
Bytes.unsafe_to_string r
646+
else
647+
(* Assuming shift is < 8 *)
648+
let r' = Bytes.create Param.byte_length in
649+
let p = ref 0x00 in
650+
for i = 0 to Param.byte_length - 1 do
651+
let x = Bytes.get_uint8 r i in
652+
let v = (x lsr shift) lor (!p lsl (8 - shift)) in
653+
p := x;
654+
Bytes.set_uint8 r' i v
655+
done;
656+
Bytes.unsafe_to_string r'
657+
635658
(* take qbit length, and ensure it is suitable for ECDSA (> 0 & < n) *)
636659
let gen g =
637660
let rec go () =
638-
let r = Mirage_crypto_rng.generate ~g Param.byte_length in
661+
let b = Bytes.create Param.byte_length in
662+
Mirage_crypto_rng.generate_into ~g b Param.byte_length;
663+
(* truncate to the desired number of bits *)
664+
let r = bits2int b in
639665
if S.is_in_range r then r else go ()
640666
in
641667
go ()
@@ -758,6 +784,7 @@ module P256 : Dh_dsa = struct
758784
let n = "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xBC\xE6\xFA\xAD\xA7\x17\x9E\x84\xF3\xB9\xCA\xC2\xFC\x63\x25\x51"
759785
let pident = "\x3F\xFF\xFF\xFF\xC0\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" |> rev_string (* (Params.p + 1) / 4*)
760786
let byte_length = 32
787+
let bit_length = 256
761788
let fe_length = 32
762789
let first_byte_bits = None
763790
end
@@ -809,6 +836,7 @@ module P384 : Dh_dsa = struct
809836
let n = "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xC7\x63\x4D\x81\xF4\x37\x2D\xDF\x58\x1A\x0D\xB2\x48\xB0\xA7\x7A\xEC\xEC\x19\x6A\xCC\xC5\x29\x73"
810837
let pident = "\x3F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xBF\xFF\xFF\xFF\xC0\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00" |> rev_string (* (Params.p + 1) / 4*)
811838
let byte_length = 48
839+
let bit_length = 384
812840
let fe_length = 48
813841
let first_byte_bits = None
814842
end
@@ -861,6 +889,7 @@ module P521 : Dh_dsa = struct
861889
let n = "\x01\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFA\x51\x86\x87\x83\xBF\x2F\x96\x6B\x7F\xCC\x01\x48\xF7\x09\xA5\xD0\x3B\xB5\xC9\xB8\x89\x9C\x47\xAE\xBB\x6F\xB7\x1E\x91\x38\x64\x09"
862890
let pident = "\x01\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" |> rev_string
863891
let byte_length = 66
892+
let bit_length = 521
864893
let fe_length = if Sys.word_size == 64 then 72 else 68 (* TODO: is this congruent with C code? *)
865894
let first_byte_bits = Some 0x01
866895
end

ec/mirage_crypto_ec.mli

+3
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ module type Dsa = sig
7575
val byte_length : int
7676
(** [byte_length] is the size of a ECDSA signature in bytes. *)
7777

78+
val bit_length : int
79+
(** [bit_length] is the number of significant bits in a ECDSA signature *)
80+
7881
(** {2 Serialisation} *)
7982

8083
val priv_of_octets : string -> (priv, error) result

tests/test_ec.ml

+4-4
Original file line numberDiff line numberDiff line change
@@ -511,7 +511,7 @@ let ecdsa_rfc6979_p521 =
511511
let sig' = P521.Dsa.sign ~key:priv ~k msg in
512512
Alcotest.(check bool __LOC__ true (sig_eq sig'))
513513
in
514-
let _cases = [
514+
let cases = [
515515

516516
case Digestif.sha1 ~message:"sample"
517517
~k:"089C071B419E1C2820962321787258469511958E80582E95D8378E0C2CCDB3CB
@@ -624,9 +624,9 @@ let ecdsa_rfc6979_p521 =
624624
CE3"
625625

626626
] in
627-
[ ("public key matches", `Quick, pub_rfc); ("public key compression and decompression", `Quick, pub_key_compression)]
628-
(* TODO: our deterministic generator for bit_size mod 8 <> 0 is different from RFC 6979 *)
629-
(* List.mapi (fun i c -> "RFC 6979 A.2.7 " ^ string_of_int i, `Quick, c) cases *)
627+
("public key matches", `Quick, pub_rfc) ::
628+
("public key compression and decompression", `Quick, pub_key_compression) ::
629+
List.mapi (fun i c -> "RFC 6979 A.2.7 " ^ string_of_int i, `Quick, c) cases
630630

631631
let x25519 () =
632632
(* RFC 7748, 6.1 *)

0 commit comments

Comments
 (0)