@kojinx/collab-crypto 0.0.2 → 0.0.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.
@@ -24,6 +24,21 @@ export class SessionKeyManagerService {
24
24
  this.activeKeysByConversation = new Map();
25
25
  /** Fast in-memory cache mapped by sessionKeyId (for historic messages) */
26
26
  this.keysById = new Map();
27
+ /** Deduplicates in-flight server requests for the same conversation ID */
28
+ this.inFlightFetches = new Map();
29
+ }
30
+ /** Storage provider falling back to browser localStorage if not explicitly injected */
31
+ get storageProvider() {
32
+ if (this.storage)
33
+ return this.storage;
34
+ if (typeof localStorage !== 'undefined') {
35
+ return {
36
+ getItem: (k) => Promise.resolve(localStorage.getItem(k)),
37
+ setItem: (k, v) => Promise.resolve(localStorage.setItem(k, v)),
38
+ removeItem: (k) => Promise.resolve(localStorage.removeItem(k))
39
+ };
40
+ }
41
+ return null;
27
42
  }
28
43
  /**
29
44
  * Retrieves or creates an active session key for a 1:1 Direct Message conversation.
@@ -92,8 +107,9 @@ export class SessionKeyManagerService {
92
107
  if (cached)
93
108
  return cached;
94
109
  // Check local storage
95
- if (this.storage) {
96
- const stored = await this.storage.getItem(`kojinx_sk_${sessionKeyId}`);
110
+ const sp = this.storageProvider;
111
+ if (sp) {
112
+ const stored = await sp.getItem(`kojinx_sk_${sessionKeyId}`);
97
113
  if (stored) {
98
114
  try {
99
115
  const parsed = JSON.parse(stored);
@@ -190,7 +206,8 @@ export class SessionKeyManagerService {
190
206
  * Persists an unwrapped session key to local storage for fast startup.
191
207
  */
192
208
  async persistKeyLocally(key) {
193
- if (!this.storage)
209
+ const sp = this.storageProvider;
210
+ if (!sp)
194
211
  return;
195
212
  try {
196
213
  const rawKeyBase64 = await this.cryptoService.exportRawKeyBase64(key.rawKey);
@@ -202,45 +219,59 @@ export class SessionKeyManagerService {
202
219
  generation: key.generation,
203
220
  createdAt: key.createdAt
204
221
  });
205
- await this.storage.setItem(`kojinx_sk_${key.id}`, data);
222
+ await sp.setItem(`kojinx_sk_${key.id}`, data);
206
223
  }
207
224
  catch {
208
225
  // ignore persistence error
209
226
  }
210
227
  }
211
228
  /**
212
- * Fetches latest session key from server and unwraps it locally.
229
+ * Fetches latest session key from server and unwraps it locally with deduplication.
213
230
  */
