ca_roots
import ca_roots from "ca_roots";ca_roots is the set of certificate authorities tape trusts by default
for TLS: the roots Mozilla’s root store trusts for server authentication,
as DER, compiled into your program. It is the same set on Windows, Linux,
macOS and leivos, so a chain that validates on one validates on all of
them. It changes when tape is released, not with operating system updates.
import x509 from "x509";
import ca_roots from "ca_roots";
var anchors: [ca_roots.COUNT]x509.Cert;
var store: x509.TrustStore = x509.trust_store(anchors[0..ca_roots.COUNT]);
ca_roots.load(&store) or { @trap("ca_roots: store too small"); };
// x509.verify_chain(&store, chain, host, now) ...The module is pure and works in every profile, t0 included. Importing it adds the bundle to your program’s read-only data, about 124 KiB.
Constants
| Constant | |
|---|---|
NSS_VERSION: []const u8 | the NSS release the bundle comes from, e.g. "3.130" |
DATE: []const u8 | that release’s date, "YYYY-MM-DD" |
COUNT: i64 | the number of certificates in the bundle |
DER: []const u8 | the bundle: the certificates’ DER, one after another |
io.println("{ca_roots.COUNT} roots from NSS {ca_roots.NSS_VERSION} ({ca_roots.DATE})");Functions
pub fn certs() -> x509.Der
pub fn load(store: *x509.TrustStore) -> void or x509.X509Errorcerts reads the bundle one certificate at a time (each next is a whole
DER certificate):
var d: x509.Der = ca_roots.certs();
while (!d.is_empty()) {
let t: x509.Tlv = d.next() or { break; };
let c: x509.Cert = x509.parse(t.whole) or { continue; };
io.println(c.common_name() or { "(no common name)" });
}load adds every certificate to store. The store needs room for
COUNT more anchors: give it [ca_roots.COUNT]x509.Cert (about 26 KiB),
or more if you add your own roots too. When it runs out of room load
returns StoreFull, keeping the anchors added so far. The anchors are
views into DER, which lives as long as the program.
What is in it
Every root in Mozilla’s certdata.txt (from the NSS release named by
NSS_VERSION) that Mozilla trusts to issue TLS server certificates, except:
- roots Mozilla trusts only for certificates issued before a cut-off date
(distrust-after), which
x509cannot express; - the few that
x509cannot use (a P-521 key).
Roots that sign themselves with SHA-1 are included: a root’s own signature is never checked; certificates they issue must still be signed with SHA-256 or better.
The file stdlib/ca_roots/SOURCE beside the module records where the
bundle came from: the certdata.txt URL and its SHA-256, the bundle’s
SHA-256, the count, and each left-out root with its reason.
Trusting other roots
The bundle is the default, not the only choice. To also trust a corporate CA, add its DER to the same store (give the store room for it):
const ROOTS: i64 = ca_roots.COUNT + 1; // an array size is a constant's name
var anchors: [ROOTS]x509.Cert;
var store: x509.TrustStore = x509.trust_store(anchors[0..ROOTS]);
ca_roots.load(&store) or { @trap("ca_roots: store too small"); };
store.add(corporate_root_der) or { io.println("corporate root: {err}"); };A program that wants only its own roots doesn’t import ca_roots, and
doesn’t carry the bundle.
Last modified: