diff --git a/lib/bcoin/address.js b/lib/bcoin/address.js index 721c52de..a80932e0 100644 --- a/lib/bcoin/address.js +++ b/lib/bcoin/address.js @@ -174,14 +174,6 @@ Address.prototype.getPrivateKey = function getPrivateKey(enc) { return this.key.getPrivateKey(enc); }; -Address.toSecret = function toSecret(privateKey, compressed) { - return bcoin.keypair.toSecret(privateKey, compressed); -}; - -Address.fromSecret = function fromSecret(privateKey) { - return bcoin.keypair.fromSecret(privateKey); -}; - Address.prototype.getScript = function getScript() { var redeem; @@ -565,6 +557,11 @@ Address.getType = function getType(addr) { }; Address.prototype.toJSON = function toJSON(passphrase) { + var key = this.key; + + if (!(key instanceof bcoin.keypair)) + key = new bcoin.keypair({ privateKey: key.getPrivateKey() }); + return { v: 1, name: 'address', @@ -575,7 +572,7 @@ Address.prototype.toJSON = function toJSON(passphrase) { index: this.index, path: this.path, address: this.getAddress(), - key: this.key.toJSON(passphrase), + key: key.toJSON(passphrase), type: this.type, redeem: this.redeem ? utils.toHex(this.redeem) : null, keys: this.keys.map(utils.toBase58), diff --git a/lib/bcoin/ec.js b/lib/bcoin/ec.js index e218f5b3..594d42fb 100644 --- a/lib/bcoin/ec.js +++ b/lib/bcoin/ec.js @@ -15,21 +15,29 @@ var ec = exports; * EC */ -ec.generate = function generate(options) { - var key, priv, pub; +ec.generatePrivateKey = function generatePrivateKey() { + var key, priv; if (bcoin.secp256k1 && bcoin.crypto) { do { priv = bcoin.crypto.randomBytes(32); } while (!bcoin.secp256k1.privateKeyVerify(priv)); - pub = bcoin.secp256k1.publicKeyCreate(priv, true); } else { key = bcoin.ecdsa.genKeyPair(); priv = new Buffer(key.getPrivate().toArray('be', 32)); - pub = new Buffer(key.getPublic(true, 'array')); } - return { privateKey: priv, publicKey: pub }; + return priv; +}; + +ec.publicKeyCreate = function publicKeyCreate(priv, compressed) { + assert(Buffer.isBuffer(priv)); + + if (bcoin.ecdsa.secp256k1) + return bcoin.secp256k1.publicKeyCreate(priv, compressed); + + priv = bcoin.ecdsa.keyPair({ priv: priv }).getPublic(compressed, 'array'); + return new Buffer(priv); }; ec.random = function random(size) { @@ -43,15 +51,15 @@ bn.prototype.toBuffer = function toBuffer(order, size) { }; ec.verify = function verify(msg, sig, key, historical) { - if (key.getPublicKey) - key = key.getPublicKey(); - if (!Buffer.isBuffer(sig)) return false; if (sig.length === 0) return false; + if (key.getPublicKey) + key = key.getPublicKey(); + // Attempt to normalize the signature // length before passing to elliptic. // Note: We only do this for historical data! @@ -61,7 +69,7 @@ ec.verify = function verify(msg, sig, key, historical) { try { if (bcoin.secp256k1) { - // secp256k1 fails on low s values. This is + // secp256k1 fails on high s values. This is // bad for verifying historical data. if (historical) sig = ec.toLowS(sig); @@ -92,9 +100,10 @@ ec.verify = function verify(msg, sig, key, historical) { ec.sign = function sign(msg, key) { var sig; - if (bcoin.secp256k1) { + if (key.getPrivateKey) key = key.getPrivateKey(); + if (bcoin.secp256k1) { // Sign message sig = bcoin.secp256k1.sign(msg, key); @@ -105,7 +114,7 @@ ec.sign = function sign(msg, key) { sig = bcoin.secp256k1.signatureExport(sig); } else { // Sign message and ensure low S value - sig = bcoin.ecdsa.sign(msg, key.privatePoint, { canonical: true }); + sig = bcoin.ecdsa.sign(msg, key, { canonical: true }); // Convert to DER array sig = new Buffer(sig.toDER()); diff --git a/lib/bcoin/hd.js b/lib/bcoin/hd.js index 692b45db..24e5841b 100644 --- a/lib/bcoin/hd.js +++ b/lib/bcoin/hd.js @@ -62,8 +62,6 @@ var EventEmitter = require('events').EventEmitter; var english = require('../../etc/english.json'); -var ec = elliptic.curves.secp256k1; - /** * HD Seeds */ @@ -109,6 +107,14 @@ function HD(options) { return new HDPrivateKey(options); } +HD.generate = function generate(privateKey, entropy) { + return HDPrivateKey.generate(privateKey, entropy); +}; + +HD.fromSeed = function fromSeed(options) { + return HDPrivateKey.fromSeed(options); +}; + /** * HD Private Key */ @@ -122,8 +128,7 @@ function HDPrivateKey(options) { assert(!(options instanceof HDPrivateKey)); assert(!(options instanceof HDPublicKey)); - if (!options) - options = { seed: bcoin.hd.seed() }; + assert(options); if (HDPrivateKey.isExtended(options)) options = { xkey: options }; @@ -137,23 +142,6 @@ function HDPrivateKey(options) { if (HDPublicKey.isExtended(options.xkey)) return new HDPublicKey(options); - if (options instanceof bcoin.hd.seed) - options = { seed: options }; - - if (options.passphrase !== undefined - || options.bits - || options.entropy - || options.mnemonic) { - options.seed = bcoin.hd.seed(options); - } - - if (options.seed - && typeof options.seed === 'object' - && !Buffer.isBuffer(options.seed) - && !(options.seed instanceof bcoin.hd.seed)) { - options.seed = bcoin.hd.seed(options.seed); - } - this.network = options.network || network.type; if (options.seed) { @@ -161,8 +149,6 @@ function HDPrivateKey(options) { data = this._seed(options.seed); } else if (options.xkey) { data = this._unbuild(options.xkey); - } else if (options.privateKey) { - data = this._generate(options.privateKey, options.chainCode); } else { data = options.data; } @@ -612,20 +598,20 @@ HDPrivateKey.prototype._seed = function _seed(seed) { }; }; -HDPrivateKey.prototype._generate = function _generate(privateKey, entropy) { - if (!privateKey) - privateKey = bcoin.ec.generate().privateKey; +HDPrivateKey.fromSeed = function fromSeed(options) { + var seed = (options instanceof HDSeed) ? options : new HDSeed(options); + return new HDPrivateKey({ seed: seed }); +}; - if (utils.isHex(privateKey)) - privateKey = new Buffer(privateKey, 'hex'); - else if (utils.isBase58(privateKey)) - privateKey = bcoin.keypair._fromSecret(privateKey).privateKey; +HDPrivateKey._generate = function _generate(privateKey, entropy) { + if (!privateKey) + privateKey = bcoin.ec.generatePrivateKey(); if (!entropy) entropy = bcoin.ec.random(32); return { - version: network[this.network].prefixes.xprivkey, + version: null, depth: 0, parentFingerPrint: 0, childIndex: 0, @@ -635,6 +621,16 @@ HDPrivateKey.prototype._generate = function _generate(privateKey, entropy) { }; }; +HDPrivateKey.generate = function generate(privateKey, entropy) { + return new HDPrivateKey(HDPrivateKey._generate(privateKey, entropy)); +}; + +HDPrivateKey.prototype._generate = function _generate(privateKey, entropy) { + var data = HDPrivateKey._generate(privateKey, entropy); + data.version = network[this.network].prefixes.xprivkey; + return data; +}; + HDPrivateKey.prototype._unbuild = function _unbuild(xkey) { var raw = utils.fromBase58(xkey); var data = {}; @@ -672,7 +668,7 @@ HDPrivateKey.prototype._unbuild = function _unbuild(xkey) { HDPrivateKey.prototype._build = function _build(data) { var sequence = new Buffer(82); var off = 0; - var checksum, xprivkey, key, privateKey, publicKey, size, fingerPrint; + var checksum, xprivkey, privateKey, publicKey, size, fingerPrint; off += utils.copy(data.version, sequence, off); off += utils.copy(data.depth, sequence, off); @@ -688,8 +684,7 @@ HDPrivateKey.prototype._build = function _build(data) { xprivkey = utils.toBase58(sequence); - key = bcoin.keypair({ privateKey: data.privateKey }); - publicKey = key.getPublicKey(); + publicKey = bcoin.ec.publicKeyCreate(data.privateKey, true); size = constants.hd.parentFingerPrintSize; fingerPrint = utils.ripesha(publicKey).slice(0, size); @@ -704,7 +699,6 @@ HDPrivateKey.prototype._build = function _build(data) { this.fingerPrint = fingerPrint; this.publicKey = publicKey; - this.key = key; this.hdPrivateKey = this; this.xprivkey = xprivkey; @@ -758,7 +752,7 @@ HDPrivateKey.prototype.derive = function derive(index, hardened) { privateKey = new Buffer(leftPart .add(new bn(this.privateKey)) - .mod(ec.curve.n) + .mod(bcoin.ecdsa.curve.n) .toArray('be', 32)); child = new HDPrivateKey({ @@ -911,11 +905,8 @@ HDPrivateKey._fromJSON = function _fromJSON(json, passphrase) { HDPrivateKey.fromJSON = function fromJSON(json, passphrase) { json = HDPrivateKey._fromJSON(json, passphrase); - if (json.seed) { - return new HDPrivateKey({ - seed: new HDSeed(json.seed) - }); - } + if (json.seed) + return HDPrivateKey.fromSeed(json.seed); if (json.xprivkey) { return new HDPrivateKey({ @@ -1078,7 +1069,6 @@ HDPublicKey.prototype._build = function _build(data) { this.fingerPrint = fingerPrint; this.privateKey = null; - this.key = data.key || bcoin.keypair({ publicKey: this.publicKey }); this.hdPublicKey = this; this.xpubkey = xpubkey; @@ -1088,8 +1078,9 @@ HDPublicKey.prototype._build = function _build(data) { }; HDPublicKey.prototype.derive = function derive(index, hardened) { - var cached, data, hash, leftPart, chainCode, key, point, publicKey, child; var off = 0; + var cached, data, hash, leftPart, chainCode; + var publicPoint, point, publicKey, child; if (typeof index === 'string') return this.deriveString(index); @@ -1113,9 +1104,9 @@ HDPublicKey.prototype.derive = function derive(index, hardened) { leftPart = new bn(hash.slice(0, 32)); chainCode = hash.slice(32, 64); - key = bcoin.keypair({ publicKey: this.publicKey }); - point = ec.curve.g.mul(leftPart).add(key.publicPoint); - publicKey = bcoin.keypair({ publicKey: point }).getPublicKey(); + publicPoint = bcoin.ecdsa.curve.decodePoint(this.publicKey); + point = bcoin.ecdsa.curve.g.mul(leftPart).add(publicPoint); + publicKey = new Buffer(point.encode('array', true)); child = new HDPublicKey({ network: this.network, @@ -1165,14 +1156,14 @@ HDPublicKey.prototype.deriveString = function deriveString(path) { */ [HDPrivateKey, HDPublicKey].forEach(function(HD) { - HD.prototype.getPublicKey = function getPublicKey() { - return bcoin.keypair.prototype.getPublicKey.apply(this, arguments); - }; - HD.prototype.getPrivateKey = function getPrivateKey() { return bcoin.keypair.prototype.getPrivateKey.apply(this, arguments); }; + HD.prototype.getPublicKey = function getPublicKey() { + return bcoin.keypair.prototype.getPublicKey.apply(this, arguments); + }; + HD.prototype.sign = function sign() { return this.key.sign.apply(this.key, arguments); }; @@ -1181,19 +1172,11 @@ HDPublicKey.prototype.deriveString = function deriveString(path) { return this.key.verify.apply(this.key, arguments); }; - HD.prototype.__defineGetter__('publicPoint', function() { - return this.key.publicPoint; - }); - - HD.prototype.__defineGetter__('privatePoint', function() { - return this.key.privatePoint; - }); - HD.prototype.compressed = true; }); HDPrivateKey.prototype.toSecret = function toSecret() { - return bcoin.keypair.toSecret(this.privateKey, this.compressed); + return bcoin.keypair.toSecret.call(this); }; HDPrivateKey.fromSecret = function fromSecret(privateKey) { diff --git a/lib/bcoin/keypair.js b/lib/bcoin/keypair.js index 6267ca3b..2456caf8 100644 --- a/lib/bcoin/keypair.js +++ b/lib/bcoin/keypair.js @@ -28,90 +28,56 @@ function KeyPair(options) { if (!options.privateKey && !options.publicKey) throw new Error('No options for keypair'); + + assert(!options.privateKey || Buffer.isBuffer(options.privateKey)); + assert(!options.publicKey || Buffer.isBuffer(options.publicKey)); + + this.privateKey = options.privateKey; + this.publicKey = options.publicKey; } -KeyPair.prototype.__defineGetter__('key', function() { - if (!this._key) { - this._key = bcoin.ecdsa.keyPair({ - priv: this.options.privateKey, - pub: this.options.publicKey - }); - } - return this._key; -}); - -KeyPair.prototype.__defineGetter__('privatePoint', function() { - if (!this._privatePoint) - this._privatePoint = this.key.getPrivate(); - return this._privatePoint; -}); - -KeyPair.prototype.__defineGetter__('publicPoint', function() { - if (!this._publicPoint) - this._publicPoint = this.key.getPublic(); - return this._publicPoint; -}); - -KeyPair.prototype.__defineGetter__('privateKey', function() { - return this.getPrivateKey(); -}); - -KeyPair.prototype.__defineGetter__('publicKey', function() { - return this.getPublicKey(); -}); - KeyPair.generate = function() { - return new KeyPair(bcoin.ec.generate()); + return new KeyPair({ privateKey: bcoin.ec.generatePrivateKey() }); }; KeyPair.prototype.sign = function sign(msg) { - return bcoin.ec.sign(msg, this); + return bcoin.ec.sign(msg, this.getPrivateKey()); }; KeyPair.prototype.verify = function verify(msg, sig) { - return bcoin.ec.verify(msg, sig, this); + return bcoin.ec.verify(msg, sig, this.getPublicKey()); }; KeyPair.prototype.getPrivateKey = function getPrivateKey(enc) { - var privateKey; - - if (!this._privateKey) { - privateKey = this.privatePoint; - - if (!privateKey) - return; - - privateKey = new Buffer(privateKey.toArray('be', 32)); - - this._privateKey = privateKey; - } - - privateKey = this._privateKey; + if (!this.privateKey) + return; if (enc === 'base58') - return KeyPair.toSecret(privateKey, this.compressed); + return this.toSecret(); if (enc === 'hex') - return utils.toHex(privateKey); + return utils.toHex(this.privateKey); - return privateKey; + return this.privateKey; }; KeyPair.prototype.getPublicKey = function getPublicKey(enc) { - var publicKey; + if (!this.publicKey) { + if (!this.privateKey) + return; - if (!this._publicKey) - this._publicKey = new Buffer(this.key.getPublic(this.compressed, 'array')); - - publicKey = this._publicKey; + this.publicKey = bcoin.ec.publicKeyCreate( + this.privateKey, this.compressed + ); + } if (enc === 'base58') - return utils.toBase58(publicKey); + return utils.toBase58(this.publicKey); if (enc === 'hex') - return utils.toHex(publicKey); + return utils.toHex(this.publicKey); - return publicKey; + return this.publicKey; }; KeyPair.prototype.toSecret = function toSecret() { @@ -161,14 +127,6 @@ KeyPair.fromSecret = function fromSecret(privateKey) { return new KeyPair(KeyPair._fromSecret(privateKey)); }; -KeyPair.verify = function verify(msg, sig, key) { - return bcoin.ec.verify(msg, sig, key); -}; - -KeyPair.sign = function sign(msg, key) { - return bcoin.ec.sign(msg, key); -}; - KeyPair.prototype.toJSON = function toJSON(passphrase) { var json = { v: 1, diff --git a/lib/bcoin/wallet.js b/lib/bcoin/wallet.js index a454831c..7cf6a4f2 100644 --- a/lib/bcoin/wallet.js +++ b/lib/bcoin/wallet.js @@ -41,7 +41,7 @@ function Wallet(options) { } if (!options.master) - options.master = bcoin.hd.privateKey(); + options.master = bcoin.hd.fromSeed(); this.options = options; this.db = options.db || new bcoin.walletdb({ type: 'file' }); @@ -355,8 +355,7 @@ Wallet.prototype.deriveAddress = function deriveAddress(change, index) { key = this.accountKey.derive(data.path); options = { - key: key.key, - compressed: key.compressed, + key: key, change: data.change, index: data.index, path: data.path, diff --git a/test/hd-test.js b/test/hd-test.js index 76a41bb4..87343435 100644 --- a/test/hd-test.js +++ b/test/hd-test.js @@ -86,7 +86,7 @@ describe('HD', function() { }); it('should deserialize and reserialize', function() { - var key = bcoin.hd.priv(); + var key = bcoin.hd.fromSeed(); assert.equal(bcoin.hd.fromJSON(key.toJSON()).xprivkey, key.xprivkey); }); diff --git a/test/wallet-test.js b/test/wallet-test.js index c5a1b4e6..311cdeba 100644 --- a/test/wallet-test.js +++ b/test/wallet-test.js @@ -52,7 +52,7 @@ describe('Wallet', function() { m: 1, n: 2 }); - var k2 = bcoin.hd.priv().deriveAccount44(0).hdPublicKey; + var k2 = bcoin.hd.fromSeed().deriveAccount44(0).hdPublicKey; w.addKey(k2); // Input transcation