cmail_rpgp/composed/
shared.rs

1use std::io::{BufRead, Read};
2
3use buffer_redux::BufReader;
4use log::{debug, warn};
5
6use crate::armor::{self, BlockType};
7use crate::errors::{Error, Result};
8use crate::packet::{Packet, PacketParser};
9
10pub trait Deserializable: Sized {
11    /// Parse a single byte encoded composition.
12    fn from_bytes(bytes: impl Read) -> Result<Self> {
13        let mut el = Self::from_bytes_many(bytes);
14        el.next().ok_or(Error::NoMatchingPacket)?
15    }
16
17    /// Parse a single armor encoded composition.
18    fn from_string(input: &str) -> Result<(Self, armor::Headers)> {
19        let (mut el, headers) = Self::from_string_many(input)?;
20        Ok((el.next().ok_or(Error::NoMatchingPacket)??, headers))
21    }
22
23    /// Parse an armor encoded list of compositions.
24    #[allow(clippy::type_complexity)]
25    fn from_string_many<'a>(
26        input: &'a str,
27    ) -> Result<(Box<dyn Iterator<Item = Result<Self>> + 'a>, armor::Headers)> {
28        Self::from_armor_many_buf(input.as_bytes())
29    }
30
31    /// Armored ascii data.
32    fn from_armor_single<R: Read>(input: R) -> Result<(Self, armor::Headers)> {
33        let (mut el, headers) = Self::from_armor_many(input)?;
34        Ok((el.next().ok_or(Error::NoMatchingPacket)??, headers))
35    }
36
37    /// Armored ascii data.
38    fn from_armor_single_buf<R: BufRead>(input: R) -> Result<(Self, armor::Headers)> {
39        let (mut el, headers) = Self::from_armor_many_buf(input)?;
40        Ok((el.next().ok_or(Error::NoMatchingPacket)??, headers))
41    }
42
43    /// Armored ascii data.
44    #[allow(clippy::type_complexity)]
45    fn from_armor_many<'a, R: Read + 'a>(
46        input: R,
47    ) -> Result<(Box<dyn Iterator<Item = Result<Self>> + 'a>, armor::Headers)> {
48        Self::from_armor_many_buf(BufReader::new(input))
49    }
50
51    #[allow(clippy::type_complexity)]
52    fn from_armor_many_buf<'a, R: BufRead + 'a>(
53        input: R,
54    ) -> Result<(Box<dyn Iterator<Item = Result<Self>> + 'a>, armor::Headers)> {
55        let mut dearmor = armor::Dearmor::new(input);
56        dearmor.read_header()?;
57        // Safe to unwrap, as read_header succeeded.
58        let typ = dearmor
59            .typ
60            .ok_or_else(|| format_err!("dearmor failed to retrieve armor type"))?;
61
62        // TODO: add typ information to the key possibly?
63        match typ {
64            // Standard PGP types
65            BlockType::PublicKey
66            | BlockType::PrivateKey
67            | BlockType::Message
68            | BlockType::MultiPartMessage(_, _)
69            | BlockType::Signature
70            | BlockType::CleartextMessage
71            | BlockType::File => {
72                let headers = dearmor.headers.clone(); // FIXME: avoid clone
73
74                if !Self::matches_block_type(typ) {
75                    bail!("unexpected block type: {}", typ);
76                }
77                Ok((Self::from_bytes_many(dearmor), headers))
78            }
79            BlockType::PublicKeyPKCS1(_)
80            | BlockType::PublicKeyPKCS8
81            | BlockType::PublicKeyOpenssh
82            | BlockType::PrivateKeyPKCS1(_)
83            | BlockType::PrivateKeyPKCS8
84            | BlockType::PrivateKeyOpenssh => {
85                unimplemented_err!("key format {:?}", typ);
86            }
87        }
88    }
89
90    /// Parse a list of compositions in raw byte format.
91    fn from_bytes_many<'a>(bytes: impl Read + 'a) -> Box<dyn Iterator<Item = Result<Self>> + 'a> {
92        let packets = PacketParser::new(bytes).filter_map(filter_parsed_packet_results);
93
94        Self::from_packets(packets.peekable())
95    }
96
97    /// Turn a list of packets into a usable representation.
98    fn from_packets<'a, I: Iterator<Item = Result<Packet>> + 'a>(
99        packets: std::iter::Peekable<I>,
100    ) -> Box<dyn Iterator<Item = Result<Self>> + 'a>;
101
102    /// Check if the given typ is a valid block type for this type.
103    fn matches_block_type(typ: BlockType) -> bool;
104
105    /// Parses a single composition, from either ASCII-armored or binary OpenPGP data.
106    ///
107    /// Returns a composition and a BTreeMap containing armor headers
108    /// (None, if the data was unarmored)
109    #[allow(clippy::type_complexity)]
110    fn from_reader_single<'a, R: Read + 'a>(input: R) -> Result<(Self, Option<armor::Headers>)> {
111        Self::from_reader_single_buf(BufReader::new(input))
112    }
113
114    #[allow(clippy::type_complexity)]
115    fn from_reader_single_buf<'a, R: BufRead + 'a>(
116        mut input: R,
117    ) -> Result<(Self, Option<armor::Headers>)> {
118        if !is_binary(&mut input)? {
119            let (keys, headers) = Self::from_armor_single(input)?;
120            Ok((keys, Some(headers)))
121        } else {
122            Ok((Self::from_bytes(input)?, None))
123        }
124    }
125
126    /// Parses a list of compositions, from either ASCII-armored or binary OpenPGP data.
127    ///
128    /// Returns an iterator of compositions and a BTreeMap containing armor headers
129    /// (None, if the data was unarmored)
130    #[allow(clippy::type_complexity)]
131    fn from_reader_many<'a, R: Read + 'a>(
132        input: R,
133    ) -> Result<(
134        Box<dyn Iterator<Item = Result<Self>> + 'a>,
135        Option<armor::Headers>,
136    )> {
137        Self::from_reader_many_buf(BufReader::new(input))
138    }
139
140    /// Parses a list of compositions, from either ASCII-armored or binary OpenPGP data.
141    ///
142    /// Returns an iterator of compositions and a BTreeMap containing armor headers
143    /// (None, if the data was unarmored)
144    #[allow(clippy::type_complexity)]
145    fn from_reader_many_buf<'a, R: BufRead + 'a>(
146        mut input: R,
147    ) -> Result<(
148        Box<dyn Iterator<Item = Result<Self>> + 'a>,
149        Option<armor::Headers>,
150    )> {
151        if !is_binary(&mut input)? {
152            let (keys, headers) = Self::from_armor_many_buf(input)?;
153            Ok((keys, Some(headers)))
154        } else {
155            Ok((Self::from_bytes_many(input), None))
156        }
157    }
158}
159
160/// Process results from low level packet parser:
161///
162/// - Skip Marker packets.
163/// - Pass through other packets.
164/// - Skip any `Error::Unsupported`, those were marked as "safe to ignore" by the low level parser.
165/// - Skip `Error::Incomplete`
166/// - Skip `Error::EllipticCurve`
167/// - Pass through other errors.
168pub(crate) fn filter_parsed_packet_results(p: Result<Packet>) -> Option<Result<Packet>> {
169    // FIXME: handle padding packets (skip)
170    // FIXME: handle criticality of packets from 9580 (error, if unsupported)
171
172    match &p {
173        Ok(Packet::Marker(_m)) => {
174            debug!("skipping marker packet");
175            None
176        }
177        Ok(_) => Some(p),
178        Err(e) => {
179            if let Error::Unsupported(e) = e {
180                // "Error::Unsupported" signals parser errors that we can safely ignore
181                // (e.g. packets with unsupported versions)
182                warn!("skipping unsupported packet: {p:?}");
183                debug!("error: {e:?}");
184                return None;
185            }
186            if let Error::InvalidPacketContent(b) = &e {
187                let err: &Error = b; // unbox
188                if let Error::Unsupported(e) = err {
189                    // "Error::Unsupported" signals parser errors that we can safely ignore
190                    // (e.g. packets with unsupported versions)
191                    warn!("skipping unsupported packet: {p:?}");
192                    debug!("error: {e:?}");
193                    return None;
194                }
195                if let Error::EllipticCurve(e) = err {
196                    // this error happens in one SKS test key, presumably bad public key material.
197                    // ignoring the packet seems safe.
198                    warn!("skipping bad elliptic curve data: {p:?}");
199                    debug!("error: {e:?}");
200                    return None;
201                }
202            }
203            if let Error::PacketIncomplete = e {
204                // We ignore incomplete packets for now (some of these occur in the SKS dumps under `tests`)
205                warn!("skipping incomplete packet: {p:?}");
206                return None;
207            }
208
209            // Pass through all other errors from the low level parser, they should be surfaced
210            Some(Err(Error::Message(format!(
211                "unexpected packet data: {e:?}"
212            ))))
213        }
214    }
215}
216
217/// Check if the OpenPGP data in `input` seems to be ASCII-armored or binary (by looking at the
218/// highest bit of the first byte)
219pub(crate) fn is_binary<R: BufRead>(input: &mut R) -> Result<bool> {
220    // Peek at the first byte in the reader
221    let buf = input.fill_buf()?;
222    if buf.is_empty() {
223        bail!("empty input");
224    }
225
226    // If the first bit of the first byte is set, we assume this is binary OpenPGP data, otherwise
227    // we assume it is ASCII-armored.
228    let binary = buf[0] & 0x80 != 0;
229
230    Ok(binary)
231}