shogun-core 5.2.0 → 5.2.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 (185) hide show
  1. package/dist/browser/defaultVendors-node_modules_hpke_chacha20poly1305_esm_mod_js.shogun-core.js +1220 -0
  2. package/dist/browser/defaultVendors-node_modules_hpke_chacha20poly1305_esm_mod_js.shogun-core.js.map +1 -0
  3. package/dist/browser/defaultVendors-node_modules_hpke_hybridkem-x-wing_esm_mod_js.shogun-core.js +844 -0
  4. package/dist/browser/defaultVendors-node_modules_hpke_hybridkem-x-wing_esm_mod_js.shogun-core.js.map +1 -0
  5. package/dist/browser/defaultVendors-node_modules_mlkem_esm_mod_js.shogun-core.js +2335 -0
  6. package/dist/browser/defaultVendors-node_modules_mlkem_esm_mod_js.shogun-core.js.map +1 -0
  7. package/dist/browser/defaultVendors-node_modules_noble_ciphers_chacha_js.shogun-core.js +999 -0
  8. package/dist/browser/defaultVendors-node_modules_noble_ciphers_chacha_js.shogun-core.js.map +1 -0
  9. package/dist/browser/defaultVendors-node_modules_noble_curves_esm_abstract_curve_js-node_modules_noble_curves_esm_-1ce4ed.shogun-core.js +1651 -0
  10. package/dist/browser/defaultVendors-node_modules_noble_curves_esm_abstract_curve_js-node_modules_noble_curves_esm_-1ce4ed.shogun-core.js.map +1 -0
  11. package/dist/browser/defaultVendors-node_modules_noble_curves_esm_abstract_edwards_js-node_modules_noble_curves_es-a82056.shogun-core.js +825 -0
  12. package/dist/browser/defaultVendors-node_modules_noble_curves_esm_abstract_edwards_js-node_modules_noble_curves_es-a82056.shogun-core.js.map +1 -0
  13. package/dist/browser/defaultVendors-node_modules_noble_curves_esm_ed25519_js.shogun-core.js +508 -0
  14. package/dist/browser/defaultVendors-node_modules_noble_curves_esm_ed25519_js.shogun-core.js.map +1 -0
  15. package/dist/browser/defaultVendors-node_modules_noble_curves_esm_ed448_js.shogun-core.js +747 -0
  16. package/dist/browser/defaultVendors-node_modules_noble_curves_esm_ed448_js.shogun-core.js.map +1 -0
  17. package/dist/browser/defaultVendors-node_modules_noble_curves_esm_nist_js.shogun-core.js +1608 -0
  18. package/dist/browser/defaultVendors-node_modules_noble_curves_esm_nist_js.shogun-core.js.map +1 -0
  19. package/dist/browser/defaultVendors-node_modules_noble_post-quantum_ml-dsa_js.shogun-core.js +2117 -0
  20. package/dist/browser/defaultVendors-node_modules_noble_post-quantum_ml-dsa_js.shogun-core.js.map +1 -0
  21. package/dist/browser/defaultVendors-node_modules_openpgp_dist_openpgp_min_mjs.shogun-core.js +86 -0
  22. package/dist/browser/defaultVendors-node_modules_openpgp_dist_openpgp_min_mjs.shogun-core.js.map +1 -0
  23. package/dist/browser/node_modules_hpke_ml-kem_esm_mod_js.shogun-core.js +539 -0
  24. package/dist/browser/node_modules_hpke_ml-kem_esm_mod_js.shogun-core.js.map +1 -0
  25. package/dist/browser/shogun-core.js +160386 -0
  26. package/dist/browser/shogun-core.js.map +1 -0
  27. package/dist/config/simplified-config.js +236 -0
  28. package/dist/core.js +329 -0
  29. package/dist/crypto/asymmetric.js +99 -0
  30. package/dist/crypto/double-ratchet.js +370 -0
  31. package/dist/crypto/file-encryption.js +213 -0
  32. package/dist/crypto/hashing.js +87 -0
  33. package/dist/crypto/index.js +34 -0
  34. package/dist/crypto/mls-codec.js +202 -0
  35. package/dist/crypto/mls.js +550 -0
  36. package/dist/crypto/pgp.js +390 -0
  37. package/dist/crypto/random-generation.js +341 -0
  38. package/dist/crypto/sframe.js +350 -0
  39. package/dist/crypto/signal-protocol.js +376 -0
  40. package/dist/crypto/symmetric.js +91 -0
  41. package/dist/crypto/types.js +2 -0
  42. package/dist/crypto/utils.js +140 -0
  43. package/dist/examples/auth-test.js +253 -0
  44. package/dist/examples/crypto-identity-example.js +151 -0
  45. package/dist/examples/crypto-working-test.js +83 -0
  46. package/dist/examples/double-ratchet-test.js +155 -0
  47. package/dist/examples/mls-advanced-example.js +294 -0
  48. package/dist/examples/mls-sframe-test.js +304 -0
  49. package/dist/examples/pgp-example.js +200 -0
  50. package/dist/examples/quick-auth-test.js +61 -0
  51. package/dist/examples/random-generation-test.js +151 -0
  52. package/dist/examples/signal-protocol-test.js +38 -0
  53. package/dist/examples/simple-api-test.js +114 -0
  54. package/dist/examples/simple-crypto-identity-example.js +84 -0
  55. package/dist/examples/timeout-test.js +227 -0
  56. package/dist/examples/zkproof-credentials-example.js +212 -0
  57. package/dist/examples/zkproof-example.js +201 -0
  58. package/dist/gundb/api.js +435 -0
  59. package/dist/gundb/crypto.js +283 -0
  60. package/dist/gundb/db.js +1946 -0
  61. package/dist/gundb/derive.js +232 -0
  62. package/dist/gundb/errors.js +76 -0
  63. package/dist/gundb/index.js +22 -0
  64. package/dist/gundb/rxjs.js +447 -0
  65. package/dist/gundb/types.js +5 -0
  66. package/dist/index.js +58 -0
  67. package/dist/interfaces/common.js +2 -0
  68. package/dist/interfaces/events.js +40 -0
  69. package/dist/interfaces/plugin.js +2 -0
  70. package/dist/interfaces/shogun.js +37 -0
  71. package/dist/managers/AuthManager.js +226 -0
  72. package/dist/managers/CoreInitializer.js +228 -0
  73. package/dist/managers/CryptoIdentityManager.js +366 -0
  74. package/dist/managers/EventManager.js +70 -0
  75. package/dist/managers/PluginManager.js +299 -0
  76. package/dist/plugins/base.js +50 -0
  77. package/dist/plugins/index.js +32 -0
  78. package/dist/plugins/nostr/index.js +20 -0
  79. package/dist/plugins/nostr/nostrConnector.js +419 -0
  80. package/dist/plugins/nostr/nostrConnectorPlugin.js +453 -0
  81. package/dist/plugins/nostr/nostrSigner.js +319 -0
  82. package/dist/plugins/nostr/types.js +2 -0
  83. package/dist/plugins/smartwallet/index.js +18 -0
  84. package/dist/plugins/smartwallet/smartWalletPlugin.js +511 -0
  85. package/dist/plugins/smartwallet/types.js +2 -0
  86. package/dist/plugins/web3/index.js +20 -0
  87. package/dist/plugins/web3/types.js +2 -0
  88. package/dist/plugins/web3/web3Connector.js +533 -0
  89. package/dist/plugins/web3/web3ConnectorPlugin.js +455 -0
  90. package/dist/plugins/web3/web3Signer.js +314 -0
  91. package/dist/plugins/webauthn/index.js +19 -0
  92. package/dist/plugins/webauthn/types.js +14 -0
  93. package/dist/plugins/webauthn/webauthn.js +496 -0
  94. package/dist/plugins/webauthn/webauthnPlugin.js +489 -0
  95. package/dist/plugins/webauthn/webauthnSigner.js +310 -0
  96. package/dist/plugins/zkproof/index.js +53 -0
  97. package/dist/plugins/zkproof/types.js +2 -0
  98. package/dist/plugins/zkproof/zkCredentials.js +213 -0
  99. package/dist/plugins/zkproof/zkProofConnector.js +198 -0
  100. package/dist/plugins/zkproof/zkProofPlugin.js +272 -0
  101. package/dist/storage/storage.js +145 -0
  102. package/dist/types/config/simplified-config.d.ts +114 -0
  103. package/dist/types/core.d.ts +305 -0
  104. package/dist/types/crypto/asymmetric.d.ts +6 -0
  105. package/dist/types/crypto/double-ratchet.d.ts +22 -0
  106. package/dist/types/crypto/file-encryption.d.ts +19 -0
  107. package/dist/types/crypto/hashing.d.ts +9 -0
  108. package/dist/types/crypto/index.d.ts +13 -0
  109. package/dist/types/crypto/mls-codec.d.ts +39 -0
  110. package/dist/types/crypto/mls.d.ts +130 -0
  111. package/dist/types/crypto/pgp.d.ts +95 -0
  112. package/dist/types/crypto/random-generation.d.ts +35 -0
  113. package/dist/types/crypto/sframe.d.ts +102 -0
  114. package/dist/types/crypto/signal-protocol.d.ts +26 -0
  115. package/dist/types/crypto/symmetric.d.ts +9 -0
  116. package/dist/types/crypto/types.d.ts +144 -0
  117. package/dist/types/crypto/utils.d.ts +22 -0
  118. package/dist/types/examples/auth-test.d.ts +8 -0
  119. package/dist/types/examples/crypto-identity-example.d.ts +5 -0
  120. package/dist/types/examples/crypto-working-test.d.ts +1 -0
  121. package/dist/types/examples/double-ratchet-test.d.ts +1 -0
  122. package/dist/types/examples/mls-advanced-example.d.ts +53 -0
  123. package/dist/types/examples/mls-sframe-test.d.ts +1 -0
  124. package/dist/types/examples/pgp-example.d.ts +75 -0
  125. package/dist/types/examples/quick-auth-test.d.ts +8 -0
  126. package/dist/types/examples/random-generation-test.d.ts +1 -0
  127. package/dist/types/examples/signal-protocol-test.d.ts +1 -0
  128. package/dist/types/examples/simple-api-test.d.ts +10 -0
  129. package/dist/types/examples/simple-crypto-identity-example.d.ts +6 -0
  130. package/dist/types/examples/timeout-test.d.ts +8 -0
  131. package/dist/types/examples/zkproof-credentials-example.d.ts +12 -0
  132. package/dist/types/examples/zkproof-example.d.ts +11 -0
  133. package/dist/types/gundb/api.d.ts +185 -0
  134. package/dist/types/gundb/crypto.d.ts +95 -0
  135. package/dist/types/gundb/db.d.ts +397 -0
  136. package/dist/types/gundb/derive.d.ts +21 -0
  137. package/dist/types/gundb/errors.d.ts +42 -0
  138. package/dist/types/gundb/index.d.ts +3 -0
  139. package/dist/types/gundb/rxjs.d.ts +110 -0
  140. package/dist/types/gundb/types.d.ts +255 -0
  141. package/dist/types/index.d.ts +16 -0
  142. package/dist/types/interfaces/common.d.ts +85 -0
  143. package/dist/types/interfaces/events.d.ts +131 -0
  144. package/dist/types/interfaces/plugin.d.ts +162 -0
  145. package/dist/types/interfaces/shogun.d.ts +208 -0
  146. package/dist/types/managers/AuthManager.d.ts +72 -0
  147. package/dist/types/managers/CoreInitializer.d.ts +40 -0
  148. package/dist/types/managers/CryptoIdentityManager.d.ts +102 -0
  149. package/dist/types/managers/EventManager.d.ts +49 -0
  150. package/dist/types/managers/PluginManager.d.ts +145 -0
  151. package/dist/types/plugins/base.d.ts +35 -0
  152. package/dist/types/plugins/index.d.ts +18 -0
  153. package/dist/types/plugins/nostr/index.d.ts +4 -0
  154. package/dist/types/plugins/nostr/nostrConnector.d.ts +119 -0
  155. package/dist/types/plugins/nostr/nostrConnectorPlugin.d.ts +163 -0
  156. package/dist/types/plugins/nostr/nostrSigner.d.ts +105 -0
  157. package/dist/types/plugins/nostr/types.d.ts +122 -0
  158. package/dist/types/plugins/smartwallet/index.d.ts +2 -0
  159. package/dist/types/plugins/smartwallet/smartWalletPlugin.d.ts +67 -0
  160. package/dist/types/plugins/smartwallet/types.d.ts +80 -0
  161. package/dist/types/plugins/web3/index.d.ts +4 -0
  162. package/dist/types/plugins/web3/types.d.ts +107 -0
  163. package/dist/types/plugins/web3/web3Connector.d.ts +129 -0
  164. package/dist/types/plugins/web3/web3ConnectorPlugin.d.ts +160 -0
  165. package/dist/types/plugins/web3/web3Signer.d.ts +114 -0
  166. package/dist/types/plugins/webauthn/index.d.ts +3 -0
  167. package/dist/types/plugins/webauthn/types.d.ts +183 -0
  168. package/dist/types/plugins/webauthn/webauthn.d.ts +129 -0
  169. package/dist/types/plugins/webauthn/webauthnPlugin.d.ts +179 -0
  170. package/dist/types/plugins/webauthn/webauthnSigner.d.ts +91 -0
  171. package/dist/types/plugins/zkproof/index.d.ts +48 -0
  172. package/dist/types/plugins/zkproof/types.d.ts +123 -0
  173. package/dist/types/plugins/zkproof/zkCredentials.d.ts +112 -0
  174. package/dist/types/plugins/zkproof/zkProofConnector.d.ts +46 -0
  175. package/dist/types/plugins/zkproof/zkProofPlugin.d.ts +76 -0
  176. package/dist/types/storage/storage.d.ts +51 -0
  177. package/dist/types/utils/errorHandler.d.ts +119 -0
  178. package/dist/types/utils/eventEmitter.d.ts +39 -0
  179. package/dist/types/utils/seedPhrase.d.ts +50 -0
  180. package/dist/types/utils/validation.d.ts +27 -0
  181. package/dist/utils/errorHandler.js +246 -0
  182. package/dist/utils/eventEmitter.js +79 -0
  183. package/dist/utils/seedPhrase.js +97 -0
  184. package/dist/utils/validation.js +81 -0
  185. package/package.json +10 -1
