@towns-protocol/encryption 0.0.191
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.
- package/LICENSE.txt +21 -0
- package/README.md +3 -0
- package/dist/base.d.ts +64 -0
- package/dist/base.d.ts.map +1 -0
- package/dist/base.js +44 -0
- package/dist/base.js.map +1 -0
- package/dist/cryptoAesGcm.d.ts +9 -0
- package/dist/cryptoAesGcm.d.ts.map +1 -0
- package/dist/cryptoAesGcm.js +30 -0
- package/dist/cryptoAesGcm.js.map +1 -0
- package/dist/cryptoStore.d.ts +52 -0
- package/dist/cryptoStore.d.ts.map +1 -0
- package/dist/cryptoStore.js +131 -0
- package/dist/cryptoStore.js.map +1 -0
- package/dist/decryptionExtensions.d.ts +200 -0
- package/dist/decryptionExtensions.d.ts.map +1 -0
- package/dist/decryptionExtensions.js +687 -0
- package/dist/decryptionExtensions.js.map +1 -0
- package/dist/derivedEncryption.d.ts +2 -0
- package/dist/derivedEncryption.d.ts.map +1 -0
- package/dist/derivedEncryption.js +2 -0
- package/dist/derivedEncryption.js.map +1 -0
- package/dist/encryptionDelegate.d.ts +20 -0
- package/dist/encryptionDelegate.d.ts.map +1 -0
- package/dist/encryptionDelegate.js +86 -0
- package/dist/encryptionDelegate.js.map +1 -0
- package/dist/encryptionDevice.d.ts +264 -0
- package/dist/encryptionDevice.d.ts.map +1 -0
- package/dist/encryptionDevice.js +742 -0
- package/dist/encryptionDevice.js.map +1 -0
- package/dist/encryptionTypes.d.ts +20 -0
- package/dist/encryptionTypes.d.ts.map +1 -0
- package/dist/encryptionTypes.js +2 -0
- package/dist/encryptionTypes.js.map +1 -0
- package/dist/groupDecryption.d.ts +34 -0
- package/dist/groupDecryption.d.ts.map +1 -0
- package/dist/groupDecryption.js +84 -0
- package/dist/groupDecryption.js.map +1 -0
- package/dist/groupEncryption.d.ts +36 -0
- package/dist/groupEncryption.d.ts.map +1 -0
- package/dist/groupEncryption.js +90 -0
- package/dist/groupEncryption.js.map +1 -0
- package/dist/groupEncryptionCrypto.d.ts +119 -0
- package/dist/groupEncryptionCrypto.d.ts.map +1 -0
- package/dist/groupEncryptionCrypto.js +256 -0
- package/dist/groupEncryptionCrypto.js.map +1 -0
- package/dist/hybridGroupDecryption.d.ts +33 -0
- package/dist/hybridGroupDecryption.d.ts.map +1 -0
- package/dist/hybridGroupDecryption.js +84 -0
- package/dist/hybridGroupDecryption.js.map +1 -0
- package/dist/hybridGroupEncryption.d.ts +30 -0
- package/dist/hybridGroupEncryption.d.ts.map +1 -0
- package/dist/hybridGroupEncryption.js +92 -0
- package/dist/hybridGroupEncryption.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/olmLib.d.ts +35 -0
- package/dist/olmLib.d.ts.map +1 -0
- package/dist/olmLib.js +37 -0
- package/dist/olmLib.js.map +1 -0
- package/dist/storeTypes.d.ts +27 -0
- package/dist/storeTypes.d.ts.map +1 -0
- package/dist/storeTypes.js +2 -0
- package/dist/storeTypes.js.map +1 -0
- package/dist/tests/cryptoAesGcm.test.d.ts +2 -0
- package/dist/tests/cryptoAesGcm.test.d.ts.map +1 -0
- package/dist/tests/cryptoAesGcm.test.js +71 -0
- package/dist/tests/cryptoAesGcm.test.js.map +1 -0
- package/dist/tests/cryptoStore.test.d.ts +5 -0
- package/dist/tests/cryptoStore.test.d.ts.map +1 -0
- package/dist/tests/cryptoStore.test.js +114 -0
- package/dist/tests/cryptoStore.test.js.map +1 -0
- package/dist/tests/decryptionExtensions.test.d.ts +2 -0
- package/dist/tests/decryptionExtensions.test.d.ts.map +1 -0
- package/dist/tests/decryptionExtensions.test.js +355 -0
- package/dist/tests/decryptionExtensions.test.js.map +1 -0
- package/dist/tests/encryption-protocol.test.d.ts +2 -0
- package/dist/tests/encryption-protocol.test.d.ts.map +1 -0
- package/dist/tests/encryption-protocol.test.js +150 -0
- package/dist/tests/encryption-protocol.test.js.map +1 -0
- package/dist/tests/encryptionDelegate.test.d.ts +2 -0
- package/dist/tests/encryptionDelegate.test.d.ts.map +1 -0
- package/dist/tests/encryptionDelegate.test.js +78 -0
- package/dist/tests/encryptionDelegate.test.js.map +1 -0
- package/dist/tests/group-encryption-protocol.test.d.ts +2 -0
- package/dist/tests/group-encryption-protocol.test.d.ts.map +1 -0
- package/dist/tests/group-encryption-protocol.test.js +103 -0
- package/dist/tests/group-encryption-protocol.test.js.map +1 -0
- package/dist/tests/group-encryptionDelegate.test.d.ts +2 -0
- package/dist/tests/group-encryptionDelegate.test.d.ts.map +1 -0
- package/dist/tests/group-encryptionDelegate.test.js +23 -0
- package/dist/tests/group-encryptionDelegate.test.js.map +1 -0
- package/dist/tests/pk.test.d.ts +2 -0
- package/dist/tests/pk.test.d.ts.map +1 -0
- package/dist/tests/pk.test.js +103 -0
- package/dist/tests/pk.test.js.map +1 -0
- package/package.json +54 -0
package/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 River Association
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
package/dist/base.d.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { GroupEncryptionAlgorithmId, GroupEncryptionSession, UserDeviceCollection } from './olmLib';
|
|
2
|
+
import { EncryptionDevice } from './encryptionDevice';
|
|
3
|
+
import { EncryptedData } from '@towns-protocol/proto';
|
|
4
|
+
export interface IGroupEncryptionClient {
|
|
5
|
+
downloadUserDeviceInfo(userIds: string[], forceDownload: boolean): Promise<UserDeviceCollection>;
|
|
6
|
+
encryptAndShareGroupSessions(streamId: string, sessions: GroupEncryptionSession[], devicesInRoom: UserDeviceCollection, algorithm: GroupEncryptionAlgorithmId): Promise<void>;
|
|
7
|
+
getDevicesInStream(streamId: string): Promise<UserDeviceCollection>;
|
|
8
|
+
getMiniblockInfo(streamId: string): Promise<{
|
|
9
|
+
miniblockNum: bigint;
|
|
10
|
+
miniblockHash: Uint8Array;
|
|
11
|
+
}>;
|
|
12
|
+
}
|
|
13
|
+
export interface IDecryptionParams {
|
|
14
|
+
/** olm.js wrapper */
|
|
15
|
+
device: EncryptionDevice;
|
|
16
|
+
}
|
|
17
|
+
export interface IEncryptionParams {
|
|
18
|
+
client: IGroupEncryptionClient;
|
|
19
|
+
/** olm.js wrapper */
|
|
20
|
+
device: EncryptionDevice;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* base type for encryption implementations
|
|
24
|
+
*/
|
|
25
|
+
export declare abstract class EncryptionAlgorithm implements IEncryptionParams {
|
|
26
|
+
readonly device: EncryptionDevice;
|
|
27
|
+
readonly client: IGroupEncryptionClient;
|
|
28
|
+
/**
|
|
29
|
+
* @param params - parameters
|
|
30
|
+
*/
|
|
31
|
+
constructor(params: IEncryptionParams);
|
|
32
|
+
abstract ensureOutboundSession(streamId: string, opts?: {
|
|
33
|
+
awaitInitialShareSession: boolean;
|
|
34
|
+
}): Promise<void>;
|
|
35
|
+
abstract encrypt_deprecated_v0(streamId: string, payload: string): Promise<EncryptedData>;
|
|
36
|
+
abstract encrypt(streamId: string, payload: Uint8Array): Promise<EncryptedData>;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* base type for decryption implementations
|
|
40
|
+
*/
|
|
41
|
+
export declare abstract class DecryptionAlgorithm implements IDecryptionParams {
|
|
42
|
+
readonly device: EncryptionDevice;
|
|
43
|
+
constructor(params: IDecryptionParams);
|
|
44
|
+
abstract decrypt(streamId: string, content: EncryptedData): Promise<Uint8Array | string>;
|
|
45
|
+
abstract importStreamKey(streamId: string, session: GroupEncryptionSession): Promise<void>;
|
|
46
|
+
abstract exportGroupSession(streamId: string, sessionId: string): Promise<GroupEncryptionSession | undefined>;
|
|
47
|
+
abstract exportGroupSessions(): Promise<GroupEncryptionSession[]>;
|
|
48
|
+
abstract exportGroupSessionIds(streamId: string): Promise<string[]>;
|
|
49
|
+
abstract hasSessionKey(streamId: string, sessionId: string): Promise<boolean>;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Exception thrown when decryption fails
|
|
53
|
+
*
|
|
54
|
+
* @param msg - user-visible message describing the problem
|
|
55
|
+
*
|
|
56
|
+
* @param details - key/value pairs reported in the logs but not shown
|
|
57
|
+
* to the user.
|
|
58
|
+
*/
|
|
59
|
+
export declare class DecryptionError extends Error {
|
|
60
|
+
readonly code: string;
|
|
61
|
+
constructor(code: string, msg: string);
|
|
62
|
+
}
|
|
63
|
+
export declare function isDecryptionError(e: Error): e is DecryptionError;
|
|
64
|
+
//# sourceMappingURL=base.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../src/base.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,0BAA0B,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAA;AAEnG,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AAErD,MAAM,WAAW,sBAAsB;IACnC,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,aAAa,EAAE,OAAO,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAA;IAChG,4BAA4B,CACxB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,sBAAsB,EAAE,EAClC,aAAa,EAAE,oBAAoB,EACnC,SAAS,EAAE,0BAA0B,GACtC,OAAO,CAAC,IAAI,CAAC,CAAA;IAChB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAA;IACnE,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,UAAU,CAAA;KAAE,CAAC,CAAA;CACnG;AAED,MAAM,WAAW,iBAAiB;IAC9B,qBAAqB;IACrB,MAAM,EAAE,gBAAgB,CAAA;CAC3B;AAED,MAAM,WAAW,iBAAiB;IAC9B,MAAM,EAAE,sBAAsB,CAAA;IAC9B,qBAAqB;IACrB,MAAM,EAAE,gBAAgB,CAAA;CAC3B;AAED;;GAEG;AACH,8BAAsB,mBAAoB,YAAW,iBAAiB;IAClE,SAAgB,MAAM,EAAE,gBAAgB,CAAA;IACxC,SAAgB,MAAM,EAAE,sBAAsB,CAAA;IAE9C;;OAEG;gBACgB,MAAM,EAAE,iBAAiB;IAK5C,QAAQ,CAAC,qBAAqB,CAC1B,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE;QAAE,wBAAwB,EAAE,OAAO,CAAA;KAAE,GAC7C,OAAO,CAAC,IAAI,CAAC;IAEhB,QAAQ,CAAC,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IACzF,QAAQ,CAAC,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC;CAClF;AAED;;GAEG;AACH,8BAAsB,mBAAoB,YAAW,iBAAiB;IAClE,SAAgB,MAAM,EAAE,gBAAgB,CAAA;gBAErB,MAAM,EAAE,iBAAiB;IAI5C,QAAQ,CAAC,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,UAAU,GAAG,MAAM,CAAC;IAExF,QAAQ,CAAC,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC;IAE1F,QAAQ,CAAC,kBAAkB,CACvB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAClB,OAAO,CAAC,sBAAsB,GAAG,SAAS,CAAC;IAE9C,QAAQ,CAAC,mBAAmB,IAAI,OAAO,CAAC,sBAAsB,EAAE,CAAC;IACjE,QAAQ,CAAC,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IACnE,QAAQ,CAAC,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAChF;AAED;;;;;;;GAOG;AACH,qBAAa,eAAgB,SAAQ,KAAK;aACH,IAAI,EAAE,MAAM;gBAAZ,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM;CAK/D;AAED,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,IAAI,eAAe,CAEhE"}
|
package/dist/base.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* base type for encryption implementations
|
|
3
|
+
*/
|
|
4
|
+
export class EncryptionAlgorithm {
|
|
5
|
+
device;
|
|
6
|
+
client;
|
|
7
|
+
/**
|
|
8
|
+
* @param params - parameters
|
|
9
|
+
*/
|
|
10
|
+
constructor(params) {
|
|
11
|
+
this.device = params.device;
|
|
12
|
+
this.client = params.client;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* base type for decryption implementations
|
|
17
|
+
*/
|
|
18
|
+
export class DecryptionAlgorithm {
|
|
19
|
+
device;
|
|
20
|
+
constructor(params) {
|
|
21
|
+
this.device = params.device;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Exception thrown when decryption fails
|
|
26
|
+
*
|
|
27
|
+
* @param msg - user-visible message describing the problem
|
|
28
|
+
*
|
|
29
|
+
* @param details - key/value pairs reported in the logs but not shown
|
|
30
|
+
* to the user.
|
|
31
|
+
*/
|
|
32
|
+
export class DecryptionError extends Error {
|
|
33
|
+
code;
|
|
34
|
+
constructor(code, msg) {
|
|
35
|
+
super(msg);
|
|
36
|
+
this.code = code;
|
|
37
|
+
this.code = code;
|
|
38
|
+
this.name = 'DecryptionError';
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export function isDecryptionError(e) {
|
|
42
|
+
return e.name === 'DecryptionError';
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=base.js.map
|
package/dist/base.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base.js","sourceRoot":"","sources":["../src/base.ts"],"names":[],"mappings":"AA4BA;;GAEG;AACH,MAAM,OAAgB,mBAAmB;IACrB,MAAM,CAAkB;IACxB,MAAM,CAAwB;IAE9C;;OAEG;IACH,YAAmB,MAAyB;QACxC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAA;QAC3B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAA;IAC/B,CAAC;CASJ;AAED;;GAEG;AACH,MAAM,OAAgB,mBAAmB;IACrB,MAAM,CAAkB;IAExC,YAAmB,MAAyB;QACxC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAA;IAC/B,CAAC;CAcJ;AAED;;;;;;;GAOG;AACH,MAAM,OAAO,eAAgB,SAAQ,KAAK;IACH;IAAnC,YAAmC,IAAY,EAAE,GAAW;QACxD,KAAK,CAAC,GAAG,CAAC,CAAA;QADqB,SAAI,GAAJ,IAAI,CAAQ;QAE3C,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAChB,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAA;IACjC,CAAC;CACJ;AAED,MAAM,UAAU,iBAAiB,CAAC,CAAQ;IACtC,OAAO,CAAC,CAAC,IAAI,KAAK,iBAAiB,CAAA;AACvC,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare function generateNewAesGcmKey(): Promise<CryptoKey>;
|
|
2
|
+
export declare function exportAesGsmKeyBytes(key: CryptoKey): Promise<Uint8Array>;
|
|
3
|
+
export declare function importAesGsmKeyBytes(key: Uint8Array): Promise<CryptoKey>;
|
|
4
|
+
export declare function encryptAesGcm(key: CryptoKey, data: Uint8Array): Promise<{
|
|
5
|
+
ciphertext: Uint8Array;
|
|
6
|
+
iv: Uint8Array;
|
|
7
|
+
}>;
|
|
8
|
+
export declare function decryptAesGcm(key: CryptoKey, ciphertext: Uint8Array, iv: Uint8Array): Promise<Uint8Array>;
|
|
9
|
+
//# sourceMappingURL=cryptoAesGcm.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cryptoAesGcm.d.ts","sourceRoot":"","sources":["../src/cryptoAesGcm.ts"],"names":[],"mappings":"AAAA,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,SAAS,CAAC,CAE/D;AAED,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAG9E;AAED,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CAE9E;AAED,wBAAsB,aAAa,CAC/B,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,UAAU,GACjB,OAAO,CAAC;IAAE,UAAU,EAAE,UAAU,CAAC;IAAC,EAAE,EAAE,UAAU,CAAA;CAAE,CAAC,CAYrD;AAED,wBAAsB,aAAa,CAC/B,GAAG,EAAE,SAAS,EACd,UAAU,EAAE,UAAU,EACtB,EAAE,EAAE,UAAU,GACf,OAAO,CAAC,UAAU,CAAC,CAarB"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export async function generateNewAesGcmKey() {
|
|
2
|
+
return crypto.subtle.generateKey({ name: 'AES-GCM', length: 256 }, true, ['encrypt', 'decrypt']);
|
|
3
|
+
}
|
|
4
|
+
export async function exportAesGsmKeyBytes(key) {
|
|
5
|
+
const exportedKey = await crypto.subtle.exportKey('raw', key);
|
|
6
|
+
return new Uint8Array(exportedKey);
|
|
7
|
+
}
|
|
8
|
+
export async function importAesGsmKeyBytes(key) {
|
|
9
|
+
return crypto.subtle.importKey('raw', key, 'AES-GCM', true, ['encrypt', 'decrypt']);
|
|
10
|
+
}
|
|
11
|
+
export async function encryptAesGcm(key, data) {
|
|
12
|
+
// If data is empty, it's obvious what the message is from the result length.
|
|
13
|
+
if (data.length === 0) {
|
|
14
|
+
throw new Error('Data to encrypt cannot be empty');
|
|
15
|
+
}
|
|
16
|
+
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
17
|
+
const encrypted = await crypto.subtle.encrypt({ name: 'AES-GCM', iv, tagLength: 128 }, key, data);
|
|
18
|
+
return { ciphertext: new Uint8Array(encrypted), iv };
|
|
19
|
+
}
|
|
20
|
+
export async function decryptAesGcm(key, ciphertext, iv) {
|
|
21
|
+
if (iv.length !== 12) {
|
|
22
|
+
throw new Error('IV must be 12 bytes');
|
|
23
|
+
}
|
|
24
|
+
if (ciphertext.length < 17) {
|
|
25
|
+
throw new Error('Ciphertext can not be this short');
|
|
26
|
+
}
|
|
27
|
+
const decrypted = await crypto.subtle.decrypt({ name: 'AES-GCM', iv, tagLength: 128 }, key, ciphertext);
|
|
28
|
+
return new Uint8Array(decrypted);
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=cryptoAesGcm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cryptoAesGcm.js","sourceRoot":"","sources":["../src/cryptoAesGcm.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,KAAK,UAAU,oBAAoB;IACtC,OAAO,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAA;AACpG,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,GAAc;IACrD,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;IAC7D,OAAO,IAAI,UAAU,CAAC,WAAW,CAAC,CAAA;AACtC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,GAAe;IACtD,OAAO,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAA;AACvF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAC/B,GAAc,EACd,IAAgB;IAEhB,6EAA6E;IAC7E,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAA;IACtD,CAAC;IACD,MAAM,EAAE,GAAG,MAAM,CAAC,eAAe,CAAC,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC,CAAA;IACrD,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,CACzC,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,EACvC,GAAG,EACH,IAAI,CACP,CAAA;IACD,OAAO,EAAE,UAAU,EAAE,IAAI,UAAU,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,CAAA;AACxD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAC/B,GAAc,EACd,UAAsB,EACtB,EAAc;IAEd,IAAI,EAAE,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAA;IAC1C,CAAC;IACD,IAAI,UAAU,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAA;IACvD,CAAC;IACD,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,CACzC,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,EACvC,GAAG,EACH,UAAU,CACb,CAAA;IACD,OAAO,IAAI,UAAU,CAAC,SAAS,CAAC,CAAA;AACpC,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { AccountRecord, ExtendedInboundGroupSessionData, GroupSessionRecord, HybridGroupSessionRecord, UserDeviceRecord } from './storeTypes';
|
|
2
|
+
import Dexie, { Table } from 'dexie';
|
|
3
|
+
import { InboundGroupSessionData } from './encryptionDevice';
|
|
4
|
+
import { UserDevice } from './olmLib';
|
|
5
|
+
export declare class CryptoStore extends Dexie {
|
|
6
|
+
account: Table<AccountRecord>;
|
|
7
|
+
outboundGroupSessions: Table<GroupSessionRecord>;
|
|
8
|
+
inboundGroupSessions: Table<ExtendedInboundGroupSessionData>;
|
|
9
|
+
hybridGroupSessions: Table<HybridGroupSessionRecord>;
|
|
10
|
+
devices: Table<UserDeviceRecord>;
|
|
11
|
+
userId: string;
|
|
12
|
+
constructor(databaseName: string, userId: string);
|
|
13
|
+
initialize(): Promise<void>;
|
|
14
|
+
deleteAllData(): void;
|
|
15
|
+
deleteInboundGroupSessions(streamId: string, sessionId: string): Promise<void>;
|
|
16
|
+
deleteAccount(userId: string): Promise<void>;
|
|
17
|
+
getAccount(): Promise<string>;
|
|
18
|
+
storeAccount(accountPickle: string): Promise<void>;
|
|
19
|
+
storeEndToEndOutboundGroupSession(sessionId: string, sessionData: string, streamId: string): Promise<void>;
|
|
20
|
+
getEndToEndOutboundGroupSession(streamId: string): Promise<string>;
|
|
21
|
+
getAllEndToEndOutboundGroupSessions(): Promise<GroupSessionRecord[]>;
|
|
22
|
+
getEndToEndInboundGroupSession(streamId: string, sessionId: string): Promise<InboundGroupSessionData | undefined>;
|
|
23
|
+
getHybridGroupSession(streamId: string, sessionId: string): Promise<HybridGroupSessionRecord | undefined>;
|
|
24
|
+
getHybridGroupSessionsForStream(streamId: string): Promise<HybridGroupSessionRecord[]>;
|
|
25
|
+
getAllEndToEndInboundGroupSessions(): Promise<ExtendedInboundGroupSessionData[]>;
|
|
26
|
+
getAllHybridGroupSessions(): Promise<HybridGroupSessionRecord[]>;
|
|
27
|
+
storeEndToEndInboundGroupSession(streamId: string, sessionId: string, sessionData: InboundGroupSessionData): Promise<void>;
|
|
28
|
+
storeHybridGroupSession(sessionData: HybridGroupSessionRecord): Promise<void>;
|
|
29
|
+
getInboundGroupSessionIds(streamId: string): Promise<string[]>;
|
|
30
|
+
getHybridGroupSessionIds(streamId: string): Promise<string[]>;
|
|
31
|
+
withAccountTx<T>(fn: () => Promise<T>): Promise<T>;
|
|
32
|
+
withGroupSessions<T>(fn: () => Promise<T>): Promise<T>;
|
|
33
|
+
/**
|
|
34
|
+
* Only used for testing
|
|
35
|
+
* @returns total number of devices in the store
|
|
36
|
+
*/
|
|
37
|
+
deviceRecordCount(): Promise<number>;
|
|
38
|
+
/**
|
|
39
|
+
* Store a list of devices for a given userId
|
|
40
|
+
* @param userId string
|
|
41
|
+
* @param devices UserDeviceInfo[]
|
|
42
|
+
* @param expirationMs Expiration time in milliseconds
|
|
43
|
+
*/
|
|
44
|
+
saveUserDevices(userId: string, devices: UserDevice[], expirationMs?: number): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* Get all stored devices for a given userId
|
|
47
|
+
* @param userId string
|
|
48
|
+
* @returns UserDevice[], a list of devices
|
|
49
|
+
*/
|
|
50
|
+
getUserDevices(userId: string): Promise<UserDevice[]>;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=cryptoStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cryptoStore.d.ts","sourceRoot":"","sources":["../src/cryptoStore.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,aAAa,EACb,+BAA+B,EAC/B,kBAAkB,EAClB,wBAAwB,EACxB,gBAAgB,EACnB,MAAM,cAAc,CAAA;AACrB,OAAO,KAAK,EAAE,EAAE,KAAK,EAAE,MAAM,OAAO,CAAA;AAEpC,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAA;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAMrC,qBAAa,WAAY,SAAQ,KAAK;IAClC,OAAO,EAAG,KAAK,CAAC,aAAa,CAAC,CAAA;IAC9B,qBAAqB,EAAG,KAAK,CAAC,kBAAkB,CAAC,CAAA;IACjD,oBAAoB,EAAG,KAAK,CAAC,+BAA+B,CAAC,CAAA;IAC7D,mBAAmB,EAAG,KAAK,CAAC,wBAAwB,CAAC,CAAA;IACrD,OAAO,EAAG,KAAK,CAAC,gBAAgB,CAAC,CAAA;IACjC,MAAM,EAAE,MAAM,CAAA;gBAEF,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAY1C,UAAU;IAIhB,aAAa;IAIP,0BAA0B,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI9E,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI5C,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;IAQ7B,YAAY,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlD,iCAAiC,CACnC,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC;IAIV,+BAA+B,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAQlE,mCAAmC,IAAI,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAIpE,8BAA8B,CAChC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAClB,OAAO,CAAC,uBAAuB,GAAG,SAAS,CAAC;IAIzC,qBAAqB,CACvB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAClB,OAAO,CAAC,wBAAwB,GAAG,SAAS,CAAC;IAI1C,+BAA+B,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,wBAAwB,EAAE,CAAC;IAKtF,kCAAkC,IAAI,OAAO,CAAC,+BAA+B,EAAE,CAAC;IAIhF,yBAAyB,IAAI,OAAO,CAAC,wBAAwB,EAAE,CAAC;IAIhE,gCAAgC,CAClC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,uBAAuB,GACrC,OAAO,CAAC,IAAI,CAAC;IAIV,uBAAuB,CAAC,WAAW,EAAE,wBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7E,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAK9D,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAK7D,aAAa,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAIlD,iBAAiB,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAU5D;;;OAGG;IACG,iBAAiB;IAIvB;;;;;OAKG;IACG,eAAe,CACjB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,UAAU,EAAE,EACrB,YAAY,GAAE,MAA+C;IAQjE;;;;OAIG;IACG,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;CAa9D"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import Dexie from 'dexie';
|
|
2
|
+
// TODO: Increase this time to 10 days or something.
|
|
3
|
+
// Its 15 min right now so we can catch any issues with the expiration time.
|
|
4
|
+
const DEFAULT_USER_DEVICE_EXPIRATION_TIME_MS = 15 * 60 * 1000;
|
|
5
|
+
export class CryptoStore extends Dexie {
|
|
6
|
+
account;
|
|
7
|
+
outboundGroupSessions;
|
|
8
|
+
inboundGroupSessions;
|
|
9
|
+
hybridGroupSessions;
|
|
10
|
+
devices;
|
|
11
|
+
userId;
|
|
12
|
+
constructor(databaseName, userId) {
|
|
13
|
+
super(databaseName);
|
|
14
|
+
this.userId = userId;
|
|
15
|
+
this.version(6).stores({
|
|
16
|
+
account: 'id',
|
|
17
|
+
inboundGroupSessions: '[streamId+sessionId]',
|
|
18
|
+
outboundGroupSessions: 'streamId',
|
|
19
|
+
hybridGroupSessions: '[streamId+sessionId],streamId',
|
|
20
|
+
devices: '[userId+deviceKey],expirationTimestamp',
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
async initialize() {
|
|
24
|
+
await this.devices.where('expirationTimestamp').below(Date.now()).delete();
|
|
25
|
+
}
|
|
26
|
+
deleteAllData() {
|
|
27
|
+
throw new Error('Method not implemented.');
|
|
28
|
+
}
|
|
29
|
+
async deleteInboundGroupSessions(streamId, sessionId) {
|
|
30
|
+
await this.inboundGroupSessions.where({ streamId, sessionId }).delete();
|
|
31
|
+
}
|
|
32
|
+
async deleteAccount(userId) {
|
|
33
|
+
await this.account.where({ id: userId }).delete();
|
|
34
|
+
}
|
|
35
|
+
async getAccount() {
|
|
36
|
+
const account = await this.account.get({ id: this.userId });
|
|
37
|
+
if (!account) {
|
|
38
|
+
throw new Error('account not found');
|
|
39
|
+
}
|
|
40
|
+
return account.accountPickle;
|
|
41
|
+
}
|
|
42
|
+
async storeAccount(accountPickle) {
|
|
43
|
+
await this.account.put({ id: this.userId, accountPickle });
|
|
44
|
+
}
|
|
45
|
+
async storeEndToEndOutboundGroupSession(sessionId, sessionData, streamId) {
|
|
46
|
+
await this.outboundGroupSessions.put({ sessionId, session: sessionData, streamId });
|
|
47
|
+
}
|
|
48
|
+
async getEndToEndOutboundGroupSession(streamId) {
|
|
49
|
+
const session = await this.outboundGroupSessions.get({ streamId });
|
|
50
|
+
if (!session) {
|
|
51
|
+
throw new Error('session not found');
|
|
52
|
+
}
|
|
53
|
+
return session.session;
|
|
54
|
+
}
|
|
55
|
+
async getAllEndToEndOutboundGroupSessions() {
|
|
56
|
+
return await this.outboundGroupSessions.toArray();
|
|
57
|
+
}
|
|
58
|
+
async getEndToEndInboundGroupSession(streamId, sessionId) {
|
|
59
|
+
return await this.inboundGroupSessions.get({ sessionId, streamId });
|
|
60
|
+
}
|
|
61
|
+
async getHybridGroupSession(streamId, sessionId) {
|
|
62
|
+
return await this.hybridGroupSessions.get({ streamId, sessionId });
|
|
63
|
+
}
|
|
64
|
+
async getHybridGroupSessionsForStream(streamId) {
|
|
65
|
+
const sessions = await this.hybridGroupSessions.where({ streamId }).toArray();
|
|
66
|
+
return sessions;
|
|
67
|
+
}
|
|
68
|
+
async getAllEndToEndInboundGroupSessions() {
|
|
69
|
+
return await this.inboundGroupSessions.toArray();
|
|
70
|
+
}
|
|
71
|
+
async getAllHybridGroupSessions() {
|
|
72
|
+
return await this.hybridGroupSessions.toArray();
|
|
73
|
+
}
|
|
74
|
+
async storeEndToEndInboundGroupSession(streamId, sessionId, sessionData) {
|
|
75
|
+
await this.inboundGroupSessions.put({ streamId, sessionId, ...sessionData });
|
|
76
|
+
}
|
|
77
|
+
async storeHybridGroupSession(sessionData) {
|
|
78
|
+
await this.hybridGroupSessions.put({ ...sessionData });
|
|
79
|
+
}
|
|
80
|
+
async getInboundGroupSessionIds(streamId) {
|
|
81
|
+
const sessions = await this.inboundGroupSessions.where({ streamId }).toArray();
|
|
82
|
+
return sessions.map((s) => s.sessionId);
|
|
83
|
+
}
|
|
84
|
+
async getHybridGroupSessionIds(streamId) {
|
|
85
|
+
const sessions = await this.hybridGroupSessions.where({ streamId }).toArray();
|
|
86
|
+
return sessions.map((s) => s.sessionId);
|
|
87
|
+
}
|
|
88
|
+
async withAccountTx(fn) {
|
|
89
|
+
return await this.transaction('rw', this.account, fn);
|
|
90
|
+
}
|
|
91
|
+
async withGroupSessions(fn) {
|
|
92
|
+
return await this.transaction('rw', this.outboundGroupSessions, this.inboundGroupSessions, this.hybridGroupSessions, // aellis this should be in its own transaction but tests were failing otherwise
|
|
93
|
+
fn);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Only used for testing
|
|
97
|
+
* @returns total number of devices in the store
|
|
98
|
+
*/
|
|
99
|
+
async deviceRecordCount() {
|
|
100
|
+
return await this.devices.count();
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Store a list of devices for a given userId
|
|
104
|
+
* @param userId string
|
|
105
|
+
* @param devices UserDeviceInfo[]
|
|
106
|
+
* @param expirationMs Expiration time in milliseconds
|
|
107
|
+
*/
|
|
108
|
+
async saveUserDevices(userId, devices, expirationMs = DEFAULT_USER_DEVICE_EXPIRATION_TIME_MS) {
|
|
109
|
+
const expirationTimestamp = Date.now() + expirationMs;
|
|
110
|
+
for (const device of devices) {
|
|
111
|
+
await this.devices.put({ userId, expirationTimestamp, ...device });
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Get all stored devices for a given userId
|
|
116
|
+
* @param userId string
|
|
117
|
+
* @returns UserDevice[], a list of devices
|
|
118
|
+
*/
|
|
119
|
+
async getUserDevices(userId) {
|
|
120
|
+
const expirationTimestamp = Date.now();
|
|
121
|
+
return (await this.devices
|
|
122
|
+
.where('userId')
|
|
123
|
+
.equals(userId)
|
|
124
|
+
.and((record) => record.expirationTimestamp > expirationTimestamp)
|
|
125
|
+
.toArray()).map((record) => ({
|
|
126
|
+
deviceKey: record.deviceKey,
|
|
127
|
+
fallbackKey: record.fallbackKey,
|
|
128
|
+
}));
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=cryptoStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cryptoStore.js","sourceRoot":"","sources":["../src/cryptoStore.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import TypedEmitter from 'typed-emitter';
|
|
2
|
+
import { Permission } from '@towns-protocol/web3';
|
|
3
|
+
import { AddEventResponse_Error, EncryptedData, SessionKeys, UserInboxPayload_GroupEncryptionSessions } from '@towns-protocol/proto';
|
|
4
|
+
import { DLogger } from '@towns-protocol/dlog';
|
|
5
|
+
import { GroupEncryptionAlgorithmId, GroupEncryptionSession, UserDevice } from './olmLib';
|
|
6
|
+
import { GroupEncryptionCrypto } from './groupEncryptionCrypto';
|
|
7
|
+
export interface EntitlementsDelegate {
|
|
8
|
+
isEntitled(spaceId: string | undefined, channelId: string | undefined, user: string, permission: Permission): Promise<boolean>;
|
|
9
|
+
}
|
|
10
|
+
export declare enum DecryptionStatus {
|
|
11
|
+
initializing = "initializing",
|
|
12
|
+
updating = "updating",
|
|
13
|
+
working = "working",
|
|
14
|
+
idle = "idle",
|
|
15
|
+
done = "done"
|
|
16
|
+
}
|
|
17
|
+
export type DecryptionEvents = {
|
|
18
|
+
decryptionExtStatusChanged: (status: DecryptionStatus) => void;
|
|
19
|
+
};
|
|
20
|
+
export interface NewGroupSessionItem {
|
|
21
|
+
streamId: string;
|
|
22
|
+
sessions: UserInboxPayload_GroupEncryptionSessions;
|
|
23
|
+
}
|
|
24
|
+
export interface EncryptedContentItem {
|
|
25
|
+
streamId: string;
|
|
26
|
+
eventId: string;
|
|
27
|
+
kind: string;
|
|
28
|
+
encryptedData: EncryptedData;
|
|
29
|
+
}
|
|
30
|
+
export interface KeySolicitationContent {
|
|
31
|
+
deviceKey: string;
|
|
32
|
+
fallbackKey: string;
|
|
33
|
+
isNewDevice: boolean;
|
|
34
|
+
sessionIds: string[];
|
|
35
|
+
}
|
|
36
|
+
export interface EventSignatureBundle {
|
|
37
|
+
hash: Uint8Array;
|
|
38
|
+
signature: Uint8Array | undefined;
|
|
39
|
+
event: {
|
|
40
|
+
creatorAddress: Uint8Array;
|
|
41
|
+
delegateSig: Uint8Array;
|
|
42
|
+
delegateExpiryEpochMs: bigint;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export interface KeySolicitationItem {
|
|
46
|
+
streamId: string;
|
|
47
|
+
fromUserId: string;
|
|
48
|
+
fromUserAddress: Uint8Array;
|
|
49
|
+
solicitation: KeySolicitationContent;
|
|
50
|
+
respondAfter: number;
|
|
51
|
+
sigBundle: EventSignatureBundle;
|
|
52
|
+
hashStr: string;
|
|
53
|
+
}
|
|
54
|
+
export interface KeySolicitationData {
|
|
55
|
+
streamId: string;
|
|
56
|
+
isNewDevice: boolean;
|
|
57
|
+
missingSessionIds: string[];
|
|
58
|
+
}
|
|
59
|
+
export interface KeyFulfilmentData {
|
|
60
|
+
streamId: string;
|
|
61
|
+
userAddress: Uint8Array;
|
|
62
|
+
deviceKey: string;
|
|
63
|
+
sessionIds: string[];
|
|
64
|
+
}
|
|
65
|
+
export interface GroupSessionsData {
|
|
66
|
+
streamId: string;
|
|
67
|
+
item: KeySolicitationItem;
|
|
68
|
+
sessions: GroupEncryptionSession[];
|
|
69
|
+
algorithm: GroupEncryptionAlgorithmId;
|
|
70
|
+
}
|
|
71
|
+
export interface DecryptionSessionError {
|
|
72
|
+
missingSession: boolean;
|
|
73
|
+
kind: string;
|
|
74
|
+
encryptedData: EncryptedData;
|
|
75
|
+
error?: unknown;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
*
|
|
79
|
+
* Responsibilities:
|
|
80
|
+
* 1. Download new to-device messages that happened while we were offline
|
|
81
|
+
* 2. Decrypt new to-device messages
|
|
82
|
+
* 3. Decrypt encrypted content
|
|
83
|
+
* 4. Retry decryption failures, request keys for failed decryption
|
|
84
|
+
* 5. Respond to key solicitations
|
|
85
|
+
*
|
|
86
|
+
*
|
|
87
|
+
* Notes:
|
|
88
|
+
* If in the future we started snapshotting the eventNum of the last message sent by every user,
|
|
89
|
+
* we could use that to determine the order we send out keys, and the order that we reply to key solicitations.
|
|
90
|
+
*
|
|
91
|
+
* It should be easy to introduce a priority stream, where we decrypt messages from that stream first, before
|
|
92
|
+
* anything else, so the messages show up quicky in the ui that the user is looking at.
|
|
93
|
+
*
|
|
94
|
+
* We need code to purge bad sessions (if someones sends us the wrong key, or a key that doesn't decrypt the message)
|
|
95
|
+
*/
|
|
96
|
+
export declare abstract class BaseDecryptionExtensions {
|
|
97
|
+
private _status;
|
|
98
|
+
private mainQueues;
|
|
99
|
+
private streamQueues;
|
|
100
|
+
private upToDateStreams;
|
|
101
|
+
private highPriorityIds;
|
|
102
|
+
private recentStreamIds;
|
|
103
|
+
private decryptionFailures;
|
|
104
|
+
private inProgressTick?;
|
|
105
|
+
private timeoutId?;
|
|
106
|
+
private delayMs;
|
|
107
|
+
private started;
|
|
108
|
+
private numRecentStreamIds;
|
|
109
|
+
private emitter;
|
|
110
|
+
protected _onStopFn?: () => void;
|
|
111
|
+
protected log: {
|
|
112
|
+
debug: DLogger;
|
|
113
|
+
info: DLogger;
|
|
114
|
+
error: DLogger;
|
|
115
|
+
};
|
|
116
|
+
readonly crypto: GroupEncryptionCrypto;
|
|
117
|
+
readonly entitlementDelegate: EntitlementsDelegate;
|
|
118
|
+
readonly userDevice: UserDevice;
|
|
119
|
+
readonly userId: string;
|
|
120
|
+
constructor(emitter: TypedEmitter<DecryptionEvents>, crypto: GroupEncryptionCrypto, entitlementDelegate: EntitlementsDelegate, userDevice: UserDevice, userId: string, upToDateStreams: Set<string>, inLogId: string);
|
|
121
|
+
abstract ackNewGroupSession(session: UserInboxPayload_GroupEncryptionSessions): Promise<void>;
|
|
122
|
+
abstract decryptGroupEvent(streamId: string, eventId: string, kind: string, encryptedData: EncryptedData): Promise<void>;
|
|
123
|
+
abstract downloadNewMessages(): Promise<void>;
|
|
124
|
+
abstract getKeySolicitations(streamId: string): KeySolicitationContent[];
|
|
125
|
+
abstract hasStream(streamId: string): boolean;
|
|
126
|
+
abstract isUserEntitledToKeyExchange(streamId: string, userId: string, opts?: {
|
|
127
|
+
skipOnChainValidation: boolean;
|
|
128
|
+
}): Promise<boolean>;
|
|
129
|
+
abstract isValidEvent(item: KeySolicitationItem): {
|
|
130
|
+
isValid: boolean;
|
|
131
|
+
reason?: string;
|
|
132
|
+
};
|
|
133
|
+
abstract isUserInboxStreamUpToDate(upToDateStreams: Set<string>): boolean;
|
|
134
|
+
abstract onDecryptionError(item: EncryptedContentItem, err: DecryptionSessionError): void;
|
|
135
|
+
abstract sendKeySolicitation(args: KeySolicitationData): Promise<void>;
|
|
136
|
+
abstract sendKeyFulfillment(args: KeyFulfilmentData): Promise<{
|
|
137
|
+
error?: AddEventResponse_Error;
|
|
138
|
+
}>;
|
|
139
|
+
abstract encryptAndShareGroupSessions(args: GroupSessionsData): Promise<void>;
|
|
140
|
+
abstract shouldPauseTicking(): boolean;
|
|
141
|
+
/**
|
|
142
|
+
* uploadDeviceKeys
|
|
143
|
+
* upload device keys to the server
|
|
144
|
+
*/
|
|
145
|
+
abstract uploadDeviceKeys(): Promise<void>;
|
|
146
|
+
abstract getPriorityForStream(streamId: string, highPriorityIds: Set<string>, recentStreamIds: Set<string>): number;
|
|
147
|
+
enqueueNewGroupSessions(sessions: UserInboxPayload_GroupEncryptionSessions, _senderId: string): void;
|
|
148
|
+
enqueueNewEncryptedContent(streamId: string, eventId: string, kind: string, // kind of encrypted data
|
|
149
|
+
encryptedData: EncryptedData): void;
|
|
150
|
+
enqueueInitKeySolicitations(streamId: string, eventHashStr: string, members: {
|
|
151
|
+
userId: string;
|
|
152
|
+
userAddress: Uint8Array;
|
|
153
|
+
solicitations: KeySolicitationContent[];
|
|
154
|
+
}[], sigBundle: EventSignatureBundle): void;
|
|
155
|
+
enqueueKeySolicitation(streamId: string, eventHashStr: string, fromUserId: string, fromUserAddress: Uint8Array, keySolicitation: KeySolicitationContent, sigBundle: EventSignatureBundle): void;
|
|
156
|
+
setStreamUpToDate(streamId: string): void;
|
|
157
|
+
resetUpToDateStreams(): void;
|
|
158
|
+
retryDecryptionFailures(streamId: string): void;
|
|
159
|
+
start(): void;
|
|
160
|
+
enqueueNewMessageDownload(): void;
|
|
161
|
+
onStart(): void;
|
|
162
|
+
stop(): Promise<void>;
|
|
163
|
+
onStop(): Promise<void>;
|
|
164
|
+
get status(): DecryptionStatus;
|
|
165
|
+
private setStatus;
|
|
166
|
+
private compareStreamIds;
|
|
167
|
+
private lastPrintedAt;
|
|
168
|
+
protected checkStartTicking(): void;
|
|
169
|
+
private stopTicking;
|
|
170
|
+
private getDelayMs;
|
|
171
|
+
private tick;
|
|
172
|
+
/**
|
|
173
|
+
* processNewGroupSession
|
|
174
|
+
* process new group sessions that were sent to our to device stream inbox
|
|
175
|
+
* re-enqueue any decryption failures with matching session id
|
|
176
|
+
*/
|
|
177
|
+
private processNewGroupSession;
|
|
178
|
+
/**
|
|
179
|
+
* processEncryptedContentItem
|
|
180
|
+
* try to decrypt encrytped content
|
|
181
|
+
*/
|
|
182
|
+
private processEncryptedContentItem;
|
|
183
|
+
/**
|
|
184
|
+
* processMissingKeys
|
|
185
|
+
* process missing keys and send key solicitations to streams
|
|
186
|
+
*/
|
|
187
|
+
private processMissingKeys;
|
|
188
|
+
/**
|
|
189
|
+
* processKeySolicitation
|
|
190
|
+
* process incoming key solicitations and send keys and key fulfillments
|
|
191
|
+
*/
|
|
192
|
+
private processKeySolicitation;
|
|
193
|
+
/**
|
|
194
|
+
* can be overridden to add a delay to the key solicitation response
|
|
195
|
+
*/
|
|
196
|
+
getRespondDelayMSForKeySolicitation(_streamId: string, _userId: string): number;
|
|
197
|
+
setHighPriorityStreams(streamIds: string[]): void;
|
|
198
|
+
}
|
|
199
|
+
export declare function makeSessionKeys(sessions: GroupEncryptionSession[]): SessionKeys;
|
|
200
|
+
//# sourceMappingURL=decryptionExtensions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decryptionExtensions.d.ts","sourceRoot":"","sources":["../src/decryptionExtensions.ts"],"names":[],"mappings":"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"}
|