Skip to content

doc: Are XPubs Safe to Share? #28

@coolaj86

Description

@coolaj86

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 the Key Tweak
    (you also need to know the Index, but that's sequential, and probably less than 2000 - so easy to guess and check)
  • privateKeyTweakAdd() and publicKeyTweakAdd() are reversible
    (if you know the Key Tweak and the Private 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:

DashHD.js/dashhd.js

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

DashHD.js/dashhd.js

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    documentationImprovements or additions to documentation

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions