From 6bdbd5912fd4fc8ba77d848aac0cbc606ae4a8c4 Mon Sep 17 00:00:00 2001 From: timvisee Date: Tue, 3 Jan 2023 11:49:10 +0100 Subject: [PATCH 1/4] Add TOTP::new_unchecked methods --- src/lib.rs | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 56 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index cc71cc1..1708d14 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -265,7 +265,7 @@ impl TOTP { if account_name.contains(':') { return Err(TotpUrlError::AccountName(account_name)); } - Ok(TOTP { + Ok(Self::new_unchecked( algorithm, digits, skew, @@ -273,7 +273,38 @@ impl TOTP { secret, issuer, account_name, - }) + )) + } + + #[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. This is unchecked and does not check the `digits` and `secret` size + /// + /// # Description + /// * `secret`: expect a non-encoded value, to pass in base32 string use `Secret::Encoded(String)` + /// + /// ```rust + /// use totp_rs::{Secret, TOTP, Algorithm}; + /// let secret = Secret::Encoded("OBWGC2LOFVZXI4TJNZTS243FMNZGK5BNGEZDG".to_string()); + /// let totp = TOTP::new_unchecked(Algorithm::SHA1, 6, 1, 30, secret.to_bytes().unwrap(), None, "".to_string()).unwrap(); + /// ``` + pub fn new_unchecked( + algorithm: Algorithm, + digits: usize, + skew: u8, + step: u64, + secret: Vec, + issuer: Option, + account_name: String, + ) -> TOTP { + TOTP { + algorithm, + digits, + skew, + step, + secret, + issuer, + account_name, + } } #[cfg(not(feature = "otpauth"))] @@ -302,13 +333,34 @@ impl TOTP { ) -> Result { crate::rfc::assert_digits(&digits)?; crate::rfc::assert_secret_length(secret.as_ref())?; - Ok(TOTP { + Ok(Self::new_unchecked(algorithm, digits, skew, step, secret)) + } + + #[cfg(not(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. This is unchecked and does not check the `digits` and `secret` size + /// + /// # Description + /// * `secret`: expect a non-encoded value, to pass in base32 string use `Secret::Encoded(String)` + /// + /// ```rust + /// use totp_rs::{Secret, TOTP, Algorithm}; + /// let secret = Secret::Encoded("OBWGC2LOFVZXI4TJNZTS243FMNZGK5BNGEZDG".to_string()); + /// let totp = TOTP::new_unchecked(Algorithm::SHA1, 6, 1, 30, secret.to_bytes().unwrap()).unwrap(); + /// ``` + pub fn new_unchecked( + algorithm: Algorithm, + digits: usize, + skew: u8, + step: u64, + secret: Vec, + ) -> TOTP { + TOTP { algorithm, digits, skew, step, secret, - }) + } } /// Will create a new instance of TOTP from the given [Rfc6238](struct.Rfc6238.html) struct From 505569fa222096ef63dbb777033f655ea1e04eb8 Mon Sep 17 00:00:00 2001 From: timvisee Date: Tue, 3 Jan 2023 11:52:43 +0100 Subject: [PATCH 2/4] Remove issuer and account name colon check in TOTP::new method --- src/lib.rs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1708d14..94024f8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -242,12 +242,10 @@ impl TOTP { /// ``` /// * `digits`: MUST be between 6 & 8 /// * `secret`: Must have bitsize of at least 128 - /// * `account_name`: Must not contain `:` - /// * `issuer`: Must not contain `:` /// /// # Errors /// - /// Will return an error in case issuer or label contain the character ':' + /// Will return an error if the `digit` or `secret` size is invalid pub fn new( algorithm: Algorithm, digits: usize, @@ -259,12 +257,6 @@ impl TOTP { ) -> Result { crate::rfc::assert_digits(&digits)?; crate::rfc::assert_secret_length(secret.as_ref())?; - if issuer.is_some() && issuer.as_ref().unwrap().contains(':') { - return Err(TotpUrlError::Issuer(issuer.as_ref().unwrap().to_string())); - } - if account_name.contains(':') { - return Err(TotpUrlError::AccountName(account_name)); - } Ok(Self::new_unchecked( algorithm, digits, @@ -323,7 +315,7 @@ impl TOTP { /// /// # Errors /// - /// Will return an error in case issuer or label contain the character ':' + /// Will return an error if the `digit` or `secret` size is invalid pub fn new( algorithm: Algorithm, digits: usize, From 39e0d16d5b7612cd5fed5c4de8e96a27881b39ac Mon Sep 17 00:00:00 2001 From: timvisee Date: Tue, 3 Jan 2023 11:58:59 +0100 Subject: [PATCH 3/4] Add TOTP::from_url_unchecked variant with uses TOTP::new_unchecked --- src/lib.rs | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 94024f8..0df86c9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -442,6 +442,32 @@ impl TOTP { /// Generate a TOTP from the standard otpauth URL #[cfg(feature = "otpauth")] pub fn from_url>(url: S) -> Result { + let (algorithm, digits, skew, step, secret, issuer, account_name) = + Self::parts_from_url(url)?; + TOTP::new(algorithm, digits, skew, step, secret, issuer, account_name) + } + + /// Generate a TOTP from the standard otpauth URL, using `TOTP::new_unchecked` internally + #[cfg(feature = "otpauth")] + pub fn from_url_unchecked>(url: S) -> Result { + let (algorithm, digits, skew, step, secret, issuer, account_name) = + Self::parts_from_url(url)?; + Ok(TOTP::new_unchecked( + algorithm, + digits, + skew, + step, + secret, + issuer, + account_name, + )) + } + + /// Parse the TOTP parts from the standard otpauth URL + #[cfg(feature = "otpauth")] + fn parts_from_url>( + url: S, + ) -> Result<(Algorithm, usize, u8, u64, Vec, Option, String), TotpUrlError> { let url = Url::parse(url.as_ref()).map_err(TotpUrlError::Url)?; if url.scheme() != "otpauth" { return Err(TotpUrlError::Scheme(url.scheme().to_string())); @@ -521,7 +547,7 @@ impl TOTP { return Err(TotpUrlError::Secret("".to_string())); } - TOTP::new(algorithm, digits, 1, step, secret, issuer, account_name) + Ok((algorithm, digits, 1, step, secret, issuer, account_name)) } /// Will generate a standard URL used to automatically add TOTP auths. Usually used with qr codes From d293068353b898d1f546e4ee91dd2eef1f93979c Mon Sep 17 00:00:00 2001 From: timvisee Date: Tue, 3 Jan 2023 17:55:34 +0100 Subject: [PATCH 4/4] Add issue and account name colon check back, fix unchecked doctests --- src/lib.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 0df86c9..55ba5d5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -242,10 +242,12 @@ impl TOTP { /// ``` /// * `digits`: MUST be between 6 & 8 /// * `secret`: Must have bitsize of at least 128 + /// * `account_name`: Must not contain `:` + /// * `issuer`: Must not contain `:` /// /// # Errors /// - /// Will return an error if the `digit` or `secret` size is invalid + /// Will return an error if the `digit` or `secret` size is invalid or if `issuer` or `label` contain the character ':' pub fn new( algorithm: Algorithm, digits: usize, @@ -257,6 +259,12 @@ impl TOTP { ) -> Result { crate::rfc::assert_digits(&digits)?; crate::rfc::assert_secret_length(secret.as_ref())?; + if issuer.is_some() && issuer.as_ref().unwrap().contains(':') { + return Err(TotpUrlError::Issuer(issuer.as_ref().unwrap().to_string())); + } + if account_name.contains(':') { + return Err(TotpUrlError::AccountName(account_name)); + } Ok(Self::new_unchecked( algorithm, digits, @@ -277,7 +285,7 @@ impl TOTP { /// ```rust /// use totp_rs::{Secret, TOTP, Algorithm}; /// let secret = Secret::Encoded("OBWGC2LOFVZXI4TJNZTS243FMNZGK5BNGEZDG".to_string()); - /// let totp = TOTP::new_unchecked(Algorithm::SHA1, 6, 1, 30, secret.to_bytes().unwrap(), None, "".to_string()).unwrap(); + /// let totp = TOTP::new_unchecked(Algorithm::SHA1, 6, 1, 30, secret.to_bytes().unwrap(), None, "".to_string()); /// ``` pub fn new_unchecked( algorithm: Algorithm, @@ -337,7 +345,7 @@ impl TOTP { /// ```rust /// use totp_rs::{Secret, TOTP, Algorithm}; /// let secret = Secret::Encoded("OBWGC2LOFVZXI4TJNZTS243FMNZGK5BNGEZDG".to_string()); - /// let totp = TOTP::new_unchecked(Algorithm::SHA1, 6, 1, 30, secret.to_bytes().unwrap()).unwrap(); + /// let totp = TOTP::new_unchecked(Algorithm::SHA1, 6, 1, 30, secret.to_bytes().unwrap()); /// ``` pub fn new_unchecked( algorithm: Algorithm,