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

Commit

Permalink
feat: adds support for ed25199 and secp256k1
Browse files Browse the repository at this point in the history
  • Loading branch information
AlbertoElias committed Feb 16, 2019
1 parent aa5a6cb commit 07ffeb2
Show file tree
Hide file tree
Showing 4 changed files with 120 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
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,12 @@
"async": "^2.6.1",
"interface-datastore": "~0.6.0",
"libp2p-crypto": "~0.16.0",
"libp2p-crypto-secp256k1": "~0.2.3",
"merge-options": "^1.0.1",
"node-forge": "~0.7.6",
"pull-stream": "^3.6.8",
"sanitize-filename": "^1.6.1"
"sanitize-filename": "^1.6.1",
"secp256k1": "^3.6.2"
},
"devDependencies": {
"aegir": "^18.0.3",
Expand Down
118 changes: 60 additions & 58 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.marshal()
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,7 +361,8 @@ 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
Expand All @@ -376,65 +373,65 @@ class Keychain {
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)
})
const encKey = res.toString()
if (password) {
crypto.keys.import(encKey, this._(), (err, privateKey) => {
if (err) return _error(callback, err)
privateKey.export(password, callback)
})
} else {
crypto.keys.unmarshalPrivateKey(encKey, 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.
* @param {function(Error, KeyInfo)} callback
* @returns {undefined}
*/
importKey (name, pem, password, callback) {
importKey (name, encKey, password, callback) {
const self = this
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 {
const privateKey = crypto.keys.marshalPrivateKey(encKey)
privateKey.id((err, kid) => {
if (err) return _error(callback, err)
self._storeKey(name, kid, encKey, dsname, callback)
})
}
})
}

Expand All @@ -457,23 +454,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 07ffeb2

Please sign in to comment.