2014-11-03 10:44:03 -08:00
|
|
|
var elliptic = require('elliptic');
|
|
|
|
var ecdsa = new elliptic.ec(elliptic.curves.secp256k1);
|
|
|
|
var hashjs = require('hash.js');
|
|
|
|
var bs58 = require('bs58');
|
2014-11-04 07:53:37 -08:00
|
|
|
var BitAuth = {};
|
2014-05-27 09:04:57 -07:00
|
|
|
|
2014-10-28 13:19:02 -07:00
|
|
|
/**
|
|
|
|
* Will return a key pair and identity
|
|
|
|
*
|
|
|
|
* @returns {Object} An object with keys: created, priv, pub and sin
|
|
|
|
*/
|
2014-05-27 09:04:57 -07:00
|
|
|
BitAuth.generateSin = function() {
|
2014-10-28 13:19:02 -07:00
|
|
|
|
|
|
|
var keys = ecdsa.genKeyPair();
|
|
|
|
|
2014-11-02 13:14:23 -08:00
|
|
|
var privateKey = keys.getPrivate('hex');
|
2014-10-29 16:09:53 -07:00
|
|
|
var publicKey = this.getPublicKeyFromPrivateKey(privateKey);
|
2014-10-28 13:19:02 -07:00
|
|
|
var sin = this.getSinFromPublicKey(publicKey);
|
|
|
|
|
|
|
|
var sinObj = {
|
2014-11-04 07:53:37 -08:00
|
|
|
created: Math.round(Date.now() / 1000),
|
2014-10-29 16:09:53 -07:00
|
|
|
priv: privateKey,
|
2014-10-28 13:19:02 -07:00
|
|
|
pub: publicKey,
|
|
|
|
sin: sin
|
2014-11-03 10:44:03 -08:00
|
|
|
};
|
2014-10-28 13:19:02 -07:00
|
|
|
|
|
|
|
return sinObj;
|
2014-05-27 09:04:57 -07:00
|
|
|
};
|
|
|
|
|
2014-10-28 13:19:02 -07:00
|
|
|
/**
|
2014-11-04 07:53:37 -08:00
|
|
|
* Will return a public key from a private key
|
2014-10-28 13:19:02 -07:00
|
|
|
*
|
|
|
|
* @param {String} A private key in hex
|
|
|
|
* @returns {String} A compressed public key in hex
|
|
|
|
*/
|
2014-05-27 09:04:57 -07:00
|
|
|
BitAuth.getPublicKeyFromPrivateKey = function(privkey) {
|
2014-06-25 10:56:24 -07:00
|
|
|
|
2014-10-28 13:19:02 -07:00
|
|
|
var keys = ecdsa.keyPair(privkey, 'hex');
|
2014-06-25 10:56:24 -07:00
|
|
|
|
2014-10-28 13:19:02 -07:00
|
|
|
// compressed public key
|
|
|
|
var pubKey = keys.getPublic();
|
2014-11-03 09:05:33 -08:00
|
|
|
var xbuf = new Buffer(pubKey.x.toString('hex', 64), 'hex');
|
|
|
|
var ybuf = new Buffer(pubKey.y.toString('hex', 64), 'hex');
|
2014-11-03 10:44:03 -08:00
|
|
|
var pub;
|
2014-10-29 16:09:53 -07:00
|
|
|
|
2014-10-28 13:19:02 -07:00
|
|
|
if (ybuf[ybuf.length-1] % 2) { //odd
|
2014-11-03 10:44:03 -08:00
|
|
|
pub = Buffer.concat([new Buffer([3]), xbuf]);
|
|
|
|
} else { //even
|
|
|
|
pub = Buffer.concat([new Buffer([2]), xbuf]);
|
2014-10-28 13:19:02 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
var hexPubKey = pub.toString('hex');
|
|
|
|
|
|
|
|
return hexPubKey;
|
|
|
|
|
2014-05-27 09:04:57 -07:00
|
|
|
};
|
|
|
|
|
2014-10-28 13:19:02 -07:00
|
|
|
/**
|
2014-11-03 10:44:03 -08:00
|
|
|
* Will return a SIN from a compressed public key
|
2014-10-28 13:19:02 -07:00
|
|
|
*
|
|
|
|
* @param {String} A public key in hex
|
|
|
|
* @returns {String} A SIN identity
|
|
|
|
*/
|
2014-05-27 09:04:57 -07:00
|
|
|
BitAuth.getSinFromPublicKey = function(pubkey) {
|
2014-10-28 13:19:02 -07:00
|
|
|
|
|
|
|
// sha256 hash the pubkey
|
2014-11-03 10:30:11 -08:00
|
|
|
var pubHash = (new hashjs.sha256()).update(pubkey, 'hex').digest('hex');
|
2014-10-28 13:19:02 -07:00
|
|
|
|
|
|
|
// get the ripemd160 hash of the pubkey
|
2014-11-03 10:30:11 -08:00
|
|
|
var pubRipe = (new hashjs.ripemd160()).update(pubHash, 'hex').digest('hex');
|
2014-10-28 13:19:02 -07:00
|
|
|
|
|
|
|
// add the version
|
|
|
|
var pubPrefixed = '0f02'+pubRipe;
|
|
|
|
|
|
|
|
// two rounds of hashing to generate the checksum
|
2014-11-03 10:30:11 -08:00
|
|
|
var hash1 = (new hashjs.sha256()).update(pubPrefixed, 'hex').digest('hex');
|
|
|
|
var checksumTotal = (new hashjs.sha256()).update(hash1, 'hex').digest('hex');
|
2014-10-28 13:19:02 -07:00
|
|
|
|
|
|
|
// slice the hash to arrive at the checksum
|
|
|
|
var checksum = checksumTotal.slice(0,8);
|
|
|
|
|
|
|
|
// add the checksum to the ripemd160 pubkey
|
|
|
|
var pubWithChecksum = pubPrefixed + checksum;
|
|
|
|
|
|
|
|
// encode into base58
|
|
|
|
var sin = bs58.encode(new Buffer(pubWithChecksum, 'hex'));
|
|
|
|
|
|
|
|
return sin;
|
|
|
|
|
2014-11-03 10:44:03 -08:00
|
|
|
};
|
2014-05-27 09:04:57 -07:00
|
|
|
|
2014-10-28 13:19:02 -07:00
|
|
|
/**
|
2014-11-04 07:53:37 -08:00
|
|
|
* Will sign a string of data with a private key
|
2014-10-28 13:19:02 -07:00
|
|
|
*
|
|
|
|
* @param {String} data - A string of data to be signed
|
2014-11-03 10:44:03 -08:00
|
|
|
* @param {String} privkey - A private key in hex
|
2014-10-28 13:19:02 -07:00
|
|
|
* @returns {String} signature - A DER signature in hex
|
|
|
|
*/
|
2014-05-27 09:04:57 -07:00
|
|
|
BitAuth.sign = function(data, privkey) {
|
2014-11-03 10:30:11 -08:00
|
|
|
var hash = (new hashjs.sha256()).update(data).digest('hex');
|
2014-10-28 13:19:02 -07:00
|
|
|
var signature = ecdsa.sign(hash, privkey);
|
|
|
|
var hexsignature = signature.toDER('hex');
|
|
|
|
return hexsignature;
|
2014-05-27 09:04:57 -07:00
|
|
|
};
|
|
|
|
|
2014-10-28 13:19:02 -07:00
|
|
|
/**
|
|
|
|
* Will verify a signature
|
|
|
|
*
|
|
|
|
* @param {String} data - A string of data that has been signed
|
2014-11-04 07:53:37 -08:00
|
|
|
* @param {String} pubkey - The compressed public key in hex that has signed the data
|
2014-10-28 13:19:02 -07:00
|
|
|
* @param {String} hexsignature - A DER signature in hex
|
|
|
|
* @returns {Function|Boolean} - If the signature is valid
|
|
|
|
*/
|
|
|
|
BitAuth.verifySignature = function(data, pubkey, hexsignature, callback) {
|
2014-11-03 10:30:11 -08:00
|
|
|
var hash = (new hashjs.sha256()).update(data).digest('hex');
|
2014-10-28 13:19:02 -07:00
|
|
|
var signature = new Buffer(hexsignature, 'hex');
|
|
|
|
var valid = ecdsa.verify(hash, signature, pubkey);
|
2014-11-04 07:53:37 -08:00
|
|
|
if (callback)
|
2014-10-28 13:19:02 -07:00
|
|
|
return callback(null, valid);
|
2014-11-03 10:44:03 -08:00
|
|
|
return valid;
|
2014-05-27 09:04:57 -07:00
|
|
|
};
|
|
|
|
|
2014-10-28 13:19:02 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Will verify that a SIN is valid
|
|
|
|
*
|
|
|
|
* @param {String} sin - A SIN identity
|
|
|
|
* @returns {Function|Boolean} - If the SIN identity is valid
|
|
|
|
*/
|
2014-07-16 10:46:49 -07:00
|
|
|
BitAuth.validateSin = function(sin, callback) {
|
|
|
|
|
2014-11-03 10:44:03 -08:00
|
|
|
var pubWithChecksum;
|
|
|
|
|
|
|
|
// check for non-base58 characters
|
2014-11-03 10:30:11 -08:00
|
|
|
try {
|
2014-11-03 10:44:03 -08:00
|
|
|
pubWithChecksum = new Buffer(bs58.decode(sin), 'hex').toString('hex');
|
2014-11-04 07:53:37 -08:00
|
|
|
} catch(err) {
|
|
|
|
if (callback)
|
|
|
|
return callback(err);
|
2014-11-03 10:44:03 -08:00
|
|
|
return false;
|
2014-11-03 10:30:11 -08:00
|
|
|
}
|
2014-10-28 13:19:02 -07:00
|
|
|
|
|
|
|
// check the version
|
2014-11-04 07:53:37 -08:00
|
|
|
if (pubWithChecksum.slice(0, 4) !== '0f02') {
|
|
|
|
if (callback)
|
2014-10-28 13:19:02 -07:00
|
|
|
return callback(new Error('Invalid prefix or SIN version'));
|
2014-07-16 10:46:49 -07:00
|
|
|
return false;
|
|
|
|
}
|
2014-10-28 13:19:02 -07:00
|
|
|
|
|
|
|
// get the checksum
|
2014-11-03 10:44:03 -08:00
|
|
|
var checksum = pubWithChecksum.slice(pubWithChecksum.length-8,
|
|
|
|
pubWithChecksum.length);
|
2014-10-28 13:19:02 -07:00
|
|
|
var pubPrefixed = pubWithChecksum.slice(0, pubWithChecksum.length-8);
|
|
|
|
|
|
|
|
// two rounds of hashing to generate the checksum
|
2014-11-03 10:30:11 -08:00
|
|
|
var hash1 = (new hashjs.sha256()).update(pubPrefixed, 'hex').digest('hex');
|
|
|
|
var checksumTotal = (new hashjs.sha256()).update(hash1, 'hex').digest('hex');
|
2014-10-28 13:19:02 -07:00
|
|
|
|
|
|
|
// check the checksum
|
2014-11-04 07:53:37 -08:00
|
|
|
if (checksumTotal.slice(0,8) === checksum) {
|
|
|
|
if (callback)
|
2014-10-28 13:19:02 -07:00
|
|
|
return callback(null);
|
2014-11-03 10:44:03 -08:00
|
|
|
return true;
|
2014-10-28 13:19:02 -07:00
|
|
|
} else {
|
2014-11-04 07:53:37 -08:00
|
|
|
if (callback)
|
2014-10-28 13:19:02 -07:00
|
|
|
return callback(new Error('Checksum does not match'));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-07-16 10:46:49 -07:00
|
|
|
};
|
|
|
|
|
2014-05-27 09:04:57 -07:00
|
|
|
module.exports = BitAuth;
|