Skip to main content

canic_core/dto/
auth.rs

1//! Delegated signing DTOs.
2//!
3//! These types define the **data model and trust boundaries** for delegated
4//! authorization using **IC canister signatures only**.
5//!
6//! High-level design:
7//! - A single root authority canister is the trust anchor.
8//! - The root delegates signing authority to signer canisters via certificates.
9//! - Signer canisters issue authorization tokens.
10//! - Tokens are verified **locally** with no directory, registry, or topology calls.
11//!
12//! This model enables:
13//! - Offline / deterministic verification
14//! - No runtime dependency on external canisters
15//! - Clear, auditable trust chains
16//!
17//! Any change to these structures is **security-sensitive** and must be
18//! evaluated against the trust model below.
19
20use crate::dto::{error::Error, prelude::*};
21
22/// ---------------------------------------------------------------------------
23/// Trust model summary
24/// ---------------------------------------------------------------------------
25///
26/// - The **root authority canister** is the only long-term trust anchor.
27/// - The root signs `DelegationCert` objects using IC canister signatures.
28/// - A `DelegationCert` grants limited signing authority to a signer canister.
29/// - Signer canisters sign `DelegatedToken` objects.
30/// - Verifiers trust a token **only if**:
31///     - The delegation certificate is root-signed
32///     - The token signature matches the delegated signer
33///     - All temporal, scope, and audience constraints hold
34///
35/// No canister calls occur during verification.
36/// All trust is established cryptographically.
37///
38/// ---------------------------------------------------------------------------
39
40///
41/// DelegationCert
42///
43/// A root-signed certificate that delegates token-signing authority
44/// to a signer canister.
45///
46/// WHY THIS EXISTS
47/// ----------------
48/// This is the *only* object signed by the root authority canister.
49/// It is the **trust anchor** for all delegated tokens.
50///
51/// If this object is valid and trusted:
52/// - The signer canister is authorized to issue tokens
53/// - But *only* within the audiences, scopes, and lifetime specified here
54///
55/// Anything not explicitly allowed here is forbidden.
56///
57#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
58pub struct DelegationCert {
59    /// Version of the delegation certificate format.
60    ///
61    /// WHY:
62    /// - Allows forward-compatible evolution of fields and semantics
63    /// - Prevents silent misinterpretation during upgrades
64    pub v: u16,
65
66    /// Principal of the delegated signer canister.
67    ///
68    /// Tokens must be signed by this canister.
69    /// No other signer is valid under this certificate.
70    pub signer_pid: Principal,
71
72    /// Audiences the delegated signer is allowed to issue tokens for.
73    ///
74    /// Token `claims.aud` MUST be a member of this set.
75    /// This prevents a signer from issuing tokens for unrelated services.
76    pub audiences: Vec<String>,
77
78    /// Scopes the delegated signer is allowed to assert.
79    ///
80    /// Token `claims.scopes` MUST be a subset of this set.
81    /// This ensures least-privilege delegation.
82    pub scopes: Vec<String>,
83
84    /// Time (seconds since epoch) when this delegation was issued.
85    ///
86    /// Used for auditing and temporal validation.
87    pub issued_at: u64,
88
89    /// Absolute expiration time of this delegation.
90    ///
91    /// Tokens MUST NOT outlive this value, regardless of token TTL.
92    /// Rotation invalidates all tokens bound to an expired certificate.
93    pub expires_at: u64,
94}
95
96///
97/// DelegationProof
98///
99/// Cryptographic proof that a `DelegationCert` was authorized by the root.
100///
101/// WHY THIS EXISTS
102/// ----------------
103/// The `DelegationCert` alone is just data.
104/// This struct binds it to a **root canister signature**, establishing
105/// a verifiable trust chain:
106///
107/// root authority -> signer canister
108///
109/// Verifiers validate `cert_sig` using:
110/// - the root authority’s public key
111/// - a fixed domain separator
112///
113#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
114pub struct DelegationProof {
115    /// The delegated certificate describing signer authority.
116    pub cert: DelegationCert,
117
118    /// Root authority signature over the DelegationCert hash.
119    ///
120    /// This signature:
121    /// - Authenticates the certificate
122    /// - Prevents forgery or tampering
123    /// - Anchors the delegation chain
124    pub cert_sig: Vec<u8>,
125}
126
127///
128/// DelegatedTokenClaims
129///
130/// Claims asserted by a delegated signer.
131///
132/// IMPORTANT:
133/// -----------
134/// All fields in this struct are **untrusted input** until:
135/// - the delegation proof is verified
136/// - the token signature is verified
137///
138/// Authorization derives from *both* the claims AND the delegation.
139/// Claims alone are never sufficient.
140///
141#[derive(CandidType, Clone, Debug, Deserialize, Serialize)]
142pub struct DelegatedTokenClaims {
143    /// Subject of the token (e.g. user principal).
144    pub sub: Principal,
145
146    /// Intended audience of the token.
147    ///
148    /// MUST match one of `DelegationCert.audiences`.
149    /// This prevents cross-service token reuse.
150    pub aud: String,
151
152    /// Scopes asserted by this token.
153    ///
154    /// MUST be a subset of `DelegationCert.scopes`.
155    /// The signer cannot exceed delegated authority.
156    pub scopes: Vec<String>,
157
158    /// Issued-at timestamp.
159    ///
160    /// Used to enforce token freshness and TTL constraints.
161    pub iat: u64,
162
163    /// Expiration timestamp.
164    ///
165    /// MUST be:
166    /// - >= iat
167    /// - <= DelegationCert.expires_at
168    pub exp: u64,
169
170    /// Optional application-defined extension payload.
171    ///
172    /// SECURITY:
173    /// ---------
174    /// - This field is covered by the token signature.
175    /// - Canic does NOT interpret this field.
176    /// - Semantics are defined entirely by the application.
177    ///
178    /// INTENDED USE:
179    /// -------------
180    /// - Application identity (e.g. user_id)
181    /// - Role snapshots
182    /// - Authorization context
183    ///
184    /// This enables stateless application authentication across
185    /// arbitrary canisters without database lookups.
186    #[serde(default)]
187    pub ext: Option<Vec<u8>>,
188
189    /// Optional nonce for replay protection or correlation.
190    ///
191    /// Semantics are intentionally undefined at this layer.
192    /// Consumers may use it for replay detection or tracing.
193    pub nonce: Option<Vec<u8>>,
194}
195
196///
197/// DelegatedToken
198///
199/// A signed authorization token issued by a delegated signer.
200///
201/// Verification steps (normative):
202/// 1. Validate token version.
203/// 2. Verify the root signature on the delegation certificate.
204/// 3. Validate time bounds and scope/audience constraints.
205/// 4. Verify the token signature using the signer canister.
206///
207/// Design constraints:
208/// - No directory or registry lookup is required.
209/// - No topology or environment inspection is required.
210/// - All verification is local and deterministic.
211///
212#[derive(CandidType, Clone, Debug, Deserialize, Serialize)]
213pub struct DelegatedToken {
214    /// Version of the delegated token format.
215    ///
216    /// Allows format evolution without ambiguity.
217    pub v: u16,
218
219    /// Claims asserted by the signer.
220    pub claims: DelegatedTokenClaims,
221
222    /// Delegation proof binding signer authority to root trust.
223    pub proof: DelegationProof,
224
225    /// Signature over canonicalized claims and delegation hash.
226    ///
227    /// Produced by the delegated signer canister.
228    pub token_sig: Vec<u8>,
229}
230
231///
232/// DelegationRequest
233///
234
235#[derive(CandidType, Clone, Debug, Deserialize, Serialize)]
236pub struct DelegationRequest {
237    pub signer_pid: Principal,
238    pub audiences: Vec<String>,
239    pub scopes: Vec<String>,
240    pub ttl_secs: u64,
241    pub verifier_targets: Vec<Principal>,
242    pub include_root_verifier: bool,
243}
244
245// admin-only: not part of canonical delegation flow.
246// used for tests / tooling due to PocketIC limitations.
247///
248/// DelegationProvisionRequest
249///
250
251#[derive(CandidType, Clone, Debug, Deserialize, Serialize)]
252pub struct DelegationProvisionRequest {
253    pub cert: DelegationCert,
254    pub signer_targets: Vec<Principal>,
255    pub verifier_targets: Vec<Principal>,
256}
257
258// admin-only: not part of canonical delegation flow.
259// used for tests / tooling due to PocketIC limitations.
260///
261/// DelegationProvisionResponse
262///
263
264#[derive(CandidType, Clone, Debug, Deserialize, Serialize)]
265pub struct DelegationProvisionResponse {
266    pub proof: DelegationProof,
267    pub results: Vec<DelegationProvisionTargetResponse>,
268}
269
270///
271/// DelegationProofStatus
272///
273
274#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
275pub struct DelegationProofStatus {
276    pub signer_pid: Principal,
277    pub issued_at: u64,
278    pub expires_at: u64,
279}
280
281#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
282pub enum DelegationProvisionTargetKind {
283    Signer,
284    Verifier,
285}
286
287#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
288pub enum DelegationProvisionStatus {
289    Ok,
290    Failed,
291}
292
293///
294/// DelegationProvisionTargetResponse
295///
296
297#[derive(CandidType, Clone, Debug, Deserialize, Serialize)]
298pub struct DelegationProvisionTargetResponse {
299    pub target: Principal,
300    pub kind: DelegationProvisionTargetKind,
301    pub status: DelegationProvisionStatus,
302    pub error: Option<Error>,
303}