Skip to content
This repository has been archived by the owner on Jun 15, 2023. It is now read-only.

refactor: use async/await instead of callbacks #37

Merged
merged 10 commits into from
Aug 16, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ logs
*.log

coverage
.nyc_output

# Runtime data
pids
Expand Down
8 changes: 4 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ stages:

node_js:
- '10'
- '12'

os:
- linux
Expand All @@ -20,23 +21,22 @@ jobs:
include:
- stage: check
script:
- npx aegir commitlint --travis
- npx aegir dep-check -- -i wrtc -i electron-webrtc
- npx aegir dep-check
- npm run lint

- stage: test
name: chrome
addons:
chrome: stable
script:
- npx aegir test -t browser
- npx aegir test -t browser -t webworker

- stage: test
name: firefox
addons:
firefox: latest
script:
- npx aegir test -t browser -- --browsers FirefoxHeadless
- npx aegir test -t browser -t webworker -- --browsers FirefoxHeadless

notifications:
email: false
40 changes: 18 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
# js-libp2p-keychain

[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io)
[![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/)
[![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23ipfs)
[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://protocol.ai)
[![](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/)
[![](https://img.shields.io/badge/freenode-%23libp2p-yellow.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23libp2p)
[![Discourse posts](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg)](https://discuss.libp2p.io)
[![standard-readme compliant](https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme)
[![Coverage Status](https://coveralls.io/repos/github/libp2p/js-libp2p-keychain/badge.svg?branch=master)](https://coveralls.io/github/libp2p/js-libp2p-keychain?branch=master)
[![Travis CI](https://travis-ci.org/libp2p/js-libp2p-keychain.svg?branch=master)](https://travis-ci.org/libp2p/js-libp2p-keychain)
[![Circle CI](https://circleci.com/gh/libp2p/js-libp2p-keychain.svg?style=svg)](https://circleci.com/gh/libp2p/js-libp2p-keychain)
[![](https://img.shields.io/codecov/c/github/libp2p/js-libp2p-keychain.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-keychain)
[![](https://img.shields.io/travis/libp2p/js-libp2p-keychain.svg?style=flat-square)](https://travis-ci.com/libp2p/js-libp2p-keychain)
[![Dependency Status](https://david-dm.org/libp2p/js-libp2p-keychain.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-keychain)
[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/feross/standard)
![](https://img.shields.io/badge/npm-%3E%3D3.0.0-orange.svg?style=flat-square)
![](https://img.shields.io/badge/Node.js-%3E%3D6.0.0-orange.svg?style=flat-square)

> A secure key chain for libp2p in JavaScript

Expand Down Expand Up @@ -55,23 +51,23 @@ const keychain = new Keychain(datastore, opts)

Managing a key

- `createKey (name, type, size, callback)`
- `renameKey (oldName, newName, callback)`
- `removeKey (name, callback)`
- `exportKey (name, password, callback)`
- `importKey (name, pem, password, callback)`
- `importPeer (name, peer, callback)`
- `async createKey (name, type, size)`
- `async renameKey (oldName, newName)`
- `async removeKey (name)`
- `async exportKey (name, password)`
- `async importKey (name, pem, password)`
- `async importPeer (name, peer)`

A naming service for a key

- `listKeys (callback)`
- `findKeyById (id, callback)`
- `findKeyByName (name, callback)`
- `async listKeys ()`
- `async findKeyById (id)`
- `async findKeyByName (name)`

Cryptographically protected messages

- `cms.encrypt (name, plain, callback)`
- `cms.decrypt (cmsData, callback)`
- `async cms.encrypt (name, plain)`
- `async cms.decrypt (cmsData)`

### KeyInfo

Expand Down Expand Up @@ -116,11 +112,11 @@ CMS, aka [PKCS #7](https://en.wikipedia.org/wiki/PKCS) and [RFC 5652](https://to

## Contribute

Feel free to join in. All welcome. Open an [issue](https://github.com/libp2p/js-libp2p-crypto/issues)!
Feel free to join in. All welcome. Open an [issue](https://github.com/libp2p/js-libp2p-keychain/issues)!

This repository falls under the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md).

[![](https://cdn.rawgit.com/jbenet/contribute-ipfs-gif/master/img/contribute.gif)](https://github.com/ipfs/community/blob/master/contributing.md)
[![](https://cdn.rawgit.com/jbenet/contribute-ipfs-gif/master/img/contribute.gif)](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md)

## License

Expand Down
34 changes: 15 additions & 19 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,19 @@
"scripts": {
"lint": "aegir lint",
"build": "aegir build",
"coverage": "nyc --reporter=text --reporter=lcov npm run test:node",
"test": "aegir test -t node -t browser",
"test:node": "aegir test -t node",
"test:browser": "aegir test -t browser",
"release": "aegir release",
"release-minor": "aegir release --type minor",
"release-major": "aegir release --type major",
"coverage": "aegir coverage",
"coverage-publish": "aegir coverage publish"
"release-major": "aegir release --type major"
jacobheun marked this conversation as resolved.
Show resolved Hide resolved
},
"pre-push": [
"lint",
"test"
"lint"
],
"engines": {
"node": ">=6.0.0",
"node": ">=10.0.0",
"npm": ">=3.0.0"
},
"repository": {
Expand All @@ -42,26 +40,24 @@
},
"homepage": "https://github.com/libp2p/js-libp2p-keychain#readme",
"dependencies": {
"async": "^2.6.2",
"err-code": "^1.1.2",
"interface-datastore": "~0.6.0",
"libp2p-crypto": "~0.16.1",
"err-code": "^2.0.0",
"interface-datastore": "^0.7.0",
"libp2p-crypto": "^0.17.0",
"merge-options": "^1.0.1",
"node-forge": "~0.7.6",
"pull-stream": "^3.6.9",
"node-forge": "^0.8.5",
"sanitize-filename": "^1.6.1"
},
"devDependencies": {
"aegir": "^18.2.1",
"aegir": "^20.0.0",
"chai": "^4.2.0",
"chai-string": "^1.5.0",
"datastore-fs": "~0.8.0",
"datastore-level": "~0.10.0",
"datastore-fs": "^0.9.0",
"datastore-level": "^0.12.1",
"dirty-chai": "^2.0.1",
"level-js": "^4.0.1",
"mocha": "^5.2.0",
"multihashes": "~0.4.14",
"peer-id": "~0.12.2",
"level": "^5.0.1",
"multihashes": "^0.4.15",
"peer-id": "^0.13.2",
"promisify-es6": "^1.0.3",
"rimraf": "^2.6.3"
},
"contributors": [
Expand Down
105 changes: 39 additions & 66 deletions src/cms.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
'use strict'

const setImmediate = require('async/setImmediate')
const series = require('async/series')
const detect = require('async/detect')
const waterfall = require('async/waterfall')
require('node-forge/lib/pkcs7')
require('node-forge/lib/pbe')
const forge = require('node-forge/lib/forge')
const util = require('./util')
const { certificateForKey, findAsync } = require('./util')
const errcode = require('err-code')

/**
Expand Down Expand Up @@ -40,44 +36,27 @@ class CMS {
*
* @param {string} name - The local key name.
* @param {Buffer} plain - The data to encrypt.
* @param {function(Error, Buffer)} callback
* @returns {undefined}
*/
encrypt (name, plain, callback) {
const self = this
const done = (err, result) => setImmediate(() => callback(err, result))

async encrypt (name, plain) {
if (!Buffer.isBuffer(plain)) {
return done(errcode(new Error('Plain data must be a Buffer'), 'ERR_INVALID_PARAMS'))
throw errcode(new Error('Plain data must be a Buffer'), 'ERR_INVALID_PARAMS')
}

series([
(cb) => self.keychain.findKeyByName(name, cb),
(cb) => self.keychain._getPrivateKey(name, cb)
], (err, results) => {
if (err) return done(err)

let key = results[0]
let pem = results[1]
try {
const privateKey = forge.pki.decryptRsaPrivateKey(pem, self.keychain._())
util.certificateForKey(key, privateKey, (err, certificate) => {
if (err) return callback(err)
const key = await this.keychain.findKeyByName(name)
const pem = await this.keychain._getPrivateKey(name)
const privateKey = forge.pki.decryptRsaPrivateKey(pem, this.keychain._())
const certificate = await certificateForKey(key, privateKey)

// create a p7 enveloped message
const p7 = forge.pkcs7.createEnvelopedData()
p7.addRecipient(certificate)
p7.content = forge.util.createBuffer(plain)
p7.encrypt()
// create a p7 enveloped message
const p7 = forge.pkcs7.createEnvelopedData()
p7.addRecipient(certificate)
p7.content = forge.util.createBuffer(plain)
p7.encrypt()

// convert message to DER
const der = forge.asn1.toDer(p7.toAsn1()).getBytes()
done(null, Buffer.from(der, 'binary'))
})
} catch (err) {
done(err)
}
})
// convert message to DER
const der = forge.asn1.toDer(p7.toAsn1()).getBytes()
return Buffer.from(der, 'binary')
}

/**
Expand All @@ -87,24 +66,20 @@ class CMS {
* exists, an Error is returned with the property 'missingKeys'. It is array of key ids.
*
* @param {Buffer} cmsData - The CMS encrypted data to decrypt.
* @param {function(Error, Buffer)} callback
* @returns {undefined}
*/
decrypt (cmsData, callback) {
const done = (err, result) => setImmediate(() => callback(err, result))

async decrypt (cmsData) {
if (!Buffer.isBuffer(cmsData)) {
return done(errcode(new Error('CMS data is required'), 'ERR_INVALID_PARAMS'))
throw errcode(new Error('CMS data is required'), 'ERR_INVALID_PARAMS')
}

const self = this
let cms
try {
const buf = forge.util.createBuffer(cmsData.toString('binary'))
const obj = forge.asn1.fromDer(buf)
cms = forge.pkcs7.messageFromAsn1(obj)
} catch (err) {
return done(errcode(new Error('Invalid CMS: ' + err.message), 'ERR_INVALID_CMS'))
throw errcode(new Error('Invalid CMS: ' + err.message), 'ERR_INVALID_CMS')
}

// Find a recipient whose key we hold. We only deal with recipient certs
Expand All @@ -118,31 +93,29 @@ class CMS {
keyId: r.issuer.find(a => a.shortName === 'CN').value
}
})
detect(
recipients,
(r, cb) => self.keychain.findKeyById(r.keyId, (err, info) => cb(null, !err && info)),
(err, r) => {
if (err) return done(err)
if (!r) {
const missingKeys = recipients.map(r => r.keyId)
err = errcode(new Error('Decryption needs one of the key(s): ' + missingKeys.join(', ')), 'ERR_MISSING_KEYS', {
missingKeys
})
return done(err)
}

waterfall([
(cb) => self.keychain.findKeyById(r.keyId, cb),
(key, cb) => self.keychain._getPrivateKey(key.name, cb)
], (err, pem) => {
if (err) return done(err)

const privateKey = forge.pki.decryptRsaPrivateKey(pem, self.keychain._())
cms.decrypt(r.recipient, privateKey)
done(null, Buffer.from(cms.content.getBytes(), 'binary'))
})
const r = await findAsync(recipients, async (recipient) => {
try {
const key = await this.keychain.findKeyById(recipient.keyId)
if (key) return true
} catch (err) {
return false
}
)
return false
})

if (!r) {
const missingKeys = recipients.map(r => r.keyId)
throw errcode(new Error('Decryption needs one of the key(s): ' + missingKeys.join(', ')), 'ERR_MISSING_KEYS', {
missingKeys
})
}

const key = await this.keychain.findKeyById(r.keyId)
const pem = await this.keychain._getPrivateKey(key.name)
const privateKey = forge.pki.decryptRsaPrivateKey(pem, this.keychain._())
cms.decrypt(r.recipient, privateKey)
return Buffer.from(cms.content.getBytes(), 'binary')
}
}

Expand Down
Loading