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
use hmacsha1::hmac_sha1;
fn u64_to_8_length_u8_array(input: u64) -> [u8; 8] {
let mut bytes = [0_u8; 8];
for (i, item) in bytes.iter_mut().enumerate().take(7) {
*item = (input >> (i * 8)) as u8;
}
bytes.reverse();
bytes
}
pub struct HOTP {
pub secret: String,
pub counter: u64,
}
impl Clone for HOTP {
fn clone(&self) -> Self {
Self {
secret: self.secret.clone(),
counter: self.counter,
}
}
}
impl HOTP {
pub fn new(secret: String, counter: u64) -> HOTP {
HOTP { secret, counter }
}
pub fn make(&self, digits: u32) -> String {
let counter_bytes = u64_to_8_length_u8_array(self.counter);
let digest = hmac_sha1(self.secret.as_bytes(), &counter_bytes);
let offset = digest[19] as usize & 0x0f;
let value = (u32::from(digest[offset]) & 0x7f) << 24
| (u32::from(digest[offset + 1]) & 0xff) << 16
| (u32::from(digest[offset + 2]) & 0xff) << 8
| u32::from(digest[offset + 3]) & 0xff;
let mut code = (value % 10_u32.pow(digits)).to_string();
if code.len() != (digits as usize) {
code = "0".repeat((digits - (code.len() as u32)) as usize) + &code;
}
code
}
pub fn check(&mut self, otp: &str, window: u64) -> bool {
self.counter -= window;
let count = self.counter + window;
loop {
let code = self.make(otp.len() as u32);
if code == otp {
break true;
} else if count < self.counter {
break false;
}
self.counter += 1;
}
}
}