Browse Source

Keys: Modified interface of Pubkey and Privkey so that an instance can be relied upon as valid.

patch-2
Braydon Fuller 10 years ago
parent
commit
35d0cbc5a6
  1. 29
      lib/bip32.js
  2. 341
      lib/privkey.js
  3. 368
      lib/pubkey.js
  4. 21
      test/address.js
  5. 201
      test/privkey.js
  6. 202
      test/pubkey.js

29
lib/bip32.js

@ -41,8 +41,8 @@ BIP32.prototype.fromRandom = function(networkstr) {
this.parentfingerprint = new Buffer([0, 0, 0, 0]); this.parentfingerprint = new Buffer([0, 0, 0, 0]);
this.childindex = new Buffer([0, 0, 0, 0]); this.childindex = new Buffer([0, 0, 0, 0]);
this.chaincode = Random.getRandomBuffer(32); this.chaincode = Random.getRandomBuffer(32);
this.privkey = Privkey().fromRandom(); this.privkey = Privkey.fromRandom();
this.pubkey = new Pubkey().fromPrivkey(this.privkey); this.pubkey = Pubkey.fromPrivkey(this.privkey);
this.hasprivkey = true; this.hasprivkey = true;
this.pubkeyhash = Hash.sha256ripemd160(this.pubkey.toBuffer()); this.pubkeyhash = Hash.sha256ripemd160(this.pubkey.toBuffer());
this.buildxpubkey(); this.buildxpubkey();
@ -72,8 +72,8 @@ BIP32.prototype.fromSeed = function(bytes, networkstr) {
this.childindex = new Buffer([0, 0, 0, 0]); this.childindex = new Buffer([0, 0, 0, 0]);
this.chaincode = hash.slice(32, 64); this.chaincode = hash.slice(32, 64);
this.version = networks[networkstr].bip32privkey; this.version = networks[networkstr].bip32privkey;
this.privkey = new Privkey({bn: BN().fromBuffer(hash.slice(0, 32))}); this.privkey = new Privkey(BN().fromBuffer(hash.slice(0, 32)));
this.pubkey = new Pubkey().fromPrivkey(this.privkey); this.pubkey = Pubkey.fromPrivkey(this.privkey);
this.hasprivkey = true; this.hasprivkey = true;
this.pubkeyhash = Hash.sha256ripemd160(this.pubkey.toBuffer()); this.pubkeyhash = Hash.sha256ripemd160(this.pubkey.toBuffer());
@ -105,12 +105,12 @@ BIP32.prototype.initFromBytes = function(bytes) {
this.version == networks.testnet.bip32pubkey); this.version == networks.testnet.bip32pubkey);
if (isPrivate && keyBytes[0] == 0) { if (isPrivate && keyBytes[0] == 0) {
this.privkey = new Privkey({bn: BN().fromBuffer(keyBytes.slice(1, 33))}); this.privkey = new Privkey(BN().fromBuffer(keyBytes.slice(1, 33)));
this.pubkey = new Pubkey().fromPrivkey(this.privkey); this.pubkey = Pubkey.fromPrivkey(this.privkey);
this.pubkeyhash = Hash.sha256ripemd160(this.pubkey.toBuffer()); this.pubkeyhash = Hash.sha256ripemd160(this.pubkey.toBuffer());
this.hasprivkey = true; this.hasprivkey = true;
} else if (isPublic && (keyBytes[0] == 0x02 || keyBytes[0] == 0x03)) { } 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.pubkeyhash = Hash.sha256ripemd160(this.pubkey.toBuffer());
this.hasprivkey = false; this.hasprivkey = false;
} else { } else {
@ -119,7 +119,7 @@ BIP32.prototype.initFromBytes = function(bytes) {
this.buildxpubkey(); this.buildxpubkey();
this.buildxprivkey(); this.buildxprivkey();
} };
BIP32.prototype.buildxpubkey = function() { BIP32.prototype.buildxpubkey = function() {
this.xpubkey = new Buffer([]); this.xpubkey = new Buffer([]);
@ -153,7 +153,7 @@ BIP32.prototype.buildxpubkey = function() {
this.chaincode, this.chaincode,
this.pubkey.toBuffer() this.pubkey.toBuffer()
]); ]);
} };
BIP32.prototype.xpubkeyString = function(format) { BIP32.prototype.xpubkeyString = function(format) {
if (format === undefined || format === 'base58') { if (format === undefined || format === 'base58') {
@ -228,7 +228,7 @@ BIP32.prototype.derive = function(path) {
} }
return bip32; return bip32;
} };
BIP32.prototype.deriveChild = function(i) { BIP32.prototype.deriveChild = function(i) {
if (typeof i !== 'number') if (typeof i !== 'number')
@ -270,8 +270,8 @@ BIP32.prototype.deriveChild = function(i) {
ret = new BIP32(); ret = new BIP32();
ret.chaincode = ir; ret.chaincode = ir;
ret.privkey = new Privkey({bn: k}); ret.privkey = new Privkey(k);
ret.pubkey = new Pubkey().fromPrivkey(ret.privkey); ret.pubkey = Pubkey.fromPrivkey(ret.privkey);
ret.hasprivkey = true; ret.hasprivkey = true;
} else { } else {
@ -284,8 +284,7 @@ BIP32.prototype.deriveChild = function(i) {
var ilG = Point.getG().mul(il); var ilG = Point.getG().mul(il);
var Kpar = this.pubkey.point; var Kpar = this.pubkey.point;
var Ki = ilG.add(Kpar); var Ki = ilG.add(Kpar);
var newpub = new Pubkey(); var newpub = Pubkey.fromPoint(Ki);
newpub.point = Ki;
ret = new BIP32(); ret = new BIP32();
ret.chaincode = ir; ret.chaincode = ir;
@ -305,7 +304,7 @@ BIP32.prototype.deriveChild = function(i) {
ret.buildxprivkey(); ret.buildxprivkey();
return ret; return ret;
} };
BIP32.prototype.toString = function() { BIP32.prototype.toString = function() {
var isPrivate = var isPrivate =

341
lib/privkey.js

@ -5,107 +5,312 @@ var Point = require('./crypto/point');
var Random = require('./crypto/random'); var Random = require('./crypto/random');
var networks = require('./networks'); var networks = require('./networks');
var base58check = require('./encoding/base58check'); var base58check = require('./encoding/base58check');
var Address = require('./address');
var Pubkey = require('./pubkey'); var Pubkey = require('./pubkey');
var Privkey = function Privkey(bn) { /**
if (!(this instanceof Privkey)) *
return new Privkey(bn); * Bitcore Privkey
if (bn instanceof BN) *
this.bn = bn; * Instantiate a Privkey from a BN, Buffer and WIF.
else if (bn) { *
var obj = bn; * @example
this.set(obj); *
* 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) { var info = {
this.bn = obj.bn || this.bn; compressed: typeof(compressed) !== 'undefined' ? compressed : true,
this.networkstr = obj.networkstr || this.networkstr; network: network || 'mainnet'
this.compressed = typeof obj.compressed !== 'undefined' ? obj.compressed : this.compressed; };
return this;
}; // 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;
Privkey.prototype.fromJSON = function(json) {
this.fromString(json);
return this; 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 { do {
var privbuf = Random.getRandomBuffer(32); var privbuf = Random.getRandomBuffer(32);
var bn = BN().fromBuffer(privbuf); bn = BN().fromBuffer(privbuf);
var condition = bn.lt(Point.getN()); condition = bn.lt(Point.getN());
} while (!condition); } while (!condition);
this.set({ return bn;
bn: bn, };
networkstr: 'mainnet',
compressed: true /**
}); *
return this; * 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);
}; };
Privkey.prototype.validate = function() { /**
if (!this.bn.lt(Point.getN())) *
throw new Error('Number must be less than N'); * Instantiate a Privkey from a WIF string
if (typeof networks[this.networkstr] === undefined) *
throw new Error('Must specify the networkstr ("mainnet" or "testnet")'); * @param {String} str - The WIF encoded private key string
if (typeof this.compressed !== 'boolean') * @returns {Privkey} A new valid instance of Privkey
throw new Error('Must specify whether the corresponding public key is compressed or not (true or false)'); */
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() { Privkey.prototype.toWIF = function() {
var networkstr = this.networkstr; var network = this.network;
var compressed = this.compressed; 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; var buf;
if (compressed) if (compressed) {
buf = Buffer.concat([new Buffer([networks[networkstr].privkey]), this.bn.toBuffer({size: 32}), new Buffer([0x01])]); buf = Buffer.concat([new Buffer([networks[network].privkey]),
else this.bn.toBuffer({size: 32}),
buf = Buffer.concat([new Buffer([networks[networkstr].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); return base58check.encode(buf);
}; };
Privkey.prototype.fromWIF = function(str) { /**
var buf = base58check.decode(str); *
* 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;
};
if (buf.length === 1 + 32 + 1 && buf[1 + 32 + 1 - 1] == 1) /**
this.compressed = true; *
else if (buf.length === 1 + 32) * Will return the private key as a BN buffer
this.compressed = false; *
else * @returns {Buffer} A buffer of the private key
throw new Error('Length of buffer must be 33 (uncompressed) or 34 (compressed)'); */
Privkey.prototype.toBuffer = function(){
return this.bn.toBuffer();
};
if (buf[0] === networks.mainnet.privkey) /**
this.networkstr = 'mainnet'; *
else if (buf[0] === networks.testnet.privkey) * Will return the corresponding public key
this.networkstr = 'testnet'; *
else * @returns {Pubkey} A public key generated from the private key
throw new Error('Invalid networkstr'); */
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);
};
this.bn = BN.fromBuffer(buf.slice(1, 32 + 1)); /**
*
* 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() { Privkey.prototype.toString = function() {
return this.toWIF(); return this.toWIF();
}; };
Privkey.prototype.toPubkey = function() { /**
return new Pubkey().fromPrivkey(this); *
} * Will return a string formatted for the console
*
Privkey.prototype.fromString = function(str) { * @returns {String} Private key
this.fromWIF(str); */
Privkey.prototype.inspect = function() {
return '<Privkey: ' + this.toString() + ', compressed: '+this.compressed+', network: '+this.network+'>';
}; };
module.exports = Privkey; module.exports = Privkey;

368
lib/pubkey.js

@ -1,94 +1,300 @@
'use strict'; 'use strict';
var Point = require('./crypto/point'); 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); * Bitcore Pubkey
if (point instanceof Point) *
this.point = point; * Instantiate a Pubkey from a 'Privkey', 'Point', 'string', 'Buffer'.
else if (point) { *
var obj = point; * @example
this.set(obj); *
* 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 (!data) {
if (obj.point && !obj.point.getX() && !obj.point.getY()) throw new TypeError('First argument is required, please include public key data.');
throw new Error('Invalid point'); }
this.point = obj.point || this.point;
this.compressed = typeof obj.compressed !== 'undefined' ? obj.compressed : this.compressed; var info = {
return this; 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;
Pubkey.prototype.fromJSON = function(json) {
this.fromBuffer(new Buffer(json, 'hex'));
return this; return this;
};
Pubkey.prototype.toJSON = function() {
return this.toBuffer().toString('hex');
}; };
Pubkey.prototype.fromPrivkey = function(privkey) { /**
this.set({ *
point: Point.getG().mul(privkey.bn), * Internal function to transform a private key into a public key point
compressed: privkey.compressed} *
); * @param {Privkey} privkey - An instance of Privkey
return this; * @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;
}; };
Pubkey.prototype.fromBuffer = function(buf) { /**
return this.fromDER(buf); *
}; * Internal function to transform DER into a public key point
*
Pubkey.prototype.fromDER = function(buf) { * @param {Buffer} buf - An hex encoded buffer
if (buf[0] == 0x04) { * @returns {Object} An object with keys: point and compressed
var xbuf = buf.slice(1, 33); */
var ybuf = buf.slice(33, 65); Pubkey._transformDER = function(buf){
if (xbuf.length !== 32 || ybuf.length !== 32 || buf.length !== 65) var info = {};
throw new Error('Length of x and y must be 32 bytes'); if (!(buf instanceof Buffer)){
var x = bn(xbuf); throw new TypeError('Must be a hex buffer of DER encoded public key');
var y = bn(ybuf); }
this.point = Point(x, y);
this.compressed = false; var x;
} else if (buf[0] == 0x03) { var y;
var xbuf = buf.slice(1); var xbuf;
var x = bn(xbuf); var ybuf;
this.fromX(true, x);
this.compressed = true; 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) { } else if (buf[0] == 0x02) {
var xbuf = buf.slice(1); xbuf = buf.slice(1);
var x = bn(xbuf); x = BN(xbuf);
this.fromX(false, x); info = Pubkey._transformX(false, x);
this.compressed = true; info.compressed = true;
} else { } else {
throw new Error('Invalid DER format pubkey'); throw new TypeError('Invalid DER format pubkey');
} }
return this; 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);
}; };
Pubkey.prototype.fromString = function( str , encoding ) { /**
var encoding = encoding || 'hex'; *
this.fromDER( new Buffer(str, encoding ) ); * 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);
}; };
Pubkey.prototype.fromX = function(odd, x) { /**
if (typeof odd !== 'boolean') *
throw new Error('Must specify whether y is odd or not (true or false)'); * Instantiate a Pubkey from an X Point
this.point = Point.fromX(odd, x); *
* @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');
};
/**
*
* Will output the Pubkey to a Buffer
*
* @returns {Buffer} A DER hex encoded buffer
*/
Pubkey.prototype.toBuffer = function() { Pubkey.prototype.toBuffer = function() {
var compressed = typeof this.compressed === 'undefined' ? true : this.compressed; var compressed = typeof this.compressed === 'undefined' ? true : this.compressed;
return this.toDER(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) { Pubkey.prototype.toDER = function(compressed) {
compressed = typeof this.compressed === 'undefined' ? compressed : this.compressed; compressed = typeof(compressed) !== 'undefined' ? compressed : this.compressed;
if (typeof compressed !== 'boolean') if (typeof compressed !== 'boolean') {
throw new Error('Must specify whether the public key is compressed or not (true or false)'); throw new TypeError('Must specify whether the public key is compressed or not (true or false)');
}
var x = this.point.getX(); var x = this.point.getX();
var y = this.point.getY(); var y = this.point.getY();
@ -96,32 +302,40 @@ Pubkey.prototype.toDER = function(compressed) {
var xbuf = x.toBuffer({size: 32}); var xbuf = x.toBuffer({size: 32});
var ybuf = y.toBuffer({size: 32}); var ybuf = y.toBuffer({size: 32});
var prefix;
if (!compressed) { if (!compressed) {
var prefix = new Buffer([0x04]); prefix = new Buffer([0x04]);
return Buffer.concat([prefix, xbuf, ybuf]); return Buffer.concat([prefix, xbuf, ybuf]);
} else { } else {
var odd = ybuf[ybuf.length - 1] % 2; var odd = ybuf[ybuf.length - 1] % 2;
if (odd) if (odd) {
var prefix = new Buffer([0x03]); prefix = new Buffer([0x03]);
else } else {
var prefix = new Buffer([0x02]); prefix = new Buffer([0x02]);
}
return Buffer.concat([prefix, xbuf]); 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() { Pubkey.prototype.toString = function() {
var compressed = typeof this.compressed === 'undefined' ? true : this.compressed; var compressed = typeof this.compressed === 'undefined' ? true : this.compressed;
return this.toDER(compressed).toString('hex'); return this.toDER(compressed).toString('hex');
}; };
//https://www.iacr.org/archive/pkc2003/25670211/25670211.pdf /**
Pubkey.prototype.validate = function() { *
if (this.point.isInfinity()) * Will return a string formatted for the console
throw new Error('point: Point cannot be equal to Infinity'); *
if (this.point.eq(Point(bn(0), bn(0)))) * @returns {String} Public key
throw new Error('point: Point cannot be equal to 0, 0'); */
this.point.validate(); Pubkey.prototype.inspect = function() {
return this; return '<Pubkey: ' + this.toString() + ', compressed: '+this.compressed+'>';
}; };
module.exports = Pubkey; module.exports = Pubkey;

21
test/address.js

@ -266,22 +266,15 @@ describe('Address', function() {
}).should.throw('Address hashbuffers must be exactly 20 bytes.'); }).should.throw('Address hashbuffers must be exactly 20 bytes.');
}); });
it('should make this address from a compressed pubkey object', function() { it('should make this address from a compressed pubkey', function() {
var pubkey = new Pubkey(); var pubkey = Pubkey.fromDER(new Buffer('0285e9737a74c30a873f74df05124f2aa6f53042c2fc0a130d6cbd7d16b944b004', 'hex'));
pubkey.fromDER(new Buffer('0285e9737a74c30a873f74df05124f2aa6f53042c2fc0a130d6cbd7d16b944b004', var address = Address.fromPubkey(pubkey);
'hex')); address.toString().should.equal('19gH5uhqY6DKrtkU66PsZPUZdzTd11Y7ke');
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 an uncompressed pubkey', function() { it('should make this address from an uncompressed pubkey', function() {
var pubkey = new Pubkey(); var pubkey = Pubkey.fromDER(new Buffer('0485e9737a74c30a873f74df05124f2aa6f53042c2fc0a130d6cbd7d16b944b004833fef26c8be4c4823754869ff4e46755b85d851077771c220e2610496a29d98', 'hex'));
pubkey.fromDER(new Buffer('0285e9737a74c30a873f74df05124f2aa6f53042c2fc0a130d6cbd7d16b944b004', var a = Address.fromPubkey(pubkey, 'mainnet');
'hex'));
pubkey.compressed = false;
var a = Address.fromPubkey(pubkey, 'mainnet', 'pubkeyhash');
a.toString().should.equal('16JXnhxjJUhxfyx4y6H4sFcxrgt8kQ8ewX'); a.toString().should.equal('16JXnhxjJUhxfyx4y6H4sFcxrgt8kQ8ewX');
var b = new Address(pubkey, 'mainnet', 'pubkeyhash'); var b = new Address(pubkey, 'mainnet', 'pubkeyhash');
b.toString().should.equal('16JXnhxjJUhxfyx4y6H4sFcxrgt8kQ8ewX'); b.toString().should.equal('16JXnhxjJUhxfyx4y6H4sFcxrgt8kQ8ewX');
@ -310,7 +303,7 @@ describe('Address', function() {
var address = new Address(str); var address = new Address(str);
var buffer = address.toBuffer(); var buffer = address.toBuffer();
var slice = buffer.slice(1); var slice = buffer.slice(1);
var sliceString = slice.toString('hex') var sliceString = slice.toString('hex');
sliceString.should.equal(pubkeyhash.toString('hex')); sliceString.should.equal(pubkeyhash.toString('hex'));
}); });

201
test/privkey.js

@ -5,7 +5,7 @@ var bitcore = require('..');
var BN = bitcore.crypto.BN; var BN = bitcore.crypto.BN;
var Point = bitcore.crypto.Point; var Point = bitcore.crypto.Point;
var Privkey = bitcore.Privkey; var Privkey = bitcore.Privkey;
var Pubkey = bitcore.Pubkey; var base58check = bitcore.encoding.Base58Check;
describe('Privkey', function() { describe('Privkey', function() {
var hex = '96c132224121b509b7d0a16245e957d9192609c5637c6228311287b1be21627a'; var hex = '96c132224121b509b7d0a16245e957d9192609c5637c6228311287b1be21627a';
@ -15,9 +15,78 @@ describe('Privkey', function() {
var encmainnet = 'L2Gkw3kKJ6N24QcDuH4XDqt9cTqsKTVNDGz1CRZhk9cq4auDUbJy'; var encmainnet = 'L2Gkw3kKJ6N24QcDuH4XDqt9cTqsKTVNDGz1CRZhk9cq4auDUbJy';
var encmu = '5JxgQaFM1FMd38cd14e3mbdxsdSa9iM2BV6DHBYsvGzxkTNQ7Un'; var encmu = '5JxgQaFM1FMd38cd14e3mbdxsdSa9iM2BV6DHBYsvGzxkTNQ7Un';
it('should create an empty private key', function() { it('should create a new random private key', function() {
var privkey = new Privkey(); var a = new Privkey();
should.exist(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() { 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() { it('should create a mainnet private key', function() {
var privkey = new Privkey({ var privkey = new Privkey(BN.fromBuffer(buf), 'mainnet', true);
bn: BN.fromBuffer(buf),
networkstr: 'mainnet',
compressed: true
});
privkey.toString().should.equal(encmainnet); privkey.toString().should.equal(encmainnet);
}); });
it('should create an uncompressed testnet private key', function() { it('should create an uncompressed testnet private key', function() {
var privkey = new Privkey({ var privkey = new Privkey(BN.fromBuffer(buf), 'testnet', false);
bn: BN.fromBuffer(buf),
networkstr: 'testnet',
compressed: false
});
privkey.toString().should.equal(enctu); privkey.toString().should.equal(enctu);
}); });
it('should create an uncompressed mainnet private key', function() { it('should create an uncompressed mainnet private key', function() {
var privkey = new Privkey({ var privkey = new Privkey(BN.fromBuffer(buf), 'mainnet', false);
bn: BN.fromBuffer(buf),
networkstr: 'mainnet',
compressed: false
});
privkey.toString().should.equal(encmu); 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() { describe('#fromJSON', function() {
it('should input this address correctly', function() { it('should input this address correctly', function() {
var privkey = new Privkey(); var privkey = Privkey.fromJSON(encmu);
privkey.fromJSON(encmu);
privkey.toWIF().should.equal(encmu); privkey.toWIF().should.equal(encmu);
}); });
@ -76,17 +122,80 @@ describe('Privkey', function() {
describe('#toString', function() { describe('#toString', function() {
it('should output this address correctly', function() { it('should output this address correctly', function() {
var privkey = new Privkey(); var privkey = Privkey.fromJSON(encmu);
privkey.fromJSON(encmu);
privkey.toJSON().should.equal(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('<Privkey: L3T1s1TYP9oyhHpXgkyLoJFGniEgkv2Jhi138d7R2yJ9F4QdDU2m, compressed: true, network: mainnet>');
});
it('should output known testnet address for console', function() {
var privkey = Privkey.fromWIF('cR4qogdN9UxLZJXCNFNwDRRZNeLRWuds9TTSuLNweFVjiaE4gPaq');
privkey.inspect().should.equal('<Privkey: cR4qogdN9UxLZJXCNFNwDRRZNeLRWuds9TTSuLNweFVjiaE4gPaq, compressed: true, network: testnet>');
});
});
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() { describe('#fromRandom', function() {
it('should set bn gt 0 and lt n, and should be compressed', 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.gt(BN(0)).should.equal(true);
privkey.bn.lt(Point.getN()).should.equal(true); privkey.bn.lt(Point.getN()).should.equal(true);
privkey.compressed.should.equal(true); privkey.compressed.should.equal(true);
@ -97,8 +206,7 @@ describe('Privkey', function() {
describe('#fromWIF', function() { describe('#fromWIF', function() {
it('should parse this compressed testnet address correctly', function() { it('should parse this compressed testnet address correctly', function() {
var privkey = new Privkey(); var privkey = Privkey.fromWIF(encmainnet);
privkey.fromWIF(encmainnet);
privkey.toWIF().should.equal(encmainnet); privkey.toWIF().should.equal(encmainnet);
}); });
@ -107,8 +215,7 @@ describe('Privkey', function() {
describe('#toWIF', function() { describe('#toWIF', function() {
it('should parse this compressed testnet address correctly', function() { it('should parse this compressed testnet address correctly', function() {
var privkey = new Privkey(); var privkey = Privkey.fromWIF(enctestnet);
privkey.fromWIF(enctestnet);
privkey.toWIF().should.equal(enctestnet); privkey.toWIF().should.equal(enctestnet);
}); });
@ -117,8 +224,7 @@ describe('Privkey', function() {
describe('#fromString', function() { describe('#fromString', function() {
it('should parse this uncompressed testnet address correctly', function() { it('should parse this uncompressed testnet address correctly', function() {
var privkey = new Privkey(); var privkey = Privkey.fromString(enctu);
privkey.fromString(enctu);
privkey.toWIF().should.equal(enctu); privkey.toWIF().should.equal(enctu);
}); });
@ -127,8 +233,7 @@ describe('Privkey', function() {
describe('#toString', function() { describe('#toString', function() {
it('should parse this uncompressed mainnet address correctly', function() { it('should parse this uncompressed mainnet address correctly', function() {
var privkey = new Privkey(); var privkey = Privkey.fromString(encmu);
privkey.fromString(encmu);
privkey.toString().should.equal(encmu); privkey.toString().should.equal(encmu);
}); });
@ -139,18 +244,14 @@ describe('Privkey', function() {
it('should convert this known Privkey to known Pubkey', function() { it('should convert this known Privkey to known Pubkey', function() {
var privhex = '906977a061af29276e40bf377042ffbde414e496ae2260bbf1fa9d085637bfff'; var privhex = '906977a061af29276e40bf377042ffbde414e496ae2260bbf1fa9d085637bfff';
var pubhex = '02a1633cafcc01ebfb6d78e39f687a1f0995c62fc95f51ead10a02ee0be551b5dc'; var pubhex = '02a1633cafcc01ebfb6d78e39f687a1f0995c62fc95f51ead10a02ee0be551b5dc';
var privkey = new Privkey({ var privkey = new Privkey(BN(new Buffer(privhex, 'hex')));
bn: BN(new Buffer(privhex, 'hex'))
});
var pubkey = privkey.toPubkey(); var pubkey = privkey.toPubkey();
pubkey.toString().should.equal(pubhex); pubkey.toString().should.equal(pubhex);
}); });
it('should convert this known Privkey to known Pubkey and preserve compressed=true', function() { it('should convert this known Privkey to known Pubkey and preserve compressed=true', function() {
var privhex = '906977a061af29276e40bf377042ffbde414e496ae2260bbf1fa9d085637bfff'; var privhex = '906977a061af29276e40bf377042ffbde414e496ae2260bbf1fa9d085637bfff';
var privkey = new Privkey({ var privkey = new Privkey(BN(new Buffer(privhex, 'hex')));
bn: BN(new Buffer(privhex, 'hex'))
});
privkey.compressed = true; privkey.compressed = true;
var pubkey = privkey.toPubkey(); var pubkey = privkey.toPubkey();
pubkey.compressed.should.equal(true); 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() { it('should convert this known Privkey to known Pubkey and preserve compressed=true', function() {
var privhex = '906977a061af29276e40bf377042ffbde414e496ae2260bbf1fa9d085637bfff'; var privhex = '906977a061af29276e40bf377042ffbde414e496ae2260bbf1fa9d085637bfff';
var privkey = new Privkey({ var privkey = new Privkey(BN(new Buffer(privhex, 'hex')));
bn: BN(new Buffer(privhex, 'hex'))
});
privkey.compressed = false; privkey.compressed = false;
var pubkey = privkey.toPubkey(); var pubkey = privkey.toPubkey();
pubkey.compressed.should.equal(false); pubkey.compressed.should.equal(false);

202
test/pubkey.js

@ -8,38 +8,101 @@ var Pubkey = bitcore.Pubkey;
var Privkey = bitcore.Privkey; var Privkey = bitcore.Privkey;
describe('Pubkey', function() { 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() { it('should error because of an invalid point', function() {
var pk = new Pubkey(); (function() {
should.exist(pk); var pk = new Pubkey(Point());
}).should.throw('Point cannot be equal to 0, 0');
}); });
it('should create a public key with a point', function() { it('should error because of an invalid public key point, not on the secp256k1 curve', function() {
var p = Point(); (function() {
var pk = new Pubkey({point: p}); 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); should.exist(pk.point);
pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a');
}); });
it('should create a public key with a point with this convenient method', function() { it('should instantiate from a hex encoded DER buffer', function() {
var p = Point(); var pk = new Pubkey(new Buffer('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341', 'hex'));
var pk = new Pubkey(p);
should.exist(pk.point); should.exist(pk.point);
pk.point.toString().should.equal(p.toString()); pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a');
}); });
describe('#set', function() { it('should create a public key with a point', function() {
var p = Point('86a80a5a2bfc48dddde2b0bd88bd56b0b6ddc4e6811445b175b90268924d7d48',
it('should make a public key from a point', function() { '3b402dfc89712cfe50963e670a0598e6b152b3cd94735001cdac6794975d3afd');
should.exist(Pubkey().set({point: Point.getG()}).point); 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());
});
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() { describe('#fromJSON', function() {
it('should input this public key', function() { it('should input this public key', function() {
var pk = new Pubkey(); var pk = Pubkey.fromJSON('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341');
pk.fromJSON('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341');
pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a');
pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341');
}); });
@ -49,9 +112,9 @@ describe('Pubkey', function() {
describe('#toJSON', function() { describe('#toJSON', function() {
it('should output this pubkey', function() { it('should output this pubkey', function() {
var pk = new Pubkey();
var hex = '041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'; 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() { describe('#fromPrivkey', function() {
it('should make a public key from a privkey', 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() { describe('#fromBuffer', function() {
it('should parse this uncompressed public key', function() { it('should parse this uncompressed public key', function() {
var pk = new Pubkey(); var pk = Pubkey.fromBuffer(new Buffer('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341', 'hex'));
pk.fromBuffer(new Buffer('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341', 'hex'));
pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a');
pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341');
}); });
it('should parse this compressed public key', function() { it('should parse this compressed public key', function() {
var pk = new Pubkey(); var pk = Pubkey.fromBuffer(new Buffer('031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex'));
pk.fromBuffer(new Buffer('031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex'));
pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a');
pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341');
}); });
it('should throw an error on this invalid public key', function() { it('should throw an error on this invalid public key', function() {
var pk = new Pubkey();
(function() { (function() {
pk.fromBuffer(new Buffer('091ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); Pubkey.fromBuffer(new Buffer('091ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex'));
}).should.throw(); }).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() { describe('#fromDER', function() {
it('should parse this uncompressed public key', function() { it('should parse this uncompressed public key', function() {
var pk = new Pubkey(); var pk = Pubkey.fromDER(new Buffer('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341', 'hex'));
pk.fromDER(new Buffer('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341', 'hex'));
pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a');
pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341');
}); });
it('should parse this compressed public key', function() { it('should parse this compressed public key', function() {
var pk = new Pubkey(); var pk = Pubkey.fromDER(new Buffer('031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex'));
pk.fromDER(new Buffer('031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex'));
pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a');
pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341');
}); });
it('should throw an error on this invalid public key', function() { it('should throw an error on this invalid public key', function() {
var pk = new Pubkey();
(function() { (function() {
pk.fromDER(new Buffer('091ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); Pubkey.fromDER(new Buffer('091ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex'));
}).should.throw(); }).should.throw();
}); });
@ -117,8 +192,7 @@ describe('Pubkey', function() {
describe('#fromString', function() { describe('#fromString', function() {
it('should parse this known valid public key', function() { it('should parse this known valid public key', function() {
var pk = new Pubkey(); var pk = Pubkey.fromString('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341');
pk.fromString('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341');
pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a');
pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341');
}); });
@ -129,20 +203,26 @@ describe('Pubkey', function() {
it('should create this known public key', function() { it('should create this known public key', function() {
var x = BN.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); var x = BN.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex'));
var pk = new Pubkey(); var pk = Pubkey.fromX(true, x);
pk.fromX(true, x);
pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a');
pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); 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() { describe('#toBuffer', function() {
it('should return this compressed DER format', function() { it('should return this compressed DER format', function() {
var x = BN.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); var x = BN.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex'));
var pk = new Pubkey(); var pk = Pubkey.fromX(true, x);
pk.fromX(true, x);
pk.toBuffer().toString('hex').should.equal('031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); pk.toBuffer().toString('hex').should.equal('031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a');
}); });
@ -152,54 +232,66 @@ describe('Pubkey', function() {
it('should return this compressed DER format', function() { it('should return this compressed DER format', function() {
var x = BN.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); var x = BN.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex'));
var pk = new Pubkey(); var pk = Pubkey.fromX(true, x);
pk.fromX(true, x);
pk.toDER(true).toString('hex').should.equal('031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); pk.toDER(true).toString('hex').should.equal('031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a');
}); });
it('should return this uncompressed DER format', function() { it('should return this uncompressed DER format', function() {
var x = BN.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); var x = BN.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex'));
var pk = new Pubkey(); var pk = Pubkey.fromX(true, x);
pk.fromX(true, x);
pk.toDER(false).toString('hex').should.equal('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); 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() { describe('#toString', function() {
it('should print this known public key', function() { it('should print this known public key', function() {
var hex = '031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'; var hex = '031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a';
var pk = new Pubkey(); var pk = Pubkey.fromString(hex);
pk.fromString(hex);
pk.toString().should.equal(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('<Pubkey: 041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341, compressed: false>');
});
it('should output known compressed pubkey for console', function() {
var pubkey = Pubkey.fromString('031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a');
pubkey.inspect().should.equal('<Pubkey: 031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a, compressed: true>');
});
});
describe('#validate', function() { 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 hex = '031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a';
var pk = new Pubkey(); var pk = Pubkey.fromString(hex);
pk.fromString(hex);
should.exist(pk.validate());
}); });
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 hex = '041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a0000000000000000000000000000000000000000000000000000000000000000';
var pk = new Pubkey();
pk.fromString(hex);
(function() { (function() {
pk.validate(); var pk = Pubkey.fromString(hex);
}).should.throw('Invalid y value of public key'); }).should.throw('Invalid y value of public key');
}); });
it('should not throw an error if pubkey is infinity', function() { it('should throw an error if pubkey is infinity', function() {
var pk = new Pubkey();
pk.point = Point.getG().mul(Point.getN());
(function() { (function() {
pk.validate(); var pk = new Pubkey(Point.getG().mul(Point.getN()));
}).should.throw('Point cannot be equal to Infinity'); }).should.throw('Point cannot be equal to Infinity');
}); });

Loading…
Cancel
Save