Service Info

Status--
Endpointhttp://docker:8082
PQC AlgorithmML-DSA-44 / ML-DSA-65 / ML-DSA-87 + SLH-DSA
FIPS StandardFIPS 204 (ML-DSA) + FIPS 205 (SLH-DSA)

Where PQC is Used

Code & Binary Signing

Sign application binaries, libraries, and scripts with ML-DSA. Replaces GPG/ECDSA code signing.

ML-DSA-65 (FIPS 204)
📦
Container Image Signing

Sign container image digests (SHA-256 hashes) with ML-DSA. Replaces cosign/Notary with PQC.

ML-DSA-65 (FIPS 204)
🔧
Firmware & Long-Term Signing

Sign firmware images with SLH-DSA (FIPS 205) for long-term integrity. Hash-based signatures that survive even if lattice assumptions break.

SLH-DSA-SHA2-128s / 256s (FIPS 205)
Signature Verification

Verify any PQC signature against its public key using provider.verify().

ML-DSA-44 / ML-DSA-65 / ML-DSA-87

Description

This service demonstrates PQC code signing — the same patterns you'll implement in your CI/CD pipeline using the Qudo JNI provider.

Sign: provider.sign(data, privKey, "ML-DSA-65")
Verify: provider.verify(data, sig, pubKey, "ML-DSA-65")
Algorithms: ML-DSA-44 (IoT), ML-DSA-65 (general), ML-DSA-87 (highest security)

Generate a keypair once with provider.generateKeyPair("ML-DSA-65"), then sign every artifact. Ship the signature + public key alongside your binary.

POST /api/sign Sign data with ML-DSA-65 (general purpose)
🔒 Qudo provider: provider.sign(data, privKey, "ML-DSA-65"). Returns 3,309-byte signature + public key. Recommended for most use cases.
POST /api/sign Sign data with ML-DSA-44 (lightweight/IoT)
🔒 Qudo provider: provider.sign(data, privKey, "ML-DSA-44"). Smaller signatures (2,420 bytes), faster. Best for constrained devices.
POST /api/sign Sign data with ML-DSA-87 (highest security)
🔒 Qudo provider: provider.sign(data, privKey, "ML-DSA-87"). NIST Level 5. Larger signatures (4,627 bytes). Use for high-value, long-term data.
POST /api/sign Sign with SLH-DSA-SHA2-128s (long-term integrity)
🔒 Qudo provider: provider.sign(data, privKey, "SLH-DSA-SHA2-128s"). Hash-based signatures (FIPS 205) that survive even if lattice math breaks. Best for firmware and documents needing decades of integrity.
POST /api/verify Verify a PQC signature
🔒 Qudo provider: provider.verify(data, sig, pubKey, "ML-DSA-65") -> true/false.
POST /api/sign-container Sign a container image digest
🔒 Qudo provider: provider.sign(digest, privKey, "ML-DSA-65"). Quantum-safe replacement for cosign/Notary ECDSA signatures.
GET /api/algorithms List supported PQC signing algorithms and sizes
🔒 Returns ML-DSA-44/65/87 with FIPS standard, NIST level, signature size, and public key size.
GET /api/health Service health + available algorithms
🔒 Returns service status and list of ML-DSA algorithms available from the Qudo provider.

Signing Service Migration Reference

Replace classical code signing (GPG, ECDSA, RSA) with post-quantum ML-DSA signatures. Use this playground to try PQC signing, then use the Qudo JNI provider directly in your build pipeline.

1. When to Use 2. Try It (Interactive) 3. Choose Algorithm 4. Migrate Your System 5. Performance 6. Pitfalls 7. Compatibility 8. FAQ

1. When to Use PQC Signing

📦
Software Releases

Sign binaries, JARs, packages before distribution. Customers verify the signature to ensure the software wasn't tampered with.

📡
Container Images

Sign Docker/OCI image digests. Replaces cosign/Notary ECDSA signatures with quantum-safe ML-DSA.

📄
Documents & Contracts

Sign PDFs, legal documents, audit logs. ML-DSA-87 provides highest security for long-term validity.

🔌
Firmware Updates

Sign firmware for IoT/embedded devices. Devices verify the signature before applying updates.

2. Try It: Sign & Verify

Test all three ML-DSA security levels. Sign data, verify the signature, then try tampering to see verification fail.

Step 1: Sign Data

Step 2: Verify Signature

Verify the original data, then try tampering to see ML-DSA detect the change.

