1use crate::{
2 dto::{prelude::*, rpc::RootRequestMetadata},
3 ids::BuildNetwork,
4};
5
6#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
11pub enum DelegationAudience {
12 Canister(Principal),
13 CanicSubnet(Principal),
14 Project(String),
15}
16
17#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
22pub struct DelegatedRoleGrant {
23 pub target: CanisterRole,
24 pub scopes: Vec<String>,
25}
26
27#[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#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
46pub enum RootProofMode {
47 IcCanisterSignature,
48 ChainKeyBatch,
49}
50
51#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
56pub enum IssuerProof {
57 IcCanisterSignatureV1(IcCanisterSignatureProofV1),
58}
59
60#[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#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
75pub enum ChainKeyAlgorithm {
76 EcdsaSecp256k1,
77}
78
79#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
84pub struct ChainKeyKeyId {
85 pub name: String,
86}
87
88#[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#[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#[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#[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#[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#[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#[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#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
214pub struct ChainKeyBatchWitnessV1 {
215 pub steps: Vec<ChainKeyBatchWitnessStepV1>,
216}
217
218#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
223pub enum ChainKeyBatchWitnessStepV1 {
224 LeftSibling([u8; 32]),
225 RightSibling([u8; 32]),
226}
227
228#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
233pub enum IssuerProofAlgorithm {
234 IcCanisterSignatureV1,
235}
236
237#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
242pub enum IssuerProofBinding {
243 IcCanisterSignatureV1 { seed_hash: [u8; 32] },
244}
245
246#[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#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
270pub struct DelegationProof {
271 pub cert: DelegationCert,
272 pub root_proof: RootProof,
273}
274
275#[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#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
295pub struct InstallActiveDelegationProofRequest {
296 pub proof: DelegationProof,
297}
298
299#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
304pub struct InstallActiveDelegationProofResponse {
305 pub active_proof: ActiveDelegationProof,
306}
307
308#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
313pub enum ActiveDelegationProofStatus {
314 Missing,
315 Valid,
316 RefreshNeeded,
317 Expired,
318}
319
320#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
451pub struct RootIssuerPolicyResponse {
452 pub issuer: RootIssuerPolicyView,
453}
454
455#[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#[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#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
486pub struct RootIssuerRenewalTemplateResponse {
487 pub template: RootIssuerRenewalTemplateView,
488}
489
490#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
495pub struct RootIssuerRenewalStatusRequest {
496 pub issuer_pid: Principal,
497}
498
499#[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#[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#[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#[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#[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#[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#[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#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
617pub struct DelegatedTokenGetRequest {
618 pub claims_hash: [u8; 32],
619}
620
621#[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#[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#[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#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
670pub struct RoleAttestationGetRequest {
671 pub payload_hash: [u8; 32],
672}
673
674#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
679pub struct SignedRoleAttestation {
680 pub payload: RoleAttestation,
681 pub root_proof: RootProof,
682}
683
684#[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}