@libp2p/keychain 4.1.6 → 5.0.0-18dd3cb26
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/dist/index.min.js +3 -3
- package/dist/src/index.d.ts +31 -51
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/keychain.d.ts +9 -54
- package/dist/src/keychain.d.ts.map +1 -1
- package/dist/src/keychain.js +100 -255
- package/dist/src/keychain.js.map +1 -1
- package/dist/src/utils/constants.d.ts +4 -0
- package/dist/src/utils/constants.d.ts.map +1 -0
- package/dist/src/utils/constants.js +4 -0
- package/dist/src/utils/constants.js.map +1 -0
- package/dist/src/utils/export.d.ts +33 -0
- package/dist/src/utils/export.d.ts.map +1 -0
- package/dist/src/utils/export.js +183 -0
- package/dist/src/utils/export.js.map +1 -0
- package/dist/src/utils/import.d.ts +15 -0
- package/dist/src/utils/import.d.ts.map +1 -0
- package/dist/src/utils/import.js +137 -0
- package/dist/src/utils/import.js.map +1 -0
- package/package.json +9 -9
- package/src/index.ts +33 -56
- package/src/keychain.ts +113 -278
- package/src/utils/constants.ts +3 -0
- package/src/utils/export.ts +210 -0
- package/src/utils/import.ts +174 -0
- package/dist/src/errors.d.ts +0 -18
- package/dist/src/errors.d.ts.map +0 -1
- package/dist/src/errors.js +0 -19
- package/dist/src/errors.js.map +0 -1
- package/dist/src/util.d.ts +0 -12
- package/dist/src/util.d.ts.map +0 -1
- package/dist/src/util.js +0 -17
- package/dist/src/util.js.map +0 -1
- package/dist/typedoc-urls.json +0 -14
- package/src/errors.ts +0 -17
- package/src/util.ts +0 -16
package/src/keychain.ts
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
/* eslint max-nested-callbacks: ["error", 5] */
|
|
2
2
|
|
|
3
3
|
import { pbkdf2, randomBytes } from '@libp2p/crypto'
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { peerIdFromKeys } from '@libp2p/peer-id'
|
|
4
|
+
import { privateKeyToProtobuf } from '@libp2p/crypto/keys'
|
|
5
|
+
import { InvalidParametersError, NotFoundError, serviceCapabilities } from '@libp2p/interface'
|
|
7
6
|
import { Key } from 'interface-datastore/key'
|
|
8
7
|
import mergeOptions from 'merge-options'
|
|
8
|
+
import { base58btc } from 'multiformats/bases/base58'
|
|
9
|
+
import { sha256 } from 'multiformats/hashes/sha2'
|
|
9
10
|
import sanitize from 'sanitize-filename'
|
|
10
11
|
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
|
|
11
12
|
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
|
|
12
|
-
import {
|
|
13
|
-
import
|
|
14
|
-
import type {
|
|
13
|
+
import { exportPrivateKey } from './utils/export.js'
|
|
14
|
+
import { importPrivateKey } from './utils/import.js'
|
|
15
|
+
import type { KeychainComponents, KeychainInit, Keychain as KeychainInterface, KeyInfo } from './index.js'
|
|
16
|
+
import type { Logger, PrivateKey } from '@libp2p/interface'
|
|
15
17
|
|
|
16
18
|
const keyPrefix = '/pkcs8/'
|
|
17
19
|
const infoPrefix = '/info/'
|
|
@@ -72,6 +74,13 @@ function DsInfoName (name: string): Key {
|
|
|
72
74
|
return new Key(infoPrefix + name)
|
|
73
75
|
}
|
|
74
76
|
|
|
77
|
+
export async function keyId (key: PrivateKey): Promise<string> {
|
|
78
|
+
const pb = privateKeyToProtobuf(key)
|
|
79
|
+
const hash = await sha256.digest(pb)
|
|
80
|
+
|
|
81
|
+
return base58btc.encode(hash.bytes).substring(1)
|
|
82
|
+
}
|
|
83
|
+
|
|
75
84
|
/**
|
|
76
85
|
* Manages the lifecycle of a key. Keys are encrypted at rest using PKCS #8.
|
|
77
86
|
*
|
|
@@ -80,7 +89,7 @@ function DsInfoName (name: string): Key {
|
|
|
80
89
|
* - '/pkcs8/*key-name*', contains the PKCS #8 for the key
|
|
81
90
|
*
|
|
82
91
|
*/
|
|
83
|
-
export class
|
|
92
|
+
export class Keychain implements KeychainInterface {
|
|
84
93
|
private readonly components: KeychainComponents
|
|
85
94
|
private readonly init: KeychainInit
|
|
86
95
|
private readonly log: Logger
|
|
@@ -147,151 +156,149 @@ export class DefaultKeychain implements Keychain {
|
|
|
147
156
|
return defaultOptions
|
|
148
157
|
}
|
|
149
158
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
*
|
|
153
|
-
* @param {string} name - The local key name; cannot already exist.
|
|
154
|
-
* @param {string} type - One of the key types; 'rsa'.
|
|
155
|
-
* @param {number} [size = 2048] - The key size in bits. Used for rsa keys only
|
|
156
|
-
*/
|
|
157
|
-
async createKey (name: string, type: KeyType, size = 2048): Promise<KeyInfo> {
|
|
158
|
-
if (!validateKeyName(name) || name === 'self') {
|
|
159
|
+
async findKeyByName (name: string): Promise<KeyInfo> {
|
|
160
|
+
if (!validateKeyName(name)) {
|
|
159
161
|
await randomDelay()
|
|
160
|
-
throw new
|
|
162
|
+
throw new InvalidParametersError(`Invalid key name '${name}'`)
|
|
161
163
|
}
|
|
162
164
|
|
|
163
|
-
|
|
164
|
-
await randomDelay()
|
|
165
|
-
throw new CodeError('Invalid key type', codes.ERR_INVALID_KEY_TYPE)
|
|
166
|
-
}
|
|
165
|
+
const dsname = DsInfoName(name)
|
|
167
166
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
167
|
+
try {
|
|
168
|
+
const res = await this.components.datastore.get(dsname)
|
|
169
|
+
return JSON.parse(uint8ArrayToString(res))
|
|
170
|
+
} catch (err: any) {
|
|
171
171
|
await randomDelay()
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
switch (type.toLowerCase()) {
|
|
176
|
-
case 'rsa':
|
|
177
|
-
if (!Number.isSafeInteger(size) || size < 2048) {
|
|
178
|
-
await randomDelay()
|
|
179
|
-
throw new CodeError('Invalid RSA key size', codes.ERR_INVALID_KEY_SIZE)
|
|
180
|
-
}
|
|
181
|
-
break
|
|
182
|
-
default:
|
|
183
|
-
break
|
|
172
|
+
this.log.error(err)
|
|
173
|
+
throw new NotFoundError(`Key '${name}' does not exist.`)
|
|
184
174
|
}
|
|
175
|
+
}
|
|
185
176
|
|
|
186
|
-
|
|
177
|
+
async findKeyById (id: string): Promise<KeyInfo> {
|
|
187
178
|
try {
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
const cached = privates.get(this)
|
|
191
|
-
|
|
192
|
-
if (cached == null) {
|
|
193
|
-
throw new CodeError('dek missing', codes.ERR_INVALID_PARAMETERS)
|
|
179
|
+
const query = {
|
|
180
|
+
prefix: infoPrefix
|
|
194
181
|
}
|
|
195
182
|
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
183
|
+
for await (const value of this.components.datastore.query(query)) {
|
|
184
|
+
const key = JSON.parse(uint8ArrayToString(value.value))
|
|
185
|
+
|
|
186
|
+
if (key.id === id) {
|
|
187
|
+
return key
|
|
188
|
+
}
|
|
201
189
|
}
|
|
202
|
-
const batch = this.components.datastore.batch()
|
|
203
|
-
batch.put(dsname, uint8ArrayFromString(pem))
|
|
204
|
-
batch.put(DsInfoName(name), uint8ArrayFromString(JSON.stringify(keyInfo)))
|
|
205
190
|
|
|
206
|
-
|
|
191
|
+
throw new InvalidParametersError(`Key with id '${id}' does not exist.`)
|
|
207
192
|
} catch (err: any) {
|
|
208
193
|
await randomDelay()
|
|
209
194
|
throw err
|
|
210
195
|
}
|
|
211
|
-
|
|
212
|
-
return keyInfo
|
|
213
196
|
}
|
|
214
197
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
*/
|
|
220
|
-
async listKeys (): Promise<KeyInfo[]> {
|
|
221
|
-
const query = {
|
|
222
|
-
prefix: infoPrefix
|
|
198
|
+
async importKey (name: string, key: PrivateKey): Promise<KeyInfo> {
|
|
199
|
+
if (!validateKeyName(name) || name === 'self') {
|
|
200
|
+
await randomDelay()
|
|
201
|
+
throw new InvalidParametersError(`Invalid key name '${name}'`)
|
|
223
202
|
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
203
|
+
if (key == null) {
|
|
204
|
+
await randomDelay()
|
|
205
|
+
throw new InvalidParametersError('Key is required')
|
|
206
|
+
}
|
|
207
|
+
const dsname = DsName(name)
|
|
208
|
+
const exists = await this.components.datastore.has(dsname)
|
|
209
|
+
if (exists) {
|
|
210
|
+
await randomDelay()
|
|
211
|
+
throw new InvalidParametersError(`Key '${name}' already exists`)
|
|
228
212
|
}
|
|
229
213
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* Find a key by it's id
|
|
235
|
-
*/
|
|
236
|
-
async findKeyById (id: string): Promise<KeyInfo> {
|
|
214
|
+
let kid: string
|
|
215
|
+
let pem: string
|
|
237
216
|
try {
|
|
238
|
-
|
|
239
|
-
const
|
|
217
|
+
kid = await keyId(key)
|
|
218
|
+
const cached = privates.get(this)
|
|
240
219
|
|
|
241
|
-
if (
|
|
242
|
-
throw new
|
|
220
|
+
if (cached == null) {
|
|
221
|
+
throw new InvalidParametersError('dek missing')
|
|
243
222
|
}
|
|
244
223
|
|
|
245
|
-
|
|
224
|
+
const dek = cached.dek
|
|
225
|
+
pem = await exportPrivateKey(key, dek, key.type === 'RSA' ? 'pkcs-8' : 'libp2p-key')
|
|
246
226
|
} catch (err: any) {
|
|
247
227
|
await randomDelay()
|
|
248
228
|
throw err
|
|
249
229
|
}
|
|
230
|
+
|
|
231
|
+
const keyInfo = {
|
|
232
|
+
name,
|
|
233
|
+
id: kid
|
|
234
|
+
}
|
|
235
|
+
const batch = this.components.datastore.batch()
|
|
236
|
+
batch.put(dsname, uint8ArrayFromString(pem))
|
|
237
|
+
batch.put(DsInfoName(name), uint8ArrayFromString(JSON.stringify(keyInfo)))
|
|
238
|
+
await batch.commit()
|
|
239
|
+
|
|
240
|
+
return keyInfo
|
|
250
241
|
}
|
|
251
242
|
|
|
252
|
-
|
|
253
|
-
* Find a key by it's name.
|
|
254
|
-
*
|
|
255
|
-
* @param {string} name - The local key name.
|
|
256
|
-
* @returns {Promise<KeyInfo>}
|
|
257
|
-
*/
|
|
258
|
-
async findKeyByName (name: string): Promise<KeyInfo> {
|
|
243
|
+
async exportKey (name: string): Promise<PrivateKey> {
|
|
259
244
|
if (!validateKeyName(name)) {
|
|
260
245
|
await randomDelay()
|
|
261
|
-
throw new
|
|
246
|
+
throw new InvalidParametersError(`Invalid key name '${name}'`)
|
|
262
247
|
}
|
|
263
248
|
|
|
264
|
-
const dsname =
|
|
249
|
+
const dsname = DsName(name)
|
|
265
250
|
try {
|
|
266
251
|
const res = await this.components.datastore.get(dsname)
|
|
267
|
-
|
|
252
|
+
const pem = uint8ArrayToString(res)
|
|
253
|
+
const cached = privates.get(this)
|
|
254
|
+
|
|
255
|
+
if (cached == null) {
|
|
256
|
+
throw new InvalidParametersError('dek missing')
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const dek = cached.dek
|
|
260
|
+
|
|
261
|
+
return await importPrivateKey(pem, dek)
|
|
268
262
|
} catch (err: any) {
|
|
269
263
|
await randomDelay()
|
|
270
|
-
|
|
271
|
-
throw new CodeError(`Key '${name}' does not exist.`, codes.ERR_KEY_NOT_FOUND)
|
|
264
|
+
throw err
|
|
272
265
|
}
|
|
273
266
|
}
|
|
274
267
|
|
|
275
|
-
/**
|
|
276
|
-
* Remove an existing key.
|
|
277
|
-
*
|
|
278
|
-
* @param {string} name - The local key name; must already exist.
|
|
279
|
-
* @returns {Promise<KeyInfo>}
|
|
280
|
-
*/
|
|
281
268
|
async removeKey (name: string): Promise<KeyInfo> {
|
|
282
269
|
if (!validateKeyName(name) || name === 'self') {
|
|
283
270
|
await randomDelay()
|
|
284
|
-
throw new
|
|
271
|
+
throw new InvalidParametersError(`Invalid key name '${name}'`)
|
|
285
272
|
}
|
|
273
|
+
|
|
286
274
|
const dsname = DsName(name)
|
|
287
275
|
const keyInfo = await this.findKeyByName(name)
|
|
288
276
|
const batch = this.components.datastore.batch()
|
|
289
277
|
batch.delete(dsname)
|
|
290
278
|
batch.delete(DsInfoName(name))
|
|
291
279
|
await batch.commit()
|
|
280
|
+
|
|
292
281
|
return keyInfo
|
|
293
282
|
}
|
|
294
283
|
|
|
284
|
+
/**
|
|
285
|
+
* List all the keys.
|
|
286
|
+
*
|
|
287
|
+
* @returns {Promise<KeyInfo[]>}
|
|
288
|
+
*/
|
|
289
|
+
async listKeys (): Promise<KeyInfo[]> {
|
|
290
|
+
const query = {
|
|
291
|
+
prefix: infoPrefix
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const info = []
|
|
295
|
+
for await (const value of this.components.datastore.query(query)) {
|
|
296
|
+
info.push(JSON.parse(uint8ArrayToString(value.value)))
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return info
|
|
300
|
+
}
|
|
301
|
+
|
|
295
302
|
/**
|
|
296
303
|
* Rename a key
|
|
297
304
|
*
|
|
@@ -302,11 +309,11 @@ export class DefaultKeychain implements Keychain {
|
|
|
302
309
|
async renameKey (oldName: string, newName: string): Promise<KeyInfo> {
|
|
303
310
|
if (!validateKeyName(oldName) || oldName === 'self') {
|
|
304
311
|
await randomDelay()
|
|
305
|
-
throw new
|
|
312
|
+
throw new InvalidParametersError(`Invalid old key name '${oldName}'`)
|
|
306
313
|
}
|
|
307
314
|
if (!validateKeyName(newName) || newName === 'self') {
|
|
308
315
|
await randomDelay()
|
|
309
|
-
throw new
|
|
316
|
+
throw new InvalidParametersError(`Invalid new key name '${newName}'`)
|
|
310
317
|
}
|
|
311
318
|
const oldDsname = DsName(oldName)
|
|
312
319
|
const newDsname = DsName(newName)
|
|
@@ -316,7 +323,7 @@ export class DefaultKeychain implements Keychain {
|
|
|
316
323
|
const exists = await this.components.datastore.has(newDsname)
|
|
317
324
|
if (exists) {
|
|
318
325
|
await randomDelay()
|
|
319
|
-
throw new
|
|
326
|
+
throw new InvalidParametersError(`Key '${newName}' already exists`)
|
|
320
327
|
}
|
|
321
328
|
|
|
322
329
|
try {
|
|
@@ -338,199 +345,27 @@ export class DefaultKeychain implements Keychain {
|
|
|
338
345
|
}
|
|
339
346
|
}
|
|
340
347
|
|
|
341
|
-
/**
|
|
342
|
-
* Export an existing key as a PEM encrypted PKCS #8 string
|
|
343
|
-
*/
|
|
344
|
-
async exportKey (name: string, password: string): Promise<string> {
|
|
345
|
-
if (!validateKeyName(name)) {
|
|
346
|
-
await randomDelay()
|
|
347
|
-
throw new CodeError(`Invalid key name '${name}'`, codes.ERR_INVALID_KEY_NAME)
|
|
348
|
-
}
|
|
349
|
-
if (password == null) {
|
|
350
|
-
await randomDelay()
|
|
351
|
-
throw new CodeError('Password is required', codes.ERR_PASSWORD_REQUIRED)
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
const dsname = DsName(name)
|
|
355
|
-
try {
|
|
356
|
-
const res = await this.components.datastore.get(dsname)
|
|
357
|
-
const pem = uint8ArrayToString(res)
|
|
358
|
-
const cached = privates.get(this)
|
|
359
|
-
|
|
360
|
-
if (cached == null) {
|
|
361
|
-
throw new CodeError('dek missing', codes.ERR_INVALID_PARAMETERS)
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
const dek = cached.dek
|
|
365
|
-
const privateKey = await importKey(pem, dek)
|
|
366
|
-
const keyString = await privateKey.export(password)
|
|
367
|
-
|
|
368
|
-
return keyString
|
|
369
|
-
} catch (err: any) {
|
|
370
|
-
await randomDelay()
|
|
371
|
-
throw err
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
/**
|
|
376
|
-
* Export an existing key as a PeerId
|
|
377
|
-
*/
|
|
378
|
-
async exportPeerId (name: string): Promise<PeerId> {
|
|
379
|
-
const password = 'temporary-password'
|
|
380
|
-
const pem = await this.exportKey(name, password)
|
|
381
|
-
const privateKey = await importKey(pem, password)
|
|
382
|
-
|
|
383
|
-
return peerIdFromKeys(privateKey.public.bytes, privateKey.bytes)
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
/**
|
|
387
|
-
* Import a new key from a PEM encoded PKCS #8 string
|
|
388
|
-
*
|
|
389
|
-
* @param {string} name - The local key name; must not already exist.
|
|
390
|
-
* @param {string} pem - The PEM encoded PKCS #8 string
|
|
391
|
-
* @param {string} password - The password.
|
|
392
|
-
* @returns {Promise<KeyInfo>}
|
|
393
|
-
*/
|
|
394
|
-
async importKey (name: string, pem: string, password: string): Promise<KeyInfo> {
|
|
395
|
-
if (!validateKeyName(name) || name === 'self') {
|
|
396
|
-
await randomDelay()
|
|
397
|
-
throw new CodeError(`Invalid key name '${name}'`, codes.ERR_INVALID_KEY_NAME)
|
|
398
|
-
}
|
|
399
|
-
if (pem == null) {
|
|
400
|
-
await randomDelay()
|
|
401
|
-
throw new CodeError('PEM encoded key is required', codes.ERR_PEM_REQUIRED)
|
|
402
|
-
}
|
|
403
|
-
const dsname = DsName(name)
|
|
404
|
-
const exists = await this.components.datastore.has(dsname)
|
|
405
|
-
if (exists) {
|
|
406
|
-
await randomDelay()
|
|
407
|
-
throw new CodeError(`Key '${name}' already exists`, codes.ERR_KEY_ALREADY_EXISTS)
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
let privateKey
|
|
411
|
-
try {
|
|
412
|
-
privateKey = await importKey(pem, password)
|
|
413
|
-
} catch (err: any) {
|
|
414
|
-
await randomDelay()
|
|
415
|
-
throw new CodeError('Cannot read the key, most likely the password is wrong', codes.ERR_CANNOT_READ_KEY)
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
let kid
|
|
419
|
-
try {
|
|
420
|
-
kid = await privateKey.id()
|
|
421
|
-
const cached = privates.get(this)
|
|
422
|
-
|
|
423
|
-
if (cached == null) {
|
|
424
|
-
throw new CodeError('dek missing', codes.ERR_INVALID_PARAMETERS)
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
const dek = cached.dek
|
|
428
|
-
pem = await privateKey.export(dek)
|
|
429
|
-
} catch (err: any) {
|
|
430
|
-
await randomDelay()
|
|
431
|
-
throw err
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
const keyInfo = {
|
|
435
|
-
name,
|
|
436
|
-
id: kid
|
|
437
|
-
}
|
|
438
|
-
const batch = this.components.datastore.batch()
|
|
439
|
-
batch.put(dsname, uint8ArrayFromString(pem))
|
|
440
|
-
batch.put(DsInfoName(name), uint8ArrayFromString(JSON.stringify(keyInfo)))
|
|
441
|
-
await batch.commit()
|
|
442
|
-
|
|
443
|
-
return keyInfo
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
/**
|
|
447
|
-
* Import a peer key
|
|
448
|
-
*/
|
|
449
|
-
async importPeer (name: string, peer: PeerId): Promise<KeyInfo> {
|
|
450
|
-
try {
|
|
451
|
-
if (!validateKeyName(name)) {
|
|
452
|
-
throw new CodeError(`Invalid key name '${name}'`, codes.ERR_INVALID_KEY_NAME)
|
|
453
|
-
}
|
|
454
|
-
if (peer == null) {
|
|
455
|
-
throw new CodeError('PeerId is required', codes.ERR_MISSING_PRIVATE_KEY)
|
|
456
|
-
}
|
|
457
|
-
if (peer.privateKey == null) {
|
|
458
|
-
throw new CodeError('PeerId.privKey is required', codes.ERR_MISSING_PRIVATE_KEY)
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
const privateKey = await unmarshalPrivateKey(peer.privateKey)
|
|
462
|
-
|
|
463
|
-
const dsname = DsName(name)
|
|
464
|
-
const exists = await this.components.datastore.has(dsname)
|
|
465
|
-
if (exists) {
|
|
466
|
-
await randomDelay()
|
|
467
|
-
throw new CodeError(`Key '${name}' already exists`, codes.ERR_KEY_ALREADY_EXISTS)
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
const cached = privates.get(this)
|
|
471
|
-
|
|
472
|
-
if (cached == null) {
|
|
473
|
-
throw new CodeError('dek missing', codes.ERR_INVALID_PARAMETERS)
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
const dek = cached.dek
|
|
477
|
-
const pem = await privateKey.export(dek)
|
|
478
|
-
const keyInfo: KeyInfo = {
|
|
479
|
-
name,
|
|
480
|
-
id: peer.toString()
|
|
481
|
-
}
|
|
482
|
-
const batch = this.components.datastore.batch()
|
|
483
|
-
batch.put(dsname, uint8ArrayFromString(pem))
|
|
484
|
-
batch.put(DsInfoName(name), uint8ArrayFromString(JSON.stringify(keyInfo)))
|
|
485
|
-
await batch.commit()
|
|
486
|
-
return keyInfo
|
|
487
|
-
} catch (err: any) {
|
|
488
|
-
await randomDelay()
|
|
489
|
-
throw err
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
/**
|
|
494
|
-
* Gets the private key as PEM encoded PKCS #8 string
|
|
495
|
-
*/
|
|
496
|
-
async getPrivateKey (name: string): Promise<string> {
|
|
497
|
-
if (!validateKeyName(name)) {
|
|
498
|
-
await randomDelay()
|
|
499
|
-
throw new CodeError(`Invalid key name '${name}'`, codes.ERR_INVALID_KEY_NAME)
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
try {
|
|
503
|
-
const dsname = DsName(name)
|
|
504
|
-
const res = await this.components.datastore.get(dsname)
|
|
505
|
-
return uint8ArrayToString(res)
|
|
506
|
-
} catch (err: any) {
|
|
507
|
-
await randomDelay()
|
|
508
|
-
this.log.error(err)
|
|
509
|
-
throw new CodeError(`Key '${name}' does not exist.`, codes.ERR_KEY_NOT_FOUND)
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
|
|
513
348
|
/**
|
|
514
349
|
* Rotate keychain password and re-encrypt all associated keys
|
|
515
350
|
*/
|
|
516
351
|
async rotateKeychainPass (oldPass: string, newPass: string): Promise<void> {
|
|
517
352
|
if (typeof oldPass !== 'string') {
|
|
518
353
|
await randomDelay()
|
|
519
|
-
throw new
|
|
354
|
+
throw new InvalidParametersError(`Invalid old pass type '${typeof oldPass}'`)
|
|
520
355
|
}
|
|
521
356
|
if (typeof newPass !== 'string') {
|
|
522
357
|
await randomDelay()
|
|
523
|
-
throw new
|
|
358
|
+
throw new InvalidParametersError(`Invalid new pass type '${typeof newPass}'`)
|
|
524
359
|
}
|
|
525
360
|
if (newPass.length < 20) {
|
|
526
361
|
await randomDelay()
|
|
527
|
-
throw new
|
|
362
|
+
throw new InvalidParametersError(`Invalid pass length ${newPass.length}`)
|
|
528
363
|
}
|
|
529
364
|
this.log('recreating keychain')
|
|
530
365
|
const cached = privates.get(this)
|
|
531
366
|
|
|
532
367
|
if (cached == null) {
|
|
533
|
-
throw new
|
|
368
|
+
throw new InvalidParametersError('dek missing')
|
|
534
369
|
}
|
|
535
370
|
|
|
536
371
|
const oldDek = cached.dek
|
|
@@ -548,9 +383,9 @@ export class DefaultKeychain implements Keychain {
|
|
|
548
383
|
for (const key of keys) {
|
|
549
384
|
const res = await this.components.datastore.get(DsName(key.name))
|
|
550
385
|
const pem = uint8ArrayToString(res)
|
|
551
|
-
const privateKey = await
|
|
386
|
+
const privateKey = await importPrivateKey(pem, oldDek)
|
|
552
387
|
const password = newDek.toString()
|
|
553
|
-
const keyAsPEM = await privateKey.
|
|
388
|
+
const keyAsPEM = await exportPrivateKey(privateKey, password, privateKey.type === 'RSA' ? 'pkcs-8' : 'libp2p-key')
|
|
554
389
|
|
|
555
390
|
// Update stored key
|
|
556
391
|
const batch = this.components.datastore.batch()
|