Skip to content

Commit 73b7438

Browse files
authored
Align MTC support closer to spec (#57)
* Add support for MTC's initial log entry In MTCs, the certificate serial number is its index in the log. Since X.509 serial numbers must be non-zero, MTCs adds an initial 'null_entry' at index 0 to help avoid off-by-one errors in consumers of the log. * Add support for parsing the log ID as a ObjectIdentifier This is not compatibile with the current MTC draft which uses RELATIVE-OID, but will do until RustCrypto/formats#1875 is handled. * Rename Signature -> NoteSignature to avoid naming conflicts * Clean up config * Update wrangler KV and R2 resources for MTC dev deploy * Re-enable wasm-opt (see cloudflare/workers-rs#767) * Add cosignature support Add support for cosignature/v1 signatures from tlog-cosignature Add support for subtree_signatures from current MTC draft. This is currently limited to signing checkpoints but not arbitrary subtrees. Other changes (hopefully improvements): - Consolidate signed_note error types into NoteError - Add new() functions for Ed25519NoteSigner/Verifier to avoid needing to go through text format - Add KeyName wrapper around string that enforces that key names are valid according to signed_note spec - Move Ed25519 signer/verifier to separate file in signed_note - Add enum for signed_note signature types * Make kv_namespaces optional when enable_dedup = false Previously, when enable_dedup was set to false, entries were still written to the long-term deduplication cache in KV. Now, deduplication is skipped entirely. * Document how to reset a targeted dev log without needing to clear DO storage and R2
1 parent 9a69712 commit 73b7438

36 files changed

+1365
-651
lines changed

Cargo.lock

Lines changed: 7 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ jsonschema = "0.30"
4949
length_prefixed = { path = "crates/length_prefixed" }
5050
libfuzzer-sys = "0.4"
5151
log = { version = "0.4" }
52+
mtc_api = { version = "0.2.0", path = "crates/mtc_api" }
5253
p256 = { version = "0.13", features = ["ecdsa"] }
5354
parking_lot = "0.12"
5455
prometheus = "0.14"

crates/ct_worker/Cargo.toml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,6 @@ release = false
1818
[package.metadata.wasm-pack.profile.dev.wasm-bindgen]
1919
dwarf-debug-info = true
2020

21-
# https://github.com/rustwasm/wasm-pack/issues/1247
22-
[package.metadata.wasm-pack.profile.release]
23-
wasm-opt = false
24-
2521
[lib]
2622
crate-type = ["cdylib"]
2723

@@ -56,6 +52,7 @@ serde_json.workspace = true
5652
serde_with.workspace = true
5753
sha2.workspace = true
5854
static_ct_api.workspace = true
55+
signed_note.workspace = true
5956
tlog_tiles.workspace = true
6057
worker.workspace = true
6158
x509-verify.workspace = true

crates/ct_worker/config.schema.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@
107107
"enable_dedup": {
108108
"type": "boolean",
109109
"default": true,
110-
"description": "Enables checking the deduplication cache for add-(pre-)chain requests. Can be disabled for tests and benchmarks."
110+
"description": "Enables checking the deduplication cache for add-(pre-)chain requests. Can be disabled for tests and benchmarks. If disabled, `kv_namespaces` can be omitted from `wrangler.jsonc`."
111111
}
112112
},
113113
"required": [

crates/ct_worker/src/batcher_do.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,11 @@ impl DurableObject for Batcher {
3131
false
3232
})
3333
.expect("unable to find batcher name");
34-
let kv = load_cache_kv(&env, name).unwrap();
34+
let kv = if params.enable_dedup {
35+
Some(load_cache_kv(&env, name).unwrap())
36+
} else {
37+
None
38+
};
3539
let sequencer = get_durable_object_stub(
3640
&env,
3741
name,

crates/ct_worker/src/frontend_worker.rs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -214,13 +214,16 @@ async fn add_chain_or_pre_chain(
214214
let signing_key = load_signing_key(env, name)?;
215215

216216
// Check if entry is cached and return right away if so.
217-
let kv = load_cache_kv(env, name)?;
218-
if let Some(metadata) = get_cached_metadata(&kv, &pending_entry, params.enable_dedup).await? {
219-
debug!("{name}: Entry is cached");
220-
let entry = StaticCTLogEntry::new(pending_entry, metadata);
221-
let sct = static_ct_api::signed_certificate_timestamp(signing_key, &entry)
222-
.map_err(|e| e.to_string())?;
223-
return Response::from_json(&sct);
217+
if params.enable_dedup {
218+
if let Some(metadata) =
219+
get_cached_metadata(&load_cache_kv(env, name)?, &pending_entry).await?
220+
{
221+
debug!("{name}: Entry is cached");
222+
let entry = StaticCTLogEntry::new(pending_entry, metadata);
223+
let sct = static_ct_api::signed_certificate_timestamp(signing_key, &entry)
224+
.map_err(|e| e.to_string())?;
225+
return Response::from_json(&sct);
226+
}
224227
}
225228

226229
// Entry is not cached, so we need to sequence it.
@@ -272,10 +275,10 @@ async fn add_chain_or_pre_chain(
272275
return Ok(response);
273276
}
274277
let metadata = response.json::<SequenceMetadata>().await?;
275-
if params.num_batchers == 0 {
278+
if params.num_batchers == 0 && params.enable_dedup {
276279
// Write sequenced entry to the long-term deduplication cache in Workers
277280
// KV as there are no batchers configured to do it for us.
278-
if put_cache_entry_metadata(&kv, &pending_entry, metadata)
281+
if put_cache_entry_metadata(&load_cache_kv(env, name)?, &pending_entry, metadata)
279282
.await
280283
.is_err()
281284
{

crates/ct_worker/src/sequencer_do.rs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use std::time::Duration;
88
use crate::{load_signing_key, load_witness_key, CONFIG};
99
use generic_log_worker::{load_public_bucket, GenericSequencer, SequencerConfig};
1010
use prometheus::Registry;
11+
use signed_note::KeyName;
1112
use static_ct_api::{StaticCTCheckpointSigner, StaticCTLogEntry};
1213
use tlog_tiles::{CheckpointSigner, Ed25519CheckpointSigner};
1314
#[allow(clippy::wildcard_imports)]
@@ -31,11 +32,15 @@ impl DurableObject for Sequencer {
3132

3233
// https://github.com/C2SP/C2SP/blob/main/static-ct-api.md#checkpoints
3334
// The origin line MUST be the submission prefix of the log as a schema-less URL with no trailing slashes.
34-
let origin = params
35-
.submission_url
36-
.trim_start_matches("http://")
37-
.trim_start_matches("https://")
38-
.trim_end_matches('/');
35+
let origin = KeyName::new(
36+
params
37+
.submission_url
38+
.trim_start_matches("http://")
39+
.trim_start_matches("https://")
40+
.trim_end_matches('/')
41+
.to_string(),
42+
)
43+
.expect("invalid origin name");
3944
let sequence_interval = Duration::from_millis(params.sequence_interval_millis);
4045

4146
// We don't use checkpoint extensions for CT
@@ -46,10 +51,10 @@ impl DurableObject for Sequencer {
4651
let witness_key = load_witness_key(&env, name).unwrap().clone();
4752

4853
// Make the checkpoint signers from the secret keys and put them in a vec
49-
let signer = StaticCTCheckpointSigner::new(origin, signing_key)
54+
let signer = StaticCTCheckpointSigner::new(origin.clone(), signing_key)
5055
.map_err(|e| format!("could not create static-ct checkpoint signer: {e}"))
5156
.unwrap();
52-
let witness = Ed25519CheckpointSigner::new(origin, witness_key)
57+
let witness = Ed25519CheckpointSigner::new(origin.clone(), witness_key)
5358
.map_err(|e| format!("could not create ed25519 checkpoint signer: {e}"))
5459
.unwrap();
5560

@@ -60,7 +65,7 @@ impl DurableObject for Sequencer {
6065

6166
let config = SequencerConfig {
6267
name: name.to_string(),
63-
origin: origin.to_string(),
68+
origin,
6469
checkpoint_signers,
6570
checkpoint_extension,
6671
sequence_interval,

crates/generic_log_worker/src/batcher_do.rs

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use worker::*;
2121

2222
pub struct GenericBatcher<E: PendingLogEntry> {
2323
config: BatcherConfig,
24-
kv: KvStore,
24+
kv: Option<KvStore>,
2525
sequencer: Stub,
2626
batch: Batch<E>,
2727
in_flight: usize,
@@ -54,7 +54,7 @@ impl<E: PendingLogEntry> Default for Batch<E> {
5454

5555
impl<E: PendingLogEntry> GenericBatcher<E> {
5656
/// Returns a new batcher with the given config.
57-
pub fn new(config: BatcherConfig, kv: KvStore, sequencer: Stub) -> Self {
57+
pub fn new(config: BatcherConfig, kv: Option<KvStore>, sequencer: Stub) -> Self {
5858
Self {
5959
config,
6060
kv,
@@ -190,17 +190,18 @@ impl<E: PendingLogEntry> GenericBatcher<E> {
190190
batch.done.send_modify(|v| v.clone_from(&sequenced_entries));
191191

192192
// Write sequenced entries to the long-term deduplication cache in Workers KV.
193-
let futures = sequenced_entries
194-
.into_iter()
195-
.map(|(k, v)| {
196-
Ok(self
197-
.kv
198-
.put(&BASE64_STANDARD.encode(k), "")?
199-
.metadata::<SequenceMetadata>(v)?
200-
.execute())
201-
})
202-
.collect::<Result<Vec<_>>>()?;
203-
join_all(futures).await;
193+
if let Some(kv) = &self.kv {
194+
let futures = sequenced_entries
195+
.into_iter()
196+
.map(|(k, v)| {
197+
Ok(kv
198+
.put(&BASE64_STANDARD.encode(k), "")?
199+
.metadata::<SequenceMetadata>(v)?
200+
.execute())
201+
})
202+
.collect::<Result<Vec<_>>>()?;
203+
join_all(futures).await;
204+
}
204205
Ok(())
205206
}
206207
}

crates/generic_log_worker/src/lib.rs

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -111,21 +111,16 @@ pub fn load_cache_kv(env: &Env, name: &str) -> Result<kv::KvStore> {
111111
pub async fn get_cached_metadata(
112112
kv: &KvStore,
113113
pending: &impl PendingLogEntry,
114-
enable_dedup: bool,
115114
) -> Result<Option<SequenceMetadata>> {
116-
if enable_dedup {
117-
let lookup_key = pending.lookup_key();
118-
119-
// Query the cache and return the entry metadata if it exists
120-
let metadata_opt = kv
121-
.get(&BASE64_STANDARD.encode(lookup_key))
122-
.bytes_with_metadata::<SequenceMetadata>()
123-
.await?
124-
.1;
125-
Ok(metadata_opt)
126-
} else {
127-
Ok(None)
128-
}
115+
let lookup_key = pending.lookup_key();
116+
117+
// Query the cache and return the entry metadata if it exists
118+
let metadata_opt = kv
119+
.get(&BASE64_STANDARD.encode(lookup_key))
120+
.bytes_with_metadata::<SequenceMetadata>()
121+
.await?
122+
.1;
123+
Ok(metadata_opt)
129124
}
130125

131126
/// Makes an empty entry in the dedup cache with `pending.lookup_key()` as the

0 commit comments

Comments
 (0)