#jws #etsi #signature #ades

jades

JSON Advanced Electronic Signatures (JAdES) — JWS signing and verification implementing ETSI TS 119 182-1

1 unstable release

0.1.0 Jun 1, 2026

#1734 in Cryptography

BSD-2-Clause

98KB
2K SLoC

jades

JSON Advanced Electronic Signatures (JAdES) — JWS signing and verification implementing ETSI TS 119 182-1, with conformance levels B-B through B-LTA.

Features

  • JAdES-B-B — Basic signature: JWS + signing time (sigT) + signer certificate (x5c)
  • JAdES-B-T — Signature timestamp: RFC 3161 token over signature value (sigTst in etsiU)
  • JAdES-B-LT — Long-term: certificate chain (xVals) + revocation data (rVals)
  • JAdES-B-LTA — Long-term archival: archive timestamp (arcTst) for indefinite validity
  • Minimal JWS — Built-in JWS Compact and JSON (Flattened/General) serialization
  • Detached signatures — sign/verify with an external (non-embedded) payload (RFC 7515 empty-payload form; the ETSI sigD header is not populated)
  • Three-phase remote signing — prepare/finalize pattern for HSM and cloud KMS backends (B-B and B-T)
  • Verification — in-crate signature verification, crit enforcement, level detection, and (with a trust store) certificate-chain, timestamp, and revocation validation
  • Post-quantum — ML-DSA (FIPS 204) signing and verification behind the pq feature

Design

Built on a minimal JWS (RFC 7515) implementation because existing Rust JWS crates don't support JWS JSON Serialization with custom unprotected headers (required for etsiU). Uses tsp-ltv for RFC 3161 timestamping, OCSP/CRL revocation, trust stores, and certificate chain validation.

jades (this crate)
   ├── jws/JWS Compact + JSON serialization, base64url
   ├── header/ETSI protected headers (sigT, x5c, sigD, ...)+ unprotected etsiU (sigTst, xVals, rVals, arcTst)
   ├── sign     — sign_jades_{bb,bt,blt,blta}
   ├── verify   — verify_jades: in-crate JWS signature verification + level detection
   ├── remote   — three-phase prepare/finalize for remote signing (B-B, B-T)
   └── signer   — JadesSigner trait + SoftwareSigner

JWS signature verification (RSA/ECDSA/EdDSA) is performed in-crate with
RustCrypto, using the raw JWS signature encodings of RFC 7518. tsp-ltv is used
for the trust- and time-related parts of higher conformance levels:

   ├── tsp    — RFC 3161 TSA client (sigTst, arcTst) + timestamp-token verification
   ├── ltv    — OCSP/CRL clients (rVals) + offline revocation checking
   └── trust  — TrustStore + certificate-chain verification

Quick start

Add to your Cargo.toml:

[dependencies]
jades = "0.1"

Network-dependent features are enabled by default (tsp, ltv). For B-B only (no network, no async runtime):

[dependencies]
jades = { version = "0.1", default-features = false }

Sign a JAdES-B-B

use jades::{sign_jades_bb, SignOptions};
use jades::signer::SoftwareSigner;

let signer = SoftwareSigner::from_p256(signing_key, cert_chain_der)?;
let payload = b"document content";
let opts = SignOptions::default();
let jws = sign_jades_bb(payload, &signer, &opts)?;

Three-phase remote signing

use jades::remote::{prepare_jades, finalize_jades_bb};
use jades::signer::JwsAlgorithm;
use jades::SignOptions;

// Phase 1: prepare (local)
let prepared = prepare_jades(payload, &cert_chain, JwsAlgorithm::ES256, &opts)?;

// Phase 2: sign (remote — send prepared.signing_input to HSM/KMS)
let signature = remote_sign(&prepared.signing_input)?;

// Phase 3: finalize (local)
let jws = finalize_jades_bb(prepared, &signature)?;

Verify

use jades::{verify_jades, VerifyOptions};

let opts = VerifyOptions::default();
let result = verify_jades(&jws, &opts)?;
assert!(result.signature_valid);
println!("Level: {}", result.level); // "JAdES-B-B", "JAdES-B-T", etc.

signature_valid is a cryptographic check only — it proves the signature matches the public key in the embedded signer certificate. To establish trust, supply a jades::TrustStore (re-exported from tsp-ltv) via VerifyOptions::trust_store; the signer chain (and any TSA chains) are then validated and reported in result.chain_valid. Without a trust store, chain_valid is None (not evaluated). For detached signatures, supply the external bytes via VerifyOptions::detached_payload.

Feature flags

Flag Default Description
tsp yes RFC 3161 timestamping + timestamp verification (B-T and above)
ltv yes OCSP/CRL + chain validation for B-LT/B-LTA (implies tsp)
pq no Post-quantum ML-DSA (FIPS 204) signing and verification — ML-DSA-44/65/87

The tsp/ltv signing and timestamp/revocation functions are async and require a Tokio runtime (pulled in by these features). The B-B signing and the verify_jades API are synchronous.

Serialization forms

Form Unprotected Headers Levels produced by signing
JWS Compact No B-B only
JWS JSON Flattened Yes (via etsiU) B-B, B-T, B-LT, B-LTA
JWS JSON General Yes (via etsiU) B-B only

For B-T and above, signing always emits the Flattened form (the JAdES common case). Verification accepts Compact, Flattened, and General forms; for a General JWS, every signature is verified.

Supported algorithms

Verification (verify_jades) supports all of the following:

  • RSA: RS256, RS384, RS512, PS256, PS384, PS512
  • ECDSA: ES256 (P-256), ES384 (P-384), ES512 (P-521)
  • EdDSA: Ed25519
  • ML-DSA (FIPS 204, pq feature): ML-DSA-44, ML-DSA-65, ML-DSA-87

The bundled SoftwareSigner can sign with ES256, ES384, Ed25519, the RSA algorithms, and — with the pq feature — ML-DSA-44/65/87 (via SoftwareSigner::from_ml_dsa_44 / _65 / _87). ES512 (P-521) and any other algorithm can be signed via a custom JadesSigner implementation (e.g. an HSM/KMS backend) or the three-phase remote-signing API.

ML-DSA uses the pure (non-prehashed) algorithm with an empty context string, the convention adopted by the JOSE/COSE post-quantum drafts. Construct ML-DSA keys through the re-exported jades::ml_dsa module so no separate dependency is needed.

License

BSD-2-Clause

Dependencies

~10–19MB
~354K SLoC