You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

E2EEContext.js 5.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. /* global __filename, RTCRtpScriptTransform */
  2. import { getLogger } from 'jitsi-meet-logger';
  3. const logger = getLogger(__filename);
  4. // Flag to set on senders / receivers to avoid setting up the encryption transform
  5. // more than once.
  6. const kJitsiE2EE = Symbol('kJitsiE2EE');
  7. /**
  8. * Context encapsulating the cryptography bits required for E2EE.
  9. * This uses the WebRTC Insertable Streams API which is explained in
  10. * https://github.com/alvestrand/webrtc-media-streams/blob/master/explainer.md
  11. * that provides access to the encoded frames and allows them to be transformed.
  12. *
  13. * The encoded frame format is explained below in the _encodeFunction method.
  14. * High level design goals were:
  15. * - do not require changes to existing SFUs and retain (VP8) metadata.
  16. * - allow the SFU to rewrite SSRCs, timestamp, pictureId.
  17. * - allow for the key to be rotated frequently.
  18. */
  19. export default class E2EEcontext {
  20. /**
  21. * Build a new E2EE context instance, which will be used in a given conference.
  22. * @param {boolean} [options.sharedKey] - whether there is a uniques key shared amoung all participants.
  23. */
  24. constructor({ sharedKey } = {}) {
  25. // Determine the URL for the worker script. Relative URLs are relative to
  26. // the entry point, not the script that launches the worker.
  27. let baseUrl = '';
  28. const ljm = document.querySelector('script[src*="lib-jitsi-meet"]');
  29. if (ljm) {
  30. const idx = ljm.src.lastIndexOf('/');
  31. baseUrl = `${ljm.src.substring(0, idx)}/`;
  32. }
  33. // Initialize the E2EE worker. In order to avoid CORS issues, start the worker and have it
  34. // synchronously load the JS.
  35. const workerUrl = `${baseUrl}lib-jitsi-meet.e2ee-worker.js`;
  36. const workerBlob
  37. = new Blob([ `importScripts("${workerUrl}");` ], { type: 'application/javascript' });
  38. const blobUrl = window.URL.createObjectURL(workerBlob);
  39. this._worker = new Worker(blobUrl, { name: 'E2EE Worker' });
  40. this._worker.onerror = e => logger.error(e);
  41. this._worker.postMessage({
  42. operation: 'initialize',
  43. sharedKey
  44. });
  45. }
  46. /**
  47. * Cleans up all state associated with the given participant. This is needed when a
  48. * participant leaves the current conference.
  49. *
  50. * @param {string} participantId - The participant that just left.
  51. */
  52. cleanup(participantId) {
  53. this._worker.postMessage({
  54. operation: 'cleanup',
  55. participantId
  56. });
  57. }
  58. /**
  59. * Cleans up all state associated with all participants in the conference. This is needed when disabling e2ee.
  60. *
  61. */
  62. cleanupAll() {
  63. this._worker.postMessage({
  64. operation: 'cleanupAll'
  65. });
  66. }
  67. /**
  68. * Handles the given {@code RTCRtpReceiver} by creating a {@code TransformStream} which will inject
  69. * a frame decoder.
  70. *
  71. * @param {RTCRtpReceiver} receiver - The receiver which will get the decoding function injected.
  72. * @param {string} kind - The kind of track this receiver belongs to.
  73. * @param {string} participantId - The participant id that this receiver belongs to.
  74. */
  75. handleReceiver(receiver, kind, participantId) {
  76. if (receiver[kJitsiE2EE]) {
  77. return;
  78. }
  79. receiver[kJitsiE2EE] = true;
  80. if (window.RTCRtpScriptTransform) {
  81. const options = {
  82. operation: 'decode',
  83. participantId
  84. };
  85. receiver.transform = new RTCRtpScriptTransform(this._worker, options);
  86. } else {
  87. const receiverStreams = receiver.createEncodedStreams();
  88. this._worker.postMessage({
  89. operation: 'decode',
  90. readableStream: receiverStreams.readable,
  91. writableStream: receiverStreams.writable,
  92. participantId
  93. }, [ receiverStreams.readable, receiverStreams.writable ]);
  94. }
  95. }
  96. /**
  97. * Handles the given {@code RTCRtpSender} by creating a {@code TransformStream} which will inject
  98. * a frame encoder.
  99. *
  100. * @param {RTCRtpSender} sender - The sender which will get the encoding function injected.
  101. * @param {string} kind - The kind of track this sender belongs to.
  102. * @param {string} participantId - The participant id that this sender belongs to.
  103. */
  104. handleSender(sender, kind, participantId) {
  105. if (sender[kJitsiE2EE]) {
  106. return;
  107. }
  108. sender[kJitsiE2EE] = true;
  109. if (window.RTCRtpScriptTransform) {
  110. const options = {
  111. operation: 'encode',
  112. participantId
  113. };
  114. sender.transform = new RTCRtpScriptTransform(this._worker, options);
  115. } else {
  116. const senderStreams = sender.createEncodedStreams();
  117. this._worker.postMessage({
  118. operation: 'encode',
  119. readableStream: senderStreams.readable,
  120. writableStream: senderStreams.writable,
  121. participantId
  122. }, [ senderStreams.readable, senderStreams.writable ]);
  123. }
  124. }
  125. /**
  126. * Set the E2EE key for the specified participant.
  127. *
  128. * @param {string} participantId - the ID of the participant who's key we are setting.
  129. * @param {Uint8Array | boolean} key - they key for the given participant.
  130. * @param {Number} keyIndex - the key index.
  131. */
  132. setKey(participantId, key, keyIndex) {
  133. this._worker.postMessage({
  134. operation: 'setKey',
  135. key,
  136. keyIndex,
  137. participantId
  138. });
  139. }
  140. }