469 lines
16 KiB
OCaml
469 lines
16 KiB
OCaml
|
|
(* partial PKCS12 implementation, as defined in RFC 7292
|
||
|
|
- no public/private key mode, only password privacy and integrity
|
||
|
|
- algorithmidentifier those I need for openssl interop (looking at the p12 I have on my disk)
|
||
|
|
- require version being 3
|
||
|
|
|
||
|
|
some definitions from PKCS7 (RFC 2315) are implemented as well, as needed
|
||
|
|
*)
|
||
|
|
|
||
|
|
type content_info = Asn.oid * string
|
||
|
|
|
||
|
|
type digest_info = Algorithm.t * string
|
||
|
|
|
||
|
|
type mac_data = digest_info * string * int
|
||
|
|
|
||
|
|
type t = string * mac_data
|
||
|
|
|
||
|
|
module Asn = struct
|
||
|
|
open Asn_grammars
|
||
|
|
open Asn.S
|
||
|
|
open Registry
|
||
|
|
|
||
|
|
let encrypted_content_info =
|
||
|
|
let f (oid, algo, content) =
|
||
|
|
if Asn.OID.equal PKCS7.data oid then
|
||
|
|
(algo, content)
|
||
|
|
else
|
||
|
|
parse_error "expected OID PKCS7 data"
|
||
|
|
and g (algo, content) =
|
||
|
|
(PKCS7.data, algo, content)
|
||
|
|
in
|
||
|
|
Asn.S.map f g @@
|
||
|
|
sequence3
|
||
|
|
(required ~label:"content type" oid) (* here we assume data!? *)
|
||
|
|
(required ~label:"content encryption algorithm" Algorithm.identifier)
|
||
|
|
(optional ~label:"encrypted content" (implicit 0 octet_string))
|
||
|
|
|
||
|
|
let encrypted_data =
|
||
|
|
let f (v, eci) =
|
||
|
|
if v = 0 then eci else parse_error "unknown encrypted data version"
|
||
|
|
and g eci = 0, eci
|
||
|
|
in
|
||
|
|
map f g @@
|
||
|
|
sequence2
|
||
|
|
(required ~label:"version" int)
|
||
|
|
(required ~label:"encrypted content info" encrypted_content_info)
|
||
|
|
|
||
|
|
let content_info =
|
||
|
|
let f (oid, data) =
|
||
|
|
match data with
|
||
|
|
| None -> parse_error "found no value for content info"
|
||
|
|
| Some `C1 data when Asn.OID.equal PKCS7.data oid -> `Data data
|
||
|
|
| Some `C2 eci when Asn.OID.equal PKCS7.encrypted_data oid -> `Encrypted eci
|
||
|
|
| _ -> parse_error "couldn't match PKCS7 oid with choice"
|
||
|
|
and g = function
|
||
|
|
| `Data data -> PKCS7.data, Some (`C1 data)
|
||
|
|
| `Encrypted eci -> PKCS7.encrypted_data, Some (`C2 eci)
|
||
|
|
in
|
||
|
|
map f g @@
|
||
|
|
sequence2
|
||
|
|
(required ~label:"content type" oid)
|
||
|
|
(optional ~label:"content" (explicit 0
|
||
|
|
(choice2 octet_string encrypted_data)))
|
||
|
|
|
||
|
|
let digest_info =
|
||
|
|
sequence2
|
||
|
|
(required ~label:"digest algorithm" Algorithm.identifier)
|
||
|
|
(required ~label:"digest" octet_string)
|
||
|
|
|
||
|
|
let mac_data =
|
||
|
|
sequence3
|
||
|
|
(required ~label:"mac" digest_info)
|
||
|
|
(required ~label:"mac salt" octet_string)
|
||
|
|
(required ~label:"iterations" int)
|
||
|
|
|
||
|
|
let pfx =
|
||
|
|
let f (version, content_info, mac_data) =
|
||
|
|
if version = 3 then
|
||
|
|
match content_info, mac_data with
|
||
|
|
| `Data data, Some md -> data, md
|
||
|
|
| _, None -> parse_error "missing mac_data"
|
||
|
|
| _, _ -> parse_error "unsupported content_info"
|
||
|
|
else
|
||
|
|
parse_error "unsupported pfx version"
|
||
|
|
and g (content, mac_data) =
|
||
|
|
(3, `Data content, Some mac_data)
|
||
|
|
in
|
||
|
|
map f g @@
|
||
|
|
sequence3
|
||
|
|
(required ~label:"version" int)
|
||
|
|
(required ~label:"auth safe" content_info)
|
||
|
|
(* contentType is signedData in public-key integrity mode and data in
|
||
|
|
password integrity mode *)
|
||
|
|
(optional ~label:"mac data" mac_data) (* not present if public keys used *)
|
||
|
|
|
||
|
|
let pfx_of_cs, pfx_to_cs = projections_of Asn.der pfx
|
||
|
|
|
||
|
|
(* payload is a sequence of content_info *)
|
||
|
|
let authenticated_safe = sequence_of content_info
|
||
|
|
|
||
|
|
let auth_safe_of_cs, auth_safe_to_cs =
|
||
|
|
projections_of Asn.der authenticated_safe
|
||
|
|
|
||
|
|
let pkcs12_attribute =
|
||
|
|
sequence2
|
||
|
|
(required ~label:"attribute id" oid)
|
||
|
|
(required ~label:"attribute value" (set_of octet_string))
|
||
|
|
|
||
|
|
(* here:
|
||
|
|
key_bag = PKCS8 private key
|
||
|
|
pkcs8_shrouded_key_bag = encrypted private key info ==
|
||
|
|
sequence2 Algorithm octet_string
|
||
|
|
cert_bag =
|
||
|
|
sequence2
|
||
|
|
cert_id (PKCS9 cert_types <| 1 (X509) or 2 (SDSI))
|
||
|
|
expl 0 cert_value (DER-encoded certificate)
|
||
|
|
crl_bag = sequence2 crl_id (PKCS9 crl_types <| 1) (expl 0 crl (DER-encoded))
|
||
|
|
|
||
|
|
^^^---^^^ those we plan to support
|
||
|
|
|
||
|
|
secret_bag = sequence2 secret_type (expl 0 secret_value)
|
||
|
|
safe_contents_bag = (any of the above) safe_contents (recursive!)
|
||
|
|
*)
|
||
|
|
(* since asn1 does not yet support ANY defined BY, we develop a rather
|
||
|
|
complex grammar covering all supported bags *)
|
||
|
|
let safe_bag =
|
||
|
|
let cert_oid, crl_oid =
|
||
|
|
Asn.OID.(PKCS9.cert_types <| 1, PKCS9.crl_types <| 1)
|
||
|
|
in
|
||
|
|
let f (oid, (a, algo, data), attrs) =
|
||
|
|
match a, algo, data with
|
||
|
|
| `C1 v, Some a, `C1 data when Asn.OID.equal oid PKCS12.key_bag ->
|
||
|
|
let key = Private_key.Asn.reparse_private (v, a, data) in
|
||
|
|
`Private_key key, attrs
|
||
|
|
| `C2 id, None, `C2 data ->
|
||
|
|
if Asn.OID.equal oid PKCS12.cert_bag && Asn.OID.equal id cert_oid then
|
||
|
|
match Certificate.decode_der data with
|
||
|
|
| Error (`Msg e) -> error (`Parse e)
|
||
|
|
| Ok cert -> `Certificate cert, attrs
|
||
|
|
else if Asn.OID.equal oid PKCS12.crl_bag && Asn.OID.equal id crl_oid then
|
||
|
|
match Crl.decode_der data with
|
||
|
|
| Error (`Msg e) -> error (`Parse e)
|
||
|
|
| Ok crl -> `Crl crl, attrs
|
||
|
|
else
|
||
|
|
parse_error "crl bag with non-standard crl"
|
||
|
|
| `C3 algo, None, `C1 data when Asn.OID.equal oid PKCS12.pkcs8_shrouded_key_bag ->
|
||
|
|
`Encrypted_private_key (algo, data), attrs
|
||
|
|
| _ -> parse_error "safe bag OID not supported"
|
||
|
|
and g (v, attrs) =
|
||
|
|
let oid, d = match v with
|
||
|
|
| `Encrypted_private_key (algo, data) ->
|
||
|
|
PKCS12.pkcs8_shrouded_key_bag, (`C3 algo, None, `C1 data)
|
||
|
|
| `Private_key pk ->
|
||
|
|
let v, algo, data = Private_key.Asn.unparse_private pk in
|
||
|
|
PKCS12.key_bag, (`C1 v, Some algo, `C1 data)
|
||
|
|
| `Certificate cert -> PKCS12.cert_bag, (`C2 cert_oid, None, `C2 (Certificate.encode_der cert))
|
||
|
|
| `Crl crl -> PKCS12.crl_bag, (`C2 crl_oid, None, `C2 (Crl.encode_der crl))
|
||
|
|
in
|
||
|
|
(oid, d, attrs)
|
||
|
|
in
|
||
|
|
map f g @@
|
||
|
|
sequence3
|
||
|
|
(required ~label:"bag id" oid)
|
||
|
|
(required ~label:"bag value"
|
||
|
|
(explicit 0
|
||
|
|
(sequence3
|
||
|
|
(required ~label:"fst" (choice3 int oid Algorithm.identifier))
|
||
|
|
(optional ~label:"algorithm" Algorithm.identifier)
|
||
|
|
(required ~label:"data" (choice2 octet_string (explicit 0 octet_string))))))
|
||
|
|
(* (explicit 0 (* encrypted private key *)
|
||
|
|
(sequence2
|
||
|
|
(required ~label:"encryption algorithm" Algorithm.identifier)
|
||
|
|
(required ~label:"encrypted data" octet_string))) *)
|
||
|
|
(* (explicit 0 (* private key ] *)
|
||
|
|
(sequence3
|
||
|
|
(required ~label:"version" int)
|
||
|
|
(required ~label:"privateKeyAlgorithm" Algorithm.identifier)
|
||
|
|
(required ~label:"privateKey" octet_string))) *)
|
||
|
|
(* (explicit 0 (* cert / crl *)
|
||
|
|
(sequence2
|
||
|
|
(required ~label:"oid" oid)
|
||
|
|
(required ~label:"data" (explicit 0 octet_string)))) *)
|
||
|
|
(optional ~label:"bag attributes" (set_of pkcs12_attribute))
|
||
|
|
|
||
|
|
let safe_contents = sequence_of safe_bag
|
||
|
|
|
||
|
|
let safe_contents_of_cs, safe_contents_to_cs =
|
||
|
|
projections_of Asn.der safe_contents
|
||
|
|
end
|
||
|
|
|
||
|
|
let prepare_pw str =
|
||
|
|
let l = String.length str in
|
||
|
|
let cs = Bytes.make ((succ l) * 2) '\000' in
|
||
|
|
for i = 0 to pred l do
|
||
|
|
Bytes.set cs (succ (i * 2)) (String.get str i)
|
||
|
|
done;
|
||
|
|
Bytes.unsafe_to_string cs
|
||
|
|
|
||
|
|
let id len purpose =
|
||
|
|
let id = match purpose with
|
||
|
|
| `Encryption -> 1
|
||
|
|
| `Iv -> 2
|
||
|
|
| `Hmac -> 3
|
||
|
|
in
|
||
|
|
String.make len (Char.unsafe_chr id)
|
||
|
|
|
||
|
|
let v = function
|
||
|
|
| `MD5 | `SHA1 | `SHA224 | `SHA256 -> 512 / 8
|
||
|
|
| `SHA384 | `SHA512 -> 1024 / 8
|
||
|
|
|
||
|
|
let fill ~data ~out =
|
||
|
|
let len = Bytes.length out
|
||
|
|
and l = String.length data
|
||
|
|
in
|
||
|
|
let rec c off =
|
||
|
|
if off < len then begin
|
||
|
|
Bytes.blit_string data 0 out off (min (len - off) l);
|
||
|
|
c (off + l)
|
||
|
|
end
|
||
|
|
in
|
||
|
|
c 0
|
||
|
|
|
||
|
|
let fill_or_empty size data =
|
||
|
|
let l = String.length data in
|
||
|
|
if l = 0 then data
|
||
|
|
else
|
||
|
|
let len = size * ((l + size - 1) / size) in
|
||
|
|
let buf = Bytes.make len '\000' in
|
||
|
|
fill ~data ~out:buf;
|
||
|
|
Bytes.unsafe_to_string buf
|
||
|
|
|
||
|
|
let pbes algorithm purpose password salt iterations n =
|
||
|
|
let module Hash = (val (Digestif.module_of_hash' (algorithm :> Digestif.hash'))) in
|
||
|
|
let pw = prepare_pw password
|
||
|
|
and v = v algorithm
|
||
|
|
and u = Hash.digest_size
|
||
|
|
in
|
||
|
|
let diversifier = id v purpose in
|
||
|
|
let salt = fill_or_empty v salt in
|
||
|
|
let pass = fill_or_empty v pw in
|
||
|
|
let out = Bytes.make n '\000' in
|
||
|
|
let rec one off i =
|
||
|
|
let ai = ref Hash.(to_raw_string (digest_string (diversifier ^ i))) in
|
||
|
|
for _j = 1 to pred iterations do
|
||
|
|
ai := Hash.(to_raw_string (digest_string !ai));
|
||
|
|
done;
|
||
|
|
Bytes.blit_string !ai 0 out off (min (n - off) u);
|
||
|
|
if u >= n - off then () else
|
||
|
|
(* 6B *)
|
||
|
|
let b = Bytes.make v '\000' in
|
||
|
|
fill ~data:!ai ~out:b;
|
||
|
|
(* 6C *)
|
||
|
|
let i' = Bytes.create (String.length i) in
|
||
|
|
for j = 0 to pred (String.length i / v) do
|
||
|
|
let c = ref 1 in
|
||
|
|
for k = pred v downto 0 do
|
||
|
|
let idx = j * v + k in
|
||
|
|
c := (!c + String.get_uint8 i idx + Bytes.get_uint8 b k) land 0xFFFF;
|
||
|
|
Bytes.set_uint8 i' idx (!c land 0xFF);
|
||
|
|
c := !c lsr 8;
|
||
|
|
done;
|
||
|
|
done;
|
||
|
|
one (off + u) (Bytes.to_string i')
|
||
|
|
in
|
||
|
|
let i = salt ^ pass in
|
||
|
|
one 0 i;
|
||
|
|
Bytes.unsafe_to_string out
|
||
|
|
|
||
|
|
let split str off =
|
||
|
|
String.sub str 0 off,
|
||
|
|
String.sub str off (String.length str - off)
|
||
|
|
|
||
|
|
(* TODO PKCS5/7 padding is "k - (l mod k)" i.e. always > 0!
|
||
|
|
(and rc4 being a stream cipher has no padding!) *)
|
||
|
|
let unpad x =
|
||
|
|
(* TODO can there be bad padding in this scheme? *)
|
||
|
|
let l = String.length x in
|
||
|
|
if l > 0 then
|
||
|
|
let amount = String.get_uint8 x (pred l) in
|
||
|
|
let split_point = if l > amount then l - amount else l in
|
||
|
|
let data, pad = split x split_point in
|
||
|
|
let good = ref true in
|
||
|
|
for i = 0 to pred amount do
|
||
|
|
if String.get_uint8 pad i <> amount then good := false
|
||
|
|
done;
|
||
|
|
if !good then data else x
|
||
|
|
else
|
||
|
|
x
|
||
|
|
|
||
|
|
let pad bs x =
|
||
|
|
let l = String.length x in
|
||
|
|
let to_pad = bs - (l mod bs) in
|
||
|
|
let amount = String.make to_pad (Char.unsafe_chr to_pad) in
|
||
|
|
x ^ amount
|
||
|
|
|
||
|
|
let ( let* ) = Result.bind
|
||
|
|
|
||
|
|
(* there are 3 possibilities to encrypt / decrypt things:
|
||
|
|
- PKCS12 KDF (see above), with RC2/RC4/DES
|
||
|
|
- PKCS5 v1 (PBES, PBKDF1) -- not (yet?) supported
|
||
|
|
- PKCS5 v2 (PBES2, PBKDF2)
|
||
|
|
*)
|
||
|
|
let pkcs12_decrypt algo password data =
|
||
|
|
let open Algorithm in
|
||
|
|
let hash = `SHA1 in
|
||
|
|
let* salt, count, key_len, iv_len =
|
||
|
|
match algo with
|
||
|
|
| SHA_RC4_128 (s, i) -> Ok (s, i, 16, 0)
|
||
|
|
| SHA_RC4_40 (s, i) -> Ok (s, i, 5, 0)
|
||
|
|
| SHA_3DES_CBC (s, i) -> Ok (s, i, 24, 8)
|
||
|
|
| SHA_2DES_CBC (s, i) -> Ok (s, i, 16, 8) (* TODO 2des -> 3des keys (if relevant)*)
|
||
|
|
| SHA_RC2_128_CBC (s, i) -> Ok (s, i, 16, 8)
|
||
|
|
| SHA_RC2_40_CBC (s, i) -> Ok (s, i, 5, 8)
|
||
|
|
| _ -> Error (`Msg "unsupported algorithm")
|
||
|
|
in
|
||
|
|
let key = pbes hash `Encryption password salt count key_len
|
||
|
|
and iv = pbes hash `Iv password salt count iv_len
|
||
|
|
in
|
||
|
|
let open Mirage_crypto in
|
||
|
|
let* data =
|
||
|
|
match algo with
|
||
|
|
| SHA_RC2_40_CBC _ | SHA_RC2_128_CBC _ ->
|
||
|
|
Ok (Rc2.decrypt_cbc ~effective:(key_len * 8) ~key ~iv data)
|
||
|
|
| SHA_RC4_40 _ | SHA_RC4_128 _ ->
|
||
|
|
let key = ARC4.of_secret key in
|
||
|
|
let { ARC4.message ; _ } = ARC4.decrypt ~key data in
|
||
|
|
Ok message
|
||
|
|
| SHA_3DES_CBC _ ->
|
||
|
|
let key = DES.CBC.of_secret key in
|
||
|
|
Ok (DES.CBC.decrypt ~key ~iv data)
|
||
|
|
| _ -> Error (`Msg "encryption algorithm not supported")
|
||
|
|
in
|
||
|
|
Ok (unpad data)
|
||
|
|
|
||
|
|
let pkcs5_2_decrypt kdf enc password data =
|
||
|
|
let* dk_len, iv =
|
||
|
|
match enc with
|
||
|
|
| Algorithm.AES128_CBC iv -> Ok (16l, iv)
|
||
|
|
| Algorithm.AES192_CBC iv -> Ok (24l, iv)
|
||
|
|
| Algorithm.AES256_CBC iv -> Ok (32l, iv)
|
||
|
|
| _ -> Error (`Msg "unsupported encryption algorithm")
|
||
|
|
in
|
||
|
|
let* salt, count, prf =
|
||
|
|
match kdf with
|
||
|
|
| Algorithm.PBKDF2 (salt, iterations, _ (* todo handle keylength *), prf) ->
|
||
|
|
let* prf =
|
||
|
|
match Algorithm.to_hmac prf with
|
||
|
|
| Some prf -> Ok prf
|
||
|
|
| None -> Error (`Msg "unsupported PRF")
|
||
|
|
in
|
||
|
|
Ok (salt, iterations, prf)
|
||
|
|
| _ -> Error (`Msg "expected kdf being pbkdf2")
|
||
|
|
in
|
||
|
|
let key = Pbkdf.pbkdf2 ~prf ~password ~salt ~count ~dk_len in
|
||
|
|
let key = Mirage_crypto.AES.CBC.of_secret key in
|
||
|
|
let msg = Mirage_crypto.AES.CBC.decrypt ~key ~iv data in
|
||
|
|
Ok (unpad msg)
|
||
|
|
|
||
|
|
let pkcs5_2_encrypt (mac : [ `SHA1 | `SHA224 | `SHA256 | `SHA384 | `SHA512 ]) count algo password data =
|
||
|
|
let module Hash = (val (Digestif.module_of_hash' (mac :> Digestif.hash'))) in
|
||
|
|
let bs = Mirage_crypto.AES.CBC.block_size in
|
||
|
|
let iv = Mirage_crypto_rng.generate bs in
|
||
|
|
let enc, dk_len =
|
||
|
|
match algo with
|
||
|
|
| `AES128_CBC -> Algorithm.AES128_CBC iv, 16l
|
||
|
|
| `AES192_CBC -> Algorithm.AES192_CBC iv, 24l
|
||
|
|
| `AES256_CBC -> Algorithm.AES256_CBC iv, 32l
|
||
|
|
in
|
||
|
|
let salt = Mirage_crypto_rng.generate Hash.digest_size in
|
||
|
|
let key = Pbkdf.pbkdf2 ~prf:(mac :> Digestif.hash') ~password ~salt ~count ~dk_len in
|
||
|
|
let key = Mirage_crypto.AES.CBC.of_secret key in
|
||
|
|
let padded_data = pad bs data in
|
||
|
|
let enc_data =
|
||
|
|
Mirage_crypto.AES.CBC.encrypt ~key ~iv padded_data
|
||
|
|
in
|
||
|
|
let kdf = Algorithm.PBKDF2 (salt, count, None, Algorithm.of_hmac mac) in
|
||
|
|
Algorithm.PBES2 (kdf, enc), enc_data
|
||
|
|
|
||
|
|
let decrypt algo password data =
|
||
|
|
let open Algorithm in
|
||
|
|
match algo with
|
||
|
|
| SHA_RC4_128 _ | SHA_RC4_40 _
|
||
|
|
| SHA_3DES_CBC _ | SHA_2DES_CBC _
|
||
|
|
| SHA_RC2_128_CBC _ | SHA_RC2_40_CBC _ -> pkcs12_decrypt algo password data
|
||
|
|
| PBES2 (kdf, enc) -> pkcs5_2_decrypt kdf enc password data
|
||
|
|
| _ -> Error (`Msg "unsupported encryption algorithm")
|
||
|
|
|
||
|
|
let password_decrypt password (algo, data) =
|
||
|
|
match data with
|
||
|
|
| None -> Error (`Msg "no data to decrypt")
|
||
|
|
| Some data -> decrypt algo password data
|
||
|
|
|
||
|
|
let verify password (data, ((algorithm, digest), salt, iterations)) =
|
||
|
|
let* hash =
|
||
|
|
Option.to_result
|
||
|
|
~none:(`Msg "unsupported hash algorithm")
|
||
|
|
(Algorithm.to_hash algorithm)
|
||
|
|
in
|
||
|
|
let module Hash = (val (Digestif.module_of_hash' (hash :> Digestif.hash'))) in
|
||
|
|
let key =
|
||
|
|
pbes hash `Hmac password salt iterations Hash.digest_size
|
||
|
|
in
|
||
|
|
let computed = Hash.(to_raw_string (hmac_string ~key data)) in
|
||
|
|
if String.equal computed digest then begin
|
||
|
|
let* content = Asn_grammars.err_to_msg (Asn.auth_safe_of_cs data) in
|
||
|
|
let* safe_contents =
|
||
|
|
List.fold_left (fun acc c ->
|
||
|
|
let* acc = acc in
|
||
|
|
match c with
|
||
|
|
| `Data data -> Ok (data :: acc)
|
||
|
|
| `Encrypted data ->
|
||
|
|
let* data = password_decrypt password data in
|
||
|
|
Ok (data :: acc))
|
||
|
|
(Ok []) content
|
||
|
|
in
|
||
|
|
List.fold_left (fun acc cs ->
|
||
|
|
let* acc = acc in
|
||
|
|
let* bags = Asn_grammars.err_to_msg (Asn.safe_contents_of_cs cs) in
|
||
|
|
List.fold_left (fun acc bag ->
|
||
|
|
let* acc = acc in
|
||
|
|
match bag with
|
||
|
|
| `Certificate c, _ -> Ok (`Certificate c :: acc)
|
||
|
|
| `Crl c, _ -> Ok (`Crl c :: acc)
|
||
|
|
| `Private_key p, _ -> Ok (`Private_key p :: acc)
|
||
|
|
| `Encrypted_private_key (algo, enc_data), _ ->
|
||
|
|
let* data = decrypt algo password enc_data in
|
||
|
|
let* p =
|
||
|
|
Asn_grammars.err_to_msg (Private_key.Asn.private_of_octets data)
|
||
|
|
in
|
||
|
|
Ok (`Decrypted_private_key p :: acc))
|
||
|
|
(Ok acc) bags)
|
||
|
|
(Ok []) safe_contents
|
||
|
|
end else
|
||
|
|
Error (`Msg "invalid signature")
|
||
|
|
|
||
|
|
let create ?(mac = `SHA256) ?(algorithm = `AES256_CBC) ?(iterations = 2048) password certificates private_key =
|
||
|
|
let key_fp pub = Public_key.fingerprint pub in
|
||
|
|
let priv_fp = key_fp (Private_key.public private_key) in
|
||
|
|
let attributes = [ Registry.PKCS9.local_key_id, [ priv_fp ]] in
|
||
|
|
let maybe_attr c =
|
||
|
|
if String.equal priv_fp (key_fp (Certificate.public_key c)) then
|
||
|
|
Some attributes
|
||
|
|
else
|
||
|
|
None
|
||
|
|
in
|
||
|
|
let cert_sc =
|
||
|
|
Asn.safe_contents_to_cs (List.map (fun c -> `Certificate c, maybe_attr c) certificates)
|
||
|
|
and priv_sc =
|
||
|
|
let data = Private_key.Asn.private_to_octets private_key in
|
||
|
|
let algo, data = pkcs5_2_encrypt mac iterations algorithm password data in
|
||
|
|
Asn.safe_contents_to_cs [ `Encrypted_private_key (algo, data), Some attributes ]
|
||
|
|
in
|
||
|
|
let cert_sc_enc =
|
||
|
|
let algo, data = pkcs5_2_encrypt mac iterations algorithm password cert_sc in
|
||
|
|
algo, Some data
|
||
|
|
in
|
||
|
|
let auth_data =
|
||
|
|
Asn.auth_safe_to_cs [ `Encrypted cert_sc_enc ; `Data priv_sc ]
|
||
|
|
in
|
||
|
|
let module Hash = (val (Digestif.module_of_hash' (mac :> Digestif.hash'))) in
|
||
|
|
let mac_size = Hash.digest_size in
|
||
|
|
let salt = Mirage_crypto_rng.generate mac_size in
|
||
|
|
let key = pbes mac `Hmac password salt iterations mac_size in
|
||
|
|
let digest = Hash.(to_raw_string (hmac_string ~key auth_data)) in
|
||
|
|
auth_data, ((Algorithm.of_hash mac, digest), salt, iterations)
|
||
|
|
|
||
|
|
let decode_der cs = Asn_grammars.err_to_msg (Asn.pfx_of_cs cs)
|
||
|
|
|
||
|
|
let encode_der = Asn.pfx_to_cs
|