Skip to content

Commit 33c3d07

Browse files
authored
feat: Support for CoreCrypto 9 [WPB-20699] (#19608)
* feat: Support for CoreCrypto 9 [WPB-20699] * improve logs * fix errors * fix lock file * recover semi reseted mls conversations * bump core * fix: align EnrollmentTimer test with implementation (notAfter field) and clarify test description
1 parent 35ce2e5 commit 33c3d07

File tree

20 files changed

+1009
-1215
lines changed

20 files changed

+1009
-1215
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"@wireapp/avs": "10.0.46",
1717
"@wireapp/avs-debugger": "0.0.7",
1818
"@wireapp/commons": "5.4.5",
19-
"@wireapp/core": "46.35.3",
19+
"@wireapp/core": "46.38.1",
2020
"@wireapp/kalium-backup": "0.0.4",
2121
"@wireapp/promise-queue": "2.4.5",
2222
"@wireapp/react-ui-kit": "9.66.0",

src/script/E2EIdentity/E2EIdentityEnrollment.test.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,13 @@ const generateWireIdentity = (
6767
x509Identity: {
6868
free: jest.fn(),
6969
certificate: '',
70-
display_name: 'John Doe',
70+
displayName: 'John Doe',
7171
domain: 'domain',
7272
handle: 'johndoe',
73-
not_after: BigInt(0),
74-
not_before: BigInt(0),
75-
serial_number: '',
73+
notAfter: BigInt(0),
74+
notBefore: BigInt(0),
75+
serialNumber: '',
76+
[Symbol.dispose]: () => {},
7677
},
7778
thumbprint: '',
7879
credentialType,

src/script/E2EIdentity/EnrollmentTimer/EnrollmentTimer.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ describe('e2ei delays', () => {
4848
{
4949
x509Identity: {
5050
certificate: ' ',
51-
not_after: (Date.now() + validityPeriod) / 1000,
51+
notAfter: (Date.now() + validityPeriod) / 1000,
5252
},
5353
} as any,
5454
Date.now(),
@@ -67,7 +67,7 @@ describe('e2ei delays', () => {
6767
{
6868
x509Identity: {
6969
certificate: ' ',
70-
not_after: deadline / 1000,
70+
notAfter: deadline / 1000,
7171
},
7272
} as any,
7373
Date.now(),
@@ -78,14 +78,14 @@ describe('e2ei delays', () => {
7878
expect(firingDate).toBe(gracePeriodStartingPoint);
7979
});
8080

81-
it('should return a non snoozable timer if device is out of the grace period', () => {
81+
it('should return a snoozable timer scheduled at the start of the grace period if we are not in it yet', () => {
8282
const deadline = Date.now() + gracePeriod + 1000;
8383
const gracePeriodStartingPoint = deadline - gracePeriod;
8484
const {firingDate, isSnoozable} = getEnrollmentTimer(
8585
{
8686
x509Identity: {
8787
certificate: ' ',
88-
not_after: deadline / 1000,
88+
notAfter: deadline / 1000,
8989
},
9090
} as any,
9191
Date.now(),

src/script/E2EIdentity/EnrollmentTimer/EnrollmentTimer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ function getGracePeriod(
8080
// To be sure the device does not expire, we want to keep a safe delay
8181
const safeDelay = randomInt(TimeInMillis.DAY) + messageRetentionTime;
8282

83-
const end = Number(identity?.x509Identity?.not_after) * TimeInMillis.SECOND;
83+
const end = Number(identity?.x509Identity?.notAfter) * TimeInMillis.SECOND;
8484
const start = Math.max(end - safeDelay, end - teamGracePeriodDuration);
8585

8686
return {end, start};

src/script/components/Badge/components/VerificationBadges/VerificationBadges.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ const getMLSStatuses = ({identities, user}: {identities?: WireIdentity[]; user?:
8888
}
8989

9090
return identities.map(identity => {
91-
const matchingName = identity.x509Identity?.display_name === user.name();
91+
const matchingName = identity.x509Identity?.displayName === user.name();
9292
const matchingHandle = checkUserHandle(identity, user);
9393

9494
if (!matchingName || !matchingHandle) {

src/script/main/app.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -581,20 +581,26 @@ export class App {
581581
await conversationRepository.init1To1Conversations(connections, conversations);
582582
if (this.core.hasMLSDevice) {
583583
// add the potential `self` and `team` conversations
584-
await initialiseSelfAndTeamConversations(conversations, selfUser, clientEntity.id, this.core);
584+
await initialiseSelfAndTeamConversations(
585+
conversations,
586+
conversationRepository,
587+
selfUser,
588+
clientEntity.id,
589+
this.core,
590+
);
585591

586592
// join all the mls groups that are known by the user but were migrated to mls
587593
await joinConversationsAfterMigrationFinalisation({
588594
conversations,
589-
selfUser,
595+
conversationRepository,
590596
core: this.core,
591597
onSuccess: conversationRepository.injectJoinedAfterMigrationFinalisationMessage,
592598
onError: ({id}, error) =>
593599
this.logger.error(`Failed when joining a migrated mls conversation with id ${id}, error: `, error),
594600
});
595601

596602
// join all the mls groups we're member of and have not yet joined (eg. we were not send welcome message)
597-
await initMLSGroupConversations(conversations, selfUser, {
603+
await initMLSGroupConversations(conversations, conversationRepository, {
598604
core: this.core,
599605
onError: ({id}, error) =>
600606
this.logger.error(`Failed when initialising mls conversation with id ${id}, error: `, error),

src/script/mls/MLSConversations.test.ts

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import {Account} from '@wireapp/core';
2626
import {MLSConversation} from 'Repositories/conversation/ConversationSelectors';
2727
import {Conversation} from 'Repositories/entity/Conversation';
2828
import {User} from 'Repositories/entity/User';
29+
import {Core} from 'src/script/service/CoreSingleton';
30+
import {TestFactory} from 'test/helper/TestFactory';
2931

3032
import {initMLSGroupConversations, initialiseSelfAndTeamConversations} from './MLSConversations';
3133

@@ -44,20 +46,28 @@ function createMLSConversations(nbConversations: number, type?: CONVERSATION_TYP
4446
}
4547

4648
describe('MLSConversations', () => {
49+
const testFactory = new TestFactory();
50+
51+
afterEach(() => {
52+
jest.clearAllMocks();
53+
});
54+
4755
describe('initMLSGroupConversations', () => {
48-
it('joins all the unestablished MLS groups', async () => {
49-
const core = new Account();
56+
it('joins all the unestablished MLS groups (epoch > 0)', async () => {
5057
const nbMLSConversations = 5 + Math.ceil(Math.random() * 10);
51-
5258
const mlsConversations = createMLSConversations(nbMLSConversations, CONVERSATION_TYPE.REGULAR);
59+
// Force epoch > 0 to trigger join path instead of establish
60+
mlsConversations.forEach(c => (c.epoch = 1));
5361

54-
jest.spyOn(core.service!.conversation, 'mlsGroupExistsLocally').mockResolvedValue(false);
55-
jest.spyOn(core.service!.conversation, 'joinByExternalCommit');
62+
const conversationRepository = await testFactory.exposeConversationActors();
63+
const repositoryCore = (conversationRepository as any).core as Core;
64+
jest.spyOn(repositoryCore.service!.conversation, 'mlsGroupExistsLocally').mockResolvedValue(false);
65+
const joinSpy = jest.spyOn(repositoryCore.service!.conversation, 'joinByExternalCommit');
5666

57-
await initMLSGroupConversations(mlsConversations, new User(), {core});
67+
await initMLSGroupConversations(mlsConversations, conversationRepository, {core: repositoryCore});
5868

5969
for (const conversation of mlsConversations) {
60-
expect(core.service?.conversation.joinByExternalCommit).toHaveBeenCalledWith(conversation.qualifiedId);
70+
expect(joinSpy).toHaveBeenCalledWith(conversation.qualifiedId);
6171
}
6272
});
6373
});
@@ -71,7 +81,8 @@ describe('MLSConversations', () => {
7181
jest.spyOn(core.service!.conversation!, 'mlsGroupExistsLocally').mockResolvedValue(true);
7282
jest.spyOn(core.service!.mls!, 'scheduleKeyMaterialRenewal');
7383

74-
await initMLSGroupConversations(mlsConversations, new User(), {core});
84+
const conversationRepository = await testFactory.exposeConversationActors();
85+
await initMLSGroupConversations(mlsConversations, conversationRepository, {core});
7586

7687
for (const conversation of mlsConversations) {
7788
expect(core.service!.mls!.scheduleKeyMaterialRenewal).toHaveBeenCalledWith(conversation.groupId);
@@ -92,7 +103,9 @@ describe('MLSConversations', () => {
92103
const mlsConversations = createMLSConversations(nbMLSConversations);
93104
const conversations = [teamConversation, ...mlsConversations, selfConversation];
94105

95-
await initialiseSelfAndTeamConversations(conversations, new User(), 'client-1', core);
106+
const conversationRepository = await testFactory.exposeConversationActors();
107+
108+
await initialiseSelfAndTeamConversations(conversations, conversationRepository, new User(), 'client-1', core);
96109

97110
expect(core.service!.mls!.registerConversation).toHaveBeenCalledTimes(2);
98111
});
@@ -112,13 +125,14 @@ describe('MLSConversations', () => {
112125
const mlsConversations = createMLSConversations(nbMLSConversations);
113126
const conversations = [teamConversation, ...mlsConversations, selfConversation];
114127

115-
await initialiseSelfAndTeamConversations(conversations, new User(), 'clientId', core);
128+
const conversationRepository = await testFactory.exposeConversationActors();
129+
130+
await initialiseSelfAndTeamConversations(conversations, conversationRepository, new User(), 'clientId', core);
116131

117132
expect(core.service!.mls!.registerConversation).toHaveBeenCalledTimes(0);
118133
});
119134

120135
it('joins self and team conversation with external commit that have epoch > 0', async () => {
121-
const core = new Account();
122136
const nbMLSConversations = 5 + Math.ceil(Math.random() * 10);
123137

124138
const selfConversation = createMLSConversation();
@@ -132,13 +146,22 @@ describe('MLSConversations', () => {
132146
const mlsConversations = createMLSConversations(nbMLSConversations);
133147
const conversations = [teamConversation, ...mlsConversations, selfConversation];
134148

149+
const conversationRepository = await testFactory.exposeConversationActors();
150+
const repositoryCore = (conversationRepository as any).core as Core;
135151
// MLS group is not yet established locally
136-
jest.spyOn(core.service!.mls!, 'isConversationEstablished').mockResolvedValue(false);
137-
138-
await initialiseSelfAndTeamConversations(conversations, new User(), 'clientId', core);
139-
140-
expect(core.service!.mls!.registerConversation).toHaveBeenCalledTimes(0);
141-
expect(core.service!.conversation!.joinByExternalCommit).toHaveBeenCalledTimes(2);
152+
jest.spyOn(repositoryCore.service!.mls!, 'isConversationEstablished').mockResolvedValue(false);
153+
const joinSpy = jest.spyOn(repositoryCore.service!.conversation!, 'joinByExternalCommit');
154+
155+
await initialiseSelfAndTeamConversations(
156+
conversations,
157+
conversationRepository,
158+
new User(),
159+
'clientId',
160+
repositoryCore,
161+
);
162+
163+
expect(repositoryCore.service!.mls!.registerConversation).toHaveBeenCalledTimes(0);
164+
expect(joinSpy).toHaveBeenCalledTimes(2);
142165
});
143166

144167
it('DOES NOT join self and team conversation with external commit that have epoch > 0, but is already established locally', async () => {
@@ -158,7 +181,8 @@ describe('MLSConversations', () => {
158181

159182
jest.spyOn(core.service!.mls!, 'isConversationEstablished').mockResolvedValue(true);
160183

161-
await initialiseSelfAndTeamConversations(conversations, new User(), 'clientId', core);
184+
const conversationRepository = await testFactory.exposeConversationActors();
185+
await initialiseSelfAndTeamConversations(conversations, conversationRepository, new User(), 'clientId', core);
162186

163187
expect(core.service!.mls!.registerConversation).not.toHaveBeenCalled();
164188
expect(core.service!.conversation!.joinByExternalCommit).not.toHaveBeenCalled();

src/script/mls/MLSConversations.ts

Lines changed: 34 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,9 @@
1717
*
1818
*/
1919

20-
import {QualifiedId} from '@wireapp/api-client/lib/user';
21-
import {KeyPackageClaimUser} from '@wireapp/core/lib/conversation';
22-
2320
import {Account} from '@wireapp/core';
2421

22+
import {ConversationRepository} from 'Repositories/conversation/ConversationRepository';
2523
import {
2624
isMLSCapableConversation,
2725
isMLSConversation,
@@ -32,6 +30,9 @@ import {
3230
} from 'Repositories/conversation/ConversationSelectors';
3331
import {Conversation} from 'Repositories/entity/Conversation';
3432
import {User} from 'Repositories/entity/User';
33+
import {getLogger} from 'Util/Logger';
34+
35+
const logger = getLogger('Webapp/MLSConversations');
3536

3637
/**
3738
* Will initialize all the MLS conversations that the user is member of but that are not yet locally established.
@@ -41,7 +42,7 @@ import {User} from 'Repositories/entity/User';
4142
*/
4243
export async function initMLSGroupConversations(
4344
conversations: Conversation[],
44-
selfUser: User,
45+
conversationRepository: ConversationRepository,
4546
{
4647
core,
4748
onSuccessfulJoin,
@@ -63,7 +64,7 @@ export async function initMLSGroupConversations(
6364
);
6465

6566
for (const mlsConversation of mlsGroupConversations) {
66-
await initMLSGroupConversation(mlsConversation, selfUser.qualifiedId, {
67+
await initMLSGroupConversation(mlsConversation, conversationRepository, {
6768
core,
6869
onSuccessfulJoin,
6970
onError,
@@ -79,7 +80,7 @@ export async function initMLSGroupConversations(
7980
*/
8081
export async function initMLSGroupConversation(
8182
mlsConversation: MLSCapableConversation,
82-
selfUserQualifiedId: QualifiedId,
83+
conversationRepository: ConversationRepository,
8384
{
8485
core,
8586
onSuccessfulJoin,
@@ -100,15 +101,20 @@ export async function initMLSGroupConversation(
100101

101102
const doesMLSGroupExist = await conversationService.mlsGroupExistsLocally(groupId);
102103

103-
//if group is already established, we just schedule periodic key material updates
104+
// if group is already established, we just schedule periodic key material updates
104105
if (doesMLSGroupExist) {
105106
await mlsService.scheduleKeyMaterialRenewal(groupId);
106107
return;
107108
}
108109

109-
//otherwise we should try joining via external commit
110-
await conversationService.joinByExternalCommit(qualifiedId);
111-
await addOtherSelfClientsToMLSConversation(mlsConversation, selfUserQualifiedId, core.clientId, core);
110+
// otherwise we should try to ensure the conversation exists (this will establish it if epoch is 0, or join by external commit if epoch > 0)
111+
console.info('Conversation does not exist, ensuring establishment');
112+
await conversationRepository.ensureConversationExists({
113+
groupId,
114+
conversationId: qualifiedId,
115+
epoch: mlsConversation.epoch,
116+
core,
117+
});
112118

113119
onSuccessfulJoin?.(mlsConversation);
114120
} catch (error) {
@@ -127,6 +133,7 @@ export async function initMLSGroupConversation(
127133
*/
128134
export async function initialiseSelfAndTeamConversations(
129135
conversations: Conversation[],
136+
conversationRepository: ConversationRepository,
130137
selfUser: User,
131138
selfClientId: string,
132139
core: Account,
@@ -154,52 +161,27 @@ export async function initialiseSelfAndTeamConversations(
154161

155162
// If the conversation is already established, we don't need to do anything.
156163
const isGroupAlreadyEstablished = await mlsService.isConversationEstablished(conversation.groupId);
164+
logger.info('Checking if group is already established', {
165+
isGroupAlreadyEstablished,
166+
qualifiedId: conversation.qualifiedId,
167+
});
157168
if (isGroupAlreadyEstablished) {
158169
return Promise.resolve();
159170
}
160171

161-
// Otherwise, we need to join the conversation via external commit.
162-
await conversationService.joinByExternalCommit(conversation.qualifiedId);
163-
await addOtherSelfClientsToMLSConversation(conversation, selfUser.qualifiedId, selfClientId, core);
172+
logger.info('Conversation does not exist, ensuring establishment', {
173+
conversationId: conversation.qualifiedId,
174+
groupId: conversation.groupId,
175+
epoch: conversation.epoch,
176+
});
177+
178+
// Otherwise, we need to ensure the conversation exists by establishing it or joining it by external commit.
179+
await conversationRepository.ensureConversationExists({
180+
conversationId: conversation.qualifiedId,
181+
groupId: conversation.groupId,
182+
epoch: conversation.epoch,
183+
core,
184+
});
164185
}),
165186
);
166187
}
167-
168-
/**
169-
* Will add all other user's self clients to the mls group.
170-
*
171-
* @param conversation id of the conversation
172-
* @param selfUserId id of the self user who's clients should be added
173-
* @param selfClientId id of the current client (that should be skipped)
174-
* @param core instance of the core
175-
*/
176-
export async function addOtherSelfClientsToMLSConversation(
177-
conversation: Conversation,
178-
selfUserId: QualifiedId,
179-
selfClientId: string,
180-
core: Account,
181-
) {
182-
try {
183-
const {groupId, qualifiedId} = conversation;
184-
185-
if (!groupId) {
186-
throw new Error(`No group id found for MLS conversation ${conversation.id}`);
187-
}
188-
189-
const selfQualifiedUser: KeyPackageClaimUser = {
190-
...selfUserId,
191-
skipOwnClientId: selfClientId,
192-
};
193-
194-
await core.service?.conversation.addUsersToMLSConversation({
195-
conversationId: qualifiedId,
196-
groupId,
197-
qualifiedUsers: [selfQualifiedUser],
198-
});
199-
} catch (error) {
200-
console.warn(
201-
`Error when tried to add other self clients to MLS conversation ${conversation.qualifiedId.id} ${conversation.qualifiedId.domain}`,
202-
error,
203-
);
204-
}
205-
}

0 commit comments

Comments
 (0)