A TypeScript library for encoding and decoding post‑quantum public keys into human‑friendly Bech32m addresses.
Sharing a post‑quantum public key needs:
- A hash of the public key for privacy and fixed length.
- A strong checksum to catch typos and errors.
- A readable, typo‑resistant encoding.
- A clear network flag (production vs development).
pq‑address
provides all four. It lets you generate and parse addresses for any public‑key type (ML‑DSA, SLH‑DSA, etc.), while guaranteeing future‑proof safety.
-
Bech32m (BIP‑350)
- Case insensitive
- Friendly character set
- Strong error detection
- Improved checksum over Bech32
- Efficient encoding & decoding
- Smaller QR codes due to use of alphanumeric mode
-
Disjoint byte ranges
- Version codes in
0x00–0x3F
(up to 64 versions). - PubKeyType codes in
0x40–0xFF
(up to 192 public key types). - Any byte‑swap or mis‑read triggers a clear “unknown code” error.
By carving out non-overlapping slots for versions (0x00–0x3F) and public key types (0x40–0xFF), parsing becomes trivial—and any stray or swapped byte instantly flags itself as an “unknown code,” preventing silent failures.
- Version codes in
-
HRP flag
-
"yp"
for production/mainnet,"rh"
for development/testnet.
-
-
Extendable
- Add new
PubKeyType
variants without breaking old addresses.
- Add new
Address example: yp1qpqg39uw700gcctpahe650p9zlzpnjt60cpz09m4kx7ncz8922635hs5cdx7q
-
HRP (
yp
/rh
)-
yp
= Mainnet -
rh
= Testnet
-
-
Separator
- Always the character
1
.
- Always the character
-
Data
- The payload bytes:
- Version
- PubKeyType
- Raw pubkey hash digest
- Converted into 5-bit words and then into Bech32 characters.
- The payload bytes:
-
Checksum
- 6 Bech32 characters (BIP-350)
- Catches typos and bit-errors.
PQ address length is 64 characters.
Note: A Bech32 string is at most 90 characters long [BIP-173]
The default hash function for @project-eleven/pq-address
is SHA-256.
256 bit hash functions are currently considered secure against Grover's attack.
Even if the preimage is recovered, it only reveals a PQ secure public key and thus Shor's is not applicable.
Add to your package.json
:
npm install @project-eleven/pq-address
Import @project-eleven/pq-address
import {
encodeAddress,
decodeAddress,
Network,
Version
PubKeyType,
} from '@project-eleven/pq-address';
Encoding
const params = {
network: Network.MAINNET,
version: Version.V1,
pubkeyType: PubKeyType.MlDsa44,
pubkeyBytes: <PUB_KEY_BYTES>,
};
const pq_addr = encodeAddress(params);
console.log(pq_addr);
Decoding
const decoded = decodeAddress(pq_addr);
console.log(pq_addr);
import {
Bech32EncodeFailure,
InvalidLengthError,
InvalidHashLengthError,
InvalidPubkeyLengthError,
UnknownHrpError,
UnknownPubKeyTypeError,
UnknownVersionError,
PayloadTooShortError,
Bech32DecodeFailure
} from '@project-eleven/pq-address';
// Example params...
const params = {
network: Network.MAINNET,
version: Version.V1,
pubkeyType: PubKeyType.MlDsa44,
pubkeyBytes: <PUB_KEY_BYTES>,
};
try {
// ENCODE
const addr = encodeAddress(params);
console.log('Address:', addr);
// DECODE
const decoded = decodeAddress(addr);
console.log('Decoded:', decoded);
console.log('Re-encoded string:', decoded.toString());
} catch (e: unknown) {
// Handle encode errors
if (e instanceof Bech32EncodeFailure) {
console.error('Bech32 encode failed:', e.original.message);
} else if (e instanceof InvalidLengthError) {
console.error(`Bech32m address exceeds safe length: got ${e.got}, max ${e.max}`);
} else if (e instanceof InvalidPubKeyLengthError) {
console.error(`Pubkey length mismatch: got ${e.got}, expected ${e.expected}`);
}
// Handle decode errors
else if (e instanceof Bech32DecodeFailure) {
console.error('Bad address format or checksum:', e.error.message);
} else if (e instanceof UnknownHrpError) {
console.error('Unknown network prefix (HRP):', e.hrp);
} else if (e instanceof PayloadTooShortError) {
console.error(`Payload too short: got ${e.got}, need ≥ ${e.need}`);
} else if (e instanceof UnknownVersionError) {
console.error('Unknown version byte:', e.code);
} else if (e instanceof UnknownPubKeyTypeError) {
console.error('Unknown pubkey type byte:', e.code);
} else if (e instanceof InvalidHashLengthError) {
console.error(`Hash length mismatch: got ${e.got}, expected ${e.expected}`);
} else {
// Fallback for any unexpected error
console.error('Unexpected error:', e);
}
}
See CONTRIBUTING.md for details on how to contribute to this project.
This project is licensed under the MIT License - see the LICENSE file for details.