268 lines
5.6 KiB
JavaScript
268 lines
5.6 KiB
JavaScript
/**
|
|
* keypair.js - keypair object for bcoin
|
|
* Copyright (c) 2014-2015, Fedor Indutny (MIT License)
|
|
* https://github.com/indutny/bcoin
|
|
*/
|
|
|
|
var bcoin = require('../bcoin');
|
|
var bn = require('bn.js');
|
|
var utils = bcoin.utils;
|
|
var assert = utils.assert;
|
|
var constants = bcoin.protocol.constants;
|
|
var network = bcoin.protocol.network;
|
|
|
|
/**
|
|
* KeyPair
|
|
*/
|
|
|
|
function KeyPair(options) {
|
|
if (!(this instanceof KeyPair))
|
|
return new KeyPair(options);
|
|
|
|
if (!options)
|
|
options = {};
|
|
|
|
if (options instanceof KeyPair)
|
|
return options;
|
|
|
|
if (options.key instanceof KeyPair)
|
|
return options.key;
|
|
|
|
this.options = options;
|
|
this._key = options.key || null;
|
|
this.hd = options.hd || null;
|
|
this.compressed = options.compressed !== false;
|
|
|
|
if (options.priv instanceof bcoin.hd.priv) {
|
|
this.hd = options.priv;
|
|
this._key = options.priv.pair;
|
|
} else if (options.pub instanceof bcoin.hd.pub) {
|
|
this.hd = options.pub;
|
|
this._key = options.pub.pair;
|
|
} else if (options.hd) {
|
|
this.hd = typeof options.hd === 'object'
|
|
? bcoin.hd.priv(options.hd)
|
|
: bcoin.hd.priv();
|
|
this._key = this.hd.pair;
|
|
} else if (options.key) {
|
|
if ((options.key instanceof bcoin.hd.priv)
|
|
|| (options.key instanceof bcoin.hd.pub)) {
|
|
this.hd = options.key;
|
|
this._key = options.key.pair;
|
|
} else {
|
|
this._key = options.key;
|
|
}
|
|
} else if (options.priv || options.pub) {
|
|
this._key = bcoin.ecdsa.keyPair({
|
|
priv: options.priv,
|
|
pub: options.pub
|
|
});
|
|
} else {
|
|
this._key = bcoin.ecdsa.genKeyPair({
|
|
pers: options.personalization,
|
|
entropy: options.entropy
|
|
|| (options.passphrase ? utils.sha256(options.passphrase) : null)
|
|
});
|
|
}
|
|
}
|
|
|
|
KeyPair.prototype.__defineGetter__('priv', function() {
|
|
return this._key.getPrivate();
|
|
});
|
|
|
|
KeyPair.prototype.__defineGetter__('pub', function() {
|
|
return this._key.getPublic();
|
|
});
|
|
|
|
KeyPair.prototype.getPrivate = function getPrivate(enc) {
|
|
var priv = this._key.getPrivate();
|
|
|
|
if (!priv)
|
|
return;
|
|
|
|
priv = priv.toArray();
|
|
|
|
if (enc === 'base58')
|
|
return KeyPair.toSecret(priv, this.compressed);
|
|
|
|
if (enc === 'hex')
|
|
return utils.toHex(priv);
|
|
|
|
return priv;
|
|
};
|
|
|
|
KeyPair.prototype.getPublic = function getPublic(enc) {
|
|
var pub = this._key.getPublic(this.compressed, 'array');
|
|
|
|
if (enc === 'base58')
|
|
return utils.toBase58(pub);
|
|
|
|
if (enc === 'hex')
|
|
return utils.toHex(pub);
|
|
|
|
return pub;
|
|
};
|
|
|
|
KeyPair.prototype.toSecret = function toSecret() {
|
|
return KeyPair.toSecret(this.getPrivate(), this.compressed);
|
|
};
|
|
|
|
KeyPair.toSecret = function toSecret(priv, compressed) {
|
|
var arr, chk;
|
|
|
|
// We'll be using ncompressed public key as an address
|
|
arr = [network.prefixes.privkey];
|
|
|
|
// 0-pad key
|
|
while (arr.length + priv.length < 33)
|
|
arr.push(0);
|
|
|
|
arr = arr.concat(priv);
|
|
|
|
if (compressed)
|
|
arr.push(1);
|
|
|
|
chk = utils.checksum(arr);
|
|
|
|
return utils.toBase58(arr.concat(chk));
|
|
};
|
|
|
|
KeyPair.fromSecret = function fromSecret(priv) {
|
|
var key, compressed;
|
|
|
|
key = utils.fromBase58(priv);
|
|
assert(utils.isEqual(key.slice(-4), utils.checksum(key.slice(0, -4))));
|
|
assert.equal(key[0], network.prefixes.privkey);
|
|
|
|
key = key.slice(0, -4);
|
|
if (key.length === 34) {
|
|
assert.equal(key[33], 1);
|
|
priv = key.slice(1, -1);
|
|
compressed = true;
|
|
} else {
|
|
priv = key.slice(1);
|
|
compressed = false;
|
|
}
|
|
|
|
return new KeyPair({
|
|
priv: priv,
|
|
compressed: compressed
|
|
});
|
|
};
|
|
|
|
KeyPair.prototype.toJSON = function toJSON(encrypt) {
|
|
var json = {
|
|
v: 1,
|
|
name: 'keypair',
|
|
encrypted: encrypt ? true : false
|
|
};
|
|
|
|
if (this.hd) {
|
|
if (this.hd.xprivkey) {
|
|
if (this.hd.seed) {
|
|
json.mnemonic = encrypt
|
|
? encrypt(this.hd.seed.mnemonic)
|
|
: this.hd.seed.mnemonic;
|
|
json.passphrase = encrypt
|
|
? encrypt(this.hd.seed.passphrase)
|
|
: this.hd.seed.passphrase;
|
|
return json;
|
|
}
|
|
json.xpriv = encrypt
|
|
? encrypt(this.hd.xprivkey)
|
|
: this.hd.xprivkey;
|
|
return json;
|
|
}
|
|
|
|
json.xpub = this.hd.xpubkey;
|
|
|
|
return json;
|
|
}
|
|
|
|
if (this._key.priv) {
|
|
json.priv = encrypt
|
|
? encrypt(this.getPrivate('base58'))
|
|
: this.getPrivate('base58');
|
|
return json;
|
|
}
|
|
|
|
json.pub = this.getPublic('hex');
|
|
return json;
|
|
};
|
|
|
|
KeyPair.fromJSON = function fromJSON(json, decrypt) {
|
|
var key, priv, pub, compressed, xprivkey;
|
|
var path = {};
|
|
|
|
assert.equal(json.v, 1);
|
|
assert.equal(json.name, 'keypair');
|
|
|
|
if (json.encrypted && !decrypt)
|
|
throw new Error('Cannot decrypt address');
|
|
|
|
if (json.mnemonic) {
|
|
return new KeyPair({
|
|
key: bcoin.hd.priv({
|
|
seed: bcoin.hd.seed({
|
|
mnemonic: json.encrypted
|
|
? decrypt(json.mnemonic)
|
|
: json.mnemonic,
|
|
passphrase: json.encrypted
|
|
? decrypt(json.passphrase)
|
|
: json.passphrase
|
|
})
|
|
})
|
|
});
|
|
}
|
|
|
|
if (json.xpriv) {
|
|
xprivkey = json.xpriv;
|
|
if (json.encrypted)
|
|
xprivkey = decrypt(xprivkey);
|
|
return new KeyPair({
|
|
key: bcoin.hd.priv({
|
|
xkey: xprivkey
|
|
})
|
|
});
|
|
}
|
|
|
|
if (json.xpub) {
|
|
return new KeyPair({
|
|
key: bcoin.hd.pub({
|
|
xkey: json.xpub
|
|
})
|
|
});
|
|
}
|
|
|
|
if (json.priv) {
|
|
priv = json.priv;
|
|
if (json.encrypted)
|
|
priv = decrypt(priv);
|
|
|
|
key = KeyPair.fromSecret(json.priv);
|
|
priv = key.priv;
|
|
compressed = key.compressed;
|
|
return new KeyPair({
|
|
priv: priv,
|
|
compressed: compressed
|
|
});
|
|
}
|
|
|
|
if (json.pub) {
|
|
pub = bcoin.utils.toArray(json.pub, 'hex');
|
|
compressed = pub[0] !== 0x04;
|
|
return new KeyPair({
|
|
pub: pub,
|
|
compressed: compressed
|
|
});
|
|
}
|
|
|
|
assert(false);
|
|
};
|
|
|
|
/**
|
|
* Expose
|
|
*/
|
|
|
|
module.exports = KeyPair;
|