use crate::{constant_time, digest, error, hkdf, rand};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Algorithm(&'static digest::Algorithm);
impl Algorithm {
#[inline]
pub fn digest_algorithm(&self) -> &'static digest::Algorithm {
self.0
}
}
pub static HMAC_SHA1_FOR_LEGACY_USE_ONLY: Algorithm = Algorithm(&digest::SHA1_FOR_LEGACY_USE_ONLY);
pub static HMAC_SHA256: Algorithm = Algorithm(&digest::SHA256);
pub static HMAC_SHA384: Algorithm = Algorithm(&digest::SHA384);
pub static HMAC_SHA512: Algorithm = Algorithm(&digest::SHA512);
#[deprecated(note = "`Signature` was renamed to `Tag`. This alias will be removed soon.")]
pub type Signature = Tag;
#[derive(Clone, Copy, Debug)]
pub struct Tag(digest::Digest);
impl AsRef<[u8]> for Tag {
#[inline]
fn as_ref(&self) -> &[u8] {
self.0.as_ref()
}
}
#[derive(Clone)]
pub struct Key {
inner: digest::BlockContext,
outer: digest::BlockContext,
}
#[deprecated(note = "Renamed to `hmac::Key`.")]
pub type SigningKey = Key;
#[deprecated(
note = "The distinction between verification & signing keys was removed. Use `hmac::Key`."
)]
pub type VerificationKey = Key;
impl core::fmt::Debug for Key {
fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
f.debug_struct("Key")
.field("algorithm", self.algorithm().digest_algorithm())
.finish()
}
}
impl Key {
pub fn generate(
algorithm: Algorithm,
rng: &dyn rand::SecureRandom,
) -> Result<Self, error::Unspecified> {
Self::construct(algorithm, |buf| rng.fill(buf))
}
fn construct<F>(algorithm: Algorithm, fill: F) -> Result<Self, error::Unspecified>
where
F: FnOnce(&mut [u8]) -> Result<(), error::Unspecified>,
{
let mut key_bytes = [0; digest::MAX_OUTPUT_LEN];
let key_bytes = &mut key_bytes[..algorithm.0.output_len];
fill(key_bytes)?;
Ok(Self::new(algorithm, key_bytes))
}
pub fn new(algorithm: Algorithm, key_value: &[u8]) -> Self {
let digest_alg = algorithm.0;
let mut key = Self {
inner: digest::BlockContext::new(digest_alg),
outer: digest::BlockContext::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;
let mut padded_key = [IPAD; digest::MAX_BLOCK_LEN];
let padded_key = &mut padded_key[..digest_alg.block_len];
for (padded_key, key_value) in padded_key.iter_mut().zip(key_value.iter()) {
*padded_key ^= *key_value;
}
key.inner.update(&padded_key);
const OPAD: u8 = 0x5C;
for b in padded_key.iter_mut() {
*b ^= IPAD ^ OPAD;
}
key.outer.update(&padded_key);
key
}
#[inline]
pub fn algorithm(&self) -> Algorithm {
Algorithm(self.inner.algorithm)
}
}
impl hkdf::KeyType for Algorithm {
fn len(&self) -> usize {
self.digest_algorithm().output_len
}
}
impl From<hkdf::Okm<'_, Algorithm>> for Key {
fn from(okm: hkdf::Okm<Algorithm>) -> Self {
Key::construct(*okm.len(), |buf| okm.fill(buf)).unwrap()
}
}
#[derive(Clone)]
pub struct Context {
inner: digest::Context,
outer: digest::BlockContext,
}
#[deprecated(note = "Renamed to `hmac::Context`.")]
pub type SigningContext = Context;
impl core::fmt::Debug for Context {
fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
f.debug_struct("Context")
.field("algorithm", self.inner.algorithm())
.finish()
}
}
impl Context {
pub fn with_key(signing_key: &Key) -> Self {
Self {
inner: digest::Context::clone_from(&signing_key.inner),
outer: signing_key.outer.clone(),
}
}
pub fn update(&mut self, data: &[u8]) {
self.inner.update(data);
}
pub fn sign(self) -> Tag {
let algorithm = self.inner.algorithm();
let mut pending = [0u8; digest::MAX_BLOCK_LEN];
let pending = &mut pending[..algorithm.block_len];
let num_pending = algorithm.output_len;
pending[..num_pending].copy_from_slice(self.inner.finish().as_ref());
Tag(self.outer.finish(pending, num_pending))
}
}
pub fn sign(key: &Key, data: &[u8]) -> Tag {
let mut ctx = Context::with_key(key);
ctx.update(data);
ctx.sign()
}
pub fn verify(key: &Key, data: &[u8], tag: &[u8]) -> Result<(), error::Unspecified> {
constant_time::verify_slices_are_equal(sign(key, data).as_ref(), tag)
}
#[cfg(test)]
mod tests {
use crate::{hmac, rand};
#[test]
pub fn hmac_signing_key_coverage() {
let mut rng = rand::SystemRandom::new();
const HELLO_WORLD_GOOD: &[u8] = b"hello, world";
const HELLO_WORLD_BAD: &[u8] = b"hello, worle";
for algorithm in &[
hmac::HMAC_SHA1_FOR_LEGACY_USE_ONLY,
hmac::HMAC_SHA256,
hmac::HMAC_SHA384,
hmac::HMAC_SHA512,
] {
let key = hmac::Key::generate(*algorithm, &mut rng).unwrap();
let tag = hmac::sign(&key, HELLO_WORLD_GOOD);
assert!(hmac::verify(&key, HELLO_WORLD_GOOD, tag.as_ref()).is_ok());
assert!(hmac::verify(&key, HELLO_WORLD_BAD, tag.as_ref()).is_err())
}
}
}