Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions sha-crypt/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

use core::fmt;

/// Result type for the `sha-crypt` crate with its [`Error`] type.
pub type Result<T> = core::result::Result<T, Error>;

/// Error type.
#[derive(Debug)]
pub enum Error {
Expand Down
104 changes: 44 additions & 60 deletions sha-crypt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand All @@ -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();
Expand All @@ -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 {
Expand All @@ -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.
Expand All @@ -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 {
Expand Down Expand Up @@ -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();
Expand All @@ -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 {
Expand All @@ -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.
Expand All @@ -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 {
Expand Down Expand Up @@ -251,28 +235,15 @@ pub fn sha256_crypt(
digest_c
}

fn produce_byte_seq(len: usize, fill_from: &[u8]) -> Vec<u8> {
let bs = fill_from.len();
let mut seq: Vec<u8> = 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.
Expand All @@ -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;
Expand All @@ -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.
Expand All @@ -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;
Expand All @@ -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<u8> {
let bs = fill_from.len();
let mut seq: Vec<u8> = 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
}
91 changes: 26 additions & 65 deletions sha-crypt/src/params.rs
Original file line number Diff line number Diff line change
@@ -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<Self, errors::Error> {
todo!()
}
}

impl Sha512Params {
/// Create new algorithm parameters.
pub fn new(rounds: u32) -> Result<Sha512Params, errors::Error> {
if (ROUNDS_MIN..=ROUNDS_MAX).contains(&rounds) {
Ok(Sha512Params { rounds })
} else {
Err(errors::Error::RoundsError)
pub fn new(rounds: u32) -> Result<Params> {
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<Self, errors::Error> {
fn from_str(_s: &str) -> Result<Self> {
todo!()
}
}

impl Sha256Params {
/// Create new algorithm parameters.
pub fn new(rounds: u32) -> Result<Sha256Params, errors::Error> {
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());
}
}
Loading