canaad-core
Parse, validate, and canonicalize AAD contexts per RFC 8785. Produces deterministic bytes for use as AEAD additional authenticated data — same input, same bytes, across Rust, WASM, and any conforming implementation.
Use it
Default profile
Parse existing JSON:
use parse_default;
let json = r#"{"v":1,"tenant":"org_abc","resource":"secrets/db","purpose":"encryption"}"#;
let ctx = parse_default?;
let canonical = ctx.canonicalize_string?;
Build from scratch:
use AadContext;
let ctx = new?
.with_timestamp?
.with_string_extension?;
let bytes = ctx.canonicalize?;
Use the builder if you prefer:
use AadContext;
let ctx = builder
.tenant
.resource
.purpose
.extension_string
.build?;
Builder defers all validation to build() — invalid extensions surface as errors, not silent drops.
Generic object layer
Canonicalize any valid JSON object without profile validation — no required fields, no version check:
use canonicalize_object;
let bytes = canonicalize_object?;
// → {"a":"first","z":"last"} (keys sorted per JCS)
Use this layer to build custom profiles on top of canaad-core.
What it checks
vmust be 1tenant: 1–256 bytes, no NULresource: 1–1024 bytes, no NULpurpose: 1+ bytes, no NULts: optional, 0 to 2^53−1- extensions:
x_<app>_<field>pattern, values are strings or integers - no duplicate keys (custom single-pass scanner, not serde_json)
- 16 KiB max serialized size
All 17 error variants are strongly typed via AadError. See docs.rs/canaad-core for the full API.
Spec
gnu.foo/specs/aad-spec — field constraints, extension patterns, test vectors.