From d07f6763c795fcef13c857db6532e021c5511f74 Mon Sep 17 00:00:00 2001 From: Tejas Mehta Date: Sun, 30 Jan 2022 23:55:37 -0500 Subject: [PATCH 1/4] base wasm refactoring for conditional imports based on compile targer --- Cargo.toml | 3 ++ src/hotp.rs | 7 +++++ src/lib.rs | 5 +++- src/otp_result.rs | 7 +++++ src/totp.rs | 8 +++++ src/util.rs | 6 +++- src/wasm_utils.rs | 75 +++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 109 insertions(+), 2 deletions(-) create mode 100644 src/wasm_utils.rs diff --git a/Cargo.toml b/Cargo.toml index 2da6d41..92121df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,3 +16,6 @@ sha-1 = "0.10.0" sha2 = "0.10.1" base32 = "0.4.0" url = "2.2.2" + +[target.'cfg(target_arch = "wasm32")'.dependencies] +wasm-bindgen = "0.2" \ No newline at end of file diff --git a/src/hotp.rs b/src/hotp.rs index 22bda46..38c1cfc 100644 --- a/src/hotp.rs +++ b/src/hotp.rs @@ -3,6 +3,9 @@ use crate::otp_result::OTPResult; use crate::util::{base32_decode, get_code, hash_generic, MacDigest}; +#[cfg(target_arch = "wasm32")] +use wasm_bindgen::prelude::*; + /// A HOTP Generator /// /// Follows the specification listed in [RFC4226]. Needs a secret and @@ -18,6 +21,7 @@ use crate::util::{base32_decode, get_code, hash_generic, MacDigest}; /// /// [RFC4226]: https://datatracker.ietf.org/doc/html/rfc4226 +#[cfg_attr(target_arch = "wasm32", wasm_bindgen)] #[derive(Debug, Clone, Hash)] pub struct HOTP { /// The secret key used in the HMAC process. @@ -33,6 +37,7 @@ pub struct HOTP { } /// All initializer implementations for the [`HOTP`] struct. +#[cfg_attr(target_arch = "wasm32", wasm_bindgen)] impl HOTP { /// Creates a new HOTP instance with a byte-array representation /// of the secret and specified digit count. @@ -87,6 +92,7 @@ impl HOTP { } /// All getters for the ['HOTP'] struct +#[cfg_attr(target_arch = "wasm32", wasm_bindgen)] impl HOTP { /// Gets the number of digits of the code. pub fn get_digits(&self) -> u32 { @@ -95,6 +101,7 @@ impl HOTP { } /// All otp generation methods for the [`HOTP`] struct. +#[cfg_attr(target_arch = "wasm32", wasm_bindgen)] impl HOTP { /// Generates and returns the HOTP value. /// diff --git a/src/lib.rs b/src/lib.rs index 6c3a16b..e694643 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -80,4 +80,7 @@ pub mod hotp; pub mod totp; pub mod util; -pub mod otp_result; \ No newline at end of file +pub mod otp_result; + +#[cfg(target_arch = "wasm32")] +pub mod wasm_utils; \ No newline at end of file diff --git a/src/otp_result.rs b/src/otp_result.rs index dcb6be4..b2f07c5 100644 --- a/src/otp_result.rs +++ b/src/otp_result.rs @@ -1,6 +1,9 @@ use std::fmt; use std::fmt::Formatter; +#[cfg(target_arch = "wasm32")] +use wasm_bindgen::prelude::*; + /// A convenience struct to hold the result of a [`HOTP`] or [`TOTP`] /// generation. /// @@ -13,12 +16,14 @@ use std::fmt::Formatter; /// Returned as a result of either [`HOTP::get_otp`], [`TOTP::get_otp`] /// or [`TOTP::get_otp_with_custom_time_start`]. #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen)] pub struct OTPResult { digits: u32, code: u32, } /// Constructors for the [`OTPResult`] struct. +#[cfg_attr(target_arch = "wasm32", wasm_bindgen)] impl OTPResult { /// Creates a new instance with the provided digit count and OTP code. pub fn new(digits: u32, code: u32 ) -> Self { @@ -27,6 +32,7 @@ impl OTPResult { } /// Getters for the [`OTPResult`] struct. +#[cfg_attr(target_arch = "wasm32", wasm_bindgen)] impl OTPResult { /// Gets the digit count given to the struct on creation. /// @@ -35,6 +41,7 @@ impl OTPResult { } /// Convenience code getters for the [`OTPResult`] struct +#[cfg_attr(target_arch = "wasm32", wasm_bindgen)] impl OTPResult { /// Returns the OTP as a formatted string of length [`OTPResult.digits`]. /// diff --git a/src/totp.rs b/src/totp.rs index d9de267..7ef482c 100644 --- a/src/totp.rs +++ b/src/totp.rs @@ -1,6 +1,9 @@ use crate::otp_result::OTPResult; use crate::util::{base32_decode, get_code, hash_generic, MacDigest}; +#[cfg(target_arch = "wasm32")] +use wasm_bindgen::prelude::*; + /// A TOTP generator /// /// Follows the specification listed in [RFC6238]. Needs a secret, @@ -16,6 +19,7 @@ use crate::util::{base32_decode, get_code, hash_generic, MacDigest}; /// utilized in a similar manner. /// /// [RFC6238]: https://datatracker.ietf.org/doc/html/rfc6238 +#[cfg_attr(target_arch = "wasm32", wasm_bindgen)] #[derive(Debug, Clone, Hash)] pub struct TOTP { /// The secret key used in the HMAC process. @@ -42,6 +46,7 @@ pub struct TOTP { } /// All initializer implementations for the [`TOTP`] struct +#[cfg_attr(target_arch = "wasm32", wasm_bindgen)] impl TOTP { /// Generates a new TOTP instance from a byte array representation of the /// secret, a digest algorithm, a number of digits, @@ -132,6 +137,7 @@ impl TOTP { } /// All getters for the [`TOTP`] struct +#[cfg_attr(target_arch = "wasm32", wasm_bindgen)] impl TOTP { /// Gets the algorithm used for code generation. pub fn get_digest(&self) -> MacDigest { @@ -150,6 +156,7 @@ impl TOTP { } /// All helper methods for totp generation +#[cfg_attr(target_arch = "wasm32", wasm_bindgen)] impl TOTP { /// Returns the time in seconds until an OTP refresh is needed. @@ -173,6 +180,7 @@ impl TOTP { } /// All otp generation methods for the [`TOTP`] struct. +#[cfg_attr(target_arch = "wasm32", wasm_bindgen)] impl TOTP { /// Generates and returns the TOTP value for the specified time. /// diff --git a/src/util.rs b/src/util.rs index 119a5a4..90afa1e 100644 --- a/src/util.rs +++ b/src/util.rs @@ -8,6 +8,9 @@ use url::Url; use crate::hotp::HOTP; use crate::totp::TOTP; +#[cfg(target_arch = "wasm32")] +use wasm_bindgen::prelude::*; + /// The digest to use with TOTP. /// /// All three digests referenced in [RFC6238] are supported: @@ -20,6 +23,7 @@ use crate::totp::TOTP; /// /// [RFC6238]: https://datatracker.ietf.org/doc/html/rfc6238 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen)] pub enum MacDigest { SHA1, SHA256, @@ -194,4 +198,4 @@ pub fn parse_otpauth_uri(uri: &str) -> Result { } else { Err(UnknownOtpType(String::from(type_str))) } -} +} \ No newline at end of file diff --git a/src/wasm_utils.rs b/src/wasm_utils.rs new file mode 100644 index 0000000..01b9dde --- /dev/null +++ b/src/wasm_utils.rs @@ -0,0 +1,75 @@ +use crate::hotp::HOTP; +use crate::totp::TOTP; +use crate::util::parse_otpauth_uri; +use crate::util: + +// WASM-specific code (Non C-style enums aren't supported by wasm_bindgen) +// So we have to add some compatibility to the parse_otpauth_uri method + +/// A tuple struct to hold an OTP Parse Result +/// +/// Holds an [`Option`] of each HOTP (with counter) or TOTP parse result. +/// The value which isn't of type [`None`] is the parse result. If both +/// are [`None`] then there was an error in parsing the URI. +#[wasm_bindgen] +pub struct ParseResult( + /// The potential HOTP result given by the parser + Option, + /// The potential TOTP result given by the parser + Option, +); + +/// Getters for the [`ParseResult`] struct +#[wasm_bindgen] +impl ParseResult { + /// Gets the [`HOTPResult`] provided by the parser + /// + /// If [`None`], then the parsed URI was not a HOTP URI. + /// If a value, then the parsed URI was HOTP and also contains the + /// associated counter. + #[wasm_bindgen(getter)] + pub fn get_hotp_result(&self) -> Option { + self.0.clone() + } + + /// Gets the [`TOTP`] provided by the parser + /// + /// If [`None`], then the parsed URI was not a TOTP URI. + /// If a value, then the parsed URI was TOTP and OTPs can be generated + #[wasm_bindgen(getter)] + pub fn get_totp(&self) -> Option { + self.1.clone() + } +} + +/// A tuple struct to hold the HOTP result given by a parser +/// +/// Holds an instance of the [`HOTP`] struct and a numeric counter +#[wasm_bindgen] +#[derive(Clone)] +pub struct HOTPResult( + HOTP, + u64 +); + +#[wasm_bindgen] +impl HOTPResult { + #[wasm_bindgen(getter)] + pub fn get_hotp(&self) -> HOTP { + self.0.clone() + } + + #[wasm_bindgen(getter)] + pub fn get_counter(&self) -> u64 { + self.1 + } +} + +#[wasm_bindgen] +pub fn parse_otpauth_uri_wasm(uri: &str) -> ParseResult { + match parse_otpauth_uri(uri) { + Ok(util::ParseResult::HOTP(result, counter)) => ParseResult(Some(HOTPResult(result, counter)), None), + Ok(util::ParseResult::TOTP(result)) => ParseResult(None, Some(result)), + Err(_e) => ParseResult(None, None) + } +} \ No newline at end of file From 17359282110af0aefff55edc17ebf401c666c3b6 Mon Sep 17 00:00:00 2001 From: Tejas Mehta Date: Sun, 30 Jan 2022 23:57:56 -0500 Subject: [PATCH 2/4] add some more docs --- src/wasm_utils.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/wasm_utils.rs b/src/wasm_utils.rs index 01b9dde..63ba117 100644 --- a/src/wasm_utils.rs +++ b/src/wasm_utils.rs @@ -48,23 +48,31 @@ impl ParseResult { #[wasm_bindgen] #[derive(Clone)] pub struct HOTPResult( + /// The HOTP instance HOTP, + /// The counter needed for OTP generation u64 ); +/// Getters for the [`HOTPResult`] struct #[wasm_bindgen] impl HOTPResult { + /// Gets the [`HOTP`] instance associated with this result #[wasm_bindgen(getter)] pub fn get_hotp(&self) -> HOTP { self.0.clone() } + /// Gets the current counter value for use with the HOTP generation #[wasm_bindgen(getter)] pub fn get_counter(&self) -> u64 { self.1 } } +/// A wasm-compatible method to parse an otpauth URI into its specific OTP +/// generator. Returns the [`ParseResult`] object, which will contain +/// one or neither of the HOTP/TOTP instances. #[wasm_bindgen] pub fn parse_otpauth_uri_wasm(uri: &str) -> ParseResult { match parse_otpauth_uri(uri) { From f937c1dea700ef4f6c23bc5b97b912bfb54f4bd0 Mon Sep 17 00:00:00 2001 From: Tejas Mehta Date: Sun, 30 Jan 2022 23:58:23 -0500 Subject: [PATCH 3/4] fix the import --- src/wasm_utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wasm_utils.rs b/src/wasm_utils.rs index 63ba117..b7d1049 100644 --- a/src/wasm_utils.rs +++ b/src/wasm_utils.rs @@ -1,7 +1,7 @@ use crate::hotp::HOTP; use crate::totp::TOTP; use crate::util::parse_otpauth_uri; -use crate::util: +use crate::util; // WASM-specific code (Non C-style enums aren't supported by wasm_bindgen) // So we have to add some compatibility to the parse_otpauth_uri method From 986ce8f45fa67d0e01f66a84229032beb213e44b Mon Sep 17 00:00:00 2001 From: Tejas Mehta Date: Mon, 31 Jan 2022 00:08:35 -0500 Subject: [PATCH 4/4] add the wasm-bindgen import I missed --- src/wasm_utils.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/wasm_utils.rs b/src/wasm_utils.rs index b7d1049..6948d0f 100644 --- a/src/wasm_utils.rs +++ b/src/wasm_utils.rs @@ -3,6 +3,8 @@ use crate::totp::TOTP; use crate::util::parse_otpauth_uri; use crate::util; +use wasm_bindgen::prelude::*; + // WASM-specific code (Non C-style enums aren't supported by wasm_bindgen) // So we have to add some compatibility to the parse_otpauth_uri method