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 diff --git a/src/argon2.rs b/src/argon2.rs index 15d272a..1e30ade 100644 --- a/src/argon2.rs +++ b/src/argon2.rs @@ -9,6 +9,7 @@ use crate::config::Config; use crate::context::Context; use crate::core; +use crate::digest::Digest; use crate::encoding; use crate::memory::Memory; use crate::result::Result; @@ -530,7 +531,44 @@ 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)?; + verify_digest_ext(&encoded.parse()?, pwd, secret, ad) +} + +/// Verifies the password with the [`Digest`]. +/// +/// # Example +/// +/// ``` +/// use argon2::Digest; +/// +/// let enc = "$argon2i$v=19$m=4096,t=3,p=1$c29tZXNhbHQ\ +/// $iWh06vD8Fy27wf9npn6FXWiCX4K6pW6Ue1Bnzz07Z8A"; +/// +/// 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, &[], &[]) +} + +/// Verifies the password with the [`Digest`], secret and associated data. +/// +/// # Example +/// +/// ``` +/// use argon2::Digest; +/// +/// let enc = "$argon2i$v=19$m=4096,t=3,p=1$c29tZXNhbHQ\ +/// $OlcSvlN20Lz43sK3jhCJ9K04oejhiY0AmI+ck6nuETo"; +/// +/// let pwd = b"password"; +/// let secret = b"secret"; +/// let ad = b"ad"; +/// +/// 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 config = Config { variant: decoded.variant, diff --git a/src/decoded.rs b/src/decoded.rs deleted file mode 100644 index ff772c1..0000000 --- a/src/decoded.rs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) 2017 Martijn Rijkeboer -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use crate::variant::Variant; -use crate::version::Version; - -/// Structure that contains the decoded data. -#[derive(Debug, Eq, PartialEq)] -pub struct Decoded { - /// The variant. - pub variant: Variant, - - /// The version. - pub version: Version, - - /// The amount of memory requested (KiB). - pub mem_cost: u32, - - /// The number of passes. - pub time_cost: u32, - - /// The parallelism. - pub parallelism: u32, - - /// The salt. - pub salt: Vec, - - /// The hash. - pub hash: Vec, -} diff --git a/src/digest.rs b/src/digest.rs new file mode 100644 index 0000000..0fa09e8 --- /dev/null +++ b/src/digest.rs @@ -0,0 +1,325 @@ +// Copyright (c) 2017 Martijn Rijkeboer +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// 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; + +/// 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 Digest { + /// The variant of [Argon2] being used. + /// + /// [Argon2]: https://en.wikipedia.org/wiki/Argon2 + pub variant: Variant, + + /// 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 iterations (or passes) over the memory. + pub time_cost: u32, + + /// The number of threads (or lanes) used by the algorithm. + pub parallelism: u32, + + /// The salt. + pub salt: Vec, + + /// The hash. + pub hash: Vec, +} + +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. + 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(Digest { + 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(Digest { + 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) + } + } +} + +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::*; + + #[test] + fn decode_string_with_version10_returns_correct_result() { + let encoded = "$argon2i$v=16$m=4096,t=3,p=1\ + $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; + let expected = Digest { + 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 = Digest::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 = Digest { + 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 = Digest::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 = Digest { + 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 = Digest::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 = Digest::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 = Digest::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 = Digest::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 = Digest::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 = Digest::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 = Digest::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 = Digest::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 = Digest::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 = Digest::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 = Digest::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 = Digest::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 = Digest::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 = Digest::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 = Digest::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 = Digest::from_str(encoded); + assert_eq!(result, Err(Error::DecodingFail)); + } +} diff --git a/src/encoding.rs b/src/encoding.rs index 438c046..9bf5b39 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -6,20 +6,9 @@ // option. This file may not be copied, modified, or distributed // 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, -} +use crate::context::Context; /// Gets the base64 encoded length of a byte slice with the specified length. pub fn base64_len(length: u32) -> u32 { @@ -31,105 +20,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!( @@ -162,11 +52,9 @@ mod tests { use crate::config::Config; #[cfg(feature = "crossbeam-utils")] use crate::context::Context; - use crate::decoded::Decoded; #[cfg(feature = "crossbeam-utils")] use crate::encoding::encode_string; - use crate::encoding::{base64_len, decode_string, num_len}; - use crate::error::Error; + use crate::encoding::{base64_len, num_len}; #[cfg(feature = "crossbeam-utils")] use crate::thread_mode::ThreadMode; use crate::variant::Variant; @@ -192,177 +80,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..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,6 +100,7 @@ mod version; pub use crate::argon2::*; pub use crate::config::Config; +pub use crate::digest::Digest; 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..5191bd4 100644 --- a/src/variant.rs +++ b/src/variant.rs @@ -6,9 +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; /// The Argon2 variant. #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)] @@ -55,19 +56,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 { @@ -79,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 @@ -94,6 +98,8 @@ impl fmt::Display for Variant { #[cfg(test)] mod tests { + use std::str::FromStr; + use crate::error::Error; use crate::variant::Variant; diff --git a/src/version.rs b/src/version.rs index edda427..4ce7503 100644 --- a/src/version.rs +++ b/src/version.rs @@ -6,9 +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; /// The Argon2 version. #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)] @@ -26,15 +27,6 @@ impl Version { *self as u32 } - /// Attempts to create a version from a string slice. - pub fn from_str(str: &str) -> Result { - match str { - "16" => Ok(Version::Version10), - "19" => Ok(Version::Version13), - _ => Err(Error::DecodingFail), - } - } - /// Attempts to create a version from an u32. pub fn from_u32(val: u32) -> Result { match val { @@ -45,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 @@ -60,6 +65,8 @@ impl fmt::Display for Version { #[cfg(test)] mod tests { + use std::str::FromStr; + use crate::error::Error; use crate::version::Version;