Skip to content
This repository has been archived by the owner on Aug 11, 2021. It is now read-only.

Commit

Permalink
feat: webcrypto and sha3
Browse files Browse the repository at this point in the history
In Node.js the underlying hashing functions have not changed, but the browser now uses `webcrypto` instead of JavaScript based methods for `SHA1`, `SHA2-256` and `SHA2-512`.

Also `SHA3` support was added in both Node.js and the browser.

BREAKING CHANGE:

The api was changed to be callback based, as webcrypto only exposes async methods.

Closes #10
  • Loading branch information
dignifiedquire committed Sep 22, 2016
1 parent f3b1478 commit af7e003
Show file tree
Hide file tree
Showing 6 changed files with 225 additions and 102 deletions.
46 changes: 17 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ This module just makes working with multihashes a bit nicer.
[js-multihash](//github.com/jbenet/js-multihash) is only for
encoding/decoding multihashes, and does not depend on other libs.
This module will depend on various implementations for each hash.
For now, it just uses `crypto`, but will use `sha3` and `blake2`, etc.
It currently uses `crypto` and [`sha3`](https://github.com/phusion/node-sha3) in Node.js. In the browser [`webcrypto`](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto) and [`browserify-sha3`](https://github.com/wanderer/browserify-sha3) are used.

## Table of Contents

Expand Down Expand Up @@ -80,16 +80,20 @@ You will need to use Node.js `Buffer` API compatible, if you are running inside
var multihashing = require('multihashing')
var buf = new Buffer('beep boop')

// by default returns a multihash.
multihashing(buf, 'sha1')
multihashing(buf, 'sha1', function (err, multihash) {
// by default calls back with a multihash.
})

// Use `.digest(...)` if you want only the hash digest (drops the prefix indicating the hash type).
multihashing.digest(buf, 'sha1')
multihashing.digest(buf, 'sha1', function (err , digest) {
// digest is the raw digest
})

// Use `.createHash(...)` for a `crypto.createHash` interface.
// Use `.createHash(...)` for the raw hash functions
var h = multihashing.createHash('sha1')
h.update(buf)
h.digest()
h(buf, (err, digest) => {
// digest is a buffer of the sha1 of buf
})
```

## Examples
Expand All @@ -100,39 +104,23 @@ h.digest()
> var multihashing = require('multihashing')
> var buf = new Buffer('beep boop')

> console.log(multihashing(buf, 'sha1'))
> multihashing(buf, 'sha1'), function (err, mh) { console.log(mh) })
// => <Buffer 11 14 7c 83 57 57 7f 51 d4 f0 a8 d3 93 aa 1a aa fb 28 86 3d 94 21>

> console.log(multihashing(buf, 'sha2-256'))
> multihashing(buf, 'sha2-256', function (err, mh) { console.log(mh) })
// => <Buffer 12 20 90 ea 68 8e 27 5d 58 05 67 32 50 32 49 2b 59 7b c7 72 21 c6 24 93 e7 63 30 b8 5d dd a1 91 ef 7c>

> console.log(multihashing(buf, 'sha2-512'))
> multihashing(buf, 'sha2-512'), function (err, mh) { console.log(mh) })
// => <Buffer 13 40 14 f3 01 f3 1b e2 43 f3 4c 56 68 93 78 83 77 1f a3 81 00 2f 1a aa 5f 31 b3 f7 8e 50 0b 66 ff 2f 4f 8e a5 e3 c9 f5 a6 1b d0 73 e2 45 2c 48 04 84 b0 ...>
```

### Raw digest output

```js
> var multihashing = require('multihashing')
> var buf = new Buffer('beep boop')

> console.log(multihashing.digest(buf, 'sha1'))
// => <SlowBuffer 7c 83 57 57 7f 51 d4 f0 a8 d3 93 aa 1a aa fb 28 86 3d 94 21>

> console.log(multihashing.digest(buf, 'sha2-256'))
// => <SlowBuffer 90 ea 68 8e 27 5d 58 05 67 32 50 32 49 2b 59 7b c7 72 21 c6 24 93 e7 63 30 b8 5d dd a1 91 ef 7c>

> console.log(multihashing.digest(buf, 'sha2-512'))
// => <SlowBuffer 14 f3 01 f3 1b e2 43 f3 4c 56 68 93 78 83 77 1f a3 81 00 2f 1a aa 5f 31 b3 f7 8e 50 0b 66 ff 2f 4f 8e a5 e3 c9 f5 a6 1b d0 73 e2 45 2c 48 04 84 b0 2e 03 ...>
```

## API

### `multihashing(buf, func, length)`
### `multihashing(buf, func, [length,] callback)`

### `digest(buf, func, length)`
### `digest(buf, func, [length,] callback)`

### `createHash(func, length)`
### `createHash(func)`

### `functions`

Expand Down
20 changes: 12 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@
"description": "multiple hash functions",
"main": "lib/index.js",
"jsnext:main": "src/index.js",
"browser": {
"./src/crypto.js": "./src/crypto-browser.js"
},
"scripts": {
"test": "aegir-test",
"test:browser": "aegir-test browser",
"test": "PHANTOM=off aegir-test",
"test:browser": "PHANTOM=off aegir-test browser",
"test:node": "aegir-test node",
"lint": "aegir-lint",
"release": "aegir-release",
"release-minor": "aegir-release minor",
"release-major": "aegir-release major",
"release": "PHANTOM=off aegir-release",
"release-minor": "PHANTOM=off aegir-release minor",
"release-major": "PHANTOM=off aegir-release major",
"build": "aegir-build",
"coverage": "aegir-coverage",
"coverage-publish": "aegir-coverage publish"
Expand All @@ -33,11 +36,12 @@
"url": "https://github.com/jbenet/js-multihashing/issues"
},
"dependencies": {
"browserify-sha3": "0.0.2",
"multihashes": "^0.2.0",
"webcrypto": "^0.1.0"
"sha3": "^1.2.0"
},
"devDependencies": {
"aegir": "^2.1.1",
"aegir": "^8.1.0",
"chai": "^3.5.0",
"pre-commit": "^1.1.2"
},
Expand All @@ -48,4 +52,4 @@
"Juan Batiz-Benet <juan@benet.ai>",
"dignifiedquire <dignifiedquire@gmail.com>"
]
}
}
66 changes: 66 additions & 0 deletions src/crypto-browser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
'use strict'

