dryoc 0.9.0

Don't Roll Your Own Crypto: pure-Rust, hard to misuse cryptography library
Documentation
use subtle::ConstantTimeEq;
use zeroize::Zeroize;

use crate::constants::{CRYPTO_AUTH_HMACSHA256_BYTES, CRYPTO_AUTH_HMACSHA512_BYTES};
use crate::error::Error;
use crate::rng::copy_randombytes;
use crate::sha256::Sha256;
use crate::sha512::Sha512;

pub(crate) trait HmacHash<const OUT_BYTES: usize>: Sized {
    fn new() -> Self;
    fn compute_into_bytes(output: &mut [u8; OUT_BYTES], input: &[u8]);
    fn update(&mut self, input: &[u8]);
    fn finalize_into_bytes(self, output: &mut [u8; OUT_BYTES]);
}

macro_rules! impl_hmac_hash {
    ($hash:ty, $out_bytes:expr) => {
        impl HmacHash<$out_bytes> for $hash {
            fn new() -> Self {
                <$hash>::new()
            }

            fn compute_into_bytes(output: &mut [u8; $out_bytes], input: &[u8]) {
                <$hash>::compute_into_bytes(output, input);
            }

            fn update(&mut self, input: &[u8]) {
                <$hash>::update(self, input);
            }

            fn finalize_into_bytes(self, output: &mut [u8; $out_bytes]) {
                <$hash>::finalize_into_bytes(self, output);
            }
        }
    };
}

impl_hmac_hash!(Sha256, CRYPTO_AUTH_HMACSHA256_BYTES);
impl_hmac_hash!(Sha512, CRYPTO_AUTH_HMACSHA512_BYTES);

pub(crate) struct HmacState<H, const BLOCK_BYTES: usize, const OUT_BYTES: usize> {
    octx: H,
    ictx: H,
}

pub(crate) fn hmac_init<H, const BLOCK_BYTES: usize, const OUT_BYTES: usize>(
    key: &[u8],
) -> HmacState<H, BLOCK_BYTES, OUT_BYTES>
where
    H: HmacHash<OUT_BYTES>,
{
    let mut khash = [0u8; OUT_BYTES];
    let key = if key.len() > BLOCK_BYTES {
        H::compute_into_bytes(&mut khash, key);
        khash.as_slice()
    } else {
        key
    };

    let mut ipad = [0x36u8; BLOCK_BYTES];
    let mut opad = [0x5cu8; BLOCK_BYTES];
    for (dst, src) in ipad.iter_mut().zip(key) {
        *dst ^= src;
    }
    for (dst, src) in opad.iter_mut().zip(key) {
        *dst ^= src;
    }

    let mut ictx = H::new();
    ictx.update(&ipad);
    let mut octx = H::new();
    octx.update(&opad);

    khash.zeroize();
    ipad.zeroize();
    opad.zeroize();

    HmacState { octx, ictx }
}

pub(crate) fn hmac_update<H, const BLOCK_BYTES: usize, const OUT_BYTES: usize>(
    state: &mut HmacState<H, BLOCK_BYTES, OUT_BYTES>,
    input: &[u8],
) where
    H: HmacHash<OUT_BYTES>,
{
    state.ictx.update(input);
}

pub(crate) fn hmac_final<H, const BLOCK_BYTES: usize, const OUT_BYTES: usize>(
    mut state: HmacState<H, BLOCK_BYTES, OUT_BYTES>,
    output: &mut [u8; OUT_BYTES],
) where
    H: HmacHash<OUT_BYTES>,
{
    let mut ihash = [0u8; OUT_BYTES];
    state.ictx.finalize_into_bytes(&mut ihash);
    state.octx.update(&ihash);
    ihash.zeroize();
    state.octx.finalize_into_bytes(output);
}

pub(crate) fn hmac<H, const KEY_BYTES: usize, const BLOCK_BYTES: usize, const OUT_BYTES: usize>(
    mac: &mut [u8; OUT_BYTES],
    message: &[u8],
    key: &[u8; KEY_BYTES],
) where
    H: HmacHash<OUT_BYTES>,
{
    let mut state = hmac_init::<H, BLOCK_BYTES, OUT_BYTES>(key);
    hmac_update(&mut state, message);
    hmac_final(state, mac);
}

pub(crate) fn hmac_verify<
    H,
    const KEY_BYTES: usize,
    const BLOCK_BYTES: usize,
    const OUT_BYTES: usize,
>(
    mac: &[u8; OUT_BYTES],
    input: &[u8],
    key: &[u8; KEY_BYTES],
) -> Result<(), Error>
where
    H: HmacHash<OUT_BYTES>,
{
    let mut computed_mac = [0u8; OUT_BYTES];
    hmac::<H, KEY_BYTES, BLOCK_BYTES, OUT_BYTES>(&mut computed_mac, input, key);
    let valid = mac.ct_eq(&computed_mac).unwrap_u8();
    computed_mac.zeroize();
    if valid == 1 {
        Ok(())
    } else {
        Err(dryoc_error!("authentication codes do not match"))
    }
}

pub(crate) fn hmac_keygen<const KEY_BYTES: usize>() -> [u8; KEY_BYTES] {
    let mut key = [0u8; KEY_BYTES];
    copy_randombytes(&mut key);
    key
}