canaad_core/lib.rs
1//! Core library for AAD canonicalization per RFC 8785 (JCS).
2//!
3//! Provides deterministic serialization and contextual binding for AEAD
4//! additional authenticated data. Mitigates confused deputy attacks,
5//! cross-tenant decryption, and purpose confusion by binding ciphertext
6//! to tenant, resource, and purpose fields with a canonical byte representation.
7//!
8//! See the [AAD specification](https://gnu.foo/specs/aad-spec/) for the full schema,
9//! constraint set, and test vectors. Architecture and integration guidance at
10//! [gnu.foo/projects/canaad](https://gnu.foo/projects/canaad).
11//!
12//! ## Architecture
13//!
14//! The library exposes two layers:
15//!
16//! - **Default-profile layer** (`parse_default`, `validate_default`,
17//! `canonicalize_default`, `canonicalize_default_string`): enforces the standard
18//! AAD field set — `v`, `tenant`, `resource`, `purpose`, optional `ts` and `x_*`
19//! extensions.
20//!
21//! - **Generic-object layer** (`validate_object`, `canonicalize_object`,
22//! `canonicalize_object_string`): applies core rules only (size, duplicate-key
23//! detection, object assertion, JCS canonicalization) without requiring any
24//! specific fields. Use this layer to build custom profiles on top of canaad.
25//!
26//! ## Quick Start
27//!
28//! ### Default profile
29//!
30//! ```rust
31//! use canaad_core::{parse_default, canonicalize_default, canonicalize_default_string, AadContext};
32//!
33//! // Parse and validate existing JSON against the default profile
34//! let json = r#"{"v":1,"tenant":"org_abc","resource":"secrets/db","purpose":"encryption"}"#;
35//! let ctx = parse_default(json)?;
36//! let canonical = ctx.canonicalize_string()?;
37//!
38//! // Or build from scratch
39//! let ctx = AadContext::new("org_abc", "secrets/db", "encryption")?
40//! .with_timestamp(1706400000)?
41//! .with_string_extension("x_vault_cluster", "us-east-1")?;
42//!
43//! let bytes = ctx.canonicalize()?;
44//! # Ok::<(), canaad_core::AadError>(())
45//! ```
46//!
47//! ### Generic object (custom profile)
48//!
49//! ```rust
50//! use canaad_core::canonicalize_object;
51//!
52//! // Canonicalize any valid JSON object without profile validation
53//! let json = r#"{"z":"last","a":"first"}"#;
54//! let bytes = canonicalize_object(json)?;
55//! # Ok::<(), canaad_core::AadError>(())
56//! ```
57//!
58//! ## Validation Rules (default profile)
59//!
60//! - Version (`v`): Must be 1
61//! - Tenant: 1-256 bytes, no NUL bytes
62//! - Resource: 1-1024 bytes, no NUL bytes
63//! - Purpose: 1+ bytes, no NUL bytes
64//! - Timestamp (`ts`): Optional, 0 to 2^53-1
65//! - Extension keys: Must match pattern `x_<app>_<field>` where app is `[a-z]+` and field is `[a-z_]+`
66//! - All integers: 0 to 2^53-1 (JavaScript safe integer range)
67//! - Total serialized size: Maximum 16 KiB
68//! - No duplicate keys allowed
69
70mod api;
71mod canon;
72mod context;
73mod error;
74mod parse;
75mod profile;
76mod types;
77
78#[cfg(test)]
79mod tests;
80
81pub use api::{
82 canonicalize_default, canonicalize_default_string, canonicalize_object,
83 canonicalize_object_string, parse_default, validate_default, validate_object,
84};
85pub use context::{AadContext, AadContextBuilder};
86pub use error::{AadError, JsonType};
87pub use parse::{CURRENT_VERSION, MAX_AAD_SIZE};
88pub use types::{
89 ExtensionValue, Extensions, FieldKey, Purpose, Resource, SafeInt, Tenant, MAX_SAFE_INTEGER,
90 RESERVED_KEYS,
91};