diff --git a/README.md b/README.md index 95d35021b..ca705c7c1 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,7 @@ The first part of the ```ring``` Rust crate is now available. Currently these features are supported through the Rust API: +* AEAD (authenticated encryption) using AES-GCM (more algorithms to follow) * Cryptographic digests (SHA-256, SHA-384, SHA-512, SHA-1, and MD5) * HMAC, HKDF, and PBKDF2-HMAC * Ephemeral ECDH key agreement for curves P-256, P-384, and P-521 diff --git a/crypto/cipher/e_aes.c b/crypto/cipher/e_aes.c index b15b7dbd9..efdabafff 100644 --- a/crypto/cipher/e_aes.c +++ b/crypto/cipher/e_aes.c @@ -899,8 +899,8 @@ struct aead_aes_gcm_ctx { uint8_t tag_len; }; -static int aead_aes_gcm_init(EVP_AEAD_CTX *ctx, const uint8_t *key, - size_t key_len, size_t tag_len) { +int evp_aead_aes_gcm_init(EVP_AEAD_CTX *ctx, const uint8_t *key, + size_t key_len, size_t tag_len) { struct aead_aes_gcm_ctx *gcm_ctx; const size_t key_bits = key_len * 8; @@ -931,17 +931,17 @@ static int aead_aes_gcm_init(EVP_AEAD_CTX *ctx, const uint8_t *key, return 1; } -static void aead_aes_gcm_cleanup(EVP_AEAD_CTX *ctx) { +void evp_aead_aes_gcm_cleanup(EVP_AEAD_CTX *ctx) { struct aead_aes_gcm_ctx *gcm_ctx = ctx->aead_state; OPENSSL_cleanse(gcm_ctx, sizeof(struct aead_aes_gcm_ctx)); OPENSSL_free(gcm_ctx); } -static int aead_aes_gcm_seal(const EVP_AEAD_CTX *ctx, uint8_t *out, - size_t *out_len, size_t max_out_len, - const uint8_t *nonce, size_t nonce_len, - const uint8_t *in, size_t in_len, - const uint8_t *ad, size_t ad_len) { +int evp_aead_aes_gcm_seal(const EVP_AEAD_CTX *ctx, uint8_t *out, + size_t *out_len, size_t max_out_len, + const uint8_t *nonce, size_t nonce_len, + const uint8_t *in, size_t in_len, + const uint8_t *ad, size_t ad_len) { size_t bulk = 0; const struct aead_aes_gcm_ctx *gcm_ctx = ctx->aead_state; GCM128_CONTEXT gcm; @@ -979,11 +979,11 @@ static int aead_aes_gcm_seal(const EVP_AEAD_CTX *ctx, uint8_t *out, return 1; } -static int aead_aes_gcm_open(const EVP_AEAD_CTX *ctx, uint8_t *out, - size_t *out_len, size_t max_out_len, - const uint8_t *nonce, size_t nonce_len, - const uint8_t *in, size_t in_len, - const uint8_t *ad, size_t ad_len) { +int evp_aead_aes_gcm_open(const EVP_AEAD_CTX *ctx, uint8_t *out, + size_t *out_len, size_t max_out_len, + const uint8_t *nonce, size_t nonce_len, + const uint8_t *in, size_t in_len, + const uint8_t *ad, size_t ad_len) { size_t bulk = 0; const struct aead_aes_gcm_ctx *gcm_ctx = ctx->aead_state; uint8_t tag[EVP_AEAD_AES_GCM_TAG_LEN]; @@ -1032,28 +1032,30 @@ static int aead_aes_gcm_open(const EVP_AEAD_CTX *ctx, uint8_t *out, return 1; } +/* TODO(ring): We currently duplicate these between Rust and C. Avoid doing that. */ static const EVP_AEAD aead_aes_128_gcm = { 16, /* key len */ 12, /* nonce len */ EVP_AEAD_AES_GCM_TAG_LEN, /* overhead */ EVP_AEAD_AES_GCM_TAG_LEN, /* max tag length */ - aead_aes_gcm_init, + evp_aead_aes_gcm_init, NULL, /* init_with_direction */ - aead_aes_gcm_cleanup, - aead_aes_gcm_seal, - aead_aes_gcm_open, + evp_aead_aes_gcm_cleanup, + evp_aead_aes_gcm_seal, + evp_aead_aes_gcm_open, }; +/* TODO(ring): We currently duplicate these between Rust and C. Avoid doing that. */ static const EVP_AEAD aead_aes_256_gcm = { 32, /* key len */ 12, /* nonce len */ EVP_AEAD_AES_GCM_TAG_LEN, /* overhead */ EVP_AEAD_AES_GCM_TAG_LEN, /* max tag length */ - aead_aes_gcm_init, + evp_aead_aes_gcm_init, NULL, /* init_with_direction */ - aead_aes_gcm_cleanup, - aead_aes_gcm_seal, - aead_aes_gcm_open, + evp_aead_aes_gcm_cleanup, + evp_aead_aes_gcm_seal, + evp_aead_aes_gcm_open, }; const EVP_AEAD *EVP_aead_aes_128_gcm(void) { return &aead_aes_128_gcm; } diff --git a/crypto/cipher/internal.h b/crypto/cipher/internal.h index ca960148e..27abc9c78 100644 --- a/crypto/cipher/internal.h +++ b/crypto/cipher/internal.h @@ -72,6 +72,8 @@ extern "C" { /* EVP_AEAD represents a specific AEAD algorithm. */ struct evp_aead_st { + /* ring: Keep the layout of this in sync with the layout of + * |ring::aead::Algorithm|. */ uint8_t key_len; uint8_t nonce_len; uint8_t overhead; diff --git a/src/aead.rs b/src/aead.rs new file mode 100644 index 000000000..f7f748f8c --- /dev/null +++ b/src/aead.rs @@ -0,0 +1,397 @@ +// Copyright 2015 Brian Smith. +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +//! Authenticated Encryption with Associated Data (AEAD). +//! +//! See [Authenticated encryption: relations among notions and analysis of the +//! generic composition +//! paradigm](http://www-cse.ucsd.edu/~mihir/papers/oem.html) for an +//! introduction to the concept of AEADs. +//! +//! C analog: `openssl/aead.h` +//! +//! Go analog: [`crypto.cipher.AEAD`](https://golang.org/pkg/crypto/cipher/#AEAD) + +use libc; +use std; +use super::ffi; + +/// A key for authenticating and decrypting (“opening”) +/// AEAD-protected data. +/// +/// C analog: `EVP_AEAD_CTX` with direction `evp_aead_open` +/// +/// Go analog: [`crypto.cipher.AEAD`](https://golang.org/pkg/crypto/cipher/#AEAD) +pub struct OpeningKey { + key: Key, +} + +impl OpeningKey { + /// C analogs: `EVP_AEAD_CTX_init_with_direction` with direction + /// `evp_aead_open`, `EVP_AEAD_CTX_init`. + /// + /// Go analog: [`crypto.aes.NewCipher`](https://golang.org/pkg/crypto/aes/#NewCipher) + /// + [`crypto.cipher.NewGCM`](https://golang.org/pkg/crypto/cipher/#NewGCM) + #[inline] + pub fn new(algorithm: &'static Algorithm, key_bytes: &[u8]) + -> Result { + let mut key = OpeningKey { + key: Key { + ctx: EVP_AEAD_CTX { + aead: algorithm, + aead_state: std::ptr::null_mut() + } + } + }; + try!(key.key.init(Direction::Open, key_bytes)); + Ok(key) + } + + /// The key's AEAD algorithm. + /// + /// C analog: `EVP_AEAD_CTX.aead` + #[inline(always)] + pub fn algorithm(&self) -> &'static Algorithm { self.key.algorithm() } +} + +/// Authenticates and decrypts (“opens”) data in place. +/// +/// The input is `in_out[in_prefix_len..]`; i.e. the input is the part of +/// `in_out` after the prefix. When `open` returns `Ok(out_len)`, the decrypted +/// output is `in_out[0..out_len]`; i.e. the output has been written over the +/// top of the prefix and the input. To put it a different way, the output +/// overwrites the input, shifted by `in_prefix_len` bytes. To have the output +/// overwrite the input without shifting, pass 0 as `in_prefix_len`. (The input +/// and output buffers are expressed this way because Rust's type system does +/// not allow us to have two slices, one mutable and one immutable, that +/// reference overlapping memory.) +/// +/// C analog: `EVP_AEAD_CTX_open` +/// +/// Go analog: [`AEAD.Open`](https://golang.org/pkg/crypto/cipher/#AEAD) +pub fn open_in_place(key: &OpeningKey, nonce: &[u8], in_prefix_len: usize, + in_out: &mut [u8], ad: &[u8]) -> Result { + if in_out.len() < in_prefix_len { + return Err(()); + } + unsafe { + key.key.open_or_seal_in_place(EVP_AEAD_CTX_open, nonce, + in_out[in_prefix_len..].as_ptr(), + in_out.len() - in_prefix_len, ad, in_out) + } +} + +/// A key for encrypting and signing (“sealing”) data. +/// +/// C analog: `EVP_AEAD_CTX` with direction `evp_aead_seal`. +/// +/// Go analog: [`AEAD`](https://golang.org/pkg/crypto/cipher/#AEAD) +pub struct SealingKey { + key: Key, +} + +impl SealingKey { + /// C analogs: `EVP_AEAD_CTX_init_with_direction` with direction + /// `evp_aead_seal`, `EVP_AEAD_CTX_init`. + /// + /// Go analog: [`crypto.aes.NewCipher`](https://golang.org/pkg/crypto/aes/#NewCipher) + /// + [`crypto.cipher.NewGCM`](https://golang.org/pkg/crypto/cipher/#NewGCM) + #[inline] + pub fn new(algorithm: &'static Algorithm, key_bytes: &[u8]) + -> Result { + let mut key = SealingKey { + key: Key { + ctx: EVP_AEAD_CTX { + aead: algorithm, + aead_state: std::ptr::null_mut() + } + } + }; + try!(key.key.init(Direction::Seal, key_bytes)); + Ok(key) + } + + /// The key's AEAD algorithm. + /// + /// C analog: `EVP_AEAD_CTX.aead` + #[inline(always)] + pub fn algorithm(&self) -> &'static Algorithm { self.key.algorithm() } +} + +/// Encrypts and signs (“seals”) data in place. +/// +/// `nonce` must be unique for every use of the key to seal data. +/// +/// The input is `in_out[0..(in_out.len() - out_suffix_capacity]`; i.e. the +/// input is the part of `in_out` that precedes the suffix. When `seal` returns +/// `Ok(out_len)`, the encrypted and signed output is `in_out[0..out_len]`; i.e. +/// the output has been written over input and at least part of the data +/// reserved for the suffix. (This way the input and output buffers are +/// expressed this way because Rust's type system does not allow us to have two +/// slices, one mutable and one immutable, that reference overlapping memory.) +/// +/// `out_suffix_capacity` should be at least `key.algorithm.max_overhead_len`. +/// See also `MAX_OVERHEAD_LEN`. +/// +/// `ad` is the additional authenticated data, if any. +/// +/// C analog: `EVP_AEAD_CTX_seal`. +/// +/// Go analog: [`AEAD.Seal`](https://golang.org/pkg/crypto/cipher/#AEAD) +pub fn seal_in_place(key: &SealingKey, nonce: &[u8], in_out: &mut [u8], + out_suffix_capacity: usize, ad: &[u8]) + -> Result { + if in_out.len() < out_suffix_capacity { + return Err(()); + } + unsafe { + key.key.open_or_seal_in_place(EVP_AEAD_CTX_seal, nonce, in_out.as_ptr(), + in_out.len() - out_suffix_capacity, ad, + in_out) + } +} + +/// `OpeningKey` and `SealingKey` are type-safety wrappers around `Key`, which +/// does all the actual work via the C AEAD interface. +/// +/// C analog: `EVP_AEAD_CTX` +struct Key { + ctx: EVP_AEAD_CTX +} + +impl Key { + /// XXX: Assumes self.ctx.aead is already filled in. + /// + /// C analogs: `EVP_AEAD_CTX_init` or `EVP_AEAD_CTX_init_with_direction` + fn init(&mut self, direction: Direction, key_bytes: &[u8]) -> Result<(), ()> { + ffi::map_bssl_result(unsafe { + EVP_AEAD_CTX_init_with_direction(&mut self.ctx, self.ctx.aead, + key_bytes.as_ptr(), + key_bytes.len() as libc::size_t, + 0, // EVP_AEAD_DEFAULT_TAG_LENGTH + direction) + }) + } + + /// The key's AEAD algorithm. + #[inline(always)] + fn algorithm(&self) -> &'static Algorithm { self.ctx.aead } + + unsafe fn open_or_seal_in_place(&self, open_or_seal_fn: OpenOrSealFn, + nonce: &[u8], in_ptr: *const u8, + in_len: usize, ad: &[u8], out: &mut [u8]) + -> Result { + let mut out_len: libc::size_t = 0; + match (open_or_seal_fn)(&self.ctx, out.as_mut_ptr(), &mut out_len, + out.len() as libc::size_t, nonce.as_ptr(), + nonce.len() as libc::size_t, in_ptr, + in_len as libc::size_t, ad.as_ptr(), + ad.len() as libc::size_t) { + 1 => Ok(out_len as usize), + _ => Err(()) + } + } + +} + +impl Drop for Key { + #[inline(always)] + fn drop(&mut self) { + unsafe { + EVP_AEAD_CTX_cleanup(&mut self.ctx); + } + } +} + +#[repr(C)] +struct EVP_AEAD_CTX { + aead: &'static Algorithm, + aead_state: *mut libc::c_void +} + +/// C analog: `evp_aead_direction_t` +#[repr(C)] +enum Direction { + /// C analog: `evp_aead_open` + Open, + + /// C analog: `evp_aead_seal` + Seal, +} + +/// An AEAD Algorithm. +/// +/// C analog: `EVP_AEAD` +/// +/// Go analog: [`crypto.cipher.AEAD`](https://golang.org/pkg/crypto/cipher/#AEAD) +#[repr(C)] +pub struct Algorithm { + // Keep the layout of this in sync with the layout of `EVP_AEAD`. + + /// The length of the key. + /// + /// C analog: `EVP_AEAD_key_length` + pub key_len: libc::uint8_t, + + /// The length of the nonces. + /// + /// C analog: `EVP_AEAD_nonce_length` + /// + /// Go analog: [`crypto.cipher.AEAD.NonceSize`](https://golang.org/pkg/crypto/cipher/#AEAD) + pub nonce_len: libc::uint8_t, + + /// The maximum number of bytes that sealing operations may add to plaintexts. + /// See also `MAX_OVERHEAD_LEN`. + /// + /// C analog: `EVP_AEAD_max_overhead` + /// + /// Go analog: [`crypto.cipher.AEAD.Overhead`](https://golang.org/pkg/crypto/cipher/#AEAD) + pub max_overhead_len: libc::uint8_t, + + /// The length of the authentication tags or MACs. + /// + /// Use `max_overhead_len` or `MAX_OVERHEAD_LEN` when sizing buffers for + /// sealing operations. + /// + /// C analog: `EVP_AEAD_max_tag_len` + pub tag_len: libc::uint8_t, + + init: Option libc::c_int>, + + init_with_direction: Option libc::c_int>, + + cleanup: unsafe extern fn(ctx: *mut EVP_AEAD_CTX), + + seal: unsafe extern fn(ctx: *mut EVP_AEAD_CTX, out: *mut libc::uint8_t, + out_len: libc::size_t, max_out_len: libc::size_t, + nonce: *const libc::uint8_t, nonce_len: libc::size_t, + in_: *const libc::uint8_t, in_len: libc::size_t, + ad: *const libc::uint8_t, ad_len: libc::size_t) + -> libc::c_int, + + open: unsafe extern fn(ctx: *mut EVP_AEAD_CTX, out: *mut libc::uint8_t, + out_len: libc::size_t, max_out_len: libc::size_t, + nonce: *const libc::uint8_t, nonce_len: libc::size_t, + in_: *const libc::uint8_t, in_len: libc::size_t, + ad: *const libc::uint8_t, ad_len: libc::size_t) + -> libc::c_int, +} + +const AES_128_KEY_LEN: libc::uint8_t = 128 / 8; +const AES_256_KEY_LEN: libc::uint8_t = (256 as usize / 8) as libc::uint8_t; +const AES_GCM_NONCE_LEN: libc::uint8_t = 96 / 8; +const AES_GCM_TAG_LEN: libc::uint8_t = 128 / 8; + +/// The maximum value of `Algorithm.max_overhead_len` for the algorithms in +/// this module. +pub const MAX_OVERHEAD_LEN: usize = AES_GCM_TAG_LEN as usize; + +/// AES-128 in GCM mode with 128-bit tags and 96 bit nonces. +/// +/// C analog: `EVP_aead_aes_128_gcm` +/// +/// Go analog: [`crypto.aes`](https://golang.org/pkg/crypto/aes/) +pub static AES_128_GCM: Algorithm = Algorithm { + key_len: AES_128_KEY_LEN, + nonce_len: AES_GCM_NONCE_LEN, + max_overhead_len: AES_GCM_TAG_LEN, + tag_len: AES_GCM_TAG_LEN, + init: Some(evp_aead_aes_gcm_init), + init_with_direction: None, + cleanup: evp_aead_aes_gcm_cleanup, + seal: evp_aead_aes_gcm_seal, + open: evp_aead_aes_gcm_open, +}; + +/// AES-256 in GCM mode with 128-bit tags and 96 bit nonces. +/// +/// C analog: `EVP_aead_aes_256_gcm` +/// +/// Go analog: [`crypto.aes`](https://golang.org/pkg/crypto/aes/) +pub static AES_256_GCM: Algorithm = Algorithm { + key_len: AES_256_KEY_LEN, + nonce_len: AES_GCM_NONCE_LEN, + max_overhead_len: AES_GCM_TAG_LEN, + tag_len: AES_GCM_TAG_LEN, + init: Some(evp_aead_aes_gcm_init), + init_with_direction: None, + cleanup: evp_aead_aes_gcm_cleanup, + seal: evp_aead_aes_gcm_seal, + open: evp_aead_aes_gcm_open, +}; + +type OpenOrSealFn = + unsafe extern fn(ctx: &EVP_AEAD_CTX, out: *mut libc::uint8_t, + out_len: &mut libc::size_t, max_out_len: libc::size_t, + nonce: *const libc::uint8_t, nonce_len: libc::size_t, + in_: *const libc::uint8_t, in_len: libc::size_t, + ad: *const libc::uint8_t, ad_len: libc::size_t) + -> libc::c_int; + + +extern { + // TODO: C analog documentation + + fn evp_aead_aes_gcm_init(ctx: *mut EVP_AEAD_CTX, key: *const libc::uint8_t, + key_len: libc::size_t, tag_len: libc::size_t) + -> libc::c_int; + + fn evp_aead_aes_gcm_cleanup(ctx: *mut EVP_AEAD_CTX); + + fn evp_aead_aes_gcm_seal(ctx: *mut EVP_AEAD_CTX, out: *mut libc::uint8_t, + out_len: libc::size_t, max_out_len: libc::size_t, + nonce: *const libc::uint8_t, + nonce_len: libc::size_t, + in_: *const libc::uint8_t, in_len: libc::size_t, + ad: *const libc::uint8_t, ad_len: libc::size_t) + -> libc::c_int; + + fn evp_aead_aes_gcm_open(ctx: *mut EVP_AEAD_CTX, out: *mut libc::uint8_t, + out_len: libc::size_t, max_out_len: libc::size_t, + nonce: *const libc::uint8_t, + nonce_len: libc::size_t, + in_: *const libc::uint8_t, in_len: libc::size_t, + ad: *const libc::uint8_t, ad_len: libc::size_t) + -> libc::c_int; + + fn EVP_AEAD_CTX_init_with_direction(ctx: &mut EVP_AEAD_CTX, + aead: &Algorithm, key: *const u8, + key_len: libc::size_t, + tag_len: libc::size_t, + direction: Direction) -> libc::c_int; + + fn EVP_AEAD_CTX_seal(ctx: &EVP_AEAD_CTX, out: *mut libc::uint8_t, + out_len: &mut libc::size_t, max_out_len: libc::size_t, + nonce: *const libc::uint8_t, nonce_len: libc::size_t, + in_: *const libc::uint8_t, in_len: libc::size_t, + ad: *const libc::uint8_t, ad_len: libc::size_t) + -> libc::c_int; + + fn EVP_AEAD_CTX_open(ctx: &EVP_AEAD_CTX, out: *mut libc::uint8_t, + out_len: &mut libc::size_t, max_out_len: libc::size_t, + nonce: *const libc::uint8_t, nonce_len: libc::size_t, + in_: *const libc::uint8_t, in_len: libc::size_t, + ad: *const libc::uint8_t, ad_len: libc::size_t) + -> libc::c_int; + + fn EVP_AEAD_CTX_cleanup(ctx: &mut EVP_AEAD_CTX); +} diff --git a/src/lib.rs b/src/lib.rs index e1683f2c4..844fc8f37 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,6 +17,7 @@ extern crate libc; #[cfg(test)] extern crate rustc_serialize; +pub mod aead; pub mod digest; pub mod ecc; mod ffi;