1use hmac::digest::{
2 Update, BlockInput, FixedOutput, Reset,
3 generic_array::ArrayLength,
4};
5use crate::error::Error;
6
7#[cfg(any(feature = "std", feature = "libc"))]
9pub fn totp_now<D>(key: &[u8]) -> Result<u64, Error>
10 where D: Update + BlockInput + FixedOutput + Reset + Default + Clone,
11 D::BlockSize: ArrayLength<u8>,
12{
13 totp::<D>(&TotpOptions::with_key_now(key))
14}
15
16pub fn totp<D>(options: &TotpOptions) -> Result<u64, Error>
18 where D: Update + BlockInput + FixedOutput + Reset + Default + Clone,
19 D::BlockSize: ArrayLength<u8>,
20{
21 if options.step == 0 {
22 return Err(Error::InvalidStep);
23 }
24
25 if options.timestamp < options.epoch {
26 return Err(Error::InvalidTimeOrEpoch);
27 }
28
29 let counter = (options.timestamp - options.epoch) / options.step;
30 crate::hotp::<D>(options.key, counter, options.digits)
31}
32
33pub struct TotpOptions<'a> {
35 pub key: &'a [u8],
37 pub digits: u32,
41 pub step: u64,
45 pub epoch: u64,
49 pub timestamp: u64,
53}
54
55impl<'a> TotpOptions<'a> {
56 #[cfg(any(feature = "std", feature = "libc"))]
60 pub fn with_key_now(key: &'a [u8]) -> Self {
61 Self {
62 key,
63 digits: 6,
64 step: 30,
65 epoch: 0,
66 timestamp: now().unwrap_or_default(),
67 }
68 }
69}
70
71#[cfg(all(feature = "std", not(feature = "libc")))]
72fn now() -> Option<u64> {
73 use std::time::{SystemTime, UNIX_EPOCH};
74
75 SystemTime::now().duration_since(UNIX_EPOCH)
76 .ok()
77 .map(|t| t.as_secs())
78}
79
80#[cfg(feature = "libc")]
81fn now() -> Option<u64> {
82 let time = unsafe {
83 libc::time(core::ptr::null_mut())
84 };
85 if time < 0 {
86 return None;
87 }
88
89 Some(time as u64)
90}