mirror of
https://github.com/fluencelabs/js-libp2p
synced 2025-05-02 22:12:16 +00:00
refactor: use async/await instead of callbacks (#37)
BREAKING CHANGE: The api now uses async/await instead of callbacks. Co-Authored-By: Vasco Santos <vasco.santos@moxy.studio>
This commit is contained in:
parent
717112bdf8
commit
dda315a9c8
1
.gitignore
vendored
1
.gitignore
vendored
@ -9,6 +9,7 @@ logs
|
||||
*.log
|
||||
|
||||
coverage
|
||||
.nyc_output
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
|
@ -7,6 +7,7 @@ stages:
|
||||
|
||||
node_js:
|
||||
- '10'
|
||||
- '12'
|
||||
|
||||
os:
|
||||
- linux
|
||||
@ -20,8 +21,7 @@ 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
|
||||
@ -29,14 +29,14 @@ jobs:
|
||||
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
README.md
40
README.md
@ -1,17 +1,13 @@
|
||||
# js-libp2p-keychain
|
||||
|
||||
[](http://ipn.io)
|
||||
[](http://ipfs.io/)
|
||||
[](http://webchat.freenode.net/?channels=%23ipfs)
|
||||
[](http://protocol.ai)
|
||||
[](http://libp2p.io/)
|
||||
[](http://webchat.freenode.net/?channels=%23libp2p)
|
||||
[](https://discuss.libp2p.io)
|
||||
[](https://github.com/RichardLitt/standard-readme)
|
||||
[](https://coveralls.io/github/libp2p/js-libp2p-keychain?branch=master)
|
||||
[](https://travis-ci.org/libp2p/js-libp2p-keychain)
|
||||
[](https://circleci.com/gh/libp2p/js-libp2p-keychain)
|
||||
[](https://codecov.io/gh/libp2p/js-libp2p-keychain)
|
||||
[](https://travis-ci.com/libp2p/js-libp2p-keychain)
|
||||
[](https://david-dm.org/libp2p/js-libp2p-keychain)
|
||||
[](https://github.com/feross/standard)
|
||||

|
||||

|
||||
|
||||
> A secure key chain for libp2p in JavaScript
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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://github.com/ipfs/community/blob/master/contributing.md)
|
||||
[](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md)
|
||||
|
||||
## License
|
||||
|
||||
|
34
package.json
34
package.json
@ -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"
|
||||
},
|
||||
"pre-push": [
|
||||
"lint",
|
||||
"test"
|
||||
"lint"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=6.0.0",
|
||||
"node": ">=10.0.0",
|
||||
"npm": ">=3.0.0"
|
||||
},
|
||||
"repository": {
|
||||
@ -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": [
|
||||
|
81
src/cms.js
81
src/cms.js
@ -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')
|
||||
|
||||
/**
|
||||
@ -40,29 +36,17 @@ 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()
|
||||
@ -72,12 +56,7 @@ class CMS {
|
||||
|
||||
// convert message to DER
|
||||
const der = forge.asn1.toDer(p7.toAsn1()).getBytes()
|
||||
done(null, Buffer.from(der, 'binary'))
|
||||
})
|
||||
} catch (err) {
|
||||
done(err)
|
||||
}
|
||||
})
|
||||
return Buffer.from(der, 'binary')
|
||||
}
|
||||
|
||||
/**
|
||||
@ -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
|
||||
@ -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)
|
||||
|
||||
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)
|
||||
err = errcode(new Error('Decryption needs one of the key(s): ' + missingKeys.join(', ')), 'ERR_MISSING_KEYS', {
|
||||
throw 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._())
|
||||
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)
|
||||
done(null, Buffer.from(cms.content.getBytes(), 'binary'))
|
||||
})
|
||||
}
|
||||
)
|
||||
return Buffer.from(cms.content.getBytes(), 'binary')
|
||||
}
|
||||
}
|
||||
|
||||
|
287
src/keychain.js
287
src/keychain.js
@ -5,8 +5,6 @@ const sanitize = require('sanitize-filename')
|
||||
const mergeOptions = require('merge-options')
|
||||
const crypto = require('libp2p-crypto')
|
||||
const DS = require('interface-datastore')
|
||||
const collect = require('pull-stream/sinks/collect')
|
||||
const pull = require('pull-stream/pull')
|
||||
const CMS = require('./cms')
|
||||
const errcode = require('err-code')
|
||||
|
||||
@ -37,22 +35,21 @@ function validateKeyName (name) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an error to the caller, after a delay
|
||||
* Throws an error after a delay
|
||||
*
|
||||
* This assumes than an error indicates that the keychain is under attack. Delay returning an
|
||||
* error to make brute force attacks harder.
|
||||
*
|
||||
* @param {function(Error)} callback - The caller
|
||||
* @param {string | Error} err - The error
|
||||
* @returns {undefined}
|
||||
* @private
|
||||
*/
|
||||
function _error (callback, err) {
|
||||
async function throwDelayed (err) {
|
||||
const min = 200
|
||||
const max = 1000
|
||||
const delay = Math.random() * (max - min) + min
|
||||
|
||||
setTimeout(callback, delay, err, null)
|
||||
await new Promise(resolve => setTimeout(resolve, delay))
|
||||
throw err
|
||||
}
|
||||
|
||||
/**
|
||||
@ -175,146 +172,131 @@ class Keychain {
|
||||
* @param {string} name - The local key name; cannot already exist.
|
||||
* @param {string} type - One of the key types; 'rsa'.
|
||||
* @param {int} size - The key size in bits.
|
||||
* @param {function(Error, KeyInfo)} callback
|
||||
* @returns {undefined}
|
||||
* @returns {KeyInfo}
|
||||
*/
|
||||
createKey (name, type, size, callback) {
|
||||
async createKey (name, type, size) {
|
||||
const self = this
|
||||
|
||||
if (!validateKeyName(name) || name === 'self') {
|
||||
return _error(callback, errcode(new Error(`Invalid key name '${name}'`), 'ERR_INVALID_KEY_NAME'))
|
||||
return throwDelayed(errcode(new Error(`Invalid key name '${name}'`), 'ERR_INVALID_KEY_NAME'))
|
||||
}
|
||||
|
||||
if (typeof type !== 'string') {
|
||||
return _error(callback, errcode(new Error(`Invalid key type '${type}'`), 'ERR_INVALID_KEY_TYPE'))
|
||||
return throwDelayed(errcode(new Error(`Invalid key type '${type}'`), 'ERR_INVALID_KEY_TYPE'))
|
||||
}
|
||||
|
||||
if (!Number.isSafeInteger(size)) {
|
||||
return _error(callback, errcode(new Error(`Invalid key size '${size}'`), 'ERR_INVALID_KEY_SIZE'))
|
||||
return throwDelayed(errcode(new Error(`Invalid key size '${size}'`), 'ERR_INVALID_KEY_SIZE'))
|
||||
}
|
||||
|
||||
const dsname = DsName(name)
|
||||
self.store.has(dsname, (err, exists) => {
|
||||
if (err) return _error(callback, err)
|
||||
if (exists) return _error(callback, errcode(new Error(`Key '${name}' already exists`), 'ERR_KEY_ALREADY_EXISTS'))
|
||||
const exists = await self.store.has(dsname)
|
||||
if (exists) return throwDelayed(errcode(new Error(`Key '${name}' already exists`), 'ERR_KEY_ALREADY_EXISTS'))
|
||||
|
||||
switch (type.toLowerCase()) {
|
||||
case 'rsa':
|
||||
if (size < 2048) {
|
||||
return _error(callback, errcode(new Error(`Invalid RSA key size ${size}`), 'ERR_INVALID_KEY_SIZE'))
|
||||
return throwDelayed(errcode(new Error(`Invalid RSA key size ${size}`), 'ERR_INVALID_KEY_SIZE'))
|
||||
}
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
crypto.keys.generateKeyPair(type, size, (err, keypair) => {
|
||||
if (err) return _error(callback, err)
|
||||
keypair.id((err, kid) => {
|
||||
if (err) return _error(callback, err)
|
||||
keypair.export(this._(), (err, pem) => {
|
||||
if (err) return _error(callback, err)
|
||||
const keyInfo = {
|
||||
let keyInfo
|
||||
try {
|
||||
const keypair = await crypto.keys.generateKeyPair(type, size)
|
||||
|
||||
const kid = await keypair.id()
|
||||
const pem = await keypair.export(this._())
|
||||
keyInfo = {
|
||||
name: name,
|
||||
id: kid
|
||||
}
|
||||
const batch = self.store.batch()
|
||||
batch.put(dsname, pem)
|
||||
batch.put(DsInfoName(name), JSON.stringify(keyInfo))
|
||||
batch.commit((err) => {
|
||||
if (err) return _error(callback, err)
|
||||
|
||||
callback(null, keyInfo)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
await batch.commit()
|
||||
} catch (err) {
|
||||
return throwDelayed(err)
|
||||
}
|
||||
|
||||
return keyInfo
|
||||
}
|
||||
|
||||
/**
|
||||
* List all the keys.
|
||||
*
|
||||
* @param {function(Error, KeyInfo[])} callback
|
||||
* @returns {undefined}
|
||||
* @returns {KeyInfo[]}
|
||||
*/
|
||||
listKeys (callback) {
|
||||
async listKeys () {
|
||||
const self = this
|
||||
const query = {
|
||||
prefix: infoPrefix
|
||||
}
|
||||
pull(
|
||||
self.store.query(query),
|
||||
collect((err, res) => {
|
||||
if (err) return _error(callback, err)
|
||||
|
||||
const info = res.map(r => JSON.parse(r.value))
|
||||
callback(null, info)
|
||||
})
|
||||
)
|
||||
const info = []
|
||||
for await (const value of self.store.query(query)) {
|
||||
info.push(JSON.parse(value.value))
|
||||
}
|
||||
|
||||
return info
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a key by it's id.
|
||||
*
|
||||
* @param {string} id - The universally unique key identifier.
|
||||
* @param {function(Error, KeyInfo)} callback
|
||||
* @returns {undefined}
|
||||
* @returns {KeyInfo}
|
||||
*/
|
||||
findKeyById (id, callback) {
|
||||
this.listKeys((err, keys) => {
|
||||
if (err) return _error(callback, err)
|
||||
|
||||
const key = keys.find((k) => k.id === id)
|
||||
callback(null, key)
|
||||
})
|
||||
async findKeyById (id) {
|
||||
try {
|
||||
const keys = await this.listKeys()
|
||||
return keys.find((k) => k.id === id)
|
||||
} catch (err) {
|
||||
return throwDelayed(err)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a key by it's name.
|
||||
*
|
||||
* @param {string} name - The local key name.
|
||||
* @param {function(Error, KeyInfo)} callback
|
||||
* @returns {undefined}
|
||||
* @returns {KeyInfo}
|
||||
*/
|
||||
findKeyByName (name, callback) {
|
||||
async findKeyByName (name) {
|
||||
if (!validateKeyName(name)) {
|
||||
return _error(callback, errcode(new Error(`Invalid key name '${name}'`), 'ERR_INVALID_KEY_NAME'))
|
||||
return throwDelayed(errcode(new Error(`Invalid key name '${name}'`), 'ERR_INVALID_KEY_NAME'))
|
||||
}
|
||||
|
||||
const dsname = DsInfoName(name)
|
||||
this.store.get(dsname, (err, res) => {
|
||||
if (err) {
|
||||
return _error(callback, errcode(new Error(`Key '${name}' does not exist. ${err.message}`), 'ERR_KEY_NOT_FOUND'))
|
||||
try {
|
||||
const res = await this.store.get(dsname)
|
||||
return JSON.parse(res.toString())
|
||||
} catch (err) {
|
||||
return throwDelayed(errcode(new Error(`Key '${name}' does not exist. ${err.message}`), 'ERR_KEY_NOT_FOUND'))
|
||||
}
|
||||
|
||||
callback(null, JSON.parse(res.toString()))
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an existing key.
|
||||
*
|
||||
* @param {string} name - The local key name; must already exist.
|
||||
* @param {function(Error, KeyInfo)} callback
|
||||
* @returns {undefined}
|
||||
* @returns {KeyInfo}
|
||||
*/
|
||||
removeKey (name, callback) {
|
||||
async removeKey (name) {
|
||||
const self = this
|
||||
if (!validateKeyName(name) || name === 'self') {
|
||||
return _error(callback, errcode(new Error(`Invalid key name '${name}'`), 'ERR_INVALID_KEY_NAME'))
|
||||
return throwDelayed(errcode(new Error(`Invalid key name '${name}'`), 'ERR_INVALID_KEY_NAME'))
|
||||
}
|
||||
const dsname = DsName(name)
|
||||
self.findKeyByName(name, (err, keyinfo) => {
|
||||
if (err) return _error(callback, err)
|
||||
const keyInfo = await self.findKeyByName(name)
|
||||
const batch = self.store.batch()
|
||||
batch.delete(dsname)
|
||||
batch.delete(DsInfoName(name))
|
||||
batch.commit((err) => {
|
||||
if (err) return _error(callback, err)
|
||||
callback(null, keyinfo)
|
||||
})
|
||||
})
|
||||
await batch.commit()
|
||||
return keyInfo
|
||||
}
|
||||
|
||||
/**
|
||||
@ -322,32 +304,28 @@ class Keychain {
|
||||
*
|
||||
* @param {string} oldName - The old local key name; must already exist.
|
||||
* @param {string} newName - The new local key name; must not already exist.
|
||||
* @param {function(Error, KeyInfo)} callback
|
||||
* @returns {undefined}
|
||||
* @returns {KeyInfo}
|
||||
*/
|
||||
renameKey (oldName, newName, callback) {
|
||||
async renameKey (oldName, newName) {
|
||||
const self = this
|
||||
if (!validateKeyName(oldName) || oldName === 'self') {
|
||||
return _error(callback, errcode(new Error(`Invalid old key name '${oldName}'`), 'ERR_OLD_KEY_NAME_INVALID'))
|
||||
return throwDelayed(errcode(new Error(`Invalid old key name '${oldName}'`), 'ERR_OLD_KEY_NAME_INVALID'))
|
||||
}
|
||||
if (!validateKeyName(newName) || newName === 'self') {
|
||||
return _error(callback, errcode(new Error(`Invalid new key name '${newName}'`), 'ERR_NEW_KEY_NAME_INVALID'))
|
||||
return throwDelayed(errcode(new Error(`Invalid new key name '${newName}'`), 'ERR_NEW_KEY_NAME_INVALID'))
|
||||
}
|
||||
const oldDsname = DsName(oldName)
|
||||
const newDsname = DsName(newName)
|
||||
const oldInfoName = DsInfoName(oldName)
|
||||
const newInfoName = DsInfoName(newName)
|
||||
this.store.get(oldDsname, (err, res) => {
|
||||
if (err) {
|
||||
return _error(callback, errcode(new Error(`Key '${oldName}' does not exist. ${err.message}`), 'ERR_KEY_NOT_FOUND'))
|
||||
}
|
||||
const pem = res.toString()
|
||||
self.store.has(newDsname, (err, exists) => {
|
||||
if (err) return _error(callback, err)
|
||||
if (exists) return _error(callback, errcode(new Error(`Key '${newName}' already exists`), 'ERR_KEY_ALREADY_EXISTS'))
|
||||
|
||||
self.store.get(oldInfoName, (err, res) => {
|
||||
if (err) return _error(callback, err)
|
||||
const exists = await self.store.has(newDsname)
|
||||
if (exists) return throwDelayed(errcode(new Error(`Key '${newName}' already exists`), 'ERR_KEY_ALREADY_EXISTS'))
|
||||
|
||||
try {
|
||||
let res = await this.store.get(oldDsname)
|
||||
const pem = res.toString()
|
||||
res = await self.store.get(oldInfoName)
|
||||
|
||||
const keyInfo = JSON.parse(res.toString())
|
||||
keyInfo.name = newName
|
||||
@ -356,13 +334,11 @@ class Keychain {
|
||||
batch.put(newInfoName, JSON.stringify(keyInfo))
|
||||
batch.delete(oldDsname)
|
||||
batch.delete(oldInfoName)
|
||||
batch.commit((err) => {
|
||||
if (err) return _error(callback, err)
|
||||
callback(null, keyInfo)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
await batch.commit()
|
||||
return keyInfo
|
||||
} catch (err) {
|
||||
return throwDelayed(err)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -370,28 +346,25 @@ class Keychain {
|
||||
*
|
||||
* @param {string} name - The local key name; must already exist.
|
||||
* @param {string} password - The password
|
||||
* @param {function(Error, string)} callback
|
||||
* @returns {undefined}
|
||||
* @returns {string}
|
||||
*/
|
||||
exportKey (name, password, callback) {
|
||||
async exportKey (name, password) {
|
||||
if (!validateKeyName(name)) {
|
||||
return _error(callback, errcode(new Error(`Invalid key name '${name}'`), 'ERR_INVALID_KEY_NAME'))
|
||||
return throwDelayed(errcode(new Error(`Invalid key name '${name}'`), 'ERR_INVALID_KEY_NAME'))
|
||||
}
|
||||
if (!password) {
|
||||
return _error(callback, errcode(new Error('Password is required'), 'ERR_PASSWORD_REQUIRED'))
|
||||
return throwDelayed(errcode(new Error('Password is required'), 'ERR_PASSWORD_REQUIRED'))
|
||||
}
|
||||
|
||||
const dsname = DsName(name)
|
||||
this.store.get(dsname, (err, res) => {
|
||||
if (err) {
|
||||
return _error(callback, errcode(new Error(`Key '${name}' does not exist. ${err.message}`), 'ERR_KEY_NOT_FOUND'))
|
||||
}
|
||||
try {
|
||||
const res = await this.store.get(dsname)
|
||||
const pem = res.toString()
|
||||
crypto.keys.import(pem, this._(), (err, privateKey) => {
|
||||
if (err) return _error(callback, err)
|
||||
privateKey.export(password, callback)
|
||||
})
|
||||
})
|
||||
const privateKey = await crypto.keys.import(pem, this._())
|
||||
return privateKey.export(password)
|
||||
} catch (err) {
|
||||
return throwDelayed(err)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -400,27 +373,35 @@ class Keychain {
|
||||
* @param {string} name - The local key name; must not already exist.
|
||||
* @param {string} pem - The PEM encoded PKCS #8 string
|
||||
* @param {string} password - The password.
|
||||
* @param {function(Error, KeyInfo)} callback
|
||||
* @returns {undefined}
|
||||
* @returns {KeyInfo}
|
||||
*/
|
||||
importKey (name, pem, password, callback) {
|
||||
async importKey (name, pem, password) {
|
||||
const self = this
|
||||
if (!validateKeyName(name) || name === 'self') {
|
||||
return _error(callback, errcode(new Error(`Invalid key name '${name}'`), 'ERR_INVALID_KEY_NAME'))
|
||||
return throwDelayed(errcode(new Error(`Invalid key name '${name}'`), 'ERR_INVALID_KEY_NAME'))
|
||||
}
|
||||
if (!pem) {
|
||||
return _error(callback, 'PEM encoded key is required')
|
||||
return throwDelayed(errcode(new Error('PEM encoded key is required'), 'ERR_PEM_REQUIRED'))
|
||||
}
|
||||
const dsname = DsName(name)
|
||||
self.store.has(dsname, (err, exists) => {
|
||||
if (err) return _error(callback, err)
|
||||
if (exists) return _error(callback, errcode(new Error(`Key '${name}' already exists`), 'ERR_KEY_ALREADY_EXISTS'))
|
||||
crypto.keys.import(pem, password, (err, privateKey) => {
|
||||
if (err) return _error(callback, errcode(new Error('Cannot read the key, most likely the password is wrong'), 'ERR_CANNOT_READ_KEY'))
|
||||
privateKey.id((err, kid) => {
|
||||
if (err) return _error(callback, err)
|
||||
privateKey.export(this._(), (err, pem) => {
|
||||
if (err) return _error(callback, err)
|
||||
const exists = await self.store.has(dsname)
|
||||
if (exists) return throwDelayed(errcode(new Error(`Key '${name}' already exists`), 'ERR_KEY_ALREADY_EXISTS'))
|
||||
|
||||
let privateKey
|
||||
try {
|
||||
privateKey = await crypto.keys.import(pem, password)
|
||||
} catch (err) {
|
||||
return throwDelayed(errcode(new Error('Cannot read the key, most likely the password is wrong'), 'ERR_CANNOT_READ_KEY'))
|
||||
}
|
||||
|
||||
let kid
|
||||
try {
|
||||
kid = await privateKey.id()
|
||||
pem = await privateKey.export(this._())
|
||||
} catch (err) {
|
||||
return throwDelayed(err)
|
||||
}
|
||||
|
||||
const keyInfo = {
|
||||
name: name,
|
||||
id: kid
|
||||
@ -428,36 +409,28 @@ class Keychain {
|
||||
const batch = self.store.batch()
|
||||
batch.put(dsname, pem)
|
||||
batch.put(DsInfoName(name), JSON.stringify(keyInfo))
|
||||
batch.commit((err) => {
|
||||
if (err) return _error(callback, err)
|
||||
await batch.commit()
|
||||
|
||||
callback(null, keyInfo)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
return keyInfo
|
||||
}
|
||||
|
||||
importPeer (name, peer, callback) {
|
||||
async importPeer (name, peer) {
|
||||
const self = this
|
||||
if (!validateKeyName(name)) {
|
||||
return _error(callback, errcode(new Error(`Invalid key name '${name}'`), 'ERR_INVALID_KEY_NAME'))
|
||||
return throwDelayed(errcode(new Error(`Invalid key name '${name}'`), 'ERR_INVALID_KEY_NAME'))
|
||||
}
|
||||
if (!peer || !peer.privKey) {
|
||||
return _error(callback, errcode(new Error('Peer.privKey is required'), 'ERR_MISSING_PRIVATE_KEY'))
|
||||
return throwDelayed(errcode(new Error('Peer.privKey is required'), 'ERR_MISSING_PRIVATE_KEY'))
|
||||
}
|
||||
|
||||
const privateKey = peer.privKey
|
||||
const dsname = DsName(name)
|
||||
self.store.has(dsname, (err, exists) => {
|
||||
if (err) return _error(callback, err)
|
||||
if (exists) return _error(callback, errcode(new Error(`Key '${name}' already exists`), 'ERR_KEY_ALREADY_EXISTS'))
|
||||
const exists = await self.store.has(dsname)
|
||||
if (exists) return throwDelayed(errcode(new Error(`Key '${name}' already exists`), 'ERR_KEY_ALREADY_EXISTS'))
|
||||
|
||||
privateKey.id((err, kid) => {
|
||||
if (err) return _error(callback, err)
|
||||
privateKey.export(this._(), (err, pem) => {
|
||||
if (err) return _error(callback, err)
|
||||
try {
|
||||
const kid = await privateKey.id()
|
||||
const pem = await privateKey.export(this._())
|
||||
const keyInfo = {
|
||||
name: name,
|
||||
id: kid
|
||||
@ -465,34 +438,32 @@ class Keychain {
|
||||
const batch = self.store.batch()
|
||||
batch.put(dsname, pem)
|
||||
batch.put(DsInfoName(name), JSON.stringify(keyInfo))
|
||||
batch.commit((err) => {
|
||||
if (err) return _error(callback, err)
|
||||
|
||||
callback(null, keyInfo)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
await batch.commit()
|
||||
return keyInfo
|
||||
} catch (err) {
|
||||
return throwDelayed(err)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the private key as PEM encoded PKCS #8 string.
|
||||
*
|
||||
* @param {string} name
|
||||
* @param {function(Error, string)} callback
|
||||
* @returns {undefined}
|
||||
* @returns {string}
|
||||
* @private
|
||||
*/
|
||||
_getPrivateKey (name, callback) {
|
||||
async _getPrivateKey (name) {
|
||||
if (!validateKeyName(name)) {
|
||||
return _error(callback, errcode(new Error(`Invalid key name '${name}'`), 'ERR_INVALID_KEY_NAME'))
|
||||
return throwDelayed(errcode(new Error(`Invalid key name '${name}'`), 'ERR_INVALID_KEY_NAME'))
|
||||
}
|
||||
this.store.get(DsName(name), (err, res) => {
|
||||
if (err) {
|
||||
return _error(callback, errcode(new Error(`Key '${name}' does not exist. ${err.message}`), 'ERR_KEY_NOT_FOUND'))
|
||||
|
||||
try {
|
||||
const dsname = DsName(name)
|
||||
const res = await this.store.get(dsname)
|
||||
return res.toString()
|
||||
} catch (err) {
|
||||
return throwDelayed(errcode(new Error(`Key '${name}' does not exist. ${err.message}`), 'ERR_KEY_NOT_FOUND'))
|
||||
}
|
||||
callback(null, res.toString())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
24
src/util.js
24
src/util.js
@ -14,10 +14,9 @@ exports = module.exports
|
||||
*
|
||||
* @param {KeyInfo} key - The id and name of the key
|
||||
* @param {RsaPrivateKey} privateKey - The naked key
|
||||
* @param {function(Error, Certificate)} callback
|
||||
* @returns {undefined}
|
||||
*/
|
||||
exports.certificateForKey = (key, privateKey, callback) => {
|
||||
exports.certificateForKey = (key, privateKey) => {
|
||||
const publicKey = pki.setRsaPublicKey(privateKey.n, privateKey.e)
|
||||
const cert = pki.createCertificate()
|
||||
cert.publicKey = publicKey
|
||||
@ -67,5 +66,24 @@ exports.certificateForKey = (key, privateKey, callback) => {
|
||||
// self-sign certificate
|
||||
cert.sign(privateKey)
|
||||
|
||||
return callback(null, cert)
|
||||
return cert
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the first item in a collection that is matched in the
|
||||
* `asyncCompare` function.
|
||||
*
|
||||
* `asyncCompare` is an async function that must
|
||||
* resolve to either `true` or `false`.
|
||||
*
|
||||
* @param {Array} array
|
||||
* @param {function(*)} asyncCompare An async function that returns a boolean
|
||||
*/
|
||||
async function findAsync (array, asyncCompare) {
|
||||
const promises = array.map(asyncCompare)
|
||||
const results = await Promise.all(promises)
|
||||
const index = results.findIndex(result => result)
|
||||
return array[index]
|
||||
}
|
||||
|
||||
module.exports.findAsync = findAsync
|
||||
|
@ -1,25 +1,24 @@
|
||||
/* eslint-env mocha */
|
||||
'use strict'
|
||||
|
||||
const series = require('async/series')
|
||||
const LevelStore = require('datastore-level')
|
||||
|
||||
describe('browser', () => {
|
||||
const datastore1 = new LevelStore('test-keystore-1', { db: require('level-js') })
|
||||
const datastore2 = new LevelStore('test-keystore-2', { db: require('level-js') })
|
||||
const datastore1 = new LevelStore('test-keystore-1', { db: require('level') })
|
||||
const datastore2 = new LevelStore('test-keystore-2', { db: require('level') })
|
||||
|
||||
before((done) => {
|
||||
series([
|
||||
(cb) => datastore1.open(cb),
|
||||
(cb) => datastore2.open(cb)
|
||||
], done)
|
||||
before(() => {
|
||||
return Promise.all([
|
||||
datastore1.open(),
|
||||
datastore2.open()
|
||||
])
|
||||
})
|
||||
|
||||
after((done) => {
|
||||
series([
|
||||
(cb) => datastore1.close(cb),
|
||||
(cb) => datastore2.close(cb)
|
||||
], done)
|
||||
after(() => {
|
||||
return Promise.all([
|
||||
datastore1.close(),
|
||||
datastore2.close()
|
||||
])
|
||||
})
|
||||
|
||||
require('./keychain.spec')(datastore1, datastore2)
|
||||
|
@ -15,14 +15,13 @@ module.exports = (datastore) => {
|
||||
const aliceKeyName = 'cms-interop-alice'
|
||||
let ks
|
||||
|
||||
before((done) => {
|
||||
before(() => {
|
||||
ks = new Keychain(datastore, { passPhrase: passPhrase })
|
||||
done()
|
||||
})
|
||||
|
||||
const plainData = Buffer.from('This is a message from Alice to Bob')
|
||||
|
||||
it('imports openssl key', function (done) {
|
||||
it('imports openssl key', async function () {
|
||||
this.timeout(10 * 1000)
|
||||
const aliceKid = 'QmNzBqPwp42HZJccsLtc4ok6LjZAspckgs2du5tTmjPfFA'
|
||||
const alice = `-----BEGIN ENCRYPTED PRIVATE KEY-----
|
||||
@ -43,15 +42,12 @@ igg5jozKCW82JsuWSiW9tu0F/6DuvYiZwHS3OLiJP0CuLfbOaRw8Jia1RTvXEH7m
|
||||
cn4oisOvxCprs4aM9UVjtZTCjfyNpX8UWwT1W3rySV+KQNhxuMy3RzmL
|
||||
-----END ENCRYPTED PRIVATE KEY-----
|
||||
`
|
||||
ks.importKey(aliceKeyName, alice, 'mypassword', (err, key) => {
|
||||
expect(err).to.not.exist()
|
||||
const key = await ks.importKey(aliceKeyName, alice, 'mypassword')
|
||||
expect(key.name).to.equal(aliceKeyName)
|
||||
expect(key.id).to.equal(aliceKid)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('decrypts node-forge example', (done) => {
|
||||
it('decrypts node-forge example', async () => {
|
||||
const example = `
|
||||
MIIBcwYJKoZIhvcNAQcDoIIBZDCCAWACAQAxgfowgfcCAQAwYDBbMQ0wCwYDVQQK
|
||||
EwRpcGZzMREwDwYDVQQLEwhrZXlzdG9yZTE3MDUGA1UEAxMuUW1OekJxUHdwNDJI
|
||||
@ -62,12 +58,9 @@ knU1yykWGkdlbclCuu0NaAfmb8o0OX50CbEKZB7xmsv8tnqn0H0jMF4GCSqGSIb3
|
||||
DQEHATAdBglghkgBZQMEASoEEP/PW1JWehQx6/dsLkp/Mf+gMgQwFM9liLTqC56B
|
||||
nHILFmhac/+a/StQOKuf9dx5qXeGvt9LnwKuGGSfNX4g+dTkoa6N
|
||||
`
|
||||
ks.cms.decrypt(Buffer.from(example, 'base64'), (err, plain) => {
|
||||
expect(err).to.not.exist()
|
||||
const plain = await ks.cms.decrypt(Buffer.from(example, 'base64'))
|
||||
expect(plain).to.exist()
|
||||
expect(plain.toString()).to.equal(plainData.toString())
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -3,11 +3,11 @@
|
||||
'use strict'
|
||||
|
||||
const chai = require('chai')
|
||||
const dirtyChai = require('dirty-chai')
|
||||
const expect = chai.expect
|
||||
chai.use(dirtyChai)
|
||||
const fail = expect.fail
|
||||
chai.use(require('dirty-chai'))
|
||||
chai.use(require('chai-string'))
|
||||
const Keychain = require('..')
|
||||
const Keychain = require('../')
|
||||
const PeerId = require('peer-id')
|
||||
|
||||
module.exports = (datastore1, datastore2) => {
|
||||
@ -55,149 +55,112 @@ module.exports = (datastore1, datastore2) => {
|
||||
})
|
||||
|
||||
describe('key name', () => {
|
||||
it('is a valid filename and non-ASCII', () => {
|
||||
ks.removeKey('../../nasty', (err) => {
|
||||
expect(err).to.exist()
|
||||
expect(err).to.have.property('message', 'Invalid key name \'../../nasty\'')
|
||||
expect(err).to.have.property('code', 'ERR_INVALID_KEY_NAME')
|
||||
})
|
||||
ks.removeKey('', (err) => {
|
||||
expect(err).to.exist()
|
||||
expect(err).to.have.property('message', 'Invalid key name \'\'')
|
||||
expect(err).to.have.property('code', 'ERR_INVALID_KEY_NAME')
|
||||
})
|
||||
ks.removeKey(' ', (err) => {
|
||||
expect(err).to.exist()
|
||||
expect(err).to.have.property('message', 'Invalid key name \' \'')
|
||||
expect(err).to.have.property('code', 'ERR_INVALID_KEY_NAME')
|
||||
})
|
||||
ks.removeKey(null, (err) => {
|
||||
expect(err).to.exist()
|
||||
expect(err).to.have.property('message', 'Invalid key name \'null\'')
|
||||
expect(err).to.have.property('code', 'ERR_INVALID_KEY_NAME')
|
||||
})
|
||||
ks.removeKey(undefined, (err) => {
|
||||
expect(err).to.exist()
|
||||
expect(err).to.have.property('message', 'Invalid key name \'undefined\'')
|
||||
expect(err).to.have.property('code', 'ERR_INVALID_KEY_NAME')
|
||||
it('is a valid filename and non-ASCII', async () => {
|
||||
const errors = await Promise.all([
|
||||
ks.removeKey('../../nasty').then(fail, err => err),
|
||||
ks.removeKey('').then(fail, err => err),
|
||||
ks.removeKey(' ').then(fail, err => err),
|
||||
ks.removeKey(null).then(fail, err => err),
|
||||
ks.removeKey(undefined).then(fail, err => err)
|
||||
])
|
||||
|
||||
expect(errors).to.have.length(5)
|
||||
errors.forEach(error => {
|
||||
expect(error).to.have.property('code', 'ERR_INVALID_KEY_NAME')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('key', () => {
|
||||
it('can be an RSA key', function (done) {
|
||||
this.timeout(50 * 1000)
|
||||
ks.createKey(rsaKeyName, 'rsa', 2048, (err, info) => {
|
||||
expect(err).to.not.exist()
|
||||
expect(info).exist()
|
||||
rsaKeyInfo = info
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('has a name and id', () => {
|
||||
it('can be an RSA key', async () => {
|
||||
rsaKeyInfo = await ks.createKey(rsaKeyName, 'rsa', 2048)
|
||||
expect(rsaKeyInfo).to.exist()
|
||||
expect(rsaKeyInfo).to.have.property('name', rsaKeyName)
|
||||
expect(rsaKeyInfo).to.have.property('id')
|
||||
})
|
||||
|
||||
it('is encrypted PEM encoded PKCS #8', (done) => {
|
||||
ks._getPrivateKey(rsaKeyName, (err, pem) => {
|
||||
expect(err).to.not.exist()
|
||||
expect(pem).to.startsWith('-----BEGIN ENCRYPTED PRIVATE KEY-----')
|
||||
done()
|
||||
})
|
||||
it('is encrypted PEM encoded PKCS #8', async () => {
|
||||
const pem = await ks._getPrivateKey(rsaKeyName)
|
||||
return expect(pem).to.startsWith('-----BEGIN ENCRYPTED PRIVATE KEY-----')
|
||||
})
|
||||
|
||||
it('does not overwrite existing key', (done) => {
|
||||
ks.createKey(rsaKeyName, 'rsa', 2048, (err) => {
|
||||
it('throws if an invalid private key name is given', async () => {
|
||||
const err = await ks._getPrivateKey(undefined).then(fail, err => err)
|
||||
expect(err).to.exist()
|
||||
expect(err).to.have.property('code', 'ERR_INVALID_KEY_NAME')
|
||||
})
|
||||
|
||||
it('throws if a private key cant be found', async () => {
|
||||
const err = await ks._getPrivateKey('not real').then(fail, err => err)
|
||||
expect(err).to.exist()
|
||||
expect(err).to.have.property('code', 'ERR_KEY_NOT_FOUND')
|
||||
})
|
||||
|
||||
it('does not overwrite existing key', async () => {
|
||||
const err = await ks.createKey(rsaKeyName, 'rsa', 2048).then(fail, err => err)
|
||||
expect(err).to.have.property('code', 'ERR_KEY_ALREADY_EXISTS')
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('cannot create the "self" key', (done) => {
|
||||
ks.createKey('self', 'rsa', 2048, (err) => {
|
||||
it('cannot create the "self" key', async () => {
|
||||
const err = await ks.createKey('self', 'rsa', 2048).then(fail, err => err)
|
||||
expect(err).to.exist()
|
||||
done()
|
||||
})
|
||||
expect(err).to.have.property('code', 'ERR_INVALID_KEY_NAME')
|
||||
})
|
||||
|
||||
it('should validate name is string', (done) => {
|
||||
ks.createKey(5, 'rsa', 2048, (err) => {
|
||||
it('should validate name is string', async () => {
|
||||
const err = await ks.createKey(5, 'rsa', 2048).then(fail, err => err)
|
||||
expect(err).to.exist()
|
||||
expect(err.message).to.contain('Invalid key name')
|
||||
done()
|
||||
})
|
||||
expect(err).to.have.property('code', 'ERR_INVALID_KEY_NAME')
|
||||
})
|
||||
|
||||
it('should validate type is string', (done) => {
|
||||
ks.createKey('TEST' + Date.now(), null, 2048, (err) => {
|
||||
it('should validate type is string', async () => {
|
||||
const err = await ks.createKey('TEST' + Date.now(), null, 2048).then(fail, err => err)
|
||||
expect(err).to.exist()
|
||||
expect(err.message).to.contain('Invalid key type')
|
||||
done()
|
||||
})
|
||||
expect(err).to.have.property('code', 'ERR_INVALID_KEY_TYPE')
|
||||
})
|
||||
|
||||
it('should validate size is integer', (done) => {
|
||||
ks.createKey('TEST' + Date.now(), 'rsa', 'string', (err) => {
|
||||
it('should validate size is integer', async () => {
|
||||
const err = await ks.createKey('TEST' + Date.now(), 'rsa', 'string').then(fail, err => err)
|
||||
expect(err).to.exist()
|
||||
expect(err.message).to.contain('Invalid key size')
|
||||
done()
|
||||
})
|
||||
expect(err).to.have.property('code', 'ERR_INVALID_KEY_SIZE')
|
||||
})
|
||||
|
||||
describe('implements NIST SP 800-131A', () => {
|
||||
it('disallows RSA length < 2048', (done) => {
|
||||
ks.createKey('bad-nist-rsa', 'rsa', 1024, (err) => {
|
||||
it('disallows RSA length < 2048', async () => {
|
||||
const err = await ks.createKey('bad-nist-rsa', 'rsa', 1024).then(fail, err => err)
|
||||
expect(err).to.exist()
|
||||
expect(err).to.have.property('message', 'Invalid RSA key size 1024')
|
||||
expect(err).to.have.property('code', 'ERR_INVALID_KEY_SIZE')
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('query', () => {
|
||||
it('finds all existing keys', (done) => {
|
||||
ks.listKeys((err, keys) => {
|
||||
expect(err).to.not.exist()
|
||||
it('finds all existing keys', async () => {
|
||||
const keys = await ks.listKeys()
|
||||
expect(keys).to.exist()
|
||||
const mykey = keys.find((k) => k.name.normalize() === rsaKeyName.normalize())
|
||||
expect(mykey).to.exist()
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('finds a key by name', (done) => {
|
||||
ks.findKeyByName(rsaKeyName, (err, key) => {
|
||||
expect(err).to.not.exist()
|
||||
it('finds a key by name', async () => {
|
||||
const key = await ks.findKeyByName(rsaKeyName)
|
||||
expect(key).to.exist()
|
||||
expect(key).to.deep.equal(rsaKeyInfo)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('finds a key by id', (done) => {
|
||||
ks.findKeyById(rsaKeyInfo.id, (err, key) => {
|
||||
expect(err).to.not.exist()
|
||||
it('finds a key by id', async () => {
|
||||
const key = await ks.findKeyById(rsaKeyInfo.id)
|
||||
expect(key).to.exist()
|
||||
expect(key).to.deep.equal(rsaKeyInfo)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('returns the key\'s name and id', (done) => {
|
||||
ks.listKeys((err, keys) => {
|
||||
expect(err).to.not.exist()
|
||||
it('returns the key\'s name and id', async () => {
|
||||
const keys = await ks.listKeys()
|
||||
expect(keys).to.exist()
|
||||
keys.forEach((key) => {
|
||||
expect(key).to.have.property('name')
|
||||
expect(key).to.have.property('id')
|
||||
})
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -205,103 +168,97 @@ module.exports = (datastore1, datastore2) => {
|
||||
const plainData = Buffer.from('This is a message from Alice to Bob')
|
||||
let cms
|
||||
|
||||
it('service is available', (done) => {
|
||||
it('service is available', () => {
|
||||
expect(ks).to.have.property('cms')
|
||||
done()
|
||||
})
|
||||
|
||||
it('requires a key', (done) => {
|
||||
ks.cms.encrypt('no-key', plainData, (err, msg) => {
|
||||
it('requires a key', async () => {
|
||||
const err = await ks.cms.encrypt('no-key', plainData).then(fail, err => err)
|
||||
expect(err).to.exist()
|
||||
done()
|
||||
})
|
||||
expect(err).to.have.property('code', 'ERR_KEY_NOT_FOUND')
|
||||
})
|
||||
|
||||
it('requires plain data as a Buffer', (done) => {
|
||||
ks.cms.encrypt(rsaKeyName, 'plain data', (err, msg) => {
|
||||
it('requires plain data as a Buffer', async () => {
|
||||
const err = await ks.cms.encrypt(rsaKeyName, 'plain data').then(fail, err => err)
|
||||
expect(err).to.exist()
|
||||
done()
|
||||
})
|
||||
expect(err).to.have.property('code', 'ERR_INVALID_PARAMS')
|
||||
})
|
||||
|
||||
it('encrypts', (done) => {
|
||||
ks.cms.encrypt(rsaKeyName, plainData, (err, msg) => {
|
||||
expect(err).to.not.exist()
|
||||
expect(msg).to.exist()
|
||||
expect(msg).to.be.instanceOf(Buffer)
|
||||
cms = msg
|
||||
done()
|
||||
})
|
||||
it('encrypts', async () => {
|
||||
cms = await ks.cms.encrypt(rsaKeyName, plainData)
|
||||
expect(cms).to.exist()
|
||||
expect(cms).to.be.instanceOf(Buffer)
|
||||
})
|
||||
|
||||
it('is a PKCS #7 message', (done) => {
|
||||
ks.cms.decrypt('not CMS', (err) => {
|
||||
it('is a PKCS #7 message', async () => {
|
||||
const err = await ks.cms.decrypt('not CMS').then(fail, err => err)
|
||||
expect(err).to.exist()
|
||||
done()
|
||||
})
|
||||
expect(err).to.have.property('code', 'ERR_INVALID_PARAMS')
|
||||
})
|
||||
|
||||
it('is a PKCS #7 binary message', (done) => {
|
||||
ks.cms.decrypt(plainData, (err) => {
|
||||
it('is a PKCS #7 binary message', async () => {
|
||||
const err = await ks.cms.decrypt(plainData).then(fail, err => err)
|
||||
expect(err).to.exist()
|
||||
done()
|
||||
})
|
||||
expect(err).to.have.property('code', 'ERR_INVALID_CMS')
|
||||
})
|
||||
|
||||
it('cannot be read without the key', (done) => {
|
||||
emptyKeystore.cms.decrypt(cms, (err, plain) => {
|
||||
it('cannot be read without the key', async () => {
|
||||
const err = await emptyKeystore.cms.decrypt(cms).then(fail, err => err)
|
||||
expect(err).to.exist()
|
||||
expect(err).to.have.property('missingKeys')
|
||||
expect(err.missingKeys).to.eql([rsaKeyInfo.id])
|
||||
expect(err).to.have.property('code', 'ERR_MISSING_KEYS')
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('can be read with the key', (done) => {
|
||||
ks.cms.decrypt(cms, (err, plain) => {
|
||||
expect(err).to.not.exist()
|
||||
it('can be read with the key', async () => {
|
||||
const plain = await ks.cms.decrypt(cms)
|
||||
expect(plain).to.exist()
|
||||
expect(plain.toString()).to.equal(plainData.toString())
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('exported key', () => {
|
||||
let pemKey
|
||||
|
||||
it('is a PKCS #8 encrypted pem', (done) => {
|
||||
ks.exportKey(rsaKeyName, 'password', (err, pem) => {
|
||||
expect(err).to.not.exist()
|
||||
expect(pem).to.startsWith('-----BEGIN ENCRYPTED PRIVATE KEY-----')
|
||||
pemKey = pem
|
||||
done()
|
||||
})
|
||||
it('requires the password', async () => {
|
||||
const err = await ks.exportKey(rsaKeyName).then(fail, err => err)
|
||||
expect(err).to.exist()
|
||||
expect(err).to.have.property('code', 'ERR_PASSWORD_REQUIRED')
|
||||
})
|
||||
|
||||
it('can be imported', (done) => {
|
||||
ks.importKey('imported-key', pemKey, 'password', (err, key) => {
|
||||
expect(err).to.not.exist()
|
||||
it('requires the key name', async () => {
|
||||
const err = await ks.exportKey(undefined, 'password').then(fail, err => err)
|
||||
expect(err).to.exist()
|
||||
expect(err).to.have.property('code', 'ERR_INVALID_KEY_NAME')
|
||||
})
|
||||
|
||||
it('is a PKCS #8 encrypted pem', async () => {
|
||||
pemKey = await ks.exportKey(rsaKeyName, 'password')
|
||||
expect(pemKey).to.startsWith('-----BEGIN ENCRYPTED PRIVATE KEY-----')
|
||||
})
|
||||
|
||||
it('can be imported', async () => {
|
||||
const key = await ks.importKey('imported-key', pemKey, 'password')
|
||||
expect(key.name).to.equal('imported-key')
|
||||
expect(key.id).to.equal(rsaKeyInfo.id)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('cannot be imported as an existing key name', (done) => {
|
||||
ks.importKey(rsaKeyName, pemKey, 'password', (err, key) => {
|
||||
it('requires the pem', async () => {
|
||||
const err = await ks.importKey('imported-key', undefined, 'password').then(fail, err => err)
|
||||
expect(err).to.exist()
|
||||
done()
|
||||
})
|
||||
expect(err).to.have.property('code', 'ERR_PEM_REQUIRED')
|
||||
})
|
||||
|
||||
it('cannot be imported with the wrong password', function (done) {
|
||||
this.timeout(5 * 1000)
|
||||
ks.importKey('a-new-name-for-import', pemKey, 'not the password', (err, key) => {
|
||||
it('cannot be imported as an existing key name', async () => {
|
||||
const err = await ks.importKey(rsaKeyName, pemKey, 'password').then(fail, err => err)
|
||||
expect(err).to.exist()
|
||||
done()
|
||||
expect(err).to.have.property('code', 'ERR_KEY_ALREADY_EXISTS')
|
||||
})
|
||||
|
||||
it('cannot be imported with the wrong password', async () => {
|
||||
const err = await ks.importKey('a-new-name-for-import', pemKey, 'not the password').then(fail, err => err)
|
||||
expect(err).to.exist()
|
||||
expect(err).to.have.property('code', 'ERR_CANNOT_READ_KEY')
|
||||
})
|
||||
})
|
||||
|
||||
@ -309,136 +266,117 @@ module.exports = (datastore1, datastore2) => {
|
||||
const alicePrivKey = 'CAASpgkwggSiAgEAAoIBAQC2SKo/HMFZeBml1AF3XijzrxrfQXdJzjePBZAbdxqKR1Mc6juRHXij6HXYPjlAk01BhF1S3Ll4Lwi0cAHhggf457sMg55UWyeGKeUv0ucgvCpBwlR5cQ020i0MgzjPWOLWq1rtvSbNcAi2ZEVn6+Q2EcHo3wUvWRtLeKz+DZSZfw2PEDC+DGPJPl7f8g7zl56YymmmzH9liZLNrzg/qidokUv5u1pdGrcpLuPNeTODk0cqKB+OUbuKj9GShYECCEjaybJDl9276oalL9ghBtSeEv20kugatTvYy590wFlJkkvyl+nPxIH0EEYMKK9XRWlu9XYnoSfboiwcv8M3SlsjAgMBAAECggEAZtju/bcKvKFPz0mkHiaJcpycy9STKphorpCT83srBVQi59CdFU6Mj+aL/xt0kCPMVigJw8P3/YCEJ9J+rS8BsoWE+xWUEsJvtXoT7vzPHaAtM3ci1HZd302Mz1+GgS8Epdx+7F5p80XAFLDUnELzOzKftvWGZmWfSeDnslwVONkL/1VAzwKy7Ce6hk4SxRE7l2NE2OklSHOzCGU1f78ZzVYKSnS5Ag9YrGjOAmTOXDbKNKN/qIorAQ1bovzGoCwx3iGIatQKFOxyVCyO1PsJYT7JO+kZbhBWRRE+L7l+ppPER9bdLFxs1t5CrKc078h+wuUr05S1P1JjXk68pk3+kQKBgQDeK8AR11373Mzib6uzpjGzgNRMzdYNuExWjxyxAzz53NAR7zrPHvXvfIqjDScLJ4NcRO2TddhXAfZoOPVH5k4PJHKLBPKuXZpWlookCAyENY7+Pd55S8r+a+MusrMagYNljb5WbVTgN8cgdpim9lbbIFlpN6SZaVjLQL3J8TWH6wKBgQDSChzItkqWX11CNstJ9zJyUE20I7LrpyBJNgG1gtvz3ZMUQCn3PxxHtQzN9n1P0mSSYs+jBKPuoSyYLt1wwe10/lpgL4rkKWU3/m1Myt0tveJ9WcqHh6tzcAbb/fXpUFT/o4SWDimWkPkuCb+8j//2yiXk0a/T2f36zKMuZvujqQKBgC6B7BAQDG2H2B/ijofp12ejJU36nL98gAZyqOfpLJ+FeMz4TlBDQ+phIMhnHXA5UkdDapQ+zA3SrFk+6yGk9Vw4Hf46B+82SvOrSbmnMa+PYqKYIvUzR4gg34rL/7AhwnbEyD5hXq4dHwMNsIDq+l2elPjwm/U9V0gdAl2+r50HAoGALtsKqMvhv8HucAMBPrLikhXP/8um8mMKFMrzfqZ+otxfHzlhI0L08Bo3jQrb0Z7ByNY6M8epOmbCKADsbWcVre/AAY0ZkuSZK/CaOXNX/AhMKmKJh8qAOPRY02LIJRBCpfS4czEdnfUhYV/TYiFNnKRj57PPYZdTzUsxa/yVTmECgYBr7slQEjb5Onn5mZnGDh+72BxLNdgwBkhO0OCdpdISqk0F0Pxby22DFOKXZEpiyI9XYP1C8wPiJsShGm2yEwBPWXnrrZNWczaVuCbXHrZkWQogBDG3HGXNdU4MAWCyiYlyinIBpPpoAJZSzpGLmWbMWh28+RJS6AQX6KHrK1o2uw=='
|
||||
let alice
|
||||
|
||||
before(function (done) {
|
||||
before(async function () {
|
||||
const encoded = Buffer.from(alicePrivKey, 'base64')
|
||||
PeerId.createFromPrivKey(encoded, (err, id) => {
|
||||
expect(err).to.not.exist()
|
||||
alice = id
|
||||
done()
|
||||
})
|
||||
alice = await PeerId.createFromPrivKey(encoded)
|
||||
})
|
||||
|
||||
it('private key can be imported', (done) => {
|
||||
ks.importPeer('alice', alice, (err, key) => {
|
||||
expect(err).to.not.exist()
|
||||
it('private key can be imported', async () => {
|
||||
const key = await ks.importPeer('alice', alice)
|
||||
expect(key.name).to.equal('alice')
|
||||
expect(key.id).to.equal(alice.toB58String())
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('key id exists', (done) => {
|
||||
ks.findKeyById(alice.toB58String(), (err, key) => {
|
||||
expect(err).to.not.exist()
|
||||
it('private key import requires a valid name', async () => {
|
||||
const err = await ks.importPeer(undefined, alice).then(fail, err => err)
|
||||
expect(err).to.exist()
|
||||
expect(err).to.have.property('code', 'ERR_INVALID_KEY_NAME')
|
||||
})
|
||||
|
||||
it('private key import requires the peer', async () => {
|
||||
const err = await ks.importPeer('alice').then(fail, err => err)
|
||||
expect(err).to.exist()
|
||||
expect(err).to.have.property('code', 'ERR_MISSING_PRIVATE_KEY')
|
||||
})
|
||||
|
||||
it('key id exists', async () => {
|
||||
const key = await ks.findKeyById(alice.toB58String())
|
||||
expect(key).to.exist()
|
||||
expect(key).to.have.property('name', 'alice')
|
||||
expect(key).to.have.property('id', alice.toB58String())
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('key name exists', (done) => {
|
||||
ks.findKeyByName('alice', (err, key) => {
|
||||
expect(err).to.not.exist()
|
||||
it('key name exists', async () => {
|
||||
const key = await ks.findKeyByName('alice')
|
||||
expect(key).to.exist()
|
||||
expect(key).to.have.property('name', 'alice')
|
||||
expect(key).to.have.property('id', alice.toB58String())
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('rename', () => {
|
||||
it('requires an existing key name', (done) => {
|
||||
ks.renameKey('not-there', renamedRsaKeyName, (err) => {
|
||||
it('requires an existing key name', async () => {
|
||||
const err = await ks.renameKey('not-there', renamedRsaKeyName).then(fail, err => err)
|
||||
expect(err).to.exist()
|
||||
expect(err).to.have.property('code', 'ERR_KEY_NOT_FOUND')
|
||||
done()
|
||||
})
|
||||
expect(err).to.have.property('code', 'ERR_NOT_FOUND')
|
||||
})
|
||||
|
||||
it('requires a valid new key name', (done) => {
|
||||
ks.renameKey(rsaKeyName, '..\not-valid', (err) => {
|
||||
it('requires a valid new key name', async () => {
|
||||
const err = await ks.renameKey(rsaKeyName, '..\not-valid').then(fail, err => err)
|
||||
expect(err).to.exist()
|
||||
expect(err).to.have.property('code', 'ERR_NEW_KEY_NAME_INVALID')
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('does not overwrite existing key', (done) => {
|
||||
ks.renameKey(rsaKeyName, rsaKeyName, (err) => {
|
||||
it('does not overwrite existing key', async () => {
|
||||
const err = await ks.renameKey(rsaKeyName, rsaKeyName).then(fail, err => err)
|
||||
expect(err).to.exist()
|
||||
expect(err).to.have.property('code', 'ERR_KEY_ALREADY_EXISTS')
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('cannot create the "self" key', (done) => {
|
||||
ks.renameKey(rsaKeyName, 'self', (err) => {
|
||||
it('cannot create the "self" key', async () => {
|
||||
const err = await ks.renameKey(rsaKeyName, 'self').then(fail, err => err)
|
||||
expect(err).to.exist()
|
||||
expect(err).to.have.property('code', 'ERR_NEW_KEY_NAME_INVALID')
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('removes the existing key name', (done) => {
|
||||
ks.renameKey(rsaKeyName, renamedRsaKeyName, (err, key) => {
|
||||
expect(err).to.not.exist()
|
||||
it('removes the existing key name', async () => {
|
||||
const key = await ks.renameKey(rsaKeyName, renamedRsaKeyName)
|
||||
expect(key).to.exist()
|
||||
expect(key).to.have.property('name', renamedRsaKeyName)
|
||||
expect(key).to.have.property('id', rsaKeyInfo.id)
|
||||
ks.findKeyByName(rsaKeyName, (err, key) => {
|
||||
// Try to find the changed key
|
||||
const err = await ks.findKeyByName(rsaKeyName).then(fail, err => err)
|
||||
expect(err).to.exist()
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('creates the new key name', (done) => {
|
||||
ks.findKeyByName(renamedRsaKeyName, (err, key) => {
|
||||
expect(err).to.not.exist()
|
||||
it('creates the new key name', async () => {
|
||||
const key = await ks.findKeyByName(renamedRsaKeyName)
|
||||
expect(key).to.exist()
|
||||
expect(key).to.have.property('name', renamedRsaKeyName)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('does not change the key ID', (done) => {
|
||||
ks.findKeyByName(renamedRsaKeyName, (err, key) => {
|
||||
expect(err).to.not.exist()
|
||||
it('does not change the key ID', async () => {
|
||||
const key = await ks.findKeyByName(renamedRsaKeyName)
|
||||
expect(key).to.exist()
|
||||
expect(key).to.have.property('name', renamedRsaKeyName)
|
||||
expect(key).to.have.property('id', rsaKeyInfo.id)
|
||||
done()
|
||||
})
|
||||
|
||||
it('throws with invalid key names', async () => {
|
||||
const err = await ks.findKeyByName(undefined).then(fail, err => err)
|
||||
expect(err).to.exist()
|
||||
expect(err).to.have.property('code', 'ERR_INVALID_KEY_NAME')
|
||||
})
|
||||
})
|
||||
|
||||
describe('key removal', () => {
|
||||
it('cannot remove the "self" key', (done) => {
|
||||
ks.removeKey('self', (err) => {
|
||||
it('cannot remove the "self" key', async () => {
|
||||
const err = await ks.removeKey('self').then(fail, err => err)
|
||||
expect(err).to.exist()
|
||||
expect(err).to.have.property('code', 'ERR_INVALID_KEY_NAME')
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('cannot remove an unknown key', (done) => {
|
||||
ks.removeKey('not-there', (err) => {
|
||||
it('cannot remove an unknown key', async () => {
|
||||
const err = await ks.removeKey('not-there').then(fail, err => err)
|
||||
expect(err).to.exist()
|
||||
expect(err).to.have.property('code', 'ERR_KEY_NOT_FOUND')
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('can remove a known key', (done) => {
|
||||
ks.removeKey(renamedRsaKeyName, (err, key) => {
|
||||
expect(err).to.not.exist()
|
||||
it('can remove a known key', async () => {
|
||||
const key = await ks.removeKey(renamedRsaKeyName)
|
||||
expect(key).to.exist()
|
||||
expect(key).to.have.property('name', renamedRsaKeyName)
|
||||
expect(key).to.have.property('id', rsaKeyInfo.id)
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
24
test/node.js
24
test/node.js
@ -3,8 +3,8 @@
|
||||
|
||||
const os = require('os')
|
||||
const path = require('path')
|
||||
const rimraf = require('rimraf')
|
||||
const series = require('async/series')
|
||||
const promisify = require('promisify-es6')
|
||||
const rimraf = promisify(require('rimraf'))
|
||||
const FsStore = require('datastore-fs')
|
||||
|
||||
describe('node', () => {
|
||||
@ -13,20 +13,16 @@ describe('node', () => {
|
||||
const datastore1 = new FsStore(store1)
|
||||
const datastore2 = new FsStore(store2)
|
||||
|
||||
before((done) => {
|
||||
series([
|
||||
(cb) => datastore1.open(cb),
|
||||
(cb) => datastore2.open(cb)
|
||||
], done)
|
||||
before(async () => {
|
||||
await datastore1.open()
|
||||
await datastore2.open()
|
||||
})
|
||||
|
||||
after((done) => {
|
||||
series([
|
||||
(cb) => datastore1.close(cb),
|
||||
(cb) => datastore2.close(cb),
|
||||
(cb) => rimraf(store1, cb),
|
||||
(cb) => rimraf(store2, cb)
|
||||
], done)
|
||||
after(async () => {
|
||||
await datastore1.close()
|
||||
await datastore2.close()
|
||||
await rimraf(store1)
|
||||
await rimraf(store2)
|
||||
})
|
||||
|
||||
require('./keychain.spec')(datastore1, datastore2)
|
||||
|
@ -21,55 +21,32 @@ describe('peer ID', () => {
|
||||
let peer
|
||||
let publicKeyDer // a buffer
|
||||
|
||||
before(function (done) {
|
||||
before(async () => {
|
||||
const encoded = Buffer.from(sample.privKey, 'base64')
|
||||
PeerId.createFromPrivKey(encoded, (err, id) => {
|
||||
expect(err).to.not.exist()
|
||||
peer = id
|
||||
done()
|
||||
})
|
||||
peer = await PeerId.createFromPrivKey(encoded)
|
||||
})
|
||||
|
||||
it('decoded public key', (done) => {
|
||||
// console.log('peer id', peer.toJSON())
|
||||
// console.log('id', peer.toB58String())
|
||||
// console.log('id decoded', multihash.decode(peer.id))
|
||||
|
||||
it('decoded public key', () => {
|
||||
// get protobuf version of the public key
|
||||
const publicKeyProtobuf = peer.marshalPubKey()
|
||||
const publicKey = crypto.keys.unmarshalPublicKey(publicKeyProtobuf)
|
||||
// console.log('public key', publicKey)
|
||||
publicKeyDer = publicKey.marshal()
|
||||
// console.log('public key der', publicKeyDer.toString('base64'))
|
||||
|
||||
// get protobuf version of the private key
|
||||
const privateKeyProtobuf = peer.marshalPrivKey()
|
||||
crypto.keys.unmarshalPrivateKey(privateKeyProtobuf, (err, key) => {
|
||||
expect(err).to.not.exist()
|
||||
// console.log('private key', key)
|
||||
// console.log('\nprivate key der', key.marshal().toString('base64'))
|
||||
done()
|
||||
})
|
||||
const key = crypto.keys.unmarshalPrivateKey(privateKeyProtobuf)
|
||||
expect(key).to.exist()
|
||||
})
|
||||
|
||||
it('encoded public key with DER', (done) => {
|
||||
it('encoded public key with DER', async () => {
|
||||
const jwk = rsaUtils.pkixToJwk(publicKeyDer)
|
||||
// console.log('jwk', jwk)
|
||||
const rsa = new rsaClass.RsaPublicKey(jwk)
|
||||
// console.log('rsa', rsa)
|
||||
rsa.hash((err, keyId) => {
|
||||
expect(err).to.not.exist()
|
||||
// console.log('err', err)
|
||||
// console.log('keyId', keyId)
|
||||
// console.log('id decoded', multihash.decode(keyId))
|
||||
const keyId = await rsa.hash()
|
||||
const kids = multihash.toB58String(keyId)
|
||||
// console.log('id', kids)
|
||||
expect(kids).to.equal(peer.toB58String())
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('encoded public key with JWT', (done) => {
|
||||
it('encoded public key with JWT', async () => {
|
||||
const jwk = {
|
||||
kty: 'RSA',
|
||||
n: 'tkiqPxzBWXgZpdQBd14o868a30F3Sc43jwWQG3caikdTHOo7kR14o-h12D45QJNNQYRdUty5eC8ItHAB4YIH-Oe7DIOeVFsnhinlL9LnILwqQcJUeXENNtItDIM4z1ji1qta7b0mzXAItmRFZ-vkNhHB6N8FL1kbS3is_g2UmX8NjxAwvgxjyT5e3_IO85eemMpppsx_ZYmSza84P6onaJFL-btaXRq3KS7jzXkzg5NHKigfjlG7io_RkoWBAghI2smyQ5fdu-qGpS_YIQbUnhL9tJLoGrU72MufdMBZSZJL8pfpz8SB9BBGDCivV0VpbvV2J6En26IsHL_DN0pbIw',
|
||||
@ -77,33 +54,16 @@ describe('peer ID', () => {
|
||||
alg: 'RS256',
|
||||
kid: '2011-04-29'
|
||||
}
|
||||
// console.log('jwk', jwk)
|
||||
const rsa = new rsaClass.RsaPublicKey(jwk)
|
||||
// console.log('rsa', rsa)
|
||||
rsa.hash((err, keyId) => {
|
||||
expect(err).to.not.exist()
|
||||
// console.log('err', err)
|
||||
// console.log('keyId', keyId)
|
||||
// console.log('id decoded', multihash.decode(keyId))
|
||||
const keyId = await rsa.hash()
|
||||
const kids = multihash.toB58String(keyId)
|
||||
// console.log('id', kids)
|
||||
expect(kids).to.equal(peer.toB58String())
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('decoded private key', (done) => {
|
||||
// console.log('peer id', peer.toJSON())
|
||||
// console.log('id', peer.toB58String())
|
||||
// console.log('id decoded', multihash.decode(peer.id))
|
||||
|
||||
it('decoded private key', async () => {
|
||||
// get protobuf version of the private key
|
||||
const privateKeyProtobuf = peer.marshalPrivKey()
|
||||
crypto.keys.unmarshalPrivateKey(privateKeyProtobuf, (err, key) => {
|
||||
expect(err).to.not.exist()
|
||||
// console.log('private key', key)
|
||||
// console.log('\nprivate key der', key.marshal().toString('base64'))
|
||||
done()
|
||||
})
|
||||
const key = await crypto.keys.unmarshalPrivateKey(privateKeyProtobuf)
|
||||
expect(key).to.exist()
|
||||
})
|
||||
})
|
||||
|
Loading…
x
Reference in New Issue
Block a user