diff --git a/lib/bcoin/ec.js b/lib/bcoin/ec.js index 5e7c4e92..00d7613f 100644 --- a/lib/bcoin/ec.js +++ b/lib/bcoin/ec.js @@ -134,6 +134,52 @@ ec.publicKeyConvert = function publicKeyConvert(key, compressed) { return new Buffer(point.encode('array', compressed)); }; +/** + * ((tweak + key) % n) + * @param {Buffer} privateKey + * @param {Buffer} tweak + * @returns {Buffer} privateKey + */ + +ec.privateKeyTweakAdd = function privateKeyTweakAdd(privateKey, tweak) { + if (secp256k1) + return secp256k1.privateKeyTweakAdd(privateKey, tweak); + + privateKey = new bn(tweak) + .add(new bn(privateKey)) + .mod(ec.curve.n) + .toArrayLike(Buffer, 'be', 32); + + // Only a 1 in 2^127 chance of happening. + if (!ec.privateKeyVerify(privateKey)) + throw new Error('Private key is invalid.'); + + return privateKey; +}; + +/** + * ((g * tweak) + key) + * @param {Buffer} publicKey + * @param {Buffer} tweak + * @returns {Buffer} publicKey + */ + +ec.publicKeyTweakAdd = function publicKeyTweakAdd(publicKey, tweak, compressed) { + var point; + + if (secp256k1) + return secp256k1.publicKeyTweakAdd(publicKey, tweak, compressed); + + point = ec.decodePoint(publicKey); + point = ec.curve.g.mul(new bn(tweak)).add(point); + publicKey = new Buffer(point.encode('array', compressed)); + + if (!ec.publicKeyVerify(publicKey)) + throw new Error('Public key is invalid.'); + + return publicKey; +}; + /** * Create an ecdh. * @param {Buffer} pub diff --git a/lib/bcoin/hd.js b/lib/bcoin/hd.js index ac039274..3cd0dd7f 100644 --- a/lib/bcoin/hd.js +++ b/lib/bcoin/hd.js @@ -945,7 +945,7 @@ HDPrivateKey.prototype.__defineGetter__('xpubkey', function() { */ HDPrivateKey.prototype.derive = function derive(index, hardened) { - var p, id, data, hash, left, chainCode, privateKey, child; + var p, id, data, hash, left, right, privateKey, child; if (typeof index === 'string') return this.derivePath(index); @@ -981,17 +981,14 @@ HDPrivateKey.prototype.derive = function derive(index, hardened) { data = p.render(); hash = utils.hmac('sha512', data, this.chainCode); - left = new bn(hash.slice(0, 32)); - chainCode = hash.slice(32, 64); + left = hash.slice(0, 32); + right = hash.slice(32, 64); - privateKey = left - .add(new bn(this.privateKey)) - .mod(ec.curve.n) - .toArrayLike(Buffer, 'be', 32); - - // Only a 1 in 2^127 chance of happening. - if (!ec.privateKeyVerify(privateKey)) - throw new Error('Private key is invalid.'); + try { + privateKey = ec.privateKeyTweakAdd(this.privateKey, left); + } catch (e) { + return this.derive(index + 1); + } if (!this.fingerPrint) this.fingerPrint = utils.hash160(this.publicKey).slice(0, 4); @@ -1001,7 +998,7 @@ HDPrivateKey.prototype.derive = function derive(index, hardened) { child.depth = this.depth + 1; child.parentFingerPrint = this.fingerPrint; child.childIndex = index; - child.chainCode = chainCode; + child.chainCode = right; child.privateKey = privateKey; child.publicKey = ec.publicKeyCreate(privateKey, true); @@ -1179,7 +1176,7 @@ HDPrivateKey.prototype.equal = function equal(obj) { */ HDPrivateKey.prototype.fromSeed = function fromSeed(seed, network) { - var hash, chainCode, privateKey; + var hash, left, right; assert(Buffer.isBuffer(seed)); @@ -1190,19 +1187,19 @@ HDPrivateKey.prototype.fromSeed = function fromSeed(seed, network) { hash = utils.hmac('sha512', seed, 'Bitcoin seed'); - privateKey = hash.slice(0, 32); - chainCode = hash.slice(32, 64); + left = hash.slice(0, 32); + right = hash.slice(32, 64); // Only a 1 in 2^127 chance of happening. - if (!ec.privateKeyVerify(privateKey)) + if (!ec.privateKeyVerify(left)) throw new Error('Master private key is invalid.'); this.network = bcoin.network.get(network); this.depth = 0; this.parentFingerPrint = new Buffer([0, 0, 0, 0]); this.childIndex = 0; - this.chainCode = chainCode; - this.privateKey = privateKey; + this.chainCode = right; + this.privateKey = left; this.publicKey = ec.publicKeyCreate(this.privateKey, true); return this; @@ -1597,7 +1594,7 @@ HDPublicKey.prototype.__defineGetter__('xpubkey', function() { */ HDPublicKey.prototype.derive = function derive(index, hardened) { - var p, id, data, hash, left, chainCode, point, publicKey, child; + var p, id, data, hash, left, right, publicKey, child; if (typeof index === 'string') return this.derivePath(index); @@ -1623,16 +1620,14 @@ HDPublicKey.prototype.derive = function derive(index, hardened) { data = p.render(); hash = utils.hmac('sha512', data, this.chainCode); - left = new bn(hash.slice(0, 32)); - chainCode = hash.slice(32, 64); + left = hash.slice(0, 32); + right = hash.slice(32, 64); - point = ec.decodePoint(this.publicKey); - point = ec.curve.g.mul(left).add(point); - publicKey = new Buffer(point.encode('array', true)); - assert(publicKey.length === 33); - - if (!ec.publicKeyVerify(publicKey)) - throw new Error('Public key is invalid.'); + try { + publicKey = ec.publicKeyTweakAdd(this.publicKey, left, true); + } catch (e) { + return this.derive(index + 1); + } if (!this.fingerPrint) this.fingerPrint = utils.hash160(this.publicKey).slice(0, 4); @@ -1642,7 +1637,7 @@ HDPublicKey.prototype.derive = function derive(index, hardened) { child.depth = this.depth + 1; child.parentFingerPrint = this.fingerPrint; child.childIndex = index; - child.chainCode = chainCode; + child.chainCode = right; child.publicKey = publicKey; HD.cache.set(id, child);