diff --git a/lib/bip32.js b/lib/bip32.js index 7c4bc3940..157c80443 100644 --- a/lib/bip32.js +++ b/lib/bip32.js @@ -41,8 +41,8 @@ BIP32.prototype.fromRandom = function(networkstr) { this.parentfingerprint = new Buffer([0, 0, 0, 0]); this.childindex = new Buffer([0, 0, 0, 0]); this.chaincode = Random.getRandomBuffer(32); - this.privkey = Privkey().fromRandom(); - this.pubkey = new Pubkey().fromPrivkey(this.privkey); + this.privkey = Privkey.fromRandom(); + this.pubkey = Pubkey.fromPrivkey(this.privkey); this.hasprivkey = true; this.pubkeyhash = Hash.sha256ripemd160(this.pubkey.toBuffer()); this.buildxpubkey(); @@ -72,8 +72,8 @@ BIP32.prototype.fromSeed = function(bytes, networkstr) { this.childindex = new Buffer([0, 0, 0, 0]); this.chaincode = hash.slice(32, 64); this.version = networks[networkstr].bip32privkey; - this.privkey = new Privkey({bn: BN().fromBuffer(hash.slice(0, 32))}); - this.pubkey = new Pubkey().fromPrivkey(this.privkey); + this.privkey = new Privkey(BN().fromBuffer(hash.slice(0, 32))); + this.pubkey = Pubkey.fromPrivkey(this.privkey); this.hasprivkey = true; this.pubkeyhash = Hash.sha256ripemd160(this.pubkey.toBuffer()); @@ -105,12 +105,12 @@ BIP32.prototype.initFromBytes = function(bytes) { this.version == networks.testnet.bip32pubkey); if (isPrivate && keyBytes[0] == 0) { - this.privkey = new Privkey({bn: BN().fromBuffer(keyBytes.slice(1, 33))}); - this.pubkey = new Pubkey().fromPrivkey(this.privkey); + this.privkey = new Privkey(BN().fromBuffer(keyBytes.slice(1, 33))); + this.pubkey = Pubkey.fromPrivkey(this.privkey); this.pubkeyhash = Hash.sha256ripemd160(this.pubkey.toBuffer()); this.hasprivkey = true; } else if (isPublic && (keyBytes[0] == 0x02 || keyBytes[0] == 0x03)) { - this.pubkey = (new Pubkey()).fromDER(keyBytes); + this.pubkey = Pubkey.fromDER(keyBytes); this.pubkeyhash = Hash.sha256ripemd160(this.pubkey.toBuffer()); this.hasprivkey = false; } else { @@ -119,7 +119,7 @@ BIP32.prototype.initFromBytes = function(bytes) { this.buildxpubkey(); this.buildxprivkey(); -} +}; BIP32.prototype.buildxpubkey = function() { this.xpubkey = new Buffer([]); @@ -153,7 +153,7 @@ BIP32.prototype.buildxpubkey = function() { this.chaincode, this.pubkey.toBuffer() ]); -} +}; BIP32.prototype.xpubkeyString = function(format) { if (format === undefined || format === 'base58') { @@ -228,7 +228,7 @@ BIP32.prototype.derive = function(path) { } return bip32; -} +}; BIP32.prototype.deriveChild = function(i) { if (typeof i !== 'number') @@ -270,8 +270,8 @@ BIP32.prototype.deriveChild = function(i) { ret = new BIP32(); ret.chaincode = ir; - ret.privkey = new Privkey({bn: k}); - ret.pubkey = new Pubkey().fromPrivkey(ret.privkey); + ret.privkey = new Privkey(k); + ret.pubkey = Pubkey.fromPrivkey(ret.privkey); ret.hasprivkey = true; } else { @@ -284,8 +284,7 @@ BIP32.prototype.deriveChild = function(i) { var ilG = Point.getG().mul(il); var Kpar = this.pubkey.point; var Ki = ilG.add(Kpar); - var newpub = new Pubkey(); - newpub.point = Ki; + var newpub = Pubkey.fromPoint(Ki); ret = new BIP32(); ret.chaincode = ir; @@ -305,7 +304,7 @@ BIP32.prototype.deriveChild = function(i) { ret.buildxprivkey(); return ret; -} +}; BIP32.prototype.toString = function() { var isPrivate = diff --git a/lib/privkey.js b/lib/privkey.js index 5f024a892..a2203fdbc 100644 --- a/lib/privkey.js +++ b/lib/privkey.js @@ -5,107 +5,312 @@ var Point = require('./crypto/point'); var Random = require('./crypto/random'); var networks = require('./networks'); var base58check = require('./encoding/base58check'); +var Address = require('./address'); var Pubkey = require('./pubkey'); -var Privkey = function Privkey(bn) { - if (!(this instanceof Privkey)) - return new Privkey(bn); - if (bn instanceof BN) - this.bn = bn; - else if (bn) { - var obj = bn; - this.set(obj); +/** + * + * Bitcore Privkey + * + * Instantiate a Privkey from a BN, Buffer and WIF. + * + * @example + * + * var privkey = new Privkey(); + * + * @param {String} data - The encoded data in various formats + * @param {String} [network] - Either "mainnet" or "testnet" + * @param {Boolean} [compressed] - If the key is in compressed format + * @returns {Privkey} A new valid instance of an Privkey + */ +var Privkey = function Privkey(data, network, compressed) { + + if (!(this instanceof Privkey)) { + return new Privkey(data, network, compressed); } -}; -Privkey.prototype.set = function(obj) { - this.bn = obj.bn || this.bn; - this.networkstr = obj.networkstr || this.networkstr; - this.compressed = typeof obj.compressed !== 'undefined' ? obj.compressed : this.compressed; + var info = { + compressed: typeof(compressed) !== 'undefined' ? compressed : true, + network: network || 'mainnet' + }; + + // detect type of data + if (!data){ + info.bn = Privkey._getRandomBN(); + } else if (data instanceof BN) { + info.bn = data; + } else if (data instanceof Buffer) { + info = Privkey._transformBuffer(data, network, compressed); + } else if (typeof(data) === 'string'){ + info = Privkey._transformWIF(data, network, compressed); + } else { + throw new TypeError('First argument is an unrecognized data type.'); + } + + // validation + if (!info.bn.lt(Point.getN())) { + throw new TypeError('Number must be less than N'); + } + if (typeof(networks[info.network]) === 'undefined') { + throw new TypeError('Must specify the network ("mainnet" or "testnet")'); + } + if (typeof(info.compressed) !== 'boolean') { + throw new TypeError('Must specify whether the corresponding public key is compressed or not (true or false)'); + } + + this.bn = info.bn; + this.compressed = info.compressed; + this.network = info.network; + return this; + }; -Privkey.prototype.fromJSON = function(json) { - this.fromString(json); - return this; -}; - -Privkey.prototype.toJSON = function() { - return this.toString(); -}; - -Privkey.prototype.fromRandom = function() { +/** + * + * Internal function to get a random BN + * + * @returns {Object} An object with keys: bn, network and compressed + */ +Privkey._getRandomBN = function(){ + var condition; + var bn; do { var privbuf = Random.getRandomBuffer(32); - var bn = BN().fromBuffer(privbuf); - var condition = bn.lt(Point.getN()); + bn = BN().fromBuffer(privbuf); + condition = bn.lt(Point.getN()); } while (!condition); - this.set({ - bn: bn, - networkstr: 'mainnet', - compressed: true - }); - return this; + return bn; }; -Privkey.prototype.validate = function() { - if (!this.bn.lt(Point.getN())) - throw new Error('Number must be less than N'); - if (typeof networks[this.networkstr] === undefined) - throw new Error('Must specify the networkstr ("mainnet" or "testnet")'); - if (typeof this.compressed !== 'boolean') - throw new Error('Must specify whether the corresponding public key is compressed or not (true or false)'); +/** + * + * Internal function to transform a WIF Buffer into a private key + * + * @param {Buffer} buf - An WIF string + * @param {String} [network] - Either "mainnet" or "testnet" + * @param {String} [compressed] - If the private key is compressed + * @returns {Object} An object with keys: bn, network and compressed + */ +Privkey._transformBuffer = function(buf, network, compressed) { + + var info = {}; + + if (buf.length === 1 + 32 + 1 && buf[1 + 32 + 1 - 1] === 1) { + info.compressed = true; + } else if (buf.length === 1 + 32) { + info.compressed = false; + } else { + throw new Error('Length of buffer must be 33 (uncompressed) or 34 (compressed)'); + } + + if (buf[0] === networks.mainnet.privkey) { + info.network = 'mainnet'; + } else if (buf[0] === networks.testnet.privkey) { + info.network = 'testnet'; + } else { + throw new Error('Invalid network'); + } + + if (network && info.network !== network){ + throw TypeError('Private key network mismatch'); + } + + if (typeof(compressed) !== 'undefined' && info.compressed !== compressed){ + throw TypeError('Private key compression mismatch'); + } + + info.bn = BN.fromBuffer(buf.slice(1, 32 + 1)); + + return info; + }; +/** + * + * Internal function to transform a WIF string into a private key + * + * @param {String} buf - An WIF string + * @returns {Object} An object with keys: bn, network and compressed + */ +Privkey._transformWIF = function(str, network, compressed) { + return Privkey._transformBuffer(base58check.decode(str), network, compressed); +}; + +/** + * + * Instantiate a Privkey from a WIF string + * + * @param {String} str - The WIF encoded private key string + * @returns {Privkey} A new valid instance of Privkey + */ +Privkey.fromWIF = function(str) { + var info = Privkey._transformWIF(str); + return new Privkey(info.bn, info.network, info.compressed); +}; + +/** + * + * Instantiate a Privkey from a WIF JSON string + * + * @param {String} str - The WIF encoded private key string + * @returns {Privkey} A new valid instance of Privkey + */ +Privkey.fromJSON = function(json) { + var info = Privkey._transformWIF(json); + return new Privkey(info.bn, info.network, info.compressed); +}; + +/** + * + * Instantiate a Privkey from random bytes + * + * @param {String} [network] - Either "mainnet" or "testnet" + * @param {String} [compressed] - If the private key is compressed + * @returns {Privkey} A new valid instance of Privkey + */ +Privkey.fromRandom = function(network, compressed) { + var bn = Privkey._getRandomBN(); + return new Privkey(bn, network, compressed); +}; + +/** + * + * Instantiate a Privkey from a WIF string + * + * @param {String} str - The WIF encoded private key string + * @returns {Privkey} A new valid instance of Privkey + */ +Privkey.fromString = function(str) { + var info = Privkey._transformWIF(str); + return new Privkey(info.bn, info.network, info.compressed); +}; + +/** + * + * Check if there would be any errors when initializing a Privkey + * + * @param {String} data - The encoded data in various formats + * @param {String} [network] - Either "mainnet" or "testnet" + * @param {String} [compressed] - If the private key is compressed + * @returns {null|Error} An error if exists + */ + +Privkey.getValidationError = function(data, network, compressed) { + var error; + try { + new Privkey(data, network, compressed); + } catch (e) { + error = e; + } + return error; +}; + +/** + * + * Check if the parameters are valid + * + * @param {String} data - The encoded data in various formats + * @param {String} [network] - Either "mainnet" or "testnet" + * @param {String} [compressed] - If the private key is compressed + * @returns {Boolean} If the private key is would be valid + */ +Privkey.isValid = function(data, network, compressed){ + return !Privkey.getValidationError(data, network, compressed); +}; + +/** + * + * Will output the Privkey to a WIF string + * + * @returns {String} A WIP representation of the private key + */ Privkey.prototype.toWIF = function() { - var networkstr = this.networkstr; + var network = this.network; var compressed = this.compressed; - if (typeof this.networkstr === 'undefined') - networkstr = 'mainnet'; - if (typeof this.compressed === 'undefined') - compressed = true; - - var privbuf = this.bn.toBuffer({size: 32}); var buf; - if (compressed) - buf = Buffer.concat([new Buffer([networks[networkstr].privkey]), this.bn.toBuffer({size: 32}), new Buffer([0x01])]); - else - buf = Buffer.concat([new Buffer([networks[networkstr].privkey]), this.bn.toBuffer({size: 32})]); + if (compressed) { + buf = Buffer.concat([new Buffer([networks[network].privkey]), + this.bn.toBuffer({size: 32}), + new Buffer([0x01])]); + } else { + buf = Buffer.concat([new Buffer([networks[network].privkey]), + this.bn.toBuffer({size: 32})]); + } return base58check.encode(buf); }; -Privkey.prototype.fromWIF = function(str) { - var buf = base58check.decode(str); - - if (buf.length === 1 + 32 + 1 && buf[1 + 32 + 1 - 1] == 1) - this.compressed = true; - else if (buf.length === 1 + 32) - this.compressed = false; - else - throw new Error('Length of buffer must be 33 (uncompressed) or 34 (compressed)'); - - if (buf[0] === networks.mainnet.privkey) - this.networkstr = 'mainnet'; - else if (buf[0] === networks.testnet.privkey) - this.networkstr = 'testnet'; - else - throw new Error('Invalid networkstr'); - - this.bn = BN.fromBuffer(buf.slice(1, 32 + 1)); +/** + * + * Will return the private key as a BN instance + * + * @returns {BN} A BN instance of the private key + */ +Privkey.prototype.toBigNumber = function(){ + return this.bn; }; +/** + * + * Will return the private key as a BN buffer + * + * @returns {Buffer} A buffer of the private key + */ +Privkey.prototype.toBuffer = function(){ + return this.bn.toBuffer(); +}; + +/** + * + * Will return the corresponding public key + * + * @returns {Pubkey} A public key generated from the private key + */ +Privkey.prototype.toPubkey = function(){ + return Pubkey.fromPrivkey(this); +}; + +/** + * + * Will return an address for the private key + * + * @returns {Address} An address generated from the private key + */ +Privkey.prototype.toAddress = function() { + var pubkey = this.toPubkey(); + return Address.fromPubkey(pubkey, this.network); +}; + +/** + * + * Will output the Privkey to a WIF string + * + * @returns {String} A WIF representation of the private key + */ +Privkey.prototype.toJSON = function() { + return this.toString(); +}; + +/** + * + * Will output the Privkey to a WIF string + * + * @returns {String} A WIF representation of the private key + */ Privkey.prototype.toString = function() { return this.toWIF(); }; -Privkey.prototype.toPubkey = function() { - return new Pubkey().fromPrivkey(this); -} - -Privkey.prototype.fromString = function(str) { - this.fromWIF(str); +/** + * + * Will return a string formatted for the console + * + * @returns {String} Private key + */ +Privkey.prototype.inspect = function() { + return ''; }; module.exports = Privkey; diff --git a/lib/pubkey.js b/lib/pubkey.js index d9062a4ff..77a1f0f00 100644 --- a/lib/pubkey.js +++ b/lib/pubkey.js @@ -1,94 +1,300 @@ 'use strict'; var Point = require('./crypto/point'); -var bn = require('./crypto/bn'); +var BN = require('./crypto/bn'); -var Pubkey = function Pubkey(point) { - if (!(this instanceof Pubkey)) - return new Pubkey(point); - if (point instanceof Point) - this.point = point; - else if (point) { - var obj = point; - this.set(obj); +/** + * + * Bitcore Pubkey + * + * Instantiate a Pubkey from a 'Privkey', 'Point', 'string', 'Buffer'. + * + * @example + * + * var pubkey = new Pubkey(privkey, true); + * + * @param {String} data - The encoded data in various formats + * @param {String} [compressed] - If the public key is compressed + * @returns {Pubkey} A new valid instance of an Pubkey + */ +var Pubkey = function Pubkey(data, compressed) { + + if (!(this instanceof Pubkey)) { + return new Pubkey(data, compressed); } -}; -Pubkey.prototype.set = function(obj) { - if (obj.point && !obj.point.getX() && !obj.point.getY()) - throw new Error('Invalid point'); - this.point = obj.point || this.point; - this.compressed = typeof obj.compressed !== 'undefined' ? obj.compressed : this.compressed; + if (!data) { + throw new TypeError('First argument is required, please include public key 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 = Pubkey._transformDER(new Buffer(data, 'hex' )); + } else if (data instanceof Buffer){ + info = Pubkey._transformDER(data); + } else if (data.constructor && (data.constructor.name && + data.constructor.name === 'Privkey')) { + info = Pubkey._transformPrivkey(data); + } else { + throw new TypeError('First argument is an unrecognized data format.'); + } + + // validation + if (info.point.isInfinity()){ + throw new Error('Point cannot be equal to Infinity'); + } + if (info.point.eq(Point(BN(0), BN(0)))){ + throw new Error('Point cannot be equal to 0, 0'); + } + + //https://www.iacr.org/archive/pkc2003/25670211/25670211.pdf + info.point.validate(); + + this.point = info.point; + this.compressed = info.compressed; + return this; + }; -Pubkey.prototype.fromJSON = function(json) { - this.fromBuffer(new Buffer(json, 'hex')); - return this; +/** + * + * Internal function to transform a private key into a public key point + * + * @param {Privkey} privkey - An instance of Privkey + * @returns {Object} An object with keys: point and compressed + */ +Pubkey._transformPrivkey = function(privkey) { + var info = {}; + if (!privkey.constructor || + (privkey.constructor.name && privkey.constructor.name !== 'Privkey')) { + throw new TypeError('Must be an instance of Privkey'); + } + info.point = Point.getG().mul(privkey.bn); + info.compressed = privkey.compressed; + return info; }; +/** + * + * 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 + */ +Pubkey._transformDER = function(buf){ + var info = {}; + if (!(buf instanceof Buffer)){ + 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 = Pubkey._transformX(true, x); + info.compressed = true; + } else if (buf[0] == 0x02) { + xbuf = buf.slice(1); + x = BN(xbuf); + info = Pubkey._transformX(false, x); + info.compressed = true; + } else { + throw new TypeError('Invalid DER format pubkey'); + } + 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 + */ +Pubkey._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 Pubkey from JSON + * + * @param {String} json - A JSON string of DER encoded pubkey + * @returns {Pubkey} A new valid instance of Pubkey + */ +Pubkey.fromJSON = function(json) { + var buf = new Buffer(json, 'hex'); + var info = Pubkey._transformDER(buf); + return new Pubkey(info.point, info.compressed); +}; + +/** + * + * Instantiate a Pubkey from a Privkey + * + * @param {Privkey} privkey - An instance of Privkey + * @returns {Pubkey} A new valid instance of Pubkey + */ +Pubkey.fromPrivkey = function(privkey) { + var info = Pubkey._transformPrivkey(privkey); + return new Pubkey(info.point, info.compressed); +}; + +/** + * + * Instantiate a Pubkey from a Buffer + * + * @param {Buffer} buf - A DER hex buffer + * @returns {Pubkey} A new valid instance of Pubkey + */ +Pubkey.fromBuffer = function(buf) { + var info = Pubkey._transformDER(buf); + return new Pubkey(info.point, info.compressed); +}; + +/** + * + * Instantiate a Pubkey from a Point + * + * @param {Point} point - A Point instance + * @returns {Pubkey} A new valid instance of Pubkey + */ +Pubkey.fromPoint = function(point){ + if (!(point instanceof Point)) { + throw new TypeError('First argument must be an instance of Point.'); + } + return new Pubkey(point); +}; + +/** + * + * Instantiate a Pubkey from a DER Buffer + * + * @param {Buffer} buf - A DER Buffer + * @returns {Pubkey} A new valid instance of Pubkey + */ +Pubkey.fromDER = function(buf) { + var info = Pubkey._transformDER(buf); + return new Pubkey(info.point, info.compressed); +}; + +/** + * + * Instantiate a Pubkey from a DER hex encoded string + * + * @param {String} str - A DER hex string + * @param {String} [encoding] - The type of string encoding + * @returns {Pubkey} A new valid instance of Pubkey + */ +Pubkey.fromString = function(str, encoding) { + var buf = new Buffer(str, encoding || 'hex'); + var info = Pubkey._transformDER(buf); + return new Pubkey(info.point, info.compressed); +}; + +/** + * + * Instantiate a Pubkey from an X Point + * + * @param {Boolean} odd - If the point is above or below the x axis + * @param {Point} x - The x point + * @returns {Pubkey} A new valid instance of Pubkey + */ +Pubkey.fromX = function(odd, x) { + var info = Pubkey._transformX(odd, x); + return new Pubkey(info.point, info.compressed); +}; + + +/** + * + * Check if there would be any errors when initializing a Pubkey + * + * @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 + */ +Pubkey.getValidationError = function(data, compressed) { + var error; + try { + new Pubkey(data, compressed); + } 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 pubkey is would be valid + */ +Pubkey.isValid = function(data, compressed) { + return !Pubkey.getValidationError(data, compressed); +}; + +/** + * + * Will output the Pubkey to JSON + * + * @returns {String} A hex encoded string + */ Pubkey.prototype.toJSON = function() { return this.toBuffer().toString('hex'); }; -Pubkey.prototype.fromPrivkey = function(privkey) { - this.set({ - point: Point.getG().mul(privkey.bn), - compressed: privkey.compressed} - ); - return this; -}; - -Pubkey.prototype.fromBuffer = function(buf) { - return this.fromDER(buf); -}; - -Pubkey.prototype.fromDER = function(buf) { - if (buf[0] == 0x04) { - var xbuf = buf.slice(1, 33); - var ybuf = buf.slice(33, 65); - if (xbuf.length !== 32 || ybuf.length !== 32 || buf.length !== 65) - throw new Error('Length of x and y must be 32 bytes'); - var x = bn(xbuf); - var y = bn(ybuf); - this.point = Point(x, y); - this.compressed = false; - } else if (buf[0] == 0x03) { - var xbuf = buf.slice(1); - var x = bn(xbuf); - this.fromX(true, x); - this.compressed = true; - } else if (buf[0] == 0x02) { - var xbuf = buf.slice(1); - var x = bn(xbuf); - this.fromX(false, x); - this.compressed = true; - } else { - throw new Error('Invalid DER format pubkey'); - } - return this; -}; - -Pubkey.prototype.fromString = function( str , encoding ) { - var encoding = encoding || 'hex'; - this.fromDER( new Buffer(str, encoding ) ); -}; - -Pubkey.prototype.fromX = function(odd, x) { - if (typeof odd !== 'boolean') - throw new Error('Must specify whether y is odd or not (true or false)'); - this.point = Point.fromX(odd, x); -}; - +/** + * + * Will output the Pubkey to a Buffer + * + * @returns {Buffer} A DER hex encoded buffer + */ Pubkey.prototype.toBuffer = function() { var compressed = typeof this.compressed === 'undefined' ? true : this.compressed; return this.toDER(compressed); }; +/** + * + * Will output the Pubkey to a DER Buffer + * + * @returns {Buffer} A DER hex encoded buffer + */ Pubkey.prototype.toDER = function(compressed) { - compressed = typeof this.compressed === 'undefined' ? compressed : this.compressed; - if (typeof compressed !== 'boolean') - throw new Error('Must specify whether the public key is compressed or not (true or false)'); + 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)'); + } var x = this.point.getX(); var y = this.point.getY(); @@ -96,32 +302,40 @@ Pubkey.prototype.toDER = function(compressed) { var xbuf = x.toBuffer({size: 32}); var ybuf = y.toBuffer({size: 32}); + var prefix; if (!compressed) { - var prefix = new Buffer([0x04]); + prefix = new Buffer([0x04]); return Buffer.concat([prefix, xbuf, ybuf]); } else { var odd = ybuf[ybuf.length - 1] % 2; - if (odd) - var prefix = new Buffer([0x03]); - else - var prefix = new Buffer([0x02]); + if (odd) { + prefix = new Buffer([0x03]); + } else { + prefix = new Buffer([0x02]); + } return Buffer.concat([prefix, xbuf]); } }; +/** + * + * Will output the Pubkey to a DER encoded hex string + * + * @returns {String} A DER hex encoded string + */ Pubkey.prototype.toString = function() { var compressed = typeof this.compressed === 'undefined' ? true : this.compressed; return this.toDER(compressed).toString('hex'); }; -//https://www.iacr.org/archive/pkc2003/25670211/25670211.pdf -Pubkey.prototype.validate = function() { - if (this.point.isInfinity()) - throw new Error('point: Point cannot be equal to Infinity'); - if (this.point.eq(Point(bn(0), bn(0)))) - throw new Error('point: Point cannot be equal to 0, 0'); - this.point.validate(); - return this; +/** + * + * Will return a string formatted for the console + * + * @returns {String} Public key + */ +Pubkey.prototype.inspect = function() { + return ''; }; module.exports = Pubkey; diff --git a/test/address.js b/test/address.js index 6fb2019b1..b107be0f6 100644 --- a/test/address.js +++ b/test/address.js @@ -266,22 +266,15 @@ describe('Address', function() { }).should.throw('Address hashbuffers must be exactly 20 bytes.'); }); - it('should make this address from a compressed pubkey object', function() { - var pubkey = new Pubkey(); - pubkey.fromDER(new Buffer('0285e9737a74c30a873f74df05124f2aa6f53042c2fc0a130d6cbd7d16b944b004', - 'hex')); - var a = Address.fromPubkey(pubkey); - a.toString().should.equal('19gH5uhqY6DKrtkU66PsZPUZdzTd11Y7ke'); - var b = new Address(pubkey); - b.toString().should.equal('19gH5uhqY6DKrtkU66PsZPUZdzTd11Y7ke'); + it('should make this address from a compressed pubkey', function() { + var pubkey = Pubkey.fromDER(new Buffer('0285e9737a74c30a873f74df05124f2aa6f53042c2fc0a130d6cbd7d16b944b004', 'hex')); + var address = Address.fromPubkey(pubkey); + address.toString().should.equal('19gH5uhqY6DKrtkU66PsZPUZdzTd11Y7ke'); }); it('should make this address from an uncompressed pubkey', function() { - var pubkey = new Pubkey(); - pubkey.fromDER(new Buffer('0285e9737a74c30a873f74df05124f2aa6f53042c2fc0a130d6cbd7d16b944b004', - 'hex')); - pubkey.compressed = false; - var a = Address.fromPubkey(pubkey, 'mainnet', 'pubkeyhash'); + var pubkey = Pubkey.fromDER(new Buffer('0485e9737a74c30a873f74df05124f2aa6f53042c2fc0a130d6cbd7d16b944b004833fef26c8be4c4823754869ff4e46755b85d851077771c220e2610496a29d98', 'hex')); + var a = Address.fromPubkey(pubkey, 'mainnet'); a.toString().should.equal('16JXnhxjJUhxfyx4y6H4sFcxrgt8kQ8ewX'); var b = new Address(pubkey, 'mainnet', 'pubkeyhash'); b.toString().should.equal('16JXnhxjJUhxfyx4y6H4sFcxrgt8kQ8ewX'); @@ -310,7 +303,7 @@ describe('Address', function() { var address = new Address(str); var buffer = address.toBuffer(); var slice = buffer.slice(1); - var sliceString = slice.toString('hex') + var sliceString = slice.toString('hex'); sliceString.should.equal(pubkeyhash.toString('hex')); }); diff --git a/test/privkey.js b/test/privkey.js index 497fc9cce..1420f800e 100644 --- a/test/privkey.js +++ b/test/privkey.js @@ -5,7 +5,7 @@ var bitcore = require('..'); var BN = bitcore.crypto.BN; var Point = bitcore.crypto.Point; var Privkey = bitcore.Privkey; -var Pubkey = bitcore.Pubkey; +var base58check = bitcore.encoding.Base58Check; describe('Privkey', function() { var hex = '96c132224121b509b7d0a16245e957d9192609c5637c6228311287b1be21627a'; @@ -15,9 +15,78 @@ describe('Privkey', function() { var encmainnet = 'L2Gkw3kKJ6N24QcDuH4XDqt9cTqsKTVNDGz1CRZhk9cq4auDUbJy'; var encmu = '5JxgQaFM1FMd38cd14e3mbdxsdSa9iM2BV6DHBYsvGzxkTNQ7Un'; - it('should create an empty private key', function() { - var privkey = new Privkey(); - should.exist(privkey); + it('should create a new random private key', function() { + var a = new Privkey(); + should.exist(a); + should.exist(a.bn); + var b = Privkey(); + should.exist(b); + should.exist(b.bn); + }); + + it('should create a private key from WIF string', function() { + var a = new Privkey('L3T1s1TYP9oyhHpXgkyLoJFGniEgkv2Jhi138d7R2yJ9F4QdDU2m'); + should.exist(a); + should.exist(a.bn); + }); + + it('should create a private key from WIF buffer', function() { + var a = new Privkey(base58check.decode('L3T1s1TYP9oyhHpXgkyLoJFGniEgkv2Jhi138d7R2yJ9F4QdDU2m')); + should.exist(a); + should.exist(a.bn); + }); + + it('should not be able to instantiate private key greater than N', function() { + (function() { + var n = Point.getN(); + var a = new Privkey(n); + }).should.throw('Number must be less than N'); + }); + + it('should not be able to instantiate private key because of network mismatch', function() { + (function() { + var a = new Privkey('L3T1s1TYP9oyhHpXgkyLoJFGniEgkv2Jhi138d7R2yJ9F4QdDU2m', 'testnet'); + }).should.throw('Private key network mismatch'); + }); + + it('should not be able to instantiate private key because of compression mismatch', function() { + (function() { + var a = new Privkey('L3T1s1TYP9oyhHpXgkyLoJFGniEgkv2Jhi138d7R2yJ9F4QdDU2m', 'mainnet', false); + }).should.throw('Private key compression mismatch'); + }); + + it('should not be able to instantiate private key WIF is too long', function() { + (function() { + var buf = base58check.decode('L3T1s1TYP9oyhHpXgkyLoJFGniEgkv2Jhi138d7R2yJ9F4QdDU2m'); + var buf2 = Buffer.concat([buf, new Buffer(0x01)]); + var a = new Privkey(buf2); + }).should.throw('Length of buffer must be 33 (uncompressed) or 34 (compressed'); + }); + + it('should not be able to instantiate private key WIF because of unknown network byte', function() { + (function() { + var buf = base58check.decode('L3T1s1TYP9oyhHpXgkyLoJFGniEgkv2Jhi138d7R2yJ9F4QdDU2m'); + var buf2 = Buffer.concat([new Buffer(0x01, 'hex'), buf.slice(1, 33)]); + var a = new Privkey(buf2); + }).should.throw('Invalid network'); + }); + + it('should not be able to instantiate because compressed is non-boolean', function() { + (function() { + var a = new Privkey(null, 'testnet', 'compressed'); + }).should.throw('Must specify whether the corresponding public key is compressed or not (true or false)'); + }); + + it('should not be able to instantiate because of unrecognized data', function() { + (function() { + var a = new Privkey(new Error()); + }).should.throw('First argument is an unrecognized data type.'); + }); + + it('should not be able to instantiate with unknown network', function() { + (function() { + var a = new Privkey(null, 'unknown'); + }).should.throw('Must specify the network ("mainnet" or "testnet")'); }); it('should create a 0 private key with this convenience method', function() { @@ -27,47 +96,24 @@ describe('Privkey', function() { }); it('should create a mainnet private key', function() { - var privkey = new Privkey({ - bn: BN.fromBuffer(buf), - networkstr: 'mainnet', - compressed: true - }); + var privkey = new Privkey(BN.fromBuffer(buf), 'mainnet', true); privkey.toString().should.equal(encmainnet); }); it('should create an uncompressed testnet private key', function() { - var privkey = new Privkey({ - bn: BN.fromBuffer(buf), - networkstr: 'testnet', - compressed: false - }); + var privkey = new Privkey(BN.fromBuffer(buf), 'testnet', false); privkey.toString().should.equal(enctu); }); it('should create an uncompressed mainnet private key', function() { - var privkey = new Privkey({ - bn: BN.fromBuffer(buf), - networkstr: 'mainnet', - compressed: false - }); + var privkey = new Privkey(BN.fromBuffer(buf), 'mainnet', false); privkey.toString().should.equal(encmu); }); - describe('#set', function() { - - it('should set bn', function() { - should.exist(Privkey().set({ - bn: BN.fromBuffer(buf) - }).bn); - }); - - }); - describe('#fromJSON', function() { it('should input this address correctly', function() { - var privkey = new Privkey(); - privkey.fromJSON(encmu); + var privkey = Privkey.fromJSON(encmu); privkey.toWIF().should.equal(encmu); }); @@ -76,17 +122,80 @@ describe('Privkey', function() { describe('#toString', function() { it('should output this address correctly', function() { - var privkey = new Privkey(); - privkey.fromJSON(encmu); + var privkey = Privkey.fromJSON(encmu); privkey.toJSON().should.equal(encmu); }); }); + describe('#toAddress', function() { + it('should output this known mainnet address correctly', function() { + var privkey = Privkey.fromWIF('L3T1s1TYP9oyhHpXgkyLoJFGniEgkv2Jhi138d7R2yJ9F4QdDU2m'); + var address = privkey.toAddress(); + address.toString().should.equal('1A6ut1tWnUq1SEQLMr4ttDh24wcbJ5o9TT'); + }); + + it('should output this known testnet address correctly', function() { + var privkey = Privkey.fromWIF('cR4qogdN9UxLZJXCNFNwDRRZNeLRWuds9TTSuLNweFVjiaE4gPaq'); + var address = privkey.toAddress(); + address.toString().should.equal('mtX8nPZZdJ8d3QNLRJ1oJTiEi26Sj6LQXS'); + }); + + }); + + describe('#inspect', function() { + it('should output known mainnet address for console', function() { + var privkey = Privkey.fromWIF('L3T1s1TYP9oyhHpXgkyLoJFGniEgkv2Jhi138d7R2yJ9F4QdDU2m'); + privkey.inspect().should.equal(''); + }); + + it('should output known testnet address for console', function() { + var privkey = Privkey.fromWIF('cR4qogdN9UxLZJXCNFNwDRRZNeLRWuds9TTSuLNweFVjiaE4gPaq'); + privkey.inspect().should.equal(''); + }); + + }); + + describe('#getValidationError', function(){ + it('should get an error because private key greater than N', function() { + var n = Point.getN(); + var a = Privkey.getValidationError(n); + a.message.should.equal('Number must be less than N'); + }); + + it('should validate as false because private key greater than N', function() { + var n = Point.getN(); + var a = Privkey.isValid(n); + a.should.equal(false); + }); + + it('should validate as true', function() { + var a = Privkey.isValid('L3T1s1TYP9oyhHpXgkyLoJFGniEgkv2Jhi138d7R2yJ9F4QdDU2m'); + a.should.equal(true); + }); + + }); + + describe('#toBuffer', function() { + it('should output known buffer', function() { + var privkey = new Privkey(BN.fromBuffer(buf), 'mainnet', true); + var b = privkey.toBuffer().toString('hex').should.equal(buf.toString('hex')); + }); + }); + + describe('#toBigNumber', function() { + it('should output known BN', function() { + var a = BN.fromBuffer(buf); + var privkey = new Privkey(a, 'mainnet', true); + var b = privkey.toBigNumber(); + b.toString('hex').should.equal(a.toString('hex')); + }); + }); + describe('#fromRandom', function() { it('should set bn gt 0 and lt n, and should be compressed', function() { - var privkey = Privkey().fromRandom(); + var privkey = Privkey.fromRandom(); privkey.bn.gt(BN(0)).should.equal(true); privkey.bn.lt(Point.getN()).should.equal(true); privkey.compressed.should.equal(true); @@ -97,8 +206,7 @@ describe('Privkey', function() { describe('#fromWIF', function() { it('should parse this compressed testnet address correctly', function() { - var privkey = new Privkey(); - privkey.fromWIF(encmainnet); + var privkey = Privkey.fromWIF(encmainnet); privkey.toWIF().should.equal(encmainnet); }); @@ -107,8 +215,7 @@ describe('Privkey', function() { describe('#toWIF', function() { it('should parse this compressed testnet address correctly', function() { - var privkey = new Privkey(); - privkey.fromWIF(enctestnet); + var privkey = Privkey.fromWIF(enctestnet); privkey.toWIF().should.equal(enctestnet); }); @@ -117,8 +224,7 @@ describe('Privkey', function() { describe('#fromString', function() { it('should parse this uncompressed testnet address correctly', function() { - var privkey = new Privkey(); - privkey.fromString(enctu); + var privkey = Privkey.fromString(enctu); privkey.toWIF().should.equal(enctu); }); @@ -127,8 +233,7 @@ describe('Privkey', function() { describe('#toString', function() { it('should parse this uncompressed mainnet address correctly', function() { - var privkey = new Privkey(); - privkey.fromString(encmu); + var privkey = Privkey.fromString(encmu); privkey.toString().should.equal(encmu); }); @@ -139,18 +244,14 @@ describe('Privkey', function() { it('should convert this known Privkey to known Pubkey', function() { var privhex = '906977a061af29276e40bf377042ffbde414e496ae2260bbf1fa9d085637bfff'; var pubhex = '02a1633cafcc01ebfb6d78e39f687a1f0995c62fc95f51ead10a02ee0be551b5dc'; - var privkey = new Privkey({ - bn: BN(new Buffer(privhex, 'hex')) - }); + var privkey = new Privkey(BN(new Buffer(privhex, 'hex'))); var pubkey = privkey.toPubkey(); pubkey.toString().should.equal(pubhex); }); it('should convert this known Privkey to known Pubkey and preserve compressed=true', function() { var privhex = '906977a061af29276e40bf377042ffbde414e496ae2260bbf1fa9d085637bfff'; - var privkey = new Privkey({ - bn: BN(new Buffer(privhex, 'hex')) - }); + var privkey = new Privkey(BN(new Buffer(privhex, 'hex'))); privkey.compressed = true; var pubkey = privkey.toPubkey(); pubkey.compressed.should.equal(true); @@ -158,9 +259,7 @@ describe('Privkey', function() { it('should convert this known Privkey to known Pubkey and preserve compressed=true', function() { var privhex = '906977a061af29276e40bf377042ffbde414e496ae2260bbf1fa9d085637bfff'; - var privkey = new Privkey({ - bn: BN(new Buffer(privhex, 'hex')) - }); + var privkey = new Privkey(BN(new Buffer(privhex, 'hex'))); privkey.compressed = false; var pubkey = privkey.toPubkey(); pubkey.compressed.should.equal(false); diff --git a/test/pubkey.js b/test/pubkey.js index be96a16e9..80fecf422 100644 --- a/test/pubkey.js +++ b/test/pubkey.js @@ -8,38 +8,101 @@ var Pubkey = bitcore.Pubkey; var Privkey = bitcore.Privkey; describe('Pubkey', function() { + + it('should error because of missing data', function() { + (function() { + var pk = new Pubkey(); + }).should.throw('First argument is required, please include public key data.'); + }); - it('should create a blank public key', function() { - var pk = new Pubkey(); - should.exist(pk); + it('should error because of an invalid point', function() { + (function() { + var pk = new Pubkey(Point()); + }).should.throw('Point cannot be equal to 0, 0'); + }); + + it('should error because of an invalid public key point, not on the secp256k1 curve', function() { + (function() { + var pk = new Pubkey(Point(1000, 1000)); + }).should.throw('Invalid y value of public key'); + }); + + it('should error because of an unrecognized data type', function() { + (function() { + var pk = new Pubkey(new Error()); + }).should.throw('First argument is an unrecognized data format.'); + }); + + it('should instantiate from a private key', function() { + var privhex = '906977a061af29276e40bf377042ffbde414e496ae2260bbf1fa9d085637bfff'; + var pubhex = '02a1633cafcc01ebfb6d78e39f687a1f0995c62fc95f51ead10a02ee0be551b5dc'; + var privkey = new Privkey(BN(new Buffer(privhex, 'hex'))); + var pk = new Pubkey(privkey); + pk.toString().should.equal(pubhex); + }); + + it('should instantiate from a hex encoded DER string', function() { + var pk = new Pubkey('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); + should.exist(pk.point); + pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); + }); + + it('should instantiate from a hex encoded DER buffer', function() { + var pk = new Pubkey(new Buffer('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341', 'hex')); + should.exist(pk.point); + pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); }); it('should create a public key with a point', function() { - var p = Point(); - var pk = new Pubkey({point: p}); - should.exist(pk.point); + var p = Point('86a80a5a2bfc48dddde2b0bd88bd56b0b6ddc4e6811445b175b90268924d7d48', + '3b402dfc89712cfe50963e670a0598e6b152b3cd94735001cdac6794975d3afd'); + var a = new Pubkey(p); + should.exist(a.point); + a.point.toString().should.equal(p.toString()); + var c = Pubkey(p); + should.exist(c.point); + c.point.toString().should.equal(p.toString()); }); - it('should create a public key with a point with this convenient method', function() { - var p = Point(); - var pk = new Pubkey(p); - should.exist(pk.point); - pk.point.toString().should.equal(p.toString()); - }); - - describe('#set', function() { - - it('should make a public key from a point', function() { - should.exist(Pubkey().set({point: Point.getG()}).point); + describe('#getValidationError', function(){ + it('should recieve an error message', function() { + var error = Pubkey.getValidationError(Point()); + should.exist(error); }); + it('should recieve a boolean as false', function() { + var valid = Pubkey.isValid(Point()); + valid.should.equal(false); + }); + + it('should recieve a boolean as true', function() { + var valid = Pubkey.isValid('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); + valid.should.equal(true); + }); + + }); + + describe('#fromPoint', function() { + + it('should instantiate from a point', function() { + var p = Point('86a80a5a2bfc48dddde2b0bd88bd56b0b6ddc4e6811445b175b90268924d7d48', + '3b402dfc89712cfe50963e670a0598e6b152b3cd94735001cdac6794975d3afd'); + var b = Pubkey.fromPoint(p); + should.exist(b.point); + b.point.toString().should.equal(p.toString()); + }); + + it('should error because paramater is not a point', function() { + (function() { + Pubkey.fromPoint(new Error()); + }).should.throw('First argument must be an instance of Point.'); + }); }); describe('#fromJSON', function() { it('should input this public key', function() { - var pk = new Pubkey(); - pk.fromJSON('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); + var pk = Pubkey.fromJSON('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); }); @@ -49,9 +112,9 @@ describe('Pubkey', function() { describe('#toJSON', function() { it('should output this pubkey', function() { - var pk = new Pubkey(); var hex = '041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'; - pk.fromJSON(hex).toJSON().should.equal(hex); + var pk = Pubkey.fromJSON(hex); + pk.toJSON().should.equal(hex); }); }); @@ -59,7 +122,13 @@ describe('Pubkey', function() { describe('#fromPrivkey', function() { it('should make a public key from a privkey', function() { - should.exist(Pubkey().fromPrivkey(Privkey().fromRandom())); + should.exist(Pubkey.fromPrivkey(Privkey.fromRandom())); + }); + + it('should error because not an instance of privkey', function() { + (function() { + Pubkey.fromPrivkey(new Error()); + }).should.throw('Must be an instance of Privkey'); }); }); @@ -67,48 +136,54 @@ describe('Pubkey', function() { describe('#fromBuffer', function() { it('should parse this uncompressed public key', function() { - var pk = new Pubkey(); - pk.fromBuffer(new Buffer('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341', 'hex')); + var pk = Pubkey.fromBuffer(new Buffer('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341', 'hex')); pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); }); it('should parse this compressed public key', function() { - var pk = new Pubkey(); - pk.fromBuffer(new Buffer('031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); + var pk = Pubkey.fromBuffer(new Buffer('031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); }); it('should throw an error on this invalid public key', function() { - var pk = new Pubkey(); (function() { - pk.fromBuffer(new Buffer('091ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); + Pubkey.fromBuffer(new Buffer('091ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); }).should.throw(); }); + it('should throw error because not a buffer', function() { + (function() { + Pubkey.fromBuffer('091ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); + }).should.throw('Must be a hex buffer of DER encoded public key'); + }); + + it('should throw error because buffer is the incorrect length', function() { + (function() { + Pubkey.fromBuffer(new Buffer('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a34112', 'hex')); + }).should.throw('Length of x and y must be 32 bytes'); + }); + }); describe('#fromDER', function() { it('should parse this uncompressed public key', function() { - var pk = new Pubkey(); - pk.fromDER(new Buffer('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341', 'hex')); + var pk = Pubkey.fromDER(new Buffer('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341', 'hex')); pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); }); it('should parse this compressed public key', function() { - var pk = new Pubkey(); - pk.fromDER(new Buffer('031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); + var pk = Pubkey.fromDER(new Buffer('031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); }); it('should throw an error on this invalid public key', function() { - var pk = new Pubkey(); (function() { - pk.fromDER(new Buffer('091ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); + Pubkey.fromDER(new Buffer('091ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); }).should.throw(); }); @@ -117,8 +192,7 @@ describe('Pubkey', function() { describe('#fromString', function() { it('should parse this known valid public key', function() { - var pk = new Pubkey(); - pk.fromString('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); + var pk = Pubkey.fromString('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); }); @@ -129,20 +203,26 @@ describe('Pubkey', function() { it('should create this known public key', function() { var x = BN.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); - var pk = new Pubkey(); - pk.fromX(true, x); + var pk = Pubkey.fromX(true, x); pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); }); + + it('should error because odd was not included as a param', function() { + var x = BN.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); + (function() { + var pk = Pubkey.fromX(null, x); + }).should.throw('Must specify whether y is odd or not (true or false)'); + }); + }); describe('#toBuffer', function() { it('should return this compressed DER format', function() { var x = BN.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); - var pk = new Pubkey(); - pk.fromX(true, x); + var pk = Pubkey.fromX(true, x); pk.toBuffer().toString('hex').should.equal('031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); }); @@ -152,54 +232,66 @@ describe('Pubkey', function() { it('should return this compressed DER format', function() { var x = BN.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); - var pk = new Pubkey(); - pk.fromX(true, x); + var pk = Pubkey.fromX(true, x); pk.toDER(true).toString('hex').should.equal('031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); }); it('should return this uncompressed DER format', function() { var x = BN.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); - var pk = new Pubkey(); - pk.fromX(true, x); + var pk = Pubkey.fromX(true, x); pk.toDER(false).toString('hex').should.equal('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); }); + it('should error because compressed param is invalid', function() { + var x = BN.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); + var pk = Pubkey.fromX(true, x); + (function() { + pk.toDER('false'); //string not boolean + }).should.throw('Must specify whether the public key is compressed or not (true or false)'); + }); + }); describe('#toString', function() { it('should print this known public key', function() { var hex = '031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'; - var pk = new Pubkey(); - pk.fromString(hex); + var pk = Pubkey.fromString(hex); pk.toString().should.equal(hex); }); }); + describe('#inspect', function() { + it('should output known uncompressed pubkey for console', function() { + var pubkey = Pubkey.fromString('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); + pubkey.inspect().should.equal(''); + }); + + it('should output known compressed pubkey for console', function() { + var pubkey = Pubkey.fromString('031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); + pubkey.inspect().should.equal(''); + }); + + }); + describe('#validate', function() { - it('should not throw an error if pubkey is valid', function() { + it('should not have an error if pubkey is valid', function() { var hex = '031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'; - var pk = new Pubkey(); - pk.fromString(hex); - should.exist(pk.validate()); + var pk = Pubkey.fromString(hex); }); - it('should not throw an error if pubkey is invalid', function() { + it('should throw an error if pubkey is invalid', function() { var hex = '041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a0000000000000000000000000000000000000000000000000000000000000000'; - var pk = new Pubkey(); - pk.fromString(hex); (function() { - pk.validate(); + var pk = Pubkey.fromString(hex); }).should.throw('Invalid y value of public key'); }); - it('should not throw an error if pubkey is infinity', function() { - var pk = new Pubkey(); - pk.point = Point.getG().mul(Point.getN()); + it('should throw an error if pubkey is infinity', function() { (function() { - pk.validate(); + var pk = new Pubkey(Point.getG().mul(Point.getN())); }).should.throw('Point cannot be equal to Infinity'); });