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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. /* global __filename */
  2. import { createWorkerScript } from './Worker';
  3. import { getLogger } from 'jitsi-meet-logger';
  4. const logger = getLogger(__filename);
  5. // Flag to set on senders / receivers to avoid setting up the encryption transform
  6. // more than once.
  7. const kJitsiE2EE = Symbol('kJitsiE2EE');
  8. /**
  9. * Context encapsulating the cryptography bits required for E2EE.
  10. * This uses the WebRTC Insertable Streams API which is explained in
  11. * https://github.com/alvestrand/webrtc-media-streams/blob/master/explainer.md
  12. * that provides access to the encoded frames and allows them to be transformed.
  13. *
  14. * The encoded frame format is explained below in the _encodeFunction method.
  15. * High level design goals were:
  16. * - do not require changes to existing SFUs and retain (VP8) metadata.
  17. * - allow the SFU to rewrite SSRCs, timestamp, pictureId.
  18. * - allow for the key to be rotated frequently.
  19. */
  20. export default class E2EEcontext {
  21. /**
  22. * Build a new E2EE context instance, which will be used in a given conference.
  23. *
  24. * @param {string} options.salt - Salt to be used for key deviation.
  25. * FIXME: We currently use the MUC room name for this which has the same lifetime
  26. * as this context. While not (pseudo)random as recommended in
  27. * https://developer.mozilla.org/en-US/docs/Web/API/Pbkdf2Params
  28. * this is easily available and the same for all participants.
  29. * We currently do not enforce a minimum length of 16 bytes either.
  30. */
  31. constructor(options) {
  32. this._options = options;
  33. // Initialize the E2EE worker.
  34. this._worker = new Worker(createWorkerScript(), {
  35. name: 'E2EE Worker'
  36. });
  37. this._worker.onerror = e => logger.onerror(e);
  38. // Initialize the salt and convert it once.
  39. const encoder = new TextEncoder();
  40. // Send initial options to worker.
  41. this._worker.postMessage({
  42. operation: 'initialize',
  43. salt: encoder.encode(options.salt)
  44. });
  45. }
  46. /**
  47. * Handles the given {@code RTCRtpReceiver} by creating a {@code TransformStream} which will inject
  48. * a frame decoder.
  49. *
  50. * @param {RTCRtpReceiver} receiver - The receiver which will get the decoding function injected.
  51. * @param {string} kind - The kind of track this receiver belongs to.
  52. * @param {string} participantId - The participant id that this receiver belongs to.
  53. */
  54. handleReceiver(receiver, kind, participantId) {
  55. if (receiver[kJitsiE2EE]) {
  56. return;
  57. }
  58. receiver[kJitsiE2EE] = true;
  59. const receiverStreams
  60. = kind === 'video' ? receiver.createEncodedVideoStreams() : receiver.createEncodedAudioStreams();
  61. this._worker.postMessage({
  62. operation: 'decode',
  63. readableStream: receiverStreams.readableStream,
  64. writableStream: receiverStreams.writableStream,
  65. participantId
  66. }, [ receiverStreams.readableStream, receiverStreams.writableStream ]);
  67. }
  68. /**
  69. * Handles the given {@code RTCRtpSender} by creating a {@code TransformStream} which will inject
  70. * a frame encoder.
  71. *
  72. * @param {RTCRtpSender} sender - The sender which will get the encoding function injected.
  73. * @param {string} kind - The kind of track this sender belongs to.
  74. * @param {string} participantId - The participant id that this sender belongs to.
  75. */
  76. handleSender(sender, kind, participantId) {
  77. if (sender[kJitsiE2EE]) {
  78. return;
  79. }
  80. sender[kJitsiE2EE] = true;
  81. const senderStreams
  82. = kind === 'video' ? sender.createEncodedVideoStreams() : sender.createEncodedAudioStreams();
  83. this._worker.postMessage({
  84. operation: 'encode',
  85. readableStream: senderStreams.readableStream,
  86. writableStream: senderStreams.writableStream,
  87. participantId
  88. }, [ senderStreams.readableStream, senderStreams.writableStream ]);
  89. }
  90. /**
  91. * Sets the key to be used for E2EE.
  92. *
  93. * @param {string} value - Value to be used as the new key. May be falsy to disable end-to-end encryption.
  94. */
  95. setKey(value) {
  96. let key;
  97. if (value) {
  98. const encoder = new TextEncoder();
  99. key = encoder.encode(value);
  100. } else {
  101. key = false;
  102. }
  103. this._worker.postMessage({
  104. operation: 'setKey',
  105. key
  106. });
  107. }
  108. }