diff --git a/CHANGELOG.md b/CHANGELOG.md index 690ca72..7c5bc98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ 0.2.0 / 2014-06-25 ------------------ - upgraded `"ecurve": "^0.8.0"` to `"ecurve": "^1.0.0"` +- added functionality to dervice public to public child keys 0.1.0 / 2014-06-16 ------------------ diff --git a/lib/hdkey.js b/lib/hdkey.js index 90477d8..5bdecbc 100644 --- a/lib/hdkey.js +++ b/lib/hdkey.js @@ -2,7 +2,7 @@ var assert = require('assert') var crypto = require('crypto') var BigInteger = require('bigi') var ecurve = require('ecurve') -var ecparams = ecurve.getCurveByName('secp256k1') +var curve = ecurve.getCurveByName('secp256k1') var Point = ecurve.Point var sha512 = require('sha512') @@ -39,7 +39,8 @@ Object.defineProperty(HDKey.prototype, 'privateKey', { assert.equal(value.length, 32, 'Private key must be 32 bytes.') this._privateKey = value this._privateKeyInteger = BigInteger.fromBuffer(this._privateKey) - this._publicKey = ecparams.G.multiply(this._privateKeyInteger).getEncoded(true) //force compressed point + this._publicPoint = curve.G.multiply(this._privateKeyInteger) + this._publicKey = this._publicPoint.getEncoded(true) //force compressed point this._identifier = hash160(this.publicKey) this._fingerprint = this._identifier.slice(0, 4).readUInt32BE(0) } @@ -51,8 +52,8 @@ Object.defineProperty(HDKey.prototype, 'publicKey', { }, set: function(value) { assert(value.length === 33 || value.length === 65, 'Public key must be 33 or 65 bytes.') - var pt = Point.decodeFrom(ecparams, value) - this._publicKey = pt.getEncoded(true) //force compressed point + this._publicPoint = Point.decodeFrom(curve, value) + this._publicKey = this._publicPoint.getEncoded(true) //force compressed point this._identifier = hash160(this.publicKey) this._fingerprint = this._identifier.slice(0, 4).readUInt32BE(0) this._privateKey = null @@ -128,10 +129,10 @@ HDKey.prototype.deriveChild = function(index) { // Private parent key -> private child key if (this.privateKey) { // ki = parse256(IL) + kpar (mod n) - var ki = pIL.add(BigInteger.fromBuffer(this.privateKey)).mod(ecparams.n) + var ki = pIL.add(BigInteger.fromBuffer(this.privateKey)).mod(curve.n) // In case parse256(IL) >= n or ki == 0, one should proceed with the next value for i - if (pIL.compareTo(ecparams.n) >= 0 || ki.signum() === 0) { + if (pIL.compareTo(curve.n) >= 0 || ki.signum() === 0) { return this.derive(index + 1) } @@ -141,14 +142,16 @@ HDKey.prototype.deriveChild = function(index) { } else { // Ki = point(parse256(IL)) + Kpar // = G*IL + Kpar - //var Ki = ecparams.getG().multiply(pIL).add(this.pub.Q) + var Ki = curve.G.multiply(pIL).add(this._publicPoint) // In case parse256(IL) >= n or Ki is the point at infinity, one should proceed with the next value for i - //if (pIL.compareTo(ecparams.getN()) >= 0 || Ki.isInfinity()) { - // return this.derive(index + 1) - //} + if (curve.isInfinity(Ki)) { + return this.derive(index + 1) + } //hd.pub = new ECPubKey(Ki, true) + //this._publicPoint = Ki + hd.publicKey = Ki.getEncoded(true) } hd.chainCode = IR diff --git a/test/hdkey.test.js b/test/hdkey.test.js index 650b6ec..3817daf 100644 --- a/test/hdkey.test.js +++ b/test/hdkey.test.js @@ -4,7 +4,7 @@ var BigInteger = require('bigi') var cs = require('coinstring') var ecurve = require('ecurve') var secureRandom = require('secure-random') -var ecparams = ecurve.getCurveByName('secp256k1') +var curve = ecurve.getCurveByName('secp256k1') var HDKey = require('../') var fixtures = require('./fixtures/hdkey') @@ -23,7 +23,7 @@ describe('hdkey', function() { assert.equal(encode(childkey.privateExtendedKey), f.private) assert.equal(encode(childkey.publicExtendedKey), f.public) - }) + }) }) }) @@ -46,7 +46,7 @@ describe('hdkey', function() { it('should not throw if key is 33 bytes (compressed)', function() { var priv = secureRandom.randomBuffer(32) - var pub = ecparams.G.multiply(BigInteger.fromBuffer(priv)).getEncoded(true) + var pub = curve.G.multiply(BigInteger.fromBuffer(priv)).getEncoded(true) assert.equal(pub.length, 33) var hdkey = new HDKey() hdkey.publicKey = pub @@ -54,7 +54,7 @@ describe('hdkey', function() { it('should not throw if key is 65 bytes (not compressed)', function() { var priv = secureRandom.randomBuffer(32) - var pub = ecparams.G.multiply(BigInteger.fromBuffer(priv)).getEncoded(false) + var pub = curve.G.multiply(BigInteger.fromBuffer(priv)).getEncoded(false) assert.equal(pub.length, 65) var hdkey = new HDKey() hdkey.publicKey = pub @@ -66,7 +66,7 @@ describe('hdkey', function() { it('should parse it', function() { //m/0/2147483647'/1/2147483646'/2 var key = "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j" - var keyBuffer = cs.decode(key).slice(0, 78)//bs58.decode(key).slice(0, 78) + var keyBuffer = cs.decode(key) var hdkey = HDKey.fromExtendedKey(keyBuffer) assert.equal(hdkey.versions.private, 0x0488ade4) assert.equal(hdkey.versions.public, 0x0488b21e) @@ -84,7 +84,7 @@ describe('hdkey', function() { it('should parse it', function() { //m/0/2147483647'/1/2147483646'/2 var key = "xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt" - var keyBuffer = cs.decode(key).slice(0, 78)//bs58.decode(key).slice(0, 78) + var keyBuffer = cs.decode(key) var hdkey = HDKey.fromExtendedKey(keyBuffer) assert.equal(hdkey.versions.private, 0x0488ade4) assert.equal(hdkey.versions.public, 0x0488b21e) @@ -98,4 +98,19 @@ describe('hdkey', function() { }) }) }) + + describe('> when deriving public key', function() { + it('should work', function() { + var key = "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8" + var keyBuffer = cs.decode(key) + var hdkey = HDKey.fromExtendedKey(keyBuffer) + + var path = "m/3353535/2223/0/99424/4/33" + var derivedHDKey = hdkey.derive(path) + + var expected = "xpub6JdKdVJtdx6sC3nh87pDvnGhotXuU5Kz6Qy7Piy84vUAwWSYShsUGULE8u6gCivTHgz7cCKJHiXaaMeieB4YnoFVAsNgHHKXJ2mN6jCMbH1" + assert.equal(cs.encode(derivedHDKey.publicExtendedKey), expected) + + }) + }) })