Skip to main content

wae_authentication/totp/
service.rs

1//! TOTP 服务实现
2
3use crate::totp::{
4    HotpConfig, SecretFormat, TotpAlgorithm, TotpConfig, TotpSecret, generate_hotp, generate_totp, verify_hotp, verify_totp,
5};
6use std::time::{SystemTime, UNIX_EPOCH};
7use wae_types::WaeError;
8
9/// TOTP 结果类型
10pub type TotpResult<T> = Result<T, WaeError>;
11
12/// TOTP 服务
13#[derive(Debug, Clone)]
14pub struct TotpService {
15    config: TotpConfig,
16    secret: TotpSecret,
17}
18
19impl TotpService {
20    /// 创建新的 TOTP 服务
21    ///
22    /// # Arguments
23    /// * `config` - TOTP 配置
24    pub fn new(config: TotpConfig) -> TotpResult<Self> {
25        let secret = TotpSecret::from_base32(&config.secret)?;
26        Ok(Self { config, secret })
27    }
28
29    /// 创建新的 TOTP 服务 (使用随机密钥)
30    ///
31    /// # Arguments
32    /// * `issuer` - 发行者名称
33    /// * `account_name` - 用户账号
34    pub fn create(issuer: impl Into<String>, account_name: impl Into<String>) -> TotpResult<Self> {
35        let secret = TotpSecret::generate_default()?;
36        let config = TotpConfig::new(issuer, account_name, secret.as_base32());
37        Ok(Self { config, secret })
38    }
39
40    /// 生成当前 TOTP 码
41    pub fn generate_code(&self) -> TotpResult<String> {
42        let timestamp = self.current_timestamp();
43        generate_totp(self.secret.as_bytes(), timestamp, self.config.time_step, self.config.digits, self.config.algorithm)
44    }
45
46    /// 验证 TOTP 码
47    ///
48    /// # Arguments
49    /// * `code` - 用户输入的验证码
50    pub fn verify(&self, code: &str) -> TotpResult<bool> {
51        let timestamp = self.current_timestamp();
52        verify_totp(
53            self.secret.as_bytes(),
54            code,
55            timestamp,
56            self.config.time_step,
57            self.config.digits,
58            self.config.algorithm,
59            self.config.valid_window,
60        )
61    }
62
63    /// 验证 TOTP 码 (严格模式,不使用窗口)
64    pub fn verify_strict(&self, code: &str) -> TotpResult<bool> {
65        let timestamp = self.current_timestamp();
66        verify_totp(
67            self.secret.as_bytes(),
68            code,
69            timestamp,
70            self.config.time_step,
71            self.config.digits,
72            self.config.algorithm,
73            0,
74        )
75    }
76
77    /// 获取当前步剩余秒数
78    pub fn remaining_seconds(&self) -> u64 {
79        let timestamp = self.current_timestamp();
80        crate::totp::algorithm::get_remaining_seconds(timestamp, self.config.time_step)
81    }
82
83    /// 获取进度百分比 (0.0 - 1.0)
84    pub fn progress(&self) -> f32 {
85        let remaining = self.remaining_seconds() as f32;
86        1.0 - (remaining / self.config.time_step as f32)
87    }
88
89    /// 获取 otpauth:// URI
90    pub fn to_uri(&self) -> String {
91        self.config.to_uri()
92    }
93
94    /// 获取密钥
95    pub fn secret(&self) -> &TotpSecret {
96        &self.secret
97    }
98
99    /// 获取格式化的密钥
100    pub fn formatted_secret(&self, format: SecretFormat) -> String {
101        self.secret.format(format)
102    }
103
104    /// 获取配置
105    pub fn config(&self) -> &TotpConfig {
106        &self.config
107    }
108
109    fn current_timestamp(&self) -> u64 {
110        SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs()
111    }
112}
113
114/// HOTP 服务
115#[derive(Debug, Clone)]
116pub struct HotpService {
117    config: HotpConfig,
118    secret: TotpSecret,
119}
120
121impl HotpService {
122    /// 创建新的 HOTP 服务
123    pub fn new(config: HotpConfig) -> TotpResult<Self> {
124        let secret = TotpSecret::from_base32(&config.secret)?;
125        Ok(Self { config, secret })
126    }
127
128    /// 创建新的 HOTP 服务 (使用随机密钥)
129    pub fn create(issuer: impl Into<String>, account_name: impl Into<String>) -> TotpResult<Self> {
130        let secret = TotpSecret::generate_default()?;
131        let config = HotpConfig::new(issuer, account_name, secret.as_base32());
132        Ok(Self { config, secret })
133    }
134
135    /// 生成当前计数器的 HOTP 码
136    pub fn generate_code(&self) -> TotpResult<String> {
137        generate_hotp(self.secret.as_bytes(), self.config.counter, self.config.digits, self.config.algorithm)
138    }
139
140    /// 生成指定计数器的 HOTP 码
141    pub fn generate_code_at(&self, counter: u64) -> TotpResult<String> {
142        generate_hotp(self.secret.as_bytes(), counter, self.config.digits, self.config.algorithm)
143    }
144
145    /// 验证 HOTP 码
146    pub fn verify(&self, code: &str) -> TotpResult<bool> {
147        verify_hotp(self.secret.as_bytes(), code, self.config.counter, self.config.digits, self.config.algorithm)
148    }
149
150    /// 验证 HOTP 码并递增计数器
151    pub fn verify_and_increment(&mut self, code: &str) -> TotpResult<bool> {
152        let valid = self.verify(code)?;
153        if valid {
154            self.config.counter += 1;
155        }
156        Ok(valid)
157    }
158
159    /// 获取当前计数器值
160    pub fn counter(&self) -> u64 {
161        self.config.counter
162    }
163
164    /// 设置计数器值
165    pub fn set_counter(&mut self, counter: u64) {
166        self.config.counter = counter;
167    }
168
169    /// 获取 otpauth:// URI
170    pub fn to_uri(&self) -> String {
171        self.config.to_uri()
172    }
173
174    /// 获取密钥
175    pub fn secret(&self) -> &TotpSecret {
176        &self.secret
177    }
178
179    /// 获取配置
180    pub fn config(&self) -> &HotpConfig {
181        &self.config
182    }
183}
184
185/// 便捷函数:创建 TOTP 服务
186pub fn create_totp(issuer: impl Into<String>, account_name: impl Into<String>) -> TotpResult<TotpService> {
187    TotpService::create(issuer, account_name)
188}
189
190/// 便捷函数:从密钥创建 TOTP 服务
191pub fn totp_from_secret(
192    issuer: impl Into<String>,
193    account_name: impl Into<String>,
194    secret: impl Into<String>,
195) -> TotpResult<TotpService> {
196    let config = TotpConfig::new(issuer, account_name, secret);
197    TotpService::new(config)
198}
199
200/// 便捷函数:生成 TOTP 码
201pub fn generate_totp_code(secret: &str) -> TotpResult<String> {
202    let secret = TotpSecret::from_base32(secret)?;
203    let timestamp = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs();
204    generate_totp(secret.as_bytes(), timestamp, 30, 6, TotpAlgorithm::default())
205}
206
207/// 便捷函数:验证 TOTP 码
208pub fn verify_totp_code(secret: &str, code: &str) -> TotpResult<bool> {
209    let secret = TotpSecret::from_base32(secret)?;
210    let timestamp = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs();
211    verify_totp(secret.as_bytes(), code, timestamp, 30, 6, TotpAlgorithm::default(), 1)
212}