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