deriveKeyPair static method
Derives the ML-KEM-1024 keypair for account from a BIP-39 seed.
The keys are siblings of the secp256k1 key derived from the same mnemonic, never children of it — an adversary who recovers the Nostr private key cannot repeat the derivation to reach these.
seed must be the 64-byte BIP-39 seed, not a private key. Passing a
32-byte secp256k1 private key is rejected rather than silently producing
unrelated keys.
A 12-word mnemonic expands to a valid 64-byte seed carrying only 128 bits of entropy, which would make the seed — not the lattice — the weakest link. Callers advertising keys as seed-derived should require 24 words; this function cannot detect mnemonic length, because PBKDF2 stretches any mnemonic to 64 bytes.
Implementation
static PqKeyPair deriveKeyPair(Uint8List seed, {int account = 0}) {
if (seed.length != seedBytes) {
throw ArgumentError.value(
seed.length,
'seed',
'expected a $seedBytes-byte BIP-39 seed',
);
}
final seedPtr = calloc<Uint8>(seed.length);
final pk = calloc<rust_lib.QsBuffer>();
final sk = calloc<rust_lib.QsBuffer>();
try {
seedPtr.asTypedList(seed.length).setAll(0, seed);
final ok = rust_lib.pqDeriveKemKeypair(
seedPtr,
seed.length,
account,
pk,
sk,
);
if (ok != 1) throw StateError('Post-quantum key derivation failed');
return PqKeyPair(publicKey: _copyOut(pk), secretKey: _copyOut(sk));
} finally {
// Wipe our copy of the seed before releasing it.
seedPtr.asTypedList(seed.length).fillRange(0, seed.length, 0);
calloc.free(seedPtr);
calloc.free(pk);
calloc.free(sk);
}
}