Revert "feat: adds support for ed25199 and secp256k1 (#31)"

This reverts commit 9eb11f42452b4266db0c19de3310e809a0087669.
This commit is contained in:
Vasco Santos 2019-03-14 22:26:07 +00:00
parent e30330e1a0
commit f71a6bbb0a
3 changed files with 98 additions and 168 deletions

View File

@ -57,8 +57,8 @@ Managing a key
- `createKey (name, type, size, callback)` - `createKey (name, type, size, callback)`
- `renameKey (oldName, newName, callback)` - `renameKey (oldName, newName, callback)`
- `removeKey (name, callback)` - `removeKey (name, callback)`
- `exportKey (name, password, callback)` // Omit _password_ for `ed25199` or `secp256k1` keys - `exportKey (name, password, callback)`
- `importKey (name, encKey, password, callback)` // Omit _password_ for `ed25199` or `secp256k1` keys - `importKey (name, pem, password, callback)`
- `importPeer (name, peer, callback)` - `importPeer (name, peer, callback)`
A naming service for a key A naming service for a key
@ -67,7 +67,7 @@ A naming service for a key
- `findKeyById (id, callback)` - `findKeyById (id, callback)`
- `findKeyByName (name, callback)` - `findKeyByName (name, callback)`
Cryptographically protected messages (Only supported with RSA keys) Cryptographically protected messages
- `cms.encrypt (name, plain, callback)` - `cms.encrypt (name, plain, callback)`
- `cms.decrypt (cmsData, callback)` - `cms.decrypt (cmsData, callback)`

View File

