logo
Expand description

RustCrypto: PEM Encoding (RFC 7468)

crate Docs Build Status Apache2/MIT licensed Rust Version Project Chat

Pure Rust implementation of PEM Encoding (RFC 7468) for PKIX, PKCS, and CMS Structures, a strict subset of the original Privacy-Enhanced Mail encoding intended specifically for use with cryptographic keys, certificates, and other messages.

Provides a no_std-friendly, constant-time implementation suitable for use with cryptographic private keys.

Documentation

About

Many cryptography-related document formats, such as certificates (PKIX), private and public keys/keypairs (PKCS), and other cryptographic messages (CMS) provide an ASCII encoding which can be traced back to Privacy-Enhanced Mail (PEM) as defined RFC 1421, which look like the following:

-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIBftnHPp22SewYmmEoMcX8VwI4IHwaqd+9LFPj/15eqF
-----END PRIVATE KEY-----

However, all of these formats actually implement a text-based encoding that is similar to, but not identical with, the legacy PEM encoding as described in RFC 1421.

For this reason, RFC 7468 was created to describe a stricter form of “PEM encoding” for use in these applications which codifies the previously de facto rules that most implementations operate by, and makes recommendations to promote interoperability.

This crate attempts to implement a strict interpretation of the RFC 7468 rules, implementing all of the MUSTs and SHOULDs while avoiding the MAYs, and targeting the “ABNF (Strict)” subset of the grammar as described in RFC 7468 Section 3 Figure 3 (p6).

Implementation notes

  • Core PEM implementation is no_std-friendly and requires no heap allocations.
  • Avoids use of copies and temporary buffers.
  • Uses the base64ct crate to decode/encode Base64 in constant-time.
  • PEM parser avoids branching on potentially secret data as much as possible. In the happy path, only 1-byte of secret data is potentially branched upon.

The paper Util::Lookup: Exploiting key decoding in cryptographic libraries demonstrates how the leakage from non-constant-time PEM parsers can be used to practically extract RSA private keys from SGX enclaves.

Minimum Supported Rust Version

This crate requires Rust 1.56 at a minimum.

We may change the MSRV in the future, but it will be accompanied by a minor version bump.

License

Licensed under either of:

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Usage

/// Example PEM document
/// NOTE: do not actually put private key literals into your source code!!!
let example_pem = "\
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIBftnHPp22SewYmmEoMcX8VwI4IHwaqd+9LFPj/15eqF
-----END PRIVATE KEY-----
";

// Decode PEM
let (type_label, data) = pem_rfc7468::decode_vec(example_pem.as_bytes())?;
assert_eq!(type_label, "PRIVATE KEY");
assert_eq!(
   data,
   &[
       48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32, 23, 237, 156, 115, 233, 219,
       100, 158, 193, 137, 166, 18, 131, 28, 95, 197, 112, 35, 130, 7, 193, 170, 157, 251,
       210, 197, 62, 63, 245, 229, 234, 133
   ]
);

// Encode PEM
use pem_rfc7468::LineEnding;
let encoded_pem = pem_rfc7468::encode_string(type_label, LineEnding::default(), &data)?;
assert_eq!(&encoded_pem, example_pem);

Structs

Buffered PEM decoder.

Buffered PEM encoder.

Enums

PEM errors.

Line endings: variants of newline characters that can be used with Base64.

Constants

Width at which the Base64 body of RFC7468-compliant PEM is wrapped.

Traits

Marker trait for types with an associated PEM type label.

Functions

Decode a PEM document according to RFC 7468’s “Strict” grammar.

Decode the encapsulation boundaries of a PEM document according to RFC 7468’s “Strict” grammar.

Decode a PEM document according to RFC 7468’s “Strict” grammar, returning the result as a Vec upon success.

Compute the length of a PEM encoded document which encapsulates a Base64-encoded body including line endings every 64 characters.

Compute the length of a PEM encoded document with the Base64 body line wrapped at the specified width.

Encode a PEM document according to RFC 7468’s “Strict” grammar.

Encode a PEM document according to RFC 7468’s “Strict” grammar, returning the result as a String.

Get the length of a PEM encoded document with the given bytes and label.

Type Definitions

Buffered Base64 decoder type.

Buffered Base64 encoder type.

Result type with the pem-rfc7468 crate’s Error type.