Skip to content

Commit dda315a

Browse files
refactor: use async/await instead of callbacks (#37)
BREAKING CHANGE: The api now uses async/await instead of callbacks. Co-Authored-By: Vasco Santos <[email protected]>
1 parent 717112b commit dda315a

12 files changed

+537
-696
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ logs
99
*.log
1010

1111
coverage
12+
.nyc_output
1213

1314
# Runtime data
1415
pids

.travis.yml

+4-4
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ stages:
77

88
node_js:
99
- '10'
10+
- '12'
1011

1112
os:
1213
- linux
@@ -20,23 +21,22 @@ jobs:
2021
include:
2122
- stage: check
2223
script:
23-
- npx aegir commitlint --travis
24-
- npx aegir dep-check -- -i wrtc -i electron-webrtc
24+
- npx aegir dep-check
2525
- npm run lint
2626

2727
- stage: test
2828
name: chrome
2929
addons:
3030
chrome: stable
3131
script:
32-
- npx aegir test -t browser
32+
- npx aegir test -t browser -t webworker
3333

3434
- stage: test
3535
name: firefox
3636
addons:
3737
firefox: latest
3838
script:
39-
- npx aegir test -t browser -- --browsers FirefoxHeadless
39+
- npx aegir test -t browser -t webworker -- --browsers FirefoxHeadless
4040

4141
notifications:
4242
email: false

README.md

+18-22
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
11
# js-libp2p-keychain
22

3-
[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io)
4-
[![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/)
5-
[![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23ipfs)
3+
[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://protocol.ai)
4+
[![](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/)
5+
[![](https://img.shields.io/badge/freenode-%23libp2p-yellow.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23libp2p)
66
[![Discourse posts](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg)](https://discuss.libp2p.io)
7-
[![standard-readme compliant](https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme)
8-
[![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)
9-
[![Travis CI](https://travis-ci.org/libp2p/js-libp2p-keychain.svg?branch=master)](https://travis-ci.org/libp2p/js-libp2p-keychain)
10-
[![Circle CI](https://circleci.com/gh/libp2p/js-libp2p-keychain.svg?style=svg)](https://circleci.com/gh/libp2p/js-libp2p-keychain)
7+
[![](https://img.shields.io/codecov/c/github/libp2p/js-libp2p-keychain.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-keychain)
8+
[![](https://img.shields.io/travis/libp2p/js-libp2p-keychain.svg?style=flat-square)](https://travis-ci.com/libp2p/js-libp2p-keychain)
119
[![Dependency Status](https://david-dm.org/libp2p/js-libp2p-keychain.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-keychain)
1210
[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/feross/standard)
13-
![](https://img.shields.io/badge/npm-%3E%3D3.0.0-orange.svg?style=flat-square)
14-
![](https://img.shields.io/badge/Node.js-%3E%3D6.0.0-orange.svg?style=flat-square)
1511

1612
> A secure key chain for libp2p in JavaScript
1713
@@ -55,23 +51,23 @@ const keychain = new Keychain(datastore, opts)
5551

5652
Managing a key
5753

58-
- `createKey (name, type, size, callback)`
59-
- `renameKey (oldName, newName, callback)`
60-
- `removeKey (name, callback)`
61-
- `exportKey (name, password, callback)`
62-
- `importKey (name, pem, password, callback)`
63-
- `importPeer (name, peer, callback)`
54+
- `async createKey (name, type, size)`
55+
- `async renameKey (oldName, newName)`
56+
- `async removeKey (name)`
57+
- `async exportKey (name, password)`
58+
- `async importKey (name, pem, password)`
59+
- `async importPeer (name, peer)`
6460

6561
A naming service for a key
6662

67-
- `listKeys (callback)`
68-
- `findKeyById (id, callback)`
69-
- `findKeyByName (name, callback)`
63+
- `async listKeys ()`
64+
- `async findKeyById (id)`
65+
- `async findKeyByName (name)`
7066

7167
Cryptographically protected messages
7268

73-
- `cms.encrypt (name, plain, callback)`
74-
- `cms.decrypt (cmsData, callback)`
69+
- `async cms.encrypt (name, plain)`
70+
- `async cms.decrypt (cmsData)`
7571

7672
### KeyInfo
7773

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

117113
## Contribute
118114

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

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

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

125121
## License
126122

package.json

+15-19
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,19 @@
77
"scripts": {
88
"lint": "aegir lint",
99
"build": "aegir build",
10+
"coverage": "nyc --reporter=text --reporter=lcov npm run test:node",
1011
"test": "aegir test -t node -t browser",
1112
"test:node": "aegir test -t node",
1213
"test:browser": "aegir test -t browser",
1314
"release": "aegir release",
1415
"release-minor": "aegir release --type minor",
15-
"release-major": "aegir release --type major",
16-
"coverage": "aegir coverage",
17-
"coverage-publish": "aegir coverage publish"
16+
"release-major": "aegir release --type major"
1817
},
1918
"pre-push": [
20-
"lint",
21-
"test"
19+
"lint"
2220
],
2321
"engines": {
24-
"node": ">=6.0.0",
22+
"node": ">=10.0.0",
2523
"npm": ">=3.0.0"
2624
},
2725
"repository": {
@@ -42,26 +40,24 @@
4240
},
4341
"homepage": "https://github.com/libp2p/js-libp2p-keychain#readme",
4442
"dependencies": {
45-
"async": "^2.6.2",
46-
"err-code": "^1.1.2",
47-
"interface-datastore": "~0.6.0",
48-
"libp2p-crypto": "~0.16.1",
43+
"err-code": "^2.0.0",
44+
"interface-datastore": "^0.7.0",
45+
"libp2p-crypto": "^0.17.0",
4946
"merge-options": "^1.0.1",
50-
"node-forge": "~0.7.6",
51-
"pull-stream": "^3.6.9",
47+
"node-forge": "^0.8.5",
5248
"sanitize-filename": "^1.6.1"
5349
},
5450
"devDependencies": {
55-
"aegir": "^18.2.1",
51+
"aegir": "^20.0.0",
5652
"chai": "^4.2.0",
5753
"chai-string": "^1.5.0",
58-
"datastore-fs": "~0.8.0",
59-
"datastore-level": "~0.10.0",
54+
"datastore-fs": "^0.9.0",
55+
"datastore-level": "^0.12.1",
6056
"dirty-chai": "^2.0.1",
61-
"level-js": "^4.0.1",
62-
"mocha": "^5.2.0",
63-
"multihashes": "~0.4.14",
64-
"peer-id": "~0.12.2",
57+
"level": "^5.0.1",
58+
"multihashes": "^0.4.15",
59+
"peer-id": "^0.13.2",
60+
"promisify-es6": "^1.0.3",
6561
"rimraf": "^2.6.3"
6662
},
6763
"contributors": [

src/cms.js

+39-66
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
11
'use strict'
22

3-
const setImmediate = require('async/setImmediate')
4-
const series = require('async/series')
5-
const detect = require('async/detect')
6-
const waterfall = require('async/waterfall')
73
require('node-forge/lib/pkcs7')
84
require('node-forge/lib/pbe')
95
const forge = require('node-forge/lib/forge')
10-
const util = require('./util')
6+
const { certificateForKey, findAsync } = require('./util')
117
const errcode = require('err-code')
128

139
/**
@@ -40,44 +36,27 @@ class CMS {
4036
*
4137
* @param {string} name - The local key name.
4238
* @param {Buffer} plain - The data to encrypt.
43-
* @param {function(Error, Buffer)} callback
4439
* @returns {undefined}
4540
*/
46-
encrypt (name, plain, callback) {
47-
const self = this
48-
const done = (err, result) => setImmediate(() => callback(err, result))
49-
41+
async encrypt (name, plain) {
5042
if (!Buffer.isBuffer(plain)) {
51-
return done(errcode(new Error('Plain data must be a Buffer'), 'ERR_INVALID_PARAMS'))
43+
throw errcode(new Error('Plain data must be a Buffer'), 'ERR_INVALID_PARAMS')
5244
}
5345

54-
series([
55-
(cb) => self.keychain.findKeyByName(name, cb),
56-
(cb) => self.keychain._getPrivateKey(name, cb)
57-
], (err, results) => {
58-
if (err) return done(err)
59-
60-
let key = results[0]
61-
let pem = results[1]
62-
try {
63-
const privateKey = forge.pki.decryptRsaPrivateKey(pem, self.keychain._())
64-
util.certificateForKey(key, privateKey, (err, certificate) => {
65-
if (err) return callback(err)
46+
const key = await this.keychain.findKeyByName(name)
47+
const pem = await this.keychain._getPrivateKey(name)
48+
const privateKey = forge.pki.decryptRsaPrivateKey(pem, this.keychain._())
49+
const certificate = await certificateForKey(key, privateKey)
6650

67-
// create a p7 enveloped message
68-
const p7 = forge.pkcs7.createEnvelopedData()
69-
p7.addRecipient(certificate)
70-
p7.content = forge.util.createBuffer(plain)
71-
p7.encrypt()
51+
// create a p7 enveloped message
52+
const p7 = forge.pkcs7.createEnvelopedData()
53+
p7.addRecipient(certificate)
54+
p7.content = forge.util.createBuffer(plain)
55+
p7.encrypt()
7256

73-
// convert message to DER
74-
const der = forge.asn1.toDer(p7.toAsn1()).getBytes()
75-
done(null, Buffer.from(der, 'binary'))
76-
})
77-
} catch (err) {
78-
done(err)
79-
}
80-
})
57+
// convert message to DER
58+
const der = forge.asn1.toDer(p7.toAsn1()).getBytes()
59+
return Buffer.from(der, 'binary')
8160
}
8261

8362
/**
@@ -87,24 +66,20 @@ class CMS {
8766
* exists, an Error is returned with the property 'missingKeys'. It is array of key ids.
8867
*
8968
* @param {Buffer} cmsData - The CMS encrypted data to decrypt.
90-
* @param {function(Error, Buffer)} callback
9169
* @returns {undefined}
9270
*/
93-
decrypt (cmsData, callback) {
94-
const done = (err, result) => setImmediate(() => callback(err, result))
95-
71+
async decrypt (cmsData) {
9672
if (!Buffer.isBuffer(cmsData)) {
97-
return done(errcode(new Error('CMS data is required'), 'ERR_INVALID_PARAMS'))
73+
throw errcode(new Error('CMS data is required'), 'ERR_INVALID_PARAMS')
9874
}
9975

100-
const self = this
10176
let cms
10277
try {
10378
const buf = forge.util.createBuffer(cmsData.toString('binary'))
10479
const obj = forge.asn1.fromDer(buf)
10580
cms = forge.pkcs7.messageFromAsn1(obj)
10681
} catch (err) {
107-
return done(errcode(new Error('Invalid CMS: ' + err.message), 'ERR_INVALID_CMS'))
82+
throw errcode(new Error('Invalid CMS: ' + err.message), 'ERR_INVALID_CMS')
10883
}
10984

11085
// Find a recipient whose key we hold. We only deal with recipient certs
@@ -118,31 +93,29 @@ class CMS {
11893
keyId: r.issuer.find(a => a.shortName === 'CN').value
11994
}
12095
})
121-
detect(
122-
recipients,
123-
(r, cb) => self.keychain.findKeyById(r.keyId, (err, info) => cb(null, !err && info)),
124-
(err, r) => {
125-
if (err) return done(err)
126-
if (!r) {
127-
const missingKeys = recipients.map(r => r.keyId)
128-
err = errcode(new Error('Decryption needs one of the key(s): ' + missingKeys.join(', ')), 'ERR_MISSING_KEYS', {
129-
missingKeys
130-
})
131-
return done(err)
132-
}
13396

134-
waterfall([
135-
(cb) => self.keychain.findKeyById(r.keyId, cb),
136-
(key, cb) => self.keychain._getPrivateKey(key.name, cb)
137-
], (err, pem) => {
138-
if (err) return done(err)
139-
140-
const privateKey = forge.pki.decryptRsaPrivateKey(pem, self.keychain._())
141-
cms.decrypt(r.recipient, privateKey)
142-
done(null, Buffer.from(cms.content.getBytes(), 'binary'))
143-
})
97+
const r = await findAsync(recipients, async (recipient) => {
98+
try {
99+
const key = await this.keychain.findKeyById(recipient.keyId)
100+
if (key) return true
101+
} catch (err) {
102+
return false
144103
}
145-
)
104+
return false
105+
})
106+
107+
if (!r) {
108+
const missingKeys = recipients.map(r => r.keyId)
109+
throw errcode(new Error('Decryption needs one of the key(s): ' + missingKeys.join(', ')), 'ERR_MISSING_KEYS', {
110+
missingKeys
111+
})
112+
}
113+
114+
const key = await this.keychain.findKeyById(r.keyId)
115+
const pem = await this.keychain._getPrivateKey(key.name)
116+
const privateKey = forge.pki.decryptRsaPrivateKey(pem, this.keychain._())
117+
cms.decrypt(r.recipient, privateKey)
118+
return Buffer.from(cms.content.getBytes(), 'binary')
146119
}
147120
}
148121

0 commit comments

Comments
 (0)