瀏覽代碼

feat(e2ee): makes olm sessions initialisation lazy

dev1
tmoldovan8x8 4 年之前
父節點
當前提交
2b94da12e8
沒有連結到貢獻者的電子郵件帳戶。
共有 3 個檔案被更改,包括 112 行新增53 行删除
  1. 4
    4
      JitsiConference.js
  2. 19
    5
      modules/e2ee/E2EEncryption.js
  3. 89
    44
      modules/e2ee/OlmAdapter.js

+ 4
- 4
JitsiConference.js 查看文件

@@ -2021,7 +2021,7 @@ JitsiConference.prototype._acceptJvbIncomingCall = function(
2021 2021
     try {
2022 2022
         jingleSession.initialize(this.room, this.rtc, {
2023 2023
             ...this.options.config,
2024
-            enableInsertableStreams: this._isE2EEEnabled()
2024
+            enableInsertableStreams: this.isE2EEEnabled()
2025 2025
         });
2026 2026
     } catch (error) {
2027 2027
         GlobalOnErrorHandler.callErrorHandler(error);
@@ -2774,7 +2774,7 @@ JitsiConference.prototype._acceptP2PIncomingCall = function(
2774 2774
         this.room,
2775 2775
         this.rtc, {
2776 2776
             ...this.options.config,
2777
-            enableInsertableStreams: this._isE2EEEnabled()
2777
+            enableInsertableStreams: this.isE2EEEnabled()
2778 2778
         });
2779 2779
 
2780 2780
     logger.info('Starting CallStats for P2P connection...');
@@ -3134,7 +3134,7 @@ JitsiConference.prototype._startP2PSession = function(remoteJid) {
3134 3134
         this.room,
3135 3135
         this.rtc, {
3136 3136
             ...this.options.config,
3137
-            enableInsertableStreams: this._isE2EEEnabled()
3137
+            enableInsertableStreams: this.isE2EEEnabled()
3138 3138
         });
3139 3139
 
3140 3140
     logger.info('Starting CallStats for P2P connection...');
@@ -3544,7 +3544,7 @@ JitsiConference.prototype._restartMediaSessions = function() {
3544 3544
  *
3545 3545
  * @returns {boolean}
3546 3546
  */
3547
-JitsiConference.prototype._isE2EEEnabled = function() {
3547
+JitsiConference.prototype.isE2EEEnabled = function() {
3548 3548
     return this._e2eEncryption && this._e2eEncryption.isEnabled();
3549 3549
 };
3550 3550
 

+ 19
- 5
modules/e2ee/E2EEncryption.js 查看文件

@@ -6,6 +6,7 @@ import debounce from 'lodash.debounce';
6 6
 import * as JitsiConferenceEvents from '../../JitsiConferenceEvents';
7 7
 import RTCEvents from '../../service/RTC/RTCEvents';
8 8
 import browser from '../browser';
9
+import Deferred from '../util/Deferred';
9 10
 
10 11
 import E2EEContext from './E2EEContext';
11 12
 import { OlmAdapter } from './OlmAdapter';
@@ -32,6 +33,7 @@ export class E2EEncryption {
32 33
         this._enabled = false;
33 34
         this._initialized = false;
34 35
         this._key = undefined;
36
+        this._enabling = undefined;
35 37
 
36 38
         this._e2eeCtx = new E2EEContext();
37 39
         this._olmAdapter = new OlmAdapter(conference);
@@ -120,8 +122,18 @@ export class E2EEncryption {
120 122
             return;
121 123
         }
122 124
 
125
+        this._enabling && await this._enabling;
126
+
127
+        this._enabling = new Deferred();
128
+
123 129
         this._enabled = enabled;
124 130
 
131
+        if (enabled) {
132
+            await this._olmAdapter.initSessions();
133
+        }
134
+
135
+        this.conference.setLocalParticipantProperty('e2ee.enabled', enabled);
136
+
125 137
         if (!this._initialized && enabled) {
126 138
             // Need to re-create the peerconnections in order to apply the insertable streams constraint.
127 139
             // TODO: this was necessary due to some audio issues when indertable streams are used
@@ -136,10 +148,12 @@ export class E2EEncryption {
136 148
         this._key = enabled ? this._generateKey() : false;
137 149
 
138 150
         // Send it to others using the E2EE olm channel.
139
-        this._olmAdapter.updateKey(this._key).then(index => {
140
-            // Set our key so we begin encrypting.
141
-            this._e2eeCtx.setKey(this.conference.myUserId(), this._key, index);
142
-        });
151
+        const index = await this._olmAdapter.updateKey(this._key);
152
+
153
+        // Set our key so we begin encrypting.
154
+        this._e2eeCtx.setKey(this.conference.myUserId(), this._key, index);
155
+
156
+        this._enabling.resolve();
143 157
     }
144 158
 
145 159
     /**
@@ -265,7 +279,7 @@ export class E2EEncryption {
265 279
 
266 280
         this._key = new Uint8Array(newKey);
267 281
 
268
-        const index = await this._olmAdapter.updateCurrentKey(this._key);
282
+        const index = this._olmAdapter.updateCurrentKey(this._key);
269 283
 
270 284
         this._e2eeCtx.setKey(this.conference.myUserId(), this._key, index);
271 285
     }

+ 89
- 44
modules/e2ee/OlmAdapter.js 查看文件

@@ -8,7 +8,7 @@ import { v4 as uuidv4 } from 'uuid';
8 8
 import * as JitsiConferenceEvents from '../../JitsiConferenceEvents';
9 9
 import Deferred from '../util/Deferred';
10 10
 import Listenable from '../util/Listenable';
11
-import { JITSI_MEET_MUC_TYPE } from '../xmpp/xmpp';
11
+import { FEATURE_E2EE, JITSI_MEET_MUC_TYPE } from '../xmpp/xmpp';
12 12
 
13 13
 const logger = getLogger(__filename);
14 14
 
@@ -62,39 +62,59 @@ export class OlmAdapter extends Listenable {
62 62
         this._key = undefined;
63 63
         this._keyIndex = -1;
64 64
         this._reqs = new Map();
65
+        this._sessionInitialization = undefined;
65 66
 
66 67
         if (OlmAdapter.isSupported()) {
67 68
             this._bootstrapOlm();
68 69
 
69 70
             this._conf.on(JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED, this._onEndpointMessageReceived.bind(this));
70
-            this._conf.on(JitsiConferenceEvents.CONFERENCE_JOINED, this._onConferenceJoined.bind(this));
71 71
             this._conf.on(JitsiConferenceEvents.CONFERENCE_LEFT, this._onConferenceLeft.bind(this));
72 72
             this._conf.on(JitsiConferenceEvents.USER_LEFT, this._onParticipantLeft.bind(this));
73
+            this._conf.on(JitsiConferenceEvents.PARTICIPANT_PROPERTY_CHANGED,
74
+                this._onParticipantPropertyChanged.bind(this));
73 75
         } else {
74 76
             this._init.reject(new Error('Olm not supported'));
75 77
         }
76 78
     }
77 79
 
78 80
     /**
79
-     * Indicates if olm is supported on the current platform.
80
-     *
81
-     * @returns {boolean}
81
+     * Starts new olm sessions with every other participant that has the participantId "smaller" the localParticipantId.
82 82
      */
83
-    static isSupported() {
84
-        return typeof window.Olm !== 'undefined';
83
+    async initSessions() {
84
+        if (this._sessionInitialization) {
85
+            throw new Error('OlmAdapte initSessions called multiple times');
86
+        } else {
87
+            this._sessionInitialization = new Deferred();
88
+
89
+            await this._init;
90
+
91
+            const promises = [];
92
+            const localParticipantId = this._conf.myUserId();
93
+
94
+            for (const participant of this._conf.getParticipants()) {
95
+                const participantFeatures = await participant.getFeatures();
96
+
97
+                if (participantFeatures.has(FEATURE_E2EE) && localParticipantId < participant.getId()) {
98
+                    promises.push(this._sendSessionInit(participant));
99
+                }
100
+            }
101
+
102
+            await Promise.allSettled(promises);
103
+
104
+            // TODO: retry failed ones.
105
+
106
+            this._sessionInitialization.resolve();
107
+            this._sessionInitialization = undefined;
108
+        }
85 109
     }
86 110
 
87 111
     /**
88
-     * Updates the current participant key and distributes it to all participants in the conference
89
-     * by sending a key-info message.
112
+     * Indicates if olm is supported on the current platform.
90 113
      *
91
-     * @param {Uint8Array|boolean} key - The new key.
92
-     * @returns {number}
114
+     * @returns {boolean}
93 115
      */
94
-    async updateCurrentKey(key) {
95
-        this._key = key;
96
-
97
-        return this._keyIndex;
116
+    static isSupported() {
117
+        return typeof window.Olm !== 'undefined';
98 118
     }
99 119
 
100 120
     /**
@@ -117,7 +137,6 @@ export class OlmAdapter extends Listenable {
117 137
             const olmData = this._getParticipantOlmData(participant);
118 138
 
119 139
             // TODO: skip those who don't support E2EE.
120
-
121 140
             if (!olmData.session) {
122 141
                 logger.warn(`Tried to send key to participant ${pId} but we have no session`);
123 142
 
@@ -155,6 +174,17 @@ export class OlmAdapter extends Listenable {
155 174
         return this._keyIndex;
156 175
     }
157 176
 
177
+    /**
178
+     * Updates the current participant key.
179
+     * @param {Uint8Array|boolean} key - The new key.
180
+     * @returns {number}
181
+    */
182
+    updateCurrentKey(key) {
183
+        this._key = key;
184
+
185
+        return this._keyIndex;
186
+    }
187
+
158 188
     /**
159 189
      * Internal helper to bootstrap the olm library.
160 190
      *
@@ -215,32 +245,6 @@ export class OlmAdapter extends Listenable {
215 245
         return participant[kOlmData];
216 246
     }
217 247
 
218
-    /**
219
-     * Handles the conference joined event. Upon joining a conference, the participant
220
-     * who just joined will start new olm sessions with every other participant.
221
-     *
222
-     * @private
223
-     */
224
-    async _onConferenceJoined() {
225
-        logger.debug('Conference joined');
226
-
227
-        await this._init;
228
-
229
-        const promises = [];
230
-
231
-        // Establish a 1-to-1 Olm session with every participant in the conference.
232
-        // We are forcing the last user to join the conference to start the exchange
233
-        // so we can send some pre-established secrets in the ACK.
234
-        for (const participant of this._conf.getParticipants()) {
235
-            promises.push(this._sendSessionInit(participant));
236
-        }
237
-
238
-        await Promise.allSettled(promises);
239
-
240
-        // TODO: retry failed ones.
241
-        // TODO: skip participants which don't support E2EE.
242
-    }
243
-
244 248
     /**
245 249
      * Handles leaving the conference, cleaning up olm sessions.
246 250
      *
@@ -311,7 +315,6 @@ export class OlmAdapter extends Listenable {
311 315
                 };
312 316
 
313 317
                 this._sendMessage(ack, pId);
314
-
315 318
                 this.eventEmitter.emit(OlmAdapterEvents.PARTICIPANT_E2EE_CHANNEL_READY, pId);
316 319
             }
317 320
             break;
@@ -427,7 +430,6 @@ export class OlmAdapter extends Listenable {
427 430
             break;
428 431
         }
429 432
         }
430
-
431 433
     }
432 434
 
433 435
     /**
@@ -446,6 +448,49 @@ export class OlmAdapter extends Listenable {
446 448
         }
447 449
     }
448 450
 
451
+    /**
452
+    * Handles an update in a participant's presence property.
453
+    *
454
+    * @param {JitsiParticipant} participant - The participant.
455
+    * @param {string} name - The name of the property that changed.
456
+    * @param {*} oldValue - The property's previous value.
457
+    * @param {*} newValue - The property's new value.
458
+    * @private
459
+    */
460
+    async _onParticipantPropertyChanged(participant, name, oldValue, newValue) {
461
+        switch (name) {
462
+        case 'e2ee.enabled':
463
+            if (newValue && this._conf.isE2EEEnabled()) {
464
+                const localParticipantId = this._conf.myUserId();
465
+                const participantId = participant.getId();
466
+                const participantFeatures = await participant.getFeatures();
467
+
468
+                if (participantFeatures.has(FEATURE_E2EE) && localParticipantId < participantId) {
469
+                    if (this._sessionInitialization) {
470
+                        await this._sessionInitialization;
471
+                    }
472
+                    await this._sendSessionInit(participant);
473
+
474
+                    const olmData = this._getParticipantOlmData(participant);
475
+                    const uuid = uuidv4();
476
+                    const data = {
477
+                        [JITSI_MEET_MUC_TYPE]: OLM_MESSAGE_TYPE,
478
+                        olm: {
479
+                            type: OLM_MESSAGE_TYPES.KEY_INFO,
480
+                            data: {
481
+                                ciphertext: this._encryptKeyInfo(olmData.session),
482
+                                uuid
483
+                            }
484
+                        }
485
+                    };
486
+
487
+                    this._sendMessage(data, participantId);
488
+                }
489
+            }
490
+            break;
491
+        }
492
+    }
493
+
449 494
     /**
450 495
      * Builds and sends an error message to the target participant.
451 496
      *

Loading…
取消
儲存