@wireapp/core 18.0.0 → 20.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.
- package/CHANGELOG.md +57 -0
- package/package.json +3 -3
- package/src/main/conversation/AssetService.d.ts +3 -4
- package/src/main/conversation/AssetService.js +12 -16
- package/src/main/conversation/message/MessageBuilder.d.ts +2 -7
- package/src/main/conversation/message/MessageBuilder.js +2 -2
- package/src/main/conversation/message/MessageService.js +12 -6
- package/src/main/cryptography/CryptographyService.d.ts +8 -3
- package/src/main/cryptography/CryptographyService.js +20 -22
- package/src/main/linkPreview/LinkPreviewService.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,63 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [20.0.2](https://github.com/wireapp/wire-web-packages/tree/main/packages/core/compare/@wireapp/core@20.0.1...@wireapp/core@20.0.2) (2021-12-09)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @wireapp/core
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
## [20.0.1](https://github.com/wireapp/wire-web-packages/tree/main/packages/core/compare/@wireapp/core@20.0.0...@wireapp/core@20.0.1) (2021-12-09)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Bug Fixes
|
|
18
|
+
|
|
19
|
+
* **core:** Generate new session and reencrypt payload when sessionIds are missing ([#4200](https://github.com/wireapp/wire-web-packages/tree/main/packages/core/issues/4200)) ([07eab82](https://github.com/wireapp/wire-web-packages/tree/main/packages/core/commit/07eab82ee7512bb4eba71adf20c13c5609dbc024))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# [20.0.0](https://github.com/wireapp/wire-web-packages/tree/main/packages/core/compare/@wireapp/core@19.0.0...@wireapp/core@20.0.0) (2021-12-08)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
### Code Refactoring
|
|
29
|
+
|
|
30
|
+
* **core:** Harmonize asset param name ([#4199](https://github.com/wireapp/wire-web-packages/tree/main/packages/core/issues/4199)) ([f29c825](https://github.com/wireapp/wire-web-packages/tree/main/packages/core/commit/f29c825df427e9fccae164a728f0071fdf1bc3af))
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
### BREAKING CHANGES
|
|
34
|
+
|
|
35
|
+
* **core:** The `imageAsset` property given to the `MessageBuilder.createImage` function has been renamed `asset` to be coherent with sending files.
|
|
36
|
+
Replace `MessageBuilder.createImage({..., imageAsset: asset})` with `MessageBuilder.createImage({..., asset})`
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# [19.0.0](https://github.com/wireapp/wire-web-packages/tree/main/packages/core/compare/@wireapp/core@18.0.0...@wireapp/core@19.0.0) (2021-12-08)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
### Features
|
|
46
|
+
|
|
47
|
+
* **core:** Ability to cancel asset uploading ([#4198](https://github.com/wireapp/wire-web-packages/tree/main/packages/core/issues/4198)) ([e111f46](https://github.com/wireapp/wire-web-packages/tree/main/packages/core/commit/e111f46d06bf2ec22f2002c9a2954cdf0c9e8d09))
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
### BREAKING CHANGES
|
|
51
|
+
|
|
52
|
+
* **core:** Uploading an asset now return a structure that allow cancelling the upload. Thus instances of `await account.service.asset.uploadAsset(...)` must be replaced by
|
|
53
|
+
```
|
|
54
|
+
const {cancel, response} = await account.service.asset.uploadAsset(...);
|
|
55
|
+
cancel() // This is how you cancel the upload
|
|
56
|
+
await response// This will contain the uploaded asset once the upload is done
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
6
63
|
# [18.0.0](https://github.com/wireapp/wire-web-packages/tree/main/packages/core/compare/@wireapp/core@17.34.0...@wireapp/core@18.0.0) (2021-12-08)
|
|
7
64
|
|
|
8
65
|
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@types/long": "4.0.1",
|
|
7
7
|
"@types/node": "~14",
|
|
8
|
-
"@wireapp/api-client": "
|
|
8
|
+
"@wireapp/api-client": "16.1.0",
|
|
9
9
|
"@wireapp/cryptobox": "12.7.1",
|
|
10
10
|
"bazinga64": "5.10.0",
|
|
11
11
|
"hash.js": "1.1.7",
|
|
@@ -69,6 +69,6 @@
|
|
|
69
69
|
"test:project": "yarn dist && yarn test",
|
|
70
70
|
"test:node": "nyc jasmine --config=jasmine.json"
|
|
71
71
|
},
|
|
72
|
-
"version": "
|
|
73
|
-
"gitHead": "
|
|
72
|
+
"version": "20.0.2",
|
|
73
|
+
"gitHead": "34e17094bdbf142c28dc09726427dd6ac197a48a"
|
|
74
74
|
}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
1
2
|
import type { APIClient } from '@wireapp/api-client';
|
|
2
3
|
import type { AssetOptions } from '@wireapp/api-client/src/asset';
|
|
3
|
-
import type {
|
|
4
|
+
import type { ProgressCallback, RequestCancelable } from '@wireapp/api-client/src/http';
|
|
4
5
|
import type { EncryptedAssetUploaded } from '../cryptography/';
|
|
5
6
|
export declare class AssetService {
|
|
6
7
|
private readonly apiClient;
|
|
7
8
|
constructor(apiClient: APIClient);
|
|
8
|
-
|
|
9
|
-
uploadImageAsset(image: ImageContent, options?: AssetOptions): Promise<EncryptedAssetUploaded>;
|
|
10
|
-
uploadFileAsset(file: FileContent, options?: AssetOptions): Promise<EncryptedAssetUploaded>;
|
|
9
|
+
uploadAsset(plainText: Buffer | Uint8Array, options?: AssetOptions, progressCallback?: ProgressCallback): Promise<RequestCancelable<EncryptedAssetUploaded>>;
|
|
11
10
|
}
|
|
@@ -24,27 +24,23 @@ class AssetService {
|
|
|
24
24
|
constructor(apiClient) {
|
|
25
25
|
this.apiClient = apiClient;
|
|
26
26
|
}
|
|
27
|
-
async
|
|
27
|
+
async uploadAsset(plainText, options, progressCallback) {
|
|
28
28
|
const { cipherText, keyBytes, sha256 } = await (0, AssetCryptography_1.encryptAsset)({
|
|
29
29
|
plainText,
|
|
30
30
|
algorithm: options === null || options === void 0 ? void 0 : options.algorithm,
|
|
31
31
|
hash: options === null || options === void 0 ? void 0 : options.hash,
|
|
32
32
|
});
|
|
33
|
-
const request =
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return this.postAsset(image.data, options);
|
|
45
|
-
}
|
|
46
|
-
uploadFileAsset(file, options) {
|
|
47
|
-
return this.postAsset(file.data, options);
|
|
33
|
+
const request = this.apiClient.asset.api.postAsset(new Uint8Array(cipherText), options, progressCallback);
|
|
34
|
+
return Object.assign(Object.assign({}, request), { response: request.response.then(response => {
|
|
35
|
+
const { key, token } = response;
|
|
36
|
+
return {
|
|
37
|
+
cipherText,
|
|
38
|
+
key,
|
|
39
|
+
keyBytes,
|
|
40
|
+
sha256,
|
|
41
|
+
token,
|
|
42
|
+
};
|
|
43
|
+
}) });
|
|
48
44
|
}
|
|
49
45
|
}
|
|
50
46
|
exports.AssetService = AssetService;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { CipherOptions } from '@wireapp/api-client/src/asset';
|
|
2
1
|
import { Confirmation } from '@wireapp/protocol-messaging';
|
|
3
2
|
import { AbortReason } from '..';
|
|
4
3
|
import { EncryptedAssetUploaded } from '../../cryptography';
|
|
@@ -12,19 +11,15 @@ interface BaseOptions {
|
|
|
12
11
|
messageId?: string;
|
|
13
12
|
}
|
|
14
13
|
interface CreateImageOptions extends BaseOptions {
|
|
15
|
-
cipherOptions?: CipherOptions;
|
|
16
14
|
expectsReadConfirmation?: boolean;
|
|
17
|
-
|
|
15
|
+
asset: EncryptedAssetUploaded;
|
|
18
16
|
image: ImageContent;
|
|
19
17
|
legalHoldStatus?: LegalHoldStatus;
|
|
20
18
|
}
|
|
21
|
-
interface CreateFileOptions {
|
|
22
|
-
cipherOptions?: CipherOptions;
|
|
23
|
-
conversationId: string;
|
|
19
|
+
interface CreateFileOptions extends BaseOptions {
|
|
24
20
|
expectsReadConfirmation?: boolean;
|
|
25
21
|
asset: EncryptedAssetUploaded;
|
|
26
22
|
file: FileContent;
|
|
27
|
-
from: string;
|
|
28
23
|
legalHoldStatus?: LegalHoldStatus;
|
|
29
24
|
originalMessageId: string;
|
|
30
25
|
}
|
|
@@ -70,11 +70,11 @@ class MessageBuilder {
|
|
|
70
70
|
}, id: payload.originalMessageId, type: __1.PayloadBundleType.ASSET_ABORT });
|
|
71
71
|
}
|
|
72
72
|
static createImage(payload) {
|
|
73
|
-
const { expectsReadConfirmation, image,
|
|
73
|
+
const { expectsReadConfirmation, image, asset, legalHoldStatus } = payload;
|
|
74
74
|
return Object.assign(Object.assign({}, createCommonProperties(payload)), { content: {
|
|
75
|
-
asset: imageAsset,
|
|
76
75
|
expectsReadConfirmation,
|
|
77
76
|
image,
|
|
77
|
+
asset,
|
|
78
78
|
legalHoldStatus,
|
|
79
79
|
}, type: __1.PayloadBundleType.ASSET_IMAGE });
|
|
80
80
|
}
|
|
@@ -59,7 +59,10 @@ class MessageService {
|
|
|
59
59
|
plainTextPayload = externalPayload.text;
|
|
60
60
|
cipherText = externalPayload.cipherText;
|
|
61
61
|
}
|
|
62
|
-
const
|
|
62
|
+
const { encrypted, missing } = await this.cryptographyService.encrypt(plainTextPayload, recipients);
|
|
63
|
+
const encryptedPayload = Object.keys(missing).length
|
|
64
|
+
? await this.reencryptAfterMismatch({ missing, deleted: {} }, encrypted, plainText)
|
|
65
|
+
: encrypted;
|
|
63
66
|
const send = (payload) => {
|
|
64
67
|
return options.sendAsProtobuf
|
|
65
68
|
? this.sendOTRProtobufMessage(sendingClientId, payload, Object.assign(Object.assign({}, options), { assetData: cipherText }))
|
|
@@ -97,7 +100,10 @@ class MessageService {
|
|
|
97
100
|
const send = (payload) => {
|
|
98
101
|
return this.sendFederatedOtrMessage(sendingClientId, payload, options);
|
|
99
102
|
};
|
|
100
|
-
const
|
|
103
|
+
const { encrypted, missing } = await this.cryptographyService.encryptQualified(plainText, recipients);
|
|
104
|
+
const encryptedPayload = Object.keys(missing).length
|
|
105
|
+
? await this.reencryptAfterFederatedMismatch({ missing, deleted: {} }, encrypted, plainText)
|
|
106
|
+
: encrypted;
|
|
101
107
|
try {
|
|
102
108
|
return await send(encryptedPayload);
|
|
103
109
|
}
|
|
@@ -216,8 +222,8 @@ class MessageService {
|
|
|
216
222
|
deleted.forEach(({ userId, data }) => data.forEach(clientId => delete recipients[userId.id][clientId]));
|
|
217
223
|
if (missing.length) {
|
|
218
224
|
const missingPreKeyBundles = await this.apiClient.user.api.postMultiPreKeyBundles(mismatch.missing);
|
|
219
|
-
const
|
|
220
|
-
const reEncryptedPayloads = (0, UserClientsUtil_1.flattenUserClients)(
|
|
225
|
+
const { encrypted } = await this.cryptographyService.encrypt(plainText, missingPreKeyBundles);
|
|
226
|
+
const reEncryptedPayloads = (0, UserClientsUtil_1.flattenUserClients)(encrypted);
|
|
221
227
|
// add missing clients to the recipients
|
|
222
228
|
reEncryptedPayloads.forEach(({ data, userId }) => (recipients[userId.id] = Object.assign(Object.assign({}, recipients[userId.id]), data)));
|
|
223
229
|
}
|
|
@@ -238,8 +244,8 @@ class MessageService {
|
|
|
238
244
|
deleted.forEach(({ userId, data }) => data.forEach(clientId => delete recipients[userId.domain][userId.id][clientId]));
|
|
239
245
|
if (Object.keys(missing).length) {
|
|
240
246
|
const missingPreKeyBundles = await this.apiClient.user.api.postQualifiedMultiPreKeyBundles(mismatch.missing);
|
|
241
|
-
const
|
|
242
|
-
const reEncryptedPayloads = (0, UserClientsUtil_1.flattenQualifiedUserClients)(
|
|
247
|
+
const { encrypted } = await this.cryptographyService.encryptQualified(plainText, missingPreKeyBundles);
|
|
248
|
+
const reEncryptedPayloads = (0, UserClientsUtil_1.flattenQualifiedUserClients)(encrypted);
|
|
243
249
|
reEncryptedPayloads.forEach(({ data, userId }) => (recipients[userId.domain][userId.id] = Object.assign(Object.assign({}, recipients[userId.domain][userId.id]), data)));
|
|
244
250
|
}
|
|
245
251
|
return recipients;
|
|
@@ -29,9 +29,14 @@ export declare class CryptographyService {
|
|
|
29
29
|
static convertBase64RecipientsToArray(recipients: OTRRecipients<string>): OTRRecipients<Uint8Array>;
|
|
30
30
|
createCryptobox(): Promise<SerializedPreKey[]>;
|
|
31
31
|
decrypt(sessionId: string, encodedCiphertext: string): Promise<Uint8Array>;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
encryptQualified(plainText: Uint8Array, preKeyBundles: QualifiedUserPreKeyBundleMap | QualifiedUserClients): Promise<{
|
|
33
|
+
missing: QualifiedUserClients;
|
|
34
|
+
encrypted: QualifiedOTRRecipients;
|
|
35
|
+
}>;
|
|
36
|
+
encrypt(plainText: Uint8Array, users: UserPreKeyBundleMap | UserClients, domain?: string): Promise<{
|
|
37
|
+
missing: UserClients;
|
|
38
|
+
encrypted: OTRRecipients<Uint8Array>;
|
|
39
|
+
}>;
|
|
35
40
|
private encryptPayloadForSession;
|
|
36
41
|
initCryptobox(): Promise<void>;
|
|
37
42
|
deleteCryptographyStores(): Promise<boolean[]>;
|
|
@@ -81,41 +81,39 @@ class CryptographyService {
|
|
|
81
81
|
const messageBytes = bazinga64_1.Decoder.fromBase64(encodedCiphertext).asBytes;
|
|
82
82
|
return this.cryptobox.decrypt(sessionId, messageBytes.buffer);
|
|
83
83
|
}
|
|
84
|
-
static dismantleSessionId(sessionId) {
|
|
85
|
-
// see https://regex101.com/r/c8FtCw/1
|
|
86
|
-
const regex = /((?<domain>.+)@)?(?<userId>.+)@(?<clientId>.+)$/g;
|
|
87
|
-
const match = regex.exec(sessionId);
|
|
88
|
-
const { domain, userId, clientId } = (match === null || match === void 0 ? void 0 : match.groups) || {};
|
|
89
|
-
return { clientId, domain, userId };
|
|
90
|
-
}
|
|
91
84
|
async encryptQualified(plainText, preKeyBundles) {
|
|
92
85
|
const qualifiedOTRRecipients = {};
|
|
86
|
+
const missing = {};
|
|
93
87
|
for (const [domain, preKeyBundleMap] of Object.entries(preKeyBundles)) {
|
|
94
|
-
|
|
88
|
+
const result = await this.encrypt(plainText, preKeyBundleMap, domain);
|
|
89
|
+
qualifiedOTRRecipients[domain] = result.encrypted;
|
|
90
|
+
missing[domain] = result.missing;
|
|
95
91
|
}
|
|
96
|
-
return
|
|
92
|
+
return {
|
|
93
|
+
encrypted: qualifiedOTRRecipients,
|
|
94
|
+
missing,
|
|
95
|
+
};
|
|
97
96
|
}
|
|
98
97
|
async encrypt(plainText, users, domain) {
|
|
99
|
-
const
|
|
98
|
+
const encrypted = {};
|
|
99
|
+
const missing = {};
|
|
100
100
|
for (const userId in users) {
|
|
101
101
|
const clientIds = (0, util_1.isUserClients)(users) ? users[userId] : Object.keys(users[userId]);
|
|
102
102
|
for (const clientId of clientIds) {
|
|
103
103
|
const base64PreKey = (0, util_1.isUserClients)(users) ? undefined : users[userId][clientId].key;
|
|
104
104
|
const sessionId = CryptographyService.constructSessionId(userId, clientId, domain || null);
|
|
105
|
-
|
|
105
|
+
const result = await this.encryptPayloadForSession(sessionId, plainText, base64PreKey);
|
|
106
|
+
if (result) {
|
|
107
|
+
encrypted[userId] || (encrypted[userId] = {});
|
|
108
|
+
encrypted[userId][clientId] = result.encryptedPayload;
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
missing[userId] || (missing[userId] = []);
|
|
112
|
+
missing[userId].push(clientId);
|
|
113
|
+
}
|
|
106
114
|
}
|
|
107
115
|
}
|
|
108
|
-
|
|
109
|
-
return payloads.reduce((recipients, payload) => {
|
|
110
|
-
if (!payload) {
|
|
111
|
-
return recipients;
|
|
112
|
-
}
|
|
113
|
-
const { encryptedPayload, sessionId } = payload;
|
|
114
|
-
const { userId, clientId } = CryptographyService.dismantleSessionId(sessionId);
|
|
115
|
-
recipients[userId] || (recipients[userId] = {});
|
|
116
|
-
recipients[userId][clientId] = encryptedPayload;
|
|
117
|
-
return recipients;
|
|
118
|
-
}, {});
|
|
116
|
+
return { encrypted, missing };
|
|
119
117
|
}
|
|
120
118
|
async encryptPayloadForSession(sessionId, plainText, base64EncodedPreKey) {
|
|
121
119
|
this.logger.log(`Encrypting payload for session ID "${sessionId}"`);
|
|
@@ -40,7 +40,7 @@ class LinkPreviewService {
|
|
|
40
40
|
return preview;
|
|
41
41
|
}
|
|
42
42
|
const uploadedLinkPreview = preview;
|
|
43
|
-
const asset = await this.assetService.
|
|
43
|
+
const asset = await (await this.assetService.uploadAsset(linkPreview.image.data)).response;
|
|
44
44
|
uploadedLinkPreview.imageUploaded = {
|
|
45
45
|
asset,
|
|
46
46
|
image,
|