@kojinx/collab-crypto 0.0.2

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.
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Generated bundle index. Do not edit.
3
+ */
4
+ export * from './public-api';
5
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoia29qaW54LWNvbGxhYi1jcnlwdG8uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9wcm9qZWN0cy9rb2ppbngvY29sbGFiLWNyeXB0by9zcmMva29qaW54LWNvbGxhYi1jcnlwdG8udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0dBRUc7QUFFSCxjQUFjLGNBQWMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogR2VuZXJhdGVkIGJ1bmRsZSBpbmRleC4gRG8gbm90IGVkaXQuXG4gKi9cblxuZXhwb3J0ICogZnJvbSAnLi9wdWJsaWMtYXBpJztcbiJdfQ==
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3J5cHRvLm1vZGVscy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL2tvamlueC9jb2xsYWItY3J5cHRvL3NyYy9saWIvbW9kZWxzL2NyeXB0by5tb2RlbHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogRW5jcnlwdGVkIG1lc3NhZ2UgcGF5bG9hZCBmb3JtYXQgdHJhbnNtaXR0ZWQgb3ZlciBIVFRQL1NTRSBhbmQgc3RvcmVkIGluIGRhdGFiYXNlLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIEVuY3J5cHRlZE1lc3NhZ2VQYXlsb2FkIHtcbiAgLyoqIEJhc2U2NC1lbmNvZGVkIEFFUy0yNTYtR0NNIGNpcGhlcnRleHQgKi9cbiAgY2lwaGVydGV4dDogc3RyaW5nO1xuICAvKiogQmFzZTY0LWVuY29kZWQgMTItYnl0ZSAoOTYtYml0KSBJbml0aWFsaXphdGlvbiBWZWN0b3IgKE5vbmNlKSAqL1xuICBub25jZTogc3RyaW5nO1xuICAvKiogVVVJRCBvZiB0aGUgU2Vzc2lvbiBLZXkgdXNlZCB0byBlbmNyeXB0IHRoaXMgbWVzc2FnZSAqL1xuICBzZXNzaW9uS2V5SWQ6IHN0cmluZztcbiAgLyoqIEdlbmVyYXRpb24gbnVtYmVyIG9mIHRoZSBTZXNzaW9uIEtleSAoaW5jcmVtZW50ZWQgZHVyaW5nIGtleSByb3RhdGlvbikgKi9cbiAgZ2VuZXJhdGlvbjogbnVtYmVyO1xufVxuXG4vKipcbiAqIEluLW1lbW9yeSBhY3RpdmUgU2Vzc2lvbiBLZXkgaG9sZGluZyBzdGFuZGFyZCBXM0MgQ3J5cHRvS2V5LlxuICovXG5leHBvcnQgaW50ZXJmYWNlIENvbnZlcnNhdGlvblNlc3Npb25LZXkge1xuICAvKiogVW5pcXVlIFVVSUQgaWRlbnRpZmllciBmb3IgdGhpcyBzZXNzaW9uIGtleSAqL1xuICBpZDogc3RyaW5nO1xuICAvKiogQ29udmVyc2F0aW9uIHNjb3BlICovXG4gIHR5cGU6ICdkbScgfCAnY2hhbm5lbCcgfCAnZ3JvdXAnO1xuICAvKiogVGFyZ2V0IGNvbnZlcnNhdGlvbiBJRCAocmVjaXBpZW50SWQgZm9yIDE6MSBETSwgY2hhbm5lbElkIGZvciBjaGFubmVscywgZ3JvdXBJZCBmb3IgZ3JvdXBzKSAqL1xuICBjb252ZXJzYXRpb25JZDogc3RyaW5nO1xuICAvKiogVzNDIFN1YnRsZUNyeXB0byBBRVMtR0NNIDI1Ni1iaXQgS2V5ICovXG4gIHJhd0tleTogQ3J5cHRvS2V5O1xuICAvKiogR2VuZXJhdGlvbiBpbmRleCAoc3RhcnRzIGF0IDEsIGluY3JlbWVudGVkIHVwb24gcm90YXRpb24pICovXG4gIGdlbmVyYXRpb246IG51bWJlcjtcbiAgLyoqIENyZWF0aW9uIGVwb2NoIHRpbWVzdGFtcCBpbiBtaWxsaXNlY29uZHMgKi9cbiAgY3JlYXRlZEF0OiBudW1iZXI7XG59XG5cbi8qKlxuICogV2lyZS1mb3JtYXQgZm9yIHN0b3JpbmcgYW5kIHN5bmNocm9uaXppbmcgZW5jcnlwdGVkIHNlc3Npb24ga2V5cyBhY3Jvc3MgbWVtYmVycy5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBTZXNzaW9uS2V5RXhjaGFuZ2VQYXlsb2FkIHtcbiAgc2Vzc2lvbktleUlkOiBzdHJpbmc7XG4gIGNvbnZlcnNhdGlvblR5cGU6ICdkbScgfCAnY2hhbm5lbCcgfCAnZ3JvdXAnO1xuICBjb252ZXJzYXRpb25JZDogc3RyaW5nO1xuICBnZW5lcmF0aW9uOiBudW1iZXI7XG4gIC8qKiBNYXAgb2YgVXNlcklkIC0+IFJTQS1PQUVQIEJhc2U2NCBFbmNyeXB0ZWQgQUVTIFJhdyBLZXkgKi9cbiAgbWVtYmVyUGF5bG9hZHM6IFJlY29yZDxzdHJpbmcsIHN0cmluZz47XG4gIGNyZWF0ZWRBdD86IHN0cmluZztcbiAgY3JlYXRlZEJ5Pzogc3RyaW5nO1xufVxuXG4vKipcbiAqIE1lbWJlciBwdWJsaWMga2V5IGVudHJ5IHJldHVybmVkIGJ5IHB1YmxpYyBrZXkgZGlyZWN0b3J5LlxuICovXG5leHBvcnQgaW50ZXJmYWNlIE1lbWJlclB1YmxpY0tleSB7XG4gIHVzZXJJZDogc3RyaW5nO1xuICBwdWJsaWNLZXk6IHN0cmluZztcbn1cblxuLyoqXG4gKiBTYWZldHkgTnVtYmVyIHZlcmlmaWNhdGlvbiBtb2RlbCAoU2lnbmFsLXN0eWxlKS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBTYWZldHlOdW1iZXIge1xuICAvKiogUmF3IDYwLWNoYXJhY3RlciBoZXhhZGVjaW1hbCBkaWdlc3QgKi9cbiAgcmF3OiBzdHJpbmc7XG4gIC8qKiBTdGFuZGFyZGl6ZWQgZm9ybWF0dGVkIGRpc3BsYXkgaW4gNiBibG9ja3Mgb2YgNSBkZWNpbWFsIGRpZ2l0cyAqL1xuICBmb3JtYXR0ZWQ6IHN0cmluZztcbiAgLyoqIEZpbmdlcnByaW50IGNvbXBvbmVudCBmb3IgdGhlIGxvY2FsIHVzZXIgKi9cbiAgbG9jYWxGaW5nZXJwcmludDogc3RyaW5nO1xuICAvKiogRmluZ2VycHJpbnQgY29tcG9uZW50IGZvciB0aGUgcmVtb3RlIHVzZXIgKi9cbiAgcmVtb3RlRmluZ2VycHJpbnQ6IHN0cmluZztcbn1cblxuLyoqXG4gKiBDcnlwdG9ncmFwaHkgcHJvdmlkZXIgYWJzdHJhY3Rpb24gaW50ZXJmYWNlIChSU0EtMjA0OCBPQUVQKS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBLb2ppbnhDcnlwdG9Qcm92aWRlciB7XG4gIC8qKiBSZXR1cm5zIHRoZSBiYXNlNjQtZW5jb2RlZCBSU0EgcHVibGljIGtleSAqL1xuICBnZXRQdWJsaWNLZXkoKTogUHJvbWlzZTxzdHJpbmc+O1xuICAvKiogQXN5bW1ldHJpY2FsbHkgZW5jcnlwdHMgcGxhaW50ZXh0IHVzaW5nIHRhcmdldCBiYXNlNjQgUlNBIHB1YmxpYyBrZXkgKi9cbiAgZW5jcnlwdChwdWJsaWNLZXlCYXNlNjQ6IHN0cmluZywgcGxhaW50ZXh0OiBzdHJpbmcpOiBQcm9taXNlPHN0cmluZz47XG4gIC8qKiBBc3ltbWV0cmljYWxseSBkZWNyeXB0cyBjaXBoZXJ0ZXh0IHVzaW5nIGxvY2FsIHByaXZhdGUga2V5ICovXG4gIGRlY3J5cHQoY2lwaGVydGV4dEJhc2U2NDogc3RyaW5nKTogUHJvbWlzZTxzdHJpbmc+O1xufVxuXG4vKipcbiAqIENsaWVudC1hZ25vc3RpYyBmZXRjaCBvcHRpb25zLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIEtvamlueEZldGNoT3B0aW9ucyB7XG4gIG1ldGhvZD86IHN0cmluZztcbiAgaGVhZGVycz86IFJlY29yZDxzdHJpbmcsIHN0cmluZz47XG4gIGJvZHk/OiBzdHJpbmc7XG59XG5cbi8qKlxuICogQ2xpZW50LWFnbm9zdGljIGZldGNoIHJlc3BvbnNlLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIEtvamlueEZldGNoUmVzcG9uc2U8VCA9IGFueT4ge1xuICBzdGF0dXM6IG51bWJlcjtcbiAgZGF0YTogVDtcbiAgaGVhZGVycz86IFJlY29yZDxzdHJpbmcsIHN0cmluZz47XG59XG5cbmV4cG9ydCB0eXBlIEtvamlueEZldGNoRnVuY3Rpb24gPSA8VCA9IGFueT4oXG4gIHVybDogc3RyaW5nLFxuICBvcHRpb25zPzogS29qaW54RmV0Y2hPcHRpb25zXG4pID0+IFByb21pc2U8S29qaW54RmV0Y2hSZXNwb25zZTxUPj47XG5cbi8qKlxuICogTG9jYWwgc3RvcmFnZSBpbnRlcmZhY2UgZm9yIHNlc3Npb24ga2V5IGNhY2hpbmcuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgS29qaW54U3RvcmFnZVByb3ZpZGVyIHtcbiAgZ2V0SXRlbShrZXk6IHN0cmluZyk6IFByb21pc2U8c3RyaW5nIHwgbnVsbD47XG4gIHNldEl0ZW0oa2V5OiBzdHJpbmcsIHZhbHVlOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+O1xuICByZW1vdmVJdGVtKGtleTogc3RyaW5nKTogUHJvbWlzZTx2b2lkPjtcbn1cbiJdfQ==
@@ -0,0 +1,148 @@
1
+ import { Injectable, inject } from '@angular/core';
2
+ import { KOJINX_CRYPTO_PROVIDER } from '../tokens';
3
+ import * as i0 from "@angular/core";
4
+ /**
5
+ * Pure End-to-End Encryption cryptographic service.
6
+ * Handles AES-256-GCM message encryption/decryption and RSA key wrapping/unwrapping.
7
+ */
8
+ export class E2EChatCryptoService {
9
+ constructor() {
10
+ this.cryptoProvider = inject(KOJINX_CRYPTO_PROVIDER, {
11
+ optional: true
12
+ });
13
+ }
14
+ /**
15
+ * Generates a new cryptographically secure 256-bit AES-GCM session key.
16
+ */
17
+ async generateAesKey() {
18
+ return crypto.subtle.generateKey({
19
+ name: 'AES-GCM',
20
+ length: 256
21
+ }, true, // extractable for RSA wrapping & secure export
22
+ ['encrypt', 'decrypt']);
23
+ }
24
+ /**
25
+ * Encrypts plaintext string using AES-256-GCM with a fresh 12-byte (96-bit) nonce.
26
+ *
27
+ * @param sessionKey In-memory active session key
28
+ * @param plaintext Plaintext message content
29
+ */
30
+ async encryptMessage(sessionKey, plaintext) {
31
+ if (!sessionKey || !sessionKey.rawKey) {
32
+ throw new Error('Invalid session key provided for encryption.');
33
+ }
34
+ // Standard 12-byte initialization vector for AES-GCM
35
+ const nonce = crypto.getRandomValues(new Uint8Array(12));
36
+ const encoder = new TextEncoder();
37
+ const encodedPlaintext = encoder.encode(plaintext);
38
+ const ciphertextBuffer = await crypto.subtle.encrypt({
39
+ name: 'AES-GCM',
40
+ iv: nonce
41
+ }, sessionKey.rawKey, encodedPlaintext);
42
+ return {
43
+ ciphertext: this.arrayBufferToBase64(ciphertextBuffer),
44
+ nonce: this.arrayBufferToBase64(nonce.buffer),
45
+ sessionKeyId: sessionKey.id,
46
+ generation: sessionKey.generation
47
+ };
48
+ }
49
+ /**
50
+ * Decrypts an EncryptedMessagePayload using AES-256-GCM.
51
+ *
52
+ * @param sessionKey In-memory session key corresponding to payload.sessionKeyId
53
+ * @param payload Encrypted payload containing ciphertext and nonce
54
+ */
55
+ async decryptMessage(sessionKey, payload) {
56
+ if (!sessionKey || !sessionKey.rawKey) {
57
+ throw new Error('Invalid session key provided for decryption.');
58
+ }
59
+ const ciphertext = this.base64ToArrayBuffer(payload.ciphertext);
60
+ const nonce = this.base64ToArrayBuffer(payload.nonce);
61
+ const decryptedBuffer = await crypto.subtle.decrypt({
62
+ name: 'AES-GCM',
63
+ iv: new Uint8Array(nonce)
64
+ }, sessionKey.rawKey, ciphertext);
65
+ const decoder = new TextDecoder();
66
+ return decoder.decode(decryptedBuffer);
67
+ }
68
+ /**
69
+ * Exports an AES-GCM key and wraps (encrypts) it with a recipient's RSA public key.
70
+ *
71
+ * @param aesKey AES CryptoKey to wrap
72
+ * @param recipientPublicKey Base64-encoded recipient RSA public key
73
+ */
74
+ async wrapSessionKey(aesKey, recipientPublicKey) {
75
+ if (!this.cryptoProvider) {
76
+ throw new Error('KOJINX_CRYPTO_PROVIDER is not provided. Cannot wrap session keys.');
77
+ }
78
+ const rawKeyBytes = await crypto.subtle.exportKey('raw', aesKey);
79
+ const rawKeyBase64 = this.arrayBufferToBase64(rawKeyBytes);
80
+ // Asymmetrically encrypt using host RSA provider
81
+ return this.cryptoProvider.encrypt(recipientPublicKey, rawKeyBase64);
82
+ }
83
+ /**
84
+ * Unwraps (decrypts) an RSA-encrypted AES raw key and imports it back into a W3C CryptoKey.
85
+ *
86
+ * @param encryptedAesKeyBase64 RSA-encrypted AES raw key in base64 format
87
+ */
88
+ async unwrapSessionKey(encryptedAesKeyBase64) {
89
+ if (!this.cryptoProvider) {
90
+ throw new Error('KOJINX_CRYPTO_PROVIDER is not provided. Cannot unwrap session keys.');
91
+ }
92
+ // Asymmetrically decrypt using local private key
93
+ const rawKeyBase64 = await this.cryptoProvider.decrypt(encryptedAesKeyBase64);
94
+ const rawKeyBuffer = this.base64ToArrayBuffer(rawKeyBase64);
95
+ return crypto.subtle.importKey('raw', rawKeyBuffer, {
96
+ name: 'AES-GCM',
97
+ length: 256
98
+ }, true, ['encrypt', 'decrypt']);
99
+ }
100
+ /**
101
+ * Exports a CryptoKey to base64 raw bytes.
102
+ */
103
+ async exportRawKeyBase64(key) {
104
+ const raw = await crypto.subtle.exportKey('raw', key);
105
+ return this.arrayBufferToBase64(raw);
106
+ }
107
+ /**
108
+ * Imports a base64 raw key string into a CryptoKey.
109
+ */
110
+ async importRawKeyBase64(base64) {
111
+ const buffer = this.base64ToArrayBuffer(base64);
112
+ return crypto.subtle.importKey('raw', buffer, {
113
+ name: 'AES-GCM',
114
+ length: 256
115
+ }, true, ['encrypt', 'decrypt']);
116
+ }
117
+ /**
118
+ * Encodes ArrayBuffer to Base64 string.
119
+ */
120
+ arrayBufferToBase64(buffer) {
121
+ const bytes = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);
122
+ let binary = '';
123
+ for (let i = 0; i < bytes.byteLength; i++) {
124
+ binary += String.fromCharCode(bytes[i]);
125
+ }
126
+ return btoa(binary);
127
+ }
128
+ /**
129
+ * Decodes Base64 string to ArrayBuffer.
130
+ */
131
+ base64ToArrayBuffer(base64) {
132
+ const binary = atob(base64.trim());
133
+ const bytes = new Uint8Array(binary.length);
134
+ for (let i = 0; i < binary.length; i++) {
135
+ bytes[i] = binary.charCodeAt(i);
136
+ }
137
+ return bytes.buffer;
138
+ }
139
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: E2EChatCryptoService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
140
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: E2EChatCryptoService, providedIn: 'root' }); }
141
+ }
142
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: E2EChatCryptoService, decorators: [{
143
+ type: Injectable,
144
+ args: [{
145
+ providedIn: 'root'
146
+ }]
147
+ }] });
148
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZTJlLWNoYXQtY3J5cHRvLnNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9rb2ppbngvY29sbGFiLWNyeXB0by9zcmMvbGliL3NlcnZpY2VzL2UyZS1jaGF0LWNyeXB0by5zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBTW5ELE9BQU8sRUFBRSxzQkFBc0IsRUFBRSxNQUFNLFdBQVcsQ0FBQzs7QUFFbkQ7OztHQUdHO0FBSUgsTUFBTSxPQUFPLG9CQUFvQjtJQUhqQztRQUlVLG1CQUFjLEdBQUcsTUFBTSxDQUF1QixzQkFBc0IsRUFBRTtZQUM1RSxRQUFRLEVBQUUsSUFBSTtTQUNmLENBQUMsQ0FBQztLQXVMSjtJQXJMQzs7T0FFRztJQUNILEtBQUssQ0FBQyxjQUFjO1FBQ2xCLE9BQU8sTUFBTSxDQUFDLE1BQU0sQ0FBQyxXQUFXLENBQzlCO1lBQ0UsSUFBSSxFQUFFLFNBQVM7WUFDZixNQUFNLEVBQUUsR0FBRztTQUNaLEVBQ0QsSUFBSSxFQUFFLCtDQUErQztRQUNyRCxDQUFDLFNBQVMsRUFBRSxTQUFTLENBQUMsQ0FDdkIsQ0FBQztJQUNKLENBQUM7SUFFRDs7Ozs7T0FLRztJQUNILEtBQUssQ0FBQyxjQUFjLENBQ2xCLFVBQWtDLEVBQ2xDLFNBQWlCO1FBRWpCLElBQUksQ0FBQyxVQUFVLElBQUksQ0FBQyxVQUFVLENBQUMsTUFBTSxFQUFFLENBQUM7WUFDdEMsTUFBTSxJQUFJLEtBQUssQ0FBQyw4Q0FBOEMsQ0FBQyxDQUFDO1FBQ2xFLENBQUM7UUFFRCxxREFBcUQ7UUFDckQsTUFBTSxLQUFLLEdBQUcsTUFBTSxDQUFDLGVBQWUsQ0FBQyxJQUFJLFVBQVUsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO1FBQ3pELE1BQU0sT0FBTyxHQUFHLElBQUksV0FBVyxFQUFFLENBQUM7UUFDbEMsTUFBTSxnQkFBZ0IsR0FBRyxPQUFPLENBQUMsTUFBTSxDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBRW5ELE1BQU0sZ0JBQWdCLEdBQUcsTUFBTSxNQUFNLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FDbEQ7WUFDRSxJQUFJLEVBQUUsU0FBUztZQUNmLEVBQUUsRUFBRSxLQUFLO1NBQ1YsRUFDRCxVQUFVLENBQUMsTUFBTSxFQUNqQixnQkFBZ0IsQ0FDakIsQ0FBQztRQUVGLE9BQU87WUFDTCxVQUFVLEVBQUUsSUFBSSxDQUFDLG1CQUFtQixDQUFDLGdCQUFnQixDQUFDO1lBQ3RELEtBQUssRUFBRSxJQUFJLENBQUMsbUJBQW1CLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQztZQUM3QyxZQUFZLEVBQUUsVUFBVSxDQUFDLEVBQUU7WUFDM0IsVUFBVSxFQUFFLFVBQVUsQ0FBQyxVQUFVO1NBQ2xDLENBQUM7SUFDSixDQUFDO0lBRUQ7Ozs7O09BS0c7SUFDSCxLQUFLLENBQUMsY0FBYyxDQUNsQixVQUFrQyxFQUNsQyxPQUFnQztRQUVoQyxJQUFJLENBQUMsVUFBVSxJQUFJLENBQUMsVUFBVSxDQUFDLE1BQU0sRUFBRSxDQUFDO1lBQ3RDLE1BQU0sSUFBSSxLQUFLLENBQUMsOENBQThDLENBQUMsQ0FBQztRQUNsRSxDQUFDO1FBRUQsTUFBTSxVQUFVLEdBQUcsSUFBSSxDQUFDLG1CQUFtQixDQUFDLE9BQU8sQ0FBQyxVQUFVLENBQUMsQ0FBQztRQUNoRSxNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsbUJBQW1CLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBRXRELE1BQU0sZUFBZSxHQUFHLE1BQU0sTUFBTSxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQ2pEO1lBQ0UsSUFBSSxFQUFFLFNBQVM7WUFDZixFQUFFLEVBQUUsSUFBSSxVQUFVLENBQUMsS0FBSyxDQUFDO1NBQzFCLEVBQ0QsVUFBVSxDQUFDLE1BQU0sRUFDakIsVUFBVSxDQUNYLENBQUM7UUFFRixNQUFNLE9BQU8sR0FBRyxJQUFJLFdBQVcsRUFBRSxDQUFDO1FBQ2xDLE9BQU8sT0FBTyxDQUFDLE1BQU0sQ0FBQyxlQUFlLENBQUMsQ0FBQztJQUN6QyxDQUFDO0lBRUQ7Ozs7O09BS0c7SUFDSCxLQUFLLENBQUMsY0FBYyxDQUNsQixNQUFpQixFQUNqQixrQkFBMEI7UUFFMUIsSUFBSSxDQUFDLElBQUksQ0FBQyxjQUFjLEVBQUUsQ0FBQztZQUN6QixNQUFNLElBQUksS0FBSyxDQUNiLG1FQUFtRSxDQUNwRSxDQUFDO1FBQ0osQ0FBQztRQUVELE1BQU0sV0FBVyxHQUFHLE1BQU0sTUFBTSxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsS0FBSyxFQUFFLE1BQU0sQ0FBQyxDQUFDO1FBQ2pFLE1BQU0sWUFBWSxHQUFHLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxXQUFXLENBQUMsQ0FBQztRQUUzRCxpREFBaUQ7UUFDakQsT0FBTyxJQUFJLENBQUMsY0FBYyxDQUFDLE9BQU8sQ0FBQyxrQkFBa0IsRUFBRSxZQUFZLENBQUMsQ0FBQztJQUN2RSxDQUFDO0lBRUQ7Ozs7T0FJRztJQUNILEtBQUssQ0FBQyxnQkFBZ0IsQ0FBQyxxQkFBNkI7UUFDbEQsSUFBSSxDQUFDLElBQUksQ0FBQyxjQUFjLEVBQUUsQ0FBQztZQUN6QixNQUFNLElBQUksS0FBSyxDQUNiLHFFQUFxRSxDQUN0RSxDQUFDO1FBQ0osQ0FBQztRQUVELGlEQUFpRDtRQUNqRCxNQUFNLFlBQVksR0FBRyxNQUFNLElBQUksQ0FBQyxjQUFjLENBQUMsT0FBTyxDQUNwRCxxQkFBcUIsQ0FDdEIsQ0FBQztRQUNGLE1BQU0sWUFBWSxHQUFHLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxZQUFZLENBQUMsQ0FBQztRQUU1RCxPQUFPLE1BQU0sQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUM1QixLQUFLLEVBQ0wsWUFBWSxFQUNaO1lBQ0UsSUFBSSxFQUFFLFNBQVM7WUFDZixNQUFNLEVBQUUsR0FBRztTQUNaLEVBQ0QsSUFBSSxFQUNKLENBQUMsU0FBUyxFQUFFLFNBQVMsQ0FBQyxDQUN2QixDQUFDO0lBQ0osQ0FBQztJQUVEOztPQUVHO0lBQ0gsS0FBSyxDQUFDLGtCQUFrQixDQUFDLEdBQWM7UUFDckMsTUFBTSxHQUFHLEdBQUcsTUFBTSxNQUFNLENBQUMsTUFBTSxDQUFDLFNBQVMsQ0FBQyxLQUFLLEVBQUUsR0FBRyxDQUFDLENBQUM7UUFDdEQsT0FBTyxJQUFJLENBQUMsbUJBQW1CLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDdkMsQ0FBQztJQUVEOztPQUVHO0lBQ0gsS0FBSyxDQUFDLGtCQUFrQixDQUFDLE1BQWM7UUFDckMsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLG1CQUFtQixDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQ2hELE9BQU8sTUFBTSxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQzVCLEtBQUssRUFDTCxNQUFNLEVBQ047WUFDRSxJQUFJLEVBQUUsU0FBUztZQUNmLE1BQU0sRUFBRSxHQUFHO1NBQ1osRUFDRCxJQUFJLEVBQ0osQ0FBQyxTQUFTLEVBQUUsU0FBUyxDQUFDLENBQ3ZCLENBQUM7SUFDSixDQUFDO0lBRUQ7O09BRUc7SUFDSCxtQkFBbUIsQ0FBQyxNQUFnQztRQUNsRCxNQUFNLEtBQUssR0FBRyxNQUFNLFlBQVksVUFBVSxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLElBQUksVUFBVSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQzdFLElBQUksTUFBTSxHQUFHLEVBQUUsQ0FBQztRQUNoQixLQUFLLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsS0FBSyxDQUFDLFVBQVUsRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDO1lBQzFDLE1BQU0sSUFBSSxNQUFNLENBQUMsWUFBWSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQzFDLENBQUM7UUFDRCxPQUFPLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUN0QixDQUFDO0lBRUQ7O09BRUc7SUFDSCxtQkFBbUIsQ0FBQyxNQUFjO1FBQ2hDLE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQztRQUNuQyxNQUFNLEtBQUssR0FBRyxJQUFJLFVBQVUsQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDNUMsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLE1BQU0sQ0FBQyxNQUFNLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQztZQUN2QyxLQUFLLENBQUMsQ0FBQyxDQUFDLEdBQUcsTUFBTSxDQUFDLFVBQVUsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNsQyxDQUFDO1FBQ0QsT0FBTyxLQUFLLENBQUMsTUFBTSxDQUFDO0lBQ3RCLENBQUM7K0dBekxVLG9CQUFvQjttSEFBcEIsb0JBQW9CLGNBRm5CLE1BQU07OzRGQUVQLG9CQUFvQjtrQkFIaEMsVUFBVTttQkFBQztvQkFDVixVQUFVLEVBQUUsTUFBTTtpQkFDbkIiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBJbmplY3RhYmxlLCBpbmplY3QgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7XG4gIENvbnZlcnNhdGlvblNlc3Npb25LZXksXG4gIEVuY3J5cHRlZE1lc3NhZ2VQYXlsb2FkLFxuICBLb2ppbnhDcnlwdG9Qcm92aWRlclxufSBmcm9tICcuLi9tb2RlbHMvY3J5cHRvLm1vZGVscyc7XG5pbXBvcnQgeyBLT0pJTlhfQ1JZUFRPX1BST1ZJREVSIH0gZnJvbSAnLi4vdG9rZW5zJztcblxuLyoqXG4gKiBQdXJlIEVuZC10by1FbmQgRW5jcnlwdGlvbiBjcnlwdG9ncmFwaGljIHNlcnZpY2UuXG4gKiBIYW5kbGVzIEFFUy0yNTYtR0NNIG1lc3NhZ2UgZW5jcnlwdGlvbi9kZWNyeXB0aW9uIGFuZCBSU0Ega2V5IHdyYXBwaW5nL3Vud3JhcHBpbmcuXG4gKi9cbkBJbmplY3RhYmxlKHtcbiAgcHJvdmlkZWRJbjogJ3Jvb3QnXG59KVxuZXhwb3J0IGNsYXNzIEUyRUNoYXRDcnlwdG9TZXJ2aWNlIHtcbiAgcHJpdmF0ZSBjcnlwdG9Qcm92aWRlciA9IGluamVjdDxLb2ppbnhDcnlwdG9Qcm92aWRlcj4oS09KSU5YX0NSWVBUT19QUk9WSURFUiwge1xuICAgIG9wdGlvbmFsOiB0cnVlXG4gIH0pO1xuXG4gIC8qKlxuICAgKiBHZW5lcmF0ZXMgYSBuZXcgY3J5cHRvZ3JhcGhpY2FsbHkgc2VjdXJlIDI1Ni1iaXQgQUVTLUdDTSBzZXNzaW9uIGtleS5cbiAgICovXG4gIGFzeW5jIGdlbmVyYXRlQWVzS2V5KCk6IFByb21pc2U8Q3J5cHRvS2V5PiB7XG4gICAgcmV0dXJuIGNyeXB0by5zdWJ0bGUuZ2VuZXJhdGVLZXkoXG4gICAgICB7XG4gICAgICAgIG5hbWU6ICdBRVMtR0NNJyxcbiAgICAgICAgbGVuZ3RoOiAyNTZcbiAgICAgIH0sXG4gICAgICB0cnVlLCAvLyBleHRyYWN0YWJsZSBmb3IgUlNBIHdyYXBwaW5nICYgc2VjdXJlIGV4cG9ydFxuICAgICAgWydlbmNyeXB0JywgJ2RlY3J5cHQnXVxuICAgICk7XG4gIH1cblxuICAvKipcbiAgICogRW5jcnlwdHMgcGxhaW50ZXh0IHN0cmluZyB1c2luZyBBRVMtMjU2LUdDTSB3aXRoIGEgZnJlc2ggMTItYnl0ZSAoOTYtYml0KSBub25jZS5cbiAgICpcbiAgICogQHBhcmFtIHNlc3Npb25LZXkgSW4tbWVtb3J5IGFjdGl2ZSBzZXNzaW9uIGtleVxuICAgKiBAcGFyYW0gcGxhaW50ZXh0IFBsYWludGV4dCBtZXNzYWdlIGNvbnRlbnRcbiAgICovXG4gIGFzeW5jIGVuY3J5cHRNZXNzYWdlKFxuICAgIHNlc3Npb25LZXk6IENvbnZlcnNhdGlvblNlc3Npb25LZXksXG4gICAgcGxhaW50ZXh0OiBzdHJpbmdcbiAgKTogUHJvbWlzZTxFbmNyeXB0ZWRNZXNzYWdlUGF5bG9hZD4ge1xuICAgIGlmICghc2Vzc2lvbktleSB8fCAhc2Vzc2lvbktleS5yYXdLZXkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignSW52YWxpZCBzZXNzaW9uIGtleSBwcm92aWRlZCBmb3IgZW5jcnlwdGlvbi4nKTtcbiAgICB9XG5cbiAgICAvLyBTdGFuZGFyZCAxMi1ieXRlIGluaXRpYWxpemF0aW9uIHZlY3RvciBmb3IgQUVTLUdDTVxuICAgIGNvbnN0IG5vbmNlID0gY3J5cHRvLmdldFJhbmRvbVZhbHVlcyhuZXcgVWludDhBcnJheSgxMikpO1xuICAgIGNvbnN0IGVuY29kZXIgPSBuZXcgVGV4dEVuY29kZXIoKTtcbiAgICBjb25zdCBlbmNvZGVkUGxhaW50ZXh0ID0gZW5jb2Rlci5lbmNvZGUocGxhaW50ZXh0KTtcblxuICAgIGNvbnN0IGNpcGhlcnRleHRCdWZmZXIgPSBhd2FpdCBjcnlwdG8uc3VidGxlLmVuY3J5cHQoXG4gICAgICB7XG4gICAgICAgIG5hbWU6ICdBRVMtR0NNJyxcbiAgICAgICAgaXY6IG5vbmNlXG4gICAgICB9LFxuICAgICAgc2Vzc2lvbktleS5yYXdLZXksXG4gICAgICBlbmNvZGVkUGxhaW50ZXh0XG4gICAgKTtcblxuICAgIHJldHVybiB7XG4gICAgICBjaXBoZXJ0ZXh0OiB0aGlzLmFycmF5QnVmZmVyVG9CYXNlNjQoY2lwaGVydGV4dEJ1ZmZlciksXG4gICAgICBub25jZTogdGhpcy5hcnJheUJ1ZmZlclRvQmFzZTY0KG5vbmNlLmJ1ZmZlciksXG4gICAgICBzZXNzaW9uS2V5SWQ6IHNlc3Npb25LZXkuaWQsXG4gICAgICBnZW5lcmF0aW9uOiBzZXNzaW9uS2V5LmdlbmVyYXRpb25cbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIERlY3J5cHRzIGFuIEVuY3J5cHRlZE1lc3NhZ2VQYXlsb2FkIHVzaW5nIEFFUy0yNTYtR0NNLlxuICAgKlxuICAgKiBAcGFyYW0gc2Vzc2lvbktleSBJbi1tZW1vcnkgc2Vzc2lvbiBrZXkgY29ycmVzcG9uZGluZyB0byBwYXlsb2FkLnNlc3Npb25LZXlJZFxuICAgKiBAcGFyYW0gcGF5bG9hZCBFbmNyeXB0ZWQgcGF5bG9hZCBjb250YWluaW5nIGNpcGhlcnRleHQgYW5kIG5vbmNlXG4gICAqL1xuICBhc3luYyBkZWNyeXB0TWVzc2FnZShcbiAgICBzZXNzaW9uS2V5OiBDb252ZXJzYXRpb25TZXNzaW9uS2V5LFxuICAgIHBheWxvYWQ6IEVuY3J5cHRlZE1lc3NhZ2VQYXlsb2FkXG4gICk6IFByb21pc2U8c3RyaW5nPiB7XG4gICAgaWYgKCFzZXNzaW9uS2V5IHx8ICFzZXNzaW9uS2V5LnJhd0tleSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdJbnZhbGlkIHNlc3Npb24ga2V5IHByb3ZpZGVkIGZvciBkZWNyeXB0aW9uLicpO1xuICAgIH1cblxuICAgIGNvbnN0IGNpcGhlcnRleHQgPSB0aGlzLmJhc2U2NFRvQXJyYXlCdWZmZXIocGF5bG9hZC5jaXBoZXJ0ZXh0KTtcbiAgICBjb25zdCBub25jZSA9IHRoaXMuYmFzZTY0VG9BcnJheUJ1ZmZlcihwYXlsb2FkLm5vbmNlKTtcblxuICAgIGNvbnN0IGRlY3J5cHRlZEJ1ZmZlciA9IGF3YWl0IGNyeXB0by5zdWJ0bGUuZGVjcnlwdChcbiAgICAgIHtcbiAgICAgICAgbmFtZTogJ0FFUy1HQ00nLFxuICAgICAgICBpdjogbmV3IFVpbnQ4QXJyYXkobm9uY2UpXG4gICAgICB9LFxuICAgICAgc2Vzc2lvbktleS5yYXdLZXksXG4gICAgICBjaXBoZXJ0ZXh0XG4gICAgKTtcblxuICAgIGNvbnN0IGRlY29kZXIgPSBuZXcgVGV4dERlY29kZXIoKTtcbiAgICByZXR1cm4gZGVjb2Rlci5kZWNvZGUoZGVjcnlwdGVkQnVmZmVyKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBFeHBvcnRzIGFuIEFFUy1HQ00ga2V5IGFuZCB3cmFwcyAoZW5jcnlwdHMpIGl0IHdpdGggYSByZWNpcGllbnQncyBSU0EgcHVibGljIGtleS5cbiAgICpcbiAgICogQHBhcmFtIGFlc0tleSBBRVMgQ3J5cHRvS2V5IHRvIHdyYXBcbiAgICogQHBhcmFtIHJlY2lwaWVudFB1YmxpY0tleSBCYXNlNjQtZW5jb2RlZCByZWNpcGllbnQgUlNBIHB1YmxpYyBrZXlcbiAgICovXG4gIGFzeW5jIHdyYXBTZXNzaW9uS2V5KFxuICAgIGFlc0tleTogQ3J5cHRvS2V5LFxuICAgIHJlY2lwaWVudFB1YmxpY0tleTogc3RyaW5nXG4gICk6IFByb21pc2U8c3RyaW5nPiB7XG4gICAgaWYgKCF0aGlzLmNyeXB0b1Byb3ZpZGVyKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICdLT0pJTlhfQ1JZUFRPX1BST1ZJREVSIGlzIG5vdCBwcm92aWRlZC4gQ2Fubm90IHdyYXAgc2Vzc2lvbiBrZXlzLidcbiAgICAgICk7XG4gICAgfVxuXG4gICAgY29uc3QgcmF3S2V5Qnl0ZXMgPSBhd2FpdCBjcnlwdG8uc3VidGxlLmV4cG9ydEtleSgncmF3JywgYWVzS2V5KTtcbiAgICBjb25zdCByYXdLZXlCYXNlNjQgPSB0aGlzLmFycmF5QnVmZmVyVG9CYXNlNjQocmF3S2V5Qnl0ZXMpO1xuXG4gICAgLy8gQXN5bW1ldHJpY2FsbHkgZW5jcnlwdCB1c2luZyBob3N0IFJTQSBwcm92aWRlclxuICAgIHJldHVybiB0aGlzLmNyeXB0b1Byb3ZpZGVyLmVuY3J5cHQocmVjaXBpZW50UHVibGljS2V5LCByYXdLZXlCYXNlNjQpO1xuICB9XG5cbiAgLyoqXG4gICAqIFVud3JhcHMgKGRlY3J5cHRzKSBhbiBSU0EtZW5jcnlwdGVkIEFFUyByYXcga2V5IGFuZCBpbXBvcnRzIGl0IGJhY2sgaW50byBhIFczQyBDcnlwdG9LZXkuXG4gICAqXG4gICAqIEBwYXJhbSBlbmNyeXB0ZWRBZXNLZXlCYXNlNjQgUlNBLWVuY3J5cHRlZCBBRVMgcmF3IGtleSBpbiBiYXNlNjQgZm9ybWF0XG4gICAqL1xuICBhc3luYyB1bndyYXBTZXNzaW9uS2V5KGVuY3J5cHRlZEFlc0tleUJhc2U2NDogc3RyaW5nKTogUHJvbWlzZTxDcnlwdG9LZXk+IHtcbiAgICBpZiAoIXRoaXMuY3J5cHRvUHJvdmlkZXIpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgJ0tPSklOWF9DUllQVE9fUFJPVklERVIgaXMgbm90IHByb3ZpZGVkLiBDYW5ub3QgdW53cmFwIHNlc3Npb24ga2V5cy4nXG4gICAgICApO1xuICAgIH1cblxuICAgIC8vIEFzeW1tZXRyaWNhbGx5IGRlY3J5cHQgdXNpbmcgbG9jYWwgcHJpdmF0ZSBrZXlcbiAgICBjb25zdCByYXdLZXlCYXNlNjQgPSBhd2FpdCB0aGlzLmNyeXB0b1Byb3ZpZGVyLmRlY3J5cHQoXG4gICAgICBlbmNyeXB0ZWRBZXNLZXlCYXNlNjRcbiAgICApO1xuICAgIGNvbnN0IHJhd0tleUJ1ZmZlciA9IHRoaXMuYmFzZTY0VG9BcnJheUJ1ZmZlcihyYXdLZXlCYXNlNjQpO1xuXG4gICAgcmV0dXJuIGNyeXB0by5zdWJ0bGUuaW1wb3J0S2V5KFxuICAgICAgJ3JhdycsXG4gICAgICByYXdLZXlCdWZmZXIsXG4gICAgICB7XG4gICAgICAgIG5hbWU6ICdBRVMtR0NNJyxcbiAgICAgICAgbGVuZ3RoOiAyNTZcbiAgICAgIH0sXG4gICAgICB0cnVlLFxuICAgICAgWydlbmNyeXB0JywgJ2RlY3J5cHQnXVxuICAgICk7XG4gIH1cblxuICAvKipcbiAgICogRXhwb3J0cyBhIENyeXB0b0tleSB0byBiYXNlNjQgcmF3IGJ5dGVzLlxuICAgKi9cbiAgYXN5bmMgZXhwb3J0UmF3S2V5QmFzZTY0KGtleTogQ3J5cHRvS2V5KTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgICBjb25zdCByYXcgPSBhd2FpdCBjcnlwdG8uc3VidGxlLmV4cG9ydEtleSgncmF3Jywga2V5KTtcbiAgICByZXR1cm4gdGhpcy5hcnJheUJ1ZmZlclRvQmFzZTY0KHJhdyk7XG4gIH1cblxuICAvKipcbiAgICogSW1wb3J0cyBhIGJhc2U2NCByYXcga2V5IHN0cmluZyBpbnRvIGEgQ3J5cHRvS2V5LlxuICAgKi9cbiAgYXN5bmMgaW1wb3J0UmF3S2V5QmFzZTY0KGJhc2U2NDogc3RyaW5nKTogUHJvbWlzZTxDcnlwdG9LZXk+IHtcbiAgICBjb25zdCBidWZmZXIgPSB0aGlzLmJhc2U2NFRvQXJyYXlCdWZmZXIoYmFzZTY0KTtcbiAgICByZXR1cm4gY3J5cHRvLnN1YnRsZS5pbXBvcnRLZXkoXG4gICAgICAncmF3JyxcbiAgICAgIGJ1ZmZlcixcbiAgICAgIHtcbiAgICAgICAgbmFtZTogJ0FFUy1HQ00nLFxuICAgICAgICBsZW5ndGg6IDI1NlxuICAgICAgfSxcbiAgICAgIHRydWUsXG4gICAgICBbJ2VuY3J5cHQnLCAnZGVjcnlwdCddXG4gICAgKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBFbmNvZGVzIEFycmF5QnVmZmVyIHRvIEJhc2U2NCBzdHJpbmcuXG4gICAqL1xuICBhcnJheUJ1ZmZlclRvQmFzZTY0KGJ1ZmZlcjogQXJyYXlCdWZmZXIgfCBVaW50OEFycmF5KTogc3RyaW5nIHtcbiAgICBjb25zdCBieXRlcyA9IGJ1ZmZlciBpbnN0YW5jZW9mIFVpbnQ4QXJyYXkgPyBidWZmZXIgOiBuZXcgVWludDhBcnJheShidWZmZXIpO1xuICAgIGxldCBiaW5hcnkgPSAnJztcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGJ5dGVzLmJ5dGVMZW5ndGg7IGkrKykge1xuICAgICAgYmluYXJ5ICs9IFN0cmluZy5mcm9tQ2hhckNvZGUoYnl0ZXNbaV0pO1xuICAgIH1cbiAgICByZXR1cm4gYnRvYShiaW5hcnkpO1xuICB9XG5cbiAgLyoqXG4gICAqIERlY29kZXMgQmFzZTY0IHN0cmluZyB0byBBcnJheUJ1ZmZlci5cbiAgICovXG4gIGJhc2U2NFRvQXJyYXlCdWZmZXIoYmFzZTY0OiBzdHJpbmcpOiBBcnJheUJ1ZmZlciB7XG4gICAgY29uc3QgYmluYXJ5ID0gYXRvYihiYXNlNjQudHJpbSgpKTtcbiAgICBjb25zdCBieXRlcyA9IG5ldyBVaW50OEFycmF5KGJpbmFyeS5sZW5ndGgpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgYmluYXJ5Lmxlbmd0aDsgaSsrKSB7XG4gICAgICBieXRlc1tpXSA9IGJpbmFyeS5jaGFyQ29kZUF0KGkpO1xuICAgIH1cbiAgICByZXR1cm4gYnl0ZXMuYnVmZmVyO1xuICB9XG59XG4iXX0=
@@ -0,0 +1,267 @@
1
+ import { Injectable, inject } from '@angular/core';
2
+ import { KOJINX_API_BASE_URL, KOJINX_AUTH_TOKEN, KOJINX_CRYPTO_STORAGE, KOJINX_HTTP_FETCH } from '../tokens';
3
+ import { E2EChatCryptoService } from './e2e-chat-crypto.service';
4
+ import * as i0 from "@angular/core";
5
+ /** 30 days in milliseconds for maximum session key lifetime */
6
+ const THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1000;
7
+ /**
8
+ * Manages the lifecycle, caching, negotiation, and rotation of conversation session keys.
9
+ */
10
+ export class SessionKeyManagerService {
11
+ constructor() {
12
+ this.cryptoService = inject(E2EChatCryptoService);
13
+ this.httpFetch = inject(KOJINX_HTTP_FETCH, {
14
+ optional: true
15
+ });
16
+ this.getApiUrl = inject(KOJINX_API_BASE_URL, {
17
+ optional: true
18
+ });
19
+ this.getAuthToken = inject(KOJINX_AUTH_TOKEN, { optional: true });
20
+ this.storage = inject(KOJINX_CRYPTO_STORAGE, {
21
+ optional: true
22
+ });
23
+ /** Fast in-memory cache mapped by conversationId */
24
+ this.activeKeysByConversation = new Map();
25
+ /** Fast in-memory cache mapped by sessionKeyId (for historic messages) */
26
+ this.keysById = new Map();
27
+ }
28
+ /**
29
+ * Retrieves or creates an active session key for a 1:1 Direct Message conversation.
30
+ *
31
+ * @param localUserId Current user's ID
32
+ * @param remoteUserId Remote contact's ID
33
+ * @param remotePublicKey Base64 RSA public key of remote contact
34
+ * @param localPublicKey Base64 RSA public key of local user
35
+ */
36
+ async getOrCreateDmSessionKey(localUserId, remoteUserId, remotePublicKey, localPublicKey) {
37
+ const conversationId = this.buildDmConversationId(localUserId, remoteUserId);
38
+ // 1. Check in-memory cache
39
+ const cached = this.activeKeysByConversation.get(conversationId);
40
+ if (cached && !this.isKeyExpired(cached)) {
41
+ return cached;
42
+ }
43
+ // 2. Attempt fetching from server if HTTP fetch is provided
44
+ if (this.httpFetch && this.getApiUrl && this.getAuthToken) {
45
+ const serverKey = await this.fetchSessionKeyFromServer(conversationId, 'dm');
46
+ if (serverKey) {
47
+ this.cacheKey(serverKey);
48
+ return serverKey;
49
+ }
50
+ }
51
+ // 3. Create fresh session key and distribute
52
+ return this.createAndDistributeSessionKey({
53
+ type: 'dm',
54
+ conversationId,
55
+ generation: cached ? cached.generation + 1 : 1,
56
+ members: [
57
+ { userId: localUserId, publicKey: localPublicKey },
58
+ { userId: remoteUserId, publicKey: remotePublicKey }
59
+ ]
60
+ });
61
+ }
62
+ /**
63
+ * Retrieves or creates an active session key for a Native Channel or Group.
64
+ *
65
+ * @param channelId Unique channel ID
66
+ * @param members List of all workspace/channel members with their public keys
67
+ */
68
+ async getOrCreateChannelSessionKey(channelId, members) {
69
+ const cached = this.activeKeysByConversation.get(channelId);
70
+ if (cached && !this.isKeyExpired(cached)) {
71
+ return cached;
72
+ }
73
+ if (this.httpFetch && this.getApiUrl && this.getAuthToken) {
74
+ const serverKey = await this.fetchSessionKeyFromServer(channelId, 'channel');
75
+ if (serverKey) {
76
+ this.cacheKey(serverKey);
77
+ return serverKey;
78
+ }
79
+ }
80
+ return this.createAndDistributeSessionKey({
81
+ type: 'channel',
82
+ conversationId: channelId,
83
+ generation: cached ? cached.generation + 1 : 1,
84
+ members
85
+ });
86
+ }
87
+ /**
88
+ * Retrieves a session key by its specific sessionKeyId (used for decrypting historic messages).
89
+ */
90
+ async getSessionKeyById(sessionKeyId) {
91
+ const cached = this.keysById.get(sessionKeyId);
92
+ if (cached)
93
+ return cached;
94
+ // Check local storage
95
+ if (this.storage) {
96
+ const stored = await this.storage.getItem(`kojinx_sk_${sessionKeyId}`);
97
+ if (stored) {
98
+ try {
99
+ const parsed = JSON.parse(stored);
100
+ const rawKey = await this.cryptoService.importRawKeyBase64(parsed.rawKeyBase64);
101
+ const key = {
102
+ id: parsed.id,
103
+ type: parsed.type,
104
+ conversationId: parsed.conversationId,
105
+ rawKey,
106
+ generation: parsed.generation,
107
+ createdAt: parsed.createdAt
108
+ };
109
+ this.cacheKey(key);
110
+ return key;
111
+ }
112
+ catch {
113
+ // ignore corrupted local key
114
+ }
115
+ }
116
+ }
117
+ return null;
118
+ }
119
+ /**
120
+ * Generates a new AES session key, wraps it for all members, and uploads to the server.
121
+ */
122
+ async createAndDistributeSessionKey(params) {
123
+ const rawAesKey = await this.cryptoService.generateAesKey();
124
+ const sessionKeyId = this.generateUuid();
125
+ const memberPayloads = {};
126
+ for (const member of params.members) {
127
+ if (member.publicKey) {
128
+ memberPayloads[member.userId] =
129
+ await this.cryptoService.wrapSessionKey(rawAesKey, member.publicKey);
130
+ }
131
+ }
132
+ const sessionKey = {
133
+ id: sessionKeyId,
134
+ type: params.type,
135
+ conversationId: params.conversationId,
136
+ rawKey: rawAesKey,
137
+ generation: params.generation,
138
+ createdAt: Date.now()
139
+ };
140
+ // Store in memory
141
+ this.cacheKey(sessionKey);
142
+ await this.persistKeyLocally(sessionKey);
143
+ // Upload to server if online
144
+ if (this.httpFetch && this.getApiUrl && this.getAuthToken) {
145
+ const token = await this.getAuthToken();
146
+ const apiUrl = this.getApiUrl();
147
+ const payload = {
148
+ sessionKeyId,
149
+ conversationType: params.type,
150
+ conversationId: params.conversationId,
151
+ generation: params.generation,
152
+ memberPayloads
153
+ };
154
+ try {
155
+ await this.httpFetch(`${apiUrl}/api/e2e/session-keys`, {
156
+ method: 'POST',
157
+ headers: {
158
+ Authorization: `Bearer ${token}`,
159
+ 'Content-Type': 'application/json'
160
+ },
161
+ body: JSON.stringify(payload)
162
+ });
163
+ }
164
+ catch (err) {
165
+ console.warn('[SessionKeyManager] Failed to sync session key to server:', err);
166
+ }
167
+ }
168
+ return sessionKey;
169
+ }
170
+ /**
171
+ * Helper to check if a key has exceeded the 30-day rotation threshold.
172
+ */
173
+ isKeyExpired(key) {
174
+ return Date.now() - key.createdAt > THIRTY_DAYS_MS;
175
+ }
176
+ /**
177
+ * Builds a deterministic conversation identifier for 1:1 DMs (lexicographical sort).
178
+ */
179
+ buildDmConversationId(userIdA, userIdB) {
180
+ return [userIdA, userIdB].sort().join('_');
181
+ }
182
+ /**
183
+ * Caches a session key in memory.
184
+ */
185
+ cacheKey(key) {
186
+ this.activeKeysByConversation.set(key.conversationId, key);
187
+ this.keysById.set(key.id, key);
188
+ }
189
+ /**
190
+ * Persists an unwrapped session key to local storage for fast startup.
191
+ */
192
+ async persistKeyLocally(key) {
193
+ if (!this.storage)
194
+ return;
195
+ try {
196
+ const rawKeyBase64 = await this.cryptoService.exportRawKeyBase64(key.rawKey);
197
+ const data = JSON.stringify({
198
+ id: key.id,
199
+ type: key.type,
200
+ conversationId: key.conversationId,
201
+ rawKeyBase64,
202
+ generation: key.generation,
203
+ createdAt: key.createdAt
204
+ });
205
+ await this.storage.setItem(`kojinx_sk_${key.id}`, data);
206
+ }
207
+ catch {
208
+ // ignore persistence error
209
+ }
210
+ }
211
+ /**
212
+ * Fetches latest session key from server and unwraps it locally.
213
+ */
214
+ async fetchSessionKeyFromServer(conversationId, type) {
215
+ if (!this.httpFetch || !this.getApiUrl || !this.getAuthToken)
216
+ 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}`
224
+ }
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
+ }
239
+ }
240
+ catch {
241
+ // Not found or network error
242
+ }
243
+ return null;
244
+ }
245
+ /**
246
+ * Generates a random UUID (W3C standard or fallback).
247
+ */
248
+ generateUuid() {
249
+ if (typeof crypto !== 'undefined' && crypto.randomUUID) {
250
+ return crypto.randomUUID();
251
+ }
252
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
253
+ const r = (Math.random() * 16) | 0;
254
+ const v = c === 'x' ? r : (r & 0x3) | 0x8;
255
+ return v.toString(16);
256
+ });
257
+ }
258
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SessionKeyManagerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
259
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SessionKeyManagerService, providedIn: 'root' }); }
260
+ }
261
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SessionKeyManagerService, decorators: [{
262
+ type: Injectable,
263
+ args: [{
264
+ providedIn: 'root'
265
+ }]
266
+ }] });
267
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2Vzc2lvbi1rZXktbWFuYWdlci5zZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMva29qaW54L2NvbGxhYi1jcnlwdG8vc3JjL2xpYi9zZXJ2aWNlcy9zZXNzaW9uLWtleS1tYW5hZ2VyLnNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFRbkQsT0FBTyxFQUNMLG1CQUFtQixFQUNuQixpQkFBaUIsRUFDakIscUJBQXFCLEVBQ3JCLGlCQUFpQixFQUNsQixNQUFNLFdBQVcsQ0FBQztBQUNuQixPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQzs7QUFFakUsK0RBQStEO0FBQy9ELE1BQU0sY0FBYyxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxJQUFJLENBQUM7QUFFaEQ7O0dBRUc7QUFJSCxNQUFNLE9BQU8sd0JBQXdCO0lBSHJDO1FBSVUsa0JBQWEsR0FBRyxNQUFNLENBQUMsb0JBQW9CLENBQUMsQ0FBQztRQUM3QyxjQUFTLEdBQUcsTUFBTSxDQUFzQixpQkFBaUIsRUFBRTtZQUNqRSxRQUFRLEVBQUUsSUFBSTtTQUNmLENBQUMsQ0FBQztRQUNLLGNBQVMsR0FBRyxNQUFNLENBQWUsbUJBQW1CLEVBQUU7WUFDNUQsUUFBUSxFQUFFLElBQUk7U0FDZixDQUFDLENBQUM7UUFDSyxpQkFBWSxHQUFHLE1BQU0sQ0FDM0IsaUJBQWlCLEVBQ2pCLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxDQUNuQixDQUFDO1FBQ00sWUFBTyxHQUFHLE1BQU0sQ0FBd0IscUJBQXFCLEVBQUU7WUFDckUsUUFBUSxFQUFFLElBQUk7U0FDZixDQUFDLENBQUM7UUFFSCxvREFBb0Q7UUFDNUMsNkJBQXdCLEdBQUcsSUFBSSxHQUFHLEVBQWtDLENBQUM7UUFDN0UsMEVBQTBFO1FBQ2xFLGFBQVEsR0FBRyxJQUFJLEdBQUcsRUFBa0MsQ0FBQztLQTBTOUQ7SUF4U0M7Ozs7Ozs7T0FPRztJQUNILEtBQUssQ0FBQyx1QkFBdUIsQ0FDM0IsV0FBbUIsRUFDbkIsWUFBb0IsRUFDcEIsZUFBdUIsRUFDdkIsY0FBc0I7UUFFdEIsTUFBTSxjQUFjLEdBQUcsSUFBSSxDQUFDLHFCQUFxQixDQUMvQyxXQUFXLEVBQ1gsWUFBWSxDQUNiLENBQUM7UUFFRiwyQkFBMkI7UUFDM0IsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLHdCQUF3QixDQUFDLEdBQUcsQ0FBQyxjQUFjLENBQUMsQ0FBQztRQUNqRSxJQUFJLE1BQU0sSUFBSSxDQUFDLElBQUksQ0FBQyxZQUFZLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQztZQUN6QyxPQUFPLE1BQU0sQ0FBQztRQUNoQixDQUFDO1FBRUQsNERBQTREO1FBQzVELElBQUksSUFBSSxDQUFDLFNBQVMsSUFBSSxJQUFJLENBQUMsU0FBUyxJQUFJLElBQUksQ0FBQyxZQUFZLEVBQUUsQ0FBQztZQUMxRCxNQUFNLFNBQVMsR0FBRyxNQUFNLElBQUksQ0FBQyx5QkFBeUIsQ0FDcEQsY0FBYyxFQUNkLElBQUksQ0FDTCxDQUFDO1lBQ0YsSUFBSSxTQUFTLEVBQUUsQ0FBQztnQkFDZCxJQUFJLENBQUMsUUFBUSxDQUFDLFNBQVMsQ0FBQyxDQUFDO2dCQUN6QixPQUFPLFNBQVMsQ0FBQztZQUNuQixDQUFDO1FBQ0gsQ0FBQztRQUVELDZDQUE2QztRQUM3QyxPQUFPLElBQUksQ0FBQyw2QkFBNkIsQ0FBQztZQUN4QyxJQUFJLEVBQUUsSUFBSTtZQUNWLGNBQWM7WUFDZCxVQUFVLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsVUFBVSxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUM5QyxPQUFPLEVBQUU7Z0JBQ1AsRUFBRSxNQUFNLEVBQUUsV0FBVyxFQUFFLFNBQVMsRUFBRSxjQUFjLEVBQUU7Z0JBQ2xELEVBQUUsTUFBTSxFQUFFLFlBQVksRUFBRSxTQUFTLEVBQUUsZUFBZSxFQUFFO2FBQ3JEO1NBQ0YsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVEOzs7OztPQUtHO0lBQ0gsS0FBSyxDQUFDLDRCQUE0QixDQUNoQyxTQUFpQixFQUNqQixPQUEwQjtRQUUxQixNQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsd0JBQXdCLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBQzVELElBQUksTUFBTSxJQUFJLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDO1lBQ3pDLE9BQU8sTUFBTSxDQUFDO1FBQ2hCLENBQUM7UUFFRCxJQUFJLElBQUksQ0FBQyxTQUFTLElBQUksSUFBSSxDQUFDLFNBQVMsSUFBSSxJQUFJLENBQUMsWUFBWSxFQUFFLENBQUM7WUFDMUQsTUFBTSxTQUFTLEdBQUcsTUFBTSxJQUFJLENBQUMseUJBQXlCLENBQ3BELFNBQVMsRUFDVCxTQUFTLENBQ1YsQ0FBQztZQUNGLElBQUksU0FBUyxFQUFFLENBQUM7Z0JBQ2QsSUFBSSxDQUFDLFFBQVEsQ0FBQyxTQUFTLENBQUMsQ0FBQztnQkFDekIsT0FBTyxTQUFTLENBQUM7WUFDbkIsQ0FBQztRQUNILENBQUM7UUFFRCxPQUFPLElBQUksQ0FBQyw2QkFBNkIsQ0FBQztZQUN4QyxJQUFJLEVBQUUsU0FBUztZQUNmLGNBQWMsRUFBRSxTQUFTO1lBQ3pCLFVBQVUsRUFBRSxNQUFNLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxVQUFVLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQzlDLE9BQU87U0FDUixDQUFDLENBQUM7SUFDTCxDQUFDO0lBRUQ7O09BRUc7SUFDSCxLQUFLLENBQUMsaUJBQWlCLENBQ3JCLFlBQW9CO1FBRXBCLE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLFlBQVksQ0FBQyxDQUFDO1FBQy9DLElBQUksTUFBTTtZQUFFLE9BQU8sTUFBTSxDQUFDO1FBRTFCLHNCQUFzQjtRQUN0QixJQUFJLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUNqQixNQUFNLE1BQU0sR0FBRyxNQUFNLElBQUksQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUN2QyxhQUFhLFlBQVksRUFBRSxDQUM1QixDQUFDO1lBQ0YsSUFBSSxNQUFNLEVBQUUsQ0FBQztnQkFDWCxJQUFJLENBQUM7b0JBQ0gsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsQ0FBQztvQkFDbEMsTUFBTSxNQUFNLEdBQUcsTUFBTSxJQUFJLENBQUMsYUFBYSxDQUFDLGtCQUFrQixDQUN4RCxNQUFNLENBQUMsWUFBWSxDQUNwQixDQUFDO29CQUNGLE1BQU0sR0FBRyxHQUEyQjt3QkFDbEMsRUFBRSxFQUFFLE1BQU0sQ0FBQyxFQUFFO3dCQUNiLElBQUksRUFBRSxNQUFNLENBQUMsSUFBSTt3QkFDakIsY0FBYyxFQUFFLE1BQU0sQ0FBQyxjQUFjO3dCQUNyQyxNQUFNO3dCQUNOLFVBQVUsRUFBRSxNQUFNLENBQUMsVUFBVTt3QkFDN0IsU0FBUyxFQUFFLE1BQU0sQ0FBQyxTQUFTO3FCQUM1QixDQUFDO29CQUNGLElBQUksQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLENBQUM7b0JBQ25CLE9BQU8sR0FBRyxDQUFDO2dCQUNiLENBQUM7Z0JBQUMsTUFBTSxDQUFDO29CQUNQLDZCQUE2QjtnQkFDL0IsQ0FBQztZQUNILENBQUM7UUFDSCxDQUFDO1FBRUQsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBRUQ7O09BRUc7SUFDSCxLQUFLLENBQUMsNkJBQTZCLENBQUMsTUFLbkM7UUFDQyxNQUFNLFNBQVMsR0FBRyxNQUFNLElBQUksQ0FBQyxhQUFhLENBQUMsY0FBYyxFQUFFLENBQUM7UUFDNUQsTUFBTSxZQUFZLEdBQUcsSUFBSSxDQUFDLFlBQVksRUFBRSxDQUFDO1FBRXpDLE1BQU0sY0FBYyxHQUEyQixFQUFFLENBQUM7UUFDbEQsS0FBSyxNQUFNLE1BQU0sSUFBSSxNQUFNLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDcEMsSUFBSSxNQUFNLENBQUMsU0FBUyxFQUFFLENBQUM7Z0JBQ3JCLGNBQWMsQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDO29CQUMzQixNQUFNLElBQUksQ0FBQyxhQUFhLENBQUMsY0FBYyxDQUNyQyxTQUFTLEVBQ1QsTUFBTSxDQUFDLFNBQVMsQ0FDakIsQ0FBQztZQUNOLENBQUM7UUFDSCxDQUFDO1FBRUQsTUFBTSxVQUFVLEdBQTJCO1lBQ3pDLEVBQUUsRUFBRSxZQUFZO1lBQ2hCLElBQUksRUFBRSxNQUFNLENBQUMsSUFBSTtZQUNqQixjQUFjLEVBQUUsTUFBTSxDQUFDLGNBQWM7WUFDckMsTUFBTSxFQUFFLFNBQVM7WUFDakIsVUFBVSxFQUFFLE1BQU0sQ0FBQyxVQUFVO1lBQzdCLFNBQVMsRUFBRSxJQUFJLENBQUMsR0FBRyxFQUFFO1NBQ3RCLENBQUM7UUFFRixrQkFBa0I7UUFDbEIsSUFBSSxDQUFDLFFBQVEsQ0FBQyxVQUFVLENBQUMsQ0FBQztRQUMxQixNQUFNLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxVQUFVLENBQUMsQ0FBQztRQUV6Qyw2QkFBNkI7UUFDN0IsSUFBSSxJQUFJLENBQUMsU0FBUyxJQUFJLElBQUksQ0FBQyxTQUFTLElBQUksSUFBSSxDQUFDLFlBQVksRUFBRSxDQUFDO1lBQzFELE1BQU0sS0FBSyxHQUFHLE1BQU0sSUFBSSxDQUFDLFlBQVksRUFBRSxDQUFDO1lBQ3hDLE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxTQUFTLEVBQUUsQ0FBQztZQUNoQyxNQUFNLE9BQU8sR0FBOEI7Z0JBQ3pDLFlBQVk7Z0JBQ1osZ0JBQWdCLEVBQUUsTUFBTSxDQUFDLElBQUk7Z0JBQzdCLGNBQWMsRUFBRSxNQUFNLENBQUMsY0FBYztnQkFDckMsVUFBVSxFQUFFLE1BQU0sQ0FBQyxVQUFVO2dCQUM3QixjQUFjO2FBQ2YsQ0FBQztZQUVGLElBQUksQ0FBQztnQkFDSCxNQUFNLElBQUksQ0FBQyxTQUFTLENBQUMsR0FBRyxNQUFNLHVCQUF1QixFQUFFO29CQUNyRCxNQUFNLEVBQUUsTUFBTTtvQkFDZCxPQUFPLEVBQUU7d0JBQ1AsYUFBYSxFQUFFLFVBQVUsS0FBSyxFQUFFO3dCQUNoQyxjQUFjLEVBQUUsa0JBQWtCO3FCQUNuQztvQkFDRCxJQUFJLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUM7aUJBQzlCLENBQUMsQ0FBQztZQUNMLENBQUM7WUFBQyxPQUFPLEdBQUcsRUFBRSxDQUFDO2dCQUNiLE9BQU8sQ0FBQyxJQUFJLENBQ1YsMkRBQTJELEVBQzNELEdBQUcsQ0FDSixDQUFDO1lBQ0osQ0FBQztRQUNILENBQUM7UUFFRCxPQUFPLFVBQVUsQ0FBQztJQUNwQixDQUFDO0lBRUQ7O09BRUc7SUFDSCxZQUFZLENBQUMsR0FBMkI7UUFDdEMsT0FBTyxJQUFJLENBQUMsR0FBRyxFQUFFLEdBQUcsR0FBRyxDQUFDLFNBQVMsR0FBRyxjQUFjLENBQUM7SUFDckQsQ0FBQztJQUVEOztPQUVHO0lBQ0gscUJBQXFCLENBQUMsT0FBZSxFQUFFLE9BQWU7UUFDcEQsT0FBTyxDQUFDLE9BQU8sRUFBRSxPQUFPLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDN0MsQ0FBQztJQUVEOztPQUVHO0lBQ0ssUUFBUSxDQUFDLEdBQTJCO1FBQzFDLElBQUksQ0FBQyx3QkFBd0IsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLGNBQWMsRUFBRSxHQUFHLENBQUMsQ0FBQztRQUMzRCxJQUFJLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsRUFBRSxFQUFFLEdBQUcsQ0FBQyxDQUFDO0lBQ2pDLENBQUM7SUFFRDs7T0FFRztJQUNLLEtBQUssQ0FBQyxpQkFBaUIsQ0FDN0IsR0FBMkI7UUFFM0IsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPO1lBQUUsT0FBTztRQUMxQixJQUFJLENBQUM7WUFDSCxNQUFNLFlBQVksR0FBRyxNQUFNLElBQUksQ0FBQyxhQUFhLENBQUMsa0JBQWtCLENBQzlELEdBQUcsQ0FBQyxNQUFNLENBQ1gsQ0FBQztZQUNGLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUM7Z0JBQzFCLEVBQUUsRUFBRSxHQUFHLENBQUMsRUFBRTtnQkFDVixJQUFJLEVBQUUsR0FBRyxDQUFDLElBQUk7Z0JBQ2QsY0FBYyxFQUFFLEdBQUcsQ0FBQyxjQUFjO2dCQUNsQyxZQUFZO2dCQUNaLFVBQVUsRUFBRSxHQUFHLENBQUMsVUFBVTtnQkFDMUIsU0FBUyxFQUFFLEdBQUcsQ0FBQyxTQUFTO2FBQ3pCLENBQUMsQ0FBQztZQUNILE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsYUFBYSxHQUFHLENBQUMsRUFBRSxFQUFFLEVBQUUsSUFBSSxDQUFDLENBQUM7UUFDMUQsQ0FBQztRQUFDLE1BQU0sQ0FBQztZQUNQLDJCQUEyQjtRQUM3QixDQUFDO0lBQ0gsQ0FBQztJQUVEOztPQUVHO0lBQ0ssS0FBSyxDQUFDLHlCQUF5QixDQUNyQyxjQUFzQixFQUN0QixJQUFnQztRQUVoQyxJQUFJLENBQUMsSUFBSSxDQUFDLFNBQVMsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLElBQUksQ0FBQyxJQUFJLENBQUMsWUFBWTtZQUFFLE9BQU8sSUFBSSxDQUFDO1FBRTFFLElBQUksQ0FBQztZQUNILE1BQU0sS0FBSyxHQUFHLE1BQU0sSUFBSSxDQUFDLFlBQVksRUFBRSxDQUFDO1lBQ3hDLE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxTQUFTLEVBQUUsQ0FBQztZQUNoQyxNQUFNLEdBQUcsR0FBRyxNQUFNLElBQUksQ0FBQyxTQUFTLENBSzdCLEdBQUcsTUFBTSx5QkFBeUIsY0FBYyxFQUFFLEVBQUU7Z0JBQ3JELE1BQU0sRUFBRSxLQUFLO2dCQUNiLE9BQU8sRUFBRTtvQkFDUCxhQUFhLEVBQUUsVUFBVSxLQUFLLEVBQUU7aUJBQ2pDO2FBQ0YsQ0FBQyxDQUFDO1lBRUgsSUFBSSxHQUFHLENBQUMsTUFBTSxLQUFLLEdBQUcsSUFBSSxHQUFHLENBQUMsSUFBSSxFQUFFLFlBQVksRUFBRSxDQUFDO2dCQUNqRCxNQUFNLE1BQU0sR0FBRyxNQUFNLElBQUksQ0FBQyxhQUFhLENBQUMsZ0JBQWdCLENBQ3RELEdBQUcsQ0FBQyxJQUFJLENBQUMsWUFBWSxDQUN0QixDQUFDO2dCQUNGLE9BQU87b0JBQ0wsRUFBRSxFQUFFLEdBQUcsQ0FBQyxJQUFJLENBQUMsWUFBWTtvQkFDekIsSUFBSTtvQkFDSixjQUFjO29CQUNkLE1BQU07b0JBQ04sVUFBVSxFQUFFLEdBQUcsQ0FBQyxJQUFJLENBQUMsVUFBVSxJQUFJLENBQUM7b0JBQ3BDLFNBQVMsRUFBRSxHQUFHLENBQUMsSUFBSSxDQUFDLFNBQVM7d0JBQzNCLENBQUMsQ0FBQyxJQUFJLElBQUksQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFDLE9BQU8sRUFBRTt3QkFDeEMsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUU7aUJBQ2YsQ0FBQztZQUNKLENBQUM7UUFDSCxDQUFDO1FBQUMsTUFBTSxDQUFDO1lBQ1AsNkJBQTZCO1FBQy9CLENBQUM7UUFFRCxPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7SUFFRDs7T0FFRztJQUNLLFlBQVk7UUFDbEIsSUFBSSxPQUFPLE1BQU0sS0FBSyxXQUFXLElBQUksTUFBTSxDQUFDLFVBQVUsRUFBRSxDQUFDO1lBQ3ZELE9BQU8sTUFBTSxDQUFDLFVBQVUsRUFBRSxDQUFDO1FBQzdCLENBQUM7UUFDRCxPQUFPLHNDQUFzQyxDQUFDLE9BQU8sQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFDLEVBQUU7WUFDakUsTUFBTSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLEdBQUcsRUFBRSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1lBQ25DLE1BQU0sQ0FBQyxHQUFHLENBQUMsS0FBSyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsR0FBRyxDQUFDLEdBQUcsR0FBRyxDQUFDO1lBQzFDLE9BQU8sQ0FBQyxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUN4QixDQUFDLENBQUMsQ0FBQztJQUNMLENBQUM7K0dBNVRVLHdCQUF3QjttSEFBeEIsd0JBQXdCLGNBRnZCLE1BQU07OzRGQUVQLHdCQUF3QjtrQkFIcEMsVUFBVTttQkFBQztvQkFDVixVQUFVLEVBQUUsTUFBTTtpQkFDbkIiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBJbmplY3RhYmxlLCBpbmplY3QgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7XG4gIENvbnZlcnNhdGlvblNlc3Npb25LZXksXG4gIEtvamlueEZldGNoRnVuY3Rpb24sXG4gIEtvamlueFN0b3JhZ2VQcm92aWRlcixcbiAgTWVtYmVyUHVibGljS2V5LFxuICBTZXNzaW9uS2V5RXhjaGFuZ2VQYXlsb2FkXG59IGZyb20gJy4uL21vZGVscy9jcnlwdG8ubW9kZWxzJztcbmltcG9ydCB7XG4gIEtPSklOWF9BUElfQkFTRV9VUkwsXG4gIEtPSklOWF9BVVRIX1RPS0VOLFxuICBLT0pJTlhfQ1JZUFRPX1NUT1JBR0UsXG4gIEtPSklOWF9IVFRQX0ZFVENIXG59IGZyb20gJy4uL3Rva2Vucyc7XG5pbXBvcnQgeyBFMkVDaGF0Q3J5cHRvU2VydmljZSB9IGZyb20gJy4vZTJlLWNoYXQtY3J5cHRvLnNlcnZpY2UnO1xuXG4vKiogMzAgZGF5cyBpbiBtaWxsaXNlY29uZHMgZm9yIG1heGltdW0gc2Vzc2lvbiBrZXkgbGlmZXRpbWUgKi9cbmNvbnN0IFRISVJUWV9EQVlTX01TID0gMzAgKiAyNCAqIDYwICogNjAgKiAxMDAwO1xuXG4vKipcbiAqIE1hbmFnZXMgdGhlIGxpZmVjeWNsZSwgY2FjaGluZywgbmVnb3RpYXRpb24sIGFuZCByb3RhdGlvbiBvZiBjb252ZXJzYXRpb24gc2Vzc2lvbiBrZXlzLlxuICovXG5ASW5qZWN0YWJsZSh7XG4gIHByb3ZpZGVkSW46ICdyb290J1xufSlcbmV4cG9ydCBjbGFzcyBTZXNzaW9uS2V5TWFuYWdlclNlcnZpY2Uge1xuICBwcml2YXRlIGNyeXB0b1NlcnZpY2UgPSBpbmplY3QoRTJFQ2hhdENyeXB0b1NlcnZpY2UpO1xuICBwcml2YXRlIGh0dHBGZXRjaCA9IGluamVjdDxLb2ppbnhGZXRjaEZ1bmN0aW9uPihLT0pJTlhfSFRUUF9GRVRDSCwge1xuICAgIG9wdGlvbmFsOiB0cnVlXG4gIH0pO1xuICBwcml2YXRlIGdldEFwaVVybCA9IGluamVjdDwoKSA9PiBzdHJpbmc+KEtPSklOWF9BUElfQkFTRV9VUkwsIHtcbiAgICBvcHRpb25hbDogdHJ1ZVxuICB9KTtcbiAgcHJpdmF0ZSBnZXRBdXRoVG9rZW4gPSBpbmplY3Q8KCkgPT4gUHJvbWlzZTxzdHJpbmcgfCBudWxsPj4oXG4gICAgS09KSU5YX0FVVEhfVE9LRU4sXG4gICAgeyBvcHRpb25hbDogdHJ1ZSB9XG4gICk7XG4gIHByaXZhdGUgc3RvcmFnZSA9IGluamVjdDxLb2ppbnhTdG9yYWdlUHJvdmlkZXI+KEtPSklOWF9DUllQVE9fU1RPUkFHRSwge1xuICAgIG9wdGlvbmFsOiB0cnVlXG4gIH0pO1xuXG4gIC8qKiBGYXN0IGluLW1lbW9yeSBjYWNoZSBtYXBwZWQgYnkgY29udmVyc2F0aW9uSWQgKi9cbiAgcHJpdmF0ZSBhY3RpdmVLZXlzQnlDb252ZXJzYXRpb24gPSBuZXcgTWFwPHN0cmluZywgQ29udmVyc2F0aW9uU2Vzc2lvbktleT4oKTtcbiAgLyoqIEZhc3QgaW4tbWVtb3J5IGNhY2hlIG1hcHBlZCBieSBzZXNzaW9uS2V5SWQgKGZvciBoaXN0b3JpYyBtZXNzYWdlcykgKi9cbiAgcHJpdmF0ZSBrZXlzQnlJZCA9IG5ldyBNYXA8c3RyaW5nLCBDb252ZXJzYXRpb25TZXNzaW9uS2V5PigpO1xuXG4gIC8qKlxuICAgKiBSZXRyaWV2ZXMgb3IgY3JlYXRlcyBhbiBhY3RpdmUgc2Vzc2lvbiBrZXkgZm9yIGEgMToxIERpcmVjdCBNZXNzYWdlIGNvbnZlcnNhdGlvbi5cbiAgICpcbiAgICogQHBhcmFtIGxvY2FsVXNlcklkIEN1cnJlbnQgdXNlcidzIElEXG4gICAqIEBwYXJhbSByZW1vdGVVc2VySWQgUmVtb3RlIGNvbnRhY3QncyBJRFxuICAgKiBAcGFyYW0gcmVtb3RlUHVibGljS2V5IEJhc2U2NCBSU0EgcHVibGljIGtleSBvZiByZW1vdGUgY29udGFjdFxuICAgKiBAcGFyYW0gbG9jYWxQdWJsaWNLZXkgQmFzZTY0IFJTQSBwdWJsaWMga2V5IG9mIGxvY2FsIHVzZXJcbiAgICovXG4gIGFzeW5jIGdldE9yQ3JlYXRlRG1TZXNzaW9uS2V5KFxuICAgIGxvY2FsVXNlcklkOiBzdHJpbmcsXG4gICAgcmVtb3RlVXNlcklkOiBzdHJpbmcsXG4gICAgcmVtb3RlUHVibGljS2V5OiBzdHJpbmcsXG4gICAgbG9jYWxQdWJsaWNLZXk6IHN0cmluZ1xuICApOiBQcm9taXNlPENvbnZlcnNhdGlvblNlc3Npb25LZXk+IHtcbiAgICBjb25zdCBjb252ZXJzYXRpb25JZCA9IHRoaXMuYnVpbGREbUNvbnZlcnNhdGlvbklkKFxuICAgICAgbG9jYWxVc2VySWQsXG4gICAgICByZW1vdGVVc2VySWRcbiAgICApO1xuXG4gICAgLy8gMS4gQ2hlY2sgaW4tbWVtb3J5IGNhY2hlXG4gICAgY29uc3QgY2FjaGVkID0gdGhpcy5hY3RpdmVLZXlzQnlDb252ZXJzYXRpb24uZ2V0KGNvbnZlcnNhdGlvbklkKTtcbiAgICBpZiAoY2FjaGVkICYmICF0aGlzLmlzS2V5RXhwaXJlZChjYWNoZWQpKSB7XG4gICAgICByZXR1cm4gY2FjaGVkO1xuICAgIH1cblxuICAgIC8vIDIuIEF0dGVtcHQgZmV0Y2hpbmcgZnJvbSBzZXJ2ZXIgaWYgSFRUUCBmZXRjaCBpcyBwcm92aWRlZFxuICAgIGlmICh0aGlzLmh0dHBGZXRjaCAmJiB0aGlzLmdldEFwaVVybCAmJiB0aGlzLmdldEF1dGhUb2tlbikge1xuICAgICAgY29uc3Qgc2VydmVyS2V5ID0gYXdhaXQgdGhpcy5mZXRjaFNlc3Npb25LZXlGcm9tU2VydmVyKFxuICAgICAgICBjb252ZXJzYXRpb25JZCxcbiAgICAgICAgJ2RtJ1xuICAgICAgKTtcbiAgICAgIGlmIChzZXJ2ZXJLZXkpIHtcbiAgICAgICAgdGhpcy5jYWNoZUtleShzZXJ2ZXJLZXkpO1xuICAgICAgICByZXR1cm4gc2VydmVyS2V5O1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIDMuIENyZWF0ZSBmcmVzaCBzZXNzaW9uIGtleSBhbmQgZGlzdHJpYnV0ZVxuICAgIHJldHVybiB0aGlzLmNyZWF0ZUFuZERpc3RyaWJ1dGVTZXNzaW9uS2V5KHtcbiAgICAgIHR5cGU6ICdkbScsXG4gICAgICBjb252ZXJzYXRpb25JZCxcbiAgICAgIGdlbmVyYXRpb246IGNhY2hlZCA/IGNhY2hlZC5nZW5lcmF0aW9uICsgMSA6IDEsXG4gICAgICBtZW1iZXJzOiBbXG4gICAgICAgIHsgdXNlcklkOiBsb2NhbFVzZXJJZCwgcHVibGljS2V5OiBsb2NhbFB1YmxpY0tleSB9LFxuICAgICAgICB7IHVzZXJJZDogcmVtb3RlVXNlcklkLCBwdWJsaWNLZXk6IHJlbW90ZVB1YmxpY0tleSB9XG4gICAgICBdXG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogUmV0cmlldmVzIG9yIGNyZWF0ZXMgYW4gYWN0aXZlIHNlc3Npb24ga2V5IGZvciBhIE5hdGl2ZSBDaGFubmVsIG9yIEdyb3VwLlxuICAgKlxuICAgKiBAcGFyYW0gY2hhbm5lbElkIFVuaXF1ZSBjaGFubmVsIElEXG4gICAqIEBwYXJhbSBtZW1iZXJzIExpc3Qgb2YgYWxsIHdvcmtzcGFjZS9jaGFubmVsIG1lbWJlcnMgd2l0aCB0aGVpciBwdWJsaWMga2V5c1xuICAgKi9cbiAgYXN5bmMgZ2V0T3JDcmVhdGVDaGFubmVsU2Vzc2lvbktleShcbiAgICBjaGFubmVsSWQ6IHN0cmluZyxcbiAgICBtZW1iZXJzOiBNZW1iZXJQdWJsaWNLZXlbXVxuICApOiBQcm9taXNlPENvbnZlcnNhdGlvblNlc3Npb25LZXk+IHtcbiAgICBjb25zdCBjYWNoZWQgPSB0aGlzLmFjdGl2ZUtleXNCeUNvbnZlcnNhdGlvbi5nZXQoY2hhbm5lbElkKTtcbiAgICBpZiAoY2FjaGVkICYmICF0aGlzLmlzS2V5RXhwaXJlZChjYWNoZWQpKSB7XG4gICAgICByZXR1cm4gY2FjaGVkO1xuICAgIH1cblxuICAgIGlmICh0aGlzLmh0dHBGZXRjaCAmJiB0aGlzLmdldEFwaVVybCAmJiB0aGlzLmdldEF1dGhUb2tlbikge1xuICAgICAgY29uc3Qgc2VydmVyS2V5ID0gYXdhaXQgdGhpcy5mZXRjaFNlc3Npb25LZXlGcm9tU2VydmVyKFxuICAgICAgICBjaGFubmVsSWQsXG4gICAgICAgICdjaGFubmVsJ1xuICAgICAgKTtcbiAgICAgIGlmIChzZXJ2ZXJLZXkpIHtcbiAgICAgICAgdGhpcy5jYWNoZUtleShzZXJ2ZXJLZXkpO1xuICAgICAgICByZXR1cm4gc2VydmVyS2V5O1xuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiB0aGlzLmNyZWF0ZUFuZERpc3RyaWJ1dGVTZXNzaW9uS2V5KHtcbiAgICAgIHR5cGU6ICdjaGFubmVsJyxcbiAgICAgIGNvbnZlcnNhdGlvbklkOiBjaGFubmVsSWQsXG4gICAgICBnZW5lcmF0aW9uOiBjYWNoZWQgPyBjYWNoZWQuZ2VuZXJhdGlvbiArIDEgOiAxLFxuICAgICAgbWVtYmVyc1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHJpZXZlcyBhIHNlc3Npb24ga2V5IGJ5IGl0cyBzcGVjaWZpYyBzZXNzaW9uS2V5SWQgKHVzZWQgZm9yIGRlY3J5cHRpbmcgaGlzdG9yaWMgbWVzc2FnZXMpLlxuICAgKi9cbiAgYXN5bmMgZ2V0U2Vzc2lvbktleUJ5SWQoXG4gICAgc2Vzc2lvbktleUlkOiBzdHJpbmdcbiAgKTogUHJvbWlzZTxDb252ZXJzYXRpb25TZXNzaW9uS2V5IHwgbnVsbD4ge1xuICAgIGNvbnN0IGNhY2hlZCA9IHRoaXMua2V5c0J5SWQuZ2V0KHNlc3Npb25LZXlJZCk7XG4gICAgaWYgKGNhY2hlZCkgcmV0dXJuIGNhY2hlZDtcblxuICAgIC8vIENoZWNrIGxvY2FsIHN0b3JhZ2VcbiAgICBpZiAodGhpcy5zdG9yYWdlKSB7XG4gICAgICBjb25zdCBzdG9yZWQgPSBhd2FpdCB0aGlzLnN0b3JhZ2UuZ2V0SXRlbShcbiAgICAgICAgYGtvamlueF9za18ke3Nlc3Npb25LZXlJZH1gXG4gICAgICApO1xuICAgICAgaWYgKHN0b3JlZCkge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGNvbnN0IHBhcnNlZCA9IEpTT04ucGFyc2Uoc3RvcmVkKTtcbiAgICAgICAgICBjb25zdCByYXdLZXkgPSBhd2FpdCB0aGlzLmNyeXB0b1NlcnZpY2UuaW1wb3J0UmF3S2V5QmFzZTY0KFxuICAgICAgICAgICAgcGFyc2VkLnJhd0tleUJhc2U2NFxuICAgICAgICAgICk7XG4gICAgICAgICAgY29uc3Qga2V5OiBDb252ZXJzYXRpb25TZXNzaW9uS2V5ID0ge1xuICAgICAgICAgICAgaWQ6IHBhcnNlZC5pZCxcbiAgICAgICAgICAgIHR5cGU6IHBhcnNlZC50eXBlLFxuICAgICAgICAgICAgY29udmVyc2F0aW9uSWQ6IHBhcnNlZC5jb252ZXJzYXRpb25JZCxcbiAgICAgICAgICAgIHJhd0tleSxcbiAgICAgICAgICAgIGdlbmVyYXRpb246IHBhcnNlZC5nZW5lcmF0aW9uLFxuICAgICAgICAgICAgY3JlYXRlZEF0OiBwYXJzZWQuY3JlYXRlZEF0XG4gICAgICAgICAgfTtcbiAgICAgICAgICB0aGlzLmNhY2hlS2V5KGtleSk7XG4gICAgICAgICAgcmV0dXJuIGtleTtcbiAgICAgICAgfSBjYXRjaCB7XG4gICAgICAgICAgLy8gaWdub3JlIGNvcnJ1cHRlZCBsb2NhbCBrZXlcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgLyoqXG4gICAqIEdlbmVyYXRlcyBhIG5ldyBBRVMgc2Vzc2lvbiBrZXksIHdyYXBzIGl0IGZvciBhbGwgbWVtYmVycywgYW5kIHVwbG9hZHMgdG8gdGhlIHNlcnZlci5cbiAgICovXG4gIGFzeW5jIGNyZWF0ZUFuZERpc3RyaWJ1dGVTZXNzaW9uS2V5KHBhcmFtczoge1xuICAgIHR5cGU6ICdkbScgfCAnY2hhbm5lbCcgfCAnZ3JvdXAnO1xuICAgIGNvbnZlcnNhdGlvbklkOiBzdHJpbmc7XG4gICAgZ2VuZXJhdGlvbjogbnVtYmVyO1xuICAgIG1lbWJlcnM6IE1lbWJlclB1YmxpY0tleVtdO1xuICB9KTogUHJvbWlzZTxDb252ZXJzYXRpb25TZXNzaW9uS2V5PiB7XG4gICAgY29uc3QgcmF3QWVzS2V5ID0gYXdhaXQgdGhpcy5jcnlwdG9TZXJ2aWNlLmdlbmVyYXRlQWVzS2V5KCk7XG4gICAgY29uc3Qgc2Vzc2lvbktleUlkID0gdGhpcy5nZW5lcmF0ZVV1aWQoKTtcblxuICAgIGNvbnN0IG1lbWJlclBheWxvYWRzOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0ge307XG4gICAgZm9yIChjb25zdCBtZW1iZXIgb2YgcGFyYW1zLm1lbWJlcnMpIHtcbiAgICAgIGlmIChtZW1iZXIucHVibGljS2V5KSB7XG4gICAgICAgIG1lbWJlclBheWxvYWRzW21lbWJlci51c2VySWRdID1cbiAgICAgICAgICBhd2FpdCB0aGlzLmNyeXB0b1NlcnZpY2Uud3JhcFNlc3Npb25LZXkoXG4gICAgICAgICAgICByYXdBZXNLZXksXG4gICAgICAgICAgICBtZW1iZXIucHVibGljS2V5XG4gICAgICAgICAgKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBjb25zdCBzZXNzaW9uS2V5OiBDb252ZXJzYXRpb25TZXNzaW9uS2V5ID0ge1xuICAgICAgaWQ6IHNlc3Npb25LZXlJZCxcbiAgICAgIHR5cGU6IHBhcmFtcy50eXBlLFxuICAgICAgY29udmVyc2F0aW9uSWQ6IHBhcmFtcy5jb252ZXJzYXRpb25JZCxcbiAgICAgIHJhd0tleTogcmF3QWVzS2V5LFxuICAgICAgZ2VuZXJhdGlvbjogcGFyYW1zLmdlbmVyYXRpb24sXG4gICAgICBjcmVhdGVkQXQ6IERhdGUubm93KClcbiAgICB9O1xuXG4gICAgLy8gU3RvcmUgaW4gbWVtb3J5XG4gICAgdGhpcy5jYWNoZUtleShzZXNzaW9uS2V5KTtcbiAgICBhd2FpdCB0aGlzLnBlcnNpc3RLZXlMb2NhbGx5KHNlc3Npb25LZXkpO1xuXG4gICAgLy8gVXBsb2FkIHRvIHNlcnZlciBpZiBvbmxpbmVcbiAgICBpZiAodGhpcy5odHRwRmV0Y2ggJiYgdGhpcy5nZXRBcGlVcmwgJiYgdGhpcy5nZXRBdXRoVG9rZW4pIHtcbiAgICAgIGNvbnN0IHRva2VuID0gYXdhaXQgdGhpcy5nZXRBdXRoVG9rZW4oKTtcbiAgICAgIGNvbnN0IGFwaVVybCA9IHRoaXMuZ2V0QXBpVXJsKCk7XG4gICAgICBjb25zdCBwYXlsb2FkOiBTZXNzaW9uS2V5RXhjaGFuZ2VQYXlsb2FkID0ge1xuICAgICAgICBzZXNzaW9uS2V5SWQsXG4gICAgICAgIGNvbnZlcnNhdGlvblR5cGU6IHBhcmFtcy50eXBlLFxuICAgICAgICBjb252ZXJzYXRpb25JZDogcGFyYW1zLmNvbnZlcnNhdGlvbklkLFxuICAgICAgICBnZW5lcmF0aW9uOiBwYXJhbXMuZ2VuZXJhdGlvbixcbiAgICAgICAgbWVtYmVyUGF5bG9hZHNcbiAgICAgIH07XG5cbiAgICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IHRoaXMuaHR0cEZldGNoKGAke2FwaVVybH0vYXBpL2UyZS9zZXNzaW9uLWtleXNgLCB7XG4gICAgICAgICAgbWV0aG9kOiAnUE9TVCcsXG4gICAgICAgICAgaGVhZGVyczoge1xuICAgICAgICAgICAgQXV0aG9yaXphdGlvbjogYEJlYXJlciAke3Rva2VufWAsXG4gICAgICAgICAgICAnQ29udGVudC1UeXBlJzogJ2FwcGxpY2F0aW9uL2pzb24nXG4gICAgICAgICAgfSxcbiAgICAgICAgICBib2R5OiBKU09OLnN0cmluZ2lmeShwYXlsb2FkKVxuICAgICAgICB9KTtcbiAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICBjb25zb2xlLndhcm4oXG4gICAgICAgICAgJ1tTZXNzaW9uS2V5TWFuYWdlcl0gRmFpbGVkIHRvIHN5bmMgc2Vzc2lvbiBrZXkgdG8gc2VydmVyOicsXG4gICAgICAgICAgZXJyXG4gICAgICAgICk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIHNlc3Npb25LZXk7XG4gIH1cblxuICAvKipcbiAgICogSGVscGVyIHRvIGNoZWNrIGlmIGEga2V5IGhhcyBleGNlZWRlZCB0aGUgMzAtZGF5IHJvdGF0aW9uIHRocmVzaG9sZC5cbiAgICovXG4gIGlzS2V5RXhwaXJlZChrZXk6IENvbnZlcnNhdGlvblNlc3Npb25LZXkpOiBib29sZWFuIHtcbiAgICByZXR1cm4gRGF0ZS5ub3coKSAtIGtleS5jcmVhdGVkQXQgPiBUSElSVFlfREFZU19NUztcbiAgfVxuXG4gIC8qKlxuICAgKiBCdWlsZHMgYSBkZXRlcm1pbmlzdGljIGNvbnZlcnNhdGlvbiBpZGVudGlmaWVyIGZvciAxOjEgRE1zIChsZXhpY29ncmFwaGljYWwgc29ydCkuXG4gICAqL1xuICBidWlsZERtQ29udmVyc2F0aW9uSWQodXNlcklkQTogc3RyaW5nLCB1c2VySWRCOiBzdHJpbmcpOiBzdHJpbmcge1xuICAgIHJldHVybiBbdXNlcklkQSwgdXNlcklkQl0uc29ydCgpLmpvaW4oJ18nKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDYWNoZXMgYSBzZXNzaW9uIGtleSBpbiBtZW1vcnkuXG4gICAqL1xuICBwcml2YXRlIGNhY2hlS2V5KGtleTogQ29udmVyc2F0aW9uU2Vzc2lvbktleSk6IHZvaWQge1xuICAgIHRoaXMuYWN0aXZlS2V5c0J5Q29udmVyc2F0aW9uLnNldChrZXkuY29udmVyc2F0aW9uSWQsIGtleSk7XG4gICAgdGhpcy5rZXlzQnlJZC5zZXQoa2V5LmlkLCBrZXkpO1xuICB9XG5cbiAgLyoqXG4gICAqIFBlcnNpc3RzIGFuIHVud3JhcHBlZCBzZXNzaW9uIGtleSB0byBsb2NhbCBzdG9yYWdlIGZvciBmYXN0IHN0YXJ0dXAuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIHBlcnNpc3RLZXlMb2NhbGx5KFxuICAgIGtleTogQ29udmVyc2F0aW9uU2Vzc2lvbktleVxuICApOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBpZiAoIXRoaXMuc3RvcmFnZSkgcmV0dXJuO1xuICAgIHRyeSB7XG4gICAgICBjb25zdCByYXdLZXlCYXNlNjQgPSBhd2FpdCB0aGlzLmNyeXB0b1NlcnZpY2UuZXhwb3J0UmF3S2V5QmFzZTY0KFxuICAgICAgICBrZXkucmF3S2V5XG4gICAgICApO1xuICAgICAgY29uc3QgZGF0YSA9IEpTT04uc3RyaW5naWZ5KHtcbiAgICAgICAgaWQ6IGtleS5pZCxcbiAgICAgICAgdHlwZToga2V5LnR5cGUsXG4gICAgICAgIGNvbnZlcnNhdGlvbklkOiBrZXkuY29udmVyc2F0aW9uSWQsXG4gICAgICAgIHJhd0tleUJhc2U2NCxcbiAgICAgICAgZ2VuZXJhdGlvbjoga2V5LmdlbmVyYXRpb24sXG4gICAgICAgIGNyZWF0ZWRBdDoga2V5LmNyZWF0ZWRBdFxuICAgICAgfSk7XG4gICAgICBhd2FpdCB0aGlzLnN0b3JhZ2Uuc2V0SXRlbShga29qaW54X3NrXyR7a2V5LmlkfWAsIGRhdGEpO1xuICAgIH0gY2F0Y2gge1xuICAgICAgLy8gaWdub3JlIHBlcnNpc3RlbmNlIGVycm9yXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEZldGNoZXMgbGF0ZXN0IHNlc3Npb24ga2V5IGZyb20gc2VydmVyIGFuZCB1bndyYXBzIGl0IGxvY2FsbHkuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGZldGNoU2Vzc2lvbktleUZyb21TZXJ2ZXIoXG4gICAgY29udmVyc2F0aW9uSWQ6IHN0cmluZyxcbiAgICB0eXBlOiAnZG0nIHwgJ2NoYW5uZWwnIHwgJ2dyb3VwJ1xuICApOiBQcm9taXNlPENvbnZlcnNhdGlvblNlc3Npb25LZXkgfCBudWxsPiB7XG4gICAgaWYgKCF0aGlzLmh0dHBGZXRjaCB8fCAhdGhpcy5nZXRBcGlVcmwgfHwgIXRoaXMuZ2V0QXV0aFRva2VuKSByZXR1cm4gbnVsbDtcblxuICAgIHRyeSB7XG4gICAgICBjb25zdCB0b2tlbiA9IGF3YWl0IHRoaXMuZ2V0QXV0aFRva2VuKCk7XG4gICAgICBjb25zdCBhcGlVcmwgPSB0aGlzLmdldEFwaVVybCgpO1xuICAgICAgY29uc3QgcmVzID0gYXdhaXQgdGhpcy5odHRwRmV0Y2g8e1xuICAgICAgICBzZXNzaW9uS2V5SWQ6IHN0cmluZztcbiAgICAgICAgZ2VuZXJhdGlvbjogbnVtYmVyO1xuICAgICAgICBlbmNyeXB0ZWRLZXk6IHN0cmluZztcbiAgICAgICAgY3JlYXRlZEF0Pzogc3RyaW5nO1xuICAgICAgfT4oYCR7YXBpVXJsfS9hcGkvZTJlL3Nlc3Npb24ta2V5cy8ke2NvbnZlcnNhdGlvbklkfWAsIHtcbiAgICAgICAgbWV0aG9kOiAnR0VUJyxcbiAgICAgICAgaGVhZGVyczoge1xuICAgICAgICAgIEF1dGhvcml6YXRpb246IGBCZWFyZXIgJHt0b2tlbn1gXG4gICAgICAgIH1cbiAgICAgIH0pO1xuXG4gICAgICBpZiAocmVzLnN0YXR1cyA9PT0gMjAwICYmIHJlcy5kYXRhPy5lbmNyeXB0ZWRLZXkpIHtcbiAgICAgICAgY29uc3QgcmF3S2V5ID0gYXdhaXQgdGhpcy5jcnlwdG9TZXJ2aWNlLnVud3JhcFNlc3Npb25LZXkoXG4gICAgICAgICAgcmVzLmRhdGEuZW5jcnlwdGVkS2V5XG4gICAgICAgICk7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgaWQ6IHJlcy5kYXRhLnNlc3Npb25LZXlJZCxcbiAgICAgICAgICB0eXBlLFxuICAgICAgICAgIGNvbnZlcnNhdGlvbklkLFxuICAgICAgICAgIHJhd0tleSxcbiAgICAgICAgICBnZW5lcmF0aW9uOiByZXMuZGF0YS5nZW5lcmF0aW9uIHx8IDEsXG4gICAgICAgICAgY3JlYXRlZEF0OiByZXMuZGF0YS5jcmVhdGVkQXRcbiAgICAgICAgICAgID8gbmV3IERhdGUocmVzLmRhdGEuY3JlYXRlZEF0KS5nZXRUaW1lKClcbiAgICAgICAgICAgIDogRGF0ZS5ub3coKVxuICAgICAgICB9O1xuICAgICAgfVxuICAgIH0gY2F0Y2gge1xuICAgICAgLy8gTm90IGZvdW5kIG9yIG5ldHdvcmsgZXJyb3JcbiAgICB9XG5cbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZW5lcmF0ZXMgYSByYW5kb20gVVVJRCAoVzNDIHN0YW5kYXJkIG9yIGZhbGxiYWNrKS5cbiAgICovXG4gIHByaXZhdGUgZ2VuZXJhdGVVdWlkKCk6IHN0cmluZyB7XG4gICAgaWYgKHR5cGVvZiBjcnlwdG8gIT09ICd1bmRlZmluZWQnICYmIGNyeXB0by5yYW5kb21VVUlEKSB7XG4gICAgICByZXR1cm4gY3J5cHRvLnJhbmRvbVVVSUQoKTtcbiAgICB9XG4gICAgcmV0dXJuICd4eHh4eHh4eC14eHh4LTR4eHgteXh4eC14eHh4eHh4eHh4eHgnLnJlcGxhY2UoL1t4eV0vZywgYyA9PiB7XG4gICAgICBjb25zdCByID0gKE1hdGgucmFuZG9tKCkgKiAxNikgfCAwO1xuICAgICAgY29uc3QgdiA9IGMgPT09ICd4JyA/IHIgOiAociAmIDB4MykgfCAweDg7XG4gICAgICByZXR1cm4gdi50b1N0cmluZygxNik7XG4gICAgfSk7XG4gIH1cbn1cbiJdfQ==
@@ -0,0 +1,36 @@
1
+ import { InjectionToken } from '@angular/core';
2
+ /**
3
+ * Injected Asymmetric Cryptography Provider (RSA-2048 OAEP).
4
+ * On desktop: Implemented via Tauri Rust Keyring IPC.
5
+ * On mobile: Implemented via SubtleCrypto + Capacitor SecureStorage.
6
+ */
7
+ export const KOJINX_CRYPTO_PROVIDER = new InjectionToken('KOJINX_CRYPTO_PROVIDER');
8
+ /**
9
+ * Injected Client-Agnostic HTTP Fetcher (bypass CORS on native).
10
+ */
11
+ export const KOJINX_HTTP_FETCH = new InjectionToken('KOJINX_HTTP_FETCH');
12
+ /**
13
+ * Injected Storage Provider for local session key caching.
14
+ */
15
+ export const KOJINX_CRYPTO_STORAGE = new InjectionToken('KOJINX_CRYPTO_STORAGE', {
16
+ factory: () => ({
17
+ getItem: (key) => Promise.resolve(localStorage.getItem(key)),
18
+ setItem: (key, value) => {
19
+ localStorage.setItem(key, value);
20
+ return Promise.resolve();
21
+ },
22
+ removeItem: (key) => {
23
+ localStorage.removeItem(key);
24
+ return Promise.resolve();
25
+ }
26
+ })
27
+ });
28
+ /**
29
+ * Injected API Base URL accessor function.
30
+ */
31
+ export const KOJINX_API_BASE_URL = new InjectionToken('KOJINX_API_BASE_URL');
32
+ /**
33
+ * Injected Auth Bearer Token accessor function.
34
+ */
35
+ export const KOJINX_AUTH_TOKEN = new InjectionToken('KOJINX_AUTH_TOKEN');
36
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG9rZW5zLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMva29qaW54L2NvbGxhYi1jcnlwdG8vc3JjL2xpYi90b2tlbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQU8vQzs7OztHQUlHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sc0JBQXNCLEdBQUcsSUFBSSxjQUFjLENBQ3RELHdCQUF3QixDQUN6QixDQUFDO0FBRUY7O0dBRUc7QUFDSCxNQUFNLENBQUMsTUFBTSxpQkFBaUIsR0FBRyxJQUFJLGNBQWMsQ0FDakQsbUJBQW1CLENBQ3BCLENBQUM7QUFFRjs7R0FFRztBQUNILE1BQU0sQ0FBQyxNQUFNLHFCQUFxQixHQUFHLElBQUksY0FBYyxDQUNyRCx1QkFBdUIsRUFDdkI7SUFDRSxPQUFPLEVBQUUsR0FBRyxFQUFFLENBQUMsQ0FBQztRQUNkLE9BQU8sRUFBRSxDQUFDLEdBQVcsRUFBRSxFQUFFLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxZQUFZLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQ3BFLE9BQU8sRUFBRSxDQUFDLEdBQVcsRUFBRSxLQUFhLEVBQUUsRUFBRTtZQUN0QyxZQUFZLENBQUMsT0FBTyxDQUFDLEdBQUcsRUFBRSxLQUFLLENBQUMsQ0FBQztZQUNqQyxPQUFPLE9BQU8sQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUMzQixDQUFDO1FBQ0QsVUFBVSxFQUFFLENBQUMsR0FBVyxFQUFFLEVBQUU7WUFDMUIsWUFBWSxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUM3QixPQUFPLE9BQU8sQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUMzQixDQUFDO0tBQ0YsQ0FBQztDQUNILENBQ0YsQ0FBQztBQUVGOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sbUJBQW1CLEdBQUcsSUFBSSxjQUFjLENBQ25ELHFCQUFxQixDQUN0QixDQUFDO0FBRUY7O0dBRUc7QUFDSCxNQUFNLENBQUMsTUFBTSxpQkFBaUIsR0FBRyxJQUFJLGNBQWMsQ0FDakQsbUJBQW1CLENBQ3BCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBJbmplY3Rpb25Ub2tlbiB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHtcbiAgS29qaW54Q3J5cHRvUHJvdmlkZXIsXG4gIEtvamlueEZldGNoRnVuY3Rpb24sXG4gIEtvamlueFN0b3JhZ2VQcm92aWRlclxufSBmcm9tICcuL21vZGVscy9jcnlwdG8ubW9kZWxzJztcblxuLyoqXG4gKiBJbmplY3RlZCBBc3ltbWV0cmljIENyeXB0b2dyYXBoeSBQcm92aWRlciAoUlNBLTIwNDggT0FFUCkuXG4gKiBPbiBkZXNrdG9wOiBJbXBsZW1lbnRlZCB2aWEgVGF1cmkgUnVzdCBLZXlyaW5nIElQQy5cbiAqIE9uIG1vYmlsZTogSW1wbGVtZW50ZWQgdmlhIFN1YnRsZUNyeXB0byArIENhcGFjaXRvciBTZWN1cmVTdG9yYWdlLlxuICovXG5leHBvcnQgY29uc3QgS09KSU5YX0NSWVBUT19QUk9WSURFUiA9IG5ldyBJbmplY3Rpb25Ub2tlbjxLb2ppbnhDcnlwdG9Qcm92aWRlcj4oXG4gICdLT0pJTlhfQ1JZUFRPX1BST1ZJREVSJ1xuKTtcblxuLyoqXG4gKiBJbmplY3RlZCBDbGllbnQtQWdub3N0aWMgSFRUUCBGZXRjaGVyIChieXBhc3MgQ09SUyBvbiBuYXRpdmUpLlxuICovXG5leHBvcnQgY29uc3QgS09KSU5YX0hUVFBfRkVUQ0ggPSBuZXcgSW5qZWN0aW9uVG9rZW48S29qaW54RmV0Y2hGdW5jdGlvbj4oXG4gICdLT0pJTlhfSFRUUF9GRVRDSCdcbik7XG5cbi8qKlxuICogSW5qZWN0ZWQgU3RvcmFnZSBQcm92aWRlciBmb3IgbG9jYWwgc2Vzc2lvbiBrZXkgY2FjaGluZy5cbiAqL1xuZXhwb3J0IGNvbnN0IEtPSklOWF9DUllQVE9fU1RPUkFHRSA9IG5ldyBJbmplY3Rpb25Ub2tlbjxLb2ppbnhTdG9yYWdlUHJvdmlkZXI+KFxuICAnS09KSU5YX0NSWVBUT19TVE9SQUdFJyxcbiAge1xuICAgIGZhY3Rvcnk6ICgpID0+ICh7XG4gICAgICBnZXRJdGVtOiAoa2V5OiBzdHJpbmcpID0+IFByb21pc2UucmVzb2x2ZShsb2NhbFN0b3JhZ2UuZ2V0SXRlbShrZXkpKSxcbiAgICAgIHNldEl0ZW06IChrZXk6IHN0cmluZywgdmFsdWU6IHN0cmluZykgPT4ge1xuICAgICAgICBsb2NhbFN0b3JhZ2Uuc2V0SXRlbShrZXksIHZhbHVlKTtcbiAgICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZSgpO1xuICAgICAgfSxcbiAgICAgIHJlbW92ZUl0ZW06IChrZXk6IHN0cmluZykgPT4ge1xuICAgICAgICBsb2NhbFN0b3JhZ2UucmVtb3ZlSXRlbShrZXkpO1xuICAgICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKCk7XG4gICAgICB9XG4gICAgfSlcbiAgfVxuKTtcblxuLyoqXG4gKiBJbmplY3RlZCBBUEkgQmFzZSBVUkwgYWNjZXNzb3IgZnVuY3Rpb24uXG4gKi9cbmV4cG9ydCBjb25zdCBLT0pJTlhfQVBJX0JBU0VfVVJMID0gbmV3IEluamVjdGlvblRva2VuPCgpID0+IHN0cmluZz4oXG4gICdLT0pJTlhfQVBJX0JBU0VfVVJMJ1xuKTtcblxuLyoqXG4gKiBJbmplY3RlZCBBdXRoIEJlYXJlciBUb2tlbiBhY2Nlc3NvciBmdW5jdGlvbi5cbiAqL1xuZXhwb3J0IGNvbnN0IEtPSklOWF9BVVRIX1RPS0VOID0gbmV3IEluamVjdGlvblRva2VuPCgpID0+IFByb21pc2U8c3RyaW5nIHwgbnVsbD4+KFxuICAnS09KSU5YX0FVVEhfVE9LRU4nXG4pO1xuIl19
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Computes deterministic SHA-256 safety verification numbers (Signal-style).
3
+ * Users can compare these 30-digit numbers out-of-band to verify end-to-end identity.
4
+ */
5
+ export class SafetyNumberUtils {
6
+ /**
7
+ * Generates a Safety Number between two identity public keys.
8
+ *
9
+ * @param localPublicKey Base64-encoded local RSA public key
10
+ * @param remotePublicKey Base64-encoded remote RSA public key
11
+ */
12
+ static async generate(localPublicKey, remotePublicKey) {
13
+ if (!localPublicKey || !remotePublicKey) {
14
+ throw new Error('Both public keys are required to generate safety numbers.');
15
+ }
16
+ const localFingerprint = await this.hashPublicKey(localPublicKey);
17
+ const remoteFingerprint = await this.hashPublicKey(remotePublicKey);
18
+ // Sort lexicographically to ensure order-independent verification
19
+ const sorted = [localFingerprint, remoteFingerprint].sort();
20
+ const combinedDigest = await this.sha256(sorted.join(''));
21
+ // Convert hex digest to a 30-digit decimal representation
22
+ const digits = this.hexTo30Digits(combinedDigest);
23
+ const formatted = this.formatBlocks(digits);
24
+ return {
25
+ raw: combinedDigest,
26
+ formatted,
27
+ localFingerprint: localFingerprint.substring(0, 16),
28
+ remoteFingerprint: remoteFingerprint.substring(0, 16)
29
+ };
30
+ }
31
+ /**
32
+ * Hashes a single public key using SHA-256.
33
+ */
34
+ static async hashPublicKey(publicKeyBase64) {
35
+ const cleanKey = publicKeyBase64.replace(/\s+/g, '');
36
+ return this.sha256(cleanKey);
37
+ }
38
+ /**
39
+ * Computes SHA-256 hex string using W3C Web Crypto API.
40
+ */
41
+ static async sha256(input) {
42
+ const encoder = new TextEncoder();
43
+ const data = encoder.encode(input);
44
+ const hashBuffer = await crypto.subtle.digest('SHA-256', data);
45
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
46
+ return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
47
+ }
48
+ /**
49
+ * Converts a hexadecimal string into a deterministic 30-digit decimal string.
50
+ */
51
+ static hexTo30Digits(hex) {
52
+ let result = '';
53
+ // Take chunks of 8 hex characters (32-bit integers) to produce 5-digit decimal blocks
54
+ for (let i = 0; i < 6; i++) {
55
+ const chunk = hex.substring(i * 8, (i + 1) * 8);
56
+ const num = parseInt(chunk, 16) % 100000;
57
+ result += num.toString().padStart(5, '0');
58
+ }
59
+ return result;
60
+ }
61
+ /**
62
+ * Formats a 30-digit string into 6 blocks of 5 digits separated by spaces.
63
+ */
64
+ static formatBlocks(digits) {
65
+ const blocks = [];
66
+ for (let i = 0; i < digits.length; i += 5) {
67
+ blocks.push(digits.substring(i, i + 5));
68
+ }
69
+ return blocks.join(' ');
70
+ }
71
+ }
72
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2FmZXR5LW51bWJlci51dGlscy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL2tvamlueC9jb2xsYWItY3J5cHRvL3NyYy9saWIvdXRpbHMvc2FmZXR5LW51bWJlci51dGlscy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFFQTs7O0dBR0c7QUFDSCxNQUFNLE9BQU8saUJBQWlCO0lBQzVCOzs7OztPQUtHO0lBQ0gsTUFBTSxDQUFDLEtBQUssQ0FBQyxRQUFRLENBQ25CLGNBQXNCLEVBQ3RCLGVBQXVCO1FBRXZCLElBQUksQ0FBQyxjQUFjLElBQUksQ0FBQyxlQUFlLEVBQUUsQ0FBQztZQUN4QyxNQUFNLElBQUksS0FBSyxDQUFDLDJEQUEyRCxDQUFDLENBQUM7UUFDL0UsQ0FBQztRQUVELE1BQU0sZ0JBQWdCLEdBQUcsTUFBTSxJQUFJLENBQUMsYUFBYSxDQUFDLGNBQWMsQ0FBQyxDQUFDO1FBQ2xFLE1BQU0saUJBQWlCLEdBQUcsTUFBTSxJQUFJLENBQUMsYUFBYSxDQUFDLGVBQWUsQ0FBQyxDQUFDO1FBRXBFLGtFQUFrRTtRQUNsRSxNQUFNLE1BQU0sR0FBRyxDQUFDLGdCQUFnQixFQUFFLGlCQUFpQixDQUFDLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDNUQsTUFBTSxjQUFjLEdBQUcsTUFBTSxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQztRQUUxRCwwREFBMEQ7UUFDMUQsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLGFBQWEsQ0FBQyxjQUFjLENBQUMsQ0FBQztRQUNsRCxNQUFNLFNBQVMsR0FBRyxJQUFJLENBQUMsWUFBWSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBRTVDLE9BQU87WUFDTCxHQUFHLEVBQUUsY0FBYztZQUNuQixTQUFTO1lBQ1QsZ0JBQWdCLEVBQUUsZ0JBQWdCLENBQUMsU0FBUyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUM7WUFDbkQsaUJBQWlCLEVBQUUsaUJBQWlCLENBQUMsU0FBUyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUM7U0FDdEQsQ0FBQztJQUNKLENBQUM7SUFFRDs7T0FFRztJQUNILE1BQU0sQ0FBQyxLQUFLLENBQUMsYUFBYSxDQUFDLGVBQXVCO1FBQ2hELE1BQU0sUUFBUSxHQUFHLGVBQWUsQ0FBQyxPQUFPLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQyxDQUFDO1FBQ3JELE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUMvQixDQUFDO0lBRUQ7O09BRUc7SUFDSyxNQUFNLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxLQUFhO1FBQ3ZDLE1BQU0sT0FBTyxHQUFHLElBQUksV0FBVyxFQUFFLENBQUM7UUFDbEMsTUFBTSxJQUFJLEdBQUcsT0FBTyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUNuQyxNQUFNLFVBQVUsR0FBRyxNQUFNLE1BQU0sQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLFNBQVMsRUFBRSxJQUFJLENBQUMsQ0FBQztRQUMvRCxNQUFNLFNBQVMsR0FBRyxLQUFLLENBQUMsSUFBSSxDQUFDLElBQUksVUFBVSxDQUFDLFVBQVUsQ0FBQyxDQUFDLENBQUM7UUFDekQsT0FBTyxTQUFTLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUMsQ0FBQyxRQUFRLENBQUMsQ0FBQyxFQUFFLEdBQUcsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBQ3RFLENBQUM7SUFFRDs7T0FFRztJQUNLLE1BQU0sQ0FBQyxhQUFhLENBQUMsR0FBVztRQUN0QyxJQUFJLE1BQU0sR0FBRyxFQUFFLENBQUM7UUFDaEIsc0ZBQXNGO1FBQ3RGLEtBQUssSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQztZQUMzQixNQUFNLEtBQUssR0FBRyxHQUFHLENBQUMsU0FBUyxDQUFDLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7WUFDaEQsTUFBTSxHQUFHLEdBQUcsUUFBUSxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsR0FBRyxNQUFNLENBQUM7WUFDekMsTUFBTSxJQUFJLEdBQUcsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxRQUFRLENBQUMsQ0FBQyxFQUFFLEdBQUcsQ0FBQyxDQUFDO1FBQzVDLENBQUM7UUFDRCxPQUFPLE1BQU0sQ0FBQztJQUNoQixDQUFDO0lBRUQ7O09BRUc7SUFDSyxNQUFNLENBQUMsWUFBWSxDQUFDLE1BQWM7UUFDeEMsTUFBTSxNQUFNLEdBQWEsRUFBRSxDQUFDO1FBQzVCLEtBQUssSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxNQUFNLENBQUMsTUFBTSxFQUFFLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQztZQUMxQyxNQUFNLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsQ0FBQyxFQUFFLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQzFDLENBQUM7UUFDRCxPQUFPLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDMUIsQ0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgU2FmZXR5TnVtYmVyIH0gZnJvbSAnLi4vbW9kZWxzL2NyeXB0by5tb2RlbHMnO1xuXG4vKipcbiAqIENvbXB1dGVzIGRldGVybWluaXN0aWMgU0hBLTI1NiBzYWZldHkgdmVyaWZpY2F0aW9uIG51bWJlcnMgKFNpZ25hbC1zdHlsZSkuXG4gKiBVc2VycyBjYW4gY29tcGFyZSB0aGVzZSAzMC1kaWdpdCBudW1iZXJzIG91dC1vZi1iYW5kIHRvIHZlcmlmeSBlbmQtdG8tZW5kIGlkZW50aXR5LlxuICovXG5leHBvcnQgY2xhc3MgU2FmZXR5TnVtYmVyVXRpbHMge1xuICAvKipcbiAgICogR2VuZXJhdGVzIGEgU2FmZXR5IE51bWJlciBiZXR3ZWVuIHR3byBpZGVudGl0eSBwdWJsaWMga2V5cy5cbiAgICpcbiAgICogQHBhcmFtIGxvY2FsUHVibGljS2V5IEJhc2U2NC1lbmNvZGVkIGxvY2FsIFJTQSBwdWJsaWMga2V5XG4gICAqIEBwYXJhbSByZW1vdGVQdWJsaWNLZXkgQmFzZTY0LWVuY29kZWQgcmVtb3RlIFJTQSBwdWJsaWMga2V5XG4gICAqL1xuICBzdGF0aWMgYXN5bmMgZ2VuZXJhdGUoXG4gICAgbG9jYWxQdWJsaWNLZXk6IHN0cmluZyxcbiAgICByZW1vdGVQdWJsaWNLZXk6IHN0cmluZ1xuICApOiBQcm9taXNlPFNhZmV0eU51bWJlcj4ge1xuICAgIGlmICghbG9jYWxQdWJsaWNLZXkgfHwgIXJlbW90ZVB1YmxpY0tleSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdCb3RoIHB1YmxpYyBrZXlzIGFyZSByZXF1aXJlZCB0byBnZW5lcmF0ZSBzYWZldHkgbnVtYmVycy4nKTtcbiAgICB9XG5cbiAgICBjb25zdCBsb2NhbEZpbmdlcnByaW50ID0gYXdhaXQgdGhpcy5oYXNoUHVibGljS2V5KGxvY2FsUHVibGljS2V5KTtcbiAgICBjb25zdCByZW1vdGVGaW5nZXJwcmludCA9IGF3YWl0IHRoaXMuaGFzaFB1YmxpY0tleShyZW1vdGVQdWJsaWNLZXkpO1xuXG4gICAgLy8gU29ydCBsZXhpY29ncmFwaGljYWxseSB0byBlbnN1cmUgb3JkZXItaW5kZXBlbmRlbnQgdmVyaWZpY2F0aW9uXG4gICAgY29uc3Qgc29ydGVkID0gW2xvY2FsRmluZ2VycHJpbnQsIHJlbW90ZUZpbmdlcnByaW50XS5zb3J0KCk7XG4gICAgY29uc3QgY29tYmluZWREaWdlc3QgPSBhd2FpdCB0aGlzLnNoYTI1Nihzb3J0ZWQuam9pbignJykpO1xuXG4gICAgLy8gQ29udmVydCBoZXggZGlnZXN0IHRvIGEgMzAtZGlnaXQgZGVjaW1hbCByZXByZXNlbnRhdGlvblxuICAgIGNvbnN0IGRpZ2l0cyA9IHRoaXMuaGV4VG8zMERpZ2l0cyhjb21iaW5lZERpZ2VzdCk7XG4gICAgY29uc3QgZm9ybWF0dGVkID0gdGhpcy5mb3JtYXRCbG9ja3MoZGlnaXRzKTtcblxuICAgIHJldHVybiB7XG4gICAgICByYXc6IGNvbWJpbmVkRGlnZXN0LFxuICAgICAgZm9ybWF0dGVkLFxuICAgICAgbG9jYWxGaW5nZXJwcmludDogbG9jYWxGaW5nZXJwcmludC5zdWJzdHJpbmcoMCwgMTYpLFxuICAgICAgcmVtb3RlRmluZ2VycHJpbnQ6IHJlbW90ZUZpbmdlcnByaW50LnN1YnN0cmluZygwLCAxNilcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIEhhc2hlcyBhIHNpbmdsZSBwdWJsaWMga2V5IHVzaW5nIFNIQS0yNTYuXG4gICAqL1xuICBzdGF0aWMgYXN5bmMgaGFzaFB1YmxpY0tleShwdWJsaWNLZXlCYXNlNjQ6IHN0cmluZyk6IFByb21pc2U8c3RyaW5nPiB7XG4gICAgY29uc3QgY2xlYW5LZXkgPSBwdWJsaWNLZXlCYXNlNjQucmVwbGFjZSgvXFxzKy9nLCAnJyk7XG4gICAgcmV0dXJuIHRoaXMuc2hhMjU2KGNsZWFuS2V5KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBTSEEtMjU2IGhleCBzdHJpbmcgdXNpbmcgVzNDIFdlYiBDcnlwdG8gQVBJLlxuICAgKi9cbiAgcHJpdmF0ZSBzdGF0aWMgYXN5bmMgc2hhMjU2KGlucHV0OiBzdHJpbmcpOiBQcm9taXNlPHN0cmluZz4ge1xuICAgIGNvbnN0IGVuY29kZXIgPSBuZXcgVGV4dEVuY29kZXIoKTtcbiAgICBjb25zdCBkYXRhID0gZW5jb2Rlci5lbmNvZGUoaW5wdXQpO1xuICAgIGNvbnN0IGhhc2hCdWZmZXIgPSBhd2FpdCBjcnlwdG8uc3VidGxlLmRpZ2VzdCgnU0hBLTI1NicsIGRhdGEpO1xuICAgIGNvbnN0IGhhc2hBcnJheSA9IEFycmF5LmZyb20obmV3IFVpbnQ4QXJyYXkoaGFzaEJ1ZmZlcikpO1xuICAgIHJldHVybiBoYXNoQXJyYXkubWFwKGIgPT4gYi50b1N0cmluZygxNikucGFkU3RhcnQoMiwgJzAnKSkuam9pbignJyk7XG4gIH1cblxuICAvKipcbiAgICogQ29udmVydHMgYSBoZXhhZGVjaW1hbCBzdHJpbmcgaW50byBhIGRldGVybWluaXN0aWMgMzAtZGlnaXQgZGVjaW1hbCBzdHJpbmcuXG4gICAqL1xuICBwcml2YXRlIHN0YXRpYyBoZXhUbzMwRGlnaXRzKGhleDogc3RyaW5nKTogc3RyaW5nIHtcbiAgICBsZXQgcmVzdWx0ID0gJyc7XG4gICAgLy8gVGFrZSBjaHVua3Mgb2YgOCBoZXggY2hhcmFjdGVycyAoMzItYml0IGludGVnZXJzKSB0byBwcm9kdWNlIDUtZGlnaXQgZGVjaW1hbCBibG9ja3NcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IDY7IGkrKykge1xuICAgICAgY29uc3QgY2h1bmsgPSBoZXguc3Vic3RyaW5nKGkgKiA4LCAoaSArIDEpICogOCk7XG4gICAgICBjb25zdCBudW0gPSBwYXJzZUludChjaHVuaywgMTYpICUgMTAwMDAwO1xuICAgICAgcmVzdWx0ICs9IG51bS50b1N0cmluZygpLnBhZFN0YXJ0KDUsICcwJyk7XG4gICAgfVxuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cblxuICAvKipcbiAgICogRm9ybWF0cyBhIDMwLWRpZ2l0IHN0cmluZyBpbnRvIDYgYmxvY2tzIG9mIDUgZGlnaXRzIHNlcGFyYXRlZCBieSBzcGFjZXMuXG4gICAqL1xuICBwcml2YXRlIHN0YXRpYyBmb3JtYXRCbG9ja3MoZGlnaXRzOiBzdHJpbmcpOiBzdHJpbmcge1xuICAgIGNvbnN0IGJsb2Nrczogc3RyaW5nW10gPSBbXTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGRpZ2l0cy5sZW5ndGg7IGkgKz0gNSkge1xuICAgICAgYmxvY2tzLnB1c2goZGlnaXRzLnN1YnN0cmluZyhpLCBpICsgNSkpO1xuICAgIH1cbiAgICByZXR1cm4gYmxvY2tzLmpvaW4oJyAnKTtcbiAgfVxufVxuIl19
@@ -0,0 +1,9 @@
1
+ /*
2
+ * Public API Surface of @kojinx/collab-crypto
3
+ */
4
+ export * from './lib/tokens';
5
+ export * from './lib/models/crypto.models';
6
+ export * from './lib/utils/safety-number.utils';
7
+ export * from './lib/services/e2e-chat-crypto.service';
8
+ export * from './lib/services/session-key-manager.service';
9
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHVibGljLWFwaS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3Byb2plY3RzL2tvamlueC9jb2xsYWItY3J5cHRvL3NyYy9wdWJsaWMtYXBpLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBRUgsY0FBYyxjQUFjLENBQUM7QUFDN0IsY0FBYyw0QkFBNEIsQ0FBQztBQUMzQyxjQUFjLGlDQUFpQyxDQUFDO0FBQ2hELGNBQWMsd0NBQXdDLENBQUM7QUFDdkQsY0FBYyw0Q0FBNEMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qXG4gKiBQdWJsaWMgQVBJIFN1cmZhY2Ugb2YgQGtvamlueC9jb2xsYWItY3J5cHRvXG4gKi9cblxuZXhwb3J0ICogZnJvbSAnLi9saWIvdG9rZW5zJztcbmV4cG9ydCAqIGZyb20gJy4vbGliL21vZGVscy9jcnlwdG8ubW9kZWxzJztcbmV4cG9ydCAqIGZyb20gJy4vbGliL3V0aWxzL3NhZmV0eS1udW1iZXIudXRpbHMnO1xuZXhwb3J0ICogZnJvbSAnLi9saWIvc2VydmljZXMvZTJlLWNoYXQtY3J5cHRvLnNlcnZpY2UnO1xuZXhwb3J0ICogZnJvbSAnLi9saWIvc2VydmljZXMvc2Vzc2lvbi1rZXktbWFuYWdlci5zZXJ2aWNlJztcbiJdfQ==