Skip to main content

canic_core/dto/
auth.rs

1use crate::dto::{prelude::*, rpc::RootRequestMetadata};
2
3//
4// SignatureAlgorithm
5//
6
7#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
8pub enum SignatureAlgorithm {
9    EcdsaP256Sha256,
10}
11
12//
13// DelegationAudience
14//
15
16#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
17pub enum DelegationAudience {
18    Roles(Vec<CanisterRole>),
19    Principals(Vec<Principal>),
20    RolesOrPrincipals {
21        roles: Vec<CanisterRole>,
22        principals: Vec<Principal>,
23    },
24}
25
26//
27// RootPublicKey
28//
29
30#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
31pub struct RootPublicKey {
32    pub root_pid: Principal,
33    pub key_id: String,
34    pub alg: SignatureAlgorithm,
35    pub public_key_sec1: Vec<u8>,
36    pub key_hash: [u8; 32],
37    pub not_before: u64,
38    pub not_after: Option<u64>,
39}
40
41//
42// RootTrustAnchor
43//
44
45#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
46pub struct RootTrustAnchor {
47    pub root_pid: Principal,
48    pub root_key: RootPublicKey,
49}
50
51//
52// ShardKeyBinding
53//
54
55#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
56pub enum ShardKeyBinding {
57    IcThresholdEcdsa {
58        key_name_hash: [u8; 32],
59        derivation_path_hash: [u8; 32],
60    },
61}
62
63//
64// DelegationCert
65//
66
67#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
68pub struct DelegationCert {
69    pub version: u16,
70    pub root_pid: Principal,
71    pub root_key_id: String,
72    pub root_key_hash: [u8; 32],
73    pub alg: SignatureAlgorithm,
74    pub shard_pid: Principal,
75    pub shard_key_id: String,
76    pub shard_public_key_sec1: Vec<u8>,
77    pub shard_key_hash: [u8; 32],
78    pub shard_key_binding: ShardKeyBinding,
79    pub issued_at: u64,
80    pub expires_at: u64,
81    pub max_token_ttl_secs: u64,
82    pub scopes: Vec<String>,
83    pub aud: DelegationAudience,
84    pub verifier_role_hash: Option<[u8; 32]>,
85}
86
87//
88// DelegationProof
89//
90
91#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
92pub struct DelegationProof {
93    pub cert: DelegationCert,
94    pub root_sig: Vec<u8>,
95}
96
97//
98// DelegatedTokenClaims
99//
100
101#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
102pub struct DelegatedTokenClaims {
103    pub version: u16,
104    pub subject: Principal,
105    pub issuer_shard_pid: Principal,
106    pub cert_hash: [u8; 32],
107    pub issued_at: u64,
108    pub expires_at: u64,
109    pub aud: DelegationAudience,
110    pub scopes: Vec<String>,
111    pub nonce: [u8; 16],
112}
113
114//
115// DelegatedToken
116//
117
118#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
119pub struct DelegatedToken {
120    pub claims: DelegatedTokenClaims,
121    pub proof: DelegationProof,
122    pub shard_sig: Vec<u8>,
123}
124
125//
126// DelegationProofIssueRequest
127//
128
129#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
130pub struct DelegationProofIssueRequest {
131    pub shard_pid: Principal,
132    pub scopes: Vec<String>,
133    pub aud: DelegationAudience,
134    pub cert_ttl_secs: u64,
135}
136
137//
138// DelegatedTokenIssueRequest
139//
140
141#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
142pub struct DelegatedTokenIssueRequest {
143    pub proof: DelegationProof,
144    pub subject: Principal,
145    pub aud: DelegationAudience,
146    pub scopes: Vec<String>,
147    pub ttl_secs: u64,
148    pub nonce: [u8; 16],
149}
150
151//
152// DelegatedTokenMintRequest
153//
154
155#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
156pub struct DelegatedTokenMintRequest {
157    pub subject: Principal,
158    pub aud: DelegationAudience,
159    pub scopes: Vec<String>,
160    pub token_ttl_secs: u64,
161    pub cert_ttl_secs: u64,
162    pub nonce: [u8; 16],
163}
164
165//
166// RoleAttestationRequest
167//
168
169#[derive(CandidType, Clone, Debug, Deserialize)]
170pub struct RoleAttestationRequest {
171    pub subject: Principal,
172    pub role: CanisterRole,
173    #[serde(default)]
174    pub subnet_id: Option<Principal>,
175    pub audience: Principal,
176    pub ttl_secs: u64,
177    pub epoch: u64,
178    #[serde(default)]
179    pub metadata: Option<RootRequestMetadata>,
180}
181
182//
183// RoleAttestation
184//
185
186#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
187pub struct RoleAttestation {
188    pub subject: Principal,
189    pub role: CanisterRole,
190    #[serde(default)]
191    pub subnet_id: Option<Principal>,
192    pub audience: Principal,
193    pub issued_at: u64,
194    pub expires_at: u64,
195    pub epoch: u64,
196}
197
198//
199// SignedRoleAttestation
200//
201
202#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
203pub struct SignedRoleAttestation {
204    pub payload: RoleAttestation,
205    pub signature: Vec<u8>,
206    pub key_id: u32,
207}
208
209//
210// AttestationKeyStatus
211//
212
213#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq)]
214pub enum AttestationKeyStatus {
215    Current,
216    Previous,
217}
218
219//
220// AttestationKey
221//
222
223#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq)]
224pub struct AttestationKey {
225    pub key_id: u32,
226    pub public_key: Vec<u8>,
227    pub key_name: String,
228    pub key_hash: [u8; 32],
229    pub status: AttestationKeyStatus,
230    #[serde(default)]
231    pub valid_from: Option<u64>,
232    #[serde(default)]
233    pub valid_until: Option<u64>,
234}
235
236//
237// AttestationKeySet
238//
239
240#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq)]
241pub struct AttestationKeySet {
242    pub root_pid: Principal,
243    pub generated_at: u64,
244    pub keys: Vec<AttestationKey>,
245}
246
247#[cfg(test)]
248mod tests {
249    #[test]
250    fn auth_dtos_remain_passive_boundary_types() {
251        let source = include_str!("auth.rs");
252        let production_source = source
253            .split("#[cfg(test)]")
254            .next()
255            .expect("production source exists");
256
257        for marker in [
258            "impl DelegatedToken",
259            "impl DelegatedTokenClaims",
260            "impl RoleAttestation",
261            "impl SignedRoleAttestation",
262            "fn verify",
263            "fn sign",
264            "fn resolve",
265            "fn replay",
266            "fn consume",
267            "fn policy",
268            "fn validate",
269        ] {
270            assert!(
271                !production_source.contains(marker),
272                "auth DTOs must stay passive; found marker `{marker}`"
273            );
274        }
275    }
276}