Skip to content

Commit

Permalink
feat(keychain): add support for ed25519 and secp keys (#725)
Browse files Browse the repository at this point in the history
* feat(keychain): add support for ed25519 and secp keys

* chore: bump crypto

* refactor: cleanup keychain usage
  • Loading branch information
jacobheun authored Aug 5, 2020
1 parent 726a746 commit 51d7ca4
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 11 deletions.
6 changes: 3 additions & 3 deletions doc/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,7 @@ const latency = await libp2p.ping(otherPeerId)

## multiaddrs

Gets the multiaddrs the libp2p node announces to the network. This computes the advertising multiaddrs
Gets the multiaddrs the libp2p node announces to the network. This computes the advertising multiaddrs
of the peer by joining the multiaddrs that libp2p transports are listening on with the announce multiaddrs
provided in the libp2p config. Configured no announce multiaddrs will be filtered out of the advertised addresses.

Expand Down Expand Up @@ -1454,7 +1454,7 @@ Create a key in the keychain.
|------|------|-------------|
| name | `string` | The local key name. It cannot already exist. |
| type | `string` | One of the key types; 'rsa' |
| size | `number` | The key size in bits. |
| [size] | `number` | The key size in bits. Must be provided for rsa keys. |

#### Returns

Expand Down Expand Up @@ -1801,7 +1801,7 @@ console.log(peerStats.toJSON())

## Events

Once you have a libp2p instance, you can listen to several events it emits, so that you can be notified of relevant network events.
Once you have a libp2p instance, you can listen to several events it emits, so that you can be notified of relevant network events.

### libp2p

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
"it-length-prefixed": "^3.0.1",
"it-pipe": "^1.1.0",
"it-protocol-buffers": "^0.2.0",
"libp2p-crypto": "^0.17.8",
"libp2p-crypto": "^0.17.9",
"libp2p-interfaces": "^0.3.1",
"libp2p-utils": "^0.1.2",
"mafmt": "^7.0.0",
Expand Down
13 changes: 6 additions & 7 deletions src/keychain/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ const crypto = require('libp2p-crypto')
const DS = require('interface-datastore')
const CMS = require('./cms')
const errcode = require('err-code')
const { Number } = require('ipfs-utils/src/globalthis')

require('node-forge/lib/sha512')

const keyPrefix = '/pkcs8/'
const infoPrefix = '/info/'
Expand Down Expand Up @@ -171,8 +174,8 @@ 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.
* @returns {KeyInfo}
* @param {int} [size] - The key size in bits. Used for rsa keys only.
* @returns {KeyInfo}
*/
async createKey (name, type, size) {
const self = this
Expand All @@ -185,17 +188,13 @@ class Keychain {
return throwDelayed(errcode(new Error(`Invalid key type '${type}'`), 'ERR_INVALID_KEY_TYPE'))
}

if (!Number.isSafeInteger(size)) {
return throwDelayed(errcode(new Error(`Invalid key size '${size}'`), 'ERR_INVALID_KEY_SIZE'))
}

const dsname = DsName(name)
const exists = await self.store.has(dsname)
if (exists) return throwDelayed(errcode(new Error(`Key '${name}' already exists`), 'ERR_KEY_ALREADY_EXISTS'))

switch (type.toLowerCase()) {
case 'rsa':
if (size < 2048) {
if (!Number.isSafeInteger(size) || size < 2048) {
return throwDelayed(errcode(new Error(`Invalid RSA key size ${size}`), 'ERR_INVALID_KEY_SIZE'))
}
break
Expand Down
64 changes: 64 additions & 0 deletions test/keychain/keychain.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,70 @@ describe('keychain', () => {
})
})

describe('ed25519 keys', () => {
const keyName = 'my custom key'
it('can be an ed25519 key', async () => {
const keyInfo = await ks.createKey(keyName, 'ed25519')
expect(keyInfo).to.exist()
expect(keyInfo).to.have.property('name', keyName)
expect(keyInfo).to.have.property('id')
})

it('does not overwrite existing key', async () => {
const err = await ks.createKey(keyName, 'ed25519').then(fail, err => err)
expect(err).to.have.property('code', 'ERR_KEY_ALREADY_EXISTS')
})

it('can export/import a key', async () => {
const keyName = 'a new key'
const password = 'my sneaky password'
const keyInfo = await ks.createKey(keyName, 'ed25519')
const exportedKey = await ks.exportKey(keyName, password)
// remove it so we can import it
await ks.removeKey(keyName)
const importedKey = await ks.importKey(keyName, exportedKey, password)
expect(importedKey.id).to.eql(keyInfo.id)
})

it('cannot create the "self" key', async () => {
const err = await ks.createKey('self', 'ed25519').then(fail, err => err)
expect(err).to.exist()
expect(err).to.have.property('code', 'ERR_INVALID_KEY_NAME')
})
})

describe('secp256k1 keys', () => {
const keyName = 'my secp256k1 key'
it('can be an secp256k1 key', async () => {
const keyInfo = await ks.createKey(keyName, 'secp256k1')
expect(keyInfo).to.exist()
expect(keyInfo).to.have.property('name', keyName)
expect(keyInfo).to.have.property('id')
})

it('does not overwrite existing key', async () => {
const err = await ks.createKey(keyName, 'secp256k1').then(fail, err => err)
expect(err).to.have.property('code', 'ERR_KEY_ALREADY_EXISTS')
})

it('can export/import a key', async () => {
const keyName = 'a new secp256k1 key'
const password = 'my sneaky password'
const keyInfo = await ks.createKey(keyName, 'secp256k1')
const exportedKey = await ks.exportKey(keyName, password)
// remove it so we can import it
await ks.removeKey(keyName)
const importedKey = await ks.importKey(keyName, exportedKey, password)
expect(importedKey.id).to.eql(keyInfo.id)
})

it('cannot create the "self" key', async () => {
const err = await ks.createKey('self', 'secp256k1').then(fail, err => err)
expect(err).to.exist()
expect(err).to.have.property('code', 'ERR_INVALID_KEY_NAME')
})
})

describe('query', () => {
it('finds all existing keys', async () => {
const keys = await ks.listKeys()
Expand Down

0 comments on commit 51d7ca4

Please sign in to comment.