feat(crypto): add post-quantum signature support for nile

Closed
FE
3mo ago
+11091-186

Summary

Adds post-quantum (PQ) signature support to TRON across protocol, crypto, transaction/block validation, TVM precompiles, witness configuration, consensus signing, relay handshakes, governance flags, and tests.

Two PQ schemes are supported, each independently gated by a committee proposal:

  • FN_DSA_512 / Falcon-512 (variable-length signature, max 667 B, pubkey 896 B)
  • ML_DSA_44 / Dilithium-2 (fixed-length signature 2420 B, pubkey 1312 B)

Protocol changes

  • Adds PQScheme enum: UNKNOWN_PQ_SCHEME, FN_DSA_512, ML_DSA_44.
  • Adds PQAuthSig { scheme, public_key, signature } as the shared PQ authentication envelope.
  • Adds repeated pq_auth_sig to Transaction so ECDSA and PQ signatures coexist for account permission threshold checks.
  • Adds pq_auth_sig to BlockHeader; legacy witness_signature and PQ pq_auth_sig are mutually exclusive.
  • Adds pq_auth_sig to HelloMessage for relay/fast-forward authentication by PQ witnesses.

Address derivation

  • PQ addresses: 0x41 || deriveHash(scheme, public_key)[12..32], matching the ECDSA shape.
  • Both schemes use Keccak-256 as the fingerprint hash.
  • PQSchemeRegistry.computeAddress(scheme, publicKey) is the single entry point.

Crypto module (crypto)

  • FNDSA512: Falcon-512 sign/verify; variable-length signatures validated against the canonical [SIGNATURE_MIN_LENGTH, SIGNATURE_MAX_LENGTH] range.
  • MLDSA44: ML-DSA-44 sign/verify; fixed-length 2420-byte signatures.
  • PQSignature, PQSchemeRegistry, PqKeypair: shared PQ abstraction layer.
  • PQSchemeRegistry centralises key lengths, signature lengths, seed handling, address derivation, sign, verify, and block-size wire-size computation (computePQAuthSigWireSize).

Governance and activation

  • Adds ALLOW_FN_DSA_512 (proposal id 99) and ALLOW_ML_DSA_44 (proposal id 100).
  • Both proposals are fork-gated on VERSION_4_8_2.
  • Runtime checks reject PQ signatures and precompile calls when the scheme is not yet active.

Witness configuration

  • PQ witness keys are configured via localPqWitness.keys — a list of paths to JSON key files (relative paths resolve against the working directory); each file holds one keypair, keeping the long key material out of config.conf.
  • Each JSON key file names a scheme and may contain any combination of the following material fields — privateKey takes priority at load time; seed is retained as a backup field when both are present:
    • seed — FN_DSA_512: 96 hex chars / 48 B (accepted with a drift warning — Falcon keygen is FFT-based and not bit-stable across JVMs or CPU architectures); ML_DSA_44: 64 hex chars / 32 B (deterministic).
    • privateKey — FN_DSA_512 must also supply publicKey (BouncyCastle exposes no API to derive it from the private key); ML_DSA_44 supplies privateKey only — publicKey is derived automatically, but may optionally be included for verification (if present it must match the derived value, otherwise the node rejects the file).
    • address — informational field written by pq-key new, ignored at load time.
  • PqKeyFile (Jackson-bound) is the JSON shape.
  • PQ-only witnesses derive their witness address from the configured PQ public key when no explicit localPqWitness.accountAddress is set.
  • A node can host a mix of ECDSA and PQ SRs simultaneously.

Consensus and block production

  • Consensus miners carry optional PQ key material and sign blocks with the configured PQ scheme.
  • Block production fails fast if a PQ miner is configured for a scheme that is not yet active on-chain.
  • generateBlock pre-reserves the exact proto3 wire size of pq_auth_sig (via PQSchemeRegistry.computePQAuthSigWireSize) before the transaction packing loop, preventing PQ SR blocks from exceeding the receiver-side maxBlockSize check in BlockMsgHandler.

Transaction validation

  • TransactionCapsule validates mixed ECDSA + PQ signatures against the same account permission threshold.
  • PQ signer identity is derived from the in-band public key and matched against Permission.keys[].address.
  • Duplicate signers are rejected across ECDSA and PQ paths.
  • Shielded-transfer validation rejects transparent/PQ signatures on shielded-from transactions.

Block validation

  • Block signature validation supports both legacy ECDSA and PQ schemes.
  • The derived PQ address must match the on-chain witness permission address.

