let ( let* ) = Result.bind type ecdsa = [ | `P256 of Mirage_crypto_ec.P256.Dsa.priv | `P384 of Mirage_crypto_ec.P384.Dsa.priv | `P521 of Mirage_crypto_ec.P521.Dsa.priv ] type t = [ ecdsa | `RSA of Mirage_crypto_pk.Rsa.priv | `ED25519 of Mirage_crypto_ec.Ed25519.priv ] let key_type = function | `RSA _ -> `RSA | `ED25519 _ -> `ED25519 | `P256 _ -> `P256 | `P384 _ -> `P384 | `P521 _ -> `P521 let generate ?seed ?(bits = 4096) typ = let g = match seed with | None -> None | Some seed -> Some Mirage_crypto_rng.(create ~seed (module Fortuna)) in match typ with | `RSA -> `RSA (Mirage_crypto_pk.Rsa.generate ?g ~bits ()) | `ED25519 -> `ED25519 (fst (Mirage_crypto_ec.Ed25519.generate ?g ())) | `P256 -> `P256 (fst (Mirage_crypto_ec.P256.Dsa.generate ?g ())) | `P384 -> `P384 (fst (Mirage_crypto_ec.P384.Dsa.generate ?g ())) | `P521 -> `P521 (fst (Mirage_crypto_ec.P521.Dsa.generate ?g ())) let of_octets data = let open Mirage_crypto_ec in let ec_err e = Result.map_error (fun e -> `Msg (Fmt.to_to_string Mirage_crypto_ec.pp_error e)) e in function | `RSA -> Error (`Msg "cannot decode an RSA key") | `ED25519 -> let* k = ec_err (Ed25519.priv_of_octets data) in Ok (`ED25519 k) | `P256 -> let* k = ec_err (P256.Dsa.priv_of_octets data) in Ok (`P256 k) | `P384 -> let* k = ec_err (P384.Dsa.priv_of_octets data) in Ok (`P384 k) | `P521 -> let* k = ec_err (P521.Dsa.priv_of_octets data) in Ok (`P521 k) let of_string ?seed_or_data ?bits typ data = match seed_or_data with | None -> begin match typ with | `RSA -> Ok (generate ~seed:data ?bits `RSA) | _ -> let* data = Base64.decode data in of_octets data typ end | Some `Seed -> Ok (generate ~seed:data ?bits typ) | Some `Data -> let* data = Base64.decode data in of_octets data typ let public = function | `RSA priv -> `RSA (Mirage_crypto_pk.Rsa.pub_of_priv priv) | `ED25519 priv -> `ED25519 (Mirage_crypto_ec.Ed25519.pub_of_priv priv) | `P256 priv -> `P256 (Mirage_crypto_ec.P256.Dsa.pub_of_priv priv) | `P384 priv -> `P384 (Mirage_crypto_ec.P384.Dsa.pub_of_priv priv) | `P521 priv -> `P521 (Mirage_crypto_ec.P521.Dsa.pub_of_priv priv) let sign hash ?scheme key data = let open Mirage_crypto_ec in let hashed () = Public_key.hashed hash data and ecdsa_to_str s = Algorithm.ecdsa_sig_to_octets s in let scheme = Key_type.opt_signature_scheme ?scheme (key_type key) in try match key, scheme with | `RSA key, `RSA_PSS -> let module H = (val (Digestif.module_of_hash' hash)) in let module PSS = Mirage_crypto_pk.Rsa.PSS(H) in let* d = hashed () in Ok (PSS.sign ~key (`Digest d)) | `RSA key, `RSA_PKCS1 -> let* d = hashed () in Ok (Mirage_crypto_pk.Rsa.PKCS1.sign ~key ~hash (`Digest d)) | `ED25519 key, `ED25519 -> begin match data with | `Message m -> Ok (Ed25519.sign ~key m) | `Digest _ -> Error (`Msg "Ed25519 only suitable with raw message") end | #ecdsa as key, `ECDSA -> let* d = hashed () in Ok (ecdsa_to_str (match key with | `P256 key -> P256.Dsa.(sign ~key (Public_key.trunc byte_length d)) | `P384 key -> P384.Dsa.(sign ~key (Public_key.trunc byte_length d)) | `P521 key -> P521.Dsa.(sign ~key (Public_key.trunc byte_length d)))) | _ -> Error (`Msg "invalid key and signature scheme combination") with | Mirage_crypto_pk.Rsa.Insufficient_key -> Error (`Msg "RSA key of insufficient length") | Message_too_long -> Error (`Msg "message too long") module Asn = struct open Asn.S open Mirage_crypto_pk (* RSA *) let other_prime_infos = sequence_of @@ (sequence3 (required ~label:"prime" unsigned_integer) (required ~label:"exponent" unsigned_integer) (required ~label:"coefficient" unsigned_integer)) let rsa_private_key = let integer = map Z_extra.of_octets_be Z_extra.to_octets_be unsigned_integer in let f (v, (n, (e, (d, (p, (q, (dp, (dq, (q', other))))))))) = match (v, other) with | (0, None) -> begin match Rsa.priv ~e ~d ~n ~p ~q ~dp ~dq ~q' with | Ok p -> p | Error (`Msg m) -> parse_error "bad RSA private key %s" m end | _ -> parse_error "multi-prime RSA keys not supported" and g { Rsa.e; d; n; p; q; dp; dq; q' } = (0, (n, (e, (d, (p, (q, (dp, (dq, (q', None))))))))) in map f g @@ sequence @@ (required ~label:"version" int) @ (required ~label:"modulus" integer) (* n *) @ (required ~label:"publicExponent" integer) (* e *) @ (required ~label:"privateExponent" integer) (* d *) @ (required ~label:"prime1" integer) (* p *) @ (required ~label:"prime2" integer) (* q *) @ (required ~label:"exponent1" integer) (* dp *) @ (required ~label:"exponent2" integer) (* dq *) @ (required ~label:"coefficient" integer) (* qinv *) -@ (optional ~label:"otherPrimeInfos" other_prime_infos) (* For outside uses. *) let (rsa_private_of_octets, rsa_private_to_octets) = Asn_grammars.projections_of Asn.der rsa_private_key (* PKCS8 *) let (rsa_priv_of_str, rsa_priv_to_str) = Asn_grammars.project_exn rsa_private_key let ec_to_err = function | Ok x -> x | Error e -> parse_error "%a" Mirage_crypto_ec.pp_error e let ed25519_of_str, ed25519_to_str = Asn_grammars.project_exn octet_string let ec_private_key = let f (v, pk, nc, pub) = if v <> 1 then parse_error "bad version for ec Private key" else let curve = match nc with | Some c -> Some (Algorithm.curve_of_oid c) | None -> None in pk, curve, pub and g (pk, curve, pub) = let nc = match curve with | None -> None | Some c -> Some (Algorithm.curve_to_oid c) in (1, pk, nc, pub) in Asn.S.map f g @@ sequence4 (required ~label:"version" int) (* ecPrivkeyVer1(1) *) (required ~label:"privateKey" octet_string) (* from rfc5480: choice3, but only namedCurve is allowed in PKIX *) (optional ~label:"namedCurve" (explicit 0 oid)) (optional ~label:"publicKey" (explicit 1 bit_string)) let ec_of_str, ec_to_str = Asn_grammars.project_exn ec_private_key let reparse_ec_private curve priv = let open Mirage_crypto_ec in match curve with | `SECP256R1 -> let* p = P256.Dsa.priv_of_octets priv in Ok (`P256 p) | `SECP384R1 -> let* p = P384.Dsa.priv_of_octets priv in Ok (`P384 p) | `SECP521R1 -> let* p = P521.Dsa.priv_of_octets priv in Ok (`P521 p) (* external use (result) *) let ec_priv_of_str = let dec, _ = Asn_grammars.projections_of Asn.der ec_private_key in fun cs -> let* priv, curve, _pub = dec cs in match curve with | None -> Error (`Parse "no curve provided") | Some c -> Result.map_error (fun e -> `Parse (Fmt.to_to_string Mirage_crypto_ec.pp_error e)) (reparse_ec_private c priv) let ec_of_str ?curve cs = let (priv, named_curve, _pub) = ec_of_str cs in let nc = match curve, named_curve with | Some c, None -> c | None, Some c -> c | Some c, Some c' -> if c = c' then c else parse_error "conflicting curve" | None, None -> parse_error "unknown curve" in ec_to_err (reparse_ec_private nc priv) let ec_to_str ?curve ?pub key = ec_to_str (key, curve, pub) let reparse_private pk = match pk with | (0, Algorithm.RSA, cs) -> `RSA (rsa_priv_of_str cs) | (0, Algorithm.ED25519, cs) -> let data = ed25519_of_str cs in `ED25519 (ec_to_err (Mirage_crypto_ec.Ed25519.priv_of_octets data)) | (0, Algorithm.EC_pub curve, cs) -> ec_of_str ~curve cs | _ -> parse_error "unknown private key info" let unparse_private p = let open Mirage_crypto_ec in let open Algorithm in let alg, cs = match p with | `RSA pk -> RSA, rsa_priv_to_str pk | `ED25519 pk -> ED25519, ed25519_to_str (Ed25519.priv_to_octets pk) | `P256 pk -> EC_pub `SECP256R1, ec_to_str (P256.Dsa.priv_to_octets pk) | `P384 pk -> EC_pub `SECP384R1, ec_to_str (P384.Dsa.priv_to_octets pk) | `P521 pk -> EC_pub `SECP521R1, ec_to_str (P521.Dsa.priv_to_octets pk) in (0, alg, cs) let private_key_info = map reparse_private unparse_private @@ sequence3 (required ~label:"version" int) (required ~label:"privateKeyAlgorithm" Algorithm.identifier) (required ~label:"privateKey" octet_string) (* TODO: there's an (optional ~label:"attributes" @@ implicit 0 (SET of Attributes) which are defined in X.501; but nobody seems to use them anyways *) let (private_of_octets, private_to_octets) = Asn_grammars.projections_of Asn.der private_key_info end let decode_der cs = Asn_grammars.err_to_msg (Asn.private_of_octets cs) let encode_der = Asn.private_to_octets let decode_pem cs = let* data = Pem.parse cs in let rsa_p (t, _) = String.equal "RSA PRIVATE KEY" t and ec_p (t, _) = String.equal "EC PRIVATE KEY" t and pk_p (t, _) = String.equal "PRIVATE KEY" t in let r, _ = List.partition rsa_p data and ec, _ = List.partition ec_p data and p, _ = List.partition pk_p data in let* k = Pem.foldM (fun (_, k) -> let* k = Asn_grammars.err_to_msg (Asn.rsa_private_of_octets k) in Ok (`RSA k)) r in let* k' = Pem.foldM (fun (_, k) -> Asn_grammars.err_to_msg (Asn.ec_priv_of_str k)) ec in let* k'' = Pem.foldM (fun (_, k) -> Asn_grammars.err_to_msg (Asn.private_of_octets k)) p in Pem.exactly_one ~what:"private key" (k @ k' @ k'') let encode_pem p = Pem.unparse ~tag:"PRIVATE KEY" (Asn.private_to_octets p)