-
Notifications
You must be signed in to change notification settings - Fork 1
Description
In Short
Never share a child private key along with a parent key of any sort - it directly reveals the XPrv (no brute force, just sequential index walking).
Due to the associative nature of ECDSA:
Any direct parent (XPub, XPrv, or Chain Code) can be used with any direct child private key to reveal the "Tweak" number (the amount added to the parent's private and public keys). This, with the Chain Code, directly reveals the parent's private key - regardless of being hardened or public derivation.
The purpose of public derivation is that it allows for any level of n-grand-child public keys to be generated using only public keys. However, knowing any child private key can be used to directly unlock all parent private keys up to the known XPub or hardened level (without the use of brute force - aside from a very a small amount of index-walking).
Hardened derivation requires the parent private key. A hardened XPub can only be used to public-derive children keys.
Vulnerabilities
- if you know the parent
Chain Code
, then you know theKey Tweak
(you also need to know theIndex
, but that's sequential, and probably less than 2000 - so easy to guess and check) privateKeyTweakAdd()
andpublicKeyTweakAdd()
are reversible
(if you know theKey Tweak
and thePrivate Key
, you know the parent private key)- Knowing an XPub at depth 3 means that you know all publicly-derived chain codes for all levels 3 and below
- Knowing any descendent private key of a publicly derived XPub means you know all private keys up to the level of the XPub
Fun facts
Key Tweak
is associative - addition to a private key results in the same public key as addition to the previous private key
References
Knowing multiple XPubs does NOT reveal a hardened parent.
Knowing any public-derived Private Key along with the XPub (the Chain Code) does reveal the direct parent private key (XPrv) and all sibling private keys (but not grand parents).
One weakness that may not be immediately obvious, is that
knowledge of a parent extended public key plus any non-hardened private key descending from it is equivalent to knowing the parent extended private key
(and thus every private and public key descending from it).This means that extended public keys must be treated more carefully than regular public keys.
It is also the reason for the existence of hardened keys, and why they are used for the account level in the tree.
This way, a leak of account-specific (or below) private key never risks compromising the master or other accounts.
https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#implications
https://github.com/bitcoinbook/bitcoinbook/blob/develop/ch05.asciidoc#hardened-child-key-derivation
Hardened vs Public
An HD Path component is either hardened or public:
Lines 373 to 385 in 55a3034
let seed = new Uint8Array(INDEXED_KEY_SIZE); | |
if (hardened) { | |
if (!hdkey.privateKey) { | |
throw new Error("Could not derive hardened child key"); | |
} | |
index += HARDENED_OFFSET; | |
seed.set([0], 0); | |
seed.set(hdkey.privateKey, 1); | |
} else { | |
seed.set(hdkey.publicKey, 0); | |
} | |
_indexDv.setUint32(0, index, BUFFER_BE); | |
seed.set(_indexBuffer, KEY_SIZE); |
- a hardened path can only be derived from a Private key
- a public path can be derived from a Private or Public key
- a hardened public key can derive a public XPub
- a hardened private key can derive a public XPrv or XPub
Lines 429 to 450 in 09e9cb3
let I = await Utils.sha512hmac(chainParts.chainCode, seed); | |
let IL = I.slice(0, 32); | |
let IR = I.slice(32); | |
let nextPrivKey; | |
let nextPubkey; | |
if (chainParts.privateKey) { | |
nextPrivKey = await Utils.privateKeyTweakAdd(chainParts.privateKey, IL); | |
nextPubkey = await Utils.toPublicKey(nextPrivKey); | |
} else if (chainParts.publicKey) { | |
nextPubkey = await Utils.publicKeyTweakAdd(chainParts.publicKey, IL); | |
} else { | |
// TODO | |
nextPrivKey = IL; | |
nextPubkey = await Utils.toPublicKey(IL); | |
} | |
return { | |
chainCode: IR, | |
privateKey: nextPrivKey, | |
publicKey: nextPubkey, | |
}; |
- key tweaks are associative (i.e.
+ n
to private key is also+ n
to public key)
Sharing XPubs
Each contact (customer) account should have its own XPub, which is generated from a hardened XPrv'.
- At the level of an XPrv' (or higher), knowing an XPrv' reveals all children, but nothing about siblings or parents
- At the level of an XPrv/XPub, knowing the XPub (Chain Code) and any child private key is equal to the XPrv
- At the level of an Key/Address, knowing a public key (or address) reveals nothing, and knowing a key (without the parent XPub / Chain Code) reveals nothing about siblings
XPrv'/XPub' XPrv/XPub Key/Address
(account) (usage) (index)
/0' ==> /0 ==> /0
BIP44 Dash / (safe) (unsafe) /1
(seed) (spec) (coin) / /2
/m ==> /44' ==> /5' /n'
\
\ /2
\ /1
\1' ==> /0 ==> /0