Bandwidth

  • PQ pq_auth_sig bytes are subtracted from net bandwidth as signature overhead (same treatment as ECDSA signature bytes).

Transaction and pending pool limits

  • Per-block PQ transaction cap: 1000 (PQ_TRANS_IN_BLOCK_COUNTS).
  • Configurable pending pool cap: node.pqTransInPendingMaxCounts (default 1000).

TVM precompiles

Five new precompiles covering single verify, batch verify, and mixed multi-sign for both PQ schemes:

AddressNameDescription
0x16VerifyFnDsa512Single Falcon-512 verify
0x17BatchValidateFnDsa512Batch Falcon-512 verify, bitmap result, MAX_SIZE=16
0x18VerifyMlDsa44Single ML-DSA-44 verify
0x19BatchValidateMlDsa44Batch ML-DSA-44 verify, bitmap result, MAX_SIZE=16
0x1aValidateMultiPQSigUnified ECDSA + PQ permission threshold verify, MAX_SIZE=5

Energy values are calibrated via JMH benchmarks (warmup 5×2s, measurement 10×3s, 2 forks, 20 samples) using ECRecover.execute() — precompile 0x01, 3000 energy, 816.9 µs — as the reference unit. All benchmarks call contract.execute(input) to cover the full precompile path (ABI decode, slot scan, address derivation, verify):

PrecompilePer-call operationMeasured (µs)Energy
0x16full path: validate + slot scan + verify()44.8170
0x17 per entryfull path: ABI + computeAddress() + verify()57.5220
0x18full path: validate + verify()114.2420
0x19 per entryfull path: ABI + computeAddress() + verify()126.6470
0x1a FN-DSA entrysame operation as 0x17 per entry—220
0x1a ML-DSA entrysame operation as 0x19 per entry—470

ECRecover baseline: 816.9 µs → 3000 energy (CV 1.9%); all PQ values rounded up to the nearest 10. 0x1a ValidateMultiPQSig.execute() requires chain DB and cannot be benchmarked in isolation — its per-entry crypto work is identical to 0x17/0x19 respectively, so those values are reused.

These values are proposed based on this benchmark run and subject to community review before mainnet activation.

Relay / fast-forward support

  • RelayService signs and verifies HelloMessage using either legacy signatures or PQAuthSig.
  • PQ hello-message verification checks scheme registration, activation, key/signature lengths, address binding, and cryptographic validity.

Toolkit (plugins)

  • New Toolkit.jar pq-key new command generates a post-quantum key JSON file.
  • Default scheme: FN_DSA_512; default output directory: Wallet/.
  • Generated file contains all material fields in one place:
    {
      "scheme": "FN_DSA_512",
      "seed": "<96 hex chars>",
      "privateKey": "<2560 hex chars>",
      "publicKey": "<1792 hex chars>",
      "address": "T..."
    }
    
    ML_DSA_44 files follow the same structure with scheme-appropriate lengths (seed 64 hex, privateKey 5120 hex, publicKey 2624 hex).
  • File is written with 0600 permissions (owner read/write only).
  • --json flag prints a machine-readable summary (address, scheme, file).

Example module (example:pqc-example)

  • New Gradle sub-module example:pqc-example (package org.tron.example.pqc).
  • PQWitnessNode: in-process PQ witness node with deterministic keypairs.
  • PQFullNode: fullnode that dials PQWitnessNode via P2P and validates PQ-signed blocks.
  • PQClient: broadcasts a single PQ-signed transfer transaction.
  • PQTxSender: continuous multi-scheme (FN-DSA-512, ML-DSA-44, ECDSA) transfer and TRC20 load generator.
  • PQWitnessNode.writeWitnessConfig emits the full key file format (seed, privateKey, publicKey, address), matching the pq-key new output.
  • Run via ./gradlew :example:pqc-example:run -PmainClass=org.tron.example.pqc.PQWitnessNode.

Compatibility

  • Pre-activation behavior is legacy-only; PQ fields are rejected when no scheme is active.
  • UNKNOWN_PQ_SCHEME is reserved and never treated as a valid signing scheme.
  • Existing ECDSA transaction and block signing paths remain fully supported.

Tests

  • Unit tests: Falcon-512 and ML-DSA-44 sign/verify, KAT regression, PQSchemeRegistry, PQSignature.
  • TVM tests: single, batch, and unified multi-sign PQ precompiles.
  • Transaction and block capsule tests for PQ authentication.
  • Witness config, proposal, relay, bandwidth, JSON, and account-permission tests.