From 4d9e41ee68443bfbd60ae1b9c429940de367fe45 Mon Sep 17 00:00:00 2001 From: constantoine Date: Wed, 5 Oct 2022 11:45:21 +0200 Subject: [PATCH 1/2] Fix clippy warnings Signed-off-by: constantoine --- src/lib.rs | 20 ++++++++------------ src/rfc.rs | 2 +- src/secret.rs | 2 +- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 339f914..a8d2bfe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -54,7 +54,6 @@ mod url_error; pub use secret::{Secret, SecretParseError}; pub use url_error::TotpUrlError; pub use rfc::{Rfc6238, Rfc6238Error}; -use base32; use constant_time_eq::constant_time_eq; @@ -64,12 +63,10 @@ use serde::{Deserialize, Serialize}; use core::fmt; #[cfg(feature = "qr")] -use {base64, image::Luma, qrcodegen}; +use image::Luma; #[cfg(feature = "otpauth")] use url::{Host, Url}; -#[cfg(feature = "otpauth")] -use urlencoding; use hmac::Mac; use std::time::{SystemTime, SystemTimeError, UNIX_EPOCH}; @@ -185,7 +182,7 @@ impl Default for TOTP { #[cfg(all(feature = "gen_secret", feature = "otpauth"))] impl Default for TOTP { fn default() -> Self { - return TOTP::new(Algorithm::SHA1, 6, 1, 30, Secret::generate_secret().to_bytes().unwrap(), None, "".to_string()).unwrap() + TOTP::new(Algorithm::SHA1, 6, 1, 30, Secret::generate_secret().to_bytes().unwrap(), None, "".to_string()).unwrap() } } @@ -345,7 +342,7 @@ impl> TOTP { /// Generate a TOTP from the standard otpauth URL #[cfg(feature = "otpauth")] pub fn from_url>(url: S) -> Result>, TotpUrlError> { - let url = Url::parse(url.as_ref()).map_err(|err| TotpUrlError::Url(err))?; + let url = Url::parse(url.as_ref()).map_err(TotpUrlError::Url)?; if url.scheme() != "otpauth" { return Err(TotpUrlError::Scheme(url.scheme().to_string())); } @@ -363,7 +360,7 @@ impl> TOTP { let path = url.path().trim_start_matches('/'); if path.contains(':') { let parts = path.split_once(':').unwrap(); - issuer = Some(urlencoding::decode(parts.0.to_owned().as_str()).map_err(|_| TotpUrlError::IssuerDecoding(parts.0.to_owned().to_string()))?.to_string()); + issuer = Some(urlencoding::decode(parts.0.to_owned().as_str()).map_err(|_| TotpUrlError::IssuerDecoding(parts.0.to_owned()))?.to_string()); account_name = parts.1.trim_start_matches(':').to_owned(); } else { account_name = path.to_owned(); @@ -390,7 +387,7 @@ impl> TOTP { "secret" => { secret = base32::decode(base32::Alphabet::RFC4648 { padding: false }, value.as_ref()) - .ok_or(TotpUrlError::Secret(value.to_string()))?; + .ok_or_else(|| TotpUrlError::Secret(value.to_string()))?; } "issuer" => { let param_issuer = value.parse::().map_err(|_| TotpUrlError::Issuer(value.to_string()))?; @@ -416,20 +413,19 @@ impl> TOTP { /// Secret will be base 32'd without padding, as per RFC. #[cfg(feature = "otpauth")] pub fn get_url(&self) -> String { - let label: String; + let account_name: String = urlencoding::encode(self.account_name.as_str()).to_string(); + let mut label: String = format!("{}?", account_name); if self.issuer.is_some() { let issuer: String = urlencoding::encode(self.issuer.as_ref().unwrap().as_str()).to_string(); label = format!("{0}:{1}?issuer={0}&", issuer, account_name); - } else { - label = format!("{}?", account_name); } format!( "otpauth://totp/{}secret={}&digits={}&algorithm={}", label, self.get_secret_base32(), - self.digits.to_string(), + self.digits, self.algorithm, ) } diff --git a/src/rfc.rs b/src/rfc.rs index f5f763c..2bca3e4 100644 --- a/src/rfc.rs +++ b/src/rfc.rs @@ -324,7 +324,7 @@ mod tests { ) .unwrap(); let totp = TOTP::try_from(rfc); - assert!(!totp.is_err()); + assert!(totp.is_ok()); } #[test] diff --git a/src/secret.rs b/src/secret.rs index 1b2351d..609849f 100644 --- a/src/secret.rs +++ b/src/secret.rs @@ -107,7 +107,7 @@ impl PartialEq for Secret { #[cfg(feature = "gen_secret")] impl Default for Secret { fn default() -> Self { - return Secret::generate_secret() + Secret::generate_secret() } } From 0f0e7d603217b0202ff1c3dc528886a7e7551dcf Mon Sep 17 00:00:00 2001 From: constantoine Date: Wed, 5 Oct 2022 11:55:37 +0200 Subject: [PATCH 2/2] Cargo fmt Signed-off-by: constantoine --- examples/gen_secret.rs | 10 +- examples/rfc-6238.rs | 14 +- examples/secret.rs | 62 +++++---- examples/ttl.rs | 14 +- src/lib.rs | 300 +++++++++++++++++++++++++++++++++-------- src/rfc.rs | 46 +++---- src/secret.rs | 24 ++-- src/url_error.rs | 58 ++++++-- 8 files changed, 369 insertions(+), 159 deletions(-) diff --git a/examples/gen_secret.rs b/examples/gen_secret.rs index 527fc10..a536b20 100644 --- a/examples/gen_secret.rs +++ b/examples/gen_secret.rs @@ -1,9 +1,8 @@ #[cfg(all(feature = "gen_secret", feature = "otpauth"))] -use totp_rs::{Secret, TOTP, Algorithm}; +use totp_rs::{Algorithm, Secret, TOTP}; #[cfg(all(feature = "gen_secret", feature = "otpauth"))] -fn main () { - +fn main() { let secret = Secret::generate_secret(); let totp = TOTP::new( @@ -14,7 +13,8 @@ fn main () { secret.to_bytes().unwrap(), None, "account".to_string(), - ).unwrap(); + ) + .unwrap(); println!( "secret raw: {} ; secret base32 {} ; code: {}", @@ -25,4 +25,4 @@ fn main () { } #[cfg(not(all(feature = "gen_secret", feature = "otpauth")))] -fn main () {} \ No newline at end of file +fn main() {} diff --git a/examples/rfc-6238.rs b/examples/rfc-6238.rs index 4e3975c..0dc9912 100644 --- a/examples/rfc-6238.rs +++ b/examples/rfc-6238.rs @@ -1,10 +1,8 @@ use totp_rs::{Rfc6238, TOTP}; #[cfg(feature = "otpauth")] -fn main () { - let mut rfc = Rfc6238::with_defaults( - "totp-sercret-123" - ).unwrap(); +fn main() { + let mut rfc = Rfc6238::with_defaults("totp-sercret-123").unwrap(); // optional, set digits, issuer, account_name rfc.digits(8).unwrap(); @@ -18,10 +16,8 @@ fn main () { } #[cfg(not(feature = "otpauth"))] -fn main () { - let mut rfc = Rfc6238::with_defaults( - "totp-sercret-123" - ).unwrap(); +fn main() { + let mut rfc = Rfc6238::with_defaults("totp-sercret-123").unwrap(); // optional, set digits, issuer, account_name rfc.digits(8).unwrap(); @@ -30,4 +26,4 @@ fn main () { let totp = TOTP::from_rfc6238(rfc).unwrap(); let code = totp.generate_current().unwrap(); println!("code: {}", code); -} \ No newline at end of file +} diff --git a/examples/secret.rs b/examples/secret.rs index adf1eaa..a5bb2a7 100644 --- a/examples/secret.rs +++ b/examples/secret.rs @@ -1,7 +1,7 @@ -use totp_rs::{Secret, TOTP, Algorithm}; +use totp_rs::{Algorithm, Secret, TOTP}; #[cfg(feature = "otpauth")] -fn main () { +fn main() { // create TOTP from base32 secret let secret_b32 = Secret::Encoded(String::from("OBWGC2LOFVZXI4TJNZTS243FMNZGK5BNGEZDG")); let totp_b32 = TOTP::new( @@ -12,10 +12,18 @@ fn main () { secret_b32.to_bytes().unwrap(), Some("issuer".to_string()), "user-account".to_string(), - ).unwrap(); + ) + .unwrap(); - println!("base32 {} ; raw {}", secret_b32, secret_b32.to_raw().unwrap()); - println!("code from base32:\t{}", totp_b32.generate_current().unwrap()); + println!( + "base32 {} ; raw {}", + secret_b32, + secret_b32.to_raw().unwrap() + ); + println!( + "code from base32:\t{}", + totp_b32.generate_current().unwrap() + ); // create TOTP from raw binary value let secret = [ @@ -31,26 +39,31 @@ fn main () { secret_raw.to_bytes().unwrap(), Some("issuer".to_string()), "user-account".to_string(), - ).unwrap(); + ) + .unwrap(); println!("raw {} ; base32 {}", secret_raw, secret_raw.to_encoded()); - println!("code from raw secret:\t{}", totp_raw.generate_current().unwrap()); + println!( + "code from raw secret:\t{}", + totp_raw.generate_current().unwrap() + ); } #[cfg(not(feature = "otpauth"))] -fn main () { +fn main() { // create TOTP from base32 secret let secret_b32 = Secret::Encoded(String::from("OBWGC2LOFVZXI4TJNZTS243FMNZGK5BNGEZDG")); - let totp_b32 = TOTP::new( - Algorithm::SHA1, - 6, - 1, - 30, - secret_b32.to_bytes().unwrap(), - ).unwrap(); + let totp_b32 = TOTP::new(Algorithm::SHA1, 6, 1, 30, secret_b32.to_bytes().unwrap()).unwrap(); - println!("base32 {} ; raw {}", secret_b32, secret_b32.to_raw().unwrap()); - println!("code from base32:\t{}", totp_b32.generate_current().unwrap()); + println!( + "base32 {} ; raw {}", + secret_b32, + secret_b32.to_raw().unwrap() + ); + println!( + "code from base32:\t{}", + totp_b32.generate_current().unwrap() + ); // create TOTP from raw binary value let secret = [ @@ -58,14 +71,11 @@ fn main () { 0x63, 0x72, 0x65, 0x74, 0x2d, 0x31, 0x32, 0x33, ]; let secret_raw = Secret::Raw(secret.to_vec()); - let totp_raw = TOTP::new( - Algorithm::SHA1, - 6, - 1, - 30, - secret_raw.to_bytes().unwrap(), - ).unwrap(); + let totp_raw = TOTP::new(Algorithm::SHA1, 6, 1, 30, secret_raw.to_bytes().unwrap()).unwrap(); println!("raw {} ; base32 {}", secret_raw, secret_raw.to_encoded()); - println!("code from raw secret:\t{}", totp_raw.generate_current().unwrap()); -} \ No newline at end of file + println!( + "code from raw secret:\t{}", + totp_raw.generate_current().unwrap() + ); +} diff --git a/examples/ttl.rs b/examples/ttl.rs index ec2c1a8..3de5891 100644 --- a/examples/ttl.rs +++ b/examples/ttl.rs @@ -2,13 +2,7 @@ use totp_rs::{Algorithm, TOTP}; #[cfg(not(feature = "otpauth"))] fn main() { - let totp = TOTP::new( - Algorithm::SHA1, - 6, - 1, - 30, - "my-secret".to_string(), - ).unwrap(); + let totp = TOTP::new(Algorithm::SHA1, 6, 1, 30, "my-secret".to_string()).unwrap(); loop { println!( @@ -21,7 +15,6 @@ fn main() { } } - #[cfg(feature = "otpauth")] fn main() { let totp = TOTP::new( @@ -31,8 +24,9 @@ fn main() { 30, "my-secret".to_string(), Some("Github".to_string()), - "constantoine@github.com".to_string() - ).unwrap(); + "constantoine@github.com".to_string(), + ) + .unwrap(); loop { println!( diff --git a/src/lib.rs b/src/lib.rs index a8d2bfe..74b5c40 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,13 +47,13 @@ //! # } //! ``` -mod secret; mod rfc; +mod secret; mod url_error; +pub use rfc::{Rfc6238, Rfc6238Error}; pub use secret::{Secret, SecretParseError}; pub use url_error::TotpUrlError; -pub use rfc::{Rfc6238, Rfc6238Error}; use constant_time_eq::constant_time_eq; @@ -119,9 +119,7 @@ impl Algorithm { } fn system_time() -> Result { - let t = SystemTime::now() - .duration_since(UNIX_EPOCH)? - .as_secs(); + let t = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs(); Ok(t) } @@ -149,10 +147,10 @@ pub struct TOTP> { #[cfg(feature = "otpauth")] /// The "constantoine@github.com" part of "Github:constantoine@github.com". Must not contain a colon `:` /// For example, the name of your user's account. - pub account_name: String + pub account_name: String, } -impl > PartialEq for TOTP { +impl> PartialEq for TOTP { /// Will not check for issuer and account_name equality /// As they aren't taken in account for token generation/token checking fn eq(&self, other: &Self) -> bool { @@ -175,19 +173,34 @@ impl > PartialEq for TOTP { #[cfg(all(feature = "gen_secret", not(feature = "otpauth")))] impl Default for TOTP { fn default() -> Self { - return TOTP::new(Algorithm::SHA1, 6, 1, 30, Secret::generate_secret().to_bytes().unwrap()).unwrap() + return TOTP::new( + Algorithm::SHA1, + 6, + 1, + 30, + Secret::generate_secret().to_bytes().unwrap(), + ) + .unwrap(); } } #[cfg(all(feature = "gen_secret", feature = "otpauth"))] impl Default for TOTP { fn default() -> Self { - TOTP::new(Algorithm::SHA1, 6, 1, 30, Secret::generate_secret().to_bytes().unwrap(), None, "".to_string()).unwrap() + TOTP::new( + Algorithm::SHA1, + 6, + 1, + 30, + Secret::generate_secret().to_bytes().unwrap(), + None, + "".to_string(), + ) + .unwrap() } } impl> TOTP { - #[cfg(feature = "otpauth")] /// Will create a new instance of TOTP with given parameters. See [the doc](struct.TOTP.html#fields) for reference as to how to choose those values /// @@ -207,7 +220,15 @@ impl> TOTP { /// # Errors /// /// Will return an error in case issuer or label contain the character ':' - pub fn new(algorithm: Algorithm, digits: usize, skew: u8, step: u64, secret: T, issuer: Option, account_name: String) -> Result, TotpUrlError> { + pub fn new( + algorithm: Algorithm, + digits: usize, + skew: u8, + step: u64, + secret: T, + issuer: Option, + account_name: String, + ) -> Result, TotpUrlError> { crate::rfc::assert_digits(&digits)?; crate::rfc::assert_secret_length(secret.as_ref())?; if issuer.is_some() && issuer.as_ref().unwrap().contains(':') { @@ -244,7 +265,13 @@ impl> TOTP { /// # Errors /// /// Will return an error in case issuer or label contain the character ':' - pub fn new(algorithm: Algorithm, digits: usize,skew: u8, step: u64, secret: T) -> Result, TotpUrlError> { + pub fn new( + algorithm: Algorithm, + digits: usize, + skew: u8, + step: u64, + secret: T, + ) -> Result, TotpUrlError> { crate::rfc::assert_digits(&digits)?; crate::rfc::assert_secret_length(secret.as_ref())?; Ok(TOTP { @@ -277,7 +304,8 @@ impl> TOTP { pub fn generate(&self, time: u64) -> String { let result: &[u8] = &self.sign(time); let offset = (result.last().unwrap() & 15) as usize; - let result = u32::from_be_bytes(result[offset..offset + 4].try_into().unwrap()) & 0x7fff_ffff; + let result = + u32::from_be_bytes(result[offset..offset + 4].try_into().unwrap()) & 0x7fff_ffff; format!( "{1:00$}", self.digits, @@ -295,7 +323,7 @@ impl> TOTP { /// Returns the timestamp of the first second of the next step /// According to system time - pub fn next_step_current(&self)-> Result { + pub fn next_step_current(&self) -> Result { let t = system_time()?; Ok(self.next_step(t)) } @@ -360,13 +388,19 @@ impl> TOTP { let path = url.path().trim_start_matches('/'); if path.contains(':') { let parts = path.split_once(':').unwrap(); - issuer = Some(urlencoding::decode(parts.0.to_owned().as_str()).map_err(|_| TotpUrlError::IssuerDecoding(parts.0.to_owned()))?.to_string()); + issuer = Some( + urlencoding::decode(parts.0.to_owned().as_str()) + .map_err(|_| TotpUrlError::IssuerDecoding(parts.0.to_owned()))? + .to_string(), + ); account_name = parts.1.trim_start_matches(':').to_owned(); } else { account_name = path.to_owned(); } - account_name = urlencoding::decode(account_name.as_str()).map_err(|_| TotpUrlError::AccountName(account_name.to_string()))?.to_string(); + account_name = urlencoding::decode(account_name.as_str()) + .map_err(|_| TotpUrlError::AccountName(account_name.to_string()))? + .to_string(); for (key, value) in url.query_pairs() { match key.as_ref() { @@ -379,20 +413,31 @@ impl> TOTP { } } "digits" => { - digits = value.parse::().map_err(|_| TotpUrlError::Digits(value.to_string()))?; + digits = value + .parse::() + .map_err(|_| TotpUrlError::Digits(value.to_string()))?; } "period" => { - step = value.parse::().map_err(|_| TotpUrlError::Step(value.to_string()))?; + step = value + .parse::() + .map_err(|_| TotpUrlError::Step(value.to_string()))?; } "secret" => { - secret = - base32::decode(base32::Alphabet::RFC4648 { padding: false }, value.as_ref()) - .ok_or_else(|| TotpUrlError::Secret(value.to_string()))?; + secret = base32::decode( + base32::Alphabet::RFC4648 { padding: false }, + value.as_ref(), + ) + .ok_or_else(|| TotpUrlError::Secret(value.to_string()))?; } "issuer" => { - let param_issuer = value.parse::().map_err(|_| TotpUrlError::Issuer(value.to_string()))?; + let param_issuer = value + .parse::() + .map_err(|_| TotpUrlError::Issuer(value.to_string()))?; if issuer.is_some() && param_issuer.as_str() != issuer.as_ref().unwrap() { - return Err(TotpUrlError::IssuerMistmatch(issuer.as_ref().unwrap().to_string(), param_issuer)); + return Err(TotpUrlError::IssuerMistmatch( + issuer.as_ref().unwrap().to_string(), + param_issuer, + )); } issuer = Some(param_issuer); } @@ -413,11 +458,11 @@ impl> TOTP { /// Secret will be base 32'd without padding, as per RFC. #[cfg(feature = "otpauth")] pub fn get_url(&self) -> String { - let account_name: String = urlencoding::encode(self.account_name.as_str()).to_string(); let mut label: String = format!("{}?", account_name); if self.issuer.is_some() { - let issuer: String = urlencoding::encode(self.issuer.as_ref().unwrap().as_str()).to_string(); + let issuer: String = + urlencoding::encode(self.issuer.as_ref().unwrap().as_str()).to_string(); label = format!("{0}:{1}?issuer={0}&", issuer, account_name); } @@ -441,7 +486,8 @@ impl> TOTP { // Draw the border for x in 0..image_size { for y in 0..image_size { - if (y < 8*4 || y >= image_size - 8*4) || (x < 8*4 || x >= image_size - 8*4) { + if (y < 8 * 4 || y >= image_size - 8 * 4) || (x < 8 * 4 || x >= image_size - 8 * 4) + { canvas.put_pixel(x, y, Luma([255])); } } @@ -457,17 +503,13 @@ impl> TOTP { // Multiply coordinates by width of pixels // And take into account the 8*4 padding on top and left side - let x_start = x_qr * 8 + 8*4; - let y_start = y_qr * 8 + 8*4; + let x_start = x_qr * 8 + 8 * 4; + let y_start = y_qr * 8 + 8 * 4; // Draw a 8-pixels-wide square for x_img in x_start..x_start + 8 { for y_img in y_start..y_start + 8 { - canvas.put_pixel( - x_img, - y_img, - Luma([val]), - ); + canvas.put_pixel(x_img, y_img, Luma([val])); } } } @@ -529,7 +571,15 @@ mod tests { #[test] #[cfg(feature = "otpauth")] fn new_wrong_issuer() { - let totp = TOTP::new(Algorithm::SHA1, 6, 1, 1, "TestSecretSuperSecret", Some("Github:".to_string()), "constantoine@github.com".to_string()); + let totp = TOTP::new( + Algorithm::SHA1, + 6, + 1, + 1, + "TestSecretSuperSecret", + Some("Github:".to_string()), + "constantoine@github.com".to_string(), + ); assert!(totp.is_err()); assert!(matches!(totp.unwrap_err(), TotpUrlError::Issuer(_))); } @@ -537,7 +587,15 @@ mod tests { #[test] #[cfg(feature = "otpauth")] fn new_wrong_account_name() { - let totp = TOTP::new(Algorithm::SHA1, 6, 1, 1, "TestSecretSuperSecret", Some("Github".to_string()), "constantoine:github.com".to_string()); + let totp = TOTP::new( + Algorithm::SHA1, + 6, + 1, + 1, + "TestSecretSuperSecret", + Some("Github".to_string()), + "constantoine:github.com".to_string(), + ); assert!(totp.is_err()); assert!(matches!(totp.unwrap_err(), TotpUrlError::AccountName(_))); } @@ -545,7 +603,15 @@ mod tests { #[test] #[cfg(feature = "otpauth")] fn new_wrong_account_name_no_issuer() { - let totp = TOTP::new(Algorithm::SHA1, 6, 1, 1, "TestSecretSuperSecret", None, "constantoine:github.com".to_string()); + let totp = TOTP::new( + Algorithm::SHA1, + 6, + 1, + 1, + "TestSecretSuperSecret", + None, + "constantoine:github.com".to_string(), + ); assert!(totp.is_err()); assert!(matches!(totp.unwrap_err(), TotpUrlError::AccountName(_))); } @@ -553,8 +619,26 @@ mod tests { #[test] #[cfg(feature = "otpauth")] fn comparison_ok() { - let reference = TOTP::new(Algorithm::SHA1, 6, 1, 1, "TestSecretSuperSecret", Some("Github".to_string()), "constantoine@github.com".to_string()).unwrap(); - let test = TOTP::new(Algorithm::SHA1, 6, 1, 1, "TestSecretSuperSecret", Some("Github".to_string()), "constantoine@github.com".to_string()).unwrap(); + let reference = TOTP::new( + Algorithm::SHA1, + 6, + 1, + 1, + "TestSecretSuperSecret", + Some("Github".to_string()), + "constantoine@github.com".to_string(), + ) + .unwrap(); + let test = TOTP::new( + Algorithm::SHA1, + 6, + 1, + 1, + "TestSecretSuperSecret", + Some("Github".to_string()), + "constantoine@github.com".to_string(), + ) + .unwrap(); assert_eq!(reference, test); } @@ -601,7 +685,16 @@ mod tests { #[test] #[cfg(feature = "otpauth")] fn url_for_secret_matches_sha1_without_issuer() { - let totp = TOTP::new(Algorithm::SHA1, 6, 1, 1, "TestSecretSuperSecret", None, "constantoine@github.com".to_string()).unwrap(); + let totp = TOTP::new( + Algorithm::SHA1, + 6, + 1, + 1, + "TestSecretSuperSecret", + None, + "constantoine@github.com".to_string(), + ) + .unwrap(); let url = totp.get_url(); assert_eq!(url.as_str(), "otpauth://totp/constantoine%40github.com?secret=KRSXG5CTMVRXEZLUKN2XAZLSKNSWG4TFOQ&digits=6&algorithm=SHA1"); } @@ -609,7 +702,16 @@ mod tests { #[test] #[cfg(feature = "otpauth")] fn url_for_secret_matches_sha1() { - let totp = TOTP::new(Algorithm::SHA1, 6, 1, 1, "TestSecretSuperSecret", Some("Github".to_string()), "constantoine@github.com".to_string()).unwrap(); + let totp = TOTP::new( + Algorithm::SHA1, + 6, + 1, + 1, + "TestSecretSuperSecret", + Some("Github".to_string()), + "constantoine@github.com".to_string(), + ) + .unwrap(); let url = totp.get_url(); assert_eq!(url.as_str(), "otpauth://totp/Github:constantoine%40github.com?issuer=Github&secret=KRSXG5CTMVRXEZLUKN2XAZLSKNSWG4TFOQ&digits=6&algorithm=SHA1"); } @@ -617,7 +719,16 @@ mod tests { #[test] #[cfg(feature = "otpauth")] fn url_for_secret_matches_sha256() { - let totp = TOTP::new(Algorithm::SHA256, 6, 1, 1, "TestSecretSuperSecret", Some("Github".to_string()), "constantoine@github.com".to_string()).unwrap(); + let totp = TOTP::new( + Algorithm::SHA256, + 6, + 1, + 1, + "TestSecretSuperSecret", + Some("Github".to_string()), + "constantoine@github.com".to_string(), + ) + .unwrap(); let url = totp.get_url(); assert_eq!(url.as_str(), "otpauth://totp/Github:constantoine%40github.com?issuer=Github&secret=KRSXG5CTMVRXEZLUKN2XAZLSKNSWG4TFOQ&digits=6&algorithm=SHA256"); } @@ -625,7 +736,16 @@ mod tests { #[test] #[cfg(feature = "otpauth")] fn url_for_secret_matches_sha512() { - let totp = TOTP::new(Algorithm::SHA512, 6, 1, 1, "TestSecretSuperSecret", Some("Github".to_string()), "constantoine@github.com".to_string()).unwrap(); + let totp = TOTP::new( + Algorithm::SHA512, + 6, + 1, + 1, + "TestSecretSuperSecret", + Some("Github".to_string()), + "constantoine@github.com".to_string(), + ) + .unwrap(); let url = totp.get_url(); assert_eq!(url.as_str(), "otpauth://totp/Github:constantoine%40github.com?issuer=Github&secret=KRSXG5CTMVRXEZLUKN2XAZLSKNSWG4TFOQ&digits=6&algorithm=SHA512"); } @@ -634,7 +754,10 @@ mod tests { #[cfg(not(feature = "otpauth"))] fn returns_base32() { let totp = TOTP::new(Algorithm::SHA1, 6, 1, 1, "TestSecretSuperSecret").unwrap(); - assert_eq!(totp.get_secret_base32().as_str(), "KRSXG5CTMVRXEZLUKN2XAZLSKNSWG4TFOQ"); + assert_eq!( + totp.get_secret_base32().as_str(), + "KRSXG5CTMVRXEZLUKN2XAZLSKNSWG4TFOQ" + ); } #[test] @@ -649,9 +772,13 @@ mod tests { fn generate_token_current() { let totp = TOTP::new(Algorithm::SHA1, 6, 1, 1, "TestSecretSuperSecret").unwrap(); let time = SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH).unwrap() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap() .as_secs(); - assert_eq!(totp.generate(time).as_str(), totp.generate_current().unwrap()); + assert_eq!( + totp.generate(time).as_str(), + totp.generate_current().unwrap() + ); } #[test] @@ -679,7 +806,9 @@ mod tests { #[cfg(not(feature = "otpauth"))] fn checks_token_current() { let totp = TOTP::new(Algorithm::SHA1, 6, 0, 1, "TestSecretSuperSecret").unwrap(); - assert!(totp.check_current(&totp.generate_current().unwrap()).unwrap()); + assert!(totp + .check_current(&totp.generate_current().unwrap()) + .unwrap()); assert!(!totp.check_current("bogus").unwrap()); } @@ -714,16 +843,28 @@ mod tests { fn from_url_err() { assert!(TOTP::>::from_url("otpauth://hotp/123").is_err()); assert!(TOTP::>::from_url("otpauth://totp/GitHub:test").is_err()); - assert!(TOTP::>::from_url("otpauth://totp/GitHub:test:?secret=ABC&digits=8&period=60&algorithm=SHA256").is_err()); + assert!(TOTP::>::from_url( + "otpauth://totp/GitHub:test:?secret=ABC&digits=8&period=60&algorithm=SHA256" + ) + .is_err()); assert!(TOTP::>::from_url("otpauth://totp/Github:constantoine%40github.com?issuer=GitHub&secret=KRSXG5CTMVRXEZLUKN2XAZLSKNSWG4TFOQ&digits=6&algorithm=SHA1").is_err()) - } #[test] #[cfg(feature = "otpauth")] fn from_url_default() { - let totp = TOTP::>::from_url("otpauth://totp/GitHub:test?secret=KRSXG5CTMVRXEZLUKN2XAZLSKNSWG4TFOQ").unwrap(); - assert_eq!(totp.secret, base32::decode(base32::Alphabet::RFC4648 { padding: false }, "KRSXG5CTMVRXEZLUKN2XAZLSKNSWG4TFOQ").unwrap()); + let totp = TOTP::>::from_url( + "otpauth://totp/GitHub:test?secret=KRSXG5CTMVRXEZLUKN2XAZLSKNSWG4TFOQ", + ) + .unwrap(); + assert_eq!( + totp.secret, + base32::decode( + base32::Alphabet::RFC4648 { padding: false }, + "KRSXG5CTMVRXEZLUKN2XAZLSKNSWG4TFOQ" + ) + .unwrap() + ); assert_eq!(totp.algorithm, Algorithm::SHA1); assert_eq!(totp.digits, 6); assert_eq!(totp.skew, 1); @@ -734,7 +875,14 @@ mod tests { #[cfg(feature = "otpauth")] fn from_url_query() { let totp = TOTP::>::from_url("otpauth://totp/GitHub:test?secret=KRSXG5CTMVRXEZLUKN2XAZLSKNSWG4TFOQ&digits=8&period=60&algorithm=SHA256").unwrap(); - assert_eq!(totp.secret, base32::decode(base32::Alphabet::RFC4648 { padding: false }, "KRSXG5CTMVRXEZLUKN2XAZLSKNSWG4TFOQ").unwrap()); + assert_eq!( + totp.secret, + base32::decode( + base32::Alphabet::RFC4648 { padding: false }, + "KRSXG5CTMVRXEZLUKN2XAZLSKNSWG4TFOQ" + ) + .unwrap() + ); assert_eq!(totp.algorithm, Algorithm::SHA256); assert_eq!(totp.digits, 8); assert_eq!(totp.skew, 1); @@ -745,7 +893,16 @@ mod tests { #[cfg(feature = "otpauth")] fn from_url_to_url() { let totp = TOTP::>::from_url("otpauth://totp/Github:constantoine%40github.com?issuer=Github&secret=KRSXG5CTMVRXEZLUKN2XAZLSKNSWG4TFOQ&digits=6&algorithm=SHA1").unwrap(); - let totp_bis = TOTP::new(Algorithm::SHA1, 6, 1, 1, "TestSecretSuperSecret", Some("Github".to_string()), "constantoine@github.com".to_string()).unwrap(); + let totp_bis = TOTP::new( + Algorithm::SHA1, + 6, + 1, + 1, + "TestSecretSuperSecret", + Some("Github".to_string()), + "constantoine@github.com".to_string(), + ) + .unwrap(); assert_eq!(totp.get_url(), totp_bis.get_url()); } @@ -753,7 +910,16 @@ mod tests { #[cfg(feature = "otpauth")] fn from_url_issuer_special() { let totp = TOTP::>::from_url("otpauth://totp/Github%40:constantoine%40github.com?issuer=Github%40&secret=KRSXG5CTMVRXEZLUKN2XAZLSKNSWG4TFOQ&digits=6&algorithm=SHA1").unwrap(); - let totp_bis = TOTP::new(Algorithm::SHA1, 6, 1, 1, "TestSecretSuperSecret", Some("Github@".to_string()), "constantoine@github.com".to_string()).unwrap(); + let totp_bis = TOTP::new( + Algorithm::SHA1, + 6, + 1, + 1, + "TestSecretSuperSecret", + Some("Github@".to_string()), + "constantoine@github.com".to_string(), + ) + .unwrap(); assert_eq!(totp.get_url(), totp_bis.get_url()); assert_eq!(totp.issuer.unwrap(), "Github@"); } @@ -762,7 +928,14 @@ mod tests { #[cfg(feature = "otpauth")] fn from_url_query_issuer() { let totp = TOTP::>::from_url("otpauth://totp/GitHub:test?issuer=GitHub&secret=KRSXG5CTMVRXEZLUKN2XAZLSKNSWG4TFOQ&digits=8&period=60&algorithm=SHA256").unwrap(); - assert_eq!(totp.secret, base32::decode(base32::Alphabet::RFC4648 { padding: false }, "KRSXG5CTMVRXEZLUKN2XAZLSKNSWG4TFOQ").unwrap()); + assert_eq!( + totp.secret, + base32::decode( + base32::Alphabet::RFC4648 { padding: false }, + "KRSXG5CTMVRXEZLUKN2XAZLSKNSWG4TFOQ" + ) + .unwrap() + ); assert_eq!(totp.algorithm, Algorithm::SHA256); assert_eq!(totp.digits, 8); assert_eq!(totp.skew, 1); @@ -775,7 +948,10 @@ mod tests { fn from_url_query_different_issuers() { let totp = TOTP::>::from_url("otpauth://totp/GitHub:test?issuer=Gitlab&secret=KRSXG5CTMVRXEZLUKN2XAZLSKNSWG4TFOQ&digits=8&period=60&algorithm=SHA256"); assert!(totp.is_err()); - assert!(matches!(totp.unwrap_err(), TotpUrlError::IssuerMistmatch(_, _))); + assert!(matches!( + totp.unwrap_err(), + TotpUrlError::IssuerMistmatch(_, _) + )); } #[test] @@ -783,9 +959,19 @@ mod tests { fn generates_qr() { use sha2::{Digest, Sha512}; - let totp = TOTP::new(Algorithm::SHA1, 6, 1, 1, "TestSecretSuperSecret", Some("Github".to_string()), "constantoine@github.com".to_string()).unwrap(); + let totp = TOTP::new( + Algorithm::SHA1, + 6, + 1, + 1, + "TestSecretSuperSecret", + Some("Github".to_string()), + "constantoine@github.com".to_string(), + ) + .unwrap(); let url = totp.get_url(); - let qr = qrcodegen::QrCode::encode_text(&url, qrcodegen::QrCodeEcc::Medium).expect("could not generate qr"); + let qr = qrcodegen::QrCode::encode_text(&url, qrcodegen::QrCodeEcc::Medium) + .expect("could not generate qr"); let data = totp.get_qr_draw_canvas(qr).into_raw(); // Create hash from image diff --git a/src/rfc.rs b/src/rfc.rs index 2bca3e4..94dda8b 100644 --- a/src/rfc.rs +++ b/src/rfc.rs @@ -103,7 +103,7 @@ impl> Rfc6238 { ) -> Result, Rfc6238Error> { assert_digits(&digits)?; assert_secret_length(secret.as_ref())?; - + Ok(Rfc6238 { algorithm: Algorithm::SHA1, digits, @@ -115,13 +115,10 @@ impl> Rfc6238 { }) } #[cfg(not(feature = "otpauth"))] - pub fn new( - digits: usize, - secret: T, - ) -> Result, Rfc6238Error> { + pub fn new(digits: usize, secret: T) -> Result, Rfc6238Error> { assert_digits(&digits)?; assert_secret_length(secret.as_ref())?; - + Ok(Rfc6238 { algorithm: Algorithm::SHA1, digits, @@ -150,7 +147,7 @@ impl> Rfc6238 { } /// Set the `digits` - pub fn digits(&mut self, value:usize) -> Result<(), Rfc6238Error> { + pub fn digits(&mut self, value: usize) -> Result<(), Rfc6238Error> { assert_digits(&value)?; self.digits = value; Ok(()) @@ -175,13 +172,7 @@ impl> TryFrom> for TOTP { /// Try to create a [TOTP](struct.TOTP.html) from a [Rfc6238](struct.Rfc6238.html) config fn try_from(rfc: Rfc6238) -> Result { - TOTP::new( - rfc.algorithm, - rfc.digits, - rfc.skew, - rfc.step, - rfc.secret, - ) + TOTP::new(rfc.algorithm, rfc.digits, rfc.skew, rfc.step, rfc.secret) } } @@ -198,7 +189,7 @@ impl> TryFrom> for TOTP { rfc.step, rfc.secret, rfc.issuer, - rfc.account_name + rfc.account_name, ) } } @@ -228,10 +219,7 @@ mod tests { #[cfg(not(feature = "otpauth"))] fn new_rfc_digits() { for x in 0..=20 { - let rfc = Rfc6238::new( - x, - GOOD_SECRET.to_string(), - ); + let rfc = Rfc6238::new(x, GOOD_SECRET.to_string()); if !(6..=8).contains(&x) { assert!(rfc.is_err()); assert!(matches!(rfc.unwrap_err(), Rfc6238Error::InvalidDigits(_))); @@ -247,16 +235,16 @@ mod tests { let mut secret = String::from(""); for _ in 0..=20 { secret = format!("{}{}", secret, "0"); - let rfc = Rfc6238::new( - 6, - secret.clone(), - ); + let rfc = Rfc6238::new(6, secret.clone()); let rfc_default = Rfc6238::with_defaults(secret.clone()); if secret.len() < 16 { assert!(rfc.is_err()); assert!(matches!(rfc.unwrap_err(), Rfc6238Error::SecretTooSmall(_))); assert!(rfc_default.is_err()); - assert!(matches!(rfc_default.unwrap_err(), Rfc6238Error::SecretTooSmall(_))); + assert!(matches!( + rfc_default.unwrap_err(), + Rfc6238Error::SecretTooSmall(_) + )); } else { assert!(rfc.is_ok()); assert!(rfc_default.is_ok()); @@ -267,11 +255,7 @@ mod tests { #[test] #[cfg(not(feature = "otpauth"))] fn rfc_to_totp_ok() { - let rfc = Rfc6238::new( - 8, - GOOD_SECRET.to_string(), - ) - .unwrap(); + let rfc = Rfc6238::new(8, GOOD_SECRET.to_string()).unwrap(); let totp = TOTP::try_from(rfc); assert!(totp.is_ok()); let otp = totp.unwrap(); @@ -286,7 +270,9 @@ mod tests { #[cfg(not(feature = "otpauth"))] fn rfc_to_totp_ok_2() { let rfc = Rfc6238::with_defaults( - Secret::Encoded("KRSXG5CTMVRXEZLUKN2XAZLSKNSWG4TFOQ".to_string()).to_bytes().unwrap(), + Secret::Encoded("KRSXG5CTMVRXEZLUKN2XAZLSKNSWG4TFOQ".to_string()) + .to_bytes() + .unwrap(), ) .unwrap(); let totp = TOTP::try_from(rfc); diff --git a/src/secret.rs b/src/secret.rs index 609849f..115566e 100644 --- a/src/secret.rs +++ b/src/secret.rs @@ -40,7 +40,7 @@ //! //! println!("code from base32:\t{}", totp_b32.generate_current().unwrap()); //! # } -//! +//! //! ``` //! - Create a TOTP from a Generated Secret //! ``` @@ -77,8 +77,8 @@ //! # } //! ``` -use std::string::FromUtf8Error; use base32::{self, Alphabet}; +use std::string::FromUtf8Error; use constant_time_eq::constant_time_eq; @@ -112,7 +112,6 @@ impl Default for Secret { } impl Secret { - /// Get the inner String value as a Vec of bytes pub fn to_bytes(&self) -> Result, SecretParseError> { match self { @@ -138,10 +137,9 @@ impl Secret { /// Try to transforms a `Secret::Raw` into a `Secret::Encoded` pub fn to_encoded(&self) -> Self { match self { - Secret::Raw(s) => Secret::Encoded(base32::encode( - Alphabet::RFC4648 { padding: false }, - s, - )), + Secret::Raw(s) => { + Secret::Encoded(base32::encode(Alphabet::RFC4648 { padding: false }, s)) + } Secret::Encoded(_) => self.clone(), } } @@ -175,7 +173,7 @@ impl std::fmt::Display for Secret { s = format!("{}{:02x}", &s, &b); } write!(f, "{}", s) - }, + } Secret::Encoded(s) => write!(f, "{}", s), } } @@ -218,8 +216,14 @@ mod tests { #[test] fn secret_as_bytes() { let base32_str = String::from(BASE32); - assert_eq!(Secret::Raw(BYTES.to_vec()).to_bytes().unwrap(), BYTES.to_vec()); - assert_eq!(Secret::Encoded(base32_str).to_bytes().unwrap(), BYTES.to_vec()); + assert_eq!( + Secret::Raw(BYTES.to_vec()).to_bytes().unwrap(), + BYTES.to_vec() + ); + assert_eq!( + Secret::Encoded(base32_str).to_bytes().unwrap(), + BYTES.to_vec() + ); } #[test] diff --git a/src/url_error.rs b/src/url_error.rs index 0161587..76ef73f 100644 --- a/src/url_error.rs +++ b/src/url_error.rs @@ -117,25 +117,37 @@ mod tests { #[test] fn account_name() { let error = TotpUrlError::AccountName("Laziz:".to_string()); - assert_eq!(error.to_string(), "Account Name can't contain a colon. \"Laziz:\" contains a colon") + assert_eq!( + error.to_string(), + "Account Name can't contain a colon. \"Laziz:\" contains a colon" + ) } #[test] fn account_name_decoding() { let error = TotpUrlError::AccountNameDecoding("Laz&iz".to_string()); - assert_eq!(error.to_string(), "Couldn't URL decode \"Laz&iz\"".to_string()) + assert_eq!( + error.to_string(), + "Couldn't URL decode \"Laz&iz\"".to_string() + ) } #[test] fn algorithm() { let error = TotpUrlError::Algorithm("SIKE".to_string()); - assert_eq!(error.to_string(), "Algorithm can only be SHA1, SHA256 or SHA512, not \"SIKE\"".to_string()) + assert_eq!( + error.to_string(), + "Algorithm can only be SHA1, SHA256 or SHA512, not \"SIKE\"".to_string() + ) } #[test] fn digits() { let error = TotpUrlError::Digits("six".to_string()); - assert_eq!(error.to_string(), "Could not parse \"six\" as a number.".to_string()) + assert_eq!( + error.to_string(), + "Could not parse \"six\" as a number.".to_string() + ) } #[test] @@ -147,19 +159,28 @@ mod tests { #[test] fn host() { let error = TotpUrlError::Host("hotp".to_string()); - assert_eq!(error.to_string(), "Host should be totp, not \"hotp\"".to_string()) + assert_eq!( + error.to_string(), + "Host should be totp, not \"hotp\"".to_string() + ) } #[test] fn issuer() { let error = TotpUrlError::Issuer("Iss:uer".to_string()); - assert_eq!(error.to_string(), "Issuer can't contain a colon. \"Iss:uer\" contains a colon".to_string()) + assert_eq!( + error.to_string(), + "Issuer can't contain a colon. \"Iss:uer\" contains a colon".to_string() + ) } #[test] fn issuer_decoding() { let error = TotpUrlError::IssuerDecoding("iss&uer".to_string()); - assert_eq!(error.to_string(), "Couldn't URL decode \"iss&uer\"".to_string()) + assert_eq!( + error.to_string(), + "Couldn't URL decode \"iss&uer\"".to_string() + ) } #[test] @@ -171,25 +192,38 @@ mod tests { #[test] fn scheme() { let error = TotpUrlError::Scheme("https".to_string()); - assert_eq!(error.to_string(), "Scheme should be otpauth, not \"https\"".to_string()) + assert_eq!( + error.to_string(), + "Scheme should be otpauth, not \"https\"".to_string() + ) } #[test] fn secret() { let error = TotpUrlError::Secret("YoLo".to_string()); - assert_eq!(error.to_string(), "Secret \"YoLo\" is not a valid non-padded base32 string".to_string()) + assert_eq!( + error.to_string(), + "Secret \"YoLo\" is not a valid non-padded base32 string".to_string() + ) } #[test] fn secret_size() { let error = TotpUrlError::SecretSize(112); - assert_eq!(error.to_string(), "The length of the shared secret MUST be at least 128 bits. 112 bits is not enough".to_string()) + assert_eq!( + error.to_string(), + "The length of the shared secret MUST be at least 128 bits. 112 bits is not enough" + .to_string() + ) } #[test] #[cfg(feature = "otpauth")] fn step() { let error = TotpUrlError::Url(url::ParseError::EmptyHost); - assert_eq!(error.to_string(), "Error parsing URL: empty host".to_string()) + assert_eq!( + error.to_string(), + "Error parsing URL: empty host".to_string() + ) } -} \ No newline at end of file +}