Skip to content
This repository has been archived by the owner on Oct 30, 2018. It is now read-only.

Commit

Permalink
Merge pull request #678 from braydonf/master
Browse files Browse the repository at this point in the history
audit: use buffers when hashing
  • Loading branch information
bookchin committed Mar 31, 2017
2 parents af08c5e + 72464e1 commit 5042852
Show file tree
Hide file tree
Showing 9 changed files with 177 additions and 43 deletions.
22 changes: 14 additions & 8 deletions lib/audit-tools/audit-stream.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ inherits(AuditStream, stream.Writable);
AuditStream.prototype.getPublicRecord = function() {
assert(this._finished, 'Challenge generation is not finished');

return this._tree.level(this._tree.levels() - 1);
return this._tree.level(this._tree.levels() - 1)
.map((i) => i.toString('hex'));
};

/**
Expand All @@ -60,7 +61,7 @@ AuditStream.prototype.getPrivateRecord = function() {
return {
root: this._tree.root(),
depth: this._tree.levels(),
challenges: this._challenges
challenges: this._challenges.map((i) => i.toString('hex'))
};
};

Expand All @@ -73,7 +74,7 @@ AuditStream.prototype._write = function(bytes, encoding, next) {

this._inputs.forEach(function(input, i) {
if (i < self._audits) {
input.update(bytes.toString('hex'));
input.update(bytes);
}
});
next();
Expand All @@ -98,7 +99,7 @@ AuditStream.prototype._prepareChallenges = function() {
}

while (iterations < utils.getNextPowerOfTwo(this._audits)) {
inputs.push(utils.rmd160sha256(''));
inputs.push(utils.rmd160sha256b(''));
iterations++;
}

Expand All @@ -118,9 +119,12 @@ AuditStream.prototype._generateTree = function() {
if (i >= self._audits) {
return input;
} else {
return utils.rmd160sha256(utils.rmd160(input.digest('hex')));
const rmd1 = crypto.createHash('rmd160').update(input.digest()).digest();
const sha = crypto.createHash('sha256').update(rmd1).digest();
const rmd2 = crypto.createHash('rmd160').update(sha).digest();
return rmd2;
}
}), utils.rmd160sha256);
}), utils.rmd160sha256b);
};

/**
Expand All @@ -129,7 +133,7 @@ AuditStream.prototype._generateTree = function() {
* @returns {String} Hex encoded random bytes
*/
AuditStream.prototype._generateChallenge = function() {
return crypto.randomBytes(constants.AUDIT_BYTES).toString('hex');
return crypto.randomBytes(constants.AUDIT_BYTES);
};

/**
Expand All @@ -154,10 +158,12 @@ AuditStream.fromRecords = function(challenges, tree) {
'Challenges and tree do not match'
);

tree = tree.map((i) => Buffer.from(i, 'hex'));

var auditor = new AuditStream(challenges.length);

auditor._challenges = challenges;
auditor._tree = new MerkleTree(tree, utils.rmd160sha256);
auditor._tree = new MerkleTree(tree, utils.rmd160sha256b);
auditor._finished = true;

return auditor;
Expand Down
34 changes: 28 additions & 6 deletions lib/audit-tools/proof-stream.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,13 @@ function ProofStream(leaves, challenge) {
assert(Array.isArray(leaves), 'Merkle leaves must be an array');
assert.ok(challenge, 'Invalid challenge supplied');

this._tree = new MerkleTree(this._generateLeaves(leaves), utils.rmd160sha256);
this._challenge = challenge;
this._tree = new MerkleTree(this._generateLeaves(leaves),
utils.rmd160sha256b);
if (!Buffer.isBuffer(challenge)) {
this._challenge = Buffer.from(challenge, 'hex');
} else {
this._challenge = challenge;
}
this._hasher = crypto.createHash('sha256').update(this._challenge);
this._proof = null;

Expand All @@ -48,7 +53,7 @@ ProofStream.prototype.getProofResult = function() {
* @private
*/
ProofStream.prototype._transform = function(chunk, encoding, next) {
this._hasher.update(chunk.toString('hex'));
this._hasher.update(chunk, encoding);
next();
};

Expand All @@ -67,16 +72,31 @@ ProofStream.prototype._flush = function(done) {
done();
};

ProofStream.prototype._findMatchIndex = function(leaves, leaf) {
var challengenum = -1;
for (let i = 0; i < leaves.length; i++) {
if (Buffer.compare(leaves[i], leaf) === 0) {
challengenum = i;
break;
}
}
return challengenum;
}