@ -20,7 +20,7 @@ const NIST = {
} }
const defaultOptions = { const defaultOptions = {
// See https://cryptosense.com/blog/parameter-choice-for-pbkdf2/ // See https://cryptosense.com/parametesr-choice-for-pbkdf2/
dek: { dek: {
keyLength: 512 / 8, keyLength: 512 / 8,
iterationCount: 10000, iterationCount: 10000,
@ -197,8 +197,7 @@ class Keychain {
if (err) return _error(callback, err) if (err) return _error(callback, err)
if (exists) return _error(callback, `Key '${name}' already exists`) if (exists) return _error(callback, `Key '${name}' already exists`)
type = type.toLowerCase() switch (type.toLowerCase()) {
switch (type) {
case 'rsa': case 'rsa':
if (size < 2048) { if (size < 2048) {
return _error(callback, `Invalid RSA key size ${size}`) return _error(callback, `Invalid RSA key size ${size}`)
@ -212,16 +211,21 @@ class Keychain {
if (err) return _error(callback, err) if (err) return _error(callback, err)
keypair.id((err, kid) => { keypair.id((err, kid) => {
if (err) return _error(callback, err) if (err) return _error(callback, err)
keypair.export(this._(), (err, pem) => {
if (type === 'ed25519' || type === 'secp256k1') { if (err) return _error(callback, err)
const keypairMarshal = keypair.bytes const keyInfo = {
self._storeKey(name, kid, keypairMarshal, dsname, callback) name: name,
} else { id: kid
keypair.export(this._(), (err, pem) => { }
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) if (err) return _error(callback, err)
self._storeKey(name, kid, pem, dsname, callback)
callback(null, keyInfo)
}) })
} })
}) })
}) })
}) })
@ -361,85 +365,76 @@ class Keychain {
} }
/** /**
* Export an existing key. * Export an existing key as a PEM encrypted PKCS #8 string
* If it's as an RSA key, include a password to export as a PEM encrypted PKCS #8 string
* *
* @param {string} name - The local key name; must already exist. * @param {string} name - The local key name; must already exist.
* @param {string} password - The password, for RSA keys (optional) * @param {string} password - The password
* @param {function(Error, string)} callback * @param {function(Error, string)} callback
* @returns {undefined} * @returns {undefined}
*/ */
exportKey (name, password, callback) { exportKey (name, password, callback) {
if (typeof password === 'function' && typeof callback === 'undefined') {
callback = password
password = undefined
}
if (!validateKeyName(name)) { if (!validateKeyName(name)) {
return _error(callback, `Invalid key name '${name}'`) return _error(callback, `Invalid key name '${name}'`)
} }
if (!password) {
return _error(callback, 'Password is required')
}
const dsname = DsName(name) const dsname = DsName(name)
this.store.get(dsname, (err, res) => { this.store.get(dsname, (err, res) => {
if (err) { if (err) {
return _error(callback, `Key '${name}' does not exist. ${err.message}`) return _error(callback, `Key '${name}' does not exist. ${err.message}`)
} }
if (password) { const pem = res.toString()
const encKey = res.toString() crypto.keys.import(pem, this._(), (err, privateKey) => {
crypto.keys.import(encKey, this._(), (err, privateKey) => { if (err) return _error(callback, err)
if (err) return _error(callback, err) privateKey.export(password, callback)
privateKey.export(password, callback) })
})
} else {
crypto.keys.unmarshalPrivateKey(res, callback)
}
}) })
} }
/** /**
* Import a new key * Import a new key from a PEM encoded PKCS #8 string
* If it's as an RSA key, include a password to import from a PEM encrypted PKCS #8 string
* *
* @param {string} name - The local key name; must not already exist. * @param {string} name - The local key name; must not already exist.
* @param {string} encKey - The encoded key. If it's an RSA key, it needs to be a PEM encoded PKCS #8 string * @param {string} pem - The PEM encoded PKCS #8 string
* @param {string} password - The password for RSA keys. (optional) * @param {string} password - The password.
* @param {function(Error, KeyInfo)} callback * @param {function(Error, KeyInfo)} callback
* @returns {undefined} * @returns {undefined}
*/ */
importKey (name, encKey, password, callback) { importKey (name, pem, password, callback) {
const self = this const self = this
if (typeof password === 'function' && typeof callback === 'undefined') {
callback = password
password = undefined
}
if (!validateKeyName(name) || name === 'self') { if (!validateKeyName(name) || name === 'self') {
return _error(callback, `Invalid key name '${name}'`) return _error(callback, `Invalid key name '${name}'`)
} }
if (!encKey) { if (!pem) {
return _error(callback, 'The encoded key is required') return _error(callback, 'PEM encoded key is required')
} }
const dsname = DsName(name) const dsname = DsName(name)
self.store.has(dsname, (err, exists) => { self.store.has(dsname, (err, exists) => {
if (err) return _error(callback, err) if (err) return _error(callback, err)
if (exists) return _error(callback, `Key '${name}' already exists`) if (exists) return _error(callback, `Key '${name}' already exists`)
crypto.keys.import(pem, password, (err, privateKey) => {
if (password) { if (err) return _error(callback, 'Cannot read the key, most likely the password is wrong')
crypto.keys.import(encKey, password, (err, privateKey) => { privateKey.id((err, kid) => {
if (err) return _error(callback, 'Cannot read the key, most likely the password is wrong') if (err) return _error(callback, err)
privateKey.id((err, kid) => { privateKey.export(this._(), (err, pem) => {
if (err) return _error(callback, err) if (err) return _error(callback, err)
privateKey.export(this._(), (err, pem) => { const 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) if (err) return _error(callback, err)
self._storeKey(name, kid, pem, dsname, callback)
callback(null, keyInfo)
}) })
}) })
}) })
} else { })
encKey.id((err, kid) => {
if (err) return _error(callback, err)
self._storeKey(name, kid, encKey.bytes, dsname, callback)
})
}
}) })
} }
@ -462,28 +457,23 @@ class Keychain {
if (err) return _error(callback, err) if (err) return _error(callback, err)
privateKey.export(this._(), (err, pem) => { privateKey.export(this._(), (err, pem) => {
if (err) return _error(callback, err) if (err) return _error(callback, err)
self._storeKey(name, kid, pem, dsname, callback) const 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)
})
}) })
}) })
}) })
} }
_storeKey (name, kid, encKey, dsname, callback) {
const self = this
const keyInfo = {
name: name,
id: kid
}
const batch = self.store.batch()
batch.put(dsname, encKey)
batch.put(DsInfoName(name), JSON.stringify(keyInfo))
batch.commit((err) => {
if (err) return _error(callback, err)
callback(null, keyInfo)
})
}
/** /**
* Gets the private key as PEM encoded PKCS #8 string. * Gets the private key as PEM encoded PKCS #8 string.
* *

View File

@ -13,11 +13,9 @@ const PeerId = require('peer-id')
module.exports = (datastore1, datastore2) => { module.exports = (datastore1, datastore2) => {
describe('keychain', () => { describe('keychain', () => {
const passPhrase = 'this is not a secure phrase' const passPhrase = 'this is not a secure phrase'
const keyName = 'tajné jméno' const rsaKeyName = 'tajné jméno'
const renamedKeyName = 'ชื่อลับ' const renamedRsaKeyName = 'ชื่อลับ'
let keyInfo let rsaKeyInfo
let ecKeyInfo
let secpKeyInfo
let emptyKeystore let emptyKeystore
let ks let ks
@ -82,43 +80,23 @@ module.exports = (datastore1, datastore2) => {
}) })
describe('key', () => { describe('key', () => {
it('can be an ed25519 key', function (done) {
this.timeout(50 * 1000)
ks.createKey(keyName + 'ed25519', 'ed25519', 2048, (err, info) => {
expect(err).to.not.exist()
expect(info).exist()
ecKeyInfo = info
done()
})
})
it('can be an secp256k1 key', function (done) {
this.timeout(50 * 1000)
ks.createKey(keyName + 'secp256k1', 'secp256k1', 2048, (err, info) => {
expect(err).to.not.exist()
expect(info).exist()
secpKeyInfo = info
done()
})
})
it('can be an RSA key', function (done) { it('can be an RSA key', function (done) {
this.timeout(50 * 1000) this.timeout(50 * 1000)
ks.createKey(keyName, 'rsa', 2048, (err, info) => { ks.createKey(rsaKeyName, 'rsa', 2048, (err, info) => {
expect(err).to.not.exist() expect(err).to.not.exist()
expect(info).exist() expect(info).exist()
keyInfo = info rsaKeyInfo = info
done() done()
}) })
}) })
it('has a name and id', () => { it('has a name and id', () => {
expect(keyInfo).to.have.property('name', keyName) expect(rsaKeyInfo).to.have.property('name', rsaKeyName)
expect(keyInfo).to.have.property('id') expect(rsaKeyInfo).to.have.property('id')
}) })
it('is encrypted PEM encoded PKCS #8', (done) => { it('is encrypted PEM encoded PKCS #8', (done) => {
ks._getPrivateKey(keyName, (err, pem) => { ks._getPrivateKey(rsaKeyName, (err, pem) => {
expect(err).to.not.exist() expect(err).to.not.exist()
expect(pem).to.startsWith('-----BEGIN ENCRYPTED PRIVATE KEY-----') expect(pem).to.startsWith('-----BEGIN ENCRYPTED PRIVATE KEY-----')
done() done()
@ -126,7 +104,7 @@ module.exports = (datastore1, datastore2) => {
}) })
it('does not overwrite existing key', (done) => { it('does not overwrite existing key', (done) => {
ks.createKey(keyName, 'rsa', 2048, (err) => { ks.createKey(rsaKeyName, 'rsa', 2048, (err) => {
expect(err).to.exist() expect(err).to.exist()
done() done()
}) })
@ -179,26 +157,26 @@ module.exports = (datastore1, datastore2) => {
ks.listKeys((err, keys) => { ks.listKeys((err, keys) => {
expect(err).to.not.exist() expect(err).to.not.exist()
expect(keys).to.exist() expect(keys).to.exist()
const mykey = keys.find((k) => k.name.normalize() === keyName.normalize()) const mykey = keys.find((k) => k.name.normalize() === rsaKeyName.normalize())
expect(mykey).to.exist() expect(mykey).to.exist()
done() done()
}) })
}) })
it('finds a key by name', (done) => { it('finds a key by name', (done) => {
ks.findKeyByName(keyName, (err, key) => { ks.findKeyByName(rsaKeyName, (err, key) => {
expect(err).to.not.exist() expect(err).to.not.exist()
expect(key).to.exist() expect(key).to.exist()
expect(key).to.deep.equal(keyInfo) expect(key).to.deep.equal(rsaKeyInfo)
done() done()
}) })
}) })
it('finds a key by id', (done) => { it('finds a key by id', (done) => {
ks.findKeyById(keyInfo.id, (err, key) => { ks.findKeyById(rsaKeyInfo.id, (err, key) => {
expect(err).to.not.exist() expect(err).to.not.exist()
expect(key).to.exist() expect(key).to.exist()
expect(key).to.deep.equal(keyInfo) expect(key).to.deep.equal(rsaKeyInfo)
done() done()
}) })
}) })
@ -233,14 +211,14 @@ module.exports = (datastore1, datastore2) => {
}) })
it('requires plain data as a Buffer', (done) => { it('requires plain data as a Buffer', (done) => {
ks.cms.encrypt(keyName, 'plain data', (err, msg) => { ks.cms.encrypt(rsaKeyName, 'plain data', (err, msg) => {
expect(err).to.exist() expect(err).to.exist()
done() done()
}) })
}) })
it('encrypts', (done) => { it('encrypts', (done) => {
ks.cms.encrypt(keyName, plainData, (err, msg) => { ks.cms.encrypt(rsaKeyName, plainData, (err, msg) => {
expect(err).to.not.exist() expect(err).to.not.exist()
expect(msg).to.exist() expect(msg).to.exist()
expect(msg).to.be.instanceOf(Buffer) expect(msg).to.be.instanceOf(Buffer)
@ -267,7 +245,7 @@ module.exports = (datastore1, datastore2) => {
emptyKeystore.cms.decrypt(cms, (err, plain) => { emptyKeystore.cms.decrypt(cms, (err, plain) => {
expect(err).to.exist() expect(err).to.exist()
expect(err).to.have.property('missingKeys') expect(err).to.have.property('missingKeys')
expect(err.missingKeys).to.eql([keyInfo.id]) expect(err.missingKeys).to.eql([rsaKeyInfo.id])
done() done()
}) })
}) })
@ -284,11 +262,9 @@ module.exports = (datastore1, datastore2) => {
describe('exported key', () => { describe('exported key', () => {
let pemKey let pemKey
let ed25519Key
let secp256k1Key
it('is a PKCS #8 encrypted pem', (done) => { it('is a PKCS #8 encrypted pem', (done) => {
ks.exportKey(keyName, 'password', (err, pem) => { ks.exportKey(rsaKeyName, 'password', (err, pem) => {
expect(err).to.not.exist() expect(err).to.not.exist()
expect(pem).to.startsWith('-----BEGIN ENCRYPTED PRIVATE KEY-----') expect(pem).to.startsWith('-----BEGIN ENCRYPTED PRIVATE KEY-----')
pemKey = pem pemKey = pem
@ -300,49 +276,13 @@ module.exports = (datastore1, datastore2) => {
ks.importKey('imported-key', pemKey, 'password', (err, key) => { ks.importKey('imported-key', pemKey, 'password', (err, key) => {
expect(err).to.not.exist() expect(err).to.not.exist()
expect(key.name).to.equal('imported-key') expect(key.name).to.equal('imported-key')
expect(key.id).to.equal(keyInfo.id) expect(key.id).to.equal(rsaKeyInfo.id)
done()
})
})
it('can export ed25519 key', (done) => {
ks.exportKey(keyName + 'ed25519', (err, key) => {
expect(err).to.not.exist()
ed25519Key = key
expect(key).to.exist()
done()
})
})
it('ed25519 key can be imported', (done) => {
ks.importKey('imported-key-ed25199', ed25519Key, (err, key) => {
expect(err).to.not.exist()
expect(key.name).to.equal('imported-key-ed25199')
expect(key.id).to.equal(ecKeyInfo.id)
done()
})
})
it('can export secp256k1 key', (done) => {
ks.exportKey(keyName + 'secp256k1', (err, key) => {
expect(err).to.not.exist()
secp256k1Key = key
expect(key).to.exist()
done()
})
})
it('secp256k1 key can be imported', (done) => {
ks.importKey('imported-key-secp256k1', secp256k1Key, (err, key) => {
expect(err).to.not.exist()
expect(key.name).to.equal('imported-key-secp256k1')
expect(key.id).to.equal(secpKeyInfo.id)
done() done()
}) })
}) })
it('cannot be imported as an existing key name', (done) => { it('cannot be imported as an existing key name', (done) => {
ks.importKey(keyName, pemKey, 'password', (err, key) => { ks.importKey(rsaKeyName, pemKey, 'password', (err, key) => {
expect(err).to.exist() expect(err).to.exist()
done() done()
}) })
@ -402,40 +342,40 @@ module.exports = (datastore1, datastore2) => {
describe('rename', () => { describe('rename', () => {
it('requires an existing key name', (done) => { it('requires an existing key name', (done) => {
ks.renameKey('not-there', renamedKeyName, (err) => { ks.renameKey('not-there', renamedRsaKeyName, (err) => {
expect(err).to.exist() expect(err).to.exist()
done() done()
}) })
}) })
it('requires a valid new key name', (done) => { it('requires a valid new key name', (done) => {
ks.renameKey(keyName, '..\not-valid', (err) => { ks.renameKey(rsaKeyName, '..\not-valid', (err) => {
expect(err).to.exist() expect(err).to.exist()
done() done()
}) })
}) })
it('does not overwrite existing key', (done) => { it('does not overwrite existing key', (done) => {
ks.renameKey(keyName, keyName, (err) => { ks.renameKey(rsaKeyName, rsaKeyName, (err) => {
expect(err).to.exist() expect(err).to.exist()
done() done()
}) })
}) })
it('cannot create the "self" key', (done) => { it('cannot create the "self" key', (done) => {
ks.renameKey(keyName, 'self', (err) => { ks.renameKey(rsaKeyName, 'self', (err) => {
expect(err).to.exist() expect(err).to.exist()
done() done()
}) })
}) })
it('removes the existing key name', (done) => { it('removes the existing key name', (done) => {
ks.renameKey(keyName, renamedKeyName, (err, key) => { ks.renameKey(rsaKeyName, renamedRsaKeyName, (err, key) => {
expect(err).to.not.exist() expect(err).to.not.exist()
expect(key).to.exist() expect(key).to.exist()
expect(key).to.have.property('name', renamedKeyName) expect(key).to.have.property('name', renamedRsaKeyName)
expect(key).to.have.property('id', keyInfo.id) expect(key).to.have.property('id', rsaKeyInfo.id)
ks.findKeyByName(keyName, (err, key) => { ks.findKeyByName(rsaKeyName, (err, key) => {
expect(err).to.exist() expect(err).to.exist()
done() done()
}) })
@ -443,20 +383,20 @@ module.exports = (datastore1, datastore2) => {
}) })
it('creates the new key name', (done) => { it('creates the new key name', (done) => {
ks.findKeyByName(renamedKeyName, (err, key) => { ks.findKeyByName(renamedRsaKeyName, (err, key) => {
expect(err).to.not.exist() expect(err).to.not.exist()
expect(key).to.exist() expect(key).to.exist()
expect(key).to.have.property('name', renamedKeyName) expect(key).to.have.property('name', renamedRsaKeyName)
done() done()
}) })
}) })
it('does not change the key ID', (done) => { it('does not change the key ID', (done) => {
ks.findKeyByName(renamedKeyName, (err, key) => { ks.findKeyByName(renamedRsaKeyName, (err, key) => {
expect(err).to.not.exist() expect(err).to.not.exist()
expect(key).to.exist() expect(key).to.exist()
expect(key).to.have.property('name', renamedKeyName) expect(key).to.have.property('name', renamedRsaKeyName)
expect(key).to.have.property('id', keyInfo.id) expect(key).to.have.property('id', rsaKeyInfo.id)
done() done()
}) })
}) })
@ -478,11 +418,11 @@ module.exports = (datastore1, datastore2) => {
}) })
it('can remove a known key', (done) => { it('can remove a known key', (done) => {
ks.removeKey(renamedKeyName, (err, key) => { ks.removeKey(renamedRsaKeyName, (err, key) => {
expect(err).to.not.exist() expect(err).to.not.exist()
expect(key).to.exist() expect(key).to.exist()
expect(key).to.have.property('name', renamedKeyName) expect(key).to.have.property('name', renamedRsaKeyName)
expect(key).to.have.property('id', keyInfo.id) expect(key).to.have.property('id', rsaKeyInfo.id)
done() done()
}) })
}) })