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, "{:05}", n).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 {}",
165                iterations
166            )));
167        }
168
169        let fingerprint_version = [0u8, 0u8]; // 0x0000
170        let key_bytes = local_key.serialize();
171
172        let mut sha512 = Sha512::new();
173
174        // iteration=0
175        // Explicitly pass a slice to avoid generating multiple versions of update().
176        sha512.update(&fingerprint_version[..]);
177        sha512.update(&key_bytes);
178        sha512.update(local_id);
179        sha512.update(&key_bytes);
180        let mut buf = sha512.finalize();
181
182        for _i in 1..iterations {
183            let mut sha512 = Sha512::new();
184            // Explicitly pass a slice to avoid generating multiple versions of update().
185            sha512.update(&buf[..]);
186            sha512.update(&key_bytes);
187            buf = sha512.finalize();
188        }
189
190        Ok(buf.to_vec())
191    }
192
193    pub fn new(
194        version: u32,
195        iterations: u32,
196        local_id: &[u8],
197        local_key: &IdentityKey,
198        remote_id: &[u8],
199        remote_key: &IdentityKey,
200    ) -> Result<Fingerprint> {
201        let local_fingerprint = Fingerprint::get_fingerprint(iterations, local_id, local_key)?;
202        let remote_fingerprint = Fingerprint::get_fingerprint(iterations, remote_id, remote_key)?;
203
204        Ok(Fingerprint {
205            display: DisplayableFingerprint::new(&local_fingerprint, &remote_fingerprint)?,
206            scannable: ScannableFingerprint::new(version, &local_fingerprint, &remote_fingerprint),
207        })
208    }
209
210    pub fn display_string(&self) -> Result<String> {
211        Ok(format!("{}", self.display))
212    }
213}
214
215#[cfg(test)]
216mod test {
217    use hex_literal::hex;
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);
366        let b_key_pair = IdentityKeyPair::generate(&mut OsRng);
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 a_key_pair = IdentityKeyPair::generate(&mut OsRng);
423        let b_key_pair = IdentityKeyPair::generate(&mut OsRng);
424        let m_key_pair = IdentityKeyPair::generate(&mut OsRng); // mitm
425
426        let a_key = a_key_pair.identity_key();
427        let b_key = b_key_pair.identity_key();
428        let m_key = m_key_pair.identity_key();
429
430        let version = 1;
431        let iterations = 1024;
432
433        let a_fprint = Fingerprint::new(
434            version,
435            iterations,
436            ALICE_STABLE_ID.as_bytes(),
437            a_key,
438            BOB_STABLE_ID.as_bytes(),
439            m_key,
440        )?;
441
442        let b_fprint = Fingerprint::new(
443            version,
444            iterations,
445            BOB_STABLE_ID.as_bytes(),
446            b_key,
447            ALICE_STABLE_ID.as_bytes(),
448            a_key,
449        )?;
450
451        assert_ne!(
452            format!("{}", a_fprint.display),
453            format!("{}", b_fprint.display)
454        );
455
456        assert!(!a_fprint
457            .scannable
458            .compare(&b_fprint.scannable.serialize()?)?);
459        assert!(!b_fprint
460            .scannable
461            .compare(&a_fprint.scannable.serialize()?)?);
462
463        Ok(())
464    }
465
466    #[test]
467    fn fingerprint_mismatching_identifiers() -> Result<()> {
468        use rand::rngs::OsRng;
469
470        use crate::IdentityKeyPair;
471
472        let a_key_pair = IdentityKeyPair::generate(&mut OsRng);
473        let b_key_pair = IdentityKeyPair::generate(&mut OsRng);
474
475        let a_key = a_key_pair.identity_key();
476        let b_key = b_key_pair.identity_key();
477
478        let version = 1;
479        let iterations = 1024;
480
481        let a_fprint = Fingerprint::new(
482            version,
483            iterations,
484            "+141512222222".as_bytes(),
485            a_key,
486            BOB_STABLE_ID.as_bytes(),
487            b_key,
488        )?;
489
490        let b_fprint = Fingerprint::new(
491            version,
492            iterations,
493            BOB_STABLE_ID.as_bytes(),
494            b_key,
495            ALICE_STABLE_ID.as_bytes(),
496            a_key,
497        )?;
498
499        assert_ne!(
500            format!("{}", a_fprint.display),
501            format!("{}", b_fprint.display)
502        );
503
504        assert!(!a_fprint
505            .scannable
506            .compare(&b_fprint.scannable.serialize()?)?);
507        assert!(!b_fprint
508            .scannable
509            .compare(&a_fprint.scannable.serialize()?)?);
510
511        Ok(())
512    }
513
514    #[test]
515    fn fingerprint_mismatching_versions() -> Result<()> {
516        let a_key = IdentityKey::decode(ALICE_IDENTITY)?;
517        let b_key = IdentityKey::decode(BOB_IDENTITY)?;
518
519        let iterations = 5200;
520
521        let a_fprint_v1 = Fingerprint::new(
522            1,
523            iterations,
524            ALICE_STABLE_ID.as_bytes(),
525            &a_key,
526            BOB_STABLE_ID.as_bytes(),
527            &b_key,
528        )?;
529
530        let a_fprint_v2 = Fingerprint::new(
531            2,
532            iterations,
533            BOB_STABLE_ID.as_bytes(),
534            &b_key,
535            ALICE_STABLE_ID.as_bytes(),
536            &a_key,
537        )?;
538
539        // Display fingerprint doesn't change
540        assert_eq!(
541            format!("{}", a_fprint_v1.display),
542            format!("{}", a_fprint_v2.display)
543        );
544
545        // Scannable fingerprint does
546        assert_ne!(
547            hex::encode(a_fprint_v1.scannable.serialize()?),
548            hex::encode(a_fprint_v2.scannable.serialize()?)
549        );
550
551        Ok(())
552    }
553}