1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
//! # obsigil
//!
//! A mandate-token format and shared-secret **JWT alternative**: a
//! token split into a public **manifest** and an encrypted
//! **mandate**. Each half is an
//! authenticated, deterministically-encrypted ciphertext — AES-SIV
//! (RFC 5297, code `0`) or AES-GCM-SIV (RFC 8452, code `1`) — joined by a
//! separator that names the text encoding (`.` b64, `~` hex), with a
//! per-half algorithm code in the clear:
//!
//! ```text
//! token = [ manifest ALG ] SEP [ ALG mandate ]
//! ```
//!
//! This crate is the **backend** side: an [`Issuer`] mints mandates under
//! a secret [`MandateKey`], and [`Verifier::verify`] checks them against the
//! reserved fields (spec §11). The manifest is keyless and advisory; open
//! it with [`open_manifest`]. Verification is symmetric — the same
//! [`MandateKey`] both mints and verifies — so obsigil fits shared-secret
//! (HS256-style) JWT and JWE use cases, not public-key verification.
//!
//! Built directly on RustCrypto (`aes-siv`, `aes-gcm-siv`, `hkdf`). Only
//! authenticated AEADs are ever compiled in, so an unauthenticated mandate
//! is structurally unrepresentable (spec §9.2).
//!
//! The normative format is the obsigil specification; section references in
//! this source (e.g. `spec §5.2`) point there.
//!
//! # Example
//!
//! ```rust
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! use obsigil::{open_manifest, Issuer, Mandate, MandateKey, Manifest, Verifier};
//! use serde::{Deserialize, Serialize};
//!
//! #[derive(Serialize, Deserialize)]
//! struct Access { role: String }
//! #[derive(Serialize, Deserialize)]
//! struct Ui { theme: String }
//!
//! // One 64-byte secret, provisioned to both sides (use
//! // `MandateKey::generate()` in production).
//! let issuer_key = MandateKey::from_bytes([42u8; 64])?;
//!
//! // Issuer: an authoritative mandate plus an advisory public manifest.
//! let token = Issuer::new(issuer_key)
//! .mandate(&Access { role: "admin".into() })
//! .exp(4_000_000_000)
//! .audience(["api"])
//! .manifest("auth.example", &Ui { theme: "dark".into() })
//! .mint()?;
//!
//! // Front end: read the manifest with no secret (advisory only).
//! let manifest: Manifest<Ui> = open_manifest(&token).expect("present");
//! assert_eq!(manifest.issuer(), "auth.example");
//!
//! // Backend: verify the mandate (authoritative). `now` is pinned here
//! // for a deterministic example; omit it to read the system clock.
//! let verify_key = MandateKey::from_bytes([42u8; 64])?;
//! let mandate: Mandate<Access> = Verifier::new()
//! .key(&verify_key)
//! .audience("api")
//! .now(1_000_000_000)
//! .verify(&token)?;
//! assert_eq!(mandate.app().role, "admin");
//! # Ok(()) }
//! ```
// At least one serialization format must be compiled in; without one the
// mint/verify API cannot encode or decode claims (spec §7).
compile_error!;
pub use ;
pub use MandateKey;
pub use ;
pub use ;
pub use ;
pub use ;
// Re-exported for callers handling `tid` (spec §11.3).
pub use Uuid;