@@ -0,0 +1,550 @@
1
+ "use strict";
2
+ /**
3
+ * MLS (Message Layer Security) Manager
4
+ * RFC 9420 implementation using ts-mls library
5
+ * Provides end-to-end encrypted group messaging with forward secrecy
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.MLSManager = void 0;
9
+ const ts_mls_1 = require("ts-mls");
10
+ // Helper to strip trailing null nodes per RFC 9420
11
+ function stripTrailingNulls(tree) {
12
+ let lastNonNull = tree.length - 1;
13
+ while (lastNonNull >= 0 && tree[lastNonNull] === null) {
14
+ lastNonNull--;
15
+ }
16
+ return tree.slice(0, lastNonNull + 1);
17
+ }
18
+ /**
19
+ * MLSManager wraps the ts-mls functional API with a class-based interface
20
+ * for easier state management in applications
21
+ */
22
+ class MLSManager {
23
+ constructor(userId) {
24
+ this.cipherSuite = null;
25
+ this.initialized = false;
26
+ this.groups = new Map();
27
+ this.keyPackage = null;
28
+ this.userId = userId;
29
+ this.credential = {
30
+ credentialType: "basic",
31
+ identity: new TextEncoder().encode(userId),
32
+ };
33
+ }
34
+ /**
35
+ * Initialize the MLS client with a ciphersuite
36
+ */
37
+ async initialize() {
38
+ if (this.initialized) {
39
+ console.warn("MLS Manager already initialized");
40
+ return;
41
+ }
42
+ try {
43
+ console.log(`🔐 [MLS] Initializing for user: ${this.userId}`);
44
+ // Use MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519 (ID: 1)
45
+ // Using nobleCryptoProvider for compatibility (pure JS implementation)
46
+ const cipherSuiteName = "MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519";
47
+ const cs = (0, ts_mls_1.getCiphersuiteFromName)(cipherSuiteName);
48
+ this.cipherSuite = await ts_mls_1.nobleCryptoProvider.getCiphersuiteImpl(cs);
49
+ console.log(`✅ [MLS] Using ciphersuite: ${cipherSuiteName}`);
50
+ // Mark as initialized before generating key package
51
+ this.initialized = true;
52
+ // Generate initial key package for this user
53
+ await this.generateKeyPackage();
54
+ console.log("✅ [MLS] Initialized successfully");
55
+ }
56
+ catch (error) {
57
+ console.error("❌ [MLS] Failed to initialize:", error);
58
+ throw new Error(`MLS initialization failed: ${error instanceof Error ? error.message : String(error)}`);
59
+ }
60
+ }
61
+ /**
62
+ * Generate a new key package for joining groups
63
+ */
64
+ async generateKeyPackage() {
65
+ this.ensureInitialized();
66
+ try {
67
+ console.log("🔑 [MLS] Generating key package");
68
+ const keyPackageResult = await (0, ts_mls_1.generateKeyPackage)(this.credential, (0, ts_mls_1.defaultCapabilities)(), ts_mls_1.defaultLifetime, [], this.cipherSuite);
69
+ this.keyPackage = {
70
+ ...keyPackageResult,
71
+ userId: this.userId,
72
+ };
73
+ console.log("✅ [MLS] Key package generated");
74
+ return this.keyPackage;
75
+ }
76
+ catch (error) {
77
+ console.error("❌ [MLS] Failed to generate key package:", error);
78
+ throw new Error(`Key package generation failed: ${error instanceof Error ? error.message : String(error)}`);
79
+ }
80
+ }
81
+ /**
82
+ * Get the current key package
83
+ */
84
+ getKeyPackage() {
85
+ return this.keyPackage;
86
+ }
87
+ /**
88
+ * Create a new MLS group
89
+ */
90
+ async createGroup(groupId) {
91
+ this.ensureInitialized();
92
+ try {
93
+ console.log(`📝 [MLS] Creating group: ${groupId}`);
94
+ if (!this.keyPackage) {
95
+ throw new Error("No key package available. Call generateKeyPackage() first.");
96
+ }
97
+ const groupIdBytes = new TextEncoder().encode(groupId);
98
+ // Create group using ts-mls
99
+ const groupState = await (0, ts_mls_1.createGroup)(groupIdBytes, this.keyPackage.publicPackage, this.keyPackage.privatePackage, [], this.cipherSuite);
100
+ this.groups.set(groupId, groupState);
101
+ const groupInfo = {
102
+ groupId: groupIdBytes,
103
+ members: [this.userId],
104
+ epoch: groupState.groupContext.epoch,
105
+ };
106
+ console.log(`✅ [MLS] Group created: ${groupId}, epoch: ${groupState.groupContext.epoch}`);
107
+ return groupInfo;
108
+ }
109
+ catch (error) {
110
+ console.error("❌ [MLS] Failed to create group:", error);
111
+ throw new Error(`Group creation failed: ${error instanceof Error ? error.message : String(error)}`);
112
+ }
113
+ }
114
+ /**
115
+ * Add members to an existing group
116
+ */
117
+ async addMembers(groupId, keyPackages) {
118
+ this.ensureInitialized();
119
+ try {
120
+ console.log(`➕ [MLS] Adding ${keyPackages.length} member(s) to group: ${groupId}`);
121
+ const groupState = this.groups.get(groupId);
122
+ if (!groupState) {
123
+ throw new Error(`Group ${groupId} not found`);
124
+ }
125
+ // Create add proposals for each key package
126
+ const addProposals = keyPackages.map((kp) => ({
127
+ proposalType: "add",
128
+ add: {
129
+ keyPackage: kp.publicPackage,
130
+ },
131
+ }));
132
+ // Create commit with add proposals
133
+ const commitResult = await (0, ts_mls_1.createCommit)({ state: groupState, cipherSuite: this.cipherSuite }, { extraProposals: addProposals });
134
+ // Update group state
135
+ this.groups.set(groupId, commitResult.newState);
136
+ if (!commitResult.welcome) {
137
+ throw new Error("No welcome message generated");
138
+ }
139
+ console.log(`✅ [MLS] Members added, new epoch: ${commitResult.newState.groupContext.epoch}`);
140
+ // Debug: Log the commit structure
141
+ console.group("🔍 [MLS Debug] Commit Structure");
142
+ console.log("commitResult keys:", Object.keys(commitResult));
143
+ console.log("commit:", commitResult.commit);
144
+ console.log("commit.privateMessage:", commitResult.commit?.privateMessage);
145
+ console.groupEnd();
146
+ // RFC 9420 Section 11.2: Commit Distribution
147
+ // ⚠️ IMPORTANT: The returned commit MUST be sent to all existing group members
148
+ // so they can process it with processCommit() to stay synchronized.
149
+ //
150
+ // Distribution flow:
151
+ // 1. Alice adds Bob: addMembers() returns { welcome, commit }
152
+ // 2. Alice sends welcome to Bob (new member)
153
+ // 3. Alice sends commit to existing members (Charlie, David, etc.)
154
+ // 4. All existing members call processCommit(commit) to update their state
155
+ //
156
+ // Without distributing the commit, existing members will remain at old epoch
157
+ // and won't be able to decrypt messages from the updated group.
158
+ // Convert ratchetTree to a real array (it's Uint8Array-like with numeric indices)
159
+ const ratchetTreeArray = Array.from(commitResult.newState.ratchetTree);
160
+ // RFC 9420: Strip trailing null nodes before transmission
161
+ const strippedTree = stripTrailingNulls(ratchetTreeArray);
162
+ console.log(`🔍 [MLS] Ratchet tree stripped: ${ratchetTreeArray.length} -> ${strippedTree.length} nodes`);
163
+ return {
164
+ welcome: commitResult.welcome,
165
+ ratchetTree: strippedTree,
166
+ commit: commitResult.commit,
167
+ };
168
+ }
169
+ catch (error) {
170
+ console.error("❌ [MLS] Failed to add members:", error);
171
+ throw new Error(`Adding members failed: ${error instanceof Error ? error.message : String(error)}`);
172
+ }
173
+ }
174
+ /**
175
+ * Process a Welcome message to join an MLS group
176
+ *
177
+ * RFC 9420 Compliance:
178
+ * - Interior null nodes represent blank parent nodes (unmerged positions)
179
+ * - These nulls are REQUIRED for proper binary tree structure
180
+ * - Trailing nulls are stripped by sender (per RFC 9420 requirement)
181
+ * - ratchetTree parameter is optional; ts-mls can extract from Welcome extension
182
+ *
183
+ * @param welcome - The Welcome message from group creator
184
+ * @param ratchetTree - Optional ratchet tree (normally provided out-of-band)
185
+ */
186
+ async processWelcome(welcome, ratchetTree) {
187
+ this.ensureInitialized();
188
+ try {
189
+ console.log("📩 [MLS] Processing welcome message");
190
+ if (!this.keyPackage) {
191
+ throw new Error("No key package available");
192
+ }
193
+ // RFC 9420: Interior null nodes are valid (represent blank parent nodes)
194
+ // Trailing nulls are stripped by sender per RFC requirement
195
+ // Simply pass the tree as-is to ts-mls joinGroup()
196
+ if (ratchetTree && Array.isArray(ratchetTree)) {
197
+ const nullCount = ratchetTree.filter((n) => n === null).length;
198
+ console.log(`🔍 [MLS] Ratchet tree received: ${ratchetTree.length} nodes (${nullCount} interior nulls)`);
199
+ // DEBUG: Log structure of each node
200
+ console.group("🔍 [MLS Debug] Ratchet Tree Structure");
201
+ ratchetTree.forEach((node, i) => {
202
+ if (node === null) {
203
+ console.log(` Node ${i}: NULL`);
204
+ }
205
+ else {
206
+ console.log(` Node ${i}:`, {
207
+ type: typeof node,
208
+ isObject: typeof node === "object",
209
+ hasNodeType: node && typeof node === "object" && "nodeType" in node,
210
+ nodeType: node?.nodeType,
211
+ keys: node && typeof node === "object"
212
+ ? Object.keys(node).slice(0, 5)
213
+ : "n/a",
214
+ });
215
+ }
216
+ });
217
+ console.groupEnd();
218
+ }
219
+ const groupState = await (0, ts_mls_1.joinGroup)(welcome, this.keyPackage.publicPackage, this.keyPackage.privatePackage, ts_mls_1.emptyPskIndex, this.cipherSuite, ratchetTree);
220
+ const groupId = new TextDecoder().decode(groupState.groupContext.groupId);
221
+ this.groups.set(groupId, groupState);
222
+ // Extract member identities from ratchet tree
223
+ const members = this.extractMembersFromState(groupState);
224
+ const groupInfo = {
225
+ groupId: groupState.groupContext.groupId,
226
+ members,
227
+ epoch: groupState.groupContext.epoch,
228
+ };
229
+ console.log(`✅ [MLS] Welcome processed, joined group: ${groupId}`);
230
+ return groupInfo;
231
+ }
232
+ catch (error) {
233
+ console.error("❌ [MLS] Failed to process welcome:", error);
234
+ throw new Error(`Welcome processing failed: ${error instanceof Error ? error.message : String(error)}`);
235
+ }
236
+ }
237
+ /**
238
+ * Encrypt a message for a group
239
+ */
240
+ async encryptMessage(groupId, plaintext) {
241
+ this.ensureInitialized();
242
+ try {
243
+ console.log(`🔒 [MLS] Encrypting message for group: ${groupId}`);
244
+ const groupState = this.groups.get(groupId);
245
+ if (!groupState) {
246
+ throw new Error(`Group ${groupId} not found`);
247
+ }
248
+ const plaintextBytes = new TextEncoder().encode(plaintext);
249
+ // Create application message
250
+ const result = await (0, ts_mls_1.createApplicationMessage)(groupState, plaintextBytes, this.cipherSuite);
251
+ // Update group state (for key ratcheting)
252
+ this.groups.set(groupId, result.newState);
253
+ // Encode the private message
254
+ const encoded = (0, ts_mls_1.encodeMlsMessage)({
255
+ privateMessage: result.privateMessage,
256
+ wireformat: "mls_private_message",
257
+ version: "mls10",
258
+ });
259
+ const envelope = {
260
+ groupId: new TextEncoder().encode(groupId),
261
+ ciphertext: encoded,
262
+ timestamp: Date.now(),
263
+ };
264
+ console.log("✅ [MLS] Message encrypted");
265
+ return envelope;
266
+ }
267
+ catch (error) {
268
+ console.error("❌ [MLS] Failed to encrypt message:", error);
269
+ throw new Error(`Message encryption failed: ${error instanceof Error ? error.message : String(error)}`);
270
+ }
271
+ }
272
+ /**
273
+ * Decrypt a message from a group
274
+ */
275
+ async decryptMessage(envelope) {
276
+ this.ensureInitialized();
277
+ try {
278
+ const groupId = new TextDecoder().decode(envelope.groupId);
279
+ console.log(`🔓 [MLS] Decrypting message for group: ${groupId}`);
280
+ const groupState = this.groups.get(groupId);
281
+ if (!groupState) {
282
+ throw new Error(`Group ${groupId} not found`);
283
+ }
284
+ // Decode the message
285
+ const decoded = (0, ts_mls_1.decodeMlsMessage)(envelope.ciphertext, 0);
286
+ if (!decoded) {
287
+ throw new Error("Failed to decode message");
288
+ }
289
+ const [decodedMessage] = decoded;
290
+ if (decodedMessage.wireformat !== "mls_private_message") {
291
+ throw new Error("Expected private message");
292
+ }
293
+ // Process the private message
294
+ const result = await (0, ts_mls_1.processPrivateMessage)(groupState, decodedMessage.privateMessage, ts_mls_1.emptyPskIndex, this.cipherSuite);
295
+ // Update group state
296
+ this.groups.set(groupId, result.newState);
297
+ if (result.kind !== "applicationMessage") {
298
+ throw new Error("Expected application message");
299
+ }
300
+ const plaintext = new TextDecoder().decode(result.message);
301
+ console.log("✅ [MLS] Message decrypted");
302
+ return plaintext;
303
+ }
304
+ catch (error) {
305
+ console.error("❌ [MLS] Failed to decrypt message:", error);
306
+ throw new Error(`Decryption failed: ${error instanceof Error ? error.message : String(error)}`);
307
+ }
308
+ }
309
+ /**
310
+ * Update the group keys (key rotation)
311
+ */
312
+ async updateKey(groupId) {
313
+ this.ensureInitialized();
314
+ try {
315
+ console.log(`🔄 [MLS] Performing key rotation for group: ${groupId}`);
316
+ const groupState = this.groups.get(groupId);
317
+ if (!groupState) {
318
+ throw new Error(`Group ${groupId} not found`);
319
+ }
320
+ // Create update commit (forces path update)
321
+ const commitResult = await (0, ts_mls_1.createCommit)({ state: groupState, cipherSuite: this.cipherSuite }, {});
322
+ // Update group state
323
+ this.groups.set(groupId, commitResult.newState);
324
+ console.log(`✅ [MLS] Key rotation successful, new epoch: ${commitResult.newState.groupContext.epoch}`);
325
+ // Return the raw commit object for other members to process
326
+ return commitResult.commit;
327
+ }
328
+ catch (error) {
329
+ console.error("❌ [MLS] Failed to update key:", error);
330
+ throw new Error(`Key update failed: ${error instanceof Error ? error.message : String(error)}`);
331
+ }
332
+ }
333
+ /**
334
+ * Process a commit message (key rotation, member changes)
335
+ *
336
+ * RFC 9420 Section 12.1.8:
337
+ * - Update commits (key rotation) → PrivateMessage
338
+ * - Add/Remove commits → PublicMessage (for existing group members)
339
+ *
340
+ * This implementation handles both types based on wireformat.
341
+ */
342
+ async processCommit(groupId, commit) {
343
+ this.ensureInitialized();
344
+ try {
345
+ console.log(`⚙️ [MLS] Processing commit for group: ${groupId}`);
346
+ console.log(`🔍 [MLS Debug] Commit wireformat: ${commit.wireformat}`);
347
+ // DETAILED DEBUG LOGGING
348
+ console.group("🔍 [MLS Debug] Full Commit Structure");
349
+ console.log("commit keys:", Object.keys(commit));
350
+ console.log("commit.wireformat:", commit.wireformat);
351
+ console.log("commit.publicMessage:", commit.publicMessage);
352
+ console.log("commit.privateMessage:", commit.privateMessage);
353
+ // Log proposals if present
354
+ if (commit.publicMessage?.content) {
355
+ console.log("publicMessage.content:", commit.publicMessage.content);
356
+ console.log("publicMessage.content.proposals:", commit.publicMessage.content.proposals);
357
+ if (commit.publicMessage.content.proposals) {
358
+ commit.publicMessage.content.proposals.forEach((prop, i) => {
359
+ console.log(` Proposal ${i}:`, {
360
+ proposalType: prop.proposalType,
361
+ keys: Object.keys(prop),
362
+ full: prop,
363
+ });
364
+ });
365
+ }
366
+ }
367
+ console.groupEnd();
368
+ const groupState = this.groups.get(groupId);
369
+ if (!groupState) {
370
+ throw new Error(`Group ${groupId} not found`);
371
+ }
372
+ let result;
373
+ // RFC 9420: Route based on message type
374
+ if (commit.wireformat === "mls_public_message") {
375
+ // Public messages (add/remove member commits)
376
+ console.log("🔍 [MLS Debug] Processing as PUBLIC message (add/remove)...");
377
+ const publicMessage = commit.publicMessage || commit;
378
+ result = await (0, ts_mls_1.processPublicMessage)(groupState, publicMessage, ts_mls_1.emptyPskIndex, this.cipherSuite);
379
+ }
380
+ else if (commit.wireformat === "mls_private_message") {
381
+ // Private messages (update/key rotation commits)
382
+ console.log("🔍 [MLS Debug] Processing as PRIVATE message (update)...");
383
+ const privateMessage = commit.privateMessage || commit;
384
+ result = await (0, ts_mls_1.processPrivateMessage)(groupState, privateMessage, ts_mls_1.emptyPskIndex, this.cipherSuite);
385
+ }
386
+ else {
387
+ throw new Error(`Unknown commit wireformat: ${commit.wireformat}`);
388
+ }
389
+ // Update group state
390
+ this.groups.set(groupId, result.newState);
391
+ console.log(`✅ [MLS] Commit processed, epoch: ${result.newState.groupContext.epoch}`);
392
+ }
393
+ catch (error) {
394
+ console.error("❌ [MLS] Failed to process commit:", error);
395
+ console.error("❌ [MLS Debug] Error details:", error instanceof Error ? error.stack : "No stack trace");
396
+ console.error("❌ [MLS Debug] Error message:", error instanceof Error ? error.message : String(error));
397
+ throw new Error(`Commit processing failed: ${error instanceof Error ? error.message : String(error)}`);
398
+ }
399
+ }
400
+ /**
401
+ * Remove members from a group
402
+ */
403
+ async removeMembers(groupId, memberIndices) {
404
+ this.ensureInitialized();
405
+ try {
406
+ console.log(`➖ [MLS] Removing ${memberIndices.length} member(s) from group: ${groupId}`);
407
+ const groupState = this.groups.get(groupId);
408
+ if (!groupState) {
409
+ throw new Error(`Group ${groupId} not found`);
410
+ }
411
+ // Create remove proposals
412
+ const removeProposals = memberIndices.map((index) => ({
413
+ proposalType: "remove",
414
+ remove: {
415
+ removed: index, // ts-mls expects number, not BigInt
416
+ },
417
+ }));
418
+ // Create commit with remove proposals
419
+ const commitResult = await (0, ts_mls_1.createCommit)({ state: groupState, cipherSuite: this.cipherSuite }, { extraProposals: removeProposals });
420
+ // Update group state
421
+ this.groups.set(groupId, commitResult.newState);
422
+ // Encode the commit
423
+ const encodedCommit = (0, ts_mls_1.encodeMlsMessage)({
424
+ publicMessage: commitResult.publicMessage,
425
+ wireformat: "mls_public_message",
426
+ version: "mls10",
427
+ });
428
+ console.log("✅ [MLS] Members removed");
429
+ return encodedCommit;
430
+ }
431
+ catch (error) {
432
+ console.error("❌ [MLS] Failed to remove members:", error);
433
+ throw new Error(`Member removal failed: ${error instanceof Error ? error.message : String(error)}`);
434
+ }
435
+ }
436
+ /**
437
+ * Get list of groups
438
+ */
439
+ async getGroups() {
440
+ this.ensureInitialized();
441
+ try {
442
+ const groupIds = Array.from(this.groups.keys()).map((id) => new TextEncoder().encode(id));
443
+ return groupIds;
444
+ }
445
+ catch (error) {
446
+ console.error("❌ [MLS] Failed to get groups:", error);
447
+ throw new Error(`Getting groups failed: ${error instanceof Error ? error.message : String(error)}`);
448
+ }
449
+ }
450
+ /**
451
+ * Export group state for persistence
452
+ */
453
+ async exportGroupState(groupId) {
454
+ this.ensureInitialized();
455
+ try {
456
+ console.log(`💾 [MLS] Exporting state for group: ${groupId}`);
457
+ const groupState = this.groups.get(groupId);
458
+ if (!groupState) {
459
+ throw new Error(`Group ${groupId} not found`);
460
+ }
461
+ // Note: ts-mls ClientState contains non-serializable crypto keys
462
+ // This is a simplified export - in production you'd need proper serialization
463
+ const exportData = {
464
+ groupId,
465
+ epoch: groupState.groupContext.epoch.toString(),
466
+ exported: Date.now(),
467
+ // Add other serializable fields as needed
468
+ };
469
+ console.log("✅ [MLS] Group state exported");
470
+ return exportData;
471
+ }
472
+ catch (error) {
473
+ console.error("❌ [MLS] Failed to export group state:", error);
474
+ throw new Error(`Group state export failed: ${error instanceof Error ? error.message : String(error)}`);
475
+ }
476
+ }
477
+ /**
478
+ * Get user ID
479
+ */
480
+ getUserId() {
481
+ return this.userId;
482
+ }
483
+ /**
484
+ * Get group information
485
+ */
486
+ async getGroupKeyInfo(groupId) {
487
+ const groupState = this.groups.get(groupId);
488
+ if (!groupState) {
489
+ return null;
490
+ }
491
+ const members = this.extractMembersFromState(groupState);
492
+ return {
493
+ groupId,
494
+ epoch: groupState.groupContext.epoch.toString(),
495
+ members,
496
+ cipherSuite: "MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519",
497
+ treeHash: this.bytesToHex(groupState.groupContext.treeHash).substring(0, 16),
498
+ };
499
+ }
500
+ /**
501
+ * Clean up resources
502
+ */
503
+ async destroy() {
504
+ this.groups.clear();
505
+ this.keyPackage = null;
506
+ this.initialized = false;
507
+ console.log("✅ [MLS] Manager destroyed");
508
+ }
509
+ /**
510
+ * Extract member identities from group state
511
+ */
512
+ extractMembersFromState(state) {
513
+ const members = [];
514
+ try {
515
+ // Iterate through ratchet tree to find leaf nodes
516
+ for (let i = 0; i < state.ratchetTree.length; i++) {
517
+ const node = state.ratchetTree[i];
518
+ if (node &&
519
+ node.nodeType === "leaf" &&
520
+ node.leaf?.credential) {
521
+ const identity = new TextDecoder().decode(node.leaf.credential.identity);
522
+ members.push(identity);
523
+ }
524
+ }
525
+ }
526
+ catch (error) {
527
+ console.warn("Could not extract members:", error);
528
+ members.push(this.userId); // At least include self
529
+ }
530
+ return members;
531
+ }
532
+ /**
533
+ * Convert bytes to hex string
534
+ */
535
+ bytesToHex(bytes) {
536
+ return Array.from(bytes)
537
+ .map((b) => b.toString(16).padStart(2, "0"))
538
+ .join("");
539
+ }
540
+ /**
541
+ * Ensure the manager is initialized
542
+ */
543
+ ensureInitialized() {
544
+ if (!this.initialized) {
545
+ throw new Error("MLS Manager not initialized. Call initialize() first.");
546
+ }
547
+ }
548
+ }
549
+ exports.MLSManager = MLSManager;
550
+ exports.default = MLSManager;