dcrypt 2.0.0

Rust APIs for classical, post-quantum, and hybrid cryptographic primitives
Documentation
# ML-DSA signatures (`sign/dilithium`)

The historical `dilithium` module now exposes final-standard FIPS 204 ML-DSA.
Key generation, signing, and verification use libcrux's portable backend. Its
field arithmetic, NTT polynomial arithmetic, and serialization components are
formally verified, but dcrypt as a whole is not formally verified, independently
audited, or FIPS validated. The preferred type names are
`MlDsa44`, `MlDsa65`, and `MlDsa87`. `Dilithium2`, `Dilithium3`, and
`Dilithium5` remain source-level aliases only; they do not restore compatibility
with objects emitted by the confirmed-affected dcrypt `v1.2.3`. The exact
introduction range in earlier releases remains under investigation.

The public `Signature` wrapper implements randomized pure ML-DSA with an empty
context. The test harness exercises libcrux's full pure/context, prehash, and
internal ACVP interfaces against official expected results; the externally
supplied-`mu` interface alone uses RustCrypto in tests because libcrux does not
expose it. Wrapper-level key/signature behavior also has bidirectional
differential tests. Test-only implementations do not process untrusted keys at
runtime.

## Encoding boundary

The adapter accepts and emits the exact final FIPS 204 key and signature sizes.
Expanded private keys contain the standard 64-byte
`tr = SHAKE256(public_key, 64)` value. Signature parsing enforces canonical hint
encoding, including strictly increasing indices, monotonic boundaries, and zero
unused padding.

Legacy dcrypt keys used a 32-byte `tr` followed by synthetic padding, and legacy
signatures admitted noncanonical hints. They are nonstandard objects and are
not knowingly migrated or relabeled. A bare expanded secret has no version tag,
so provenance/versioned framing is required to distinguish formats reliably;
paired import rejects legacy keys through `tr` and sign/verify coherence checks.

A bare final-standard expanded key can be syntactically decoded with
`DilithiumSecretKey::from_bytes`, but libcrux does not expose public-key
derivation from that representation. Prefer `from_bytes_with_public_key`, which
checks the 64-byte `tr`, performs a libcrux sign/verify coherence check, and
caches the paired public key. Calling `public_key()` after a bare import returns
an error rather than invoking a second secret-arithmetic implementation.

## Usage

```rust
use dcrypt::api::Signature;
use dcrypt::sign::MlDsa65;
use rand::rngs::OsRng;

# fn main() -> dcrypt::api::Result<()> {
let (public_key, secret_key) = MlDsa65::keypair(&mut OsRng)?;
let message = b"ML-DSA test message";
let signature = MlDsa65::sign(message, &secret_key)?;
MlDsa65::verify(message, &signature, &public_key)?;
# Ok(())
# }
```

## Validation

The test suite covers all three parameter sets, exact serialization sizes,
canonical negative cases, full backend-level NIST ACVP expected results, and
cross-imported wrapper keys/signatures between libcrux and the independent
test-only implementation.

See [FIPS 204](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.204.pdf) for the
standard. See the workspace `SECURITY.md` before using any published dcrypt
version.