├── .babelrc ├── .eslintrc.js ├── .gitignore ├── .npmignore ├── .travis.yml ├── Gruntfile.js ├── LICENSE ├── README.md ├── component.json ├── dist ├── rusha.js └── rusha.min.js ├── misc ├── bench.html ├── bench.js ├── bench │ ├── cifre │ │ ├── sha1.js │ │ └── utils.js │ └── johnston.js ├── random.js ├── runuithread.html ├── single.js └── test.js ├── package.json ├── perf └── benchmark.js ├── src ├── conv.js ├── core.sjs ├── hash.js ├── index.js ├── rusha.js ├── utils.js └── worker.js ├── test ├── compat │ ├── require.js │ ├── vanilla_script.js │ └── vanilla_worker.js ├── functional │ ├── digest.js │ ├── hash.js │ └── worker.js ├── fuzz.js └── unit │ ├── conv.js │ └── utils.js └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "transform-es2015-arrow-functions", 4 | ["transform-es2015-block-scoping", { 5 | "throwIfClosureRequired": true 6 | }], 7 | "transform-es2015-block-scoped-functions", 8 | ["transform-es2015-classes", { 9 | "loose": true 10 | }], 11 | "transform-es2015-destructuring" 12 | ] 13 | } -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "parser": "babel-eslint", 3 | "extends": "eslint:recommended", 4 | "rules": { 5 | // enable additional rules 6 | "indent": ["error", 2], 7 | "linebreak-style": ["error", "unix"], 8 | "quotes": ["error", "single"], 9 | "semi": ["error", "always"], 10 | "no-var": "error", 11 | "no-cond-assign": ["error", "always"], 12 | "strict": ["error", "global"], 13 | 14 | // disable rules from base configurations 15 | "no-console": "off", 16 | "no-fallthrough": "off" 17 | }, 18 | "globals": { 19 | "ArrayBuffer": true, 20 | "DataView": true, 21 | "Int8Array": true, 22 | "Int32Array": true, 23 | "Uint8Array": true 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | examples 2 | rusha.pp.js 3 | component.json 4 | node_modules 5 | .babelrc 6 | .eslintrc.* 7 | Gruntfile.js 8 | Makefile 9 | coverage 10 | test 11 | src 12 | perf 13 | misc 14 | webpack.config.js 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | os: 2 | - linux 3 | - osx 4 | language: node_js 5 | node_js: 6 | - "6" 7 | - "8" 8 | addons: 9 | chrome: stable 10 | firefox: latest -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | 2 | const webpackConfig = require('./webpack.config'); 3 | 4 | module.exports = function (grunt) { 5 | const browsers = ['ChromeHeadless', 'FirefoxHeadless']; 6 | 7 | grunt.initConfig({ 8 | pkg: grunt.file.readJSON('package.json'), 9 | uglify: { 10 | options: { 11 | banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n', 12 | compress: false 13 | }, 14 | build: { 15 | src: 'dist/<%= pkg.name %>.js', 16 | dest: 'dist/<%= pkg.name %>.min.js' 17 | } 18 | }, 19 | karma: { 20 | options: { 21 | basePath: '', 22 | singleRun: true, 23 | logLevel: 'WARN', 24 | files: [], 25 | reporters: ['mocha'], 26 | mochaReporter: { 27 | showDiff: true 28 | }, 29 | customLaunchers: { 30 | FirefoxHeadless: { 31 | base: 'Firefox', 32 | flags: ['-headless'], 33 | }, 34 | }, 35 | browserNoActivityTimeout: 60000 36 | }, 37 | unit: { 38 | options: { 39 | frameworks: ['browserify', 'mocha', 'chai'], 40 | files: ['test/unit/*.js'], 41 | preprocessors: { 42 | 'test/unit/*.js': ['browserify'] 43 | }, 44 | browsers 45 | } 46 | }, 47 | fuzz: { 48 | options: { 49 | frameworks: ['browserify', 'mocha', 'chai'], 50 | files: ['test/fuzz.js'], 51 | preprocessors: { 52 | 'test/fuzz.js': ['browserify'] 53 | }, 54 | browsers 55 | } 56 | }, 57 | functional: { 58 | options: { 59 | frameworks: ['browserify', 'mocha', 'chai-as-promised', 'chai'], 60 | files: ['test/functional/*.js'], 61 | preprocessors: { 62 | 'test/functional/*.js': ['browserify'] 63 | }, 64 | browserify: { 65 | transform: ['brfs'] 66 | }, 67 | browsers 68 | } 69 | }, 70 | compatibilityWithVanillaScript: { 71 | options: { 72 | frameworks: ['mocha', 'chai-as-promised', 'chai'], 73 | files: [ 74 | 'test/compat/vanilla_script.js', 75 | 'dist/rusha.min.js' 76 | ], 77 | browsers 78 | } 79 | }, 80 | compatibilityWithVanillaWorker: { 81 | options: { 82 | frameworks: ['mocha', 'chai-as-promised', 'chai'], 83 | files: [ 84 | 'test/compat/vanilla_worker.js', 85 | {pattern: 'dist/rusha.min.js', included: false, served: true} 86 | ], 87 | browsers 88 | } 89 | }, 90 | compatibilityWithBrowserify: { 91 | options: { 92 | frameworks: ['mocha', 'chai-as-promised', 'chai', 'browserify'], 93 | files: [ 94 | 'test/compat/require.js', 95 | ], 96 | preprocessors: { 97 | 'test/compat/require.js': ['browserify'] 98 | }, 99 | browsers 100 | } 101 | }, 102 | compatibilityWithWebpack: { 103 | options: { 104 | frameworks: ['mocha', 'chai-as-promised', 'chai'], 105 | files: [ 106 | 'test/compat/require.js', 107 | ], 108 | preprocessors: { 109 | 'test/compat/require.js': ['webpack'] 110 | }, 111 | browsers 112 | } 113 | }, 114 | benchmark: { 115 | options: { 116 | frameworks: ['browserify', 'benchmark'], 117 | reporters: ['benchmark'], 118 | files: ['perf/benchmark.js'], 119 | preprocessors: { 120 | 'perf/benchmark.js': ['browserify'] 121 | }, 122 | browsers 123 | } 124 | } 125 | }, 126 | eslint: { 127 | target: [ 128 | 'src/*.js' 129 | ] 130 | }, 131 | webpack: { 132 | prod: webpackConfig, 133 | dev: webpackConfig 134 | } 135 | }); 136 | 137 | grunt.loadNpmTasks('grunt-eslint'); 138 | grunt.loadNpmTasks('grunt-karma'); 139 | grunt.loadNpmTasks('grunt-contrib-uglify'); 140 | grunt.loadNpmTasks('grunt-webpack'); 141 | 142 | grunt.registerTask('test', [ 143 | 'eslint', 144 | 'webpack:dev', 145 | 'uglify', 146 | 'karma:unit', 147 | 'karma:fuzz', 148 | 'karma:functional', 149 | 'karma:compatibilityWithVanillaScript', 150 | 'karma:compatibilityWithVanillaWorker', 151 | 'karma:compatibilityWithBrowserify', 152 | 'karma:compatibilityWithWebpack' 153 | ]); 154 | 155 | grunt.registerTask('test:unit', [ 156 | 'eslint', 157 | 'webpack:dev', 158 | 'uglify', 159 | 'karma:unit' 160 | ]); 161 | 162 | grunt.registerTask('benchmark', ['webpack:dev', 'uglify', 'karma:benchmark']); 163 | 164 | grunt.registerTask('build', ['eslint', 'webpack:prod', 'uglify']); 165 | }; 166 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-2014 Sam Rijs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rusha 2 | 3 | *A high-performance pure-javascript SHA1 implementation suitable for large binary data.* 4 | 5 | [![npm](https://img.shields.io/npm/v/rusha.svg)](https://www.npmjs.com/package/rusha) [![npm](https://img.shields.io/npm/dm/rusha.svg)](https://www.npmjs.com/package/rusha) [![Build Status](https://travis-ci.org/srijs/rusha.svg?branch=master)](https://travis-ci.org/srijs/rusha) 6 | 7 | ## Installing 8 | 9 | ### NPM 10 | 11 | Rusha is available via [npm](http://npmjs.org/): 12 | 13 | ``` 14 | npm install rusha 15 | ``` 16 | 17 | ### Bower 18 | 19 | Rusha is available via [bower](http://twitter.github.com/bower/): 20 | 21 | ``` 22 | bower install rusha 23 | ``` 24 | 25 | ## Usage 26 | 27 | It is highly recommended to run CPU-intensive tasks in a [Web Worker](http://developer.mozilla.org/en-US/docs/DOM/Using_web_workers). To do so, just follow the instructions on [_Using the Rusha Worker_](#using-the-rusha-worker). 28 | 29 | If you have a good reason not to use Web Workers, follow the instructions on [_Using the Rusha Hash API_](#using-the-rusha-hash-api) instead. 30 | 31 | ### Using the Rusha Worker 32 | 33 | #### Spawning workers 34 | 35 | You can create a new worker in two ways. The preferred way is using `Rusha.createWorker()`, which spawns a webworker containing the hashing logic, and returns back a `Worker` object: 36 | 37 | ```js 38 | const worker = Rusha.createWorker(); 39 | ``` 40 | 41 | If for some reason this does not work for you, you can also just point the `Worker` constructor 42 | at `rusha.js` or `rusha.min.js`, like so: 43 | 44 | ```js 45 | const worker = new Worker("dist/rusha.min.js"); 46 | ``` 47 | 48 | > _**Note**: In order to make the latter work, Rusha will by default subscribe to incoming messages 49 | when it finds itself inside a worker context. This can lead to problems when you would like to use Rusha as a library inside a web worker, but still have control over the messaging. To disable this behaviour, you can call `Rusha.disableWorkerBehaviour()` from within the worker._ 50 | 51 | #### Communicating with the worker 52 | 53 | You can send your instance of the web worker messages in the format `{id: jobid, data: dataobject}`. The worker then sends back a message in the format `{id: jobid, hash: hash}`, were jobid is the id of the job previously received and hash is the hash of the data-object you passed, be it a `Blob`, `Array`, `Buffer`, `ArrayBuffer` or `String` 54 | 55 | ### Using the Rusha Hash API 56 | 57 | The Rusha `Hash` API is inspired by the [Node.js `Hash` API](https://nodejs.org/api/crypto.html#crypto_class_hash). 58 | 59 | #### Examples 60 | 61 | ##### Simple usage 62 | 63 | ```js 64 | const hexHash = Rusha.createHash().update('I am Rusha').digest('hex'); 65 | ``` 66 | 67 | ##### Incremental usage 68 | 69 | ```js 70 | const hash = Rusha.createHash(); 71 | hash.update('I am'); 72 | hash.update(' Rusha'); 73 | const hexHash = rusha.digest('hex'); 74 | ``` 75 | 76 | #### Reference 77 | 78 | You instantiate a new Hash object by calling `Rusha.createHash()`. 79 | 80 | ##### Methods 81 | 82 | - `update(data)`: Update the hash state with the given `data`, which can be a binary `String`, `Buffer`, `Array` or `ArrayBuffer`. 83 | - `digest([encoding])`: Calculates the digest of all of the data passed to be hashed. The `encoding` can be `'hex'` or undefined. If `encoding` is provided a string will be returned; otherwise an `ArrayBuffer` is returned. 84 | 85 | > _**Note**: Due to its synchronous nature, `Hash#update` does not accept data of type `Blob`. If you need to work with `Blob`s, you can either use the [Rusha Worker](#using-the-rusha-worker), or use [`FileReader#readAsArrayBuffer`](https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsArrayBuffer) to read the contents of the `Blob`, and then invoke `Hash#update` with the `ArrayBuffer` that was returned._ 86 | 87 | ##### Properties 88 | 89 | - `state` (getter and setter): Allows getting and setting the internal hashing state. 90 | 91 | ### Using the Rusha Object (DEPRECATED) 92 | 93 | The Rusha Object API is deprecated, and is only documented here for older code bases that might still be using it. 94 | 95 | You should be using the `Hash` API instead, which is documented above. 96 | 97 | #### Examples 98 | 99 | ##### Normal usage 100 | 101 | ```js 102 | const rusha = new Rusha(); 103 | const hexHash = rusha.digest('I am Rusha'); 104 | ``` 105 | 106 | ##### Incremental usage 107 | 108 | ```js 109 | const rusha = new Rusha(); 110 | rusha.resetState(); 111 | rusha.append('I am'); 112 | rusha.append(' Rusha'); 113 | const hexHash = rusha.end(); 114 | ``` 115 | 116 | #### Reference 117 | 118 | Your instantiate a new Rusha object by doing `new Rusha()`. When created, it provides the following methods: 119 | 120 | - `digest(d)`: Create a hex digest from data of the three kinds mentioned below, or throw and error if the type is unsupported. 121 | - `digestFromString(s)`: Create a hex digest from a binary `String`. A binary string is expected to only contain characters whose charCode < 256. 122 | - `digestFromBuffer(b)`: Create a hex digest from a `Buffer` or `Array`. Both are expected to only contain elements < 256. 123 | - `digestFromArrayBuffer(a)`: Create a hex digest from an `ArrayBuffer` object. 124 | - `rawDigest(d)`: Behaves just like #digest(d), except that it returns the digest as an Int32Array of size 5. 125 | - `resetState()`: Resets the internal state of the computation. 126 | - `append(d)`: Appends a binary `String`, `Buffer`, `Array`, `ArrayBuffer` or `Blob`. 127 | - `setState(state)`: Sets the internal computation state. See: getState(). 128 | - `setState()`: Returns an object representing the internal computation state. You can pass this state to setState(). This feature is useful to resume an incremental sha. 129 | - `end()`: Finishes the computation of the sha, returning a hex digest. 130 | - `rawEnd()`: Behaves just like #end(), except that it returns the digest as an Int32Array of size 5. 131 | 132 | ## Development 133 | 134 | * Download npm dependencies with `npm install` 135 | * Make changes to the files in `src/` 136 | * Build with `npm run build` 137 | * Run tests with `npm test` 138 | 139 | ## Benchmarks 140 | 141 | Tested were my Rusha implementation, the sha1.js implementation by [P. A. Johnston](http://pajhome.org.uk/crypt/md5/sha1.html), Tim Caswell's [Cifre](http://github.com/openpeer/cifre) and the Node.JS native implementation. 142 | 143 | If you want to check the performance for yourself in your own browser, I compiled a [JSPerf Page](http://jsperf.com/rusha/13). 144 | 145 | A normalized estimation based on the best results for each implementation, smaller is better: 146 | ![rough performance graph](http://srijs.github.io/rusha/bench/unscientific01.png) 147 | 148 | Results per Implementation and Platform: 149 | ![performance chart](https://docs.google.com/spreadsheet/oimg?key=0Ag9CYh5kHpegdDB1ZG16WU1xVFgxdjRuQUVwQXRnWVE&oid=1&zx=pcatr2aits9) 150 | 151 | All tests were performed on a MacBook Air 1.7 GHz Intel Core i5 and 4 GB 1333 MHz DDR3. 152 | -------------------------------------------------------------------------------- /component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Rusha", 3 | "main": "dist/rusha.js", 4 | "ignore": [ 5 | "rusha.sweet.js", 6 | "examples/", 7 | "package.json", 8 | ".npmignore", 9 | "node_modules" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /dist/rusha.js: -------------------------------------------------------------------------------- 1 | (function webpackUniversalModuleDefinition(root, factory) { 2 | if(typeof exports === 'object' && typeof module === 'object') 3 | module.exports = factory(); 4 | else if(typeof define === 'function' && define.amd) 5 | define([], factory); 6 | else if(typeof exports === 'object') 7 | exports["Rusha"] = factory(); 8 | else 9 | root["Rusha"] = factory(); 10 | })(typeof self !== 'undefined' ? self : this, function() { 11 | return /******/ (function(modules) { // webpackBootstrap 12 | /******/ // The module cache 13 | /******/ var installedModules = {}; 14 | /******/ 15 | /******/ // The require function 16 | /******/ function __webpack_require__(moduleId) { 17 | /******/ 18 | /******/ // Check if module is in cache 19 | /******/ if(installedModules[moduleId]) { 20 | /******/ return installedModules[moduleId].exports; 21 | /******/ } 22 | /******/ // Create a new module (and put it into the cache) 23 | /******/ var module = installedModules[moduleId] = { 24 | /******/ i: moduleId, 25 | /******/ l: false, 26 | /******/ exports: {} 27 | /******/ }; 28 | /******/ 29 | /******/ // Execute the module function 30 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 31 | /******/ 32 | /******/ // Flag the module as loaded 33 | /******/ module.l = true; 34 | /******/ 35 | /******/ // Return the exports of the module 36 | /******/ return module.exports; 37 | /******/ } 38 | /******/ 39 | /******/ 40 | /******/ // expose the modules object (__webpack_modules__) 41 | /******/ __webpack_require__.m = modules; 42 | /******/ 43 | /******/ // expose the module cache 44 | /******/ __webpack_require__.c = installedModules; 45 | /******/ 46 | /******/ // define getter function for harmony exports 47 | /******/ __webpack_require__.d = function(exports, name, getter) { 48 | /******/ if(!__webpack_require__.o(exports, name)) { 49 | /******/ Object.defineProperty(exports, name, { 50 | /******/ configurable: false, 51 | /******/ enumerable: true, 52 | /******/ get: getter 53 | /******/ }); 54 | /******/ } 55 | /******/ }; 56 | /******/ 57 | /******/ // getDefaultExport function for compatibility with non-harmony modules 58 | /******/ __webpack_require__.n = function(module) { 59 | /******/ var getter = module && module.__esModule ? 60 | /******/ function getDefault() { return module['default']; } : 61 | /******/ function getModuleExports() { return module; }; 62 | /******/ __webpack_require__.d(getter, 'a', getter); 63 | /******/ return getter; 64 | /******/ }; 65 | /******/ 66 | /******/ // Object.prototype.hasOwnProperty.call 67 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 68 | /******/ 69 | /******/ // __webpack_public_path__ 70 | /******/ __webpack_require__.p = ""; 71 | /******/ 72 | /******/ // Load entry module and return exports 73 | /******/ return __webpack_require__(__webpack_require__.s = 3); 74 | /******/ }) 75 | /************************************************************************/ 76 | /******/ ([ 77 | /* 0 */ 78 | /***/ (function(module, exports, __webpack_require__) { 79 | 80 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 81 | 82 | /* eslint-env commonjs, browser */ 83 | 84 | var RushaCore = __webpack_require__(5); 85 | 86 | var _require = __webpack_require__(1), 87 | toHex = _require.toHex, 88 | ceilHeapSize = _require.ceilHeapSize; 89 | 90 | var conv = __webpack_require__(6); 91 | 92 | // Calculate the length of buffer that the sha1 routine uses 93 | // including the padding. 94 | var padlen = function (len) { 95 | for (len += 9; len % 64 > 0; len += 1) {} 96 | return len; 97 | }; 98 | 99 | var padZeroes = function (bin, len) { 100 | var h8 = new Uint8Array(bin.buffer); 101 | var om = len % 4, 102 | align = len - om; 103 | switch (om) { 104 | case 0: 105 | h8[align + 3] = 0; 106 | case 1: 107 | h8[align + 2] = 0; 108 | case 2: 109 | h8[align + 1] = 0; 110 | case 3: 111 | h8[align + 0] = 0; 112 | } 113 | for (var i = (len >> 2) + 1; i < bin.length; i++) { 114 | bin[i] = 0; 115 | } 116 | }; 117 | 118 | var padData = function (bin, chunkLen, msgLen) { 119 | bin[chunkLen >> 2] |= 0x80 << 24 - (chunkLen % 4 << 3); 120 | // To support msgLen >= 2 GiB, use a float division when computing the 121 | // high 32-bits of the big-endian message length in bits. 122 | bin[((chunkLen >> 2) + 2 & ~0x0f) + 14] = msgLen / (1 << 29) | 0; 123 | bin[((chunkLen >> 2) + 2 & ~0x0f) + 15] = msgLen << 3; 124 | }; 125 | 126 | var getRawDigest = function (heap, padMaxChunkLen) { 127 | var io = new Int32Array(heap, padMaxChunkLen + 320, 5); 128 | var out = new Int32Array(5); 129 | var arr = new DataView(out.buffer); 130 | arr.setInt32(0, io[0], false); 131 | arr.setInt32(4, io[1], false); 132 | arr.setInt32(8, io[2], false); 133 | arr.setInt32(12, io[3], false); 134 | arr.setInt32(16, io[4], false); 135 | return out; 136 | }; 137 | 138 | var Rusha = function () { 139 | function Rusha(chunkSize) { 140 | _classCallCheck(this, Rusha); 141 | 142 | chunkSize = chunkSize || 64 * 1024; 143 | if (chunkSize % 64 > 0) { 144 | throw new Error('Chunk size must be a multiple of 128 bit'); 145 | } 146 | this._offset = 0; 147 | this._maxChunkLen = chunkSize; 148 | this._padMaxChunkLen = padlen(chunkSize); 149 | // The size of the heap is the sum of: 150 | // 1. The padded input message size 151 | // 2. The extended space the algorithm needs (320 byte) 152 | // 3. The 160 bit state the algoritm uses 153 | this._heap = new ArrayBuffer(ceilHeapSize(this._padMaxChunkLen + 320 + 20)); 154 | this._h32 = new Int32Array(this._heap); 155 | this._h8 = new Int8Array(this._heap); 156 | this._core = new RushaCore({ Int32Array: Int32Array }, {}, this._heap); 157 | } 158 | 159 | Rusha.prototype._initState = function _initState(heap, padMsgLen) { 160 | this._offset = 0; 161 | var io = new Int32Array(heap, padMsgLen + 320, 5); 162 | io[0] = 1732584193; 163 | io[1] = -271733879; 164 | io[2] = -1732584194; 165 | io[3] = 271733878; 166 | io[4] = -1009589776; 167 | }; 168 | 169 | Rusha.prototype._padChunk = function _padChunk(chunkLen, msgLen) { 170 | var padChunkLen = padlen(chunkLen); 171 | var view = new Int32Array(this._heap, 0, padChunkLen >> 2); 172 | padZeroes(view, chunkLen); 173 | padData(view, chunkLen, msgLen); 174 | return padChunkLen; 175 | }; 176 | 177 | Rusha.prototype._write = function _write(data, chunkOffset, chunkLen, off) { 178 | conv(data, this._h8, this._h32, chunkOffset, chunkLen, off || 0); 179 | }; 180 | 181 | Rusha.prototype._coreCall = function _coreCall(data, chunkOffset, chunkLen, msgLen, finalize) { 182 | var padChunkLen = chunkLen; 183 | this._write(data, chunkOffset, chunkLen); 184 | if (finalize) { 185 | padChunkLen = this._padChunk(chunkLen, msgLen); 186 | } 187 | this._core.hash(padChunkLen, this._padMaxChunkLen); 188 | }; 189 | 190 | Rusha.prototype.rawDigest = function rawDigest(str) { 191 | var msgLen = str.byteLength || str.length || str.size || 0; 192 | this._initState(this._heap, this._padMaxChunkLen); 193 | var chunkOffset = 0, 194 | chunkLen = this._maxChunkLen; 195 | for (chunkOffset = 0; msgLen > chunkOffset + chunkLen; chunkOffset += chunkLen) { 196 | this._coreCall(str, chunkOffset, chunkLen, msgLen, false); 197 | } 198 | this._coreCall(str, chunkOffset, msgLen - chunkOffset, msgLen, true); 199 | return getRawDigest(this._heap, this._padMaxChunkLen); 200 | }; 201 | 202 | Rusha.prototype.digest = function digest(str) { 203 | return toHex(this.rawDigest(str).buffer); 204 | }; 205 | 206 | Rusha.prototype.digestFromString = function digestFromString(str) { 207 | return this.digest(str); 208 | }; 209 | 210 | Rusha.prototype.digestFromBuffer = function digestFromBuffer(str) { 211 | return this.digest(str); 212 | }; 213 | 214 | Rusha.prototype.digestFromArrayBuffer = function digestFromArrayBuffer(str) { 215 | return this.digest(str); 216 | }; 217 | 218 | Rusha.prototype.resetState = function resetState() { 219 | this._initState(this._heap, this._padMaxChunkLen); 220 | return this; 221 | }; 222 | 223 | Rusha.prototype.append = function append(chunk) { 224 | var chunkOffset = 0; 225 | var chunkLen = chunk.byteLength || chunk.length || chunk.size || 0; 226 | var turnOffset = this._offset % this._maxChunkLen; 227 | var inputLen = void 0; 228 | 229 | this._offset += chunkLen; 230 | while (chunkOffset < chunkLen) { 231 | inputLen = Math.min(chunkLen - chunkOffset, this._maxChunkLen - turnOffset); 232 | this._write(chunk, chunkOffset, inputLen, turnOffset); 233 | turnOffset += inputLen; 234 | chunkOffset += inputLen; 235 | if (turnOffset === this._maxChunkLen) { 236 | this._core.hash(this._maxChunkLen, this._padMaxChunkLen); 237 | turnOffset = 0; 238 | } 239 | } 240 | return this; 241 | }; 242 | 243 | Rusha.prototype.getState = function getState() { 244 | var turnOffset = this._offset % this._maxChunkLen; 245 | var heap = void 0; 246 | if (!turnOffset) { 247 | var io = new Int32Array(this._heap, this._padMaxChunkLen + 320, 5); 248 | heap = io.buffer.slice(io.byteOffset, io.byteOffset + io.byteLength); 249 | } else { 250 | heap = this._heap.slice(0); 251 | } 252 | return { 253 | offset: this._offset, 254 | heap: heap 255 | }; 256 | }; 257 | 258 | Rusha.prototype.setState = function setState(state) { 259 | this._offset = state.offset; 260 | if (state.heap.byteLength === 20) { 261 | var io = new Int32Array(this._heap, this._padMaxChunkLen + 320, 5); 262 | io.set(new Int32Array(state.heap)); 263 | } else { 264 | this._h32.set(new Int32Array(state.heap)); 265 | } 266 | return this; 267 | }; 268 | 269 | Rusha.prototype.rawEnd = function rawEnd() { 270 | var msgLen = this._offset; 271 | var chunkLen = msgLen % this._maxChunkLen; 272 | var padChunkLen = this._padChunk(chunkLen, msgLen); 273 | this._core.hash(padChunkLen, this._padMaxChunkLen); 274 | var result = getRawDigest(this._heap, this._padMaxChunkLen); 275 | this._initState(this._heap, this._padMaxChunkLen); 276 | return result; 277 | }; 278 | 279 | Rusha.prototype.end = function end() { 280 | return toHex(this.rawEnd().buffer); 281 | }; 282 | 283 | return Rusha; 284 | }(); 285 | 286 | module.exports = Rusha; 287 | module.exports._core = RushaCore; 288 | 289 | /***/ }), 290 | /* 1 */ 291 | /***/ (function(module, exports) { 292 | 293 | /* eslint-env commonjs, browser */ 294 | 295 | // 296 | // toHex 297 | // 298 | 299 | var precomputedHex = new Array(256); 300 | for (var i = 0; i < 256; i++) { 301 | precomputedHex[i] = (i < 0x10 ? '0' : '') + i.toString(16); 302 | } 303 | 304 | module.exports.toHex = function (arrayBuffer) { 305 | var binarray = new Uint8Array(arrayBuffer); 306 | var res = new Array(arrayBuffer.byteLength); 307 | for (var _i = 0; _i < res.length; _i++) { 308 | res[_i] = precomputedHex[binarray[_i]]; 309 | } 310 | return res.join(''); 311 | }; 312 | 313 | // 314 | // ceilHeapSize 315 | // 316 | 317 | module.exports.ceilHeapSize = function (v) { 318 | // The asm.js spec says: 319 | // The heap object's byteLength must be either 320 | // 2^n for n in [12, 24) or 2^24 * n for n ≥ 1. 321 | // Also, byteLengths smaller than 2^16 are deprecated. 322 | var p = 0; 323 | // If v is smaller than 2^16, the smallest possible solution 324 | // is 2^16. 325 | if (v <= 65536) return 65536; 326 | // If v < 2^24, we round up to 2^n, 327 | // otherwise we round up to 2^24 * n. 328 | if (v < 16777216) { 329 | for (p = 1; p < v; p = p << 1) {} 330 | } else { 331 | for (p = 16777216; p < v; p += 16777216) {} 332 | } 333 | return p; 334 | }; 335 | 336 | // 337 | // isDedicatedWorkerScope 338 | // 339 | 340 | module.exports.isDedicatedWorkerScope = function (self) { 341 | var isRunningInWorker = 'WorkerGlobalScope' in self && self instanceof self.WorkerGlobalScope; 342 | var isRunningInSharedWorker = 'SharedWorkerGlobalScope' in self && self instanceof self.SharedWorkerGlobalScope; 343 | var isRunningInServiceWorker = 'ServiceWorkerGlobalScope' in self && self instanceof self.ServiceWorkerGlobalScope; 344 | 345 | // Detects whether we run inside a dedicated worker or not. 346 | // 347 | // We can't just check for `DedicatedWorkerGlobalScope`, since IE11 348 | // has a bug where it only supports `WorkerGlobalScope`. 349 | // 350 | // Therefore, we consider us as running inside a dedicated worker 351 | // when we are running inside a worker, but not in a shared or service worker. 352 | // 353 | // When new types of workers are introduced, we will need to adjust this code. 354 | return isRunningInWorker && !isRunningInSharedWorker && !isRunningInServiceWorker; 355 | }; 356 | 357 | /***/ }), 358 | /* 2 */ 359 | /***/ (function(module, exports, __webpack_require__) { 360 | 361 | /* eslint-env commonjs, worker */ 362 | 363 | module.exports = function () { 364 | var Rusha = __webpack_require__(0); 365 | 366 | var hashData = function (hasher, data, cb) { 367 | try { 368 | return cb(null, hasher.digest(data)); 369 | } catch (e) { 370 | return cb(e); 371 | } 372 | }; 373 | 374 | var hashFile = function (hasher, readTotal, blockSize, file, cb) { 375 | var reader = new self.FileReader(); 376 | reader.onloadend = function onloadend() { 377 | if (reader.error) { 378 | return cb(reader.error); 379 | } 380 | var buffer = reader.result; 381 | readTotal += reader.result.byteLength; 382 | try { 383 | hasher.append(buffer); 384 | } catch (e) { 385 | cb(e); 386 | return; 387 | } 388 | if (readTotal < file.size) { 389 | hashFile(hasher, readTotal, blockSize, file, cb); 390 | } else { 391 | cb(null, hasher.end()); 392 | } 393 | }; 394 | reader.readAsArrayBuffer(file.slice(readTotal, readTotal + blockSize)); 395 | }; 396 | 397 | var workerBehaviourEnabled = true; 398 | 399 | self.onmessage = function (event) { 400 | if (!workerBehaviourEnabled) { 401 | return; 402 | } 403 | 404 | var data = event.data.data, 405 | file = event.data.file, 406 | id = event.data.id; 407 | if (typeof id === 'undefined') return; 408 | if (!file && !data) return; 409 | var blockSize = event.data.blockSize || 4 * 1024 * 1024; 410 | var hasher = new Rusha(blockSize); 411 | hasher.resetState(); 412 | var done = function (err, hash) { 413 | if (!err) { 414 | self.postMessage({ id: id, hash: hash }); 415 | } else { 416 | self.postMessage({ id: id, error: err.name }); 417 | } 418 | }; 419 | if (data) hashData(hasher, data, done); 420 | if (file) hashFile(hasher, 0, blockSize, file, done); 421 | }; 422 | 423 | return function () { 424 | workerBehaviourEnabled = false; 425 | }; 426 | }; 427 | 428 | /***/ }), 429 | /* 3 */ 430 | /***/ (function(module, exports, __webpack_require__) { 431 | 432 | /* eslint-env commonjs, browser */ 433 | 434 | var work = __webpack_require__(4); 435 | var Rusha = __webpack_require__(0); 436 | var createHash = __webpack_require__(7); 437 | var runWorker = __webpack_require__(2); 438 | 439 | var _require = __webpack_require__(1), 440 | isDedicatedWorkerScope = _require.isDedicatedWorkerScope; 441 | 442 | var isRunningInDedicatedWorker = typeof self !== 'undefined' && isDedicatedWorkerScope(self); 443 | 444 | Rusha.disableWorkerBehaviour = isRunningInDedicatedWorker ? runWorker() : function () {}; 445 | 446 | Rusha.createWorker = function () { 447 | var worker = work(/*require.resolve*/(2)); 448 | var terminate = worker.terminate; 449 | worker.terminate = function () { 450 | URL.revokeObjectURL(worker.objectURL); 451 | terminate.call(worker); 452 | }; 453 | return worker; 454 | }; 455 | 456 | Rusha.createHash = createHash; 457 | 458 | module.exports = Rusha; 459 | 460 | /***/ }), 461 | /* 4 */ 462 | /***/ (function(module, exports, __webpack_require__) { 463 | 464 | function webpackBootstrapFunc (modules) { 465 | /******/ // The module cache 466 | /******/ var installedModules = {}; 467 | 468 | /******/ // The require function 469 | /******/ function __webpack_require__(moduleId) { 470 | 471 | /******/ // Check if module is in cache 472 | /******/ if(installedModules[moduleId]) 473 | /******/ return installedModules[moduleId].exports; 474 | 475 | /******/ // Create a new module (and put it into the cache) 476 | /******/ var module = installedModules[moduleId] = { 477 | /******/ i: moduleId, 478 | /******/ l: false, 479 | /******/ exports: {} 480 | /******/ }; 481 | 482 | /******/ // Execute the module function 483 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 484 | 485 | /******/ // Flag the module as loaded 486 | /******/ module.l = true; 487 | 488 | /******/ // Return the exports of the module 489 | /******/ return module.exports; 490 | /******/ } 491 | 492 | /******/ // expose the modules object (__webpack_modules__) 493 | /******/ __webpack_require__.m = modules; 494 | 495 | /******/ // expose the module cache 496 | /******/ __webpack_require__.c = installedModules; 497 | 498 | /******/ // identity function for calling harmony imports with the correct context 499 | /******/ __webpack_require__.i = function(value) { return value; }; 500 | 501 | /******/ // define getter function for harmony exports 502 | /******/ __webpack_require__.d = function(exports, name, getter) { 503 | /******/ if(!__webpack_require__.o(exports, name)) { 504 | /******/ Object.defineProperty(exports, name, { 505 | /******/ configurable: false, 506 | /******/ enumerable: true, 507 | /******/ get: getter 508 | /******/ }); 509 | /******/ } 510 | /******/ }; 511 | 512 | /******/ // define __esModule on exports 513 | /******/ __webpack_require__.r = function(exports) { 514 | /******/ Object.defineProperty(exports, '__esModule', { value: true }); 515 | /******/ }; 516 | 517 | /******/ // getDefaultExport function for compatibility with non-harmony modules 518 | /******/ __webpack_require__.n = function(module) { 519 | /******/ var getter = module && module.__esModule ? 520 | /******/ function getDefault() { return module['default']; } : 521 | /******/ function getModuleExports() { return module; }; 522 | /******/ __webpack_require__.d(getter, 'a', getter); 523 | /******/ return getter; 524 | /******/ }; 525 | 526 | /******/ // Object.prototype.hasOwnProperty.call 527 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 528 | 529 | /******/ // __webpack_public_path__ 530 | /******/ __webpack_require__.p = "/"; 531 | 532 | /******/ // on error function for async loading 533 | /******/ __webpack_require__.oe = function(err) { console.error(err); throw err; }; 534 | 535 | var f = __webpack_require__(__webpack_require__.s = ENTRY_MODULE) 536 | return f.default || f // try to call default if defined to also support babel esmodule exports 537 | } 538 | 539 | var moduleNameReqExp = '[\\.|\\-|\\+|\\w|\/|@]+' 540 | var dependencyRegExp = '\\((\/\\*.*?\\*\/)?\s?.*?(' + moduleNameReqExp + ').*?\\)' // additional chars when output.pathinfo is true 541 | 542 | // http://stackoverflow.com/a/2593661/130442 543 | function quoteRegExp (str) { 544 | return (str + '').replace(/[.?*+^$[\]\\(){}|-]/g, '\\$&') 545 | } 546 | 547 | function getModuleDependencies (sources, module, queueName) { 548 | var retval = {} 549 | retval[queueName] = [] 550 | 551 | var fnString = module.toString() 552 | var wrapperSignature = fnString.match(/^function\s?\(\w+,\s*\w+,\s*(\w+)\)/) 553 | if (!wrapperSignature) return retval 554 | var webpackRequireName = wrapperSignature[1] 555 | 556 | // main bundle deps 557 | var re = new RegExp('(\\\\n|\\W)' + quoteRegExp(webpackRequireName) + dependencyRegExp, 'g') 558 | var match 559 | while ((match = re.exec(fnString))) { 560 | if (match[3] === 'dll-reference') continue 561 | retval[queueName].push(match[3]) 562 | } 563 | 564 | // dll deps 565 | re = new RegExp('\\(' + quoteRegExp(webpackRequireName) + '\\("(dll-reference\\s(' + moduleNameReqExp + '))"\\)\\)' + dependencyRegExp, 'g') 566 | while ((match = re.exec(fnString))) { 567 | if (!sources[match[2]]) { 568 | retval[queueName].push(match[1]) 569 | sources[match[2]] = __webpack_require__(match[1]).m 570 | } 571 | retval[match[2]] = retval[match[2]] || [] 572 | retval[match[2]].push(match[4]) 573 | } 574 | 575 | return retval 576 | } 577 | 578 | function hasValuesInQueues (queues) { 579 | var keys = Object.keys(queues) 580 | return keys.reduce(function (hasValues, key) { 581 | return hasValues || queues[key].length > 0 582 | }, false) 583 | } 584 | 585 | function getRequiredModules (sources, moduleId) { 586 | var modulesQueue = { 587 | main: [moduleId] 588 | } 589 | var requiredModules = { 590 | main: [] 591 | } 592 | var seenModules = { 593 | main: {} 594 | } 595 | 596 | while (hasValuesInQueues(modulesQueue)) { 597 | var queues = Object.keys(modulesQueue) 598 | for (var i = 0; i < queues.length; i++) { 599 | var queueName = queues[i] 600 | var queue = modulesQueue[queueName] 601 | var moduleToCheck = queue.pop() 602 | seenModules[queueName] = seenModules[queueName] || {} 603 | if (seenModules[queueName][moduleToCheck] || !sources[queueName][moduleToCheck]) continue 604 | seenModules[queueName][moduleToCheck] = true 605 | requiredModules[queueName] = requiredModules[queueName] || [] 606 | requiredModules[queueName].push(moduleToCheck) 607 | var newModules = getModuleDependencies(sources, sources[queueName][moduleToCheck], queueName) 608 | var newModulesKeys = Object.keys(newModules) 609 | for (var j = 0; j < newModulesKeys.length; j++) { 610 | modulesQueue[newModulesKeys[j]] = modulesQueue[newModulesKeys[j]] || [] 611 | modulesQueue[newModulesKeys[j]] = modulesQueue[newModulesKeys[j]].concat(newModules[newModulesKeys[j]]) 612 | } 613 | } 614 | } 615 | 616 | return requiredModules 617 | } 618 | 619 | module.exports = function (moduleId, options) { 620 | options = options || {} 621 | var sources = { 622 | main: __webpack_require__.m 623 | } 624 | 625 | var requiredModules = options.all ? { main: Object.keys(sources) } : getRequiredModules(sources, moduleId) 626 | 627 | var src = '' 628 | 629 | Object.keys(requiredModules).filter(function (m) { return m !== 'main' }).forEach(function (module) { 630 | var entryModule = 0 631 | while (requiredModules[module][entryModule]) { 632 | entryModule++ 633 | } 634 | requiredModules[module].push(entryModule) 635 | sources[module][entryModule] = '(function(module, exports, __webpack_require__) { module.exports = __webpack_require__; })' 636 | src = src + 'var ' + module + ' = (' + webpackBootstrapFunc.toString().replace('ENTRY_MODULE', JSON.stringify(entryModule)) + ')({' + requiredModules[module].map(function (id) { return '' + JSON.stringify(id) + ': ' + sources[module][id].toString() }).join(',') + '});\n' 637 | }) 638 | 639 | src = src + '(' + webpackBootstrapFunc.toString().replace('ENTRY_MODULE', JSON.stringify(moduleId)) + ')({' + requiredModules.main.map(function (id) { return '' + JSON.stringify(id) + ': ' + sources.main[id].toString() }).join(',') + '})(self);' 640 | 641 | var blob = new window.Blob([src], { type: 'text/javascript' }) 642 | if (options.bare) { return blob } 643 | 644 | var URL = window.URL || window.webkitURL || window.mozURL || window.msURL 645 | 646 | var workerUrl = URL.createObjectURL(blob) 647 | var worker = new window.Worker(workerUrl) 648 | worker.objectURL = workerUrl 649 | 650 | return worker 651 | } 652 | 653 | 654 | /***/ }), 655 | /* 5 */ 656 | /***/ (function(module, exports) { 657 | 658 | // The low-level RushCore module provides the heart of Rusha, 659 | // a high-speed sha1 implementation working on an Int32Array heap. 660 | // At first glance, the implementation seems complicated, however 661 | // with the SHA1 spec at hand, it is obvious this almost a textbook 662 | // implementation that has a few functions hand-inlined and a few loops 663 | // hand-unrolled. 664 | module.exports = function RushaCore(stdlib$840, foreign$841, heap$842) { 665 | 'use asm'; 666 | var H$843 = new stdlib$840.Int32Array(heap$842); 667 | function hash$844(k$845, x$846) { 668 | // k in bytes 669 | k$845 = k$845 | 0; 670 | x$846 = x$846 | 0; 671 | var i$847 = 0, j$848 = 0, y0$849 = 0, z0$850 = 0, y1$851 = 0, z1$852 = 0, y2$853 = 0, z2$854 = 0, y3$855 = 0, z3$856 = 0, y4$857 = 0, z4$858 = 0, t0$859 = 0, t1$860 = 0; 672 | y0$849 = H$843[x$846 + 320 >> 2] | 0; 673 | y1$851 = H$843[x$846 + 324 >> 2] | 0; 674 | y2$853 = H$843[x$846 + 328 >> 2] | 0; 675 | y3$855 = H$843[x$846 + 332 >> 2] | 0; 676 | y4$857 = H$843[x$846 + 336 >> 2] | 0; 677 | for (i$847 = 0; (i$847 | 0) < (k$845 | 0); i$847 = i$847 + 64 | 0) { 678 | z0$850 = y0$849; 679 | z1$852 = y1$851; 680 | z2$854 = y2$853; 681 | z3$856 = y3$855; 682 | z4$858 = y4$857; 683 | for (j$848 = 0; (j$848 | 0) < 64; j$848 = j$848 + 4 | 0) { 684 | t1$860 = H$843[i$847 + j$848 >> 2] | 0; 685 | t0$859 = ((y0$849 << 5 | y0$849 >>> 27) + (y1$851 & y2$853 | ~y1$851 & y3$855) | 0) + ((t1$860 + y4$857 | 0) + 1518500249 | 0) | 0; 686 | y4$857 = y3$855; 687 | y3$855 = y2$853; 688 | y2$853 = y1$851 << 30 | y1$851 >>> 2; 689 | y1$851 = y0$849; 690 | y0$849 = t0$859; 691 | H$843[k$845 + j$848 >> 2] = t1$860; 692 | } 693 | for (j$848 = k$845 + 64 | 0; (j$848 | 0) < (k$845 + 80 | 0); j$848 = j$848 + 4 | 0) { 694 | t1$860 = (H$843[j$848 - 12 >> 2] ^ H$843[j$848 - 32 >> 2] ^ H$843[j$848 - 56 >> 2] ^ H$843[j$848 - 64 >> 2]) << 1 | (H$843[j$848 - 12 >> 2] ^ H$843[j$848 - 32 >> 2] ^ H$843[j$848 - 56 >> 2] ^ H$843[j$848 - 64 >> 2]) >>> 31; 695 | t0$859 = ((y0$849 << 5 | y0$849 >>> 27) + (y1$851 & y2$853 | ~y1$851 & y3$855) | 0) + ((t1$860 + y4$857 | 0) + 1518500249 | 0) | 0; 696 | y4$857 = y3$855; 697 | y3$855 = y2$853; 698 | y2$853 = y1$851 << 30 | y1$851 >>> 2; 699 | y1$851 = y0$849; 700 | y0$849 = t0$859; 701 | H$843[j$848 >> 2] = t1$860; 702 | } 703 | for (j$848 = k$845 + 80 | 0; (j$848 | 0) < (k$845 + 160 | 0); j$848 = j$848 + 4 | 0) { 704 | t1$860 = (H$843[j$848 - 12 >> 2] ^ H$843[j$848 - 32 >> 2] ^ H$843[j$848 - 56 >> 2] ^ H$843[j$848 - 64 >> 2]) << 1 | (H$843[j$848 - 12 >> 2] ^ H$843[j$848 - 32 >> 2] ^ H$843[j$848 - 56 >> 2] ^ H$843[j$848 - 64 >> 2]) >>> 31; 705 | t0$859 = ((y0$849 << 5 | y0$849 >>> 27) + (y1$851 ^ y2$853 ^ y3$855) | 0) + ((t1$860 + y4$857 | 0) + 1859775393 | 0) | 0; 706 | y4$857 = y3$855; 707 | y3$855 = y2$853; 708 | y2$853 = y1$851 << 30 | y1$851 >>> 2; 709 | y1$851 = y0$849; 710 | y0$849 = t0$859; 711 | H$843[j$848 >> 2] = t1$860; 712 | } 713 | for (j$848 = k$845 + 160 | 0; (j$848 | 0) < (k$845 + 240 | 0); j$848 = j$848 + 4 | 0) { 714 | t1$860 = (H$843[j$848 - 12 >> 2] ^ H$843[j$848 - 32 >> 2] ^ H$843[j$848 - 56 >> 2] ^ H$843[j$848 - 64 >> 2]) << 1 | (H$843[j$848 - 12 >> 2] ^ H$843[j$848 - 32 >> 2] ^ H$843[j$848 - 56 >> 2] ^ H$843[j$848 - 64 >> 2]) >>> 31; 715 | t0$859 = ((y0$849 << 5 | y0$849 >>> 27) + (y1$851 & y2$853 | y1$851 & y3$855 | y2$853 & y3$855) | 0) + ((t1$860 + y4$857 | 0) - 1894007588 | 0) | 0; 716 | y4$857 = y3$855; 717 | y3$855 = y2$853; 718 | y2$853 = y1$851 << 30 | y1$851 >>> 2; 719 | y1$851 = y0$849; 720 | y0$849 = t0$859; 721 | H$843[j$848 >> 2] = t1$860; 722 | } 723 | for (j$848 = k$845 + 240 | 0; (j$848 | 0) < (k$845 + 320 | 0); j$848 = j$848 + 4 | 0) { 724 | t1$860 = (H$843[j$848 - 12 >> 2] ^ H$843[j$848 - 32 >> 2] ^ H$843[j$848 - 56 >> 2] ^ H$843[j$848 - 64 >> 2]) << 1 | (H$843[j$848 - 12 >> 2] ^ H$843[j$848 - 32 >> 2] ^ H$843[j$848 - 56 >> 2] ^ H$843[j$848 - 64 >> 2]) >>> 31; 725 | t0$859 = ((y0$849 << 5 | y0$849 >>> 27) + (y1$851 ^ y2$853 ^ y3$855) | 0) + ((t1$860 + y4$857 | 0) - 899497514 | 0) | 0; 726 | y4$857 = y3$855; 727 | y3$855 = y2$853; 728 | y2$853 = y1$851 << 30 | y1$851 >>> 2; 729 | y1$851 = y0$849; 730 | y0$849 = t0$859; 731 | H$843[j$848 >> 2] = t1$860; 732 | } 733 | y0$849 = y0$849 + z0$850 | 0; 734 | y1$851 = y1$851 + z1$852 | 0; 735 | y2$853 = y2$853 + z2$854 | 0; 736 | y3$855 = y3$855 + z3$856 | 0; 737 | y4$857 = y4$857 + z4$858 | 0; 738 | } 739 | H$843[x$846 + 320 >> 2] = y0$849; 740 | H$843[x$846 + 324 >> 2] = y1$851; 741 | H$843[x$846 + 328 >> 2] = y2$853; 742 | H$843[x$846 + 332 >> 2] = y3$855; 743 | H$843[x$846 + 336 >> 2] = y4$857; 744 | } 745 | return { hash: hash$844 }; 746 | }; 747 | 748 | /***/ }), 749 | /* 6 */ 750 | /***/ (function(module, exports) { 751 | 752 | var _this = this; 753 | 754 | /* eslint-env commonjs, browser */ 755 | 756 | var reader = void 0; 757 | if (typeof self !== 'undefined' && typeof self.FileReaderSync !== 'undefined') { 758 | reader = new self.FileReaderSync(); 759 | } 760 | 761 | // Convert a binary string and write it to the heap. 762 | // A binary string is expected to only contain char codes < 256. 763 | var convStr = function (str, H8, H32, start, len, off) { 764 | var i = void 0, 765 | om = off % 4, 766 | lm = (len + om) % 4, 767 | j = len - lm; 768 | switch (om) { 769 | case 0: 770 | H8[off] = str.charCodeAt(start + 3); 771 | case 1: 772 | H8[off + 1 - (om << 1) | 0] = str.charCodeAt(start + 2); 773 | case 2: 774 | H8[off + 2 - (om << 1) | 0] = str.charCodeAt(start + 1); 775 | case 3: 776 | H8[off + 3 - (om << 1) | 0] = str.charCodeAt(start); 777 | } 778 | if (len < lm + (4 - om)) { 779 | return; 780 | } 781 | for (i = 4 - om; i < j; i = i + 4 | 0) { 782 | H32[off + i >> 2] = str.charCodeAt(start + i) << 24 | str.charCodeAt(start + i + 1) << 16 | str.charCodeAt(start + i + 2) << 8 | str.charCodeAt(start + i + 3); 783 | } 784 | switch (lm) { 785 | case 3: 786 | H8[off + j + 1 | 0] = str.charCodeAt(start + j + 2); 787 | case 2: 788 | H8[off + j + 2 | 0] = str.charCodeAt(start + j + 1); 789 | case 1: 790 | H8[off + j + 3 | 0] = str.charCodeAt(start + j); 791 | } 792 | }; 793 | 794 | // Convert a buffer or array and write it to the heap. 795 | // The buffer or array is expected to only contain elements < 256. 796 | var convBuf = function (buf, H8, H32, start, len, off) { 797 | var i = void 0, 798 | om = off % 4, 799 | lm = (len + om) % 4, 800 | j = len - lm; 801 | switch (om) { 802 | case 0: 803 | H8[off] = buf[start + 3]; 804 | case 1: 805 | H8[off + 1 - (om << 1) | 0] = buf[start + 2]; 806 | case 2: 807 | H8[off + 2 - (om << 1) | 0] = buf[start + 1]; 808 | case 3: 809 | H8[off + 3 - (om << 1) | 0] = buf[start]; 810 | } 811 | if (len < lm + (4 - om)) { 812 | return; 813 | } 814 | for (i = 4 - om; i < j; i = i + 4 | 0) { 815 | H32[off + i >> 2 | 0] = buf[start + i] << 24 | buf[start + i + 1] << 16 | buf[start + i + 2] << 8 | buf[start + i + 3]; 816 | } 817 | switch (lm) { 818 | case 3: 819 | H8[off + j + 1 | 0] = buf[start + j + 2]; 820 | case 2: 821 | H8[off + j + 2 | 0] = buf[start + j + 1]; 822 | case 1: 823 | H8[off + j + 3 | 0] = buf[start + j]; 824 | } 825 | }; 826 | 827 | var convBlob = function (blob, H8, H32, start, len, off) { 828 | var i = void 0, 829 | om = off % 4, 830 | lm = (len + om) % 4, 831 | j = len - lm; 832 | var buf = new Uint8Array(reader.readAsArrayBuffer(blob.slice(start, start + len))); 833 | switch (om) { 834 | case 0: 835 | H8[off] = buf[3]; 836 | case 1: 837 | H8[off + 1 - (om << 1) | 0] = buf[2]; 838 | case 2: 839 | H8[off + 2 - (om << 1) | 0] = buf[1]; 840 | case 3: 841 | H8[off + 3 - (om << 1) | 0] = buf[0]; 842 | } 843 | if (len < lm + (4 - om)) { 844 | return; 845 | } 846 | for (i = 4 - om; i < j; i = i + 4 | 0) { 847 | H32[off + i >> 2 | 0] = buf[i] << 24 | buf[i + 1] << 16 | buf[i + 2] << 8 | buf[i + 3]; 848 | } 849 | switch (lm) { 850 | case 3: 851 | H8[off + j + 1 | 0] = buf[j + 2]; 852 | case 2: 853 | H8[off + j + 2 | 0] = buf[j + 1]; 854 | case 1: 855 | H8[off + j + 3 | 0] = buf[j]; 856 | } 857 | }; 858 | 859 | module.exports = function (data, H8, H32, start, len, off) { 860 | if (typeof data === 'string') { 861 | return convStr(data, H8, H32, start, len, off); 862 | } 863 | if (data instanceof Array) { 864 | return convBuf(data, H8, H32, start, len, off); 865 | } 866 | // Safely doing a Buffer check using "this" to avoid Buffer polyfill to be included in the dist 867 | if (_this && _this.Buffer && _this.Buffer.isBuffer(data)) { 868 | return convBuf(data, H8, H32, start, len, off); 869 | } 870 | if (data instanceof ArrayBuffer) { 871 | return convBuf(new Uint8Array(data), H8, H32, start, len, off); 872 | } 873 | if (data.buffer instanceof ArrayBuffer) { 874 | return convBuf(new Uint8Array(data.buffer, data.byteOffset, data.byteLength), H8, H32, start, len, off); 875 | } 876 | if (data instanceof Blob) { 877 | return convBlob(data, H8, H32, start, len, off); 878 | } 879 | throw new Error('Unsupported data type.'); 880 | }; 881 | 882 | /***/ }), 883 | /* 7 */ 884 | /***/ (function(module, exports, __webpack_require__) { 885 | 886 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 887 | 888 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 889 | 890 | /* eslint-env commonjs, browser */ 891 | 892 | var Rusha = __webpack_require__(0); 893 | 894 | var _require = __webpack_require__(1), 895 | toHex = _require.toHex; 896 | 897 | var Hash = function () { 898 | function Hash() { 899 | _classCallCheck(this, Hash); 900 | 901 | this._rusha = new Rusha(); 902 | this._rusha.resetState(); 903 | } 904 | 905 | Hash.prototype.update = function update(data) { 906 | this._rusha.append(data); 907 | return this; 908 | }; 909 | 910 | Hash.prototype.digest = function digest(encoding) { 911 | var digest = this._rusha.rawEnd().buffer; 912 | if (!encoding) { 913 | return digest; 914 | } 915 | if (encoding === 'hex') { 916 | return toHex(digest); 917 | } 918 | throw new Error('unsupported digest encoding'); 919 | }; 920 | 921 | _createClass(Hash, [{ 922 | key: 'state', 923 | get: function () { 924 | return this._rusha.getState(); 925 | }, 926 | set: function (state) { 927 | this._rusha.setState(state); 928 | } 929 | }]); 930 | 931 | return Hash; 932 | }(); 933 | 934 | module.exports = function () { 935 | return new Hash(); 936 | }; 937 | 938 | /***/ }) 939 | /******/ ]); 940 | }); -------------------------------------------------------------------------------- /dist/rusha.min.js: -------------------------------------------------------------------------------- 1 | /*! rusha 2018-02-26 */ 2 | 3 | (function e(t,r){if(typeof exports==="object"&&typeof module==="object")module.exports=r();else if(typeof define==="function"&&define.amd)define([],r);else if(typeof exports==="object")exports["Rusha"]=r();else t["Rusha"]=r()})(typeof self!=="undefined"?self:this,function(){return function(e){var t={};function r(n){if(t[n]){return t[n].exports}var a=t[n]={i:n,l:false,exports:{}};e[n].call(a.exports,a,a.exports,r);a.l=true;return a.exports}r.m=e;r.c=t;r.d=function(e,t,n){if(!r.o(e,t)){Object.defineProperty(e,t,{configurable:false,enumerable:true,get:n})}};r.n=function(e){var t=e&&e.__esModule?function t(){return e["default"]}:function t(){return e};r.d(t,"a",t);return t};r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)};r.p="";return r(r.s=3)}([function(e,t,r){function n(e,t){if(!(e instanceof t)){throw new TypeError("Cannot call a class as a function")}}var a=r(5);var i=r(1),o=i.toHex,s=i.ceilHeapSize;var f=r(6);var u=function(e){for(e+=9;e%64>0;e+=1){}return e};var c=function(e,t){var r=new Uint8Array(e.buffer);var n=t%4,a=t-n;switch(n){case 0:r[a+3]=0;case 1:r[a+2]=0;case 2:r[a+1]=0;case 3:r[a+0]=0}for(var i=(t>>2)+1;i>2]|=128<<24-(t%4<<3);e[((t>>2)+2&~15)+14]=r/(1<<29)|0;e[((t>>2)+2&~15)+15]=r<<3};var p=function(e,t){var r=new Int32Array(e,t+320,5);var n=new Int32Array(5);var a=new DataView(n.buffer);a.setInt32(0,r[0],false);a.setInt32(4,r[1],false);a.setInt32(8,r[2],false);a.setInt32(12,r[3],false);a.setInt32(16,r[4],false);return n};var l=function(){function e(t){n(this,e);t=t||64*1024;if(t%64>0){throw new Error("Chunk size must be a multiple of 128 bit")}this._offset=0;this._maxChunkLen=t;this._padMaxChunkLen=u(t);this._heap=new ArrayBuffer(s(this._padMaxChunkLen+320+20));this._h32=new Int32Array(this._heap);this._h8=new Int8Array(this._heap);this._core=new a({Int32Array:Int32Array},{},this._heap)}e.prototype._initState=function e(t,r){this._offset=0;var n=new Int32Array(t,r+320,5);n[0]=1732584193;n[1]=-271733879;n[2]=-1732584194;n[3]=271733878;n[4]=-1009589776};e.prototype._padChunk=function e(t,r){var n=u(t);var a=new Int32Array(this._heap,0,n>>2);c(a,t);h(a,t,r);return n};e.prototype._write=function e(t,r,n,a){f(t,this._h8,this._h32,r,n,a||0)};e.prototype._coreCall=function e(t,r,n,a,i){var o=n;this._write(t,r,n);if(i){o=this._padChunk(n,a)}this._core.hash(o,this._padMaxChunkLen)};e.prototype.rawDigest=function e(t){var r=t.byteLength||t.length||t.size||0;this._initState(this._heap,this._padMaxChunkLen);var n=0,a=this._maxChunkLen;for(n=0;r>n+a;n+=a){this._coreCall(t,n,a,r,false)}this._coreCall(t,n,r-n,r,true);return p(this._heap,this._padMaxChunkLen)};e.prototype.digest=function e(t){return o(this.rawDigest(t).buffer)};e.prototype.digestFromString=function e(t){return this.digest(t)};e.prototype.digestFromBuffer=function e(t){return this.digest(t)};e.prototype.digestFromArrayBuffer=function e(t){return this.digest(t)};e.prototype.resetState=function e(){this._initState(this._heap,this._padMaxChunkLen);return this};e.prototype.append=function e(t){var r=0;var n=t.byteLength||t.length||t.size||0;var a=this._offset%this._maxChunkLen;var i=void 0;this._offset+=n;while(r0},false)}function u(e,t){var r={main:[t]};var n={main:[]};var a={main:{}};while(f(r)){var i=Object.keys(r);for(var o=0;o>2]|0;s=a[t+324>>2]|0;u=a[t+328>>2]|0;h=a[t+332>>2]|0;l=a[t+336>>2]|0;for(r=0;(r|0)<(e|0);r=r+64|0){o=i;f=s;c=u;p=h;v=l;for(n=0;(n|0)<64;n=n+4|0){y=a[r+n>>2]|0;d=((i<<5|i>>>27)+(s&u|~s&h)|0)+((y+l|0)+1518500249|0)|0;l=h;h=u;u=s<<30|s>>>2;s=i;i=d;a[e+n>>2]=y}for(n=e+64|0;(n|0)<(e+80|0);n=n+4|0){y=(a[n-12>>2]^a[n-32>>2]^a[n-56>>2]^a[n-64>>2])<<1|(a[n-12>>2]^a[n-32>>2]^a[n-56>>2]^a[n-64>>2])>>>31;d=((i<<5|i>>>27)+(s&u|~s&h)|0)+((y+l|0)+1518500249|0)|0;l=h;h=u;u=s<<30|s>>>2;s=i;i=d;a[n>>2]=y}for(n=e+80|0;(n|0)<(e+160|0);n=n+4|0){y=(a[n-12>>2]^a[n-32>>2]^a[n-56>>2]^a[n-64>>2])<<1|(a[n-12>>2]^a[n-32>>2]^a[n-56>>2]^a[n-64>>2])>>>31;d=((i<<5|i>>>27)+(s^u^h)|0)+((y+l|0)+1859775393|0)|0;l=h;h=u;u=s<<30|s>>>2;s=i;i=d;a[n>>2]=y}for(n=e+160|0;(n|0)<(e+240|0);n=n+4|0){y=(a[n-12>>2]^a[n-32>>2]^a[n-56>>2]^a[n-64>>2])<<1|(a[n-12>>2]^a[n-32>>2]^a[n-56>>2]^a[n-64>>2])>>>31;d=((i<<5|i>>>27)+(s&u|s&h|u&h)|0)+((y+l|0)-1894007588|0)|0;l=h;h=u;u=s<<30|s>>>2;s=i;i=d;a[n>>2]=y}for(n=e+240|0;(n|0)<(e+320|0);n=n+4|0){y=(a[n-12>>2]^a[n-32>>2]^a[n-56>>2]^a[n-64>>2])<<1|(a[n-12>>2]^a[n-32>>2]^a[n-56>>2]^a[n-64>>2])>>>31;d=((i<<5|i>>>27)+(s^u^h)|0)+((y+l|0)-899497514|0)|0;l=h;h=u;u=s<<30|s>>>2;s=i;i=d;a[n>>2]=y}i=i+o|0;s=s+f|0;u=u+c|0;h=h+p|0;l=l+v|0}a[t+320>>2]=i;a[t+324>>2]=s;a[t+328>>2]=u;a[t+332>>2]=h;a[t+336>>2]=l}return{hash:i}}},function(e,t){var r=this;var n=void 0;if(typeof self!=="undefined"&&typeof self.FileReaderSync!=="undefined"){n=new self.FileReaderSync}var a=function(e,t,r,n,a,i){var o=void 0,s=i%4,f=(a+s)%4,u=a-f;switch(s){case 0:t[i]=e.charCodeAt(n+3);case 1:t[i+1-(s<<1)|0]=e.charCodeAt(n+2);case 2:t[i+2-(s<<1)|0]=e.charCodeAt(n+1);case 3:t[i+3-(s<<1)|0]=e.charCodeAt(n)}if(a>2]=e.charCodeAt(n+o)<<24|e.charCodeAt(n+o+1)<<16|e.charCodeAt(n+o+2)<<8|e.charCodeAt(n+o+3)}switch(f){case 3:t[i+u+1|0]=e.charCodeAt(n+u+2);case 2:t[i+u+2|0]=e.charCodeAt(n+u+1);case 1:t[i+u+3|0]=e.charCodeAt(n+u)}};var i=function(e,t,r,n,a,i){var o=void 0,s=i%4,f=(a+s)%4,u=a-f;switch(s){case 0:t[i]=e[n+3];case 1:t[i+1-(s<<1)|0]=e[n+2];case 2:t[i+2-(s<<1)|0]=e[n+1];case 3:t[i+3-(s<<1)|0]=e[n]}if(a>2|0]=e[n+o]<<24|e[n+o+1]<<16|e[n+o+2]<<8|e[n+o+3]}switch(f){case 3:t[i+u+1|0]=e[n+u+2];case 2:t[i+u+2|0]=e[n+u+1];case 1:t[i+u+3|0]=e[n+u]}};var o=function(e,t,r,a,i,o){var s=void 0,f=o%4,u=(i+f)%4,c=i-u;var h=new Uint8Array(n.readAsArrayBuffer(e.slice(a,a+i)));switch(f){case 0:t[o]=h[3];case 1:t[o+1-(f<<1)|0]=h[2];case 2:t[o+2-(f<<1)|0]=h[1];case 3:t[o+3-(f<<1)|0]=h[0]}if(i>2|0]=h[s]<<24|h[s+1]<<16|h[s+2]<<8|h[s+3]}switch(u){case 3:t[o+c+1|0]=h[c+2];case 2:t[o+c+2|0]=h[c+1];case 1:t[o+c+3|0]=h[c]}};e.exports=function(e,t,n,s,f,u){if(typeof e==="string"){return a(e,t,n,s,f,u)}if(e instanceof Array){return i(e,t,n,s,f,u)}if(r&&r.Buffer&&r.Buffer.isBuffer(e)){return i(e,t,n,s,f,u)}if(e instanceof ArrayBuffer){return i(new Uint8Array(e),t,n,s,f,u)}if(e.buffer instanceof ArrayBuffer){return i(new Uint8Array(e.buffer,e.byteOffset,e.byteLength),t,n,s,f,u)}if(e instanceof Blob){return o(e,t,n,s,f,u)}throw new Error("Unsupported data type.")}},function(e,t,r){var n=function(){function e(e,t){for(var r=0;r 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /misc/bench.js: -------------------------------------------------------------------------------- 1 | if (typeof require === 'function') { 2 | var crypto = require('crypto'); 3 | var johnston = require('./bench/johnston'); 4 | var Rusha = require('../dist/rusha.min.js'); 5 | var cifre_utils = require('./bench/cifre/utils.js'); 6 | var cifre_sha1 = require('./bench/cifre/sha1.js'); 7 | var random = require('./random'); 8 | var fnNative = random.fnNative, 9 | randomBytes = random.randomBytes; 10 | } 11 | 12 | var sizes = [4*1024, 65535, 1024*1024, 4*1024*1024, 8*1024*1024]; 13 | var repeats = [ 100, 30, 3, 1, 1]; 14 | 15 | var _rush = new Rusha(Math.max.apply(Math, sizes)), 16 | fnRusha = function (bytes) { 17 | return _rush.digestFromBuffer(bytes); 18 | }; 19 | 20 | var fnJohnston = function (bytes) { 21 | return johnston(bytes); 22 | }; 23 | 24 | var fnCifre = function (bytes) { 25 | return cifre_utils.tohex(cifre_sha1(bytes)); 26 | }; 27 | 28 | var ids = ['Native ', 'Rusha ', 'Johnst. ', 'Cifre ']; 29 | var fns = [fnNative, fnRusha, fnJohnston, fnCifre]; 30 | 31 | var bench = function () { 32 | sizes.forEach(function (size, k) { 33 | console.log('Benchmarking ' + size + ' bytes ...'); 34 | var bytes = randomBytes(size); 35 | fns.forEach(function (fn, i) { 36 | var t0 = (new Date()).getTime(); 37 | var res = ""; 38 | for (j=0;j>> 27))) >>> 0; 47 | } 48 | 49 | function f1(a, b, c, d, e, w) { 50 | return common(a, e, w, 0x5A827999, d ^ (b & (c ^ d))); 51 | } 52 | 53 | function f2(a, b, c, d, e, w) { 54 | return common(a, e, w, 0x6ED9EBA1, b ^ c ^ d); 55 | } 56 | 57 | function f3(a, b, c, d, e, w) { 58 | return common(a, e, w, 0x8F1BBCDC, (b & c) | (d & (b | c))); 59 | } 60 | 61 | function f4(a, b, c, d, e, w) { 62 | return common(a, e, w, 0xCA62C1D6, b ^ c ^ d); 63 | } 64 | 65 | // function hex8(num) { 66 | // var hex = num.toString(16).toUpperCase(); 67 | // return "00000000".substr(hex.length) + hex; 68 | // } 69 | 70 | function cycle(state, block) { 71 | var a = state[0], 72 | b = state[1], 73 | c = state[2], 74 | d = state[3], 75 | e = state[4]; 76 | 77 | // console.log("\nInitial hash value:"); 78 | // for (var i = 0; i < 5; i++) { 79 | // console.log(" H[" + i + "] = " + hex8(state[i])); 80 | // } 81 | // console.log("\nBlock Contents:"); 82 | // for (var i = 0; i < 16; i++) { 83 | // console.log(" W[" + i + "] = " + hex8(block[i])); 84 | // } 85 | 86 | // console.log("\n A B C D E"); 87 | // Partially unroll loops so we don't have to shift variables. 88 | var fn = f1;; 89 | for (var i = 0; i < 80; i += 5) { 90 | if (i === 20) { fn = f2; } 91 | else if (i === 40) { fn = f3; } 92 | else if (i === 60) { fn = f4; } 93 | e = fn(a, b, c, d, e, block[i]); b = ((b << 30) | (b >>> 2)) >>> 0; 94 | // console.log("t=%s: %s %s %s %s %s", i, 95 | // hex8(e), hex8(a), hex8(b), hex8(c), hex8(d)); 96 | d = fn(e, a, b, c, d, block[i + 1]); a = ((a << 30) | (a >>> 2)) >>> 0; 97 | // console.log("t=%s: %s %s %s %s %s", i + 1, 98 | // hex8(d), hex8(e), hex8(a), hex8(b), hex8(c)); 99 | c = fn(d, e, a, b, c, block[i + 2]); e = ((e << 30) | (e >>> 2)) >>> 0; 100 | // console.log("t=%s: %s %s %s %s %s", i + 2, 101 | // hex8(c), hex8(d), hex8(e), hex8(a), hex8(b)); 102 | b = fn(c, d, e, a, b, block[i + 3]); d = ((d << 30) | (d >>> 2)) >>> 0; 103 | // console.log("t=%s: %s %s %s %s %s", i + 3, 104 | // hex8(b), hex8(c), hex8(d), hex8(e), hex8(a)); 105 | a = fn(b, c, d, e, a, block[i + 4]); c = ((c << 30) | (c >>> 2)) >>> 0; 106 | // console.log("t=%s: %s %s %s %s %s", i + 4, 107 | // hex8(a), hex8(b), hex8(c), hex8(d), hex8(e)); 108 | } 109 | 110 | // console.log(); 111 | // process.stdout.write("H[0] = " + hex8(state[0]) + " + " + hex8(a)); 112 | state[0] += a; 113 | // console.log(" = " + hex8(state[0])); 114 | // process.stdout.write("H[1] = " + hex8(state[1]) + " + " + hex8(b)); 115 | state[1] += b; 116 | // console.log(" = " + hex8(state[1])); 117 | // process.stdout.write("H[2] = " + hex8(state[2]) + " + " + hex8(c)); 118 | state[2] += c; 119 | // console.log(" = " + hex8(state[2])); 120 | // process.stdout.write("H[3] = " + hex8(state[3]) + " + " + hex8(d)); 121 | state[3] += d; 122 | // console.log(" = " + hex8(state[3])); 123 | // process.stdout.write("H[4] = " + hex8(state[4]) + " + " + hex8(e)); 124 | state[4] += e; 125 | // console.log(" = " + hex8(state[4])); 126 | 127 | 128 | } 129 | 130 | // input is a Uint8Array bitstream of the data 131 | function sha1(input) { 132 | 133 | var inputLength = input.length; 134 | 135 | // Pad the input string length. 136 | var length = inputLength + 9; 137 | length += 64 - (length % 64); 138 | 139 | state[0] = 0x67452301; 140 | state[1] = 0xefcdab89; 141 | state[2] = 0x98badcfe; 142 | state[3] = 0x10325476; 143 | state[4] = 0xc3d2e1f0; 144 | 145 | for (var offset = 0; offset < length; offset += 64) { 146 | 147 | // Copy input to block and write padding as needed 148 | for (var i = 0; i < 64; i++) { 149 | var b = 0, 150 | o = offset + i; 151 | if (o < inputLength) { 152 | b = input[o]; 153 | } 154 | else if (o === inputLength) { 155 | b = 0x80; 156 | } 157 | else { 158 | // Write original bit length as a 64bit big-endian integer to the end. 159 | var x = length - o - 1; 160 | if (x >= 0 && x < 4) { 161 | b = (inputLength << 3 >>> (x * 8)) & 0xff; 162 | } 163 | } 164 | 165 | // Interpret the input bytes as big-endian per the spec 166 | if (i % 4 === 0) { 167 | block[i >> 2] = b << 24; 168 | } 169 | else { 170 | block[i >> 2] |= b << ((3 - (i % 4)) * 8); 171 | } 172 | } 173 | 174 | // Extend the block 175 | for (var i = 16; i < 80; i++) { 176 | var w = block[i - 3] ^ block[i - 8] ^ block[i - 14] ^ block[i - 16]; 177 | block[i] = (w << 1) | (w >>> 31); 178 | } 179 | 180 | cycle(state, block); 181 | 182 | } 183 | 184 | // Swap the bytes around since they are big endian internally 185 | return [ 186 | bstate[3], bstate[2], bstate[1], bstate[0], 187 | bstate[7], bstate[6], bstate[5], bstate[4], 188 | bstate[11], bstate[10], bstate[9], bstate[8], 189 | bstate[15], bstate[14], bstate[13], bstate[12], 190 | bstate[19], bstate[18], bstate[17], bstate[16], 191 | ] 192 | } 193 | 194 | return sha1; 195 | }); 196 | -------------------------------------------------------------------------------- /misc/bench/cifre/utils.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2013 SMB Phone Inc. 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | 26 | The views and conclusions contained in the software and documentation are those 27 | of the authors and should not be interpreted as representing official policies, 28 | either expressed or implied, of the FreeBSD Project. 29 | 30 | */ 31 | 32 | ( // Module boilerplate to support browser globals, node.js and AMD. 33 | (typeof module !== "undefined" && function (m) { module.exports = m(); }) || 34 | (typeof define === "function" && function (m) { define(m); }) || 35 | (function (m) { window.cifre_utils = m(); }) 36 | )(function () { 37 | "use strict"; 38 | 39 | function hex(val) { 40 | if (val >>> 0 !== val) { return " "; } 41 | if (val < 0x10) { return "0" + val.toString(16); } 42 | return val.toString(16); 43 | } 44 | 45 | function ensureArray(buffer) { 46 | if (buffer instanceof Uint8Array) { 47 | return buffer; 48 | } 49 | if (buffer instanceof ArrayBuffer || Array.isArray(buffer)) { 50 | return new Uint8Array(buffer); 51 | } 52 | if (buffer.buffer instanceof ArrayBuffer) { 53 | return new Uint8Array(buffer.buffer); 54 | } 55 | throw new TypeError("Invalid buffer type " + buffer); 56 | } 57 | 58 | // Dump a Uint8Array as a 4-row hex stream 59 | function dump(block) { 60 | block = ensureArray(block); 61 | var rows = new Array(4); 62 | var width = Math.ceil(block.length / 4); 63 | for (var i = 0; i < 4; i++) { 64 | rows[i] = new Array(width); 65 | for (var j = 0; j < width; j++) { 66 | rows[i][j] = block[i + j * 4]; 67 | } 68 | } 69 | console.log(rows.map(function (row) { 70 | return row.map(function (val, i) { 71 | if (val === undefined) return ""; 72 | return hex(val) + ((i % 4 === 3) ? " " : ","); }).join(""); 73 | }).join("\n")); 74 | } 75 | 76 | function fromhex(string) { 77 | var length = string.length; 78 | var array = new Uint8Array(length / 2); 79 | for (var i = 0; i < length; i += 2) { 80 | array[i / 2] = parseInt(string.substr(i, 2), 16); 81 | } 82 | return array; 83 | } 84 | 85 | function tohex(array) { 86 | var string = ""; 87 | array = ensureArray(array); 88 | for (var i = 0, l = array.length; i < l; i++) { 89 | string += hex(array[i]); 90 | } 91 | return string; 92 | } 93 | 94 | function stringToArray(string) { 95 | // UTF-8 encode the string using one character per byte 96 | string = unescape(encodeURIComponent(string)); 97 | var length = string.length; 98 | var arr = new Array(length); 99 | for (var i = 0; i < length; i++) { 100 | arr[i] = string.charCodeAt(i); 101 | } 102 | return arr; 103 | } 104 | 105 | function stringToBuffer(string) { 106 | // UTF-8 encode the string using one character per byte 107 | string = unescape(encodeURIComponent(string)); 108 | var length = string.length; 109 | var arr = new Uint8Array(length); 110 | for (var i = 0; i < length; i++) { 111 | arr[i] = string.charCodeAt(i); 112 | } 113 | return arr; 114 | } 115 | 116 | return { 117 | dump: dump, 118 | tohex: tohex, 119 | fromhex: fromhex, 120 | stringToArray: stringToArray, 121 | stringToBuffer: stringToBuffer 122 | }; 123 | }); 124 | -------------------------------------------------------------------------------- /misc/bench/johnston.js: -------------------------------------------------------------------------------- 1 | /* 2 | * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined 3 | * in FIPS 180-1 4 | * Version 2.2 Copyright Paul Johnston 2000 - 2009. 5 | * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet 6 | * Distributed under the BSD License 7 | * See http://pajhome.org.uk/crypt/md5 for details. 8 | */ 9 | 10 | /* 11 | * Configurable variables. You may need to tweak these to be compatible with 12 | * the server-side, but the defaults work in most cases. 13 | */ 14 | var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */ 15 | var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */ 16 | 17 | /* 18 | * These are the functions you'll usually want to call 19 | * They take string arguments and return either hex or base-64 encoded strings 20 | */ 21 | if (typeof module !== 'undefined') { 22 | module.exports = function (s) { return rstr2hex(binb2rstr(binb_sha1(buf2binb(s), s.length * 8))); } 23 | } else { 24 | function johnston (s) { return rstr2hex(binb2rstr(binb_sha1(tarr2binb(s), s.byteLength * 8))); } 25 | } 26 | function hex_sha1(s) { return rstr2hex(rstr_sha1(str2rstr_utf8(s))); } 27 | function b64_sha1(s) { return rstr2b64(rstr_sha1(str2rstr_utf8(s))); } 28 | function any_sha1(s, e) { return rstr2any(rstr_sha1(str2rstr_utf8(s)), e); } 29 | function hex_hmac_sha1(k, d) 30 | { return rstr2hex(rstr_hmac_sha1(str2rstr_utf8(k), str2rstr_utf8(d))); } 31 | function b64_hmac_sha1(k, d) 32 | { return rstr2b64(rstr_hmac_sha1(str2rstr_utf8(k), str2rstr_utf8(d))); } 33 | function any_hmac_sha1(k, d, e) 34 | { return rstr2any(rstr_hmac_sha1(str2rstr_utf8(k), str2rstr_utf8(d)), e); } 35 | 36 | /* 37 | * Perform a simple self-test to see if the VM is working 38 | */ 39 | function sha1_vm_test() 40 | { 41 | return hex_sha1("abc").toLowerCase() == "a9993e364706816aba3e25717850c26c9cd0d89d"; 42 | } 43 | 44 | /* 45 | * Calculate the SHA1 of a raw string 46 | */ 47 | function rstr_sha1(s) 48 | { 49 | return binb2rstr(binb_sha1(rstr2binb(s), s.length * 8)); 50 | } 51 | 52 | /* 53 | * Calculate the HMAC-SHA1 of a key and some data (raw strings) 54 | */ 55 | function rstr_hmac_sha1(key, data) 56 | { 57 | var bkey = rstr2binb(key); 58 | if(bkey.length > 16) bkey = binb_sha1(bkey, key.length * 8); 59 | 60 | var ipad = Array(16), opad = Array(16); 61 | for(var i = 0; i < 16; i++) 62 | { 63 | ipad[i] = bkey[i] ^ 0x36363636; 64 | opad[i] = bkey[i] ^ 0x5C5C5C5C; 65 | } 66 | 67 | var hash = binb_sha1(ipad.concat(rstr2binb(data)), 512 + data.length * 8); 68 | return binb2rstr(binb_sha1(opad.concat(hash), 512 + 160)); 69 | } 70 | 71 | /* 72 | * Convert a raw string to a hex string 73 | */ 74 | function rstr2hex(input) 75 | { 76 | try { hexcase } catch(e) { hexcase=0; } 77 | var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef"; 78 | var output = ""; 79 | var x; 80 | for(var i = 0; i < input.length; i++) 81 | { 82 | x = input.charCodeAt(i); 83 | output += hex_tab.charAt((x >>> 4) & 0x0F) 84 | + hex_tab.charAt( x & 0x0F); 85 | } 86 | return output; 87 | } 88 | 89 | /* 90 | * Convert a raw string to a base-64 string 91 | */ 92 | function rstr2b64(input) 93 | { 94 | try { b64pad } catch(e) { b64pad=''; } 95 | var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 96 | var output = ""; 97 | var len = input.length; 98 | for(var i = 0; i < len; i += 3) 99 | { 100 | var triplet = (input.charCodeAt(i) << 16) 101 | | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0) 102 | | (i + 2 < len ? input.charCodeAt(i+2) : 0); 103 | for(var j = 0; j < 4; j++) 104 | { 105 | if(i * 8 + j * 6 > input.length * 8) output += b64pad; 106 | else output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F); 107 | } 108 | } 109 | return output; 110 | } 111 | 112 | /* 113 | * Convert a raw string to an arbitrary string encoding 114 | */ 115 | function rstr2any(input, encoding) 116 | { 117 | var divisor = encoding.length; 118 | var remainders = Array(); 119 | var i, q, x, quotient; 120 | 121 | /* Convert to an array of 16-bit big-endian values, forming the dividend */ 122 | var dividend = Array(Math.ceil(input.length / 2)); 123 | for(i = 0; i < dividend.length; i++) 124 | { 125 | dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1); 126 | } 127 | 128 | /* 129 | * Repeatedly perform a long division. The binary array forms the dividend, 130 | * the length of the encoding is the divisor. Once computed, the quotient 131 | * forms the dividend for the next step. We stop when the dividend is zero. 132 | * All remainders are stored for later use. 133 | */ 134 | while(dividend.length > 0) 135 | { 136 | quotient = Array(); 137 | x = 0; 138 | for(i = 0; i < dividend.length; i++) 139 | { 140 | x = (x << 16) + dividend[i]; 141 | q = Math.floor(x / divisor); 142 | x -= q * divisor; 143 | if(quotient.length > 0 || q > 0) 144 | quotient[quotient.length] = q; 145 | } 146 | remainders[remainders.length] = x; 147 | dividend = quotient; 148 | } 149 | 150 | /* Convert the remainders to the output string */ 151 | var output = ""; 152 | for(i = remainders.length - 1; i >= 0; i--) 153 | output += encoding.charAt(remainders[i]); 154 | 155 | /* Append leading zero equivalents */ 156 | var full_length = Math.ceil(input.length * 8 / 157 | (Math.log(encoding.length) / Math.log(2))) 158 | for(i = output.length; i < full_length; i++) 159 | output = encoding[0] + output; 160 | 161 | return output; 162 | } 163 | 164 | /* 165 | * Encode a string as utf-8. 166 | * For efficiency, this assumes the input is valid utf-16. 167 | */ 168 | function str2rstr_utf8(input) 169 | { 170 | var output = ""; 171 | var i = -1; 172 | var x, y; 173 | 174 | while(++i < input.length) 175 | { 176 | /* Decode utf-16 surrogate pairs */ 177 | x = input.charCodeAt(i); 178 | y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0; 179 | if(0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF) 180 | { 181 | x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF); 182 | i++; 183 | } 184 | 185 | /* Encode output as utf-8 */ 186 | if(x <= 0x7F) 187 | output += String.fromCharCode(x); 188 | else if(x <= 0x7FF) 189 | output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F), 190 | 0x80 | ( x & 0x3F)); 191 | else if(x <= 0xFFFF) 192 | output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F), 193 | 0x80 | ((x >>> 6 ) & 0x3F), 194 | 0x80 | ( x & 0x3F)); 195 | else if(x <= 0x1FFFFF) 196 | output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07), 197 | 0x80 | ((x >>> 12) & 0x3F), 198 | 0x80 | ((x >>> 6 ) & 0x3F), 199 | 0x80 | ( x & 0x3F)); 200 | } 201 | return output; 202 | } 203 | 204 | /* 205 | * Encode a string as utf-16 206 | */ 207 | function str2rstr_utf16le(input) 208 | { 209 | var output = ""; 210 | for(var i = 0; i < input.length; i++) 211 | output += String.fromCharCode( input.charCodeAt(i) & 0xFF, 212 | (input.charCodeAt(i) >>> 8) & 0xFF); 213 | return output; 214 | } 215 | 216 | function str2rstr_utf16be(input) 217 | { 218 | var output = ""; 219 | for(var i = 0; i < input.length; i++) 220 | output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF, 221 | input.charCodeAt(i) & 0xFF); 222 | return output; 223 | } 224 | 225 | /* 226 | * Convert a raw string to an array of big-endian words 227 | * Characters >255 have their high-byte silently ignored. 228 | */ 229 | function rstr2binb(input) 230 | { 231 | var output = Array(input.length >> 2); 232 | for(var i = 0; i < output.length; i++) 233 | output[i] = 0; 234 | for(var i = 0; i < input.length * 8; i += 8) 235 | output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (24 - i % 32); 236 | return output; 237 | } 238 | 239 | function buf2binb(input) 240 | { 241 | var output = Array(input.length >> 2); 242 | for(var i = 0; i < output.length; i++) 243 | output[i] = 0; 244 | for(var i = 0; i < input.length * 8; i += 8) 245 | output[i>>5] |= (input[i / 8] & 0xFF) << (24 - i % 32); 246 | return output; 247 | } 248 | 249 | function tarr2binb(input) 250 | { 251 | var output = Array(input.byteLength >> 2); 252 | for(var i = 0; i < output.length; i++) 253 | output[i] = 0; 254 | for(var i = 0; i < input.byteLength * 8; i += 8) 255 | output[i>>5] |= (input[i / 8] & 0xFF) << (24 - i % 32); 256 | return output; 257 | } 258 | /* 259 | * Convert an array of big-endian words to a string 260 | */ 261 | function binb2rstr(input) 262 | { 263 | var output = ""; 264 | for(var i = 0; i < input.length * 32; i += 8) 265 | output += String.fromCharCode((input[i>>5] >>> (24 - i % 32)) & 0xFF); 266 | return output; 267 | } 268 | 269 | /* 270 | * Calculate the SHA-1 of an array of big-endian words, and a bit length 271 | */ 272 | function binb_sha1(x, len) 273 | { 274 | /* append padding */ 275 | x[len >> 5] |= 0x80 << (24 - len % 32); 276 | x[((len + 64 >> 9) << 4) + 15] = len; 277 | 278 | var w = Array(80); 279 | var a = 1732584193; 280 | var b = -271733879; 281 | var c = -1732584194; 282 | var d = 271733878; 283 | var e = -1009589776; 284 | 285 | for(var i = 0; i < x.length; i += 16) 286 | { 287 | var olda = a; 288 | var oldb = b; 289 | var oldc = c; 290 | var oldd = d; 291 | var olde = e; 292 | 293 | for(var j = 0; j < 80; j++) 294 | { 295 | if(j < 16) w[j] = x[i + j]; 296 | else w[j] = bit_rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1); 297 | var t = safe_add(safe_add(bit_rol(a, 5), sha1_ft(j, b, c, d)), 298 | safe_add(safe_add(e, w[j]), sha1_kt(j))); 299 | e = d; 300 | d = c; 301 | c = bit_rol(b, 30); 302 | b = a; 303 | a = t; 304 | } 305 | 306 | a = safe_add(a, olda); 307 | b = safe_add(b, oldb); 308 | c = safe_add(c, oldc); 309 | d = safe_add(d, oldd); 310 | e = safe_add(e, olde); 311 | } 312 | return Array(a, b, c, d, e); 313 | 314 | } 315 | 316 | /* 317 | * Perform the appropriate triplet combination function for the current 318 | * iteration 319 | */ 320 | function sha1_ft(t, b, c, d) 321 | { 322 | if(t < 20) return (b & c) | ((~b) & d); 323 | if(t < 40) return b ^ c ^ d; 324 | if(t < 60) return (b & c) | (b & d) | (c & d); 325 | return b ^ c ^ d; 326 | } 327 | 328 | /* 329 | * Determine the appropriate additive constant for the current iteration 330 | */ 331 | function sha1_kt(t) 332 | { 333 | return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 : 334 | (t < 60) ? -1894007588 : -899497514; 335 | } 336 | 337 | /* 338 | * Add integers, wrapping at 2^32. This uses 16-bit operations internally 339 | * to work around bugs in some JS interpreters. 340 | */ 341 | function safe_add(x, y) 342 | { 343 | var lsw = (x & 0xFFFF) + (y & 0xFFFF); 344 | var msw = (x >> 16) + (y >> 16) + (lsw >> 16); 345 | return (msw << 16) | (lsw & 0xFFFF); 346 | } 347 | 348 | /* 349 | * Bitwise rotate a 32-bit number to the left. 350 | */ 351 | function bit_rol(num, cnt) 352 | { 353 | return (num << cnt) | (num >>> (32 - cnt)); 354 | } 355 | -------------------------------------------------------------------------------- /misc/random.js: -------------------------------------------------------------------------------- 1 | if (typeof require === 'function') { 2 | var crypto = require('crypto'); 3 | var johnston = require('./bench/johnston'); 4 | var Rusha = require('../dist/rusha.js'); 5 | var cifre_utils = require('./bench/cifre/utils.js'); 6 | var cifre_sha1 = require('./bench/cifre/sha1.js'); 7 | } 8 | 9 | if (typeof module !== 'undefined') { 10 | 11 | module.exports = { 12 | 13 | fnNative: function (bytes) { 14 | var shasum = crypto.createHash('sha1'); 15 | shasum.update(bytes); 16 | return shasum.digest('hex'); 17 | }, 18 | 19 | randomBytes: function (size) { 20 | return crypto.pseudoRandomBytes(size); 21 | }, 22 | 23 | }; 24 | 25 | } else { 26 | 27 | function fnNative () { return 'unavailable'; } 28 | 29 | function randomBytes (size) { 30 | var bytes = new Uint8Array(size); 31 | var r; 32 | for (var i = 0, r; i < size; i++) { 33 | if ((i & 0x03) == 0) r = Math.random() * 0x100000000; 34 | bytes[i] = r >>> ((i & 0x03) << 3) & 0xff; 35 | } 36 | return bytes; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /misc/runuithread.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /misc/single.js: -------------------------------------------------------------------------------- 1 | if (typeof require === 'function') { 2 | var Rusha = require('../dist/rusha.js'); 3 | var randomBytes = require('crypto').pseudoRandomBytes; 4 | } 5 | 6 | var sizes = [4*1024, 65535, 1024*1024, 4*1024*1024, 8*1024*1024]; 7 | 8 | var _rush = new Rusha(Math.max.apply(Math, sizes)); 9 | 10 | var i, j, bytes, t0, t1; 11 | 12 | for (i = 0; i < sizes.length; i++) { 13 | bytes = randomBytes(sizes[i]); 14 | t0 = (new Date()).getTime(); 15 | for (j = 0; j < 3; j++) _rush.digestFromBuffer(bytes); 16 | t1 = (new Date()).getTime(); 17 | console.log('Emitted in ' + ((t1-t0)/3) + ' milliseconds'); 18 | } 19 | -------------------------------------------------------------------------------- /misc/test.js: -------------------------------------------------------------------------------- 1 | if (typeof require === 'function') { 2 | var crypto = require('crypto'); 3 | var johnston = require('./bench/johnston'); 4 | var Rusha = require('../dist/rusha.js'); 5 | var cifre_utils = require('./bench/cifre/utils.js'); 6 | var cifre_sha1 = require('./bench/cifre/sha1.js'); 7 | var random = require('./random'); 8 | var fnNative = random.fnNative, 9 | randomBytes = random.randomBytes; 10 | } 11 | 12 | var _rush = new Rusha(), 13 | fnRusha = function (bytes) { 14 | return _rush.digestFromBuffer(bytes); 15 | }; 16 | 17 | var fnJohnston = function (bytes) { 18 | return johnston(bytes); 19 | }; 20 | 21 | var fnCifre = function (bytes) { 22 | return cifre_utils.tohex(cifre_sha1(bytes)); 23 | }; 24 | 25 | var ids = ['Native ', 'Rusha ', 'Cifre ']; 26 | var fns = [fnNative, fnRusha/*, fnCifre*/]; 27 | 28 | var bench = function () { 29 | for (size=0;size<1024*8;size++) { 30 | // use random test data 31 | var bytes = randomBytes(size);//"Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquy".substr(0, size); 32 | var ref = ""; 33 | fns.forEach(function (fn, i) { 34 | var res = fn(bytes); 35 | if (ref == "") 36 | ref = res; 37 | else if (ref != res) 38 | console.log(ids[i] + ' hash mismatch on size ' + size + ': ' + res + ' expected: ' + ref); 39 | }); 40 | if (!(size % 1000)) 41 | console.log(size); 42 | }; 43 | } 44 | 45 | bench(); 46 | 47 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rusha", 3 | "version": "0.8.14", 4 | "description": "A high-performance pure-javascript SHA1 implementation suitable for large binary data.", 5 | "main": "dist/rusha.js", 6 | "keywords": [ 7 | "sha1", 8 | "binary", 9 | "crypto", 10 | "hash" 11 | ], 12 | "scripts": { 13 | "test": "grunt test", 14 | "test:unit": "grunt test:unit", 15 | "build": "grunt build", 16 | "benchmark": "grunt benchmark" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "https://github.com/srijs/rusha" 21 | }, 22 | "devDependencies": { 23 | "asm.js": "^0.0.2", 24 | "babel-core": "^6.26.0", 25 | "babel-eslint": "^8.0.3", 26 | "babel-loader": "^7.1.2", 27 | "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", 28 | "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", 29 | "babel-plugin-transform-es2015-block-scoping": "^6.26.0", 30 | "babel-plugin-transform-es2015-classes": "^6.24.1", 31 | "babel-plugin-transform-es2015-destructuring": "^6.23.0", 32 | "benchmark": "^1.0.0", 33 | "brfs": "^1.4.3", 34 | "browserify": "^14.5.0", 35 | "grunt": "^1.0.1", 36 | "grunt-cli": "^1.2.0", 37 | "grunt-contrib-uglify": "^3.1.0", 38 | "grunt-eslint": "^20.1.0", 39 | "grunt-karma": "^2.0.0", 40 | "grunt-webpack": "^3.0.2", 41 | "karma": "^1.7.1", 42 | "karma-benchmark": "^0.7.1", 43 | "karma-benchmark-reporter": "^0.1.1", 44 | "karma-browserify": "^5.1.2", 45 | "karma-chai-plugins": "^0.9.0", 46 | "karma-chrome-launcher": "^2.2.0", 47 | "karma-firefox-launcher": "^1.0.1", 48 | "karma-mocha": "^1.3.0", 49 | "karma-mocha-reporter": "^2.2.5", 50 | "karma-webpack": "^2.0.6", 51 | "microtime": "^2.1.1", 52 | "mocha": "^4.0.1", 53 | "node-forge": "^0.7.1", 54 | "sha.js": "^2.4.9", 55 | "sweet.js": "^0.7.1", 56 | "sweetjs-loader": "^0.0.7", 57 | "watchify": "^3.9.0", 58 | "webpack": "^3.11.0", 59 | "webworkify-webpack": "^2.1.0" 60 | }, 61 | "author": "Sam Rijs", 62 | "license": "MIT", 63 | "readmeFilename": "README.md" 64 | } 65 | -------------------------------------------------------------------------------- /perf/benchmark.js: -------------------------------------------------------------------------------- 1 | const shajs = require('sha.js'); 2 | const forge = require('node-forge'); 3 | 4 | const Rusha = require('../dist/rusha.min.js'); 5 | 6 | suite('SHA1 4MB', () => { 7 | const bytes = new Uint8Array(2 * 1024 * 1024); 8 | 9 | benchmark('rusha', () => { 10 | Rusha.createHash().update(bytes).digest(); 11 | }); 12 | 13 | benchmark('sha.js', () => { 14 | shajs('sha1').update(bytes).digest(); 15 | }); 16 | 17 | benchmark('node-forge', () => { 18 | const md = forge.md.sha1.create(); 19 | md.update(bytes); 20 | md.digest(); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /src/conv.js: -------------------------------------------------------------------------------- 1 | /* eslint-env commonjs, browser */ 2 | 3 | let reader; 4 | if (typeof self !== 'undefined' && typeof self.FileReaderSync !== 'undefined') { 5 | reader = new self.FileReaderSync(); 6 | } 7 | 8 | // Convert a binary string and write it to the heap. 9 | // A binary string is expected to only contain char codes < 256. 10 | const convStr = (str, H8, H32, start, len, off) => { 11 | let i, om = off % 4, lm = (len + om) % 4, j = len - lm; 12 | switch (om) { 13 | case 0: H8[off] = str.charCodeAt(start+3); 14 | case 1: H8[off+1-(om<<1)|0] = str.charCodeAt(start+2); 15 | case 2: H8[off+2-(om<<1)|0] = str.charCodeAt(start+1); 16 | case 3: H8[off+3-(om<<1)|0] = str.charCodeAt(start); 17 | } 18 | if (len < lm + (4-om)) { 19 | return; 20 | } 21 | for (i = 4 - om; i < j; i = i + 4 | 0) { 22 | H32[off+i>>2] = str.charCodeAt(start+i) << 24 | 23 | str.charCodeAt(start+i+1) << 16 | 24 | str.charCodeAt(start+i+2) << 8 | 25 | str.charCodeAt(start+i+3); 26 | } 27 | switch (lm) { 28 | case 3: H8[off+j+1|0] = str.charCodeAt(start+j+2); 29 | case 2: H8[off+j+2|0] = str.charCodeAt(start+j+1); 30 | case 1: H8[off+j+3|0] = str.charCodeAt(start+j); 31 | } 32 | }; 33 | 34 | // Convert a buffer or array and write it to the heap. 35 | // The buffer or array is expected to only contain elements < 256. 36 | const convBuf = (buf, H8, H32, start, len, off) => { 37 | let i, om = off % 4, lm = (len + om) % 4, j = len - lm; 38 | switch (om) { 39 | case 0: H8[off] = buf[start + 3]; 40 | case 1: H8[off+1-(om<<1)|0] = buf[start+2]; 41 | case 2: H8[off+2-(om<<1)|0] = buf[start+1]; 42 | case 3: H8[off+3-(om<<1)|0] = buf[start]; 43 | } 44 | if (len < lm + (4-om)) { 45 | return; 46 | } 47 | for (i = 4 - om; i < j; i = i + 4 | 0) { 48 | H32[off+i>>2|0] = buf[start+i] << 24 | 49 | buf[start+i+1] << 16 | 50 | buf[start+i+2] << 8 | 51 | buf[start+i+3]; 52 | } 53 | switch (lm) { 54 | case 3: H8[off+j+1|0] = buf[start+j+2]; 55 | case 2: H8[off+j+2|0] = buf[start+j+1]; 56 | case 1: H8[off+j+3|0] = buf[start+j]; 57 | } 58 | }; 59 | 60 | const convBlob = (blob, H8, H32, start, len, off) => { 61 | let i, om = off % 4, lm = (len + om) % 4, j = len - lm; 62 | const buf = new Uint8Array(reader.readAsArrayBuffer(blob.slice(start, start + len))); 63 | switch (om) { 64 | case 0: H8[off] = buf[3]; 65 | case 1: H8[off+1-(om<<1)|0] = buf[2]; 66 | case 2: H8[off+2-(om<<1)|0] = buf[1]; 67 | case 3: H8[off+3-(om<<1)|0] = buf[0]; 68 | } 69 | if (len < lm + (4-om)) { 70 | return; 71 | } 72 | for (i = 4 - om; i < j; i = i + 4 | 0) { 73 | H32[off+i>>2|0] = buf[i] << 24 | 74 | buf[i+1] << 16 | 75 | buf[i+2] << 8 | 76 | buf[i+3]; 77 | } 78 | switch (lm) { 79 | case 3: H8[off+j+1|0] = buf[j + 2]; 80 | case 2: H8[off+j+2|0] = buf[j + 1]; 81 | case 1: H8[off+j+3|0] = buf[j]; 82 | } 83 | }; 84 | 85 | module.exports = (data, H8, H32, start, len, off) => { 86 | if (typeof data === 'string') { 87 | return convStr(data, H8, H32, start, len, off); 88 | } 89 | if (data instanceof Array) { 90 | return convBuf(data, H8, H32, start, len, off); 91 | } 92 | // Safely doing a Buffer check using "this" to avoid Buffer polyfill to be included in the dist 93 | if (this && this.Buffer && this.Buffer.isBuffer(data)) { 94 | return convBuf(data, H8, H32, start, len, off); 95 | } 96 | if (data instanceof ArrayBuffer) { 97 | return convBuf(new Uint8Array(data), H8, H32, start, len, off); 98 | } 99 | if (data.buffer instanceof ArrayBuffer) { 100 | return convBuf(new Uint8Array(data.buffer, data.byteOffset, data.byteLength), H8, H32, start, len, off); 101 | } 102 | if (data instanceof Blob) { 103 | return convBlob(data, H8, H32, start, len, off); 104 | } 105 | throw new Error('Unsupported data type.'); 106 | }; 107 | -------------------------------------------------------------------------------- /src/core.sjs: -------------------------------------------------------------------------------- 1 | macro rol1 { rule { ($v:expr) } => { ($v << 1 | $v >>> 31) } } 2 | macro rol5 { rule { ($v:expr) } => { ($v << 5 | $v >>> 27) } } 3 | macro rol30 { rule { ($v:expr) } => { ($v << 30 | $v >>> 2) } } 4 | 5 | macro extended { 6 | rule { ($H, $j:expr) } => { 7 | rol1($H[$j-12>>2] ^ $H[$j-32>>2] ^ $H[$j-56>>2] ^ $H[$j-64>>2]) 8 | } 9 | } 10 | 11 | macro F0 { rule { ($b,$c,$d) } => { ($b & $c | ~$b & $d) } } 12 | macro F1 { rule { ($b,$c,$d) } => { ($b ^ $c ^ $d) }} 13 | macro F2 { rule { ($b,$c,$d) } => { ($b & $c | $b & $d | $c & $d) }} 14 | 15 | macro swap { 16 | rule { ($y0, $y1, $y2, $y3, $y4, $t0) } => { 17 | $y4 = $y3; 18 | $y3 = $y2; 19 | $y2 = rol30($y1); 20 | $y1 = $y0; 21 | $y0 = $t0; 22 | } 23 | } 24 | 25 | macro roundL { rule { ($y0, $f:expr) } => { (rol5($y0) + $f |0) } } 26 | macro roundR { rule { ($y4, $t1) } => { ($t1 + $y4 |0) } } 27 | 28 | // The low-level RushCore module provides the heart of Rusha, 29 | // a high-speed sha1 implementation working on an Int32Array heap. 30 | // At first glance, the implementation seems complicated, however 31 | // with the SHA1 spec at hand, it is obvious this almost a textbook 32 | // implementation that has a few functions hand-inlined and a few loops 33 | // hand-unrolled. 34 | module.exports = function RushaCore (stdlib, foreign, heap) { 35 | 'use asm'; 36 | 37 | var H = new stdlib.Int32Array(heap); 38 | 39 | function hash (k, x) { // k in bytes 40 | 41 | k = k|0; 42 | x = x|0; 43 | var i = 0, j = 0, 44 | y0 = 0, z0 = 0, y1 = 0, z1 = 0, 45 | y2 = 0, z2 = 0, y3 = 0, z3 = 0, 46 | y4 = 0, z4 = 0, t0 = 0, t1 = 0; 47 | 48 | y0 = H[x+320>>2]|0; 49 | y1 = H[x+324>>2]|0; 50 | y2 = H[x+328>>2]|0; 51 | y3 = H[x+332>>2]|0; 52 | y4 = H[x+336>>2]|0; 53 | 54 | for (i = 0; (i|0) < (k|0); i = i + 64 |0) { 55 | 56 | z0 = y0; 57 | z1 = y1; 58 | z2 = y2; 59 | z3 = y3; 60 | z4 = y4; 61 | 62 | for (j = 0; (j|0) < 64; j = j + 4 |0) { 63 | t1 = H[i+j>>2]|0; 64 | t0 = roundL(y0, F0(y1, y2, y3)) + (roundR(y4, t1) + 1518500249 |0) |0; 65 | swap(y0, y1, y2, y3, y4, t0) 66 | H[k+j>>2] = t1; 67 | } 68 | 69 | for (j = k + 64 |0; (j|0) < (k + 80 |0); j = j + 4 |0) { 70 | t1 = extended(H, j); 71 | t0 = roundL(y0, F0(y1, y2, y3)) + (roundR(y4, t1) + 1518500249 |0) |0; 72 | swap(y0, y1, y2, y3, y4, t0) 73 | H[j>>2] = t1; 74 | } 75 | 76 | for (j = k + 80 |0; (j|0) < (k + 160 |0); j = j + 4 |0) { 77 | t1 = extended(H, j); 78 | t0 = roundL(y0, F1(y1, y2, y3)) + (roundR(y4, t1) + 1859775393 |0) |0; 79 | swap(y0, y1, y2, y3, y4, t0) 80 | H[j>>2] = t1; 81 | } 82 | 83 | for (j = k + 160 |0; (j|0) < (k + 240 |0); j = j + 4 |0) { 84 | t1 = extended(H, j); 85 | t0 = roundL(y0, F2(y1, y2, y3)) + (roundR(y4, t1) - 1894007588 |0) |0; 86 | swap(y0, y1, y2, y3, y4, t0) 87 | H[j>>2] = t1; 88 | } 89 | 90 | for (j = k + 240 |0; (j|0) < (k + 320 |0); j = j + 4 |0) { 91 | t1 = extended(H, j); 92 | t0 = roundL(y0, F1(y1, y2, y3)) + (roundR(y4, t1) - 899497514 |0) |0; 93 | swap(y0, y1, y2, y3, y4, t0) 94 | H[j>>2] = t1; 95 | } 96 | 97 | y0 = y0 + z0 |0; 98 | y1 = y1 + z1 |0; 99 | y2 = y2 + z2 |0; 100 | y3 = y3 + z3 |0; 101 | y4 = y4 + z4 |0; 102 | 103 | } 104 | 105 | H[x+320>>2] = y0; 106 | H[x+324>>2] = y1; 107 | H[x+328>>2] = y2; 108 | H[x+332>>2] = y3; 109 | H[x+336>>2] = y4; 110 | 111 | } 112 | 113 | return {hash: hash}; 114 | }; 115 | -------------------------------------------------------------------------------- /src/hash.js: -------------------------------------------------------------------------------- 1 | /* eslint-env commonjs, browser */ 2 | 3 | const Rusha = require('./rusha'); 4 | const {toHex} = require('./utils'); 5 | 6 | class Hash { 7 | constructor() { 8 | this._rusha = new Rusha(); 9 | this._rusha.resetState(); 10 | } 11 | 12 | update(data) { 13 | this._rusha.append(data); 14 | return this; 15 | } 16 | 17 | digest(encoding) { 18 | const digest = this._rusha.rawEnd().buffer; 19 | if (!encoding) { 20 | return digest; 21 | } 22 | if (encoding === 'hex') { 23 | return toHex(digest); 24 | } 25 | throw new Error('unsupported digest encoding'); 26 | } 27 | 28 | get state() { 29 | return this._rusha.getState(); 30 | } 31 | 32 | set state(state) { 33 | this._rusha.setState(state); 34 | } 35 | } 36 | 37 | module.exports = () => { 38 | return new Hash(); 39 | }; 40 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-env commonjs, browser */ 2 | 3 | const work = require('webworkify-webpack'); 4 | const Rusha = require('./rusha'); 5 | const createHash = require('./hash'); 6 | const runWorker = require('./worker'); 7 | const { isDedicatedWorkerScope } = require('./utils'); 8 | 9 | const isRunningInDedicatedWorker = typeof self !== 'undefined' 10 | && isDedicatedWorkerScope(self); 11 | 12 | Rusha.disableWorkerBehaviour = isRunningInDedicatedWorker ? runWorker() : () => {}; 13 | 14 | Rusha.createWorker = () => { 15 | const worker = work(require.resolve('./worker')); 16 | const terminate = worker.terminate; 17 | worker.terminate = () => { 18 | URL.revokeObjectURL(worker.objectURL); 19 | terminate.call(worker); 20 | }; 21 | return worker; 22 | }; 23 | 24 | Rusha.createHash = createHash; 25 | 26 | module.exports = Rusha; -------------------------------------------------------------------------------- /src/rusha.js: -------------------------------------------------------------------------------- 1 | /* eslint-env commonjs, browser */ 2 | 3 | const RushaCore = require('./core.sjs'); 4 | const {toHex, ceilHeapSize} = require('./utils'); 5 | const conv = require('./conv'); 6 | 7 | // Calculate the length of buffer that the sha1 routine uses 8 | // including the padding. 9 | const padlen = (len) => { 10 | for (len += 9; len % 64 > 0; len += 1); 11 | return len; 12 | }; 13 | 14 | const padZeroes = (bin, len) => { 15 | const h8 = new Uint8Array(bin.buffer); 16 | const om = len % 4, align = len - om; 17 | switch (om) { 18 | case 0: h8[align + 3] = 0; 19 | case 1: h8[align + 2] = 0; 20 | case 2: h8[align + 1] = 0; 21 | case 3: h8[align + 0] = 0; 22 | } 23 | for (let i = (len >> 2) + 1; i < bin.length; i++) { 24 | bin[i] = 0; 25 | } 26 | }; 27 | 28 | const padData = (bin, chunkLen, msgLen) => { 29 | bin[chunkLen>>2] |= 0x80 << (24 - (chunkLen % 4 << 3)); 30 | // To support msgLen >= 2 GiB, use a float division when computing the 31 | // high 32-bits of the big-endian message length in bits. 32 | bin[(((chunkLen >> 2) + 2) & ~0x0f) + 14] = (msgLen / (1 << 29)) |0; 33 | bin[(((chunkLen >> 2) + 2) & ~0x0f) + 15] = msgLen << 3; 34 | }; 35 | 36 | const getRawDigest = (heap, padMaxChunkLen) => { 37 | const io = new Int32Array(heap, padMaxChunkLen + 320, 5); 38 | const out = new Int32Array(5); 39 | const arr = new DataView(out.buffer); 40 | arr.setInt32(0, io[0], false); 41 | arr.setInt32(4, io[1], false); 42 | arr.setInt32(8, io[2], false); 43 | arr.setInt32(12, io[3], false); 44 | arr.setInt32(16, io[4], false); 45 | return out; 46 | }; 47 | 48 | class Rusha { 49 | constructor(chunkSize) { 50 | chunkSize = chunkSize || 64 * 1024; 51 | if (chunkSize % 64 > 0) { 52 | throw new Error('Chunk size must be a multiple of 128 bit'); 53 | } 54 | this._offset = 0; 55 | this._maxChunkLen = chunkSize; 56 | this._padMaxChunkLen = padlen(chunkSize); 57 | // The size of the heap is the sum of: 58 | // 1. The padded input message size 59 | // 2. The extended space the algorithm needs (320 byte) 60 | // 3. The 160 bit state the algoritm uses 61 | this._heap = new ArrayBuffer(ceilHeapSize(this._padMaxChunkLen + 320 + 20)); 62 | this._h32 = new Int32Array(this._heap); 63 | this._h8 = new Int8Array(this._heap); 64 | this._core = new RushaCore({Int32Array: Int32Array}, {}, this._heap); 65 | } 66 | 67 | _initState(heap, padMsgLen) { 68 | this._offset = 0; 69 | const io = new Int32Array(heap, padMsgLen + 320, 5); 70 | io[0] = 1732584193; 71 | io[1] = -271733879; 72 | io[2] = -1732584194; 73 | io[3] = 271733878; 74 | io[4] = -1009589776; 75 | } 76 | 77 | _padChunk(chunkLen, msgLen) { 78 | const padChunkLen = padlen(chunkLen); 79 | const view = new Int32Array(this._heap, 0, padChunkLen >> 2); 80 | padZeroes(view, chunkLen); 81 | padData(view, chunkLen, msgLen); 82 | return padChunkLen; 83 | } 84 | 85 | _write(data, chunkOffset, chunkLen, off) { 86 | conv(data, this._h8, this._h32, chunkOffset, chunkLen, off || 0); 87 | } 88 | 89 | _coreCall(data, chunkOffset, chunkLen, msgLen, finalize) { 90 | let padChunkLen = chunkLen; 91 | this._write(data, chunkOffset, chunkLen); 92 | if (finalize) { 93 | padChunkLen = this._padChunk(chunkLen, msgLen); 94 | } 95 | this._core.hash(padChunkLen, this._padMaxChunkLen); 96 | } 97 | 98 | rawDigest(str) { 99 | const msgLen = str.byteLength || str.length || str.size || 0; 100 | this._initState(this._heap, this._padMaxChunkLen); 101 | let chunkOffset = 0, chunkLen = this._maxChunkLen; 102 | for (chunkOffset = 0; msgLen > chunkOffset + chunkLen; chunkOffset += chunkLen) { 103 | this._coreCall(str, chunkOffset, chunkLen, msgLen, false); 104 | } 105 | this._coreCall(str, chunkOffset, msgLen - chunkOffset, msgLen, true); 106 | return getRawDigest(this._heap, this._padMaxChunkLen); 107 | } 108 | 109 | digest(str) { 110 | return toHex(this.rawDigest(str).buffer); 111 | } 112 | 113 | digestFromString(str) { 114 | return this.digest(str); 115 | } 116 | 117 | digestFromBuffer(str) { 118 | return this.digest(str); 119 | } 120 | 121 | digestFromArrayBuffer(str) { 122 | return this.digest(str); 123 | } 124 | 125 | resetState() { 126 | this._initState(this._heap, this._padMaxChunkLen); 127 | return this; 128 | } 129 | 130 | append(chunk) { 131 | let chunkOffset = 0; 132 | let chunkLen = chunk.byteLength || chunk.length || chunk.size || 0; 133 | let turnOffset = this._offset % this._maxChunkLen; 134 | let inputLen; 135 | 136 | this._offset += chunkLen; 137 | while (chunkOffset < chunkLen) { 138 | inputLen = Math.min(chunkLen - chunkOffset, this._maxChunkLen - turnOffset); 139 | this._write(chunk, chunkOffset, inputLen, turnOffset); 140 | turnOffset += inputLen; 141 | chunkOffset += inputLen; 142 | if (turnOffset === this._maxChunkLen) { 143 | this._core.hash(this._maxChunkLen, this._padMaxChunkLen); 144 | turnOffset = 0; 145 | } 146 | } 147 | return this; 148 | } 149 | 150 | getState() { 151 | const turnOffset = this._offset % this._maxChunkLen; 152 | let heap; 153 | if (!turnOffset) { 154 | const io = new Int32Array(this._heap, this._padMaxChunkLen + 320, 5); 155 | heap = io.buffer.slice(io.byteOffset, io.byteOffset + io.byteLength); 156 | } else { 157 | heap = this._heap.slice(0); 158 | } 159 | return { 160 | offset: this._offset, 161 | heap: heap 162 | }; 163 | } 164 | 165 | setState(state) { 166 | this._offset = state.offset; 167 | if (state.heap.byteLength === 20) { 168 | const io = new Int32Array(this._heap, this._padMaxChunkLen + 320, 5); 169 | io.set(new Int32Array(state.heap)); 170 | } else { 171 | this._h32.set(new Int32Array(state.heap)); 172 | } 173 | return this; 174 | } 175 | 176 | rawEnd() { 177 | const msgLen = this._offset; 178 | const chunkLen = msgLen % this._maxChunkLen; 179 | const padChunkLen = this._padChunk(chunkLen, msgLen); 180 | this._core.hash(padChunkLen, this._padMaxChunkLen); 181 | const result = getRawDigest(this._heap, this._padMaxChunkLen); 182 | this._initState(this._heap, this._padMaxChunkLen); 183 | return result; 184 | } 185 | 186 | end() { 187 | return toHex(this.rawEnd().buffer); 188 | } 189 | } 190 | 191 | module.exports = Rusha; 192 | module.exports._core = RushaCore; 193 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | /* eslint-env commonjs, browser */ 2 | 3 | // 4 | // toHex 5 | // 6 | 7 | const precomputedHex = new Array(256); 8 | for (let i = 0; i < 256; i++) { 9 | precomputedHex[i] = (i < 0x10 ? '0' : '') + i.toString(16); 10 | } 11 | 12 | module.exports.toHex = (arrayBuffer) => { 13 | const binarray = new Uint8Array(arrayBuffer); 14 | const res = new Array(arrayBuffer.byteLength); 15 | for (let i = 0; i < res.length; i++) { 16 | res[i] = precomputedHex[binarray[i]]; 17 | } 18 | return res.join(''); 19 | }; 20 | 21 | // 22 | // ceilHeapSize 23 | // 24 | 25 | module.exports.ceilHeapSize = (v) => { 26 | // The asm.js spec says: 27 | // The heap object's byteLength must be either 28 | // 2^n for n in [12, 24) or 2^24 * n for n ≥ 1. 29 | // Also, byteLengths smaller than 2^16 are deprecated. 30 | let p = 0; 31 | // If v is smaller than 2^16, the smallest possible solution 32 | // is 2^16. 33 | if (v <= 65536) return 65536; 34 | // If v < 2^24, we round up to 2^n, 35 | // otherwise we round up to 2^24 * n. 36 | if (v < 16777216) { 37 | for (p = 1; p < v; p = p << 1); 38 | } else { 39 | for (p = 16777216; p < v; p += 16777216); 40 | } 41 | return p; 42 | }; 43 | 44 | // 45 | // isDedicatedWorkerScope 46 | // 47 | 48 | module.exports.isDedicatedWorkerScope = (self) => { 49 | const isRunningInWorker = 'WorkerGlobalScope' in self 50 | && self instanceof self.WorkerGlobalScope; 51 | const isRunningInSharedWorker = 'SharedWorkerGlobalScope' in self 52 | && self instanceof self.SharedWorkerGlobalScope; 53 | const isRunningInServiceWorker = 'ServiceWorkerGlobalScope' in self 54 | && self instanceof self.ServiceWorkerGlobalScope; 55 | 56 | // Detects whether we run inside a dedicated worker or not. 57 | // 58 | // We can't just check for `DedicatedWorkerGlobalScope`, since IE11 59 | // has a bug where it only supports `WorkerGlobalScope`. 60 | // 61 | // Therefore, we consider us as running inside a dedicated worker 62 | // when we are running inside a worker, but not in a shared or service worker. 63 | // 64 | // When new types of workers are introduced, we will need to adjust this code. 65 | return isRunningInWorker 66 | && !isRunningInSharedWorker 67 | && !isRunningInServiceWorker; 68 | }; 69 | -------------------------------------------------------------------------------- /src/worker.js: -------------------------------------------------------------------------------- 1 | /* eslint-env commonjs, worker */ 2 | 3 | module.exports = () => { 4 | const Rusha = require('./rusha'); 5 | 6 | const hashData = (hasher, data, cb) => { 7 | try { 8 | return cb(null, hasher.digest(data)); 9 | } catch (e) { 10 | return cb(e); 11 | } 12 | }; 13 | 14 | const hashFile = (hasher, readTotal, blockSize, file, cb) => { 15 | const reader = new self.FileReader(); 16 | reader.onloadend = function onloadend () { 17 | if (reader.error) { 18 | return cb(reader.error); 19 | } 20 | const buffer = reader.result; 21 | readTotal += reader.result.byteLength; 22 | try { 23 | hasher.append(buffer); 24 | } 25 | catch (e) { 26 | cb(e); 27 | return; 28 | } 29 | if (readTotal < file.size) { 30 | hashFile(hasher, readTotal, blockSize, file, cb); 31 | } else { 32 | cb(null, hasher.end()); 33 | } 34 | }; 35 | reader.readAsArrayBuffer(file.slice(readTotal, readTotal + blockSize)); 36 | }; 37 | 38 | let workerBehaviourEnabled = true; 39 | 40 | self.onmessage = (event) => { 41 | if (!workerBehaviourEnabled) { 42 | return; 43 | } 44 | 45 | const data = event.data.data, file = event.data.file, id = event.data.id; 46 | if (typeof id === 'undefined') return; 47 | if (!file && !data) return; 48 | const blockSize = event.data.blockSize || (4 * 1024 * 1024); 49 | const hasher = new Rusha(blockSize); 50 | hasher.resetState(); 51 | const done = (err, hash) => { 52 | if (!err) { 53 | self.postMessage({id: id, hash: hash}); 54 | } else { 55 | self.postMessage({id: id, error: err.name}); 56 | } 57 | }; 58 | if (data) hashData(hasher, data, done); 59 | if (file) hashFile(hasher, 0, blockSize, file, done); 60 | }; 61 | 62 | return () => { 63 | workerBehaviourEnabled = false; 64 | }; 65 | }; 66 | -------------------------------------------------------------------------------- /test/compat/require.js: -------------------------------------------------------------------------------- 1 | describe('require("rusha.min.js")', () => { 2 | it('does not pollute the global namespace', () => { 3 | const Rusha = require('../../dist/rusha.min.js'); 4 | expect(typeof window.Rusha).to.equal('undefined'); 5 | expect(typeof global.Rusha).to.equal('undefined'); 6 | }); 7 | 8 | it('provides compatibility with Rusha in-process', () => { 9 | const Rusha = require('../../dist/rusha.min.js'); 10 | const digest = Rusha.createHash().update('abc').digest('hex'); 11 | expect(digest).to.equal('a9993e364706816aba3e25717850c26c9cd0d89d'); 12 | }); 13 | 14 | it('provides compatibility with Rusha worker', () => { 15 | const Rusha = require('../../dist/rusha.min.js'); 16 | 17 | const promise = new Promise((resolve, reject) => { 18 | const worker = Rusha.createWorker(); 19 | worker.onmessage = (e) => { 20 | worker.terminate(); 21 | if (e.data.error) { 22 | reject(e.data.error); 23 | } else { 24 | resolve(e.data.hash); 25 | } 26 | }; 27 | worker.postMessage({id: 0, data: 'abc'}); 28 | }); 29 | 30 | return expect(promise).to.eventually.equal('a9993e364706816aba3e25717850c26c9cd0d89d'); 31 | }); 32 | }); 33 | 34 | describe('require("rusha.js")', () => { 35 | it('does not pollute the global namespace', () => { 36 | const Rusha = require('../../dist/rusha.js'); 37 | expect(typeof window.Rusha).to.equal('undefined'); 38 | expect(typeof global.Rusha).to.equal('undefined'); 39 | }); 40 | 41 | it('provides compatibility with Rusha in-process', () => { 42 | const Rusha = require('../../dist/rusha.js'); 43 | const digest = Rusha.createHash().update('abc').digest('hex'); 44 | expect(digest).to.equal('a9993e364706816aba3e25717850c26c9cd0d89d'); 45 | }); 46 | 47 | it('provides compatibility with Rusha worker', () => { 48 | const Rusha = require('../../dist/rusha.js'); 49 | 50 | const promise = new Promise((resolve, reject) => { 51 | const worker = Rusha.createWorker(); 52 | worker.onmessage = (e) => { 53 | worker.terminate(); 54 | if (e.data.error) { 55 | reject(e.data.error); 56 | } else { 57 | resolve(e.data.hash); 58 | } 59 | }; 60 | worker.postMessage({id: 0, data: 'abc'}); 61 | }); 62 | 63 | return expect(promise).to.eventually.equal('a9993e364706816aba3e25717850c26c9cd0d89d'); 64 | }); 65 | }); 66 | -------------------------------------------------------------------------------- /test/compat/vanilla_script.js: -------------------------------------------------------------------------------- 1 | describe('Vanilla Script Compatibility', () => { 2 | it('provides compatibility with Rusha in-process', () => { 3 | const digest = Rusha.createHash().update('abc').digest('hex'); 4 | expect(digest).to.equal('a9993e364706816aba3e25717850c26c9cd0d89d'); 5 | }); 6 | 7 | it('provides compatibility with Rusha worker', () => { 8 | const promise = new Promise((resolve, reject) => { 9 | const worker = Rusha.createWorker(); 10 | worker.onmessage = (e) => { 11 | worker.terminate(); 12 | if (e.data.error) { 13 | reject(e.data.error); 14 | } else { 15 | resolve(e.data.hash); 16 | } 17 | }; 18 | worker.postMessage({id: 0, data: 'abc'}); 19 | }); 20 | 21 | return expect(promise).to.eventually.equal('a9993e364706816aba3e25717850c26c9cd0d89d'); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /test/compat/vanilla_worker.js: -------------------------------------------------------------------------------- 1 | describe('Vanilla Worker Compatibility', () => { 2 | it('provides compatibility with Rusha worker', () => { 3 | const promise = new Promise((resolve, reject) => { 4 | const worker = new Worker('/base/dist/rusha.min.js'); 5 | worker.onmessage = (e) => { 6 | worker.terminate(); 7 | if (e.data.error) { 8 | reject(e.data.error); 9 | } else { 10 | resolve(e.data.hash); 11 | } 12 | }; 13 | worker.postMessage({id: 0, data: 'abc'}); 14 | }); 15 | 16 | return expect(promise).to.eventually.equal('a9993e364706816aba3e25717850c26c9cd0d89d'); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /test/functional/digest.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | 5 | const asm = require('asm.js'); 6 | 7 | const Rusha = require('../../dist/rusha.js'); 8 | 9 | const assertBytesEqual = (buffer1, buffer2) => { 10 | const v1 = new Int8Array(buffer1); 11 | const v2 = new Int8Array(buffer2); 12 | assert.strictEqual(v1.length, v2.length, 'Buffers do not have the same length'); 13 | for (let i = 0; i < v1.length; i++) { 14 | assert.strictEqual(v1[i], v2[i], 'Item at ' + i + ' differs: ' + v1[i] + ' vs ' + v2[i]); 15 | } 16 | }; 17 | 18 | const digestAppendOneByOne = (input) => { 19 | let middleState; 20 | for (let i = 0, len = (input.byteLength || input.length); i < len; i++) { 21 | if (i !== 0){ 22 | r.setState(middleState); 23 | } else { 24 | r.resetState(); 25 | } 26 | middleState = r.append(input.slice(i, i + 1)).getState(); 27 | } 28 | return r.setState(middleState).end(); 29 | }; 30 | 31 | const r = new Rusha(); 32 | 33 | const abcString = 'abc'; 34 | let abcBuffer; 35 | const abcArray = [97, 98, 99]; 36 | const abcArrayBuffer = new Int8Array(abcArray).buffer; 37 | 38 | if (typeof Buffer === 'function') { 39 | abcBuffer = new Buffer('abc', 'ascii'); 40 | } else { 41 | abcBuffer = new Int8Array(abcArray); 42 | } 43 | 44 | const abcHashedInt32Array = new Int32Array(new Int8Array([0xA9, 0x99, 0x3E, 0x36, 0x47, 0x06, 0x81, 0x6A, 0xBA, 0x3E, 0x25, 0x71, 0x78, 0x50, 0xC2, 0x6C, 0x9C, 0xD0, 0xD8, 0x9D]).buffer); 45 | 46 | describe('Rusha', () => { 47 | it('is valid asm.js', () => { 48 | assert(asm.validate(Rusha._core.toString())); 49 | }); 50 | 51 | describe('digestAppendOneByOne', () => { 52 | it('returns hex string from string', () => { 53 | assert.strictEqual('a9993e364706816aba3e25717850c26c9cd0d89d', digestAppendOneByOne(abcString)); 54 | }); 55 | it('returns hex string from buffer', () => { 56 | assert.strictEqual('a9993e364706816aba3e25717850c26c9cd0d89d', digestAppendOneByOne(abcBuffer)); 57 | }); 58 | it('returns hex string from array', () => { 59 | assert.strictEqual('a9993e364706816aba3e25717850c26c9cd0d89d', digestAppendOneByOne(abcArray)); 60 | }); 61 | it('returns hex string from ArrayBuffer', () => { 62 | assert.strictEqual('a9993e364706816aba3e25717850c26c9cd0d89d', digestAppendOneByOne(abcArrayBuffer)); 63 | }); 64 | }); 65 | 66 | describe('digest', () => { 67 | it('returns hex string from string', () => { 68 | assert.strictEqual('a9993e364706816aba3e25717850c26c9cd0d89d', r.digest(abcString)); 69 | }); 70 | it('returns hex string from buffer', () => { 71 | assert.strictEqual('a9993e364706816aba3e25717850c26c9cd0d89d', r.digest(abcBuffer)); 72 | }); 73 | it('returns hex string from array', () => { 74 | assert.strictEqual('a9993e364706816aba3e25717850c26c9cd0d89d', r.digest(abcArray)); 75 | }); 76 | it('returns hex string from ArrayBuffer', () => { 77 | assert.strictEqual('a9993e364706816aba3e25717850c26c9cd0d89d', r.digest(abcArrayBuffer)); 78 | }); 79 | }); 80 | 81 | describe('digestFromString', () => { 82 | it('returns hex string from string', () => { 83 | assert.strictEqual('a9993e364706816aba3e25717850c26c9cd0d89d', r.digestFromString(abcString)); 84 | }); 85 | }); 86 | 87 | describe('digestFromBuffer', () => { 88 | it('returns hex string from buffer', () => { 89 | assert.strictEqual('a9993e364706816aba3e25717850c26c9cd0d89d', r.digestFromBuffer(abcBuffer)); 90 | }); 91 | it('returns hex string from array', () => { 92 | assert.strictEqual('a9993e364706816aba3e25717850c26c9cd0d89d', r.digestFromBuffer(abcArray)); 93 | }); 94 | }); 95 | 96 | describe('digestFromArrayBuffer', () => { 97 | it('returns hex string from ArrayBuffer', () => { 98 | assert.strictEqual('a9993e364706816aba3e25717850c26c9cd0d89d', r.digestFromArrayBuffer(abcArrayBuffer)); 99 | }); 100 | }); 101 | 102 | describe('rawDigest', () => { 103 | it('returns a sliced Int32Array', () => { 104 | assert.strictEqual(20, r.rawDigest(abcString).buffer.byteLength); 105 | }); 106 | it('returns Int32Array from string', () => { 107 | assertBytesEqual(abcHashedInt32Array, r.rawDigest(abcString)); 108 | }); 109 | it('returns Int32Array from buffer', () => { 110 | assertBytesEqual(abcHashedInt32Array, r.rawDigest(abcBuffer)); 111 | }); 112 | it('returns Int32Array from array', () => { 113 | assertBytesEqual(abcHashedInt32Array, r.rawDigest(abcArray)); 114 | }); 115 | it('returns Int32Array from ArrayBuffer', () => { 116 | assertBytesEqual(abcHashedInt32Array, r.rawDigest(abcArrayBuffer)); 117 | }); 118 | }); 119 | }); 120 | -------------------------------------------------------------------------------- /test/functional/hash.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | 5 | const Rusha = require('../../dist/rusha.js'); 6 | 7 | const assertBytesEqual = (buffer1, buffer2) => { 8 | const v1 = new Int8Array(buffer1); 9 | const v2 = new Int8Array(buffer2); 10 | assert.strictEqual(v1.length, v2.length, 'Buffers do not have the same length'); 11 | for (let i = 0; i < v1.length; i++) { 12 | assert.strictEqual(v1[i], v2[i], 'Item at ' + i + ' differs: ' + v1[i] + ' vs ' + v2[i]); 13 | } 14 | }; 15 | 16 | const digestAppendOneByOne = (input) => { 17 | const hash = Rusha.createHash(); 18 | for (let i = 0, len = (input.byteLength || input.length); i < len; i++) { 19 | hash.update(input.slice(i, i + 1)); 20 | } 21 | return hash.digest('hex'); 22 | }; 23 | 24 | const digestAppendOneByOneByGettingAndSettingState = (input) => { 25 | let state = Rusha.createHash().state; 26 | for (let i = 0, len = (input.byteLength || input.length); i < len; i++) { 27 | const hash = Rusha.createHash(); 28 | hash.state = state; 29 | state = hash.update(input.slice(i, i + 1)).state; 30 | } 31 | const hash = Rusha.createHash(); 32 | hash.state = state; 33 | return hash.digest('hex'); 34 | }; 35 | 36 | const abcString = 'abc'; 37 | let abcBuffer; 38 | const abcArray = [97, 98, 99]; 39 | const abcArrayBuffer = new Int8Array(abcArray).buffer; 40 | 41 | if (typeof Buffer === 'function') { 42 | abcBuffer = new Buffer('abc', 'ascii'); 43 | } else { 44 | abcBuffer = new Int8Array(abcArray); 45 | } 46 | 47 | const abcHashedBuffer = new Int8Array([0xA9, 0x99, 0x3E, 0x36, 0x47, 0x06, 0x81, 0x6A, 0xBA, 0x3E, 0x25, 0x71, 0x78, 0x50, 0xC2, 0x6C, 0x9C, 0xD0, 0xD8, 0x9D]).buffer; 48 | 49 | describe('Hash', () => { 50 | describe('digestAppendOneByOne', () => { 51 | it('returns hex string from string', () => { 52 | assert.strictEqual('a9993e364706816aba3e25717850c26c9cd0d89d', digestAppendOneByOne(abcString)); 53 | }); 54 | it('returns hex string from buffer', () => { 55 | assert.strictEqual('a9993e364706816aba3e25717850c26c9cd0d89d', digestAppendOneByOne(abcBuffer)); 56 | }); 57 | it('returns hex string from array', () => { 58 | assert.strictEqual('a9993e364706816aba3e25717850c26c9cd0d89d', digestAppendOneByOne(abcArray)); 59 | }); 60 | it('returns hex string from ArrayBuffer', () => { 61 | assert.strictEqual('a9993e364706816aba3e25717850c26c9cd0d89d', digestAppendOneByOne(abcArrayBuffer)); 62 | }); 63 | }); 64 | 65 | describe('digestAppendOneByOneByGettingAndSettingState', () => { 66 | it('returns hex string from string', () => { 67 | assert.strictEqual('a9993e364706816aba3e25717850c26c9cd0d89d', 68 | digestAppendOneByOneByGettingAndSettingState(abcString)); 69 | }); 70 | }); 71 | 72 | describe('hex digest', () => { 73 | it('returns hex string from string', () => { 74 | const digest = Rusha.createHash().update(abcString).digest('hex'); 75 | assert.strictEqual('a9993e364706816aba3e25717850c26c9cd0d89d', digest); 76 | }); 77 | it('returns hex string from buffer', () => { 78 | const digest = Rusha.createHash().update(abcBuffer).digest('hex'); 79 | assert.strictEqual('a9993e364706816aba3e25717850c26c9cd0d89d', digest); 80 | }); 81 | it('returns hex string from array', () => { 82 | const digest = Rusha.createHash().update(abcArray).digest('hex'); 83 | assert.strictEqual('a9993e364706816aba3e25717850c26c9cd0d89d', digest); 84 | }); 85 | it('returns hex string from ArrayBuffer', () => { 86 | const digest = Rusha.createHash().update(abcArrayBuffer).digest('hex'); 87 | assert.strictEqual('a9993e364706816aba3e25717850c26c9cd0d89d', digest); 88 | }); 89 | }); 90 | 91 | describe('raw digest', () => { 92 | it('returns an ArrayBuffer', () => { 93 | const digest = Rusha.createHash().update(abcString).digest(); 94 | assert(digest instanceof ArrayBuffer); 95 | assert.strictEqual(20, digest.byteLength); 96 | }); 97 | it('returns ArrayBuffer from string', () => { 98 | const digest = Rusha.createHash().update(abcString).digest(); 99 | assert(digest instanceof ArrayBuffer); 100 | assertBytesEqual(abcHashedBuffer, digest); 101 | }); 102 | it('returns ArrayBuffer from buffer', () => { 103 | const digest = Rusha.createHash().update(abcBuffer).digest(); 104 | assert(digest instanceof ArrayBuffer); 105 | assertBytesEqual(abcHashedBuffer, digest); 106 | }); 107 | it('returns ArrayBuffer from array', () => { 108 | const digest = Rusha.createHash().update(abcArray).digest(); 109 | assert(digest instanceof ArrayBuffer); 110 | assertBytesEqual(abcHashedBuffer, digest); 111 | }); 112 | it('returns ArrayBuffer from ArrayBuffer', () => { 113 | const digest = Rusha.createHash().update(abcArrayBuffer).digest(); 114 | assert(digest instanceof ArrayBuffer); 115 | assertBytesEqual(abcHashedBuffer, digest); 116 | }); 117 | }); 118 | }); 119 | -------------------------------------------------------------------------------- /test/functional/worker.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const fs = require('fs'); 5 | 6 | const Rusha = require('../../dist/rusha.js'); 7 | 8 | const hashInWorker = (createWorker, input) => { 9 | return new Promise((resolve, reject) => { 10 | const worker = createWorker(); 11 | worker.onmessage = (e) => { 12 | worker.terminate(); 13 | if (e.data.error) { 14 | reject(e.data.error); 15 | } else { 16 | resolve(e.data.hash); 17 | } 18 | }; 19 | worker.postMessage(Object.assign({id: 0}, input)); 20 | }); 21 | }; 22 | 23 | describe('Rusha Worker', () => { 24 | describe('createWorker', () => { 25 | it('spawns a new worker`', () => { 26 | const blob = new Blob([]); 27 | return expect(hashInWorker(Rusha.createWorker, {data: blob})) 28 | .to.eventually.equal('da39a3ee5e6b4b0d3255bfef95601890afd80709'); 29 | }); 30 | }); 31 | 32 | describe('automagic worker behaviour', () => { 33 | it('spawns when used by Worker constructor', () => { 34 | const workerSource = fs.readFileSync(__dirname + '/../../dist/rusha.min.js', 'utf8'); 35 | const workerBlob = new Blob([workerSource]); 36 | const workerURL = URL.createObjectURL(workerBlob); 37 | const blob = new Blob([]); 38 | return expect(hashInWorker(() => new Worker(workerURL), {data: blob})) 39 | .to.eventually.equal('da39a3ee5e6b4b0d3255bfef95601890afd80709'); 40 | }); 41 | 42 | it('can be disabled', (done) => { 43 | const workerSource = fs.readFileSync(__dirname + '/../../dist/rusha.min.js', 'utf8'); 44 | const workerBlob = new Blob([workerSource, 'Rusha.disableWorkerBehaviour();']); 45 | const workerURL = URL.createObjectURL(workerBlob); 46 | const rw = new Worker(workerURL); 47 | const blob = new Blob([]); 48 | let gotReply = false 49 | rw.onmessage = (e) => { 50 | gotReply = true; 51 | }; 52 | rw.postMessage({id: 0, data: blob}); 53 | setTimeout(() => { 54 | assert(!gotReply); 55 | done(); 56 | }, 1000); 57 | }); 58 | }); 59 | 60 | describe('hashing', () => { 61 | it('1 kiB', () => { 62 | const zero1k = new Int8Array(1024); 63 | for (let i = 0; i < 1024; i++) { 64 | zero1k[i] = i; 65 | } 66 | const blob = new Blob([zero1k]); 67 | return expect(hashInWorker(Rusha.createWorker, {data: blob})) 68 | .to.eventually.equal('5b00669c480d5cffbdfa8bdba99561160f2d1b77'); 69 | }); 70 | 71 | it('1 kiB file', () => { 72 | const zero1k = new Int8Array(1024); 73 | for (let i = 0; i < 1024; i++) { 74 | zero1k[i] = i; 75 | } 76 | const blob = new Blob([zero1k]); 77 | return expect(hashInWorker(Rusha.createWorker, {file: blob})) 78 | .to.eventually.equal('5b00669c480d5cffbdfa8bdba99561160f2d1b77'); 79 | }); 80 | 81 | it('1 MiB', () => { 82 | const zero1M = new Int8Array(1024 * 1024); 83 | for (let i = 0; i < 1024 * 1024; i++) { 84 | zero1M[i] = i; 85 | } 86 | const blob = new Blob([zero1M]); 87 | return expect(hashInWorker(Rusha.createWorker, {data: blob})) 88 | .to.eventually.equal('ecfc8e86fdd83811f9cc9bf500993b63069923be'); 89 | }); 90 | 91 | it('1 MiB file', () => { 92 | const zero1M = new Int8Array(1024 * 1024); 93 | for (let i = 0; i < 1024 * 1024; i++) { 94 | zero1M[i] = i; 95 | } 96 | const blob = new Blob([zero1M]); 97 | return expect(hashInWorker(Rusha.createWorker, {file: blob})) 98 | .to.eventually.equal('ecfc8e86fdd83811f9cc9bf500993b63069923be'); 99 | }); 100 | 101 | it('10 MiB', () => { 102 | const zero1M = new Int8Array(1024 * 1024); 103 | for (let i = 0; i < 1024 * 1024; i++) { 104 | zero1M[i] = i; 105 | } 106 | const blob = new Blob(new Array(8).fill(zero1M)); 107 | return expect(hashInWorker(Rusha.createWorker, {data: blob})) 108 | .to.eventually.equal('2f9ae0e3d61b155cb367ef711bda8c5ee2924954'); 109 | }); 110 | 111 | it('10 MiB file', () => { 112 | const zero1M = new Int8Array(1024 * 1024); 113 | for (let i = 0; i < 1024 * 1024; i++) { 114 | zero1M[i] = i; 115 | } 116 | const blob = new Blob(new Array(8).fill(zero1M)); 117 | return expect(hashInWorker(Rusha.createWorker, {file: blob})) 118 | .to.eventually.equal('2f9ae0e3d61b155cb367ef711bda8c5ee2924954'); 119 | }); 120 | }); 121 | }); 122 | -------------------------------------------------------------------------------- /test/fuzz.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | 5 | const Rusha = require('../dist/rusha'); 6 | const {toHex} = require('../src/utils'); 7 | 8 | const generateRandomChunks = (count) => { 9 | const lengths = []; 10 | for (let i = 0; i < count; i++) { 11 | lengths.push(Math.ceil(Math.random() * 65536)); 12 | } 13 | const buffer = new ArrayBuffer(lengths.reduce((x, y) => x + y, 0)); 14 | const chunks = []; 15 | lengths.reduce((off, len) => { 16 | const data = new Uint8Array(buffer, off, len); 17 | window.crypto.getRandomValues(data); 18 | chunks.push(data); 19 | return off + len; 20 | }, 0); 21 | return {chunks, buffer}; 22 | }; 23 | 24 | describe('fuzzing using random Uint8Array', () => { 25 | for (let i = 0; i < 100; i++) { 26 | it(`chunk count = ${i}`, () => { 27 | const {chunks, buffer} = generateRandomChunks(i); 28 | const hash = Rusha.createHash(); 29 | for (const chunk of chunks) { 30 | hash.update(chunk); 31 | } 32 | const digest = hash.digest('hex'); 33 | 34 | return crypto.subtle.digest('SHA-1', buffer).then(referenceDigest => { 35 | assert.strictEqual(digest, toHex(referenceDigest)); 36 | }); 37 | }); 38 | } 39 | }); 40 | -------------------------------------------------------------------------------- /test/unit/conv.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const conv = require('../../src/conv'); 4 | 5 | describe('binary string conversion', () => { 6 | it('converts a full string with a zero offset', () => { 7 | const buf = new ArrayBuffer(16); 8 | conv('foobarbazquux42', new Int8Array(buf), new Int32Array(buf), 0, 15, 0); 9 | expect(Array.from(new Uint8Array(buf))).to.deep.equal( 10 | [98, 111, 111, 102, 97, 98, 114, 97, 117, 117, 113, 122, 0, 50, 52, 120] 11 | ); 12 | }); 13 | 14 | it('converts a string in chunks of one byte', () => { 15 | const buf = new ArrayBuffer(16); 16 | conv('f', new Int8Array(buf), new Int32Array(buf), 0, 1, 0); 17 | conv('o', new Int8Array(buf), new Int32Array(buf), 0, 1, 1); 18 | conv('o', new Int8Array(buf), new Int32Array(buf), 0, 1, 2); 19 | conv('b', new Int8Array(buf), new Int32Array(buf), 0, 1, 3); 20 | conv('a', new Int8Array(buf), new Int32Array(buf), 0, 1, 4); 21 | conv('r', new Int8Array(buf), new Int32Array(buf), 0, 1, 5); 22 | conv('b', new Int8Array(buf), new Int32Array(buf), 0, 1, 6); 23 | conv('a', new Int8Array(buf), new Int32Array(buf), 0, 1, 7); 24 | conv('z', new Int8Array(buf), new Int32Array(buf), 0, 1, 8); 25 | conv('q', new Int8Array(buf), new Int32Array(buf), 0, 1, 9); 26 | conv('u', new Int8Array(buf), new Int32Array(buf), 0, 1, 10); 27 | conv('u', new Int8Array(buf), new Int32Array(buf), 0, 1, 11); 28 | conv('x', new Int8Array(buf), new Int32Array(buf), 0, 1, 12); 29 | conv('4', new Int8Array(buf), new Int32Array(buf), 0, 1, 13); 30 | conv('2', new Int8Array(buf), new Int32Array(buf), 0, 1, 14); 31 | expect(Array.from(new Uint8Array(buf))).to.deep.equal( 32 | [98, 111, 111, 102, 97, 98, 114, 97, 117, 117, 113, 122, 0, 50, 52, 120] 33 | ); 34 | }); 35 | 36 | it('converts a string in chunks of two bytes', () => { 37 | const buf = new ArrayBuffer(16); 38 | conv('fo', new Int8Array(buf), new Int32Array(buf), 0, 2, 0); 39 | conv('ob', new Int8Array(buf), new Int32Array(buf), 0, 2, 2); 40 | conv('ar', new Int8Array(buf), new Int32Array(buf), 0, 2, 4); 41 | conv('ba', new Int8Array(buf), new Int32Array(buf), 0, 2, 6); 42 | conv('zq', new Int8Array(buf), new Int32Array(buf), 0, 2, 8); 43 | conv('uu', new Int8Array(buf), new Int32Array(buf), 0, 2, 10); 44 | conv('x4', new Int8Array(buf), new Int32Array(buf), 0, 2, 12); 45 | conv('2', new Int8Array(buf), new Int32Array(buf), 0, 1, 14); 46 | expect(Array.from(new Uint8Array(buf))).to.deep.equal( 47 | [98, 111, 111, 102, 97, 98, 114, 97, 117, 117, 113, 122, 0, 50, 52, 120] 48 | ); 49 | }); 50 | 51 | it('converts a string in chunks of three bytes', () => { 52 | const buf = new ArrayBuffer(16); 53 | conv('foo', new Int8Array(buf), new Int32Array(buf), 0, 3, 0); 54 | conv('bar', new Int8Array(buf), new Int32Array(buf), 0, 3, 3); 55 | conv('baz', new Int8Array(buf), new Int32Array(buf), 0, 3, 6); 56 | conv('quu', new Int8Array(buf), new Int32Array(buf), 0, 3, 9); 57 | conv('x42', new Int8Array(buf), new Int32Array(buf), 0, 3, 12); 58 | expect(Array.from(new Uint8Array(buf))).to.deep.equal( 59 | [98, 111, 111, 102, 97, 98, 114, 97, 117, 117, 113, 122, 0, 50, 52, 120] 60 | ); 61 | }); 62 | 63 | it('converts a string in chunks of five bytes', () => { 64 | const buf = new ArrayBuffer(16); 65 | conv('fooba', new Int8Array(buf), new Int32Array(buf), 0, 5, 0); 66 | conv('rbazq', new Int8Array(buf), new Int32Array(buf), 0, 5, 5); 67 | conv('uux42', new Int8Array(buf), new Int32Array(buf), 0, 5, 10); 68 | expect(Array.from(new Uint8Array(buf))).to.deep.equal( 69 | [98, 111, 111, 102, 97, 98, 114, 97, 117, 117, 113, 122, 0, 50, 52, 120] 70 | ); 71 | }); 72 | 73 | it('converts a string in chunks of seven bytes', () => { 74 | const buf = new ArrayBuffer(16); 75 | conv('foobarb', new Int8Array(buf), new Int32Array(buf), 0, 7, 0); 76 | conv('azquux4', new Int8Array(buf), new Int32Array(buf), 0, 7, 7); 77 | conv('2', new Int8Array(buf), new Int32Array(buf), 0, 1, 14); 78 | expect(Array.from(new Uint8Array(buf))).to.deep.equal( 79 | [98, 111, 111, 102, 97, 98, 114, 97, 117, 117, 113, 122, 0, 50, 52, 120] 80 | ); 81 | }); 82 | 83 | it('converts a string in chunks of eleven bytes', () => { 84 | const buf = new ArrayBuffer(16); 85 | conv('foobarbazqu', new Int8Array(buf), new Int32Array(buf), 0, 11, 0); 86 | conv('ux42', new Int8Array(buf), new Int32Array(buf), 0, 4, 11); 87 | expect(Array.from(new Uint8Array(buf))).to.deep.equal( 88 | [98, 111, 111, 102, 97, 98, 114, 97, 117, 117, 113, 122, 0, 50, 52, 120] 89 | ); 90 | }); 91 | }); 92 | 93 | describe('Array conversion', () => { 94 | it('converts a full array with a zero offset', () => { 95 | const buf = new ArrayBuffer(16); 96 | const arr = [102, 111, 111, 98, 97, 114, 98, 97, 122, 113, 117, 117, 120, 52, 50]; 97 | conv(arr, new Int8Array(buf), new Int32Array(buf), 0, 15, 0); 98 | expect(Array.from(new Uint8Array(buf))).to.deep.equal( 99 | [98, 111, 111, 102, 97, 98, 114, 97, 117, 117, 113, 122, 0, 50, 52, 120] 100 | ); 101 | }); 102 | }); 103 | 104 | describe('ArrayBuffer conversion', () => { 105 | it('converts a full array with a zero offset', () => { 106 | const buf = new ArrayBuffer(16); 107 | const arr = Uint8Array.from([102, 111, 111, 98, 97, 114, 98, 97, 122, 113, 117, 117, 120, 52, 50]); 108 | conv(arr.buffer, new Int8Array(buf), new Int32Array(buf), 0, 15, 0); 109 | expect(Array.from(new Uint8Array(buf))).to.deep.equal( 110 | [98, 111, 111, 102, 97, 98, 114, 97, 117, 117, 113, 122, 0, 50, 52, 120] 111 | ); 112 | }); 113 | }); 114 | 115 | describe('TypedArray conversion', () => { 116 | it('converts a full array with a zero offset', () => { 117 | const buf = new ArrayBuffer(16); 118 | const arr = Uint8Array.from([102, 111, 111, 98, 97, 114, 98, 97, 122, 113, 117, 117, 120, 52, 50]); 119 | conv(arr, new Int8Array(buf), new Int32Array(buf), 0, 15, 0); 120 | expect(Array.from(new Uint8Array(buf))).to.deep.equal( 121 | [98, 111, 111, 102, 97, 98, 114, 97, 117, 117, 113, 122, 0, 50, 52, 120] 122 | ); 123 | }); 124 | }); 125 | -------------------------------------------------------------------------------- /test/unit/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | 5 | const utils = require('../../src/utils'); 6 | 7 | describe('toHex', () => { 8 | it('converts an array buffer full of zeroes', () => { 9 | assert.strictEqual('0000000000000000', utils.toHex(new ArrayBuffer(8))); 10 | }); 11 | 12 | it('converts an array buffer of mixed digits', () => { 13 | const buf = new ArrayBuffer(8); 14 | const view = new Int8Array(buf); 15 | view[0] = 0; 16 | view[1] = 255; 17 | view[2] = 127; 18 | view[3] = 126; 19 | view[4] = 52; 20 | view[5] = 200; 21 | view[6] = 178; 22 | view[7] = 15; 23 | assert.strictEqual('00ff7f7e34c8b20f', utils.toHex(buf)); 24 | }); 25 | }); 26 | 27 | describe('ceilHeapSize', () => { 28 | it('rounds up to 2^16', () => { 29 | assert.strictEqual(65536, utils.ceilHeapSize(0)); 30 | assert.strictEqual(65536, utils.ceilHeapSize(255)); 31 | assert.strictEqual(65536, utils.ceilHeapSize(65535)); 32 | assert.strictEqual(65536, utils.ceilHeapSize(65536)); 33 | }); 34 | 35 | it('rounds up to 2^n while n < 24', () => { 36 | assert.strictEqual(131072, utils.ceilHeapSize(65537)); 37 | assert.strictEqual(131072, utils.ceilHeapSize(131000)); 38 | assert.strictEqual(131072, utils.ceilHeapSize(131072)); 39 | assert.strictEqual(262144, utils.ceilHeapSize(131073)); 40 | assert.strictEqual(262144, utils.ceilHeapSize(262144)); 41 | assert.strictEqual(524288, utils.ceilHeapSize(262145)); 42 | assert.strictEqual(524288, utils.ceilHeapSize(524288)); 43 | assert.strictEqual(1048576, utils.ceilHeapSize(524289)); 44 | assert.strictEqual(1048576, utils.ceilHeapSize(1048576)); 45 | assert.strictEqual(2097152, utils.ceilHeapSize(1048577)); 46 | assert.strictEqual(2097152, utils.ceilHeapSize(2097152)); 47 | assert.strictEqual(4194304, utils.ceilHeapSize(2097153)); 48 | assert.strictEqual(4194304, utils.ceilHeapSize(4194304)); 49 | assert.strictEqual(8388608, utils.ceilHeapSize(4194305)); 50 | assert.strictEqual(8388608, utils.ceilHeapSize(8388608)); 51 | }); 52 | 53 | it('otherwise rounds up to 2^24 * n', () => { 54 | assert.strictEqual(16777216, utils.ceilHeapSize(8388609)); 55 | assert.strictEqual(16777216, utils.ceilHeapSize(16777216)); 56 | assert.strictEqual(33554432, utils.ceilHeapSize(16777217)); 57 | }); 58 | }); 59 | 60 | describe('isDedicatedWorkerScope', () => { 61 | it('detects a standard dedicated worker scope', () => { 62 | class CustomWorkerScope {}; 63 | class CustomDedicatedWorkerScope extends CustomWorkerScope {}; 64 | const scope = new CustomDedicatedWorkerScope; 65 | scope.WorkerGlobalScope = CustomWorkerScope; 66 | scope.DedicatedWorkerGlobalScope = CustomDedicatedWorkerScope; 67 | assert.equal(true, utils.isDedicatedWorkerScope(scope)); 68 | }); 69 | 70 | it('detects a legacy dedicated worker scope (IE11)', () => { 71 | class CustomWorkerScope {}; 72 | const scope = new CustomWorkerScope; 73 | scope.WorkerGlobalScope = CustomWorkerScope; 74 | assert.equal(true, utils.isDedicatedWorkerScope(scope)); 75 | }); 76 | 77 | it('bails out on a shared worker scope', () => { 78 | class CustomWorkerScope {}; 79 | class CustomSharedWorkerScope extends CustomWorkerScope {}; 80 | const scope = new CustomSharedWorkerScope; 81 | scope.WorkerGlobalScope = CustomWorkerScope; 82 | scope.SharedWorkerGlobalScope = CustomSharedWorkerScope; 83 | assert.equal(false, utils.isDedicatedWorkerScope(scope)); 84 | }); 85 | 86 | it('bails out on a service worker scope', () => { 87 | class CustomWorkerScope {}; 88 | class CustomServiceWorkerScope extends CustomWorkerScope {}; 89 | const scope = new CustomServiceWorkerScope; 90 | scope.WorkerGlobalScope = CustomWorkerScope; 91 | scope.ServiceWorkerGlobalScope = CustomServiceWorkerScope; 92 | assert.equal(false, utils.isDedicatedWorkerScope(scope)); 93 | }); 94 | }); 95 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | entry: './src/index.js', 5 | output: { 6 | library: "Rusha", 7 | filename: "./dist/rusha.js", 8 | libraryTarget: "umd", 9 | }, 10 | resolve: { 11 | extensions: [".js", ".jsx"] 12 | }, 13 | module: { 14 | rules: [ 15 | { 16 | test: /\.jsx?$/, 17 | loader: ['babel-loader'], 18 | exclude: /(node_modules)/ 19 | }, 20 | { 21 | test: /\.sjs?$/, 22 | loader: ['sweetjs-loader'], 23 | exclude: /(node_modules)/ 24 | } 25 | ] 26 | } 27 | }; 28 | --------------------------------------------------------------------------------