Merge pull request #29 from braydonf/feature/minify
Reduce Filesize of Browser Bundle
This commit is contained in:
commit
8a52d04f71
188
lib/bitauth.js
188
lib/bitauth.js
|
@ -1,76 +1,172 @@
|
||||||
var bitcore = require('bitcore');
|
var elliptic = require('elliptic');
|
||||||
var Key = bitcore.Key;
|
var ecdsa = new elliptic.ec(elliptic.curves.secp256k1);
|
||||||
var SIN = bitcore.SIN;
|
var hashjs = require('hash.js');
|
||||||
var SINKey = bitcore.SINKey
|
var bs58 = require('bs58');
|
||||||
var util = bitcore.util;
|
|
||||||
|
|
||||||
var BitAuth = {};
|
var BitAuth = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will return a key pair and identity
|
||||||
|
*
|
||||||
|
* @returns {Object} An object with keys: created, priv, pub and sin
|
||||||
|
*/
|
||||||
BitAuth.generateSin = function() {
|
BitAuth.generateSin = function() {
|
||||||
var sk = new SINKey();
|
|
||||||
sk.generate();
|
var keys = ecdsa.genKeyPair();
|
||||||
return sk.storeObj();
|
|
||||||
|
var privateKey = keys.getPrivate('hex');
|
||||||
|
var publicKey = this.getPublicKeyFromPrivateKey(privateKey);
|
||||||
|
var sin = this.getSinFromPublicKey(publicKey);
|
||||||
|
|
||||||
|
var sinObj = {
|
||||||
|
created: Math.round(Date.now() / 1000),
|
||||||
|
priv: privateKey,
|
||||||
|
pub: publicKey,
|
||||||
|
sin: sin
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return sinObj;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will return a 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) {
|
BitAuth.getPublicKeyFromPrivateKey = function(privkey) {
|
||||||
try {
|
|
||||||
var key = new Key();
|
|
||||||
|
|
||||||
key.private = new Buffer(privkey, 'hex');
|
var keys = ecdsa.keyPair(privkey, 'hex');
|
||||||
key.regenerateSync();
|
|
||||||
|
|
||||||
return key.public.toString('hex');
|
// compressed public key
|
||||||
} catch (err) {
|
var pubKey = keys.getPublic();
|
||||||
console.log(err);
|
var xbuf = new Buffer(pubKey.x.toString('hex', 64), 'hex');
|
||||||
return null;
|
var ybuf = new Buffer(pubKey.y.toString('hex', 64), 'hex');
|
||||||
|
var pub;
|
||||||
|
|
||||||
|
if (ybuf[ybuf.length-1] % 2) { //odd
|
||||||
|
pub = Buffer.concat([new Buffer([3]), xbuf]);
|
||||||
|
} else { //even
|
||||||
|
pub = Buffer.concat([new Buffer([2]), xbuf]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var hexPubKey = pub.toString('hex');
|
||||||
|
|
||||||
|
return hexPubKey;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will return a SIN from a compressed public key
|
||||||
|
*
|
||||||
|
* @param {String} A public key in hex
|
||||||
|
* @returns {String} A SIN identity
|
||||||
|
*/
|
||||||
BitAuth.getSinFromPublicKey = function(pubkey) {
|
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(pubkey, 'hex').digest('hex');
|
||||||
|
|
||||||
|
// get the ripemd160 hash of the pubkey
|
||||||
|
var pubRipe = (new hashjs.ripemd160()).update(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(pubPrefixed, 'hex').digest('hex');
|
||||||
|
var checksumTotal = (new hashjs.sha256()).update(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 sign a string of data with 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) {
|
BitAuth.sign = function(data, privkey) {
|
||||||
var hash = util.sha256(data);
|
var hash = (new hashjs.sha256()).update(data).digest('hex');
|
||||||
|
var signature = ecdsa.sign(hash, privkey);
|
||||||
try {
|
var hexsignature = signature.toDER('hex');
|
||||||
var key = new Key();
|
return hexsignature;
|
||||||
key.private = new Buffer(privkey, 'hex');
|
|
||||||
return key.signSync(hash).toString('hex');
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err.stack);
|
|
||||||
console.log(err);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
BitAuth.verifySignature = function(data, pubkey, signature, callback) {
|
/**
|
||||||
var hash = util.sha256(data);
|
* Will verify a signature
|
||||||
|
*
|
||||||
try {
|
* @param {String} data - A string of data that has been signed
|
||||||
var key = new Key();
|
* @param {String} pubkey - The compressed public key in hex that has signed the data
|
||||||
key.public = new Buffer(pubkey, 'hex');
|
* @param {String} hexsignature - A DER signature in hex
|
||||||
key.verifySignature(hash, new Buffer(signature, 'hex'), callback);
|
* @returns {Function|Boolean} - If the signature is valid
|
||||||
} catch (err) {
|
*/
|
||||||
callback(err, false);
|
BitAuth.verifySignature = function(data, pubkey, hexsignature, callback) {
|
||||||
}
|
var hash = (new hashjs.sha256()).update(data).digest('hex');
|
||||||
|
var signature = new Buffer(hexsignature, 'hex');
|
||||||
|
var valid = ecdsa.verify(hash, signature, pubkey);
|
||||||
|
if (callback)
|
||||||
|
return callback(null, valid);
|
||||||
|
return valid;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
BitAuth.validateSin = function(sin, callback) {
|
||||||
var s = new SIN(sin);
|
|
||||||
|
|
||||||
|
var pubWithChecksum;
|
||||||
|
|
||||||
|
// check for non-base58 characters
|
||||||
try {
|
try {
|
||||||
s.validate()
|
pubWithChecksum = new Buffer(bs58.decode(sin), 'hex').toString('hex');
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
if (callback)
|
if (callback)
|
||||||
callback(err);
|
return callback(err);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check the version
|
||||||
|
if (pubWithChecksum.slice(0, 4) !== '0f02') {
|
||||||
if (callback)
|
if (callback)
|
||||||
callback(null);
|
return callback(new Error('Invalid prefix or SIN version'));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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(pubPrefixed, 'hex').digest('hex');
|
||||||
|
var checksumTotal = (new hashjs.sha256()).update(hash1, 'hex').digest('hex');
|
||||||
|
|
||||||
|
// check the checksum
|
||||||
|
if (checksumTotal.slice(0,8) === checksum) {
|
||||||
|
if (callback)
|
||||||
|
return callback(null);
|
||||||
return true;
|
return true;
|
||||||
|
} else {
|
||||||
|
if (callback)
|
||||||
|
return callback(new Error('Checksum does not match'));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = BitAuth;
|
module.exports = BitAuth;
|
||||||
|
|
13
package.json
13
package.json
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
{
|
{
|
||||||
"name": "bitauth",
|
"name": "bitauth",
|
||||||
"description": "Passwordless authentication using Bitcoin cryptography",
|
"description": "Passwordless authentication using Bitcoin cryptography",
|
||||||
|
@ -18,17 +17,23 @@
|
||||||
{
|
{
|
||||||
"name": "Gordon Hall",
|
"name": "Gordon Hall",
|
||||||
"email": "gordon@bitpay.com"
|
"email": "gordon@bitpay.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Braydon Fuller",
|
||||||
|
"email": "braydon@bitpay.com"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"make-dist": "sh scripts/make-dist.sh",
|
"make-dist": "sh scripts/make-dist.sh",
|
||||||
"test": "mocha test/*.js --reporter spec",
|
"postinstall": "npm run make-dist",
|
||||||
"postinstall": "npm run make-dist"
|
"test": "mocha test/*.js --reporter spec"
|
||||||
},
|
},
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"version": "0.1.1",
|
"version": "0.1.1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bitcore": "0.1.32",
|
"elliptic": "^0.15.12",
|
||||||
|
"hash.js": "^0.3.2",
|
||||||
|
"bs58": "^2.0.0",
|
||||||
"request": "^2.36.0",
|
"request": "^2.36.0",
|
||||||
"express": "^4.3.1",
|
"express": "^4.3.1",
|
||||||
"base58-native": "^0.1.4",
|
"base58-native": "^0.1.4",
|
||||||
|
|
|
@ -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..."
|
echo "Building browser bundle for bitauth..."
|
||||||
node_modules/.bin/browserify lib/bitauth.js -s bitauth -x buffertools -x bitcore -o dist/bitauth.bundle.js
|
node_modules/.bin/browserify lib/bitauth.js -s bitauth -o dist/bitauth.bundle.js
|
||||||
echo "Minifying bitcore and bitauth..."
|
echo "Minifying bitauth..."
|
||||||
node_modules/.bin/uglifyjs dist/bitcore.bundle.js dist/bitauth.bundle.js -o dist/bitauth.browser.min.js
|
node_modules/.bin/uglifyjs dist/bitauth.bundle.js --compress --mangle -o dist/bitauth.browser.min.js
|
||||||
echo "Done!"
|
echo "Done!"
|
||||||
|
|
|
@ -12,12 +12,31 @@ describe('bitauth', function() {
|
||||||
|
|
||||||
var should = chai.should();
|
var should = chai.should();
|
||||||
|
|
||||||
|
// previously known keys for comparison
|
||||||
|
var keysKnown = {
|
||||||
|
priv: '97811b691dd7ebaeb67977d158e1da2c4d3eaa4ee4e2555150628acade6b344c',
|
||||||
|
pub: '02326209e52f6f17e987ec27c56a1321acf3d68088b8fb634f232f12ccbc9a4575',
|
||||||
|
sin: 'Tf3yr5tYvccKNVrE26BrPs6LWZRh8woHwjR'
|
||||||
|
}
|
||||||
|
|
||||||
|
// a private key that will produce a public key with a leading zero
|
||||||
|
var privateKeyToZero = 'c6b7f6bfe5bb19b1e390e55ed4ba5df8af6068d0eb89379a33f9c19aacf6c08c';
|
||||||
|
|
||||||
|
// keys generated
|
||||||
var keys = null;
|
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 contract = 'keyboard cat';
|
||||||
var secret = 'o hai, nsa. how i do teh cryptos?';
|
var secret = 'o hai, nsa. how i do teh cryptos?';
|
||||||
var password = 's4705hiru13z!';
|
var password = 's4705hiru13z!';
|
||||||
|
|
||||||
|
// signature from generate keys
|
||||||
var signature = null;
|
var signature = null;
|
||||||
var enc = null;
|
var enc = null;
|
||||||
|
|
||||||
|
@ -41,6 +60,11 @@ describe('bitauth', function() {
|
||||||
done();
|
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() {
|
describe('#getSinFromPublicKey', function() {
|
||||||
|
@ -50,6 +74,11 @@ describe('bitauth', function() {
|
||||||
done();
|
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() {
|
describe('#sign', function() {
|
||||||
|
@ -65,7 +94,30 @@ describe('bitauth', function() {
|
||||||
describe('#verifySignature', function() {
|
describe('#verifySignature', function() {
|
||||||
|
|
||||||
it('should verify the signature', function(done) {
|
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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should verify the signature with leading zero public key', function(done) {
|
||||||
|
|
||||||
|
var leadingZeroKeys = {
|
||||||
|
priv: privateKeyToZero,
|
||||||
|
pub: bitauth.getPublicKeyFromPrivateKey(privateKeyToZero)
|
||||||
|
};
|
||||||
|
|
||||||
|
signature = bitauth.sign(contract, leadingZeroKeys.priv);
|
||||||
|
bitauth.verifySignature(contract, leadingZeroKeys.pub, signature, function(err, valid){
|
||||||
|
should.not.exist(err);
|
||||||
|
should.exist(valid);
|
||||||
|
valid.should.equal(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
done();
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -73,7 +125,7 @@ describe('bitauth', function() {
|
||||||
describe('#validateSinTrue', function() {
|
describe('#validateSinTrue', function() {
|
||||||
|
|
||||||
it('should validate the sin as true', function(done) {
|
it('should validate the sin as true', function(done) {
|
||||||
var valid = bitauth.validateSin(sin);
|
var valid = bitauth.validateSin(singood);
|
||||||
should.equal(true, valid);
|
should.equal(true, valid);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -82,8 +134,14 @@ describe('bitauth', function() {
|
||||||
|
|
||||||
describe('#validateSinFalse', function() {
|
describe('#validateSinFalse', function() {
|
||||||
|
|
||||||
it('should validate the sin as false', function(done) {
|
it('should validate the sin as false because of bad checksum', function(done) {
|
||||||
var valid = bitauth.validateSin(sinb);
|
var valid = bitauth.validateSin(sinbad);
|
||||||
|
should.equal(false, valid);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should validate the sin as false because of non-base58', function(done) {
|
||||||
|
var valid = bitauth.validateSin('not#base!58');
|
||||||
should.equal(false, valid);
|
should.equal(false, valid);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -93,8 +151,9 @@ describe('bitauth', function() {
|
||||||
describe('#validateSinCallback', function() {
|
describe('#validateSinCallback', function() {
|
||||||
|
|
||||||
it('should receive error callback', function(done) {
|
it('should receive error callback', function(done) {
|
||||||
var valid = bitauth.validateSin(sinb, function(err){
|
var valid = bitauth.validateSin(sinbad, function(err){
|
||||||
should.exist(err);
|
should.exist(err);
|
||||||
|
err.message.should.equal('Checksum does not match');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue