ring/src/hmac.rs
Brian Smith 279bd0a2e9 Reformat based on suggestions from rustfmt.
This is a first step toward fully-automated formatting. A custom format
is used, primarily to tell rustfmt to wrap at column 80(-ish) instead of
column 100(-ish), and to use more compact styles.

Many rustfmt suggestions for rewrapping function calls were ignored
because they did not result in the minimum number of lines and/or
because I'm still unsure the best way to format a long chain. Some
suggestions for reformatting macros were ignored because they ruined
the indention. Some other suggestions were ignored because they seemed
like bugs and/or seemed to make things clearly worse.

Further work is planned, in order to make the formatting fully
automatic.
2016-08-28 17:32:32 -10:00

433 lines
16 KiB
Rust

// Copyright 2015-2016 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.
//! HMAC is specified in [RFC 2104].
//!
//! After a `SigningKey` or `VerificationKey` is constructed, it can be used
//! for multiple signing or verification operations. Separating the
//! construction of the key from the rest of the HMAC operation allows the
//! per-key precomputation to be done only once, instead of it being done in
//! every HMAC operation.
//!
//! Frequently all the data to be signed in a message is available in a single
//! contiguous piece. In that case, the module-level `sign` function can be
//! used. Otherwise, if the input is in multiple parts, `SigningContext` should
//! be used.
//!
//! # Use Case: Multi-party Communication
//!
//! Examples: TLS, SSH, and IPSEC record/packet authentication.
//!
//! The key that is used to sign messages to send to other parties should be a
//! `SigningKey`; `SigningContext` or `sign` should be used for the signing.
//! Each key that is used to authenticate messages received from peers should
//! be a `VerificationKey`; `verify` should be used for the authentication. All
//! of the keys should have distinct, independent, values.
//!
//! # Use Case: One-party Anti-tampering Protection
//!
//! Examples: Signed cookies, stateless CSRF protection.
//!
//! The key that is used to sign the data should be a `SigningKey`;
//! `SigningContext` or `sign` should be used for the signing. Use
//! `verify_with_own_key` to verify the signature using the signing key; this
//! is equivalent to, but more efficient than, constructing a `VerificationKey`
//! with the same value as the signing key and then calling `verify`.
//!
//! # Use Case: Key Derivation and Password Hashing
//!
//! Examples: HKDF, PBKDF2, the TLS PRF.
//!
//! All keys used during the key derivation should be `SigningKey`s;
//! `SigningContext` should usually be used for the HMAC calculations. The
//! [code for `ring::pbkdf2`] and the [code for `ring::hkdf`] are good
//! examples of how to use `ring::hmac` efficiently for key derivation.
//!
//!
//! # Examples:
//!
//! ## Signing a value and verifying it wasn't tampered with
//!
//! ```
//! use ring::{digest, hmac, rand};
//!
//! # fn main_with_result() -> Result<(), ring::error::Unspecified> {
//! let rng = rand::SystemRandom::new();
//! let key = try!(hmac::SigningKey::generate(&digest::SHA256, &rng));
//!
//! let msg = "hello, world";
//!
//! let signature = hmac::sign(&key, msg.as_bytes());
//!
//! // [We give access to the message to an untrusted party, and they give it
//! // back to us. We need to verify they didn't tamper with it.]
//!
//! try!(hmac::verify_with_own_key(&key, msg.as_bytes(), signature.as_ref()));
//! #
//! # Ok(())
//! # }
//! #
//! # fn main() { main_with_result().unwrap() }
//! ```
//!
//! ## Using the one-shot API:
//!
//! ```
//! use ring::{digest, hmac, rand};
//!
//! # fn main_with_result() -> Result<(), ring::error::Unspecified> {
//! let msg = "hello, world";
//!
//! // The sender generates a secure key value and signs the message with it.
//! // Note that in a real protocol, a key agreement protocol would be used to
//! // derive `key_value`.
//! let mut key_value = [0u8; 32];
//! let rng = rand::SystemRandom::new();
//! try!(rng.fill(&mut key_value));
//!
//! let s_key = hmac::SigningKey::new(&digest::SHA256, key_value.as_ref());
//! let signature = hmac::sign(&s_key, msg.as_bytes());
//!
//! // The receiver (somehow!) knows the key value, and uses it to verify the
//! // integrity of the message.
//! let v_key = hmac::VerificationKey::new(&digest::SHA256, key_value.as_ref());
//! try!(hmac::verify(&v_key, msg.as_bytes(), signature.as_ref()));
//! #
//! # Ok(())
//! # }
//! #
//! # fn main() { main_with_result().unwrap() }
//! ```
//!
//! ## Using the multi-part API:
//! ```
//! use ring::{digest, hmac, rand};
//!
//! # fn main_with_result() -> Result<(), ring::error::Unspecified> {
//! let parts = ["hello", ", ", "world"];
//!
//! // The sender generates a secure key value and signs the message with it.
//! // Note that in a real protocol, a key agreement protocol would be used to
//! // derive `key_value`.
//! let mut key_value = [0u8; 48];
//! let rng = rand::SystemRandom::new();
//! try!(rng.fill(&mut key_value));
//!
//! let s_key = hmac::SigningKey::new(&digest::SHA384, key_value.as_ref());
//! let mut s_ctx = hmac::SigningContext::with_key(&s_key);
//! for part in &parts {
//! s_ctx.update(part.as_bytes());
//! }
//! let signature = s_ctx.sign();
//!
//! // The receiver (somehow!) knows the key value, and uses it to verify the
//! // integrity of the message.
//! let v_key = hmac::VerificationKey::new(&digest::SHA384, key_value.as_ref());
//! let mut msg = Vec::<u8>::new();
//! for part in &parts {
//! msg.extend(part.as_bytes());
//! }
//! try!(hmac::verify(&v_key, &msg.as_ref(), signature.as_ref()));
//! #
//! # Ok(())
//! # }
//! #
//! # fn main() { main_with_result().unwrap() }
//! ```
//!
//! [RFC 2104]: https://tools.ietf.org/html/rfc2104
//! [code for `ring::pbkdf2`]:
//! https://github.com/briansmith/ring/blob/master/src/pbkdf2.rs
//! [code for `ring::hkdf`]:
//! https://github.com/briansmith/ring/blob/master/src/hkdf.rs
use {constant_time, digest, error, rand};
/// A key to use for HMAC signing.
pub struct SigningKey {
ctx_prototype: SigningContext,
}
impl SigningKey {
/// Generate an HMAC signing key for the given digest algorithm using
/// |ring::rand|. The key will be `digest_alg.chaining_len` bytes long. The
/// key size choice is based on the recommendation of
/// [NIST SP 800-107], Section 5.3.4: Security Effect of the HMAC
/// Key, and is consistent with the key lengths chosen for TLS as
/// described in [RFC 5246, Appendix C].
///
/// [NIST SP 800-107]:
/// http://csrc.nist.gov/publications/nistpubs/800-107-rev1/sp800-107-rev1.pdf
/// [RFC 5246, Appendix C]:
/// https://tools.ietf.org/html/rfc5246#appendix-C
pub fn generate(digest_alg: &'static digest::Algorithm,
rng: &rand::SecureRandom)
-> Result<SigningKey, error::Unspecified> {
// XXX: There should probably be a `digest::MAX_CHAINING_LEN`, but for
// now `digest::MAX_OUTPUT_LEN` is good enough.
let mut key_data = [0u8; digest::MAX_OUTPUT_LEN];
let key_data = &mut key_data[..digest_alg.output_len];
try!(rng.fill(key_data));
Ok(SigningKey::new(digest_alg, key_data))
}
/// Construct an HMAC signing key using the given digest algorithm and key
/// value.
///
/// As specified in RFC 2104, if `key_value` is shorter than the digest
/// algorithm's block length (as returned by `digest::Algorithm::block_len`,
/// not the digest length returned by `digest::Algorithm::output_len`) then
/// it will be padded with zeros. Similarly, if it is longer than the block
/// length then it will be compressed using the digest algorithm.
///
/// You should not use keys larger than the `digest_alg.block_len` because
/// the truncation described above reduces their strength to only
/// `digest_alg.output_len * 8` bits. Support for such keys is likely to be
/// removed in a future version of *ring*.
pub fn new(digest_alg: &'static digest::Algorithm, key_value: &[u8])
-> SigningKey {
let mut key = SigningKey {
ctx_prototype: SigningContext {
inner: digest::Context::new(digest_alg),
outer: digest::Context::new(digest_alg),
},
};
let key_hash;
let key_value = if key_value.len() <= digest_alg.block_len {
key_value
} else {
key_hash = digest::digest(digest_alg, key_value);
key_hash.as_ref()
};
const IPAD: u8 = 0x36;
const OPAD: u8 = 0x5C;
for b in key_value {
key.ctx_prototype.inner.update(&[IPAD ^ b]);
key.ctx_prototype.outer.update(&[OPAD ^ b]);
}
// If the key is shorter than one block then act as though the key is
// padded with zeros.
for _ in key_value.len()..digest_alg.block_len {
key.ctx_prototype.inner.update(&[IPAD]);
key.ctx_prototype.outer.update(&[OPAD]);
}
key
}
/// The digest algorithm for the key.
pub fn digest_algorithm(&self) -> &'static digest::Algorithm {
self.ctx_prototype.inner.algorithm()
}
}
/// A context for multi-step (Init-Update-Finish) HMAC signing.
///
/// Use `sign` for single-step HMAC signing.
///
/// C analog: `HMAC_CTX`.
pub struct SigningContext {
inner: digest::Context,
outer: digest::Context,
}
impl SigningContext {
/// Constructs a new HMAC signing context using the given digest algorithm
/// and key.
///
/// C analog: `HMAC_CTX_init`
pub fn with_key(signing_key: &SigningKey) -> SigningContext {
SigningContext {
inner: signing_key.ctx_prototype.inner.clone(),
outer: signing_key.ctx_prototype.outer.clone(),
}
}
/// Updates the HMAC with all the data in `data`. `update` may be called
/// zero or more times until `finish` is called.
///
/// C analog: `HMAC_Update`
pub fn update(&mut self, data: &[u8]) { self.inner.update(data); }
/// Finalizes the HMAC calculation and returns the HMAC value. `sign`
/// consumes the context so it cannot be (mis-)used after `sign` has been
/// called.
///
/// It is generally not safe to implement HMAC verification by comparing
// the return value of `sign` to a signature. Use `verify` for verification
// instead.
///
/// C analog: `HMAC_Final`
pub fn sign(mut self) -> digest::Digest {
self.outer.update(self.inner.finish().as_ref());
self.outer.finish()
}
}
/// Calculates the HMAC of `data` using the key `key` in one step.
///
/// Use `SigningContext` to calculate HMACs where the input is in multiple
/// parts.
///
/// It is generally not safe to implement HMAC verification by comparing the
/// return value of `sign` to a signature. Use `verify` for verification
/// instead.
///
/// C analog: `HMAC_CTX_init` + `HMAC_Update` + `HMAC_Final`.
pub fn sign(key: &SigningKey, data: &[u8]) -> digest::Digest {
let mut ctx = SigningContext::with_key(key);
ctx.update(data);
ctx.sign()
}
/// A key to use for HMAC authentication.
pub struct VerificationKey {
wrapped: SigningKey,
}
impl VerificationKey {
/// Construct an HMAC verification key using the given digest algorithm and
/// key value.
///
/// As specified in RFC 2104, if `key_value` is shorter than the digest
/// algorithm's block length (as returned by `digest::Algorithm::block_len`,
/// not the digest length returned by `digest::Algorithm::output_len`) then
/// it will be padded with zeros. Similarly, if it is longer than the block
/// length then it will be compressed using the digest algorithm.
#[inline(always)]
pub fn new(digest_alg: &'static digest::Algorithm, key_value: &[u8])
-> VerificationKey {
VerificationKey { wrapped: SigningKey::new(digest_alg, key_value) }
}
}
/// Calculates the HMAC of `data` using the key `key`, and verifies whether the
/// resultant value equals `signature`, in one step.
///
/// The verification will be done in constant time to prevent timing attacks.
///
/// C analog: `HMAC_Init` + `HMAC_Update` + `HMAC_Final` + `CRYPTO_memcmp`
#[inline(always)]
pub fn verify(key: &VerificationKey, data: &[u8], signature: &[u8])
-> Result<(), error::Unspecified> {
verify_with_own_key(&key.wrapped, data, signature)
}
/// Calculates the HMAC of `data` using the signing key `key`, and verifies
/// whether the resultant value equals `signature`, in one step.
///
/// This is logically equivalent to, but more efficient than, constructing a
/// `VerificationKey` with the same value as `key` and then using `verify`.
///
/// The verification will be done in constant time to prevent timing attacks.
///
/// C analog: `HMAC_Init` + `HMAC_Update` + `HMAC_Final` + `CRYPTO_memcmp`
pub fn verify_with_own_key(key: &SigningKey, data: &[u8], signature: &[u8])
-> Result<(), error::Unspecified> {
constant_time::verify_slices_are_equal(sign(key, data).as_ref(), signature)
}
#[cfg(test)]
mod tests {
use {digest, error, hmac, rand, test};
// Make sure that `SigningKey::generate` and `verify_with_own_key` aren't
// completely wacky.
#[test]
pub fn hmac_signing_key_coverage() {
let mut rng = rand::SystemRandom::new();
const HELLO_WORLD_GOOD: &'static [u8] = b"hello, world";
const HELLO_WORLD_BAD: &'static [u8] = b"hello, worle";
for d in &digest::test_util::ALL_ALGORITHMS {
let key = hmac::SigningKey::generate(d, &mut rng).unwrap();
let signature = hmac::sign(&key, HELLO_WORLD_GOOD);
assert!(hmac::verify_with_own_key(&key, HELLO_WORLD_GOOD,
signature.as_ref()).is_ok());
assert!(hmac::verify_with_own_key(&key, HELLO_WORLD_BAD,
signature.as_ref()).is_err())
}
}
#[test]
pub fn hmac_tests() {
test::from_file("src/hmac_tests.txt", |section, test_case| {
assert_eq!(section, "");
let digest_alg = test_case.consume_digest_alg("HMAC");
let key_value = test_case.consume_bytes("Key");
let mut input = test_case.consume_bytes("Input");
let output = test_case.consume_bytes("Output");
let digest_alg = match digest_alg {
Some(digest_alg) => digest_alg,
None => { return Ok(()); }, // Unsupported digest algorithm
};
try!(hmac_test_case_inner(digest_alg, &key_value[..], &input[..],
&output[..], true));
// Tamper with the input and check that verification fails.
if input.len() == 0 {
input.push(0);
} else {
input[0] ^= 1;
}
hmac_test_case_inner(digest_alg, &key_value[..], &input[..],
&output[..], false)
});
}
fn hmac_test_case_inner(digest_alg: &'static digest::Algorithm,
key_value: &[u8], input: &[u8], output: &[u8],
is_ok: bool) -> Result<(), error::Unspecified> {
let s_key = hmac::SigningKey::new(digest_alg, key_value);
let v_key = hmac::VerificationKey::new(digest_alg, key_value);
// One-shot API.
{
let signature = hmac::sign(&s_key, input);
assert_eq!(is_ok, signature.as_ref() == output);
assert_eq!(is_ok, hmac::verify(&v_key, input, output).is_ok());
}
// Multi-part API, one single part.
{
let mut s_ctx = hmac::SigningContext::with_key(&s_key);
s_ctx.update(input);
let signature = s_ctx.sign();
assert_eq!(is_ok, signature.as_ref() == output);
}
// Multi-part API, byte by byte.
{
let mut s_ctx = hmac::SigningContext::with_key(&s_key);
for b in input {
s_ctx.update(&[*b]);
}
let signature = s_ctx.sign();
assert_eq!(is_ok, signature.as_ref() == output);
}
Ok(())
}
}