From b2b660827c2f505b550176f7055af1018260212f Mon Sep 17 00:00:00 2001 From: Kagami Hiiragi Date: Mon, 29 Dec 2014 22:59:18 +0300 Subject: [PATCH] WIF encode/decode --- README.md | 6 +++--- lib/index.js | 3 ++- lib/wif.js | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++ test.js | 39 ++++++++++++++++++++++++++++++++++++ 4 files changed, 100 insertions(+), 4 deletions(-) create mode 100644 lib/wif.js diff --git a/README.md b/README.md index 0ab7325..c291aef 100644 --- a/README.md +++ b/README.md @@ -60,9 +60,9 @@ With the help of browserify `bitmessage` provides different implementations for - [ ] Message - [ ] encode - [ ] decode -- [ ] WIF - - [ ] encode - - [ ] decode +- [x] WIF + - [x] encode + - [x] decode - [ ] Parse PyBitmessage configs - [ ] decode keys.dat - [ ] decode knownnodes.dat diff --git a/lib/index.js b/lib/index.js index ac1e3f4..1045c2c 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,5 +1,5 @@ /** - * Main Bitmessage module. Just reexports all main submodules. + * Bitmessage library entry point. Just reexports common submodules. * @module bitmessage */ @@ -9,3 +9,4 @@ // replace it with other library with the same API. exports.Int64 = require("int64-native"); exports.Address = require("./address"); +exports.wif = require("./wif"); diff --git a/lib/wif.js b/lib/wif.js new file mode 100644 index 0000000..9c62451 --- /dev/null +++ b/lib/wif.js @@ -0,0 +1,56 @@ +/** + * Implement WIF encoding/decoding. + * Reference: + * @module bitmessage/wif + */ + +"use strict"; + +require("es6-promise").polyfill(); +var assert = require("assert"); +var bufferEqual = require("buffer-equal"); +var bs58 = require("bs58"); +var bmcrypto = require("./crypto"); + +// Compute the WIF checksum for the given data. +function getchecksum(data) { + return bmcrypto.sha256(data).then(bmcrypto.sha256).then(function(dhash) { + return dhash.slice(0, 4); + }); +} + +/** + * Decode WIF encoded private key. + * @param {string} input - Input data + * @param {Promise.} A promise than contain private + * key when fulfilled + */ +exports.decode = function(input) { + var bytes; + try { + bytes = bs58.decode(input); + assert(bytes[0] === 0x80, "Bad WIF"); + } catch(e) { + return Promise.reject(e); + } + var data = new Buffer(bytes.slice(0, -4)); + var checksum = new Buffer(bytes.slice(-4)); + return getchecksum(data).then(function(realchecksum) { + assert(bufferEqual(checksum, realchecksum), "Bad checkum"); + return data.slice(1); + }); +}; + +/** + * Convert private key to a WIF. + * @param {Buffer} privateKey - A private key to encode + * @return {Promise.} A promise that contains the + * encoded key when fulfilled + */ +exports.encode = function(privateKey) { + var data = Buffer.concat([new Buffer([0x80]), privateKey]); + return getchecksum(data).then(function(checksum) { + var bytes = Buffer.concat([data, checksum]); + return bs58.encode(bytes); + }); +}; diff --git a/test.js b/test.js index 7d176b8..a58148c 100644 --- a/test.js +++ b/test.js @@ -6,6 +6,7 @@ var allTests = typeof window === "undefined" ? var bitmessage = require("./lib"); var Int64 = bitmessage.Int64; var Address = bitmessage.Address; +var wif = bitmessage.wif; var varint = require("./lib/varint"); var bmcrypto = require("./lib/crypto"); @@ -55,6 +56,44 @@ describe("var_int", function() { }); }); +describe("WIF", function() { + var wifSign = "5JgQ79vTBusc61xYPtUEHYQ38AXKdDZgQ5rFp7Cbb4ZjXUKFZEV"; + var wifEnc = "5K2aL8cnsEWHwHfHnUrPo8QdYyRfoYUBmhAnWY5GTpDLbeyusnE"; + var signPrivateKey = Buffer("71c95d26c716a5e85e9af9efe26fb5f744dc98005a13d05d23ee92c77e038d9f", "hex"); + var encPrivateKey = Buffer("9f9969c93c2d186787a7653f70e49be34c03c4a853e6ad0c867db0946bc433c6", "hex"); + + it("should decode", function() { + return wif.decode(wifSign) + .then(function(key1) { + expect(Buffer.isBuffer(key1)).to.be.true; + expect(key1.length).to.equal(32); + expect(key1.toString("hex")).to.equal(signPrivateKey.toString("hex")); + return wif.decode(wifEnc).then(function(key2) { + expect(Buffer.isBuffer(key2)).to.be.true; + expect(key2.length).to.equal(32); + expect(key2.toString("hex")).to.equal(encPrivateKey.toString("hex")); + return { + version: 4, + stream: 1, + signPrivateKey: key1, + encPrivateKey: key2, + }; + }); + }).then(Address.encode).then(function(str) { + expect(str).to.equal("BM-2cTux3PGRqHTEH6wyUP2sWeT4LrsGgy63z"); + }); + }); + + it("should encode", function() { + return wif.encode(signPrivateKey).then(function(wif1) { + expect(wif1).to.equal(wifSign); + return wif.encode(encPrivateKey); + }).then(function(wif2) { + expect(wif2).to.equal(wifEnc); + }); + }); +}); + describe("Crypto", function() { it("should implement SHA-512 hash", function() { return bmcrypto.sha512(Buffer("test")).then(function(res) {