Skip to main content

canic_core/dto/
auth.rs

1use crate::{
2    dto::{prelude::*, rpc::RootRequestMetadata},
3    ids::BuildNetwork,
4};
5
6//
7// DelegationAudience
8//
9
10#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
11pub enum DelegationAudience {
12    Canister(Principal),
13    CanicSubnet(Principal),
14    Project(String),
15}
16
17//
18// DelegatedRoleGrant
19//
20
21#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
22pub struct DelegatedRoleGrant {
23    pub target: CanisterRole,
24    pub scopes: Vec<String>,
25}
26
27//
28// RootProof
29//
30
31#[expect(
32    clippy::large_enum_variant,
33    reason = "RootProof is a Candid boundary DTO; boxing the chain-key variant would change the public Rust contract"
34)]
35#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
36pub enum RootProof {
37    IcCanisterSignatureV1(IcCanisterSignatureProofV1),
38    IcChainKeyBatchSignatureV1(IcChainKeyBatchSignatureProofV1),
39}
40
41//
42// RootProofMode
43//
44
45#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
46pub enum RootProofMode {
47    IcCanisterSignature,
48    ChainKeyBatch,
49}
50
51//
52// IssuerProof
53//
54
55#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
56pub enum IssuerProof {
57    IcCanisterSignatureV1(IcCanisterSignatureProofV1),
58}
59
60//
61// IcCanisterSignatureProofV1
62//
63
64#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
65pub struct IcCanisterSignatureProofV1 {
66    pub signature_cbor: Vec<u8>,
67    pub public_key_der: Vec<u8>,
68}
69
70//
71// ChainKeyAlgorithm
72//
73
74#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
75pub enum ChainKeyAlgorithm {
76    EcdsaSecp256k1,
77}
78
79//
80// ChainKeyKeyId
81//
82
83#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
84pub struct ChainKeyKeyId {
85    pub name: String,
86}
87
88//
89// RootKeyPolicyV1
90//
91
92#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
93pub struct RootKeyPolicyV1 {
94    pub root_canister_id: Principal,
95    pub proof_mode: RootProofMode,
96    pub algorithm: ChainKeyAlgorithm,
97    pub key_id: ChainKeyKeyId,
98    pub derivation_path_hash: [u8; 32],
99    pub public_key: Vec<u8>,
100    pub key_version: u64,
101    pub min_accepted_key_version: u64,
102    pub min_accepted_proof_epoch: u64,
103    pub min_accepted_registry_epoch: u64,
104    pub max_revocation_latency_ns: u64,
105    pub valid_from_ns: u64,
106    pub accept_until_ns: u64,
107    pub build_network: BuildNetwork,
108}
109
110//
111// DelegatedAuthRegistrySnapshotV1
112//
113
114#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
115pub struct DelegatedAuthRegistrySnapshotV1 {
116    pub schema_version: u16,
117    pub root_canister_id: Principal,
118    pub registry_epoch: u64,
119    pub proof_mode: RootProofMode,
120    pub root_key_policy_hash: [u8; 32],
121    pub issuer_policies: Vec<DelegatedAuthIssuerPolicySnapshotV1>,
122}
123
124//
125// DelegatedAuthIssuerPolicySnapshotV1
126//
127
128#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
129pub struct DelegatedAuthIssuerPolicySnapshotV1 {
130    pub issuer_canister_id: Principal,
131    pub enabled: bool,
132    pub preferred_proof_mode: RootProofMode,
133    pub allowed_audiences: Vec<DelegationAudience>,
134    pub allowed_grants: Vec<DelegatedRoleGrant>,
135    pub max_root_proof_ttl_ns: u64,
136    pub max_token_ttl_ns: u64,
137    pub issuer_proof_algorithm: IssuerProofAlgorithm,
138    pub issuer_proof_binding_hash: [u8; 32],
139    pub renewal_template_hash: [u8; 32],
140}
141
142//
143// IcChainKeyBatchSignatureProofV1
144//
145
146#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
147pub struct IcChainKeyBatchSignatureProofV1 {
148    pub header: ChainKeyBatchHeaderV1,
149    pub delegation_cert: ChainKeyDelegationCertV1,
150    pub issuer_witness: ChainKeyBatchWitnessV1,
151    pub signature: ChainKeyRootSignatureV1,
152}
153
154//
155// ChainKeyBatchHeaderV1
156//
157
158#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
159pub struct ChainKeyBatchHeaderV1 {
160    pub schema_version: u16,
161    pub root_canister_id: Principal,
162    pub batch_id: [u8; 32],
163    pub proof_epoch: u64,
164    pub registry_epoch: u64,
165    pub registry_hash: [u8; 32],
166    pub tree_root: [u8; 32],
167    pub not_before_ns: u64,
168    pub expires_at_ns: u64,
169    pub algorithm: ChainKeyAlgorithm,
170    pub key_id: ChainKeyKeyId,
171    pub derivation_path_hash: [u8; 32],
172    pub key_version: u64,
173}
174
175//
176// ChainKeyDelegationCertV1
177//
178
179#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
180pub struct ChainKeyDelegationCertV1 {
181    pub root_canister_id: Principal,
182    pub issuer_canister_id: Principal,
183    pub proof_epoch: u64,
184    pub issuer_proof_algorithm: IssuerProofAlgorithm,
185    pub issuer_proof_binding_hash: [u8; 32],
186    pub issuer_proof_binding: IssuerProofBinding,
187    pub max_token_ttl_ns: u64,
188    pub audience: DelegationAudience,
189    pub grants: Vec<DelegatedRoleGrant>,
190    pub not_before_ns: u64,
191    pub expires_at_ns: u64,
192    pub registry_epoch: u64,
193    pub registry_hash: [u8; 32],
194}
195
196//
197// ChainKeyRootSignatureV1
198//
199
200#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
201pub struct ChainKeyRootSignatureV1 {
202    pub algorithm: ChainKeyAlgorithm,
203    pub key_id: ChainKeyKeyId,
204    pub derivation_path: Vec<Vec<u8>>,
205    pub public_key: Vec<u8>,
206    pub signature: Vec<u8>,
207}
208
209//
210// ChainKeyBatchWitnessV1
211//
212
213#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
214pub struct ChainKeyBatchWitnessV1 {
215    pub steps: Vec<ChainKeyBatchWitnessStepV1>,
216}
217
218//
219// ChainKeyBatchWitnessStepV1
220//
221
222#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
223pub enum ChainKeyBatchWitnessStepV1 {
224    LeftSibling([u8; 32]),
225    RightSibling([u8; 32]),
226}
227
228//
229// IssuerProofAlgorithm
230//
231
232#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
233pub enum IssuerProofAlgorithm {
234    IcCanisterSignatureV1,
235}
236
237//
238// IssuerProofBinding
239//
240
241#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
242pub enum IssuerProofBinding {
243    IcCanisterSignatureV1 { seed_hash: [u8; 32] },
244}
245
246//
247// DelegationCert
248//
249
250#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
251pub struct DelegationCert {
252    pub root_pid: Principal,
253    pub issuer_pid: Principal,
254    pub issuer_proof_alg: IssuerProofAlgorithm,
255    pub issuer_proof_binding_hash: [u8; 32],
256    pub issuer_proof_binding: IssuerProofBinding,
257    pub issued_at_ns: u64,
258    pub not_before_ns: u64,
259    pub expires_at_ns: u64,
260    pub max_token_ttl_ns: u64,
261    pub aud: DelegationAudience,
262    pub grants: Vec<DelegatedRoleGrant>,
263}
264
265//
266// DelegationProof
267//
268
269#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
270pub struct DelegationProof {
271    pub cert: DelegationCert,
272    pub root_proof: RootProof,
273}
274
275//
276// ActiveDelegationProof
277//
278
279#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
280pub struct ActiveDelegationProof {
281    pub proof: DelegationProof,
282    pub cert_hash: [u8; 32],
283    pub not_before_ns: u64,
284    pub expires_at_ns: u64,
285    pub refresh_after_ns: u64,
286    pub installed_at_ns: u64,
287    pub installed_by: Principal,
288}
289
290//
291// InstallActiveDelegationProofRequest
292//
293
294#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
295pub struct InstallActiveDelegationProofRequest {
296    pub proof: DelegationProof,
297}
298
299//
300// InstallActiveDelegationProofResponse
301//
302
303#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
304pub struct InstallActiveDelegationProofResponse {
305    pub active_proof: ActiveDelegationProof,
306}
307
308//
309// ActiveDelegationProofStatus
310//
311
312#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
313pub enum ActiveDelegationProofStatus {
314    Missing,
315    Valid,
316    RefreshNeeded,
317    Expired,
318}
319
320//
321// ActiveDelegationProofStatusResponse
322//
323
324#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
325pub struct ActiveDelegationProofStatusResponse {
326    pub status: ActiveDelegationProofStatus,
327    pub root_pid: Option<Principal>,
328    pub issuer_pid: Option<Principal>,
329    pub cert_hash: Option<[u8; 32]>,
330    pub expires_at_ns: Option<u64>,
331    pub refresh_after_ns: Option<u64>,
332}
333
334//
335// DelegatedTokenClaims
336//
337
338#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
339pub struct DelegatedTokenClaims {
340    pub subject: Principal,
341    pub issuer_pid: Principal,
342    pub cert_hash: [u8; 32],
343    pub issued_at_ns: u64,
344    pub expires_at_ns: u64,
345    pub aud: DelegationAudience,
346    pub grants: Vec<DelegatedRoleGrant>,
347    pub nonce: [u8; 16],
348    #[serde(default)]
349    pub ext: Option<Vec<u8>>,
350}
351
352//
353// DelegatedToken
354//
355
356#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
357pub struct DelegatedToken {
358    pub claims: DelegatedTokenClaims,
359    pub proof: DelegationProof,
360    pub issuer_proof: IssuerProof,
361}
362
363//
364// AuthRequestMetadata
365//
366
367#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
368pub struct AuthRequestMetadata {
369    pub request_id: [u8; 32],
370    pub ttl_ns: u64,
371}
372
373//
374// RootDelegationProofBatchProofRef
375//
376
377#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
378pub struct RootDelegationProofBatchProofRef {
379    pub issuer_pid: Principal,
380    pub cert_hash: [u8; 32],
381}
382
383//
384// RootDelegationProofBatchProof
385//
386
387#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
388pub struct RootDelegationProofBatchProof {
389    pub issuer_pid: Principal,
390    pub cert_hash: [u8; 32],
391    pub proof: DelegationProof,
392}
393
394//
395// RootDelegationProofBatchInstallRequest
396//
397
398#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
399pub struct RootDelegationProofBatchInstallRequest {
400    pub batch_id: [u8; 32],
401    pub proofs: Vec<RootDelegationProofBatchProof>,
402}
403
404//
405// RootDelegationProofInstallOutcome
406//
407
408#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
409pub enum RootDelegationProofInstallOutcome {
410    Installed,
411    AlreadyInstalled,
412    RejectedBySigner,
413    CallFailed,
414    ProofMismatch,
415    ExpiredOrSuperseded,
416}
417
418//
419// RootIssuerPolicyUpsertRequest
420//
421
422#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
423pub struct RootIssuerPolicyUpsertRequest {
424    pub issuer_pid: Principal,
425    pub enabled: bool,
426    pub allowed_audiences: Vec<DelegationAudience>,
427    pub allowed_grants: Vec<DelegatedRoleGrant>,
428    pub max_cert_ttl_ns: u64,
429    pub refresh_after_ratio_bps: u16,
430}
431
432//
433// RootIssuerPolicyView
434//
435
436#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
437pub struct RootIssuerPolicyView {
438    pub issuer_pid: Principal,
439    pub enabled: bool,
440    pub allowed_audiences: Vec<DelegationAudience>,
441    pub allowed_grants: Vec<DelegatedRoleGrant>,
442    pub max_cert_ttl_ns: u64,
443    pub refresh_after_ratio_bps: u16,
444}
445
446//
447// RootIssuerPolicyResponse
448//
449
450#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
451pub struct RootIssuerPolicyResponse {
452    pub issuer: RootIssuerPolicyView,
453}
454
455//
456// RootIssuerRenewalTemplateUpsertRequest
457//
458
459#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
460pub struct RootIssuerRenewalTemplateUpsertRequest {
461    pub issuer_pid: Principal,
462    pub enabled: bool,
463    pub aud: DelegationAudience,
464    pub grants: Vec<DelegatedRoleGrant>,
465    pub cert_ttl_ns: u64,
466}
467
468//
469// RootIssuerRenewalTemplateView
470//
471
472#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
473pub struct RootIssuerRenewalTemplateView {
474    pub issuer_pid: Principal,
475    pub enabled: bool,
476    pub aud: DelegationAudience,
477    pub grants: Vec<DelegatedRoleGrant>,
478    pub cert_ttl_ns: u64,
479}
480
481//
482// RootIssuerRenewalTemplateResponse
483//
484
485#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
486pub struct RootIssuerRenewalTemplateResponse {
487    pub template: RootIssuerRenewalTemplateView,
488}
489
490//
491// RootIssuerRenewalStatusRequest
492//
493
494#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
495pub struct RootIssuerRenewalStatusRequest {
496    pub issuer_pid: Principal,
497}
498
499//
500// RootIssuerRenewalOutcome
501//
502
503#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
504pub enum RootIssuerRenewalOutcome {
505    AlreadyInstalled,
506    DriftDetected,
507    InstallDeadlineExpired,
508    Installed,
509    IssuerCallFailed,
510    NeverRun,
511    PolicyRejected,
512    ProofMismatch,
513    QuotaExceeded,
514    RejectedByIssuer,
515    RetrievalExpired,
516    TemplateChanged,
517    TemplateDisabled,
518}
519
520//
521// RootIssuerRenewalAttemptStatus
522//
523
524#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
525pub enum RootIssuerRenewalAttemptStatus {
526    Prepared,
527    Installing,
528    Installed,
529    FailedRetryable,
530    FailedTerminal,
531    Disabled,
532    Expired,
533}
534
535//
536// RootIssuerRenewalAttemptView
537//
538
539#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
540pub struct RootIssuerRenewalAttemptView {
541    pub attempt_id: [u8; 32],
542    pub issuer_pid: Principal,
543    pub template_fingerprint: [u8; 32],
544    pub batch_id: [u8; 32],
545    pub proof_ref: RootDelegationProofBatchProofRef,
546    pub status: RootIssuerRenewalAttemptStatus,
547    pub prepared_at_ns: u64,
548    pub retrieval_expires_at_ns: u64,
549    pub install_deadline_ns: u64,
550    pub prepared_cert_hash: [u8; 32],
551    pub prepared_expires_at_ns: u64,
552    pub prepared_refresh_after_ns: u64,
553    pub failure: Option<RootIssuerRenewalOutcome>,
554}
555
556//
557// RootIssuerRenewalStateView
558//
559
560#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
561pub struct RootIssuerRenewalStateView {
562    pub issuer_pid: Principal,
563    pub template_fingerprint: [u8; 32],
564    pub last_installed_cert_hash: Option<[u8; 32]>,
565    pub last_installed_expires_at_ns: Option<u64>,
566    pub last_installed_refresh_after_ns: Option<u64>,
567    pub active_attempt_id: Option<[u8; 32]>,
568    pub last_outcome: RootIssuerRenewalOutcome,
569    pub consecutive_failures: u32,
570    pub next_attempt_after_ns: u64,
571    pub updated_at_ns: u64,
572}
573
574//
575// RootIssuerRenewalStatusResponse
576//
577
578#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
579pub struct RootIssuerRenewalStatusResponse {
580    pub template: Option<RootIssuerRenewalTemplateView>,
581    pub state: Option<RootIssuerRenewalStateView>,
582    pub active_attempt: Option<RootIssuerRenewalAttemptView>,
583}
584
585//
586// DelegatedTokenPrepareRequest
587//
588
589#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
590pub struct DelegatedTokenPrepareRequest {
591    #[serde(default)]
592    pub metadata: Option<AuthRequestMetadata>,
593    pub subject: Principal,
594    pub aud: DelegationAudience,
595    pub grants: Vec<DelegatedRoleGrant>,
596    pub ttl_ns: u64,
597    #[serde(default)]
598    pub ext: Option<Vec<u8>>,
599}
600
601//
602// DelegatedTokenPrepareResponse
603//
604
605#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
606pub struct DelegatedTokenPrepareResponse {
607    pub claims: DelegatedTokenClaims,
608    pub claims_hash: [u8; 32],
609    pub retrieval_expires_at_ns: u64,
610}
611
612//
613// DelegatedTokenGetRequest
614//
615
616#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
617pub struct DelegatedTokenGetRequest {
618    pub claims_hash: [u8; 32],
619}
620
621//
622// RoleAttestationRequest
623//
624
625#[derive(CandidType, Clone, Debug, Deserialize)]
626pub struct RoleAttestationRequest {
627    pub subject: Principal,
628    pub role: CanisterRole,
629    #[serde(default)]
630    pub subnet_id: Option<Principal>,
631    pub audience: Principal,
632    pub ttl_ns: u64,
633    pub epoch: u64,
634    #[serde(default)]
635    pub metadata: Option<RootRequestMetadata>,
636}
637
638//
639// RoleAttestation
640//
641
642#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
643pub struct RoleAttestation {
644    pub subject: Principal,
645    pub role: CanisterRole,
646    #[serde(default)]
647    pub subnet_id: Option<Principal>,
648    pub audience: Principal,
649    pub issued_at_ns: u64,
650    pub expires_at_ns: u64,
651    pub epoch: u64,
652}
653
654//
655// RoleAttestationPrepareResponse
656//
657
658#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
659pub struct RoleAttestationPrepareResponse {
660    pub payload: RoleAttestation,
661    pub payload_hash: [u8; 32],
662    pub retrieval_expires_at_ns: u64,
663}
664
665//
666// RoleAttestationGetRequest
667//
668
669#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
670pub struct RoleAttestationGetRequest {
671    pub payload_hash: [u8; 32],
672}
673
674//
675// SignedRoleAttestation
676//
677
678#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
679pub struct SignedRoleAttestation {
680    pub payload: RoleAttestation,
681    pub root_proof: RootProof,
682}
683
684// -----------------------------------------------------------------------------
685// Tests
686// -----------------------------------------------------------------------------
687
688#[cfg(test)]
689mod tests {
690    #[test]
691    fn auth_dtos_remain_passive_boundary_types() {
692        let source = include_str!("auth.rs");
693        let production_source = source
694            .split("#[cfg(test)]")
695            .next()
696            .expect("production source exists");
697
698        for marker in [
699            "impl DelegatedToken",
700            "impl DelegatedTokenClaims",
701            "impl RoleAttestation",
702            "impl SignedRoleAttestation",
703            "fn verify",
704            "fn sign",
705            "fn resolve",
706            "fn replay",
707            "fn consume",
708            "fn policy",
709            "fn validate",
710        ] {
711            assert!(
712                !production_source.contains(marker),
713                "auth DTOs must stay passive; found marker `{marker}`"
714            );
715        }
716    }
717}