@mysten/seal 0.10.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/dist/bcs.d.mts +40 -0
  3. package/dist/bcs.d.mts.map +1 -0
  4. package/dist/bcs.mjs +89 -0
  5. package/dist/bcs.mjs.map +1 -0
  6. package/dist/bls12381.d.mts +30 -0
  7. package/dist/bls12381.d.mts.map +1 -0
  8. package/dist/bls12381.mjs +135 -0
  9. package/dist/bls12381.mjs.map +1 -0
  10. package/dist/client.d.mts +106 -0
  11. package/dist/client.d.mts.map +1 -0
  12. package/dist/client.mjs +274 -0
  13. package/dist/client.mjs.map +1 -0
  14. package/dist/decrypt.mjs +53 -0
  15. package/dist/decrypt.mjs.map +1 -0
  16. package/dist/dem.d.mts +1 -0
  17. package/dist/dem.mjs +134 -0
  18. package/dist/dem.mjs.map +1 -0
  19. package/dist/elgamal.mjs +35 -0
  20. package/dist/elgamal.mjs.map +1 -0
  21. package/dist/encrypt.d.mts +15 -0
  22. package/dist/encrypt.d.mts.map +1 -0
  23. package/dist/encrypt.mjs +61 -0
  24. package/dist/encrypt.mjs.map +1 -0
  25. package/dist/error.d.mts +75 -0
  26. package/dist/error.d.mts.map +1 -0
  27. package/dist/error.mjs +150 -0
  28. package/dist/error.mjs.map +1 -0
  29. package/dist/ibe.mjs +176 -0
  30. package/dist/ibe.mjs.map +1 -0
  31. package/dist/index.d.mts +7 -0
  32. package/dist/index.mjs +7 -0
  33. package/dist/kdf.mjs +81 -0
  34. package/dist/kdf.mjs.map +1 -0
  35. package/dist/key-server.d.mts +22 -0
  36. package/dist/key-server.d.mts.map +1 -0
  37. package/dist/key-server.mjs +195 -0
  38. package/dist/key-server.mjs.map +1 -0
  39. package/dist/session-key.d.mts +83 -0
  40. package/dist/session-key.d.mts.map +1 -0
  41. package/dist/session-key.mjs +171 -0
  42. package/dist/session-key.mjs.map +1 -0
  43. package/dist/shamir.mjs +730 -0
  44. package/dist/shamir.mjs.map +1 -0
  45. package/dist/types.d.mts +79 -0
  46. package/dist/types.d.mts.map +1 -0
  47. package/dist/utils.mjs +89 -0
  48. package/dist/utils.mjs.map +1 -0
  49. package/dist/version.mjs +6 -0
  50. package/dist/version.mjs.map +1 -0
  51. package/package.json +22 -20
  52. package/dist/cjs/bcs.d.ts +0 -147
  53. package/dist/cjs/bcs.js +0 -104
  54. package/dist/cjs/bcs.js.map +0 -7
  55. package/dist/cjs/bls12381.d.ts +0 -44
  56. package/dist/cjs/bls12381.js +0 -151
  57. package/dist/cjs/bls12381.js.map +0 -7
  58. package/dist/cjs/client.d.ts +0 -84
  59. package/dist/cjs/client.js +0 -419
  60. package/dist/cjs/client.js.map +0 -7
  61. package/dist/cjs/decrypt.d.ts +0 -22
  62. package/dist/cjs/decrypt.js +0 -109
  63. package/dist/cjs/decrypt.js.map +0 -7
  64. package/dist/cjs/dem.d.ts +0 -38
  65. package/dist/cjs/dem.js +0 -185
  66. package/dist/cjs/dem.js.map +0 -7
  67. package/dist/cjs/elgamal.d.ts +0 -13
  68. package/dist/cjs/elgamal.js +0 -46
  69. package/dist/cjs/elgamal.js.map +0 -7
  70. package/dist/cjs/encrypt.d.ts +0 -32
  71. package/dist/cjs/encrypt.js +0 -104
  72. package/dist/cjs/encrypt.js.map +0 -7
  73. package/dist/cjs/error.d.ts +0 -86
  74. package/dist/cjs/error.js +0 -239
  75. package/dist/cjs/error.js.map +0 -7
  76. package/dist/cjs/ibe.d.ts +0 -98
  77. package/dist/cjs/ibe.js +0 -167
  78. package/dist/cjs/ibe.js.map +0 -7
  79. package/dist/cjs/index.d.ts +0 -6
  80. package/dist/cjs/index.js +0 -33
  81. package/dist/cjs/index.js.map +0 -7
  82. package/dist/cjs/kdf.d.ts +0 -30
  83. package/dist/cjs/kdf.js +0 -97
  84. package/dist/cjs/kdf.js.map +0 -7
  85. package/dist/cjs/key-server.d.ts +0 -105
  86. package/dist/cjs/key-server.js +0 -230
  87. package/dist/cjs/key-server.js.map +0 -7
  88. package/dist/cjs/package.json +0 -5
  89. package/dist/cjs/session-key.d.ts +0 -74
  90. package/dist/cjs/session-key.js +0 -245
  91. package/dist/cjs/session-key.js.map +0 -7
  92. package/dist/cjs/shamir.d.ts +0 -91
  93. package/dist/cjs/shamir.js +0 -770
  94. package/dist/cjs/shamir.js.map +0 -7
  95. package/dist/cjs/types.d.ts +0 -86
  96. package/dist/cjs/types.js +0 -17
  97. package/dist/cjs/types.js.map +0 -7
  98. package/dist/cjs/utils.d.ts +0 -47
  99. package/dist/cjs/utils.js +0 -106
  100. package/dist/cjs/utils.js.map +0 -7
  101. package/dist/cjs/version.d.ts +0 -1
  102. package/dist/cjs/version.js +0 -25
  103. package/dist/cjs/version.js.map +0 -7
  104. package/dist/esm/bcs.d.ts +0 -147
  105. package/dist/esm/bcs.js +0 -84
  106. package/dist/esm/bcs.js.map +0 -7
  107. package/dist/esm/bls12381.d.ts +0 -44
  108. package/dist/esm/bls12381.js +0 -131
  109. package/dist/esm/bls12381.js.map +0 -7
  110. package/dist/esm/client.d.ts +0 -84
  111. package/dist/esm/client.js +0 -412
  112. package/dist/esm/client.js.map +0 -7
  113. package/dist/esm/decrypt.d.ts +0 -22
  114. package/dist/esm/decrypt.js +0 -94
  115. package/dist/esm/decrypt.js.map +0 -7
  116. package/dist/esm/dem.d.ts +0 -38
  117. package/dist/esm/dem.js +0 -165
  118. package/dist/esm/dem.js.map +0 -7
  119. package/dist/esm/elgamal.d.ts +0 -13
  120. package/dist/esm/elgamal.js +0 -26
  121. package/dist/esm/elgamal.js.map +0 -7
  122. package/dist/esm/encrypt.d.ts +0 -32
  123. package/dist/esm/encrypt.js +0 -84
  124. package/dist/esm/encrypt.js.map +0 -7
  125. package/dist/esm/error.d.ts +0 -86
  126. package/dist/esm/error.js +0 -219
  127. package/dist/esm/error.js.map +0 -7
  128. package/dist/esm/ibe.d.ts +0 -98
  129. package/dist/esm/ibe.js +0 -147
  130. package/dist/esm/ibe.js.map +0 -7
  131. package/dist/esm/index.d.ts +0 -6
  132. package/dist/esm/index.js +0 -12
  133. package/dist/esm/index.js.map +0 -7
  134. package/dist/esm/kdf.d.ts +0 -30
  135. package/dist/esm/kdf.js +0 -83
  136. package/dist/esm/kdf.js.map +0 -7
  137. package/dist/esm/key-server.d.ts +0 -105
  138. package/dist/esm/key-server.js +0 -215
  139. package/dist/esm/key-server.js.map +0 -7
  140. package/dist/esm/package.json +0 -5
  141. package/dist/esm/session-key.d.ts +0 -74
  142. package/dist/esm/session-key.js +0 -230
  143. package/dist/esm/session-key.js.map +0 -7
  144. package/dist/esm/shamir.d.ts +0 -91
  145. package/dist/esm/shamir.js +0 -750
  146. package/dist/esm/shamir.js.map +0 -7
  147. package/dist/esm/types.d.ts +0 -86
  148. package/dist/esm/types.js +0 -1
  149. package/dist/esm/types.js.map +0 -7
  150. package/dist/esm/utils.d.ts +0 -47
  151. package/dist/esm/utils.js +0 -86
  152. package/dist/esm/utils.js.map +0 -7
  153. package/dist/esm/version.d.ts +0 -1
  154. package/dist/esm/version.js +0 -5
  155. package/dist/esm/version.js.map +0 -7
  156. package/dist/tsconfig.esm.tsbuildinfo +0 -1
  157. package/dist/tsconfig.tsbuildinfo +0 -1
