Development

Hybrid Classical and Post-Quantum Signatures: Running ECDSA and ML-DSA Together

How hybrid signature schemes work in practice: signing transactions with both ECDSA and ML-DSA simultaneously, the overhead costs, how to structure transaction formats that carry both signatures, and when to sunset the classical leg of the scheme.

QuanChain Research
August 30, 2026
12 min read
Share
Hybrid Classical and Post-Quantum Signatures: Running ECDSA and ML-DSA Together

When you migrate a live blockchain from classical to post-quantum cryptography, you face a compatibility problem. Existing wallets, exchanges, and infrastructure understand ECDSA. New clients need to produce and verify ML-DSA signatures. If you flip a switch and require ML-DSA overnight, every client that has not updated stops working. If you accept only ECDSA indefinitely, you never get quantum resistance.

The answer is hybrid signing: every transaction carries both an ECDSA signature and an ML-DSA signature. Clients that understand ML-DSA verify both. Clients that only understand ECDSA verify what they can. After a defined transition window, you sunset ECDSA and require ML-DSA. This is the approach NIST recommends in its transition guidance, and it is the pattern used in post-quantum TLS hybrid key exchange deployments that are already in production across the internet.

Why Run Both Signature Schemes

Hybrid classical and post-quantum signatures provide backwards compatibility and defense in depth during the migration window. A transaction signed with both ECDSA and ML-DSA can be verified by any client regardless of which scheme it supports, while ensuring that a quantum-capable adversary cannot forge a transaction using only the broken classical scheme.

There are three independent reasons to run both schemes in parallel rather than cutting over immediately:

Backwards compatibility during the transition window. You cannot upgrade all clients simultaneously. Wallets, mobile apps, hardware devices, exchanges, and downstream integrations each have their own update cycles. A hybrid scheme allows all of them to continue operating on the ECDSA leg while new clients adopt ML-DSA verification. The transition window is measured in years, not months.

Defense in depth against implementation bugs. ML-DSA implementations in production are newer than ECDSA implementations that have been tested for a decade. If a bug in a new ML-DSA library produces invalid signatures in certain edge cases, the ECDSA signature on hybrid transactions remains a valid security backstop. You can patch the ML-DSA implementation without losing transaction validity.

Defense in depth against cryptographic breaks. Both directions matter. If an ML-DSA implementation flaw is discovered, ECDSA provides continuity. If a quantum computer breaks ECDSA, ML-DSA provides quantum resistance. The security of a hybrid scheme is at least as strong as the stronger of the two components, as long as the verification policy requires both signatures to be present and at least one to be valid.

Signature Size Overhead

The cost of hybrid signing is additive. Per transaction, you pay:

  • ECDSA signature: 64 bytes (71-72 bytes DER-encoded)
  • ML-DSA-44 signature: 2,420 bytes
  • Combined: approximately 2,484 bytes of signature data per transaction

For comparison, ECDSA-only transactions carry 64-71 bytes. The hybrid overhead is roughly 38x the ECDSA-only baseline. This is a real cost you carry for the duration of the transition window. Design your transition timeline with this in mind: every month of hybrid operation is a month of 38x signature overhead in every block.

Some implementations also include both public keys in the transaction, which adds 33 bytes (ECDSA compressed) + 1,312 bytes (ML-DSA-44) = 1,345 bytes more. If your chain stores public keys separately (in an account state database keyed by address), you avoid this additional per-transaction cost. Audit your transaction format before assuming the worst-case.

Quick Win

Define your transition end date before you start the transition. Announce it publicly at the beginning. A vague "we will phase out ECDSA eventually" gives users no incentive to upgrade; a firm date (for example, "ECDSA signatures will not be accepted after block 10,000,000") creates a concrete migration deadline that drives action. Build countdown tooling into your wallet so users see the deadline.

Implementing a Hybrid Signing Function

A hybrid signing function takes a private key for each scheme and produces a combined signature object:

import oqs
import hashlib
from ecdsa import SigningKey, VerifyingKey, SECP256k1, BadSignatureError
from dataclasses import dataclass

@dataclass
class HybridSignature:
    ecdsa_sig: bytes      # 64-71 bytes
    mldsa_sig: bytes      # 2420 bytes (ML-DSA-44)

@dataclass
class HybridKeyPair:
    ecdsa_private: SigningKey
    ecdsa_public: VerifyingKey
    mldsa_private: bytes   # 2528 bytes
    mldsa_public: bytes    # 1312 bytes

def generate_hybrid_keypair() -> HybridKeyPair:
    ecdsa_sk = SigningKey.generate(curve=SECP256k1)
    with oqs.Signature("ML-DSA-44") as s:
        mldsa_pub = s.generate_keypair()
        mldsa_priv = s.export_secret_key()
    return HybridKeyPair(
        ecdsa_private=ecdsa_sk,
        ecdsa_public=ecdsa_sk.get_verifying_key(),
        mldsa_private=mldsa_priv,
        mldsa_public=mldsa_pub,
    )

