summaryrefslogtreecommitdiffstats
path: root/ffi/src/openpgp/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'ffi/src/openpgp/mod.rs')
-rw-r--r--ffi/src/openpgp/mod.rs3080
1 files changed, 3080 insertions, 0 deletions
diff --git a/ffi/src/openpgp/mod.rs b/ffi/src/openpgp/mod.rs
new file mode 100644
index 00000000..bfd59733
--- /dev/null
+++ b/ffi/src/openpgp/mod.rs
@@ -0,0 +1,3080 @@
+//! XXX
+
+use failure;
+use std::ffi::{CString, CStr};
+use std::hash::{Hash, Hasher};
+use std::mem::{size_of, forget};
+use std::ptr;
+use std::slice;
+use std::io;
+use std::io::{Read, Write};
+use libc::{self, uint8_t, uint64_t, c_char, c_int, size_t, ssize_t, c_void, time_t};
+use failure::ResultExt;
+
+extern crate sequoia_openpgp as openpgp;
+extern crate time;
+
+use self::openpgp::{
+ armor,
+ Fingerprint,
+ KeyID,
+ RevocationStatus,
+ PacketPile,
+ TPK,
+ TSK,
+ Packet,
+ packet::{
+ Signature,
+ Tag,
+ PKESK,
+ SKESK,
+ key::SecretKey,
+ },
+ crypto::Password,
+};
+use self::openpgp::tpk::{
+ CipherSuite,
+ TPKBuilder,
+ UserIDBinding,
+ UserIDBindingIter,
+ KeyIter
+};
+use self::openpgp::packet;
+use self::openpgp::parse::{
+ Parse,
+ PacketParserResult,
+ PacketParser,
+ PacketParserEOF,
+};
+use self::openpgp::parse::stream::{
+ DecryptionHelper,
+ Decryptor,
+ Secret,
+ VerificationHelper,
+ VerificationResult,
+ Verifier,
+ DetachedVerifier,
+};
+use self::openpgp::serialize::Serialize;
+use self::openpgp::constants::{
+ DataFormat,
+ ReasonForRevocation,
+};
+
+use super::build_hasher;
+use super::error::Status;
+use super::core::Context;
+
+/* openpgp::packet::Tag. */
+
+/// Returns a human-readable tag name.
+///
+/// ```c
+/// #include <assert.h>
+/// #include <string.h>
+/// #include <sequoia.h>
+///
+/// assert (strcmp (sq_tag_to_string (2), "SIGNATURE") == 0);
+/// ```
+#[no_mangle]
+pub extern "system" fn sq_tag_to_string(tag: u8) -> *const c_char {
+ match Tag::from(tag) {
+ Tag::PKESK => "PKESK\x00",
+ Tag::Signature => "SIGNATURE\x00",
+ Tag::SKESK => "SKESK\x00",
+ Tag::OnePassSig => "ONE PASS SIG\x00",
+ Tag::SecretKey => "SECRET KEY\x00",
+ Tag::PublicKey => "PUBLIC KEY\x00",
+ Tag::SecretSubkey => "SECRET SUBKEY\x00",
+ Tag::CompressedData => "COMPRESSED DATA\x00",
+ Tag::SED => "SED\x00",
+ Tag::Marker => "MARKER\x00",
+ Tag::Literal => "LITERAL\x00",
+ Tag::Trust => "TRUST\x00",
+ Tag::UserID => "USER ID\x00",
+ Tag::PublicSubkey => "PUBLIC SUBKEY\x00",
+ Tag::UserAttribute => "USER ATTRIBUTE\x00",
+ Tag::SEIP => "SEIP\x00",
+ Tag::MDC => "MDC\x00",
+ _ => "OTHER\x00",
+ }.as_bytes().as_ptr() as *const c_char
+}
+
+/* sequoia::openpgp::KeyID. */
+
+/// Reads a binary key ID.
+#[no_mangle]
+pub extern "system" fn sq_keyid_from_bytes(id: *const uint8_t) -> *mut KeyID {
+ assert!(!id.is_null());
+ let id = unsafe { slice::from_raw_parts(id, 8) };
+ Box::into_raw(Box::new(KeyID::from_bytes(id)))
+}
+
+/// Reads a hex-encoded Key ID.
+#[no_mangle]
+pub extern "system" fn sq_keyid_from_hex(id: *const c_char) -> *mut KeyID {
+ assert!(!id.is_null());
+ let id = unsafe { CStr::from_ptr(id).to_string_lossy() };
+ KeyID::from_hex(&id)
+ .map(|id| Box::into_raw(Box::new(id)))
+ .unwrap_or(ptr::null_mut())
+}
+
+/// Frees an `KeyID` object.
+#[no_mangle]
+pub extern "system" fn sq_keyid_free(keyid: *mut KeyID) {
+ if keyid.is_null() { return }
+ unsafe {
+ drop(Box::from_raw(keyid));
+ }
+}
+
+/// Clones the KeyID.
+#[no_mangle]
+pub extern "system" fn sq_keyid_clone(id: Option<&KeyID>)
+ -> *mut KeyID {
+ let id = id.expect("KeyID is NULL");
+ box_raw!(id.clone())
+}
+
+/// Hashes the KeyID.
+#[no_mangle]
+pub extern "system" fn sq_keyid_hash(id: Option<&KeyID>)
+ -> uint64_t {
+ let id = id.expect("KeyID is NULL");
+ let mut hasher = build_hasher();
+ id.hash(&mut hasher);
+ hasher.finish()
+}
+
+/// Converts the KeyID to its standard representation.
+#[no_mangle]
+pub extern "system" fn sq_keyid_to_string(id: Option<&KeyID>)
+ -> *mut c_char {
+ let id = id.expect("KeyID is NULL");
+ CString::new(id.to_string())
+ .unwrap() // Errors only on internal nul bytes.
+ .into_raw()
+}
+
+/// Converts the KeyID to a hexadecimal number.
+#[no_mangle]
+pub extern "system" fn sq_keyid_to_hex(id: Option<&KeyID>)
+ -> *mut c_char {
+ let id = id.expect("KeyID is NULL");
+ CString::new(id.to_hex())
+ .unwrap() // Errors only on internal nul bytes.
+ .into_raw()
+}
+
+/// Compares KeyIDs.
+#[no_mangle]
+pub extern "system" fn sq_keyid_equal(a: Option<&KeyID>,
+ b: Option<&KeyID>)
+ -> bool {
+ let a = a.expect("KeyID 'a' is NULL");
+ let b = b.expect("KeyID 'b' is NULL");
+ a == b
+}
+
+
+/* sequoia::openpgp::Fingerprint. */
+
+/// Reads a binary fingerprint.
+#[no_mangle]
+pub extern "system" fn sq_fingerprint_from_bytes(buf: *const uint8_t,
+ len: size_t)
+ -> *mut Fingerprint {
+ assert!(!buf.is_null());
+ let buf = unsafe {
+ slice::from_raw_parts(buf, len as usize)
+ };
+ Box::into_raw(Box::new(Fingerprint::from_bytes(buf)))
+}
+
+/// Reads a hexadecimal fingerprint.
+#[no_mangle]
+pub extern "system" fn sq_fingerprint_from_hex(hex: *const c_char)
+ -> *mut Fingerprint {
+ assert!(!hex.is_null());
+ let hex = unsafe { CStr::from_ptr(hex).to_string_lossy() };
+ Fingerprint::from_hex(&hex)
+ .map(|fp| Box::into_raw(Box::new(fp)))
+ .unwrap_or(ptr::null_mut())
+}
+
+/// Frees a sq_fingerprint_t.
+#[no_mangle]
+pub extern "system" fn sq_fingerprint_free(fp: *mut Fingerprint) {
+ if fp.is_null() { return }
+ unsafe {
+ drop(Box::from_raw(fp));
+ }
+}
+
+/// Clones the Fingerprint.
+#[no_mangle]
+pub extern "system" fn sq_fingerprint_clone(fp: Option<&Fingerprint>)
+ -> *mut Fingerprint {
+ let fp = fp.expect("Fingerprint is NULL");
+ box_raw!(fp.clone())
+}
+
+/// Hashes the Fingerprint.
+#[no_mangle]
+pub extern "system" fn sq_fingerprint_hash(fp: Option<&Fingerprint>)
+ -> uint64_t {
+ let fp = fp.expect("Fingerprint is NULL");
+ let mut hasher = build_hasher();
+ fp.hash(&mut hasher);
+ hasher.finish()
+}
+
+/// Returns a reference to the raw Fingerprint.
+///
+/// This returns a reference to the internal buffer that is valid as
+/// long as the fingerprint is.
+#[no_mangle]
+pub extern "system" fn sq_fingerprint_as_bytes(fp: Option<&Fingerprint>, fp_len: Option<&mut size_t>)
+ -> *const uint8_t {
+ let fp = fp.expect("Fingerprint is NULL");
+ if let Some(p) = fp_len {
+ *p = fp.as_slice().len();
+ }
+ fp.as_slice().as_ptr()
+}
+
+/// Converts the fingerprint to its standard representation.
+#[no_mangle]
+pub extern "system" fn sq_fingerprint_to_string(fp: Option<&Fingerprint>)
+ -> *mut c_char {
+ let fp = fp.expect("Fingerprint is NULL");
+ CString::new(fp.to_string())
+ .unwrap() // Errors only on internal nul bytes.
+ .into_raw()
+}
+
+/// Converts the fingerprint to a hexadecimal number.
+#[no_mangle]
+pub extern "system" fn sq_fingerprint_to_hex(fp: Option<&Fingerprint>)
+ -> *mut c_char {
+ let fp = fp.expect("Fingerprint is NULL");
+ CString::new(fp.to_hex())
+ .unwrap() // Errors only on internal nul bytes.
+ .into_raw()
+}
+
+/// Converts the fingerprint to a key ID.
+#[no_mangle]
+pub extern "system" fn sq_fingerprint_to_keyid(fp: Option<&Fingerprint>)
+ -> *mut KeyID {
+ let fp = fp.expect("Fingerprint is NULL");
+ Box::into_raw(Box::new(fp.to_keyid()))
+}
+
+/// Compares Fingerprints.
+#[no_mangle]
+pub extern "system" fn sq_fingerprint_equal(a: Option<&Fingerprint>,
+ b: Option<&Fingerprint>)
+ -> bool {
+ let a = a.expect("Fingerprint 'a' is NULL");
+ let b = b.expect("Fingerprint 'b' is NULL");
+ a == b
+}
+
+
+/* openpgp::armor. */
+
+/// Represents a (key, value) pair in an armor header.
+#[repr(C)]
+pub struct ArmorHeader {
+ key: *const c_char,
+ value: *const c_char,
+}
+
+fn int_to_kind(kind: c_int) -> Option<armor::Kind> {
+ match kind {
+ 0 => None,
+ 1 => Some(armor::Kind::Message),
+ 2 => Some(armor::Kind::PublicKey),
+ 3 => Some(armor::Kind::SecretKey),
+ 4 => Some(armor::Kind::Signature),
+ 5 => Some(armor::Kind::File),
+ _ => panic!("Bad kind: {}", kind),
+ }
+}
+
+fn kind_to_int(kind: Option<armor::Kind>) -> c_int {
+ match kind {
+ None => 0,
+ Some(armor::Kind::Message) => 1,
+ Some(armor::Kind::PublicKey) => 2,
+ Some(armor::Kind::SecretKey) => 3,
+ Some(armor::Kind::Signature) => 4,
+ Some(armor::Kind::File) => 5,
+ }
+}
+
+/// Constructs a new filter for the given type of data.
+///
+/// A filter that strips ASCII Armor from a stream of data.
+///
+/// # Example
+///
+/// ```c
+/// #define _GNU_SOURCE
+/// #include <assert.h>
+/// #include <error.h>
+/// #include <stdio.h>
+/// #include <stdlib.h>
+/// #include <string.h>
+///
+/// #include <sequoia.h>
+///
+/// const char *armored =
+/// "-----BEGIN PGP ARMORED FILE-----\n"
+/// "Key0: Value0\n"
+/// "Key1: Value1\n"
+/// "\n"
+/// "SGVsbG8gd29ybGQh\n"
+/// "=s4Gu\n"
+/// "-----END PGP ARMORED FILE-----\n";
+///
+/// int
+/// main (int argc, char **argv)
+/// {
+/// sq_error_t err;
+/// sq_context_t ctx;
+/// sq_reader_t bytes;
+/// sq_reader_t armor;
+/// sq_armor_kind_t kind;
+/// char message[12];
+/// sq_armor_header_t *header;
+/// size_t header_len;
+///
+/// ctx = sq_context_new ("org.sequoia-pgp.example", &err);
+/// if (ctx == NULL)
+/// error (1, 0, "Initializing sequoia failed: %s",
+/// sq_error_string (err));
+///
+/// bytes = sq_reader_from_bytes ((uint8_t *) armored, strlen (armored));
+/// armor = sq_armor_reader_new (bytes, SQ_ARMOR_KIND_ANY);
+///
+/// header = sq_armor_reader_headers (ctx, armor, &header_len);
+/// if (header == NULL)
+/// {
+/// err = sq_context_last_error (ctx);
+/// error (1, 0, "Getting headers failed: %s",
+/// sq_error_string (err));
+/// }
+///
+/// assert (header_len == 2);
+/// assert (strcmp (header[0].key, "Key0") == 0
+/// && strcmp (header[0].value, "Value0") == 0);
+/// assert (strcmp (header[1].key, "Key1") == 0
+/// && strcmp (header[1].value, "Value1") == 0);
+/// for (size_t i = 0; i < header_len; i++)
+/// {
+/// free (header[i].key);
+/// free (header[i].value);
+/// }
+/// free (header);
+///
+/// kind = sq_armor_reader_kind (armor);
+/// assert (kind == SQ_ARMOR_KIND_FILE);
+///
+/// if (sq_reader_read (ctx, armor, (uint8_t *) message, 12) < 0)
+/// {
+/// err = sq_context_last_error (ctx);
+/// error (1, 0, "Reading failed: %s",
+/// sq_error_string (err));
+/// }
+///
+/// assert (memcmp (message, "Hello world!", 12) == 0);
+///
+/// sq_reader_free (armor);
+/// sq_reader_free (bytes);
+/// sq_context_free (ctx);
+/// return 0;
+/// }
+/// ```
+#[no_mangle]
+pub extern "system" fn sq_armor_reader_new(inner: Option<&'static mut Box<Read>>,
+ kind: c_int)
+ -> *mut Box<Read> {
+ let inner = inner.expect("Inner is NULL");
+ let kind = int_to_kind(kind);
+
+ box_raw!(Box::new(armor::Reader::new(inner, kind)))
+}
+
+/// Creates a `Reader` from a file.
+#[no_mangle]
+pub extern "system" fn sq_armor_reader_from_file(ctx: Option<&mut Context>,
+ filename: *const c_char,
+ kind: c_int)
+ -> *mut Box<Read> {
+ let ctx = ctx.expect("Context is NULL");
+ assert!(! filename.is_null());
+ let filename = unsafe {
+ CStr::from_ptr(filename).to_string_lossy().into_owned()
+ };
+ let kind = int_to_kind(kind);
+
+ fry_box!(ctx, armor::Reader::from_file(&filename, kind)
+ .map(|r| Box::new(r))
+ .map_err(|e| e.into()))
+}
+
+/// Creates a `Reader` from a buffer.
+#[no_mangle]
+pub extern "system" fn sq_armor_reader_from_bytes(b: *const uint8_t, len: size_t,
+ kind: c_int)
+ -> *mut Box<Read> {
+ assert!(!b.is_null());
+ let buf = unsafe {
+ slice::from_raw_parts(b, len as usize)
+ };
+ let kind = int_to_kind(kind);
+
+ box_raw!(Box::new(armor::Reader::from_bytes(buf, kind)))
+}
+
+/// Returns the kind of data this reader is for.
+///
+/// Useful if the kind of data is not known in advance. If the header
+/// has not been encountered yet (try reading some data first!), this
+/// function returns SQ_ARMOR_KIND_ANY.
+///
+/// # Example
+///
+/// See [this] example.
+///
+/// [this]: fn.sq_armor_reader_new.html
+#[no_mangle]
+pub extern "system" fn sq_armor_reader_kind(reader: *mut Box<Read>)
+ -> c_int {
+ // We need to downcast `reader`. To do that, we need to do a
+ // little dance. We will momentarily take ownership of `reader`,
+ // wrapping it in a Box again. Then, at the end of the function,
+ // we will leak it again.
+ assert!(! reader.is_null());
+ let reader = unsafe {
+ Box::from_raw(reader as *mut Box<armor::Reader>)
+ };
+ let kind = kind_to_int(reader.kind());
+ Box::into_raw(reader);
+ kind
+}
+
+/// Returns the armored headers.
+///
+/// The tuples contain a key and a value.
+///
+/// Note: if a key occurs multiple times, then there are multiple
+/// entries in the vector with the same key; values with the same
+/// key are *not* combined.
+///
+/// The returned array and the strings in the headers have been
+/// allocated with `malloc`, and the caller is responsible for freeing
+/// both the array and the strings.
+///
+/// # Example
+///
+/// See [this] example.
+///
+/// [this]: fn.sq_armor_reader_new.html
+#[no_mangle]
+pub extern "system" fn sq_armor_reader_headers(ctx: Option<&mut Context>,
+ reader: *mut Box<Read>,
+ len: Option<&mut size_t>)
+ -> *mut ArmorHeader {
+ let ctx = ctx.expect("Context is NULL");
+ assert!(! reader.is_null());
+ let len = len.expect("LEN is NULL");
+
+ // We need to downcast `reader`. To do that, we need to do a
+ // little dance. We will momentarily take ownership of `reader`,
+ // wrapping it in a Box again. Then, at the end of the function,
+ // we will leak it again.
+ let mut reader = unsafe {
+ Box::from_raw(reader as *mut Box<armor::Reader>)
+ };
+
+ // We need to be extra careful here in order not to keep ownership
+ // of `reader` in case of errors.
+ let result = match reader.headers().map_err(|e| e.into()) {
+ Ok(headers) => {
+ // Allocate space for the result.
+ let buf = unsafe {
+ libc::calloc(headers.len(), size_of::<ArmorHeader>())
+ as *mut ArmorHeader
+ };
+ let sl = unsafe {
+ slice::from_raw_parts_mut(buf, headers.len())
+ };
+ for (i, (key, value)) in headers.iter().enumerate() {
+ sl[i].key = strdup(key);
+ sl[i].value = strdup(value);
+ }
+
+ *len = headers.len();
+ buf
+ },
+ Err(e) => {
+ ctx.e = Some(e);
+ ptr::null_mut()
+ },
+ };
+
+ // Release temporary ownership.
+ Box::into_raw(reader);
+ result
+}
+
+/// Creates a zero-terminated C string from a &str allocated using
+/// malloc.
+fn strdup(s: &str) -> *mut c_char {
+ let b = s.as_bytes();
+ let len = b.len() + 1;
+ let dup = unsafe {
+ libc::malloc(len) as *mut c_char
+ };
+ let sl = unsafe {
+ slice::from_raw_parts_mut(dup as *mut uint8_t, len)
+ };
+ sl[..len-1].copy_from_slice(b);
+ sl[len-1] = 0;
+ dup
+}
+
+/// Constructs a new filter for the given type of data.
+///
+/// A filter that applies ASCII Armor to the data written to it.
+///
+/// # Example
+///
+/// ```c
+/// #define _GNU_SOURCE
+/// #include <assert.h>
+/// #include <error.h>
+/// #include <stdio.h>
+/// #include <stdlib.h>
+/// #include <string.h>
+///
+/// #include <sequoia.h>
+///
+/// int
+/// main (int argc, char **argv)
+/// {
+/// void *buf = NULL;
+/// size_t len = 0;
+/// sq_writer_t alloc;
+/// sq_writer_t armor;
+/// sq_error_t err;
+/// sq_context_t ctx;
+/// char *message = "Hello world!";
+/// sq_armor_header_t header[2] = {
+/// { "Key0", "Value0" },
+/// { "Key1", "Value1" },
+/// };
+///
+/// ctx = sq_context_new ("org.sequoia-pgp.example", &err);
+/// if (ctx == NULL)
+/// error (1, 0, "Initializing sequoia failed: %s",
+/// sq_error_string (err));
+///
+/// alloc = sq_writer_alloc (&buf, &len);
+/// armor = sq_armor_writer_new (ctx, alloc, SQ_ARMOR_KIND_FILE, header, 2);
+/// if (armor == NULL)
+/// {
+/// err = sq_context_last_error (ctx);
+/// error (1, 0, "Creating armor writer failed: %s",
+/// sq_error_string (err));
+/// }
+///
+/// if (sq_writer_write (ctx, armor, (uint8_t *) message, strlen (message)) < 0)
+/// {
+/// err = sq_context_last_error (ctx);
+/// error (1, 0, "Writing failed: %s",
+/// sq_error_string (err));
+/// }
+/// sq_writer_free (armor);
+/// sq_writer_free (alloc);
+///
+/// assert (len == 114);
+/// assert (memcmp (buf,
+/// "-----BEGIN PGP ARMORED FILE-----\n"
+/// "Key0: Value0\n"
+/// "Key1: Value1\n"
+/// "\n"
+/// "SGVsbG8gd29ybGQh\n"
+/// "=s4Gu\n"
+/// "-----END PGP ARMORED FILE-----\n",
+/// len) == 0);
+///
+/// free (buf);
+/// sq_context_free (ctx);
+/// return 0;
+/// }
+/// ```
+#[no_mangle]
+pub extern "system" fn sq_armor_writer_new
+ (ctx: Option<&mut Context>,
+ inner: Option<&'static mut Box<Write>>,
+ kind: c_int,
+ header: Option<&ArmorHeader>,
+ header_len: size_t)
+ -> *mut Box<Write>
+{
+ let ctx = ctx.expect("Context is NULL");
+ let inner = inner.expect("Inner is NULL");
+ let kind = int_to_kind(kind).expect("KIND must not be SQ_ARMOR_KIND_ANY");
+
+ let mut header_ = Vec::new();
+ if header_len > 0 {
+ let header = header.expect("HEADER is NULL");
+ let header = unsafe {
+ slice::from_raw_parts(header, header_len)
+ };
+ for h in header {
+ assert!(! h.key.is_null());
+ assert!(! h.value.is_null());
+ header_.push(unsafe {
+ (CStr::from_ptr(h.key).to_string_lossy(),
+ CStr::from_ptr(h.value).to_string_lossy())
+ });
+ }
+ }
+
+ let header: Vec<(&str, &str)> =
+ header_.iter().map(|h| (h.0.as_ref(), h.1.as_ref())).collect();
+
+ fry_box!(ctx, armor::Writer::new(inner, kind, &header)
+ .map(|r| Box::new(r))
+ .map_err(|e| e.into()))
+}
+
+
+/* openpgp::PacketPile. */
+
+/// Deserializes the OpenPGP message stored in a `std::io::Read`
+/// object.
+///
+/// Although this method is easier to use to parse an OpenPGP
+/// message than a `PacketParser` or a `PacketPileParser`, this
+/// interface buffers the whole message in memory. Thus, the
+/// caller must be certain that the *deserialized* message is not
+/// too large.
+///
+/// Note: this interface *does* buffer the contents of packets.
+#[no_mangle]
+pub extern "system" fn sq_packet_pile_from_reader(ctx: Option<&mut Context>,
+ reader: Option<&mut Box<Read>>)
+ -> *mut PacketPile {
+ let ctx = ctx.expect("Context is NULL");
+ let reader = reader.expect("Reader is NULL");
+ fry_box!(ctx, PacketPile::from_reader(reader))
+}
+
+/// Deserializes the OpenPGP message stored in the file named by
+/// `filename`.
+///
+/// See `sq_packet_pile_from_reader` for more details and caveats.
+#[no_mangle]
+pub extern "system" fn sq_packet_pile_from_file(ctx: Option<&mut Context>,
+ filename: *const c_char)
+ -> *mut PacketPile {
+ let ctx = ctx.expect("Context is NULL");
+ assert!(! filename.is_null());
+ let filename = unsafe {
+ CStr::from_ptr(filename).to_string_lossy().into_owned()
+ };
+ fry_box!(ctx, PacketPile::from_file(&filename))
+}
+
+/// Deserializes the OpenPGP message stored in the provided buffer.
+///
+/// See `sq_packet_pile_from_reader` for more details and caveats.
+#[no_mangle]
+pub extern "system" fn sq_packet_pile_from_bytes(ctx: Option<&mut Context>,
+ b: *const uint8_t, len: size_t)
+ -> *mut PacketPile {
+ let ctx = ctx.expect("Context is NULL");
+ assert!(!b.is_null());
+ let buf = unsafe {
+ slice::from_raw_parts(b, len as usize)
+ };
+
+ fry_box!(ctx, PacketPile::from_bytes(buf))
+}
+
+/// Frees the packet_pile.
+#[no_mangle]
+pub extern "system" fn sq_packet_pile_free(packet_pile: *mut PacketPile) {
+ if packet_pile.is_null() {
+ return
+ }
+ unsafe {
+ drop(Box::from_raw(packet_pile));
+ }
+}
+
+/// Clones the PacketPile.
+#[no_mangle]
+pub extern "system" fn sq_packet_pile_clone(packet_pile: Option<&PacketPile>)
+ -> *mut PacketPile {
+ let packet_pile = packet_pile.expect("PacketPile is NULL");
+ box_raw!(packet_pile.clone())
+}
+
+/// Serializes the packet pile.
+#[no_mangle]
+pub extern "system" fn sq_packet_pile_serialize(ctx: Option<&mut Context>,
+ packet_pile: Option<&PacketPile>,
+ writer: Option<&mut Box<Write>>)
+ -> Status {
+ let ctx = ctx.expect("Context is NULL");
+ let packet_pile = packet_pile.expect("PacketPile is NULL");
+ let writer = writer.expect("Writer is NULL");
+ fry_status!(ctx, packet_pile.serialize(writer))
+}
+
+
+/* sequoia::keys. */
+
+/// Returns the first TPK encountered in the reader.
+#[no_mangle]
+pub extern "system" fn sq_tpk_from_reader(ctx: Option<&mut Context>,
+ reader: Option<&mut Box<Read>>)
+ -> *mut TPK {
+ let ctx = ctx.expect("Context is NULL");
+ let reader = reader.expect("Reader is NULL");
+ fry_box!(ctx, TPK::from_reader(reader))
+}
+
+/// Returns the first TPK encountered in the file.
+#[no_mangle]
+pub extern "system" fn sq_tpk_from_file(ctx: Option<&mut Context>,
+ filename: *const c_char)
+ -> *mut TPK {
+ let ctx = ctx.expect("Context is NULL");
+ assert!(! filename.is_null());
+ let filename = unsafe {
+ CStr::from_ptr(filename).to_string_lossy().into_owned()
+ };
+ fry_box!(ctx, TPK::from_file(&filename))
+}
+
+/// Returns the first TPK found in `m`.
+///
+/// Consumes `m`.
+#[no_mangle]
+pub extern "system" fn sq_tpk_from_packet_pile(ctx: Option<&mut Context>,
+ m: *mut PacketPile)
+ -> *mut TPK {
+ let ctx = ctx.expect("Context is NULL");
+ assert!(! m.is_null());
+ let m = unsafe { Box::from_raw(m) };
+ fry_box!(ctx, TPK::from_packet_pile(*m))
+}
+
+/// Returns the first TPK found in `buf`.
+///
+/// `buf` must be an OpenPGP-encoded TPK.
+#[no_mangle]
+pub extern "system" fn sq_tpk_from_bytes(ctx: Option<&mut Context>,
+ b: *const uint8_t, len: size_t)
+ -> *mut TPK {
+ let ctx = ctx.expect("Context is NULL");
+ assert!(!b.is_null());
+ let buf = unsafe {
+ slice::from_raw_parts(b, len as usize)
+ };
+
+ fry_box!(ctx, TPK::from_bytes(buf))
+}
+
+/// Returns the first TPK found in the packet parser.
+///
+/// Consumes the packet parser result.
+#[no_mangle]
+pub extern "system" fn sq_tpk_from_packet_parser(ctx: Option<&mut Context>,
+ ppr: *mut PacketParserResult)
+ -> *mut TPK
+{
+ let ctx = ctx.expect("Context is NULL");
+ assert!(! ppr.is_null());
+ let ppr = unsafe { Box::from_raw(ppr) };
+
+ fry_box!(ctx, TPK::from_packet_parser(*ppr))
+}
+
+/// Frees the TPK.
+#[no_mangle]
+pub extern "system" fn sq_tpk_free(tpk: *mut TPK) {
+ if tpk.is_null() {
+ return
+ }
+ unsafe {
+ drop(Box::from_raw(tpk));
+ }
+}
+
+/// Clones the TPK.
+#[no_mangle]
+pub extern "system" fn sq_tpk_clone(tpk: Option<&TPK>)
+ -> *mut TPK {
+ let tpk = tpk.expect("TPK is NULL");
+ box_raw!(tpk.clone())
+}
+
+/// Compares TPKs.
+#[no_mangle]
+pub extern "system" fn sq_tpk_equal(a: Option<&TPK>,
+ b: Option<&TPK>)
+ -> bool {
+ let a = a.expect("TPK 'a' is NULL");
+ let b = b.expect("TPK 'b' is NULL");
+ a == b
+}
+
+/// Serializes the TPK.
+#[no_mangle]
+pub extern "system" fn sq_tpk_serialize(ctx: Option<&mut Context>,
+ tpk: Option<&TPK>,
+ writer: Option<&mut Box<Write>>)
+ -> Status {
+ let ctx = ctx.expect("Context is NULL");
+ let tpk = tpk.expect("TPK is NULL");
+ let writer = writer.expect("Writer is NULL");
+ fry_status!(ctx, tpk.serialize(writer))
+}
+
+/// Merges `other` into `tpk`.
+///
+/// If `other` is a different key, then nothing is merged into
+/// `tpk`, but `tpk` is still canonicalized.
+///
+/// Consumes `tpk` and `other`.
+#[no_mangle]
+pub extern "system" fn sq_tpk_merge(ctx: Option<&mut Context>,
+