wallet: key importing.

This commit is contained in:
Christopher Jeffrey 2016-08-19 12:39:50 -07:00
parent eecae63cf3
commit 4b008540e0
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
5 changed files with 163 additions and 130 deletions

View File

@ -266,13 +266,10 @@ crypto.decrypt = function decrypt(data, passphrase, iv, callback) {
try {
data = crypto.decipher(data, key, iv);
} catch (e) {
key.fill(0);
return callback(e);
}
key.fill(0);
return callback(null, data);
return callback(null, data, key);
});
};

View File

@ -326,42 +326,6 @@ KeyRing.fromSecret = function fromSecret(data) {
return new KeyRing().fromSecret(data);
};
/**
* Inject properties from account object.
* @private
* @param {Account} account
* @param {Buffer} key
* @param {Buffer[]} keys
* @param {Number} change
* @param {Number} index
*/
KeyRing.prototype.fromAccount = function fromAccount(account, key, keys, change, index) {
this.network = account.network;
this.publicKey = key.publicKey;
if (account.n > 1)
this.script = bcoin.script.fromMultisig(account.m, account.n, keys);
this.witness = account.witness;
return this;
};
/**
* Instantiate key ring from an account.
* @param {Account} account
* @param {Buffer} key
* @param {Buffer[]} keys
* @param {Number} change
* @param {Number} index
* @returns {KeyRing}
*/
KeyRing.fromAccount = function fromAccount(account, key, keys, change, index) {
return new KeyRing().fromAccount(account, key, keys, change, index);
};
/**
* Get public key.
* @param {String?} enc - `"hex"` or `null`.

View File

@ -628,46 +628,6 @@ Wallet.prototype.createReceive = function createReceive(account, callback) {
return this.createAddress(account, false, callback);
};
Wallet.prototype.importKey = function importKey(account, ring, callback) {
var self = this;
if (typeof ring === 'function') {
callback = ring;
ring = account;
account = 0;
}
callback = this._lockWrite(importKey, [account, ring, callback]);
if (!callback)
return;
this.getAccount(account, function(err, account) {
if (err)
return callback(err);
if (!account)
return callback(new Error('Account not found.'));
self.start();
ring.path = bcoin.walletdb.Path.fromAccount(account, ring, -1, -1);
ring.path.imported = ring.toRaw();
account.saveAddress([ring], function(err) {
if (err) {
self.drop();
return callback(err);
}
self.commit(function(err) {
if (err)
return callback(err);
callback();
});
});
}, true);
};
/**
* Create a new change address (increments receiveDepth).
* @param {(Number|String)?} account
@ -831,6 +791,71 @@ Wallet.prototype.getPaths = function getPaths(account, callback) {
});
};
/**
* Import a keyring (will not exist on derivation chain).
* Rescanning must be invoked manually.
* @param {(String|Number)?} account
* @param {KeyRing} ring
* @param {(String|Buffer)?} passphrase
* @param {Function} callback
*/
Wallet.prototype.importKey = function importKey(account, ring, passphrase, callback) {
var self = this;
var raw, path;
if (typeof passphrase === 'function') {
callback = passphrase;
passphrase = null;
}
if (typeof ring === 'function') {
callback = ring;
ring = account;
account = 0;
}
callback = this._lockWrite(importKey, [account, ring, callback]);
if (!callback)
return;
this.getAccount(account, function(err, account) {
if (err)
return callback(err);
if (!account)
return callback(new Error('Account not found.'));
self.unlock(passphrase, null, function(err) {
if (err)
return callback(err);
raw = ring.toRaw();
path = bcoin.path.fromAccount(account, ring);
if (self.master.encrypted) {
raw = self.master.encipher(raw, path.hash);
assert(raw);
path.encrypted = true;
}
path.imported = raw;
ring.path = path;
self.start();
account.saveAddress([ring], function(err) {
if (err) {
self.drop();
return callback(err);
}
self.commit(callback);
});
}, true);
});
};
/**
* Fill a transaction with inputs, estimate
* transaction size, calculate fee, and add a change output.
@ -1067,16 +1092,11 @@ Wallet.prototype.resend = function resend(callback) {
* @param {Function} callback - Returns [Error, {@link KeyRing}[]].
*/
Wallet.prototype.deriveInputs = function deriveInputs(tx, master, callback) {
Wallet.prototype.deriveInputs = function deriveInputs(tx, callback) {
var self = this;
var rings = [];
var ring;
if (typeof master === 'function') {
callback = master;
master = null;
}
this.getInputPaths(tx, function(err, paths) {
if (err)
return callback(err);
@ -1089,8 +1109,10 @@ Wallet.prototype.deriveInputs = function deriveInputs(tx, master, callback) {
if (!account)
return next();
ring = account.derivePath(path, master);
rings.push(ring);
ring = account.derivePath(path, self.master);
if (ring)
rings.push(ring);
next();
});
@ -1131,7 +1153,7 @@ Wallet.prototype.getKeyring = function getKeyring(address, callback) {
if (!account)
return callback();
ring = account.derivePath(path, self.master.key);
ring = account.derivePath(path, self.master);
callback(null, ring);
});
@ -1387,7 +1409,10 @@ Wallet.prototype.getRedeem = function getRedeem(hash, callback) {
if (!account)
return callback();
ring = account.derivePath(path);
ring = account.derivePath(path, self.master);
if (!ring)
return callback();
if (ring.program && hash.length === 20) {
if (utils.equal(hash, ring.programHash))
@ -1453,7 +1478,7 @@ Wallet.prototype.sign = function sign(tx, options, callback) {
if (err)
return callback(err);
self.deriveInputs(tx, master, function(err, rings) {
self.deriveInputs(tx, function(err, rings) {
if (err)
return callback(err);
@ -2540,15 +2565,32 @@ Account.prototype.deriveChange = function deriveChange(index, master) {
return this.deriveAddress(true, index, master);
};
/**
* Derive an address from `path` object.
* @param {Path} path
* @param {MasterKey} master
* @returns {KeyRing}
*/
Account.prototype.derivePath = function derivePath(path, master) {
var ring, script;
var ring, script, raw;
// Imported key.
if (path.index === -1) {
assert(path.imported);
assert(this.n === 1);
ring = bcoin.keyring.fromRaw(path.imported);
raw = path.imported;
if (path.encrypted)
raw = master.decipher(raw, path.hash);
if (!raw)
return;
ring = bcoin.keyring.fromRaw(raw);
ring.path = path;
return ring;
}
@ -2574,33 +2616,43 @@ Account.prototype.deriveAddress = function deriveAddress(change, index, master,
change = +change;
if (master) {
key = master.deriveAccount44(this.accountIndex);
if (master && master.key) {
key = master.key.deriveAccount44(this.accountIndex);
key = key.derive(change).derive(index);
} else {
key = this.accountKey.derive(change).derive(index);
}
ring = bcoin.keyring.fromPublic(key.publicKey, this.network);
ring.witness = this.witness;
if (script) {
// Custom redeem script.
assert(this.n === 1);
ring = bcoin.keyring.fromScript(key.publicKey, script, this.network);
assert(this.type === Account.types.PUBKEYHASH);
ring.script = script;
} else {
keys.push(key.publicKey);
switch (this.type) {
case Account.types.PUBKEYHASH:
break;
case Account.types.MULTISIG:
keys.push(key.publicKey);
for (i = 0; i < this.keys.length; i++) {
shared = this.keys[i];
shared = shared.derive(change).derive(index);
keys.push(shared.publicKey);
for (i = 0; i < this.keys.length; i++) {
shared = this.keys[i];
shared = shared.derive(change).derive(index);
keys.push(shared.publicKey);
}
ring.script = bcoin.script.fromMultisig(this.m, this.n, keys);
break;
}
ring = bcoin.keyring.fromAccount(this, key, keys, change, index);
}
if (master)
if (key.privateKey)
ring.privateKey = key.privateKey;
ring.path = bcoin.walletdb.Path.fromAccount(this, ring, change, index);
ring.path = bcoin.path.fromAccount(this, ring, change, index);
return ring;
};
@ -2908,6 +2960,7 @@ function MasterKey(options) {
this.ciphertext = null;
this.key = null;
this.aesKey = null;
this.timer = null;
this.until = 0;
this._destroy = this.destroy.bind(this);
@ -2989,7 +3042,7 @@ MasterKey.prototype.unlock = function unlock(passphrase, timeout, callback) {
assert(this.encrypted);
utils.decrypt(this.ciphertext, passphrase, this.iv, function(err, data) {
utils.decrypt(this.ciphertext, passphrase, this.iv, function(err, data, key) {
if (err)
return callback(err);
@ -3001,6 +3054,8 @@ MasterKey.prototype.unlock = function unlock(passphrase, timeout, callback) {
self.start(timeout);
self.aesKey = key;
callback(null, self.key);
});
};
@ -3037,6 +3092,26 @@ MasterKey.prototype.stop = function stop() {
}
};
MasterKey.prototype.encipher = function encipher(data, iv) {
if (!this.aesKey)
return;
if (typeof iv === 'string')
iv = new Buffer(iv, 'hex');
return utils.encipher(data, this.aesKey, iv.slice(0, 16));
};
MasterKey.prototype.decipher = function decipher(data, iv) {
if (!this.aesKey)
return;
if (typeof iv === 'string')
iv = new Buffer(iv, 'hex');
return utils.decipher(data, this.aesKey, iv.slice(0, 16));
};
/**
* Destroy the key by zeroing the
* privateKey and chainCode. Stop
@ -3056,6 +3131,11 @@ MasterKey.prototype.destroy = function destroy() {
this.key.destroy(true);
this.key = null;
}
if (this.aesKey) {
this.aesKey.fill(0);
this.aesKey = null;
}
};
/**

View File

@ -1591,9 +1591,10 @@ function Path() {
this.wid = null;
this.name = null;
this.account = 0;
this.change = 0;
this.index = 0;
this.change = -1;
this.index = -1;
this.encrypted = false;
this.imported = null;
this.script = null;
@ -1627,6 +1628,7 @@ Path.prototype.fromRaw = function fromRaw(data) {
this.script = p.readVarBytes();
break;
case 1:
this.encrypted = p.readU8() === 1;
this.imported = p.readVarBytes();
this.change = -1;
this.index = -1;
@ -1676,6 +1678,7 @@ Path.prototype.toRaw = function toRaw(writer) {
} else {
assert(this.imported);
p.writeU8(1);
p.writeU8(this.encrypted ? 1 : 0);
p.writeVarBytes(this.imported);
}
@ -1689,34 +1692,22 @@ Path.prototype.toRaw = function toRaw(writer) {
};
/**
* Inject properties from keyring.
* Inject properties from account.
* @private
* @param {WalletID} wid
* @param {KeyRing} ring
*/
Path.prototype.fromKeyRing = function fromKeyRing(ring) {
this.wid = ring.wid;
this.name = ring.name;
this.account = ring.account;
this.change = ring.change;
this.index = ring.index;
this.version = ring.witness ? 0 : -1;
this.type = ring.getType();
this.id = ring.id;
this.hash = ring.getHash('hex');
return this;
};
Path.prototype.fromAccount = function fromAccount(account, ring, change, index) {
this.wid = account.wid;
this.name = account.name;
this.account = account.accountIndex;
this.change = change;
this.index = index;
if (change != null)
this.change = change;
if (index != null)
this.index = index;
this.version = ring.witness ? 0 : -1;
this.type = ring.getType();

View File

@ -1065,13 +1065,14 @@ describe('Wallet', function() {
it('should import key', function(cb) {
var key = bcoin.keyring.generate();
walletdb.create(function(err, w1) {
walletdb.create({ passphrase: 'test' }, function(err, w1) {
assert.ifError(err);
w1.importKey(key, function(err) {
w1.importKey('default', key, 'test', function(err) {
assert.ifError(err);
w1.getKeyring(key.getHash('hex'), function(err, k) {
if (err)
return callback(err);
assert.equal(k.getHash('hex'), key.getHash('hex'));
// Coinbase