diff --git a/Cargo.lock b/Cargo.lock index 6d8901214..36dfcc9f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1636,7 +1636,6 @@ dependencies = [ "bytes", "constant_time_eq", "crc32fast", - "digest", "fastrand", "flume", "fnv", @@ -1650,7 +1649,6 @@ dependencies = [ "libc", "log", "lru", - "md-5", "memchr", "metrohash", "num-traits", @@ -1663,7 +1661,6 @@ dependencies = [ "rustls", "rustls-native-certs", "rustls-pki-types", - "sha-1", "slog", "smallvec", "smol_str", @@ -1691,13 +1688,12 @@ dependencies = [ [[package]] name = "g3-xcrypt" -version = "0.1.0" +version = "0.2.0" dependencies = [ - "digest", - "md-5", + "constant_time_eq", "memchr", - "sha2", "thiserror 2.0.3", + "variant-ssl", ] [[package]] @@ -2686,9 +2682,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.166" +version = "0.2.167" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2ccc108bbc0b1331bd061864e7cd823c0cab660bbe6970e66e2c0614decde36" +checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc" [[package]] name = "libloading" @@ -2749,16 +2745,6 @@ dependencies = [ "libc", ] -[[package]] -name = "md-5" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" -dependencies = [ - "cfg-if", - "digest", -] - [[package]] name = "memchr" version = "2.7.4" @@ -3490,17 +3476,6 @@ dependencies = [ "serde", ] -[[package]] -name = "sha-1" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - [[package]] name = "sha2" version = "0.10.8" diff --git a/Cargo.toml b/Cargo.toml index b7ed31024..2df73dc24 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -117,9 +117,7 @@ bitflags = "2.4" lru = { version = "0.12", default-features = false } # digest = "0.10.7" -md-5 = "0.10.0" sha2 = "0.10.0" -sha-1 = "0.10.0" blake3 = { version = "1.5", default-features = false } hex = "0.4.2" hex-literal = "0.4" @@ -228,7 +226,7 @@ g3-tls-cert = { version = "0.5", path = "lib/g3-tls-cert" } g3-tls-ticket = { version = "0.1", path = "lib/g3-tls-ticket" } g3-types = { version = "0.5", path = "lib/g3-types" } g3-udpdump = { version = "0.1", path = "lib/g3-udpdump" } -g3-xcrypt = { version = "0.1", path = "lib/g3-xcrypt" } +g3-xcrypt = { version = "0.2", path = "lib/g3-xcrypt" } g3-yaml = { version = "0.5.0", path = "lib/g3-yaml" } [profile.release-lto] diff --git a/g3proxy/src/config/auth/user/mod.rs b/g3proxy/src/config/auth/user/mod.rs index 4179cacf3..c78352044 100644 --- a/g3proxy/src/config/auth/user/mod.rs +++ b/g3proxy/src/config/auth/user/mod.rs @@ -139,8 +139,8 @@ impl UserConfig { match &self.password_token { PasswordToken::Forbidden => false, PasswordToken::SkipVerify => true, - PasswordToken::FastHash(fast_hash) => fast_hash.verify(password), - PasswordToken::XCrypt(xcrypt_hash) => xcrypt_hash.verify(password.as_bytes()), + PasswordToken::FastHash(fast_hash) => fast_hash.verify(password).unwrap(), + PasswordToken::XCrypt(xcrypt_hash) => xcrypt_hash.verify(password.as_bytes()).unwrap(), } } diff --git a/lib/g3-types/Cargo.toml b/lib/g3-types/Cargo.toml index e85bacea7..622432f28 100644 --- a/lib/g3-types/Cargo.toml +++ b/lib/g3-types/Cargo.toml @@ -29,9 +29,6 @@ num-traits.workspace = true arc-swap.workspace = true fastrand = { workspace = true, optional = true } governor = { workspace = true, features = ["std", "jitter"] } -digest = { workspace = true, optional = true } -md-5 = { workspace = true, optional = true } -sha-1 = { workspace = true, optional = true } blake3 = { workspace = true, optional = true } hex = { workspace = true, optional = true } ip_network = { workspace = true, optional = true } @@ -56,7 +53,7 @@ brotli = { version = "7.0", optional = true, default-features = false, features [features] default = [] quic = [] -auth-crypt = ["dep:digest", "dep:md-5", "dep:sha-1", "dep:blake3", "dep:hex"] +auth-crypt = ["dep:openssl", "dep:blake3", "dep:hex"] resolve = ["dep:radix_trie", "dep:fastrand"] quinn = ["dep:quinn", "quic"] rustls = ["dep:rustls", "dep:rustls-pki-types", "dep:webpki-roots", "dep:rustls-native-certs", "dep:lru"] diff --git a/lib/g3-types/src/auth/crypt.rs b/lib/g3-types/src/auth/crypt.rs index 7a1acb18b..427781669 100644 --- a/lib/g3-types/src/auth/crypt.rs +++ b/lib/g3-types/src/auth/crypt.rs @@ -17,9 +17,10 @@ use std::cell::RefCell; use anyhow::anyhow; -use digest::Digest; -use md5::Md5; -use sha1::Sha1; +use constant_time_eq::{constant_time_eq_16, constant_time_eq_n}; +use openssl::error::ErrorStack; +use openssl::md::Md; +use openssl::md_ctx::MdCtx; const SALT_LENGTH: usize = 8; const MD5_LENGTH: usize = 16; @@ -37,19 +38,27 @@ enum HashValue { } impl HashValue { - fn hash_match(&self, buf: &[u8]) -> bool { + fn hash_match(&self, buf: &[u8]) -> Result { match self { HashValue::Md5(v) => { - let md5 = Md5::digest(buf); - v.eq(md5.as_slice()) + let mut md = MdCtx::new()?; + md.digest_init(Md::md5())?; + md.digest_update(v)?; + let mut hash = [0; MD5_LENGTH]; + md.digest_final(&mut hash)?; + Ok(constant_time_eq_16(v, &hash)) } HashValue::Sha1(v) => { - let sha1 = Sha1::digest(buf); - v.eq(sha1.as_slice()) + let mut md = MdCtx::new()?; + md.digest_init(Md::sha1())?; + md.digest_update(v)?; + let mut hash = [0; SHA1_LENGTH]; + md.digest_final(&mut hash)?; + Ok(constant_time_eq_n(v, &hash)) } HashValue::Blake3(v) => { let b3 = blake3::hash(buf); - v.eq(&b3) + Ok(v.eq(&b3)) } } } @@ -115,20 +124,20 @@ impl FastHashedPassPhrase { Ok(()) } - pub fn verify(&self, pass: &str) -> bool { + pub fn verify(&self, pass: &str) -> Result { HASH_TL_BUF.with_borrow_mut(|buf| { buf.extend_from_slice(pass.as_bytes()); buf.extend_from_slice(&self.salt); let mut all_verified = true; for hv in self.values.iter() { - if !hv.hash_match(buf.as_slice()) { + if !hv.hash_match(buf.as_slice())? { all_verified = false; break; } } buf.clear(); - all_verified + Ok(all_verified) }) } @@ -152,6 +161,6 @@ mod tests { p.push_sha1("0b39e984b59251425245e81241aebf7dbe197cc3") .unwrap(); - assert!(p.verify("IQ5ZhanWaop2cw")); + assert!(p.verify("IQ5ZhanWaop2cw").unwrap()); } } diff --git a/lib/g3-xcrypt/Cargo.toml b/lib/g3-xcrypt/Cargo.toml index 75012a40e..5e0d91822 100644 --- a/lib/g3-xcrypt/Cargo.toml +++ b/lib/g3-xcrypt/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "g3-xcrypt" -version = "0.1.0" +version = "0.2.0" license.workspace = true edition.workspace = true @@ -9,6 +9,5 @@ edition.workspace = true [dependencies] thiserror.workspace = true memchr.workspace = true -digest.workspace = true -md-5.workspace = true -sha2.workspace = true +openssl.workspace = true +constant_time_eq.workspace = true diff --git a/lib/g3-xcrypt/src/lib.rs b/lib/g3-xcrypt/src/lib.rs index 2adf0338a..7fe7df473 100644 --- a/lib/g3-xcrypt/src/lib.rs +++ b/lib/g3-xcrypt/src/lib.rs @@ -14,7 +14,10 @@ * limitations under the License. */ +use openssl::error::ErrorStack; + mod b64; + pub(crate) use b64::B64CryptDecoder; pub use b64::B64CryptEncoder; @@ -48,7 +51,7 @@ impl XCryptHash { } } - pub fn verify(&self, phrase: &[u8]) -> bool { + pub fn verify(&self, phrase: &[u8]) -> Result { match self { XCryptHash::Md5(this) => this.verify(phrase), XCryptHash::Sha256(this) => this.verify(phrase), @@ -64,7 +67,7 @@ mod tests { #[test] fn md5() { let crypt = XCryptHash::parse("$1$DDiGYGte$K/SAC4VvllDonGcP1EfaY1").unwrap(); - assert!(crypt.verify("123456".as_bytes())); + assert!(crypt.verify("123456".as_bytes()).unwrap()); } #[test] @@ -72,7 +75,7 @@ mod tests { let crypt = XCryptHash::parse("$5$W9wFmTCpBILzJn18$X496nPJHVQ895fwotE3WPBLmxgxGD8ivpUhfmoKbtb7") .unwrap(); - assert!(crypt.verify("123456".as_bytes())); + assert!(crypt.verify("123456".as_bytes()).unwrap()); } #[test] @@ -80,6 +83,6 @@ mod tests { let s = "$6$yeDpErl4xq9E2vKP$\ .reNyfNzRJyAJrlh38J1XGx/5QTfBy3IedVNdTqfWqSeZFPAbXzV85uNK9fdmXvGCxizHVcAiIoQ4uXMJWuB6/"; let crypt = XCryptHash::parse(s).unwrap(); - assert!(crypt.verify("123456".as_bytes())); + assert!(crypt.verify("123456".as_bytes()).unwrap()); } } diff --git a/lib/g3-xcrypt/src/md5.rs b/lib/g3-xcrypt/src/md5.rs index a800513a7..f599ee871 100644 --- a/lib/g3-xcrypt/src/md5.rs +++ b/lib/g3-xcrypt/src/md5.rs @@ -14,8 +14,10 @@ * limitations under the License. */ -use digest::Digest; -use md5::Md5; +use constant_time_eq::constant_time_eq_16; +use openssl::error::ErrorStack; +use openssl::md::Md; +use openssl::md_ctx::MdCtx; use super::{B64CryptDecoder, XCryptParseError, XCryptParseResult}; @@ -35,99 +37,89 @@ pub struct Md5Crypt { hash_bin: [u8; HASH_BIN_LEN], } -fn do_md5_hash(phrase: &[u8], salt: &str) -> [u8; HASH_BIN_LEN] { +fn do_md5_hash(phrase: &[u8], salt: &str) -> Result<[u8; HASH_BIN_LEN], ErrorStack> { /* Compute alternate MD5 sum with input PHRASE, SALT, and PHRASE. The final result will be added to the first context. */ - let mut digest = Md5::new(); + let mut md = MdCtx::new()?; + md.digest_init(Md::md5())?; - digest.update(phrase); - digest.update(salt.as_bytes()); - digest.update(phrase); + md.digest_update(phrase)?; + md.digest_update(salt.as_bytes())?; + md.digest_update(phrase)?; - let hash = digest.finalize(); // the results should be of HASH_BIN_LEN bytes + let mut hash = [0u8; HASH_BIN_LEN]; + md.digest_final(&mut hash)?; /* Prepare for the real work. */ - let mut digest = Md5::new(); + md.digest_init(Md::md5())?; - digest.update(phrase); - digest.update(PREFIX.as_bytes()); - digest.update(salt.as_bytes()); + md.digest_update(phrase)?; + md.digest_update(PREFIX.as_bytes())?; + md.digest_update(salt.as_bytes())?; /* Add for any character in the phrase one byte of the alternate sum. */ let mut plen = phrase.len(); while plen > HASH_BIN_LEN { - digest.update(hash); + md.digest_update(&hash)?; plen -= HASH_BIN_LEN; } if plen > 0 { - digest.update(&hash[0..plen]); + md.digest_update(&hash[0..plen])?; } /* The original implementation now does something weird: for every 1 bit in the phrase the first 0 is added to the buffer, for every 0 bit the first character of the phrase. This does not seem to be - what was intended but we have to follow this to be compatible. + what was intended, but we have to follow this to be compatible. */ plen = phrase.len(); while plen > 0 { if (plen & 1) == 0 { - digest.update(&phrase[..1]); + md.digest_update(&phrase[..1])?; } else { - digest.update([0u8]); + md.digest_update(&[0u8])?; } plen >>= 1; } /* Create intermediate result. */ - let mut hash = digest.finalize(); + md.digest_final(&mut hash)?; for r in 0..1000 { - let mut digest = Md5::new(); + md.digest_init(Md::md5())?; /* Add phrase or last result. */ if (r & 1) == 0 { - digest.update(hash); + md.digest_update(&hash)?; } else { - digest.update(phrase); + md.digest_update(phrase)?; } /* Add salt for numbers not divisible by 3. */ if (r % 3) != 0 { - digest.update(salt.as_bytes()); + md.digest_update(salt.as_bytes())?; } /* Add phrase for numbers not divisible by 7. */ if (r % 7) != 0 { - digest.update(phrase); + md.digest_update(phrase)?; } /* Add phrase or last result. */ if (r & 1) == 0 { - digest.update(phrase); + md.digest_update(phrase)?; } else { - digest.update(hash); + md.digest_update(&hash)?; } /* Create intermediate result. */ - hash = digest.finalize(); + md.digest_final(&mut hash)?; } - hash.into() - - /* - let mut encoder = B64CryptEncoder::new(HASH_STR_LEN); - encoder.push::<4>(hash[0], hash[6], hash[12]); - encoder.push::<4>(hash[1], hash[7], hash[13]); - encoder.push::<4>(hash[2], hash[8], hash[14]); - encoder.push::<4>(hash[3], hash[9], hash[15]); - encoder.push::<4>(hash[4], hash[10], hash[5]); - encoder.push::<2>(0, 0, hash[11]); - - encoder.into() - */ + Ok(hash) } impl Md5Crypt { @@ -169,8 +161,7 @@ impl Md5Crypt { } } - pub(super) fn verify(&self, phrase: &[u8]) -> bool { - let hash = do_md5_hash(phrase, &self.salt); - self.hash_bin.eq(&hash) + pub(super) fn verify(&self, phrase: &[u8]) -> Result { + do_md5_hash(phrase, &self.salt).map(|hash| constant_time_eq_16(&hash, &self.hash_bin)) } } diff --git a/lib/g3-xcrypt/src/sha256.rs b/lib/g3-xcrypt/src/sha256.rs index e58627f18..f5850118a 100644 --- a/lib/g3-xcrypt/src/sha256.rs +++ b/lib/g3-xcrypt/src/sha256.rs @@ -16,8 +16,10 @@ use std::str::FromStr; -use digest::{Digest, Output}; -use sha2::Sha256; +use constant_time_eq::constant_time_eq_32; +use openssl::error::ErrorStack; +use openssl::md::Md; +use openssl::md_ctx::{MdCtx, MdCtxRef}; use super::{B64CryptDecoder, XCryptParseError, XCryptParseResult}; @@ -45,46 +47,54 @@ pub struct Sha256Crypt { hash_bin: [u8; HASH_BIN_LEN], } -fn sha256_update_recycled(digest: &mut D, block: &Output, len: usize) -where - D: Digest, -{ +fn sha256_update_recycled( + md: &mut MdCtxRef, + block: &[u8; HASH_BIN_LEN], + len: usize, +) -> Result<(), ErrorStack> { let mut n = len; while n > HASH_BIN_LEN { - digest.update(block); + md.digest_update(block)?; n -= HASH_BIN_LEN; } if n > 0 { - digest.update(&block[0..n]); + md.digest_update(&block[0..n])?; } + Ok(()) } -fn do_sha256_hash(phrase: &[u8], salt: &str, rounds: usize) -> [u8; HASH_BIN_LEN] { +fn do_sha256_hash( + phrase: &[u8], + salt: &str, + rounds: usize, +) -> Result<[u8; HASH_BIN_LEN], ErrorStack> { /* - Compute alternate MD5 sum with input PHRASE, SALT, and PHRASE. The + Compute alternate SHA256 sum with input PHRASE, SALT, and PHRASE. The final result will be added to the first context. */ - let mut digest = Sha256::new(); + let mut md = MdCtx::new()?; + md.digest_init(Md::sha256())?; - digest.update(phrase); - digest.update(salt.as_bytes()); - digest.update(phrase); + md.digest_update(phrase)?; + md.digest_update(salt.as_bytes())?; + md.digest_update(phrase)?; - let hash = digest.finalize(); // the results should be of HASH_BIN_LEN bytes + let mut hash = [0u8; HASH_BIN_LEN]; + md.digest_final(&mut hash)?; /* Prepare for the real work. */ - let mut digest = Sha256::new(); + md.digest_init(Md::sha256())?; - digest.update(phrase); + md.digest_update(phrase)?; /* The last part is the salt string. This must be at most 8 - characters and it ends at the first `$' character (for + characters, and it ends at the first `$' character (for compatibility with existing implementations). */ - digest.update(salt.as_bytes()); + md.digest_update(salt.as_bytes())?; /* Add for any character in the phrase one byte of the alternate sum. */ - sha256_update_recycled(&mut digest, &hash, phrase.len()); + sha256_update_recycled(&mut md, &hash, phrase.len())?; /* Take the binary representation of the length of the phrase and for every @@ -93,79 +103,64 @@ fn do_sha256_hash(phrase: &[u8], salt: &str, rounds: usize) -> [u8; HASH_BIN_LEN let mut plen = phrase.len(); while plen > 0 { if (plen & 1) == 0 { - digest.update(phrase); + md.digest_update(phrase)?; } else { - digest.update(hash); + md.digest_update(&hash)?; } plen >>= 1; } /* Create intermediate result. */ - let mut hash = digest.finalize(); + md.digest_final(&mut hash)?; /* Start computation of P byte sequence. */ - let mut digest = Sha256::new(); + md.digest_init(Md::sha256())?; /* For every character in the password add the entire password. */ for _ in 0..phrase.len() { - digest.update(phrase); + md.digest_update(phrase)?; } - let p_bytes = digest.finalize(); + let mut p_bytes = [0u8; HASH_BIN_LEN]; + md.digest_final(&mut p_bytes)?; /* Start computation of S byte sequence. */ - let mut digest = Sha256::new(); + md.digest_init(Md::sha256())?; for _ in 0..(hash[0] as usize + 16) { - digest.update(salt.as_bytes()); + md.digest_update(salt.as_bytes())?; } - let s_bytes = digest.finalize(); + let mut s_bytes = [0u8; HASH_BIN_LEN]; + md.digest_final(&mut s_bytes)?; for r in 0..rounds { - let mut digest = Sha256::new(); + md.digest_init(Md::sha256())?; /* Add phrase or last result. */ if (r & 1) == 0 { - digest.update(hash); + md.digest_update(&hash)?; } else { - sha256_update_recycled(&mut digest, &p_bytes, phrase.len()); + sha256_update_recycled(&mut md, &p_bytes, phrase.len())?; } /* Add salt for numbers not divisible by 3. */ if (r % 3) != 0 { - sha256_update_recycled(&mut digest, &s_bytes, salt.len()); + sha256_update_recycled(&mut md, &s_bytes, salt.len())?; } /* Add phrase for numbers not divisible by 7. */ if (r % 7) != 0 { - sha256_update_recycled(&mut digest, &p_bytes, phrase.len()); + sha256_update_recycled(&mut md, &p_bytes, phrase.len())?; } /* Add phrase or last result. */ if (r & 1) == 0 { - sha256_update_recycled(&mut digest, &p_bytes, phrase.len()); + sha256_update_recycled(&mut md, &p_bytes, phrase.len())?; } else { - digest.update(hash); + md.digest_update(&hash)?; } - hash = digest.finalize(); + md.digest_final(&mut hash)?; } - hash.into() - - /* - let mut encoder = B64CryptEncoder::new(HASH_STR_LEN); - encoder.push::<4>(hash[0], hash[10], hash[20]); - encoder.push::<4>(hash[21], hash[1], hash[11]); - encoder.push::<4>(hash[12], hash[22], hash[2]); - encoder.push::<4>(hash[3], hash[13], hash[23]); - encoder.push::<4>(hash[24], hash[4], hash[14]); - encoder.push::<4>(hash[15], hash[25], hash[5]); - encoder.push::<4>(hash[6], hash[16], hash[26]); - encoder.push::<4>(hash[27], hash[7], hash[17]); - encoder.push::<4>(hash[18], hash[28], hash[8]); - encoder.push::<4>(hash[9], hash[19], hash[29]); - encoder.push::<3>(0, hash[31], hash[30]); - - encoder.into() - */ + Ok(hash) } impl Sha256Crypt { @@ -229,8 +224,8 @@ impl Sha256Crypt { } } - pub(super) fn verify(&self, phrase: &[u8]) -> bool { - let hash = do_sha256_hash(phrase, &self.salt, self.rounds); - self.hash_bin.eq(&hash) + pub(super) fn verify(&self, phrase: &[u8]) -> Result { + do_sha256_hash(phrase, &self.salt, self.rounds) + .map(|hash| constant_time_eq_32(&hash, &self.hash_bin)) } } diff --git a/lib/g3-xcrypt/src/sha512.rs b/lib/g3-xcrypt/src/sha512.rs index d56d6fb64..c487a8004 100644 --- a/lib/g3-xcrypt/src/sha512.rs +++ b/lib/g3-xcrypt/src/sha512.rs @@ -16,8 +16,10 @@ use std::str::FromStr; -use digest::{Digest, Output}; -use sha2::Sha512; +use constant_time_eq::constant_time_eq_64; +use openssl::error::ErrorStack; +use openssl::md::Md; +use openssl::md_ctx::{MdCtx, MdCtxRef}; use super::{B64CryptDecoder, XCryptParseError, XCryptParseResult}; @@ -46,46 +48,54 @@ pub struct Sha512Crypt { hash_bin: [u8; HASH_BIN_LEN], } -fn sha512_update_recycled(digest: &mut D, block: &Output, len: usize) -where - D: Digest, -{ +fn sha512_update_recycled( + md: &mut MdCtxRef, + block: &[u8; HASH_BIN_LEN], + len: usize, +) -> Result<(), ErrorStack> { let mut n = len; while n > HASH_BIN_LEN { - digest.update(block); + md.digest_update(block)?; n -= HASH_BIN_LEN; } if n > 0 { - digest.update(&block[0..n]); + md.digest_update(&block[0..n])?; } + Ok(()) } -fn do_sha512_hash(phrase: &[u8], salt: &str, rounds: usize) -> [u8; HASH_BIN_LEN] { +fn do_sha512_hash( + phrase: &[u8], + salt: &str, + rounds: usize, +) -> Result<[u8; HASH_BIN_LEN], ErrorStack> { /* - Compute alternate MD5 sum with input PHRASE, SALT, and PHRASE. The + Compute alternate SHA512 sum with input PHRASE, SALT, and PHRASE. The final result will be added to the first context. */ - let mut digest = Sha512::new(); + let mut md = MdCtx::new()?; + md.digest_init(Md::sha512())?; - digest.update(phrase); - digest.update(salt.as_bytes()); - digest.update(phrase); + md.digest_update(phrase)?; + md.digest_update(salt.as_bytes())?; + md.digest_update(phrase)?; - let hash = digest.finalize(); // the results should be of HASH_BIN_LEN bytes + let mut hash = [0u8; HASH_BIN_LEN]; + md.digest_final(&mut hash)?; /* Prepare for the real work. */ - let mut digest = Sha512::new(); + md.digest_init(Md::sha512())?; - digest.update(phrase); + md.digest_update(phrase)?; /* The last part is the salt string. This must be at most 8 - characters and it ends at the first `$' character (for + characters, and it ends at the first `$' character (for compatibility with existing implementations). */ - digest.update(salt.as_bytes()); + md.digest_update(salt.as_bytes())?; /* Add for any character in the phrase one byte of the alternate sum. */ - sha512_update_recycled(&mut digest, &hash, phrase.len()); + sha512_update_recycled(&mut md, &hash, phrase.len())?; /* Take the binary representation of the length of the phrase and for every @@ -94,90 +104,64 @@ fn do_sha512_hash(phrase: &[u8], salt: &str, rounds: usize) -> [u8; HASH_BIN_LEN let mut plen = phrase.len(); while plen > 0 { if (plen & 1) == 0 { - digest.update(phrase); + md.digest_update(phrase)?; } else { - digest.update(hash); + md.digest_update(&hash)?; } plen >>= 1; } /* Create intermediate result. */ - let mut hash = digest.finalize(); + md.digest_final(&mut hash)?; /* Start computation of P byte sequence. */ - let mut digest = Sha512::new(); + md.digest_init(Md::sha512())?; /* For every character in the password add the entire password. */ for _ in 0..phrase.len() { - digest.update(phrase); + md.digest_update(phrase)?; } - let p_bytes = digest.finalize(); + let mut p_bytes = [0u8; HASH_BIN_LEN]; + md.digest_final(&mut p_bytes)?; /* Start computation of S byte sequence. */ - let mut digest = Sha512::new(); + md.digest_init(Md::sha512())?; for _ in 0..(hash[0] as usize + 16) { - digest.update(salt.as_bytes()); + md.digest_update(salt.as_bytes())?; } - let s_bytes = digest.finalize(); + let mut s_bytes = [0u8; HASH_BIN_LEN]; + md.digest_final(&mut s_bytes)?; for r in 0..rounds { - let mut digest = Sha512::new(); + md.digest_init(Md::sha512())?; /* Add phrase or last result. */ if (r & 1) == 0 { - digest.update(hash); + md.digest_update(&hash)?; } else { - sha512_update_recycled(&mut digest, &p_bytes, phrase.len()); + sha512_update_recycled(&mut md, &p_bytes, phrase.len())?; } /* Add salt for numbers not divisible by 3. */ if (r % 3) != 0 { - sha512_update_recycled(&mut digest, &s_bytes, salt.len()); + sha512_update_recycled(&mut md, &s_bytes, salt.len())?; } /* Add phrase for numbers not divisible by 7. */ if (r % 7) != 0 { - sha512_update_recycled(&mut digest, &p_bytes, phrase.len()); + sha512_update_recycled(&mut md, &p_bytes, phrase.len())?; } /* Add phrase or last result. */ if (r & 1) == 0 { - sha512_update_recycled(&mut digest, &p_bytes, phrase.len()); + sha512_update_recycled(&mut md, &p_bytes, phrase.len())?; } else { - digest.update(hash); + md.digest_update(&hash)?; } - hash = digest.finalize(); + md.digest_final(&mut hash)?; } - hash.into() - - /* - let mut encoder = B64CryptEncoder::new(HASH_STR_LEN); - encoder.push::<4>(hash[0], hash[21], hash[42]); - encoder.push::<4>(hash[22], hash[43], hash[1]); - encoder.push::<4>(hash[44], hash[2], hash[23]); - encoder.push::<4>(hash[3], hash[24], hash[45]); - encoder.push::<4>(hash[25], hash[46], hash[4]); - encoder.push::<4>(hash[47], hash[5], hash[26]); - encoder.push::<4>(hash[6], hash[27], hash[48]); - encoder.push::<4>(hash[28], hash[49], hash[7]); - encoder.push::<4>(hash[50], hash[8], hash[29]); - encoder.push::<4>(hash[9], hash[30], hash[51]); - encoder.push::<4>(hash[31], hash[52], hash[10]); - encoder.push::<4>(hash[53], hash[11], hash[32]); - encoder.push::<4>(hash[12], hash[33], hash[54]); - encoder.push::<4>(hash[34], hash[55], hash[13]); - encoder.push::<4>(hash[56], hash[14], hash[35]); - encoder.push::<4>(hash[15], hash[36], hash[57]); - encoder.push::<4>(hash[37], hash[58], hash[16]); - encoder.push::<4>(hash[59], hash[17], hash[38]); - encoder.push::<4>(hash[18], hash[39], hash[60]); - encoder.push::<4>(hash[40], hash[61], hash[19]); - encoder.push::<4>(hash[62], hash[20], hash[41]); - encoder.push::<2>(0, 0, hash[63]); - - encoder.into() - */ + Ok(hash) } impl Sha512Crypt { @@ -240,8 +224,8 @@ impl Sha512Crypt { } } - pub(super) fn verify(&self, phrase: &[u8]) -> bool { - let hash = do_sha512_hash(phrase, &self.salt, self.rounds); - self.hash_bin.eq(&hash) + pub(super) fn verify(&self, phrase: &[u8]) -> Result { + do_sha512_hash(phrase, &self.salt, self.rounds) + .map(|hash| constant_time_eq_64(&hash, &self.hash_bin)) } }