Repo: RustCrypto/traits (affects pbkdf2 crate)
Labels: api-design, pbkdf2
Background
pbkdf2::pbkdf2 has this signature (simplified):
pub fn pbkdf2<PRF: Mac + Sync>(prf: PRF, salt: &[u8], rounds: u32, res: &mut [u8])
-> Result<(), InvalidLength>
The Sync bound exists to support the parallel feature, which distributes
PBKDF2 rounds across a thread pool. When multiple threads run rounds
concurrently, they need to share the PRF instance — hence Sync.
The problem
The Sync bound is present unconditionally, even when the parallel feature
is disabled and the function runs entirely on a single thread. A type that is
correctly !Sync — because it holds interior mutable state that is unsafe to
share — cannot call pbkdf2, even in a single-threaded program with
parallel = false.
This is a logic error in the API: Sync is a sharing guarantee, and no
sharing occurs when parallel is off. Requiring it in that case excludes
legitimate types for no benefit.
In our implementation
Discovered while implementing wolfcrypt, a RustCrypto backend wrapping wolfCrypt — a FIPS 140-3 validated C cryptographic library with hardware dispatch via WOLF_CRYPTO_CB.
wolfCrypt's EVP-based digest types (WolfSha256, etc.) are !Sync. The
EVP_MD_CTX C struct contains interior mutable state — counter values, partial
block buffers — that is updated on every call. It is not safe to read from two
threads simultaneously. Marking these types Sync would be unsound.
Because pbkdf2::pbkdf2 requires PRF: Sync unconditionally, this does not
compile even in a single-threaded binary with parallel = false:
pbkdf2::pbkdf2::<hmac::SimpleHmac<WolfSha256>>(password, salt, rounds, &mut out)?;
// error[E0277]: `WolfSha256` cannot be shared between threads safely
// = help: the trait `Sync` is not implemented for `WolfSha256`
// note: required by a bound in `pbkdf2`
Proposed change
Gate the Sync bound on the parallel feature flag:
#[cfg(feature = "parallel")]
pub fn pbkdf2<PRF: Mac + Clone + Sync>(
prf: PRF, salt: &[u8], rounds: u32, res: &mut [u8],
) -> Result<(), InvalidLength> { ... }
#[cfg(not(feature = "parallel"))]
pub fn pbkdf2<PRF: Mac + Clone>(
prf: PRF, salt: &[u8], rounds: u32, res: &mut [u8],
) -> Result<(), InvalidLength> { ... }
This is fully backward-compatible: all existing callers use Sync types and
are unaffected. Callers with !Sync types gain access to the function when
parallel is not enabled. This is the smallest and most self-contained change
in this set of issues.
Repo:
RustCrypto/traits(affectspbkdf2crate)Labels:
api-design,pbkdf2Background
pbkdf2::pbkdf2has this signature (simplified):The
Syncbound exists to support theparallelfeature, which distributesPBKDF2 rounds across a thread pool. When multiple threads run rounds
concurrently, they need to share the PRF instance — hence
Sync.The problem
The
Syncbound is present unconditionally, even when theparallelfeatureis disabled and the function runs entirely on a single thread. A type that is
correctly
!Sync— because it holds interior mutable state that is unsafe toshare — cannot call
pbkdf2, even in a single-threaded program withparallel = false.This is a logic error in the API:
Syncis a sharing guarantee, and nosharing occurs when
parallelis off. Requiring it in that case excludeslegitimate types for no benefit.
In our implementation
Discovered while implementing
wolfcrypt, a RustCrypto backend wrapping wolfCrypt — a FIPS 140-3 validated C cryptographic library with hardware dispatch viaWOLF_CRYPTO_CB.wolfCrypt's EVP-based digest types (
WolfSha256, etc.) are!Sync. TheEVP_MD_CTXC struct contains interior mutable state — counter values, partialblock buffers — that is updated on every call. It is not safe to read from two
threads simultaneously. Marking these types
Syncwould be unsound.Because
pbkdf2::pbkdf2requiresPRF: Syncunconditionally, this does notcompile even in a single-threaded binary with
parallel = false:pbkdf2::pbkdf2requiresSyncunconditionally":wolfcrypt/src/lib.rs:136-145pbkdf2module explains whySimpleHmac<WolfSha256>is blocked:wolfcrypt/src/pbkdf2.rs:8-16!Sync:wolfcrypt/src/aead.rs:22-27Proposed change
Gate the
Syncbound on theparallelfeature flag:This is fully backward-compatible: all existing callers use
Synctypes andare unaffected. Callers with
!Synctypes gain access to the function whenparallelis not enabled. This is the smallest and most self-contained changein this set of issues.