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