google_authenticator/
lib.rs

1#![deny(missing_docs)]
2#![cfg_attr(not(feature = "clib"), deny(unsafe_code))]
3// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
4// file at the top-level directory of this distribution and at
5// http://rust-lang.org/COPYRIGHT.
6//
7// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
8// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
9// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
10// option. This file may not be copied, modified, or distributed
11// except according to those terms.
12
13//! This Rust crate can be used to interact with the Google Authenticator mobile app for 2-factor-authentication.
14//! This Rust crates can generate secrets, generate codes, validate codes and present a QR-Code for scanning the secret.
15//! It implements TOTP according to RFC6238
16//!
17//! # Examples
18//!
19//! ```rust
20//! use google_authenticator::GoogleAuthenticator;
21//!
22//! let secret = "I3VFM3JKMNDJCDH5BMBEEQAW6KJ6NOE3";
23//!
24//! let auth = GoogleAuthenticator::new();
25//! let code = auth.get_code(secret,0).unwrap();
26//! if auth.verify_code(secret, code.as_str(), 1, 0) {
27//!     println!("match!");
28//! }
29//! ```
30
31mod 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    /// A globally accessible, thread safe instance of a `GoogleAuthenticator`. Note that if the
41    /// code panics while this variable is in scope, the `std::sync::Mutex` can be poisoned,
42    /// preventing further access to this variable.
43    pub static ref GA_AUTH: GoogleAuthenticator = GoogleAuthenticator::new();
44}
45
46/// A macro that can be used for convenient access to the function
47/// `GoogleAuthenticator::create_secret`, by providing a default of `32` to the `length` parameter.
48#[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/// A macro that can be used for convenient access to the function
59/// `GoogleAuthenticator::get_code`, by providing a default of the current time to the
60/// `times_slice` parameter.
61#[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/// A macro that can be used for convenient access to the function
72/// `GoogleAuthenticator::verify_code`, by providing a default of 0 to the `discrepancy` parameter,
73/// and the current time to the `times_slice` parameter.
74#[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/// A macro that can be used for convenient access to the function
85/// `GoogleAuthenticator::qr_code_url`, by providing a default of 200 to the `width` parameter, 200
86/// to the `height` parameter, and `ErrorCorrectionLevel::Medium` to the `level` parameter.
87#[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/// A macro that can be used for convenient access to the function
105/// `GoogleAuthenticator::qr_code`, by providing a default of 200 to the `width` parameter, 200
106/// to the `height` parameter, and `ErrorCorrectionLevel::Medium` to the `level` parameter.
107#[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/// A function that can be used for convenient access to the function
125/// `create_secret`, by providing a default of `32` to the `length` parameter.
126#[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/// A function that can be used for convenient access to the function
135/// `qr_code`, by providing a default of 200 to the `width` parameter, 200
136/// to the `height` parameter, and `ErrorCorrectionLevel::Medium` to the `level` parameter.
137#[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/// # Safety
164/// A function that can be used for convenient access to the function
165/// `qr_code_url`, by providing a default of 200 to the `width` parameter, 200
166/// to the `height` parameter, and `ErrorCorrectionLevel::Medium` to the `level` parameter.
167#[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/// # Safety
190/// A function that can be used for convenient access to the function
191/// `get_code`, by providing a default of the current time to the
192/// `secret` parameter.
193/// `times_slice` parameter.
194#[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/// # Safety
210/// A function that can be used for convenient access to the function
211/// `verify_code`, by providing a default of 0 to the `discrepancy` parameter,
212/// and the current time to the `times_slice` parameter.
213#[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/// # Safety
230/// A function that can be used for free returnd to C string
231/// `str`, the string which be passed to outside
232#[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        //auth.get_code(secret.as_str(),0);
251        //        println!("{:?}",secret);
252        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        // panic!();
284    }
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}