Skip to content

Commit 0ae86aa

Browse files
authored
sha-crypt: use mcf crate (#726)
Begins an initial integration of using the `mcf` crate for the purposes of constructing strings in Modular Crypt Format.
1 parent 32249a6 commit 0ae86aa

File tree

4 files changed

+38
-35
lines changed

4 files changed

+38
-35
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sha-crypt/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,13 @@ sha2 = { version = "0.11.0-rc.2", default-features = false }
2121
base64ct = { version = "1.7.1", default-features = false }
2222

2323
# optional dependencies
24+
mcf = { version = "0.2", optional = true, default-features = false, features = ["alloc", "base64"] }
2425
rand_core = { version = "0.9", optional = true, default-features = false, features = ["os_rng"] }
2526
subtle = { version = "2", optional = true, default-features = false }
2627

2728
[features]
2829
default = ["simple"]
29-
simple = ["base64ct/alloc", "dep:rand_core", "dep:subtle"]
30+
simple = ["base64ct/alloc", "dep:mcf", "dep:rand_core", "dep:subtle"]
3031

3132
[package.metadata.docs.rs]
3233
all-features = true

sha-crypt/src/lib.rs

Lines changed: 33 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,11 @@ use {
6666
};
6767

6868
#[cfg(feature = "simple")]
69-
static SHA256_SALT_PREFIX: &str = "$5$";
69+
static SHA256_MCF_ID: &str = "5";
7070
#[cfg(feature = "simple")]
71-
static SHA256_ROUNDS_PREFIX: &str = "rounds=";
72-
73-
#[cfg(feature = "simple")]
74-
static SHA512_SALT_PREFIX: &str = "$6$";
71+
static SHA512_MCF_ID: &str = "6";
7572
#[cfg(feature = "simple")]
76-
static SHA512_ROUNDS_PREFIX: &str = "rounds=";
73+
static ROUNDS_PARAM: &str = "rounds=";
7774

7875
/// The SHA512 crypt function returned as byte vector
7976
///
@@ -321,17 +318,20 @@ pub fn sha512_simple(password: &str, params: &Sha512Params) -> String {
321318
let salt = random_salt();
322319
let out = sha512_crypt(password.as_bytes(), salt.as_bytes(), params);
323320

324-
let mut result = String::new();
325-
result.push_str(SHA512_SALT_PREFIX);
321+
let mut mcf_hash = mcf::PasswordHash::from_id(SHA512_MCF_ID).expect("should have valid ID");
322+
326323
if params.rounds != ROUNDS_DEFAULT {
327-
result.push_str(&format!("{}{}", SHA512_ROUNDS_PREFIX, params.rounds));
328-
result.push('$');
324+
mcf_hash
325+
.push_str(&format!("{}{}", ROUNDS_PARAM, params.rounds))
326+
.expect("should be valid field");
329327
}
330-
result.push_str(&salt);
331-
result.push('$');
328+
329+
mcf_hash.push_str(&salt).expect("should have valid salt");
330+
332331
let s = String::from_utf8(b64::encode_sha512(&out).to_vec()).unwrap();
333-
result.push_str(&s);
334-
result
332+
mcf_hash.push_str(&s).expect("should have valid hash");
333+
334+
mcf_hash.into()
335335
}
336336

337337
/// Simple interface for generating a SHA256 password hash.
@@ -350,17 +350,20 @@ pub fn sha256_simple(password: &str, params: &Sha256Params) -> String {
350350
let salt = random_salt();
351351
let out = sha256_crypt(password.as_bytes(), salt.as_bytes(), params);
352352

353-
let mut result = String::new();
354-
result.push_str(SHA256_SALT_PREFIX);
353+
let mut mcf_hash = mcf::PasswordHash::from_id(SHA256_MCF_ID).expect("should have valid ID");
354+
355355
if params.rounds != ROUNDS_DEFAULT {
356-
result.push_str(&format!("{}{}", SHA256_ROUNDS_PREFIX, params.rounds));
357-
result.push('$');
356+
mcf_hash
357+
.push_str(&format!("{}{}", ROUNDS_PARAM, params.rounds))
358+
.expect("should be valid field");
358359
}
359-
result.push_str(&salt);
360-
result.push('$');
360+
361+
mcf_hash.push_str(&salt).expect("should have valid salt");
362+
361363
let s = String::from_utf8(b64::encode_sha256(&out).to_vec()).unwrap();
362-
result.push_str(&s);
363-
result
364+
mcf_hash.push_str(&s).expect("should have valid hash");
365+
366+
mcf_hash.into()
364367
}
365368

366369
/// Checks that given password matches provided hash.
@@ -388,23 +391,21 @@ pub fn sha512_check(password: &str, hashed_value: &str) -> Result<(), CheckError
388391

389392
if iter.next() != Some("6") {
390393
return Err(CheckError::InvalidFormat(format!(
391-
"does not contain SHA512 identifier: '{SHA512_SALT_PREFIX}'",
394+
"does not contain SHA512 identifier: '${SHA512_MCF_ID}$'",
392395
)));
393396
}
394397

395398
let mut next = iter.next().ok_or_else(|| {
396399
CheckError::InvalidFormat("Does not contain a rounds or salt nor hash string".to_string())
397400
})?;
398-
let rounds = if next.starts_with(SHA512_ROUNDS_PREFIX) {
401+
let rounds = if next.starts_with(ROUNDS_PARAM) {
399402
let rounds = next;
400403
next = iter.next().ok_or_else(|| {
401404
CheckError::InvalidFormat("Does not contain a salt nor hash string".to_string())
402405
})?;
403406

404-
rounds[SHA512_ROUNDS_PREFIX.len()..].parse().map_err(|_| {
405-
CheckError::InvalidFormat(format!(
406-
"{SHA512_ROUNDS_PREFIX} specifier need to be a number",
407-
))
407+
rounds[ROUNDS_PARAM.len()..].parse().map_err(|_| {
408+
CheckError::InvalidFormat(format!("{ROUNDS_PARAM} specifier need to be a number",))
408409
})?
409410
} else {
410411
ROUNDS_DEFAULT
@@ -465,23 +466,21 @@ pub fn sha256_check(password: &str, hashed_value: &str) -> Result<(), CheckError
465466

466467
if iter.next() != Some("5") {
467468
return Err(CheckError::InvalidFormat(format!(
468-
"does not contain SHA256 identifier: '{SHA256_SALT_PREFIX}'",
469+
"does not contain SHA256 identifier: '${SHA256_MCF_ID}$'",
469470
)));
470471
}
471472

472473
let mut next = iter.next().ok_or_else(|| {
473474
CheckError::InvalidFormat("Does not contain a rounds or salt nor hash string".to_string())
474475
})?;
475-
let rounds = if next.starts_with(SHA256_ROUNDS_PREFIX) {
476+
let rounds = if next.starts_with(ROUNDS_PARAM) {
476477
let rounds = next;
477478
next = iter.next().ok_or_else(|| {
478479
CheckError::InvalidFormat("Does not contain a salt nor hash string".to_string())
479480
})?;
480481

481-
rounds[SHA256_ROUNDS_PREFIX.len()..].parse().map_err(|_| {
482-
CheckError::InvalidFormat(format!(
483-
"{SHA256_ROUNDS_PREFIX} specifier need to be a number",
484-
))
482+
rounds[ROUNDS_PARAM.len()..].parse().map_err(|_| {
483+
CheckError::InvalidFormat(format!("{ROUNDS_PARAM} specifier need to be a number",))
485484
})?
486485
} else {
487486
ROUNDS_DEFAULT

sha-crypt/tests/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,10 @@ fn test_sha512_simple_check_roundtrip() {
162162
let params = Sha512Params::new(5_000).expect("Rounds error");
163163

164164
let hash = sha512_simple(pw, &params);
165+
dbg!(&hash);
165166

166167
let c_r = sha512_check(pw, &hash);
168+
dbg!(&c_r);
167169
assert!(c_r.is_ok());
168170
}
169171

0 commit comments

Comments
 (0)