1 //! ASCII Armor.
2 //!
3 //! Wraps [`sequoia-openpgp::armor`].
4 //!
5 //! [`sequoia-openpgp::armor`]: ../../../sequoia_openpgp/armor/index.html
6 
7 use std::mem::size_of;
8 use std::ptr;
9 use std::slice;
10 use libc::{self, c_char, c_int, size_t};
11 
12 extern crate sequoia_openpgp;
13 use self::sequoia_openpgp::armor;
14 
15 use super::io::{Reader, ReaderKind, WriterKind};
16 use crate::Maybe;
17 use crate::MoveFromRaw;
18 use crate::MoveIntoRaw;
19 use crate::MoveResultIntoRaw;
20 use crate::RefRaw;
21 use crate::RefMutRaw;
22 
23 /// Represents a (key, value) pair in an armor header.
24 #[repr(C)]
25 pub struct ArmorHeader {
26     key: *const c_char,
27     value: *const c_char,
28 }
29 
int_to_kind(kind: c_int) -> Option<armor::Kind>30 fn int_to_kind(kind: c_int) -> Option<armor::Kind> {
31     match kind {
32         0 => None,
33         1 => Some(armor::Kind::Message),
34         2 => Some(armor::Kind::PublicKey),
35         3 => Some(armor::Kind::SecretKey),
36         4 => Some(armor::Kind::Signature),
37         5 => Some(armor::Kind::File),
38         _ => panic!("Bad kind: {}", kind),
39     }
40 }
41 
kind_to_int(kind: Option<armor::Kind>) -> c_int42 fn kind_to_int(kind: Option<armor::Kind>) -> c_int {
43     match kind {
44         None => 0,
45         Some(armor::Kind::Message) => 1,
46         Some(armor::Kind::PublicKey) => 2,
47         Some(armor::Kind::SecretKey) => 3,
48         Some(armor::Kind::Signature) => 4,
49         Some(armor::Kind::File) => 5,
50     }
51 }
52 
int_to_reader_mode(mode: c_int) -> armor::ReaderMode53 fn int_to_reader_mode(mode: c_int) -> armor::ReaderMode {
54     match mode {
55         -1 => armor::ReaderMode::VeryTolerant,
56         _ => armor::ReaderMode::Tolerant(int_to_kind(mode)),
57     }
58 }
59 
60 // fn reader_mode_to_int(mode: armor::ReaderMode) -> c_int {
61 //     match mode {
62 //         armor::ReaderMode::VeryTolerant => -1,
63 //         armor::ReaderMode::Tolerant(kind) => kind_to_int(kind),
64 //     }
65 // }
66 
67 /// Constructs a new filter for the given type of data.
68 ///
69 /// A filter that strips ASCII Armor from a stream of data.
70 ///
71 /// # Example
72 ///
73 /// ```c
74 /// #include <assert.h>
75 /// #include <error.h>
76 /// #include <stdio.h>
77 /// #include <stdlib.h>
78 /// #include <string.h>
79 ///
80 /// #include <sequoia/openpgp.h>
81 ///
82 /// const char *armored =
83 ///   "-----BEGIN PGP ARMORED FILE-----\n"
84 ///   "Key0: Value0\n"
85 ///   "Key1: Value1\n"
86 ///   "\n"
87 ///   "SGVsbG8gd29ybGQh\n"
88 ///   "=s4Gu\n"
89 ///   "-----END PGP ARMORED FILE-----\n";
90 ///
91 /// pgp_reader_t bytes = pgp_reader_from_bytes ((uint8_t *) armored, strlen (armored));
92 /// pgp_reader_t armor = pgp_armor_reader_new (bytes, PGP_ARMOR_KIND_ANY);
93 ///
94 /// pgp_error_t err;
95 /// pgp_armor_header_t header;
96 /// size_t header_len;
97 /// header = pgp_armor_reader_headers (&err, armor, &header_len);
98 /// if (header == NULL)
99 ///   error (1, 0, "Getting headers failed: %s", pgp_error_to_string (err));
100 ///
101 /// assert (header_len == 2);
102 /// assert (strcmp (header[0].key, "Key0") == 0
103 ///         && strcmp (header[0].value, "Value0") == 0);
104 /// assert (strcmp (header[1].key, "Key1") == 0
105 ///         && strcmp (header[1].value, "Value1") == 0);
106 /// for (size_t i = 0; i < header_len; i++)
107 ///   {
108 ///     free (header[i].key);
109 ///     free (header[i].value);
110 ///   }
111 /// free (header);
112 ///
113 /// char message[12];
114 /// if (pgp_reader_read (&err, armor, (uint8_t *) message, 12) < 0)
115 ///   error (1, 0, "Reading failed: %s", pgp_error_to_string (err));
116 ///
117 /// assert (pgp_armor_reader_kind (armor) == PGP_ARMOR_KIND_FILE);
118 /// assert (memcmp (message, "Hello world!", 12) == 0);
119 ///
120 /// pgp_reader_free (armor);
121 /// pgp_reader_free (bytes);
122 /// ```
123 #[::sequoia_ffi_macros::extern_fn] #[no_mangle]
pgp_armor_reader_new(inner: *mut Reader, mode: c_int) -> *mut Reader124 pub extern "C" fn pgp_armor_reader_new(inner: *mut Reader,
125                                        mode: c_int)
126     -> *mut Reader
127 {
128     let inner = inner.ref_mut_raw();
129     let mode = int_to_reader_mode(mode);
130 
131     ReaderKind::Armored(armor::Reader::new(inner, mode)).move_into_raw()
132 }
133 
134 /// Creates a `Reader` from a file.
135 #[::sequoia_ffi_macros::extern_fn] #[no_mangle]
pgp_armor_reader_from_file(errp: Option<&mut *mut crate::error::Error>, filename: *const c_char, mode: c_int) -> Maybe<Reader>136 pub extern "C" fn pgp_armor_reader_from_file(errp: Option<&mut *mut crate::error::Error>,
137                                              filename: *const c_char,
138                                              mode: c_int)
139     -> Maybe<Reader>
140 {
141     let filename = ffi_param_cstr!(filename).to_string_lossy().into_owned();
142     let mode = int_to_reader_mode(mode);
143 
144     armor::Reader::from_file(&filename, mode)
145         .map(|r| ReaderKind::Armored(r))
146         .map_err(|e| ::anyhow::Error::from(e))
147         .move_into_raw(errp)
148 }
149 
150 /// Creates a `Reader` from a buffer.
151 ///
152 /// # Example
153 ///
154 /// ```c
155 /// #include <assert.h>
156 /// #include <error.h>
157 /// #include <stdio.h>
158 /// #include <stdlib.h>
159 /// #include <string.h>
160 ///
161 /// #include <sequoia/openpgp.h>
162 ///
163 /// const char *armored =
164 ///   "-----BEGIN PGP ARMORED FILE-----\n"
165 ///   "Key0: Value0\n"
166 ///   "Key1: Value1\n"
167 ///   "\n"
168 ///   "SGVsbG8gd29ybGQh\n"
169 ///   "=s4Gu\n"
170 ///   "-----END PGP ARMORED FILE-----\n";
171 ///
172 /// pgp_reader_t armor =
173 ///     pgp_armor_reader_from_bytes ((uint8_t *) armored, strlen (armored),
174 ///     PGP_ARMOR_KIND_ANY);
175 ///
176 /// pgp_error_t err;
177 /// pgp_armor_header_t header;
178 /// size_t header_len;
179 /// header = pgp_armor_reader_headers (&err, armor, &header_len);
180 /// if (header == NULL)
181 ///   error (1, 0, "Getting headers failed: %s", pgp_error_to_string (err));
182 ///
183 /// assert (header_len == 2);
184 /// assert (strcmp (header[0].key, "Key0") == 0
185 ///         && strcmp (header[0].value, "Value0") == 0);
186 /// assert (strcmp (header[1].key, "Key1") == 0
187 ///         && strcmp (header[1].value, "Value1") == 0);
188 /// for (size_t i = 0; i < header_len; i++)
189 ///   {
190 ///     free (header[i].key);
191 ///     free (header[i].value);
192 ///   }
193 /// free (header);
194 ///
195 /// char message[12];
196 /// if (pgp_reader_read (&err, armor, (uint8_t *) message, 12) < 0)
197 ///   error (1, 0, "Reading failed: %s", pgp_error_to_string (err));
198 ///
199 /// assert (pgp_armor_reader_kind (armor) == PGP_ARMOR_KIND_FILE);
200 /// assert (memcmp (message, "Hello world!", 12) == 0);
201 ///
202 /// pgp_reader_free (armor);
203 /// ```
204 #[::sequoia_ffi_macros::extern_fn] #[no_mangle] pub extern "C"
pgp_armor_reader_from_bytes(b: *const u8, len: size_t, mode: c_int) -> *mut Reader205 fn pgp_armor_reader_from_bytes(b: *const u8, len: size_t,
206                                mode: c_int)
207                                -> *mut Reader {
208     assert!(!b.is_null());
209     let buf = unsafe {
210         slice::from_raw_parts(b, len as usize)
211     };
212     let mode = int_to_reader_mode(mode);
213 
214     ReaderKind::Armored(armor::Reader::from_bytes(buf, mode)).move_into_raw()
215 }
216 
217 /// Returns the kind of data this reader is for.
218 ///
219 /// Useful if the kind of data is not known in advance.  If the header
220 /// has not been encountered yet (try reading some data first!), this
221 /// function returns PGP_ARMOR_KIND_ANY.
222 ///
223 /// # Example
224 ///
225 /// See [this] example.
226 ///
227 ///   [this]: fn.pgp_armor_reader_new.html
228 #[::sequoia_ffi_macros::extern_fn] #[no_mangle]
pgp_armor_reader_kind(reader: *const Reader) -> c_int229 pub extern "C" fn pgp_armor_reader_kind(reader: *const Reader)
230                                              -> c_int {
231     if let ReaderKind::Armored(ref armor_reader) = reader.ref_raw()
232     {
233         kind_to_int(armor_reader.kind())
234     } else {
235         panic!(
236             "FFI contract violation: Wrong parameter type: \
237              expected an armor reader")
238     }
239 }
240 
241 /// Returns the armored headers.
242 ///
243 /// The tuples contain a key and a value.
244 ///
245 /// Note: if a key occurs multiple times, then there are multiple
246 /// entries in the vector with the same key; values with the same
247 /// key are *not* combined.
248 ///
249 /// The returned array and the strings in the headers have been
250 /// allocated with `malloc`, and the caller is responsible for freeing
251 /// both the array and the strings.
252 ///
253 /// # Example
254 ///
255 /// See [this] example.
256 ///
257 ///   [this]: fn.pgp_armor_reader_new.html
258 #[::sequoia_ffi_macros::extern_fn] #[no_mangle]
pgp_armor_reader_headers(errp: Option<&mut *mut crate::error::Error>, reader: *mut Reader, len: *mut size_t) -> *mut ArmorHeader259 pub extern "C" fn pgp_armor_reader_headers(errp: Option<&mut *mut crate::error::Error>,
260                                                reader: *mut Reader,
261                                                len: *mut size_t)
262                                                -> *mut ArmorHeader {
263     ffi_make_fry_from_errp!(errp);
264     let len = ffi_param_ref_mut!(len);
265 
266     let reader = if let ReaderKind::Armored(ref mut reader) = reader.ref_mut_raw() {
267         reader
268     } else {
269         panic!("FFI contract violation: Wrong parameter type: \
270                 expected armor reader");
271     };
272 
273     match reader.headers().map_err(|e| ::anyhow::Error::from(e)) {
274         Ok(headers) => {
275             // Allocate space for the result.
276             let buf = unsafe {
277                 libc::calloc(headers.len(), size_of::<ArmorHeader>())
278                     as *mut ArmorHeader
279             };
280             let sl = unsafe {
281                 slice::from_raw_parts_mut(buf, headers.len())
282             };
283             for (i, (key, value)) in headers.iter().enumerate() {
284                 sl[i].key =
285                     super::strndup(key.as_bytes()).unwrap_or(ptr::null_mut());
286                 sl[i].value =
287                     super::strndup(value.as_bytes()).unwrap_or(ptr::null_mut());
288             }
289 
290             *len = headers.len();
291             buf
292         },
293         Err(e) => {
294             if let Some(errp) = errp {
295                 *errp = e.move_into_raw();
296             }
297             ptr::null_mut()
298         },
299     }
300 }
301 
302 /// Constructs a new filter for the given type of data.
303 ///
304 /// A filter that applies ASCII Armor to the data written to it.
305 ///
306 /// Note: You must call `pgp_armor_writer_finalize` to deallocate this
307 /// writer.
308 ///
309 /// # Example
310 ///
311 /// ```c
312 /// #define _GNU_SOURCE
313 /// #include <assert.h>
314 /// #include <error.h>
315 /// #include <stdio.h>
316 /// #include <stdlib.h>
317 /// #include <string.h>
318 ///
319 /// #include <sequoia/openpgp.h>
320 ///
321 /// int
322 /// main (int argc, char **argv)
323 /// {
324 ///   void *buf = NULL;
325 ///   size_t len = 0;
326 ///   pgp_writer_t alloc;
327 ///   pgp_writer_t armor;
328 ///   pgp_error_t err = NULL;
329 ///
330 ///   char *message = "Hello world!";
331 ///   struct pgp_armor_header header[] = {
332 ///     { "Key0", "Value0" },
333 ///     { "Key1", "Value1" },
334 ///   };
335 ///
336 ///   alloc = pgp_writer_alloc (&buf, &len);
337 ///   armor = pgp_armor_writer_new (&err, alloc, PGP_ARMOR_KIND_FILE, header, 2);
338 ///   if (armor == NULL)
339 ///     error (1, 0, "Creating armor writer failed: %s", pgp_error_to_string (err));
340 ///
341 ///   if (pgp_writer_write (&err, armor, (uint8_t *) message, strlen (message)) < 0)
342 ///     error (1, 0, "Writing failed: %s", pgp_error_to_string (err));
343 ///
344 ///   pgp_armor_writer_finalize (&err, armor);
345 ///   assert (err == NULL);
346 ///   pgp_writer_free (alloc);
347 ///
348 ///   assert (len == 114);
349 ///   assert (memcmp (buf,
350 ///                   "-----BEGIN PGP ARMORED FILE-----\n"
351 ///                   "Key0: Value0\n"
352 ///                   "Key1: Value1\n"
353 ///                   "\n"
354 ///                   "SGVsbG8gd29ybGQh\n"
355 ///                   "=s4Gu\n"
356 ///                   "-----END PGP ARMORED FILE-----\n",
357 ///                   len) == 0);
358 ///
359 ///   free (buf);
360 ///   return 0;
361 /// }
362 /// ```
363 #[::sequoia_ffi_macros::extern_fn] #[no_mangle]
pgp_armor_writer_new(errp: Option<&mut *mut crate::error::Error>, inner: *mut super::io::Writer, kind: c_int, header: *const ArmorHeader, header_len: size_t) -> Maybe<super::io::Writer>364 pub extern "C" fn pgp_armor_writer_new
365     (errp: Option<&mut *mut crate::error::Error>,
366      inner: *mut super::io::Writer,
367      kind: c_int,
368      header: *const ArmorHeader,
369      header_len: size_t)
370      -> Maybe<super::io::Writer>
371 {
372     let inner = inner.ref_mut_raw();
373     let kind = int_to_kind(kind).expect("KIND must not be PGP_ARMOR_KIND_ANY");
374 
375     let mut header_ = Vec::new();
376     if header_len > 0 {
377         let headers = ffi_param_ref!(header);
378         let headers = unsafe {
379             slice::from_raw_parts(headers, header_len)
380         };
381         for header in headers {
382             header_.push(
383                 (ffi_param_cstr!(header.key).to_string_lossy(),
384                  ffi_param_cstr!(header.value).to_string_lossy())
385             );
386         }
387     }
388 
389     let header: Vec<(&str, &str)> =
390         header_.iter().map(|h| (h.0.as_ref(), h.1.as_ref())).collect();
391 
392     armor::Writer::with_headers(inner, kind, header)
393         .map(|w| WriterKind::Armored(w))
394         .map_err(|e| ::anyhow::Error::from(e))
395         .move_into_raw(errp)
396 }
397 
398 /// Finalizes the armor writer.
399 ///
400 /// Consumes the writer.  No further deallocation of the writer is
401 /// required.
402 #[::sequoia_ffi_macros::extern_fn] #[no_mangle]
pgp_armor_writer_finalize(errp: Option<&mut *mut crate::error::Error>, writer: *mut super::io::Writer) -> crate::error::Status403 pub extern "C" fn pgp_armor_writer_finalize
404     (errp: Option<&mut *mut crate::error::Error>,
405      writer: *mut super::io::Writer)
406      -> crate::error::Status
407 {
408     ffi_make_fry_from_errp!(errp);
409     let writer = if let WriterKind::Armored(writer) = writer.move_from_raw() {
410         writer
411     } else {
412         panic!("FFI contract violation: Wrong parameter type: \
413                 expected armor writer");
414     };
415 
416     ffi_try_status!(writer.finalize().map_err(|e| e.into()))
417 }
418