Skip to main content

pgp/packet/
many.rs

1use std::io::BufRead;
2
3use log::debug;
4
5use crate::{
6    composed::PacketBodyReader,
7    errors::{Error, Result},
8    packet::{Packet, PacketHeader},
9};
10
11/// A PacketParser reads an input stream and produces a series of [`Packet`]s from it.
12pub struct PacketParser<R: BufRead> {
13    /// The reader that gets advanced through the original source
14    reader: R,
15    /// Are we done?
16    is_done: bool,
17}
18
19impl<R: BufRead> PacketParser<R> {
20    pub fn new(source: R) -> Self {
21        PacketParser {
22            reader: source,
23            is_done: false,
24        }
25    }
26
27    pub fn into_inner(self) -> R {
28        self.reader
29    }
30}
31
32impl<R: BufRead> Iterator for PacketParser<R> {
33    type Item = Result<Packet>;
34
35    fn next(&mut self) -> Option<Self::Item> {
36        if self.is_done {
37            return None;
38        }
39
40        let header = match PacketHeader::try_from_reader(&mut self.reader) {
41            Ok(header) => header,
42            Err(err) => {
43                self.is_done = true;
44                if err.kind() == std::io::ErrorKind::UnexpectedEof {
45                    return None;
46                }
47
48                return Some(Err(err.into()));
49            }
50        };
51
52        debug!("found header: {header:?}");
53        let res = PacketBodyReader::new(header, &mut self.reader)
54            .map_err(Error::from)
55            .and_then(|mut body| {
56                match Packet::from_reader(header, &mut body) {
57                    Ok(packet) => Ok(packet),
58                    Err(Error::PacketParsing { source }) if source.is_incomplete() => {
59                        debug!("incomplete packet for: {source:?}");
60                        // not bailing, we are just skipping incomplete bodies
61                        Err(Error::PacketIncomplete { source })
62                    }
63                    Err(err) => Err(err),
64                }
65            });
66        Some(res)
67    }
68}
69
70impl<R: BufRead> PacketParser<R> {
71    pub fn next_ref(&mut self) -> Option<Result<PacketBodyReader<&'_ mut R>>> {
72        if self.is_done {
73            return None;
74        }
75
76        let header = match PacketHeader::try_from_reader(&mut self.reader) {
77            Ok(header) => header,
78            Err(err) => {
79                if err.kind() == std::io::ErrorKind::UnexpectedEof {
80                    return None;
81                }
82
83                self.is_done = true;
84                return Some(Err(err.into()));
85            }
86        };
87
88        debug!("found header: {header:?}");
89        let body = PacketBodyReader::new(header, &mut self.reader).map_err(Into::into);
90        Some(body)
91    }
92
93    pub fn next_owned(mut self) -> Option<Result<PacketBodyReader<R>>> {
94        if self.is_done {
95            return None;
96        }
97
98        let header = match PacketHeader::try_from_reader(&mut self.reader) {
99            Ok(header) => header,
100            Err(err) => {
101                self.is_done = true;
102                return Some(Err(err.into()));
103            }
104        };
105
106        debug!("found header: {header:?}");
107        let body = PacketBodyReader::new(header, self.reader).map_err(Into::into);
108        Some(body)
109    }
110}
111
112#[cfg(test)]
113mod tests {
114    use std::{
115        fs::File,
116        io::{BufRead, BufReader, Read, Seek, SeekFrom},
117        path::Path,
118    };
119
120    use log::warn;
121    use regex::Regex;
122
123    use super::*;
124    use crate::{packet::PacketTrait, ser::Serialize, types::Tag};
125
126    #[test]
127    #[ignore]
128    fn test_packet_roundtrip_0001() {
129        packet_roundtrip(
130            "0001",
131            vec![
132                (556, 6 + 2224),
133                (805, 6 + 95),
134                (806, 6 + 6495),
135                (1027, 6 + 6246),
136                (1074, 2490),
137                (1331, 838),
138                (1898, 6420),
139                (1935, 3583),
140            ],
141        )
142    }
143
144    #[test]
145    #[ignore]
146    fn test_packet_roundtrip_0002() {
147        packet_roundtrip(
148            "0002",
149            vec![
150                (82, 199),    // invalid hash alg 06
151                (85, 196),    // invalid hash alg 06
152                (836, 3136),  // non canonical length encoding
153                (1200, 2772), // non canonical length encoding
154                (1268, 1223), // non canonical length encoding
155                (1670, 3419), // non canonical length encoding
156            ],
157        )
158    }
159
160    #[test]
161    #[ignore]
162    fn test_packet_roundtrip_0009() {
163        packet_roundtrip(
164            "0009",
165            vec![
166                (37, 3960),   // non canonical length encoding
167                (39, 3960),   // non canonical length encoding
168                (258, 75),    // non canonical length encoding
169                (260, 78),    // non canonical length encoding
170                (1053, 3181), // non canonical length encoding
171                (1473, 5196), // non canonical length encoding
172                (1895, 4243), // non canonical length encoding
173            ],
174        )
175    }
176
177    fn packet_roundtrip(dump: &str, skips: Vec<(usize, i64)>) {
178        let _ = pretty_env_logger::try_init();
179
180        let path = format!("./tests/tests/sks-dump/{dump}.pgp");
181        let p = Path::new(&path);
182        let mut file = BufReader::new(std::fs::File::open(p).unwrap());
183        let mut bytes = File::open(p).unwrap();
184
185        let packets = PacketParser::new(&mut file);
186
187        for (i, packet) in packets.take(2000).enumerate() {
188            // packets we need to skip, because we can not roundtrip them for some reason
189            if let Some((_, size)) = skips.iter().find(|(j, _)| *j == i) {
190                bytes.seek(SeekFrom::Current(*size)).unwrap();
191                continue;
192            }
193
194            let packet = packet.expect("invalid packet");
195            let mut buf = Vec::new();
196            packet
197                .to_writer(&mut buf)
198                .expect("failed to serialize packet");
199
200            let mut expected_buf = vec![0u8; buf.len()];
201            assert_eq!(bytes.read(&mut expected_buf).unwrap(), buf.len());
202            // println!("\n-- packet: {} expected size: {}", i, expected_buf.len());
203
204            if buf != expected_buf {
205                assert_eq!(hex::encode(buf), hex::encode(expected_buf));
206            }
207        }
208    }
209
210    #[test]
211    #[ignore]
212    fn test_many_parser() {
213        let _ = pretty_env_logger::try_init();
214
215        let p = Path::new("./tests/tests/sks-dump/0000.pgp");
216        let file = BufReader::new(std::fs::File::open(p).unwrap());
217
218        // list of expected tags
219        // this file is built by
220        // `gpg --list-packets tests/tests/sks-dump/0000.pgp`
221        let fixture = File::open("./tests/tests/sks-dump/0000_parsed.txt").unwrap();
222        let re = Regex::new(r"^#\soff=(\d+)\sctb=[[:alpha:]\d]+\stag=(\d+)\s.*").unwrap();
223        let expected_tags = BufReader::new(fixture)
224            .lines()
225            .filter(|line| line.as_ref().unwrap().starts_with("# off"))
226            .map(|line| {
227                let (offset, tag) = {
228                    let cap = re.captures(line.as_ref().unwrap()).unwrap();
229                    (cap[1].to_string(), cap[2].to_string())
230                };
231
232                (offset, tag, line)
233            })
234            .filter(|(offset, _, _)| {
235                let list = [
236                    "1193538",  // invalid mpi
237                    "9218758",  // invalid packet length
238                    "6844449",  // RSA public exponent too large
239                    "24798372", // TODO: unclear why this public sub key fails to parse
240                    "38521947", // RSA public exponent too large
241                    "32162244", // Invalid DSA key
242                    "43825283", // Invalid DSA key
243                    "9745167",  // MPI_NULL
244                    "9797527",  // MPI_NULL
245                    "19045846", // invalid packet length
246                    "19047047", // ?
247                    "3122229",  // RSA >8192
248                    "15412686", // RSA >8192
249                    "15416968", // RSA >8192
250                    "19333639", // RSA >8192
251                    "19336829", // RSA >8192
252                    "19690793", // RSA >8192
253                    "19697083", // RSA >8192
254                    "19703308", // RSA >8192
255                    "20084489", // RSA >8192
256                    "20098904", // RSA >8192
257                    "22420219", // RSA >8192
258                    "22424421", // RSA >8192
259                    "23086701", // RSA >8192
260                    "24016578", // RSA >8192
261                    "24022877", // RSA >8192
262                    "41034801", // RSA >8192
263                ];
264                if list.contains(&offset.as_str()) {
265                    warn!("skipping {offset}");
266                    false
267                } else {
268                    true
269                }
270            });
271
272        let actual_tags = PacketParser::new(file).filter(|p| {
273            p.as_ref()
274                .inspect_err(|e| {
275                    warn!("failed to parse packet: {e:?}");
276                })
277                .is_ok()
278        });
279        let iter = expected_tags.zip(actual_tags);
280
281        for ((_offset, tag, e), packet) in iter {
282            let e = e.as_ref().unwrap();
283            let packet = packet.unwrap();
284
285            println!("-- checking: {:?} {}", packet.tag(), e);
286
287            let tag: Tag = u8::into(tag.parse().unwrap());
288            assert_eq!(tag, packet.tag(), "mismatch in packet {:?} ({})", p, e);
289        }
290    }
291
292    #[test]
293    fn incomplete_packet_parser() {
294        let _ = pretty_env_logger::try_init();
295
296        let bytes = [0x97];
297        let parser = PacketParser::new(&bytes[..]);
298        let mut packets = parser.filter_map(|p| {
299            // for now we are skipping any packets that we failed to parse
300            match p {
301                Ok(pp) => Some(pp),
302                Err(err) => {
303                    log::warn!("skipping packet: {err:?}");
304                    None
305                }
306            }
307        });
308        assert!(packets.next().is_none());
309    }
310
311    #[test]
312    fn test_partial_length_encoding() {
313        let _ = pretty_env_logger::try_init();
314
315        use crate::composed::Message;
316
317        const TEXT: &str = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n";
318
319        for msg_file in [
320            // Literal Data Packet with two octet length encoding
321            "./tests/unit-tests/partial-body-length/literal.packet-two-octet-length.asc",
322            // Literal Data Packet with first partial length of 512 bytes, followed by a part with five octet length encoding
323            "./tests/unit-tests/partial-body-length/literal.packet-partial.512.asc",
324        ] {
325            let (mut message, _headers) =
326                Message::from_armor_file(msg_file).expect("failed to parse message");
327
328            assert!(message.is_literal());
329            assert_eq!(message.as_data_vec().unwrap(), TEXT.as_bytes());
330        }
331
332        // Literal Data Packet with illegal first partial length of 256 bytes
333        let res = Message::from_armor_file(
334            "./tests/unit-tests/partial-body-length/literal.packet-partial.256.asc",
335        );
336
337        #[cfg(not(feature = "malformed-artifact-compat"))]
338        assert!(res.is_err());
339
340        #[cfg(feature = "malformed-artifact-compat")]
341        assert!(res.is_ok());
342    }
343}