From 56679a0f8ffdbe38d2d262807747975d8452ab44 Mon Sep 17 00:00:00 2001 From: Kagami Hiiragi Date: Fri, 30 Jan 2015 20:13:09 +0300 Subject: [PATCH] Check objects POW --- lib/objects.js | 18 +++++++++-------- lib/platform.browser.js | 1 - lib/platform.js | 5 +---- lib/pow.js | 6 +++--- lib/structs.js | 33 +++++++++++++++++++++---------- test.js | 43 ++++++++++++++++++++++++----------------- 6 files changed, 62 insertions(+), 44 deletions(-) diff --git a/lib/objects.js b/lib/objects.js index 7d1101e..72f8ac5 100644 --- a/lib/objects.js +++ b/lib/objects.js @@ -73,26 +73,28 @@ var getpubkey = exports.getpubkey = { /** * Decode `getpubkey` object message. * @param {Buffer} buf - Message + * @param {?Object} opts - Decoding options * @return {Promise.} A promise that contains decoded * `getpubkey` object structure when fulfilled. */ - decodeAsync: function(buf) { + decodeAsync: function(buf, opts) { return new promise(function(resolve) { var decoded = message.decode(buf); assert(decoded.command === "object", "Bad command"); - resolve(getpubkey.decodePayloadAsync(decoded.payload)); + resolve(getpubkey.decodePayloadAsync(decoded.payload, opts)); }); }, /** * Decode `getpubkey` object message payload. * @param {Buffer} buf - Message payload + * @param {?Object} opts - Decoding options * @return {Promise.} A promise that contains decoded * `getpubkey` object structure when fulfilled. */ - decodePayloadAsync: function(buf) { + decodePayloadAsync: function(buf, opts) { return new promise(function(resolve) { - var decoded = object.decodePayload(buf); + var decoded = object.decodePayload(buf, opts); assert(decoded.type === object.GETPUBKEY, "Wrong object type"); assert(decoded.version >= 2, "getpubkey version is too low"); assert(decoded.version <= 4, "getpubkey version is too high"); @@ -242,7 +244,7 @@ var pubkey = exports.pubkey = { decodePayloadAsync: function(buf, opts) { return new promise(function(resolve) { opts = opts || {}; - var decoded = object.decodePayload(buf); + var decoded = object.decodePayload(buf, opts); assert(decoded.type === object.PUBKEY, "Wrong object type"); var version = decoded.version; assert(version >= 2, "Address version is too low"); @@ -552,7 +554,7 @@ var msg = exports.msg = { */ decodePayloadAsync: function(buf, opts) { return new promise(function(resolve) { - var decoded = object.decodePayload(buf); + var decoded = object.decodePayload(buf, opts); assert(decoded.type === object.MSG, "Bad object type"); assert(decoded.version === 1, "Bad msg version"); var objectPayload = util.popkey(decoded, "objectPayload"); @@ -738,7 +740,7 @@ var DEFAULT_ENCODING = msg.TRIVIAL; function tryDecryptBroadcastV4(subscriptions, buf) { function inner(i) { if (i > last) { - var err = new Error("Failed to decrypt msg with given identities"); + var err = new Error("Failed to decrypt broadcast with given identities"); return promise.reject(err); } return bmcrypto @@ -801,7 +803,7 @@ var broadcast = exports.broadcast = { */ decodePayloadAsync: function(buf, opts) { return new promise(function(resolve) { - var decoded = object.decodePayload(buf); + var decoded = object.decodePayload(buf, opts); assert(decoded.type === object.BROADCAST, "Bad object type"); var version = decoded.version; assert(version === 4 || version === 5, "Bad broadcast version"); diff --git a/lib/platform.browser.js b/lib/platform.browser.js index 28cd1e5..f557ff2 100644 --- a/lib/platform.browser.js +++ b/lib/platform.browser.js @@ -45,7 +45,6 @@ exports.randomBytes = function(size) { var B80 = new BN("1208925819614629174706176"); exports.getTarget = function(opts) { var length = new BN(opts.payloadLength); - length.iaddn(8); length.iaddn(opts.payloadLengthExtraBytes); var denominator = new BN(opts.ttl); denominator.iaddn(65536); diff --git a/lib/platform.js b/lib/platform.js index e277df6..7597bdc 100644 --- a/lib/platform.js +++ b/lib/platform.js @@ -43,10 +43,7 @@ exports.getTarget = function(opts) { // Slightly rearrange calculations and compute it bottom-up, // right-to-left. See also: // . - var length = bignum(opts.payloadLength) - // To account for the nonce which we will append later. - .add(8) - .add(opts.payloadLengthExtraBytes); + var length = bignum(opts.payloadLength).add(opts.payloadLengthExtraBytes); var denominator = bignum(opts.ttl) .add(65536) .mul(length) diff --git a/lib/pow.js b/lib/pow.js index 74f2cdd..bf81d22 100644 --- a/lib/pow.js +++ b/lib/pow.js @@ -36,9 +36,9 @@ exports.getTarget = function(opts) { exports.check = function(opts) { var initialHash; var nonce; - if (opts.data) { - nonce = opts.data.slice(0, 8); - initialHash = bmcrypto.sha512(opts.data.slice(8)); + if (opts.payload) { + nonce = opts.payload.slice(0, 8); + initialHash = bmcrypto.sha512(opts.payload.slice(8)); } else { if (typeof opts.nonce === "number") { nonce = new Buffer(8); diff --git a/lib/structs.js b/lib/structs.js index 6900fb9..fe90fa2 100644 --- a/lib/structs.js +++ b/lib/structs.js @@ -11,6 +11,7 @@ var objectAssign = Object.assign || require("object-assign"); var bufferEqual = require("buffer-equal"); var assert = require("./_util").assert; var bmcrypto = require("./crypto"); +var POW = require("./pow"); var util = require("./_util"); function isAscii(str) { @@ -112,42 +113,54 @@ var object = exports.object = { * Decode `object` message. * NOTE: `nonce` and `objectPayload` are copied. * @param {Buffer} buf - Message + * @param {?Object} opts - Decoding options * @return {Object} Decoded `object` structure. */ - decode: function(buf) { + decode: function(buf, opts) { var decoded = message.decode(buf); assert(decoded.command === "object", "Bad command"); - return object.decodePayload(decoded.payload); + return object.decodePayload(decoded.payload, opts); }, /** * Decode `object` message payload. * NOTE: `nonce` and `objectPayload` are copied. * @param {Buffer} buf - Message payload + * @param {?Object} opts - Decoding options * @return {Object} Decoded `object` structure. */ - // FIXME(Kagami): Check a POW. - // TODO(Kagami): Allow lower POW for friends. - // TODO(Kagami): Option to not fail on bad POW (may be useful for - // bitchan). - // TODO(Kagami): Option to not fail on expired objects (would be - // useful for bitchan). - decodePayload: function(buf) { + decodePayload: function(buf, opts) { + opts = opts || {}; // 8 + 8 + 4 + (1+) + (1+) assert(buf.length >= 22, "object message payload is too small"); assert(buf.length <= 262144, "object message payload is too big"); var nonce = new Buffer(8); buf.copy(nonce, 0, 0, 8); + + // TTL. var expiresTime = util.readTimestamp64BE(buf.slice(8, 16)); var ttl = expiresTime - util.tnow(); - assert(ttl >= -3600, "Object expired more than a hour ago"); assert(ttl <= 2430000, "expiresTime is too far in the future"); + if (!opts.allowExpired) { + assert(ttl >= -3600, "Object expired more than a hour ago"); + } + + // POW. + if (!opts.skipPow) { + // User may specify trials/payload extra options and we will + // account in here. + var targetOpts = objectAssign({}, opts, {ttl: ttl, payload: buf}); + var target = POW.getTarget(targetOpts); + assert(POW.check({target: target, payload: buf}), "Insufficient POW"); + } + var type = buf.readUInt32BE(16, true); var decodedVersion = var_int.decode(buf.slice(20)); var decodedStream = var_int.decode(decodedVersion.rest); var headerLength = 20 + decodedVersion.length + decodedStream.length; var objectPayload = new Buffer(decodedStream.rest.length); decodedStream.rest.copy(objectPayload); + return { nonce: nonce, ttl: ttl, diff --git a/test.js b/test.js index 0024666..31b9d4e 100644 --- a/test.js +++ b/test.js @@ -33,6 +33,8 @@ var POW = bitmessage.POW; var Address = bitmessage.Address; var UserAgent = bitmessage.UserAgent; +var skipPow = {skipPow: true}; + describe("Crypto", function() { it("should implement SHA-1 hash", function() { expect(bmcrypto.sha1(Buffer("test")).toString("hex")).to.equal("a94a8fe5ccb19ba61c4c0873d391e987982fbbd3"); @@ -147,7 +149,7 @@ describe("Common structures", function() { type: 2, version: 1, objectPayload: Buffer("test"), - })); + }), skipPow); expect(bufferEqual(nonce, res.nonce)).to.be.true; expect(res.ttl).to.be.at.most(100); @@ -570,7 +572,7 @@ describe("Object types", function() { to: "BM-2D8Jxw5yiepaQqxrx43iPPNfRqbvWoJLoU", }).then(function(buf) { expect(message.decode(buf).command).to.equal("object"); - return getpubkey.decodeAsync(buf); + return getpubkey.decodeAsync(buf, skipPow); }).then(function(res) { expect(res.ttl).to.be.at.most(100); expect(res.type).to.equal(object.GETPUBKEY); @@ -587,7 +589,7 @@ describe("Object types", function() { to: "2cTux3PGRqHTEH6wyUP2sWeT4LrsGgy63z", }).then(function(buf) { expect(message.decode(buf).command).to.equal("object"); - return getpubkey.decodeAsync(buf); + return getpubkey.decodeAsync(buf, skipPow); }).then(function(res) { expect(res.ttl).to.be.at.most(100); expect(res.type).to.equal(object.GETPUBKEY); @@ -607,7 +609,7 @@ describe("Object types", function() { to: "BM-onhypnh1UMhbQpmvdiPuG6soLLytYJAfH", }).then(function(buf) { expect(message.decode(buf).command).to.equal("object"); - return pubkey.decodeAsync(buf); + return pubkey.decodeAsync(buf, skipPow); }).then(function(res) { expect(res.ttl).to.be.at.most(123); expect(res.type).to.equal(object.PUBKEY); @@ -627,7 +629,7 @@ describe("Object types", function() { to: "BM-2D8Jxw5yiepaQqxrx43iPPNfRqbvWoJLoU", }).then(function(buf) { expect(message.decode(buf).command).to.equal("object"); - return pubkey.decodeAsync(buf); + return pubkey.decodeAsync(buf, skipPow); }).then(function(res) { expect(res.ttl).to.be.at.most(456); expect(res.type).to.equal(object.PUBKEY); @@ -646,7 +648,7 @@ describe("Object types", function() { return pubkey.encodeAsync({ttl: 789, from: from, to: from}) .then(function(buf) { expect(message.decode(buf).command).to.equal("object"); - return pubkey.decodeAsync(buf, {needed: from}); + return pubkey.decodeAsync(buf, {needed: from, skipPow: true}); }).then(function(res) { expect(res.ttl).to.be.at.most(789); expect(res.type).to.equal(object.PUBKEY); @@ -672,7 +674,7 @@ describe("Object types", function() { message: "test", }).then(function(buf) { expect(message.decode(buf).command).to.equal("object"); - return msg.decodeAsync(buf, {identities: [from]}); + return msg.decodeAsync(buf, {identities: [from], skipPow: true}); }).then(function(res) { expect(res.ttl).to.be.at.most(111); expect(res.type).to.equal(object.MSG); @@ -701,7 +703,7 @@ describe("Object types", function() { message: "test", }).then(function(buf) { expect(message.decode(buf).command).to.equal("object"); - return msg.decodeAsync(buf, {identities: [fromV2]}); + return msg.decodeAsync(buf, {identities: [fromV2], skipPow: true}); }).then(function(res) { expect(res.ttl).to.be.at.most(111); expect(res.type).to.equal(object.MSG); @@ -729,8 +731,9 @@ describe("Object types", function() { to: from, message: "test", }).then(function(buf) { - return msg.decodeAsync(buf, {identities: []}); - }).catch(function() { + return msg.decodeAsync(buf, {identities: [], skipPow: true}); + }).catch(function(err) { + expect(err.message).to.match(/with given identities/i); done(); }); }); @@ -744,7 +747,7 @@ describe("Object types", function() { subject: "Тема", message: "Сообщение", }).then(function(buf) { - return msg.decodeAsync(buf, {identities: [from]}); + return msg.decodeAsync(buf, {identities: [from], skipPow: true}); }).then(function(res) { expect(res.encoding).to.equal(msg.SIMPLE); expect(res.subject).to.equal("Тема"); @@ -773,7 +776,7 @@ describe("Object types", function() { message: "test", }).then(function(buf) { expect(message.decode(buf).command).to.equal("object"); - return broadcast.decodeAsync(buf, {subscriptions: fromV3}); + return broadcast.decodeAsync(buf, {subscriptions: fromV3, skipPow: true}); }).then(function(res) { expect(res.ttl).to.be.at.most(987); expect(res.type).to.equal(object.BROADCAST); @@ -800,7 +803,7 @@ describe("Object types", function() { message: "test", }).then(function(buf) { expect(message.decode(buf).command).to.equal("object"); - return broadcast.decodeAsync(buf, {subscriptions: fromV2}); + return broadcast.decodeAsync(buf, {subscriptions: fromV2, skipPow: true}); }).then(function(res) { expect(res.ttl).to.be.at.most(999); expect(res.type).to.equal(object.BROADCAST); @@ -827,7 +830,7 @@ describe("Object types", function() { message: "キタ━━━(゜∀゜)━━━!!!!!", }).then(function(buf) { expect(message.decode(buf).command).to.equal("object"); - return broadcast.decodeAsync(buf, {subscriptions: [from]}); + return broadcast.decodeAsync(buf, {subscriptions: [from], skipPow: true}); }).then(function(res) { expect(res.ttl).to.be.at.most(987); expect(res.type).to.equal(object.BROADCAST); @@ -853,8 +856,12 @@ describe("Object types", function() { from: from, message: "test", }).then(function(buf) { - return broadcast.decodeAsync(buf, {subscriptions: [fromV3]}); - }).catch(function() { + return broadcast.decodeAsync(buf, { + subscriptions: [fromV3], + skipPow: true, + }); + }).catch(function(err) { + expect(err.message).to.match(/not interested/i); done(); }); }); @@ -901,8 +908,8 @@ describe("WIF", function() { describe("POW", function() { it("should calculate target", function() { - expect(POW.getTarget({ttl: 2418984, payloadLength: 628, nonceTrialsPerByte: 1000, payloadLengthExtraBytes: 1000})).to.equal(297422525267); - expect(POW.getTarget({ttl: 86400, payloadLength: 628})).to.equal(4863575534951); + expect(POW.getTarget({ttl: 2418984, payloadLength: 636, nonceTrialsPerByte: 1000, payloadLengthExtraBytes: 1000})).to.equal(297422525267); + expect(POW.getTarget({ttl: 86400, payloadLength: 636})).to.equal(4863575534951); }); it("should check a POW", function() {