pqchain-crypto
v0.5.0
Published
Post-quantum signing SDK for PQChain: ML-DSA-87 (FIPS 204) key generation, BLAKE3 address derivation, ledger-v2 transaction signing with chain-id replay protection, and cell routing. Rust compiled to WebAssembly — runs in Node.js and browsers.
Maintainers
Readme
pqchain-crypto
Post-quantum signing SDK for PQChain — the same Rust code that runs inside the PQChain node, compiled to WebAssembly.
- ML-DSA-87 (FIPS 204, security level 5) key generation, signing, verification
- Address derivation —
blake3(public_key)[..20] - Transaction building & signing — protobuf wire format, with ledger-v2 chain-id replay protection (EIP-155-style)
- Cell routing —
cell_of(address, num_cells)for multi-cell deployments - Runs in Node.js ≥ 18 and in browsers (separate
./webbuild) - No native dependencies, no network access — pure WASM
Intended for exchanges, custodians, and wallet developers integrating PQChain. Full protocol details and acceptance criteria: see the exchange integration guide.
Install
npm install pqchain-cryptoQuickstart (Node.js)
const pq = require('pqchain-crypto');
// 1. Generate an ML-DSA-87 keypair and derive its address
const keypair = new pq.PQKeyPair('ml-dsa-87');
const publicKeyHex = keypair.get_public_key_hex(); // 2592 bytes (hex)
const privateKey = keypair.get_private_key_bytes(); // 4896 bytes — keep secret!
const address = pq.derive_address(publicKeyHex); // 40 hex chars
// 2. Build the unsigned transaction (amounts in Qubits: 1 PQC = 10^9)
const unsignedTx = pq.build_transaction_protobuf(
address, // from
'22'.repeat(20), // to
1000n, // value
0n, // nonce (strictly increasing per sender)
21000n, // gas_limit
1n, // max_fee
'' // data (hex, empty for transfers)
);
// 3. Sign. On ledger-v2 chains pass the chain id (BigInt) — it is bound into
// the signed message as replay protection. Omit it on legacy chains.
const rawTx = pq.build_signed_transaction_protobuf(privateKey, unsignedTx, 1n);
// 4. Route & submit: POST the raw bytes to the cell that owns the SENDER.
const cell = pq.cell_of(address, 8); // num_cells from chain config
// POST /api/v1/transactions/raw (Content-Type: application/octet-stream)Signature verification and detached signatures:
const sigHex = pq.sign_transaction_protobuf(privateKey, unsignedTx, 1n);
pq.verify_signature(publicKeyHex, signedMessageBytes, sigHex); // → booleanBrowser usage
The ./web build is an ES module that must be initialized with the WASM
binary before use (bundlers resolve the browser condition automatically):
import * as pq from 'pqchain-crypto/web';
await pq.default(); // or pq.default({ module_or_path: wasmBytesOrUrl })API surface (excerpt)
| Export | Purpose |
| --- | --- |
| PQKeyPair | ML-DSA-87 keypair: generate, restore, sign, verify |
| derive_address(pubKeyHex) | blake3(pubkey)[..20] as 40 hex chars |
| build_transaction_protobuf(...) | Unsigned wire::Tx protobuf bytes |
| sign_transaction_protobuf(sk, tx, chainId?) | Detached hex signature (chain-bound if chainId given) |
| build_signed_transaction_protobuf(sk, tx, chainId?) | Fully signed tx bytes for /api/v1/transactions/raw |
| cell_of(addressHex, numCells) | Owning cell of an account (multi-cell routing) |
| blake3_hex(bytes) | Canonical BLAKE3 hash (hex) |
| encrypt_private_key / decrypt_private_key | Argon2id + ChaCha20-Poly1305 key storage |
| verify_signature(pubKeyHex, msg, sigHex) | Standalone verification |
Complete TypeScript definitions ship with the package.
Test vectors
npm test (in the package source) reproduces the published ground-truth
vectors — unsigned encoding, chain-id-bound signing preimage, and cell_of
routing — against the built WASM. Reproduce the same vectors with your own
integration before submitting to a live network.
Security notes
- Private keys are 4896 bytes; treat
get_private_key_bytes()output as highly sensitive and zero it after use where your runtime allows. - Signing happens entirely inside the WASM module; nothing is transmitted.
- The PQChain protocol has not completed an external audit yet — see pqchain.io/security for current status.
License
Apache-2.0 OR MIT, at your option. Source: the
pq-wasm-crypto
crate in the PQChain repository.
