1 //! Module to serialize and enarmor a Cert and add informative headers.
2 use std::io;
3 use std::str;
4 
5 use crate::armor;
6 use crate::cert::{Cert, amalgamation::ValidAmalgamation};
7 use crate::Result;
8 use crate::types::RevocationStatus;
9 use crate::serialize::{
10     Marshal, MarshalInto,
11     generic_serialize_into, generic_export_into,
12 };
13 use crate::policy::StandardPolicy as P;
14 
15 
16 /// Whether or not a character is printable.
is_printable(c: &char) -> bool17 pub(crate) fn is_printable(c: &char) -> bool {
18     // c.is_ascii_alphanumeric || c.is_whitespace || c.is_ascii_punctuation
19     // would exclude any utf8 character, so it seems that to obtain all
20     // printable chars, it works just excluding the control chars.
21     !c.is_control() && !c.is_ascii_control()
22 }
23 
24 impl Cert {
25     /// Creates descriptive armor headers.
26     ///
27     /// Returns armor headers that describe this Cert.  The Cert's
28     /// primary fingerprint and valid userids (according to the
29     /// default policy) are included as comments, so that it is easier
30     /// to identify the Cert when looking at the armored data.
armor_headers(&self) -> Vec<String>31     pub fn armor_headers(&self) -> Vec<String> {
32         let p = &P::default();
33 
34         let length_value = armor::LINE_LENGTH - "Comment: ".len();
35         // Create a header per userid.
36         let mut headers: Vec<String> = self.userids().with_policy(p, None)
37             // Ignore revoked userids.
38             .filter_map(|uidb| {
39                 if let RevocationStatus::Revoked(_) = uidb.revocation_status() {
40                     None
41                 } else {
42                     Some(uidb)
43                 }
44             // Ignore userids with non-printable characters.
45             }).filter_map(|uidb| {
46                 let value = str::from_utf8(uidb.userid().value()).ok()?;
47                 for c in value.chars().take(length_value) {
48                     if !is_printable(&c){
49                         return None;
50                     }
51                 }
52                 // Make sure the line length does not exceed armor::LINE_LENGTH
53                 Some(value.chars().take(length_value).collect())
54             }).collect();
55 
56         // Add the fingerprint to the front.
57         headers.insert(0, self.fingerprint().to_string());
58 
59         headers
60     }
61 
62     /// Wraps this Cert in an armor structure when serialized.
63     ///
64     /// Derives an object from this Cert that adds an armor structure
65     /// to the serialized Cert when it is serialized.  Additionally,
66     /// the Cert's userids are added as comments, so that it is easier
67     /// to identify the Cert when looking at the armored data.
68     ///
69     /// # Example
70     ///
71     /// ```rust
72     /// use sequoia_openpgp as openpgp;
73     /// use openpgp::cert::prelude::*;
74     /// use openpgp::serialize::SerializeInto;
75     ///
76     /// # f().unwrap();
77     /// # fn f() -> openpgp::Result<()> {
78     /// let (cert, _) =
79     ///     CertBuilder::general_purpose(None, Some("Mr. Pink ☮☮☮"))
80     ///     .generate()?;
81     /// let armored = String::from_utf8(cert.armored().to_vec()?)?;
82     ///
83     /// assert!(armored.starts_with("-----BEGIN PGP PUBLIC KEY BLOCK-----"));
84     /// assert!(armored.contains("Mr. Pink ☮☮☮"));
85     /// # Ok(()) }
86     /// ```
armored<'a>(&'a self) -> impl crate::serialize::Serialize + crate::serialize::SerializeInto + 'a87     pub fn armored<'a>(&'a self)
88         -> impl crate::serialize::Serialize + crate::serialize::SerializeInto + 'a
89     {
90         Encoder::new(self)
91     }
92 }
93 
94 /// A `Cert` to be armored and serialized.
95 struct Encoder<'a> {
96     cert: &'a Cert,
97 }
98 
99 
100 impl<'a> Encoder<'a> {
101     /// Returns a new Encoder to enarmor and serialize a `Cert`.
new(cert: &'a Cert) -> Self102     fn new(cert: &'a Cert) -> Self {
103         Self {
104             cert,
105         }
106     }
107 
serialize_common(&self, o: &mut dyn io::Write, export: bool) -> Result<()>108     fn serialize_common(&self, o: &mut dyn io::Write, export: bool)
109                         -> Result<()> {
110         let headers = self.cert.armor_headers();
111 
112         // Convert the Vec<String> into Vec<(&str, &str)>
113         // `iter_into` can not be used here because will take ownership and
114         // what is needed is the reference.
115         let headers: Vec<_> = headers.iter()
116             .map(|value| ("Comment", value.as_str()))
117             .collect();
118 
119         let mut w =
120             armor::Writer::with_headers(o, armor::Kind::PublicKey, headers)?;
121         if export {
122             self.cert.export(&mut w)?;
123         } else {
124             self.cert.serialize(&mut w)?;
125         }
126         w.finalize()?;
127         Ok(())
128     }
129 }
130 
131 impl<'a> crate::serialize::Serialize for Encoder<'a> {}
132 
133 impl<'a> Marshal for Encoder<'a> {
serialize(&self, o: &mut dyn io::Write) -> Result<()>134     fn serialize(&self, o: &mut dyn io::Write) -> Result<()> {
135         self.serialize_common(o, false)
136     }
137 
export(&self, o: &mut dyn io::Write) -> Result<()>138     fn export(&self, o: &mut dyn io::Write) -> Result<()> {
139         self.serialize_common(o, true)
140     }
141 }
142 
143 impl<'a> crate::serialize::SerializeInto for Encoder<'a> {}
144 
145 impl<'a> MarshalInto for Encoder<'a> {
serialized_len(&self) -> usize146     fn serialized_len(&self) -> usize {
147         let h = self.cert.armor_headers();
148         let headers_len =
149             ("Comment: ".len() + 1 /* NL */) * h.len()
150             + h.iter().map(|c| c.len()).sum::<usize>();
151         let body_len = (self.cert.serialized_len() + 2) / 3 * 4; // base64
152 
153         "-----BEGIN PGP PUBLIC KEY BLOCK-----\n\n".len()
154             + headers_len
155             + body_len
156             + (body_len + armor::LINE_LENGTH - 1) / armor::LINE_LENGTH // NLs
157             + "=FUaG\n-----END PGP PUBLIC KEY BLOCK-----\n".len()
158     }
159 
serialize_into(&self, buf: &mut [u8]) -> Result<usize>160     fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
161         generic_serialize_into(self, self.serialized_len(), buf)
162     }
163 
export_into(&self, buf: &mut [u8]) -> Result<usize>164     fn export_into(&self, buf: &mut [u8]) -> Result<usize> {
165         generic_export_into(self, self.serialized_len(), buf)
166     }
167 }
168 
169 
170 #[cfg(test)]
171 mod tests {
172     use crate::armor::{Kind, Reader, ReaderMode};
173     use crate::cert::prelude::*;
174     use crate::parse::Parse;
175 
176     use super::*;
177 
178     #[test]
is_printable_succeed()179     fn is_printable_succeed() {
180         let chars: Vec<char> = vec![
181             'a', 'z', 'A', 'Z', '1', '9', '0',
182             '|', '!', '#', '$', '%', '^', '&', '*', '-', '+', '/',
183             // The following unicode characters were taken from:
184             // https://doc.rust-lang.org/std/primitive.char.html
185             'é', 'ß', 'ℝ', '��', '❤', '東', '京', '��', '��', 'δ',
186             'Δ', '中', '越', '٣', '7', '৬', '¾', '①', 'K',
187             'و', '藏', '山', 'I', 'ï', 'İ', 'i'
188         ];
189         for c in &chars {
190             assert!(is_printable(c));
191         }
192     }
193 
194     #[test]
is_printable_fail()195     fn is_printable_fail() {
196         let chars: Vec<char> = vec![
197             '\n', 0x1b_u8.into(),
198             // U+009C, STRING TERMINATOR
199             'œ'
200         ];
201         for c in &chars {
202             assert!(!is_printable(c));
203         }
204     }
205 
206     #[test]
serialize_succeed()207     fn serialize_succeed() {
208         let cert = Cert::from_bytes(crate::tests::key("neal.pgp")).unwrap();
209 
210         // Enarmor the Cert.
211         let mut buffer = Vec::new();
212         cert.armored()
213             .serialize(&mut buffer)
214             .unwrap();
215 
216         // Parse the armor.
217         let mut cursor = io::Cursor::new(&buffer);
218         let mut reader = Reader::new(
219             &mut cursor, ReaderMode::Tolerant(Some(Kind::PublicKey)));
220 
221         // Extract the headers.
222         let mut headers: Vec<&str> = reader.headers()
223             .unwrap()
224             .into_iter()
225             .map(|header| {
226                 assert_eq!(&header.0[..], "Comment");
227                 &header.1[..]})
228             .collect();
229         headers.sort();
230 
231         // Ensure the headers are correct
232         let mut expected_headers = [
233             "Neal H. Walfield <neal@walfield.org>",
234             "Neal H. Walfield <neal@gnupg.org>",
235             "Neal H. Walfield <neal@pep-project.org>",
236             "Neal H. Walfield <neal@pep.foundation>",
237             "Neal H. Walfield <neal@sequoia-pgp.org>",
238             "8F17 7771 18A3 3DDA 9BA4  8E62 AACB 3243 6300 52D9"];
239         expected_headers.sort();
240 
241         assert_eq!(&expected_headers[..], &headers[..]);
242     }
243 
244     #[test]
serialize_length_succeed()245     fn serialize_length_succeed() {
246         let length_value = armor::LINE_LENGTH - "Comment: ".len();
247 
248         // Create userids one character longer than the size allowed in the
249         // header and expect headers with the correct length.
250         // 1 byte character
251         // Can not use `to_string` here because not such method for
252         //`std::vec::Vec<char>`
253         let userid1: String = vec!['a'; length_value + 1].into_iter()
254             .collect();
255         let userid1_expected: String = vec!['a'; length_value].into_iter()
256             .collect();
257         // 2 bytes character.
258         let userid2: String = vec!['ß'; length_value + 1].into_iter()
259             .collect();
260         let userid2_expected: String = vec!['ß'; length_value].into_iter()
261             .collect();
262         // 3 bytes character.
263         let userid3: String = vec!['€'; length_value + 1].into_iter()
264             .collect();
265         let userid3_expected: String = vec!['€'; length_value].into_iter()
266             .collect();
267         // 4 bytes character.
268         let userid4: String = vec!['��'; length_value + 1].into_iter()
269             .collect();
270         let userid4_expected: String = vec!['��'; length_value].into_iter()
271             .collect();
272         let mut userid5 = vec!['a'; length_value];
273         userid5[length_value-1] = 'ß';
274         let userid5: String = userid5.into_iter().collect();
275 
276         // Create a Cert with the userids.
277         let (cert, _) = CertBuilder::general_purpose(None, Some(&userid1[..]))
278             .add_userid(&userid2[..])
279             .add_userid(&userid3[..])
280             .add_userid(&userid4[..])
281             .add_userid(&userid5[..])
282             .generate()
283             .unwrap();
284 
285         // Enarmor the Cert.
286         let mut buffer = Vec::new();
287         cert.armored()
288             .serialize(&mut buffer)
289             .unwrap();
290 
291         // Parse the armor.
292         let mut cursor = io::Cursor::new(&buffer);
293         let mut reader = Reader::new(
294             &mut cursor, ReaderMode::Tolerant(Some(Kind::PublicKey)));
295 
296         // Extract the headers.
297         let mut headers: Vec<&str> = reader.headers()
298             .unwrap()
299             .into_iter()
300             .map(|header| {
301                 assert_eq!(&header.0[..], "Comment");
302                 &header.1[..]})
303             .skip(1) // Ignore the first header since it is the fingerprint
304             .collect();
305         // Cert canonicalization does not preserve the order of
306         // userids.
307         headers.sort();
308 
309         let mut headers_iter = headers.into_iter();
310         assert_eq!(headers_iter.next().unwrap(), &userid1_expected);
311         assert_eq!(headers_iter.next().unwrap(), &userid5);
312         assert_eq!(headers_iter.next().unwrap(), &userid2_expected);
313         assert_eq!(headers_iter.next().unwrap(), &userid3_expected);
314         assert_eq!(headers_iter.next().unwrap(), &userid4_expected);
315     }
316 
317     #[test]
serialize_into()318     fn serialize_into() {
319         let cert = Cert::from_bytes(crate::tests::key("neal.pgp")).unwrap();
320         let mut v = Vec::new();
321         cert.armored().serialize(&mut v).unwrap();
322         let v_ = cert.armored().to_vec().unwrap();
323         assert_eq!(v, v_);
324 
325         // Test truncation.
326         let mut v = vec![0; cert.armored().serialized_len() - 1];
327         let r = cert.armored().serialize_into(&mut v[..]);
328         assert_match!(
329             crate::Error::InvalidArgument(_) =
330                 r.unwrap_err().downcast().expect("not an openpgp::Error"));
331     }
332 }
333