diff --git a/docs/address.js.html b/docs/address.js.html index 6d0010b..12dd2f0 100644 --- a/docs/address.js.html +++ b/docs/address.js.html @@ -34,12 +34,14 @@ "use strict"; -require("object.assign").shim(); -var assert = require("assert"); +var objectAssign = Object.assign || require("object-assign"); var bufferEqual = require("buffer-equal"); var bs58 = require("bs58"); +var assert = require("./_util").assert; var var_int = require("./structs").var_int; +var PubkeyBitfield = require("./structs").PubkeyBitfield; var bmcrypto = require("./crypto"); +var popkey = require("./_util").popkey; /** * Create a new Bitmessage address object. @@ -51,29 +53,48 @@ function Address(opts) { if (!(this instanceof Address)) { return new Address(opts); } - opts = opts || {}; - Object.assign(this, opts); - this.version = this.version || 4; + opts = objectAssign({}, opts); + // Pull out version right away because it may be needed in setters. + this.version = popkey(opts, "version") || 4; assert(this.version <= 4, "Version too high"); assert(this.version >= 1, "Version too low"); - this.stream = this.stream || 1; - if (this.ripe) { - assertripelen(getripelen(this.ripe), this.version, this.ripe); - if (this.ripe.length < 20) { - var fullripe = new Buffer(20); - fullripe.fill(0); - this.ripe.copy(fullripe, 20 - this.ripe.length); - this.ripe = fullripe; - } - } + // Set defaults. + opts.stream = opts.stream || 1; + opts.behavior = opts.behavior || + PubkeyBitfield().set(PubkeyBitfield.DOES_ACK); + // Merge remained values. + objectAssign(this, opts); } +/** + * Create a copy of the address object. + * @return {Address} Cloned address. + */ +Address.prototype.clone = function() { + return new Address(this); +}; + +/** + * Test if given object is an Address instance. + * NOTE: Implementation is just simple `instanceof` but it improves + * readability and consistent with `isArray`, `isBuffer`, etc. + * @param {Object} obj - Given object + * @return {boolean} + */ +Address.isAddress = function(obj) { + return obj instanceof Address; +}; + /** * Parse Bitmessage address into address object. - * @param {String} str - Address string (with or without `BM-` prefix) + * @param {string} str - Address string (with or without `BM-` prefix) * @return {Address} Decoded address object. */ Address.decode = function(str) { + if (Address.isAddress(str)) { + return str; + } + str = str.trim(); if (str.slice(0, 3) === "BM-") { str = str.slice(3); @@ -82,7 +103,7 @@ Address.decode = function(str) { var bytes = bs58.decode(str); var data = new Buffer(bytes.slice(0, -4)); var checksum = new Buffer(bytes.slice(-4)); - assert(bufferEqual(checksum, getchecksum(data)), "Bad checkum"); + assert(bufferEqual(checksum, getaddrchecksum(data)), "Bad checkum"); var decoded = var_int.decode(data); var version = decoded.value; @@ -100,55 +121,79 @@ Address.decode = function(str) { }; // Compute the Bitmessage checksum for the given data. -function getchecksum(data) { +function getaddrchecksum(data) { return bmcrypto.sha512(bmcrypto.sha512(data)).slice(0, 4); } -// Get RIPEMD160(SHA512(SIGN_PUBLIC_KEY || ENC_PUBLIC_KEY)). -// Arguments could be either private or public keys. Private keys are -// **always** 32 bytes in length. -function keys2ripe(signKey, encKey) { - var signPublicKey, encPublicKey; - if (signKey.length === 32) { - signPublicKey = bmcrypto.getPublic(signKey); - } else { - signPublicKey = signKey; - } - if (encKey.length === 32) { - encPublicKey = bmcrypto.getPublic(encKey); - } else { - encPublicKey = encKey; - } - var concat = Buffer.concat([signPublicKey, encPublicKey]); - return bmcrypto.ripemd160(bmcrypto.sha512(concat)); +/** + * Get the ripe hash of the address without prefix zeroes. + * @return {Buffer} A short ripe hash. + */ +Address.prototype.getShortRipe = function() { + var ripe = this.ripe; + return ripe.slice(20 - getripelen(ripe)); +}; + +function getaddrhash(addr) { + var dataToHash = Buffer.concat([ + var_int.encode(addr.version), + var_int.encode(addr.stream), + addr.ripe, + ]); + return bmcrypto.sha512(dataToHash); } /** - * Calculate the ripe hash of the address. - * @param {?Object} opts - Options - * @return {Buffer} Resulting ripe hash. + * Calculate the encryption key used to encrypt/decrypt + * [pubkey]{@link module:bitmessage/objects.pubkey} objects. + * @return {Buffer} A 32-byte private key. */ -Address.prototype.getRipe = function(opts) { - var ripe; - opts = opts || {}; - if (this.ripe) { - ripe = this.ripe; +Address.prototype.getPubkeyPrivateKey = function() { + return bmcrypto.sha512(getaddrhash(this)).slice(0, 32); +}; + +/** + * Calculate the corresponding public key for encryption key used to + * encrypt/decrypt + * [pubkey]{@link module:bitmessage/objects.pubkey} objects. + * @return {Buffer} A 65-byte public key. + */ +Address.prototype.getPubkeyPublicKey = function() { + return bmcrypto.getPublic(this.getPubkeyPrivateKey()); +}; + +/** + * Calculate the encryption key used to encrypt/decrypt + * [broadcast]{@link module:bitmessage/objects.broadcast} objects. + * @return {Buffer} A 32-byte private key. + */ +Address.prototype.getBroadcastPrivateKey = function() { + if (this.version >= 4) { + return bmcrypto.sha512(getaddrhash(this)).slice(0, 32); } else { - var signKey = this.signPrivateKey || this.signPublicKey; - assert(signKey, "No signing key"); - var encKey = this.encPrivateKey || this.encPublicKey; - assert(encKey, "No encryption key"); - ripe = keys2ripe(signKey, encKey); - } - var ripelen = getripelen(ripe); - assertripelen(ripelen, this.version, ripe); - if (opts.short) { - return ripe.slice(20 - ripelen); - } else { - return ripe; + return getaddrhash(this).slice(0, 32); } }; +/** + * Calculate the corresponding public key for encryption key used to + * encrypt/decrypt + * [broadcast]{@link module:bitmessage/objects.broadcast} objects. + * @return {Buffer} A 65-byte public key. + */ +Address.prototype.getBroadcastPublicKey = function() { + return bmcrypto.getPublic(this.getBroadcastPrivateKey()); +}; + +/** + * Calculate the address tag. + * @return {Buffer} A 32-byte address tag. + */ +Address.prototype.getTag = function() { + return bmcrypto.sha512(getaddrhash(this)).slice(32); +}; + + // Get truncated ripe hash length. function getripelen(ripe) { var zeroes = 0; @@ -198,22 +243,15 @@ function checkripelen(ripelen, version) { * @return {string} Address string. */ Address.prototype.encode = function() { - var ripe = this.getRipe({short: true}); var data = Buffer.concat([ var_int.encode(this.version), var_int.encode(this.stream), - ripe, + this.getShortRipe(), ]); - var addr = Buffer.concat([data, getchecksum(data)]); + var addr = Buffer.concat([data, getaddrchecksum(data)]); return "BM-" + bs58.encode(addr); }; -function popkey(obj, key) { - var value = obj[key]; - delete obj[key]; - return value; -} - /** * Create new Bitmessage address from random encryption and signing * private keys. @@ -221,43 +259,159 @@ function popkey(obj, key) { * @return {Address} Generated address object. */ Address.fromRandom = function(opts) { - opts = Object.assign({}, opts); + opts = objectAssign({}, opts); var version = opts.version = opts.version || 4; - var ripelen = popkey(opts, "ripelen") || 19; + var ripelen = popkey(opts, "ripeLength") || 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)) - ) { - // TODO(Kagami): Do we need to put all these properties or compute - // them manually via ECMA5 getters/setters instead? + len = getripelen(ripe); + if (len <= ripelen && checkripelen(len, version)) { opts.signPrivateKey = signPrivateKey; - opts.signPublicKey = signPublicKey; opts.encPrivateKey = encPrivateKey; - opts.encPublicKey = encPublicKey; - opts.ripe = ripe; return new Address(opts); } } }; +/** + * Create new Bitmessage address from passphrase. + * @param {?Object} opts - Address options + * @return {Address} Generated address object. + */ +Address.fromPassphrase = function(opts) { + if (typeof opts === "string") { + opts = {passphrase: opts}; + } else { + opts = objectAssign({}, opts); + } + var version = opts.version = opts.version || 4; + var ripelen = popkey(opts, "ripeLength") || 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)) { + opts.signPrivateKey = signPrivateKey; + opts.encPrivateKey = encPrivateKey; + return new Address(opts); + } + signnonce += 2; + encnonce += 2; + } +}; + +Object.defineProperty(Address.prototype, "signPrivateKey", { + get: function() { + return this._signPrivateKey; + }, + set: function(signPrivateKey) { + this._signPrivateKey = signPrivateKey; + // Invalidate cached values; + delete this._signPublicKey; + delete this._ripe; + }, +}); + +Object.defineProperty(Address.prototype, "signPublicKey", { + get: function() { + if (this._signPublicKey) { + return this._signPublicKey; + } else if (this.signPrivateKey) { + this._signPublicKey = bmcrypto.getPublic(this.signPrivateKey); + return this._signPublicKey; + } else { + throw new Error("No signing key"); + } + }, + set: function(signPublicKey) { + this._signPublicKey = signPublicKey; + }, +}); + +Object.defineProperty(Address.prototype, "encPrivateKey", { + get: function() { + return this._encPrivateKey; + }, + set: function(encPrivateKey) { + this._encPrivateKey = encPrivateKey; + // Invalidate cached values; + delete this._encPublicKey; + delete this._ripe; + }, +}); + +Object.defineProperty(Address.prototype, "encPublicKey", { + get: function() { + if (this._encPublicKey) { + return this._encPublicKey; + } else if (this.encPrivateKey) { + this._encPublicKey = bmcrypto.getPublic(this.encPrivateKey); + return this._encPublicKey; + } else { + throw new Error("No encryption key"); + } + }, + set: function(encPublicKey) { + this._encPublicKey = encPublicKey; + }, +}); + +Object.defineProperty(Address.prototype, "ripe", { + get: function() { + if (this._ripe) { + return this._ripe; + } + var dataToHash = Buffer.concat([this.signPublicKey, this.encPublicKey]); + this._ripe = bmcrypto.ripemd160(bmcrypto.sha512(dataToHash)); + return this._ripe; + }, + set: function(ripe) { + assertripelen(getripelen(ripe), this.version, ripe); + if (ripe.length < 20) { + var fullripe = new Buffer(20); + fullripe.fill(0); + ripe.copy(fullripe, 20 - ripe.length); + ripe = fullripe; + } + this._ripe = ripe; + }, +}); + module.exports = Address; @@ -269,13 +423,13 @@ module.exports = Address;
diff --git a/docs/crypto.js.html b/docs/crypto.js.html index 626996d..6ae5f1c 100644 --- a/docs/crypto.js.html +++ b/docs/crypto.js.html @@ -28,22 +28,26 @@
/**
  * Isomorphic Bitmessage crypto module. Reexports platform-dependent
- * implementations and and also some common routines.
+ * implementations and also some common routines.
  * @module bitmessage/crypto
  */
 
 "use strict";
 
 var eccrypto = require("eccrypto");
+var assert = require("./_util").assert;
 var platform = require("./platform");
 
+var promise = platform.promise;
+
 /**
- * Calculate SHA-512 hash.
+ * Calculate SHA-1 hash.
  * @param {Buffer} buf - Input data
  * @return {Buffer} Resulting hash.
  * @function
+ * @static
  */
-exports.sha512 = platform.sha512;
+var sha1 = exports.sha1 = platform.sha1;
 
 /**
  * Calculate SHA-256 hash.
@@ -53,6 +57,14 @@ exports.sha512 = platform.sha512;
  */
 exports.sha256 = platform.sha256;
 
+/**
+ * Calculate SHA-512 hash.
+ * @param {Buffer} buf - Input data
+ * @return {Buffer} Resulting hash.
+ * @function
+ */
+exports.sha512 = platform.sha512;
+
 /**
  * Calculate RIPEMD-160 hash.
  * @param {Buffer} buf - Input data
@@ -78,12 +90,119 @@ exports.getPrivate = function() {
 };
 
 /**
- * Generate public key for a given private key.
- * @param {Buffer} privateKey - Private key
- * @return {Buffer} Public key.
+ * Generate public key for the given private key.
+ * @param {Buffer} privateKey - A 32-byte private key
+ * @return {Buffer} A 65-byte (uncompressed) public key.
  * @function
  */
 exports.getPublic = eccrypto.getPublic;
+
+/**
+ * Sign message using ecdsa-with-sha1 scheme.
+ * @param {Buffer} privateKey - A 32-byte private key
+ * @param {Buffer} msg - The message being signed
+ * @return {Promise.<Buffer>} A promise that contains signature in DER
+ * format when fulfilled.
+ */
+exports.sign = function(privateKey, msg) {
+  var hash = sha1(msg);
+  return eccrypto.sign(privateKey, hash);
+};
+
+/**
+ * Verify signature using ecdsa-with-sha1 scheme.
+ * @param {Buffer} publicKey - A 65-byte public key
+ * @param {Buffer} msg - The message being verified
+ * @param {Buffer} sig - The signature in DER format
+ * @return {Promise.<undefined>} A promise that resolves on correct
+ * signature and rejects on bad key or signature.
+ */
+exports.verify = function(publicKey, msg, sig) {
+  var hash = sha1(msg);
+  return eccrypto.verify(publicKey, hash, sig);
+};
+
+var SECP256K1_TYPE = 714;
+
+// We define this structure here to avoid circular imports. However we
+// rexport and document it in `structs` module for consistency.
+var encrypted = exports.encrypted = {
+  decode: function(buf) {
+    assert(buf.length >= 118, "Buffer is too small");
+    assert(buf.readUInt16BE(16, true) === SECP256K1_TYPE, "Bad curve type");
+    assert(buf.readUInt16BE(18, true) === 32, "Bad Rx length");
+    assert(buf.readUInt16BE(52, true) === 32, "Bad Ry length");
+    var iv = new Buffer(16);
+    buf.copy(iv, 0, 0, 16);
+    var ephemPublicKey = new Buffer(65);
+    ephemPublicKey[0] = 0x04;
+    buf.copy(ephemPublicKey, 1, 20, 52);
+    buf.copy(ephemPublicKey, 33, 54, 86);
+    // NOTE(Kagami): We do copy instead of slice to protect against
+    // possible source buffer modification by user.
+    var ciphertext = new Buffer(buf.length - 118);
+    buf.copy(ciphertext, 0, 86, buf.length - 32);
+    var mac = new Buffer(32);
+    buf.copy(mac, 0, buf.length - 32);
+    return {
+      iv: iv,
+      ephemPublicKey: ephemPublicKey,
+      ciphertext: ciphertext,
+      mac: mac,
+    };
+  },
+
+  encode: function(opts) {
+    assert(opts.iv.length === 16, "Bad IV");
+    assert(opts.ephemPublicKey.length === 65, "Bad public key");
+    assert(opts.mac.length === 32, "Bad MAC");
+    // 16 + 2 + 2 + 32 + 2 + 32 + ? + 32
+    var buf = new Buffer(118 + opts.ciphertext.length);
+    opts.iv.copy(buf);
+    buf.writeUInt16BE(SECP256K1_TYPE, 16, true);  // Curve type
+    buf.writeUInt16BE(32, 18, true);  // Rx length
+    opts.ephemPublicKey.copy(buf, 20, 1, 33);  // Rx
+    buf.writeUInt16BE(32, 52, true);  // Ry length
+    opts.ephemPublicKey.copy(buf, 54, 33);  // Ry
+    opts.ciphertext.copy(buf, 86);
+    opts.mac.copy(buf, 86 + opts.ciphertext.length);
+    return buf;
+  },
+};
+
+/**
+ * Encrypt message for given recepient's public key.
+ * @param {Buffer} publicKeyTo - Recipient's public key (65 bytes)
+ * @param {Buffer} msg - The message being encrypted
+ * @param {?{?iv: Buffer, ?ephemPrivateKey: Buffer}} opts - You may also
+ * specify initialization vector (16 bytes) and ephemeral private key
+ * (32 bytes) to get deterministic results.
+ * @return {Promise.<Buffer>} - A promise that resolves with the buffer
+ * in `encrypted` format successful encryption and rejects on failure.
+ */
+// TODO(Kagami): Properly document `opts`. Documenting multiple
+// function arguments with options object at the end for now gives
+// strange results (probably a bug in jsdoc).
+exports.encrypt = function(publicKeyTo, msg, opts) {
+  return eccrypto.encrypt(publicKeyTo, msg, opts).then(function(encObj) {
+    return encrypted.encode(encObj);
+  });
+};
+
+/**
+ * Decrypt message using given private key.
+ * @param {Buffer} privateKey - A 32-byte private key of recepient of
+ * the mesage
+ * @param {Buffer} buf - Encrypted data
+ * @return {Promise.<Buffer>} - A promise that resolves with the
+ * plaintext on successful decryption and rejects on failure.
+ */
+exports.decrypt = function(privateKey, buf) {
+  return new promise(function(resolve) {
+    var encObj = encrypted.decode(buf);
+    resolve(eccrypto.decrypt(privateKey, encObj));
+  });
+};
 
@@ -94,13 +213,13 @@ exports.getPublic = eccrypto.getPublic;
diff --git a/docs/index.html b/docs/index.html index 3adc910..02e6317 100644 --- a/docs/index.html +++ b/docs/index.html @@ -43,73 +43,64 @@
-

bitmessage Build Status

JavaScript Bitmessage library for both browserify and node. The goal of this project is to implement Bitmessage protocol v3 for both platforms at the maximum possible level (we still can't create TCP connections or listen for incoming connections in the Browser but the Proof of work and crypto is fully doable).

+

bitmessage Build Status

NPM

+

JavaScript Bitmessage library for both browserify and node. The goal of this project is to implement Bitmessage protocol v3 for both platforms at the maximum possible level (we still can't create TCP connections or listen for incoming connections in the Browser but the Proof of work and crypto is fully doable).

Public library API is currently in alpha stage, breaking changes are very likely to happen.

API documentation is available here.

References

Feature matrix (both Browser and Node)

    -
  • [ ] crypto
      -
    • [x] SHA-512
    • +
    • [x] Crypto
        +
      • [x] SHA-1
      • [x] SHA-256
      • +
      • [x] SHA-512
      • [x] RIPEMD-160
      • [x] PRNG
      • [x] ECC keys manipulation
      • [x] ECDSA
      • -
      • [ ] ECDH
      • -
      • [ ] ECIES
      • -
      • [ ] AES-256-CBC
      • -
      • [ ] HMAC-SHA-256
      • +
      • [x] ECDH
      • +
      • [x] ECIES
      • +
      • [x] AES-256-CBC
      • +
      • [x] HMAC-SHA-256
    • -
    • [ ] Common structures
        -
      • [ ] message
      • +
      • [x] Common structures
          +
        • [x] message
        • +
        • [x] object
        • [x] var_int
        • [x] var_str
        • [x] var_int_list
        • -
        • [ ] net_addr
        • -
        • [ ] inv_vect
        • -
        • [ ] encrypted
        • -
        • [ ] encoding
        • -
        • [ ] bitfield
        • +
        • [x] net_addr
        • +
        • [x] inv_vect
        • +
        • [x] encrypted
        • +
        • [x] service features
        • +
        • [x] pubkey features
      • -
      • [ ] Message types
          -
        • [ ] version
        • -
        • [ ] verack
        • -
        • [ ] addr
        • -
        • [ ] inv
        • -
        • [ ] getdata
        • -
        • [ ] error
        • -
        • [ ] object
        • +
        • [x] Message types
            +
          • [x] version
          • +
          • [x] verack
          • +
          • [x] addr
          • +
          • [x] inv
          • +
          • [x] getdata
          • +
          • [x] error
        • -
        • [ ] Object types
            -
          • [ ] getpubkey
          • -
          • [ ] pubkey
          • -
          • [ ] msg
          • -
          • [ ] broadcast
          • +
          • [x] Object types
              +
            • [x] getpubkey
            • +
            • [x] pubkey
            • +
            • [x] msg
            • +
            • [x] broadcast
          • [x] WIF
          • -
          • [ ] POW
          • -
          • [ ] High-level classes
              -
            • [ ] Address
                -
              • [x] encode
              • -
              • [x] decode
              • -
              • [x] getRipe
              • -
              • [x] fromRandom
              • -
              • [ ] fromPassphrase
              • -
              -
            • -
            • [ ] Message
                -
              • [ ] encrypt
              • -
              • [ ] decrypt
              • -
              -
            • +
            • [x] POW
            • +
            • [x] High-level classes
                +
              • [x] Address
              • +
              • [x] UserAgent
            • [ ] Parse PyBitmessage configs
                @@ -119,19 +110,21 @@
            -

            Feature matrix (Node.js only)

              -
            • [ ] Network
                +

                Network feature matrix (Node.js only)

                • [ ] Bootstrap
                • [ ] Connect to the network
                • [ ] Accept connections
                - -
              -

              Usage

              // Generate a new random Bitmessage identity.
              -var Address = require("bitmessage").Address;
              -var addr = Address.fromRandom();
              -console.log("New random Bitmessage address:", addr.encode());

              License

              bitmessage - JavaScript Bitmessage library

              -

              Written in 2014 by Kagami Hiiragi kagami@genshiken.org

              +

              Usage

              Address

              var Address = require("bitmessage").Address;
              +
              +// Generate a new random Bitmessage identity.
              +var addr1 = Address.fromRandom();
              +console.log("New random Bitmessage address:", addr1.encode());
              +
              +// Or create it from passphrase.
              +var addr2 = Address.fromPassphrase("test");
              +console.log("Deterministic Bitmessage address:", addr2.encode());

              License

              bitmessage - JavaScript Bitmessage library

              +

              Written in 2014 by Kagami Hiiragi kagami@genshiken.org

              To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. This software is distributed without any warranty.

              You should have received a copy of the CC0 Public Domain Dedication along with this software. If not, see .

@@ -144,13 +137,13 @@ console.log("New random Bitmessage address:", addr.encode());
diff --git a/docs/index.js.html b/docs/index.js.html index 5a8ab91..9e98b79 100644 --- a/docs/index.js.html +++ b/docs/index.js.html @@ -33,6 +33,12 @@ "use strict"; +/** + * Current protocol version. + * @constant {number} + */ +exports.PROTOCOL_VERSION = require("./_util").PROTOCOL_VERSION; + /** [Common structures.]{@link module:bitmessage/structs} */ exports.structs = require("./structs"); /** [Messages.]{@link module:bitmessage/messages} */ @@ -47,6 +53,8 @@ exports.POW = require("./pow"); /** [Working with addresses.]{@link module:bitmessage/address} */ exports.Address = require("./address"); +/** [User agent.]{@link module:bitmessage/user-agent} */ +exports.UserAgent = require("./user-agent"); @@ -57,13 +65,13 @@ exports.Address = require("./address");
diff --git a/docs/messages.js.html b/docs/messages.js.html index 5ab3849..f9f63fc 100644 --- a/docs/messages.js.html +++ b/docs/messages.js.html @@ -29,8 +29,426 @@
/**
  * Working with messages.
  * @see {@link https://bitmessage.org/wiki/Protocol_specification#Message_types}
+ * @see {@link https://bitmessage.org/wiki/Protocol_specification_v3#Message_types}
+ * @see {@link https://bitmessage.org/Bitmessage%20Technical%20Paper.pdf}
  * @module bitmessage/messages
  */
+// TODO(Kagami): Document object-like params.
+
+"use strict";
+
+var objectAssign = Object.assign || require("object-assign");
+var assert = require("./_util").assert;
+var structs = require("./structs");
+var UserAgent = require("./user-agent");
+var util = require("./_util");
+
+var message = structs.message;
+var ServicesBitfield = structs.ServicesBitfield;
+
+/**
+ * Try to get command of the given encoded message.
+ * Note that this function doesn't do any validation because it is
+ * already provided by
+ * [message.decode]{@link module:bitmessage/structs.message.decode}
+ * routine. Normally you call this for each incoming message and then
+ * call decode function of the appropriate message handler.
+ * @param {Buffer} buf - Buffer that starts with encoded message
+ * @return {?string} Message's command if any.
+ */
+exports.getCommand = function(buf) {
+  if (buf.length < 16) {
+    return;
+  }
+  var command = buf.slice(4, 16);
+  var firstNonNull = 0;
+  for (var i = 11; i >=0; i--) {
+    if (command[i] !== 0) {
+      firstNonNull = i + 1;
+      break;
+    }
+  }
+  return command.slice(0, firstNonNull).toString("ascii");
+};
+
+/**
+ * `version` message.
+ * @see {@link https://bitmessage.org/wiki/Protocol_specification#version}
+ * @namespace
+ * @static
+ */
+var version = exports.version = {
+  /**
+   * Random nonce used to detect connections to self.
+   * @const {Buffer}
+   */
+  NONCE: new Buffer("20bde0a3355dad78", "hex"),
+
+  /**
+   * Decode `version` message.
+   * NOTE: `nonce` is copied.
+   * @param {Buffer} buf - Message
+   * @return {Object} Decoded `version` structure.
+   */
+  decode: function(buf) {
+    var decoded = message.decode(buf);
+    assert(decoded.command === "version", "Bad command");
+    return version.decodePayload(decoded.payload);
+  },
+
+  /**
+   * Decode `version` message payload.
+   * NOTE: `nonce` is copied.
+   * @param {Buffer} buf - Message payload
+   * @return {Object} Decoded `version` structure.
+   */
+  decodePayload: function(buf) {
+    // 4 + 8 + 8 + 26 + 26 + 8 + (1+) + (1+)
+    assert(buf.length >= 82, "Buffer is too small");
+    var protoVersion = buf.readUInt32BE(0, true);
+    var services = ServicesBitfield(buf.slice(4, 12), {copy: true});
+    var time = util.readTime64BE(buf, 12);
+    var short = {short: true};
+    var addrRecv = structs.net_addr.decode(buf.slice(20, 46), short);
+    var addrFrom = structs.net_addr.decode(buf.slice(46, 72), short);
+    var nonce = new Buffer(8);
+    buf.copy(nonce, 0, 72, 80);
+    var decodedUa = UserAgent.decode(buf.slice(80));
+    var decodedStreamNumbers = structs.var_int_list.decode(decodedUa.rest);
+    return {
+      version: protoVersion,
+      services: services,
+      time: time,
+      remoteHost: addrRecv.host,
+      remotePort: addrRecv.port,
+      port: addrFrom.port,
+      nonce: nonce,
+      userAgent: decodedUa.str,
+      streamNumbers: decodedStreamNumbers.list,
+      // NOTE(Kagami): Real data length. It may be some gap between end
+      // of stream numbers list and end of payload:
+      //     [payload..............[stream numbers]xxxx]
+      // We are currently ignoring that.
+      length: 80 + decodedUa.length + decodedStreamNumbers.length,
+    };
+  },
+
+  /**
+   * Encode `version` message.
+   * @param {Object} opts - Version options
+   * @return {Buffer} Encoded message.
+   */
+  encode: function(opts) {
+    var payload = version.encodePayload(opts);
+    return message.encode("version", payload);
+  },
+
+  /**
+   * Encode `version` message payload.
+   * @param {Object} opts - Version options
+   * @return {Buffer} Encoded payload.
+   */
+  encodePayload: function(opts) {
+    // Deal with default options.
+    var services = opts.services ||
+                   ServicesBitfield().set(ServicesBitfield.NODE_NETWORK);
+    var time = opts.time || new Date();
+    var nonce = opts.nonce || version.NONCE;
+    assert(nonce.length === 8, "Bad nonce");
+    var userAgent = opts.userAgent || UserAgent.SELF;
+    var streamNumbers = opts.streamNumbers || [1];
+    // Start encoding.
+    var protoVersion = new Buffer(4);
+    protoVersion.writeUInt32BE(util.PROTOCOL_VERSION, 0);
+    var addrRecv = structs.net_addr.encode({
+      services: services,
+      host: opts.remoteHost,
+      port: opts.remotePort,
+      short: true,
+    });
+    var addrFrom = structs.net_addr.encode({
+      services: services,
+      host: "127.0.0.1",
+      port: opts.port,
+      short: true,
+    });
+    return Buffer.concat([
+      protoVersion,
+      services.buffer,
+      util.writeTime64BE(null, time),
+      addrRecv,
+      addrFrom,
+      nonce,
+      UserAgent.encode(userAgent),
+      structs.var_int_list.encode(streamNumbers),
+    ]);
+  },
+};
+
+/**
+ * `addr` message. Provide information on known nodes of the network.
+ * @see {@link https://bitmessage.org/wiki/Protocol_specification#addr}
+ * @namespace
+ * @static
+ */
+var addr = exports.addr = {
+  /**
+   * Decode `addr` message.
+   * @param {Buffer} buf - Message
+   * @return {Object} Decoded `addr` structure.
+   */
+  decode: function(buf) {
+    var decoded = message.decode(buf);
+    assert(decoded.command === "addr", "Bad command");
+    return addr.decodePayload(decoded.payload);
+  },
+
+  /**
+   * Decode `addr` message payload.
+   * @param {Buffer} buf - Message payload
+   * @return {Object} Decoded `addr` structure.
+   */
+  decodePayload: function(buf) {
+    var decoded = structs.var_int.decode(buf);
+    var listLength = decoded.value;
+    assert(listLength <= 1000, "Too many address entires");
+    var length = decoded.length + listLength * 38;
+    assert(buf.length >= length, "Buffer is too small");
+    var rest = decoded.rest;
+    var addrs = new Array(listLength);
+    for (var i = 0; i < listLength; i++) {
+      addrs[i] = structs.net_addr.decode(rest.slice(i*38, (i+1)*38));
+    }
+    return {
+      addrs: addrs,
+      // Real data length.
+      length: length,
+    };
+  },
+
+  /**
+   * Encode `addr` message.
+   * @param {Object[]} addrs - Network addresses
+   * @return {Buffer} Encoded message.
+   */
+  encode: function(addrs) {
+    var payload = addr.encodePayload(addrs);
+    return message.encode("addr", payload);
+  },
+
+  /**
+   * Encode `addr` message payload.
+   * @param {Object[]} addrs - Network addresses
+   * @return {Buffer} Encoded payload.
+   */
+  encodePayload: function(addrs) {
+    assert(addrs.length <= 1000, "Too many address entires");
+    var addrsBuf = Buffer.concat(addrs.map(structs.net_addr.encode));
+    return Buffer.concat([structs.var_int.encode(addrs.length), addrsBuf]);
+  },
+};
+
+/**
+ * `inv` message. Allows a node to advertise its knowledge of one or
+ * more objects.
+ * @see {@link https://bitmessage.org/wiki/Protocol_specification#inv}
+ * @namespace
+ * @static
+ */
+var inv = exports.inv = {
+  /**
+   * Decode `inv` message.
+   * @param {Buffer} buf - Message
+   * @return {Object} Decoded `inv` structure.
+   */
+  decode: function(buf) {
+    var decoded = message.decode(buf);
+    assert(decoded.command === "inv", "Bad command");
+    return inv.decodePayload(decoded.payload);
+  },
+
+  /**
+   * Decode `inv` message payload.
+   * @param {Buffer} buf - Message payload
+   * @return {Object} Decoded `inv` structure.
+   */
+  decodePayload: function(buf) {
+    var decoded = structs.var_int.decode(buf);
+    var listLength = decoded.value;
+    assert(listLength <= 50000, "Too many inventory entires");
+    var length = decoded.length + listLength * 32;
+    assert(buf.length >= length, "Buffer is too small");
+    var rest = decoded.rest;
+    var inventory = new Array(listLength);
+    for (var i = 0; i < listLength; i++) {
+      inventory[i] = rest.slice(i*32, (i+1)*32);
+    }
+    return {
+      inventory: inventory,
+      // Real data length.
+      length: length,
+    };
+  },
+
+  /**
+   * Encode `inv` message.
+   * @param {Buffer[]} inventory - Inventory vector list
+   * @return {Buffer} Encoded message.
+   */
+  encode: function(inventory) {
+    var payload = inv.encodePayload(inventory);
+    return message.encode("inv", payload);
+  },
+
+  /**
+   * Encode `inv` message payload.
+   * @param {Buffer[]} inventory - Inventory vector list
+   * @return {Buffer} Encoded payload.
+   */
+  encodePayload: function(inventory) {
+    assert(inventory.length <= 50000, "Too many inventory entires");
+    var invBuf = Buffer.concat(inventory);
+    return Buffer.concat([structs.var_int.encode(inventory.length), invBuf]);
+  },
+};
+
+/**
+ * `getdata` message. `getdata` is used in response to an
+ * [inv]{@link module:bitmessage/messages.inv} message to retrieve the
+ * content of a specific object after filtering known elements.
+ * @see {@link https://bitmessage.org/wiki/Protocol_specification#getdata}
+ * @namespace
+ */
+exports.getdata = objectAssign({}, inv, {
+  /**
+   * Decode `getdata` message.
+   * @param {Buffer} buf - Message
+   * @return {Object} Decoded `getdata` structure.
+   * @memberof module:bitmessage/messages.getdata
+   */
+  decode: function(buf) {
+    var decoded = message.decode(buf);
+    assert(decoded.command === "getdata", "Bad command");
+    return inv.decodePayload(decoded.payload);
+  },
+  /**
+   * Encode `getdata` message.
+   * @param {Buffer[]} inventory - Inventory vector list
+   * @return {Buffer} Encoded message.
+   * @memberof module:bitmessage/messages.getdata
+   */
+  encode: function(inventory) {
+    var payload = inv.encodePayload(inventory);
+    return message.encode("getdata", payload);
+  },
+  /**
+   * Decode `getdata` message payload.
+   * @param {Buffer} buf - Message payload
+   * @return {Object} Decoded `inv` structure.
+   * @function decodePayload
+   * @memberof module:bitmessage/messages.getdata
+   */
+  /**
+   * Encode `getdata` message payload.
+   * @param {Buffer[]} inventory - Inventory vector list
+   * @return {Buffer} Encoded payload.
+   * @function encodePayload
+   * @memberof module:bitmessage/messages.getdata
+   */
+});
+
+/**
+ * `error` message.
+ * @see {@link https://bitmessage.org/wiki/Protocol_specification_v3#error}
+ * @namespace
+ * @static
+ */
+var error = exports.error = {
+  /**
+   * Just a warning.
+   * @constant {number}
+   */
+  WARNING: 0,
+
+  /**
+   * It's an error, something was going wrong (e.g. an object got lost).
+   * @constant {number}
+   */
+  ERROR: 1,
+
+  /**
+   * It's a fatal error. The node will drop the line for that error and
+   * maybe ban you for some time.
+   * @constant {number}
+   */
+  FATAL: 2,
+
+  /**
+   * Decode `error` message.
+   * @param {Buffer} buf - Message
+   * @return {Object} Decoded `error` structure.
+   */
+  decode: function(buf) {
+    var decoded = message.decode(buf);
+    assert(decoded.command === "error", "Bad command");
+    return error.decodePayload(decoded.payload);
+  },
+
+  /**
+   * Decode `error` message payload.
+   * @param {Buffer} buf - Message payload
+   * @return {Object} Decoded `error` structure.
+   */
+  decodePayload: function(buf) {
+    assert(buf.length >= 4, "Buffer is too small");
+    var decodedFatal = structs.var_int.decode(buf);
+    var decodedBanTime = structs.var_int.decode(decodedFatal.rest);
+    var decodedVector = structs.var_str.decode(decodedBanTime.rest);
+    var decodedErrorText = structs.var_str.decode(decodedVector.rest);
+    var length = (
+      decodedFatal.length +
+      decodedBanTime.length +
+      decodedVector.length +
+      decodedErrorText.length
+    );
+    return {
+      fatal: decodedFatal.value,
+      banTime: decodedBanTime.value,
+      vector: decodedVector.str,
+      errorText: decodedErrorText.str,
+      // Real data length.
+      length: length,
+    };
+  },
+
+  /**
+   * Encode `error` message.
+   * @param {Object} opts - Error options
+   * @return {Buffer} Encoded message.
+   */
+  encode: function(opts) {
+    var payload = error.encodePayload(opts);
+    return message.encode("error", payload);
+  },
+
+  /**
+   * Encode `error` message payload.
+   * @param {Object} opts - Error options
+   * @return {Buffer} Encoded payload.
+   */
+  encodePayload: function(opts) {
+    var fatal = opts.fatal || error.WARNING;
+    var banTime = opts.banTime || 0;
+    var vector = opts.vector || "";
+    var errorText = opts.errorText || "";
+    return Buffer.concat([
+      structs.var_int.encode(fatal),
+      structs.var_int.encode(banTime),
+      structs.var_str.encode(vector),
+      structs.var_str.encode(errorText),
+    ]);
+  },
+};
 
@@ -41,13 +459,13 @@
diff --git a/docs/module-bitmessage.html b/docs/module-bitmessage.html index 3728e03..14f81f1 100644 --- a/docs/module-bitmessage.html +++ b/docs/module-bitmessage.html @@ -176,7 +176,7 @@
Source:
@@ -238,7 +238,7 @@
Source:
@@ -300,7 +300,7 @@
Source:
@@ -362,7 +362,79 @@
Source:
+ + + + + + + + + + + + + + + + +

(static, constant) PROTOCOL_VERSION :number

+ + + + +
+

Current protocol version.

+
+ + + +
Type:
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
@@ -424,7 +496,69 @@
Source:
+ + + + + + + +
+ + + + + + + + +

(static) UserAgent

+ + + + +
+

User agent.

+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
@@ -486,7 +620,7 @@
Source:
@@ -520,13 +654,13 @@
diff --git a/docs/module-bitmessage_address.Address.html b/docs/module-bitmessage_address.Address.html index 257ef95..c880a6d 100644 --- a/docs/module-bitmessage_address.Address.html +++ b/docs/module-bitmessage_address.Address.html @@ -43,8 +43,6 @@ -

Constructor

-

new Address(optsnullable)

@@ -128,6 +126,7 @@ +
@@ -157,7 +156,7 @@
Source:
@@ -199,6 +198,1482 @@ +

Methods

+ + + + + + +

(static) decode(str) → {Address}

+ + + + + +
+

Parse Bitmessage address into address object.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
str + + +string + + + +

Address string (with or without BM- prefix)

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+

Decoded address object.

+
+ + + +
+
+ Type +
+
+ +Address + + +
+
+ + + + + + + + + + +

(static) fromPassphrase(optsnullable) → {Address}

+ + + + + +
+

Create new Bitmessage address from passphrase.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
opts + + +Object + + + + + + + + <nullable>
+ + + +

Address options

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+

Generated address object.

+
+ + + +
+
+ Type +
+
+ +Address + + +
+
+ + + + + + + + + + +

(static) fromRandom(optsnullable) → {Address}

+ + + + + +
+

Create new Bitmessage address from random encryption and signing +private keys.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
opts + + +Object + + + + + + + + <nullable>
+ + + +

Address options

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+

Generated address object.

+
+ + + +
+
+ Type +
+
+ +Address + + +
+
+ + + + + + + + + + +

(static) isAddress(obj) → {boolean}

+ + + + + +
+

Test if given object is an Address instance.
NOTE: Implementation is just simple instanceof but it improves +readability and consistent with isArray, isBuffer, etc.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
obj + + +Object + + + +

Given object

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +boolean + + +
+
+ + + + + + + + + + +

clone() → {Address}

+ + + + + +
+

Create a copy of the address object.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+

Cloned address.

+
+ + + +
+
+ Type +
+
+ +Address + + +
+
+ + + + + + + + + + +

encode() → {string}

+ + + + + +
+

Encode Bitmessage address object into address string.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+

Address string.

+
+ + + +
+
+ Type +
+
+ +string + + +
+
+ + + + + + + + + + +

getBroadcastPrivateKey() → {Buffer}

+ + + + + +
+

Calculate the encryption key used to encrypt/decrypt +broadcast objects.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+

A 32-byte private key.

+
+ + + +
+
+ Type +
+
+ +Buffer + + +
+
+ + + + + + + + + + +

getBroadcastPublicKey() → {Buffer}

+ + + + + +
+

Calculate the corresponding public key for encryption key used to +encrypt/decrypt +broadcast objects.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+

A 65-byte public key.

+
+ + + +
+
+ Type +
+
+ +Buffer + + +
+
+ + + + + + + + + + +

getPubkeyPrivateKey() → {Buffer}

+ + + + + +
+

Calculate the encryption key used to encrypt/decrypt +pubkey objects.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+

A 32-byte private key.

+
+ + + +
+
+ Type +
+
+ +Buffer + + +
+
+ + + + + + + + + + +

getPubkeyPublicKey() → {Buffer}

+ + + + + +
+

Calculate the corresponding public key for encryption key used to +encrypt/decrypt +pubkey objects.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+

A 65-byte public key.

+
+ + + +
+
+ Type +
+
+ +Buffer + + +
+
+ + + + + + + + + + +

getShortRipe() → {Buffer}

+ + + + + +
+

Get the ripe hash of the address without prefix zeroes.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+

A short ripe hash.

+
+ + + +
+
+ Type +
+
+ +Buffer + + +
+
+ + + + + + + + + + +

getTag() → {Buffer}

+ + + + + +
+

Calculate the address tag.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+

A 32-byte address tag.

+
+ + + +
+
+ Type +
+
+ +Buffer + + +
+
+ + + + + + + @@ -213,13 +1688,13 @@
- Documentation generated by JSDoc 3.3.0-alpha13 on Sat Jan 03 2015 19:33:03 GMT+0300 (MSK) + Documentation generated by JSDoc 3.3.0-beta1 on Sat Jan 31 2015 14:53:21 GMT+0300 (MSK)
diff --git a/docs/module-bitmessage_address.html b/docs/module-bitmessage_address.html index c6eef64..ebeeb2a 100644 --- a/docs/module-bitmessage_address.html +++ b/docs/module-bitmessage_address.html @@ -158,13 +158,13 @@
- Documentation generated by JSDoc 3.3.0-alpha13 on Sat Jan 03 2015 19:33:03 GMT+0300 (MSK) + Documentation generated by JSDoc 3.3.0-beta1 on Sat Jan 31 2015 14:53:21 GMT+0300 (MSK)
diff --git a/docs/module-bitmessage_crypto.html b/docs/module-bitmessage_crypto.html index b3bf6dc..96217ae 100644 --- a/docs/module-bitmessage_crypto.html +++ b/docs/module-bitmessage_crypto.html @@ -39,7 +39,7 @@

Isomorphic Bitmessage crypto module. Reexports platform-dependent -implementations and and also some common routines.

+implementations and also some common routines.

@@ -138,6 +138,418 @@ implementations and and also some common routines.

+

(static) decrypt(privateKey, buf) → {Promise.<Buffer>}

+ + + + + +
+

Decrypt message using given private key.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
privateKey + + +Buffer + + + +

A 32-byte private key of recepient of +the mesage

buf + + +Buffer + + + +

Encrypted data

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+
    +
  • A promise that resolves with the +plaintext on successful decryption and rejects on failure.
  • +
+
+ + + +
+
+ Type +
+
+ +Promise.<Buffer> + + +
+
+ + + + + + + + + + +

(static) encrypt(publicKeyTo, msg, optsnullable) → {Promise.<Buffer>}

+ + + + + +
+

Encrypt message for given recepient's public key.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
publicKeyTo + + +Buffer + + + + + + + + + +

Recipient's public key (65 bytes)

msg + + +Buffer + + + + + + + + + +

The message being encrypted

opts + + +Object + + + + + + + + <nullable>
+ + + +

You may also +specify initialization vector (16 bytes) and ephemeral private key +(32 bytes) to get deterministic results.

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+
    +
  • A promise that resolves with the buffer +in encrypted format successful encryption and rejects on failure.
  • +
+
+ + + +
+
+ Type +
+
+ +Promise.<Buffer> + + +
+
+ + + + + + + + + +

(static) getPrivate() → {Buffer}

@@ -189,7 +601,7 @@ implementations and and also some common routines.

Source:
@@ -249,7 +661,7 @@ implementations and and also some common routines.

-

Generate public key for a given private key.

+

Generate public key for the given private key.

@@ -301,7 +713,7 @@ implementations and and also some common routines.

-

Private key

+

A 32-byte private key

@@ -312,6 +724,7 @@ implementations and and also some common routines.

+
@@ -341,7 +754,7 @@ implementations and and also some common routines.

Source:
@@ -368,7 +781,7 @@ implementations and and also some common routines.

-

Public key.

+

A 65-byte (uncompressed) public key.

@@ -464,6 +877,7 @@ implementations and and also some common routines.

+
@@ -493,7 +907,7 @@ implementations and and also some common routines.

Source:
@@ -616,6 +1030,7 @@ implementations and and also some common routines.

+
@@ -645,7 +1060,160 @@ implementations and and also some common routines.

Source:
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+

Resulting hash.

+
+ + + +
+
+ Type +
+
+ +Buffer + + +
+
+ + + + + + + + + + +

(static) sha1(buf) → {Buffer}

+ + + + + +
+

Calculate SHA-1 hash.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
buf + + +Buffer + + + +

Input data

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
@@ -768,6 +1336,7 @@ implementations and and also some common routines.

+
@@ -797,7 +1366,7 @@ implementations and and also some common routines.

Source:
@@ -920,6 +1489,7 @@ implementations and and also some common routines.

+
@@ -949,7 +1519,7 @@ implementations and and also some common routines.

Source:
@@ -998,6 +1568,383 @@ implementations and and also some common routines.

+ + + + +

(static) sign(privateKey, msg) → {Promise.<Buffer>}

+ + + + + +
+

Sign message using ecdsa-with-sha1 scheme.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
privateKey + + +Buffer + + + +

A 32-byte private key

msg + + +Buffer + + + +

The message being signed

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+

A promise that contains signature in DER +format when fulfilled.

+
+ + + +
+
+ Type +
+
+ +Promise.<Buffer> + + +
+
+ + + + + + + + + + +

(static) verify(publicKey, msg, sig) → {Promise.<undefined>}

+ + + + + +
+

Verify signature using ecdsa-with-sha1 scheme.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
publicKey + + +Buffer + + + +

A 65-byte public key

msg + + +Buffer + + + +

The message being verified

sig + + +Buffer + + + +

The signature in DER format

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+

A promise that resolves on correct +signature and rejects on bad key or signature.

+
+ + + +
+
+ Type +
+
+ +Promise.<undefined> + + +
+
+ + + + + + @@ -1013,13 +1960,13 @@ implementations and and also some common routines.


- Documentation generated by JSDoc 3.3.0-alpha13 on Sat Jan 03 2015 19:33:03 GMT+0300 (MSK) + Documentation generated by JSDoc 3.3.0-beta1 on Sat Jan 31 2015 14:53:21 GMT+0300 (MSK)
diff --git a/docs/module-bitmessage_messages.html b/docs/module-bitmessage_messages.html index 9997463..53ec610 100644 --- a/docs/module-bitmessage_messages.html +++ b/docs/module-bitmessage_messages.html @@ -98,6 +98,10 @@
@@ -133,10 +137,191 @@ +

Namespaces

+ +
+
addr
+
+ +
error
+
+ +
getdata
+
+ +
inv
+
+ +
version
+
+
+ +

Methods

+ + + + + + +

(static) getCommand(buf) → (nullable) {string}

+ + + + + +
+

Try to get command of the given encoded message. +Note that this function doesn't do any validation because it is +already provided by +message.decode +routine. Normally you call this for each incoming message and then +call decode function of the appropriate message handler.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
buf + + +Buffer + + + +

Buffer that starts with encoded message

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+

Message's command if any.

+
+ + + +
+
+ Type +
+
+ +string + + +
+
+ + + + + + + @@ -151,13 +336,13 @@
- Documentation generated by JSDoc 3.3.0-alpha13 on Sat Jan 03 2015 19:33:03 GMT+0300 (MSK) + Documentation generated by JSDoc 3.3.0-beta1 on Sat Jan 31 2015 14:53:21 GMT+0300 (MSK)
diff --git a/docs/module-bitmessage_objects.html b/docs/module-bitmessage_objects.html index 77dad8f..79017cf 100644 --- a/docs/module-bitmessage_objects.html +++ b/docs/module-bitmessage_objects.html @@ -38,7 +38,8 @@
-

Working with objects.

+

Working with objects.
NOTE: Most operations with objects in this module are asynchronous +and return promises.

@@ -133,10 +134,347 @@ +

Namespaces

+ +
+
broadcast
+
+ +
getpubkey
+
+ +
msg
+
+ +
pubkey
+
+
+ +

Methods

+ + + + + + +

(static) getPayloadType(buf) → (nullable) {number}

+ + + + + +
+

Try to get type of the given object message payload. +Note that this function doesn't do any validation because it is +already provided by +object.decodePayload +routine. Normally you call this for each incoming object message +payload and then call decode function of the appropriate object +handler.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
buf + + +Buffer + + + +

Buffer that starts with object message payload

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+

Object's type if any.

+
+ + + +
+
+ Type +
+
+ +number + + +
+
+ + + + + + + + + + +

(static) getType(buf) → (nullable) {number}

+ + + + + +
+

Try to get type of the given encoded object message. +Note that this function doesn't do any validation because it is +already provided by +object.decode +routine. Normally you call this for each incoming object message and +then call decode function of the appropriate object handler.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
buf + + +Buffer + + + +

Buffer that starts with encoded object message

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+

Object's type if any.

+
+ + + +
+
+ Type +
+
+ +number + + +
+
+ + + + + + + @@ -151,13 +489,13 @@

- Documentation generated by JSDoc 3.3.0-alpha13 on Sat Jan 03 2015 19:33:03 GMT+0300 (MSK) + Documentation generated by JSDoc 3.3.0-beta1 on Sat Jan 31 2015 14:53:22 GMT+0300 (MSK)
diff --git a/docs/module-bitmessage_pow.html b/docs/module-bitmessage_pow.html index 4e28009..f7423cd 100644 --- a/docs/module-bitmessage_pow.html +++ b/docs/module-bitmessage_pow.html @@ -137,6 +137,596 @@ +

Methods

+ + + + + + +

(static) check(opts) → {boolean}

+ + + + + +
+

Check a POW.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
opts + + +Object + + + +

Proof of work options

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+

Is the proof of work sufficient.

+
+ + + +
+
+ Type +
+
+ +boolean + + +
+
+ + + + + + + + + + +

(static) doAsync(opts) → {Promise.<number>}

+ + + + + +
+

Do a POW.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
opts + + +Object + + + +

Proof of work options

+
Properties
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
data + + +Buffer + + + + + + + + <nullable>
+ + + +

Object message payload without nonce to +get the initial hash from

initialHash + + +Buffer + + + + + + + + <nullable>
+ + + +

Or already computed initial hash

target + + +number + + + + + + + + + +

POW target

+ +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+

A promise that contains computed nonce for +the given target when fulfilled.

+
+ + + +
+
+ Type +
+
+ +Promise.<number> + + +
+
+ + + + + + + + + + +

(static) getTarget(opts) → {number}

+ + + + + +
+

Calculate target.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
opts + + +Object + + + +

Target options

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+

Target.

+
+ + + +
+
+ Type +
+
+ +number + + +
+
+ + + + + + + @@ -151,13 +741,13 @@
- Documentation generated by JSDoc 3.3.0-alpha13 on Sat Jan 03 2015 19:33:03 GMT+0300 (MSK) + Documentation generated by JSDoc 3.3.0-beta1 on Sat Jan 31 2015 14:53:22 GMT+0300 (MSK)
diff --git a/docs/module-bitmessage_structs.html b/docs/module-bitmessage_structs.html index 42a756d..af08ebf 100644 --- a/docs/module-bitmessage_structs.html +++ b/docs/module-bitmessage_structs.html @@ -136,6 +136,27 @@

Namespaces

+
encrypted
+
+ +
inv_vect
+
+ +
message
+
+ +
net_addr
+
+ +
object
+
+ +
PubkeyBitfield
+
+ +
ServicesBitfield
+
+
var_int
@@ -164,13 +185,13 @@
- Documentation generated by JSDoc 3.3.0-alpha13 on Sat Jan 03 2015 19:33:03 GMT+0300 (MSK) + Documentation generated by JSDoc 3.3.0-beta1 on Sat Jan 31 2015 14:53:22 GMT+0300 (MSK)
diff --git a/docs/module-bitmessage_structs.var_int.html b/docs/module-bitmessage_structs.var_int.html index e0f76e1..7807db4 100644 --- a/docs/module-bitmessage_structs.var_int.html +++ b/docs/module-bitmessage_structs.var_int.html @@ -41,7 +41,7 @@
-

var_int.

+

Variable length integer.

@@ -76,7 +76,7 @@
Source:
@@ -126,7 +126,7 @@
-

Decode var_int.

+

Decode var_int.
NOTE: rest references input buffer.

@@ -178,7 +178,7 @@ -

A buffer that starts with encoded var_int

+

A buffer that starts with encoded var_int

@@ -189,6 +189,7 @@ +
@@ -218,7 +219,7 @@
Source:
@@ -245,7 +246,7 @@
-

Decoded var_int structure.

+

Decoded var_int structure.

@@ -278,7 +279,7 @@
-

Encode number into var_int.

+

Encode number into var_int.

@@ -344,6 +345,7 @@ +
@@ -373,7 +375,7 @@
Source:
@@ -400,7 +402,7 @@
-

Encoded var_int.

+

Encoded var_int.

@@ -437,13 +439,13 @@

- Documentation generated by JSDoc 3.3.0-alpha13 on Sat Jan 03 2015 19:33:03 GMT+0300 (MSK) + Documentation generated by JSDoc 3.3.0-beta1 on Sat Jan 31 2015 14:53:22 GMT+0300 (MSK)
diff --git a/docs/module-bitmessage_structs.var_int_list.html b/docs/module-bitmessage_structs.var_int_list.html index 872841e..521b4a1 100644 --- a/docs/module-bitmessage_structs.var_int_list.html +++ b/docs/module-bitmessage_structs.var_int_list.html @@ -41,7 +41,7 @@
-

var_int_list.

+

Variable length list of integers.

@@ -76,7 +76,7 @@
Source:
@@ -126,7 +126,7 @@
-

Decode var_int_list.

+

Decode var_int_list.
NOTE: rest references input buffer.

@@ -179,7 +179,7 @@

A buffer that starts with encoded -var_int_list

+var_int_list

@@ -190,6 +190,7 @@ var_int_list

+
@@ -219,7 +220,7 @@ var_int_list

Source:
@@ -246,7 +247,7 @@ var_int_list

-

Decoded var_int_list structure.

+

Decoded var_int_list structure.

@@ -279,7 +280,7 @@ var_int_list

-

Encode list of numbers into var_int_list.

+

Encode list of numbers into var_int_list.

@@ -342,6 +343,7 @@ var_int_list

+
@@ -371,7 +373,7 @@ var_int_list

Source:
@@ -398,7 +400,7 @@ var_int_list

-

Encoded var_int_list.

+

Encoded var_int_list.

@@ -435,13 +437,13 @@ var_int_list


- Documentation generated by JSDoc 3.3.0-alpha13 on Sat Jan 03 2015 19:33:03 GMT+0300 (MSK) + Documentation generated by JSDoc 3.3.0-beta1 on Sat Jan 31 2015 14:53:22 GMT+0300 (MSK)
diff --git a/docs/module-bitmessage_structs.var_str.html b/docs/module-bitmessage_structs.var_str.html index 7d414ee..134460f 100644 --- a/docs/module-bitmessage_structs.var_str.html +++ b/docs/module-bitmessage_structs.var_str.html @@ -41,7 +41,7 @@
-

var_str.

+

Variable length string.

@@ -76,7 +76,7 @@
Source:
@@ -126,7 +126,7 @@
-

Decode var_str.

+

Decode var_str.
NOTE: rest references input buffer.

@@ -178,7 +178,7 @@ -

A buffer that starts with encoded var_str

+

A buffer that starts with encoded var_str

@@ -189,6 +189,7 @@ +
@@ -218,7 +219,7 @@
Source:
@@ -245,7 +246,7 @@
-

Decoded var_str structure.

+

Decoded var_str structure.

@@ -278,7 +279,7 @@
-

Encode string into var_str.

+

Encode string into var_str.

@@ -341,6 +342,7 @@ +
@@ -370,7 +372,7 @@
Source:
@@ -397,7 +399,7 @@
-

Encoded var_str.

+

Encoded var_str.

@@ -434,13 +436,13 @@

- Documentation generated by JSDoc 3.3.0-alpha13 on Sat Jan 03 2015 19:33:03 GMT+0300 (MSK) + Documentation generated by JSDoc 3.3.0-beta1 on Sat Jan 31 2015 14:53:22 GMT+0300 (MSK)
diff --git a/docs/module-bitmessage_wif.html b/docs/module-bitmessage_wif.html index bfd983d..62e2a5c 100644 --- a/docs/module-bitmessage_wif.html +++ b/docs/module-bitmessage_wif.html @@ -214,6 +214,7 @@ +
@@ -366,6 +367,7 @@ +
@@ -459,13 +461,13 @@
- Documentation generated by JSDoc 3.3.0-alpha13 on Sat Jan 03 2015 19:33:03 GMT+0300 (MSK) + Documentation generated by JSDoc 3.3.0-beta1 on Sat Jan 31 2015 14:53:22 GMT+0300 (MSK)
diff --git a/docs/objects.js.html b/docs/objects.js.html index dc43733..d209058 100644 --- a/docs/objects.js.html +++ b/docs/objects.js.html @@ -27,10 +27,1002 @@
/**
- * Working with objects.
+ * Working with objects.  
+ * NOTE: Most operations with objects in this module are asynchronous
+ * and return promises.
  * @see {@link https://bitmessage.org/wiki/Protocol_specification#Object_types}
  * @module bitmessage/objects
  */
+// TODO(Kagami): Document object-like params.
+// FIXME(Kagami): Think through the API, we may want to get decoded
+// structure even if it contains unsupported version. Also error
+// handling may need some refactoring.
+
+"use strict";
+
+var objectAssign = Object.assign || require("object-assign");
+var bufferEqual = require("buffer-equal");
+var assert = require("./_util").assert;
+var promise = require("./platform").promise;
+var bmcrypto = require("./crypto");
+var Address = require("./address");
+var structs = require("./structs");
+var POW = require("./pow");
+var util = require("./_util");
+
+var var_int = structs.var_int;
+var PubkeyBitfield = structs.PubkeyBitfield;
+var message = structs.message;
+var object = structs.object;
+
+/**
+ * Try to get type of the given encoded object message.
+ * Note that this function doesn't do any validation because it is
+ * already provided by
+ * [object.decode]{@link module:bitmessage/structs.object.decode}
+ * routine. Normally you call this for each incoming object message and
+ * then call decode function of the appropriate object handler.
+ * @param {Buffer} buf - Buffer that starts with encoded object message
+ * @return {?number} Object's type if any.
+ */
+exports.getType = function(buf) {
+  // Message header: 4 + 12 + 4 + 4
+  // Object header: 8 + 8 + 4
+  if (buf.length < 44) {
+    return;
+  }
+  return buf.readUInt32BE(40, true);
+};
+
+/**
+ * Try to get type of the given object message payload.
+ * Note that this function doesn't do any validation because it is
+ * already provided by
+ * [object.decodePayload]{@link module:bitmessage/structs.object.decodePayload}
+ * routine. Normally you call this for each incoming object message
+ * payload and then call decode function of the appropriate object
+ * handler.
+ * @param {Buffer} buf - Buffer that starts with object message payload
+ * @return {?number} Object's type if any.
+ */
+exports.getPayloadType = function(buf) {
+  // Object header: 8 + 8 + 4
+  if (buf.length < 20) {
+    return;
+  }
+  return buf.readUInt32BE(16, true);
+};
+
+// Prepend nonce to a given object without nonce.
+function prependNonce(obj, opts) {
+  return new promise(function(resolve) {
+    assert(obj.length <= 262136, "object message payload is too big");
+    opts = objectAssign({}, opts);
+    var nonce, target, powp;
+    if (opts.skipPow) {
+      nonce = new Buffer(8);
+      nonce.fill(0);
+      resolve(Buffer.concat([nonce, obj]));
+    } else {
+      opts.payloadLength = obj.length + 8;  // Compensate for nonce
+      target = POW.getTarget(opts);
+      powp = POW.doAsync({target: target, data: obj})
+        .then(function(nonce) {
+          // TODO(Kagami): We may want to receive nonce as a Buffer from
+          // POW module to skip conversion step.
+          var payload = new Buffer(opts.payloadLength);
+          util.writeUInt64BE(payload, nonce, 0, true);
+          obj.copy(payload, 8);
+          return payload;
+        });
+      resolve(powp);
+    }
+  });
+}
+
+/**
+ * `getpubkey` object. When a node has the hash of a public key (from an
+ * address) but not the public key itself, it must send out a request
+ * for the public key.
+ * @see {@link https://bitmessage.org/wiki/Protocol_specification#getpubkey}
+ * @namespace
+ * @static
+ */
+var getpubkey = exports.getpubkey = {
+  /**
+   * Decode `getpubkey` object message.
+   * @param {Buffer} buf - Message
+   * @param {?Object} opts - Decoding options
+   * @return {Promise.<Object>} A promise that contains decoded
+   * `getpubkey` object structure when fulfilled.
+   */
+  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, opts));
+    });
+  },
+
+  /**
+   * Decode `getpubkey` object message payload.
+   * @param {Buffer} buf - Message payload
+   * @param {?Object} opts - Decoding options
+   * @return {Promise.<Object>} A promise that contains decoded
+   * `getpubkey` object structure when fulfilled.
+   */
+  decodePayloadAsync: function(buf, opts) {
+    return new promise(function(resolve) {
+      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");
+      var objectPayload = util.popkey(decoded, "objectPayload");
+      if (decoded.version < 4) {
+        assert(objectPayload.length === 20, "getpubkey ripe is too small");
+        // Object payload is copied so it's safe to return it right away.
+        decoded.ripe = objectPayload;
+      } else {
+        assert(objectPayload.length === 32, "getpubkey tag is too small");
+        // Object payload is copied so it's safe to return it right away.
+        decoded.tag = objectPayload;
+      }
+      resolve(decoded);
+    });
+  },
+
+  /**
+   * Encode `getpubkey` object message.
+   * @param {Object} opts - `getpubkey` object options
+   * @return {Promise.<Buffer>} A promise that contains encoded message
+   * when fulfilled.
+   */
+  encodeAsync: function(opts) {
+    return getpubkey.encodePayloadAsync(opts).then(function(payload) {
+      return message.encode("object", payload);
+    });
+  },
+
+  /**
+   * Encode `getpubkey` object message payload.
+   * @param {Object} opts - `getpubkey` object options
+   * @return {Promise.<Buffer>} A promise that contains encoded message
+   * payload when fulfilled.
+   */
+  encodePayloadAsync: function(opts) {
+    return new promise(function(resolve) {
+      opts = objectAssign({}, opts);
+      opts.type = object.GETPUBKEY;
+      // Bitmessage address of recepeint of `getpubkey` message.
+      var to = Address.decode(opts.to);
+      assert(to.version >= 2, "Address version is too low");
+      assert(to.version <= 4, "Address version is too high");
+      opts.version = to.version;
+      opts.stream = to.stream;
+      opts.objectPayload = to.version < 4 ? to.ripe : to.getTag();
+      var obj = object.encodePayloadWithoutNonce(opts);
+      resolve(prependNonce(obj, opts));
+    });
+  },
+};
+
+// Extract pubkey data from decrypted object payload.
+function extractPubkey(buf) {
+  var decoded = {length: 132};
+  // We assume here that input buffer was copied before so it's safe to
+  // return reference to it.
+  decoded.behavior = PubkeyBitfield(buf.slice(0, 4));
+  var signPublicKey = decoded.signPublicKey = new Buffer(65);
+  signPublicKey[0] = 4;
+  buf.copy(signPublicKey, 1, 4, 68);
+  var encPublicKey = decoded.encPublicKey = new Buffer(65);
+  encPublicKey[0] = 4;
+  buf.copy(encPublicKey, 1, 68, 132);
+  return decoded;
+}
+
+// Extract pubkey version 3 data from decrypted object payload.
+function extractPubkeyV3(buf) {
+  var decoded = extractPubkey(buf);
+  var decodedTrials = var_int.decode(buf.slice(132));
+  decoded.nonceTrialsPerByte = decodedTrials.value;
+  decoded.length += decodedTrials.length;
+  var decodedExtraBytes = var_int.decode(decodedTrials.rest);
+  decoded.payloadLengthExtraBytes = decodedExtraBytes.value;
+  decoded.length += decodedExtraBytes.length;
+  var decodedSigLength = var_int.decode(decodedExtraBytes.rest);
+  var siglen = decodedSigLength.value;
+  var rest = decodedSigLength.rest;
+  assert(rest.length >= siglen, "Bad pubkey object payload length");
+  decoded.signature = rest.slice(0, siglen);
+  siglen += decodedSigLength.length;
+  decoded._siglen = siglen;  // Internal value
+  decoded.length += siglen;
+  return decoded;
+}
+
+// Note that tag matching only works for address version >= 4.
+function findAddrByTag(addrs, tag) {
+  var i, addr;
+  addrs = addrs || [];
+  if (Address.isAddress(addrs)) {
+    addrs = [addrs];
+  }
+  if (Array.isArray(addrs)) {
+    for (i = 0; i < addrs.length; i++) {
+      addr = addrs[i];
+      if (addr.version >= 4 && bufferEqual(addr.getTag(), tag)) {
+        return addr;
+      }
+    }
+  } else {
+    addr = addrs[tag];
+    if (addr && addr.version >= 4) {
+      return addr;
+    }
+  }
+}
+
+/**
+ * `pubkey` object.
+ * @see {@link https://bitmessage.org/wiki/Protocol_specification#pubkey}
+ * @namespace
+ * @static
+ */
+var pubkey = exports.pubkey = {
+  /**
+   * Decode `pubkey` object message.
+   * @param {Buffer} buf - Message
+   * @param {?Object} opts - Decoding options
+   * @return {Promise.<Object>} A promise that contains decoded `pubkey`
+   * object structure when fulfilled.
+   */
+  decodeAsync: function(buf, opts) {
+    return new promise(function(resolve) {
+      var decoded = message.decode(buf);
+      assert(decoded.command === "object", "Bad command");
+      resolve(pubkey.decodePayloadAsync(decoded.payload, opts));
+    });
+  },
+
+  /**
+   * Decode `pubkey` object message payload.
+   * @param {Buffer} buf - Message payload
+   * @param {?Object} opts - Decoding options
+   * @return {Promise.<Object>} A promise that contains decoded `pubkey`
+   * object structure when fulfilled.
+   */
+  decodePayloadAsync: function(buf, opts) {
+    return new promise(function(resolve) {
+      opts = opts || {};
+      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");
+      assert(version <= 4, "Address version is too high");
+      var objectPayload = util.popkey(decoded, "objectPayload");
+      var siglen, pos, sig, dataToVerify, pubkeyp;
+      var tag, addr, pubkeyPrivateKey, dataToDecrypt;
+
+      // v2 pubkey.
+      if (version === 2) {
+        // 4 + 64 + 64
+        assert(
+          objectPayload.length === 132,
+          "Bad pubkey v2 object payload length");
+        objectAssign(decoded, extractPubkey(objectPayload));
+        return resolve(decoded);
+      }
+
+      // v3 pubkey.
+      if (version === 3) {
+        // 4 + 64 + 64 + (1+) + (1+) + (1+)
+        assert(
+          objectPayload.length >= 135,
+          "Bad pubkey v3 object payload length");
+        objectAssign(decoded, extractPubkeyV3(objectPayload));
+        siglen = util.popkey(decoded, "_siglen");
+        pos = decoded.headerLength + decoded.length - siglen;
+        // Object message payload from `expiresTime` up to `sig_length`.
+        dataToVerify = buf.slice(8, pos);
+        sig = decoded.signature;
+        pubkeyp = bmcrypto.verify(decoded.signPublicKey, dataToVerify, sig)
+          .then(function() {
+            return decoded;
+          });
+        return resolve(pubkeyp);
+      }
+
+      // v4 pubkey.
+      assert(objectPayload.length >= 32, "Bad pubkey v4 object payload length");
+      tag = decoded.tag = objectPayload.slice(0, 32);
+      addr = findAddrByTag(opts.needed, tag);
+      assert(addr, "You are not interested in this pubkey v4");
+      pubkeyPrivateKey = addr.getPubkeyPrivateKey();
+      dataToDecrypt = objectPayload.slice(32);
+      pubkeyp = bmcrypto
+        .decrypt(pubkeyPrivateKey, dataToDecrypt)
+        .then(function(decrypted) {
+          // 4 + 64 + 64 + (1+) + (1+) + (1+)
+          assert(
+            decrypted.length >= 135,
+            "Bad pubkey v4 object payload length");
+          objectAssign(decoded, extractPubkeyV3(decrypted));
+          siglen = util.popkey(decoded, "_siglen");
+          dataToVerify = Buffer.concat([
+            // Object header without nonce + tag.
+            buf.slice(8, decoded.headerLength + 32),
+            // Unencrypted pubkey data without signature.
+            decrypted.slice(0, decoded.length - siglen),
+          ]);
+          sig = decoded.signature;
+          // Since data is encrypted, entire object payload is used.
+          decoded.length = objectPayload.length;
+          return bmcrypto.verify(decoded.signPublicKey, dataToVerify, sig);
+        }).then(function() {
+          return decoded;
+        });
+      resolve(pubkeyp);
+    });
+  },
+
+  /**
+   * Encode `pubkey` object message.
+   * @param {Object} opts - `pubkey` object options
+   * @return {Promise.<Buffer>} A promise that contains encoded message
+   * when fulfilled.
+   */
+  encodeAsync: function(opts) {
+    return pubkey.encodePayloadAsync(opts).then(function(payload) {
+      return message.encode("object", payload);
+    });
+  },
+
+  /**
+   * Encode `pubkey` object message payload.
+   * @param {Object} opts - `pubkey` object options
+   * @return {Promise.<Buffer>} A promise that contains encoded message
+   * payload when fulfilled.
+   */
+  encodePayloadAsync: function(opts) {
+    return new promise(function(resolve) {
+      opts = objectAssign({}, opts);
+      opts.type = object.PUBKEY;
+      // Originator of `pubkey` message.
+      var from = Address.decode(opts.from);
+      var nonceTrialsPerByte = util.getTrials(from);
+      var payloadLengthExtraBytes = util.getExtraBytes(from);
+      // Bitmessage address of recepient of `pubkey` message.
+      var to, version, stream;
+      if (opts.to) {
+        to = Address.decode(opts.to);
+        version = to.version;
+        stream = to.stream;
+      } else {
+        version = opts.version || 4;
+        stream = opts.stream || 1;
+      }
+      assert(version >= 2, "Address version is too low");
+      assert(version <= 4, "Address version is too high");
+      opts.version = version;
+      opts.stream = stream;
+      var obj, pubkeyp;
+
+      // v2 pubkey.
+      if (version === 2) {
+        opts.objectPayload = Buffer.concat([
+          from.behavior.buffer,
+          from.signPublicKey.slice(1),
+          from.encPublicKey.slice(1),
+        ]);
+        obj = object.encodePayloadWithoutNonce(opts);
+        return resolve(prependNonce(obj, opts));
+      }
+
+      var pubkeyData = [
+        from.behavior.buffer,
+        from.signPublicKey.slice(1),
+        from.encPublicKey.slice(1),
+        var_int.encode(nonceTrialsPerByte),
+        var_int.encode(payloadLengthExtraBytes),
+      ];
+
+      // v3 pubkey.
+      if (version === 3) {
+        opts.objectPayload = Buffer.concat(pubkeyData);
+        obj = object.encodePayloadWithoutNonce(opts);
+        pubkeyp = bmcrypto
+          .sign(from.signPrivateKey, obj)
+          .then(function(sig) {
+            // Append signature to the encoded object and we are done.
+            obj = Buffer.concat([obj, var_int.encode(sig.length), sig]);
+            return prependNonce(obj, opts);
+          });
+        return resolve(pubkeyp);
+      }
+
+      // v4 pubkey.
+      opts.objectPayload = from.getTag();
+      obj = object.encodePayloadWithoutNonce(opts);
+      var dataToSign = Buffer.concat([obj].concat(pubkeyData));
+      pubkeyp = bmcrypto
+        .sign(from.signPrivateKey, dataToSign)
+        .then(function(sig) {
+          var dataToEnc = pubkeyData.concat(var_int.encode(sig.length), sig);
+          dataToEnc = Buffer.concat(dataToEnc);
+          return bmcrypto.encrypt(from.getPubkeyPublicKey(), dataToEnc);
+        }).then(function(enc) {
+          // Concat object header with ecnrypted data and we are done.
+          obj = Buffer.concat([obj, enc]);
+          return prependNonce(obj, opts);
+        });
+      resolve(pubkeyp);
+    });
+  },
+};
+
+// Try to decrypt message with all provided identities.
+function tryDecryptMsg(identities, buf) {
+  function inner(i) {
+    if (i > last) {
+      var err = new Error("Failed to decrypt msg with given identities");
+      return promise.reject(err);
+    }
+    return bmcrypto
+      .decrypt(identities[i].encPrivateKey, buf)
+      .then(function(decrypted) {
+        return {addr: identities[i], decrypted: decrypted};
+      }).catch(function() {
+        return inner(i + 1);
+      });
+  }
+
+  if (Address.isAddress(identities)) {
+    identities = [identities];
+  }
+  var last = identities.length - 1;
+  return inner(0);
+}
+
+// Encode message from the given options.
+function encodeMessage(opts) {
+  var encoding = opts.encoding || DEFAULT_ENCODING;
+  var message = opts.message;
+  var subject = opts.subject;
+  if (encoding === msg.IGNORE && !message) {
+    // User may omit message for IGNORE encoding.
+    message = new Buffer(0);
+  } else if (!Buffer.isBuffer(message)) {
+    // User may specify message as a string.
+    message = new Buffer(message, "utf8");
+  }
+  if (encoding === msg.SIMPLE && subject) {
+    // User may specify subject for SIMPLE encoding.
+    if (!Buffer.isBuffer(subject)) {
+      subject = new Buffer(subject, "utf8");
+    }
+    message = Buffer.concat([
+      new Buffer("Subject:"),
+      subject,
+      new Buffer("\nBody:"),
+      message,
+    ]);
+  }
+  return message;
+}
+
+// Decode message to the given encoding.
+function decodeMessage(message, encoding) {
+  var decoded = {};
+  if (encoding === msg.TRIVIAL || encoding === msg.SIMPLE) {
+    message = message.toString("utf8");
+  }
+  if (encoding !== msg.SIMPLE) {
+    decoded.message = message;
+    return decoded;
+  }
+
+  // SIMPLE.
+  var subject, index;
+  if (message.slice(0, 8) === "Subject:") {
+    subject = message.slice(8);
+    index = subject.indexOf("\nBody:");
+    if (index !== -1) {
+      message = subject.slice(index + 6);
+      subject = subject.slice(0, index);
+    } else {
+      message = "";
+    }
+    decoded.subject = subject;
+    decoded.message = message;
+  } else {
+    decoded.subject = "";
+    decoded.message = message;
+  }
+  return decoded;
+}
+
+/**
+ * `msg` object.
+ * @see {@link https://bitmessage.org/wiki/Protocol_specification#msg}
+ * @namespace
+ * @static
+ */
+var msg = exports.msg = {
+  /**
+   * Any data with this number may be ignored. The sending node might
+   * simply be sharing its public key with you.
+   * @constant {number}
+   */
+  IGNORE: 0,
+  /**
+   * UTF-8. No 'Subject' or 'Body' sections. Useful for simple strings
+   * of data, like URIs or magnet links.
+   * @constant {number}
+   */
+  TRIVIAL: 1,
+  /**
+   * UTF-8. Uses 'Subject' and 'Body' sections. No MIME is used.
+   * @constant {number}
+   */
+  SIMPLE: 2,
+
+  /**
+   * Decode `msg` object message.
+   * @param {Buffer} buf - Message
+   * @param {Object} opts - Decoding options
+   * @return {Promise.<Object>} A promise that contains decoded `msg`
+   * object structure when fulfilled.
+   */
+  decodeAsync: function(buf, opts) {
+    return new promise(function(resolve) {
+      var decoded = message.decode(buf);
+      assert(decoded.command === "object", "Bad command");
+      resolve(msg.decodePayloadAsync(decoded.payload, opts));
+    });
+  },
+
+  /**
+   * Decode `msg` object message payload.
+   * @param {Buffer} buf - Message payload
+   * @param {Object} opts - Decoding options
+   * @return {Promise.<Object>} A promise that contains decoded `msg`
+   * object structure when fulfilled.
+   */
+  decodePayloadAsync: function(buf, opts) {
+    return new promise(function(resolve) {
+      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");
+
+      var msgp = tryDecryptMsg(opts.identities, objectPayload)
+        .then(function(decInfo) {
+          var decrypted = decInfo.decrypted;
+
+          // Version, stream.
+          var decodedVersion = var_int.decode(decrypted);
+          var senderVersion = decoded.senderVersion = decodedVersion.value;
+          assert(senderVersion >= 2, "Sender version is too low");
+          assert(senderVersion <= 4, "Sender version is too high");
+          var decodedStream = var_int.decode(decodedVersion.rest);
+          decoded.senderStream = decodedStream.value;
+
+          // Behavior, keys.
+          assert(
+            decodedStream.rest.length >= 132,
+            "Bad msg object payload length");
+          objectAssign(decoded, extractPubkey(decodedStream.rest));
+          decoded.length += decodedVersion.length + decodedStream.length;
+          var rest = decrypted.slice(decoded.length);
+
+          // Pow extra.
+          if (decoded.senderVersion >= 3) {
+            var decodedTrials = var_int.decode(rest);
+            decoded.nonceTrialsPerByte = decodedTrials.value;
+            decoded.length += decodedTrials.length;
+            var decodedExtraBytes = var_int.decode(decodedTrials.rest);
+            decoded.payloadLengthExtraBytes = decodedExtraBytes.value;
+            decoded.length += decodedExtraBytes.length;
+            rest = decodedExtraBytes.rest;
+          }
+
+          // Ripe, encoding.
+          assert(rest.length >= 20, "Bad msg object payload length");
+          decoded.ripe = rest.slice(0, 20);
+          // TODO(Kagami): Also check against the calculated ripe (see
+          // GH-6)?
+          assert(
+            bufferEqual(decoded.ripe, decInfo.addr.ripe),
+            "msg was decrypted but the destination ripe doesn't match");
+          decoded.length += 20;
+          var decodedEncoding = var_int.decode(rest.slice(20));
+          var encoding = decoded.encoding = decodedEncoding.value;
+          decoded.length += decodedEncoding.length;
+
+          // Message.
+          var decodedMsgLength = var_int.decode(decodedEncoding.rest);
+          var msglen = decodedMsgLength.value;
+          rest = decodedMsgLength.rest;
+          assert(rest.length >= msglen, "Bad msg object payload length");
+          decoded.length += decodedMsgLength.length + msglen;
+          var message = rest.slice(0, msglen);
+          objectAssign(decoded, decodeMessage(message, encoding));
+
+          // Acknowledgement data.
+          // TODO(Kagami): Validate ack, check a POW.
+          var decodedAckLength = var_int.decode(rest.slice(msglen));
+          var acklen = decodedAckLength.value;
+          rest = decodedAckLength.rest;
+          assert(rest.length >= acklen, "Bad msg object payload length");
+          decoded.length += decodedAckLength.length + acklen;
+          decoded.ack = rest.slice(0, acklen);
+
+          // Signature.
+          var decodedSigLength = var_int.decode(rest.slice(acklen));
+          var siglen = decodedSigLength.value;
+          rest = decodedSigLength.rest;
+          assert(rest.length >= siglen, "Bad msg object payload length");
+          var sig = decoded.signature = rest.slice(0, siglen);
+
+          // Verify signature.
+          var dataToVerify = Buffer.concat([
+            // Object header without nonce.
+            buf.slice(8, decoded.headerLength),
+            // Unencrypted pubkey data without signature.
+            decrypted.slice(0, decoded.length),
+          ]);
+          // Since data is encrypted, entire object payload is used.
+          decoded.length = objectPayload.length;
+          return bmcrypto.verify(decoded.signPublicKey, dataToVerify, sig);
+        }).then(function() {
+          return decoded;
+        });
+      resolve(msgp);
+    });
+  },
+
+  /**
+   * Encode `msg` object message.
+   * @param {Object} opts - `msg` object options
+   * @return {Promise.<Buffer>} A promise that contains encoded message
+   * when fulfilled.
+   */
+  encodeAsync: function(opts) {
+    return msg.encodePayloadAsync(opts).then(function(payload) {
+      return message.encode("object", payload);
+    });
+  },
+
+  /**
+   * Encode `msg` object message payload.
+   * @param {Object} opts - `msg` object options
+   * @return {Promise.<Buffer>} A promise that contains encoded message
+   * payload when fulfilled.
+   */
+  encodePayloadAsync: function(opts) {
+    return new promise(function(resolve) {
+      // Deal with options.
+      opts = objectAssign({}, opts);
+      opts.type = object.MSG;
+      opts.version = 1;  // The only known msg version
+      var from = Address.decode(opts.from);
+      assert(from.version >= 2, "Address version is too low");
+      assert(from.version <= 4, "Address version is too high");
+      var to = Address.decode(opts.to);
+      opts.stream = to.stream;
+      var nonceTrialsPerByte, payloadLengthExtraBytes;
+      if (from.version >= 3) {
+        if (opts.friend) {
+          nonceTrialsPerByte = util.DEFAULT_TRIALS_PER_BYTE;
+          payloadLengthExtraBytes = util.DEFAULT_EXTRA_BYTES;
+        } else {
+          nonceTrialsPerByte = util.getTrials(from);
+          payloadLengthExtraBytes = util.getExtraBytes(from);
+        }
+      }
+      var encoding = opts.encoding || DEFAULT_ENCODING;
+      var message = encodeMessage(opts);
+
+      // Assemble the unencrypted message data.
+      var msgData = [
+        var_int.encode(from.version),
+        var_int.encode(from.stream),
+        from.behavior.buffer,
+        from.signPublicKey.slice(1),
+        from.encPublicKey.slice(1),
+      ];
+      if (from.version >= 3) {
+        msgData.push(
+          var_int.encode(nonceTrialsPerByte),
+          var_int.encode(payloadLengthExtraBytes)
+        );
+      }
+      msgData.push(
+        to.ripe,
+        var_int.encode(encoding),
+        var_int.encode(message.length),
+        message
+      );
+      // TODO(Kagami): Calculate ACK.
+      msgData.push(var_int.encode(0));
+
+      // Sign and encrypt.
+      opts.objectPayload = new Buffer(0);
+      var obj = object.encodePayloadWithoutNonce(opts);
+      var dataToSign = Buffer.concat([obj].concat(msgData));
+      var msgp = bmcrypto
+        .sign(from.signPrivateKey, dataToSign)
+        .then(function(sig) {
+          var dataToEnc = msgData.concat(var_int.encode(sig.length), sig);
+          dataToEnc = Buffer.concat(dataToEnc);
+          return bmcrypto.encrypt(to.encPublicKey, dataToEnc);
+        }).then(function(enc) {
+          // Concat object header with ecnrypted data and we are done.
+          obj = Buffer.concat([obj, enc]);
+          // TODO(Kagami): Merge receiver's trials/extra bytes options
+          // so we can calculate right POW (now we need to pass them to
+          // opts manually).
+          return prependNonce(obj, opts);
+        });
+      resolve(msgp);
+    });
+  },
+};
+
+var DEFAULT_ENCODING = msg.TRIVIAL;
+
+// Try to decrypt broadcast v4 with all provided subscription objects.
+function tryDecryptBroadcastV4(subscriptions, buf) {
+  function inner(i) {
+    if (i > last) {
+      var err = new Error("Failed to decrypt broadcast with given identities");
+      return promise.reject(err);
+    }
+    return bmcrypto
+      .decrypt(subscriptions[i].getBroadcastPrivateKey(), buf)
+      .then(function(decrypted) {
+        return {addr: subscriptions[i], decrypted: decrypted};
+      }).catch(function() {
+        return inner(i + 1);
+      });
+  }
+
+  if (Address.isAddress(subscriptions)) {
+    subscriptions = [subscriptions];
+  } else if (!Array.isArray(subscriptions)) {
+    subscriptions = Object.keys(subscriptions).map(function(k) {
+      return subscriptions[k];
+    });
+  }
+  // Only addresses with version < 4 may be used to encode broadcast v4.
+  subscriptions = subscriptions.filter(function(a) {
+    return a.version < 4;
+  });
+  var last = subscriptions.length - 1;
+  return inner(0);
+}
+
+/**
+ * `broadcast` object.
+ * @see {@link https://bitmessage.org/wiki/Protocol_specification#broadcast}
+ * @namespace
+ * @static
+ */
+var broadcast = exports.broadcast = {
+  /**
+   * Decode `broadcast` object message.
+   * @param {Buffer} buf - Message
+   * @param {Object} opts - Decoding options
+   * @return {Promise.<Object>} A promise that contains decoded
+   * `broadcast` object structure when fulfilled.
+   */
+  decodeAsync: function(buf, opts) {
+    return new promise(function(resolve) {
+      var decoded = message.decode(buf);
+      assert(decoded.command === "object", "Bad command");
+      resolve(broadcast.decodePayloadAsync(decoded.payload, opts));
+    });
+  },
+
+  /**
+   * Decode `broadcast` object message payload.
+   * @param {Buffer} buf - Message payload
+   * @param {Object} opts - Decoding options
+   * @return {Promise.<Object>} A promise that contains decoded `pubkey`
+   * object structure when fulfilled.
+   */
+  decodePayloadAsync: function(buf, opts) {
+    return new promise(function(resolve) {
+      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");
+      var objectPayload = util.popkey(decoded, "objectPayload");
+      var tag, addr, broadPrivateKey, dataToDecrypt, broadp;
+
+      if (version === 4) {
+        broadp = tryDecryptBroadcastV4(opts.subscriptions, objectPayload);
+      } else {
+        assert(
+          objectPayload.length >= 32,
+          "Bad broadcast v5 object payload length");
+        tag = decoded.tag = objectPayload.slice(0, 32);
+        addr = findAddrByTag(opts.subscriptions, tag);
+        assert(addr, "You are not interested in this broadcast v5");
+        broadPrivateKey = addr.getBroadcastPrivateKey();
+        dataToDecrypt = objectPayload.slice(32);
+        broadp = bmcrypto
+          .decrypt(broadPrivateKey, dataToDecrypt)
+          .then(function(decrypted) {
+            return {addr: addr, decrypted: decrypted};
+          });
+      }
+
+      broadp = broadp.then(function(decInfo) {
+        var decrypted = decInfo.decrypted;
+
+        // Version, stream.
+        var decodedVersion = var_int.decode(decrypted);
+        var senderVersion = decoded.senderVersion = decodedVersion.value;
+        if (version === 4) {
+          assert(senderVersion >= 2, "Sender version is too low");
+          assert(senderVersion <= 3, "Sender version is too high");
+        } else {
+          assert(senderVersion === 4, "Bad sender version");
+        }
+        var decodedStream = var_int.decode(decodedVersion.rest);
+        var senderStream = decoded.senderStream = decodedStream.value;
+        assert(
+          senderStream === decoded.stream,
+          "Cleartext broadcast object stream doesn't match encrypted");
+
+        // Behavior, keys.
+        assert(
+          decodedStream.rest.length >= 132,
+          "Bad broadcast object payload length");
+        objectAssign(decoded, extractPubkey(decodedStream.rest));
+        decoded.length += decodedVersion.length + decodedStream.length;
+        var rest = decrypted.slice(decoded.length);
+        var sender = new Address({
+          version: senderVersion,
+          stream: senderStream,
+          signPublicKey: decoded.signPublicKey,
+          encPublicKey: decoded.encPublicKey,
+        });
+        if (version === 4) {
+          assert(
+            bufferEqual(sender.ripe, decInfo.addr.ripe),
+            "The keys used to encrypt the broadcast doesn't match the keys "+
+            "embedded into the object");
+        } else {
+          assert(
+            bufferEqual(sender.getTag(), tag),
+            "The tag used to encrypt the broadcast doesn't match the keys "+
+            "and version/stream embedded into the object");
+        }
+
+        // Pow extra.
+        if (senderVersion >= 3) {
+          var decodedTrials = var_int.decode(rest);
+          decoded.nonceTrialsPerByte = decodedTrials.value;
+          decoded.length += decodedTrials.length;
+          var decodedExtraBytes = var_int.decode(decodedTrials.rest);
+          decoded.payloadLengthExtraBytes = decodedExtraBytes.value;
+          decoded.length += decodedExtraBytes.length;
+          rest = decodedExtraBytes.rest;
+        }
+
+        // Encoding, message
+        var decodedEncoding = var_int.decode(rest);
+        var encoding = decoded.encoding = decodedEncoding.value;
+        decoded.length += decodedEncoding.length;
+        var decodedMsgLength = var_int.decode(decodedEncoding.rest);
+        var msglen = decodedMsgLength.value;
+        rest = decodedMsgLength.rest;
+        assert(rest.length >= msglen, "Bad broadcast object payload length");
+        decoded.length += decodedMsgLength.length + msglen;
+        var message = rest.slice(0, msglen);
+        objectAssign(decoded, decodeMessage(message, encoding));
+
+        // Signature.
+        var decodedSigLength = var_int.decode(rest.slice(msglen));
+        var siglen = decodedSigLength.value;
+        rest = decodedSigLength.rest;
+        assert(rest.length >= siglen, "Bad broadcast object payload length");
+        var sig = decoded.signature = rest.slice(0, siglen);
+
+        // Verify signature.
+        var headerLength = decoded.headerLength;
+        if (version !== 4) {
+          // Compensate for tag.
+          headerLength += 32;
+        }
+        var dataToVerify = Buffer.concat([
+          // Object header without nonce.
+          buf.slice(8, headerLength),
+          // Unencrypted pubkey data without signature.
+          decrypted.slice(0, decoded.length),
+        ]);
+        // Since data is encrypted, entire object payload is used.
+        decoded.length = objectPayload.length;
+        return bmcrypto.verify(decoded.signPublicKey, dataToVerify, sig);
+      }).then(function() {
+        return decoded;
+      });
+      resolve(broadp);
+    });
+  },
+
+  /**
+   * Encode `broadcast` object message.
+   * @param {Object} opts - `broadcast` object options
+   * @return {Promise.<Buffer>} A promise that contains encoded message
+   * when fulfilled.
+   */
+  encodeAsync: function(opts) {
+    return broadcast.encodePayloadAsync(opts).then(function(payload) {
+      return message.encode("object", payload);
+    });
+  },
+
+  /**
+   * Encode `broadcast` object message payload.
+   * @param {Object} opts - `broadcast` object options
+   * @return {Promise.<Buffer>} A promise that contains encoded message
+   * payload when fulfilled.
+   */
+  encodePayloadAsync: function(opts) {
+    return new promise(function(resolve) {
+      // Deal with options.
+      opts = objectAssign({}, opts);
+      opts.type = object.BROADCAST;
+      var from = Address.decode(opts.from);
+      assert(from.version >= 2, "Address version is too low");
+      assert(from.version <= 4, "Address version is too high");
+      opts.version = from.version >= 4 ? 5 : 4;
+      opts.stream = from.stream;
+      var encoding = opts.encoding || DEFAULT_ENCODING;
+      var message = encodeMessage(opts);
+
+      // Assemble the unencrypted message data.
+      var broadData = [
+        var_int.encode(from.version),
+        var_int.encode(from.stream),
+        from.behavior.buffer,
+        from.signPublicKey.slice(1),
+        from.encPublicKey.slice(1),
+      ];
+      if (from.version >= 3) {
+        broadData.push(
+          var_int.encode(util.getTrials(from)),
+          var_int.encode(util.getExtraBytes(from))
+        );
+      }
+      broadData.push(
+        var_int.encode(encoding),
+        var_int.encode(message.length),
+        message
+      );
+
+      // Sign and encrypt.
+      opts.objectPayload = from.version >= 4 ? from.getTag() : new Buffer(0);
+      var obj = object.encodePayloadWithoutNonce(opts);
+      var dataToSign = Buffer.concat([obj].concat(broadData));
+      var broadp = bmcrypto
+        .sign(from.signPrivateKey, dataToSign)
+        .then(function(sig) {
+          var dataToEnc = broadData.concat(var_int.encode(sig.length), sig);
+          dataToEnc = Buffer.concat(dataToEnc);
+          return bmcrypto.encrypt(from.getBroadcastPublicKey(), dataToEnc);
+        }).then(function(enc) {
+          obj = Buffer.concat([obj, enc]);
+          return prependNonce(obj, opts);
+        });
+      resolve(broadp);
+    });
+  },
+};
 