@@ -0,0 +1,22 @@
1
+ import "./session-key.mjs";
2
+ import "./types.mjs";
3
+
4
+ //#region src/key-server.d.ts
5
+ type ServerType = 'Independent' | 'Committee';
6
+ type KeyServer = {
7
+ objectId: string;
8
+ name: string;
9
+ url: string;
10
+ keyType: KeyType;
11
+ pk: Uint8Array<ArrayBuffer>;
12
+ serverType: ServerType;
13
+ };
14
+ declare enum KeyType {
15
+ BonehFranklinBLS12381 = 0,
16
+ }
17
+ interface DerivedKey {
18
+ toString(): string;
19
+ }
20
+ //#endregion
21
+ export { DerivedKey, KeyServer };
22
+ //# sourceMappingURL=key-server.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"key-server.d.mts","names":[],"sources":["../src/key-server.ts"],"sourcesContent":[],"mappings":";;;;AAsBY,KAAA,UAAA,GAAU,aAAA,GAAA,WAAA;AAEV,KAAA,SAAA,GAAS;EAIX,QAAA,EAAA,MAAA;EACM,IAAA,EAAA,MAAA;EAAX,GAAA,EAAA,MAAA;EACQ,OAAA,EAFH,OAEG;EAAU,EAAA,EADlB,UACkB,CADP,WACO,CAAA;EAGX,UAAO,EAHN,UAGM;AAkMnB,CAAA;aAlMY,OAAA;;;UAkMK,UAAA"}
@@ -0,0 +1,195 @@
1
+ import { KeyServerMove, KeyServerMoveV1, KeyServerMoveV2 } from "./bcs.mjs";
2
+ import { InvalidClientOptionsError, InvalidKeyServerError, InvalidKeyServerVersionError, SealAPIError } from "./error.mjs";
3
+ import { Version, flatten } from "./utils.mjs";
4
+ import { DST_POP } from "./ibe.mjs";
5
+ import { PACKAGE_VERSION } from "./version.mjs";
6
+ import { elgamalDecrypt } from "./elgamal.mjs";
7
+ import { bcs, fromBase64, fromHex, toBase64, toHex } from "@mysten/bcs";
8
+ import { bls12_381 } from "@noble/curves/bls12-381.js";
9
+
10
+ //#region src/key-server.ts
11
+ const SUPPORTED_SERVER_VERSIONS = [2, 1];
12
+ let KeyType = /* @__PURE__ */ function(KeyType$1) {
13
+ KeyType$1[KeyType$1["BonehFranklinBLS12381"] = 0] = "BonehFranklinBLS12381";
14
+ return KeyType$1;
15
+ }({});
16
+ const SERVER_VERSION_REQUIREMENT = new Version("0.4.1");
17
+ /**
18
+ * Given a list of key server object IDs, returns a list of SealKeyServer
19
+ * from onchain state containing name, objectId, URL and pk.
20
+ *
21
+ * Supports both V1 (independent servers) and V2 (independent + committee servers).
22
+ * For V2 committee servers, returns the aggregator URL from the config.
23
+ *
24
+ * @param objectIds - The key server object IDs.
25
+ * @param client - The Sui client to use (SuiGrpcClient, SuiGraphQLClient, or SuiJsonRpcClient).
26
+ * @param configs - The key server configurations containing aggregator URLs.
27
+ * @returns - An array of SealKeyServer.
28
+ */
29
+ async function retrieveKeyServers({ objectIds, client, configs }) {
30
+ return await Promise.all(objectIds.map(async (objectId) => {
31
+ const res = await client.core.getObject({
32
+ objectId,
33
+ include: { content: true }
34
+ });
35
+ const ks = KeyServerMove.parse(res.object.content);
36
+ const firstVersion = Number(ks.firstVersion);
37
+ const lastVersion = Number(ks.lastVersion);
38
+ const version = SUPPORTED_SERVER_VERSIONS.find((v) => v >= firstVersion && v <= lastVersion);
39
+ if (version === void 0) throw new InvalidKeyServerVersionError(`Key server ${objectId} supports versions between ${ks.firstVersion} and ${ks.lastVersion} (inclusive), but SDK expects one of ${SUPPORTED_SERVER_VERSIONS.join(", ")}`);
40
+ const versionedKeyServer = await client.core.getDynamicField({
41
+ parentId: objectId,
42
+ name: {
43
+ type: "u64",
44
+ bcs: bcs.u64().serialize(version).toBytes()
45
+ }
46
+ });
47
+ switch (version) {
48
+ case 2: {
49
+ const ksV2 = KeyServerMoveV2.parse(versionedKeyServer.dynamicField.value.bcs);
50
+ if (ksV2.keyType !== KeyType.BonehFranklinBLS12381) throw new InvalidKeyServerError(`Server ${objectId} has invalid key type: ${ksV2.keyType}`);
51
+ switch (ksV2.serverType.$kind) {
52
+ case "Independent":
53
+ if (configs.get(objectId)?.aggregatorUrl) throw new InvalidClientOptionsError(`Independent server ${objectId} should not have aggregatorUrl in config`);
54
+ return {
55
+ objectId,
56
+ name: ksV2.name,
57
+ url: ksV2.serverType.Independent.url,
58
+ keyType: ksV2.keyType,
59
+ pk: new Uint8Array(ksV2.pk),
60
+ serverType: "Independent"
61
+ };
62
+ case "Committee": {
63
+ const config = configs.get(objectId);
64
+ if (!config?.aggregatorUrl) throw new InvalidClientOptionsError(`Committee server ${objectId} requires aggregatorUrl in config`);
65
+ return {
66
+ objectId,
67
+ name: ksV2.name,
68
+ url: config.aggregatorUrl,
69
+ keyType: ksV2.keyType,
70
+ pk: new Uint8Array(ksV2.pk),
71
+ serverType: "Committee"
72
+ };
73
+ }
74
+ default: throw new InvalidKeyServerError(`Unknown server type for ${objectId}`);
75
+ }
76
+ }
77
+ case 1: {
78
+ const ksV1 = KeyServerMoveV1.parse(versionedKeyServer.dynamicField.value.bcs);
79
+ if (ksV1.keyType !== KeyType.BonehFranklinBLS12381) throw new InvalidKeyServerError(`Server ${objectId} has invalid key type: ${ksV1.keyType}`);
80
+ if (configs.get(objectId)?.aggregatorUrl) throw new InvalidClientOptionsError(`V1 server ${objectId} is always Independent and should not have aggregatorUrl in config`);
81
+ return {
82
+ objectId,
83
+ name: ksV1.name,
84
+ url: ksV1.url,
85
+ keyType: ksV1.keyType,
86
+ pk: new Uint8Array(ksV1.pk),
87
+ serverType: "Independent"
88
+ };
89
+ }
90
+ default: throw new InvalidKeyServerVersionError(`Unsupported key server version: ${version}`);
91
+ }
92
+ }));
93
+ }
94
+ /**
95
+ * Given a KeyServer, fetch the proof of possession (PoP) from the URL and verify it
96
+ * against the pubkey. This should be used only rarely when the dapp uses a dynamic
97
+ * set of key servers.
98
+ *
99
+ * @param server - The KeyServer to verify.
100
+ * @returns - True if the key server is valid, false otherwise.
101
+ */
102
+ async function verifyKeyServer(server, timeout, apiKeyName, apiKey) {
103
+ const requestId = crypto.randomUUID();
104
+ const response = await fetch(server.url + "/v1/service?service_id=" + server.objectId, {
105
+ method: "GET",
106
+ headers: {
107
+ "Content-Type": "application/json",
108
+ "Request-Id": requestId,
109
+ "Client-Sdk-Type": "typescript",
110
+ "Client-Sdk-Version": PACKAGE_VERSION,
111
+ ...apiKeyName && apiKey ? { [apiKeyName]: apiKey } : {}
112
+ },
113
+ signal: AbortSignal.timeout(timeout)
114
+ });
115
+ await SealAPIError.assertResponse(response, requestId);
116
+ verifyKeyServerVersion(response);
117
+ const serviceResponse = await response.json();
118
+ if (serviceResponse.service_id !== server.objectId) return false;
119
+ const fullMsg = flatten([
120
+ DST_POP,
121
+ server.pk,
122
+ fromHex(server.objectId)
123
+ ]);
124
+ return bls12_381.shortSignatures.verify(fromBase64(serviceResponse.pop), bls12_381.shortSignatures.hash(fullMsg), server.pk);
125
+ }
126
+ /**
127
+ * Verify the key server version. Throws an `InvalidKeyServerError` if the version is not supported.
128
+ *
129
+ * @param response - The response from the key server.
130
+ */
131
+ function verifyKeyServerVersion(response) {
132
+ const keyServerVersion = response.headers.get("X-KeyServer-Version");
133
+ if (keyServerVersion == null) throw new InvalidKeyServerVersionError("Key server version not found");
134
+ if (new Version(keyServerVersion).older_than(SERVER_VERSION_REQUIREMENT)) throw new InvalidKeyServerVersionError(`Key server version ${keyServerVersion} is not supported`);
135
+ }
136
+ /**
137
+ * A user secret key for the Boneh-Franklin BLS12381 scheme.
138
+ * This is a wrapper around the G1Element type.
139
+ */
140
+ var BonehFranklinBLS12381DerivedKey = class {
141
+ constructor(key) {
142
+ this.key = key;
143
+ this.representation = toHex(key.toBytes());
144
+ }
145
+ toString() {
146
+ return this.representation;
147
+ }
148
+ };
149
+ /**
150
+ * Helper function to request all keys from URL with requestSig, txBytes, ephemeral pubkey.
151
+ * Then decrypt the Seal key with ephemeral secret key. Returns a list decryption keys with
152
+ * their full IDs.
153
+ *
154
+ * @param url - The URL of the key server.
155
+ * @param requestSig - The Base64 string of request signature.
156
+ * @param txBytes - The transaction bytes.
157
+ * @param encKey - The ephemeral secret key.
158
+ * @param certificate - The certificate.
159
+ * @returns - A list of full ID and the decrypted key.
160
+ */
161
+ async function fetchKeysForAllIds({ url, requestSignature, transactionBytes, encKey, encKeyPk, encVerificationKey, certificate, timeout, apiKeyName, apiKey, signal }) {
162
+ const body = {
163
+ ptb: toBase64(transactionBytes.slice(1)),
164
+ enc_key: toBase64(encKeyPk),
165
+ enc_verification_key: toBase64(encVerificationKey),
166
+ request_signature: requestSignature,
167
+ certificate
168
+ };
169
+ const timeoutSignal = AbortSignal.timeout(timeout);
170
+ const combinedSignal = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
171
+ const requestId = crypto.randomUUID();
172
+ const response = await fetch(url + "/v1/fetch_key", {
173
+ method: "POST",
174
+ headers: {
175
+ "Content-Type": "application/json",
176
+ "Request-Id": requestId,
177
+ "Client-Sdk-Type": "typescript",
178
+ "Client-Sdk-Version": PACKAGE_VERSION,
179
+ ...apiKeyName && apiKey ? { [apiKeyName]: apiKey } : {}
180
+ },
181
+ body: JSON.stringify(body),
182
+ signal: combinedSignal
183
+ });
184
+ await SealAPIError.assertResponse(response, requestId);
185
+ const resp = await response.json();
186
+ verifyKeyServerVersion(response);
187
+ return resp.decryption_keys.map((dk) => ({
188
+ fullId: toHex(dk.id),
189
+ key: elgamalDecrypt(encKey, dk.encrypted_key.map(fromBase64))
190
+ }));
191
+ }
192
+
193
+ //#endregion
194
+ export { BonehFranklinBLS12381DerivedKey, fetchKeysForAllIds, retrieveKeyServers, verifyKeyServer };
195
+ //# sourceMappingURL=key-server.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"key-server.mjs","names":[],"sources":["../src/key-server.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\nimport { bcs, fromBase64, fromHex, toBase64, toHex } from '@mysten/bcs';\nimport { bls12_381 } from '@noble/curves/bls12-381.js';\n\nimport { KeyServerMove, KeyServerMoveV1, KeyServerMoveV2 } from './bcs.js';\nimport {\n\tInvalidClientOptionsError,\n\tInvalidKeyServerError,\n\tInvalidKeyServerVersionError,\n\tSealAPIError,\n} from './error.js';\nimport { DST_POP } from './ibe.js';\nimport { PACKAGE_VERSION } from './version.js';\nimport type { KeyServerConfig, SealCompatibleClient } from './types.js';\nimport type { G1Element } from './bls12381.js';\nimport { flatten, Version } from './utils.js';\nimport { elgamalDecrypt } from './elgamal.js';\nimport type { Certificate } from './session-key.js';\n\nconst SUPPORTED_SERVER_VERSIONS = [2, 1]; // Must be configured in descending order.\n\nexport type ServerType = 'Independent' | 'Committee';\n\nexport type KeyServer = {\n\tobjectId: string;\n\tname: string;\n\turl: string;\n\tkeyType: KeyType;\n\tpk: Uint8Array<ArrayBuffer>;\n\tserverType: ServerType;\n};\n\nexport enum KeyType {\n\tBonehFranklinBLS12381 = 0,\n}\n\nexport const SERVER_VERSION_REQUIREMENT = new Version('0.4.1');\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 * Supports both V1 (independent servers) and V2 (independent + committee servers).\n * For V2 committee servers, returns the aggregator URL from the config.\n *\n * @param objectIds - The key server object IDs.\n * @param client - The Sui client to use (SuiGrpcClient, SuiGraphQLClient, or SuiJsonRpcClient).\n * @param configs - The key server configurations containing aggregator URLs.\n * @returns - An array of SealKeyServer.\n */\nexport async function retrieveKeyServers({\n\tobjectIds,\n\tclient,\n\tconfigs,\n}: {\n\tobjectIds: string[];\n\tclient: SealCompatibleClient;\n\tconfigs: Map<string, KeyServerConfig>;\n}): Promise<KeyServer[]> {\n\treturn await Promise.all(\n\t\tobjectIds.map(async (objectId) => {\n\t\t\t// First get the KeyServer object and validate it.\n\t\t\tconst res = await client.core.getObject({\n\t\t\t\tobjectId,\n\t\t\t\tinclude: { content: true },\n\t\t\t});\n\t\t\tconst ks = KeyServerMove.parse(res.object.content);\n\n\t\t\t// Find the highest supported version.\n\t\t\tconst firstVersion = Number(ks.firstVersion);\n\t\t\tconst lastVersion = Number(ks.lastVersion);\n\t\t\tconst version = SUPPORTED_SERVER_VERSIONS.find((v) => v >= firstVersion && v <= lastVersion);\n\n\t\t\tif (version === undefined) {\n\t\t\t\tthrow new InvalidKeyServerVersionError(\n\t\t\t\t\t`Key server ${objectId} supports versions between ${ks.firstVersion} and ${ks.lastVersion} (inclusive), but SDK expects one of ${SUPPORTED_SERVER_VERSIONS.join(', ')}`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Fetch the versioned object.\n\t\t\tconst versionedKeyServer = await client.core.getDynamicField({\n\t\t\t\tparentId: objectId,\n\t\t\t\tname: {\n\t\t\t\t\ttype: 'u64',\n\t\t\t\t\tbcs: bcs.u64().serialize(version).toBytes(),\n\t\t\t\t},\n\t\t\t});\n\n\t\t\t// Parse based on version.\n\t\t\tswitch (version) {\n\t\t\t\tcase 2: {\n\t\t\t\t\tconst ksV2 = KeyServerMoveV2.parse(versionedKeyServer.dynamicField.value.bcs);\n\t\t\t\t\tif (ksV2.keyType !== KeyType.BonehFranklinBLS12381) {\n\t\t\t\t\t\tthrow new InvalidKeyServerError(\n\t\t\t\t\t\t\t`Server ${objectId} has invalid key type: ${ksV2.keyType}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Return based on server type.\n\t\t\t\t\tswitch (ksV2.serverType.$kind) {\n\t\t\t\t\t\tcase 'Independent': {\n\t\t\t\t\t\t\tif (configs.get(objectId)?.aggregatorUrl) {\n\t\t\t\t\t\t\t\tthrow new InvalidClientOptionsError(\n\t\t\t\t\t\t\t\t\t`Independent server ${objectId} should not have aggregatorUrl in config`,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tobjectId,\n\t\t\t\t\t\t\t\tname: ksV2.name,\n\t\t\t\t\t\t\t\turl: ksV2.serverType.Independent.url,\n\t\t\t\t\t\t\t\tkeyType: ksV2.keyType,\n\t\t\t\t\t\t\t\tpk: new Uint8Array(ksV2.pk),\n\t\t\t\t\t\t\t\tserverType: 'Independent',\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcase 'Committee': {\n\t\t\t\t\t\t\t// For committee mode, get aggregator URL from config\n\t\t\t\t\t\t\tconst config = configs.get(objectId);\n\t\t\t\t\t\t\tif (!config?.aggregatorUrl) {\n\t\t\t\t\t\t\t\tthrow new InvalidClientOptionsError(\n\t\t\t\t\t\t\t\t\t`Committee server ${objectId} requires aggregatorUrl in config`,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tobjectId,\n\t\t\t\t\t\t\t\tname: ksV2.name,\n\t\t\t\t\t\t\t\turl: config.aggregatorUrl,\n\t\t\t\t\t\t\t\tkeyType: ksV2.keyType,\n\t\t\t\t\t\t\t\tpk: new Uint8Array(ksV2.pk),\n\t\t\t\t\t\t\t\tserverType: 'Committee',\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tthrow new InvalidKeyServerError(`Unknown server type for ${objectId}`);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcase 1: {\n\t\t\t\t\tconst ksV1 = KeyServerMoveV1.parse(versionedKeyServer.dynamicField.value.bcs);\n\t\t\t\t\tif (ksV1.keyType !== KeyType.BonehFranklinBLS12381) {\n\t\t\t\t\t\tthrow new InvalidKeyServerError(\n\t\t\t\t\t\t\t`Server ${objectId} has invalid key type: ${ksV1.keyType}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\t// V1 servers are always Independent and should not have aggregatorUrl\n\t\t\t\t\tif (configs.get(objectId)?.aggregatorUrl) {\n\t\t\t\t\t\tthrow new InvalidClientOptionsError(\n\t\t\t\t\t\t\t`V1 server ${objectId} is always Independent and should not have aggregatorUrl in config`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\treturn {\n\t\t\t\t\t\tobjectId,\n\t\t\t\t\t\tname: ksV1.name,\n\t\t\t\t\t\turl: ksV1.url,\n\t\t\t\t\t\tkeyType: ksV1.keyType,\n\t\t\t\t\t\tpk: new Uint8Array(ksV1.pk),\n\t\t\t\t\t\tserverType: 'Independent',\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t\tthrow new InvalidKeyServerVersionError(`Unsupported key server version: ${version}`);\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(\n\tserver: KeyServer,\n\ttimeout: number,\n\tapiKeyName?: string,\n\tapiKey?: string,\n): Promise<boolean> {\n\tconst requestId = crypto.randomUUID();\n\tconst response = await fetch(server.url! + '/v1/service?service_id=' + server.objectId, {\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\t...(apiKeyName && apiKey ? { [apiKeyName]: apiKey } : {}),\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 = flatten([DST_POP, server.pk, fromHex(server.objectId)]);\n\treturn bls12_381.shortSignatures.verify(\n\t\tfromBase64(serviceResponse.pop),\n\t\tbls12_381.shortSignatures.hash(fullMsg),\n\t\tserver.pk,\n\t);\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\n/**\n * Options for fetching keys from the key server.\n */\nexport interface FetchKeysOptions {\n\t/** The URL of the key server. */\n\turl: string;\n\t/** The Base64 string of request signature. */\n\trequestSignature: string;\n\t/** The transaction bytes. */\n\ttransactionBytes: Uint8Array;\n\t/** The ephemeral secret key. */\n\tencKey: Uint8Array<ArrayBuffer>;\n\t/** The ephemeral public key. */\n\tencKeyPk: Uint8Array<ArrayBuffer>;\n\t/** The ephemeral verification key. */\n\tencVerificationKey: Uint8Array;\n\t/** The certificate. */\n\tcertificate: Certificate;\n\t/** Request timeout in milliseconds. */\n\ttimeout: number;\n\t/** Optional API key name. */\n\tapiKeyName?: string;\n\t/** Optional API key. */\n\tapiKey?: string;\n\t/** Optional abort signal for cancellation. */\n\tsignal?: AbortSignal;\n}\n\n/**\n * Helper function to request all keys from URL with requestSig, txBytes, ephemeral pubkey.\n * Then decrypt the Seal key with ephemeral secret key. Returns a list decryption keys with\n * their full IDs.\n *\n * @param url - The URL of the key server.\n * @param requestSig - The Base64 string of request signature.\n * @param txBytes - The transaction bytes.\n * @param encKey - The ephemeral secret key.\n * @param certificate - The certificate.\n * @returns - A list of full ID and the decrypted key.\n */\nexport async function fetchKeysForAllIds({\n\turl,\n\trequestSignature,\n\ttransactionBytes,\n\tencKey,\n\tencKeyPk,\n\tencVerificationKey,\n\tcertificate,\n\ttimeout,\n\tapiKeyName,\n\tapiKey,\n\tsignal,\n}: FetchKeysOptions): Promise<{ fullId: string; key: Uint8Array<ArrayBuffer> }[]> {\n\tconst body = {\n\t\tptb: toBase64(transactionBytes.slice(1)), // removes the byte of the transaction type version\n\t\tenc_key: toBase64(encKeyPk),\n\t\tenc_verification_key: toBase64(encVerificationKey),\n\t\trequest_signature: requestSignature, // already b64\n\t\tcertificate,\n\t};\n\n\tconst timeoutSignal = AbortSignal.timeout(timeout);\n\tconst combinedSignal = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;\n\n\tconst requestId = crypto.randomUUID();\n\tconst response = await fetch(url + '/v1/fetch_key', {\n\t\tmethod: 'POST',\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\t...(apiKeyName && apiKey ? { [apiKeyName]: apiKey } : {}),\n\t\t},\n\t\tbody: JSON.stringify(body),\n\t\tsignal: combinedSignal,\n\t});\n\tawait SealAPIError.assertResponse(response, requestId);\n\tconst resp = await response.json();\n\tverifyKeyServerVersion(response);\n\n\treturn resp.decryption_keys.map(\n\t\t(dk: { id: Uint8Array<ArrayBuffer>; encrypted_key: [string, string] }) => ({\n\t\t\tfullId: toHex(dk.id),\n\t\t\tkey: elgamalDecrypt(encKey, dk.encrypted_key.map(fromBase64) as [Uint8Array, Uint8Array]),\n\t\t}),\n\t);\n}\n"],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ import { SealCompatibleClient } from "./types.mjs";
2
+ import "@mysten/bcs";
3
+ import { Signer } from "@mysten/sui/cryptography";
4
+
5
+ //#region src/session-key.d.ts
6
+
7
+ type Certificate = {
8
+ user: string;
9
+ session_vk: string;
10
+ creation_time: number;
11
+ ttl_min: number;
12
+ signature: string;
13
+ mvr_name?: string;
14
+ };
15
+ type ExportedSessionKey = {
16
+ address: string;
17
+ packageId: string;
18
+ mvrName?: string;
19
+ creationTimeMs: number;
20
+ ttlMin: number;
21
+ personalMessageSignature?: string;
22
+ sessionKey: string;
23
+ };
24
+ declare class SessionKey {
25
+ #private;
26
+ private constructor();
27
+ /**
28
+ * Create a new SessionKey instance.
29
+ * @param address - The address of the user.
30
+ * @param packageId - The ID of the package.
31
+ * @param mvrName - Optional. The name of the MVR, if there is one.
32
+ * @param ttlMin - The TTL in minutes.
33
+ * @param signer - Optional. The signer instance, e.g. EnokiSigner.
34
+ * @param suiClient - The Sui client.
35
+ * @returns A new SessionKey instance.
36
+ */
37
+ static create({
38
+ address,
39
+ packageId,
40
+ mvrName,
41
+ ttlMin,
42
+ signer,
43
+ suiClient
44
+ }: {
45
+ address: string;
46
+ packageId: string;
47
+ mvrName?: string;
48
+ ttlMin: number;
49
+ signer?: Signer;
50
+ suiClient: SealCompatibleClient;
51
+ }): Promise<SessionKey>;
52
+ isExpired(): boolean;
53
+ getAddress(): string;
54
+ getPackageName(): string;
55
+ getPackageId(): string;
56
+ getPersonalMessage(): Uint8Array;
57
+ setPersonalMessageSignature(personalMessageSignature: string): Promise<void>;
58
+ getCertificate(): Promise<Certificate>;
59
+ /**
60
+ * Create request params for the given transaction bytes.
61
+ * @param txBytes - The transaction bytes.
62
+ * @returns The request params containing the ephemeral secret key,
63
+ * its public key and its verification key.
64
+ */
65
+ createRequestParams(txBytes: Uint8Array): Promise<{
66
+ encKey: Uint8Array<ArrayBuffer>;
67
+ encKeyPk: Uint8Array<ArrayBuffer>;
68
+ encVerificationKey: Uint8Array<ArrayBuffer>;
69
+ requestSignature: string;
70
+ }>;
71
+ /**
72
+ * Export the Session Key object from the instance. Store the object in IndexedDB to persist.
73
+ */
74
+ export(): ExportedSessionKey;
75
+ /**
76
+ * Restore a SessionKey instance for the given object.
77
+ * @returns A new SessionKey instance with restored state
78
+ */
79
+ static import(data: ExportedSessionKey, suiClient: SealCompatibleClient, signer?: Signer): SessionKey;
80
+ }
81
+ //#endregion
82
+ export { ExportedSessionKey, SessionKey };
83
+ //# sourceMappingURL=session-key.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-key.d.mts","names":[],"sources":["../src/session-key.ts"],"sourcesContent":[],"mappings":";;;;;;AA2Ca,KAnBD,WAAA,GAmBW;EA4DrB,IAAA,EAAA,MAAA;EACA,UAAA,EAAA,MAAA;EACA,aAAA,EAAA,MAAA;EACA,OAAA,EAAA,MAAA;EACA,SAAA,EAAA,MAAA;EACA,QAAA,CAAA,EAAA,MAAA;CAMS;AACE,KAlFD,kBAAA,GAkFC;EACA,OAAA,EAAA,MAAA;EAAR,SAAA,EAAA,MAAA;EAmCkB,OAAA,CAAA,EAAA,MAAA;EAO4C,cAAA,EAAA,MAAA;EAclC,MAAA,EAAA,MAAA;EAAR,wBAAA,CAAA,EAAA,MAAA;EAyBW,UAAA,EAAA,MAAA;CACf;AAAX,cA3JG,UAAA,CA2JH;EACa,CAAA,OAAA;EAAX,QAAA,WAAA,CAAA;EACqB;;;;;;;;;;;;;;;;;;;;;;aAtFtB;eACE;MACR,QAAQ;;;;;wBAmCU;iEAO4C;oBAc1C,QAAQ;;;;;;;+BAyBG,aAAa;YACvC,WAAW;cACT,WAAW;wBACD,WAAW;;;;;;YA0BtB;;;;;sBAyBH,+BACK,+BACF,SACP"}
@@ -0,0 +1,171 @@
1
+ import { ExpiredSessionKeyError, InvalidPackageError, InvalidPersonalMessageSignatureError, UserError } from "./error.mjs";
2
+ import { generateSecretKey, toPublicKey, toVerificationKey } from "./elgamal.mjs";
3
+ import { toBase64 } from "@mysten/bcs";
4
+ import { bcs as bcs$1 } from "@mysten/sui/bcs";
5
+ import { isValidNamedPackage, isValidSuiAddress, isValidSuiObjectId } from "@mysten/sui/utils";
6
+ import { Ed25519Keypair } from "@mysten/sui/keypairs/ed25519";
7
+ import { verifyPersonalMessageSignature } from "@mysten/sui/verify";
8
+
9
+ //#region src/session-key.ts
10
+ const RequestFormat = bcs$1.struct("RequestFormat", {
11
+ ptb: bcs$1.byteVector(),
12
+ encKey: bcs$1.byteVector(),
13
+ encVerificationKey: bcs$1.byteVector()
14
+ });
15
+ var SessionKey = class SessionKey {
16
+ #address;
17
+ #packageId;
18
+ #mvrName;
19
+ #creationTimeMs;
20
+ #ttlMin;
21
+ #sessionKey;
22
+ #personalMessageSignature;
23
+ #signer;
24
+ #suiClient;
25
+ constructor({ address, packageId, mvrName, ttlMin, signer, suiClient }) {
26
+ if (mvrName && !isValidNamedPackage(mvrName)) throw new UserError(`Invalid package name ${mvrName}`);
27
+ if (!isValidSuiObjectId(packageId) || !isValidSuiAddress(address)) throw new UserError(`Invalid package ID ${packageId} or address ${address}`);
28
+ if (ttlMin > 30 || ttlMin < 1) throw new UserError(`Invalid TTL ${ttlMin}, must be between 1 and 30`);
29
+ if (signer && signer.getPublicKey().toSuiAddress() !== address) throw new UserError("Signer address does not match session key address");
30
+ this.#address = address;
31
+ this.#packageId = packageId;
32
+ this.#mvrName = mvrName;
33
+ this.#creationTimeMs = Date.now();
34
+ this.#ttlMin = ttlMin;
35
+ this.#sessionKey = Ed25519Keypair.generate();
36
+ this.#signer = signer;
37
+ this.#suiClient = suiClient;
38
+ }
39
+ /**
40
+ * Create a new SessionKey instance.
41
+ * @param address - The address of the user.
42
+ * @param packageId - The ID of the package.
43
+ * @param mvrName - Optional. The name of the MVR, if there is one.
44
+ * @param ttlMin - The TTL in minutes.
45
+ * @param signer - Optional. The signer instance, e.g. EnokiSigner.
46
+ * @param suiClient - The Sui client.
47
+ * @returns A new SessionKey instance.
48
+ */
49
+ static async create({ address, packageId, mvrName, ttlMin, signer, suiClient }) {
50
+ const packageObj = await suiClient.core.getObject({ objectId: packageId });
51
+ if (String(packageObj.object.version) !== "1") throw new InvalidPackageError(`Package ${packageId} is not the first version`);
52
+ return new SessionKey({
53
+ address,
54
+ packageId,
55
+ mvrName,
56
+ ttlMin,
57
+ signer,
58
+ suiClient
59
+ });
60
+ }
61
+ isExpired() {
62
+ return this.#creationTimeMs + this.#ttlMin * 60 * 1e3 - 1e4 < Date.now();
63
+ }
64
+ getAddress() {
65
+ return this.#address;
66
+ }
67
+ getPackageName() {
68
+ if (this.#mvrName) return this.#mvrName;
69
+ return this.#packageId;
70
+ }
71
+ getPackageId() {
72
+ return this.#packageId;
73
+ }
74
+ getPersonalMessage() {
75
+ const creationTimeUtc = new Date(this.#creationTimeMs).toISOString().slice(0, 19).replace("T", " ") + " UTC";
76
+ const message = `Accessing keys of package ${this.getPackageName()} for ${this.#ttlMin} mins from ${creationTimeUtc}, session key ${toBase64(this.#sessionKey.getPublicKey().toRawBytes())}`;
77
+ return new TextEncoder().encode(message);
78
+ }
79
+ async setPersonalMessageSignature(personalMessageSignature) {
80
+ if (!this.#personalMessageSignature) try {
81
+ await verifyPersonalMessageSignature(this.getPersonalMessage(), personalMessageSignature, {
82
+ address: this.#address,
83
+ client: this.#suiClient
84
+ });
85
+ this.#personalMessageSignature = personalMessageSignature;
86
+ } catch {
87
+ throw new InvalidPersonalMessageSignatureError("Not valid");
88
+ }
89
+ }
90
+ async getCertificate() {
91
+ if (!this.#personalMessageSignature) if (this.#signer) {
92
+ const { signature } = await this.#signer.signPersonalMessage(this.getPersonalMessage());
93
+ this.#personalMessageSignature = signature;
94
+ } else throw new InvalidPersonalMessageSignatureError("Personal message signature is not set");
95
+ return {
96
+ user: this.#address,
97
+ session_vk: toBase64(this.#sessionKey.getPublicKey().toRawBytes()),
98
+ creation_time: this.#creationTimeMs,
99
+ ttl_min: this.#ttlMin,
100
+ signature: this.#personalMessageSignature,
101
+ mvr_name: this.#mvrName
102
+ };
103
+ }
104
+ /**
105
+ * Create request params for the given transaction bytes.
106
+ * @param txBytes - The transaction bytes.
107
+ * @returns The request params containing the ephemeral secret key,
108
+ * its public key and its verification key.
109
+ */
110
+ async createRequestParams(txBytes) {
111
+ if (this.isExpired()) throw new ExpiredSessionKeyError();
112
+ const encKey = generateSecretKey();
113
+ const encKeyPk = toPublicKey(encKey);
114
+ const encVerificationKey = toVerificationKey(encKey);
115
+ const msgToSign = RequestFormat.serialize({
116
+ ptb: txBytes.slice(1),
117
+ encKey: encKeyPk,
118
+ encVerificationKey
119
+ }).toBytes();
120
+ return {
121
+ encKey,
122
+ encKeyPk,
123
+ encVerificationKey,
124
+ requestSignature: toBase64(await this.#sessionKey.sign(msgToSign))
125
+ };
126
+ }
127
+ /**
128
+ * Export the Session Key object from the instance. Store the object in IndexedDB to persist.
129
+ */
130
+ export() {
131
+ const obj = {
132
+ address: this.#address,
133
+ packageId: this.#packageId,
134
+ mvrName: this.#mvrName,
135
+ creationTimeMs: this.#creationTimeMs,
136
+ ttlMin: this.#ttlMin,
137
+ personalMessageSignature: this.#personalMessageSignature,
138
+ sessionKey: this.#sessionKey.getSecretKey()
139
+ };
140
+ Object.defineProperty(obj, "toJSON", {
141
+ enumerable: false,
142
+ value: () => {
143
+ throw new Error("This object is not serializable");
144
+ }
145
+ });
146
+ return obj;
147
+ }
148
+ /**
149
+ * Restore a SessionKey instance for the given object.
150
+ * @returns A new SessionKey instance with restored state
151
+ */
152
+ static import(data, suiClient, signer) {
153
+ const instance = new SessionKey({
154
+ address: data.address,
155
+ packageId: data.packageId,
156
+ mvrName: data.mvrName,
157
+ ttlMin: data.ttlMin,
158
+ signer,
159
+ suiClient
160
+ });
161
+ instance.#creationTimeMs = data.creationTimeMs;
162
+ instance.#sessionKey = Ed25519Keypair.fromSecretKey(data.sessionKey);
163
+ instance.#personalMessageSignature = data.personalMessageSignature;
164
+ if (instance.isExpired()) throw new ExpiredSessionKeyError();
165
+ return instance;
166
+ }
167
+ };
168
+
169
+ //#endregion
170
+ export { SessionKey };
171
+ //# sourceMappingURL=session-key.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-key.mjs","names":["bcs","#address","#packageId","#mvrName","#creationTimeMs","#ttlMin","#sessionKey","#signer","#suiClient","#personalMessageSignature"],"sources":["../src/session-key.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { toBase64 } from '@mysten/bcs';\nimport { bcs } from '@mysten/sui/bcs';\nimport type { Signer } from '@mysten/sui/cryptography';\nimport { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519';\nimport { isValidNamedPackage, isValidSuiAddress, isValidSuiObjectId } from '@mysten/sui/utils';\nimport { verifyPersonalMessageSignature } from '@mysten/sui/verify';\nimport { generateSecretKey, toPublicKey, toVerificationKey } from './elgamal.js';\nimport {\n\tExpiredSessionKeyError,\n\tInvalidPackageError,\n\tInvalidPersonalMessageSignatureError,\n\tUserError,\n} from './error.js';\nimport type { SealCompatibleClient } from './types.js';\n\nexport const RequestFormat = bcs.struct('RequestFormat', {\n\tptb: bcs.byteVector(),\n\tencKey: bcs.byteVector(),\n\tencVerificationKey: bcs.byteVector(),\n});\n\nexport type Certificate = {\n\tuser: string;\n\tsession_vk: string;\n\tcreation_time: number;\n\tttl_min: number;\n\tsignature: string;\n\tmvr_name?: string;\n};\n\nexport type ExportedSessionKey = {\n\taddress: string;\n\tpackageId: string;\n\tmvrName?: string;\n\tcreationTimeMs: number;\n\tttlMin: number;\n\tpersonalMessageSignature?: string;\n\tsessionKey: string;\n};\n\nexport class SessionKey {\n\t#address: string;\n\t#packageId: string;\n\t#mvrName?: string;\n\t#creationTimeMs: number;\n\t#ttlMin: number;\n\t#sessionKey: Ed25519Keypair;\n\t#personalMessageSignature?: string;\n\t#signer?: Signer;\n\t#suiClient: SealCompatibleClient;\n\n\tprivate constructor({\n\t\taddress,\n\t\tpackageId,\n\t\tmvrName,\n\t\tttlMin,\n\t\tsigner,\n\t\tsuiClient,\n\t}: {\n\t\taddress: string;\n\t\tpackageId: string;\n\t\tmvrName?: string;\n\t\tttlMin: number;\n\t\tsigner?: Signer;\n\t\tsuiClient: SealCompatibleClient;\n\t}) {\n\t\tif (mvrName && !isValidNamedPackage(mvrName)) {\n\t\t\tthrow new UserError(`Invalid package name ${mvrName}`);\n\t\t}\n\t\tif (!isValidSuiObjectId(packageId) || !isValidSuiAddress(address)) {\n\t\t\tthrow new UserError(`Invalid package ID ${packageId} or address ${address}`);\n\t\t}\n\t\tif (ttlMin > 30 || ttlMin < 1) {\n\t\t\tthrow new UserError(`Invalid TTL ${ttlMin}, must be between 1 and 30`);\n\t\t}\n\t\tif (signer && signer.getPublicKey().toSuiAddress() !== address) {\n\t\t\tthrow new UserError('Signer address does not match session key address');\n\t\t}\n\n\t\tthis.#address = address;\n\t\tthis.#packageId = packageId;\n\t\tthis.#mvrName = mvrName;\n\t\tthis.#creationTimeMs = Date.now();\n\t\tthis.#ttlMin = ttlMin;\n\t\tthis.#sessionKey = Ed25519Keypair.generate();\n\t\tthis.#signer = signer;\n\t\tthis.#suiClient = suiClient;\n\t}\n\n\t/**\n\t * Create a new SessionKey instance.\n\t * @param address - The address of the user.\n\t * @param packageId - The ID of the package.\n\t * @param mvrName - Optional. The name of the MVR, if there is one.\n\t * @param ttlMin - The TTL in minutes.\n\t * @param signer - Optional. The signer instance, e.g. EnokiSigner.\n\t * @param suiClient - The Sui client.\n\t * @returns A new SessionKey instance.\n\t */\n\tstatic async create({\n\t\taddress,\n\t\tpackageId,\n\t\tmvrName,\n\t\tttlMin,\n\t\tsigner,\n\t\tsuiClient,\n\t}: {\n\t\taddress: string;\n\t\tpackageId: string;\n\t\tmvrName?: string;\n\t\tttlMin: number;\n\t\tsigner?: Signer;\n\t\tsuiClient: SealCompatibleClient;\n\t}): Promise<SessionKey> {\n\t\tconst packageObj = await suiClient.core.getObject({ objectId: packageId });\n\t\tif (String(packageObj.object.version) !== '1') {\n\t\t\tthrow new InvalidPackageError(`Package ${packageId} is not the first version`);\n\t\t}\n\n\t\treturn new SessionKey({\n\t\t\taddress,\n\t\t\tpackageId,\n\t\t\tmvrName,\n\t\t\tttlMin,\n\t\t\tsigner,\n\t\t\tsuiClient,\n\t\t});\n\t}\n\tisExpired(): boolean {\n\t\t// Allow 10 seconds for clock skew\n\t\treturn this.#creationTimeMs + this.#ttlMin * 60 * 1000 - 10_000 < Date.now();\n\t}\n\n\tgetAddress(): string {\n\t\treturn this.#address;\n\t}\n\n\tgetPackageName(): string {\n\t\tif (this.#mvrName) {\n\t\t\treturn this.#mvrName;\n\t\t}\n\t\treturn this.#packageId;\n\t}\n\n\tgetPackageId(): string {\n\t\treturn this.#packageId;\n\t}\n\n\tgetPersonalMessage(): Uint8Array {\n\t\tconst creationTimeUtc =\n\t\t\tnew Date(this.#creationTimeMs).toISOString().slice(0, 19).replace('T', ' ') + ' UTC';\n\t\tconst message = `Accessing keys of package ${this.getPackageName()} for ${this.#ttlMin} mins from ${creationTimeUtc}, session key ${toBase64(this.#sessionKey.getPublicKey().toRawBytes())}`;\n\t\treturn new TextEncoder().encode(message);\n\t}\n\n\tasync setPersonalMessageSignature(personalMessageSignature: string) {\n\t\tif (!this.#personalMessageSignature) {\n\t\t\ttry {\n\t\t\t\tawait verifyPersonalMessageSignature(this.getPersonalMessage(), personalMessageSignature, {\n\t\t\t\t\taddress: this.#address,\n\t\t\t\t\tclient: this.#suiClient,\n\t\t\t\t});\n\t\t\t\tthis.#personalMessageSignature = personalMessageSignature;\n\t\t\t} catch {\n\t\t\t\tthrow new InvalidPersonalMessageSignatureError('Not valid');\n\t\t\t}\n\t\t}\n\t}\n\n\tasync getCertificate(): Promise<Certificate> {\n\t\tif (!this.#personalMessageSignature) {\n\t\t\tif (this.#signer) {\n\t\t\t\tconst { signature } = await this.#signer.signPersonalMessage(this.getPersonalMessage());\n\t\t\t\tthis.#personalMessageSignature = signature;\n\t\t\t} else {\n\t\t\t\tthrow new InvalidPersonalMessageSignatureError('Personal message signature is not set');\n\t\t\t}\n\t\t}\n\t\treturn {\n\t\t\tuser: this.#address,\n\t\t\tsession_vk: toBase64(this.#sessionKey.getPublicKey().toRawBytes()),\n\t\t\tcreation_time: this.#creationTimeMs,\n\t\t\tttl_min: this.#ttlMin,\n\t\t\tsignature: this.#personalMessageSignature,\n\t\t\tmvr_name: this.#mvrName,\n\t\t};\n\t}\n\n\t/**\n\t * Create request params for the given transaction bytes.\n\t * @param txBytes - The transaction bytes.\n\t * @returns The request params containing the ephemeral secret key,\n\t * its public key and its verification key.\n\t */\n\tasync createRequestParams(txBytes: Uint8Array): Promise<{\n\t\tencKey: Uint8Array<ArrayBuffer>;\n\t\tencKeyPk: Uint8Array<ArrayBuffer>;\n\t\tencVerificationKey: Uint8Array<ArrayBuffer>;\n\t\trequestSignature: string;\n\t}> {\n\t\tif (this.isExpired()) {\n\t\t\tthrow new ExpiredSessionKeyError();\n\t\t}\n\t\tconst encKey = generateSecretKey();\n\t\tconst encKeyPk = toPublicKey(encKey);\n\t\tconst encVerificationKey = toVerificationKey(encKey);\n\n\t\tconst msgToSign = RequestFormat.serialize({\n\t\t\tptb: txBytes.slice(1),\n\t\t\tencKey: encKeyPk,\n\t\t\tencVerificationKey,\n\t\t}).toBytes();\n\t\treturn {\n\t\t\tencKey,\n\t\t\tencKeyPk,\n\t\t\tencVerificationKey,\n\t\t\trequestSignature: toBase64(await this.#sessionKey.sign(msgToSign)),\n\t\t};\n\t}\n\n\t/**\n\t * Export the Session Key object from the instance. Store the object in IndexedDB to persist.\n\t */\n\texport(): ExportedSessionKey {\n\t\tconst obj = {\n\t\t\taddress: this.#address,\n\t\t\tpackageId: this.#packageId,\n\t\t\tmvrName: this.#mvrName,\n\t\t\tcreationTimeMs: this.#creationTimeMs,\n\t\t\tttlMin: this.#ttlMin,\n\t\t\tpersonalMessageSignature: this.#personalMessageSignature,\n\t\t\tsessionKey: this.#sessionKey.getSecretKey(), // bech32 encoded string\n\t\t};\n\n\t\tObject.defineProperty(obj, 'toJSON', {\n\t\t\tenumerable: false,\n\t\t\tvalue: () => {\n\t\t\t\tthrow new Error('This object is not serializable');\n\t\t\t},\n\t\t});\n\t\treturn obj;\n\t}\n\n\t/**\n\t * Restore a SessionKey instance for the given object.\n\t * @returns A new SessionKey instance with restored state\n\t */\n\tstatic import(\n\t\tdata: ExportedSessionKey,\n\t\tsuiClient: SealCompatibleClient,\n\t\tsigner?: Signer,\n\t): SessionKey {\n\t\tconst instance = new SessionKey({\n\t\t\taddress: data.address,\n\t\t\tpackageId: data.packageId,\n\t\t\tmvrName: data.mvrName,\n\t\t\tttlMin: data.ttlMin,\n\t\t\tsigner,\n\t\t\tsuiClient,\n\t\t});\n\n\t\tinstance.#creationTimeMs = data.creationTimeMs;\n\t\tinstance.#sessionKey = Ed25519Keypair.fromSecretKey(data.sessionKey);\n\t\tinstance.#personalMessageSignature = data.personalMessageSignature;\n\n\t\tif (instance.isExpired()) {\n\t\t\tthrow new ExpiredSessionKeyError();\n\t\t}\n\t\treturn instance;\n\t}\n}\n"],"mappings":"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"}