def hybrid_sign(keypair: HybridKeyPair, message_hash: bytes) -> HybridSignature:
    ecdsa_sig = keypair.ecdsa_private.sign_digest(message_hash)
    with oqs.Signature("ML-DSA-44", secret_key=keypair.mldsa_private) as signer:
        mldsa_sig = signer.sign(message_hash)
    return HybridSignature(ecdsa_sig=ecdsa_sig, mldsa_sig=mldsa_sig)

def hybrid_verify(
    pub_ecdsa: VerifyingKey,
    pub_mldsa: bytes,
    message_hash: bytes,
    hybrid_sig: HybridSignature,
    require_both: bool = False,
) -> bool:
    ecdsa_valid = False
    mldsa_valid = False
    try:
        ecdsa_valid = pub_ecdsa.verify_digest(hybrid_sig.ecdsa_sig, message_hash)
    except BadSignatureError:
        pass
    with oqs.Signature("ML-DSA-44") as verifier:
        mldsa_valid = verifier.verify(message_hash, hybrid_sig.mldsa_sig, pub_mldsa)
    if require_both:
        return ecdsa_valid and mldsa_valid
    return ecdsa_valid or mldsa_valid

The require_both flag controls the verification policy. During the early transition phase, set it to False: accept if either is valid. Once you are confident the ML-DSA rollout is complete across clients, switch to True: require both. Before the ECDSA sunset, flip the policy to ML-DSA only and stop including ECDSA signatures in new transactions.

Transaction Format Design

A transaction that carries both signatures needs to encode them unambiguously. The simplest approach uses a length-prefixed structure:

import struct

def serialize_hybrid_tx(payload: bytes, hybrid_sig: HybridSignature) -> bytes:
    """
    Wire format:
    [4 bytes: payload length][payload bytes]
    [2 bytes: ECDSA sig length][ECDSA sig bytes]
    [4 bytes: ML-DSA sig length][ML-DSA sig bytes]
    """
    buf = b""
    buf += struct.pack(">I", len(payload)) + payload
    buf += struct.pack(">H", len(hybrid_sig.ecdsa_sig)) + hybrid_sig.ecdsa_sig
    buf += struct.pack(">I", len(hybrid_sig.mldsa_sig)) + hybrid_sig.mldsa_sig
    return buf

def deserialize_hybrid_tx(raw: bytes):
    offset = 0
    payload_len = struct.unpack_from(">I", raw, offset)[0]
    offset += 4
    payload = raw[offset:offset + payload_len]
    offset += payload_len
    ecdsa_len = struct.unpack_from(">H", raw, offset)[0]
    offset += 2
    ecdsa_sig = raw[offset:offset + ecdsa_len]
    offset += ecdsa_len
    mldsa_len = struct.unpack_from(">I", raw, offset)[0]
    offset += 4
    mldsa_sig = raw[offset:offset + mldsa_len]
    return payload, HybridSignature(ecdsa_sig=ecdsa_sig, mldsa_sig=mldsa_sig)

Using fixed-width length prefixes means your parser does not need to know the signature sizes at compile time. This is important because you might switch ML-DSA parameter sets during the transition (from ML-DSA-44 to ML-DSA-65, for example) without breaking the framing format. Include an algorithm version byte at the front of the signature section if you want to support parameter set negotiation.

Quick Win

Add a 1-byte signature scheme flags field to your transaction format, where bit 0 means "ECDSA present" and bit 1 means "ML-DSA present." This lets you phase out ECDSA gracefully: new transactions set only bit 1, legacy transactions set only bit 0, hybrid transactions set both bits. Your parser can branch on the flags field rather than trying to guess the format from byte offsets.

When to Sunset the ECDSA Leg

The ECDSA leg of a hybrid scheme should be retired when two conditions are met. First, greater than 99% of active addresses have successfully submitted at least one ML-DSA-signed transaction (confirming that their clients support the new scheme). Second, the defined sunset block height or date has passed and been communicated to users for at least 12 months.

Tracking the first condition requires on-chain analytics. Index every address that has submitted a hybrid transaction with a valid ML-DSA signature; the remainder are the "not yet upgraded" population. Wallet developers and exchanges should receive automated alerts when their address population drops below a threshold, prompting them to push updates.

After sunset, you can reduce transaction size by removing the ECDSA signature field entirely. Over time, this recaptures some of the storage overhead that the hybrid period imposed. For the background on how NIST arrived at ML-DSA as the primary post-quantum signature standard, see CRYSTALS-Dilithium explained. For the step-by-step migration mechanics, see migrating from ECDSA to ML-DSA.

Build on a Quantum-Safe Chain

QuanChain is the only blockchain built with post-quantum cryptography at the protocol layer. Explore the developer docs to start building.

View Developer Docs

Frequently Asked Questions

QuanChain Research

Research Division

The QuanChain Research Division investigates post-quantum cryptographic standards, quantum hardware timelines, and blockchain protocol security. Research outputs inform both the QuanChain protocol roadmap and the broader open-source post-quantum blockchain community.

Related Articles