Implement address decoding
This commit is contained in:
parent
e425730021
commit
1ccd56f17a
|
@ -7,7 +7,7 @@
|
|||
// Enforcing
|
||||
"bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.)
|
||||
"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
|
||||
"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()
|
||||
|
|
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.
|
||||
* @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";
|
||||
|
||||
require("es6-promise").polyfill();
|
||||
var ripemd160 = require("ripemd160");
|
||||
|
||||
exports.sha512 = function(buf) {
|
||||
return window.crypto.subtle.digest({name: "SHA-512"}, buf).then(function(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.
|
||||
* Wrap all crypto functions with promises because WebCryptoAPI uses it
|
||||
* throughout.
|
||||
* @module bitmessage/lib/crypto
|
||||
* @module bitmessage/crypto
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
var Promise = require("es6-promise").Promise; // jshint ignore:line
|
||||
require("es6-promise").polyfill();
|
||||
var crypto = require("crypto");
|
||||
|
||||
exports.sha512 = function(buf) {
|
||||
|
@ -15,3 +13,9 @@ exports.sha512 = function(buf) {
|
|||
hash.update(buf);
|
||||
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",
|
||||
"main": "./lib/index.js",
|
||||
"browser": {
|
||||
"int64-native": "node-int64",
|
||||
"./lib/crypto.js": "./lib/crypto.browser.js"
|
||||
},
|
||||
"scripts": {
|
||||
|
@ -38,6 +39,11 @@
|
|||
"mocha": "*"
|
||||
},
|
||||
"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 bufferEqual = require("buffer-equal");
|
||||
var bitmessage = require("./lib");
|
||||
var Address = bitmessage.Address;
|
||||
var varint = require("./lib/varint");
|
||||
var bmcrypto = require("./lib/crypto");
|
||||
|
||||
describe("Bitmessage crypto", function() {
|
||||
it("should calculate sha512 hash", function() {
|
||||
return bmcrypto.sha512(new Buffer("test")).then(function(res) {
|
||||
describe("var_int", function() {
|
||||
it("should decode", function() {
|
||||
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");
|
||||
});
|
||||
});
|
||||
|
||||
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