214
231
  async fetchSessionKeyFromServer(conversationId, type) {
215
232
  if (!this.httpFetch || !this.getApiUrl || !this.getAuthToken)
216
233
  return null;
217
- try {
218
- const token = await this.getAuthToken();
219
- const apiUrl = this.getApiUrl();
220
- const res = await this.httpFetch(`${apiUrl}/api/e2e/session-keys/${conversationId}`, {
221
- method: 'GET',
222
- headers: {
223
- Authorization: `Bearer ${token}`
234
+ const existingPromise = this.inFlightFetches.get(conversationId);
235
+ if (existingPromise) {
236
+ return existingPromise;
237
+ }
238
+ const fetchPromise = (async () => {
239
+ try {
240
+ const token = await this.getAuthToken();
241
+ const apiUrl = this.getApiUrl();
242
+ const res = await this.httpFetch(`${apiUrl}/api/e2e/session-keys/${conversationId}`, {
243
+ method: 'GET',
244
+ headers: {
245
+ Authorization: `Bearer ${token}`
246
+ }
247
+ });
248
+ if (res.status === 200 && res.data?.encryptedKey) {
249
+ const rawKey = await this.cryptoService.unwrapSessionKey(res.data.encryptedKey);
250
+ const key = {
251
+ id: res.data.sessionKeyId,
252
+ type,
253
+ conversationId,
254
+ rawKey,
255
+ generation: res.data.generation || 1,
256
+ createdAt: res.data.createdAt
257
+ ? new Date(res.data.createdAt).getTime()
258
+ : Date.now()
259
+ };
260
+ this.cacheKey(key);
261
+ await this.persistKeyLocally(key);
262
+ return key;
224
263
  }
225
- });
226
- if (res.status === 200 && res.data?.encryptedKey) {
227
- const rawKey = await this.cryptoService.unwrapSessionKey(res.data.encryptedKey);
228
- return {
229
- id: res.data.sessionKeyId,
230
- type,
231
- conversationId,
232
- rawKey,
233
- generation: res.data.generation || 1,
234
- createdAt: res.data.createdAt
235
- ? new Date(res.data.createdAt).getTime()
236
- : Date.now()
237
- };
238
264
  }
239
- }
240
- catch {
241
- // Not found or network error
242
- }
243
- return null;
265
+ catch {
266
+ // Not found or network error
267
+ }
268
+ finally {
269
+ this.inFlightFetches.delete(conversationId);
270
+ }
271
+ return null;
272
+ })();
273
+ this.inFlightFetches.set(conversationId, fetchPromise);
274
+ return fetchPromise;
244
275
  }
245
276
  /**
246
277
  * Generates a random UUID (W3C standard or fallback).
@@ -264,4 +295,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
264
295
  providedIn: 'root'
265
296
  }]
266
297
  }] });
267
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2Vzc2lvbi1rZXktbWFuYWdlci5zZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMva29qaW54L2NvbGxhYi1jcnlwdG8vc3JjL2xpYi9zZXJ2aWNlcy9zZXNzaW9uLWtleS1tYW5hZ2VyLnNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFRbkQsT0FBTyxFQUNMLG1CQUFtQixFQUNuQixpQkFBaUIsRUFDakIscUJBQXFCLEVBQ3JCLGlCQUFpQixFQUNsQixNQUFNLFdBQVcsQ0FBQztBQUNuQixPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQzs7QUFFakUsK0RBQStEO0FBQy9ELE1BQU0sY0FBYyxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxJQUFJLENBQUM7QUFFaEQ7O0dBRUc7QUFJSCxNQUFNLE9BQU8sd0JBQXdCO0lBSHJDO1FBSVUsa0JBQWEsR0FBRyxNQUFNLENBQUMsb0JBQW9CLENBQUMsQ0FBQztRQUM3QyxjQUFTLEdBQUcsTUFBTSxDQUFzQixpQkFBaUIsRUFBRTtZQUNqRSxRQUFRLEVBQUUsSUFBSTtTQUNmLENBQUMsQ0FBQztRQUNLLGNBQVMsR0FBRyxNQUFNLENBQWUsbUJBQW1CLEVBQUU7WUFDNUQsUUFBUSxFQUFFLElBQUk7U0FDZixDQUFDLENBQUM7UUFDSyxpQkFBWSxHQUFHLE1BQU0sQ0FDM0IsaUJBQWlCLEVBQ2pCLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxDQUNuQixDQUFDO1FBQ00sWUFBTyxHQUFHLE1BQU0sQ0FBd0IscUJBQXFCLEVBQUU7WUFDckUsUUFBUSxFQUFFLElBQUk7U0FDZixDQUFDLENBQUM7UUFFSCxvREFBb0Q7UUFDNUMsNkJBQXdCLEdBQUcsSUFBSSxHQUFHLEVBQWtDLENBQUM7UUFD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QixDQUFDLENBQUMsQ0FBQztJQUNMLENBQUM7K0dBNVRVLHdCQUF3QjttSEFBeEIsd0JBQXdCLGNBRnZCLE1BQU07OzRGQUVQLHdCQUF3QjtrQkFIcEMsVUFBVTttQkFBQztvQkFDVixVQUFVLEVBQUUsTUFBTTtpQkFDbkIiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBJbmplY3RhYmxlLCBpbmplY3QgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7XG4gIENvbnZlcnNhdGlvblNlc3Npb25LZXksXG4gIEtvamlueEZldGNoRnVuY3Rpb24sXG4gIEtvamlueFN0b3JhZ2VQcm92aWRlcixcbiAgTWVtYmVyUHVibGljS2V5LFxuICBTZXNzaW9uS2V5RXhjaGFuZ2VQYXlsb2FkXG59IGZyb20gJy4uL21vZGVscy9jcnlwdG8ubW9kZWxzJztcbmltcG9ydCB7XG4gIEtPSklOWF9BUElfQkFTRV9VUkwsXG4gIEtPSklOWF9BVVRIX1RPS0VOLFxuICBLT0pJTlhfQ1JZUFRPX1NUT1JBR0UsXG4gIEtPSklOWF9IVFRQX0ZFVENIXG59IGZyb20gJy4uL3Rva2Vucyc7XG5pbXBvcnQgeyBFMkVDaGF0Q3J5cHRvU2VydmljZSB9IGZyb20gJy4vZTJlLWNoYXQtY3J5cHRvLnNlcnZpY2UnO1xuXG4vKiogMzAgZGF5cyBpbiBtaWxsaXNlY29uZHMgZm9yIG1heGltdW0gc2Vzc2lvbiBrZXkgbGlmZXRpbWUgKi9cbmNvbnN0IFRISVJUWV9EQVlTX01TID0gMzAgKiAyNCAqIDYwICogNjAgKiAxMDAwO1xuXG4vKipcbiAqIE1hbmFnZXMgdGhlIGxpZmVjeWNsZSwgY2FjaGluZywgbmVnb3RpYXRpb24sIGFuZCByb3RhdGlvbiBvZiBjb252ZXJzYXRpb24gc2Vzc2lvbiBrZXlzLlxuICovXG5ASW5qZWN0YWJsZSh7XG4gIHByb3ZpZGVkSW46ICdyb290J1xufSlcbmV4cG9ydCBjbGFzcyBTZXNzaW9uS2V5TWFuYWdlclNlcnZpY2Uge1xuICBwcml2YXRlIGNyeXB0b1NlcnZpY2UgPSBpbmplY3QoRTJFQ2hhdENyeXB0b1NlcnZpY2UpO1xuICBwcml2YXRlIGh0dHBGZXRjaCA9IGluamVjdDxLb2ppbnhGZXRjaEZ1bmN0aW9uPihLT0pJTlhfSFRUUF9GRVRDSCwge1xuICAgIG9wdGlvbmFsOiB0cnVlXG4gIH0pO1xuICBwcml2YXRlIGdldEFwaVVybCA9IGluamVjdDwoKSA9PiBzdHJpbmc+KEtPSklOWF9BUElfQkFTRV9VUkwsIHtcbiAgICBvcHRpb25hbDogdHJ1ZVxuICB9KTtcbiAgcHJpdmF0ZSBnZXRBdXRoVG9rZW4gPSBpbmplY3Q8KCkgPT4gUHJvbWlzZTxzdHJpbmcgfCBudWxsPj4oXG4gICAgS09KSU5YX0FVVEhfVE9LRU4sXG4gICAgeyBvcHRpb25hbDogdHJ1ZSB9XG4gICk7XG4gIHByaXZhdGUgc3RvcmFnZSA9IGluamVjdDxLb2ppbnhTdG9yYWdlUHJvdmlkZXI+KEtPSklOWF9DUllQVE9fU1RPUkFHRSwge1xuICAgIG9wdGlvbmFsOiB0cnVlXG4gIH0pO1xuXG4gIC8qKiBGYXN0IGluLW1lbW9yeSBjYWNoZSBtYXBwZWQgYnkgY29udmVyc2F0aW9uSWQgKi9cbiAgcHJpdmF0ZSBhY3RpdmVLZXlzQnlDb252ZXJzYXRpb24gPSBuZXcgTWFwPHN0cmluZywgQ29udmVyc2F0aW9uU2Vzc2lvbktleT4oKTtcbiAgLyoqIEZhc3QgaW4tbWVtb3J5IGNhY2hlIG1hcHBlZCBieSBzZXNzaW9uS2V5SWQgKGZvciBoaXN0b3JpYyBtZXNzYWdlcykgKi9cbiAgcHJpdmF0ZSBrZXlzQnlJZCA9IG5ldyBNYXA8c3RyaW5nLCBDb252ZXJzYXRpb25TZXNzaW9uS2V5PigpO1xuXG4gIC8qKlxuICAgKiBSZXRyaWV2ZXMgb3IgY3JlYXRlcyBhbiBhY3RpdmUgc2Vzc2lvbiBrZXkgZm9yIGEgMToxIERpcmVjdCBNZXNzYWdlIGNvbnZlcnNhdGlvbi5cbiAgICpcbiAgICogQHBhcmFtIGxvY2FsVXNlcklkIEN1cnJlbnQgdXNlcidzIElEXG4gICAqIEBwYXJhbSByZW1vdGVVc2VySWQgUmVtb3RlIGNvbnRhY3QncyBJRFxuICAgKiBAcGFyYW0gcmVtb3RlUHVibGljS2V5IEJhc2U2NCBSU0EgcHVibGljIGtleSBvZiByZW1vdGUgY29udGFjdFxuICAgKiBAcGFyYW0gbG9jYWxQdWJsaWNLZXkgQmFzZTY0IFJTQSBwdWJsaWMga2V5IG9mIGxvY2FsIHVzZXJcbiAgICovXG4gIGFzeW5jIGdldE9yQ3JlYXRlRG1TZXNzaW9uS2V5KFxuICAgIGxvY2FsVXNlcklkOiBzdHJpbmcsXG4gICAgcmVtb3RlVXNlcklkOiBzdHJpbmcsXG4gICAgcmVtb3RlUHVibGljS2V5OiBzdHJpbmcsXG4gICAgbG9jYWxQdWJsaWNLZXk6IHN0cmluZ1xuICApOiBQcm9taXNlPENvbnZlcnNhdGlvblNlc3Npb25LZXk+IHtcbiAgICBjb25zdCBjb252ZXJzYXRpb25JZCA9IHRoaXMuYnVpbGREbUNvbnZlcnNhdGlvbklkKFxuICAgICAgbG9jYWxVc2VySWQsXG4gICAgICByZW1vdGVVc2VySWRcbiAgICApO1xuXG4gICAgLy8gMS4gQ2hlY2sgaW4tbWVtb3J5IGNhY2hlXG4gICAgY29uc3QgY2FjaGVkID0gdGhpcy5hY3RpdmVLZXlzQnlDb252ZXJzYXRpb24uZ2V0KGNvbnZlcnNhdGlvbklkKTtcbiAgICBpZiAoY2FjaGVkICYmICF0aGlzLmlzS2V5RXhwaXJlZChjYWNoZWQpKSB7XG4gICAgICByZXR1cm4gY2FjaGVkO1xuICAgIH1cblxuICAgIC8vIDIuIEF0dGVtcHQgZmV0Y2hpbmcgZnJvbSBzZXJ2ZXIgaWYgSFRUUCBmZXRjaCBpcyBwcm92aWRlZFxuICAgIGlmICh0aGlzLmh0dHBGZXRjaCAmJiB0aGlzLmdldEFwaVVybCAmJiB0aGlzLmdldEF1dGhUb2tlbikge1xuICAgICAgY29uc3Qgc2VydmVyS2V5ID0gYXdhaXQgdGhpcy5mZXRjaFNlc3Npb25LZXlGcm9tU2VydmVyKFxuICAgICAgICBjb252ZXJzYXRpb25JZCxcbiAgICAgICAgJ2RtJ1xuICAgICAgKTtcbiAgICAgIGlmIChzZXJ2ZXJLZXkpIHtcbiAgICAgICAgdGhpcy5jYWNoZUtleShzZXJ2ZXJLZXkpO1xuICAgICAgICByZXR1cm4gc2VydmVyS2V5O1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIDMuIENyZWF0ZSBmcmVzaCBzZXNzaW9uIGtleSBhbmQgZGlzdHJpYnV0ZVxuICAgIHJldHVybiB0aGlzLmNyZWF0ZUFuZERpc3RyaWJ1dGVTZXNzaW9uS2V5KHtcbiAgICAgIHR5cGU6ICdkbScsXG4gICAgICBjb252ZXJzYXRpb25JZCxcbiAgICAgIGdlbmVyYXRpb246IGNhY2hlZCA/IGNhY2hlZC5nZW5lcmF0aW9uICsgMSA6IDEsXG4gICAgICBtZW1iZXJzOiBbXG4gICAgICAgIHsgdXNlcklkOiBsb2NhbFVzZXJJZCwgcHVibGljS2V5OiBsb2NhbFB1YmxpY0tleSB9LFxuICAgICAgICB7IHVzZXJJZDogcmVtb3RlVXNlcklkLCBwdWJsaWNLZXk6IHJlbW90ZVB1YmxpY0tleSB9XG4gICAgICBdXG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogUmV0cmlldmVzIG9yIGNyZWF0ZXMgYW4gYWN0aXZlIHNlc3Npb24ga2V5IGZvciBhIE5hdGl2ZSBDaGFubmVsIG9yIEdyb3VwLlxuICAgKlxuICAgKiBAcGFyYW0gY2hhbm5lbElkIFVuaXF1ZSBjaGFubmVsIElEXG4gICAqIEBwYXJhbSBtZW1iZXJzIExpc3Qgb2YgYWxsIHdvcmtzcGFjZS9jaGFubmVsIG1lbWJlcnMgd2l0aCB0aGVpciBwdWJsaWMga2V5c1xuICAgKi9cbiAgYXN5bmMgZ2V0T3JDcmVhdGVDaGFubmVsU2Vzc2lvbktleShcbiAgICBjaGFubmVsSWQ6IHN0cmluZyxcbiAgICBtZW1iZXJzOiBNZW1iZXJQdWJsaWNLZXlbXVxuICApOiBQcm9taXNlPENvbnZlcnNhdGlvblNlc3Npb25LZXk+IHtcbiAgICBjb25zdCBjYWNoZWQgPSB0aGlzLmFjdGl2ZUtleXNCeUNvbnZlcnNhdGlvbi5nZXQoY2hhbm5lbElkKTtcbiAgICBpZiAoY2FjaGVkICYmICF0aGlzLmlzS2V5RXhwaXJlZChjYWNoZWQpKSB7XG4gICAgICByZXR1cm4gY2FjaGVkO1xuICAgIH1cblxuICAgIGlmICh0aGlzLmh0dHBGZXRjaCAmJiB0aGlzLmdldEFwaVVybCAmJiB0aGlzLmdldEF1dGhUb2tlbikge1xuICAgICAgY29uc3Qgc2VydmVyS2V5ID0gYXdhaXQgdGhpcy5mZXRjaFNlc3Npb25LZXlGcm9tU2VydmVyKFxuICAgICAgICBjaGFubmVsSWQsXG4gICAgICAgICdjaGFubmVsJ1xuICAgICAgKTtcbiAgICAgIGlmIChzZXJ2ZXJLZXkpIHtcbiAgICAgICAgdGhpcy5jYWNoZUtleShzZXJ2ZXJLZXkpO1xuICAgICAgICByZXR1cm4gc2VydmVyS2V5O1xuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiB0aGlzLmNyZWF0ZUFuZERpc3RyaWJ1dGVTZXNzaW9uS2V5KHtcbiAgICAgIHR5cGU6ICdjaGFubmVsJyxcbiAgICAgIGNvbnZlcnNhdGlvbklkOiBjaGFubmVsSWQsXG4gICAgICBnZW5lcmF0aW9uOiBjYWNoZWQgPyBjYWNoZWQuZ2VuZXJhdGlvbiArIDEgOiAxLFxuICAgICAgbWVtYmVyc1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHJpZXZlcyBhIHNlc3Npb24ga2V5IGJ5IGl0cyBzcGVjaWZpYyBzZXNzaW9uS2V5SWQgKHVzZWQgZm9yIGRlY3J5cHRpbmcgaGlzdG9yaWMgbWVzc2FnZXMpLlxuICAgKi9cbiAgYXN5bmMgZ2V0U2Vzc2lvbktleUJ5SWQoXG4gICAgc2Vzc2lvbktleUlkOiBzdHJpbmdcbiAgKTogUHJvbWlzZTxDb252ZXJzYXRpb25TZXNzaW9uS2V5IHwgbnVsbD4ge1xuICAgIGNvbnN0IGNhY2hlZCA9IHRoaXMua2V5c0J5SWQuZ2V0KHNlc3Npb25LZXlJZCk7XG4gICAgaWYgKGNhY2hlZCkgcmV0dXJuIGNhY2hlZDtcblxuICAgIC8vIENoZWNrIGxvY2FsIHN0b3JhZ2VcbiAgICBpZiAodGhpcy5zdG9yYWdlKSB7XG4gICAgICBjb25zdCBzdG9yZWQgPSBhd2FpdCB0aGlzLnN0b3JhZ2UuZ2V0SXRlbShcbiAgICAgICAgYGtvamlueF9za18ke3Nlc3Npb25LZXlJZH1gXG4gICAgICApO1xuICAgICAgaWYgKHN0b3JlZCkge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGNvbnN0IHBhcnNlZCA9IEpTT04ucGFyc2Uoc3RvcmVkKTtcbiAgICAgICAgICBjb25zdCByYXdLZXkgPSBhd2FpdCB0aGlzLmNyeXB0b1NlcnZpY2UuaW1wb3J0UmF3S2V5QmFzZTY0KFxuICAgICAgICAgICAgcGFyc2VkLnJhd0tleUJhc2U2NFxuICAgICAgICAgICk7XG4gICAgICAgICAgY29uc3Qga2V5OiBDb252ZXJzYXRpb25TZXNzaW9uS2V5ID0ge1xuICAgICAgICAgICAgaWQ6IHBhcnNlZC5pZCxcbiAgICAgICAgICAgIHR5cGU6IHBhcnNlZC50eXBlLFxuICAgICAgICAgICAgY29udmVyc2F0aW9uSWQ6IHBhcnNlZC5jb252ZXJzYXRpb25JZCxcbiAgICAgICAgICAgIHJhd0tleSxcbiAgICAgICAgICAgIGdlbmVyYXRpb246IHBhcnNlZC5nZW5lcmF0aW9uLFxuICAgICAgICAgICAgY3JlYXRlZEF0OiBwYXJzZWQuY3JlYXRlZEF0XG4gICAgICAgICAgfTtcbiAgICAgICAgICB0aGlzLmNhY2hlS2V5KGtleSk7XG4gICAgICAgICAgcmV0dXJuIGtleTtcbiAgICAgICAgfSBjYXRjaCB7XG4gICAgICAgICAgLy8gaWdub3JlIGNvcnJ1cHRlZCBsb2NhbCBrZXlcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgLyoqXG4gICAqIEdlbmVyYXRlcyBhIG5ldyBBRVMgc2Vzc2lvbiBrZXksIHdyYXBzIGl0IGZvciBhbGwgbWVtYmVycywgYW5kIHVwbG9hZHMgdG8gdGhlIHNlcnZlci5cbiAgICovXG4gIGFzeW5jIGNyZWF0ZUFuZERpc3RyaWJ1dGVTZXNzaW9uS2V5KHBhcmFtczoge1xuICAgIHR5cGU6ICdkbScgfCAnY2hhbm5lbCcgfCAnZ3JvdXAnO1xuICAgIGNvbnZlcnNhdGlvbklkOiBzdHJpbmc7XG4gICAgZ2VuZXJhdGlvbjogbnVtYmVyO1xuICAgIG1lbWJlcnM6IE1lbWJlclB1YmxpY0tleVtdO1xuICB9KTogUHJvbWlzZTxDb252ZXJzYXRpb25TZXNzaW9uS2V5PiB7XG4gICAgY29uc3QgcmF3QWVzS2V5ID0gYXdhaXQgdGhpcy5jcnlwdG9TZXJ2aWNlLmdlbmVyYXRlQWVzS2V5KCk7XG4gICAgY29uc3Qgc2Vzc2lvbktleUlkID0gdGhpcy5nZW5lcmF0ZVV1aWQoKTtcblxuICAgIGNvbnN0IG1lbWJlclBheWxvYWRzOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0ge307XG4gICAgZm9yIChjb25zdCBtZW1iZXIgb2YgcGFyYW1zLm1lbWJlcnMpIHtcbiAgICAgIGlmIChtZW1iZXIucHVibGljS2V5KSB7XG4gICAgICAgIG1lbWJlclBheWxvYWRzW21lbWJlci51c2VySWRdID1cbiAgICAgICAgICBhd2FpdCB0aGlzLmNyeXB0b1NlcnZpY2Uud3JhcFNlc3Npb25LZXkoXG4gICAgICAgICAgICByYXdBZXNLZXksXG4gICAgICAgICAgICBtZW1iZXIucHVibGljS2V5XG4gICAgICAgICAgKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBjb25zdCBzZXNzaW9uS2V5OiBDb252ZXJzYXRpb25TZXNzaW9uS2V5ID0ge1xuICAgICAgaWQ6IHNlc3Npb25LZXlJZCxcbiAgICAgIHR5cGU6IHBhcmFtcy50eXBlLFxuICAgICAgY29udmVyc2F0aW9uSWQ6IHBhcmFtcy5jb252ZXJzYXRpb25JZCxcbiAgICAgIHJhd0tleTogcmF3QWVzS2V5LFxuICAgICAgZ2VuZXJhdGlvbjogcGFyYW1zLmdlbmVyYXRpb24sXG4gICAgICBjcmVhdGVkQXQ6IERhdGUubm93KClcbiAgICB9O1xuXG4gICAgLy8gU3RvcmUgaW4gbWVtb3J5XG4gICAgdGhpcy5jYWNoZUtleShzZXNzaW9uS2V5KTtcbiAgICBhd2FpdCB0aGlzLnBlcnNpc3RLZXlMb2NhbGx5KHNlc3Npb25LZXkpO1xuXG4gICAgLy8gVXBsb2FkIHRvIHNlcnZlciBpZiBvbmxpbmVcbiAgICBpZiAodGhpcy5odHRwRmV0Y2ggJiYgdGhpcy5nZXRBcGlVcmwgJiYgdGhpcy5nZXRBdXRoVG9rZW4pIHtcbiAgICAgIGNvbnN0IHRva2VuID0gYXdhaXQgdGhpcy5nZXRBdXRoVG9rZW4oKTtcbiAgICAgIGNvbnN0IGFwaVVybCA9IHRoaXMuZ2V0QXBpVXJsKCk7XG4gICAgICBjb25zdCBwYXlsb2FkOiBTZXNzaW9uS2V5RXhjaGFuZ2VQYXlsb2FkID0ge1xuICAgICAgICBzZXNzaW9uS2V5SWQsXG4gICAgICAgIGNvbnZlcnNhdGlvblR5cGU6IHBhcmFtcy50eXBlLFxuICAgICAgICBjb252ZXJzYXRpb25JZDogcGFyYW1zLmNvbnZlcnNhdGlvbklkLFxuICAgICAgICBnZW5lcmF0aW9uOiBwYXJhbXMuZ2VuZXJhdGlvbixcbiAgICAgICAgbWVtYmVyUGF5bG9hZHNcbiAgICAgIH07XG5cbiAgICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IHRoaXMuaHR0cEZldGNoKGAke2FwaVVybH0vYXBpL2UyZS9zZXNzaW9uLWtleXNgLCB7XG4gICAgICAgICAgbWV0aG9kOiAnUE9TVCcsXG4gICAgICAgICAgaGVhZGVyczoge1xuICAgICAgICAgICAgQXV0aG9yaXphdGlvbjogYEJlYXJlciAke3Rva2VufWAsXG4gICAgICAgICAgICAnQ29udGVudC1UeXBlJzogJ2FwcGxpY2F0aW9uL2pzb24nXG4gICAgICAgICAgfSxcbiAgICAgICAgICBib2R5OiBKU09OLnN0cmluZ2lmeShwYXlsb2FkKVxuICAgICAgICB9KTtcbiAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICBjb25zb2xlLndhcm4oXG4gICAgICAgICAgJ1tTZXNzaW9uS2V5TWFuYWdlcl0gRmFpbGVkIHRvIHN5bmMgc2Vzc2lvbiBrZXkgdG8gc2VydmVyOicsXG4gICAgICAgICAgZXJyXG4gICAgICAgICk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIHNlc3Npb25LZXk7XG4gIH1cblxuICAvKipcbiAgICogSGVscGVyIHRvIGNoZWNrIGlmIGEga2V5IGhhcyBleGNlZWRlZCB0aGUgMzAtZGF5IHJvdGF0aW9uIHRocmVzaG9sZC5cbiAgICovXG4gIGlzS2V5RXhwaXJlZChrZXk6IENvbnZlcnNhdGlvblNlc3Npb25LZXkpOiBib29sZWFuIHtcbiAgICByZXR1cm4gRGF0ZS5ub3coKSAtIGtleS5jcmVhdGVkQXQgPiBUSElSVFlfREFZU19NUztcbiAgfVxuXG4gIC8qKlxuICAgKiBCdWlsZHMgYSBkZXRlcm1pbmlzdGljIGNvbnZlcnNhdGlvbiBpZGVudGlmaWVyIGZvciAxOjEgRE1zIChsZXhpY29ncmFwaGljYWwgc29ydCkuXG4gICAqL1xuICBidWlsZERtQ29udmVyc2F0aW9uSWQodXNlcklkQTogc3RyaW5nLCB1c2VySWRCOiBzdHJpbmcpOiBzdHJpbmcge1xuICAgIHJldHVybiBbdXNlcklkQSwgdXNlcklkQl0uc29ydCgpLmpvaW4oJ18nKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDYWNoZXMgYSBzZXNzaW9uIGtleSBpbiBtZW1vcnkuXG4gICAqL1xuICBwcml2YXRlIGNhY2hlS2V5KGtleTogQ29udmVyc2F0aW9uU2Vzc2lvbktleSk6IHZvaWQge1xuICAgIHRoaXMuYWN0aXZlS2V5c0J5Q29udmVyc2F0aW9uLnNldChrZXkuY29udmVyc2F0aW9uSWQsIGtleSk7XG4gICAgdGhpcy5rZXlzQnlJZC5zZXQoa2V5LmlkLCBrZXkpO1xuICB9XG5cbiAgLyoqXG4gICAqIFBlcnNpc3RzIGFuIHVud3JhcHBlZCBzZXNzaW9uIGtleSB0byBsb2NhbCBzdG9yYWdlIGZvciBmYXN0IHN0YXJ0dXAuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIHBlcnNpc3RLZXlMb2NhbGx5KFxuICAgIGtleTogQ29udmVyc2F0aW9uU2Vzc2lvbktleVxuICApOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBpZiAoIXRoaXMuc3RvcmFnZSkgcmV0dXJuO1xuICAgIHRyeSB7XG4gICAgICBjb25zdCByYXdLZXlCYXNlNjQgPSBhd2FpdCB0aGlzLmNyeXB0b1NlcnZpY2UuZXhwb3J0UmF3S2V5QmFzZTY0KFxuICAgICAgICBrZXkucmF3S2V5XG4gICAgICApO1xuICAgICAgY29uc3QgZGF0YSA9IEpTT04uc3RyaW5naWZ5KHtcbiAgICAgICAgaWQ6IGtleS5pZCxcbiAgICAgICAgdHlwZToga2V5LnR5cGUsXG4gICAgICAgIGNvbnZlcnNhdGlvbklkOiBrZXkuY29udmVyc2F0aW9uSWQsXG4gICAgICAgIHJhd0tleUJhc2U2NCxcbiAgICAgICAgZ2VuZXJhdGlvbjoga2V5LmdlbmVyYXRpb24sXG4gICAgICAgIGNyZWF0ZWRBdDoga2V5LmNyZWF0ZWRBdFxuICAgICAgfSk7XG4gICAgICBhd2FpdCB0aGlzLnN0b3JhZ2Uuc2V0SXRlbShga29qaW54X3NrXyR7a2V5LmlkfWAsIGRhdGEpO1xuICAgIH0gY2F0Y2gge1xuICAgICAgLy8gaWdub3JlIHBlcnNpc3RlbmNlIGVycm9yXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEZldGNoZXMgbGF0ZXN0IHNlc3Npb24ga2V5IGZyb20gc2VydmVyIGFuZCB1bndyYXBzIGl0IGxvY2FsbHkuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGZldGNoU2Vzc2lvbktleUZyb21TZXJ2ZXIoXG4gICAgY29udmVyc2F0aW9uSWQ6IHN0cmluZyxcbiAgICB0eXBlOiAnZG0nIHwgJ2NoYW5uZWwnIHwgJ2dyb3VwJ1xuICApOiBQcm9taXNlPENvbnZlcnNhdGlvblNlc3Npb25LZXkgfCBudWxsPiB7XG4gICAgaWYgKCF0aGlzLmh0dHBGZXRjaCB8fCAhdGhpcy5nZXRBcGlVcmwgfHwgIXRoaXMuZ2V0QXV0aFRva2VuKSByZXR1cm4gbnVsbDtcblxuICAgIHRyeSB7XG4gICAgICBjb25zdCB0b2tlbiA9IGF3YWl0IHRoaXMuZ2V0QXV0aFRva2VuKCk7XG4gICAgICBjb25zdCBhcGlVcmwgPSB0aGlzLmdldEFwaVVybCgpO1xuICAgICAgY29uc3QgcmVzID0gYXdhaXQgdGhpcy5odHRwRmV0Y2g8e1xuICAgICAgICBzZXNzaW9uS2V5SWQ6IHN0cmluZztcbiAgICAgICAgZ2VuZXJhdGlvbjogbnVtYmVyO1xuICAgICAgICBlbmNyeXB0ZWRLZXk6IHN0cmluZztcbiAgICAgICAgY3JlYXRlZEF0Pzogc3RyaW5nO1xuICAgICAgfT4oYCR7YXBpVXJsfS9hcGkvZTJlL3Nlc3Npb24ta2V5cy8ke2NvbnZlcnNhdGlvbklkfWAsIHtcbiAgICAgICAgbWV0aG9kOiAnR0VUJyxcbiAgICAgICAgaGVhZGVyczoge1xuICAgICAgICAgIEF1dGhvcml6YXRpb246IGBCZWFyZXIgJHt0b2tlbn1gXG4gICAgICAgIH1cbiAgICAgIH0pO1xuXG4gICAgICBpZiAocmVzLnN0YXR1cyA9PT0gMjAwICYmIHJlcy5kYXRhPy5lbmNyeXB0ZWRLZXkpIHtcbiAgICAgICAgY29uc3QgcmF3S2V5ID0gYXdhaXQgdGhpcy5jcnlwdG9TZXJ2aWNlLnVud3JhcFNlc3Npb25LZXkoXG4gICAgICAgICAgcmVzLmRhdGEuZW5jcnlwdGVkS2V5XG4gICAgICAgICk7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgaWQ6IHJlcy5kYXRhLnNlc3Npb25LZXlJZCxcbiAgICAgICAgICB0eXBlLFxuICAgICAgICAgIGNvbnZlcnNhdGlvbklkLFxuICAgICAgICAgIHJhd0tleSxcbiAgICAgICAgICBnZW5lcmF0aW9uOiByZXMuZGF0YS5nZW5lcmF0aW9uIHx8IDEsXG4gICAgICAgICAgY3JlYXRlZEF0OiByZXMuZGF0YS5jcmVhdGVkQXRcbiAgICAgICAgICAgID8gbmV3IERhdGUocmVzLmRhdGEuY3JlYXRlZEF0KS5nZXRUaW1lKClcbiAgICAgICAgICAgIDogRGF0ZS5ub3coKVxuICAgICAgICB9O1xuICAgICAgfVxuICAgIH0gY2F0Y2gge1xuICAgICAgLy8gTm90IGZvdW5kIG9yIG5ldHdvcmsgZXJyb3JcbiAgICB9XG5cbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZW5lcmF0ZXMgYSByYW5kb20gVVVJRCAoVzNDIHN0YW5kYXJkIG9yIGZhbGxiYWNrKS5cbiAgICovXG4gIHByaXZhdGUgZ2VuZXJhdGVVdWlkKCk6IHN0cmluZyB7XG4gICAgaWYgKHR5cGVvZiBjcnlwdG8gIT09ICd1bmRlZmluZWQnICYmIGNyeXB0by5yYW5kb21VVUlEKSB7XG4gICAgICByZXR1cm4gY3J5cHRvLnJhbmRvbVVVSUQoKTtcbiAgICB9XG4gICAgcmV0dXJuICd4eHh4eHh4eC14eHh4LTR4eHgteXh4eC14eHh4eHh4eHh4eHgnLnJlcGxhY2UoL1t4eV0vZywgYyA9PiB7XG4gICAgICBjb25zdCByID0gKE1hdGgucmFuZG9tKCkgKiAxNikgfCAwO1xuICAgICAgY29uc3QgdiA9IGMgPT09ICd4JyA/IHIgOiAociAmIDB4MykgfCAweDg7XG4gICAgICByZXR1cm4gdi50b1N0cmluZygxNik7XG4gICAgfSk7XG4gIH1cbn1cbiJdfQ==
298
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2Vzc2lvbi1rZXktbWFuYWdlci5zZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMva29qaW54L2NvbGxhYi1jcnlwdG8vc3JjL2xpYi9zZXJ2aWNlcy9zZXNzaW9uLWtleS1tYW5hZ2VyLnNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFRbkQsT0FBTyxFQUNMLG1CQUFtQixFQUNuQixpQkFBaUIsRUFDakIscUJBQXFCLEVBQ3JCLGlCQUFpQixFQUNsQixNQUFNLFdBQVcsQ0FBQztBQUNuQixPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQzs7QUFFakUsK0RBQStEO0FBQy9ELE1BQU0sY0FBYyxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxJQUFJLENBQUM7QUFFaEQ7O0dBRUc7QUFJSCxNQUFNLE9BQU8sd0JBQXdCO0lBSHJDO1FBSVUsa0JBQWEsR0FBRyxNQUFNLENBQUMsb0JBQW9CLENBQUMsQ0FBQztRQUM3QyxjQUFTLEdBQUcsTUFBTSxDQUFzQixpQkFBaUIsRUFBRTtZQUNqRSxRQUFRLEVBQUUsSUFBSTtTQUNmLENBQUMsQ0FBQztRQUNLLGNBQVMsR0FBRyxNQUFNLENBQWUsbUJBQW1CLEVBQUU7WUFDNUQsUUFBUSxFQUFFLElBQUk7U0FDZixDQUFDLENBQUM7UUFDSyxpQkFBWSxHQUFHLE1BQU0sQ0FDM0IsaUJBQWlCLEVBQ2pCLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxDQUNuQixDQUFDO1FBQ00sWUFBTyxHQUFHLE1BQU0sQ0FBd0IscUJBQXFCLEVBQUU7WUFDckUsUUFBUSxFQUFFLElBQUk7U0FDZixDQUFDLENBQUM7UUFlSCxvREFBb0Q7UUFDNUMsNkJBQXdCLEdBQUcsSUFBSSxHQUFHLEVBQWtDLENBQUM7UUFD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QUM7WUFDbkMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxLQUFLLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRyxHQUFHLENBQUMsR0FBRyxHQUFHLENBQUM7WUFDMUMsT0FBTyxDQUFDLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQ3hCLENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQzsrR0EzVlUsd0JBQXdCO21IQUF4Qix3QkFBd0IsY0FGdkIsTUFBTTs7NEZBRVAsd0JBQXdCO2tCQUhwQyxVQUFVO21CQUFDO29CQUNWLFVBQVUsRUFBRSxNQUFNO2lCQUNuQiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEluamVjdGFibGUsIGluamVjdCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHtcbiAgQ29udmVyc2F0aW9uU2Vzc2lvbktleSxcbiAgS29qaW54RmV0Y2hGdW5jdGlvbixcbiAgS29qaW54U3RvcmFnZVByb3ZpZGVyLFxuICBNZW1iZXJQdWJsaWNLZXksXG4gIFNlc3Npb25LZXlFeGNoYW5nZVBheWxvYWRcbn0gZnJvbSAnLi4vbW9kZWxzL2NyeXB0by5tb2RlbHMnO1xuaW1wb3J0IHtcbiAgS09KSU5YX0FQSV9CQVNFX1VSTCxcbiAgS09KSU5YX0FVVEhfVE9LRU4sXG4gIEtPSklOWF9DUllQVE9fU1RPUkFHRSxcbiAgS09KSU5YX0hUVFBfRkVUQ0hcbn0gZnJvbSAnLi4vdG9rZW5zJztcbmltcG9ydCB7IEUyRUNoYXRDcnlwdG9TZXJ2aWNlIH0gZnJvbSAnLi9lMmUtY2hhdC1jcnlwdG8uc2VydmljZSc7XG5cbi8qKiAzMCBkYXlzIGluIG1pbGxpc2Vjb25kcyBmb3IgbWF4aW11bSBzZXNzaW9uIGtleSBsaWZldGltZSAqL1xuY29uc3QgVEhJUlRZX0RBWVNfTVMgPSAzMCAqIDI0ICogNjAgKiA2MCAqIDEwMDA7XG5cbi8qKlxuICogTWFuYWdlcyB0aGUgbGlmZWN5Y2xlLCBjYWNoaW5nLCBuZWdvdGlhdGlvbiwgYW5kIHJvdGF0aW9uIG9mIGNvbnZlcnNhdGlvbiBzZXNzaW9uIGtleXMuXG4gKi9cbkBJbmplY3RhYmxlKHtcbiAgcHJvdmlkZWRJbjogJ3Jvb3QnXG59KVxuZXhwb3J0IGNsYXNzIFNlc3Npb25LZXlNYW5hZ2VyU2VydmljZSB7XG4gIHByaXZhdGUgY3J5cHRvU2VydmljZSA9IGluamVjdChFMkVDaGF0Q3J5cHRvU2VydmljZSk7XG4gIHByaXZhdGUgaHR0cEZldGNoID0gaW5qZWN0PEtvamlueEZldGNoRnVuY3Rpb24+KEtPSklOWF9IVFRQX0ZFVENILCB7XG4gICAgb3B0aW9uYWw6IHRydWVcbiAgfSk7XG4gIHByaXZhdGUgZ2V0QXBpVXJsID0gaW5qZWN0PCgpID0+IHN0cmluZz4oS09KSU5YX0FQSV9CQVNFX1VSTCwge1xuICAgIG9wdGlvbmFsOiB0cnVlXG4gIH0pO1xuICBwcml2YXRlIGdldEF1dGhUb2tlbiA9IGluamVjdDwoKSA9PiBQcm9taXNlPHN0cmluZyB8IG51bGw+PihcbiAgICBLT0pJTlhfQVVUSF9UT0tFTixcbiAgICB7IG9wdGlvbmFsOiB0cnVlIH1cbiAgKTtcbiAgcHJpdmF0ZSBzdG9yYWdlID0gaW5qZWN0PEtvamlueFN0b3JhZ2VQcm92aWRlcj4oS09KSU5YX0NSWVBUT19TVE9SQUdFLCB7XG4gICAgb3B0aW9uYWw6IHRydWVcbiAgfSk7XG5cbiAgLyoqIFN0b3JhZ2UgcHJvdmlkZXIgZmFsbGluZyBiYWNrIHRvIGJyb3dzZXIgbG9jYWxTdG9yYWdlIGlmIG5vdCBleHBsaWNpdGx5IGluamVjdGVkICovXG4gIHByaXZhdGUgZ2V0IHN0b3JhZ2VQcm92aWRlcigpOiBLb2ppbnhTdG9yYWdlUHJvdmlkZXIgfCBudWxsIHtcbiAgICBpZiAodGhpcy5zdG9yYWdlKSByZXR1cm4gdGhpcy5zdG9yYWdlO1xuICAgIGlmICh0eXBlb2YgbG9jYWxTdG9yYWdlICE9PSAndW5kZWZpbmVkJykge1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgZ2V0SXRlbTogKGs6IHN0cmluZykgPT4gUHJvbWlzZS5yZXNvbHZlKGxvY2FsU3RvcmFnZS5nZXRJdGVtKGspKSxcbiAgICAgICAgc2V0SXRlbTogKGs6IHN0cmluZywgdjogc3RyaW5nKSA9PiBQcm9taXNlLnJlc29sdmUobG9jYWxTdG9yYWdlLnNldEl0ZW0oaywgdikpLFxuICAgICAgICByZW1vdmVJdGVtOiAoazogc3RyaW5nKSA9PiBQcm9taXNlLnJlc29sdmUobG9jYWxTdG9yYWdlLnJlbW92ZUl0ZW0oaykpXG4gICAgICB9O1xuICAgIH1cbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuXG4gIC8qKiBGYXN0IGluLW1lbW9yeSBjYWNoZSBtYXBwZWQgYnkgY29udmVyc2F0aW9uSWQgKi9cbiAgcHJpdmF0ZSBhY3RpdmVLZXlzQnlDb252ZXJzYXRpb24gPSBuZXcgTWFwPHN0cmluZywgQ29udmVyc2F0aW9uU2Vzc2lvbktleT4oKTtcbiAgLyoqIEZhc3QgaW4tbWVtb3J5IGNhY2hlIG1hcHBlZCBieSBzZXNzaW9uS2V5SWQgKGZvciBoaXN0b3JpYyBtZXNzYWdlcykgKi9cbiAgcHJpdmF0ZSBrZXlzQnlJZCA9IG5ldyBNYXA8c3RyaW5nLCBDb252ZXJzYXRpb25TZXNzaW9uS2V5PigpO1xuICAvKiogRGVkdXBsaWNhdGVzIGluLWZsaWdodCBzZXJ2ZXIgcmVxdWVzdHMgZm9yIHRoZSBzYW1lIGNvbnZlcnNhdGlvbiBJRCAqL1xuICBwcml2YXRlIGluRmxpZ2h0RmV0Y2hlcyA9IG5ldyBNYXA8c3RyaW5nLCBQcm9taXNlPENvbnZlcnNhdGlvblNlc3Npb25LZXkgfCBudWxsPj4oKTtcblxuICAvKipcbiAgICogUmV0cmlldmVzIG9yIGNyZWF0ZXMgYW4gYWN0aXZlIHNlc3Npb24ga2V5IGZvciBhIDE6MSBEaXJlY3QgTWVzc2FnZSBjb252ZXJzYXRpb24uXG4gICAqXG4gICAqIEBwYXJhbSBsb2NhbFVzZXJJZCBDdXJyZW50IHVzZXIncyBJRFxuICAgKiBAcGFyYW0gcmVtb3RlVXNlcklkIFJlbW90ZSBjb250YWN0J3MgSURcbiAgICogQHBhcmFtIHJlbW90ZVB1YmxpY0tleSBCYXNlNjQgUlNBIHB1YmxpYyBrZXkgb2YgcmVtb3RlIGNvbnRhY3RcbiAgICogQHBhcmFtIGxvY2FsUHVibGljS2V5IEJhc2U2NCBSU0EgcHVibGljIGtleSBvZiBsb2NhbCB1c2VyXG4gICAqL1xuICBhc3luYyBnZXRPckNyZWF0ZURtU2Vzc2lvbktleShcbiAgICBsb2NhbFVzZXJJZDogc3RyaW5nLFxuICAgIHJlbW90ZVVzZXJJZDogc3RyaW5nLFxuICAgIHJlbW90ZVB1YmxpY0tleTogc3RyaW5nLFxuICAgIGxvY2FsUHVibGljS2V5OiBzdHJpbmdcbiAgKTogUHJvbWlzZTxDb252ZXJzYXRpb25TZXNzaW9uS2V5PiB7XG4gICAgY29uc3QgY29udmVyc2F0aW9uSWQgPSB0aGlzLmJ1aWxkRG1Db252ZXJzYXRpb25JZChcbiAgICAgIGxvY2FsVXNlcklkLFxuICAgICAgcmVtb3RlVXNlcklkXG4gICAgKTtcblxuICAgIC8vIDEuIENoZWNrIGluLW1lbW9yeSBjYWNoZVxuICAgIGNvbnN0IGNhY2hlZCA9IHRoaXMuYWN0aXZlS2V5c0J5Q29udmVyc2F0aW9uLmdldChjb252ZXJzYXRpb25JZCk7XG4gICAgaWYgKGNhY2hlZCAmJiAhdGhpcy5pc0tleUV4cGlyZWQoY2FjaGVkKSkge1xuICAgICAgcmV0dXJuIGNhY2hlZDtcbiAgICB9XG5cbiAgICAvLyAyLiBBdHRlbXB0IGZldGNoaW5nIGZyb20gc2VydmVyIGlmIEhUVFAgZmV0Y2ggaXMgcHJvdmlkZWRcbiAgICBpZiAodGhpcy5odHRwRmV0Y2ggJiYgdGhpcy5nZXRBcGlVcmwgJiYgdGhpcy5nZXRBdXRoVG9rZW4pIHtcbiAgICAgIGNvbnN0IHNlcnZlcktleSA9IGF3YWl0IHRoaXMuZmV0Y2hTZXNzaW9uS2V5RnJvbVNlcnZlcihcbiAgICAgICAgY29udmVyc2F0aW9uSWQsXG4gICAgICAgICdkbSdcbiAgICAgICk7XG4gICAgICBpZiAoc2VydmVyS2V5KSB7XG4gICAgICAgIHRoaXMuY2FjaGVLZXkoc2VydmVyS2V5KTtcbiAgICAgICAgcmV0dXJuIHNlcnZlcktleTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyAzLiBDcmVhdGUgZnJlc2ggc2Vzc2lvbiBrZXkgYW5kIGRpc3RyaWJ1dGVcbiAgICByZXR1cm4gdGhpcy5jcmVhdGVBbmREaXN0cmlidXRlU2Vzc2lvbktleSh7XG4gICAgICB0eXBlOiAnZG0nLFxuICAgICAgY29udmVyc2F0aW9uSWQsXG4gICAgICBnZW5lcmF0aW9uOiBjYWNoZWQgPyBjYWNoZWQuZ2VuZXJhdGlvbiArIDEgOiAxLFxuICAgICAgbWVtYmVyczogW1xuICAgICAgICB7IHVzZXJJZDogbG9jYWxVc2VySWQsIHB1YmxpY0tleTogbG9jYWxQdWJsaWNLZXkgfSxcbiAgICAgICAgeyB1c2VySWQ6IHJlbW90ZVVzZXJJZCwgcHVibGljS2V5OiByZW1vdGVQdWJsaWNLZXkgfVxuICAgICAgXVxuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHJpZXZlcyBvciBjcmVhdGVzIGFuIGFjdGl2ZSBzZXNzaW9uIGtleSBmb3IgYSBOYXRpdmUgQ2hhbm5lbCBvciBHcm91cC5cbiAgICpcbiAgICogQHBhcmFtIGNoYW5uZWxJZCBVbmlxdWUgY2hhbm5lbCBJRFxuICAgKiBAcGFyYW0gbWVtYmVycyBMaXN0IG9mIGFsbCB3b3Jrc3BhY2UvY2hhbm5lbCBtZW1iZXJzIHdpdGggdGhlaXIgcHVibGljIGtleXNcbiAgICovXG4gIGFzeW5jIGdldE9yQ3JlYXRlQ2hhbm5lbFNlc3Npb25LZXkoXG4gICAgY2hhbm5lbElkOiBzdHJpbmcsXG4gICAgbWVtYmVyczogTWVtYmVyUHVibGljS2V5W11cbiAgKTogUHJvbWlzZTxDb252ZXJzYXRpb25TZXNzaW9uS2V5PiB7XG4gICAgY29uc3QgY2FjaGVkID0gdGhpcy5hY3RpdmVLZXlzQnlDb252ZXJzYXRpb24uZ2V0KGNoYW5uZWxJZCk7XG4gICAgaWYgKGNhY2hlZCAmJiAhdGhpcy5pc0tleUV4cGlyZWQoY2FjaGVkKSkge1xuICAgICAgcmV0dXJuIGNhY2hlZDtcbiAgICB9XG5cbiAgICBpZiAodGhpcy5odHRwRmV0Y2ggJiYgdGhpcy5nZXRBcGlVcmwgJiYgdGhpcy5nZXRBdXRoVG9rZW4pIHtcbiAgICAgIGNvbnN0IHNlcnZlcktleSA9IGF3YWl0IHRoaXMuZmV0Y2hTZXNzaW9uS2V5RnJvbVNlcnZlcihcbiAgICAgICAgY2hhbm5lbElkLFxuICAgICAgICAnY2hhbm5lbCdcbiAgICAgICk7XG4gICAgICBpZiAoc2VydmVyS2V5KSB7XG4gICAgICAgIHRoaXMuY2FjaGVLZXkoc2VydmVyS2V5KTtcbiAgICAgICAgcmV0dXJuIHNlcnZlcktleTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gdGhpcy5jcmVhdGVBbmREaXN0cmlidXRlU2Vzc2lvbktleSh7XG4gICAgICB0eXBlOiAnY2hhbm5lbCcsXG4gICAgICBjb252ZXJzYXRpb25JZDogY2hhbm5lbElkLFxuICAgICAgZ2VuZXJhdGlvbjogY2FjaGVkID8gY2FjaGVkLmdlbmVyYXRpb24gKyAxIDogMSxcbiAgICAgIG1lbWJlcnNcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXRyaWV2ZXMgYSBzZXNzaW9uIGtleSBieSBpdHMgc3BlY2lmaWMgc2Vzc2lvbktleUlkICh1c2VkIGZvciBkZWNyeXB0aW5nIGhpc3RvcmljIG1lc3NhZ2VzKS5cbiAgICovXG4gIGFzeW5jIGdldFNlc3Npb25LZXlCeUlkKFxuICAgIHNlc3Npb25LZXlJZDogc3RyaW5nXG4gICk6IFByb21pc2U8Q29udmVyc2F0aW9uU2Vzc2lvbktleSB8IG51bGw+IHtcbiAgICBjb25zdCBjYWNoZWQgPSB0aGlzLmtleXNCeUlkLmdldChzZXNzaW9uS2V5SWQpO1xuICAgIGlmIChjYWNoZWQpIHJldHVybiBjYWNoZWQ7XG5cbiAgICAvLyBDaGVjayBsb2NhbCBzdG9yYWdlXG4gICAgY29uc3Qgc3AgPSB0aGlzLnN0b3JhZ2VQcm92aWRlcjtcbiAgICBpZiAoc3ApIHtcbiAgICAgIGNvbnN0IHN0b3JlZCA9IGF3YWl0IHNwLmdldEl0ZW0oXG4gICAgICAgIGBrb2ppbnhfc2tfJHtzZXNzaW9uS2V5SWR9YFxuICAgICAgKTtcbiAgICAgIGlmIChzdG9yZWQpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBjb25zdCBwYXJzZWQgPSBKU09OLnBhcnNlKHN0b3JlZCk7XG4gICAgICAgICAgY29uc3QgcmF3S2V5ID0gYXdhaXQgdGhpcy5jcnlwdG9TZXJ2aWNlLmltcG9ydFJhd0tleUJhc2U2NChcbiAgICAgICAgICAgIHBhcnNlZC5yYXdLZXlCYXNlNjRcbiAgICAgICAgICApO1xuICAgICAgICAgIGNvbnN0IGtleTogQ29udmVyc2F0aW9uU2Vzc2lvbktleSA9IHtcbiAgICAgICAgICAgIGlkOiBwYXJzZWQuaWQsXG4gICAgICAgICAgICB0eXBlOiBwYXJzZWQudHlwZSxcbiAgICAgICAgICAgIGNvbnZlcnNhdGlvbklkOiBwYXJzZWQuY29udmVyc2F0aW9uSWQsXG4gICAgICAgICAgICByYXdLZXksXG4gICAgICAgICAgICBnZW5lcmF0aW9uOiBwYXJzZWQuZ2VuZXJhdGlvbixcbiAgICAgICAgICAgIGNyZWF0ZWRBdDogcGFyc2VkLmNyZWF0ZWRBdFxuICAgICAgICAgIH07XG4gICAgICAgICAgdGhpcy5jYWNoZUtleShrZXkpO1xuICAgICAgICAgIHJldHVybiBrZXk7XG4gICAgICAgIH0gY2F0Y2gge1xuICAgICAgICAgIC8vIGlnbm9yZSBjb3JydXB0ZWQgbG9jYWwga2V5XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZW5lcmF0ZXMgYSBuZXcgQUVTIHNlc3Npb24ga2V5LCB3cmFwcyBpdCBmb3IgYWxsIG1lbWJlcnMsIGFuZCB1cGxvYWRzIHRvIHRoZSBzZXJ2ZXIuXG4gICAqL1xuICBhc3luYyBjcmVhdGVBbmREaXN0cmlidXRlU2Vzc2lvbktleShwYXJhbXM6IHtcbiAgICB0eXBlOiAnZG0nIHwgJ2NoYW5uZWwnIHwgJ2dyb3VwJztcbiAgICBjb252ZXJzYXRpb25JZDogc3RyaW5nO1xuICAgIGdlbmVyYXRpb246IG51bWJlcjtcbiAgICBtZW1iZXJzOiBNZW1iZXJQdWJsaWNLZXlbXTtcbiAgfSk6IFByb21pc2U8Q29udmVyc2F0aW9uU2Vzc2lvbktleT4ge1xuICAgIGNvbnN0IHJhd0Flc0tleSA9IGF3YWl0IHRoaXMuY3J5cHRvU2VydmljZS5nZW5lcmF0ZUFlc0tleSgpO1xuICAgIGNvbnN0IHNlc3Npb25LZXlJZCA9IHRoaXMuZ2VuZXJhdGVVdWlkKCk7XG5cbiAgICBjb25zdCBtZW1iZXJQYXlsb2FkczogUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IHt9O1xuICAgIGZvciAoY29uc3QgbWVtYmVyIG9mIHBhcmFtcy5tZW1iZXJzKSB7XG4gICAgICBpZiAobWVtYmVyLnB1YmxpY0tleSkge1xuICAgICAgICBtZW1iZXJQYXlsb2Fkc1ttZW1iZXIudXNlcklkXSA9XG4gICAgICAgICAgYXdhaXQgdGhpcy5jcnlwdG9TZXJ2aWNlLndyYXBTZXNzaW9uS2V5KFxuICAgICAgICAgICAgcmF3QWVzS2V5LFxuICAgICAgICAgICAgbWVtYmVyLnB1YmxpY0tleVxuICAgICAgICAgICk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgY29uc3Qgc2Vzc2lvbktleTogQ29udmVyc2F0aW9uU2Vzc2lvbktleSA9IHtcbiAgICAgIGlkOiBzZXNzaW9uS2V5SWQsXG4gICAgICB0eXBlOiBwYXJhbXMudHlwZSxcbiAgICAgIGNvbnZlcnNhdGlvbklkOiBwYXJhbXMuY29udmVyc2F0aW9uSWQsXG4gICAgICByYXdLZXk6IHJhd0Flc0tleSxcbiAgICAgIGdlbmVyYXRpb246IHBhcmFtcy5nZW5lcmF0aW9uLFxuICAgICAgY3JlYXRlZEF0OiBEYXRlLm5vdygpXG4gICAgfTtcblxuICAgIC8vIFN0b3JlIGluIG1lbW9yeVxuICAgIHRoaXMuY2FjaGVLZXkoc2Vzc2lvbktleSk7XG4gICAgYXdhaXQgdGhpcy5wZXJzaXN0S2V5TG9jYWxseShzZXNzaW9uS2V5KTtcblxuICAgIC8vIFVwbG9hZCB0byBzZXJ2ZXIgaWYgb25saW5lXG4gICAgaWYgKHRoaXMuaHR0cEZldGNoICYmIHRoaXMuZ2V0QXBpVXJsICYmIHRoaXMuZ2V0QXV0aFRva2VuKSB7XG4gICAgICBjb25zdCB0b2tlbiA9IGF3YWl0IHRoaXMuZ2V0QXV0aFRva2VuKCk7XG4gICAgICBjb25zdCBhcGlVcmwgPSB0aGlzLmdldEFwaVVybCgpO1xuICAgICAgY29uc3QgcGF5bG9hZDogU2Vzc2lvbktleUV4Y2hhbmdlUGF5bG9hZCA9IHtcbiAgICAgICAgc2Vzc2lvbktleUlkLFxuICAgICAgICBjb252ZXJzYXRpb25UeXBlOiBwYXJhbXMudHlwZSxcbiAgICAgICAgY29udmVyc2F0aW9uSWQ6IHBhcmFtcy5jb252ZXJzYXRpb25JZCxcbiAgICAgICAgZ2VuZXJhdGlvbjogcGFyYW1zLmdlbmVyYXRpb24sXG4gICAgICAgIG1lbWJlclBheWxvYWRzXG4gICAgICB9O1xuXG4gICAgICB0cnkge1xuICAgICAgICBhd2FpdCB0aGlzLmh0dHBGZXRjaChgJHthcGlVcmx9L2FwaS9lMmUvc2Vzc2lvbi1rZXlzYCwge1xuICAgICAgICAgIG1ldGhvZDogJ1BPU1QnLFxuICAgICAgICAgIGhlYWRlcnM6IHtcbiAgICAgICAgICAgIEF1dGhvcml6YXRpb246IGBCZWFyZXIgJHt0b2tlbn1gLFxuICAgICAgICAgICAgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJ1xuICAgICAgICAgIH0sXG4gICAgICAgICAgYm9keTogSlNPTi5zdHJpbmdpZnkocGF5bG9hZClcbiAgICAgICAgfSk7XG4gICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgY29uc29sZS53YXJuKFxuICAgICAgICAgICdbU2Vzc2lvbktleU1hbmFnZXJdIEZhaWxlZCB0byBzeW5jIHNlc3Npb24ga2V5IHRvIHNlcnZlcjonLFxuICAgICAgICAgIGVyclxuICAgICAgICApO1xuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiBzZXNzaW9uS2V5O1xuICB9XG5cbiAgLyoqXG4gICAqIEhlbHBlciB0byBjaGVjayBpZiBhIGtleSBoYXMgZXhjZWVkZWQgdGhlIDMwLWRheSByb3RhdGlvbiB0aHJlc2hvbGQuXG4gICAqL1xuICBpc0tleUV4cGlyZWQoa2V5OiBDb252ZXJzYXRpb25TZXNzaW9uS2V5KTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIERhdGUubm93KCkgLSBrZXkuY3JlYXRlZEF0ID4gVEhJUlRZX0RBWVNfTVM7XG4gIH1cblxuICAvKipcbiAgICogQnVpbGRzIGEgZGV0ZXJtaW5pc3RpYyBjb252ZXJzYXRpb24gaWRlbnRpZmllciBmb3IgMToxIERNcyAobGV4aWNvZ3JhcGhpY2FsIHNvcnQpLlxuICAgKi9cbiAgYnVpbGREbUNvbnZlcnNhdGlvbklkKHVzZXJJZEE6IHN0cmluZywgdXNlcklkQjogc3RyaW5nKTogc3RyaW5nIHtcbiAgICByZXR1cm4gW3VzZXJJZEEsIHVzZXJJZEJdLnNvcnQoKS5qb2luKCdfJyk7XG4gIH1cblxuICAvKipcbiAgICogQ2FjaGVzIGEgc2Vzc2lvbiBrZXkgaW4gbWVtb3J5LlxuICAgKi9cbiAgcHJpdmF0ZSBjYWNoZUtleShrZXk6IENvbnZlcnNhdGlvblNlc3Npb25LZXkpOiB2b2lkIHtcbiAgICB0aGlzLmFjdGl2ZUtleXNCeUNvbnZlcnNhdGlvbi5zZXQoa2V5LmNvbnZlcnNhdGlvbklkLCBrZXkpO1xuICAgIHRoaXMua2V5c0J5SWQuc2V0KGtleS5pZCwga2V5KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBQZXJzaXN0cyBhbiB1bndyYXBwZWQgc2Vzc2lvbiBrZXkgdG8gbG9jYWwgc3RvcmFnZSBmb3IgZmFzdCBzdGFydHVwLlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBwZXJzaXN0S2V5TG9jYWxseShcbiAgICBrZXk6IENvbnZlcnNhdGlvblNlc3Npb25LZXlcbiAgKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3Qgc3AgPSB0aGlzLnN0b3JhZ2VQcm92aWRlcjtcbiAgICBpZiAoIXNwKSByZXR1cm47XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHJhd0tleUJhc2U2NCA9IGF3YWl0IHRoaXMuY3J5cHRvU2VydmljZS5leHBvcnRSYXdLZXlCYXNlNjQoXG4gICAgICAgIGtleS5yYXdLZXlcbiAgICAgICk7XG4gICAgICBjb25zdCBkYXRhID0gSlNPTi5zdHJpbmdpZnkoe1xuICAgICAgICBpZDoga2V5LmlkLFxuICAgICAgICB0eXBlOiBrZXkudHlwZSxcbiAgICAgICAgY29udmVyc2F0aW9uSWQ6IGtleS5jb252ZXJzYXRpb25JZCxcbiAgICAgICAgcmF3S2V5QmFzZTY0LFxuICAgICAgICBnZW5lcmF0aW9uOiBrZXkuZ2VuZXJhdGlvbixcbiAgICAgICAgY3JlYXRlZEF0OiBrZXkuY3JlYXRlZEF0XG4gICAgICB9KTtcbiAgICAgIGF3YWl0IHNwLnNldEl0ZW0oYGtvamlueF9za18ke2tleS5pZH1gLCBkYXRhKTtcbiAgICB9IGNhdGNoIHtcbiAgICAgIC8vIGlnbm9yZSBwZXJzaXN0ZW5jZSBlcnJvclxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBGZXRjaGVzIGxhdGVzdCBzZXNzaW9uIGtleSBmcm9tIHNlcnZlciBhbmQgdW53cmFwcyBpdCBsb2NhbGx5IHdpdGggZGVkdXBsaWNhdGlvbi5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgZmV0Y2hTZXNzaW9uS2V5RnJvbVNlcnZlcihcbiAgICBjb252ZXJzYXRpb25JZDogc3RyaW5nLFxuICAgIHR5cGU6ICdkbScgfCAnY2hhbm5lbCcgfCAnZ3JvdXAnXG4gICk6IFByb21pc2U8Q29udmVyc2F0aW9uU2Vzc2lvbktleSB8IG51bGw+IHtcbiAgICBpZiAoIXRoaXMuaHR0cEZldGNoIHx8ICF0aGlzLmdldEFwaVVybCB8fCAhdGhpcy5nZXRBdXRoVG9rZW4pIHJldHVybiBudWxsO1xuXG4gICAgY29uc3QgZXhpc3RpbmdQcm9taXNlID0gdGhpcy5pbkZsaWdodEZldGNoZXMuZ2V0KGNvbnZlcnNhdGlvbklkKTtcbiAgICBpZiAoZXhpc3RpbmdQcm9taXNlKSB7XG4gICAgICByZXR1cm4gZXhpc3RpbmdQcm9taXNlO1xuICAgIH1cblxuICAgIGNvbnN0IGZldGNoUHJvbWlzZSA9IChhc3luYyAoKSA9PiB7XG4gICAgICB0cnkge1xuICAgICAgICBjb25zdCB0b2tlbiA9IGF3YWl0IHRoaXMuZ2V0QXV0aFRva2VuISgpO1xuICAgICAgICBjb25zdCBhcGlVcmwgPSB0aGlzLmdldEFwaVVybCEoKTtcbiAgICAgICAgY29uc3QgcmVzID0gYXdhaXQgdGhpcy5odHRwRmV0Y2ghPHtcbiAgICAgICAgICBzZXNzaW9uS2V5SWQ6IHN0cmluZztcbiAgICAgICAgICBnZW5lcmF0aW9uOiBudW1iZXI7XG4gICAgICAgICAgZW5jcnlwdGVkS2V5OiBzdHJpbmc7XG4gICAgICAgICAgY3JlYXRlZEF0Pzogc3RyaW5nO1xuICAgICAgICB9PihgJHthcGlVcmx9L2FwaS9lMmUvc2Vzc2lvbi1rZXlzLyR7Y29udmVyc2F0aW9uSWR9YCwge1xuICAgICAgICAgIG1ldGhvZDogJ0dFVCcsXG4gICAgICAgICAgaGVhZGVyczoge1xuICAgICAgICAgICAgQXV0aG9yaXphdGlvbjogYEJlYXJlciAke3Rva2VufWBcbiAgICAgICAgICB9XG4gICAgICAgIH0pO1xuXG4gICAgICAgIGlmIChyZXMuc3RhdHVzID09PSAyMDAgJiYgcmVzLmRhdGE/LmVuY3J5cHRlZEtleSkge1xuICAgICAgICAgIGNvbnN0IHJhd0tleSA9IGF3YWl0IHRoaXMuY3J5cHRvU2VydmljZS51bndyYXBTZXNzaW9uS2V5KFxuICAgICAgICAgICAgcmVzLmRhdGEuZW5jcnlwdGVkS2V5XG4gICAgICAgICAgKTtcbiAgICAgICAgICBjb25zdCBrZXk6IENvbnZlcnNhdGlvblNlc3Npb25LZXkgPSB7XG4gICAgICAgICAgICBpZDogcmVzLmRhdGEuc2Vzc2lvbktleUlkLFxuICAgICAgICAgICAgdHlwZSxcbiAgICAgICAgICAgIGNvbnZlcnNhdGlvbklkLFxuICAgICAgICAgICAgcmF3S2V5LFxuICAgICAgICAgICAgZ2VuZXJhdGlvbjogcmVzLmRhdGEuZ2VuZXJhdGlvbiB8fCAxLFxuICAgICAgICAgICAgY3JlYXRlZEF0OiByZXMuZGF0YS5jcmVhdGVkQXRcbiAgICAgICAgICAgICAgPyBuZXcgRGF0ZShyZXMuZGF0YS5jcmVhdGVkQXQpLmdldFRpbWUoKVxuICAgICAgICAgICAgICA6IERhdGUubm93KClcbiAgICAgICAgICB9O1xuICAgICAgICAgIHRoaXMuY2FjaGVLZXkoa2V5KTtcbiAgICAgICAgICBhd2FpdCB0aGlzLnBlcnNpc3RLZXlMb2NhbGx5KGtleSk7XG4gICAgICAgICAgcmV0dXJuIGtleTtcbiAgICAgICAgfVxuICAgICAgfSBjYXRjaCB7XG4gICAgICAgIC8vIE5vdCBmb3VuZCBvciBuZXR3b3JrIGVycm9yXG4gICAgICB9IGZpbmFsbHkge1xuICAgICAgICB0aGlzLmluRmxpZ2h0RmV0Y2hlcy5kZWxldGUoY29udmVyc2F0aW9uSWQpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfSkoKTtcblxuICAgIHRoaXMuaW5GbGlnaHRGZXRjaGVzLnNldChjb252ZXJzYXRpb25JZCwgZmV0Y2hQcm9taXNlKTtcbiAgICByZXR1cm4gZmV0Y2hQcm9taXNlO1xuICB9XG5cbiAgLyoqXG4gICAqIEdlbmVyYXRlcyBhIHJhbmRvbSBVVUlEIChXM0Mgc3RhbmRhcmQgb3IgZmFsbGJhY2spLlxuICAgKi9cbiAgcHJpdmF0ZSBnZW5lcmF0ZVV1aWQoKTogc3RyaW5nIHtcbiAgICBpZiAodHlwZW9mIGNyeXB0byAhPT0gJ3VuZGVmaW5lZCcgJiYgY3J5cHRvLnJhbmRvbVVVSUQpIHtcbiAgICAgIHJldHVybiBjcnlwdG8ucmFuZG9tVVVJRCgpO1xuICAgIH1cbiAgICByZXR1cm4gJ3h4eHh4eHh4LXh4eHgtNHh4eC15eHh4LXh4eHh4eHh4eHh4eCcucmVwbGFjZSgvW3h5XS9nLCBjID0+IHtcbiAgICAgIGNvbnN0IHIgPSAoTWF0aC5yYW5kb20oKSAqIDE2KSB8IDA7XG4gICAgICBjb25zdCB2ID0gYyA9PT0gJ3gnID8gciA6IChyICYgMHgzKSB8IDB4ODtcbiAgICAgIHJldHVybiB2LnRvU3RyaW5nKDE2KTtcbiAgICB9KTtcbiAgfVxufVxuIl19
@@ -275,6 +275,21 @@ class SessionKeyManagerService {
275
275
  this.activeKeysByConversation = new Map();
276
276
  /** Fast in-memory cache mapped by sessionKeyId (for historic messages) */
277
277
  this.keysById = new Map();
278
+ /** Deduplicates in-flight server requests for the same conversation ID */
279
+ this.inFlightFetches = new Map();
280
+ }
281
+ /** Storage provider falling back to browser localStorage if not explicitly injected */
282
+ get storageProvider() {
283
+ if (this.storage)
284
+ return this.storage;
285
+ if (typeof localStorage !== 'undefined') {
286
+ return {
287
+ getItem: (k) => Promise.resolve(localStorage.getItem(k)),
288
+ setItem: (k, v) => Promise.resolve(localStorage.setItem(k, v)),
289
+ removeItem: (k) => Promise.resolve(localStorage.removeItem(k))
290
+ };
291
+ }
292
+ return null;
278
293
  }
279
294
  /**
280
295
  * Retrieves or creates an active session key for a 1:1 Direct Message conversation.
@@ -343,8 +358,9 @@ class SessionKeyManagerService {
343
358
  if (cached)
344
359
  return cached;
345
360
  // Check local storage
346
- if (this.storage) {
347
- const stored = await this.storage.getItem(`kojinx_sk_${sessionKeyId}`);
361
+ const sp = this.storageProvider;
362
+ if (sp) {
363
+ const stored = await sp.getItem(`kojinx_sk_${sessionKeyId}`);
348
364
  if (stored) {
349
365
  try {
350
366
  const parsed = JSON.parse(stored);
@@ -441,7 +457,8 @@ class SessionKeyManagerService {
441
457
  * Persists an unwrapped session key to local storage for fast startup.
442
458
  */
443
459
  async persistKeyLocally(key) {
444
- if (!this.storage)
460
+ const sp = this.storageProvider;
461
+ if (!sp)
445
462
  return;
446
463
  try {
447
464
  const rawKeyBase64 = await this.cryptoService.exportRawKeyBase64(key.rawKey);
@@ -453,45 +470,59 @@ class SessionKeyManagerService {
453
470
  generation: key.generation,
454
471
  createdAt: key.createdAt
455
472
  });
456
- await this.storage.setItem(`kojinx_sk_${key.id}`, data);
473
+ await sp.setItem(`kojinx_sk_${key.id}`, data);
457
474
  }
458
475
  catch {
459
476
  // ignore persistence error
460
477
  }
461
478
  }
