type _ k = | Other : Asn.oid -> string list k | Rfc_822 : string list k | DNS : string list k | X400_address : unit k | Directory : Distinguished_name.t list k | EDI_party : (string option * string) list k | URI : string list k | IP : string list k | Registered_id : Asn.oid list k 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 | Rfc_822, Rfc_822 -> Eq | Rfc_822, _ -> Lt | _, Rfc_822 -> Gt | DNS, DNS -> Eq | DNS, _ -> Lt | _, DNS -> Gt | X400_address, X400_address -> Eq | X400_address, _ -> Lt | _, X400_address -> Gt | Directory, Directory -> Eq | Directory, _ -> Lt | _, Directory -> Gt | EDI_party, EDI_party -> Eq | EDI_party, _ -> Lt | _, EDI_party -> Gt | URI, URI -> Eq | URI, _ -> Lt | _, URI -> Gt | IP, IP -> Eq | IP, _ -> Lt | _, IP -> Gt | Registered_id, Registered_id -> Eq | Registered_id, _ -> Lt | _, Registered_id -> Gt | Other a, Other b -> match Asn.OID.compare a b with | 0 -> Eq | x when x < 0 -> Lt | _ -> Gt end include Gmap.Make(K) let pp_k : type a. a k -> Format.formatter -> a -> unit = fun k ppf v -> let pp_strs = Fmt.(list ~sep:(any "; ") string) in match k, v with | Rfc_822, x -> Fmt.pf ppf "rfc822 %a" pp_strs x | DNS, x -> Fmt.pf ppf "dns %a" Fmt.(list ~sep:(any "; ") string) x | X400_address, () -> Fmt.string ppf "x400 address" | Directory, x -> Fmt.pf ppf "directory %a" Fmt.(list ~sep:(any "; ") Distinguished_name.pp) x | EDI_party, xs -> Fmt.pf ppf "edi party %a" Fmt.(list ~sep:(any "; ") (pair ~sep:(any ", ") (option ~none:(any "") string) string)) xs | URI, x -> Fmt.pf ppf "uri %a" pp_strs x | IP, x -> Fmt.pf ppf "ip %a" Fmt.(list ~sep:(any ";") (fmt "%S")) x | Registered_id, x -> Fmt.pf ppf "registered id %a" Fmt.(list ~sep:(any ";") Asn.OID.pp) x | Other oid, x -> Fmt.pf ppf "other %a: %a" Asn.OID.pp oid pp_strs x let pp ppf m = iter (fun (B (k, v)) -> pp_k k ppf v ; Fmt.sp ppf ()) m let merge_values : type a. a k -> a -> a -> a = fun k v v' -> match k, v, v' with | Other _, a, b -> a @ b | Registered_id, a, b -> a @ b | IP, a, b -> a @ b | URI, a, b -> a @ b | EDI_party, a, b -> a @ b | Directory, a, b -> a @ b | X400_address, (), () -> () | DNS, a, b -> a @ b | Rfc_822, a, b -> a @ b module Asn = struct open Asn.S (* GeneralName is also pretty pervasive. *) (* OID x ANY. Hunt down the alternatives.... *) (* XXX * Cross-check. NSS seems to accept *all* oids here and just assumes UTF8. * *) let another_name = let open Registry in let f = function | (oid, `C1 n) -> (oid, n) | (oid, `C2 n) -> (oid, n) | (oid, `C3 _) -> (oid, "") and g = function | (oid, "") -> (oid, `C3 ()) | (oid, n ) when Name_extn.is_utf8_id oid -> (oid, `C1 n) | (oid, n ) -> (oid, `C2 n) in map f g @@ sequence2 (required ~label:"type-id" oid) (required ~label:"value" @@ explicit 0 (choice3 utf8_string ia5_string null)) and or_address = null (* Horrible crap, need to fill it. *) let dir_name = let f = function | `C1 s -> s | `C2 s -> s | `C3 s -> s | `C4 s -> s | `C5 s -> s | `C6 s -> s and g s = `C1 s in Asn.S.map f g Distinguished_name.Asn.directory_name let edi_party_name = sequence2 (optional ~label:"nameAssigner" @@ implicit 0 dir_name) (required ~label:"partyName" @@ implicit 1 dir_name) let general_name = let f = function | `C1 (`C1 (oid, x)) -> B (Other oid, [ x ]) | `C1 (`C2 x) -> B (Rfc_822, [ x ]) | `C1 (`C3 x) -> B (DNS, [ x ]) | `C1 (`C4 _x) -> B (X400_address, ()) | `C1 (`C5 x) -> B (Directory, [ x ]) | `C1 (`C6 x) -> B (EDI_party, [ x ]) | `C2 (`C1 x) -> B (URI, [ x ]) | `C2 (`C2 x) -> B (IP, [ x ]) | `C2 (`C3 x) -> B (Registered_id, [ x ]) and g (B (k, v)) = match k, v with | Other oid, [ x ] -> `C1 (`C1 (oid, x)) | Rfc_822, [ x ] -> `C1 (`C2 x) | DNS, [ x ] -> `C1 (`C3 x) | X400_address, () -> `C1 (`C4 ()) | Directory, [ x ] -> `C1 (`C5 x) | EDI_party, [ x ] -> `C1 (`C6 x) | URI, [ x ] -> `C2 (`C1 x) | IP, [ x ] -> `C2 (`C2 x) | Registered_id, [ x ] -> `C2 (`C3 x) | _ -> Asn.S.error (`Parse "bad general name") in map f g @@ choice2 (choice6 (implicit 0 another_name) (implicit 1 ia5_string) (implicit 2 ia5_string) (implicit 3 or_address) (* Everybody uses this as explicit, contrary to x509 (?) *) (explicit 4 Distinguished_name.Asn.name) (implicit 5 edi_party_name)) (choice3 (implicit 6 ia5_string) (implicit 7 octet_string) (implicit 8 oid)) let gen_names = let f exts = List.fold_left (fun map (B (k, v)) -> match find k map with | None -> add k v map | Some b -> add k (merge_values k b v) map) empty exts and g map = List.flatten (List.map (fun (B (k, v)) -> match k, v with | Other oid, xs -> List.map (fun d -> B (Other oid, [ d ])) xs | Registered_id, xs -> List.map (fun d -> B (Registered_id, [ d ])) xs | IP, xs -> List.map (fun d -> B (IP, [ d ])) xs | URI, xs -> List.map (fun d -> B (URI, [ d ])) xs | EDI_party, xs -> List.map (fun d -> B (EDI_party, [ d ])) xs | Directory, xs -> List.map (fun d -> B (Directory, [ d ])) xs | X400_address, () -> [ B (X400_address, ()) ] | DNS, xs -> List.map (fun d -> B (DNS, [ d ])) xs | Rfc_822, xs -> List.map (fun d -> B (Rfc_822, [ d ])) xs) (bindings map)) in map f g @@ sequence_of general_name end