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
5 changes: 3 additions & 2 deletions sha-crypt/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
name = "sha-crypt"
version = "0.6.0-rc.1"
description = """
Pure Rust implementation of the SHA-crypt password hash based on SHA-512
as implemented by the POSIX crypt C library
Pure Rust implementation of the SHA-crypt password hashing algorithm based on SHA-256/SHA-512
as implemented by the POSIX crypt C library, including support for generating and verifying password
hash strings in the Modular Crypt Format
"""
authors = ["RustCrypto Developers"]
license = "MIT OR Apache-2.0"
Expand Down
14 changes: 7 additions & 7 deletions sha-crypt/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@

[![crate][crate-image]][crate-link]
[![Docs][docs-image]][docs-link]
[![Build Status][build-image]][build-link]
![Apache2/MIT licensed][license-image]
![Rust Version][rustc-image]
[![Project Chat][chat-image]][chat-link]
[![Build Status][build-image]][build-link]

Pure Rust implementation of the [SHA-crypt password hash based on SHA-512][1],
Pure Rust implementation of the [SHA-crypt password hash based on SHA-256/SHA-512][1],
a legacy password hashing scheme supported by the [POSIX crypt C library][2].

Password hashes using this algorithm start with `$6$` when encoded using the
[PHC string format][3].
Password hashes using this algorithm start with `$5$` or `$6$` when encoded
using the [Modular Crypt Format][3].

## License

