canaad-core 0.2.2

Core library for AAD canonicalization per RFC 8785
Documentation
# canaad-core

Parse, validate, and canonicalize AAD contexts per RFC 8785.

## use it

Parse existing JSON:

```rust
use canaad_core::parse;

let json = r#"{"v":1,"tenant":"org_abc","resource":"secrets/db","purpose":"encryption"}"#;
let ctx = parse(json)?;
let canonical = ctx.canonicalize_string()?;
```

Build from scratch:

```rust
use canaad_core::AadContext;

let ctx = AadContext::new("org_abc", "secrets/db", "encryption")?
    .with_timestamp(1706400000)?
    .with_string_extension("x_vault_cluster", "us-east-1")?;

let bytes = ctx.canonicalize()?;
```

Use the builder if you prefer:

```rust
use canaad_core::AadContext;

let ctx = AadContext::builder()
    .tenant("org_abc")
    .resource("secrets/db")
    .purpose("encryption")
    .extension_string("x_vault_cluster", "us-east-1")
    .build()?;
```

Builder defers all validation to `build()` — invalid extensions surface as errors, not silent drops.

## what it checks

- `v` must be 1
- `tenant`: 1-256 bytes, no NUL
- `resource`: 1-1024 bytes, no NUL
- `purpose`: 1+ bytes, no NUL
- `ts`: optional, 0 to 2^53-1
- extensions: `x_<app>_<field>` pattern, values are strings or integers
- no duplicate keys (custom scanner, not serde_json)
- 16 KiB max serialized size

All 16 error variants are strongly typed via `AadError`. Run `cargo doc -p canaad-core --open` for the full API.

## spec

See [aad-spec.md](../../aad-spec.md) for the complete specification and test vectors.