Bonus: Sign a Container Image

3. Choose Your Algorithm

AlgorithmSecuritySignature SizeSign LatencyBest For
ML-DSA-44NIST Level 22,420 bytes~118msIoT, high-throughput, constrained devices
ML-DSA-87NIST Level 54,627 bytes~125msHigh-value, government, long-term
Rule of thumb: Use ML-DSA-65 unless you have a specific reason for 44 (size-constrained) or 87 (maximum security). All three are quantum-safe — the difference is the security margin.

4. Migrate Your System

Replace your classical signing tool with the Qudo JNI provider. Your build pipeline stays the same — only the sign/verify step changes.

Before (Classical)

// GPG signing
gpg --sign --armor myapp.jar

// ECDSA
Signature signer = Signature.getInstance(
    "SHA256withECDSA");
signer.initSign(ecPrivateKey);
signer.update(data);
byte[] sig = signer.sign();

// cosign (container)
cosign sign --key cosign.key myimage@sha256:...

After (PQC via Qudo JNI)

// Generate keypair once at startup
QudoKeyPair keys = provider.generateKeyPair(
    "ML-DSA-65");

// Sign
byte[] sig = provider.sign(data,
    keys.getPrivateKeyPem(), "ML-DSA-65");

// Verify
boolean ok = provider.verify(data, sig,
    keys.getPublicKeyPem(), "ML-DSA-65");

Complete Java Implementation (Qudo JNI Provider)

import com.qudo.crypto.QudoCrypto;
import com.qudo.crypto.QudoKeyPair;
import java.nio.file.Files;
import java.nio.file.Path;

// ============================================================
// Initialize once at application startup
// ============================================================
QudoCrypto provider = QudoCrypto.create();

// Generate ML-DSA-65 keypair once (store securely, reuse across signs)
QudoKeyPair signingKeys = provider.generateKeyPair("ML-DSA-65");

// ============================================================
// Sign a binary artifact
// ============================================================
byte[] artifact = Files.readAllBytes(Path.of("myapp.jar"));
byte[] signature = provider.sign(artifact, signingKeys.getPrivateKeyPem(), "ML-DSA-65");

// Save signature and public key alongside artifact
Files.write(Path.of("myapp.jar.sig"), signature);
Files.write(Path.of("myapp.jar.pub"), signingKeys.getPublicKeyPem());

// ============================================================
// Verify (on the consumer side)
// ============================================================
byte[] sig = Files.readAllBytes(Path.of("myapp.jar.sig"));
byte[] pubKey = Files.readAllBytes(Path.of("myapp.jar.pub"));
boolean valid = provider.verify(artifact, sig, pubKey, "ML-DSA-65");

// ============================================================
// Sign a container image digest
// ============================================================
byte[] digest = "sha256:e3b0c44298fc1c149afbf4c8996fb924...".getBytes();
byte[] containerSig = provider.sign(digest, signingKeys.getPrivateKeyPem(), "ML-DSA-65");

provider.close();
This is what runs in your CI/CD pipeline. Add com.qudo:qudo-jni-crypto to your build and set -Djava.library.path to the native library location. Generate the keypair once, sign every artifact with provider.sign(), ship the signature + public key alongside.

Summary: What Changes vs What Stays

ComponentChanges?What to do
Signing toolYes — use Qudoprovider.sign(data, privKey, "ML-DSA-65")
VerificationYes — use Qudoprovider.verify(data, sig, pubKey, "ML-DSA-65")
Key formatYesML-DSA PEM keys from provider.generateKeyPair()
Signature sizeYes2.4-4.6KB (vs 64-256 bytes for ECDSA/RSA)
Build pipelineMinimalReplace the sign step in CI/CD — same workflow, different call
DistributionNoShip signature + public key alongside artifact (same as classical)
Verification workflowNoConsumer gets artifact + sig + pubkey, verifies — same pattern

5. Performance

AlgorithmKey GenSignVerifySignature SizePublic Key Size
ML-DSA-44~50ms~118ms (total)~40ms2,420 bytes1,860 bytes (PEM)
ML-DSA-65~55ms~120ms (total)~41ms3,309 bytes2,726 bytes (PEM)
ML-DSA-87~60ms~125ms (total)~45ms4,627 bytes3,595 bytes (PEM)
ECDSA P-256 (classical)~1ms~2ms~2ms64 bytes65 bytes
Key takeaway: ML-DSA signing is ~60x slower than ECDSA and produces ~50x larger signatures. This is acceptable for release signing (done once per build) but may need optimization for high-frequency signing. Verify is only ~20x slower, which is acceptable for all use cases.

