Compare commits

..

68 Commits

Author SHA1 Message Date
Geert Weening
b14343f3cf [TASK] shrinkwrap dependencies 2015-01-06 14:48:13 -08:00
Geert Weening
732b50dea7 [TASK] bump version to 0.10.0 2015-01-06 14:48:05 -08:00
Geert Weening
be3bbe9b61 [DOC] update release notes 2015-01-06 14:46:44 -08:00
Geert Weening
1eaad617cb [TASK] bump version to 0.10.0-rc2 2014-12-11 00:20:50 -08:00
wltsmrz
3c21994adc [FIX] Fix empty queue check on reconnect 2014-12-10 23:58:00 -08:00
wltsmrz
d15d14e197 [FIX] Fix undefined Transaction.prototype.summary() 2014-12-10 14:17:28 -08:00
Geert Weening
e32694dc79 [TASK] bump version to 0.10.0-rc1 2014-12-10 14:02:48 -08:00
wltsmrz
6ec8124287 [DOC] Update release notes 2014-12-10 14:00:10 -08:00
Geert Weening
2222adfc10 [DOC] update release notes 2014-12-10 13:29:47 -08:00
Geert Weening
fcc2377657 Merge pull request #223 from geertweening/feature/memo_format_type
[FEATURE] improve memo support
2014-12-10 13:08:04 -08:00
Geert Weening
1704ac4ae1 [FEATURE] improve memo support
- add MemoFormat property for memo
- MemoFormat and MemoType must be valid ASCII
- Memo content is converted on the serialization level
- add parsed_* version of Memo content if the parser understand the format
- support `text` and `json` MemoFormat

[FIX] double serialization overriding Memo contents

The copy made in from_json wasn't a deep copy
2014-12-10 13:07:44 -08:00
Geert Weening
666e4348e0 Merge pull request #222 from boxbag/transaction-set-fee
[FEATURE] allow per transaction fees to be set
2014-12-10 13:07:20 -08:00
Bo Chen
9b22f279bc [FEATURE] allow per transaction fees to be set 2014-12-10 11:52:16 -08:00
Geert Weening
0835de983b Merge pull request #221 from ripple/transaction-updates
Transaction updates
2014-12-10 11:37:36 -08:00
wltsmrz
1a892d58fc Rework tef and tel-class transaction error handling
Do not finalize tef or tel-class errors until LastLedgerSequence is
exceeded. Transactions that fail in this way will now be aborted
with a tej-class error. Errors like tefALREADY and tefMAX_LEDGER
should now be opaque to the user and have no consequence in
determining a final state.
2014-12-10 11:30:45 -08:00
wltsmrz
73a3cce4a4 Do not bump LastLedgerSequence on resubmit 2014-12-10 11:30:45 -08:00
wltsmrz
d5ef4774fa Make LastLedgerSequence offset configurable, default to 3 2014-12-10 11:30:45 -08:00
wltsmrz
c5bd4239a4 Cleanup
- Deprecate 'save' event
- Add TransactionQueue.getMinLedger(), use this as ledger_index_min
  in account_tx request on reconnect
- tx.sign() no longer accepts a callback
- Add various setters and jsdoc to transaction.js
- Normalize setters, e.g. sourceTag() and destinationTag()
- Minor optimization in call to tx.hash() in TransactionManager prior
  to submit; allow `serialized` argument to tx.hash() such that the
  transaction is not serialized twice
2014-12-10 11:30:45 -08:00
Geert Weening
634e811888 Merge pull request #220 from geertweening/update_sjcl
[TASK] update sjcl
2014-12-08 15:21:13 -08:00
Geert Weening
3204998fcb [TASK] update sjcl
- native support for k256
- improved entropy by taking advantage of platform crypto

- remove unnecessary sjcl overrides from sjcl-secp256k1.js
- updated ripple-lib curve instantiations to use k256
- add curve override so c256 points to k256

16dde36fa2
2014-12-08 15:02:24 -08:00
Geert Weening
12e428733a [TASK] bump version to 0.9.4 2014-12-05 15:48:24 -08:00
Geert Weening
9cc6ad09a9 [DOC] update release notes 2014-12-05 15:47:46 -08:00
Geert Weening
84abb5962e [TASK] bump ripple-lib to 0.9.4-rc2 2014-12-04 12:56:17 -08:00
Geert Weening
4bba55d2dc Revert "[FEATURE] improve memo support"
This reverts commit 89adcf4f4e.
2014-12-04 12:54:48 -08:00
Geert Weening
b4cabad44e [TASK] bump version to 0.9.4-rc1 2014-12-04 11:03:01 -08:00
Geert Weening
28cc0f9e3b [DOC] update release notes 2014-12-04 11:02:20 -08:00
wltsmrz
95a2cc18fe Merge pull request #213 from geertweening/feature/memo_format_type
[FEATURE] improve memo support
2014-12-02 00:00:10 -08:00
Geert Weening
8e315a9859 [DOC] update generate wallet example
to take advantage of randomness collected from a rippled
2014-12-01 17:54:34 -08:00
Geert Weening
89adcf4f4e [FEATURE] improve memo support
- add MemoFormat property for memo
- MemoFormat and MemoType must be valid ASCII
- Memo content is converted on the serialization level
- add parsed_* version of Memo content if the parser understand the format
- support `text` and `json` MemoFormat
2014-12-01 09:48:56 -08:00
Geert Weening
3a6c5e41c9 Merge pull request #217 from ripple/orderbook-cleanup
Cleanup, normalize offers from book_offers and transaction stream
2014-11-30 14:14:01 -08:00
wltsmrz
86ed24b94c Cleanup, normalize offers from book_offers and transaction stream 2014-11-29 15:24:15 -08:00
wltsmrz
c792c471c3 Merge pull request #215 from ripple/fix-precision-rounding
Fix to_human precision rounding
2014-11-26 18:31:29 -08:00
wltsmrz
e371cc2c3c Fix to_human precision rounding 2014-11-26 11:32:15 -08:00
Geert Weening
ccf218c8f0 Merge pull request #214 from ripple/fix-fractional-drops
Fix fractional drops in funded taker_pays setter
2014-11-26 09:16:55 -08:00
wltsmrz
0d7fc0a573 Fix fractional drops in funded taker_pays setter 2014-11-25 21:10:57 -08:00
Geert Weening
74cacd5209 [DOC] update offer example 2014-11-19 18:04:45 -08:00
Geert Weening
bb79cf2a87 [TASK] bump version to 0.9.3 2014-11-19 12:09:18 -08:00
Geert Weening
28451df1a8 Merge branch 'develop' into release 2014-11-19 11:32:19 -08:00
Geert Weening
38e288f62a [TASK] bump version to 0.9.2-rc3 2014-11-19 11:31:19 -08:00
Geert Weening
905f908450 [DOC] update release notes 2014-11-19 11:31:19 -08:00
wltsmrz
672171fd0c Merge pull request #211 from jks-liu/fix-link
Fix link in README
2014-11-19 05:24:58 -08:00
Jks Liu
520660ecbc Fix link in README 2014-11-19 16:42:25 +08:00
Geert Weening
06acb5faf2 [TASK] bump version to 0.9.2-rc3 2014-11-18 11:58:41 -08:00
Geert Weening
d43fa03f05 [DOC] update release notes 2014-11-18 11:57:49 -08:00
Geert Weening
baed1aaf92 Merge branch 'release' into develop 2014-11-18 11:54:17 -08:00
wltsmrz
cc229e803c Merge pull request #210 from geertweening/fix/max_fee
[TASK] change default `max_fee` for Remote to 1 XRP
2014-11-18 11:48:37 -08:00
Geert Weening
d6b1728c23 [TASK] change default max_fee for Remote to 1 XRP 2014-11-18 10:47:28 -08:00
wltsmrz
bc5dcc359c Merge pull request #209 from ximinez/ledger_accept
Request ledger_accept returns the Request, not Remote
2014-11-17 12:08:38 -08:00
Edward Hennis
ced07e1d6b Request ledger_accept returns the Request, not Remote 2014-11-17 15:02:45 -05:00
Geert Weening
cffffd9591 [TASK] bump version to 0.9.3-rc2 2014-11-14 10:24:44 -08:00
Geert Weening
b8766e263f [DOC] update release notes 2014-11-14 10:24:00 -08:00
Geert Weening
fc426d5764 Merge branch 'release' into develop 2014-11-14 10:16:34 -08:00
Geert Weening
056d2381cd Merge pull request #208 from ripple/relocate-presubmit
Relocate presubmit emission to immediately before transaction submit
2014-11-14 10:14:02 -08:00
Geert Weening
2932a0ec5f [DOC] add doc that accountRequest throws
if a marker is provided, but no ledger_index or ledger_hash
2014-11-14 10:11:26 -08:00
Geert Weening
d3d85a3fcf [DOC] add doc that accountRequest throws
if a marker is provided, but no ledger_index or ledger_hash
2014-11-14 10:11:13 -08:00
wltsmrz
7a1feaa897 Relocate presubmit emission to immediately before transaction submit 2014-11-13 21:44:20 -08:00
Geert Weening
5f3cf72cc6 Merge pull request #207 from shekenahglory/develop
[TASK] binformat: update fields to match rippled
2014-11-13 14:13:52 -08:00
Matthew Fettig
cae980788e binformat: update fields to match rippled 2014-11-13 11:14:46 -08:00
Geert Weening
df763b8765 Merge pull request #205 from ripple/core-build
Add core build
2014-11-12 10:51:45 -08:00
wltsmrz
365085809e Add note on restricted browser builds 2014-11-12 03:04:51 -08:00
Geert Weening
3ee7998261 [TASK] bump version to 0.9.3-rc1 2014-11-11 17:52:30 -08:00
Geert Weening
6fb9ed8312 [DOC] update release notes 2014-11-11 17:52:13 -08:00
Geert Weening
89f79c35f5 Merge pull request #206 from geertweening/fix/tec_wait_for_validated
[TASK] wait for validation before returning tec error
2014-11-11 17:49:38 -08:00
Geert Weening
6bdd4b2670 [TASK] wait for validation before returning tec error 2014-11-11 16:54:25 -08:00
Geert Weening
acd79d19e2 [TASK] bump version to 0.9.2 2014-11-11 12:14:04 -08:00
wltsmrz
8f17873da2 Remove server._computeFee(Transaction), require fee units argument 2014-11-11 06:24:34 -08:00
wltsmrz
b0cac776ee Throw an error when trying to use unavailable class in WebPack build 2014-11-11 06:23:37 -08:00
wltsmrz
625dba4d85 Add build-core gulp task 2014-11-11 05:30:50 -08:00
63 changed files with 3521 additions and 1518 deletions

View File

