From 9ae1b758e99e3fc9067e26b4eae4c15ccb1ba303 Mon Sep 17 00:00:00 2001 From: Vasco Santos Date: Fri, 11 Dec 2020 17:53:37 +0100 Subject: [PATCH] fix: types from ipfs integration (#832) --- package.json | 2 +- src/keychain/cms.js | 19 ++++++++--- src/keychain/index.js | 78 +++++++++++++++++++++++++++---------------- src/peer-routing.js | 2 +- 4 files changed, 66 insertions(+), 35 deletions(-) diff --git a/package.json b/package.json index 6a3cba38..7c8da347 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "it-pipe": "^1.1.0", "it-protocol-buffers": "^0.2.0", "libp2p-crypto": "^0.18.0", - "libp2p-interfaces": "^0.8.0", + "libp2p-interfaces": "^0.8.1", "libp2p-utils": "^0.2.2", "mafmt": "^8.0.0", "merge-options": "^2.0.0", diff --git a/src/keychain/cms.js b/src/keychain/cms.js index 3ba99cfb..bcd5c365 100644 --- a/src/keychain/cms.js +++ b/src/keychain/cms.js @@ -1,4 +1,3 @@ -// @ts-nocheck 'use strict' require('node-forge/lib/pkcs7') @@ -9,6 +8,8 @@ const errcode = require('err-code') const uint8ArrayFromString = require('uint8arrays/from-string') const uint8ArrayToString = require('uint8arrays/to-string') +const privates = new WeakMap() + /** * Cryptographic Message Syntax (aka PKCS #7) * @@ -23,13 +24,15 @@ class CMS { * Creates a new instance with a keychain * * @param {import('./index')} keychain - the available keys + * @param {string} dek */ - constructor (keychain) { + constructor (keychain, dek) { if (!keychain) { throw errcode(new Error('keychain is required'), 'ERR_KEYCHAIN_REQUIRED') } this.keychain = keychain + privates.set(this, { dek }) } /** @@ -48,7 +51,9 @@ class CMS { const key = await this.keychain.findKeyByName(name) const pem = await this.keychain._getPrivateKey(name) - const privateKey = forge.pki.decryptRsaPrivateKey(pem, this.keychain._()) + /** @type {string} */ + const dek = privates.get(this).dek + const privateKey = forge.pki.decryptRsaPrivateKey(pem, dek) const certificate = await certificateForKey(key, privateKey) // create a p7 enveloped message @@ -115,8 +120,14 @@ class CMS { } const key = await this.keychain.findKeyById(r.keyId) + + if (!key) { + throw errcode(new Error('No key available to decrypto'), 'ERR_NO_KEY') + } + const pem = await this.keychain._getPrivateKey(key.name) - const privateKey = forge.pki.decryptRsaPrivateKey(pem, this.keychain._()) + const dek = privates.get(this).dek + const privateKey = forge.pki.decryptRsaPrivateKey(pem, dek) cms.decrypt(r.recipient, privateKey) return uint8ArrayFromString(cms.content.getBytes(), 'ascii') } diff --git a/src/keychain/index.js b/src/keychain/index.js index 440a2913..5dc694fd 100644 --- a/src/keychain/index.js +++ b/src/keychain/index.js @@ -1,11 +1,10 @@ -// @ts-nocheck /* eslint max-nested-callbacks: ["error", 5] */ 'use strict' const sanitize = require('sanitize-filename') const mergeOptions = require('merge-options') const crypto = require('libp2p-crypto') -const DS = require('interface-datastore') +const Datastore = require('interface-datastore') const CMS = require('./cms') const errcode = require('err-code') const { Number } = require('ipfs-utils/src/globalthis') @@ -14,8 +13,14 @@ const uint8ArrayFromString = require('uint8arrays/from-string') require('node-forge/lib/sha512') +/** + * @typedef {import('peer-id')} PeerId + * @typedef {import('interface-datastore/src/key')} Key + */ + const keyPrefix = '/pkcs8/' const infoPrefix = '/info/' +const privates = new WeakMap() // NIST SP 800-132 const NIST = { @@ -46,7 +51,8 @@ function validateKeyName (name) { * This assumes than an error indicates that the keychain is under attack. Delay returning an * error to make brute force attacks harder. * - * @param {string | Error} err - The error + * @param {string|Error} err - The error + * @returns {Promise} * @private */ async function throwDelayed (err) { @@ -62,29 +68,28 @@ async function throwDelayed (err) { * Converts a key name into a datastore name. * * @param {string} name - * @returns {DS.Key} + * @returns {Key} * @private */ function DsName (name) { - return new DS.Key(keyPrefix + name) + return new Datastore.Key(keyPrefix + name) } /** * Converts a key name into a datastore info name. * * @param {string} name - * @returns {DS.Key} + * @returns {Key} * @private */ function DsInfoName (name) { - return new DS.Key(infoPrefix + name) + return new Datastore.Key(infoPrefix + name) } /** * Information about a key. * * @typedef {Object} KeyInfo - * * @property {string} id - The universally unique key id. * @property {string} name - The local key name. */ @@ -101,7 +106,7 @@ class Keychain { /** * Creates a new instance of a key chain. * - * @param {DS} store - where the key are. + * @param {Datastore} store - where the key are. * @param {object} options * @class */ @@ -134,7 +139,7 @@ class Keychain { this.opts.dek.keyLength, this.opts.dek.hash) : '' - Object.defineProperty(this, '_', { value: () => dek }) + privates.set(this, { dek }) } /** @@ -148,13 +153,13 @@ class Keychain { * @returns {CMS} */ get cms () { - return new CMS(this) + return new CMS(this, privates.get(this).dek) } /** * Generates the options for a keychain. A random salt is produced. * - * @returns {object} + * @returns {Object} */ static generateOptions () { const options = Object.assign({}, defaultOptions) @@ -167,7 +172,7 @@ class Keychain { * Gets an object that can encrypt/decrypt protected data. * The default options for a keychain. * - * @returns {object} + * @returns {Object} */ static get options () { return defaultOptions @@ -178,10 +183,10 @@ 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. Used for rsa keys only. - * @returns {KeyInfo} + * @param {number} [size = 2048] - The key size in bits. Used for rsa keys only. + * @returns {Promise} */ - async createKey (name, type, size) { + async createKey (name, type, size = 2048) { const self = this if (!validateKeyName(name) || name === 'self') { @@ -208,9 +213,12 @@ class Keychain { let keyInfo try { + // @ts-ignore Differences between several crypto return types need to be fixed in libp2p-crypto const keypair = await crypto.keys.generateKeyPair(type, size) const kid = await keypair.id() - const pem = await keypair.export(this._()) + /** @type {string} */ + const dek = privates.get(this).dek + const pem = await keypair.export(dek) keyInfo = { name: name, id: kid @@ -230,7 +238,7 @@ class Keychain { /** * List all the keys. * - * @returns {KeyInfo[]} + * @returns {Promise} */ async listKeys () { const self = this @@ -250,7 +258,7 @@ class Keychain { * Find a key by it's id. * * @param {string} id - The universally unique key identifier. - * @returns {KeyInfo} + * @returns {Promise} */ async findKeyById (id) { try { @@ -265,7 +273,7 @@ class Keychain { * Find a key by it's name. * * @param {string} name - The local key name. - * @returns {KeyInfo} + * @returns {Promise} */ async findKeyByName (name) { if (!validateKeyName(name)) { @@ -285,7 +293,7 @@ class Keychain { * Remove an existing key. * * @param {string} name - The local key name; must already exist. - * @returns {KeyInfo} + * @returns {Promise} */ async removeKey (name) { const self = this @@ -306,7 +314,7 @@ 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. - * @returns {KeyInfo} + * @returns {Promise} */ async renameKey (oldName, newName) { const self = this @@ -347,7 +355,7 @@ class Keychain { * * @param {string} name - The local key name; must already exist. * @param {string} password - The password - * @returns {string} + * @returns {Promise} */ async exportKey (name, password) { if (!validateKeyName(name)) { @@ -361,7 +369,9 @@ class Keychain { try { const res = await this.store.get(dsname) const pem = uint8ArrayToString(res) - const privateKey = await crypto.keys.import(pem, this._()) + /** @type {string} */ + const dek = privates.get(this).dek + const privateKey = await crypto.keys.import(pem, dek) return privateKey.export(password) } catch (err) { return throwDelayed(err) @@ -374,7 +384,7 @@ 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. - * @returns {KeyInfo} + * @returns {Promise} */ async importKey (name, pem, password) { const self = this @@ -398,7 +408,9 @@ class Keychain { let kid try { kid = await privateKey.id() - pem = await privateKey.export(this._()) + /** @type {string} */ + const dek = privates.get(this).dek + pem = await privateKey.export(dek) } catch (err) { return throwDelayed(err) } @@ -415,6 +427,13 @@ class Keychain { return keyInfo } + /** + * Import a peer key + * + * @param {string} name - The local key name; must not already exist. + * @param {PeerId} peer - The PEM encoded PKCS #8 string + * @returns {Promise} + */ async importPeer (name, peer) { const self = this if (!validateKeyName(name)) { @@ -431,7 +450,9 @@ class Keychain { try { const kid = await privateKey.id() - const pem = await privateKey.export(this._()) + /** @type {string} */ + const dek = privates.get(this).dek + const pem = await privateKey.export(dek) const keyInfo = { name: name, id: kid @@ -450,8 +471,7 @@ class Keychain { * Gets the private key as PEM encoded PKCS #8 string. * * @param {string} name - * @returns {string} - * @private + * @returns {Promise} */ async _getPrivateKey (name) { if (!validateKeyName(name)) { diff --git a/src/peer-routing.js b/src/peer-routing.js index 26fe9625..9a4507ae 100644 --- a/src/peer-routing.js +++ b/src/peer-routing.js @@ -73,7 +73,7 @@ class PeerRouting { /** * Iterates over all peer routers in series to find the given peer. * - * @param {string} id - The id of the peer to find + * @param {PeerId} id - The id of the peer to find * @param {object} [options] * @param {number} [options.timeout] - How long the query should run * @returns {Promise<{ id: PeerId, multiaddrs: Multiaddr[] }>}