cmail_rpgp/composed/message/
parser.rs

1use std::iter::Peekable;
2
3use log::debug;
4
5use crate::armor::BlockType;
6use crate::composed::message::Message;
7use crate::composed::Deserializable;
8use crate::errors::{Error, Result};
9use crate::packet::Packet;
10use crate::types::{PkeskVersion, SkeskVersion, Tag};
11use crate::{Edata, Esk};
12
13pub struct MessageParser<I: Sized + Iterator<Item = Result<Packet>>> {
14    source: Peekable<I>,
15}
16
17fn next<I: Iterator<Item = Result<Packet>>>(packets: &mut Peekable<I>) -> Option<Result<Message>> {
18    while let Some(res) = packets.by_ref().next() {
19        let packet = match res {
20            Ok(packet) => packet,
21            Err(err) => return Some(Err(err)),
22        };
23
24        debug!("{:?}: ", packet);
25        let tag = packet.tag();
26        match tag {
27            Tag::LiteralData => {
28                return match packet.try_into() {
29                    Ok(data) => Some(Ok(Message::Literal(data))),
30                    Err(err) => Some(Err(err)),
31                };
32            }
33            Tag::CompressedData => {
34                return match packet.try_into() {
35                    Ok(data) => Some(Ok(Message::Compressed(data))),
36                    Err(err) => Some(Err(err)),
37                };
38            }
39            //    ESK :- Public-Key Encrypted Session Key Packet |
40            //           Symmetric-Key Encrypted Session Key Packet.
41            Tag::PublicKeyEncryptedSessionKey | Tag::SymKeyEncryptedSessionKey => {
42                return match packet.try_into() {
43                    Ok(p) => {
44                        let mut esk: Vec<Esk> = vec![p];
45
46                        // while ESK take em
47                        while let Some(res) = packets.next_if(|res| {
48                            res.as_ref().is_ok_and(|p| {
49                                p.tag() == Tag::PublicKeyEncryptedSessionKey
50                                    || p.tag() == Tag::SymKeyEncryptedSessionKey
51                            })
52                        }) {
53                            match res {
54                                Ok(packet) => esk.push(packet.try_into().expect("peeked")),
55                                Err(e) => return Some(Err(e)),
56                            }
57                        }
58
59                        // we expect exactly one edata after the ESKs
60                        let edata = match packets.next() {
61                            Some(Ok(p))
62                                if p.tag() == Tag::SymEncryptedData
63                                    || p.tag() == Tag::SymEncryptedProtectedData =>
64                            {
65                                Edata::try_from(p).expect("peeked")
66                            }
67                            Some(Ok(p)) => {
68                                return Some(Err(Error::Message(format!(
69                                    "Expected encrypted data packet, but found {:?}",
70                                    p
71                                ))));
72                            }
73                            None => {
74                                return Some(Err(Error::Message(
75                                    "Missing encrypted data packet".to_string(),
76                                )))
77                            }
78                            Some(Err(e)) => return Some(Err(e)),
79                        };
80
81                        // Drop PKESK and SKESK with versions that are not aligned with the encryption container
82                        fn esk_filter(
83                            esk: Vec<Esk>,
84                            pkesk_allowed: PkeskVersion,
85                            skesk_allowed: SkeskVersion,
86                        ) -> Vec<Esk> {
87                            esk.into_iter()
88                                .filter(|esk| match esk {
89                                    Esk::PublicKeyEncryptedSessionKey(pkesk) => {
90                                        pkesk.version() == pkesk_allowed
91                                    }
92                                    Esk::SymKeyEncryptedSessionKey(skesk) => {
93                                        skesk.version() == skesk_allowed
94                                    }
95                                })
96                                .collect()
97                        }
98
99                        // An implementation processing an Encrypted Message MUST discard any
100                        // preceding ESK packet with a version that does not align with the
101                        // version of the payload.
102                        // (See https://www.rfc-editor.org/rfc/rfc9580.html#section-10.3.2.1-7)
103                        let esk = match edata {
104                            Edata::SymEncryptedData(_) => {
105                                esk_filter(esk, PkeskVersion::V3, SkeskVersion::V4)
106                            }
107                            Edata::SymEncryptedProtectedData(ref p) if p.version() == 1 => {
108                                esk_filter(esk, PkeskVersion::V3, SkeskVersion::V4)
109                            }
110                            Edata::SymEncryptedProtectedData(ref p) if p.version() == 2 => {
111                                esk_filter(esk, PkeskVersion::V6, SkeskVersion::V6)
112                            }
113                            _ => {
114                                return Some(Err(format_err!("Unsupported Edata variant")));
115                            }
116                        };
117
118                        Some(Ok(Message::Encrypted { esk, edata }))
119                    }
120                    Err(err) => Some(Err(err)),
121                };
122            }
123            Tag::Signature => {
124                return match packet.try_into() {
125                    Ok(signature) => {
126                        let message = match next(packets.by_ref()) {
127                            Some(Ok(m)) => Some(Box::new(m)),
128                            Some(Err(err)) => return Some(Err(err)),
129                            None => None,
130                        };
131
132                        Some(Ok(Message::Signed {
133                            message,
134                            one_pass_signature: None,
135                            signature,
136                        }))
137                    }
138                    Err(err) => Some(Err(err)),
139                };
140            }
141            Tag::OnePassSignature => {
142                return match packet.try_into() {
143                    Ok(p) => {
144                        let one_pass_signature = Some(p);
145
146                        let message = match next(packets.by_ref()) {
147                            Some(Ok(m)) => Some(Box::new(m)),
148                            Some(Err(err)) => return Some(Err(err)),
149                            None => None,
150                        };
151
152                        let signature = if let Some(res) = packets
153                            .next_if(|res| res.as_ref().is_ok_and(|p| p.tag() == Tag::Signature))
154                        {
155                            match res {
156                                Ok(packet) => packet.try_into().expect("peeked"),
157                                Err(e) => return Some(Err(e)),
158                            }
159                        } else {
160                            return Some(Err(format_err!(
161                                "missing signature for, one pass signature"
162                            )));
163                        };
164
165                        Some(Ok(Message::Signed {
166                            message,
167                            one_pass_signature,
168                            signature,
169                        }))
170                    }
171                    Err(err) => Some(Err(err)),
172                };
173            }
174            Tag::Marker => {
175                // Marker Packets are ignored
176                // see https://www.rfc-editor.org/rfc/rfc9580.html#marker-packet
177            }
178            Tag::Padding => {
179                // Padding Packets are ignored
180                //
181                // "Such a packet MUST be ignored when received."
182                // (See https://www.rfc-editor.org/rfc/rfc9580.html#section-5.14-2)
183            }
184            _ => {
185                return Some(Err(format_err!("unexpected packet {:?}", packet.tag())));
186            }
187        }
188    }
189
190    None
191}
192
193impl<I: Sized + Iterator<Item = Result<Packet>>> Iterator for MessageParser<I> {
194    type Item = Result<Message>;
195
196    fn next(&mut self) -> Option<Self::Item> {
197        next(self.source.by_ref())
198    }
199}
200
201impl Deserializable for Message {
202    /// Parse a composed message.
203    /// Ref: <https://www.rfc-editor.org/rfc/rfc9580.html#name-openpgp-messages>
204    fn from_packets<'a, I: Iterator<Item = Result<Packet>> + 'a>(
205        packets: std::iter::Peekable<I>,
206    ) -> Box<dyn Iterator<Item = Result<Self>> + 'a> {
207        Box::new(MessageParser {
208            source: packets.peekable(),
209        })
210    }
211
212    fn matches_block_type(typ: BlockType) -> bool {
213        matches!(
214            typ,
215            BlockType::Message | BlockType::MultiPartMessage(_, _) | BlockType::File
216        )
217    }
218}