123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233 |
- import { getLogger } from '@jitsi/logger';
- import debounce from 'lodash.debounce';
-
- import * as JitsiConferenceEvents from '../../JitsiConferenceEvents';
-
- import { KeyHandler } from './KeyHandler';
- import { OlmAdapter } from './OlmAdapter';
- import { importKey, ratchet } from './crypto-utils';
-
- const logger = getLogger(__filename);
-
- // Period which we'll wait before updating / rotating our keys when a participant
- // joins or leaves.
- const DEBOUNCE_PERIOD = 5000;
-
- /**
- * This module integrates {@link E2EEContext} with {@link OlmAdapter} in order to distribute the keys for encryption.
- */
- export class ManagedKeyHandler extends KeyHandler {
- /**
- * Build a new AutomaticKeyHandler instance, which will be used in a given conference.
- */
- constructor(conference) {
- super(conference);
-
- this._key = undefined;
- this._conferenceJoined = false;
-
- this._olmAdapter = new OlmAdapter(conference);
-
- this._rotateKey = debounce(this._rotateKeyImpl, DEBOUNCE_PERIOD);
- this._ratchetKey = debounce(this._ratchetKeyImpl, DEBOUNCE_PERIOD);
-
- // Olm signalling events.
- this._olmAdapter.on(
- OlmAdapter.events.PARTICIPANT_KEY_UPDATED,
- this._onParticipantKeyUpdated.bind(this));
-
- this._olmAdapter.on(
- OlmAdapter.events.PARTICIPANT_SAS_READY,
- this._onParticipantSasReady.bind(this));
-
- this._olmAdapter.on(
- OlmAdapter.events.PARTICIPANT_SAS_AVAILABLE,
- this._onParticipantSasAvailable.bind(this));
-
- this._olmAdapter.on(
- OlmAdapter.events.PARTICIPANT_VERIFICATION_COMPLETED,
- this._onParticipantVerificationCompleted.bind(this));
-
- this.conference.on(
- JitsiConferenceEvents.PARTICIPANT_PROPERTY_CHANGED,
- this._onParticipantPropertyChanged.bind(this));
- this.conference.on(
- JitsiConferenceEvents.USER_JOINED,
- this._onParticipantJoined.bind(this));
- this.conference.on(
- JitsiConferenceEvents.USER_LEFT,
- this._onParticipantLeft.bind(this));
- this.conference.on(
- JitsiConferenceEvents.CONFERENCE_JOINED,
- () => {
- this._conferenceJoined = true;
- });
- }
-
- /**
- * Returns the sasVerficiation object.
- *
- * @returns {Object}
- */
- get sasVerification() {
- return this._olmAdapter;
- }
-
- /**
- * When E2EE is enabled it initializes sessions and sets the key.
- * Cleans up the sessions when disabled.
- *
- * @param {boolean} enabled - whether E2EE should be enabled or not.
- * @returns {void}
- */
- async _setEnabled(enabled) {
- if (enabled) {
- await this._olmAdapter.initSessions();
- } else {
- this._olmAdapter.clearAllParticipantsSessions();
- }
-
- // Generate a random key in case we are enabling.
- this._key = enabled ? this._generateKey() : false;
-
- // Send it to others using the E2EE olm channel.
- const index = await this._olmAdapter.updateKey(this._key);
-
- // Set our key so we begin encrypting.
- this.e2eeCtx.setKey(this.conference.myUserId(), this._key, index);
- }
-
- /**
- * Handles an update in a participant's presence property.
- *
- * @param {JitsiParticipant} participant - The participant.
- * @param {string} name - The name of the property that changed.
- * @param {*} oldValue - The property's previous value.
- * @param {*} newValue - The property's new value.
- * @private
- */
- async _onParticipantPropertyChanged(participant, name, oldValue, newValue) {
- switch (name) {
- case 'e2ee.idKey':
- logger.debug(`Participant ${participant.getId()} updated their id key: ${newValue}`);
- break;
- case 'e2ee.enabled':
- if (!newValue && this.enabled) {
- this._olmAdapter.clearParticipantSession(participant);
- }
- break;
- }
- }
-
- /**
- * Advances (using ratcheting) the current key when a new participant joins the conference.
- * @private
- */
- _onParticipantJoined() {
- if (this._conferenceJoined && this.enabled) {
- this._ratchetKey();
- }
- }
-
- /**
- * Rotates the current key when a participant leaves the conference.
- * @private
- */
- _onParticipantLeft(id) {
- this.e2eeCtx.cleanup(id);
-
- if (this.enabled) {
- this._rotateKey();
- }
- }
-
- /**
- * Rotates the local key. Rotating the key implies creating a new one, then distributing it
- * to all participants and once they all received it, start using it.
- *
- * @private
- */
- async _rotateKeyImpl() {
- logger.debug('Rotating key');
-
- this._key = this._generateKey();
- const index = await this._olmAdapter.updateKey(this._key);
-
- this.e2eeCtx.setKey(this.conference.myUserId(), this._key, index);
- }
-
- /**
- * Advances the current key by using ratcheting.
- *
- * @private
- */
- async _ratchetKeyImpl() {
- logger.debug('Ratchetting key');
-
- const material = await importKey(this._key);
- const newKey = await ratchet(material);
-
- this._key = new Uint8Array(newKey);
-
- const index = this._olmAdapter.updateCurrentMediaKey(this._key);
-
- this.e2eeCtx.setKey(this.conference.myUserId(), this._key, index);
- }
-
- /**
- * Handles an update in a participant's key.
- *
- * @param {string} id - The participant ID.
- * @param {Uint8Array | boolean} key - The new key for the participant.
- * @param {Number} index - The new key's index.
- * @private
- */
- _onParticipantKeyUpdated(id, key, index) {
- logger.debug(`Participant ${id} updated their key`);
-
- this.e2eeCtx.setKey(id, key, index);
- }
-
- /**
- * Handles the SAS ready event.
- *
- * @param {string} pId - The participant ID.
- * @param {Uint8Array} sas - The bytes from sas.generate_bytes..
- * @private
- */
- _onParticipantSasReady(pId, sas) {
- this.conference.eventEmitter.emit(JitsiConferenceEvents.E2EE_VERIFICATION_READY, pId, sas);
- }
-
- /**
- * Handles the sas available event.
- *
- * @param {string} pId - The participant ID.
- * @private
- */
- _onParticipantSasAvailable(pId) {
- this.conference.eventEmitter.emit(JitsiConferenceEvents.E2EE_VERIFICATION_AVAILABLE, pId);
- }
-
-
- /**
- * Handles the SAS completed event.
- *
- * @param {string} pId - The participant ID.
- * @param {boolean} success - Wheter the verification was succesfull.
- * @private
- */
- _onParticipantVerificationCompleted(pId, success, message) {
- this.conference.eventEmitter.emit(JitsiConferenceEvents.E2EE_VERIFICATION_COMPLETED, pId, success, message);
- }
-
- /**
- * Generates a new 256 bit random key.
- *
- * @returns {Uint8Array}
- * @private
- */
- _generateKey() {
- return window.crypto.getRandomValues(new Uint8Array(32));
- }
- }
|