@mysten/seal 0.4.2 → 0.4.3

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 (56) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/cjs/bls12381.d.ts +5 -0
  3. package/dist/cjs/bls12381.js +38 -23
  4. package/dist/cjs/bls12381.js.map +2 -2
  5. package/dist/cjs/client.d.ts +18 -2
  6. package/dist/cjs/client.js +46 -1
  7. package/dist/cjs/client.js.map +2 -2
  8. package/dist/cjs/dem.js +1 -1
  9. package/dist/cjs/dem.js.map +2 -2
  10. package/dist/cjs/encrypt.js +12 -15
  11. package/dist/cjs/encrypt.js.map +2 -2
  12. package/dist/cjs/ibe.d.ts +3 -1
  13. package/dist/cjs/ibe.js +4 -4
  14. package/dist/cjs/ibe.js.map +2 -2
  15. package/dist/cjs/index.d.ts +1 -1
  16. package/dist/cjs/index.js.map +2 -2
  17. package/dist/cjs/kdf.js +6 -16
  18. package/dist/cjs/kdf.js.map +2 -2
  19. package/dist/cjs/key-server.d.ts +14 -0
  20. package/dist/cjs/key-server.js +10 -0
  21. package/dist/cjs/key-server.js.map +2 -2
  22. package/dist/cjs/utils.d.ts +7 -0
  23. package/dist/cjs/utils.js +12 -8
  24. package/dist/cjs/utils.js.map +2 -2
  25. package/dist/cjs/version.d.ts +1 -1
  26. package/dist/cjs/version.js +1 -1
  27. package/dist/cjs/version.js.map +1 -1
  28. package/dist/esm/bls12381.d.ts +5 -0
  29. package/dist/esm/bls12381.js +38 -23
  30. package/dist/esm/bls12381.js.map +2 -2
  31. package/dist/esm/client.d.ts +18 -2
  32. package/dist/esm/client.js +52 -2
  33. package/dist/esm/client.js.map +2 -2
  34. package/dist/esm/dem.js +1 -1
  35. package/dist/esm/dem.js.map +2 -2
  36. package/dist/esm/encrypt.js +12 -15
  37. package/dist/esm/encrypt.js.map +2 -2
  38. package/dist/esm/ibe.d.ts +3 -1
  39. package/dist/esm/ibe.js +4 -4
  40. package/dist/esm/ibe.js.map +2 -2
  41. package/dist/esm/index.d.ts +1 -1
  42. package/dist/esm/index.js.map +2 -2
  43. package/dist/esm/kdf.js +6 -16
  44. package/dist/esm/kdf.js.map +2 -2
  45. package/dist/esm/key-server.d.ts +14 -0
  46. package/dist/esm/key-server.js +11 -1
  47. package/dist/esm/key-server.js.map +2 -2
  48. package/dist/esm/utils.d.ts +7 -0
  49. package/dist/esm/utils.js +12 -8
  50. package/dist/esm/utils.js.map +2 -2
  51. package/dist/esm/version.d.ts +1 -1
  52. package/dist/esm/version.js +1 -1
  53. package/dist/esm/version.js.map +1 -1
  54. package/dist/tsconfig.esm.tsbuildinfo +1 -1
  55. package/dist/tsconfig.tsbuildinfo +1 -1
  56. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/client.ts"],
