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

Commit c1627a9

Browse files
richardschneiderdaviddias
authored andcommitted
feat: use libp2p-crypto (#18)
* test: openssl interop is now the responsibility of libp2p-crypto * feat: use libp2p-crypto, not node-forge, for key management * fix: use libp2p-crypto.pbkdf, not node-forge * fix: do not ship CMS This removes all depencies on node-forge * test: update dependencies * test: remove dead code
1 parent 605d290 commit c1627a9

File tree

9 files changed

+79
-511
lines changed

9 files changed

+79
-511
lines changed

README.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,15 +85,14 @@ The **key id** is the SHA-256 [multihash](https://github.com/multiformats/multih
8585

8686
A private key is stored as an encrypted PKCS 8 structure in the PEM format. It is protected by a key generated from the key chain's *passPhrase* using **PBKDF2**.
8787

88-
The default options for generating the derived encryption key are in the `dek` object
88+
The default options for generating the derived encryption key are in the `dek` object. This, along with the passPhrase, is the input to a `PBKDF2` function.
89+
8990
```js
9091
const defaultOptions = {
91-
createIfNeeded: true,
92-
9392
//See https://cryptosense.com/parameter-choice-for-pbkdf2/
9493
dek: {
9594
keyLength: 512 / 8,
96-
iterationCount: 10000,
95+
iterationCount: 1000,
9796
salt: 'at least 16 characters long',
9897
hash: 'sha2-512'
9998
}

package.json

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,22 +45,20 @@
4545
"async": "^2.6.0",
4646
"deepmerge": "^1.5.2",
4747
"interface-datastore": "~0.4.1",
48-
"libp2p-crypto": "~0.10.3",
49-
"multihashes": "~0.4.12",
50-
"node-forge": "~0.7.1",
48+
"libp2p-crypto": "~0.11.0",
5149
"pull-stream": "^3.6.1",
5250
"sanitize-filename": "^1.6.1"
5351
},
5452
"devDependencies": {
55-
"aegir": "^12.2.0",
53+
"aegir": "^12.3.0",
5654
"chai": "^4.1.2",
5755
"chai-string": "^1.4.0",
58-
"datastore-fs": "^0.4.1",
59-
"datastore-level": "^0.7.0",
56+
"datastore-fs": "~0.4.1",
57+
"datastore-level": "~0.7.0",
6058
"dirty-chai": "^2.0.1",
6159
"level-js": "^2.2.4",
6260
"mocha": "^4.0.1",
63-
"peer-id": "^0.10.2",
61+
"peer-id": "~0.10.4",
6462
"pre-commit": "^1.2.2",
6563
"rimraf": "^2.6.2"
6664
}

src/cms.js

Lines changed: 0 additions & 96 deletions
This file was deleted.

src/keychain.js

Lines changed: 59 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1+
/* eslint max-nested-callbacks: ["error", 5] */
12
'use strict'
23

34
const sanitize = require('sanitize-filename')
4-
const forge = require('node-forge')
55
const deepmerge = require('deepmerge')
66
const crypto = require('libp2p-crypto')
7-
const util = require('./util')
8-
const CMS = require('./cms')
97
const DS = require('interface-datastore')
108
const pull = require('pull-stream')
119

@@ -19,24 +17,11 @@ const NIST = {
1917
minIterationCount: 1000
2018
}
2119

22-
/**
23-
* Maps an IPFS hash name to its forge equivalent.
24-
*
25-
* See https://github.com/multiformats/multihash/blob/master/hashtable.csv
26-
*
27-
* @private
28-
*/
29-
const hashName2Forge = {
30-
sha1: 'sha1',
31-
'sha2-256': 'sha256',
32-
'sha2-512': 'sha512'
33-
}
34-
3520
const defaultOptions = {
3621
// See https://cryptosense.com/parametesr-choice-for-pbkdf2/
3722
dek: {
3823
keyLength: 512 / 8,
39-
iterationCount: 10000,
24+
iterationCount: 1000,
4025
salt: 'you should override this value with a crypto secure random number',
4126
hash: 'sha2-512'
4227
}
@@ -133,26 +118,15 @@ class Keychain {
133118
if (opts.dek.iterationCount < NIST.minIterationCount) {
134119
throw new Error(`dek.iterationCount must be least ${NIST.minIterationCount}`)
135120
}
136-
this.dek = opts.dek
137-
138-
// Get the hashing alogorithm
139-
const hashAlgorithm = hashName2Forge[opts.dek.hash]
140-
if (!hashAlgorithm) {
141-
throw new Error(`dek.hash '${opts.dek.hash}' is unknown or not supported`)
142-
}
143121

144122
// Create the derived encrypting key
145-
let dek = forge.pkcs5.pbkdf2(
123+
const dek = crypto.pbkdf2(
146124
opts.passPhrase,
147125
opts.dek.salt,
148126
opts.dek.iterationCount,
149127
opts.dek.keyLength,
150-
hashAlgorithm)
151-
dek = forge.util.bytesToHex(dek)
128+
opts.dek.hash)
152129
Object.defineProperty(this, '_', { value: () => dek })
153-
154-
// Provide access to protected messages
155-
this.cms = new CMS(this)
156130
}
157131

158132
/**
@@ -189,31 +163,32 @@ class Keychain {
189163
if (size < 2048) {
190164
return _error(callback, `Invalid RSA key size ${size}`)
191165
}
192-
forge.pki.rsa.generateKeyPair({bits: size, workers: -1}, (err, keypair) => {
166+
break
167+
default:
168+
break
169+
}
170+
171+
crypto.keys.generateKeyPair(type, size, (err, keypair) => {
172+
if (err) return _error(callback, err)
173+
keypair.id((err, kid) => {
174+
if (err) return _error(callback, err)
175+
keypair.export(this._(), (err, pem) => {
193176
if (err) return _error(callback, err)
194-
util.keyId(keypair.privateKey, (err, kid) => {
177+
const keyInfo = {
178+
name: name,
179+
id: kid
180+
}
181+
const batch = self.store.batch()
182+
batch.put(dsname, pem)
183+
batch.put(DsInfoName(name), JSON.stringify(keyInfo))
184+
batch.commit((err) => {
195185
if (err) return _error(callback, err)
196186

197-
const pem = forge.pki.encryptRsaPrivateKey(keypair.privateKey, this._())
198-
const keyInfo = {
199-
name: name,
200-
id: kid
201-
}
202-
const batch = self.store.batch()
203-
batch.put(dsname, pem)
204-
batch.put(DsInfoName(name), JSON.stringify(keyInfo))
205-
batch.commit((err) => {
206-
if (err) return _error(callback, err)
207-
208-
callback(null, keyInfo)
209-
})
187+
callback(null, keyInfo)
210188
})
211189
})
212-
break
213-
214-
default:
215-
return _error(callback, `Invalid key type '${type}'`)
216-
}
190+
})
191+
})
217192
})
218193
}
219194

@@ -372,19 +347,10 @@ class Keychain {
372347
return _error(callback, `Key '${name}' does not exist. ${err.message}`)
373348
}
374349
const pem = res.toString()
375-
try {
376-
const options = {
377-
algorithm: 'aes256',
378-
count: this.dek.iterationCount,
379-
saltSize: NIST.minSaltLength,
380-
prfAlgorithm: 'sha512'
381-
}
382-
const privateKey = forge.pki.decryptRsaPrivateKey(pem, this._())
383-
const res = forge.pki.encryptRsaPrivateKey(privateKey, password, options)
384-
return callback(null, res)
385-
} catch (e) {
386-
_error(callback, e)
387-
}
350+
crypto.keys.import(pem, this._(), (err, privateKey) => {
351+
if (err) return _error(callback, err)
352+
privateKey.export(password, callback)
353+
})
388354
})
389355
}
390356

@@ -409,31 +375,27 @@ class Keychain {
409375
self.store.has(dsname, (err, exists) => {
410376
if (err) return _error(callback, err)
411377
if (exists) return _error(callback, `Key '${name}' already exists`)
412-
try {
413-
const privateKey = forge.pki.decryptRsaPrivateKey(pem, password)
414-
if (privateKey === null) {
415-
return _error(callback, 'Cannot read the key, most likely the password is wrong')
416-
}
417-
const newpem = forge.pki.encryptRsaPrivateKey(privateKey, this._())
418-
util.keyId(privateKey, (err, kid) => {
378+
crypto.keys.import(pem, password, (err, privateKey) => {
379+
if (err) return _error(callback, 'Cannot read the key, most likely the password is wrong')
380+
privateKey.id((err, kid) => {
419381
if (err) return _error(callback, err)
420-
421-
const keyInfo = {
422-
name: name,
423-
id: kid
424-
}
425-
const batch = self.store.batch()
426-
batch.put(dsname, newpem)
427-
batch.put(DsInfoName(name), JSON.stringify(keyInfo))
428-
batch.commit((err) => {
382+
privateKey.export(this._(), (err, pem) => {
429383
if (err) return _error(callback, err)
384+
const keyInfo = {
385+
name: name,
386+
id: kid
387+
}
388+
const batch = self.store.batch()
389+
batch.put(dsname, pem)
390+
batch.put(DsInfoName(name), JSON.stringify(keyInfo))
391+
batch.commit((err) => {
392+
if (err) return _error(callback, err)
430393

431-
callback(null, keyInfo)
394+
callback(null, keyInfo)
395+
})
432396
})
433397
})
434-
} catch (err) {
435-
_error(callback, err)
436-
}
398+
})
437399
})
438400
}
439401

@@ -445,42 +407,30 @@ class Keychain {
445407
if (!peer || !peer.privKey) {
446408
return _error(callback, 'Peer.privKey is required')
447409
}
410+
411+
const privateKey = peer.privKey
448412
const dsname = DsName(name)
449413
self.store.has(dsname, (err, exists) => {
450414
if (err) return _error(callback, err)
451415
if (exists) return _error(callback, `Key '${name}' already exists`)
452416

453-
const privateKeyProtobuf = peer.marshalPrivKey()
454-
crypto.keys.unmarshalPrivateKey(privateKeyProtobuf, (err, key) => {
417+
privateKey.id((err, kid) => {
455418
if (err) return _error(callback, err)
456-
try {
457-
const der = key.marshal()
458-
const buf = forge.util.createBuffer(der.toString('binary'))
459-
const obj = forge.asn1.fromDer(buf)
460-
const privateKey = forge.pki.privateKeyFromAsn1(obj)
461-
if (privateKey === null) {
462-
return _error(callback, 'Cannot read the peer private key')
419+
privateKey.export(this._(), (err, pem) => {
420+
if (err) return _error(callback, err)
421+
const keyInfo = {
422+
name: name,
423+
id: kid
463424
}
464-
const pem = forge.pki.encryptRsaPrivateKey(privateKey, this._())
465-
util.keyId(privateKey, (err, kid) => {
425+
const batch = self.store.batch()
426+
batch.put(dsname, pem)
427+
batch.put(DsInfoName(name), JSON.stringify(keyInfo))
428+
batch.commit((err) => {
466429
if (err) return _error(callback, err)
467430

468-
const keyInfo = {
469-
name: name,
470-
id: kid
471-
}
472-
const batch = self.store.batch()
473-
batch.put(dsname, pem)
474-
batch.put(DsInfoName(name), JSON.stringify(keyInfo))
475-
batch.commit((err) => {
476-
if (err) return _error(callback, err)
477-
478-
callback(null, keyInfo)
479-
})
431+
callback(null, keyInfo)
480432
})
481-
} catch (err) {
482-
_error(callback, err)
483-
}
433+
})
484434
})
485435
})
486436
}

0 commit comments

Comments
 (0)