Implement address decoding
This commit is contained in:
parent
e425730021
commit
1ccd56f17a
|
@ -7,7 +7,7 @@
|
||||||
// Enforcing
|
// Enforcing
|
||||||
"bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.)
|
"bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.)
|
||||||
"camelcase" : false, // true: Identifiers must be in camelCase
|
"camelcase" : false, // true: Identifiers must be in camelCase
|
||||||
"curly" : false, // true: Require {} for every new block or scope
|
"curly" : true, // true: Require {} for every new block or scope
|
||||||
"eqeqeq" : true, // true: Require triple equals (===) for comparison
|
"eqeqeq" : true, // true: Require triple equals (===) for comparison
|
||||||
"freeze" : true, // true: prohibits overwriting prototypes of native objects such as Array, Date etc.
|
"freeze" : true, // true: prohibits overwriting prototypes of native objects such as Array, Date etc.
|
||||||
"forin" : true, // true: Require filtering for..in loops with obj.hasOwnProperty()
|
"forin" : true, // true: Require filtering for..in loops with obj.hasOwnProperty()
|
||||||
|
|
72
lib/address.js
Normal file
72
lib/address.js
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
/**
|
||||||
|
* Working with Bitmessage addresses.
|
||||||
|
* @module bitmessage/address
|
||||||
|
*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var bufferEqual = require("buffer-equal");
|
||||||
|
var bs58 = require("bs58");
|
||||||
|
var varint = require("./varint");
|
||||||
|
var bmcrypto = require("./crypto");
|
||||||
|
var assert = require("./utils").assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse Bitmessage Base58 encoded address (with or without `BM-`
|
||||||
|
* prefix) into address object.
|
||||||
|
*/
|
||||||
|
exports.decode = function(str) {
|
||||||
|
str = str.trim();
|
||||||
|
if (str.slice(0, 3) === "BM-") {
|
||||||
|
str = str.slice(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
var bytes;
|
||||||
|
try {
|
||||||
|
bytes = bs58.decode(str);
|
||||||
|
} catch(e) {
|
||||||
|
return Promise.reject(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checksum validating.
|
||||||
|
var data = new Buffer(bytes.slice(0, -4));
|
||||||
|
var checksum = new Buffer(bytes.slice(-4));
|
||||||
|
return bmcrypto.sha512(data).then(bmcrypto.sha512).then(function(dhash) {
|
||||||
|
assert(bufferEqual(dhash.slice(0, 4), checksum), "Bad checkum");
|
||||||
|
|
||||||
|
var decoded = varint.decode(data);
|
||||||
|
var version = decoded.value;
|
||||||
|
assert(version <= 4, "Version too high");
|
||||||
|
assert(version >= 1, "Version too low");
|
||||||
|
|
||||||
|
data = decoded.rest;
|
||||||
|
decoded = varint.decode(data);
|
||||||
|
var stream = decoded.value;
|
||||||
|
|
||||||
|
var ripe = decoded.rest;
|
||||||
|
var ripelen = ripe.length;
|
||||||
|
switch (version) {
|
||||||
|
case 1:
|
||||||
|
assert(ripelen === 20);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
case 3:
|
||||||
|
assert(ripelen >= 18, "Ripe too short");
|
||||||
|
assert(ripelen <= 20, "Ripe too long");
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
assert(ripelen >= 4, "Ripe too short");
|
||||||
|
assert(ripelen <= 20, "Ripe too long");
|
||||||
|
assert(ripe[0] !== 0, "Ripe encode error");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent extra allocation. God, kill me please for premature
|
||||||
|
// optimizations.
|
||||||
|
if (ripelen < 20) {
|
||||||
|
var zeroes = new Buffer(Array(20 - ripelen));
|
||||||
|
ripe = Buffer.concat([zeroes, ripe]);
|
||||||
|
}
|
||||||
|
return {version: version, stream: stream, ripe: ripe};
|
||||||
|
});
|
||||||
|
};
|
|
@ -1,12 +1,28 @@
|
||||||
/**
|
/**
|
||||||
* Browser version of the crypto for Bitmessage JS implementation.
|
* Browser version of the crypto for Bitmessage JS implementation.
|
||||||
* @module bitmessage/lib/crypto.browser
|
*
|
||||||
|
* Documentation: <http://www.w3.org/TR/WebCryptoAPI/>
|
||||||
|
* Browsers support: <http://caniuse.com/#feat=cryptography>
|
||||||
|
* Blink implementation details: <https://sites.google.com/a/chromium.org/dev/blink/webcrypto>
|
||||||
|
*
|
||||||
|
* @module bitmessage/crypto.browser
|
||||||
*/
|
*/
|
||||||
|
// FIXME(Kagami): Support webkit subtle prefix!
|
||||||
|
// TODO(Kagami): Try to support IE11.
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
require("es6-promise").polyfill();
|
||||||
|
var ripemd160 = require("ripemd160");
|
||||||
|
|
||||||
exports.sha512 = function(buf) {
|
exports.sha512 = function(buf) {
|
||||||
return window.crypto.subtle.digest({name: "SHA-512"}, buf).then(function(arr) {
|
return window.crypto.subtle.digest({name: "SHA-512"}, buf).then(function(arr) {
|
||||||
return new Buffer(new Uint8Array(arr));
|
return new Buffer(new Uint8Array(arr));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
exports.ripemd160 = function(buf) {
|
||||||
|
// XXX(Kagami): No support in browsers via Web Crypto API currently,
|
||||||
|
// so use module.
|
||||||
|
return Promise.resolve(ripemd160(buf));
|
||||||
|
};
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
/**
|
/**
|
||||||
* Node.js version of the crypto for Bitmessage JS implementation.
|
* Node.js version of the crypto for Bitmessage JS implementation.
|
||||||
* Wrap all crypto functions with promises because WebCryptoAPI uses it
|
* @module bitmessage/crypto
|
||||||
* throughout.
|
|
||||||
* @module bitmessage/lib/crypto
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var Promise = require("es6-promise").Promise; // jshint ignore:line
|
require("es6-promise").polyfill();
|
||||||
var crypto = require("crypto");
|
var crypto = require("crypto");
|
||||||
|
|
||||||
exports.sha512 = function(buf) {
|
exports.sha512 = function(buf) {
|
||||||
|
@ -15,3 +13,9 @@ exports.sha512 = function(buf) {
|
||||||
hash.update(buf);
|
hash.update(buf);
|
||||||
return Promise.resolve(hash.digest());
|
return Promise.resolve(hash.digest());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
exports.ripemd160 = function(buf) {
|
||||||
|
var hash = crypto.createHash("ripemd160");
|
||||||
|
hash.update(buf);
|
||||||
|
return Promise.resolve(hash.digest());
|
||||||
|
};
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
/**
|
||||||
|
* Main Bitmessage module. Just reexports all public submodules.
|
||||||
|
* @module bitmessage
|
||||||
|
*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
exports.Address = require("./address");
|
7
lib/utils.js
Normal file
7
lib/utils.js
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
exports.assert = function(condition, message) {
|
||||||
|
if (!condition) {
|
||||||
|
throw new Error(message || "Assertion failed");
|
||||||
|
}
|
||||||
|
};
|
39
lib/varint.js
Normal file
39
lib/varint.js
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
/**
|
||||||
|
* Implement `var_int` encoding/decoding.
|
||||||
|
* @module bitmessage/varint
|
||||||
|
*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// TODO(Kagami): Since `node-int64` and `int64-native` APIs are slightly
|
||||||
|
// differ, there might be need in platform-dependent wrapper. Also think
|
||||||
|
// that to do with 64bit arithmetic since `node-int64` doesn't implement
|
||||||
|
// it.
|
||||||
|
var Int64 = require("int64-native");
|
||||||
|
var assert = require("./utils").assert;
|
||||||
|
|
||||||
|
exports.decode = function(buf) {
|
||||||
|
assert(buf.length > 0, "Empty buffer");
|
||||||
|
var value, length;
|
||||||
|
switch (buf[0]) {
|
||||||
|
case 253:
|
||||||
|
value = buf.readUInt16BE(1);
|
||||||
|
length = 3;
|
||||||
|
break;
|
||||||
|
case 254:
|
||||||
|
value = buf.readUInt32BE(1);
|
||||||
|
length = 5;
|
||||||
|
break;
|
||||||
|
case 255:
|
||||||
|
var hi = buf.readUInt32BE(1);
|
||||||
|
var lo = buf.readUInt32BE(5);
|
||||||
|
value = new Int64(hi, lo);
|
||||||
|
length = 9;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
value = buf[0];
|
||||||
|
length = 1;
|
||||||
|
}
|
||||||
|
var rest = buf.slice(length);
|
||||||
|
return {value: value, length: length, rest: rest};
|
||||||
|
};
|
|
@ -4,6 +4,7 @@
|
||||||
"description": "JavaScript Bitmessage library",
|
"description": "JavaScript Bitmessage library",
|
||||||
"main": "./lib/index.js",
|
"main": "./lib/index.js",
|
||||||
"browser": {
|
"browser": {
|
||||||
|
"int64-native": "node-int64",
|
||||||
"./lib/crypto.js": "./lib/crypto.browser.js"
|
"./lib/crypto.js": "./lib/crypto.browser.js"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -38,6 +39,11 @@
|
||||||
"mocha": "*"
|
"mocha": "*"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"es6-promise": "^2.0.1"
|
"bs58": "^2.0.0",
|
||||||
|
"buffer-equal": "~0.0.1",
|
||||||
|
"es6-promise": "^2.0.1",
|
||||||
|
"int64-native": "^0.3.2",
|
||||||
|
"node-int64": "^0.3.2",
|
||||||
|
"ripemd160": "^0.2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
63
test.js
63
test.js
|
@ -1,11 +1,68 @@
|
||||||
var expect = require("chai").expect;
|
var expect = require("chai").expect;
|
||||||
|
|
||||||
|
var bufferEqual = require("buffer-equal");
|
||||||
|
var bitmessage = require("./lib");
|
||||||
|
var Address = bitmessage.Address;
|
||||||
|
var varint = require("./lib/varint");
|
||||||
var bmcrypto = require("./lib/crypto");
|
var bmcrypto = require("./lib/crypto");
|
||||||
|
|
||||||
describe("Bitmessage crypto", function() {
|
describe("var_int", function() {
|
||||||
it("should calculate sha512 hash", function() {
|
it("should decode", function() {
|
||||||
return bmcrypto.sha512(new Buffer("test")).then(function(res) {
|
var res;
|
||||||
|
expect(varint.decode.bind(Buffer([]))).to.throw(Error);
|
||||||
|
|
||||||
|
res = varint.decode(Buffer([123]));
|
||||||
|
expect(res.value).to.equal(123);
|
||||||
|
expect(res.length).to.equal(1);
|
||||||
|
expect(bufferEqual(res.rest, Buffer([]))).to.be.true;
|
||||||
|
|
||||||
|
res = varint.decode(Buffer("fd123456", "hex"));
|
||||||
|
expect(res.value).to.equal(0x1234);
|
||||||
|
expect(res.length).to.equal(3);
|
||||||
|
expect(bufferEqual(res.rest, Buffer("56", "hex"))).to.be.true;
|
||||||
|
|
||||||
|
res = varint.decode(Buffer("fe1234567890", "hex"));
|
||||||
|
expect(res.value).to.equal(0x12345678);
|
||||||
|
expect(res.length).to.equal(5);
|
||||||
|
expect(bufferEqual(res.rest, Buffer("90", "hex"))).to.be.true;
|
||||||
|
|
||||||
|
res = varint.decode(Buffer("ff0000001234567890", "hex"));
|
||||||
|
expect(res.value == 0x1234567890).to.be.true;
|
||||||
|
expect(res.length).to.equal(9);
|
||||||
|
expect(res.rest.length).to.equal(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Crypto", function() {
|
||||||
|
it("should implement SHA-512 hash", function() {
|
||||||
|
return bmcrypto.sha512(Buffer("test")).then(function(res) {
|
||||||
expect(res.toString("hex")).to.equal("ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff");
|
expect(res.toString("hex")).to.equal("ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should implement RIPEMD-160 hash", function() {
|
||||||
|
return bmcrypto.ripemd160(Buffer("test")).then(function(res) {
|
||||||
|
expect(res.toString("hex")).to.equal("5e52fee47e6b070565f74372468cdc699de89107");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Address", function() {
|
||||||
|
it("should decode Bitmessage address", function() {
|
||||||
|
return Address.decode("BM-2cTux3PGRqHTEH6wyUP2sWeT4LrsGgy63z")
|
||||||
|
.then(function(addr) {
|
||||||
|
expect(addr.version).to.equal(4);
|
||||||
|
expect(addr.stream).to.equal(1);
|
||||||
|
expect(bufferEqual(addr.ripe, Buffer("003ab6655de4bd8c603eba9b00dd5970725fdd56", "hex"))).to.be.true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should decode Bitmessage address badly formatted", function() {
|
||||||
|
return Address.decode(" 2cTux3PGRqHTEH6wyUP2sWeT4LrsGgy63z ")
|
||||||
|
.then(function(addr) {
|
||||||
|
expect(addr.version).to.equal(4);
|
||||||
|
expect(addr.stream).to.equal(1);
|
||||||
|
expect(bufferEqual(addr.ripe, Buffer("003ab6655de4bd8c603eba9b00dd5970725fdd56", "hex"))).to.be.true;
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue
Block a user