deltachat/
key.rs

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