Skip to content

Commit

Permalink
sha-crypt: Change parameter type and make hashing infallible (#500)
Browse files Browse the repository at this point in the history
* sha-crypt: Change `rounds` parameter type to `u32`

Its type doesn't need to depend on the target architecture pointer
width.

Fixes #498.

* sha-crypt: Make hashing infallible

The rounds were already checked to be sane in
`Sha{256,512}Parameters::new`.

Fixes #499.
  • Loading branch information
tbu- authored May 17, 2024
1 parent b487053 commit b289d5a
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 62 deletions.
73 changes: 27 additions & 46 deletions sha-crypt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@
//! let params = Sha512Params::new(10_000).expect("RandomError!");
//!
//! // Hash the password for storage
//! let hashed_password = sha512_simple("Not so secure password", &params)
//! .expect("Should not fail");
//! let hashed_password = sha512_simple("Not so secure password", &params);
//!
//! // Verifying a stored password
//! assert!(sha512_check("Not so secure password", &hashed_password).is_ok());
Expand Down Expand Up @@ -96,7 +95,7 @@ pub fn sha512_crypt(
password: &[u8],
salt: &[u8],
params: &Sha512Params,
) -> Result<[u8; BLOCK_SIZE_SHA512], CryptError> {
) -> [u8; BLOCK_SIZE_SHA512] {
let pw_len = password.len();

let salt_len = salt.len();
Expand All @@ -106,10 +105,6 @@ pub fn sha512_crypt(
};
let salt_len = salt.len();

if params.rounds < ROUNDS_MIN || params.rounds > ROUNDS_MAX {
return Err(CryptError::RoundsError);
}

let digest_a = sha512crypt_intermediate(password, salt);

// 13.
Expand Down Expand Up @@ -178,7 +173,7 @@ pub fn sha512_crypt(
digest_c.clone_from_slice(&hasher.finalize());
}

Ok(digest_c)
digest_c
}

/// The SHA256 crypt function returned as byte vector
Expand All @@ -199,7 +194,7 @@ pub fn sha256_crypt(
password: &[u8],
salt: &[u8],
params: &Sha256Params,
) -> Result<[u8; BLOCK_SIZE_SHA256], CryptError> {
) -> [u8; BLOCK_SIZE_SHA256] {
let pw_len = password.len();

let salt_len = salt.len();
Expand All @@ -209,10 +204,6 @@ pub fn sha256_crypt(
};
let salt_len = salt.len();

if params.rounds < ROUNDS_MIN || params.rounds > ROUNDS_MAX {
return Err(CryptError::RoundsError);
}

let digest_a = sha256crypt_intermediate(password, salt);

// 13.
Expand Down Expand Up @@ -281,7 +272,7 @@ pub fn sha256_crypt(
digest_c.clone_from_slice(&hasher.finalize());
}

Ok(digest_c)
digest_c
}

/// Same as sha512_crypt except base64 representation will be returned.
Expand All @@ -295,14 +286,9 @@ pub fn sha256_crypt(
/// # Returns
/// - `Ok(())` if calculation was successful
/// - `Err(errors::CryptError)` otherwise
pub fn sha512_crypt_b64(
password: &[u8],
salt: &[u8],
params: &Sha512Params,
) -> Result<String, CryptError> {
let output = sha512_crypt(password, salt, params)?;
let r = String::from_utf8(b64::encode_sha512(&output).to_vec())?;
Ok(r)
pub fn sha512_crypt_b64(password: &[u8], salt: &[u8], params: &Sha512Params) -> String {
let output = sha512_crypt(password, salt, params);
String::from_utf8(b64::encode_sha512(&output).to_vec()).unwrap()
}

/// Same as sha256_crypt except base64 representation will be returned.
Expand All @@ -316,14 +302,9 @@ pub fn sha512_crypt_b64(
/// # Returns
/// - `Ok(())` if calculation was successful
/// - `Err(errors::CryptError)` otherwise
pub fn sha256_crypt_b64(
password: &[u8],
salt: &[u8],
params: &Sha256Params,
) -> Result<String, CryptError> {
let output = sha256_crypt(password, salt, params)?;
let r = String::from_utf8(b64::encode_sha256(&output).to_vec())?;
Ok(r)
pub fn sha256_crypt_b64(password: &[u8], salt: &[u8], params: &Sha256Params) -> String {
let output = sha256_crypt(password, salt, params);
String::from_utf8(b64::encode_sha256(&output).to_vec()).unwrap()
}

/// Simple interface for generating a SHA512 password hash.
Expand All @@ -339,15 +320,15 @@ pub fn sha256_crypt_b64(
/// [1]: https://www.akkadia.org/drepper/SHA-crypt.txt
#[cfg(feature = "simple")]
#[cfg_attr(docsrs, doc(cfg(feature = "simple")))]
pub fn sha512_simple(password: &str, params: &Sha512Params) -> Result<String, CryptError> {
pub fn sha512_simple(password: &str, params: &Sha512Params) -> String {
let rng = thread_rng();

let salt: String = rng
.sample_iter(&ShaCryptDistribution)
.take(SALT_MAX_LEN)
.collect();

let out = sha512_crypt(password.as_bytes(), salt.as_bytes(), params)?;
let out = sha512_crypt(password.as_bytes(), salt.as_bytes(), params);

let mut result = String::new();
result.push_str(SHA512_SALT_PREFIX);
Expand All @@ -357,9 +338,9 @@ pub fn sha512_simple(password: &str, params: &Sha512Params) -> Result<String, Cr
}
result.push_str(&salt);
result.push('$');
let s = String::from_utf8(b64::encode_sha512(&out).to_vec())?;
let s = String::from_utf8(b64::encode_sha512(&out).to_vec()).unwrap();
result.push_str(&s);
Ok(result)
result
}

/// Simple interface for generating a SHA256 password hash.
Expand All @@ -375,15 +356,15 @@ pub fn sha512_simple(password: &str, params: &Sha512Params) -> Result<String, Cr
/// [1]: https://www.akkadia.org/drepper/SHA-crypt.txt
#[cfg(feature = "simple")]
#[cfg_attr(docsrs, doc(cfg(feature = "simple")))]
pub fn sha256_simple(password: &str, params: &Sha256Params) -> Result<String, CryptError> {
pub fn sha256_simple(password: &str, params: &Sha256Params) -> String {
let rng = thread_rng();

let salt: String = rng
.sample_iter(&ShaCryptDistribution)
.take(SALT_MAX_LEN)
.collect();

let out = sha256_crypt(password.as_bytes(), salt.as_bytes(), params)?;
let out = sha256_crypt(password.as_bytes(), salt.as_bytes(), params);

let mut result = String::new();
result.push_str(SHA256_SALT_PREFIX);
Expand All @@ -393,9 +374,9 @@ pub fn sha256_simple(password: &str, params: &Sha256Params) -> Result<String, Cr
}
result.push_str(&salt);
result.push('$');
let s = String::from_utf8(b64::encode_sha256(&out).to_vec())?;
let s = String::from_utf8(b64::encode_sha256(&out).to_vec()).unwrap();
result.push_str(&s);
Ok(result)
result
}

/// Checks that given password matches provided hash.
Expand Down Expand Up @@ -459,13 +440,13 @@ pub fn sha512_check(password: &str, hashed_value: &str) -> Result<(), CheckError
));
}

let params = Sha512Params { rounds };

let output = match sha512_crypt(password.as_bytes(), salt.as_bytes(), &params) {
Ok(v) => v,
let params = match Sha512Params::new(rounds) {
Ok(p) => p,
Err(e) => return Err(CheckError::Crypt(e)),
};

let output = sha512_crypt(password.as_bytes(), salt.as_bytes(), &params);

let hash = b64::decode_sha512(hash.as_bytes())?;

use subtle::ConstantTimeEq;
Expand Down Expand Up @@ -537,13 +518,13 @@ pub fn sha256_check(password: &str, hashed_value: &str) -> Result<(), CheckError
));
}

let params = Sha256Params { rounds };

let output = match sha256_crypt(password.as_bytes(), salt.as_bytes(), &params) {
Ok(v) => v,
let params = match Sha256Params::new(rounds) {
Ok(p) => p,
Err(e) => return Err(CheckError::Crypt(e)),
};

let output = sha256_crypt(password.as_bytes(), salt.as_bytes(), &params);

let hash = b64::decode_sha256(hash.as_bytes())?;

use subtle::ConstantTimeEq;
Expand Down
14 changes: 7 additions & 7 deletions sha-crypt/src/params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,18 @@ use crate::errors;
use core::default::Default;

/// Default number of rounds.
pub const ROUNDS_DEFAULT: usize = 5_000;
pub const ROUNDS_DEFAULT: u32 = 5_000;

/// Minimum number of rounds allowed.
pub const ROUNDS_MIN: usize = 1_000;
pub const ROUNDS_MIN: u32 = 1_000;

/// Maximum number of rounds allowed.
pub const ROUNDS_MAX: usize = 999_999_999;
pub const ROUNDS_MAX: u32 = 999_999_999;

/// Algorithm parameters.
#[derive(Debug, Clone)]
pub struct Sha512Params {
pub(crate) rounds: usize,
pub(crate) rounds: u32,
}

impl Default for Sha512Params {
Expand All @@ -28,7 +28,7 @@ impl Default for Sha512Params {

impl Sha512Params {
/// Create new algorithm parameters.
pub fn new(rounds: usize) -> Result<Sha512Params, errors::CryptError> {
pub fn new(rounds: u32) -> Result<Sha512Params, errors::CryptError> {
if (ROUNDS_MIN..=ROUNDS_MAX).contains(&rounds) {
Ok(Sha512Params { rounds })
} else {
Expand All @@ -40,7 +40,7 @@ impl Sha512Params {
/// Algorithm parameters.
#[derive(Debug, Clone)]
pub struct Sha256Params {
pub(crate) rounds: usize,
pub(crate) rounds: u32,
}

impl Default for Sha256Params {
Expand All @@ -53,7 +53,7 @@ impl Default for Sha256Params {

impl Sha256Params {
/// Create new algorithm parameters.
pub fn new(rounds: usize) -> Result<Sha256Params, errors::CryptError> {
pub fn new(rounds: u32) -> Result<Sha256Params, errors::CryptError> {
if (ROUNDS_MIN..=ROUNDS_MAX).contains(&rounds) {
Ok(Sha256Params { rounds })
} else {
Expand Down
14 changes: 5 additions & 9 deletions sha-crypt/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ struct TestVector {
salt: &'static str,
result_sha256: &'static str,
result_sha512: &'static str,
rounds: usize,
rounds: u32,
}

const TEST_VECTORS: &[TestVector] = &[
Expand Down Expand Up @@ -100,7 +100,7 @@ const TEST_VECTORS: &[TestVector] = &[
fn test_sha512_crypt() {
for t in TEST_VECTORS {
let params = Sha512Params::new(t.rounds).expect("Rounds error");
let result = sha512_crypt_b64(t.input.as_bytes(), t.salt.as_bytes(), &params).unwrap();
let result = sha512_crypt_b64(t.input.as_bytes(), t.salt.as_bytes(), &params);
assert!(result == t.result_sha512);
}
}
Expand All @@ -109,7 +109,7 @@ fn test_sha512_crypt() {
fn test_sha256_crypt() {
for t in TEST_VECTORS {
let params = Sha256Params::new(t.rounds).expect("Rounds error");
let result = sha256_crypt_b64(t.input.as_bytes(), t.salt.as_bytes(), &params).unwrap();
let result = sha256_crypt_b64(t.input.as_bytes(), t.salt.as_bytes(), &params);
println!("result {:?}", result);
println!("correct {:?}", t.result_sha256);
assert!(result == t.result_sha256);
Expand Down Expand Up @@ -172,9 +172,7 @@ fn test_sha512_simple_check_roundtrip() {
let pw = "this is my password";
let params = Sha512Params::new(5_000).expect("Rounds error");

let r = sha512_simple(pw, &params);
assert!(r.is_ok());
let hash = r.unwrap();
let hash = sha512_simple(pw, &params);

let c_r = sha512_check(pw, &hash);
assert!(c_r.is_ok());
Expand All @@ -186,9 +184,7 @@ fn test_sha256_simple_check_roundtrip() {
let pw = "this is my password";
let params = Sha256Params::new(5_000).expect("Rounds error");

let r = sha256_simple(pw, &params);
assert!(r.is_ok());
let hash = r.unwrap();
let hash = sha256_simple(pw, &params);

let c_r = sha256_check(pw, &hash);
assert!(c_r.is_ok());
Expand Down

0 comments on commit b289d5a

Please sign in to comment.