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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. /* global __filename */
  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. * @param {string} options.salt - Salt to be used for key deviation.
  24. * FIXME: We currently use the MUC room name for this which has the same lifetime
  25. * as this context. While not (pseudo)random as recommended in
  26. * https://developer.mozilla.org/en-US/docs/Web/API/Pbkdf2Params
  27. * this is easily available and the same for all participants.
  28. * We currently do not enforce a minimum length of 16 bytes either.
  29. */
  30. constructor(options) {
  31. this._options = options;
  32. // Determine the URL for the worker script. Relative URLs are relative to
  33. // the entry point, not the script that launches the worker.
  34. let baseUrl = '';
  35. const ljm = document.querySelector('script[src*="lib-jitsi-meet"]');
  36. if (ljm) {
  37. const idx = ljm.src.lastIndexOf('/');
  38. baseUrl = `${ljm.src.substring(0, idx)}/`;
  39. }
  40. // Initialize the E2EE worker.
  41. this._worker = new Worker(`${baseUrl}lib-jitsi-meet.e2ee-worker.js`, { name: 'E2EE Worker' });
  42. this._worker.onerror = e => logger.onerror(e);
  43. // Initialize the salt and convert it once.
  44. const encoder = new TextEncoder();
  45. // Send initial options to worker.
  46. this._worker.postMessage({
  47. operation: 'initialize',
  48. salt: encoder.encode(options.salt)
  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. * Handles the given {@code RTCRtpReceiver} by creating a {@code TransformStream} which will inject
  65. * a frame decoder.
  66. *
  67. * @param {RTCRtpReceiver} receiver - The receiver which will get the decoding function injected.
  68. * @param {string} kind - The kind of track this receiver belongs to.
  69. * @param {string} participantId - The participant id that this receiver belongs to.
  70. */
  71. handleReceiver(receiver, kind, participantId) {
  72. if (receiver[kJitsiE2EE]) {
  73. return;
  74. }
  75. receiver[kJitsiE2EE] = true;
  76. let receiverStreams;
  77. if (receiver.createEncodedStreams) {
  78. receiverStreams = receiver.createEncodedStreams();
  79. } else {
  80. receiverStreams = kind === 'video' ? receiver.createEncodedVideoStreams()
  81. : receiver.createEncodedAudioStreams();
  82. }
  83. this._worker.postMessage({
  84. operation: 'decode',
  85. readableStream: receiverStreams.readable || receiverStreams.readableStream,
  86. writableStream: receiverStreams.writable || receiverStreams.writableStream,
  87. participantId
  88. }, [ receiverStreams.readable || receiverStreams.readableStream,
  89. receiverStreams.writable || receiverStreams.writableStream ]);
  90. }
  91. /**
  92. * Handles the given {@code RTCRtpSender} by creating a {@code TransformStream} which will inject
  93. * a frame encoder.
  94. *
  95. * @param {RTCRtpSender} sender - The sender which will get the encoding function injected.
  96. * @param {string} kind - The kind of track this sender belongs to.
  97. * @param {string} participantId - The participant id that this sender belongs to.
  98. */
  99. handleSender(sender, kind, participantId) {
  100. if (sender[kJitsiE2EE]) {
  101. return;
  102. }
  103. sender[kJitsiE2EE] = true;
  104. let senderStreams;
  105. if (sender.createEncodedStreams) {
  106. senderStreams = sender.createEncodedStreams();
  107. } else {
  108. senderStreams = kind === 'video' ? sender.createEncodedVideoStreams()
  109. : sender.createEncodedAudioStreams();
  110. }
  111. this._worker.postMessage({
  112. operation: 'encode',
  113. readableStream: senderStreams.readable || senderStreams.readableStream,
  114. writableStream: senderStreams.writable || senderStreams.writableStream,
  115. participantId
  116. }, [ senderStreams.readable || senderStreams.readableStream,
  117. senderStreams.writable || senderStreams.writableStream ]);
  118. }
  119. /**
  120. * Set the E2EE key for the specified participant.
  121. *
  122. * @param {string} participantId - the ID of the participant who's key we are setting.
  123. * @param {Uint8Array | boolean} key - they key for the given participant.
  124. * @param {Number} keyIndex - the key index.
  125. */
  126. setKey(participantId, key, keyIndex) {
  127. this._worker.postMessage({
  128. operation: 'setKey',
  129. participantId,
  130. key,
  131. keyIndex
  132. });
  133. }
  134. }