const SHA3 = require('browserify-sha3')

const webCrypto = getWebCrypto()

function getWebCrypto () {
if (typeof window !== 'undefined') {
if (window.crypto) {
return window.crypto.subtle || window.crypto.webkitSubtle
}

if (window.msCrypto) {
return window.msCrypto.subtle
}
}
}

function webCryptoHash (type) {
if (!webCrypto) {
throw new Error('Please use a browser with webcrypto support')
}

return (data, callback) => {
const res = webCrypto.digest({ name: type }, data)

if (typeof res.then !== 'function') { // IE11
res.onerror = () => {
callback(`Error hashing data using ${type}`)
}
res.oncomplete = (e) => {
callback(null, e.target.result)
}
return
}

return res.then((arrbuf) => {
callback(null, new Buffer(new Uint8Array(arrbuf)))
}).catch((err) => callback(err))
}
}

function sha1 (buf, callback) {
webCryptoHash('SHA-1')(buf, callback)
}

function sha2256 (buf, callback) {
webCryptoHash('SHA-256')(buf, callback)
}

function sha2512 (buf, callback) {
webCryptoHash('SHA-512')(buf, callback)
}

function sha3 (buf, callback) {
const d = new SHA3.SHA3Hash()
const digest = new Buffer(d.update(buf).digest('hex'), 'hex')
callback(null, digest)
}

module.exports = {
sha1: sha1,
sha2256: sha2256,
sha2512: sha2512,
sha3: sha3
}
32 changes: 32 additions & 0 deletions src/crypto.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
'use strict'

const SHA3 = require('sha3')
const crypto = require('crypto')

function sha1 (buf, callback) {
const digest = crypto.createHash('sha1').update(buf).digest()
callback(null, digest)
}

function sha2256 (buf, callback) {
const digest = crypto.createHash('sha256').update(buf).digest()
callback(null, digest)
}

function sha2512 (buf, callback) {
const digest = crypto.createHash('sha512').update(buf).digest()
callback(null, digest)
}

function sha3 (buf, callback) {
const d = new SHA3.SHA3Hash()
const digest = new Buffer(d.update(buf).digest('hex'), 'hex')
callback(null, digest)
}

module.exports = {
sha1: sha1,
sha2256: sha2256,
sha2512: sha2512,
sha3: sha3
}
80 changes: 50 additions & 30 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,55 +1,75 @@
'use strict'

const multihash = require('multihashes')
const crypto = require('webcrypto')
const crypto = require('./crypto')

const mh = module.exports = Multihashing
module.exports = Multihashing

mh.Buffer = Buffer // for browser things
function Multihashing (buf, func, length, callback) {
if (typeof length === 'function') {
callback = length
length = undefined
}

Multihashing.digest(buf, func, length, (err, digest) => {
if (err) {
return callback(err)
}

function Multihashing (buf, func, length) {
return multihash.encode(mh.digest(buf, func, length), func, length)
callback(null, multihash.encode(digest, func, length))
})
}

Multihashing.Buffer = Buffer // for browser things

// expose multihash itself, to avoid silly double requires.
mh.multihash = multihash
Multihashing.multihash = multihash

Multihashing.digest = function (buf, func, length, callback) {
if (typeof length === 'function') {
callback = length
length = undefined
}

mh.digest = function (buf, func, length) {
let digest = mh.createHash(func).update(buf).digest()
if (!callback) {
throw new Error('Missing callback')
}

let cb = callback
if (length) {
digest = digest.slice(0, length)
cb = (err, digest) => {
if (err) {
return callback(err)
}

callback(null, digest.slice(0, length))
}
}

return digest
let hash
try {
hash = Multihashing.createHash(func)
} catch (err) {
return cb(err)
}

hash(buf, cb)
}

mh.createHash = function (func, length) {
Multihashing.createHash = function (func) {
func = multihash.coerceCode(func)
if (!mh.functions[func]) {
if (!Multihashing.functions[func]) {
throw new Error('multihash function ' + func + ' not yet supported')
}

return mh.functions[func]()
return Multihashing.functions[func]
}

mh.functions = {
0x11: gsha1,
0x12: gsha2_256,
0x13: gsha2_512
// 0x14: gsha3 // not implemented yet
Multihashing.functions = {
0x11: crypto.sha1,
0x12: crypto.sha2256,
0x13: crypto.sha2512,
0x14: crypto.sha3
// 0x40: blake2b, // not implemented yet
// 0x41: blake2s, // not implemented yet
}

function gsha1 () {
return crypto.createHash('sha1')
}

function gsha2_256 () {
return crypto.createHash('sha256')
}

function gsha2_512 () {
return crypto.createHash('sha512')
}
Loading

0 comments on commit af7e003

Please sign in to comment.