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
10 changes: 9 additions & 1 deletion crates/ct_worker/.dev.vars
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
SIGNING_KEY_dev2025h1a="-----BEGIN PRIVATE KEY-----\nMIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQguu9K86g+++gKa7ag\ntkPw5E3xPhTeSkj69l0VL06EeQGhRANCAAQLOWnBI0PojH8rWoAoitlJ+Ip6iQl4\nWycheJdCWsXF2NzbOLM5aFMGpz3Bwm5egkGCzrLbhGW7z9p3FAI0N08o\n-----END PRIVATE KEY-----\n"
WITNESS_KEY_dev2025h1a="-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEIHwiErJNKCNGZL+Osj+O8MqSMiwPP4kdcC4iTpojV9Od\n-----END PRIVATE KEY-----\n"
SIGNING_KEY_dev2025h2a="-----BEGIN PRIVATE KEY-----\nMIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgR2CdNf+JR6afd0gb\n+lMSINqCqiLDb7L88lo1qhxBynOhRANCAARLjKsvuNqvDER1Jasmnfm55/vz1Rgu\nZr8XTHtt8GlbYpac3nK4MTleB44Ap5YzdGnJwJkXbFEYCnaIcUJrg+2o\n-----END PRIVATE KEY-----\n"
WITNESS_KEY_dev2025h2a="-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEINKYH1WadDgPJEXYzLx0OWzNoi4hRcpUnYpoWrTc7BDO\n-----END PRIVATE KEY-----\n"
WITNESS_KEY_dev2025h2a="-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEINKYH1WadDgPJEXYzLx0OWzNoi4hRcpUnYpoWrTc7BDO\n-----END PRIVATE KEY-----\n"
SIGNING_KEY_dev2026h1a="-----BEGIN PRIVATE KEY-----\nMIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgMcpVNLWTILGapBcQ\n0e59OCX+MC3ik6S/o3EzrBvISi2hRANCAATuqQUZCcCoG0yQWPiXy11zQwhUCNjw\nQb7fWqyzNGBZSgeDeUXB1+F1J3x6Nv9wb+PWj91XRYKN5zMpBwoZXrfz\n-----END PRIVATE KEY-----\n"
WITNESS_KEY_dev2026h1a="-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEIGttRSMUB4BfaxIWodXTMiGqLnBMaNAmOdms0gVelXvn\n-----END PRIVATE KEY-----\n"
SIGNING_KEY_dev2026h2a="-----BEGIN PRIVATE KEY-----\nMIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgt14ClEtNlyhyy8IQ\njYA1gV0KH35xcHbaJ5g5tU7TbGqhRANCAAR1lWiAPxLrYQ5OjAWIwaYRDckh2+GD\nS2pyO25lj/lWJg94IY2MY/CaRbrIuWGUWjBRJczMjDBkajeaFC//dpG9\n-----END PRIVATE KEY-----\n"
WITNESS_KEY_dev2026h2a="-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEIFBSB+nqtrOlOg5f3GiVYlt9Q1ni9s+ooqPBDyciEQzw\n-----END PRIVATE KEY-----\n"
SIGNING_KEY_dev2027h1a="-----BEGIN PRIVATE KEY-----\nMIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQggM2mn5ZSIIMwO7XG\nn2t6qcJWBUCx2rO2F6nAdY6OdIahRANCAARQiB2uy0Xl37DU8SROPUaQugrkqwRI\nw3JFQfZql6u7y++P68b5mad7vQShq5Js0kZ0YPV6rRlVJ5elhe2NQ5Dp\n-----END PRIVATE KEY-----\n"
WITNESS_KEY_dev2027h1a="-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEINECRYfEk5MrjaJsb11e05m+5JPBI9u/sjLw8iWanaHI\n-----END PRIVATE KEY-----\n"
SIGNING_KEY_dev2027h2a="-----BEGIN PRIVATE KEY-----\nMIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgiFMQhVR1SyEE8Qba\nvsZGkNKESFww6dhLd+qtFkjIxeyhRANCAATnRzos2J2mVztHcgVDp0W95o4ENRsm\nAcvpTgLIgk4XB3RlmV4Ye1+I76n1tY4SJgOXccTDWfQVLUD8ilI2eC9f\n-----END PRIVATE KEY-----\n"
WITNESS_KEY_dev2027h2a="-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEIAFzi3DVqaBuo3Ms2aurLSzOdgXH2MY1ezNOUqTd33HQ\n-----END PRIVATE KEY-----\n"
11 changes: 2 additions & 9 deletions crates/ct_worker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,20 +64,13 @@ Follow these instructions to spin up a CT log on your local machine using the `d

1. (Optional) [Clear the local storage cache](https://developers.cloudflare.com/workers/testing/local-development/#clear-wranglers-local-storage):

rm -r .workers/state
./reset-dev.sh

1. Deploy worker locally with `npx wrangler -e=dev dev`.

1. Send some requests. After the first request that hits the Durable Object (`/ct/v1/add-[pre-]chain` or `/metrics`), the sequencing loop will begin.

Submit a certificate from a server:

```text
openssl s_client -showcerts -connect google.com:443 -servername google.com </dev/null 2>/dev/null |\
while (set -o pipefail; openssl x509 -outform DER 2>/dev/null | base64); do :; done |\
sed '/^$/d' | sed 's/.*/"&"/' | jq -sc '{"chain":.}' |\
curl -s "http://localhost:8787/logs/dev2025h1a/ct/v1/add-chain" -d@-
```
Submit a certificate from `google.com` by running `python3 scripts/add_cert_to_local_dev.py`. Note you need Python 3.13 or greater.

Use [ctclient](https://github.com/google/certificate-transparency-go/tree/master/client/ctclient) to 'cross-pollinate' entries from another log (RFC6962 logs only, until [static-ct-api support is added](https://github.com/google/certificate-transparency-go/issues/1669)) with overlapping roots and NotAfter temporal interval:

Expand Down
48 changes: 47 additions & 1 deletion crates/ct_worker/config.dev.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,52 @@
"sequence_interval_millis": 750,
"sequence_skip_threshold_millis": 250,
"location_hint": "enam"
},
"dev2026h1a": {
"description": "Dev 2026h1a",
"log_type": "test",
"submission_url": "http://localhost:8787/logs/dev2026h1a/",
"temporal_interval": {
"start_inclusive": "2026-01-01T00:00:00Z",
"end_exclusive": "2026-07-01T00:00:00Z"
},
"location_hint": "enam"
},
"dev2026h2a": {
"description": "Dev 2026h2a",
"log_type": "test",
"submission_url": "http://localhost:8787/logs/dev2026h2a/",
"temporal_interval": {
"start_inclusive": "2026-07-01T00:00:00Z",
"end_exclusive": "2027-01-01T00:00:00Z"
},
"max_sequence_skips": 1,
"sequence_interval_millis": 750,
"sequence_skip_threshold_millis": 250,
"location_hint": "enam"
},
"dev2027h1a": {
"description": "Dev 2027h1a",
"log_type": "test",
"submission_url": "http://localhost:8787/logs/dev2027h1a/",
"temporal_interval": {
"start_inclusive": "2027-01-01T00:00:00Z",
"end_exclusive": "2027-07-01T00:00:00Z"
},
"location_hint": "enam"
},
"dev2027h2a": {
"description": "Dev 2027h2a",
"log_type": "test",
"submission_url": "http://localhost:8787/logs/dev2027h2a/",
"temporal_interval": {
"start_inclusive": "2027-07-01T00:00:00Z",
"end_exclusive": "2028-01-01T00:00:00Z"
},
"max_sequence_skips": 1,
"sequence_interval_millis": 750,
"sequence_skip_threshold_millis": 250,
"location_hint": "enam"
}
}
}
}
51 changes: 51 additions & 0 deletions crates/ct_worker/scripts/add_cert_to_local_dev.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import base64
from datetime import datetime
import json
import urllib.request
import socket
import ssl
import sys

HOST = "www.google.com"

# Need >=3.13 to use conn.get_unverified_chain()
if sys.version_info < (3, 13, 0):
cur_version = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
print("Error: This script requires Python 3.13 or newer. ")
print(f"Current version is {cur_version}")
exit(1)

# Make a connection to HOST
context = ssl.create_default_context()
conn = context.wrap_socket(socket.socket(socket.AF_INET), server_hostname=HOST)
conn.connect((HOST, 443))

# Get the leaf cert. That's the not_after we use to decide which shard to submit to
parsed_cert = conn.getpeercert()
# Get the cert chain. This is what we'll submit
der_cert_chain = conn.get_unverified_chain()
b64_cert_chain = [str(base64.b64encode(c), encoding="utf-8") for c in der_cert_chain]

# Figure out the year and half-year that this not_after belongs to. Eg dev2026h2a is the second half
# of 2026
not_after = datetime.strptime(parsed_cert["notAfter"], "%b %d %H:%M:%S %Y %Z")
not_after_year = not_after.year
not_after_half = 1 if not_after.month < 7 else 2
log_name = f"dev{not_after_year}h{not_after_half}a"

# Make a POST request to localhost add-chain
payload = {"chain": b64_cert_chain}
url = f"http://localhost:8787/logs/{log_name}/ct/v1/add-chain"
request = urllib.request.Request(
url,
data=bytes(json.dumps(payload), encoding="utf-8"),
headers={'Content-Type': 'application/json'},
method='POST'
)
try:
urllib.request.urlopen(request, timeout=5)
except Exception as e:
print("Error: ", e)
sys.exit(1)

print(f"Successfully submitted to {url}")
12 changes: 12 additions & 0 deletions crates/ct_worker/scripts/create-log.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#!/bin/sh

set -e -o pipefail

# Helper script to create resources for a log shard.

if [ -z $ENV ] || [ -z $LOG_NAME ] || [ -z $LOCATION ] || [ -z $CLOUDFLARE_ACCOUNT_ID ]; then
Expand Down Expand Up @@ -33,3 +35,13 @@ if npx wrangler -e=${ENV} secret list | grep -q SIGNING_KEY_${LOG_NAME}; then
else
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 | npx wrangler -e=${ENV} secret put SIGNING_KEY_${LOG_NAME}
fi

echo "DONE"
echo "NOTE: If you intend to run wrangler dev with this log, you must add the appropriate signing keys to .dev.vars"
echo "~~~~~~"
echo "echo -n \"SIGNING_KEY_${LOG_NAME}=\\\\\"\" >> .dev.vars"
echo "openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 | sed 's/$/\\\\\\\\\\\\\\\\n/g' | tr -d \\\\n >> .dev.vars"
echo "echo '\"' >> .dev.vars"
echo "echo -n \"WITNESS_KEY_${LOG_NAME}=\\\\\"\" >> .dev.vars"
echo "openssl genpkey -algorithm ed25519 | sed 's/$/\\\\\\\\\\\\\\\\n/g' | tr -d \\\\n >> .dev.vars"
echo "echo '\"' >> .dev.vars"
32 changes: 32 additions & 0 deletions crates/ct_worker/wrangler.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,22 @@
{
"id": "38045c90369140b091b3b68451dc6c29",
"binding": "cache_dev2025h2a"
},
{
"id": "00297460599846869decff3d015191ce",
"binding": "cache_dev2026h1a"
},
{
"id": "f3a1a27d611b4c4bab366d7211133e3d",
"binding": "cache_dev2026h2a"
},
{
"id": "4b60b0dd42b24763b0048bbd380f5d1d",
"binding": "cache_dev2027h1a"
},
{
"id": "4ff667ad67d54a39b41acc7f097c671a",
"binding": "cache_dev2027h2a"
}
],
"r2_buckets": [
Expand All @@ -45,6 +61,22 @@
{
"bucket_name": "static-ct-public-dev2025h2a",
"binding": "public_dev2025h2a"
},
{
"bucket_name": "static-ct-public-dev2026h1a",
"binding": "public_dev2026h1a"
},
{
"bucket_name": "static-ct-public-dev2026h2a",
"binding": "public_dev2026h2a"
},
{
"bucket_name": "static-ct-public-dev2027h1a",
"binding": "public_dev2027h1a"
},
{
"bucket_name": "static-ct-public-dev2027h2a",
"binding": "public_dev2027h2a"
}
],
"durable_objects": {
Expand Down
12 changes: 12 additions & 0 deletions crates/x509_util/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,18 @@ fn check_well_formedness(cert: &Certificate) -> Result<(), ValidationError> {
/// provided by the submitter.
/// ```
fn is_link_valid(child: &Certificate, issuer: &Certificate) -> bool {
// Currently paths are built by comparing
// child.tbs_certificate.issuer.to_string()
// to
// issuer.tbs_certificate.subject.to_string().
// When these are equal, there is a plausible link between these. This is NOT the actual
// algorithm for determining whether a link is valid. A discussion on the correct algorithm can
// be found here
// https://github.com/golang/go/issues/31440#issuecomment-537222858
// The short version is: many clients do byte-by-byte comparison. This to_string() comparison is
// strictly laxer than that. Which is probably fine for MTC and (static) CT use cases.

// Verify the issuer's signature on the child cert
if let Ok(key) = VerifyingKey::try_from(issuer) {
key.verify_strict(child).is_ok()
} else {
Expand Down