462
479
  /**
463
- * Fetches latest session key from server and unwraps it locally.
480
+ * Fetches latest session key from server and unwraps it locally with deduplication.
464
481
  */
465
482
  async fetchSessionKeyFromServer(conversationId, type) {
466
483
  if (!this.httpFetch || !this.getApiUrl || !this.getAuthToken)
467
484
  return null;
468
- try {
469
- const token = await this.getAuthToken();
470
- const apiUrl = this.getApiUrl();
471
- const res = await this.httpFetch(`${apiUrl}/api/e2e/session-keys/${conversationId}`, {
472
- method: 'GET',
473
- headers: {
474
- Authorization: `Bearer ${token}`
485
+ const existingPromise = this.inFlightFetches.get(conversationId);
486
+ if (existingPromise) {
487
+ return existingPromise;
488
+ }
489
+ const fetchPromise = (async () => {
490
+ try {
491
+ const token = await this.getAuthToken();
492
+ const apiUrl = this.getApiUrl();
493
+ const res = await this.httpFetch(`${apiUrl}/api/e2e/session-keys/${conversationId}`, {
494
+ method: 'GET',
495
+ headers: {
496
+ Authorization: `Bearer ${token}`
497
+ }
498
+ });
499
+ if (res.status === 200 && res.data?.encryptedKey) {
500
+ const rawKey = await this.cryptoService.unwrapSessionKey(res.data.encryptedKey);
501
+ const key = {
502
+ id: res.data.sessionKeyId,
503
+ type,
504
+ conversationId,
505
+ rawKey,
506
+ generation: res.data.generation || 1,
507
+ createdAt: res.data.createdAt
508
+ ? new Date(res.data.createdAt).getTime()
509
+ : Date.now()
510
+ };
511
+ this.cacheKey(key);
512
+ await this.persistKeyLocally(key);
513
+ return key;
475
514
  }
476
- });
477
- if (res.status === 200 && res.data?.encryptedKey) {
478
- const rawKey = await this.cryptoService.unwrapSessionKey(res.data.encryptedKey);
479
- return {
480
- id: res.data.sessionKeyId,
481
- type,
482
- conversationId,
483
- rawKey,
484
- generation: res.data.generation || 1,
485
- createdAt: res.data.createdAt
486
- ? new Date(res.data.createdAt).getTime()
487
- : Date.now()
488
- };
489
515
  }
490
- }
491
- catch {
492
- // Not found or network error
493
- }
494
- return null;
516
+ catch {
517
+ // Not found or network error
518
+ }
519
+ finally {
520
+ this.inFlightFetches.delete(conversationId);
521
+ }
522
+ return null;
523
+ })();
524
+ this.inFlightFetches.set(conversationId, fetchPromise);
525
+ return fetchPromise;
495
526
  }
496
527
  /**
497
528
  * Generates a random UUID (W3C standard or fallback).
@@ -1 +1 @@
1
- {"version":3,"file":"kojinx-collab-crypto.mjs","sources":["../../../../projects/kojinx/collab-crypto/src/lib/tokens.ts","../../../../projects/kojinx/collab-crypto/src/lib/utils/safety-number.utils.ts","../../../../projects/kojinx/collab-crypto/src/lib/services/e2e-chat-crypto.service.ts","../../../../projects/kojinx/collab-crypto/src/lib/services/session-key-manager.service.ts","../../../../projects/kojinx/collab-crypto/src/public-api.ts","../../../../projects/kojinx/collab-crypto/src/kojinx-collab-crypto.ts"],"sourcesContent":["import { InjectionToken } from '@angular/core';\nimport {\n KojinxCryptoProvider,\n KojinxFetchFunction,\n KojinxStorageProvider\n} from './models/crypto.models';\n\n/**\n * Injected Asymmetric Cryptography Provider (RSA-2048 OAEP).\n * On desktop: Implemented via Tauri Rust Keyring IPC.\n * On mobile: Implemented via SubtleCrypto + Capacitor SecureStorage.\n */\nexport const KOJINX_CRYPTO_PROVIDER = new InjectionToken<KojinxCryptoProvider>(\n 'KOJINX_CRYPTO_PROVIDER'\n);\n\n/**\n * Injected Client-Agnostic HTTP Fetcher (bypass CORS on native).\n */\nexport const KOJINX_HTTP_FETCH = new InjectionToken<KojinxFetchFunction>(\n 'KOJINX_HTTP_FETCH'\n);\n\n/**\n * Injected Storage Provider for local session key caching.\n */\nexport const KOJINX_CRYPTO_STORAGE = new InjectionToken<KojinxStorageProvider>(\n 'KOJINX_CRYPTO_STORAGE',\n {\n factory: () => ({\n getItem: (key: string) => Promise.resolve(localStorage.getItem(key)),\n setItem: (key: string, value: string) => {\n localStorage.setItem(key, value);\n return Promise.resolve();\n },\n removeItem: (key: string) => {\n localStorage.removeItem(key);\n return Promise.resolve();\n }\n })\n }\n);\n\n/**\n * Injected API Base URL accessor function.\n */\nexport const KOJINX_API_BASE_URL = new InjectionToken<() => string>(\n 'KOJINX_API_BASE_URL'\n);\n\n/**\n * Injected Auth Bearer Token accessor function.\n */\nexport const KOJINX_AUTH_TOKEN = new InjectionToken<() => Promise<string | null>>(\n 'KOJINX_AUTH_TOKEN'\n);\n","import { SafetyNumber } from '../models/crypto.models';\n\n/**\n * Computes deterministic SHA-256 safety verification numbers (Signal-style).\n * Users can compare these 30-digit numbers out-of-band to verify end-to-end identity.\n */\nexport class SafetyNumberUtils {\n /**\n * Generates a Safety Number between two identity public keys.\n *\n * @param localPublicKey Base64-encoded local RSA public key\n * @param remotePublicKey Base64-encoded remote RSA public key\n */\n static async generate(\n localPublicKey: string,\n remotePublicKey: string\n ): Promise<SafetyNumber> {\n if (!localPublicKey || !remotePublicKey) {\n throw new Error('Both public keys are required to generate safety numbers.');\n }\n\n const localFingerprint = await this.hashPublicKey(localPublicKey);\n const remoteFingerprint = await this.hashPublicKey(remotePublicKey);\n\n // Sort lexicographically to ensure order-independent verification\n const sorted = [localFingerprint, remoteFingerprint].sort();\n const combinedDigest = await this.sha256(sorted.join(''));\n\n // Convert hex digest to a 30-digit decimal representation\n const digits = this.hexTo30Digits(combinedDigest);\n const formatted = this.formatBlocks(digits);\n\n return {\n raw: combinedDigest,\n formatted,\n localFingerprint: localFingerprint.substring(0, 16),\n remoteFingerprint: remoteFingerprint.substring(0, 16)\n };\n }\n\n /**\n * Hashes a single public key using SHA-256.\n */\n static async hashPublicKey(publicKeyBase64: string): Promise<string> {\n const cleanKey = publicKeyBase64.replace(/\\s+/g, '');\n return this.sha256(cleanKey);\n }\n\n /**\n * Computes SHA-256 hex string using W3C Web Crypto API.\n */\n private static async sha256(input: string): Promise<string> {\n const encoder = new TextEncoder();\n const data = encoder.encode(input);\n const hashBuffer = await crypto.subtle.digest('SHA-256', data);\n const hashArray = Array.from(new Uint8Array(hashBuffer));\n return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');\n }\n\n /**\n * Converts a hexadecimal string into a deterministic 30-digit decimal string.\n */\n private static hexTo30Digits(hex: string): string {\n let result = '';\n // Take chunks of 8 hex characters (32-bit integers) to produce 5-digit decimal blocks\n for (let i = 0; i < 6; i++) {\n const chunk = hex.substring(i * 8, (i + 1) * 8);\n const num = parseInt(chunk, 16) % 100000;\n result += num.toString().padStart(5, '0');\n }\n return result;\n }\n\n /**\n * Formats a 30-digit string into 6 blocks of 5 digits separated by spaces.\n */\n private static formatBlocks(digits: string): string {\n const blocks: string[] = [];\n for (let i = 0; i < digits.length; i += 5) {\n blocks.push(digits.substring(i, i + 5));\n }\n return blocks.join(' ');\n }\n}\n","import { Injectable, inject } from '@angular/core';\nimport {\n ConversationSessionKey,\n EncryptedMessagePayload,\n KojinxCryptoProvider\n} from '../models/crypto.models';\nimport { KOJINX_CRYPTO_PROVIDER } from '../tokens';\n\n/**\n * Pure End-to-End Encryption cryptographic service.\n * Handles AES-256-GCM message encryption/decryption and RSA key wrapping/unwrapping.\n */\n@Injectable({\n providedIn: 'root'\n})\nexport class E2EChatCryptoService {\n private cryptoProvider = inject<KojinxCryptoProvider>(KOJINX_CRYPTO_PROVIDER, {\n optional: true\n });\n\n /**\n * Generates a new cryptographically secure 256-bit AES-GCM session key.\n */\n async generateAesKey(): Promise<CryptoKey> {\n return crypto.subtle.generateKey(\n {\n name: 'AES-GCM',\n length: 256\n },\n true, // extractable for RSA wrapping & secure export\n ['encrypt', 'decrypt']\n );\n }\n\n /**\n * Encrypts plaintext string using AES-256-GCM with a fresh 12-byte (96-bit) nonce.\n *\n * @param sessionKey In-memory active session key\n * @param plaintext Plaintext message content\n */\n async encryptMessage(\n sessionKey: ConversationSessionKey,\n plaintext: string\n ): Promise<EncryptedMessagePayload> {\n if (!sessionKey || !sessionKey.rawKey) {\n throw new Error('Invalid session key provided for encryption.');\n }\n\n // Standard 12-byte initialization vector for AES-GCM\n const nonce = crypto.getRandomValues(new Uint8Array(12));\n const encoder = new TextEncoder();\n const encodedPlaintext = encoder.encode(plaintext);\n\n const ciphertextBuffer = await crypto.subtle.encrypt(\n {\n name: 'AES-GCM',\n iv: nonce\n },\n sessionKey.rawKey,\n encodedPlaintext\n );\n\n return {\n ciphertext: this.arrayBufferToBase64(ciphertextBuffer),\n nonce: this.arrayBufferToBase64(nonce.buffer),\n sessionKeyId: sessionKey.id,\n generation: sessionKey.generation\n };\n }\n\n /**\n * Decrypts an EncryptedMessagePayload using AES-256-GCM.\n *\n * @param sessionKey In-memory session key corresponding to payload.sessionKeyId\n * @param payload Encrypted payload containing ciphertext and nonce\n */\n async decryptMessage(\n sessionKey: ConversationSessionKey,\n payload: EncryptedMessagePayload\n ): Promise<string> {\n if (!sessionKey || !sessionKey.rawKey) {\n throw new Error('Invalid session key provided for decryption.');\n }\n\n const ciphertext = this.base64ToArrayBuffer(payload.ciphertext);\n const nonce = this.base64ToArrayBuffer(payload.nonce);\n\n const decryptedBuffer = await crypto.subtle.decrypt(\n {\n name: 'AES-GCM',\n iv: new Uint8Array(nonce)\n },\n sessionKey.rawKey,\n ciphertext\n );\n\n const decoder = new TextDecoder();\n return decoder.decode(decryptedBuffer);\n }\n\n /**\n * Exports an AES-GCM key and wraps (encrypts) it with a recipient's RSA public key.\n *\n * @param aesKey AES CryptoKey to wrap\n * @param recipientPublicKey Base64-encoded recipient RSA public key\n */\n async wrapSessionKey(\n aesKey: CryptoKey,\n recipientPublicKey: string\n ): Promise<string> {\n if (!this.cryptoProvider) {\n throw new Error(\n 'KOJINX_CRYPTO_PROVIDER is not provided. Cannot wrap session keys.'\n );\n }\n\n const rawKeyBytes = await crypto.subtle.exportKey('raw', aesKey);\n const rawKeyBase64 = this.arrayBufferToBase64(rawKeyBytes);\n\n // Asymmetrically encrypt using host RSA provider\n return this.cryptoProvider.encrypt(recipientPublicKey, rawKeyBase64);\n }\n\n /**\n * Unwraps (decrypts) an RSA-encrypted AES raw key and imports it back into a W3C CryptoKey.\n *\n * @param encryptedAesKeyBase64 RSA-encrypted AES raw key in base64 format\n */\n async unwrapSessionKey(encryptedAesKeyBase64: string): Promise<CryptoKey> {\n if (!this.cryptoProvider) {\n throw new Error(\n 'KOJINX_CRYPTO_PROVIDER is not provided. Cannot unwrap session keys.'\n );\n }\n\n // Asymmetrically decrypt using local private key\n const rawKeyBase64 = await this.cryptoProvider.decrypt(\n encryptedAesKeyBase64\n );\n const rawKeyBuffer = this.base64ToArrayBuffer(rawKeyBase64);\n\n return crypto.subtle.importKey(\n 'raw',\n rawKeyBuffer,\n {\n name: 'AES-GCM',\n length: 256\n },\n true,\n ['encrypt', 'decrypt']\n );\n }\n\n /**\n * Exports a CryptoKey to base64 raw bytes.\n */\n async exportRawKeyBase64(key: CryptoKey): Promise<string> {\n const raw = await crypto.subtle.exportKey('raw', key);\n return this.arrayBufferToBase64(raw);\n }\n\n /**\n * Imports a base64 raw key string into a CryptoKey.\n */\n async importRawKeyBase64(base64: string): Promise<CryptoKey> {\n const buffer = this.base64ToArrayBuffer(base64);\n return crypto.subtle.importKey(\n 'raw',\n buffer,\n {\n name: 'AES-GCM',\n length: 256\n },\n true,\n ['encrypt', 'decrypt']\n );\n }\n\n /**\n * Encodes ArrayBuffer to Base64 string.\n */\n arrayBufferToBase64(buffer: ArrayBuffer | Uint8Array): string {\n const bytes = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);\n let binary = '';\n for (let i = 0; i < bytes.byteLength; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary);\n }\n\n /**\n * Decodes Base64 string to ArrayBuffer.\n */\n base64ToArrayBuffer(base64: string): ArrayBuffer {\n const binary = atob(base64.trim());\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes.buffer;\n }\n}\n","import { Injectable, inject } from '@angular/core';\nimport {\n ConversationSessionKey,\n KojinxFetchFunction,\n KojinxStorageProvider,\n MemberPublicKey,\n SessionKeyExchangePayload\n} from '../models/crypto.models';\nimport {\n KOJINX_API_BASE_URL,\n KOJINX_AUTH_TOKEN,\n KOJINX_CRYPTO_STORAGE,\n KOJINX_HTTP_FETCH\n} from '../tokens';\nimport { E2EChatCryptoService } from './e2e-chat-crypto.service';\n\n/** 30 days in milliseconds for maximum session key lifetime */\nconst THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1000;\n\n/**\n * Manages the lifecycle, caching, negotiation, and rotation of conversation session keys.\n */\n@Injectable({\n providedIn: 'root'\n})\nexport class SessionKeyManagerService {\n private cryptoService = inject(E2EChatCryptoService);\n private httpFetch = inject<KojinxFetchFunction>(KOJINX_HTTP_FETCH, {\n optional: true\n });\n private getApiUrl = inject<() => string>(KOJINX_API_BASE_URL, {\n optional: true\n });\n private getAuthToken = inject<() => Promise<string | null>>(\n KOJINX_AUTH_TOKEN,\n { optional: true }\n );\n private storage = inject<KojinxStorageProvider>(KOJINX_CRYPTO_STORAGE, {\n optional: true\n });\n\n /** Fast in-memory cache mapped by conversationId */\n private activeKeysByConversation = new Map<string, ConversationSessionKey>();\n /** Fast in-memory cache mapped by sessionKeyId (for historic messages) */\n private keysById = new Map<string, ConversationSessionKey>();\n\n /**\n * Retrieves or creates an active session key for a 1:1 Direct Message conversation.\n *\n * @param localUserId Current user's ID\n * @param remoteUserId Remote contact's ID\n * @param remotePublicKey Base64 RSA public key of remote contact\n * @param localPublicKey Base64 RSA public key of local user\n */\n async getOrCreateDmSessionKey(\n localUserId: string,\n remoteUserId: string,\n remotePublicKey: string,\n localPublicKey: string\n ): Promise<ConversationSessionKey> {\n const conversationId = this.buildDmConversationId(\n localUserId,\n remoteUserId\n );\n\n // 1. Check in-memory cache\n const cached = this.activeKeysByConversation.get(conversationId);\n if (cached && !this.isKeyExpired(cached)) {\n return cached;\n }\n\n // 2. Attempt fetching from server if HTTP fetch is provided\n if (this.httpFetch && this.getApiUrl && this.getAuthToken) {\n const serverKey = await this.fetchSessionKeyFromServer(\n conversationId,\n 'dm'\n );\n if (serverKey) {\n this.cacheKey(serverKey);\n return serverKey;\n }\n }\n\n // 3. Create fresh session key and distribute\n return this.createAndDistributeSessionKey({\n type: 'dm',\n conversationId,\n generation: cached ? cached.generation + 1 : 1,\n members: [\n { userId: localUserId, publicKey: localPublicKey },\n { userId: remoteUserId, publicKey: remotePublicKey }\n ]\n });\n }\n\n /**\n * Retrieves or creates an active session key for a Native Channel or Group.\n *\n * @param channelId Unique channel ID\n * @param members List of all workspace/channel members with their public keys\n */\n async getOrCreateChannelSessionKey(\n channelId: string,\n members: MemberPublicKey[]\n ): Promise<ConversationSessionKey> {\n const cached = this.activeKeysByConversation.get(channelId);\n if (cached && !this.isKeyExpired(cached)) {\n return cached;\n }\n\n if (this.httpFetch && this.getApiUrl && this.getAuthToken) {\n const serverKey = await this.fetchSessionKeyFromServer(\n channelId,\n 'channel'\n );\n if (serverKey) {\n this.cacheKey(serverKey);\n return serverKey;\n }\n }\n\n return this.createAndDistributeSessionKey({\n type: 'channel',\n conversationId: channelId,\n generation: cached ? cached.generation + 1 : 1,\n members\n });\n }\n\n /**\n * Retrieves a session key by its specific sessionKeyId (used for decrypting historic messages).\n */\n async getSessionKeyById(\n sessionKeyId: string\n ): Promise<ConversationSessionKey | null> {\n const cached = this.keysById.get(sessionKeyId);\n if (cached) return cached;\n\n // Check local storage\n if (this.storage) {\n const stored = await this.storage.getItem(\n `kojinx_sk_${sessionKeyId}`\n );\n if (stored) {\n try {\n const parsed = JSON.parse(stored);\n const rawKey = await this.cryptoService.importRawKeyBase64(\n parsed.rawKeyBase64\n );\n const key: ConversationSessionKey = {\n id: parsed.id,\n type: parsed.type,\n conversationId: parsed.conversationId,\n rawKey,\n generation: parsed.generation,\n createdAt: parsed.createdAt\n };\n this.cacheKey(key);\n return key;\n } catch {\n // ignore corrupted local key\n }\n }\n }\n\n return null;\n }\n\n /**\n * Generates a new AES session key, wraps it for all members, and uploads to the server.\n */\n async createAndDistributeSessionKey(params: {\n type: 'dm' | 'channel' | 'group';\n conversationId: string;\n generation: number;\n members: MemberPublicKey[];\n }): Promise<ConversationSessionKey> {\n const rawAesKey = await this.cryptoService.generateAesKey();\n const sessionKeyId = this.generateUuid();\n\n const memberPayloads: Record<string, string> = {};\n for (const member of params.members) {\n if (member.publicKey) {\n memberPayloads[member.userId] =\n await this.cryptoService.wrapSessionKey(\n rawAesKey,\n member.publicKey\n );\n }\n }\n\n const sessionKey: ConversationSessionKey = {\n id: sessionKeyId,\n type: params.type,\n conversationId: params.conversationId,\n rawKey: rawAesKey,\n generation: params.generation,\n createdAt: Date.now()\n };\n\n // Store in memory\n this.cacheKey(sessionKey);\n await this.persistKeyLocally(sessionKey);\n\n // Upload to server if online\n if (this.httpFetch && this.getApiUrl && this.getAuthToken) {\n const token = await this.getAuthToken();\n const apiUrl = this.getApiUrl();\n const payload: SessionKeyExchangePayload = {\n sessionKeyId,\n conversationType: params.type,\n conversationId: params.conversationId,\n generation: params.generation,\n memberPayloads\n };\n\n try {\n await this.httpFetch(`${apiUrl}/api/e2e/session-keys`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify(payload)\n });\n } catch (err) {\n console.warn(\n '[SessionKeyManager] Failed to sync session key to server:',\n err\n );\n }\n }\n\n return sessionKey;\n }\n\n /**\n * Helper to check if a key has exceeded the 30-day rotation threshold.\n */\n isKeyExpired(key: ConversationSessionKey): boolean {\n return Date.now() - key.createdAt > THIRTY_DAYS_MS;\n }\n\n /**\n * Builds a deterministic conversation identifier for 1:1 DMs (lexicographical sort).\n */\n buildDmConversationId(userIdA: string, userIdB: string): string {\n return [userIdA, userIdB].sort().join('_');\n }\n\n /**\n * Caches a session key in memory.\n */\n private cacheKey(key: ConversationSessionKey): void {\n this.activeKeysByConversation.set(key.conversationId, key);\n this.keysById.set(key.id, key);\n }\n\n /**\n * Persists an unwrapped session key to local storage for fast startup.\n */\n private async persistKeyLocally(\n key: ConversationSessionKey\n ): Promise<void> {\n if (!this.storage) return;\n try {\n const rawKeyBase64 = await this.cryptoService.exportRawKeyBase64(\n key.rawKey\n );\n const data = JSON.stringify({\n id: key.id,\n type: key.type,\n conversationId: key.conversationId,\n rawKeyBase64,\n generation: key.generation,\n createdAt: key.createdAt\n });\n await this.storage.setItem(`kojinx_sk_${key.id}`, data);\n } catch {\n // ignore persistence error\n }\n }\n\n /**\n * Fetches latest session key from server and unwraps it locally.\n */\n private async fetchSessionKeyFromServer(\n conversationId: string,\n type: 'dm' | 'channel' | 'group'\n ): Promise<ConversationSessionKey | null> {\n if (!this.httpFetch || !this.getApiUrl || !this.getAuthToken) return null;\n\n try {\n const token = await this.getAuthToken();\n const apiUrl = this.getApiUrl();\n const res = await this.httpFetch<{\n sessionKeyId: string;\n generation: number;\n encryptedKey: string;\n createdAt?: string;\n }>(`${apiUrl}/api/e2e/session-keys/${conversationId}`, {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${token}`\n }\n });\n\n if (res.status === 200 && res.data?.encryptedKey) {\n const rawKey = await this.cryptoService.unwrapSessionKey(\n res.data.encryptedKey\n );\n return {\n id: res.data.sessionKeyId,\n type,\n conversationId,\n rawKey,\n generation: res.data.generation || 1,\n createdAt: res.data.createdAt\n ? new Date(res.data.createdAt).getTime()\n : Date.now()\n };\n }\n } catch {\n // Not found or network error\n }\n\n return null;\n }\n\n /**\n * Generates a random UUID (W3C standard or fallback).\n */\n private generateUuid(): string {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {\n const r = (Math.random() * 16) | 0;\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n }\n}\n","/*\n * Public API Surface of @kojinx/collab-crypto\n */\n\nexport * from './lib/tokens';\nexport * from './lib/models/crypto.models';\nexport * from './lib/utils/safety-number.utils';\nexport * from './lib/services/e2e-chat-crypto.service';\nexport * from './lib/services/session-key-manager.service';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"kojinx-collab-crypto.mjs","sources":["../../../../projects/kojinx/collab-crypto/src/lib/tokens.ts","../../../../projects/kojinx/collab-crypto/src/lib/utils/safety-number.utils.ts","../../../../projects/kojinx/collab-crypto/src/lib/services/e2e-chat-crypto.service.ts","../../../../projects/kojinx/collab-crypto/src/lib/services/session-key-manager.service.ts","../../../../projects/kojinx/collab-crypto/src/public-api.ts","../../../../projects/kojinx/collab-crypto/src/kojinx-collab-crypto.ts"],"sourcesContent":["import { InjectionToken } from '@angular/core';\nimport {\n KojinxCryptoProvider,\n KojinxFetchFunction,\n KojinxStorageProvider\n} from './models/crypto.models';\n\n/**\n * Injected Asymmetric Cryptography Provider (RSA-2048 OAEP).\n * On desktop: Implemented via Tauri Rust Keyring IPC.\n * On mobile: Implemented via SubtleCrypto + Capacitor SecureStorage.\n */\nexport const KOJINX_CRYPTO_PROVIDER = new InjectionToken<KojinxCryptoProvider>(\n 'KOJINX_CRYPTO_PROVIDER'\n);\n\n/**\n * Injected Client-Agnostic HTTP Fetcher (bypass CORS on native).\n */\nexport const KOJINX_HTTP_FETCH = new InjectionToken<KojinxFetchFunction>(\n 'KOJINX_HTTP_FETCH'\n);\n\n/**\n * Injected Storage Provider for local session key caching.\n */\nexport const KOJINX_CRYPTO_STORAGE = new InjectionToken<KojinxStorageProvider>(\n 'KOJINX_CRYPTO_STORAGE',\n {\n factory: () => ({\n getItem: (key: string) => Promise.resolve(localStorage.getItem(key)),\n setItem: (key: string, value: string) => {\n localStorage.setItem(key, value);\n return Promise.resolve();\n },\n removeItem: (key: string) => {\n localStorage.removeItem(key);\n return Promise.resolve();\n }\n })\n }\n);\n\n/**\n * Injected API Base URL accessor function.\n */\nexport const KOJINX_API_BASE_URL = new InjectionToken<() => string>(\n 'KOJINX_API_BASE_URL'\n);\n\n/**\n * Injected Auth Bearer Token accessor function.\n */\nexport const KOJINX_AUTH_TOKEN = new InjectionToken<() => Promise<string | null>>(\n 'KOJINX_AUTH_TOKEN'\n);\n","import { SafetyNumber } from '../models/crypto.models';\n\n/**\n * Computes deterministic SHA-256 safety verification numbers (Signal-style).\n * Users can compare these 30-digit numbers out-of-band to verify end-to-end identity.\n */\nexport class SafetyNumberUtils {\n /**\n * Generates a Safety Number between two identity public keys.\n *\n * @param localPublicKey Base64-encoded local RSA public key\n * @param remotePublicKey Base64-encoded remote RSA public key\n */\n static async generate(\n localPublicKey: string,\n remotePublicKey: string\n ): Promise<SafetyNumber> {\n if (!localPublicKey || !remotePublicKey) {\n throw new Error('Both public keys are required to generate safety numbers.');\n }\n\n const localFingerprint = await this.hashPublicKey(localPublicKey);\n const remoteFingerprint = await this.hashPublicKey(remotePublicKey);\n\n // Sort lexicographically to ensure order-independent verification\n const sorted = [localFingerprint, remoteFingerprint].sort();\n const combinedDigest = await this.sha256(sorted.join(''));\n\n // Convert hex digest to a 30-digit decimal representation\n const digits = this.hexTo30Digits(combinedDigest);\n const formatted = this.formatBlocks(digits);\n\n return {\n raw: combinedDigest,\n formatted,\n localFingerprint: localFingerprint.substring(0, 16),\n remoteFingerprint: remoteFingerprint.substring(0, 16)\n };\n }\n\n /**\n * Hashes a single public key using SHA-256.\n */\n static async hashPublicKey(publicKeyBase64: string): Promise<string> {\n const cleanKey = publicKeyBase64.replace(/\\s+/g, '');\n return this.sha256(cleanKey);\n }\n\n /**\n * Computes SHA-256 hex string using W3C Web Crypto API.\n */\n private static async sha256(input: string): Promise<string> {\n const encoder = new TextEncoder();\n const data = encoder.encode(input);\n const hashBuffer = await crypto.subtle.digest('SHA-256', data);\n const hashArray = Array.from(new Uint8Array(hashBuffer));\n return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');\n }\n\n /**\n * Converts a hexadecimal string into a deterministic 30-digit decimal string.\n */\n private static hexTo30Digits(hex: string): string {\n let result = '';\n // Take chunks of 8 hex characters (32-bit integers) to produce 5-digit decimal blocks\n for (let i = 0; i < 6; i++) {\n const chunk = hex.substring(i * 8, (i + 1) * 8);\n const num = parseInt(chunk, 16) % 100000;\n result += num.toString().padStart(5, '0');\n }\n return result;\n }\n\n /**\n * Formats a 30-digit string into 6 blocks of 5 digits separated by spaces.\n */\n private static formatBlocks(digits: string): string {\n const blocks: string[] = [];\n for (let i = 0; i < digits.length; i += 5) {\n blocks.push(digits.substring(i, i + 5));\n }\n return blocks.join(' ');\n }\n}\n","import { Injectable, inject } from '@angular/core';\nimport {\n ConversationSessionKey,\n EncryptedMessagePayload,\n KojinxCryptoProvider\n} from '../models/crypto.models';\nimport { KOJINX_CRYPTO_PROVIDER } from '../tokens';\n\n/**\n * Pure End-to-End Encryption cryptographic service.\n * Handles AES-256-GCM message encryption/decryption and RSA key wrapping/unwrapping.\n */\n@Injectable({\n providedIn: 'root'\n})\nexport class E2EChatCryptoService {\n private cryptoProvider = inject<KojinxCryptoProvider>(KOJINX_CRYPTO_PROVIDER, {\n optional: true\n });\n\n /**\n * Generates a new cryptographically secure 256-bit AES-GCM session key.\n */\n async generateAesKey(): Promise<CryptoKey> {\n return crypto.subtle.generateKey(\n {\n name: 'AES-GCM',\n length: 256\n },\n true, // extractable for RSA wrapping & secure export\n ['encrypt', 'decrypt']\n );\n }\n\n /**\n * Encrypts plaintext string using AES-256-GCM with a fresh 12-byte (96-bit) nonce.\n *\n * @param sessionKey In-memory active session key\n * @param plaintext Plaintext message content\n */\n async encryptMessage(\n sessionKey: ConversationSessionKey,\n plaintext: string\n ): Promise<EncryptedMessagePayload> {\n if (!sessionKey || !sessionKey.rawKey) {\n throw new Error('Invalid session key provided for encryption.');\n }\n\n // Standard 12-byte initialization vector for AES-GCM\n const nonce = crypto.getRandomValues(new Uint8Array(12));\n const encoder = new TextEncoder();\n const encodedPlaintext = encoder.encode(plaintext);\n\n const ciphertextBuffer = await crypto.subtle.encrypt(\n {\n name: 'AES-GCM',\n iv: nonce\n },\n sessionKey.rawKey,\n encodedPlaintext\n );\n\n return {\n ciphertext: this.arrayBufferToBase64(ciphertextBuffer),\n nonce: this.arrayBufferToBase64(nonce.buffer),\n sessionKeyId: sessionKey.id,\n generation: sessionKey.generation\n };\n }\n\n /**\n * Decrypts an EncryptedMessagePayload using AES-256-GCM.\n *\n * @param sessionKey In-memory session key corresponding to payload.sessionKeyId\n * @param payload Encrypted payload containing ciphertext and nonce\n */\n async decryptMessage(\n sessionKey: ConversationSessionKey,\n payload: EncryptedMessagePayload\n ): Promise<string> {\n if (!sessionKey || !sessionKey.rawKey) {\n throw new Error('Invalid session key provided for decryption.');\n }\n\n const ciphertext = this.base64ToArrayBuffer(payload.ciphertext);\n const nonce = this.base64ToArrayBuffer(payload.nonce);\n\n const decryptedBuffer = await crypto.subtle.decrypt(\n {\n name: 'AES-GCM',\n iv: new Uint8Array(nonce)\n },\n sessionKey.rawKey,\n ciphertext\n );\n\n const decoder = new TextDecoder();\n return decoder.decode(decryptedBuffer);\n }\n\n /**\n * Exports an AES-GCM key and wraps (encrypts) it with a recipient's RSA public key.\n *\n * @param aesKey AES CryptoKey to wrap\n * @param recipientPublicKey Base64-encoded recipient RSA public key\n */\n async wrapSessionKey(\n aesKey: CryptoKey,\n recipientPublicKey: string\n ): Promise<string> {\n if (!this.cryptoProvider) {\n throw new Error(\n 'KOJINX_CRYPTO_PROVIDER is not provided. Cannot wrap session keys.'\n );\n }\n\n const rawKeyBytes = await crypto.subtle.exportKey('raw', aesKey);\n const rawKeyBase64 = this.arrayBufferToBase64(rawKeyBytes);\n\n // Asymmetrically encrypt using host RSA provider\n return this.cryptoProvider.encrypt(recipientPublicKey, rawKeyBase64);\n }\n\n /**\n * Unwraps (decrypts) an RSA-encrypted AES raw key and imports it back into a W3C CryptoKey.\n *\n * @param encryptedAesKeyBase64 RSA-encrypted AES raw key in base64 format\n */\n async unwrapSessionKey(encryptedAesKeyBase64: string): Promise<CryptoKey> {\n if (!this.cryptoProvider) {\n throw new Error(\n 'KOJINX_CRYPTO_PROVIDER is not provided. Cannot unwrap session keys.'\n );\n }\n\n // Asymmetrically decrypt using local private key\n const rawKeyBase64 = await this.cryptoProvider.decrypt(\n encryptedAesKeyBase64\n );\n const rawKeyBuffer = this.base64ToArrayBuffer(rawKeyBase64);\n\n return crypto.subtle.importKey(\n 'raw',\n rawKeyBuffer,\n {\n name: 'AES-GCM',\n length: 256\n },\n true,\n ['encrypt', 'decrypt']\n );\n }\n\n /**\n * Exports a CryptoKey to base64 raw bytes.\n */\n async exportRawKeyBase64(key: CryptoKey): Promise<string> {\n const raw = await crypto.subtle.exportKey('raw', key);\n return this.arrayBufferToBase64(raw);\n }\n\n /**\n * Imports a base64 raw key string into a CryptoKey.\n */\n async importRawKeyBase64(base64: string): Promise<CryptoKey> {\n const buffer = this.base64ToArrayBuffer(base64);\n return crypto.subtle.importKey(\n 'raw',\n buffer,\n {\n name: 'AES-GCM',\n length: 256\n },\n true,\n ['encrypt', 'decrypt']\n );\n }\n\n /**\n * Encodes ArrayBuffer to Base64 string.\n */\n arrayBufferToBase64(buffer: ArrayBuffer | Uint8Array): string {\n const bytes = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);\n let binary = '';\n for (let i = 0; i < bytes.byteLength; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary);\n }\n\n /**\n * Decodes Base64 string to ArrayBuffer.\n */\n base64ToArrayBuffer(base64: string): ArrayBuffer {\n const binary = atob(base64.trim());\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes.buffer;\n }\n}\n","import { Injectable, inject } from '@angular/core';\nimport {\n ConversationSessionKey,\n KojinxFetchFunction,\n KojinxStorageProvider,\n MemberPublicKey,\n SessionKeyExchangePayload\n} from '../models/crypto.models';\nimport {\n KOJINX_API_BASE_URL,\n KOJINX_AUTH_TOKEN,\n KOJINX_CRYPTO_STORAGE,\n KOJINX_HTTP_FETCH\n} from '../tokens';\nimport { E2EChatCryptoService } from './e2e-chat-crypto.service';\n\n/** 30 days in milliseconds for maximum session key lifetime */\nconst THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1000;\n\n/**\n * Manages the lifecycle, caching, negotiation, and rotation of conversation session keys.\n */\n@Injectable({\n providedIn: 'root'\n})\nexport class SessionKeyManagerService {\n private cryptoService = inject(E2EChatCryptoService);\n private httpFetch = inject<KojinxFetchFunction>(KOJINX_HTTP_FETCH, {\n optional: true\n });\n private getApiUrl = inject<() => string>(KOJINX_API_BASE_URL, {\n optional: true\n });\n private getAuthToken = inject<() => Promise<string | null>>(\n KOJINX_AUTH_TOKEN,\n { optional: true }\n );\n private storage = inject<KojinxStorageProvider>(KOJINX_CRYPTO_STORAGE, {\n optional: true\n });\n\n /** Storage provider falling back to browser localStorage if not explicitly injected */\n private get storageProvider(): KojinxStorageProvider | null {\n if (this.storage) return this.storage;\n if (typeof localStorage !== 'undefined') {\n return {\n getItem: (k: string) => Promise.resolve(localStorage.getItem(k)),\n setItem: (k: string, v: string) => Promise.resolve(localStorage.setItem(k, v)),\n removeItem: (k: string) => Promise.resolve(localStorage.removeItem(k))\n };\n }\n return null;\n }\n\n /** Fast in-memory cache mapped by conversationId */\n private activeKeysByConversation = new Map<string, ConversationSessionKey>();\n /** Fast in-memory cache mapped by sessionKeyId (for historic messages) */\n private keysById = new Map<string, ConversationSessionKey>();\n /** Deduplicates in-flight server requests for the same conversation ID */\n private inFlightFetches = new Map<string, Promise<ConversationSessionKey | null>>();\n\n /**\n * Retrieves or creates an active session key for a 1:1 Direct Message conversation.\n *\n * @param localUserId Current user's ID\n * @param remoteUserId Remote contact's ID\n * @param remotePublicKey Base64 RSA public key of remote contact\n * @param localPublicKey Base64 RSA public key of local user\n */\n async getOrCreateDmSessionKey(\n localUserId: string,\n remoteUserId: string,\n remotePublicKey: string,\n localPublicKey: string\n ): Promise<ConversationSessionKey> {\n const conversationId = this.buildDmConversationId(\n localUserId,\n remoteUserId\n );\n\n // 1. Check in-memory cache\n const cached = this.activeKeysByConversation.get(conversationId);\n if (cached && !this.isKeyExpired(cached)) {\n return cached;\n }\n\n // 2. Attempt fetching from server if HTTP fetch is provided\n if (this.httpFetch && this.getApiUrl && this.getAuthToken) {\n const serverKey = await this.fetchSessionKeyFromServer(\n conversationId,\n 'dm'\n );\n if (serverKey) {\n this.cacheKey(serverKey);\n return serverKey;\n }\n }\n\n // 3. Create fresh session key and distribute\n return this.createAndDistributeSessionKey({\n type: 'dm',\n conversationId,\n generation: cached ? cached.generation + 1 : 1,\n members: [\n { userId: localUserId, publicKey: localPublicKey },\n { userId: remoteUserId, publicKey: remotePublicKey }\n ]\n });\n }\n\n /**\n * Retrieves or creates an active session key for a Native Channel or Group.\n *\n * @param channelId Unique channel ID\n * @param members List of all workspace/channel members with their public keys\n */\n async getOrCreateChannelSessionKey(\n channelId: string,\n members: MemberPublicKey[]\n ): Promise<ConversationSessionKey> {\n const cached = this.activeKeysByConversation.get(channelId);\n if (cached && !this.isKeyExpired(cached)) {\n return cached;\n }\n\n if (this.httpFetch && this.getApiUrl && this.getAuthToken) {\n const serverKey = await this.fetchSessionKeyFromServer(\n channelId,\n 'channel'\n );\n if (serverKey) {\n this.cacheKey(serverKey);\n return serverKey;\n }\n }\n\n return this.createAndDistributeSessionKey({\n type: 'channel',\n conversationId: channelId,\n generation: cached ? cached.generation + 1 : 1,\n members\n });\n }\n\n /**\n * Retrieves a session key by its specific sessionKeyId (used for decrypting historic messages).\n */\n async getSessionKeyById(\n sessionKeyId: string\n ): Promise<ConversationSessionKey | null> {\n const cached = this.keysById.get(sessionKeyId);\n if (cached) return cached;\n\n // Check local storage\n const sp = this.storageProvider;\n if (sp) {\n const stored = await sp.getItem(\n `kojinx_sk_${sessionKeyId}`\n );\n if (stored) {\n try {\n const parsed = JSON.parse(stored);\n const rawKey = await this.cryptoService.importRawKeyBase64(\n parsed.rawKeyBase64\n );\n const key: ConversationSessionKey = {\n id: parsed.id,\n type: parsed.type,\n conversationId: parsed.conversationId,\n rawKey,\n generation: parsed.generation,\n createdAt: parsed.createdAt\n };\n this.cacheKey(key);\n return key;\n } catch {\n // ignore corrupted local key\n }\n }\n }\n\n return null;\n }\n\n /**\n * Generates a new AES session key, wraps it for all members, and uploads to the server.\n */\n async createAndDistributeSessionKey(params: {\n type: 'dm' | 'channel' | 'group';\n conversationId: string;\n generation: number;\n members: MemberPublicKey[];\n }): Promise<ConversationSessionKey> {\n const rawAesKey = await this.cryptoService.generateAesKey();\n const sessionKeyId = this.generateUuid();\n\n const memberPayloads: Record<string, string> = {};\n for (const member of params.members) {\n if (member.publicKey) {\n memberPayloads[member.userId] =\n await this.cryptoService.wrapSessionKey(\n rawAesKey,\n member.publicKey\n );\n }\n }\n\n const sessionKey: ConversationSessionKey = {\n id: sessionKeyId,\n type: params.type,\n conversationId: params.conversationId,\n rawKey: rawAesKey,\n generation: params.generation,\n createdAt: Date.now()\n };\n\n // Store in memory\n this.cacheKey(sessionKey);\n await this.persistKeyLocally(sessionKey);\n\n // Upload to server if online\n if (this.httpFetch && this.getApiUrl && this.getAuthToken) {\n const token = await this.getAuthToken();\n const apiUrl = this.getApiUrl();\n const payload: SessionKeyExchangePayload = {\n sessionKeyId,\n conversationType: params.type,\n conversationId: params.conversationId,\n generation: params.generation,\n memberPayloads\n };\n\n try {\n await this.httpFetch(`${apiUrl}/api/e2e/session-keys`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify(payload)\n });\n } catch (err) {\n console.warn(\n '[SessionKeyManager] Failed to sync session key to server:',\n err\n );\n }\n }\n\n return sessionKey;\n }\n\n /**\n * Helper to check if a key has exceeded the 30-day rotation threshold.\n */\n isKeyExpired(key: ConversationSessionKey): boolean {\n return Date.now() - key.createdAt > THIRTY_DAYS_MS;\n }\n\n /**\n * Builds a deterministic conversation identifier for 1:1 DMs (lexicographical sort).\n */\n buildDmConversationId(userIdA: string, userIdB: string): string {\n return [userIdA, userIdB].sort().join('_');\n }\n\n /**\n * Caches a session key in memory.\n */\n private cacheKey(key: ConversationSessionKey): void {\n this.activeKeysByConversation.set(key.conversationId, key);\n this.keysById.set(key.id, key);\n }\n\n /**\n * Persists an unwrapped session key to local storage for fast startup.\n */\n private async persistKeyLocally(\n key: ConversationSessionKey\n ): Promise<void> {\n const sp = this.storageProvider;\n if (!sp) return;\n try {\n const rawKeyBase64 = await this.cryptoService.exportRawKeyBase64(\n key.rawKey\n );\n const data = JSON.stringify({\n id: key.id,\n type: key.type,\n conversationId: key.conversationId,\n rawKeyBase64,\n generation: key.generation,\n createdAt: key.createdAt\n });\n await sp.setItem(`kojinx_sk_${key.id}`, data);\n } catch {\n // ignore persistence error\n }\n }\n\n /**\n * Fetches latest session key from server and unwraps it locally with deduplication.\n */\n private async fetchSessionKeyFromServer(\n conversationId: string,\n type: 'dm' | 'channel' | 'group'\n ): Promise<ConversationSessionKey | null> {\n if (!this.httpFetch || !this.getApiUrl || !this.getAuthToken) return null;\n\n const existingPromise = this.inFlightFetches.get(conversationId);\n if (existingPromise) {\n return existingPromise;\n }\n\n const fetchPromise = (async () => {\n try {\n const token = await this.getAuthToken!();\n const apiUrl = this.getApiUrl!();\n const res = await this.httpFetch!<{\n sessionKeyId: string;\n generation: number;\n encryptedKey: string;\n createdAt?: string;\n }>(`${apiUrl}/api/e2e/session-keys/${conversationId}`, {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${token}`\n }\n });\n\n if (res.status === 200 && res.data?.encryptedKey) {\n const rawKey = await this.cryptoService.unwrapSessionKey(\n res.data.encryptedKey\n );\n const key: ConversationSessionKey = {\n id: res.data.sessionKeyId,\n type,\n conversationId,\n rawKey,\n generation: res.data.generation || 1,\n createdAt: res.data.createdAt\n ? new Date(res.data.createdAt).getTime()\n : Date.now()\n };\n this.cacheKey(key);\n await this.persistKeyLocally(key);\n return key;\n }\n } catch {\n // Not found or network error\n } finally {\n this.inFlightFetches.delete(conversationId);\n }\n return null;\n })();\n\n this.inFlightFetches.set(conversationId, fetchPromise);\n return fetchPromise;\n }\n\n /**\n * Generates a random UUID (W3C standard or fallback).\n */\n private generateUuid(): string {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {\n const r = (Math.random() * 16) | 0;\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n }\n}\n","/*\n * Public API Surface of @kojinx/collab-crypto\n */\n\nexport * from './lib/tokens';\nexport * from './lib/models/crypto.models';\nexport * from './lib/utils/safety-number.utils';\nexport * from './lib/services/e2e-chat-crypto.service';\nexport * from './lib/services/session-key-manager.service';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -9,10 +9,14 @@ export declare class SessionKeyManagerService {
9
9
  private getApiUrl;
10
10
  private getAuthToken;
11
11
  private storage;
12
+ /** Storage provider falling back to browser localStorage if not explicitly injected */
13
+ private get storageProvider();
12
14
  /** Fast in-memory cache mapped by conversationId */
13
15
  private activeKeysByConversation;
14
16
  /** Fast in-memory cache mapped by sessionKeyId (for historic messages) */
15
17
  private keysById;
18
+ /** Deduplicates in-flight server requests for the same conversation ID */
19
+ private inFlightFetches;
16
20
  /**
17
21
  * Retrieves or creates an active session key for a 1:1 Direct Message conversation.
18
22
  *
@@ -59,7 +63,7 @@ export declare class SessionKeyManagerService {
59
63
  */
60
64
  private persistKeyLocally;
61
65
  /**
62
- * Fetches latest session key from server and unwraps it locally.
66
+ * Fetches latest session key from server and unwraps it locally with deduplication.
63
67
  */
64
68
  private fetchSessionKeyFromServer;
65
69
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kojinx/collab-crypto",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "Kojinx End-to-End Encryption Trust Module for Collaboration Hub",
5
5
  "peerDependencies": {
6
6
  "@angular/common": "^17.3.0",