@@ -41,13 +1033,13 @@
- Documentation generated by JSDoc 3.3.0-alpha13 on Sat Jan 03 2015 19:33:03 GMT+0300 (MSK) + Documentation generated by JSDoc 3.3.0-beta1 on Sat Jan 31 2015 14:53:21 GMT+0300 (MSK)
diff --git a/docs/pow.js.html b/docs/pow.js.html index 5d194b4..25fb3c7 100644 --- a/docs/pow.js.html +++ b/docs/pow.js.html @@ -31,6 +31,95 @@ * @see {@link https://bitmessage.org/wiki/Proof_of_work} * @module bitmessage/pow */ +// TODO(Kagami): Find a way how to document object params properly. + +"use strict"; + +var objectAssign = Object.assign || require("object-assign"); +var bmcrypto = require("./crypto"); +var platform = require("./platform"); +var util = require("./_util"); + +/** + * Calculate target. + * @param {Object} opts - Target options + * @return {number} Target. + * @function + * @static + */ +// Just a wrapper around platform-specific implementation. +var getTarget = exports.getTarget = function(opts) { + var payloadLength = opts.payloadLength || opts.payload.length; + return platform.getTarget({ + ttl: opts.ttl, + payloadLength: payloadLength, + nonceTrialsPerByte: util.getTrials(opts), + payloadLengthExtraBytes: util.getExtraBytes(opts), + }); +}; + +/** + * Check a POW. + * @param {Object} opts - Proof of work options + * @return {boolean} Is the proof of work sufficient. + */ +exports.check = function(opts) { + var initialHash; + var nonce; + var target = opts.target; + if (target === undefined) { + target = getTarget(opts); + } + 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); + // High 32 bits. + nonce.writeUInt32BE(Math.floor(opts.nonce / 4294967296), 0, true); + // Low 32 bits. + nonce.writeUInt32BE(opts.nonce % 4294967296, 4, true); + } else { + nonce = opts.nonce; + } + initialHash = opts.initialHash; + } + var targetHi = Math.floor(target / 4294967296); + var targetLo = target % 4294967296; + var dataToHash = Buffer.concat([nonce, initialHash]); + var resultHash = bmcrypto.sha512(bmcrypto.sha512(dataToHash)); + var trialHi = resultHash.readUInt32BE(0, true); + if (trialHi > targetHi) { + return false; + } else if (trialHi < targetHi) { + return true; + } else { + var trialLo = resultHash.readUInt32BE(4, true); + return trialLo <= targetLo; + } +}; + +/** + * Do a POW. + * @param {Object} opts - Proof of work options + * @param {?Buffer} opts.data - Object message payload without nonce to + * get the initial hash from + * @param {?Buffer} opts.initialHash - Or already computed initial hash + * @param {number} opts.target - POW target + * @return {Promise.<number>} A promise that contains computed nonce for + * the given target when fulfilled. + */ +exports.doAsync = function(opts) { + var initialHash; + if (opts.data) { + initialHash = bmcrypto.sha512(opts.data); + } else { + initialHash = opts.initialHash; + } + opts = objectAssign({}, opts, {initialHash: initialHash}); + return platform.pow(opts); +}; @@ -41,13 +130,13 @@
- Documentation generated by JSDoc 3.3.0-alpha13 on Sat Jan 03 2015 19:33:03 GMT+0300 (MSK) + Documentation generated by JSDoc 3.3.0-beta1 on Sat Jan 31 2015 14:53:21 GMT+0300 (MSK)
diff --git a/docs/structs.js.html b/docs/structs.js.html index 2f3bcfb..37afdd1 100644 --- a/docs/structs.js.html +++ b/docs/structs.js.html @@ -31,23 +31,249 @@ * @see {@link https://bitmessage.org/wiki/Protocol_specification#Common_structures} * @module bitmessage/structs */ +// TODO(Kagami): Document object-like params. "use strict"; -var assert = require("assert"); +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) { + for (var i = 0; i < str.length; i++) { + if (str.charCodeAt(i) > 127) { + return false; + } + } + return true; +} + +// Compute the message checksum for the given data. +function getmsgchecksum(data) { + return bmcrypto.sha512(data).slice(0, 4); +} /** - * var_int. + * Message structure. + * @see {@link https://bitmessage.org/wiki/Protocol_specification#Message_structure} + * @namespace + * @static + */ +var message = exports.message = { + /** + * Bitmessage magic value. + * @constant {number} + */ + MAGIC: 0xE9BEB4D9, + + /** + * Decode message structure. + * NOTE: `payload` is copied, `rest` references input buffer. + * @param {Buffer} buf - Buffer that starts with encoded message + * @return {{command: string, payload: Buffer, length: number, rest: Buffer}} + * Decoded message structure. + */ + decode: function(buf) { + assert(buf.length >= 24, "Buffer is too small"); + assert(buf.readUInt32BE(0, true) === message.MAGIC, "Wrong magic"); + var command = buf.slice(4, 16); + var firstNonNull = 0; + for (var i = 11; i >=0; i--) { + assert(command[i] <= 127, "Non-ASCII characters in command"); + if (!firstNonNull && command[i] !== 0) { + firstNonNull = i + 1; + } + } + // NOTE(Kagami): Command can be empty. + // NOTE(Kagami): "ascii" encoding is not necessary here since we + // already validated the command but that should be quite faster + // than default "utf-8" encoding. + command = command.slice(0, firstNonNull).toString("ascii"); + var payloadLength = buf.readUInt32BE(16, true); + var length = 24 + payloadLength; + assert(buf.length >= length, "Truncated payload"); + var checksum = buf.slice(20, 24); + // NOTE(Kagami): We do copy instead of slice to protect against + // possible source buffer modification by user. + var payload = new Buffer(payloadLength); + buf.copy(payload, 0, 24, length); + assert(bufferEqual(checksum, getmsgchecksum(payload)), "Bad checksum"); + var rest = buf.slice(length); + return {command: command, payload: payload, length: length, rest: rest}; + }, + + /** + * Encode message structure. + * @param {string} command - Message command + * @param {Bufer} payload - Message payload + * @return {Buffer} Encoded message structure. + */ + encode: function(command, payload) { + assert(command.length <= 12, "Command is too long"); + assert(isAscii(command), "Non-ASCII characters in command"); + var buf = new Buffer(24 + payload.length); + buf.fill(0); + buf.writeUInt32BE(message.MAGIC, 0, true); + buf.write(command, 4); + buf.writeUInt32BE(payload.length, 16, true); + getmsgchecksum(payload).copy(buf, 20); + payload.copy(buf, 24); + return buf; + }, +}; + +/** + * An `object` is a message which is shared throughout a stream. It is + * the only message which propagates; all others are only between two + * nodes. + * @see {@link https://bitmessage.org/wiki/Protocol_specification#object} + * @namespace + * @static + */ +var object = exports.object = { + // Known types. + GETPUBKEY: 0, + PUBKEY: 1, + MSG: 2, + BROADCAST: 3, + + /** + * 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, opts) { + var decoded = message.decode(buf); + assert(decoded.command === "object", "Bad command"); + 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. + */ + 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 <= 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, + type: type, + version: decodedVersion.value, + stream: decodedStream.value, + headerLength: headerLength, + objectPayload: objectPayload, + }; + }, + + /** + * Encode `object` message. + * @param {Object} opts - Object options + * @return {Buffer} Encoded message. + */ + encode: function(opts) { + var payload = object.encodePayload(opts); + return message.encode("object", payload); + }, + + /** + * Encode `object` message payload. + * @param {Object} opts - Object options + * @return {Buffer} Encoded payload. + */ + encodePayload: function(opts) { + // NOTE(Kagami): We do not try to calculate nonce here if it is not + // provided because: + // 1) It's async operation but in `structs` module all operations + // are synchronous. + // 2) It shouldn't be useful because almost all objects signatures + // include object header and POW is computed for entire object so at + // first the object header should be assembled and only then we can + // do a POW. + assert(opts.nonce.length === 8, "Bad nonce"); + // NOTE(Kagami): This may be a bit inefficient since we allocate + // twice. + return Buffer.concat([ + opts.nonce, + object.encodePayloadWithoutNonce(opts), + ]); + }, + + /** + * Encode `object` message payload without leading nonce field (may be + * useful if you are going to calculate it later). + * @param {Object} opts - Object options + * @return {Buffer} Encoded payload. + */ + encodePayloadWithoutNonce: function(opts) { + assert(opts.ttl > 0, "Bad TTL"); + assert(opts.ttl <= 2430000, "TTL may not be larger than 28 days + 3 hours"); + var expiresTime = util.tnow() + opts.ttl; + var type = new Buffer(4); + type.writeUInt32BE(opts.type, 0); + var stream = opts.stream || 1; + var obj = Buffer.concat([ + util.writeUInt64BE(null, expiresTime), + type, + var_int.encode(opts.version), + var_int.encode(stream), + opts.objectPayload, + ]); + assert(obj.length <= 262136, "object message payload is too big"); + return obj; + }, +}; + +/** + * Variable length integer. * @see {@link https://bitmessage.org/wiki/Protocol_specification#Variable_length_integer} * @namespace * @static */ var var_int = exports.var_int = { /** - * Decode var_int. - * @param {Buffer} buf - A buffer that starts with encoded var_int + * Decode `var_int`. + * NOTE: `rest` references input buffer. + * @param {Buffer} buf - A buffer that starts with encoded `var_int` * @return {{value: number, length: number, rest: Buffer}} - * Decoded var_int structure. + * Decoded `var_int` structure. */ decode: function(buf) { var value, length; @@ -73,7 +299,7 @@ var var_int = exports.var_int = { // <http://mdn.io/issafeinteger>, // <https://stackoverflow.com/q/307179> for details. // TODO(Kagami): We may want to return raw Buffer for - // 2^53 <= value <= 2^64 - 1 range. Possibly using the optional + // 2^53 <= value <= 2^64 - 1 range. Probably using the optional // argument because most of the code expect to get a number when // calling `var_int.decode`. assert(hi <= 2097151, "Unsafe integer"); @@ -90,9 +316,9 @@ var var_int = exports.var_int = { }, /** - * Encode number into var_int. + * Encode number into `var_int`. * @param {(number|Buffer)} value - Input number - * @return {Buffer} Encoded var_int. + * @return {Buffer} Encoded `var_int`. */ encode: function(value) { var buf, targetStart; @@ -123,57 +349,61 @@ var var_int = exports.var_int = { targetStart = 1 + (8 - value.length); value.copy(buf, targetStart); } else { - throw new Error("Value encode error"); + throw new Error("Unknown value type"); } return buf; }, }; /** - * var_str. + * Variable length string. * @see {@link https://bitmessage.org/wiki/Protocol_specification#Variable_length_string} * @namespace */ exports.var_str = { /** - * Decode var_str. - * @param {Buffer} buf - A buffer that starts with encoded var_str + * Decode `var_str`. + * NOTE: `rest` references input buffer. + * @param {Buffer} buf - A buffer that starts with encoded `var_str` * @return {{str: string, length: number, rest: Buffer}} - * Decoded var_str structure. + * Decoded `var_str` structure. */ decode: function(buf) { var decoded = var_int.decode(buf); var strLength = decoded.value; + var length = decoded.length + strLength; + assert(buf.length >= length, "Buffer is too small"); // XXX(Kagami): Spec doesn't mention encoding, using UTF-8. - var str = decoded.rest.slice(0, strLength).toString(); + var str = decoded.rest.slice(0, strLength).toString("utf8"); var rest = decoded.rest.slice(strLength); - return {str: str, length: decoded.length + strLength, rest: rest}; + return {str: str, length: length, rest: rest}; }, /** - * Encode string into var_str. + * Encode string into `var_str`. * @param {string} str - A string - * @return {Buffer} Encoded var_str. + * @return {Buffer} Encoded `var_str`. */ encode: function(str) { // XXX(Kagami): Spec doesn't mention encoding, using UTF-8. - var strBuf = new Buffer(str); + var strBuf = new Buffer(str, "utf8"); return Buffer.concat([var_int.encode(strBuf.length), strBuf]); }, }; /** - * var_int_list. + * Variable length list of integers. * @see {@link https://bitmessage.org/wiki/Protocol_specification#Variable_length_list_of_integers} * @namespace */ exports.var_int_list = { /** - * Decode var_int_list. + * Decode `var_int_list`. + * NOTE: `rest` references input buffer. * @param {Buffer} buf - A buffer that starts with encoded - * var_int_list + * `var_int_list` * @return {{list: number[], length: number, rest: Buffer}} - * Decoded var_int_list structure. + * Decoded `var_int_list` structure. */ decode: function(buf) { var decoded = var_int.decode(buf); @@ -191,15 +421,315 @@ exports.var_int_list = { }, /** - * Encode list of numbers into var_int_list. + * Encode list of numbers into `var_int_list`. * @param {number[]} list - A number list - * @return {Buffer} Encoded var_int_list. + * @return {Buffer} Encoded `var_int_list`. */ encode: function(list) { var listBuf = Buffer.concat(list.map(var_int.encode)); return Buffer.concat([var_int.encode(list.length), listBuf]); }, }; + +// See https://en.wikipedia.org/wiki/IPv6#IPv4-mapped_IPv6_addresses +var IPv4_MAPPING = new Buffer([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255]); + +// Very simple inet_ntop(3) equivalent. +function inet_ntop(buf) { + assert(buf.length === 16, "Bad buffer size"); + // IPv4 mapped to IPv6. + if (bufferEqual(buf.slice(0, 12), IPv4_MAPPING)) { + return Array.prototype.join.call(buf.slice(12), "."); + // IPv6. + } else { + var groups = []; + for (var i = 0; i < 8; i++) { + groups.push(buf.readUInt16BE(i * 2, true).toString(16)); + } + return groups.join(":"); + } +} + +// Very simple inet_pton(3) equivalent. +function inet_pton(str) { + var buf = new Buffer(16); + buf.fill(0); + // IPv4. + if (str.indexOf(":") === -1) { + IPv4_MAPPING.copy(buf); + var octets = str.split(/\./g).map(function(s) {return parseInt(s, 10);}); + // Support short form from inet_aton(3) man page. + if (octets.length === 1) { + buf.writeUInt32BE(octets[0], 12); + } else { + // Check against 1000.bad.addr + octets.forEach(function(octet) { + assert(octet >= 0, "Bad IPv4 address"); + assert(octet <= 255, "Bad IPv4 address"); + }); + if (octets.length === 2) { + buf[12] = octets[0]; + buf[15] = octets[1]; + } else if (octets.length === 3) { + buf[12] = octets[0]; + buf[13] = octets[1]; + buf[15] = octets[2]; + } else if (octets.length === 4) { + buf[12] = octets[0]; + buf[13] = octets[1]; + buf[14] = octets[2]; + buf[15] = octets[3]; + } else { + throw new Error("Bad IPv4 address"); + } + } + // IPv6. + } else { + var dgroups = str.split(/::/g); + // Check against 1::1::1 + assert(dgroups.length <= 2, "Bad IPv6 address"); + var groups = []; + var i; + if (dgroups[0]) { + groups.push.apply(groups, dgroups[0].split(/:/g)); + } + if (dgroups.length === 2) { + if (dgroups[1]) { + var splitted = dgroups[1].split(/:/g); + var fill = 8 - (groups.length + splitted.length); + // Check against 1:1:1:1::1:1:1:1 + assert(fill > 0, "Bad IPv6 address"); + for (i = 0; i < fill; i++) { + groups.push(0); + } + groups.push.apply(groups, splitted); + } else { + // Check against 1:1:1:1:1:1:1:1:: + assert(groups.length <= 7, "Bad IPv6 address"); + } + } else { + // Check against 1:1:1 + assert(groups.length === 8, "Bad IPv6 address"); + } + for (i = 0; i < Math.min(groups.length, 8); i++) { + buf.writeUInt16BE(parseInt(groups[i], 16), i * 2); + } + } + return buf; +} + +/** + * Network address. + * @see {@link https://bitmessage.org/wiki/Protocol_specification#Network_address} + * @namespace + */ +exports.net_addr = { + /** + * Decode `net_addr`. + * @param {Buffer} buf - A buffer that contains encoded `net_addr` + * @param {?Object} opts - Decoding options; use `short` option to + * decode `net_addr` from + * [version message]{@link module:bitmessage/messages.version} + * @return {Object} Decoded `net_addr` structure. + */ + decode: function(buf, opts) { + var short = !!(opts || {}).short; + var res = {}; + if (short) { + assert(buf.length === 26, "Bad buffer size"); + } else { + assert(buf.length === 38, "Bad buffer size"); + var timeHi = buf.readUInt32BE(0, true); + var timeLo = buf.readUInt32BE(4, true); + // JavaScript's Date object can't work with timestamps higher than + // 8640000000000 (~2^43, ~275760 year). Hope JavaScript will + // support 64-bit numbers up to this date. + assert(timeHi <= 2011, "Time is too high"); + assert(timeHi !== 2011 || timeLo <= 2820767744, "Time is too high"); + res.time = new Date((timeHi * 4294967296 + timeLo) * 1000); + res.stream = buf.readUInt32BE(8, true); + buf = buf.slice(12); + } + res.services = ServicesBitfield(buf.slice(0, 8), {copy: true}); + res.host = inet_ntop(buf.slice(8, 24)); + res.port = buf.readUInt16BE(24, true); + return res; + }, + + /** + * Encode `net_addr`. + * @param {Object} opts - Encode options; use `short` option to encode + * `net_addr` for + * [version message]{@link module:bitmessage/messages.version} + * @return {Buffer} Encoded `net_addr`. + */ + encode: function(opts) { + // Be aware of `Buffer.slice` quirk in browserify: + // <http://git.io/lNZF1A> (does not modify parent buffer's memory in + // old browsers). So we use offset instead of `buf = buf.slice`. + var buf, shift; + if (opts.short) { + buf = new Buffer(26); + shift = 0; + } else { + buf = new Buffer(38); + var time = opts.time || new Date(); + time = Math.floor(time.getTime() / 1000); + buf.writeUInt32BE(Math.floor(time / 4294967296), 0, true); // high32 + buf.writeUInt32BE(time % 4294967296, 4, true); // low32 + var stream = opts.stream || 1; + buf.writeUInt32BE(stream, 8); + shift = 12; + } + var services = opts.services || + ServicesBitfield().set(ServicesBitfield.NODE_NETWORK); + services.buffer.copy(buf, shift); + inet_pton(opts.host).copy(buf, shift + 8); + buf.writeUInt16BE(opts.port, shift + 24); + return buf; + }, +}; + +/** + * Inventory vector. + * @see {@link https://bitmessage.org/wiki/Protocol_specification#Inventory_Vectors} + * @namespace + */ +// Only encode operation is defined because decode is impossible. +exports.inv_vect = { + /** + * Encode inventory vector. + * @param {Buffer} buf - Payload to calculate the inventory vector for + * @return {Buffer} Encoded `inv_vect`. + */ + encode: function(buf) { + return bmcrypto.sha512(bmcrypto.sha512(buf)).slice(0, 32); + }, +}; + +/** + * Encrypted payload. + * @see {@link https://bitmessage.org/wiki/Protocol_specification#Encrypted_payload} + * @namespace encrypted + * @static + */ +/** + * Decode encrypted payload. + * NOTE: all structure members are copied. + * @param {Buffer} buf - A buffer that contains encrypted payload + * @return {Object} Decoded encrypted structure. + * @function decode + * @memberof module:bitmessage/structs.encrypted + */ +/** + * Encode `encrypted`. + * @param {Object} opts - Encode options + * @return {Buffer} Encoded encrypted payload. + * @function encode + * @memberof module:bitmessage/structs.encrypted + */ +// Reexport struct. +exports.encrypted = bmcrypto.encrypted; + +// Creates bitfield (MSB 0) class of the specified size. +var Bitfield = function(size) { + var bytesize = size / 8; + + // Inspired by <https://github.com/fb55/bitfield>. + function BitfieldInner(buf, opts) { + if (!(this instanceof BitfieldInner)) { + return new BitfieldInner(buf); + } + opts = opts || {}; + if (buf) { + assert(buf.length === bytesize, "Bad buffer size"); + if (opts.copy) { + var dup = new Buffer(bytesize); + dup.fill(0); + buf.copy(dup); + buf = dup; + } + } else { + buf = new Buffer(bytesize); + buf.fill(0); + } + this.buffer = buf; + } + + BitfieldInner.prototype.get = function(bits) { + if (!Array.isArray(bits)) { + bits = [bits]; + } + var buf = this.buffer; + return bits.every(function(bit) { + assert(bit >= 0, "Bit number is too low"); + assert(bit < size, "Bit number is too high"); + var index = Math.floor(bit / 8); + var shift = 7 - (bit % 8); + return (buf[index] & (1 << shift)) !== 0; // jshint ignore:line + }); + }; + + BitfieldInner.prototype.set = function(bits) { + if (!Array.isArray(bits)) { + bits = [bits]; + } + var buf = this.buffer; + bits.forEach(function(bit) { + assert(bit >= 0, "Bit number is too low"); + assert(bit < size, "Bit number is too high"); + var index = Math.floor(bit / 8); + var shift = 7 - (bit % 8); + buf[index] |= 1 << shift; // jshint ignore:line + }); + return this; + }; + + return BitfieldInner; +}; + +/** + * Services bitfield features. + * @see {@link https://bitmessage.org/wiki/Protocol_specification#version} + * @namespace + * @static + */ +// TODO(Kagami): Document methods. +// NOTE(Kagami): Since pubkey bitfield uses MSB 0, we use it here too. +// See <https://github.com/Bitmessage/PyBitmessage/issues/769> for +// details. +var ServicesBitfield = exports.ServicesBitfield = objectAssign(Bitfield(64), { + /** + * This is a normal network node. + * @memberof module:bitmessage/structs.ServicesBitfield + * @constant {number} + */ + NODE_NETWORK: 63, +}); + +/** + * Pubkey bitfield features. + * @see {@link https://bitmessage.org/wiki/Protocol_specification#Pubkey_bitfield_features} + * @namespace + */ +// TODO(Kagami): Document methods. +exports.PubkeyBitfield = objectAssign(Bitfield(32), { + /** + * Receiving node expects that the RIPE hash encoded in their address + * preceedes the encrypted message data of msg messages bound for + * them. + * @memberof module:bitmessage/structs.PubkeyBitfield + * @constant {number} + */ + INCLUDE_DESTINATION: 30, + /** + * If true, the receiving node does send acknowledgements (rather than + * dropping them). + * @memberof module:bitmessage/structs.PubkeyBitfield + * @constant {number} + */ + DOES_ACK: 31, +}); @@ -210,13 +740,13 @@ exports.var_int_list = {
- Documentation generated by JSDoc 3.3.0-alpha13 on Sat Jan 03 2015 19:33:03 GMT+0300 (MSK) + Documentation generated by JSDoc 3.3.0-beta1 on Sat Jan 31 2015 14:53:21 GMT+0300 (MSK)
diff --git a/docs/wif.js.html b/docs/wif.js.html index b42a8de..5fb37f1 100644 --- a/docs/wif.js.html +++ b/docs/wif.js.html @@ -34,13 +34,13 @@ "use strict"; -var assert = require("assert"); var bufferEqual = require("buffer-equal"); var bs58 = require("bs58"); +var assert = require("./_util").assert; var bmcrypto = require("./crypto"); // Compute the WIF checksum for the given data. -function getchecksum(data) { +function getwifchecksum(data) { return bmcrypto.sha256(bmcrypto.sha256(data)).slice(0, 4); } @@ -54,7 +54,7 @@ exports.decode = function(wif) { assert(bytes[0] === 0x80, "Bad WIF"); var data = new Buffer(bytes.slice(0, -4)); var checksum = new Buffer(bytes.slice(-4)); - assert(bufferEqual(checksum, getchecksum(data)), "Bad checkum"); + assert(bufferEqual(checksum, getwifchecksum(data)), "Bad checkum"); return data.slice(1); }; @@ -65,7 +65,7 @@ exports.decode = function(wif) { */ exports.encode = function(privateKey) { var data = Buffer.concat([new Buffer([0x80]), privateKey]); - var checksum = getchecksum(data); + var checksum = getwifchecksum(data); var bytes = Buffer.concat([data, checksum]); return bs58.encode(bytes); }; @@ -79,13 +79,13 @@ exports.encode = function(privateKey) {
- Documentation generated by JSDoc 3.3.0-alpha13 on Sat Jan 03 2015 19:33:03 GMT+0300 (MSK) + Documentation generated by JSDoc 3.3.0-beta1 on Sat Jan 31 2015 14:53:21 GMT+0300 (MSK)