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
use super::{
Array,
HOTP,
TOTP,
};
use std::{
error::Error,
fmt,
primitive::str,
time::*,
};
#[derive(Debug)]
pub enum OTPErr {
Time(SystemTimeError),
Radix(char),
Overflow,
VerificationFailed,
}
impl fmt::Display for OTPErr {
fn fmt(
&self,
f: &mut fmt::Formatter,
) -> fmt::Result {
use OTPErr::*;
match self {
Time(e) => write!(
f,
"system time was less than unix epoch by {:?}",
e.duration(),
),
Radix(c) => write!(f, "char {:?} is not a base-10 digit", c),
Overflow => write!(f, "otp would overflow an i32"),
VerificationFailed => write!(f, "verification failed"),
}
}
}
fn input_to_i32(input: &str) -> Result<i32, OTPErr> {
input
.split_ascii_whitespace()
.flat_map(str::chars)
.try_fold(0i32, |acc, val| {
let add = val.to_digit(10).ok_or_else(|| OTPErr::Radix(val))?;
acc
.checked_mul(10)
.ok_or_else(|| OTPErr::Overflow)?
.checked_add(add as i32)
.ok_or_else(|| OTPErr::Overflow)
})
}
impl Error for OTPErr {}
pub fn read_totp_gauth<Val, Secret>(
input: Val,
secret: Secret,
) -> Result<(), OTPErr>
where
Val: AsRef<str>,
Secret: Array,
{
let time = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.map_err(|e| OTPErr::Time(e))?
.as_secs();
let input = input_to_i32(input.as_ref())?;
if TOTP::new(secret).verify_range_default(time, input as u32) {
Ok(())
} else {
Err(OTPErr::VerificationFailed)
}
}
pub fn read_hotp_gauth<Val, Secret>(
input: Val,
secret: Secret,
counter: u64,
) -> Result<(), OTPErr>
where
Val: AsRef<str>,
Secret: Array,
{
let input = input_to_i32(input.as_ref())?;
if HOTP::new(secret).verify(counter, input as u32) {
Ok(())
} else {
Err(OTPErr::VerificationFailed)
}
}