-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgenerator.rs
69 lines (63 loc) · 2.1 KB
/
generator.rs
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
use byteorder::{BigEndian, ByteOrder};
use chrono::Local;
use crypto::mac::Mac;
use crypto::{hmac::Hmac, sha1::Sha1};
use data_encoding::BASE32;
use std::error::Error;
use std::io::{self, ErrorKind};
pub fn generate_otp_token(token: &str) -> Result<String, Box<Error>> {
let now = Local::now().timestamp();
let timer = (now / 30) as u64;
let secret_bytes = BASE32.decode(token.as_bytes());
let bytes = match secret_bytes {
Ok(bytes) => bytes,
Err(_) => {
return Err(io::Error::new(ErrorKind::InvalidInput, "token is not a valid base32 data type").into());
}
};
let mut buf = [0; 8];
let mut hm = Hmac::new(Sha1::new(), &bytes[..]);
BigEndian::write_u64(&mut buf, timer);
hm.input(&buf[..]);
let res = hm.result();
let result = res.code();
let offset = match &result.last() {
Some(l) => *l & 0xf,
None => {
return Err(io::Error::new(ErrorKind::InvalidInput, "was not able to get last byte of hmac result").into());
}
};
// let result = result.to_vec();
let offset = offset as usize;
let value = ((((result[offset]) as i32 & 0x7f) << 24)
| (((result[offset + 1]) as i32 & 0xff) << 16)
| (((result[offset + 2]) as i32 & 0xff) << 8)
| ((result[offset + 3]) as i32 & 0xff)) as i64;
let length = 6;
let pow10: i64 = 10;
let modulo = value % pow10.pow(length);
Ok(format!("{:0length$}", modulo, length = 6))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_generate_token() {
let token = "MFZWIZQ=";
let otp = generate_otp_token(token);
assert_eq!(6, otp.unwrap().len());
}
#[test]
fn test_generate_token_is_the_same_when_called_twice_quickly() {
let token = "MFZWIZQ=";
let otp1 = generate_otp_token(token);
let otp2 = generate_otp_token(token);
assert_eq!(otp1.unwrap(), otp2.unwrap());
}
#[test]
fn test_generate_error_when_giving_non_base32_token() {
let token = "asdf";
let otp = generate_otp_token(token);
assert_eq!(true, otp.is_err());
}
}