Repository navigation
Expand file tree
/
Copy pathCargo.toml
More file actions
478 lines (411 loc) · 18.2 KB
/
Copy pathCargo.toml
File metadata and controls
478 lines (411 loc) · 18.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
[package]
name = "cipherpost"
version = "2.0.0-alpha.1"
edition = "2021"
license = "MIT"
description = "Self-sovereign, serverless, accountless CLI for cryptographic-material handoff over Mainline DHT via PKARR."
repository = "https://github.com/johnzilla/cipherpost"
rust-version = "1.88"
[lib]
name = "cipherpost"
path = "src/lib.rs"
[[bin]]
name = "cipherpost"
path = "src/main.rs"
[features]
mock = []
# v2 large-payload handoff (iroh-free; pubky-homeserver over blocking ureq).
# Feature-gated so the DEFAULT build's dependency tree — and every supply-chain
# guard test (no ring/aws-lc, no direct tokio) — is unchanged. Pulls a blocking
# HTTP client (ureq + OS-native TLS, no ring/aws-lc) and tar only when enabled.
large-payload = ["dep:ureq", "dep:tar"]
# Phase 9 Plan 02 (D-P9-D2 + DHT-03): real-DHT cross-identity round trip is
# behind this feature flag. CI NEVER enables it (PR + nightly stay mock-
# only per Pitfall #29). The flag gates a single test in tests/real_dht_e2e.rs
# that is also marked #[ignore] + #[serial] — belt-and-suspenders. Manual
# invocation only via RELEASE-CHECKLIST.md (Plan 09-03).
real-dht-e2e = []
[dependencies]
# DHT transport — no_relays + dht feature only; inherits ed25519-dalek 3.x pre-release
pkarr = { version = "5.0.3", default-features = false, features = ["dht"] }
# HARD PIN: pkarr 5.0.3 depends on ^3.0.0-pre.1 and cargo won't auto-pick newer pre-releases without =.
# Do NOT change until pkarr publishes on a stable ed25519-dalek 3.x.
# `hazmat` + `digest`: Phase 2 of derived-key addressing (docs/design/derived-key-
# addressing.md) signs BEP44 packets under blinded keys via raw-scalar signing —
# pkarr's seed-based Keypair can't. `digest` gates the re-exported Sha512 that
# hazmat::raw_sign's CtxDigest bound requires. USE IS CONFINED to the derived-key
# signing path in transport.rs (self-verified before use; ed25519-unsafe-libs guard).
ed25519-dalek = { version = "=3.0.0-pre.5", features = ["hazmat", "digest"] }
# Derived-key addressing: point + scalar arithmetic for single-hop stealth
# derivation (src/derive.rs). Already in the tree via ed25519-dalek; same exact
# pin; `zeroize` so secret intermediate scalars can be wiped.
curve25519-dalek = { version = "=5.0.0-pre.5", features = ["zeroize"] }
# from_relay_payload takes a `bytes::Bytes` — used to assemble derived-key packets.
bytes = "1"
age = "0.11"
argon2 = "0.5"
hkdf = "0.12"
sha2 = "0.10"
zeroize = { version = "1", features = ["zeroize_derive"] }
secrecy = "0.10"
clap = { version = "4.5", features = ["derive"] }
anyhow = "1"
thiserror = "2"
base64 = "0.22"
bech32 = "0.9"
dirs = "5"
# Per-share_ref advisory file lock for the receive-side concurrency window (see src/flow.rs::acquire_share_lock + .planning/research/PITFALLS.md Pitfall #26 amendment). MIT/Apache-2.0 — within deny.toml [licenses] allowlist; transitive deps = libc only (already in [dev-dependencies]).
fs2 = "0.4"
dialoguer = "0.12"
serde = { version = "1", features = ["derive"] }
# NOTE: no extra serde_json features — key-ordering features break canonical-JSON invariants.
serde_json = "1"
serde_canonical_json = "1"
rand = "0.8"
# Local-time rendering on the acceptance screen (D-ACCEPT-02 / RECV-04).
# `clock` feature pulls in `iana-time-zone` for local-offset lookup. std alone
# has no stable tz-aware local-time API.
chrono = { version = "0.4", default-features = false, features = ["clock"] }
# Phase 6: X.509 DER/PEM parsing. `verify` feature STAYS OFF — would pull `ring`
# which is on the supply-chain rejected list (see .planning/research/SUMMARY.md
# §Phase 6). `default-features = false` is belt-and-suspenders: 0.16 has
# `default = []` so the result is identical, but explicit intent aids audit.
# Do NOT bump to 0.17 — that version breaks to nom 8.0.
x509-parser = { version = "0.16", default-features = false }
# Phase 7: OpenPGP binary packet-stream parsing. Exact-pin (`=0.19.0`) because
# the supply-chain matrix (MSRV 1.88, RUSTSEC-2023-0071 via rsa 0.9, unconditional
# ed25519-dalek 2.1.1 pull) is version-specific — any upstream change requires
# re-running Phase 7 research. `default-features = false` disables `bzip2`
# (we do not encounter compressed key material — keys are not messages) and
# `asm` (supply-chain prefers pure-Rust). See D-P7-04 + research §"Crate: pgp".
#
# Transitive advisory accepted: RUSTSEC-2023-0071 (Marvin timing attack on rsa 0.9).
# Cipherpost parses PGP for metadata display only — never decrypts/signs with RSA.
# See deny.toml [advisories] ignore entry below.
pgp = { version = "=0.19.0", default-features = false }
# Phase 7 Plan 02: rsa is already pulled transitively by pgp 0.19.0 (see RUSTSEC
# acceptance comment above). preview.rs needs `rsa::traits::PublicKeyParts` in
# scope to read RSA modulus bit-size for the `Key: RSA-2048` banner
# line. Re-exporting through a direct dep is the cleanest path (no new supply
# chain risk — the version pin matches what pgp resolves to). We do NOT enable
# any rsa feature; default features are fine for the trait-only import.
rsa = { version = "0.9", default-features = false }
# Phase 7 Plan 05: OpenSSH v1 key parsing + canonical re-encode + SHA-256
# fingerprint. `default-features = false` strips `["ecdsa", "rand_core", "std"]`;
# we add `["alloc"]` because PrivateKey::to_openssh() requires alloc.
# Crucially, the `ed25519` feature is NOT enabled — research D-P7-10 verified
# source-level that Ed25519 parsing + Fingerprint::compute() work without it
# (sha2 is unconditional; only the ed25519-dalek interop TryFrom impls are gated).
# This keeps the dep tree clean: NO new ed25519-dalek version leak from ssh-key
# (verified post-Plan-05 against Plan 01's evidence file).
ssh-key = { version = "0.6.7", default-features = false, features = ["alloc"] }
# v2 large-payload (feature-gated via `large-payload`; both optional). ureq is a
# BLOCKING HTTP client — no tokio at the cipherpost layer (preserves SCAF-05).
# default-features=false drops ureq's default rustls(+ring) backend; native-tls
# uses OS TLS (Security.framework / schannel / system OpenSSL) → no ring, no aws-lc.
ureq = { version = "3", default-features = false, features = ["native-tls"], optional = true }
tar = { version = "0.4", optional = true }
[[test]]
name = "signed_packet_budget"
path = "tests/signed_packet_budget.rs"
[[test]]
name = "outer_record_canonical_form"
path = "tests/outer_record_canonical_form.rs"
[[test]]
name = "phase2_envelope_round_trip"
path = "tests/phase2_envelope_round_trip.rs"
[[test]]
name = "phase2_material_variants_unimplemented"
path = "tests/phase2_material_variants_unimplemented.rs"
[[test]]
name = "phase2_share_uri_parse"
path = "tests/phase2_share_uri_parse.rs"
[[test]]
name = "phase2_self_round_trip"
path = "tests/phase2_self_round_trip.rs"
required-features = ["mock"]
[[test]]
name = "phase2_share_round_trip"
path = "tests/phase2_share_round_trip.rs"
required-features = ["mock"]
[[test]]
name = "phase2_tamper_aborts_before_decrypt"
path = "tests/phase2_tamper_aborts_before_decrypt.rs"
required-features = ["mock"]
[[test]]
name = "phase2_expired_share"
path = "tests/phase2_expired_share.rs"
required-features = ["mock"]
[[test]]
name = "phase2_size_cap"
path = "tests/phase2_size_cap.rs"
required-features = ["mock"]
[[test]]
name = "phase2_idempotent_re_receive"
path = "tests/phase2_idempotent_re_receive.rs"
required-features = ["mock"]
[[test]]
name = "phase2_state_perms"
path = "tests/phase2_state_perms.rs"
required-features = ["mock"]
[[test]]
name = "phase2_cli_version_git_sha"
path = "tests/phase2_cli_version_git_sha.rs"
[[test]]
name = "phase2_cli_help_examples"
path = "tests/phase2_cli_help_examples.rs"
[[test]]
name = "phase2_cli_stderr_no_secrets"
path = "tests/phase2_cli_stderr_no_secrets.rs"
[[test]]
name = "phase2_cli_declined_exit_7"
path = "tests/phase2_cli_declined_exit_7.rs"
required-features = ["mock"]
[[test]]
name = "phase2_cli_not_tty_aborts"
path = "tests/phase2_cli_not_tty_aborts.rs"
[[test]]
name = "phase3_receipt_sign_verify"
path = "tests/phase3_receipt_sign_verify.rs"
[[test]]
name = "phase3_receipt_canonical_form"
path = "tests/phase3_receipt_canonical_form.rs"
[[test]]
name = "phase3_end_to_end_a_sends_b_receipt"
path = "tests/phase3_end_to_end_a_sends_b_receipt.rs"
required-features = ["mock"]
[[test]]
name = "phase3_coexistence_b_self_share_and_receipt"
path = "tests/phase3_coexistence_b_self_share_and_receipt.rs"
required-features = ["mock"]
[[test]]
name = "phase3_share_ref_filter"
path = "tests/phase3_share_ref_filter.rs"
required-features = ["mock"]
[[test]]
name = "phase3_tamper_zero_receipts"
path = "tests/phase3_tamper_zero_receipts.rs"
required-features = ["mock"]
[[test]]
name = "spec_test_vectors"
path = "tests/spec_test_vectors.rs"
# Phase 5 Plan 05-01: exact one-newline strip rule (D-P5-08) exercised via file-branch.
[[test]]
name = "passphrase_strip_rule"
path = "tests/passphrase_strip_rule.rs"
# Phase 5 Plan 05-01: BorrowedFd fd-lifecycle test + fd=0 rejection (D-P5-03/D-P5-07).
[[test]]
name = "passphrase_fd_borrowed"
path = "tests/passphrase_fd_borrowed.rs"
# Phase 5 Plan 05-02: PASS-09 scripted-roundtrip integration test under MockTransport
# (D-P5-09). Requires the `mock` feature; gates the CI guarantee that scripted
# send→receive works end-to-end with no TTY, via --passphrase-file and --passphrase-fd.
[[test]]
name = "pass09_scripted_roundtrip"
path = "tests/pass09_scripted_roundtrip.rs"
required-features = ["mock"]
# Phase 6 Plan 04: X509 Envelope JCS fixture byte-identity.
[[test]]
name = "material_x509_envelope_round_trip"
path = "tests/material_x509_envelope_round_trip.rs"
# Phase 6 Plan 04: X509 ingest pipeline coverage (positive + negative paths + oracle hygiene).
[[test]]
name = "material_x509_ingest"
path = "tests/material_x509_ingest.rs"
# Phase 6 Plan 04: X509 self-mode round-trip under MockTransport (X509-09).
[[test]]
name = "x509_roundtrip"
path = "tests/x509_roundtrip.rs"
required-features = ["mock"]
# Phase 6 Plan 04: X509 banner render golden-string test.
[[test]]
name = "x509_banner_render"
path = "tests/x509_banner_render.rs"
# Phase 6 Plan 04: Error-oracle hygiene for Error::InvalidMaterial (X509-08).
[[test]]
name = "x509_error_oracle"
path = "tests/x509_error_oracle.rs"
# Phase 6 Plan 04: CI-visible dep-tree guard — ring/aws-lc must NEVER appear.
[[test]]
name = "x509_dep_tree_guard"
path = "tests/x509_dep_tree_guard.rs"
# Phase 7 Plan 04: PGP Envelope JCS fixture byte-identity.
[[test]]
name = "material_pgp_envelope_round_trip"
path = "tests/material_pgp_envelope_round_trip.rs"
# Phase 7 Plan 04: PGP ingest pipeline coverage (positive + negative + oracle).
[[test]]
name = "material_pgp_ingest"
path = "tests/material_pgp_ingest.rs"
# Phase 7 Plan 04: PGP round-trip under MockTransport.
[[test]]
name = "pgp_roundtrip"
path = "tests/pgp_roundtrip.rs"
required-features = ["mock"]
# Phase 7 Plan 04: PGP banner golden-string render tests.
[[test]]
name = "pgp_banner_render"
path = "tests/pgp_banner_render.rs"
# Phase 7 Plan 04: PGP error-oracle hygiene (FORBIDDEN_DISPLAY_TOKENS extended).
[[test]]
name = "pgp_error_oracle"
path = "tests/pgp_error_oracle.rs"
# Phase 7 Plan 08: SSH Envelope JCS fixture byte-identity (SSH-07).
[[test]]
name = "material_ssh_envelope_round_trip"
path = "tests/material_ssh_envelope_round_trip.rs"
# Phase 7 Plan 08: SSH ingest pipeline coverage (SSH-01, SSH-02, SSH-08).
[[test]]
name = "material_ssh_ingest"
path = "tests/material_ssh_ingest.rs"
# Phase 7 Plan 08: SSH self-mode round-trip + WireBudgetExceeded + armor-rejection
# (SSH-05, SSH-08, SSH-09; round-trip #[ignore]'d from day 1 per D-P7-03).
[[test]]
name = "ssh_roundtrip"
path = "tests/ssh_roundtrip.rs"
required-features = ["mock"]
# Phase 7 Plan 08: SSH banner golden-string render tests (SSH-04).
[[test]]
name = "ssh_banner_render"
path = "tests/ssh_banner_render.rs"
# Phase 7 Plan 08: SSH error-oracle hygiene + SshKeyFormatNotSupported exit-1
# regression (SSH-08 oracle).
[[test]]
name = "ssh_error_oracle"
path = "tests/ssh_error_oracle.rs"
# Phase 8 Plan 01: pin send-side smoke (pin_required wire shape + salt prefix).
# Receive-side PIN decrypt lands in Plan 02 (tests/pin_roundtrip.rs).
[[test]]
name = "pin_send_smoke"
path = "tests/pin_send_smoke.rs"
required-features = ["mock"]
# Phase 8 Plan 02: PIN-08 round-trip matrix (a)/(b)/(c). (a) and (b) are
# wire-budget #[ignore]'d (mirrors Phase 6/7 X.509/PGP/SSH pattern); (c) is
# the no-PIN-at-receive non-TTY rejection path and ALWAYS runs.
[[test]]
name = "pin_roundtrip"
path = "tests/pin_roundtrip.rs"
required-features = ["mock"]
# Phase 8 Plan 02: PIN-07 narrow oracle (wrong-PIN ≡ wrong-passphrase ≡
# tampered-inner-ciphertext Display + exit-4 equality; sig-failures stay
# in the distinct exit-3 lane).
[[test]]
name = "pin_error_oracle"
path = "tests/pin_error_oracle.rs"
required-features = ["mock"]
# Phase 8 Plan 02: JCS byte-identity for OuterRecordSignable{pin_required=true}.
# Pins alphabetic placement between created_at and protocol_version (NOT
# purpose — purpose lives on Envelope, not OuterRecord).
[[test]]
name = "outer_record_pin_required_signable"
path = "tests/outer_record_pin_required_signable.rs"
# Phase 8 Plan 02: PIN entropy floor + anti-pattern + blocklist matrix.
# Tests `validate_pin` rejection rules and the oracle-hygiene invariant
# that every rejection class produces the IDENTICAL generic Display.
[[test]]
name = "pin_validation"
path = "tests/pin_validation.rs"
# Phase 8 Plan 03: BURN send-side smoke (burn-only round-trip + pin+burn
# compose orthogonality + burn=false v1.0 byte-identity preservation).
# Receive-side burn marking + BURN-09 full round-trip lands in Plan 04
# (tests/burn_roundtrip.rs).
[[test]]
name = "burn_send_smoke"
path = "tests/burn_send_smoke.rs"
required-features = ["mock"]
# Phase 8 Plan 04 (BURN-01 + D-P8-04): JCS byte-identity fixture for
# Envelope { burn_after_read=true, .. } — pins alphabetic placement
# (`burn_after_read` lands FIRST before `created_at`) and asserts the
# non-burn elision path preserves v1.0 byte-identity.
[[test]]
name = "envelope_burn_signable"
path = "tests/envelope_burn_signable.rs"
# Phase 8 Plan 04 (D-P8-10): state-schema migration invariants — v1.0 row
# without `state` field deserializes as Accepted; v1.1 burned row
# deserializes as Burned; sentinel-without-row falls back to Accepted.
[[test]]
name = "state_ledger"
path = "tests/state_ledger.rs"
required-features = ["mock"]
# Quick 260427-axn: per-share_ref receive lock regression. Two Barrier-synced
# threads both invoke run_receive on the same share_ref; only one observes the
# fresh path. True Barrier-synced threads, never sleep-based simulation.
[[test]]
name = "state_ledger_concurrency"
path = "tests/state_ledger_concurrency.rs"
required-features = ["mock"]
# Phase 8 Plan 04 (BURN-09): two consecutive receives of the same burn
# share return exit 0 then exit 7. Receipt published exactly ONCE on the
# first successful receive (BURN-04). The receipt-count assertion
# encodes the explicit lock that publish_receipt is NOT guarded by
# !envelope.burn_after_read.
[[test]]
name = "burn_roundtrip"
path = "tests/burn_roundtrip.rs"
required-features = ["mock"]
# Phase 8 Plan 05: pin × burn × typed-material compose matrix. 12 base
# round-trip cases (4 variants × {pin, burn, pin+burn}) + 4 receipt-count
# cross-cutting (BURN-04) + 4 second-receive exit-7 cross-cutting (BURN-09)
# + 2 negative-path safety (wrong-PIN-on-burn / declined-z32-on-burn don't
# mark burned) + 1 pin+burn+pgp wire-budget pre-flight (Open Risk #5).
[[test]]
name = "pin_burn_compose"
path = "tests/pin_burn_compose.rs"
required-features = ["mock"]
# Phase 9 Plan 01 (DHT-07): pin + burn + 2KB GenericSecret payload wire-budget
# composite — Pitfall #22 third instance after Phase 6 X.509 and Phase 7
# PGP/SSH. Asserts the cleanly-surfaced Error::WireBudgetExceeded path on
# the worst-case Phase 8 compose. Synthesized 2KB byte vector (not the
# PGP fixture — D-P9-E1 + Pitfall E in 09-RESEARCH.md).
[[test]]
name = "wire_budget_compose_pin_burn_pgp"
path = "tests/wire_budget_compose_pin_burn_pgp.rs"
required-features = ["mock"]
# Phase 9 Plan 02 (DHT-03 + DHT-04 + DHT-05): real-DHT cross-identity round
# trip — Alice publishes, Bob resolves with 7-step exp-backoff and 120s
# deadline, Bob decrypts + publishes receipt, Alice fetches. UDP pre-flight
# to router.bittorrent.com:6881 skips gracefully if unreachable. NEVER runs
# in CI (cfg-feature off + #[ignore] + #[serial] triple gate per D-P9-D2);
# manual-only via RELEASE-CHECKLIST.md (Plan 09-03).
[[test]]
name = "real_dht_e2e"
path = "tests/real_dht_e2e.rs"
required-features = ["real-dht-e2e"]
# v2 large-payload: live HomeserverBlobStore round-trip. #[ignore]'d + manual-only
# (like real_dht_e2e); needs the `large-payload` feature to compile.
[[test]]
name = "homeserver_live"
path = "tests/homeserver_live.rs"
required-features = ["large-payload"]
# v2 large-payload: send-large→receive-large round trip under MockTransport +
# MockBlobStore (no live server / DHT). CI-safe.
[[test]]
name = "large_payload_round_trip"
path = "tests/large_payload_round_trip.rs"
required-features = ["large-payload", "mock"]
[dev-dependencies]
tempfile = "3"
proptest = "1"
# (Phase 2: ed25519-dalek hazmat/digest + bytes are now real [dependencies] — the
# derived-key signing path is production code in transport.rs.)
# Plan 02 Task 2's tests/identity_passphrase_argv_rejected.rs uses these to spawn
# the compiled binary and assert exit code + stderr substring. Declared here (not in
# Plan 02) because Cargo.toml is owned by Plan 01. Both crates are MIT/Apache-2.0 —
# allowed by deny.toml's license allowlist.
assert_cmd = "2"
predicates = "3"
# Serializes tests that mutate the CIPHERPOST_HOME env var (integration tests in
# identity_perms_0600.rs, identity_phc_header.rs, debug_leak_scan.rs). Rust's
# default parallel test runner races on process-global env; #[serial] forces
# these tests to run one at a time within their crate.
serial_test = "3"
# Phase 5 Plan 05-01: fd-lifecycle test (tests/passphrase_fd_borrowed.rs) uses
# libc::pipe / libc::write / libc::close / libc::fcntl directly to build a pipe,
# pass its read-end fd through resolve_passphrase, and then assert fcntl(fd,
# F_GETFD) != -1 after the call (proves BorrowedFd did not close the caller's
# fd — Pitfall #31). MIT/Apache-2.0.
libc = "0.2"
[profile.release]
panic = "abort" # prevents panic backtrace from leaking stack contents (Security Mistakes table, PITFALLS.md)
lto = "thin"
codegen-units = 1