[FIX] Handling public key validation for unfunded accounts

This commit is contained in:
Evan Schwartz
2014-05-01 17:22:20 -07:00
parent 13a6a2c335
commit a2b07d5edd
4 changed files with 109 additions and 27 deletions

View File

@@ -170,7 +170,12 @@ Message.verifyHashSignature = function(data, remote, callback) {
} catch (err) {
return async_callback(err);
}
if (public_key) {
async_callback(null, public_key);
} else {
async_callback(new Error('Could not recover public key from signature'));
}
};

View File

@@ -19,27 +19,6 @@ function PubKeyValidator(remote) {
throw(new Error('Must instantiate the PubKeyValidator with a ripple-lib Remote'));
}
// Convert hex string to UInt160
self._parsePublicKey = function(public_key) {
// Based on functions in /src/js/ripple/keypair.js
function hexToUInt160(public_key) {
var bits = sjcl.codec.hex.toBits(public_key);
var hash = sjcl.hash.ripemd160.hash(sjcl.hash.sha256.hash(bits));
var address = UInt160.from_bits(hash);
address.set_version(Base.VER_ACCOUNT_ID);
return address.to_json();
}
if (UInt160.is_valid(public_key)) {
return public_key;
} else if (/^[0-9a-fA-F]+$/.test(public_key)) {
return hexToUInt160(public_key);
} else {
throw(new Error('Public key is invalid. Must be a UInt160 or a hex string'));
}
};
}
/**
@@ -60,16 +39,37 @@ PubKeyValidator.prototype.validate = function(address, public_key, callback) {
var public_key_as_uint160;
try {
public_key_as_uint160 = self._parsePublicKey(public_key);
} catch (e) {
return callback(e);
} catch (err) {
return callback(err);
}
function getAccountInfo(async_callback) {
self._remote.account(address).getInfo(async_callback);
self._remote.account(address).getInfo(function(err, account_info_res){
// If the remote responds with an Account Not Found error then the account
// is unfunded and thus we can assume that the master key is active
if (err && err.remote && err.remote.error === 'actNotFound') {
async_callback(null, null);
} else {
async_callback(err, account_info_res);
}
});
};
function publicKeyIsValid(account_info_res, async_callback) {
// Catch the case of unfunded accounts
if (!account_info_res) {
if (public_key_as_uint160 === address) {
async_callback(null, true);
} else {
async_callback(null, false);
}
return;
}
var account_info = account_info_res.account_data;
// Respond with true if the RegularKey is set and matches the given public key or
@@ -101,4 +101,39 @@ PubKeyValidator.prototype.validate = function(address, public_key, callback) {
};
/**
* Convert a hex-encoded public key to a Ripple Address
*
* @param {Hex-encoded string|RippleAddress} public_key
* @returns {RippleAddress}
*/
PubKeyValidator.prototype._parsePublicKey = function(public_key) {
// Based on functions in /src/js/ripple/keypair.js
function hexToUInt160(public_key) {
var bits = sjcl.codec.hex.toBits(public_key);
var hash = sjcl.hash.ripemd160.hash(sjcl.hash.sha256.hash(bits));
var address = UInt160.from_bits(hash);
address.set_version(Base.VER_ACCOUNT_ID);
return address.to_json();
}
if (UInt160.is_valid(public_key)) {
return public_key;
} else if (/^[0-9a-fA-F]+$/.test(public_key)) {
return hexToUInt160(public_key);
} else {
throw(new Error('Public key is invalid. Must be a UInt160 or a hex string'));
}
};
module.exports = PubKeyValidator;

View File

@@ -70,8 +70,6 @@ sjcl.ecc.ecdsa.secretKey.prototype.signWithRecoverablePublicKey = function(hash,
*/
sjcl.ecc.ecdsa.publicKey.recoverFromSignature = function(hash, signature, curve) {
var self = this;
if (!signature || signature instanceof sjcl.ecc.curve) {
throw new sjcl.exception.invalid('must supply hash and signature to recover public key');
}

View File

@@ -25,6 +25,8 @@ describe('PubKeyValidator', function(){
assert('rLpq5RcRzA8FU1yUqEPW4xfsdwon7casuM' === pkv._parsePublicKey('03BFA879C00D58CF55F2B5975FF9B5293008FF49BEFB3EE6BEE2814247BF561A23'));
assert('rP4yWwjoDGF2iZSBdAQAgpC449YDezEbT1' === pkv._parsePublicKey('02DF0AB18930B6410CA9F55CB37541F1FED891B8EDF8AB1D01D8F23018A4B204A7'));
assert('rLdfp6eoR948KVxfn6EpaaNTKwfwXhzSeQ' === pkv._parsePublicKey('0310C451A40CAFFD39D6B8A3BD61BF65BCA55246E9DABC3170EBE431D30655B61F'));
});
});
@@ -153,5 +155,47 @@ describe('PubKeyValidator', function(){
});
it('should assume the master key is valid for unfunded accounts', function(){
var pkv = new PubKeyValidator({
account: function(address){
return {
getInfo: function(callback) {
if (address === 'rLdfp6eoR948KVxfn6EpaaNTKwfwXhzSeQ') {
callback({ error: 'remoteError',
error_message: 'Remote reported an error.',
remote:
{ account: 'rLdfp6eoR948KVxfn6EpaaNTKwfwXhzSeQ',
error: 'actNotFound',
error_code: 15,
error_message: 'Account not found.',
id: 3,
ledger_current_index: 6391106,
request:
{ account: 'rLdfp6eoR948KVxfn6EpaaNTKwfwXhzSeQ',
command: 'account_info',
id: 3,
ident: 'rLdfp6eoR948KVxfn6EpaaNTKwfwXhzSeQ' },
status: 'error',
type: 'response' },
result: 'remoteError',
engine_result: 'remoteError',
result_message: 'Remote reported an error.',
engine_result_message: 'Remote reported an error.',
message: 'Remote reported an error.'
});
}
}
}
}
});
pkv.validate('rLdfp6eoR948KVxfn6EpaaNTKwfwXhzSeQ', '0310C451A40CAFFD39D6B8A3BD61BF65BCA55246E9DABC3170EBE431D30655B61F', function(err, is_valid){
assert(!err);
assert(is_valid);
});
});
});
});