use crate::{constant_time, digest, error, hkdf, rand};
#[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 {
ctx_prototype: Context,
}
#[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.digest_algorithm())
.finish()
}
}
impl Key {
pub fn derive(digest_alg: &'static digest::Algorithm, okm: hkdf::Okm) -> Self {
let mut key_bytes = [0; digest::MAX_OUTPUT_LEN];
let key_bytes = &mut key_bytes[..digest_alg.output_len];
okm.fill(key_bytes).unwrap();
Self::new(digest_alg, key_bytes)
}
pub fn generate(
digest_alg: &'static digest::Algorithm,
rng: &dyn rand::SecureRandom,
) -> Result<Self, error::Unspecified> {
let mut key_bytes = [0; digest::MAX_OUTPUT_LEN];
let key_bytes = &mut key_bytes[..digest_alg.output_len];
rng.fill(key_bytes)?;
Ok(Self::new(digest_alg, key_bytes))
}
pub fn new(digest_alg: &'static digest::Algorithm, key_value: &[u8]) -> Self {
let mut key = Self {
ctx_prototype: Context {
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;
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.ctx_prototype.inner.update(&padded_key);
const OPAD: u8 = 0x5C;
for b in padded_key.iter_mut() {
*b ^= IPAD ^ OPAD;
}
key.ctx_prototype.outer.update(&padded_key);
key
}
pub fn digest_algorithm(&self) -> &'static digest::Algorithm {
self.ctx_prototype.inner.algorithm()
}
}
#[derive(Clone)]
pub struct Context {
inner: digest::Context,
outer: digest::Context,
}
#[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 {
signing_key.ctx_prototype.clone()
}
pub fn update(&mut self, data: &[u8]) {
self.inner.update(data);
}
pub fn sign(mut self) -> Tag {
self.outer.update(self.inner.finish().as_ref());
Tag(self.outer.finish())
}
}
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::{digest, 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 d in &digest::test_util::ALL_ALGORITHMS {
let key = hmac::Key::generate(d, &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())
}
}
}