feat(e2ee) add externally managed key mode

This commit is contained in:
tmoldovan8x8 2021-11-16 13:12:10 +02:00 committed by GitHub
parent be6adad61b
commit 2e69ec71c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 138 additions and 13 deletions

View File

@ -826,6 +826,10 @@ var config = {
// format: 'flac'
//
// },
// e2ee: {
// labels,
// externallyManagedKey: false
// },
// Options related to end-to-end (participant to participant) ping.

View File

@ -52,7 +52,7 @@ import {
processExternalDeviceRequest
} from '../../react/features/device-selection/functions';
import { isEnabled as isDropboxEnabled } from '../../react/features/dropbox';
import { toggleE2EE } from '../../react/features/e2ee/actions';
import { setMediaEncryptionKey, toggleE2EE } from '../../react/features/e2ee/actions';
import { setVolume } from '../../react/features/filmstrip';
import { invite } from '../../react/features/invite';
import {
@ -364,6 +364,9 @@ function initCommands() {
logger.debug('Toggle E2EE key command received');
APP.store.dispatch(toggleE2EE(enabled));
},
'set-media-encryption-key': keyInfo => {
APP.store.dispatch(setMediaEncryptionKey(JSON.parse(keyInfo)));
},
'set-video-quality': frameHeight => {
logger.debug('Set video quality command received');
sendAnalytics(createApiEvent('set.video.quality'));

View File

@ -50,6 +50,7 @@ const commands = {
sendTones: 'send-tones',
setFollowMe: 'set-follow-me',
setLargeVideoParticipant: 'set-large-video-participant',
setMediaEncryptionKey: 'set-media-encryption-key',
setParticipantVolume: 'set-participant-volume',
setTileView: 'set-tile-view',
setVideoQuality: 'set-video-quality',
@ -63,6 +64,7 @@ const commands = {
toggleCamera: 'toggle-camera',
toggleCameraMirror: 'toggle-camera-mirror',
toggleChat: 'toggle-chat',
toggleE2EE: 'toggle-e2ee',
toggleFilmStrip: 'toggle-film-strip',
toggleModeration: 'toggle-moderation',
toggleRaiseHand: 'toggle-raise-hand',
@ -1185,6 +1187,40 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
* @returns {void}
*/
stopRecording(mode) {
this.executeCommand('startRecording', mode);
this.executeCommand('stopRecording', mode);
}
/**
* Sets e2ee enabled/disabled.
*
* @param {boolean} enabled - The new value for e2ee enabled.
* @returns {void}
*/
toggleE2EE(enabled) {
this.executeCommand('toggleE2EE', enabled);
}
/**
* Sets the key and keyIndex for e2ee.
*
* @param {Object} keyInfo - Json containing key information.
* @param {CryptoKey} [keyInfo.encryptionKey] - The encryption key.
* @param {number} [keyInfo.index] - The index of the encryption key.
* @returns {void}
*/
async setMediaEncryptionKey(keyInfo) {
const { key, index } = keyInfo;
if (key) {
const exportedKey = await crypto.subtle.exportKey('raw', key);
this.executeCommand('setMediaEncryptionKey', JSON.stringify({
exportedKey: Array.from(new Uint8Array(exportedKey)),
index }));
} else {
this.executeCommand('setMediaEncryptionKey', JSON.stringify({
exportedKey: false,
index }));
}
}
}

View File

@ -123,6 +123,7 @@ export default [
'doNotFlipLocalVideo',
'dropbox',
'e2eeLabels',
'e2ee',
'e2eping',
'enableDisplayNameInStats',
'enableEmailInStats',

View File

@ -314,6 +314,12 @@ function _translateLegacyConfig(oldValue: Object) {
newValue.disableModeratorIndicator = interfaceConfig.DISABLE_FOCUS_INDICATOR;
}
newValue.e2ee = newValue.e2ee || {};
if (oldValue.e2eeLabels) {
newValue.e2ee.e2eeLabels = oldValue.e2eeLabels;
}
return newValue;
}

View File

@ -332,18 +332,23 @@ StateListenerRegistry.register(
*/
function _e2eeUpdated({ getState, dispatch }, conference, participantId, newValue) {
const e2eeEnabled = newValue === 'true';
const { maxMode } = getState()['features/e2ee'] || {};
if (maxMode !== MAX_MODE.THRESHOLD_EXCEEDED || !e2eeEnabled) {
dispatch(toggleE2EE(e2eeEnabled));
}
const { e2ee = {} } = getState()['features/base/config'];
dispatch(participantUpdated({
conference,
id: participantId,
e2eeEnabled
}));
if (e2ee.externallyManagedKey) {
return;
}
const { maxMode } = getState()['features/e2ee'] || {};
if (maxMode !== MAX_MODE.THRESHOLD_EXCEEDED || !e2eeEnabled) {
dispatch(toggleE2EE(e2eeEnabled));
}
}
/**

View File

@ -34,3 +34,12 @@ export const SET_EVERYONE_SUPPORT_E2EE = 'SET_EVERYONE_SUPPORT_E2EE';
* }
*/
export const SET_MAX_MODE = 'SET_MAX_MODE';
/**
* The type of the action which signals to set media encryption key for e2ee.
*
* {
* type: SET_MEDIA_ENCRYPTION_KEY
* }
*/
export const SET_MEDIA_ENCRYPTION_KEY = 'SET_MEDIA_ENCRYPTION_KEY';

View File

@ -1,6 +1,11 @@
// @flow
import { SET_EVERYONE_ENABLED_E2EE, SET_EVERYONE_SUPPORT_E2EE, SET_MAX_MODE, TOGGLE_E2EE } from './actionTypes';
import {
SET_EVERYONE_ENABLED_E2EE,
SET_EVERYONE_SUPPORT_E2EE,
SET_MAX_MODE,
SET_MEDIA_ENCRYPTION_KEY,
TOGGLE_E2EE } from './actionTypes';
/**
* Dispatches an action to enable / disable E2EE.
@ -59,3 +64,21 @@ export function setE2EEMaxMode(maxMode: string) {
maxMode
};
}
/**
* Dispatches an action to set media encryption key.
*
* @param {Object} keyInfo - Json containing key information.
* @param {string} [keyInfo.encryptionKey] - The exported encryption key.
* @param {number} [keyInfo.index] - The index of the encryption key.
* @returns {{
* type: SET_MEDIA_ENCRYPTION_KEY,
* keyInfo: Object
* }}
*/
export function setMediaEncryptionKey(keyInfo: Object) {
return {
type: SET_MEDIA_ENCRYPTION_KEY,
keyInfo
};
}

View File

@ -27,8 +27,10 @@ export type Props = {
* @returns {Props}
*/
export function _mapStateToProps(state: Object) {
const { e2ee = {} } = state['features/base/config'];
return {
_e2eeLabels: state['features/base/config'].e2eeLabels,
_e2eeLabels: e2ee.labels,
_showLabel: state['features/e2ee'].everyoneEnabledE2EE
};
}

View File

@ -17,7 +17,7 @@ import {
import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux';
import { playSound, registerSound, unregisterSound } from '../base/sounds';
import { TOGGLE_E2EE } from './actionTypes';
import { SET_MEDIA_ENCRYPTION_KEY, TOGGLE_E2EE } from './actionTypes';
import { setE2EEMaxMode, setEveryoneEnabledE2EE, setEveryoneSupportE2EE, toggleE2EE } from './actions';
import { E2EE_OFF_SOUND_ID, E2EE_ON_SOUND_ID, MAX_MODE } from './constants';
import { isMaxModeReached, isMaxModeThresholdReached } from './functions';
@ -31,6 +31,8 @@ import { E2EE_OFF_SOUND_FILE, E2EE_ON_SOUND_FILE } from './sounds';
* @returns {Function}
*/
MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
const conference = getCurrentConference(getState);
switch (action.type) {
case APP_WILL_MOUNT:
dispatch(registerSound(
@ -179,8 +181,6 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
}
case TOGGLE_E2EE: {
const conference = getCurrentConference(getState);
if (conference && conference.isE2EEEnabled() !== action.enabled) {
logger.debug(`E2EE will be ${action.enabled ? 'enabled' : 'disabled'}`);
conference.toggleE2EE(action.enabled);
@ -201,6 +201,36 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
break;
}
case SET_MEDIA_ENCRYPTION_KEY: {
if (conference && conference.isE2EESupported()) {
const { exportedKey, index } = action.keyInfo;
if (exportedKey) {
window.crypto.subtle.importKey(
'raw',
new Uint8Array(exportedKey),
'AES-GCM',
false,
[ 'encrypt', 'decrypt' ])
.then(
encryptionKey => {
conference.setMediaEncryptionKey({
encryptionKey,
index
});
})
.catch(error => logger.error('SET_MEDIA_ENCRYPTION_KEY error', error));
} else {
conference.setMediaEncryptionKey({
encryptionKey: false,
index
});
}
}
break;
}
}
return next(action);
@ -229,6 +259,12 @@ StateListenerRegistry.register(
function _updateMaxMode(dispatch, getState) {
const state = getState();
const { e2ee = {} } = state['features/base/config'];
if (e2ee.externallyManagedKey) {
return;
}
if (isMaxModeThresholdReached(state)) {
dispatch(setE2EEMaxMode(MAX_MODE.THRESHOLD_EXCEEDED));
dispatch(toggleE2EE(false));