diff --git a/yescrypt/README.md b/yescrypt/README.md index b9d08b4c..e0151d46 100644 --- a/yescrypt/README.md +++ b/yescrypt/README.md @@ -15,6 +15,8 @@ yescrypt is a variant of the [scrypt] password-based key derivation function and [Password Hashing Competition]. It has been adopted by several Linux distributions for the system password hashing function, including Fedora, Debian, Ubuntu, and Arch. +The algorithm is described in [yescrypt - a Password Hashing Competition submission][paper]. + ## ⚠️ Security Warning The implementation contained in this crate has never been independently audited! @@ -64,3 +66,4 @@ dual licensed as above, without any additional terms or conditions. [yescrypt]: https://www.openwall.com/yescrypt/ [scrypt]: https://en.wikipedia.org/wiki/Scrypt [Password Hashing Competition]: https://www.password-hashing.net/ +[paper]: https://www.password-hashing.net/submissions/specs/yescrypt-v2.pdf diff --git a/yescrypt/src/flags.rs b/yescrypt/src/flags.rs index 99e22ead..8fe30472 100644 --- a/yescrypt/src/flags.rs +++ b/yescrypt/src/flags.rs @@ -10,6 +10,12 @@ const MODE_MASK: u32 = 0x3; const RW_FLAVOR_MASK: u32 = 0x3fc; /// Flags for selecting the "flavor" of `yescrypt`. +/// +/// A bitmask allowing to enable the individual extra features of yescrypt, e.g: +/// +/// - [`Flags::RW`]: yescrypt’s native mode. Call [`Flags::default`] to fully configure this. +/// - [`Flags::WORM`]: conservative enhancement of classic scrypt. Mutually exclusive with `RW`. +/// - [`Flags::EMPTY`]: requests classic scrypt. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct Flags(pub(crate) u32); diff --git a/yescrypt/src/params.rs b/yescrypt/src/params.rs index 68e0af19..1bd1949c 100644 --- a/yescrypt/src/params.rs +++ b/yescrypt/src/params.rs @@ -20,6 +20,9 @@ pub struct Params { pub(crate) flags: Flags, /// `N`: CPU/memory cost (like `scrypt`). + /// + /// yescrypt, including in scrypt compatibility mode, is defined only for values of N that are + /// powers of 2 (and larger than 1, which matches scrypt’s requirements). pub(crate) n: u64, /// `r`: block size (like `scrypt`). @@ -28,10 +31,14 @@ pub struct Params { /// `p`: parallelism (like `scrypt`). pub(crate) p: u32, - /// special to yescrypt. + /// Controls yescrypt’s computation time while keeping its peak memory usage the same. + /// + /// `t = 0` is optimal for achieving the highest normalized area-time cost for ASIC attackers. pub(crate) t: u32, - /// special to yescrypt. + /// The number of cost upgrades performed to the hash so far. + /// + /// `0` means no upgrades yet, and is currently the only allowed value. pub(crate) g: u32, /// special to yescrypt. diff --git a/yescrypt/src/pwxform.rs b/yescrypt/src/pwxform.rs index 49629823..520d7ef9 100644 --- a/yescrypt/src/pwxform.rs +++ b/yescrypt/src/pwxform.rs @@ -1,17 +1,31 @@ -//! pwxform: parallel wide transformation +//! pwxform stands for "parallel wide transformation", although it can as well be tuned to be as +//! narrow as one 64-bit lane. +//! +//! It operates on 64-bit lanes which are designed to be grouped into wider "simple SIMD" lanes, +//! which are in turn possibly grouped into an even wider "gather SIMD" vector. use crate::{ salsa20, util::{slice_as_chunks_mut, xor}, }; -// These are tunable, but they must meet certain constraints. +/// Number of 64-bit lanes per "simple SIMD" lane (requiring only arithmetic and bitwise operations +/// on its 64-bit elements). Must be a power of 2. const PWXSIMPLE: usize = 2; + +/// Number of parallel "simple SIMD" lanes per "gather SIMD" vector (requiring "S-box lookups" of +/// values as wide as a "simple SIMD" lane from PWXgather typically non-contiguous memory +/// locations). Must be a power of 2. const PWXGATHER: usize = 4; + +/// Number of sequential rounds of pwxform’s basic transformation. Must be a power of 2, plus 2 +/// (e.g. 3, 4, 6, 10). const PWXROUNDS: usize = 6; + +/// Number of S-box index bits, thereby controlling the size of each of pwxform’s two S-boxes +/// (in "simple SIMD" wide elements). const SWIDTH: usize = 8; -// Derived values. Not tunable on their own. const PWXBYTES: usize = PWXGATHER * PWXSIMPLE * 8; const PWXWORDS: usize = PWXBYTES / size_of::(); const SMASK: usize = ((1 << SWIDTH) - 1) * PWXSIMPLE * 8; @@ -28,9 +42,13 @@ pub(crate) struct PwxformCtx<'a> { } impl PwxformCtx<'_> { - /// Compute `B = BlockMix_pwxform{salsa20/2, ctx, r}(B)`. + /// Compute `B = BlockMix_pwxform{salsa20/2, ctx, r}(B)`. Input `B` must be 128 bytes in length. /// - /// The input `B` must be 128r bytes in length. + /// `BlockMix_pwxform` differs from scrypt’s `BlockMix` in that it doesn’t shuffle output + /// sub-blocks, uses pwxform in place of Salsa20/8 for as long as sub-blocks processed with + /// pwxform fit in the provided block B, and finally uses Salsa20/2 (that is, Salsa20 with only + /// one double-round) to post-process the last sub-block output by pwxform (thereby finally + /// mixing pwxform’s parallel lanes). pub(crate) fn blockmix_pwxform(&mut self, b: &mut [u32], r: usize) { // Convert 128-byte blocks to PWXbytes blocks // TODO(tarcieri): use upstream `[T]::as_chunks_mut` when MSRV is 1.88