Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions crates/mtc_api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,15 @@ pub struct AddEntryResponse {
pub timestamp: UnixTimestamp,
}

/// Get-roots response. This is in the same format as the RFC 6962 get-roots
/// response, which is the base64 encoding of the DER-encoded certificate bytes.
#[serde_as]
#[derive(Serialize)]
pub struct GetRootsResponse {
#[serde_as(as = "Vec<Base64>")]
pub certificates: Vec<Vec<u8>>,
}

/// A wrapper around `TlogTilesPendingLogEntry` that supports auxiliary bootstrap data.
#[derive(Deserialize, Serialize, Debug, Clone, PartialEq)]
pub struct BootstrapMtcPendingLogEntry {
Expand Down
3 changes: 3 additions & 0 deletions crates/mtc_worker/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ description = "An implementation of a Merkle Tree Certificates CA on Cloudflare
categories = ["cryptography"]
keywords = ["certificate", "transparency", "crypto", "pki"]

[features]
dev-bootstrap-roots = []

[package.metadata.release]
release = false

Expand Down
48 changes: 48 additions & 0 deletions crates/mtc_worker/dev-bootstrap-roots.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Root certificate for generating test data for Bushbaby.
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 2023 (0x7e7)
Signature Algorithm: ecdsa-with-SHA256
Issuer: C=US, ST=California, L=San Francisco, O=My Corp, CN=My Corp Root CA
Validity
Not Before: Sep 26 20:53:47 2025 GMT
Not After : Sep 26 20:53:47 2035 GMT
Subject: C=US, ST=California, L=San Francisco, O=My Corp, CN=My Corp Root CA
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
pub:
04:da:58:ff:b6:8f:ce:41:79:b2:3e:5d:48:2d:fc:
01:d6:fe:d1:fe:97:91:15:28:9d:64:a7:c3:80:99:
d9:20:92:28:cd:9e:0e:14:57:85:37:0a:e8:2c:9d:
1d:c7:e2:03:04:61:f7:71:b8:33:51:f8:91:69:ad:
cb:38:d2:56:38
ASN1 OID: prime256v1
NIST CURVE: P-256
X509v3 extensions:
X509v3 Key Usage: critical
Digital Signature, Certificate Sign
X509v3 Basic Constraints: critical
CA:TRUE
X509v3 Subject Key Identifier:
9B:6B:39:DD:FB:E2:22:55:F3:20:55:17:53:3D:81:B4:C3:5E:EB:BB
Signature Algorithm: ecdsa-with-SHA256
Signature Value:
30:44:02:20:08:5e:82:d8:d5:a0:d5:6f:27:85:f8:76:8c:05:
2d:9e:e5:30:b9:b8:ca:07:99:a1:b6:91:6e:74:43:0d:a0:3e:
02:20:57:6e:cc:e2:38:b2:8c:65:97:ef:e0:a8:9c:f3:44:86:
7f:1f:7a:47:8a:fc:56:8a:69:32:b0:da:65:c1:63:21
-----BEGIN CERTIFICATE-----
MIIB/TCCAaSgAwIBAgICB+cwCgYIKoZIzj0EAwIwZjELMAkGA1UEBhMCVVMxEzAR
BgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xEDAOBgNV
BAoTB015IENvcnAxGDAWBgNVBAMTD015IENvcnAgUm9vdCBDQTAeFw0yNTA5MjYy
MDUzNDdaFw0zNTA5MjYyMDUzNDdaMGYxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpD
YWxpZm9ybmlhMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRAwDgYDVQQKEwdNeSBD
b3JwMRgwFgYDVQQDEw9NeSBDb3JwIFJvb3QgQ0EwWTATBgcqhkjOPQIBBggqhkjO
PQMBBwNCAATaWP+2j85BebI+XUgt/AHW/tH+l5EVKJ1kp8OAmdkgkijNng4UV4U3
CugsnR3H4gMEYfdxuDNR+JFprcs40lY4o0IwQDAOBgNVHQ8BAf8EBAMCAoQwDwYD
VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUm2s53fviIlXzIFUXUz2BtMNe67swCgYI
KoZIzj0EAwIDRwAwRAIgCF6C2NWg1W8nhfh2jAUtnuUwubjKB5mhtpFudEMNoD4C
IFduzOI4soxll+/gqJzzRIZ/H3pHivxWimkysNplwWMh
-----END CERTIFICATE-----
10 changes: 9 additions & 1 deletion crates/mtc_worker/src/frontend_worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use generic_log_worker::{
};
use mtc_api::{
serialize_signatureless_cert, AddEntryRequest, AddEntryResponse, BootstrapMtcLogEntry,
LandmarkSequence, ID_RDNA_TRUSTANCHOR_ID, LANDMARK_KEY,
GetRootsResponse, LandmarkSequence, ID_RDNA_TRUSTANCHOR_ID, LANDMARK_KEY,
};
use p256::pkcs8::EncodePublicKey;
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -104,6 +104,14 @@ async fn main(req: Request, env: Env, _ctx: Context) -> Result<Response> {
// Now that we've validated the log name, use an inner router to
// handle the request.
Router::with_data(name)
.get_async("/logs/:log/ct/v1/get-roots", |_req, ctx| async move {
Response::from_json(&GetRootsResponse {
certificates: x509_util::certs_to_bytes(
&load_roots(&ctx.env, ctx.data).await?.certs,
)
.unwrap(),
})
})
.post_async("/logs/:log/add-entry", |req, ctx| async move {
add_entry(req, &ctx.env, ctx.data).await
})
Expand Down
31 changes: 31 additions & 0 deletions crates/mtc_worker/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,40 @@ async fn load_roots(env: &Env, name: &str) -> Result<&'static CertPool> {
.await?
.ok_or(format!("{name}: '{CCADB_ROOTS_FILENAME}' not found in KV"))?
};

pool.append_certs_from_pem(pem.as_bytes())
.map_err(|e| format!("failed to add CCADB certs to pool: {e}"))?;

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

Ok(pool)
})
.await
Expand Down
7 changes: 5 additions & 2 deletions crates/mtc_worker/wrangler.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@
"dev": {
"build": {
// Change '--release' to '--dev' to compile with debug symbols.
// DEPLOY_ENV is used in build.rs to select per-environment config and roots.
// DEPLOY_ENV is used in build.rs to select per-environment
// config and roots. Add `--features dev-bootstrap-roots` to the
// worker-build command to enable dev bootstrap roots (which
// should NOT be enabled in a production deployment).
"command": "cargo install -q worker-build && DEPLOY_ENV=dev worker-build --release"
},
"workers_dev": true,
Expand Down Expand Up @@ -113,4 +116,4 @@
]
}
}
}
}
8 changes: 6 additions & 2 deletions crates/x509_util/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,12 @@ impl CertPool {
///
/// Returns an error if there are DER encoding issues.
pub fn append_certs_from_pem(&mut self, input: &[u8]) -> Result<(), DerError> {
for cert in Certificate::load_pem_chain(input)? {
self.add_cert(cert)?;
// Until next x509-cert release, load_pem_chain doesn't support an empty
// input: https://github.com/RustCrypto/formats/pull/1965
if !input.is_empty() {
for cert in Certificate::load_pem_chain(input)? {
self.add_cert(cert)?;
}
}
Ok(())
}
Expand Down