From 7d22754e8debbdfce230cce91871ee33e489baae Mon Sep 17 00:00:00 2001 From: Hannes Mehnert Date: Wed, 19 Feb 2020 23:09:19 +0100 Subject: [PATCH 1/6] certify: export csr, is_csr, certificate, is_certificate, ca_certificate, is_ca_certificate, is_name since ACME v2 the certificate chain is part of the provisioning: - no need to hardcode the certificates in Dns_certificy_mirage anymore - use the CA certificates from dns (TLSA records again) --- app/ocertify.ml | 12 +-- certify/dns_certify.ml | 114 +++++++++++++++++++-------- certify/dns_certify.mli | 29 ++++++- certify/dune | 2 +- dns-certify.opam | 1 + mirage/certify/dns_certify_mirage.ml | 65 +-------------- 6 files changed, 119 insertions(+), 104 deletions(-) diff --git a/app/ocertify.ml b/app/ocertify.ml index 3ff1b7f0a..6e3959de2 100644 --- a/app/ocertify.ml +++ b/app/ocertify.ml @@ -79,18 +79,18 @@ let jump _ server_ip port hostname more_hostnames dns_key_opt csr key seed bits | _ -> Ok ()) >>= fun () -> (* strategy: unless force is provided, we can request DNS, and if a certificate is present, compare its public key with csr public key *) - let write_certificate cert = - let cert = X509.Certificate.encode_pem cert in + let write_certificate certs = + let data = X509.Certificate.encode_pem_multiple certs in Bos.OS.File.delete cert_filename >>= fun () -> - Bos.OS.File.write cert_filename (Cstruct.to_string cert) + Bos.OS.File.write cert_filename (Cstruct.to_string data) in let sock = Dns_cli.connect_tcp server_ip port in (if force then Ok true else match query_certificate sock public_key hostname with - | Ok x -> + | Ok (server, chain) -> Logs.app (fun m -> m "found cached certificate in DNS"); - write_certificate x >>| fun () -> + write_certificate (server :: chain) >>| fun () -> false | Error `No_tlsa -> Logs.debug (fun m -> m "no TLSA found, sending update"); @@ -119,7 +119,7 @@ let jump _ server_ip port hostname more_hostnames dns_key_opt csr key seed bits Logs.err (fun m -> m "error %a while handling TLSA reply (retrying)" Dns_certify.pp_q_err e); request (pred retries) - | Ok x -> write_certificate x + | Ok (server, chain) -> write_certificate (server :: chain) in request 10) >>| fun () -> Logs.app (fun m -> m "success! your certificate is stored in %a (private key %a, csr %a)" diff --git a/certify/dns_certify.ml b/certify/dns_certify.ml index 00858a844..0ce3f1fe3 100644 --- a/certify/dns_certify.ml +++ b/certify/dns_certify.ml @@ -1,5 +1,44 @@ open Dns +let tlsa_is usage sel typ t = + t.Tlsa.cert_usage = usage && + t.Tlsa.selector = sel && + t.Tlsa.matching_type = typ + +let is_csr t = + tlsa_is Tlsa.Domain_issued_certificate Tlsa.Private Tlsa.No_hash t + +let csr req = + let data = X509.Signing_request.encode_der req in + { + Tlsa.matching_type = Tlsa.No_hash ; + cert_usage = Tlsa.Domain_issued_certificate ; + selector = Tlsa.Private ; + data + } + +let is_certificate t = + tlsa_is Tlsa.Domain_issued_certificate Tlsa.Full_certificate Tlsa.No_hash t + +let certificate cert = + let data = X509.Certificate.encode_der cert in + { + Tlsa.matching_type = Tlsa.No_hash ; + cert_usage = Tlsa.Domain_issued_certificate ; + selector = Tlsa.Full_certificate ; + data + } + +let is_ca_certificate t = + tlsa_is Tlsa.CA_constraint Tlsa.Full_certificate Tlsa.No_hash t + +let ca_certificate data = { + Tlsa.matching_type = Tlsa.No_hash ; + cert_usage = Tlsa.CA_constraint ; + selector = Tlsa.Full_certificate ; + data +} + let signing_request hostname ?(more_hostnames = []) key = let host = Domain_name.to_string hostname in let extensions = @@ -21,9 +60,19 @@ let dns_header rng = let id = Randomconv.int16 rng in (id, Packet.Flags.empty) +let le_label = "_letsencrypt" +and p_label = "_tcp" + +let is_name name = + if Domain_name.count_labels name < 2 then + false + else + Domain_name.(equal_label le_label (get_label_exn name 0) && + equal_label p_label (get_label_exn name 1)) + let letsencrypt_name name = - match Domain_name.(prepend_label (raw name) "_tcp") with - | Ok name' -> Domain_name.prepend_label name' "_letsencrypt" + match Domain_name.(prepend_label (raw name) p_label) with + | Ok name' -> Domain_name.prepend_label name' le_label | Error e -> Error e type u_err = [ `Tsig of Dns_tsig.e | `Bad_reply of Packet.mismatch * Packet.t | `Unexpected_reply of Packet.reply ] @@ -33,17 +82,11 @@ let pp_u_err ppf = function | `Bad_reply (e, res) -> Fmt.pf ppf "bad reply %a: %a" Packet.pp_mismatch e Packet.pp res | `Unexpected_reply r -> Fmt.pf ppf "unexpected reply %a" Packet.pp_reply r -let nsupdate rng now ~host ~keyname ~zone dnskey csr = +let nsupdate rng now ~host ~keyname ~zone dnskey request = match letsencrypt_name host with | Error e -> Error e | Ok host -> - let tlsa = - { Tlsa.cert_usage = Domain_issued_certificate ; - selector = Private ; - matching_type = No_hash ; - data = X509.Signing_request.encode_der csr ; - } - in + let tlsa = csr request in let zone = Packet.Question.create zone Soa and update = let up = @@ -83,26 +126,39 @@ let pp_q_err ppf = function | `Unexpected_reply r -> Fmt.pf ppf "unexpected reply %a" Packet.pp_reply r | `No_tlsa -> Fmt.pf ppf "No TLSA record found" +let tlsas_to_certchain host public_key tlsas = + let certificates, ca_certificates = + Rr_map.Tlsa_set.fold (fun tlsa (certs, cacerts as acc) -> + if is_certificate tlsa || is_ca_certificate tlsa then + match X509.Certificate.decode_der tlsa.Tlsa.data with + | Error (`Msg msg) -> + Logs.warn (fun m -> m "couldn't decode tlsa record %a: %s (%a)" + Domain_name.pp host msg + Cstruct.hexdump_pp tlsa.Tlsa.data); + acc + | Ok cert -> + match is_certificate tlsa, is_ca_certificate tlsa with + | true, _ -> (cert :: certs, cacerts) + | _, true -> (certs, cert :: cacerts) + | _ -> acc + else acc) + tlsas ([], []) + in + let matches_public_key cert = + let key = X509.Certificate.public_key cert in + Cstruct.equal (X509.Public_key.id key) (X509.Public_key.id public_key) + in + match List.find_opt matches_public_key certificates with + | None -> Error `No_tlsa + | Some server_cert -> + match List.rev (X509.Validation.build_paths server_cert ca_certificates) with + | (_server :: chain) :: _ -> Ok (server_cert, chain) + | _ -> Ok (server_cert, []) (* build_paths always returns the server_cert *) + let query rng public_key host = match letsencrypt_name host with | Error e -> Error e | Ok host -> - let good_tlsa tlsa = - tlsa.Tlsa.cert_usage = Domain_issued_certificate - && tlsa.selector = Full_certificate - && tlsa.matching_type = No_hash - in - let parse tlsa = - match X509.Certificate.decode_der tlsa.Tlsa.data with - | Ok cert -> - let keys_equal a b = - Cstruct.equal (X509.Public_key.id a) (X509.Public_key.id b) in - if keys_equal (X509.Certificate.public_key cert) public_key then - Some cert - else - None - | _ -> None - in let header = dns_header rng and question = Packet.Question.create host Tlsa in @@ -116,11 +172,7 @@ let query rng public_key host = | Ok (`Answer (answer, _)) -> begin match Name_rr_map.find host Tlsa answer with | None -> Error `No_tlsa - | Some (_, tlsas) -> - Rr_map.Tlsa_set.(fold (fun tlsa r -> - match parse tlsa, r with Some c, _ -> Ok c | None, x -> x) - (filter good_tlsa tlsas) - (Error `No_tlsa)) + | Some (_, tlsas) -> tlsas_to_certchain host public_key tlsas end | Ok (`Rcode_error (Rcode.NXDomain, Opcode.Query, _)) -> Error `No_tlsa | Ok reply -> Error (`Unexpected_reply reply) diff --git a/certify/dns_certify.mli b/certify/dns_certify.mli index ba6fa5119..a0e342c0c 100644 --- a/certify/dns_certify.mli +++ b/certify/dns_certify.mli @@ -13,6 +13,30 @@ val letsencrypt_name : 'a Domain_name.t -> (** [letsencrypt_name host] is the service name at which we store let's encrypt certificates for the [host]. *) +val is_csr : Dns.Tlsa.t -> bool +(** [is_csr tlsa] is true if [tlsa] is a certificate signing request of + interest for this library. *) + +val csr : X509.Signing_request.t -> Dns.Tlsa.t +(** [csr req] is the signing request [req] encoded as TLSA record. *) + +val is_certificate : Dns.Tlsa.t -> bool +(** [is_certificate tlsa] is true if [tlsa] is a certificate of interest for + this library. *) + +val certificate : X509.Certificate.t -> Dns.Tlsa.t +(** [certificate crt] is the certificate [crt] encoded as TLSA record. *) + +val is_ca_certificate : Dns.Tlsa.t -> bool +(** [is_ca_certificate tlsa] is true if [tlsa] is a CA certificate of interest + for this library. *) + +val ca_certificate : Cstruct.t -> Dns.Tlsa.t +(** [ca_certificate data] is the CA certificate [data] encoded as TLSA record. *) + +val is_name : 'a Domain_name.t -> bool +(** [is_name domain_name] is true if it contains the prefix used in this library. *) + type u_err = [ | `Tsig of Dns_tsig.e | `Bad_reply of Packet.mismatch * Packet.t @@ -49,8 +73,9 @@ val pp_q_err : q_err Fmt.t val query : (int -> Cstruct.t) -> X509.Public_key.t -> [ `host ] Domain_name.t -> - (Cstruct.t * (Cstruct.t -> (X509.Certificate.t, [> q_err ]) result), + (Cstruct.t * + (Cstruct.t -> (X509.Certificate.t * X509.Certificate.t list, [> q_err ]) result), [> `Msg of string ]) result (** [query rng pubkey name] is a [buffer] with a DNS TLSA query for the given [name], and a function that decodes a given answer, either returning a X.509 - certificate or an error. *) + certificate and a chain, or an error. *) diff --git a/certify/dune b/certify/dune index 9b94ae5f3..962a1f5a7 100644 --- a/certify/dune +++ b/certify/dune @@ -2,4 +2,4 @@ (name dns_certify) (public_name dns-certify) (wrapped false) - (libraries dns dns-tsig x509 randomconv)) + (libraries dns dns-tsig x509 randomconv logs)) diff --git a/dns-certify.opam b/dns-certify.opam index 149f58870..25b70ccca 100644 --- a/dns-certify.opam +++ b/dns-certify.opam @@ -22,6 +22,7 @@ depends: [ "mirage-time" {>= "2.0.0"} "mirage-clock" {>= "3.0.0"} "mirage-stack" {>= "2.0.0"} + "logs" ] build: [ diff --git a/mirage/certify/dns_certify_mirage.ml b/mirage/certify/dns_certify_mirage.ml index 5b01da08b..c3d9a5d85 100644 --- a/mirage/certify/dns_certify_mirage.ml +++ b/mirage/certify/dns_certify_mirage.ml @@ -9,62 +9,6 @@ module Make (R : Mirage_random.S) (P : Mirage_clock.PCLOCK) (TIME : Mirage_time. module D = Dns_mirage.Make(S) - let staging = {|-----BEGIN CERTIFICATE----- -MIIEqzCCApOgAwIBAgIRAIvhKg5ZRO08VGQx8JdhT+UwDQYJKoZIhvcNAQELBQAw -GjEYMBYGA1UEAwwPRmFrZSBMRSBSb290IFgxMB4XDTE2MDUyMzIyMDc1OVoXDTM2 -MDUyMzIyMDc1OVowIjEgMB4GA1UEAwwXRmFrZSBMRSBJbnRlcm1lZGlhdGUgWDEw -ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDtWKySDn7rWZc5ggjz3ZB0 -8jO4xti3uzINfD5sQ7Lj7hzetUT+wQob+iXSZkhnvx+IvdbXF5/yt8aWPpUKnPym -oLxsYiI5gQBLxNDzIec0OIaflWqAr29m7J8+NNtApEN8nZFnf3bhehZW7AxmS1m0 -ZnSsdHw0Fw+bgixPg2MQ9k9oefFeqa+7Kqdlz5bbrUYV2volxhDFtnI4Mh8BiWCN -xDH1Hizq+GKCcHsinDZWurCqder/afJBnQs+SBSL6MVApHt+d35zjBD92fO2Je56 -dhMfzCgOKXeJ340WhW3TjD1zqLZXeaCyUNRnfOmWZV8nEhtHOFbUCU7r/KkjMZO9 -AgMBAAGjgeMwgeAwDgYDVR0PAQH/BAQDAgGGMBIGA1UdEwEB/wQIMAYBAf8CAQAw -HQYDVR0OBBYEFMDMA0a5WCDMXHJw8+EuyyCm9Wg6MHoGCCsGAQUFBwEBBG4wbDA0 -BggrBgEFBQcwAYYoaHR0cDovL29jc3Auc3RnLXJvb3QteDEubGV0c2VuY3J5cHQu -b3JnLzA0BggrBgEFBQcwAoYoaHR0cDovL2NlcnQuc3RnLXJvb3QteDEubGV0c2Vu -Y3J5cHQub3JnLzAfBgNVHSMEGDAWgBTBJnSkikSg5vogKNhcI5pFiBh54DANBgkq -hkiG9w0BAQsFAAOCAgEABYSu4Il+fI0MYU42OTmEj+1HqQ5DvyAeyCA6sGuZdwjF -UGeVOv3NnLyfofuUOjEbY5irFCDtnv+0ckukUZN9lz4Q2YjWGUpW4TTu3ieTsaC9 -AFvCSgNHJyWSVtWvB5XDxsqawl1KzHzzwr132bF2rtGtazSqVqK9E07sGHMCf+zp -DQVDVVGtqZPHwX3KqUtefE621b8RI6VCl4oD30Olf8pjuzG4JKBFRFclzLRjo/h7 -IkkfjZ8wDa7faOjVXx6n+eUQ29cIMCzr8/rNWHS9pYGGQKJiY2xmVC9h12H99Xyf -zWE9vb5zKP3MVG6neX1hSdo7PEAb9fqRhHkqVsqUvJlIRmvXvVKTwNCP3eCjRCCI -PTAvjV+4ni786iXwwFYNz8l3PmPLCyQXWGohnJ8iBm+5nk7O2ynaPVW0U2W+pt2w -SVuvdDM5zGv2f9ltNWUiYZHJ1mmO97jSY/6YfdOUH66iRtQtDkHBRdkNBsMbD+Em -2TgBldtHNSJBfB3pm9FblgOcJ0FSWcUDWJ7vO0+NTXlgrRofRT6pVywzxVo6dND0 -WzYlTWeUVsO40xJqhgUQRER9YLOLxJ0O6C8i0xFxAMKOtSdodMB3RIwt7RFQ0uyt -n5Z5MqkYhlMI3J1tPRTp1nEt9fyGspBOO05gi148Qasp+3N+svqKomoQglNoAxU= ------END CERTIFICATE-----|} - - let production = {|-----BEGIN CERTIFICATE----- -MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/ -MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT -DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow -SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT -GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC -AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF -q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8 -SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0 -Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA -a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj -/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T -AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG -CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv -bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k -c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw -VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC -ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz -MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu -Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF -AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo -uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/ -wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu -X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG -PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6 -KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg== ------END CERTIFICATE-----|} - let nsupdate_csr flow host keyname zone dnskey csr = match Dns_certify.nsupdate R.generate (fun () -> Ptime.v (P.now_d_ps ())) @@ -174,12 +118,5 @@ KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg== S.TCPV4.close (D.flow flow) >|= fun () -> match certificate with | Error e -> Error e - | Ok certificate -> - let ca = match ca with - | `Production -> production - | `Staging -> staging - in - match X509.Certificate.decode_pem (Cstruct.of_string ca) with - | Ok ca -> Ok (`Single ([certificate ; ca], priv)) - | Error (`Msg msg) -> Error (`Msg msg) + | Ok (cert, chain) -> Ok (`Single (cert :: chain, priv)) end From d2974280202817f08006c2d2c87698d1d66bfe63 Mon Sep 17 00:00:00 2001 From: Hannes Mehnert Date: Thu, 20 Feb 2020 00:19:14 +0100 Subject: [PATCH 2/6] dns_certify_mirage: remove 'ca' argument from retrieve_certificate --- mirage/certify/dns_certify_mirage.ml | 5 +---- mirage/certify/dns_certify_mirage.mli | 5 ++--- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/mirage/certify/dns_certify_mirage.ml b/mirage/certify/dns_certify_mirage.ml index c3d9a5d85..6645e38cd 100644 --- a/mirage/certify/dns_certify_mirage.ml +++ b/mirage/certify/dns_certify_mirage.ml @@ -92,10 +92,7 @@ module Make (R : Mirage_random.S) (P : Mirage_clock.PCLOCK) (TIME : Mirage_time. in wait_for_cert () - let retrieve_certificate ?(ca = `Staging) stack ~dns_key ~hostname ?(additional_hostnames = []) ?key_seed dns port = - (match ca with - | `Staging -> Logs.warn (fun m -> m "staging environment - test use only") - | `Production -> Logs.warn (fun m -> m "production environment - take care what you do")); + let retrieve_certificate stack ~dns_key ~hostname ?(additional_hostnames = []) ?key_seed dns port = let keyname, zone, dnskey = match Dns.Dnskey.name_key_of_string dns_key with | Ok (name, key) -> diff --git a/mirage/certify/dns_certify_mirage.mli b/mirage/certify/dns_certify_mirage.mli index 1818f4434..a5634c776 100644 --- a/mirage/certify/dns_certify_mirage.mli +++ b/mirage/certify/dns_certify_mirage.mli @@ -2,11 +2,10 @@ module Make (R : Mirage_random.S) (P : Mirage_clock.PCLOCK) (T : Mirage_time.S) (S : Mirage_stack.V4) : sig val retrieve_certificate : - ?ca:[ `Production | `Staging ] -> S.t -> dns_key:string -> hostname:[ `host ] Domain_name.t -> ?additional_hostnames:[ `host ] Domain_name.t list -> ?key_seed:string -> S.TCPV4.ipaddr -> int -> (Tls.Config.own_cert, [ `Msg of string ]) result Lwt.t - (** [retrieve_certificate ~ca stack ~dns_key ~hostname ~key_seed server_ip port] + (** [retrieve_certificate stack ~dns_key ~hostname ~key_seed server_ip port] generates a RSA private key (using the [key_seed]), a certificate signing request for the given [hostname] and [additional_hostnames], and sends [server_ip] an nsupdate (DNS-TSIG with [dns_key]) with the csr as @@ -15,5 +14,5 @@ module Make (R : Mirage_random.S) (P : Mirage_clock.PCLOCK) (T : Mirage_time.S) signed certificate. If something fails, an exception (via [Lwt.fail]) is raised. This is meant for unikernels that require a valid TLS certificate before they can start their service (i.e. most web servers, mail - servers). Has let's encrypt certificates (expiry March 2021) hardcoded. *) + servers). *) end From ad9d1af312f2118a4f775123565c93252169c474 Mon Sep 17 00:00:00 2001 From: Hannes Mehnert Date: Fri, 13 Mar 2020 18:25:13 +0100 Subject: [PATCH 3/6] use mirage-crypto instead of nocrypto --- app/dune | 6 ++--- app/ocertify.ml | 10 ++++---- dns-certify.opam | 4 ++-- dns-cli.opam | 5 ++-- dns-server.opam | 2 +- dns-tsig.opam | 3 ++- mirage/certify/dns_certify_mirage.ml | 6 ++--- mirage/certify/dune | 2 +- test/dune | 2 +- test/server.ml | 34 ++++++++++++++-------------- test/tsig.ml | 11 ++++----- tsig/dns_tsig.ml | 14 +++++++----- tsig/dune | 2 +- 13 files changed, 51 insertions(+), 50 deletions(-) diff --git a/app/dune b/app/dune index 9e3e3d567..3e835fce1 100644 --- a/app/dune +++ b/app/dune @@ -10,21 +10,21 @@ (public_name ocertify) (package dns-cli) (modules ocertify) - (libraries dns dns-certify dns-cli bos fpath x509 ptime ptime.clock.os nocrypto nocrypto.unix)) + (libraries dns dns-certify dns-cli bos fpath x509 ptime ptime.clock.os mirage-crypto-pk mirage-crypto-rng mirage-crypto-rng.unix)) (executable (name oupdate) (public_name oupdate) (package dns-cli) (modules oupdate) - (libraries dns dns-tsig dns-cli ptime ptime.clock.os nocrypto nocrypto.unix)) + (libraries dns dns-tsig dns-cli ptime ptime.clock.os mirage-crypto mirage-crypto-rng.unix)) (executable (name onotify) (public_name onotify) (package dns-cli) (modules onotify) - (libraries dns dns-tsig dns-cli ptime ptime.clock.os nocrypto nocrypto.unix)) + (libraries dns dns-tsig dns-cli ptime ptime.clock.os mirage-crypto mirage-crypto-rng.unix)) (executable (name ozone) diff --git a/app/ocertify.ml b/app/ocertify.ml index 6e3959de2..608e4c49a 100644 --- a/app/ocertify.ml +++ b/app/ocertify.ml @@ -13,16 +13,16 @@ let find_or_generate_key key_filename bits seed = | None -> None | Some seed -> let seed = Cstruct.of_string seed in - Some Nocrypto.Rng.(create ~seed (module Generators.Fortuna)) + Some Mirage_crypto_rng.(create ~seed (module Fortuna)) in - `RSA (Nocrypto.Rsa.generate ?g bits) + `RSA (Mirage_crypto_pk.Rsa.generate ?g ~bits ()) in let pem = X509.Private_key.encode_pem key in Bos.OS.File.write ~mode:0o600 key_filename (Cstruct.to_string pem) >>= fun () -> Ok key let query_certificate sock public_key fqdn = - match Dns_certify.query Nocrypto.Rng.generate public_key fqdn with + match Dns_certify.query Mirage_crypto_rng.generate public_key fqdn with | Error e -> Error e | Ok (out, cb) -> Dns_cli.send_tcp sock out; @@ -30,7 +30,7 @@ let query_certificate sock public_key fqdn = cb data let nsupdate_csr sock host keyname zone dnskey csr = - match Dns_certify.nsupdate Nocrypto.Rng.generate Ptime_clock.now ~host ~keyname ~zone dnskey csr with + match Dns_certify.nsupdate Mirage_crypto_rng.generate Ptime_clock.now ~host ~keyname ~zone dnskey csr with | Error s -> Error s | Ok (out, cb) -> Dns_cli.send_tcp sock out; @@ -40,7 +40,7 @@ let nsupdate_csr sock host keyname zone dnskey csr = | Error e -> Error (`Msg (Fmt.strf "nsupdate reply error %a" Dns_certify.pp_u_err e)) let jump _ server_ip port hostname more_hostnames dns_key_opt csr key seed bits cert force = - Nocrypto_entropy_unix.initialize (); + Mirage_crypto_rng_unix.initialize (); let fn suffix = function | None -> Fpath.(v (Domain_name.to_string hostname) + suffix) | Some x -> Fpath.v x diff --git a/dns-certify.opam b/dns-certify.opam index 25b70ccca..ece6589b4 100644 --- a/dns-certify.opam +++ b/dns-certify.opam @@ -15,9 +15,9 @@ depends: [ "dns-mirage" {= version} "randomconv" {>= "0.1.2"} "duration" {>= "0.1.2"} - "x509" {>= "0.8.0"} + "x509" {>= "0.10.0"} "lwt" {>= "4.2.1"} - "tls" {>= "0.10.3"} + "tls" {>= "0.11.0"} "mirage-random" {>= "2.0.0"} "mirage-time" {>= "2.0.0"} "mirage-clock" {>= "3.0.0"} diff --git a/dns-cli.opam b/dns-cli.opam index 66c93a2ce..7ef4ff52f 100644 --- a/dns-cli.opam +++ b/dns-cli.opam @@ -19,8 +19,9 @@ depends: [ "bos" {>= "0.2.0"} "cmdliner" {>= "1.0.0"} "fpath" {>= "0.7.2"} - "x509" {>= "0.8.0"} - "nocrypto" {>= "0.5.4"} + "x509" {>= "0.10.0"} + "mirage-crypto" + "mirage-crypto-rng" "hex" {>= "1.4.0"} "ptime" {>= "0.8.5"} "mtime" {>= "1.2.0"} diff --git a/dns-server.opam b/dns-server.opam index 937366c77..9076d273b 100644 --- a/dns-server.opam +++ b/dns-server.opam @@ -18,7 +18,7 @@ depends: [ "mirage-time" {>= "2.0.0"} "mirage-clock" {>= "3.0.0"} "mirage-stack" {>= "2.0.0"} - "nocrypto" {with-test} + "mirage-crypto-rng" {with-test} "alcotest" {with-test} "dns-tsig" {with-test} "metrics" diff --git a/dns-tsig.opam b/dns-tsig.opam index e398a0f68..4344addbe 100644 --- a/dns-tsig.opam +++ b/dns-tsig.opam @@ -11,7 +11,8 @@ depends: [ "dune" {>= "1.2.0"} "ocaml" {>= "4.07.0"} "dns" {= version} - "nocrypto" {>= "0.5.4"} + "mirage-crypto" + "base64" {>= "3.0.0"} "alcotest" {with-test} ] diff --git a/mirage/certify/dns_certify_mirage.ml b/mirage/certify/dns_certify_mirage.ml index 6645e38cd..f02620f78 100644 --- a/mirage/certify/dns_certify_mirage.ml +++ b/mirage/certify/dns_certify_mirage.ml @@ -44,16 +44,16 @@ module Make (R : Mirage_random.S) (P : Mirage_clock.PCLOCK) (TIME : Mirage_time. | None -> (None, true) | Some seed -> let seed = Cstruct.of_string seed in - Some (Nocrypto.Rng.(create ~seed (module Generators.Fortuna))), false + Some (Mirage_crypto_rng.(create ~seed (module Fortuna))), false in - let key = Nocrypto.Rsa.generate ?g 4096 in + let key = Mirage_crypto_pk.Rsa.generate ?g ~bits:4096 () in (if print then let pem = X509.Private_key.encode_pem (`RSA key) in Log.info (fun m -> m "using private key@.%s" (Cstruct.to_string pem))); key in let csr = Dns_certify.signing_request hostname ~more_hostnames (`RSA private_key) in - let public_key = `RSA (Nocrypto.Rsa.pub_of_priv private_key) in + let public_key = `RSA (Mirage_crypto_pk.Rsa.pub_of_priv private_key) in (private_key, public_key, csr) let query_certificate_or_csr flow pub hostname keyname zone dnskey csr = diff --git a/mirage/certify/dune b/mirage/certify/dune index 8563a0ab5..4d4a31668 100644 --- a/mirage/certify/dune +++ b/mirage/certify/dune @@ -2,4 +2,4 @@ (name dns_certify_mirage) (public_name dns-certify.mirage) (wrapped false) - (libraries dns dns-mirage dns-certify tls lwt duration mirage-random mirage-time mirage-clock mirage-stack)) + (libraries dns dns-mirage dns-certify mirage-crypto-rng mirage-crypto-pk tls lwt duration mirage-random mirage-time mirage-clock mirage-stack)) diff --git a/test/dune b/test/dune index 657094f90..24a93f59c 100644 --- a/test/dune +++ b/test/dune @@ -7,7 +7,7 @@ (test (name server) (package dns-server) - (libraries dns-server dns-tsig alcotest nocrypto.unix) + (libraries dns-server dns-tsig alcotest mirage-crypto-rng.unix) (modules server)) (test diff --git a/test/server.ml b/test/server.ml index 52f42b827..d4978ff6a 100644 --- a/test/server.ml +++ b/test/server.ml @@ -482,7 +482,7 @@ module S = struct Dns_trie.empty)) let simple () = - let server = Dns_server.Primary.create ~rng:Nocrypto.Rng.generate data in + let server = Dns_server.Primary.create ~rng:Mirage_crypto_rng.generate data in let _, notifications = Dns_server.Primary.timer server Ptime.epoch ts in Alcotest.(check int __LOC__ 0 (List.length notifications)); let tbn = Dns_server.Primary.to_be_notified server (Domain_name.host_exn (n_of_s "one.com")) in @@ -500,7 +500,7 @@ module S = struct (Dns_trie.insert (n_of_s "ns2.one.com") Rr_map.A (300l, Rr_map.Ipv4_set.singleton (ip_of_s "10.0.0.2")) data) in - let server = Dns_server.Primary.create ~rng:Nocrypto.Rng.generate data in + let server = Dns_server.Primary.create ~rng:Mirage_crypto_rng.generate data in let _, notifications = Dns_server.Primary.timer server Ptime.epoch ts in Alcotest.(check int __LOC__ 1 (List.length notifications)); let tbn = Dns_server.Primary.to_be_notified server (Domain_name.host_exn (n_of_s "one.com")) in @@ -517,7 +517,7 @@ module S = struct (Dns_trie.insert (n_of_s "ns2.two.com") Rr_map.A (300l, Rr_map.Ipv4_set.singleton (ip_of_s "10.0.0.2")) data) in - let server = Dns_server.Primary.create ~rng:Nocrypto.Rng.generate data in + let server = Dns_server.Primary.create ~rng:Mirage_crypto_rng.generate data in let _, notifications = Dns_server.Primary.timer server Ptime.epoch ts in Alcotest.(check int __LOC__ 1 (List.length notifications)); let tbn = Dns_server.Primary.to_be_notified server (Domain_name.host_exn (n_of_s "one.com")) in @@ -535,7 +535,7 @@ module S = struct Dns_trie.insert (n_of_s "one.com") Rr_map.Ns (300l, ns) (Dns_trie.insert (n_of_s "ns2.one.com") Rr_map.A (300l, ips) data) in - let server = Dns_server.Primary.create ~rng:Nocrypto.Rng.generate data in + let server = Dns_server.Primary.create ~rng:Mirage_crypto_rng.generate data in let _, notifications = Dns_server.Primary.timer server Ptime.epoch ts in Alcotest.(check int __LOC__ 2 (List.length notifications)); let tbn = Dns_server.Primary.to_be_notified server (Domain_name.host_exn (n_of_s "one.com")) in @@ -556,7 +556,7 @@ module S = struct (300l, Rr_map.Ipv4_set.singleton (ip_of_s "10.0.0.3")) data)) in - let server = Dns_server.Primary.create ~rng:Nocrypto.Rng.generate data' in + let server = Dns_server.Primary.create ~rng:Mirage_crypto_rng.generate data' in let _, notifications = Dns_server.Primary.timer server Ptime.epoch ts in Alcotest.(check int __LOC__ 2 (List.length notifications)); let tbn = Dns_server.Primary.to_be_notified server (Domain_name.host_exn (n_of_s "one.com")) in @@ -578,7 +578,7 @@ module S = struct (300l, Rr_map.Ipv4_set.(add (ip_of_s "10.0.0.3") (singleton (ip_of_s "10.0.0.4")))) data)) in - let server = Dns_server.Primary.create ~rng:Nocrypto.Rng.generate data' in + let server = Dns_server.Primary.create ~rng:Mirage_crypto_rng.generate data' in let _, notifications = Dns_server.Primary.timer server Ptime.epoch ts in Alcotest.(check int __LOC__ 3 (List.length notifications)); let tbn = Dns_server.Primary.to_be_notified server (Domain_name.host_exn (n_of_s "one.com")) in @@ -609,7 +609,7 @@ module S = struct (300l, Rr_map.Ipv4_set.singleton (ip_of_s "10.0.0.4")) Dns_trie.empty)))))) in - let server = Dns_server.Primary.create ~rng:Nocrypto.Rng.generate data in + let server = Dns_server.Primary.create ~rng:Mirage_crypto_rng.generate data in let _, notifications = Dns_server.Primary.timer server Ptime.epoch ts in Alcotest.(check int __LOC__ 2 (List.length notifications)); let tbn = Dns_server.Primary.to_be_notified server (Domain_name.host_exn (n_of_s "one.com")) in @@ -622,7 +622,7 @@ module S = struct [ n_of_s "1.2.3.4.5.6.7.8._transfer.one.com", { Dnskey.flags = 0 ; algorithm = SHA256 ; key = Cstruct.create 10 } ] in - let server = Dns_server.Primary.create ~rng:Nocrypto.Rng.generate ~keys data in + let server = Dns_server.Primary.create ~rng:Mirage_crypto_rng.generate ~keys data in let _, notifications = Dns_server.Primary.timer server Ptime.epoch ts in Alcotest.(check int __LOC__ 1 (List.length notifications)); let tbn = Dns_server.Primary.to_be_notified server (Domain_name.host_exn (n_of_s "one.com")) in @@ -634,7 +634,7 @@ module S = struct [ n_of_s "1.2.3.4.5.6.7.8._transfer", { Dnskey.flags = 0 ; algorithm = SHA256 ; key = Cstruct.create 10 } ] in - let server = Dns_server.Primary.create ~rng:Nocrypto.Rng.generate ~keys data in + let server = Dns_server.Primary.create ~rng:Mirage_crypto_rng.generate ~keys data in let _, notifications = Dns_server.Primary.timer server Ptime.epoch ts in Alcotest.(check int __LOC__ 1 (List.length notifications)); let tbn = Dns_server.Primary.to_be_notified server (Domain_name.host_exn (n_of_s "one.com")) in @@ -659,7 +659,7 @@ module S = struct (300l, Rr_map.Ipv4_set.(add (ip_of_s "10.0.0.1") (singleton (ip_of_s "192.168.1.1")))) data)) in - let server = Dns_server.Primary.create ~rng:Nocrypto.Rng.generate ~keys data' in + let server = Dns_server.Primary.create ~rng:Mirage_crypto_rng.generate ~keys data' in let _, notifications = Dns_server.Primary.timer server Ptime.epoch ts in Alcotest.(check int __LOC__ 4 (List.length notifications)); let tbn = Dns_server.Primary.to_be_notified server (Domain_name.host_exn (n_of_s "one.com")) in @@ -685,7 +685,7 @@ module S = struct (300l, Rr_map.Ipv4_set.(add (ip_of_s "10.0.0.1") (singleton (ip_of_s "192.168.1.1")))) data)) in - let server = Dns_server.Primary.create ~rng:Nocrypto.Rng.generate ~keys data' in + let server = Dns_server.Primary.create ~rng:Mirage_crypto_rng.generate ~keys data' in let _, notifications = Dns_server.Primary.timer server Ptime.epoch ts in Alcotest.(check int __LOC__ 3 (List.length notifications)); let tbn = Dns_server.Primary.to_be_notified server (Domain_name.host_exn (n_of_s "one.com")) in @@ -712,7 +712,7 @@ module S = struct Dns_trie.insert (n_of_s "two.com") Rr_map.Ns (300l, ns') (Dns_trie.insert (n_of_s "two.com") Rr_map.Soa soa data) in - let server = Dns_server.Primary.create ~rng:Nocrypto.Rng.generate ~keys data' in + let server = Dns_server.Primary.create ~rng:Mirage_crypto_rng.generate ~keys data' in let s', notifications = Dns_server.Primary.timer server Ptime.epoch ts in Alcotest.(check int __LOC__ 1 (List.length notifications)); Alcotest.(check int __LOC__ 2 (List.length (snd (List.hd notifications)))); @@ -733,12 +733,12 @@ module S = struct let test_secondary () = let keys = - let key = Nocrypto.Base64.encode (Cstruct.create 32) in + let key = Cstruct.(create 32 |> to_string |> Base64.encode_string |> of_string) in [ n_of_s "1.2.3.4.9.10.11.12._transfer.one.com", { Dnskey.flags = 0 ; algorithm = SHA256 ; key } ] in let s = - Dns_server.Secondary.create ~rng:Nocrypto.Rng.generate + Dns_server.Secondary.create ~rng:Mirage_crypto_rng.generate ~tsig_verify:Dns_tsig.verify ~tsig_sign:Dns_tsig.sign keys in let s', reqs = Dns_server.Secondary.timer s Ptime.epoch ts in @@ -929,7 +929,7 @@ module A = struct (Dns_trie.insert_map example_zone Dns_trie.empty) let server ?unauthenticated_zone_transfer () = - let p = Primary.create ~rng:Nocrypto.Rng.generate ?unauthenticated_zone_transfer example_trie in + let p = Primary.create ~rng:Mirage_crypto_rng.generate ?unauthenticated_zone_transfer example_trie in Primary.server p let answer_test = Alcotest.testable Packet.Answer.pp Packet.Answer.equal @@ -1048,7 +1048,7 @@ module A = struct let ixfr_test = Alcotest.testable Packet.Ixfr.pp Packet.Ixfr.equal let ixfr () = - let primary = Primary.create ~rng:Nocrypto.Rng.generate example_trie in + let primary = Primary.create ~rng:Mirage_crypto_rng.generate example_trie in let server = Primary.server primary in let cache = Primary.trie_cache primary in let key = Some (n_of_s "foo._transfer.one.com") in @@ -1331,5 +1331,5 @@ let tests = [ ] let () = - Nocrypto_entropy_unix.initialize (); + Mirage_crypto_rng_unix.initialize (); Alcotest.run "DNS server tests" tests diff --git a/test/tsig.ml b/test/tsig.ml index 7f2af8adf..96e7124df 100644 --- a/test/tsig.ml +++ b/test/tsig.ml @@ -17,16 +17,13 @@ let msg = (module M: Alcotest.TESTABLE with type t = M.t) let key = - match - Nocrypto.Base64.decode - (Cstruct.of_string "GSnQJ+fHuzwj5yKzCOkXdISyGQXBUxMrjEjL4Kr1WIs=") - with - | None -> assert false - | Some x -> x + match Base64.decode "GSnQJ+fHuzwj5yKzCOkXdISyGQXBUxMrjEjL4Kr1WIs=" with + | Error _ -> assert false + | Ok x -> Cstruct.of_string x let key_name = Domain_name.of_string_exn "mykey.bla.example" -let of_h = Nocrypto.Uncommon.Cs.of_hex +let of_h = Cstruct.of_hex let tsig ?(fudge = 300) algorithm signed = let fudge = Ptime.Span.of_int_s fudge in diff --git a/tsig/dns_tsig.ml b/tsig/dns_tsig.ml index 9a099164b..0abab0c7a 100644 --- a/tsig/dns_tsig.ml +++ b/tsig/dns_tsig.ml @@ -19,7 +19,7 @@ let compute_tsig name tsig ~key buf = let h = algorithm_to_nc tsig.Tsig.algorithm and data = Tsig.encode_raw raw_name tsig in - Nocrypto.Hash.mac h ~key (Cstruct.append buf data) + Mirage_crypto.Hash.mac h ~key (Cstruct.append buf data) let guard p err = if p then Ok () else Error err @@ -39,9 +39,10 @@ let mac_to_prep = function Cstruct.append l mac let sign ?mac ?max_size name tsig ~key p buf = - match Nocrypto.Base64.decode key.Dnskey.key with - | None -> None - | Some key -> + match Base64.decode (Cstruct.to_string key.Dnskey.key) with + | Error _ -> None + | Ok key -> + let key = Cstruct.of_string key in let prep = mac_to_prep mac in let mac = compute_tsig name tsig ~key (Cstruct.append prep buf) in let tsig = Tsig.with_mac tsig mac in @@ -81,11 +82,12 @@ let sign ?mac ?max_size name tsig ~key p buf = let verify_raw ?mac now name ~key tsig tbs = let name = Domain_name.raw name in - Rresult.R.of_option ~none:(fun () -> Error (`Bad_key (name, tsig))) - (Nocrypto.Base64.decode key.Dnskey.key) >>= fun priv -> + Rresult.R.reword_error (function _ -> `Bad_key (name, tsig)) + (Base64.decode (Cstruct.to_string key.Dnskey.key)) >>= fun priv -> let ac = Cstruct.BE.get_uint16 tbs 10 in Cstruct.BE.set_uint16 tbs 10 (pred ac) ; let prep = mac_to_prep mac in + let priv = Cstruct.of_string priv in let computed = compute_tsig name tsig ~key:priv (Cstruct.append prep tbs) in let mac = tsig.Tsig.mac in guard (Cstruct.len mac = Cstruct.len computed) (`Bad_truncation (name, tsig)) >>= fun () -> diff --git a/tsig/dune b/tsig/dune index 91b068d98..c7341f2c6 100644 --- a/tsig/dune +++ b/tsig/dune @@ -2,4 +2,4 @@ (name dns_tsig) (public_name dns-tsig) (wrapped false) - (libraries dns nocrypto)) + (libraries dns mirage-crypto base64)) From 68d37071241b18ae40083dfef5f4fd1b7d29882f Mon Sep 17 00:00:00 2001 From: Hannes Mehnert Date: Fri, 13 Mar 2020 21:58:59 +0100 Subject: [PATCH 4/6] Apply suggestions from code review Co-Authored-By: C For C's Sake --- test/dune | 2 +- test/server.ml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/dune b/test/dune index 24a93f59c..b8661c6c1 100644 --- a/test/dune +++ b/test/dune @@ -7,7 +7,7 @@ (test (name server) (package dns-server) - (libraries dns-server dns-tsig alcotest mirage-crypto-rng.unix) + (libraries base64 dns-server dns-tsig alcotest mirage-crypto-rng.unix) (modules server)) (test diff --git a/test/server.ml b/test/server.ml index d4978ff6a..302de56c7 100644 --- a/test/server.ml +++ b/test/server.ml @@ -733,7 +733,7 @@ module S = struct let test_secondary () = let keys = - let key = Cstruct.(create 32 |> to_string |> Base64.encode_string |> of_string) in + let key = String.make 32 '\000' |> Base64.encode_string |> Cstruct.of_string in [ n_of_s "1.2.3.4.9.10.11.12._transfer.one.com", { Dnskey.flags = 0 ; algorithm = SHA256 ; key } ] in From cc818f23a34d247903604c53f4fb6150f6960eba Mon Sep 17 00:00:00 2001 From: Hannes Mehnert Date: Fri, 13 Mar 2020 22:14:09 +0100 Subject: [PATCH 5/6] [ci skip] release 4.4.0 --- CHANGES.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index d25318368..e7caf57e7 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,19 @@ +### v4.4.0 (2020-03-13) + +* dns-stub, a new opam package, is a stub resolver #209 @hannesm, review by + @cfcs +* embed IP address of recursive resolver only once #214 @hannesm, fixes #210, + review by @cfcs +* Dns_trie.lookup returns NotAuthoritative if no SOA is present #217 @hannesm, + review by @cfcs +* Secondary server is looked up in trie properly (may be in another zone, which + primary is not authoritative for the other zone) #217 @hannesm, review by + @cfcs +* new function Dns.Dnskey.pp_name_key #218 @hannesm, review by @cfcs +* dns-certify uses new ACME protocol (where the intermediate certificate is + part of the issuance process) #219 @hannesm, review by @cfcs +* dns-certify/dns-tsig/dns-cli: use mirage-crypto #219 @hannesm, review by @cfcs + ### v4.3.1 (2020-01-21) * server (#207, @hannesm, review by @cfcs) From c5f49471d11dd2b0a3b057b7e44d4a3f1650af52 Mon Sep 17 00:00:00 2001 From: Hannes Mehnert Date: Fri, 13 Mar 2020 22:19:03 +0100 Subject: [PATCH 6/6] dns-certify: enhance docstrings as suggested by @cfcs --- certify/dns_certify.mli | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/certify/dns_certify.mli b/certify/dns_certify.mli index a0e342c0c..80dd3fedf 100644 --- a/certify/dns_certify.mli +++ b/certify/dns_certify.mli @@ -14,28 +14,32 @@ val letsencrypt_name : 'a Domain_name.t -> certificates for the [host]. *) val is_csr : Dns.Tlsa.t -> bool -(** [is_csr tlsa] is true if [tlsa] is a certificate signing request of - interest for this library. *) +(** [is_csr tlsa] is true if [tlsa] is a certificate signing request (cert_usage + is Domain_issued_certificate, selector is Private, and matching_type is + No_hash). *) val csr : X509.Signing_request.t -> Dns.Tlsa.t (** [csr req] is the signing request [req] encoded as TLSA record. *) val is_certificate : Dns.Tlsa.t -> bool -(** [is_certificate tlsa] is true if [tlsa] is a certificate of interest for - this library. *) +(** [is_certificate tlsa] is true if [tlsa] is a certificate (cert_usage is + Domain_issued_certificate, selector is Full_certificate, and matching_type + is No_hash). *) val certificate : X509.Certificate.t -> Dns.Tlsa.t (** [certificate crt] is the certificate [crt] encoded as TLSA record. *) val is_ca_certificate : Dns.Tlsa.t -> bool -(** [is_ca_certificate tlsa] is true if [tlsa] is a CA certificate of interest - for this library. *) +(** [is_ca_certificate tlsa] is true if [tlsa] is a CA certificate (cert_usage + is CA_constraint, selector is Full_certificate, and matching_type is + No_hash). *) val ca_certificate : Cstruct.t -> Dns.Tlsa.t (** [ca_certificate data] is the CA certificate [data] encoded as TLSA record. *) val is_name : 'a Domain_name.t -> bool -(** [is_name domain_name] is true if it contains the prefix used in this library. *) +(** [is_name domain_name] is true if it contains the prefix used in this + library ("_letsencrypt._tcp"). *) type u_err = [ | `Tsig of Dns_tsig.e @@ -57,8 +61,8 @@ val nsupdate : (int -> Cstruct.t) -> (unit -> Ptime.t) -> TLSA record containing the certificate signing request. It also returns a function which decodes a given answer, checks it to be a valid reply, and returns either unit or an error. The outgoing packet is signed with the - provided [dnskey], the answer is checked to be signed by the same key. If - the sign operation fails, [nsupdate] returns an error. *) + provided [dnskey], the answer is checked to be signed by the same key. If + the sign operation fails, [nsupdate] returns an error. *) type q_err = [ | `Decode of Packet.err