wallet: better encrypted for imported keys.

This commit is contained in:
Christopher Jeffrey 2016-10-02 18:57:58 -07:00
parent 1c483aa9ca
commit 57bc9bf4b0
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
3 changed files with 191 additions and 24 deletions

View File

@ -339,35 +339,104 @@ Wallet.prototype._removeKey = co(function* removeKey(account, key) {
*/
Wallet.prototype.setPassphrase = co(function* setPassphrase(old, new_) {
var unlock = yield this.writeLock.lock();
try {
return yield this._setPassphrase(old, new_);
} finally {
unlock();
}
});
/**
* Change or set master key's passphrase without a lock.
* @private
* @param {(String|Buffer)?} old
* @param {String|Buffer} new_
* @returns {Promise}
*/
Wallet.prototype._setPassphrase = co(function* setPassphrase(old, new_) {
if (new_ == null) {
new_ = old;
old = null;
}
if (old != null)
yield this.master.decrypt(old);
yield this.decrypt(old);
if (new_ != null)
yield this.master.encrypt(new_);
yield this.encrypt(new_);
});
/**
* Encrypt the wallet permanently.
* @param {String|Buffer} passphrase
* @returns {Promise}
*/
Wallet.prototype.encrypt = co(function* encrypt(passphrase) {
var unlock = yield this.writeLock.lock();
try {
return yield this._encrypt(passphrase);
} finally {
unlock();
}
});
/**
* Encrypt the wallet permanently, without a lock.
* @private
* @param {String|Buffer} passphrase
* @returns {Promise}
*/
Wallet.prototype._encrypt = co(function* encrypt(passphrase) {
var key;
if (this.master.encrypted)
throw new Error('Wallet is already encrypted.');
this.start();
try {
key = yield this.master.encrypt(passphrase);
yield this.db.encryptKeys(this.wid, key);
} catch (e) {
this.drop();
throw e;
}
key.fill(0);
this.save();
yield this.commit();
});
/**
* Decrypt the wallet permanently.
* @param {String|Buffer} passphrase
* @returns {Promise}
*/
Wallet.prototype.decrypt = co(function* decrypt(passphrase) {
var unlock = yield this.writeLock.lock();
try {
return yield this._decrypt(passphrase);
} finally {
unlock();
}
});
/**
* Decrypt the wallet permanently, without a lock.
* @private
* @param {String|Buffer} passphrase
* @returns {Promise}
*/
Wallet.prototype._decrypt = co(function* decrypt(passphrase) {
var key;
if (!this.master.encrypted)
throw new Error('Wallet is not encrypted.');
this.start();
try {
key = yield this.master.decrypt(passphrase);
yield this.db.decryptKeys(this.wid, key);
} catch (e) {
this.drop();
throw e;
}
key.fill(0);
this.save();
yield this.commit();
@ -2328,7 +2397,7 @@ MasterKey.prototype.decrypt = co(function* decrypt(passphrase) {
*/
MasterKey.prototype._decrypt = co(function* decrypt(passphrase) {
var data;
var key, data;
if (!this.encrypted) {
assert(this.key);
@ -2340,12 +2409,15 @@ MasterKey.prototype._decrypt = co(function* decrypt(passphrase) {
this.destroy();
data = yield crypto.decrypt(this.ciphertext, passphrase, this.iv);
key = yield crypto.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;
});
/**
@ -2371,7 +2443,7 @@ MasterKey.prototype.encrypt = co(function* encrypt(passphrase) {
*/
MasterKey.prototype._encrypt = co(function* encrypt(passphrase) {
var data, iv;
var key, data, iv;
if (this.encrypted)
return;
@ -2384,12 +2456,15 @@ MasterKey.prototype._encrypt = co(function* encrypt(passphrase) {
this.stop();
data = yield crypto.encrypt(data, passphrase, iv);
key = yield crypto.derive(passphrase);
data = crypto.encipher(data, key, iv);
this.key = null;
this.encrypted = true;
this.iv = iv;
this.ciphertext = data;
return key;
});
/**

View File

@ -1149,6 +1149,56 @@ WalletDB.prototype.getWallets = function getWallets() {
});
};
/**
* Encrypt all imported keys for a wallet.
* @param {WalletID} wid
* @returns {Promise}
*/
WalletDB.prototype.encryptKeys = co(function* encryptKeys(wid, key) {
var paths = yield this.getWalletPaths(wid);
var batch = this.batch(wid);
var i, path, iv;
for (i = 0; i < paths.length; i++) {
path = paths[i];
if (path.data && !path.encrypted) {
iv = new Buffer(path.hash, 'hex');
iv = iv.slice(0, 16);
path.data = crypto.encipher(path.data, key, iv);
path.encrypted = true;
this.pathCache.set(wid + path.hash, path);
batch.put(layout.P(wid, path.hash), path.toRaw());
}
}
});
/**
* Decrypt all imported keys for a wallet.
* @param {WalletID} wid
* @returns {Promise}
*/
WalletDB.prototype.decryptKeys = co(function* decryptKeys(wid, key) {
var paths = yield this.getWalletPaths(wid);
var batch = this.batch(wid);
var i, path, iv;
for (i = 0; i < paths.length; i++) {
path = paths[i];
if (path.data && path.encrypted) {
iv = new Buffer(path.hash, 'hex');
iv = iv.slice(0, 16);
path.data = crypto.decipher(path.data, key, iv);
path.encrypted = false;
this.pathCache.set(wid + path.hash, path);
batch.put(layout.P(wid, path.hash), path.toRaw());
}
}
});
/**
* Rescan the blockchain.
* @param {ChainDB} chaindb

View File

@ -41,7 +41,7 @@ var dummyInput = {
};
describe('Wallet', function() {
var walletdb, wallet, doubleSpendWallet, doubleSpend;
var walletdb, wallet, ewallet, ekey, doubleSpendWallet, doubleSpend;
walletdb = new bcoin.walletdb({
name: 'wallet-test',
@ -938,6 +938,9 @@ describe('Wallet', function() {
yield w.sign(t2);
assert(t2.verify());
assert(t2.inputs[0].prevout.hash === tx.hash('hex'));
ewallet = w;
ekey = key;
}));
it('should import address', cob(function *() {
@ -967,6 +970,45 @@ describe('Wallet', function() {
assert.equal(details[0].toJSON().id, 'test');
}));
it('should handle changed passphrase with encrypted imports', cob(function *() {
var w = ewallet;
var addr = ekey.getAddress();
var path, d1, d2, k;
assert(w.master.encrypted);
path = yield w.getPath(addr);
assert(path);
assert(path.data && path.encrypted);
d1 = path.data;
yield w.decrypt('test');
path = yield w.getPath(addr);
assert(path);
assert(path.data && !path.encrypted);
k = yield w.getKey(addr);
assert(k);
yield w.encrypt('foo');
path = yield w.getPath(addr);
assert(path);
assert(path.data && path.encrypted);
d2 = path.data;
assert(!utils.equal(d1, d2));
k = yield w.getKey(addr);
assert(!k);
yield w.unlock('foo');
k = yield w.getKey(addr);
assert(k);
assert.equal(k.getHash('hex'), addr.getHash('hex'));
}));
it('should cleanup', cob(function *() {
var records = yield walletdb.dump();
constants.tx.COINBASE_MATURITY = 100;