crypt-sha512 1.0.0

no_std SHA512-crypt ($6$) password hashing, ported from Ulrich Drepper's reference implementation. Pluggable crypto backend (aws-lc, BoringSSL, OpenSSL, or RustCrypto) selected by feature.
Documentation

crypt-sha512

A no_std-compatible Rust implementation of the SHA512-crypt password hashing algorithm ($6$ Unix crypt format), ported from Ulrich Drepper's reference C implementation.

The four cryptographic primitives this crate needs — SHA-512, a CSPRNG, a constant-time comparison, and a non-elidable memory wipe — are delegated to a backend selected at build time via cargo features. This lets the crate slot into projects that have already standardized on a particular crypto stack without dragging in a second one.

Backend selection

Exactly one of the following features must be enabled. There is no default; selecting zero or more than one is a compile error.

Feature Stack Build requirements
backend-aws-lc aws-lc-sys C compiler, cmake
backend-boring boring-sys (BoringSSL) C/C++ compiler, cmake, go
backend-openssl openssl-sys OpenSSL headers/libs (libssl-dev / brew install openssl)
backend-rust-crypto sha2 + getrandom + subtle + zeroize none (pure Rust)

The public API is identical regardless of backend; switching is a manifest-only change.

Features

  • no_std-compatible — only requires alloc.
  • Auto-zeroizing input type — passwords are passed as a [Password] newtype whose Drop impl wipes the backing buffer with the backend's non-elidable zeroing primitive (OPENSSL_cleanse for the FFI backends, zeroize::Zeroize for backend-rust-crypto).
  • Timing-attack resistant — verification compares with the backend's constant-time memcmp.
  • Cryptographically secure salts — generated with the backend's CSPRNG.
  • Unix $6$ compatible — output is bit-identical to glibc / libxcrypt.

Usage

[dependencies]
crypt-sha512 = { version = "1.0.0", default-features = false, features = ["backend-aws-lc"] }

Hash a new password

use crypt_sha512::{hash, Password};

// Default work factor (5000 rounds), random salt
let h = hash(Password::from("hunter2".to_string()), None);

// Higher work factor for more sensitive deployments
let h = hash(Password::from("hunter2".to_string()), Some(100_000));

Verify a password

use crypt_sha512::{hash, verify, InvalidHash, Password};

let stored = hash(Password::from("correct horse battery staple"), None);

assert_eq!(verify(Password::from("correct horse battery staple"), &stored), Ok(true));
assert_eq!(verify(Password::from("Tr0ub4dor&3"), &stored), Ok(false));

// Malformed hash strings are reported separately from a wrong password.
assert_eq!(verify(Password::from("anything"), "not a hash"), Err(InvalidHash));

verify distinguishes three outcomes:

  • Ok(true) — the hash parses and the password matches.
  • Ok(false) — the hash parses and the password does not match.
  • Err(InvalidHash) — the hash string is malformed; no comparison was performed. Useful for handling multiple hash types or detecting data corruption separately from authentication failures.

Hash with an explicit salt (low-level)

For interoperability tests or when you need to control the salt directly:

use crypt_sha512::{hash_with_salt, Password};

let h = hash_with_salt(Password::from("password"), b"$6$rounds=10000$saltstring");
assert!(h.starts_with("$6$rounds=10000$saltstring$"));

The Password type

Password is the only way to feed plaintext into this crate's hashing functions. It deliberately:

  • accepts From<String>, From<&str>, From<Vec<u8>>, and Password::from_bytes;
  • does not implement Clone, so secrets aren't silently duplicated;
  • does not implement Display, and its Debug impl elides the contents, so secrets can't accidentally land in logs;
  • zeroes its buffer with the active backend's non-elidable zeroing primitive on drop — including on panic.

Pass it by value into hash, hash_with_salt, or verify; the function takes ownership and the buffer is wiped before the call returns.

Hash format

$6$[rounds=N$]salt$digest
  • $6$ — SHA512-crypt identifier.
  • rounds=N$ — optional; default is 5000. Values are clamped into [1000, 999_999_999] per the SHA-crypt spec.
  • salt — up to 16 bytes from the crypt base64 alphabet.
  • digest — 86 bytes, crypt base64.

MSRV

Rust 1.81.

License

BSD 2-Clause. See the LICENSE file in the repository.

Provenance

The hashing algorithm is a direct port of Ulrich Drepper's public-domain SHA-crypt reference implementation. The b64_from_24bit macro mirrors the shape of the original C macro; tests reuse Drepper's published vectors.