rpc/http/wallet: more aggressive validation for addrs/keys.

This commit is contained in:
Christopher Jeffrey 2017-02-28 14:49:50 -08:00
parent aa869e0b6a
commit 9f09de4867
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
10 changed files with 84 additions and 56 deletions

View File

@ -20,13 +20,14 @@ var HD = exports;
/** /**
* Instantiate an HD key (public or private) from an base58 string. * Instantiate an HD key (public or private) from an base58 string.
* @param {Base58String} xkey * @param {Base58String} xkey
* @param {Network?} network
* @returns {HDPrivateKey|HDPublicKey} * @returns {HDPrivateKey|HDPublicKey}
*/ */
HD.fromBase58 = function fromBase58(xkey) { HD.fromBase58 = function fromBase58(xkey, network) {
if (HDPrivateKey.isBase58(xkey)) if (HDPrivateKey.isBase58(xkey))
return HDPrivateKey.fromBase58(xkey); return HDPrivateKey.fromBase58(xkey, network);
return HDPublicKey.fromBase58(xkey); return HDPublicKey.fromBase58(xkey, network);
}; };
/** /**
@ -68,13 +69,14 @@ HD.fromMnemonic = function fromMnemonic(options, network) {
/** /**
* Instantiate an HD key from a jsonified key object. * Instantiate an HD key from a jsonified key object.
* @param {Object} json - The jsonified transaction object. * @param {Object} json - The jsonified transaction object.
* @param {Network?} network
* @returns {HDPrivateKey|HDPublicKey} * @returns {HDPrivateKey|HDPublicKey}
*/ */
HD.fromJSON = function fromJSON(json) { HD.fromJSON = function fromJSON(json, network) {
if (json.xprivkey) if (json.xprivkey)
return HDPrivateKey.fromJSON(json); return HDPrivateKey.fromJSON(json, network);
return HDPublicKey.fromJSON(json); return HDPublicKey.fromJSON(json, network);
}; };
/** /**
@ -116,7 +118,7 @@ HD.from = function from(options, network) {
return options; return options;
if (HD.isBase58(options)) if (HD.isBase58(options))
return HD.fromBase58(options); return HD.fromBase58(options, network);
if (HD.isRaw(options)) if (HD.isRaw(options))
return HD.fromRaw(options); return HD.fromRaw(options);

View File

@ -615,11 +615,14 @@ HDPrivateKey.generate = function generate(network) {
* Inject properties from base58 key. * Inject properties from base58 key.
* @private * @private
* @param {Base58String} xkey * @param {Base58String} xkey
* @param {Network?} network
*/ */
HDPrivateKey.prototype.fromBase58 = function fromBase58(xkey) { HDPrivateKey.prototype.fromBase58 = function fromBase58(xkey, network) {
this.fromRaw(base58.decode(xkey)); this.fromRaw(base58.decode(xkey));
this._xprivkey = xkey; this._xprivkey = xkey;
if (network && !this.verifyNetwork(network))
throw new Error('Network mismatch for HD private key.');
return this; return this;
}; };
@ -813,11 +816,12 @@ HDPrivateKey.fromExtended = function fromExtended(data) {
/** /**
* Instantiate an HD private key from a base58 string. * Instantiate an HD private key from a base58 string.
* @param {Base58String} xkey * @param {Base58String} xkey
* @param {Network?} network
* @returns {HDPrivateKey} * @returns {HDPrivateKey}
*/ */
HDPrivateKey.fromBase58 = function fromBase58(xkey) { HDPrivateKey.fromBase58 = function fromBase58(xkey, network) {
return new HDPrivateKey().fromBase58(xkey); return new HDPrivateKey().fromBase58(xkey, network);
}; };
/** /**
@ -856,12 +860,13 @@ HDPrivateKey.prototype.toJSON = function toJSON() {
* Inject properties from json object. * Inject properties from json object.
* @private * @private
* @param {Object} json * @param {Object} json
* @param {Network?} network
*/ */
HDPrivateKey.prototype.fromJSON = function fromJSON(json) { HDPrivateKey.prototype.fromJSON = function fromJSON(json, network) {
assert(json.xprivkey, 'Could not handle key JSON.'); assert(json.xprivkey, 'Could not handle key JSON.');
this.fromBase58(json.xprivkey); this.fromBase58(json.xprivkey, network);
if (json.mnemonic) if (json.mnemonic)
this.mnemonic = Mnemonic.fromJSON(json.mnemonic); this.mnemonic = Mnemonic.fromJSON(json.mnemonic);
@ -872,11 +877,12 @@ HDPrivateKey.prototype.fromJSON = function fromJSON(json) {
/** /**
* Instantiate an HDPrivateKey from a jsonified key object. * Instantiate an HDPrivateKey from a jsonified key object.
* @param {Object} json - The jsonified key object. * @param {Object} json - The jsonified key object.
* @param {Network?} network
* @returns {HDPrivateKey} * @returns {HDPrivateKey}
*/ */
HDPrivateKey.fromJSON = function fromJSON(json) { HDPrivateKey.fromJSON = function fromJSON(json, network) {
return new HDPrivateKey().fromJSON(json); return new HDPrivateKey().fromJSON(json, network);
}; };
/** /**

View File

@ -409,22 +409,24 @@ HDPublicKey.prototype.toJSON = function toJSON() {
* Inject properties from json object. * Inject properties from json object.
* @private * @private
* @param {Object} json * @param {Object} json
* @param {Network?} network
*/ */
HDPublicKey.prototype.fromJSON = function fromJSON(json) { HDPublicKey.prototype.fromJSON = function fromJSON(json, network) {
assert(json.xpubkey, 'Could not handle HD key JSON.'); assert(json.xpubkey, 'Could not handle HD key JSON.');
this.fromBase58(json.xpubkey); this.fromBase58(json.xpubkey, network);
return this; return this;
}; };
/** /**
* Instantiate an HDPrivateKey from a jsonified key object. * Instantiate an HDPublicKey from a jsonified key object.
* @param {Object} json - The jsonified transaction object. * @param {Object} json - The jsonified transaction object.
* @param {Network?} network
* @returns {HDPrivateKey} * @returns {HDPrivateKey}
*/ */
HDPublicKey.fromJSON = function fromJSON(json) { HDPublicKey.fromJSON = function fromJSON(json, network) {
return new HDPublicKey().fromJSON(json); return new HDPublicKey().fromJSON(json, network);
}; };
/** /**
@ -477,11 +479,14 @@ HDPublicKey.isRaw = function isRaw(data) {
* Inject properties from a base58 key. * Inject properties from a base58 key.
* @private * @private
* @param {Base58String} xkey * @param {Base58String} xkey
* @param {Network?} network
*/ */
HDPublicKey.prototype.fromBase58 = function fromBase58(xkey) { HDPublicKey.prototype.fromBase58 = function fromBase58(xkey, network) {
this.fromRaw(base58.decode(xkey)); this.fromRaw(base58.decode(xkey));
this._xpubkey = xkey; this._xpubkey = xkey;
if (network && !this.verifyNetwork(network))
throw new Error('Network mismatch for HD public key.');
return this; return this;
}; };
@ -581,11 +586,12 @@ HDPublicKey.prototype.toRaw = function toRaw(network) {
/** /**
* Instantiate an HD public key from a base58 string. * Instantiate an HD public key from a base58 string.
* @param {Base58String} xkey * @param {Base58String} xkey
* @param {Network?} network
* @returns {HDPublicKey} * @returns {HDPublicKey}
*/ */
HDPublicKey.fromBase58 = function fromBase58(xkey) { HDPublicKey.fromBase58 = function fromBase58(xkey, network) {
return new HDPublicKey().fromBase58(xkey); return new HDPublicKey().fromBase58(xkey, network);
}; };
/** /**

View File

@ -2055,7 +2055,7 @@ RPC.prototype._generatetoaddress = co(function* generatetoaddress(args, help) {
throw new RPCError('generatetoaddress numblocks address ( maxtries )'); throw new RPCError('generatetoaddress numblocks address ( maxtries )');
numblocks = toNumber(args[0], 1); numblocks = toNumber(args[0], 1);
address = Address.fromBase58(toString(args[1])); address = Address.fromBase58(toString(args[1]), this.network);
return yield this._generateBlocks(numblocks, address); return yield this._generateBlocks(numblocks, address);
}); });
@ -2137,7 +2137,7 @@ RPC.prototype.createrawtransaction = co(function* createrawtransaction(args, hel
continue; continue;
} }
address = Address.fromBase58(key); address = Address.fromBase58(key, this.network);
b58 = address.toBase58(this.network); b58 = address.toBase58(this.network);
if (addrs[b58]) if (addrs[b58])
@ -2270,7 +2270,7 @@ RPC.prototype._signrawtransaction = co(function* signrawtransaction(wallet, tx,
if (typeof secret !== 'string') if (typeof secret !== 'string')
throw new RPCError('Invalid parameter'); throw new RPCError('Invalid parameter');
key = KeyRing.fromSecret(secret); key = KeyRing.fromSecret(secret, this.network);
keyMap[key.getPublicKey('hex')] = key; keyMap[key.getPublicKey('hex')] = key;
keys.push(key); keys.push(key);
} }
@ -2375,7 +2375,7 @@ RPC.prototype.fundrawtransaction = co(function* fundrawtransaction(args, help) {
changeAddress = toString(options.changeAddress); changeAddress = toString(options.changeAddress);
if (changeAddress) if (changeAddress)
changeAddress = Address.fromBase58(changeAddress); changeAddress = Address.fromBase58(changeAddress, this.network);
feeRate = options.feeRate; feeRate = options.feeRate;
@ -2494,7 +2494,7 @@ RPC.prototype.validateaddress = co(function* validateaddress(args, help) {
b58 = toString(args[0]); b58 = toString(args[0]);
try { try {
address = Address.fromBase58(b58); address = Address.fromBase58(b58, this.network);
} catch (e) { } catch (e) {
return { return {
isvalid: false isvalid: false
@ -2561,7 +2561,7 @@ RPC.prototype.signmessagewithprivkey = co(function* signmessagewithprivkey(args,
key = toString(args[0]); key = toString(args[0]);
msg = toString(args[1]); msg = toString(args[1]);
key = KeyRing.fromSecret(key); key = KeyRing.fromSecret(key, this.network);
msg = new Buffer(RPC.magic + msg, 'utf8'); msg = new Buffer(RPC.magic + msg, 'utf8');
msg = crypto.hash256(msg); msg = crypto.hash256(msg);
@ -3242,7 +3242,7 @@ RPC.prototype.importprivkey = co(function* importprivkey(args, help) {
if (rescan && this.chain.options.prune) if (rescan && this.chain.options.prune)
throw new RPCError('Cannot rescan when pruned.'); throw new RPCError('Cannot rescan when pruned.');
key = KeyRing.fromSecret(secret); key = KeyRing.fromSecret(secret, this.network);
yield wallet.importKey(0, key); yield wallet.importKey(0, key);
@ -3291,7 +3291,7 @@ RPC.prototype.importwallet = co(function* importwallet(args, help) {
if (parts.length < 4) if (parts.length < 4)
throw new RPCError('Malformed wallet.'); throw new RPCError('Malformed wallet.');
secret = KeyRing.fromSecret(parts[0]); secret = KeyRing.fromSecret(parts[0], this.network);
time = +parts[1]; time = +parts[1];
label = parts[2]; label = parts[2];
@ -3332,7 +3332,7 @@ RPC.prototype.importaddress = co(function* importaddress(args, help) {
if (rescan && this.chain.options.prune) if (rescan && this.chain.options.prune)
throw new RPCError('Cannot rescan when pruned.'); throw new RPCError('Cannot rescan when pruned.');
addr = Address.fromBase58(addr); addr = Address.fromBase58(addr, this.network);
yield wallet.importAddress(0, addr); yield wallet.importAddress(0, addr);
@ -3888,7 +3888,7 @@ RPC.prototype.sendfrom = co(function* sendfrom(args, help) {
} }
account = toString(args[0]); account = toString(args[0]);
address = Address.fromBase58(toString(args[1])); address = Address.fromBase58(toString(args[1]), this.network);
amount = toSatoshi(args[2]); amount = toSatoshi(args[2]);
if (!account) if (!account)
@ -3940,7 +3940,7 @@ RPC.prototype.sendmany = co(function* sendmany(args, help) {
for (i = 0; i < keys.length; i++) { for (i = 0; i < keys.length; i++) {
key = keys[i]; key = keys[i];
value = toSatoshi(sendTo[key]); value = toSatoshi(sendTo[key]);
address = Address.fromBase58(key); address = Address.fromBase58(key, this.network);
hash = address.getHash('hex'); hash = address.getHash('hex');
if (uniq[hash]) if (uniq[hash])
@ -3976,7 +3976,7 @@ RPC.prototype.sendtoaddress = co(function* sendtoaddress(args, help) {
+ ' subtractfeefromamount )'); + ' subtractfeefromamount )');
} }
address = Address.fromBase58(toString(args[0])); address = Address.fromBase58(toString(args[0]), this.network);
amount = toSatoshi(args[1]); amount = toSatoshi(args[1]);
subtractFee = toBool(args[4]); subtractFee = toBool(args[4]);

View File

@ -345,7 +345,7 @@ HTTPServer.prototype._init = function _init() {
if (params.accountKey) { if (params.accountKey) {
enforce(typeof params.accountKey === 'string', enforce(typeof params.accountKey === 'string',
'accountKey must be a string.'); 'accountKey must be a string.');
options.accountKey = HD.fromBase58(params.accountKey); options.accountKey = HD.fromBase58(params.accountKey, this.network);
} }
if (params.timeout != null) { if (params.timeout != null) {
@ -371,9 +371,7 @@ HTTPServer.prototype._init = function _init() {
if (output.address) { if (output.address) {
enforce(typeof output.address === 'string', enforce(typeof output.address === 'string',
'Address must be a string.'); 'Address must be a string.');
output.address = Address.fromBase58(output.address); output.address = Address.fromBase58(output.address, this.network);
enforce(output.address.verifyNetwork(this.network),
'Wrong network for address.');
} else if (output.script) { } else if (output.script) {
enforce(typeof output.script === 'string', enforce(typeof output.script === 'string',
'Script must be a string.'); 'Script must be a string.');
@ -396,13 +394,13 @@ HTTPServer.prototype._init = function _init() {
for (i = 0; i < params.address.length; i++) { for (i = 0; i < params.address.length; i++) {
address = params.address[i]; address = params.address[i];
enforce(typeof address === 'string', 'Address must be a string.'); enforce(typeof address === 'string', 'Address must be a string.');
address = Address.fromBase58(address); address = Address.fromBase58(address, this.network);
options.address.push(address); options.address.push(address);
} }
} else { } else {
enforce(typeof params.address === 'string', enforce(typeof params.address === 'string',
'Address must be a string.'); 'Address must be a string.');
options.address = Address.fromBase58(params.address); options.address = Address.fromBase58(params.address, this.network);
} }
} }
@ -439,7 +437,7 @@ HTTPServer.prototype._init = function _init() {
if (params.privateKey) { if (params.privateKey) {
enforce(typeof params.privateKey === 'string', 'Key must be a string.'); enforce(typeof params.privateKey === 'string', 'Key must be a string.');
options.privateKey = KeyRing.fromSecret(params.privateKey); options.privateKey = KeyRing.fromSecret(params.privateKey, this.network);
} }
if (params.publicKey) { if (params.publicKey) {
@ -450,7 +448,7 @@ HTTPServer.prototype._init = function _init() {
if (params.master) { if (params.master) {
enforce(typeof params.master === 'string', 'Key must be a string.'); enforce(typeof params.master === 'string', 'Key must be a string.');
options.master = HD.fromBase58(params.master); options.master = HD.fromBase58(params.master, this.network);
} }
if (params.mnemonic) { if (params.mnemonic) {

View File

@ -284,23 +284,31 @@ Address.fromRaw = function fromRaw(data) {
* Inject properties from base58 address. * Inject properties from base58 address.
* @private * @private
* @param {Base58Address} data * @param {Base58Address} data
* @param {Network?} network
* @throws Parse error * @throws Parse error
*/ */
Address.prototype.fromBase58 = function fromBase58(data) { Address.prototype.fromBase58 = function fromBase58(data, network) {
assert(typeof data === 'string'); assert(typeof data === 'string');
return this.fromRaw(base58.decode(data));
this.fromRaw(base58.decode(data));
if (network && !this.verifyNetwork(network))
throw new Error('Network mismatch for address.');
return this;
}; };
/** /**
* Create an address object from a base58 address. * Create an address object from a base58 address.
* @param {Base58Address} address * @param {Base58Address} address
* @param {Network?} network
* @returns {Address} * @returns {Address}
* @throws Parse error. * @throws Parse error.
*/ */
Address.fromBase58 = function fromBase58(address) { Address.fromBase58 = function fromBase58(address, network) {
return new Address().fromBase58(address); return new Address().fromBase58(address, network);
}; };
/** /**

View File

@ -323,9 +323,10 @@ KeyRing.prototype.toSecret = function toSecret() {
* Inject properties from serialized CBitcoinSecret. * Inject properties from serialized CBitcoinSecret.
* @private * @private
* @param {Base58String} secret * @param {Base58String} secret
* @param {Network?} network
*/ */
KeyRing.prototype.fromSecret = function fromSecret(data) { KeyRing.prototype.fromSecret = function fromSecret(data, network) {
var br = new BufferReader(base58.decode(data), true); var br = new BufferReader(base58.decode(data), true);
var i, prefix, version, type, key, compressed; var i, prefix, version, type, key, compressed;
@ -351,17 +352,23 @@ KeyRing.prototype.fromSecret = function fromSecret(data) {
br.verifyChecksum(); br.verifyChecksum();
return this.fromPrivate(key, compressed, type); this.fromPrivate(key, compressed, type);
if (network && !this.verifyNetwork(network))
throw new Error('Network mismatch for WIF.');
return this;
}; };
/** /**
* Instantiate a keyring from a serialized CBitcoinSecret. * Instantiate a keyring from a serialized CBitcoinSecret.
* @param {Base58String} secret * @param {Base58String} secret
* @param {Network?} network
* @returns {KeyRing} * @returns {KeyRing}
*/ */
KeyRing.fromSecret = function fromSecret(data) { KeyRing.fromSecret = function fromSecret(data, network) {
return new KeyRing().fromSecret(data); return new KeyRing().fromSecret(data, network);
}; };
/** /**

View File

@ -280,7 +280,7 @@ Account.prototype.pushKey = function pushKey(key) {
var index; var index;
if (HD.isBase58(key)) if (HD.isBase58(key))
key = HD.fromBase58(key); key = HD.fromBase58(key, this.network);
assert(key.verifyNetwork(this.network), assert(key.verifyNetwork(this.network),
'Network mismatch for account key.'); 'Network mismatch for account key.');
@ -320,7 +320,7 @@ Account.prototype.pushKey = function pushKey(key) {
Account.prototype.spliceKey = function spliceKey(key) { Account.prototype.spliceKey = function spliceKey(key) {
if (HD.isBase58(key)) if (HD.isBase58(key))
key = HD.fromBase58(key); key = HD.fromBase58(key, this.network);
assert(key.verifyNetwork(this.network), assert(key.verifyNetwork(this.network),
'Network mismatch for account key.'); 'Network mismatch for account key.');

View File

@ -111,7 +111,7 @@ Wallet.prototype.fromOptions = function fromOptions(options) {
key = HD.fromMnemonic(null, this.network); key = HD.fromMnemonic(null, this.network);
if (HD.isBase58(key)) if (HD.isBase58(key))
key = HD.fromBase58(key); key = HD.fromBase58(key, this.network);
assert(HD.isPrivate(key), assert(HD.isPrivate(key),
'Must create wallet with hd private key.'); 'Must create wallet with hd private key.');
@ -697,7 +697,7 @@ Wallet.prototype._createAccount = co(function* createAccount(options, passphrase
key = options.accountKey; key = options.accountKey;
if (HD.isBase58(key)) if (HD.isBase58(key))
key = HD.fromBase58(key); key = HD.fromBase58(key, this.network);
if (!HD.isPublic(key)) if (!HD.isPublic(key))
throw new Error('Must add HD public keys to watch only wallet.'); throw new Error('Must add HD public keys to watch only wallet.');

View File

@ -106,11 +106,12 @@ WalletKey.fromScript = function fromScript(key, script, compressed, network) {
/** /**
* Instantiate a wallet key from a serialized CBitcoinSecret. * Instantiate a wallet key from a serialized CBitcoinSecret.
* @param {Base58String} secret * @param {Base58String} secret
* @param {Network?} network
* @returns {WalletKey} * @returns {WalletKey}
*/ */
WalletKey.fromSecret = function fromSecret(data) { WalletKey.fromSecret = function fromSecret(data, network) {
return new WalletKey().fromSecret(data); return new WalletKey().fromSecret(data, network);
}; };
/** /**