@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 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": "15.11.1",
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": "18.0.0",
73
- "gitHead": "2881ae7d03e14fda38627186f93079e028f4af17"
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 { FileContent, ImageContent } from '../conversation/content/';
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
- private postAsset;
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 postAsset(plainText, options, progressCallback) {
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 = await this.apiClient.asset.api.postAsset(new Uint8Array(cipherText), options, progressCallback);
34
- const { key, token } = await request.response;
35
- return {
36
- cipherText,
37
- key,
38
- keyBytes,
39
- sha256,
40
- token,
41
- };
42
- }
43
- uploadImageAsset(image, options) {
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
- imageAsset: EncryptedAssetUploaded;
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, imageAsset, legalHoldStatus } = payload;
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 encryptedPayload = await this.cryptographyService.encrypt(plainTextPayload, recipients);
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 encryptedPayload = await this.cryptographyService.encryptQualified(plainText, recipients);
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 reEncrypted = await this.cryptographyService.encrypt(plainText, missingPreKeyBundles);
220
- const reEncryptedPayloads = (0, UserClientsUtil_1.flattenUserClients)(reEncrypted);
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 reEncrypted = await this.cryptographyService.encryptQualified(plainText, missingPreKeyBundles);
242
- const reEncryptedPayloads = (0, UserClientsUtil_1.flattenQualifiedUserClients)(reEncrypted);
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
- private static dismantleSessionId;
33
- encryptQualified(plainText: Uint8Array, preKeyBundles: QualifiedUserPreKeyBundleMap | QualifiedUserClients): Promise<QualifiedOTRRecipients>;
34
- encrypt(plainText: Uint8Array, users: UserPreKeyBundleMap | UserClients, domain?: string): Promise<OTRRecipients<Uint8Array>>;
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
- qualifiedOTRRecipients[domain] = await this.encrypt(plainText, preKeyBundleMap, domain);
88
+ const result = await this.encrypt(plainText, preKeyBundleMap, domain);
89
+ qualifiedOTRRecipients[domain] = result.encrypted;
90
+ missing[domain] = result.missing;
95
91
  }
96
- return qualifiedOTRRecipients;
92
+ return {
93
+ encrypted: qualifiedOTRRecipients,
94
+ missing,
95
+ };
97
96
  }
98
97
  async encrypt(plainText, users, domain) {
99
- const bundles = [];
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
- bundles.push(this.encryptPayloadForSession(sessionId, plainText, base64PreKey));
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
- const payloads = await Promise.all(bundles);
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.uploadImageAsset(linkPreview.image);
43
+ const asset = await (await this.assetService.uploadAsset(linkPreview.image.data)).response;
44
44
  uploadedLinkPreview.imageUploaded = {
45
45
  asset,
46
46
  image,