1use ic_auth_protocol_types as domain;
3use ic_auth_protocol_types::limits::{
4 MAX_CLAIMS_BYTES, MAX_GRANTS, MAX_LABEL_BYTES, MAX_SCOPES_PER_GRANT,
5};
6use ic_wire::{Bytes, Error, List, Text};
7
8trait Field<D>: Sized {
9 fn from_domain(value: D) -> Result<Self, Error>;
10 fn into_domain(self) -> Result<D, Error>;
11}
12macro_rules! scalar {
13 ($($ty:ty),*) => {$(impl Field<$ty> for $ty {
14 fn from_domain(value: $ty) -> Result<Self, Error> { Ok(value) }
15 fn into_domain(self) -> Result<$ty, Error> { Ok(self) }
16 })*};
17}
18scalar!(u16, u64, [u8; 16], [u8; 32], domain::Principal);
19impl<const N: usize> Field<Vec<u8>> for Bytes<N> {
20 fn from_domain(value: Vec<u8>) -> Result<Self, Error> {
21 Self::new(value)
22 }
23 fn into_domain(self) -> Result<Vec<u8>, Error> {
24 Ok(self.into_vec())
25 }
26}
27impl<const N: usize> Field<String> for Text<N> {
28 fn from_domain(value: String) -> Result<Self, Error> {
29 Self::new(value)
30 }
31 fn into_domain(self) -> Result<String, Error> {
32 Ok(self.into_string())
33 }
34}
35impl<D, W: Field<D>, const N: usize> Field<Vec<D>> for List<W, N> {
36 fn from_domain(value: Vec<D>) -> Result<Self, Error> {
37 if value.len() > N {
38 return Err(Error::Limit);
39 }
40 Self::new(
41 value
42 .into_iter()
43 .map(W::from_domain)
44 .collect::<Result<_, _>>()?,
45 )
46 }
47 fn into_domain(self) -> Result<Vec<D>, Error> {
48 self.into_vec().into_iter().map(W::into_domain).collect()
49 }
50}
51impl Field<domain::CanonicalId> for [u8; 32] {
52 fn from_domain(value: domain::CanonicalId) -> Result<Self, Error> {
53 Ok(*value.as_bytes())
54 }
55 fn into_domain(self) -> Result<domain::CanonicalId, Error> {
56 Ok(domain::CanonicalId::from_bytes(self))
57 }
58}
59impl Field<domain::AuthRole> for Text<MAX_LABEL_BYTES> {
60 fn from_domain(value: domain::AuthRole) -> Result<Self, Error> {
61 Self::new(value.into_string())
62 }
63 fn into_domain(self) -> Result<domain::AuthRole, Error> {
64 domain::AuthRole::try_from(self.into_string()).map_err(|_| Error::Malformed)
65 }
66}
67macro_rules! record {
70 ($name:ident { $($field:ident: $ty:ty),* $(,)? }) => {
71 #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
72#[cfg_attr(feature = "typescript", derive(ic_wire::Wire))]
73 pub struct $name { $(pub $field: $ty),* }
74 impl TryFrom<domain::$name> for $name {
75 type Error = Error;
76 fn try_from(value: domain::$name) -> Result<Self, Error> {
77 Ok(Self { $($field: Field::from_domain(value.$field)?),* })
78 }
79 }
80 impl TryFrom<$name> for domain::$name {
81 type Error = Error;
82 fn try_from(value: $name) -> Result<Self, Error> {
83 Ok(Self { $($field: Field::into_domain(value.$field)?),* })
84 }
85 }
86 impl Field<domain::$name> for $name {
87 fn from_domain(value: domain::$name) -> Result<Self, Error> { value.try_into() }
88 fn into_domain(self) -> Result<domain::$name, Error> { self.try_into() }
89 }
90 };
91}
92macro_rules! unit_enum {
93 ($name:ident { $($variant:ident),* $(,)? }) => {
94 #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
95#[cfg_attr(feature = "typescript", derive(ic_wire::Wire))]
96 pub enum $name { $($variant),* }
97 impl Field<domain::$name> for $name {
98 fn from_domain(value: domain::$name) -> Result<Self, Error> {
99 Ok(value.into())
100 }
101 fn into_domain(self) -> Result<domain::$name, Error> {
102 Ok(self.into())
103 }
104 }
105 impl From<domain::$name> for $name {
106 fn from(value: domain::$name) -> Self {
107 match value { $(domain::$name::$variant => Self::$variant),* }
108 }
109 }
110 impl From<$name> for domain::$name {
111 fn from(value: $name) -> Self {
112 match value { $($name::$variant => Self::$variant),* }
113 }
114 }
115 };
116}
117macro_rules! tuple_enum {
118 ($name:ident { $($variant:ident($ty:ty)),* $(,)? }) => {
119 #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
120#[cfg_attr(feature = "typescript", derive(ic_wire::Wire))]
121 pub enum $name { $($variant($ty)),* }
122 impl Field<domain::$name> for $name {
123 fn from_domain(value: domain::$name) -> Result<Self, Error> {
124 Ok(match value { $(domain::$name::$variant(v) => Self::$variant(Field::from_domain(v)?)),* })
125 }
126 fn into_domain(self) -> Result<domain::$name, Error> {
127 Ok(match self { $(Self::$variant(v) => domain::$name::$variant(Field::into_domain(v)?)),* })
128 }
129 }
130 };
131}
132record!(AudienceId {
133 canonical_network_id: [u8; 32],
134 fleet_id: [u8; 32]
135});
136record!(DelegatedRoleGrant { target: Text<MAX_LABEL_BYTES>, scopes: List<Text<MAX_LABEL_BYTES>, MAX_SCOPES_PER_GRANT> });
137tuple_enum!(DelegationAudience {
138 Fleet(AudienceId)
139});
140unit_enum!(ChainKeyAlgorithm { EcdsaSecp256k1 });
141record!(ChainKeyKeyId { name: Text<128> });
142unit_enum!(IssuerProofAlgorithm {
143 IcCanisterSignatureV1
144});
145#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
146#[cfg_attr(feature = "typescript", derive(ic_wire::Wire))]
147pub enum IssuerProofBinding {
148 IcCanisterSignatureV1([u8; 32]),
149}
150impl Field<domain::IssuerProofBinding> for IssuerProofBinding {
151 fn from_domain(value: domain::IssuerProofBinding) -> Result<Self, Error> {
152 let domain::IssuerProofBinding::IcCanisterSignatureV1 { seed_hash } = value;
153 Ok(Self::IcCanisterSignatureV1(seed_hash))
154 }
155 fn into_domain(self) -> Result<domain::IssuerProofBinding, Error> {
156 let Self::IcCanisterSignatureV1(seed_hash) = self;
157 Ok(domain::IssuerProofBinding::IcCanisterSignatureV1 { seed_hash })
158 }
159}
160record!(IcCanisterSignatureProofV1 { signature_cbor: Bytes<131072>, public_key_der: Bytes<256> });
161record!(ChainKeyBatchHeaderV1 {
162 schema_version: u16,
163 root_canister_id: domain::Principal,
164 batch_id: [u8; 32],
165 proof_epoch: u64,
166 registry_epoch: u64,
167 registry_hash: [u8; 32],
168 tree_root: [u8; 32],
169 not_before_ns: u64,
170 expires_at_ns: u64,
171 algorithm: ChainKeyAlgorithm,
172 key_id: ChainKeyKeyId,
173 derivation_path_hash: [u8; 32],
174 key_version: u64,
175});
176record!(ChainKeyDelegationCertV1 {
177 root_canister_id: domain::Principal, issuer_canister_id: domain::Principal, proof_epoch: u64,
178 issuer_proof_algorithm: IssuerProofAlgorithm, issuer_proof_binding_hash: [u8; 32],
179 issuer_proof_binding: IssuerProofBinding, max_token_ttl_ns: u64,
180 audience: DelegationAudience, grants: List<DelegatedRoleGrant, MAX_GRANTS>,
181 not_before_ns: u64, expires_at_ns: u64, registry_epoch: u64, registry_hash: [u8; 32],
182});
183record!(ChainKeyRootSignatureV1 {
184 algorithm: ChainKeyAlgorithm, key_id: ChainKeyKeyId,
185 derivation_path: List<Bytes<256>, 32>, public_key: Bytes<65>, signature: Bytes<64>,
186});
187tuple_enum!(ChainKeyBatchWitnessStepV1 {
188 LeftSibling([u8; 32]),
189 RightSibling([u8; 32])
190});
191record!(ChainKeyBatchWitnessV1 { steps: List<ChainKeyBatchWitnessStepV1, 64> });
192record!(IcChainKeyBatchSignatureProofV1 {
193 header: ChainKeyBatchHeaderV1,
194 delegation_cert: ChainKeyDelegationCertV1,
195 issuer_witness: ChainKeyBatchWitnessV1,
196 signature: ChainKeyRootSignatureV1,
197});
198tuple_enum!(RootProof {
199 IcChainKeyBatchSignatureV1(IcChainKeyBatchSignatureProofV1)
200});
201record!(DelegationCert {
202 root_pid: domain::Principal, issuer_pid: domain::Principal, issuer_proof_alg: IssuerProofAlgorithm,
203 issuer_proof_binding_hash: [u8; 32], issuer_proof_binding: IssuerProofBinding,
204 issued_at_ns: u64, not_before_ns: u64, expires_at_ns: u64,
205 max_token_ttl_ns: u64, aud: DelegationAudience, grants: List<DelegatedRoleGrant, MAX_GRANTS>,
206});
207record!(DelegationProof {
208 cert: DelegationCert,
209 root_proof: RootProof
210});
211#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
212#[cfg_attr(feature = "typescript", derive(ic_wire::Wire))]
213pub struct CertifiedProof {
214 pub delegation: DelegationProof,
215 pub issuer_signature: IcCanisterSignatureProofV1,
216}
217#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
218#[cfg_attr(feature = "typescript", derive(ic_wire::Wire))]
219pub enum PortableProof {
220 Certified(Box<CertifiedProof>),
221 Ed25519(Bytes<64>),
222}
223impl Field<domain::PortableProof> for PortableProof {
224 fn from_domain(value: domain::PortableProof) -> Result<Self, Error> {
225 Ok(match value {
226 domain::PortableProof::Certified {
227 delegation,
228 issuer_signature,
229 } => Self::Certified(Box::new(CertifiedProof {
230 delegation: (*delegation).try_into()?,
231 issuer_signature: issuer_signature.try_into()?,
232 })),
233 domain::PortableProof::Ed25519 { signature } => Self::Ed25519(Bytes::new(signature)?),
234 })
235 }
236 fn into_domain(self) -> Result<domain::PortableProof, Error> {
237 Ok(match self {
238 Self::Certified(value) => domain::PortableProof::Certified {
239 delegation: Box::new(value.delegation.try_into()?),
240 issuer_signature: value.issuer_signature.try_into()?,
241 },
242 Self::Ed25519(signature) => domain::PortableProof::Ed25519 {
243 signature: signature.into_vec(),
244 },
245 })
246 }
247}
248record!(PortableCredential { claims: Bytes<MAX_CLAIMS_BYTES>, proof: PortableProof });
249unit_enum!(AdmissionRefusal {
251 NotYetValid,
252 Expired,
253 InvalidCredential,
254 AuthorityUnavailable,
255 BeforeReset,
256 ScopeRejected,
257 ReplayConflict,
258 Revoked,
259 Capacity,
260 Throttled,
261 Unavailable
262});
263unit_enum!(LogoutUnavailable {
264 Capacity,
265 Storage,
266 Clock,
267 ResetPending
268});
269#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
270#[cfg_attr(feature = "typescript", derive(ic_wire::Wire))]
271pub enum LogoutResult {
272 Revoked(u64),
273 InvalidCaller,
274 Unavailable(LogoutUnavailable),
275}
276impl From<domain::LogoutResult> for LogoutResult {
277 fn from(value: domain::LogoutResult) -> Self {
278 match value {
279 domain::LogoutResult::Revoked { deny_until_ns } => Self::Revoked(deny_until_ns),
280 domain::LogoutResult::InvalidCaller => Self::InvalidCaller,
281 domain::LogoutResult::Unavailable(reason) => Self::Unavailable(reason.into()),
282 }
283 }
284}
285unit_enum!(SessionReadRefusal {
286 MissingOrExpired,
287 AuthorityUnavailable,
288 ScopeRejected,
289 Revoked,
290 Unavailable
291});