Merge branch 'release'

Conflicts:
	npm-shrinkwrap.json
	package.json
This commit is contained in:
Geert Weening
2015-04-29 08:50:44 -07:00
9 changed files with 166 additions and 613 deletions

View File

@@ -1,3 +1,9 @@
##0.12.4
+ [Improve entropy security](https://github.com/ripple/ripple-lib/commit/c7ba822320880037796f57876d1abb4e525648ed)
+ [Remove unused crypt.js file](https://github.com/ripple/ripple-lib/commit/1f68eba1461bca03a4d22872450d15ae5a185334)
##0.12.3
+ [Add getLedgerSequence to Remote](https://github.com/ripple/ripple-lib/commit/d09548d04d3238fca653d482ec1d5faa7254559a)
@@ -72,8 +78,8 @@
- [Handle invalid input in parse_human - c8f18c8c](https://github.com/ripple/ripple-lib/commit/c8f18c8c8590b7b48e370e0325b6677b7720294f)
- [Check for null in isNumber - b86790c8](https://github.com/ripple/ripple-lib/commit/b86790c8543c239a532fd7697d4652829019d385)
- [Cleanup amount.js - d0fb291c](https://github.com/ripple/ripple-lib/commit/d0fb291c4e330193a244902156f1d74730da357d)
**Changes**
+ [Add deprecation warnings to request constructors. The first argument to request constructor functions should be an object containing request properties](https://github.com/ripple/ripple-lib/commit/35d76b3520934285f80059c1badd6c522539104c)
@@ -88,13 +94,13 @@
+ [Bumped dependencies](https://github.com/ripple/ripple-lib/commit/f9bc7cc746b44b24b61bbe260ae2e9d9617286da)
##0.11.0
+ [Track the funded status of an order based on cumulative account orders](https://github.com/ripple/ripple-lib/commit/67d39737a4d5e0fcd9d9b47b9083ee00e5a9e652) and [67d3973](https://github.com/ripple/ripple-lib/commit/b6b99dde022e1e14c4797e454b1d7fca50e49482)
+ Remove blobvault client from ripple-lib, use the [`ripple-vault-client`](https://github.com/ripple/ripple-vault-client) instead [9b3d62b7](https://github.com/ripple/ripple-lib/commit/9b3d62b765c4c25beae6eb0fa57ef3a07f2581b1)
+ Remove blobvault client from ripple-lib, use the [`ripple-vault-client`](https://github.com/ripple/ripple-vault-client) instead [9b3d62b7](https://github.com/ripple/ripple-lib/commit/9b3d62b765c4c25beae6eb0fa57ef3a07f2581b1)
+ [Add support for `ledger` option in requestBookOffers](https://github.com/ripple/ripple-lib/commit/34c0677c453c409ef0a5b351959abdc176d3bacb)
@@ -133,13 +139,13 @@ are locally determined to have expired: `tejMaxLedger`.
+ [Improve memo support](https://github.com/ripple/ripple-lib/commit/1704ac4ae144c0ce54afad86f644c75a632080b1)
- Add `MemoFormat` property for memo
- Enforce `MemoFormat` and `MemoType` to be valid ASCII
- Support `text` and `json` MemoFormat
- Support `text` and `json` MemoFormat
+ [Update jscl library](https://github.com/ripple/ripple-lib/commit/3204998fcb6f31d6c90532a737a4adb8a1e420f6)
- Improved entropy by taking advantage of platform crypto
- Use jscl's k256 curve instead of altering the c256 curve with k256 configuration
- **deprecated:** the c256 curve is linked to the k256 curve to provide backwards compatibility, this link will be removed in the future
+ [Fix empty queue check on reconnect](https://github.com/ripple/ripple-lib/commit/3c21994adcf72d1fbd87d453ceb917f9ad6df4ec)
##0.9.4
@@ -168,7 +174,7 @@ are locally determined to have expired: `tejMaxLedger`.
+ [**Breaking change**: Change accountRequest method signature](https://github.com/ripple/ripple-lib/commit/6f5d1104aa3eb440c518ec4f39e264fdce15fa15)
+ [Add paging behavior for account requests, `account_lines` and `account_offers`](https://github.com/ripple/ripple-lib/commit/722f4e175dbbf378e51b49142d0285f87acb22d7)
+ [Add paging behavior for account requests, `account_lines` and `account_offers`](https://github.com/ripple/ripple-lib/commit/722f4e175dbbf378e51b49142d0285f87acb22d7)
+ [Add max_fee setter to transactions to set max fee the submitter is willing to pay] (https://github.com/ripple/ripple-lib/commit/24587fab9c8ad3840d7aa345a7037b48839e09d7)
@@ -184,7 +190,7 @@ var options = {
ledger: < valid ledger_index or ledger_hash >
}
// The `marker` comes back in an account request if there are more results than are returned
// The `marker` comes back in an account request if there are more results than are returned
// in the current response. The amount of results per response are determined by the `limit`.
if (marker) {
options.marker = < marker >;

153
npm-shrinkwrap.json generated
View File

@@ -1,180 +1,69 @@
{
"name": "ripple-lib",
"version": "0.12.3",
"version": "0.12.4",
"npm-shrinkwrap-version": "5.3.0",
"node-version": "v0.10.38",
"dependencies": {
"async": {
"version": "0.9.0",
"from": "async@>=0.9.0 <0.10.0",
"resolved": "https://registry.npmjs.org/async/-/async-0.9.0.tgz"
},
"bignumber.js": {
"version": "2.0.3",
"from": "bignumber.js@>=2.0.3 <3.0.0",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-2.0.3.tgz"
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-2.0.7.tgz"
},
"extend": {
"version": "1.2.1",
"from": "extend@>=1.2.1 <1.3.0",
"resolved": "https://registry.npmjs.org/extend/-/extend-1.2.1.tgz"
},
"lodash": {
"version": "3.5.0",
"from": "lodash@>=3.1.0 <4.0.0",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-3.5.0.tgz"
"version": "3.7.0",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-3.7.0.tgz"
},
"lru-cache": {
"version": "2.5.0",
"from": "lru-cache@>=2.5.0 <2.6.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz"
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.2.tgz"
},
"ripple-wallet-generator": {
"version": "1.0.2",
"from": "ripple-wallet-generator@1.0.2",
"resolved": "https://registry.npmjs.org/ripple-wallet-generator/-/ripple-wallet-generator-1.0.2.tgz"
},
"superagent": {
"version": "0.18.2",
"from": "superagent@>=0.18.0 <0.19.0",
"resolved": "https://registry.npmjs.org/superagent/-/superagent-0.18.2.tgz",
"dependencies": {
"qs": {
"version": "0.6.6",
"from": "qs@0.6.6",
"resolved": "https://registry.npmjs.org/qs/-/qs-0.6.6.tgz"
},
"formidable": {
"version": "1.0.14",
"from": "formidable@1.0.14",
"resolved": "https://registry.npmjs.org/formidable/-/formidable-1.0.14.tgz"
},
"mime": {
"version": "1.2.11",
"from": "mime@1.2.11",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz"
},
"component-emitter": {
"version": "1.1.2",
"from": "component-emitter@1.1.2",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.1.2.tgz"
},
"methods": {
"version": "1.0.1",
"from": "methods@1.0.1",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.0.1.tgz"
},
"cookiejar": {
"version": "2.0.1",
"from": "cookiejar@2.0.1",
"resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.0.1.tgz"
},
"debug": {
"version": "1.0.4",
"from": "debug@>=1.0.1 <1.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-1.0.4.tgz",
"dependencies": {
"ms": {
"version": "0.6.2",
"from": "ms@0.6.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz"
}
}
},
"reduce-component": {
"version": "1.0.1",
"from": "reduce-component@1.0.1",
"resolved": "https://registry.npmjs.org/reduce-component/-/reduce-component-1.0.1.tgz"
},
"form-data": {
"version": "0.1.3",
"from": "form-data@0.1.3",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-0.1.3.tgz",
"dependencies": {
"combined-stream": {
"version": "0.0.7",
"from": "combined-stream@>=0.0.4 <0.1.0",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz",
"dependencies": {
"delayed-stream": {
"version": "0.0.5",
"from": "delayed-stream@0.0.5",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz"
}
}
}
}
},
"readable-stream": {
"version": "1.0.27-1",
"from": "readable-stream@1.0.27-1",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.27-1.tgz",
"dependencies": {
"core-util-is": {
"version": "1.0.1",
"from": "core-util-is@>=1.0.0 <1.1.0",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz"
},
"isarray": {
"version": "0.0.1",
"from": "isarray@0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
},
"string_decoder": {
"version": "0.10.31",
"from": "string_decoder@>=0.10.0 <0.11.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz"
},
"inherits": {
"version": "2.0.1",
"from": "inherits@>=2.0.1 <2.1.0",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
}
}
}
}
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/ripple-wallet-generator/-/ripple-wallet-generator-1.0.3.tgz"
},
"ws": {
"version": "0.7.1",
"from": "ws@>=0.7.1 <0.8.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-0.7.1.tgz",
"dependencies": {
"options": {
"version": "0.0.6",
"from": "options@>=0.0.5",
"resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz"
},
"ultron": {
"version": "1.0.1",
"from": "ultron@>=1.0.0 <1.1.0",
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.1.tgz"
},
"bufferutil": {
"version": "1.0.1",
"from": "bufferutil@>=1.0.0 <1.1.0",
"resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-1.0.1.tgz",
"dependencies": {
"bindings": {
"version": "1.2.1",
"from": "bindings@>=1.2.0 <1.3.0"
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz"
},
"nan": {
"version": "1.6.2",
"from": "nan@>=1.6.0 <1.7.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-1.6.2.tgz"
}
}
},
"options": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz"
},
"ultron": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.1.tgz"
},
"utf-8-validate": {
"version": "1.0.1",
"from": "utf-8-validate@>=1.0.0 <1.1.0",
"resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-1.0.1.tgz",
"dependencies": {
"bindings": {
"version": "1.2.1",
"from": "bindings@>=1.2.0 <1.3.0"
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz"
},
"nan": {
"version": "1.6.2",
"from": "nan@>=1.6.0 <1.7.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-1.6.2.tgz"
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "ripple-lib",
"version": "0.12.3",
"version": "0.12.4",
"description": "A JavaScript API for interacting with Ripple in Node.js and the browser",
"files": [
"src/js/*",
@@ -20,9 +20,8 @@
"extend": "~1.2.1",
"lodash": "^3.1.0",
"lru-cache": "~2.5.0",
"ripple-wallet-generator": "1.0.2",
"ws": "~0.7.1",
"superagent": "^0.18.0"
"ripple-wallet-generator": "^1.0.3",
"ws": "~0.7.1"
},
"devDependencies": {
"assert-diff": "^1.0.1",

View File

@@ -1,333 +0,0 @@
var sjcl = require('./utils').sjcl;
var base = require('./base').Base;
var Seed = require('./seed').Seed;
var UInt160 = require('./uint160').UInt160;
var UInt256 = require('./uint256').UInt256;
var request = require('superagent');
var querystring = require('querystring');
var extend = require("extend");
var parser = require("url");
var Crypt = { };
var cryptConfig = {
cipher : 'aes',
mode : 'ccm',
ts : 64, // tag length
ks : 256, // key size
iter : 1000 // iterations (key derivation)
};
/**
* Full domain hash based on SHA512
*/
function fdh(data, bytelen) {
var bitlen = bytelen << 3;
if (typeof data === 'string') {
data = sjcl.codec.utf8String.toBits(data);
}
// Add hashing rounds until we exceed desired length in bits
var counter = 0, output = [];
while (sjcl.bitArray.bitLength(output) < bitlen) {
var hash = sjcl.hash.sha512.hash(sjcl.bitArray.concat([counter], data));
output = sjcl.bitArray.concat(output, hash);
counter++;
}
// Truncate to desired length
output = sjcl.bitArray.clamp(output, bitlen);
return output;
};
/**
* This is a function to derive different hashes from the same key.
* Each hash is derived as HMAC-SHA512HALF(key, token).
*
* @param {string} key
* @param {string} hash
*/
function keyHash(key, token) {
var hmac = new sjcl.misc.hmac(key, sjcl.hash.sha512);
return sjcl.codec.hex.fromBits(sjcl.bitArray.bitSlice(hmac.encrypt(token), 0, 256));
};
/**
* add entropy at each call to get random words
* @param {number} nWords
*/
function randomWords (nWords) {
for (var i = 0; i < 8; i++) {
sjcl.random.addEntropy(Math.random(), 32, "Math.random()");
}
return sjcl.random.randomWords(nWords);
}
/****** exposed functions ******/
/**
* KEY DERIVATION FUNCTION
*
* This service takes care of the key derivation, i.e. converting low-entropy
* secret into higher entropy secret via either computationally expensive
* processes or peer-assisted key derivation (PAKDF).
*
* @param {object} opts
* @param {string} purpose - Key type/purpose
* @param {string} username
* @param {string} secret - Also known as passphrase/password
* @param {function} fn
*/
Crypt.derive = function(opts, purpose, username, secret, fn) {
var tokens;
if (purpose === 'login') {
tokens = ['id', 'crypt'];
} else {
tokens = ['unlock'];
}
var iExponent = new sjcl.bn(String(opts.exponent));
var iModulus = new sjcl.bn(String(opts.modulus));
var iAlpha = new sjcl.bn(String(opts.alpha));
var publicInfo = [ 'PAKDF_1_0_0', opts.host.length, opts.host, username.length, username, purpose.length, purpose ].join(':') + ':';
var publicSize = Math.ceil(Math.min((7 + iModulus.bitLength()) >>> 3, 256) / 8);
var publicHash = fdh(publicInfo, publicSize);
var publicHex = sjcl.codec.hex.fromBits(publicHash);
var iPublic = new sjcl.bn(String(publicHex)).setBitM(0);
var secretInfo = [ publicInfo, secret.length, secret ].join(':') + ':';
var secretSize = (7 + iModulus.bitLength()) >>> 3;
var secretHash = fdh(secretInfo, secretSize);
var secretHex = sjcl.codec.hex.fromBits(secretHash);
var iSecret = new sjcl.bn(String(secretHex)).mod(iModulus);
if (iSecret.jacobi(iModulus) !== 1) {
iSecret = iSecret.mul(iAlpha).mod(iModulus);
}
var iRandom;
for (;;) {
iRandom = sjcl.bn.random(iModulus, 0);
if (iRandom.jacobi(iModulus) === 1) {
break;
}
}
var iBlind = iRandom.powermodMontgomery(iPublic.mul(iExponent), iModulus);
var iSignreq = iSecret.mulmod(iBlind, iModulus);
var signreq = sjcl.codec.hex.fromBits(iSignreq.toBits());
request.post(opts.url)
.send({ info: publicInfo, signreq: signreq })
.end(function(err, resp) {
if (err || !resp) {
return fn(new Error('Could not query PAKDF server ' + opts.host));
}
var data = resp.body || resp.text ? JSON.parse(resp.text) : {};
if (data.result !== 'success') {
return fn(new Error('Could not query PAKDF server '+opts.host));
}
var iSignres = new sjcl.bn(String(data.signres));
var iRandomInv = iRandom.inverseMod(iModulus);
var iSigned = iSignres.mulmod(iRandomInv, iModulus);
var key = iSigned.toBits();
var result = { };
tokens.forEach(function(token) {
result[token] = keyHash(key, token);
});
fn(null, result);
});
};
/**
* Imported from ripple-client
*/
/**
* Encrypt data
*
* @param {string} key
* @param {string} data
*/
Crypt.encrypt = function(key, data) {
key = sjcl.codec.hex.toBits(key);
var opts = extend(true, {}, cryptConfig);
var encryptedObj = JSON.parse(sjcl.encrypt(key, data, opts));
var version = [sjcl.bitArray.partial(8, 0)];
var initVector = sjcl.codec.base64.toBits(encryptedObj.iv);
var ciphertext = sjcl.codec.base64.toBits(encryptedObj.ct);
var encryptedBits = sjcl.bitArray.concat(version, initVector);
encryptedBits = sjcl.bitArray.concat(encryptedBits, ciphertext);
return sjcl.codec.base64.fromBits(encryptedBits);
};
/**
* Decrypt data
*
* @param {string} key
* @param {string} data
*/
Crypt.decrypt = function (key, data) {
key = sjcl.codec.hex.toBits(key);
var encryptedBits = sjcl.codec.base64.toBits(data);
var version = sjcl.bitArray.extract(encryptedBits, 0, 8);
if (version !== 0) {
throw new Error('Unsupported encryption version: '+version);
}
var encrypted = extend(true, {}, cryptConfig, {
iv: sjcl.codec.base64.fromBits(sjcl.bitArray.bitSlice(encryptedBits, 8, 8+128)),
ct: sjcl.codec.base64.fromBits(sjcl.bitArray.bitSlice(encryptedBits, 8+128))
});
return sjcl.decrypt(key, JSON.stringify(encrypted));
};
/**
* Validate a ripple address
*
* @param {string} address
*/
Crypt.isValidAddress = function (address) {
return UInt160.is_valid(address);
};
/**
* Create an encryption key
*
* @param {integer} nWords - number of words
*/
Crypt.createSecret = function (nWords) {
return sjcl.codec.hex.fromBits(randomWords(nWords));
};
/**
* Create a new master key
*/
Crypt.createMaster = function () {
return base.encode_check(33, sjcl.codec.bytes.fromBits(randomWords(4)));
};
/**
* Create a ripple address from a master key
*
* @param {string} masterkey
*/
Crypt.getAddress = function (masterkey) {
return Seed.from_json(masterkey).get_key().get_address().to_json();
};
/**
* Hash data using SHA-512.
*
* @param {string|bitArray} data
* @return {string} Hash of the data
*/
Crypt.hashSha512 = function (data) {
// XXX Should return a UInt512
return sjcl.codec.hex.fromBits(sjcl.hash.sha512.hash(data));
};
/**
* Hash data using SHA-512 and return the first 256 bits.
*
* @param {string|bitArray} data
* @return {UInt256} Hash of the data
*/
Crypt.hashSha512Half = function (data) {
return UInt256.from_hex(Crypt.hashSha512(data).substr(0, 64));
};
/**
* Sign a data string with a secret key
*
* @param {string} secret
* @param {string} data
*/
Crypt.signString = function(secret, data) {
var hmac = new sjcl.misc.hmac(sjcl.codec.hex.toBits(secret), sjcl.hash.sha512);
return sjcl.codec.hex.fromBits(hmac.mac(data));
};
/**
* Create an an accout recovery key
*
* @param {string} secret
*/
Crypt.deriveRecoveryEncryptionKeyFromSecret = function(secret) {
var seed = Seed.from_json(secret).to_bits();
var hmac = new sjcl.misc.hmac(seed, sjcl.hash.sha512);
var key = hmac.mac('ripple/hmac/recovery_encryption_key/v1');
key = sjcl.bitArray.bitSlice(key, 0, 256);
return sjcl.codec.hex.fromBits(key);
};
/**
* Convert base64 encoded data into base64url encoded data.
*
* @param {String} base64 Data
*/
Crypt.base64ToBase64Url = function(encodedData) {
return encodedData.replace(/\+/g, '-').replace(/\//g, '_').replace(/[=]+$/, '');
};
/**
* Convert base64url encoded data into base64 encoded data.
*
* @param {String} base64 Data
*/
Crypt.base64UrlToBase64 = function(encodedData) {
encodedData = encodedData.replace(/-/g, '+').replace(/_/g, '/');
while (encodedData.length % 4) {
encodedData += '=';
}
return encodedData;
};
/**
* base64 to UTF8
*/
Crypt.decodeBase64 = function (data) {
return sjcl.codec.utf8String.fromBits(sjcl.codec.base64.toBits(data));
}
exports.Crypt = Crypt;

View File

@@ -1,5 +1,5 @@
// Ledger
/* eslint-disable valid-jsdoc */
'use strict';
var Transaction = require('./transaction').Transaction;
var SHAMap = require('./shamap').SHAMap;
var SHAMapTreeNode = require('./shamap').SHAMapTreeNode;
@@ -7,16 +7,12 @@ var SerializedObject = require('./serializedobject').SerializedObject;
var stypes = require('./serializedtypes');
var UInt160 = require('./uint160').UInt160;
var Currency = require('./currency').Currency;
var stypes = require('./serializedtypes');
var sjcl = require('./utils').sjcl;
var Crypt = require('./crypt').Crypt;
function Ledger()
{
function Ledger() {
this.ledger_json = {};
}
Ledger.from_json = function (v) {
Ledger.from_json = function(v) {
var ledger = new Ledger();
ledger.parse_json(v);
return ledger;
@@ -31,7 +27,7 @@ Ledger.space = require('./ledgerspaces');
* @return {UInt256}
*/
Ledger.calcAccountRootEntryHash =
Ledger.prototype.calcAccountRootEntryHash = function (account) {
Ledger.prototype.calcAccountRootEntryHash = function(account) {
account = UInt160.from_json(account);
var index = new SerializedObject();
@@ -51,9 +47,9 @@ Ledger.prototype.calcAccountRootEntryHash = function (account) {
* @return {UInt256}
*/
Ledger.calcOfferEntryHash =
Ledger.prototype.calcOfferEntryHash = function (account, sequence) {
Ledger.prototype.calcOfferEntryHash = function(account, sequence) {
account = UInt160.from_json(account);
sequence = parseInt(sequence);
sequence = parseInt(sequence, 10);
var index = new SerializedObject();
@@ -75,19 +71,20 @@ Ledger.prototype.calcOfferEntryHash = function (account, sequence) {
* @return {UInt256}
*/
Ledger.calcRippleStateEntryHash =
Ledger.prototype.calcRippleStateEntryHash = function (account1, account2, currency) {
Ledger.prototype.calcRippleStateEntryHash = function(
account1, account2, currency) {
currency = Currency.from_json(currency);
account1 = UInt160.from_json(account1);
account2 = UInt160.from_json(account2);
if (!account1.is_valid()) {
throw new Error("Invalid first account");
throw new Error('Invalid first account');
}
if (!account2.is_valid()) {
throw new Error("Invalid second account");
throw new Error('Invalid second account');
}
if (!currency.is_valid()) {
throw new Error("Invalid currency");
throw new Error('Invalid currency');
}
// The lower ID has to come first
@@ -107,14 +104,14 @@ Ledger.prototype.calcRippleStateEntryHash = function (account1, account2, curren
return index.hash();
};
Ledger.prototype.parse_json = function (v) {
Ledger.prototype.parse_json = function(v) {
this.ledger_json = v;
};
Ledger.prototype.calc_tx_hash = function () {
Ledger.prototype.calc_tx_hash = function() {
var tx_map = new SHAMap();
this.ledger_json.transactions.forEach(function (tx_json) {
this.ledger_json.transactions.forEach(function(tx_json) {
var tx = Transaction.from_json(tx_json);
var meta = SerializedObject.from_json(tx_json.metaData);
@@ -128,37 +125,39 @@ Ledger.prototype.calc_tx_hash = function () {
};
/**
* @param options.sanity_test {Boolean}
* @param options .sanity_test {Boolean}
* @return hash of shamap
*
* If `true`, will serialize each accountState item to binary and then back to
* json before finally serializing for hashing. This is mostly to expose any
* issues with ripple-lib's binary <--> json codecs.
*
*/
Ledger.prototype.calc_account_hash = function (options) {
Ledger.prototype.calc_account_hash = function(options) {
var account_map = new SHAMap();
var erred;
this.ledger_json.accountState.forEach(function (le) {
this.ledger_json.accountState.forEach(function(le) {
var data = SerializedObject.from_json(le);
if (options != null && options.sanity_test) {
var json;
if (options && options.sanity_test) {
try {
var json = data.to_json();
json = data.to_json();
data = SerializedObject.from_json(json);
} catch (e) {
console.log("account state item: ", le);
console.log("to_json() ",json);
console.log("exception: ", e);
console.log('account state item: ', le);
console.log('to_json() ', json);
console.log('exception: ', e);
erred = true;
}
};
}
account_map.add_item(le.index, data, SHAMapTreeNode.TYPE_ACCOUNT_STATE);
});
if (erred) {
throw new Error("There were errors with sanity_test"); // all logged above
throw new Error('There were errors with sanity_test'); // all logged above
}
return account_map.hash();

View File

@@ -1,20 +1,22 @@
var async = require('async');
var crypto = require('crypto');
var sjcl = require('./utils').sjcl;
var Remote = require('./remote').Remote;
var Seed = require('./seed').Seed;
var KeyPair = require('./keypair').KeyPair;
var Account = require('./account').Account;
var UInt160 = require('./uint160').UInt160;
/* eslint-disable valid-jsdoc */
'use strict';
var async = require('async');
var sjcl = require('./utils').sjcl;
var Remote = require('./remote').Remote;
var Seed = require('./seed').Seed;
var KeyPair = require('./keypair').KeyPair;
var Account = require('./account').Account;
var UInt160 = require('./uint160').UInt160;
// Message class (static)
var Message = {};
Message.HASH_FUNCTION = sjcl.hash.sha512.hash;
Message.MAGIC_BYTES = 'Ripple Signed Message:\n';
Message.hashFunction = sjcl.hash.sha512.hash;
Message.MAGIC_BYTES = 'Ripple Signed Message:\n';
var REGEX_HEX = /^[0-9a-fA-F]+$/;
var REGEX_BASE64 = /^([A-Za-z0-9\+]{4})*([A-Za-z0-9\+]{2}==)|([A-Za-z0-9\+]{3}=)?$/;
var REGEX_BASE64 =
/^([A-Za-z0-9\+]{4})*([A-Za-z0-9\+]{2}==)|([A-Za-z0-9\+]{3}=)?$/;
/**
* Produce a Base64-encoded signature on the given message with
@@ -27,15 +29,16 @@ var REGEX_BASE64 = /^([A-Za-z0-9\+]{4})*([A-Za-z0-9\+]{2}==)|([A-Za-z0-9\+]{3}=)
* @static
*
* @param {String} message
* @param {sjcl.ecc.ecdsa.secretKey|Any format accepted by Seed.from_json} secret_key
* @param {RippleAddress} [The first key] account Field to specify the signing account.
* If this is omitted the first account produced by the secret generator will be used.
* @returns {Base64-encoded String} signature
* @param {sjcl.ecc.ecdsa.secretKey|Any format accepted by Seed.from_json}
* secret_key
* @param {RippleAddress} [The first key] account Field to specify the signing
* account. If this is omitted the first account produced by the secret
* generator will be used.
* @return {Base64-encoded String} signature
*/
Message.signMessage = function(message, secret_key, account) {
return Message.signHash(Message.HASH_FUNCTION(Message.MAGIC_BYTES + message), secret_key, account);
return Message.signHash(Message.hashFunction(Message.MAGIC_BYTES + message),
secret_key, account);
};
/**
@@ -48,9 +51,11 @@ Message.signMessage = function(message, secret_key, account) {
* @static
*
* @param {bitArray|Hex-encoded String} hash
* @param {sjcl.ecc.ecdsa.secretKey|Any format accepted by Seed.from_json} secret_key
* @param {RippleAddress} [The first key] account Field to specify the signing account.
* If this is omitted the first account produced by the secret generator will be used.
* @param {sjcl.ecc.ecdsa.secretKey|Any format accepted by Seed.from_json}
* secret_key
* @param {RippleAddress} [The first key] account Field to specify the
* signing account. If this is omitted the first account produced by
* the secret generator will be used.
* @returns {Base64-encoded String} signature
*/
Message.signHash = function(hash, secret_key, account) {
@@ -59,7 +64,8 @@ Message.signHash = function(hash, secret_key, account) {
hash = sjcl.codec.hex.toBits(hash);
}
if (typeof hash !== 'object' || hash.length <= 0 || typeof hash[0] !== 'number') {
if (typeof hash !== 'object' || hash.length <= 0
|| typeof hash[0] !== 'number') {
throw new Error('Hash must be a bitArray or hex-encoded string');
}
@@ -67,7 +73,9 @@ Message.signHash = function(hash, secret_key, account) {
secret_key = Seed.from_json(secret_key).get_key(account)._secret;
}
var signature_bits = secret_key.signWithRecoverablePublicKey(hash);
var PARANOIA_256_BITS = 6; // sjcl constant for ensuring 256 bits of entropy
var signature_bits = secret_key.signWithRecoverablePublicKey(hash,
PARANOIA_256_BITS);
var signature_base64 = sjcl.codec.base64.fromBits(signature_bits);
return signature_base64;
@@ -78,7 +86,7 @@ Message.signHash = function(hash, secret_key, account) {
/**
* Verify the signature on a given message.
*
* Note that this function is asynchronous.
* Note that this function is asynchronous.
* The ripple-lib remote is used to check that the public
* key extracted from the signature corresponds to one that is currently
* active for the given account.
@@ -98,9 +106,10 @@ Message.signHash = function(hash, secret_key, account) {
Message.verifyMessageSignature = function(data, remote, callback) {
if (typeof data.message === 'string') {
data.hash = Message.HASH_FUNCTION(Message.MAGIC_BYTES + data.message);
data.hash = Message.hashFunction(Message.MAGIC_BYTES + data.message);
} else {
return callback(new Error('Data object must contain message field to verify signature'));
return callback(new Error(
'Data object must contain message field to verify signature'));
}
return Message.verifyHashSignature(data, remote, callback);
@@ -111,7 +120,7 @@ Message.verifyMessageSignature = function(data, remote, callback) {
/**
* Verify the signature on a given hash.
*
* Note that this function is asynchronous.
* Note that this function is asynchronous.
* The ripple-lib remote is used to check that the public
* key extracted from the signature corresponds to one that is currently
* active for the given account.
@@ -134,7 +143,7 @@ Message.verifyHashSignature = function(data, remote, callback) {
account,
signature;
if(typeof callback !== 'function') {
if (typeof callback !== 'function') {
throw new Error('Must supply callback function');
}
@@ -143,7 +152,8 @@ Message.verifyHashSignature = function(data, remote, callback) {
hash = sjcl.codec.hex.toBits(hash);
}
if (typeof hash !== 'object' || hash.length <= 0 || typeof hash[0] !== 'number') {
if (typeof hash !== 'object' || hash.length <= 0
|| typeof hash[0] !== 'number') {
return callback(new Error('Hash must be a bitArray or hex-encoded string'));
}
@@ -159,14 +169,16 @@ Message.verifyHashSignature = function(data, remote, callback) {
signature = sjcl.codec.base64.toBits(signature);
if (!(remote instanceof Remote) || remote.state !== 'online') {
return callback(new Error('Must supply connected Remote to verify signature'));
return callback(new Error(
'Must supply connected Remote to verify signature'));
}
function recoverPublicKey (async_callback) {
function recoverPublicKey(async_callback) {
var public_key;
try {
public_key = sjcl.ecc.ecdsa.publicKey.recoverFromSignature(hash, signature);
public_key =
sjcl.ecc.ecdsa.publicKey.recoverFromSignature(hash, signature);
} catch (err) {
return async_callback(err);
}
@@ -177,9 +189,9 @@ Message.verifyHashSignature = function(data, remote, callback) {
async_callback(new Error('Could not recover public key from signature'));
}
};
}
function checkPublicKeyIsValid (public_key, async_callback) {
function checkPublicKeyIsValid(public_key, async_callback) {
// Get hex-encoded public key
var key_pair = new KeyPair();
@@ -189,7 +201,7 @@ Message.verifyHashSignature = function(data, remote, callback) {
var account_class_instance = new Account(remote, account);
account_class_instance.publicKeyIsActive(public_key_hex, async_callback);
};
}
var steps = [
recoverPublicKey,

View File

@@ -33,7 +33,6 @@ var PathFind = require('./pathfind').PathFind;
var SerializedObject = require('./serializedobject').SerializedObject;
var RippleError = require('./rippleerror').RippleError;
var utils = require('./utils');
var sjcl = require('./utils').sjcl;
var hashprefixes = require('./hashprefixes');
var config = require('./config');
var log = require('./log').internal.sub('remote');
@@ -1760,19 +1759,7 @@ Remote.prototype._serverPrepareSubscribe = function(server, callback) {
function serverSubscribed(message) {
self._stand_alone = !!message.stand_alone;
self._testnet = !!message.testnet;
if (typeof message.random === 'string') {
var rand = message.random.match(/[0-9A-F]{8}/ig);
while (rand && rand.length) {
sjcl.random.addEntropy(parseInt(rand.pop(), 16));
}
self.emit('random', utils.hexToArray(message.random));
}
self._handleLedgerClosed(message, server);
self.emit('subscribed');
}

View File

@@ -5,8 +5,8 @@ var assert = require('assert');
var extend = require('extend');
var binformat = require('./binformat');
var stypes = require('./serializedtypes');
var Crypt = require('./crypt').Crypt;
var utils = require('./utils');
var UInt256 = require('./uint256').UInt256;
var sjcl = utils.sjcl;
@@ -280,9 +280,9 @@ SerializedObject.prototype.hash = function(prefix) {
// Copy buffer to temporary buffer
sign_buffer.append(this.buffer);
// XXX We need a proper Buffer class then Crypt could accept that
var bits = sjcl.codec.bytes.toBits(sign_buffer.buffer);
return Crypt.hashSha512Half(bits);
var sha512hex = sjcl.codec.hex.fromBits(sjcl.hash.sha512.hash(bits));
return UInt256.from_hex(sha512hex.substr(0, 64));
};
// DEPRECATED

View File

@@ -1,14 +1,16 @@
var assert = require('assert');
var sjcl = require('ripple-lib').sjcl;
/* eslint-disable max-len */
'use strict';
var assert = require('assert');
var sjcl = require('ripple-lib').sjcl;
var Message = require('ripple-lib').Message;
var Seed = require('ripple-lib').Seed;
var Remote = require('ripple-lib').Remote;
var Seed = require('ripple-lib').Seed;
var Remote = require('ripple-lib').Remote;
describe('Message', function(){
describe('Message', function() {
describe('signMessage', function(){
describe('signMessage', function() {
it('should prepend the MAGIC_BYTES, call the HASH_FUNCTION, and then call signHash', function(){
it('should prepend the MAGIC_BYTES, call the hashFunction, and then call signHash', function() {
var normal_signHash = Message.signHash;
@@ -17,7 +19,7 @@ describe('Message', function(){
var signHash_called = false;
Message.signHash = function(hash) {
signHash_called = true;
assert.deepEqual(hash, Message.HASH_FUNCTION(Message.MAGIC_BYTES + message_text));
assert.deepEqual(hash, Message.hashFunction(Message.MAGIC_BYTES + message_text));
};
Message.signMessage(message_text);
@@ -29,13 +31,13 @@ describe('Message', function(){
});
describe('signHash', function(){
describe('signHash', function() {
it('should accept the hash as either a hex string or a bitArray', 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){
sjcl.random.randomWords = function(num_words) {
var words = [];
for (var w = 0; w < num_words; w++) {
words.push(sjcl.codec.hex.toBits('00000000'));
@@ -56,11 +58,11 @@ describe('Message', function(){
});
it('should accept the secret as a string or scjl.ecc.ecdsa.secretKey object', function(){
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){
sjcl.random.randomWords = function(num_words) {
var words = [];
for (var w = 0; w < num_words; w++) {
words.push(sjcl.codec.hex.toBits('00000000'));
@@ -81,7 +83,7 @@ describe('Message', function(){
});
it('should throw an error if given an invalid secret key', function(){
it('should throw an error if given an invalid secret key', function() {
// Annoyingly non hex can be fed to the BigInteger(s, 16) constructor and
// it will parse as a number. Before the commit of this comment, this test
// involved a fixture of 32 chars, which was assumed to be hex. The test
@@ -95,41 +97,41 @@ describe('Message', function(){
var secret_string = 'sbadsafRpB5euNL52PZPTSqrE9gvuFwTC';
var hash = 'e865bcc63a86ef21585ac8340a7cc8590ed85175a2a718c6fb2bfb2715d13778';
assert.throws(function(){
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(){
it('should throw an error if the parameters are reversed', function() {
var secret_string = 'safRpB5euNL52PZPTSqrE9gvuFwTC';
var hash = 'e865bcc63a86ef21585ac8340a7cc8590ed85175a2a718c6fb2bfb2715d13778';
assert.throws(function(){
assert.throws(function() {
Message.signHash(secret_string, hash);
}, Error);
assert.throws(function(){
assert.throws(function() {
Message.signHash(secret_string, sjcl.codec.hex.toBits(hash));
}, Error);
assert.throws(function(){
assert.throws(function() {
Message.signHash(Seed.from_json(secret_string).get_key()._secret, hash);
}, Error);
assert.throws(function(){
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(){
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){
sjcl.random.randomWords = function(num_words) {
var words = [];
for (var w = 0; w < num_words; w++) {
words.push(sjcl.codec.hex.toBits('00000000'));
@@ -150,9 +152,9 @@ describe('Message', function(){
});
describe('verifyMessageSignature', function(){
describe('verifyMessageSignature', function() {
it('should prepend the MAGIC_BYTES, call the HASH_FUNCTION, and then call verifyHashSignature', function(){
it('should prepend the MAGIC_BYTES, call the hashFunction, and then call verifyHashSignature', function() {
var normal_verifyHashSignature = Message.verifyHashSignature;
@@ -165,13 +167,13 @@ describe('Message', function(){
Message.verifyHashSignature = function(vhs_data, remote, callback) {
verifyHashSignature_called = true;
assert.deepEqual(vhs_data.hash, Message.HASH_FUNCTION(Message.MAGIC_BYTES + data.message));
assert.deepEqual(vhs_data.hash, Message.hashFunction(Message.MAGIC_BYTES + data.message));
assert.strictEqual(vhs_data.signature, data.signature);
callback();
};
Message.verifyMessageSignature(data, {}, function(err){
Message.verifyMessageSignature(data, {}, function(err) {
assert(!err);
});
assert(verifyHashSignature_called);
@@ -182,9 +184,9 @@ describe('Message', function(){
});
describe('verifyHashSignature', function(){
describe('verifyHashSignature', function() {
it('should throw an error if a callback function is not supplied', function(){
it('should throw an error if a callback function is not supplied', function() {
var data = {
message: 'Hello world!',
@@ -193,15 +195,12 @@ describe('Message', function(){
account: 'rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz'
};
//Remote.prototype.addServer = function(){};
var test_remote = new Remote();
assert.throws(function(){
assert.throws(function() {
Message.verifyHashSignature(data);
}, /(?=.*callback\ function).*/);
});
it('should respond with an error if the hash is missing or invalid', function(done){
it('should respond with an error if the hash is missing or invalid', function(done) {
var data = {
message: 'Hello world!',
@@ -209,18 +208,17 @@ describe('Message', function(){
account: 'rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz'
};
//Remote.prototype.addServer = function(){};
var test_remote = new Remote();
test_remote.state = 'online';
Message.verifyHashSignature(data, test_remote, function(err, valid){
Message.verifyHashSignature(data, test_remote, function(err) {
assert(/hash/i.test(err.message));
done();
});
});
it('should respond with an error if the account is missing or invalid', function(done){
it('should respond with an error if the account is missing or invalid', function(done) {
var data = {
message: 'Hello world!',
@@ -228,18 +226,17 @@ describe('Message', function(){
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){
Message.verifyHashSignature(data, test_remote, function(err) {
assert(/account|address/i.test(err.message));
done();
});
});
it('should respond with an error if the signature is missing or invalid', function(done){
it('should respond with an error if the signature is missing or invalid', function(done) {
var data = {
message: 'Hello world!',
@@ -247,18 +244,17 @@ describe('Message', function(){
account: 'rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz'
};
//Remote.prototype.addServer = function(){};
var test_remote = new Remote();
test_remote.state = 'online';
Message.verifyHashSignature(data, test_remote, function(err, valid){
Message.verifyHashSignature(data, test_remote, function(err) {
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){
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!',
@@ -267,18 +263,17 @@ describe('Message', function(){
signature: 'AAAAHMIPCQGLgdnpX1Ccv1wHb56H4NggxIM6U08Qkb9mUjN2Vn9pZ3CHvq1yWLBi6NqpW+7kedLnmfu4VG2+y43p4Xs='
};
//Remote.prototype.addServer = function(){};
var test_remote = new Remote();
test_remote.state = 'online';
test_remote.requestAccountInfo = function(options, callback) {
var account = options.account;
if (account === data.account) {
callback(null, {
"account_data": {
"Account": "rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz",
"Flags": 1114112,
"LedgerEntryType": "AccountRoot",
"RegularKey": "rHq2wyUtLkAad3vURUk33q9gozd97skhSf"
'account_data': {
'Account': 'rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz',
'Flags': 1114112,
'LedgerEntryType': 'AccountRoot',
'RegularKey': 'rHq2wyUtLkAad3vURUk33q9gozd97skhSf'
}
});
} else {
@@ -286,7 +281,7 @@ describe('Message', function(){
}
};
Message.verifyHashSignature(data, test_remote, function(err, valid){
Message.verifyHashSignature(data, test_remote, function(err, valid) {
assert(!err);
assert(valid);
done();
@@ -294,7 +289,7 @@ describe('Message', function(){
});
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){
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 = {
@@ -304,18 +299,17 @@ describe('Message', function(){
signature: 'AAAAG+dB/rAjZ5m8eQ/opcqQOJsFbKxOu9jq9KrOAlNO4OdcBDXyCBlkZqS9Xr8oZI2uh0boVsgYOS3pOLJz+Dh3Otk='
};
//Remote.prototype.addServer = function(){};
var test_remote = new Remote();
test_remote.state = 'online';
test_remote.requestAccountInfo = function(options, callback) {
var account = options.account;
if (account === data.account) {
callback(null, {
"account_data": {
"Account": "rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz",
"Flags": 1114112,
"LedgerEntryType": "AccountRoot",
"RegularKey": "rHq2wyUtLkAad3vURUk33q9gozd97skhSf"
'account_data': {
'Account': 'rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz',
'Flags': 1114112,
'LedgerEntryType': 'AccountRoot',
'RegularKey': 'rHq2wyUtLkAad3vURUk33q9gozd97skhSf'
}
});
} else {
@@ -323,7 +317,7 @@ describe('Message', function(){
}
};
Message.verifyHashSignature(data, test_remote, function(err, valid){
Message.verifyHashSignature(data, test_remote, function(err, valid) {
assert(!err);
assert(!valid);
done();