1#![deny(missing_docs)]
2#![cfg_attr(not(feature = "clib"), deny(unsafe_code))]
3mod authenticator;
32
33pub use authenticator::*;
34#[cfg(feature = "clib")]
35use std::ffi::{CStr, CString};
36#[cfg(feature = "clib")]
37use std::os::raw::c_char;
38
39lazy_static::lazy_static! {
40 pub static ref GA_AUTH: GoogleAuthenticator = GoogleAuthenticator::new();
44}
45
46#[macro_export]
49macro_rules! create_secret {
50 ($length: expr) => {
51 GA_AUTH.create_secret($length)
52 };
53 () => {
54 GA_AUTH.create_secret(32)
55 };
56}
57
58#[macro_export]
62macro_rules! get_code {
63 ($secret: expr, $time_slice: expr) => {
64 GA_AUTH.get_code($secret, time_slice)
65 };
66 ($secret: expr) => {
67 GA_AUTH.get_code($secret, 0)
68 };
69}
70
71#[macro_export]
75macro_rules! verify_code {
76 ($secret: expr, $code: expr, $discrepancy: expr, $time_slice: expr) => {
77 GA_AUTH.verify_code($secret, $code, $discrepancy, $time_slice)
78 };
79 ($secret: expr, $code: expr) => {
80 GA_AUTH.verify_code($secret, $code, 0, 0)
81 };
82}
83
84#[macro_export]
88macro_rules! qr_code_url {
89 ($secret: expr, $name: expr, $title: expr, $width: expr, $height: expr, $level: expr) => {
90 GA_AUTH.qr_code_url($secret, $name, $title, $width, $height, $level)
91 };
92 ($secret: expr, $name: expr, $title: expr) => {
93 GA_AUTH.qr_code_url(
94 $secret,
95 $name,
96 $title,
97 200,
98 200,
99 $crate::ErrorCorrectionLevel::Medium,
100 )
101 };
102}
103
104#[macro_export]
108macro_rules! qr_code {
109 ($secret: expr, $name: expr, $title: expr, $width: expr, $height: expr, $level: expr) => {
110 GA_AUTH.qr_code($secret, $name, $title, $width, $height, $level)
111 };
112 ($secret: expr, $name: expr, $title: expr) => {
113 GA_AUTH.qr_code(
114 $secret,
115 $name,
116 $title,
117 200,
118 200,
119 $crate::ErrorCorrectionLevel::Medium,
120 )
121 };
122}
123
124#[cfg(feature = "clib")]
127#[no_mangle]
128pub extern "C" fn create_secret(len: u8) -> *const c_char {
129 CString::new(GA_AUTH.create_secret(len))
130 .expect("can't make secret.")
131 .into_raw()
132}
133
134#[cfg(all(feature = "with-qrcode", feature = "clib"))]
138#[no_mangle]
139pub unsafe extern "C" fn qr_code(
140 secret: *const c_char,
141 name: *const c_char,
142 title: *const c_char,
143 witdh: u32,
144 height: u32,
145 level: crate::ErrorCorrectionLevel,
146) -> *const c_char {
147 CString::new(
148 GA_AUTH
149 .qr_code(
150 unsafe { CStr::from_ptr(secret) }.to_str().unwrap(),
151 unsafe { CStr::from_ptr(name) }.to_str().unwrap(),
152 unsafe { CStr::from_ptr(title) }.to_str().unwrap(),
153 witdh,
154 height,
155 level,
156 )
157 .expect("can't get qr code."),
158 )
159 .unwrap()
160 .into_raw()
161}
162
163#[cfg(feature = "clib")]
168#[no_mangle]
169pub unsafe extern "C" fn qr_code_url(
170 secret: *const c_char,
171 name: *const c_char,
172 title: *const c_char,
173 witdh: u32,
174 height: u32,
175 level: crate::ErrorCorrectionLevel,
176) -> *const c_char {
177 CString::new(GA_AUTH.qr_code_url(
178 unsafe { CStr::from_ptr(secret) }.to_str().unwrap(),
179 unsafe { CStr::from_ptr(name) }.to_str().unwrap(),
180 unsafe { CStr::from_ptr(title) }.to_str().unwrap(),
181 witdh,
182 height,
183 level,
184 ))
185 .expect("can't get qrcode url now.")
186 .into_raw()
187}
188
189#[cfg(feature = "clib")]
195#[no_mangle]
196pub unsafe extern "C" fn get_code(secret: *const c_char, time_slice: u64) -> *const c_char {
197 CString::new(
198 GA_AUTH
199 .get_code(
200 unsafe { CStr::from_ptr(secret) }.to_str().unwrap(),
201 time_slice,
202 )
203 .expect("can't get code now"),
204 )
205 .unwrap()
206 .into_raw()
207}
208
209#[cfg(feature = "clib")]
214#[no_mangle]
215pub unsafe extern "C" fn verify_code(
216 secret: *const c_char,
217 code: *const c_char,
218 discrepancy: u64,
219 time_slice: u64,
220) -> bool {
221 GA_AUTH.verify_code(
222 unsafe { CStr::from_ptr(secret) }.to_str().unwrap(),
223 unsafe { CStr::from_ptr(code) }.to_str().unwrap(),
224 discrepancy,
225 time_slice,
226 )
227}
228
229#[cfg(feature = "clib")]
233#[no_mangle]
234pub unsafe extern "C" fn free_str(str: *mut c_char) {
235 unsafe {
236 let _ = CString::from_raw(str);
237 }
238}
239
240#[cfg(test)]
241mod tests {
242 #[cfg(feature = "with-qrcode")]
243 use crate::ErrorCorrectionLevel::*;
244 use crate::GoogleAuthenticator;
245
246 #[test]
247 fn create_secret() {
248 let auth = GoogleAuthenticator::new();
249 let secret = auth.create_secret(32);
250 assert_eq!(secret.len(), 32);
253 }
254
255 #[test]
256 fn test_code() {
257 let auth = GoogleAuthenticator::new();
258 let secret = "I3VFM3JKMNDJCDH5BMBEEQAW6KJ6NOE3";
259 assert_eq!(6, auth.get_code(secret, 0).unwrap().len());
260 }
261
262 #[test]
263 fn test_verify_code() {
264 let auth = GoogleAuthenticator::new();
265 let secret = "I3VFM3JKMNDJCDH5BMBEEQAW6KJ6NOE3";
266 #[cfg(feature = "with-qrcode")]
267 println!(
268 "{:?}",
269 auth.qr_code(secret, "qr_code", "name", 0, 0, Medium)
270 );
271 assert!(auth.verify_code(secret, "224124", 3, 1523610659 / 30));
272 }
273
274 #[test]
275 #[cfg(feature = "with-qrcode")]
276 fn test_qr_code_url() {
277 let auth = GoogleAuthenticator::new();
278 let secret = "I3VFM3JKMNDJCDH5BMBEEQAW6KJ6NOE3";
279 let url = auth.qr_code_url(secret, "secret code", "hi there", 0, 0, Medium);
280 println!("{}", url);
281 let resp = ureq::get(&url).call();
282 assert!(resp)
283 }
285
286 #[test]
287 #[cfg(feature = "with-qrcode")]
288 fn test_qr_code() {
289 let auth = GoogleAuthenticator::new();
290 let secret = "I3VFM3JKMNDJCDH5BMBEEQAW6KJ6NOE3";
291 auth.qr_code(secret, "secret_code", "hi", 0, 0, Medium)
292 .unwrap();
293 }
294}
295
296#[cfg(test)]
297mod macro_tests {
298 use crate::GA_AUTH;
299
300 #[test]
301 fn create_secret() {
302 let secret = create_secret!();
303 assert_eq!(secret.len(), 32);
304 }
305
306 #[test]
307 fn test_code() {
308 let secret = "I3VFM3JKMNDJCDH5BMBEEQAW6KJ6NOE3";
309 assert_eq!(6, get_code!(secret).unwrap().len());
310 }
311
312 #[test]
313 fn test_verify_code() {
314 let secret = "I3VFM3JKMNDJCDH5BMBEEQAW6KJ6NOE3";
315 let code = get_code!(secret).unwrap();
316 assert!(verify_code!(secret, &code));
317 }
318
319 #[test]
320 #[cfg(feature = "with-qrcode")]
321 fn test_qr_code() {
322 let secret = "I3VFM3JKMNDJCDH5BMBEEQAW6KJ6NOE3";
323 assert!(qr_code!(secret, "qr_code", "name").is_ok());
324 }
325 #[test]
326 #[cfg(feature = "with-qrcode")]
327 fn test_qr_code_url() {
328 let secret = "I3VFM3JKMNDJCDH5BMBEEQAW6KJ6NOE3";
329 qr_code_url!(secret, "qr_code", "name");
330 }
331}