t-rust-less-lib 0.2.23

Password manager library
Documentation
use super::OTPAlgorithm;
use aes_gcm::aes::cipher::BlockSizeUser;
use byteorder::{BigEndian, ByteOrder};
use hmac::digest::block_buffer::Eager;
use hmac::digest::core_api::{BufferKindUser, CoreProxy, FixedOutputCore, UpdateCore};
use hmac::digest::typenum::{IsLess, Le, NonZero};
use hmac::digest::HashMarker;
use hmac::{Hmac, Mac};
use sha1::Sha1;
use sha2::{Sha256, Sha512};
use typenum::consts::U256;

#[derive(Debug)]
pub struct HOTPGenerator<'a> {
  pub algorithm: OTPAlgorithm,
  pub counter: u64,
  pub digits: u8,
  pub secret: &'a [u8],
}

impl HOTPGenerator<'_> {
  fn calculate<D>(&mut self) -> String
  where
    D: CoreProxy,
    D::Core: HashMarker + UpdateCore + FixedOutputCore + BufferKindUser<BufferKind = Eager> + Default + Clone,
    <D::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
    Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
  {
    let mut mac = Hmac::<D>::new_from_slice(self.secret).unwrap();
    mac.update(&self.counter.to_be_bytes());

    self.counter += 1;

    let result = mac.finalize();
    let digest = result.into_bytes();

    let offset: usize = (digest[digest.len() - 1] & 0xf) as usize;

    let base = BigEndian::read_u32(&digest[offset..offset + 4]) & 0x7fff_ffff;

    format!(
      "{:01$}",
      base % (10_u32).pow(u32::from(self.digits)),
      self.digits as usize
    )
  }

  pub fn generate(&mut self) -> (String, u64) {
    let otp = match self.algorithm {
      OTPAlgorithm::SHA1 => self.calculate::<Sha1>(),
      OTPAlgorithm::SHA256 => self.calculate::<Sha256>(),
      OTPAlgorithm::SHA512 => self.calculate::<Sha512>(),
    };
    (otp, self.counter)
  }
}