rscrypto 0.8.0

Pure Rust Cryptography: RSA, Ed25519, X25519, SHA-2/3, BLAKE2/3, AES-GCM/GCM-SIV, X/ChaCha20-Poly1305, Argon2, HMAC/HKDF, CRC. no_std, WASM, hardware acceleration.
Documentation
# Migration: `scrypt` (RustCrypto) → `rscrypto`

rscrypto separates deterministic key derivation from password-record handling:

- `Scrypt` is the raw RFC 7914 KDF.
- `ScryptPassword` generates canonical password records and verifies hostile PHC input under finite resource limits.

The implementation is checked against RFC 7914 vectors and the RustCrypto `scrypt 0.12.0` oracle in `tests/scrypt_vectors.rs` and `tests/scrypt_differential.rs`.

## Cargo features

```toml
# Raw scrypt KDF
rscrypto = { version = "0.7.8", default-features = false, features = ["scrypt"] }

# Password-record generation and verification
rscrypto = { version = "0.7.8", default-features = false, features = [
  "scrypt",
  "phc-strings",
  "getrandom",
] }
```

`getrandom` is needed only to generate password records. Verification needs `scrypt` and `phc-strings`.

## Raw KDF

RustCrypto:

```rust
use scrypt::{Params, scrypt};

let params = Params::new(15, 8, 1)?;
let mut output = [0u8; 32];
scrypt(password, salt, &params, &mut output)?;
```

rscrypto:

```rust
use rscrypto::{Scrypt, ScryptParams};

let params = ScryptParams::new(15, 8, 1)?;
let mut output = [0u8; 32];
Scrypt::derive(&params, password, salt, &mut output)?;
```

`ScryptParams::new(log_n, r, p)` rejects invalid profiles. As in RFC 7914 and RustCrypto, output length comes from the destination slice. The raw API accepts caller-provided salts, including the empty salt required by the RFC test vector; application salt policy belongs to the password-record API.

## Password records

`ScryptPassword` owns the generation profile and finite verification limits:

```rust
use rscrypto::{PasswordStatus, ScryptPassword};

let passwords = ScryptPassword::default();
let record = passwords.hash_password(password)?;

match passwords.verify_password(password, &record)? {
  PasswordStatus::Current => {}
  PasswordStatus::NeedsRehash => {
    let replacement = passwords.hash_password(password)?;
    // Store replacement atomically.
  }
}
```

Generation always uses a fresh 16-byte OS-random salt, a 32-byte verifier, and canonical `$scrypt$ln=...,r=...,p=...$...$...` encoding. There is no caller-salt password helper.

For a custom generation profile:

```rust
use rscrypto::{ScryptParams, ScryptPassword};

let generation = ScryptParams::new(17, 8, 1)?;
let passwords = ScryptPassword::new(generation)?;
```

To accept a broader finite migration envelope, derive limits from the largest deployment profile you intend to admit:

```rust
use rscrypto::{
  ScryptParams, ScryptPassword, ScryptVerificationLimits,
};

let generation = ScryptParams::new(17, 8, 1)?;
let accepted_profile = ScryptParams::new(18, 8, 1)?;
let limits = ScryptVerificationLimits::for_profile(accepted_profile);
let passwords = ScryptPassword::with_limits(generation, limits)?;
```

Limits compare the portable implementation's complete working-set bytes and `N × r × p` work. Verification rejects target-size overflow and over-budget parameters before base64 decoding, allocation, or KDF work.

## Compatibility boundary

The password verifier accepts only the protocol rscrypto emits:

- no PHC version segment;
- canonical ordered `ln,r,p` parameters;
- an 8–48-byte decoded salt;
- exactly 32 decoded verifier bytes;
- costs admitted by the configured finite limits.

Common canonical RustCrypto scrypt records with 32-byte outputs remain verifiable. Noncanonical encodings, different output sizes, and out-of-envelope profiles must be rehashed through the old stack during migration. rscrypto intentionally exposes neither a public PHC decoder nor an unbounded verifier.

## Operational notes

- `ScryptParams::default()` is `log_n=17, r=8, p=1`, matching the
  [OWASP Password Storage Cheat Sheet]https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
  baseline for deployments that cannot use Argon2id, as checked on 2026-07-29.
- The raw algorithm accepts arbitrary salt lengths; generated password records use 16 bytes.
- Working buffers are zeroized on drop. Target-size overflow and allocation failure are distinct errors.
- scrypt ROMix uses password-derived, data-dependent memory access and is not a local side-channel constant-time claim. The final verifier traverses all expected bytes before returning one opaque result; any generated-code timing claim is limited to the exact configuration in the matching [release evidence]../../constant-time.md.
- scrypt requires `alloc` and is outside rscrypto's
  [FIPS-oriented inventory]../../compliance.md. A validation effort must
  determine whether and how it may appear within the module boundary and
  approved mode.