@libp2p/keychain 0.6.2 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -7
- package/dist/index.min.js +9 -9
- package/dist/src/errors.d.ts +0 -4
- package/dist/src/errors.d.ts.map +1 -1
- package/dist/src/errors.js +0 -4
- package/dist/src/errors.js.map +1 -1
- package/dist/src/index.d.ts +11 -9
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +39 -35
- package/dist/src/index.js.map +1 -1
- package/dist/src/util.d.ts +1 -1
- package/dist/src/util.d.ts.map +1 -1
- package/dist/typedoc-urls.json +6 -0
- package/package.json +2 -2
- package/src/errors.ts +1 -5
- package/src/index.ts +51 -45
- package/src/util.ts +1 -1
package/src/index.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { logger } from '@libp2p/logger'
|
|
|
4
4
|
import sanitize from 'sanitize-filename'
|
|
5
5
|
import mergeOptions from 'merge-options'
|
|
6
6
|
import { Key } from 'interface-datastore/key'
|
|
7
|
-
import
|
|
7
|
+
import { CodeError } from '@libp2p/interfaces/errors'
|
|
8
8
|
import { codes } from './errors.js'
|
|
9
9
|
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
|
|
10
10
|
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
|
|
@@ -50,7 +50,7 @@ const defaultOptions = {
|
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
function validateKeyName (name: string) {
|
|
53
|
+
function validateKeyName (name: string): boolean {
|
|
54
54
|
if (name == null) {
|
|
55
55
|
return false
|
|
56
56
|
}
|
|
@@ -66,7 +66,7 @@ function validateKeyName (name: string) {
|
|
|
66
66
|
* This assumes than an error indicates that the keychain is under attack. Delay returning an
|
|
67
67
|
* error to make brute force attacks harder.
|
|
68
68
|
*/
|
|
69
|
-
async function randomDelay () {
|
|
69
|
+
async function randomDelay (): Promise<void> {
|
|
70
70
|
const min = 200
|
|
71
71
|
const max = 1000
|
|
72
72
|
const delay = Math.random() * (max - min) + min
|
|
@@ -77,14 +77,14 @@ async function randomDelay () {
|
|
|
77
77
|
/**
|
|
78
78
|
* Converts a key name into a datastore name
|
|
79
79
|
*/
|
|
80
|
-
function DsName (name: string) {
|
|
80
|
+
function DsName (name: string): Key {
|
|
81
81
|
return new Key(keyPrefix + name)
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
/**
|
|
85
85
|
* Converts a key name into a datastore info name
|
|
86
86
|
*/
|
|
87
|
-
function DsInfoName (name: string) {
|
|
87
|
+
function DsInfoName (name: string): Key {
|
|
88
88
|
return new Key(infoPrefix + name)
|
|
89
89
|
}
|
|
90
90
|
|
|
@@ -155,7 +155,7 @@ export class DefaultKeyChain implements KeyChain {
|
|
|
155
155
|
*
|
|
156
156
|
* @returns {object}
|
|
157
157
|
*/
|
|
158
|
-
static get options () {
|
|
158
|
+
static get options (): typeof defaultOptions {
|
|
159
159
|
return defaultOptions
|
|
160
160
|
}
|
|
161
161
|
|
|
@@ -169,26 +169,26 @@ export class DefaultKeyChain implements KeyChain {
|
|
|
169
169
|
async createKey (name: string, type: KeyType, size = 2048): Promise<KeyInfo> {
|
|
170
170
|
if (!validateKeyName(name) || name === 'self') {
|
|
171
171
|
await randomDelay()
|
|
172
|
-
throw
|
|
172
|
+
throw new CodeError('Invalid key name', codes.ERR_INVALID_KEY_NAME)
|
|
173
173
|
}
|
|
174
174
|
|
|
175
175
|
if (typeof type !== 'string') {
|
|
176
176
|
await randomDelay()
|
|
177
|
-
throw
|
|
177
|
+
throw new CodeError('Invalid key type', codes.ERR_INVALID_KEY_TYPE)
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
const dsname = DsName(name)
|
|
181
181
|
const exists = await this.components.datastore.has(dsname)
|
|
182
182
|
if (exists) {
|
|
183
183
|
await randomDelay()
|
|
184
|
-
throw
|
|
184
|
+
throw new CodeError('Key name already exists', codes.ERR_KEY_ALREADY_EXISTS)
|
|
185
185
|
}
|
|
186
186
|
|
|
187
187
|
switch (type.toLowerCase()) {
|
|
188
188
|
case 'rsa':
|
|
189
189
|
if (!Number.isSafeInteger(size) || size < 2048) {
|
|
190
190
|
await randomDelay()
|
|
191
|
-
throw
|
|
191
|
+
throw new CodeError('Invalid RSA key size', codes.ERR_INVALID_KEY_SIZE)
|
|
192
192
|
}
|
|
193
193
|
break
|
|
194
194
|
default:
|
|
@@ -202,13 +202,13 @@ export class DefaultKeyChain implements KeyChain {
|
|
|
202
202
|
const cached = privates.get(this)
|
|
203
203
|
|
|
204
204
|
if (cached == null) {
|
|
205
|
-
throw
|
|
205
|
+
throw new CodeError('dek missing', codes.ERR_INVALID_PARAMETERS)
|
|
206
206
|
}
|
|
207
207
|
|
|
208
208
|
const dek = cached.dek
|
|
209
209
|
const pem = await keypair.export(dek)
|
|
210
210
|
keyInfo = {
|
|
211
|
-
name
|
|
211
|
+
name,
|
|
212
212
|
id: kid
|
|
213
213
|
}
|
|
214
214
|
const batch = this.components.datastore.batch()
|
|
@@ -229,7 +229,7 @@ export class DefaultKeyChain implements KeyChain {
|
|
|
229
229
|
*
|
|
230
230
|
* @returns {Promise<KeyInfo[]>}
|
|
231
231
|
*/
|
|
232
|
-
async listKeys () {
|
|
232
|
+
async listKeys (): Promise<KeyInfo[]> {
|
|
233
233
|
const query = {
|
|
234
234
|
prefix: infoPrefix
|
|
235
235
|
}
|
|
@@ -248,7 +248,13 @@ export class DefaultKeyChain implements KeyChain {
|
|
|
248
248
|
async findKeyById (id: string): Promise<KeyInfo> {
|
|
249
249
|
try {
|
|
250
250
|
const keys = await this.listKeys()
|
|
251
|
-
|
|
251
|
+
const key = keys.find((k) => k.id === id)
|
|
252
|
+
|
|
253
|
+
if (key == null) {
|
|
254
|
+
throw new CodeError(`Key with id '${id}' does not exist.`, codes.ERR_KEY_NOT_FOUND)
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return key
|
|
252
258
|
} catch (err: any) {
|
|
253
259
|
await randomDelay()
|
|
254
260
|
throw err
|
|
@@ -264,7 +270,7 @@ export class DefaultKeyChain implements KeyChain {
|
|
|
264
270
|
async findKeyByName (name: string): Promise<KeyInfo> {
|
|
265
271
|
if (!validateKeyName(name)) {
|
|
266
272
|
await randomDelay()
|
|
267
|
-
throw
|
|
273
|
+
throw new CodeError(`Invalid key name '${name}'`, codes.ERR_INVALID_KEY_NAME)
|
|
268
274
|
}
|
|
269
275
|
|
|
270
276
|
const dsname = DsInfoName(name)
|
|
@@ -274,7 +280,7 @@ export class DefaultKeyChain implements KeyChain {
|
|
|
274
280
|
} catch (err: any) {
|
|
275
281
|
await randomDelay()
|
|
276
282
|
log.error(err)
|
|
277
|
-
throw
|
|
283
|
+
throw new CodeError(`Key '${name}' does not exist.`, codes.ERR_KEY_NOT_FOUND)
|
|
278
284
|
}
|
|
279
285
|
}
|
|
280
286
|
|
|
@@ -284,10 +290,10 @@ export class DefaultKeyChain implements KeyChain {
|
|
|
284
290
|
* @param {string} name - The local key name; must already exist.
|
|
285
291
|
* @returns {Promise<KeyInfo>}
|
|
286
292
|
*/
|
|
287
|
-
async removeKey (name: string) {
|
|
293
|
+
async removeKey (name: string): Promise<KeyInfo> {
|
|
288
294
|
if (!validateKeyName(name) || name === 'self') {
|
|
289
295
|
await randomDelay()
|
|
290
|
-
throw
|
|
296
|
+
throw new CodeError(`Invalid key name '${name}'`, codes.ERR_INVALID_KEY_NAME)
|
|
291
297
|
}
|
|
292
298
|
const dsname = DsName(name)
|
|
293
299
|
const keyInfo = await this.findKeyByName(name)
|
|
@@ -308,11 +314,11 @@ export class DefaultKeyChain implements KeyChain {
|
|
|
308
314
|
async renameKey (oldName: string, newName: string): Promise<KeyInfo> {
|
|
309
315
|
if (!validateKeyName(oldName) || oldName === 'self') {
|
|
310
316
|
await randomDelay()
|
|
311
|
-
throw
|
|
317
|
+
throw new CodeError(`Invalid old key name '${oldName}'`, codes.ERR_OLD_KEY_NAME_INVALID)
|
|
312
318
|
}
|
|
313
319
|
if (!validateKeyName(newName) || newName === 'self') {
|
|
314
320
|
await randomDelay()
|
|
315
|
-
throw
|
|
321
|
+
throw new CodeError(`Invalid new key name '${newName}'`, codes.ERR_NEW_KEY_NAME_INVALID)
|
|
316
322
|
}
|
|
317
323
|
const oldDsname = DsName(oldName)
|
|
318
324
|
const newDsname = DsName(newName)
|
|
@@ -322,7 +328,7 @@ export class DefaultKeyChain implements KeyChain {
|
|
|
322
328
|
const exists = await this.components.datastore.has(newDsname)
|
|
323
329
|
if (exists) {
|
|
324
330
|
await randomDelay()
|
|
325
|
-
throw
|
|
331
|
+
throw new CodeError(`Key '${newName}' already exists`, codes.ERR_KEY_ALREADY_EXISTS)
|
|
326
332
|
}
|
|
327
333
|
|
|
328
334
|
try {
|
|
@@ -347,14 +353,14 @@ export class DefaultKeyChain implements KeyChain {
|
|
|
347
353
|
/**
|
|
348
354
|
* Export an existing key as a PEM encrypted PKCS #8 string
|
|
349
355
|
*/
|
|
350
|
-
async exportKey (name: string, password: string) {
|
|
356
|
+
async exportKey (name: string, password: string): Promise<string> {
|
|
351
357
|
if (!validateKeyName(name)) {
|
|
352
358
|
await randomDelay()
|
|
353
|
-
throw
|
|
359
|
+
throw new CodeError(`Invalid key name '${name}'`, codes.ERR_INVALID_KEY_NAME)
|
|
354
360
|
}
|
|
355
361
|
if (password == null) {
|
|
356
362
|
await randomDelay()
|
|
357
|
-
throw
|
|
363
|
+
throw new CodeError('Password is required', codes.ERR_PASSWORD_REQUIRED)
|
|
358
364
|
}
|
|
359
365
|
|
|
360
366
|
const dsname = DsName(name)
|
|
@@ -364,7 +370,7 @@ export class DefaultKeyChain implements KeyChain {
|
|
|
364
370
|
const cached = privates.get(this)
|
|
365
371
|
|
|
366
372
|
if (cached == null) {
|
|
367
|
-
throw
|
|
373
|
+
throw new CodeError('dek missing', codes.ERR_INVALID_PARAMETERS)
|
|
368
374
|
}
|
|
369
375
|
|
|
370
376
|
const dek = cached.dek
|
|
@@ -379,7 +385,7 @@ export class DefaultKeyChain implements KeyChain {
|
|
|
379
385
|
/**
|
|
380
386
|
* Export an existing key as a PeerId
|
|
381
387
|
*/
|
|
382
|
-
async exportPeerId (name: string) {
|
|
388
|
+
async exportPeerId (name: string): Promise<PeerId> {
|
|
383
389
|
const password = 'temporary-password'
|
|
384
390
|
const pem = await this.exportKey(name, password)
|
|
385
391
|
const privateKey = await importKey(pem, password)
|
|
@@ -398,17 +404,17 @@ export class DefaultKeyChain implements KeyChain {
|
|
|
398
404
|
async importKey (name: string, pem: string, password: string): Promise<KeyInfo> {
|
|
399
405
|
if (!validateKeyName(name) || name === 'self') {
|
|
400
406
|
await randomDelay()
|
|
401
|
-
throw
|
|
407
|
+
throw new CodeError(`Invalid key name '${name}'`, codes.ERR_INVALID_KEY_NAME)
|
|
402
408
|
}
|
|
403
409
|
if (pem == null) {
|
|
404
410
|
await randomDelay()
|
|
405
|
-
throw
|
|
411
|
+
throw new CodeError('PEM encoded key is required', codes.ERR_PEM_REQUIRED)
|
|
406
412
|
}
|
|
407
413
|
const dsname = DsName(name)
|
|
408
414
|
const exists = await this.components.datastore.has(dsname)
|
|
409
415
|
if (exists) {
|
|
410
416
|
await randomDelay()
|
|
411
|
-
throw
|
|
417
|
+
throw new CodeError(`Key '${name}' already exists`, codes.ERR_KEY_ALREADY_EXISTS)
|
|
412
418
|
}
|
|
413
419
|
|
|
414
420
|
let privateKey
|
|
@@ -416,7 +422,7 @@ export class DefaultKeyChain implements KeyChain {
|
|
|
416
422
|
privateKey = await importKey(pem, password)
|
|
417
423
|
} catch (err: any) {
|
|
418
424
|
await randomDelay()
|
|
419
|
-
throw
|
|
425
|
+
throw new CodeError('Cannot read the key, most likely the password is wrong', codes.ERR_CANNOT_READ_KEY)
|
|
420
426
|
}
|
|
421
427
|
|
|
422
428
|
let kid
|
|
@@ -425,7 +431,7 @@ export class DefaultKeyChain implements KeyChain {
|
|
|
425
431
|
const cached = privates.get(this)
|
|
426
432
|
|
|
427
433
|
if (cached == null) {
|
|
428
|
-
throw
|
|
434
|
+
throw new CodeError('dek missing', codes.ERR_INVALID_PARAMETERS)
|
|
429
435
|
}
|
|
430
436
|
|
|
431
437
|
const dek = cached.dek
|
|
@@ -436,7 +442,7 @@ export class DefaultKeyChain implements KeyChain {
|
|
|
436
442
|
}
|
|
437
443
|
|
|
438
444
|
const keyInfo = {
|
|
439
|
-
name
|
|
445
|
+
name,
|
|
440
446
|
id: kid
|
|
441
447
|
}
|
|
442
448
|
const batch = this.components.datastore.batch()
|
|
@@ -453,13 +459,13 @@ export class DefaultKeyChain implements KeyChain {
|
|
|
453
459
|
async importPeer (name: string, peer: PeerId): Promise<KeyInfo> {
|
|
454
460
|
try {
|
|
455
461
|
if (!validateKeyName(name)) {
|
|
456
|
-
throw
|
|
462
|
+
throw new CodeError(`Invalid key name '${name}'`, codes.ERR_INVALID_KEY_NAME)
|
|
457
463
|
}
|
|
458
464
|
if (peer == null) {
|
|
459
|
-
throw
|
|
465
|
+
throw new CodeError('PeerId is required', codes.ERR_MISSING_PRIVATE_KEY)
|
|
460
466
|
}
|
|
461
467
|
if (peer.privateKey == null) {
|
|
462
|
-
throw
|
|
468
|
+
throw new CodeError('PeerId.privKey is required', codes.ERR_MISSING_PRIVATE_KEY)
|
|
463
469
|
}
|
|
464
470
|
|
|
465
471
|
const privateKey = await unmarshalPrivateKey(peer.privateKey)
|
|
@@ -468,19 +474,19 @@ export class DefaultKeyChain implements KeyChain {
|
|
|
468
474
|
const exists = await this.components.datastore.has(dsname)
|
|
469
475
|
if (exists) {
|
|
470
476
|
await randomDelay()
|
|
471
|
-
throw
|
|
477
|
+
throw new CodeError(`Key '${name}' already exists`, codes.ERR_KEY_ALREADY_EXISTS)
|
|
472
478
|
}
|
|
473
479
|
|
|
474
480
|
const cached = privates.get(this)
|
|
475
481
|
|
|
476
482
|
if (cached == null) {
|
|
477
|
-
throw
|
|
483
|
+
throw new CodeError('dek missing', codes.ERR_INVALID_PARAMETERS)
|
|
478
484
|
}
|
|
479
485
|
|
|
480
486
|
const dek = cached.dek
|
|
481
487
|
const pem = await privateKey.export(dek)
|
|
482
488
|
const keyInfo: KeyInfo = {
|
|
483
|
-
name
|
|
489
|
+
name,
|
|
484
490
|
id: peer.toString()
|
|
485
491
|
}
|
|
486
492
|
const batch = this.components.datastore.batch()
|
|
@@ -500,7 +506,7 @@ export class DefaultKeyChain implements KeyChain {
|
|
|
500
506
|
async getPrivateKey (name: string): Promise<string> {
|
|
501
507
|
if (!validateKeyName(name)) {
|
|
502
508
|
await randomDelay()
|
|
503
|
-
throw
|
|
509
|
+
throw new CodeError(`Invalid key name '${name}'`, codes.ERR_INVALID_KEY_NAME)
|
|
504
510
|
}
|
|
505
511
|
|
|
506
512
|
try {
|
|
@@ -510,31 +516,31 @@ export class DefaultKeyChain implements KeyChain {
|
|
|
510
516
|
} catch (err: any) {
|
|
511
517
|
await randomDelay()
|
|
512
518
|
log.error(err)
|
|
513
|
-
throw
|
|
519
|
+
throw new CodeError(`Key '${name}' does not exist.`, codes.ERR_KEY_NOT_FOUND)
|
|
514
520
|
}
|
|
515
521
|
}
|
|
516
522
|
|
|
517
523
|
/**
|
|
518
524
|
* Rotate keychain password and re-encrypt all associated keys
|
|
519
525
|
*/
|
|
520
|
-
async rotateKeychainPass (oldPass: string, newPass: string) {
|
|
526
|
+
async rotateKeychainPass (oldPass: string, newPass: string): Promise<void> {
|
|
521
527
|
if (typeof oldPass !== 'string') {
|
|
522
528
|
await randomDelay()
|
|
523
|
-
throw
|
|
529
|
+
throw new CodeError(`Invalid old pass type '${typeof oldPass}'`, codes.ERR_INVALID_OLD_PASS_TYPE)
|
|
524
530
|
}
|
|
525
531
|
if (typeof newPass !== 'string') {
|
|
526
532
|
await randomDelay()
|
|
527
|
-
throw
|
|
533
|
+
throw new CodeError(`Invalid new pass type '${typeof newPass}'`, codes.ERR_INVALID_NEW_PASS_TYPE)
|
|
528
534
|
}
|
|
529
535
|
if (newPass.length < 20) {
|
|
530
536
|
await randomDelay()
|
|
531
|
-
throw
|
|
537
|
+
throw new CodeError(`Invalid pass length ${newPass.length}`, codes.ERR_INVALID_PASS_LENGTH)
|
|
532
538
|
}
|
|
533
539
|
log('recreating keychain')
|
|
534
540
|
const cached = privates.get(this)
|
|
535
541
|
|
|
536
542
|
if (cached == null) {
|
|
537
|
-
throw
|
|
543
|
+
throw new CodeError('dek missing', codes.ERR_INVALID_PARAMETERS)
|
|
538
544
|
}
|
|
539
545
|
|
|
540
546
|
const oldDek = cached.dek
|
package/src/util.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* @param {Array} array
|
|
9
9
|
* @param {function(*)} asyncCompare - An async function that returns a boolean
|
|
10
10
|
*/
|
|
11
|
-
export async function findAsync <T> (array: T[], asyncCompare: (val: T) => Promise<any>) {
|
|
11
|
+
export async function findAsync <T> (array: T[], asyncCompare: (val: T) => Promise<any>): Promise<T | undefined> {
|
|
12
12
|
const promises = array.map(asyncCompare)
|
|
13
13
|
const results = await Promise.all(promises)
|
|
14
14
|
const index = results.findIndex(result => result)
|