I recently noticed that dehydrated certs don't work as trust roots in mozilla:pkix (which is used to validate certificates in Firefox and most software based on Firefox), because mozilla::pkix does not respect end-entity certificates that are marked as trusted in NSS. AIUI, David Keeler from Mozilla says that this is because NSS doesn't support attached name constraints on trust anchors, which introduces a risk that you'll trust a cert for one domain and inadvertently trust it for other domains. David recommends that if you require an end-entity trust anchor, you should use cert_override.txt. Unfortunately, cert_override.txt can't be edited while Firefox is running, so it's not really a replacement for NSS's cert store for us.
AIUI, name-constrained CA's are honored by mozilla:pkix when they're trusted by NSS. (Indeed, that's what tlsrestrict_nss_tool does.) That means we can do this:
Dehydrated cert includes:
Public key
Validity period
Signature
(Note that this is the same data that we currently store, we're just changing the semantics.)
When we rehydrate, we end up with 3 certificates:
Root CA
Keypair locally generated at random
Subject/Issuer CommonName == Namecoin Rehydrated Root CA for example.bit
Self-signed
CA bit enabled
No name constraint
Validity period from dehydrated cert
Private key deleted as soon as Intermediate CA is signed
Inject as trusted for TLS implementations that support name constraints
Do not inject for TLS implementations that don't support name constraints
Intermediate CA
Public key from dehydrated cert
Subject CommonName == Namecoin Rehydrated Intermediate CA for example.bit
Issuer CommonName == Namecoin Rehydrated Root CA for example.bit
Signed by Root CA key (before we delete Root CA's private key)
CA bit enabled
Name constraint, only example.bit permitted
Validity period from dehydrated cert
Inject as trust-neutral for TLS implementations that support name constraints
Do not inject for TLS implementations that don't support name constraints
End-Entity Cert
Public key from dehydrated cert (same as Intermediate CA's public key)
Subject CommonName == example.bit
Issuer CommonName == Namecoin Rehydrated Intermediate CA for example.bit
Signature from dehydrated cert
CA bit disabled
Validity period from dehydrated cert
Do not inject for TLS implementations that support name constraints
Inject as trusted for TLS implementations that don't support name constraints
Why are we using an intermediate CA here? Because the RFC says that name constraints don't apply when they're present in root CA's. (I suspect that they'll be applied anyway, but I'd rather not cause security issues when someone actually is following the letter of the RFC.)
Why are we storing a signature and validity period for the end-entity cert, instead of just a public key (which is all the name-constrained CA needs)? Because macOS doesn't support name constraints, but it does (I think?) support treating non-root-CA certs as trust anchors -- but we need a full end-entity cert for that, not just a public key. So I believe that this construction should work fine on any TLS implementation that supports either name constraints or end-entity trust anchors (doesn't need to be both).
I recently noticed that dehydrated certs don't work as trust roots in mozilla:pkix (which is used to validate certificates in Firefox and most software based on Firefox), because mozilla::pkix does not respect end-entity certificates that are marked as trusted in NSS. AIUI, David Keeler from Mozilla says that this is because NSS doesn't support attached name constraints on trust anchors, which introduces a risk that you'll trust a cert for one domain and inadvertently trust it for other domains. David recommends that if you require an end-entity trust anchor, you should use
cert_override.txt
. Unfortunately,cert_override.txt
can't be edited while Firefox is running, so it's not really a replacement for NSS's cert store for us.AIUI, name-constrained CA's are honored by mozilla:pkix when they're trusted by NSS. (Indeed, that's what
tlsrestrict_nss_tool
does.) That means we can do this:Namecoin Rehydrated Root CA for example.bit
Namecoin Rehydrated Intermediate CA for example.bit
Namecoin Rehydrated Root CA for example.bit
example.bit
Namecoin Rehydrated Intermediate CA for example.bit
Why are we using an intermediate CA here? Because the RFC says that name constraints don't apply when they're present in root CA's. (I suspect that they'll be applied anyway, but I'd rather not cause security issues when someone actually is following the letter of the RFC.)
Why are we storing a signature and validity period for the end-entity cert, instead of just a public key (which is all the name-constrained CA needs)? Because macOS doesn't support name constraints, but it does (I think?) support treating non-root-CA certs as trust anchors -- but we need a full end-entity cert for that, not just a public key. So I believe that this construction should work fine on any TLS implementation that supports either name constraints or end-entity trust anchors (doesn't need to be both).
Thoughts on this?