diff --git a/src/keychain/index.ts b/src/keychain/index.ts index ca91cb31e4..d508c26007 100644 --- a/src/keychain/index.ts +++ b/src/keychain/index.ts @@ -14,6 +14,8 @@ import type { PeerId } from '@libp2p/interface-peer-id' import { pbkdf2, randomBytes } from '@libp2p/crypto' import type { Startable } from '@libp2p/interfaces/dist/src/startable' import type { Datastore } from 'interface-datastore' +import { peerIdFromKeys } from '@libp2p/peer-id' +import type { KeyTypes } from '@libp2p/crypto/keys' const log = logger('libp2p:keychain') @@ -222,7 +224,7 @@ export class KeyChain implements Startable { * @param {string} type - One of the key types; 'rsa'. * @param {number} [size = 2048] - The key size in bits. Used for rsa keys only */ - async createKey (name: string, type: 'RSA' | 'Ed25519', size = 2048): Promise { + async createKey (name: string, type: KeyTypes, size = 2048): Promise { if (!validateKeyName(name) || name === 'self') { await randomDelay() throw errCode(new Error('Invalid key name'), codes.ERR_INVALID_KEY_NAME) @@ -432,6 +434,17 @@ export class KeyChain implements Startable { } } + /** + * Export an existing key as a PeerId + */ + async exportPeerId (name: string) { + const password = 'temporary-password' + const pem = await this.exportKey(name, password) + const privateKey = await importKey(pem, password) + + return await peerIdFromKeys(privateKey.public.bytes, privateKey.bytes) + } + /** * Import a new key from a PEM encoded PKCS #8 string * diff --git a/test/keychain/keychain.spec.ts b/test/keychain/keychain.spec.ts index b58c8e8a15..287f0cd44f 100644 --- a/test/keychain/keychain.spec.ts +++ b/test/keychain/keychain.spec.ts @@ -373,6 +373,14 @@ describe('keychain', () => { expect(key.id).to.equal(alice.toString()) }) + it('private key can be exported', async () => { + const alice2 = await ks.exportPeerId('alice') + + expect(alice.equals(alice2)).to.be.true() + expect(alice2).to.have.property('privateKey').that.is.ok() + expect(alice2).to.have.property('publicKey').that.is.ok() + }) + it('private key import requires a valid name', async () => { // @ts-expect-error invalid parameters await expect(ks.importPeer(undefined, alice)).to.eventually.be.rejected.with.property('code', 'ERR_INVALID_KEY_NAME') @@ -396,6 +404,36 @@ describe('keychain', () => { expect(key).to.have.property('name', 'alice') expect(key).to.have.property('id', alice.toString()) }) + + it('can create Ed25519 peer id', async () => { + const name = 'ed-key' + await ks.createKey(name, 'Ed25519') + const peer = await ks.exportPeerId(name) + + expect(peer).to.have.property('type', 'Ed25519') + expect(peer).to.have.property('privateKey').that.is.ok() + expect(peer).to.have.property('publicKey').that.is.ok() + }) + + it('can create RSA peer id', async () => { + const name = 'rsa-key' + await ks.createKey(name, 'RSA', 2048) + const peer = await ks.exportPeerId(name) + + expect(peer).to.have.property('type', 'RSA') + expect(peer).to.have.property('privateKey').that.is.ok() + expect(peer).to.have.property('publicKey').that.is.ok() + }) + + it('can create secp256k1 peer id', async () => { + const name = 'secp256k1-key' + await ks.createKey(name, 'secp256k1') + const peer = await ks.exportPeerId(name) + + expect(peer).to.have.property('type', 'secp256k1') + expect(peer).to.have.property('privateKey').that.is.ok() + expect(peer).to.have.property('publicKey').that.is.ok() + }) }) describe('rename', () => {