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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
use crate::utils::*;
use botan_sys::*;

/// Generate or check HOTP tokens
#[derive(Debug)]
#[allow(clippy::upper_case_acronyms)]
pub struct HOTP {
    obj: botan_hotp_t,
}

unsafe impl Sync for HOTP {}
unsafe impl Send for HOTP {}

botan_impl_drop!(HOTP, botan_hotp_destroy);

/// Generate or check TOTP tokens
#[derive(Debug)]
#[allow(clippy::upper_case_acronyms)]
pub struct TOTP {
    obj: botan_totp_t,
}

unsafe impl Sync for TOTP {}
unsafe impl Send for TOTP {}

botan_impl_drop!(TOTP, botan_totp_destroy);

impl HOTP {
    /// Instantiate a new HOTP instance with the given parameters
    ///
    /// # Examples
    ///
    /// ```
    /// let hotp = botan::HOTP::new(&[1,2,3,4], "SHA-1", 6);
    /// ```
    pub fn new(key: &[u8], hash_algo: &str, digits: usize) -> Result<HOTP> {
        let hash_algo = make_cstr(hash_algo)?;

        let obj = botan_init!(
            botan_hotp_init,
            key.as_ptr(),
            key.len(),
            hash_algo.as_ptr(),
            digits
        )?;

        Ok(HOTP { obj })
    }

    /// Generate an HOTP code
    pub fn generate(&self, counter: u64) -> Result<u32> {
        let mut code = 0;
        botan_call!(botan_hotp_generate, self.obj, &mut code, counter)?;
        Ok(code)
    }

    /// Check an HOTP code
    pub fn check(&self, code: u32, counter: u64) -> Result<bool> {
        let cmp_code = self.generate(counter)?;
        Ok(cmp_code == code)
    }

    /// Check an HOTP code, allowing counter resync
    pub fn check_with_resync(
        &self,
        code: u32,
        counter: u64,
        resync_range: usize,
    ) -> Result<(bool, u64)> {
        let mut new_ctr = 0;
        let res = botan_bool_in_rc!(
            botan_hotp_check,
            self.obj,
            &mut new_ctr,
            code,
            counter,
            resync_range
        )?;

        // Return value is inverted
        if !res {
            Ok((true, new_ctr))
        } else {
            Ok((false, counter))
        }
    }
}

impl TOTP {
    /// Instantiate a new TOTP instance with the given parameters
    ///
    /// # Examples
    ///
    /// ```
    /// let totp = botan::TOTP::new(&[1,2,3,4], "SHA-1", 6, 30);
    /// ```
    pub fn new(key: &[u8], hash_algo: &str, digits: usize, time_step: usize) -> Result<TOTP> {
        let hash_algo = make_cstr(hash_algo)?;

        let obj = botan_init!(
            botan_totp_init,
            key.as_ptr(),
            key.len(),
            hash_algo.as_ptr(),
            digits,
            time_step
        )?;

        Ok(TOTP { obj })
    }

    /// Generate an TOTP code
    pub fn generate(&self, timestamp: u64) -> Result<u32> {
        let mut code = 0;
        botan_call!(botan_totp_generate, self.obj, &mut code, timestamp)?;
        Ok(code)
    }

    /// Check an TOTP code
    pub fn check(&self, code: u32, timestamp: u64, allowed_drift: usize) -> Result<bool> {
        // Return value is inverted
        Ok(!botan_bool_in_rc!(
            botan_totp_check,
            self.obj,
            code,
            timestamp,
            allowed_drift
        )?)
    }
}