From 4f46bb3324ecf110860ff8b8d2868b0a7a3db9fb Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Sun, 2 Oct 2016 22:50:42 -0700 Subject: [PATCH] wallet: move master key. --- lib/wallet/masterkey.js | 587 ++++++++++++++++++++++++++++++++++++++++ lib/wallet/wallet.js | 568 +------------------------------------- 2 files changed, 588 insertions(+), 567 deletions(-) create mode 100644 lib/wallet/masterkey.js diff --git a/lib/wallet/masterkey.js b/lib/wallet/masterkey.js new file mode 100644 index 00000000..37ffea69 --- /dev/null +++ b/lib/wallet/masterkey.js @@ -0,0 +1,587 @@ +/*! + * masterkey.js - master bip32 key object for bcoin + * Copyright (c) 2014-2016, Christopher Jeffrey (MIT License). + * https://github.com/bcoin-org/bcoin + */ + +'use strict'; + +var utils = require('../utils/utils'); +var Locker = require('../utils/locker'); +var co = require('../utils/co'); +var crypto = require('../crypto/crypto'); +var assert = require('assert'); +var BufferReader = require('../utils/reader'); +var BufferWriter = require('../utils/writer'); +var HD = require('../hd/hd'); + +/** + * Master BIP32 key which can exist + * in a timed out encrypted state. + * @exports Master + * @constructor + * @param {Object} options + */ + +function MasterKey(options) { + if (!(this instanceof MasterKey)) + return new MasterKey(options); + + this.encrypted = false; + this.iv = null; + this.ciphertext = null; + this.key = null; + + this.alg = MasterKey.alg.PBKDF2; + this.N = 50000; + this.r = 0; + this.p = 0; + + this.aesKey = null; + this.timer = null; + this.until = 0; + this._destroy = this.destroy.bind(this); + this.locker = new Locker(this); + + if (options) + this.fromOptions(options); +} + +/** + * Key derivation algorithms. + * @enum {Number} + * @default + */ + +MasterKey.alg = { + PBKDF2: 0, + SCRYPT: 1 +}; + +/** + * Key derivation algorithms by value. + * @enum {String} + * @default + */ + +MasterKey.algByVal = { + 0: 'pbkdf2', + 1: 'scrypt' +}; + +/** + * Inject properties from options object. + * @private + * @param {Object} options + */ + +MasterKey.prototype.fromOptions = function fromOptions(options) { + assert(options); + + if (options.encrypted != null) { + assert(typeof options.encrypted === 'boolean'); + this.encrypted = options.encrypted; + } + + if (options.iv) { + assert(Buffer.isBuffer(options.iv)); + this.iv = options.iv; + } + + if (options.ciphertext) { + assert(Buffer.isBuffer(options.ciphertext)); + this.ciphertext = options.ciphertext; + } + + if (options.key) { + assert(HD.isHD(options.key)); + this.key = options.key; + } + + if (options.alg != null) { + if (typeof options.alg === 'string') { + this.alg = MasterKey.alg[options.alg.toLowerCase()]; + assert(this.alg != null, 'Unknown algorithm.'); + } else { + assert(typeof options.alg === 'number'); + assert(MasterKey.algByVal[options.alg]); + this.alg = options.alg; + } + } + + if (options.rounds != null) { + assert(utils.isNumber(options.rounds)); + this.N = options.rounds; + } + + if (options.N != null) { + assert(utils.isNumber(options.N)); + this.N = options.N; + } + + if (options.r != null) { + assert(utils.isNumber(options.r)); + this.r = options.r; + } + + if (options.p != null) { + assert(utils.isNumber(options.p)); + this.p = options.p; + } + + assert(this.encrypted ? !this.key : this.key); + + return this; +}; + +/** + * Instantiate master key from options. + * @returns {MasterKey} + */ + +MasterKey.fromOptions = function fromOptions(options) { + return new MasterKey().fromOptions(options); +}; + +/** + * Decrypt the key and set a timeout to destroy decrypted data. + * @param {Buffer|String} passphrase - Zero this yourself. + * @param {Number} [timeout=60000] timeout in ms. + * @returns {Promise} - Returns {@link HDPrivateKey}. + */ + +MasterKey.prototype.unlock = co(function* _unlock(passphrase, timeout) { + var unlock = yield this.locker.lock(); + try { + return yield this._unlock(passphrase, timeout); + } finally { + unlock(); + } +}); + +/** + * Decrypt the key without a lock. + * @private + * @param {Buffer|String} passphrase - Zero this yourself. + * @param {Number} [timeout=60000] timeout in ms. + * @returns {Promise} - Returns {@link HDPrivateKey}. + */ + +MasterKey.prototype._unlock = co(function* _unlock(passphrase, timeout) { + var data, key; + + if (this.key) + return this.key; + + if (!passphrase) + throw new Error('No passphrase.'); + + assert(this.encrypted); + + key = yield this.derive(passphrase); + data = crypto.decipher(this.ciphertext, key, this.iv); + + this.key = HD.fromExtended(data); + + this.start(timeout); + + this.aesKey = key; + + return this.key; +}); + +/** + * Start the destroy timer. + * @private + * @param {Number} [timeout=60000] timeout in ms. + */ + +MasterKey.prototype.start = function start(timeout) { + if (!timeout) + timeout = 60000; + + this.stop(); + + if (timeout === -1) + return; + + this.until = utils.now() + (timeout / 1000 | 0); + this.timer = setTimeout(this._destroy, timeout); +}; + +/** + * Stop the destroy timer. + * @private + */ + +MasterKey.prototype.stop = function stop() { + if (this.timer != null) { + clearTimeout(this.timer); + this.timer = null; + this.until = 0; + } +}; + +/** + * Derive an aes key based on params. + * @param {String|Buffer} passphrase + * @returns {Promise} + */ + +MasterKey.prototype.derive = function derive(passwd) { + switch (this.alg) { + case MasterKey.alg.PBKDF2: + return crypto.pbkdf2Async(passwd, 'bcoin', this.N, 32, 'sha256'); + case MasterKey.alg.SCRYPT: + return crypto.scryptAsync(passwd, 'bcoin', this.N, this.r, this.p, 32); + default: + return Promise.reject(new Error('Unknown algorithm: ' + this.alg)); + } +}; + +/** + * Encrypt data with in-memory aes key. + * @param {Buffer} data + * @param {Buffer} iv + * @returns {Buffer} + */ + +MasterKey.prototype.encipher = function encipher(data, iv) { + if (!this.aesKey) + return; + + if (typeof iv === 'string') + iv = new Buffer(iv, 'hex'); + + return crypto.encipher(data, this.aesKey, iv.slice(0, 16)); +}; + +/** + * Decrypt data with in-memory aes key. + * @param {Buffer} data + * @param {Buffer} iv + * @returns {Buffer} + */ + +MasterKey.prototype.decipher = function decipher(data, iv) { + if (!this.aesKey) + return; + + if (typeof iv === 'string') + iv = new Buffer(iv, 'hex'); + + return crypto.decipher(data, this.aesKey, iv.slice(0, 16)); +}; + +/** + * Destroy the key by zeroing the + * privateKey and chainCode. Stop + * the timer if there is one. + */ + +MasterKey.prototype.destroy = function destroy() { + if (!this.encrypted) { + assert(this.timer == null); + assert(this.key); + return; + } + + this.stop(); + + if (this.key) { + this.key.destroy(true); + this.key = null; + } + + if (this.aesKey) { + this.aesKey.fill(0); + this.aesKey = null; + } +}; + +/** + * Decrypt the key permanently. + * @param {Buffer|String} passphrase - Zero this yourself. + * @returns {Promise} + */ + +MasterKey.prototype.decrypt = co(function* decrypt(passphrase) { + var unlock = yield this.locker.lock(); + try { + return yield this._decrypt(passphrase); + } finally { + unlock(); + } +}); + +/** + * Decrypt the key permanently without a lock. + * @private + * @param {Buffer|String} passphrase - Zero this yourself. + * @returns {Promise} + */ + +MasterKey.prototype._decrypt = co(function* decrypt(passphrase) { + var key, data; + + if (!this.encrypted) { + assert(this.key); + return; + } + + if (!passphrase) + return; + + this.destroy(); + + key = yield this.derive(passphrase); + data = crypto.decipher(this.ciphertext, key, this.iv); + + this.key = HD.fromExtended(data); + this.encrypted = false; + this.iv = null; + this.ciphertext = null; + + return key; +}); + +/** + * Encrypt the key permanently. + * @param {Buffer|String} passphrase - Zero this yourself. + * @returns {Promise} + */ + +MasterKey.prototype.encrypt = co(function* encrypt(passphrase) { + var unlock = yield this.locker.lock(); + try { + return yield this._encrypt(passphrase); + } finally { + unlock(); + } +}); + +/** + * Encrypt the key permanently without a lock. + * @private + * @param {Buffer|String} passphrase - Zero this yourself. + * @returns {Promise} + */ + +MasterKey.prototype._encrypt = co(function* encrypt(passphrase) { + var key, data, iv; + + if (this.encrypted) + return; + + if (!passphrase) + return; + + data = this.key.toExtended(); + iv = crypto.randomBytes(16); + + this.stop(); + + key = yield this.derive(passphrase); + data = crypto.encipher(data, key, iv); + + this.key = null; + this.encrypted = true; + this.iv = iv; + this.ciphertext = data; + + return key; +}); + +/** + * Serialize the key in the form of: + * `[enc-flag][iv?][ciphertext?][extended-key?]` + * @returns {Buffer} + */ + +MasterKey.prototype.toRaw = function toRaw(writer) { + var p = new BufferWriter(writer); + + if (this.encrypted) { + p.writeU8(1); + p.writeVarBytes(this.iv); + p.writeVarBytes(this.ciphertext); + + p.writeU8(this.alg); + p.writeU32(this.N); + p.writeU32(this.r); + p.writeU32(this.p); + + if (!writer) + p = p.render(); + + return p; + } + + p.writeU8(0); + p.writeVarBytes(this.key.toExtended()); + + if (!writer) + p = p.render(); + + return p; +}; + +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} raw + */ + +MasterKey.prototype.fromRaw = function fromRaw(raw) { + var p = new BufferReader(raw); + + this.encrypted = p.readU8() === 1; + + if (this.encrypted) { + this.iv = p.readVarBytes(); + this.ciphertext = p.readVarBytes(); + + this.alg = p.readU8(); + + assert(MasterKey.algByVal[this.alg]); + + this.N = p.readU32(); + this.r = p.readU32(); + this.p = p.readU32(); + + return this; + } + + this.key = HD.fromExtended(p.readVarBytes()); + + return this; +}; + +/** + * Instantiate master key from serialized data. + * @returns {MasterKey} + */ + +MasterKey.fromRaw = function fromRaw(raw) { + return new MasterKey().fromRaw(raw); +}; + +/** + * Inject properties from an HDPrivateKey. + * @private + * @param {HDPrivateKey} key + */ + +MasterKey.prototype.fromKey = function fromKey(key) { + this.encrypted = false; + this.iv = null; + this.ciphertext = null; + this.key = key; + return this; +}; + +/** + * Instantiate master key from an HDPrivateKey. + * @param {HDPrivateKey} key + * @returns {MasterKey} + */ + +MasterKey.fromKey = function fromKey(key) { + return new MasterKey().fromKey(key); +}; + +/** + * Convert master key to a jsonifiable object. + * @returns {Object} + */ + +MasterKey.prototype.toJSON = function toJSON() { + if (this.encrypted) { + return { + encrypted: true, + iv: this.iv.toString('hex'), + ciphertext: this.ciphertext.toString('hex'), + algorithm: MasterKey.algByVal[this.alg], + N: this.N, + r: this.r, + p: this.p + }; + } + + return { + encrypted: false, + key: this.key.toJSON() + }; +}; + +/** + * Inject properties from JSON object. + * @private + * @param {Object} json + */ + +MasterKey.prototype.fromJSON = function fromJSON(json) { + assert(typeof json.encrypted === 'boolean'); + + this.encrypted = json.encrypted; + + if (json.encrypted) { + assert(typeof json.iv === 'string'); + assert(typeof json.ciphertext === 'string'); + assert(typeof json.algorithm === 'string'); + assert(utils.isNumber(json.N)); + assert(utils.isNumber(json.r)); + assert(utils.isNumber(json.p)); + this.iv = new Buffer(json.iv, 'hex'); + this.ciphertext = new Buffer(json.ciphertext, 'hex'); + this.alg = MasterKey.alg[json.algorithm]; + assert(this.alg != null); + this.N = json.N; + this.r = json.r; + this.p = json.p; + } else { + this.key = HD.fromJSON(json.key); + } + + return this; +}; + +/** + * Instantiate master key from jsonified object. + * @param {Object} json + * @returns {MasterKey} + */ + +MasterKey.fromJSON = function fromJSON(json) { + return new MasterKey().fromJSON(json); +}; + +/** + * Inspect the key. + * @returns {Object} + */ + +MasterKey.prototype.inspect = function inspect() { + var json = this.toJSON(); + if (this.key) + json.key = this.key.toJSON(); + return json; +}; + +/** + * Test whether an object is a MasterKey. + * @param {Object} obj + * @returns {Boolean} + */ + +MasterKey.isMasterKey = function isMasterKey(obj) { + return obj + && typeof obj.encrypted === 'boolean' + && typeof obj.decrypt === 'function'; +}; + +/* + * Expose + */ + +module.exports = MasterKey; diff --git a/lib/wallet/wallet.js b/lib/wallet/wallet.js index ff48725f..648e3263 100644 --- a/lib/wallet/wallet.js +++ b/lib/wallet/wallet.js @@ -24,6 +24,7 @@ var MTX = require('../primitives/mtx'); var WalletKey = require('./walletkey'); var HD = require('../hd/hd'); var Account = require('./account'); +var MasterKey = require('./masterkey'); var Input = require('../primitives/input'); var Output = require('../primitives/output'); @@ -2181,573 +2182,6 @@ Wallet.isWallet = function isWallet(obj) { && obj.template === 'function'; }; -/** - * Master BIP32 key which can exist - * in a timed out encrypted state. - * @exports Master - * @constructor - * @param {Object} options - */ - -function MasterKey(options) { - if (!(this instanceof MasterKey)) - return new MasterKey(options); - - this.encrypted = false; - this.iv = null; - this.ciphertext = null; - this.key = null; - - this.alg = MasterKey.alg.PBKDF2; - this.N = 50000; - this.r = 0; - this.p = 0; - - this.aesKey = null; - this.timer = null; - this.until = 0; - this._destroy = this.destroy.bind(this); - this.locker = new Locker(this); - - if (options) - this.fromOptions(options); -} - -/** - * Key derivation algorithms. - * @enum {Number} - * @default - */ - -MasterKey.alg = { - PBKDF2: 0, - SCRYPT: 1 -}; - -/** - * Key derivation algorithms by value. - * @enum {String} - * @default - */ - -MasterKey.algByVal = { - 0: 'pbkdf2', - 1: 'scrypt' -}; - -/** - * Inject properties from options object. - * @private - * @param {Object} options - */ - -MasterKey.prototype.fromOptions = function fromOptions(options) { - var alg; - - assert(options); - - if (options.encrypted != null) { - assert(typeof options.encrypted === 'boolean'); - this.encrypted = options.encrypted; - } - - if (options.iv) { - assert(Buffer.isBuffer(options.iv)); - this.iv = options.iv; - } - - if (options.ciphertext) { - assert(Buffer.isBuffer(options.ciphertext)); - this.ciphertext = options.ciphertext; - } - - if (options.key) { - assert(HD.isHD(options.key)); - this.key = options.key; - } - - if (options.alg != null) { - if (typeof options.alg === 'string') { - this.alg = MasterKey.alg[options.alg.toLowerCase()]; - assert(this.alg != null, 'Unknown algorithm.'); - } else { - assert(typeof options.alg === 'number'); - assert(MasterKey.algByVal[options.alg]); - this.alg = options.alg; - } - } - - if (options.rounds != null) { - assert(utils.isNumber(options.rounds)); - this.N = options.rounds; - } - - if (options.N != null) { - assert(utils.isNumber(options.N)); - this.N = options.N; - } - - if (options.r != null) { - assert(utils.isNumber(options.r)); - this.r = options.r; - } - - if (options.p != null) { - assert(utils.isNumber(options.p)); - this.p = options.p; - } - - assert(this.encrypted ? !this.key : this.key); - - return this; -}; - -/** - * Instantiate master key from options. - * @returns {MasterKey} - */ - -MasterKey.fromOptions = function fromOptions(options) { - return new MasterKey().fromOptions(options); -}; - -/** - * Decrypt the key and set a timeout to destroy decrypted data. - * @param {Buffer|String} passphrase - Zero this yourself. - * @param {Number} [timeout=60000] timeout in ms. - * @returns {Promise} - Returns {@link HDPrivateKey}. - */ - -MasterKey.prototype.unlock = co(function* _unlock(passphrase, timeout) { - var unlock = yield this.locker.lock(); - try { - return yield this._unlock(passphrase, timeout); - } finally { - unlock(); - } -}); - -/** - * Decrypt the key without a lock. - * @private - * @param {Buffer|String} passphrase - Zero this yourself. - * @param {Number} [timeout=60000] timeout in ms. - * @returns {Promise} - Returns {@link HDPrivateKey}. - */ - -MasterKey.prototype._unlock = co(function* _unlock(passphrase, timeout) { - var data, key; - - if (this.key) - return this.key; - - if (!passphrase) - throw new Error('No passphrase.'); - - assert(this.encrypted); - - key = yield this.derive(passphrase); - data = crypto.decipher(this.ciphertext, key, this.iv); - - this.key = HD.fromExtended(data); - - this.start(timeout); - - this.aesKey = key; - - return this.key; -}); - -/** - * Start the destroy timer. - * @private - * @param {Number} [timeout=60000] timeout in ms. - */ - -MasterKey.prototype.start = function start(timeout) { - if (!timeout) - timeout = 60000; - - this.stop(); - - if (timeout === -1) - return; - - this.until = utils.now() + (timeout / 1000 | 0); - this.timer = setTimeout(this._destroy, timeout); -}; - -/** - * Stop the destroy timer. - * @private - */ - -MasterKey.prototype.stop = function stop() { - if (this.timer != null) { - clearTimeout(this.timer); - this.timer = null; - this.until = 0; - } -}; - -/** - * Derive an aes key based on params. - * @param {String|Buffer} passphrase - * @returns {Promise} - */ - -MasterKey.prototype.derive = function derive(passwd) { - switch (this.alg) { - case MasterKey.alg.PBKDF2: - return crypto.pbkdf2Async(passwd, 'bcoin', this.N, 32, 'sha256'); - case MasterKey.alg.SCRYPT: - return crypto.scryptAsync(passwd, 'bcoin', this.N, this.r, this.p, 32); - default: - return Promise.reject(new Error('Unknown algorithm: ' + this.alg)); - } -}; - -/** - * Encrypt data with in-memory aes key. - * @param {Buffer} data - * @param {Buffer} iv - * @returns {Buffer} - */ - -MasterKey.prototype.encipher = function encipher(data, iv) { - if (!this.aesKey) - return; - - if (typeof iv === 'string') - iv = new Buffer(iv, 'hex'); - - return crypto.encipher(data, this.aesKey, iv.slice(0, 16)); -}; - -/** - * Decrypt data with in-memory aes key. - * @param {Buffer} data - * @param {Buffer} iv - * @returns {Buffer} - */ - -MasterKey.prototype.decipher = function decipher(data, iv) { - if (!this.aesKey) - return; - - if (typeof iv === 'string') - iv = new Buffer(iv, 'hex'); - - return crypto.decipher(data, this.aesKey, iv.slice(0, 16)); -}; - -/** - * Destroy the key by zeroing the - * privateKey and chainCode. Stop - * the timer if there is one. - */ - -MasterKey.prototype.destroy = function destroy() { - if (!this.encrypted) { - assert(this.timer == null); - assert(this.key); - return; - } - - this.stop(); - - if (this.key) { - this.key.destroy(true); - this.key = null; - } - - if (this.aesKey) { - this.aesKey.fill(0); - this.aesKey = null; - } -}; - -/** - * Decrypt the key permanently. - * @param {Buffer|String} passphrase - Zero this yourself. - * @returns {Promise} - */ - -MasterKey.prototype.decrypt = co(function* decrypt(passphrase) { - var unlock = yield this.locker.lock(); - try { - return yield this._decrypt(passphrase); - } finally { - unlock(); - } -}); - -/** - * Decrypt the key permanently without a lock. - * @private - * @param {Buffer|String} passphrase - Zero this yourself. - * @returns {Promise} - */ - -MasterKey.prototype._decrypt = co(function* decrypt(passphrase) { - var key, data; - - if (!this.encrypted) { - assert(this.key); - return; - } - - if (!passphrase) - return; - - this.destroy(); - - key = yield this.derive(passphrase); - data = crypto.decipher(this.ciphertext, key, this.iv); - - this.key = HD.fromExtended(data); - this.encrypted = false; - this.iv = null; - this.ciphertext = null; - - return key; -}); - -/** - * Encrypt the key permanently. - * @param {Buffer|String} passphrase - Zero this yourself. - * @returns {Promise} - */ - -MasterKey.prototype.encrypt = co(function* encrypt(passphrase) { - var unlock = yield this.locker.lock(); - try { - return yield this._encrypt(passphrase); - } finally { - unlock(); - } -}); - -/** - * Encrypt the key permanently without a lock. - * @private - * @param {Buffer|String} passphrase - Zero this yourself. - * @returns {Promise} - */ - -MasterKey.prototype._encrypt = co(function* encrypt(passphrase) { - var key, data, iv; - - if (this.encrypted) - return; - - if (!passphrase) - return; - - data = this.key.toExtended(); - iv = crypto.randomBytes(16); - - this.stop(); - - key = yield this.derive(passphrase); - data = crypto.encipher(data, key, iv); - - this.key = null; - this.encrypted = true; - this.iv = iv; - this.ciphertext = data; - - return key; -}); - -/** - * Serialize the key in the form of: - * `[enc-flag][iv?][ciphertext?][extended-key?]` - * @returns {Buffer} - */ - -MasterKey.prototype.toRaw = function toRaw(writer) { - var p = new BufferWriter(writer); - - if (this.encrypted) { - p.writeU8(1); - p.writeVarBytes(this.iv); - p.writeVarBytes(this.ciphertext); - - p.writeU8(this.alg); - p.writeU32(this.N); - p.writeU32(this.r); - p.writeU32(this.p); - - if (!writer) - p = p.render(); - - return p; - } - - p.writeU8(0); - p.writeVarBytes(this.key.toExtended()); - - if (!writer) - p = p.render(); - - return p; -}; - -/** - * Inject properties from serialized data. - * @private - * @param {Buffer} raw - */ - -MasterKey.prototype.fromRaw = function fromRaw(raw) { - var p = new BufferReader(raw); - - this.encrypted = p.readU8() === 1; - - if (this.encrypted) { - this.iv = p.readVarBytes(); - this.ciphertext = p.readVarBytes(); - - this.alg = p.readU8(); - - assert(MasterKey.algByVal[this.alg]); - - this.N = p.readU32(); - this.r = p.readU32(); - this.p = p.readU32(); - - return this; - } - - this.key = HD.fromExtended(p.readVarBytes()); - - return this; -}; - -/** - * Instantiate master key from serialized data. - * @returns {MasterKey} - */ - -MasterKey.fromRaw = function fromRaw(raw) { - return new MasterKey().fromRaw(raw); -}; - -/** - * Inject properties from an HDPrivateKey. - * @private - * @param {HDPrivateKey} key - */ - -MasterKey.prototype.fromKey = function fromKey(key) { - this.encrypted = false; - this.iv = null; - this.ciphertext = null; - this.key = key; - return this; -}; - -/** - * Instantiate master key from an HDPrivateKey. - * @param {HDPrivateKey} key - * @returns {MasterKey} - */ - -MasterKey.fromKey = function fromKey(key) { - return new MasterKey().fromKey(key); -}; - -/** - * Convert master key to a jsonifiable object. - * @returns {Object} - */ - -MasterKey.prototype.toJSON = function toJSON() { - if (this.encrypted) { - return { - encrypted: true, - iv: this.iv.toString('hex'), - ciphertext: this.ciphertext.toString('hex'), - algorithm: MasterKey.algByVal[this.alg], - N: this.N, - r: this.r, - p: this.p - }; - } - - return { - encrypted: false, - key: this.key.toJSON() - }; -}; - -/** - * Inject properties from JSON object. - * @private - * @param {Object} json - */ - -MasterKey.prototype.fromJSON = function fromJSON(json) { - assert(typeof json.encrypted === 'boolean'); - - this.encrypted = json.encrypted; - - if (json.encrypted) { - assert(typeof json.iv === 'string'); - assert(typeof json.ciphertext === 'string'); - assert(typeof json.algorithm === 'string'); - assert(utils.isNumber(json.N)); - assert(utils.isNumber(json.r)); - assert(utils.isNumber(json.p)); - this.iv = new Buffer(json.iv, 'hex'); - this.ciphertext = new Buffer(json.ciphertext, 'hex'); - this.alg = MasterKey.alg[json.algorithm]; - assert(this.alg != null); - this.N = json.N; - this.r = json.r; - this.p = json.p; - } else { - this.key = HD.fromJSON(json.key); - } - - return this; -}; - -/** - * Instantiate master key from jsonified object. - * @param {Object} json - * @returns {MasterKey} - */ - -MasterKey.fromJSON = function fromJSON(json) { - return new MasterKey().fromJSON(json); -}; - -/** - * Inspect the key. - * @returns {Object} - */ - -MasterKey.prototype.inspect = function inspect() { - var json = this.toJSON(); - if (this.key) - json.key = this.key.toJSON(); - return json; -}; - -/** - * Test whether an object is a MasterKey. - * @param {Object} obj - * @returns {Boolean} - */ - -MasterKey.isMasterKey = function isMasterKey(obj) { - return obj - && typeof obj.encrypted === 'boolean' - && typeof obj.decrypt === 'function'; -}; - /* * Expose */