4
- "sourcesContent": ["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { EncryptedObject } from './bcs.js';\nimport { G1Element, G2Element } from './bls12381.js';\nimport { decrypt } from './decrypt.js';\nimport type { EncryptionInput } from './dem.js';\nimport { AesGcm256, Hmac256Ctr } from './dem.js';\nimport { DemType, encrypt, KemType } from './encrypt.js';\nimport {\n\tInconsistentKeyServersError,\n\tInvalidKeyServerError,\n\tInvalidThresholdError,\n\ttoMajorityError,\n} from './error.js';\nimport { BonehFranklinBLS12381Services, DST } from './ibe.js';\nimport { KeyServerType, retrieveKeyServers, verifyKeyServer } from './key-server.js';\nimport type { KeyServer } from './key-server.js';\nimport { fetchKeysForAllIds } from './keys.js';\nimport type { SessionKey } from './session-key.js';\nimport type { KeyCacheKey, SealCompatibleClient } from './types.js';\nimport { createFullId } from './utils.js';\n\n/**\n * Configuration options for initializing a SealClient\n * @property serverObjectIds: Array of object IDs for the key servers to use.\n * @property verifyKeyServers: Whether to verify the key servers' authenticity.\n * \t Should be false if servers are pre-verified (e.g., getAllowlistedKeyServers).\n * \t Defaults to true.\n * @property timeout: Timeout in milliseconds for network requests. Defaults to 10 seconds.\n */\n\nexport interface SealClientExtensionOptions {\n\tserverObjectIds: string[];\n\tverifyKeyServers?: boolean;\n\ttimeout?: number;\n}\n\nexport interface SealClientOptions extends SealClientExtensionOptions {\n\tsuiClient: SealCompatibleClient;\n}\n\nexport class SealClient {\n\t#suiClient: SealCompatibleClient;\n\t#serverObjectIds: string[];\n\t#verifyKeyServers: boolean;\n\t#keyServers: Promise<KeyServer[]> | null = null;\n\t// A caching map for: fullId:object_id -> partial key.\n\t#cachedKeys = new Map<KeyCacheKey, G1Element>();\n\t#timeout: number;\n\n\tconstructor(options: SealClientOptions) {\n\t\tthis.#suiClient = options.suiClient;\n\t\tthis.#serverObjectIds = options.serverObjectIds;\n\t\tthis.#verifyKeyServers = options.verifyKeyServers ?? true;\n\t\tthis.#timeout = options.timeout ?? 10_000;\n\t}\n\n\tstatic experimental_asClientExtension(options: SealClientExtensionOptions) {\n\t\treturn {\n\t\t\tname: 'seal' as const,\n\t\t\tregister: (client: SealCompatibleClient) => {\n\t\t\t\treturn new SealClient({\n\t\t\t\t\tsuiClient: client,\n\t\t\t\t\t...options,\n\t\t\t\t});\n\t\t\t},\n\t\t};\n\t}\n\n\t/**\n\t * Return an encrypted message under the identity.\n\t *\n\t * @param kemType - The type of KEM to use.\n\t * @param demType - The type of DEM to use.\n\t * @param threshold - The threshold for the TSS encryption.\n\t * @param packageId - the packageId namespace.\n\t * @param id - the identity to use.\n\t * @param data - the data to encrypt.\n\t * @param aad - optional additional authenticated data.\n\t * @returns The bcs bytes of the encrypted object containing all metadata and the 256-bit symmetric key that was used to encrypt the object.\n\t * \tSince the symmetric key can be used to decrypt, it should not be shared but can be used e.g. for backup.\n\t */\n\tasync encrypt({\n\t\tkemType = KemType.BonehFranklinBLS12381DemCCA,\n\t\tdemType = DemType.AesGcm256,\n\t\tthreshold,\n\t\tpackageId,\n\t\tid,\n\t\tdata,\n\t\taad = new Uint8Array(),\n\t}: {\n\t\tkemType?: KemType;\n\t\tdemType?: DemType;\n\t\tthreshold: number;\n\t\tpackageId: string;\n\t\tid: string;\n\t\tdata: Uint8Array;\n\t\taad?: Uint8Array;\n\t}) {\n\t\t// TODO: Verify that packageId is first version of its package (else throw error).\n\t\treturn encrypt({\n\t\t\tkeyServers: await this.getKeyServers(),\n\t\t\tkemType,\n\t\t\tthreshold,\n\t\t\tpackageId,\n\t\t\tid,\n\t\t\tencryptionInput: this.#createEncryptionInput(demType, data, aad),\n\t\t});\n\t}\n\n\t#createEncryptionInput(type: DemType, data: Uint8Array, aad: Uint8Array): EncryptionInput {\n\t\tswitch (type) {\n\t\t\tcase DemType.AesGcm256:\n\t\t\t\treturn new AesGcm256(data, aad);\n\t\t\tcase DemType.Hmac256Ctr:\n\t\t\t\treturn new Hmac256Ctr(data, aad);\n\t\t}\n\t}\n\n\t/**\n\t * Decrypt the given encrypted bytes using cached keys.\n\t * Calls fetchKeys in case one or more of the required keys is not cached yet.\n\t * The function throws an error if the client's key servers are not a subset of\n\t * the encrypted object's key servers (including the same weights) or if the\n\t * threshold cannot be met.\n\t *\n\t * @param data - The encrypted bytes to decrypt.\n\t * @param sessionKey - The session key to use.\n\t * @param txBytes - The transaction bytes to use (that calls seal_approve* functions).\n\t * @returns - The decrypted plaintext corresponding to ciphertext.\n\t */\n\tasync decrypt({\n\t\tdata,\n\t\tsessionKey,\n\t\ttxBytes,\n\t}: {\n\t\tdata: Uint8Array;\n\t\tsessionKey: SessionKey;\n\t\ttxBytes: Uint8Array;\n\t}) {\n\t\tconst encryptedObject = EncryptedObject.parse(data);\n\n\t\tthis.#validateEncryptionServices(\n\t\t\tencryptedObject.services.map((s) => s[0]),\n\t\t\tencryptedObject.threshold,\n\t\t);\n\n\t\tawait this.fetchKeys({\n\t\t\tids: [encryptedObject.id],\n\t\t\ttxBytes,\n\t\t\tsessionKey,\n\t\t\tthreshold: encryptedObject.threshold,\n\t\t});\n\n\t\treturn decrypt({ encryptedObject, keys: this.#cachedKeys });\n\t}\n\n\t#validateEncryptionServices(services: string[], threshold: number) {\n\t\t// Check that the client's key servers are a subset of the encrypted object's key servers.\n\t\tconst serverObjectIdsMap = new Map<string, number>();\n\t\tfor (const objectId of this.#serverObjectIds) {\n\t\t\tserverObjectIdsMap.set(objectId, (serverObjectIdsMap.get(objectId) ?? 0) + 1);\n\t\t}\n\t\tconst servicesMap = new Map<string, number>();\n\t\tfor (const service of services) {\n\t\t\tservicesMap.set(service, (servicesMap.get(service) ?? 0) + 1);\n\t\t}\n\t\tfor (const [objectId, count] of serverObjectIdsMap) {\n\t\t\tif (servicesMap.get(objectId) !== count) {\n\t\t\t\tthrow new InconsistentKeyServersError(\n\t\t\t\t\t`Client's key servers must be a subset of the encrypted object's key servers`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\t// Check that the threshold can be met with the client's key servers.\n\t\tif (threshold > this.#serverObjectIds.length) {\n\t\t\tthrow new InvalidThresholdError(\n\t\t\t\t`Invalid threshold ${threshold} for ${this.#serverObjectIds.length} servers`,\n\t\t\t);\n\t\t}\n\t}\n\n\tasync getKeyServers() {\n\t\tif (!this.#keyServers) {\n\t\t\tthis.#keyServers = this.#loadKeyServers().catch((error) => {\n\t\t\t\tthis.#keyServers = null;\n\t\t\t\tthrow error;\n\t\t\t});\n\t\t}\n\n\t\treturn this.#keyServers;\n\t}\n\n\tasync #loadKeyServers(): Promise<KeyServer[]> {\n\t\tconst keyServers = await retrieveKeyServers({\n\t\t\tobjectIds: this.#serverObjectIds,\n\t\t\tclient: this.#suiClient,\n\t\t});\n\n\t\tif (keyServers.length === 0) {\n\t\t\tthrow new InvalidKeyServerError('No key servers found');\n\t\t}\n\n\t\tif (this.#verifyKeyServers) {\n\t\t\tawait Promise.all(\n\t\t\t\tkeyServers.map(async (server) => {\n\t\t\t\t\tif (!(await verifyKeyServer(server, this.#timeout))) {\n\t\t\t\t\t\tthrow new InvalidKeyServerError(`Key server ${server.objectId} is not valid`);\n\t\t\t\t\t}\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\treturn keyServers;\n\t}\n\n\t/**\n\t * Fetch keys from the key servers and update the cache.\n\t *\n\t * It is recommended to call this function once for all ids of all encrypted obejcts if\n\t * there are multiple, then call decrypt for each object. This avoids calling fetchKey\n\t * individually for each decrypt.\n\t *\n\t * @param ids - The ids of the encrypted objects.\n\t * @param txBytes - The transaction bytes to use (that calls seal_approve* functions).\n\t * @param sessionKey - The session key to use.\n\t * @param threshold - The threshold for the TSS encryptions. The function returns when a threshold of key servers had returned keys for all ids.\n\t */\n\tasync fetchKeys({\n\t\tids,\n\t\ttxBytes,\n\t\tsessionKey,\n\t\tthreshold,\n\t}: {\n\t\tids: string[];\n\t\ttxBytes: Uint8Array;\n\t\tsessionKey: SessionKey;\n\t\tthreshold: number;\n\t}) {\n\t\tconst keyServers = await this.getKeyServers();\n\t\tif (threshold > keyServers.length || threshold < 1 || keyServers.length < 1) {\n\t\t\tthrow new InvalidThresholdError(\n\t\t\t\t`Invalid threshold ${threshold} for ${keyServers.length} servers`,\n\t\t\t);\n\t\t}\n\n\t\tlet completedServerCount = 0;\n\t\tconst remainingKeyServers = new Set<KeyServer>();\n\t\tconst fullIds = ids.map((id) => createFullId(DST, sessionKey.getPackageId(), id));\n\n\t\t// Count a server as completed if it has keys for all fullIds.\n\t\t// Duplicated key server ids will be counted towards the threshold.\n\t\tfor (const server of keyServers) {\n\t\t\tlet hasAllKeys = true;\n\t\t\tfor (const fullId of fullIds) {\n\t\t\t\tif (!this.#cachedKeys.has(`${fullId}:${server.objectId}`)) {\n\t\t\t\t\thasAllKeys = false;\n\t\t\t\t\tremainingKeyServers.add(server);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (hasAllKeys) {\n\t\t\t\tcompletedServerCount++;\n\t\t\t}\n\t\t}\n\n\t\t// Return early if we have enough keys from cache.\n\t\tif (completedServerCount >= threshold) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Check server validities.\n\t\tfor (const server of remainingKeyServers) {\n\t\t\tif (server.keyType !== KeyServerType.BonehFranklinBLS12381) {\n\t\t\t\tthrow new InvalidKeyServerError(\n\t\t\t\t\t`Server ${server.objectId} has invalid key type: ${server.keyType}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tconst cert = await sessionKey.getCertificate();\n\t\tconst signedRequest = await sessionKey.createRequestParams(txBytes);\n\n\t\tconst controller = new AbortController();\n\t\tconst errors: Error[] = [];\n\n\t\tconst keyFetches = [...remainingKeyServers].map(async (server) => {\n\t\t\ttry {\n\t\t\t\tconst allKeys = await fetchKeysForAllIds(\n\t\t\t\t\tserver.url,\n\t\t\t\t\tsignedRequest.requestSignature,\n\t\t\t\t\ttxBytes,\n\t\t\t\t\tsignedRequest.decryptionKey,\n\t\t\t\t\tcert,\n\t\t\t\t\tthis.#timeout,\n\t\t\t\t\tcontroller.signal,\n\t\t\t\t);\n\t\t\t\t// Check validity of the keys and add them to the cache.\n\t\t\t\tconst receivedIds = new Set<string>();\n\t\t\t\tfor (const { fullId, key } of allKeys) {\n\t\t\t\t\tconst keyElement = G1Element.fromBytes(key);\n\t\t\t\t\tif (\n\t\t\t\t\t\t!BonehFranklinBLS12381Services.verifyUserSecretKey(\n\t\t\t\t\t\t\tkeyElement,\n\t\t\t\t\t\t\tfullId,\n\t\t\t\t\t\t\tG2Element.fromBytes(server.pk),\n\t\t\t\t\t\t)\n\t\t\t\t\t) {\n\t\t\t\t\t\tconsole.warn('Received invalid key from key server ' + server.objectId);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tthis.#cachedKeys.set(`${fullId}:${server.objectId}`, keyElement);\n\t\t\t\t\treceivedIds.add(fullId);\n\t\t\t\t}\n\n\t\t\t\t// Check if all the receivedIds are consistent with the requested fullIds.\n\t\t\t\t// If so, consider the key server got all keys and mark as completed.\n\t\t\t\tconst expectedIds = new Set(fullIds);\n\t\t\t\tconst hasAllKeys =\n\t\t\t\t\treceivedIds.size === expectedIds.size &&\n\t\t\t\t\t[...receivedIds].every((id) => expectedIds.has(id));\n\n\t\t\t\t// Return early if the completed servers is more than threshold.\n\t\t\t\tif (hasAllKeys) {\n\t\t\t\t\tcompletedServerCount++;\n\t\t\t\t\tif (completedServerCount >= threshold) {\n\t\t\t\t\t\tcontroller.abort();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tif (!controller.signal.aborted) {\n\t\t\t\t\terrors.push(error as Error);\n\t\t\t\t}\n\t\t\t\t// If there are too many errors that the threshold is not attainable, return early with error.\n\t\t\t\tif (remainingKeyServers.size - errors.length < threshold - completedServerCount) {\n\t\t\t\t\tcontroller.abort(error);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tawait Promise.allSettled(keyFetches);\n\n\t\tif (completedServerCount < threshold) {\n\t\t\tthrow toMajorityError(errors);\n\t\t}\n\t}\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { EncryptedObject } from './bcs.js';\nimport { G1Element, G2Element } from './bls12381.js';\nimport { decrypt } from './decrypt.js';\nimport type { EncryptionInput } from './dem.js';\nimport { AesGcm256, Hmac256Ctr } from './dem.js';\nimport { DemType, encrypt, KemType } from './encrypt.js';\nimport {\n\tInconsistentKeyServersError,\n\tInvalidKeyServerError,\n\tInvalidThresholdError,\n\ttoMajorityError,\n} from './error.js';\nimport { BonehFranklinBLS12381Services, DST } from './ibe.js';\nimport {\n\tBonehFranklinBLS12381DerivedKey,\n\tKeyServerType,\n\tretrieveKeyServers,\n\tverifyKeyServer,\n} from './key-server.js';\nimport type { DerivedKey, KeyServer } from './key-server.js';\nimport { fetchKeysForAllIds } from './keys.js';\nimport type { SessionKey } from './session-key.js';\nimport type { KeyCacheKey, SealCompatibleClient } from './types.js';\nimport { createFullId } from './utils.js';\n\n/**\n * Configuration options for initializing a SealClient\n * @property serverObjectIds: Array of object IDs for the key servers to use.\n * @property verifyKeyServers: Whether to verify the key servers' authenticity.\n * \t Should be false if servers are pre-verified (e.g., getAllowlistedKeyServers).\n * \t Defaults to true.\n * @property timeout: Timeout in milliseconds for network requests. Defaults to 10 seconds.\n */\n\nexport interface SealClientExtensionOptions {\n\tserverObjectIds: string[];\n\tverifyKeyServers?: boolean;\n\ttimeout?: number;\n}\n\nexport interface SealClientOptions extends SealClientExtensionOptions {\n\tsuiClient: SealCompatibleClient;\n}\n\nexport class SealClient {\n\t#suiClient: SealCompatibleClient;\n\t#serverObjectIds: string[];\n\t#verifyKeyServers: boolean;\n\t#keyServers: Promise<KeyServer[]> | null = null;\n\t// A caching map for: fullId:object_id -> partial key.\n\t#cachedKeys = new Map<KeyCacheKey, G1Element>();\n\t#timeout: number;\n\n\tconstructor(options: SealClientOptions) {\n\t\tthis.#suiClient = options.suiClient;\n\t\tthis.#serverObjectIds = options.serverObjectIds;\n\t\tthis.#verifyKeyServers = options.verifyKeyServers ?? true;\n\t\tthis.#timeout = options.timeout ?? 10_000;\n\t}\n\n\tstatic experimental_asClientExtension(options: SealClientExtensionOptions) {\n\t\treturn {\n\t\t\tname: 'seal' as const,\n\t\t\tregister: (client: SealCompatibleClient) => {\n\t\t\t\treturn new SealClient({\n\t\t\t\t\tsuiClient: client,\n\t\t\t\t\t...options,\n\t\t\t\t});\n\t\t\t},\n\t\t};\n\t}\n\n\t/**\n\t * Return an encrypted message under the identity.\n\t *\n\t * @param kemType - The type of KEM to use.\n\t * @param demType - The type of DEM to use.\n\t * @param threshold - The threshold for the TSS encryption.\n\t * @param packageId - the packageId namespace.\n\t * @param id - the identity to use.\n\t * @param data - the data to encrypt.\n\t * @param aad - optional additional authenticated data.\n\t * @returns The bcs bytes of the encrypted object containing all metadata and the 256-bit symmetric key that was used to encrypt the object.\n\t * \tSince the symmetric key can be used to decrypt, it should not be shared but can be used e.g. for backup.\n\t */\n\tasync encrypt({\n\t\tkemType = KemType.BonehFranklinBLS12381DemCCA,\n\t\tdemType = DemType.AesGcm256,\n\t\tthreshold,\n\t\tpackageId,\n\t\tid,\n\t\tdata,\n\t\taad = new Uint8Array(),\n\t}: {\n\t\tkemType?: KemType;\n\t\tdemType?: DemType;\n\t\tthreshold: number;\n\t\tpackageId: string;\n\t\tid: string;\n\t\tdata: Uint8Array;\n\t\taad?: Uint8Array;\n\t}) {\n\t\t// TODO: Verify that packageId is first version of its package (else throw error).\n\t\treturn encrypt({\n\t\t\tkeyServers: await this.getKeyServers(),\n\t\t\tkemType,\n\t\t\tthreshold,\n\t\t\tpackageId,\n\t\t\tid,\n\t\t\tencryptionInput: this.#createEncryptionInput(demType, data, aad),\n\t\t});\n\t}\n\n\t#createEncryptionInput(type: DemType, data: Uint8Array, aad: Uint8Array): EncryptionInput {\n\t\tswitch (type) {\n\t\t\tcase DemType.AesGcm256:\n\t\t\t\treturn new AesGcm256(data, aad);\n\t\t\tcase DemType.Hmac256Ctr:\n\t\t\t\treturn new Hmac256Ctr(data, aad);\n\t\t}\n\t}\n\n\t/**\n\t * Decrypt the given encrypted bytes using cached keys.\n\t * Calls fetchKeys in case one or more of the required keys is not cached yet.\n\t * The function throws an error if the client's key servers are not a subset of\n\t * the encrypted object's key servers (including the same weights) or if the\n\t * threshold cannot be met.\n\t *\n\t * @param data - The encrypted bytes to decrypt.\n\t * @param sessionKey - The session key to use.\n\t * @param txBytes - The transaction bytes to use (that calls seal_approve* functions).\n\t * @returns - The decrypted plaintext corresponding to ciphertext.\n\t */\n\tasync decrypt({\n\t\tdata,\n\t\tsessionKey,\n\t\ttxBytes,\n\t}: {\n\t\tdata: Uint8Array;\n\t\tsessionKey: SessionKey;\n\t\ttxBytes: Uint8Array;\n\t}) {\n\t\tconst encryptedObject = EncryptedObject.parse(data);\n\n\t\tthis.#validateEncryptionServices(\n\t\t\tencryptedObject.services.map((s) => s[0]),\n\t\t\tencryptedObject.threshold,\n\t\t);\n\n\t\tawait this.fetchKeys({\n\t\t\tids: [encryptedObject.id],\n\t\t\ttxBytes,\n\t\t\tsessionKey,\n\t\t\tthreshold: encryptedObject.threshold,\n\t\t});\n\n\t\treturn decrypt({ encryptedObject, keys: this.#cachedKeys });\n\t}\n\n\t#validateEncryptionServices(services: string[], threshold: number) {\n\t\t// Check that the client's key servers are a subset of the encrypted object's key servers.\n\t\tconst serverObjectIdsMap = new Map<string, number>();\n\t\tfor (const objectId of this.#serverObjectIds) {\n\t\t\tserverObjectIdsMap.set(objectId, (serverObjectIdsMap.get(objectId) ?? 0) + 1);\n\t\t}\n\t\tconst servicesMap = new Map<string, number>();\n\t\tfor (const service of services) {\n\t\t\tservicesMap.set(service, (servicesMap.get(service) ?? 0) + 1);\n\t\t}\n\t\tfor (const [objectId, count] of serverObjectIdsMap) {\n\t\t\tif (servicesMap.get(objectId) !== count) {\n\t\t\t\tthrow new InconsistentKeyServersError(\n\t\t\t\t\t`Client's key servers must be a subset of the encrypted object's key servers`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\t// Check that the threshold can be met with the client's key servers.\n\t\tif (threshold > this.#serverObjectIds.length) {\n\t\t\tthrow new InvalidThresholdError(\n\t\t\t\t`Invalid threshold ${threshold} for ${this.#serverObjectIds.length} servers`,\n\t\t\t);\n\t\t}\n\t}\n\n\tasync getKeyServers() {\n\t\tif (!this.#keyServers) {\n\t\t\tthis.#keyServers = this.#loadKeyServers().catch((error) => {\n\t\t\t\tthis.#keyServers = null;\n\t\t\t\tthrow error;\n\t\t\t});\n\t\t}\n\n\t\treturn this.#keyServers;\n\t}\n\n\tasync #loadKeyServers(): Promise<KeyServer[]> {\n\t\tconst keyServers = await retrieveKeyServers({\n\t\t\tobjectIds: this.#serverObjectIds,\n\t\t\tclient: this.#suiClient,\n\t\t});\n\n\t\tif (keyServers.length === 0) {\n\t\t\tthrow new InvalidKeyServerError('No key servers found');\n\t\t}\n\n\t\tif (this.#verifyKeyServers) {\n\t\t\tawait Promise.all(\n\t\t\t\tkeyServers.map(async (server) => {\n\t\t\t\t\tif (!(await verifyKeyServer(server, this.#timeout))) {\n\t\t\t\t\t\tthrow new InvalidKeyServerError(`Key server ${server.objectId} is not valid`);\n\t\t\t\t\t}\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\treturn keyServers;\n\t}\n\n\t/**\n\t * Fetch keys from the key servers and update the cache.\n\t *\n\t * It is recommended to call this function once for all ids of all encrypted objects if\n\t * there are multiple, then call decrypt for each object. This avoids calling fetchKey\n\t * individually for each decrypt.\n\t *\n\t * @param ids - The ids of the encrypted objects.\n\t * @param txBytes - The transaction bytes to use (that calls seal_approve* functions).\n\t * @param sessionKey - The session key to use.\n\t * @param threshold - The threshold for the TSS encryptions. The function returns when a threshold of key servers had returned keys for all ids.\n\t */\n\tasync fetchKeys({\n\t\tids,\n\t\ttxBytes,\n\t\tsessionKey,\n\t\tthreshold,\n\t}: {\n\t\tids: string[];\n\t\ttxBytes: Uint8Array;\n\t\tsessionKey: SessionKey;\n\t\tthreshold: number;\n\t}) {\n\t\tconst keyServers = await this.getKeyServers();\n\t\tif (threshold > keyServers.length || threshold < 1 || keyServers.length < 1) {\n\t\t\tthrow new InvalidThresholdError(\n\t\t\t\t`Invalid threshold ${threshold} for ${keyServers.length} servers`,\n\t\t\t);\n\t\t}\n\n\t\tlet completedServerCount = 0;\n\t\tconst remainingKeyServers = new Set<KeyServer>();\n\t\tconst fullIds = ids.map((id) => createFullId(DST, sessionKey.getPackageId(), id));\n\n\t\t// Count a server as completed if it has keys for all fullIds.\n\t\t// Duplicated key server ids will be counted towards the threshold.\n\t\tfor (const server of keyServers) {\n\t\t\tlet hasAllKeys = true;\n\t\t\tfor (const fullId of fullIds) {\n\t\t\t\tif (!this.#cachedKeys.has(`${fullId}:${server.objectId}`)) {\n\t\t\t\t\thasAllKeys = false;\n\t\t\t\t\tremainingKeyServers.add(server);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (hasAllKeys) {\n\t\t\t\tcompletedServerCount++;\n\t\t\t}\n\t\t}\n\n\t\t// Return early if we have enough keys from cache.\n\t\tif (completedServerCount >= threshold) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Check server validities.\n\t\tfor (const server of remainingKeyServers) {\n\t\t\tif (server.keyType !== KeyServerType.BonehFranklinBLS12381) {\n\t\t\t\tthrow new InvalidKeyServerError(\n\t\t\t\t\t`Server ${server.objectId} has invalid key type: ${server.keyType}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tconst cert = await sessionKey.getCertificate();\n\t\tconst signedRequest = await sessionKey.createRequestParams(txBytes);\n\n\t\tconst controller = new AbortController();\n\t\tconst errors: Error[] = [];\n\n\t\tconst keyFetches = [...remainingKeyServers].map(async (server) => {\n\t\t\ttry {\n\t\t\t\tconst allKeys = await fetchKeysForAllIds(\n\t\t\t\t\tserver.url,\n\t\t\t\t\tsignedRequest.requestSignature,\n\t\t\t\t\ttxBytes,\n\t\t\t\t\tsignedRequest.decryptionKey,\n\t\t\t\t\tcert,\n\t\t\t\t\tthis.#timeout,\n\t\t\t\t\tcontroller.signal,\n\t\t\t\t);\n\t\t\t\t// Check validity of the keys and add them to the cache.\n\t\t\t\tconst receivedIds = new Set<string>();\n\t\t\t\tfor (const { fullId, key } of allKeys) {\n\t\t\t\t\tconst keyElement = G1Element.fromBytes(key);\n\t\t\t\t\tif (\n\t\t\t\t\t\t!BonehFranklinBLS12381Services.verifyUserSecretKey(\n\t\t\t\t\t\t\tkeyElement,\n\t\t\t\t\t\t\tfullId,\n\t\t\t\t\t\t\tG2Element.fromBytes(server.pk),\n\t\t\t\t\t\t)\n\t\t\t\t\t) {\n\t\t\t\t\t\tconsole.warn('Received invalid key from key server ' + server.objectId);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tthis.#cachedKeys.set(`${fullId}:${server.objectId}`, keyElement);\n\t\t\t\t\treceivedIds.add(fullId);\n\t\t\t\t}\n\n\t\t\t\t// Check if all the receivedIds are consistent with the requested fullIds.\n\t\t\t\t// If so, consider the key server got all keys and mark as completed.\n\t\t\t\tconst expectedIds = new Set(fullIds);\n\t\t\t\tconst hasAllKeys =\n\t\t\t\t\treceivedIds.size === expectedIds.size &&\n\t\t\t\t\t[...receivedIds].every((id) => expectedIds.has(id));\n\n\t\t\t\t// Return early if the completed servers is more than threshold.\n\t\t\t\tif (hasAllKeys) {\n\t\t\t\t\tcompletedServerCount++;\n\t\t\t\t\tif (completedServerCount >= threshold) {\n\t\t\t\t\t\tcontroller.abort();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tif (!controller.signal.aborted) {\n\t\t\t\t\terrors.push(error as Error);\n\t\t\t\t}\n\t\t\t\t// If there are too many errors that the threshold is not attainable, return early with error.\n\t\t\t\tif (remainingKeyServers.size - errors.length < threshold - completedServerCount) {\n\t\t\t\t\tcontroller.abort(error);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tawait Promise.allSettled(keyFetches);\n\n\t\tif (completedServerCount < threshold) {\n\t\t\tthrow toMajorityError(errors);\n\t\t}\n\t}\n\n\t/**\n\t * Get derived keys from the given services.\n\t *\n\t * @param id - The id of the encrypted object.\n\t * @param txBytes - The transaction bytes to use (that calls seal_approve* functions).\n\t * @param sessionKey - The session key to use.\n\t * @param threshold - The threshold.\n\t * @returns - Derived keys for the given services that are in the cache as a \"service object ID\" -> derived key map. If the call is succesful, exactly threshold keys will be returned.\n\t */\n\tasync getDerivedKeys({\n\t\tkemType = KemType.BonehFranklinBLS12381DemCCA,\n\t\tid,\n\t\ttxBytes,\n\t\tsessionKey,\n\t\tthreshold,\n\t}: {\n\t\tkemType?: KemType;\n\t\tid: string;\n\t\ttxBytes: Uint8Array;\n\t\tsessionKey: SessionKey;\n\t\tthreshold: number;\n\t}): Promise<Map<string, DerivedKey>> {\n\t\tswitch (kemType) {\n\t\t\tcase KemType.BonehFranklinBLS12381DemCCA:\n\t\t\t\tconst keyServers = await this.getKeyServers();\n\t\t\t\tif (threshold > this.#serverObjectIds.length) {\n\t\t\t\t\tthrow new InvalidThresholdError(\n\t\t\t\t\t\t`Invalid threshold ${threshold} for ${this.#serverObjectIds.length} servers`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tawait this.fetchKeys({\n\t\t\t\t\tids: [id],\n\t\t\t\t\ttxBytes,\n\t\t\t\t\tsessionKey,\n\t\t\t\t\tthreshold,\n\t\t\t\t});\n\n\t\t\t\t// After calling fetchKeys, we can be sure that there are at least `threshold` of the required keys in the cache.\n\t\t\t\t// It is also checked there that the KeyServerType is BonehFranklinBLS12381 for all services.\n\n\t\t\t\tconst fullId = createFullId(DST, sessionKey.getPackageId(), id);\n\n\t\t\t\tconst derivedKeys = new Map<string, DerivedKey>();\n\t\t\t\tlet servicesAdded = 0;\n\t\t\t\tfor (const keyServer of keyServers) {\n\t\t\t\t\t// The code below assumes that the KeyServerType is BonehFranklinBLS12381.\n\t\t\t\t\tconst cachedKey = this.#cachedKeys.get(`${fullId}:${keyServer.objectId}`);\n\t\t\t\t\tif (cachedKey) {\n\t\t\t\t\t\tderivedKeys.set(keyServer.objectId, new BonehFranklinBLS12381DerivedKey(cachedKey));\n\t\t\t\t\t\tif (++servicesAdded === threshold) {\n\t\t\t\t\t\t\t// We have enough keys, so we can stop.\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn derivedKeys;\n\t\t}\n\t}\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/dist/esm/dem.js CHANGED
@@ -131,7 +131,7 @@ class Hmac256Ctr {
131
131
  const result = Uint8Array.from({ length: msg.length }, () => 0);
132
132
  const encryptionKey = hmac(sha3_256, key, EncryptionKeyTag);
133
133
  for (let i = 0; i * blockSize < msg.length; i++) {
134
- const block = msg.slice(i * blockSize, (i + 1) * blockSize);
134
+ const block = msg.subarray(i * blockSize, (i + 1) * blockSize);
135
135
  const mask = hmac(sha3_256, encryptionKey, toBytes(i));
136
136
  const encryptedBlock = xorUnchecked(block, mask);
137
137
  result.set(encryptedBlock, i * blockSize);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/dem.ts"],
4
- "sourcesContent": ["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { bcs } from '@mysten/bcs';\nimport { equalBytes } from '@noble/curves/abstract/utils';\nimport { hmac } from '@noble/hashes/hmac';\nimport { sha3_256 } from '@noble/hashes/sha3';\n\nimport type { Ciphertext } from './bcs.js';\nimport { InvalidCiphertextError } from './error.js';\nimport { xorUnchecked } from './utils.js';\n\n// Use a fixed IV for AES.\nexport const iv = Uint8Array.from([\n\t138, 55, 153, 253, 198, 46, 121, 219, 160, 128, 89, 7, 214, 156, 148, 220,\n]);\n\nasync function generateAesKey(): Promise<Uint8Array> {\n\tconst key = await crypto.subtle.generateKey(\n\t\t{\n\t\t\tname: 'AES-GCM',\n\t\t\tlength: 256,\n\t\t},\n\t\ttrue,\n\t\t['encrypt', 'decrypt'],\n\t);\n\treturn await crypto.subtle.exportKey('raw', key).then((keyData) => new Uint8Array(keyData));\n}\n\nexport interface EncryptionInput {\n\tencrypt(key: Uint8Array): Promise<typeof Ciphertext.$inferInput>;\n\tgenerateKey(): Promise<Uint8Array>;\n}\n\nexport class AesGcm256 implements EncryptionInput {\n\treadonly plaintext: Uint8Array;\n\treadonly aad: Uint8Array;\n\n\tconstructor(msg: Uint8Array, aad: Uint8Array) {\n\t\tthis.plaintext = msg;\n\t\tthis.aad = aad;\n\t}\n\n\tgenerateKey(): Promise<Uint8Array> {\n\t\treturn generateAesKey();\n\t}\n\n\tasync encrypt(key: Uint8Array): Promise<typeof Ciphertext.$inferInput> {\n\t\tconst aesCryptoKey = await crypto.subtle.importKey('raw', key, 'AES-GCM', false, ['encrypt']);\n\n\t\tconst blob = new Uint8Array(\n\t\t\tawait crypto.subtle.encrypt(\n\t\t\t\t{\n\t\t\t\t\tname: 'AES-GCM',\n\t\t\t\t\tiv,\n\t\t\t\t\tadditionalData: this.aad,\n\t\t\t\t},\n\t\t\t\taesCryptoKey,\n\t\t\t\tthis.plaintext,\n\t\t\t),\n\t\t);\n\n\t\treturn {\n\t\t\tAes256Gcm: {\n\t\t\t\tblob,\n\t\t\t\taad: this.aad ?? [],\n\t\t\t},\n\t\t};\n\t}\n\n\tstatic async decrypt(\n\t\tkey: Uint8Array,\n\t\tciphertext: typeof Ciphertext.$inferInput,\n\t): Promise<Uint8Array> {\n\t\tif (!('Aes256Gcm' in ciphertext)) {\n\t\t\tthrow new InvalidCiphertextError(`Invalid ciphertext ${ciphertext}`);\n\t\t}\n\n\t\tconst aesCryptoKey = await crypto.subtle.importKey('raw', key, 'AES-GCM', false, ['decrypt']);\n\n\t\treturn new Uint8Array(\n\t\t\tawait crypto.subtle.decrypt(\n\t\t\t\t{\n\t\t\t\t\tname: 'AES-GCM',\n\t\t\t\t\tiv,\n\t\t\t\t\tadditionalData: new Uint8Array(ciphertext.Aes256Gcm.aad ?? []),\n\t\t\t\t},\n\t\t\t\taesCryptoKey,\n\t\t\t\tnew Uint8Array(ciphertext.Aes256Gcm.blob),\n\t\t\t),\n\t\t);\n\t}\n}\n\nexport class Plain implements EncryptionInput {\n\tasync encrypt(_key: Uint8Array): Promise<typeof Ciphertext.$inferInput> {\n\t\treturn {\n\t\t\tPlain: {},\n\t\t};\n\t}\n\n\tgenerateKey(): Promise<Uint8Array> {\n\t\treturn generateAesKey();\n\t}\n}\n\n/**\n * Authenticated encryption using CTR mode with HMAC-SHA3-256 as a PRF.\n * 1. Derive an encryption key, <i>k<sub>1</sub> = <b>hmac</b>(key, 1)</i>.\n * 2. Chunk the message into blocks of 32 bytes, <i>m = m<sub>1</sub> || ... || m<sub>n</sub></i>.\n * 3. Let the ciphertext be defined by <i>c = c<sub>1</sub> || ... || c<sub>n</sub></i> where <i>c<sub>i</sub> = m<sub>i</sub> \u2295 <b>hmac</b>(k<sub>1</sub>, i)</i>.\n * 4. Compute a MAC over the AAD and the ciphertext, <i>mac = <b>hmac</b>(k<sub>2</sub>, aad || c) where k<sub>2</sub> = <b>hmac</b>(key, 2)</i>.\n * 5. Return <i>mac || c</i>.\n */\nexport class Hmac256Ctr implements EncryptionInput {\n\treadonly plaintext: Uint8Array;\n\treadonly aad: Uint8Array;\n\n\tconstructor(msg: Uint8Array, aad: Uint8Array) {\n\t\tthis.plaintext = msg;\n\t\tthis.aad = aad;\n\t}\n\n\tgenerateKey(): Promise<Uint8Array> {\n\t\treturn generateAesKey();\n\t}\n\n\tasync encrypt(key: Uint8Array): Promise<typeof Ciphertext.$inferInput> {\n\t\tconst blob = Hmac256Ctr.encryptInCtrMode(key, this.plaintext);\n\t\tconst mac = Hmac256Ctr.computeMac(key, this.aad, blob);\n\t\treturn {\n\t\t\tHmac256Ctr: {\n\t\t\t\tblob,\n\t\t\t\tmac,\n\t\t\t\taad: this.aad ?? [],\n\t\t\t},\n\t\t};\n\t}\n\n\tstatic async decrypt(\n\t\tkey: Uint8Array,\n\t\tciphertext: typeof Ciphertext.$inferInput,\n\t): Promise<Uint8Array> {\n\t\tif (!('Hmac256Ctr' in ciphertext)) {\n\t\t\tthrow new InvalidCiphertextError(`Invalid ciphertext ${ciphertext}`);\n\t\t}\n\t\tconst aad = new Uint8Array(ciphertext.Hmac256Ctr.aad ?? []);\n\t\tconst blob = new Uint8Array(ciphertext.Hmac256Ctr.blob);\n\t\tconst mac = Hmac256Ctr.computeMac(key, aad, blob);\n\t\tif (!equalBytes(mac, new Uint8Array(ciphertext.Hmac256Ctr.mac))) {\n\t\t\tthrow new InvalidCiphertextError(`Invalid MAC ${mac}`);\n\t\t}\n\t\treturn Hmac256Ctr.encryptInCtrMode(key, blob);\n\t}\n\n\tprivate static computeMac(key: Uint8Array, aad: Uint8Array, ciphertext: Uint8Array): Uint8Array {\n\t\tconst macKey = hmac(sha3_256, key, MacKeyTag);\n\t\tconst macInput = new Uint8Array([...toBytes(aad.length), ...aad, ...ciphertext]);\n\t\tconst mac = hmac(sha3_256, macKey, macInput);\n\t\treturn mac;\n\t}\n\n\tprivate static encryptInCtrMode(key: Uint8Array, msg: Uint8Array): Uint8Array {\n\t\tconst blockSize = 32;\n\t\tconst result = Uint8Array.from({ length: msg.length }, () => 0);\n\t\tconst encryptionKey = hmac(sha3_256, key, EncryptionKeyTag);\n\t\tfor (let i = 0; i * blockSize < msg.length; i++) {\n\t\t\tconst block = msg.slice(i * blockSize, (i + 1) * blockSize);\n\t\t\tconst mask = hmac(sha3_256, encryptionKey, toBytes(i));\n\t\t\tconst encryptedBlock = xorUnchecked(block, mask);\n\t\t\tresult.set(encryptedBlock, i * blockSize);\n\t\t}\n\t\treturn result;\n\t}\n}\n\n/**\n * Convert a u64 to bytes using little-endian representation.\n */\nfunction toBytes(n: number): Uint8Array {\n\treturn bcs.u64().serialize(n).toBytes();\n}\n\nconst EncryptionKeyTag = new Uint8Array([1]);\nconst MacKeyTag = new Uint8Array([2]);\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { bcs } from '@mysten/bcs';\nimport { equalBytes } from '@noble/curves/abstract/utils';\nimport { hmac } from '@noble/hashes/hmac';\nimport { sha3_256 } from '@noble/hashes/sha3';\n\nimport type { Ciphertext } from './bcs.js';\nimport { InvalidCiphertextError } from './error.js';\nimport { xorUnchecked } from './utils.js';\n\n// Use a fixed IV for AES. This is okay because the key is unique for each message.\nexport const iv = Uint8Array.from([\n\t138, 55, 153, 253, 198, 46, 121, 219, 160, 128, 89, 7, 214, 156, 148, 220,\n]);\n\nasync function generateAesKey(): Promise<Uint8Array> {\n\tconst key = await crypto.subtle.generateKey(\n\t\t{\n\t\t\tname: 'AES-GCM',\n\t\t\tlength: 256,\n\t\t},\n\t\ttrue,\n\t\t['encrypt', 'decrypt'],\n\t);\n\treturn await crypto.subtle.exportKey('raw', key).then((keyData) => new Uint8Array(keyData));\n}\n\nexport interface EncryptionInput {\n\tencrypt(key: Uint8Array): Promise<typeof Ciphertext.$inferInput>;\n\tgenerateKey(): Promise<Uint8Array>;\n}\n\nexport class AesGcm256 implements EncryptionInput {\n\treadonly plaintext: Uint8Array;\n\treadonly aad: Uint8Array;\n\n\tconstructor(msg: Uint8Array, aad: Uint8Array) {\n\t\tthis.plaintext = msg;\n\t\tthis.aad = aad;\n\t}\n\n\tgenerateKey(): Promise<Uint8Array> {\n\t\treturn generateAesKey();\n\t}\n\n\tasync encrypt(key: Uint8Array): Promise<typeof Ciphertext.$inferInput> {\n\t\tconst aesCryptoKey = await crypto.subtle.importKey('raw', key, 'AES-GCM', false, ['encrypt']);\n\n\t\tconst blob = new Uint8Array(\n\t\t\tawait crypto.subtle.encrypt(\n\t\t\t\t{\n\t\t\t\t\tname: 'AES-GCM',\n\t\t\t\t\tiv,\n\t\t\t\t\tadditionalData: this.aad,\n\t\t\t\t},\n\t\t\t\taesCryptoKey,\n\t\t\t\tthis.plaintext,\n\t\t\t),\n\t\t);\n\n\t\treturn {\n\t\t\tAes256Gcm: {\n\t\t\t\tblob,\n\t\t\t\taad: this.aad ?? [],\n\t\t\t},\n\t\t};\n\t}\n\n\tstatic async decrypt(\n\t\tkey: Uint8Array,\n\t\tciphertext: typeof Ciphertext.$inferInput,\n\t): Promise<Uint8Array> {\n\t\tif (!('Aes256Gcm' in ciphertext)) {\n\t\t\tthrow new InvalidCiphertextError(`Invalid ciphertext ${ciphertext}`);\n\t\t}\n\n\t\tconst aesCryptoKey = await crypto.subtle.importKey('raw', key, 'AES-GCM', false, ['decrypt']);\n\n\t\treturn new Uint8Array(\n\t\t\tawait crypto.subtle.decrypt(\n\t\t\t\t{\n\t\t\t\t\tname: 'AES-GCM',\n\t\t\t\t\tiv,\n\t\t\t\t\tadditionalData: new Uint8Array(ciphertext.Aes256Gcm.aad ?? []),\n\t\t\t\t},\n\t\t\t\taesCryptoKey,\n\t\t\t\tnew Uint8Array(ciphertext.Aes256Gcm.blob),\n\t\t\t),\n\t\t);\n\t}\n}\n\nexport class Plain implements EncryptionInput {\n\tasync encrypt(_key: Uint8Array): Promise<typeof Ciphertext.$inferInput> {\n\t\treturn {\n\t\t\tPlain: {},\n\t\t};\n\t}\n\n\tgenerateKey(): Promise<Uint8Array> {\n\t\treturn generateAesKey();\n\t}\n}\n\n/**\n * Authenticated encryption using CTR mode with HMAC-SHA3-256 as a PRF.\n * 1. Derive an encryption key, <i>k<sub>1</sub> = <b>hmac</b>(key, 1)</i>.\n * 2. Chunk the message into blocks of 32 bytes, <i>m = m<sub>1</sub> || ... || m<sub>n</sub></i>.\n * 3. Let the ciphertext be defined by <i>c = c<sub>1</sub> || ... || c<sub>n</sub></i> where <i>c<sub>i</sub> = m<sub>i</sub> \u2295 <b>hmac</b>(k<sub>1</sub>, i)</i>.\n * 4. Compute a MAC over the AAD and the ciphertext, <i>mac = <b>hmac</b>(k<sub>2</sub>, aad || c) where k<sub>2</sub> = <b>hmac</b>(key, 2)</i>.\n * 5. Return <i>mac || c</i>.\n */\nexport class Hmac256Ctr implements EncryptionInput {\n\treadonly plaintext: Uint8Array;\n\treadonly aad: Uint8Array;\n\n\tconstructor(msg: Uint8Array, aad: Uint8Array) {\n\t\tthis.plaintext = msg;\n\t\tthis.aad = aad;\n\t}\n\n\tgenerateKey(): Promise<Uint8Array> {\n\t\treturn generateAesKey();\n\t}\n\n\tasync encrypt(key: Uint8Array): Promise<typeof Ciphertext.$inferInput> {\n\t\tconst blob = Hmac256Ctr.encryptInCtrMode(key, this.plaintext);\n\t\tconst mac = Hmac256Ctr.computeMac(key, this.aad, blob);\n\t\treturn {\n\t\t\tHmac256Ctr: {\n\t\t\t\tblob,\n\t\t\t\tmac,\n\t\t\t\taad: this.aad ?? [],\n\t\t\t},\n\t\t};\n\t}\n\n\tstatic async decrypt(\n\t\tkey: Uint8Array,\n\t\tciphertext: typeof Ciphertext.$inferInput,\n\t): Promise<Uint8Array> {\n\t\tif (!('Hmac256Ctr' in ciphertext)) {\n\t\t\tthrow new InvalidCiphertextError(`Invalid ciphertext ${ciphertext}`);\n\t\t}\n\t\tconst aad = new Uint8Array(ciphertext.Hmac256Ctr.aad ?? []);\n\t\tconst blob = new Uint8Array(ciphertext.Hmac256Ctr.blob);\n\t\tconst mac = Hmac256Ctr.computeMac(key, aad, blob);\n\t\tif (!equalBytes(mac, new Uint8Array(ciphertext.Hmac256Ctr.mac))) {\n\t\t\tthrow new InvalidCiphertextError(`Invalid MAC ${mac}`);\n\t\t}\n\t\treturn Hmac256Ctr.encryptInCtrMode(key, blob);\n\t}\n\n\tprivate static computeMac(key: Uint8Array, aad: Uint8Array, ciphertext: Uint8Array): Uint8Array {\n\t\tconst macKey = hmac(sha3_256, key, MacKeyTag);\n\t\tconst macInput = new Uint8Array([...toBytes(aad.length), ...aad, ...ciphertext]);\n\t\tconst mac = hmac(sha3_256, macKey, macInput);\n\t\treturn mac;\n\t}\n\n\tprivate static encryptInCtrMode(key: Uint8Array, msg: Uint8Array): Uint8Array {\n\t\tconst blockSize = 32;\n\t\tconst result = Uint8Array.from({ length: msg.length }, () => 0);\n\t\tconst encryptionKey = hmac(sha3_256, key, EncryptionKeyTag);\n\t\tfor (let i = 0; i * blockSize < msg.length; i++) {\n\t\t\tconst block = msg.subarray(i * blockSize, (i + 1) * blockSize);\n\t\t\tconst mask = hmac(sha3_256, encryptionKey, toBytes(i));\n\t\t\tconst encryptedBlock = xorUnchecked(block, mask);\n\t\t\tresult.set(encryptedBlock, i * blockSize);\n\t\t}\n\t\treturn result;\n\t}\n}\n\n/**\n * Convert a u64 to bytes using little-endian representation.\n */\nfunction toBytes(n: number): Uint8Array {\n\treturn bcs.u64().serialize(n).toBytes();\n}\n\nconst EncryptionKeyTag = new Uint8Array([1]);\nconst MacKeyTag = new Uint8Array([2]);\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -20,11 +20,10 @@ async function encrypt({
20
20
  `Invalid key servers or threshold ${threshold} for ${keyServers.length} key servers for package ${packageId}`
21
21
  );
22
22
  }
23
- const key = await encryptionInput.generateKey();
24
- const demKey = deriveKey(KeyPurpose.DEM, key);
25
- const ciphertext = await encryptionInput.encrypt(demKey);
26
- const shares = await split(key, keyServers.length, threshold);
23
+ const baseKey = await encryptionInput.generateKey();
24
+ const shares = await split(baseKey, keyServers.length, threshold);
27
25
  const fullId = createFullId(DST, packageId, id);
26
+ const randomnessKey = deriveKey(KeyPurpose.EncryptedRandomness, baseKey);
28
27
  const encryptedShares = encryptBatched(
29
28
  keyServers,
30
29
  kemType,
@@ -33,10 +32,12 @@ async function encrypt({
33
32
  msg: share,
34
33
  index
35
34
  })),
36
- deriveKey(KeyPurpose.EncryptedRandomness, key)
35
+ randomnessKey
37
36
  );
38
- const services = keyServers.map((server, i) => [
39
- server.objectId,
37
+ const demKey = deriveKey(KeyPurpose.DEM, baseKey);
38
+ const ciphertext = await encryptionInput.encrypt(demKey);
39
+ const services = keyServers.map(({ objectId }, i) => [
40
+ objectId,
40
41
  shares[i].index
41
42
  ]);
42
43
  return {
@@ -61,14 +62,10 @@ var DemType = /* @__PURE__ */ ((DemType2) => {
61
62
  DemType2[DemType2["Hmac256Ctr"] = 1] = "Hmac256Ctr";
62
63
  return DemType2;
63
64
  })(DemType || {});
64
- function encryptBatched(keyServers, kemType, id, shares, randomnessKey) {
65
+ function encryptBatched(keyServers, kemType, id, msgs, randomnessKey) {
65
66
  switch (kemType) {
66
67
  case 0 /* BonehFranklinBLS12381DemCCA */:
67
- return new BonehFranklinBLS12381Services(keyServers).encryptBatched(
68
- id,
69
- shares,
70
- randomnessKey
71
- );
68
+ return new BonehFranklinBLS12381Services(keyServers).encryptBatched(id, msgs, randomnessKey);
72
69
  }
73
70
  }
74
71
  async function split(secret, n, threshold) {
@@ -76,7 +73,7 @@ async function split(secret, n, threshold) {
76
73
  throw new Error("Invalid threshold or number of shares");
77
74
  } else if (threshold === 1) {
78
75
  const result = [];
79
- for (let i = 0; i < n; i++) {
76
+ for (let i = 1; i <= n; i++) {
80
77
  result.push({ share: secret, index: i });
81
78
  }
82
79
  return Promise.resolve(result);
@@ -84,7 +81,7 @@ async function split(secret, n, threshold) {
84
81
  return externalSplit(secret, n, threshold).then(
85
82
  (share) => share.map((s) => ({
86
83
  share: s.subarray(0, s.length - 1),
87
- // split() returns the share index in the last byte
84
+ // split() returns the share index in the last byte. See https://github.com/privy-io/shamir-secret-sharing/blob/b59534d03e66d44ae36fc074aaf0684aa39c7505/src/index.ts#L247.
88
85
  index: s[s.length - 1]
89
86
  }))
90
87
  );
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/encrypt.ts"],
4
- "sourcesContent": ["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { fromHex } from '@mysten/bcs';\nimport { isValidSuiObjectId } from '@mysten/sui/utils';\nimport { split as externalSplit } from 'shamir-secret-sharing';\n\nimport type { IBEEncryptions } from './bcs.js';\nimport { EncryptedObject } from './bcs.js';\nimport type { EncryptionInput } from './dem.js';\nimport { UserError } from './error.js';\nimport { BonehFranklinBLS12381Services, DST } from './ibe.js';\nimport { deriveKey, KeyPurpose } from './kdf.js';\nimport type { KeyServer } from './key-server.js';\nimport { createFullId } from './utils.js';\n\nexport const MAX_U8 = 255;\n\n/**\n * Given full ID and what key servers to use, return the encrypted message under the identity and return the bcs bytes of the encrypted object.\n *\n * @param keyServers - A list of KeyServers (same server can be used multiple times)\n * @param kemType - The type of KEM to use.\n * @param packageId - packageId\n * @param id - id\n * @param encryptionInput - Input to the encryption. Should be one of the EncryptionInput types, AesGcmEncryptionInput or Plain.\n * @param threshold - The threshold for the TSS encryption.\n * @returns The bcs bytes of the encrypted object containing all metadata and the 256-bit symmetric key that was used to encrypt the object.\n * Since the key can be used to decrypt, it should not be shared but can be used eg. for backup.\n */\nexport async function encrypt({\n\tkeyServers,\n\tkemType,\n\tthreshold,\n\tpackageId,\n\tid,\n\tencryptionInput,\n}: {\n\tkeyServers: KeyServer[];\n\tkemType: KemType;\n\tthreshold: number;\n\tpackageId: string;\n\tid: string;\n\tencryptionInput: EncryptionInput;\n}): Promise<{\n\tencryptedObject: Uint8Array;\n\tkey: Uint8Array;\n}> {\n\t// Check inputs\n\tif (\n\t\tkeyServers.length < threshold ||\n\t\tthreshold === 0 ||\n\t\tkeyServers.length > MAX_U8 ||\n\t\tthreshold > MAX_U8 ||\n\t\t!isValidSuiObjectId(packageId)\n\t) {\n\t\tthrow new UserError(\n\t\t\t`Invalid key servers or threshold ${threshold} for ${keyServers.length} key servers for package ${packageId}`,\n\t\t);\n\t}\n\n\t// Generate a random symmetric key and encrypt the encryption input using this key.\n\tconst key = await encryptionInput.generateKey();\n\tconst demKey = deriveKey(KeyPurpose.DEM, key);\n\tconst ciphertext = await encryptionInput.encrypt(demKey);\n\n\t// Split the symmetric key into shares and encrypt each share with the public keys of the key servers.\n\tconst shares = await split(key, keyServers.length, threshold);\n\n\t// Encrypt the shares with the public keys of the key servers.\n\tconst fullId = createFullId(DST, packageId, id);\n\tconst encryptedShares = encryptBatched(\n\t\tkeyServers,\n\t\tkemType,\n\t\tfromHex(fullId),\n\t\tshares.map(({ share, index }) => ({\n\t\t\tmsg: share,\n\t\t\tindex,\n\t\t})),\n\t\tderiveKey(KeyPurpose.EncryptedRandomness, key),\n\t);\n\n\t// Services and indices of their shares are stored as a tuple\n\tconst services: [string, number][] = keyServers.map((server, i) => [\n\t\tserver.objectId,\n\t\tshares[i].index,\n\t]);\n\n\treturn {\n\t\tencryptedObject: EncryptedObject.serialize({\n\t\t\tversion: 0,\n\t\t\tpackageId,\n\t\t\tid,\n\t\t\tservices,\n\t\t\tthreshold,\n\t\t\tencryptedShares,\n\t\t\tciphertext,\n\t\t}).toBytes(),\n\t\tkey: demKey,\n\t};\n}\n\nexport enum KemType {\n\tBonehFranklinBLS12381DemCCA = 0,\n}\n\nexport enum DemType {\n\tAesGcm256 = 0,\n\tHmac256Ctr = 1,\n}\n\nfunction encryptBatched(\n\tkeyServers: KeyServer[],\n\tkemType: KemType,\n\tid: Uint8Array,\n\tshares: { msg: Uint8Array; index: number }[],\n\trandomnessKey: Uint8Array,\n): typeof IBEEncryptions.$inferType {\n\tswitch (kemType) {\n\t\tcase KemType.BonehFranklinBLS12381DemCCA:\n\t\t\treturn new BonehFranklinBLS12381Services(keyServers).encryptBatched(\n\t\t\t\tid,\n\t\t\t\tshares,\n\t\t\t\trandomnessKey,\n\t\t\t);\n\t}\n}\n\nasync function split(\n\tsecret: Uint8Array,\n\tn: number,\n\tthreshold: number,\n): Promise<{ index: number; share: Uint8Array }[]> {\n\t// The externalSplit function is from the 'shamir-secret-sharing' package and requires t > 1 and n >= 2.\n\t// So we handle the special cases here.\n\tif (n === 0 || threshold === 0 || threshold > n) {\n\t\tthrow new Error('Invalid threshold or number of shares');\n\t} else if (threshold === 1) {\n\t\t// If the threshold is 1, the secret is not split.\n\t\tconst result = [];\n\t\tfor (let i = 0; i < n; i++) {\n\t\t\t// The shared polynomial is a constant in this case, so the index doesn't matter.\n\t\t\t// To make sure they are unique, we use a counter.\n\t\t\tresult.push({ share: secret, index: i });\n\t\t}\n\t\treturn Promise.resolve(result);\n\t}\n\n\treturn externalSplit(secret, n, threshold).then((share) =>\n\t\tshare.map((s) => ({\n\t\t\tshare: s.subarray(0, s.length - 1),\n\t\t\t// split() returns the share index in the last byte\n\t\t\tindex: s[s.length - 1],\n\t\t})),\n\t);\n}\n"],
5
- "mappings": "AAGA,SAAS,eAAe;AACxB,SAAS,0BAA0B;AACnC,SAAS,SAAS,qBAAqB;AAGvC,SAAS,uBAAuB;AAEhC,SAAS,iBAAiB;AAC1B,SAAS,+BAA+B,WAAW;AACnD,SAAS,WAAW,kBAAkB;AAEtC,SAAS,oBAAoB;AAEtB,MAAM,SAAS;AActB,eAAsB,QAAQ;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,GAUG;AAEF,MACC,WAAW,SAAS,aACpB,cAAc,KACd,WAAW,SAAS,UACpB,YAAY,UACZ,CAAC,mBAAmB,SAAS,GAC5B;AACD,UAAM,IAAI;AAAA,MACT,oCAAoC,SAAS,QAAQ,WAAW,MAAM,4BAA4B,SAAS;AAAA,IAC5G;AAAA,EACD;AAGA,QAAM,MAAM,MAAM,gBAAgB,YAAY;AAC9C,QAAM,SAAS,UAAU,WAAW,KAAK,GAAG;AAC5C,QAAM,aAAa,MAAM,gBAAgB,QAAQ,MAAM;AAGvD,QAAM,SAAS,MAAM,MAAM,KAAK,WAAW,QAAQ,SAAS;AAG5D,QAAM,SAAS,aAAa,KAAK,WAAW,EAAE;AAC9C,QAAM,kBAAkB;AAAA,IACvB;AAAA,IACA;AAAA,IACA,QAAQ,MAAM;AAAA,IACd,OAAO,IAAI,CAAC,EAAE,OAAO,MAAM,OAAO;AAAA,MACjC,KAAK;AAAA,MACL;AAAA,IACD,EAAE;AAAA,IACF,UAAU,WAAW,qBAAqB,GAAG;AAAA,EAC9C;AAGA,QAAM,WAA+B,WAAW,IAAI,CAAC,QAAQ,MAAM;AAAA,IAClE,OAAO;AAAA,IACP,OAAO,CAAC,EAAE;AAAA,EACX,CAAC;AAED,SAAO;AAAA,IACN,iBAAiB,gBAAgB,UAAU;AAAA,MAC1C,SAAS;AAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACD,CAAC,EAAE,QAAQ;AAAA,IACX,KAAK;AAAA,EACN;AACD;AAEO,IAAK,UAAL,kBAAKA,aAAL;AACN,EAAAA,kBAAA,iCAA8B,KAA9B;AADW,SAAAA;AAAA,GAAA;AAIL,IAAK,UAAL,kBAAKC,aAAL;AACN,EAAAA,kBAAA,eAAY,KAAZ;AACA,EAAAA,kBAAA,gBAAa,KAAb;AAFW,SAAAA;AAAA,GAAA;AAKZ,SAAS,eACR,YACA,SACA,IACA,QACA,eACmC;AACnC,UAAQ,SAAS;AAAA,IAChB,KAAK;AACJ,aAAO,IAAI,8BAA8B,UAAU,EAAE;AAAA,QACpD;AAAA,QACA;AAAA,QACA;AAAA,MACD;AAAA,EACF;AACD;AAEA,eAAe,MACd,QACA,GACA,WACkD;AAGlD,MAAI,MAAM,KAAK,cAAc,KAAK,YAAY,GAAG;AAChD,UAAM,IAAI,MAAM,uCAAuC;AAAA,EACxD,WAAW,cAAc,GAAG;AAE3B,UAAM,SAAS,CAAC;AAChB,aAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAG3B,aAAO,KAAK,EAAE,OAAO,QAAQ,OAAO,EAAE,CAAC;AAAA,IACxC;AACA,WAAO,QAAQ,QAAQ,MAAM;AAAA,EAC9B;AAEA,SAAO,cAAc,QAAQ,GAAG,SAAS,EAAE;AAAA,IAAK,CAAC,UAChD,MAAM,IAAI,CAAC,OAAO;AAAA,MACjB,OAAO,EAAE,SAAS,GAAG,EAAE,SAAS,CAAC;AAAA;AAAA,MAEjC,OAAO,EAAE,EAAE,SAAS,CAAC;AAAA,IACtB,EAAE;AAAA,EACH;AACD;",
4
+ "sourcesContent": ["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { fromHex } from '@mysten/bcs';\nimport { isValidSuiObjectId } from '@mysten/sui/utils';\nimport { split as externalSplit } from 'shamir-secret-sharing';\n\nimport type { IBEEncryptions } from './bcs.js';\nimport { EncryptedObject } from './bcs.js';\nimport type { EncryptionInput } from './dem.js';\nimport { UserError } from './error.js';\nimport { BonehFranklinBLS12381Services, DST } from './ibe.js';\nimport { deriveKey, KeyPurpose } from './kdf.js';\nimport type { KeyServer } from './key-server.js';\nimport { createFullId } from './utils.js';\n\nexport const MAX_U8 = 255;\n\n/**\n * Given full ID and what key servers to use, return the encrypted message under the identity and return the bcs bytes of the encrypted object.\n *\n * @param keyServers - A list of KeyServers (same server can be used multiple times)\n * @param kemType - The type of KEM to use.\n * @param packageId - packageId\n * @param id - id\n * @param encryptionInput - Input to the encryption. Should be one of the EncryptionInput types, AesGcmEncryptionInput or Plain.\n * @param threshold - The threshold for the TSS encryption.\n * @returns The bcs bytes of the encrypted object containing all metadata and the 256-bit symmetric key that was used to encrypt the object.\n * Since the key can be used to decrypt, it should not be shared but can be used eg. for backup.\n */\nexport async function encrypt({\n\tkeyServers,\n\tkemType,\n\tthreshold,\n\tpackageId,\n\tid,\n\tencryptionInput,\n}: {\n\tkeyServers: KeyServer[];\n\tkemType: KemType;\n\tthreshold: number;\n\tpackageId: string;\n\tid: string;\n\tencryptionInput: EncryptionInput;\n}): Promise<{\n\tencryptedObject: Uint8Array;\n\tkey: Uint8Array;\n}> {\n\t// Check inputs\n\tif (\n\t\tkeyServers.length < threshold ||\n\t\tthreshold === 0 ||\n\t\tkeyServers.length > MAX_U8 ||\n\t\tthreshold > MAX_U8 ||\n\t\t!isValidSuiObjectId(packageId)\n\t) {\n\t\tthrow new UserError(\n\t\t\t`Invalid key servers or threshold ${threshold} for ${keyServers.length} key servers for package ${packageId}`,\n\t\t);\n\t}\n\n\t// Generate a random base key.\n\tconst baseKey = await encryptionInput.generateKey();\n\n\t// Split the key into shares and encrypt each share with the public keys of the key servers.\n\tconst shares = await split(baseKey, keyServers.length, threshold);\n\n\t// Encrypt the shares with the public keys of the key servers.\n\tconst fullId = createFullId(DST, packageId, id);\n\tconst randomnessKey = deriveKey(KeyPurpose.EncryptedRandomness, baseKey);\n\tconst encryptedShares = encryptBatched(\n\t\tkeyServers,\n\t\tkemType,\n\t\tfromHex(fullId),\n\t\tshares.map(({ share, index }) => ({\n\t\t\tmsg: share,\n\t\t\tindex,\n\t\t})),\n\t\trandomnessKey,\n\t);\n\n\t// Encrypt the object with the derived DEM key.\n\tconst demKey = deriveKey(KeyPurpose.DEM, baseKey);\n\tconst ciphertext = await encryptionInput.encrypt(demKey);\n\n\t// Services and indices of their shares are stored as a tuple\n\tconst services: [string, number][] = keyServers.map(({ objectId }, i) => [\n\t\tobjectId,\n\t\tshares[i].index,\n\t]);\n\n\treturn {\n\t\tencryptedObject: EncryptedObject.serialize({\n\t\t\tversion: 0,\n\t\t\tpackageId,\n\t\t\tid,\n\t\t\tservices,\n\t\t\tthreshold,\n\t\t\tencryptedShares,\n\t\t\tciphertext,\n\t\t}).toBytes(),\n\t\tkey: demKey,\n\t};\n}\n\nexport enum KemType {\n\tBonehFranklinBLS12381DemCCA = 0,\n}\n\nexport enum DemType {\n\tAesGcm256 = 0,\n\tHmac256Ctr = 1,\n}\n\nfunction encryptBatched(\n\tkeyServers: KeyServer[],\n\tkemType: KemType,\n\tid: Uint8Array,\n\tmsgs: { msg: Uint8Array; index: number }[],\n\trandomnessKey: Uint8Array,\n): typeof IBEEncryptions.$inferType {\n\tswitch (kemType) {\n\t\tcase KemType.BonehFranklinBLS12381DemCCA:\n\t\t\treturn new BonehFranklinBLS12381Services(keyServers).encryptBatched(id, msgs, randomnessKey);\n\t}\n}\n\nasync function split(\n\tsecret: Uint8Array,\n\tn: number,\n\tthreshold: number,\n): Promise<{ index: number; share: Uint8Array }[]> {\n\t// The externalSplit function is from the 'shamir-secret-sharing' package and requires t > 1 and n >= 2.\n\t// So we handle the special cases here.\n\tif (n === 0 || threshold === 0 || threshold > n) {\n\t\tthrow new Error('Invalid threshold or number of shares');\n\t} else if (threshold === 1) {\n\t\t// If the threshold is 1, the secret is not split.\n\t\tconst result = [];\n\t\tfor (let i = 1; i <= n; i++) {\n\t\t\t// The shared polynomial is a constant in this case, so the index doesn't matter.\n\t\t\t// To make sure they are unique, we use a counter.\n\t\t\tresult.push({ share: secret, index: i });\n\t\t}\n\t\treturn Promise.resolve(result);\n\t}\n\n\treturn externalSplit(secret, n, threshold).then((share) =>\n\t\tshare.map((s) => ({\n\t\t\tshare: s.subarray(0, s.length - 1),\n\t\t\t// split() returns the share index in the last byte. See https://github.com/privy-io/shamir-secret-sharing/blob/b59534d03e66d44ae36fc074aaf0684aa39c7505/src/index.ts#L247.\n\t\t\tindex: s[s.length - 1],\n\t\t})),\n\t);\n}\n"],
5
+ "mappings": "AAGA,SAAS,eAAe;AACxB,SAAS,0BAA0B;AACnC,SAAS,SAAS,qBAAqB;AAGvC,SAAS,uBAAuB;AAEhC,SAAS,iBAAiB;AAC1B,SAAS,+BAA+B,WAAW;AACnD,SAAS,WAAW,kBAAkB;AAEtC,SAAS,oBAAoB;AAEtB,MAAM,SAAS;AActB,eAAsB,QAAQ;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,GAUG;AAEF,MACC,WAAW,SAAS,aACpB,cAAc,KACd,WAAW,SAAS,UACpB,YAAY,UACZ,CAAC,mBAAmB,SAAS,GAC5B;AACD,UAAM,IAAI;AAAA,MACT,oCAAoC,SAAS,QAAQ,WAAW,MAAM,4BAA4B,SAAS;AAAA,IAC5G;AAAA,EACD;AAGA,QAAM,UAAU,MAAM,gBAAgB,YAAY;AAGlD,QAAM,SAAS,MAAM,MAAM,SAAS,WAAW,QAAQ,SAAS;AAGhE,QAAM,SAAS,aAAa,KAAK,WAAW,EAAE;AAC9C,QAAM,gBAAgB,UAAU,WAAW,qBAAqB,OAAO;AACvE,QAAM,kBAAkB;AAAA,IACvB;AAAA,IACA;AAAA,IACA,QAAQ,MAAM;AAAA,IACd,OAAO,IAAI,CAAC,EAAE,OAAO,MAAM,OAAO;AAAA,MACjC,KAAK;AAAA,MACL;AAAA,IACD,EAAE;AAAA,IACF;AAAA,EACD;AAGA,QAAM,SAAS,UAAU,WAAW,KAAK,OAAO;AAChD,QAAM,aAAa,MAAM,gBAAgB,QAAQ,MAAM;AAGvD,QAAM,WAA+B,WAAW,IAAI,CAAC,EAAE,SAAS,GAAG,MAAM;AAAA,IACxE;AAAA,IACA,OAAO,CAAC,EAAE;AAAA,EACX,CAAC;AAED,SAAO;AAAA,IACN,iBAAiB,gBAAgB,UAAU;AAAA,MAC1C,SAAS;AAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACD,CAAC,EAAE,QAAQ;AAAA,IACX,KAAK;AAAA,EACN;AACD;AAEO,IAAK,UAAL,kBAAKA,aAAL;AACN,EAAAA,kBAAA,iCAA8B,KAA9B;AADW,SAAAA;AAAA,GAAA;AAIL,IAAK,UAAL,kBAAKC,aAAL;AACN,EAAAA,kBAAA,eAAY,KAAZ;AACA,EAAAA,kBAAA,gBAAa,KAAb;AAFW,SAAAA;AAAA,GAAA;AAKZ,SAAS,eACR,YACA,SACA,IACA,MACA,eACmC;AACnC,UAAQ,SAAS;AAAA,IAChB,KAAK;AACJ,aAAO,IAAI,8BAA8B,UAAU,EAAE,eAAe,IAAI,MAAM,aAAa;AAAA,EAC7F;AACD;AAEA,eAAe,MACd,QACA,GACA,WACkD;AAGlD,MAAI,MAAM,KAAK,cAAc,KAAK,YAAY,GAAG;AAChD,UAAM,IAAI,MAAM,uCAAuC;AAAA,EACxD,WAAW,cAAc,GAAG;AAE3B,UAAM,SAAS,CAAC;AAChB,aAAS,IAAI,GAAG,KAAK,GAAG,KAAK;AAG5B,aAAO,KAAK,EAAE,OAAO,QAAQ,OAAO,EAAE,CAAC;AAAA,IACxC;AACA,WAAO,QAAQ,QAAQ,MAAM;AAAA,EAC9B;AAEA,SAAO,cAAc,QAAQ,GAAG,SAAS,EAAE;AAAA,IAAK,CAAC,UAChD,MAAM,IAAI,CAAC,OAAO;AAAA,MACjB,OAAO,EAAE,SAAS,GAAG,EAAE,SAAS,CAAC;AAAA;AAAA,MAEjC,OAAO,EAAE,EAAE,SAAS,CAAC;AAAA,IACtB,EAAE;AAAA,EACH;AACD;",
6
6
  "names": ["KemType", "DemType"]
7
7
  }
package/dist/esm/ibe.d.ts CHANGED
@@ -32,7 +32,9 @@ export declare abstract class IBEServers {
32
32
  }[], randomnessKey: Uint8Array): typeof IBEEncryptions.$inferType;
33
33
  }
34
34
  /**
35
- * Identity-based encryption based on the Boneh-Franklin IBE scheme.
35
+ * Identity-based encryption based on the Boneh-Franklin IBE scheme (https://eprint.iacr.org/2001/090).
36
+ * Note that this implementation is of the "BasicIdent" protocol which on its own is not CCA secure, so this IBE implementation should not be used on its own.
37
+ *
36
38
  * This object represents a set of key servers that can be used to encrypt messages for a given identity.
37
39
  */
38
40
  export declare class BonehFranklinBLS12381Services extends IBEServers {
package/dist/esm/ibe.js CHANGED
@@ -26,7 +26,7 @@ class BonehFranklinBLS12381Services extends IBEServers {
26
26
  }
27
27
  const [r, nonce, keys] = encapBatched(this.publicKeys, id);
28
28
  const encryptedShares = msgAndIndices.map(
29
- (msgAndIndex, i) => xor(msgAndIndex.msg, kdf(keys[i], nonce, id, this.objectIds[i], msgAndIndex.index))
29
+ ({ msg, index }, i) => xor(msg, kdf(keys[i], nonce, id, this.objectIds[i], index))
30
30
  );
31
31
  const encryptedRandomness = xor(randomnessKey, r.toBytes());
32
32
  return {
@@ -46,9 +46,9 @@ class BonehFranklinBLS12381Services extends IBEServers {
46
46
  * @returns True if the user secret key is valid for the given public key and id.
47
47
  */
48
48
  static verifyUserSecretKey(userSecretKey, id, publicKey) {
49
- const lhs = userSecretKey.pairing(G2Element.generator()).toBytes();
50
- const rhs = G1Element.hashToCurve(fromHex(id)).pairing(publicKey).toBytes();
51
- return lhs.length === rhs.length && lhs.every((value, index) => value === rhs[index]);
49
+ const lhs = userSecretKey.pairing(G2Element.generator());
50
+ const rhs = G1Element.hashToCurve(fromHex(id)).pairing(publicKey);
51
+ return lhs.equals(rhs);
52
52
  }
53
53
  /**
54
54
  * Identity-based decryption.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/ibe.ts"],
4
- "sourcesContent": ["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { fromHex } from '@mysten/bcs';\n\nimport type { IBEEncryptions } from './bcs.js';\nimport type { GTElement } from './bls12381.js';\nimport { G1Element, G2Element, Scalar } from './bls12381.js';\nimport { kdf } from './kdf.js';\nimport type { KeyServer } from './key-server.js';\nimport { xor } from './utils.js';\n\n/**\n * The domain separation tag for the hash-to-group function.\n */\nexport const DST: Uint8Array = new TextEncoder().encode('SUI-SEAL-IBE-BLS12381-00');\n\n/**\n * The domain separation tag for the signing proof of possession.\n */\nexport const DST_POP: Uint8Array = new TextEncoder().encode('SUI-SEAL-IBE-BLS12381-POP-00');\n\n/**\n * The interface for the key servers.\n */\nexport abstract class IBEServers {\n\tobjectIds: string[];\n\n\tconstructor(objectIds: string[]) {\n\t\tthis.objectIds = objectIds;\n\t}\n\n\t/**\n\t * The number of key servers.\n\t */\n\tsize(): number {\n\t\treturn this.objectIds.length;\n\t}\n\n\t/**\n\t * Encrypt a batch of messages for the given identity.\n\t *\n\t * @param id The identity.\n\t * @param msgAndIndices The messages and the corresponding indices of the share being encrypted.\n\t * @returns The encrypted messages.\n\t */\n\tabstract encryptBatched(\n\t\tid: Uint8Array,\n\t\tmsgAndIndices: { msg: Uint8Array; index: number }[],\n\t\trandomnessKey: Uint8Array,\n\t): typeof IBEEncryptions.$inferType;\n}\n\n/**\n * Identity-based encryption based on the Boneh-Franklin IBE scheme.\n * This object represents a set of key servers that can be used to encrypt messages for a given identity.\n */\nexport class BonehFranklinBLS12381Services extends IBEServers {\n\treadonly publicKeys: G2Element[];\n\n\tconstructor(services: KeyServer[]) {\n\t\tsuper(services.map((service) => service.objectId));\n\t\tthis.publicKeys = services.map((service) => G2Element.fromBytes(service.pk));\n\t}\n\n\tencryptBatched(\n\t\tid: Uint8Array,\n\t\tmsgAndIndices: { msg: Uint8Array; index: number }[],\n\t\trandomnessKey: Uint8Array,\n\t): typeof IBEEncryptions.$inferType {\n\t\tif (this.publicKeys.length === 0 || this.publicKeys.length !== msgAndIndices.length) {\n\t\t\tthrow new Error('Invalid public keys');\n\t\t}\n\t\tconst [r, nonce, keys] = encapBatched(this.publicKeys, id);\n\t\tconst encryptedShares = msgAndIndices.map((msgAndIndex, i) =>\n\t\t\txor(msgAndIndex.msg, kdf(keys[i], nonce, id, this.objectIds[i], msgAndIndex.index)),\n\t\t);\n\t\tconst encryptedRandomness = xor(randomnessKey, r.toBytes());\n\n\t\treturn {\n\t\t\tBonehFranklinBLS12381: {\n\t\t\t\tnonce: nonce.toBytes(),\n\t\t\t\tencryptedShares,\n\t\t\t\tencryptedRandomness,\n\t\t\t},\n\t\t\t$kind: 'BonehFranklinBLS12381',\n\t\t};\n\t}\n\n\t/**\n\t * Returns true if the user secret key is valid for the given public key and id.\n\t * @param user_secret_key - The user secret key.\n\t * @param id - The identity.\n\t * @param public_key - The public key.\n\t * @returns True if the user secret key is valid for the given public key and id.\n\t */\n\tstatic verifyUserSecretKey(userSecretKey: G1Element, id: string, publicKey: G2Element): boolean {\n\t\tconst lhs = userSecretKey.pairing(G2Element.generator()).toBytes();\n\t\tconst rhs = G1Element.hashToCurve(fromHex(id)).pairing(publicKey).toBytes();\n\t\treturn lhs.length === rhs.length && lhs.every((value, index) => value === rhs[index]);\n\t}\n\n\t/**\n\t * Identity-based decryption.\n\t *\n\t * @param nonce The encryption nonce.\n\t * @param sk The user secret key.\n\t * @param ciphertext The encrypted message.\n\t * @param info An info parameter also included in the KDF.\n\t * @returns The decrypted message.\n\t */\n\tstatic decrypt(\n\t\tnonce: G2Element,\n\t\tsk: G1Element,\n\t\tciphertext: Uint8Array,\n\t\tid: Uint8Array,\n\t\t[objectId, index]: [string, number],\n\t): Uint8Array {\n\t\treturn xor(ciphertext, kdf(decap(nonce, sk), nonce, id, objectId, index));\n\t}\n}\n\n/**\n * Batched identity-based key-encapsulation mechanism: encapsulate multiple keys for given identity using different key servers.\n *\n * @param publicKeys Public keys for a set of key servers.\n * @param id The identity used to encapsulate the keys.\n * @returns A common nonce of the keys and a list of keys, 32 bytes each.\n */\nfunction encapBatched(publicKeys: G2Element[], id: Uint8Array): [Scalar, G2Element, GTElement[]] {\n\tif (publicKeys.length === 0) {\n\t\tthrow new Error('No public keys provided');\n\t}\n\tconst r = Scalar.random();\n\tconst nonce = G2Element.generator().multiply(r);\n\tconst gid = G1Element.hashToCurve(id).multiply(r);\n\treturn [r, nonce, publicKeys.map((public_key) => gid.pairing(public_key))];\n}\n\n/**\n * Decapsulate a key using a user secret key and the nonce.\n *\n * @param usk The user secret key.\n * @param nonce The nonce.\n * @returns The encapsulated key.\n */\nfunction decap(nonce: G2Element, usk: G1Element): GTElement {\n\treturn usk.pairing(nonce);\n}\n"],
5
- "mappings": "AAGA,SAAS,eAAe;AAIxB,SAAS,WAAW,WAAW,cAAc;AAC7C,SAAS,WAAW;AAEpB,SAAS,WAAW;AAKb,MAAM,MAAkB,IAAI,YAAY,EAAE,OAAO,0BAA0B;AAK3E,MAAM,UAAsB,IAAI,YAAY,EAAE,OAAO,8BAA8B;AAKnF,MAAe,WAAW;AAAA,EAGhC,YAAY,WAAqB;AAChC,SAAK,YAAY;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA,EAKA,OAAe;AACd,WAAO,KAAK,UAAU;AAAA,EACvB;AAcD;AAMO,MAAM,sCAAsC,WAAW;AAAA,EAG7D,YAAY,UAAuB;AAClC,UAAM,SAAS,IAAI,CAAC,YAAY,QAAQ,QAAQ,CAAC;AACjD,SAAK,aAAa,SAAS,IAAI,CAAC,YAAY,UAAU,UAAU,QAAQ,EAAE,CAAC;AAAA,EAC5E;AAAA,EAEA,eACC,IACA,eACA,eACmC;AACnC,QAAI,KAAK,WAAW,WAAW,KAAK,KAAK,WAAW,WAAW,cAAc,QAAQ;AACpF,YAAM,IAAI,MAAM,qBAAqB;AAAA,IACtC;AACA,UAAM,CAAC,GAAG,OAAO,IAAI,IAAI,aAAa,KAAK,YAAY,EAAE;AACzD,UAAM,kBAAkB,cAAc;AAAA,MAAI,CAAC,aAAa,MACvD,IAAI,YAAY,KAAK,IAAI,KAAK,CAAC,GAAG,OAAO,IAAI,KAAK,UAAU,CAAC,GAAG,YAAY,KAAK,CAAC;AAAA,IACnF;AACA,UAAM,sBAAsB,IAAI,eAAe,EAAE,QAAQ,CAAC;AAE1D,WAAO;AAAA,MACN,uBAAuB;AAAA,QACtB,OAAO,MAAM,QAAQ;AAAA,QACrB;AAAA,QACA;AAAA,MACD;AAAA,MACA,OAAO;AAAA,IACR;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,OAAO,oBAAoB,eAA0B,IAAY,WAA+B;AAC/F,UAAM,MAAM,cAAc,QAAQ,UAAU,UAAU,CAAC,EAAE,QAAQ;AACjE,UAAM,MAAM,UAAU,YAAY,QAAQ,EAAE,CAAC,EAAE,QAAQ,SAAS,EAAE,QAAQ;AAC1E,WAAO,IAAI,WAAW,IAAI,UAAU,IAAI,MAAM,CAAC,OAAO,UAAU,UAAU,IAAI,KAAK,CAAC;AAAA,EACrF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,OAAO,QACN,OACA,IACA,YACA,IACA,CAAC,UAAU,KAAK,GACH;AACb,WAAO,IAAI,YAAY,IAAI,MAAM,OAAO,EAAE,GAAG,OAAO,IAAI,UAAU,KAAK,CAAC;AAAA,EACzE;AACD;AASA,SAAS,aAAa,YAAyB,IAAkD;AAChG,MAAI,WAAW,WAAW,GAAG;AAC5B,UAAM,IAAI,MAAM,yBAAyB;AAAA,EAC1C;AACA,QAAM,IAAI,OAAO,OAAO;AACxB,QAAM,QAAQ,UAAU,UAAU,EAAE,SAAS,CAAC;AAC9C,QAAM,MAAM,UAAU,YAAY,EAAE,EAAE,SAAS,CAAC;AAChD,SAAO,CAAC,GAAG,OAAO,WAAW,IAAI,CAAC,eAAe,IAAI,QAAQ,UAAU,CAAC,CAAC;AAC1E;AASA,SAAS,MAAM,OAAkB,KAA2B;AAC3D,SAAO,IAAI,QAAQ,KAAK;AACzB;",
4
+ "sourcesContent": ["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { fromHex } from '@mysten/bcs';\n\nimport type { IBEEncryptions } from './bcs.js';\nimport type { GTElement } from './bls12381.js';\nimport { G1Element, G2Element, Scalar } from './bls12381.js';\nimport { kdf } from './kdf.js';\nimport type { KeyServer } from './key-server.js';\nimport { xor } from './utils.js';\n\n/**\n * The domain separation tag for the hash-to-group function.\n */\nexport const DST: Uint8Array = new TextEncoder().encode('SUI-SEAL-IBE-BLS12381-00');\n\n/**\n * The domain separation tag for the signing proof of possession.\n */\nexport const DST_POP: Uint8Array = new TextEncoder().encode('SUI-SEAL-IBE-BLS12381-POP-00');\n\n/**\n * The interface for the key servers.\n */\nexport abstract class IBEServers {\n\tobjectIds: string[];\n\n\tconstructor(objectIds: string[]) {\n\t\tthis.objectIds = objectIds;\n\t}\n\n\t/**\n\t * The number of key servers.\n\t */\n\tsize(): number {\n\t\treturn this.objectIds.length;\n\t}\n\n\t/**\n\t * Encrypt a batch of messages for the given identity.\n\t *\n\t * @param id The identity.\n\t * @param msgAndIndices The messages and the corresponding indices of the share being encrypted.\n\t * @returns The encrypted messages.\n\t */\n\tabstract encryptBatched(\n\t\tid: Uint8Array,\n\t\tmsgAndIndices: { msg: Uint8Array; index: number }[],\n\t\trandomnessKey: Uint8Array,\n\t): typeof IBEEncryptions.$inferType;\n}\n\n/**\n * Identity-based encryption based on the Boneh-Franklin IBE scheme (https://eprint.iacr.org/2001/090).\n * Note that this implementation is of the \"BasicIdent\" protocol which on its own is not CCA secure, so this IBE implementation should not be used on its own.\n *\n * This object represents a set of key servers that can be used to encrypt messages for a given identity.\n */\nexport class BonehFranklinBLS12381Services extends IBEServers {\n\treadonly publicKeys: G2Element[];\n\n\tconstructor(services: KeyServer[]) {\n\t\tsuper(services.map((service) => service.objectId));\n\t\tthis.publicKeys = services.map((service) => G2Element.fromBytes(service.pk));\n\t}\n\n\tencryptBatched(\n\t\tid: Uint8Array,\n\t\tmsgAndIndices: { msg: Uint8Array; index: number }[],\n\t\trandomnessKey: Uint8Array,\n\t): typeof IBEEncryptions.$inferType {\n\t\tif (this.publicKeys.length === 0 || this.publicKeys.length !== msgAndIndices.length) {\n\t\t\tthrow new Error('Invalid public keys');\n\t\t}\n\t\tconst [r, nonce, keys] = encapBatched(this.publicKeys, id);\n\t\tconst encryptedShares = msgAndIndices.map(({ msg, index }, i) =>\n\t\t\txor(msg, kdf(keys[i], nonce, id, this.objectIds[i], index)),\n\t\t);\n\t\tconst encryptedRandomness = xor(randomnessKey, r.toBytes());\n\n\t\treturn {\n\t\t\tBonehFranklinBLS12381: {\n\t\t\t\tnonce: nonce.toBytes(),\n\t\t\t\tencryptedShares,\n\t\t\t\tencryptedRandomness,\n\t\t\t},\n\t\t\t$kind: 'BonehFranklinBLS12381',\n\t\t};\n\t}\n\n\t/**\n\t * Returns true if the user secret key is valid for the given public key and id.\n\t * @param user_secret_key - The user secret key.\n\t * @param id - The identity.\n\t * @param public_key - The public key.\n\t * @returns True if the user secret key is valid for the given public key and id.\n\t */\n\tstatic verifyUserSecretKey(userSecretKey: G1Element, id: string, publicKey: G2Element): boolean {\n\t\tconst lhs = userSecretKey.pairing(G2Element.generator());\n\t\tconst rhs = G1Element.hashToCurve(fromHex(id)).pairing(publicKey);\n\t\treturn lhs.equals(rhs);\n\t}\n\n\t/**\n\t * Identity-based decryption.\n\t *\n\t * @param nonce The encryption nonce.\n\t * @param sk The user secret key.\n\t * @param ciphertext The encrypted message.\n\t * @param info An info parameter also included in the KDF.\n\t * @returns The decrypted message.\n\t */\n\tstatic decrypt(\n\t\tnonce: G2Element,\n\t\tsk: G1Element,\n\t\tciphertext: Uint8Array,\n\t\tid: Uint8Array,\n\t\t[objectId, index]: [string, number],\n\t): Uint8Array {\n\t\treturn xor(ciphertext, kdf(decap(nonce, sk), nonce, id, objectId, index));\n\t}\n}\n\n/**\n * Batched identity-based key-encapsulation mechanism: encapsulate multiple keys for given identity using different key servers.\n *\n * @param publicKeys Public keys for a set of key servers.\n * @param id The identity used to encapsulate the keys.\n * @returns A common nonce of the keys and a list of keys, 32 bytes each.\n */\nfunction encapBatched(publicKeys: G2Element[], id: Uint8Array): [Scalar, G2Element, GTElement[]] {\n\tif (publicKeys.length === 0) {\n\t\tthrow new Error('No public keys provided');\n\t}\n\tconst r = Scalar.random();\n\tconst nonce = G2Element.generator().multiply(r);\n\tconst gid = G1Element.hashToCurve(id).multiply(r);\n\treturn [r, nonce, publicKeys.map((public_key) => gid.pairing(public_key))];\n}\n\n/**\n * Decapsulate a key using a user secret key and the nonce.\n *\n * @param usk The user secret key.\n * @param nonce The nonce.\n * @returns The encapsulated key.\n */\nfunction decap(nonce: G2Element, usk: G1Element): GTElement {\n\treturn usk.pairing(nonce);\n}\n"],
5
+ "mappings": "AAGA,SAAS,eAAe;AAIxB,SAAS,WAAW,WAAW,cAAc;AAC7C,SAAS,WAAW;AAEpB,SAAS,WAAW;AAKb,MAAM,MAAkB,IAAI,YAAY,EAAE,OAAO,0BAA0B;AAK3E,MAAM,UAAsB,IAAI,YAAY,EAAE,OAAO,8BAA8B;AAKnF,MAAe,WAAW;AAAA,EAGhC,YAAY,WAAqB;AAChC,SAAK,YAAY;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA,EAKA,OAAe;AACd,WAAO,KAAK,UAAU;AAAA,EACvB;AAcD;AAQO,MAAM,sCAAsC,WAAW;AAAA,EAG7D,YAAY,UAAuB;AAClC,UAAM,SAAS,IAAI,CAAC,YAAY,QAAQ,QAAQ,CAAC;AACjD,SAAK,aAAa,SAAS,IAAI,CAAC,YAAY,UAAU,UAAU,QAAQ,EAAE,CAAC;AAAA,EAC5E;AAAA,EAEA,eACC,IACA,eACA,eACmC;AACnC,QAAI,KAAK,WAAW,WAAW,KAAK,KAAK,WAAW,WAAW,cAAc,QAAQ;AACpF,YAAM,IAAI,MAAM,qBAAqB;AAAA,IACtC;AACA,UAAM,CAAC,GAAG,OAAO,IAAI,IAAI,aAAa,KAAK,YAAY,EAAE;AACzD,UAAM,kBAAkB,cAAc;AAAA,MAAI,CAAC,EAAE,KAAK,MAAM,GAAG,MAC1D,IAAI,KAAK,IAAI,KAAK,CAAC,GAAG,OAAO,IAAI,KAAK,UAAU,CAAC,GAAG,KAAK,CAAC;AAAA,IAC3D;AACA,UAAM,sBAAsB,IAAI,eAAe,EAAE,QAAQ,CAAC;AAE1D,WAAO;AAAA,MACN,uBAAuB;AAAA,QACtB,OAAO,MAAM,QAAQ;AAAA,QACrB;AAAA,QACA;AAAA,MACD;AAAA,MACA,OAAO;AAAA,IACR;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,OAAO,oBAAoB,eAA0B,IAAY,WAA+B;AAC/F,UAAM,MAAM,cAAc,QAAQ,UAAU,UAAU,CAAC;AACvD,UAAM,MAAM,UAAU,YAAY,QAAQ,EAAE,CAAC,EAAE,QAAQ,SAAS;AAChE,WAAO,IAAI,OAAO,GAAG;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,OAAO,QACN,OACA,IACA,YACA,IACA,CAAC,UAAU,KAAK,GACH;AACb,WAAO,IAAI,YAAY,IAAI,MAAM,OAAO,EAAE,GAAG,OAAO,IAAI,UAAU,KAAK,CAAC;AAAA,EACzE;AACD;AASA,SAAS,aAAa,YAAyB,IAAkD;AAChG,MAAI,WAAW,WAAW,GAAG;AAC5B,UAAM,IAAI,MAAM,yBAAyB;AAAA,EAC1C;AACA,QAAM,IAAI,OAAO,OAAO;AACxB,QAAM,QAAQ,UAAU,UAAU,EAAE,SAAS,CAAC;AAC9C,QAAM,MAAM,UAAU,YAAY,EAAE,EAAE,SAAS,CAAC;AAChD,SAAO,CAAC,GAAG,OAAO,WAAW,IAAI,CAAC,eAAe,IAAI,QAAQ,UAAU,CAAC,CAAC;AAC1E;AASA,SAAS,MAAM,OAAkB,KAA2B;AAC3D,SAAO,IAAI,QAAQ,KAAK;AACzB;",
6
6
  "names": []
7
7
  }
@@ -1,6 +1,6 @@
1
1
  export { getAllowlistedKeyServers } from './key-server.js';
2
2
  export { EncryptedObject } from './bcs.js';
3
3
  export { SealClient, type SealClientOptions } from './client.js';
4
- export { SessionKey } from './session-key.js';
4
+ export { SessionKey, type SessionKeyType } from './session-key.js';
5
5
  export * from './error.js';
6
6
  export type { SealCompatibleClient } from './types.js';
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/index.ts"],
4
- "sourcesContent": ["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nexport { getAllowlistedKeyServers } from './key-server.js';\nexport { EncryptedObject } from './bcs.js';\nexport { SealClient, type SealClientOptions } from './client.js';\nexport { SessionKey } from './session-key.js';\nexport * from './error.js';\nexport type { SealCompatibleClient } from './types.js';\n"],
5
- "mappings": "AAGA,SAAS,gCAAgC;AACzC,SAAS,uBAAuB;AAChC,SAAS,kBAA0C;AACnD,SAAS,kBAAkB;AAC3B,cAAc;",
4
+ "sourcesContent": ["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nexport { getAllowlistedKeyServers } from './key-server.js';\nexport { EncryptedObject } from './bcs.js';\nexport { SealClient, type SealClientOptions } from './client.js';\nexport { SessionKey, type SessionKeyType } from './session-key.js';\nexport * from './error.js';\nexport type { SealCompatibleClient } from './types.js';\n"],
5
+ "mappings": "AAGA,SAAS,gCAAgC;AACzC,SAAS,uBAAuB;AAChC,SAAS,kBAA0C;AACnD,SAAS,kBAAuC;AAChD,cAAc;",
6
6
  "names": []
7
7
  }
package/dist/esm/kdf.js CHANGED
@@ -3,24 +3,14 @@ import { hkdf } from "@noble/hashes/hkdf";
3
3
  import { hmac } from "@noble/hashes/hmac";
4
4
  import { sha3_256 } from "@noble/hashes/sha3";
5
5
  import { G1Element } from "./bls12381.js";
6
+ import { flatten } from "./utils.js";
6
7
  function kdf(element, nonce, id, objectId, index) {
7
- const GT_ELEMENT_BYTE_LENGTH = 576;
8
- const PERMUTATION = [0, 2, 4, 1, 3, 5];
9
- const COEFFICIENT_SIZE = GT_ELEMENT_BYTE_LENGTH / PERMUTATION.length;
10
- const bytes = element.toBytes();
11
- const permutedBytes = new Uint8Array(GT_ELEMENT_BYTE_LENGTH);
12
- PERMUTATION.forEach((pi, i) => {
13
- permutedBytes.set(
14
- bytes.slice(i * COEFFICIENT_SIZE, (i + 1) * COEFFICIENT_SIZE),
15
- pi * COEFFICIENT_SIZE
16
- );
17
- });
18
- const inputBytes = new Uint8Array([
19
- ...permutedBytes,
20
- ...nonce.toBytes(),
21
- ...G1Element.hashToCurve(id).toBytes()
8
+ const inputBytes = flatten([
9
+ element.toBytes(),
10
+ nonce.toBytes(),
11
+ G1Element.hashToCurve(id).toBytes()
22
12
  ]);
23
- const info = new Uint8Array([...fromHex(objectId), index]);
13
+ const info = flatten([fromHex(objectId), new Uint8Array([index])]);
24
14
  return hkdf(sha3_256, inputBytes, "", info, 32);
25
15
  }
26
16
  var KeyPurpose = /* @__PURE__ */ ((KeyPurpose2) => {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/kdf.ts"],
4
- "sourcesContent": ["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { fromHex } from '@mysten/bcs';\nimport { hkdf } from '@noble/hashes/hkdf';\nimport { hmac } from '@noble/hashes/hmac';\nimport { sha3_256 } from '@noble/hashes/sha3';\n\nimport { G1Element } from './bls12381.js';\nimport type { G2Element, GTElement } from './bls12381.js';\n\n/**\n * The default key derivation function.\n *\n * @param element The GTElement to derive the key from.\n * @param info Optional context and application specific information.\n * @returns The derived key.\n */\nexport function kdf(\n\telement: GTElement,\n\tnonce: G2Element,\n\tid: Uint8Array,\n\tobjectId: string,\n\tindex: number,\n): Uint8Array {\n\t// This permutation flips the order of 6 pairs of coefficients of the GT element.\n\t// The permutation may be computed as:\n\t// for i in 0..3 {\n\t// for j in 0..2 {\n\t// PERMUTATION[i + j * 3] = i * 2 + j;\n\t// }\n\t// }\n\tconst GT_ELEMENT_BYTE_LENGTH = 576;\n\tconst PERMUTATION = [0, 2, 4, 1, 3, 5];\n\tconst COEFFICIENT_SIZE = GT_ELEMENT_BYTE_LENGTH / PERMUTATION.length;\n\n\tconst bytes = element.toBytes();\n\tconst permutedBytes = new Uint8Array(GT_ELEMENT_BYTE_LENGTH);\n\tPERMUTATION.forEach((pi, i) => {\n\t\tpermutedBytes.set(\n\t\t\tbytes.slice(i * COEFFICIENT_SIZE, (i + 1) * COEFFICIENT_SIZE),\n\t\t\tpi * COEFFICIENT_SIZE,\n\t\t);\n\t});\n\tconst inputBytes = new Uint8Array([\n\t\t...permutedBytes,\n\t\t...nonce.toBytes(),\n\t\t...G1Element.hashToCurve(id).toBytes(),\n\t]);\n\tconst info = new Uint8Array([...fromHex(objectId), index]);\n\treturn hkdf(sha3_256, inputBytes, '', info, 32);\n}\n\nexport enum KeyPurpose {\n\tEncryptedRandomness,\n\tDEM,\n}\n\nexport function deriveKey(purpose: KeyPurpose, baseKey: Uint8Array): Uint8Array {\n\tswitch (purpose) {\n\t\tcase KeyPurpose.EncryptedRandomness:\n\t\t\treturn hmac(sha3_256, baseKey, new Uint8Array([0]));\n\t\tcase KeyPurpose.DEM:\n\t\t\treturn hmac(sha3_256, baseKey, new Uint8Array([1]));\n\t}\n}\n"],
5
- "mappings": "AAGA,SAAS,eAAe;AACxB,SAAS,YAAY;AACrB,SAAS,YAAY;AACrB,SAAS,gBAAgB;AAEzB,SAAS,iBAAiB;AAUnB,SAAS,IACf,SACA,OACA,IACA,UACA,OACa;AAQb,QAAM,yBAAyB;AAC/B,QAAM,cAAc,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;AACrC,QAAM,mBAAmB,yBAAyB,YAAY;AAE9D,QAAM,QAAQ,QAAQ,QAAQ;AAC9B,QAAM,gBAAgB,IAAI,WAAW,sBAAsB;AAC3D,cAAY,QAAQ,CAAC,IAAI,MAAM;AAC9B,kBAAc;AAAA,MACb,MAAM,MAAM,IAAI,mBAAmB,IAAI,KAAK,gBAAgB;AAAA,MAC5D,KAAK;AAAA,IACN;AAAA,EACD,CAAC;AACD,QAAM,aAAa,IAAI,WAAW;AAAA,IACjC,GAAG;AAAA,IACH,GAAG,MAAM,QAAQ;AAAA,IACjB,GAAG,UAAU,YAAY,EAAE,EAAE,QAAQ;AAAA,EACtC,CAAC;AACD,QAAM,OAAO,IAAI,WAAW,CAAC,GAAG,QAAQ,QAAQ,GAAG,KAAK,CAAC;AACzD,SAAO,KAAK,UAAU,YAAY,IAAI,MAAM,EAAE;AAC/C;AAEO,IAAK,aAAL,kBAAKA,gBAAL;AACN,EAAAA,wBAAA;AACA,EAAAA,wBAAA;AAFW,SAAAA;AAAA,GAAA;AAKL,SAAS,UAAU,SAAqB,SAAiC;AAC/E,UAAQ,SAAS;AAAA,IAChB,KAAK;AACJ,aAAO,KAAK,UAAU,SAAS,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC;AAAA,IACnD,KAAK;AACJ,aAAO,KAAK,UAAU,SAAS,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC;AAAA,EACpD;AACD;",
4
+ "sourcesContent": ["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { fromHex } from '@mysten/bcs';\nimport { hkdf } from '@noble/hashes/hkdf';\nimport { hmac } from '@noble/hashes/hmac';\nimport { sha3_256 } from '@noble/hashes/sha3';\n\nimport { G1Element } from './bls12381.js';\nimport type { G2Element, GTElement } from './bls12381.js';\nimport { flatten } from './utils.js';\n\n/**\n * The default key derivation function.\n *\n * @param element The GTElement to derive the key from.\n * @param info Optional context and application specific information.\n * @returns The derived key.\n */\nexport function kdf(\n\telement: GTElement,\n\tnonce: G2Element,\n\tid: Uint8Array,\n\tobjectId: string,\n\tindex: number,\n): Uint8Array {\n\tconst inputBytes = flatten([\n\t\telement.toBytes(),\n\t\tnonce.toBytes(),\n\t\tG1Element.hashToCurve(id).toBytes(),\n\t]);\n\n\tconst info = flatten([fromHex(objectId), new Uint8Array([index])]);\n\n\treturn hkdf(sha3_256, inputBytes, '', info, 32);\n}\n\nexport enum KeyPurpose {\n\tEncryptedRandomness,\n\tDEM,\n}\n\nexport function deriveKey(purpose: KeyPurpose, baseKey: Uint8Array): Uint8Array {\n\tswitch (purpose) {\n\t\tcase KeyPurpose.EncryptedRandomness:\n\t\t\treturn hmac(sha3_256, baseKey, new Uint8Array([0]));\n\t\tcase KeyPurpose.DEM:\n\t\t\treturn hmac(sha3_256, baseKey, new Uint8Array([1]));\n\t}\n}\n"],
5
+ "mappings": "AAGA,SAAS,eAAe;AACxB,SAAS,YAAY;AACrB,SAAS,YAAY;AACrB,SAAS,gBAAgB;AAEzB,SAAS,iBAAiB;AAE1B,SAAS,eAAe;AASjB,SAAS,IACf,SACA,OACA,IACA,UACA,OACa;AACb,QAAM,aAAa,QAAQ;AAAA,IAC1B,QAAQ,QAAQ;AAAA,IAChB,MAAM,QAAQ;AAAA,IACd,UAAU,YAAY,EAAE,EAAE,QAAQ;AAAA,EACnC,CAAC;AAED,QAAM,OAAO,QAAQ,CAAC,QAAQ,QAAQ,GAAG,IAAI,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC;AAEjE,SAAO,KAAK,UAAU,YAAY,IAAI,MAAM,EAAE;AAC/C;AAEO,IAAK,aAAL,kBAAKA,gBAAL;AACN,EAAAA,wBAAA;AACA,EAAAA,wBAAA;AAFW,SAAAA;AAAA,GAAA;AAKL,SAAS,UAAU,SAAqB,SAAiC;AAC/E,UAAQ,SAAS;AAAA,IAChB,KAAK;AACJ,aAAO,KAAK,UAAU,SAAS,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC;AAAA,IACnD,KAAK;AACJ,aAAO,KAAK,UAAU,SAAS,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC;AAAA,EACpD;AACD;",
6
6
  "names": ["KeyPurpose"]
7
7
  }
@@ -1,5 +1,6 @@
1
1
  import type { SealCompatibleClient } from './types.js';
2
2
  import { Version } from './utils.js';
3
+ import type { G1Element } from './bls12381.js';
3
4
  export type KeyServer = {
4
5
  objectId: string;
5
6
  name: string;
@@ -44,3 +45,16 @@ export declare function verifyKeyServer(server: KeyServer, timeout: number): Pro
44
45
  * @param response - The response from the key server.
45
46
  */
46
47
  export declare function verifyKeyServerVersion(response: Response): void;
48
+ export interface DerivedKey {
49
+ toString(): string;
50
+ }
51
+ /**
52
+ * A user secret key for the Boneh-Franklin BLS12381 scheme.
53
+ * This is a wrapper around the G1Element type.
54
+ */
55
+ export declare class BonehFranklinBLS12381DerivedKey implements DerivedKey {
56
+ key: G1Element;
57
+ representation: string;
58
+ constructor(key: G1Element);
59
+ toString(): string;
60
+ }
@@ -1,4 +1,4 @@
1
- import { fromBase64, fromHex } from "@mysten/bcs";
1
+ import { fromBase64, fromHex, toHex } from "@mysten/bcs";
2
2
  import { bls12_381 } from "@noble/curves/bls12-381";
3
3
  import { KeyServerMove } from "./bcs.js";
4
4
  import {
@@ -86,7 +86,17 @@ function verifyKeyServerVersion(response) {
86
86
  );
87
87
  }
88
88
  }
89
+ class BonehFranklinBLS12381DerivedKey {
90
+ constructor(key) {
91
+ this.key = key;
92
+ this.representation = toHex(key.toBytes());
93
+ }
94
+ toString() {
95
+ return this.representation;
96
+ }
97
+ }
89
98
  export {
99
+ BonehFranklinBLS12381DerivedKey,
90
100
  KeyServerType,
91
101
  SERVER_VERSION_REQUIREMENT,
92
102
  getAllowlistedKeyServers,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/key-server.ts"],
4
- "sourcesContent": ["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\nimport { fromBase64, fromHex } from '@mysten/bcs';\nimport { bls12_381 } from '@noble/curves/bls12-381';\n\nimport { KeyServerMove } from './bcs.js';\nimport {\n\tInvalidGetObjectError,\n\tInvalidKeyServerVersionError,\n\tSealAPIError,\n\tUnsupportedFeatureError,\n\tUnsupportedNetworkError,\n} from './error.js';\nimport { DST_POP } from './ibe.js';\nimport { PACKAGE_VERSION } from './version.js';\nimport type { SealCompatibleClient } from './types.js';\nimport { Version } from './utils.js';\n\nexport type KeyServer = {\n\tobjectId: string;\n\tname: string;\n\turl: string;\n\tkeyType: KeyServerType;\n\tpk: Uint8Array;\n};\n\nexport enum KeyServerType {\n\tBonehFranklinBLS12381 = 0,\n}\n\nexport const SERVER_VERSION_REQUIREMENT = new Version('0.2.0');\n\n/**\n * Returns a static list of Seal key server object ids that the dapp can choose to use.\n * @param network - The network to use.\n * @returns The object id's of the key servers.\n */\nexport function getAllowlistedKeyServers(network: 'testnet' | 'mainnet'): string[] {\n\tif (network === 'testnet') {\n\t\treturn [\n\t\t\t'0xb35a7228d8cf224ad1e828c0217c95a5153bafc2906d6f9c178197dce26fbcf8',\n\t\t\t'0x2d6cde8a9d9a65bde3b0a346566945a63b4bfb70e9a06c41bdb70807e2502b06',\n\t\t];\n\t} else {\n\t\tthrow new UnsupportedNetworkError(`Unsupported network ${network}`);\n\t}\n}\n\n/**\n * Given a list of key server object IDs, returns a list of SealKeyServer\n * from onchain state containing name, objectId, URL and pk.\n *\n * @param objectIds - The key server object IDs.\n * @param client - The SuiClient to use.\n * @returns - An array of SealKeyServer.\n */\nexport async function retrieveKeyServers({\n\tobjectIds,\n\tclient,\n}: {\n\tobjectIds: string[];\n\tclient: SealCompatibleClient;\n}): Promise<KeyServer[]> {\n\t// todo: do not fetch the same object ID if this is fetched before.\n\treturn await Promise.all(\n\t\tobjectIds.map(async (objectId) => {\n\t\t\tlet res;\n\t\t\ttry {\n\t\t\t\tres = await client.core.getObject({\n\t\t\t\t\tobjectId,\n\t\t\t\t});\n\t\t\t} catch (e) {\n\t\t\t\tthrow new InvalidGetObjectError(`KeyServer ${objectId} not found; ${(e as Error).message}`);\n\t\t\t}\n\n\t\t\tconst ks = KeyServerMove.parse(res.object.content);\n\t\t\tif (ks.keyType !== 0) {\n\t\t\t\tthrow new UnsupportedFeatureError(`Unsupported key type ${ks.keyType}`);\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tobjectId,\n\t\t\t\tname: ks.name,\n\t\t\t\turl: ks.url,\n\t\t\t\tkeyType: KeyServerType.BonehFranklinBLS12381,\n\t\t\t\tpk: new Uint8Array(ks.pk),\n\t\t\t};\n\t\t}),\n\t);\n}\n\n/**\n * Given a KeyServer, fetch the proof of possession (PoP) from the URL and verify it\n * against the pubkey. This should be used only rarely when the dapp uses a dynamic\n * set of key servers.\n *\n * @param server - The KeyServer to verify.\n * @returns - True if the key server is valid, false otherwise.\n */\nexport async function verifyKeyServer(server: KeyServer, timeout: number): Promise<boolean> {\n\tconst requestId = crypto.randomUUID();\n\tconst response = await fetch(server.url! + '/v1/service', {\n\t\tmethod: 'GET',\n\t\theaders: {\n\t\t\t'Content-Type': 'application/json',\n\t\t\t'Request-Id': requestId,\n\t\t\t'Client-Sdk-Type': 'typescript',\n\t\t\t'Client-Sdk-Version': PACKAGE_VERSION,\n\t\t},\n\t\tsignal: AbortSignal.timeout(timeout),\n\t});\n\n\tawait SealAPIError.assertResponse(response, requestId);\n\tverifyKeyServerVersion(response);\n\tconst serviceResponse = await response.json();\n\n\tif (serviceResponse.service_id !== server.objectId) {\n\t\treturn false;\n\t}\n\tconst fullMsg = new Uint8Array([...DST_POP, ...server.pk, ...fromHex(server.objectId)]);\n\treturn bls12_381.verifyShortSignature(fromBase64(serviceResponse.pop), fullMsg, server.pk);\n}\n\n/**\n * Verify the key server version. Throws an `InvalidKeyServerError` if the version is not supported.\n *\n * @param response - The response from the key server.\n */\nexport function verifyKeyServerVersion(response: Response) {\n\tconst keyServerVersion = response.headers.get('X-KeyServer-Version');\n\tif (keyServerVersion == null) {\n\t\tthrow new InvalidKeyServerVersionError('Key server version not found');\n\t}\n\tif (new Version(keyServerVersion).older_than(SERVER_VERSION_REQUIREMENT)) {\n\t\tthrow new InvalidKeyServerVersionError(\n\t\t\t`Key server version ${keyServerVersion} is not supported`,\n\t\t);\n\t}\n}\n"],
5
- "mappings": "AAEA,SAAS,YAAY,eAAe;AACpC,SAAS,iBAAiB;AAE1B,SAAS,qBAAqB;AAC9B;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AACP,SAAS,eAAe;AACxB,SAAS,uBAAuB;AAEhC,SAAS,eAAe;AAUjB,IAAK,gBAAL,kBAAKA,mBAAL;AACN,EAAAA,8BAAA,2BAAwB,KAAxB;AADW,SAAAA;AAAA,GAAA;AAIL,MAAM,6BAA6B,IAAI,QAAQ,OAAO;AAOtD,SAAS,yBAAyB,SAA0C;AAClF,MAAI,YAAY,WAAW;AAC1B,WAAO;AAAA,MACN;AAAA,MACA;AAAA,IACD;AAAA,EACD,OAAO;AACN,UAAM,IAAI,wBAAwB,uBAAuB,OAAO,EAAE;AAAA,EACnE;AACD;AAUA,eAAsB,mBAAmB;AAAA,EACxC;AAAA,EACA;AACD,GAGyB;AAExB,SAAO,MAAM,QAAQ;AAAA,IACpB,UAAU,IAAI,OAAO,aAAa;AACjC,UAAI;AACJ,UAAI;AACH,cAAM,MAAM,OAAO,KAAK,UAAU;AAAA,UACjC;AAAA,QACD,CAAC;AAAA,MACF,SAAS,GAAG;AACX,cAAM,IAAI,sBAAsB,aAAa,QAAQ,eAAgB,EAAY,OAAO,EAAE;AAAA,MAC3F;AAEA,YAAM,KAAK,cAAc,MAAM,IAAI,OAAO,OAAO;AACjD,UAAI,GAAG,YAAY,GAAG;AACrB,cAAM,IAAI,wBAAwB,wBAAwB,GAAG,OAAO,EAAE;AAAA,MACvE;AAEA,aAAO;AAAA,QACN;AAAA,QACA,MAAM,GAAG;AAAA,QACT,KAAK,GAAG;AAAA,QACR,SAAS;AAAA,QACT,IAAI,IAAI,WAAW,GAAG,EAAE;AAAA,MACzB;AAAA,IACD,CAAC;AAAA,EACF;AACD;AAUA,eAAsB,gBAAgB,QAAmB,SAAmC;AAC3F,QAAM,YAAY,OAAO,WAAW;AACpC,QAAM,WAAW,MAAM,MAAM,OAAO,MAAO,eAAe;AAAA,IACzD,QAAQ;AAAA,IACR,SAAS;AAAA,MACR,gBAAgB;AAAA,MAChB,cAAc;AAAA,MACd,mBAAmB;AAAA,MACnB,sBAAsB;AAAA,IACvB;AAAA,IACA,QAAQ,YAAY,QAAQ,OAAO;AAAA,EACpC,CAAC;AAED,QAAM,aAAa,eAAe,UAAU,SAAS;AACrD,yBAAuB,QAAQ;AAC/B,QAAM,kBAAkB,MAAM,SAAS,KAAK;AAE5C,MAAI,gBAAgB,eAAe,OAAO,UAAU;AACnD,WAAO;AAAA,EACR;AACA,QAAM,UAAU,IAAI,WAAW,CAAC,GAAG,SAAS,GAAG,OAAO,IAAI,GAAG,QAAQ,OAAO,QAAQ,CAAC,CAAC;AACtF,SAAO,UAAU,qBAAqB,WAAW,gBAAgB,GAAG,GAAG,SAAS,OAAO,EAAE;AAC1F;AAOO,SAAS,uBAAuB,UAAoB;AAC1D,QAAM,mBAAmB,SAAS,QAAQ,IAAI,qBAAqB;AACnE,MAAI,oBAAoB,MAAM;AAC7B,UAAM,IAAI,6BAA6B,8BAA8B;AAAA,EACtE;AACA,MAAI,IAAI,QAAQ,gBAAgB,EAAE,WAAW,0BAA0B,GAAG;AACzE,UAAM,IAAI;AAAA,MACT,sBAAsB,gBAAgB;AAAA,IACvC;AAAA,EACD;AACD;",
4
+ "sourcesContent": ["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\nimport { fromBase64, fromHex, toHex } from '@mysten/bcs';\nimport { bls12_381 } from '@noble/curves/bls12-381';\n\nimport { KeyServerMove } from './bcs.js';\nimport {\n\tInvalidGetObjectError,\n\tInvalidKeyServerVersionError,\n\tSealAPIError,\n\tUnsupportedFeatureError,\n\tUnsupportedNetworkError,\n} from './error.js';\nimport { DST_POP } from './ibe.js';\nimport { PACKAGE_VERSION } from './version.js';\nimport type { SealCompatibleClient } from './types.js';\nimport { Version } from './utils.js';\nimport type { G1Element } from './bls12381.js';\n\nexport type KeyServer = {\n\tobjectId: string;\n\tname: string;\n\turl: string;\n\tkeyType: KeyServerType;\n\tpk: Uint8Array;\n};\n\nexport enum KeyServerType {\n\tBonehFranklinBLS12381 = 0,\n}\n\nexport const SERVER_VERSION_REQUIREMENT = new Version('0.2.0');\n\n/**\n * Returns a static list of Seal key server object ids that the dapp can choose to use.\n * @param network - The network to use.\n * @returns The object id's of the key servers.\n */\nexport function getAllowlistedKeyServers(network: 'testnet' | 'mainnet'): string[] {\n\tif (network === 'testnet') {\n\t\treturn [\n\t\t\t'0xb35a7228d8cf224ad1e828c0217c95a5153bafc2906d6f9c178197dce26fbcf8',\n\t\t\t'0x2d6cde8a9d9a65bde3b0a346566945a63b4bfb70e9a06c41bdb70807e2502b06',\n\t\t];\n\t} else {\n\t\tthrow new UnsupportedNetworkError(`Unsupported network ${network}`);\n\t}\n}\n\n/**\n * Given a list of key server object IDs, returns a list of SealKeyServer\n * from onchain state containing name, objectId, URL and pk.\n *\n * @param objectIds - The key server object IDs.\n * @param client - The SuiClient to use.\n * @returns - An array of SealKeyServer.\n */\nexport async function retrieveKeyServers({\n\tobjectIds,\n\tclient,\n}: {\n\tobjectIds: string[];\n\tclient: SealCompatibleClient;\n}): Promise<KeyServer[]> {\n\t// todo: do not fetch the same object ID if this is fetched before.\n\treturn await Promise.all(\n\t\tobjectIds.map(async (objectId) => {\n\t\t\tlet res;\n\t\t\ttry {\n\t\t\t\tres = await client.core.getObject({\n\t\t\t\t\tobjectId,\n\t\t\t\t});\n\t\t\t} catch (e) {\n\t\t\t\tthrow new InvalidGetObjectError(`KeyServer ${objectId} not found; ${(e as Error).message}`);\n\t\t\t}\n\n\t\t\tconst ks = KeyServerMove.parse(res.object.content);\n\t\t\tif (ks.keyType !== 0) {\n\t\t\t\tthrow new UnsupportedFeatureError(`Unsupported key type ${ks.keyType}`);\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tobjectId,\n\t\t\t\tname: ks.name,\n\t\t\t\turl: ks.url,\n\t\t\t\tkeyType: KeyServerType.BonehFranklinBLS12381,\n\t\t\t\tpk: new Uint8Array(ks.pk),\n\t\t\t};\n\t\t}),\n\t);\n}\n\n/**\n * Given a KeyServer, fetch the proof of possession (PoP) from the URL and verify it\n * against the pubkey. This should be used only rarely when the dapp uses a dynamic\n * set of key servers.\n *\n * @param server - The KeyServer to verify.\n * @returns - True if the key server is valid, false otherwise.\n */\nexport async function verifyKeyServer(server: KeyServer, timeout: number): Promise<boolean> {\n\tconst requestId = crypto.randomUUID();\n\tconst response = await fetch(server.url! + '/v1/service', {\n\t\tmethod: 'GET',\n\t\theaders: {\n\t\t\t'Content-Type': 'application/json',\n\t\t\t'Request-Id': requestId,\n\t\t\t'Client-Sdk-Type': 'typescript',\n\t\t\t'Client-Sdk-Version': PACKAGE_VERSION,\n\t\t},\n\t\tsignal: AbortSignal.timeout(timeout),\n\t});\n\n\tawait SealAPIError.assertResponse(response, requestId);\n\tverifyKeyServerVersion(response);\n\tconst serviceResponse = await response.json();\n\n\tif (serviceResponse.service_id !== server.objectId) {\n\t\treturn false;\n\t}\n\tconst fullMsg = new Uint8Array([...DST_POP, ...server.pk, ...fromHex(server.objectId)]);\n\treturn bls12_381.verifyShortSignature(fromBase64(serviceResponse.pop), fullMsg, server.pk);\n}\n\n/**\n * Verify the key server version. Throws an `InvalidKeyServerError` if the version is not supported.\n *\n * @param response - The response from the key server.\n */\nexport function verifyKeyServerVersion(response: Response) {\n\tconst keyServerVersion = response.headers.get('X-KeyServer-Version');\n\tif (keyServerVersion == null) {\n\t\tthrow new InvalidKeyServerVersionError('Key server version not found');\n\t}\n\tif (new Version(keyServerVersion).older_than(SERVER_VERSION_REQUIREMENT)) {\n\t\tthrow new InvalidKeyServerVersionError(\n\t\t\t`Key server version ${keyServerVersion} is not supported`,\n\t\t);\n\t}\n}\n\nexport interface DerivedKey {\n\ttoString(): string;\n}\n\n/**\n * A user secret key for the Boneh-Franklin BLS12381 scheme.\n * This is a wrapper around the G1Element type.\n */\nexport class BonehFranklinBLS12381DerivedKey implements DerivedKey {\n\trepresentation: string;\n\n\tconstructor(public key: G1Element) {\n\t\tthis.representation = toHex(key.toBytes());\n\t}\n\n\ttoString(): string {\n\t\treturn this.representation;\n\t}\n}\n"],
5
+ "mappings": "AAEA,SAAS,YAAY,SAAS,aAAa;AAC3C,SAAS,iBAAiB;AAE1B,SAAS,qBAAqB;AAC9B;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AACP,SAAS,eAAe;AACxB,SAAS,uBAAuB;AAEhC,SAAS,eAAe;AAWjB,IAAK,gBAAL,kBAAKA,mBAAL;AACN,EAAAA,8BAAA,2BAAwB,KAAxB;AADW,SAAAA;AAAA,GAAA;AAIL,MAAM,6BAA6B,IAAI,QAAQ,OAAO;AAOtD,SAAS,yBAAyB,SAA0C;AAClF,MAAI,YAAY,WAAW;AAC1B,WAAO;AAAA,MACN;AAAA,MACA;AAAA,IACD;AAAA,EACD,OAAO;AACN,UAAM,IAAI,wBAAwB,uBAAuB,OAAO,EAAE;AAAA,EACnE;AACD;AAUA,eAAsB,mBAAmB;AAAA,EACxC;AAAA,EACA;AACD,GAGyB;AAExB,SAAO,MAAM,QAAQ;AAAA,IACpB,UAAU,IAAI,OAAO,aAAa;AACjC,UAAI;AACJ,UAAI;AACH,cAAM,MAAM,OAAO,KAAK,UAAU;AAAA,UACjC;AAAA,QACD,CAAC;AAAA,MACF,SAAS,GAAG;AACX,cAAM,IAAI,sBAAsB,aAAa,QAAQ,eAAgB,EAAY,OAAO,EAAE;AAAA,MAC3F;AAEA,YAAM,KAAK,cAAc,MAAM,IAAI,OAAO,OAAO;AACjD,UAAI,GAAG,YAAY,GAAG;AACrB,cAAM,IAAI,wBAAwB,wBAAwB,GAAG,OAAO,EAAE;AAAA,MACvE;AAEA,aAAO;AAAA,QACN;AAAA,QACA,MAAM,GAAG;AAAA,QACT,KAAK,GAAG;AAAA,QACR,SAAS;AAAA,QACT,IAAI,IAAI,WAAW,GAAG,EAAE;AAAA,MACzB;AAAA,IACD,CAAC;AAAA,EACF;AACD;AAUA,eAAsB,gBAAgB,QAAmB,SAAmC;AAC3F,QAAM,YAAY,OAAO,WAAW;AACpC,QAAM,WAAW,MAAM,MAAM,OAAO,MAAO,eAAe;AAAA,IACzD,QAAQ;AAAA,IACR,SAAS;AAAA,MACR,gBAAgB;AAAA,MAChB,cAAc;AAAA,MACd,mBAAmB;AAAA,MACnB,sBAAsB;AAAA,IACvB;AAAA,IACA,QAAQ,YAAY,QAAQ,OAAO;AAAA,EACpC,CAAC;AAED,QAAM,aAAa,eAAe,UAAU,SAAS;AACrD,yBAAuB,QAAQ;AAC/B,QAAM,kBAAkB,MAAM,SAAS,KAAK;AAE5C,MAAI,gBAAgB,eAAe,OAAO,UAAU;AACnD,WAAO;AAAA,EACR;AACA,QAAM,UAAU,IAAI,WAAW,CAAC,GAAG,SAAS,GAAG,OAAO,IAAI,GAAG,QAAQ,OAAO,QAAQ,CAAC,CAAC;AACtF,SAAO,UAAU,qBAAqB,WAAW,gBAAgB,GAAG,GAAG,SAAS,OAAO,EAAE;AAC1F;AAOO,SAAS,uBAAuB,UAAoB;AAC1D,QAAM,mBAAmB,SAAS,QAAQ,IAAI,qBAAqB;AACnE,MAAI,oBAAoB,MAAM;AAC7B,UAAM,IAAI,6BAA6B,8BAA8B;AAAA,EACtE;AACA,MAAI,IAAI,QAAQ,gBAAgB,EAAE,WAAW,0BAA0B,GAAG;AACzE,UAAM,IAAI;AAAA,MACT,sBAAsB,gBAAgB;AAAA,IACvC;AAAA,EACD;AACD;AAUO,MAAM,gCAAsD;AAAA,EAGlE,YAAmB,KAAgB;AAAhB;AAClB,SAAK,iBAAiB,MAAM,IAAI,QAAQ,CAAC;AAAA,EAC1C;AAAA,EAEA,WAAmB;AAClB,WAAO,KAAK;AAAA,EACb;AACD;",
6
6
  "names": ["KeyServerType"]
7
7
  }
@@ -8,6 +8,13 @@ export declare function xorUnchecked(a: Uint8Array, b: Uint8Array): Uint8Array;
8
8
  * @returns The full ID.
9
9
  */
10
10
  export declare function createFullId(dst: Uint8Array, packageId: string, innerId: string): string;
11
+ /**
12
+ * Flatten an array of Uint8Arrays into a single Uint8Array.
13
+ *
14
+ * @param arrays - An array of Uint8Arrays to flatten.
15
+ * @returns A single Uint8Array containing all the elements of the input arrays in the given order.
16
+ */
17
+ export declare function flatten(arrays: Uint8Array[]): Uint8Array;
11
18
  /**
12
19
  * A simple class to represent a version number of the form x.y.z.
13
20
  */
package/dist/esm/utils.js CHANGED
@@ -14,19 +14,22 @@ function createFullId(dst, packageId, innerId) {
14
14
  if (!isValidSuiObjectId(packageId)) {
15
15
  throw new UserError(`Invalid package ID ${packageId}`);
16
16
  }
17
- const packageIdBytes = fromHex(packageId);
18
- const innerIdBytes = fromHex(innerId);
19
- const fullId = new Uint8Array(1 + dst.length + packageIdBytes.length + innerIdBytes.length);
20
- fullId.set([dst.length], 0);
21
- fullId.set(dst, 1);
22
- fullId.set(packageIdBytes, 1 + dst.length);
23
- fullId.set(innerIdBytes, 1 + dst.length + packageIdBytes.length);
17
+ const fullId = flatten([new Uint8Array([dst.length]), dst, fromHex(packageId), fromHex(innerId)]);
24
18
  return toHex(fullId);
25
19
  }
20
+ function flatten(arrays) {
21
+ const length = arrays.reduce((sum, arr) => sum + arr.length, 0);
22
+ const result = new Uint8Array(length);
23
+ arrays.reduce((offset, array) => {
24
+ result.set(array, offset);
25
+ return offset + array.length;
26
+ }, 0);
27
+ return result;
28
+ }
26
29
  class Version {
27
30
  constructor(version) {
28
31
  const parts = version.split(".").map(Number);
29
- if (parts.length !== 3 || parts.some((part) => isNaN(part) || part < 0)) {
32
+ if (parts.length !== 3 || parts.some((part) => isNaN(part) || !Number.isInteger(part) || part < 0)) {
30
33
  throw new UserError(`Invalid version format: ${version}`);
31
34
  }
32
35
  this.major = parts[0];
@@ -46,6 +49,7 @@ class Version {
46
49
  export {
47
50
  Version,
48
51
  createFullId,
52
+ flatten,
49
53
  xor,
50
54
  xorUnchecked
51
55
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/utils.ts"],
4
- "sourcesContent": ["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { fromHex, toHex } from '@mysten/bcs';\nimport { isValidSuiObjectId } from '@mysten/sui/utils';\n\nimport { UserError } from './error.js';\n\nexport function xor(a: Uint8Array, b: Uint8Array): Uint8Array {\n\tif (a.length !== b.length) {\n\t\tthrow new Error('Invalid input');\n\t}\n\treturn xorUnchecked(a, b);\n}\n\nexport function xorUnchecked(a: Uint8Array, b: Uint8Array): Uint8Array {\n\treturn a.map((ai, i) => ai ^ b[i]);\n}\n\n/**\n * Create a full ID concatenating DST || package ID || inner ID.\n * @param dst - The domain separation tag.\n * @param packageId - The package ID.\n * @param innerId - The inner ID.\n * @returns The full ID.\n */\nexport function createFullId(dst: Uint8Array, packageId: string, innerId: string): string {\n\tif (!isValidSuiObjectId(packageId)) {\n\t\tthrow new UserError(`Invalid package ID ${packageId}`);\n\t}\n\tconst packageIdBytes = fromHex(packageId);\n\tconst innerIdBytes = fromHex(innerId);\n\tconst fullId = new Uint8Array(1 + dst.length + packageIdBytes.length + innerIdBytes.length);\n\tfullId.set([dst.length], 0);\n\tfullId.set(dst, 1);\n\tfullId.set(packageIdBytes, 1 + dst.length);\n\tfullId.set(innerIdBytes, 1 + dst.length + packageIdBytes.length);\n\treturn toHex(fullId);\n}\n\n/**\n * A simple class to represent a version number of the form x.y.z.\n */\nexport class Version {\n\tmajor: number;\n\tminor: number;\n\tpatch: number;\n\n\tconstructor(version: string) {\n\t\t// Very basic version parsing. Assumes version is in the format x.y.z where x, y, and z are non-negative integers.\n\t\tconst parts = version.split('.').map(Number);\n\t\tif (parts.length !== 3 || parts.some((part) => isNaN(part) || part < 0)) {\n\t\t\tthrow new UserError(`Invalid version format: ${version}`);\n\t\t}\n\t\tthis.major = parts[0];\n\t\tthis.minor = parts[1];\n\t\tthis.patch = parts[2];\n\t}\n\n\t// Compare this version with another version. True if this version is older than the other version.\n\tolder_than(other: Version): boolean {\n\t\tif (this.major !== other.major) {\n\t\t\treturn this.major < other.major;\n\t\t} else if (this.minor !== other.minor) {\n\t\t\treturn this.minor < other.minor;\n\t\t}\n\t\treturn this.patch < other.patch;\n\t}\n}\n"],
5
- "mappings": "AAGA,SAAS,SAAS,aAAa;AAC/B,SAAS,0BAA0B;AAEnC,SAAS,iBAAiB;AAEnB,SAAS,IAAI,GAAe,GAA2B;AAC7D,MAAI,EAAE,WAAW,EAAE,QAAQ;AAC1B,UAAM,IAAI,MAAM,eAAe;AAAA,EAChC;AACA,SAAO,aAAa,GAAG,CAAC;AACzB;AAEO,SAAS,aAAa,GAAe,GAA2B;AACtE,SAAO,EAAE,IAAI,CAAC,IAAI,MAAM,KAAK,EAAE,CAAC,CAAC;AAClC;AASO,SAAS,aAAa,KAAiB,WAAmB,SAAyB;AACzF,MAAI,CAAC,mBAAmB,SAAS,GAAG;AACnC,UAAM,IAAI,UAAU,sBAAsB,SAAS,EAAE;AAAA,EACtD;AACA,QAAM,iBAAiB,QAAQ,SAAS;AACxC,QAAM,eAAe,QAAQ,OAAO;AACpC,QAAM,SAAS,IAAI,WAAW,IAAI,IAAI,SAAS,eAAe,SAAS,aAAa,MAAM;AAC1F,SAAO,IAAI,CAAC,IAAI,MAAM,GAAG,CAAC;AAC1B,SAAO,IAAI,KAAK,CAAC;AACjB,SAAO,IAAI,gBAAgB,IAAI,IAAI,MAAM;AACzC,SAAO,IAAI,cAAc,IAAI,IAAI,SAAS,eAAe,MAAM;AAC/D,SAAO,MAAM,MAAM;AACpB;AAKO,MAAM,QAAQ;AAAA,EAKpB,YAAY,SAAiB;AAE5B,UAAM,QAAQ,QAAQ,MAAM,GAAG,EAAE,IAAI,MAAM;AAC3C,QAAI,MAAM,WAAW,KAAK,MAAM,KAAK,CAAC,SAAS,MAAM,IAAI,KAAK,OAAO,CAAC,GAAG;AACxE,YAAM,IAAI,UAAU,2BAA2B,OAAO,EAAE;AAAA,IACzD;AACA,SAAK,QAAQ,MAAM,CAAC;AACpB,SAAK,QAAQ,MAAM,CAAC;AACpB,SAAK,QAAQ,MAAM,CAAC;AAAA,EACrB;AAAA;AAAA,EAGA,WAAW,OAAyB;AACnC,QAAI,KAAK,UAAU,MAAM,OAAO;AAC/B,aAAO,KAAK,QAAQ,MAAM;AAAA,IAC3B,WAAW,KAAK,UAAU,MAAM,OAAO;AACtC,aAAO,KAAK,QAAQ,MAAM;AAAA,IAC3B;AACA,WAAO,KAAK,QAAQ,MAAM;AAAA,EAC3B;AACD;",
4
+ "sourcesContent": ["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { fromHex, toHex } from '@mysten/bcs';\nimport { isValidSuiObjectId } from '@mysten/sui/utils';\n\nimport { UserError } from './error.js';\n\nexport function xor(a: Uint8Array, b: Uint8Array): Uint8Array {\n\tif (a.length !== b.length) {\n\t\tthrow new Error('Invalid input');\n\t}\n\treturn xorUnchecked(a, b);\n}\n\nexport function xorUnchecked(a: Uint8Array, b: Uint8Array): Uint8Array {\n\treturn a.map((ai, i) => ai ^ b[i]);\n}\n\n/**\n * Create a full ID concatenating DST || package ID || inner ID.\n * @param dst - The domain separation tag.\n * @param packageId - The package ID.\n * @param innerId - The inner ID.\n * @returns The full ID.\n */\nexport function createFullId(dst: Uint8Array, packageId: string, innerId: string): string {\n\tif (!isValidSuiObjectId(packageId)) {\n\t\tthrow new UserError(`Invalid package ID ${packageId}`);\n\t}\n\tconst fullId = flatten([new Uint8Array([dst.length]), dst, fromHex(packageId), fromHex(innerId)]);\n\treturn toHex(fullId);\n}\n\n/**\n * Flatten an array of Uint8Arrays into a single Uint8Array.\n *\n * @param arrays - An array of Uint8Arrays to flatten.\n * @returns A single Uint8Array containing all the elements of the input arrays in the given order.\n */\nexport function flatten(arrays: Uint8Array[]): Uint8Array {\n\tconst length = arrays.reduce((sum, arr) => sum + arr.length, 0);\n\tconst result = new Uint8Array(length);\n\tarrays.reduce((offset, array) => {\n\t\tresult.set(array, offset);\n\t\treturn offset + array.length;\n\t}, 0);\n\treturn result;\n}\n\n/**\n * A simple class to represent a version number of the form x.y.z.\n */\nexport class Version {\n\tmajor: number;\n\tminor: number;\n\tpatch: number;\n\n\tconstructor(version: string) {\n\t\t// Very basic version parsing. Assumes version is in the format x.y.z where x, y, and z are non-negative integers.\n\t\tconst parts = version.split('.').map(Number);\n\t\tif (\n\t\t\tparts.length !== 3 ||\n\t\t\tparts.some((part) => isNaN(part) || !Number.isInteger(part) || part < 0)\n\t\t) {\n\t\t\tthrow new UserError(`Invalid version format: ${version}`);\n\t\t}\n\t\tthis.major = parts[0];\n\t\tthis.minor = parts[1];\n\t\tthis.patch = parts[2];\n\t}\n\n\t// Compare this version with another version. True if this version is older than the other version.\n\tolder_than(other: Version): boolean {\n\t\tif (this.major !== other.major) {\n\t\t\treturn this.major < other.major;\n\t\t} else if (this.minor !== other.minor) {\n\t\t\treturn this.minor < other.minor;\n\t\t}\n\t\treturn this.patch < other.patch;\n\t}\n}\n"],
5
+ "mappings": "AAGA,SAAS,SAAS,aAAa;AAC/B,SAAS,0BAA0B;AAEnC,SAAS,iBAAiB;AAEnB,SAAS,IAAI,GAAe,GAA2B;AAC7D,MAAI,EAAE,WAAW,EAAE,QAAQ;AAC1B,UAAM,IAAI,MAAM,eAAe;AAAA,EAChC;AACA,SAAO,aAAa,GAAG,CAAC;AACzB;AAEO,SAAS,aAAa,GAAe,GAA2B;AACtE,SAAO,EAAE,IAAI,CAAC,IAAI,MAAM,KAAK,EAAE,CAAC,CAAC;AAClC;AASO,SAAS,aAAa,KAAiB,WAAmB,SAAyB;AACzF,MAAI,CAAC,mBAAmB,SAAS,GAAG;AACnC,UAAM,IAAI,UAAU,sBAAsB,SAAS,EAAE;AAAA,EACtD;AACA,QAAM,SAAS,QAAQ,CAAC,IAAI,WAAW,CAAC,IAAI,MAAM,CAAC,GAAG,KAAK,QAAQ,SAAS,GAAG,QAAQ,OAAO,CAAC,CAAC;AAChG,SAAO,MAAM,MAAM;AACpB;AAQO,SAAS,QAAQ,QAAkC;AACzD,QAAM,SAAS,OAAO,OAAO,CAAC,KAAK,QAAQ,MAAM,IAAI,QAAQ,CAAC;AAC9D,QAAM,SAAS,IAAI,WAAW,MAAM;AACpC,SAAO,OAAO,CAAC,QAAQ,UAAU;AAChC,WAAO,IAAI,OAAO,MAAM;AACxB,WAAO,SAAS,MAAM;AAAA,EACvB,GAAG,CAAC;AACJ,SAAO;AACR;AAKO,MAAM,QAAQ;AAAA,EAKpB,YAAY,SAAiB;AAE5B,UAAM,QAAQ,QAAQ,MAAM,GAAG,EAAE,IAAI,MAAM;AAC3C,QACC,MAAM,WAAW,KACjB,MAAM,KAAK,CAAC,SAAS,MAAM,IAAI,KAAK,CAAC,OAAO,UAAU,IAAI,KAAK,OAAO,CAAC,GACtE;AACD,YAAM,IAAI,UAAU,2BAA2B,OAAO,EAAE;AAAA,IACzD;AACA,SAAK,QAAQ,MAAM,CAAC;AACpB,SAAK,QAAQ,MAAM,CAAC;AACpB,SAAK,QAAQ,MAAM,CAAC;AAAA,EACrB;AAAA;AAAA,EAGA,WAAW,OAAyB;AACnC,QAAI,KAAK,UAAU,MAAM,OAAO;AAC/B,aAAO,KAAK,QAAQ,MAAM;AAAA,IAC3B,WAAW,KAAK,UAAU,MAAM,OAAO;AACtC,aAAO,KAAK,QAAQ,MAAM;AAAA,IAC3B;AACA,WAAO,KAAK,QAAQ,MAAM;AAAA,EAC3B;AACD;",
6
6
  "names": []
7
7
  }
@@ -1 +1 @@
1
- export declare const PACKAGE_VERSION = "0.4.2";
1
+ export declare const PACKAGE_VERSION = "0.4.3";
@@ -1,4 +1,4 @@
1
- const PACKAGE_VERSION = "0.4.2";
1
+ const PACKAGE_VERSION = "0.4.3";
2
2
  export {
3
3
  PACKAGE_VERSION
4
4
  };