1use crate::{hotp, totp};
4use hmac::digest::{
5 block_buffer::Eager,
6 core_api::{BufferKindUser, CoreProxy, FixedOutputCore, UpdateCore},
7 crypto_common::BlockSizeUser,
8 typenum::{IsLess, Le, NonZero, U256},
9 HashMarker,
10};
11
12pub fn check_totp<D>(
14 time: u64,
15 step: u64,
16 secret: &[u8],
17 digits: u32,
18 skew: (u64, u64),
19 expected: u32,
20) -> bool
21where
22 D: CoreProxy,
23 D::Core: HashMarker
24 + UpdateCore
25 + FixedOutputCore
26 + BufferKindUser<BufferKind = Eager>
27 + Default
28 + Clone,
29 <D::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
30 Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
31{
32 iter_skew(time, step, skew)
33 .map(|e| totp::<D>(e, step, secret, digits))
34 .any(|code| code == expected)
35}
36
37pub fn check_hotp<D>(time: u64, secret: &[u8], digits: u32, skew: u64, expected: u32) -> bool
39where
40 D: CoreProxy,
41 D::Core: HashMarker
42 + UpdateCore
43 + FixedOutputCore
44 + BufferKindUser<BufferKind = Eager>
45 + Default
46 + Clone,
47 <D::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
48 Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
49{
50 iter_skew(time, 1, (0, skew))
51 .map(|e| hotp::<D>(e, secret, digits))
52 .any(|code| code == expected)
53}
54
55fn iter_skew(time: u64, step: u64, skew: (u64, u64)) -> impl Iterator<Item = u64> {
56 let start = time - (step * skew.0);
57 let end = time + (step * skew.1);
58
59 (start..=end).step_by(step as usize)
60}
61
62#[cfg(test)]
63mod test {
64 use crate::util::iter_skew;
65
66 #[test]
67 fn test_iter_skew() {
68 let values = [
69 (
70 (10000, 2, (2, 4)),
71 vec![9996, 9998, 10000, 10002, 10004, 10006, 10008],
72 ),
73 ((100, 1, (0, 5)), vec![100, 101, 102, 103, 104, 105]),
74 ];
75
76 for ((time, step, skew), output) in values {
77 assert_eq!(iter_skew(time, step, skew).collect::<Vec<_>>(), output);
78 }
79 }
80}