wallet: fix master key race conditions.

This commit is contained in:
Christopher Jeffrey 2016-10-26 17:45:13 -07:00
parent ea756026e7
commit a4a408a18d
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
4 changed files with 94 additions and 35 deletions

View File

@ -271,6 +271,24 @@ Node.prototype.getTip = function getTip() {
return Promise.resolve(this.chain.tip);
};
/**
* Get chain entry.
* @param {Hash} hash
* @returns {Promise}
*/
Node.prototype.getEntry = co(function* getEntry(hash) {
var entry = yield this.chain.db.get(hash);
if (!entry)
return;
if (!(yield entry.isMainChain()))
return;
return entry;
});
/**
* Send a transaction. Do not wait for promise.
* @param {TX} tx

View File

@ -335,10 +335,10 @@ MasterKey.prototype.destroy = co(function* destroy() {
* @returns {Promise}
*/
MasterKey.prototype.decrypt = co(function* decrypt(passphrase) {
MasterKey.prototype.decrypt = co(function* decrypt(passphrase, aes) {
var unlock = yield this.locker.lock();
try {
return yield this._decrypt(passphrase);
return yield this._decrypt(passphrase, aes);
} finally {
unlock();
}
@ -351,16 +351,14 @@ MasterKey.prototype.decrypt = co(function* decrypt(passphrase) {
* @returns {Promise}
*/
MasterKey.prototype._decrypt = co(function* decrypt(passphrase) {
MasterKey.prototype._decrypt = co(function* decrypt(passphrase, aes) {
var key, data;
if (!this.encrypted) {
assert(this.key);
return;
}
if (!this.encrypted)
throw new Error('Master key is not encrypted.');
if (!passphrase)
return;
throw new Error('No passphrase provided.');
this._lock();
@ -372,6 +370,11 @@ MasterKey.prototype._decrypt = co(function* decrypt(passphrase) {
this.iv = null;
this.ciphertext = null;
if (!aes) {
key.fill(0);
return;
}
return key;
});
@ -381,10 +384,10 @@ MasterKey.prototype._decrypt = co(function* decrypt(passphrase) {
* @returns {Promise}
*/
MasterKey.prototype.encrypt = co(function* encrypt(passphrase) {
MasterKey.prototype.encrypt = co(function* encrypt(passphrase, aes) {
var unlock = yield this.locker.lock();
try {
return yield this._encrypt(passphrase);
return yield this._encrypt(passphrase, aes);
} finally {
unlock();
}
@ -397,14 +400,14 @@ MasterKey.prototype.encrypt = co(function* encrypt(passphrase) {
* @returns {Promise}
*/
MasterKey.prototype._encrypt = co(function* encrypt(passphrase) {
MasterKey.prototype._encrypt = co(function* encrypt(passphrase, aes) {
var key, data, iv;
if (this.encrypted)
return;
throw new Error('Master key is already encrypted.');
if (!passphrase)
return;
throw new Error('No passphrase provided.');
data = this.key.toExtended();
iv = crypto.randomBytes(16);
@ -419,6 +422,11 @@ MasterKey.prototype._encrypt = co(function* encrypt(passphrase) {
this.iv = iv;
this.ciphertext = data;
if (!aes) {
key.fill(0);
return;
}
return key;
});

View File

@ -157,7 +157,7 @@ Wallet.prototype.fromOptions = function fromOptions(options) {
id = this.getID();
if (!token)
token = this.getToken(this.master.key, this.tokenDepth);
token = this.getToken(this.tokenDepth);
this.id = id;
this.token = token;
@ -398,17 +398,14 @@ Wallet.prototype.encrypt = co(function* encrypt(passphrase) {
*/
Wallet.prototype._encrypt = co(function* encrypt(passphrase) {
var key;
if (this.master.encrypted)
throw new Error('Wallet is already encrypted.');
var key = yield this.master.encrypt(passphrase, true);
this.start();
try {
key = yield this.master.encrypt(passphrase);
yield this.db.encryptKeys(this, key);
} catch (e) {
key.fill(0);
this.drop();
throw e;
}
@ -443,17 +440,14 @@ Wallet.prototype.decrypt = co(function* decrypt(passphrase) {
*/
Wallet.prototype._decrypt = co(function* decrypt(passphrase) {
var key;
if (!this.master.encrypted)
throw new Error('Wallet is not encrypted.');
var key = yield this.master.decrypt(passphrase, true);
this.start();
try {
key = yield this.master.decrypt(passphrase);
yield this.db.decryptKeys(this, key);
} catch (e) {
key.fill(0);
this.drop();
throw e;
}
@ -491,7 +485,7 @@ Wallet.prototype._retoken = co(function* retoken(passphrase) {
var master = yield this.unlock(passphrase);
this.tokenDepth++;
this.token = this.getToken(master, this.tokenDepth);
this.token = this.getToken(this.tokenDepth);
this.start();
this.save();
@ -614,7 +608,7 @@ Wallet.prototype.unlock = function unlock(passphrase, timeout) {
*/
Wallet.prototype.getID = function getID() {
var key, p, hash;
var p, key, hash;
assert(this.master.key, 'Cannot derive id.');
@ -645,12 +639,12 @@ Wallet.prototype.getID = function getID() {
* @returns {Buffer}
*/
Wallet.prototype.getToken = function getToken(master, nonce) {
var key, p;
Wallet.prototype.getToken = function getToken(nonce) {
var p, key;
assert(master, 'Cannot derive token.');
assert(this.master.key, 'Cannot derive token.');
key = master.derive(44, true);
key = this.master.key.derive(44, true);
p = new BufferWriter();
p.writeBytes(key.privateKey);
@ -682,7 +676,7 @@ Wallet.prototype.createAccount = co(function* createAccount(options, passphrase)
Wallet.prototype._createAccount = co(function* createAccount(options, passphrase) {
var name = options.name;
var key, master, account, exists;
var key, account, exists;
if (!name)
name = this.accountDepth + '';
@ -692,7 +686,7 @@ Wallet.prototype._createAccount = co(function* createAccount(options, passphrase
if (exists)
throw new Error('Account already exists.');
master = yield this.unlock(passphrase);
yield this.unlock(passphrase);
if (this.watchOnly && options.accountKey) {
key = options.accountKey;
@ -706,7 +700,8 @@ Wallet.prototype._createAccount = co(function* createAccount(options, passphrase
assert(key.network === this.network,
'Network mismatch for watch only key.');
} else {
key = master.deriveAccount44(this.accountDepth);
assert(this.master.key);
key = this.master.key.deriveAccount44(this.accountDepth);
key = key.hdPublicKey;
}

View File

@ -1285,10 +1285,11 @@ WalletDB.prototype.encryptKeys = co(function* encryptKeys(wallet, key) {
iv = new Buffer(path.hash, 'hex');
iv = iv.slice(0, 16);
path = path.clone();
path.data = crypto.encipher(path.data, key, iv);
path.encrypted = true;
wallet.pathCache.set(path.hash, path);
wallet.pathCache.push(path.hash, path);
batch.put(layout.P(wid, path.hash), path.toRaw());
}
@ -1317,10 +1318,11 @@ WalletDB.prototype.decryptKeys = co(function* decryptKeys(wallet, key) {
iv = new Buffer(path.hash, 'hex');
iv = iv.slice(0, 16);
path = path.clone();
path.data = crypto.decipher(path.data, key, iv);
path.encrypted = false;
wallet.pathCache.set(path.hash, path);
wallet.pathCache.push(path.hash, path);
batch.put(layout.P(wid, path.hash), path.toRaw());
}
@ -1715,6 +1717,42 @@ WalletDB.prototype.rollback = co(function* rollback(height) {
}
});
/**
* Sync with chain height.
* @param {Number} height
* @returns {Promise}
*/
WalletDB.prototype.revert = co(function* revert(height) {
var i, iter, item, height, block, tx;
iter = this.db.iterator({
gte: layout.b(height + 1),
lte: layout.b(0xffffffff),
reverse: true
});
for (;;) {
item = yield iter.next();
if (!item)
break;
try {
height = layout.bb(item.key);
block = BlockMapRecord.fromRaw(item.value);
for (i = block.txs.length - 1; i >= 0; i--) {
tx = block.txs[i];
yield this._unconfirm(tx);
}
} catch (e) {
yield iter.end();
throw e;
}
}
});
/**
* Add a block's transactions and write the new best hash.
* @param {ChainEntry} entry