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
131
132
133
134
135
136
137
138
139
140
141
142
143
use core::{mem, ptr};
use crate::Algorithm;
enum HmacKey {
Sha1(lhash::HmacKey::<lhash::Sha1>),
Sha256(lhash::HmacKey::<lhash::Sha256>),
Sha512(lhash::HmacKey::<lhash::Sha512>),
}
#[derive(Copy, Clone)]
enum HmacOutput {
Sha1(<lhash::Sha1 as lhash::Digest>::OutputType),
Sha256(<lhash::Sha256 as lhash::Digest>::OutputType),
Sha512(<lhash::Sha512 as lhash::Digest>::OutputType),
}
impl AsRef<[u8]> for HmacOutput {
#[inline]
fn as_ref(&self) -> &[u8] {
match self {
HmacOutput::Sha1(ref output) => output.as_ref(),
HmacOutput::Sha256(ref output) => output.as_ref(),
HmacOutput::Sha512(ref output) => output.as_ref(),
}
}
}
pub struct HOTP {
key: HmacKey
}
impl HOTP {
#[inline]
pub fn new<T: AsRef<[u8]>>(algorithm: Algorithm, secret: T) -> Self {
let secret = secret.as_ref();
debug_assert_ne!(secret.len(), 0);
Self {
key: match algorithm {
Algorithm::SHA1 => HmacKey::Sha1(lhash::HmacKey::new(secret)),
Algorithm::SHA256 => HmacKey::Sha256(lhash::HmacKey::new(secret)),
Algorithm::SHA512 => HmacKey::Sha512(lhash::HmacKey::new(secret)),
}
}
}
#[inline]
pub fn sign(&self, counter: u64) -> impl AsRef<[u8]> + Clone + Copy {
let counter = counter.to_be_bytes();
match self.key {
HmacKey::Sha1(ref key) => HmacOutput::Sha1(key.sign(&counter)),
HmacKey::Sha256(ref key) => HmacOutput::Sha256(key.sign(&counter)),
HmacKey::Sha512(ref key) => HmacOutput::Sha512(key.sign(&counter)),
}
}
pub fn generate_num(&self, counter: u64, digits: u8) -> u32 {
const BASE: u32 = 10;
let sign = self.sign(counter);
let sign = sign.as_ref();
let offset = (sign[sign.len() - 1] & 15) as usize;
debug_assert!(offset + mem::size_of::<u32>() < sign.len());
let snum = unsafe {
let mut snum = mem::MaybeUninit::<u32>::uninit();
ptr::copy_nonoverlapping(sign.as_ptr().add(offset), snum.as_mut_ptr() as _, 4);
snum.assume_init().to_be() & 0x7fff_ffff
};
snum % BASE.pow(digits as u32)
}
unsafe fn generate_to_ptr(&self, counter: u64, dest: *mut u8, len: usize) {
use core::fmt::{self, Write};
struct WriteBuffer(*mut u8, usize);
impl Write for WriteBuffer {
#[inline]
fn write_str(&mut self, text: &str) -> fmt::Result {
let written_len = text.len();
debug_assert!(written_len <= self.1);
unsafe {
ptr::copy_nonoverlapping(text.as_ptr(), self.0, written_len);
self.0 = self.0.add(written_len);
}
self.1 = self.1 - written_len;
Ok(())
}
}
debug_assert_ne!(len, 0);
debug_assert!(len <= u8::max_value() as _);
debug_assert!(!dest.is_null());
let snum = self.generate_num(counter, len as u8);
let mut buffer = WriteBuffer(dest, len);
let _ = write!(buffer, "{:0width$}", snum, width = len);
}
#[inline]
pub fn generate_to<T: AsMut<[u8]>>(&self, counter: u64, mut dest: T) {
let dest = dest.as_mut();
unsafe {
self.generate_to_ptr(counter, dest.as_mut_ptr(), dest.len())
}
}
pub fn verify(&self, token: &str, counter: u64) -> bool {
debug_assert!(token.len() <= u8::max_value() as _);
let expected = match u32::from_str_radix(token, 10) {
Ok(expected) => expected,
Err(_) => return false,
};
self.generate_num(counter, token.len() as u8) == expected
}
}