diff options
Diffstat (limited to 'ffi/src/openpgp/mod.rs')
-rw-r--r-- | ffi/src/openpgp/mod.rs | 3080 |
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>, + |