//! Generic implementation of Hash-based Message Authentication Code (HMAC). //! //! To use it you will need a cryptographic hash function implementation which //! implements the [`digest`] crate traits. You can find compatible crates //! (e.g. [`sha2`]) in the [`RustCrypto/hashes`] repository. //! //! This crate provides two HMAC implementation [`Hmac`] and [`SimpleHmac`]. //! The first one is a buffered wrapper around block-level [`HmacCore`]. //! Internally it uses efficient state representation, but works only with //! hash functions which expose block-level API and consume blocks eagerly //! (e.g. it will not work with the BLAKE2 family of hash functions). //! On the other hand, [`SimpleHmac`] is a bit less efficient memory-wise, //! but works with all hash functions which implement the [`Digest`] trait. //! //! # Examples //! Let us demonstrate how to use HMAC using the SHA-256 hash function. //! //! In the following examples [`Hmac`] is interchangeable with [`SimpleHmac`]. //! //! To get authentication code: //! //! ```rust //! use sha2::Sha256; //! use hmac::{Hmac, Mac}; //! use hex_literal::hex; //! //! // Create alias for HMAC-SHA256 //! type HmacSha256 = Hmac; //! //! 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 `CtOutput` 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`, but be careful, since //! // incorrect use of the code value may permit timing attacks which defeats //! // the security provided by the `CtOutput` //! let code_bytes = result.into_bytes(); //! let expected = hex!(" //! 97d2a569059bbcd8ead4444ff99071f4 //! c01d005bcefe0d3567e1be628e5fdcd9 //! "); //! assert_eq!(code_bytes[..], expected[..]); //! ``` //! //! To verify the message: //! //! ```rust //! # use sha2::Sha256; //! # use hmac::{Hmac, Mac}; //! # use hex_literal::hex; //! # type HmacSha256 = Hmac; //! 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 = hex!(" //! 97d2a569059bbcd8ead4444ff99071f4 //! c01d005bcefe0d3567e1be628e5fdcd9 //! "); //! // `verify_slice` will return `Ok(())` if code is correct, `Err(MacError)` otherwise //! mac.verify_slice(&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. This is done by truncating hash output to the hash //! block size if needed. //! //! [`digest`]: https://docs.rs/digest //! [`sha2`]: https://docs.rs/sha2 //! [`RustCrypto/hashes`]: https://github.com/RustCrypto/hashes #![no_std] #![doc( html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg", html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg", html_root_url = "https://docs.rs/hmac/0.12.1" )] #![forbid(unsafe_code)] #![cfg_attr(docsrs, feature(doc_cfg))] #![warn(missing_docs, rust_2018_idioms)] #[cfg(feature = "std")] extern crate std; pub use digest; pub use digest::Mac; use digest::{ core_api::{Block, BlockSizeUser}, Digest, }; mod optim; mod simple; pub use optim::{Hmac, HmacCore}; pub use simple::SimpleHmac; const IPAD: u8 = 0x36; const OPAD: u8 = 0x5C; fn get_der_key(key: &[u8]) -> Block { let mut der_key = Block::::default(); // The key that HMAC processes must be the same as the block size of the // underlying hash function. 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() <= der_key.len() { der_key[..key.len()].copy_from_slice(key); } else { let hash = D::digest(key); // All commonly used hash functions have block size bigger // than output hash size, but to be extra rigorous we // handle the potential uncommon cases as well. // The condition is calcualted at compile time, so this // branch gets removed from the final binary. if hash.len() <= der_key.len() { der_key[..hash.len()].copy_from_slice(&hash); } else { let n = der_key.len(); der_key.copy_from_slice(&hash[..n]); } } der_key }