dcrypt-sign 4.0.1

Digital Signature Schemes for the dcrypt library
Documentation

Digital Signature Schemes

Crates.io Docs.rs License

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

Algorithm Variants Implemented Standard
ML-DSA MlDsa44, MlDsa65, MlDsa87 FIPS 204

Traditional Signatures

Algorithm Variants Implemented Standard
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.

[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)

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)

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.

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.