@nice-code/util 0.5.4 → 0.6.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.
Files changed (28) hide show
  1. package/README.md +165 -73
  2. package/build/index.js +779 -7
  3. package/build/types/core/core_valibot_schemas.d.ts +13 -0
  4. package/build/types/core/createDataStringConverter_stringToObject.d.ts +12 -0
  5. package/build/types/crypto/aes_gcm/createAesGcmKeyFromX25519Keys.d.ts +6 -0
  6. package/build/types/crypto/aes_gcm/decryptTextDataWithAesGcmKey.d.ts +5 -0
  7. package/build/types/crypto/aes_gcm/encryptTextDataWithAesGcmKey.d.ts +5 -0
  8. package/build/types/crypto/client_key_link/ClientCryptoKeyLink.d.ts +167 -0
  9. package/build/types/crypto/client_key_link/buildVerifyKeyBoundInfoString.d.ts +20 -0
  10. package/build/types/crypto/crypto.converters.d.ts +53 -0
  11. package/build/types/crypto/crypto.schema.d.ts +83 -0
  12. package/build/types/crypto/ed25519/generateEd25519KeyPair.d.ts +1 -0
  13. package/build/types/crypto/ed25519/importEd25519Key.d.ts +35 -0
  14. package/build/types/crypto/ed25519/serializeEd25519Key_Jwk.d.ts +2 -0
  15. package/build/types/crypto/ed25519/serializeEd25519Key_Raw.d.ts +2 -0
  16. package/build/types/crypto/ed25519/signCombinedTextDataWithKeyEd25519.d.ts +2 -0
  17. package/build/types/crypto/ed25519/signTextDataWithKeyEd25519.d.ts +1 -0
  18. package/build/types/crypto/ed25519/verifyWithKeyEd25519.d.ts +5 -0
  19. package/build/types/crypto/index.d.ts +18 -0
  20. package/build/types/crypto/x25519/createSharedBitsFromX25519.d.ts +4 -0
  21. package/build/types/crypto/x25519/generateX25519KeyPair.d.ts +1 -0
  22. package/build/types/crypto/x25519/importX25519Key.d.ts +35 -0
  23. package/build/types/crypto/x25519/serializeX25519Key_Jwk.d.ts +2 -0
  24. package/build/types/crypto/x25519/serializeX25519Key_Raw.d.ts +2 -0
  25. package/build/types/data_type/index.d.ts +1 -0
  26. package/build/types/data_type/string/nullEmpty.d.ts +3 -0
  27. package/build/types/index.d.ts +1 -0
  28. package/package.json +5 -1
package/build/index.js CHANGED
@@ -1,9 +1,93 @@
1
- // src/storage_adapter/storage_adapter.types.ts
2
- var EStorageAdapterType;
3
- ((EStorageAdapterType2) => {
4
- EStorageAdapterType2["string"] = "string";
5
- EStorageAdapterType2["json"] = "json";
6
- })(EStorageAdapterType ||= {});
1
+ // src/data_type/string/nullEmpty.ts
2
+ var notNullEmpty = (str) => {
3
+ return str != null && str.length > 0;
4
+ };
5
+
6
+ // src/crypto/x25519/createSharedBitsFromX25519.ts
7
+ var createSharedBitsFromX25519 = async ({
8
+ privateKey,
9
+ publicKey
10
+ }) => {
11
+ return new Uint8Array(await crypto.subtle.deriveBits({ name: "X25519", public: publicKey }, privateKey, 256));
12
+ };
13
+
14
+ // src/crypto/aes_gcm/createAesGcmKeyFromX25519Keys.ts
15
+ var DEFAULT_INFO_STRING = "METEOR_BRIDGE_DEFAULT_INFO_STRING";
16
+ var createAesGcmKeyFromX25519Keys = async ({
17
+ externalX25519PublicKey,
18
+ internalX25519PrivateKey,
19
+ infoString,
20
+ saltString
21
+ }) => {
22
+ const sharedBits = await createSharedBitsFromX25519({
23
+ privateKey: internalX25519PrivateKey,
24
+ publicKey: externalX25519PublicKey
25
+ });
26
+ const ikm = await crypto.subtle.importKey("raw", new Uint8Array(sharedBits), "HKDF", false, [
27
+ "deriveKey"
28
+ ]);
29
+ const salt = notNullEmpty(saltString) ? new TextEncoder().encode(saltString) : new Uint8Array;
30
+ const info = new TextEncoder().encode(notNullEmpty(infoString) ? infoString : DEFAULT_INFO_STRING);
31
+ return await crypto.subtle.deriveKey({
32
+ name: "HKDF",
33
+ hash: "SHA-256",
34
+ salt,
35
+ info
36
+ }, ikm, { name: "AES-GCM", length: 256 }, false, ["encrypt", "decrypt"]);
37
+ };
38
+ // src/crypto/aes_gcm/decryptTextDataWithAesGcmKey.ts
39
+ import { base64 } from "@scure/base";
40
+ var decryptTextDataWithAesGcmKey = async ({
41
+ aesGcmKey,
42
+ dataToDecrypt
43
+ }) => {
44
+ const decryptedData = await crypto.subtle.decrypt({
45
+ name: "AES-GCM",
46
+ iv: new Uint8Array(base64.decode(dataToDecrypt.nonce))
47
+ }, aesGcmKey, new Uint8Array(base64.decode(dataToDecrypt.ciphertext)));
48
+ return new TextDecoder().decode(decryptedData);
49
+ };
50
+ // src/crypto/aes_gcm/encryptTextDataWithAesGcmKey.ts
51
+ import { base64 as base642 } from "@scure/base";
52
+ var encryptTextDataWithAesGcmKey = async ({
53
+ aesGcmKey,
54
+ dataToEncrypt
55
+ }) => {
56
+ const nonce = crypto.getRandomValues(new Uint8Array(12));
57
+ const encryptedData = await crypto.subtle.encrypt({
58
+ name: "AES-GCM",
59
+ iv: nonce
60
+ }, aesGcmKey, new TextEncoder().encode(dataToEncrypt));
61
+ return {
62
+ nonce: base642.encode(nonce),
63
+ ciphertext: base642.encode(new Uint8Array(encryptedData))
64
+ };
65
+ };
66
+ // src/crypto/ed25519/signTextDataWithKeyEd25519.ts
67
+ var signTextDataWithKeyEd25519 = async (data, cryptoKey) => {
68
+ const dataBuffer = new TextEncoder().encode(data);
69
+ const signature = await crypto.subtle.sign({
70
+ name: "ED25519"
71
+ }, cryptoKey, dataBuffer);
72
+ return new Uint8Array(signature);
73
+ };
74
+
75
+ // src/crypto/ed25519/signCombinedTextDataWithKeyEd25519.ts
76
+ var DEFAULT_COMBINED_TEXT_DATA_SEPARATOR = "::";
77
+ var signCombinedTextDataWithKeyEd25519 = async (data, cryptoKey, separator = DEFAULT_COMBINED_TEXT_DATA_SEPARATOR) => {
78
+ return await signTextDataWithKeyEd25519(data.join(separator), cryptoKey);
79
+ };
80
+
81
+ // src/crypto/client_key_link/buildVerifyKeyBoundInfoString.ts
82
+ var buildVerifyKeyBoundInfoString = ({
83
+ infoString,
84
+ verifyPublicKeys
85
+ }) => {
86
+ const sortedKeys = [...verifyPublicKeys].sort();
87
+ return [...infoString != null ? [infoString] : [], ...sortedKeys].join(DEFAULT_COMBINED_TEXT_DATA_SEPARATOR);
88
+ };
89
+ // src/crypto/client_key_link/ClientCryptoKeyLink.ts
90
+ import { base64 as base649 } from "@scure/base";
7
91
 
