use super::ThotpError;
use digest::{
block_buffer::Eager,
core_api::{BufferKindUser, CoreProxy, FixedOutputCore, UpdateCore},
crypto_common::BlockSizeUser,
typenum::{IsLess, Le, NonZero, U256},
FixedOutput, HashMarker, InvalidLength, Update,
};
use hmac::{Hmac, Mac};
use std::time::{SystemTime, UNIX_EPOCH};
pub(super) const DIGITS_DEFAULT: u8 = 6;
pub(super) const TIME_STEP: u8 = 30;
pub(super) const ALLOWED_DRIFT: u8 = 1;
#[inline]
pub(super) fn hmac_digest<H>(secret: &[u8], nonce: &[u8]) -> Result<Vec<u8>, InvalidLength>
where
H: Update + FixedOutput + CoreProxy,
H::Core: HashMarker
+ UpdateCore
+ FixedOutputCore
+ BufferKindUser<BufferKind = Eager>
+ Default
+ Clone,
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
{
let mut mac = Hmac::<H>::new_from_slice(secret)?;
<Hmac<H> as Update>::update(&mut mac, nonce);
Ok(mac.finalize().into_bytes().to_vec())
}
#[inline]
pub(super) fn dynamic_trunc(input: &mut [u8]) -> u32 {
let offset = (input.last().unwrap() & 0xf) as usize;
let mut result: [u8; 4] = input[offset..=offset + 3].try_into().unwrap();
result[0] &= 0x7f;
u32::from_be_bytes(result)
}
#[inline]
pub(super) fn time_step_now() -> Result<u64, ThotpError> {
let time_step = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs() / TIME_STEP as u64;
Ok(time_step)
}
#[cfg(test)]
mod tests {
use super::super::custom::{Sha1, Sha256, Sha512};
use super::super::ThotpError;
use super::*;
#[test]
fn hmac() -> Result<(), ThotpError> {
let hmac = super::hmac_digest::<Sha1>(b"12345678901234567890", b"1")?;
assert!(hmac.len() == 20);
let hmac = super::hmac_digest::<Sha256>(b"12345678901234567890123456789012", b"1")?;
assert!(hmac.len() == 32);
let hmac = super::hmac_digest::<Sha512>(
b"1234567890123456789012345678901234567890123456789012345678901234",
b"1",
)?;
assert!(hmac.len() == 64);
Ok(())
}
#[test]
fn dynamic_trunc_() -> Result<(), ThotpError> {
let mut hmac = super::hmac_digest::<Sha1>(b"super secret key", b"1")?;
assert_eq!(
hmac,
[
104, 105, 130, 165, 155, 87, 155, 213, 180, 67, 104, 223, 123, 179, 211, 125, 173,
78, 220, 226
]
);
let mask = 226 & 0x0f;
assert_eq!(mask, 2);
let res = dynamic_trunc(&mut hmac);
assert_eq!(res, 0x02_a5_9b_57);
Ok(())
}
}