@nice-code/util 0.25.0 → 0.26.0
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/build/index.cjs +111 -5
- package/build/index.cjs.map +1 -1
- package/build/index.d.cts +106 -8
- package/build/index.d.mts +106 -8
- package/build/index.mjs +111 -6
- package/build/index.mjs.map +1 -1
- package/package.json +1 -1
package/build/index.d.cts
CHANGED
|
@@ -230,8 +230,34 @@ declare class StorageAdapter {
|
|
|
230
230
|
}
|
|
231
231
|
//#endregion
|
|
232
232
|
//#region src/crypto/client_key_link/ClientCryptoKeyLink.d.ts
|
|
233
|
+
/**
|
|
234
|
+
* How the local identity (the exchange + verify key pairs) is allowed to come into being:
|
|
235
|
+
*
|
|
236
|
+
* - `"lazy"` (default): generate + persist the identity on first use. Correct for frontend / ephemeral
|
|
237
|
+
* peers, and safe on a *strongly-consistent* store (a read miss genuinely means "never written").
|
|
238
|
+
* - `"required"`: never auto-mint. `ensureLocal*` throws {@link IdentityNotProvisionedError} if no
|
|
239
|
+
* identity was loaded from storage; the only way to mint is the explicit one-shot
|
|
240
|
+
* {@link ClientCryptoKeyLink.provisionIdentity}. Use this for a shared backend identity that must
|
|
241
|
+
* stay stable across isolates: on an *eventually-consistent* store (e.g. Cloudflare KV) a transient
|
|
242
|
+
* read miss must NOT be allowed to silently fork a second identity — that would change the server's
|
|
243
|
+
* verify key and get it permanently rejected by every trust-on-first-use-pinned client.
|
|
244
|
+
*/
|
|
245
|
+
type TIdentityMode = "lazy" | "required";
|
|
246
|
+
/**
|
|
247
|
+
* Thrown by the local-key getters when the link is in {@link TIdentityMode} `"required"` and no
|
|
248
|
+
* identity has been provisioned. Surfacing this (instead of silently minting a fresh identity on a
|
|
249
|
+
* storage miss) is what keeps a shared backend identity from forking across isolates.
|
|
250
|
+
*/
|
|
251
|
+
declare class IdentityNotProvisionedError extends Error {
|
|
252
|
+
constructor(message?: string);
|
|
253
|
+
}
|
|
233
254
|
interface IClientCryptoKeyLink_Constructor {
|
|
234
255
|
storageAdapter?: StorageAdapter;
|
|
256
|
+
/**
|
|
257
|
+
* Controls whether the local identity may be minted on first use. Defaults to `"lazy"` (unchanged
|
|
258
|
+
* behavior). Pass `"required"` for a stable shared backend identity — see {@link TIdentityMode}.
|
|
259
|
+
*/
|
|
260
|
+
identityMode?: TIdentityMode;
|
|
235
261
|
}
|
|
236
262
|
interface ILinkedClientPublicKeys {
|
|
237
263
|
verifyPublicKey?: TSerializedCryptoKeyData_Ed25519_Raw;
|
|
@@ -270,12 +296,14 @@ declare class ClientCryptoKeyLink {
|
|
|
270
296
|
private localVerifyKeyPair;
|
|
271
297
|
private linkedClientKeys;
|
|
272
298
|
private storage;
|
|
299
|
+
private readonly identityMode;
|
|
273
300
|
private initialized;
|
|
274
301
|
private initializePromise;
|
|
275
302
|
private localExchangeKeyPairPromise;
|
|
276
303
|
private localVerifyKeyPairPromise;
|
|
277
304
|
constructor({
|
|
278
|
-
storageAdapter
|
|
305
|
+
storageAdapter,
|
|
306
|
+
identityMode
|
|
279
307
|
}?: IClientCryptoKeyLink_Constructor);
|
|
280
308
|
/**
|
|
281
309
|
* Loads the local key pairs and any linked client public keys from storage (when a storage
|
|
@@ -285,6 +313,20 @@ declare class ClientCryptoKeyLink {
|
|
|
285
313
|
*/
|
|
286
314
|
initialize(): Promise<void>;
|
|
287
315
|
private runInitialize;
|
|
316
|
+
/**
|
|
317
|
+
* Provision the local identity once: load any persisted identity, then mint + persist the exchange
|
|
318
|
+
* and verify key pairs only if absent. Idempotent — a no-op when an identity already exists.
|
|
319
|
+
*
|
|
320
|
+
* This is the *only* way to mint when {@link TIdentityMode} is `"required"`. Run it from a single
|
|
321
|
+
* coordinated writer (a deploy hook, a first-boot path, a locked admin route) — **never on the hot
|
|
322
|
+
* request path** — because concurrent provisioning over an eventually-consistent store could read
|
|
323
|
+
* "absent" on two isolates at once and fork divergent identities. The serving path uses `"required"`,
|
|
324
|
+
* which never mints, so a transient storage miss fails that one request loudly instead of forking.
|
|
325
|
+
*
|
|
326
|
+
* A genuine storage *read error* propagates here (it is never coerced to "absent"), so an outage
|
|
327
|
+
* surfaces rather than silently minting a replacement identity.
|
|
328
|
+
*/
|
|
329
|
+
provisionIdentity(): Promise<void>;
|
|
288
330
|
/**
|
|
289
331
|
* Loads the local key pairs from storage if they were previously persisted. Does NOT generate
|
|
290
332
|
* fresh keys — local identity is created lazily on first use (see {@link ensureLocalExchangeKeyPair}
|
|
@@ -293,15 +335,29 @@ declare class ClientCryptoKeyLink {
|
|
|
293
335
|
*/
|
|
294
336
|
private loadStoredLocalKeys;
|
|
295
337
|
/**
|
|
296
|
-
* Returns the local exchange (X25519) key pair
|
|
297
|
-
*
|
|
338
|
+
* Returns the local exchange (X25519) key pair. In `"lazy"` mode it generates + persists it on first
|
|
339
|
+
* use; in `"required"` mode it throws {@link IdentityNotProvisionedError} when none was loaded (mint
|
|
340
|
+
* only via {@link provisionIdentity}). The read path of every encrypt/derive operation.
|
|
298
341
|
*/
|
|
299
342
|
private ensureLocalExchangeKeyPair;
|
|
300
343
|
/**
|
|
301
|
-
*
|
|
302
|
-
*
|
|
344
|
+
* Generates + persists the local exchange key pair if absent, returning an already-loaded pair
|
|
345
|
+
* untouched (so provisioning is idempotent). Bypasses the `"required"` guard by design — it is the
|
|
346
|
+
* sole minting path. Concurrent callers share a single generation.
|
|
347
|
+
*/
|
|
348
|
+
private mintLocalExchangeKeyPair;
|
|
349
|
+
/**
|
|
350
|
+
* Returns the local verify (Ed25519) key pair. In `"lazy"` mode it generates + persists it on first
|
|
351
|
+
* use; in `"required"` mode it throws {@link IdentityNotProvisionedError} when none was loaded (mint
|
|
352
|
+
* only via {@link provisionIdentity}). The read path of every sign operation.
|
|
303
353
|
*/
|
|
304
354
|
private ensureLocalVerifyKeyPair;
|
|
355
|
+
/**
|
|
356
|
+
* Generates + persists the local verify key pair if absent, returning an already-loaded pair
|
|
357
|
+
* untouched (so provisioning is idempotent). Bypasses the `"required"` guard by design — it is the
|
|
358
|
+
* sole minting path. Concurrent callers share a single generation.
|
|
359
|
+
*/
|
|
360
|
+
private mintLocalVerifyKeyPair;
|
|
305
361
|
private loadLinkedClients;
|
|
306
362
|
private serializeExchangeKeyPair;
|
|
307
363
|
private serializeVerifyKeyPair;
|
|
@@ -377,6 +433,48 @@ declare class ClientCryptoKeyLink {
|
|
|
377
433
|
reset(): Promise<void>;
|
|
378
434
|
private getLinkedClient;
|
|
379
435
|
private getAesGcmKeyForLinkedClient;
|
|
436
|
+
/**
|
|
437
|
+
* Derive the shared AES-GCM key from *explicit* key material + this link's local exchange private
|
|
438
|
+
* key, returning a standalone key that is **not** stored in (or read from) the per-`linkedClientId`
|
|
439
|
+
* cache. Use it to give a single session its own immutable key: two sessions that share one
|
|
440
|
+
* `linkedClientId` (e.g. a secure WebSocket and a secure HTTP exchange to the same peer) would
|
|
441
|
+
* otherwise clobber each other's cached shared key — the second handshake's re-link drops the
|
|
442
|
+
* first's key, so frames on the other transport fail to decrypt. Mirrors the derivation in
|
|
443
|
+
* {@link getAesGcmKeyForLinkedClient} exactly, so the resulting key matches the peer's.
|
|
444
|
+
*
|
|
445
|
+
* Requires the local identity to be loaded — call {@link initialize} first when resuming a link
|
|
446
|
+
* cold (the handshake paths have already initialized by the time they hold key material).
|
|
447
|
+
*/
|
|
448
|
+
deriveSharedAesGcmKey({
|
|
449
|
+
exchangePublicKey,
|
|
450
|
+
saltString,
|
|
451
|
+
infoString,
|
|
452
|
+
bindVerifyKeysIntoDerivation,
|
|
453
|
+
verifyPublicKey
|
|
454
|
+
}: {
|
|
455
|
+
exchangePublicKey: TSerializedCryptoKeyData_X25519_Raw;
|
|
456
|
+
saltString?: string;
|
|
457
|
+
infoString?: string;
|
|
458
|
+
bindVerifyKeysIntoDerivation?: boolean; /** The peer's verify public key — required when `bindVerifyKeysIntoDerivation` is true. */
|
|
459
|
+
verifyPublicKey?: TSerializedCryptoKeyData_Ed25519_Raw;
|
|
460
|
+
}): Promise<CryptoKey>;
|
|
461
|
+
/**
|
|
462
|
+
* Derive a symmetric AES-GCM key bound to **this link's own identity** — for sealing data the server
|
|
463
|
+
* hands a client and reads back later (e.g. a stateless secure-exchange session ticket), where only
|
|
464
|
+
* this server (any isolate that loaded the same persisted identity) can open it.
|
|
465
|
+
*
|
|
466
|
+
* Deterministic: an X25519 ECDH of the local exchange private key against its own public key, fed
|
|
467
|
+
* through the same vetted HKDF as peer key derivation, with a fixed versioned info label. So every
|
|
468
|
+
* isolate sharing the persisted identity derives the identical key, and no raw private-key bytes are
|
|
469
|
+
* exposed. The {@link version} is part of the label, so a future rotation can run two keys during a
|
|
470
|
+
* grace window (old tickets opened with `v1` while new ones issue under `v2`).
|
|
471
|
+
*
|
|
472
|
+
* Requires the local identity (call {@link initialize} / {@link provisionIdentity} first); in
|
|
473
|
+
* `"required"` mode an unprovisioned link throws {@link IdentityNotProvisionedError}.
|
|
474
|
+
*/
|
|
475
|
+
deriveLocalSealKey(options?: {
|
|
476
|
+
version?: string;
|
|
477
|
+
}): Promise<CryptoKey>;
|
|
380
478
|
encryptDataForLinkedClient({
|
|
381
479
|
dataToEncrypt,
|
|
382
480
|
linkedClientId
|
|
@@ -523,8 +621,8 @@ declare const importEd25519Key: {
|
|
|
523
621
|
readonly nonExtractable: (input: `ed25519::raw_base64::${string}` | `ed25519::jwk::${string}`) => Promise<CryptoKey>;
|
|
524
622
|
};
|
|
525
623
|
fromSerializedObject: {
|
|
526
|
-
readonly extractable: (input:
|
|
527
|
-
readonly nonExtractable: (input:
|
|
624
|
+
readonly extractable: (input: TSerializedCryptoKeyData_Ed25519_Raw_Transformed | TSerializedCryptoKeyData_Ed25519_Jwk_Transformed) => Promise<CryptoKey>;
|
|
625
|
+
readonly nonExtractable: (input: TSerializedCryptoKeyData_Ed25519_Raw_Transformed | TSerializedCryptoKeyData_Ed25519_Jwk_Transformed) => Promise<CryptoKey>;
|
|
528
626
|
};
|
|
529
627
|
fromJwk: {
|
|
530
628
|
readonly extractable: (input: JsonWebKey) => Promise<CryptoKey>;
|
|
@@ -767,5 +865,5 @@ declare function createMemoryStorageMethods_json(memoryStorageMap?: TMemoryStora
|
|
|
767
865
|
declare const createMemoryStorageAdapter_json: (options?: TCreateMemoryStorageOptions_json) => StorageAdapter;
|
|
768
866
|
declare function createTypedMemoryStorage_json<T extends Record<string, any>>(options?: TCreateMemoryStorageOptions_json): ITypedStorage<T>;
|
|
769
867
|
//#endregion
|
|
770
|
-
export { ClientCryptoKeyLink, DEFAULT_COMBINED_TEXT_DATA_SEPARATOR, ECryptoKeyAlgo, ECryptoKeyFormat, EStorageAdapterType, ICreateKVStorageMethodsOptions, ISerializedKeyData_Ed25519_Jwk, ISerializedKeyData_Ed25519_Raw, ISerializedKeyData_X25519_Jwk, ISerializedKeyData_X25519_Raw, IStorageAdapterConstructor, IStorageAdapterMethods_Json, IStorageAdapterMethods_String, IStorageKeyGetterAndSetter, ITypedStorage, StorageAdapter, StringKeys, TCreateDurableObjectStorageOptions, TCreateKVStorageOptions, TEncryptedAesGcmBytes, TEncryptedAesGcmPayload, TEncryptedAesGcmPayload_Transformed, TMemoryStorage_json, TMemoryStorage_string, TSerializedCryptoKeyData_Ed25519_Jwk, TSerializedCryptoKeyData_Ed25519_Jwk_Transformed, TSerializedCryptoKeyData_Ed25519_Raw, TSerializedCryptoKeyData_Ed25519_Raw_Transformed, TSerializedCryptoKeyData_X25519_Jwk, TSerializedCryptoKeyData_X25519_Jwk_Transformed, TSerializedCryptoKeyData_X25519_Raw, TSerializedCryptoKeyData_X25519_Raw_Transformed, TSerializedCryptoKeyPairDataEd25519, TSerializedCryptoKeyPairDataX25519, TSerializedKeyData, TStorageAdapterMethods, type TTypeAndId, TVerifyChallengeWithSignature_Input, TVerifyChallengeWithSignature_WithThrow_Input, buildVerifyKeyBoundInfoString, convertEd25519FormattedStringToObject, convertEd25519FormattedStringToSerializedKeyData, convertEd25519JwkDataStringToObject, convertEd25519JwkDataStringToSerializedKeyData, convertEd25519RawDataStringToObject, convertEd25519RawDataStringToSerializedKeyData, convertX25519FormattedStringToObject, convertX25519FormattedStringToSerializedKeyData, convertX25519JwkDataStringToObject, convertX25519JwkDataStringToSerializedKeyData, convertX25519RawDataStringToObject, convertX25519RawDataStringToSerializedKeyData, createAesGcmKeyFromX25519Keys, createDurableObjectStorageAdapter, createDurableObjectStorageMethods, createDurableObjectTypedStorage, createKVStorageAdapter, createKVStorageMethods, createKVTypedStorage, createMemoryStorageAdapter_json, createMemoryStorageAdapter_string, createMemoryStorageMethods_json, createMemoryStorageMethods_string, createSharedBitsFromX25519, createTypedMemoryStorage_json, createTypedMemoryStorage_string, createTypedStorage, createTypedWebLocalStorage, createTypedWebSessionStorage, createWebLocalStorageAdapter, createWebLocalStorageMethods, createWebSessionStorageAdapter, createWebSessionStorageMethods, decryptBytesWithAesGcmKey, decryptTextDataWithAesGcmKey, encryptBytesWithAesGcmKey, encryptTextDataWithAesGcmKey, generateEd25519KeyPair, generateX25519KeyPair, importEd25519Key, importX25519Key, serializeEd25519Key_Jwk, serializeEd25519Key_Raw, serializeX25519Key_Jwk, serializeX25519Key_Raw, signCombinedTextDataWithKeyEd25519, signTextDataWithKeyEd25519, vCryptoKeyPairDataEd25519, vCryptoKeyPairDataX25519, vEncryptedAesGcmPayload, vSerializedCryptoKeyDataEd25519_Jwk, vSerializedCryptoKeyDataEd25519_Raw, vSerializedCryptoKeyDataX25519_Jwk, vSerializedCryptoKeyDataX25519_Raw, vVerifyChallengeWithSignature_Input, vVerifyChallengeWithSignature_WithThrow_Input, verifyWithKeyEd25519 };
|
|
868
|
+
export { ClientCryptoKeyLink, DEFAULT_COMBINED_TEXT_DATA_SEPARATOR, ECryptoKeyAlgo, ECryptoKeyFormat, EStorageAdapterType, ICreateKVStorageMethodsOptions, ISerializedKeyData_Ed25519_Jwk, ISerializedKeyData_Ed25519_Raw, ISerializedKeyData_X25519_Jwk, ISerializedKeyData_X25519_Raw, IStorageAdapterConstructor, IStorageAdapterMethods_Json, IStorageAdapterMethods_String, IStorageKeyGetterAndSetter, ITypedStorage, IdentityNotProvisionedError, StorageAdapter, StringKeys, TCreateDurableObjectStorageOptions, TCreateKVStorageOptions, TEncryptedAesGcmBytes, TEncryptedAesGcmPayload, TEncryptedAesGcmPayload_Transformed, TIdentityMode, TMemoryStorage_json, TMemoryStorage_string, TSerializedCryptoKeyData_Ed25519_Jwk, TSerializedCryptoKeyData_Ed25519_Jwk_Transformed, TSerializedCryptoKeyData_Ed25519_Raw, TSerializedCryptoKeyData_Ed25519_Raw_Transformed, TSerializedCryptoKeyData_X25519_Jwk, TSerializedCryptoKeyData_X25519_Jwk_Transformed, TSerializedCryptoKeyData_X25519_Raw, TSerializedCryptoKeyData_X25519_Raw_Transformed, TSerializedCryptoKeyPairDataEd25519, TSerializedCryptoKeyPairDataX25519, TSerializedKeyData, TStorageAdapterMethods, type TTypeAndId, TVerifyChallengeWithSignature_Input, TVerifyChallengeWithSignature_WithThrow_Input, buildVerifyKeyBoundInfoString, convertEd25519FormattedStringToObject, convertEd25519FormattedStringToSerializedKeyData, convertEd25519JwkDataStringToObject, convertEd25519JwkDataStringToSerializedKeyData, convertEd25519RawDataStringToObject, convertEd25519RawDataStringToSerializedKeyData, convertX25519FormattedStringToObject, convertX25519FormattedStringToSerializedKeyData, convertX25519JwkDataStringToObject, convertX25519JwkDataStringToSerializedKeyData, convertX25519RawDataStringToObject, convertX25519RawDataStringToSerializedKeyData, createAesGcmKeyFromX25519Keys, createDurableObjectStorageAdapter, createDurableObjectStorageMethods, createDurableObjectTypedStorage, createKVStorageAdapter, createKVStorageMethods, createKVTypedStorage, createMemoryStorageAdapter_json, createMemoryStorageAdapter_string, createMemoryStorageMethods_json, createMemoryStorageMethods_string, createSharedBitsFromX25519, createTypedMemoryStorage_json, createTypedMemoryStorage_string, createTypedStorage, createTypedWebLocalStorage, createTypedWebSessionStorage, createWebLocalStorageAdapter, createWebLocalStorageMethods, createWebSessionStorageAdapter, createWebSessionStorageMethods, decryptBytesWithAesGcmKey, decryptTextDataWithAesGcmKey, encryptBytesWithAesGcmKey, encryptTextDataWithAesGcmKey, generateEd25519KeyPair, generateX25519KeyPair, importEd25519Key, importX25519Key, serializeEd25519Key_Jwk, serializeEd25519Key_Raw, serializeX25519Key_Jwk, serializeX25519Key_Raw, signCombinedTextDataWithKeyEd25519, signTextDataWithKeyEd25519, vCryptoKeyPairDataEd25519, vCryptoKeyPairDataX25519, vEncryptedAesGcmPayload, vSerializedCryptoKeyDataEd25519_Jwk, vSerializedCryptoKeyDataEd25519_Raw, vSerializedCryptoKeyDataX25519_Jwk, vSerializedCryptoKeyDataX25519_Raw, vVerifyChallengeWithSignature_Input, vVerifyChallengeWithSignature_WithThrow_Input, verifyWithKeyEd25519 };
|
|
771
869
|
//# sourceMappingURL=index.d.cts.map
|
package/build/index.d.mts
CHANGED
|
@@ -230,8 +230,34 @@ declare class StorageAdapter {
|
|
|
230
230
|
}
|
|
231
231
|
//#endregion
|
|
232
232
|
//#region src/crypto/client_key_link/ClientCryptoKeyLink.d.ts
|
|
233
|
+
/**
|
|
234
|
+
* How the local identity (the exchange + verify key pairs) is allowed to come into being:
|
|
235
|
+
*
|
|
236
|
+
* - `"lazy"` (default): generate + persist the identity on first use. Correct for frontend / ephemeral
|
|
237
|
+
* peers, and safe on a *strongly-consistent* store (a read miss genuinely means "never written").
|
|
238
|
+
* - `"required"`: never auto-mint. `ensureLocal*` throws {@link IdentityNotProvisionedError} if no
|
|
239
|
+
* identity was loaded from storage; the only way to mint is the explicit one-shot
|
|
240
|
+
* {@link ClientCryptoKeyLink.provisionIdentity}. Use this for a shared backend identity that must
|
|
241
|
+
* stay stable across isolates: on an *eventually-consistent* store (e.g. Cloudflare KV) a transient
|
|
242
|
+
* read miss must NOT be allowed to silently fork a second identity — that would change the server's
|
|
243
|
+
* verify key and get it permanently rejected by every trust-on-first-use-pinned client.
|
|
244
|
+
*/
|
|
245
|
+
type TIdentityMode = "lazy" | "required";
|
|
246
|
+
/**
|
|
247
|
+
* Thrown by the local-key getters when the link is in {@link TIdentityMode} `"required"` and no
|
|
248
|
+
* identity has been provisioned. Surfacing this (instead of silently minting a fresh identity on a
|
|
249
|
+
* storage miss) is what keeps a shared backend identity from forking across isolates.
|
|
250
|
+
*/
|
|
251
|
+
declare class IdentityNotProvisionedError extends Error {
|
|
252
|
+
constructor(message?: string);
|
|
253
|
+
}
|
|
233
254
|
interface IClientCryptoKeyLink_Constructor {
|
|
234
255
|
storageAdapter?: StorageAdapter;
|
|
256
|
+
/**
|
|
257
|
+
* Controls whether the local identity may be minted on first use. Defaults to `"lazy"` (unchanged
|
|
258
|
+
* behavior). Pass `"required"` for a stable shared backend identity — see {@link TIdentityMode}.
|
|
259
|
+
*/
|
|
260
|
+
identityMode?: TIdentityMode;
|
|
235
261
|
}
|
|
236
262
|
interface ILinkedClientPublicKeys {
|
|
237
263
|
verifyPublicKey?: TSerializedCryptoKeyData_Ed25519_Raw;
|
|
@@ -270,12 +296,14 @@ declare class ClientCryptoKeyLink {
|
|
|
270
296
|
private localVerifyKeyPair;
|
|
271
297
|
private linkedClientKeys;
|
|
272
298
|
private storage;
|
|
299
|
+
private readonly identityMode;
|
|
273
300
|
private initialized;
|
|
274
301
|
private initializePromise;
|
|
275
302
|
private localExchangeKeyPairPromise;
|
|
276
303
|
private localVerifyKeyPairPromise;
|
|
277
304
|
constructor({
|
|
278
|
-
storageAdapter
|
|
305
|
+
storageAdapter,
|
|
306
|
+
identityMode
|
|
279
307
|
}?: IClientCryptoKeyLink_Constructor);
|
|
280
308
|
/**
|
|
281
309
|
* Loads the local key pairs and any linked client public keys from storage (when a storage
|
|
@@ -285,6 +313,20 @@ declare class ClientCryptoKeyLink {
|
|
|
285
313
|
*/
|
|
286
314
|
initialize(): Promise<void>;
|
|
287
315
|
private runInitialize;
|
|
316
|
+
/**
|
|
317
|
+
* Provision the local identity once: load any persisted identity, then mint + persist the exchange
|
|
318
|
+
* and verify key pairs only if absent. Idempotent — a no-op when an identity already exists.
|
|
319
|
+
*
|
|
320
|
+
* This is the *only* way to mint when {@link TIdentityMode} is `"required"`. Run it from a single
|
|
321
|
+
* coordinated writer (a deploy hook, a first-boot path, a locked admin route) — **never on the hot
|
|
322
|
+
* request path** — because concurrent provisioning over an eventually-consistent store could read
|
|
323
|
+
* "absent" on two isolates at once and fork divergent identities. The serving path uses `"required"`,
|
|
324
|
+
* which never mints, so a transient storage miss fails that one request loudly instead of forking.
|
|
325
|
+
*
|
|
326
|
+
* A genuine storage *read error* propagates here (it is never coerced to "absent"), so an outage
|
|
327
|
+
* surfaces rather than silently minting a replacement identity.
|
|
328
|
+
*/
|
|
329
|
+
provisionIdentity(): Promise<void>;
|
|
288
330
|
/**
|
|
289
331
|
* Loads the local key pairs from storage if they were previously persisted. Does NOT generate
|
|
290
332
|
* fresh keys — local identity is created lazily on first use (see {@link ensureLocalExchangeKeyPair}
|
|
@@ -293,15 +335,29 @@ declare class ClientCryptoKeyLink {
|
|
|
293
335
|
*/
|
|
294
336
|
private loadStoredLocalKeys;
|
|
295
337
|
/**
|
|
296
|
-
* Returns the local exchange (X25519) key pair
|
|
297
|
-
*
|
|
338
|
+
* Returns the local exchange (X25519) key pair. In `"lazy"` mode it generates + persists it on first
|
|
339
|
+
* use; in `"required"` mode it throws {@link IdentityNotProvisionedError} when none was loaded (mint
|
|
340
|
+
* only via {@link provisionIdentity}). The read path of every encrypt/derive operation.
|
|
298
341
|
*/
|
|
299
342
|
private ensureLocalExchangeKeyPair;
|
|
300
343
|
/**
|
|
301
|
-
*
|
|
302
|
-
*
|
|
344
|
+
* Generates + persists the local exchange key pair if absent, returning an already-loaded pair
|
|
345
|
+
* untouched (so provisioning is idempotent). Bypasses the `"required"` guard by design — it is the
|
|
346
|
+
* sole minting path. Concurrent callers share a single generation.
|
|
347
|
+
*/
|
|
348
|
+
private mintLocalExchangeKeyPair;
|
|
349
|
+
/**
|
|
350
|
+
* Returns the local verify (Ed25519) key pair. In `"lazy"` mode it generates + persists it on first
|
|
351
|
+
* use; in `"required"` mode it throws {@link IdentityNotProvisionedError} when none was loaded (mint
|
|
352
|
+
* only via {@link provisionIdentity}). The read path of every sign operation.
|
|
303
353
|
*/
|
|
304
354
|
private ensureLocalVerifyKeyPair;
|
|
355
|
+
/**
|
|
356
|
+
* Generates + persists the local verify key pair if absent, returning an already-loaded pair
|
|
357
|
+
* untouched (so provisioning is idempotent). Bypasses the `"required"` guard by design — it is the
|
|
358
|
+
* sole minting path. Concurrent callers share a single generation.
|
|
359
|
+
*/
|
|
360
|
+
private mintLocalVerifyKeyPair;
|
|
305
361
|
private loadLinkedClients;
|
|
306
362
|
private serializeExchangeKeyPair;
|
|
307
363
|
private serializeVerifyKeyPair;
|
|
@@ -377,6 +433,48 @@ declare class ClientCryptoKeyLink {
|
|
|
377
433
|
reset(): Promise<void>;
|
|
378
434
|
private getLinkedClient;
|
|
379
435
|
private getAesGcmKeyForLinkedClient;
|
|
436
|
+
/**
|
|
437
|
+
* Derive the shared AES-GCM key from *explicit* key material + this link's local exchange private
|
|
438
|
+
* key, returning a standalone key that is **not** stored in (or read from) the per-`linkedClientId`
|
|
439
|
+
* cache. Use it to give a single session its own immutable key: two sessions that share one
|
|
440
|
+
* `linkedClientId` (e.g. a secure WebSocket and a secure HTTP exchange to the same peer) would
|
|
441
|
+
* otherwise clobber each other's cached shared key — the second handshake's re-link drops the
|
|
442
|
+
* first's key, so frames on the other transport fail to decrypt. Mirrors the derivation in
|
|
443
|
+
* {@link getAesGcmKeyForLinkedClient} exactly, so the resulting key matches the peer's.
|
|
444
|
+
*
|
|
445
|
+
* Requires the local identity to be loaded — call {@link initialize} first when resuming a link
|
|
446
|
+
* cold (the handshake paths have already initialized by the time they hold key material).
|
|
447
|
+
*/
|
|
448
|
+
deriveSharedAesGcmKey({
|
|
449
|
+
exchangePublicKey,
|
|
450
|
+
saltString,
|
|
451
|
+
infoString,
|
|
452
|
+
bindVerifyKeysIntoDerivation,
|
|
453
|
+
verifyPublicKey
|
|
454
|
+
}: {
|
|
455
|
+
exchangePublicKey: TSerializedCryptoKeyData_X25519_Raw;
|
|
456
|
+
saltString?: string;
|
|
457
|
+
infoString?: string;
|
|
458
|
+
bindVerifyKeysIntoDerivation?: boolean; /** The peer's verify public key — required when `bindVerifyKeysIntoDerivation` is true. */
|
|
459
|
+
verifyPublicKey?: TSerializedCryptoKeyData_Ed25519_Raw;
|
|
460
|
+
}): Promise<CryptoKey>;
|
|
461
|
+
/**
|
|
462
|
+
* Derive a symmetric AES-GCM key bound to **this link's own identity** — for sealing data the server
|
|
463
|
+
* hands a client and reads back later (e.g. a stateless secure-exchange session ticket), where only
|
|
464
|
+
* this server (any isolate that loaded the same persisted identity) can open it.
|
|
465
|
+
*
|
|
466
|
+
* Deterministic: an X25519 ECDH of the local exchange private key against its own public key, fed
|
|
467
|
+
* through the same vetted HKDF as peer key derivation, with a fixed versioned info label. So every
|
|
468
|
+
* isolate sharing the persisted identity derives the identical key, and no raw private-key bytes are
|
|
469
|
+
* exposed. The {@link version} is part of the label, so a future rotation can run two keys during a
|
|
470
|
+
* grace window (old tickets opened with `v1` while new ones issue under `v2`).
|
|
471
|
+
*
|
|
472
|
+
* Requires the local identity (call {@link initialize} / {@link provisionIdentity} first); in
|
|
473
|
+
* `"required"` mode an unprovisioned link throws {@link IdentityNotProvisionedError}.
|
|
474
|
+
*/
|
|
475
|
+
deriveLocalSealKey(options?: {
|
|
476
|
+
version?: string;
|
|
477
|
+
}): Promise<CryptoKey>;
|
|
380
478
|
encryptDataForLinkedClient({
|
|
381
479
|
dataToEncrypt,
|
|
382
480
|
linkedClientId
|
|
@@ -523,8 +621,8 @@ declare const importEd25519Key: {
|
|
|
523
621
|
readonly nonExtractable: (input: `ed25519::raw_base64::${string}` | `ed25519::jwk::${string}`) => Promise<CryptoKey>;
|
|
524
622
|
};
|
|
525
623
|
fromSerializedObject: {
|
|
526
|
-
readonly extractable: (input:
|
|
527
|
-
readonly nonExtractable: (input:
|
|
624
|
+
readonly extractable: (input: TSerializedCryptoKeyData_Ed25519_Raw_Transformed | TSerializedCryptoKeyData_Ed25519_Jwk_Transformed) => Promise<CryptoKey>;
|
|
625
|
+
readonly nonExtractable: (input: TSerializedCryptoKeyData_Ed25519_Raw_Transformed | TSerializedCryptoKeyData_Ed25519_Jwk_Transformed) => Promise<CryptoKey>;
|
|
528
626
|
};
|
|
529
627
|
fromJwk: {
|
|
530
628
|
readonly extractable: (input: JsonWebKey) => Promise<CryptoKey>;
|
|
@@ -767,5 +865,5 @@ declare function createMemoryStorageMethods_json(memoryStorageMap?: TMemoryStora
|
|
|
767
865
|
declare const createMemoryStorageAdapter_json: (options?: TCreateMemoryStorageOptions_json) => StorageAdapter;
|
|
768
866
|
declare function createTypedMemoryStorage_json<T extends Record<string, any>>(options?: TCreateMemoryStorageOptions_json): ITypedStorage<T>;
|
|
769
867
|
//#endregion
|
|
770
|
-
export { ClientCryptoKeyLink, DEFAULT_COMBINED_TEXT_DATA_SEPARATOR, ECryptoKeyAlgo, ECryptoKeyFormat, EStorageAdapterType, ICreateKVStorageMethodsOptions, ISerializedKeyData_Ed25519_Jwk, ISerializedKeyData_Ed25519_Raw, ISerializedKeyData_X25519_Jwk, ISerializedKeyData_X25519_Raw, IStorageAdapterConstructor, IStorageAdapterMethods_Json, IStorageAdapterMethods_String, IStorageKeyGetterAndSetter, ITypedStorage, StorageAdapter, StringKeys, TCreateDurableObjectStorageOptions, TCreateKVStorageOptions, TEncryptedAesGcmBytes, TEncryptedAesGcmPayload, TEncryptedAesGcmPayload_Transformed, TMemoryStorage_json, TMemoryStorage_string, TSerializedCryptoKeyData_Ed25519_Jwk, TSerializedCryptoKeyData_Ed25519_Jwk_Transformed, TSerializedCryptoKeyData_Ed25519_Raw, TSerializedCryptoKeyData_Ed25519_Raw_Transformed, TSerializedCryptoKeyData_X25519_Jwk, TSerializedCryptoKeyData_X25519_Jwk_Transformed, TSerializedCryptoKeyData_X25519_Raw, TSerializedCryptoKeyData_X25519_Raw_Transformed, TSerializedCryptoKeyPairDataEd25519, TSerializedCryptoKeyPairDataX25519, TSerializedKeyData, TStorageAdapterMethods, type TTypeAndId, TVerifyChallengeWithSignature_Input, TVerifyChallengeWithSignature_WithThrow_Input, buildVerifyKeyBoundInfoString, convertEd25519FormattedStringToObject, convertEd25519FormattedStringToSerializedKeyData, convertEd25519JwkDataStringToObject, convertEd25519JwkDataStringToSerializedKeyData, convertEd25519RawDataStringToObject, convertEd25519RawDataStringToSerializedKeyData, convertX25519FormattedStringToObject, convertX25519FormattedStringToSerializedKeyData, convertX25519JwkDataStringToObject, convertX25519JwkDataStringToSerializedKeyData, convertX25519RawDataStringToObject, convertX25519RawDataStringToSerializedKeyData, createAesGcmKeyFromX25519Keys, createDurableObjectStorageAdapter, createDurableObjectStorageMethods, createDurableObjectTypedStorage, createKVStorageAdapter, createKVStorageMethods, createKVTypedStorage, createMemoryStorageAdapter_json, createMemoryStorageAdapter_string, createMemoryStorageMethods_json, createMemoryStorageMethods_string, createSharedBitsFromX25519, createTypedMemoryStorage_json, createTypedMemoryStorage_string, createTypedStorage, createTypedWebLocalStorage, createTypedWebSessionStorage, createWebLocalStorageAdapter, createWebLocalStorageMethods, createWebSessionStorageAdapter, createWebSessionStorageMethods, decryptBytesWithAesGcmKey, decryptTextDataWithAesGcmKey, encryptBytesWithAesGcmKey, encryptTextDataWithAesGcmKey, generateEd25519KeyPair, generateX25519KeyPair, importEd25519Key, importX25519Key, serializeEd25519Key_Jwk, serializeEd25519Key_Raw, serializeX25519Key_Jwk, serializeX25519Key_Raw, signCombinedTextDataWithKeyEd25519, signTextDataWithKeyEd25519, vCryptoKeyPairDataEd25519, vCryptoKeyPairDataX25519, vEncryptedAesGcmPayload, vSerializedCryptoKeyDataEd25519_Jwk, vSerializedCryptoKeyDataEd25519_Raw, vSerializedCryptoKeyDataX25519_Jwk, vSerializedCryptoKeyDataX25519_Raw, vVerifyChallengeWithSignature_Input, vVerifyChallengeWithSignature_WithThrow_Input, verifyWithKeyEd25519 };
|
|
868
|
+
export { ClientCryptoKeyLink, DEFAULT_COMBINED_TEXT_DATA_SEPARATOR, ECryptoKeyAlgo, ECryptoKeyFormat, EStorageAdapterType, ICreateKVStorageMethodsOptions, ISerializedKeyData_Ed25519_Jwk, ISerializedKeyData_Ed25519_Raw, ISerializedKeyData_X25519_Jwk, ISerializedKeyData_X25519_Raw, IStorageAdapterConstructor, IStorageAdapterMethods_Json, IStorageAdapterMethods_String, IStorageKeyGetterAndSetter, ITypedStorage, IdentityNotProvisionedError, StorageAdapter, StringKeys, TCreateDurableObjectStorageOptions, TCreateKVStorageOptions, TEncryptedAesGcmBytes, TEncryptedAesGcmPayload, TEncryptedAesGcmPayload_Transformed, TIdentityMode, TMemoryStorage_json, TMemoryStorage_string, TSerializedCryptoKeyData_Ed25519_Jwk, TSerializedCryptoKeyData_Ed25519_Jwk_Transformed, TSerializedCryptoKeyData_Ed25519_Raw, TSerializedCryptoKeyData_Ed25519_Raw_Transformed, TSerializedCryptoKeyData_X25519_Jwk, TSerializedCryptoKeyData_X25519_Jwk_Transformed, TSerializedCryptoKeyData_X25519_Raw, TSerializedCryptoKeyData_X25519_Raw_Transformed, TSerializedCryptoKeyPairDataEd25519, TSerializedCryptoKeyPairDataX25519, TSerializedKeyData, TStorageAdapterMethods, type TTypeAndId, TVerifyChallengeWithSignature_Input, TVerifyChallengeWithSignature_WithThrow_Input, buildVerifyKeyBoundInfoString, convertEd25519FormattedStringToObject, convertEd25519FormattedStringToSerializedKeyData, convertEd25519JwkDataStringToObject, convertEd25519JwkDataStringToSerializedKeyData, convertEd25519RawDataStringToObject, convertEd25519RawDataStringToSerializedKeyData, convertX25519FormattedStringToObject, convertX25519FormattedStringToSerializedKeyData, convertX25519JwkDataStringToObject, convertX25519JwkDataStringToSerializedKeyData, convertX25519RawDataStringToObject, convertX25519RawDataStringToSerializedKeyData, createAesGcmKeyFromX25519Keys, createDurableObjectStorageAdapter, createDurableObjectStorageMethods, createDurableObjectTypedStorage, createKVStorageAdapter, createKVStorageMethods, createKVTypedStorage, createMemoryStorageAdapter_json, createMemoryStorageAdapter_string, createMemoryStorageMethods_json, createMemoryStorageMethods_string, createSharedBitsFromX25519, createTypedMemoryStorage_json, createTypedMemoryStorage_string, createTypedStorage, createTypedWebLocalStorage, createTypedWebSessionStorage, createWebLocalStorageAdapter, createWebLocalStorageMethods, createWebSessionStorageAdapter, createWebSessionStorageMethods, decryptBytesWithAesGcmKey, decryptTextDataWithAesGcmKey, encryptBytesWithAesGcmKey, encryptTextDataWithAesGcmKey, generateEd25519KeyPair, generateX25519KeyPair, importEd25519Key, importX25519Key, serializeEd25519Key_Jwk, serializeEd25519Key_Raw, serializeX25519Key_Jwk, serializeX25519Key_Raw, signCombinedTextDataWithKeyEd25519, signTextDataWithKeyEd25519, vCryptoKeyPairDataEd25519, vCryptoKeyPairDataX25519, vEncryptedAesGcmPayload, vSerializedCryptoKeyDataEd25519_Jwk, vSerializedCryptoKeyDataEd25519_Raw, vSerializedCryptoKeyDataX25519_Jwk, vSerializedCryptoKeyDataX25519_Raw, vVerifyChallengeWithSignature_Input, vVerifyChallengeWithSignature_WithThrow_Input, verifyWithKeyEd25519 };
|
|
771
869
|
//# sourceMappingURL=index.d.mts.map
|
package/build/index.mjs
CHANGED
|
@@ -428,17 +428,30 @@ const serializeX25519Key_Raw = async (key) => {
|
|
|
428
428
|
};
|
|
429
429
|
//#endregion
|
|
430
430
|
//#region src/crypto/client_key_link/ClientCryptoKeyLink.ts
|
|
431
|
+
/**
|
|
432
|
+
* Thrown by the local-key getters when the link is in {@link TIdentityMode} `"required"` and no
|
|
433
|
+
* identity has been provisioned. Surfacing this (instead of silently minting a fresh identity on a
|
|
434
|
+
* storage miss) is what keeps a shared backend identity from forking across isolates.
|
|
435
|
+
*/
|
|
436
|
+
var IdentityNotProvisionedError = class extends Error {
|
|
437
|
+
constructor(message = "ClientCryptoKeyLink: identity not provisioned (identityMode 'required'). Call provisionIdentity() once, out-of-band, before serving.") {
|
|
438
|
+
super(message);
|
|
439
|
+
this.name = "IdentityNotProvisionedError";
|
|
440
|
+
}
|
|
441
|
+
};
|
|
431
442
|
var ClientCryptoKeyLink = class {
|
|
432
443
|
localExchangeKeyPair;
|
|
433
444
|
localVerifyKeyPair;
|
|
434
445
|
linkedClientKeys = /* @__PURE__ */ new Map();
|
|
435
446
|
storage;
|
|
447
|
+
identityMode;
|
|
436
448
|
initialized = false;
|
|
437
449
|
initializePromise;
|
|
438
450
|
localExchangeKeyPairPromise;
|
|
439
451
|
localVerifyKeyPairPromise;
|
|
440
|
-
constructor({ storageAdapter } = {}) {
|
|
452
|
+
constructor({ storageAdapter, identityMode } = {}) {
|
|
441
453
|
if (storageAdapter != null) this.storage = createTypedStorage({ storageAdapter });
|
|
454
|
+
this.identityMode = identityMode ?? "lazy";
|
|
442
455
|
}
|
|
443
456
|
/**
|
|
444
457
|
* Loads the local key pairs and any linked client public keys from storage (when a storage
|
|
@@ -461,6 +474,24 @@ var ClientCryptoKeyLink = class {
|
|
|
461
474
|
this.initialized = true;
|
|
462
475
|
}
|
|
463
476
|
/**
|
|
477
|
+
* Provision the local identity once: load any persisted identity, then mint + persist the exchange
|
|
478
|
+
* and verify key pairs only if absent. Idempotent — a no-op when an identity already exists.
|
|
479
|
+
*
|
|
480
|
+
* This is the *only* way to mint when {@link TIdentityMode} is `"required"`. Run it from a single
|
|
481
|
+
* coordinated writer (a deploy hook, a first-boot path, a locked admin route) — **never on the hot
|
|
482
|
+
* request path** — because concurrent provisioning over an eventually-consistent store could read
|
|
483
|
+
* "absent" on two isolates at once and fork divergent identities. The serving path uses `"required"`,
|
|
484
|
+
* which never mints, so a transient storage miss fails that one request loudly instead of forking.
|
|
485
|
+
*
|
|
486
|
+
* A genuine storage *read error* propagates here (it is never coerced to "absent"), so an outage
|
|
487
|
+
* surfaces rather than silently minting a replacement identity.
|
|
488
|
+
*/
|
|
489
|
+
async provisionIdentity() {
|
|
490
|
+
await this.initialize();
|
|
491
|
+
await this.mintLocalExchangeKeyPair();
|
|
492
|
+
await this.mintLocalVerifyKeyPair();
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
464
495
|
* Loads the local key pairs from storage if they were previously persisted. Does NOT generate
|
|
465
496
|
* fresh keys — local identity is created lazily on first use (see {@link ensureLocalExchangeKeyPair}
|
|
466
497
|
* / {@link ensureLocalVerifyKeyPair}), so a verify-only or otherwise key-less consumer never
|
|
@@ -479,10 +510,21 @@ var ClientCryptoKeyLink = class {
|
|
|
479
510
|
};
|
|
480
511
|
}
|
|
481
512
|
/**
|
|
482
|
-
* Returns the local exchange (X25519) key pair
|
|
483
|
-
*
|
|
513
|
+
* Returns the local exchange (X25519) key pair. In `"lazy"` mode it generates + persists it on first
|
|
514
|
+
* use; in `"required"` mode it throws {@link IdentityNotProvisionedError} when none was loaded (mint
|
|
515
|
+
* only via {@link provisionIdentity}). The read path of every encrypt/derive operation.
|
|
484
516
|
*/
|
|
485
517
|
async ensureLocalExchangeKeyPair() {
|
|
518
|
+
if (this.localExchangeKeyPair != null) return this.localExchangeKeyPair;
|
|
519
|
+
if (this.identityMode === "required") throw new IdentityNotProvisionedError();
|
|
520
|
+
return this.mintLocalExchangeKeyPair();
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* Generates + persists the local exchange key pair if absent, returning an already-loaded pair
|
|
524
|
+
* untouched (so provisioning is idempotent). Bypasses the `"required"` guard by design — it is the
|
|
525
|
+
* sole minting path. Concurrent callers share a single generation.
|
|
526
|
+
*/
|
|
527
|
+
async mintLocalExchangeKeyPair() {
|
|
486
528
|
if (this.localExchangeKeyPair != null) return this.localExchangeKeyPair;
|
|
487
529
|
this.localExchangeKeyPairPromise ??= (async () => {
|
|
488
530
|
const keyPair = await generateX25519KeyPair();
|
|
@@ -497,10 +539,21 @@ var ClientCryptoKeyLink = class {
|
|
|
497
539
|
}
|
|
498
540
|
}
|
|
499
541
|
/**
|
|
500
|
-
* Returns the local verify (Ed25519) key pair
|
|
501
|
-
*
|
|
542
|
+
* Returns the local verify (Ed25519) key pair. In `"lazy"` mode it generates + persists it on first
|
|
543
|
+
* use; in `"required"` mode it throws {@link IdentityNotProvisionedError} when none was loaded (mint
|
|
544
|
+
* only via {@link provisionIdentity}). The read path of every sign operation.
|
|
502
545
|
*/
|
|
503
546
|
async ensureLocalVerifyKeyPair() {
|
|
547
|
+
if (this.localVerifyKeyPair != null) return this.localVerifyKeyPair;
|
|
548
|
+
if (this.identityMode === "required") throw new IdentityNotProvisionedError();
|
|
549
|
+
return this.mintLocalVerifyKeyPair();
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* Generates + persists the local verify key pair if absent, returning an already-loaded pair
|
|
553
|
+
* untouched (so provisioning is idempotent). Bypasses the `"required"` guard by design — it is the
|
|
554
|
+
* sole minting path. Concurrent callers share a single generation.
|
|
555
|
+
*/
|
|
556
|
+
async mintLocalVerifyKeyPair() {
|
|
504
557
|
if (this.localVerifyKeyPair != null) return this.localVerifyKeyPair;
|
|
505
558
|
this.localVerifyKeyPairPromise ??= (async () => {
|
|
506
559
|
const keyPair = await generateEd25519KeyPair();
|
|
@@ -718,6 +771,58 @@ var ClientCryptoKeyLink = class {
|
|
|
718
771
|
});
|
|
719
772
|
return sharedEncryptKey;
|
|
720
773
|
}
|
|
774
|
+
/**
|
|
775
|
+
* Derive the shared AES-GCM key from *explicit* key material + this link's local exchange private
|
|
776
|
+
* key, returning a standalone key that is **not** stored in (or read from) the per-`linkedClientId`
|
|
777
|
+
* cache. Use it to give a single session its own immutable key: two sessions that share one
|
|
778
|
+
* `linkedClientId` (e.g. a secure WebSocket and a secure HTTP exchange to the same peer) would
|
|
779
|
+
* otherwise clobber each other's cached shared key — the second handshake's re-link drops the
|
|
780
|
+
* first's key, so frames on the other transport fail to decrypt. Mirrors the derivation in
|
|
781
|
+
* {@link getAesGcmKeyForLinkedClient} exactly, so the resulting key matches the peer's.
|
|
782
|
+
*
|
|
783
|
+
* Requires the local identity to be loaded — call {@link initialize} first when resuming a link
|
|
784
|
+
* cold (the handshake paths have already initialized by the time they hold key material).
|
|
785
|
+
*/
|
|
786
|
+
async deriveSharedAesGcmKey({ exchangePublicKey, saltString, infoString, bindVerifyKeysIntoDerivation, verifyPublicKey }) {
|
|
787
|
+
const localExchangeKeyPair = await this.ensureLocalExchangeKeyPair();
|
|
788
|
+
const externalX25519PublicKey = await importX25519Key.public.fromFormattedString.extractable(exchangePublicKey);
|
|
789
|
+
let derivedInfoString = infoString;
|
|
790
|
+
if (bindVerifyKeysIntoDerivation === true) {
|
|
791
|
+
if (verifyPublicKey == null) throw new Error("ClientCryptoKeyLink.deriveSharedAesGcmKey: a verify public key is required when binding verify keys into the derivation");
|
|
792
|
+
derivedInfoString = buildVerifyKeyBoundInfoString({
|
|
793
|
+
infoString,
|
|
794
|
+
verifyPublicKeys: [await this.getLocalVerifyPublicKey(), verifyPublicKey]
|
|
795
|
+
});
|
|
796
|
+
}
|
|
797
|
+
return await createAesGcmKeyFromX25519Keys({
|
|
798
|
+
internalX25519PrivateKey: localExchangeKeyPair.privateKey,
|
|
799
|
+
externalX25519PublicKey,
|
|
800
|
+
saltString,
|
|
801
|
+
infoString: derivedInfoString
|
|
802
|
+
});
|
|
803
|
+
}
|
|
804
|
+
/**
|
|
805
|
+
* Derive a symmetric AES-GCM key bound to **this link's own identity** — for sealing data the server
|
|
806
|
+
* hands a client and reads back later (e.g. a stateless secure-exchange session ticket), where only
|
|
807
|
+
* this server (any isolate that loaded the same persisted identity) can open it.
|
|
808
|
+
*
|
|
809
|
+
* Deterministic: an X25519 ECDH of the local exchange private key against its own public key, fed
|
|
810
|
+
* through the same vetted HKDF as peer key derivation, with a fixed versioned info label. So every
|
|
811
|
+
* isolate sharing the persisted identity derives the identical key, and no raw private-key bytes are
|
|
812
|
+
* exposed. The {@link version} is part of the label, so a future rotation can run two keys during a
|
|
813
|
+
* grace window (old tickets opened with `v1` while new ones issue under `v2`).
|
|
814
|
+
*
|
|
815
|
+
* Requires the local identity (call {@link initialize} / {@link provisionIdentity} first); in
|
|
816
|
+
* `"required"` mode an unprovisioned link throws {@link IdentityNotProvisionedError}.
|
|
817
|
+
*/
|
|
818
|
+
async deriveLocalSealKey(options) {
|
|
819
|
+
const localExchangeKeyPair = await this.ensureLocalExchangeKeyPair();
|
|
820
|
+
return await createAesGcmKeyFromX25519Keys({
|
|
821
|
+
internalX25519PrivateKey: localExchangeKeyPair.privateKey,
|
|
822
|
+
externalX25519PublicKey: localExchangeKeyPair.publicKey,
|
|
823
|
+
infoString: `exchange-ticket-seal/${options?.version ?? "v1"}`
|
|
824
|
+
});
|
|
825
|
+
}
|
|
721
826
|
async encryptDataForLinkedClient({ dataToEncrypt, linkedClientId }) {
|
|
722
827
|
return await encryptTextDataWithAesGcmKey({
|
|
723
828
|
dataToEncrypt,
|
|
@@ -1045,6 +1150,6 @@ function createTypedMemoryStorage_json(options) {
|
|
|
1045
1150
|
return createTypedStorage({ storageAdapter: createMemoryStorageAdapter_json(options) });
|
|
1046
1151
|
}
|
|
1047
1152
|
//#endregion
|
|
1048
|
-
export { ClientCryptoKeyLink, DEFAULT_COMBINED_TEXT_DATA_SEPARATOR, ECryptoKeyAlgo, ECryptoKeyFormat, EStorageAdapterType, StorageAdapter, buildVerifyKeyBoundInfoString, convertEd25519FormattedStringToObject, convertEd25519FormattedStringToSerializedKeyData, convertEd25519JwkDataStringToObject, convertEd25519JwkDataStringToSerializedKeyData, convertEd25519RawDataStringToObject, convertEd25519RawDataStringToSerializedKeyData, convertX25519FormattedStringToObject, convertX25519FormattedStringToSerializedKeyData, convertX25519JwkDataStringToObject, convertX25519JwkDataStringToSerializedKeyData, convertX25519RawDataStringToObject, convertX25519RawDataStringToSerializedKeyData, createAesGcmKeyFromX25519Keys, createDurableObjectStorageAdapter, createDurableObjectStorageMethods, createDurableObjectTypedStorage, createKVStorageAdapter, createKVStorageMethods, createKVTypedStorage, createMemoryStorageAdapter_json, createMemoryStorageAdapter_string, createMemoryStorageMethods_json, createMemoryStorageMethods_string, createSharedBitsFromX25519, createTypedMemoryStorage_json, createTypedMemoryStorage_string, createTypedStorage, createTypedWebLocalStorage, createTypedWebSessionStorage, createWebLocalStorageAdapter, createWebLocalStorageMethods, createWebSessionStorageAdapter, createWebSessionStorageMethods, decryptBytesWithAesGcmKey, decryptTextDataWithAesGcmKey, encryptBytesWithAesGcmKey, encryptTextDataWithAesGcmKey, generateEd25519KeyPair, generateX25519KeyPair, importEd25519Key, importX25519Key, serializeEd25519Key_Jwk, serializeEd25519Key_Raw, serializeX25519Key_Jwk, serializeX25519Key_Raw, signCombinedTextDataWithKeyEd25519, signTextDataWithKeyEd25519, vCryptoKeyPairDataEd25519, vCryptoKeyPairDataX25519, vEncryptedAesGcmPayload, vSerializedCryptoKeyDataEd25519_Jwk, vSerializedCryptoKeyDataEd25519_Raw, vSerializedCryptoKeyDataX25519_Jwk, vSerializedCryptoKeyDataX25519_Raw, vVerifyChallengeWithSignature_Input, vVerifyChallengeWithSignature_WithThrow_Input, verifyWithKeyEd25519 };
|
|
1153
|
+
export { ClientCryptoKeyLink, DEFAULT_COMBINED_TEXT_DATA_SEPARATOR, ECryptoKeyAlgo, ECryptoKeyFormat, EStorageAdapterType, IdentityNotProvisionedError, StorageAdapter, buildVerifyKeyBoundInfoString, convertEd25519FormattedStringToObject, convertEd25519FormattedStringToSerializedKeyData, convertEd25519JwkDataStringToObject, convertEd25519JwkDataStringToSerializedKeyData, convertEd25519RawDataStringToObject, convertEd25519RawDataStringToSerializedKeyData, convertX25519FormattedStringToObject, convertX25519FormattedStringToSerializedKeyData, convertX25519JwkDataStringToObject, convertX25519JwkDataStringToSerializedKeyData, convertX25519RawDataStringToObject, convertX25519RawDataStringToSerializedKeyData, createAesGcmKeyFromX25519Keys, createDurableObjectStorageAdapter, createDurableObjectStorageMethods, createDurableObjectTypedStorage, createKVStorageAdapter, createKVStorageMethods, createKVTypedStorage, createMemoryStorageAdapter_json, createMemoryStorageAdapter_string, createMemoryStorageMethods_json, createMemoryStorageMethods_string, createSharedBitsFromX25519, createTypedMemoryStorage_json, createTypedMemoryStorage_string, createTypedStorage, createTypedWebLocalStorage, createTypedWebSessionStorage, createWebLocalStorageAdapter, createWebLocalStorageMethods, createWebSessionStorageAdapter, createWebSessionStorageMethods, decryptBytesWithAesGcmKey, decryptTextDataWithAesGcmKey, encryptBytesWithAesGcmKey, encryptTextDataWithAesGcmKey, generateEd25519KeyPair, generateX25519KeyPair, importEd25519Key, importX25519Key, serializeEd25519Key_Jwk, serializeEd25519Key_Raw, serializeX25519Key_Jwk, serializeX25519Key_Raw, signCombinedTextDataWithKeyEd25519, signTextDataWithKeyEd25519, vCryptoKeyPairDataEd25519, vCryptoKeyPairDataX25519, vEncryptedAesGcmPayload, vSerializedCryptoKeyDataEd25519_Jwk, vSerializedCryptoKeyDataEd25519_Raw, vSerializedCryptoKeyDataX25519_Jwk, vSerializedCryptoKeyDataX25519_Raw, vVerifyChallengeWithSignature_Input, vVerifyChallengeWithSignature_WithThrow_Input, verifyWithKeyEd25519 };
|
|
1049
1154
|
|
|
1050
1155
|
//# sourceMappingURL=index.mjs.map
|