shopify
  analytics ecommerce

Introducing Mundane, a new cryptography library for Rust

Discuss this post on Reddit.

Mundane, meet the world. The world, meet Mundane.

Mundane is a cryptography library written in Rust and backed by BoringSSL. It aims to be difficult to misuse, ergonomic, and performant (in that order). It was originally created to serve the cryptography needs of Fuchsia, but we’ve decided to split it off as a general-purpose crate.

Why Mundane?

Mundane’s raison d’ĂȘtre is to provide an API which is difficult to misuse. Experience shows that one of the most common failure modes of cryptography is incorrect implementation, and often, that failure occurs at the boundary between an application and a cryptography library.

Given this experience, Mundane takes the approach of giving the programmer the fewest degrees of freedom possible. Doing the right thing should be easy and feel natural. Doing the wrong thing should feel difficult and ideally be entirely impossible.

Rust is the perfect language for a library with this design philosophy. Its expressive and strict type system allows us to ensure that, across a wide array of correctness properties, an incorrect program will not compile.

Consider, for example, the humble signature. A signature consists of two components: a hash of some input data, and a digital signature of that hash. Mundane’s Signature trait looks like this:

/// A cryptographic signature generated by a private key.
pub trait Signature: Sealed + Sized {
    /// The private key type used to generate this signature.
    type PrivateKey: PrivateKey;

    /// Sign a message.
    ///
    /// The input to this function is always a message, never a
    /// digest. If a signature scheme calls for hashing a message
    /// and signing the hash digest, `sign` is responsible for
    /// both hashing and signing.
    fn sign(key: &Self::PrivateKey, message: &[u8])
        -> Result<Self, Error>;

    /// Verify a signature.
    ///
    /// The input to this function is always a message, never a
    /// digest. If a signature scheme calls for hashing a message
    /// and signing the hash digest, `verify` is responsible for
    /// both hashing and verifying the digest.
    fn verify(&self, key: &<Self::PrivateKey as PrivateKey>::Public,
        message: &[u8]) -> bool;
}

Right away, we see an example of a mistake which is impossible with Mundane’s API: It’s impossible to incorrectly combine hashing and signing because they’re not exposed as separate operations. Instead, both on the signing path and the verification path, hashing is handled by the API automatically.

This allows us to unlock more powerful and subtle correctness properties. For example, since we are responsible for hashing, not only can we ensure that hashing is done properly, we can also ensure that a secure hash function is used. Consider, for example, the EcdsaSignature, which implements Signature:

/// A DER-encoded ECDSA signature.
pub struct EcdsaSignature<C: PCurve, H: Hasher + EcdsaHash<C>> { ... }

The ECDSA signature algorithm can operate over any elliptic curve (the C type parameter) and any hash function (the H type parameter). If we look closely, we see that H is not just a Hasher (the trait implemented by hash functions), but is, in addition, an EcdsaHash<C>. This trait is only implemented for valid hash/curve pairs. Some hashes are large enough that, in order to sign them using ECDSA, they would need to be truncated. For example, using a SHA-512 hash with ECDSA over a P-256 curve would require truncating the hash before signing, reducing the security of the hash.

Thus, we only implement EcdsaHash for hash/curve pairs where no truncation is necessary. Hash/curve pairs which require truncation aren’t supported, and code which tries to use them won’t compile.

For more examples, see the documentation. For an in-depth discussion of our design philosophy, see the design doc.

Implementation

Of course, there’s more to cryptography than the API. Mundane is based on the well-vetted and actively-developed BoringSSL. Mundane does not provide any implementations itself, and does not currently have any plans to do so in the future.

A common problem with relying on native libraries from Rust crates is compatibility - while Rust and Cargo are able to compile multiple crate versions into a single binary, C is not so flexible. In order to avoid this problem, Mundane vendors a copy of the BoringSSL source, which is shipped along with the crate on crates.io. It uses symbol prefixing to ensure that the version of BoringSSL compiled for a particular version of Mundane will not conflict with either a) the version of BoringSSL compiled for a different version of Mundane or, b) any version of BoringSSL compiled for any other Rust crate. Because the source is shipped with Mundane, users do not need to have BoringSSL installed when building.

The approach we use to prefix BoringSSL’s symbols is complex, and makes compilations take longer than we’d like. If you’re interested in helping improve the situation, please get in touch!

Why not <other cryptography crate>?

There are a host of fantastic cryptography crates in Rust, each with its own focus and design tradeoffs. Mundane has its focus - to be misuse-resistant at all costs - and it makes design tradeoffs with that focus in mind. If that’s not what you’re looking for, then one of the other cryptography crates might be a better fit for your use case.

Additionally, as the primary Rust cryptography crate used by the Fuchsia OS, Mundane has a very high bar for security and active maintenance. While Rust is our ideal language for almost everything, it still has some shortcomings when it comes to writing cryptography code, such as the lack of guaranteed constant-time operations. Moreover, to be blunt, we don’t trust ourselves to implement cryptography correctly. That’s why we rely exclusively on BoringSSL, which is developed full-time by a team of professional cryptographers, and which provides strong guarantees about fixing vulnerabilities quickly if they arise.

How can I help?

Use Mundane! Use it for high-level applications and low level applications. Use it in all different environments. The more users we have, the more feedback we’ll get, and the better Mundane will be. Tell us what you like, and what you don’t. Tell us what features you want to see in the future. Submit issues.