diff --git a/sha-crypt/src/errors.rs b/sha-crypt/src/errors.rs index d471cfdd..e7bf4d24 100644 --- a/sha-crypt/src/errors.rs +++ b/sha-crypt/src/errors.rs @@ -2,6 +2,9 @@ use core::fmt; +/// Result type for the `sha-crypt` crate with its [`Error`] type. +pub type Result = core::result::Result; + /// Error type. #[derive(Debug)] pub enum Error { diff --git a/sha-crypt/src/lib.rs b/sha-crypt/src/lib.rs index 0a75d9cb..96cdd471 100644 --- a/sha-crypt/src/lib.rs +++ b/sha-crypt/src/lib.rs @@ -39,8 +39,8 @@ mod simple; pub use crate::{ consts::{BLOCK_SIZE_SHA256, BLOCK_SIZE_SHA512}, - errors::Error, - params::{ROUNDS_DEFAULT, ROUNDS_MAX, ROUNDS_MIN, Sha256Params, Sha512Params}, + errors::{Error, Result}, + params::Params, }; #[cfg(feature = "simple")] @@ -53,25 +53,17 @@ pub use { use alloc::vec::Vec; use sha2::{Digest, Sha256, Sha512}; -/// The SHA512 crypt function returned as byte vector +/// The SHA-256 crypt function returned as byte vector /// /// If the provided hash is longer than defs::SALT_MAX_LEN character, it will /// be stripped down to defs::SALT_MAX_LEN characters. /// /// # Arguments -/// - `password` - The password to process as a byte vector -/// - `salt` - The salt value to use as a byte vector -/// - `params` - The Sha512Params to use +/// - `password`: the password to process as a byte vector +/// - `salt`: the salt value to use as a byte vector +/// - `params`: the parameters to use /// **WARNING: Make sure to compare this value in constant time!** -/// -/// # Returns -/// - `Ok(())` if calculation was successful -/// - `Err(errors::CryptError)` otherwise -pub fn sha512_crypt( - password: &[u8], - salt: &[u8], - params: &Sha512Params, -) -> [u8; BLOCK_SIZE_SHA512] { +pub fn sha256_crypt(password: &[u8], salt: &[u8], params: &Params) -> [u8; BLOCK_SIZE_SHA256] { let pw_len = password.len(); let salt_len = salt.len(); @@ -81,10 +73,10 @@ pub fn sha512_crypt( }; let salt_len = salt.len(); - let digest_a = sha512crypt_intermediate(password, salt); + let digest_a = sha256crypt_intermediate(password, salt); // 13. - let mut hasher_alt = Sha512::default(); + let mut hasher_alt = Sha256::default(); // 14. for _ in 0..pw_len { @@ -99,7 +91,7 @@ pub fn sha512_crypt( let p_vec = produce_byte_seq(pw_len, &dp); // 17. - hasher_alt = Sha512::default(); + hasher_alt = Sha256::default(); // 18. // For every character in the password add the entire password. @@ -116,11 +108,11 @@ pub fn sha512_crypt( let s_vec = produce_byte_seq(salt_len, &ds); let mut digest_c = digest_a; - // Repeatedly run the collected hash value through SHA512 to burn + // Repeatedly run the collected hash value through SHA256 to burn // CPU cycles for i in 0..params.rounds { // new hasher - let mut hasher = Sha512::default(); + let mut hasher = Sha256::default(); // Add key or last result if (i & 1) != 0 { @@ -152,25 +144,17 @@ pub fn sha512_crypt( digest_c } -/// The SHA256 crypt function returned as byte vector +/// The SHA-512 crypt function returned as byte vector /// /// If the provided hash is longer than defs::SALT_MAX_LEN character, it will /// be stripped down to defs::SALT_MAX_LEN characters. /// /// # Arguments -/// - `password` - The password to process as a byte vector +/// - `password`The password to process as a byte vector /// - `salt` - The salt value to use as a byte vector -/// - `params` - The Sha256Params to use +/// - `params` - The parameters to use /// **WARNING: Make sure to compare this value in constant time!** -/// -/// # Returns -/// - `Ok(())` if calculation was successful -/// - `Err(errors::CryptError)` otherwise -pub fn sha256_crypt( - password: &[u8], - salt: &[u8], - params: &Sha256Params, -) -> [u8; BLOCK_SIZE_SHA256] { +pub fn sha512_crypt(password: &[u8], salt: &[u8], params: &Params) -> [u8; BLOCK_SIZE_SHA512] { let pw_len = password.len(); let salt_len = salt.len(); @@ -180,10 +164,10 @@ pub fn sha256_crypt( }; let salt_len = salt.len(); - let digest_a = sha256crypt_intermediate(password, salt); + let digest_a = sha512crypt_intermediate(password, salt); // 13. - let mut hasher_alt = Sha256::default(); + let mut hasher_alt = Sha512::default(); // 14. for _ in 0..pw_len { @@ -198,7 +182,7 @@ pub fn sha256_crypt( let p_vec = produce_byte_seq(pw_len, &dp); // 17. - hasher_alt = Sha256::default(); + hasher_alt = Sha512::default(); // 18. // For every character in the password add the entire password. @@ -215,11 +199,11 @@ pub fn sha256_crypt( let s_vec = produce_byte_seq(salt_len, &ds); let mut digest_c = digest_a; - // Repeatedly run the collected hash value through SHA256 to burn + // Repeatedly run the collected hash value through SHA512 to burn // CPU cycles for i in 0..params.rounds { // new hasher - let mut hasher = Sha256::default(); + let mut hasher = Sha512::default(); // Add key or last result if (i & 1) != 0 { @@ -251,28 +235,15 @@ pub fn sha256_crypt( digest_c } -fn produce_byte_seq(len: usize, fill_from: &[u8]) -> Vec { - let bs = fill_from.len(); - let mut seq: Vec = vec![0; len]; - let mut offset: usize = 0; - for _ in 0..(len / bs) { - seq[offset..offset + bs].clone_from_slice(fill_from); - offset += bs; - } - let from_slice = &fill_from[..(len % bs)]; - seq[offset..offset + (len % bs)].clone_from_slice(from_slice); - seq -} - -fn sha512crypt_intermediate(password: &[u8], salt: &[u8]) -> [u8; BLOCK_SIZE_SHA512] { +fn sha256crypt_intermediate(password: &[u8], salt: &[u8]) -> [u8; BLOCK_SIZE_SHA256] { let pw_len = password.len(); - let mut hasher = Sha512::default(); + let mut hasher = Sha256::default(); hasher.update(password); hasher.update(salt); // 4. - let mut hasher_alt = Sha512::default(); + let mut hasher_alt = Sha256::default(); // 5. hasher_alt.update(password); // 6. @@ -283,11 +254,11 @@ fn sha512crypt_intermediate(password: &[u8], salt: &[u8]) -> [u8; BLOCK_SIZE_SHA let digest_b = hasher_alt.finalize(); // 9. - for _ in 0..(pw_len / BLOCK_SIZE_SHA512) { + for _ in 0..(pw_len / BLOCK_SIZE_SHA256) { hasher.update(digest_b); } // 10. - hasher.update(&digest_b[..(pw_len % BLOCK_SIZE_SHA512)]); + hasher.update(&digest_b[..(pw_len % BLOCK_SIZE_SHA256)]); // 11 let mut n = pw_len; @@ -307,15 +278,15 @@ fn sha512crypt_intermediate(password: &[u8], salt: &[u8]) -> [u8; BLOCK_SIZE_SHA hasher.finalize().as_slice().try_into().unwrap() } -fn sha256crypt_intermediate(password: &[u8], salt: &[u8]) -> [u8; BLOCK_SIZE_SHA256] { +fn sha512crypt_intermediate(password: &[u8], salt: &[u8]) -> [u8; BLOCK_SIZE_SHA512] { let pw_len = password.len(); - let mut hasher = Sha256::default(); + let mut hasher = Sha512::default(); hasher.update(password); hasher.update(salt); // 4. - let mut hasher_alt = Sha256::default(); + let mut hasher_alt = Sha512::default(); // 5. hasher_alt.update(password); // 6. @@ -326,11 +297,11 @@ fn sha256crypt_intermediate(password: &[u8], salt: &[u8]) -> [u8; BLOCK_SIZE_SHA let digest_b = hasher_alt.finalize(); // 9. - for _ in 0..(pw_len / BLOCK_SIZE_SHA256) { + for _ in 0..(pw_len / BLOCK_SIZE_SHA512) { hasher.update(digest_b); } // 10. - hasher.update(&digest_b[..(pw_len % BLOCK_SIZE_SHA256)]); + hasher.update(&digest_b[..(pw_len % BLOCK_SIZE_SHA512)]); // 11 let mut n = pw_len; @@ -349,3 +320,16 @@ fn sha256crypt_intermediate(password: &[u8], salt: &[u8]) -> [u8; BLOCK_SIZE_SHA // 12. hasher.finalize().as_slice().try_into().unwrap() } + +fn produce_byte_seq(len: usize, fill_from: &[u8]) -> Vec { + let bs = fill_from.len(); + let mut seq: Vec = vec![0; len]; + let mut offset: usize = 0; + for _ in 0..(len / bs) { + seq[offset..offset + bs].clone_from_slice(fill_from); + offset += bs; + } + let from_slice = &fill_from[..(len % bs)]; + seq[offset..offset + (len % bs)].clone_from_slice(from_slice); + seq +} diff --git a/sha-crypt/src/params.rs b/sha-crypt/src/params.rs index febadaf4..b6969dee 100644 --- a/sha-crypt/src/params.rs +++ b/sha-crypt/src/params.rs @@ -1,118 +1,79 @@ //! Algorithm parameters. -use crate::errors; +use crate::{Error, Result}; use core::{ default::Default, fmt::{self, Display}, str::FromStr, }; -/// Default number of rounds. -pub const ROUNDS_DEFAULT: u32 = 5_000; - -/// Minimum number of rounds allowed. -pub const ROUNDS_MIN: u32 = 1_000; - -/// Maximum number of rounds allowed. -pub const ROUNDS_MAX: u32 = 999_999_999; - /// Algorithm parameters. #[derive(Debug, Clone)] -pub struct Sha512Params { +pub struct Params { + /// Number of times to apply the digest function pub(crate) rounds: u32, } -impl Default for Sha512Params { - fn default() -> Self { - Sha512Params { - rounds: ROUNDS_DEFAULT, - } - } -} +impl Params { + /// Default number of rounds. + pub const ROUNDS_DEFAULT: u32 = 5_000; -impl Display for Sha512Params { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "rounds={}", self.rounds) - } -} + /// Minimum number of rounds allowed. + pub const ROUNDS_MIN: u32 = 1_000; -impl FromStr for Sha512Params { - type Err = errors::Error; + /// Maximum number of rounds allowed. + pub const ROUNDS_MAX: u32 = 999_999_999; - fn from_str(_s: &str) -> Result { - todo!() - } -} - -impl Sha512Params { /// Create new algorithm parameters. - pub fn new(rounds: u32) -> Result { - if (ROUNDS_MIN..=ROUNDS_MAX).contains(&rounds) { - Ok(Sha512Params { rounds }) - } else { - Err(errors::Error::RoundsError) + pub fn new(rounds: u32) -> Result { + match rounds { + Self::ROUNDS_MIN..=Self::ROUNDS_MAX => Ok(Params { rounds }), + _ => Err(Error::RoundsError), } } } -/// Algorithm parameters. -#[derive(Debug, Clone)] -pub struct Sha256Params { - pub(crate) rounds: u32, -} - -impl Default for Sha256Params { +impl Default for Params { fn default() -> Self { - Sha256Params { - rounds: ROUNDS_DEFAULT, + Params { + rounds: Self::ROUNDS_DEFAULT, } } } -impl Display for Sha256Params { +impl Display for Params { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "rounds={}", self.rounds) } } -impl FromStr for Sha256Params { - type Err = errors::Error; +impl FromStr for Params { + type Err = Error; - fn from_str(_s: &str) -> Result { + fn from_str(_s: &str) -> Result { todo!() } } -impl Sha256Params { - /// Create new algorithm parameters. - pub fn new(rounds: u32) -> Result { - if (ROUNDS_MIN..=ROUNDS_MAX).contains(&rounds) { - Ok(Sha256Params { rounds }) - } else { - Err(errors::Error::RoundsError) - } - } -} - #[cfg(test)] mod tests { - use super::{ROUNDS_MAX, ROUNDS_MIN, Sha256Params, Sha512Params}; + use super::Params; #[test] fn test_sha256_crypt_invalid_rounds() { - let params = Sha256Params::new(ROUNDS_MAX + 1); + let params = Params::new(Params::ROUNDS_MAX + 1); assert!(params.is_err()); - let params = Sha256Params::new(ROUNDS_MIN - 1); + let params = Params::new(Params::ROUNDS_MIN - 1); assert!(params.is_err()); } #[test] fn test_sha512_crypt_invalid_rounds() { - let params = Sha512Params::new(ROUNDS_MAX + 1); + let params = Params::new(Params::ROUNDS_MAX + 1); assert!(params.is_err()); - let params = Sha512Params::new(ROUNDS_MIN - 1); + let params = Params::new(Params::ROUNDS_MIN - 1); assert!(params.is_err()); } } diff --git a/sha-crypt/src/simple.rs b/sha-crypt/src/simple.rs index 53418c48..5d524f36 100644 --- a/sha-crypt/src/simple.rs +++ b/sha-crypt/src/simple.rs @@ -1,7 +1,7 @@ //! Implementation of the `password-hash` crate API. use crate::{ - BLOCK_SIZE_SHA256, BLOCK_SIZE_SHA512, ROUNDS_DEFAULT, Sha256Params, Sha512Params, + BLOCK_SIZE_SHA256, BLOCK_SIZE_SHA512, Params, consts::{MAP_SHA256, MAP_SHA512}, sha256_crypt, sha512_crypt, }; @@ -34,7 +34,7 @@ pub const SHA512_CRYPT: ShaCrypt = ShaCrypt { }; impl CustomizedPasswordHasher for ShaCrypt { - type Params = Sha256Params; + type Params = Params; fn hash_password_customized( &self, @@ -42,7 +42,7 @@ impl CustomizedPasswordHasher for ShaCrypt { salt: &[u8], alg_id: Option<&str>, version: Option, - params: Sha256Params, + params: Params, ) -> Result { match alg_id { Some(SHA256_MCF_ID) | None => (), @@ -59,7 +59,7 @@ impl CustomizedPasswordHasher for ShaCrypt { let mut mcf_hash = PasswordHash::from_id(SHA256_MCF_ID).expect("should have valid ID"); - if params.rounds != ROUNDS_DEFAULT { + if params.rounds != Params::ROUNDS_DEFAULT { mcf_hash .push_str(&format!("{}{}", ROUNDS_PARAM, params.rounds)) .expect("should be valid field"); @@ -74,7 +74,7 @@ impl CustomizedPasswordHasher for ShaCrypt { } impl CustomizedPasswordHasher for ShaCrypt { - type Params = Sha512Params; + type Params = Params; fn hash_password_customized( &self, @@ -82,7 +82,7 @@ impl CustomizedPasswordHasher for ShaCrypt { salt: &[u8], alg_id: Option<&str>, version: Option, - params: Sha512Params, + params: Params, ) -> Result { match alg_id { Some(SHA512_MCF_ID) | None => (), @@ -99,7 +99,7 @@ impl CustomizedPasswordHasher for ShaCrypt { let mut mcf_hash = PasswordHash::from_id(SHA512_MCF_ID).expect("should have valid ID"); - if params.rounds != ROUNDS_DEFAULT { + if params.rounds != Params::ROUNDS_DEFAULT { mcf_hash .push_str(&format!("{}{}", ROUNDS_PARAM, params.rounds)) .expect("should be valid field"); @@ -115,13 +115,13 @@ impl CustomizedPasswordHasher for ShaCrypt { impl PasswordHasher for ShaCrypt { fn hash_password_with_salt(&self, password: &[u8], salt: &[u8]) -> Result { - self.hash_password_customized(password, salt, None, None, Sha256Params::default()) + self.hash_password_customized(password, salt, None, None, Params::default()) } } impl PasswordHasher for ShaCrypt { fn hash_password_with_salt(&self, password: &[u8], salt: &[u8]) -> Result { - self.hash_password_customized(password, salt, None, None, Sha512Params::default()) + self.hash_password_customized(password, salt, None, None, Params::default()) } } @@ -147,13 +147,13 @@ impl PasswordVerifier for ShaCrypt { let mut fields = hash.fields(); let mut next = fields.next().ok_or(Error::EncodingInvalid)?; - let mut params = Sha256Params::default(); + let mut params = Params::default(); // decode params // TODO(tarcieri): `mcf::Field` helper methods for parsing params? if let Some(rounds_str) = next.as_str().strip_prefix(ROUNDS_PARAM) { let rounds = rounds_str.parse().map_err(|_| Error::EncodingInvalid)?; - params = Sha256Params::new(rounds)?; + params = Params::new(rounds)?; next = fields.next().ok_or(Error::EncodingInvalid)?; } @@ -191,13 +191,13 @@ impl PasswordVerifier for ShaCrypt { let mut fields = hash.fields(); let mut next = fields.next().ok_or(Error::EncodingInvalid)?; - let mut params = Sha512Params::default(); + let mut params = Params::default(); // decode params // TODO(tarcieri): `mcf::Field` helper methods for parsing params? if let Some(rounds_str) = next.as_str().strip_prefix(ROUNDS_PARAM) { let rounds = rounds_str.parse().map_err(|_| Error::EncodingInvalid)?; - params = Sha512Params::new(rounds)?; + params = Params::new(rounds)?; next = fields.next().ok_or(Error::EncodingInvalid)?; } @@ -229,7 +229,7 @@ impl PasswordVerifier for ShaCrypt { fn sha256_crypt_transposed( password: &[u8], salt: &[u8], - params: &Sha256Params, + params: &Params, ) -> [u8; BLOCK_SIZE_SHA256] { let output = sha256_crypt(password, salt, params); @@ -245,7 +245,7 @@ fn sha256_crypt_transposed( fn sha512_crypt_transposed( password: &[u8], salt: &[u8], - params: &Sha512Params, + params: &Params, ) -> [u8; BLOCK_SIZE_SHA512] { let output = sha512_crypt(password, salt, params); diff --git a/sha-crypt/tests/simple.rs b/sha-crypt/tests/simple.rs index 8da3c1e8..4225bc22 100644 --- a/sha-crypt/tests/simple.rs +++ b/sha-crypt/tests/simple.rs @@ -3,7 +3,7 @@ use base64ct::{Base64ShaCrypt, Encoding}; use mcf::PasswordHash; use sha_crypt::{ - SHA256_CRYPT, SHA512_CRYPT, Sha256Params, Sha512Params, + Params, SHA256_CRYPT, SHA512_CRYPT, password_hash::{CustomizedPasswordHasher, Error, PasswordVerifier}, }; @@ -93,7 +93,7 @@ fn hash_sha256_crypt() { for t in TEST_VECTORS { if let Ok(salt) = Base64ShaCrypt::decode_vec(&t.salt) { - let params = Sha256Params::new(t.rounds).unwrap(); + let params = Params::new(t.rounds).unwrap(); let result = SHA256_CRYPT .hash_password_with_params(t.input.as_bytes(), &salt, params) .unwrap(); @@ -112,7 +112,7 @@ fn hash_sha512_crypt() { for t in TEST_VECTORS { if let Ok(salt) = Base64ShaCrypt::decode_vec(&t.salt) { - let params = Sha512Params::new(t.rounds).unwrap(); + let params = Params::new(t.rounds).unwrap(); let result = SHA512_CRYPT .hash_password_with_params(t.input.as_bytes(), &salt, params) .unwrap();