1use alloc::vec::Vec;
2use core::fmt;
3
4use rns_crypto::identity::Identity;
5
6use crate::constants;
7use crate::hash;
8
9#[derive(Debug)]
10pub enum AnnounceError {
11 DataTooShort,
12 InvalidSignature,
13 DestinationMismatch,
14 SigningError,
15}
16
17impl fmt::Display for AnnounceError {
18 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
19 match self {
20 AnnounceError::DataTooShort => write!(f, "Announce data too short"),
21 AnnounceError::InvalidSignature => write!(f, "Invalid announce signature"),
22 AnnounceError::DestinationMismatch => write!(f, "Destination hash mismatch"),
23 AnnounceError::SigningError => write!(f, "Could not sign announce"),
24 }
25 }
26}
27
28#[derive(Debug, Clone)]
40pub struct AnnounceData {
41 pub public_key: [u8; 64],
42 pub name_hash: [u8; 10],
43 pub random_hash: [u8; 10],
44 pub ratchet: Option<[u8; 32]>,
45 pub signature: [u8; 64],
46 pub app_data: Option<Vec<u8>>,
47}
48
49#[derive(Debug)]
51pub struct ValidatedAnnounce {
52 pub identity_hash: [u8; 16],
53 pub public_key: [u8; 64],
54 pub name_hash: [u8; 10],
55 pub random_hash: [u8; 10],
56 pub ratchet: Option<[u8; 32]>,
57 pub app_data: Option<Vec<u8>>,
58}
59
60impl AnnounceData {
61 pub fn pack(
65 identity: &Identity,
66 destination_hash: &[u8; 16],
67 name_hash: &[u8; 10],
68 random_hash: &[u8; 10],
69 ratchet: Option<&[u8; 32]>,
70 app_data: Option<&[u8]>,
71 ) -> Result<(Vec<u8>, bool), AnnounceError> {
72 let public_key = identity
73 .get_public_key()
74 .ok_or(AnnounceError::SigningError)?;
75
76 let mut signed_data = Vec::new();
78 signed_data.extend_from_slice(destination_hash);
79 signed_data.extend_from_slice(&public_key);
80 signed_data.extend_from_slice(name_hash);
81 signed_data.extend_from_slice(random_hash);
82
83 let has_ratchet = ratchet.is_some();
84 if let Some(r) = ratchet {
85 signed_data.extend_from_slice(r);
86 }
87 if let Some(ad) = app_data {
88 signed_data.extend_from_slice(ad);
89 }
90
91 let signature = identity
92 .sign(&signed_data)
93 .map_err(|_| AnnounceError::SigningError)?;
94
95 let mut announce_data = Vec::new();
97 announce_data.extend_from_slice(&public_key);
98 announce_data.extend_from_slice(name_hash);
99 announce_data.extend_from_slice(random_hash);
100 if let Some(r) = ratchet {
101 announce_data.extend_from_slice(r);
102 }
103 announce_data.extend_from_slice(&signature);
104 if let Some(ad) = app_data {
105 announce_data.extend_from_slice(ad);
106 }
107
108 Ok((announce_data, has_ratchet))
109 }
110
111 pub fn unpack(data: &[u8], has_ratchet: bool) -> Result<Self, AnnounceError> {
115 let keysize = constants::KEYSIZE / 8; let name_hash_len = constants::NAME_HASH_LENGTH / 8; let sig_len = constants::SIGLENGTH / 8; let ratchet_size = constants::RATCHETSIZE / 8; let min_len = if has_ratchet {
121 keysize + name_hash_len + 10 + ratchet_size + sig_len
122 } else {
123 keysize + name_hash_len + 10 + sig_len
124 };
125
126 if data.len() < min_len {
127 return Err(AnnounceError::DataTooShort);
128 }
129
130 let mut public_key = [0u8; 64];
131 public_key.copy_from_slice(&data[..keysize]);
132
133 let mut name_hash = [0u8; 10];
134 name_hash.copy_from_slice(&data[keysize..keysize + name_hash_len]);
135
136 let mut random_hash = [0u8; 10];
137 random_hash.copy_from_slice(&data[keysize + name_hash_len..keysize + name_hash_len + 10]);
138
139 let (ratchet, sig_start) = if has_ratchet {
140 let mut ratchet = [0u8; 32];
141 ratchet.copy_from_slice(
142 &data[keysize + name_hash_len + 10..keysize + name_hash_len + 10 + ratchet_size],
143 );
144 (Some(ratchet), keysize + name_hash_len + 10 + ratchet_size)
145 } else {
146 (None, keysize + name_hash_len + 10)
147 };
148
149 let mut signature = [0u8; 64];
150 signature.copy_from_slice(&data[sig_start..sig_start + sig_len]);
151
152 let app_data_start = sig_start + sig_len;
155 let app_data = if data.len() > app_data_start {
156 Some(data[app_data_start..].to_vec())
157 } else {
158 None
159 };
160
161 Ok(AnnounceData {
162 public_key,
163 name_hash,
164 random_hash,
165 ratchet,
166 signature,
167 app_data,
168 })
169 }
170
171 pub fn validate(
181 &self,
182 destination_hash: &[u8; 16],
183 ) -> Result<ValidatedAnnounce, AnnounceError> {
184 let announced_identity = Identity::from_public_key(&self.public_key);
186
187 let mut signed_data = Vec::new();
189 signed_data.extend_from_slice(destination_hash);
190 signed_data.extend_from_slice(&self.public_key);
191 signed_data.extend_from_slice(&self.name_hash);
192 signed_data.extend_from_slice(&self.random_hash);
193
194 if let Some(ref ratchet) = self.ratchet {
195 signed_data.extend_from_slice(ratchet);
196 }
197
198 if let Some(ref ad) = self.app_data {
201 signed_data.extend_from_slice(ad);
202 }
203
204 if !announced_identity.verify(&self.signature, &signed_data) {
206 return Err(AnnounceError::InvalidSignature);
207 }
208
209 let identity_hash = *announced_identity.hash();
211
212 let mut hash_material = Vec::new();
213 hash_material.extend_from_slice(&self.name_hash);
214 hash_material.extend_from_slice(&identity_hash);
215
216 let expected_hash = hash::truncated_hash(&hash_material);
217
218 if &expected_hash != destination_hash {
219 return Err(AnnounceError::DestinationMismatch);
220 }
221
222 Ok(ValidatedAnnounce {
223 identity_hash,
224 public_key: self.public_key,
225 name_hash: self.name_hash,
226 random_hash: self.random_hash,
227 ratchet: self.ratchet,
228 app_data: self.app_data.clone(),
229 })
230 }
231
232 pub fn to_validated_unchecked(&self) -> ValidatedAnnounce {
237 let identity_hash = hash::truncated_hash(&self.public_key);
238 ValidatedAnnounce {
239 identity_hash,
240 public_key: self.public_key,
241 name_hash: self.name_hash,
242 random_hash: self.random_hash,
243 ratchet: self.ratchet,
244 app_data: self.app_data.clone(),
245 }
246 }
247}
248
249#[cfg(test)]
250mod tests {
251 use super::*;
252 use crate::destination;
253
254 #[test]
255 fn test_pack_unpack_roundtrip_no_ratchet() {
256 let identity = Identity::from_private_key(&[0x42; 64]);
257 let id_hash = *identity.hash();
258
259 let nh = destination::name_hash("testapp", &["aspect"]);
260 let dh = destination::destination_hash("testapp", &["aspect"], Some(&id_hash));
261 let random = [0xAA; 10];
262
263 let (data, has_ratchet) =
264 AnnounceData::pack(&identity, &dh, &nh, &random, None, None).unwrap();
265 assert!(!has_ratchet);
266
267 let parsed = AnnounceData::unpack(&data, false).unwrap();
268 assert_eq!(parsed.public_key, identity.get_public_key().unwrap());
269 assert_eq!(parsed.name_hash, nh);
270 assert_eq!(parsed.random_hash, random);
271 assert!(parsed.ratchet.is_none());
272 assert!(parsed.app_data.is_none());
273 }
274
275 #[test]
276 fn test_pack_unpack_roundtrip_with_ratchet() {
277 let identity = Identity::from_private_key(&[0x42; 64]);
278 let id_hash = *identity.hash();
279
280 let nh = destination::name_hash("testapp", &["aspect"]);
281 let dh = destination::destination_hash("testapp", &["aspect"], Some(&id_hash));
282 let random = [0xBB; 10];
283 let ratchet = [0xCC; 32];
284
285 let (data, has_ratchet) =
286 AnnounceData::pack(&identity, &dh, &nh, &random, Some(&ratchet), None).unwrap();
287 assert!(has_ratchet);
288
289 let parsed = AnnounceData::unpack(&data, true).unwrap();
290 assert_eq!(parsed.ratchet.unwrap(), ratchet);
291 }
292
293 #[test]
294 fn test_pack_unpack_with_app_data() {
295 let identity = Identity::from_private_key(&[0x42; 64]);
296 let id_hash = *identity.hash();
297
298 let nh = destination::name_hash("testapp", &["aspect"]);
299 let dh = destination::destination_hash("testapp", &["aspect"], Some(&id_hash));
300 let random = [0xDD; 10];
301 let app_data = b"hello app data";
302
303 let (data, _) =
304 AnnounceData::pack(&identity, &dh, &nh, &random, None, Some(app_data)).unwrap();
305
306 let parsed = AnnounceData::unpack(&data, false).unwrap();
307 assert_eq!(parsed.app_data.as_deref(), Some(app_data.as_slice()));
308 }
309
310 #[test]
311 fn test_validate_valid_announce() {
312 let identity = Identity::from_private_key(&[0x42; 64]);
313 let id_hash = *identity.hash();
314
315 let nh = destination::name_hash("testapp", &["aspect"]);
316 let dh = destination::destination_hash("testapp", &["aspect"], Some(&id_hash));
317 let random = [0xEE; 10];
318
319 let (data, _) =
320 AnnounceData::pack(&identity, &dh, &nh, &random, None, Some(b"data")).unwrap();
321
322 let parsed = AnnounceData::unpack(&data, false).unwrap();
323 let validated = parsed.validate(&dh).unwrap();
324
325 assert_eq!(validated.identity_hash, id_hash);
326 assert_eq!(validated.name_hash, nh);
327 }
328
329 #[test]
330 fn test_validate_tampered_signature() {
331 let identity = Identity::from_private_key(&[0x42; 64]);
332 let id_hash = *identity.hash();
333
334 let nh = destination::name_hash("testapp", &["aspect"]);
335 let dh = destination::destination_hash("testapp", &["aspect"], Some(&id_hash));
336 let random = [0xFF; 10];
337
338 let (mut data, _) = AnnounceData::pack(&identity, &dh, &nh, &random, None, None).unwrap();
339
340 data[84] ^= 0xFF;
342
343 let parsed = AnnounceData::unpack(&data, false).unwrap();
344 assert!(parsed.validate(&dh).is_err());
345 }
346
347 #[test]
348 fn test_validate_wrong_destination_hash() {
349 let identity = Identity::from_private_key(&[0x42; 64]);
350 let id_hash = *identity.hash();
351
352 let nh = destination::name_hash("testapp", &["aspect"]);
353 let dh = destination::destination_hash("testapp", &["aspect"], Some(&id_hash));
354 let random = [0x11; 10];
355
356 let (data, _) = AnnounceData::pack(&identity, &dh, &nh, &random, None, None).unwrap();
357
358 let parsed = AnnounceData::unpack(&data, false).unwrap();
359 let wrong_hash = [0x00; 16];
360 assert!(parsed.validate(&wrong_hash).is_err());
361 }
362
363 #[test]
364 fn test_to_validated_unchecked_matches_validate() {
365 let identity = Identity::from_private_key(&[0x42; 64]);
366 let id_hash = *identity.hash();
367
368 let nh = destination::name_hash("testapp", &["aspect"]);
369 let dh = destination::destination_hash("testapp", &["aspect"], Some(&id_hash));
370 let random = [0xAB; 10];
371
372 let (data, _) =
373 AnnounceData::pack(&identity, &dh, &nh, &random, None, Some(b"appdata")).unwrap();
374
375 let parsed = AnnounceData::unpack(&data, false).unwrap();
376 let validated = parsed.validate(&dh).unwrap();
377 let unchecked = parsed.to_validated_unchecked();
378
379 assert_eq!(validated.identity_hash, unchecked.identity_hash);
380 assert_eq!(validated.public_key, unchecked.public_key);
381 assert_eq!(validated.name_hash, unchecked.name_hash);
382 assert_eq!(validated.random_hash, unchecked.random_hash);
383 assert_eq!(validated.ratchet, unchecked.ratchet);
384 assert_eq!(validated.app_data, unchecked.app_data);
385 }
386}