summaryrefslogtreecommitdiffstats
path: root/openpgp-ffi
diff options
context:
space:
mode:
authorJustus Winter <justus@sequoia-pgp.org>2019-01-17 11:11:27 +0100
committerJustus Winter <justus@sequoia-pgp.org>2019-01-17 16:48:28 +0100
commit3f58832474a4b270e136544016a401ef773ac065 (patch)
treec617160250c3040ca964c1b72ab5957cd872b82f /openpgp-ffi
parent38b4108cc1eac851ac17932c5c33623dd535bebb (diff)
openpgp-ffi: New crate.
- This creates a new crate, 'sequoia-openpgp-ffi', and moves a handful of functions from 'sequoia-ffi' to it. - The 'sequoia-ffi' crate is a superset of the 'sequoia-openpgp-ffi' crate. This is accomplished by some include! magic. - My first attempt involved having 'sequoia-ffi' depend on 'sequoia-openpgp-ffi', so that the former just re-exports the symbols. However, that turned out to be unreliable, and might be not what we want, because it could also duplicate parts of Rust's standard library. - Fixes #144.
Diffstat (limited to 'openpgp-ffi')
-rw-r--r--openpgp-ffi/Cargo.toml35
-rw-r--r--openpgp-ffi/Makefile66
-rw-r--r--openpgp-ffi/examples/.gitignore5
-rw-r--r--openpgp-ffi/examples/Makefile17
-rw-r--r--openpgp-ffi/examples/armor.c60
-rw-r--r--openpgp-ffi/examples/encrypt-for.c96
-rw-r--r--openpgp-ffi/examples/example.c45
-rw-r--r--openpgp-ffi/examples/parser.c93
-rw-r--r--openpgp-ffi/examples/reader.c48
-rw-r--r--openpgp-ffi/include/sequoia/io.h83
-rw-r--r--openpgp-ffi/include/sequoia/openpgp.h1663
-rw-r--r--openpgp-ffi/include/sequoia/openpgp/crypto.h50
-rw-r--r--openpgp-ffi/include/sequoia/openpgp/error.h147
-rw-r--r--openpgp-ffi/rustdoc.head.html4
-rw-r--r--openpgp-ffi/sequoia-openpgp.pc.in11
-rw-r--r--openpgp-ffi/src/armor.rs347
-rw-r--r--openpgp-ffi/src/common.rs1873
-rw-r--r--openpgp-ffi/src/crypto.rs54
-rw-r--r--openpgp-ffi/src/error.rs173
-rw-r--r--openpgp-ffi/src/fingerprint.rs110
-rw-r--r--openpgp-ffi/src/io.rs175
-rw-r--r--openpgp-ffi/src/keyid.rs99
-rw-r--r--openpgp-ffi/src/lib.rs118
-rw-r--r--openpgp-ffi/src/mod.rs0
-rw-r--r--openpgp-ffi/src/packet_pile.rs93
-rw-r--r--openpgp-ffi/src/tpk.rs698
-rw-r--r--openpgp-ffi/src/tsk.rs76
-rw-r--r--openpgp-ffi/tests/c-tests.rs303
28 files changed, 6542 insertions, 0 deletions
diff --git a/openpgp-ffi/Cargo.toml b/openpgp-ffi/Cargo.toml
new file mode 100644
index 00000000..1bb6aab1
--- /dev/null
+++ b/openpgp-ffi/Cargo.toml
@@ -0,0 +1,35 @@
+[package]
+name = "sequoia-openpgp-ffi"
+description = "C API for Sequoia's low-level OpenPGP crate"
+version = "0.3.0"
+authors = [
+ "Justus Winter <justus@sequoia-pgp.org>",
+ "Kai Michaelis <kai@sequoia-pgp.org>",
+ "Neal H. Walfield <neal@sequoia-pgp.org>",
+]
+documentation = "https://docs.sequoia-pgp.org/sequoia_openpgp_ffi"
+homepage = "https://sequoia-pgp.org/"
+repository = "https://gitlab.com/sequoia-pgp/sequoia"
+readme = "../README.md"
+keywords = ["cryptography", "openpgp", "pgp", "encryption", "signing"]
+categories = ["cryptography", "authentication", "development-tools::ffi",
+ "email"]
+license = "GPL-3.0"
+
+[badges]
+gitlab = { repository = "sequoia-pgp/sequoia" }
+maintenance = { status = "actively-developed" }
+
+[dependencies]
+sequoia-ffi-macros = { path = "../ffi-macros" }
+sequoia-openpgp = { path = "../openpgp" }
+failure = "0.1.2"
+lazy_static = "1.0.0"
+libc = "0.2.33"
+time = "0.1.40"
+
+[dev-dependencies]
+nettle = "2.0"
+
+[lib]
+crate-type = ["lib", "cdylib", "staticlib"]
diff --git a/openpgp-ffi/Makefile b/openpgp-ffi/Makefile
new file mode 100644
index 00000000..ba4e1d0f
--- /dev/null
+++ b/openpgp-ffi/Makefile
@@ -0,0 +1,66 @@
+# Makefile for Sequoia's bindings.
+
+# Configuration.
+PREFIX ?= /usr/local
+DESTDIR ?=
+
+CARGO ?= cargo
+CARGO_TARGET_DIR ?= $(shell pwd)/../target
+# We currently only support absolute paths.
+CARGO_TARGET_DIR := $(abspath $(CARGO_TARGET_DIR))
+
+VERSION ?= $(shell grep '^version[[:space:]]*=[[:space:]]*' Cargo.toml | cut -d'"' -f2)
+VERSION_MAJOR = $(shell echo $(VERSION) | cut -d'.' -f1)
+
+# Tools.
+INSTALL ?= install
+
+# Make sure subprocesses pick these up.
+export PREFIX
+export DESTDIR
+
+all: build
+
+.PHONY: build
+build:
+ :
+
+# Testing and examples.
+.PHONY: test check
+test check:
+ :
+
+.PHONY: examples
+examples:
+ $(MAKE) -Cexamples
+
+# Installation.
+.PHONY: build-release
+build-release:
+ :
+
+.PHONY: install
+install:
+ $(INSTALL) -d $(DESTDIR)$(PREFIX)/share/pkgconfig
+ sed -e 's|VERSION|$(VERSION)|g' \
+ -e 's|PREFIX|$(PREFIX)|g' \
+ sequoia-openpgp.pc.in \
+ > $(DESTDIR)$(PREFIX)/share/pkgconfig/sequoia-openpgp.pc
+ $(INSTALL) -d $(DESTDIR)$(PREFIX)/include
+ $(INSTALL) -d $(DESTDIR)$(PREFIX)/include/sequoia
+ $(INSTALL) -t $(DESTDIR)$(PREFIX)/include/sequoia \
+ include/sequoia/*.h
+ $(INSTALL) -d $(DESTDIR)$(PREFIX)/lib
+ $(INSTALL) $(CARGO_TARGET_DIR)/release/libsequoia_openpgp_ffi.so \
+ $(DESTDIR)$(PREFIX)/lib/libsequoia_openpgp_ffi.so.$(VERSION)
+ ln -fs libsequoia_openpgp_ffi.so.$(VERSION) \
+ $(DESTDIR)$(PREFIX)/lib/libsequoia_openpgp_ffi.so.$(VERSION_MAJOR)
+ ln -fs libsequoia_openpgp_ffi.so.$(VERSION) \
+ $(DESTDIR)$(PREFIX)/lib/libsequoia_openpgp_ffi.so
+ $(INSTALL) $(CARGO_TARGET_DIR)/release/libsequoia_openpgp_ffi.a \
+ $(DESTDIR)$(PREFIX)/lib/libsequoia_openpgp_ffi.a
+
+# Housekeeping.
+.PHONY: clean
+clean:
+ $(MAKE) -Cexamples clean
diff --git a/openpgp-ffi/examples/.gitignore b/openpgp-ffi/examples/.gitignore
new file mode 100644
index 00000000..6a47c497
--- /dev/null
+++ b/openpgp-ffi/examples/.gitignore
@@ -0,0 +1,5 @@
+armor
+encrypt-for
+example
+parser
+reader
diff --git a/openpgp-ffi/examples/Makefile b/openpgp-ffi/examples/Makefile
new file mode 100644
index 00000000..29408634
--- /dev/null
+++ b/openpgp-ffi/examples/Makefile
@@ -0,0 +1,17 @@
+# Makefile for examples written in C.
+
+CARGO ?= cargo
+CARGO_TARGET_DIR ?= $(shell pwd)/../../target
+# We currently only support absolute paths.
+CARGO_TARGET_DIR := $(abspath $(CARGO_TARGET_DIR))
+
+TARGETS = example reader parser encrypt-for armor
+CFLAGS = -I../include -O0 -g -Wall -Werror
+LDFLAGS = -L$(CARGO_TARGET_DIR)/debug -lsequoia_openpgp_ffi
+
+all: $(TARGETS)
+
+clean:
+ rm -f $(TARGETS)
+
+$(TARGETS): ../include/sequoia/openpgp.h
diff --git a/openpgp-ffi/examples/armor.c b/openpgp-ffi/examples/armor.c
new file mode 100644
index 00000000..9b965c5b
--- /dev/null
+++ b/openpgp-ffi/examples/armor.c
@@ -0,0 +1,60 @@
+#define _GNU_SOURCE
+#include <assert.h>
+#include <error.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sequoia/openpgp.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_reader_t bytes;
+ sq_reader_t armor;
+ sq_armor_kind_t kind;
+ char message[12];
+ sq_armor_header_t *header;
+ size_t header_len;
+
+ 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 (&err, armor, &header_len);
+ if (header == NULL)
+ 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 (&err, armor, (uint8_t *) message, 12) < 0)
+ 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);
+ return 0;
+}
diff --git a/openpgp-ffi/examples/encrypt-for.c b/openpgp-ffi/examples/encrypt-for.c
new file mode 100644
index 00000000..ab0943ba
--- /dev/null
+++ b/openpgp-ffi/examples/encrypt-for.c
@@ -0,0 +1,96 @@
+/* This example demonstrates how to use the low-level interface to
+ encrypt a file. */
+
+#define _GNU_SOURCE
+#include <error.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <time.h>
+
+#include <sequoia/openpgp.h>
+
+int
+main (int argc, char **argv)
+{
+ struct stat st;
+ int fd;
+ uint8_t *b;
+ sq_status_t rc;
+ sq_error_t err;
+ int use_armor = 1;
+ sq_tpk_t tpk;
+ sq_writer_t sink;
+ sq_writer_stack_t writer = NULL;
+ void *cipher = NULL;
+ size_t cipher_bytes = 0;
+
+ if (argc != 2)
+ error (1, 0, "Usage: %s <keyfile> <plain >cipher", argv[0]);
+
+ if (stat (argv[1], &st))
+ error (1, errno, "%s", argv[1]);
+
+ fd = open (argv[1], O_RDONLY);
+ if (fd == -1)
+ error (1, errno, "%s", argv[1]);
+
+ b = mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ close (fd);
+ if (b == MAP_FAILED)
+ error (1, errno, "mmap");
+
+ tpk = sq_tpk_from_bytes (&err, b, st.st_size);
+ if (tpk == NULL)
+ error (1, 0, "sq_packet_parser_from_bytes: %s", sq_error_string (err));
+
+ sink = sq_writer_alloc (&cipher, &cipher_bytes);
+
+ if (use_armor)
+ sink = sq_armor_writer_new (&err, sink, SQ_ARMOR_KIND_MESSAGE,
+ NULL, 0);
+
+ writer = sq_writer_stack_message (sink);
+ writer = sq_encryptor_new (&err,
+ writer,
+ NULL, 0, /* no passwords */
+ &tpk, 1,
+ SQ_ENCRYPTION_MODE_FOR_TRANSPORT);
+ if (writer == NULL)
+ error (1, 0, "sq_encryptor_new: %s", sq_error_string (err));
+
+ writer = sq_literal_writer_new (&err, writer);
+ if (writer == NULL)
+ error (1, 0, "sq_literal_writer_new: %s", sq_error_string (err));
+
+ size_t nread;
+ uint8_t buf[4096];
+ while ((nread = fread (buf, 1, sizeof buf, stdin)))
+ {
+ uint8_t *b = buf;
+ while (nread)
+ {
+ ssize_t written;
+ written = sq_writer_stack_write (&err, writer, b, nread);
+ if (written < 0)
+ error (1, 0, "sq_writer_stack_write: %s", sq_error_string (err));
+
+ b += written;
+ nread -= written;
+ }
+ }
+
+ rc = sq_writer_stack_finalize (&err, writer);
+ writer = NULL;
+ if (rc)
+ error (1, 0, "sq_writer_stack_write: %s", sq_error_string (err));
+
+ fwrite (cipher, 1, cipher_bytes, stdout);
+
+ munmap (b, st.st_size);
+ return 0;
+}
diff --git a/openpgp-ffi/examples/example.c b/openpgp-ffi/examples/example.c
new file mode 100644
index 00000000..ac23ff57
--- /dev/null
+++ b/openpgp-ffi/examples/example.c
@@ -0,0 +1,45 @@
+#define _GNU_SOURCE
+#include <error.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <sequoia/openpgp.h>
+
+int
+main (int argc, char **argv)
+{
+ struct stat st;
+ int fd;
+ uint8_t *b;
+ sq_error_t err;
+ sq_tpk_t tpk;
+
+ if (argc != 2)
+ error (1, 0, "Usage: %s <file>", argv[0]);
+
+ if (stat (argv[1], &st))
+ error (1, errno, "%s", argv[1]);
+
+ fd = open (argv[1], O_RDONLY);
+ if (fd == -1)
+ error (1, errno, "%s", argv[1]);
+
+ b = mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ if (b == MAP_FAILED)
+ error (1, errno, "mmap");
+
+ tpk = sq_tpk_from_bytes (&err, b, st.st_size);
+ if (tpk == NULL)
+ error (1, 0, "sq_tpk_from_bytes: %s", sq_error_string (err));
+
+ sq_tpk_dump (tpk);
+ sq_tpk_free (tpk);
+ munmap (b, st.st_size);
+ close (fd);
+ return 0;
+}
diff --git a/openpgp-ffi/examples/parser.c b/openpgp-ffi/examples/parser.c
new file mode 100644
index 00000000..81b097af
--- /dev/null
+++ b/openpgp-ffi/examples/parser.c
@@ -0,0 +1,93 @@
+/* This example demonstrates how to use the packet parser from C. It
+ * also serves as a simple benchmark. */
+
+#define _GNU_SOURCE
+#include <error.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <time.h>
+
+#include <sequoia/openpgp.h>
+
+int
+main (int argc, char **argv)
+{
+ struct stat st;
+ int fd;
+ uint8_t *b;
+ sq_status_t rc;
+ sq_error_t err;
+ sq_packet_parser_result_t ppr;
+ sq_packet_parser_t pp;
+
+ if (argc != 2)
+ error (1, 0, "Usage: %s <file>", argv[0]);
+
+ if (stat (argv[1], &st))
+ error (1, errno, "%s", argv[1]);
+
+ fd = open (argv[1], O_RDONLY);
+ if (fd == -1)
+ error (1, errno, "%s", argv[1]);
+
+ b = mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ close (fd);
+ if (b == MAP_FAILED)
+ error (1, errno, "mmap");
+
+ size_t n = 0;
+ time_t start = time (NULL);
+ time_t elapsed;
+ size_t tens_of_s = 0;
+
+ ppr = sq_packet_parser_from_bytes (&err, b, st.st_size);
+ while (ppr && (pp = sq_packet_parser_result_packet_parser (ppr)))
+ {
+ // Get a reference to the packet that is currently being parsed.
+ sq_packet_t p = sq_packet_parser_packet (pp);
+
+ if (sq_packet_tag(p) == SQ_TAG_LITERAL)
+ {
+ // Stream the packet here.
+ }
+
+ // Finish parsing the current packet (returned in p), and read
+ // the header of the next packet (returned in ppr).
+ rc = sq_packet_parser_next (&err, pp, &p, &ppr);
+ if (rc)
+ error (1, 0, "sq_packet_parser_from_bytes: %s",
+ sq_error_string (err));
+
+ // We now own p. If we want, we can save it in some structure.
+ // This would be useful when collecting PKESK packets. Either
+ // way, we need to free it when we are done.
+
+ n += 1;
+
+ sq_packet_free (p);
+
+ elapsed = time (NULL) - start;
+ if (elapsed % 10 == 0 && tens_of_s != elapsed / 10)
+ {
+ fprintf (stderr,
+ "Parsed %ld packets in %ld seconds, %.2f packets/s.\n",
+ n, elapsed, (double) n / (double) elapsed);
+ fflush (stderr);
+ tens_of_s = elapsed / 10;
+ }
+ }
+ if (ppr == NULL)
+ error (1, 0, "sq_packet_parser_from_bytes: %s", sq_error_string (err));
+
+ fprintf (stderr, "Parsed %ld packets in %ld seconds, %.2f packets/s.\n",
+ n, elapsed, (double) n / (double) elapsed);
+
+ sq_packet_parser_result_free (ppr);
+ munmap (b, st.st_size);
+ return 0;
+}
diff --git a/openpgp-ffi/examples/reader.c b/openpgp-ffi/examples/reader.c
new file mode 100644
index 00000000..019d87d9
--- /dev/null
+++ b/openpgp-ffi/examples/reader.c
@@ -0,0 +1,48 @@
+#define _GNU_SOURCE
+#include <error.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <sequoia/openpgp.h>
+
+int
+main (int argc, char **argv)
+{
+ struct stat st;
+ int fd;
+ uint8_t *b;
+ sq_error_t err;
+ sq_reader_t reader;
+ sq_tpk_t tpk;
+
+ if (argc != 2)
+ error (1, 0, "Usage: %s <file>", argv[0]);
+
+ if (stat (argv[1], &st))
+ error (1, errno, "%s", argv[1]);
+
+ fd = open (argv[1], O_RDONLY);
+ if (fd == -1)
+ error (1, errno, "%s", argv[1]);
+
+ b = mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ if (b == MAP_FAILED)
+ error (1, errno, "mmap");
+
+ reader = sq_reader_from_bytes (b, st.st_size);
+ tpk = sq_tpk_from_reader (&err, reader);
+ if (tpk == NULL)
+ error (1, 0, "sq_tpk_from_reader: %s", sq_error_string (err));
+
+ sq_tpk_dump (tpk);
+ sq_tpk_free (tpk);
+ sq_reader_free (reader);
+ munmap (b, st.st_size);
+ close (fd);
+ return 0;
+}
diff --git a/openpgp-ffi/include/sequoia/io.h b/openpgp-ffi/include/sequoia/io.h
new file mode 100644
index 00000000..c84de166
--- /dev/null
+++ b/openpgp-ffi/include/sequoia/io.h
@@ -0,0 +1,83 @@
+#ifndef SEQUOIA_IO_H
+#define SEQUOIA_IO_H
+
+#include <sequoia/openpgp/error.h>
+
+/*/
+/// A generic reader.
+/*/
+typedef struct sq_reader *sq_reader_t;
+
+/*/
+/// Opens a file returning a reader.
+/*/
+sq_reader_t sq_reader_from_file (sq_error_t *errp, const char *filename);
+
+/*/
+/// Opens a file descriptor returning a reader.
+/*/
+sq_reader_t sq_reader_from_fd (int fd);
+
+/*/
+/// Creates a reader from a buffer.
+/*/
+sq_reader_t sq_reader_from_bytes (const uint8_t *buf, size_t len);
+
+/*/
+/// Frees a reader.
+/*/
+void sq_reader_free (sq_reader_t reader);
+
+/*/
+/// Reads up to `len` bytes into `buf`.
+/*/
+ssize_t sq_reader_read (sq_error_t *errp, sq_reader_t reader,
+ uint8_t *buf, size_t len);
+
+/*/
+/// A generic writer.
+/*/
+typedef struct sq_writer *sq_writer_t;
+
+/*/
+/// Opens a file returning a writer.
+///
+/// The file will be created if it does not exist, or be truncated
+/// otherwise. If you need more control, use `sq_writer_from_fd`.
+/*/
+sq_writer_t sq_writer_from_file (sq_error_t *errp, const char *filename);
+
+/*/
+/// Opens a file descriptor returning a writer.
+/*/
+sq_writer_t sq_writer_from_fd (int fd);
+
+/*/
+/// Creates a writer from a buffer.
+/*/
+sq_writer_t sq_writer_from_bytes (uint8_t *buf, size_t len);
+
+/*/
+/// Creates an allocating writer.
+///
+/// This writer allocates memory using `malloc`, and stores the
+/// pointer to the memory and the number of bytes written to the given
+/// locations `buf`, and `len`. Both must either be set to zero, or
+/// reference a chunk of memory allocated using libc's heap allocator.
+/// The caller is responsible to `free` it once the writer has been
+/// destroyed.
+/*/
+sq_writer_t sq_writer_alloc (void **buf, size_t *len);
+
+/*/
+/// Frees a writer.
+/*/
+void sq_writer_free (sq_writer_t writer);
+
+/*/
+/// Writes up to `len` bytes of `buf` into `writer`.
+/*/
+ssize_t sq_writer_write (sq_error_t *errp, sq_writer_t writer,
+ const uint8_t *buf, size_t len);
+
+#endif
diff --git a/openpgp-ffi/include/sequoia/openpgp.h b/openpgp-ffi/include/sequoia/openpgp.h
new file mode 100644
index 00000000..ddc6ed5a
--- /dev/null
+++ b/openpgp-ffi/include/sequoia/openpgp.h
@@ -0,0 +1,1663 @@
+#ifndef SEQUOIA_OPENPGP_H
+#define SEQUOIA_OPENPGP_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <limits.h>
+#include <unistd.h>
+#include <time.h>
+
+#include <sequoia/io.h>
+#include <sequoia/openpgp/error.h>
+#include <sequoia/openpgp/crypto.h>
+
+/*/
+/// A low-level OpenPGP message parser.
+///
+/// A `PacketParser` provides a low-level, iterator-like interface to
+/// parse OpenPGP messages.
+///
+/// For each iteration, the user is presented with a [`Packet`]
+/// corresponding to the last packet, a `PacketParser` for the next
+/// packet, and their positions within the message.
+///
+/// Using the `PacketParser`, the user is able to configure how the
+/// new packet will be parsed. For instance, it is possible to stream
+/// the packet's contents (a `PacketParser` implements the
+/// `std::io::Read` and the `BufferedReader` traits), buffer them
+/// within the [`Packet`], or drop them. The user can also decide to
+/// recurse into the packet, if it is a container, instead of getting
+/// the following packet.
+/*/
+typedef struct sq_packet_parser *sq_packet_parser_t;
+
+/*/
+/// Like an `Option<PacketParser>`, but the `None` variant
+/// (`PacketParserEOF`) contains some summary information.
+/*/
+typedef struct sq_packet_parser_result *sq_packet_parser_result_t;
+
+/*/
+/// The `None` variant of a `PacketParserResult`.
+/*/
+typedef struct sq_packet_parser_eof *sq_packet_parser_eof_t;
+
+/* sequoia::openpgp::KeyID. */
+
+/*/
+/// Holds a KeyID.
+/*/
+typedef struct sq_keyid *sq_keyid_t;
+
+/*/
+/// Reads a binary key ID.
+/*/
+sq_keyid_t sq_keyid_from_bytes (const uint8_t *id);
+
+/*/
+/// Reads a hex-encoded Key ID.
+/*/
+sq_keyid_t sq_keyid_from_hex (const char *id);
+
+/*/
+/// Frees a sq_keyid_t.
+/*/
+void sq_keyid_free (sq_keyid_t keyid);
+
+/*/
+/// Clones the