1use std::fmt;
7use std::fmt::Write;
8
9use prost::Message;
10use sha2::Sha512;
11use sha2::digest::Digest;
12use subtle::ConstantTimeEq;
13
14use crate::{IdentityKey, proto};
15
16#[derive(Debug, displaydoc::Display)]
17pub enum Error {
18 VersionMismatch { theirs: u32, ours: u32 },
20 ParsingError(&'static str),
22 InvalidIterationCount(u32),
24}
25
26#[derive(Debug, Clone)]
27pub struct DisplayableFingerprint {
28 local: String,
29 remote: String,
30}
31
32impl fmt::Display for DisplayableFingerprint {
33 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
34 if self.local < self.remote {
35 write!(f, "{}{}", self.local, self.remote)
36 } else {
37 write!(f, "{}{}", self.remote, self.local)
38 }
39 }
40}
41
42fn get_encoded_string(fprint: &[u8]) -> Result<String, Error> {
43 if fprint.len() < 30 {
44 return Err(Error::ParsingError(
45 "DisplayableFingerprint created with short encoding",
46 ));
47 }
48
49 fn read5_mod_100k(fprint: &[u8]) -> u64 {
50 assert_eq!(fprint.len(), 5);
51 let x = fprint.iter().fold(0u64, |acc, &x| (acc << 8) | (x as u64));
52 x % 100_000
53 }
54
55 let s = fprint.chunks_exact(5).take(6).map(read5_mod_100k).fold(
56 String::with_capacity(5 * 6),
57 |mut s, n| {
58 write!(s, "{n:05}").expect("can always write to a String");
59 s
60 },
61 );
62
63 Ok(s)
64}
65
66impl DisplayableFingerprint {
67 pub fn new(local: &[u8], remote: &[u8]) -> Result<Self, Error> {
68 Ok(Self {
69 local: get_encoded_string(local)?,
70 remote: get_encoded_string(remote)?,
71 })
72 }
73}
74
75#[derive(Debug, Clone)]
76pub struct ScannableFingerprint {
77 version: u32,
78 local_fingerprint: Vec<u8>,
79 remote_fingerprint: Vec<u8>,
80}
81
82impl ScannableFingerprint {
83 fn new(version: u32, local_fprint: &[u8], remote_fprint: &[u8]) -> Self {
84 Self {
85 version,
86 local_fingerprint: local_fprint[..32].to_vec(),
87 remote_fingerprint: remote_fprint[..32].to_vec(),
88 }
89 }
90
91 pub fn deserialize(protobuf: &[u8]) -> Result<Self, Error> {
92 let fingerprint = proto::fingerprint::CombinedFingerprints::decode(protobuf)
93 .map_err(|_| Error::ParsingError("failed to decode protobuf"))?;
94
95 Ok(Self {
96 version: fingerprint
97 .version
98 .ok_or(Error::ParsingError("missing version"))?,
99 local_fingerprint: fingerprint
100 .local_fingerprint
101 .and_then(|m| m.content)
102 .ok_or(Error::ParsingError("missing local fingerprint"))?,
103 remote_fingerprint: fingerprint
104 .remote_fingerprint
105 .and_then(|m| m.content)
106 .ok_or(Error::ParsingError("missing remote fingerprint"))?,
107 })
108 }
109
110 pub fn serialize(&self) -> Result<Vec<u8>, Error> {
111 let combined_fingerprints = proto::fingerprint::CombinedFingerprints {
112 version: Some(self.version),
113 local_fingerprint: Some(proto::fingerprint::LogicalFingerprint {
114 content: Some(self.local_fingerprint.to_owned()),
115 }),
116 remote_fingerprint: Some(proto::fingerprint::LogicalFingerprint {
117 content: Some(self.remote_fingerprint.to_owned()),
118 }),
119 };
120
121 Ok(combined_fingerprints.encode_to_vec())
122 }
123
124 pub fn compare(&self, combined: &[u8]) -> Result<bool, Error> {
125 let combined = proto::fingerprint::CombinedFingerprints::decode(combined)
126 .map_err(|_| Error::ParsingError("failed to decode their protobuf"))?;
127
128 let their_version = combined.version.unwrap_or(0);
129
130 if their_version != self.version {
131 return Err(Error::VersionMismatch {
132 theirs: their_version,
133 ours: self.version,
134 });
135 }
136
137 let same1 = combined
138 .local_fingerprint
139 .as_ref()
140 .and_then(|m| m.content.as_ref())
141 .ok_or(Error::ParsingError("missing their local fingerprint"))?
142 .ct_eq(&self.remote_fingerprint);
143 let same2 = combined
144 .remote_fingerprint
145 .as_ref()
146 .and_then(|m| m.content.as_ref())
147 .ok_or(Error::ParsingError("missing their remote fingerprint"))?
148 .ct_eq(&self.local_fingerprint);
149
150 Ok(same1.into() && same2.into())
151 }
152}
153
154#[derive(Debug, Clone)]
155pub struct Fingerprint {
156 pub display: DisplayableFingerprint,
157 pub scannable: ScannableFingerprint,
158}
159
160impl Fingerprint {
161 fn get_fingerprint(
162 iterations: u32,
163 local_id: &[u8],
164 local_key: &IdentityKey,
165 ) -> Result<Vec<u8>, Error> {
166 if iterations <= 1 || iterations > 1000000 {
167 return Err(Error::InvalidIterationCount(iterations));
168 }
169
170 let fingerprint_version = [0u8, 0u8]; let key_bytes = local_key.serialize();
172
173 let mut sha512 = Sha512::new();
174
175 sha512.update(&fingerprint_version[..]);
178 sha512.update(&key_bytes);
179 sha512.update(local_id);
180 sha512.update(&key_bytes);
181 let mut buf = sha512.finalize();
182
183 for _i in 1..iterations {
184 let mut sha512 = Sha512::new();
185 sha512.update(&buf[..]);
187 sha512.update(&key_bytes);
188 buf = sha512.finalize();
189 }
190
191 Ok(buf.to_vec())
192 }
193
194 pub fn new(
195 version: u32,
196 iterations: u32,
197 local_id: &[u8],
198 local_key: &IdentityKey,
199 remote_id: &[u8],
200 remote_key: &IdentityKey,
201 ) -> Result<Fingerprint, Error> {
202 let local_fingerprint = Fingerprint::get_fingerprint(iterations, local_id, local_key)?;
203 let remote_fingerprint = Fingerprint::get_fingerprint(iterations, remote_id, remote_key)?;
204
205 Ok(Fingerprint {
206 display: DisplayableFingerprint::new(&local_fingerprint, &remote_fingerprint)?,
207 scannable: ScannableFingerprint::new(version, &local_fingerprint, &remote_fingerprint),
208 })
209 }
210
211 pub fn display_string(&self) -> Result<String, Error> {
212 Ok(self.display.to_string())
213 }
214}
215
216#[cfg(test)]
217mod test {
218 use const_str::hex;
219 use rand::TryRngCore as _;
220
221 use super::*;
222
223 const ALICE_IDENTITY: &[u8] =
224 &hex!("0506863bc66d02b40d27b8d49ca7c09e9239236f9d7d25d6fcca5ce13c7064d868");
225 const BOB_IDENTITY: &[u8] =
226 &hex!("05f781b6fb32fed9ba1cf2de978d4d5da28dc34046ae814402b5c0dbd96fda907b");
227
228 const DISPLAYABLE_FINGERPRINT_V1: &str =
229 "300354477692869396892869876765458257569162576843440918079131";
230 const ALICE_SCANNABLE_FINGERPRINT_V1: &str = "080112220a201e301a0353dce3dbe7684cb8336e85136cdc0ee96219494ada305d62a7bd61df1a220a20d62cbf73a11592015b6b9f1682ac306fea3aaf3885b84d12bca631e9d4fb3a4d";
231 const BOB_SCANNABLE_FINGERPRINT_V1: &str = "080112220a20d62cbf73a11592015b6b9f1682ac306fea3aaf3885b84d12bca631e9d4fb3a4d1a220a201e301a0353dce3dbe7684cb8336e85136cdc0ee96219494ada305d62a7bd61df";
232
233 const ALICE_SCANNABLE_FINGERPRINT_V2: &str = "080212220a201e301a0353dce3dbe7684cb8336e85136cdc0ee96219494ada305d62a7bd61df1a220a20d62cbf73a11592015b6b9f1682ac306fea3aaf3885b84d12bca631e9d4fb3a4d";
234 const BOB_SCANNABLE_FINGERPRINT_V2: &str = "080212220a20d62cbf73a11592015b6b9f1682ac306fea3aaf3885b84d12bca631e9d4fb3a4d1a220a201e301a0353dce3dbe7684cb8336e85136cdc0ee96219494ada305d62a7bd61df";
235
236 const ALICE_STABLE_ID: &str = "+14152222222";
237 const BOB_STABLE_ID: &str = "+14153333333";
238
239 #[test]
240 fn fingerprint_encodings() -> Result<(), Error> {
241 let l = vec![0x12; 32];
242 let r = vec![0xBA; 32];
243
244 let fprint2 = ScannableFingerprint::new(2, &l, &r);
245 let proto2 = fprint2.serialize()?;
246
247 let expected2_encoding =
248 "080212220a20".to_owned() + &"12".repeat(32) + "1a220a20" + &"ba".repeat(32);
249 assert_eq!(hex::encode(proto2), expected2_encoding);
250
251 Ok(())
252 }
253
254 #[test]
255 fn fingerprint_test_v1() -> Result<(), Error> {
256 let a_key = IdentityKey::decode(ALICE_IDENTITY).expect("valid");
259 let b_key = IdentityKey::decode(BOB_IDENTITY).expect("valid");
260
261 let version = 1;
262 let iterations = 5200;
263
264 let a_fprint = Fingerprint::new(
265 version,
266 iterations,
267 ALICE_STABLE_ID.as_bytes(),
268 &a_key,
269 BOB_STABLE_ID.as_bytes(),
270 &b_key,
271 )?;
272
273 let b_fprint = Fingerprint::new(
274 version,
275 iterations,
276 BOB_STABLE_ID.as_bytes(),
277 &b_key,
278 ALICE_STABLE_ID.as_bytes(),
279 &a_key,
280 )?;
281
282 assert_eq!(
283 hex::encode(a_fprint.scannable.serialize()?),
284 ALICE_SCANNABLE_FINGERPRINT_V1
285 );
286 assert_eq!(
287 hex::encode(b_fprint.scannable.serialize()?),
288 BOB_SCANNABLE_FINGERPRINT_V1
289 );
290
291 assert_eq!(format!("{}", a_fprint.display), DISPLAYABLE_FINGERPRINT_V1);
292 assert_eq!(format!("{}", b_fprint.display), DISPLAYABLE_FINGERPRINT_V1);
293
294 assert_eq!(
295 hex::encode(a_fprint.scannable.serialize()?),
296 ALICE_SCANNABLE_FINGERPRINT_V1
297 );
298 assert_eq!(
299 hex::encode(b_fprint.scannable.serialize()?),
300 BOB_SCANNABLE_FINGERPRINT_V1
301 );
302
303 Ok(())
304 }
305
306 #[test]
307 fn fingerprint_test_v2() -> Result<(), Error> {
308 let a_key = IdentityKey::decode(ALICE_IDENTITY).expect("valid");
311 let b_key = IdentityKey::decode(BOB_IDENTITY).expect("valid");
312
313 let version = 2;
314 let iterations = 5200;
315
316 let a_fprint = Fingerprint::new(
317 version,
318 iterations,
319 ALICE_STABLE_ID.as_bytes(),
320 &a_key,
321 BOB_STABLE_ID.as_bytes(),
322 &b_key,
323 )?;
324
325 let b_fprint = Fingerprint::new(
326 version,
327 iterations,
328 BOB_STABLE_ID.as_bytes(),
329 &b_key,
330 ALICE_STABLE_ID.as_bytes(),
331 &a_key,
332 )?;
333
334 assert_eq!(
335 hex::encode(a_fprint.scannable.serialize()?),
336 ALICE_SCANNABLE_FINGERPRINT_V2
337 );
338 assert_eq!(
339 hex::encode(b_fprint.scannable.serialize()?),
340 BOB_SCANNABLE_FINGERPRINT_V2
341 );
342
343 assert_eq!(format!("{}", a_fprint.display), DISPLAYABLE_FINGERPRINT_V1);
345 assert_eq!(format!("{}", b_fprint.display), DISPLAYABLE_FINGERPRINT_V1);
346
347 assert_eq!(
348 hex::encode(a_fprint.scannable.serialize()?),
349 ALICE_SCANNABLE_FINGERPRINT_V2
350 );
351 assert_eq!(
352 hex::encode(b_fprint.scannable.serialize()?),
353 BOB_SCANNABLE_FINGERPRINT_V2
354 );
355
356 Ok(())
357 }
358
359 #[test]
360 fn fingerprint_matching_identifiers() -> Result<(), Error> {
361 use rand::rngs::OsRng;
364
365 use crate::IdentityKeyPair;
366
367 let a_key_pair = IdentityKeyPair::generate(&mut OsRng.unwrap_err());
368 let b_key_pair = IdentityKeyPair::generate(&mut OsRng.unwrap_err());
369
370 let a_key = a_key_pair.identity_key();
371 let b_key = b_key_pair.identity_key();
372
373 let version = 1;
374 let iterations = 1024;
375
376 let a_fprint = Fingerprint::new(
377 version,
378 iterations,
379 ALICE_STABLE_ID.as_bytes(),
380 a_key,
381 BOB_STABLE_ID.as_bytes(),
382 b_key,
383 )?;
384
385 let b_fprint = Fingerprint::new(
386 version,
387 iterations,
388 BOB_STABLE_ID.as_bytes(),
389 b_key,
390 ALICE_STABLE_ID.as_bytes(),
391 a_key,
392 )?;
393
394 assert_eq!(
395 format!("{}", a_fprint.display),
396 format!("{}", b_fprint.display)
397 );
398 assert_eq!(format!("{}", a_fprint.display).len(), 60);
399
400 assert!(
401 a_fprint
402 .scannable
403 .compare(&b_fprint.scannable.serialize()?)?
404 );
405 assert!(
406 b_fprint
407 .scannable
408 .compare(&a_fprint.scannable.serialize()?)?
409 );
410
411 assert!(
413 !a_fprint
414 .scannable
415 .compare(&a_fprint.scannable.serialize()?)?
416 );
417 assert!(
418 !b_fprint
419 .scannable
420 .compare(&b_fprint.scannable.serialize()?)?
421 );
422
423 Ok(())
424 }
425
426 #[test]
427 fn fingerprint_mismatching_fingerprints() -> Result<(), Error> {
428 use rand::rngs::OsRng;
429
430 use crate::IdentityKeyPair;
431
432 let mut rng = OsRng.unwrap_err();
433 let a_key_pair = IdentityKeyPair::generate(&mut rng);
434 let b_key_pair = IdentityKeyPair::generate(&mut rng);
435 let m_key_pair = IdentityKeyPair::generate(&mut rng); let a_key = a_key_pair.identity_key();
438 let b_key = b_key_pair.identity_key();
439 let m_key = m_key_pair.identity_key();
440
441 let version = 1;
442 let iterations = 1024;
443
444 let a_fprint = Fingerprint::new(
445 version,
446 iterations,
447 ALICE_STABLE_ID.as_bytes(),
448 a_key,
449 BOB_STABLE_ID.as_bytes(),
450 m_key,
451 )?;
452
453 let b_fprint = Fingerprint::new(
454 version,
455 iterations,
456 BOB_STABLE_ID.as_bytes(),
457 b_key,
458 ALICE_STABLE_ID.as_bytes(),
459 a_key,
460 )?;
461
462 assert_ne!(
463 format!("{}", a_fprint.display),
464 format!("{}", b_fprint.display)
465 );
466
467 assert!(
468 !a_fprint
469 .scannable
470 .compare(&b_fprint.scannable.serialize()?)?
471 );
472 assert!(
473 !b_fprint
474 .scannable
475 .compare(&a_fprint.scannable.serialize()?)?
476 );
477
478 Ok(())
479 }
480
481 #[test]
482 fn fingerprint_mismatching_identifiers() -> Result<(), Error> {
483 use rand::rngs::OsRng;
484
485 use crate::IdentityKeyPair;
486
487 let mut rng = OsRng.unwrap_err();
488 let a_key_pair = IdentityKeyPair::generate(&mut rng);
489 let b_key_pair = IdentityKeyPair::generate(&mut rng);
490
491 let a_key = a_key_pair.identity_key();
492 let b_key = b_key_pair.identity_key();
493
494 let version = 1;
495 let iterations = 1024;
496
497 let a_fprint = Fingerprint::new(
498 version,
499 iterations,
500 "+141512222222".as_bytes(),
501 a_key,
502 BOB_STABLE_ID.as_bytes(),
503 b_key,
504 )?;
505
506 let b_fprint = Fingerprint::new(
507 version,
508 iterations,
509 BOB_STABLE_ID.as_bytes(),
510 b_key,
511 ALICE_STABLE_ID.as_bytes(),
512 a_key,
513 )?;
514
515 assert_ne!(
516 format!("{}", a_fprint.display),
517 format!("{}", b_fprint.display)
518 );
519
520 assert!(
521 !a_fprint
522 .scannable
523 .compare(&b_fprint.scannable.serialize()?)?
524 );
525 assert!(
526 !b_fprint
527 .scannable
528 .compare(&a_fprint.scannable.serialize()?)?
529 );
530
531 Ok(())
532 }
533
534 #[test]
535 fn fingerprint_mismatching_versions() -> Result<(), Error> {
536 let a_key = IdentityKey::decode(ALICE_IDENTITY).expect("valid");
537 let b_key = IdentityKey::decode(BOB_IDENTITY).expect("valid");
538
539 let iterations = 5200;
540
541 let a_fprint_v1 = Fingerprint::new(
542 1,
543 iterations,
544 ALICE_STABLE_ID.as_bytes(),
545 &a_key,
546 BOB_STABLE_ID.as_bytes(),
547 &b_key,
548 )?;
549
550 let a_fprint_v2 = Fingerprint::new(
551 2,
552 iterations,
553 BOB_STABLE_ID.as_bytes(),
554 &b_key,
555 ALICE_STABLE_ID.as_bytes(),
556 &a_key,
557 )?;
558
559 assert_eq!(
561 format!("{}", a_fprint_v1.display),
562 format!("{}", a_fprint_v2.display)
563 );
564
565 assert_ne!(
567 hex::encode(a_fprint_v1.scannable.serialize()?),
568 hex::encode(a_fprint_v2.scannable.serialize()?)
569 );
570
571 Ok(())
572 }
573}