1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
use crate::{calc_digest, decode_secret, encode_digest};
use crate::{Algorithm, OTP, TOTP};
use anyhow::Result;

pub static HOTP_DEFAULT_COUNTER: u32 = 1;

pub struct HOTP {
    pub algorithm: Algorithm,
    pub digits: u32,
    pub secret: String,
}

impl OTP for HOTP {
    /// Performs the [HMAC-based One-time Password Algorithm](http://en.wikipedia.org/wiki/HMAC-based_One-time_Password_Algorithm)
    /// (HOTP) given an RFC4648 base32 encoded secret, and an integer counter.
    fn compute(&self, counter: u64) -> Result<String> {
        let decoded = decode_secret(&self.secret)?;
        let digest =
            encode_digest(calc_digest(decoded.as_slice(), counter, self.algorithm).as_ref())?;
        let padded_code = format!(
            "{:0width$}",
            digest % 10_u32.pow(self.digits),
            width = self.digits as usize
        );
        Ok(padded_code)
    }
}

impl From<&TOTP> for HOTP {
    fn from(totp: &TOTP) -> Self {
        Self {
            algorithm: totp.algorithm,
            digits: totp.digits,
            secret: totp.secret.clone(),
        }
    }
}