cmail_rpgp/composed/message/
types.rs

1use std::io;
2
3use bstr::BStr;
4use chrono::SubsecRound;
5use flate2::write::{DeflateEncoder, ZlibEncoder};
6use flate2::Compression;
7use log::{debug, warn};
8use rand::{CryptoRng, Rng};
9
10use crate::armor;
11use crate::composed::message::decrypt::*;
12use crate::composed::shared::Deserializable;
13use crate::composed::signed_key::SignedSecretKey;
14use crate::composed::StandaloneSignature;
15use crate::crypto::aead::AeadAlgorithm;
16use crate::crypto::hash::HashAlgorithm;
17use crate::crypto::sym::SymmetricKeyAlgorithm;
18use crate::errors::{Error, Result};
19use crate::packet::{
20    write_packet, CompressedData, LiteralData, OnePassSignature, Packet,
21    PublicKeyEncryptedSessionKey, Signature, SignatureConfig, SignatureType,
22    SignatureVersionSpecific, Subpacket, SubpacketData, SymEncryptedData,
23    SymEncryptedProtectedData, SymKeyEncryptedSessionKey,
24};
25use crate::ser::Serialize;
26use crate::types::{
27    CompressionAlgorithm, EskType, Fingerprint, KeyId, KeyVersion, PkeskVersion, PublicKeyTrait,
28    SecretKeyTrait, StringToKey, Tag,
29};
30
31/// An [OpenPGP message](https://www.rfc-editor.org/rfc/rfc9580.html#name-openpgp-messages)
32#[derive(Clone, Debug, PartialEq, Eq)]
33pub enum Message {
34    Literal(LiteralData),
35    Compressed(CompressedData),
36    Signed {
37        /// nested message
38        message: Option<Box<Message>>,
39        /// for signature packets that contain a one pass message
40        one_pass_signature: Option<OnePassSignature>,
41        // actual signature
42        signature: Signature,
43    },
44    Encrypted {
45        esk: Vec<Esk>,
46        edata: Edata,
47    },
48}
49
50/// Encrypted Session Key
51///
52/// Public-Key Encrypted Session Key Packet |
53/// Symmetric-Key Encrypted Session Key Packet.
54#[derive(Debug, Clone, PartialEq, Eq)]
55pub enum Esk {
56    PublicKeyEncryptedSessionKey(PublicKeyEncryptedSessionKey),
57    SymKeyEncryptedSessionKey(SymKeyEncryptedSessionKey),
58}
59
60impl Serialize for Esk {
61    fn to_writer<W: io::Write>(&self, writer: &mut W) -> Result<()> {
62        match self {
63            Esk::PublicKeyEncryptedSessionKey(k) => write_packet(writer, k),
64            Esk::SymKeyEncryptedSessionKey(k) => write_packet(writer, k),
65        }
66    }
67}
68
69impl_try_from_into!(
70    Esk,
71    PublicKeyEncryptedSessionKey => PublicKeyEncryptedSessionKey,
72    SymKeyEncryptedSessionKey => SymKeyEncryptedSessionKey
73);
74
75impl Esk {
76    pub fn tag(&self) -> Tag {
77        match self {
78            Esk::PublicKeyEncryptedSessionKey(_) => Tag::PublicKeyEncryptedSessionKey,
79            Esk::SymKeyEncryptedSessionKey(_) => Tag::SymKeyEncryptedSessionKey,
80        }
81    }
82}
83
84impl TryFrom<Packet> for Esk {
85    type Error = Error;
86
87    fn try_from(other: Packet) -> Result<Esk> {
88        match other {
89            Packet::PublicKeyEncryptedSessionKey(k) => Ok(Esk::PublicKeyEncryptedSessionKey(k)),
90            Packet::SymKeyEncryptedSessionKey(k) => Ok(Esk::SymKeyEncryptedSessionKey(k)),
91            _ => Err(format_err!("not a valid edata packet: {:?}", other)),
92        }
93    }
94}
95
96impl From<Esk> for Packet {
97    fn from(other: Esk) -> Packet {
98        match other {
99            Esk::PublicKeyEncryptedSessionKey(k) => Packet::PublicKeyEncryptedSessionKey(k),
100            Esk::SymKeyEncryptedSessionKey(k) => Packet::SymKeyEncryptedSessionKey(k),
101        }
102    }
103}
104
105/// Encrypted Data
106/// Symmetrically Encrypted Data Packet |
107/// Symmetrically Encrypted Integrity Protected Data Packet
108#[derive(Debug, Clone, PartialEq, Eq)]
109pub enum Edata {
110    SymEncryptedData(SymEncryptedData),
111    SymEncryptedProtectedData(SymEncryptedProtectedData),
112}
113
114impl Serialize for Edata {
115    fn to_writer<W: io::Write>(&self, writer: &mut W) -> Result<()> {
116        match self {
117            Edata::SymEncryptedData(d) => write_packet(writer, d),
118            Edata::SymEncryptedProtectedData(d) => write_packet(writer, d),
119        }
120    }
121}
122
123impl_try_from_into!(
124    Edata,
125    SymEncryptedData => SymEncryptedData,
126    SymEncryptedProtectedData => SymEncryptedProtectedData
127);
128
129impl TryFrom<Packet> for Edata {
130    type Error = Error;
131
132    fn try_from(other: Packet) -> Result<Edata> {
133        match other {
134            Packet::SymEncryptedData(d) => Ok(Edata::SymEncryptedData(d)),
135            Packet::SymEncryptedProtectedData(d) => Ok(Edata::SymEncryptedProtectedData(d)),
136            _ => Err(format_err!("not a valid edata packet: {:?}", other)),
137        }
138    }
139}
140
141impl From<Edata> for Packet {
142    fn from(other: Edata) -> Packet {
143        match other {
144            Edata::SymEncryptedData(d) => Packet::SymEncryptedData(d),
145            Edata::SymEncryptedProtectedData(d) => Packet::SymEncryptedProtectedData(d),
146        }
147    }
148}
149
150impl Edata {
151    pub fn data(&self) -> &[u8] {
152        match self {
153            Edata::SymEncryptedData(d) => d.data(),
154            Edata::SymEncryptedProtectedData(d) => d.data_as_slice(),
155        }
156    }
157
158    pub fn tag(&self) -> Tag {
159        match self {
160            Edata::SymEncryptedData(_) => Tag::SymEncryptedData,
161            Edata::SymEncryptedProtectedData(_) => Tag::SymEncryptedProtectedData,
162        }
163    }
164
165    fn version(&self) -> Option<usize> {
166        match self {
167            Edata::SymEncryptedData(_) => None,
168            Edata::SymEncryptedProtectedData(d) => Some(d.version()),
169        }
170    }
171
172    /// Transform decrypted data into a message.
173    /// Bails if the packets contain no message or multiple messages.
174    fn process_decrypted(packet_data: &[u8]) -> Result<Message> {
175        let mut messages = Message::from_bytes_many(packet_data);
176        // First message is the one we want to return
177        let Some(message) = messages.next() else {
178            bail!("no valid message found");
179        };
180        let message = message?;
181
182        // The only other message allowed is a padding packet, which will be skipped
183        // by the parser, so check that we have only a single message.
184        if let Some(msg) = messages.next() {
185            bail!("unexpected message: {:?}", msg);
186        }
187
188        Ok(message)
189    }
190
191    pub fn decrypt(&self, key: PlainSessionKey) -> Result<Message> {
192        let protected = self.tag() == Tag::SymEncryptedProtectedData;
193        debug!("decrypting protected = {:?}", protected);
194
195        match key {
196            PlainSessionKey::V3_4 { sym_alg, ref key } => {
197                ensure!(
198                    sym_alg != SymmetricKeyAlgorithm::Plaintext,
199                    "session key algorithm cannot be plaintext"
200                );
201
202                match self {
203                    Self::SymEncryptedProtectedData(p) => {
204                        ensure_eq!(
205                            self.version(),
206                            Some(1),
207                            "Version mismatch between key and integrity packet"
208                        );
209                        let data = p.decrypt(key, Some(sym_alg))?;
210                        Self::process_decrypted(&data[..])
211                    }
212                    Self::SymEncryptedData(p) => {
213                        ensure_eq!(
214                            self.version(),
215                            None,
216                            "Version mismatch between key and integrity packet"
217                        );
218                        let mut data = p.data().to_vec();
219                        let res = sym_alg.decrypt(key, &mut data)?;
220                        Self::process_decrypted(res)
221                    }
222                }
223            }
224            PlainSessionKey::V5 { .. } => match self {
225                Self::SymEncryptedProtectedData(_p) => {
226                    ensure_eq!(
227                        self.version(),
228                        Some(2),
229                        "Version mismatch between key and integrity packet"
230                    );
231                    unimplemented_err!("V5 decryption");
232                }
233                Self::SymEncryptedData(_) => {
234                    bail!("invalid packet combination");
235                }
236            },
237            PlainSessionKey::V6 { ref key } => match self {
238                Self::SymEncryptedProtectedData(p) => {
239                    ensure_eq!(
240                        self.version(),
241                        Some(2),
242                        "Version mismatch between key and integrity packet"
243                    );
244
245                    let decrypted_packets = p.decrypt(key, None)?;
246                    Self::process_decrypted(&decrypted_packets[..])
247                }
248                Self::SymEncryptedData(_) => {
249                    bail!("invalid packet combination");
250                }
251            },
252        }
253    }
254}
255
256impl Serialize for Message {
257    fn to_writer<W: io::Write>(&self, writer: &mut W) -> Result<()> {
258        match self {
259            Message::Literal(data) => write_packet(writer, data),
260            Message::Compressed(data) => write_packet(writer, data),
261            Message::Signed {
262                message,
263                one_pass_signature,
264                signature,
265                ..
266            } => {
267                if let Some(ops) = one_pass_signature {
268                    write_packet(writer, ops)?;
269                }
270                if let Some(message) = message {
271                    (**message).to_writer(writer)?;
272                }
273
274                write_packet(writer, signature)?;
275
276                Ok(())
277            }
278            Message::Encrypted { esk, edata, .. } => {
279                for e in esk {
280                    e.to_writer(writer)?;
281                }
282                edata.to_writer(writer)?;
283
284                Ok(())
285            }
286        }
287    }
288}
289
290impl Message {
291    pub fn new_literal(file_name: impl AsRef<BStr>, data: &str) -> Self {
292        Message::Literal(LiteralData::from_str(file_name.as_ref(), data))
293    }
294
295    pub fn new_literal_bytes(file_name: impl AsRef<BStr>, data: &[u8]) -> Self {
296        Message::Literal(LiteralData::from_bytes(file_name.as_ref(), data))
297    }
298
299    /// Compresses the message.
300    pub fn compress(&self, alg: CompressionAlgorithm) -> Result<Self> {
301        let data = match alg {
302            CompressionAlgorithm::Uncompressed => {
303                let mut data = Vec::new();
304                self.to_writer(&mut data)?;
305                data
306            }
307            CompressionAlgorithm::ZIP => {
308                let mut enc = DeflateEncoder::new(Vec::new(), Compression::default());
309                self.to_writer(&mut enc)?;
310                enc.finish()?
311            }
312            CompressionAlgorithm::ZLIB => {
313                let mut enc = ZlibEncoder::new(Vec::new(), Compression::default());
314                self.to_writer(&mut enc)?;
315                enc.finish()?
316            }
317            CompressionAlgorithm::BZip2 => unimplemented_err!("BZip2"),
318            CompressionAlgorithm::Private10 | CompressionAlgorithm::Other(_) => {
319                unsupported_err!("CompressionAlgorithm {} is unsupported", u8::from(alg))
320            }
321        };
322
323        Ok(Message::Compressed(CompressedData::from_compressed(
324            alg, data,
325        )))
326    }
327
328    /// Decompresses the data if compressed.
329    pub fn decompress(self) -> Result<Self> {
330        match self {
331            Message::Compressed(data) => Message::from_bytes(data.decompress()?),
332            _ => Ok(self),
333        }
334    }
335
336    /// Encrypt the message in SEIPDv1 format to a list of public keys `pkeys`.
337    pub fn encrypt_to_keys_seipdv1<R: CryptoRng + Rng>(
338        &self,
339        mut rng: R,
340        alg: SymmetricKeyAlgorithm,
341        pkeys: &[&impl PublicKeyTrait],
342    ) -> Result<Self> {
343        // 1. Generate a session key.
344        let session_key = alg.new_session_key(&mut rng);
345
346        // 2. Encrypt (pub) the session key, to each PublicKey.
347        let esk = pkeys
348            .iter()
349            .map(|pkey| {
350                let pkes = PublicKeyEncryptedSessionKey::from_session_key_v3(
351                    &mut rng,
352                    &session_key,
353                    alg,
354                    pkey,
355                )?;
356                Ok(Esk::PublicKeyEncryptedSessionKey(pkes))
357            })
358            .collect::<Result<_>>()?;
359
360        // 3. Encrypt (sym) the data using the session key.
361        self.encrypt_symmetric_seipdv1(&mut rng, esk, alg, &session_key)
362    }
363
364    /// Encrypt the message in SEIPDv2 format to a list of public keys `pkeys`.
365    pub fn encrypt_to_keys_seipdv2<R: CryptoRng + Rng>(
366        &self,
367        mut rng: R,
368        alg: SymmetricKeyAlgorithm,
369        aead: AeadAlgorithm,
370        chunk_size: u8,
371        pkeys: &[&impl PublicKeyTrait],
372    ) -> Result<Self> {
373        // 1. Generate a session key.
374        let session_key = alg.new_session_key(&mut rng);
375
376        // 2. Encrypt (pub) the session key, to each PublicKey.
377        let esk = pkeys
378            .iter()
379            .map(|pkey| {
380                let pkes = PublicKeyEncryptedSessionKey::from_session_key_v6(
381                    &mut rng,
382                    &session_key,
383                    pkey,
384                )?;
385                Ok(Esk::PublicKeyEncryptedSessionKey(pkes))
386            })
387            .collect::<Result<_>>()?;
388
389        // 3. Encrypt (sym) the data using the session key.
390        self.encrypt_symmetric_seipdv2(&mut rng, esk, alg, aead, chunk_size, &session_key)
391    }
392
393    /// Encrypt the message in SEIPDv1 format to a password `msg_pw`.
394    pub fn encrypt_with_password_seipdv1<R, F>(
395        &self,
396        mut rng: R,
397        s2k: StringToKey,
398        alg: SymmetricKeyAlgorithm,
399        msg_pw: F,
400    ) -> Result<Self>
401    where
402        R: Rng + CryptoRng,
403        F: FnOnce() -> String + Clone,
404    {
405        // 1. Generate a session key.
406        let session_key = alg.new_session_key(&mut rng);
407
408        // 2. Encrypt (sym) the session key using the provided password.
409        let skesk = Esk::SymKeyEncryptedSessionKey(SymKeyEncryptedSessionKey::encrypt_v4(
410            msg_pw,
411            &session_key,
412            s2k,
413            alg,
414        )?);
415
416        // 3. Encrypt (sym) the data using the session key.
417        self.encrypt_symmetric_seipdv1(rng, vec![skesk], alg, &session_key)
418    }
419
420    /// Encrypt the message in SEIPDv2 format to a password `msg_pw`.
421    pub fn encrypt_with_password_seipdv2<R, F>(
422        &self,
423        mut rng: R,
424        s2k: StringToKey,
425        alg: SymmetricKeyAlgorithm,
426        aead: AeadAlgorithm,
427        chunk_size: u8,
428        msg_pw: F,
429    ) -> Result<Self>
430    where
431        R: Rng + CryptoRng,
432        F: FnOnce() -> String + Clone,
433    {
434        // 1. Generate a session key.
435        let session_key = alg.new_session_key(&mut rng);
436
437        // 2. Encrypt (sym) the session key using the provided password.
438        let skesk = Esk::SymKeyEncryptedSessionKey(SymKeyEncryptedSessionKey::encrypt_v6(
439            &mut rng,
440            msg_pw,
441            &session_key,
442            s2k,
443            alg,
444            aead,
445        )?);
446
447        // 3. Encrypt (sym) the data using the session key.
448        self.encrypt_symmetric_seipdv2(rng, vec![skesk], alg, aead, chunk_size, &session_key)
449    }
450
451    /// Symmetrically encrypt this Message in SEIPDv1 format using the provided `session_key`.
452    ///
453    /// This function assumes that it is only called with Esk that are legal to use with SEIPDv1.
454    fn encrypt_symmetric_seipdv1<R: CryptoRng + Rng>(
455        &self,
456        rng: R,
457        esk: Vec<Esk>,
458        alg: SymmetricKeyAlgorithm,
459        session_key: &[u8],
460    ) -> Result<Self> {
461        let data = self.to_bytes()?;
462
463        let edata = Edata::SymEncryptedProtectedData(SymEncryptedProtectedData::encrypt_seipdv1(
464            rng,
465            alg,
466            session_key,
467            &data,
468        )?);
469
470        Ok(Message::Encrypted { esk, edata })
471    }
472
473    /// Symmetrically encrypt this Message in SEIPDv2 format using the provided `session_key`.
474    ///
475    /// This function assumes that it is only called with Esk that are legal to use with SEIPDv2.
476    fn encrypt_symmetric_seipdv2<R: CryptoRng + Rng>(
477        &self,
478        rng: R,
479        esk: Vec<Esk>,
480        alg: SymmetricKeyAlgorithm,
481        aead: AeadAlgorithm,
482        chunk_size: u8,
483        session_key: &[u8],
484    ) -> Result<Self> {
485        let data = self.to_bytes()?;
486
487        let edata = Edata::SymEncryptedProtectedData(SymEncryptedProtectedData::encrypt_seipdv2(
488            rng,
489            alg,
490            aead,
491            chunk_size,
492            session_key,
493            &data,
494        )?);
495
496        Ok(Message::Encrypted { esk, edata })
497    }
498
499    /// Sign this message using the provided key.
500    pub fn sign<R, F>(
501        self,
502        rng: R,
503        key: &impl SecretKeyTrait,
504        key_pw: F,
505        hash_algorithm: HashAlgorithm,
506    ) -> Result<Self>
507    where
508        R: CryptoRng + Rng,
509        F: FnOnce() -> String,
510    {
511        let key_id = key.key_id();
512        let algorithm = key.algorithm();
513
514        let hashed_subpackets = vec![
515            Subpacket::regular(SubpacketData::IssuerFingerprint(key.fingerprint())),
516            Subpacket::regular(SubpacketData::SignatureCreationTime(
517                chrono::Utc::now().trunc_subsecs(0),
518            )),
519        ];
520        let unhashed_subpackets = vec![Subpacket::regular(SubpacketData::Issuer(key_id.clone()))];
521
522        let (typ, signature) = match self {
523            Message::Literal(ref l) => {
524                let typ = if l.is_binary() {
525                    SignatureType::Binary
526                } else {
527                    SignatureType::Text
528                };
529
530                let mut config = match key.version() {
531                    KeyVersion::V4 => SignatureConfig::v4(typ, algorithm, hash_algorithm),
532                    KeyVersion::V6 => SignatureConfig::v6(rng, typ, algorithm, hash_algorithm)?,
533                    v => bail!("unsupported key version {:?}", v),
534                };
535                config.hashed_subpackets = hashed_subpackets;
536                config.unhashed_subpackets = unhashed_subpackets;
537
538                (typ, config.sign(key, key_pw, l.data())?)
539            }
540            _ => {
541                let typ = SignatureType::Binary;
542
543                let mut config = match key.version() {
544                    KeyVersion::V4 => SignatureConfig::v4(typ, algorithm, hash_algorithm),
545                    KeyVersion::V6 => SignatureConfig::v6(rng, typ, algorithm, hash_algorithm)?,
546                    v => bail!("unsupported key version {:?}", v),
547                };
548                config.hashed_subpackets = hashed_subpackets;
549                config.unhashed_subpackets = unhashed_subpackets;
550
551                let data = self.to_bytes()?;
552                let signature = config.sign(key, key_pw, &data[..])?;
553
554                (typ, signature)
555            }
556        };
557
558        let ops = match key.version() {
559            KeyVersion::V4 => OnePassSignature::v3(typ, hash_algorithm, algorithm, key_id),
560            KeyVersion::V6 => {
561                let SignatureVersionSpecific::V6 { ref salt } = signature.config.version_specific
562                else {
563                    // This should never happen
564                    bail!("Inconsistent Signature and OnePassSignature version")
565                };
566
567                let Fingerprint::V6(fp) = key.fingerprint() else {
568                    bail!("Inconsistent Signature and Fingerprint version")
569                };
570
571                OnePassSignature::v6(typ, hash_algorithm, algorithm, salt.clone(), fp)
572            }
573            v => bail!("Unsupported key version {:?}", v),
574        };
575
576        Ok(Message::Signed {
577            message: Some(Box::new(self)),
578            one_pass_signature: Some(ops),
579            signature,
580        })
581    }
582
583    /// Convert the message to a standalone signature according to the cleartext framework.
584    pub fn into_signature(self) -> StandaloneSignature {
585        match self {
586            Message::Signed { signature, .. } => StandaloneSignature::new(signature),
587            _ => panic!("only signed messages can be converted to standalone signature messages"),
588        }
589    }
590
591    /// Verify this message.
592    /// For signed messages this verifies the signature and for compressed messages
593    /// they are decompressed and checked for signatures to verify.
594    ///
595    /// Decompresses up to one layer of compressed data.
596    pub fn verify(&self, key: &impl PublicKeyTrait) -> Result<()> {
597        self.verify_internal(key, true)
598    }
599
600    /// Verifies this message.
601    /// For signed messages this verifies the signature.
602    ///
603    /// If `decompress` is true and the message is compressed,
604    /// the message is decompressed and verified.
605    fn verify_internal(&self, key: &impl PublicKeyTrait, decompress: bool) -> Result<()> {
606        match self {
607            Message::Signed {
608                signature, message, ..
609            } => {
610                if let Some(message) = message {
611                    match **message {
612                        Message::Literal(ref data) => signature.verify(key, data.data()),
613                        _ => {
614                            let data = message.to_bytes()?;
615                            signature.verify(key, &data[..])
616                        }
617                    }
618                } else {
619                    unimplemented_err!("no message, what to do?");
620                }
621            }
622            Message::Compressed(data) => {
623                if decompress {
624                    let msg = Message::from_bytes(data.decompress()?)?;
625                    msg.verify_internal(key, false)
626                } else {
627                    bail!("Recursive decompression not allowed");
628                }
629            }
630            // We don't know how to verify a signature for other Message types, and shouldn't return Ok
631            _ => Err(Error::Unsupported(format!(
632                "Unexpected message format: {self:?}",
633            ))),
634        }
635    }
636
637    /// Decrypt the message using the given key.
638    /// Returns a message decrypter, and a list of [KeyId]s that are valid recipients of this message.
639    pub fn decrypt<G>(&self, key_pw: G, keys: &[&SignedSecretKey]) -> Result<(Message, Vec<KeyId>)>
640    where
641        G: FnOnce() -> String + Clone,
642    {
643        match self {
644            Message::Compressed { .. } | Message::Literal { .. } => {
645                bail!("not encrypted");
646            }
647            Message::Signed { message, .. } => match message {
648                Some(message) => message.as_ref().decrypt(key_pw, keys),
649                None => bail!("not encrypted"),
650            },
651            Message::Encrypted { esk, edata, .. } => {
652                let valid_keys = keys
653                    .iter()
654                    .filter_map(|key| {
655                        // search for a packet with a key id that we have and that key.
656                        let mut packet = None;
657                        let mut encoding_key = None;
658                        let mut encoding_subkey = None;
659
660                        for esk_packet in esk.iter().filter_map(|k| match k {
661                            Esk::PublicKeyEncryptedSessionKey(k) => Some(k),
662                            _ => None,
663                        }) {
664                            debug!("esk packet: {:?}", esk_packet);
665                            debug!("{:?}", key.key_id());
666                            debug!(
667                                "{:?}",
668                                key.secret_subkeys
669                                    .iter()
670                                    .map(PublicKeyTrait::key_id)
671                                    .collect::<Vec<_>>()
672                            );
673
674                            // find the matching key or subkey
675
676                            if esk_packet.match_identity(&key.primary_key) {
677                                encoding_key = Some(&key.primary_key);
678                            }
679
680                            if encoding_key.is_none() {
681                                encoding_subkey = key
682                                    .secret_subkeys
683                                    .iter()
684                                    .find(|&subkey| esk_packet.match_identity(&subkey));
685                            }
686
687                            if encoding_key.is_some() || encoding_subkey.is_some() {
688                                packet = Some(esk_packet);
689                                break;
690                            }
691                        }
692
693                        packet.map(|packet| (packet, encoding_key, encoding_subkey))
694                    })
695                    .collect::<Vec<_>>();
696
697                if valid_keys.is_empty() {
698                    return Err(Error::MissingKey);
699                }
700
701                let session_keys = valid_keys
702                    .iter()
703                    .map(|(pkesk, encoding_key, encoding_subkey)| {
704                        let typ = match pkesk.version() {
705                            PkeskVersion::V3 => EskType::V3_4,
706                            PkeskVersion::V6 => EskType::V6,
707                            PkeskVersion::Other(v) => {
708                                unimplemented_err!("Unexpected PKESK version {}", v)
709                            }
710                        };
711
712                        if let Some(ek) = encoding_key {
713                            Ok((
714                                ek.key_id(),
715                                decrypt_session_key(ek, key_pw.clone(), pkesk.values()?, typ)?,
716                            ))
717                        } else if let Some(ek) = encoding_subkey {
718                            Ok((
719                                ek.key_id(),
720                                decrypt_session_key(ek, key_pw.clone(), pkesk.values()?, typ)?,
721                            ))
722                        } else {
723                            unreachable!("either a key or a subkey were found");
724                        }
725                    })
726                    .filter(|res| match res {
727                        Ok(_) => true,
728                        Err(err) => {
729                            warn!("failed to decrypt session_key for key: {:?}", err);
730                            false
731                        }
732                    })
733                    .collect::<Result<Vec<_>>>()?;
734
735                ensure!(!session_keys.is_empty(), "failed to decrypt session key");
736
737                // make sure all the keys are the same, otherwise we are in a bad place
738                let session_key = {
739                    let (_key_id, k0) = &session_keys[0];
740                    if !session_keys.iter().skip(1).all(|(_, k)| k0 == k) {
741                        bail!("found inconsistent session keys, possible message corruption");
742                    }
743
744                    // TODO: avoid cloning
745                    k0.clone()
746                };
747
748                let ids = session_keys.into_iter().map(|(k, _)| k).collect();
749                let msg = edata.decrypt(session_key)?;
750
751                Ok((msg, ids))
752            }
753        }
754    }
755
756    /// Decrypt the message using the given key.
757    /// Returns a message decrypter, and a list of [KeyId]s that are valid recipients of this message.
758    pub fn decrypt_with_password<F>(&self, msg_pw: F) -> Result<Message>
759    where
760        F: FnOnce() -> String + Clone,
761    {
762        match self {
763            Message::Compressed { .. } | Message::Literal { .. } => {
764                bail!("not encrypted");
765            }
766            Message::Signed { message, .. } => match message {
767                Some(ref message) => message.decrypt_with_password(msg_pw),
768                None => bail!("not encrypted"),
769            },
770            Message::Encrypted { esk, edata, .. } => {
771                // TODO: handle multiple passwords
772                let skesk = esk.iter().find_map(|esk| match esk {
773                    Esk::SymKeyEncryptedSessionKey(k) => Some(k),
774                    _ => None,
775                });
776
777                ensure!(skesk.is_some(), "message is not password protected");
778
779                let session_key =
780                    decrypt_session_key_with_password(skesk.expect("checked above"), msg_pw)?;
781                edata.decrypt(session_key)
782            }
783        }
784    }
785
786    /// Check if this message is a signature, that was signed with a one pass signature.
787    pub fn is_one_pass_signed(&self) -> bool {
788        match self {
789            Message::Signed {
790                one_pass_signature, ..
791            } => one_pass_signature.is_some(),
792            _ => false,
793        }
794    }
795
796    pub fn is_literal(&self) -> bool {
797        match self {
798            Message::Literal { .. } => true,
799            Message::Signed { message, .. } => message
800                .as_ref()
801                .map(|msg| msg.is_literal())
802                .unwrap_or_default(),
803            _ => false,
804        }
805    }
806
807    pub fn get_literal(&self) -> Option<&LiteralData> {
808        match self {
809            Message::Literal(ref data) => Some(data),
810            Message::Signed { message, .. } => message.as_ref().and_then(|msg| msg.get_literal()),
811            _ => None,
812        }
813    }
814
815    /// Returns the underlying content and `None` if the message is encrypted.
816    ///
817    /// Decompresses up to one layer of compressed data.
818    pub fn get_content(&self) -> Result<Option<Vec<u8>>> {
819        self.get_content_internal(true)
820    }
821
822    /// Returns the underlying content and `None` if the message is encrypted.
823    ///
824    /// If `decompress` is true, may decompress a compressed message.
825    fn get_content_internal(&self, decompress: bool) -> Result<Option<Vec<u8>>> {
826        match self {
827            Message::Literal(ref data) => Ok(Some(data.data().to_vec())),
828            Message::Signed { message, .. } => Ok(message
829                .as_ref()
830                .and_then(|m| m.get_literal())
831                .map(|l| l.data().to_vec())),
832            Message::Compressed(data) => {
833                if decompress {
834                    let msg = Message::from_bytes(data.decompress()?)?;
835                    msg.get_content_internal(false)
836                } else {
837                    bail!("Recursive decompression not allowed");
838                }
839            }
840            Message::Encrypted { .. } => Ok(None),
841        }
842    }
843
844    pub fn to_armored_writer(
845        &self,
846        writer: &mut impl io::Write,
847        opts: ArmorOptions<'_>,
848    ) -> Result<()> {
849        armor::write(
850            self,
851            armor::BlockType::Message,
852            writer,
853            opts.headers,
854            opts.include_checksum,
855        )
856    }
857
858    pub fn to_armored_bytes(&self, opts: ArmorOptions<'_>) -> Result<Vec<u8>> {
859        let mut buf = Vec::new();
860
861        self.to_armored_writer(&mut buf, opts)?;
862
863        Ok(buf)
864    }
865
866    pub fn to_armored_string(&self, opts: ArmorOptions<'_>) -> Result<String> {
867        let res = String::from_utf8(self.to_armored_bytes(opts)?).map_err(|e| e.utf8_error())?;
868        Ok(res)
869    }
870}
871
872/// Options for generating armored content.
873#[derive(Debug, Clone)]
874pub struct ArmorOptions<'a> {
875    /// Armor headers
876    pub headers: Option<&'a armor::Headers>,
877    /// Should a checksum be included? Default to `true`.
878    pub include_checksum: bool,
879}
880
881impl Default for ArmorOptions<'_> {
882    fn default() -> Self {
883        Self {
884            headers: None,
885            include_checksum: true,
886        }
887    }
888}
889
890impl<'a> From<Option<&'a armor::Headers>> for ArmorOptions<'a> {
891    fn from(headers: Option<&'a armor::Headers>) -> Self {
892        Self {
893            headers,
894            include_checksum: true,
895        }
896    }
897}
898
899#[cfg(test)]
900mod tests {
901    #![allow(clippy::unwrap_used)]
902
903    use std::fs;
904
905    use rand::SeedableRng;
906    use rand_chacha::ChaCha8Rng;
907
908    use super::*;
909    use crate::cleartext::CleartextSignedMessage;
910    use crate::SignedPublicKey;
911
912    #[test]
913    fn test_compression_zlib() {
914        let lit_msg = Message::new_literal("hello-zlib.txt", "hello world");
915
916        let compressed_msg = lit_msg.compress(CompressionAlgorithm::ZLIB).unwrap();
917        let uncompressed_msg = compressed_msg.decompress().unwrap();
918
919        assert_eq!(&lit_msg, &uncompressed_msg);
920    }
921
922    #[test]
923    fn test_compression_zip() {
924        let lit_msg = Message::new_literal("hello-zip.txt", "hello world");
925
926        let compressed_msg = lit_msg.compress(CompressionAlgorithm::ZIP).unwrap();
927        let uncompressed_msg = compressed_msg.decompress().unwrap();
928
929        assert_eq!(&lit_msg, &uncompressed_msg);
930    }
931
932    #[test]
933    fn test_compression_uncompressed() {
934        let lit_msg = Message::new_literal("hello.txt", "hello world");
935
936        let compressed_msg = lit_msg
937            .compress(CompressionAlgorithm::Uncompressed)
938            .unwrap();
939        let uncompressed_msg = compressed_msg.decompress().unwrap();
940
941        assert_eq!(&lit_msg, &uncompressed_msg);
942    }
943
944    #[test]
945    fn test_rsa_encryption_seipdv1() {
946        let (skey, _headers) = SignedSecretKey::from_armor_single(
947            fs::File::open("./tests/openpgp-interop/testcases/messages/gnupg-v1-001-decrypt.asc")
948                .unwrap(),
949        )
950        .unwrap();
951
952        // subkey[0] is the encryption key
953        let pkey = skey.secret_subkeys[0].public_key();
954        let mut rng = rand::rngs::StdRng::seed_from_u64(100);
955        let mut rng2 = rand::rngs::StdRng::seed_from_u64(100);
956
957        let lit_msg = Message::new_literal("hello.txt", "hello world\n");
958        let compressed_msg = lit_msg.compress(CompressionAlgorithm::ZLIB).unwrap();
959
960        // Encrypt and test that rng is the only source of randomness.
961        let encrypted = compressed_msg
962            .encrypt_to_keys_seipdv1(&mut rng, SymmetricKeyAlgorithm::AES128, &[&pkey][..])
963            .unwrap();
964        let encrypted2 = compressed_msg
965            .encrypt_to_keys_seipdv1(&mut rng2, SymmetricKeyAlgorithm::AES128, &[&pkey][..])
966            .unwrap();
967        assert_eq!(encrypted, encrypted2);
968
969        let armored = encrypted.to_armored_bytes(None.into()).unwrap();
970        // fs::write("./message-rsa.asc", &armored).unwrap();
971
972        let parsed = Message::from_armor_single(&armored[..]).unwrap().0;
973
974        let decrypted = parsed.decrypt(|| "test".into(), &[&skey]).unwrap().0;
975
976        assert_eq!(compressed_msg, decrypted);
977    }
978
979    #[test]
980    fn test_rsa_encryption_seipdv2() {
981        let (skey, _headers) = SignedSecretKey::from_armor_single(
982            fs::File::open("./tests/openpgp-interop/testcases/messages/gnupg-v1-001-decrypt.asc")
983                .unwrap(),
984        )
985        .unwrap();
986
987        // subkey[0] is the encryption key
988        let pkey = skey.secret_subkeys[0].public_key();
989        let mut rng = rand::rngs::StdRng::seed_from_u64(100);
990        let mut rng2 = rand::rngs::StdRng::seed_from_u64(100);
991
992        let lit_msg = Message::new_literal("hello.txt", "hello world\n");
993        let compressed_msg = lit_msg.compress(CompressionAlgorithm::ZLIB).unwrap();
994
995        // Encrypt and test that rng is the only source of randomness.
996        let encrypted = compressed_msg
997            .encrypt_to_keys_seipdv2(
998                &mut rng,
999                SymmetricKeyAlgorithm::AES128,
1000                AeadAlgorithm::Ocb,
1001                0x06,
1002                &[&pkey][..],
1003            )
1004            .unwrap();
1005        let encrypted2 = compressed_msg
1006            .encrypt_to_keys_seipdv2(
1007                &mut rng2,
1008                SymmetricKeyAlgorithm::AES128,
1009                AeadAlgorithm::Ocb,
1010                0x06,
1011                &[&pkey][..],
1012            )
1013            .unwrap();
1014        assert_eq!(encrypted, encrypted2);
1015
1016        let armored = encrypted.to_armored_bytes(None.into()).unwrap();
1017        // fs::write("./message-rsa.asc", &armored).unwrap();
1018
1019        let parsed = Message::from_armor_single(&armored[..]).unwrap().0;
1020
1021        let decrypted = parsed.decrypt(|| "test".into(), &[&skey]).unwrap().0;
1022
1023        assert_eq!(compressed_msg, decrypted);
1024    }
1025
1026    #[test]
1027    fn test_x25519_encryption_seipdv1() {
1028        let (skey, _headers) = SignedSecretKey::from_armor_single(
1029            fs::File::open("./tests/autocrypt/alice@autocrypt.example.sec.asc").unwrap(),
1030        )
1031        .unwrap();
1032
1033        // subkey[0] is the encryption key
1034        let pkey = skey.secret_subkeys[0].public_key();
1035        let mut rng = ChaCha8Rng::seed_from_u64(0);
1036
1037        let lit_msg = Message::new_literal("hello.txt", "hello world\n");
1038        let compressed_msg = lit_msg.compress(CompressionAlgorithm::ZLIB).unwrap();
1039        for _ in 0..1000 {
1040            let encrypted = compressed_msg
1041                .encrypt_to_keys_seipdv1(&mut rng, SymmetricKeyAlgorithm::AES128, &[&pkey][..])
1042                .unwrap();
1043
1044            let armored = encrypted.to_armored_bytes(None.into()).unwrap();
1045            // fs::write("./message-x25519.asc", &armored).unwrap();
1046
1047            let parsed = Message::from_armor_single(&armored[..]).unwrap().0;
1048
1049            let decrypted = parsed.decrypt(|| "".into(), &[&skey]).unwrap().0;
1050
1051            assert_eq!(compressed_msg, decrypted);
1052        }
1053    }
1054
1055    #[test]
1056    fn test_x25519_encryption_seipdv2() {
1057        let (skey, _headers) = SignedSecretKey::from_armor_single(
1058            fs::File::open("./tests/autocrypt/alice@autocrypt.example.sec.asc").unwrap(),
1059        )
1060        .unwrap();
1061
1062        // subkey[0] is the encryption key
1063        let pkey = skey.secret_subkeys[0].public_key();
1064        let mut rng = ChaCha8Rng::seed_from_u64(0);
1065
1066        let lit_msg = Message::new_literal("hello.txt", "hello world\n");
1067        let compressed_msg = lit_msg.compress(CompressionAlgorithm::ZLIB).unwrap();
1068
1069        for aead in [AeadAlgorithm::Ocb, AeadAlgorithm::Eax, AeadAlgorithm::Gcm] {
1070            for sym in [
1071                SymmetricKeyAlgorithm::MKV128256,
1072                SymmetricKeyAlgorithm::MKV128192,
1073                SymmetricKeyAlgorithm::MKV128128,
1074                SymmetricKeyAlgorithm::AES128,
1075                SymmetricKeyAlgorithm::AES192,
1076                SymmetricKeyAlgorithm::AES256,
1077            ] {
1078                for _ in 0..1000 {
1079                    let encrypted = compressed_msg
1080                        .encrypt_to_keys_seipdv2(&mut rng, sym, aead, 0x06, &[&pkey][..])
1081                        .unwrap();
1082
1083                    let armored = encrypted.to_armored_bytes(None.into()).unwrap();
1084                    // fs::write("./message-x25519.asc", &armored).unwrap();
1085
1086                    let parsed = Message::from_armor_single(&armored[..]).unwrap().0;
1087
1088                    let decrypted = parsed.decrypt(|| "".into(), &[&skey]).unwrap().0;
1089
1090                    assert_eq!(compressed_msg, decrypted);
1091                }
1092            }
1093        }
1094    }
1095
1096    #[test]
1097    fn test_password_encryption_seipdv1() {
1098        let _ = pretty_env_logger::try_init();
1099
1100        let mut rng = ChaCha8Rng::seed_from_u64(0);
1101
1102        let lit_msg = Message::new_literal("hello.txt", "hello world\n");
1103        let compressed_msg = lit_msg.compress(CompressionAlgorithm::ZLIB).unwrap();
1104
1105        let s2k = StringToKey::new_default(&mut rng);
1106
1107        let encrypted = compressed_msg
1108            .encrypt_with_password_seipdv1(&mut rng, s2k, SymmetricKeyAlgorithm::AES128, || {
1109                "secret".into()
1110            })
1111            .unwrap();
1112
1113        let armored = encrypted.to_armored_bytes(None.into()).unwrap();
1114        // fs::write("./message-password.asc", &armored).unwrap();
1115
1116        let parsed = Message::from_armor_single(&armored[..]).unwrap().0;
1117
1118        let decrypted = parsed.decrypt_with_password(|| "secret".into()).unwrap();
1119
1120        assert_eq!(compressed_msg, decrypted);
1121    }
1122
1123    #[test]
1124    fn test_password_encryption_seipdv2() {
1125        let _ = pretty_env_logger::try_init();
1126
1127        let mut rng = ChaCha8Rng::seed_from_u64(0);
1128
1129        let lit_msg = Message::new_literal("hello.txt", "hello world\n");
1130        let compressed_msg = lit_msg.compress(CompressionAlgorithm::ZLIB).unwrap();
1131
1132        for aead in [AeadAlgorithm::Ocb, AeadAlgorithm::Eax, AeadAlgorithm::Gcm] {
1133            for sym in [
1134                SymmetricKeyAlgorithm::MKV128256,
1135                SymmetricKeyAlgorithm::MKV128192,
1136                SymmetricKeyAlgorithm::MKV128128,
1137                SymmetricKeyAlgorithm::AES128,
1138                SymmetricKeyAlgorithm::AES192,
1139                SymmetricKeyAlgorithm::AES256,
1140            ] {
1141                let s2k = StringToKey::new_default(&mut rng);
1142
1143                let encrypted = compressed_msg
1144                    .encrypt_with_password_seipdv2(&mut rng, s2k, sym, aead, 0x06, || {
1145                        "secret".into()
1146                    })
1147                    .unwrap();
1148
1149                let armored = encrypted.to_armored_bytes(None.into()).unwrap();
1150                // fs::write("./message-password.asc", &armored).unwrap();
1151
1152                let parsed = Message::from_armor_single(&armored[..]).unwrap().0;
1153
1154                let decrypted = parsed.decrypt_with_password(|| "secret".into()).unwrap();
1155
1156                assert_eq!(compressed_msg, decrypted);
1157            }
1158        }
1159    }
1160
1161    #[test]
1162    fn test_no_plaintext_decryption() {
1163        // Invalid message "encrypted" with plaintext algorithm.
1164        // Generated with the Python script below.
1165        let msg_raw = b"\xc3\x04\x04\x00\x00\x08\xd2-\x01\x00\x00\xcb\x12b\x00\x00\x00\x00\x00Hello world!\xd3\x14\xc3\xadw\x022\x05\x0ek'k\x8d\x12\xaa8\r'\x8d\xc0\x82)";
1166        /*
1167                import hashlib
1168                import sys
1169                data = (
1170                    b"\xc3"  # PTag = 11000011, new packet format, tag 3 = SKESK
1171                    b"\x04"  # Packet length, 4
1172                    b"\x04"  # Version number, 4
1173                    b"\x00"  # Algorithm, plaintext
1174                    b"\x00\x08"  # S2K specifier, Simple S2K, SHA256
1175                    b"\xd2"  # PTag = 1101 0010, new packet format, tag 18 = SEIPD
1176                    b"\x2d"  # Packet length, 45
1177                    b"\x01"  # Version number, 1
1178                )
1179                inner_data = (
1180                    b"\x00\x00"  # IV
1181                    b"\xcb"  # PTag = 11001011, new packet format, tag 11 = literal data packet
1182                    b"\x12"  # Packet length, 18
1183                    b"\x62"  # Binary data ('b')
1184                    b"\x00"  # No filename, empty filename length
1185                    b"\x00\x00\x00\x00"  # Date
1186                    b"Hello world!"
1187                )
1188                data += inner_data
1189                data += (
1190                    b"\xd3"  # Modification Detection Code packet, tag 19
1191                    b"\x14"  # MDC packet length, 20 bytes
1192                )
1193                data += hashlib.new("SHA1", inner_data + b"\xd3\x14").digest()
1194                print(data)
1195        */
1196
1197        let msg = Message::from_bytes(&msg_raw[..]).unwrap();
1198
1199        // Before the fix message eventually decrypted to
1200        //   Literal(LiteralData { packet_version: New, mode: Binary, created: 1970-01-01T00:00:00Z, file_name: "", data: "48656c6c6f20776f726c6421" })
1201        // where "48656c6c6f20776f726c6421" is an encoded "Hello world!" string.
1202        assert!(msg
1203            .decrypt_with_password(|| "foobarbaz".into())
1204            .err()
1205            .unwrap()
1206            .to_string()
1207            .contains("plaintext"));
1208    }
1209
1210    #[test]
1211    fn test_x25519_signing_string() {
1212        let (skey, _headers) = SignedSecretKey::from_armor_single(
1213            fs::File::open("./tests/autocrypt/alice@autocrypt.example.sec.asc").unwrap(),
1214        )
1215        .unwrap();
1216
1217        let pkey = skey.public_key();
1218        let mut rng = ChaCha8Rng::seed_from_u64(0);
1219
1220        let lit_msg = Message::new_literal("hello.txt", "hello world\n");
1221        assert!(lit_msg.verify(&pkey).is_err()); // Unsigned message shouldn't verify
1222
1223        let signed_msg = lit_msg
1224            .sign(&mut rng, &skey, || "".into(), HashAlgorithm::SHA2_256)
1225            .unwrap();
1226
1227        let armored = signed_msg.to_armored_bytes(None.into()).unwrap();
1228        // fs::write("./message-string-signed-x25519.asc", &armored).unwrap();
1229
1230        signed_msg.verify(&pkey).unwrap();
1231
1232        let parsed = Message::from_armor_single(&armored[..]).unwrap().0;
1233        parsed.verify(&pkey).unwrap();
1234    }
1235
1236    #[test]
1237    fn test_x25519_signing_bytes() {
1238        let (skey, _headers) = SignedSecretKey::from_armor_single(
1239            fs::File::open("./tests/autocrypt/alice@autocrypt.example.sec.asc").unwrap(),
1240        )
1241        .unwrap();
1242
1243        let pkey = skey.public_key();
1244        let mut rng = ChaCha8Rng::seed_from_u64(0);
1245
1246        let lit_msg = Message::new_literal_bytes("hello.txt", &b"hello world\n"[..]);
1247        let signed_msg = lit_msg
1248            .sign(&mut rng, &skey, || "".into(), HashAlgorithm::SHA2_256)
1249            .unwrap();
1250
1251        let armored = signed_msg.to_armored_bytes(None.into()).unwrap();
1252        // fs::write("./message-bytes-signed-x25519.asc", &armored).unwrap();
1253
1254        signed_msg.verify(&pkey).unwrap();
1255
1256        let parsed = Message::from_armor_single(&armored[..]).unwrap().0;
1257        parsed.verify(&pkey).unwrap();
1258    }
1259
1260    #[test]
1261    fn test_x25519_signing_bytes_compressed() {
1262        let (skey, _headers) = SignedSecretKey::from_armor_single(
1263            fs::File::open("./tests/autocrypt/alice@autocrypt.example.sec.asc").unwrap(),
1264        )
1265        .unwrap();
1266
1267        let pkey = skey.public_key();
1268        let mut rng = ChaCha8Rng::seed_from_u64(0);
1269
1270        let lit_msg = Message::new_literal_bytes("hello.txt", &b"hello world\n"[..]);
1271        let signed_msg = lit_msg
1272            .sign(&mut rng, &skey, || "".into(), HashAlgorithm::SHA2_256)
1273            .unwrap();
1274        let compressed_msg = signed_msg.compress(CompressionAlgorithm::ZLIB).unwrap();
1275
1276        let armored = compressed_msg.to_armored_bytes(None.into()).unwrap();
1277        // fs::write("./message-bytes-compressed-signed-x25519.asc", &armored).unwrap();
1278
1279        signed_msg.verify(&pkey).unwrap();
1280
1281        let parsed = Message::from_armor_single(&armored[..]).unwrap().0;
1282        parsed.verify(&pkey).unwrap();
1283    }
1284
1285    #[test]
1286    fn test_rsa_signing_string() {
1287        for _ in 0..100 {
1288            let (skey, _headers) = SignedSecretKey::from_armor_single(
1289                fs::File::open(
1290                    "./tests/openpgp-interop/testcases/messages/gnupg-v1-001-decrypt.asc",
1291                )
1292                .unwrap(),
1293            )
1294            .unwrap();
1295
1296            let pkey = skey.public_key();
1297            let mut rng = ChaCha8Rng::seed_from_u64(0);
1298
1299            let lit_msg = Message::new_literal("hello.txt", "hello world\n");
1300            let signed_msg = lit_msg
1301                .sign(&mut rng, &skey, || "test".into(), HashAlgorithm::SHA2_256)
1302                .unwrap();
1303
1304            let armored = signed_msg.to_armored_bytes(None.into()).unwrap();
1305            // fs::write("./message-string-signed-rsa.asc", &armored).unwrap();
1306
1307            signed_msg.verify(&pkey).unwrap();
1308
1309            let parsed = Message::from_armor_single(&armored[..]).unwrap().0;
1310            parsed.verify(&pkey).unwrap();
1311        }
1312    }
1313
1314    #[test]
1315    fn test_rsa_signing_bytes() {
1316        let (skey, _headers) = SignedSecretKey::from_armor_single(
1317            fs::File::open("./tests/openpgp-interop/testcases/messages/gnupg-v1-001-decrypt.asc")
1318                .unwrap(),
1319        )
1320        .unwrap();
1321
1322        let pkey = skey.public_key();
1323        let mut rng = ChaCha8Rng::seed_from_u64(0);
1324
1325        let lit_msg = Message::new_literal_bytes("hello.txt", &b"hello world\n"[..]);
1326        let signed_msg = lit_msg
1327            .sign(&mut rng, &skey, || "test".into(), HashAlgorithm::SHA2_256)
1328            .unwrap();
1329
1330        let armored = signed_msg.to_armored_bytes(None.into()).unwrap();
1331        // fs::write("./message-bytes-signed-rsa.asc", &armored).unwrap();
1332
1333        signed_msg.verify(&pkey).unwrap();
1334
1335        let parsed = Message::from_armor_single(&armored[..]).unwrap().0;
1336        parsed.verify(&pkey).unwrap();
1337    }
1338
1339    #[test]
1340    fn test_rsa_signing_bytes_compressed() {
1341        let (skey, _headers) = SignedSecretKey::from_armor_single(
1342            fs::File::open("./tests/openpgp-interop/testcases/messages/gnupg-v1-001-decrypt.asc")
1343                .unwrap(),
1344        )
1345        .unwrap();
1346
1347        let pkey = skey.public_key();
1348        let mut rng = ChaCha8Rng::seed_from_u64(0);
1349
1350        let lit_msg = Message::new_literal_bytes("hello.txt", &b"hello world\n"[..]);
1351        let signed_msg = lit_msg
1352            .sign(&mut rng, &skey, || "test".into(), HashAlgorithm::SHA2_256)
1353            .unwrap();
1354
1355        let compressed_msg = signed_msg.compress(CompressionAlgorithm::ZLIB).unwrap();
1356        let armored = compressed_msg.to_armored_bytes(None.into()).unwrap();
1357        // fs::write("./message-bytes-compressed-signed-rsa.asc", &armored).unwrap();
1358
1359        signed_msg.verify(&pkey).unwrap();
1360
1361        let parsed = Message::from_armor_single(&armored[..]).unwrap().0;
1362        parsed.verify(&pkey).unwrap();
1363    }
1364
1365    #[test]
1366    fn test_text_signature_normalization() {
1367        // Test verifying an inlined signed message.
1368        //
1369        // The signature type is 0x01 ("Signature of a canonical text document").
1370        //
1371        // The literal data packet (which is in binary mode) contains the output of:
1372        // echo -en "foo\nbar\r\nbaz"
1373        //
1374        // RFC 9580 mandates that the hash for signature type 0x01 has to be calculated over normalized line endings,
1375        // so the hash for this message is calculated over "foo\r\nbar\r\nbaz".
1376        //
1377        // So it must also be verified against a hash digest over this normalized format.
1378        let (signed_msg, _header) = Message::from_armor_single(
1379            fs::File::open("./tests/unit-tests/text_signature_normalization.msg").unwrap(),
1380        )
1381        .unwrap();
1382
1383        let (skey, _headers) = SignedSecretKey::from_armor_single(
1384            fs::File::open("./tests/unit-tests/text_signature_normalization_alice.key").unwrap(),
1385        )
1386        .unwrap();
1387
1388        // Manually find the signing subkey
1389        let signing = skey
1390            .secret_subkeys
1391            .iter()
1392            .find(|key| {
1393                key.key_id()
1394                    == KeyId::from_slice(&[0x64, 0x35, 0x7E, 0xB6, 0xBB, 0x55, 0xDE, 0x12]).unwrap()
1395            })
1396            .unwrap();
1397
1398        // And transform it into a public subkey for signature verification
1399        let verify = signing.public_key();
1400
1401        // verify the signature with alice's signing subkey
1402        signed_msg.verify(&verify).expect("signature seems bad");
1403    }
1404
1405    /// Tests that decompressing compression quine does not result in stack overflow.
1406    /// quine.out comes from <https://mumble.net/~campbell/misc/pgp-quine/>
1407    /// See <https://mumble.net/~campbell/2013/10/08/compression> for details.
1408    #[test]
1409    fn test_compression_quine() {
1410        // Public key does not matter as the message is not signed.
1411        let (skey, _headers) = SignedSecretKey::from_armor_single(
1412            fs::File::open("./tests/autocrypt/alice@autocrypt.example.sec.asc").unwrap(),
1413        )
1414        .unwrap();
1415        let pkey = skey.public_key();
1416
1417        let msg = Message::from_bytes(&include_bytes!("../../../tests/quine.out")[..]).unwrap();
1418        assert!(msg.get_content().is_err());
1419        assert!(msg.verify(&pkey).is_err());
1420    }
1421
1422    // Sample Version 6 Certificate (Transferable Public Key)
1423    // https://www.rfc-editor.org/rfc/rfc9580.html#name-sample-version-6-certificat
1424    const ANNEX_A_3: &str = "-----BEGIN PGP PUBLIC KEY BLOCK-----
1425
1426xioGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laPCsQYf
1427GwoAAABCBYJjh3/jAwsJBwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxy
1428KwwfHifBilZwj2Ul7Ce62azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lw
1429gyU2kCcUmKfvBXbAf6rhRYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaE
1430QsiPlR4zxP/TP7mhfVEe7XWPxtnMUMtf15OyA51YBM4qBmOHf+MZAAAAIIaTJINn
1431+eUBXbki+PSAld2nhJh/LVmFsS+60WyvXkQ1wpsGGBsKAAAALAWCY4d/4wKbDCIh
1432BssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce62azJAAAAAAQBIKbpGG2dWTX8
1433j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDEM0g12vYxoWM8Y81W+bHBw805
1434I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUrk0mXubZvyl4GBg==
1435-----END PGP PUBLIC KEY BLOCK-----";
1436
1437    // Sample Version 6 Secret Key (Transferable Secret Key)
1438    // https://www.rfc-editor.org/rfc/rfc9580.html#name-sample-version-6-secret-key
1439    const ANNEX_A_4: &str = "-----BEGIN PGP PRIVATE KEY BLOCK-----
1440
1441xUsGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laMAGXKB
1442exK+cH6NX1hs5hNhIB00TrJmosgv3mg1ditlsLfCsQYfGwoAAABCBYJjh3/jAwsJ
1443BwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6
14442azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lwgyU2kCcUmKfvBXbAf6rh
1445RYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaEQsiPlR4zxP/TP7mhfVEe
14467XWPxtnMUMtf15OyA51YBMdLBmOHf+MZAAAAIIaTJINn+eUBXbki+PSAld2nhJh/
1447LVmFsS+60WyvXkQ1AE1gCk95TUR3XFeibg/u/tVY6a//1q0NWC1X+yui3O24wpsG
1448GBsKAAAALAWCY4d/4wKbDCIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6
14492azJAAAAAAQBIKbpGG2dWTX8j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDE
1450M0g12vYxoWM8Y81W+bHBw805I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUr
1451k0mXubZvyl4GBg==
1452-----END PGP PRIVATE KEY BLOCK-----";
1453
1454    /// Verify Cleartext Signed Message
1455    ///
1456    /// Test data from RFC 9580, see
1457    /// https://www.rfc-editor.org/rfc/rfc9580.html#name-sample-cleartext-signed-mes
1458    #[test]
1459    fn test_v6_annex_a_6() {
1460        let (ssk, _) = SignedPublicKey::from_string(ANNEX_A_3).expect("SSK from armor");
1461
1462        let msg = "-----BEGIN PGP SIGNED MESSAGE-----
1463
1464What we need from the grocery store:
1465
1466- - tofu
1467- - vegetables
1468- - noodles
1469
1470-----BEGIN PGP SIGNATURE-----
1471
1472wpgGARsKAAAAKQWCY5ijYyIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6
14732azJAAAAAGk2IHZJX1AhiJD39eLuPBgiUU9wUA9VHYblySHkBONKU/usJ9BvuAqo
1474/FvLFuGWMbKAdA+epq7V4HOtAPlBWmU8QOd6aud+aSunHQaaEJ+iTFjP2OMW0KBr
1475NK2ay45cX1IVAQ==
1476-----END PGP SIGNATURE-----";
1477
1478        let (msg, _) = CleartextSignedMessage::from_string(msg).unwrap();
1479
1480        msg.verify(&ssk).expect("verify");
1481    }
1482
1483    /// Verify Inline Signed Message
1484    ///
1485    /// Test data from RFC 9580, see
1486    /// https://www.rfc-editor.org/rfc/rfc9580.html#name-sample-inline-signed-messag
1487    #[test]
1488    fn test_v6_annex_a_7() {
1489        let (ssk, _) = SignedPublicKey::from_string(ANNEX_A_3).expect("SSK from armor");
1490
1491        let msg = "-----BEGIN PGP MESSAGE-----
1492
1493xEYGAQobIHZJX1AhiJD39eLuPBgiUU9wUA9VHYblySHkBONKU/usyxhsTwYJppfk
14941S36bHIrDB8eJ8GKVnCPZSXsJ7rZrMkBy0p1AAAAAABXaGF0IHdlIG5lZWQgZnJv
1495bSB0aGUgZ3JvY2VyeSBzdG9yZToKCi0gdG9mdQotIHZlZ2V0YWJsZXMKLSBub29k
1496bGVzCsKYBgEbCgAAACkFgmOYo2MiIQbLGGxPBgmml+TVLfpscisMHx4nwYpWcI9l
1497JewnutmsyQAAAABpNiB2SV9QIYiQ9/Xi7jwYIlFPcFAPVR2G5ckh5ATjSlP7rCfQ
1498b7gKqPxbyxbhljGygHQPnqau1eBzrQD5QVplPEDnemrnfmkrpx0GmhCfokxYz9jj
1499FtCgazStmsuOXF9SFQE=
1500-----END PGP MESSAGE-----";
1501
1502        let (msg, _) = Message::from_string(msg).unwrap();
1503
1504        msg.verify(&ssk).expect("verify");
1505    }
1506
1507    /// Decrypt an X25519-AEAD-OCB Encrypted Packet Sequence
1508    ///
1509    /// Test data from RFC 9580, see
1510    /// https://www.rfc-editor.org/rfc/rfc9580.html#name-sample-x25519-aead-ocb-encr
1511    #[test]
1512    fn test_v6_annex_a_8() {
1513        let (ssk, _) = SignedSecretKey::from_string(ANNEX_A_4).expect("SSK from armor");
1514
1515        // A.8. Sample X25519-AEAD-OCB Decryption
1516        let msg = "-----BEGIN PGP MESSAGE-----
1517
1518wV0GIQYSyD8ecG9jCP4VGkF3Q6HwM3kOk+mXhIjR2zeNqZMIhRmHzxjV8bU/gXzO
1519WgBM85PMiVi93AZfJfhK9QmxfdNnZBjeo1VDeVZheQHgaVf7yopqR6W1FT6NOrfS
1520aQIHAgZhZBZTW+CwcW1g4FKlbExAf56zaw76/prQoN+bAzxpohup69LA7JW/Vp0l
1521yZnuSj3hcFj0DfqLTGgr4/u717J+sPWbtQBfgMfG9AOIwwrUBqsFE9zW+f1zdlYo
1522bhF30A+IitsxxA==
1523-----END PGP MESSAGE-----";
1524
1525        let (message, _) = Message::from_string(msg).expect("ok");
1526        let (dec, _) = message.decrypt(String::default, &[&ssk]).expect("decrypt");
1527
1528        let decrypted =
1529            String::from_utf8(dec.get_literal().expect("literal").data().to_vec()).expect("utf8");
1530
1531        assert_eq!(&decrypted, "Hello, world!");
1532    }
1533}