diff --git a/.gitattributes b/.gitattributes
deleted file mode 100644
index ef41d4fa..00000000
--- a/.gitattributes
+++ /dev/null
@@ -1,2 +0,0 @@
-*.png binary
-* crlf=input
diff --git a/keychain/CHANGELOG.md b/keychain/CHANGELOG.md
deleted file mode 100644
index f661d419..00000000
--- a/keychain/CHANGELOG.md
+++ /dev/null
@@ -1,147 +0,0 @@
-
-# [0.6.0](https://github.com/libp2p/js-libp2p-keychain/compare/v0.5.4...v0.6.0) (2019-12-18)
-
-
-
-
-## [0.5.4](https://github.com/libp2p/js-libp2p-keychain/compare/v0.5.3...v0.5.4) (2019-12-18)
-
-
-
-
-## [0.5.3](https://github.com/libp2p/js-libp2p-keychain/compare/v0.5.2...v0.5.3) (2019-12-18)
-
-
-
-
-## [0.5.2](https://github.com/libp2p/js-libp2p-keychain/compare/v0.5.1...v0.5.2) (2019-12-02)
-
-
-
-
-## [0.5.1](https://github.com/libp2p/js-libp2p-keychain/compare/v0.5.0...v0.5.1) (2019-09-25)
-
-
-
-
-# [0.5.0](https://github.com/libp2p/js-libp2p-keychain/compare/v0.4.2...v0.5.0) (2019-08-16)
-
-
-* refactor: use async/await instead of callbacks (#37) ([dda315a](https://github.com/libp2p/js-libp2p-keychain/commit/dda315a)), closes [#37](https://github.com/libp2p/js-libp2p-keychain/issues/37)
-
-
-### BREAKING CHANGES
-
-* The api now uses async/await instead of callbacks.
-
-Co-Authored-By: Vasco Santos
-
-
-
-
-## [0.4.2](https://github.com/libp2p/js-libp2p-keychain/compare/v0.4.1...v0.4.2) (2019-06-13)
-
-
-### Bug Fixes
-
-* throw errors with correct stack trace ([#35](https://github.com/libp2p/js-libp2p-keychain/issues/35)) ([7051b9c](https://github.com/libp2p/js-libp2p-keychain/commit/7051b9c))
-
-
-
-
-## [0.4.1](https://github.com/libp2p/js-libp2p-keychain/compare/v0.4.0...v0.4.1) (2019-03-14)
-
-
-
-
-# [0.4.0](https://github.com/libp2p/js-libp2p-keychain/compare/v0.3.6...v0.4.0) (2019-02-26)
-
-
-### Features
-
-* adds support for ed25199 and secp256k1 ([#31](https://github.com/libp2p/js-libp2p-keychain/issues/31)) ([9eb11f4](https://github.com/libp2p/js-libp2p-keychain/commit/9eb11f4))
-
-
-
-
-## [0.3.6](https://github.com/libp2p/js-libp2p-keychain/compare/v0.3.5...v0.3.6) (2019-01-10)
-
-
-### Bug Fixes
-
-* reduce bundle size ([#28](https://github.com/libp2p/js-libp2p-keychain/issues/28)) ([7eeed87](https://github.com/libp2p/js-libp2p-keychain/commit/7eeed87))
-
-
-
-
-## [0.3.5](https://github.com/libp2p/js-libp2p-keychain/compare/v0.3.4...v0.3.5) (2019-01-10)
-
-
-
-
-## [0.3.4](https://github.com/libp2p/js-libp2p-keychain/compare/v0.3.3...v0.3.4) (2019-01-04)
-
-
-
-
-## [0.3.3](https://github.com/libp2p/js-libp2p-keychain/compare/v0.3.2...v0.3.3) (2018-10-25)
-
-
-
-
-## [0.3.2](https://github.com/libp2p/js-libp2p-keychain/compare/v0.3.1...v0.3.2) (2018-09-18)
-
-
-### Bug Fixes
-
-* validate createKey params properly ([#26](https://github.com/libp2p/js-libp2p-keychain/issues/26)) ([8dfaab1](https://github.com/libp2p/js-libp2p-keychain/commit/8dfaab1))
-
-
-
-
-## [0.3.1](https://github.com/libp2p/js-libp2p-keychain/compare/v0.3.0...v0.3.1) (2018-01-29)
-
-
-
-
-# [0.3.0](https://github.com/libp2p/js-libp2p-keychain/compare/v0.2.1...v0.3.0) (2018-01-29)
-
-
-### Bug Fixes
-
-* deepmerge 2.0.1 fails in browser, stay with 1.5.2 ([2ce4444](https://github.com/libp2p/js-libp2p-keychain/commit/2ce4444))
-
-
-
-
-## [0.2.1](https://github.com/libp2p/js-libp2p-keychain/compare/v0.2.0...v0.2.1) (2017-12-28)
-
-
-### Features
-
-* generate unique options for a key chain ([#20](https://github.com/libp2p/js-libp2p-keychain/issues/20)) ([89a451c](https://github.com/libp2p/js-libp2p-keychain/commit/89a451c))
-
-
-
-
-# 0.2.0 (2017-12-20)
-
-
-### Bug Fixes
-
-* error message ([8305d20](https://github.com/libp2p/js-libp2p-keychain/commit/8305d20))
-* lint errors ([06917f7](https://github.com/libp2p/js-libp2p-keychain/commit/06917f7))
-* lint errors ([ff4f656](https://github.com/libp2p/js-libp2p-keychain/commit/ff4f656))
-* linting ([409a999](https://github.com/libp2p/js-libp2p-keychain/commit/409a999))
-* maps an IPFS hash name to its forge equivalent ([f71d3a6](https://github.com/libp2p/js-libp2p-keychain/commit/f71d3a6)), closes [#12](https://github.com/libp2p/js-libp2p-keychain/issues/12)
-* more linting ([7c44c91](https://github.com/libp2p/js-libp2p-keychain/commit/7c44c91))
-* return info on removed key [#10](https://github.com/libp2p/js-libp2p-keychain/issues/10) ([f49e753](https://github.com/libp2p/js-libp2p-keychain/commit/f49e753))
-
-
-### Features
-
-* move bits from https://github.com/richardschneider/ipfs-encryption ([1a96ae8](https://github.com/libp2p/js-libp2p-keychain/commit/1a96ae8))
-* use libp2p-crypto ([#18](https://github.com/libp2p/js-libp2p-keychain/issues/18)) ([c1627a9](https://github.com/libp2p/js-libp2p-keychain/commit/c1627a9))
-
-
-
diff --git a/keychain/LICENSE b/keychain/LICENSE
deleted file mode 100644
index bbfffbf9..00000000
--- a/keychain/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2017 libp2p
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/keychain/package.json b/keychain/package.json
deleted file mode 100644
index 29b8873a..00000000
--- a/keychain/package.json
+++ /dev/null
@@ -1,77 +0,0 @@
-{
- "name": "libp2p-keychain",
- "version": "0.6.0",
- "description": "Key management and cryptographically protected messages",
- "leadMaintainer": "Vasco Santos ",
- "main": "src/index.js",
- "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"
- },
- "pre-push": [
- "lint"
- ],
- "engines": {
- "node": ">=10.0.0",
- "npm": ">=3.0.0"
- },
- "repository": {
- "type": "git",
- "url": "git+https://github.com/libp2p/js-libp2p-keychain.git"
- },
- "keywords": [
- "IPFS",
- "libp2p",
- "keys",
- "encryption",
- "secure",
- "crypto"
- ],
- "license": "MIT",
- "bugs": {
- "url": "https://github.com/libp2p/js-libp2p-keychain/issues"
- },
- "homepage": "https://github.com/libp2p/js-libp2p-keychain#readme",
- "dependencies": {
- "err-code": "^2.0.0",
- "interface-datastore": "^0.8.0",
- "libp2p-crypto": "^0.17.1",
- "merge-options": "^2.0.0",
- "node-forge": "^0.9.1",
- "sanitize-filename": "^1.6.1"
- },
- "devDependencies": {
- "aegir": "^20.0.0",
- "chai": "^4.2.0",
- "chai-string": "^1.5.0",
- "datastore-fs": "^0.9.0",
- "datastore-level": "^0.14.0",
- "dirty-chai": "^2.0.1",
- "level": "^6.0.0",
- "multihashes": "^0.4.15",
- "peer-id": "^0.13.5",
- "promisify-es6": "^1.0.3",
- "rimraf": "^3.0.0"
- },
- "contributors": [
- "Alan Shaw ",
- "Alberto Elias ",
- "Alex Potsides ",
- "David Dias ",
- "Hugo Dias ",
- "Jacob Heun ",
- "Maciej Krüger ",
- "Masahiro Saito ",
- "Richard Schneider ",
- "Vasco Santos ",
- "Vasco Santos ",
- "Victor Bjelkholm "
- ]
-}
diff --git a/keychain/test/browser.js b/keychain/test/browser.js
deleted file mode 100644
index 02222fb3..00000000
--- a/keychain/test/browser.js
+++ /dev/null
@@ -1,27 +0,0 @@
-/* eslint-env mocha */
-'use strict'
-
-const LevelStore = require('datastore-level')
-
-describe('browser', () => {
- const datastore1 = new LevelStore('test-keystore-1', { db: require('level') })
- const datastore2 = new LevelStore('test-keystore-2', { db: require('level') })
-
- before(() => {
- return Promise.all([
- datastore1.open(),
- datastore2.open()
- ])
- })
-
- after(() => {
- return Promise.all([
- datastore1.close(),
- datastore2.close()
- ])
- })
-
- require('./keychain.spec')(datastore1, datastore2)
- require('./cms-interop')(datastore2)
- require('./peerid')
-})
diff --git a/keychain/test/keychain.spec.js b/keychain/test/keychain.spec.js
deleted file mode 100644
index c455f2d7..00000000
--- a/keychain/test/keychain.spec.js
+++ /dev/null
@@ -1,383 +0,0 @@
-/* eslint max-nested-callbacks: ["error", 8] */
-/* eslint-env mocha */
-'use strict'
-
-const chai = require('chai')
-const expect = chai.expect
-const fail = expect.fail
-chai.use(require('dirty-chai'))
-chai.use(require('chai-string'))
-const Keychain = require('../')
-const PeerId = require('peer-id')
-
-module.exports = (datastore1, datastore2) => {
- describe('keychain', () => {
- const passPhrase = 'this is not a secure phrase'
- const rsaKeyName = 'tajné jméno'
- const renamedRsaKeyName = 'ชื่อลับ'
- let rsaKeyInfo
- let emptyKeystore
- let ks
-
- before((done) => {
- ks = new Keychain(datastore2, { passPhrase: passPhrase })
- emptyKeystore = new Keychain(datastore1, { passPhrase: passPhrase })
- done()
- })
-
- it('needs a pass phrase to encrypt a key', () => {
- expect(() => new Keychain(datastore2)).to.throw()
- })
-
- it('needs a NIST SP 800-132 non-weak pass phrase', () => {
- expect(() => new Keychain(datastore2, { passPhrase: '< 20 character' })).to.throw()
- })
-
- it('needs a store to persist a key', () => {
- expect(() => new Keychain(null, { passPhrase: passPhrase })).to.throw()
- })
-
- it('has default options', () => {
- expect(Keychain.options).to.exist()
- })
-
- it('needs a supported hashing alorithm', () => {
- const ok = new Keychain(datastore2, { passPhrase: passPhrase, dek: { hash: 'sha2-256' } })
- expect(ok).to.exist()
- expect(() => new Keychain(datastore2, { passPhrase: passPhrase, dek: { hash: 'my-hash' } })).to.throw()
- })
-
- it('can generate options', () => {
- const options = Keychain.generateOptions()
- options.passPhrase = passPhrase
- const chain = new Keychain(datastore2, options)
- expect(chain).to.exist()
- })
-
- describe('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', 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', async () => {
- const pem = await ks._getPrivateKey(rsaKeyName)
- return expect(pem).to.startsWith('-----BEGIN ENCRYPTED PRIVATE KEY-----')
- })
-
- 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')
- })
-
- it('cannot create the "self" key', async () => {
- const err = await ks.createKey('self', 'rsa', 2048).then(fail, err => err)
- expect(err).to.exist()
- expect(err).to.have.property('code', 'ERR_INVALID_KEY_NAME')
- })
-
- 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).to.have.property('code', 'ERR_INVALID_KEY_NAME')
- })
-
- 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).to.have.property('code', 'ERR_INVALID_KEY_TYPE')
- })
-
- 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).to.have.property('code', 'ERR_INVALID_KEY_SIZE')
- })
-
- describe('implements NIST SP 800-131A', () => {
- 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('code', 'ERR_INVALID_KEY_SIZE')
- })
- })
- })
-
- describe('query', () => {
- 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()
- })
-
- it('finds a key by name', async () => {
- const key = await ks.findKeyByName(rsaKeyName)
- expect(key).to.exist()
- expect(key).to.deep.equal(rsaKeyInfo)
- })
-
- it('finds a key by id', async () => {
- const key = await ks.findKeyById(rsaKeyInfo.id)
- expect(key).to.exist()
- expect(key).to.deep.equal(rsaKeyInfo)
- })
-
- 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')
- })
- })
- })
-
- describe('CMS protected data', () => {
- const plainData = Buffer.from('This is a message from Alice to Bob')
- let cms
-
- it('service is available', () => {
- expect(ks).to.have.property('cms')
- })
-
- it('requires a key', async () => {
- const err = await ks.cms.encrypt('no-key', plainData).then(fail, err => err)
- expect(err).to.exist()
- expect(err).to.have.property('code', 'ERR_KEY_NOT_FOUND')
- })
-
- 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()
- expect(err).to.have.property('code', 'ERR_INVALID_PARAMS')
- })
-
- 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', async () => {
- const err = await ks.cms.decrypt('not CMS').then(fail, err => err)
- expect(err).to.exist()
- expect(err).to.have.property('code', 'ERR_INVALID_PARAMS')
- })
-
- it('is a PKCS #7 binary message', async () => {
- const err = await ks.cms.decrypt(plainData).then(fail, err => err)
- expect(err).to.exist()
- expect(err).to.have.property('code', 'ERR_INVALID_CMS')
- })
-
- 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')
- })
-
- 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())
- })
- })
-
- describe('exported key', () => {
- let pemKey
-
- 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('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)
- })
-
- it('requires the pem', async () => {
- const err = await ks.importKey('imported-key', undefined, 'password').then(fail, err => err)
- expect(err).to.exist()
- expect(err).to.have.property('code', 'ERR_PEM_REQUIRED')
- })
-
- 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()
- 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')
- })
- })
-
- describe('peer id', () => {
- 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(async function () {
- const encoded = Buffer.from(alicePrivKey, 'base64')
- alice = await PeerId.createFromPrivKey(encoded)
- })
-
- 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())
- })
-
- 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())
- })
-
- 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())
- })
- })
-
- describe('rename', () => {
- 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_NOT_FOUND')
- })
-
- 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')
- })
-
- 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')
- })
-
- 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')
- })
-
- 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)
- // Try to find the changed key
- const err = await ks.findKeyByName(rsaKeyName).then(fail, err => err)
- expect(err).to.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)
- })
-
- 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)
- })
-
- 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', 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')
- })
-
- 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')
- })
-
- 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)
- })
- })
- })
-}
diff --git a/keychain/test/node.js b/keychain/test/node.js
deleted file mode 100644
index bbb25089..00000000
--- a/keychain/test/node.js
+++ /dev/null
@@ -1,31 +0,0 @@
-/* eslint-env mocha */
-'use strict'
-
-const os = require('os')
-const path = require('path')
-const promisify = require('promisify-es6')
-const rimraf = promisify(require('rimraf'))
-const FsStore = require('datastore-fs')
-
-describe('node', () => {
- const store1 = path.join(os.tmpdir(), 'test-keystore-1-' + Date.now())
- const store2 = path.join(os.tmpdir(), 'test-keystore-2-' + Date.now())
- const datastore1 = new FsStore(store1)
- const datastore2 = new FsStore(store2)
-
- before(async () => {
- await datastore1.open()
- await datastore2.open()
- })
-
- after(async () => {
- await datastore1.close()
- await datastore2.close()
- await rimraf(store1)
- await rimraf(store2)
- })
-
- require('./keychain.spec')(datastore1, datastore2)
- require('./cms-interop')(datastore2)
- require('./peerid')
-})
diff --git a/package.json b/package.json
index 0a1a2995..4b86ae3a 100644
--- a/package.json
+++ b/package.json
@@ -49,6 +49,7 @@
"debug": "^4.1.1",
"err-code": "^1.1.2",
"hashlru": "^2.3.0",
+ "interface-datastore": "^0.8.0",
"it-all": "^1.0.1",
"it-buffer": "^0.1.1",
"it-handshake": "^1.0.1",
@@ -64,6 +65,7 @@
"multiaddr": "^7.2.1",
"multistream-select": "^0.15.0",
"mutable-proxy": "^1.0.0",
+ "node-forge": "^0.9.1",
"p-any": "^2.1.0",
"p-fifo": "^1.0.0",
"p-settle": "^3.1.0",
@@ -71,6 +73,7 @@
"peer-info": "^0.17.0",
"protons": "^1.0.1",
"retimer": "^2.0.0",
+ "sanitize-filename": "^1.6.3",
"timeout-abort-controller": "^1.0.0",
"xsalsa20": "^1.0.2"
},
@@ -80,12 +83,17 @@
"aegir": "^20.5.1",
"chai": "^4.2.0",
"chai-as-promised": "^7.1.1",
+ "chai-string": "^1.5.0",
"cids": "^0.7.1",
+ "datastore-fs": "^0.9.1",
+ "datastore-level": "^0.14.0",
"delay": "^4.3.0",
"dirty-chai": "^2.0.1",
+ "is-browser": "^2.1.0",
"it-concat": "^1.0.0",
"it-pair": "^1.0.0",
"it-pushable": "^1.4.0",
+ "level": "^6.0.0",
"libp2p-bootstrap": "^0.10.3",
"libp2p-delegated-content-routing": "^0.4.1",
"libp2p-delegated-peer-routing": "^0.4.0",
@@ -98,10 +106,13 @@
"libp2p-tcp": "^0.14.1",
"libp2p-webrtc-star": "^0.17.0",
"libp2p-websockets": "^0.13.1",
+ "multihashes": "^0.4.15",
"nock": "^10.0.6",
"p-defer": "^3.0.0",
"p-times": "^2.1.0",
"p-wait-for": "^3.1.0",
+ "promisify-es6": "^1.0.3",
+ "rimraf": "^3.0.0",
"sinon": "^8.1.0",
"streaming-iterables": "^4.1.0",
"wrtc": "^0.4.1"
diff --git a/keychain/README.md b/src/keychain/README.md
similarity index 98%
rename from keychain/README.md
rename to src/keychain/README.md
index 37829b48..1d1e08a8 100644
--- a/keychain/README.md
+++ b/src/keychain/README.md
@@ -28,12 +28,6 @@
## Table of Contents
-## Install
-
-```sh
-npm install --save libp2p-keychain
-```
-
### Usage
```js
diff --git a/keychain/src/cms.js b/src/keychain/cms.js
similarity index 100%
rename from keychain/src/cms.js
rename to src/keychain/cms.js
diff --git a/keychain/doc/private-key.png b/src/keychain/doc/private-key.png
similarity index 100%
rename from keychain/doc/private-key.png
rename to src/keychain/doc/private-key.png
diff --git a/keychain/doc/private-key.xml b/src/keychain/doc/private-key.xml
similarity index 100%
rename from keychain/doc/private-key.xml
rename to src/keychain/doc/private-key.xml
diff --git a/keychain/src/index.js b/src/keychain/index.js
similarity index 100%
rename from keychain/src/index.js
rename to src/keychain/index.js
diff --git a/keychain/src/keychain.js b/src/keychain/keychain.js
similarity index 100%
rename from keychain/src/keychain.js
rename to src/keychain/keychain.js
diff --git a/keychain/src/util.js b/src/keychain/util.js
similarity index 100%
rename from keychain/src/util.js
rename to src/keychain/util.js
diff --git a/keychain/test/cms-interop.js b/test/keychain/cms-interop.spec.js
similarity index 57%
rename from keychain/test/cms-interop.js
rename to test/keychain/cms-interop.spec.js
index 06eb6312..3ffba04a 100644
--- a/keychain/test/cms-interop.js
+++ b/test/keychain/cms-interop.spec.js
@@ -7,24 +7,33 @@ const dirtyChai = require('dirty-chai')
const expect = chai.expect
chai.use(dirtyChai)
chai.use(require('chai-string'))
-const Keychain = require('..')
-module.exports = (datastore) => {
- describe('cms interop', () => {
- const passPhrase = 'this is not a secure phrase'
- const aliceKeyName = 'cms-interop-alice'
- let ks
+const os = require('os')
+const path = require('path')
+const isBrowser = require('is-browser')
+const FsStore = require('datastore-fs')
+const LevelStore = require('datastore-level')
- before(() => {
- ks = new Keychain(datastore, { passPhrase: passPhrase })
- })
+const Keychain = require('../../src/keychain')
- const plainData = Buffer.from('This is a message from Alice to Bob')
+describe('cms interop', () => {
+ const passPhrase = 'this is not a secure phrase'
+ const aliceKeyName = 'cms-interop-alice'
+ let ks
- it('imports openssl key', async function () {
- this.timeout(10 * 1000)
- const aliceKid = 'QmNzBqPwp42HZJccsLtc4ok6LjZAspckgs2du5tTmjPfFA'
- const alice = `-----BEGIN ENCRYPTED PRIVATE KEY-----
+ before(() => {
+ const datastore = isBrowser
+ ? new LevelStore('test-keystore-1', { db: require('level') })
+ : new FsStore(path.join(os.tmpdir(), 'test-keystore-1-' + Date.now()))
+ ks = new Keychain(datastore, { passPhrase: passPhrase })
+ })
+
+ const plainData = Buffer.from('This is a message from Alice to Bob')
+
+ it('imports openssl key', async function () {
+ this.timeout(10 * 1000)
+ const aliceKid = 'QmNzBqPwp42HZJccsLtc4ok6LjZAspckgs2du5tTmjPfFA'
+ const alice = `-----BEGIN ENCRYPTED PRIVATE KEY-----
MIICxjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIMhYqiVoLJMICAggA
MBQGCCqGSIb3DQMHBAhU7J9bcJPLDQSCAoDzi0dP6z97wJBs3jK2hDvZYdoScknG
QMPOnpG1LO3IZ7nFha1dta5liWX+xRFV04nmVYkkNTJAPS0xjJOG9B5Hm7wm8uTd
@@ -42,13 +51,13 @@ igg5jozKCW82JsuWSiW9tu0F/6DuvYiZwHS3OLiJP0CuLfbOaRw8Jia1RTvXEH7m
cn4oisOvxCprs4aM9UVjtZTCjfyNpX8UWwT1W3rySV+KQNhxuMy3RzmL
-----END ENCRYPTED PRIVATE KEY-----
`
- const key = await ks.importKey(aliceKeyName, alice, 'mypassword')
- expect(key.name).to.equal(aliceKeyName)
- expect(key.id).to.equal(aliceKid)
- })
+ const key = await ks.importKey(aliceKeyName, alice, 'mypassword')
+ expect(key.name).to.equal(aliceKeyName)
+ expect(key.id).to.equal(aliceKid)
+ })
- it('decrypts node-forge example', async () => {
- const example = `
+ it('decrypts node-forge example', async () => {
+ const example = `
MIIBcwYJKoZIhvcNAQcDoIIBZDCCAWACAQAxgfowgfcCAQAwYDBbMQ0wCwYDVQQK
EwRpcGZzMREwDwYDVQQLEwhrZXlzdG9yZTE3MDUGA1UEAxMuUW1OekJxUHdwNDJI
WkpjY3NMdGM0b2s2TGpaQXNwY2tnczJkdTV0VG1qUGZGQQIBATANBgkqhkiG9w0B
@@ -58,9 +67,8 @@ knU1yykWGkdlbclCuu0NaAfmb8o0OX50CbEKZB7xmsv8tnqn0H0jMF4GCSqGSIb3
DQEHATAdBglghkgBZQMEASoEEP/PW1JWehQx6/dsLkp/Mf+gMgQwFM9liLTqC56B
nHILFmhac/+a/StQOKuf9dx5qXeGvt9LnwKuGGSfNX4g+dTkoa6N
`
- const plain = await ks.cms.decrypt(Buffer.from(example, 'base64'))
- expect(plain).to.exist()
- expect(plain.toString()).to.equal(plainData.toString())
- })
+ const plain = await ks.cms.decrypt(Buffer.from(example, 'base64'))
+ expect(plain).to.exist()
+ expect(plain.toString()).to.equal(plainData.toString())
})
-}
+})
diff --git a/test/keychain/keychain.spec.js b/test/keychain/keychain.spec.js
new file mode 100644
index 00000000..c7f8d625
--- /dev/null
+++ b/test/keychain/keychain.spec.js
@@ -0,0 +1,394 @@
+/* eslint max-nested-callbacks: ["error", 8] */
+/* eslint-env mocha */
+'use strict'
+
+const chai = require('chai')
+const expect = chai.expect
+const fail = expect.fail
+chai.use(require('dirty-chai'))
+chai.use(require('chai-string'))
+
+const os = require('os')
+const path = require('path')
+const isBrowser = require('is-browser')
+const FsStore = require('datastore-fs')
+const LevelStore = require('datastore-level')
+const Keychain = require('../../src/keychain')
+const PeerId = require('peer-id')
+
+describe('keychain', () => {
+ const passPhrase = 'this is not a secure phrase'
+ const rsaKeyName = 'tajné jméno'
+ const renamedRsaKeyName = 'ชื่อลับ'
+ let rsaKeyInfo
+ let emptyKeystore
+ let ks
+ let datastore1, datastore2
+
+ before(() => {
+ datastore1 = isBrowser
+ ? new LevelStore('test-keystore-1', { db: require('level') })
+ : new FsStore(path.join(os.tmpdir(), 'test-keystore-1-' + Date.now()))
+ datastore2 = isBrowser
+ ? new LevelStore('test-keystore-2', { db: require('level') })
+ : new FsStore(path.join(os.tmpdir(), 'test-keystore-2-' + Date.now()))
+
+ ks = new Keychain(datastore2, { passPhrase: passPhrase })
+ emptyKeystore = new Keychain(datastore1, { passPhrase: passPhrase })
+ })
+
+ it('needs a pass phrase to encrypt a key', () => {
+ expect(() => new Keychain(datastore2)).to.throw()
+ })
+
+ it('needs a NIST SP 800-132 non-weak pass phrase', () => {
+ expect(() => new Keychain(datastore2, { passPhrase: '< 20 character' })).to.throw()
+ })
+
+ it('needs a store to persist a key', () => {
+ expect(() => new Keychain(null, { passPhrase: passPhrase })).to.throw()
+ })
+
+ it('has default options', () => {
+ expect(Keychain.options).to.exist()
+ })
+
+ it('needs a supported hashing alorithm', () => {
+ const ok = new Keychain(datastore2, { passPhrase: passPhrase, dek: { hash: 'sha2-256' } })
+ expect(ok).to.exist()
+ expect(() => new Keychain(datastore2, { passPhrase: passPhrase, dek: { hash: 'my-hash' } })).to.throw()
+ })
+
+ it('can generate options', () => {
+ const options = Keychain.generateOptions()
+ options.passPhrase = passPhrase
+ const chain = new Keychain(datastore2, options)
+ expect(chain).to.exist()
+ })
+
+ describe('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', 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', async () => {
+ const pem = await ks._getPrivateKey(rsaKeyName)
+ return expect(pem).to.startsWith('-----BEGIN ENCRYPTED PRIVATE KEY-----')
+ })
+
+ 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')
+ })
+
+ it('cannot create the "self" key', async () => {
+ const err = await ks.createKey('self', 'rsa', 2048).then(fail, err => err)
+ expect(err).to.exist()
+ expect(err).to.have.property('code', 'ERR_INVALID_KEY_NAME')
+ })
+
+ 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).to.have.property('code', 'ERR_INVALID_KEY_NAME')
+ })
+
+ 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).to.have.property('code', 'ERR_INVALID_KEY_TYPE')
+ })
+
+ 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).to.have.property('code', 'ERR_INVALID_KEY_SIZE')
+ })
+
+ describe('implements NIST SP 800-131A', () => {
+ 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('code', 'ERR_INVALID_KEY_SIZE')
+ })
+ })
+ })
+
+ describe('query', () => {
+ 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()
+ })
+
+ it('finds a key by name', async () => {
+ const key = await ks.findKeyByName(rsaKeyName)
+ expect(key).to.exist()
+ expect(key).to.deep.equal(rsaKeyInfo)
+ })
+
+ it('finds a key by id', async () => {
+ const key = await ks.findKeyById(rsaKeyInfo.id)
+ expect(key).to.exist()
+ expect(key).to.deep.equal(rsaKeyInfo)
+ })
+
+ 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')
+ })
+ })
+ })
+
+ describe('CMS protected data', () => {
+ const plainData = Buffer.from('This is a message from Alice to Bob')
+ let cms
+
+ it('service is available', () => {
+ expect(ks).to.have.property('cms')
+ })
+
+ it('requires a key', async () => {
+ const err = await ks.cms.encrypt('no-key', plainData).then(fail, err => err)
+ expect(err).to.exist()
+ expect(err).to.have.property('code', 'ERR_KEY_NOT_FOUND')
+ })
+
+ 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()
+ expect(err).to.have.property('code', 'ERR_INVALID_PARAMS')
+ })
+
+ 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', async () => {
+ const err = await ks.cms.decrypt('not CMS').then(fail, err => err)
+ expect(err).to.exist()
+ expect(err).to.have.property('code', 'ERR_INVALID_PARAMS')
+ })
+
+ it('is a PKCS #7 binary message', async () => {
+ const err = await ks.cms.decrypt(plainData).then(fail, err => err)
+ expect(err).to.exist()
+ expect(err).to.have.property('code', 'ERR_INVALID_CMS')
+ })
+
+ 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')
+ })
+
+ 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())
+ })
+ })
+
+ describe('exported key', () => {
+ let pemKey
+
+ 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('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)
+ })
+
+ it('requires the pem', async () => {
+ const err = await ks.importKey('imported-key', undefined, 'password').then(fail, err => err)
+ expect(err).to.exist()
+ expect(err).to.have.property('code', 'ERR_PEM_REQUIRED')
+ })
+
+ 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()
+ 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')
+ })
+ })
+
+ describe('peer id', () => {
+ 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(async function () {
+ const encoded = Buffer.from(alicePrivKey, 'base64')
+ alice = await PeerId.createFromPrivKey(encoded)
+ })
+
+ 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())
+ })
+
+ 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())
+ })
+
+ 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())
+ })
+ })
+
+ describe('rename', () => {
+ 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_NOT_FOUND')
+ })
+
+ 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')
+ })
+
+ 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')
+ })
+
+ 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')
+ })
+
+ 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)
+ // Try to find the changed key
+ const err = await ks.findKeyByName(rsaKeyName).then(fail, err => err)
+ expect(err).to.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)
+ })
+
+ 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)
+ })
+
+ 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', 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')
+ })
+
+ 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')
+ })
+
+ 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)
+ })
+ })
+})
diff --git a/keychain/test/peerid.js b/test/keychain/peerid.spec.js
similarity index 100%
rename from keychain/test/peerid.js
rename to test/keychain/peerid.spec.js