├── .airtap.yml ├── .gitignore ├── .npmignore ├── .travis.yml ├── benchmark.js ├── browser.js ├── example.js ├── index.js ├── package.json ├── readme.md ├── rusha-worker-sha1.js └── test └── basic.js /.airtap.yml: -------------------------------------------------------------------------------- 1 | sauce_connect: true 2 | loopback: airtap.local 3 | browsers: 4 | - name: chrome 5 | version: latest 6 | - name: firefox 7 | version: latest 8 | - name: safari 9 | version: latest 10 | - name: microsoftedge 11 | version: latest 12 | - name: iphone 13 | version: latest 14 | - name: android 15 | version: latest 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .travis.yml 2 | .airtap.yml 3 | benchmark.js 4 | example.js 5 | test/ 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - lts/* 4 | addons: 5 | sauce_connect: true 6 | hosts: 7 | - airtap.local 8 | env: 9 | global: 10 | - secure: gKa38FGijv9dNyCgNsbG4+iyAOKlkYFGTln8zvOlSa3x5rOf+skyuO91bv/CQ3NPCjVXZXXhOje2OqH1lwn3XpaFUpAwfL587waUkYdDndPCd6BCGU6IGEduEzaj5A5IbClDkOxKgZaKj9Uk12rVu+ye/DTVvZTIqz9//ZFkD50= 11 | - secure: Gwpl/9A3NO2ehKxpgMqSd9YVPoNLMzjIBWfnUYfPxk0cbJbqCkGFaAr3DaZVgtOqnLQr6e3MWGudX8bNK0hfA3WLi6x7vBNw8Rt2180kl+YXTcQ+YKyeCvDmYchSvQIEgrY3q4Gz+43uGOW/xva7waH+LM8WqOp6n1uyUwIuddU= 12 | -------------------------------------------------------------------------------- /benchmark.js: -------------------------------------------------------------------------------- 1 | const benchmark = window.Benchmark = require('benchmark') 2 | const gitsha1 = require('git-sha1') 3 | const sha1 = require('./') 4 | 5 | const size = Math.pow(2, 24) 6 | const buffer = Buffer.alloc(size) 7 | 8 | benchmark.Suite() 9 | .add('sha1', function () { 10 | sha1(buffer, function () {}) 11 | }) 12 | .add('sha1.sync', function () { 13 | sha1.sync(buffer) 14 | }) 15 | .add('git-sha1', function () { 16 | gitsha1(buffer) 17 | }) 18 | .on('error', error) 19 | .on('cycle', cycle) 20 | .on('complete', complete) 21 | .run({ async: true }) 22 | 23 | function error (e) { 24 | console.error(e.target.error.stack) 25 | } 26 | 27 | function cycle (e) { 28 | console.log(String(e.target)) 29 | } 30 | 31 | function complete () { 32 | if (process.browser) { 33 | const crypto = window.crypto || window.msCrypto || {} 34 | let subtle = crypto.subtle || crypto.webkitSubtle 35 | try { subtle.digest({ name: 'sha-1' }, new Uint8Array()) } catch (err) { subtle = false } 36 | if (subtle) console.log('sha1 used WebCryptoAPI') 37 | } 38 | console.log('Fastest is ' + this.filter('fastest').map('name')) 39 | } 40 | -------------------------------------------------------------------------------- /browser.js: -------------------------------------------------------------------------------- 1 | /* global self */ 2 | 3 | const Rusha = require('rusha') 4 | const rushaWorkerSha1 = require('./rusha-worker-sha1') 5 | 6 | const rusha = new Rusha() 7 | const scope = typeof window !== 'undefined' ? window : self 8 | const crypto = scope.crypto || scope.msCrypto || {} 9 | let subtle = crypto.subtle || crypto.webkitSubtle 10 | 11 | function sha1sync (buf) { 12 | return rusha.digest(buf) 13 | } 14 | 15 | // Browsers throw if they lack support for an algorithm. 16 | // Promise will be rejected on non-secure origins. (http://goo.gl/lq4gCo) 17 | try { 18 | subtle.digest({ name: 'sha-1' }, new Uint8Array()).catch(function () { 19 | subtle = false 20 | }) 21 | } catch (err) { subtle = false } 22 | 23 | function sha1 (buf, cb) { 24 | if (!subtle) { 25 | if (typeof window !== 'undefined') { 26 | rushaWorkerSha1(buf, function onRushaWorkerSha1 (err, hash) { 27 | if (err) { 28 | // On error, fallback to synchronous method which cannot fail 29 | cb(sha1sync(buf)) 30 | return 31 | } 32 | 33 | cb(hash) 34 | }) 35 | } else { 36 | queueMicrotask(() => cb(sha1sync(buf))) 37 | } 38 | return 39 | } 40 | 41 | if (typeof buf === 'string') { 42 | buf = uint8array(buf) 43 | } 44 | 45 | subtle.digest({ name: 'sha-1' }, buf) 46 | .then(function succeed (result) { 47 | cb(hex(new Uint8Array(result))) 48 | }, 49 | function fail () { 50 | // On error, fallback to synchronous method which cannot fail 51 | cb(sha1sync(buf)) 52 | }) 53 | } 54 | 55 | function uint8array (s) { 56 | const l = s.length 57 | const array = new Uint8Array(l) 58 | for (let i = 0; i < l; i++) { 59 | array[i] = s.charCodeAt(i) 60 | } 61 | return array 62 | } 63 | 64 | function hex (buf) { 65 | const l = buf.length 66 | const chars = [] 67 | for (let i = 0; i < l; i++) { 68 | const bite = buf[i] 69 | chars.push((bite >>> 4).toString(16)) 70 | chars.push((bite & 0x0f).toString(16)) 71 | } 72 | return chars.join('') 73 | } 74 | 75 | module.exports = sha1 76 | module.exports.sync = sha1sync 77 | -------------------------------------------------------------------------------- /example.js: -------------------------------------------------------------------------------- 1 | const sha1 = require('./') 2 | 3 | // Because the WebCryptoAPI uses Promises (shudder), 4 | // you have to pass a callback if you want to take 5 | // advantage of its mad-sick performance. 6 | 7 | sha1('hey there', function (hash) { 8 | console.log('async:', hash) 9 | }) 10 | 11 | // However, if you don’t mind always using Rusha in 12 | // the browser, you can just call sha1.sync and be 13 | // done with it. 14 | 15 | console.log('sync:', sha1.sync('hey there')) 16 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const crypto = require('crypto') 2 | 3 | function sha1 (buf, cb) { 4 | const hash = sha1sync(buf) 5 | process.nextTick(function () { 6 | cb(hash) 7 | }) 8 | } 9 | 10 | function sha1sync (buf) { 11 | return crypto.createHash('sha1') 12 | .update(buf) 13 | .digest('hex') 14 | } 15 | 16 | module.exports = sha1 17 | module.exports.sync = sha1sync 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simple-sha1", 3 | "version": "3.1.0", 4 | "description": "A simple api for generating sha1 hashes in node and the browser.", 5 | "main": "index.js", 6 | "browser": "browser.js", 7 | "react-native": "index.js", 8 | "scripts": { 9 | "test": "standard && npm run test-node && npm run test-browser", 10 | "test-local": "npm run test-node && npm run test-browser-local", 11 | "test-node": "tape test/*.js", 12 | "test-browser": "airtap -- test/*.js", 13 | "test-browser-local": "airtap --local -- test/*.js", 14 | "example": "beefy example.js", 15 | "benchmark": "beefy benchmark.js" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git@github.com:michaelrhodes/simple-sha1.git" 20 | }, 21 | "keywords": [ 22 | "sha1", 23 | "rusha", 24 | "browser", 25 | "node", 26 | "browserify" 27 | ], 28 | "author": "Michael Rhodes", 29 | "license": "MIT", 30 | "bugs": { 31 | "url": "https://github.com/michaelrhodes/simple-sha1/issues" 32 | }, 33 | "homepage": "https://github.com/michaelrhodes/simple-sha1", 34 | "dependencies": { 35 | "queue-microtask": "^1.2.2", 36 | "rusha": "^0.8.13" 37 | }, 38 | "devDependencies": { 39 | "airtap": "^3.0.0", 40 | "beefy": "^2.1.8", 41 | "benchmark": "^2.1.4", 42 | "git-sha1": "^0.1.2", 43 | "standard": "^16.0.3", 44 | "tape": "^5.2.0" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # simple-sha1 2 | simple-sha1 wraps three fast SHA1 implementations, and exposes a simple api for generating hashes in node ([crypto](http://nodejs.org/api/crypto.html)) and the browser ([WebCryptoAPI](http://www.w3.org/TR/WebCryptoAPI/) || [Rusha](https://github.com/srijs/rusha)). 3 | 4 | [![Build status](https://travis-ci.org/michaelrhodes/simple-sha1.png?branch=master)](https://travis-ci.org/michaelrhodes/simple-sha1) 5 | 6 | [![Sauce Test Status](https://saucelabs.com/browser-matrix/simple-sha1.svg)](https://saucelabs.com/u/simple-sha1) 7 | 8 | ## install 9 | ```sh 10 | $ npm install simple-sha1 11 | ``` 12 | 13 | ## example 14 | ```js 15 | var sha1 = require('simple-sha1') 16 | 17 | // Because the WebCryptoAPI uses Promises (shudder), 18 | // you have to pass a callback if you want to take 19 | // advantage of its mad-sick performance. 20 | 21 | sha1('hey there', function (hash) { 22 | console.log(hash) 23 | > 6b1c01703b68cf9b35ab049385900b5c428651b6 24 | }) 25 | 26 | // However, if you don’t mind always using Rusha in 27 | // the browser, you can just call sha1.sync and be 28 | // done with it. 29 | 30 | console.log(sha1.sync('hey there')) 31 | > 6b1c01703b68cf9b35ab049385900b5c428651b6 32 | ``` 33 | 34 | ## license 35 | [MIT](http://opensource.org/licenses/MIT) 36 | -------------------------------------------------------------------------------- /rusha-worker-sha1.js: -------------------------------------------------------------------------------- 1 | const Rusha = require('rusha') 2 | 3 | let worker 4 | let nextTaskId 5 | let cbs 6 | 7 | function init () { 8 | worker = Rusha.createWorker() 9 | nextTaskId = 1 10 | cbs = {} // taskId -> cb 11 | 12 | worker.onmessage = function onRushaMessage (e) { 13 | const taskId = e.data.id 14 | const cb = cbs[taskId] 15 | delete cbs[taskId] 16 | 17 | if (e.data.error != null) { 18 | cb(new Error('Rusha worker error: ' + e.data.error)) 19 | } else { 20 | cb(null, e.data.hash) 21 | } 22 | } 23 | } 24 | 25 | function sha1 (buf, cb) { 26 | if (!worker) init() 27 | 28 | cbs[nextTaskId] = cb 29 | worker.postMessage({ id: nextTaskId, data: buf }) 30 | nextTaskId += 1 31 | } 32 | 33 | module.exports = sha1 34 | -------------------------------------------------------------------------------- /test/basic.js: -------------------------------------------------------------------------------- 1 | const sha1 = require('../') 2 | const test = require('tape') 3 | 4 | test('sha1', function (t) { 5 | t.plan(2) 6 | // buffer 7 | sha1(Buffer.from('hey there'), function (hash) { 8 | t.equal(hash, '6b1c01703b68cf9b35ab049385900b5c428651b6') 9 | }) 10 | // string 11 | sha1('hey there', function (hash) { 12 | t.equal(hash, '6b1c01703b68cf9b35ab049385900b5c428651b6') 13 | }) 14 | }) 15 | 16 | test('sha1.sync', function (t) { 17 | // buffer 18 | t.equal(sha1.sync(Buffer.from('hey there')), '6b1c01703b68cf9b35ab049385900b5c428651b6') 19 | // string 20 | t.equal(sha1.sync('hey there'), '6b1c01703b68cf9b35ab049385900b5c428651b6') 21 | t.end() 22 | }) 23 | --------------------------------------------------------------------------------