From bc3fef1aea13f39869fd19cb309d7f994fff5130 Mon Sep 17 00:00:00 2001 From: Kagami Hiiragi Date: Sun, 18 Jan 2015 15:34:02 +0300 Subject: [PATCH] Address.fromPassphrase --- README.md | 8 ++----- lib/address.js | 65 ++++++++++++++++++++++++++++++++++++++++++-------- test.js | 13 +++++++++- 3 files changed, 69 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 6d035f7..29052dc 100644 --- a/README.md +++ b/README.md @@ -51,12 +51,8 @@ API documentation is available [here](https://bitchan.github.io/bitmessage/docs/ - [ ] broadcast - [x] WIF - [x] POW -- [ ] High-level classes - - [ ] Address - - [x] encode - - [x] decode - - [x] fromRandom - - [ ] fromPassphrase +- [x] High-level classes + - [x] Address - [x] UserAgent - [ ] Parse PyBitmessage configs - [ ] keys.dat diff --git a/lib/address.js b/lib/address.js index e544eee..a1e3a3f 100644 --- a/lib/address.js +++ b/lib/address.js @@ -216,27 +216,21 @@ Address.fromRandom = function(opts) { var version = opts.version = opts.version || 4; var ripelen = popkey(opts, "ripelen") || 19; assertripelen(ripelen, version); - // Should the generated ripe length be strictly equal to the specified - // (less or equal by default). - var strictripelen = !!popkey(opts, "strictripelen"); // TODO(Kagami): Speed it up using web workers in Browser. // TODO(Kagami): Bind to C++ version of this code in Node. - var encPrivateKey, encPublicKey, ripe; + var encPrivateKey, encPublicKey, ripe, len; var signPrivateKey = bmcrypto.getPrivate(); var signPublicKey = bmcrypto.getPublic(signPrivateKey); - var keysbuf = Buffer(130); + var keysbuf = new Buffer(130); signPublicKey.copy(keysbuf); while (true) { encPrivateKey = bmcrypto.getPrivate(); encPublicKey = bmcrypto.getPublic(encPrivateKey); encPublicKey.copy(keysbuf, 65); ripe = bmcrypto.ripemd160(bmcrypto.sha512(keysbuf)); - var len = getripelen(ripe); - if ( - (strictripelen && len === ripelen) || - (!strictripelen && len <= ripelen && checkripelen(ripelen, version)) - ) { + len = getripelen(ripe); + if (len <= ripelen && checkripelen(len, version)) { // TODO(Kagami): Do we need to put all these properties or compute // them manually via ECMA5 getters/setters instead? opts.signPrivateKey = signPrivateKey; @@ -249,4 +243,55 @@ Address.fromRandom = function(opts) { } }; +/** + * Create new Bitmessage address from passphrase. + * @param {?Object} opts - Address options + * @return {Address} Generated address object. + */ +Address.fromPassphrase = function(opts) { + opts = objectAssign({}, opts); + var version = opts.version = opts.version || 4; + var ripelen = popkey(opts, "ripelen") || 19; + assertripelen(ripelen, version); + var passphrase = popkey(opts, "passphrase"); + + // TODO(Kagami): Speed it up using web workers in Browser. + // TODO(Kagami): Bind to C++ version of this code in Node. + var signPrivateKey, signPublicKey, encPrivateKey, encPublicKey; + var ripe, len, tmp; + var signnonce = 0; + var encnonce = 1; + var keysbuf = new Buffer(130); + // XXX(Kagami): Spec doesn't mention encoding, using UTF-8. + var phrasebuf = new Buffer(passphrase, "utf8"); + while (true) { + // TODO(Kagami): We may slightly optimize it and pre-create tmp + // buffers based on the encoded nonce size (1, 3, 5 and 9 bytes). + tmp = Buffer.concat([phrasebuf, var_int.encode(signnonce)]); + signPrivateKey = bmcrypto.sha512(tmp).slice(0, 32); + signPublicKey = bmcrypto.getPublic(signPrivateKey); + signPublicKey.copy(keysbuf); + + tmp = Buffer.concat([phrasebuf, var_int.encode(encnonce)]); + encPrivateKey = bmcrypto.sha512(tmp).slice(0, 32); + encPublicKey = bmcrypto.getPublic(encPrivateKey); + encPublicKey.copy(keysbuf, 65); + + ripe = bmcrypto.ripemd160(bmcrypto.sha512(keysbuf)); + len = getripelen(ripe); + if (len <= ripelen && checkripelen(len, version)) { + // TODO(Kagami): Do we need to put all these properties or compute + // them manually via ECMA5 getters/setters instead? + opts.signPrivateKey = signPrivateKey; + opts.signPublicKey = signPublicKey; + opts.encPrivateKey = encPrivateKey; + opts.encPublicKey = encPublicKey; + opts.ripe = ripe; + return new Address(opts); + } + signnonce += 2; + encnonce += 2; + }; +}; + module.exports = Address; diff --git a/test.js b/test.js index 0227cbd..7c0207b 100644 --- a/test.js +++ b/test.js @@ -525,7 +525,7 @@ describe("High-level classes", function() { expect(addr.ripe.toString("hex")).to.equal("003ab6655de4bd8c603eba9b00dd5970725fdd56"); }); - it("should allow to generate new Bitmessage address", function() { + it("should allow to create random Bitmessage address", function() { this.timeout(60000); var addr = Address.fromRandom(); expect(addr.version).to.equal(4); @@ -541,6 +541,17 @@ describe("High-level classes", function() { expect(addr2.ripe[0]).to.equal(0); }); + it("should allow to create Bitmessage address from passphrase", function() { + this.timeout(60000); + var addr = Address.fromPassphrase({passphrase: "test"}); + expect(addr.version).to.equal(4); + expect(addr.stream).to.equal(1); + expect(bufferEqual(addr.signPrivateKey, WIF.decode("5JY1CFeeyN4eyfL35guWAuUqu5VLmd7LojtkNP6wmt5msZxxZ57"))).to.be.true; + expect(bufferEqual(addr.encPrivateKey, WIF.decode("5J1oDgZDicNhUgbfzBDQqi2m5jUPnDrfZinnTqEEEaLv63jVFTM"))).to.be.true; + expect(addr.getRipe().toString("hex")).to.equal("00ac14944b00decea5628eb40d0ff4b0f9ee9eca"); + expect(addr.encode()).to.equal("BM-2cWFkyuXXFw6d393RGnin2RpSXj8wxtt6F"); + }); + it("should calculate tag", function() { var addr = Address.decode("2cTux3PGRqHTEH6wyUP2sWeT4LrsGgy63z"); expect(addr.getTag().toString("hex")).to.equal("facf1e3e6c74916203b7f714ca100d4d60604f0917696d0f09330f82f52bed1a");