@@ -68,45 +68,6 @@ gulp.task('build', [ 'concat-sjcl' ], function(callback) {
}, callback);
});
gulp.task('bower-build', [ 'build' ], function(callback) {
return gulp.src([ './build/ripple-', '.js' ].join(pkg.version))
.pipe(rename('ripple.js'))
.pipe(gulp.dest('./dist/'));
});
gulp.task('bower-build-min', [ 'build-min' ], function(callback) {
return gulp.src([ './build/ripple-', '-min.js' ].join(pkg.version))
.pipe(rename('ripple-min.js'))
.pipe(gulp.dest('./dist/'));
});
gulp.task('bower-build-debug', [ 'build-debug' ], function(callback) {
return gulp.src([ './build/ripple-', '-debug.js' ].join(pkg.version))
.pipe(rename('ripple-debug.js'))
.pipe(gulp.dest('./dist/'));
});
gulp.task('bower-version', function() {
gulp.src('./dist/bower.json')
.pipe(bump({version: pkg.version}))
.pipe(gulp.dest('./dist/'));
});
gulp.task('version-bump', function() {
if (!argv.type) {
throw new Error("No type found, pass it in using the --type argument");
}
gulp.src('./package.json')
.pipe(bump({type:argv.type}))
.pipe(gulp.dest('./'));
});
gulp.task('version-beta', function() {
gulp.src('./package.json')
.pipe(bump({version: pkg.version+'-beta'}))
.pipe(gulp.dest('./'));
});
gulp.task('build-min', [ 'build' ], function(callback) {
return gulp.src([ './build/ripple-', '.js' ].join(pkg.version))
.pipe(uglify())
@@ -128,6 +89,66 @@ gulp.task('build-debug', [ 'concat-sjcl' ], function(callback) {
}, callback);
});
/**
* Generate a WebPack external for a given unavailable module which replaces
* that module's constructor with an error-thrower
*/
function buildUseError(cons) {
return 'var {<CONS>:function(){throw new Error("Class is unavailable in this build: <CONS>")}}'
.replace(new RegExp('<CONS>', 'g'), cons);
};
gulp.task('build-core', [ 'concat-sjcl' ], function(callback) {
webpack({
entry: [
'./src/js/ripple/remote.js'
],
externals: [
{
'./transaction': buildUseError('Transaction'),
'./orderbook': buildUseError('OrderBook'),
'./account': buildUseError('Account'),
'./serializedobject': buildUseError('SerializedObject')
}
],
output: {
library: 'ripple',
path: './build/',
filename: [ 'ripple-', '-core.js' ].join(pkg.version)
},
plugins: [
new webpack.optimize.UglifyJsPlugin()
]
}, callback);
});
gulp.task('bower-build', [ 'build' ], function(callback) {
return gulp.src([ './build/ripple-', '.js' ].join(pkg.version))
.pipe(rename('ripple.js'))
.pipe(gulp.dest('./dist/'));
});
gulp.task('bower-build-min', [ 'build-min' ], function(callback) {
return gulp.src([ './build/ripple-', '-min.js' ].join(pkg.version))
.pipe(rename('ripple-min.js'))
.pipe(gulp.dest('./dist/'));
});
gulp.task('bower-build-debug', [ 'build-debug' ], function(callback) {
return gulp.src([ './build/ripple-', '-debug.js' ].join(pkg.version))
.pipe(rename('ripple-debug.js'))
.pipe(gulp.dest('./dist/'));
});
gulp.task('bower-version', function() {
gulp.src('./dist/bower.json')
.pipe(bump({ version: pkg.version }))
.pipe(gulp.dest('./dist/'));
});
gulp.task('bower', ['bower-build', 'bower-build-min', 'bower-build-debug', 'bower-version']);
gulp.task('lint', function() {
gulp.src('src/js/ripple/*.js')
.pipe(jshint())
@@ -158,6 +179,20 @@ gulp.task('watch', function() {
gulp.watch('src/js/ripple/*', [ 'build-debug' ]);
});
gulp.task('default', [ 'concat-sjcl', 'build', 'build-debug', 'build-min' ]);
gulp.task('version-bump', function() {
if (!argv.type) {
throw new Error("No type found, pass it in using the --type argument");
}
gulp.task('bower', ['bower-build', 'bower-build-min', 'bower-build-debug', 'bower-version']);
gulp.src('./package.json')
.pipe(bump({ type: argv.type }))
.pipe(gulp.dest('./'));
});
gulp.task('version-beta', function() {
gulp.src('./package.json')
.pipe(bump({ version: pkg.version + '-beta' }))
.pipe(gulp.dest('./'));
});
gulp.task('default', [ 'concat-sjcl', 'build', 'build-debug', 'build-min' ]);

View File

@@ -1,3 +1,52 @@
##0.10.0
+ [Transaction changes](https://github.com/ripple/ripple-lib/pull/221)
+ **Important** `tef*` and `tel*` and errors will no longer be presented as
final. Rather than considering these errors final, ripple-lib will wait until
the `LastLedgerSequence` specified in the transaction is exceeded. This makes
failures more definitive, and ensures that no transaction will resubmit
indefinitely.
+ A new, final tej-class error is introduced to account for transactions that
are locally determined to have expired: `tejMaxLedger`.
+ [Allow per transaction fees to be set, `transaction.setFixedFee()`](https://github.com/ripple/ripple-lib/commit/9b22f279bcbe60ee6bcf4b7fa60a48e9c197a828)
+ [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
+ [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
+ [Normalize offers from book_offers and transaction stream](https://github.com/ripple/ripple-lib/commit/86ed24b94cf7c8929c87db3a63e9bbea7f767e9c)
+ [Fix: Amount.to_human() precision rounding](https://github.com/ripple/ripple-lib/commit/e371cc2c3ceccb3c1cfdf18b98d80093147dd8b2)
+ [Fix: fractional drops in funded taker_pays setter](https://github.com/ripple/ripple-lib/commit/0d7fc0a573a144caac15dd13798b23eeb1f95fb4)
##0.9.3
+ [Change `presubmit` to emit immediately before transaction submit](https://github.com/ripple/ripple-lib/commit/7a1feaa89701bf861ab31ebd8ffdc8d8d1474e29)
+ [Add a "core" browser build of ripple-lib which has a subset of features and smaller file size](https://github.com/ripple/ripple-lib/pull/205)
+ [Update binformat with missing fields from rippled](https://github.com/ripple/ripple-lib/commit/cae980788efb00191bfd0988ed836d60cdf7a9a2)
+ [Wait for transaction validation before returning `tec` error](https://github.com/ripple/ripple-lib/commit/6bdd4b2670906588852fc4dda457607b4aac08e4)
+ [Change default `max_fee` on `Remote` to `1 XRP`](https://github.com/ripple/ripple-lib/commit/d6b1728c23ff85c3cc791bed6982a750641fd95f)
+ [Fix: Request ledger_accept should return the Remote](https://github.com/ripple/ripple-lib/pull/209)
##0.9.2
+ [**Breaking change**: Change accountRequest method signature](https://github.com/ripple/ripple-lib/commit/6f5d1104aa3eb440c518ec4f39e264fdce15fa15)

View File

@@ -15,9 +15,9 @@ A JavaScript API for interacting with Ripple in Node.js and the browser
###In this file
1. [Installation](README.md#installation)
2. [Quickstart](README.md#quickstart)
3. [Running tests](https://github.com/ripple/ripple-lib#running-tests)
1. [Installation](#installation)
2. [Quick start](#quick-start)
3. [Running tests](#running-tests)
###Additional documentation
@@ -47,7 +47,9 @@ A JavaScript API for interacting with Ripple in Node.js and the browser
See the [bower-ripple repo](https://github.com/ripple/bower-ripple) for additional bower instructions
**Building ripple-lib from github**
**Building ripple-lib for browser environments**
ripple-lib uses Gulp to generate browser builds. These steps will generate minified and non-minified builds of ripple-lib in the `build/` directory.
```
$ git clone https://github.com/ripple/ripple-lib
@@ -55,9 +57,13 @@ See the [bower-ripple repo](https://github.com/ripple/bower-ripple) for addition
$ npm run build
```
Then use the minified `build/ripple-*-min.js`
**Restricted browser builds**
##Quickstart
You may generate browser builds that contain a subset of features. To do this, run `./node_modules/.bin/gulp build-<name>`
+ `build-core` Contains the functionality to make requests and listen for events such as `ledgerClose`. Only `ripple.Remote` is currently exposed. Advanced features like transaction submission and orderbook tracking are excluded from this build.
##Quick start
`Remote.js` ([remote.js](https://github.com/ripple/ripple-lib/blob/develop/src/js/ripple/remote.js)) is the point of entry for interacting with rippled

View File

@@ -16,17 +16,6 @@ This file provides step-by-step walkthroughs for some of the most common usages
1. [The ripple-lib README](../README.md)
2. [The ripple-lib API Reference](REFERENCE.md)
##Generating a new Ripple Wallet
```js
var Wallet = require('ripple-lib').Wallet;
var wallet = Wallet.generate();
console.log(wallet);
// { address: 'rEf4sbVobiiDGExrNj2PkNHGMA8eS6jWh3',
// secret: 'shFh4a38EZpEdZxrLifEnVPAoBRce' }
```
##Connecting to the Ripple network
1. [Get ripple-lib](README.md#getting-ripple-lib)
@@ -60,10 +49,37 @@ This file provides step-by-step walkthroughs for some of the most common usages
4. You're connected! Read on to see what to do now.
##Generating a new Ripple Wallet
```js
var ripple = require('ripple-lib');
// subscribing to a server allows for more entropy
var remote = new ripple.Remote({
servers: [
{ host: 's1.ripple.com', port: 443, secure: true }
]
});
remote.connect(function(err, res) {
/* remote connected */
});
// Wait for randomness to have been added.
// The entropy of the random generator is increased
// by random data received from a rippled
remote.once('random', function(err, info) {
var wallet = ripple.Wallet.generate();
console.log(wallet);
// { address: 'rEf4sbVobiiDGExrNj2PkNHGMA8eS6jWh3',
// secret: 'shFh4a38EZpEdZxrLifEnVPAoBRce' }
});
```
##Sending rippled API requests
`Remote` contains functions for constructing a `Request` object.
`Remote` contains functions for constructing a `Request` object.
A `Request` is an `EventEmitter` so you can listen for success or failure events -- or, instead, you can provide a callback.
@@ -124,29 +140,29 @@ See the [wiki](https://ripple.com/wiki/JSON_Messages#subscribe) for details on s
'ledger',
'transactions'
];
var request = remote.requestSubscribe(streams);
request.on('error', function(error) {
console.log('request error: ', error);
});
// the `ledger_closed` and `transaction` will come in on the remote
// since the request for subscribe is finalized after the success return
// the streaming events will still come in, but not on the initial request
remote.on('ledger_closed', function(ledger) {
console.log('ledger_closed: ', JSON.stringify(ledger, null, 2));
});
remote.on('transaction', function(transaction) {
console.log('transaction: ', JSON.stringify(transaction, null, 2));
});
remote.on('error', function(error) {
console.log('remote error: ', error);
});
// fire the request
request.request();
});
@@ -160,7 +176,7 @@ See the [wiki](https://ripple.com/wiki/JSON_Messages#subscribe) for details on s
Submitting a payment transaction to the Ripple network involves connecting to a `Remote`, creating a transaction, signing it with the user's secret, and submitting it to the `rippled` server. Note that the `Amount` module is used to convert human-readable amounts like '1XRP' or '10.50USD' to the type of Amount object used by the Ripple network.
```js
/* Loading ripple-lib Remote and Amount modules in Node.js */
/* Loading ripple-lib Remote and Amount modules in Node.js */
var Remote = require('ripple-lib').Remote;
var Amount = require('ripple-lib').Amount;
@@ -179,8 +195,8 @@ remote.connect(function() {
remote.setSecret(MY_ADDRESS, MY_SECRET);
var transaction = remote.createTransaction('Payment', {
account: MY_ADDRESS,
destination: RECIPIENT,
account: MY_ADDRESS,
destination: RECIPIENT,
amount: AMOUNT
});
@@ -201,12 +217,12 @@ Since the fee required for a transaction may change between the time when the or
The [`max_fee`](REFERENCE.md#1-remote-options) option can be used to avoid submitting a transaction to a server that is charging unreasonably high fees.
##4. Submitting a trade offer to the network
##Submitting a trade offer to the network
Submitting a trade offer to the network is similar to submitting a payment transaction. Here is an example for a trade that expires in 24 hours where you are offering to sell 1 USD in exchange for 100 XRP:
Submitting a trade offer to the network is similar to submitting a payment transaction. Here is an example offering to sell 1 USD in exchange for 100 XRP:
```js
/* Loading ripple-lib Remote and Amount modules in Node.js */
/* Loading ripple-lib Remote and Amount modules in Node.js */
var Remote = require('ripple-lib').Remote;
var Amount = require('ripple-lib').Amount;
@@ -225,7 +241,7 @@ remote.connect(function() {
var transaction = remote.createTransaction('OfferCreate', {
account: MY_ADDRESS,
taker_pays: '1',
taker_pays: '100',
taker_gets: '1/USD/' + GATEWAY
});

33
npm-shrinkwrap.json generated
View File

@@ -1,18 +1,21 @@
{
"name": "ripple-lib",
"version": "0.9.0-rc5",
"version": "0.10.0",
"dependencies": {
"async": {
"version": "0.8.0",
"from": "async@>=0.8.0 <0.9.0"
"from": "async@>=0.8.0 <0.9.0",
"resolved": "https://registry.npmjs.org/async/-/async-0.8.0.tgz"
},
"extend": {
"version": "1.2.1",
"from": "extend@>=1.2.1 <1.3.0"
"from": "extend@>=1.2.1 <1.3.0",
"resolved": "https://registry.npmjs.org/extend/-/extend-1.2.1.tgz"
},
"lru-cache": {
"version": "2.5.0",
"from": "lru-cache@>=2.5.0 <2.6.0"
"from": "lru-cache@>=2.5.0 <2.6.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz"
},
"ripple-wallet-generator": {
"version": "1.0.1",
@@ -22,6 +25,7 @@
"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",
@@ -56,6 +60,7 @@
"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",
@@ -75,8 +80,9 @@
"resolved": "https://registry.npmjs.org/form-data/-/form-data-0.1.3.tgz",
"dependencies": {
"combined-stream": {
"version": "0.0.5",
"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",
@@ -87,17 +93,20 @@
},
"async": {
"version": "0.9.0",
"from": "async@>=0.9.0 <0.10.0"
"from": "async@>=0.9.0 <0.10.0",
"resolved": "https://registry.npmjs.org/async/-/async-0.9.0.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"
"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",
@@ -106,11 +115,13 @@
},
"string_decoder": {
"version": "0.10.31",
"from": "string_decoder@>=0.10.0 <0.11.0"
"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"
"from": "inherits@>=2.0.1 <2.1.0",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
}
}
}
@@ -119,10 +130,12 @@
"ws": {
"version": "0.4.32",
"from": "ws@>=0.4.31 <0.5.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-0.4.32.tgz",
"dependencies": {
"commander": {
"version": "2.1.0",
"from": "commander@>=2.1.0 <2.2.0"
"from": "commander@>=2.1.0 <2.2.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.1.0.tgz"
},
"nan": {
"version": "1.0.0",

View File

@@ -1,6 +1,6 @@
{
"name": "ripple-lib",
"version": "0.9.2-rc6",
"version": "0.10.0",
"description": "A JavaScript API for interacting with Ripple in Node.js and the browser",
"files": [
"src/js/*",

View File

@@ -1145,25 +1145,21 @@ Amount.prototype.to_human = function(opts) {
fraction_part = fraction_part.replace(/0*$/, '');
if (fraction_part.length || !opts.skip_empty_fraction) {
// Enforce the maximum number of decimal digits (precision)
if (typeof opts.precision === 'number') {
if (opts.precision <= 0) {
var precision = Math.max(0, opts.precision);
precision = Math.min(precision, fraction_part.length);
var rounded = Number('0.' + fraction_part).toFixed(precision);
// increment the int_part if the first decimal is 5 or higher
if (fraction_part.charCodeAt(0) >= 53) {
int_part = (Number(int_part) + 1).toString();
}
fraction_part = '';
if (rounded < 1) {
fraction_part = rounded.substring(2);
} else {
var precision = Math.min(opts.precision, fraction_part.length);
fraction_part = Math.round(fraction_part / Math.pow(10, fraction_part.length - precision)).toString();
int_part = (Number(int_part) + 1).toString();
fraction_part = '';
}
// because the division above will cut off the leading 0's we have to add them back again
// XXX look for a more elegant alternative
while (fraction_part.length < precision) {
fraction_part = '0' + fraction_part;
}
while (fraction_part.length < precision) {
fraction_part = '0' + fraction_part;
}
}
@@ -1171,7 +1167,7 @@ Amount.prototype.to_human = function(opts) {
if (typeof opts.max_sig_digits === 'number') {
// First, we count the significant digits we have.
// A zero in the integer part does not count.
var int_is_zero = +int_part === 0;
var int_is_zero = Number(int_part) === 0;
var digits = int_is_zero ? 0 : int_part.length;
// Don't count leading zeros in the fractional part if the integer part is
@@ -1197,6 +1193,7 @@ Amount.prototype.to_human = function(opts) {
// Enforce the minimum number of decimal digits (min_precision)
if (typeof opts.min_precision === 'number') {
opts.min_precision = Math.max(0, opts.min_precision);
while (fraction_part.length < opts.min_precision) {
fraction_part += '0';
}

View File

@@ -2,6 +2,9 @@
* Data type map.
*
* Mapping of type ids to data types. The type id is specified by the high
*
* For reference, see rippled's definition:
* https://github.com/ripple/rippled/blob/develop/src/ripple/data/protocol/SField.cpp
*/
var TYPES_MAP = exports.types = [
void(0),
@@ -106,7 +109,8 @@ var FIELDS_MAP = exports.fields = {
16: 'BookDirectory',
17: 'InvoiceID',
18: 'Nickname',
19: 'Feature'
19: 'Amendment',
20: 'TicketID'
},
6: { // Amount
1: 'Amount',
@@ -135,7 +139,8 @@ var FIELDS_MAP = exports.fields = {
10: 'ExpireCode',
11: 'CreateCode',
12: 'MemoType',
13: 'MemoData'
13: 'MemoData',
14: 'MemoFormat'
},
8: { // Account
1: 'Account',
@@ -187,7 +192,7 @@ var FIELDS_MAP = exports.fields = {
19: { // Vector256
1: 'Indexes',
2: 'Hashes',
3: 'Features'
3: 'Amendments'
}
};
@@ -373,7 +378,7 @@ exports.ledger = {
['Balance', REQUIRED],
['LowLimit', REQUIRED],
['HighLimit', REQUIRED]])
}
};
exports.metadata = [
[ 'TransactionIndex' , REQUIRED ],

View File

@@ -5,7 +5,7 @@ var UInt256 = require('./uint256').UInt256;
var Base = require('./base').Base;
function KeyPair() {
this._curve = sjcl.ecc.curves.c256;
this._curve = sjcl.ecc.curves.k256;
this._secret = null;
this._pubkey = null;
};
@@ -15,7 +15,7 @@ KeyPair.from_bn_secret = function(j) {
};
KeyPair.prototype.parse_bn_secret = function(j) {
this._secret = new sjcl.ecc.ecdsa.secretKey(sjcl.ecc.curves.c256, j);
this._secret = new sjcl.ecc.ecdsa.secretKey(sjcl.ecc.curves.k256, j);
return this;
};

View File

@@ -77,10 +77,15 @@ function OrderBook(remote, getsC, getsI, paysC, paysI, key) {
listenersModified('remove', event);
});
function updateFundedAmounts(transaction) {
self.updateFundedAmounts(transaction);
};
this._remote.on('transaction', updateFundedAmounts);
this.on('unsubscribe', function() {
self.resetCache();
self._remote.removeListener('transaction', updateFundedAmounts);
self._remote.removeListener('transaction', updateTransferRate);
});
this._remote.once('prepare_subscribe', function() {
@@ -94,18 +99,6 @@ function OrderBook(remote, getsC, getsI, paysC, paysI, key) {
});
});
function updateFundedAmounts(message) {
self.updateFundedAmounts(message);
};
this._remote.on('transaction', updateFundedAmounts);
function updateTransferRate(message) {
self.updateTransferRate(message);
};
this._remote.on('transaction', updateTransferRate);
return this;
};
@@ -115,30 +108,15 @@ util.inherits(OrderBook, EventEmitter);
* Events emitted from OrderBook
*/
OrderBook.EVENTS = [ 'transaction', 'model', 'trade', 'offer' ];
OrderBook.EVENTS = [
'transaction', 'model', 'trade',
'offer_added', 'offer_removed',
'offer_changed', 'offer_funds_changed'
];
OrderBook.DEFAULT_TRANSFER_RATE = 1000000000;
/**
* Whether the OrderBook is valid.
*
* Note: This only checks whether the parameters (currencies and issuer) are
* syntactically valid. It does not check anything against the ledger.
*
* @return {Boolean} is valid
*/
OrderBook.prototype.isValid =
OrderBook.prototype.is_valid = function() {
// XXX Should check for same currency (non-native) && same issuer
return (
this._currencyPays && this._currencyPays.is_valid() &&
(this._currencyPays.is_native() || UInt160.is_valid(this._issuerPays)) &&
this._currencyGets && this._currencyGets.is_valid() &&
(this._currencyGets.is_native() || UInt160.is_valid(this._issuerGets)) &&
!(this._currencyPays.is_native() && this._currencyGets.is_native())
);
};
OrderBook.IOU_SUFFIX = '/000/rrrrrrrrrrrrrrrrrrrrrhoLvTp';
/**
* Initialize orderbook. Get orderbook offers and subscribe to transactions
@@ -167,7 +145,7 @@ OrderBook.prototype.subscribe = function() {
}
];
async.series(steps, function(err) {
async.series(steps, function(err, res) {
//XXX What now?
});
};
@@ -308,8 +286,7 @@ OrderBook.prototype.applyTransferRate = function(balance, transferRate) {
return balance;
}
var iouSuffix = '/USD/rrrrrrrrrrrrrrrrrrrrBZbvji';
var adjustedBalance = Amount.from_json(balance + iouSuffix)
var adjustedBalance = Amount.from_json(balance + OrderBook.IOU_SUFFIX)
.divide(transferRate)
.multiply(Amount.from_json(OrderBook.DEFAULT_TRANSFER_RATE))
.to_json()
@@ -321,40 +298,41 @@ OrderBook.prototype.applyTransferRate = function(balance, transferRate) {
/**
* Request transfer rate for this orderbook's issuer
*
* @param [Function] calback
* @param {Function} callback
*/
OrderBook.prototype.requestTransferRate = function(callback) {
assert.strictEqual(typeof callback, 'function');
var self = this;
var issuer = this._issuerGets;
this.once('transfer_rate', function(rate) {
if (typeof callback === 'function') {
callback(null, rate);
}
});
if (this._currencyGets.is_native()) {
// Transfer rate is default
return this.emit('transfer_rate', OrderBook.DEFAULT_TRANSFER_RATE);
// Transfer rate is default (native currency)
callback(null, OrderBook.DEFAULT_TRANSFER_RATE);
return;
}
if (this._issuerTransferRate) {
// Transfer rate has been cached
return this.emit('transfer_rate', this._issuerTransferRate);
// Transfer rate has already been cached
callback(null, this._issuerTransferRate);
return;
}
this._remote.requestAccountInfo({account: issuer}, function(err, info) {
this._remote.requestAccountInfo({ account: issuer }, function(err, info) {
if (err) {
// XXX What now?
return callback(err);
}
var transferRate = info.account_data.TransferRate
|| OrderBook.DEFAULT_TRANSFER_RATE;
var transferRate = info.account_data.TransferRate;
if (!transferRate) {
transferRate = OrderBook.DEFAULT_TRANSFER_RATE;
}
self._issuerTransferRate = transferRate;
self.emit('transfer_rate', transferRate);
callback(null, transferRate);
});
};
@@ -380,11 +358,10 @@ OrderBook.prototype.setFundedAmount = function(offer, fundedAmount) {
return offer;
}
var iouSuffix = '/' + this._currencyGets.to_json()
+ '/' + this._issuerGets;
offer.is_fully_funded = Amount.from_json(
this._currencyGets.is_native() ? fundedAmount : fundedAmount + iouSuffix
this._currencyGets.is_native()
? fundedAmount
: fundedAmount + OrderBook.IOU_SUFFIX
).compareTo(Amount.from_json(offer.TakerGets)) >= 0;
if (offer.is_fully_funded) {
@@ -395,40 +372,40 @@ OrderBook.prototype.setFundedAmount = function(offer, fundedAmount) {
offer.taker_gets_funded = fundedAmount;
var takerPaysValue = typeof offer.TakerPays === 'object'
var takerPaysValue = (typeof offer.TakerPays === 'object')
? offer.TakerPays.value
: offer.TakerPays;
var takerGetsValue = typeof offer.TakerGets === 'object'
var takerGetsValue = (typeof offer.TakerGets === 'object')
? offer.TakerGets.value
: offer.TakerGets;
var takerPays = Amount.from_json(
takerPaysValue + '/000/rrrrrrrrrrrrrrrrrrrrBZbvji'
);
var takerGets = Amount.from_json(
takerGetsValue + '/000/rrrrrrrrrrrrrrrrrrrrBZbvji'
);
var fundedPays = Amount.from_json(
fundedAmount + '/000/rrrrrrrrrrrrrrrrrrrrBZbvji'
);
var takerPays = Amount.from_json(takerPaysValue + OrderBook.IOU_SUFFIX);
var takerGets = Amount.from_json(takerGetsValue + OrderBook.IOU_SUFFIX);
var fundedPays = Amount.from_json(fundedAmount + OrderBook.IOU_SUFFIX);
var rate = takerPays.divide(takerGets);
fundedPays = fundedPays.multiply(rate);
if (fundedPays.compareTo(takerPays) < 0) {
offer.taker_pays_funded = fundedPays.to_json().value;
if (this._currencyPays.is_native()) {
fundedPays = String(parseInt(fundedPays.to_json().value, 10));
} else {
fundedPays = fundedPays.to_json().value;
}
} else {
offer.taker_pays_funded = takerPays.to_json().value;
fundedPays = takerPays.to_json().value;
}
offer.taker_pays_funded = fundedPays;
return offer;
};
/**
* DEPRECATED:
* Should only be called for old versions of rippled
*
* Determine what an account is funded to offer for orderbook's
* currency/issuer
*
@@ -447,7 +424,7 @@ OrderBook.prototype.requestFundedAmount = function(account, callback) {
}
function requestNativeBalance(callback) {
self._remote.requestAccountInfo({account: account}, function(err, info) {
self._remote.requestAccountInfo({ account: account }, function(err, info) {
if (err) {
callback(err);
} else {
@@ -457,13 +434,11 @@ OrderBook.prototype.requestFundedAmount = function(account, callback) {
};
function requestLineBalance(callback) {
var request = self._remote.requestAccountLines(
{
account: account,
ledger: 'validated',
peer: self._issuerGets
}
);
var request = self._remote.requestAccountLines({
account: account,
ledger: 'validated',
peer: self._issuerGets
});
request.request(function(err, res) {
if (err) {
@@ -602,10 +577,10 @@ OrderBook.prototype.isBalanceChange = function(node) {
* @param {Object} transaction
*/
OrderBook.prototype.updateFundedAmounts = function(message) {
OrderBook.prototype.updateFundedAmounts = function(transaction) {
var self = this;
var affectedAccounts = message.mmeta.getAffectedAccounts();
var affectedAccounts = transaction.mmeta.getAffectedAccounts();
var isOwnerAffected = affectedAccounts.some(function(account) {
return self.hasCachedFunds(account);
@@ -617,25 +592,23 @@ OrderBook.prototype.updateFundedAmounts = function(message) {
if (!this._currencyGets.is_native() && !this._issuerTransferRate) {
// Defer until transfer rate is requested
if (self._remote.trace) {
if (this._remote.trace) {
log.info('waiting for transfer rate');
}
this.once('transfer_rate', function() {
self.updateFundedAmounts(message);
this.requestTransferRate(function() {
self.updateFundedAmounts(transaction);
});
this.requestTransferRate();
return;
}
var nodes = message.mmeta.getNodes({
var affectedNodes = transaction.mmeta.getNodes({
nodeType: 'ModifiedNode',
entryType: this._currencyGets.is_native() ? 'AccountRoot' : 'RippleState'
});
for (var i=0; i<nodes.length; i++) {
var node = nodes[i];
for (var i=0, l=affectedNodes.length; i<l; i++) {
var node = affectedNodes[i];
if (!this.isBalanceChange(node)) {
continue;
@@ -643,39 +616,72 @@ OrderBook.prototype.updateFundedAmounts = function(message) {
var result = this.getBalanceChange(node);
if (result.isValid) {
if (this.hasCachedFunds(result.account)) {
this.updateOfferFunds(result.account, result.balance);
}
if (result.isValid && this.hasCachedFunds(result.account)) {
this.updateAccountFunds(result.account, result.balance);
}
}
};
/**
* Update issuer's TransferRate as it changes
* Normalize offers from book_offers and transaction stream
*
* @param {Object} transaction
* @param {Object} offer
* @return {Object} normalized
*/
OrderBook.prototype.updateTransferRate = function(message) {
var self = this;
OrderBook.offerRewrite = function(offer) {
var result = { };
var keys = Object.keys(offer);
var affectedAccounts = message.mmeta.getAffectedAccounts();
var isIssuerAffected = affectedAccounts.some(function(account) {
return account === self._issuerGets;
});
if (!isIssuerAffected) {
return;
for (var i=0, l=keys.length; i<l; i++) {
var key = keys[i];
switch (key) {
case 'PreviousTxnID':
case 'PreviousTxnLgrSeq':
case 'quality':
break;
default:
result[key] = offer[key];
}
}
// XXX Update transfer rate
//
// var nodes = message.mmeta.getNodes({
// nodeType: 'ModifiedNode',
// entryType: 'AccountRoot'
// });
result.Flags = result.Flags || 0;
result.OwnerNode = result.OwnerNode || new Array(16 + 1).join('0');
result.BookNode = result.BookNode || new Array(16 + 1).join('0');
return result;
};
/**
* Reset internal offers cache from book_offers request
*
* @param {Array} offers
* @api private
*/
OrderBook.prototype.setOffers = function(offers) {
assert(Array.isArray(offers));
var newOffers = [ ];
for (var i=0, l=offers.length; i<l; i++) {
var offer = OrderBook.offerRewrite(offers[i]);
var fundedAmount;
if (this.hasCachedFunds(offer.Account)) {
fundedAmount = this.getCachedFunds(offer.Account);
} else if (offer.hasOwnProperty('owner_funds')) {
fundedAmount = this.applyTransferRate(offer.owner_funds);
this.addCachedFunds(offer.Account, fundedAmount);
}
this.setFundedAmount(offer, fundedAmount);
this.incrementOfferCount(offer.Account);
newOffers.push(offer);
}
this._offers = newOffers;
};
/**
@@ -707,27 +713,8 @@ OrderBook.prototype.requestOffers = function(callback) {
log.info('requested offers', self._key, 'offers: ' + res.offers.length);
}
// Reset offers
self._offers = [ ];
for (var i=0, l=res.offers.length; i<l; i++) {
var offer = res.offers[i];
var fundedAmount;
if (self.hasCachedFunds(offer.Account)) {
fundedAmount = self.getCachedFunds(offer.Account);
} else if (offer.hasOwnProperty('owner_funds')) {
fundedAmount = self.applyTransferRate(offer.owner_funds);
self.addCachedFunds(offer.Account, fundedAmount);
}
self.setFundedAmount(offer, fundedAmount);
self.incrementOfferCount(offer.Account);
self._offers.push(offer);
}
self.setOffers(res.offers);
self._synchronized = true;
self.emit('model', self._offers);
callback(null, self._offers);
@@ -839,6 +826,57 @@ OrderBook.prototype.getOffersSync = function() {
return this._offers;
};
/**
* Update offers whose account's funds have changed
*
* @param {String} account address
* @param {String|Object} offer funds
*/
OrderBook.prototype.updateAccountFunds = function(account, balance) {
assert(UInt160.is_valid(account), 'Account is invalid');
assert(!isNaN(balance), 'Funded amount is invalid');
if (this._remote.trace) {
log.info('updating offer funds', this._key, account, fundedAmount);
}
var fundedAmount = this.applyTransferRate(balance);
// Update cached account funds
this.addCachedFunds(account, fundedAmount);
for (var i=0, l=this._offers.length; i<l; i++) {
var offer = this._offers[i];
if (offer.Account !== account) {
continue;
}
var previousOffer = extend({ }, offer);
var previousFundedGets = Amount.from_json(
offer.taker_gets_funded + OrderBook.IOU_SUFFIX
);
offer.owner_funds = balance;
this.setFundedAmount(offer, fundedAmount);
var hasChangedFunds = !previousFundedGets.equals(
Amount.from_json(offer.taker_gets_funded + OrderBook.IOU_SUFFIX)
);
if (!hasChangedFunds) {
continue;
}
this.emit('offer_changed', previousOffer, offer);
this.emit('offer_funds_changed', offer,
previousOffer.taker_gets_funded,
offer.taker_gets_funded
);
}
};
/**
* Insert an offer into the orderbook
*
@@ -850,8 +888,8 @@ OrderBook.prototype.insertOffer = function(node, fundedAmount) {
log.info('inserting offer', this._key, node.fields);
}
var nodeFields = node.fields;
var nodeFields = OrderBook.offerRewrite(node.fields);
nodeFields.LedgerEntryType = node.entryType;
nodeFields.index = node.ledgerIndex;
if (!isNaN(fundedAmount)) {
@@ -902,7 +940,7 @@ OrderBook.prototype.modifyOffer = function(node, isDeletedNode) {
}
}
for (var i=0; i<this._offers.length; i++) {
for (var i=0, l=this._offers.length; i<l; i++) {
var offer = this._offers[i];
if (offer.index === node.ledgerIndex) {
if (isDeletedNode) {
@@ -921,57 +959,6 @@ OrderBook.prototype.modifyOffer = function(node, isDeletedNode) {
}
};
/**
* Update funded status on offers whose account's balance has changed
*
* Update cached account funds
*
* @param {String} account address
* @param {String|Object} offer funds
*/
OrderBook.prototype.updateOfferFunds = function(account, balance) {
assert(UInt160.is_valid(account), 'Account is invalid');
assert(!isNaN(balance), 'Funded amount is invalid');
if (this._remote.trace) {
log.info('updating offer funds', this._key, account, fundedAmount);
}
var fundedAmount = this.applyTransferRate(balance);
// Update cached account funds
this.addCachedFunds(account, fundedAmount);
for (var i=0; i<this._offers.length; i++) {
var offer = this._offers[i];
if (offer.Account !== account) {
continue;
}
var suffix = '/USD/rrrrrrrrrrrrrrrrrrrrBZbvji';
var previousOffer = extend({}, offer);
var previousFundedGets = Amount.from_json(offer.taker_gets_funded + suffix);
offer.owner_funds = balance;
this.setFundedAmount(offer, fundedAmount);
var hasChangedFunds = !previousFundedGets.equals(
Amount.from_json(offer.taker_gets_funded + suffix)
);
if (hasChangedFunds) {
this.emit('offer_changed', previousOffer, offer);
this.emit(
'offer_funds_changed', offer,
previousOffer.taker_gets_funded,
offer.taker_gets_funded
);
}
}
};
/**
* Notify orderbook of a relevant transaction
*
@@ -979,7 +966,7 @@ OrderBook.prototype.updateOfferFunds = function(account, balance) {
* @api private
*/
OrderBook.prototype.notify = function(message) {
OrderBook.prototype.notify = function(transaction) {
var self = this;
// Unsubscribed from OrderBook
@@ -987,7 +974,7 @@ OrderBook.prototype.notify = function(message) {
return;
}
var affectedNodes = message.mmeta.getNodes({
var affectedNodes = transaction.mmeta.getNodes({
entryType: 'Offer',
bookKey: this._key
});
@@ -997,7 +984,7 @@ OrderBook.prototype.notify = function(message) {
}
if (this._remote.trace) {
log.info('notifying', this._key, message.transaction.hash);
log.info('notifying', this._key, transaction.transaction.hash);
}
var tradeGets = Amount.from_json(
@@ -1014,7 +1001,7 @@ OrderBook.prototype.notify = function(message) {
function handleNode(node, callback) {
var isDeletedNode = node.nodeType === 'DeletedNode';
var isOfferCancel = message.transaction.TransactionType === 'OfferCancel';
var isOfferCancel = transaction.transaction.TransactionType === 'OfferCancel';
switch (node.nodeType) {
case 'DeletedNode':
@@ -1041,7 +1028,7 @@ OrderBook.prototype.notify = function(message) {
case 'CreatedNode':
self.incrementOfferCount(node.fields.Account);
var fundedAmount = message.transaction.owner_funds;
var fundedAmount = transaction.transaction.owner_funds;
if (!isNaN(fundedAmount)) {
self.insertOffer(node, fundedAmount);
@@ -1063,7 +1050,7 @@ OrderBook.prototype.notify = function(message) {
};
async.eachSeries(affectedNodes, handleNode, function() {
self.emit('transaction', message);
self.emit('transaction', transaction);
self.emit('model', self._offers);
if (!tradeGets.is_zero()) {
self.emit('trade', tradePays, tradeGets);
@@ -1099,6 +1086,27 @@ OrderBook.prototype.to_json = function() {
return json;
};
/**
* Whether the OrderBook is valid.
*
* Note: This only checks whether the parameters (currencies and issuer) are
* syntactically valid. It does not check anything against the ledger.
*
* @return {Boolean} is valid
*/
OrderBook.prototype.isValid =
OrderBook.prototype.is_valid = function() {
// XXX Should check for same currency (non-native) && same issuer
return (
this._currencyPays && this._currencyPays.is_valid() &&
(this._currencyPays.is_native() || UInt160.is_valid(this._issuerPays)) &&
this._currencyGets && this._currencyGets.is_valid() &&
(this._currencyGets.is_native() || UInt160.is_valid(this._issuerGets)) &&
!(this._currencyPays.is_native() && this._currencyGets.is_native())
);
};
exports.OrderBook = OrderBook;
// vim:sw=2:sts=2:ts=8:et

View File

@@ -93,7 +93,7 @@ function Remote(opts, trace) {
this.canonical_signing = (typeof opts.canonical_signing === 'boolean') ? opts.canonical_signing : true;
this.fee_cushion = (typeof opts.fee_cushion === 'number') ? opts.fee_cushion : 1.2;
this.max_fee = (typeof opts.max_fee === 'number') ? opts.max_fee : Infinity;
this.max_fee = (typeof opts.max_fee === 'number') ? opts.max_fee : 1000000; // default max fee is 1 XRP, 10^6 drops
this.max_attempts = (typeof opts.max_attempts === 'number') ? opts.max_attempts : 10;
@@ -111,6 +111,7 @@ function Remote(opts, trace) {
this._should_connect = true;
this._submission_timeout = 1000 * (typeof opts.submission_timeout === 'number' ? opts.submission_timeout : 20);
this._last_ledger_offset = (typeof opts.last_ledger_offset === 'number') ? opts.last_ledger_offset : 3;
this._received_tx = LRU({ max: 100 });
this._cur_path_find = null;
@@ -1217,6 +1218,7 @@ Remote.prototype.requestTx = function(hash, callback) {
* @param {String} marker - start position in response paging
* @param [Function] callback
* @return {Request}
* @throws {Error} if a marker is provided, but no ledger_index or ledger_hash
*/
Remote.accountRequest = function(type, options, callback) {
var account, ledger, peer, limit, marker;
@@ -1658,7 +1660,7 @@ Remote.prototype.requestLedgerAccept = function(callback) {
request.callback(callback, 'ledger_closed');
request.request();
return this;
return request;
};
/**

View File

@@ -16,7 +16,7 @@ var KeyPair = require('./keypair').KeyPair;
var Seed = extend(function () {
// Internal form: NaN or BigInteger
this._curve = sjcl.ecc.curves.c256;
this._curve = sjcl.ecc.curves.k256;
this._value = NaN;
}, UInt);

View File

@@ -42,7 +42,7 @@ function SerializedObject(buf) {
SerializedObject.from_json = function(obj) {
// Create a copy of the object so we don't modify it
var obj = extend({}, obj);
var obj = extend(true, {}, obj);
var so = new SerializedObject();
var typedef;

View File

@@ -24,6 +24,7 @@ var Currency = amount.Currency;
// Shortcuts
var hex = sjcl.codec.hex;
var bytes = sjcl.codec.bytes;
var utf8 = sjcl.codec.utf8String;
var BigInteger = utils.jsbn.BigInteger;
@@ -52,7 +53,7 @@ function isBigInteger(val) {
return val instanceof BigInteger;
};
function serialize_hex(so, hexData, noLength) {
function serializeHex(so, hexData, noLength) {
var byteData = bytes.fromBits(hex.toBits(hexData));
if (!noLength) {
SerializedType.serialize_varint(so, byteData.length);
@@ -63,10 +64,18 @@ function serialize_hex(so, hexData, noLength) {
/**
* parses bytes as hex
*/
function convert_bytes_to_hex (byte_array) {
function convertByteArrayToHex (byte_array) {
return sjcl.codec.hex.fromBits(sjcl.codec.bytes.toBits(byte_array)).toUpperCase();
};
function convertStringToHex(string) {
return hex.fromBits(utf8.toBits(string)).toUpperCase();
}
function convertHexToString(hexString) {
return utf8.fromBits(hex.toBits(hexString));
}
SerializedType.serialize_varint = function (so, val) {
if (val < 0) {
throw new Error('Variable integers are unsigned.');
@@ -115,7 +124,7 @@ SerializedType.prototype.parse_varint = function (so) {
*
* The result is appended to the serialized object ('so').
*/
function append_byte_array(so, val, bytes) {
function convertIntegerToByteArray(val, bytes) {
if (!isNumber(val)) {
throw new Error('Value is not a number', bytes);
}
@@ -130,7 +139,7 @@ function append_byte_array(so, val, bytes) {
newBytes.unshift(val >>> (i * 8) & 0xff);
}
so.append(newBytes);
return newBytes;
};
// Convert a certain number of bytes from the serialized object ('so') into an integer.
@@ -152,7 +161,7 @@ function readAndSum(so, bytes) {
var STInt8 = exports.Int8 = new SerializedType({
serialize: function (so, val) {
append_byte_array(so, val, 1);
so.append(convertIntegerToByteArray(val, 1));
},
parse: function (so) {
return readAndSum(so, 1);
@@ -163,7 +172,7 @@ STInt8.id = 16;
var STInt16 = exports.Int16 = new SerializedType({
serialize: function (so, val) {
append_byte_array(so, val, 2);
so.append(convertIntegerToByteArray(val, 2));
},
parse: function (so) {
return readAndSum(so, 2);
@@ -174,7 +183,7 @@ STInt16.id = 1;
var STInt32 = exports.Int32 = new SerializedType({
serialize: function (so, val) {
append_byte_array(so, val, 4);
so.append(convertIntegerToByteArray(val, 4));
},
parse: function (so) {
return readAndSum(so, 4);
@@ -217,7 +226,7 @@ var STInt64 = exports.Int64 = new SerializedType({
hex = '0' + hex;
}
serialize_hex(so, hex, true); //noLength = true
serializeHex(so, hex, true); //noLength = true
},
parse: function (so) {
var bytes = so.read(8);
@@ -237,7 +246,7 @@ var STHash128 = exports.Hash128 = new SerializedType({
if (!hash.is_valid()) {
throw new Error('Invalid Hash128');
}
serialize_hex(so, hash.to_hex(), true); //noLength = true
serializeHex(so, hash.to_hex(), true); //noLength = true
},
parse: function (so) {
return UInt128.from_bytes(so.read(16));
@@ -252,7 +261,7 @@ var STHash256 = exports.Hash256 = new SerializedType({
if (!hash.is_valid()) {
throw new Error('Invalid Hash256');
}
serialize_hex(so, hash.to_hex(), true); //noLength = true
serializeHex(so, hash.to_hex(), true); //noLength = true
},
parse: function (so) {
return UInt256.from_bytes(so.read(32));
@@ -267,7 +276,7 @@ var STHash160 = exports.Hash160 = new SerializedType({
if (!hash.is_valid()) {
throw new Error('Invalid Hash160');
}
serialize_hex(so, hash.to_hex(), true); //noLength = true
serializeHex(so, hash.to_hex(), true); //noLength = true
},
parse: function (so) {
return UInt160.from_bytes(so.read(20));
@@ -294,7 +303,7 @@ var STCurrency = new SerializedType({
// UInt160 value and consider it valid. But it doesn't, so for the
// deserialization to be usable, we need to allow invalid results for now.
//if (!currency.is_valid()) {
// throw new Error('Invalid currency: '+convert_bytes_to_hex(bytes));
// throw new Error('Invalid currency: '+convertByteArrayToHex(bytes));
//}
return currency;
}
@@ -409,15 +418,16 @@ STAmount.id = 6;
var STVL = exports.VariableLength = exports.VL = new SerializedType({
serialize: function (so, val) {
if (typeof val === 'string') {
serialize_hex(so, val);
serializeHex(so, val);
} else {
throw new Error('Unknown datatype.');
}
},
parse: function (so) {
var len = this.parse_varint(so);
return convert_bytes_to_hex(so.read(len));
return convertByteArrayToHex(so.read(len));
}
});
@@ -429,7 +439,7 @@ var STAccount = exports.Account = new SerializedType({
if (!account.is_valid()) {
throw new Error('Invalid account!');
}
serialize_hex(so, account.to_hex());
serializeHex(so, account.to_hex());
},
parse: function (so) {
var len = this.parse_varint(so);
@@ -441,7 +451,6 @@ var STAccount = exports.Account = new SerializedType({
var result = UInt160.from_bytes(so.read(len));
result.set_version(Base.VER_ACCOUNT_ID);
//console.log('PARSED 160:', result.to_json());
if (false && !result.is_valid()) {
throw new Error('Invalid Account');
}
@@ -593,6 +602,105 @@ var STVector256 = exports.Vector256 = new SerializedType({
STVector256.id = 19;
// Internal
var STMemo = exports.STMemo = new SerializedType({
serialize: function(so, val, no_marker) {
var keys = [];
Object.keys(val).forEach(function (key) {
// Ignore lowercase field names - they're non-serializable fields by
// convention.
if (key[0] === key[0].toLowerCase()) {
return;
}
if (typeof binformat.fieldsInverseMap[key] === 'undefined') {
throw new Error('JSON contains unknown field: "' + key + '"');
}
keys.push(key);
});
// Sort fields
keys = sort_fields(keys);
// store that we're dealing with json
var isJson = val.MemoFormat === 'json';
for (var i=0; i<keys.length; i++) {
var key = keys[i];
var value = val[key];
switch (key) {
// MemoType and MemoFormat are always ASCII strings
case 'MemoType':
case 'MemoFormat':
value = convertStringToHex(value);
break;
// MemoData can be a JSON object, otherwise it's a string
case 'MemoData':
if (typeof value !== 'string') {
if (isJson) {
try {
value = convertStringToHex(JSON.stringify(value));
} catch (e) {
throw new Error('MemoFormat json with invalid JSON in MemoData field');
}
} else {
throw new Error('MemoData can only be a JSON object with a valid json MemoFormat');
}
} else if (isString(value)) {
value = convertStringToHex(value);
}
break;
}
serialize(so, key, value);
}
if (!no_marker) {
//Object ending marker
STInt8.serialize(so, 0xe1);
}
},
parse: function(so) {
var output = {};
while (so.peek(1)[0] !== 0xe1) {
var keyval = parse(so);
output[keyval[0]] = keyval[1];
}
if (output['MemoType'] !== void(0)) {
output['parsed_memo_type'] = convertHexToString(output['MemoType']);
}
if (output['MemoFormat'] !== void(0)) {
output['parsed_memo_format'] = convertHexToString(output['MemoFormat']);
}
if (output['MemoData'] !== void(0)) {
// see if we can parse JSON
if (output['parsed_memo_format'] === 'json') {
try {
output['parsed_memo_data'] = JSON.parse(convertHexToString(output['MemoData']));
} catch(e) {
// fail, which is fine, we just won't add the memo_data field
}
} else if(output['parsed_memo_format'] === 'text') {
output['parsed_memo_data'] = convertHexToString(output['MemoData']);
}
}
so.read(1);
return output;
}
});
exports.serialize = exports.serialize_whatever = serialize;
function serialize(so, field_name, value) {
@@ -622,9 +730,15 @@ function serialize(so, field_name, value) {
STInt8.serialize(so, field_bits);
}
// Get the serializer class (ST...) for a field based on the type bits.
var serialized_object_type = exports[binformat.types[type_bits]];
//do something with val[keys] and val[keys[i]];
// Get the serializer class (ST...)
var serialized_object_type;
if (field_name === 'Memo' && typeof value === 'object') {
// for Memo we override the default behavior with our STMemo serializer
serialized_object_type = exports.STMemo;
} else {
// for a field based on the type bits.
serialized_object_type = exports[binformat.types[type_bits]];
}
try {
serialized_object_type.serialize(so, value);
@@ -645,18 +759,21 @@ function parse(so) {
type_bits = so.read(1)[0];
}
// Get the parser class (ST...) for a field based on the type bits.
var type = exports[binformat.types[type_bits]];
assert(type, 'Unknown type - header byte is 0x' + tag_byte.toString(16));
var field_bits = tag_byte & 0x0f;
var field_name = (field_bits === 0)
? field_name = binformat.fields[type_bits][so.read(1)[0]]
: field_name = binformat.fields[type_bits][field_bits];
? field_name = binformat.fields[type_bits][so.read(1)[0]]
: field_name = binformat.fields[type_bits][field_bits];
assert(field_name, 'Unknown field - header byte is 0x' + tag_byte.toString(16));
// Get the parser class (ST...) for a field based on the type bits.
var type = (field_name === 'Memo')
? exports.STMemo
: exports[binformat.types[type_bits]];
assert(type, 'Unknown type - header byte is 0x' + tag_byte.toString(16));
return [ field_name, type.parse(so) ]; //key, value
};
@@ -678,18 +795,20 @@ function sort_fields(keys) {
var STObject = exports.Object = new SerializedType({
serialize: function (so, val, no_marker) {
var keys = Object.keys(val);
var keys = [];
// Ignore lowercase field names - they're non-serializable fields by
// convention.
keys = keys.filter(function (key) {
return key[0] !== key[0].toLowerCase();
});
Object.keys(val).forEach(function (key) {
// Ignore lowercase field names - they're non-serializable fields by
// convention.
if (key[0] === key[0].toLowerCase()) {
return;
}
keys.forEach(function (key) {
if (typeof binformat.fieldsInverseMap[key] === 'undefined') {
throw new Error('JSON contains unknown field: "' + key + '"');
}
keys.push(key);
});
// Sort fields

View File

@@ -2,7 +2,6 @@ var util = require('util');
var url = require('url');
var EventEmitter = require('events').EventEmitter;
var Amount = require('./amount').Amount;
var Transaction = require('./transaction').Transaction;
var log = require('./log').internal.sub('server');
/**
@@ -760,22 +759,16 @@ Server.prototype._isConnected = function() {
* Calculate transaction fee
*
* @param {Transaction|Number} Fee units for a provided transaction
* @return {Number} Final fee in XRP for specified number of fee units
* @return {String} Final fee in XRP for specified number of fee units
* @api private
*/
Server.prototype._computeFee = function(transaction) {
var units;
if (transaction instanceof Transaction) {
units = transaction._getFeeUnits();
} else if (typeof transaction === 'number') {
units = transaction;
} else {
Server.prototype._computeFee = function(feeUnits) {
if (isNaN(feeUnits)) {
throw new Error('Invalid argument');
}
return this._feeTx(units).to_json();
return this._feeTx(Number(feeUnits)).to_json();
};
/**

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,19 +1,20 @@
var LRU = require('lru-cache');
var Transaction = require('./transaction').Transaction;
/**
* Manager for pending transactions
*/
var LRU = require('lru-cache');
var Transaction = require('./transaction').Transaction;
function TransactionQueue() {
this._queue = [ ];
this._idCache = LRU();
this._sequenceCache = LRU();
this._idCache = LRU({ max: 200 });
this._sequenceCache = LRU({ max: 200 });
};
/**
* Store received (validated) sequence
*
* @param {Number} sequence
*/
TransactionQueue.prototype.addReceivedSequence = function(sequence) {
@@ -23,6 +24,9 @@ TransactionQueue.prototype.addReceivedSequence = function(sequence) {
/**
* Check that sequence number has been consumed by a validated
* transaction
*
* @param {Number} sequence
* @return {Boolean}
*/
TransactionQueue.prototype.hasSequence = function(sequence) {
@@ -31,6 +35,9 @@ TransactionQueue.prototype.hasSequence = function(sequence) {
/**
* Store received (validated) ID transaction
*
* @param {String} transaction id
* @param {Transaction} transaction
*/
TransactionQueue.prototype.addReceivedId = function(id, transaction) {
@@ -39,6 +46,9 @@ TransactionQueue.prototype.addReceivedId = function(id, transaction) {
/**
* Get received (validated) transaction by ID
*
* @param {String} transaction id
* @return {Object}
*/
TransactionQueue.prototype.getReceived = function(id) {
@@ -48,6 +58,9 @@ TransactionQueue.prototype.getReceived = function(id) {
/**
* Get a submitted transaction by ID. Transactions
* may have multiple associated IDs.
*
* @param {String} transaction id
* @return {Transaction}
*/
TransactionQueue.prototype.getSubmission = function(id) {
@@ -63,8 +76,32 @@ TransactionQueue.prototype.getSubmission = function(id) {
return result;
};
/**
* Get earliest ledger in the pending queue
*
* @return {Number} ledger
*/
TransactionQueue.prototype.getMinLedger = function() {
var result = Infinity;
for (var i=0, tx; (tx=this._queue[i]); i++) {
if (tx.initialSubmitIndex < result) {
result = tx.initialSubmitIndex;
}
}
if (!isFinite(result)) {
result = -1;
}
return result;
};
/**
* Remove a transaction from the queue
*
* @param {String|Transaction} transaction or id
*/
TransactionQueue.prototype.remove = function(tx) {
@@ -87,14 +124,30 @@ TransactionQueue.prototype.remove = function(tx) {
}
};
/**
* Add a transaction to pending queue
*
* @param {Transaction} transaction
*/
TransactionQueue.prototype.push = function(tx) {
this._queue.push(tx);
};
/**
* Iterate over pending transactions
*
* @param {Function} iterator
*/
TransactionQueue.prototype.forEach = function(fn) {
this._queue.forEach(fn);
};
/**
* @return {Number} length of pending queue
*/
TransactionQueue.prototype.length =
TransactionQueue.prototype.getLength = function() {
return this._queue.length;

View File

@@ -65,7 +65,7 @@ sjcl.ecc.ecdsa.secretKey.prototype.signWithRecoverablePublicKey = function(hash,
*
* @param {bitArray} hash
* @param {bitArray} signature
* @param {sjcl.ecc.curve} [sjcl.ecc.curves['c256']] curve
* @param {sjcl.ecc.curve} [sjcl.ecc.curves['k256']] curve
* @returns {sjcl.ecc.ecdsa.publicKey} Public key
*/
sjcl.ecc.ecdsa.publicKey.recoverFromSignature = function(hash, signature, curve) {
@@ -75,7 +75,7 @@ sjcl.ecc.ecdsa.publicKey.recoverFromSignature = function(hash, signature, curve)
}
if (!curve) {
curve = sjcl.ecc.curves['c256'];
curve = sjcl.ecc.curves['k256'];
}
// Convert hash to bits and determine encoding for output

View File

@@ -1,17 +1,16 @@
// ----- for secp256k1 ------
// Overwrite NIST-P256 with secp256k1
sjcl.ecc.curves.c256 = new sjcl.ecc.curve(
sjcl.bn.pseudoMersennePrime(256, [[0,-1],[4,-1],[6,-1],[7,-1],[8,-1],[9,-1],[32,-1]]),
"0x14551231950b75fc4402da1722fc9baee",
0,
7,
"0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
"0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8"
);
sjcl.ecc.point.prototype.toBytesCompressed = function () {
var header = this.y.mod(2).toString() == "0x0" ? 0x02 : 0x03;
return [header].concat(sjcl.codec.bytes.fromBits(this.x.toBits()))
};
// Replace point addition and doubling algorithms
// NIST-P256 is a=-3, we need algorithms for a=0
//
// This is a custom point addition formula that
// only works for a=-3 Jacobian curve. It's much
// faster than the generic implementation
sjcl.ecc.pointJac.prototype.add = function(T) {
var S = this;
if (S.curve !== T.curve) {
@@ -43,7 +42,7 @@ sjcl.ecc.pointJac.prototype.add = function(T) {
var j = h.mul(i);
var r = s2.sub(S.y).doubleM();
var v = S.x.mul(i);
var x = r.square().subM(j).subM(v.copy().doubleM());
var y = r.mul(v.sub(x)).subM(S.y.mul(j).doubleM());
var z = S.z.add(h).square().subM(z1z1).subM(hh);
@@ -51,6 +50,9 @@ sjcl.ecc.pointJac.prototype.add = function(T) {
return new sjcl.ecc.pointJac(this.curve,x,y,z);
};
// This is a custom doubling algorithm that
// only works for a=-3 Jacobian curve. It's much
// faster than the generic implementation
sjcl.ecc.pointJac.prototype.doubl = function () {
if (this.isIdentity) { return this; }
@@ -66,7 +68,9 @@ sjcl.ecc.pointJac.prototype.doubl = function () {
return new sjcl.ecc.pointJac(this.curve, x, y, z);
};
sjcl.ecc.point.prototype.toBytesCompressed = function () {
var header = this.y.mod(2).toString() == "0x0" ? 0x02 : 0x03;
return [header].concat(sjcl.codec.bytes.fromBits(this.x.toBits()))
};
// DEPRECATED:
// previously the c256 curve was overridden with the secp256k1 curve
// since then, sjcl has been updated to support k256
// this override exist to keep supporting the old c256 with k256 behavior
// this will be removed in future release
sjcl.ecc.curves.c256 = sjcl.ecc.curves.k256;

2
src/js/sjcl/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
core.js
core_closure.js

9
src/js/sjcl/.travis.yml Normal file
View File

@@ -0,0 +1,9 @@
before_script: ./configure --with-all
language: node_js
node_js:
- "0.11"
- "0.10"
- "0.8"
# 0.6 appears to be unreliable on Travis at the moment.
# - "0.6"

View File

@@ -54,10 +54,11 @@ lint: core.js core/*.js test/*.js browserTest/*.js lint/coding_guidelines.pl
lint/coding_guidelines.pl core/*.js test/*.js browserTest/*.js
TEST_COMMON= browserTest/rhinoUtil.js test/test.js
TEST_COMMON= browserTest/nodeUtil.js test/test.js
TEST_SCRIPTS= $(TEST_COMMON) \
test/aes_vectors.js test/aes_test.js \
test/bitArray_vectors.js test/bitArray_test.js \
test/ocb2_vectors.js test/ocb2_test.js \
test/ccm_vectors.js test/ccm_test.js \
test/cbc_vectors.js test/cbc_test.js \
@@ -70,18 +71,14 @@ TEST_SCRIPTS= $(TEST_COMMON) \
test/hmac_vectors.js test/hmac_test.js \
test/pbkdf2_test.js \
test/bn_vectors.js test/bn_test.js \
test/ecdsa_test.js test/ecdsa_vectors.js test/ecdh_test.js
test/ecdsa_test.js test/ecdsa_vectors.js test/ecdh_test.js \
test/srp_vectors.js test/srp_test.js \
test/json_test.js
TEST_SCRIPTS_OPT= $(TEST_COMMON) \
test/srp_vectors.js test/srp_test.js
# Run all tests in node.js.
# Rhino fails at -O 0. Probably because the big files full of test vectors blow the
# bytecode limit. So, run most tests with -O -1. But modular exponentiation is
# currently very slow (on Rhino), so run the SRP test with optimizations on.
test: sjcl.js $(TEST_SCRIPTS) test/run_tests_rhino.js
@rhino -O -1 -w test/run_tests_rhino.js $< $(TEST_SCRIPTS)
@rhino -O 9 -w test/run_tests_rhino.js $< $(TEST_SCRIPTS_OPT)
test: sjcl.js $(TEST_SCRIPTS) test/run_tests_node.js
node test/run_tests_node.js $< $(TEST_SCRIPTS)
tidy:
find . -name '*~' -delete

21
src/js/sjcl/README.md Normal file
View File

@@ -0,0 +1,21 @@
sjcl
====
[![Build Status](https://travis-ci.org/bitwiseshiftleft/sjcl.png)](https://travis-ci.org/bitwiseshiftleft/sjcl)
Stanford Javascript Crypto Library
Security Advisories
===
* 12.02.2014: the current development version has a paranoia bug in the ecc module. The bug was introduced in commit [ac0b3fe0](https://github.com/bitwiseshiftleft/sjcl/commit/ac0b3fe0) and might affect ecc key generation on platforms without a platform random number generator.
*
Security Contact
====
Security Mail: sjcl@ovt.me
OpenPGP-Key Fingerprint: 0D54 3E52 87B4 EC06 3FA9 0115 72ED A6C7 7AAF 48ED
Keyserver: pool.sks-keyservers.net
Documentation
====
The documentation is available [here](http://bitwiseshiftleft.github.io/sjcl/doc/)

12
src/js/sjcl/bower.json Normal file
View File

@@ -0,0 +1,12 @@
{
"name": "sjcl",
"version": "1.0.0",
"main": ["./sjcl.js"],
"ignore": [
"**/*",
"!README.md",
"!README/*",
"!bower.json",
"!sjcl.js"
]
}

View File

@@ -1,6 +1,6 @@
browserUtil = {};
browserUtil.isRhino = (typeof(window) === 'undefined');
browserUtil.isNodeJS = (typeof(window) === 'undefined');
/**
* Pause (for the graphics to update and the script timer to clear), then run the

View File

@@ -0,0 +1,88 @@
<html>
<head>
<title>Entropy Generator Progress</title>
<!-- ProgressBar source: http://stackoverflow.com/questions/7190898/progress-bar-with-html-and-css -->
<style>
#progressbar {
background-color: black;
border-radius: 13px; /* (height of inner div) / 2 + padding */
padding: 3px;
}
#progressbar > div {
background-color: orange;
width: 0%; /* Adjust with JavaScript */
height: 20px;
border-radius: 10px;
}
</style>
<script type="text/javascript" src="../sjcl.js">
</script>
<script type="text/javascript">
var busy = 0;
var collecting = 0;
function showprogress () {
var barwidth = document.getElementById ("progresswidth");
var paranoia = parseInt (document.getElementById ("paranoialevel").value);
var progress = 100 * sjcl.random.getProgress (paranoia);
barwidth.style.width = progress+"%";
if (!sjcl.random.isReady (paranoia)) {
setTimeout ("showprogress()", 10, "JavaScript");
} else {
busy = 0;
document.getElementById ("startbutton").style.disabled = 1;
}
}
function startup () {
if (collecting == 0) {
sjcl.random.startCollectors ();
collecting = 1;
}
if (busy == 0) {
busy = 1;
document.getElementById ("startbutton").style.disabled = 1;
showprogress ();
}
}
function consume (numbits) {
var collector = document.getElementById ("collector");
collector.value = "retrieving random data";
var paranoia = document.getElementById ("paranoialevel").value;
var numwords = Math.ceil (numbits / 32);
var bits = sjcl.random.randomWords (numwords, paranoia);
collector.value = '';
for (var i=0; i<numwords; i++) {
var hi = (bits [i] >> 16) & 0x0000ffff;
var lo = bits [i] & 0x0000ffff;
collector.value = collector.value + hi.toString (16) + lo.toString (16);
}
startup ();
}
</script>
</head>
<body>
<h1>Entropy Generator Progress</h1>
<p>Target: 192 bits, available at paranoia level 5.</p>
<p>Corresponding paranoia level from [0,1..10]: <input type="text" value="5" id="paranoialevel"/> <input type=button onclick="startup ()" id="startbutton" value=" Start &gt;&gt; "> (the idea being that you can see the progress bar advance gently from empty/black to full/yellow after you press this)</p>
<p><input type=button onclick="consume (192)" value=" Consume 192 bits &gt;&gt; "><input type=text id=collector size=50 value="" onkeypress="consume (192)"> (also consumes 192 bits with every keypress in the text field; use key repeat to consume swiftly)</p>
<div id="progressbar">
<div id="progresswidth"></div>
</div>
<p>Please move your mouse, play around and generally introduce entropy into your environment.</p>
</body>
</html>

View File

@@ -0,0 +1,44 @@
browserUtil = {
isNodeJS: true,
pauseAndThen: function (cb) { cb(); },
cpsIterate: function (f, start, end, pause, callback) {
function go() {
var called = false;
if (start >= end) {
callback && callback();
} else {
f(start, function () {
if (!called) { called = true; start++; go(); }
});
}
}
go (start);
},
cpsMap: function (map, list, pause, callback) {
browserUtil.cpsIterate(function (i, cb) { map(list[i], i, list.length, cb); },
0, list.length, pause, callback);
},
loadScripts: function(scriptNames, callback) {
for (i=0; i<scriptNames.length; i++) {
load(scriptNames[i]);
callback && callback();
}
},
write: function(type, message) {
console.log(message);
return { update: function (type2, message2) {
if (type2 === 'pass') { console.log(" + " + message2); }
else if (type2 === 'unimplemented') { console.log(" ? " + message2); }
else { console.log(" - " + message2); }
}};
},
writeNewline: function () { console.log(""); },
status: function(message) {}
};

View File

@@ -16,7 +16,7 @@ sub digitize {
}
while (<>) {
s/([^a-zA-Z0-9_])(\d+)/$1 . digitize $2/eg;
s/([^a-zA-Z0-9_"])(\d+)/$1 . digitize $2/eg;
print;
}

11
src/js/sjcl/configure vendored
View File

@@ -4,12 +4,13 @@ use strict;
my ($arg, $i, $j, $targ);
my @targets = qw/sjcl aes bitArray codecString codecHex codecBase64 codecBytes sha256 sha512 sha1 ccm cbc ocb2 gcm hmac pbkdf2 random convenience bn ecc srp/;
my @targets = qw/sjcl aes bitArray codecString codecHex codecBase32 codecBase64 codecBytes sha256 sha512 sha1 ccm cbc ocb2 gcm hmac pbkdf2 random convenience bn ecc srp/;
my %deps = ('aes'=>'sjcl',
'bitArray'=>'sjcl',
'codecString'=>'bitArray',
'codecHex'=>'bitArray',
'codecBase64'=>'bitArray',
'codecBase32'=>'bitArray',
'codecBytes'=>'bitArray',
'sha256'=>'codecString',
'sha512'=>'codecString',
@@ -32,10 +33,10 @@ my %enabled = ();
$enabled{$_} = 0 foreach (@targets);
# by default, all but codecBytes, srp, bn
$enabled{$_} = 1 foreach (qw/aes bitArray codecString codecHex codecBase64 sha256 ccm ocb2 gcm hmac pbkdf2 random convenience/);
$enabled{$_} = 1 foreach (qw/aes bitArray codecString codecHex codecBase32 codecBase64 sha256 ccm ocb2 gcm hmac pbkdf2 random convenience/);
# argument parsing
while ($arg = shift @ARGV) {
while (my $arg = shift @ARGV) {
if ($arg =~ /^--?with-all$/) {
foreach (@targets) {
if ($enabled{$_} == 0) {
@@ -97,7 +98,7 @@ my $config = '';
my $pconfig;
# dependency analysis: forbidden
foreach $i (@targets) {
foreach my $i (@targets) {
if ($enabled{$i} > 0) {
foreach $j (split /,/, $deps{$i}) {
if ($enabled{$j} == -1) {
@@ -114,7 +115,7 @@ foreach $i (@targets) {
}
# reverse
foreach $i (reverse @targets) {
foreach my $i (reverse @targets) {
if ($enabled{$i} > 0) {
foreach $j (split /,/, $deps{$i}) {
if ($enabled{$j} < $enabled{$i}) {

View File

@@ -74,7 +74,7 @@ sjcl.bitArray = {
return a1.concat(a2);
}
var out, i, last = a1[a1.length-1], shift = sjcl.bitArray.getPartial(last);
var last = a1[a1.length-1], shift = sjcl.bitArray.getPartial(last);
if (shift === 32) {
return a1.concat(a2);
} else {
@@ -183,5 +183,19 @@ sjcl.bitArray = {
*/
_xor4: function(x,y) {
return [x[0]^y[0],x[1]^y[1],x[2]^y[2],x[3]^y[3]];
},
/** byteswap a word array inplace.
* (does not handle partial words)
* @param {sjcl.bitArray} a word array
* @return {sjcl.bitArray} byteswapped array
*/
byteswapM: function(a) {
var i, v, m = 0xff00;
for (i = 0; i < a.length; ++i) {
v = a[i];
a[i] = (v >>> 24) | ((v >>> 8) & m) | ((v & m) << 8) | (v << 24);
}
return a;
}
};

View File

@@ -19,7 +19,7 @@ sjcl.bn.prototype = {
* Initializes this with it, either as a bn, a number, or a hex string.
*/
initWith: function(it) {
var i=0, k, n, l;
var i=0, k;
switch(typeof it) {
case "object":
this.limbs = it.limbs.slice(0);
@@ -328,7 +328,7 @@ sjcl.bn.prototype = {
carry = (l-m)*ipv;
}
if (carry === -1) {
limbs[i-1] -= this.placeVal;
limbs[i-1] -= pv;
}
return this;
},
@@ -370,7 +370,9 @@ sjcl.bn.prototype = {
}
};
/** @this { sjcl.bn } */
/** @memberOf sjcl.bn
* @this { sjcl.bn }
*/
sjcl.bn.fromBits = function(bits) {
var Class = this, out = new Class(), words=[], w=sjcl.bitArray, t = this.prototype,
l = Math.min(this.bitLength || 0x100000000, w.bitLength(bits)), e = l % t.radix || t.radix;
@@ -394,7 +396,9 @@ sjcl.bn.prototype.radixMask = (1 << sjcl.bn.prototype.radix) - 1;
* i.e. a prime of the form 2^e + sum(a * 2^b),where the sum is negative and sparse.
*/
sjcl.bn.pseudoMersennePrime = function(exponent, coeff) {
/** @constructor */
/** @constructor
* @private
*/
function p(it) {
this.initWith(it);
/*if (this.limbs[this.modOffset]) {
@@ -427,10 +431,11 @@ sjcl.bn.pseudoMersennePrime = function(exponent, coeff) {
ppr.modulus.cnormalize();
/** Approximate reduction mod p. May leave a number which is negative or slightly larger than p.
* @this {sjcl.bn}
* @memberof sjcl.bn
* @this { sjcl.bn }
*/
ppr.reduce = function() {
var i, k, l, mo = this.modOffset, limbs = this.limbs, aff, off = this.offset, ol = this.offset.length, fac = this.factor, ll;
var i, k, l, mo = this.modOffset, limbs = this.limbs, off = this.offset, ol = this.offset.length, fac = this.factor, ll;
i = this.minOffset;
while (limbs.length > mo) {
@@ -452,7 +457,9 @@ sjcl.bn.pseudoMersennePrime = function(exponent, coeff) {
return this;
};
/** @this {sjcl.bn} */
/** @memberof sjcl.bn
* @this { sjcl.bn }
*/
ppr._strongReduce = (ppr.fullMask === -1) ? ppr.reduce : function() {
var limbs = this.limbs, i = limbs.length - 1, k, l;
this.reduce();
@@ -467,7 +474,8 @@ sjcl.bn.pseudoMersennePrime = function(exponent, coeff) {
};
/** mostly constant-time, very expensive full reduction.
* @this {sjcl.bn}
* @memberof sjcl.bn
* @this { sjcl.bn }
*/
ppr.fullReduce = function() {
var greater, i;
@@ -501,7 +509,9 @@ sjcl.bn.pseudoMersennePrime = function(exponent, coeff) {
};
/** @this {sjcl.bn} */
/** @memberof sjcl.bn
* @this { sjcl.bn }
*/
ppr.inverse = function() {
return (this.power(this.modulus.sub(2)));
};
@@ -512,18 +522,24 @@ sjcl.bn.pseudoMersennePrime = function(exponent, coeff) {
};
// a small Mersenne prime
var sbp = sjcl.bn.pseudoMersennePrime;
sjcl.bn.prime = {
p127: sjcl.bn.pseudoMersennePrime(127, [[0,-1]]),
p127: sbp(127, [[0,-1]]),
// Bernstein's prime for Curve25519
p25519: sjcl.bn.pseudoMersennePrime(255, [[0,-19]]),
p25519: sbp(255, [[0,-19]]),
// Koblitz primes
p192k: sbp(192, [[32,-1],[12,-1],[8,-1],[7,-1],[6,-1],[3,-1],[0,-1]]),
p224k: sbp(224, [[32,-1],[12,-1],[11,-1],[9,-1],[7,-1],[4,-1],[1,-1],[0,-1]]),
p256k: sbp(256, [[32,-1],[9,-1],[8,-1],[7,-1],[6,-1],[4,-1],[0,-1]]),
// NIST primes
p192: sjcl.bn.pseudoMersennePrime(192, [[0,-1],[64,-1]]),
p224: sjcl.bn.pseudoMersennePrime(224, [[0,1],[96,-1]]),
p256: sjcl.bn.pseudoMersennePrime(256, [[0,-1],[96,1],[192,1],[224,-1]]),
p384: sjcl.bn.pseudoMersennePrime(384, [[0,-1],[32,1],[96,-1],[128,-1]]),
p521: sjcl.bn.pseudoMersennePrime(521, [[0,-1]])
p192: sbp(192, [[0,-1],[64,-1]]),
p224: sbp(224, [[0,1],[96,-1]]),
p256: sbp(256, [[0,-1],[96,1],[192,1],[224,-1]]),
p384: sbp(384, [[0,-1],[32,1],[96,-1],[128,-1]]),
p521: sbp(521, [[0,-1]])
};
sjcl.bn.random = function(modulus, paranoia) {

View File

@@ -5,18 +5,18 @@
* @author Dan Boneh
*/
/** @namespace
* Dangerous: CBC mode with PKCS#5 padding.
*
* @author Emily Stark
* @author Mike Hamburg
* @author Dan Boneh
*/
if (sjcl.beware === undefined) {
sjcl.beware = {};
}
sjcl.beware["CBC mode is dangerous because it doesn't protect message integrity."
] = function() {
/** @namespace
* Dangerous: CBC mode with PKCS#5 padding.
*
* @author Emily Stark
* @author Mike Hamburg
* @author Dan Boneh
*/
sjcl.mode.cbc = {
/** The name of the mode.
* @constant
@@ -100,7 +100,7 @@ sjcl.beware["CBC mode is dangerous because it doesn't protect message integrity.
/* check and remove the pad */
bi = output[i-1] & 255;
if (bi == 0 || bi > 16) {
if (bi === 0 || bi > 16) {
throw new sjcl.exception.corrupt("pkcs#5 padding corrupt");
}
bo = bi * 0x1010101;

View File

@@ -25,7 +25,7 @@ sjcl.mode.ccm = {
* @return {bitArray} The encrypted data, an array of bytes.
*/
encrypt: function(prf, plaintext, iv, adata, tlen) {
var L, i, out = plaintext.slice(0), tag, w=sjcl.bitArray, ivl = w.bitLength(iv) / 8, ol = w.bitLength(out) / 8;
var L, out = plaintext.slice(0), tag, w=sjcl.bitArray, ivl = w.bitLength(iv) / 8, ol = w.bitLength(out) / 8;
tlen = tlen || 64;
adata = adata || [];
@@ -59,7 +59,7 @@ sjcl.mode.ccm = {
decrypt: function(prf, ciphertext, iv, adata, tlen) {
tlen = tlen || 64;
adata = adata || [];
var L, i,
var L,
w=sjcl.bitArray,
ivl = w.bitLength(iv) / 8,
ol = w.bitLength(ciphertext),
@@ -101,7 +101,7 @@ sjcl.mode.ccm = {
*/
_computeTag: function(prf, plaintext, iv, adata, tlen, L) {
// compute B[0]
var q, mac, field = 0, offset = 24, tmp, i, macData = [], w=sjcl.bitArray, xor = w._xor4;
var mac, tmp, i, macData = [], w=sjcl.bitArray, xor = w._xor4;
tlen /= 8;
@@ -161,7 +161,7 @@ sjcl.mode.ccm = {
* @private
*/
_ctrMode: function(prf, data, iv, tag, tlen, L) {
var enc, i, w=sjcl.bitArray, xor = w._xor4, ctr, b, l = data.length, bl=w.bitLength(data);
var enc, i, w=sjcl.bitArray, xor = w._xor4, ctr, l = data.length, bl=w.bitLength(data);
// start the ctr
ctr = w.concat([w.partial(8,L-1)],iv).concat([0,0,0]).slice(0,4);

View File

@@ -0,0 +1,64 @@
/** @fileOverview Bit array codec implementations.
*
* @author Nils Kenneweg
*/
/** @namespace Base32 encoding/decoding */
sjcl.codec.base32 = {
/** The base32 alphabet.
* @private
*/
_chars: "0123456789abcdefghjkmnpqrstvwxyz",
/* bits in an array */
BITS: 32,
/* base to encode at (2^x) */
BASE: 5,
/* bits - base */
REMAINING: 27,
/** Convert from a bitArray to a base32 string. */
fromBits: function (arr, _noEquals) {
var BITS = sjcl.codec.base32.BITS, BASE = sjcl.codec.base32.BASE, REMAINING = sjcl.codec.base32.REMAINING;
var out = "", i, bits=0, c = sjcl.codec.base32._chars, ta=0, bl = sjcl.bitArray.bitLength(arr);
for (i=0; out.length * BASE <= bl; ) {
out += c.charAt((ta ^ arr[i]>>>bits) >>> REMAINING);
if (bits < BASE) {
ta = arr[i] << (BASE-bits);
bits += REMAINING;
i++;
} else {
ta <<= BASE;
bits -= BASE;
}
}
return out;
},
/** Convert from a base32 string to a bitArray */
toBits: function(str) {
var BITS = sjcl.codec.base32.BITS, BASE = sjcl.codec.base32.BASE, REMAINING = sjcl.codec.base32.REMAINING;
var out = [], i, bits=0, c = sjcl.codec.base32._chars, ta=0, x;
for (i=0; i<str.length; i++) {
x = c.indexOf(str.charAt(i));
if (x < 0) {
throw new sjcl.exception.invalid("this isn't base32!");
}
if (bits > REMAINING) {
bits -= REMAINING;
out.push(ta ^ x>>>bits);
ta = x << (BITS-bits);
} else {
bits += BASE;
ta ^= x << (BITS-bits);
}
}
if (bits&56) {
out.push(sjcl.bitArray.partial(bits&56, ta, 1));
}
return out;
}
};

View File

@@ -15,7 +15,9 @@ sjcl.codec.base64 = {
/** Convert from a bitArray to a base64 string. */
fromBits: function (arr, _noEquals, _url) {
var out = "", i, bits=0, c = sjcl.codec.base64._chars, ta=0, bl = sjcl.bitArray.bitLength(arr);
if (_url) c = c.substr(0,62) + '-_';
if (_url) {
c = c.substr(0,62) + '-_';
}
for (i=0; out.length * 6 < bl; ) {
out += c.charAt((ta ^ arr[i]>>>bits) >>> 26);
if (bits < 6) {
@@ -35,7 +37,9 @@ sjcl.codec.base64 = {
toBits: function(str, _url) {
str = str.replace(/\s|=/g,'');
var out = [], i, bits=0, c = sjcl.codec.base64._chars, ta=0, x;
if (_url) c = c.substr(0,62) + '-_';
if (_url) {
c = c.substr(0,62) + '-_';
}
for (i=0; i<str.length; i++) {
x = c.indexOf(str.charAt(i));
if (x < 0) {

View File

@@ -9,7 +9,7 @@
sjcl.codec.hex = {
/** Convert from a bitArray to a hex string. */
fromBits: function (arr) {
var out = "", i, x;
var out = "", i;
for (i=0; i<arr.length; i++) {
out += ((arr[i]|0)+0xF00000000000).toString(16).substr(4);
}

View File

@@ -15,13 +15,13 @@
* @param {String} plaintext The data to encrypt.
* @param {Object} [params] The parameters including tag, iv and salt.
* @param {Object} [rp] A returned version with filled-in parameters.
* @return {String} The ciphertext.
* @return {Object} The cipher raw data.
* @throws {sjcl.exception.invalid} if a parameter is invalid.
*/
encrypt: function (password, plaintext, params, rp) {
_encrypt: function (password, plaintext, params, rp) {
params = params || {};
rp = rp || {};
var j = sjcl.json, p = j._add({ iv: sjcl.random.randomWords(4,0) },
j.defaults), tmp, prp, adata;
j._add(p, params);
@@ -32,7 +32,7 @@
if (typeof p.iv === "string") {
p.iv = sjcl.codec.base64.toBits(p.iv);
}
if (!sjcl.mode[p.mode] ||
!sjcl.cipher[p.cipher] ||
(typeof password === "string" && p.iter <= 100) ||
@@ -41,7 +41,7 @@
(p.iv.length < 2 || p.iv.length > 4)) {
throw new sjcl.exception.invalid("json encrypt: invalid parameters");
}
if (typeof password === "string") {
tmp = sjcl.misc.cachedPbkdf2(password, p);
password = tmp.key.slice(0,p.ks/32);
@@ -58,39 +58,52 @@
adata = sjcl.codec.utf8String.toBits(adata);
}
prp = new sjcl.cipher[p.cipher](password);
/* return the json data */
j._add(rp, p);
rp.key = password;
/* do the encryption */
p.ct = sjcl.mode[p.mode].encrypt(prp, plaintext, p.iv, adata, p.ts);
//return j.encode(j._subtract(p, j.defaults));
return p;
},
/** Simple encryption function.
* @param {String|bitArray} password The password or key.
* @param {String} plaintext The data to encrypt.
* @param {Object} [params] The parameters including tag, iv and salt.
* @param {Object} [rp] A returned version with filled-in parameters.
* @return {String} The ciphertext serialized data.
* @throws {sjcl.exception.invalid} if a parameter is invalid.
*/
encrypt: function (password, plaintext, params, rp) {
var j = sjcl.json, p = j._encrypt.apply(j, arguments);
return j.encode(p);
},
/** Simple decryption function.
* @param {String|bitArray} password The password or key.
* @param {String} ciphertext The ciphertext to decrypt.
* @param {Object} ciphertext The cipher raw data to decrypt.
* @param {Object} [params] Additional non-default parameters.
* @param {Object} [rp] A returned object with filled parameters.
* @return {String} The plaintext.
* @throws {sjcl.exception.invalid} if a parameter is invalid.
* @throws {sjcl.exception.corrupt} if the ciphertext is corrupt.
*/
decrypt: function (password, ciphertext, params, rp) {
_decrypt: function (password, ciphertext, params, rp) {
params = params || {};
rp = rp || {};
var j = sjcl.json, p = j._add(j._add(j._add({},j.defaults),j.decode(ciphertext)), params, true), ct, tmp, prp, adata=p.adata;
var j = sjcl.json, p = j._add(j._add(j._add({},j.defaults),ciphertext), params, true), ct, tmp, prp, adata=p.adata;
if (typeof p.salt === "string") {
p.salt = sjcl.codec.base64.toBits(p.salt);
}
if (typeof p.iv === "string") {
p.iv = sjcl.codec.base64.toBits(p.iv);
}
if (!sjcl.mode[p.mode] ||
!sjcl.cipher[p.cipher] ||
(typeof password === "string" && p.iter <= 100) ||
@@ -100,7 +113,7 @@
(p.iv.length < 2 || p.iv.length > 4)) {
throw new sjcl.exception.invalid("json decrypt: invalid parameters");
}
if (typeof password === "string") {
tmp = sjcl.misc.cachedPbkdf2(password, p);
password = tmp.key.slice(0,p.ks/32);
@@ -112,15 +125,33 @@
adata = sjcl.codec.utf8String.toBits(adata);
}
prp = new sjcl.cipher[p.cipher](password);
/* do the decryption */
ct = sjcl.mode[p.mode].decrypt(prp, p.ct, p.iv, adata, p.ts);
/* return the json data */
j._add(rp, p);
rp.key = password;
return sjcl.codec.utf8String.fromBits(ct);
if (params.raw === 1) {
return ct;
} else {
return sjcl.codec.utf8String.fromBits(ct);
}
},
/** Simple decryption function.
* @param {String|bitArray} password The password or key.
* @param {String} ciphertext The ciphertext to decrypt.
* @param {Object} [params] Additional non-default parameters.
* @param {Object} [rp] A returned object with filled parameters.
* @return {String} The plaintext.
* @throws {sjcl.exception.invalid} if a parameter is invalid.
* @throws {sjcl.exception.corrupt} if the ciphertext is corrupt.
*/
decrypt: function (password, ciphertext, params, rp) {
var j = sjcl.json;
return j._decrypt(password, j.decode(ciphertext), params, rp);
},
/** Encode a flat structure into a JSON string.
@@ -138,23 +169,23 @@
}
out += comma + '"' + i + '":';
comma = ',';
switch (typeof obj[i]) {
case 'number':
case 'boolean':
out += obj[i];
break;
case 'string':
out += '"' + escape(obj[i]) + '"';
break;
case 'object':
out += '"' + sjcl.codec.base64.fromBits(obj[i],0) + '"';
break;
default:
throw new sjcl.exception.bug("json encode: unsupported type");
case 'number':
case 'boolean':
out += obj[i];
break;
case 'string':
out += '"' + escape(obj[i]) + '"';
break;
case 'object':
out += '"' + sjcl.codec.base64.fromBits(obj[i],0) + '"';
break;
default:
throw new sjcl.exception.bug("json encode: unsupported type");
}
}
}
@@ -174,13 +205,15 @@
}
var a = str.replace(/^\{|\}$/g, '').split(/,/), out={}, i, m;
for (i=0; i<a.length; i++) {
if (!(m=a[i].match(/^(?:(["']?)([a-z][a-z0-9]*)\1):(?:(\d+)|"([a-z0-9+\/%*_.@=\-]*)")$/i))) {
if (!(m=a[i].match(/^\s*(?:(["']?)([a-z][a-z0-9]*)\1)\s*:\s*(?:(-?\d+)|"([a-z0-9+\/%*_.@=\-]*)"|(true|false))$/i))) {
throw new sjcl.exception.invalid("json decode: this isn't json!");
}
if (m[3]) {
out[m[2]] = parseInt(m[3],10);
} else {
} else if (m[4]) {
out[m[2]] = m[2].match(/^(ct|salt|iv)$/) ? sjcl.codec.base64.toBits(m[4]) : unescape(m[4]);
} else if (m[5]) {
out[m[2]] = m[5] === 'true';
}
}
return out;
@@ -213,13 +246,13 @@
*/
_subtract: function (plus, minus) {
var out = {}, i;
for (i in plus) {
if (plus.hasOwnProperty(i) && plus[i] !== minus[i]) {
out[i] = plus[i];
}
}
return out;
},
@@ -262,7 +295,7 @@ sjcl.misc._pbkdf2Cache = {};
/** Cached PBKDF2 key derivation.
* @param {String} password The password.
* @param {Object} [params] The derivation params (iteration count and optional salt).
* @param {Object} [obj] The derivation params (iteration count and optional salt).
* @return {Object} The derived data in key, the salt in salt.
*/
sjcl.misc.cachedPbkdf2 = function (password, obj) {

View File

@@ -1,3 +1,6 @@
/**
* base class for all ecc operations.
*/
sjcl.ecc = {};
/**
@@ -152,7 +155,9 @@ sjcl.ecc.pointJac.prototype = {
a = y2.mul(this.x.mul(4)),
b = y2.square().mul(8),
z2 = this.z.square(),
c = this.x.sub(z2).mul(3).mul(this.x.add(z2)),
c = this.curve.a.toString() == (new sjcl.bn(-3)).toString() ?
this.x.sub(z2).mul(3).mul(this.x.add(z2)) :
this.x.square().mul(3).add(z2.square().mul(this.curve.a)),
x = c.square().subM(a).subM(a),
y = a.sub(x).mul(c).subM(b),
z = this.y.add(this.y).mul(this.z);
@@ -162,7 +167,7 @@ sjcl.ecc.pointJac.prototype = {
/**
* Returns a copy of this point converted to affine coordinates.
* @return {sjcl.ecc.point} The converted point.
*/
*/
toAffine: function() {
if (this.isIdentity || this.z.equals(0)) {
return new sjcl.ecc.point(this.curve);
@@ -250,7 +255,7 @@ sjcl.ecc.pointJac.prototype = {
*/
sjcl.ecc.curve = function(Field, r, a, b, x, y) {
this.field = Field;
this.r = Field.prototype.modulus.sub(r);
this.r = new sjcl.bn(r);
this.a = new Field(a);
this.b = new Field(b);
this.G = new sjcl.ecc.point(this, new Field(x), new Field(y));
@@ -269,7 +274,7 @@ sjcl.ecc.curve.prototype.fromBits = function (bits) {
sjcl.ecc.curves = {
c192: new sjcl.ecc.curve(
sjcl.bn.prime.p192,
"0x662107c8eb94364e4b2dd7ce",
"0xffffffffffffffffffffffff99def836146bc9b1b4d22831",
-3,
"0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1",
"0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012",
@@ -277,7 +282,7 @@ sjcl.ecc.curves = {
c224: new sjcl.ecc.curve(
sjcl.bn.prime.p224,
"0xe95c1f470fc1ec22d6baa3a3d5c4",
"0xffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d",
-3,
"0xb4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4",
"0xb70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21",
@@ -285,7 +290,7 @@ sjcl.ecc.curves = {
c256: new sjcl.ecc.curve(
sjcl.bn.prime.p256,
"0x4319055358e8617b0c46353d039cdaae",
"0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551",
-3,
"0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b",
"0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296",
@@ -293,71 +298,135 @@ sjcl.ecc.curves = {
c384: new sjcl.ecc.curve(
sjcl.bn.prime.p384,
"0x389cb27e0bc8d21fa7e5f24cb74f58851313e696333ad68c",
"0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973",
-3,
"0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef",
"0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7",
"0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f")
"0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f"),
k192: new sjcl.ecc.curve(
sjcl.bn.prime.p192k,
"0xfffffffffffffffffffffffe26f2fc170f69466a74defd8d",
0,
3,
"0xdb4ff10ec057e9ae26b07d0280b7f4341da5d1b1eae06c7d",
"0x9b2f2f6d9c5628a7844163d015be86344082aa88d95e2f9d"),
k224: new sjcl.ecc.curve(
sjcl.bn.prime.p224k,
"0x010000000000000000000000000001dce8d2ec6184caf0a971769fb1f7",
0,
5,
"0xa1455b334df099df30fc28a169a467e9e47075a90f7e650eb6b7a45c",
"0x7e089fed7fba344282cafbd6f7e319f7c0b0bd59e2ca4bdb556d61a5"),
k256: new sjcl.ecc.curve(
sjcl.bn.prime.p256k,
"0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141",
0,
7,
"0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
"0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8")
};
/* Diffie-Hellman-like public-key system */
sjcl.ecc._dh = function(cn) {
sjcl.ecc[cn] = {
/** @constructor */
publicKey: function(curve, point) {
this._curve = curve;
this._curveBitLength = curve.r.bitLength();
if (point instanceof Array) {
this._point = curve.fromBits(point);
} else {
this._point = point;
}
this.get = function() {
var pointbits = this._point.toBits();
var len = sjcl.bitArray.bitLength(pointbits);
var x = sjcl.bitArray.bitSlice(pointbits, 0, len/2);
var y = sjcl.bitArray.bitSlice(pointbits, len/2);
return { x: x, y: y };
}
},
/** @constructor */
secretKey: function(curve, exponent) {
this._curve = curve;
this._curveBitLength = curve.r.bitLength();
this._exponent = exponent;
this.get = function() {
return this._exponent.toBits();
}
},
/** @constructor */
generateKeys: function(curve, paranoia, sec) {
if (curve === undefined) {
curve = 256;
}
if (typeof curve === "number") {
curve = sjcl.ecc.curves['c'+curve];
if (curve === undefined) {
throw new sjcl.exception.invalid("no such curve");
}
}
if (sec === undefined) {
var sec = sjcl.bn.random(curve.r, paranoia);
}
var pub = curve.G.mult(sec);
return { pub: new sjcl.ecc[cn].publicKey(curve, pub),
sec: new sjcl.ecc[cn].secretKey(curve, sec) };
/** our basicKey classes
*/
sjcl.ecc.basicKey = {
/** ecc publicKey.
* @constructor
* @param {curve} curve the elliptic curve
* @param {point} point the point on the curve
*/
publicKey: function(curve, point) {
this._curve = curve;
this._curveBitLength = curve.r.bitLength();
if (point instanceof Array) {
this._point = curve.fromBits(point);
} else {
this._point = point;
}
};
/** get this keys point data
* @return x and y as bitArrays
*/
this.get = function() {
var pointbits = this._point.toBits();
var len = sjcl.bitArray.bitLength(pointbits);
var x = sjcl.bitArray.bitSlice(pointbits, 0, len/2);
var y = sjcl.bitArray.bitSlice(pointbits, len/2);
return { x: x, y: y };
};
},
/** ecc secretKey
* @constructor
* @param {curve} curve the elliptic curve
* @param exponent
*/
secretKey: function(curve, exponent) {
this._curve = curve;
this._curveBitLength = curve.r.bitLength();
this._exponent = exponent;
/** get this keys exponent data
* @return {bitArray} exponent
*/
this.get = function () {
return this._exponent.toBits();
};
}
};
sjcl.ecc._dh("elGamal");
/** @private */
sjcl.ecc.basicKey.generateKeys = function(cn) {
return function generateKeys(curve, paranoia, sec) {
curve = curve || 256;
if (typeof curve === "number") {
curve = sjcl.ecc.curves['c'+curve];
if (curve === undefined) {
throw new sjcl.exception.invalid("no such curve");
}
}
sec = sec || sjcl.bn.random(curve.r, paranoia);
var pub = curve.G.mult(sec);
return { pub: new sjcl.ecc[cn].publicKey(curve, pub),
sec: new sjcl.ecc[cn].secretKey(curve, sec) };
};
};
/** elGamal keys */
sjcl.ecc.elGamal = {
/** generate keys
* @function
* @param curve
* @param {int} paranoia Paranoia for generation (default 6)
* @param {secretKey} sec secret Key to use. used to get the publicKey for ones secretKey
*/
generateKeys: sjcl.ecc.basicKey.generateKeys("elGamal"),
/** elGamal publicKey.
* @constructor
* @augments sjcl.ecc.basicKey.publicKey
*/
publicKey: function (curve, point) {
sjcl.ecc.basicKey.publicKey.apply(this, arguments);
},
/** elGamal secretKey
* @constructor
* @augments sjcl.ecc.basicKey.secretKey
*/
secretKey: function (curve, exponent) {
sjcl.ecc.basicKey.secretKey.apply(this, arguments);
}
};
sjcl.ecc.elGamal.publicKey.prototype = {
/** Kem function of elGamal Public Key
* @param paranoia paranoia to use for randomization.
* @return {object} key and tag. unkem(tag) with the corresponding secret key results in the key returned.
*/
kem: function(paranoia) {
var sec = sjcl.bn.random(this._curve.r, paranoia),
tag = this._curve.G.mult(sec).toBits(),
@@ -367,34 +436,58 @@ sjcl.ecc.elGamal.publicKey.prototype = {
};
sjcl.ecc.elGamal.secretKey.prototype = {
/** UnKem function of elGamal Secret Key
* @param {bitArray} tag The Tag to decrypt.
* @return {bitArray} decrypted key.
*/
unkem: function(tag) {
return sjcl.hash.sha256.hash(this._curve.fromBits(tag).mult(this._exponent).toBits());
},
/** Diffie-Hellmann function
* @param {elGamal.publicKey} pk The Public Key to do Diffie-Hellmann with
* @return {bitArray} diffie-hellmann result for this key combination.
*/
dh: function(pk) {
return sjcl.hash.sha256.hash(pk._point.mult(this._exponent).toBits());
}
},
/** Diffie-Hellmann function, compatible with Java generateSecret
* @param {elGamal.publicKey} pk The Public Key to do Diffie-Hellmann with
* @return {bitArray} undigested X value, diffie-hellmann result for this key combination,
* compatible with Java generateSecret().
*/
dhJavaEc: function(pk) {
return pk._point.mult(this._exponent).x.toBits();
}
};
sjcl.ecc._dh("ecdsa");
sjcl.ecc.ecdsa.secretKey.prototype = {
sign: function(hash, paranoia, fakeLegacyVersion, fixedKForTesting) {
if (sjcl.bitArray.bitLength(hash) > this._curveBitLength) {
hash = sjcl.bitArray.clamp(hash, this._curveBitLength);
}
var R = this._curve.r,
l = R.bitLength(),
k = fixedKForTesting || sjcl.bn.random(R.sub(1), paranoia).add(1),
r = this._curve.G.mult(k).x.mod(R),
ss = sjcl.bn.fromBits(hash).add(r.mul(this._exponent)),
s = fakeLegacyVersion ? ss.inverseMod(R).mul(k).mod(R)
: ss.mul(k.inverseMod(R)).mod(R);
return sjcl.bitArray.concat(r.toBits(l), s.toBits(l));
}
/** ecdsa keys */
sjcl.ecc.ecdsa = {
/** generate keys
* @function
* @param curve
* @param {int} paranoia Paranoia for generation (default 6)
* @param {secretKey} sec secret Key to use. used to get the publicKey for ones secretKey
*/
generateKeys: sjcl.ecc.basicKey.generateKeys("ecdsa")
};
/** ecdsa publicKey.
* @constructor
* @augments sjcl.ecc.basicKey.publicKey
*/
sjcl.ecc.ecdsa.publicKey = function (curve, point) {
sjcl.ecc.basicKey.publicKey.apply(this, arguments);
};
/** specific functions for ecdsa publicKey. */
sjcl.ecc.ecdsa.publicKey.prototype = {
/** Diffie-Hellmann function
* @param {bitArray} hash hash to verify.
* @param {bitArray} rs signature bitArray.
* @param {boolean} fakeLegacyVersion use old legacy version
*/
verify: function(hash, rs, fakeLegacyVersion) {
if (sjcl.bitArray.bitLength(hash) > this._curveBitLength) {
hash = sjcl.bitArray.clamp(hash, this._curveBitLength);
@@ -418,3 +511,33 @@ sjcl.ecc.ecdsa.publicKey.prototype = {
return true;
}
};
/** ecdsa secretKey
* @constructor
* @augments sjcl.ecc.basicKey.publicKey
*/
sjcl.ecc.ecdsa.secretKey = function (curve, exponent) {
sjcl.ecc.basicKey.secretKey.apply(this, arguments);
};
/** specific functions for ecdsa secretKey. */
sjcl.ecc.ecdsa.secretKey.prototype = {
/** Diffie-Hellmann function
* @param {bitArray} hash hash to sign.
* @param {int} paranoia paranoia for random number generation
* @param {boolean} fakeLegacyVersion use old legacy version
*/
sign: function(hash, paranoia, fakeLegacyVersion, fixedKForTesting) {
if (sjcl.bitArray.bitLength(hash) > this._curveBitLength) {
hash = sjcl.bitArray.clamp(hash, this._curveBitLength);
}
var R = this._curve.r,
l = R.bitLength(),
k = fixedKForTesting || sjcl.bn.random(R.sub(1), paranoia).add(1),
r = this._curve.G.mult(k).x.mod(R),
ss = sjcl.bn.fromBits(hash).add(r.mul(this._exponent)),
s = fakeLegacyVersion ? ss.inverseMod(R).mul(k).mod(R)
: ss.mul(k.inverseMod(R)).mod(R);
return sjcl.bitArray.concat(r.toBits(l), s.toBits(l));
}
};

View File

@@ -120,7 +120,7 @@ sjcl.mode.gcm = {
* @param {Number} tlen The length of the tag, in bits.
*/
_ctrMode: function(encrypt, prf, data, adata, iv, tlen) {
var H, J0, S0, enc, i, ctr, tag, last, l, bl, abl, ivbl, w=sjcl.bitArray, xor=w._xor4;
var H, J0, S0, enc, i, ctr, tag, last, l, bl, abl, ivbl, w=sjcl.bitArray;
// Calculate data lengths
l = data.length;

View File

@@ -27,13 +27,35 @@ sjcl.misc.hmac = function (key, Hash) {
this._baseHash[0].update(exKey[0]);
this._baseHash[1].update(exKey[1]);
this._resultHash = new Hash(this._baseHash[0]);
};
/** HMAC with the specified hash function. Also called encrypt since it's a prf.
* @param {bitArray|String} data The data to mac.
*/
sjcl.misc.hmac.prototype.encrypt = sjcl.misc.hmac.prototype.mac = function (data) {
var w = new (this._hash)(this._baseHash[0]).update(data).finalize();
return new (this._hash)(this._baseHash[1]).update(w).finalize();
if (!this._updated) {
this.update(data);
return this.digest(data);
} else {
throw new sjcl.exception.invalid("encrypt on already updated hmac called!");
}
};
sjcl.misc.hmac.prototype.reset = function () {
this._resultHash = new this._hash(this._baseHash[0]);
this._updated = false;
};
sjcl.misc.hmac.prototype.update = function (data) {
this._updated = true;
this._resultHash.update(data);
};
sjcl.misc.hmac.prototype.digest = function () {
var w = this._resultHash.finalize(), result = new (this._hash)(this._baseHash[1]).update(w).finalize();
this.reset();
return result;
};

View File

@@ -12,7 +12,7 @@
* This is the method specified by RSA's PKCS #5 standard.
*
* @param {bitArray|String} password The password.
* @param {bitArray} salt The salt. Should have lots of entropy.
* @param {bitArray|String} salt The salt. Should have lots of entropy.
* @param {Number} [count=1000] The number of iterations. Higher numbers make the function slower but more secure.
* @param {Number} [length] The length of the derived key. Defaults to the
output size of the hash function.
@@ -30,6 +30,10 @@ sjcl.misc.pbkdf2 = function (password, salt, count, length, Prff) {
password = sjcl.codec.utf8String.toBits(password);
}
if (typeof salt === "string") {
salt = sjcl.codec.utf8String.toBits(salt);
}
Prff = Prff || sjcl.misc.hmac;
var prf = new Prff(password),

View File

@@ -3,12 +3,13 @@
* @author Emily Stark
* @author Mike Hamburg
* @author Dan Boneh
* @author Michael Brooks
*/
/** @constructor
* @class Random number generator
*
* @description
* <b>Use sjcl.random as a singleton for this class!</b>
* <p>
* This random number generator is a derivative of Ferguson and Schneier's
* generator Fortuna. It collects entropy from various events into several
@@ -74,10 +75,11 @@ sjcl.prng = function(defaultParanoia) {
this._PARANOIA_LEVELS = [0,48,64,96,128,192,256,384,512,768,1024];
this._MILLISECONDS_PER_RESEED = 30000;
this._BITS_PER_RESEED = 80;
}
};
sjcl.prng.prototype = {
/** Generate several random words, and return them in an array
/** Generate several random words, and return them in an array.
* A word consists of 32 bits (4 bytes)
* @param {Number} nwords The number of words to generate.
*/
randomWords: function (nwords, paranoia) {
@@ -102,7 +104,11 @@ sjcl.prng.prototype = {
return out.slice(0,nwords);
},
setDefaultParanoia: function (paranoia) {
setDefaultParanoia: function (paranoia, allowZeroParanoia) {
if (paranoia === 0 && allowZeroParanoia !== "Setting paranoia=0 will ruin your security; use it only for testing") {
throw "Setting paranoia=0 will ruin your security; use it only for testing";
}
this._defaultParanoia = paranoia;
},
@@ -119,7 +125,7 @@ sjcl.prng.prototype = {
i, tmp,
t = (new Date()).valueOf(),
robin = this._robins[source],
oldReady = this.isReady(), err = 0;
oldReady = this.isReady(), err = 0, objName;
id = this._collectorIds[source];
if (id === undefined) { id = this._collectorIds[source] = this._collectorIdNext ++; }
@@ -137,7 +143,7 @@ sjcl.prng.prototype = {
break;
case "object":
var objName = Object.prototype.toString.call(data);
objName = Object.prototype.toString.call(data);
if (objName === "[object Uint32Array]") {
tmp = [];
for (i = 0; i < data.length; i++) {
@@ -149,7 +155,7 @@ sjcl.prng.prototype = {
err = 1;
}
for (i=0; i<data.length && !err; i++) {
if (typeof(data[i]) != "number") {
if (typeof(data[i]) !== "number") {
err = 1;
}
}
@@ -234,14 +240,25 @@ sjcl.prng.prototype = {
startCollectors: function () {
if (this._collectorsStarted) { return; }
this._eventListener = {
loadTimeCollector: this._bind(this._loadTimeCollector),
mouseCollector: this._bind(this._mouseCollector),
keyboardCollector: this._bind(this._keyboardCollector),
accelerometerCollector: this._bind(this._accelerometerCollector),
touchCollector: this._bind(this._touchCollector)
};
if (window.addEventListener) {
window.addEventListener("load", this._loadTimeCollector, false);
window.addEventListener("mousemove", this._mouseCollector, false);
window.addEventListener("load", this._eventListener.loadTimeCollector, false);
window.addEventListener("mousemove", this._eventListener.mouseCollector, false);
window.addEventListener("keypress", this._eventListener.keyboardCollector, false);
window.addEventListener("devicemotion", this._eventListener.accelerometerCollector, false);
window.addEventListener("touchmove", this._eventListener.touchCollector, false);
} else if (document.attachEvent) {
document.attachEvent("onload", this._loadTimeCollector);
document.attachEvent("onmousemove", this._mouseCollector);
}
else {
document.attachEvent("onload", this._eventListener.loadTimeCollector);
document.attachEvent("onmousemove", this._eventListener.mouseCollector);
document.attachEvent("keypress", this._eventListener.keyboardCollector);
} else {
throw new sjcl.exception.bug("can't attach event");
}
@@ -253,12 +270,17 @@ sjcl.prng.prototype = {
if (!this._collectorsStarted) { return; }
if (window.removeEventListener) {
window.removeEventListener("load", this._loadTimeCollector, false);
window.removeEventListener("mousemove", this._mouseCollector, false);
} else if (window.detachEvent) {
window.detachEvent("onload", this._loadTimeCollector);
window.detachEvent("onmousemove", this._mouseCollector);
window.removeEventListener("load", this._eventListener.loadTimeCollector, false);
window.removeEventListener("mousemove", this._eventListener.mouseCollector, false);
window.removeEventListener("keypress", this._eventListener.keyboardCollector, false);
window.removeEventListener("devicemotion", this._eventListener.accelerometerCollector, false);
window.removeEventListener("touchmove", this._eventListener.touchCollector, false);
} else if (document.detachEvent) {
document.detachEvent("onload", this._eventListener.loadTimeCollector);
document.detachEvent("onmousemove", this._eventListener.mouseCollector);
document.detachEvent("keypress", this._eventListener.keyboardCollector);
}
this._collectorsStarted = false;
},
@@ -275,23 +297,30 @@ sjcl.prng.prototype = {
/** remove an event listener for progress or seeded-ness */
removeEventListener: function (name, cb) {
var i, j, cbs=this._callbacks[name], jsTemp=[];
/* I'm not sure if this is necessary; in C++, iterating over a
* collection and modifying it at the same time is a no-no.
*/
for (j in cbs) {
if (cbs.hasOwnProperty(j) && cbs[j] === cb) {
if (cbs.hasOwnProperty(j) && cbs[j] === cb) {
jsTemp.push(j);
}
}
for (i=0; i<jsTemp.length; i++) {
j = jsTemp[i];
delete cbs[j];
}
},
_bind: function (func) {
var that = this;
return function () {
func.apply(that, arguments);
};
},
/** Generate 4 random words, no reseed, no gate.
* @private
*/
@@ -363,44 +392,131 @@ sjcl.prng.prototype = {
this._reseed(reseedData);
},
_keyboardCollector: function () {
this._addCurrentTimeToEntropy(1);
},
_mouseCollector: function (ev) {
var x = ev.x || ev.clientX || ev.offsetX || 0, y = ev.y || ev.clientY || ev.offsetY || 0;
sjcl.random.addEntropy([x,y], 2, "mouse");
var x, y;
try {
x = ev.x || ev.clientX || ev.offsetX || 0;
y = ev.y || ev.clientY || ev.offsetY || 0;
} catch (err) {
// Event originated from a secure element. No mouse position available.
x = 0;
y = 0;
}
if (x != 0 && y!= 0) {
sjcl.random.addEntropy([x,y], 2, "mouse");
}
this._addCurrentTimeToEntropy(0);
},
_touchCollector: function(ev) {
var touch = ev.touches[0] || ev.changedTouches[0];
var x = touch.pageX || touch.clientX,
y = touch.pageY || touch.clientY;
sjcl.random.addEntropy([x,y],1,"touch");
this._addCurrentTimeToEntropy(0);
},
_loadTimeCollector: function (ev) {
sjcl.random.addEntropy((new Date()).valueOf(), 2, "loadtime");
_loadTimeCollector: function () {
this._addCurrentTimeToEntropy(2);
},
_addCurrentTimeToEntropy: function (estimatedEntropy) {
if (typeof window !== 'undefined' && window.performance && typeof window.performance.now === "function") {
//how much entropy do we want to add here?
sjcl.random.addEntropy(window.performance.now(), estimatedEntropy, "loadtime");
} else {
sjcl.random.addEntropy((new Date()).valueOf(), estimatedEntropy, "loadtime");
}
},
_accelerometerCollector: function (ev) {
var ac = ev.accelerationIncludingGravity.x||ev.accelerationIncludingGravity.y||ev.accelerationIncludingGravity.z;
if(window.orientation){
var or = window.orientation;
if (typeof or === "number") {
sjcl.random.addEntropy(or, 1, "accelerometer");
}
}
if (ac) {
sjcl.random.addEntropy(ac, 2, "accelerometer");
}
this._addCurrentTimeToEntropy(0);
},
_fireEvent: function (name, arg) {
var j, cbs=sjcl.random._callbacks[name], cbsTemp=[];
/* TODO: there is a race condition between removing collectors and firing them */
/* TODO: there is a race condition between removing collectors and firing them */
/* I'm not sure if this is necessary; in C++, iterating over a
* collection and modifying it at the same time is a no-no.
*/
for (j in cbs) {
if (cbs.hasOwnProperty(j)) {
if (cbs.hasOwnProperty(j)) {
cbsTemp.push(cbs[j]);
}
}
}
for (j=0; j<cbsTemp.length; j++) {
cbsTemp[j](arg);
cbsTemp[j](arg);
}
}
};
/** an instance for the prng.
* @see sjcl.prng
*/
sjcl.random = new sjcl.prng(6);
(function(){
try {
// get cryptographically strong entropy in Webkit
var ab = new Uint32Array(32);
crypto.getRandomValues(ab);
sjcl.random.addEntropy(ab, 1024, "crypto.getRandomValues");
} catch (e) {
// no getRandomValues :-(
// function for getting nodejs crypto module. catches and ignores errors.
function getCryptoModule() {
try {
return require('crypto');
}
catch (e) {
return null;
}
}
})();
try {
var buf, crypt, ab;
// get cryptographically strong entropy depending on runtime environment
if (typeof module !== 'undefined' && module.exports && (crypt = getCryptoModule()) && crypt.randomBytes) {
buf = crypt.randomBytes(1024/8);
buf = new Uint32Array(new Uint8Array(buf).buffer);
sjcl.random.addEntropy(buf, 1024, "crypto.randomBytes");
} else if (typeof window !== 'undefined' && typeof Uint32Array !== 'undefined') {
ab = new Uint32Array(32);
if (window.crypto && window.crypto.getRandomValues) {
window.crypto.getRandomValues(ab);
} else if (window.msCrypto && window.msCrypto.getRandomValues) {
window.msCrypto.getRandomValues(ab);
} else {
return;
}
// get cryptographically strong entropy in Webkit
sjcl.random.addEntropy(ab, 1024, "crypto.getRandomValues");
} else {
// no getRandomValues :-(
}
} catch (e) {
if (typeof window !== 'undefined' && window.console) {
console.log("There was an error collecting entropy from the browser:");
console.log(e);
//we do not want the library to fail due to randomness not being maintained.
}
}
}());

View File

@@ -138,8 +138,7 @@ sjcl.hash.sha1.prototype = {
_block:function (words) {
var t, tmp, a, b, c, d, e,
w = words.slice(0),
h = this._h,
k = this._key;
h = this._h;
a = h[0]; b = h[1]; c = h[2]; d = h[3]; e = h[4];

View File

@@ -299,7 +299,7 @@ sjcl.hash.sha512.prototype = {
t1h += chh + ((t1l >>> 0) < (chl >>> 0) ? 1 : 0);
t1l += krl;
t1h += krh + ((t1l >>> 0) < (krl >>> 0) ? 1 : 0);
t1l += wrl;
t1l = t1l + wrl|0; // FF32..FF34 perf issue https://bugzilla.mozilla.org/show_bug.cgi?id=1054972
t1h += wrh + ((t1l >>> 0) < (wrl >>> 0) ? 1 : 0);
// t2 = sigma0 + maj

View File

@@ -10,7 +10,7 @@
"use strict";
/*jslint indent: 2, bitwise: false, nomen: false, plusplus: false, white: false, regexp: false */
/*global document, window, escape, unescape */
/*global document, window, escape, unescape, module, require, Uint32Array */
/** @namespace The Stanford Javascript Crypto Library, top-level namespace. */
var sjcl = {
@@ -68,6 +68,11 @@ var sjcl = {
}
};
if(typeof module != 'undefined' && module.exports){
if(typeof module !== 'undefined' && module.exports){
module.exports = sjcl;
}
if (typeof define === "function") {
define([], function () {
return sjcl;
});
}

View File

@@ -36,7 +36,7 @@
<h2>Key Derivation</h2>
<div class="section">
<div>
<label for="salt"">Salt:</label>
<label for="salt">Salt:</label>
<a class="random floatright" href="javascript:randomize('salt',2,0)">random</a>
</div>
<input type="text" id="salt" class="wide hex" autocomplete="off" size="17" maxlength="35"/>

16
src/js/sjcl/package.json Normal file
View File

@@ -0,0 +1,16 @@
{
"name": "sjcl",
"version": "1.0.1",
"description": "Stanford Javascript Crypto Library",
"main": "sjcl.js",
"author": "bitwiseshiftleft",
"keywords": ["encryption", "high-level", "crypto"],
"repository" : {
"type": "git",
"url": "https://github.com/bitwiseshiftleft/sjcl.git"
},
"scripts": {
"test": "make test"
},
"engines": { "node": "*" }
}

View File

@@ -1,48 +1,54 @@
"use strict";function q(a){throw a;}var t=void 0,u=!1;var sjcl={cipher:{},hash:{},keyexchange:{},mode:{},misc:{},codec:{},exception:{corrupt:function(a){this.toString=function(){return"CORRUPT: "+this.message};this.message=a},invalid:function(a){this.toString=function(){return"INVALID: "+this.message};this.message=a},bug:function(a){this.toString=function(){return"BUG: "+this.message};this.message=a},notReady:function(a){this.toString=function(){return"NOT READY: "+this.message};this.message=a}}};
"undefined"!=typeof module&&module.exports&&(module.exports=sjcl);
sjcl.cipher.aes=function(a){this.j[0][0][0]||this.D();var b,c,d,e,f=this.j[0][4],g=this.j[1];b=a.length;var h=1;4!==b&&(6!==b&&8!==b)&&q(new sjcl.exception.invalid("invalid aes key size"));this.a=[d=a.slice(0),e=[]];for(a=b;a<4*b+28;a++){c=d[a-1];if(0===a%b||8===b&&4===a%b)c=f[c>>>24]<<24^f[c>>16&255]<<16^f[c>>8&255]<<8^f[c&255],0===a%b&&(c=c<<8^c>>>24^h<<24,h=h<<1^283*(h>>7));d[a]=d[a-b]^c}for(b=0;a;b++,a--)c=d[b&3?a:a-4],e[b]=4>=a||4>b?c:g[0][f[c>>>24]]^g[1][f[c>>16&255]]^g[2][f[c>>8&255]]^g[3][f[c&
"undefined"!==typeof module&&module.exports&&(module.exports=sjcl);"function"===typeof define&&define([],function(){return sjcl});
sjcl.cipher.aes=function(a){this.k[0][0][0]||this.D();var b,c,d,e,f=this.k[0][4],g=this.k[1];b=a.length;var h=1;4!==b&&(6!==b&&8!==b)&&q(new sjcl.exception.invalid("invalid aes key size"));this.b=[d=a.slice(0),e=[]];for(a=b;a<4*b+28;a++){c=d[a-1];if(0===a%b||8===b&&4===a%b)c=f[c>>>24]<<24^f[c>>16&255]<<16^f[c>>8&255]<<8^f[c&255],0===a%b&&(c=c<<8^c>>>24^h<<24,h=h<<1^283*(h>>7));d[a]=d[a-b]^c}for(b=0;a;b++,a--)c=d[b&3?a:a-4],e[b]=4>=a||4>b?c:g[0][f[c>>>24]]^g[1][f[c>>16&255]]^g[2][f[c>>8&255]]^g[3][f[c&
255]]};
sjcl.cipher.aes.prototype={encrypt:function(a){return y(this,a,0)},decrypt:function(a){return y(this,a,1)},j:[[[],[],[],[],[]],[[],[],[],[],[]]],D:function(){var a=this.j[0],b=this.j[1],c=a[4],d=b[4],e,f,g,h=[],l=[],k,n,m,p;for(e=0;0x100>e;e++)l[(h[e]=e<<1^283*(e>>7))^e]=e;for(f=g=0;!c[f];f^=k||1,g=l[g]||1){m=g^g<<1^g<<2^g<<3^g<<4;m=m>>8^m&255^99;c[f]=m;d[m]=f;n=h[e=h[k=h[f]]];p=0x1010101*n^0x10001*e^0x101*k^0x1010100*f;n=0x101*h[m]^0x1010100*m;for(e=0;4>e;e++)a[e][f]=n=n<<24^n>>>8,b[e][m]=p=p<<24^p>>>8}for(e=
sjcl.cipher.aes.prototype={encrypt:function(a){return y(this,a,0)},decrypt:function(a){return y(this,a,1)},k:[[[],[],[],[],[]],[[],[],[],[],[]]],D:function(){var a=this.k[0],b=this.k[1],c=a[4],d=b[4],e,f,g,h=[],l=[],k,n,m,p;for(e=0;0x100>e;e++)l[(h[e]=e<<1^283*(e>>7))^e]=e;for(f=g=0;!c[f];f^=k||1,g=l[g]||1){m=g^g<<1^g<<2^g<<3^g<<4;m=m>>8^m&255^99;c[f]=m;d[m]=f;n=h[e=h[k=h[f]]];p=0x1010101*n^0x10001*e^0x101*k^0x1010100*f;n=0x101*h[m]^0x1010100*m;for(e=0;4>e;e++)a[e][f]=n=n<<24^n>>>8,b[e][m]=p=p<<24^p>>>8}for(e=
0;5>e;e++)a[e]=a[e].slice(0),b[e]=b[e].slice(0)}};
function y(a,b,c){4!==b.length&&q(new sjcl.exception.invalid("invalid aes block size"));var d=a.a[c],e=b[0]^d[0],f=b[c?3:1]^d[1],g=b[2]^d[2];b=b[c?1:3]^d[3];var h,l,k,n=d.length/4-2,m,p=4,s=[0,0,0,0];h=a.j[c];a=h[0];var r=h[1],v=h[2],w=h[3],x=h[4];for(m=0;m<n;m++)h=a[e>>>24]^r[f>>16&255]^v[g>>8&255]^w[b&255]^d[p],l=a[f>>>24]^r[g>>16&255]^v[b>>8&255]^w[e&255]^d[p+1],k=a[g>>>24]^r[b>>16&255]^v[e>>8&255]^w[f&255]^d[p+2],b=a[b>>>24]^r[e>>16&255]^v[f>>8&255]^w[g&255]^d[p+3],p+=4,e=h,f=l,g=k;for(m=0;4>
function y(a,b,c){4!==b.length&&q(new sjcl.exception.invalid("invalid aes block size"));var d=a.b[c],e=b[0]^d[0],f=b[c?3:1]^d[1],g=b[2]^d[2];b=b[c?1:3]^d[3];var h,l,k,n=d.length/4-2,m,p=4,s=[0,0,0,0];h=a.k[c];a=h[0];var r=h[1],v=h[2],w=h[3],x=h[4];for(m=0;m<n;m++)h=a[e>>>24]^r[f>>16&255]^v[g>>8&255]^w[b&255]^d[p],l=a[f>>>24]^r[g>>16&255]^v[b>>8&255]^w[e&255]^d[p+1],k=a[g>>>24]^r[b>>16&255]^v[e>>8&255]^w[f&255]^d[p+2],b=a[b>>>24]^r[e>>16&255]^v[f>>8&255]^w[g&255]^d[p+3],p+=4,e=h,f=l,g=k;for(m=0;4>
m;m++)s[c?3&-m:m]=x[e>>>24]<<24^x[f>>16&255]<<16^x[g>>8&255]<<8^x[b&255]^d[p++],h=e,e=f,f=g,g=b,b=h;return s}
sjcl.bitArray={bitSlice:function(a,b,c){a=sjcl.bitArray.O(a.slice(b/32),32-(b&31)).slice(1);return c===t?a:sjcl.bitArray.clamp(a,c-b)},extract:function(a,b,c){var d=Math.floor(-b-c&31);return((b+c-1^b)&-32?a[b/32|0]<<32-d^a[b/32+1|0]>>>d:a[b/32|0]>>>d)&(1<<c)-1},concat:function(a,b){if(0===a.length||0===b.length)return a.concat(b);var c=a[a.length-1],d=sjcl.bitArray.getPartial(c);return 32===d?a.concat(b):sjcl.bitArray.O(b,d,c|0,a.slice(0,a.length-1))},bitLength:function(a){var b=a.length;return 0===
sjcl.bitArray={bitSlice:function(a,b,c){a=sjcl.bitArray.P(a.slice(b/32),32-(b&31)).slice(1);return c===t?a:sjcl.bitArray.clamp(a,c-b)},extract:function(a,b,c){var d=Math.floor(-b-c&31);return((b+c-1^b)&-32?a[b/32|0]<<32-d^a[b/32+1|0]>>>d:a[b/32|0]>>>d)&(1<<c)-1},concat:function(a,b){if(0===a.length||0===b.length)return a.concat(b);var c=a[a.length-1],d=sjcl.bitArray.getPartial(c);return 32===d?a.concat(b):sjcl.bitArray.P(b,d,c|0,a.slice(0,a.length-1))},bitLength:function(a){var b=a.length;return 0===
b?0:32*(b-1)+sjcl.bitArray.getPartial(a[b-1])},clamp:function(a,b){if(32*a.length<b)return a;a=a.slice(0,Math.ceil(b/32));var c=a.length;b&=31;0<c&&b&&(a[c-1]=sjcl.bitArray.partial(b,a[c-1]&2147483648>>b-1,1));return a},partial:function(a,b,c){return 32===a?b:(c?b|0:b<<32-a)+0x10000000000*a},getPartial:function(a){return Math.round(a/0x10000000000)||32},equal:function(a,b){if(sjcl.bitArray.bitLength(a)!==sjcl.bitArray.bitLength(b))return u;var c=0,d;for(d=0;d<a.length;d++)c|=a[d]^b[d];return 0===
c},O:function(a,b,c,d){var e;e=0;for(d===t&&(d=[]);32<=b;b-=32)d.push(c),c=0;if(0===b)return d.concat(a);for(e=0;e<a.length;e++)d.push(c|a[e]>>>b),c=a[e]<<32-b;e=a.length?a[a.length-1]:0;a=sjcl.bitArray.getPartial(e);d.push(sjcl.bitArray.partial(b+a&31,32<b+a?c:d.pop(),1));return d},k:function(a,b){return[a[0]^b[0],a[1]^b[1],a[2]^b[2],a[3]^b[3]]}};
c},P:function(a,b,c,d){var e;e=0;for(d===t&&(d=[]);32<=b;b-=32)d.push(c),c=0;if(0===b)return d.concat(a);for(e=0;e<a.length;e++)d.push(c|a[e]>>>b),c=a[e]<<32-b;e=a.length?a[a.length-1]:0;a=sjcl.bitArray.getPartial(e);d.push(sjcl.bitArray.partial(b+a&31,32<b+a?c:d.pop(),1));return d},l:function(a,b){return[a[0]^b[0],a[1]^b[1],a[2]^b[2],a[3]^b[3]]},byteswapM:function(a){var b,c;for(b=0;b<a.length;++b)c=a[b],a[b]=c>>>24|c>>>8&0xff00|(c&0xff00)<<8|c<<24;return a}};
sjcl.codec.utf8String={fromBits:function(a){var b="",c=sjcl.bitArray.bitLength(a),d,e;for(d=0;d<c/8;d++)0===(d&3)&&(e=a[d/4]),b+=String.fromCharCode(e>>>24),e<<=8;return decodeURIComponent(escape(b))},toBits:function(a){a=unescape(encodeURIComponent(a));var b=[],c,d=0;for(c=0;c<a.length;c++)d=d<<8|a.charCodeAt(c),3===(c&3)&&(b.push(d),d=0);c&3&&b.push(sjcl.bitArray.partial(8*(c&3),d));return b}};
sjcl.codec.hex={fromBits:function(a){var b="",c;for(c=0;c<a.length;c++)b+=((a[c]|0)+0xf00000000000).toString(16).substr(4);return b.substr(0,sjcl.bitArray.bitLength(a)/4)},toBits:function(a){var b,c=[],d;a=a.replace(/\s|0x/g,"");d=a.length;a+="00000000";for(b=0;b<a.length;b+=8)c.push(parseInt(a.substr(b,8),16)^0);return sjcl.bitArray.clamp(c,4*d)}};
sjcl.codec.base64={I:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",fromBits:function(a,b,c){var d="",e=0,f=sjcl.codec.base64.I,g=0,h=sjcl.bitArray.bitLength(a);c&&(f=f.substr(0,62)+"-_");for(c=0;6*d.length<h;)d+=f.charAt((g^a[c]>>>e)>>>26),6>e?(g=a[c]<<6-e,e+=26,c++):(g<<=6,e-=6);for(;d.length&3&&!b;)d+="=";return d},toBits:function(a,b){a=a.replace(/\s|=/g,"");var c=[],d,e=0,f=sjcl.codec.base64.I,g=0,h;b&&(f=f.substr(0,62)+"-_");for(d=0;d<a.length;d++)h=f.indexOf(a.charAt(d)),
0>h&&q(new sjcl.exception.invalid("this isn't base64!")),26<e?(e-=26,c.push(g^h>>>e),g=h<<32-e):(e+=6,g^=h<<32-e);e&56&&c.push(sjcl.bitArray.partial(e&56,g,1));return c}};sjcl.codec.base64url={fromBits:function(a){return sjcl.codec.base64.fromBits(a,1,1)},toBits:function(a){return sjcl.codec.base64.toBits(a,1)}};sjcl.hash.sha256=function(a){this.a[0]||this.D();a?(this.q=a.q.slice(0),this.m=a.m.slice(0),this.g=a.g):this.reset()};sjcl.hash.sha256.hash=function(a){return(new sjcl.hash.sha256).update(a).finalize()};
sjcl.hash.sha256.prototype={blockSize:512,reset:function(){this.q=this.M.slice(0);this.m=[];this.g=0;return this},update:function(a){"string"===typeof a&&(a=sjcl.codec.utf8String.toBits(a));var b,c=this.m=sjcl.bitArray.concat(this.m,a);b=this.g;a=this.g=b+sjcl.bitArray.bitLength(a);for(b=512+b&-512;b<=a;b+=512)z(this,c.splice(0,16));return this},finalize:function(){var a,b=this.m,c=this.q,b=sjcl.bitArray.concat(b,[sjcl.bitArray.partial(1,1)]);for(a=b.length+2;a&15;a++)b.push(0);b.push(Math.floor(this.g/
4294967296));for(b.push(this.g|0);b.length;)z(this,b.splice(0,16));this.reset();return c},M:[],a:[],D:function(){function a(a){return 0x100000000*(a-Math.floor(a))|0}var b=0,c=2,d;a:for(;64>b;c++){for(d=2;d*d<=c;d++)if(0===c%d)continue a;8>b&&(this.M[b]=a(Math.pow(c,0.5)));this.a[b]=a(Math.pow(c,1/3));b++}}};
function z(a,b){var c,d,e,f=b.slice(0),g=a.q,h=a.a,l=g[0],k=g[1],n=g[2],m=g[3],p=g[4],s=g[5],r=g[6],v=g[7];for(c=0;64>c;c++)16>c?d=f[c]:(d=f[c+1&15],e=f[c+14&15],d=f[c&15]=(d>>>7^d>>>18^d>>>3^d<<25^d<<14)+(e>>>17^e>>>19^e>>>10^e<<15^e<<13)+f[c&15]+f[c+9&15]|0),d=d+v+(p>>>6^p>>>11^p>>>25^p<<26^p<<21^p<<7)+(r^p&(s^r))+h[c],v=r,r=s,s=p,p=m+d|0,m=n,n=k,k=l,l=d+(k&n^m&(k^n))+(k>>>2^k>>>13^k>>>22^k<<30^k<<19^k<<10)|0;g[0]=g[0]+l|0;g[1]=g[1]+k|0;g[2]=g[2]+n|0;g[3]=g[3]+m|0;g[4]=g[4]+p|0;g[5]=g[5]+s|0;g[6]=
sjcl.codec.base64={J:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",fromBits:function(a,b,c){var d="",e=0,f=sjcl.codec.base64.J,g=0,h=sjcl.bitArray.bitLength(a);c&&(f=f.substr(0,62)+"-_");for(c=0;6*d.length<h;)d+=f.charAt((g^a[c]>>>e)>>>26),6>e?(g=a[c]<<6-e,e+=26,c++):(g<<=6,e-=6);for(;d.length&3&&!b;)d+="=";return d},toBits:function(a,b){a=a.replace(/\s|=/g,"");var c=[],d,e=0,f=sjcl.codec.base64.J,g=0,h;b&&(f=f.substr(0,62)+"-_");for(d=0;d<a.length;d++)h=f.indexOf(a.charAt(d)),
0>h&&q(new sjcl.exception.invalid("this isn't base64!")),26<e?(e-=26,c.push(g^h>>>e),g=h<<32-e):(e+=6,g^=h<<32-e);e&56&&c.push(sjcl.bitArray.partial(e&56,g,1));return c}};sjcl.codec.base64url={fromBits:function(a){return sjcl.codec.base64.fromBits(a,1,1)},toBits:function(a){return sjcl.codec.base64.toBits(a,1)}};sjcl.hash.sha256=function(a){this.b[0]||this.D();a?(this.r=a.r.slice(0),this.o=a.o.slice(0),this.h=a.h):this.reset()};sjcl.hash.sha256.hash=function(a){return(new sjcl.hash.sha256).update(a).finalize()};
sjcl.hash.sha256.prototype={blockSize:512,reset:function(){this.r=this.N.slice(0);this.o=[];this.h=0;return this},update:function(a){"string"===typeof a&&(a=sjcl.codec.utf8String.toBits(a));var b,c=this.o=sjcl.bitArray.concat(this.o,a);b=this.h;a=this.h=b+sjcl.bitArray.bitLength(a);for(b=512+b&-512;b<=a;b+=512)z(this,c.splice(0,16));return this},finalize:function(){var a,b=this.o,c=this.r,b=sjcl.bitArray.concat(b,[sjcl.bitArray.partial(1,1)]);for(a=b.length+2;a&15;a++)b.push(0);b.push(Math.floor(this.h/
4294967296));for(b.push(this.h|0);b.length;)z(this,b.splice(0,16));this.reset();return c},N:[],b:[],D:function(){function a(a){return 0x100000000*(a-Math.floor(a))|0}var b=0,c=2,d;a:for(;64>b;c++){for(d=2;d*d<=c;d++)if(0===c%d)continue a;8>b&&(this.N[b]=a(Math.pow(c,0.5)));this.b[b]=a(Math.pow(c,1/3));b++}}};
function z(a,b){var c,d,e,f=b.slice(0),g=a.r,h=a.b,l=g[0],k=g[1],n=g[2],m=g[3],p=g[4],s=g[5],r=g[6],v=g[7];for(c=0;64>c;c++)16>c?d=f[c]:(d=f[c+1&15],e=f[c+14&15],d=f[c&15]=(d>>>7^d>>>18^d>>>3^d<<25^d<<14)+(e>>>17^e>>>19^e>>>10^e<<15^e<<13)+f[c&15]+f[c+9&15]|0),d=d+v+(p>>>6^p>>>11^p>>>25^p<<26^p<<21^p<<7)+(r^p&(s^r))+h[c],v=r,r=s,s=p,p=m+d|0,m=n,n=k,k=l,l=d+(k&n^m&(k^n))+(k>>>2^k>>>13^k>>>22^k<<30^k<<19^k<<10)|0;g[0]=g[0]+l|0;g[1]=g[1]+k|0;g[2]=g[2]+n|0;g[3]=g[3]+m|0;g[4]=g[4]+p|0;g[5]=g[5]+s|0;g[6]=
g[6]+r|0;g[7]=g[7]+v|0}
sjcl.mode.ccm={name:"ccm",encrypt:function(a,b,c,d,e){var f,g=b.slice(0),h=sjcl.bitArray,l=h.bitLength(c)/8,k=h.bitLength(g)/8;e=e||64;d=d||[];7>l&&q(new sjcl.exception.invalid("ccm: iv must be at least 7 bytes"));for(f=2;4>f&&k>>>8*f;f++);f<15-l&&(f=15-l);c=h.clamp(c,8*(15-f));b=sjcl.mode.ccm.K(a,b,c,d,e,f);g=sjcl.mode.ccm.n(a,g,c,b,e,f);return h.concat(g.data,g.tag)},decrypt:function(a,b,c,d,e){e=e||64;d=d||[];var f=sjcl.bitArray,g=f.bitLength(c)/8,h=f.bitLength(b),l=f.clamp(b,h-e),k=f.bitSlice(b,
h-e),h=(h-e)/8;7>g&&q(new sjcl.exception.invalid("ccm: iv must be at least 7 bytes"));for(b=2;4>b&&h>>>8*b;b++);b<15-g&&(b=15-g);c=f.clamp(c,8*(15-b));l=sjcl.mode.ccm.n(a,l,c,k,e,b);a=sjcl.mode.ccm.K(a,l.data,c,d,e,b);f.equal(l.tag,a)||q(new sjcl.exception.corrupt("ccm: tag doesn't match"));return l.data},K:function(a,b,c,d,e,f){var g=[],h=sjcl.bitArray,l=h.k;e/=8;(e%2||4>e||16<e)&&q(new sjcl.exception.invalid("ccm: invalid tag length"));(0xffffffff<d.length||0xffffffff<b.length)&&q(new sjcl.exception.bug("ccm: can't deal with 4GiB or more data"));
f=[h.partial(8,(d.length?64:0)|e-2<<2|f-1)];f=h.concat(f,c);f[3]|=h.bitLength(b)/8;f=a.encrypt(f);if(d.length){c=h.bitLength(d)/8;65279>=c?g=[h.partial(16,c)]:0xffffffff>=c&&(g=h.concat([h.partial(16,65534)],[c]));g=h.concat(g,d);for(d=0;d<g.length;d+=4)f=a.encrypt(l(f,g.slice(d,d+4).concat([0,0,0])))}for(d=0;d<b.length;d+=4)f=a.encrypt(l(f,b.slice(d,d+4).concat([0,0,0])));return h.clamp(f,8*e)},n:function(a,b,c,d,e,f){var g,h=sjcl.bitArray;g=h.k;var l=b.length,k=h.bitLength(b);c=h.concat([h.partial(8,
sjcl.mode.ccm={name:"ccm",encrypt:function(a,b,c,d,e){var f,g=b.slice(0),h=sjcl.bitArray,l=h.bitLength(c)/8,k=h.bitLength(g)/8;e=e||64;d=d||[];7>l&&q(new sjcl.exception.invalid("ccm: iv must be at least 7 bytes"));for(f=2;4>f&&k>>>8*f;f++);f<15-l&&(f=15-l);c=h.clamp(c,8*(15-f));b=sjcl.mode.ccm.L(a,b,c,d,e,f);g=sjcl.mode.ccm.p(a,g,c,b,e,f);return h.concat(g.data,g.tag)},decrypt:function(a,b,c,d,e){e=e||64;d=d||[];var f=sjcl.bitArray,g=f.bitLength(c)/8,h=f.bitLength(b),l=f.clamp(b,h-e),k=f.bitSlice(b,
h-e),h=(h-e)/8;7>g&&q(new sjcl.exception.invalid("ccm: iv must be at least 7 bytes"));for(b=2;4>b&&h>>>8*b;b++);b<15-g&&(b=15-g);c=f.clamp(c,8*(15-b));l=sjcl.mode.ccm.p(a,l,c,k,e,b);a=sjcl.mode.ccm.L(a,l.data,c,d,e,b);f.equal(l.tag,a)||q(new sjcl.exception.corrupt("ccm: tag doesn't match"));return l.data},L:function(a,b,c,d,e,f){var g=[],h=sjcl.bitArray,l=h.l;e/=8;(e%2||4>e||16<e)&&q(new sjcl.exception.invalid("ccm: invalid tag length"));(0xffffffff<d.length||0xffffffff<b.length)&&q(new sjcl.exception.bug("ccm: can't deal with 4GiB or more data"));
f=[h.partial(8,(d.length?64:0)|e-2<<2|f-1)];f=h.concat(f,c);f[3]|=h.bitLength(b)/8;f=a.encrypt(f);if(d.length){c=h.bitLength(d)/8;65279>=c?g=[h.partial(16,c)]:0xffffffff>=c&&(g=h.concat([h.partial(16,65534)],[c]));g=h.concat(g,d);for(d=0;d<g.length;d+=4)f=a.encrypt(l(f,g.slice(d,d+4).concat([0,0,0])))}for(d=0;d<b.length;d+=4)f=a.encrypt(l(f,b.slice(d,d+4).concat([0,0,0])));return h.clamp(f,8*e)},p:function(a,b,c,d,e,f){var g,h=sjcl.bitArray;g=h.l;var l=b.length,k=h.bitLength(b);c=h.concat([h.partial(8,
f-1)],c).concat([0,0,0]).slice(0,4);d=h.bitSlice(g(d,a.encrypt(c)),0,e);if(!l)return{tag:d,data:[]};for(g=0;g<l;g+=4)c[3]++,e=a.encrypt(c),b[g]^=e[0],b[g+1]^=e[1],b[g+2]^=e[2],b[g+3]^=e[3];return{tag:d,data:h.clamp(b,k)}}};
sjcl.mode.ocb2={name:"ocb2",encrypt:function(a,b,c,d,e,f){128!==sjcl.bitArray.bitLength(c)&&q(new sjcl.exception.invalid("ocb iv must be 128 bits"));var g,h=sjcl.mode.ocb2.G,l=sjcl.bitArray,k=l.k,n=[0,0,0,0];c=h(a.encrypt(c));var m,p=[];d=d||[];e=e||64;for(g=0;g+4<b.length;g+=4)m=b.slice(g,g+4),n=k(n,m),p=p.concat(k(c,a.encrypt(k(c,m)))),c=h(c);m=b.slice(g);b=l.bitLength(m);g=a.encrypt(k(c,[0,0,0,b]));m=l.clamp(k(m.concat([0,0,0]),g),b);n=k(n,k(m.concat([0,0,0]),g));n=a.encrypt(k(n,k(c,h(c))));d.length&&
(n=k(n,f?d:sjcl.mode.ocb2.pmac(a,d)));return p.concat(l.concat(m,l.clamp(n,e)))},decrypt:function(a,b,c,d,e,f){128!==sjcl.bitArray.bitLength(c)&&q(new sjcl.exception.invalid("ocb iv must be 128 bits"));e=e||64;var g=sjcl.mode.ocb2.G,h=sjcl.bitArray,l=h.k,k=[0,0,0,0],n=g(a.encrypt(c)),m,p,s=sjcl.bitArray.bitLength(b)-e,r=[];d=d||[];for(c=0;c+4<s/32;c+=4)m=l(n,a.decrypt(l(n,b.slice(c,c+4)))),k=l(k,m),r=r.concat(m),n=g(n);p=s-32*c;m=a.encrypt(l(n,[0,0,0,p]));m=l(m,h.clamp(b.slice(c),p).concat([0,0,0]));
k=l(k,m);k=a.encrypt(l(k,l(n,g(n))));d.length&&(k=l(k,f?d:sjcl.mode.ocb2.pmac(a,d)));h.equal(h.clamp(k,e),h.bitSlice(b,s))||q(new sjcl.exception.corrupt("ocb: tag doesn't match"));return r.concat(h.clamp(m,p))},pmac:function(a,b){var c,d=sjcl.mode.ocb2.G,e=sjcl.bitArray,f=e.k,g=[0,0,0,0],h=a.encrypt([0,0,0,0]),h=f(h,d(d(h)));for(c=0;c+4<b.length;c+=4)h=d(h),g=f(g,a.encrypt(f(h,b.slice(c,c+4))));c=b.slice(c);128>e.bitLength(c)&&(h=f(h,d(h)),c=e.concat(c,[-2147483648,0,0,0]));g=f(g,c);return a.encrypt(f(d(f(h,
d(h))),g))},G:function(a){return[a[0]<<1^a[1]>>>31,a[1]<<1^a[2]>>>31,a[2]<<1^a[3]>>>31,a[3]<<1^135*(a[0]>>>31)]}};
sjcl.mode.gcm={name:"gcm",encrypt:function(a,b,c,d,e){var f=b.slice(0);b=sjcl.bitArray;d=d||[];a=sjcl.mode.gcm.n(!0,a,f,d,c,e||128);return b.concat(a.data,a.tag)},decrypt:function(a,b,c,d,e){var f=b.slice(0),g=sjcl.bitArray,h=g.bitLength(f);e=e||128;d=d||[];e<=h?(b=g.bitSlice(f,h-e),f=g.bitSlice(f,0,h-e)):(b=f,f=[]);a=sjcl.mode.gcm.n(u,a,f,d,c,e);g.equal(a.tag,b)||q(new sjcl.exception.corrupt("gcm: tag doesn't match"));return a.data},U:function(a,b){var c,d,e,f,g,h=sjcl.bitArray.k;e=[0,0,0,0];f=b.slice(0);
for(c=0;128>c;c++){(d=0!==(a[Math.floor(c/32)]&1<<31-c%32))&&(e=h(e,f));g=0!==(f[3]&1);for(d=3;0<d;d--)f[d]=f[d]>>>1|(f[d-1]&1)<<31;f[0]>>>=1;g&&(f[0]^=-0x1f000000)}return e},f:function(a,b,c){var d,e=c.length;b=b.slice(0);for(d=0;d<e;d+=4)b[0]^=0xffffffff&c[d],b[1]^=0xffffffff&c[d+1],b[2]^=0xffffffff&c[d+2],b[3]^=0xffffffff&c[d+3],b=sjcl.mode.gcm.U(b,a);return b},n:function(a,b,c,d,e,f){var g,h,l,k,n,m,p,s,r=sjcl.bitArray;m=c.length;p=r.bitLength(c);s=r.bitLength(d);h=r.bitLength(e);g=b.encrypt([0,
0,0,0]);96===h?(e=e.slice(0),e=r.concat(e,[1])):(e=sjcl.mode.gcm.f(g,[0,0,0,0],e),e=sjcl.mode.gcm.f(g,e,[0,0,Math.floor(h/0x100000000),h&0xffffffff]));h=sjcl.mode.gcm.f(g,[0,0,0,0],d);n=e.slice(0);d=h.slice(0);a||(d=sjcl.mode.gcm.f(g,h,c));for(k=0;k<m;k+=4)n[3]++,l=b.encrypt(n),c[k]^=l[0],c[k+1]^=l[1],c[k+2]^=l[2],c[k+3]^=l[3];c=r.clamp(c,p);a&&(d=sjcl.mode.gcm.f(g,h,c));a=[Math.floor(s/0x100000000),s&0xffffffff,Math.floor(p/0x100000000),p&0xffffffff];d=sjcl.mode.gcm.f(g,d,a);l=b.encrypt(e);d[0]^=l[0];
d[1]^=l[1];d[2]^=l[2];d[3]^=l[3];return{tag:r.bitSlice(d,0,f),data:c}}};sjcl.misc.hmac=function(a,b){this.L=b=b||sjcl.hash.sha256;var c=[[],[]],d,e=b.prototype.blockSize/32;this.o=[new b,new b];a.length>e&&(a=b.hash(a));for(d=0;d<e;d++)c[0][d]=a[d]^909522486,c[1][d]=a[d]^1549556828;this.o[0].update(c[0]);this.o[1].update(c[1])};sjcl.misc.hmac.prototype.encrypt=sjcl.misc.hmac.prototype.mac=function(a){a=(new this.L(this.o[0])).update(a).finalize();return(new this.L(this.o[1])).update(a).finalize()};
sjcl.misc.pbkdf2=function(a,b,c,d,e){c=c||1E3;(0>d||0>c)&&q(sjcl.exception.invalid("invalid params to pbkdf2"));"string"===typeof a&&(a=sjcl.codec.utf8String.toBits(a));e=e||sjcl.misc.hmac;a=new e(a);var f,g,h,l,k=[],n=sjcl.bitArray;for(l=1;32*k.length<(d||1);l++){e=f=a.encrypt(n.concat(b,[l]));for(g=1;g<c;g++){f=a.encrypt(f);for(h=0;h<f.length;h++)e[h]^=f[h]}k=k.concat(e)}d&&(k=n.clamp(k,d));return k};
sjcl.prng=function(a){this.b=[new sjcl.hash.sha256];this.h=[0];this.F=0;this.t={};this.C=0;this.J={};this.N=this.c=this.i=this.T=0;this.a=[0,0,0,0,0,0,0,0];this.e=[0,0,0,0];this.A=t;this.B=a;this.p=u;this.z={progress:{},seeded:{}};this.l=this.S=0;this.u=1;this.w=2;this.Q=0x10000;this.H=[0,48,64,96,128,192,0x100,384,512,768,1024];this.R=3E4;this.P=80};
sjcl.prng.prototype={randomWords:function(a,b){var c=[],d;d=this.isReady(b);var e;d===this.l&&q(new sjcl.exception.notReady("generator isn't seeded"));if(d&this.w){d=!(d&this.u);e=[];var f=0,g;this.N=e[0]=(new Date).valueOf()+this.R;for(g=0;16>g;g++)e.push(0x100000000*Math.random()|0);for(g=0;g<this.b.length&&!(e=e.concat(this.b[g].finalize()),f+=this.h[g],this.h[g]=0,!d&&this.F&1<<g);g++);this.F>=1<<this.b.length&&(this.b.push(new sjcl.hash.sha256),this.h.push(0));this.c-=f;f>this.i&&(this.i=f);this.F++;
this.a=sjcl.hash.sha256.hash(this.a.concat(e));this.A=new sjcl.cipher.aes(this.a);for(d=0;4>d&&!(this.e[d]=this.e[d]+1|0,this.e[d]);d++);}for(d=0;d<a;d+=4)0===(d+1)%this.Q&&A(this),e=B(this),c.push(e[0],e[1],e[2],e[3]);A(this);return c.slice(0,a)},setDefaultParanoia:function(a){this.B=a},addEntropy:function(a,b,c){c=c||"user";var d,e,f=(new Date).valueOf(),g=this.t[c],h=this.isReady(),l=0;d=this.J[c];d===t&&(d=this.J[c]=this.T++);g===t&&(g=this.t[c]=0);this.t[c]=(this.t[c]+1)%this.b.length;switch(typeof a){case "number":b===
t&&(b=1);this.b[g].update([d,this.C++,1,b,f,1,a|0]);break;case "object":c=Object.prototype.toString.call(a);if("[object Uint32Array]"===c){e=[];for(c=0;c<a.length;c++)e.push(a[c]);a=e}else{"[object Array]"!==c&&(l=1);for(c=0;c<a.length&&!l;c++)"number"!=typeof a[c]&&(l=1)}if(!l){if(b===t)for(c=b=0;c<a.length;c++)for(e=a[c];0<e;)b++,e>>>=1;this.b[g].update([d,this.C++,2,b,f,a.length].concat(a))}break;case "string":b===t&&(b=a.length);this.b[g].update([d,this.C++,3,b,f,a.length]);this.b[g].update(a);
break;default:l=1}l&&q(new sjcl.exception.bug("random: addEntropy only supports number, array of numbers or string"));this.h[g]+=b;this.c+=b;h===this.l&&(this.isReady()!==this.l&&C("seeded",Math.max(this.i,this.c)),C("progress",this.getProgress()))},isReady:function(a){a=this.H[a!==t?a:this.B];return this.i&&this.i>=a?this.h[0]>this.P&&(new Date).valueOf()>this.N?this.w|this.u:this.u:this.c>=a?this.w|this.l:this.l},getProgress:function(a){a=this.H[a?a:this.B];return this.i>=a?1:this.c>a?1:this.c/
a},startCollectors:function(){this.p||(window.addEventListener?(window.addEventListener("load",this.r,u),window.addEventListener("mousemove",this.s,u)):document.attachEvent?(document.attachEvent("onload",this.r),document.attachEvent("onmousemove",this.s)):q(new sjcl.exception.bug("can't attach event")),this.p=!0)},stopCollectors:function(){this.p&&(window.removeEventListener?(window.removeEventListener("load",this.r,u),window.removeEventListener("mousemove",this.s,u)):window.detachEvent&&(window.detachEvent("onload",
this.r),window.detachEvent("onmousemove",this.s)),this.p=u)},addEventListener:function(a,b){this.z[a][this.S++]=b},removeEventListener:function(a,b){var c,d,e=this.z[a],f=[];for(d in e)e.hasOwnProperty(d)&&e[d]===b&&f.push(d);for(c=0;c<f.length;c++)d=f[c],delete e[d]},s:function(a){sjcl.random.addEntropy([a.x||a.clientX||a.offsetX||0,a.y||a.clientY||a.offsetY||0],2,"mouse")},r:function(){sjcl.random.addEntropy((new Date).valueOf(),2,"loadtime")}};
function C(a,b){var c,d=sjcl.random.z[a],e=[];for(c in d)d.hasOwnProperty(c)&&e.push(d[c]);for(c=0;c<e.length;c++)e[c](b)}function A(a){a.a=B(a).concat(B(a));a.A=new sjcl.cipher.aes(a.a)}function B(a){for(var b=0;4>b&&!(a.e[b]=a.e[b]+1|0,a.e[b]);b++);return a.A.encrypt(a.e)}sjcl.random=new sjcl.prng(6);try{var D=new Uint32Array(32);crypto.getRandomValues(D);sjcl.random.addEntropy(D,1024,"crypto['getRandomValues']")}catch(E){}
sjcl.json={defaults:{v:1,iter:1E3,ks:128,ts:64,mode:"ccm",adata:"",cipher:"aes"},encrypt:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json,f=e.d({iv:sjcl.random.randomWords(4,0)},e.defaults),g;e.d(f,c);c=f.adata;"string"===typeof f.salt&&(f.salt=sjcl.codec.base64.toBits(f.salt));"string"===typeof f.iv&&(f.iv=sjcl.codec.base64.toBits(f.iv));(!sjcl.mode[f.mode]||!sjcl.cipher[f.cipher]||"string"===typeof a&&100>=f.iter||64!==f.ts&&96!==f.ts&&128!==f.ts||128!==f.ks&&192!==f.ks&&0x100!==f.ks||2>f.iv.length||
4<f.iv.length)&&q(new sjcl.exception.invalid("json encrypt: invalid parameters"));"string"===typeof a?(g=sjcl.misc.cachedPbkdf2(a,f),a=g.key.slice(0,f.ks/32),f.salt=g.salt):sjcl.ecc&&a instanceof sjcl.ecc.elGamal.publicKey&&(g=a.kem(),f.kemtag=g.tag,a=g.key.slice(0,f.ks/32));"string"===typeof b&&(b=sjcl.codec.utf8String.toBits(b));"string"===typeof c&&(c=sjcl.codec.utf8String.toBits(c));g=new sjcl.cipher[f.cipher](a);e.d(d,f);d.key=a;f.ct=sjcl.mode[f.mode].encrypt(g,b,f.iv,c,f.ts);return e.encode(f)},
decrypt:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json;b=e.d(e.d(e.d({},e.defaults),e.decode(b)),c,!0);var f;c=b.adata;"string"===typeof b.salt&&(b.salt=sjcl.codec.base64.toBits(b.salt));"string"===typeof b.iv&&(b.iv=sjcl.codec.base64.toBits(b.iv));(!sjcl.mode[b.mode]||!sjcl.cipher[b.cipher]||"string"===typeof a&&100>=b.iter||64!==b.ts&&96!==b.ts&&128!==b.ts||128!==b.ks&&192!==b.ks&&0x100!==b.ks||!b.iv||2>b.iv.length||4<b.iv.length)&&q(new sjcl.exception.invalid("json decrypt: invalid parameters"));
"string"===typeof a?(f=sjcl.misc.cachedPbkdf2(a,b),a=f.key.slice(0,b.ks/32),b.salt=f.salt):sjcl.ecc&&a instanceof sjcl.ecc.elGamal.secretKey&&(a=a.unkem(sjcl.codec.base64.toBits(b.kemtag)).slice(0,b.ks/32));"string"===typeof c&&(c=sjcl.codec.utf8String.toBits(c));f=new sjcl.cipher[b.cipher](a);c=sjcl.mode[b.mode].decrypt(f,b.ct,b.iv,c,b.ts);e.d(d,b);d.key=a;return sjcl.codec.utf8String.fromBits(c)},encode:function(a){var b,c="{",d="";for(b in a)if(a.hasOwnProperty(b))switch(b.match(/^[a-z0-9]+$/i)||
q(new sjcl.exception.invalid("json encode: invalid property name")),c+=d+'"'+b+'":',d=",",typeof a[b]){case "number":case "boolean":c+=a[b];break;case "string":c+='"'+escape(a[b])+'"';break;case "object":c+='"'+sjcl.codec.base64.fromBits(a[b],0)+'"';break;default:q(new sjcl.exception.bug("json encode: unsupported type"))}return c+"}"},decode:function(a){a=a.replace(/\s/g,"");a.match(/^\{.*\}$/)||q(new sjcl.exception.invalid("json decode: this isn't json!"));a=a.replace(/^\{|\}$/g,"").split(/,/);var b=
{},c,d;for(c=0;c<a.length;c++)(d=a[c].match(/^(?:(["']?)([a-z][a-z0-9]*)\1):(?:(\d+)|"([a-z0-9+\/%*_.@=\-]*)")$/i))||q(new sjcl.exception.invalid("json decode: this isn't json!")),b[d[2]]=d[3]?parseInt(d[3],10):d[2].match(/^(ct|salt|iv)$/)?sjcl.codec.base64.toBits(d[4]):unescape(d[4]);return b},d:function(a,b,c){a===t&&(a={});if(b===t)return a;for(var d in b)b.hasOwnProperty(d)&&(c&&(a[d]!==t&&a[d]!==b[d])&&q(new sjcl.exception.invalid("required parameter overridden")),a[d]=b[d]);return a},X:function(a,
b){var c={},d;for(d in a)a.hasOwnProperty(d)&&a[d]!==b[d]&&(c[d]=a[d]);return c},W:function(a,b){var c={},d;for(d=0;d<b.length;d++)a[b[d]]!==t&&(c[b[d]]=a[b[d]]);return c}};sjcl.encrypt=sjcl.json.encrypt;sjcl.decrypt=sjcl.json.decrypt;sjcl.misc.V={};
sjcl.misc.cachedPbkdf2=function(a,b){var c=sjcl.misc.V,d;b=b||{};d=b.iter||1E3;c=c[a]=c[a]||{};d=c[d]=c[d]||{firstSalt:b.salt&&b.salt.length?b.salt.slice(0):sjcl.random.randomWords(2,0)};c=b.salt===t?d.firstSalt:b.salt;d[c]=d[c]||sjcl.misc.pbkdf2(a,c,b.iter);return{key:d[c].slice(0),salt:c.slice(0)}};
sjcl.mode.ocb2={name:"ocb2",encrypt:function(a,b,c,d,e,f){128!==sjcl.bitArray.bitLength(c)&&q(new sjcl.exception.invalid("ocb iv must be 128 bits"));var g,h=sjcl.mode.ocb2.H,l=sjcl.bitArray,k=l.l,n=[0,0,0,0];c=h(a.encrypt(c));var m,p=[];d=d||[];e=e||64;for(g=0;g+4<b.length;g+=4)m=b.slice(g,g+4),n=k(n,m),p=p.concat(k(c,a.encrypt(k(c,m)))),c=h(c);m=b.slice(g);b=l.bitLength(m);g=a.encrypt(k(c,[0,0,0,b]));m=l.clamp(k(m.concat([0,0,0]),g),b);n=k(n,k(m.concat([0,0,0]),g));n=a.encrypt(k(n,k(c,h(c))));d.length&&
(n=k(n,f?d:sjcl.mode.ocb2.pmac(a,d)));return p.concat(l.concat(m,l.clamp(n,e)))},decrypt:function(a,b,c,d,e,f){128!==sjcl.bitArray.bitLength(c)&&q(new sjcl.exception.invalid("ocb iv must be 128 bits"));e=e||64;var g=sjcl.mode.ocb2.H,h=sjcl.bitArray,l=h.l,k=[0,0,0,0],n=g(a.encrypt(c)),m,p,s=sjcl.bitArray.bitLength(b)-e,r=[];d=d||[];for(c=0;c+4<s/32;c+=4)m=l(n,a.decrypt(l(n,b.slice(c,c+4)))),k=l(k,m),r=r.concat(m),n=g(n);p=s-32*c;m=a.encrypt(l(n,[0,0,0,p]));m=l(m,h.clamp(b.slice(c),p).concat([0,0,0]));
k=l(k,m);k=a.encrypt(l(k,l(n,g(n))));d.length&&(k=l(k,f?d:sjcl.mode.ocb2.pmac(a,d)));h.equal(h.clamp(k,e),h.bitSlice(b,s))||q(new sjcl.exception.corrupt("ocb: tag doesn't match"));return r.concat(h.clamp(m,p))},pmac:function(a,b){var c,d=sjcl.mode.ocb2.H,e=sjcl.bitArray,f=e.l,g=[0,0,0,0],h=a.encrypt([0,0,0,0]),h=f(h,d(d(h)));for(c=0;c+4<b.length;c+=4)h=d(h),g=f(g,a.encrypt(f(h,b.slice(c,c+4))));c=b.slice(c);128>e.bitLength(c)&&(h=f(h,d(h)),c=e.concat(c,[-2147483648,0,0,0]));g=f(g,c);return a.encrypt(f(d(f(h,
d(h))),g))},H:function(a){return[a[0]<<1^a[1]>>>31,a[1]<<1^a[2]>>>31,a[2]<<1^a[3]>>>31,a[3]<<1^135*(a[0]>>>31)]}};
sjcl.mode.gcm={name:"gcm",encrypt:function(a,b,c,d,e){var f=b.slice(0);b=sjcl.bitArray;d=d||[];a=sjcl.mode.gcm.p(!0,a,f,d,c,e||128);return b.concat(a.data,a.tag)},decrypt:function(a,b,c,d,e){var f=b.slice(0),g=sjcl.bitArray,h=g.bitLength(f);e=e||128;d=d||[];e<=h?(b=g.bitSlice(f,h-e),f=g.bitSlice(f,0,h-e)):(b=f,f=[]);a=sjcl.mode.gcm.p(u,a,f,d,c,e);g.equal(a.tag,b)||q(new sjcl.exception.corrupt("gcm: tag doesn't match"));return a.data},Z:function(a,b){var c,d,e,f,g,h=sjcl.bitArray.l;e=[0,0,0,0];f=b.slice(0);
for(c=0;128>c;c++){(d=0!==(a[Math.floor(c/32)]&1<<31-c%32))&&(e=h(e,f));g=0!==(f[3]&1);for(d=3;0<d;d--)f[d]=f[d]>>>1|(f[d-1]&1)<<31;f[0]>>>=1;g&&(f[0]^=-0x1f000000)}return e},g:function(a,b,c){var d,e=c.length;b=b.slice(0);for(d=0;d<e;d+=4)b[0]^=0xffffffff&c[d],b[1]^=0xffffffff&c[d+1],b[2]^=0xffffffff&c[d+2],b[3]^=0xffffffff&c[d+3],b=sjcl.mode.gcm.Z(b,a);return b},p:function(a,b,c,d,e,f){var g,h,l,k,n,m,p,s,r=sjcl.bitArray;m=c.length;p=r.bitLength(c);s=r.bitLength(d);h=r.bitLength(e);g=b.encrypt([0,
0,0,0]);96===h?(e=e.slice(0),e=r.concat(e,[1])):(e=sjcl.mode.gcm.g(g,[0,0,0,0],e),e=sjcl.mode.gcm.g(g,e,[0,0,Math.floor(h/0x100000000),h&0xffffffff]));h=sjcl.mode.gcm.g(g,[0,0,0,0],d);n=e.slice(0);d=h.slice(0);a||(d=sjcl.mode.gcm.g(g,h,c));for(k=0;k<m;k+=4)n[3]++,l=b.encrypt(n),c[k]^=l[0],c[k+1]^=l[1],c[k+2]^=l[2],c[k+3]^=l[3];c=r.clamp(c,p);a&&(d=sjcl.mode.gcm.g(g,h,c));a=[Math.floor(s/0x100000000),s&0xffffffff,Math.floor(p/0x100000000),p&0xffffffff];d=sjcl.mode.gcm.g(g,d,a);l=b.encrypt(e);d[0]^=l[0];
d[1]^=l[1];d[2]^=l[2];d[3]^=l[3];return{tag:r.bitSlice(d,0,f),data:c}}};sjcl.misc.hmac=function(a,b){this.M=b=b||sjcl.hash.sha256;var c=[[],[]],d,e=b.prototype.blockSize/32;this.n=[new b,new b];a.length>e&&(a=b.hash(a));for(d=0;d<e;d++)c[0][d]=a[d]^909522486,c[1][d]=a[d]^1549556828;this.n[0].update(c[0]);this.n[1].update(c[1]);this.G=new b(this.n[0])};
sjcl.misc.hmac.prototype.encrypt=sjcl.misc.hmac.prototype.mac=function(a){this.Q&&q(new sjcl.exception.invalid("encrypt on already updated hmac called!"));this.update(a);return this.digest(a)};sjcl.misc.hmac.prototype.reset=function(){this.G=new this.M(this.n[0]);this.Q=u};sjcl.misc.hmac.prototype.update=function(a){this.Q=!0;this.G.update(a)};sjcl.misc.hmac.prototype.digest=function(){var a=this.G.finalize(),a=(new this.M(this.n[1])).update(a).finalize();this.reset();return a};
sjcl.misc.pbkdf2=function(a,b,c,d,e){c=c||1E3;(0>d||0>c)&&q(sjcl.exception.invalid("invalid params to pbkdf2"));"string"===typeof a&&(a=sjcl.codec.utf8String.toBits(a));"string"===typeof b&&(b=sjcl.codec.utf8String.toBits(b));e=e||sjcl.misc.hmac;a=new e(a);var f,g,h,l,k=[],n=sjcl.bitArray;for(l=1;32*k.length<(d||1);l++){e=f=a.encrypt(n.concat(b,[l]));for(g=1;g<c;g++){f=a.encrypt(f);for(h=0;h<f.length;h++)e[h]^=f[h]}k=k.concat(e)}d&&(k=n.clamp(k,d));return k};
sjcl.prng=function(a){this.c=[new sjcl.hash.sha256];this.i=[0];this.F=0;this.s={};this.C=0;this.K={};this.O=this.d=this.j=this.W=0;this.b=[0,0,0,0,0,0,0,0];this.f=[0,0,0,0];this.A=t;this.B=a;this.q=u;this.w={progress:{},seeded:{}};this.m=this.V=0;this.t=1;this.u=2;this.S=0x10000;this.I=[0,48,64,96,128,192,0x100,384,512,768,1024];this.T=3E4;this.R=80};
sjcl.prng.prototype={randomWords:function(a,b){var c=[],d;d=this.isReady(b);var e;d===this.m&&q(new sjcl.exception.notReady("generator isn't seeded"));if(d&this.u){d=!(d&this.t);e=[];var f=0,g;this.O=e[0]=(new Date).valueOf()+this.T;for(g=0;16>g;g++)e.push(0x100000000*Math.random()|0);for(g=0;g<this.c.length&&!(e=e.concat(this.c[g].finalize()),f+=this.i[g],this.i[g]=0,!d&&this.F&1<<g);g++);this.F>=1<<this.c.length&&(this.c.push(new sjcl.hash.sha256),this.i.push(0));this.d-=f;f>this.j&&(this.j=f);this.F++;
this.b=sjcl.hash.sha256.hash(this.b.concat(e));this.A=new sjcl.cipher.aes(this.b);for(d=0;4>d&&!(this.f[d]=this.f[d]+1|0,this.f[d]);d++);}for(d=0;d<a;d+=4)0===(d+1)%this.S&&A(this),e=B(this),c.push(e[0],e[1],e[2],e[3]);A(this);return c.slice(0,a)},setDefaultParanoia:function(a,b){0===a&&"Setting paranoia=0 will ruin your security; use it only for testing"!==b&&q("Setting paranoia=0 will ruin your security; use it only for testing");this.B=a},addEntropy:function(a,b,c){c=c||"user";var d,e,f=(new Date).valueOf(),
g=this.s[c],h=this.isReady(),l=0;d=this.K[c];d===t&&(d=this.K[c]=this.W++);g===t&&(g=this.s[c]=0);this.s[c]=(this.s[c]+1)%this.c.length;switch(typeof a){case "number":b===t&&(b=1);this.c[g].update([d,this.C++,1,b,f,1,a|0]);break;case "object":c=Object.prototype.toString.call(a);if("[object Uint32Array]"===c){e=[];for(c=0;c<a.length;c++)e.push(a[c]);a=e}else{"[object Array]"!==c&&(l=1);for(c=0;c<a.length&&!l;c++)"number"!==typeof a[c]&&(l=1)}if(!l){if(b===t)for(c=b=0;c<a.length;c++)for(e=a[c];0<e;)b++,
e>>>=1;this.c[g].update([d,this.C++,2,b,f,a.length].concat(a))}break;case "string":b===t&&(b=a.length);this.c[g].update([d,this.C++,3,b,f,a.length]);this.c[g].update(a);break;default:l=1}l&&q(new sjcl.exception.bug("random: addEntropy only supports number, array of numbers or string"));this.i[g]+=b;this.d+=b;h===this.m&&(this.isReady()!==this.m&&C("seeded",Math.max(this.j,this.d)),C("progress",this.getProgress()))},isReady:function(a){a=this.I[a!==t?a:this.B];return this.j&&this.j>=a?this.i[0]>this.R&&
(new Date).valueOf()>this.O?this.u|this.t:this.t:this.d>=a?this.u|this.m:this.m},getProgress:function(a){a=this.I[a?a:this.B];return this.j>=a?1:this.d>a?1:this.d/a},startCollectors:function(){this.q||(this.a={loadTimeCollector:D(this,this.aa),mouseCollector:D(this,this.ba),keyboardCollector:D(this,this.$),accelerometerCollector:D(this,this.U)},window.addEventListener?(window.addEventListener("load",this.a.loadTimeCollector,u),window.addEventListener("mousemove",this.a.mouseCollector,u),window.addEventListener("keypress",
this.a.keyboardCollector,u),window.addEventListener("devicemotion",this.a.accelerometerCollector,u)):document.attachEvent?(document.attachEvent("onload",this.a.loadTimeCollector),document.attachEvent("onmousemove",this.a.mouseCollector),document.attachEvent("keypress",this.a.keyboardCollector)):q(new sjcl.exception.bug("can't attach event")),this.q=!0)},stopCollectors:function(){this.q&&(window.removeEventListener?(window.removeEventListener("load",this.a.loadTimeCollector,u),window.removeEventListener("mousemove",
this.a.mouseCollector,u),window.removeEventListener("keypress",this.a.keyboardCollector,u),window.removeEventListener("devicemotion",this.a.accelerometerCollector,u)):document.detachEvent&&(document.detachEvent("onload",this.a.loadTimeCollector),document.detachEvent("onmousemove",this.a.mouseCollector),document.detachEvent("keypress",this.a.keyboardCollector)),this.q=u)},addEventListener:function(a,b){this.w[a][this.V++]=b},removeEventListener:function(a,b){var c,d,e=this.w[a],f=[];for(d in e)e.hasOwnProperty(d)&&
e[d]===b&&f.push(d);for(c=0;c<f.length;c++)d=f[c],delete e[d]},$:function(){E(1)},ba:function(a){var b,c;try{b=a.x||a.clientX||a.offsetX||0,c=a.y||a.clientY||a.offsetY||0}catch(d){c=b=0}0!=b&&0!=c&&sjcl.random.addEntropy([b,c],2,"mouse");E(0)},aa:function(){E(2)},U:function(a){a=a.accelerationIncludingGravity.x||a.accelerationIncludingGravity.y||a.accelerationIncludingGravity.z;if(window.orientation){var b=window.orientation;"number"===typeof b&&sjcl.random.addEntropy(b,1,"accelerometer")}a&&sjcl.random.addEntropy(a,
2,"accelerometer");E(0)}};function C(a,b){var c,d=sjcl.random.w[a],e=[];for(c in d)d.hasOwnProperty(c)&&e.push(d[c]);for(c=0;c<e.length;c++)e[c](b)}function E(a){"undefined"!==typeof window&&window.performance&&"function"===typeof window.performance.now?sjcl.random.addEntropy(window.performance.now(),a,"loadtime"):sjcl.random.addEntropy((new Date).valueOf(),a,"loadtime")}function A(a){a.b=B(a).concat(B(a));a.A=new sjcl.cipher.aes(a.b)}
function B(a){for(var b=0;4>b&&!(a.f[b]=a.f[b]+1|0,a.f[b]);b++);return a.A.encrypt(a.f)}function D(a,b){return function(){b.apply(a,arguments)}}sjcl.random=new sjcl.prng(6);
a:try{var F,G,H,I;if(I="undefined"!==typeof module){var J;if(J=module.exports){var K;try{K=require("crypto")}catch(L){K=null}J=(G=K)&&G.randomBytes}I=J}if(I)F=G.randomBytes(128),F=new Uint32Array((new Uint8Array(F)).buffer),sjcl.random.addEntropy(F,1024,"crypto['randomBytes']");else if("undefined"!==typeof window&&"undefined"!==typeof Uint32Array){H=new Uint32Array(32);if(window.crypto&&window.crypto.getRandomValues)window.crypto.getRandomValues(H);else if(window.msCrypto&&window.msCrypto.getRandomValues)window.msCrypto.getRandomValues(H);
else break a;sjcl.random.addEntropy(H,1024,"crypto['getRandomValues']")}}catch(M){"undefined"!==typeof window&&window.console&&(console.log("There was an error collecting entropy from the browser:"),console.log(M))}
sjcl.json={defaults:{v:1,iter:1E3,ks:128,ts:64,mode:"ccm",adata:"",cipher:"aes"},Y:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json,f=e.e({iv:sjcl.random.randomWords(4,0)},e.defaults),g;e.e(f,c);c=f.adata;"string"===typeof f.salt&&(f.salt=sjcl.codec.base64.toBits(f.salt));"string"===typeof f.iv&&(f.iv=sjcl.codec.base64.toBits(f.iv));(!sjcl.mode[f.mode]||!sjcl.cipher[f.cipher]||"string"===typeof a&&100>=f.iter||64!==f.ts&&96!==f.ts&&128!==f.ts||128!==f.ks&&192!==f.ks&&0x100!==f.ks||2>f.iv.length||4<
f.iv.length)&&q(new sjcl.exception.invalid("json encrypt: invalid parameters"));"string"===typeof a?(g=sjcl.misc.cachedPbkdf2(a,f),a=g.key.slice(0,f.ks/32),f.salt=g.salt):sjcl.ecc&&a instanceof sjcl.ecc.elGamal.publicKey&&(g=a.kem(),f.kemtag=g.tag,a=g.key.slice(0,f.ks/32));"string"===typeof b&&(b=sjcl.codec.utf8String.toBits(b));"string"===typeof c&&(c=sjcl.codec.utf8String.toBits(c));g=new sjcl.cipher[f.cipher](a);e.e(d,f);d.key=a;f.ct=sjcl.mode[f.mode].encrypt(g,b,f.iv,c,f.ts);return f},encrypt:function(a,
b,c,d){var e=sjcl.json,f=e.Y.apply(e,arguments);return e.encode(f)},X:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json;b=e.e(e.e(e.e({},e.defaults),b),c,!0);var f,g;f=b.adata;"string"===typeof b.salt&&(b.salt=sjcl.codec.base64.toBits(b.salt));"string"===typeof b.iv&&(b.iv=sjcl.codec.base64.toBits(b.iv));(!sjcl.mode[b.mode]||!sjcl.cipher[b.cipher]||"string"===typeof a&&100>=b.iter||64!==b.ts&&96!==b.ts&&128!==b.ts||128!==b.ks&&192!==b.ks&&0x100!==b.ks||!b.iv||2>b.iv.length||4<b.iv.length)&&q(new sjcl.exception.invalid("json decrypt: invalid parameters"));
"string"===typeof a?(g=sjcl.misc.cachedPbkdf2(a,b),a=g.key.slice(0,b.ks/32),b.salt=g.salt):sjcl.ecc&&a instanceof sjcl.ecc.elGamal.secretKey&&(a=a.unkem(sjcl.codec.base64.toBits(b.kemtag)).slice(0,b.ks/32));"string"===typeof f&&(f=sjcl.codec.utf8String.toBits(f));g=new sjcl.cipher[b.cipher](a);f=sjcl.mode[b.mode].decrypt(g,b.ct,b.iv,f,b.ts);e.e(d,b);d.key=a;return 1===c.raw?f:sjcl.codec.utf8String.fromBits(f)},decrypt:function(a,b,c,d){var e=sjcl.json;return e.X(a,e.decode(b),c,d)},encode:function(a){var b,
c="{",d="";for(b in a)if(a.hasOwnProperty(b))switch(b.match(/^[a-z0-9]+$/i)||q(new sjcl.exception.invalid("json encode: invalid property name")),c+=d+'"'+b+'":',d=",",typeof a[b]){case "number":case "boolean":c+=a[b];break;case "string":c+='"'+escape(a[b])+'"';break;case "object":c+='"'+sjcl.codec.base64.fromBits(a[b],0)+'"';break;default:q(new sjcl.exception.bug("json encode: unsupported type"))}return c+"}"},decode:function(a){a=a.replace(/\s/g,"");a.match(/^\{.*\}$/)||q(new sjcl.exception.invalid("json decode: this isn't json!"));
a=a.replace(/^\{|\}$/g,"").split(/,/);var b={},c,d;for(c=0;c<a.length;c++)(d=a[c].match(/^(?:(["']?)([a-z][a-z0-9]*)\1):(?:(\d+)|"([a-z0-9+\/%*_.@=\-]*)")$/i))||q(new sjcl.exception.invalid("json decode: this isn't json!")),b[d[2]]=d[3]?parseInt(d[3],10):d[2].match(/^(ct|salt|iv)$/)?sjcl.codec.base64.toBits(d[4]):unescape(d[4]);return b},e:function(a,b,c){a===t&&(a={});if(b===t)return a;for(var d in b)b.hasOwnProperty(d)&&(c&&(a[d]!==t&&a[d]!==b[d])&&q(new sjcl.exception.invalid("required parameter overridden")),
a[d]=b[d]);return a},ea:function(a,b){var c={},d;for(d in a)a.hasOwnProperty(d)&&a[d]!==b[d]&&(c[d]=a[d]);return c},da:function(a,b){var c={},d;for(d=0;d<b.length;d++)a[b[d]]!==t&&(c[b[d]]=a[b[d]]);return c}};sjcl.encrypt=sjcl.json.encrypt;sjcl.decrypt=sjcl.json.decrypt;sjcl.misc.ca={};
sjcl.misc.cachedPbkdf2=function(a,b){var c=sjcl.misc.ca,d;b=b||{};d=b.iter||1E3;c=c[a]=c[a]||{};d=c[d]=c[d]||{firstSalt:b.salt&&b.salt.length?b.salt.slice(0):sjcl.random.randomWords(2,0)};c=b.salt===t?d.firstSalt:b.salt;d[c]=d[c]||sjcl.misc.pbkdf2(a,c,b.iter);return{key:d[c].slice(0),salt:c.slice(0)}};

View File

@@ -0,0 +1,115 @@
(function() {
function word2hex(w) {
return "0x" + ((w|0)+0xF00000000000).toString(16).substr(4);
}
var b0 = sjcl.bitArray.partial(1, 0);
var b1 = sjcl.bitArray.partial(1, 1);
function concatbits(s) {
var j, b, a = [];
for (j = 0; j < s.length; ++j) {
b = (s[j] == '1' ? b1 : b0);
a = sjcl.bitArray.concat(a, [b]);
}
return a;
}
new sjcl.test.TestCase("bitArray single bits", function (cb) {
if (!sjcl.bitArray) {
this.unimplemented();
cb && cb();
return;
}
this.require((b0|0) === (0x00000000|0), "bitstring '0': " + word2hex(b0));
this.require((b1|0) === (0x80000000|0), "bitstring '1': " + word2hex(b1));
cb && cb();
});
new sjcl.test.TestCase("bitArray concat small bitstrings", function (cb) {
if (!sjcl.bitArray) {
this.unimplemented();
cb && cb();
return;
}
var i, kat = sjcl.test.vector.bitArray.bits, tv, a, b, bitlen, t;
for (i=0; i<kat.length; i++) {
tv = kat[i];
a = concatbits(tv[0]);
bitlen = sjcl.bitArray.bitLength(a);
t = "bitstring '" + tv[0] + "': ";
this.require(1 === a.length, t + "array length is 1: " + a.length);
this.require(bitlen === tv[0].length, t + "length " + bitlen + " matches input length " + tv[0].length);
b = sjcl.bitArray.partial(tv[0].length, tv[1]);
this.require(a[0] === b, t + "array matches shifted number: " + word2hex(a[0]) + " == " + word2hex(b));
b = 0 | (a[0] >>> (32 - tv[0].length)); // unsigned shift, convert to signed word
this.require(b === (tv[1]|0), t + "array entry shifted is number: " + word2hex(b) + " == " + word2hex(tv[1]));
}
cb && cb();
});
new sjcl.test.TestCase("bitArray concat, slicing, shifting and clamping", function (cb) {
if (!sjcl.bitArray) {
this.unimplemented();
cb && cb();
return;
}
var i, j, kat = sjcl.test.vector.bitArray.slices, tv, a, a1, b, bitlen, t;
for (i=0; i<kat.length; i++) {
tv = kat[i];
a = [];
b = [];
bitlen = 0;
for (j=0; j<tv[0].length; j++) {
b[j] = concatbits(tv[0][j]);
a = sjcl.bitArray.concat(a, b[j]);
bitlen += tv[0][j].length;
}
// shift last array entry and set partial length on it
a1 = tv[1]; a1 = a1.slice(0, a1.length);
bitlen &= 31;
if (0 !== bitlen) a1[a1.length-1] = sjcl.bitArray.partial(bitlen, a1[a1.length-1]);
this.require(sjcl.bitArray.equal(a, a1), "concat: [" + a + "] == [" + a1 + "]");
t = 0;
for (j=0; j<tv[0].length; j++) {
bitlen = sjcl.bitArray.bitLength(b[j]);
this.require(bitlen === tv[0][j].length, "bitstring length");
a1 = sjcl.bitArray.bitSlice(a, t, t + bitlen);
this.require(sjcl.bitArray.equal(b[j], a1), "slice after concat: [" + b[j] + "] == [" + a1 + "]");
t += bitlen;
}
}
cb && cb();
});
new sjcl.test.TestCase("bitArray byteswap", function (cb) {
if (!sjcl.bitArray) {
this.unimplemented();
cb && cb();
return;
}
var i, kat = sjcl.test.vector.bitArray.byteswap, tv, a;
for (i=0; i<kat.length; i++) {
tv = kat[i];
a = tv[1];
this.require(sjcl.bitArray.equal(tv[0], sjcl.bitArray.byteswapM(a.slice(0, a.length))));
}
cb && cb();
});
})();

View File

@@ -0,0 +1,163 @@
sjcl.test.vector.bitArray = {};
// random test cases generated with ruby String#to_i(radix) and Fixnum#to_s(radix)
// bitstrings (<= 32 bits) encoding a number
sjcl.test.vector.bitArray.bits = [
[ "00" , 0|0x0 ],
[ "01" , 0|0x1 ],
[ "10" , 0|0x2 ],
[ "11" , 0|0x3 ],
[ "000" , 0|0x0 ],
[ "011" , 0|0x3 ],
[ "101" , 0|0x5 ],
[ "110" , 0|0x6 ],
[ "0100" , 0|0x4 ],
[ "0101" , 0|0x5 ],
[ "1000" , 0|0x8 ],
[ "1100" , 0|0xc ],
[ "00101" , 0|0x5 ],
[ "01010" , 0|0xa ],
[ "10011" , 0|0x13 ],
[ "11010" , 0|0x1a ],
[ "001100" , 0|0xc ],
[ "001110" , 0|0xe ],
[ "010100" , 0|0x14 ],
[ "100111" , 0|0x27 ],
[ "0001011" , 0|0xb ],
[ "0001101" , 0|0xd ],
[ "1000100" , 0|0x44 ],
[ "1101011" , 0|0x6b ],
[ "00100001" , 0|0x21 ],
[ "00100111" , 0|0x27 ],
[ "00101000" , 0|0x28 ],
[ "10101111" , 0|0xaf ],
[ "000100000" , 0|0x20 ],
[ "100100011" , 0|0x123 ],
[ "100111001" , 0|0x139 ],
[ "111010011" , 0|0x1d3 ],
[ "0001001011" , 0|0x4b ],
[ "0001100110" , 0|0x66 ],
[ "0010110111" , 0|0xb7 ],
[ "1011101111" , 0|0x2ef ],
[ "01000010110" , 0|0x216 ],
[ "01001101000" , 0|0x268 ],
[ "10111101001" , 0|0x5e9 ],
[ "11111100000" , 0|0x7e0 ],
[ "000101010001" , 0|0x151 ],
[ "010101111111" , 0|0x57f ],
[ "101010001110" , 0|0xa8e ],
[ "110101100010" , 0|0xd62 ],
[ "0010001111010" , 0|0x47a ],
[ "1000000001100" , 0|0x100c ],
[ "1100011000000" , 0|0x18c0 ],
[ "1110011000011" , 0|0x1cc3 ],
[ "00111101111110" , 0|0xf7e ],
[ "01101011001001" , 0|0x1ac9 ],
[ "10111000111101" , 0|0x2e3d ],
[ "11101010011110" , 0|0x3a9e ],
[ "010111101010110" , 0|0x2f56 ],
[ "100011010110000" , 0|0x46b0 ],
[ "110001001100100" , 0|0x6264 ],
[ "111011000100110" , 0|0x7626 ],
[ "0100101111001100" , 0|0x4bcc ],
[ "1000000001101100" , 0|0x806c ],
[ "1001000100110110" , 0|0x9136 ],
[ "1101010000100011" , 0|0xd423 ],
[ "01001001001101110" , 0|0x926e ],
[ "01111001111000010" , 0|0xf3c2 ],
[ "10011011011000011" , 0|0x136c3 ],
[ "10101011001110000" , 0|0x15670 ],
[ "010000000101000110" , 0|0x10146 ],
[ "011000100101110001" , 0|0x18971 ],
[ "101100100110110111" , 0|0x2c9b7 ],
[ "101110100100101111" , 0|0x2e92f ],
[ "0010101100101000000" , 0|0x15940 ],
[ "1011010010000101010" , 0|0x5a42a ],
[ "1011100111011011000" , 0|0x5ced8 ],
[ "1111011110011111110" , 0|0x7bcfe ],
[ "00101000011011111111" , 0|0x286ff ],
[ "01111001100011000100" , 0|0x798c4 ],
[ "11111001001110101011" , 0|0xf93ab ],
[ "11111001111001101001" , 0|0xf9e69 ],
[ "000110100000110010101" , 0|0x34195 ],
[ "011110000101101101111" , 0|0xf0b6f ],
[ "101111010011001100110" , 0|0x17a666 ],
[ "111101001011110010001" , 0|0x1e9791 ],
[ "1001111100011011100001" , 0|0x27c6e1 ],
[ "1011110101000101010110" , 0|0x2f5156 ],
[ "1100000100011110001011" , 0|0x30478b ],
[ "1100001010010110111111" , 0|0x30a5bf ],
[ "01001111001101000111101" , 0|0x279a3d ],
[ "10110011101111110000001" , 0|0x59df81 ],
[ "11000001101100110100011" , 0|0x60d9a3 ],
[ "11011000010110110010110" , 0|0x6c2d96 ],
[ "010011110100100110010100" , 0|0x4f4994 ],
[ "011101110001100111111110" , 0|0x7719fe ],
[ "011110001010011011100011" , 0|0x78a6e3 ],
[ "111100010001011101111110" , 0|0xf1177e ],
[ "0010110000010110001000010" , 0|0x582c42 ],
[ "0100100011001001101110000" , 0|0x919370 ],
[ "1000111001010110111010110" , 0|0x11cadd6 ],
[ "1111001100101000010010101" , 0|0x1e65095 ],
[ "00110010011100010101111111" , 0|0xc9c57f ],
[ "00111101011011010100111110" , 0|0xf5b53e ],
[ "01100000111011111010011100" , 0|0x183be9c ],
[ "11010001100110101111010111" , 0|0x3466bd7 ],
[ "010000001111100110000110011" , 0|0x207cc33 ],
[ "011010010000110000101011111" , 0|0x348615f ],
[ "011010110001110110001110010" , 0|0x358ec72 ],
[ "110110100001001001001110000" , 0|0x6d09270 ],
[ "0101000000000001000011100101" , 0|0x50010e5 ],
[ "0110011001001100011111111100" , 0|0x664c7fc ],
[ "1001011010000001100110111101" , 0|0x96819bd ],
[ "1011011101000000111000000010" , 0|0xb740e02 ],
[ "00100111111011011111010101101" , 0|0x4fdbead ],
[ "00110000011001110110101110010" , 0|0x60ced72 ],
[ "10111110111000010010010111101" , 0|0x17dc24bd ],
[ "11010001000001110010101000010" , 0|0x1a20e542 ],
[ "001001010001010111111101010111" , 0|0x9457f57 ],
[ "100110011101100000110111111010" , 0|0x26760dfa ],
[ "100111000100011001010011111011" , 0|0x271194fb ],
[ "111011110110101110110111010101" , 0|0x3bdaedd5 ],
[ "0111100111010100101010000111100" , 0|0x3cea543c ],
[ "1000010011010100111001110000100" , 0|0x426a7384 ],
[ "1001110000000100001011010001010" , 0|0x4e02168a ],
[ "1101000000000110110010011010100" , 0|0x680364d4 ],
[ "00000011100100001011101011100111", 0|0x390bae7 ],
[ "10011110110111111000010010010011", 0|0x9edf8493 ],
[ "11000101100000110001011010111100", 0|0xc58316bc ],
[ "11111010101110011001010001011000", 0|0xfab99458 ]
];
// concat some bitstrings into an array (the last array entry here is not "high" shifted yet)
sjcl.test.vector.bitArray.slices = [
// lengths: 17, 16, 15, 14, 13
[ ["00100010111010110", "0010000111100001", "101111111100010", "10111011110000", "1100100100001" ], [0|0x22eb10f0, 0|0xdfe2bbc3, 0|0x121] ],
[ ["00001010100110010", "0000100011110001", "111110101100100", "10110111000101", "0000100111011" ], [0|0x0a990478, 0|0xfd64b714, 0|0x13b] ],
[ ["10110101101100001", "0000110110010100", "110100011010100", "01011010010111", "0101101011011" ], [0|0xb5b086ca, 0|0x68d45a5d, 0|0x35b] ],
[ ["01010010001010110", "1110010000100111", "001010110101110", "10110111101000", "0100000010100" ], [0|0x522b7213, 0|0x95aeb7a1, 0|0x014] ],
[ ["10011001001100110", "0100100011100010", "000110011001001", "00101101101000", "0001111101100" ], [0|0x99332471, 0|0x0cc92da0, 0|0x3ec] ],
[ ["01111100000110000", "1000101001000010", "110101001111111", "01001010110110", "0111100000001" ], [0|0x7c184521, 0|0x6a7f4ad9, 0|0x701] ],
[ ["00110011010100110", "0100101101110000", "110101110100100", "00111010010110", "0110011011100" ], [0|0x335325b8, 0|0x6ba43a59, 0|0x4dc] ],
[ ["11111110011100100", "1111101001010011", "001000100110010", "01101101100001", "0001011101011" ], [0|0xfe727d29, 0|0x91326d84, 0|0x2eb] ],
[ ["00100011001001000", "1001010100000100", "010111001100111", "11011111000000", "0100111101110" ], [0|0x23244a82, 0|0x2e67df01, 0|0x1ee] ],
[ ["10111111100000010", "0111110011010001", "001000101001110", "11101010011001", "0101111101110" ], [0|0xbf813e68, 0|0x914eea65, 0|0x3ee] ],
// lengths: 17, 15, 32, 10, 20
[ ["00001111000110001", "110100011111001", "10010110001110010011011000011111", "1001101101", "01110010001111001110" ], [0|0x0f18e8f9, 0|0x9639361f, 0|0x26d723ce] ],
[ ["11011111010101100", "101010100101100", "11101101011001011011010111101011", "0010110010", "11110011110110000010" ], [0|0xdf56552c, 0|0xed65b5eb, 0|0x0b2f3d82] ],
[ ["10100100001000101", "100111001100011", "00111100101000110010000011101001", "1001110001", "11000000001111111110" ], [0|0xa422ce63, 0|0x3ca320e9, 0|0x271c03fe] ],
[ ["01110001111111010", "000101001101110", "00001100011010100100001011111001", "1011111010", "00011001110111111001" ], [0|0x71fd0a6e, 0|0x0c6a42f9, 0|0x2fa19df9] ],
[ ["01001100010011001", "000111011000100", "10100000110101110110011110001100", "0011000001", "11001101010101011100" ], [0|0x4c4c8ec4, 0|0xa0d7678c, 0|0x0c1cd55c] ],
[ ["01010010100110110", "110111110001000", "10011100001100010100000110000000", "1100111101", "00010001110100111101" ], [0|0x529b6f88, 0|0x9c314180, 0|0x33d11d3d] ],
[ ["11010100011101101", "110101011011010", "01000110000011001111101110100001", "0001001110", "01001100010010001010" ], [0|0xd476eada, 0|0x460cfba1, 0|0x04e4c48a] ],
[ ["11000001010001101", "001111011110111", "10000010100111010111011000100001", "0011000011", "11011110110000101000" ], [0|0xc1469ef7, 0|0x829d7621, 0|0x0c3dec28] ],
[ ["11000101100010100", "000110111111110", "11111011110010001100001100010000", "0000101010", "10000011010011100011" ], [0|0xc58a0dfe, 0|0xfbc8c310, 0|0x02a834e3] ],
[ ["11111111110100101", "110101000011001", "10101010010100111010100010110100", "0010011000", "10100110001000000010" ], [0|0xffd2ea19, 0|0xaa53a8b4, 0|0x098a6202] ]
];
sjcl.test.vector.bitArray.byteswap = [
[ [ 0xdab1a44e, 0x73fdc3de, 0xed6e6f00, 0x5d221b85, 0xadb987a4, 0xc20eda76, 0xa0d40d0c, 0xd1da9657, 0xd85eebf0, 0xd3f9c06d, 0xca1e39b0, 0xccd7eaeb, 0xb1dd6bd0, 0x7315a9e6, 0x77cfcac4, 0xf7914c19, 0x1bc15354, 0x935afabc, 0x3ee9d375, 0xd18a095f]
, [ 0x4ea4b1da, 0xdec3fd73, 0x006f6eed, 0x851b225d, 0xa487b9ad, 0x76da0ec2, 0x0c0dd4a0, 0x5796dad1, 0xf0eb5ed8, 0x6dc0f9d3, 0xb0391eca, 0xebead7cc, 0xd06bddb1, 0xe6a91573, 0xc4cacf77, 0x194c91f7, 0x5453c11b, 0xbcfa5a93, 0x75d3e93e, 0x5f098ad1]
]
];

View File

@@ -7,7 +7,7 @@ new sjcl.test.TestCase("ECC convenience test", function (cb) {
try {
var keys = sjcl.ecc.elGamal.generateKeys(192,0);
var ciphertext = sjcl.encrypt(keys.pub, "hello world");
var plaintext = sjcl.decrypt(keys.sec, ciphertext);

View File

@@ -5,11 +5,23 @@ new sjcl.test.TestCase("HMAC official test vectors", function (cb) {
return;
}
var i, kat = sjcl.test.vector.hmac, tv, h=sjcl.codec.hex, out;
var i, kat = sjcl.test.vector.hmac, tv, h=sjcl.codec.hex, out, data, mac;
for (i=0; i<kat.length; i++) {
tv = kat[i];
out = h.fromBits((new sjcl.misc.hmac(h.toBits(tv.key))).mac(h.toBits(tv.data)));
data = h.toBits(tv.data);
mac = new sjcl.misc.hmac(h.toBits(tv.key));
out = h.fromBits(mac.mac(data));
this.require (out.substr(0,tv.mac.length) == tv.mac, "hmac #"+i);
out = h.fromBits(mac.mac(data));
this.require (out.substr(0,tv.mac.length) == tv.mac, "hmac reset #"+i);
mac.update(sjcl.bitArray.bitSlice(data, 0, sjcl.bitArray.bitLength(data)/2));
mac.update(sjcl.bitArray.bitSlice(data, sjcl.bitArray.bitLength(data)/2));
out = h.fromBits(mac.digest());
this.require (out.substr(0,tv.mac.length) == tv.mac, "hmac reset #"+i);
}
cb && cb();
});

View File

@@ -1,4 +1,5 @@
/* Official HMAC test vectors. */
//Nilos: http://tools.ietf.org/html/draft-nystrom-smime-hmac-sha-02 for
sjcl.test.vector.hmac = [
{ key: "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
data: "4869205468657265",

View File

@@ -0,0 +1,126 @@
/*
* Asserts that an object can be encoded to an expected string.
*/
new sjcl.test.TestCase("JSON Encode Test", function (cb) {
if(!sjcl.json) {
this.unimplemented();
cb && cb();
return;
}
//Build up a standard object for encoding, this includes a nice wide variety of properties.
var obj = new Object();
obj.int = 4;
obj.nint = -5;
obj.str = 'string';
obj.iv = [ -95577995, -949876189, 1443400017, 697058741 ];
obj.truth = true;
obj.lie = false;
try {
var str = sjcl.json.encode(obj);
this.require(!(!str)); //Check for non-'falsey'
}
catch (e) {
//The standard object should encode just fine, so this is out of place. Fail.
this.fail(e);
}
cb && cb();
});
/*
* Asserts that a JSON string can be decoded to an expected object.
*/
new sjcl.test.TestCase("JSON Decode Test", function (cb) {
if(!sjcl.json) {
this.unimplemented();
cb && cb();
return;
}
var str = ''; var i;
str = '{"int":4,"nint":-5,"str":"string","iv":"/////wAAAAAAAAABAAAAAg==","truth":true,"lie":false}';
try {
var obj = sjcl.json.decode(str);
this.require(obj.int === 4);
this.require(obj.nint === -5);
this.require(obj.str === 'string');
this.require(obj.truth === true);
this.require(obj.lie === false);
for(i in obj.iv) {
this.require(obj.iv[i] == (i-1)); //Array in iv is [-1,0,1,2]
}
} catch (e) { this.fail(e); }
str = '{ "int" : 4, "nint" : -5,"str":"string", "iv": "/////wAAAAAAAAABAAAAAg==","truth": true,"lie": false }';
try {
var obj = sjcl.json.decode(str);
this.require(obj.int === 4);
this.require(obj.nint === -5);
this.require(obj.str === 'string');
this.require(obj.truth === true);
this.require(obj.lie === false);
for(i in obj.iv) {
this.require(obj.iv[i] == (i-1)); //Array in iv is [-1,0,1,2]
}
} catch (e) { this.fail(e); }
//Tests passed, return.
cb && cb();
});
/*
* Asserts that an Object can be Encoded to a string that can be decoded to an equivalent object
* as well as the converse.
*/
new sjcl.test.TestCase("JSON Commutative Test", function (cb) {
if(!sjcl.json) {
this.unimplemented();
cb && cb();
return;
}
var obj1 = new Object();
obj1.int = 4;
obj1.nint = -5;
obj1.str = 'string';
obj1.iv = [ -95577995, -949876189, 1443400017, 697058741 ];
obj1.truth = true;
obj1.lie = false;
var str1 = '';
var str2 = '';
var obj2;
try {
str1 = sjcl.json.encode(obj1);
obj2 = sjcl.json.decode(str1);
str2 = sjcl.json.encode(obj2);
}
catch (e) {
this.fail(e);
}
try {
this.require(str1 === str2);
this.require(obj1.int == obj2.int);
this.require(obj1.str == obj2.str);
this.require(obj1.lie == obj2.lie);
this.require(obj1.nint == obj2.nint);
this.require(obj1.truth == obj2.truth);
var i;
for(i in obj1.iv)
{
this.require(obj1.iv[i] == obj2.iv[i]);
}
}
catch (e) {
this.fail(e);
}
//Tests passed.
cb && cb();
});

View File

@@ -0,0 +1,16 @@
var fs = require('fs');
var vm = require('vm');
var load = function(path) {
vm.runInThisContext(fs.readFileSync(path));
};
// Assume we're run using `make test`.
// That means argv[0] is `node` and argv[1] is this file.
process.argv.slice(2).map(load);
sjcl.test.run(undefined, function(){
if(!browserUtil.allPassed) {
process.exit(1);
}
});

View File

@@ -87,7 +87,7 @@ describe('Amount', function() {
assert.strictEqual(Amount.from_human("0.8 XAU").to_human({precision:0}), '1');
});
it('to human, precision 0, precision 16', function() {
assert.strictEqual(Amount.from_human("0.0 XAU").to_human({precision:16}), '0.0');
assert.strictEqual(Amount.from_human("0.0 XAU").to_human({precision:16}), '0');
});
it('to human, precision 0, precision 8, min_precision 16', function() {
assert.strictEqual(Amount.from_human("0.0 XAU").to_human({precision:8, min_precision:16}), '0.0000000000000000');
@@ -101,6 +101,21 @@ describe('Amount', function() {
it('to human, precision 16, min_precision 6, max_sig_digits 20', function() {
assert.strictEqual(Amount.from_human("0.0 XAU").to_human({precision: 16, min_precision: 6, max_sig_digits: 20}), '0.000000');
});
it('to human rounding edge case, precision 2, 1', function() {
assert.strictEqual(Amount.from_human("0.99 XAU").to_human({precision:1}), '1.0');
});
it('to human rounding edge case, precision 2, 2', function() {
assert.strictEqual(Amount.from_human("0.99 XAU").to_human({precision:2}), '0.99');
});
it('to human rounding edge case, precision 2, 3', function() {
assert.strictEqual(Amount.from_human("0.99 XAU").to_human({precision:3}), '0.99');
});
it('to human rounding edge case, precision 2, 3 min precision 3', function() {
assert.strictEqual(Amount.from_human("0.99 XAU").to_human({precision:3, min_precision:3}), '0.990');
});
it('to human rounding edge case, precision 3, 2', function() {
assert.strictEqual(Amount.from_human("0.999 XAU").to_human({precision:2}), '1.00');
});
});
describe('from_human', function() {
it('1 XRP', function() {

View File

@@ -329,12 +329,12 @@ describe('OrderBook', function() {
});
});
it('Set funded amount - funded', function() {
it('Set funded amount - iou/xrp - funded', function() {
var remote = new Remote();
var book = remote.createOrderBook({
currency_gets: 'BTC',
currency_pays: 'XRP',
issuer_gets: 'rrrrrrrrrrrrrrrrrrrrBZbvji',
currency_gets: 'BTC'
issuer_gets: 'rrrrrrrrrrrrrrrrrrrrBZbvji'
});
var offer = {
@@ -359,7 +359,37 @@ describe('OrderBook', function() {
assert.deepEqual(offer, expected);
});
it('Set funded amount - unfunded', function() {
it('Set funded amount - iou/xrp - unfunded', function() {
var remote = new Remote();
var book = remote.createOrderBook({
currency_gets: 'BTC',
currency_pays: 'XRP',
issuer_gets: 'rrrrrrrrrrrrrrrrrrrrBZbvji'
});
var offer = {
TakerGets: {
value: '100',
currency: 'BTC',
issuer: 'rrrrrrrrrrrrrrrrrrrrBZbvji'
},
TakerPays: '123456'
};
book.setFundedAmount(offer, '99');
var expected = {
TakerGets: offer.TakerGets,
TakerPays: offer.TakerPays,
is_fully_funded: false,
taker_gets_funded: '99',
taker_pays_funded: '122221'
};
assert.deepEqual(offer, expected);
});
it('Set funded amount - xrp/iou - funded', function() {
var remote = new Remote();
var book = remote.createOrderBook({
currency_gets: 'XRP',
@@ -370,7 +400,37 @@ describe('OrderBook', function() {
var offer = {
TakerGets: '100',
TakerPays: {
value: '123456',
value: '123.456',
currency: 'BTC',
issuer: 'rrrrrrrrrrrrrrrrrrrrBZbvji'
}
};
book.setFundedAmount(offer, '100.1');
var expected = {
TakerGets: offer.TakerGets,
TakerPays: offer.TakerPays,
is_fully_funded: true,
taker_gets_funded: '100',
taker_pays_funded: '123.456'
};
assert.deepEqual(offer, expected);
});
it('Set funded amount - xrp/iou - unfunded', function() {
var remote = new Remote();
var book = remote.createOrderBook({
currency_gets: 'XRP',
issuer_pays: 'rrrrrrrrrrrrrrrrrrrrBZbvji',
currency_pays: 'BTC'
});
var offer = {
TakerGets: '100',
TakerPays: {
value: '123.456',
currency: 'BTC',
issuer: 'rrrrrrrrrrrrrrrrrrrrBZbvji'
}
@@ -383,67 +443,7 @@ describe('OrderBook', function() {
TakerPays: offer.TakerPays,
is_fully_funded: false,
taker_gets_funded: '99',
taker_pays_funded: '122221.44'
};
assert.deepEqual(offer, expected);
});
it('Set funded amount - native currency - funded', function() {
var remote = new Remote();
var book = remote.createOrderBook({
currency_gets: 'XRP',
issuer_pays: 'rrrrrrrrrrrrrrrrrrrrBZbvji',
currency_pays: 'BTC'
});
var offer = {
TakerGets: '100',
TakerPays: {
value: '100.1234',
currency: 'USD',
issuer: 'rrrrrrrrrrrrrrrrrrrrBZbvji'
}
};
book.setFundedAmount(offer, '100');
var expected = {
TakerGets: offer.TakerGets,
TakerPays: offer.TakerPays,
is_fully_funded: true,
taker_gets_funded: '100',
taker_pays_funded: '100.1234'
};
assert.deepEqual(offer, expected);
});
it('Set funded amount - native currency - unfunded', function() {
var remote = new Remote();
var book = remote.createOrderBook({
currency_gets: 'XRP',
issuer_pays: 'rrrrrrrrrrrrrrrrrrrrBZbvji',
currency_pays: 'USD'
});
var offer = {
TakerGets: {
value: '100.1234',
currency: 'USD',
issuer: 'rrrrrrrrrrrrrrrrrrrrBZbvji'
},
TakerPays: '123'
};
book.setFundedAmount(offer, '100');
var expected = {
TakerGets: offer.TakerGets,
TakerPays: offer.TakerPays,
is_fully_funded: false,
taker_gets_funded: '100',
taker_pays_funded: '122.8484050681459'
taker_pays_funded: '122.22144'
};
assert.deepEqual(offer, expected);
@@ -1495,8 +1495,6 @@ describe('OrderBook', function() {
Flags: 131072,
LedgerEntryType: 'Offer',
OwnerNode: '0000000000000000',
PreviousTxnID: '9BB337CC8B34DC8D1A3FFF468556C8BA70977C37F7436439D8DA19610F214AD1',
PreviousTxnLgrSeq: 8342933,
Sequence: 195,
TakerGets: { currency: 'BTC',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
@@ -1509,7 +1507,6 @@ describe('OrderBook', function() {
},
index: 'B6BC3B0F87976370EE11F5575593FE63AA5DC1D602830DC96F04B2D597F044BF',
owner_funds: '0.1129267125000245',
quality: '496.5',
taker_gets_funded: '0.1127013098802639',
taker_pays_funded: '55.95620035555102',
is_fully_funded: false },
@@ -1521,8 +1518,6 @@ describe('OrderBook', function() {
Flags: 131072,
LedgerEntryType: 'Offer',
OwnerNode: '0000000000000144',
PreviousTxnID: 'C8296B9CCA6DC594C7CD271C5D8FD11FEE380021A07768B25935642CDB37048A',
PreviousTxnLgrSeq: 8342469,
Sequence: 29354,
TakerGets: {
currency: 'BTC',
@@ -1536,7 +1531,6 @@ describe('OrderBook', function() {
},
index: 'A437D85DF80D250F79308F2B613CF5391C7CF8EE9099BC4E553942651CD9FA86',
owner_funds: '0.950363009783092',
quality: '498.6116758238228',
is_fully_funded: true,
taker_gets_funded: '0.2',
taker_pays_funded: '99.72233516476456'

View File

@@ -42,12 +42,12 @@ describe('Remote', function () {
it('remote server initialization - url object', function() {
var remote = new Remote({
servers: [ { host: 's-west.ripple.com', port: 443, secure: true } ],
servers: [ { host: 's-west.ripple.com', port: 443, secure: true } ]
});
assert(Array.isArray(remote._servers));
assert(remote._servers[0] instanceof Server);
assert.strictEqual(remote._servers[0]._url, 'wss://s-west.ripple.com:443');
})
});
it('remote server initialization - url object - no secure property', function() {
var remote = new Remote({
@@ -56,7 +56,7 @@ describe('Remote', function () {
assert(Array.isArray(remote._servers));
assert(remote._servers[0] instanceof Server);
assert.strictEqual(remote._servers[0]._url, 'wss://s-west.ripple.com:443');
})
});
it('remote server initialization - url object - secure: false', function() {
var remote = new Remote({
@@ -74,7 +74,7 @@ describe('Remote', function () {
assert(Array.isArray(remote._servers));
assert(remote._servers[0] instanceof Server);
assert.strictEqual(remote._servers[0]._url, 'wss://s-west.ripple.com:443');
})
});
it('remote server initialization - url object - invalid host', function() {
assert.throws(
@@ -83,7 +83,7 @@ describe('Remote', function () {
servers: [ { host: '+', port: 443, secure: true } ]
});
}, Error);
})
});
it('remote server initialization - url object - invalid port', function() {
assert.throws(
@@ -151,6 +151,34 @@ describe('Remote', function () {
);
});
it('remote server initialization - set max_fee - number', function() {
var remote = new Remote({
max_fee: 10
});
assert.strictEqual(remote.max_fee, 10);
remote = new Remote({
max_fee: 1234567890
});
assert.strictEqual(remote.max_fee, 1234567890);
});
it('remote server initialization - set max_fee - string fails, should be number', function() {
var remote = new Remote({
max_fee: '1234567890'
});
assert.strictEqual(remote.max_fee, 1e6);
});
it('remote server initialization - max_fee - default', function() {
var remote = new Remote({
max_fee: void(0)
});
assert.strictEqual(remote.max_fee, 1e6);
assert.strictEqual(remote.max_fee, 1000000);
assert.strictEqual((new Remote()).max_fee, 1e6);
});
describe('request constructors', function () {
beforeEach(function () {
callback = function () {}
@@ -489,9 +517,9 @@ describe('Remote', function () {
},
parseJson: function(json) {}
}
}
};
remote.getPendingTransactions();
})
})
})
});

View File

@@ -1,8 +1,23 @@
var utils = require('./testutils');
var assert = require('assert');
var SerializedObject = utils.load_module('serializedobject').SerializedObject;
var sjcl = require('./../src/js/ripple/utils').sjcl;
// Shortcuts
var hex = sjcl.codec.hex;
var bytes = sjcl.codec.bytes;
var utf8 = sjcl.codec.utf8String;
describe('Serialized object', function() {
function convertStringToHex(string) {
return hex.fromBits(utf8.toBits(string)).toUpperCase();
}
function convertHexToString(hexString) {
return utf8.fromBits(hex.toBits(hexString));
}
describe('#from_json(v).to_json() == v', function(){
it('outputs same as passed to from_json', function() {
var input_json = {
@@ -35,6 +50,7 @@ describe('Serialized object', function() {
assert.deepEqual(input_json, output_json);
});
});
describe('#from_json', function() {
it('understands TransactionType as a Number', function() {
var input_json = {
@@ -52,6 +68,7 @@ describe('Serialized object', function() {
assert.equal(0, input_json.TransactionType);
assert.equal("Payment", output_json.TransactionType);
});
it('understands LedgerEntryType as a Number', function() {
var input_json = {
// no, non required fields
@@ -65,6 +82,7 @@ describe('Serialized object', function() {
assert.equal(100, input_json.LedgerEntryType);
assert.equal("DirectoryNode", output_json.LedgerEntryType);
});
describe('Format validation', function() {
// Peercover actually had a problem submitting transactions without a `Fee`
// and rippled was only informing of "transaction is invalid"
@@ -80,15 +98,231 @@ describe('Serialized object', function() {
};
assert.throws (
function() {
var output_json = SerializedObject.from_json(input_json);
SerializedObject.from_json(input_json);
},
/Payment is missing fields: \["Fee"\]/
);
});
});
})
describe('Memos', function() {
var input_json;
beforeEach(function() {
input_json = {
"Flags": 2147483648,
"TransactionType": "Payment",
"Account": "rhXzSyt1q9J8uiFXpK3qSugAAPJKXLtnrF",
"Amount": "1",
"Destination": "radqi6ppXFxVhJdjzaATRBxdrPcVTf1Ung",
"Sequence": 281,
"SigningPubKey": "03D642E6457B8AB4D140E2C66EB4C484FAFB1BF267CB578EC4815FE6CD06379C51",
"Fee": "12000",
"LastLedgerSequence": 10074214,
"TxnSignature": "304402201180636F2CE215CE97A29CD302618FAE60D63EBFC8903DE17A356E857A449C430220290F4A54F9DE4AC79034C8BEA5F1F8757F7505F1A6FF04D2E19B6D62E867256B"
};
});
it('should serialize and parse - full memo, all strings text/plain ', function() {
input_json.Memos = [
{
"Memo": {
"MemoType": "test",
"MemoFormat": "text",
"MemoData": "some data"
}
}
];
var so = SerializedObject.from_json(input_json).to_json();
input_json.Memos[0].Memo.parsed_memo_type = 'test';
input_json.Memos[0].Memo.parsed_memo_format = 'text';
input_json.Memos[0].Memo.parsed_memo_data = 'some data';
input_json.Memos[0].Memo.MemoType = convertStringToHex('test');
input_json.Memos[0].Memo.MemoFormat = convertStringToHex('text');
input_json.Memos[0].Memo.MemoData = convertStringToHex('some data');
assert.deepEqual(so, input_json);
});
it('should serialize and parse - full memo, all strings, invalid MemoFormat', function() {
input_json.Memos = [
{
"Memo": {
"MemoType": "test",
"MemoFormat": "application/json",
"MemoData": "some data"
}
}
];
var so = SerializedObject.from_json(input_json).to_json();
input_json.Memos[0].Memo.parsed_memo_type = 'test';
input_json.Memos[0].Memo.parsed_memo_format = 'application/json';
input_json.Memos[0].Memo.MemoType = convertStringToHex('test');
input_json.Memos[0].Memo.MemoFormat = convertStringToHex('application/json');
input_json.Memos[0].Memo.MemoData = convertStringToHex('some data');
assert.deepEqual(so, input_json);
assert.strictEqual(input_json.Memos[0].Memo.parsed_memo_data, void(0));
});
it('should throw an error - full memo, json data, invalid MemoFormat', function() {
input_json.Memos = [
{
"Memo": {
"MemoType": "test",
"MemoFormat": "text",
"MemoData": {
"string" : "some_string",
"boolean" : true
}
}
}
];
assert.throws(function() {
SerializedObject.from_json(input_json);
}, /^Error: MemoData can only be a JSON object with a valid json MemoFormat \(Memo\) \(Memos\)/);
});
it('should serialize and parse - full memo, json data, valid MemoFormat, ignored field', function() {
input_json.Memos = [
{
"Memo": {
"MemoType": "test",
"MemoFormat": "json",
"ignored" : "ignored",
"MemoData": {
"string" : "some_string",
"boolean" : true
}
}
}
];
var so = SerializedObject.from_json(input_json).to_json();
delete input_json.Memos[0].Memo.ignored;
input_json.Memos[0].Memo.parsed_memo_type = 'test';
input_json.Memos[0].Memo.parsed_memo_format = 'json';
input_json.Memos[0].Memo.parsed_memo_data = {
"string" : "some_string",
"boolean" : true
};
input_json.Memos[0].Memo.MemoType = convertStringToHex('test');
input_json.Memos[0].Memo.MemoFormat = convertStringToHex('json');
input_json.Memos[0].Memo.MemoData = convertStringToHex(JSON.stringify(
{
"string" : "some_string",
"boolean" : true
}
));
assert.deepEqual(so, input_json);
});
it('should serialize and parse - full memo, json data, valid MemoFormat', function() {
input_json.Memos = [
{
"Memo": {
"MemoType": "test",
"MemoFormat": "json",
"MemoData": {
"string" : "some_string",
"boolean" : true
}
}
}
];
var so = SerializedObject.from_json(input_json).to_json();
input_json.Memos[0].Memo.parsed_memo_type = 'test';
input_json.Memos[0].Memo.parsed_memo_format = 'json';
input_json.Memos[0].Memo.parsed_memo_data = {
"string" : "some_string",
"boolean" : true
};
input_json.Memos[0].Memo.MemoType = convertStringToHex('test');
input_json.Memos[0].Memo.MemoFormat = convertStringToHex('json');
input_json.Memos[0].Memo.MemoData = convertStringToHex(JSON.stringify(
{
"string" : "some_string",
"boolean" : true
}
));
assert.deepEqual(so, input_json);
});
it('should serialize and parse - full memo, json data, valid MemoFormat, integer', function() {
input_json.Memos = [
{
"Memo": {
"MemoType": "test",
"MemoFormat": "json",
"MemoData": 3
}
}
];
var so = SerializedObject.from_json(input_json).to_json();
input_json.Memos[0].Memo.parsed_memo_type = 'test';
input_json.Memos[0].Memo.parsed_memo_format = 'json';
input_json.Memos[0].Memo.parsed_memo_data = 3;
input_json.Memos[0].Memo.MemoType = convertStringToHex('test');
input_json.Memos[0].Memo.MemoFormat = convertStringToHex('json');
input_json.Memos[0].Memo.MemoData = convertStringToHex(JSON.parse(3));
assert.deepEqual(so, input_json);
});
it('should throw an error - invalid Memo field', function() {
input_json.Memos = [
{
"Memo": {
"MemoType": "test",
"MemoParty": "json",
"MemoData": 3
}
}
];
assert.throws(function() {
SerializedObject.from_json(input_json);
}, /^Error: JSON contains unknown field: "MemoParty" \(Memo\) \(Memos\)/);
});
it('should serialize json with memo - match hex output', function() {
var input_json = {
Flags: 2147483648,
TransactionType: 'Payment',
Account: 'rhXzSyt1q9J8uiFXpK3qSugAAPJKXLtnrF',
Amount: '1',
Destination: 'radqi6ppXFxVhJdjzaATRBxdrPcVTf1Ung',
Memos: [
{
Memo: {
MemoType: 'image'
}
}
],
Sequence: 294,
SigningPubKey: '03D642E6457B8AB4D140E2C66EB4C484FAFB1BF267CB578EC4815FE6CD06379C51',
Fee: '12000',
LastLedgerSequence: 10404607,
TxnSignature: '304402206B53EDFA6EFCF6FE5BA76C81BABB60A3B55E9DE8A1462DEDC5F387879575E498022015AE7B59AA49E735D7F2E252802C4406CD00689BCE5057C477FE979D38D2DAC9'
};
var serializedHex = '12000022800000002400000126201B009EC2FF614000000000000001684000000000002EE0732103D642E6457B8AB4D140E2C66EB4C484FAFB1BF267CB578EC4815FE6CD06379C517446304402206B53EDFA6EFCF6FE5BA76C81BABB60A3B55E9DE8A1462DEDC5F387879575E498022015AE7B59AA49E735D7F2E252802C4406CD00689BCE5057C477FE979D38D2DAC9811426C4CFB3BD05A9AA23936F2E81634C66A9820C9483143DD06317D19C6110CAFF150AE528F58843BE2CA1F9EA7C05696D616765E1F1';
assert.strictEqual(SerializedObject.from_json(input_json).to_hex(), serializedHex);
});
});
});
});
// vim:sw=2:sts=2:ts=8:et
// vim:sw=2:sts=2:ts=8:et

View File

@@ -1006,12 +1006,6 @@ describe('Server', function() {
assert(server._isConnected());
});
it('Compute fee - transaction', function() {
var server = new Server(new Remote(), 'ws://localhost:5748');
var transaction = new Transaction();
assert.strictEqual(server._computeFee(transaction), '12');
});
it('Compute fee - fee units', function() {
var server = new Server(new Remote(), 'ws://localhost:5748');
var transaction = new Transaction();
@@ -1033,7 +1027,7 @@ describe('Server', function() {
server._load_factor = 256 * 4;
var transaction = new Transaction();
assert.strictEqual(server._computeFee(transaction), '48');
assert.strictEqual(server._computeFee(10), '48');
});
it('Compute reserve', function() {

View File

@@ -30,7 +30,7 @@ describe('ECDSA signing with recoverable public key', function(){
// signature: 'AAAAG8L/yOA3nNqK4aOiQWJmOaWvkvr3NoTk6wCdX97U3qowdgFd98UK3evWV16qO3RHgFMEnUW/Vt4+kcidqW6hMo0='
}];
var curve = sjcl.ecc.curves['c256'];
var curve = sjcl.ecc.curves['k256'];
for (var m = 0; m < messages.length; m++) {
@@ -64,7 +64,7 @@ describe('ECDSA signing with recoverable public key', function(){
// // TODO: figure out why bitcoinjs-lib and this produce different signature values
// var curve = sjcl.ecc.curves['c256'];
// var curve = sjcl.ecc.curves['k256'];
// var secret_hex = '9e623166ac44d4e75fa842f3443485b9c8380551132a8ffaa898b5c93bb18b7d';
// var secret_bn = sjcl.bn.fromBits(sjcl.codec.hex.toBits(secret_hex));
@@ -85,7 +85,7 @@ describe('ECDSA signing with recoverable public key', function(){
it('should produce an error if the hash is not given as a bitArray', function(){
var curve = sjcl.ecc.curves['c256'];
var curve = sjcl.ecc.curves['k256'];
var secret_hex = '9e623166ac44d4e75fa842f3443485b9c8380551132a8ffaa898b5c93bb18b7d';
var secret_bn = sjcl.bn.fromBits(sjcl.codec.hex.toBits(secret_hex));
var secret_key = new sjcl.ecc.ecdsa.secretKey(curve, secret_bn);
@@ -100,7 +100,7 @@ describe('ECDSA signing with recoverable public key', function(){
it('should return a bitArray', function(){
var curve = sjcl.ecc.curves['c256'];
var curve = sjcl.ecc.curves['k256'];
var secret_hex = '9e623166ac44d4e75fa842f3443485b9c8380551132a8ffaa898b5c93bb18b7d';
var secret_bn = sjcl.bn.fromBits(sjcl.codec.hex.toBits(secret_hex));
var secret_key = new sjcl.ecc.ecdsa.secretKey(curve, secret_bn);
@@ -114,7 +114,7 @@ describe('ECDSA signing with recoverable public key', function(){
it('should return a bitArray where the first word contains the recovery factor', function(){
var curve = sjcl.ecc.curves['c256'];
var curve = sjcl.ecc.curves['k256'];
var secret_hex = '9e623166ac44d4e75fa842f3443485b9c8380551132a8ffaa898b5c93bb18b7d';
var secret_bn = sjcl.bn.fromBits(sjcl.codec.hex.toBits(secret_hex));
var secret_key = new sjcl.ecc.ecdsa.secretKey(curve, secret_bn);
@@ -145,7 +145,7 @@ describe('ECDSA signing with recoverable public key', function(){
it('should produce an error if the signature given does not have the recovery factor prefix', function(){
var curve = sjcl.ecc.curves['c256'];
var curve = sjcl.ecc.curves['k256'];
var secret_hex = '9e623166ac44d4e75fa842f3443485b9c8380551132a8ffaa898b5c93bb18b7d';
var secret_bn = sjcl.bn.fromBits(sjcl.codec.hex.toBits(secret_hex));
var secret_key = new sjcl.ecc.ecdsa.secretKey(curve, secret_bn);
@@ -162,7 +162,7 @@ describe('ECDSA signing with recoverable public key', function(){
it('should produce an error if it is not given both the hash and the signature', function(){
var curve = sjcl.ecc.curves['c256'];
var curve = sjcl.ecc.curves['k256'];
var secret_hex = '9e623166ac44d4e75fa842f3443485b9c8380551132a8ffaa898b5c93bb18b7d';
var secret_bn = sjcl.bn.fromBits(sjcl.codec.hex.toBits(secret_hex));
var secret_key = new sjcl.ecc.ecdsa.secretKey(curve, secret_bn);
@@ -183,7 +183,7 @@ describe('ECDSA signing with recoverable public key', function(){
it('should produce an error if it cannot generate a valid public key from the the signature', function(){
var curve = sjcl.ecc.curves['c256'];
var curve = sjcl.ecc.curves['k256'];
var secret_hex = '9e623166ac44d4e75fa842f3443485b9c8380551132a8ffaa898b5c93bb18b7d';
var secret_bn = sjcl.bn.fromBits(sjcl.codec.hex.toBits(secret_hex));
var secret_key = new sjcl.ecc.ecdsa.secretKey(curve, secret_bn);
@@ -203,7 +203,7 @@ describe('ECDSA signing with recoverable public key', function(){
it('should return a publicKey object', function(){
var curve = sjcl.ecc.curves['c256'];
var curve = sjcl.ecc.curves['k256'];
var secret_hex = '9e623166ac44d4e75fa842f3443485b9c8380551132a8ffaa898b5c93bb18b7d';
var secret_bn = sjcl.bn.fromBits(sjcl.codec.hex.toBits(secret_hex));
var secret_key = new sjcl.ecc.ecdsa.secretKey(curve, secret_bn);
@@ -219,7 +219,7 @@ describe('ECDSA signing with recoverable public key', function(){
it('tampering with the signature should produce a different public key, if it produces a valid one at all', function(){
var curve = sjcl.ecc.curves['c256'];
var curve = sjcl.ecc.curves['k256'];
var secret_hex = '9e623166ac44d4e75fa842f3443485b9c8380551132a8ffaa898b5c93bb18b7d';
var secret_bn = sjcl.bn.fromBits(sjcl.codec.hex.toBits(secret_hex));
var secret_key = new sjcl.ecc.ecdsa.secretKey(curve, secret_bn);
@@ -238,6 +238,22 @@ describe('ECDSA signing with recoverable public key', function(){
});
it('should return a publicKey object, while using the overridden c256 curve', function(){
var curve = sjcl.ecc.curves['c256'];
var secret_hex = '9e623166ac44d4e75fa842f3443485b9c8380551132a8ffaa898b5c93bb18b7d';
var secret_bn = sjcl.bn.fromBits(sjcl.codec.hex.toBits(secret_hex));
var secret_key = new sjcl.ecc.ecdsa.secretKey(curve, secret_bn);
var random_value = 'c3aa71cecb965bbbc96083d868b4955d77adb4e02ce229fe60869f745dfcd4e4a4d0f17a15a353d7592dca1baba2824e45c8e7a8f9faad3ce2c2d3792799f27a';
var hash = sjcl.codec.hex.toBits('e865bcc63a86ef21585ac8340a7cc8590ed85175a2a718c6fb2bfb2715d13778');
var signature = secret_key.signWithRecoverablePublicKey(hash, 0, random_value);
var key = sjcl.ecc.ecdsa.publicKey.recoverFromSignature(hash, signature);
assert(key instanceof sjcl.ecc.ecdsa.publicKey);
});
});
});

View File

@@ -1,9 +1,10 @@
var utils = require('./testutils');
var assert = require('assert');
var Amount = utils.load_module('amount').Amount;
var Transaction = utils.load_module('transaction').Transaction;
var Remote = utils.load_module('remote').Remote;
var Server = utils.load_module('server').Server;
var utils = require('./testutils');
var assert = require('assert');
var Amount = utils.load_module('amount').Amount;
var Transaction = utils.load_module('transaction').Transaction;
var TransactionQueue = utils.load_module('transactionqueue').TransactionQueue;
var Remote = utils.load_module('remote').Remote;
var Server = utils.load_module('server').Server;
var transactionResult = {
engine_result: 'tesSUCCESS',
@@ -38,7 +39,7 @@ describe('Transaction', function() {
it('Success listener', function(done) {
var transaction = new Transaction();
transaction.once('cleanup', function(message) {
transaction.once('final', function(message) {
assert.deepEqual(message, transactionResult);
assert(transaction.finalized);
assert.strictEqual(transaction.state, 'validated');
@@ -51,7 +52,7 @@ describe('Transaction', function() {
it('Error listener', function(done) {
var transaction = new Transaction();
transaction.once('cleanup', function(message) {
transaction.once('final', function(message) {
assert.deepEqual(message, transactionResult);
assert(transaction.finalized);
assert.strictEqual(transaction.state, 'failed');
@@ -130,25 +131,29 @@ describe('Transaction', function() {
it('Set state', function(done) {
var transaction = new Transaction();
transaction.state = 'pending';
assert.strictEqual(transaction.state, 'unsubmitted');
var receivedEvents = 0;
var events = 2;
var events = [
'submitted',
'pending',
'validated'
];
transaction.once('state', function(state) {
assert.strictEqual(state, 'validated');
if (++receivedEvents === events) {
done();
}
transaction.on('state', function(state) {
receivedEvents++;
assert(events.indexOf(state) > -1, 'Invalid state: ' + state);
});
transaction.once('save', function() {
if (++receivedEvents === events) {
done();
}
});
transaction.setState(events[0]);
transaction.setState(events[1]);
transaction.setState(events[1]);
transaction.setState(events[2]);
transaction.setState('validated');
assert.strictEqual(receivedEvents, 3);
assert.strictEqual(transaction.state, events[2]);
done();
});
it('Finalize submission', function() {
@@ -720,47 +725,20 @@ describe('Transaction', function() {
done();
});
it('Sign transaction - with callback', function(done) {
var transaction = new Transaction();
transaction._secret = 'sh2pTicynUEG46jjR4EoexHcQEoij';
transaction.tx_json.SigningPubKey = '021FED5FD081CE5C4356431267D04C6E2167E4112C897D5E10335D4E22B4DA49ED';
transaction.tx_json.Account = 'rMWwx3Ma16HnqSd4H6saPisihX9aKpXxHJ';
transaction.tx_json.Flags = 0;
transaction.tx_json.Fee = 10;
transaction.tx_json.Sequence = 1;
transaction.tx_json.TransactionType = 'AccountSet';
transaction.sign(function() {
var signature = transaction.tx_json.TxnSignature;
assert.strictEqual(transaction.previousSigningHash, 'D1C15200CF532175F1890B6440AD223D3676140522BC11D2784E56760AE3B4FE');
assert(/^[A-Z0-9]+$/.test(signature));
done();
});
});
it('Add transaction ID', function(done) {
var transaction = new Transaction();
var saved = 0;
transaction.on('save', function() {
++saved;
});
transaction.once('save', function() {
setImmediate(function() {
assert.strictEqual(saved, 2);
assert.deepEqual(
transaction.submittedIDs,
[ 'F1C15200CF532175F1890B6440AD223D3676140522BC11D2784E56760AE3B4FE',
'D1C15200CF532175F1890B6440AD223D3676140522BC11D2784E56760AE3B4FE' ]
);
done();
});
});
transaction.addId('D1C15200CF532175F1890B6440AD223D3676140522BC11D2784E56760AE3B4FE');
transaction.addId('F1C15200CF532175F1890B6440AD223D3676140522BC11D2784E56760AE3B4FE');
transaction.addId('F1C15200CF532175F1890B6440AD223D3676140522BC11D2784E56760AE3B4FE');
assert.deepEqual(
transaction.submittedIDs,
[ 'F1C15200CF532175F1890B6440AD223D3676140522BC11D2784E56760AE3B4FE',
'D1C15200CF532175F1890B6440AD223D3676140522BC11D2784E56760AE3B4FE' ]
);
done();
});
it('Find transaction IDs in cache', function(done) {
@@ -791,9 +769,10 @@ describe('Transaction', function() {
it('Set DestinationTag', function() {
var transaction = new Transaction();
transaction.destinationTag();
transaction.destinationTag('tag');
assert.strictEqual(transaction.tx_json.DestinationTag, 'tag');
assert.strictEqual(transaction.tx_json.DestinationTag, void(0));
transaction.destinationTag(1);
assert.strictEqual(transaction.tx_json.DestinationTag, 1);
});
it('Set InvoiceID', function() {
@@ -849,6 +828,23 @@ describe('Transaction', function() {
assert.strictEqual(transaction._setMaxFee, true);
});
it('Set Fixed Fee', function() {
var transaction = new Transaction();
transaction.setFixedFee('a');
assert(!transaction._setFixedFee);
transaction.setFixedFee(-1000);
assert(!transaction._setFixedFee);
transaction.setFixedFee(NaN);
assert(!transaction._setFixedFee);
transaction.setFixedFee(1000);
assert.strictEqual(transaction._setFixedFee, true);
assert.strictEqual(transaction.tx_json.Fee, '1000');
});
it('Rewrite transaction path', function() {
var transaction = new Transaction();
@@ -872,7 +868,7 @@ describe('Transaction', function() {
}
];
assert.deepEqual(Transaction._pathRewrite(path), [
assert.deepEqual(Transaction._rewritePath(path), [
{
account: 'rP51ycDJw5ZhgvdKiRjBYZKYjsyoCcHmnY',
issuer: 'rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm',
@@ -892,7 +888,9 @@ describe('Transaction', function() {
});
it('Rewrite transaction path - invalid path', function() {
assert.strictEqual(Transaction._pathRewrite(1), void(0));
assert.throws(function() {
assert.strictEqual(Transaction._rewritePath(1), void(0));
});
});
it('Add transaction path', function() {
@@ -1001,77 +999,136 @@ describe('Transaction', function() {
it('Set SourceTag', function() {
var transaction = new Transaction();
transaction.sourceTag('tag');
assert.strictEqual(transaction.tx_json.SourceTag, 'tag');
assert.strictEqual(transaction.tx_json.SourceTag, void(0));
transaction.sourceTag(1);
assert.strictEqual(transaction.tx_json.SourceTag, 1);
});
it('Set TransferRate', function() {
var transaction = new Transaction();
assert.throws(function() {
transaction.transferRate(1);
});
assert.throws(function() {
transaction.transferRate('1');
});
transaction.transferRate(1);
assert.strictEqual(transaction.tx_json.TransferRate, void(0));
transaction.transferRate(1.5 * 1e9);
assert.strictEqual(transaction.tx_json.TransferRate, 1.5 * 1e9);
});
it('Set Flags', function(done) {
var transaction = new Transaction();
transaction.tx_json.TransactionType = 'Payment';
transaction.setFlags();
assert.strictEqual(transaction.tx_json.Flags, 0);
transaction.setFlags(1);
var transaction = new Transaction();
transaction.tx_json.TransactionType = 'Payment';
transaction.setFlags(Transaction.flags.Payment.PartialPayment);
assert.strictEqual(transaction.tx_json.Flags, 131072);
assert.strictEqual(transaction.tx_json.Flags, 1);
var transaction = new Transaction();
transaction.tx_json.TransactionType = 'Payment';
transaction.setFlags('NoRippleDirect');
assert.strictEqual(transaction.tx_json.Flags, 65536);
transaction.setFlags('PartialPayment');
assert.strictEqual(transaction.tx_json.Flags, 131073);
var transaction = new Transaction();
transaction.tx_json.TransactionType = 'Payment';
transaction.setFlags('PartialPayment', 'NoRippleDirect');
assert.strictEqual(transaction.tx_json.Flags, 196608);
var transaction = new Transaction();
transaction.tx_json.TransactionType = 'Payment';
transaction.setFlags([ 'LimitQuality', 'PartialPayment' ]);
assert.strictEqual(transaction.tx_json.Flags, 393216);
assert.strictEqual(transaction.tx_json.Flags, 524289);
var transaction = new Transaction();
transaction.tx_json.TransactionType = 'Payment';
transaction.once('error', function(err) {
assert.strictEqual(err.result, 'tejInvalidFlag');
done();
});
transaction.setFlags('test');
transaction.setFlags('asdf');
});
it('Add Memo', function() {
var transaction = new Transaction();
transaction.tx_json.TransactionType = 'Payment';
transaction.addMemo('testkey', 'testvalue');
transaction.addMemo('testkey2', 'testvalue2');
transaction.addMemo('testkey3');
transaction.addMemo(void(0), 'testvalue4');
var memoType = 'message';
var memoFormat = 'application/json';
var memoData = {
string: 'value',
bool: true,
integer: 1
};
transaction.addMemo(memoType, memoFormat, memoData);
var expected = [
{
Memo:
{
MemoType: memoType,
MemoFormat: memoFormat,
MemoData: memoData
}
}
];
assert.deepEqual(transaction.tx_json.Memos, expected);
});
it('Add Memo - by object', function() {
var transaction = new Transaction();
transaction.tx_json.TransactionType = 'Payment';
var memo = {
memoType: 'type',
memoData: 'data'
};
transaction.addMemo(memo);
var expected = [
{
Memo: {
MemoType: memo.memoType,
MemoData: memo.memoData
}
}
];
assert.deepEqual(transaction.tx_json.Memos, expected);
});
it('Add Memos', function() {
var transaction = new Transaction();
transaction.tx_json.TransactionType = 'Payment';
transaction.addMemo('testkey', void(0), 'testvalue');
transaction.addMemo('testkey2', void(0), 'testvalue2');
transaction.addMemo('testkey3', 'text/html');
transaction.addMemo(void(0), void(0), 'testvalue4');
transaction.addMemo('testkey4', 'text/html', '<html>');
var expected = [
{ Memo: {
MemoType: new Buffer('testkey').toString('hex'),
MemoData: new Buffer('testvalue').toString('hex')
MemoType: 'testkey',
MemoData: 'testvalue'
}},
{ Memo: {
MemoType: new Buffer('testkey2').toString('hex'),
MemoData: new Buffer('testvalue2').toString('hex')
MemoType: 'testkey2',
MemoData: 'testvalue2'
}},
{ Memo: {
MemoType: new Buffer('testkey3').toString('hex')
MemoType: 'testkey3',
MemoFormat: 'text/html'
}},
{ Memo: {
MemoData: new Buffer('testvalue4').toString('hex')
} }
MemoData: 'testvalue4'
}},
{ Memo: {
MemoType: 'testkey4',
MemoFormat: 'text/html',
MemoData: '<html>'
}}
];
assert.deepEqual(transaction.tx_json.Memos, expected);
@@ -1086,13 +1143,76 @@ describe('Transaction', function() {
}, /^Error: MemoType must be a string$/);
});
it('Add Memo - invalid MemoData', function() {
it('Add Memo - invalid ASCII MemoType', function() {
var transaction = new Transaction();
transaction.tx_json.TransactionType = 'Payment';
assert.throws(function() {
transaction.addMemo('key', 1);
}, /^Error: MemoData must be a string$/);
transaction.addMemo('한국어');
}, /^Error: MemoType must be valid ASCII$/);
});
it('Add Memo - invalid MemoFormat', function() {
var transaction = new Transaction();
transaction.tx_json.TransactionType = 'Payment';
assert.throws(function() {
transaction.addMemo(void(0), 1);
}, /^Error: MemoFormat must be a string$/);
});
it('Add Memo - invalid ASCII MemoFormat', function() {
var transaction = new Transaction();
transaction.tx_json.TransactionType = 'Payment';
assert.throws(function() {
transaction.addMemo(void(0), 'России');
}, /^Error: MemoFormat must be valid ASCII$/);
});
it('Add Memo - MemoData string', function() {
var transaction = new Transaction();
transaction.tx_json.TransactionType = 'Payment';
transaction.addMemo({memoData:'some_string'});
assert.deepEqual(transaction.tx_json.Memos, [
{
Memo: {
MemoData: 'some_string'
}
}
]);
});
it('Add Memo - MemoData complex object', function() {
var transaction = new Transaction();
transaction.tx_json.TransactionType = 'Payment';
var memo = {
memoData: {
string: 'string',
int: 1,
array: [
{
string: 'string'
}
],
object: {
string: 'string'
}
}
};
transaction.addMemo(memo);
assert.deepEqual(transaction.tx_json.Memos, [
{
Memo: {
MemoData: memo.memoData
}
}
]);
});
it('Construct AccountSet transaction', function() {
@@ -1269,7 +1389,7 @@ describe('Transaction', function() {
var bid = '1/USD/rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm';
var ask = '1/EUR/rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm';
assert.throws(function() {
var transaction = new Transaction().offerCreate('xrsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm', bid, ask);
new Transaction().offerCreate('xrsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm', bid, ask);
});
});
@@ -1302,13 +1422,13 @@ describe('Transaction', function() {
it('Construct SetRegularKey transaction - invalid account', function() {
assert.throws(function() {
var transaction = new Transaction().setRegularKey('xrsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm', 'r36xtKNKR43SeXnGn7kN4r4JdQzcrkqpWe');
new Transaction().setRegularKey('xrsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm', 'r36xtKNKR43SeXnGn7kN4r4JdQzcrkqpWe');
});
});
it('Construct SetRegularKey transaction - invalid regularKey', function() {
assert.throws(function() {
var transaction = new Transaction().setRegularKey('rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm', 'xr36xtKNKR43SeXnGn7kN4r4JdQzcrkqpWe');
new Transaction().setRegularKey('rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm', 'xr36xtKNKR43SeXnGn7kN4r4JdQzcrkqpWe');
});
});
@@ -1548,46 +1668,74 @@ describe('Transaction', function() {
it('Submit transaction - invalid account', function(done) {
var remote = new Remote();
var transaction = new Transaction(remote).accountSet('r36xtKNKR43SeXnGn7kN4r4JdQzcrkqpWe');
transaction.tx_json.Account += 'z';
transaction.once('error', function(err) {
assert.strictEqual(err.result, 'tejInvalidAccount');
done();
assert.throws(function() {
var transaction = new Transaction(remote).accountSet('r36xtKNKR43SeXnGn7kN4r4JdQzcrkqpWeZ');
});
transaction.submit();
done();
});
it.skip('Abort submission', function(done) {
it('Abort submission on presubmit', function(done) {
var remote = new Remote();
var transaction = new Transaction(remote).accountSet('r36xtKNKR43SeXnGn7kN4r4JdQzcrkqpWe');
var account = remote.addAccount('r36xtKNKR43SeXnGn7kN4r4JdQzcrkqpWe');
remote.setSecret('rJaT8TafQfYJqDm8aC5n3Yx5yWEL2Ery79', 'snPwFATthTkKnGjEW73q3TL4yci1Q');
var server = new Server(remote, 'wss://s1.ripple.com:443');
server._computeFee = function() { return '12'; };
server._connected = true;
remote._servers.push(server);
remote._connected = true;
remote._ledger_current_index = 1;
var transaction = new Transaction(remote).accountSet('rJaT8TafQfYJqDm8aC5n3Yx5yWEL2Ery79');
var account = remote.account('rJaT8TafQfYJqDm8aC5n3Yx5yWEL2Ery79');
account._transactionManager._nextSequence = 1;
account._transactionManager._request = function(tx) {
setTimeout(function() {
tx.emit('success', { });
}, 20);
};
transaction.once('presubmit', function() {
transaction.abort();
});
transaction.complete = function() {
return this;
};
function submitCallback(err, res) {
transaction.submit(function(err, res) {
setImmediate(function() {
assert(err);
assert.strictEqual(err.result, 'tejAbort');
done();
});
};
});
});
transaction.submit(submitCallback);
transaction.abort();
it('Get min ledger', function() {
var queue = new TransactionQueue();
// Randomized submit indexes
[
28093,
456944,
347213,
165662,
729760,
808990,
927393,
925550,
872298,
543305
]
.forEach(function(index){
var tx = new Transaction();
tx.initialSubmitIndex = index;
queue.push(tx);
});
// Pending queue sorted by submit index
var sorted = queue._queue.slice().sort(function(a, b) {
return a.initialSubmitIndex - b.initialSubmitIndex;
});
sorted.forEach(function(tx){
assert.strictEqual(queue.getMinLedger(), tx.initialSubmitIndex);
queue.remove(tx);
});
});
});
// vim:sw=2:sts=2:ts=8:et
//vim:sw=2:sts=2:ts=8:et