@wireapp/core 46.24.4 → 46.25.0
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/lib/Account.d.ts +7 -8
- package/lib/Account.d.ts.map +1 -1
- package/lib/Account.js +17 -23
- package/lib/client/ClientService.js +1 -1
- package/lib/conversation/ConversationService/ConversationService.d.ts +12 -8
- package/lib/conversation/ConversationService/ConversationService.d.ts.map +1 -1
- package/lib/conversation/ConversationService/ConversationService.js +11 -13
- package/lib/conversation/ConversationService/ConversationService.test.js +5 -11
- package/lib/messagingProtocols/common.types.d.ts +9 -0
- package/lib/messagingProtocols/common.types.d.ts.map +1 -1
- package/lib/messagingProtocols/mls/E2EIdentityService/E2EIService.types.d.ts +2 -2
- package/lib/messagingProtocols/mls/E2EIdentityService/E2EIService.types.d.ts.map +1 -1
- package/lib/messagingProtocols/mls/E2EIdentityService/E2EIService.types.js +2 -1
- package/lib/messagingProtocols/mls/E2EIdentityService/E2EIServiceExternal.d.ts.map +1 -1
- package/lib/messagingProtocols/mls/E2EIdentityService/E2EIServiceExternal.js +6 -5
- package/lib/messagingProtocols/mls/E2EIdentityService/E2EIServiceExternal.test.js +20 -15
- package/lib/messagingProtocols/mls/E2EIdentityService/E2EIServiceInternal.d.ts +9 -3
- package/lib/messagingProtocols/mls/E2EIdentityService/E2EIServiceInternal.d.ts.map +1 -1
- package/lib/messagingProtocols/mls/E2EIdentityService/E2EIServiceInternal.js +30 -12
- package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/messageAdd.d.ts.map +1 -1
- package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/messageAdd.js +7 -2
- package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/messageAdd.test.js +0 -34
- package/lib/messagingProtocols/mls/EventHandler/events/welcomeMessage/welcomeMessage.test.js +2 -2
- package/lib/messagingProtocols/mls/MLSService/MLSService.d.ts +16 -31
- package/lib/messagingProtocols/mls/MLSService/MLSService.d.ts.map +1 -1
- package/lib/messagingProtocols/mls/MLSService/MLSService.js +74 -171
- package/lib/messagingProtocols/mls/MLSService/MLSService.test.js +93 -151
- package/lib/messagingProtocols/mls/types.d.ts +0 -8
- package/lib/messagingProtocols/mls/types.d.ts.map +1 -1
- package/lib/messagingProtocols/proteus/ProteusService/CryptoClient/CoreCryptoWrapper/CoreCryptoWrapper.d.ts +4 -13
- package/lib/messagingProtocols/proteus/ProteusService/CryptoClient/CoreCryptoWrapper/CoreCryptoWrapper.d.ts.map +1 -1
- package/lib/messagingProtocols/proteus/ProteusService/CryptoClient/CoreCryptoWrapper/CoreCryptoWrapper.js +79 -62
- package/lib/messagingProtocols/proteus/ProteusService/CryptoClient/CryptoClient.types.d.ts +0 -2
- package/lib/messagingProtocols/proteus/ProteusService/CryptoClient/CryptoClient.types.d.ts.map +1 -1
- package/lib/messagingProtocols/proteus/ProteusService/ProteusService.d.ts +5 -3
- package/lib/messagingProtocols/proteus/ProteusService/ProteusService.d.ts.map +1 -1
- package/lib/messagingProtocols/proteus/ProteusService/ProteusService.js +14 -14
- package/lib/messagingProtocols/proteus/ProteusService/ProteusService.mocks.d.ts.map +1 -1
- package/lib/messagingProtocols/proteus/ProteusService/ProteusService.mocks.js +3 -1
- package/lib/messagingProtocols/proteus/ProteusService/WithMockedGenerics.test.js +3 -0
- package/lib/messagingProtocols/proteus/Utility/SessionHandler/SessionHandler.test.js +3 -0
- package/lib/secretStore/secretKeyGenerator.d.ts +1 -0
- package/lib/secretStore/secretKeyGenerator.d.ts.map +1 -1
- package/lib/secretStore/secretKeyGenerator.js +3 -1
- package/lib/team/TeamService.d.ts +5 -2
- package/lib/team/TeamService.d.ts.map +1 -1
- package/lib/team/TeamService.js +12 -2
- package/lib/test/StoreHelper.d.ts +2 -0
- package/lib/test/StoreHelper.d.ts.map +1 -0
- package/lib/test/StoreHelper.js +27 -0
- package/package.json +6 -6
|
@@ -6,6 +6,9 @@ export type getTokenCallback = (challengesData?: {
|
|
|
6
6
|
challenge: any;
|
|
7
7
|
keyAuth: string;
|
|
8
8
|
}) => Promise<string | undefined>;
|
|
9
|
+
export type getAllConversationsCallback = () => Promise<{
|
|
10
|
+
group_id: string;
|
|
11
|
+
}[]>;
|
|
9
12
|
export declare class E2EIServiceInternal {
|
|
10
13
|
private readonly coreCryptoClient;
|
|
11
14
|
private readonly apiClient;
|
|
@@ -24,7 +27,10 @@ export declare class E2EIServiceInternal {
|
|
|
24
27
|
* @param getOAuthToken function called when the process needs an oauth token
|
|
25
28
|
* @param refresh should the process refresh the current certificate or get a new one
|
|
26
29
|
*/
|
|
27
|
-
generateCertificate(getOAuthToken: getTokenCallback, refresh: boolean, ciphersuite: Ciphersuite): Promise<
|
|
30
|
+
generateCertificate(getOAuthToken: getTokenCallback, refresh: boolean, getAllConversations: getAllConversationsCallback, ciphersuite: Ciphersuite): Promise<{
|
|
31
|
+
newCrlDistributionPoints: import("@wireapp/core-crypto").NewCrlDistributionPoints;
|
|
32
|
+
keyPackages: Uint8Array[];
|
|
33
|
+
}>;
|
|
28
34
|
private continueCertificateGeneration;
|
|
29
35
|
private initIdentity;
|
|
30
36
|
private getDirectory;
|
|
@@ -42,8 +48,8 @@ export declare class E2EIServiceInternal {
|
|
|
42
48
|
* Stores the received certificate data in local storage for later use
|
|
43
49
|
*
|
|
44
50
|
* @param oAuthIdToken
|
|
45
|
-
* @returns
|
|
51
|
+
* @returns KeyPackages
|
|
46
52
|
*/
|
|
47
|
-
private
|
|
53
|
+
private getKeyPackages;
|
|
48
54
|
}
|
|
49
55
|
//# sourceMappingURL=E2EIServiceInternal.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"E2EIServiceInternal.d.ts","sourceRoot":"","sources":["../../../../src/messagingProtocols/mls/E2EIdentityService/E2EIServiceInternal.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;AAI9C,OAAO,EAAgB,WAAW,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"E2EIServiceInternal.d.ts","sourceRoot":"","sources":["../../../../src/messagingProtocols/mls/E2EIdentityService/E2EIServiceInternal.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;AAI9C,OAAO,EAAgB,WAAW,EAAE,UAAU,EAAiC,MAAM,qBAAqB,CAAC;AAS3G,OAAO,EAAqB,WAAW,EAAiC,MAAM,8BAA8B,CAAC;AAE7G,OAAO,EAAC,YAAY,EAAC,MAAM,yBAAyB,CAAC;AAErD,MAAM,MAAM,gBAAgB,GAAG,CAAC,cAAc,CAAC,EAAE;IAAC,SAAS,EAAE,GAAG,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAC,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;AAEnH,MAAM,MAAM,2BAA2B,GAAG,MAAM,OAAO,CACrD;IACE,QAAQ,EAAE,MAAM,CAAC;CAClB,EAAE,CACJ,CAAC;AACF,qBAAa,mBAAmB;IAO5B,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,wDAAwD;IACxD,OAAO,CAAC,QAAQ,CAAC,cAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAClC,OAAO,CAAC,QAAQ,CAAC,WAAW;IAX9B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoE;IAC3F,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,iBAAiB,CAAiD;gBAGxE,MAAM,EAAE,YAAY,EACH,gBAAgB,EAAE,UAAU,EAC5B,SAAS,EAAE,SAAS;IACrC,wDAAwD;IACvC,cAAc,EAAE,MAAM,EACtB,iBAAiB,EAAE,MAAM,EACzB,WAAW,EAAE,WAAW;IAO3C;;;;OAIG;IACU,mBAAmB,CAC9B,aAAa,EAAE,gBAAgB,EAC/B,OAAO,EAAE,OAAO,EAChB,mBAAmB,EAAE,2BAA2B,EAChD,WAAW,EAAE,WAAW;;;;YAoCZ,6BAA6B;YAa7B,YAAY;YAYZ,YAAY;YAUZ,eAAe;IAQ7B;;;;;OAKG;YACW,uBAAuB;IAyCrC;;;;;;;OAOG;YACW,cAAc;CAoF7B"}
|
|
@@ -21,6 +21,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
21
21
|
exports.E2EIServiceInternal = void 0;
|
|
22
22
|
const commons_1 = require("@wireapp/commons");
|
|
23
23
|
const AcmeServer_1 = require("./Connection/AcmeServer");
|
|
24
|
+
const E2EIService_types_1 = require("./E2EIService.types");
|
|
24
25
|
const Helper_1 = require("./Helper");
|
|
25
26
|
const Account_1 = require("./Steps/Account");
|
|
26
27
|
const Authorization_1 = require("./Steps/Authorization");
|
|
@@ -55,7 +56,7 @@ class E2EIServiceInternal {
|
|
|
55
56
|
* @param getOAuthToken function called when the process needs an oauth token
|
|
56
57
|
* @param refresh should the process refresh the current certificate or get a new one
|
|
57
58
|
*/
|
|
58
|
-
async generateCertificate(getOAuthToken, refresh, ciphersuite) {
|
|
59
|
+
async generateCertificate(getOAuthToken, refresh, getAllConversations, ciphersuite) {
|
|
59
60
|
const stashedEnrollmentData = await this.enrollmentStorage.getPendingEnrollmentData();
|
|
60
61
|
if (stashedEnrollmentData) {
|
|
61
62
|
// In case we have stashed data, we continue the enrollment flow (we are coming back from a redirect)
|
|
@@ -63,7 +64,7 @@ class E2EIServiceInternal {
|
|
|
63
64
|
if (!oAuthToken) {
|
|
64
65
|
throw new Error('No OAuthToken received for in progress enrollment process');
|
|
65
66
|
}
|
|
66
|
-
return this.continueCertificateGeneration(oAuthToken, stashedEnrollmentData);
|
|
67
|
+
return this.continueCertificateGeneration(oAuthToken, stashedEnrollmentData, getAllConversations, ciphersuite);
|
|
67
68
|
}
|
|
68
69
|
// We first get the challenges needed to validate the user identity
|
|
69
70
|
const identity = await this.initIdentity(refresh, ciphersuite);
|
|
@@ -71,7 +72,7 @@ class E2EIServiceInternal {
|
|
|
71
72
|
const { keyauth, oidcChallenge } = enrollmentChallenges.authorization;
|
|
72
73
|
const challengeData = { challenge: oidcChallenge, keyAuth: keyauth };
|
|
73
74
|
// store auth data for continuing the flow later on (in case we are redirected to the identity provider)
|
|
74
|
-
const handle = await this.coreCryptoClient.e2eiEnrollmentStash(identity);
|
|
75
|
+
const handle = await this.coreCryptoClient.transaction(cx => cx.e2eiEnrollmentStash(identity));
|
|
75
76
|
const enrollmentData = {
|
|
76
77
|
handle,
|
|
77
78
|
...enrollmentChallenges,
|
|
@@ -82,19 +83,19 @@ class E2EIServiceInternal {
|
|
|
82
83
|
if (!oAuthToken) {
|
|
83
84
|
throw new Error('No OAuthToken received for in initial enrollment process');
|
|
84
85
|
}
|
|
85
|
-
return this.continueCertificateGeneration(oAuthToken, enrollmentData);
|
|
86
|
+
return this.continueCertificateGeneration(oAuthToken, enrollmentData, getAllConversations, ciphersuite);
|
|
86
87
|
}
|
|
87
|
-
async continueCertificateGeneration(oAuthToken, enrollmentData) {
|
|
88
|
+
async continueCertificateGeneration(oAuthToken, enrollmentData, getAllConversations, cipherSuite) {
|
|
88
89
|
const handle = enrollmentData.handle;
|
|
89
|
-
const identity = await this.coreCryptoClient.e2eiEnrollmentStashPop(handle);
|
|
90
|
-
return this.
|
|
90
|
+
const identity = await this.coreCryptoClient.transaction(cx => cx.e2eiEnrollmentStashPop(handle));
|
|
91
|
+
return this.getKeyPackages(identity, oAuthToken, enrollmentData, getAllConversations, cipherSuite);
|
|
91
92
|
}
|
|
92
93
|
// ############ Internal Functions ############
|
|
93
94
|
async initIdentity(hasActiveCertificate, ciphersuite) {
|
|
94
95
|
const { user } = this.initialData;
|
|
95
96
|
return hasActiveCertificate
|
|
96
|
-
? this.coreCryptoClient.e2eiNewRotateEnrollment(this.certificateTtl, ciphersuite, user.displayName, user.handle, user.teamId)
|
|
97
|
-
: this.coreCryptoClient.e2eiNewActivationEnrollment(user.displayName, user.handle, this.certificateTtl, ciphersuite, user.teamId);
|
|
97
|
+
? this.coreCryptoClient.transaction(cx => cx.e2eiNewRotateEnrollment(this.certificateTtl, ciphersuite, user.displayName, user.handle, user.teamId))
|
|
98
|
+
: this.coreCryptoClient.transaction(cx => cx.e2eiNewActivationEnrollment(user.displayName, user.handle, this.certificateTtl, ciphersuite, user.teamId));
|
|
98
99
|
}
|
|
99
100
|
async getDirectory(identity, connection) {
|
|
100
101
|
const directory = await connection.getDirectory();
|
|
@@ -158,9 +159,9 @@ class E2EIServiceInternal {
|
|
|
158
159
|
* Stores the received certificate data in local storage for later use
|
|
159
160
|
*
|
|
160
161
|
* @param oAuthIdToken
|
|
161
|
-
* @returns
|
|
162
|
+
* @returns KeyPackages
|
|
162
163
|
*/
|
|
163
|
-
async
|
|
164
|
+
async getKeyPackages(identity, oAuthIdToken, enrollmentData, getAllConversations, cipherSuite) {
|
|
164
165
|
// Step 7: Do OIDC client challenge
|
|
165
166
|
const oidcData = await (0, OidcChallenge_1.doWireOidcChallenge)({
|
|
166
167
|
oAuthIdToken,
|
|
@@ -210,7 +211,24 @@ class E2EIServiceInternal {
|
|
|
210
211
|
throw new Error('Error while trying to continue OAuth flow. No certificate received');
|
|
211
212
|
}
|
|
212
213
|
// Step 10: Initialize MLS with the certificate
|
|
213
|
-
return this.coreCryptoClient.
|
|
214
|
+
return this.coreCryptoClient.transaction(async (cx) => {
|
|
215
|
+
const conversations = await getAllConversations();
|
|
216
|
+
const newCrlDistributionPoints = await cx.saveX509Credential(identity, certificate);
|
|
217
|
+
for (const conversation of conversations) {
|
|
218
|
+
if (Boolean(conversation.group_id?.length)) {
|
|
219
|
+
const idAsBytes = new TextEncoder().encode(conversation.group_id);
|
|
220
|
+
await cx.e2eiRotate(idAsBytes);
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
this.logger.error('No group id found in conversation');
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
const keyPackages = await cx.clientKeypackages(cipherSuite, E2EIService_types_1.CredentialType.X509, this.keyPackagesAmount);
|
|
227
|
+
return {
|
|
228
|
+
newCrlDistributionPoints,
|
|
229
|
+
keyPackages,
|
|
230
|
+
};
|
|
231
|
+
});
|
|
214
232
|
}
|
|
215
233
|
}
|
|
216
234
|
exports.E2EIServiceInternal = E2EIServiceInternal;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messageAdd.d.ts","sourceRoot":"","sources":["../../../../../../src/messagingProtocols/mls/EventHandler/events/messageAdd/messageAdd.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAC,8BAA8B,EAAC,MAAM,+BAA+B,CAAC;AAK7E,OAAO,EAAC,mBAAmB,EAAC,MAAM,6BAA6B,CAAC;AAChE,OAAO,EAAC,UAAU,EAAyC,MAAM,gCAAgC,CAAC;AAElG,UAAU,yBAAyB;IACjC,KAAK,EAAE,8BAA8B,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,UAAU,CAAC;CACxB;AAED,eAAO,MAAM,mBAAmB,oCAI7B,yBAAyB,KAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"messageAdd.d.ts","sourceRoot":"","sources":["../../../../../../src/messagingProtocols/mls/EventHandler/events/messageAdd/messageAdd.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAC,8BAA8B,EAAC,MAAM,+BAA+B,CAAC;AAK7E,OAAO,EAAC,mBAAmB,EAAC,MAAM,6BAA6B,CAAC;AAChE,OAAO,EAAC,UAAU,EAAyC,MAAM,gCAAgC,CAAC;AAElG,UAAU,yBAAyB;IACjC,KAAK,EAAE,8BAA8B,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,UAAU,CAAC;CACxB;AAED,eAAO,MAAM,mBAAmB,oCAI7B,yBAAyB,KAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAqChE,CAAC"}
|
|
@@ -25,14 +25,19 @@ const MLSService_1 = require("../../../MLSService/MLSService");
|
|
|
25
25
|
const handleMLSMessageAdd = async ({ event, groupId, mlsService, }) => {
|
|
26
26
|
const encryptedData = bazinga64_1.Decoder.fromBase64(event.data).asBytes;
|
|
27
27
|
const groupIdBytes = bazinga64_1.Decoder.fromBase64(groupId).asBytes;
|
|
28
|
-
const
|
|
28
|
+
const decryptedMessage = await mlsService.decryptMessage(groupIdBytes, encryptedData);
|
|
29
|
+
if (!decryptedMessage) {
|
|
30
|
+
// If the message is not decrypted, we return null
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
const { message, commitDelay, hasEpochChanged, senderClientId: encodedSenderClientId } = decryptedMessage;
|
|
29
34
|
if (encodedSenderClientId) {
|
|
30
35
|
const decoder = new TextDecoder();
|
|
31
36
|
const senderClientId = decoder.decode((0, MLSService_1.optionalToUint8Array)(encodedSenderClientId));
|
|
32
37
|
event.senderClientId = senderClientId;
|
|
33
38
|
}
|
|
34
39
|
// Check if the message includes proposals
|
|
35
|
-
if (typeof commitDelay === 'number'
|
|
40
|
+
if (typeof commitDelay === 'number') {
|
|
36
41
|
// we are dealing with a proposal, add a task to process this proposal later on
|
|
37
42
|
// Those proposals are stored inside of coreCrypto and will be handled after a timeout
|
|
38
43
|
await mlsService.handlePendingProposals({
|
|
@@ -45,45 +45,11 @@ const createMockedMessage = () => {
|
|
|
45
45
|
})).finish();
|
|
46
46
|
};
|
|
47
47
|
describe('handleMLSMessageAdd', () => {
|
|
48
|
-
it('does not handle pending proposals if message does not contain proposals', async () => {
|
|
49
|
-
const event = createMLSMessageAddEventMock({ id: 'conversationId', domain: 'staging.zinfra.io' });
|
|
50
|
-
const mockGroupId = 'AAEAAH87aajaQ011i+rNLmwpy0sAZGl5YS53aXJlLmxpbms=';
|
|
51
|
-
const message = createMockedMessage();
|
|
52
|
-
jest.spyOn(mockedMLSService, 'decryptMessage').mockResolvedValueOnce({
|
|
53
|
-
proposals: [],
|
|
54
|
-
commitDelay: undefined,
|
|
55
|
-
message,
|
|
56
|
-
hasEpochChanged: false,
|
|
57
|
-
isActive: true,
|
|
58
|
-
});
|
|
59
|
-
await (0, messageAdd_1.handleMLSMessageAdd)({ event, mlsService: mockedMLSService, groupId: mockGroupId });
|
|
60
|
-
expect(mockedMLSService.handlePendingProposals).not.toHaveBeenCalled();
|
|
61
|
-
});
|
|
62
|
-
it('handles pending proposals if message includes proposals', async () => {
|
|
63
|
-
const event = createMLSMessageAddEventMock({ id: 'conversationId', domain: 'staging.zinfra.io' });
|
|
64
|
-
const mockGroupId = 'AAEAAH87aajaQ011i+rNLmwpy0sAZGl5YS53aXJlLmxpbms=';
|
|
65
|
-
const message = createMockedMessage();
|
|
66
|
-
jest.spyOn(mockedMLSService, 'decryptMessage').mockResolvedValueOnce({
|
|
67
|
-
proposals: [{ proposal: new Uint8Array(), proposalRef: new Uint8Array(), crlNewDistributionPoints: [] }],
|
|
68
|
-
commitDelay: 2000,
|
|
69
|
-
crlNewDistributionPoints: [],
|
|
70
|
-
message,
|
|
71
|
-
hasEpochChanged: false,
|
|
72
|
-
isActive: true,
|
|
73
|
-
});
|
|
74
|
-
await (0, messageAdd_1.handleMLSMessageAdd)({ event, mlsService: mockedMLSService, groupId: mockGroupId });
|
|
75
|
-
expect(mockedMLSService.handlePendingProposals).toHaveBeenCalledWith({
|
|
76
|
-
groupId: mockGroupId,
|
|
77
|
-
delayInMs: 2000,
|
|
78
|
-
eventTime: event.time,
|
|
79
|
-
});
|
|
80
|
-
});
|
|
81
48
|
it('emits "newEpoch" event if incoming message has advanced epoch number', async () => {
|
|
82
49
|
const event = createMLSMessageAddEventMock({ id: 'conversationId', domain: 'staging.zinfra.io' });
|
|
83
50
|
const mockGroupId = 'AAEAAH87aajaQ011i+rNLmwpy0sAZGl5YS53aXJlLmxpbms=';
|
|
84
51
|
const message = createMockedMessage();
|
|
85
52
|
jest.spyOn(mockedMLSService, 'decryptMessage').mockResolvedValueOnce({
|
|
86
|
-
proposals: [],
|
|
87
53
|
message,
|
|
88
54
|
hasEpochChanged: true,
|
|
89
55
|
isActive: true,
|
package/lib/messagingProtocols/mls/EventHandler/events/welcomeMessage/welcomeMessage.test.js
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
21
|
const event_1 = require("@wireapp/api-client/lib/event");
|
|
22
22
|
const welcomeMessage_1 = require("./welcomeMessage");
|
|
23
|
-
const
|
|
23
|
+
const MLSService_1 = require("../../../MLSService");
|
|
24
24
|
jest.mock('bazinga64', () => ({
|
|
25
25
|
...jest.requireActual('bazinga64'),
|
|
26
26
|
Decoder: {
|
|
@@ -62,7 +62,7 @@ describe('MLS welcomeMessage eventHandler', () => {
|
|
|
62
62
|
it('emits new epoch event after processing a welcome message', async () => {
|
|
63
63
|
jest.spyOn(mockParams.mlsService, 'getEpoch').mockResolvedValue(1);
|
|
64
64
|
await (0, welcomeMessage_1.handleMLSWelcomeMessage)(mockParams);
|
|
65
|
-
expect(mockParams.mlsService.emit).toHaveBeenCalledWith(
|
|
65
|
+
expect(mockParams.mlsService.emit).toHaveBeenCalledWith(MLSService_1.MLSServiceEvents.NEW_EPOCH, {
|
|
66
66
|
groupId: 'conversationId',
|
|
67
67
|
epoch: 1,
|
|
68
68
|
});
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import type { MLSPublicKeyRecord, RegisteredClient } from '@wireapp/api-client/lib/client';
|
|
2
|
-
import {
|
|
2
|
+
import { SUBCONVERSATION_ID } from '@wireapp/api-client/lib/conversation';
|
|
3
3
|
import { ConversationMLSMessageAddEvent, ConversationMLSWelcomeEvent } from '@wireapp/api-client/lib/event';
|
|
4
4
|
import { QualifiedId } from '@wireapp/api-client/lib/user';
|
|
5
5
|
import { APIClient } from '@wireapp/api-client';
|
|
6
6
|
import { TypedEventEmitter } from '@wireapp/commons';
|
|
7
|
-
import {
|
|
7
|
+
import { Ciphersuite, ConversationId, CoreCrypto, DecryptedMessage } from '@wireapp/core-crypto';
|
|
8
8
|
import { AddUsersFailure, KeyPackageClaimUser } from '../../../conversation';
|
|
9
9
|
import { CoreDatabase } from '../../../storage/CoreDB';
|
|
10
10
|
import { RecurringTaskScheduler } from '../../../util/RecurringTaskScheduler';
|
|
11
11
|
import { User } from '../E2EIdentityService';
|
|
12
|
-
import { getTokenCallback } from '../E2EIdentityService/E2EIServiceInternal';
|
|
12
|
+
import { getAllConversationsCallback, getTokenCallback } from '../E2EIdentityService/E2EIServiceInternal';
|
|
13
13
|
import { ClientId, HandlePendingProposalsParams } from '../types';
|
|
14
14
|
type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
|
|
15
15
|
interface MLSConfig {
|
|
@@ -33,7 +33,8 @@ export declare const optionalToUint8Array: (array: Uint8Array | []) => Uint8Arra
|
|
|
33
33
|
export declare enum MLSServiceEvents {
|
|
34
34
|
NEW_EPOCH = "newEpoch",
|
|
35
35
|
MLS_CLIENT_MISMATCH = "mlsClientMismatch",
|
|
36
|
-
NEW_CRL_DISTRIBUTION_POINTS = "newCrlDistributionPoints"
|
|
36
|
+
NEW_CRL_DISTRIBUTION_POINTS = "newCrlDistributionPoints",
|
|
37
|
+
MLS_EVENT_DISTRIBUTED = "mlsEventDistributed"
|
|
37
38
|
}
|
|
38
39
|
type Events = {
|
|
39
40
|
[MLSServiceEvents.NEW_EPOCH]: {
|
|
@@ -42,6 +43,10 @@ type Events = {
|
|
|
42
43
|
};
|
|
43
44
|
[MLSServiceEvents.NEW_CRL_DISTRIBUTION_POINTS]: string[];
|
|
44
45
|
[MLSServiceEvents.MLS_CLIENT_MISMATCH]: void;
|
|
46
|
+
[MLSServiceEvents.MLS_EVENT_DISTRIBUTED]: {
|
|
47
|
+
events: any;
|
|
48
|
+
time: string;
|
|
49
|
+
};
|
|
45
50
|
};
|
|
46
51
|
export declare class MLSService extends TypedEventEmitter<Events> {
|
|
47
52
|
private readonly apiClient;
|
|
@@ -52,7 +57,6 @@ export declare class MLSService extends TypedEventEmitter<Events> {
|
|
|
52
57
|
private _config?;
|
|
53
58
|
private readonly textEncoder;
|
|
54
59
|
private readonly textDecoder;
|
|
55
|
-
private readonly conflictBackoffQueue;
|
|
56
60
|
constructor(apiClient: APIClient, coreCryptoClient: CoreCrypto, coreDatabase: CoreDatabase, recurringTaskScheduler: RecurringTaskScheduler);
|
|
57
61
|
/**
|
|
58
62
|
* return true if the MLS service if configured and ready to be used
|
|
@@ -73,7 +77,6 @@ export declare class MLSService extends TypedEventEmitter<Events> {
|
|
|
73
77
|
*/
|
|
74
78
|
isInitializedMLSClient: (client: RegisteredClient) => boolean;
|
|
75
79
|
private getCredentialType;
|
|
76
|
-
private uploadCommitBundle;
|
|
77
80
|
private readonly _uploadCommitBundle;
|
|
78
81
|
/**
|
|
79
82
|
* Will add users to an existing MLS group and send a commit bundle to backend.
|
|
@@ -82,9 +85,7 @@ export declare class MLSService extends TypedEventEmitter<Events> {
|
|
|
82
85
|
* @param groupId - the group id of the MLS group
|
|
83
86
|
* @param keyPackages - the list of keys of clients to add to the MLS group
|
|
84
87
|
*/
|
|
85
|
-
addUsersToExistingConversation(groupId: string, keyPackages: Uint8Array[]): Promise<
|
|
86
|
-
failures: AddUsersFailure[];
|
|
87
|
-
}>;
|
|
88
|
+
addUsersToExistingConversation(groupId: string, keyPackages: Uint8Array[]): Promise<void>;
|
|
88
89
|
/**
|
|
89
90
|
* Will return a list of client ids which are already in the group at core crypto level
|
|
90
91
|
*
|
|
@@ -97,24 +98,12 @@ export declare class MLSService extends TypedEventEmitter<Events> {
|
|
|
97
98
|
failures: AddUsersFailure[];
|
|
98
99
|
}>;
|
|
99
100
|
getEpoch(groupId: string | Uint8Array): Promise<number>;
|
|
100
|
-
|
|
101
|
-
joinByExternalCommit(getGroupInfo: () => Promise<Uint8Array>): Promise<PostMlsMessageResponse>;
|
|
101
|
+
joinByExternalCommit(getGroupInfo: () => Promise<Uint8Array>): Promise<void>;
|
|
102
102
|
exportSecretKey(groupId: string, keyLength: number): Promise<string>;
|
|
103
103
|
private dispatchNewCrlDistributionPoints;
|
|
104
104
|
processWelcomeMessage(welcomeMessage: Uint8Array): Promise<ConversationId>;
|
|
105
|
-
decryptMessage(conversationId: ConversationId, payload: Uint8Array): Promise<DecryptedMessage>;
|
|
105
|
+
decryptMessage(conversationId: ConversationId, payload: Uint8Array): Promise<DecryptedMessage | undefined>;
|
|
106
106
|
encryptMessage(conversationId: ConversationId, message: Uint8Array): Promise<Uint8Array>;
|
|
107
|
-
/**
|
|
108
|
-
* Will wrap a coreCrypto call that generates a CommitBundle and do all the necessary work so that commitbundle is handled the right way.
|
|
109
|
-
* It does:
|
|
110
|
-
* - commit the pending proposal
|
|
111
|
-
* - then generates the commitBundle with the given function
|
|
112
|
-
* - uploads the commitBundle to backend
|
|
113
|
-
* - warns coreCrypto that the commit was successfully processed
|
|
114
|
-
* @param groupId
|
|
115
|
-
* @param generateCommit The function that will generate a coreCrypto CommitBundle
|
|
116
|
-
*/
|
|
117
|
-
private processCommitAction;
|
|
118
107
|
private updateKeyingMaterial;
|
|
119
108
|
/**
|
|
120
109
|
* Will create an empty conversation inside of coreCrypto.
|
|
@@ -135,9 +124,7 @@ export declare class MLSService extends TypedEventEmitter<Events> {
|
|
|
135
124
|
client?: string;
|
|
136
125
|
};
|
|
137
126
|
parentGroupId?: string;
|
|
138
|
-
}): Promise<
|
|
139
|
-
failures: AddUsersFailure[];
|
|
140
|
-
}>;
|
|
127
|
+
}): Promise<AddUsersFailure[]>;
|
|
141
128
|
/**
|
|
142
129
|
* Will create a 1:1 conversation inside of coreCrypto, try claiming key packages for user and (if succesfull) add them to the MLS group.
|
|
143
130
|
* @param groupId the id of the group to create inside of coreCrypto
|
|
@@ -147,9 +134,7 @@ export declare class MLSService extends TypedEventEmitter<Events> {
|
|
|
147
134
|
register1to1Conversation(groupId: string, userId: QualifiedId, selfUser: {
|
|
148
135
|
user: QualifiedId;
|
|
149
136
|
client: string;
|
|
150
|
-
}, removalKeyFor1to1Signature?: MLSPublicKeyRecord): Promise<
|
|
151
|
-
failures: AddUsersFailure[];
|
|
152
|
-
}>;
|
|
137
|
+
}, removalKeyFor1to1Signature?: MLSPublicKeyRecord): Promise<AddUsersFailure[]>;
|
|
153
138
|
/**
|
|
154
139
|
* Will try to register mls group and send an empty commit to establish it.
|
|
155
140
|
*
|
|
@@ -162,7 +147,7 @@ export declare class MLSService extends TypedEventEmitter<Events> {
|
|
|
162
147
|
* @param groupId groupId of the conversation
|
|
163
148
|
* @param clientIds the list of **qualified** ids of the clients we want to remove from the group
|
|
164
149
|
*/
|
|
165
|
-
removeClientsFromConversation(groupId: string, clientIds: ClientId[]): Promise<
|
|
150
|
+
removeClientsFromConversation(groupId: string, clientIds: ClientId[]): Promise<void>;
|
|
166
151
|
/**
|
|
167
152
|
* Will check if mls group exists in corecrypto.
|
|
168
153
|
* @param groupId groupId of the conversation
|
|
@@ -273,7 +258,7 @@ export declare class MLSService extends TypedEventEmitter<Events> {
|
|
|
273
258
|
* @param oAuthIdToken The OAuth id token if the user is already authenticated
|
|
274
259
|
* @returns AcmeChallenge if the user is not authenticated, true if the user is authenticated
|
|
275
260
|
*/
|
|
276
|
-
enrollE2EI(discoveryUrl: string, user: User, client: RegisteredClient, nbPrekeys: number, certificateTtl: number, getOAuthToken: getTokenCallback): Promise<void>;
|
|
261
|
+
enrollE2EI(discoveryUrl: string, user: User, client: RegisteredClient, nbPrekeys: number, certificateTtl: number, getOAuthToken: getTokenCallback, getAllConversations: getAllConversationsCallback): Promise<void>;
|
|
277
262
|
}
|
|
278
263
|
export {};
|
|
279
264
|
//# sourceMappingURL=MLSService.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MLSService.d.ts","sourceRoot":"","sources":["../../../../src/messagingProtocols/mls/MLSService/MLSService.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAqB,kBAAkB,EAAE,gBAAgB,EAAC,MAAM,gCAAgC,CAAC;AAC7G,OAAO,EAAC,
|
|
1
|
+
{"version":3,"file":"MLSService.d.ts","sourceRoot":"","sources":["../../../../src/messagingProtocols/mls/MLSService/MLSService.ts"],"names":[],"mappings":"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"}
|