373 lines
8.1 KiB
JavaScript
373 lines
8.1 KiB
JavaScript
/*!
|
|
* keypair.js - keypair object for bcoin
|
|
* Copyright (c) 2014-2015, Fedor Indutny (MIT License)
|
|
* Copyright (c) 2014-2016, Christopher Jeffrey (MIT License).
|
|
* https://github.com/bcoin-org/bcoin
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var bcoin = require('./env');
|
|
var utils = require('./utils');
|
|
var assert = utils.assert;
|
|
var network = bcoin.protocol.network;
|
|
var BufferWriter = require('./writer');
|
|
var BufferReader = require('./reader');
|
|
|
|
/**
|
|
* Represents an ecdsa keypair.
|
|
* @exports KeyPair
|
|
* @constructor
|
|
* @param {Object} options
|
|
* @param {Buffer?} options.privateKey
|
|
* @param {Buffer?} options.publicKey
|
|
* @param {Boolean?} options.compressed
|
|
* @param {(Network|NetworkType)?} options.network
|
|
* @property {Buffer} privateKey
|
|
* @property {Buffer} publicKey
|
|
* @property {Boolean} compressed
|
|
* @property {Network} network
|
|
*/
|
|
|
|
function KeyPair(options) {
|
|
if (!(this instanceof KeyPair))
|
|
return new KeyPair(options);
|
|
|
|
this.network = bcoin.network.get();
|
|
this.compressed = true;
|
|
this.privateKey = null;
|
|
this.publicKey = null;
|
|
|
|
if (options)
|
|
this.fromOptions(options);
|
|
}
|
|
|
|
/**
|
|
* Inject properties from options object.
|
|
* @private
|
|
* @param {Object} options
|
|
*/
|
|
|
|
KeyPair.prototype.fromOptions = function fromOptions(options) {
|
|
if (options.privateKey) {
|
|
return this.fromPrivate(
|
|
options.privateKey,
|
|
options.compressed,
|
|
options.network);
|
|
}
|
|
|
|
if (options.publicKey)
|
|
return this.fromPublic(options.publicKey, options.network);
|
|
|
|
throw new Error('Must provide a key.');
|
|
};
|
|
|
|
/**
|
|
* Instantiate key pair from options object.
|
|
* @param {Object} options
|
|
* @returns {KeyPair}
|
|
*/
|
|
|
|
KeyPair.fromOptions = function fromOptions(options) {
|
|
return new KeyPair().fromOptions(options);
|
|
};
|
|
|
|
/**
|
|
* Generate a keypair.
|
|
* @param {(Network|NetworkType)?} network
|
|
* @returns {KeyPair}
|
|
*/
|
|
|
|
KeyPair.generate = function(network) {
|
|
var key = new KeyPair();
|
|
key.network = bcoin.network.get(network);
|
|
key.privateKey = bcoin.ec.generatePrivateKey();
|
|
key.publicKey = bcoin.ec.publicKeyCreate(key.privateKey, true);
|
|
return key;
|
|
};
|
|
|
|
/**
|
|
* Inject data from private key.
|
|
* @private
|
|
* @param {Buffer} privateKey
|
|
* @param {Boolean?} compressed
|
|
* @param {(NetworkType|Network}) network
|
|
*/
|
|
|
|
KeyPair.prototype.fromPrivate = function fromPrivate(privateKey, compressed, network) {
|
|
assert(Buffer.isBuffer(privateKey));
|
|
this.network = bcoin.network.get(network);
|
|
this.privateKey = privateKey;
|
|
this.compressed = compressed !== false;
|
|
this.publicKey = bcoin.ec.publicKeyCreate(this.privateKey, this.compressed);
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Instantiate key pair from a private key.
|
|
* @param {Buffer} privateKey
|
|
* @param {Boolean?} compressed
|
|
* @param {(NetworkType|Network}) network
|
|
* @returns {KeyPair}
|
|
*/
|
|
|
|
KeyPair.fromPrivate = function fromPrivate(privateKey, compressed, network) {
|
|
return new KeyPair().fromPrivate(privateKey, compressed, network);
|
|
};
|
|
|
|
/**
|
|
* Inject data from public key.
|
|
* @private
|
|
* @param {Buffer} privateKey
|
|
* @param {(NetworkType|Network}) network
|
|
*/
|
|
|
|
KeyPair.prototype.fromPublic = function fromPublic(publicKey, network) {
|
|
assert(Buffer.isBuffer(publicKey));
|
|
this.network = bcoin.network.get(network);
|
|
this.publicKey = publicKey;
|
|
this.compressed = publicKey[0] <= 0x03;
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Instantiate key pair from a public key.
|
|
* @param {Buffer} publicKey
|
|
* @param {(NetworkType|Network}) network
|
|
* @returns {KeyPair}
|
|
*/
|
|
|
|
KeyPair.fromPublic = function fromPublic(publicKey, network) {
|
|
return new KeyPair().fromPublic(publicKey, network);
|
|
};
|
|
|
|
/**
|
|
* Sign a message.
|
|
* @param {Buffer} msg
|
|
* @returns {Buffer} Signature in DER format.
|
|
*/
|
|
|
|
KeyPair.prototype.sign = function sign(msg) {
|
|
assert(this.privateKey, 'Cannot sign without private key.');
|
|
return bcoin.ec.sign(msg, this.getPrivateKey());
|
|
};
|
|
|
|
/**
|
|
* Verify a message.
|
|
* @param {Buffer} msg
|
|
* @param {Buffer} sig - Signature in DER format.
|
|
* @returns {Boolean}
|
|
*/
|
|
|
|
KeyPair.prototype.verify = function verify(msg, sig) {
|
|
return bcoin.ec.verify(msg, sig, this.getPublicKey());
|
|
};
|
|
|
|
/**
|
|
* Get private key.
|
|
* @param {String?} enc - Can be `"hex"`, `"base58"`, or `null`.
|
|
* @returns {Buffer} Private key.
|
|
*/
|
|
|
|
KeyPair.prototype.getPrivateKey = function getPrivateKey(enc) {
|
|
if (!this.privateKey)
|
|
return;
|
|
|
|
if (enc === 'base58')
|
|
return this.toSecret();
|
|
|
|
if (enc === 'hex')
|
|
return this.privateKey.toString('hex');
|
|
|
|
return this.privateKey;
|
|
};
|
|
|
|
/**
|
|
* Get public key.
|
|
* @param {String?} enc - Can be `"hex"`, or `null`.
|
|
* @returns {Buffer} Public key.
|
|
*/
|
|
|
|
KeyPair.prototype.getPublicKey = function getPublicKey(enc) {
|
|
if (enc === 'base58')
|
|
return utils.toBase58(this.publicKey);
|
|
|
|
if (enc === 'hex')
|
|
return this.publicKey.toString('hex');
|
|
|
|
return this.publicKey;
|
|
};
|
|
|
|
/**
|
|
* Convert key to a CBitcoinSecret.
|
|
* @param {(Network|NetworkType)?} network
|
|
* @returns {Buffer}
|
|
*/
|
|
|
|
KeyPair.prototype.toRaw = function toRaw(network, writer) {
|
|
var p = new BufferWriter(writer);
|
|
|
|
assert(this.privateKey, 'Cannot serialize without private key.');
|
|
|
|
if (!network)
|
|
network = this.network;
|
|
|
|
network = bcoin.network.get(network);
|
|
|
|
p.writeU8(network.keyPrefix.privkey);
|
|
p.writeBytes(this.getPrivateKey());
|
|
|
|
if (this.compressed)
|
|
p.writeU8(1);
|
|
|
|
p.writeChecksum();
|
|
|
|
if (!writer)
|
|
p = p.render();
|
|
|
|
return p;
|
|
};
|
|
|
|
/**
|
|
* Convert key to a CBitcoinSecret.
|
|
* @param {(Network|NetworkType)?} network
|
|
* @returns {Base58String}
|
|
*/
|
|
|
|
KeyPair.prototype.toSecret = function toSecret(network) {
|
|
return utils.toBase58(this.toRaw(network));
|
|
};
|
|
|
|
/**
|
|
* Inject properties from serialized CBitcoinSecret.
|
|
* @private
|
|
* @param {Buffer} data
|
|
*/
|
|
|
|
KeyPair.prototype.fromRaw = function fromRaw(data) {
|
|
var p = new BufferReader(data, true);
|
|
var i, prefix, version, type, key, compressed;
|
|
|
|
version = p.readU8();
|
|
|
|
for (i = 0; i < network.types.length; i++) {
|
|
type = network.types[i];
|
|
prefix = network[type].keyPrefix.privkey;
|
|
if (version === prefix)
|
|
break;
|
|
}
|
|
|
|
assert(i < network.types.length, 'Network not found.');
|
|
|
|
key = p.readBytes(32);
|
|
|
|
if (p.left() > 4) {
|
|
assert(p.readU8() === 1, 'Bad compression flag.');
|
|
compressed = true;
|
|
} else {
|
|
compressed = false;
|
|
}
|
|
|
|
p.verifyChecksum();
|
|
|
|
assert(bcoin.ec.privateKeyVerify(key));
|
|
|
|
return this.fromPrivate(key, compressed, type);
|
|
};
|
|
|
|
/**
|
|
* Inject properties from serialized CBitcoinSecret.
|
|
* @private
|
|
* @param {Base58String} secret
|
|
*/
|
|
|
|
KeyPair.prototype.fromSecret = function fromSecret(secret) {
|
|
return this.fromRaw(utils.fromBase58(secret));
|
|
};
|
|
|
|
/**
|
|
* Instantiate a key pair from a serialized CBitcoinSecret.
|
|
* @param {Buffer} data
|
|
* @returns {KeyPair}
|
|
*/
|
|
|
|
KeyPair.fromRaw = function fromRaw(data, enc) {
|
|
if (typeof data === 'string')
|
|
data = new Buffer(data, enc);
|
|
return new KeyPair().fromRaw(data);
|
|
};
|
|
|
|
/**
|
|
* Instantiate a key pair from a serialized CBitcoinSecret.
|
|
* @param {Base58String} secret
|
|
* @returns {KeyPair}
|
|
*/
|
|
|
|
KeyPair.fromSecret = function fromSecret(secret) {
|
|
return new KeyPair().fromSecret(secret);
|
|
};
|
|
|
|
/**
|
|
* Convert the keypair to an object suitable
|
|
* for JSON serialization.
|
|
* @returns {Object}
|
|
*/
|
|
|
|
KeyPair.prototype.toJSON = function toJSON() {
|
|
return {
|
|
network: this.network.type,
|
|
compressed: this.compressed,
|
|
privateKey: this.privateKey ? this.toSecret() : null,
|
|
publicKey: this.getPublicKey('base58')
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Inject properties from json object.
|
|
* @private
|
|
* @param {Object} json
|
|
*/
|
|
|
|
KeyPair.prototype.fromJSON = function fromJSON(json) {
|
|
var key;
|
|
|
|
if (json.privateKey)
|
|
return this.fromSecret(json.privateKey);
|
|
|
|
if (json.publicKey) {
|
|
key = utils.fromBase58(json.publicKey);
|
|
assert(bcoin.ec.publicKeyVerify(key));
|
|
return this.fromPublic(key, json.network);
|
|
}
|
|
|
|
assert(false, 'Could not parse KeyPair JSON.');
|
|
};
|
|
|
|
/**
|
|
* Instantiate a key pair from a jsonified object.
|
|
* @param {Object} json - The jsonified key pair object.
|
|
* @returns {KeyPair}
|
|
*/
|
|
|
|
KeyPair.fromJSON = function fromJSON(json) {
|
|
return new KeyPair().fromJSON(json);
|
|
};
|
|
|
|
/**
|
|
* Test whether an object is a key pair.
|
|
* @param {Object?} obj
|
|
* @returns {Boolean}
|
|
*/
|
|
|
|
KeyPair.isKeyPair = function isKeyPair(obj) {
|
|
return obj
|
|
&& obj.privateKey !== undefined
|
|
&& typeof obj.fromSecret === 'function';
|
|
};
|
|
|
|
/*
|
|
* Expose
|
|
*/
|
|
|
|
module.exports = KeyPair;
|