6. Common Pitfalls

Pitfall: Must use -rawin flag for ML-DSA signing

Cause: Without -rawin, OpenSSL treats the input as a pre-hashed digest (max 64 bytes).
Fix: Always use openssl pkeyutl -sign -rawin -inkey key.pem -in data.bin -out sig.bin

Pitfall: Generating a new keypair per-sign in production

Cause: This demo service generates a fresh keypair per request. In production, this is wasteful and loses the key binding.
Fix: Generate your signing keypair once (at deployment), store the private key securely, and reuse it for all signing operations. Publish the public key for verifiers.

Pitfall: SLH-DSA doesn't work via pkeyutl

Cause: The Qudo provider supports SLH-DSA for certificate signing (openssl req) but not arbitrary data signing via pkeyutl.
Workaround: Use ML-DSA-87 for data signing. Use SLH-DSA only for certificate issuance via the CA service.

Pitfall: Large signatures in size-constrained environments

Cause: ML-DSA-65 signatures are 3.3KB. For IoT or embedded systems with limited storage/bandwidth, this may be too large.
Fix: Use ML-DSA-44 (2.4KB) for constrained environments. It's NIST Level 2, still quantum-safe.

7. Compatibility

ComponentMinimum VersionNotes
OpenSSL3.6.0+Required for ML-DSA keygen and signing
Qudo Provider1.0.0+FIPS-validated PQC provider
Java17+For the Qudo JNI provider runtime
Any languageN/AVerification needs Qudo provider or OpenSSL 3.6+ with Qudo module
Integration: Add the Qudo JNI provider (com.qudo:qudo-jni-crypto) to your build pipeline. Set -Djava.library.path to the native library location. For verification on the consumer side, install Qudo provider or OpenSSL 3.6+.

8. FAQ

Q: Should I generate a new keypair per signing request?

A: No. This demo generates a fresh keypair per request for simplicity. In production, generate your signing keypair once (during deployment or key ceremony), store the private key in a secure vault (HSM, KMS), and reuse it for all signing operations. Publish the public key for verifiers.

Q: Which algorithm should I use for code signing?

A: ML-DSA-65 for most use cases. It's NIST Level 3 (128-bit quantum security), balances signature size (3.3KB) and performance (~120ms). Use ML-DSA-44 only if you're signing millions of items per hour or targeting constrained devices. Use ML-DSA-87 for government/defense or data that must stay secure for 50+ years.

Q: Can I verify signatures without the Qudo provider?

A: Two options:
1. Call the POST /api/verify endpoint — the server verifies using Qudo and returns the result.
2. For offline verification, install OpenSSL 3.6+ with Qudo on the verifier's machine and use: openssl pkeyutl -verify -rawin -pubin -inkey pub.pem -in data.bin -sigfile sig.bin

Q: How do I add PQC signing to my CI/CD pipeline?

A: Add a signing step after your build:
1. Base64-encode your build artifact
2. POST to /api/sign with algorithm ML-DSA-65
3. Save the returned signature and public key alongside the artifact
4. In your deployment pipeline, POST to /api/verify before deploying
Same workflow as classical signing — just a different API call.

Q: Why is SLH-DSA not supported for data signing?

A: The Qudo provider currently supports SLH-DSA for certificate operations (openssl req) but not for arbitrary data signing via openssl pkeyutl. This is a provider limitation, not a protocol limitation. For long-term document signing, use ML-DSA-87 (highest security). For certificate signing, use the CA service which supports SLH-DSA.

Q: Are ML-DSA signatures deterministic?

A: ML-DSA supports both deterministic and randomized signing. The Qudo provider uses randomized signing by default, which means signing the same data twice produces different signatures. Both are valid and will verify correctly against the same public key.

Q: How do I rotate signing keys?

A: Generate a new ML-DSA keypair, publish the new public key, and start signing with the new private key. Keep the old public key available for verifying previously-signed artifacts. Use the /api/algorithms endpoint to confirm the key type. Consider using the Cloud KMS service for managed key rotation.

Crypto Inventory Scanner

Analyze any endpoint's cryptographic configuration. Enter a host:port to scan its TLS setup and identify what's quantum-safe vs what needs migration.

Scan Endpoint