// 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 = 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.]
//!
//! 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};
//! use ring::rand::SecureRandom;
//!
//! # 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();
//! 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());
//! 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};
//! use ring::rand::SecureRandom;
//!
//! # 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();
//! 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());
//! }
//! 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};

/// An HMAC signature.
///
/// For a given signature `s`, use `s.as_ref()` to get the signature value as
/// a byte slice.
#[derive(Clone, Copy, Debug)]
pub struct Signature(digest::Digest);

/// A key to use for HMAC signing.
pub struct SigningKey {
    ctx_prototype: SigningContext,
}

impl AsRef<[u8]> for Signature {
    #[inline] fn as_ref(&self) -> &[u8] { self.0.as_ref() }
}

impl SigningKey {
    /// Generate an HMAC signing key using the given digest algorithm with a
    /// random value generated from `rng`.
    ///
    /// The key will be `recommended_key_len(digest_alg)` bytes long.
    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_bytes = [0u8; digest::MAX_OUTPUT_LEN];
        let key_bytes = &mut key_bytes[..recommended_key_len(digest_alg)];
        Self::generate_serializable(digest_alg, rng, key_bytes)
    }

    /// Generate an HMAC signing key using the given digest algorithm with a
    /// random value generated from `rng`, and puts the raw key value in
    /// `key_bytes`.
    ///
    /// The key will be `recommended_key_len(digest_alg)` bytes long. The raw
    /// value of the random key is put in `key_bytes` so that it can be
    /// serialized for later use, so `key_bytes` must be exactly
    /// `recommended_key_len(digest_alg)`. This serialized value can be
    /// deserialized with `SigningKey::new()`.
    pub fn generate_serializable(digest_alg: &'static digest::Algorithm,
                                 rng: &rand::SecureRandom, key_bytes: &mut [u8])
                    -> Result<SigningKey, error::Unspecified> {
        if key_bytes.len() != recommended_key_len(digest_alg) {
            return Err(error::Unspecified);
        }
        rng.fill(key_bytes)?;
        Ok(SigningKey::new(digest_alg, key_bytes))
    }

    /// Construct an HMAC signing key using the given digest algorithm and key
    /// value.
    ///
    /// `key_value` should be a value generated using a secure random number
    /// generator (e.g. the `key_value` output by
    /// `SealingKey::generate_serializable()`) or derived from a random key by
    /// a key derivation function (e.g. `ring::hkdf`). In particular,
    /// `key_value` shouldn't be a password.
    ///
    /// 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`.
#[derive(Clone)]
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) -> Signature {
        self.outer.update(self.inner.finish().as_ref());
        Signature(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]) -> Signature {
    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)
}

/// Returns the recommended key length for HMAC using the given digest
/// algorithm.
///
/// The value returned is the chaining length of the digest function,
/// `digest_alg.chaining_len`. This is 32 bytes (256 bits) for SHA-256, and
/// 64 bytes (512 bits) for SHA-384 and SHA-512.
///
/// This recommendation is based on [NIST SP 800-107], Section 5.3.4: Security
/// Effect of the HMAC Key. The chaining length of the digest algorithm,
/// instead of its block length, is used to be consistent with the key lengths
/// chosen for TLS for SHA-256 (see [RFC 5246, Appendix C]) and most other
/// protocols.
///
/// [NIST SP 800-107]:
///     http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-107r1.pdf
/// [RFC 5246, Appendix C]:
///     https://tools.ietf.org/html/rfc5246#appendix-C
#[inline]
pub fn recommended_key_len(digest_alg: &digest::Algorithm) -> usize {
    digest_alg.chaining_len
}


#[cfg(test)]
mod tests {
    use {digest, 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())
            }

            {
                let mut key_bytes = vec![0; d.chaining_len];
                let key =
                    hmac::SigningKey::generate_serializable(d, &mut rng,
                                                            &mut key_bytes)
                            .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())
            }

            // Attempt with a `key_bytes` parameter that wrongly uses the
            // output length instead of the chaining length, when those two
            // values differ.
            if d.chaining_len != d.output_len {
                let mut key_bytes = vec![0; d.output_len];
                assert!(hmac::SigningKey::generate_serializable(d, &mut rng,
                                                                &mut key_bytes)
                            .is_err());
            }

            // Attempt with a too-small `key_bytes`.
            {
                let mut key_bytes = vec![0; d.chaining_len - 1];
                assert!(hmac::SigningKey::generate_serializable(d, &mut rng,
                                                                &mut key_bytes)
                            .is_err());
            }

            // Attempt with a too-large `key_bytes`.
            {
                let mut key_bytes = vec![0; d.chaining_len + 1];
                assert!(hmac::SigningKey::generate_serializable(d, &mut rng,
                                                                &mut key_bytes)
                            .is_err());
            }
        }
    }

    // Test that `generate_serializable()` generates a key from the RNG, and
    // that the generated key fills the entire `key_bytes` parameter.
    #[test]
    pub fn generate_serializable_tests() {
        test::from_file("src/hmac_generate_serializable_tests.txt",
                        |section, test_case| {
            assert_eq!(section, "");
            let digest_alg = test_case.consume_digest_alg("HMAC").unwrap();
            let key_value_in = test_case.consume_bytes("Key");

            let rng = test::rand::FixedSliceRandom { bytes: &key_value_in };
            let mut key_value_out = vec![0; digest_alg.chaining_len];
            let _ = hmac::SigningKey::generate_serializable(
                    digest_alg, &rng, &mut key_value_out).unwrap();
            assert_eq!(&key_value_in, &key_value_out);

            Ok(())
        })
    }
}
