refactor keypair and hd.

This commit is contained in:
Christopher Jeffrey 2016-02-23 19:08:42 -08:00
parent 0f40d78f9e
commit 30db808930
7 changed files with 96 additions and 150 deletions

View File

@ -174,14 +174,6 @@ Address.prototype.getPrivateKey = function getPrivateKey(enc) {
return this.key.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() { Address.prototype.getScript = function getScript() {
var redeem; var redeem;
@ -565,6 +557,11 @@ Address.getType = function getType(addr) {
}; };
Address.prototype.toJSON = function toJSON(passphrase) { Address.prototype.toJSON = function toJSON(passphrase) {
var key = this.key;
if (!(key instanceof bcoin.keypair))
key = new bcoin.keypair({ privateKey: key.getPrivateKey() });
return { return {
v: 1, v: 1,
name: 'address', name: 'address',
@ -575,7 +572,7 @@ Address.prototype.toJSON = function toJSON(passphrase) {
index: this.index, index: this.index,
path: this.path, path: this.path,
address: this.getAddress(), address: this.getAddress(),
key: this.key.toJSON(passphrase), key: key.toJSON(passphrase),
type: this.type, type: this.type,
redeem: this.redeem ? utils.toHex(this.redeem) : null, redeem: this.redeem ? utils.toHex(this.redeem) : null,
keys: this.keys.map(utils.toBase58), keys: this.keys.map(utils.toBase58),

View File

@ -15,21 +15,29 @@ var ec = exports;
* EC * EC
*/ */
ec.generate = function generate(options) { ec.generatePrivateKey = function generatePrivateKey() {
var key, priv, pub; var key, priv;
if (bcoin.secp256k1 && bcoin.crypto) { if (bcoin.secp256k1 && bcoin.crypto) {
do { do {
priv = bcoin.crypto.randomBytes(32); priv = bcoin.crypto.randomBytes(32);
} while (!bcoin.secp256k1.privateKeyVerify(priv)); } while (!bcoin.secp256k1.privateKeyVerify(priv));
pub = bcoin.secp256k1.publicKeyCreate(priv, true);
} else { } else {
key = bcoin.ecdsa.genKeyPair(); key = bcoin.ecdsa.genKeyPair();
priv = new Buffer(key.getPrivate().toArray('be', 32)); 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) { ec.random = function random(size) {
@ -43,15 +51,15 @@ bn.prototype.toBuffer = function toBuffer(order, size) {
}; };
ec.verify = function verify(msg, sig, key, historical) { ec.verify = function verify(msg, sig, key, historical) {
if (key.getPublicKey)
key = key.getPublicKey();
if (!Buffer.isBuffer(sig)) if (!Buffer.isBuffer(sig))
return false; return false;
if (sig.length === 0) if (sig.length === 0)
return false; return false;
if (key.getPublicKey)
key = key.getPublicKey();
// Attempt to normalize the signature // Attempt to normalize the signature
// length before passing to elliptic. // length before passing to elliptic.
// Note: We only do this for historical data! // Note: We only do this for historical data!
@ -61,7 +69,7 @@ ec.verify = function verify(msg, sig, key, historical) {
try { try {
if (bcoin.secp256k1) { if (bcoin.secp256k1) {
// secp256k1 fails on low s values. This is // secp256k1 fails on high s values. This is
// bad for verifying historical data. // bad for verifying historical data.
if (historical) if (historical)
sig = ec.toLowS(sig); sig = ec.toLowS(sig);
@ -92,9 +100,10 @@ ec.verify = function verify(msg, sig, key, historical) {
ec.sign = function sign(msg, key) { ec.sign = function sign(msg, key) {
var sig; var sig;
if (bcoin.secp256k1) { if (key.getPrivateKey)
key = key.getPrivateKey(); key = key.getPrivateKey();
if (bcoin.secp256k1) {
// Sign message // Sign message
sig = bcoin.secp256k1.sign(msg, key); sig = bcoin.secp256k1.sign(msg, key);
@ -105,7 +114,7 @@ ec.sign = function sign(msg, key) {
sig = bcoin.secp256k1.signatureExport(sig); sig = bcoin.secp256k1.signatureExport(sig);
} else { } else {
// Sign message and ensure low S value // 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 // Convert to DER array
sig = new Buffer(sig.toDER()); sig = new Buffer(sig.toDER());

View File

@ -62,8 +62,6 @@ var EventEmitter = require('events').EventEmitter;
var english = require('../../etc/english.json'); var english = require('../../etc/english.json');
var ec = elliptic.curves.secp256k1;
/** /**
* HD Seeds * HD Seeds
*/ */
@ -109,6 +107,14 @@ function HD(options) {
return new HDPrivateKey(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 * HD Private Key
*/ */
@ -122,8 +128,7 @@ function HDPrivateKey(options) {
assert(!(options instanceof HDPrivateKey)); assert(!(options instanceof HDPrivateKey));
assert(!(options instanceof HDPublicKey)); assert(!(options instanceof HDPublicKey));
if (!options) assert(options);
options = { seed: bcoin.hd.seed() };
if (HDPrivateKey.isExtended(options)) if (HDPrivateKey.isExtended(options))
options = { xkey: options }; options = { xkey: options };
@ -137,23 +142,6 @@ function HDPrivateKey(options) {
if (HDPublicKey.isExtended(options.xkey)) if (HDPublicKey.isExtended(options.xkey))
return new HDPublicKey(options); 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; this.network = options.network || network.type;
if (options.seed) { if (options.seed) {
@ -161,8 +149,6 @@ function HDPrivateKey(options) {
data = this._seed(options.seed); data = this._seed(options.seed);
} else if (options.xkey) { } else if (options.xkey) {
data = this._unbuild(options.xkey); data = this._unbuild(options.xkey);
} else if (options.privateKey) {
data = this._generate(options.privateKey, options.chainCode);
} else { } else {
data = options.data; data = options.data;
} }
@ -612,20 +598,20 @@ HDPrivateKey.prototype._seed = function _seed(seed) {
}; };
}; };
HDPrivateKey.prototype._generate = function _generate(privateKey, entropy) { HDPrivateKey.fromSeed = function fromSeed(options) {
if (!privateKey) var seed = (options instanceof HDSeed) ? options : new HDSeed(options);
privateKey = bcoin.ec.generate().privateKey; return new HDPrivateKey({ seed: seed });
};
if (utils.isHex(privateKey)) HDPrivateKey._generate = function _generate(privateKey, entropy) {
privateKey = new Buffer(privateKey, 'hex'); if (!privateKey)
else if (utils.isBase58(privateKey)) privateKey = bcoin.ec.generatePrivateKey();
privateKey = bcoin.keypair._fromSecret(privateKey).privateKey;
if (!entropy) if (!entropy)
entropy = bcoin.ec.random(32); entropy = bcoin.ec.random(32);
return { return {
version: network[this.network].prefixes.xprivkey, version: null,
depth: 0, depth: 0,
parentFingerPrint: 0, parentFingerPrint: 0,
childIndex: 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) { HDPrivateKey.prototype._unbuild = function _unbuild(xkey) {
var raw = utils.fromBase58(xkey); var raw = utils.fromBase58(xkey);
var data = {}; var data = {};
@ -672,7 +668,7 @@ HDPrivateKey.prototype._unbuild = function _unbuild(xkey) {
HDPrivateKey.prototype._build = function _build(data) { HDPrivateKey.prototype._build = function _build(data) {
var sequence = new Buffer(82); var sequence = new Buffer(82);
var off = 0; 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.version, sequence, off);
off += utils.copy(data.depth, sequence, off); off += utils.copy(data.depth, sequence, off);
@ -688,8 +684,7 @@ HDPrivateKey.prototype._build = function _build(data) {
xprivkey = utils.toBase58(sequence); xprivkey = utils.toBase58(sequence);
key = bcoin.keypair({ privateKey: data.privateKey }); publicKey = bcoin.ec.publicKeyCreate(data.privateKey, true);
publicKey = key.getPublicKey();
size = constants.hd.parentFingerPrintSize; size = constants.hd.parentFingerPrintSize;
fingerPrint = utils.ripesha(publicKey).slice(0, size); fingerPrint = utils.ripesha(publicKey).slice(0, size);
@ -704,7 +699,6 @@ HDPrivateKey.prototype._build = function _build(data) {
this.fingerPrint = fingerPrint; this.fingerPrint = fingerPrint;
this.publicKey = publicKey; this.publicKey = publicKey;
this.key = key;
this.hdPrivateKey = this; this.hdPrivateKey = this;
this.xprivkey = xprivkey; this.xprivkey = xprivkey;
@ -758,7 +752,7 @@ HDPrivateKey.prototype.derive = function derive(index, hardened) {
privateKey = new Buffer(leftPart privateKey = new Buffer(leftPart
.add(new bn(this.privateKey)) .add(new bn(this.privateKey))
.mod(ec.curve.n) .mod(bcoin.ecdsa.curve.n)
.toArray('be', 32)); .toArray('be', 32));
child = new HDPrivateKey({ child = new HDPrivateKey({
@ -911,11 +905,8 @@ HDPrivateKey._fromJSON = function _fromJSON(json, passphrase) {
HDPrivateKey.fromJSON = function fromJSON(json, passphrase) { HDPrivateKey.fromJSON = function fromJSON(json, passphrase) {
json = HDPrivateKey._fromJSON(json, passphrase); json = HDPrivateKey._fromJSON(json, passphrase);
if (json.seed) { if (json.seed)
return new HDPrivateKey({ return HDPrivateKey.fromSeed(json.seed);
seed: new HDSeed(json.seed)
});
}
if (json.xprivkey) { if (json.xprivkey) {
return new HDPrivateKey({ return new HDPrivateKey({
@ -1078,7 +1069,6 @@ HDPublicKey.prototype._build = function _build(data) {
this.fingerPrint = fingerPrint; this.fingerPrint = fingerPrint;
this.privateKey = null; this.privateKey = null;
this.key = data.key || bcoin.keypair({ publicKey: this.publicKey });
this.hdPublicKey = this; this.hdPublicKey = this;
this.xpubkey = xpubkey; this.xpubkey = xpubkey;
@ -1088,8 +1078,9 @@ HDPublicKey.prototype._build = function _build(data) {
}; };
HDPublicKey.prototype.derive = function derive(index, hardened) { HDPublicKey.prototype.derive = function derive(index, hardened) {
var cached, data, hash, leftPart, chainCode, key, point, publicKey, child;
var off = 0; var off = 0;
var cached, data, hash, leftPart, chainCode;
var publicPoint, point, publicKey, child;
if (typeof index === 'string') if (typeof index === 'string')
return this.deriveString(index); return this.deriveString(index);
@ -1113,9 +1104,9 @@ HDPublicKey.prototype.derive = function derive(index, hardened) {
leftPart = new bn(hash.slice(0, 32)); leftPart = new bn(hash.slice(0, 32));
chainCode = hash.slice(32, 64); chainCode = hash.slice(32, 64);
key = bcoin.keypair({ publicKey: this.publicKey }); publicPoint = bcoin.ecdsa.curve.decodePoint(this.publicKey);
point = ec.curve.g.mul(leftPart).add(key.publicPoint); point = bcoin.ecdsa.curve.g.mul(leftPart).add(publicPoint);
publicKey = bcoin.keypair({ publicKey: point }).getPublicKey(); publicKey = new Buffer(point.encode('array', true));
child = new HDPublicKey({ child = new HDPublicKey({
network: this.network, network: this.network,
@ -1165,14 +1156,14 @@ HDPublicKey.prototype.deriveString = function deriveString(path) {
*/ */
[HDPrivateKey, HDPublicKey].forEach(function(HD) { [HDPrivateKey, HDPublicKey].forEach(function(HD) {
HD.prototype.getPublicKey = function getPublicKey() {
return bcoin.keypair.prototype.getPublicKey.apply(this, arguments);
};
HD.prototype.getPrivateKey = function getPrivateKey() { HD.prototype.getPrivateKey = function getPrivateKey() {
return bcoin.keypair.prototype.getPrivateKey.apply(this, arguments); 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() { HD.prototype.sign = function sign() {
return this.key.sign.apply(this.key, arguments); 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); 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; HD.prototype.compressed = true;
}); });
HDPrivateKey.prototype.toSecret = function toSecret() { HDPrivateKey.prototype.toSecret = function toSecret() {
return bcoin.keypair.toSecret(this.privateKey, this.compressed); return bcoin.keypair.toSecret.call(this);
}; };
HDPrivateKey.fromSecret = function fromSecret(privateKey) { HDPrivateKey.fromSecret = function fromSecret(privateKey) {

View File

@ -28,90 +28,56 @@ function KeyPair(options) {
if (!options.privateKey && !options.publicKey) if (!options.privateKey && !options.publicKey)
throw new Error('No options for keypair'); 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() { KeyPair.generate = function() {
return new KeyPair(bcoin.ec.generate()); return new KeyPair({ privateKey: bcoin.ec.generatePrivateKey() });
}; };
KeyPair.prototype.sign = function sign(msg) { 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) { 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) { KeyPair.prototype.getPrivateKey = function getPrivateKey(enc) {
var privateKey; if (!this.privateKey)
return;
if (!this._privateKey) {
privateKey = this.privatePoint;
if (!privateKey)
return;
privateKey = new Buffer(privateKey.toArray('be', 32));
this._privateKey = privateKey;
}
privateKey = this._privateKey;
if (enc === 'base58') if (enc === 'base58')
return KeyPair.toSecret(privateKey, this.compressed); return this.toSecret();
if (enc === 'hex') if (enc === 'hex')
return utils.toHex(privateKey); return utils.toHex(this.privateKey);
return privateKey; return this.privateKey;
}; };
KeyPair.prototype.getPublicKey = function getPublicKey(enc) { KeyPair.prototype.getPublicKey = function getPublicKey(enc) {
var publicKey; if (!this.publicKey) {
if (!this.privateKey)
return;
if (!this._publicKey) this.publicKey = bcoin.ec.publicKeyCreate(
this._publicKey = new Buffer(this.key.getPublic(this.compressed, 'array')); this.privateKey, this.compressed
);
publicKey = this._publicKey; }
if (enc === 'base58') if (enc === 'base58')
return utils.toBase58(publicKey); return utils.toBase58(this.publicKey);
if (enc === 'hex') if (enc === 'hex')
return utils.toHex(publicKey); return utils.toHex(this.publicKey);
return publicKey; return this.publicKey;
}; };
KeyPair.prototype.toSecret = function toSecret() { KeyPair.prototype.toSecret = function toSecret() {
@ -161,14 +127,6 @@ KeyPair.fromSecret = function fromSecret(privateKey) {
return new KeyPair(KeyPair._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) { KeyPair.prototype.toJSON = function toJSON(passphrase) {
var json = { var json = {
v: 1, v: 1,

View File

@ -41,7 +41,7 @@ function Wallet(options) {
} }
if (!options.master) if (!options.master)
options.master = bcoin.hd.privateKey(); options.master = bcoin.hd.fromSeed();
this.options = options; this.options = options;
this.db = options.db || new bcoin.walletdb({ type: 'file' }); 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); key = this.accountKey.derive(data.path);
options = { options = {
key: key.key, key: key,
compressed: key.compressed,
change: data.change, change: data.change,
index: data.index, index: data.index,
path: data.path, path: data.path,

View File

@ -86,7 +86,7 @@ describe('HD', function() {
}); });
it('should deserialize and reserialize', 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); assert.equal(bcoin.hd.fromJSON(key.toJSON()).xprivkey, key.xprivkey);
}); });

View File

@ -52,7 +52,7 @@ describe('Wallet', function() {
m: 1, m: 1,
n: 2 n: 2
}); });
var k2 = bcoin.hd.priv().deriveAccount44(0).hdPublicKey; var k2 = bcoin.hd.fromSeed().deriveAccount44(0).hdPublicKey;
w.addKey(k2); w.addKey(k2);
// Input transcation // Input transcation