您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

E2EEContext.js 5.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. /* global RTCRtpScriptTransform */
  2. import { getLogger } from '@jitsi/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. let workerUrl = `${baseUrl}lib-jitsi-meet.e2ee-worker.js`;
  34. // If there is no baseUrl then we create the worker in a normal way
  35. // as you cant load scripts inside blobs from relative paths.
  36. // See: https://www.html5rocks.com/en/tutorials/workers/basics/#toc-inlineworkers-loadingscripts
  37. if (baseUrl && baseUrl !== '/') {
  38. // Initialize the E2EE worker. In order to avoid CORS issues, start the worker and have it
  39. // synchronously load the JS.
  40. const workerBlob
  41. = new Blob([ `importScripts("${workerUrl}");` ], { type: 'application/javascript' });
  42. workerUrl = window.URL.createObjectURL(workerBlob);
  43. }
  44. this._worker = new Worker(workerUrl, { name: 'E2EE Worker' });
  45. this._worker.onerror = e => logger.error(e);
  46. this._worker.postMessage({
  47. operation: 'initialize',
  48. sharedKey
  49. });
  50. }
  51. /**
  52. * Cleans up all state associated with the given participant. This is needed when a
  53. * participant leaves the current conference.
  54. *
  55. * @param {string} participantId - The participant that just left.
  56. */
  57. cleanup(participantId) {
  58. this._worker.postMessage({
  59. operation: 'cleanup',
  60. participantId
  61. });
  62. }
  63. /**
  64. * Cleans up all state associated with all participants in the conference. This is needed when disabling e2ee.
  65. *
  66. */
  67. cleanupAll() {
  68. this._worker.postMessage({
  69. operation: 'cleanupAll'
  70. });
  71. }
  72. /**
  73. * Handles the given {@code RTCRtpReceiver} by creating a {@code TransformStream} which will inject
  74. * a frame decoder.
  75. *
  76. * @param {RTCRtpReceiver} receiver - The receiver which will get the decoding function injected.
  77. * @param {string} kind - The kind of track this receiver belongs to.
  78. * @param {string} participantId - The participant id that this receiver belongs to.
  79. */
  80. handleReceiver(receiver, kind, participantId) {
  81. if (receiver[kJitsiE2EE]) {
  82. return;
  83. }
  84. receiver[kJitsiE2EE] = true;
  85. if (window.RTCRtpScriptTransform) {
  86. const options = {
  87. operation: 'decode',
  88. participantId
  89. };
  90. receiver.transform = new RTCRtpScriptTransform(this._worker, options);
  91. } else {
  92. const receiverStreams = receiver.createEncodedStreams();
  93. this._worker.postMessage({
  94. operation: 'decode',
  95. readableStream: receiverStreams.readable,
  96. writableStream: receiverStreams.writable,
  97. participantId
  98. }, [ receiverStreams.readable, receiverStreams.writable ]);
  99. }
  100. }
  101. /**
  102. * Handles the given {@code RTCRtpSender} by creating a {@code TransformStream} which will inject
  103. * a frame encoder.
  104. *
  105. * @param {RTCRtpSender} sender - The sender which will get the encoding function injected.
  106. * @param {string} kind - The kind of track this sender belongs to.
  107. * @param {string} participantId - The participant id that this sender belongs to.
  108. */
  109. handleSender(sender, kind, participantId) {
  110. if (sender[kJitsiE2EE]) {
  111. return;
  112. }
  113. sender[kJitsiE2EE] = true;
  114. if (window.RTCRtpScriptTransform) {
  115. const options = {
  116. operation: 'encode',
  117. participantId
  118. };
  119. sender.transform = new RTCRtpScriptTransform(this._worker, options);
  120. } else {
  121. const senderStreams = sender.createEncodedStreams();
  122. this._worker.postMessage({
  123. operation: 'encode',
  124. readableStream: senderStreams.readable,
  125. writableStream: senderStreams.writable,
  126. participantId
  127. }, [ senderStreams.readable, senderStreams.writable ]);
  128. }
  129. }
  130. /**
  131. * Set the E2EE key for the specified participant.
  132. *
  133. * @param {string} participantId - the ID of the participant who's key we are setting.
  134. * @param {Uint8Array | boolean} key - they key for the given participant.
  135. * @param {Number} keyIndex - the key index.
  136. */
  137. setKey(participantId, key, keyIndex) {
  138. this._worker.postMessage({
  139. operation: 'setKey',
  140. key,
  141. keyIndex,
  142. participantId
  143. });
  144. }
  145. }