[ANN] castore 0.0.1 – a portable CA Store with no dependencies

Hi folks! :wave: happy to announce castore 0.0.1.

tl;dr
I’m taking a page from the Elixir community’s playbook here, the Ca_store module includes an up-to-date public certificate chain from a generally trustworthy source (eg. Mozilla) that we’ll update via CI and publish automatically to opam periodically.

How to use it
Easy, just opam install castore and in your dune-project make sure to use (castore (>= "0.0.0")) so you automatically upgrade to the latest certificate.

Now you can use it with ocaml-tls and when you need that .pem file you can feed it the contents of Ca_store.pem.

Why we did this
I was building an HTTP client for Riot and realized that to support TLS I’d need to either have custom certificates or bring in ca-store and let it resolve them from the system.

The Elixir community’s approach to this is a lot simpler.

What’s missing/next
The latest .pem file was updated on Dec 12th and I need to build the scheduled CI workflow that’ll update it / publish the lib, so if you’re into crypto (maybe i can nerdsnipe @hannes?) or ci (@ulrikstrid?) then ping me :slight_smile:

Happy hacking! :camel:

/ Leandro

8 Likes

Hi Leandro, is there an example of how to use this? Thanks.

1 Like

You can use it like any other .pem file you’d normally read from disk, but if you want some code, in that HTTP client we’re using some code we copied over from ca-certs to parse this .pem file into something we can use with X509.

I’m considering doing this parsing and conversion in the repo’s CI, so the outputs would be available in Ca_store.cas so you can run:

let cas = List.map X509.Authenticator.decode_pem Ca_store.cas in 
let authenticator = X509.Authenticator.chain_of_trust ~time cas in
let tls_config = Tls.Config.client ~authenticator () in
(* ... *)
1 Like

I spent a little time consolidating that preprocessing code into castore, and 0.0.2 is on its way on opam.

It’ll let you write this:

let decode_pem ca =
  let ca = Cstruct.of_string ca in
  let cert = X509.Certificate.decode_pem ca in
  Result.get_ok cert
in
let cas = List.map decode_pem Ca_store.certificates in
let authenticator = X509.Authenticator.chain_of_trust ~time cas in
let tls_config = Tls.Config.client ~authenticator () in
(* ... *)
1 Like

Could you elaborate what problem is solved here? How does this save you from using custom certificates in your own application and what is the role of the embedded certificate?

1 Like

This solves the issue of needing external dependencies to read in a trusted certificate chain for talking to services over SSL.

In this case, if you’re building a client for anything (HTTP, WS, IRC, etc) that needs an SSL handshake, you can give your users a reasonable default without imposing runtime requirements on their application.

The non-use cases here are:

  • If you want to use custom certificates
  • If you need a set of certificate/private keys for your server
  • Reusing the OS-installed certificates

So if you need either of these you should use X509 directly, or ca-certs.

2 Likes

Given that the trusted cert chain is updated fairly frequently I wonder if maybe publishing to opam for every update would be a bit too much churn and maybe it can be set up to send a new release PR once a month or something.

1 Like

I was wondering what the relation to GitHub - mirage/ca-certs-nss: CA certificates extracted from NSS, to be used with MirageOS is – where I’ve some (locally triggered script) automation to update. It follows the ca bundle from NSS (i.e. what is in Firefox), updates roughly every 3 months.

4 Likes