From 66ef16fb450d61e1f314e6cee3952ef7713d207b Mon Sep 17 00:00:00 2001 From: Mark Nijboer Date: Mon, 22 Jun 2020 16:09:53 +0200 Subject: [PATCH 1/3] * Added unit tests, replaced String for &str and removed println --- src/lib.rs | 98 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 81 insertions(+), 17 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d2beeff..f63dc1e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,7 +6,7 @@ //! use std::time::SystemTime; //! use totp_rs::{Algorithm, TOTP}; //! -//! let username = "example".to_owned(); +//! let username = "example"; //! let totp = TOTP::new( //! Algorithm::SHA1, //! 6, @@ -17,7 +17,7 @@ //! let time = SystemTime::now() //! .duration_since(SystemTime::UNIX_EPOCH).unwrap() //! .as_secs(); -//! let url = totp.get_url(format!("account:{}", username), "my-org.com".to_owned()); +//! let url = totp.get_url(username, "my-org.com"); //! println!("{}", url); //! let token = totp.generate(time); //! println!("{}", token); @@ -26,7 +26,7 @@ //! ```rust //! use totp_rs::{Algorithm, TOTP}; //! -//! let username = "example".to_owned(); +//! let username = "example"; //! let totp = TOTP::new( //! Algorithm::SHA1, //! 6, @@ -34,7 +34,7 @@ //! 30, //! "supersecret".to_owned().into_bytes(), //! ); -//! let code = totp.get_qr(format!("account:{}", username), "my-org.com".to_owned())?; +//! let code = totp.get_qr(username, "my-org.com").unwrap(); //! println!("{}", code); //! ``` @@ -127,11 +127,10 @@ impl TOTP { } /// Will check if token is valid by current time, accounting [skew](struct.TOTP.html#structfield.skew) - pub fn check(&self, token: String, time: u64) -> bool { + pub fn check(&self, token: &str, time: u64) -> bool { let basestep = time / self.step - (self.skew as u64); for i in 0..self.skew * 2 + 1 { let step_time = (basestep + (i as u64)) * (self.step as u64); - println!("{}", self.generate(step_time)); if self.generate(step_time) == token { return true; } @@ -139,20 +138,24 @@ impl TOTP { false } + /// Will return the base32 representation of the secret, which might be useful when users want to manually add the secret to their authenticator + pub fn get_secret_base32(&self) -> String { + base32::encode(base32::Alphabet::RFC4648 { padding: false }, &self.secret) + } + /// Will generate a standard URL used to automatically add TOTP auths. Usually used with qr codes - pub fn get_url(&self, label: String, issuer: String) -> String { - let algorithm: String; - match self.algorithm { - Algorithm::SHA1 => algorithm = "SHA1".to_owned(), - Algorithm::SHA256 => algorithm = "SHA256".to_owned(), - Algorithm::SHA512 => algorithm = "SHA512".to_owned(), - } + pub fn get_url(&self, label: &str, issuer: &str) -> String { + let algorithm = match self.algorithm { + Algorithm::SHA1 => "SHA1", + Algorithm::SHA256 => "SHA256", + Algorithm::SHA512 => "SHA512", + }; format!( "otpauth://totp/{}?secret={}&issuer={}&digits={}&algorithm={}", label, - base32::encode(base32::Alphabet::RFC4648 { padding: false }, &self.secret), + self.get_secret_base32(), issuer, - self.digits.to_string(), + self.digits, algorithm, ) } @@ -167,8 +170,8 @@ impl TOTP { #[cfg(feature = "qr")] pub fn get_qr( &self, - label: String, - issuer: String, + label: &str, + issuer: &str, ) -> Result> { let url = self.get_url(label, issuer); let code = QrCode::new(&url)?; @@ -184,3 +187,64 @@ impl TOTP { Ok(base64::encode(vec)) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn url_for_secret_matches() { + let totp = TOTP::new(Algorithm::SHA1, 6, 1, 1, String::from("TestSecret").into_bytes()); + let url = totp.get_url("test_url", "totp-rs"); + assert_eq!(url.as_str(), "otpauth://totp/test_url?secret=KRSXG5CTMVRXEZLU&issuer=totp-rs&digits=6&algorithm=SHA1"); + } + + #[test] + fn returns_base32() { + let totp = TOTP::new(Algorithm::SHA1, 6, 1, 1, String::from("TestSecret").into_bytes()); + assert_eq!(totp.get_secret_base32().as_str(), "KRSXG5CTMVRXEZLU"); + } + + #[test] + fn generates_token() { + let totp = TOTP::new(Algorithm::SHA1, 6, 1, 1, String::from("TestSecret").into_bytes()); + assert_eq!(totp.generate(1000).as_str(), "718996"); + } + + #[test] + fn generates_token_sha256() { + let totp = TOTP::new(Algorithm::SHA256, 6, 1, 1, String::from("TestSecret").into_bytes()); + assert_eq!(totp.generate(1000).as_str(), "423657"); + } + + #[test] + fn generates_token_sha512() { + let totp = TOTP::new(Algorithm::SHA512, 6, 1, 1, String::from("TestSecret").into_bytes()); + assert_eq!(totp.generate(1000).as_str(), "416767"); + } + + #[test] + fn checks_token() { + let totp = TOTP::new(Algorithm::SHA1, 6, 1, 1, String::from("TestSecret").into_bytes()); + assert!(totp.check("718996", 1000)); + } + + #[test] + fn checks_token_with_skew() { + let totp = TOTP::new(Algorithm::SHA1, 6, 1, 1, String::from("TestSecret").into_bytes()); + assert!(totp.check("527544", 2000) && totp.check("712039", 2000) && totp.check("714250", 2000)); + } + + #[test] + #[cfg(feature = "qr")] + fn generates_qr() { + use sha1::{Sha1, Digest}; + + let totp = TOTP::new(Algorithm::SHA1, 6, 1, 1, String::from("TestSecret").into_bytes()); + let qr = totp.get_qr("test_url", "totp-rs").unwrap(); + + // Create hash from image + let hash_digest = Sha1::digest(qr.as_bytes()); + assert_eq!(format!("{:x}", hash_digest).as_str(), "3abc0127e7a2b1013fb25c97ef14422c1fe9e878"); + } +} \ No newline at end of file From 2c12f476521728f0404dc13249d16eadf548b826 Mon Sep 17 00:00:00 2001 From: Mark Nijboer Date: Mon, 22 Jun 2020 16:16:05 +0200 Subject: [PATCH 2/3] * Changed version to 0.4.0 and updated docs --- Cargo.toml | 2 +- README.md | 6 ++---- src/lib.rs | 6 ++---- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cc05ed1..16f5bad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "totp-rs" -version = "0.3.2" +version = "0.4.0" authors = ["Cleo Rebert "] edition = "2018" readme = "README.md" diff --git a/README.md b/README.md index f372113..0e204f4 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,6 @@ You can then do something like: use std::time::SystemTime; use totp_rs::{Algorithm, TOTP}; -let username = "example".to_owned(); let totp = TOTP::new( Algorithm::SHA1, 6, @@ -25,7 +24,7 @@ let totp = TOTP::new( let time = SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH).unwrap() .as_secs(); -let url = totp.get_url(format!("account:{}", username), "my-org.com".to_owned()); +let url = totp.get_url("user@example.com", "my-org.com"); println!("{}", url); let token = totp.generate(time); println!("{}", token); @@ -43,7 +42,6 @@ You can then do something like: ```Rust use totp_rs::{Algorithm, TOTP}; -let username = "example".to_owned(); let totp = TOTP::new( Algorithm::SHA1, 6, @@ -51,6 +49,6 @@ let totp = TOTP::new( 30, "supersecret".to_owned().into_bytes(), ); -let code = totp.get_qr(format!("account:{}", username), "my-org.com".to_owned())?; +let code = totp.get_qr("user@example.com", "my-org.com")?; println!("{}", code); ``` diff --git a/src/lib.rs b/src/lib.rs index f63dc1e..1c7b54f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,7 +6,6 @@ //! use std::time::SystemTime; //! use totp_rs::{Algorithm, TOTP}; //! -//! let username = "example"; //! let totp = TOTP::new( //! Algorithm::SHA1, //! 6, @@ -17,7 +16,7 @@ //! let time = SystemTime::now() //! .duration_since(SystemTime::UNIX_EPOCH).unwrap() //! .as_secs(); -//! let url = totp.get_url(username, "my-org.com"); +//! let url = totp.get_url("user@example.com", "my-org.com"); //! println!("{}", url); //! let token = totp.generate(time); //! println!("{}", token); @@ -26,7 +25,6 @@ //! ```rust //! use totp_rs::{Algorithm, TOTP}; //! -//! let username = "example"; //! let totp = TOTP::new( //! Algorithm::SHA1, //! 6, @@ -34,7 +32,7 @@ //! 30, //! "supersecret".to_owned().into_bytes(), //! ); -//! let code = totp.get_qr(username, "my-org.com").unwrap(); +//! let code = totp.get_qr("user@example.com", "my-org.com").unwrap(); //! println!("{}", code); //! ``` From a80ed7f1d91f1ba0281f30b162fa9af38a6cd307 Mon Sep 17 00:00:00 2001 From: Mark Nijboer Date: Mon, 22 Jun 2020 17:02:02 +0200 Subject: [PATCH 3/3] * Changed version in README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0e204f4..c2b4d66 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ This library permits the creation of 2FA authentification tokens per TOTP, the v Add it to your `Cargo.toml`: ```toml [dependencies] -totp-rs = "~0.3" +totp-rs = "~0.4" ``` You can then do something like: ```Rust @@ -35,7 +35,7 @@ println!("{}", token); Add it to your `Cargo.toml`: ```toml [dependencies.totp-rs] -version = "~0.3" +version = "~0.4" features = ["qr"] ``` You can then do something like: