Skip to content

Commit c924759

Browse files
authored
yescrypt: test and CI fixups (#692)
- use cargo hack to test feature combos - test release builds - test the specific error is `Error::Password` for password verification failures - add rustdoc for `yescrypt_verify`
1 parent ea2c74e commit c924759

File tree

4 files changed

+60
-50
lines changed

4 files changed

+60
-50
lines changed

.github/workflows/yescrypt.yml

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,11 @@ jobs:
4949
with:
5050
toolchain: ${{ matrix.rust }}
5151
targets: ${{ matrix.target }}
52+
- uses: RustCrypto/actions/cargo-hack-install@master
5253
- run: ${{ matrix.deps }}
53-
- run: cargo test
54-
- run: cargo test --release
54+
- run: cargo check --target ${{ matrix.target }} --all-features
55+
- run: cargo hack test --target ${{ matrix.target }} --feature-powerset
56+
- run: cargo test --target ${{ matrix.target }} --all-features --release
5557

5658
careful:
5759
runs-on: ubuntu-latest
@@ -61,8 +63,8 @@ jobs:
6163
with:
6264
toolchain: nightly-2025-08-10
6365
- run: cargo install cargo-careful
64-
- run: cargo careful test
65-
- run: cargo careful test --release
66+
- run: cargo careful test --all-features
67+
- run: cargo careful test --all-features --release
6668

6769
cross:
6870
strategy:
@@ -81,5 +83,5 @@ jobs:
8183
toolchain: ${{ matrix.rust }}
8284
targets: ${{ matrix.target }}
8385
- uses: RustCrypto/actions/cross-install@master
84-
- run: cross test --target ${{ matrix.target }}
85-
- run: cross test --release --target ${{ matrix.target }}
86+
- run: cross test --target ${{ matrix.target }} --all-features
87+
- run: cross test --target ${{ matrix.target }} --all-features --release

yescrypt/src/encoding.rs

Lines changed: 45 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// TODO(tarcieri): use `base64ct` instead?
33

44
use crate::{Error, Result};
5+
6+
#[cfg(feature = "simple")]
57
use core::str;
68

79
/// (s)crypt-flavored Base64 alphabet.
@@ -18,6 +20,7 @@ pub const ATOI64: [u8; 128] = {
1820
tbl
1921
};
2022

23+
#[cfg(feature = "simple")]
2124
pub(crate) fn decode64<'o>(src: &str, dst: &'o mut [u8]) -> Result<&'o [u8]> {
2225
let src = src.as_bytes();
2326
let mut pos = 0usize;
@@ -108,6 +111,48 @@ pub(crate) fn decode64_uint32(src: &[u8], mut pos: usize, min: u32) -> Result<(u
108111
Ok((dst, pos))
109112
}
110113

114+
#[cfg(feature = "simple")]
115+
pub(crate) fn encode64<'o>(src: &[u8], out: &'o mut [u8]) -> Result<&'o str> {
116+
fn encode64_uint32_fixed(dst: &mut [u8], mut src: u32, srcbits: u32) -> Result<usize> {
117+
let mut bits: u32 = 0;
118+
let mut pos = 0;
119+
120+
while bits < srcbits {
121+
if dst.len() <= pos {
122+
return Err(Error::Encoding);
123+
}
124+
125+
dst[pos] = ITOA64[(src & 0x3f) as usize];
126+
pos += 1;
127+
src >>= 6;
128+
bits += 6;
129+
}
130+
131+
if src != 0 || dst.len() < pos {
132+
return Err(Error::Encoding);
133+
}
134+
135+
Ok(pos)
136+
}
137+
138+
let mut pos = 0;
139+
let mut i = 0;
140+
141+
while i < src.len() {
142+
let mut value = 0u32;
143+
let mut bits = 0u32;
144+
while bits < 24 && i < src.len() {
145+
value |= (src[i] as u32) << bits;
146+
bits += 8;
147+
i += 1;
148+
}
149+
let dnext = encode64_uint32_fixed(&mut out[pos..], value, bits)?;
150+
pos += dnext;
151+
}
152+
153+
str::from_utf8(&out[..pos]).map_err(|_| Error::Encoding)
154+
}
155+
111156
pub(crate) fn encode64_uint32(dst: &mut [u8], mut src: u32, min: u32) -> Result<usize> {
112157
let mut start = 0u32;
113158
let mut end = 47u32;
@@ -152,44 +197,3 @@ pub(crate) fn encode64_uint32(dst: &mut [u8], mut src: u32, min: u32) -> Result<
152197

153198
Ok(pos)
154199
}
155-
156-
pub(crate) fn encode64<'o>(src: &[u8], out: &'o mut [u8]) -> Result<&'o str> {
157-
let mut pos = 0;
158-
let mut i = 0;
159-
160-
while i < src.len() {
161-
let mut value = 0u32;
162-
let mut bits = 0u32;
163-
while bits < 24 && i < src.len() {
164-
value |= (src[i] as u32) << bits;
165-
bits += 8;
166-
i += 1;
167-
}
168-
let dnext = encode64_uint32_fixed(&mut out[pos..], value, bits)?;
169-
pos += dnext;
170-
}
171-
172-
str::from_utf8(&out[..pos]).map_err(|_| Error::Encoding)
173-
}
174-
175-
fn encode64_uint32_fixed(dst: &mut [u8], mut src: u32, srcbits: u32) -> Result<usize> {
176-
let mut bits: u32 = 0;
177-
let mut pos = 0;
178-
179-
while bits < srcbits {
180-
if dst.len() <= pos {
181-
return Err(Error::Encoding);
182-
}
183-
184-
dst[pos] = ITOA64[(src & 0x3f) as usize];
185-
pos += 1;
186-
src >>= 6;
187-
bits += 6;
188-
}
189-
190-
if src != 0 || dst.len() < pos {
191-
return Err(Error::Encoding);
192-
}
193-
194-
Ok(pos)
195-
}

yescrypt/src/lib.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@
3030
//! let password = b"pleaseletmein"; // don't actually use this as a password!
3131
//! let salt = b"WZaPV7LSUEKMo34."; // unique per password, ideally 16-bytes and random
3232
//! let password_hash = yescrypt::yescrypt(password, salt, &Default::default())?;
33-
//! assert_eq!(&password_hash[..3], "$y$");
33+
//! assert!(password_hash.starts_with("$y$"));
34+
//!
35+
//! // verify password is correct for the given hash
36+
//! yescrypt::yescrypt_verify(password, &password_hash)?;
3437
//! # Ok(())
3538
//! # }
3639
//! ```
@@ -122,6 +125,7 @@ pub fn yescrypt(passwd: &[u8], salt: &[u8], params: &Params) -> Result<String> {
122125
/// Verify a password matches the given yescrypt password hash.
123126
///
124127
/// Password hash should begin with `$y$` in Modular Crypt Format (MCF).
128+
#[cfg(feature = "simple")]
125129
pub fn yescrypt_verify(passwd: &[u8], hash: &str) -> Result<()> {
126130
let hash = mcf::PasswordHashRef::try_from(hash).map_err(|_| Error::Encoding)?;
127131

yescrypt/tests/simple.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#![cfg(feature = "simple")]
44
#![allow(non_snake_case)]
55

6-
use yescrypt::{Flags, Params, yescrypt, yescrypt_verify};
6+
use yescrypt::{Error, Flags, Params, yescrypt, yescrypt_verify};
77

88
const YESCRYPT_P: u32 = 11;
99

@@ -74,7 +74,7 @@ fn verify_reference_strings() {
7474
panic!("failed to verify password hash: {hash}");
7575
}
7676

77-
if yescrypt_verify(b"bogus", hash).is_ok() {
77+
if yescrypt_verify(b"bogus", hash) != Err(Error::Password) {
7878
panic!("verification unexpectedly succeeded for password hash: {hash}");
7979
}
8080
}

0 commit comments

Comments
 (0)