open Mirage_crypto_ec open Test_common module Testable = struct let ok_or_error = Alcotest.result Alcotest.unit (Alcotest.testable pp_error ( = )) end let pp_hex_le fmt buf = let n = String.length buf in let bbuf = Bytes.unsafe_of_string buf in for i = n - 1 downto 0 do let byte = Bytes.get_uint8 bbuf i in Format.fprintf fmt "%02x" byte done let pp_result ppf = function | Ok cs -> pp_hex_le ppf cs | Error e -> Format.fprintf ppf "%a" pp_error e let key_exchange = let test ~name d p ~expected = ( name, `Quick, fun () -> P256.Dh.key_exchange d p |> Format.asprintf "%a" pp_result |> Alcotest.check Alcotest.string __LOC__ expected ) in let kp data = match P256.Dh.secret_of_octets data with | Ok (p, s) -> p, s | Error _ -> assert false in let d_a, p_a = kp (of_hex "200102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f") and d_b, p_b = kp (of_hex "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f") in [ test ~name:"b*A" d_b p_a ~expected: "2e3e4065a62a7f425aaf8aae3d158f367c733300b5002e0b62f4bc6260789e1b"; test ~name:"a*B" d_a p_b ~expected: "2e3e4065a62a7f425aaf8aae3d158f367c733300b5002e0b62f4bc6260789e1b"; test ~name:"a*A" d_a p_a ~expected: "2ea4e810837da217a5bfd05f01d12459eeda830b6e0dec7f8afa425c5b55c507"; test ~name:"b*B" d_b p_b ~expected: "a7666bcc3818472194460f7df22d80a5886da0e1679eac930175ce1ff733c7ca"; ] let scalar_mult = let test ~n ~scalar ~point ~expected = let scalar = match P256.Dh.secret_of_octets scalar with | Ok (p, _) -> p | Error _ -> assert false in ( Printf.sprintf "Scalar mult (#%d)" n, `Quick, fun () -> P256.Dh.key_exchange scalar point |> Format.asprintf "%a" pp_result |> Alcotest.check Alcotest.string __LOC__ expected ) in let point = of_hex "046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2964FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5" in [ test ~n:0 ~scalar:(of_hex "0000000000000000000000000000000000000000000000000000000000000001") ~point ~expected:"96c298d84539a1f4a033eb2d817d0377f240a463e5e6bcf847422ce1f2d1176b"; test ~n:1 ~scalar:(of_hex "0000000000000000000000000000000000000000000000000000000000000002") ~point ~expected:"78996647fc480ba6351bf277e26989c0c31ab5040338528a7e4f038d187bf27c"; test ~n:2 ~scalar:(of_hex "0000000000000000000000000000000000000000000000000000000000000004") ~point ~expected:"5208036b44029350ef965578dbe21f03d02be69e65de2da0bb8fd032354a53e2"; test ~n:3 ~scalar:(of_hex "0612465c89a023ab17855b0a6bcebfd3febb53aef84138647b5352e02c10c346") ~point:(of_hex "0462d5bd3372af75fe85a040715d0f502428e07046868b0bfdfa61d731afe44f26ac333a93a9e70a81cd5a95b5bf8d13990eb741c8c38872b4a07d275a014e30cf") ~expected:"854271e19508bc935ab22b95cd2be13a0e78265f528b658b3219028b900d0253"; test ~n:4 ~scalar:(of_hex "0a0d622a47e48f6bc1038ace438c6f528aa00ad2bd1da5f13ee46bf5f633d71a") ~point:(of_hex "043cbc1b31b43f17dc200dd70c2944c04c6cb1b082820c234a300b05b7763844c74fde0a4ef93887469793270eb2ff148287da9265b0334f9e2609aac16e8ad503") ~expected:"ffffffffffffffffffffffffffffffff3022cfeeffffffffffffffffffffff7f"; test ~n:5 ~scalar:(of_hex "55d55f11bb8da1ea318bca7266f0376662441ea87270aa2077f1b770c4854a48") ~point:(of_hex "04000000000000000000000000000000000000000000000000000000000000000066485c780e2f83d72433bd5d84a06bb6541c2af31dae871728bf856a174f93f4") ~expected:"48e82c9b82c88cb9fc2a5cff9e7c41bc4255ff6bd3814538c9b130877c07e4cf"; ] let to_ok_or_error = function Ok _ -> Ok () | Error _ as e -> e let point_validation = let test ~name ~x ~y ~expected = let scalar = match P256.Dh.secret_of_octets (of_hex "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f") with | Ok (p, _) -> p | _ -> assert false in let point = let s04 = String.make 1 '\004' in s04 ^ x ^ y in ( name, `Quick, fun () -> P256.Dh.key_exchange scalar point |> to_ok_or_error |> Alcotest.check Testable.ok_or_error __LOC__ expected ) in let zero = String.make 32 '\000' in let sb = of_hex "66485c780e2f83d72433bd5d84a06bb6541c2af31dae871728bf856a174f93f4" in [ test ~name:"Ok" ~x:(of_hex "62d5bd3372af75fe85a040715d0f502428e07046868b0bfdfa61d731afe44f26") ~y:(of_hex "ac333a93a9e70a81cd5a95b5bf8d13990eb741c8c38872b4a07d275a014e30cf") ~expected:(Ok ()); test ~name:"P=0" ~x:(of_hex "0000000000000000000000000000000000000000000000000000000000000000") ~y:(of_hex "0000000000000000000000000000000000000000000000000000000000000000") ~expected:(Error `Not_on_curve); test ~name:"(0, sqrt(b))" ~x:zero ~y:sb ~expected:(Ok ()); test ~name:"out of range" ~x:(of_hex "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF") ~y:sb ~expected:(Error `Invalid_range); ] let scalar_validation = let ign_sec hex = match P256.Dh.secret_of_octets (of_hex hex) with | Ok _ -> Ok () | Error _ as e -> e in [ ("0", `Quick, fun () -> Alcotest.check Testable.ok_or_error __LOC__ (Error `Invalid_range) (ign_sec "0000000000000000000000000000000000000000000000000000000000000000")) ; ("1", `Quick, fun () -> Alcotest.check Testable.ok_or_error __LOC__ (Ok ()) (ign_sec "0000000000000000000000000000000000000000000000000000000000000001")) ; ("n-1", `Quick, fun () -> Alcotest.check Testable.ok_or_error __LOC__ (Ok ()) (ign_sec "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632550")) ; ("n", `Quick, fun () -> Alcotest.check Testable.ok_or_error __LOC__ (Error `Invalid_range) (ign_sec "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551")) ; ] let ecdsa_gen () = let d = of_hex "C477F9F6 5C22CCE2 0657FAA5 B2D1D812 2336F851 A508A1ED 04E479C3 4985BF96" in let p = match P256.Dsa.pub_of_octets (of_hex {|04 B7E08AFD FE94BAD3 F1DC8C73 4798BA1C 62B3A0AD 1E9EA2A3 8201CD08 89BC7A19 3603F747 959DBF7A 4BB226E4 19287290 63ADC7AE 43529E61 B563BBC6 06CC5E09|}) with | Ok a -> a | Error _ -> assert false in let pub = match P256.Dsa.priv_of_octets d with | Ok p -> P256.Dsa.pub_of_priv p | Error _ -> Alcotest.fail "couldn't decode private key" in let pub_eq a b = String.equal (P256.Dsa.pub_to_octets a) (P256.Dsa.pub_to_octets b) in Alcotest.(check bool __LOC__ true (pub_eq pub p)) let ecdsa_sign () = let d = of_hex "C477F9F6 5C22CCE2 0657FAA5 B2D1D812 2336F851 A508A1ED 04E479C3 4985BF96" and k = of_hex "7A1A7E52 797FC8CA AA435D2A 4DACE391 58504BF2 04FBE19F 14DBB427 FAEE50AE" and e = of_hex "A41A41A1 2A799548 211C410C 65D8133A FDE34D28 BDD542E4 B680CF28 99C8A8C4" in let r = of_hex "2B42F576 D07F4165 FF65D1F3 B1500F81 E44C316F 1F0B3EF5 7325B69A CA46104F" and s = of_hex "DC42C212 2D6392CD 3E3A993A 89502A81 98C1886F E69D262C 4B329BDB 6B63FAF1" in let key = match P256.Dsa.priv_of_octets d with | Ok p -> p | Error _ -> Alcotest.fail "couldn't decode private key" in let (r', s') = P256.Dsa.sign ~key ~k e in Alcotest.(check bool __LOC__ true (String.equal r r' && String.equal s s')) let ecdsa_verify () = let key = match P256.Dsa.pub_of_octets (of_hex {|04 B7E08AFD FE94BAD3 F1DC8C73 4798BA1C 62B3A0AD 1E9EA2A3 8201CD08 89BC7A19 3603F747 959DBF7A 4BB226E4 19287290 63ADC7AE 43529E61 B563BBC6 06CC5E09|}) with | Ok a -> a | Error _ -> assert false and e = of_hex "A41A41A1 2A799548 211C410C 65D8133A FDE34D28 BDD542E4 B680CF28 99C8A8C4" and r = of_hex "2B42F576 D07F4165 FF65D1F3 B1500F81 E44C316F 1F0B3EF5 7325B69A CA46104F" and s = of_hex "DC42C212 2D6392CD 3E3A993A 89502A81 98C1886F E69D262C 4B329BDB 6B63FAF1" in Alcotest.(check bool __LOC__ true (P256.Dsa.verify ~key (r, s) e)) let ecdsa = [ (* from https://csrc.nist.rip/groups/ST/toolkit/documents/Examples/ECDSA_Prime.pdf *) "ECDSA gen", `Quick, ecdsa_gen ; "ECDSA sign", `Quick, ecdsa_sign ; "ECDSA verify", `Quick, ecdsa_verify ; ] let pub_key_compression (module Dsa:Mirage_crypto_ec.Dsa) () = for _ = 1 to 20 do let _, pub = Dsa.generate () in let compressed = Dsa.pub_to_octets ~compress:true pub in let decompressed = Dsa.pub_of_octets compressed in match decompressed with | Ok decompressed -> let p1 = Dsa.pub_to_octets pub in let p2 = Dsa.pub_to_octets decompressed in Alcotest.(check string __LOC__ p1 p2); let prefix = String.get_uint8 compressed 0 in let expected = 2 + String.(get_uint8 p1 (length p1 - 1)) land 1 in Alcotest.(check int __LOC__ expected prefix); | Error e -> Alcotest.failf "%a" pp_error e done let ecdsa_rfc6979_p256 = (* A.2.5 - P 256 *) let priv, pub = let data = of_hex "C9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721" in match P256.Dsa.priv_of_octets data with | Ok p -> p, P256.Dsa.pub_of_priv p | Error _ -> assert false in let pub_rfc () = let fst = String.make 1 '\004' in let ux = of_hex "60FED4BA255A9D31C961EB74C6356D68C049B8923B61FA6CE669622E60F29FB6" and uy = of_hex "7903FE1008B8BC99A41AE9E95628BC64F2F1B20C2D7E9F5177A3C294D4462299" in match P256.Dsa.pub_of_octets (fst ^ ux ^ uy) with | Ok p -> let pub_eq = String.equal (P256.Dsa.pub_to_octets pub) (P256.Dsa.pub_to_octets p) in Alcotest.(check bool __LOC__ true pub_eq) | Error _ -> Alcotest.fail "bad public key" in let case (type a) (hash : a Digestif.hash) ~message ~k ~r ~s () = let msg = let h = Digestif.(digest_string hash message |> to_raw_string hash) in String.sub h 0 (min (String.length h) 32) and k = of_hex k in let k' = let module H = (val Digestif.module_of hash) in let module K = P256.Dsa.K_gen (H) in K.generate ~key:priv msg in Alcotest.(check bool __LOC__ true (String.equal k k')); let sig_eq (r', s') = String.equal (of_hex r) r' && String.equal (of_hex s) s' in let sig' = P256.Dsa.sign ~key:priv ~k msg in Alcotest.(check bool __LOC__ true (sig_eq sig')) in let cases = [ case Digestif.sha1 ~message:"sample" ~k:"882905F1227FD620FBF2ABF21244F0BA83D0DC3A9103DBBEE43A1FB858109DB4" ~r:"61340C88C3AAEBEB4F6D667F672CA9759A6CCAA9FA8811313039EE4A35471D32" ~s:"6D7F147DAC089441BB2E2FE8F7A3FA264B9C475098FDCF6E00D7C996E1B8B7EB" ; case Digestif.sha224 ~message:"sample" ~k:"103F90EE9DC52E5E7FB5132B7033C63066D194321491862059967C715985D473" ~r:"53B2FFF5D1752B2C689DF257C04C40A587FABABB3F6FC2702F1343AF7CA9AA3F" ~s:"B9AFB64FDC03DC1A131C7D2386D11E349F070AA432A4ACC918BEA988BF75C74C" ; case Digestif.sha256 ~message:"sample" ~k:"A6E3C57DD01ABE90086538398355DD4C3B17AA873382B0F24D6129493D8AAD60" ~r:"EFD48B2AACB6A8FD1140DD9CD45E81D69D2C877B56AAF991C34D0EA84EAF3716" ~s:"F7CB1C942D657C41D436C7A1B6E29F65F3E900DBB9AFF4064DC4AB2F843ACDA8" ; case Digestif.sha384 ~message:"sample" ~k:"09F634B188CEFD98E7EC88B1AA9852D734D0BC272F7D2A47DECC6EBEB375AAD4" ~r:"0EAFEA039B20E9B42309FB1D89E213057CBF973DC0CFC8F129EDDDC800EF7719" ~s:"4861F0491E6998B9455193E34E7B0D284DDD7149A74B95B9261F13ABDE940954" ; case Digestif.sha512 ~message:"sample" ~k:"5FA81C63109BADB88C1F367B47DA606DA28CAD69AA22C4FE6AD7DF73A7173AA5" ~r:"8496A60B5E9B47C825488827E0495B0E3FA109EC4568FD3F8D1097678EB97F00" ~s:"2362AB1ADBE2B8ADF9CB9EDAB740EA6049C028114F2460F96554F61FAE3302FE" ; case Digestif.sha1 ~message:"test" ~k:"8C9520267C55D6B980DF741E56B4ADEE114D84FBFA2E62137954164028632A2E" ~r:"0CBCC86FD6ABD1D99E703E1EC50069EE5C0B4BA4B9AC60E409E8EC5910D81A89" ~s:"01B9D7B73DFAA60D5651EC4591A0136F87653E0FD780C3B1BC872FFDEAE479B1" ; case Digestif.sha224 ~message:"test" ~k:"669F4426F2688B8BE0DB3A6BD1989BDAEFFF84B649EEB84F3DD26080F667FAA7" ~r:"C37EDB6F0AE79D47C3C27E962FA269BB4F441770357E114EE511F662EC34A692" ~s:"C820053A05791E521FCAAD6042D40AEA1D6B1A540138558F47D0719800E18F2D" ; case Digestif.sha256 ~message:"test" ~k:"D16B6AE827F17175E040871A1C7EC3500192C4C92677336EC2537ACAEE0008E0" ~r:"F1ABB023518351CD71D881567B1EA663ED3EFCF6C5132B354F28D3B0B7D38367" ~s:"019F4113742A2B14BD25926B49C649155F267E60D3814B4C0CC84250E46F0083" ; case Digestif.sha384 ~message:"test" ~k:"16AEFFA357260B04B1DD199693960740066C1A8F3E8EDD79070AA914D361B3B8" ~r:"83910E8B48BB0C74244EBDF7F07A1C5413D61472BD941EF3920E623FBCCEBEB6" ~s:"8DDBEC54CF8CD5874883841D712142A56A8D0F218F5003CB0296B6B509619F2C" ; case Digestif.sha512 ~message:"test" ~k:"6915D11632ACA3C40D5D51C08DAF9C555933819548784480E93499000D9F0B7F" ~r:"461D93F31B6540894788FD206C07CFA0CC35F46FA3C91816FFF1040AD1581A04" ~s:"39AF9F15DE0DB8D97E72719C74820D304CE5226E32DEDAE67519E840D1194E55" ; ] in ("public key matches", `Quick, pub_rfc) :: ("public key compression and decompression", `Quick, (pub_key_compression (module P256.Dsa))) :: List.mapi (fun i c -> "RFC 6979 A.2.5 " ^ string_of_int i, `Quick, c) cases let ecdsa_rfc6979_p384 = (* A.2.6 - P 384 *) let priv, pub = let data = of_hex "6B9D3DAD2E1B8C1C05B19875B6659F4DE23C3B667BF297BA9AA47740787137D896D5724E4C70A825F872C9EA60D2EDF5" in match P384.Dsa.priv_of_octets data with | Ok p -> p, P384.Dsa.pub_of_priv p | Error _ -> assert false in let pub_rfc () = let fst = String.make 1 '\004' in let ux = of_hex "EC3A4E415B4E19A4568618029F427FA5DA9A8BC4AE92E02E06AAE5286B300C64DEF8F0EA9055866064A254515480BC13" and uy = of_hex "8015D9B72D7D57244EA8EF9AC0C621896708A59367F9DFB9F54CA84B3F1C9DB1288B231C3AE0D4FE7344FD2533264720" in match P384.Dsa.pub_of_octets (fst ^ ux ^ uy) with | Ok p -> let pub_eq = String.equal (P384.Dsa.pub_to_octets pub) (P384.Dsa.pub_to_octets p) in Alcotest.(check bool __LOC__ true pub_eq) | Error _ -> Alcotest.fail "bad public key" in let case (type a) (hash : a Digestif.hash) ~message ~k ~r ~s () = let msg = let h = Digestif.(digest_string hash message |> to_raw_string hash) in String.sub h 0 (min (String.length h) 48) and k = of_hex k in let k' = let module H = (val Digestif.module_of hash) in let module K = P384.Dsa.K_gen (H) in K.generate ~key:priv msg in Alcotest.(check bool __LOC__ true (String.equal k k')); let sig_eq (r', s') = String.equal (of_hex r) r' && String.equal (of_hex s) s' in let sig' = P384.Dsa.sign ~key:priv ~k msg in Alcotest.(check bool __LOC__ true (sig_eq sig')) in let cases = [ case Digestif.sha1 ~message:"sample" ~k:"4471EF7518BB2C7C20F62EAE1C387AD0C5E8E470995DB4ACF694466E6AB09663 0F29E5938D25106C3C340045A2DB01A7" ~r:"EC748D839243D6FBEF4FC5C4859A7DFFD7F3ABDDF72014540C16D73309834FA3 7B9BA002899F6FDA3A4A9386790D4EB2" ~s:"A3BCFA947BEEF4732BF247AC17F71676CB31A847B9FF0CBC9C9ED4C1A5B3FACF 26F49CA031D4857570CCB5CA4424A443"; case Digestif.sha224 ~message:"sample" ~k:"A4E4D2F0E729EB786B31FC20AD5D849E304450E0AE8E3E341134A5C1AFA03CAB 8083EE4E3C45B06A5899EA56C51B5879" ~r:"42356E76B55A6D9B4631C865445DBE54E056D3B3431766D0509244793C3F9366 450F76EE3DE43F5A125333A6BE060122" ~s:"9DA0C81787064021E78DF658F2FBB0B042BF304665DB721F077A4298B095E483 4C082C03D83028EFBF93A3C23940CA8D"; case Digestif.sha256 ~message:"sample" ~k:"180AE9F9AEC5438A44BC159A1FCB277C7BE54FA20E7CF404B490650A8ACC414E 375572342863C899F9F2EDF9747A9B60" ~r:"21B13D1E013C7FA1392D03C5F99AF8B30C570C6F98D4EA8E354B63A21D3DAA33 BDE1E888E63355D92FA2B3C36D8FB2CD" ~s:"F3AA443FB107745BF4BD77CB3891674632068A10CA67E3D45DB2266FA7D1FEEB EFDC63ECCD1AC42EC0CB8668A4FA0AB0"; case Digestif.sha384 ~message:"sample" ~k:"94ED910D1A099DAD3254E9242AE85ABDE4BA15168EAF0CA87A555FD56D10FBCA 2907E3E83BA95368623B8C4686915CF9" ~r:"94EDBB92A5ECB8AAD4736E56C691916B3F88140666CE9FA73D64C4EA95AD133C 81A648152E44ACF96E36DD1E80FABE46" ~s:"99EF4AEB15F178CEA1FE40DB2603138F130E740A19624526203B6351D0A3A94F A329C145786E679E7B82C71A38628AC8"; case Digestif.sha512 ~message:"sample" ~k:"92FC3C7183A883E24216D1141F1A8976C5B0DD797DFA597E3D7B32198BD35331 A4E966532593A52980D0E3AAA5E10EC3" ~r:"ED0959D5880AB2D869AE7F6C2915C6D60F96507F9CB3E047C0046861DA4A799C FE30F35CC900056D7C99CD7882433709" ~s:"512C8CCEEE3890A84058CE1E22DBC2198F42323CE8ACA9135329F03C068E5112 DC7CC3EF3446DEFCEB01A45C2667FDD5"; case Digestif.sha1 ~message:"test" ~k:"66CC2C8F4D303FC962E5FF6A27BD79F84EC812DDAE58CF5243B64A4AD8094D47 EC3727F3A3C186C15054492E30698497" ~r:"4BC35D3A50EF4E30576F58CD96CE6BF638025EE624004A1F7789A8B8E43D0678 ACD9D29876DAF46638645F7F404B11C7" ~s:"D5A6326C494ED3FF614703878961C0FDE7B2C278F9A65FD8C4B7186201A29916 95BA1C84541327E966FA7B50F7382282"; case Digestif.sha224 ~message:"test" ~k:"18FA39DB95AA5F561F30FA3591DC59C0FA3653A80DAFFA0B48D1A4C6DFCBFF6E 3D33BE4DC5EB8886A8ECD093F2935726" ~r:"E8C9D0B6EA72A0E7837FEA1D14A1A9557F29FAA45D3E7EE888FC5BF954B5E624 64A9A817C47FF78B8C11066B24080E72" ~s:"07041D4A7A0379AC7232FF72E6F77B6DDB8F09B16CCE0EC3286B2BD43FA8C614 1C53EA5ABEF0D8231077A04540A96B66"; case Digestif.sha256 ~message:"test" ~k:"0CFAC37587532347DC3389FDC98286BBA8C73807285B184C83E62E26C401C0FA A48DD070BA79921A3457ABFF2D630AD7" ~r:"6D6DEFAC9AB64DABAFE36C6BF510352A4CC27001263638E5B16D9BB51D451559 F918EEDAF2293BE5B475CC8F0188636B" ~s:"2D46F3BECBCC523D5F1A1256BF0C9B024D879BA9E838144C8BA6BAEB4B53B47D 51AB373F9845C0514EEFB14024787265"; case Digestif.sha384 ~message:"test" ~k:"015EE46A5BF88773ED9123A5AB0807962D193719503C527B031B4C2D225092AD A71F4A459BC0DA98ADB95837DB8312EA" ~r:"8203B63D3C853E8D77227FB377BCF7B7B772E97892A80F36AB775D509D7A5FEB 0542A7F0812998DA8F1DD3CA3CF023DB" ~s:"DDD0760448D42D8A43AF45AF836FCE4DE8BE06B485E9B61B827C2F13173923E0 6A739F040649A667BF3B828246BAA5A5"; case Digestif.sha512 ~message:"test" ~k:"3780C4F67CB15518B6ACAE34C9F83568D2E12E47DEAB6C50A4E4EE5319D1E8CE 0E2CC8A136036DC4B9C00E6888F66B6C" ~r:"A0D5D090C9980FAF3C2CE57B7AE951D31977DD11C775D314AF55F76C676447D0 6FB6495CD21B4B6E340FC236584FB277" ~s:"976984E59B4C77B0E8E4460DCA3D9F20E07B9BB1F63BEEFAF576F6B2E8B22463 4A2092CD3792E0159AD9CEE37659C736" ] in ("public key matches", `Quick, pub_rfc) :: ("public key compression and decompression", `Quick, pub_key_compression (module P384.Dsa)) :: List.mapi (fun i c -> "RFC 6979 A.2.6 " ^ string_of_int i, `Quick, c) cases let ecdsa_rfc6979_p521 = (* A.2.7 - P 521 *) let of_h b = of_hex ((String.make 1 '0') ^ b) in let priv, pub = let data = of_h "0FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75C AA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83 538" in match P521.Dsa.priv_of_octets data with | Ok p -> p, P521.Dsa.pub_of_priv p | Error _ -> assert false in let pub_rfc () = let fst = String.make 1 '\004' in let ux = of_h "1894550D0785932E00EAA23B694F213F8C3121F86DC97A04E5A7167DB4E5BCD3 71123D46E45DB6B5D5370A7F20FB633155D38FFA16D2BD761DCAC474B9A2F502 3A4" and uy = of_h "0493101C962CD4D2FDDF782285E64584139C2F91B47F87FF82354D6630F746A2 8A0DB25741B5B34A828008B22ACC23F924FAAFBD4D33F81EA66956DFEAA2BFDF CF5" in match P521.Dsa.pub_of_octets (fst ^ ux ^ uy) with | Ok p -> let pub_eq = String.equal (P521.Dsa.pub_to_octets pub) (P521.Dsa.pub_to_octets p) in Alcotest.(check bool __LOC__ true pub_eq) | Error _ -> Alcotest.fail "bad public key" in let case (type a) (hash : a Digestif.hash) ~message ~k ~r ~s () = let msg = Digestif.(digest_string hash message |> to_raw_string hash) and k = of_h k in let k' = let module H = (val Digestif.module_of hash) in let module K = P521.Dsa.K_gen (H) in K.generate ~key:priv msg in Alcotest.(check bool __LOC__ true (String.equal k k')); let sig_eq (r', s') = String.equal (of_h r) r' && String.equal (of_h s) s' in let sig' = P521.Dsa.sign ~key:priv ~k msg in Alcotest.(check bool __LOC__ true (sig_eq sig')) in let cases = [ case Digestif.sha1 ~message:"sample" ~k:"089C071B419E1C2820962321787258469511958E80582E95D8378E0C2CCDB3CB 42BEDE42F50E3FA3C71F5A76724281D31D9C89F0F91FC1BE4918DB1C03A5838D 0F9" ~r:"0343B6EC45728975EA5CBA6659BBB6062A5FF89EEA58BE3C80B619F322C87910 FE092F7D45BB0F8EEE01ED3F20BABEC079D202AE677B243AB40B5431D497C55D 75D" ~s:"0E7B0E675A9B24413D448B8CC119D2BF7B2D2DF032741C096634D6D65D0DBE3D 5694625FB9E8104D3B842C1B0E2D0B98BEA19341E8676AEF66AE4EBA3D5475D5 D16"; case Digestif.sha224 ~message:"sample" ~k:"121415EC2CD7726330A61F7F3FA5DE14BE9436019C4DB8CB4041F3B54CF31BE0 493EE3F427FB906393D895A19C9523F3A1D54BB8702BD4AA9C99DAB2597B9211 3F3" ~r:"1776331CFCDF927D666E032E00CF776187BC9FDD8E69D0DABB4109FFE1B5E2A3 0715F4CC923A4A5E94D2503E9ACFED92857B7F31D7152E0F8C00C15FF3D87E2E D2E" ~s:"050CB5265417FE2320BBB5A122B8E1A32BD699089851128E360E620A30C7E17B A41A666AF126CE100E5799B153B60528D5300D08489CA9178FB610A2006C254B 41F"; case Digestif.sha256 ~message:"sample" ~k:"0EDF38AFCAAECAB4383358B34D67C9F2216C8382AAEA44A3DAD5FDC9C3257576 1793FEF24EB0FC276DFC4F6E3EC476752F043CF01415387470BCBD8678ED2C7E 1A0" ~r:"1511BB4D675114FE266FC4372B87682BAECC01D3CC62CF2303C92B3526012659 D16876E25C7C1E57648F23B73564D67F61C6F14D527D54972810421E7D87589E 1A7" ~s:"04A171143A83163D6DF460AAF61522695F207A58B95C0644D87E52AA1A347916 E4F7A72930B1BC06DBE22CE3F58264AFD23704CBB63B29B931F7DE6C9D949A7E CFC"; case Digestif.sha384 ~message:"sample" ~k:"1546A108BC23A15D6F21872F7DED661FA8431DDBD922D0DCDB77CC878C8553FF AD064C95A920A750AC9137E527390D2D92F153E66196966EA554D9ADFCB109C4 211" ~r:"1EA842A0E17D2DE4F92C15315C63DDF72685C18195C2BB95E572B9C5136CA4B4 B576AD712A52BE9730627D16054BA40CC0B8D3FF035B12AE75168397F5D50C67 451" ~s:"1F21A3CEE066E1961025FB048BD5FE2B7924D0CD797BABE0A83B66F1E35EEAF5 FDE143FA85DC394A7DEE766523393784484BDF3E00114A1C857CDE1AA203DB65 D61"; case Digestif.sha512 ~message:"sample" ~k:"1DAE2EA071F8110DC26882D4D5EAE0621A3256FC8847FB9022E2B7D28E6F1019 8B1574FDD03A9053C08A1854A168AA5A57470EC97DD5CE090124EF52A2F7ECBF FD3" ~r:"0C328FAFCBD79DD77850370C46325D987CB525569FB63C5D3BC53950E6D4C5F1 74E25A1EE9017B5D450606ADD152B534931D7D4E8455CC91F9B15BF05EC36E37 7FA" ~s:"0617CCE7CF5064806C467F678D3B4080D6F1CC50AF26CA209417308281B68AF2 82623EAA63E5B5C0723D8B8C37FF0777B1A20F8CCB1DCCC43997F1EE0E44DA4A 67A"; case Digestif.sha1 ~message:"test" ~k:"0BB9F2BF4FE1038CCF4DABD7139A56F6FD8BB1386561BD3C6A4FC818B20DF5DD BA80795A947107A1AB9D12DAA615B1ADE4F7A9DC05E8E6311150F47F5C57CE8B 222" ~r:"13BAD9F29ABE20DE37EBEB823C252CA0F63361284015A3BF430A46AAA80B87B0 693F0694BD88AFE4E661FC33B094CD3B7963BED5A727ED8BD6A3A202ABE009D0 367" ~s:"1E9BB81FF7944CA409AD138DBBEE228E1AFCC0C890FC78EC8604639CB0DBDC90 F717A99EAD9D272855D00162EE9527567DD6A92CBD629805C0445282BBC91679 7FF"; case Digestif.sha224 ~message:"test" ~k:"040D09FCF3C8A5F62CF4FB223CBBB2B9937F6B0577C27020A99602C25A011369 87E452988781484EDBBCF1C47E554E7FC901BC3085E5206D9F619CFF07E73D6F 706" ~r:"1C7ED902E123E6815546065A2C4AF977B22AA8EADDB68B2C1110E7EA44D42086 BFE4A34B67DDC0E17E96536E358219B23A706C6A6E16BA77B65E1C595D43CAE1 7FB" ~s:"177336676304FCB343CE028B38E7B4FBA76C1C1B277DA18CAD2A8478B2A9A9F5 BEC0F3BA04F35DB3E4263569EC6AADE8C92746E4C82F8299AE1B8F1739F8FD51 9A4"; case Digestif.sha256 ~message:"test" ~k:"01DE74955EFAABC4C4F17F8E84D881D1310B5392D7700275F82F145C61E84384 1AF09035BF7A6210F5A431A6A9E81C9323354A9E69135D44EBD2FCAA7731B909 258" ~r:"00E871C4A14F993C6C7369501900C4BC1E9C7B0B4BA44E04868B30B41D807104 2EB28C4C250411D0CE08CD197E4188EA4876F279F90B3D8D74A3C76E6F1E4656 AA8" ~s:"0CD52DBAA33B063C3A6CD8058A1FB0A46A4754B034FCC644766CA14DA8CA5CA9 FDE00E88C1AD60CCBA759025299079D7A427EC3CC5B619BFBC828E7769BCD694 E86"; case Digestif.sha384 ~message:"test" ~k:"1F1FC4A349A7DA9A9E116BFDD055DC08E78252FF8E23AC276AC88B1770AE0B5D CEB1ED14A4916B769A523CE1E90BA22846AF11DF8B300C38818F713DADD85DE0 C88" ~r:"14BEE21A18B6D8B3C93FAB08D43E739707953244FDBE924FA926D76669E7AC8C 89DF62ED8975C2D8397A65A49DCC09F6B0AC62272741924D479354D74FF60755 78C" ~s:"133330865C067A0EAF72362A65E2D7BC4E461E8C8995C3B6226A21BD1AA78F0E D94FE536A0DCA35534F0CD1510C41525D163FE9D74D134881E35141ED5E8E95B 979"; case Digestif.sha512 ~message:"test" ~k:"16200813020EC986863BEDFC1B121F605C1215645018AEA1A7B215A564DE9EB1 B38A67AA1128B80CE391C4FB71187654AAA3431027BFC7F395766CA988C964DC 56D" ~r:"13E99020ABF5CEE7525D16B69B229652AB6BDF2AFFCAEF38773B4B7D08725F10 CDB93482FDCC54EDCEE91ECA4166B2A7C6265EF0CE2BD7051B7CEF945BABD47E E6D" ~s:"1FBD0013C674AA79CB39849527916CE301C66EA7CE8B80682786AD60F98F7E78 A19CA69EFF5C57400E3B3A0AD66CE0978214D13BAF4E9AC60752F7B155E2DE4D CE3" ] in ("public key matches", `Quick, pub_rfc) :: ("public key compression and decompression", `Quick, pub_key_compression (module P521.Dsa)) :: List.mapi (fun i c -> "RFC 6979 A.2.7 " ^ string_of_int i, `Quick, c) cases let x25519 () = (* RFC 7748, 6.1 *) let a = of_hex "77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a" and apub = of_hex "8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a" and b = of_hex "5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb" and bpub = of_hex "de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f" and shared = of_hex "4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742" in let of_octets cs = match X25519.secret_of_octets cs with | Ok (a, b) -> a, b | Error _ -> Alcotest.fail "couldn't decode secret" in let apriv, apub' = of_octets a in Alcotest.(check bool __LOC__ true (String.equal apub apub')); let bpriv, bpub' = of_octets b in Alcotest.(check bool __LOC__ true (String.equal bpub bpub')); (match X25519.key_exchange apriv bpub with | Ok shared' -> Alcotest.(check bool __LOC__ true (String.equal shared shared')) | Error e -> Alcotest.failf "X25519 key exchange apriv bpub failed %a" pp_error e); match X25519.key_exchange bpriv apub with | Ok shared' -> Alcotest.(check bool __LOC__ true (String.equal shared shared')) | Error e -> Alcotest.failf "X25519 key exchange bpriv apub failed %a" pp_error e let ed25519 = let test secret public msg signature = Alcotest.( check string "public key is ok" (Ed25519.pub_to_octets public) Ed25519.(pub_to_octets (pub_of_priv secret))); Alcotest.(check string "signature is ok" signature (Ed25519.sign ~key:secret msg)); Alcotest.(check bool "verify is ok" true (Ed25519.verify ~key:public signature ~msg)) in let case i ~secret ~public ~msg ~signature = "RFC 8032 " ^ string_of_int i, `Quick, fun () -> let s = match Ed25519.priv_of_octets (of_hex secret) with | Ok p -> Alcotest.(check string "private key encoding is good" (of_hex secret) (Ed25519.priv_to_octets p)); p | Error _ -> Alcotest.fail "failed to decode private key" and p = match Ed25519.pub_of_octets (of_hex public) with | Ok p -> Alcotest.(check string "public key encoding is good" (of_hex public) (Ed25519.pub_to_octets p)); p | Error _ -> Alcotest.fail "failed to decode public key" and m = of_hex msg and si = of_hex signature in test s p m si in [ case 1 ~secret: "9d61b19deffd5a60ba844af492ec2cc4 4449c5697b326919703bac031cae7f60" ~public: "d75a980182b10ab7d54bfed3c964073a 0ee172f3daa62325af021a68f707511a" ~msg:"" ~signature: {| e5564300c360ac729086e2cc806e828a 84877f1eb8e5d974d873e06522490155 5fb8821590a33bacc61e39701cf9b46b d25bf5f0595bbe24655141438e7a100b |}; case 2 ~secret: "4ccd089b28ff96da9db6c346ec114e0f 5b8a319f35aba624da8cf6ed4fb8a6fb" ~public: "3d4017c3e843895a92b70aa74d1b7ebc 9c982ccf2ec4968cc0cd55f12af4660c" ~msg:"72" ~signature: {| 92a009a9f0d4cab8720e820b5f642540 a2b27b5416503f8fb3762223ebdb69da 085ac1e43e15996e458f3613d0f11d8c 387b2eaeb4302aeeb00d291612bb0c00 |}; case 3 ~secret: "c5aa8df43f9f837bedb7442f31dcb7b1 66d38535076f094b85ce3a2e0b4458f7" ~public: "fc51cd8e6218a1a38da47ed00230f058 0816ed13ba3303ac5deb911548908025" ~msg:"af82" ~signature: {| 6291d657deec24024827e69c3abe01a3 0ce548a284743a445e3680d7db5ac3ac 18ff9b538d16f290ae67f760984dc659 4a7c15e9716ed28dc027beceea1ec40a |}; case 4 ~secret: "f5e5767cf153319517630f226876b86c 8160cc583bc013744c6bf255f5cc0ee5" ~public: "278117fc144c72340f67d0f2316e8386 ceffbf2b2428c9c51fef7c597f1d426e" ~msg: {| 08b8b2b733424243760fe426a4b54908 632110a66c2f6591eabd3345e3e4eb98 fa6e264bf09efe12ee50f8f54e9f77b1 e355f6c50544e23fb1433ddf73be84d8 79de7c0046dc4996d9e773f4bc9efe57 38829adb26c81b37c93a1b270b20329d 658675fc6ea534e0810a4432826bf58c 941efb65d57a338bbd2e26640f89ffbc 1a858efcb8550ee3a5e1998bd177e93a 7363c344fe6b199ee5d02e82d522c4fe ba15452f80288a821a579116ec6dad2b 3b310da903401aa62100ab5d1a36553e 06203b33890cc9b832f79ef80560ccb9 a39ce767967ed628c6ad573cb116dbef efd75499da96bd68a8a97b928a8bbc10 3b6621fcde2beca1231d206be6cd9ec7 aff6f6c94fcd7204ed3455c68c83f4a4 1da4af2b74ef5c53f1d8ac70bdcb7ed1 85ce81bd84359d44254d95629e9855a9 4a7c1958d1f8ada5d0532ed8a5aa3fb2 d17ba70eb6248e594e1a2297acbbb39d 502f1a8c6eb6f1ce22b3de1a1f40cc24 554119a831a9aad6079cad88425de6bd e1a9187ebb6092cf67bf2b13fd65f270 88d78b7e883c8759d2c4f5c65adb7553 878ad575f9fad878e80a0c9ba63bcbcc 2732e69485bbc9c90bfbd62481d9089b eccf80cfe2df16a2cf65bd92dd597b07 07e0917af48bbb75fed413d238f5555a 7a569d80c3414a8d0859dc65a46128ba b27af87a71314f318c782b23ebfe808b 82b0ce26401d2e22f04d83d1255dc51a ddd3b75a2b1ae0784504df543af8969b e3ea7082ff7fc9888c144da2af58429e c96031dbcad3dad9af0dcbaaaf268cb8 fcffead94f3c7ca495e056a9b47acdb7 51fb73e666c6c655ade8297297d07ad1 ba5e43f1bca32301651339e22904cc8c 42f58c30c04aafdb038dda0847dd988d cda6f3bfd15c4b4c4525004aa06eeff8 ca61783aacec57fb3d1f92b0fe2fd1a8 5f6724517b65e614ad6808d6f6ee34df f7310fdc82aebfd904b01e1dc54b2927 094b2db68d6f903b68401adebf5a7e08 d78ff4ef5d63653a65040cf9bfd4aca7 984a74d37145986780fc0b16ac451649 de6188a7dbdf191f64b5fc5e2ab47b57 f7f7276cd419c17a3ca8e1b939ae49e4 88acba6b965610b5480109c8b17b80e1 b7b750dfc7598d5d5011fd2dcc5600a3 2ef5b52a1ecc820e308aa342721aac09 43bf6686b64b2579376504ccc493d97e 6aed3fb0f9cd71a43dd497f01f17c0e2 cb3797aa2a2f256656168e6c496afc5f b93246f6b1116398a346f1a641f3b041 e989f7914f90cc2c7fff357876e506b5 0d334ba77c225bc307ba537152f3f161 0e4eafe595f6d9d90d11faa933a15ef1 369546868a7f3a45a96768d40fd9d034 12c091c6315cf4fde7cb68606937380d b2eaaa707b4c4185c32eddcdd306705e 4dc1ffc872eeee475a64dfac86aba41c 0618983f8741c5ef68d3a101e8a3b8ca c60c905c15fc910840b94c00a0b9d0 |} ~signature: {| 0aab4c900501b3e24d7cdf4663326a3a 87df5e4843b2cbdb67cbf6e460fec350 aa5371b1508f9f4528ecea23c436d94b 5e8fcd4f681e30a6ac00a9704a188a03 |}; case 5 ~secret: "833fe62409237b9d62ec77587520911e 9a759cec1d19755b7da901b96dca3d42" ~public: "ec172b93ad5e563bf4932c70e1245034 c35467ef2efd4d64ebf819683467e2bf" ~msg: {| ddaf35a193617abacc417349ae204131 12e6fa4e89a97ea20a9eeee64b55d39a 2192992a274fc1a836ba3c23a3feebbd 454d4423643ce80e2a9ac94fa54ca49f |} ~signature: {| dc2a4459e7369633a52b1bf277839a00 201009a3efbf3ecb69bea2186c26b589 09351fc9ac90b3ecfdfbc7c66431e030 3dca179c138ac17ad9bef1177331a704 |}; ] let p521_regression () = let key = of_hex "04 01 e4 f8 8a 40 3d fe 2f 65 a0 20 50 01 9b 87 86 2c 30 2f 64 58 de 68 63 ab 92 72 88 04 c6 20 7b 6f 9a 52 95 2d ff c7 80 df 50 44 b1 c4 91 e3 a7 65 39 e6 9c cf ed d2 2a eb 47 84 ea 0f 3d 05 dd 25 0e 00 95 6e 19 fb 7f b7 ce 47 5a 59 01 5f 35 33 fc 85 ac 34 1a b0 7a 67 86 e8 3e 31 fe 38 35 5c bb a1 b5 74 f4 47 a3 4c 0a f0 5f 6d 68 47 85 0f e9 79 74 23 e8 75 47 6e 2b e5 ea 1b 0a 36 b9 c3 94 ca b0" and data = of_hex "a8 98 57 b9 3f 58 02 c7 9a 37 e2 d7 89 d8 0b f4 2d 84 c2 24 7c 7f ff 5f 7b 65 c5 17 cf 79 7d 36 ff d3 9d 47 5e 68 90 57 f1 61 48 18 04 c3 fe ee 59 b2 15 2d 75 8b 9a 3c 52 60 96 5c 52 a8 55 9c" and sigr = of_hex "3a 2c 99 0b 61 a1 da 06 20 bf 6c fe 1f d3 f8 2a cb f1 e5 0f 78 11 61 58 22 e4 a0 5f 18 81 8d 98 f8 7a ca 8b f8 f8 cc b8 95 f7 6f 03 54 1b 66 6e cf c5 cb f1 7b 48 82 d2 c3 0e 0e 1b b4 ad e6 a4 5c" and sigs = of_hex "01 7b 8c 82 a5 aa 80 c5 ee 23 0f 91 55 89 a7 b0 3c 46 7f 56 ff b4 52 89 52 99 59 1e 5e b7 f2 c1 df f8 a0 4f d3 dd 1d f0 07 78 3a 2f 29 d6 61 61 55 dc 3b be 14 82 93 75 c2 0d be 7e ca 50 e4 3c 98 88" in match P521.Dsa.pub_of_octets key with | Ok key -> Alcotest.check Alcotest.bool "regression 1" true (P521.Dsa.verify ~key (sigr, sigs) data) | Error _ -> Alcotest.fail "regression failed" let () = Mirage_crypto_rng_unix.use_default (); Alcotest.run "EC" [ ("P256 Key exchange", key_exchange); ("P256 Low level scalar mult", scalar_mult); ("P256 Point validation", point_validation); ("P256 Scalar validation when generating", scalar_validation); ("ECDSA NIST", ecdsa); ("ECDSA RFC 6979 P256", ecdsa_rfc6979_p256); ("ECDSA RFC 6979 P384", ecdsa_rfc6979_p384); ("ECDSA RFC 6979 P521", ecdsa_rfc6979_p521); ("X25519", [ "RFC 7748", `Quick, x25519 ]); ("ED25519", ed25519); ("ECDSA P521 regression", [ "regreesion1", `Quick, p521_regression ]); ]