deltachat/
key.rs

1//! Cryptographic key module.
2
3use std::collections::BTreeMap;
4use std::fmt;
5use std::io::Cursor;
6
7use anyhow::{Context as _, Result, bail, ensure};
8use base64::Engine as _;
9use deltachat_contact_tools::EmailAddress;
10use pgp::composed::Deserializable;
11pub use pgp::composed::{SignedPublicKey, SignedSecretKey};
12use pgp::ser::Serialize;
13use pgp::types::{KeyDetails, KeyId, Password};
14use tokio::runtime::Handle;
15
16use crate::context::Context;
17use crate::events::EventType;
18use crate::log::LogExt;
19use crate::pgp::KeyPair;
20use crate::tools::{self, time_elapsed};
21
22/// Convenience trait for working with keys.
23///
24/// This trait is implemented for rPGP's [SignedPublicKey] and
25/// [SignedSecretKey] types and makes working with them a little
26/// easier in the deltachat world.
27pub trait DcKey: Serialize + Deserializable + Clone {
28    /// Create a key from some bytes.
29    fn from_slice(bytes: &[u8]) -> Result<Self> {
30        let res = <Self as Deserializable>::from_bytes(Cursor::new(bytes));
31        if let Ok(res) = res {
32            return Ok(res);
33        }
34
35        // Workaround for keys imported using
36        // Delta Chat core < 1.0.0.
37        // Old Delta Chat core had a bug
38        // that resulted in treating CRC24 checksum
39        // as part of the key when reading ASCII Armor.
40        // Some users that started using Delta Chat in 2019
41        // have such corrupted keys with garbage bytes at the end.
42        //
43        // Garbage is at least 3 bytes long
44        // and may be longer due to padding
45        // at the end of the real key data
46        // and importing the key multiple times.
47        //
48        // If removing 10 bytes is not enough,
49        // the key is likely actually corrupted.
50        for garbage_bytes in 3..std::cmp::min(bytes.len(), 10) {
51            let res = <Self as Deserializable>::from_bytes(Cursor::new(
52                bytes
53                    .get(..bytes.len().saturating_sub(garbage_bytes))
54                    .unwrap_or_default(),
55            ));
56            if let Ok(res) = res {
57                return Ok(res);
58            }
59        }
60
61        // Removing garbage bytes did not help, return the error.
62        Ok(res?)
63    }
64
65    /// Create a key from a base64 string.
66    fn from_base64(data: &str) -> Result<Self> {
67        // strip newlines and other whitespace
68        let cleaned: String = data.split_whitespace().collect();
69        let bytes = base64::engine::general_purpose::STANDARD.decode(cleaned.as_bytes())?;
70        Self::from_slice(&bytes)
71    }
72
73    /// Create a key from an ASCII-armored string.
74    fn from_asc(data: &str) -> Result<Self> {
75        let bytes = data.as_bytes();
76        let res = Self::from_armor_single(Cursor::new(bytes));
77        let (key, _headers) = match res {
78            Err(pgp::errors::Error::NoMatchingPacket { .. }) => match Self::is_private() {
79                true => bail!("No private key packet found"),
80                false => bail!("No public key packet found"),
81            },
82            _ => res.context("rPGP error")?,
83        };
84        Ok(key)
85    }
86
87    /// Serialise the key as bytes.
88    fn to_bytes(&self) -> Vec<u8> {
89        // Not using Serialize::to_bytes() to make clear *why* it is
90        // safe to ignore this error.
91        // Because we write to a Vec<u8> the io::Write impls never
92        // fail and we can hide this error.
93        let mut buf = Vec::new();
94        self.to_writer(&mut buf).unwrap();
95        buf
96    }
97
98    /// Serialise the key to a base64 string.
99    fn to_base64(&self) -> String {
100        base64::engine::general_purpose::STANDARD.encode(DcKey::to_bytes(self))
101    }
102
103    /// Serialise the key to ASCII-armored representation.
104    ///
105    /// Each header line must be terminated by `\r\n`.  Only allows setting one
106    /// header as a simplification since that's the only way it's used so far.
107    // Since .to_armored_string() are actual methods on SignedPublicKey and
108    // SignedSecretKey we can not generically implement this.
109    fn to_asc(&self, header: Option<(&str, &str)>) -> String;
110
111    /// The fingerprint for the key.
112    fn dc_fingerprint(&self) -> Fingerprint;
113
114    /// Whether the key is private (or public).
115    fn is_private() -> bool;
116
117    /// Returns the OpenPGP Key ID.
118    fn key_id(&self) -> KeyId;
119}
120
121/// Attempts to load own public key.
122///
123/// Returns `None` if no key is generated yet.
124pub(crate) async fn load_self_public_key_opt(context: &Context) -> Result<Option<SignedPublicKey>> {
125    let Some(public_key_bytes) = context
126        .sql
127        .query_row_optional(
128            "SELECT public_key
129             FROM keypairs
130             WHERE id=(SELECT value FROM config WHERE keyname='key_id')",
131            (),
132            |row| {
133                let bytes: Vec<u8> = row.get(0)?;
134                Ok(bytes)
135            },
136        )
137        .await?
138    else {
139        return Ok(None);
140    };
141    let public_key = SignedPublicKey::from_slice(&public_key_bytes)?;
142    Ok(Some(public_key))
143}
144
145/// Loads own public key.
146///
147/// If no key is generated yet, generates a new one.
148pub(crate) async fn load_self_public_key(context: &Context) -> Result<SignedPublicKey> {
149    match load_self_public_key_opt(context).await? {
150        Some(public_key) => Ok(public_key),
151        None => {
152            let keypair = generate_keypair(context).await?;
153            Ok(keypair.public)
154        }
155    }
156}
157
158/// Returns our own public keyring.
159///
160/// No keys are generated and at most one key is returned.
161pub(crate) async fn load_self_public_keyring(context: &Context) -> Result<Vec<SignedPublicKey>> {
162    if let Some(public_key) = load_self_public_key_opt(context).await? {
163        Ok(vec![public_key])
164    } else {
165        Ok(vec![])
166    }
167}
168
169/// Returns own public key fingerprint in (not human-readable) hex representation.
170/// This is the fingerprint format that is used in the database.
171///
172/// If no key is generated yet, generates a new one.
173///
174/// For performance reasons, the fingerprint is cached after the first invocation.
175pub(crate) async fn self_fingerprint(context: &Context) -> Result<&str> {
176    if let Some(fp) = context.self_fingerprint.get() {
177        Ok(fp)
178    } else {
179        let fp = load_self_public_key(context).await?.dc_fingerprint().hex();
180        Ok(context.self_fingerprint.get_or_init(|| fp))
181    }
182}
183
184/// Returns own public key fingerprint in (not human-readable) hex representation.
185/// This is the fingerprint format that is used in the database.
186///
187/// Returns `None` if no key is generated yet.
188///
189/// For performance reasons, the fingerprint is cached after the first invocation.
190pub(crate) async fn self_fingerprint_opt(context: &Context) -> Result<Option<&str>> {
191    if let Some(fp) = context.self_fingerprint.get() {
192        Ok(Some(fp))
193    } else if let Some(key) = load_self_public_key_opt(context).await? {
194        let fp = key.dc_fingerprint().hex();
195        Ok(Some(context.self_fingerprint.get_or_init(|| fp)))
196    } else {
197        Ok(None)
198    }
199}
200
201pub(crate) async fn load_self_secret_key(context: &Context) -> Result<SignedSecretKey> {
202    let private_key = context
203        .sql
204        .query_row_optional(
205            "SELECT private_key
206             FROM keypairs
207             WHERE id=(SELECT value FROM config WHERE keyname='key_id')",
208            (),
209            |row| {
210                let bytes: Vec<u8> = row.get(0)?;
211                Ok(bytes)
212            },
213        )
214        .await?;
215    match private_key {
216        Some(bytes) => SignedSecretKey::from_slice(&bytes),
217        None => {
218            let keypair = generate_keypair(context).await?;
219            Ok(keypair.secret)
220        }
221    }
222}
223
224pub(crate) async fn load_self_secret_keyring(context: &Context) -> Result<Vec<SignedSecretKey>> {
225    let keys = context
226        .sql
227        .query_map_vec(
228            r#"SELECT private_key
229               FROM keypairs
230               ORDER BY id=(SELECT value FROM config WHERE keyname='key_id') DESC"#,
231            (),
232            |row| {
233                let bytes: Vec<u8> = row.get(0)?;
234                Ok(bytes)
235            },
236        )
237        .await?
238        .into_iter()
239        .filter_map(|bytes| SignedSecretKey::from_slice(&bytes).log_err(context).ok())
240        .collect();
241    Ok(keys)
242}
243
244impl DcKey for SignedPublicKey {
245    fn to_asc(&self, header: Option<(&str, &str)>) -> String {
246        // Not using .to_armored_string() to make clear *why* it is
247        // safe to ignore this error.
248        // Because we write to a Vec<u8> the io::Write impls never
249        // fail and we can hide this error.
250        let headers =
251            header.map(|(key, value)| BTreeMap::from([(key.to_string(), vec![value.to_string()])]));
252        let mut buf = Vec::new();
253        self.to_armored_writer(&mut buf, headers.as_ref().into())
254            .unwrap_or_default();
255        std::string::String::from_utf8(buf).unwrap_or_default()
256    }
257
258    fn is_private() -> bool {
259        false
260    }
261
262    fn dc_fingerprint(&self) -> Fingerprint {
263        self.fingerprint().into()
264    }
265
266    fn key_id(&self) -> KeyId {
267        KeyDetails::key_id(self)
268    }
269}
270
271impl DcKey for SignedSecretKey {
272    fn to_asc(&self, header: Option<(&str, &str)>) -> String {
273        // Not using .to_armored_string() to make clear *why* it is
274        // safe to do these unwraps.
275        // Because we write to a Vec<u8> the io::Write impls never
276        // fail and we can hide this error.  The string is always ASCII.
277        let headers =
278            header.map(|(key, value)| BTreeMap::from([(key.to_string(), vec![value.to_string()])]));
279        let mut buf = Vec::new();
280        self.to_armored_writer(&mut buf, headers.as_ref().into())
281            .unwrap_or_default();
282        std::string::String::from_utf8(buf).unwrap_or_default()
283    }
284
285    fn is_private() -> bool {
286        true
287    }
288
289    fn dc_fingerprint(&self) -> Fingerprint {
290        self.fingerprint().into()
291    }
292
293    fn key_id(&self) -> KeyId {
294        KeyDetails::key_id(&**self)
295    }
296}
297
298/// Deltachat extension trait for secret keys.
299///
300/// Provides some convenience wrappers only applicable to [SignedSecretKey].
301pub(crate) trait DcSecretKey {
302    /// Create a public key from a private one.
303    fn split_public_key(&self) -> Result<SignedPublicKey>;
304}
305
306impl DcSecretKey for SignedSecretKey {
307    fn split_public_key(&self) -> Result<SignedPublicKey> {
308        self.verify()?;
309        let unsigned_pubkey = self.public_key();
310        let mut rng = rand_old::thread_rng();
311        let signed_pubkey = unsigned_pubkey.sign(
312            &mut rng,
313            &self.primary_key,
314            self.primary_key.public_key(),
315            &Password::empty(),
316        )?;
317        Ok(signed_pubkey)
318    }
319}
320
321async fn generate_keypair(context: &Context) -> Result<KeyPair> {
322    let addr = context.get_primary_self_addr().await?;
323    let addr = EmailAddress::new(&addr)?;
324    let _guard = context.generating_key_mutex.lock().await;
325
326    // Check if the key appeared while we were waiting on the lock.
327    match load_keypair(context).await? {
328        Some(key_pair) => Ok(key_pair),
329        None => {
330            let start = tools::Time::now();
331            info!(context, "Generating keypair.");
332            let keypair = Handle::current()
333                .spawn_blocking(move || crate::pgp::create_keypair(addr))
334                .await??;
335
336            store_self_keypair(context, &keypair).await?;
337            info!(
338                context,
339                "Keypair generated in {:.3}s.",
340                time_elapsed(&start).as_secs(),
341            );
342            Ok(keypair)
343        }
344    }
345}
346
347pub(crate) async fn load_keypair(context: &Context) -> Result<Option<KeyPair>> {
348    let res = context
349        .sql
350        .query_row_optional(
351            "SELECT public_key, private_key
352             FROM keypairs
353             WHERE id=(SELECT value FROM config WHERE keyname='key_id')",
354            (),
355            |row| {
356                let pub_bytes: Vec<u8> = row.get(0)?;
357                let sec_bytes: Vec<u8> = row.get(1)?;
358                Ok((pub_bytes, sec_bytes))
359            },
360        )
361        .await?;
362
363    Ok(if let Some((pub_bytes, sec_bytes)) = res {
364        Some(KeyPair {
365            public: SignedPublicKey::from_slice(&pub_bytes)?,
366            secret: SignedSecretKey::from_slice(&sec_bytes)?,
367        })
368    } else {
369        None
370    })
371}
372
373/// Store the keypair as an owned keypair for addr in the database.
374///
375/// This will save the keypair as keys for the given address.  The
376/// "self" here refers to the fact that this DC instance owns the
377/// keypair.  Usually `addr` will be [Config::ConfiguredAddr].
378///
379/// If either the public or private keys are already present in the
380/// database, this entry will be removed first regardless of the
381/// address associated with it.  Practically this means saving the
382/// same key again overwrites it.
383///
384/// [Config::ConfiguredAddr]: crate::config::Config::ConfiguredAddr
385pub(crate) async fn store_self_keypair(context: &Context, keypair: &KeyPair) -> Result<()> {
386    let mut config_cache_lock = context.sql.config_cache.write().await;
387    let new_key_id = context
388        .sql
389        .transaction(|transaction| {
390            let public_key = DcKey::to_bytes(&keypair.public);
391            let secret_key = DcKey::to_bytes(&keypair.secret);
392
393            // private_key and public_key columns
394            // are UNIQUE since migration 107,
395            // so this fails if we already have this key.
396            transaction
397                .execute(
398                    "INSERT INTO keypairs (public_key, private_key)
399                     VALUES (?,?)",
400                    (&public_key, &secret_key),
401                )
402                .context("Failed to insert keypair")?;
403
404            let new_key_id = transaction.last_insert_rowid();
405
406            // This will fail if we already have `key_id`.
407            //
408            // Setting default key is only possible if we don't
409            // have a key already.
410            transaction.execute(
411                "INSERT INTO config (keyname, value) VALUES ('key_id', ?)",
412                (new_key_id,),
413            )?;
414            Ok(new_key_id)
415        })
416        .await?;
417    context.emit_event(EventType::AccountsItemChanged);
418    config_cache_lock.insert("key_id".to_string(), Some(new_key_id.to_string()));
419    Ok(())
420}
421
422/// Saves a keypair as the default keys.
423///
424/// This API is used for testing purposes
425/// to avoid generating the key in tests.
426/// Use import/export APIs instead.
427pub async fn preconfigure_keypair(context: &Context, secret_data: &str) -> Result<()> {
428    let secret = SignedSecretKey::from_asc(secret_data)?;
429    let public = secret.split_public_key()?;
430    let keypair = KeyPair { public, secret };
431    store_self_keypair(context, &keypair).await?;
432    Ok(())
433}
434
435/// A key fingerprint
436#[derive(Clone, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
437pub struct Fingerprint(Vec<u8>);
438
439impl Fingerprint {
440    /// Creates new 160-bit (20 bytes) fingerprint.
441    pub fn new(v: Vec<u8>) -> Fingerprint {
442        debug_assert_eq!(v.len(), 20);
443        Fingerprint(v)
444    }
445
446    /// Make a hex string from the fingerprint.
447    ///
448    /// Use [std::fmt::Display] or [ToString::to_string] to get a
449    /// human-readable formatted string.
450    pub fn hex(&self) -> String {
451        hex::encode_upper(&self.0)
452    }
453}
454
455impl From<pgp::types::Fingerprint> for Fingerprint {
456    fn from(fingerprint: pgp::types::Fingerprint) -> Fingerprint {
457        Self::new(fingerprint.as_bytes().into())
458    }
459}
460
461impl fmt::Debug for Fingerprint {
462    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
463        f.debug_struct("Fingerprint")
464            .field("hex", &self.hex())
465            .finish()
466    }
467}
468
469/// Make a human-readable fingerprint.
470impl fmt::Display for Fingerprint {
471    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
472        // Split key into chunks of 4 with space and newline at 20 chars
473        for (i, c) in self.hex().chars().enumerate() {
474            if i > 0 && i % 20 == 0 {
475                writeln!(f)?;
476            } else if i > 0 && i % 4 == 0 {
477                write!(f, " ")?;
478            }
479            write!(f, "{c}")?;
480        }
481        Ok(())
482    }
483}
484
485/// Parse a human-readable or otherwise formatted fingerprint.
486impl std::str::FromStr for Fingerprint {
487    type Err = anyhow::Error;
488
489    fn from_str(input: &str) -> Result<Self> {
490        let hex_repr: String = input
491            .to_uppercase()
492            .chars()
493            .filter(|&c| c.is_ascii_hexdigit())
494            .collect();
495        let v: Vec<u8> = hex::decode(&hex_repr)?;
496        ensure!(v.len() == 20, "wrong fingerprint length: {hex_repr}");
497        let fp = Fingerprint::new(v);
498        Ok(fp)
499    }
500}
501
502#[cfg(test)]
503mod tests {
504    use std::sync::{Arc, LazyLock};
505
506    use super::*;
507    use crate::config::Config;
508    use crate::test_utils::{TestContext, alice_keypair};
509
510    static KEYPAIR: LazyLock<KeyPair> = LazyLock::new(alice_keypair);
511
512    #[test]
513    fn test_from_armored_string() {
514        let private_key = SignedSecretKey::from_asc(
515            "-----BEGIN PGP PRIVATE KEY BLOCK-----
516
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
5697yPJeQ==
570=KZk/
571-----END PGP PRIVATE KEY BLOCK-----",
572        )
573        .expect("failed to decode");
574        let binary = DcKey::to_bytes(&private_key);
575        SignedSecretKey::from_slice(&binary).expect("invalid private key");
576    }
577
578    #[test]
579    fn test_asc_roundtrip() {
580        let key = KEYPAIR.public.clone();
581        let asc = key.to_asc(Some(("spam", "ham")));
582        let key2 = SignedPublicKey::from_asc(&asc).unwrap();
583        assert_eq!(key, key2);
584
585        let key = KEYPAIR.secret.clone();
586        let asc = key.to_asc(Some(("spam", "ham")));
587        let key2 = SignedSecretKey::from_asc(&asc).unwrap();
588        assert_eq!(key, key2);
589    }
590
591    #[test]
592    fn test_from_slice_roundtrip() {
593        let public_key = KEYPAIR.public.clone();
594        let private_key = KEYPAIR.secret.clone();
595
596        let binary = DcKey::to_bytes(&public_key);
597        let public_key2 = SignedPublicKey::from_slice(&binary).expect("invalid public key");
598        assert_eq!(public_key, public_key2);
599
600        let binary = DcKey::to_bytes(&private_key);
601        let private_key2 = SignedSecretKey::from_slice(&binary).expect("invalid private key");
602        assert_eq!(private_key, private_key2);
603    }
604
605    #[test]
606    fn test_from_slice_bad_data() {
607        let mut bad_data: [u8; 4096] = [0; 4096];
608        for (i, v) in bad_data.iter_mut().enumerate() {
609            *v = (i & 0xff) as u8;
610        }
611        for j in 0..(4096 / 40) {
612            let slice = &bad_data.get(j..j + 4096 / 2 + j).unwrap();
613            assert!(SignedPublicKey::from_slice(slice).is_err());
614            assert!(SignedSecretKey::from_slice(slice).is_err());
615        }
616    }
617
618    /// Tests workaround for Delta Chat core < 1.0.0
619    /// which parsed CRC24 at the end of ASCII Armor
620    /// as the part of the key.
621    /// Depending on the alignment and the number of
622    /// `=` characters at the end of the key,
623    /// this resulted in various number of garbage
624    /// octets at the end of the key, starting from 3 octets,
625    /// but possibly 4 or 5 and maybe more octets
626    /// if the key is imported or transferred
627    /// using Autocrypt Setup Message multiple times.
628    #[test]
629    fn test_ignore_trailing_garbage() {
630        // Test several variants of garbage.
631        for garbage in [
632            b"\x02\xfc\xaa\x38\x4b\x5c".as_slice(),
633            b"\x02\xfc\xaa".as_slice(),
634            b"\x01\x02\x03\x04\x05".as_slice(),
635        ] {
636            let private_key = KEYPAIR.secret.clone();
637
638            let mut binary = DcKey::to_bytes(&private_key);
639            binary.extend(garbage);
640
641            let private_key2 =
642                SignedSecretKey::from_slice(&binary).expect("Failed to ignore garbage");
643
644            assert_eq!(private_key.dc_fingerprint(), private_key2.dc_fingerprint());
645        }
646    }
647
648    #[test]
649    fn test_base64_roundtrip() {
650        let key = KEYPAIR.public.clone();
651        let base64 = key.to_base64();
652        let key2 = SignedPublicKey::from_base64(&base64).unwrap();
653        assert_eq!(key, key2);
654    }
655
656    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
657    async fn test_load_self_generate_public() {
658        let t = TestContext::new().await;
659        t.set_config(Config::ConfiguredAddr, Some("alice@example.org"))
660            .await
661            .unwrap();
662        let key = load_self_public_key(&t).await;
663        assert!(key.is_ok());
664    }
665
666    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
667    async fn test_load_self_generate_secret() {
668        let t = TestContext::new().await;
669        t.set_config(Config::ConfiguredAddr, Some("alice@example.org"))
670            .await
671            .unwrap();
672        let key = load_self_secret_key(&t).await;
673        assert!(key.is_ok());
674    }
675
676    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
677    async fn test_load_self_generate_concurrent() {
678        use std::thread;
679
680        let t = TestContext::new().await;
681        t.set_config(Config::ConfiguredAddr, Some("alice@example.org"))
682            .await
683            .unwrap();
684        let thr0 = {
685            let ctx = t.clone();
686            thread::spawn(move || {
687                tokio::runtime::Runtime::new()
688                    .unwrap()
689                    .block_on(load_self_public_key(&ctx))
690            })
691        };
692        let thr1 = {
693            let ctx = t.clone();
694            thread::spawn(move || {
695                tokio::runtime::Runtime::new()
696                    .unwrap()
697                    .block_on(load_self_public_key(&ctx))
698            })
699        };
700        let res0 = thr0.join().unwrap();
701        let res1 = thr1.join().unwrap();
702        assert_eq!(res0.unwrap(), res1.unwrap());
703    }
704
705    #[test]
706    fn test_split_key() {
707        let pubkey = KEYPAIR.secret.split_public_key().unwrap();
708        assert_eq!(pubkey.primary_key, KEYPAIR.public.primary_key);
709    }
710
711    /// Tests that setting a default key second time is not allowed.
712    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
713    async fn test_save_self_key_twice() {
714        // Saving the same key twice should result in only one row in
715        // the keypairs table.
716        let t = TestContext::new().await;
717        let ctx = Arc::new(t);
718
719        let nrows = || async {
720            ctx.sql
721                .count("SELECT COUNT(*) FROM keypairs;", ())
722                .await
723                .unwrap()
724        };
725        assert_eq!(nrows().await, 0);
726        store_self_keypair(&ctx, &KEYPAIR).await.unwrap();
727        assert_eq!(nrows().await, 1);
728
729        // Saving a second key fails.
730        let res = store_self_keypair(&ctx, &KEYPAIR).await;
731        assert!(res.is_err());
732
733        assert_eq!(nrows().await, 1);
734    }
735
736    #[test]
737    fn test_fingerprint_from_str() {
738        let res = Fingerprint::new(vec![
739            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
740        ]);
741
742        let fp: Fingerprint = "0102030405060708090A0B0c0d0e0F1011121314".parse().unwrap();
743        assert_eq!(fp, res);
744
745        let fp: Fingerprint = "zzzz 0102 0304 0506\n0708090a0b0c0D0E0F1011121314 yyy"
746            .parse()
747            .unwrap();
748        assert_eq!(fp, res);
749
750        assert!("1".parse::<Fingerprint>().is_err());
751    }
752
753    #[test]
754    fn test_fingerprint_hex() {
755        let fp = Fingerprint::new(vec![
756            1, 2, 4, 8, 16, 32, 64, 128, 255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
757        ]);
758        assert_eq!(fp.hex(), "0102040810204080FF0A0B0C0D0E0F1011121314");
759    }
760
761    #[test]
762    fn test_fingerprint_to_string() {
763        let fp = Fingerprint::new(vec![
764            1, 2, 4, 8, 16, 32, 64, 128, 255, 1, 2, 4, 8, 16, 32, 64, 128, 255, 19, 20,
765        ]);
766        assert_eq!(
767            fp.to_string(),
768            "0102 0408 1020 4080 FF01\n0204 0810 2040 80FF 1314"
769        );
770    }
771}