From 82dcd0d4cbcc9fd24e33ebb139093fb95fe75919 Mon Sep 17 00:00:00 2001 From: Eric Ebinger Date: Fri, 25 Jun 2021 14:25:34 +0100 Subject: [PATCH 1/3] introduce x509-async, helpers for commonly used Async functions --- async/authenticator.ml | 72 ++++++++++++++++++++++++++++++++++++ async/authenticator.mli | 41 ++++++++++++++++++++ async/cRL.ml | 26 +++++++++++++ async/cRL.mli | 39 +++++++++++++++++++ async/certificate.ml | 20 ++++++++++ async/certificate.mli | 13 +++++++ async/distinguished_name.ml | 7 ++++ async/distinguished_name.mli | 10 +++++ async/dune | 6 +++ async/extension.ml | 6 +++ async/extension.mli | 9 +++++ async/import.ml | 31 ++++++++++++++++ async/oCSP.ml | 41 ++++++++++++++++++++ async/oCSP.mli | 42 +++++++++++++++++++++ async/pKCS12.ml | 7 ++++ async/pKCS12.mli | 20 ++++++++++ async/private_key.ml | 18 +++++++++ async/private_key.mli | 18 +++++++++ async/public_key.ml | 17 +++++++++ async/public_key.mli | 18 +++++++++ async/signing_request.ml | 20 ++++++++++ async/signing_request.mli | 29 +++++++++++++++ async/validation.ml | 11 ++++++ async/validation.mli | 14 +++++++ async/x509_async.ml | 13 +++++++ x509-async.opam | 33 +++++++++++++++++ 26 files changed, 581 insertions(+) create mode 100644 async/authenticator.ml create mode 100644 async/authenticator.mli create mode 100644 async/cRL.ml create mode 100644 async/cRL.mli create mode 100644 async/certificate.ml create mode 100644 async/certificate.mli create mode 100644 async/distinguished_name.ml create mode 100644 async/distinguished_name.mli create mode 100644 async/dune create mode 100644 async/extension.ml create mode 100644 async/extension.mli create mode 100644 async/import.ml create mode 100644 async/oCSP.ml create mode 100644 async/oCSP.mli create mode 100644 async/pKCS12.ml create mode 100644 async/pKCS12.mli create mode 100644 async/private_key.ml create mode 100644 async/private_key.mli create mode 100644 async/public_key.ml create mode 100644 async/public_key.mli create mode 100644 async/signing_request.ml create mode 100644 async/signing_request.mli create mode 100644 async/validation.ml create mode 100644 async/validation.mli create mode 100644 async/x509_async.ml create mode 100644 x509-async.opam diff --git a/async/authenticator.ml b/async/authenticator.ml new file mode 100644 index 00000000..c9a8becc --- /dev/null +++ b/async/authenticator.ml @@ -0,0 +1,72 @@ +open! Core +open! Async +open! Import +include X509.Authenticator + +module Param = struct + module Chain_of_trust = struct + type t = + { trust_anchors : [ `File of Filename.t | `Directory of Filename.t ] + ; allowed_hashes : Mirage_crypto.Hash.hash list option + ; crls : Filename.t option + } + + let to_certs = function + | `File file -> Certificate.of_pem_file file + | `Directory directory -> Certificate.of_pem_directory ~directory + ;; + end + + type t = + | Chain_of_trust of Chain_of_trust.t + | Cert_fingerprints of + Mirage_crypto.Hash.hash * ([ `host ] Domain_name.t * string) list + + let ca_file ?allowed_hashes ?crls filename () = + let trust_anchors = `File filename in + Chain_of_trust { trust_anchors; allowed_hashes; crls } + ;; + + let ca_dir ?allowed_hashes ?crls directory_name () = + let trust_anchors = `Directory directory_name in + Chain_of_trust { trust_anchors; allowed_hashes; crls } + ;; + + let cert_fingerprints hash fingerprints = Cert_fingerprints (hash, fingerprints) + + let cleanup_fingerprint fingerprint = + let known_delimiters = [ ':'; ' ' ] in + String.filter fingerprint ~f:(fun c -> + not (List.exists known_delimiters ~f:(Char.equal c))) + |> Cstruct.of_hex + ;; + + let of_cas ~time ({ trust_anchors; allowed_hashes; crls } : Chain_of_trust.t) = + let open Deferred.Or_error.Let_syntax in + let%bind cas = Chain_of_trust.to_certs trust_anchors in + let%map crls = + match crls with + | Some directory -> + let%map crls = CRL.of_pem_dir ~directory in + Some crls + | None -> return None + in + X509.Authenticator.chain_of_trust ?allowed_hashes ?crls ~time cas + ;; + + let cert_fingerprint ~time hash fingerprints = + let fingerprints = + List.map fingerprints ~f:(Tuple.T2.map_snd ~f:cleanup_fingerprint) + in + X509.Authenticator.server_cert_fingerprint ~time ~hash ~fingerprints + ;; + + let time = Fn.compose Ptime.of_float_s Unix.gettimeofday + + let to_authenticator ~time param = + match param with + | Chain_of_trust chain_of_trust -> of_cas ~time chain_of_trust + | Cert_fingerprints (hash, fingerprints) -> + cert_fingerprint ~time hash fingerprints |> Deferred.Or_error.return + ;; +end diff --git a/async/authenticator.mli b/async/authenticator.mli new file mode 100644 index 00000000..823aca77 --- /dev/null +++ b/async/authenticator.mli @@ -0,0 +1,41 @@ +open! Core +open! Async +open! Import + +include module type of struct + include X509.Authenticator +end + +module Param : sig + type t + + val ca_file + : ?allowed_hashes:Mirage_crypto.Hash.hash list + -> ?crls:Filename.t + -> Filename.t + -> unit + -> t + + val ca_dir + : ?allowed_hashes:Mirage_crypto.Hash.hash list + -> ?crls:Filename.t + -> Filename.t + -> unit + -> t + + (** The fingerprint can be collected from a browser or by invoking an openssl command + like 'openssl x509 -in -noout -fingerprint -sha256' *) + val cert_fingerprints + : Mirage_crypto.Hash.hash + -> ([ `host ] Domain_name.t * string) list + -> t + + (** Async programs often don't use [Ptime_clock], so this is provided as a convenience + function. Relies on [Unix.gettimeofday]. *) + val time : unit -> Ptime.t option + + val to_authenticator + : time:(unit -> Ptime.t option) + -> t + -> X509.Authenticator.t Deferred.Or_error.t +end diff --git a/async/cRL.ml b/async/cRL.ml new file mode 100644 index 00000000..b76565d3 --- /dev/null +++ b/async/cRL.ml @@ -0,0 +1,26 @@ +open! Core +open! Async +open! Import +include X509.CRL + +let decode_der = Or_error.lift_result_msg_of_cstruct decode_der +let verification_error_to_string = Fmt.to_to_string pp_verification_error +let sexp_of_verification_error e = sexp_of_string (verification_error_to_string e) + +let revoke ?digest ~issuer ~this_update ?next_update ?extensions revoked_certs key = + revoke ?digest ~issuer ~this_update ?next_update ?extensions revoked_certs key + |> Or_error.of_result_msg +;; + +let revoke_certificate revoked ~this_update ?next_update crl key = + revoke_certificate revoked ~this_update ?next_update crl key |> Or_error.of_result_msg +;; + +let revoke_certificates revoked ~this_update ?next_update crl key = + revoke_certificates revoked ~this_update ?next_update crl key |> Or_error.of_result_msg +;; + +let of_pem_dir ~directory = + load_all_in_directory ~directory ~f:(fun ~contents -> + decode_der ~contents |> Deferred.return) +;; diff --git a/async/cRL.mli b/async/cRL.mli new file mode 100644 index 00000000..ce620a0d --- /dev/null +++ b/async/cRL.mli @@ -0,0 +1,39 @@ +open! Core +open! Async +open! Import + +include module type of struct + include X509.CRL +end + +val decode_der : contents:string -> t Or_error.t +val verification_error_to_string : verification_error -> string +val sexp_of_verification_error : verification_error -> Sexp.t + +val revoke + : ?digest:Mirage_crypto.Hash.hash + -> issuer:X509.Distinguished_name.t + -> this_update:Ptime.t + -> ?next_update:Ptime.t + -> ?extensions:X509.Extension.t + -> revoked_cert list + -> X509.Private_key.t + -> t Or_error.t + +val revoke_certificate + : revoked_cert + -> this_update:Ptime.t + -> ?next_update:Ptime.t + -> t + -> X509.Private_key.t + -> t Or_error.t + +val revoke_certificates + : revoked_cert list + -> this_update:Ptime.t + -> ?next_update:Ptime.t + -> t + -> X509.Private_key.t + -> t Or_error.t + +val of_pem_dir : directory:Filename.t -> t list Deferred.Or_error.t diff --git a/async/certificate.ml b/async/certificate.ml new file mode 100644 index 00000000..c1789cd0 --- /dev/null +++ b/async/certificate.ml @@ -0,0 +1,20 @@ +open! Core +open! Async +open! Import +include X509.Certificate +open Deferred.Or_error.Let_syntax + +let decode_pem_multiple = Or_error.lift_result_msg_of_cstruct decode_pem_multiple +let decode_pem = Or_error.lift_result_msg_of_cstruct decode_pem +let decode_der = Or_error.lift_result_msg_of_cstruct decode_der + +let of_pem_file ca_file = + let%bind contents = file_contents ca_file in + decode_pem_multiple ~contents |> Deferred.return +;; + +let of_pem_directory ~directory = + load_all_in_directory ~directory ~f:(fun ~contents -> + decode_pem_multiple ~contents |> Deferred.return) + >>| List.concat +;; diff --git a/async/certificate.mli b/async/certificate.mli new file mode 100644 index 00000000..1444f47f --- /dev/null +++ b/async/certificate.mli @@ -0,0 +1,13 @@ +open! Core +open! Async +open! Import + +include module type of struct + include X509.Certificate +end + +val decode_pem_multiple : contents:string -> t list Or_error.t +val decode_pem : contents:string -> t Or_error.t +val decode_der : contents:string -> t Or_error.t +val of_pem_file : Filename.t -> t list Deferred.Or_error.t +val of_pem_directory : directory:Filename.t -> t list Deferred.Or_error.t diff --git a/async/distinguished_name.ml b/async/distinguished_name.ml new file mode 100644 index 00000000..548cf092 --- /dev/null +++ b/async/distinguished_name.ml @@ -0,0 +1,7 @@ +open! Core +open! Async +open! Import +include X509.Distinguished_name + +let to_string = Fmt.to_to_string pp +let decode_der = Or_error.lift_result_msg_of_cstruct decode_der diff --git a/async/distinguished_name.mli b/async/distinguished_name.mli new file mode 100644 index 00000000..e4a26f06 --- /dev/null +++ b/async/distinguished_name.mli @@ -0,0 +1,10 @@ +open! Core +open! Async +open! Import + +include module type of struct + include X509.Distinguished_name +end + +val to_string : t -> string +val decode_der : contents:string -> t Or_error.t diff --git a/async/dune b/async/dune new file mode 100644 index 00000000..e59ab43c --- /dev/null +++ b/async/dune @@ -0,0 +1,6 @@ +(library + (name x509_async) + (public_name x509-async) + (preprocess (pps ppx_jane)) + (libraries async async_find core x509)) + diff --git a/async/extension.ml b/async/extension.ml new file mode 100644 index 00000000..248776bb --- /dev/null +++ b/async/extension.ml @@ -0,0 +1,6 @@ +open! Core +open! Async +open! Import +include X509.Extension + +let to_string = Fmt.to_to_string pp diff --git a/async/extension.mli b/async/extension.mli new file mode 100644 index 00000000..3f2704d9 --- /dev/null +++ b/async/extension.mli @@ -0,0 +1,9 @@ +open! Core +open! Async +open! Import + +include module type of struct + include X509.Extension +end + +val to_string : t -> string diff --git a/async/import.ml b/async/import.ml new file mode 100644 index 00000000..3b6c2ef8 --- /dev/null +++ b/async/import.ml @@ -0,0 +1,31 @@ +open! Core +open! Async +open Deferred.Or_error.Let_syntax + +module Or_error = struct + include Or_error + + let of_result ~to_string = Result.map_error ~f:(Fn.compose Error.of_string to_string) + let of_result_msg x = of_result x ~to_string:(fun (`Msg msg) -> msg) + + let lift_result_msg_of_cstruct f ~contents = + f (Cstruct.of_string contents) |> of_result_msg + ;; + + let lift_asn_error_of_cstruct f ~contents = + f (Cstruct.of_string contents) |> of_result ~to_string:(fun (`Parse msg) -> msg) + ;; +end + +let file_contents file = + Deferred.Or_error.try_with ~name:(sprintf "read %s" file) (fun () -> + Reader.file_contents file) +;; + +let load_all_in_directory ~directory ~f = + let options = Async_find.Options.ignore_errors in + let%bind files = Async_find.find_all ~options directory |> Deferred.ok in + Deferred.Or_error.List.map files ~f:(fun (file, (_ : Unix.Stats.t)) -> + let%bind contents = file_contents file in + f ~contents) +;; diff --git a/async/oCSP.ml b/async/oCSP.ml new file mode 100644 index 00000000..347c2d85 --- /dev/null +++ b/async/oCSP.ml @@ -0,0 +1,41 @@ +open! Core +open! Async +open! Import +include X509.OCSP + +module Request = struct + include Request + + let create ?certs ?digest ?requestor_name ?key cert_ids = + create ?certs ?digest ?requestor_name ?key cert_ids |> Or_error.of_result_msg + ;; + + let decode_der = Or_error.lift_asn_error_of_cstruct decode_der +end + +module Response = struct + include Response + + let create_success + ?digest + ?certs + ?response_extensions + private_key + responderID + producedAt + responses + = + create_success + ?digest + ?certs + ?response_extensions + private_key + responderID + producedAt + responses + |> Or_error.of_result_msg + ;; + + let responses t = responses t |> Or_error.of_result_msg + let decode_der = Or_error.lift_asn_error_of_cstruct decode_der +end diff --git a/async/oCSP.mli b/async/oCSP.mli new file mode 100644 index 00000000..115fb470 --- /dev/null +++ b/async/oCSP.mli @@ -0,0 +1,42 @@ +open! Core +open! Async +open! Import + +include module type of struct + include X509.OCSP +end + +module Request : sig + include module type of struct + include X509.OCSP.Request + end + + val create + : ?certs:X509.Certificate.t list + -> ?digest:Mirage_crypto.Hash.hash + -> ?requestor_name:X509.General_name.b + -> ?key:X509.Private_key.t + -> cert_id list + -> t Or_error.t + + val decode_der : contents:string -> t Or_error.t +end + +module Response : sig + include module type of struct + include X509.OCSP.Response + end + + val create_success + : ?digest:Mirage_crypto.Hash.hash + -> ?certs:X509.Certificate.t list + -> ?response_extensions:X509.Extension.t + -> X509.Private_key.t + -> responder_id + -> Ptime.t + -> single_response list + -> t Or_error.t + + val responses : t -> single_response list Or_error.t + val decode_der : contents:string -> t Or_error.t +end diff --git a/async/pKCS12.ml b/async/pKCS12.ml new file mode 100644 index 00000000..ca673afc --- /dev/null +++ b/async/pKCS12.ml @@ -0,0 +1,7 @@ +open! Core +open! Async +open! Import +include X509.PKCS12 + +let decode_der = Or_error.lift_result_msg_of_cstruct decode_der +let verify password t = verify password t |> Or_error.of_result_msg diff --git a/async/pKCS12.mli b/async/pKCS12.mli new file mode 100644 index 00000000..85878c50 --- /dev/null +++ b/async/pKCS12.mli @@ -0,0 +1,20 @@ +open! Core +open! Async +open! Import + +include module type of struct + include X509.PKCS12 +end + +val decode_der : contents:string -> t Or_error.t + +val verify + : string + -> t + -> [ `Certificate of X509.Certificate.t + | `Crl of X509.CRL.t + | `Decrypted_private_key of X509.Private_key.t + | `Private_key of X509.Private_key.t + ] + list + Or_error.t diff --git a/async/private_key.ml b/async/private_key.ml new file mode 100644 index 00000000..64315774 --- /dev/null +++ b/async/private_key.ml @@ -0,0 +1,18 @@ +open! Core +open! Async +open! Import +include X509.Private_key + +let sign hash ?scheme key data = + sign hash ?scheme key data + |> Or_error.of_result_msg + |> Or_error.map ~f:Cstruct.to_string +;; + +let decode_der = Or_error.lift_result_msg_of_cstruct decode_der +let decode_pem = Or_error.lift_result_msg_of_cstruct decode_pem + +let of_pem_file file = + let%map contents = Reader.file_contents file in + decode_pem ~contents +;; diff --git a/async/private_key.mli b/async/private_key.mli new file mode 100644 index 00000000..ee8ce4de --- /dev/null +++ b/async/private_key.mli @@ -0,0 +1,18 @@ +open! Core +open! Async +open! Import + +include module type of struct + include X509.Private_key +end + +val sign + : Mirage_crypto.Hash.hash + -> ?scheme:X509.Key_type.signature_scheme + -> t + -> [ `Digest of Cstruct.t | `Message of Cstruct.t ] + -> string Or_error.t + +val decode_der : contents:string -> t Or_error.t +val decode_pem : contents:string -> t Or_error.t +val of_pem_file : Filename.t -> t Deferred.Or_error.t diff --git a/async/public_key.ml b/async/public_key.ml new file mode 100644 index 00000000..08b5bea5 --- /dev/null +++ b/async/public_key.ml @@ -0,0 +1,17 @@ +open! Core +open! Async +open! Import +include X509.Public_key + +let verify hash ?scheme ~signature key data = + let signature = Cstruct.of_string signature in + let data = + match data with + | `Digest data -> `Digest (Cstruct.of_string data) + | `Message data -> `Message (Cstruct.of_string data) + in + verify hash ?scheme ~signature key data |> Or_error.of_result_msg +;; + +let decode_der = Or_error.lift_result_msg_of_cstruct decode_der +let decode_pem = Or_error.lift_result_msg_of_cstruct decode_pem diff --git a/async/public_key.mli b/async/public_key.mli new file mode 100644 index 00000000..ee72e820 --- /dev/null +++ b/async/public_key.mli @@ -0,0 +1,18 @@ +open! Core +open! Async +open! Import + +include module type of struct + include X509.Public_key +end + +val verify + : Mirage_crypto.Hash.hash + -> ?scheme:X509.Key_type.signature_scheme + -> signature:string + -> t + -> [ `Digest of string | `Message of string ] + -> unit Or_error.t + +val decode_der : contents:string -> t Or_error.t +val decode_pem : contents:string -> t Or_error.t diff --git a/async/signing_request.ml b/async/signing_request.ml new file mode 100644 index 00000000..ea19fd9a --- /dev/null +++ b/async/signing_request.ml @@ -0,0 +1,20 @@ +open! Core +open! Async +open! Import +include X509.Signing_request + +let decode_der ?allowed_hashes der = + Cstruct.of_string der |> decode_der ?allowed_hashes |> Or_error.of_result_msg +;; + +let decode_pem pem = Cstruct.of_string pem |> decode_pem |> Or_error.of_result_msg + +let create subject ?digest ?extensions key = + create subject ?digest ?extensions key |> Or_error.of_result_msg +;; + +let sign ?allowed_hashes ?digest ?serial ?extensions t key issuer ~valid_from ~valid_until + = + sign ?allowed_hashes ?digest ?serial ?extensions t key issuer ~valid_from ~valid_until + |> Or_error.of_result ~to_string:Validation.signature_error_to_string +;; diff --git a/async/signing_request.mli b/async/signing_request.mli new file mode 100644 index 00000000..596f2e96 --- /dev/null +++ b/async/signing_request.mli @@ -0,0 +1,29 @@ +open! Core +open! Async +open! Import + +include module type of struct + include X509.Signing_request +end + +val decode_der : ?allowed_hashes:Mirage_crypto.Hash.hash list -> string -> t Or_error.t +val decode_pem : string -> t Or_error.t + +val create + : X509.Distinguished_name.t + -> ?digest:Mirage_crypto.Hash.hash + -> ?extensions:Ext.t + -> X509.Private_key.t + -> t Or_error.t + +val sign + : ?allowed_hashes:Mirage_crypto.Hash.hash list + -> ?digest:Mirage_crypto.Hash.hash + -> ?serial:Zarith.Z.t + -> ?extensions:X509.Extension.t + -> X509.Signing_request.t + -> X509.Private_key.t + -> X509.Distinguished_name.t + -> valid_from:Ptime.t + -> valid_until:Ptime.t + -> X509.Certificate.t Or_error.t diff --git a/async/validation.ml b/async/validation.ml new file mode 100644 index 00000000..0ed230f8 --- /dev/null +++ b/async/validation.ml @@ -0,0 +1,11 @@ +open! Core +open! Async +open! Import +include X509.Validation + +let ca_error_to_string = Fmt.to_to_string pp_ca_error +let sexp_of_ca_error e = sexp_of_string (ca_error_to_string e) +let signature_error_to_string = Fmt.to_to_string pp_signature_error +let sexp_of_signature_error e = sexp_of_string (signature_error_to_string e) +let validation_error_to_string = Fmt.to_to_string pp_validation_error +let sexp_of_validation_error e = sexp_of_string (validation_error_to_string e) diff --git a/async/validation.mli b/async/validation.mli new file mode 100644 index 00000000..44555a2a --- /dev/null +++ b/async/validation.mli @@ -0,0 +1,14 @@ +open! Core +open! Async +open! Import + +include module type of struct + include X509.Validation +end + +val ca_error_to_string : ca_error -> string +val sexp_of_ca_error : ca_error -> Sexp.t +val signature_error_to_string : signature_error -> string +val sexp_of_signature_error : signature_error -> Sexp.t +val validation_error_to_string : validation_error -> string +val sexp_of_validation_error : validation_error -> Sexp.t diff --git a/async/x509_async.ml b/async/x509_async.ml new file mode 100644 index 00000000..42abb15a --- /dev/null +++ b/async/x509_async.ml @@ -0,0 +1,13 @@ +module Authenticator = Authenticator +module Certificate = Certificate +module CRL = CRL +module Distinguished_name = Distinguished_name +module Extension = Extension +module General_name = X509.General_name +module Host = X509.Host +module PKCS12 = PKCS12 +module Private_key = Private_key +module Public_key = Public_key +module OCSP = OCSP +module Signing_request = Signing_request +module Validation = Validation diff --git a/x509-async.opam b/x509-async.opam new file mode 100644 index 00000000..1e50f5c3 --- /dev/null +++ b/x509-async.opam @@ -0,0 +1,33 @@ +opam-version: "2.0" +maintainer: [ + "Hannes Mehnert " +] +authors: [ + "Hannes Mehnert " + "David Kaloper " +] +license: "BSD-2-Clause" +tags: "org:mirage" +homepage: "https://github.com/mirleft/ocaml-x509" +doc: "https://mirleft.github.io/ocaml-x509/doc" +bug-reports: "https://github.com/mirleft/ocaml-x509/issues" +depends: [ + "ocaml" {>= "4.08.0"} + "dune" {>= "1.2"} + "x509" {= version} + "async" {>= "v0.14"} + "async_unix" {>= "v0.14"} + "async_find" {>= "v0.14"} + "core" {>= "v0.14"} + "ppx_jane" {>= "v0.14"} +] +build: [ + ["dune" "subst"] {dev} + ["dune" "build" "-p" name "-j" jobs] + ["dune" "runtest" "-p" name "-j" jobs] {with-test} +] +dev-repo: "git+https://github.com/mirleft/ocaml-x509.git" +synopsis: "Public Key Infrastructure (RFC 5280, PKCS), Async layer" +description: """ +Async-friendly overlay for commonly used x509 functions +""" From b62da26de3a752483619ac25010fed3b96ad27e1 Mon Sep 17 00:00:00 2001 From: Eric Ebinger Date: Fri, 25 Jun 2021 15:13:06 +0100 Subject: [PATCH 2/3] fixing zarith signature --- async/signing_request.mli | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/async/signing_request.mli b/async/signing_request.mli index 596f2e96..3bb125d4 100644 --- a/async/signing_request.mli +++ b/async/signing_request.mli @@ -19,7 +19,7 @@ val create val sign : ?allowed_hashes:Mirage_crypto.Hash.hash list -> ?digest:Mirage_crypto.Hash.hash - -> ?serial:Zarith.Z.t + -> ?serial:Z.t -> ?extensions:X509.Extension.t -> X509.Signing_request.t -> X509.Private_key.t From 9303148504e245e42a24efa7a1853938f7dddf1a Mon Sep 17 00:00:00 2001 From: Eric Ebinger Date: Fri, 25 Jun 2021 15:29:26 +0100 Subject: [PATCH 3/3] opam-dune-lint --- x509-async.opam | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/x509-async.opam b/x509-async.opam index 1e50f5c3..0cf79ce0 100644 --- a/x509-async.opam +++ b/x509-async.opam @@ -20,6 +20,12 @@ depends: [ "async_find" {>= "v0.14"} "core" {>= "v0.14"} "ppx_jane" {>= "v0.14"} + "alcotest" {with-test & >= 1.4.0} + "cstruct-unix" {with-test & >= 6.0.0} + "mirage-crypto-ec" {with-test & >= 0.10.2} + "mirage-crypto-pk" {with-test & >= 0.10.2} + "mirage-crypto-rng" {with-test & >= 0.10.2} + "ptime" {with-test & >= 0.8.5} ] build: [ ["dune" "subst"] {dev}