blob: 7ccd578560fba74999b05b665d529cabe581ecc5 [file] [log] [blame]
//! Generic implementation of Hash-based Message Authentication Code (HMAC).
//!
//! To use it you'll need a cryptographic hash function implementation from
//! RustCrypto project. You can either import specific crate (e.g. `sha2`), or
//! meta-crate `crypto-hashes` which reexport all related crates.
//!
//! # Usage
//! Let us demonstrate how to use HMAC using SHA256 as an example.
//!
//! To get the authentication code:
//!
//! ```rust
//! use sha2::Sha256;
//! use hmac::{Hmac, Mac, NewMac};
//!
//! // Create alias for HMAC-SHA256
//! type HmacSha256 = Hmac<Sha256>;
//!
//! // Create HMAC-SHA256 instance which implements `Mac` trait
//! let mut mac = HmacSha256::new_from_slice(b"my secret and secure key")
//! .expect("HMAC can take key of any size");
//! mac.update(b"input message");
//!
//! // `result` has type `Output` which is a thin wrapper around array of
//! // bytes for providing constant time equality check
//! let result = mac.finalize();
//! // To get underlying array use `into_bytes` method, but be careful, since
//! // incorrect use of the code value may permit timing attacks which defeat
//! // the security provided by the `Output`
//! let code_bytes = result.into_bytes();
//! ```
//!
//! To verify the message:
//!
//! ```rust
//! # use sha2::Sha256;
//! # use hmac::{Hmac, Mac, NewMac};
//! # type HmacSha256 = Hmac<Sha256>;
//! let mut mac = HmacSha256::new_from_slice(b"my secret and secure key")
//! .expect("HMAC can take key of any size");
//!
//! mac.update(b"input message");
//!
//! # let code_bytes = mac.clone().finalize().into_bytes();
//! // `verify` will return `Ok(())` if code is correct, `Err(MacError)` otherwise
//! mac.verify(&code_bytes).unwrap();
//! ```
//!
//! # Block and input sizes
//! Usually it is assumed that block size is larger than output size, due to the
//! generic nature of the implementation this edge case must be handled as well
//! to remove potential panic scenario. This is done by truncating hash output
//! to the hash block size if needed.
#![no_std]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
)]
#![forbid(unsafe_code)]
#![warn(missing_docs, rust_2018_idioms)]
#[cfg(feature = "std")]
extern crate std;
pub use crypto_mac::{self, Mac, NewMac};
pub use digest;
use core::{cmp::min, fmt};
use crypto_mac::{
generic_array::{sequence::GenericSequence, ArrayLength, GenericArray},
InvalidKeyLength, Output,
};
use digest::{BlockInput, FixedOutput, Reset, Update};
const IPAD: u8 = 0x36;
const OPAD: u8 = 0x5C;
/// The `Hmac` struct represents an HMAC using a given hash function `D`.
pub struct Hmac<D>
where
D: Update + BlockInput + FixedOutput + Reset + Default + Clone,
D::BlockSize: ArrayLength<u8>,
{
digest: D,
i_key_pad: GenericArray<u8, D::BlockSize>,
opad_digest: D,
}
impl<D> Clone for Hmac<D>
where
D: Update + BlockInput + FixedOutput + Reset + Default + Clone,
D::BlockSize: ArrayLength<u8>,
{
fn clone(&self) -> Hmac<D> {
Hmac {
digest: self.digest.clone(),
i_key_pad: self.i_key_pad.clone(),
opad_digest: self.opad_digest.clone(),
}
}
}
impl<D> fmt::Debug for Hmac<D>
where
D: Update + BlockInput + FixedOutput + Reset + Default + Clone + fmt::Debug,
D::BlockSize: ArrayLength<u8>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Hmac")
.field("digest", &self.digest)
.field("i_key_pad", &self.i_key_pad)
.field("opad_digest", &self.opad_digest)
.finish()
}
}
impl<D> NewMac for Hmac<D>
where
D: Update + BlockInput + FixedOutput + Reset + Default + Clone,
D::BlockSize: ArrayLength<u8>,
D::OutputSize: ArrayLength<u8>,
{
type KeySize = D::BlockSize;
fn new(key: &GenericArray<u8, Self::KeySize>) -> Self {
Self::new_from_slice(key.as_slice()).unwrap()
}
#[inline]
fn new_from_slice(key: &[u8]) -> Result<Self, InvalidKeyLength> {
let mut hmac = Self {
digest: Default::default(),
i_key_pad: GenericArray::generate(|_| IPAD),
opad_digest: Default::default(),
};
let mut opad = GenericArray::<u8, D::BlockSize>::generate(|_| OPAD);
debug_assert!(hmac.i_key_pad.len() == opad.len());
// The key that Hmac processes must be the same as the block size of the
// underlying Digest. If the provided key is smaller than that, we just
// pad it with zeros. If its larger, we hash it and then pad it with
// zeros.
if key.len() <= hmac.i_key_pad.len() {
for (k_idx, k_itm) in key.iter().enumerate() {
hmac.i_key_pad[k_idx] ^= *k_itm;
opad[k_idx] ^= *k_itm;
}
} else {
let mut digest = D::default();
digest.update(key);
let output = digest.finalize_fixed();
// `n` is calculated at compile time and will equal
// D::OutputSize. This is used to ensure panic-free code
let n = min(output.len(), hmac.i_key_pad.len());
for idx in 0..n {
hmac.i_key_pad[idx] ^= output[idx];
opad[idx] ^= output[idx];
}
}
hmac.digest.update(&hmac.i_key_pad);
hmac.opad_digest.update(&opad);
Ok(hmac)
}
}
impl<D> Mac for Hmac<D>
where
D: Update + BlockInput + FixedOutput + Reset + Default + Clone,
D::BlockSize: ArrayLength<u8>,
D::OutputSize: ArrayLength<u8>,
{
type OutputSize = D::OutputSize;
#[inline]
fn update(&mut self, data: &[u8]) {
self.digest.update(data);
}
#[inline]
fn finalize(self) -> Output<Self> {
let mut opad_digest = self.opad_digest.clone();
let hash = self.digest.finalize_fixed();
opad_digest.update(&hash);
Output::new(opad_digest.finalize_fixed())
}
#[inline]
fn reset(&mut self) {
self.digest.reset();
self.digest.update(&self.i_key_pad);
}
}
#[cfg(feature = "std")]
impl<D> std::io::Write for Hmac<D>
where
D: Update + BlockInput + FixedOutput + Reset + Default + Clone,
D::BlockSize: ArrayLength<u8>,
D::OutputSize: ArrayLength<u8>,
{
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
Mac::update(self, buf);
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}