132 lines
4.5 KiB
Rust
132 lines
4.5 KiB
Rust
//! 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<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");
|
|
//!
|
|
//! // `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<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 = 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<D: Digest + BlockSizeUser>(key: &[u8]) -> Block<D> {
|
|
let mut der_key = Block::<D>::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
|
|
}
|