Skip to content

Commit

Permalink
feat: adds support for ed25199 and secp256k1 (#31)
Browse files Browse the repository at this point in the history
  • Loading branch information
AlbertoElias authored and vasco-santos committed Feb 25, 2019
1 parent 3779bd0 commit 9eb11f4
Show file tree
Hide file tree
Showing 3 changed files with 168 additions and 98 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ Managing a key
- `createKey (name, type, size, callback)`
- `renameKey (oldName, newName, callback)`
- `removeKey (name, callback)`
- `exportKey (name, password, callback)`
- `importKey (name, pem, password, callback)`
- `exportKey (name, password, callback)` // Omit _password_ for `ed25199` or `secp256k1` keys
- `importKey (name, encKey, password, callback)` // Omit _password_ for `ed25199` or `secp256k1` keys
- `importPeer (name, peer, callback)`

A naming service for a key
Expand All @@ -67,7 +67,7 @@ A naming service for a key
- `findKeyById (id, callback)`
- `findKeyByName (name, callback)`

Cryptographically protected messages
Cryptographically protected messages (Only supported with RSA keys)

- `cms.encrypt (name, plain, callback)`
- `cms.decrypt (cmsData, callback)`
Expand Down
128 changes: 69 additions & 59 deletions src/keychain.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const NIST = {
}

const defaultOptions = {
// See https://cryptosense.com/parametesr-choice-for-pbkdf2/
// See https://cryptosense.com/blog/parameter-choice-for-pbkdf2/
dek: {
keyLength: 512 / 8,
iterationCount: 10000,
Expand Down Expand Up @@ -197,7 +197,8 @@ class Keychain {
if (err) return _error(callback, err)
if (exists) return _error(callback, `Key '${name}' already exists`)

switch (type.toLowerCase()) {
type = type.toLowerCase()
switch (type) {
case 'rsa':
if (size < 2048) {
return _error(callback, `Invalid RSA key size ${size}`)
Expand All @@ -211,21 +212,16 @@ class Keychain {
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 = {
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)
if (type === 'ed25519' || type === 'secp256k1') {
const keypairMarshal = keypair.bytes
self._storeKey(name, kid, keypairMarshal, dsname, callback)
} else {
keypair.export(this._(), (err, pem) => {
if (err) return _error(callback, err)
self._storeKey(name, kid, pem, dsname, callback)
})
})
}
})
})
})
Expand Down Expand Up @@ -365,76 +361,85 @@ class Keychain {
}

/**
* Export an existing key as a PEM encrypted PKCS #8 string
* Export an existing key.
* 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} password - The password
* @param {string} password - The password, for RSA keys (optional)
* @param {function(Error, string)} callback
* @returns {undefined}
*/
exportKey (name, password, callback) {
if (typeof password === 'function' && typeof callback === 'undefined') {
callback = password
password = undefined
}
if (!validateKeyName(name)) {
return _error(callback, `Invalid key name '${name}'`)
}
if (!password) {
return _error(callback, 'Password is required')
}

const dsname = DsName(name)
this.store.get(dsname, (err, res) => {
if (err) {
return _error(callback, `Key '${name}' does not exist. ${err.message}`)
}
const pem = res.toString()
crypto.keys.import(pem, this._(), (err, privateKey) => {
if (err) return _error(callback, err)
privateKey.export(password, callback)
})
if (password) {
const encKey = res.toString()
crypto.keys.import(encKey, this._(), (err, privateKey) => {
if (err) return _error(callback, err)
privateKey.export(password, callback)
})
} else {
crypto.keys.unmarshalPrivateKey(res, callback)
}
})
}

/**
* Import a new key from a PEM encoded PKCS #8 string
* Import a new key
* 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} pem - The PEM encoded PKCS #8 string
* @param {string} password - The password.
* @param {string} encKey - The encoded key. If it's an RSA key, it needs to be a PEM encoded PKCS #8 string
* @param {string} password - The password for RSA keys. (optional)
* @param {function(Error, KeyInfo)} callback
* @returns {undefined}
*/
importKey (name, pem, password, callback) {
importKey (name, encKey, password, callback) {
const self = this
if (typeof password === 'function' && typeof callback === 'undefined') {
callback = password
password = undefined
}
if (!validateKeyName(name) || name === 'self') {
return _error(callback, `Invalid key name '${name}'`)
}
if (!pem) {
return _error(callback, 'PEM encoded key is required')
if (!encKey) {
return _error(callback, 'The encoded key is required')
}

const dsname = DsName(name)
self.store.has(dsname, (err, exists) => {
if (err) return _error(callback, err)
if (exists) return _error(callback, `Key '${name}' already exists`)
crypto.keys.import(pem, password, (err, privateKey) => {
if (err) return _error(callback, 'Cannot read the key, most likely the password is wrong')
privateKey.id((err, kid) => {
if (err) return _error(callback, err)
privateKey.export(this._(), (err, pem) => {

if (password) {
crypto.keys.import(encKey, password, (err, privateKey) => {
if (err) return _error(callback, 'Cannot read the key, most likely the password is wrong')
privateKey.id((err, kid) => {
if (err) return _error(callback, err)
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) => {
privateKey.export(this._(), (err, pem) => {
if (err) return _error(callback, err)

callback(null, keyInfo)
self._storeKey(name, kid, pem, dsname, callback)
})
})
})
})
} else {
encKey.id((err, kid) => {
if (err) return _error(callback, err)
self._storeKey(name, kid, encKey.bytes, dsname, callback)
})
}
})
}

Expand All @@ -457,23 +462,28 @@ class Keychain {
if (err) return _error(callback, err)
privateKey.export(this._(), (err, pem) => {
if (err) return _error(callback, err)
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)
})
self._storeKey(name, kid, pem, dsname, callback)
})
})
})
}

_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.
*
Expand Down
Loading

0 comments on commit 9eb11f4

Please sign in to comment.