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

Commit

Permalink
fix: async/await improvements
Browse files Browse the repository at this point in the history
Address comments on [parent PR](#37)
  • Loading branch information
dirkmc authored and hugomrdias committed Mar 14, 2019
1 parent 28023aa commit 626066c
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 105 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ const mh = await multihashing(buf, 'sha1')
const digest = await multihashing.digest(buf, 'sha1')

// Use `.createHash(...)` for the raw hash functions
const h = multihashing.createHash('sha1')
const digest = await h(buf)
const hash = multihashing.createHash('sha1')
const digest = await hash(buf)
```

## Examples
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,11 @@
},
"dependencies": {
"blakejs": "^1.1.0",
"err-code": "^1.1.2",
"js-sha3": "~0.8.0",
"multihashes": "~0.4.13",
"murmurhash3js-revisited": "^3.0.0"
"murmurhash3js-revisited": "^3.0.0",
"sinon": "^7.2.7"
},
"devDependencies": {
"aegir": "^18.0.3",
Expand Down
17 changes: 7 additions & 10 deletions src/blake.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,13 @@ const blake2s = {
digest: blake.blake2sFinal
}

const makeB2Hash = (size, hf) => (data) => {
return new Promise((resolve, reject) => {
try {
const ctx = hf.init(size, null)
hf.update(ctx, data)
resolve(Buffer.from(hf.digest(ctx)))
} catch (error) {
reject(error)
}
})
// Note that although this function doesn't do any asynchronous work, we mark
// the function as async because it must return a Promise to match the API
// for other functions that do perform asynchronous work (see sha.browser.js)
const makeB2Hash = (size, hf) => async (data) => {
const ctx = hf.init(size, null)
hf.update(ctx, data)
return Buffer.from(hf.digest(ctx))
}

module.exports = (table) => {
Expand Down
67 changes: 32 additions & 35 deletions src/crypto.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,42 +5,39 @@ const mur = require('murmurhash3js-revisited')
const sha = require('./sha')
const { fromNumberTo32BitBuf } = require('./utils')

const hash = (algorithm) => (data) => {
return new Promise((resolve, reject) => {
try {
switch (algorithm) {
case 'sha3-224':
return resolve(Buffer.from(sha3.sha3_224.arrayBuffer(data)))
case 'sha3-256':
return resolve(Buffer.from(sha3.sha3_256.arrayBuffer(data)))
case 'sha3-384':
return resolve(Buffer.from(sha3.sha3_384.arrayBuffer(data)))
case 'sha3-512':
return resolve(Buffer.from(sha3.sha3_512.arrayBuffer(data)))
case 'shake-128':
return resolve(Buffer.from(sha3.shake128.create(128).update(data).arrayBuffer()))
case 'shake-256':
return resolve(Buffer.from(sha3.shake256.create(256).update(data).arrayBuffer()))
case 'keccak-224':
return resolve(Buffer.from(sha3.keccak224.arrayBuffer(data)))
case 'keccak-256':
return resolve(Buffer.from(sha3.keccak256.arrayBuffer(data)))
case 'keccak-384':
return resolve(Buffer.from(sha3.keccak384.arrayBuffer(data)))
case 'keccak-512':
return resolve(Buffer.from(sha3.keccak512.arrayBuffer(data)))
case 'murmur3-128':
return resolve(Buffer.from(mur.x64.hash128(data), 'hex'))
case 'murmur3-32':
return resolve(fromNumberTo32BitBuf(mur.x86.hash32(data)))
// Note that although this function doesn't do any asynchronous work, we mark
// the function as async because it must return a Promise to match the API
// for other functions that do perform asynchronous work (see sha.browser.js)
const hash = (algorithm) => async (data) => {
switch (algorithm) {
case 'sha3-224':
return Buffer.from(sha3.sha3_224.arrayBuffer(data))
case 'sha3-256':
return Buffer.from(sha3.sha3_256.arrayBuffer(data))
case 'sha3-384':
return Buffer.from(sha3.sha3_384.arrayBuffer(data))
case 'sha3-512':
return Buffer.from(sha3.sha3_512.arrayBuffer(data))
case 'shake-128':
return Buffer.from(sha3.shake128.create(128).update(data).arrayBuffer())
case 'shake-256':
return Buffer.from(sha3.shake256.create(256).update(data).arrayBuffer())
case 'keccak-224':
return Buffer.from(sha3.keccak224.arrayBuffer(data))
case 'keccak-256':
return Buffer.from(sha3.keccak256.arrayBuffer(data))
case 'keccak-384':
return Buffer.from(sha3.keccak384.arrayBuffer(data))
case 'keccak-512':
return Buffer.from(sha3.keccak512.arrayBuffer(data))
case 'murmur3-128':
return Buffer.from(mur.x64.hash128(data), 'hex')
case 'murmur3-32':
return fromNumberTo32BitBuf(mur.x86.hash32(data))

default:
throw new TypeError(`${algorithm} is not a supported algorithm`)
}
} catch (error) {
return reject(error)
}
})
default:
throw new TypeError(`${algorithm} is not a supported algorithm`)
}
}

module.exports = {
Expand Down
52 changes: 24 additions & 28 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
'use strict'

const errcode = require('err-code')
const multihash = require('multihashes')
const crypto = require('./crypto')

/**
* Hash the given `buf` using the algorithm specified
* by `func`.
* Hash the given `buf` using the algorithm specified by `alg`.
* @param {Buffer} buf - The value to hash.
* @param {number|string} func - The algorithm to use.
* @param {number|string} alg - The algorithm to use eg 'sha1'
* @param {number} [length] - Optionally trim the result to this length.
* @returns {Promise<Buffer>}
*/
function Multihashing (buf, func, length) {
return Multihashing.digest(buf, func, length)
.then(digest => multihash.encode(digest, func, length))
async function Multihashing (buf, alg, length) {
const digest = await Multihashing.digest(buf, alg, length)
return multihash.encode(digest, alg, length)
}

/**
Expand All @@ -30,38 +30,34 @@ Multihashing.multihash = multihash

/**
* @param {Buffer} buf - The value to hash.
* @param {number|string} func - The algorithm to use.
* @param {number|string} alg - The algorithm to use eg 'sha1'
* @param {number} [length] - Optionally trim the result to this length.
* @returns {Promise}
* @returns {Promise<Buffer>}
*/
Multihashing.digest = (buf, func, length) => {
try {
return Multihashing.createHash(func)(buf)
.then(digest => {
if (length) {
return digest.slice(0, length)
}
return digest
})
} catch (err) {
return Promise.reject(err)
}
Multihashing.digest = async (buf, alg, length) => {
const hash = Multihashing.createHash(alg)
const digest = await hash(buf)
return length ? digest.slice(0, length) : digest
}

/**
* Creates a function that hashs with the provided algorithm
* Creates a function that hashes with the given algorithm
*
* @param {string|number} func
* @param {string|number} alg - The algorithm to use eg 'sha1'
*
* @returns {function} - The to `func` corresponding hash function.
* @returns {function} - The hash function corresponding to `alg`
*/
Multihashing.createHash = function (func) {
func = multihash.coerceCode(func)
if (!Multihashing.functions[func]) {
throw new Error('multihash function ' + func + ' not yet supported')
Multihashing.createHash = function (alg) {
if (!alg) {
throw errcode('hash algorithm must be specified', 'ERR_HASH_ALGORITHM_NOT_SPECIFIED')
}

alg = multihash.coerceCode(alg)
if (!Multihashing.functions[alg]) {
throw errcode(`multihash function '${alg}' not yet supported`, 'ERR_HASH_ALGORITHM_NOT_SUPPORTED')
}

return Multihashing.functions[func]
return Multihashing.functions[alg]
}

/**
Expand Down
13 changes: 6 additions & 7 deletions src/sha.browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,17 @@ module.exports = (algorithm) => {
)
}

return (data) => {
return async (data) => {
switch (algorithm) {
case 'sha1':
return crypto.subtle.digest({ name: 'SHA-1' }, data).then(Buffer.from)
return Buffer.from(await crypto.subtle.digest({ name: 'SHA-1' }, data))
case 'sha2-256':
return crypto.subtle.digest({ name: 'SHA-256' }, data).then(Buffer.from)
return Buffer.from(await crypto.subtle.digest({ name: 'SHA-256' }, data))
case 'sha2-512':
return crypto.subtle.digest({ name: 'SHA-512' }, data).then(Buffer.from)
return Buffer.from(await crypto.subtle.digest({ name: 'SHA-512' }, data))
case 'dbl-sha2-256': {
return crypto.subtle.digest({ name: 'SHA-256' }, data)
.then(d => crypto.subtle.digest({ name: 'SHA-256' }, d))
.then(Buffer.from)
const d = await crypto.subtle.digest({ name: 'SHA-256' }, data)
return Buffer.from(await crypto.subtle.digest({ name: 'SHA-256' }, d))
}
default:
throw new TypeError(`${algorithm} is not a supported algorithm`)
Expand Down
37 changes: 17 additions & 20 deletions src/sha.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,22 @@
'use strict'
const crypto = require('crypto')

module.exports = (algorithm) => (data) => {
return new Promise((resolve, reject) => {
try {
switch (algorithm) {
case 'sha1':
return resolve(crypto.createHash('sha1').update(data).digest())
case 'sha2-256':
return resolve(crypto.createHash('sha256').update(data).digest())
case 'sha2-512':
return resolve(crypto.createHash('sha512').update(data).digest())
case 'dbl-sha2-256': {
const first = crypto.createHash('sha256').update(data).digest()
return resolve(crypto.createHash('sha256').update(first).digest())
}
default:
throw new TypeError(`${algorithm} is not a supported algorithm`)
}
} catch (error) {
return reject(error)
// Note that although this function doesn't do any asynchronous work, we mark
// the function as async because it must return a Promise to match the API
// for other functions that do perform asynchronous work (see sha.browser.js)
module.exports = (algorithm) => async (data) => {
switch (algorithm) {
case 'sha1':
return crypto.createHash('sha1').update(data).digest()
case 'sha2-256':
return crypto.createHash('sha256').update(data).digest()
case 'sha2-512':
return crypto.createHash('sha512').update(data).digest()
case 'dbl-sha2-256': {
const first = crypto.createHash('sha256').update(data).digest()
return crypto.createHash('sha256').update(first).digest()
}
})
default:
throw new TypeError(`${algorithm} is not a supported algorithm`)
}
}
46 changes: 44 additions & 2 deletions test/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ const chai = require('chai')
const dirtyChai = require('dirty-chai')
chai.use(dirtyChai)
const expect = chai.expect
const sinon = require('sinon')

const multihashing = require('../src')
const fixtures = require('./fixtures/encodes')

describe('multihashing', () => {
fixtures.forEach((fixture) => {
for (const fixture of fixtures) {
const raw = fixture[0]
const func = fixture[1]
const encoded = fixture[2]
Expand All @@ -19,7 +20,7 @@ describe('multihashing', () => {
const digest = await multihashing(Buffer.from(raw), func)
expect(digest.toString('hex')).to.eql(encoded)
})
})
}

it('cuts the length', async () => {
const buf = Buffer.from('beep boop')
Expand All @@ -40,3 +41,44 @@ describe('multihashing', () => {
)
})
})

describe('error handling', () => {
const methods = {
multihashing: multihashing,
digest: multihashing.digest,
createHash: (buff, alg) => multihashing.createHash(alg)
}

for (const [name, fn] of Object.entries(methods)) {
describe(name, () => {
it('throws an error when there is no hashing algorithm specified', async () => {
const buf = Buffer.from('beep boop')

try {
await fn(buf)
} catch (err) {
expect(err).to.exist()
expect(err.code).to.eql('ERR_HASH_ALGORITHM_NOT_SPECIFIED')
return
}
expect.fail('Did not throw')
})

it('throws an error when the hashing algorithm is not supported', async () => {
const buf = Buffer.from('beep boop')

const stub = sinon.stub(require('multihashes'), 'coerceCode').returns('snake-oil')
try {
await fn(buf, 'snake-oil')
} catch (err) {
expect(err).to.exist()
expect(err.code).to.eql('ERR_HASH_ALGORITHM_NOT_SUPPORTED')
return
} finally {
stub.restore()
}
expect.fail('Did not throw')
})
})
}
})

0 comments on commit 626066c

Please sign in to comment.