From d817945c07531f7906da85694a52c07d79e27cf6 Mon Sep 17 00:00:00 2001 From: Semen Evdokimov Date: Thu, 24 Dec 2020 20:25:17 +0300 Subject: [PATCH 1/7] Make Decoded struct public, refactor --- src/argon2.rs | 55 ++++++-- src/decoded.rs | 260 ++++++++++++++++++++++++++++++++++ src/encoding.rs | 284 +------------------------------------- src/lib.rs | 1 + src/variant.rs | 31 +++-- src/version.rs | 20 ++- tests/integration_test.rs | 6 +- 7 files changed, 344 insertions(+), 313 deletions(-) diff --git a/src/argon2.rs b/src/argon2.rs index 15d272a..8521103 100644 --- a/src/argon2.rs +++ b/src/argon2.rs @@ -9,14 +9,15 @@ use crate::config::Config; use crate::context::Context; use crate::core; +use crate::decoded::Decoded; use crate::encoding; use crate::memory::Memory; use crate::result::Result; use crate::thread_mode::ThreadMode; use crate::variant::Variant; use crate::version::Version; - use constant_time_eq::constant_time_eq; +use std::str::FromStr; /// Returns the length of the encoded string. /// @@ -193,7 +194,11 @@ pub fn hash_encoded_old( ad: &[u8], hash_len: u32, ) -> Result { - let threads = if cfg!(feature = "crossbeam-utils") { threads } else { 1 }; + let threads = if cfg!(feature = "crossbeam-utils") { + threads + } else { + 1 + }; let config = Config { variant, version, @@ -263,7 +268,11 @@ pub fn hash_encoded_std( salt: &[u8], hash_len: u32, ) -> Result { - let threads = if cfg!(feature = "crossbeam-utils") { parallelism } else { 1 }; + let threads = if cfg!(feature = "crossbeam-utils") { + parallelism + } else { + 1 + }; let config = Config { variant, version, @@ -412,7 +421,11 @@ pub fn hash_raw_old( ad: &[u8], hash_len: u32, ) -> Result> { - let threads = if cfg!(feature = "crossbeam-utils") { threads } else { 1 }; + let threads = if cfg!(feature = "crossbeam-utils") { + threads + } else { + 1 + }; let config = Config { variant, version, @@ -482,7 +495,11 @@ pub fn hash_raw_std( salt: &[u8], hash_len: u32, ) -> Result> { - let threads = if cfg!(feature = "crossbeam-utils") { parallelism } else { 1 }; + let threads = if cfg!(feature = "crossbeam-utils") { + parallelism + } else { + 1 + }; let config = Config { variant, version, @@ -530,8 +547,20 @@ pub fn verify_encoded(encoded: &str, pwd: &[u8]) -> Result { /// assert!(res); /// ``` pub fn verify_encoded_ext(encoded: &str, pwd: &[u8], secret: &[u8], ad: &[u8]) -> Result { - let decoded = encoding::decode_string(encoded)?; - let threads = if cfg!(feature = "crossbeam-utils") { decoded.parallelism } else { 1 }; + let decoded = Decoded::from_str(encoded)?; + verify_parsed_ext(decoded, pwd, secret, ad) +} + +pub fn verify_parsed(decoded: Decoded, pwd: &[u8]) -> Result { + verify_parsed_ext(decoded, pwd, &[], &[]) +} + +pub fn verify_parsed_ext(decoded: Decoded, pwd: &[u8], secret: &[u8], ad: &[u8]) -> Result { + let threads = if cfg!(feature = "crossbeam-utils") { + decoded.parallelism + } else { + 1 + }; let config = Config { variant: decoded.variant, version: decoded.version, @@ -643,7 +672,11 @@ pub fn verify_raw_old( ad: &[u8], hash: &[u8], ) -> Result { - let threads = if cfg!(feature = "crossbeam-utils") { threads } else { 1 }; + let threads = if cfg!(feature = "crossbeam-utils") { + threads + } else { + 1 + }; let config = Config { variant, version, @@ -720,7 +753,11 @@ pub fn verify_raw_std( salt: &[u8], hash: &[u8], ) -> Result { - let threads = if cfg!(feature = "crossbeam-utils") { parallelism } else { 1 }; + let threads = if cfg!(feature = "crossbeam-utils") { + parallelism + } else { + 1 + }; let config = Config { variant, version, diff --git a/src/decoded.rs b/src/decoded.rs index ff772c1..483db28 100644 --- a/src/decoded.rs +++ b/src/decoded.rs @@ -6,8 +6,55 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use crate::error::Error; +use crate::error::Error::DecodingFail; +use crate::result::Result; use crate::variant::Variant; use crate::version::Version; +use std::str::FromStr; + +/// Structure containing the options. +struct Options { + mem_cost: u32, + time_cost: u32, + parallelism: u32, +} + +impl FromStr for Options { + type Err = Error; + + fn from_str(s: &str) -> Result { + let mut items = s.split(','); + let out = Self { + mem_cost: decode_option(items.next().ok_or(DecodingFail)?, "m")?, + time_cost: decode_option(items.next().ok_or(DecodingFail)?, "t")?, + parallelism: decode_option(items.next().ok_or(DecodingFail)?, "p")?, + }; + + if items.next().is_none() { + Ok(out) + } else { + Err(DecodingFail) + } + } +} + +fn decode_option(s: &str, name: &str) -> Result { + let mut items = s.split('='); + if items.next() != Some(name) { + return Err(DecodingFail); + } + let option = items + .next() + .and_then(|val| val.parse().ok()) + .ok_or(DecodingFail)?; + + if items.next().is_none() { + Ok(option) + } else { + Err(DecodingFail) + } +} /// Structure that contains the decoded data. #[derive(Debug, Eq, PartialEq)] @@ -33,3 +80,216 @@ pub struct Decoded { /// The hash. pub hash: Vec, } + +impl FromStr for Decoded { + type Err = Error; + + /// Attempts to decode the encoded string slice. + fn from_str(encoded: &str) -> Result { + let items: Vec<&str> = encoded.split('$').take(6).collect(); + if !items[0].is_empty() { + return Err(DecodingFail); + } + if items.len() == 6 { + let options = Options::from_str(items[3])?; + Ok(Decoded { + variant: items[1].parse()?, + version: decode_option(items[2], "v")?, + mem_cost: options.mem_cost, + time_cost: options.time_cost, + parallelism: options.parallelism, + salt: base64::decode(items[4])?, + hash: base64::decode(items[5])?, + }) + } else if items.len() == 5 { + let options = Options::from_str(items[2])?; + Ok(Decoded { + variant: items[1].parse()?, + version: Version::Version10, + mem_cost: options.mem_cost, + time_cost: options.time_cost, + parallelism: options.parallelism, + salt: base64::decode(items[3])?, + hash: base64::decode(items[4])?, + }) + } else { + Err(DecodingFail) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn decode_string_with_version10_returns_correct_result() { + let encoded = "$argon2i$v=16$m=4096,t=3,p=1\ + $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; + let expected = Decoded { + variant: Variant::Argon2i, + version: Version::Version10, + mem_cost: 4096, + time_cost: 3, + parallelism: 1, + salt: b"salt1234".to_vec(), + hash: b"12345678901234567890123456789012".to_vec(), + }; + let actual = Decoded::from_str(encoded).unwrap(); + assert_eq!(actual, expected); + } + + #[test] + fn decode_string_with_version13_returns_correct_result() { + let encoded = "$argon2i$v=19$m=4096,t=3,p=1\ + $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; + let expected = Decoded { + variant: Variant::Argon2i, + version: Version::Version13, + mem_cost: 4096, + time_cost: 3, + parallelism: 1, + salt: b"salt1234".to_vec(), + hash: b"12345678901234567890123456789012".to_vec(), + }; + let actual = Decoded::from_str(encoded).unwrap(); + assert_eq!(actual, expected); + } + + #[test] + fn decode_string_without_version_returns_correct_result() { + let encoded = "$argon2i$m=4096,t=3,p=1\ + $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; + let expected = Decoded { + variant: Variant::Argon2i, + version: Version::Version10, + mem_cost: 4096, + time_cost: 3, + parallelism: 1, + salt: b"salt1234".to_vec(), + hash: b"12345678901234567890123456789012".to_vec(), + }; + let actual = Decoded::from_str(encoded).unwrap(); + assert_eq!(actual, expected); + } + + #[test] + fn decode_string_without_variant_returns_error_result() { + let encoded = "$m=4096,t=3,p=1\ + $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; + let result = Decoded::from_str(encoded); + assert_eq!(result, Err(Error::DecodingFail)); + } + + #[test] + fn decode_string_with_empty_variant_returns_error_result() { + let encoded = "$$m=4096,t=3,p=1\ + $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; + let result = Decoded::from_str(encoded); + assert_eq!(result, Err(Error::DecodingFail)); + } + + #[test] + fn decode_string_with_invalid_variant_returns_error_result() { + let encoded = "$argon$m=4096,t=3,p=1\ + $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; + let result = Decoded::from_str(encoded); + assert_eq!(result, Err(Error::DecodingFail)); + } + + #[test] + fn decode_string_without_mem_cost_returns_error_result() { + let encoded = "$argon2i$t=3,p=1\ + $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; + let result = Decoded::from_str(encoded); + assert_eq!(result, Err(Error::DecodingFail)); + } + + #[test] + fn decode_string_with_empty_mem_cost_returns_error_result() { + let encoded = "$argon2i$m=,t=3,p=1\ + $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; + let result = Decoded::from_str(encoded); + assert_eq!(result, Err(Error::DecodingFail)); + } + + #[test] + fn decode_string_with_non_numeric_mem_cost_returns_error_result() { + let encoded = "$argon2i$m=a,t=3,p=1\ + $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; + let result = Decoded::from_str(encoded); + assert_eq!(result, Err(Error::DecodingFail)); + } + + #[test] + fn decode_string_without_time_cost_returns_error_result() { + let encoded = "$argon2i$m=4096,p=1\ + $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; + let result = Decoded::from_str(encoded); + assert_eq!(result, Err(Error::DecodingFail)); + } + + #[test] + fn decode_string_with_empty_time_cost_returns_error_result() { + let encoded = "$argon2i$m=4096,t=,p=1\ + $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; + let result = Decoded::from_str(encoded); + assert_eq!(result, Err(Error::DecodingFail)); + } + + #[test] + fn decode_string_with_non_numeric_time_cost_returns_error_result() { + let encoded = "$argon2i$m=4096,t=a,p=1\ + $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; + let result = Decoded::from_str(encoded); + assert_eq!(result, Err(Error::DecodingFail)); + } + + #[test] + fn decode_string_without_parallelism_returns_error_result() { + let encoded = "$argon2i$m=4096,t=3\ + $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; + let result = Decoded::from_str(encoded); + assert_eq!(result, Err(Error::DecodingFail)); + } + + #[test] + fn decode_string_with_empty_parallelism_returns_error_result() { + let encoded = "$argon2i$m=4096,t=3,p=\ + $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; + let result = Decoded::from_str(encoded); + assert_eq!(result, Err(Error::DecodingFail)); + } + + #[test] + fn decode_string_with_non_numeric_parallelism_returns_error_result() { + let encoded = "$argon2i$m=4096,t=3,p=a\ + $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; + let result = Decoded::from_str(encoded); + assert_eq!(result, Err(Error::DecodingFail)); + } + + #[test] + fn decode_string_without_salt_returns_error_result() { + let encoded = "$argon2i$m=4096,t=3,p=1\ + $MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; + let result = Decoded::from_str(encoded); + assert_eq!(result, Err(Error::DecodingFail)); + } + + #[test] + fn decode_string_without_hash_returns_error_result() { + let encoded = "$argon2i$m=4096,t=3,p=a\ + $c2FsdDEyMzQ="; + let result = Decoded::from_str(encoded); + assert_eq!(result, Err(Error::DecodingFail)); + } + + #[test] + fn decode_string_with_empty_hash_returns_error_result() { + let encoded = "$argon2i$m=4096,t=3,p=a\ + $c2FsdDEyMzQ=$"; + let result = Decoded::from_str(encoded); + assert_eq!(result, Err(Error::DecodingFail)); + } +} diff --git a/src/encoding.rs b/src/encoding.rs index 438c046..61e5ec3 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -7,20 +7,8 @@ // except according to those terms. use crate::context::Context; -use crate::decoded::Decoded; -use crate::error::Error; -use crate::result::Result; -use crate::variant::Variant; -use crate::version::Version; use base64; -/// Structure containing the options. -struct Options { - mem_cost: u32, - time_cost: u32, - parallelism: u32, -} - /// Gets the base64 encoded length of a byte slice with the specified length. pub fn base64_len(length: u32) -> u32 { let olen = (length / 3) << 2; @@ -31,105 +19,6 @@ pub fn base64_len(length: u32) -> u32 { } } -/// Attempts to decode the encoded string slice. -pub fn decode_string(encoded: &str) -> Result { - let items: Vec<&str> = encoded.split('$').collect(); - if items.len() == 6 { - decode_empty(items[0])?; - let variant = decode_variant(items[1])?; - let version = decode_version(items[2])?; - let options = decode_options(items[3])?; - let salt = base64::decode(items[4])?; - let hash = base64::decode(items[5])?; - - Ok(Decoded { - variant, - version, - mem_cost: options.mem_cost, - time_cost: options.time_cost, - parallelism: options.parallelism, - salt, - hash, - }) - } else if items.len() == 5 { - decode_empty(items[0])?; - let variant = decode_variant(items[1])?; - let options = decode_options(items[2])?; - let salt = base64::decode(items[3])?; - let hash = base64::decode(items[4])?; - - Ok(Decoded { - variant, - version: Version::Version10, - mem_cost: options.mem_cost, - time_cost: options.time_cost, - parallelism: options.parallelism, - salt, - hash, - }) - } else { - Err(Error::DecodingFail) - } -} - -fn decode_empty(str: &str) -> Result<()> { - if str == "" { - Ok(()) - } else { - Err(Error::DecodingFail) - } -} - -fn decode_options(str: &str) -> Result { - let items: Vec<&str> = str.split(',').collect(); - if items.len() == 3 { - Ok(Options { - mem_cost: decode_option(items[0], "m")?, - time_cost: decode_option(items[1], "t")?, - parallelism: decode_option(items[2], "p")?, - }) - } else { - Err(Error::DecodingFail) - } -} - -fn decode_option(str: &str, name: &str) -> Result { - let items: Vec<&str> = str.split('=').collect(); - if items.len() == 2 { - if items[0] == name { - decode_u32(items[1]) - } else { - Err(Error::DecodingFail) - } - } else { - Err(Error::DecodingFail) - } -} - -fn decode_u32(str: &str) -> Result { - match str.parse() { - Ok(i) => Ok(i), - Err(_) => Err(Error::DecodingFail), - } -} - -fn decode_variant(str: &str) -> Result { - Variant::from_str(str) -} - -fn decode_version(str: &str) -> Result { - let items: Vec<&str> = str.split('=').collect(); - if items.len() == 2 { - if items[0] == "v" { - Version::from_str(items[1]) - } else { - Err(Error::DecodingFail) - } - } else { - Err(Error::DecodingFail) - } -} - /// Encodes the hash and context. pub fn encode_string(context: &Context, hash: &[u8]) -> String { format!( @@ -165,7 +54,7 @@ mod tests { use crate::decoded::Decoded; #[cfg(feature = "crossbeam-utils")] use crate::encoding::encode_string; - use crate::encoding::{base64_len, decode_string, num_len}; + use crate::encoding::{base64_len, num_len}; use crate::error::Error; #[cfg(feature = "crossbeam-utils")] use crate::thread_mode::ThreadMode; @@ -192,177 +81,6 @@ mod tests { } } - #[test] - fn decode_string_with_version10_returns_correct_result() { - let encoded = "$argon2i$v=16$m=4096,t=3,p=1\ - $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; - let expected = Decoded { - variant: Variant::Argon2i, - version: Version::Version10, - mem_cost: 4096, - time_cost: 3, - parallelism: 1, - salt: b"salt1234".to_vec(), - hash: b"12345678901234567890123456789012".to_vec(), - }; - let actual = decode_string(encoded).unwrap(); - assert_eq!(actual, expected); - } - - #[test] - fn decode_string_with_version13_returns_correct_result() { - let encoded = "$argon2i$v=19$m=4096,t=3,p=1\ - $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; - let expected = Decoded { - variant: Variant::Argon2i, - version: Version::Version13, - mem_cost: 4096, - time_cost: 3, - parallelism: 1, - salt: b"salt1234".to_vec(), - hash: b"12345678901234567890123456789012".to_vec(), - }; - let actual = decode_string(encoded).unwrap(); - assert_eq!(actual, expected); - } - - #[test] - fn decode_string_without_version_returns_correct_result() { - let encoded = "$argon2i$m=4096,t=3,p=1\ - $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; - let expected = Decoded { - variant: Variant::Argon2i, - version: Version::Version10, - mem_cost: 4096, - time_cost: 3, - parallelism: 1, - salt: b"salt1234".to_vec(), - hash: b"12345678901234567890123456789012".to_vec(), - }; - let actual = decode_string(encoded).unwrap(); - assert_eq!(actual, expected); - } - - #[test] - fn decode_string_without_variant_returns_error_result() { - let encoded = "$m=4096,t=3,p=1\ - $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; - let result = decode_string(encoded); - assert_eq!(result, Err(Error::DecodingFail)); - } - - #[test] - fn decode_string_with_empty_variant_returns_error_result() { - let encoded = "$$m=4096,t=3,p=1\ - $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; - let result = decode_string(encoded); - assert_eq!(result, Err(Error::DecodingFail)); - } - - #[test] - fn decode_string_with_invalid_variant_returns_error_result() { - let encoded = "$argon$m=4096,t=3,p=1\ - $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; - let result = decode_string(encoded); - assert_eq!(result, Err(Error::DecodingFail)); - } - - #[test] - fn decode_string_without_mem_cost_returns_error_result() { - let encoded = "$argon2i$t=3,p=1\ - $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; - let result = decode_string(encoded); - assert_eq!(result, Err(Error::DecodingFail)); - } - - #[test] - fn decode_string_with_empty_mem_cost_returns_error_result() { - let encoded = "$argon2i$m=,t=3,p=1\ - $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; - let result = decode_string(encoded); - assert_eq!(result, Err(Error::DecodingFail)); - } - - #[test] - fn decode_string_with_non_numeric_mem_cost_returns_error_result() { - let encoded = "$argon2i$m=a,t=3,p=1\ - $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; - let result = decode_string(encoded); - assert_eq!(result, Err(Error::DecodingFail)); - } - - #[test] - fn decode_string_without_time_cost_returns_error_result() { - let encoded = "$argon2i$m=4096,p=1\ - $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; - let result = decode_string(encoded); - assert_eq!(result, Err(Error::DecodingFail)); - } - - #[test] - fn decode_string_with_empty_time_cost_returns_error_result() { - let encoded = "$argon2i$m=4096,t=,p=1\ - $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; - let result = decode_string(encoded); - assert_eq!(result, Err(Error::DecodingFail)); - } - - #[test] - fn decode_string_with_non_numeric_time_cost_returns_error_result() { - let encoded = "$argon2i$m=4096,t=a,p=1\ - $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; - let result = decode_string(encoded); - assert_eq!(result, Err(Error::DecodingFail)); - } - - #[test] - fn decode_string_without_parallelism_returns_error_result() { - let encoded = "$argon2i$m=4096,t=3\ - $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; - let result = decode_string(encoded); - assert_eq!(result, Err(Error::DecodingFail)); - } - - #[test] - fn decode_string_with_empty_parallelism_returns_error_result() { - let encoded = "$argon2i$m=4096,t=3,p=\ - $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; - let result = decode_string(encoded); - assert_eq!(result, Err(Error::DecodingFail)); - } - - #[test] - fn decode_string_with_non_numeric_parallelism_returns_error_result() { - let encoded = "$argon2i$m=4096,t=3,p=a\ - $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; - let result = decode_string(encoded); - assert_eq!(result, Err(Error::DecodingFail)); - } - - #[test] - fn decode_string_without_salt_returns_error_result() { - let encoded = "$argon2i$m=4096,t=3,p=1\ - $MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; - let result = decode_string(encoded); - assert_eq!(result, Err(Error::DecodingFail)); - } - - #[test] - fn decode_string_without_hash_returns_error_result() { - let encoded = "$argon2i$m=4096,t=3,p=a\ - $c2FsdDEyMzQ="; - let result = decode_string(encoded); - assert_eq!(result, Err(Error::DecodingFail)); - } - - #[test] - fn decode_string_with_empty_hash_returns_error_result() { - let encoded = "$argon2i$m=4096,t=3,p=a\ - $c2FsdDEyMzQ=$"; - let result = decode_string(encoded); - assert_eq!(result, Err(Error::DecodingFail)); - } - #[cfg(feature = "crossbeam-utils")] #[test] fn encode_string_returns_correct_string() { diff --git a/src/lib.rs b/src/lib.rs index bcf362b..6acfccb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -100,6 +100,7 @@ mod version; pub use crate::argon2::*; pub use crate::config::Config; +pub use crate::decoded::Decoded; pub use crate::error::Error; pub use crate::result::Result; pub use crate::thread_mode::ThreadMode; diff --git a/src/variant.rs b/src/variant.rs index f990adf..456b66e 100644 --- a/src/variant.rs +++ b/src/variant.rs @@ -9,6 +9,7 @@ use crate::error::Error; use crate::result::Result; use std::fmt; +use std::str::FromStr; /// The Argon2 variant. #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)] @@ -26,6 +27,22 @@ pub enum Variant { Argon2id = 2, } +impl FromStr for Variant { + type Err = Error; + + fn from_str(s: &str) -> Result { + match s { + "Argon2d" => Ok(Variant::Argon2d), + "Argon2i" => Ok(Variant::Argon2i), + "Argon2id" => Ok(Variant::Argon2id), + "argon2d" => Ok(Variant::Argon2d), + "argon2i" => Ok(Variant::Argon2i), + "argon2id" => Ok(Variant::Argon2id), + _ => Err(Error::DecodingFail), + } + } +} + impl Variant { /// Gets the lowercase string slice representation of the variant. pub fn as_lowercase_str(&self) -> &'static str { @@ -55,19 +72,6 @@ impl Variant { } } - /// Attempts to create a variant from a string slice. - pub fn from_str(str: &str) -> Result { - match str { - "Argon2d" => Ok(Variant::Argon2d), - "Argon2i" => Ok(Variant::Argon2i), - "Argon2id" => Ok(Variant::Argon2id), - "argon2d" => Ok(Variant::Argon2d), - "argon2i" => Ok(Variant::Argon2i), - "argon2id" => Ok(Variant::Argon2id), - _ => Err(Error::DecodingFail), - } - } - /// Attempts to create a variant from an u32. pub fn from_u32(val: u32) -> Result { match val { @@ -96,6 +100,7 @@ mod tests { use crate::error::Error; use crate::variant::Variant; + use std::str::FromStr; #[test] fn as_lowercase_str_returns_correct_str() { diff --git a/src/version.rs b/src/version.rs index edda427..145e582 100644 --- a/src/version.rs +++ b/src/version.rs @@ -9,6 +9,7 @@ use crate::error::Error; use crate::result::Result; use std::fmt; +use std::str::FromStr; /// The Argon2 version. #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)] @@ -20,20 +21,24 @@ pub enum Version { Version13 = 0x13, } -impl Version { - /// Gets the u32 representation of the version. - pub fn as_u32(&self) -> u32 { - *self as u32 - } +impl FromStr for Version { + type Err = Error; /// Attempts to create a version from a string slice. - pub fn from_str(str: &str) -> Result { - match str { + fn from_str(s: &str) -> Result { + match s { "16" => Ok(Version::Version10), "19" => Ok(Version::Version13), _ => Err(Error::DecodingFail), } } +} + +impl Version { + /// Gets the u32 representation of the version. + pub fn as_u32(&self) -> u32 { + *self as u32 + } /// Attempts to create a version from an u32. pub fn from_u32(val: u32) -> Result { @@ -62,6 +67,7 @@ mod tests { use crate::error::Error; use crate::version::Version; + use std::str::FromStr; #[test] fn as_u32_returns_correct_u32() { diff --git a/tests/integration_test.rs b/tests/integration_test.rs index 0d6a3ce..fee496c 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -1053,7 +1053,11 @@ fn hash_test( hex: &str, enc: &str, ) { - let threads = if cfg!(feature = "crossbeam-utils") { p } else { 1 }; + let threads = if cfg!(feature = "crossbeam-utils") { + p + } else { + 1 + }; let config = Config { variant: var, version: ver, From 6a2ff4b0f53bbaeed2a8ea2e47becdf50f9d66c4 Mon Sep 17 00:00:00 2001 From: Semen Evdokimov Date: Thu, 24 Dec 2020 20:25:54 +0300 Subject: [PATCH 2/7] Add .idea to the .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index a9d37c5..9c7143b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ target Cargo.lock +/.idea \ No newline at end of file From 2587ce9f161e8c517d5fd0f37ff50d1a8b2ed68e Mon Sep 17 00:00:00 2001 From: Semen Evdokimov Date: Thu, 24 Dec 2020 20:32:03 +0300 Subject: [PATCH 3/7] Add Clone to the Decoded --- src/decoded.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/decoded.rs b/src/decoded.rs index 483db28..7e6b666 100644 --- a/src/decoded.rs +++ b/src/decoded.rs @@ -57,7 +57,7 @@ fn decode_option(s: &str, name: &str) -> Result { } /// Structure that contains the decoded data. -#[derive(Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct Decoded { /// The variant. pub variant: Variant, From 1d88c98158eedda8b6254051b52e7801375a0e35 Mon Sep 17 00:00:00 2001 From: Semen Evdokimov Date: Fri, 25 Dec 2020 12:59:16 +0300 Subject: [PATCH 4/7] Add docs, rename Decoded to Digest --- src/argon2.rs | 48 +++++++++++-- src/{decoded.rs => digest.rs} | 124 +++++++++++++++++++++------------- src/encoding.rs | 2 +- src/lib.rs | 4 +- 4 files changed, 121 insertions(+), 57 deletions(-) rename src/{decoded.rs => digest.rs} (79%) diff --git a/src/argon2.rs b/src/argon2.rs index 8521103..a94afa0 100644 --- a/src/argon2.rs +++ b/src/argon2.rs @@ -9,16 +9,17 @@ use crate::config::Config; use crate::context::Context; use crate::core; -use crate::decoded::Decoded; +use crate::digest::Digest; use crate::encoding; use crate::memory::Memory; use crate::result::Result; use crate::thread_mode::ThreadMode; use crate::variant::Variant; use crate::version::Version; -use constant_time_eq::constant_time_eq; use std::str::FromStr; +use constant_time_eq::constant_time_eq; + /// Returns the length of the encoded string. /// /// # Remarks @@ -547,15 +548,48 @@ pub fn verify_encoded(encoded: &str, pwd: &[u8]) -> Result { /// assert!(res); /// ``` pub fn verify_encoded_ext(encoded: &str, pwd: &[u8], secret: &[u8], ad: &[u8]) -> Result { - let decoded = Decoded::from_str(encoded)?; - verify_parsed_ext(decoded, pwd, secret, ad) + verify_digest_ext(&Digest::from_str(encoded)?, pwd, secret, ad) } -pub fn verify_parsed(decoded: Decoded, pwd: &[u8]) -> Result { - verify_parsed_ext(decoded, pwd, &[], &[]) +/// Verifies the password with the [`Digest`]. +/// +/// # Example +/// +/// ``` +/// use argon2::Digest; +/// use std::str::FromStr as _; +/// +/// let enc = "$argon2i$v=19$m=4096,t=3,p=1$c29tZXNhbHQ\ +/// $iWh06vD8Fy27wf9npn6FXWiCX4K6pW6Ue1Bnzz07Z8A"; +/// let digest = Digest::from_str(enc).unwrap(); +/// +/// let is_matches = argon2::verify_digest(&digest, b"password").unwrap(); +/// assert!(is_matches); +/// ``` +pub fn verify_digest(decoded: &Digest, pwd: &[u8]) -> Result { + verify_digest_ext(&decoded, pwd, &[], &[]) } -pub fn verify_parsed_ext(decoded: Decoded, pwd: &[u8], secret: &[u8], ad: &[u8]) -> Result { +/// Verifies the password with the [`Digest`], secret and associated data. +/// +/// # Example +/// +/// ``` +/// use argon2::Digest; +/// use std::str::FromStr as _; +/// +/// let enc = "$argon2i$v=19$m=4096,t=3,p=1$c29tZXNhbHQ\ +/// $OlcSvlN20Lz43sK3jhCJ9K04oejhiY0AmI+ck6nuETo"; +/// let digest = Digest::from_str(enc).unwrap(); +/// +/// let pwd = b"password"; +/// let secret = b"secret"; +/// let ad = b"ad"; +/// +/// let is_matches = argon2::verify_digest_ext(&digest, pwd, secret, ad).unwrap(); +/// assert_eq!(is_matches) +/// ``` +pub fn verify_digest_ext(decoded: &Digest, pwd: &[u8], secret: &[u8], ad: &[u8]) -> Result { let threads = if cfg!(feature = "crossbeam-utils") { decoded.parallelism } else { diff --git a/src/decoded.rs b/src/digest.rs similarity index 79% rename from src/decoded.rs rename to src/digest.rs index 7e6b666..7e0e850 100644 --- a/src/decoded.rs +++ b/src/digest.rs @@ -11,8 +11,26 @@ use crate::error::Error::DecodingFail; use crate::result::Result; use crate::variant::Variant; use crate::version::Version; +use std::fmt; use std::str::FromStr; +fn decode_option(s: &str, name: &str) -> Result { + let mut items = s.split('='); + if items.next() != Some(name) { + return Err(DecodingFail); + } + let option = items + .next() + .and_then(|val| val.parse().ok()) + .ok_or(DecodingFail)?; + + if items.next().is_none() { + Ok(option) + } else { + Err(DecodingFail) + } +} + /// Structure containing the options. struct Options { mem_cost: u32, @@ -39,39 +57,35 @@ impl FromStr for Options { } } -fn decode_option(s: &str, name: &str) -> Result { - let mut items = s.split('='); - if items.next() != Some(name) { - return Err(DecodingFail); - } - let option = items - .next() - .and_then(|val| val.parse().ok()) - .ok_or(DecodingFail)?; - - if items.next().is_none() { - Ok(option) - } else { - Err(DecodingFail) - } -} - -/// Structure that contains the decoded data. +/// Parsed representation of the [Argon2] hash in encoded form. +/// +/// You can parse [`Digest`] hash from the [`str`] by [`FromStr`] +/// implementation of this structure. +/// +/// [`Digest`] can be used for password verification with a +/// [`argon2::verify_digest`] function. +/// +/// [Argon2]: https://en.wikipedia.org/wiki/Argon2 +/// [`argon2::verify_digest`]: crate::verify_digest #[derive(Clone, Debug, Eq, PartialEq)] -pub struct Decoded { - /// The variant. +pub struct Digest { + /// The variant of [Argon2] being used. + /// + /// [Argon2]: https://en.wikipedia.org/wiki/Argon2 pub variant: Variant, - /// The version. + /// The version of [Argon2] being used. + /// + /// [Argon2]: https://en.wikipedia.org/wiki/Argon2 pub version: Version, /// The amount of memory requested (KiB). pub mem_cost: u32, - /// The number of passes. + /// The number of iterations (or passes) over the memory. pub time_cost: u32, - /// The parallelism. + /// The number of threads (or lanes) used by the algorithm. pub parallelism: u32, /// The salt. @@ -81,7 +95,23 @@ pub struct Decoded { pub hash: Vec, } -impl FromStr for Decoded { +impl fmt::Display for Digest { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "${}$v={}$m={},t={},p={}${}${}", + self.variant, + self.version, + self.mem_cost, + self.time_cost, + self.parallelism, + base64::encode_config(&self.salt, base64::STANDARD_NO_PAD), + base64::encode_config(&self.hash, base64::STANDARD_NO_PAD), + ) + } +} + +impl FromStr for Digest { type Err = Error; /// Attempts to decode the encoded string slice. @@ -92,7 +122,7 @@ impl FromStr for Decoded { } if items.len() == 6 { let options = Options::from_str(items[3])?; - Ok(Decoded { + Ok(Digest { variant: items[1].parse()?, version: decode_option(items[2], "v")?, mem_cost: options.mem_cost, @@ -103,7 +133,7 @@ impl FromStr for Decoded { }) } else if items.len() == 5 { let options = Options::from_str(items[2])?; - Ok(Decoded { + Ok(Digest { variant: items[1].parse()?, version: Version::Version10, mem_cost: options.mem_cost, @@ -126,7 +156,7 @@ mod tests { fn decode_string_with_version10_returns_correct_result() { let encoded = "$argon2i$v=16$m=4096,t=3,p=1\ $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; - let expected = Decoded { + let expected = Digest { variant: Variant::Argon2i, version: Version::Version10, mem_cost: 4096, @@ -135,7 +165,7 @@ mod tests { salt: b"salt1234".to_vec(), hash: b"12345678901234567890123456789012".to_vec(), }; - let actual = Decoded::from_str(encoded).unwrap(); + let actual = Digest::from_str(encoded).unwrap(); assert_eq!(actual, expected); } @@ -143,7 +173,7 @@ mod tests { fn decode_string_with_version13_returns_correct_result() { let encoded = "$argon2i$v=19$m=4096,t=3,p=1\ $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; - let expected = Decoded { + let expected = Digest { variant: Variant::Argon2i, version: Version::Version13, mem_cost: 4096, @@ -152,7 +182,7 @@ mod tests { salt: b"salt1234".to_vec(), hash: b"12345678901234567890123456789012".to_vec(), }; - let actual = Decoded::from_str(encoded).unwrap(); + let actual = Digest::from_str(encoded).unwrap(); assert_eq!(actual, expected); } @@ -160,7 +190,7 @@ mod tests { fn decode_string_without_version_returns_correct_result() { let encoded = "$argon2i$m=4096,t=3,p=1\ $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; - let expected = Decoded { + let expected = Digest { variant: Variant::Argon2i, version: Version::Version10, mem_cost: 4096, @@ -169,7 +199,7 @@ mod tests { salt: b"salt1234".to_vec(), hash: b"12345678901234567890123456789012".to_vec(), }; - let actual = Decoded::from_str(encoded).unwrap(); + let actual = Digest::from_str(encoded).unwrap(); assert_eq!(actual, expected); } @@ -177,7 +207,7 @@ mod tests { fn decode_string_without_variant_returns_error_result() { let encoded = "$m=4096,t=3,p=1\ $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; - let result = Decoded::from_str(encoded); + let result = Digest::from_str(encoded); assert_eq!(result, Err(Error::DecodingFail)); } @@ -185,7 +215,7 @@ mod tests { fn decode_string_with_empty_variant_returns_error_result() { let encoded = "$$m=4096,t=3,p=1\ $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; - let result = Decoded::from_str(encoded); + let result = Digest::from_str(encoded); assert_eq!(result, Err(Error::DecodingFail)); } @@ -193,7 +223,7 @@ mod tests { fn decode_string_with_invalid_variant_returns_error_result() { let encoded = "$argon$m=4096,t=3,p=1\ $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; - let result = Decoded::from_str(encoded); + let result = Digest::from_str(encoded); assert_eq!(result, Err(Error::DecodingFail)); } @@ -201,7 +231,7 @@ mod tests { fn decode_string_without_mem_cost_returns_error_result() { let encoded = "$argon2i$t=3,p=1\ $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; - let result = Decoded::from_str(encoded); + let result = Digest::from_str(encoded); assert_eq!(result, Err(Error::DecodingFail)); } @@ -209,7 +239,7 @@ mod tests { fn decode_string_with_empty_mem_cost_returns_error_result() { let encoded = "$argon2i$m=,t=3,p=1\ $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; - let result = Decoded::from_str(encoded); + let result = Digest::from_str(encoded); assert_eq!(result, Err(Error::DecodingFail)); } @@ -217,7 +247,7 @@ mod tests { fn decode_string_with_non_numeric_mem_cost_returns_error_result() { let encoded = "$argon2i$m=a,t=3,p=1\ $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; - let result = Decoded::from_str(encoded); + let result = Digest::from_str(encoded); assert_eq!(result, Err(Error::DecodingFail)); } @@ -225,7 +255,7 @@ mod tests { fn decode_string_without_time_cost_returns_error_result() { let encoded = "$argon2i$m=4096,p=1\ $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; - let result = Decoded::from_str(encoded); + let result = Digest::from_str(encoded); assert_eq!(result, Err(Error::DecodingFail)); } @@ -233,7 +263,7 @@ mod tests { fn decode_string_with_empty_time_cost_returns_error_result() { let encoded = "$argon2i$m=4096,t=,p=1\ $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; - let result = Decoded::from_str(encoded); + let result = Digest::from_str(encoded); assert_eq!(result, Err(Error::DecodingFail)); } @@ -241,7 +271,7 @@ mod tests { fn decode_string_with_non_numeric_time_cost_returns_error_result() { let encoded = "$argon2i$m=4096,t=a,p=1\ $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; - let result = Decoded::from_str(encoded); + let result = Digest::from_str(encoded); assert_eq!(result, Err(Error::DecodingFail)); } @@ -249,7 +279,7 @@ mod tests { fn decode_string_without_parallelism_returns_error_result() { let encoded = "$argon2i$m=4096,t=3\ $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; - let result = Decoded::from_str(encoded); + let result = Digest::from_str(encoded); assert_eq!(result, Err(Error::DecodingFail)); } @@ -257,7 +287,7 @@ mod tests { fn decode_string_with_empty_parallelism_returns_error_result() { let encoded = "$argon2i$m=4096,t=3,p=\ $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; - let result = Decoded::from_str(encoded); + let result = Digest::from_str(encoded); assert_eq!(result, Err(Error::DecodingFail)); } @@ -265,7 +295,7 @@ mod tests { fn decode_string_with_non_numeric_parallelism_returns_error_result() { let encoded = "$argon2i$m=4096,t=3,p=a\ $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; - let result = Decoded::from_str(encoded); + let result = Digest::from_str(encoded); assert_eq!(result, Err(Error::DecodingFail)); } @@ -273,7 +303,7 @@ mod tests { fn decode_string_without_salt_returns_error_result() { let encoded = "$argon2i$m=4096,t=3,p=1\ $MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; - let result = Decoded::from_str(encoded); + let result = Digest::from_str(encoded); assert_eq!(result, Err(Error::DecodingFail)); } @@ -281,7 +311,7 @@ mod tests { fn decode_string_without_hash_returns_error_result() { let encoded = "$argon2i$m=4096,t=3,p=a\ $c2FsdDEyMzQ="; - let result = Decoded::from_str(encoded); + let result = Digest::from_str(encoded); assert_eq!(result, Err(Error::DecodingFail)); } @@ -289,7 +319,7 @@ mod tests { fn decode_string_with_empty_hash_returns_error_result() { let encoded = "$argon2i$m=4096,t=3,p=a\ $c2FsdDEyMzQ=$"; - let result = Decoded::from_str(encoded); + let result = Digest::from_str(encoded); assert_eq!(result, Err(Error::DecodingFail)); } } diff --git a/src/encoding.rs b/src/encoding.rs index 61e5ec3..1696d7c 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -51,7 +51,7 @@ mod tests { use crate::config::Config; #[cfg(feature = "crossbeam-utils")] use crate::context::Context; - use crate::decoded::Decoded; + use crate::digest::Digest; #[cfg(feature = "crossbeam-utils")] use crate::encoding::encode_string; use crate::encoding::{base64_len, num_len}; diff --git a/src/lib.rs b/src/lib.rs index 6acfccb..409ac80 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -89,7 +89,7 @@ mod common; mod config; mod context; mod core; -mod decoded; +mod digest; mod encoding; mod error; mod memory; @@ -100,7 +100,7 @@ mod version; pub use crate::argon2::*; pub use crate::config::Config; -pub use crate::decoded::Decoded; +pub use crate::digest::Digest; pub use crate::error::Error; pub use crate::result::Result; pub use crate::thread_mode::ThreadMode; From b13773b38ebe258fae21e40104f999748fd746ff Mon Sep 17 00:00:00 2001 From: Semen Evdokimov Date: Fri, 25 Dec 2020 14:38:09 +0300 Subject: [PATCH 5/7] Refactor --- src/argon2.rs | 2 +- src/digest.rs | 90 +++++++++++++++++++-------------------- src/encoding.rs | 3 +- src/variant.rs | 39 ++++++++--------- src/version.rs | 33 +++++++------- tests/integration_test.rs | 6 +-- 6 files changed, 86 insertions(+), 87 deletions(-) diff --git a/src/argon2.rs b/src/argon2.rs index a94afa0..26f1c79 100644 --- a/src/argon2.rs +++ b/src/argon2.rs @@ -587,7 +587,7 @@ pub fn verify_digest(decoded: &Digest, pwd: &[u8]) -> Result { /// let ad = b"ad"; /// /// let is_matches = argon2::verify_digest_ext(&digest, pwd, secret, ad).unwrap(); -/// assert_eq!(is_matches) +/// assert_eq!(is_matches); /// ``` pub fn verify_digest_ext(decoded: &Digest, pwd: &[u8], secret: &[u8], ad: &[u8]) -> Result { let threads = if cfg!(feature = "crossbeam-utils") { diff --git a/src/digest.rs b/src/digest.rs index 7e0e850..0fa09e8 100644 --- a/src/digest.rs +++ b/src/digest.rs @@ -6,56 +6,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::{fmt, str::FromStr}; + use crate::error::Error; use crate::error::Error::DecodingFail; use crate::result::Result; use crate::variant::Variant; use crate::version::Version; -use std::fmt; -use std::str::FromStr; - -fn decode_option(s: &str, name: &str) -> Result { - let mut items = s.split('='); - if items.next() != Some(name) { - return Err(DecodingFail); - } - let option = items - .next() - .and_then(|val| val.parse().ok()) - .ok_or(DecodingFail)?; - - if items.next().is_none() { - Ok(option) - } else { - Err(DecodingFail) - } -} - -/// Structure containing the options. -struct Options { - mem_cost: u32, - time_cost: u32, - parallelism: u32, -} - -impl FromStr for Options { - type Err = Error; - - fn from_str(s: &str) -> Result { - let mut items = s.split(','); - let out = Self { - mem_cost: decode_option(items.next().ok_or(DecodingFail)?, "m")?, - time_cost: decode_option(items.next().ok_or(DecodingFail)?, "t")?, - parallelism: decode_option(items.next().ok_or(DecodingFail)?, "p")?, - }; - - if items.next().is_none() { - Ok(out) - } else { - Err(DecodingFail) - } - } -} /// Parsed representation of the [Argon2] hash in encoded form. /// @@ -148,6 +105,49 @@ impl FromStr for Digest { } } +fn decode_option(s: &str, name: &str) -> Result { + let mut items = s.split('='); + if items.next() != Some(name) { + return Err(DecodingFail); + } + let option = items + .next() + .and_then(|val| val.parse().ok()) + .ok_or(DecodingFail)?; + + if items.next().is_none() { + Ok(option) + } else { + Err(DecodingFail) + } +} + +/// Structure containing the options. +struct Options { + mem_cost: u32, + time_cost: u32, + parallelism: u32, +} + +impl FromStr for Options { + type Err = Error; + + fn from_str(s: &str) -> Result { + let mut items = s.split(','); + let out = Self { + mem_cost: decode_option(items.next().ok_or(DecodingFail)?, "m")?, + time_cost: decode_option(items.next().ok_or(DecodingFail)?, "t")?, + parallelism: decode_option(items.next().ok_or(DecodingFail)?, "p")?, + }; + + if items.next().is_none() { + Ok(out) + } else { + Err(DecodingFail) + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/encoding.rs b/src/encoding.rs index 1696d7c..05cefd1 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -6,9 +6,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::context::Context; use base64; +use crate::context::Context; + /// Gets the base64 encoded length of a byte slice with the specified length. pub fn base64_len(length: u32) -> u32 { let olen = (length / 3) << 2; diff --git a/src/variant.rs b/src/variant.rs index 456b66e..5191bd4 100644 --- a/src/variant.rs +++ b/src/variant.rs @@ -6,10 +6,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::{fmt, str::FromStr}; + use crate::error::Error; use crate::result::Result; -use std::fmt; -use std::str::FromStr; /// The Argon2 variant. #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)] @@ -27,22 +27,6 @@ pub enum Variant { Argon2id = 2, } -impl FromStr for Variant { - type Err = Error; - - fn from_str(s: &str) -> Result { - match s { - "Argon2d" => Ok(Variant::Argon2d), - "Argon2i" => Ok(Variant::Argon2i), - "Argon2id" => Ok(Variant::Argon2id), - "argon2d" => Ok(Variant::Argon2d), - "argon2i" => Ok(Variant::Argon2i), - "argon2id" => Ok(Variant::Argon2id), - _ => Err(Error::DecodingFail), - } - } -} - impl Variant { /// Gets the lowercase string slice representation of the variant. pub fn as_lowercase_str(&self) -> &'static str { @@ -83,6 +67,22 @@ impl Variant { } } +impl FromStr for Variant { + type Err = Error; + + fn from_str(s: &str) -> Result { + match s { + "Argon2d" => Ok(Variant::Argon2d), + "Argon2i" => Ok(Variant::Argon2i), + "Argon2id" => Ok(Variant::Argon2id), + "argon2d" => Ok(Variant::Argon2d), + "argon2i" => Ok(Variant::Argon2i), + "argon2id" => Ok(Variant::Argon2id), + _ => Err(Error::DecodingFail), + } + } +} + impl Default for Variant { fn default() -> Variant { Variant::Argon2i @@ -98,9 +98,10 @@ impl fmt::Display for Variant { #[cfg(test)] mod tests { + use std::str::FromStr; + use crate::error::Error; use crate::variant::Variant; - use std::str::FromStr; #[test] fn as_lowercase_str_returns_correct_str() { diff --git a/src/version.rs b/src/version.rs index 145e582..4ce7503 100644 --- a/src/version.rs +++ b/src/version.rs @@ -6,10 +6,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::{fmt, str::FromStr}; + use crate::error::Error; use crate::result::Result; -use std::fmt; -use std::str::FromStr; /// The Argon2 version. #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)] @@ -21,19 +21,6 @@ pub enum Version { Version13 = 0x13, } -impl FromStr for Version { - type Err = Error; - - /// Attempts to create a version from a string slice. - fn from_str(s: &str) -> Result { - match s { - "16" => Ok(Version::Version10), - "19" => Ok(Version::Version13), - _ => Err(Error::DecodingFail), - } - } -} - impl Version { /// Gets the u32 representation of the version. pub fn as_u32(&self) -> u32 { @@ -50,6 +37,19 @@ impl Version { } } +impl FromStr for Version { + type Err = Error; + + /// Attempts to create a version from a string slice. + fn from_str(str: &str) -> Result { + match str { + "16" => Ok(Version::Version10), + "19" => Ok(Version::Version13), + _ => Err(Error::DecodingFail), + } + } +} + impl Default for Version { fn default() -> Version { Version::Version13 @@ -65,9 +65,10 @@ impl fmt::Display for Version { #[cfg(test)] mod tests { + use std::str::FromStr; + use crate::error::Error; use crate::version::Version; - use std::str::FromStr; #[test] fn as_u32_returns_correct_u32() { diff --git a/tests/integration_test.rs b/tests/integration_test.rs index fee496c..0d6a3ce 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -1053,11 +1053,7 @@ fn hash_test( hex: &str, enc: &str, ) { - let threads = if cfg!(feature = "crossbeam-utils") { - p - } else { - 1 - }; + let threads = if cfg!(feature = "crossbeam-utils") { p } else { 1 }; let config = Config { variant: var, version: ver, From 3ab4afbc59a9eb0a46cdee961e99512299127493 Mon Sep 17 00:00:00 2001 From: Semen Evdokimov Date: Fri, 25 Dec 2020 14:49:17 +0300 Subject: [PATCH 6/7] Fix --- src/argon2.rs | 21 ++++++--------------- src/encoding.rs | 2 -- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/src/argon2.rs b/src/argon2.rs index 26f1c79..82de93e 100644 --- a/src/argon2.rs +++ b/src/argon2.rs @@ -16,7 +16,6 @@ use crate::result::Result; use crate::thread_mode::ThreadMode; use crate::variant::Variant; use crate::version::Version; -use std::str::FromStr; use constant_time_eq::constant_time_eq; @@ -548,7 +547,7 @@ pub fn verify_encoded(encoded: &str, pwd: &[u8]) -> Result { /// assert!(res); /// ``` pub fn verify_encoded_ext(encoded: &str, pwd: &[u8], secret: &[u8], ad: &[u8]) -> Result { - verify_digest_ext(&Digest::from_str(encoded)?, pwd, secret, ad) + verify_digest_ext(&encoded.parse()?, pwd, secret, ad) } /// Verifies the password with the [`Digest`]. @@ -557,14 +556,12 @@ pub fn verify_encoded_ext(encoded: &str, pwd: &[u8], secret: &[u8], ad: &[u8]) - /// /// ``` /// use argon2::Digest; -/// use std::str::FromStr as _; /// /// let enc = "$argon2i$v=19$m=4096,t=3,p=1$c29tZXNhbHQ\ /// $iWh06vD8Fy27wf9npn6FXWiCX4K6pW6Ue1Bnzz07Z8A"; -/// let digest = Digest::from_str(enc).unwrap(); /// -/// let is_matches = argon2::verify_digest(&digest, b"password").unwrap(); -/// assert!(is_matches); +/// let matches = argon2::verify_digest(&enc.parse().unwrap(), b"password").unwrap(); +/// assert!(matches); /// ``` pub fn verify_digest(decoded: &Digest, pwd: &[u8]) -> Result { verify_digest_ext(&decoded, pwd, &[], &[]) @@ -576,25 +573,19 @@ pub fn verify_digest(decoded: &Digest, pwd: &[u8]) -> Result { /// /// ``` /// use argon2::Digest; -/// use std::str::FromStr as _; /// /// let enc = "$argon2i$v=19$m=4096,t=3,p=1$c29tZXNhbHQ\ /// $OlcSvlN20Lz43sK3jhCJ9K04oejhiY0AmI+ck6nuETo"; -/// let digest = Digest::from_str(enc).unwrap(); /// /// let pwd = b"password"; /// let secret = b"secret"; /// let ad = b"ad"; /// -/// let is_matches = argon2::verify_digest_ext(&digest, pwd, secret, ad).unwrap(); -/// assert_eq!(is_matches); +/// let matches = argon2::verify_digest_ext(&enc.parse().unwrap(), pwd, secret, ad).unwrap(); +/// assert!(matches) /// ``` pub fn verify_digest_ext(decoded: &Digest, pwd: &[u8], secret: &[u8], ad: &[u8]) -> Result { - let threads = if cfg!(feature = "crossbeam-utils") { - decoded.parallelism - } else { - 1 - }; + let threads = if cfg!(feature = "crossbeam-utils") { decoded.parallelism } else { 1 }; let config = Config { variant: decoded.variant, version: decoded.version, diff --git a/src/encoding.rs b/src/encoding.rs index 05cefd1..9bf5b39 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -52,11 +52,9 @@ mod tests { use crate::config::Config; #[cfg(feature = "crossbeam-utils")] use crate::context::Context; - use crate::digest::Digest; #[cfg(feature = "crossbeam-utils")] use crate::encoding::encode_string; use crate::encoding::{base64_len, num_len}; - use crate::error::Error; #[cfg(feature = "crossbeam-utils")] use crate::thread_mode::ThreadMode; use crate::variant::Variant; From a6e8a3f6775487839de1ce037f3b8e6cbe6fe2f6 Mon Sep 17 00:00:00 2001 From: Semen Evdokimov Date: Fri, 25 Dec 2020 15:21:59 +0300 Subject: [PATCH 7/7] Fix fmt --- src/argon2.rs | 36 ++++++------------------------------ 1 file changed, 6 insertions(+), 30 deletions(-) diff --git a/src/argon2.rs b/src/argon2.rs index 82de93e..1e30ade 100644 --- a/src/argon2.rs +++ b/src/argon2.rs @@ -194,11 +194,7 @@ pub fn hash_encoded_old( ad: &[u8], hash_len: u32, ) -> Result { - let threads = if cfg!(feature = "crossbeam-utils") { - threads - } else { - 1 - }; + let threads = if cfg!(feature = "crossbeam-utils") { threads } else { 1 }; let config = Config { variant, version, @@ -268,11 +264,7 @@ pub fn hash_encoded_std( salt: &[u8], hash_len: u32, ) -> Result { - let threads = if cfg!(feature = "crossbeam-utils") { - parallelism - } else { - 1 - }; + let threads = if cfg!(feature = "crossbeam-utils") { parallelism } else { 1 }; let config = Config { variant, version, @@ -421,11 +413,7 @@ pub fn hash_raw_old( ad: &[u8], hash_len: u32, ) -> Result> { - let threads = if cfg!(feature = "crossbeam-utils") { - threads - } else { - 1 - }; + let threads = if cfg!(feature = "crossbeam-utils") { threads } else { 1 }; let config = Config { variant, version, @@ -495,11 +483,7 @@ pub fn hash_raw_std( salt: &[u8], hash_len: u32, ) -> Result> { - let threads = if cfg!(feature = "crossbeam-utils") { - parallelism - } else { - 1 - }; + let threads = if cfg!(feature = "crossbeam-utils") { parallelism } else { 1 }; let config = Config { variant, version, @@ -697,11 +681,7 @@ pub fn verify_raw_old( ad: &[u8], hash: &[u8], ) -> Result { - let threads = if cfg!(feature = "crossbeam-utils") { - threads - } else { - 1 - }; + let threads = if cfg!(feature = "crossbeam-utils") { threads } else { 1 }; let config = Config { variant, version, @@ -778,11 +758,7 @@ pub fn verify_raw_std( salt: &[u8], hash: &[u8], ) -> Result { - let threads = if cfg!(feature = "crossbeam-utils") { - parallelism - } else { - 1 - }; + let threads = if cfg!(feature = "crossbeam-utils") { parallelism } else { 1 }; let config = Config { variant, version,