Select the types of activity you want to include in your feed.
refactor(crypto): merge ec_wycheproof into wycheproof library
Fold EC test vector logic into the wycheproof library, removing the separate ec_wycheproof library. Reference changes from Ec_wycheproof.suite to Wycheproof.suite.
···385385 match Jsont_bytesrw.encode_string Jsont.json json with
386386 | Error _ -> failwith "eddsa_test_group_exn: encode failed"
387387 | Ok s -> Jsont_bytesrw.decode_string eddsa_test_group_jsont s |> result
388388+389389+(* -- EC test vectors ---------------------------------------------------- *)
390390+391391+open Crypto_ec
392392+open Result.Syntax
393393+394394+let hex = Alcotest.testable pp_hex equal_hex
395395+396396+module Asn = struct
397397+ let parse_point curve s =
398398+ let seq2 a b = Asn.S.(sequence2 (required a) (required b)) in
399399+ let term = Asn.S.(seq2 (seq2 oid oid) bit_string_octets) in
400400+ let ec_public_key = Asn.OID.(base 1 2 <|| [ 840; 10045; 2; 1 ]) in
401401+ let prime_oid =
402402+ match curve with
403403+ | "secp256r1" -> Asn.OID.(base 1 2 <|| [ 840; 10045; 3; 1; 7 ])
404404+ | "secp384r1" -> Asn.OID.(base 1 3 <|| [ 132; 0; 34 ])
405405+ | "secp521r1" -> Asn.OID.(base 1 3 <|| [ 132; 0; 35 ])
406406+ | _ -> assert false
407407+ in
408408+ match Asn.decode (Asn.codec Asn.ber term) s with
409409+ | Error _ -> Error "ASN1 parse error"
410410+ | Ok (((oid1, oid2), data), rest) ->
411411+ if String.length rest <> 0 then Error "ASN1 leftover"
412412+ else if not (Asn.OID.equal oid1 ec_public_key) then
413413+ Error "ASN1: wrong oid 1"
414414+ else if not (Asn.OID.equal oid2 prime_oid) then
415415+ Error "ASN1: wrong oid 2"
416416+ else Ok data
417417+418418+ let parse_signature cs =
419419+ let asn =
420420+ Asn.S.(sequence2 (required unsigned_integer) (required unsigned_integer))
421421+ in
422422+ match Asn.(decode (codec der asn) cs) with
423423+ | Error _ -> Error "ASN1 parse error"
424424+ | Ok (r_s, rest) ->
425425+ if String.length rest <> 0 then Error "ASN1 leftover" else Ok r_s
426426+end
427427+428428+let to_string_result ~pp_error = function
429429+ | Ok _ as ok -> ok
430430+ | Error e ->
431431+ let msg = Format.asprintf "%a" pp_error e in
432432+ Error msg
433433+434434+let pad ~total_len buf =
435435+ match total_len - String.length buf with
436436+ | 0 -> Ok buf
437437+ | n when n < 0 ->
438438+ let is_zero = ref true in
439439+ for i = 0 to abs n - 1 do
440440+ if Bytes.(get_uint8 (Bytes.unsafe_of_string buf) i) <> 0 then
441441+ is_zero := false
442442+ done;
443443+ if !is_zero then Ok (String.sub buf (abs n) total_len)
444444+ else Error "input is too long"
445445+ | pad_len -> Ok (String.make pad_len '\000' ^ buf)
446446+447447+let len = function
448448+ | "secp256r1" -> 32
449449+ | "secp384r1" -> 48
450450+ | "secp521r1" -> 66
451451+ | _ -> assert false
452452+453453+let parse_secret curve s =
454454+ let total_len = len curve in
455455+ pad ~total_len s
456456+457457+type test = { public_key : string; raw_private_key : string; expected : string }
458458+459459+let perform_key_exchange curve ~public_key ~raw_private_key =
460460+ to_string_result ~pp_error
461461+ (match curve with
462462+ | "secp256r1" -> begin
463463+ match P256.Dh.secret_of_octets raw_private_key with
464464+ | Ok (p, _) -> P256.Dh.key_exchange p public_key
465465+ | Error _ -> assert false
466466+ end
467467+ | "secp384r1" -> begin
468468+ match P384.Dh.secret_of_octets raw_private_key with
469469+ | Ok (p, _) -> P384.Dh.key_exchange p public_key
470470+ | Error _ -> assert false
471471+ end
472472+ | "secp521r1" -> begin
473473+ match P521.Dh.secret_of_octets raw_private_key with
474474+ | Ok (p, _) -> P521.Dh.key_exchange p public_key
475475+ | Error _ -> assert false
476476+ end
477477+ | _ -> assert false)
478478+479479+let interpret_test ~tc_id curve { public_key; raw_private_key; expected } () =
480480+ match perform_key_exchange curve ~public_key ~raw_private_key with
481481+ | Ok got -> Alcotest.check hex __LOC__ expected got
482482+ | Error err ->
483483+ Printf.ksprintf
484484+ (fun s -> Alcotest.fail s)
485485+ "While parsing %d: %s" tc_id err
486486+487487+type invalid_test = { public : string; private_ : string }
488488+489489+let is_ok = function Ok _ -> true | Error _ -> false
490490+491491+let interpret_invalid_test curve { public; private_ } () =
492492+ let result =
493493+ let* public_key = Asn.parse_point curve public in
494494+ let* raw_private_key = parse_secret curve private_ in
495495+ perform_key_exchange curve ~public_key ~raw_private_key
496496+ in
497497+ Alcotest.check Alcotest.bool __LOC__ false (is_ok result)
498498+499499+type strategy = Test of test | Invalid_test of invalid_test | Skip
500500+501501+let ecdh_test curve (test : ecdh_test) =
502502+ let ignored_flags = [ "UnnamedCurve" ] in
503503+ let curve_compression_test curve =
504504+ let curves = [ "secp256r1"; "secp384r1"; "secp521r1" ] in
505505+ test.tc_id = 2 && List.exists (fun x -> String.equal x curve) curves
506506+ in
507507+ match test.result with
508508+ | _ when has_ignored_flag test ~ignored_flags -> Ok Skip
509509+ | Invalid ->
510510+ Ok (Invalid_test { public = test.public; private_ = test.private_ })
511511+ | Acceptable when curve_compression_test curve ->
512512+ let* public_key = Asn.parse_point curve test.public in
513513+ let* raw_private_key = parse_secret curve test.private_ in
514514+ Ok (Test { public_key; raw_private_key; expected = test.shared })
515515+ | Acceptable -> Ok Skip
516516+ | Valid ->
517517+ let* public_key = Asn.parse_point curve test.public in
518518+ let* raw_private_key = parse_secret curve test.private_ in
519519+ Ok (Test { public_key; raw_private_key; expected = test.shared })
520520+521521+let to_ecdh_tests curve (x : ecdh_test) =
522522+ let name = Printf.sprintf "%d - %s" x.tc_id x.comment in
523523+ match ecdh_test curve x with
524524+ | Ok (Test t) -> [ (name, `Quick, interpret_test ~tc_id:x.tc_id curve t) ]
525525+ | Ok (Invalid_test t) -> [ (name, `Quick, interpret_invalid_test curve t) ]
526526+ | Ok Skip -> []
527527+ | Error e -> Printf.ksprintf failwith "While parsing %d: %s" x.tc_id e
528528+529529+let ecdh_tests file =
530530+ let data = load_file_exn file in
531531+ let groups : ecdh_test_group list =
532532+ List.map ecdh_test_group_exn data.test_groups
533533+ in
534534+ List.concat_map
535535+ (fun (group : ecdh_test_group) ->
536536+ List.concat_map (to_ecdh_tests group.curve) group.tests)
537537+ groups
538538+539539+let ecdsa_test curve key hash (tst : dsa_test) =
540540+ let name = Printf.sprintf "%d - %s" tst.tc_id tst.comment in
541541+ let size = len curve in
542542+ let msg =
543543+ let dgst =
544544+ match hash with
545545+ | "SHA-256" -> Digestif.SHA256.(digest_string tst.msg |> to_raw_string)
546546+ | "SHA-384" -> Digestif.SHA384.(digest_string tst.msg |> to_raw_string)
547547+ | "SHA-512" -> Digestif.SHA512.(digest_string tst.msg |> to_raw_string)
548548+ | "SHA-224" -> Digestif.SHA224.(digest_string tst.msg |> to_raw_string)
549549+ | _ -> assert false
550550+ in
551551+ String.sub dgst 0 (min size (String.length dgst))
552552+ in
553553+ let verified (r, s) =
554554+ match curve with
555555+ | "secp256r1" -> begin
556556+ match P256.Dsa.pub_of_octets key with
557557+ | Ok key -> P256.Dsa.verify ~key (r, s) msg
558558+ | Error _ -> assert false
559559+ end
560560+ | "secp384r1" -> begin
561561+ match P384.Dsa.pub_of_octets key with
562562+ | Ok key -> P384.Dsa.verify ~key (r, s) msg
563563+ | Error _ -> assert false
564564+ end
565565+ | "secp521r1" -> begin
566566+ match P521.Dsa.pub_of_octets key with
567567+ | Ok key -> P521.Dsa.verify ~key (r, s) msg
568568+ | Error _ -> assert false
569569+ end
570570+ | _ -> assert false
571571+ in
572572+ match tst.result with
573573+ | Acceptable | Invalid ->
574574+ let f () =
575575+ match Asn.parse_signature tst.sig_ with
576576+ | Ok (r, s) -> Alcotest.(check bool __LOC__ false (verified (r, s)))
577577+ | Error _s -> ()
578578+ in
579579+ (name, `Quick, f)
580580+ | Valid ->
581581+ let f () =
582582+ match Asn.parse_signature tst.sig_ with
583583+ | Ok (r, s) -> Alcotest.(check bool __LOC__ true (verified (r, s)))
584584+ | Error s -> Alcotest.fail s
585585+ in
586586+ (name, `Quick, f)
587587+588588+let to_ecdsa_tests (x : ecdsa_test_group) =
589589+ List.map (ecdsa_test x.key.curve x.key.uncompressed x.sha) x.tests
590590+591591+let ecdsa_tests file =
592592+ let data = load_file_exn file in
593593+ let groups : ecdsa_test_group list =
594594+ List.map ecdsa_test_group_exn data.test_groups
595595+ in
596596+ List.concat_map to_ecdsa_tests groups
597597+598598+let to_x25519_test (x : ecdh_test) =
599599+ let name = Printf.sprintf "%d - %s" x.tc_id x.comment
600600+ and priv =
601601+ match X25519.secret_of_octets x.private_ with
602602+ | Ok (p, _) -> p
603603+ | Error _ -> assert false
604604+ in
605605+ match x.result with
606606+ | Acceptable ->
607607+ let f () =
608608+ match
609609+ ( X25519.key_exchange priv x.public,
610610+ has_ignored_flag x ~ignored_flags:[ "LowOrderPublic" ] )
611611+ with
612612+ | Ok _, true -> Alcotest.fail "acceptable should have errored"
613613+ | Ok r, false ->
614614+ Alcotest.(check bool __LOC__ true (String.equal r x.shared))
615615+ | Error _, true -> ()
616616+ | Error e, false -> Alcotest.failf "acceptable errored %a" pp_error e
617617+ in
618618+ (name, `Quick, f)
619619+ | Invalid ->
620620+ let f () =
621621+ match X25519.key_exchange priv x.public with
622622+ | Ok r -> Alcotest.(check bool __LOC__ false (String.equal r x.shared))
623623+ | Error e -> Alcotest.failf "invalid errored %a" pp_error e
624624+ in
625625+ (name, `Quick, f)
626626+ | Valid ->
627627+ let f () =
628628+ match X25519.key_exchange priv x.public with
629629+ | Ok r -> Alcotest.(check bool __LOC__ true (String.equal r x.shared))
630630+ | Error e -> Alcotest.failf "valid errored %a" pp_error e
631631+ in
632632+ (name, `Quick, f)
633633+634634+let x25519_tests =
635635+ let data = load_file_exn "x25519_test.json" in
636636+ let groups : ecdh_test_group list =
637637+ List.map ecdh_test_group_exn data.test_groups
638638+ in
639639+ List.concat_map
640640+ (fun (group : ecdh_test_group) -> List.map to_x25519_test group.tests)
641641+ groups
642642+643643+let to_ed25519_test (priv, pub) (x : dsa_test) =
644644+ let name = Printf.sprintf "%d - %s" x.tc_id x.comment in
645645+ match x.result with
646646+ | Invalid ->
647647+ let f () =
648648+ Alcotest.(
649649+ check bool __LOC__ false (Ed25519.verify ~key:pub x.sig_ ~msg:x.msg));
650650+ let s = Ed25519.sign ~key:priv x.msg in
651651+ Alcotest.(check bool __LOC__ false (String.equal s x.sig_))
652652+ in
653653+ (name, `Quick, f)
654654+ | Valid ->
655655+ let f () =
656656+ Alcotest.(
657657+ check bool __LOC__ true (Ed25519.verify ~key:pub x.sig_ ~msg:x.msg));
658658+ let s = Ed25519.sign ~key:priv x.msg in
659659+ Alcotest.(check bool __LOC__ true (String.equal s x.sig_))
660660+ in
661661+ (name, `Quick, f)
662662+ | Acceptable -> assert false
663663+664664+let to_ed25519_keys (key : eddsa_key) =
665665+ match (Ed25519.priv_of_octets key.sk, Ed25519.pub_of_octets key.pk) with
666666+ | Ok priv, Ok pub ->
667667+ assert (String.equal Ed25519.(pub_to_octets (pub_of_priv priv)) key.pk);
668668+ (priv, pub)
669669+ | _ -> assert false
670670+671671+let ed25519_tests =
672672+ let data = load_file_exn "eddsa_test.json" in
673673+ let groups : eddsa_test_group list =
674674+ List.map eddsa_test_group_exn data.test_groups
675675+ in
676676+ List.concat_map
677677+ (fun (group : eddsa_test_group) ->
678678+ let keys = to_ed25519_keys group.key in
679679+ List.map (to_ed25519_test keys) group.tests)
680680+ groups
681681+682682+let suite =
683683+ [
684684+ ("ECDH P256 test vectors", ecdh_tests "ecdh_secp256r1_test.json");
685685+ ( "ECDSA P256 test vectors (SHA256)",
686686+ ecdsa_tests "ecdsa_secp256r1_sha256_test.json" );
687687+ ( "ECDSA P256 test vectors (SHA512)",
688688+ ecdsa_tests "ecdsa_secp256r1_sha512_test.json" );
689689+ ("ECDH P384 test vectors", ecdh_tests "ecdh_secp384r1_test.json");
690690+ ( "ECDSA P384 test vectors (SHA384)",
691691+ ecdsa_tests "ecdsa_secp384r1_sha384_test.json" );
692692+ ( "ECDSA P384 test vectors (SHA512)",
693693+ ecdsa_tests "ecdsa_secp384r1_sha512_test.json" );
694694+ ("ECDH P521 test vectors", ecdh_tests "ecdh_secp521r1_test.json");
695695+ ( "ECDSA P521 test vectors (SHA512)",
696696+ ecdsa_tests "ecdsa_secp521r1_sha512_test.json" );
697697+ ("X25519 test vectors", x25519_tests);
698698+ ("ED25519 test vectors", ed25519_tests);
699699+ ]
+3
test/ec/wycheproof/wycheproof.mli
···162162163163val eddsa_test_group_exn : json -> eddsa_test_group
164164(** [eddsa_test_group_exn json] extracts an EdDSA test group from [json]. *)
165165+166166+val suite : (string * unit Alcotest.test_case list) list
167167+(** Wycheproof EC test vector suites. *)