jcs-admit 0.1.1

Admission control for JSON about to be signed: refuses duplicate object members, nesting past a depth cap, non-scalar strings and unsafe integers on the raw bytes, then delegates RFC 8785 canonicalization to serde_json_canonicalizer
Documentation

jcs-admit

crates.io docs.rs license

Admission control for JSON that is about to be signed or verified. It checks the raw bytes before any parser sees them, refuses documents that two parsers could read differently, and then writes the RFC 8785 canonical form.

It's for Rust code that signs, verifies or hashes canonical JSON, such as in-toto statements, DSSE payloads or signed receipts.

Quick start

Add it as a pinned dependency:

cargo add jcs-admit@0.1.1

Then admit a document before you sign it:

use jcs_admit::{admit, Error};

fn main() -> Result<(), Error> {
    let canonical = admit(br#"{ "b": 1, "a": [1.0, 1e30] }"#)?;
    assert_eq!(canonical, br#"{"a":[1,1e+30],"b":1}"#);

    // One document, two readings: refused before any parser picks one.
    let refused = admit(br#"{"a":1,"a":2}"#);
    assert!(matches!(refused, Err(Error::DuplicateMember { .. })));

    println!("{}", String::from_utf8_lossy(&canonical));
    Ok(())
}

cargo run prints {"a":[1,1e+30],"b":1}.

What it refuses

It refuses a repeated object member, compared on the decoded name; nesting past a depth cap, 128 by default, the value the in-toto attestation specification sets; a string whose bytes aren't Unicode scalar values, such as an unpaired surrogate or an overlong form; a number with no finite double, and under the opt-in I-JSON profile an integer at or above 2^53; and an input past a size cap. Each refusal is a named error variant carrying a byte offset, so a verifier can act on the fault. Number formatting is delegated to serde_json_canonicalizer.

Status

Version 0.1.1 on crates.io, minimum Rust 1.74. The tests load vectors produced by the Go implementation in agent-evidence-vectors, never by this crate, and every refusal is mutation-tested.

Documentation

page read it for
Design and evidence why a serializer can't make these checks, what the delegate does with a hostile document, and the test evidence
API reference every function, option and error variant, on docs.rs
Comparison with other RFC 8785 crates how seven RFC 8785 crates on crates.io handle hostile input
Mutation sweep how each refusal was shown to be tested
Contributing and changelog how to propose a change, and what each release changed

License

MIT OR Apache-2.0, at your option.