Skip to content

Commit 685c5a8

Browse files
committed
Add dev-roots feature for MTC log
1 parent feae7d5 commit 685c5a8

File tree

6 files changed

+57
-3
lines changed

6 files changed

+57
-3
lines changed

crates/mtc_api/src/lib.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,14 @@ pub struct AddEntryResponse {
123123
pub timestamp: UnixTimestamp,
124124
}
125125

126+
/// Get-roots response.
127+
#[serde_as]
128+
#[derive(Serialize)]
129+
pub struct GetRootsResponse {
130+
#[serde_as(as = "Vec<Base64>")]
131+
pub certificates: Vec<Vec<u8>>,
132+
}
133+
126134
/// A wrapper around `TlogTilesPendingLogEntry` that supports auxiliary bootstrap data.
127135
#[derive(Deserialize, Serialize, Debug, Clone, PartialEq)]
128136
pub struct BootstrapMtcPendingLogEntry {

crates/mtc_worker/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ description = "An implementation of a Merkle Tree Certificates CA on Cloudflare
1111
categories = ["cryptography"]
1212
keywords = ["certificate", "transparency", "crypto", "pki"]
1313

14+
[features]
15+
dev-bootstrap-roots = []
16+
1417
[package.metadata.release]
1518
release = false
1619

crates/mtc_worker/dev-bootstrap-roots.pem

Whitespace-only changes.

crates/mtc_worker/src/frontend_worker.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use generic_log_worker::{
2121
};
2222
use mtc_api::{
2323
serialize_signatureless_cert, AddEntryRequest, AddEntryResponse, BootstrapMtcLogEntry,
24-
LandmarkSequence, ID_RDNA_TRUSTANCHOR_ID, LANDMARK_KEY,
24+
GetRootsResponse, LandmarkSequence, ID_RDNA_TRUSTANCHOR_ID, LANDMARK_KEY,
2525
};
2626
use p256::pkcs8::EncodePublicKey;
2727
use serde::{Deserialize, Serialize};
@@ -104,6 +104,14 @@ async fn main(req: Request, env: Env, _ctx: Context) -> Result<Response> {
104104
// Now that we've validated the log name, use an inner router to
105105
// handle the request.
106106
Router::with_data(name)
107+
.get_async("/logs/:log/ct/v1/get-roots", |_req, ctx| async move {
108+
Response::from_json(&GetRootsResponse {
109+
certificates: x509_util::certs_to_bytes(
110+
&load_roots(&ctx.env, ctx.data).await?.certs,
111+
)
112+
.unwrap(),
113+
})
114+
})
107115
.post_async("/logs/:log/add-entry", |req, ctx| async move {
108116
add_entry(req, &ctx.env, ctx.data).await
109117
})

crates/mtc_worker/src/lib.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,40 @@ async fn load_roots(env: &Env, name: &str) -> Result<&'static CertPool> {
120120
.await?
121121
.ok_or(format!("{name}: '{CCADB_ROOTS_FILENAME}' not found in KV"))?
122122
};
123+
123124
pool.append_certs_from_pem(pem.as_bytes())
124125
.map_err(|e| format!("failed to add CCADB certs to pool: {e}"))?;
125126

127+
// Add additional roots when the 'dev-bootstrap-roots' feature is
128+
// enabled.
129+
//
130+
// A note on the differences between how roots are handled for the
131+
// MTC vs CT applications:
132+
//
133+
// The purpose of CT is to observe certificates but not police them.
134+
// As long as it's not a spam vector, we're generally willing to
135+
// accept any root certificates that have been trusted by at least
136+
// one major root program during the log shard's lifetime. Roots
137+
// aren't removed from the list once they're added in order to keep
138+
// a better record. We have the ability to add in custom roots from
139+
// a per-environment roots file too, in order to support test CAs.
140+
//
141+
// For bootstrap MTC, the roots are meant to ensure that the log
142+
// only accepts bootstrap MTC chains that will be trusted by Chrome,
143+
// since Chrome might reject an entire batch of MTCs if there's a
144+
// single untrusted entry. Thus, we want to keep the trusted roots
145+
// as a subset of Chrome's trust store. We're using Mozilla's CRLite
146+
// filters to check for revocation, so we need to be a subset of
147+
// Mozilla's trust store too. When either root program stops
148+
// trusting a root, we also need to remove it from our trust store.
149+
// Given that, we gate the ability to add in custom roots behind the
150+
// 'dev-bootstrap-roots' feature flag.
151+
#[cfg(feature = "dev-bootstrap-roots")]
152+
{
153+
pool.append_certs_from_pem(include_bytes!("../dev-bootstrap-roots.pem"))
154+
.map_err(|e| format!("failed to add dev certs to pool: {e}"))?;
155+
}
156+
126157
Ok(pool)
127158
})
128159
.await

crates/x509_util/src/lib.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,12 @@ impl CertPool {
104104
///
105105
/// Returns an error if there are DER encoding issues.
106106
pub fn append_certs_from_pem(&mut self, input: &[u8]) -> Result<(), DerError> {
107-
for cert in Certificate::load_pem_chain(input)? {
108-
self.add_cert(cert)?;
107+
// Until next x509-cert release, load_pem_chain doesn't support an empty
108+
// input: https://github.com/RustCrypto/formats/pull/1965
109+
if !input.is_empty() {
110+
for cert in Certificate::load_pem_chain(input)? {
111+
self.add_cert(cert)?;
112+
}
109113
}
110114
Ok(())
111115
}

0 commit comments

Comments
 (0)