fix!: load self key into keychain on startup if not present (#1357)

To prevent triggering keychain attack prevention on startup, refactor the `KeyChain` class to load the current PeerId as the `'self'` key on startup.

Fixes #1315

BREAKING CHANGE: the `loadKeychain` method has been removed as it is no longer necessary
This commit is contained in:
Alex Potsides 2022-08-17 21:34:57 +01:00 committed by GitHub
parent 29c803a63e
commit 1f38ab7ac8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 27 additions and 65 deletions

View File

@ -185,36 +185,6 @@ Required keys in the `options` object:
## Libp2p Instance Methods ## Libp2p Instance Methods
### loadKeychain
Load keychain keys from the datastore, importing the private key as 'self', if needed.
`libp2p.loadKeychain()`
#### Returns
| Type | Description |
|------|-------------|
| `Promise` | Promise resolves when the keychain is ready |
#### Example
```js
import { createLibp2p } from 'libp2p'
// ...
const libp2p = await createLibp2p({
// ...
keychain: {
pass: '0123456789pass1234567890'
}
})
// load keychain
await libp2p.loadKeychain()
```
### start ### start
Starts the libp2p node. Starts the libp2p node.

View File

@ -494,8 +494,6 @@ const node = await createLibp2p({
datastore: dsInstant, datastore: dsInstant,
} }
}) })
await node.loadKeychain()
``` ```
#### Configuring Dialing #### Configuring Dialing

View File

@ -140,12 +140,6 @@ export interface Libp2p extends Startable, EventEmitter<Libp2pEvents> {
pubsub: PubSub pubsub: PubSub
dht: DualDHT dht: DualDHT
/**
* Load keychain keys from the datastore.
* Imports the private key as 'self', if needed.
*/
loadKeychain: () => Promise<void>
/** /**
* Get a deduplicated list of peer advertising multiaddrs by concatenating * Get a deduplicated list of peer advertising multiaddrs by concatenating
* the listen addresses used by transports with any configured * the listen addresses used by transports with any configured

View File

@ -13,6 +13,7 @@ import { generateKeyPair, importKey, unmarshalPrivateKey } from '@libp2p/crypto/
import type { PeerId } from '@libp2p/interface-peer-id' import type { PeerId } from '@libp2p/interface-peer-id'
import type { Components } from '@libp2p/components' import type { Components } from '@libp2p/components'
import { pbkdf2, randomBytes } from '@libp2p/crypto' import { pbkdf2, randomBytes } from '@libp2p/crypto'
import type { Startable } from '@libp2p/interfaces/dist/src/startable'
const log = logger('libp2p:keychain') const log = logger('libp2p:keychain')
@ -110,9 +111,10 @@ function DsInfoName (name: string) {
* - '/pkcs8/*key-name*', contains the PKCS #8 for the key * - '/pkcs8/*key-name*', contains the PKCS #8 for the key
* *
*/ */
export class KeyChain { export class KeyChain implements Startable {
private readonly components: Components private readonly components: Components
private init: KeyChainInit private init: KeyChainInit
private started: boolean
/** /**
* Creates a new instance of a key chain * Creates a new instance of a key chain
@ -145,6 +147,25 @@ export class KeyChain {
: '' : ''
privates.set(this, { dek }) privates.set(this, { dek })
this.started = false
}
isStarted () {
return this.started
}
async start () {
const dsname = DsInfoName('self')
if (!(await this.components.getDatastore().has(dsname))) {
await this.importPeer('self', this.components.getPeerId())
}
this.started = true
}
stop () {
this.started = false
} }
/** /**
@ -474,8 +495,11 @@ export class KeyChain {
if (!validateKeyName(name)) { if (!validateKeyName(name)) {
throw errCode(new Error(`Invalid key name '${name}'`), codes.ERR_INVALID_KEY_NAME) throw errCode(new Error(`Invalid key name '${name}'`), codes.ERR_INVALID_KEY_NAME)
} }
if (peer == null || peer.privateKey == null) { if (peer == null) {
throw errCode(new Error('Peer.privKey is required'), codes.ERR_MISSING_PRIVATE_KEY) throw errCode(new Error('PeerId is required'), codes.ERR_MISSING_PRIVATE_KEY)
}
if (peer.privateKey == null) {
throw errCode(new Error('PeerId.privKey is required'), codes.ERR_MISSING_PRIVATE_KEY)
} }
const privateKey = await unmarshalPrivateKey(peer.privateKey) const privateKey = await unmarshalPrivateKey(peer.privateKey)

View File

@ -352,22 +352,6 @@ export class Libp2pNode extends EventEmitter<Libp2pEvents> implements Libp2p {
log('libp2p has stopped') log('libp2p has stopped')
} }
/**
* Load keychain keys from the datastore.
* Imports the private key as 'self', if needed.
*/
async loadKeychain () {
if (this.keychain == null) {
return
}
try {
await this.keychain.findKeyByName('self')
} catch (err: any) {
await this.keychain.importPeer('self', this.peerId)
}
}
isStarted () { isStarted () {
return this.started return this.started
} }

View File

@ -9,12 +9,10 @@ import type { Message, PublishResult, PubSubInit, PubSubRPC, PubSubRPCMessage }
import type { Libp2pInit, Libp2pOptions } from '../../src/index.js' import type { Libp2pInit, Libp2pOptions } from '../../src/index.js'
import type { PeerId } from '@libp2p/interface-peer-id' import type { PeerId } from '@libp2p/interface-peer-id'
import * as cborg from 'cborg' import * as cborg from 'cborg'
import { peerIdFromString } from '@libp2p/peer-id'
const relayAddr = MULTIADDRS_WEBSOCKETS[0] const relayAddr = MULTIADDRS_WEBSOCKETS[0]
export const baseOptions: Partial<Libp2pInit> = { export const baseOptions: Partial<Libp2pInit> = {
peerId: peerIdFromString('12D3KooWJKCJW8Y26pRFNv78TCMGLNTfyN8oKaFswMRYXTzSbSst'),
transports: [new WebSockets()], transports: [new WebSockets()],
streamMuxers: [new Mplex()], streamMuxers: [new Mplex()],
connectionEncryption: [new Plaintext()] connectionEncryption: [new Plaintext()]

View File

@ -512,8 +512,6 @@ describe('libp2p.keychain', () => {
} }
}) })
await libp2p.loadKeychain()
const kInfo = await libp2p.keychain.createKey('keyName', 'Ed25519') const kInfo = await libp2p.keychain.createKey('keyName', 'Ed25519')
expect(kInfo).to.exist() expect(kInfo).to.exist()
}) })
@ -526,8 +524,6 @@ describe('libp2p.keychain', () => {
} }
}) })
await libp2p.loadKeychain()
const kInfo = await libp2p.keychain.createKey('keyName', 'Ed25519') const kInfo = await libp2p.keychain.createKey('keyName', 'Ed25519')
expect(kInfo).to.exist() expect(kInfo).to.exist()
}) })
@ -543,7 +539,6 @@ describe('libp2p.keychain', () => {
} }
} }
}) })
await libp2p.loadKeychain()
const kInfo = await libp2p.keychain.createKey('keyName', 'Ed25519') const kInfo = await libp2p.keychain.createKey('keyName', 'Ed25519')
expect(kInfo).to.exist() expect(kInfo).to.exist()
@ -558,7 +553,6 @@ describe('libp2p.keychain', () => {
} }
}) })
await libp2p2.loadKeychain()
const key = await libp2p2.keychain.findKeyByName('keyName') const key = await libp2p2.keychain.findKeyByName('keyName')
expect(key).to.exist() expect(key).to.exist()