Files
RedBear-OS/src/header/crypt/sha.rs
T
2025-12-31 13:58:44 +00:00

142 lines
4.6 KiB
Rust

use alloc::string::{String, ToString};
use sha_crypt::{
ROUNDS_DEFAULT, ROUNDS_MAX, ROUNDS_MIN, Sha256Params, Sha512Params, sha256_crypt_b64,
sha512_crypt_b64,
};
use crate::platform::types::c_ulong;
// key limit is not part of the original design, added for DoS protection.
// rounds limit has been lowered (versus the reference/spec), also for DoS
// protection. runtime is O(klen^2 + klen*rounds)
const KEY_MAX: usize = 256;
const SALT_MAX: usize = 16;
const RSTRING: &str = "rounds=";
pub enum ShaType {
Sha256,
Sha512,
}
/// Performs SHA hashing on a given password with a specific setting.
///
/// # Parameters
/// * `passw`: The password to be hashed. It must be a string slice (`&str`).
/// * `setting`: The settings for the SHA hashing. It must be a string slice (`&str`)
/// and should start with "$5$" for SHA256 or "$6$" for SHA512. The rest of the string should represent the salt
/// to be used for the SHA hashing.
/// * `cipher`: The type of SHA algorithm to use. It should be either `ShaType::Sha256` or `ShaType::Sha512`.
///
/// # Returns
/// * `Option<String>`: Returns `Some(String)` if the SHA operation was successful, where the
/// returned string is the result of the SHA operation formatted according to the Modular
/// Crypt Format (MCF). If the SHA operation failed, it returns `None`.
///
/// # Errors
/// * If the `passw` length exceeds `KEY_MAX`.
/// * If the `setting` does not start with "$5$" or "$6$".
/// * If the `setting` does not contain a '$' character.
/// * If the `setting` contains another '$' character after the first one.
/// * If the `setting` contains invalid characters.
/// * If the `setting` contains an invalid number of rounds.
/// * If the `sha256_crypt_b64` or `sha512_crypt_b64` function fails to hash the password.
///
/// # Example
/// ```
/// let password = "my_password";
/// let setting = "$5$rounds=1400$anotherlongsaltstringg";
/// let result = crypt_sha(password, setting, ShaType::Sha256);
/// assert!(result.is_some());
/// ```
///
/// # Note
/// The `crypt_sha` function uses the SHA256 or SHA512 hashing algorithm for hashing.
/// The output of the SHA operation is base64-encoded using the BCrypt variant of base64.
pub fn crypt_sha(passw: &str, setting: &str, cipher: ShaType) -> Option<String> {
let mut cursor = 3;
let rounds;
/* reject large keys */
if passw.len() > KEY_MAX {
return None;
}
// SHA256
// setting: $5$rounds=n$setting$ (rounds=n$ and closing $ are optional)
// SHA512
// setting: $6$rounds=n$setting$ (rounds=n$ and closing $ are optional)
let param = match cipher {
ShaType::Sha256 => "$5$",
ShaType::Sha512 => "$6$",
};
if &setting[0..3] != param {
return None;
}
let has_round;
// 7 is len("rounds=")
if &setting[cursor..cursor + 7] == RSTRING {
cursor += 7;
has_round = true;
if let Some(c_end) = setting[cursor..].chars().position(|r| r == '$') {
if let Ok(u) = setting[cursor..cursor + c_end].parse::<c_ulong>() {
cursor += c_end + 1;
rounds = u.min(ROUNDS_MAX as c_ulong).max(ROUNDS_MIN as c_ulong);
} else {
return None;
}
} else {
return None;
}
} else {
has_round = false;
rounds = ROUNDS_DEFAULT as c_ulong;
}
let mut slen = cursor;
for i in 0..SALT_MAX.min(setting.len() - cursor) {
let idx = cursor + i;
if &setting[idx..idx + 1] == "$" {
break;
}
// reject characters that interfere with /etc/shadow parsing
if &setting[idx..idx + 1] == "\n" || &setting[idx..idx + 1] == ":" {
return None;
}
slen += 1;
}
let setting = &setting[cursor..slen];
if let Ok(enc) = match cipher {
ShaType::Sha256 => {
let params = Sha256Params::new(rounds as usize)
.unwrap_or(Sha256Params::new(ROUNDS_DEFAULT).unwrap());
sha256_crypt_b64(passw.as_bytes(), setting.as_bytes(), &params)
}
ShaType::Sha512 => {
let params = Sha512Params::new(rounds as usize)
.unwrap_or(Sha512Params::new(ROUNDS_DEFAULT).unwrap());
sha512_crypt_b64(passw.as_bytes(), setting.as_bytes(), &params)
}
} {
let (r_slice, rn_slice) = if has_round {
(RSTRING, rounds.to_string() + "$")
} else {
("", String::new())
};
Some(format!(
"{}{}{}{}${}",
param, r_slice, rn_slice, setting, enc
))
} else {
None
}
}