696 lines
25 KiB
OCaml
696 lines
25 KiB
OCaml
|
|
|
||
|
|
type key_usage = [
|
||
|
|
| `Digital_signature
|
||
|
|
| `Content_commitment
|
||
|
|
| `Key_encipherment
|
||
|
|
| `Data_encipherment
|
||
|
|
| `Key_agreement
|
||
|
|
| `Key_cert_sign
|
||
|
|
| `CRL_sign
|
||
|
|
| `Encipher_only
|
||
|
|
| `Decipher_only
|
||
|
|
]
|
||
|
|
|
||
|
|
let pp_key_usage ppf ku =
|
||
|
|
Fmt.string ppf
|
||
|
|
(match ku with
|
||
|
|
| `Digital_signature -> "digital signature"
|
||
|
|
| `Content_commitment -> "content commitment"
|
||
|
|
| `Key_encipherment -> "key encipherment"
|
||
|
|
| `Data_encipherment -> "data encipherment"
|
||
|
|
| `Key_agreement -> "key agreement"
|
||
|
|
| `Key_cert_sign -> "key cert sign"
|
||
|
|
| `CRL_sign -> "CRL sign"
|
||
|
|
| `Encipher_only -> "encipher only"
|
||
|
|
| `Decipher_only -> "decipher only")
|
||
|
|
|
||
|
|
type extended_key_usage = [
|
||
|
|
| `Any
|
||
|
|
| `Server_auth
|
||
|
|
| `Client_auth
|
||
|
|
| `Code_signing
|
||
|
|
| `Email_protection
|
||
|
|
| `Ipsec_end
|
||
|
|
| `Ipsec_tunnel
|
||
|
|
| `Ipsec_user
|
||
|
|
| `Time_stamping
|
||
|
|
| `Ocsp_signing
|
||
|
|
| `Other of Asn.oid
|
||
|
|
]
|
||
|
|
|
||
|
|
let pp_extended_key_usage ppf = function
|
||
|
|
| `Any -> Fmt.string ppf "any"
|
||
|
|
| `Server_auth -> Fmt.string ppf "server authentication"
|
||
|
|
| `Client_auth -> Fmt.string ppf "client authentication"
|
||
|
|
| `Code_signing -> Fmt.string ppf "code signing"
|
||
|
|
| `Email_protection -> Fmt.string ppf "email protection"
|
||
|
|
| `Ipsec_end -> Fmt.string ppf "ipsec end"
|
||
|
|
| `Ipsec_tunnel -> Fmt.string ppf "ipsec tunnel"
|
||
|
|
| `Ipsec_user -> Fmt.string ppf "ipsec user"
|
||
|
|
| `Time_stamping -> Fmt.string ppf "time stamping"
|
||
|
|
| `Ocsp_signing -> Fmt.string ppf "ocsp signing"
|
||
|
|
| `Other oid -> Asn.OID.pp ppf oid
|
||
|
|
|
||
|
|
type authority_key_id = string option * General_name.t * string option
|
||
|
|
|
||
|
|
let pp_authority_key_id ppf (id, issuer, serial) =
|
||
|
|
Fmt.pf ppf "identifier %a@ issuer %a@ serial %a@ "
|
||
|
|
Fmt.(option ~none:(any "none") Ohex.pp) id
|
||
|
|
General_name.pp issuer
|
||
|
|
Fmt.(option ~none:(any "none") Ohex.pp) serial
|
||
|
|
|
||
|
|
type priv_key_usage_period = [
|
||
|
|
| `Interval of Ptime.t * Ptime.t
|
||
|
|
| `Not_after of Ptime.t
|
||
|
|
| `Not_before of Ptime.t
|
||
|
|
]
|
||
|
|
|
||
|
|
let pp_priv_key_usage_period ppf =
|
||
|
|
let pp_ptime = Ptime.pp_human ~tz_offset_s:0 () in
|
||
|
|
function
|
||
|
|
| `Interval (start, stop) ->
|
||
|
|
Fmt.pf ppf "from %a till %a" pp_ptime start pp_ptime stop
|
||
|
|
| `Not_after after -> Fmt.pf ppf "not after %a" pp_ptime after
|
||
|
|
| `Not_before before -> Fmt.pf ppf "not before %a" pp_ptime before
|
||
|
|
|
||
|
|
type name_constraint = (General_name.b * int * int option) list
|
||
|
|
|
||
|
|
let pp_name_constraints ppf (permitted, excluded) =
|
||
|
|
let pp_one ppf (General_name.B (k, base), min, max) =
|
||
|
|
Fmt.pf ppf "base %a min %u max %a"
|
||
|
|
(General_name.pp_k k) base min Fmt.(option ~none:(any "none") int) max
|
||
|
|
in
|
||
|
|
Fmt.pf ppf "permitted %a@ excluded %a"
|
||
|
|
Fmt.(list ~sep:(any ", ") pp_one) permitted
|
||
|
|
Fmt.(list ~sep:(any ", ") pp_one) excluded
|
||
|
|
|
||
|
|
type policy = [ `Any | `Something of Asn.oid ]
|
||
|
|
|
||
|
|
let pp_policy ppf = function
|
||
|
|
| `Any -> Fmt.string ppf "any"
|
||
|
|
| `Something oid -> Fmt.pf ppf "some oid %a" Asn.OID.pp oid
|
||
|
|
|
||
|
|
type reason = [
|
||
|
|
| `Unspecified
|
||
|
|
| `Key_compromise
|
||
|
|
| `CA_compromise
|
||
|
|
| `Affiliation_changed
|
||
|
|
| `Superseded
|
||
|
|
| `Cessation_of_operation
|
||
|
|
| `Certificate_hold
|
||
|
|
| `Remove_from_CRL
|
||
|
|
| `Privilege_withdrawn
|
||
|
|
| `AA_compromise
|
||
|
|
]
|
||
|
|
|
||
|
|
let reason_to_int = function
|
||
|
|
| `Unspecified -> 0
|
||
|
|
| `Key_compromise -> 1
|
||
|
|
| `CA_compromise -> 2
|
||
|
|
| `Affiliation_changed -> 3
|
||
|
|
| `Superseded -> 4
|
||
|
|
| `Cessation_of_operation -> 5
|
||
|
|
| `Certificate_hold -> 6
|
||
|
|
(* 7 is not used *)
|
||
|
|
| `Remove_from_CRL -> 8
|
||
|
|
| `Privilege_withdrawn -> 9
|
||
|
|
| `AA_compromise -> 10
|
||
|
|
|
||
|
|
let reason_of_int = function
|
||
|
|
| 0 -> `Unspecified
|
||
|
|
| 1 -> `Key_compromise
|
||
|
|
| 2 -> `CA_compromise
|
||
|
|
| 3 -> `Affiliation_changed
|
||
|
|
| 4 -> `Superseded
|
||
|
|
| 5 -> `Cessation_of_operation
|
||
|
|
| 6 -> `Certificate_hold
|
||
|
|
(* 7 is not used *)
|
||
|
|
| 8 -> `Remove_from_CRL
|
||
|
|
| 9 -> `Privilege_withdrawn
|
||
|
|
| 10 -> `AA_compromise
|
||
|
|
| x -> Asn.S.parse_error "Unknown reason %d" x
|
||
|
|
|
||
|
|
let pp_reason ppf r =
|
||
|
|
Fmt.string ppf (match r with
|
||
|
|
| `Unspecified -> "unspecified"
|
||
|
|
| `Key_compromise -> "key compromise"
|
||
|
|
| `CA_compromise -> "CA compromise"
|
||
|
|
| `Affiliation_changed -> "affiliation changed"
|
||
|
|
| `Superseded -> "superseded"
|
||
|
|
| `Cessation_of_operation -> "cessation of operation"
|
||
|
|
| `Certificate_hold -> "certificate hold"
|
||
|
|
| `Remove_from_CRL -> "remove from CRL"
|
||
|
|
| `Privilege_withdrawn -> "privilege withdrawn"
|
||
|
|
| `AA_compromise -> "AA compromise")
|
||
|
|
|
||
|
|
type distribution_point_name =
|
||
|
|
[ `Full of General_name.t
|
||
|
|
| `Relative of Distinguished_name.t ]
|
||
|
|
|
||
|
|
let pp_distribution_point_name ppf = function
|
||
|
|
| `Full name -> Fmt.pf ppf "full %a" General_name.pp name
|
||
|
|
| `Relative name -> Fmt.pf ppf "relative %a" Distinguished_name.pp name
|
||
|
|
|
||
|
|
type distribution_point =
|
||
|
|
distribution_point_name option *
|
||
|
|
reason list option *
|
||
|
|
General_name.t option
|
||
|
|
|
||
|
|
let pp_distribution_point ppf (name, reasons, issuer) =
|
||
|
|
Fmt.pf ppf "name %a reason %a issuer %a"
|
||
|
|
Fmt.(option ~none:(any "none") pp_distribution_point_name) name
|
||
|
|
Fmt.(option ~none:(any "none") (list ~sep:(any ", ") pp_reason)) reasons
|
||
|
|
Fmt.(option ~none:(any "none") General_name.pp) issuer
|
||
|
|
|
||
|
|
let pp_issuing_distribution_point ppf (name, onlyuser, onlyca, onlysome, indirectcrl, onlyattributes) =
|
||
|
|
Fmt.pf ppf "name %a only user certs %B only CA certs %B only reasons %a indirectcrl %B only attribute certs %B"
|
||
|
|
Fmt.(option ~none:(any "none") pp_distribution_point_name) name
|
||
|
|
onlyuser onlyca
|
||
|
|
Fmt.(option ~none:(any "no") (list ~sep:(any ", ") pp_reason)) onlysome
|
||
|
|
indirectcrl onlyattributes
|
||
|
|
|
||
|
|
type 'a extension = bool * 'a
|
||
|
|
|
||
|
|
type _ k =
|
||
|
|
| Unsupported : Asn.oid -> string extension k
|
||
|
|
| Subject_alt_name : General_name.t extension k
|
||
|
|
| Authority_key_id : authority_key_id extension k
|
||
|
|
| Subject_key_id : string extension k
|
||
|
|
| Issuer_alt_name : General_name.t extension k
|
||
|
|
| Key_usage : key_usage list extension k
|
||
|
|
| Ext_key_usage : extended_key_usage list extension k
|
||
|
|
| Basic_constraints : (bool * int option) extension k
|
||
|
|
| CRL_number : int extension k
|
||
|
|
| Delta_CRL_indicator : int extension k
|
||
|
|
| Priv_key_period : priv_key_usage_period extension k
|
||
|
|
| Name_constraints : (name_constraint * name_constraint) extension k
|
||
|
|
| CRL_distribution_points : distribution_point list extension k
|
||
|
|
| Issuing_distribution_point : (distribution_point_name option * bool * bool * reason list option * bool * bool) extension k
|
||
|
|
| Freshest_CRL : distribution_point list extension k
|
||
|
|
| Reason : reason extension k
|
||
|
|
| Invalidity_date : Ptime.t extension k
|
||
|
|
| Certificate_issuer : General_name.t extension k
|
||
|
|
| Policies : policy list extension k
|
||
|
|
|
||
|
|
let pp_one' : type a. (Format.formatter -> Asn.oid * string -> unit) -> a k -> Format.formatter -> a -> unit = fun custom k ppf v ->
|
||
|
|
let c_to_str b = if b then "critical " else "" in
|
||
|
|
match k, v with
|
||
|
|
| Subject_alt_name, (crit, alt) ->
|
||
|
|
Fmt.pf ppf "%ssubjectAlternativeName %a" (c_to_str crit)
|
||
|
|
General_name.pp alt
|
||
|
|
| Authority_key_id, (crit, kid) ->
|
||
|
|
Fmt.pf ppf "%sauthorityKeyIdentifier %a" (c_to_str crit)
|
||
|
|
pp_authority_key_id kid
|
||
|
|
| Subject_key_id, (crit, kid) ->
|
||
|
|
Fmt.pf ppf "%ssubjectKeyIdentifier %a" (c_to_str crit)
|
||
|
|
Ohex.pp kid
|
||
|
|
| Issuer_alt_name, (crit, alt) ->
|
||
|
|
Fmt.pf ppf "%sissuerAlternativeNames %a" (c_to_str crit)
|
||
|
|
General_name.pp alt
|
||
|
|
| Key_usage, (crit, ku) ->
|
||
|
|
Fmt.pf ppf "%skeyUsage %a" (c_to_str crit)
|
||
|
|
Fmt.(list ~sep:(any ", ") pp_key_usage) ku
|
||
|
|
| Ext_key_usage, (crit, eku) ->
|
||
|
|
Fmt.pf ppf "%sextendedKeyUsage %a" (c_to_str crit)
|
||
|
|
Fmt.(list ~sep:(any ", ") pp_extended_key_usage) eku
|
||
|
|
| Basic_constraints, (crit, (ca, depth)) ->
|
||
|
|
Fmt.pf ppf "%sbasicConstraints CA %B depth %a" (c_to_str crit) ca
|
||
|
|
Fmt.(option ~none:(any "none") int) depth
|
||
|
|
| CRL_number, (crit, i) ->
|
||
|
|
Fmt.pf ppf "%scRLNumber %u" (c_to_str crit) i
|
||
|
|
| Delta_CRL_indicator, (crit, indicator) ->
|
||
|
|
Fmt.pf ppf "%sdeltaCRLIndicator %u" (c_to_str crit) indicator
|
||
|
|
| Priv_key_period, (crit, period) ->
|
||
|
|
Fmt.pf ppf "%sprivateKeyUsagePeriod %a" (c_to_str crit)
|
||
|
|
pp_priv_key_usage_period period
|
||
|
|
| Name_constraints, (crit, ncs) ->
|
||
|
|
Fmt.pf ppf "%snameConstraints %a" (c_to_str crit) pp_name_constraints ncs
|
||
|
|
| CRL_distribution_points, (crit, points) ->
|
||
|
|
Fmt.pf ppf "%scRLDistributionPoints %a" (c_to_str crit)
|
||
|
|
Fmt.(list ~sep:(any "; ") pp_distribution_point) points
|
||
|
|
| Issuing_distribution_point, (crit, point) ->
|
||
|
|
Fmt.pf ppf "%sissuingDistributionPoint %a" (c_to_str crit)
|
||
|
|
pp_issuing_distribution_point point
|
||
|
|
| Freshest_CRL, (crit, points) ->
|
||
|
|
Fmt.pf ppf "%sfreshestCRL %a" (c_to_str crit)
|
||
|
|
Fmt.(list ~sep:(any "; ") pp_distribution_point) points
|
||
|
|
| Reason, (crit, reason) ->
|
||
|
|
Fmt.pf ppf "%sreason %a" (c_to_str crit) pp_reason reason
|
||
|
|
| Invalidity_date, (crit, date) ->
|
||
|
|
Fmt.pf ppf "%sinvalidityDate %a" (c_to_str crit)
|
||
|
|
(Ptime.pp_human ~tz_offset_s:0 ()) date
|
||
|
|
| Certificate_issuer, (crit, name) ->
|
||
|
|
Fmt.pf ppf "%scertificateIssuer %a" (c_to_str crit) General_name.pp name
|
||
|
|
| Policies, (crit, pols) ->
|
||
|
|
Fmt.pf ppf "%spolicies %a" (c_to_str crit)
|
||
|
|
Fmt.(list ~sep:(any "; ") pp_policy) pols
|
||
|
|
| Unsupported oid, (crit, str) ->
|
||
|
|
Fmt.pf ppf "%s%a" (c_to_str crit) custom (oid, str)
|
||
|
|
|
||
|
|
let default_pp_custom_extension ppf (oid, str) =
|
||
|
|
Fmt.pf ppf "unsupported %a: %a" Asn.OID.pp oid Ohex.pp str
|
||
|
|
|
||
|
|
let pp_one k fmt =
|
||
|
|
pp_one' default_pp_custom_extension k fmt
|
||
|
|
|
||
|
|
module ID = Registry.Cert_extn
|
||
|
|
|
||
|
|
let to_oid : type a. a k -> Asn.oid = function
|
||
|
|
| Unsupported oid -> oid
|
||
|
|
| Subject_alt_name -> ID.subject_alternative_name
|
||
|
|
| Authority_key_id -> ID.authority_key_identifier
|
||
|
|
| Subject_key_id -> ID.subject_key_identifier
|
||
|
|
| Issuer_alt_name -> ID.issuer_alternative_name
|
||
|
|
| Key_usage -> ID.key_usage
|
||
|
|
| Ext_key_usage -> ID.extended_key_usage
|
||
|
|
| Basic_constraints -> ID.basic_constraints
|
||
|
|
| CRL_number -> ID.crl_number
|
||
|
|
| Delta_CRL_indicator -> ID.delta_crl_indicator
|
||
|
|
| Priv_key_period -> ID.private_key_usage_period
|
||
|
|
| Name_constraints -> ID.name_constraints
|
||
|
|
| CRL_distribution_points -> ID.crl_distribution_points
|
||
|
|
| Issuing_distribution_point -> ID.issuing_distribution_point
|
||
|
|
| Freshest_CRL -> ID.freshest_crl
|
||
|
|
| Reason -> ID.reason_code
|
||
|
|
| Invalidity_date -> ID.invalidity_date
|
||
|
|
| Certificate_issuer -> ID.certificate_issuer
|
||
|
|
| Policies -> ID.certificate_policies_2
|
||
|
|
|
||
|
|
let critical : type a. a k -> a -> bool = fun k v ->
|
||
|
|
match k, v with
|
||
|
|
| Unsupported _, (b, _) -> b
|
||
|
|
| Subject_alt_name, (b, _) -> b
|
||
|
|
| Authority_key_id, (b, _) -> b
|
||
|
|
| Subject_key_id, (b, _) -> b
|
||
|
|
| Issuer_alt_name, (b, _) -> b
|
||
|
|
| Key_usage, (b, _) -> b
|
||
|
|
| Ext_key_usage, (b, _) -> b
|
||
|
|
| Basic_constraints, (b, _) -> b
|
||
|
|
| CRL_number, (b, _) -> b
|
||
|
|
| Delta_CRL_indicator, (b, _) -> b
|
||
|
|
| Priv_key_period, (b, _) -> b
|
||
|
|
| Name_constraints, (b, _) -> b
|
||
|
|
| CRL_distribution_points, (b, _) -> b
|
||
|
|
| Issuing_distribution_point, (b, _) -> b
|
||
|
|
| Freshest_CRL, (b, _) -> b
|
||
|
|
| Reason, (b, _) -> b
|
||
|
|
| Invalidity_date, (b, _) -> b
|
||
|
|
| Certificate_issuer, (b, _) -> b
|
||
|
|
| Policies, (b, _) -> b
|
||
|
|
|
||
|
|
module K = struct
|
||
|
|
type 'a t = 'a k
|
||
|
|
|
||
|
|
let compare : type a b. a t -> b t -> (a, b) Gmap.Order.t = fun t t' ->
|
||
|
|
let open Gmap.Order in
|
||
|
|
match t, t' with
|
||
|
|
| Subject_alt_name, Subject_alt_name -> Eq
|
||
|
|
| Authority_key_id, Authority_key_id -> Eq
|
||
|
|
| Subject_key_id, Subject_key_id -> Eq
|
||
|
|
| Issuer_alt_name, Issuer_alt_name -> Eq
|
||
|
|
| Key_usage, Key_usage -> Eq
|
||
|
|
| Ext_key_usage, Ext_key_usage -> Eq
|
||
|
|
| Basic_constraints, Basic_constraints -> Eq
|
||
|
|
| CRL_number, CRL_number -> Eq
|
||
|
|
| Delta_CRL_indicator, Delta_CRL_indicator -> Eq
|
||
|
|
| Priv_key_period, Priv_key_period -> Eq
|
||
|
|
| Name_constraints, Name_constraints -> Eq
|
||
|
|
| CRL_distribution_points, CRL_distribution_points -> Eq
|
||
|
|
| Issuing_distribution_point, Issuing_distribution_point -> Eq
|
||
|
|
| Freshest_CRL, Freshest_CRL -> Eq
|
||
|
|
| Reason, Reason -> Eq
|
||
|
|
| Invalidity_date, Invalidity_date -> Eq
|
||
|
|
| Certificate_issuer, Certificate_issuer -> Eq
|
||
|
|
| Policies, Policies -> Eq
|
||
|
|
| Unsupported oid, Unsupported oid' when Asn.OID.equal oid oid' -> Eq
|
||
|
|
| a, b ->
|
||
|
|
let r = Asn.OID.compare (to_oid a) (to_oid b) in
|
||
|
|
if r = 0 then assert false else if r < 0 then Lt else Gt
|
||
|
|
end
|
||
|
|
|
||
|
|
include Gmap.Make(K)
|
||
|
|
|
||
|
|
let pp' custom ppf m =
|
||
|
|
iter (fun (B (k, v)) -> pp_one' custom k ppf v ; Fmt.sp ppf ()) m
|
||
|
|
|
||
|
|
let pp = pp' default_pp_custom_extension
|
||
|
|
|
||
|
|
let hostnames exts =
|
||
|
|
match find Subject_alt_name exts with
|
||
|
|
| None -> None
|
||
|
|
| Some (_, names) ->
|
||
|
|
match General_name.find DNS names with
|
||
|
|
| None -> None
|
||
|
|
| Some xs ->
|
||
|
|
let names =
|
||
|
|
List.fold_left (fun acc s ->
|
||
|
|
match Host.host s with
|
||
|
|
| Some (typ, hostname) -> Host.Set.add (typ, hostname) acc
|
||
|
|
| None -> acc)
|
||
|
|
Host.Set.empty xs
|
||
|
|
in
|
||
|
|
if Host.Set.is_empty names then None else Some names
|
||
|
|
|
||
|
|
let ips exts =
|
||
|
|
match find Subject_alt_name exts with
|
||
|
|
| None -> None
|
||
|
|
| Some (_, names) ->
|
||
|
|
match General_name.find IP names with
|
||
|
|
| None -> None
|
||
|
|
| Some xs ->
|
||
|
|
let ips =
|
||
|
|
List.fold_left (fun acc ip ->
|
||
|
|
match
|
||
|
|
match String.length ip with
|
||
|
|
| 4 -> Result.map (fun ip -> Ipaddr.V4 ip) (Ipaddr.V4.of_octets ip)
|
||
|
|
| 16 -> Result.map (fun ip -> Ipaddr.V6 ip) (Ipaddr.V6.of_octets ip)
|
||
|
|
| _ -> Error (`Msg "unknown IP address kind")
|
||
|
|
with
|
||
|
|
| Ok ip -> Ipaddr.Set.add ip acc
|
||
|
|
| Error _ -> acc)
|
||
|
|
Ipaddr.Set.empty xs
|
||
|
|
in
|
||
|
|
if Ipaddr.Set.is_empty ips then None else Some ips
|
||
|
|
|
||
|
|
module Asn = struct
|
||
|
|
open Asn.S
|
||
|
|
open Asn_grammars
|
||
|
|
|
||
|
|
let display_text =
|
||
|
|
map (function `C1 s -> s | `C2 s -> s | `C3 s -> s | `C4 s -> s)
|
||
|
|
(fun s -> `C4 s)
|
||
|
|
@@
|
||
|
|
choice4 ia5_string visible_string bmp_string utf8_string
|
||
|
|
|
||
|
|
module ID = Registry.Cert_extn
|
||
|
|
|
||
|
|
let key_usage : key_usage list Asn.t = bit_string_flags [
|
||
|
|
0, `Digital_signature
|
||
|
|
; 1, `Content_commitment
|
||
|
|
; 2, `Key_encipherment
|
||
|
|
; 3, `Data_encipherment
|
||
|
|
; 4, `Key_agreement
|
||
|
|
; 5, `Key_cert_sign
|
||
|
|
; 6, `CRL_sign
|
||
|
|
; 7, `Encipher_only
|
||
|
|
; 8, `Decipher_only
|
||
|
|
]
|
||
|
|
|
||
|
|
let ext_key_usage =
|
||
|
|
let open ID.Extended_usage in
|
||
|
|
let f = case_of_oid [
|
||
|
|
(any , `Any ) ;
|
||
|
|
(server_auth , `Server_auth ) ;
|
||
|
|
(client_auth , `Client_auth ) ;
|
||
|
|
(code_signing , `Code_signing ) ;
|
||
|
|
(email_protection , `Email_protection) ;
|
||
|
|
(ipsec_end_system , `Ipsec_end ) ;
|
||
|
|
(ipsec_tunnel , `Ipsec_tunnel ) ;
|
||
|
|
(ipsec_user , `Ipsec_user ) ;
|
||
|
|
(time_stamping , `Time_stamping ) ;
|
||
|
|
(ocsp_signing , `Ocsp_signing ) ]
|
||
|
|
~default:(fun oid -> `Other oid)
|
||
|
|
and g = function
|
||
|
|
| `Any -> any
|
||
|
|
| `Server_auth -> server_auth
|
||
|
|
| `Client_auth -> client_auth
|
||
|
|
| `Code_signing -> code_signing
|
||
|
|
| `Email_protection -> email_protection
|
||
|
|
| `Ipsec_end -> ipsec_end_system
|
||
|
|
| `Ipsec_tunnel -> ipsec_tunnel
|
||
|
|
| `Ipsec_user -> ipsec_user
|
||
|
|
| `Time_stamping -> time_stamping
|
||
|
|
| `Ocsp_signing -> ocsp_signing
|
||
|
|
| `Other oid -> oid
|
||
|
|
in
|
||
|
|
map (List.map f) (List.map g) @@ sequence_of oid
|
||
|
|
|
||
|
|
let basic_constraints =
|
||
|
|
map (fun (a, b) -> (Option.value ~default:false a, b))
|
||
|
|
(fun (a, b) -> ((if a = false then None else Some a), b))
|
||
|
|
@@
|
||
|
|
sequence2
|
||
|
|
(optional ~label:"cA" bool)
|
||
|
|
(optional ~label:"pathLen" int)
|
||
|
|
|
||
|
|
let authority_key_id =
|
||
|
|
map (fun (a, b, c) ->
|
||
|
|
(a, Option.value ~default:General_name.empty b, c))
|
||
|
|
(fun (a, b, c) ->
|
||
|
|
(a, (if General_name.is_empty b then None else Some b), c))
|
||
|
|
@@
|
||
|
|
sequence3
|
||
|
|
(optional ~label:"keyIdentifier" @@ implicit 0 octet_string)
|
||
|
|
(optional ~label:"authCertIssuer" @@ implicit 1 General_name.Asn.gen_names)
|
||
|
|
(optional ~label:"authCertSN" @@ implicit 2 serial)
|
||
|
|
|
||
|
|
let priv_key_usage_period =
|
||
|
|
let f = function
|
||
|
|
| (Some t1, Some t2) -> `Interval (t1, t2)
|
||
|
|
| (Some t1, None ) -> `Not_before t1
|
||
|
|
| (None , Some t2) -> `Not_after t2
|
||
|
|
| _ -> parse_error "empty PrivateKeyUsagePeriod"
|
||
|
|
and g = function
|
||
|
|
| `Interval (t1, t2) -> (Some t1, Some t2)
|
||
|
|
| `Not_before t1 -> (Some t1, None )
|
||
|
|
| `Not_after t2 -> (None , Some t2) in
|
||
|
|
map f g @@
|
||
|
|
sequence2
|
||
|
|
(optional ~label:"notBefore" @@ implicit 0 generalized_time_no_frac_s)
|
||
|
|
(optional ~label:"notAfter" @@ implicit 1 generalized_time_no_frac_s)
|
||
|
|
|
||
|
|
let name_constraints =
|
||
|
|
let subtree =
|
||
|
|
map
|
||
|
|
(fun (base, min, max) -> (base, Option.value ~default:0 min, max))
|
||
|
|
(fun (base, min, max) -> (base, (if min = 0 then None else Some min), max))
|
||
|
|
@@
|
||
|
|
sequence3
|
||
|
|
(required ~label:"base" General_name.Asn.general_name)
|
||
|
|
(optional ~label:"minimum" @@ implicit 0 int)
|
||
|
|
(optional ~label:"maximum" @@ implicit 1 int)
|
||
|
|
in
|
||
|
|
map
|
||
|
|
(fun (a, b) -> (Option.value ~default:[] a, Option.value ~default:[] b))
|
||
|
|
(fun (a, b) -> ((if a = [] then None else Some a),
|
||
|
|
(if b = [] then None else Some b)))
|
||
|
|
@@
|
||
|
|
sequence2
|
||
|
|
(optional ~label:"permittedSubtrees" @@ implicit 0 (sequence_of subtree))
|
||
|
|
(optional ~label:"excludedSubtrees" @@ implicit 1 (sequence_of subtree))
|
||
|
|
|
||
|
|
let cert_policies =
|
||
|
|
let open ID.Cert_policy in
|
||
|
|
let qualifier_info =
|
||
|
|
map (function | (oid, `C1 s) when oid = cps -> s
|
||
|
|
| (oid, `C2 s) when oid = unotice -> s
|
||
|
|
| _ -> parse_error "bad policy qualifier")
|
||
|
|
(function s -> (cps, `C1 s))
|
||
|
|
@@
|
||
|
|
sequence2
|
||
|
|
(required ~label:"qualifierId" oid)
|
||
|
|
(required ~label:"qualifier"
|
||
|
|
(choice2
|
||
|
|
ia5_string
|
||
|
|
@@
|
||
|
|
map (function (_, Some s) -> s | _ -> "#(BLAH BLAH)")
|
||
|
|
(fun s -> (None, Some s))
|
||
|
|
(sequence2
|
||
|
|
(optional ~label:"noticeRef"
|
||
|
|
(sequence2
|
||
|
|
(required ~label:"organization" display_text)
|
||
|
|
(required ~label:"numbers" (sequence_of integer))))
|
||
|
|
(optional ~label:"explicitText" display_text))))
|
||
|
|
in
|
||
|
|
(* "Optional qualifiers, which MAY be present, are not expected to change
|
||
|
|
* the definition of the policy."
|
||
|
|
* Hence, we just drop them. *)
|
||
|
|
sequence_of @@
|
||
|
|
map (function | (oid, _) when oid = any_policy -> `Any
|
||
|
|
| (oid, _) -> `Something oid)
|
||
|
|
(function | `Any -> (any_policy, None)
|
||
|
|
| `Something oid -> (oid, None))
|
||
|
|
@@
|
||
|
|
sequence2
|
||
|
|
(required ~label:"policyIdentifier" oid)
|
||
|
|
(optional ~label:"policyQualifiers" (sequence_of qualifier_info))
|
||
|
|
|
||
|
|
let reason : reason list Asn.t = bit_string_flags [
|
||
|
|
0, `Unspecified
|
||
|
|
; 1, `Key_compromise
|
||
|
|
; 2, `CA_compromise
|
||
|
|
; 3, `Affiliation_changed
|
||
|
|
; 4, `Superseded
|
||
|
|
; 5, `Cessation_of_operation
|
||
|
|
; 6, `Certificate_hold
|
||
|
|
; 7, `Privilege_withdrawn
|
||
|
|
; 8, `AA_compromise
|
||
|
|
]
|
||
|
|
|
||
|
|
let reason_enumerated : reason Asn.t =
|
||
|
|
enumerated reason_of_int reason_to_int
|
||
|
|
|
||
|
|
let distribution_point_name =
|
||
|
|
map (function | `C1 s -> `Full s | `C2 s -> `Relative s)
|
||
|
|
(function | `Full s -> `C1 s | `Relative s -> `C2 s)
|
||
|
|
@@
|
||
|
|
choice2
|
||
|
|
(implicit 0 General_name.Asn.gen_names)
|
||
|
|
(implicit 1 Distinguished_name.Asn.name)
|
||
|
|
|
||
|
|
let distribution_point =
|
||
|
|
sequence3
|
||
|
|
(optional ~label:"distributionPoint" @@ explicit 0 distribution_point_name)
|
||
|
|
(optional ~label:"reasons" @@ implicit 1 reason)
|
||
|
|
(optional ~label:"cRLIssuer" @@ implicit 2 General_name.Asn.gen_names)
|
||
|
|
|
||
|
|
let crl_distribution_points = sequence_of distribution_point
|
||
|
|
|
||
|
|
let issuing_distribution_point =
|
||
|
|
map
|
||
|
|
(fun (a, b, c, d, e, f) ->
|
||
|
|
(a,
|
||
|
|
Option.value ~default:false b,
|
||
|
|
Option.value ~default:false c,
|
||
|
|
d,
|
||
|
|
Option.value ~default:false e,
|
||
|
|
Option.value ~default:false f))
|
||
|
|
(fun (a, b, c, d, e, f) ->
|
||
|
|
(a,
|
||
|
|
(if b = false then None else Some b),
|
||
|
|
(if c = false then None else Some c),
|
||
|
|
d,
|
||
|
|
(if e = false then None else Some e),
|
||
|
|
(if f = false then None else Some f)))
|
||
|
|
@@
|
||
|
|
sequence6
|
||
|
|
(optional ~label:"distributionPoint" @@ explicit 0 distribution_point_name)
|
||
|
|
(optional ~label:"onlyContainsUserCerts" @@ implicit 1 bool)
|
||
|
|
(optional ~label:"onlyContainsCACerts" @@ implicit 2 bool)
|
||
|
|
(optional ~label:"onlySomeReasons" @@ implicit 3 reason)
|
||
|
|
(optional ~label:"indirectCRL" @@ implicit 4 bool)
|
||
|
|
(optional ~label:"onlyContainsAttributeCerts" @@ implicit 5 bool)
|
||
|
|
|
||
|
|
let crl_reason : reason Asn.t =
|
||
|
|
let alist = [
|
||
|
|
0, `Unspecified
|
||
|
|
; 1, `Key_compromise
|
||
|
|
; 2, `CA_compromise
|
||
|
|
; 3, `Affiliation_changed
|
||
|
|
; 4, `Superseded
|
||
|
|
; 5, `Cessation_of_operation
|
||
|
|
; 6, `Certificate_hold
|
||
|
|
; 8, `Remove_from_CRL
|
||
|
|
; 9, `Privilege_withdrawn
|
||
|
|
; 10, `AA_compromise
|
||
|
|
]
|
||
|
|
in
|
||
|
|
let rev = List.map (fun (k, v) -> (v, k)) alist in
|
||
|
|
enumerated (fun i -> List.assoc i alist) (fun k -> List.assoc k rev)
|
||
|
|
|
||
|
|
let gen_names_of_str, gen_names_to_str = project_exn General_name.Asn.gen_names
|
||
|
|
and auth_key_id_of_str, auth_key_id_to_str = project_exn authority_key_id
|
||
|
|
and subj_key_id_of_str, subj_key_id_to_str = project_exn octet_string
|
||
|
|
and key_usage_of_str, key_usage_to_str = project_exn key_usage
|
||
|
|
and e_key_usage_of_str, e_key_usage_to_str = project_exn ext_key_usage
|
||
|
|
and basic_constr_of_str, basic_constr_to_str = project_exn basic_constraints
|
||
|
|
and pr_key_peri_of_str, pr_key_peri_to_str = project_exn priv_key_usage_period
|
||
|
|
and name_con_of_str, name_con_to_str = project_exn name_constraints
|
||
|
|
and crl_distrib_of_str, crl_distrib_to_str = project_exn crl_distribution_points
|
||
|
|
and cert_pol_of_str, cert_pol_to_str = project_exn cert_policies
|
||
|
|
and int_of_str, int_to_str = project_exn int
|
||
|
|
and issuing_dp_of_str, issuing_dp_to_str = project_exn issuing_distribution_point
|
||
|
|
and crl_reason_of_str, crl_reason_to_str = project_exn crl_reason
|
||
|
|
and time_of_str, time_to_str = project_exn generalized_time_no_frac_s
|
||
|
|
|
||
|
|
(* XXX 4.2.1.4. - cert policies! ( and other x509 extensions ) *)
|
||
|
|
|
||
|
|
let reparse_extension_exn crit = case_of_oid_f [
|
||
|
|
(ID.subject_alternative_name,
|
||
|
|
fun cs -> B (Subject_alt_name, (crit, gen_names_of_str cs))) ;
|
||
|
|
(ID.issuer_alternative_name,
|
||
|
|
fun cs -> B (Issuer_alt_name, (crit, gen_names_of_str cs))) ;
|
||
|
|
(ID.authority_key_identifier,
|
||
|
|
fun cs -> B (Authority_key_id, (crit, auth_key_id_of_str cs))) ;
|
||
|
|
(ID.subject_key_identifier,
|
||
|
|
fun cs -> B (Subject_key_id, (crit, subj_key_id_of_str cs))) ;
|
||
|
|
(ID.key_usage,
|
||
|
|
fun cs -> B (Key_usage, (crit, key_usage_of_str cs))) ;
|
||
|
|
(ID.basic_constraints,
|
||
|
|
fun cs -> B (Basic_constraints, (crit, basic_constr_of_str cs))) ;
|
||
|
|
(ID.crl_number,
|
||
|
|
fun cs -> B (CRL_number, (crit, int_of_str cs))) ;
|
||
|
|
(ID.delta_crl_indicator,
|
||
|
|
fun cs -> B (Delta_CRL_indicator, (crit, int_of_str cs))) ;
|
||
|
|
(ID.extended_key_usage,
|
||
|
|
fun cs -> B (Ext_key_usage, (crit, e_key_usage_of_str cs))) ;
|
||
|
|
(ID.private_key_usage_period,
|
||
|
|
fun cs -> B (Priv_key_period, (crit, pr_key_peri_of_str cs))) ;
|
||
|
|
(ID.name_constraints,
|
||
|
|
fun cs -> B (Name_constraints, (crit, name_con_of_str cs))) ;
|
||
|
|
(ID.crl_distribution_points,
|
||
|
|
fun cs -> B (CRL_distribution_points, (crit, crl_distrib_of_str cs))) ;
|
||
|
|
(ID.issuing_distribution_point,
|
||
|
|
fun cs -> B (Issuing_distribution_point, (crit, issuing_dp_of_str cs))) ;
|
||
|
|
(ID.freshest_crl,
|
||
|
|
fun cs -> B (Freshest_CRL, (crit, crl_distrib_of_str cs))) ;
|
||
|
|
(ID.reason_code,
|
||
|
|
fun cs -> B (Reason, (crit, crl_reason_of_str cs))) ;
|
||
|
|
(ID.invalidity_date,
|
||
|
|
fun cs -> B (Invalidity_date, (crit, time_of_str cs))) ;
|
||
|
|
(ID.certificate_issuer,
|
||
|
|
fun cs -> B (Certificate_issuer, (crit, gen_names_of_str cs))) ;
|
||
|
|
(ID.certificate_policies_2,
|
||
|
|
fun cs -> B (Policies, (crit, cert_pol_of_str cs)))
|
||
|
|
]
|
||
|
|
~default:(fun oid -> fun cs -> B (Unsupported oid, (crit, cs)))
|
||
|
|
|
||
|
|
let unparse_extension (B (k, v)) =
|
||
|
|
let v' = match k, v with
|
||
|
|
| Subject_alt_name, (_, x) -> gen_names_to_str x
|
||
|
|
| Issuer_alt_name, (_, x) -> gen_names_to_str x
|
||
|
|
| Authority_key_id, (_, x) -> auth_key_id_to_str x
|
||
|
|
| Subject_key_id, (_, x) -> subj_key_id_to_str x
|
||
|
|
| Key_usage, (_, x) -> key_usage_to_str x
|
||
|
|
| Basic_constraints, (_, x) -> basic_constr_to_str x
|
||
|
|
| CRL_number, (_, x) -> int_to_str x
|
||
|
|
| Delta_CRL_indicator, (_, x) -> int_to_str x
|
||
|
|
| Ext_key_usage, (_, x) -> e_key_usage_to_str x
|
||
|
|
| Priv_key_period, (_, x) -> pr_key_peri_to_str x
|
||
|
|
| Name_constraints, (_, x) -> name_con_to_str x
|
||
|
|
| CRL_distribution_points, (_, x) -> crl_distrib_to_str x
|
||
|
|
| Issuing_distribution_point, (_, x) -> issuing_dp_to_str x
|
||
|
|
| Freshest_CRL, (_, x) -> crl_distrib_to_str x
|
||
|
|
| Reason, (_, x) -> crl_reason_to_str x
|
||
|
|
| Invalidity_date, (_, x) -> time_to_str x
|
||
|
|
| Certificate_issuer, (_, x) -> gen_names_to_str x
|
||
|
|
| Policies, (_, x) -> cert_pol_to_str x
|
||
|
|
| Unsupported _, (_, x) -> x
|
||
|
|
in
|
||
|
|
to_oid k, critical k v, v'
|
||
|
|
|
||
|
|
let extensions_der =
|
||
|
|
let extension =
|
||
|
|
let f (oid, crit, cs) =
|
||
|
|
reparse_extension_exn (Option.value ~default:false crit) (oid, cs)
|
||
|
|
and g b =
|
||
|
|
let oid, crit, cs = unparse_extension b in
|
||
|
|
(oid, (if crit = false then None else Some crit), cs)
|
||
|
|
in
|
||
|
|
map f g @@
|
||
|
|
sequence3
|
||
|
|
(required ~label:"id" oid)
|
||
|
|
(optional ~label:"critical" bool) (* default false *)
|
||
|
|
(required ~label:"value" octet_string)
|
||
|
|
in
|
||
|
|
let f exts =
|
||
|
|
List.fold_left (fun map (B (k, v)) ->
|
||
|
|
match add_unless_bound k v map with
|
||
|
|
| None -> parse_error "%a already bound" (pp_one k) v
|
||
|
|
| Some b -> b)
|
||
|
|
empty exts
|
||
|
|
and g map = bindings map
|
||
|
|
in
|
||
|
|
map f g @@ sequence_of extension
|
||
|
|
end
|