core_crypto/e2e_identity/
id.rs1use crate::{prelude::ClientId, CryptoError, CryptoResult};
2use base64::Engine;
3
4#[cfg(test)]
5const DOMAIN: &str = "wire.com";
6const COLON: u8 = 58;
7
8#[derive(Debug, Clone, PartialEq, Eq, Hash, derive_more::From, derive_more::Into, derive_more::Deref)]
10pub struct WireQualifiedClientId(ClientId);
11
12#[cfg(test)]
13impl WireQualifiedClientId {
14 pub fn get_user_id(&self) -> String {
15 let mut split = self.0.split(|b| b == &COLON);
16 let user_id = split.next().unwrap();
17 uuid::Uuid::try_parse_ascii(user_id).unwrap().to_string()
18 }
19
20 pub fn generate() -> Self {
21 let user_id = uuid::Uuid::new_v4().to_string();
22 let device_id = rand::random::<u64>();
23 let client_id = format!("{user_id}:{device_id:x}@{DOMAIN}");
24 Self(client_id.into_bytes().into())
25 }
26}
27
28impl<'a> TryFrom<&'a [u8]> for WireQualifiedClientId {
30 type Error = CryptoError;
31
32 fn try_from(bytes: &'a [u8]) -> CryptoResult<Self> {
33 const COLON: u8 = 58;
34 let mut split = bytes.split(|b| b == &COLON);
35 let user_id = split.next().ok_or(CryptoError::InvalidClientId)?;
36
37 let user_id = base64::prelude::BASE64_URL_SAFE_NO_PAD
38 .decode(user_id)
39 .map_err(|_| CryptoError::InvalidClientId)?;
40
41 let user_id = uuid::Uuid::from_slice(&user_id).map_err(|_| CryptoError::InvalidClientId)?;
42 let mut buf = [0; uuid::fmt::Hyphenated::LENGTH];
43 let user_id = user_id.hyphenated().encode_lower(&mut buf);
44
45 let rest = split.next().ok_or(CryptoError::InvalidClientId)?;
46 if split.next().is_some() {
47 return Err(CryptoError::InvalidClientId);
48 }
49
50 let client_id = [user_id.as_bytes(), &[COLON], rest].concat();
51 Ok(Self(client_id.into()))
52 }
53}
54
55impl std::str::FromStr for WireQualifiedClientId {
56 type Err = CryptoError;
57
58 fn from_str(s: &str) -> CryptoResult<Self> {
59 s.as_bytes().try_into()
60 }
61}
62
63impl TryFrom<WireQualifiedClientId> for String {
64 type Error = CryptoError;
65
66 fn try_from(id: WireQualifiedClientId) -> CryptoResult<Self> {
67 Ok(String::from_utf8(id.to_vec())?)
68 }
69}
70
71#[derive(Debug, Clone, PartialEq, Eq, Hash, derive_more::From, derive_more::Into, derive_more::Deref)]
73pub struct QualifiedE2eiClientId(ClientId);
74
75#[cfg(test)]
76impl QualifiedE2eiClientId {
77 pub fn generate() -> Self {
78 Self::generate_from_user_id(&uuid::Uuid::new_v4())
79 }
80
81 pub fn generate_with_domain(domain: &str) -> Self {
82 Self::generate_from_user_id_and_domain(&uuid::Uuid::new_v4(), domain)
83 }
84
85 pub fn generate_from_user_id(user_id: &uuid::Uuid) -> Self {
86 Self::generate_from_user_id_and_domain(user_id, DOMAIN)
87 }
88
89 pub fn generate_from_user_id_and_domain(user_id: &uuid::Uuid, domain: &str) -> Self {
90 use base64::Engine as _;
91
92 let user_id = base64::prelude::BASE64_URL_SAFE_NO_PAD.encode(user_id.as_bytes());
93
94 let device_id = rand::random::<u64>();
95 let client_id = format!("{user_id}:{device_id:x}@{domain}");
96 Self(client_id.into_bytes().into())
97 }
98
99 pub fn from_str_unchecked(s: &str) -> Self {
100 Self(s.as_bytes().into())
101 }
102}
103
104impl<'a> TryFrom<&'a [u8]> for QualifiedE2eiClientId {
106 type Error = CryptoError;
107
108 fn try_from(bytes: &'a [u8]) -> CryptoResult<Self> {
109 let mut split = bytes.split(|b| b == &COLON);
110 let user_id = split.next().ok_or(CryptoError::InvalidClientId)?;
111
112 let user_id = std::str::from_utf8(user_id)?
113 .parse::<uuid::Uuid>()
114 .map_err(|_| CryptoError::InvalidClientId)?;
115
116 let user_id = base64::prelude::BASE64_URL_SAFE_NO_PAD.encode(user_id.as_bytes());
117
118 let rest = split.next().ok_or(CryptoError::InvalidClientId)?;
119 if split.next().is_some() {
120 return Err(CryptoError::InvalidClientId);
121 }
122
123 let client_id = [user_id.as_bytes(), &[COLON], rest].concat();
124 Ok(Self(client_id.into()))
125 }
126}
127
128#[cfg(test)]
129impl std::str::FromStr for QualifiedE2eiClientId {
130 type Err = CryptoError;
131
132 fn from_str(s: &str) -> CryptoResult<Self> {
133 s.as_bytes().try_into()
134 }
135}
136
137impl TryFrom<QualifiedE2eiClientId> for String {
138 type Error = CryptoError;
139
140 fn try_from(id: QualifiedE2eiClientId) -> CryptoResult<Self> {
141 Ok(String::from_utf8(id.to_vec())?)
142 }
143}