/**
* Calculate audit response
* @private
* @param {String} challenge - Challenge string sent by auditor
* @returns {Array} result - Challenge response
*/
ProofStream.prototype._generateProof = function() {
var response = utils.rmd160(this._hasher.digest('hex'));

var response = utils.rmd160b(this._hasher.digest());
var leaves = this._tree.level(this._tree.levels() - 1);
var challengenum = leaves.indexOf(utils.rmd160sha256(response));

const leaf = utils.rmd160sha256b(response);

var challengenum = this._findMatchIndex(leaves, leaf);

assert(challengenum !== -1, 'Failed to generate proof');

Expand Down Expand Up @@ -107,9 +127,11 @@ ProofStream.prototype._generateLeaves = function(leaves) {
var emptyLeaves = [];

for (var i = 0; i < numEmpty; i++) {
emptyLeaves.push(utils.rmd160sha256(''));
emptyLeaves.push(utils.rmd160sha256b(''));
}

leaves = leaves.map((i) => Buffer.from(i, 'hex'))

return leaves.concat(emptyLeaves);
};

Expand Down
8 changes: 4 additions & 4 deletions lib/audit-tools/verification.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Verification.prototype._getChallengeResponse = function(tuple) {
var data = tuple || this._proof;

if (data.length === 1) {
return utils.rmd160sha256(data[0]);
return utils.rmd160sha256b(data[0]);
}

if (Array.isArray(data[0])) {
Expand All @@ -49,8 +49,8 @@ Verification.prototype.verify = function(root, totaldepth) {
function _collapse(proof, leaf, depth) {
if (depth === 0) {
assert(proof.length === 1, 'Invalid proof structure');
assert(utils.rmd160sha256(proof[0]) === leaf, 'Invalid proof value');

const proofhash = utils.rmd160sha256b(proof[0]);
assert(Buffer.compare(proofhash, leaf) === 0, 'Invalid proof value');
return leaf;
}

Expand All @@ -68,7 +68,7 @@ Verification.prototype.verify = function(root, totaldepth) {
hashR = proof[1];
}

return utils.rmd160sha256(hashL + hashR);
return utils.rmd160sha256b(Buffer.concat([hashL, hashR]));
}

return [
Expand Down
30 changes: 30 additions & 0 deletions lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,16 @@ module.exports.sha256 = function(input, encoding) {
return crypto.createHash('sha256').update(input, encoding).digest('hex');
};

/**
* Returns the SHA-256 hash of the input
* @param {String|Buffer} input - Data to hash
* @param {String} encoding - The encoding type of the data
* @returns {Buffer}
*/
module.exports.sha256b = function(input, encoding) {
return crypto.createHash('sha256').update(input, encoding).digest();
};

/**
* Returns the SHA-512 hash of the input
* @param {String|Buffer} input - Data to hash
Expand All @@ -62,6 +72,16 @@ module.exports.rmd160 = function(input, encoding) {
return crypto.createHash('rmd160').update(input, encoding).digest('hex');
};

/**
* Returns the RIPEMD-160 hash of the input
* @param {String|Buffer} input - Data to hash
* @param {String} encoding - The encoding type of the data
* @returns {Buffer}
*/
module.exports.rmd160b = function(input, encoding) {
return crypto.createHash('rmd160').update(input, encoding).digest();
};

/**
* Returns the WHIRLPOOL hash of the input
* @param {String|Buffer} input - Data to hash
Expand All @@ -84,6 +104,16 @@ module.exports.rmd160sha256 = function(input, encoding) {
);
};

/**
* Returns the RIPEMD-160 SHA-256 hash of this input
* @param {String|Buffer} input - Data to hash
* @param {String} encoding - The encoding type of the data
* @returns {Buffer}
*/
module.exports.rmd160sha256b = function(input, encoding) {
return module.exports.rmd160b(module.exports.sha256b(input, encoding))
};

/**
* Returns the SHA-1 WHIRLPOOL hash of this input
* @param {String|Buffer} input - Data to hash
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@
"mime": "^1.3.4",
"mkdirp": "^0.5.1",
"ms": "^0.7.1",
"mtree": "^0.0.1",
"mtree": "^1.0.0",
"nat-upnp": "^1.0.2",
"node-tar.gz": "^1.0.0",
"node-uuid": "^1.4.7",
Expand Down
43 changes: 33 additions & 10 deletions test/audit-tools/audit-stream.unit.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
'use strict';

var crypto = require('crypto');
var expect = require('chai').expect;
var AuditStream = require('../../lib/audit-tools/audit-stream');
const sinon = require('sinon');
const crypto = require('crypto');
const expect = require('chai').expect;
const AuditStream = require('../../lib/audit-tools/audit-stream');

var SHARD = new Buffer('testshard');
const SHARD = new Buffer('testshard');

describe('AuditStream', function() {

Expand All @@ -25,8 +26,29 @@ describe('AuditStream', function() {

it('should return a random 256 bit challenge', function() {
var challenge = AuditStream(6)._generateChallenge();
expect(challenge).to.have.lengthOf(64);
expect(Buffer(challenge, 'hex')).to.have.lengthOf(32);
expect(challenge).to.have.lengthOf(32);
expect(Buffer.isBuffer(challenge)).to.equal(true);
});

});

describe('#_generateTree', function() {
const sandbox = sinon.sandbox.create();
afterEach(() => sandbox.restore());

it('should generate the correct tree', function() {
const fauxChallenge = new Buffer(new Array(32));
sandbox.stub(AuditStream.prototype, '_generateChallenge')
.returns(fauxChallenge);

var stream = new AuditStream(6);
stream._generateTree();
expect(stream._tree._leaves.length).to.equal(8);
for (let i = 0; i < 8; i++) {
expect(Buffer.isBuffer(stream._tree._leaves[i])).to.equal(true);
}
expect(stream._tree._leaves[0].toString('hex'))
.to.equal('6017f1105942c0a7da8845b91da6871685fe2927');
});

});
Expand All @@ -48,7 +70,7 @@ describe('AuditStream', function() {
var audit = new AuditStream(12);
audit.on('finish', function() {
var leaves = audit.getPublicRecord();
var branch = audit._tree.level(4);
var branch = audit._tree.level(4).map((i) => i.toString('hex'));
leaves.forEach(function(leaf) {
expect(branch.indexOf(leaf)).to.not.equal(-1);
});
Expand All @@ -66,9 +88,10 @@ describe('AuditStream', function() {
var audit = new AuditStream(12);
audit.on('finish', function() {
var secret = audit.getPrivateRecord();
expect(secret.root).to.equal(audit._tree.root().toLowerCase());
expect(secret.root).to.equal(audit._tree.root());
expect(secret.depth).to.equal(audit._tree.levels());
expect(secret.challenges).to.equal(audit._challenges);
expect(secret.challenges)
.to.eql(audit._challenges.map((i) => i.toString('hex')));
done();
});
audit.write(SHARD);
Expand Down Expand Up @@ -102,7 +125,7 @@ describe('AuditStream#fromRecords', function() {
);
expect(
audit1.getPrivateRecord().root
).to.equal(
).to.eql(
audit2.getPrivateRecord().root
);
done();
Expand Down
25 changes: 14 additions & 11 deletions test/audit-tools/proof-stream.unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ var sinon = require('sinon');

describe('Proof', function() {

var SHARD = new Buffer('testshard');
const CHALLENGE = new Buffer('d3ccb55d5c9bd56606bca0187ecf28699cb674fb7e243' +
'fb4f180078735181686', 'hex');
const SHARD = new Buffer('testshard');

describe('@constructor', function() {

it('should create instance without the new keyword', function() {
expect(ProofStream([], 'challenge')).to.be.instanceOf(ProofStream);
expect(ProofStream([], CHALLENGE)).to.be.instanceOf(ProofStream);
});

});
Expand All @@ -24,11 +26,11 @@ describe('Proof', function() {
var audit = new AuditStream(12);
audit.end(SHARD);
setImmediate(function() {
var proof = new ProofStream(audit.getPublicRecord(), 'challenge');
var proof = new ProofStream(audit.getPublicRecord(), CHALLENGE);
var leaves = proof._generateLeaves(Array(12));
expect(leaves.length).to.equal(16);
leaves.splice(12).forEach(function(leaf) {
expect(leaf).to.equal(utils.rmd160sha256(''));
expect(leaf).to.equal(utils.rmd160sha256b(''));
});
});
});
Expand All @@ -38,7 +40,7 @@ describe('Proof', function() {
describe('#_flush', function() {

it('should emit an error if generate proof fails', function(done) {
var proof = ProofStream([], 'challenge');
var proof = ProofStream([], CHALLENGE);
var _generateProof = sinon.stub(proof, '_generateProof').throws(
new Error('Failed')
);
Expand All @@ -53,7 +55,7 @@ describe('Proof', function() {

describe('#getProofResult', function() {

it('should create a recursive tuple structure with leaves', function() {
it('should create a recursive tuple structure with leaves', function(done) {
var audit = new AuditStream(12);
audit.end(SHARD);
setImmediate(function() {
Expand Down Expand Up @@ -94,7 +96,7 @@ describe('Proof', function() {

function _getChallengeResponse(data) {
if (data.length === 1) {
return utils.rmd160sha256(data[0]);
return utils.rmd160sha256b(data[0]);
}

if (Array.isArray(data[0])) {
Expand All @@ -107,11 +109,12 @@ describe('Proof', function() {
var result = proof.getProofResult();

expect(result).to.have.lengthOf(2);
expect(_getChallengeResponse(result)).to.equal(
utils.rmd160sha256(utils.rmd160sha256(
challenge + SHARD.toString('hex')
))
expect(_getChallengeResponse(result)).to.eql(
utils.rmd160sha256b(utils.rmd160sha256b(
Buffer.concat([Buffer.from(challenge, 'hex'), SHARD])
))
);
done();
});
});
});
Expand Down
Loading

0 comments on commit 5042852

Please sign in to comment.