Skip to content
This repository has been archived by the owner on Jun 15, 2023. It is now read-only.

feat: adds support for ed25199 and secp256k1 #31

Merged
merged 3 commits into from
Feb 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)) {
AlbertoElias marked this conversation as resolved.
Show resolved Hide resolved
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