···6565 end
6666end
67676868+module type Point = sig
6969+ type point
7070+ type scalar
7171+ val of_octets : string -> (point, error) result
7272+ val to_octets : ?compress:bool -> point -> string
7373+ val scalar_of_octets : string -> (scalar, error) result
7474+ val scalar_to_octets : scalar -> string
7575+ val generator : point
7676+ val add : point -> point -> point
7777+ val scalar_mult : scalar -> point -> point
7878+end
7979+6880module type Dh_dsa = sig
6981 module Dh : Dh
7082 module Dsa : Dsa
8383+ module Point : Point
7184end
72857386type field_element = string
···225238 out_p_to_p tmp
226239end
227240228228-module type Point = sig
241241+module type Point_ops = sig
229242 val at_infinity : unit -> point
230243 val is_infinity : point -> bool
231244 val add : point -> point -> point
···239252 val scalar_mult_base : scalar -> point
240253end
241254242242-module Make_point (P : Parameters) (F : Foreign) : Point = struct
255255+module Make_point_ops (P : Parameters) (F : Foreign) : Point_ops = struct
243256 module Fe = Make_field_element(P)(F)
244257245258 let at_infinity () =
···431444 val generator_tables : unit -> field_element array array array
432445end
433446434434-module Make_scalar (Param : Parameters) (P : Point) : Scalar = struct
447447+module Make_scalar (Param : Parameters) (P : Point_ops) : Scalar = struct
435448 let not_zero =
436449 let zero = String.make Param.byte_length '\000' in
437450 fun buf -> not (Eqaf.equal buf zero)
···486499 Array.map (Array.map convert) table
487500end
488501489489-module Make_dh (Param : Parameters) (P : Point) (S : Scalar) : Dh = struct
502502+module Make_dh (Param : Parameters) (P : Point_ops) (S : Scalar) : Dh = struct
490503 let point_of_octets c =
491504 match P.of_octets c with
492505 | Ok p when not (P.is_infinity p) -> Ok p
···597610 b_uts tmp
598611end
599612600600-module Make_dsa (Param : Parameters) (F : Fn) (P : Point) (S : Scalar) (H : Digestif.S) = struct
613613+module Make_dsa (Param : Parameters) (F : Fn) (P : Point_ops) (S : Scalar) (H : Digestif.S) = struct
601614 type priv = scalar
602615603616 let byte_length = Param.byte_length
···774787 end
775788end
776789790790+module Make_point (P : Point_ops) (S : Scalar) : Point
791791+ with type point = point and type scalar = scalar
792792+= struct
793793+ type nonrec point = point
794794+ type nonrec scalar = scalar
795795+ let of_octets = P.of_octets
796796+ let to_octets ?(compress = false) p = P.to_octets ~compress p
797797+ let scalar_of_octets = S.of_octets
798798+ let scalar_to_octets = S.to_octets
799799+ let generator = P.params_g
800800+ let add = P.add
801801+ let scalar_mult = S.scalar_mult
802802+end
803803+777804module P256 : Dh_dsa = struct
778805 module Params = struct
779806 let a = "\xFF\xFF\xFF\xFF\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFC"
···818845 external to_montgomery : out_field_element -> field_element -> unit = "mc_np256_to_montgomery" [@@noalloc]
819846 end
820847821821- module P = Make_point(Params)(Foreign)
848848+ module P = Make_point_ops(Params)(Foreign)
822849 module S = Make_scalar(Params)(P)
823850 module Dh = Make_dh(Params)(P)(S)
824851 module Fn = Make_Fn(Params)(Foreign_n)
825852 module Dsa = Make_dsa(Params)(Fn)(P)(S)(Digestif.SHA256)
853853+ module Point = Make_point(P)(S)
826854end
827855828856module P384 : Dh_dsa = struct
···870898 external to_montgomery : out_field_element -> field_element -> unit = "mc_np384_to_montgomery" [@@noalloc]
871899 end
872900873873- module P = Make_point(Params)(Foreign)
901901+ module P = Make_point_ops(Params)(Foreign)
874902 module S = Make_scalar(Params)(P)
875903 module Dh = Make_dh(Params)(P)(S)
876904 module Fn = Make_Fn(Params)(Foreign_n)
877905 module Dsa = Make_dsa(Params)(Fn)(P)(S)(Digestif.SHA384)
906906+ module Point = Make_point(P)(S)
878907end
879908880909module P521 : Dh_dsa = struct
···923952 external to_montgomery : out_field_element -> field_element -> unit = "mc_np521_to_montgomery" [@@noalloc]
924953 end
925954926926- module P = Make_point(Params)(Foreign)
955955+ module P = Make_point_ops(Params)(Foreign)
927956 module S = Make_scalar(Params)(P)
928957 module Dh = Make_dh(Params)(P)(S)
929958 module Fn = Make_Fn(Params)(Foreign_n)
930959 module Dsa = Make_dsa(Params)(Fn)(P)(S)(Digestif.SHA512)
960960+ module Point = Make_point(P)(S)
931961end
932962933963module X25519 = struct
+37
ec/mirage_crypto_ec.mli
···155155 end
156156end
157157158158+(** Low-level point arithmetic. *)
159159+module type Point = sig
160160+ type point
161161+ (** The type for points on the elliptic curve. *)
162162+163163+ type scalar
164164+ (** The type for scalars. *)
165165+166166+ val of_octets : string -> (point, error) result
167167+ (** [of_octets buf] decodes a point from [buf] in uncompressed or compressed
168168+ SEC 1 format. Returns an error if the point is not on the curve. *)
169169+170170+ val to_octets : ?compress:bool -> point -> string
171171+ (** [to_octets ~compress point] encodes [point] to SEC 1 format. If
172172+ [compress] is [true] (default [false]), the compressed format is used. *)
173173+174174+ val scalar_of_octets : string -> (scalar, error) result
175175+ (** [scalar_of_octets buf] decodes a scalar from [buf]. Returns an error if
176176+ the scalar is not in the valid range \[1, n-1\] where n is the group
177177+ order. *)
178178+179179+ val scalar_to_octets : scalar -> string
180180+ (** [scalar_to_octets scalar] encodes [scalar] to a byte string. *)
181181+182182+ val generator : point
183183+ (** [generator] is the generator point (base point) of the curve. *)
184184+185185+ val add : point -> point -> point
186186+ (** [add p q] is the sum of points [p] and [q]. *)
187187+188188+ val scalar_mult : scalar -> point -> point
189189+ (** [scalar_mult s p] is the scalar multiplication of [p] by [s]. *)
190190+end
191191+158192(** Elliptic curve with Diffie-Hellman and DSA. *)
159193module type Dh_dsa = sig
160194···163197164198 (** Digital signature algorithm. *)
165199 module Dsa : Dsa
200200+201201+ (** Low-level point arithmetic. *)
202202+ module Point : Point
166203end
167204168205(** The NIST P-256 curve, also known as SECP256R1. *)
+88
tests/test_ec.ml
···803803 |};
804804 ]
805805806806+let point_module_tests (module C : Mirage_crypto_ec.Dh_dsa) name =
807807+ let open C in
808808+ let test_generator_not_identity () =
809809+ (* Generator should not be the identity (at infinity) *)
810810+ let g = Point.generator in
811811+ let g_bytes = Point.to_octets g in
812812+ (* Generator serialized should not be just the identity point *)
813813+ Alcotest.(check bool) "generator has non-trivial encoding"
814814+ true (String.length g_bytes > 1)
815815+ in
816816+ let test_point_serialization_roundtrip () =
817817+ (* Generate a key pair and check that the public key roundtrips through Point *)
818818+ let _priv, pub = Dsa.generate () in
819819+ let pub_bytes = Dsa.pub_to_octets pub in
820820+ match Point.of_octets pub_bytes with
821821+ | Ok point ->
822822+ let point_bytes = Point.to_octets point in
823823+ Alcotest.(check string) "point roundtrip" pub_bytes point_bytes
824824+ | Error e -> Alcotest.failf "of_octets failed: %a" pp_error e
825825+ in
826826+ let test_point_compressed_serialization () =
827827+ let _priv, pub = Dsa.generate () in
828828+ let pub_bytes = Dsa.pub_to_octets pub in
829829+ match Point.of_octets pub_bytes with
830830+ | Ok point ->
831831+ let compressed = Point.to_octets ~compress:true point in
832832+ (* Compressed form should be shorter *)
833833+ Alcotest.(check bool) "compressed is shorter"
834834+ true (String.length compressed < String.length pub_bytes);
835835+ (* Should be able to decode compressed form *)
836836+ (match Point.of_octets compressed with
837837+ | Ok point' ->
838838+ let uncompressed = Point.to_octets point' in
839839+ Alcotest.(check string) "compressed roundtrip" pub_bytes uncompressed
840840+ | Error e -> Alcotest.failf "compressed of_octets failed: %a" pp_error e)
841841+ | Error e -> Alcotest.failf "of_octets failed: %a" pp_error e
842842+ in
843843+ let test_scalar_serialization_roundtrip () =
844844+ (* Generate a key and check scalar roundtrip *)
845845+ let secret, _pub = Dh.gen_key () in
846846+ let secret_bytes = Dh.secret_to_octets secret in
847847+ match Point.scalar_of_octets secret_bytes with
848848+ | Ok scalar ->
849849+ let scalar_bytes = Point.scalar_to_octets scalar in
850850+ Alcotest.(check string) "scalar roundtrip" secret_bytes scalar_bytes
851851+ | Error e -> Alcotest.failf "scalar_of_octets failed: %a" pp_error e
852852+ in
853853+ let test_scalar_mult_with_generator () =
854854+ (* scalar_mult with generator should give the same result as pub_of_priv *)
855855+ let priv, pub = Dsa.generate () in
856856+ let priv_bytes = Dsa.priv_to_octets priv in
857857+ let pub_bytes = Dsa.pub_to_octets pub in
858858+ match Point.scalar_of_octets priv_bytes with
859859+ | Ok scalar ->
860860+ let computed_pub = Point.scalar_mult scalar Point.generator in
861861+ let computed_bytes = Point.to_octets computed_pub in
862862+ Alcotest.(check string) "scalar_mult generator" pub_bytes computed_bytes
863863+ | Error e -> Alcotest.failf "scalar_of_octets failed: %a" pp_error e
864864+ in
865865+ let test_point_add () =
866866+ (* Test that P + P = 2P (scalar_mult 2 P) *)
867867+ let g = Point.generator in
868868+ let g_plus_g = Point.add g g in
869869+ (* scalar 2 in big-endian encoding *)
870870+ let two =
871871+ let buf = Bytes.make Dsa.byte_length '\000' in
872872+ Bytes.set_uint8 buf (Dsa.byte_length - 1) 2;
873873+ Bytes.to_string buf
874874+ in
875875+ match Point.scalar_of_octets two with
876876+ | Ok scalar_2 ->
877877+ let two_g = Point.scalar_mult scalar_2 g in
878878+ Alcotest.(check string) "G + G = 2G"
879879+ (Point.to_octets g_plus_g) (Point.to_octets two_g)
880880+ | Error e -> Alcotest.failf "scalar_of_octets 2 failed: %a" pp_error e
881881+ in
882882+ [
883883+ name ^ " Point generator", `Quick, test_generator_not_identity;
884884+ name ^ " Point serialization roundtrip", `Quick, test_point_serialization_roundtrip;
885885+ name ^ " Point compressed serialization", `Quick, test_point_compressed_serialization;
886886+ name ^ " Scalar serialization roundtrip", `Quick, test_scalar_serialization_roundtrip;
887887+ name ^ " scalar_mult with generator", `Quick, test_scalar_mult_with_generator;
888888+ name ^ " Point add", `Quick, test_point_add;
889889+ ]
890890+806891let p521_regression () =
807892 let key = of_hex
808893"04 01 e4 f8 8a 40 3d fe 2f 65 a0 20 50 01 9b 87
···853938 ("X25519", [ "RFC 7748", `Quick, x25519 ]);
854939 ("ED25519", ed25519);
855940 ("ECDSA P521 regression", [ "regreesion1", `Quick, p521_regression ]);
941941+ ("P256 Point module", point_module_tests (module P256) "P256");
942942+ ("P384 Point module", point_module_tests (module P384) "P384");
943943+ ("P521 Point module", point_module_tests (module P521) "P521");
856944 ]