From 2c2ce24b4eb02beae3365a705a7db49636711241 Mon Sep 17 00:00:00 2001 From: Justus Winter Date: Fri, 18 Jan 2019 20:59:56 +0100 Subject: openpgp-ffi: Improve documentation. - Prepend the documentation for the openpgp crate as introduction. - Explain how to read the documentation, examples, Rust types. - Explain the FFI contract. --- openpgp-ffi/src/lib.rs | 303 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 253 insertions(+), 50 deletions(-) (limited to 'openpgp-ffi') diff --git a/openpgp-ffi/src/lib.rs b/openpgp-ffi/src/lib.rs index a40c64a2..92f546f5 100644 --- a/openpgp-ffi/src/lib.rs +++ b/openpgp-ffi/src/lib.rs @@ -1,62 +1,234 @@ -//! Provides a Foreign Function Interface. +//! OpenPGP data types and associated machinery for C. +//! +//! This crate aims to provide a complete implementation of OpenPGP as +//! defined by [RFC 4880] as well as several extensions (e.g., [RFC +//! 6637], which describes ECC cryptography for OpenPGP, and [RFC +//! 4880bis], the draft of the next OpenPGP standard). This includes +//! support for unbuffered message processing. +//! +//! A few features that the OpenPGP community considers to be +//! deprecated (e.g., version 3 compatibility) have been left out as +//! well as support for functionality that we consider to be not only +//! completely useless, but also dangerous (e.g., support for +//! [unhashed signature subpackets]). We have also updated some +//! OpenPGP defaults to avoid foot guns (e.g., this crate does not +//! fallback to IDEA, but instead assumes all OpenPGP implementations +//! understand AES). If some functionality is missing, please file a +//! bug report. +//! +//! A non-goal of this crate is support for any sort of high-level, +//! bolted-on functionality. For instance, [RFC 4880] does not define +//! trust models, such as the web of trust, direct trust, or TOFU. +//! Neither does this crate. [RFC 4880] does provide some mechanisms +//! for creating trust models (specifically, UserID certifications), +//! and this crate does expose those mechanisms. +//! +//! We also try hard to avoid dictating how OpenPGP should be used. +//! This doesn't mean that we don't have opinions about how OpenPGP +//! should be used in a number of common scenarios (for instance, +//! message validation). But, in this crate, we refrain from +//! expressing those opinions; we expose an opinionated, high-level +//! interface in the [sequoia-core] and related crates. In our +//! opinion, you should generally use those crates instead of this +//! one. +//! +//! [RFC 4880]: https://tools.ietf.org/html/rfc4880 +//! [RFC 6637]: https://tools.ietf.org/html/rfc6637 +//! [RFC 4880bis]: https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-05 +//! [unhashed signature subpackets]: https://tools.ietf.org/html/rfc4880#section-5.2.3.2 +//! [sequoia-core]: ../sequoia_core +//! +//! +//! # Dear user, +//! +//! this library is written in Rust. Its expressive type system helps +//! to prevent many classes of bugs, but it is also very demanding. +//! For example, the infamous borrow checker, feared by those who dare +//! to approach Rust, allows us to safely use shared resources. If +//! you want to use this library, we need you to follow a set of +//! rules, and you don't have the borrow checker to double check. //! //! We provide a set of functions that use C types and the C calling //! convention. This interfaces allows you to use Sequoia safely from -//! any other language. +//! any other language, provided that you follow [The Rules]. //! -//! # Guarantees +//! If you don't follow [The Rules], we will terminate the process. +//! That is not meant as a threat, of course. But, as in C, passing a +//! bad pointer to a function is undefined behavior in Rust. +//! Therefore, passing a bad pointer to a function in this crate is +//! undefined behavior as well. We try to mitigate this problem by +//! trying to detect undefined behavior resulting from not following +//! [The Rules] by printing assertion failures on stderr, and +//! terminating the process. //! -//! Provided that the caller obeys her side of the contract, this -//! library... +//! In return for following [The Rules], you can enjoy the reliability +//! that Rust's type system brings this library. For example, the +//! robust tracking of ownership ensures that we will not leak memory. //! -//! - will not make an invalid memory access, -//! - will not `abort(2)`, -//! - XXX +//! The C API is documented here. We invite you to visit the +//! documentation of the [corresponding Rust crate], the structure +//! closely resembles this crate. //! -//! # Types +//! [The Rules]: #the-rules +//! [corresponding Rust crate]: ../sequoia_openpgp/index.html +//! +//! +//! # Examples +//! +//! This documentation contains examples in the style of Rust +//! examples. For short examples, we will omit the main function. +//! For example: +//! +//! ```c +//! #include +//! #include +//! +//! pgp_error_t err; +//! pgp_tpk_t tpk = pgp_tpk_from_file (&err, "../openpgp/tests/data/keys/testy.pgp"); +//! if (tpk == NULL) +//! error (1, 0, "pgp_tpk_from_file: %s", pgp_error_string (err)); +//! +//! /* XXX: Do something interesting. */ +//! +//! pgp_tpk_free (tpk); +//! ``` +//! +//! The examples are compiled and executed as part of the test suite, +//! and they will free all objects that are created. +//! +//! +//! # The Rules +//! +//! These rules must be followed. Failure to follow these rules +//! results in problems like memory leaks, undefined behavior, or +//! process termination. This is also a guide on how to read the +//! documentation for this library. +//! +//! ## Undefined Behavior +//! +//! Undefined behavior means that the state of the library is +//! inconsistent, and the resulting behavior is unpredictable. It +//! often leads to security problems, like information exfiltration, +//! or code execution. +//! +//! Undefined behavior may be the result of a bug in this library, or +//! due to failure to comply to [The Rules]. Furthermore, memory +//! exhaustion may lead to undefined behavior. +//! +//! This library tries to detect cases of undefined behavior, and +//! *abort(3)* the process. This fail-fast policy helps to protect +//! against exfiltration and code execution attacks, and should +//! clearly highlight either a bug in this library (please get in +//! contact!), or a bug in your code. +//! +//! ## Error handling +//! +//! Errors happen and must be handled by the caller. There are +//! functions that cannot fail, functions that may fail, and functions +//! that may fail and communicate a complex error value to the caller. +//! +//! Functions that cannot fail are a nice consequence of the +//! 'fail-fast on undefined behavior'-rule. An example of such +//! function is [`pgp_fingerprint_to_string`]. This function cannot +//! fail, unless either the given fingerprint reference is not valid, +//! or the allocation for the string failed, which is considered +//! undefined behavior. +//! +//! [`pgp_fingerprint_to_string`]: fingerprint/fn.pgp_fingerprint_to_string.html +//! +//! Failing functions signal failure either in-band (e.g. `NULL`, or +//! -1), using `pgp_status_t`, and may store complex error information +//! in a caller-provided location. For example, constructors often +//! return `NULL` to signal errors. An example of a constructor that +//! may fail and return `NULL`, but does not communicate complex error +//! is [`pgp_fingerprint_from_hex`]. [`pgp_packet_parser_from_bytes`], +//! on the other hand, will return `NULL` and store a complex error at +//! the location given using the `errp` parameter. +//! +//! [`pgp_fingerprint_from_hex`]: fingerprint/fn.pgp_fingerprint_from_hex.html +//! [`pgp_packet_parser_from_bytes`]: parse/fn.pgp_packet_parser_from_bytes.html +//! +//! Errors may be inspected using [`pgp_error_status`], and formatted +//! as error message using [`pgp_error_string`]. Errors must be freed +//! using [`pgp_error_free`]. +//! +//! [`pgp_error_status`]: error/fn.pgp_error_status.html +//! [`pgp_error_string`]: error/fn.pgp_error_string.html +//! [`pgp_error_free`]: error/fn.pgp_error_free.html +//! +//! ## Types +//! +//! When we interact across the FFI boundary between C and Rust, we +//! need to talk about the types used to interchange data. The types +//! follow different rules. +//! +//! ### Objects //! //! Sequoia objects are opaque objects. They are created in -//! constructors, and must be freed when no longer needed. +//! constructors, and must be freed when no longer needed. Failure to +//! free an object results in a memory leak. //! -//! Pointers handed to Sequoia must not be `NULL`, unless explicitly -//! stated. See [references]. +//! A typical example of creating an object, using it, and +//! deallocating it is the following. We will [parse a fingerprint] +//! from a hexadecimal number, and then [pretty-print] it for user +//! consumption: //! -//! [references]: #references +//! [parse a fingerprint]: fingerprint/fn.pgp_fingerprint_from_hex.html +//! [pretty-print]: fingerprint/fn.pgp_fingerprint_to_string.html //! -//! Enumeration-like values must be in the valid range. +//! ```c +//! #include +//! #include +//! #include +//! #include //! -//! Strings must be UTF-8 encoded and zero-terminated. Malformed -//! characters will be substituted, and the result is likely not what -//! you expect. +//! pgp_fingerprint_t fp = +//! pgp_fingerprint_from_hex ("D2F2C5D45BE9FDE6A4EE0AAF31855247603831FD"); //! -//! # Ownership +//! char *pretty = pgp_fingerprint_to_string (fp); +//! assert (strcmp (pretty, +//! "D2F2 C5D4 5BE9 FDE6 A4EE 0AAF 3185 5247 6038 31FD") == 0); //! -//! When ownership of a `T` is transferred across the FFI boundary, a -//! `*mut T` is used. +//! free (pretty); +//! pgp_fingerprint_free (fp); +//! ``` //! -//! To transfer ownership from Rust to C, we box the Rust object, and -//! use [`Box::into_raw(..)`]. From this moment on, ownership must be -//! managed by the C application. +//! After use, the fingerprint object must be deallocated. Note that +//! objects may reference other objects, so a good practice is to +//! deallocate the objects in the reverse order they were created in. //! -//! [`Box::into_raw(..)`]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.into_raw +//! #### Ownership //! -//! To transfer ownership from C to Rust, we re-create the box using -//! [`Box::from_raw(..)`]. +//! In Rust, every value is owned. It may reside on the stack, the +//! heap, or be embedded in a struct or enum. If you take ownership +//! of an object, e.g. by using some constructor, it is your +//! responsibility to manage its lifetime. The most common way to +//! transfer ownership back to Rust is to deallocate the object. +//! Failure to deallocate the object leads to a memory leak. //! -//! [`Box::from_raw(..)`]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.from_raw +//! Looking at the Rust functions in this library, when ownership of a +//! `T` is transferred across the FFI boundary, a `*mut T` is used. //! //! In this crate we use a series of macros to transfer ownership from -//! Rust to C. `fry_box` matches on `Result`, handling errors by -//! terminating the current function, returning the error using the -//! context. `maybe_box_raw` matches on `Option`, turning `None` -//! into `NULL`. Finally, `box_raw` is merely a shortcut for +//! Rust to C. `ffi_try_box` matches on `Result`, handling errors +//! by terminating the current function returning the error. +//! `maybe_box_raw` matches on `Option`, turning `None` into +//! `NULL`. Finally, `box_raw` is merely a shortcut for //! `Box::into_raw(Box::new(..))`. //! -//! # References +//! ### References +//! +//! All references transferred across the FFI boundary must be valid, +//! and point to live objects constructed using constructors of this +//! library. Using references constructed in any other way results in +//! undefined behavior. //! -//! When references are transferred across the FFI boundary, we use -//! `*const T`, or `*mut T`. If the parameter is optional, a -//! `Option<&T>` or `Option<&mut T>` is used. +//! In some places, references handed to, or returned from this +//! library may be `NULL`. This is the exception rather than the +//! rule. If you look at a functions Rust signature, a `Option<&T>` +//! or `Option<&mut T>` is used for arguments or results that may be +//! NULL. On the other hand, function arguments that are not optional +//! use `*const T`, or `*mut T`. //! //! Application code must adhere to Rust's reference rules: //! @@ -69,40 +241,71 @@ //! function uses `Option<&T>` or `Option<&mut T>`, it may be called //! with `NULL`. A notable example are the destructors (`sq_*_free`). //! -//! # Lifetimes +//! ### Lifetimes //! //! If you derive a complex object from another complex object, you //! must assume that the original object is borrowed by the resulting //! object unless explicitly stated otherwise. For example, objects //! created using a context must not outlive that context. Similarly, -//! iterators must not outlive the object they are created from. +//! iterators must not outlive the object they are created from. It +//! is a good practice is to deallocate the objects in the reverse +//! order they were created in. //! //! Failing to adhere to lifetime restrictions results in undefined //! behavior. //! -//! # Error handling +//! ### Strings +//! +//! Strings given to this library must be UTF-8 encoded and +//! zero-terminated. Malformed characters will be substituted. //! -//! Sequoia will panic if you provide bad arguments, e.g. hand a -//! `NULL` pointer to a function that does not explicitly allow this. +//! Strings produced by this library will be UTF-8 encoded and +//! zero-terminated. Malformed characters will be substituted. They +//! will be allocated using *malloc(3)* und must be *free(3)* d. A +//! few functions in this library may return a `const char *`, which +//! must not be freed, of course. //! -//! Failing functions return `NULL`. Functions that require a -//! `Context` return complex errors. Complex errors are stored in the -//! `Context`, and can be retrieved using `sq_last_strerror`. +//! ### Enumerations //! -//! # Example +//! Values must be constructed using the symbols or macros defined by +//! this library. Using values constructed in any other way is +//! undefined behavior. +//! +//! In the following example, we will decode an armored blob in +//! memory. Note how we use `PGP_ARMOR_KIND_ANY` in the [constructor] +//! of the armor reader to indicate that we will consume any kind of +//! data, and later use `PGP_ARMOR_KIND_FILE` to check what we got in +//! the end. +//! +//! [constructor]: armor/fn.pgp_armor_reader_from_bytes.html //! //! ```c -//! #include +//! #include //! #include +//! #include +//! #include +//! #include +//! #include +//! +//! const char *armored = +//! "-----BEGIN PGP ARMORED FILE-----\n" +//! "\n" +//! "SGVsbG8gd29ybGQh\n" +//! "=s4Gu\n" +//! "-----END PGP ARMORED FILE-----\n"; +//! +//! pgp_reader_t armor = +//! pgp_armor_reader_from_bytes (armored, strlen (armored), PGP_ARMOR_KIND_ANY); //! //! pgp_error_t err; -//! pgp_tpk_t tpk; +//! char message[12]; +//! if (pgp_reader_read (&err, armor, (uint8_t *) message, 12) != 12) +//! error (1, 0, "Reading failed: %s", pgp_error_string (err)); //! -//! tpk = pgp_tpk_from_file (&err, "../openpgp/tests/data/keys/testy.pgp"); -//! if (tpk == NULL) -//! error (1, 0, "pgp_tpk_from_bytes: %s", pgp_error_string (err)); +//! assert (pgp_armor_reader_kind (armor) == PGP_ARMOR_KIND_FILE); +//! assert (memcmp (message, "Hello world!", 12) == 0); //! -//! pgp_tpk_free (tpk); +//! pgp_reader_free (armor); //! ``` #![warn(missing_docs)] -- cgit v1.2.3