Skip to main content

botan/
otp.rs

1use crate::utils::*;
2use botan_sys::*;
3
4/// Generate or check HOTP tokens
5#[derive(Debug)]
6#[allow(clippy::upper_case_acronyms)]
7pub struct HOTP {
8    obj: botan_hotp_t,
9}
10
11unsafe impl Sync for HOTP {}
12unsafe impl Send for HOTP {}
13
14botan_impl_drop!(HOTP, botan_hotp_destroy);
15
16/// Generate or check TOTP tokens
17#[derive(Debug)]
18#[allow(clippy::upper_case_acronyms)]
19pub struct TOTP {
20    obj: botan_totp_t,
21}
22
23unsafe impl Sync for TOTP {}
24unsafe impl Send for TOTP {}
25
26botan_impl_drop!(TOTP, botan_totp_destroy);
27
28impl HOTP {
29    /// Instantiate a new HOTP instance with the given parameters
30    ///
31    /// # Examples
32    ///
33    /// ```
34    /// let hotp = botan::HOTP::new(&[1,2,3,4], "SHA-1", 6);
35    /// ```
36    pub fn new(key: &[u8], hash_algo: &str, digits: usize) -> Result<HOTP> {
37        let hash_algo = make_cstr(hash_algo)?;
38
39        let obj = botan_init!(
40            botan_hotp_init,
41            key.as_ptr(),
42            key.len(),
43            hash_algo.as_ptr(),
44            digits
45        )?;
46
47        Ok(HOTP { obj })
48    }
49
50    /// Generate an HOTP code
51    pub fn generate(&self, counter: u64) -> Result<u32> {
52        let mut code = 0;
53        botan_call!(botan_hotp_generate, self.obj, &mut code, counter)?;
54        Ok(code)
55    }
56
57    /// Check an HOTP code
58    pub fn check(&self, code: u32, counter: u64) -> Result<bool> {
59        let cmp_code = self.generate(counter)?;
60        Ok(cmp_code == code)
61    }
62
63    /// Check an HOTP code, allowing counter resync
64    pub fn check_with_resync(
65        &self,
66        code: u32,
67        counter: u64,
68        resync_range: usize,
69    ) -> Result<(bool, u64)> {
70        let mut new_ctr = 0;
71        let res = botan_bool_in_rc!(
72            botan_hotp_check,
73            self.obj,
74            &mut new_ctr,
75            code,
76            counter,
77            resync_range
78        )?;
79
80        // Return value is inverted
81        if !res {
82            Ok((true, new_ctr))
83        } else {
84            Ok((false, counter))
85        }
86    }
87}
88
89impl TOTP {
90    /// Instantiate a new TOTP instance with the given parameters
91    ///
92    /// # Examples
93    ///
94    /// ```
95    /// let totp = botan::TOTP::new(&[1,2,3,4], "SHA-1", 6, 30);
96    /// ```
97    pub fn new(key: &[u8], hash_algo: &str, digits: usize, time_step: usize) -> Result<TOTP> {
98        let hash_algo = make_cstr(hash_algo)?;
99
100        let obj = botan_init!(
101            botan_totp_init,
102            key.as_ptr(),
103            key.len(),
104            hash_algo.as_ptr(),
105            digits,
106            time_step
107        )?;
108
109        Ok(TOTP { obj })
110    }
111
112    /// Generate an TOTP code
113    pub fn generate(&self, timestamp: u64) -> Result<u32> {
114        let mut code = 0;
115        botan_call!(botan_totp_generate, self.obj, &mut code, timestamp)?;
116        Ok(code)
117    }
118
119    /// Check an TOTP code
120    pub fn check(&self, code: u32, timestamp: u64, allowed_drift: usize) -> Result<bool> {
121        // Return value is inverted
122        Ok(!botan_bool_in_rc!(
123            botan_totp_check,
124            self.obj,
125            code,
126            timestamp,
127            allowed_drift
128        )?)
129    }
130}