ML-DSA-87 key derivation
Status: the canonical derivation is stable. The hierarchical derivation is a
draft: implementations may change before it is final, and keys derived with
it must be registered on chain like any other key. Source:
spec/hd-derivation-draft.md.
Canonical derivation (stable)
QoreChain wallets and the QoreChain SDK derive one ML-DSA-87 key per account from the BIP-39 mnemonic and the account address:
seed = SHAKE256( UTF-8( "qorechain:pqc:v1|" + bech32_address + "|" + mnemonic ), 32 bytes )
(pk, sk) = ML-DSA-87.KeyGen_internal(seed)
bech32_addressis theqor1form of the account (an0xaddress is converted to the same 20 bytes first).mnemonicis the phrase in canonical form: lowercase English words separated by single spaces, nothing before or after. Implementations must refuse any other spelling rather than normalize it silently, because a different string gives a different key.- The key is bound to the address: the same mnemonic gives a different key for each account.
A wallet that uses this derivation signs with the key already registered for accounts created by QoreChain wallets.
Hierarchical derivation (draft)
For wallets that need several post-quantum keys from one seed without tying them to an address. ML-DSA has no public child derivation, so every path segment is hardened.
The construction is SLIP-0010 with its own master key string:
I = HMAC-SHA512(key = "qorechain-mldsa87-seed", data = seed)
k = I[0..32], c = I[32..64]
for each index i (all >= 2^31):
I = HMAC-SHA512(key = c, data = 0x00 || k || ser32(i))
k = I[0..32], c = I[32..64]
(pk, sk) = ML-DSA-87.KeyGen_internal(k)
seedis the 64-byte BIP-39 seed (mnemonic plus optional passphrase), or any 16 to 64 byte seed.- The default path is
m/44'/118'/{account}'/0'/0'. - A path with any non-hardened segment is refused.
Open points before this becomes final: the coin type to use in the path,
interaction with key rotation on chain (MsgRotatePQCKey), and alignment with
other ecosystems that derive post-quantum keys from a seed.
Test vectors
vectors/qoresign-v1.json, fields canonical, hd and hdRejected. See
Test vectors.