core_crypto/mls/session/
id.rs

1use std::{
2    borrow::{Borrow, Cow},
3    fmt,
4    ops::Deref,
5};
6
7use super::error::Error;
8
9/// A Client identifier
10///
11/// A unique identifier for clients. A client is an identifier for each App a user is using, such as desktop,
12/// mobile, etc. Users can have multiple clients.
13/// More information [here](https://messaginglayersecurity.rocks/mls-architecture/draft-ietf-mls-architecture.html#name-group-members-and-clients)
14#[derive(
15    core_crypto_macros::Debug,
16    Clone,
17    Eq,
18    PartialOrd,
19    Ord,
20    Hash,
21    derive_more::From,
22    derive_more::Into,
23    serde::Serialize,
24    serde::Deserialize,
25)]
26#[sensitive]
27pub struct ClientId(pub(crate) Vec<u8>);
28
29impl ClientId {
30    pub(crate) fn into_inner(self) -> Vec<u8> {
31        self.0
32    }
33}
34
35impl From<Box<[u8]>> for ClientId {
36    fn from(value: Box<[u8]>) -> Self {
37        Self(value.into())
38    }
39}
40
41impl<const N: usize> From<[u8; N]> for ClientId {
42    fn from(value: [u8; N]) -> Self {
43        Self(value.into())
44    }
45}
46
47impl<'a> From<&'a [u8]> for ClientId {
48    fn from(value: &'a [u8]) -> Self {
49        Self(value.into())
50    }
51}
52
53impl From<ClientId> for Box<[u8]> {
54    fn from(value: ClientId) -> Self {
55        value.0.into_boxed_slice()
56    }
57}
58
59impl Deref for ClientId {
60    type Target = ClientIdRef;
61
62    fn deref(&self) -> &Self::Target {
63        ClientIdRef::new(&self.0)
64    }
65}
66
67impl AsRef<[u8]> for ClientId {
68    fn as_ref(&self) -> &[u8] {
69        &self.0
70    }
71}
72
73impl AsRef<ClientIdRef> for ClientId {
74    fn as_ref(&self) -> &ClientIdRef {
75        ClientIdRef::new(&self.0)
76    }
77}
78
79impl From<ClientId> for Cow<'_, [u8]> {
80    fn from(value: ClientId) -> Self {
81        Cow::Owned(value.0)
82    }
83}
84
85impl std::fmt::Display for ClientId {
86    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87        write!(f, "{}", hex::encode(self.0.as_slice()))
88    }
89}
90
91impl std::str::FromStr for ClientId {
92    type Err = Error;
93
94    fn from_str(s: &str) -> Result<Self, Self::Err> {
95        Ok(Self(
96            hex::decode(s).map_or_else(|_| s.as_bytes().to_vec(), std::convert::identity),
97        ))
98    }
99}
100
101impl<T> PartialEq<T> for ClientId
102where
103    ClientIdRef: PartialEq<T>,
104{
105    fn eq(&self, other: &T) -> bool {
106        (**self).eq(other)
107    }
108}
109
110#[cfg(test)]
111impl From<&str> for ClientId {
112    fn from(value: &str) -> Self {
113        Self(value.as_bytes().into())
114    }
115}
116
117/// Reference to a [`ClientId`].
118///
119/// This type is `!Sized` and is only ever seen as a reference, like `str` or `[u8]`.
120//
121// pattern from https://stackoverflow.com/a/64990850
122#[repr(transparent)]
123#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, derive_more::Deref)]
124pub struct ClientIdRef([u8]);
125
126impl ClientIdRef {
127    /// Creates a `ClientId` Ref, needed to implement `Borrow<ClientIdRef>` for `T`
128    pub fn new<Bytes>(bytes: &Bytes) -> &ClientIdRef
129    where
130        Bytes: AsRef<[u8]> + ?Sized,
131    {
132        // safety: because of `repr(transparent)` we know that `ClientIdRef` has a memory layout
133        // identical to `[u8]`, so we can perform this cast
134        unsafe { &*(bytes.as_ref() as *const [u8] as *const ClientIdRef) }
135    }
136
137    /// View this reference as a byte slice
138    pub fn as_slice(&self) -> &[u8] {
139        self.as_ref()
140    }
141}
142
143impl<'a> From<&'a [u8]> for &'a ClientIdRef {
144    fn from(value: &'a [u8]) -> Self {
145        ClientIdRef::new(value)
146    }
147}
148
149impl<'a> From<&'a Vec<u8>> for &'a ClientIdRef {
150    fn from(value: &'a Vec<u8>) -> Self {
151        ClientIdRef::new(value.as_slice())
152    }
153}
154
155impl Borrow<ClientIdRef> for ClientId {
156    fn borrow(&self) -> &ClientIdRef {
157        ClientIdRef::new(&self.0)
158    }
159}
160
161impl Borrow<ClientIdRef> for &'_ ClientId {
162    fn borrow(&self) -> &ClientIdRef {
163        ClientIdRef::new(&*self.0)
164    }
165}
166
167impl ToOwned for ClientIdRef {
168    type Owned = ClientId;
169
170    fn to_owned(&self) -> Self::Owned {
171        ClientId(self.0.to_owned())
172    }
173}
174
175impl AsRef<[u8]> for ClientIdRef {
176    fn as_ref(&self) -> &[u8] {
177        &self.0
178    }
179}
180
181impl<'a> From<&'a ClientIdRef> for Cow<'a, [u8]> {
182    fn from(value: &'a ClientIdRef) -> Self {
183        Cow::Borrowed(value.as_ref())
184    }
185}
186
187impl PartialEq<ClientId> for ClientIdRef {
188    fn eq(&self, other: &ClientId) -> bool {
189        &self.0 == other.as_slice()
190    }
191}
192
193impl PartialEq<[u8]> for ClientIdRef {
194    fn eq(&self, other: &[u8]) -> bool {
195        &self.0 == other
196    }
197}
198
199impl PartialEq<&'_ [u8]> for ClientIdRef {
200    fn eq(&self, other: &&'_ [u8]) -> bool {
201        &self.0 == *other
202    }
203}
204
205impl std::fmt::Display for ClientIdRef {
206    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
207        write!(f, "{}", hex::encode(&self.0))
208    }
209}
210
211macro_rules! impl_eq {
212    ($( $t:ty => |$self:ident, $other:ident| $impl:expr ; )+) => {
213        $(
214            impl PartialEq<$t> for ClientIdRef {
215                fn eq(&self, other: &$t) -> bool {
216                    let $self = self;
217                    let $other = other;
218                    $impl
219                }
220            }
221
222            impl PartialEq<ClientIdRef> for $t {
223                fn eq(&self, other: &ClientIdRef) -> bool {
224                    other.eq(self)
225                }
226            }
227
228            impl PartialEq<$t> for &'_ ClientIdRef {
229                fn eq(&self, other: &$t) -> bool {
230                    let $self = self;
231                    let $other = other;
232                    $impl
233                }
234            }
235
236            impl PartialEq<&'_ ClientIdRef> for $t {
237                fn eq(&self, other: &&'_ ClientIdRef) -> bool {
238                    other.eq(self)
239                }
240            }
241        )+
242    };
243}
244
245impl_eq!(
246    Vec<u8> => |me, other| me.0.eq(other.as_slice());
247    Cow<'_, ClientIdRef> => |me, other| me.eq(&other.as_slice());
248);
249
250// we can't use `core_crypto_macros::Debug` to generate this because `ClientIdRef: !Sized`,
251// and the `log` crate maintainers did not explicitly opt-in to allowing `!Sized` in their
252// `Value::from_debug` impl, even though it might make sense to.
253//
254// this has the consequence that we can't natively log a `ClientIdRef` as a value;
255// if we want to, we have to do `id_ref.to_owned()`. Which might be ok.
256impl fmt::Debug for ClientIdRef {
257    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
258        f.debug_tuple("ClientIdRef")
259            .field(&obfuscate::Obfuscated::from(&self.0))
260            .finish()
261    }
262}
263
264#[cfg(test)]
265impl ClientId {
266    pub(crate) fn to_user_id(&self) -> String {
267        let self_bytes: &[u8] = &self.0;
268        crate::e2e_identity::id::WireQualifiedClientId::try_from(self_bytes)
269            .unwrap()
270            .get_user_id()
271    }
272
273    pub(crate) fn to_string_triple(&self) -> [String; 3] {
274        let qualified_id = crate::e2e_identity::id::QualifiedE2eiClientId::from(self.clone());
275        let id_string: String = qualified_id.try_into().unwrap();
276        [id_string, "".into(), self.to_user_id()]
277    }
278}