bitcore/lib/publickey.js

372 lines
9.3 KiB
JavaScript
Raw Normal View History

2014-11-20 07:52:32 -08:00
'use strict';
var Address = require('./address');
var BN = require('./crypto/bn');
var Point = require('./crypto/point');
var JSUtil = require('./util/js');
/**
*
* Instantiate a PublicKey from a 'PrivateKey', 'Point', 'string', 'Buffer'.
*
* @example
*
* // instantiate from a private key
* var key = PublicKey(privateKey, true);
*
* // export to as a DER hex encoded string
* var exported = key.toString();
*
* // import the public key
* var imported = PublicKey.fromString(exported);
*
* @param {String} data - The encoded data in various formats
* @param {String} [compressed] - If the public key is compressed
* @returns {PublicKey} A new valid instance of an PublicKey
* @constructor
*/
var PublicKey = function PublicKey(data, compressed) {
if (!(this instanceof PublicKey)) {
return new PublicKey(data, compressed);
2014-09-01 21:16:10 -07:00
}
2014-08-28 17:41:38 -07:00
if (!data) {
throw new TypeError('First argument is required, please include public key data.');
}
if (data instanceof PublicKey) {
// Return copy, but as it's an immutable object, return same argument
return data;
}
var info = {
compressed: typeof(compressed) !== 'undefined' ? compressed : true
};
// detect type of data
if (data instanceof Point) {
info.point = data;
} else if (typeof(data) === 'string'){
info = PublicKey._transformDER(new Buffer(data, 'hex'));
2014-11-25 12:30:25 -08:00
} else if (data instanceof Buffer || data instanceof Uint8Array){
info = PublicKey._transformDER(data);
} else if (data.constructor && (data.constructor.name &&
data.constructor.name === 'PrivateKey')) {
info = PublicKey._transformPrivateKey(data);
} else {
throw new TypeError('First argument is an unrecognized data format.');
}
// validation
info.point.validate();
Object.defineProperty(this, 'point', {
configurable: false,
value: info.point
});
Object.defineProperty(this, 'compressed', {
configurable: false,
value: info.compressed
});
2014-08-07 16:18:17 -07:00
return this;
};
/**
* Internal function to transform a private key into a public key point
*
* @param {PrivateKey} privkey - An instance of PrivateKey
* @returns {Object} An object with keys: point and compressed
* @private
*/
PublicKey._transformPrivateKey = function(privkey) {
var info = {};
if (!privkey.constructor ||
(privkey.constructor.name && privkey.constructor.name !== 'PrivateKey')) {
throw new TypeError('Must be an instance of PrivateKey');
}
info.point = Point.getG().mul(privkey.bn);
info.compressed = privkey.compressed;
return info;
2014-08-28 20:19:30 -07:00
};
/**
* Internal function to transform DER into a public key point
*
* @param {Buffer} buf - An hex encoded buffer
* @returns {Object} An object with keys: point and compressed
* @private
*/
PublicKey._transformDER = function(buf){
var info = {};
2014-11-25 12:30:25 -08:00
if (!(buf instanceof Buffer) && !(buf instanceof Uint8Array)){
throw new TypeError('Must be a hex buffer of DER encoded public key');
}
var x;
var y;
var xbuf;
var ybuf;
if (buf[0] === 0x04) {
xbuf = buf.slice(1, 33);
ybuf = buf.slice(33, 65);
if (xbuf.length !== 32 || ybuf.length !== 32 || buf.length !== 65) {
throw new TypeError('Length of x and y must be 32 bytes');
}
x = BN(xbuf);
y = BN(ybuf);
info.point = Point(x, y);
info.compressed = false;
} else if (buf[0] === 0x03) {
xbuf = buf.slice(1);
x = BN(xbuf);
info = PublicKey._transformX(true, x);
info.compressed = true;
2014-08-07 16:18:17 -07:00
} else if (buf[0] == 0x02) {
xbuf = buf.slice(1);
x = BN(xbuf);
info = PublicKey._transformX(false, x);
info.compressed = true;
2014-08-07 16:18:17 -07:00
} else {
throw new TypeError('Invalid DER format public key');
2014-08-07 16:18:17 -07:00
}
return info;
};
/**
*
* Internal function to transform X into a public key point
*
* @param {Boolean} odd - If the point is above or below the x axis
* @param {Point} x - The x point
* @returns {Object} An object with keys: point and compressed
* @private
*/
PublicKey._transformX = function(odd, x){
var info = {};
if (typeof odd !== 'boolean') {
throw new TypeError('Must specify whether y is odd or not (true or false)');
}
info.point = Point.fromX(odd, x);
return info;
};
/**
*
* Instantiate a PublicKey from JSON
*
* @param {String} json - A JSON string
* @returns {PublicKey} A new valid instance of PublicKey
*/
PublicKey.fromJSON = function(json) {
2014-12-12 14:54:48 -08:00
if (JSUtil.isValidJSON(json)) {
json = JSON.parse(json);
}
var x = BN(json.x, 'hex');
var y = BN(json.y, 'hex');
var point = new Point(x, y);
return new PublicKey(point, json.compressed);
};
/**
*
* Instantiate a PublicKey from a PrivateKey
*
* @param {PrivateKey} privkey - An instance of PrivateKey
* @returns {PublicKey} A new valid instance of PublicKey
*/
PublicKey.fromPrivateKey = function(privkey) {
var info = PublicKey._transformPrivateKey(privkey);
return new PublicKey(info.point, info.compressed);
};
/**
*
* Instantiate a PublicKey from a Buffer
*
* @param {Buffer} buf - A DER hex buffer
* @returns {PublicKey} A new valid instance of PublicKey
*/
PublicKey.fromBuffer = function(buf) {
var info = PublicKey._transformDER(buf);
return new PublicKey(info.point, info.compressed);
};
/**
*
* Instantiate a PublicKey from a Point
*
* @param {Point} point - A Point instance
* @returns {PublicKey} A new valid instance of PublicKey
*/
PublicKey.fromPoint = function(point, compressed){
if (!(point instanceof Point)) {
throw new TypeError('First argument must be an instance of Point.');
}
return new PublicKey(point, compressed);
};
/**
*
* Instantiate a PublicKey from a DER Buffer
*
* @param {Buffer} buf - A DER Buffer
* @returns {PublicKey} A new valid instance of PublicKey
*/
PublicKey.fromDER = function(buf) {
var info = PublicKey._transformDER(buf);
return new PublicKey(info.point, info.compressed);
2014-08-07 16:18:17 -07:00
};
/**
*
* Instantiate a PublicKey from a DER hex encoded string
*
* @param {String} str - A DER hex string
* @param {String} [encoding] - The type of string encoding
* @returns {PublicKey} A new valid instance of PublicKey
*/
PublicKey.fromString = function(str, encoding) {
var buf = new Buffer(str, encoding || 'hex');
var info = PublicKey._transformDER(buf);
return new PublicKey(info.point, info.compressed);
2014-08-07 16:18:17 -07:00
};
/**
*
* Instantiate a PublicKey from an X Point
*
* @param {Boolean} odd - If the point is above or below the x axis
* @param {Point} x - The x point
* @returns {PublicKey} A new valid instance of PublicKey
*/
PublicKey.fromX = function(odd, x) {
var info = PublicKey._transformX(odd, x);
return new PublicKey(info.point, info.compressed);
2014-08-07 16:18:17 -07:00
};
/**
*
* Check if there would be any errors when initializing a PublicKey
*
* @param {String} data - The encoded data in various formats
* @param {String} [compressed] - If the public key is compressed
* @returns {null|Error} An error if exists
*/
PublicKey.getValidationError = function(data) {
var error;
try {
new PublicKey(data);
} catch (e) {
error = e;
}
return error;
};
/**
*
* Check if the parameters are valid
*
* @param {String} data - The encoded data in various formats
* @param {String} [compressed] - If the public key is compressed
* @returns {Boolean} If the public key would be valid
*/
PublicKey.isValid = function(data) {
return !PublicKey.getValidationError(data);
};
/**
*
* Will output the PublicKey to JSON
*
* @returns {Object} A JSON object
*/
PublicKey.prototype.toJSON = function() {
return {
x: this.point.getX().toString('hex'),
y: this.point.getY().toString('hex'),
compressed: this.compressed
};
};
/**
*
* Will output the PublicKey to a Buffer
*
* @returns {Buffer} A DER hex encoded buffer
*/
PublicKey.prototype.toBuffer = function() {
var compressed = typeof this.compressed === 'undefined' ? true : this.compressed;
return this.toDER(compressed);
};
/**
*
* Will output the PublicKey to a DER Buffer
*
* @returns {Buffer} A DER hex encoded buffer
*/
PublicKey.prototype.toDER = function(compressed) {
compressed = typeof(compressed) !== 'undefined' ? compressed : this.compressed;
if (typeof compressed !== 'boolean') {
throw new TypeError('Must specify whether the public key is compressed or not (true or false)');
}
2014-08-07 16:18:17 -07:00
2014-08-13 12:23:06 -07:00
var x = this.point.getX();
var y = this.point.getY();
2014-08-07 16:18:17 -07:00
var xbuf = x.toBuffer({size: 32});
var ybuf = y.toBuffer({size: 32});
var prefix;
2014-08-07 16:18:17 -07:00
if (!compressed) {
prefix = new Buffer([0x04]);
2014-08-07 16:18:17 -07:00
return Buffer.concat([prefix, xbuf, ybuf]);
} else {
var odd = ybuf[ybuf.length - 1] % 2;
if (odd) {
prefix = new Buffer([0x03]);
} else {
prefix = new Buffer([0x02]);
}
2014-08-07 16:18:17 -07:00
return Buffer.concat([prefix, xbuf]);
}
};
/**
*
* Will return an address for the public key
*
* @returns {Address} An address generated from the public key
*/
PublicKey.prototype.toAddress = function(network) {
return Address.fromPublicKey(this, network);
};
/**
*
* Will output the PublicKey to a DER encoded hex string
*
* @returns {String} A DER hex encoded string
*/
PublicKey.prototype.toString = function() {
var compressed = typeof this.compressed === 'undefined' ? true : this.compressed;
return this.toDER(compressed).toString('hex');
2014-08-07 16:18:17 -07:00
};
/**
*
* Will return a string formatted for the console
*
* @returns {String} Public key
*/
PublicKey.prototype.inspect = function() {
return '<PublicKey: ' + this.toString() + ', compressed: '+this.compressed+'>';
2014-08-09 17:43:24 -07:00
};
module.exports = PublicKey;