/* This file is part of DarkFi (https://dark.fi)
*
* Copyright (C) 2020-2023 Dyne.org foundation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
//! HMAC simplementation.
//! https://en.wikipedia.org/wiki/Hmac
use digest::{
core_api::Block, crypto_common::BlockSizeUser, Digest, FixedOutput, Output, OutputSizeUser,
Update,
};
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 zeroes. If it's larger, we hash
// it and then pad it with zeroes.
if key.len() <= der_key.len() {
der_key[..key.len()].copy_from_slice(key);
return der_key
}
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 calculated
// at compile time, so this branch gets removed from 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
}
/// HMAC for arbitrary hash functions that implement `Digest`
/// and `BlockSizeUser` traits.
#[derive(Clone)]
pub struct Hmac {
digest: D,
opad_key: Block,
}
impl Hmac {
/// Initialize a new `Hmac` with the given key.
#[inline]
pub fn new_from_slice(key: &[u8]) -> Self {
let der_key = get_der_key::(key);
let mut ipad_key = der_key.clone();
for b in ipad_key.iter_mut() {
*b ^= IPAD;
}
let mut digest = D::new();
digest.update(&ipad_key);
let mut opad_key = der_key;
for b in opad_key.iter_mut() {
*b ^= OPAD;
}
Self { digest, opad_key }
}
/// Finalize the HMAC
pub fn finalize(self) -> Output {
Output::::clone_from_slice(&self.finalize_fixed())
}
}
impl FixedOutput for Hmac {
fn finalize_into(self, out: &mut Output) {
let mut h = D::new();
h.update(&self.opad_key);
h.update(&self.digest.finalize());
h.finalize_into(out);
}
}
impl OutputSizeUser for Hmac {
type OutputSize = D::OutputSize;
}
impl Update for Hmac {
/// Update the HMAC with the given data.
fn update(&mut self, data: &[u8]) {
self.digest.update(data);
}
}