Expand All @@ -34,15 +34,15 @@ dual licensed as above, without any additional terms or conditions.
[crate-link]: https://crates.io/crates/sha-crypt
[docs-image]: https://docs.rs/sha-crypt/badge.svg
[docs-link]: https://docs.rs/sha-crypt/
[build-image]: https://github.com/RustCrypto/password-hashes/actions/workflows/sha-crypt.yml/badge.svg
[build-link]: https://github.com/RustCrypto/password-hashes/actions/workflows/sha-crypt.yml
[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg
[rustc-image]: https://img.shields.io/badge/rustc-1.81+-blue.svg
[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg
[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260046-password-hashes
[build-image]: https://github.com/RustCrypto/password-hashes/workflows/sha-crypt/badge.svg?branch=master&event=push
[build-link]: https://github.com/RustCrypto/password-hashes/actions?query=workflow%3Asha-crypt

[//]: # (general links)

[1]: https://www.akkadia.org/drepper/SHA-crypt.txt
[2]: https://en.wikipedia.org/wiki/Crypt_(C)
[3]: https://github.com/P-H-C/phc-string-format/blob/master/phc-sf-spec.md
[3]: https://passlib.readthedocs.io/en/stable/modular_crypt_format.html
81 changes: 81 additions & 0 deletions sha-crypt/src/algorithm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
use core::{fmt, str::FromStr};
use password_hash::Error;

/// SHA-crypt algorithm variants: SHA-256 or SHA-512.
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
#[non_exhaustive]
pub enum Algorithm {
/// SHA-256-crypt: SHA-crypt instantiated with SHA-256.
Sha256Crypt,

/// SHA-512-crypt: SHA-crypt instantiated with SHA-512.
Sha512Crypt,
}

impl Default for Algorithm {
/// Recommended default algorithm: SHA-512.
fn default() -> Self {
Self::RECOMMENDED
}
}

impl Algorithm {
/// SHA-256-crypt Modular Crypt Format algorithm identifier
pub const SHA256_CRYPT_IDENT: &str = "5";

/// SHA-512-crypt Modular Crypt Format algorithm identifier
pub const SHA512_CRYPT_IDENT: &str = "6";

/// Recommended default algorithm: SHA-512.
const RECOMMENDED: Self = Self::Sha512Crypt;

/// Parse an [`Algorithm`] from the provided string.
pub fn new(id: impl AsRef<str>) -> password_hash::Result<Self> {
id.as_ref().parse()
}

/// Get the Modular Crypt Format algorithm identifier for this algorithm.
pub const fn ident(&self) -> &'static str {
match self {
Algorithm::Sha256Crypt => Self::SHA256_CRYPT_IDENT,
Algorithm::Sha512Crypt => Self::SHA512_CRYPT_IDENT,
}
}

/// Get the identifier string for this PBKDF2 [`Algorithm`].
pub fn as_str(&self) -> &'static str {
self.ident()
}
}

impl AsRef<str> for Algorithm {
fn as_ref(&self) -> &str {
self.as_str()
}
}

impl fmt::Display for Algorithm {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}

impl FromStr for Algorithm {
type Err = Error;

fn from_str(s: &str) -> password_hash::Result<Algorithm> {
s.try_into()
}
}

impl<'a> TryFrom<&'a str> for Algorithm {
type Error = Error;

fn try_from(name: &'a str) -> password_hash::Result<Algorithm> {
match name {
Self::SHA256_CRYPT_IDENT => Ok(Algorithm::Sha256Crypt),
Self::SHA512_CRYPT_IDENT => Ok(Algorithm::Sha512Crypt),
_ => Err(Error::Algorithm),
}
}
}
38 changes: 19 additions & 19 deletions sha-crypt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,15 @@
//! # fn main() -> password_hash::Result<()> {
//! // NOTE: example requires `getrandom` feature is enabled
//!
//! use sha_crypt::{SHA512_CRYPT, PasswordHasher, PasswordVerifier};
//! use sha_crypt::{PasswordHasher, PasswordVerifier, ShaCrypt};
//!
//! let sha_crypt = ShaCrypt::default(); // default is SHA-512-crypt
//! let password = b"pleaseletmein"; // don't actually use this as a password!
//! let password_hash = SHA512_CRYPT.hash_password(password)?;
//! let password_hash = sha_crypt.hash_password(password)?;
//! assert!(password_hash.as_str().starts_with("$6$"));
//!
//! // verify password is correct for the given hash
//! SHA512_CRYPT.verify_password(password, &password_hash)?;
//! sha_crypt.verify_password(password, &password_hash)?;
//! # Ok(())
//! # }
//! ```
Expand All @@ -34,6 +35,8 @@ extern crate alloc;
mod errors;
mod params;

#[cfg(feature = "password-hash")]
mod algorithm;
#[cfg(feature = "password-hash")]
mod mcf;

Expand All @@ -44,30 +47,29 @@ pub use crate::{

#[cfg(feature = "password-hash")]
pub use {
crate::mcf::{
PasswordHash, PasswordHashRef, SHA256_CRYPT, SHA512_CRYPT, ShaCrypt, ShaCryptCore,
crate::{
algorithm::Algorithm,
mcf::{PasswordHash, PasswordHashRef, ShaCrypt},
},
password_hash::{self, CustomizedPasswordHasher, PasswordHasher, PasswordVerifier},
};

use alloc::vec::Vec;
use sha2::{Digest, Sha256, Sha512};

/// Block size for SHA256
/// Block size for SHA-256-crypt.
pub const BLOCK_SIZE_SHA256: usize = 32;

/// Block size for SHA512
/// Block size for SHA-512-crypt.
pub const BLOCK_SIZE_SHA512: usize = 64;

/// 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.
/// The SHA-256-crypt function which outputs a uniformly random byte array.
///
/// # Arguments
/// - `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!**
pub fn sha256_crypt(password: &[u8], salt: &[u8], params: &Params) -> [u8; BLOCK_SIZE_SHA256] {
let pw_len = password.len();
Expand All @@ -79,7 +81,7 @@ pub fn sha256_crypt(password: &[u8], salt: &[u8], params: &Params) -> [u8; BLOCK
};
let salt_len = salt.len();

let digest_a = sha256crypt_intermediate(password, salt);
let digest_a = sha256_crypt_intermediate(password, salt);

// 13.
let mut hasher_alt = Sha256::default();
Expand Down Expand Up @@ -150,15 +152,13 @@ pub fn sha256_crypt(password: &[u8], salt: &[u8], params: &Params) -> [u8; BLOCK
digest_c
}

/// 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.
/// The SHA-512-crypt function which outputs a uniformly random byte array.
///
/// # Arguments
/// - `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!**
pub fn sha512_crypt(password: &[u8], salt: &[u8], params: &Params) -> [u8; BLOCK_SIZE_SHA512] {
let pw_len = password.len();
Expand All @@ -170,7 +170,7 @@ pub fn sha512_crypt(password: &[u8], salt: &[u8], params: &Params) -> [u8; BLOCK
};
let salt_len = salt.len();

let digest_a = sha512crypt_intermediate(password, salt);
let digest_a = sha512_crypt_intermediate(password, salt);

// 13.
let mut hasher_alt = Sha512::default();
Expand Down Expand Up @@ -241,7 +241,7 @@ pub fn sha512_crypt(password: &[u8], salt: &[u8], params: &Params) -> [u8; BLOCK
digest_c
}

fn sha256crypt_intermediate(password: &[u8], salt: &[u8]) -> [u8; BLOCK_SIZE_SHA256] {
fn sha256_crypt_intermediate(password: &[u8], salt: &[u8]) -> [u8; BLOCK_SIZE_SHA256] {
let pw_len = password.len();

let mut hasher = Sha256::default();
Expand Down Expand Up @@ -284,7 +284,7 @@ fn sha256crypt_intermediate(password: &[u8], salt: &[u8]) -> [u8; BLOCK_SIZE_SHA
hasher.finalize().as_slice().try_into().unwrap()
}

fn sha512crypt_intermediate(password: &[u8], salt: &[u8]) -> [u8; BLOCK_SIZE_SHA512] {
fn sha512_crypt_intermediate(password: &[u8], salt: &[u8]) -> [u8; BLOCK_SIZE_SHA512] {
let pw_len = password.len();

let mut hasher = Sha512::default();
Expand Down
Loading