# Digital Signature Schemes
[](https://crates.io/crates/dcrypt-sign)
[](https://docs.rs/dcrypt-sign)
[](https://crates.io/crates/dcrypt-sign)
Digital Signature Schemes for the dcrypt library.
## Overview
`dcrypt-sign` exposes protected APIs for traditional and post-quantum signature
schemes. ECDSA, Ed25519, and ML-DSA implement the `dcrypt-api` signature traits.
BLS uses a dedicated API so its secret key can remain deliberately non-`Clone`
and its aggregation and proof-of-possession rules stay visible in the type
surface. Individual implementations target the encodings and equations in the
cited standards; this is not a blanket conformance, side-channel, or
certification claim.
Published versions through `v1.2.3` contain critical defects. `v2.0.0` retains
important remediations but is withdrawn because it violates dcrypt's
zero-unsafe/zero-FFI implementation policy. Version 3.0.0 is the supported
corrective line; see the workspace `SECURITY.md` before use. This crate is not
FIPS validated or certified.
## Features
- **Unified API**: All signature schemes implement the `dcrypt-api::Signature` trait for consistent usage.
- **Post-Quantum Cryptography**: Includes dcrypt-owned safe-Rust final FIPS 204 ML-DSA for key generation, deterministic or caller-randomized signing, verification, and complete expanded-key validation. Independent implementations are isolated to the non-published verification workspace.
- **Traditional Cryptography**: Provides implementations for industry-standard algorithms:
- ECDSA over NIST curves P-224, P-256, P-384, and P-521, with strict DER and low-`s` policy.
- Ed25519 with RFC 8032 encoding and strict verification behavior.
- Minimum-public-key BLS12-381 Basic, Message Augmentation, and Proof of Possession profiles pinned to `draft-irtf-cfrg-bls-signature-07`, plus an explicitly separate Ethereum draft-v4 PoP adapter.
- **Security Focused**:
- Exact-size secret-key owners invoke best-effort clearing on drop. This
cannot erase caller, compiler/register, allocator, swap, or crash-dump
copies.
- Deterministic signing for Ed25519 and deterministic nonce generation (RFC 6979) for ECDSA to enhance security against fault attacks and weak RNGs.
- Secret-dependent ML-DSA signing work uses the fixed public rejection
window required by the release policy; target-specific compiler and
timing validation remains necessary.
- **Selective Compilation**: `traditional` enables ECDSA and Ed25519; `post-quantum` enables ML-DSA.
- **No placeholder algorithms**: Public types are exposed only for implemented signature schemes.
The `alloc` feature supports allocator-backed `no_std` builds. Applications are
responsible for supplying a compatible allocator and a cryptographic RNG.
## Implemented Schemes
### Post-Quantum Signatures
| **ML-DSA** | `MlDsa44`, `MlDsa65`, `MlDsa87` | FIPS 204 |
### Traditional Signatures
| **ECDSA** | `EcdsaP224`, `EcdsaP256`, `EcdsaP384`, `EcdsaP521` | FIPS 186-5 |
| **EdDSA** | `Ed25519` | RFC 8032 |
| **BLS12-381 (minimum public key)** | `Bls12381G2Basic`, `Bls12381G2MessageAugmentation`, `Bls12381G2ProofOfPossession`; separate `Eth2Bls12381G2PopV4` adapter | CFRG BLS draft-07; Ethereum draft-v4 profile |
## Installation
Do not use v1 or v2. Pin the exact reviewed v3 release and select only the
required features.
```toml
[dependencies]
dcrypt = { version = "=3.0.0", default-features = false, features = ["std", "sign", "traditional", "post-quantum"] }
```
dcrypt never obtains operating-system entropy. Key generation and randomized
ML-DSA signing require an application-owned type implementing
`dcrypt::internal::{RngCore, CryptoRng}`. `ChaCha20Rng::from_seed` is provided
for callers that already have a fresh 32-byte seed from their own trusted
entropy boundary; reusing or hard-coding that seed is insecure.
## Usage
ECDSA, Ed25519, and ML-DSA implement `dcrypt::api::Signature`. BLS uses the
dedicated protected API shown below.
### Example: BLS12-381 Basic (minimum public key)
```rust
use dcrypt::sign::bls::{Bls12381G2Basic, Bls12381SecretKey};
fn sign(ikm: &[u8]) -> dcrypt::api::Result<()> {
// Draft-07 requires at least 32 unpredictable IKM bytes and an explicit
// caller-chosen salt. An application may instead call `generate` with its
// own CryptoRng.
let secret = Bls12381SecretKey::key_gen(ikm, b"example application salt")?;
let public = secret.public_key()?;
let message = b"standard BLS signature";
let signature = Bls12381G2Basic::sign(&secret, message)?;
Bls12381G2Basic::verify(&public, message, &signature)?;
Ok(())
}
```
Use `Bls12381G2ProofOfPossession` when same-message aggregation is required;
its aggregate verification methods take and validate a proof for every public
key. Ethereum consensus callers must deliberately select
`Eth2Bls12381G2PopV4`, whose verification contract relies on Ethereum's
registration-time proof validation and whose empty fast-aggregate case matches
the consensus specification.
### Example: ML-DSA-44 (Post-Quantum)
```rust
use dcrypt::api::Signature;
use dcrypt::internal::ChaCha20Rng;
use dcrypt::sign::MlDsa44;
fn sign_with_application_entropy(seed: [u8; 32]) -> dcrypt::api::Result<()> {
let mut rng = ChaCha20Rng::from_seed(seed);
let message = b"This is a test message for ML-DSA.";
// 1. Generate a keypair
let (pk, sk) = MlDsa44::keypair(&mut rng)?;
// 2. Sign the message with the secret key
println!("Signing message...");
let signature = MlDsa44::sign_with_rng(message, &sk, &mut rng)?;
println!("Signature generated successfully.");
// 3. Verify the signature with the public key
println!("Verifying signature...");
MlDsa44::verify(message, &signature, &pk)?;
println!("Signature is valid!");
// Verification will fail for a tampered message
let tampered_message = b"This is a tampered message.";
assert!(MlDsa44::verify(tampered_message, &signature, &pk).is_err());
println!("Signature verification failed for tampered message, as expected.");
Ok(())
}
```
### Example: Ed25519 (Traditional)
The API remains the same, just switch the type.
```rust
use dcrypt::api::Signature;
use dcrypt::internal::ChaCha20Rng;
use dcrypt::sign::Ed25519;
fn sign_with_application_entropy(seed: [u8; 32]) -> dcrypt::api::Result<()> {
let mut rng = ChaCha20Rng::from_seed(seed);
let message = b"A message signed with Ed25519.";
// 1. Generate a keypair
let (pk, sk) = Ed25519::keypair(&mut rng)?;
// 2. Sign the message
let signature = Ed25519::sign(message, &sk)?;
// 3. Verify the signature
assert!(Ed25519::verify(message, &signature, &pk).is_ok());
println!("Ed25519 signature is valid!");
Ok(())
}
```
## Feature Flags
This crate uses feature flags to control which code is included, allowing you to optimize binary size by excluding unused algorithm families.
- `std`: (Enabled by default) Enables functionality that requires the standard library.
- `traditional`: Enables ECDSA, Ed25519, and BLS12-381 signature schemes.
- `post-quantum`: Enables ML-DSA signature schemes.
The selected dcrypt facade features determine which families are exposed.
## Security
Secret key types overwrite their initialized storage on drop using dcrypt's
safe-Rust zeroing trait. Safe Rust cannot promise that an optimizing compiler
or every platform will preserve every wipe without target-specific inspection.
Users should also:
- Supply fresh cryptographic entropy through the caller-owned RNG boundary.
- Protect secret key material at rest (e.g., via encryption) and in transit.
- Ensure the authenticity of public keys before use to prevent impersonation attacks.
## License
This crate is licensed under the terms of the license specified in `Cargo.toml`.
## Contribution
Contributions are welcome! Please feel free to submit pull requests or open issues on the project repository.