mirror of
https://github.com/fluencelabs/js-libp2p
synced 2025-07-10 14:21:33 +00:00
Compare commits
1 Commits
v0.37.0
...
fix/load-k
Author | SHA1 | Date | |
---|---|---|---|
82330ac4e6 |
30
doc/API.md
30
doc/API.md
@ -181,36 +181,6 @@ Required keys in the `options` object:
|
||||
|
||||
## 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
|
||||
|
||||
Starts the libp2p node.
|
||||
|
@ -494,8 +494,6 @@ const node = await createLibp2p({
|
||||
datastore: dsInstant,
|
||||
}
|
||||
})
|
||||
|
||||
await node.loadKeychain()
|
||||
```
|
||||
|
||||
#### Configuring Dialing
|
||||
|
@ -20,7 +20,7 @@ import type { ConnectionManager, Registrar, StreamHandler } from '@libp2p/interf
|
||||
import type { Metrics, MetricsInit } from '@libp2p/interfaces/metrics'
|
||||
import type { PeerInfo } from '@libp2p/interfaces/peer-info'
|
||||
import type { DialerInit } from '@libp2p/interfaces/dialer'
|
||||
import type { KeyChain } from './keychain/index.js'
|
||||
import type { KeyChain } from '@libp2p/interfaces/keychain'
|
||||
|
||||
export interface PersistentPeerStoreOptions {
|
||||
threshold?: number
|
||||
@ -158,12 +158,6 @@ export interface Libp2p extends Startable, EventEmitter<Libp2pEvents> {
|
||||
pubsub?: PubSub
|
||||
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
|
||||
* the listen addresses used by transports with any configured
|
||||
|
@ -8,7 +8,7 @@ import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
|
||||
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
|
||||
import { codes } from '../errors.js'
|
||||
import { logger } from '@libp2p/logger'
|
||||
import type { KeyChain } from './index.js'
|
||||
import type { DefaultKeyChain } from './index.js'
|
||||
|
||||
const log = logger('libp2p:keychain:cms')
|
||||
|
||||
@ -24,12 +24,12 @@ const privates = new WeakMap<object, { dek: string }>()
|
||||
* See RFC 5652 for all the details.
|
||||
*/
|
||||
export class CMS {
|
||||
private readonly keychain: KeyChain
|
||||
private readonly keychain: DefaultKeyChain
|
||||
|
||||
/**
|
||||
* Creates a new instance with a keychain
|
||||
*/
|
||||
constructor (keychain: KeyChain, dek: string) {
|
||||
constructor (keychain: DefaultKeyChain, dek: string) {
|
||||
if (keychain == null) {
|
||||
throw errCode(new Error('keychain is required'), codes.ERR_KEYCHAIN_REQUIRED)
|
||||
}
|
||||
|
@ -14,6 +14,8 @@ import { generateKeyPair, importKey, unmarshalPrivateKey } from '@libp2p/crypto/
|
||||
import type { PeerId } from '@libp2p/interfaces/peer-id'
|
||||
import type { Components } from '@libp2p/interfaces/components'
|
||||
import { pbkdf2, randomBytes } from '@libp2p/crypto'
|
||||
import type { KeyChain } from '@libp2p/interfaces/keychain'
|
||||
import type { Startable } from '@libp2p/interfaces'
|
||||
|
||||
const log = logger('libp2p:keychain')
|
||||
|
||||
@ -111,9 +113,10 @@ function DsInfoName (name: string) {
|
||||
* - '/pkcs8/*key-name*', contains the PKCS #8 for the key
|
||||
*
|
||||
*/
|
||||
export class KeyChain {
|
||||
export class DefaultKeyChain implements KeyChain, Startable {
|
||||
private readonly components: Components
|
||||
private init: KeyChainInit
|
||||
private started: boolean
|
||||
|
||||
/**
|
||||
* Creates a new instance of a key chain
|
||||
@ -146,6 +149,27 @@ export class KeyChain {
|
||||
: ''
|
||||
|
||||
privates.set(this, { dek })
|
||||
this.started = false
|
||||
}
|
||||
|
||||
isStarted () {
|
||||
return this.started
|
||||
}
|
||||
|
||||
async start () {
|
||||
// Load keychain keys from the datastore.
|
||||
// Imports the private key as 'self', if needed.
|
||||
try {
|
||||
await this.findKeyByName('self')
|
||||
} catch (err: any) {
|
||||
await this.importPeer('self', this.components.getPeerId())
|
||||
}
|
||||
|
||||
this.started = true
|
||||
}
|
||||
|
||||
async stop () {
|
||||
this.started = false
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -12,7 +12,7 @@ import { AutoDialler } from './connection-manager/auto-dialler.js'
|
||||
import { Circuit } from './circuit/transport.js'
|
||||
import { Relay } from './circuit/index.js'
|
||||
import { DefaultDialer } from './dialer/index.js'
|
||||
import { KeyChain } from './keychain/index.js'
|
||||
import { DefaultKeyChain } from './keychain/index.js'
|
||||
import { DefaultMetrics } from './metrics/index.js'
|
||||
import { DefaultTransportManager } from './transport-manager.js'
|
||||
import { DefaultUpgrader } from './upgrader.js'
|
||||
@ -44,6 +44,7 @@ import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
|
||||
import errCode from 'err-code'
|
||||
import { unmarshalPublicKey } from '@libp2p/crypto/keys'
|
||||
import type { Metrics } from '@libp2p/interfaces/metrics'
|
||||
import type { KeyChain } from '@libp2p/interfaces/keychain'
|
||||
|
||||
const log = logger('libp2p')
|
||||
|
||||
@ -140,8 +141,8 @@ export class Libp2pNode extends EventEmitter<Libp2pEvents> implements Libp2p {
|
||||
}))
|
||||
|
||||
// Create keychain
|
||||
const keychainOpts = KeyChain.generateOptions()
|
||||
this.keychain = this.configureComponent(new KeyChain(this.components, {
|
||||
const keychainOpts = DefaultKeyChain.generateOptions()
|
||||
this.keychain = this.configureComponent(new DefaultKeyChain(this.components, {
|
||||
...keychainOpts,
|
||||
...init.keychain
|
||||
}))
|
||||
@ -352,22 +353,6 @@ export class Libp2pNode extends EventEmitter<Libp2pEvents> implements Libp2p {
|
||||
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 () {
|
||||
return this.started
|
||||
}
|
||||
|
@ -5,17 +5,17 @@ import { expect } from 'aegir/chai'
|
||||
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
|
||||
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
|
||||
import { MemoryDatastore } from 'datastore-core/memory'
|
||||
import { KeyChain } from '../../src/keychain/index.js'
|
||||
import { DefaultKeyChain } from '../../src/keychain/index.js'
|
||||
import { Components } from '@libp2p/interfaces/components'
|
||||
|
||||
describe('cms interop', () => {
|
||||
const passPhrase = 'this is not a secure phrase'
|
||||
const aliceKeyName = 'cms-interop-alice'
|
||||
let ks: KeyChain
|
||||
let ks: DefaultKeyChain
|
||||
|
||||
before(() => {
|
||||
const datastore = new MemoryDatastore()
|
||||
ks = new KeyChain(new Components({ datastore }), { pass: passPhrase })
|
||||
ks = new DefaultKeyChain(new Components({ datastore }), { pass: passPhrase })
|
||||
})
|
||||
|
||||
const plainData = uint8ArrayFromString('This is a message from Alice to Bob')
|
||||
|
@ -7,7 +7,7 @@ import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
|
||||
import { createNode } from '../utils/creators/peer.js'
|
||||
import { Key } from 'interface-datastore/key'
|
||||
import { MemoryDatastore } from 'datastore-core/memory'
|
||||
import { KeyChain, KeyChainInit, KeyInfo } from '../../src/keychain/index.js'
|
||||
import { DefaultKeyChain, KeyChainInit, KeyInfo } from '../../src/keychain/index.js'
|
||||
import { pbkdf2 } from '@libp2p/crypto'
|
||||
import { Components } from '@libp2p/interfaces/components'
|
||||
import type { Datastore } from 'interface-datastore'
|
||||
@ -20,16 +20,16 @@ describe('keychain', () => {
|
||||
const rsaKeyName = 'tajné jméno'
|
||||
const renamedRsaKeyName = 'ชื่อลับ'
|
||||
let rsaKeyInfo: KeyInfo
|
||||
let emptyKeystore: KeyChain
|
||||
let ks: KeyChain
|
||||
let emptyKeystore: DefaultKeyChain
|
||||
let ks: DefaultKeyChain
|
||||
let datastore1: Datastore, datastore2: Datastore
|
||||
|
||||
before(async () => {
|
||||
datastore1 = new MemoryDatastore()
|
||||
datastore2 = new MemoryDatastore()
|
||||
|
||||
ks = new KeyChain(new Components({ datastore: datastore2 }), { pass: passPhrase })
|
||||
emptyKeystore = new KeyChain(new Components({ datastore: datastore1 }), { pass: passPhrase })
|
||||
ks = new DefaultKeyChain(new Components({ datastore: datastore2 }), { pass: passPhrase })
|
||||
emptyKeystore = new DefaultKeyChain(new Components({ datastore: datastore1 }), { pass: passPhrase })
|
||||
|
||||
await datastore1.open()
|
||||
await datastore2.open()
|
||||
@ -41,35 +41,35 @@ describe('keychain', () => {
|
||||
})
|
||||
|
||||
it('can start without a password', () => {
|
||||
expect(() => new KeyChain(new Components({ datastore: datastore2 }), {})).to.not.throw()
|
||||
expect(() => new DefaultKeyChain(new Components({ datastore: datastore2 }), {})).to.not.throw()
|
||||
})
|
||||
|
||||
it('needs a NIST SP 800-132 non-weak pass phrase', () => {
|
||||
expect(() => new KeyChain(new Components({ datastore: datastore2 }), { pass: '< 20 character' })).to.throw()
|
||||
expect(() => new DefaultKeyChain(new Components({ datastore: datastore2 }), { pass: '< 20 character' })).to.throw()
|
||||
})
|
||||
|
||||
it('has default options', () => {
|
||||
expect(KeyChain.options).to.exist()
|
||||
expect(DefaultKeyChain.options).to.exist()
|
||||
})
|
||||
|
||||
it('supports supported hashing alorithms', () => {
|
||||
const ok = new KeyChain(new Components({ datastore: datastore2 }), { pass: passPhrase, dek: { hash: 'sha2-256', salt: 'salt-salt-salt-salt', iterationCount: 1000, keyLength: 14 } })
|
||||
const ok = new DefaultKeyChain(new Components({ datastore: datastore2 }), { pass: passPhrase, dek: { hash: 'sha2-256', salt: 'salt-salt-salt-salt', iterationCount: 1000, keyLength: 14 } })
|
||||
expect(ok).to.exist()
|
||||
})
|
||||
|
||||
it('does not support unsupported hashing alorithms', () => {
|
||||
expect(() => new KeyChain(new Components({ datastore: datastore2 }), { pass: passPhrase, dek: { hash: 'my-hash', salt: 'salt-salt-salt-salt', iterationCount: 1000, keyLength: 14 } })).to.throw()
|
||||
expect(() => new DefaultKeyChain(new Components({ datastore: datastore2 }), { pass: passPhrase, dek: { hash: 'my-hash', salt: 'salt-salt-salt-salt', iterationCount: 1000, keyLength: 14 } })).to.throw()
|
||||
})
|
||||
|
||||
it('can list keys without a password', async () => {
|
||||
const keychain = new KeyChain(new Components({ datastore: datastore2 }), {})
|
||||
const keychain = new DefaultKeyChain(new Components({ datastore: datastore2 }), {})
|
||||
|
||||
expect(await keychain.listKeys()).to.have.lengthOf(0)
|
||||
})
|
||||
|
||||
it('can find a key without a password', async () => {
|
||||
const keychain = new KeyChain(new Components({ datastore: datastore2 }), {})
|
||||
const keychainWithPassword = new KeyChain(new Components({ datastore: datastore2 }), { pass: `hello-${Date.now()}-${Date.now()}` })
|
||||
const keychain = new DefaultKeyChain(new Components({ datastore: datastore2 }), {})
|
||||
const keychainWithPassword = new DefaultKeyChain(new Components({ datastore: datastore2 }), { pass: `hello-${Date.now()}-${Date.now()}` })
|
||||
const name = `key-${Math.random()}`
|
||||
|
||||
const { id } = await keychainWithPassword.createKey(name, 'Ed25519')
|
||||
@ -78,8 +78,8 @@ describe('keychain', () => {
|
||||
})
|
||||
|
||||
it('can remove a key without a password', async () => {
|
||||
const keychainWithoutPassword = new KeyChain(new Components({ datastore: datastore2 }), {})
|
||||
const keychainWithPassword = new KeyChain(new Components({ datastore: datastore2 }), { pass: `hello-${Date.now()}-${Date.now()}` })
|
||||
const keychainWithoutPassword = new DefaultKeyChain(new Components({ datastore: datastore2 }), {})
|
||||
const keychainWithPassword = new DefaultKeyChain(new Components({ datastore: datastore2 }), { pass: `hello-${Date.now()}-${Date.now()}` })
|
||||
const name = `key-${Math.random()}`
|
||||
|
||||
expect(await keychainWithPassword.createKey(name, 'Ed25519')).to.have.property('name', name)
|
||||
@ -89,16 +89,16 @@ describe('keychain', () => {
|
||||
})
|
||||
|
||||
it('requires a name to create a password', async () => {
|
||||
const keychain = new KeyChain(new Components({ datastore: datastore2 }), {})
|
||||
const keychain = new DefaultKeyChain(new Components({ datastore: datastore2 }), {})
|
||||
|
||||
// @ts-expect-error invalid parameters
|
||||
await expect(keychain.createKey(undefined, 'derp')).to.be.rejected()
|
||||
})
|
||||
|
||||
it('can generate options', () => {
|
||||
const options = KeyChain.generateOptions()
|
||||
const options = DefaultKeyChain.generateOptions()
|
||||
options.pass = passPhrase
|
||||
const chain = new KeyChain(new Components({ datastore: datastore2 }), options)
|
||||
const chain = new DefaultKeyChain(new Components({ datastore: datastore2 }), options)
|
||||
expect(chain).to.exist()
|
||||
})
|
||||
|
||||
@ -418,7 +418,7 @@ describe('keychain', () => {
|
||||
|
||||
describe('rotate keychain passphrase', () => {
|
||||
let oldPass: string
|
||||
let kc: KeyChain
|
||||
let kc: DefaultKeyChain
|
||||
let options: KeyChainInit
|
||||
let ds: Datastore
|
||||
before(async () => {
|
||||
@ -433,7 +433,7 @@ describe('keychain', () => {
|
||||
hash: 'sha2-512'
|
||||
}
|
||||
}
|
||||
kc = new KeyChain(new Components({ datastore: ds }), options)
|
||||
kc = new DefaultKeyChain(new Components({ datastore: ds }), options)
|
||||
await ds.open()
|
||||
})
|
||||
|
||||
@ -512,8 +512,6 @@ describe('libp2p.keychain', () => {
|
||||
}
|
||||
})
|
||||
|
||||
await libp2p.loadKeychain()
|
||||
|
||||
const kInfo = await libp2p.keychain.createKey('keyName', 'Ed25519')
|
||||
expect(kInfo).to.exist()
|
||||
})
|
||||
@ -526,8 +524,6 @@ describe('libp2p.keychain', () => {
|
||||
}
|
||||
})
|
||||
|
||||
await libp2p.loadKeychain()
|
||||
|
||||
const kInfo = await libp2p.keychain.createKey('keyName', 'Ed25519')
|
||||
expect(kInfo).to.exist()
|
||||
})
|
||||
@ -543,7 +539,6 @@ describe('libp2p.keychain', () => {
|
||||
}
|
||||
}
|
||||
})
|
||||
await libp2p.loadKeychain()
|
||||
|
||||
const kInfo = await libp2p.keychain.createKey('keyName', 'Ed25519')
|
||||
expect(kInfo).to.exist()
|
||||
@ -558,7 +553,6 @@ describe('libp2p.keychain', () => {
|
||||
}
|
||||
})
|
||||
|
||||
await libp2p2.loadKeychain()
|
||||
const key = await libp2p2.keychain.findKeyByName('keyName')
|
||||
|
||||
expect(key).to.exist()
|
||||
|
Reference in New Issue
Block a user