diff --git a/src/hdnode.js b/src/hdnode.js index a3c9f4b..03e5bb1 100644 --- a/src/hdnode.js +++ b/src/hdnode.js @@ -37,6 +37,7 @@ function HDNode(K, chainCode, network) { this.chainCode = chainCode this.depth = 0 this.index = 0 + this.parentFingerprint = 0x00000000 this.network = network if (K instanceof BigInteger) { @@ -196,8 +197,7 @@ HDNode.prototype.toBuffer = function(isPrivate, __ignoreDeprecation) { buffer.writeUInt8(this.depth, 4) // 4 bytes: the fingerprint of the parent's key (0x00000000 if master key) - var fingerprint = (this.depth === 0) ? 0x00000000 : this.parentFingerprint - buffer.writeUInt32BE(fingerprint, 5) + buffer.writeUInt32BE(this.parentFingerprint, 5) // 4 bytes: child number. This is the number i in xi = xpar/i, with xi the key being serialized. // This is encoded in Big endian. (0x00000000 if master key) diff --git a/test/integration/crypto.js b/test/integration/crypto.js new file mode 100644 index 0000000..38389b7 --- /dev/null +++ b/test/integration/crypto.js @@ -0,0 +1,52 @@ +var assert = require('assert') +var bigi = require('bigi') +var bitcoin = require('../../') +var crypto = require('crypto') + +describe('bitcoinjs-lib (crypto)', function() { + it('can recover a parent private key from the parent\'s public key and a derived non-hardened child private key', function() { + function recoverParent(master, child) { + assert(!master.privKey, 'You already have the parent private key') + assert(child.privKey, 'Missing child private key') + + var curve = bitcoin.ECKey.curve + var QP = master.pubKey.toBuffer() + var QP64 = QP.toString('base64') + var d1 = child.privKey.d + var d2 + var indexBuffer = new Buffer(4) + + // search index space until we find it + for (var i = 0; i < bitcoin.HDNode.HIGHEST_BIT; ++i) { + indexBuffer.writeUInt32BE(i, 0) + + // calculate I + var data = Buffer.concat([QP, indexBuffer]) + var I = crypto.createHmac('sha512', master.chainCode).update(data).digest() + var IL = I.slice(0, 32) + var pIL = bigi.fromBuffer(IL) + + // See hdnode.js:273 to understand + d2 = d1.subtract(pIL).mod(curve.n) + + var Qp = new bitcoin.ECKey(d2, true).pub.toBuffer() + if (Qp.toString('base64') === QP64) break + } + + var node = new bitcoin.HDNode(d2, master.chainCode, master.network) + node.depth = master.depth + node.index = master.index + node.masterFingerprint = master.masterFingerprint + return node + } + + var seed = crypto.randomBytes(32) + var master = bitcoin.HDNode.fromSeedBuffer(seed) + var child = master.derive(6) // m/6 + + // now for the recovery + var neuteredMaster = master.neutered() + var recovered = recoverParent(neuteredMaster, child) + assert.equal(recovered.toBase58(), master.toBase58()) + }) +})