replace bitcore with elliptic, hashjs and bs58 to produce a smaller browser build

This commit is contained in:
Braydon Fuller 2014-10-28 16:19:02 -04:00
parent c18ee6fc9e
commit 1ae18d227f
4 changed files with 191 additions and 69 deletions

View File

@ -1,76 +1,171 @@
var bitcore = require('bitcore');
var Key = bitcore.Key;
var SIN = bitcore.SIN;
var SINKey = bitcore.SINKey
var util = bitcore.util;
var elliptic = require('elliptic');
var ecdsa = new elliptic.ec(elliptic.curves.secp256k1);
var hashjs = require('hash.js');
var bs58 = require('bs58');
var BitAuth = {};
var BitAuth = {};
/**
* Will return a key pair and identity
*
* @example
* var keys = BitAuth.generateSin();
*
* @returns {Object} An object with keys: created, priv, pub and sin
*/
BitAuth.generateSin = function() {
var sk = new SINKey();
sk.generate();
return sk.storeObj();
};
BitAuth.getPublicKeyFromPrivateKey = function(privkey) {
try {
var key = new Key();
var keys = ecdsa.genKeyPair();
key.private = new Buffer(privkey, 'hex');
key.regenerateSync();
var publicKey = this.getPublicKeyFromPrivateKey(keys.getPrivate('hex'));
var sin = this.getSinFromPublicKey(publicKey);
return key.public.toString('hex');
} catch (err) {
console.log(err);
return null;
var sinObj = {
created: new Date().getTime(),
priv: keys.getPrivate('hex'),
pub: publicKey,
sin: sin
}
return sinObj;
};
/**
* Will return an public key from a private key
*
* @param {String} A private key in hex
* @returns {String} A compressed public key in hex
*/
BitAuth.getPublicKeyFromPrivateKey = function(privkey) {
var keys = ecdsa.keyPair(privkey, 'hex');
// compressed public key
var pubKey = keys.getPublic();
var xbuf = new Buffer(pubKey.x.toString('hex'), 'hex');
var ybuf = new Buffer(pubKey.y.toString('hex'), 'hex');
if (ybuf[ybuf.length-1] % 2) { //odd
var pub = Buffer.concat([new Buffer([3]), xbuf]);
}
else { //even
var pub = Buffer.concat([new Buffer([2]), xbuf]);
}
var hexPubKey = pub.toString('hex');
return hexPubKey;
};
/**
* Will return a SIN from a compressed private key
*
* @param {String} A public key in hex
* @returns {String} A SIN identity
*/
BitAuth.getSinFromPublicKey = function(pubkey) {
var pubkeyHash = util.sha256ripe160(new Buffer(pubkey, 'hex'));
var sin = new SIN(SIN.SIN_EPHEM, pubkeyHash);
return sin.toString();
// sha256 hash the pubkey
var pubHash = (new hashjs.sha256()).update(new Buffer(pubkey, 'hex')).digest('hex');
// get the ripemd160 hash of the pubkey
var pubRipe = (new hashjs.ripemd160()).update(new Buffer(pubHash, 'hex')).digest('hex');
// add the version
var pubPrefixed = '0f02'+pubRipe;
// two rounds of hashing to generate the checksum
var hash1 = (new hashjs.sha256()).update(new Buffer(pubPrefixed, 'hex')).digest('hex');
var checksumTotal = (new hashjs.sha256()).update(new Buffer(hash1, 'hex')).digest('hex');
// 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;
}
/**
* Will return a signature from a private key
*
* @param {String} data - A string of data to be signed
* @param {String} privKey - A private key in hex
* @returns {String} signature - A DER signature in hex
*/
BitAuth.sign = function(data, privkey) {
var hash = util.sha256(data);
var hash = (new hashjs.sha256()).update(new Buffer(data, 'ascii')).digest('hex');
var signature = ecdsa.sign(hash, privkey);
var hexsignature = signature.toDER('hex');
return hexsignature;
};
try {
var key = new Key();
key.private = new Buffer(privkey, 'hex');
return key.signSync(hash).toString('hex');
} catch (err) {
console.log(err.stack);
console.log(err);
return null;
/**
* Will verify a signature
*
* @param {String} data - A string of data that has been signed
* @param {String} pubkey - The public identity that has signed the data
* @param {String} hexsignature - A DER signature in hex
* @returns {Function|Boolean} - If the signature is valid
*/
BitAuth.verifySignature = function(data, pubkey, hexsignature, callback) {
var hash = (new hashjs.sha256()).update(new Buffer(data, 'ascii')).digest('hex');
var signature = new Buffer(hexsignature, 'hex');
var valid = ecdsa.verify(hash, signature, pubkey);
if ( callback ){
return callback(null, valid);
} else {
return valid;
}
};
BitAuth.verifySignature = function(data, pubkey, signature, callback) {
var hash = util.sha256(data);
try {
var key = new Key();
key.public = new Buffer(pubkey, 'hex');
key.verifySignature(hash, new Buffer(signature, 'hex'), callback);
} catch (err) {
callback(err, false);
}
};
/**
* Will verify that a SIN is valid
*
* @param {String} sin - A SIN identity
* @returns {Function|Boolean} - If the SIN identity is valid
*/
BitAuth.validateSin = function(sin, callback) {
var s = new SIN(sin);
try {
s.validate()
} catch(err) {
if ( callback )
callback(err);
var decoded = bs58.decode(sin);
var pubWithChecksum = new Buffer(decoded, 'hex').toString('hex');
// check the version
if ( pubWithChecksum.slice(0, 4) != '0f02' ) {
if ( callback ) {
return callback(new Error('Invalid prefix or SIN version'));
}
return false;
}
if ( callback )
callback(null);
return true;
// get the checksum
var checksum = pubWithChecksum.slice(pubWithChecksum.length-8, pubWithChecksum.length);
var pubPrefixed = pubWithChecksum.slice(0, pubWithChecksum.length-8);
// two rounds of hashing to generate the checksum
var hash1 = (new hashjs.sha256()).update(new Buffer(pubPrefixed, 'hex')).digest('hex');
var checksumTotal = (new hashjs.sha256()).update(new Buffer(hash1, 'hex')).digest('hex');
// check the checksum
if ( checksumTotal.slice(0,8) == checksum ) {
if ( callback ) {
return callback(null);
}
return true
} else {
if ( callback ) {
return callback(new Error('Checksum does not match'));
}
return false;
}
};
module.exports = BitAuth;

View File

@ -1,4 +1,3 @@
{
"name": "bitauth",
"description": "Passwordless authentication using Bitcoin cryptography",
@ -22,13 +21,14 @@
],
"scripts": {
"make-dist": "sh scripts/make-dist.sh",
"test": "mocha test/*.js --reporter spec",
"postinstall": "npm run make-dist"
"test": "mocha test/*.js --reporter spec"
},
"main": "index.js",
"version": "0.1.1",
"dependencies": {
"bitcore": "0.1.32",
"elliptic": "=0.15.11",
"hash.js": "=0.3.2",
"bs58": "=2.0.0",
"request": "^2.36.0",
"express": "^4.3.1",
"base58-native": "^0.1.4",

View File

@ -1,10 +1,5 @@
cd node_modules/bitcore
echo "Building browser bundle for bitcore..."
node browser/build -s lib/Key,lib/SINKey,lib/SIN,util/util
cd ../../
cp node_modules/bitcore/browser/bundle.js dist/bitcore.bundle.js
echo "Building browser bundle for bitauth..."
node_modules/.bin/browserify lib/bitauth.js -s bitauth -x buffertools -x bitcore -o dist/bitauth.bundle.js
echo "Minifying bitcore and bitauth..."
node_modules/.bin/uglifyjs dist/bitcore.bundle.js dist/bitauth.bundle.js -o dist/bitauth.browser.min.js
node_modules/.bin/browserify lib/bitauth.js -s bitauth -o dist/bitauth.bundle.js
echo "Minifying bitauth..."
node_modules/.bin/uglifyjs dist/bitauth.bundle.js -o dist/bitauth.browser.min.js
echo "Done!"

View File

@ -12,12 +12,28 @@ describe('bitauth', function() {
var should = chai.should();
// previously known keys for comparison
var keysKnown = {
priv: '97811b691dd7ebaeb67977d158e1da2c4d3eaa4ee4e2555150628acade6b344c',
pub: '02326209e52f6f17e987ec27c56a1321acf3d68088b8fb634f232f12ccbc9a4575',
sin: 'Tf3yr5tYvccKNVrE26BrPs6LWZRh8woHwjR'
}
// keys generated
var keys = null;
var sin = 'Tf1Jc1xSbqasm5QLwwSQc5umddx2h7mAMHX';
var sinb = 'Tf1Jc1xSbqasm5QLwwSQc5umddx2h7mAMhX';
// invalid checksum
var sinbad = 'Tf1Jc1xSbqasm5QLwwSQc5umddx2h7mAMhX';
// valid sin
var singood = 'TfG4ScDgysrSpodWD4Re5UtXmcLbY5CiUHA';
// data to sign
var contract = 'keyboard cat';
var secret = 'o hai, nsa. how i do teh cryptos?';
var password = 's4705hiru13z!';
// signature from generate keys
var signature = null;
var enc = null;
@ -41,6 +57,11 @@ describe('bitauth', function() {
done();
});
it('should properly get compressed public key from a previously known private key', function(done) {
bitauth.getPublicKeyFromPrivateKey(keysKnown.priv).should.equal(keysKnown.pub);
done();
});
});
describe('#getSinFromPublicKey', function() {
@ -50,6 +71,11 @@ describe('bitauth', function() {
done();
});
it('should properly get the sin from a previously known compressed public key', function(done) {
bitauth.getSinFromPublicKey(keysKnown.pub).should.equal(keysKnown.sin);
done();
});
});
describe('#sign', function() {
@ -65,7 +91,12 @@ describe('bitauth', function() {
describe('#verifySignature', function() {
it('should verify the signature', function(done) {
bitauth.verifySignature(contract, keys.pub, signature, done);
bitauth.verifySignature(contract, keys.pub, signature, function(err, valid){
should.not.exist(err);
should.exist(valid);
valid.should.equal(true);
done();
});
});
});
@ -73,7 +104,7 @@ describe('bitauth', function() {
describe('#validateSinTrue', function() {
it('should validate the sin as true', function(done) {
var valid = bitauth.validateSin(sin);
var valid = bitauth.validateSin(singood);
should.equal(true, valid);
done();
});
@ -83,7 +114,7 @@ describe('bitauth', function() {
describe('#validateSinFalse', function() {
it('should validate the sin as false', function(done) {
var valid = bitauth.validateSin(sinb);
var valid = bitauth.validateSin(sinbad);
should.equal(false, valid);
done();
});
@ -93,8 +124,9 @@ describe('bitauth', function() {
describe('#validateSinCallback', function() {
it('should receive error callback', function(done) {
var valid = bitauth.validateSin(sinb, function(err){
var valid = bitauth.validateSin(sinbad, function(err){
should.exist(err);
err.message.should.equal('Checksum does not match');
done();
});
});