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.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  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. */
  23. constructor() {
  24. // Determine the URL for the worker script. Relative URLs are relative to
  25. // the entry point, not the script that launches the worker.
  26. let baseUrl = '';
  27. const ljm = document.querySelector('script[src*="lib-jitsi-meet"]');
  28. if (ljm) {
  29. const idx = ljm.src.lastIndexOf('/');
  30. baseUrl = `${ljm.src.substring(0, idx)}/`;
  31. }
  32. // Initialize the E2EE worker. In order to avoid CORS issues, start the worker and have it
  33. // synchronously load the JS.
  34. const workerUrl = `${baseUrl}lib-jitsi-meet.e2ee-worker.js`;
  35. const workerBlob
  36. = new Blob([ `importScripts("${workerUrl}");` ], { type: 'application/javascript' });
  37. const blobUrl = window.URL.createObjectURL(workerBlob);
  38. this._worker = new Worker(blobUrl, { name: 'E2EE Worker' });
  39. this._worker.onerror = e => logger.onerror(e);
  40. }
  41. /**
  42. * Cleans up all state associated with the given participant. This is needed when a
  43. * participant leaves the current conference.
  44. *
  45. * @param {string} participantId - The participant that just left.
  46. */
  47. cleanup(participantId) {
  48. this._worker.postMessage({
  49. operation: 'cleanup',
  50. participantId
  51. });
  52. }
  53. /**
  54. * Handles the given {@code RTCRtpReceiver} by creating a {@code TransformStream} which will inject
  55. * a frame decoder.
  56. *
  57. * @param {RTCRtpReceiver} receiver - The receiver which will get the decoding function injected.
  58. * @param {string} kind - The kind of track this receiver belongs to.
  59. * @param {string} participantId - The participant id that this receiver belongs to.
  60. */
  61. handleReceiver(receiver, kind, participantId) {
  62. if (receiver[kJitsiE2EE]) {
  63. return;
  64. }
  65. receiver[kJitsiE2EE] = true;
  66. if (window.RTCRtpScriptTransform) {
  67. const options = {
  68. operation: 'decode',
  69. participantId
  70. };
  71. receiver.transform = new RTCRtpScriptTransform(this._worker, options);
  72. } else {
  73. const receiverStreams = receiver.createEncodedStreams();
  74. this._worker.postMessage({
  75. operation: 'decode',
  76. readableStream: receiverStreams.readable,
  77. writableStream: receiverStreams.writable,
  78. participantId
  79. }, [ receiverStreams.readable, receiverStreams.writable ]);
  80. }
  81. }
  82. /**
  83. * Handles the given {@code RTCRtpSender} by creating a {@code TransformStream} which will inject
  84. * a frame encoder.
  85. *
  86. * @param {RTCRtpSender} sender - The sender which will get the encoding function injected.
  87. * @param {string} kind - The kind of track this sender belongs to.
  88. * @param {string} participantId - The participant id that this sender belongs to.
  89. */
  90. handleSender(sender, kind, participantId) {
  91. if (sender[kJitsiE2EE]) {
  92. return;
  93. }
  94. sender[kJitsiE2EE] = true;
  95. if (window.RTCRtpScriptTransform) {
  96. const options = {
  97. operation: 'encode',
  98. participantId
  99. };
  100. sender.transform = new RTCRtpScriptTransform(this._worker, options);
  101. } else {
  102. const senderStreams = sender.createEncodedStreams();
  103. this._worker.postMessage({
  104. operation: 'encode',
  105. readableStream: senderStreams.readable,
  106. writableStream: senderStreams.writable,
  107. participantId
  108. }, [ senderStreams.readable, senderStreams.writable ]);
  109. }
  110. }
  111. /**
  112. * Set the E2EE key for the specified participant.
  113. *
  114. * @param {string} participantId - the ID of the participant who's key we are setting.
  115. * @param {Uint8Array | boolean} key - they key for the given participant.
  116. * @param {Number} keyIndex - the key index.
  117. */
  118. setKey(participantId, key, keyIndex) {
  119. this._worker.postMessage({
  120. operation: 'setKey',
  121. participantId,
  122. key,
  123. keyIndex
  124. });
  125. }
  126. }