mte/unikernel/duniverse/ocaml-x509/lib/private_key.ml
2025-11-11 02:07:51 +01:00

289 lines
9.7 KiB
OCaml

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)