password_hash/lib.rs
1#![no_std]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3#![doc = include_str!("../README.md")]
4#![doc(
5 html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg",
6 html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg"
7)]
8#![forbid(unsafe_code)]
9#![warn(
10 missing_docs,
11 rust_2018_idioms,
12 unused_lifetimes,
13 missing_debug_implementations
14)]
15
16//!
17//! # Usage
18//!
19//! This crate represents password hashes using the [`PasswordHash`] type, which
20//! represents a parsed "PHC string" with the following format:
21//!
22//! ```text
23//! $<id>[$v=<version>][$<param>=<value>(,<param>=<value>)*][$<salt>[$<hash>]]
24//! ```
25//!
26//! For more information, please see the documentation for [`PasswordHash`].
27
28#[cfg(feature = "alloc")]
29#[allow(unused_extern_crates)]
30extern crate alloc;
31
32mod error;
33
34pub use crate::error::{Error, Result};
35
36#[cfg(feature = "phc")]
37pub use phc;
38
39#[cfg(feature = "rand_core")]
40pub use rand_core;
41
42/// DEPRECATED: import this as `password_hash::phc::PasswordHash`.
43#[cfg(feature = "phc")]
44#[deprecated(
45 since = "0.6.0",
46 note = "import as `password_hash::phc::PasswordHash` instead"
47)]
48pub type PasswordHash = phc::PasswordHash;
49
50/// DEPRECATED: use `password_hash::phc::PasswordHash` or `String`
51#[cfg(all(feature = "alloc", feature = "phc"))]
52#[deprecated(
53 since = "0.6.0",
54 note = "use `password_hash::phc::PasswordHash` or `String`"
55)]
56#[allow(deprecated)]
57pub type PasswordHashString = phc::PasswordHashString;
58
59use core::fmt::Debug;
60
61#[cfg(feature = "rand_core")]
62use rand_core::TryCryptoRng;
63
64/// Numeric version identifier for password hashing algorithms.
65pub type Version = u32;
66
67/// Recommended length of a salt: 16-bytes.
68///
69/// This recommendation comes from the [PHC string format specification]:
70///
71/// > The role of salts is to achieve uniqueness. A *random* salt is fine
72/// > for that as long as its length is sufficient; a 16-byte salt would
73/// > work well (by definition, UUID are very good salts, and they encode
74/// > over exactly 16 bytes). 16 bytes encode as 22 characters in B64.
75///
76/// [PHC string format specification]: https://github.com/P-H-C/phc-string-format/blob/master/phc-sf-spec.md#function-duties
77#[cfg(any(feature = "getrandom", feature = "rand_core"))]
78const RECOMMENDED_SALT_LEN: usize = 16;
79
80/// High-level trait for password hashing functions.
81///
82/// Generic around a password hash to be returned (typically [`phc::PasswordHash`])
83pub trait PasswordHasher<H> {
84 /// Compute the hash `H` from the given password and salt, potentially using configuration
85 /// stored in `&self` for the parameters, or otherwise the recommended defaults.
86 ///
87 /// The salt should be unique per password. When in doubt, use [`PasswordHasher::hash_password`]
88 /// which will choose the salt for you.
89 fn hash_password_with_salt(&self, password: &[u8], salt: &[u8]) -> Result<H>;
90
91 /// Compute the hash `H` from the given password, potentially using configuration stored in
92 /// `&self` for the parameters, or otherwise the recommended defaults.
93 ///
94 /// A large random salt will be generated automatically.
95 #[cfg(feature = "getrandom")]
96 fn hash_password(&self, password: &[u8]) -> Result<H> {
97 let salt = try_generate_salt()?;
98 self.hash_password_with_salt(password, &salt)
99 }
100
101 /// Compute the hash `H` from the given password, potentially using configuration stored in
102 /// `&self` for the parameters, or otherwise the recommended defaults.
103 ///
104 /// A large random salt will be generated automatically from the provided RNG.
105 #[cfg(feature = "rand_core")]
106 fn hash_password_with_rng<R: TryCryptoRng + ?Sized>(
107 &self,
108 rng: &mut R,
109 password: &[u8],
110 ) -> Result<H> {
111 let mut salt = [0u8; RECOMMENDED_SALT_LEN];
112 rng.try_fill_bytes(&mut salt).map_err(|_| Error::Crypto)?;
113 self.hash_password_with_salt(password, &salt)
114 }
115}
116
117/// Trait for password hashing functions which support customization.
118///
119/// Generic around a password hash to be returned (typically [`PasswordHash`])
120pub trait CustomizedPasswordHasher<H> {
121 /// Algorithm-specific parameters.
122 type Params: Clone + Debug + Default;
123
124 /// Compute a [`PasswordHash`] from the provided password using an
125 /// explicit set of customized algorithm parameters as opposed to the
126 /// defaults.
127 ///
128 /// When in doubt, use [`PasswordHasher::hash_password`] instead.
129 fn hash_password_customized(
130 &self,
131 password: &[u8],
132 salt: &[u8],
133 algorithm: Option<&str>,
134 version: Option<Version>,
135 params: Self::Params,
136 ) -> Result<H>;
137
138 /// Compute a [`PasswordHash`] using customized parameters only, using the default
139 /// algorithm and version.
140 fn hash_password_with_params(
141 &self,
142 password: &[u8],
143 salt: &[u8],
144 params: Self::Params,
145 ) -> Result<H> {
146 self.hash_password_customized(password, salt, None, None, params)
147 }
148}
149
150/// Trait for password verification.
151///
152/// Generic around a password hash to be returned (typically [`phc::PasswordHash`])
153///
154/// Automatically impl'd for type that impl [`PasswordHasher`] with [`phc::PasswordHash`] as `H`.
155///
156/// This trait is object safe and can be used to implement abstractions over
157/// multiple password hashing algorithms.
158pub trait PasswordVerifier<H: ?Sized> {
159 /// Compute this password hashing function against the provided password
160 /// using the parameters from the provided password hash and see if the
161 /// computed output matches.
162 fn verify_password(&self, password: &[u8], hash: &H) -> Result<()>;
163}
164
165#[cfg(feature = "phc")]
166impl<T: CustomizedPasswordHasher<phc::PasswordHash>> PasswordVerifier<phc::PasswordHash> for T
167where
168 T::Params: for<'a> TryFrom<&'a phc::ParamsString, Error = Error>,
169{
170 fn verify_password(&self, password: &[u8], hash: &phc::PasswordHash) -> Result<()> {
171 #[allow(clippy::single_match)]
172 match (&hash.salt, &hash.hash) {
173 (Some(salt), Some(expected_output)) => {
174 let computed_hash = self.hash_password_customized(
175 password,
176 salt,
177 Some(hash.algorithm.as_str()),
178 hash.version,
179 T::Params::try_from(&hash.params)?,
180 )?;
181
182 if let Some(computed_output) = &computed_hash.hash {
183 // See notes on `Output` about the use of a constant-time comparison
184 if expected_output == computed_output {
185 return Ok(());
186 }
187 }
188 }
189 _ => (),
190 }
191
192 Err(Error::PasswordInvalid)
193 }
194}
195
196/// Trait for password hashing algorithms which support the legacy
197/// [Modular Crypt Format (MCF)][MCF].
198///
199/// [MCF]: https://passlib.readthedocs.io/en/stable/modular_crypt_format.html
200#[cfg(feature = "phc")]
201pub trait McfHasher {
202 /// Upgrade an MCF hash to a PHC hash. MCF follow this rough format:
203 ///
204 /// ```text
205 /// $<id>$<content>
206 /// ```
207 ///
208 /// MCF hashes are otherwise largely unstructured and parsed according to
209 /// algorithm-specific rules so hashers must parse a raw string themselves.
210 fn upgrade_mcf_hash(&self, hash: &str) -> Result<phc::PasswordHash>;
211}
212
213/// Generate a random salt value of the recommended length using the system's secure RNG.
214#[cfg(feature = "getrandom")]
215pub fn generate_salt() -> [u8; RECOMMENDED_SALT_LEN] {
216 try_generate_salt().expect("RNG failure")
217}
218
219/// Try generating a random salt value of the recommended length using the system's secure RNG,
220/// returning errors if they occur.
221#[cfg(feature = "getrandom")]
222pub fn try_generate_salt() -> core::result::Result<[u8; RECOMMENDED_SALT_LEN], getrandom::Error> {
223 let mut salt = [0u8; RECOMMENDED_SALT_LEN];
224 getrandom::fill(&mut salt)?;
225 Ok(salt)
226}