wallet: better encrypted for imported keys.
This commit is contained in:
parent
1c483aa9ca
commit
57bc9bf4b0
@ -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;
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user