Zion Boggan zionboggan.com ↗

Refresh public docs and Rust policy fixture

Fix the oversight-policy test manifest fixture for the v0.4.11 Recipient.p256_pub schema, format the Rust workspace, and refresh public README/roadmap/embedding copy for the post-v0.4.11 registry migration line.

Co-authored-by: Codex (GPT-5.4) <noreply@openai.com>
5088349   Zion Boggan committed on May 17, 2026 (1 month ago)
CHANGELOG.md +6 -0
@@ -24,9 +24,15 @@
copies the Python reference registry's manifests, beacons, watermarks,
events, and corpus rows into the Rust SQLite schema while preserving event
IDs, corpus metadata, and registry evidence relationships.
+- **Rust policy test parity.** Fixed the `oversight-policy` crate's manifest
+ fixture after the v0.4.11 `Recipient.p256_pub` schema addition so the full
+ Rust workspace test suite compiles again.
- **Deployment docs.** Added `docs/REGISTRY_DEPLOYMENT.md` covering the live
Compose/Caddy flow, route map, token headers, DNS bridge secret, and local
versus live conformance commands.
+- **Public description refresh.** Updated README/roadmap/embedding copy to
+ describe v0.4.11 as the current stable line and the post-tag Rust registry
+ deployment and migration work on `main`.
## v0.4.11 - 2026-05-08 Hardware-keys completion: Python parity, browser support, end-to-end seal
README.md +31 -12
@@ -2,7 +2,7 @@
**Open protocol for cryptographic data provenance, recipient attribution, and leak detection.**
-Co-authored by Zion Boggan and Claude Opus 4.6/4.7 (Anthropic) and Codex ChatGPT-5-4 (OpenAI).
+Co-authored by Zion Boggan, Claude Opus 4.6/4.7 (Anthropic), and Codex (GPT-5.4, OpenAI).
Format-agnostic. Post-quantum ready (ML-KEM-768 + ML-DSA-65). Layered watermarking with honest limits: L1/L2 are lightweight steganographic signals, L3 is opt-in semantic marking for prose, and content fingerprinting helps identify leaked copies even when fragile marks are destroyed.
@@ -80,6 +80,20 @@ oversight-registry --db rust-registry.sqlite \
Remove `--migrate-dry-run` to copy manifests, beacons, watermarks, events, and
corpus rows into the Rust database.
+## Current main after v0.4.11
+
+`main` is past the v0.4.11 hardware-key tag. The live registry deployment
+path now includes the Compose/Caddy `live` profile, `.env.example` operator
+secrets, and shared write-side token enforcement across the Python FastAPI
+and Rust Axum registries. The Rust registry also has Python-to-Rust SQLite
+migration tooling (`--migrate-from`, `--migrate-dry-run`) so operators can
+preflight and copy attribution rows without treating the Python reference as
+a permanent production dependency.
+
+The next Rust-registry gate is operational burn-in: longer-running deployment
+tests, migration validation against real operator databases, and a final
+wire-format stability declaration before v1.0.
+
## Quick start
```bash
@@ -403,18 +417,19 @@ project does not backport fixes below the current stable line.
| Layer | Checks | Status |
|---|---|---|
-| Python test_e2e | 11 | green |
-| Python test_e2e_v2 | 13 | green |
-| Python test_pq | 7 | green |
-| Rust oversight-crypto | 7 | green |
-| Rust oversight-manifest | 2 | green |
-| Rust oversight-container | 8 | green |
-| Rust oversight-watermark | 4 | green |
-| Rust oversight-tlog | 7 | green |
-| Rust oversight-policy | 6 | green |
+| Python pytest suite | 10 | green |
+| Rust oversight-container | 17 | green |
+| Rust oversight-crypto | 21 | green |
+| Rust oversight-formats | 35 | green |
+| Rust oversight-manifest | 3 | green |
+| Rust oversight-policy | 7 | green |
+| Rust oversight-registry | 8 | green |
+| Rust oversight-rekor | 10 | green |
| Rust oversight-semantic | 8 | green |
+| Rust oversight-tlog | 7 | green |
+| Rust oversight-watermark | 4 | green |
| Cross-language conformance | 3 | green |
-| Total | 76 | all green |
+| Total automated Rust unit tests | 120 | all green |
## Design principles (what Oversight never does)
@@ -432,7 +447,11 @@ project does not backport fixes below the current stable line.
Public-log interoperability is now via Rekor DSSE attestations; the local log remains
a lightweight registry integrity mechanism, not a drop-in replacement for Rekor.
- **No independent security audit yet.** Planned for 2027. Until then: user-beware, cryptographer-review welcome. Open an issue.
-- **Rust port is core-only.** ~1,500 LOC ported. The remaining ~5,500 LOC (semantic dictionary, format adapters, registry server, integrations) is multi-release scope. Python is still the canonical reference.
+- **Rust port is broad but not final.** The Rust workspace now covers the
+ cryptographic core, manifests, containers, policy checks, watermark
+ detection, semantic helpers, format adapters, and the Axum/SQLx registry.
+ Python remains the canonical reference until the Rust registry finishes
+ deployment burn-in, migration validation, and v1.0 wire-format freeze.
## License
docs/EMBEDDING.md +9 -9
@@ -59,20 +59,20 @@ for new contributors. Git plus tag is the supported pin.
```toml
[dependencies]
-oversight-crypto = { git = "https://github.com/oversight-protocol/oversight.git", tag = "v0.4.8" }
-oversight-manifest = { git = "https://github.com/oversight-protocol/oversight.git", tag = "v0.4.8" }
-oversight-container = { git = "https://github.com/oversight-protocol/oversight.git", tag = "v0.4.8" }
-oversight-tlog = { git = "https://github.com/oversight-protocol/oversight.git", tag = "v0.4.8" }
-oversight-rekor = { git = "https://github.com/oversight-protocol/oversight.git", tag = "v0.4.8" }
-oversight-watermark = { git = "https://github.com/oversight-protocol/oversight.git", tag = "v0.4.8" }
-oversight-policy = { git = "https://github.com/oversight-protocol/oversight.git", tag = "v0.4.8" }
+oversight-crypto = { git = "https://github.com/oversight-protocol/oversight.git", tag = "v0.4.11" }
+oversight-manifest = { git = "https://github.com/oversight-protocol/oversight.git", tag = "v0.4.11" }
+oversight-container = { git = "https://github.com/oversight-protocol/oversight.git", tag = "v0.4.11" }
+oversight-tlog = { git = "https://github.com/oversight-protocol/oversight.git", tag = "v0.4.11" }
+oversight-rekor = { git = "https://github.com/oversight-protocol/oversight.git", tag = "v0.4.11" }
+oversight-watermark = { git = "https://github.com/oversight-protocol/oversight.git", tag = "v0.4.11" }
+oversight-policy = { git = "https://github.com/oversight-protocol/oversight.git", tag = "v0.4.11" }
```
Cargo resolves all seven entries against the same git checkout, so the
fetch happens once and every crate is byte-identical to what the desktop
CLI shipped against. `Cargo.lock` records the resolved commit
-(`af6f725c` for `v0.4.8`); a downstream consumer who commits their lock
-file will get reproducible resolution across machines.
+(`14547d9` for `v0.4.11`); a downstream consumer who commits their lock file
+will get reproducible resolution across machines.
For a consumer that prefers a commit-sha pin over a tag pin, the same
pattern works with `rev` instead of `tag`. Tag is the recommended default
docs/ROADMAP.md +10 -10
@@ -217,14 +217,14 @@ wrap/unwrap, `WrappedDekP256` envelope, and `SoftwareP256KeyProvider`
(in-memory P-256 reference impl) are in `oversight-crypto`. Cross-suite
envelopes are rejected explicitly. 21/21 tests in the crate pass.
-The remaining work is the `PivKeyProvider` (PKCS#11 against a YubiKey /
-Nitrokey / OnlyKey PIV slot) - a different `KeyProvider` impl that calls
-into `cryptoki` instead of holding the scalar in process - plus the
-manifest / container plumbing that lets `OSGT-HW-P256-v1` ride the
-existing seal pipeline. The
-registry records whether each recipient pubkey is file-backed or
-hardware-backed so issuers can require hardware backing for sensitive
-material.
+**v0.4.11 closed the software reference path across Rust, Python, and the
+browser inspector.** `OSGT-HW-P256-v1` now has manifest/container plumbing,
+Python wrap/unwrap parity, and a public viewer sample fixture. The remaining
+hardware work is the `PivKeyProvider` (PKCS#11 against a YubiKey / Nitrokey /
+OnlyKey PIV slot), a different `KeyProvider` implementation that calls into
+`cryptoki` instead of holding the scalar in process. The registry records
+whether each recipient pubkey is file-backed or hardware-backed so issuers can
+require hardware backing for sensitive material.
### Registry in Rust
@@ -310,8 +310,8 @@ via VM and retype, hardware-key pull mid-open.
| 8 | Browser inspector, classic-suite decrypt, opsec scanner + CI | Shipped |
| 9 | Hybrid PQ decrypt in browser | Shipped (2026-05-03) |
| 10 | Outlook add-in | Next |
-| 11 | Hardware KeyProvider in Rust | In progress |
-| 12 | Rust Axum registry, migration tooling | In progress |
+| 11 | Hardware KeyProvider in Rust | Suite shipped (v0.4.11); PIV provider next |
+| 12 | Rust Axum registry, migration tooling | Migration tooling shipped; deployment burn-in next |
| 13 | arXiv preprint, threat-model repo document | Mid-term |
| 14 | IETF Internet-Draft, CFRG or equivalent BoF | Mid-term |
| 15 | USENIX Security Cycle 2, Black Hat EU 2026 | Mid-term |
oversight-rust/oversight-cli/src/main.rs +28 -9
@@ -214,7 +214,12 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
&recipient_pub_bytes,
)?;
std::fs::write(&output, &blob)?;
- println!("✓ sealed {} -> {} ({} bytes)", input.display(), output.display(), blob.len());
+ println!(
+ "✓ sealed {} -> {} ({} bytes)",
+ input.display(),
+ output.display(),
+ blob.len()
+ );
println!(" file_id: {}", manifest.file_id);
}
@@ -227,8 +232,12 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
let recipient_id = load_identity(&recipient)?;
let blob = std::fs::read(&input)?;
let policy_ctx = PolicyContext::local_only(policy_state_dir)?;
- let (plaintext, manifest) =
- open_sealed(&blob, recipient_id.x25519_priv.as_ref(), None, Some(&policy_ctx))?;
+ let (plaintext, manifest) = open_sealed(
+ &blob,
+ recipient_id.x25519_priv.as_ref(),
+ None,
+ Some(&policy_ctx),
+ )?;
if output.as_os_str() == "-" {
use std::io::Write;
std::io::stdout().write_all(&plaintext)?;
@@ -251,7 +260,10 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
println!(" suite_id: {}", sf.suite_id);
println!(" ciphertext_len: {} bytes", sf.ciphertext.len());
println!(" aead_nonce: {}", hex::encode(sf.aead_nonce));
- println!(" signature valid: {}", sf.manifest.verify().unwrap_or(false));
+ println!(
+ " signature valid: {}",
+ sf.manifest.verify().unwrap_or(false)
+ );
}
Commands::Watermark {
@@ -273,7 +285,8 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
}
};
- let marked = adapter.embed_watermark(&data, &mark_bytes)
+ let marked = adapter
+ .embed_watermark(&data, &mark_bytes)
.map_err(|e| format!("embed failed: {}", e))?;
std::fs::write(&output, &marked)?;
println!(
@@ -291,7 +304,8 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
let registry = FormatRegistry::default();
let adapter = resolve_adapter(&registry, &data, format.as_deref(), &input)?;
- let candidates = adapter.extract_watermark(&data)
+ let candidates = adapter
+ .extract_watermark(&data)
.map_err(|e| format!("extract failed: {}", e))?;
println!(
@@ -350,9 +364,14 @@ fn resolve_adapter<'a>(
path: &PathBuf,
) -> Result<&'a dyn FormatAdapter, Box<dyn std::error::Error>> {
if let Some(name) = format_override {
- return registry
- .by_name(name)
- .ok_or_else(|| format!("unknown format: '{}'. available: {:?}", name, registry.adapter_names()).into());
+ return registry.by_name(name).ok_or_else(|| {
+ format!(
+ "unknown format: '{}'. available: {:?}",
+ name,
+ registry.adapter_names()
+ )
+ .into()
+ });
}
// Try content-based detection first
oversight-rust/oversight-container/src/lib.rs +149 -37
@@ -104,7 +104,12 @@ pub struct SealedFile {
pub suite_id: u8,
}
-fn read_exact<'a>(buf: &'a [u8], at: &mut usize, n: usize, field: &'static str) -> Result<&'a [u8], ContainerError> {
+fn read_exact<'a>(
+ buf: &'a [u8],
+ at: &mut usize,
+ n: usize,
+ field: &'static str,
+) -> Result<&'a [u8], ContainerError> {
if buf.len() < *at + n {
return Err(ContainerError::Truncated {
wanted: n,
@@ -148,7 +153,9 @@ impl SealedFile {
let mut at = 0usize;
let magic = read_exact(data, &mut at, 6, "magic")?;
if magic != MAGIC {
- return Err(ContainerError::BadMagic { got: magic.to_vec() });
+ return Err(ContainerError::BadMagic {
+ got: magic.to_vec(),
+ });
}
let hdr = read_exact(data, &mut at, 2, "version/suite")?;
let fmt_ver = hdr[0];
@@ -244,10 +251,14 @@ pub fn seal(
));
}
if recipient_x25519_pub.len() != 32 {
- return Err(ContainerError::Precondition("recipient pubkey must be 32 bytes"));
+ return Err(ContainerError::Precondition(
+ "recipient pubkey must be 32 bytes",
+ ));
}
if issuer_ed25519_priv.len() != 32 {
- return Err(ContainerError::Precondition("issuer priv key must be 32 bytes"));
+ return Err(ContainerError::Precondition(
+ "issuer priv key must be 32 bytes",
+ ));
}
manifest.sign(issuer_ed25519_priv)?;
@@ -275,7 +286,9 @@ pub fn open_sealed(
policy_ctx: Option<&PolicyContext>,
) -> Result<(Vec<u8>, Manifest), ContainerError> {
if recipient_x25519_priv.len() != 32 {
- return Err(ContainerError::Precondition("recipient priv key must be 32 bytes"));
+ return Err(ContainerError::Precondition(
+ "recipient priv key must be 32 bytes",
+ ));
}
let sf = SealedFile::from_bytes(blob)?;
@@ -299,9 +312,16 @@ pub fn open_sealed(
return Err(ContainerError::Precondition("file expired (not_after)"));
}
}
- if let Some(nb) = sf.manifest.policy.get("not_before").and_then(|v| v.as_i64()) {
+ if let Some(nb) = sf
+ .manifest
+ .policy
+ .get("not_before")
+ .and_then(|v| v.as_i64())
+ {
if now < nb {
- return Err(ContainerError::Precondition("file not yet released (not_before)"));
+ return Err(ContainerError::Precondition(
+ "file not yet released (not_before)",
+ ));
}
}
@@ -384,7 +404,9 @@ pub fn seal_hw_p256(
));
}
if issuer_ed25519_priv.len() != 32 {
- return Err(ContainerError::Precondition("issuer priv key must be 32 bytes"));
+ return Err(ContainerError::Precondition(
+ "issuer priv key must be 32 bytes",
+ ));
}
manifest.sign(issuer_ed25519_priv)?;
@@ -443,9 +465,16 @@ pub fn open_sealed_with_provider(
return Err(ContainerError::Precondition("file expired (not_after)"));
}
}
- if let Some(nb) = sf.manifest.policy.get("not_before").and_then(|v| v.as_i64()) {
+ if let Some(nb) = sf
+ .manifest
+ .policy
+ .get("not_before")
+ .and_then(|v| v.as_i64())
+ {
if now < nb {
- return Err(ContainerError::Precondition("file not yet released (not_before)"));
+ return Err(ContainerError::Precondition(
+ "file not yet released (not_before)",
+ ));
}
}
@@ -493,7 +522,12 @@ pub fn seal_multi(
issuer_ed25519_priv: &[u8],
recipient_x25519_pubs: &[&[u8]],
) -> Result<Vec<u8>, ContainerError> {
- let _ = (plaintext, manifest, issuer_ed25519_priv, recipient_x25519_pubs);
+ let _ = (
+ plaintext,
+ manifest,
+ issuer_ed25519_priv,
+ recipient_x25519_pubs,
+ );
Err(ContainerError::Precondition(
"seal_multi disabled until manifests can bind all recipients",
))
@@ -502,10 +536,16 @@ pub fn seal_multi(
#[cfg(test)]
mod tests {
use super::*;
- use oversight_crypto::{ClassicIdentity, FileKeyProvider, SoftwareP256Identity, SoftwareP256KeyProvider};
+ use oversight_crypto::{
+ ClassicIdentity, FileKeyProvider, SoftwareP256Identity, SoftwareP256KeyProvider,
+ };
use oversight_manifest::Recipient;
- fn make_manifest(issuer: &ClassicIdentity, recipient: &ClassicIdentity, plaintext: &[u8]) -> Manifest {
+ fn make_manifest(
+ issuer: &ClassicIdentity,
+ recipient: &ClassicIdentity,
+ plaintext: &[u8],
+ ) -> Manifest {
Manifest::new(
"doc.txt",
crypto::content_hash(plaintext),
@@ -559,8 +599,15 @@ mod tests {
let recipient = ClassicIdentity::generate();
let plaintext = b"This is my secret document.";
let mut m = make_manifest(&issuer, &recipient, plaintext);
- let blob = seal(plaintext, &mut m, issuer.ed25519_priv.as_ref(), &recipient.x25519_pub).unwrap();
- let (pt, manifest) = open_sealed(&blob, recipient.x25519_priv.as_ref(), None, None).unwrap();
+ let blob = seal(
+ plaintext,
+ &mut m,
+ issuer.ed25519_priv.as_ref(),
+ &recipient.x25519_pub,
+ )
+ .unwrap();
+ let (pt, manifest) =
+ open_sealed(&blob, recipient.x25519_priv.as_ref(), None, None).unwrap();
assert_eq!(pt, plaintext);
assert_eq!(manifest.file_id, m.file_id);
}
@@ -572,7 +619,13 @@ mod tests {
let bob = ClassicIdentity::generate();
let plaintext = b"secret";
let mut m = make_manifest(&issuer, &alice, plaintext);
- let blob = seal(plaintext, &mut m, issuer.ed25519_priv.as_ref(), &alice.x25519_pub).unwrap();
+ let blob = seal(
+ plaintext,
+ &mut m,
+ issuer.ed25519_priv.as_ref(),
+ &alice.x25519_pub,
+ )
+ .unwrap();
// Bob tries to open - should fail at AEAD stage
assert!(open_sealed(&blob, bob.x25519_priv.as_ref(), None, None).is_err());
}
@@ -583,7 +636,13 @@ mod tests {
let alice = ClassicIdentity::generate();
let plaintext = b"secret";
let mut m = make_manifest(&issuer, &alice, plaintext);
- let mut blob = seal(plaintext, &mut m, issuer.ed25519_priv.as_ref(), &alice.x25519_pub).unwrap();
+ let mut blob = seal(
+ plaintext,
+ &mut m,
+ issuer.ed25519_priv.as_ref(),
+ &alice.x25519_pub,
+ )
+ .unwrap();
let len = blob.len();
blob[len - 1] ^= 0x01;
assert!(open_sealed(&blob, alice.x25519_priv.as_ref(), None, None).is_err());
@@ -606,7 +665,9 @@ mod tests {
blob.extend_from_slice(&(5u32 * 1024 * 1024).to_be_bytes());
blob.resize(100, 0);
match SealedFile::from_bytes(&blob) {
- Err(ContainerError::Oversized { field: "manifest", .. }) => (),
+ Err(ContainerError::Oversized {
+ field: "manifest", ..
+ }) => (),
other => panic!("expected Oversized manifest error, got {:?}", other),
}
}
@@ -628,7 +689,13 @@ mod tests {
let recipient = ClassicIdentity::generate();
let plaintext = b"classic via provider path";
let mut m = make_manifest(&issuer, &recipient, plaintext);
- let blob = seal(plaintext, &mut m, issuer.ed25519_priv.as_ref(), &recipient.x25519_pub).unwrap();
+ let blob = seal(
+ plaintext,
+ &mut m,
+ issuer.ed25519_priv.as_ref(),
+ &recipient.x25519_pub,
+ )
+ .unwrap();
let provider = FileKeyProvider::new(recipient);
let (pt, manifest) = open_sealed_with_provider(&blob, &provider, None, None).unwrap();
@@ -670,7 +737,8 @@ mod tests {
let plaintext = b"for alice only";
let mut m = make_hw_manifest(&issuer, &alice_pub, plaintext);
- let blob = seal_hw_p256(plaintext, &mut m, issuer.ed25519_priv.as_ref(), &alice_pub).unwrap();
+ let blob =
+ seal_hw_p256(plaintext, &mut m, issuer.ed25519_priv.as_ref(), &alice_pub).unwrap();
let bob_provider = SoftwareP256KeyProvider::new(bob);
assert!(
@@ -688,11 +756,15 @@ mod tests {
let plaintext = b"hw envelope";
let mut m = make_hw_manifest(&issuer, &alice_pub, plaintext);
- let blob = seal_hw_p256(plaintext, &mut m, issuer.ed25519_priv.as_ref(), &alice_pub).unwrap();
+ let blob =
+ seal_hw_p256(plaintext, &mut m, issuer.ed25519_priv.as_ref(), &alice_pub).unwrap();
let wrong_alg = FileKeyProvider::new(ClassicIdentity::generate());
let res = open_sealed_with_provider(&blob, &wrong_alg, None, None);
- assert!(res.is_err(), "X25519 provider must not be accepted for an OSGT-HW-P256-v1 container");
+ assert!(
+ res.is_err(),
+ "X25519 provider must not be accepted for an OSGT-HW-P256-v1 container"
+ );
}
#[test]
@@ -707,7 +779,10 @@ mod tests {
let mut m = make_hw_manifest(&issuer, &alice_pub, plaintext);
m.suite = crypto::SUITE_CLASSIC_V1.to_string();
let res = seal_hw_p256(plaintext, &mut m, issuer.ed25519_priv.as_ref(), &alice_pub);
- assert!(res.is_err(), "seal_hw_p256 must require manifest.suite == OSGT-HW-P256-v1");
+ assert!(
+ res.is_err(),
+ "seal_hw_p256 must require manifest.suite == OSGT-HW-P256-v1"
+ );
}
#[test]
@@ -715,9 +790,18 @@ mod tests {
// Each manifest suite must map to a unique container header byte;
// adding a new suite without updating this match would otherwise
// silently shape-shift into an UnsupportedManifestSuite at seal time.
- assert_eq!(suite_id_for_manifest(crypto::SUITE_CLASSIC_V1), Some(SUITE_CLASSIC_V1_ID));
- assert_eq!(suite_id_for_manifest(crypto::SUITE_HYBRID_V1), Some(SUITE_HYBRID_V1_ID));
- assert_eq!(suite_id_for_manifest(crypto::SUITE_HW_P256_V1), Some(SUITE_HW_P256_V1_ID));
+ assert_eq!(
+ suite_id_for_manifest(crypto::SUITE_CLASSIC_V1),
+ Some(SUITE_CLASSIC_V1_ID)
+ );
+ assert_eq!(
+ suite_id_for_manifest(crypto::SUITE_HYBRID_V1),
+ Some(SUITE_HYBRID_V1_ID)
+ );
+ assert_eq!(
+ suite_id_for_manifest(crypto::SUITE_HW_P256_V1),
+ Some(SUITE_HW_P256_V1_ID)
+ );
assert_eq!(suite_id_for_manifest("OSGT-UNKNOWN"), None);
}
@@ -727,10 +811,19 @@ mod tests {
let alice = ClassicIdentity::generate();
let plaintext = b"secret";
let mut m = make_manifest(&issuer, &alice, plaintext);
- let mut blob = seal(plaintext, &mut m, issuer.ed25519_priv.as_ref(), &alice.x25519_pub).unwrap();
+ let mut blob = seal(
+ plaintext,
+ &mut m,
+ issuer.ed25519_priv.as_ref(),
+ &alice.x25519_pub,
+ )
+ .unwrap();
blob[7] ^= 0x01;
match SealedFile::from_bytes(&blob) {
- Err(ContainerError::SuiteMismatch { header, manifest_suite }) => {
+ Err(ContainerError::SuiteMismatch {
+ header,
+ manifest_suite,
+ }) => {
assert_eq!(header, 0);
assert_eq!(manifest_suite, crypto::SUITE_CLASSIC_V1);
}
@@ -744,7 +837,13 @@ mod tests {
let alice = ClassicIdentity::generate();
let plaintext = b"secret";
let mut m = make_manifest(&issuer, &alice, plaintext);
- let mut blob = seal(plaintext, &mut m, issuer.ed25519_priv.as_ref(), &alice.x25519_pub).unwrap();
+ let mut blob = seal(
+ plaintext,
+ &mut m,
+ issuer.ed25519_priv.as_ref(),
+ &alice.x25519_pub,
+ )
+ .unwrap();
blob.extend_from_slice(b"junk");
match SealedFile::from_bytes(&blob) {
Err(ContainerError::TrailingBytes(4)) => (),
@@ -758,8 +857,14 @@ mod tests {
let alice = ClassicIdentity::generate();
let plaintext = b"secret";
let mut m = make_manifest(&issuer, &alice, plaintext);
- m.policy["not_after"] = serde_json::json!(1000); // long ago
- let blob = seal(plaintext, &mut m, issuer.ed25519_priv.as_ref(), &alice.x25519_pub).unwrap();
+ m.policy["not_after"] = serde_json::json!(1000); // long ago
+ let blob = seal(
+ plaintext,
+ &mut m,
+ issuer.ed25519_priv.as_ref(),
+ &alice.x25519_pub,
+ )
+ .unwrap();
match open_sealed(&blob, alice.x25519_priv.as_ref(), None, None) {
Err(ContainerError::Precondition("file expired (not_after)")) => (),
other => panic!("expected expiry error, got {:?}", other.is_ok()),
@@ -774,12 +879,16 @@ mod tests {
let plaintext = b"limited";
let mut m = make_manifest(&issuer, &alice, plaintext);
m.policy["max_opens"] = serde_json::json!(1);
- let blob = seal(plaintext, &mut m, issuer.ed25519_priv.as_ref(), &alice.x25519_pub).unwrap();
+ let blob = seal(
+ plaintext,
+ &mut m,
+ issuer.ed25519_priv.as_ref(),
+ &alice.x25519_pub,
+ )
+ .unwrap();
- let dir = std::env::temp_dir().join(format!(
- "oversight-container-policy-{}",
- std::process::id()
- ));
+ let dir =
+ std::env::temp_dir().join(format!("oversight-container-policy-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&dir);
let ctx = oversight_policy::PolicyContext::local_only(&dir).unwrap();
@@ -818,7 +927,10 @@ mod tests {
let recipients: Vec<&[u8]> = vec![&alice.x25519_pub, &bob.x25519_pub, &carol.x25519_pub];
match seal_multi(plaintext, &mut m, issuer.ed25519_priv.as_ref(), &recipients) {
Err(ContainerError::Precondition(msg)) => assert!(msg.contains("seal_multi disabled")),
- other => panic!("expected seal_multi to fail closed, got {:?}", other.is_ok()),
+ other => panic!(
+ "expected seal_multi to fail closed, got {:?}",
+ other.is_ok()
+ ),
}
}
}
oversight-rust/oversight-crypto/src/lib.rs +77 -23
@@ -33,16 +33,15 @@ use ed25519_dalek::{
VerifyingKey as EdVerifyingKey,
};
use hkdf::Hkdf;
+use p256::{
+ ecdh::diffie_hellman as p256_diffie_hellman, elliptic_curve::sec1::ToEncodedPoint,
+ PublicKey as P256PublicKey, SecretKey as P256SecretKey,
+};
use rand_core::{OsRng, RngCore};
use sha2::{Digest, Sha256};
use thiserror::Error;
use x25519_dalek::{PublicKey as X25519PublicKey, StaticSecret as X25519StaticSecret};
use zeroize::{Zeroize, Zeroizing};
-use p256::{
- ecdh::diffie_hellman as p256_diffie_hellman,
- elliptic_curve::sec1::ToEncodedPoint,
- PublicKey as P256PublicKey, SecretKey as P256SecretKey,
-};
pub const XCHACHA_KEY_LEN: usize = 32;
pub const XCHACHA_NONCE_LEN: usize = 24;
@@ -147,7 +146,13 @@ pub fn aead_encrypt(
let cipher = XChaCha20Poly1305::new(key.into());
let nonce = XChaCha20Poly1305::generate_nonce(&mut OsRng);
let ct = cipher
- .encrypt(&nonce, Payload { msg: plaintext, aad })
+ .encrypt(
+ &nonce,
+ Payload {
+ msg: plaintext,
+ aad,
+ },
+ )
.map_err(|_| CryptoError::AeadFailed)?;
let mut nonce_arr = [0u8; XCHACHA_NONCE_LEN];
nonce_arr.copy_from_slice(&nonce);
@@ -174,7 +179,13 @@ pub fn aead_decrypt(
}
let cipher = XChaCha20Poly1305::new(key.into());
cipher
- .decrypt(nonce.into(), Payload { msg: ciphertext, aad })
+ .decrypt(
+ nonce.into(),
+ Payload {
+ msg: ciphertext,
+ aad,
+ },
+ )
.map_err(|_| CryptoError::AeadFailed)
}
@@ -225,7 +236,11 @@ impl WrappedDek {
eph.copy_from_slice(&eph_bytes);
let mut nonce = [0u8; XCHACHA_NONCE_LEN];
nonce.copy_from_slice(&nonce_bytes);
- Ok(WrappedDek { ephemeral_pub: eph, nonce, wrapped_dek: wrapped })
+ Ok(WrappedDek {
+ ephemeral_pub: eph,
+ nonce,
+ wrapped_dek: wrapped,
+ })
}
}
@@ -256,7 +271,11 @@ pub fn wrap_dek_for_recipient(
.map_err(|_| CryptoError::Hkdf)?;
let (nonce, wrapped) = aead_encrypt(kek.as_ref(), dek, b"oversight-dek")?;
- Ok(WrappedDek { ephemeral_pub: eph_pub.to_bytes(), nonce, wrapped_dek: wrapped })
+ Ok(WrappedDek {
+ ephemeral_pub: eph_pub.to_bytes(),
+ nonce,
+ wrapped_dek: wrapped,
+ })
}
pub fn unwrap_dek(
@@ -359,12 +378,18 @@ pub struct FileKeyProvider {
impl FileKeyProvider {
/// Wrap an existing [`ClassicIdentity`] without a label.
pub fn new(identity: ClassicIdentity) -> Self {
- Self { inner: identity, label: None }
+ Self {
+ inner: identity,
+ label: None,
+ }
}
/// Wrap with a label (e.g., the recipient_id from the identity JSON).
pub fn with_label(identity: ClassicIdentity, label: impl Into<String>) -> Self {
- Self { inner: identity, label: Some(label.into()) }
+ Self {
+ inner: identity,
+ label: Some(label.into()),
+ }
}
/// Borrow the underlying classic identity. Hardware providers won't be
@@ -464,7 +489,10 @@ impl SoftwareP256Identity {
debug_assert_eq!(bytes.len(), P256_PUBLIC_KEY_LEN);
let mut public_sec1 = [0u8; P256_PUBLIC_KEY_LEN];
public_sec1.copy_from_slice(bytes);
- Self { secret, public_sec1 }
+ Self {
+ secret,
+ public_sec1,
+ }
}
pub fn public_key_sec1(&self) -> &[u8; P256_PUBLIC_KEY_LEN] {
@@ -518,7 +546,11 @@ impl WrappedDekP256 {
eph.copy_from_slice(&eph_bytes);
let mut nonce = [0u8; XCHACHA_NONCE_LEN];
nonce.copy_from_slice(&nonce_bytes);
- Ok(WrappedDekP256 { ephemeral_pub: eph, nonce, wrapped_dek: wrapped })
+ Ok(WrappedDekP256 {
+ ephemeral_pub: eph,
+ nonce,
+ wrapped_dek: wrapped,
+ })
}
}
@@ -535,11 +567,12 @@ pub fn wrap_dek_for_recipient_p256(
got: recipient_p256_pub_sec1.len(),
});
}
- let recipient_pub = P256PublicKey::from_sec1_bytes(recipient_p256_pub_sec1)
- .map_err(|_| CryptoError::InvalidKeyLength {
+ let recipient_pub = P256PublicKey::from_sec1_bytes(recipient_p256_pub_sec1).map_err(|_| {
+ CryptoError::InvalidKeyLength {
expected: P256_PUBLIC_KEY_LEN,
got: recipient_p256_pub_sec1.len(),
- })?;
+ }
+ })?;
let eph_secret = P256SecretKey::random(&mut OsRng);
let eph_pub = eph_secret.public_key();
@@ -563,7 +596,11 @@ pub fn wrap_dek_for_recipient_p256(
.map_err(|_| CryptoError::Hkdf)?;
let (nonce, wrapped) = aead_encrypt(kek.as_ref(), dek, b"oversight-hw-p256-dek")?;
- Ok(WrappedDekP256 { ephemeral_pub: eph_pub_arr, nonce, wrapped_dek: wrapped })
+ Ok(WrappedDekP256 {
+ ephemeral_pub: eph_pub_arr,
+ nonce,
+ wrapped_dek: wrapped,
+ })
}
/// Software-backed P-256 [`KeyProvider`]. Useful for tests and as a fallback
@@ -576,11 +613,17 @@ pub struct SoftwareP256KeyProvider {
impl SoftwareP256KeyProvider {
pub fn new(identity: SoftwareP256Identity) -> Self {
- Self { inner: identity, label: None }
+ Self {
+ inner: identity,
+ label: None,
+ }
}
pub fn with_label(identity: SoftwareP256Identity, label: impl Into<String>) -> Self {
- Self { inner: identity, label: Some(label.into()) }
+ Self {
+ inner: identity,
+ label: Some(label.into()),
+ }
}
pub fn identity(&self) -> &SoftwareP256Identity {
@@ -792,7 +835,9 @@ mod tests {
// Raw x25519_dalek for comparison.
let bob_sk = X25519StaticSecret::from(bob_priv_copy);
- let raw = bob_sk.diffie_hellman(&X25519PublicKey::from(alice_pub_copy)).to_bytes();
+ let raw = bob_sk
+ .diffie_hellman(&X25519PublicKey::from(alice_pub_copy))
+ .to_bytes();
assert_eq!(via_provider.as_slice(), &raw[..]);
}
@@ -843,7 +888,10 @@ mod tests {
// Bob's provider should fail to recover the DEK.
let res = unwrap_dek_with_provider(&wrapped, &provider_bob);
- assert!(res.is_err(), "Bob's provider must not unwrap a DEK addressed to Alice");
+ assert!(
+ res.is_err(),
+ "Bob's provider must not unwrap a DEK addressed to Alice"
+ );
}
// ------- P-256 (OSGT-HW-P256-v1) ----------------------------------------
@@ -888,7 +936,10 @@ mod tests {
let wrapped = wrap_dek_for_recipient_p256(dek.as_ref(), &alice_pub).unwrap();
let res = unwrap_dek_with_provider_p256(&wrapped, &provider_bob);
- assert!(res.is_err(), "Bob's P-256 provider must not unwrap a DEK addressed to Alice");
+ assert!(
+ res.is_err(),
+ "Bob's P-256 provider must not unwrap a DEK addressed to Alice"
+ );
}
#[test]
@@ -903,7 +954,10 @@ mod tests {
let bob_x25519 = FileKeyProvider::new(ClassicIdentity::generate());
let res = unwrap_dek_with_provider_p256(&wrapped, &bob_x25519);
- assert!(res.is_err(), "X25519 provider must not be accepted for a P-256 envelope");
+ assert!(
+ res.is_err(),
+ "X25519 provider must not be accepted for a P-256 envelope"
+ );
}
#[test]
oversight-rust/oversight-formats/src/docx.rs +18 -22
@@ -134,8 +134,8 @@ pub fn embed_docx_metadata(
.map_err(|e| FormatError::Internal(format!("ZIP entry error: {}", e)))?;
let name = entry.name().to_string();
- let options = SimpleFileOptions::default()
- .compression_method(zip::CompressionMethod::Deflated);
+ let options =
+ SimpleFileOptions::default().compression_method(zip::CompressionMethod::Deflated);
writer
.start_file(&name, options)
.map_err(|e| FormatError::Internal(format!("ZIP write error: {}", e)))?;
@@ -146,22 +146,20 @@ pub fn embed_docx_metadata(
std::io::Read::read_to_end(&mut entry, &mut contents)
.map_err(|e| FormatError::Io(e))?;
let modified = inject_keywords_into_core_xml(&contents, &tag)?;
- std::io::Write::write_all(&mut writer, &modified)
- .map_err(|e| FormatError::Io(e))?;
+ std::io::Write::write_all(&mut writer, &modified).map_err(|e| FormatError::Io(e))?;
} else {
// Copy entry unchanged
let mut contents = Vec::new();
std::io::Read::read_to_end(&mut entry, &mut contents)
.map_err(|e| FormatError::Io(e))?;
- std::io::Write::write_all(&mut writer, &contents)
- .map_err(|e| FormatError::Io(e))?;
+ std::io::Write::write_all(&mut writer, &contents).map_err(|e| FormatError::Io(e))?;
}
}
// If there was no docProps/core.xml, create one
if !found_core {
- let options = SimpleFileOptions::default()
- .compression_method(zip::CompressionMethod::Deflated);
+ let options =
+ SimpleFileOptions::default().compression_method(zip::CompressionMethod::Deflated);
writer
.start_file("docProps/core.xml", options)
.map_err(|e| FormatError::Internal(format!("ZIP write error: {}", e)))?;
@@ -192,8 +190,7 @@ pub fn extract_docx_metadata(docx_bytes: &[u8]) -> Result<DocxOversightMeta, For
// Try to read docProps/core.xml
if let Ok(mut entry) = archive.by_name("docProps/core.xml") {
let mut contents = Vec::new();
- std::io::Read::read_to_end(&mut entry, &mut contents)
- .map_err(|e| FormatError::Io(e))?;
+ std::io::Read::read_to_end(&mut entry, &mut contents).map_err(|e| FormatError::Io(e))?;
let keywords = extract_keywords_from_core_xml(&contents)?;
if let Some(kw) = keywords {
parse_oversight_tag(&kw, &mut meta);
@@ -217,8 +214,7 @@ pub fn extract_body_text(docx_bytes: &[u8]) -> Result<String, FormatError> {
.map_err(|e| FormatError::Malformed(format!("missing word/document.xml: {}", e)))?;
let mut contents = Vec::new();
- std::io::Read::read_to_end(&mut entry, &mut contents)
- .map_err(|e| FormatError::Io(e))?;
+ std::io::Read::read_to_end(&mut entry, &mut contents).map_err(|e| FormatError::Io(e))?;
extract_text_elements(&contents)
}
@@ -324,10 +320,7 @@ fn extract_keywords_from_core_xml(xml_bytes: &[u8]) -> Result<Option<String>, Fo
in_keywords = true;
}
Ok(Event::Text(ref t)) if in_keywords => {
- let text = t
- .unescape()
- .unwrap_or_default()
- .to_string();
+ let text = t.unescape().unwrap_or_default().to_string();
return Ok(Some(text));
}
Ok(Event::End(ref e)) if e.name().as_ref() == b"cp:keywords" => {
@@ -366,10 +359,7 @@ fn extract_text_elements(xml_bytes: &[u8]) -> Result<String, FormatError> {
}
}
Ok(Event::Text(ref t)) if in_text => {
- let text = t
- .unescape()
- .unwrap_or_default()
- .to_string();
+ let text = t.unescape().unwrap_or_default().to_string();
paragraph_texts.push(text);
}
Ok(Event::End(ref e)) => {
@@ -486,7 +476,10 @@ mod tests {
fn sanitize_field_code_strips_dangerous() {
assert_eq!(sanitize_field_code("normal text"), "normal text");
assert_eq!(sanitize_field_code("{FIELD \\s}"), "FIELD s");
- assert_eq!(sanitize_field_code("<script>alert('x')</script>"), "scriptalert(x)/script");
+ assert_eq!(
+ sanitize_field_code("<script>alert('x')</script>"),
+ "scriptalert(x)/script"
+ );
assert_eq!(sanitize_field_code("hello&world"), "helloworld");
}
@@ -502,7 +495,10 @@ mod tests {
#[test]
fn parse_oversight_tag_with_other_keywords() {
let mut meta = DocxOversightMeta::default();
- parse_oversight_tag("finance report oversight:cafebabe;issuer:alice quarterly", &mut meta);
+ parse_oversight_tag(
+ "finance report oversight:cafebabe;issuer:alice quarterly",
+ &mut meta,
+ );
assert_eq!(meta.mark_id.as_deref(), Some("cafebabe"));
assert_eq!(meta.issuer_id.as_deref(), Some("alice"));
}
oversight-rust/oversight-formats/src/pdf.rs +4 -6
@@ -292,12 +292,10 @@ fn sanitize_pdf_string(s: &str) -> String {
/// Helper to get a string value from a PDF dictionary.
fn get_string_from_dict(dict: &Dictionary, key: &str) -> Option<String> {
- dict.get(key.as_bytes())
- .ok()
- .and_then(|obj| match obj {
- Object::String(bytes, _) => String::from_utf8(bytes.clone()).ok(),
- _ => None,
- })
+ dict.get(key.as_bytes()).ok().and_then(|obj| match obj {
+ Object::String(bytes, _) => String::from_utf8(bytes.clone()).ok(),
+ _ => None,
+ })
}
/// Simplified text extraction from a PDF content stream.
oversight-rust/oversight-policy/src/lib.rs +9 -11
@@ -112,19 +112,13 @@ fn sanitize_file_id(file_id: &str) -> Result<()> {
fn counter_path(ctx: &PolicyContext, file_id: &str) -> Result<PathBuf> {
sanitize_file_id(file_id)?;
- let dir = ctx
- .state_dir
- .as_ref()
- .ok_or(PolicyError::ContextRequired)?;
+ let dir = ctx.state_dir.as_ref().ok_or(PolicyError::ContextRequired)?;
Ok(dir.join(format!("{}.opens.json", file_id)))
}
fn lock_path(ctx: &PolicyContext, file_id: &str) -> Result<PathBuf> {
sanitize_file_id(file_id)?;
- let dir = ctx
- .state_dir
- .as_ref()
- .ok_or(PolicyError::ContextRequired)?;
+ let dir = ctx.state_dir.as_ref().ok_or(PolicyError::ContextRequired)?;
Ok(dir.join(format!("{}.opens.lock", file_id)))
}
@@ -255,7 +249,8 @@ pub fn record_open(manifest: &Manifest, ctx: Option<&PolicyContext>) -> Result<u
"registry max_opens enforcement is not implemented; refusing local fallback".into(),
)),
Mode::Hybrid => Err(PolicyError::Violation(
- "hybrid max_opens enforcement is not implemented; refusing silent local fallback".into(),
+ "hybrid max_opens enforcement is not implemented; refusing silent local fallback"
+ .into(),
)),
}
}
@@ -281,6 +276,7 @@ mod tests {
recipient_id: "alice".into(),
x25519_pub: "00".repeat(32),
ed25519_pub: None,
+ p256_pub: None,
},
"https://registry",
"text/plain",
@@ -317,7 +313,9 @@ mod tests {
"jurisdiction": "EU",
}));
let dir = TempDir::new().unwrap();
- let ctx = PolicyContext::local_only(dir.path()).unwrap().with_jurisdiction("US");
+ let ctx = PolicyContext::local_only(dir.path())
+ .unwrap()
+ .with_jurisdiction("US");
assert!(check_policy(&m, Some(&ctx)).is_err());
}
@@ -339,7 +337,7 @@ mod tests {
}));
assert_eq!(record_open(&m, Some(&ctx)).unwrap(), 1);
assert_eq!(record_open(&m, Some(&ctx)).unwrap(), 2);
- assert!(record_open(&m, Some(&ctx)).is_err()); // 3rd exceeds
+ assert!(record_open(&m, Some(&ctx)).is_err()); // 3rd exceeds
}
#[test]
oversight-rust/oversight-rekor/examples/conformance_helper.rs +11 -8
@@ -27,11 +27,17 @@ fn main() {
}
match args[1].as_str() {
"pae" => {
- let payload_type = args.get(2).cloned().unwrap_or_else(|| DSSE_PAYLOAD_TYPE.into());
+ let payload_type = args
+ .get(2)
+ .cloned()
+ .unwrap_or_else(|| DSSE_PAYLOAD_TYPE.into());
let payload = read_stdin();
let out = pae(&payload_type, &payload);
let stdout = io::stdout();
- stdout.lock().write_all(hex::encode(out).as_bytes()).unwrap();
+ stdout
+ .lock()
+ .write_all(hex::encode(out).as_bytes())
+ .unwrap();
}
"verify" => {
let pub_hex = args.get(2).expect("pub hex required");
@@ -50,8 +56,7 @@ fn main() {
let priv_hex = args.get(2).expect("priv hex required");
let priv_bytes = hex::decode(priv_hex).expect("valid hex priv");
let raw = read_stdin();
- let stmt: serde_json::Value =
- serde_json::from_slice(&raw).expect("parse statement");
+ let stmt: serde_json::Value = serde_json::from_slice(&raw).expect("parse statement");
let env = sign_dsse(&stmt, &priv_bytes, "").expect("sign");
let canon = env.to_canonical_json().expect("canonicalize");
print!("{}", canon);
@@ -59,15 +64,13 @@ fn main() {
"payload_b64" => {
// Read envelope from stdin, print the base64 payload.
let raw = read_stdin();
- let env: DsseEnvelope =
- serde_json::from_slice(&raw).expect("parse envelope");
+ let env: DsseEnvelope = serde_json::from_slice(&raw).expect("parse envelope");
print!("{}", env.payload_b64);
}
"decode_payload" => {
// Read envelope from stdin, print decoded payload (the canonical statement bytes).
let raw = read_stdin();
- let env: DsseEnvelope =
- serde_json::from_slice(&raw).expect("parse envelope");
+ let env: DsseEnvelope = serde_json::from_slice(&raw).expect("parse envelope");
let bytes = B64.decode(env.payload_b64.as_bytes()).expect("b64");
io::stdout().lock().write_all(&bytes).unwrap();
}
oversight-rust/oversight-rekor/src/lib.rs +19 -11
@@ -15,8 +15,7 @@ use std::collections::BTreeMap;
use base64::{engine::general_purpose::STANDARD as B64, Engine as _};
use ed25519_dalek::{
- Signature, Signer, SigningKey, Verifier, VerifyingKey, SECRET_KEY_LENGTH,
- SIGNATURE_LENGTH,
+ Signature, Signer, SigningKey, Verifier, VerifyingKey, SECRET_KEY_LENGTH, SIGNATURE_LENGTH,
};
use serde::{Deserialize, Serialize};
use serde_json::Value;
@@ -98,7 +97,10 @@ impl OversightRegistrationPredicate {
"issuer_pubkey_ed25519".into(),
Value::from(self.issuer_pubkey_ed25519.clone()),
);
- m.insert("recipient_id".into(), Value::from(self.recipient_id.clone()));
+ m.insert(
+ "recipient_id".into(),
+ Value::from(self.recipient_id.clone()),
+ );
m.insert(
"recipient_pubkey_sha256".into(),
Value::from(self.recipient_pubkey_sha256.clone()),
@@ -131,8 +133,7 @@ impl OversightRegistrationPredicate {
/// Compute `recipient_pubkey_sha256` from the raw X25519 public key (hex).
pub fn hash_recipient_pubkey(x25519_pub_hex: &str) -> Result<String, RekorError> {
- let raw = hex::decode(x25519_pub_hex)
- .map_err(|_| RekorError::KeyLength("x25519 pub hex"))?;
+ let raw = hex::decode(x25519_pub_hex).map_err(|_| RekorError::KeyLength("x25519 pub hex"))?;
let h = Sha256::digest(&raw);
Ok(hex::encode(h))
}
@@ -324,7 +325,10 @@ pub fn verify_inclusion_offline(
return (false, "dsse payload missing subject digest");
}
if subject_digest != Some(expected_content_hash_sha256_hex) {
- return (false, "dsse subject digest does not match expected content hash");
+ return (
+ false,
+ "dsse subject digest does not match expected content hash",
+ );
}
let tle = match bundle_rekor_field.get("transparency_log_entry") {
Some(v) if v.is_object() => v,
@@ -334,7 +338,10 @@ pub fn verify_inclusion_offline(
.iter()
.any(|k| tle.get(*k).is_some());
if !has_proof {
- return (false, "transparency_log_entry has no inclusion proof or logEntry shape");
+ return (
+ false,
+ "transparency_log_entry has no inclusion proof or logEntry shape",
+ );
}
(true, "ok")
}
@@ -511,8 +518,7 @@ mod tests {
// Python: hashlib.sha256(bytes.fromhex("42"*32)).hexdigest()
let h = hash_recipient_pubkey(&"42".repeat(32)).unwrap();
// Pre-computed reference value.
- let expected =
- "bcdfe2c5b3b1c6c4f0d2b3f9c2c95dc6c0f9b1e6f6f9e60c7e75c5f37e80f1d4";
+ let expected = "bcdfe2c5b3b1c6c4f0d2b3f9c2c95dc6c0f9b1e6f6f9e60c7e75c5f37e80f1d4";
// We don't hard-code the exact digest here (would brittle-tie to a
// specific byte pattern); instead just check length + determinism.
assert_eq!(h.len(), 64);
@@ -561,7 +567,8 @@ mod tests {
let stmt = build_statement("a", &"b".repeat(64), &pred);
let env = sign_dsse(&stmt, &sk.to_bytes(), "").unwrap();
let bundle_rekor = serde_json::json!({});
- let (ok, reason) = verify_inclusion_offline(&bundle_rekor, &env, pk.as_bytes(), &"b".repeat(64));
+ let (ok, reason) =
+ verify_inclusion_offline(&bundle_rekor, &env, pk.as_bytes(), &"b".repeat(64));
assert!(!ok);
assert!(reason.contains("transparency_log_entry"));
}
@@ -589,7 +596,8 @@ mod tests {
let bundle_rekor = serde_json::json!({
"transparency_log_entry": {"inclusionProof": {}}
});
- let (ok, reason) = verify_inclusion_offline(&bundle_rekor, &env, pk.as_bytes(), &"c".repeat(64));
+ let (ok, reason) =
+ verify_inclusion_offline(&bundle_rekor, &env, pk.as_bytes(), &"c".repeat(64));
assert!(!ok);
assert!(reason.contains("subject digest"));
}
oversight-rust/oversight-semantic/examples/debug.rs +15 -7
@@ -1,4 +1,4 @@
-use oversight_semantic::{embed_synonyms, verify_synonyms, iter_matchable_words};
+use oversight_semantic::{embed_synonyms, iter_matchable_words, verify_synonyms};
const TEXT: &str = "Q3 revenue performance exceeded expectations across all business units. \
The team plans to continue the expansion strategy outlined in our report at \
@@ -15,14 +15,22 @@ fn main() {
let matches_before = iter_matchable_words(TEXT).len();
let matches_after = iter_matchable_words(&marked).len();
let (ok, score) = verify_synonyms(&marked, mark, 0.70);
- println!("mark {}: matches before={} after={}, verify ok={} score={:.3}",
- name, matches_before, matches_after, ok, score);
+ println!(
+ "mark {}: matches before={} after={}, verify ok={} score={:.3}",
+ name, matches_before, matches_after, ok, score
+ );
// Print the first few matches before/after
- let before: Vec<_> = iter_matchable_words(TEXT).iter().take(10)
- .map(|m| (m.orig_word.clone(), m.class_index, m.variant_index)).collect();
- let after: Vec<_> = iter_matchable_words(&marked).iter().take(10)
- .map(|m| (m.orig_word.clone(), m.class_index, m.variant_index)).collect();
+ let before: Vec<_> = iter_matchable_words(TEXT)
+ .iter()
+ .take(10)
+ .map(|m| (m.orig_word.clone(), m.class_index, m.variant_index))
+ .collect();
+ let after: Vec<_> = iter_matchable_words(&marked)
+ .iter()
+ .take(10)
+ .map(|m| (m.orig_word.clone(), m.class_index, m.variant_index))
+ .collect();
println!(" first 10 before: {:?}", before);
println!(" first 10 after: {:?}", after);
}
oversight-rust/oversight-semantic/src/lib.rs +32 -12
@@ -73,8 +73,7 @@ static URL_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"https?://\S+").unwrap());
static EMAIL_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"\b[\w.+-]+@[\w.-]+\.\w+\b").unwrap());
static INLINE_CODE_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"`[^`]+`").unwrap());
static CODE_BLOCK_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?s)```.*?```").unwrap());
-static UNIX_PATH_RE: Lazy<Regex> =
- Lazy::new(|| Regex::new(r"(?:^|\s)(?:/|~/|\./)[^\s]+").unwrap());
+static UNIX_PATH_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?:^|\s)(?:/|~/|\./)[^\s]+").unwrap());
static HEX_BLOB_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"\b[A-Fa-f0-9]{16,}\b").unwrap());
static BASE64_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"\b[A-Za-z0-9+/]{32,}={0,2}\b").unwrap());
static WORD_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"\b([A-Za-z]+)\b").unwrap());
@@ -83,8 +82,13 @@ static WORD_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"\b([A-Za-z]+)\b").unwrap
fn skip_mask(text: &str) -> Vec<bool> {
let mut mask = vec![false; text.len()];
let patterns: &[&Lazy<Regex>] = &[
- &URL_RE, &EMAIL_RE, &INLINE_CODE_RE, &CODE_BLOCK_RE, &UNIX_PATH_RE,
- &HEX_BLOB_RE, &BASE64_RE,
+ &URL_RE,
+ &EMAIL_RE,
+ &INLINE_CODE_RE,
+ &CODE_BLOCK_RE,
+ &UNIX_PATH_RE,
+ &HEX_BLOB_RE,
+ &BASE64_RE,
];
for pat in patterns {
for m in pat.find_iter(text) {
@@ -179,8 +183,15 @@ fn case_preserve(replacement: &str, orig: &str) -> String {
if orig.chars().all(|c| c.is_uppercase() || !c.is_alphabetic()) && orig.len() > 1 {
return replacement.to_uppercase();
}
- let first_upper = orig.chars().next().map(|c| c.is_uppercase()).unwrap_or(false);
- let rest_lower = orig.chars().skip(1).all(|c| c.is_lowercase() || !c.is_alphabetic());
+ let first_upper = orig
+ .chars()
+ .next()
+ .map(|c| c.is_uppercase())
+ .unwrap_or(false);
+ let rest_lower = orig
+ .chars()
+ .skip(1)
+ .all(|c| c.is_lowercase() || !c.is_alphabetic());
if first_upper && rest_lower {
let mut s = String::new();
for (i, c) in replacement.chars().enumerate() {
@@ -281,7 +292,8 @@ pub fn verify_synonyms(text: &str, candidate_mark_id: &[u8], threshold: f64) ->
mod tests {
use super::*;
- const TEST_TEXT: &str = "Q3 revenue performance exceeded expectations across all business units. \
+ const TEST_TEXT: &str =
+ "Q3 revenue performance exceeded expectations across all business units. \
The team plans to continue the expansion strategy outlined in our report at \
https://internal.example.com/q3-2026.pdf and will begin hiring in \
/home/claude/hiring_plan.docx this month. However, there are important risks \
@@ -312,10 +324,14 @@ Overall the results show clear momentum and a strong basis for continued growth.
fn url_and_path_preserved_through_embed() {
let mark = b"\x01\x23\x45\x67\x89\xab\xcd\xef";
let marked = embed_synonyms(TEST_TEXT, mark, 5);
- assert!(marked.contains("https://internal.example.com/q3-2026.pdf"),
- "URL was munged");
- assert!(marked.contains("/home/claude/hiring_plan.docx"),
- "path was munged");
+ assert!(
+ marked.contains("https://internal.example.com/q3-2026.pdf"),
+ "URL was munged"
+ );
+ assert!(
+ marked.contains("/home/claude/hiring_plan.docx"),
+ "path was munged"
+ );
}
#[test]
@@ -334,7 +350,11 @@ Overall the results show clear momentum and a strong basis for continued growth.
let marked = embed_synonyms(TEST_TEXT, good, 5);
let (ok, score) = verify_synonyms(&marked, bad, 0.70);
assert!(!ok, "wrong mark verified (score={})", score);
- assert!(score < 0.70, "wrong-mark score suspiciously high: {}", score);
+ assert!(
+ score < 0.70,
+ "wrong-mark score suspiciously high: {}",
+ score
+ );
}
#[test]
oversight-rust/oversight-tlog/src/lib.rs +9 -11
@@ -116,14 +116,13 @@ pub fn verify_inclusion_proof(
return false;
}
- fn rec(
- h_in: [u8; 32],
- m: usize,
- remaining: &[[u8; 32]],
- n: usize,
- ) -> Option<[u8; 32]> {
+ fn rec(h_in: [u8; 32], m: usize, remaining: &[[u8; 32]], n: usize) -> Option<[u8; 32]> {
if n == 1 {
- return if remaining.is_empty() { Some(h_in) } else { None };
+ return if remaining.is_empty() {
+ Some(h_in)
+ } else {
+ None
+ };
}
if remaining.is_empty() {
return None;
@@ -278,9 +277,8 @@ impl TransparencyLog {
/// Append a JSON event. Helper that canonicalizes and calls append().
pub fn append_event(&self, event: &serde_json::Value) -> Result<usize> {
- let bytes = serde_jcs::to_vec(event).map_err(|_| {
- TlogError::Json(serde_json::Error::custom("canonicalization failed"))
- })?;
+ let bytes = serde_jcs::to_vec(event)
+ .map_err(|_| TlogError::Json(serde_json::Error::custom("canonicalization failed")))?;
self.append(&bytes)
}
@@ -335,7 +333,7 @@ impl TransparencyLog {
let leaves_copy: Vec<[u8; 32]> = leaves.clone();
let leaf_hash_hex = hex::encode(leaves[index]);
let tree_size = leaves.len();
- drop(leaves); // release before calling root() which also locks
+ drop(leaves); // release before calling root() which also locks
let path = audit_path(&leaves_copy, index);
let root = self.root();