8
92
  // src/storage_adapter/typed_storage/createTypedStorage.ts
9
93
  function createTypedStorage({
@@ -40,6 +124,664 @@ function createTypedStorage({
40
124
  };
41
125
  }
42
126
 
127
+ // src/crypto/ed25519/generateEd25519KeyPair.ts
128
+ var generateEd25519KeyPair = async () => {
129
+ const keyPair = await crypto.subtle.generateKey({ name: "Ed25519" }, true, [
130
+ "sign",
131
+ "verify"
132
+ ]);
133
+ return keyPair;
134
+ };
135
+
136
+ // src/crypto/ed25519/importEd25519Key.ts
137
+ import { base64 as base644 } from "@scure/base";
138
+
139
+ // src/core/createDataStringConverter_stringToObject.ts
140
+ var createDataStringConverter_stringToObject = ({
141
+ transformJsonForFormats = [],
142
+ transformJson = false
143
+ } = {}) => (inputDataString) => {
144
+ const [type, format, dataString] = inputDataString.split("::");
145
+ let parsedData = dataString;
146
+ if (transformJson || transformJsonForFormats.includes(format)) {
147
+ try {
148
+ parsedData = JSON.parse(dataString);
149
+ } catch (error) {
150
+ const err = new Error(`Failed to parse type and format data string. Given input: "${inputDataString}", expected JSON parsable "data" value in the format "${type}::${format}::data" ${error instanceof Error ? error.message : String(error)}`);
151
+ err.cause = error;
152
+ throw err;
153
+ }
154
+ }
155
+ return {
156
+ formattedString: inputDataString,
157
+ type,
158
+ format,
159
+ data: parsedData
160
+ };
161
+ };
162
+
163
+ // src/crypto/crypto.schema.ts
164
+ import * as v2 from "valibot";
165
+
166
+ // src/core/core_valibot_schemas.ts
167
+ import * as v from "valibot";
168
+ var vBase64 = v.pipe(v.string(), v.base64());
169
+ var vCreateSchema_TypeAndFormatPrefixedDataString = ({
170
+ type,
171
+ format,
172
+ typeKind,
173
+ formatKind
174
+ }) => {
175
+ const _typeKind = typeKind ?? "data_type";
176
+ const _formatKind = formatKind ?? "data_format";
177
+ return v.pipe(v.custom((input) => {
178
+ if (typeof input !== "string")
179
+ return false;
180
+ const [typePart, formatPart, dataPart] = input.split("::");
181
+ return typePart === type && formatPart === format && typeof dataPart === "string";
182
+ }, `Invalid format, expected '<${_typeKind}>::<${_formatKind}>::<value>' where "${_typeKind}" is "${type}", "${_formatKind}" is "${format}", and "value" is a string in the specified format`));
183
+ };
184
+
185
+ // src/crypto/crypto.schema.ts
186
+ var vSerializedCryptoKeyDataEd25519_Raw = vCreateSchema_TypeAndFormatPrefixedDataString({
187
+ format: "raw_base64" /* raw_base64 */,
188
+ type: "ed25519" /* ed25519 */,
189
+ typeKind: "algo"
190
+ });
191
+ var vSerializedCryptoKeyDataEd25519_Jwk = vCreateSchema_TypeAndFormatPrefixedDataString({
192
+ format: "jwk" /* jwk */,
193
+ type: "ed25519" /* ed25519 */,
194
+ typeKind: "algo",
195
+ transformJson: true
196
+ });
197
+ var vSerializedCryptoKeyDataX25519_Raw = vCreateSchema_TypeAndFormatPrefixedDataString({
198
+ format: "raw_base64" /* raw_base64 */,
199
+ type: "x25519" /* x25519 */,
200
+ typeKind: "algo"
201
+ });
202
+ var vSerializedCryptoKeyDataX25519_Jwk = vCreateSchema_TypeAndFormatPrefixedDataString({
203
+ format: "jwk" /* jwk */,
204
+ type: "x25519" /* x25519 */,
205
+ typeKind: "algo",
206
+ transformJson: true
207
+ });
208
+ var vCryptoKeyPairDataX25519 = v2.object({
209
+ publicKey: vSerializedCryptoKeyDataX25519_Raw,
210
+ privateKey: vSerializedCryptoKeyDataX25519_Jwk
211
+ });
212
+ var vCryptoKeyPairDataEd25519 = v2.object({
213
+ publicKey: vSerializedCryptoKeyDataEd25519_Raw,
214
+ privateKey: vSerializedCryptoKeyDataEd25519_Jwk
215
+ });
216
+ var vVerifyChallengeWithSignature_Input = v2.object({
217
+ challenge: v2.string(),
218
+ signatureBase64: vBase64
219
+ });
220
+ var vVerifyChallengeWithSignature_WithThrow_Input = v2.intersect([
221
+ vVerifyChallengeWithSignature_Input,
222
+ v2.object({
223
+ throwOnInvalid: v2.optional(v2.boolean())
224
+ })
225
+ ]);
226
+ var vEncryptedAesGcmPayload = v2.object({
227
+ nonce: vBase64,
228
+ ciphertext: vBase64
229
+ });
230
+
231
+ // src/crypto/crypto.converters.ts
232
+ var convertEd25519RawDataStringToObject = createDataStringConverter_stringToObject();
233
+ var convertEd25519JwkDataStringToObject = createDataStringConverter_stringToObject({ transformJson: true });
234
+ var convertEd25519FormattedStringToObject = createDataStringConverter_stringToObject({
235
+ transformJsonForFormats: ["jwk" /* jwk */]
236
+ });
237
+ var convertEd25519RawDataStringToSerializedKeyData = (input) => {
238
+ const transformed = convertEd25519RawDataStringToObject(input);
239
+ return {
240
+ prefixed: input,
241
+ transformed
242
+ };
243
+ };
244
+ var convertEd25519JwkDataStringToSerializedKeyData = (input) => {
245
+ const transformed = convertEd25519JwkDataStringToObject(input);
246
+ return {
247
+ prefixed: input,
248
+ transformed
249
+ };
250
+ };
251
+ var convertEd25519FormattedStringToSerializedKeyData = (input) => {
252
+ return convertEd25519FormattedStringToObject(input);
253
+ };
254
+ var convertX25519RawDataStringToObject = createDataStringConverter_stringToObject();
255
+ var convertX25519JwkDataStringToObject = createDataStringConverter_stringToObject({ transformJson: true });
256
+ var convertX25519FormattedStringToObject = createDataStringConverter_stringToObject({
257
+ transformJsonForFormats: ["jwk" /* jwk */]
258
+ });
259
+ var convertX25519RawDataStringToSerializedKeyData = (input) => {
260
+ const transformed = convertX25519RawDataStringToObject(input);
261
+ return {
262
+ prefixed: input,
263
+ transformed
264
+ };
265
+ };
266
+ var convertX25519JwkDataStringToSerializedKeyData = (input) => {
267
+ const transformed = convertX25519JwkDataStringToObject(input);
268
+ return {
269
+ prefixed: input,
270
+ transformed
271
+ };
272
+ };
273
+ var convertX25519FormattedStringToSerializedKeyData = (input) => {
274
+ return convertX25519FormattedStringToObject(input);
275
+ };
276
+
277
+ // src/crypto/ed25519/importEd25519Key.ts
278
+ var fromBase64 = async (dataBase64, keyUsage, extractable) => {
279
+ const keyBuffer = Uint8Array.from(base644.decode(dataBase64));
280
+ return await crypto.subtle.importKey("raw", keyBuffer, { name: "Ed25519" }, extractable, keyUsage);
281
+ };
282
+ var fromJwk = async (jwk, keyUsage, extractable = true) => {
283
+ return await crypto.subtle.importKey("jwk", jwk, { name: "Ed25519" }, extractable, keyUsage);
284
+ };
285
+ var fromSerializedObject = async (serialized, keyUsage, extractable = true) => {
286
+ if (serialized.format === "jwk" /* jwk */) {
287
+ return await fromJwk(serialized.data, keyUsage, extractable);
288
+ }
289
+ return await fromBase64(serialized.data, keyUsage, extractable);
290
+ };
291
+ var fromFormattedString = async (dataString, keyUsage, extractable = true) => {
292
+ const transformed = convertEd25519FormattedStringToSerializedKeyData(dataString);
293
+ return await fromSerializedObject(transformed, keyUsage, extractable);
294
+ };
295
+ var extractableOrNonExtractable = (keyUsage, func) => ({
296
+ extractable: (input) => func(input, keyUsage, true),
297
+ nonExtractable: (input) => func(input, keyUsage, false)
298
+ });
299
+ var importEd25519Key = {
300
+ private: {
301
+ fromFormattedString: extractableOrNonExtractable(["sign"], fromFormattedString),
302
+ fromSerializedObject: extractableOrNonExtractable(["sign"], fromSerializedObject),
303
+ fromJwk: extractableOrNonExtractable(["sign"], fromJwk)
304
+ },
305
+ public: {
306
+ fromBase64: extractableOrNonExtractable(["verify"], fromBase64),
307
+ fromFormattedString: extractableOrNonExtractable(["verify"], fromFormattedString),
308
+ fromSerializedObject: extractableOrNonExtractable(["verify"], fromSerializedObject),
309
+ fromJwk: extractableOrNonExtractable(["verify"], fromJwk)
310
+ }
311
+ };
312
+
313
+ // src/crypto/ed25519/serializeEd25519Key_Jwk.ts
314
+ var serializeEd25519Key_Jwk = async (key) => {
315
+ const keyJwk = await crypto.subtle.exportKey("jwk", key);
316
+ const prefixed = `${"ed25519" /* ed25519 */}::${"jwk" /* jwk */}::${JSON.stringify(keyJwk)}`;
317
+ const transformed = {
318
+ formattedString: prefixed,
319
+ type: "ed25519" /* ed25519 */,
320
+ data: keyJwk,
321
+ format: "jwk" /* jwk */
322
+ };
323
+ return { transformed, prefixed };
324
+ };
325
+
326
+ // src/crypto/ed25519/serializeEd25519Key_Raw.ts
327
+ import { base64 as base645 } from "@scure/base";
328
+ var serializeEd25519Key_Raw = async (publicKey) => {
329
+ const publicKeyBuffer = await crypto.subtle.exportKey("raw", publicKey);
330
+ const publicKeyBase64 = base645.encode(new Uint8Array(publicKeyBuffer));
331
+ const prefixed = `${"ed25519" /* ed25519 */}::${"raw_base64" /* raw_base64 */}::${publicKeyBase64}`;
332
+ const transformed = {
333
+ formattedString: prefixed,
334
+ type: "ed25519" /* ed25519 */,
335
+ data: publicKeyBase64,
336
+ format: "raw_base64" /* raw_base64 */
337
+ };
338
+ return { transformed, prefixed };
339
+ };
340
+
341
+ // src/crypto/ed25519/verifyWithKeyEd25519.ts
342
+ import { base64 as base646 } from "@scure/base";
343
+ var verifyWithKeyEd25519 = async ({
344
+ challenge,
345
+ signatureBase64,
346
+ publicKey
347
+ }) => {
348
+ const signatureBuffer = Uint8Array.from(base646.decode(signatureBase64));
349
+ const challengeBuffer = new TextEncoder().encode(challenge);
350
+ return await crypto.subtle.verify({
351
+ name: "ED25519"
352
+ }, publicKey, signatureBuffer, challengeBuffer);
353
+ };
354
+
355
+ // src/crypto/x25519/generateX25519KeyPair.ts
356
+ var generateX25519KeyPair = async () => {
357
+ const keyPair = await crypto.subtle.generateKey({ name: "X25519" }, true, [
358
+ "deriveKey",
359
+ "deriveBits"
360
+ ]);
361
+ return keyPair;
362
+ };
363
+
364
+ // src/crypto/x25519/importX25519Key.ts
365
+ import { base64 as base647 } from "@scure/base";
366
+ var fromBase642 = async (dataBase64, keyUsage, extractable) => {
367
+ const keyBuffer = Uint8Array.from(base647.decode(dataBase64));
368
+ return await crypto.subtle.importKey("raw", keyBuffer, { name: "X25519" }, extractable, keyUsage);
369
+ };
370
+ var fromJwk2 = async (jwk, keyUsage, extractable = true) => {
371
+ return await crypto.subtle.importKey("jwk", jwk, { name: "X25519" }, extractable, keyUsage);
372
+ };
373
+ var fromSerializedObject2 = async (serialized, keyUsage, extractable = true) => {
374
+ if (serialized.format === "jwk" /* jwk */) {
375
+ return await fromJwk2(serialized.data, keyUsage, extractable);
376
+ }
377
+ return await fromBase642(serialized.data, keyUsage, extractable);
378
+ };
379
+ var fromFormattedString2 = async (dataString, keyUsage, extractable = true) => {
380
+ const transformed = convertX25519FormattedStringToSerializedKeyData(dataString);
381
+ return await fromSerializedObject2(transformed, keyUsage, extractable);
382
+ };
383
+ var extractableOrNonExtractable2 = (keyUsage, func) => ({
384
+ extractable: (input) => func(input, keyUsage, true),
385
+ nonExtractable: (input) => func(input, keyUsage, false)
386
+ });
387
+ var importX25519Key = {
388
+ private: {
389
+ fromFormattedString: extractableOrNonExtractable2(["deriveKey", "deriveBits"], fromFormattedString2),
390
+ fromSerializedObject: extractableOrNonExtractable2(["deriveKey", "deriveBits"], fromSerializedObject2),
391
+ fromJwk: extractableOrNonExtractable2(["deriveKey", "deriveBits"], fromJwk2)
392
+ },
393
+ public: {
394
+ fromBase64: extractableOrNonExtractable2([], fromBase642),
395
+ fromFormattedString: extractableOrNonExtractable2([], fromFormattedString2),
396
+ fromSerializedObject: extractableOrNonExtractable2([], fromSerializedObject2),
397
+ fromJwk: extractableOrNonExtractable2([], fromJwk2)
398
+ }
399
+ };
400
+
401
+ // src/crypto/x25519/serializeX25519Key_Jwk.ts
402
+ var serializeX25519Key_Jwk = async (key) => {
403
+ const publicKeyJwk = await crypto.subtle.exportKey("jwk", key);
404
+ const prefixed = `${"x25519" /* x25519 */}::${"jwk" /* jwk */}::${JSON.stringify(publicKeyJwk)}`;
405
+ const transformed = {
406
+ formattedString: prefixed,
407
+ type: "x25519" /* x25519 */,
408
+ data: publicKeyJwk,
409
+ format: "jwk" /* jwk */
410
+ };
411
+ return { transformed, prefixed };
412
+ };
413
+
414
+ // src/crypto/x25519/serializeX25519Key_Raw.ts
415
+ import { base64 as base648 } from "@scure/base";
416
+ var serializeX25519Key_Raw = async (key) => {
417
+ const publicKeyBuffer = await crypto.subtle.exportKey("raw", key);
418
+ const publicKeyBase64 = base648.encode(new Uint8Array(publicKeyBuffer));
419
+ const prefixed = `${"x25519" /* x25519 */}::${"raw_base64" /* raw_base64 */}::${publicKeyBase64}`;
420
+ const transformed = {
421
+ formattedString: prefixed,
422
+ type: "x25519" /* x25519 */,
423
+ data: publicKeyBase64,
424
+ format: "raw_base64" /* raw_base64 */
425
+ };
426
+ return { transformed, prefixed };
427
+ };
428
+
429
+ // src/crypto/client_key_link/ClientCryptoKeyLink.ts
430
+ class ClientCryptoKeyLink {
431
+ localExchangeKeyPair;
432
+ localVerifyKeyPair;
433
+ linkedClientKeys = new Map;
434
+ storage;
435
+ initialized = false;
436
+ initializePromise;
437
+ localExchangeKeyPairPromise;
438
+ localVerifyKeyPairPromise;
439
+ constructor({ storageAdapter } = {}) {
440
+ if (storageAdapter != null) {
441
+ this.storage = createTypedStorage({ storageAdapter });
442
+ }
443
+ }
444
+ async initialize() {
445
+ if (this.initialized) {
446
+ return;
447
+ }
448
+ this.initializePromise ??= this.runInitialize();
449
+ try {
450
+ await this.initializePromise;
451
+ } finally {
452
+ this.initializePromise = undefined;
453
+ }
454
+ }
455
+ async runInitialize() {
456
+ await this.loadStoredLocalKeys();
457
+ await this.loadLinkedClients();
458
+ this.initialized = true;
459
+ }
460
+ async loadStoredLocalKeys() {
461
+ const storedExchange = await this.storage?.getJson("localExchangeKeyPair");
462
+ if (storedExchange != null) {
463
+ this.localExchangeKeyPair = {
464
+ privateKey: await importX25519Key.private.fromFormattedString.extractable(storedExchange.privateKey),
465
+ publicKey: await importX25519Key.public.fromFormattedString.extractable(storedExchange.publicKey)
466
+ };
467
+ }
468
+ const storedVerify = await this.storage?.getJson("localVerifyKeyPair");
469
+ if (storedVerify != null) {
470
+ this.localVerifyKeyPair = {
471
+ privateKey: await importEd25519Key.private.fromFormattedString.extractable(storedVerify.privateKey),
472
+ publicKey: await importEd25519Key.public.fromFormattedString.extractable(storedVerify.publicKey)
473
+ };
474
+ }
475
+ }
476
+ async ensureLocalExchangeKeyPair() {
477
+ if (this.localExchangeKeyPair != null) {
478
+ return this.localExchangeKeyPair;
479
+ }
480
+ this.localExchangeKeyPairPromise ??= (async () => {
481
+ const keyPair = await generateX25519KeyPair();
482
+ this.localExchangeKeyPair = keyPair;
483
+ if (this.storage != null) {
484
+ await this.storage.setJson("localExchangeKeyPair", await this.serializeExchangeKeyPair(keyPair));
485
+ }
486
+ return keyPair;
487
+ })();
488
+ try {
489
+ return await this.localExchangeKeyPairPromise;
490
+ } finally {
491
+ this.localExchangeKeyPairPromise = undefined;
492
+ }
493
+ }
494
+ async ensureLocalVerifyKeyPair() {
495
+ if (this.localVerifyKeyPair != null) {
496
+ return this.localVerifyKeyPair;
497
+ }
498
+ this.localVerifyKeyPairPromise ??= (async () => {
499
+ const keyPair = await generateEd25519KeyPair();
500
+ this.localVerifyKeyPair = keyPair;
501
+ if (this.storage != null) {
502
+ await this.storage.setJson("localVerifyKeyPair", await this.serializeVerifyKeyPair(keyPair));
503
+ }
504
+ return keyPair;
505
+ })();
506
+ try {
507
+ return await this.localVerifyKeyPairPromise;
508
+ } finally {
509
+ this.localVerifyKeyPairPromise = undefined;
510
+ }
511
+ }
512
+ async loadLinkedClients() {
513
+ const storedLinkedClients = await this.storage?.getJson("linkedClientPublicKeys");
514
+ if (storedLinkedClients == null) {
515
+ return;
516
+ }
517
+ for (const [linkedClientId, publicKeys] of Object.entries(storedLinkedClients)) {
518
+ await this.linkClient({
519
+ linkedClientId,
520
+ verifyPublicKey: publicKeys.verifyPublicKey,
521
+ exchangePublicKey: publicKeys.exchangePublicKey,
522
+ saltString: publicKeys.saltString,
523
+ infoString: publicKeys.infoString,
524
+ bindVerifyKeysIntoDerivation: publicKeys.bindVerifyKeysIntoDerivation
525
+ });
526
+ }
527
+ }
528
+ async serializeExchangeKeyPair(keyPair) {
529
+ return {
530
+ publicKey: (await serializeX25519Key_Raw(keyPair.publicKey)).prefixed,
531
+ privateKey: (await serializeX25519Key_Jwk(keyPair.privateKey)).prefixed
532
+ };
533
+ }
534
+ async serializeVerifyKeyPair(keyPair) {
535
+ return {
536
+ publicKey: (await serializeEd25519Key_Raw(keyPair.publicKey)).prefixed,
537
+ privateKey: (await serializeEd25519Key_Jwk(keyPair.privateKey)).prefixed
538
+ };
539
+ }
540
+ async getLocalPublicKeys() {
541
+ return {
542
+ verifyPublicKey: await this.getLocalVerifyPublicKey(),
543
+ exchangePublicKey: await this.getLocalExchangePublicKey()
544
+ };
545
+ }
546
+ async getLocalExchangePublicKey() {
547
+ const exchangeKeyPair = await this.ensureLocalExchangeKeyPair();
548
+ return (await serializeX25519Key_Raw(exchangeKeyPair.publicKey)).prefixed;
549
+ }
550
+ async getLocalVerifyPublicKey() {
551
+ const verifyKeyPair = await this.ensureLocalVerifyKeyPair();
552
+ return (await serializeEd25519Key_Raw(verifyKeyPair.publicKey)).prefixed;
553
+ }
554
+ async linkClient({
555
+ linkedClientId,
556
+ verifyPublicKey,
557
+ exchangePublicKey,
558
+ saltString,
559
+ infoString,
560
+ bindVerifyKeysIntoDerivation
561
+ }) {
562
+ const existing = this.linkedClientKeys.get(linkedClientId);
563
+ const verify = verifyPublicKey != null ? {
564
+ publicKey: await importEd25519Key.public.fromFormattedString.extractable(verifyPublicKey),
565
+ publicKeySerialized: verifyPublicKey
566
+ } : existing?.verify;
567
+ const verifyKeyChanged = verifyPublicKey != null && verifyPublicKey !== existing?.verify?.publicKeySerialized;
568
+ let exchange = existing?.exchange;
569
+ const exchangeParamsProvided = exchangePublicKey != null || saltString !== undefined || infoString !== undefined || bindVerifyKeysIntoDerivation !== undefined;
570
+ if (exchangeParamsProvided) {
571
+ const nextPublicKeySerialized = exchangePublicKey ?? existing?.exchange?.publicKeySerialized;
572
+ if (nextPublicKeySerialized == null) {
573
+ throw new Error(`ClientCryptoKeyLink: Cannot set salt/info for ${linkedClientId} without an exchange public key`);
574
+ }
575
+ const nextSalt = saltString !== undefined ? saltString : existing?.exchange?.saltString;
576
+ const nextInfo = infoString !== undefined ? infoString : existing?.exchange?.infoString;
577
+ const nextBind = bindVerifyKeysIntoDerivation !== undefined ? bindVerifyKeysIntoDerivation : existing?.exchange?.bindVerifyKeysIntoDerivation;
578
+ const publicKey = exchangePublicKey != null ? await importX25519Key.public.fromFormattedString.extractable(exchangePublicKey) : existing.exchange.publicKey;
579
+ const unchanged = nextPublicKeySerialized === existing?.exchange?.publicKeySerialized && nextSalt === existing?.exchange?.saltString && nextInfo === existing?.exchange?.infoString && nextBind === existing?.exchange?.bindVerifyKeysIntoDerivation && !(nextBind === true && verifyKeyChanged);
580
+ exchange = {
581
+ publicKey,
582
+ publicKeySerialized: nextPublicKeySerialized,
583
+ saltString: nextSalt,
584
+ infoString: nextInfo,
585
+ bindVerifyKeysIntoDerivation: nextBind,
586
+ sharedEncryptKey: unchanged ? existing?.exchange?.sharedEncryptKey : undefined
587
+ };
588
+ } else if (exchange?.bindVerifyKeysIntoDerivation === true && verifyKeyChanged && exchange.sharedEncryptKey != null) {
589
+ exchange = { ...exchange, sharedEncryptKey: undefined };
590
+ }
591
+ this.linkedClientKeys.set(linkedClientId, { verify, exchange });
592
+ }
593
+ async linkClientAndStore(input) {
594
+ await this.linkClient(input);
595
+ if (this.storage == null) {
596
+ return;
597
+ }
598
+ const {
599
+ linkedClientId,
600
+ verifyPublicKey,
601
+ exchangePublicKey,
602
+ saltString,
603
+ infoString,
604
+ bindVerifyKeysIntoDerivation
605
+ } = input;
606
+ await this.storage.updateJsonWithDef("linkedClientPublicKeys", {}, (current) => ({
607
+ ...current,
608
+ [linkedClientId]: {
609
+ ...current[linkedClientId],
610
+ ...verifyPublicKey != null ? { verifyPublicKey } : {},
611
+ ...exchangePublicKey != null ? { exchangePublicKey } : {},
612
+ ...saltString !== undefined ? { saltString } : {},
613
+ ...infoString !== undefined ? { infoString } : {},
614
+ ...bindVerifyKeysIntoDerivation !== undefined ? { bindVerifyKeysIntoDerivation } : {}
615
+ }
616
+ }));
617
+ }
618
+ hasLinkedClient(linkedClientId) {
619
+ return this.linkedClientKeys.has(linkedClientId);
620
+ }
621
+ getLinkedClientPublicKeys(linkedClientId) {
622
+ const linkedClient = this.linkedClientKeys.get(linkedClientId);
623
+ if (linkedClient == null) {
624
+ return;
625
+ }
626
+ return {
627
+ verifyPublicKey: linkedClient.verify?.publicKeySerialized,
628
+ exchangePublicKey: linkedClient.exchange?.publicKeySerialized
629
+ };
630
+ }
631
+ async unlinkClient(linkedClientId) {
632
+ this.linkedClientKeys.delete(linkedClientId);
633
+ if (this.storage != null) {
634
+ await this.storage.updateJson("linkedClientPublicKeys", (current) => {
635
+ if (current == null) {
636
+ return {};
637
+ }
638
+ const { [linkedClientId]: _removed, ...rest } = current;
639
+ return rest;
640
+ });
641
+ }
642
+ }
643
+ async unlinkAllClients() {
644
+ this.linkedClientKeys.clear();
645
+ if (this.storage != null) {
646
+ await this.storage.setJson("linkedClientPublicKeys", {});
647
+ }
648
+ }
649
+ async reset() {
650
+ this.linkedClientKeys.clear();
651
+ this.localExchangeKeyPair = undefined;
652
+ this.localVerifyKeyPair = undefined;
653
+ this.initialized = false;
654
+ if (this.storage != null) {
655
+ await this.storage.removeItem("linkedClientPublicKeys");
656
+ await this.storage.removeItem("localExchangeKeyPair");
657
+ await this.storage.removeItem("localVerifyKeyPair");
658
+ }
659
+ }
660
+ getLinkedClient(linkedClientId) {
661
+ const linkedClient = this.linkedClientKeys.get(linkedClientId);
662
+ if (linkedClient == null) {
663
+ throw new Error(`ClientCryptoKeyLink: No linked client for ${linkedClientId}`);
664
+ }
665
+ return linkedClient;
666
+ }
667
+ async getAesGcmKeyForLinkedClient(externalClientSourceId) {
668
+ const linkedClient = this.getLinkedClient(externalClientSourceId);
669
+ if (linkedClient.exchange?.sharedEncryptKey != null) {
670
+ return linkedClient.exchange.sharedEncryptKey;
671
+ }
672
+ if (linkedClient.exchange?.publicKey == null) {
673
+ throw new Error(`ClientCryptoKeyLink: No public exchange key set for ${externalClientSourceId}`);
674
+ }
675
+ const localExchangeKeyPair = await this.ensureLocalExchangeKeyPair();
676
+ let infoString = linkedClient.exchange.infoString;
677
+ if (linkedClient.exchange.bindVerifyKeysIntoDerivation === true) {
678
+ const linkedVerifyPublicKey = linkedClient.verify?.publicKeySerialized;
679
+ if (linkedVerifyPublicKey == null) {
680
+ throw new Error(`ClientCryptoKeyLink: Link for ${externalClientSourceId} binds verify keys into the derivation, but no verify public key is set`);
681
+ }
682
+ infoString = buildVerifyKeyBoundInfoString({
683
+ infoString: linkedClient.exchange.infoString,
684
+ verifyPublicKeys: [await this.getLocalVerifyPublicKey(), linkedVerifyPublicKey]
685
+ });
686
+ }
687
+ const sharedEncryptKey = await createAesGcmKeyFromX25519Keys({
688
+ internalX25519PrivateKey: localExchangeKeyPair.privateKey,
689
+ externalX25519PublicKey: linkedClient.exchange.publicKey,
690
+ saltString: linkedClient.exchange.saltString,
691
+ infoString
692
+ });
693
+ this.linkedClientKeys.set(externalClientSourceId, {
694
+ ...linkedClient,
695
+ exchange: {
696
+ ...linkedClient.exchange,
697
+ sharedEncryptKey
698
+ }
699
+ });
700
+ return sharedEncryptKey;
701
+ }
702
+ async encryptDataForLinkedClient({
703
+ dataToEncrypt,
704
+ linkedClientId
705
+ }) {
706
+ const key = await this.getAesGcmKeyForLinkedClient(linkedClientId);
707
+ return await encryptTextDataWithAesGcmKey({
708
+ dataToEncrypt,
709
+ aesGcmKey: key
710
+ });
711
+ }
712
+ async decryptDataFromLinkedClient({
713
+ dataToDecrypt,
714
+ linkedClientId
715
+ }) {
716
+ const key = await this.getAesGcmKeyForLinkedClient(linkedClientId);
717
+ return await decryptTextDataWithAesGcmKey({
718
+ dataToDecrypt,
719
+ aesGcmKey: key
720
+ });
721
+ }
722
+ async signAndEncryptDataForLinkedClient({
723
+ dataToEncrypt,
724
+ linkedClientId
725
+ }) {
726
+ const { signatureBase64 } = await this.signChallenge([dataToEncrypt]);
727
+ const encryptedData = await this.encryptDataForLinkedClient({
728
+ dataToEncrypt,
729
+ linkedClientId
730
+ });
731
+ return {
732
+ encryptedData,
733
+ signatureBase64
734
+ };
735
+ }
736
+ async decryptAndVerifyDataFromLinkedClient({
737
+ dataToDecrypt,
738
+ linkedClientId,
739
+ signatureBase64
740
+ }) {
741
+ const data = await this.decryptDataFromLinkedClient({
742
+ dataToDecrypt,
743
+ linkedClientId
744
+ });
745
+ const isValid = await this.verifyChallengeFromLinkedClient({
746
+ linkedClientId,
747
+ challenge: data,
748
+ signatureBase64
749
+ });
750
+ return { data, isValid };
751
+ }
752
+ async signChallenge(challenge) {
753
+ if (challenge.length === 0) {
754
+ throw new Error("Challenge must contain at least one string");
755
+ }
756
+ const localVerifyKeyPair = await this.ensureLocalVerifyKeyPair();
757
+ const signature = challenge.length > 1 ? await signCombinedTextDataWithKeyEd25519(challenge, localVerifyKeyPair.privateKey) : await signTextDataWithKeyEd25519(challenge[0], localVerifyKeyPair.privateKey);
758
+ return {
759
+ signatureBase64: base649.encode(signature)
760
+ };
761
+ }
762
+ async verifyChallengeFromLinkedClient({
763
+ linkedClientId,
764
+ challenge,
765
+ signatureBase64
766
+ }) {
767
+ const linkedClient = this.getLinkedClient(linkedClientId);
768
+ if (linkedClient.verify?.publicKey == null) {
769
+ throw new Error(`ClientCryptoKeyLink: No verify public key set for ${linkedClientId}`);
770
+ }
771
+ return await verifyWithKeyEd25519({
772
+ challenge,
773
+ signatureBase64,
774
+ publicKey: linkedClient.verify.publicKey
775
+ });
776
+ }
777
+ }
778
+ // src/storage_adapter/storage_adapter.types.ts
779
+ var EStorageAdapterType;
780
+ ((EStorageAdapterType2) => {
781
+ EStorageAdapterType2["string"] = "string";
782
+ EStorageAdapterType2["json"] = "json";
783
+ })(EStorageAdapterType ||= {});
784
+
43
785
  // src/storage_adapter/StorageAdapter.ts
44
786
  class StorageAdapter {
45
787
  implementation;
@@ -268,6 +1010,19 @@ function createTypedMemoryStorage_json(options) {
268
1010
  });
269
1011
  }
270
1012
  export {
1013
+ verifyWithKeyEd25519,
1014
+ signTextDataWithKeyEd25519,
1015
+ signCombinedTextDataWithKeyEd25519,
1016
+ serializeX25519Key_Raw,
1017
+ serializeX25519Key_Jwk,
1018
+ serializeEd25519Key_Raw,
1019
+ serializeEd25519Key_Jwk,
1020
+ importX25519Key,
1021
+ importEd25519Key,
1022
+ generateX25519KeyPair,
1023
+ generateEd25519KeyPair,
1024
+ encryptTextDataWithAesGcmKey,
1025
+ decryptTextDataWithAesGcmKey,
271
1026
  createWebSessionStorageMethods,
272
1027
  createWebSessionStorageAdapter,
273
1028
  createWebLocalStorageMethods,
@@ -277,6 +1032,7 @@ export {
277
1032
  createTypedStorage,
278
1033
  createTypedMemoryStorage_string,
279
1034
  createTypedMemoryStorage_json,
1035
+ createSharedBitsFromX25519,
280
1036
  createMemoryStorageMethods_string,
281
1037
  createMemoryStorageMethods_json,
282
1038
  createMemoryStorageAdapter_string,
@@ -284,6 +1040,22 @@ export {
284
1040
  createDurableObjectTypedStorage,
285
1041
  createDurableObjectStorageMethods,
286
1042
  createDurableObjectStorageAdapter,
1043
+ createAesGcmKeyFromX25519Keys,
1044
+ convertX25519RawDataStringToSerializedKeyData,
1045
+ convertX25519RawDataStringToObject,
1046
+ convertX25519JwkDataStringToSerializedKeyData,
1047
+ convertX25519JwkDataStringToObject,
1048
+ convertX25519FormattedStringToSerializedKeyData,
1049
+ convertX25519FormattedStringToObject,
1050
+ convertEd25519RawDataStringToSerializedKeyData,
1051
+ convertEd25519RawDataStringToObject,
1052
+ convertEd25519JwkDataStringToSerializedKeyData,
1053
+ convertEd25519JwkDataStringToObject,
1054
+ convertEd25519FormattedStringToSerializedKeyData,
1055
+ convertEd25519FormattedStringToObject,
1056
+ buildVerifyKeyBoundInfoString,
287
1057
  StorageAdapter,
288
- EStorageAdapterType
1058
+ EStorageAdapterType,
1059
+ DEFAULT_COMBINED_TEXT_DATA_SEPARATOR,
1060
+ ClientCryptoKeyLink
289
1061
  };