1use crate::nodes::{NODE_IDENTITY_SIZE, NodeIdentity};
8use nym_crypto::asymmetric::{ed25519, x25519};
9use nym_sphinx_types::Destination;
10use serde::de::{Error as SerdeError, SeqAccess, Unexpected, Visitor};
11use serde::{Deserialize, Deserializer, Serialize, Serializer};
12use std::fmt::{self, Formatter};
13use std::str::FromStr;
14use thiserror::Error;
15
16pub type ClientEncryptionKey = x25519::PublicKey;
19const CLIENT_ENCRYPTION_KEY_SIZE: usize = x25519::PUBLIC_KEY_SIZE;
20
21pub type ClientIdentity = ed25519::PublicKey;
22const CLIENT_IDENTITY_SIZE: usize = ed25519::PUBLIC_KEY_LENGTH;
23
24pub type RecipientBytes = [u8; Recipient::LEN];
25
26#[derive(Debug, Error)]
27pub enum RecipientFormattingError {
28 #[error("recipient is malformed - {reason} ")]
29 MalformedRecipientError { reason: String },
30
31 #[error("recipient's identity key is malformed: {0}")]
32 MalformedIdentityError(ed25519::Ed25519RecoveryError),
33
34 #[error("recipient's encryption key is malformed: {0}")]
35 MalformedEncryptionKeyError(#[from] x25519::KeyRecoveryError),
36
37 #[error("recipient gateway's identity key is malformed: {0}")]
38 MalformedGatewayError(ed25519::Ed25519RecoveryError),
39}
40
41#[derive(Clone, Copy, PartialEq, Eq)]
43pub struct Recipient {
44 client_identity: ClientIdentity,
45 client_encryption_key: ClientEncryptionKey,
46 gateway: NodeIdentity,
47}
48
49impl Serialize for Recipient {
52 fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
53 where
54 S: Serializer,
55 {
56 serializer.serialize_bytes(&self.to_bytes())
57 }
58}
59
60impl<'de> Deserialize<'de> for Recipient {
61 fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
62 where
63 D: Deserializer<'de>,
64 {
65 struct RecipientVisitor;
66
67 impl<'de> Visitor<'de> for RecipientVisitor {
68 type Value = Recipient;
69
70 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
71 write!(
72 formatter,
73 "A recipient is 96-byte sequence containing two ed25519 public keys and one x25519 public key all in compressed forms."
74 )
75 }
76
77 fn visit_bytes<E>(self, bytes: &[u8]) -> Result<Self::Value, E>
78 where
79 E: SerdeError,
80 {
81 if bytes.len() != Recipient::LEN {
82 return Err(SerdeError::invalid_length(bytes.len(), &self));
83 }
84
85 let mut recipient_bytes = [0u8; Recipient::LEN];
86 recipient_bytes.copy_from_slice(bytes);
88
89 Recipient::try_from_bytes(recipient_bytes).map_err(|_| {
90 SerdeError::invalid_value(
91 Unexpected::Other("At least one of the curve points was malformed"),
92 &self,
93 )
94 })
95 }
96
97 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
98 where
99 A: SeqAccess<'de>,
100 {
101 if let Some(size_hint) = seq.size_hint()
104 && size_hint != Recipient::LEN
105 {
106 return Err(SerdeError::invalid_length(size_hint, &self));
107 }
108
109 let mut recipient_bytes = [0u8; Recipient::LEN];
110
111 #[allow(clippy::needless_range_loop)]
113 for i in 0..Recipient::LEN {
114 let Some(elem) = seq.next_element::<u8>()? else {
115 return Err(SerdeError::invalid_length(i + 1, &self));
116 };
117 recipient_bytes[i] = elem;
118 }
119
120 if seq.next_element::<u8>()?.is_some() {
122 return Err(SerdeError::invalid_length(Recipient::LEN + 1, &self));
123 }
124
125 Recipient::try_from_bytes(recipient_bytes).map_err(|_| {
126 SerdeError::invalid_value(
127 Unexpected::Other("At least one of the curve points was malformed"),
128 &self,
129 )
130 })
131 }
132 }
133
134 deserializer.deserialize_bytes(RecipientVisitor)
135 }
136}
137
138impl Recipient {
139 pub const LEN: usize = CLIENT_IDENTITY_SIZE + CLIENT_ENCRYPTION_KEY_SIZE + NODE_IDENTITY_SIZE;
140
141 pub fn new(
142 client_identity: ClientIdentity,
143 client_encryption_key: ClientEncryptionKey,
144 gateway: NodeIdentity,
145 ) -> Self {
146 Recipient {
147 client_identity,
148 client_encryption_key,
149 gateway,
150 }
151 }
152
153 pub fn as_sphinx_destination(&self) -> Destination {
157 Destination::new(
160 self.client_identity.derive_destination_address(),
161 Default::default(),
162 )
163 }
164
165 pub fn identity(&self) -> &ClientIdentity {
166 &self.client_identity
167 }
168
169 pub fn encryption_key(&self) -> &ClientEncryptionKey {
170 &self.client_encryption_key
171 }
172
173 pub fn gateway(&self) -> NodeIdentity {
174 self.gateway
175 }
176
177 pub fn to_bytes(self) -> RecipientBytes {
178 let mut out = [0u8; Self::LEN];
179 out[..CLIENT_IDENTITY_SIZE].copy_from_slice(&self.client_identity.to_bytes());
180 out[CLIENT_IDENTITY_SIZE..CLIENT_IDENTITY_SIZE + CLIENT_ENCRYPTION_KEY_SIZE]
181 .copy_from_slice(&self.client_encryption_key.to_bytes());
182 out[CLIENT_IDENTITY_SIZE + CLIENT_ENCRYPTION_KEY_SIZE..]
183 .copy_from_slice(&self.gateway.to_bytes());
184
185 out
186 }
187
188 pub fn try_from_bytes(bytes: RecipientBytes) -> Result<Self, RecipientFormattingError> {
189 let identity_bytes = &bytes[..CLIENT_IDENTITY_SIZE];
190 let enc_key_bytes =
191 &bytes[CLIENT_IDENTITY_SIZE..CLIENT_IDENTITY_SIZE + CLIENT_ENCRYPTION_KEY_SIZE];
192 let gateway_bytes = &bytes[CLIENT_IDENTITY_SIZE + CLIENT_ENCRYPTION_KEY_SIZE..];
193
194 let client_identity = match ClientIdentity::from_bytes(identity_bytes) {
195 Ok(client_id) => client_id,
196 Err(err) => return Err(RecipientFormattingError::MalformedIdentityError(err)),
197 };
198
199 let client_encryption_key = ClientEncryptionKey::from_bytes(enc_key_bytes)?;
200
201 let gateway = match NodeIdentity::from_bytes(gateway_bytes) {
202 Ok(gate_id) => gate_id,
203 Err(err) => return Err(RecipientFormattingError::MalformedGatewayError(err)),
204 };
205
206 Ok(Recipient {
207 client_identity,
208 client_encryption_key,
209 gateway,
210 })
211 }
212
213 pub fn try_from_base58_string<S: Into<String>>(
214 full_address: S,
215 ) -> Result<Self, RecipientFormattingError> {
216 let string_address = full_address.into();
217 let split: Vec<_> = string_address.split('@').collect();
218 if split.len() != 2 {
219 return Err(RecipientFormattingError::MalformedRecipientError {
220 reason: "the string address does not contain exactly a single '@' character"
221 .to_string(),
222 });
223 }
224 let client_half = split[0];
225 let gateway_half = split[1];
226
227 let split_client: Vec<_> = client_half.split('.').collect();
228 if split_client.len() != 2 {
229 return Err(RecipientFormattingError::MalformedRecipientError {
230 reason: "the string address does not contain exactly a single '.' character"
231 .to_string(),
232 });
233 }
234
235 let client_identity = match ClientIdentity::from_base58_string(split_client[0]) {
236 Ok(client_id) => client_id,
237 Err(err) => return Err(RecipientFormattingError::MalformedIdentityError(err)),
238 };
239
240 let client_encryption_key = ClientEncryptionKey::from_base58_string(split_client[1])?;
241
242 let gateway = match NodeIdentity::from_base58_string(gateway_half) {
243 Ok(gate_id) => gate_id,
244 Err(err) => return Err(RecipientFormattingError::MalformedGatewayError(err)),
245 };
246
247 Ok(Recipient {
248 client_identity,
249 client_encryption_key,
250 gateway,
251 })
252 }
253}
254
255impl std::fmt::Display for Recipient {
257 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
258 write!(
259 f,
260 "{}.{}@{}",
261 self.client_identity.to_base58_string(),
262 self.client_encryption_key.to_base58_string(),
263 self.gateway.to_base58_string()
264 )
265 }
266}
267
268impl std::fmt::Debug for Recipient {
269 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
270 <Self as std::fmt::Display>::fmt(self, f)
272 }
273}
274
275impl FromStr for Recipient {
276 type Err = RecipientFormattingError;
277
278 fn from_str(s: &str) -> Result<Self, Self::Err> {
279 Recipient::try_from_base58_string(s)
280 }
281}
282
283#[cfg(test)]
284mod tests {
285 use super::*;
286
287 fn mock_recipient() -> Recipient {
288 Recipient::try_from_bytes([
289 67, 5, 132, 146, 3, 236, 116, 89, 254, 57, 131, 159, 69, 181, 55, 208, 12, 108, 136,
290 83, 58, 76, 171, 195, 31, 98, 92, 64, 68, 53, 156, 184, 100, 189, 73, 3, 238, 103, 156,
291 108, 124, 199, 42, 79, 172, 98, 81, 177, 182, 100, 167, 164, 74, 183, 199, 213, 162,
292 173, 102, 112, 30, 159, 148, 66, 44, 75, 230, 182, 138, 114, 170, 163, 209, 82, 204,
293 100, 118, 91, 57, 150, 212, 147, 151, 135, 148, 16, 213, 223, 182, 164, 242, 37, 40,
294 73, 137, 228,
295 ])
296 .unwrap()
297 }
298
299 #[test]
300 fn string_conversion_works() {
301 let mut rng = rand::rng();
302
303 let client_id_pair = ed25519::KeyPair::new(&mut rng);
304 let client_enc_pair = x25519::KeyPair::new(&mut rng);
305 let gateway_id_pair = ed25519::KeyPair::new(&mut rng);
306
307 let recipient = Recipient::new(
308 *client_id_pair.public_key(),
309 *client_enc_pair.public_key(),
310 *gateway_id_pair.public_key(),
311 );
312
313 let str_recipient = recipient.to_string();
314 let recovered_recipient = Recipient::try_from_base58_string(str_recipient).unwrap();
315
316 assert_eq!(
318 recipient.client_identity.to_bytes(),
319 recovered_recipient.client_identity.to_bytes()
320 );
321 assert_eq!(
322 recipient.client_encryption_key.to_bytes(),
323 recovered_recipient.client_encryption_key.to_bytes()
324 );
325 assert_eq!(
326 recipient.gateway.to_bytes(),
327 recovered_recipient.gateway.to_bytes()
328 );
329 }
330
331 #[test]
332 fn bytes_conversion_works() {
333 let mut rng = rand::rng();
334
335 let client_id_pair = ed25519::KeyPair::new(&mut rng);
336 let client_enc_pair = x25519::KeyPair::new(&mut rng);
337 let gateway_id_pair = ed25519::KeyPair::new(&mut rng);
338
339 let recipient = Recipient::new(
340 *client_id_pair.public_key(),
341 *client_enc_pair.public_key(),
342 *gateway_id_pair.public_key(),
343 );
344
345 let bytes_recipient = recipient.to_bytes();
346 let recovered_recipient = Recipient::try_from_bytes(bytes_recipient).unwrap();
347
348 assert_eq!(
350 recipient.client_identity.to_bytes(),
351 recovered_recipient.client_identity.to_bytes()
352 );
353 assert_eq!(
354 recipient.client_encryption_key.to_bytes(),
355 recovered_recipient.client_encryption_key.to_bytes()
356 );
357 assert_eq!(
358 recipient.gateway.to_bytes(),
359 recovered_recipient.gateway.to_bytes()
360 );
361 }
362
363 #[test]
365 fn bincode_serialisation_works() {
366 let recipient = mock_recipient();
367
368 #[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq)]
369 struct MyStruct {
370 recipient: Recipient,
371 }
372 let a = MyStruct { recipient };
373 let s = bincode::serialize(&a).unwrap();
374
375 let b = bincode::deserialize(&s).unwrap();
376
377 assert_eq!(a, b);
378 }
379
380 #[test]
382 fn json_serialisation_works() {
383 use serde::{Deserialize, Serialize};
384
385 let recipient = mock_recipient();
386
387 #[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq)]
388 struct MyStruct {
389 recipient: Recipient,
390 }
391 let a = MyStruct { recipient };
392 let s = serde_json::to_string(&a).unwrap();
393
394 let b = serde_json::from_str(&s).unwrap();
395
396 assert_eq!(a, b);
397 }
398}