mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-27 23:55:49 +00:00
[CHORE] Merged PubKeyValidator into Account class
This commit is contained in:
@@ -10,13 +10,15 @@
|
||||
//
|
||||
|
||||
// var network = require("./network.js");
|
||||
|
||||
var async = require('async');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var util = require('util');
|
||||
var extend = require('extend');
|
||||
var Amount = require('./amount').Amount;
|
||||
var UInt160 = require('./uint160').UInt160;
|
||||
var TransactionManager = require('./transactionmanager').TransactionManager;
|
||||
var sjcl = require('./utils').sjcl;
|
||||
var Base = require('./base').Base;
|
||||
|
||||
/**
|
||||
* @constructor Account
|
||||
@@ -278,6 +280,124 @@ Account.prototype.submit = function(transaction) {
|
||||
this._transactionManager.submit(transaction);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Check whether the given public key is valid for this account
|
||||
*
|
||||
* @param {Hex-encoded String|RippleAddress} public_key
|
||||
* @param {Function} callback
|
||||
*
|
||||
* @callback
|
||||
* @param {Error} err
|
||||
* @param {Boolean} true if the public key is valid and active, false otherwise
|
||||
*/
|
||||
Account.prototype.publicKeyIsActive = function(public_key, callback) {
|
||||
|
||||
var self = this;
|
||||
|
||||
var public_key_as_uint160;
|
||||
try {
|
||||
public_key_as_uint160 = Account._publicKeyToAddress(public_key);
|
||||
} catch (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
function getAccountInfo(async_callback) {
|
||||
self.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 === self._account_id) {
|
||||
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
|
||||
// if the public key matches the account address and the lsfDisableMaster is not set
|
||||
if (account_info.RegularKey &&
|
||||
account_info.RegularKey === public_key_as_uint160) {
|
||||
|
||||
async_callback(null, true);
|
||||
|
||||
} else if (account_info.Account === public_key_as_uint160 &&
|
||||
((account_info.Flags & 0x00100000) === 0)) {
|
||||
|
||||
async_callback(null, true);
|
||||
|
||||
} else {
|
||||
|
||||
async_callback(null, false);
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
var steps = [
|
||||
getAccountInfo,
|
||||
publicKeyIsValid
|
||||
];
|
||||
|
||||
async.waterfall(steps, callback);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert a hex-encoded public key to a Ripple Address
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @param {Hex-encoded string|RippleAddress} public_key
|
||||
* @returns {RippleAddress}
|
||||
*/
|
||||
Account._publicKeyToAddress = 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'));
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
exports.Account = Account;
|
||||
|
||||
// vim:sw=2:sts=2:ts=8:et
|
||||
|
||||
@@ -4,7 +4,7 @@ var sjcl = require('./utils').sjcl;
|
||||
var Remote = require('./remote').Remote;
|
||||
var Seed = require('./seed').Seed;
|
||||
var KeyPair = require('./keypair').KeyPair;
|
||||
var PublicKeyValidator = require('./pubkeyvalidator');
|
||||
var Account = require('./account').Account;
|
||||
var UInt160 = require('./uint160').UInt160;
|
||||
|
||||
// Message class (static)
|
||||
@@ -186,8 +186,8 @@ Message.verifyHashSignature = function(data, remote, callback) {
|
||||
key_pair._pubkey = public_key;
|
||||
var public_key_hex = key_pair.to_hex_pub();
|
||||
|
||||
var public_key_validator = new PublicKeyValidator(remote);
|
||||
public_key_validator.validate(account, public_key_hex, async_callback);
|
||||
var account_class_instance = new Account(remote, account);
|
||||
account_class_instance.publicKeyIsActive(public_key_hex, async_callback);
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
var async = require('async');
|
||||
var UInt160 = require('./uint160').UInt160;
|
||||
var sjcl = require('./utils').sjcl;
|
||||
var Base = require('./base').Base;
|
||||
|
||||
|
||||
/**
|
||||
* @constructor PubKeyValidator
|
||||
* @param {Remote} remote
|
||||
*/
|
||||
|
||||
function PubKeyValidator(remote) {
|
||||
|
||||
var self = this;
|
||||
|
||||
if (remote) {
|
||||
self._remote = remote;
|
||||
} else {
|
||||
throw(new Error('Must instantiate the PubKeyValidator with a ripple-lib Remote'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the public key is valid for the specified address.
|
||||
*
|
||||
* @param {String} address
|
||||
* @param {String} public_key
|
||||
* @param {Function} callback
|
||||
*
|
||||
* callback function is called with (err, is_valid), where is_valid
|
||||
* is a boolean indicating whether the public_key supplied is active
|
||||
*/
|
||||
|
||||
PubKeyValidator.prototype.validate = function(address, public_key, callback) {
|
||||
|
||||
var self = this;
|
||||
|
||||
var public_key_as_uint160;
|
||||
try {
|
||||
public_key_as_uint160 = self._parsePublicKey(public_key);
|
||||
} catch (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
|
||||
function getAccountInfo(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
|
||||
// if the public key matches the account address and the lsfDisableMaster is not set
|
||||
if (account_info.RegularKey &&
|
||||
account_info.RegularKey === public_key_as_uint160) {
|
||||
|
||||
async_callback(null, true);
|
||||
|
||||
} else if (account_info.Account === public_key_as_uint160 &&
|
||||
((account_info.Flags & 0x00100000) === 0)) {
|
||||
|
||||
async_callback(null, true);
|
||||
|
||||
} else {
|
||||
|
||||
async_callback(null, false);
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
var steps = [
|
||||
getAccountInfo,
|
||||
publicKeyIsValid
|
||||
];
|
||||
|
||||
async.waterfall(steps, 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;
|
||||
@@ -1,44 +1,41 @@
|
||||
var assert = require('assert');
|
||||
var PubKeyValidator = require('../src/js/ripple/pubkeyvalidator');
|
||||
var Account = require('../src/js/ripple/account').Account;
|
||||
|
||||
describe('PubKeyValidator', function(){
|
||||
describe('Account', function(){
|
||||
|
||||
describe('._parsePublicKey()', function(){
|
||||
|
||||
var pkv = new PubKeyValidator({});
|
||||
describe('._publicKeyToAddress()', function(){
|
||||
|
||||
it('should throw an error if the key is invalid', function(){
|
||||
try {
|
||||
pkv._parsePublicKey('not a real key');
|
||||
Account._publicKeyToAddress('not a real key');
|
||||
} catch (e) {
|
||||
assert(e);
|
||||
}
|
||||
});
|
||||
|
||||
it('should return unchanged a valid UINT160', function(){
|
||||
assert('rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz' === pkv._parsePublicKey('rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz'));
|
||||
assert('rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz' === Account._publicKeyToAddress('rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz'));
|
||||
});
|
||||
|
||||
it('should parse a hex-encoded public key as a UINT160', function(){
|
||||
assert('rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz' === pkv._parsePublicKey('025B32A54BFA33FB781581F49B235C0E2820C929FF41E677ADA5D3E53CFBA46332'));
|
||||
assert('rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz' === Account._publicKeyToAddress('025B32A54BFA33FB781581F49B235C0E2820C929FF41E677ADA5D3E53CFBA46332'));
|
||||
|
||||
assert('rLpq5RcRzA8FU1yUqEPW4xfsdwon7casuM' === pkv._parsePublicKey('03BFA879C00D58CF55F2B5975FF9B5293008FF49BEFB3EE6BEE2814247BF561A23'));
|
||||
assert('rLpq5RcRzA8FU1yUqEPW4xfsdwon7casuM' === Account._publicKeyToAddress('03BFA879C00D58CF55F2B5975FF9B5293008FF49BEFB3EE6BEE2814247BF561A23'));
|
||||
|
||||
assert('rP4yWwjoDGF2iZSBdAQAgpC449YDezEbT1' === pkv._parsePublicKey('02DF0AB18930B6410CA9F55CB37541F1FED891B8EDF8AB1D01D8F23018A4B204A7'));
|
||||
assert('rP4yWwjoDGF2iZSBdAQAgpC449YDezEbT1' === Account._publicKeyToAddress('02DF0AB18930B6410CA9F55CB37541F1FED891B8EDF8AB1D01D8F23018A4B204A7'));
|
||||
|
||||
assert('rLdfp6eoR948KVxfn6EpaaNTKwfwXhzSeQ' === pkv._parsePublicKey('0310C451A40CAFFD39D6B8A3BD61BF65BCA55246E9DABC3170EBE431D30655B61F'));
|
||||
assert('rLdfp6eoR948KVxfn6EpaaNTKwfwXhzSeQ' === Account._publicKeyToAddress('0310C451A40CAFFD39D6B8A3BD61BF65BCA55246E9DABC3170EBE431D30655B61F'));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('.validate()', function(){
|
||||
describe('.publicKeyIsActive()', function(){
|
||||
|
||||
it('should respond true if the public key corresponds to the account address and the master key IS NOT disabled', function(){
|
||||
|
||||
var pkv = new PubKeyValidator({
|
||||
account: function(address){
|
||||
return {
|
||||
getInfo: function(callback) {
|
||||
var account = new Account({
|
||||
on: function(){},
|
||||
request_account_info: function(address, callback) {
|
||||
if (address === 'rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz') {
|
||||
callback(null, { account_data: {
|
||||
Account: 'rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz',
|
||||
@@ -47,10 +44,8 @@ describe('PubKeyValidator', function(){
|
||||
}});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
pkv.validate('rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz', '025B32A54BFA33FB781581F49B235C0E2820C929FF41E677ADA5D3E53CFBA46332', function(err, is_valid){
|
||||
}, 'rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz');
|
||||
account.publicKeyIsActive('025B32A54BFA33FB781581F49B235C0E2820C929FF41E677ADA5D3E53CFBA46332', function(err, is_valid){
|
||||
assert(err === null);
|
||||
assert(is_valid === true);
|
||||
});
|
||||
@@ -59,10 +54,9 @@ describe('PubKeyValidator', function(){
|
||||
|
||||
it('should respond false if the public key corresponds to the account address and the master key IS disabled', function(){
|
||||
|
||||
var pkv = new PubKeyValidator({
|
||||
account: function(address){
|
||||
return {
|
||||
getInfo: function(callback) {
|
||||
var account = new Account({
|
||||
on: function(){},
|
||||
request_account_info: function(address, callback) {
|
||||
if (address === 'rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz') {
|
||||
callback(null, { account_data: {
|
||||
Account: 'rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz',
|
||||
@@ -71,10 +65,8 @@ describe('PubKeyValidator', function(){
|
||||
}});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
pkv.validate('rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz', '025B32A54BFA33FB781581F49B235C0E2820C929FF41E677ADA5D3E53CFBA46332', function(err, is_valid){
|
||||
}, 'rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz');
|
||||
account.publicKeyIsActive('025B32A54BFA33FB781581F49B235C0E2820C929FF41E677ADA5D3E53CFBA46332', function(err, is_valid){
|
||||
assert(err === null);
|
||||
assert(is_valid === false);
|
||||
});
|
||||
@@ -83,10 +75,9 @@ describe('PubKeyValidator', function(){
|
||||
|
||||
it('should respond true if the public key corresponds to the regular key', function(){
|
||||
|
||||
var pkv = new PubKeyValidator({
|
||||
account: function(address){
|
||||
return {
|
||||
getInfo: function(callback) {
|
||||
var account = new Account({
|
||||
on: function(){},
|
||||
request_account_info: function(address, callback) {
|
||||
if (address === 'rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz') {
|
||||
callback(null, { account_data: {
|
||||
Account: 'rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz',
|
||||
@@ -96,10 +87,8 @@ describe('PubKeyValidator', function(){
|
||||
}});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
pkv.validate('rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz', '02BE53B7ACBB0900E0BB7729C9CAC1033A0137993B17800BD1191BBD1B29D96A8C', function(err, is_valid){
|
||||
}, 'rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz');
|
||||
account.publicKeyIsActive('02BE53B7ACBB0900E0BB7729C9CAC1033A0137993B17800BD1191BBD1B29D96A8C', function(err, is_valid){
|
||||
assert(err === null);
|
||||
assert(is_valid === true);
|
||||
});
|
||||
@@ -108,10 +97,9 @@ describe('PubKeyValidator', function(){
|
||||
|
||||
it('should respond false if the public key does not correspond to an active public key for the account', function(){
|
||||
|
||||
var pkv = new PubKeyValidator({
|
||||
account: function(address){
|
||||
return {
|
||||
getInfo: function(callback) {
|
||||
var account = new Account({
|
||||
on: function(){},
|
||||
request_account_info: function(address, callback) {
|
||||
if (address === 'rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz') {
|
||||
callback(null, { account_data: {
|
||||
Account: 'rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz',
|
||||
@@ -121,10 +109,8 @@ describe('PubKeyValidator', function(){
|
||||
}});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
pkv.validate('rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz', '032ECDA93970BC7E8872EF6582CB52A5557F117244A949EB4FA8AC7688CF24FBC8', function(err, is_valid){
|
||||
}, 'rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz');
|
||||
account.publicKeyIsActive('032ECDA93970BC7E8872EF6582CB52A5557F117244A949EB4FA8AC7688CF24FBC8', function(err, is_valid){
|
||||
assert(err === null);
|
||||
assert(is_valid === false);
|
||||
});
|
||||
@@ -133,10 +119,9 @@ describe('PubKeyValidator', function(){
|
||||
|
||||
it('should respond false if the public key is invalid', function(){
|
||||
|
||||
var pkv = new PubKeyValidator({
|
||||
account: function(address){
|
||||
return {
|
||||
getInfo: function(callback) {
|
||||
var account = new Account({
|
||||
on: function(){},
|
||||
request_account_info: function(address, callback) {
|
||||
if (address === 'rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz') {
|
||||
callback(null, { account_data: {
|
||||
Account: 'rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz',
|
||||
@@ -146,10 +131,8 @@ describe('PubKeyValidator', function(){
|
||||
}});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
pkv.validate('rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz', 'not a real public key', function(err, is_valid){
|
||||
}, 'rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz');
|
||||
account.publicKeyIsActive('not a real public key', function(err, is_valid){
|
||||
assert(err);
|
||||
});
|
||||
|
||||
@@ -157,10 +140,9 @@ 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) {
|
||||
var account = new Account({
|
||||
on: function(){},
|
||||
request_account_info: function(address, callback) {
|
||||
if (address === 'rLdfp6eoR948KVxfn6EpaaNTKwfwXhzSeQ') {
|
||||
callback({ error: 'remoteError',
|
||||
error_message: 'Remote reported an error.',
|
||||
@@ -186,10 +168,8 @@ describe('PubKeyValidator', function(){
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
pkv.validate('rLdfp6eoR948KVxfn6EpaaNTKwfwXhzSeQ', '0310C451A40CAFFD39D6B8A3BD61BF65BCA55246E9DABC3170EBE431D30655B61F', function(err, is_valid){
|
||||
}, 'rLdfp6eoR948KVxfn6EpaaNTKwfwXhzSeQ');
|
||||
account.publicKeyIsActive('0310C451A40CAFFD39D6B8A3BD61BF65BCA55246E9DABC3170EBE431D30655B61F', function(err, is_valid){
|
||||
assert(!err);
|
||||
assert(is_valid);
|
||||
});
|
||||
327
test/message-test.js
Normal file
327
test/message-test.js
Normal file
@@ -0,0 +1,327 @@
|
||||
var assert = require('assert');
|
||||
var sjcl = require('../build/sjcl');
|
||||
var Message = require('../src/js/ripple/message');
|
||||
var Seed = require('../src/js/ripple/seed').Seed;
|
||||
var Remote = require('../src/js/ripple/remote').Remote;
|
||||
|
||||
describe('Message', function(){
|
||||
|
||||
describe('signMessage', function(){
|
||||
|
||||
it('should prepend the MAGIC_BYTES, call the HASH_FUNCTION, and then call signHash', function(){
|
||||
|
||||
var normal_signHash = Message.signHash;
|
||||
|
||||
var message_text = 'Hello World!';
|
||||
|
||||
var signHash_called = false;
|
||||
Message.signHash = function(hash) {
|
||||
signHash_called = true;
|
||||
assert.deepEqual(hash, Message.HASH_FUNCTION(Message.MAGIC_BYTES + message_text));
|
||||
};
|
||||
|
||||
Message.signMessage(message_text);
|
||||
assert(signHash_called);
|
||||
|
||||
Message.signHash = normal_signHash;
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('signHash', function(){
|
||||
|
||||
it('should accept the hash as either a hex string or a bitArray', function(){
|
||||
|
||||
var normal_random = sjcl.random.randomWords;
|
||||
|
||||
sjcl.random.randomWords = function(num_words){
|
||||
var words = [];
|
||||
for (var w = 0; w < num_words; w++) {
|
||||
words.push(sjcl.codec.hex.toBits('00000000'));
|
||||
}
|
||||
return words;
|
||||
};
|
||||
|
||||
var secret_string = 'safRpB5euNL52PZPTSqrE9gvuFwTC';
|
||||
// var address = 'rLLzaq61D633b5hhbNXKM9CkrYHboobVv3';
|
||||
var hash = 'e865bcc63a86ef21585ac8340a7cc8590ed85175a2a718c6fb2bfb2715d13778';
|
||||
|
||||
var signature1 = Message.signHash(hash, secret_string);
|
||||
var signature2 = Message.signHash(sjcl.codec.hex.toBits(hash), secret_string);
|
||||
|
||||
assert.strictEqual(signature1, signature2);
|
||||
|
||||
sjcl.random.randomWords = normal_random;
|
||||
|
||||
});
|
||||
|
||||
it('should accept the secret as a string or scjl.ecc.ecdsa.secretKey object', function(){
|
||||
|
||||
var normal_random = sjcl.random.randomWords;
|
||||
|
||||
sjcl.random.randomWords = function(num_words){
|
||||
var words = [];
|
||||
for (var w = 0; w < num_words; w++) {
|
||||
words.push(sjcl.codec.hex.toBits('00000000'));
|
||||
}
|
||||
return words;
|
||||
};
|
||||
|
||||
var secret_string = 'safRpB5euNL52PZPTSqrE9gvuFwTC';
|
||||
// var address = 'rLLzaq61D633b5hhbNXKM9CkrYHboobVv3';
|
||||
var hash = 'e865bcc63a86ef21585ac8340a7cc8590ed85175a2a718c6fb2bfb2715d13778';
|
||||
|
||||
var signature1 = Message.signHash(hash, secret_string);
|
||||
var signature2 = Message.signHash(hash, Seed.from_json(secret_string).get_key()._secret);
|
||||
|
||||
assert.strictEqual(signature1, signature2);
|
||||
|
||||
sjcl.random.randomWords = normal_random;
|
||||
|
||||
});
|
||||
|
||||
it('should throw an error if given an invalid secret key', function(){
|
||||
|
||||
var secret_string = 'badsafRpB5euNL52PZPTSqrE9gvuFwTC';
|
||||
var hash = 'e865bcc63a86ef21585ac8340a7cc8590ed85175a2a718c6fb2bfb2715d13778';
|
||||
|
||||
assert.throws(function(){
|
||||
Message.signHash(hash, secret_string);
|
||||
}, /Cannot\ generate\ keys\ from\ invalid\ seed/);
|
||||
|
||||
});
|
||||
|
||||
it('should throw an error if the parameters are reversed', function(){
|
||||
|
||||
var secret_string = 'safRpB5euNL52PZPTSqrE9gvuFwTC';
|
||||
var hash = 'e865bcc63a86ef21585ac8340a7cc8590ed85175a2a718c6fb2bfb2715d13778';
|
||||
|
||||
assert.throws(function(){
|
||||
Message.signHash(secret_string, hash);
|
||||
}, Error);
|
||||
|
||||
assert.throws(function(){
|
||||
Message.signHash(secret_string, sjcl.codec.hex.toBits(hash));
|
||||
}, Error);
|
||||
|
||||
assert.throws(function(){
|
||||
Message.signHash(Seed.from_json(secret_string).get_key()._secret, hash);
|
||||
}, Error);
|
||||
|
||||
assert.throws(function(){
|
||||
Message.signHash(Seed.from_json(secret_string).get_key()._secret, sjcl.codec.hex.toBits(hash));
|
||||
}, Error);
|
||||
|
||||
});
|
||||
|
||||
it('should produce a base64-encoded signature', function(){
|
||||
var REGEX_BASE64 = /^([A-Za-z0-9\+]{4})*([A-Za-z0-9\+]{2}==)|([A-Za-z0-9\+]{3}=)?$/;
|
||||
|
||||
var normal_random = sjcl.random.randomWords;
|
||||
|
||||
sjcl.random.randomWords = function(num_words){
|
||||
var words = [];
|
||||
for (var w = 0; w < num_words; w++) {
|
||||
words.push(sjcl.codec.hex.toBits('00000000'));
|
||||
}
|
||||
return words;
|
||||
};
|
||||
|
||||
var secret_string = 'safRpB5euNL52PZPTSqrE9gvuFwTC';
|
||||
// var address = 'rLLzaq61D633b5hhbNXKM9CkrYHboobVv3';
|
||||
var hash = 'e865bcc63a86ef21585ac8340a7cc8590ed85175a2a718c6fb2bfb2715d13778';
|
||||
|
||||
var signature = Message.signHash(hash, secret_string);
|
||||
|
||||
assert(REGEX_BASE64.test(signature));
|
||||
|
||||
sjcl.random.randomWords = normal_random;
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('verifyMessageSignature', function(){
|
||||
|
||||
it('should prepend the MAGIC_BYTES, call the HASH_FUNCTION, and then call verifyHashSignature', function(){
|
||||
|
||||
var normal_verifyHashSignature = Message.verifyHashSignature;
|
||||
|
||||
var data = {
|
||||
message: 'Hello world!',
|
||||
signature: 'AAAAGzFa1pYjhssCpDFZgFSnYQ8qCnMkLaZrg0mXZyNQ2NxgMQ8z9U3ngYerxSZCEt3Q4raMIpt03db7jDNGbfmHy8I='
|
||||
};
|
||||
|
||||
var verifyHashSignature_called = false;
|
||||
Message.verifyHashSignature = function(vhs_data, remote, callback) {
|
||||
verifyHashSignature_called = true;
|
||||
|
||||
assert.deepEqual(vhs_data.hash, Message.HASH_FUNCTION(Message.MAGIC_BYTES + data.message));
|
||||
assert.strictEqual(vhs_data.signature, data.signature);
|
||||
callback();
|
||||
|
||||
};
|
||||
|
||||
Message.verifyMessageSignature(data, {}, function(err){
|
||||
assert(!err);
|
||||
});
|
||||
assert(verifyHashSignature_called);
|
||||
|
||||
Message.verifyHashSignature = normal_verifyHashSignature;
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('verifyHashSignature', function(){
|
||||
|
||||
it('should throw an error if a callback function is not supplied', function(){
|
||||
|
||||
var data = {
|
||||
message: 'Hello world!',
|
||||
hash: '861844d6704e8573fec34d967e20bcfef3d424cf48be04e6dc08f2bd58c729743371015ead891cc3cf1c9d34b49264b510751b1ff9e537937bc46b5d6ff4ecc8',
|
||||
signature: 'AAAAHOUJQzG/7BO82fGNt1TNE+GGVXKuQQ0N2nTO+iJETE69PiHnaAkkOzovM177OosxbKjpt3KvwuJflgUB2YGvgjk=',
|
||||
account: 'rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz'
|
||||
};
|
||||
|
||||
Remote.prototype.addServer = function(){};
|
||||
var test_remote = new Remote({});
|
||||
|
||||
assert.throws(function(){
|
||||
Message.verifyHashSignature(data);
|
||||
}, /(?=.*callback\ function).*/);
|
||||
|
||||
|
||||
});
|
||||
|
||||
it('should respond with an error if the hash is missing or invalid', function(done){
|
||||
|
||||
var data = {
|
||||
message: 'Hello world!',
|
||||
signature: 'AAAAHOUJQzG/7BO82fGNt1TNE+GGVXKuQQ0N2nTO+iJETE69PiHnaAkkOzovM177OosxbKjpt3KvwuJflgUB2YGvgjk=',
|
||||
account: 'rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz'
|
||||
};
|
||||
|
||||
Remote.prototype.addServer = function(){};
|
||||
var test_remote = new Remote({});
|
||||
test_remote.state = 'online';
|
||||
|
||||
Message.verifyHashSignature(data, test_remote, function(err, valid){
|
||||
assert(/hash/i.test(err.message));
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('should respond with an error if the account is missing or invalid', function(done){
|
||||
|
||||
var data = {
|
||||
message: 'Hello world!',
|
||||
hash: '861844d6704e8573fec34d967e20bcfef3d424cf48be04e6dc08f2bd58c729743371015ead891cc3cf1c9d34b49264b510751b1ff9e537937bc46b5d6ff4ecc8',
|
||||
signature: 'AAAAHOUJQzG/7BO82fGNt1TNE+GGVXKuQQ0N2nTO+iJETE69PiHnaAkkOzovM177OosxbKjpt3KvwuJflgUB2YGvgjk='
|
||||
};
|
||||
|
||||
Remote.prototype.addServer = function(){};
|
||||
var test_remote = new Remote({});
|
||||
test_remote.state = 'online';
|
||||
|
||||
Message.verifyHashSignature(data, test_remote, function(err, valid){
|
||||
assert(/account|address/i.test(err.message));
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('should respond with an error if the signature is missing or invalid', function(done){
|
||||
|
||||
var data = {
|
||||
message: 'Hello world!',
|
||||
hash: '861844d6704e8573fec34d967e20bcfef3d424cf48be04e6dc08f2bd58c729743371015ead891cc3cf1c9d34b49264b510751b1ff9e537937bc46b5d6ff4ecc8',
|
||||
account: 'rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz'
|
||||
};
|
||||
|
||||
Remote.prototype.addServer = function(){};
|
||||
var test_remote = new Remote({});
|
||||
test_remote.state = 'online';
|
||||
|
||||
Message.verifyHashSignature(data, test_remote, function(err, valid){
|
||||
assert(/signature/i.test(err.message));
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('should respond true if the signature is valid and corresponds to an active public key for the account', function(done){
|
||||
|
||||
var data = {
|
||||
message: 'Hello world!',
|
||||
hash: 'e9a82ea40514787918959b1100481500a5d384030f8770575c6a587675025fe212e6623e25643f251666a7b8b23af476c2850a8ea92153de5724db432892c752',
|
||||
account: 'rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz',
|
||||
signature: 'AAAAHMIPCQGLgdnpX1Ccv1wHb56H4NggxIM6U08Qkb9mUjN2Vn9pZ3CHvq1yWLBi6NqpW+7kedLnmfu4VG2+y43p4Xs='
|
||||
};
|
||||
|
||||
Remote.prototype.addServer = function(){};
|
||||
var test_remote = new Remote({});
|
||||
test_remote.state = 'online';
|
||||
test_remote.request_account_info = function(account, callback) {
|
||||
if (account === data.account) {
|
||||
callback(null, {
|
||||
"account_data": {
|
||||
"Account": "rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz",
|
||||
"Flags": 1114112,
|
||||
"LedgerEntryType": "AccountRoot",
|
||||
"RegularKey": "rHq2wyUtLkAad3vURUk33q9gozd97skhSf"
|
||||
}
|
||||
});
|
||||
} else {
|
||||
callback(new Error('wrong account'));
|
||||
}
|
||||
};
|
||||
|
||||
Message.verifyHashSignature(data, test_remote, function(err, valid){
|
||||
assert(!err);
|
||||
assert(valid);
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('should respond false if a key can be recovered from the signature but it does not correspond to an active public key', function(done){
|
||||
|
||||
// Signature created by disabled master key
|
||||
var data = {
|
||||
message: 'Hello world!',
|
||||
hash: 'e9a82ea40514787918959b1100481500a5d384030f8770575c6a587675025fe212e6623e25643f251666a7b8b23af476c2850a8ea92153de5724db432892c752',
|
||||
account: 'rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz',
|
||||
signature: 'AAAAG+dB/rAjZ5m8eQ/opcqQOJsFbKxOu9jq9KrOAlNO4OdcBDXyCBlkZqS9Xr8oZI2uh0boVsgYOS3pOLJz+Dh3Otk='
|
||||
};
|
||||
|
||||
Remote.prototype.addServer = function(){};
|
||||
var test_remote = new Remote({});
|
||||
test_remote.state = 'online';
|
||||
test_remote.request_account_info = function(account, callback) {
|
||||
if (account === data.account) {
|
||||
callback(null, {
|
||||
"account_data": {
|
||||
"Account": "rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz",
|
||||
"Flags": 1114112,
|
||||
"LedgerEntryType": "AccountRoot",
|
||||
"RegularKey": "rHq2wyUtLkAad3vURUk33q9gozd97skhSf"
|
||||
}
|
||||
});
|
||||
} else {
|
||||
callback(new Error('wrong account'));
|
||||
}
|
||||
};
|
||||
|
||||
Message.verifyHashSignature(data, test_remote, function(err, valid){
|
||||
assert(!err);
|
||||
assert(!valid);
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
Reference in New Issue
Block a user