diff --git a/.github/workflows/yescrypt.yml b/.github/workflows/yescrypt.yml index a88f4851..863eb943 100644 --- a/.github/workflows/yescrypt.yml +++ b/.github/workflows/yescrypt.yml @@ -49,9 +49,11 @@ jobs: with: toolchain: ${{ matrix.rust }} targets: ${{ matrix.target }} + - uses: RustCrypto/actions/cargo-hack-install@master - run: ${{ matrix.deps }} - - run: cargo test - - run: cargo test --release + - run: cargo check --target ${{ matrix.target }} --all-features + - run: cargo hack test --target ${{ matrix.target }} --feature-powerset + - run: cargo test --target ${{ matrix.target }} --all-features --release careful: runs-on: ubuntu-latest @@ -61,8 +63,8 @@ jobs: with: toolchain: nightly-2025-08-10 - run: cargo install cargo-careful - - run: cargo careful test - - run: cargo careful test --release + - run: cargo careful test --all-features + - run: cargo careful test --all-features --release cross: strategy: @@ -81,5 +83,5 @@ jobs: toolchain: ${{ matrix.rust }} targets: ${{ matrix.target }} - uses: RustCrypto/actions/cross-install@master - - run: cross test --target ${{ matrix.target }} - - run: cross test --release --target ${{ matrix.target }} + - run: cross test --target ${{ matrix.target }} --all-features + - run: cross test --target ${{ matrix.target }} --all-features --release diff --git a/yescrypt/src/encoding.rs b/yescrypt/src/encoding.rs index 984612e9..85f798b8 100644 --- a/yescrypt/src/encoding.rs +++ b/yescrypt/src/encoding.rs @@ -2,6 +2,8 @@ // TODO(tarcieri): use `base64ct` instead? use crate::{Error, Result}; + +#[cfg(feature = "simple")] use core::str; /// (s)crypt-flavored Base64 alphabet. @@ -18,6 +20,7 @@ pub const ATOI64: [u8; 128] = { tbl }; +#[cfg(feature = "simple")] pub(crate) fn decode64<'o>(src: &str, dst: &'o mut [u8]) -> Result<&'o [u8]> { let src = src.as_bytes(); let mut pos = 0usize; @@ -108,6 +111,48 @@ pub(crate) fn decode64_uint32(src: &[u8], mut pos: usize, min: u32) -> Result<(u Ok((dst, pos)) } +#[cfg(feature = "simple")] +pub(crate) fn encode64<'o>(src: &[u8], out: &'o mut [u8]) -> Result<&'o str> { + fn encode64_uint32_fixed(dst: &mut [u8], mut src: u32, srcbits: u32) -> Result { + let mut bits: u32 = 0; + let mut pos = 0; + + while bits < srcbits { + if dst.len() <= pos { + return Err(Error::Encoding); + } + + dst[pos] = ITOA64[(src & 0x3f) as usize]; + pos += 1; + src >>= 6; + bits += 6; + } + + if src != 0 || dst.len() < pos { + return Err(Error::Encoding); + } + + Ok(pos) + } + + let mut pos = 0; + let mut i = 0; + + while i < src.len() { + let mut value = 0u32; + let mut bits = 0u32; + while bits < 24 && i < src.len() { + value |= (src[i] as u32) << bits; + bits += 8; + i += 1; + } + let dnext = encode64_uint32_fixed(&mut out[pos..], value, bits)?; + pos += dnext; + } + + str::from_utf8(&out[..pos]).map_err(|_| Error::Encoding) +} + pub(crate) fn encode64_uint32(dst: &mut [u8], mut src: u32, min: u32) -> Result { let mut start = 0u32; let mut end = 47u32; @@ -152,44 +197,3 @@ pub(crate) fn encode64_uint32(dst: &mut [u8], mut src: u32, min: u32) -> Result< Ok(pos) } - -pub(crate) fn encode64<'o>(src: &[u8], out: &'o mut [u8]) -> Result<&'o str> { - let mut pos = 0; - let mut i = 0; - - while i < src.len() { - let mut value = 0u32; - let mut bits = 0u32; - while bits < 24 && i < src.len() { - value |= (src[i] as u32) << bits; - bits += 8; - i += 1; - } - let dnext = encode64_uint32_fixed(&mut out[pos..], value, bits)?; - pos += dnext; - } - - str::from_utf8(&out[..pos]).map_err(|_| Error::Encoding) -} - -fn encode64_uint32_fixed(dst: &mut [u8], mut src: u32, srcbits: u32) -> Result { - let mut bits: u32 = 0; - let mut pos = 0; - - while bits < srcbits { - if dst.len() <= pos { - return Err(Error::Encoding); - } - - dst[pos] = ITOA64[(src & 0x3f) as usize]; - pos += 1; - src >>= 6; - bits += 6; - } - - if src != 0 || dst.len() < pos { - return Err(Error::Encoding); - } - - Ok(pos) -} diff --git a/yescrypt/src/lib.rs b/yescrypt/src/lib.rs index 4e043561..2fdc4cfa 100644 --- a/yescrypt/src/lib.rs +++ b/yescrypt/src/lib.rs @@ -30,7 +30,10 @@ //! let password = b"pleaseletmein"; // don't actually use this as a password! //! let salt = b"WZaPV7LSUEKMo34."; // unique per password, ideally 16-bytes and random //! let password_hash = yescrypt::yescrypt(password, salt, &Default::default())?; -//! assert_eq!(&password_hash[..3], "$y$"); +//! assert!(password_hash.starts_with("$y$")); +//! +//! // verify password is correct for the given hash +//! yescrypt::yescrypt_verify(password, &password_hash)?; //! # Ok(()) //! # } //! ``` @@ -122,6 +125,7 @@ pub fn yescrypt(passwd: &[u8], salt: &[u8], params: &Params) -> Result { /// Verify a password matches the given yescrypt password hash. /// /// Password hash should begin with `$y$` in Modular Crypt Format (MCF). +#[cfg(feature = "simple")] pub fn yescrypt_verify(passwd: &[u8], hash: &str) -> Result<()> { let hash = mcf::PasswordHashRef::try_from(hash).map_err(|_| Error::Encoding)?; diff --git a/yescrypt/tests/simple.rs b/yescrypt/tests/simple.rs index ad4965bc..069ed23a 100644 --- a/yescrypt/tests/simple.rs +++ b/yescrypt/tests/simple.rs @@ -3,7 +3,7 @@ #![cfg(feature = "simple")] #![allow(non_snake_case)] -use yescrypt::{Flags, Params, yescrypt, yescrypt_verify}; +use yescrypt::{Error, Flags, Params, yescrypt, yescrypt_verify}; const YESCRYPT_P: u32 = 11; @@ -74,7 +74,7 @@ fn verify_reference_strings() { panic!("failed to verify password hash: {hash}"); } - if yescrypt_verify(b"bogus", hash).is_ok() { + if yescrypt_verify(b"bogus", hash) != Err(Error::Password) { panic!("verification unexpectedly succeeded for password hash: {hash}"); } }