ic_platform/principal/
mod.rs1use serde::{Deserialize, Serialize};
12use std::convert::TryFrom;
13
14#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
16pub enum PrincipalError {
17 BytesTooLong(),
19
20 InvalidBase32(),
22
23 TextTooShort(),
25
26 TextTooLong(),
28
29 CheckSequenceNotMatch(),
31
32 AbnormalGrouped(Principal),
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
70pub struct Principal {
71 len: u8,
73
74 bytes: [u8; Self::MAX_LENGTH_IN_BYTES],
77}
78
79impl Principal {
80 pub const MAX_LENGTH_IN_BYTES: usize = 29;
82 pub const MAX_LENGTH_IN_TEXT: usize = 63;
84 #[allow(dead_code)]
85 const CRC_LENGTH_IN_BYTES: usize = 4;
86
87 const ANONYMOUS_TAG: u8 = 4;
88
89 pub const fn management_canister() -> Self {
91 Self {
92 len: 0,
93 bytes: [0; Self::MAX_LENGTH_IN_BYTES],
94 }
95 }
96
97 pub fn self_authenticating<P: AsRef<[u8]>>(public_key: P) -> Self {
99 ExternalPrincipal::self_authenticating(public_key).into()
100 }
101
102 pub const fn anonymous() -> Self {
104 let mut bytes = [0; Self::MAX_LENGTH_IN_BYTES];
105 bytes[0] = Self::ANONYMOUS_TAG;
106 Self { len: 1, bytes }
107 }
108
109 const fn from_slice_core(slice: &[u8]) -> Option<Self> {
111 match slice.len() {
112 len @ 0..=Self::MAX_LENGTH_IN_BYTES => {
113 let mut bytes = [0; Self::MAX_LENGTH_IN_BYTES];
114 let mut i = 0;
115 while i < len {
116 bytes[i] = slice[i];
117 i += 1;
118 }
119 Some(Self {
120 len: len as u8,
121 bytes,
122 })
123 }
124 _ => None,
125 }
126 }
127
128 pub const fn from_slice(slice: &[u8]) -> Self {
134 match Self::from_slice_core(slice) {
135 Some(principal) => principal,
136 _ => panic!("slice length exceeds capacity"),
137 }
138 }
139
140 pub const fn try_from_slice(slice: &[u8]) -> Result<Self, PrincipalError> {
142 match Self::from_slice_core(slice) {
143 Some(principal) => Ok(principal),
144 None => Err(PrincipalError::BytesTooLong()),
145 }
146 }
147
148 pub fn from_text<S: AsRef<str>>(text: S) -> Result<Self, PrincipalError> {
150 let input = text.as_ref();
151 if input.len() > Self::MAX_LENGTH_IN_TEXT {
152 return Err(PrincipalError::TextTooLong());
153 }
154 let mut encoded = [0u8; Self::MAX_LENGTH_IN_TEXT];
155 let mut len = 0;
156 for byte in input.bytes().filter(|&byte| byte != b'-') {
157 encoded[len] = byte.to_ascii_uppercase();
158 len += 1;
159 }
160 let encoding = data_encoding::BASE32_NOPAD;
161 let size = encoding
162 .decode_len(len)
163 .map_err(|_| PrincipalError::InvalidBase32())?;
164 let mut decoded = [0u8; 39]; let size = encoding
166 .decode_mut(&encoded[..len], &mut decoded[..size])
167 .map_err(|_| PrincipalError::InvalidBase32())?;
168 if size < Self::CRC_LENGTH_IN_BYTES {
169 return Err(PrincipalError::TextTooShort());
170 }
171 let payload = &decoded[Self::CRC_LENGTH_IN_BYTES..size];
172 if payload.len() > Self::MAX_LENGTH_IN_BYTES {
173 return Err(PrincipalError::TextTooLong());
174 }
175 if crc32fast::hash(payload).to_be_bytes() != decoded[..Self::CRC_LENGTH_IN_BYTES] {
176 return Err(PrincipalError::CheckSequenceNotMatch());
177 }
178 let result = Self::from_slice(payload);
179 let mut output = [0u8; Self::MAX_LENGTH_IN_TEXT];
180 if !input.eq_ignore_ascii_case(result.write_text(&mut output)) {
181 return Err(PrincipalError::AbnormalGrouped(result));
182 }
183 Ok(result)
184 }
185
186 pub fn to_text(&self) -> String {
188 self.write_text(&mut [0u8; Self::MAX_LENGTH_IN_TEXT])
189 .to_owned()
190 }
191
192 #[inline]
199 pub const fn as_slice(&self) -> &[u8] {
200 self.bytes.split_at(self.len as usize).0
201 }
202
203 #[inline]
209 #[allow(clippy::len_without_is_empty)]
210 pub const fn len(&self) -> u8 {
211 self.len
212 }
213
214 #[inline]
221 pub const fn as_fixed_bytes(&self) -> &[u8; Self::MAX_LENGTH_IN_BYTES] {
222 &self.bytes
223 }
224}
225
226impl std::fmt::Display for Principal {
227 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
228 f.write_str(self.write_text(&mut [0u8; Self::MAX_LENGTH_IN_TEXT]))
229 }
230}
231
232impl std::str::FromStr for Principal {
233 type Err = PrincipalError;
234
235 fn from_str(s: &str) -> Result<Self, Self::Err> {
236 Principal::from_text(s)
237 }
238}
239
240impl TryFrom<&str> for Principal {
241 type Error = PrincipalError;
242
243 fn try_from(s: &str) -> Result<Self, Self::Error> {
244 Principal::from_text(s)
245 }
246}
247
248impl TryFrom<Vec<u8>> for Principal {
249 type Error = PrincipalError;
250
251 fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
252 Self::try_from(bytes.as_slice())
253 }
254}
255
256impl TryFrom<&Vec<u8>> for Principal {
257 type Error = PrincipalError;
258
259 fn try_from(bytes: &Vec<u8>) -> Result<Self, Self::Error> {
260 Self::try_from(bytes.as_slice())
261 }
262}
263
264impl TryFrom<&[u8]> for Principal {
265 type Error = PrincipalError;
266
267 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
268 Self::try_from_slice(bytes)
269 }
270}
271
272impl AsRef<[u8]> for Principal {
273 fn as_ref(&self) -> &[u8] {
274 self.as_slice()
275 }
276}
277
278impl serde::Serialize for Principal {
280 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
281 if serializer.is_human_readable() {
282 self.to_text().serialize(serializer)
283 } else {
284 serializer.serialize_bytes(self.as_slice())
285 }
286 }
287}
288
289mod deserialize {
291 use super::Principal;
292 use std::convert::TryFrom;
293
294 pub(super) struct PrincipalVisitor;
297
298 impl serde::de::Visitor<'_> for PrincipalVisitor {
299 type Value = super::Principal;
300
301 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
302 formatter.write_str("bytes or string")
303 }
304
305 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
306 where
307 E: serde::de::Error,
308 {
309 Principal::from_text(v).map_err(E::custom)
310 }
311
312 fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>
313 where
314 E: serde::de::Error,
315 {
316 Principal::try_from(value).map_err(E::custom)
317 }
318 fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
320 where
321 E: serde::de::Error,
322 {
323 Principal::try_from_slice(&v).map_err(E::custom)
324 }
325 }
326}
327
328impl<'de> serde::Deserialize<'de> for Principal {
329 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Principal, D::Error> {
330 use serde::de::Error;
331 if deserializer.is_human_readable() {
332 deserializer
333 .deserialize_str(deserialize::PrincipalVisitor)
334 .map_err(D::Error::custom)
335 } else {
336 deserializer
337 .deserialize_bytes(deserialize::PrincipalVisitor)
338 .map_err(D::Error::custom)
339 }
340 }
341}
342
343pub use ic_principal::Principal as ExternalPrincipal;
346pub use ic_principal::PrincipalError as ExternalPrincipalError;
348
349impl From<ExternalPrincipal> for Principal {
350 fn from(value: ExternalPrincipal) -> Self {
351 Self::from_slice(value.as_slice())
352 }
353}
354impl From<Principal> for ExternalPrincipal {
355 fn from(value: Principal) -> Self {
356 Self::from_slice(value.as_slice())
357 }
358}
359impl std::fmt::Display for PrincipalError {
360 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
361 match self {
362 Self::BytesTooLong() => f.write_str("Bytes is longer than 29 bytes."),
363 Self::InvalidBase32() => f.write_str("Text must be in valid Base32 encoding."),
364 Self::TextTooShort() => f.write_str("Text is too short."),
365 Self::TextTooLong() => f.write_str("Text is too long."),
366 Self::CheckSequenceNotMatch() => f.write_str(
367 "CRC32 check sequence doesn't match with calculated from Principal bytes.",
368 ),
369 Self::AbnormalGrouped(principal) => write!(
370 f,
371 "Text should be separated by - (dash) every 5 characters: expected \"{principal}\""
372 ),
373 }
374 }
375}
376impl std::error::Error for PrincipalError {}
377
378impl Principal {
379 fn write_text<'a>(&self, output: &'a mut [u8; Self::MAX_LENGTH_IN_TEXT]) -> &'a str {
380 let mut input = [0u8; 33];
381 input[..4].copy_from_slice(&crc32fast::hash(self.as_slice()).to_be_bytes());
382 input[4..4 + self.len as usize].copy_from_slice(self.as_slice());
383 let encoding = data_encoding::BASE32_NOPAD;
384 let mut encoded = [0u8; 53];
385 let count = encoding.encode_len(4 + self.len as usize);
386 encoding.encode_mut(&input[..4 + self.len as usize], &mut encoded[..count]);
387 let mut written = 0;
388 for (i, byte) in encoded[..count].iter().enumerate() {
389 if i > 0 && i % 5 == 0 {
390 output[written] = b'-';
391 written += 1;
392 }
393 output[written] = byte.to_ascii_lowercase();
394 written += 1;
395 }
396 std::str::from_utf8(&output[..written]).expect("Base32 is ASCII")
397 }
398}
399
400#[cfg(test)]
401mod tests;