libsignal_protocol/
fingerprint.rs

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