werift 0.22.9 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/lib/dtls/src/flight/server/flight6.js +12 -2
  2. package/lib/dtls/src/flight/server/flight6.js.map +1 -1
  3. package/lib/dtls/src/server.js +6 -1
  4. package/lib/dtls/src/server.js.map +1 -1
  5. package/lib/dtls/src/socket.d.ts +1 -0
  6. package/lib/dtls/src/socket.js +3 -0
  7. package/lib/dtls/src/socket.js.map +1 -1
  8. package/lib/index.mjs +434 -75
  9. package/lib/nonstandard/index.mjs +207 -40
  10. package/lib/rtp/src/index.d.ts +1 -0
  11. package/lib/rtp/src/index.js +1 -0
  12. package/lib/rtp/src/index.js.map +1 -1
  13. package/lib/rtp/src/srtp/cipher/ctr.d.ts +3 -3
  14. package/lib/rtp/src/srtp/cipher/ctr.js +25 -14
  15. package/lib/rtp/src/srtp/cipher/ctr.js.map +1 -1
  16. package/lib/rtp/src/srtp/cipher/gcm.d.ts +3 -3
  17. package/lib/rtp/src/srtp/cipher/gcm.js +57 -20
  18. package/lib/rtp/src/srtp/cipher/gcm.js.map +1 -1
  19. package/lib/rtp/src/srtp/cipher/index.d.ts +1 -1
  20. package/lib/rtp/src/srtp/cipher/index.js +1 -1
  21. package/lib/rtp/src/srtp/cipher/index.js.map +1 -1
  22. package/lib/rtp/src/srtp/context/srtp.d.ts +2 -1
  23. package/lib/rtp/src/srtp/context/srtp.js +22 -5
  24. package/lib/rtp/src/srtp/context/srtp.js.map +1 -1
  25. package/lib/rtp/src/srtp/error.d.ts +3 -0
  26. package/lib/rtp/src/srtp/error.js +11 -0
  27. package/lib/rtp/src/srtp/error.js.map +1 -0
  28. package/lib/rtp/src/srtp/packet.d.ts +5 -0
  29. package/lib/rtp/src/srtp/packet.js +48 -0
  30. package/lib/rtp/src/srtp/packet.js.map +1 -0
  31. package/lib/webrtc/src/dataChannel.d.ts +1 -0
  32. package/lib/webrtc/src/dataChannel.js +5 -2
  33. package/lib/webrtc/src/dataChannel.js.map +1 -1
  34. package/lib/webrtc/src/peerConnection.d.ts +4 -1
  35. package/lib/webrtc/src/peerConnection.js +26 -5
  36. package/lib/webrtc/src/peerConnection.js.map +1 -1
  37. package/lib/webrtc/src/sctpManager.d.ts +1 -1
  38. package/lib/webrtc/src/sctpManager.js +3 -2
  39. package/lib/webrtc/src/sctpManager.js.map +1 -1
  40. package/lib/webrtc/src/sdpManager.d.ts +1 -1
  41. package/lib/webrtc/src/sdpManager.js +3 -4
  42. package/lib/webrtc/src/sdpManager.js.map +1 -1
  43. package/lib/webrtc/src/secureTransportManager.js +1 -1
  44. package/lib/webrtc/src/secureTransportManager.js.map +1 -1
  45. package/lib/webrtc/src/transceiverManager.js +3 -2
  46. package/lib/webrtc/src/transceiverManager.js.map +1 -1
  47. package/lib/webrtc/src/transport/dtls.d.ts +1 -0
  48. package/lib/webrtc/src/transport/dtls.js +117 -12
  49. package/lib/webrtc/src/transport/dtls.js.map +1 -1
  50. package/lib/webrtc/src/transport/sctp.d.ts +9 -3
  51. package/lib/webrtc/src/transport/sctp.js +35 -7
  52. package/lib/webrtc/src/transport/sctp.js.map +1 -1
  53. package/lib/webrtc/src/utils.d.ts +2 -0
  54. package/lib/webrtc/src/utils.js +20 -0
  55. package/lib/webrtc/src/utils.js.map +1 -1
  56. package/package.json +1 -1
package/lib/index.mjs CHANGED
@@ -4646,7 +4646,12 @@ import { createHmac as createHmac3 } from "crypto";
4646
4646
  import AES from "aes-js";
4647
4647
 
4648
4648
  // ../rtp/src/srtp/cipher/ctr.ts
4649
- import { createCipheriv as createCipheriv2, createDecipheriv as createDecipheriv2, createHmac as createHmac2 } from "crypto";
4649
+ import {
4650
+ createCipheriv as createCipheriv2,
4651
+ createDecipheriv as createDecipheriv2,
4652
+ createHmac as createHmac2,
4653
+ timingSafeEqual
4654
+ } from "crypto";
4650
4655
 
4651
4656
  // ../rtp/src/srtp/cipher/index.ts
4652
4657
  var CipherAesBase = class {
@@ -4659,7 +4664,7 @@ var CipherAesBase = class {
4659
4664
  encryptRtp(header, payload, rolloverCounter) {
4660
4665
  return Buffer.from([]);
4661
4666
  }
4662
- decryptRtp(cipherText, rolloverCounter) {
4667
+ decryptRtp(cipherText, rolloverCounter, header) {
4663
4668
  return [];
4664
4669
  }
4665
4670
  encryptRTCP(rawRtcp, srtcpIndex) {
@@ -4670,6 +4675,74 @@ var CipherAesBase = class {
4670
4675
  }
4671
4676
  };
4672
4677
 
4678
+ // ../rtp/src/srtp/error.ts
4679
+ var SrtpAuthenticationError = class extends Error {
4680
+ constructor(message) {
4681
+ super(message);
4682
+ this.name = "SrtpAuthenticationError";
4683
+ }
4684
+ };
4685
+
4686
+ // ../rtp/src/srtp/packet.ts
4687
+ var minRtpHeaderSize = 12;
4688
+ var minRtcpPacketSize = 8;
4689
+ function parseSrtpRtpHeader(packet, authTagLength, message = "Failed to authenticate SRTP packet") {
4690
+ const authTagOffset = packet.length - authTagLength;
4691
+ assertAuthenticatedPacketLength(
4692
+ packet.length >= minRtpHeaderSize + authTagLength,
4693
+ message
4694
+ );
4695
+ const header = wrapAuthenticationError(
4696
+ () => RtpHeader.deSerialize(packet.subarray(0, authTagOffset)),
4697
+ message
4698
+ );
4699
+ header.paddingSize = 0;
4700
+ assertAuthenticatedPacketLength(
4701
+ header.payloadOffset >= minRtpHeaderSize && header.payloadOffset <= authTagOffset,
4702
+ message
4703
+ );
4704
+ return header;
4705
+ }
4706
+ function parseSrtcpHeader(packet, authTagLength, srtcpIndexSize3, message = "Failed to authenticate SRTCP packet") {
4707
+ assertAuthenticatedPacketLength(
4708
+ packet.length >= minRtcpPacketSize + authTagLength + srtcpIndexSize3,
4709
+ message
4710
+ );
4711
+ return wrapAuthenticationError(
4712
+ () => RtcpHeader.deSerialize(packet.subarray(0, RTCP_HEADER_SIZE)),
4713
+ message
4714
+ );
4715
+ }
4716
+ function assertAuthenticatedPacketLength(condition, message) {
4717
+ if (!condition) {
4718
+ throw new SrtpAuthenticationError(message);
4719
+ }
4720
+ }
4721
+ function wrapAuthenticationError(parse, message) {
4722
+ try {
4723
+ return parse();
4724
+ } catch {
4725
+ throw new SrtpAuthenticationError(message);
4726
+ }
4727
+ }
4728
+ function finalizeSrtpRtpHeader(header, packet, message = "Failed to authenticate SRTP packet") {
4729
+ if (!header.padding) {
4730
+ header.paddingSize = 0;
4731
+ return header;
4732
+ }
4733
+ assertAuthenticatedPacketLength(
4734
+ packet.length > header.payloadOffset,
4735
+ message
4736
+ );
4737
+ const paddingSize = packet[packet.length - 1];
4738
+ assertAuthenticatedPacketLength(
4739
+ paddingSize > 0 && paddingSize <= packet.length - header.payloadOffset,
4740
+ message
4741
+ );
4742
+ header.paddingSize = paddingSize;
4743
+ return header;
4744
+ }
4745
+
4673
4746
  // ../rtp/src/srtp/cipher/ctr.ts
4674
4747
  var CipherAesCtr = class extends CipherAesBase {
4675
4748
  constructor(srtpSessionKey, srtpSessionSalt, srtcpSessionKey, srtcpSessionSalt, srtpSessionAuthTag, srtcpSessionAuthTag) {
@@ -4695,10 +4768,20 @@ var CipherAesCtr = class extends CipherAesBase {
4695
4768
  );
4696
4769
  return Buffer.concat([headerBuffer, enc, authTag]);
4697
4770
  }
4698
- decryptRtp(cipherText, rolloverCounter) {
4699
- const header = RtpHeader.deSerialize(cipherText);
4700
- const size = cipherText.length - this.authTagLength;
4701
- cipherText = cipherText.subarray(0, cipherText.length - this.authTagLength);
4771
+ decryptRtp(cipherText, rolloverCounter, header = parseSrtpRtpHeader(cipherText, this.authTagLength)) {
4772
+ const authTagOffset = cipherText.length - this.authTagLength;
4773
+ const encryptedPacket = cipherText.subarray(0, authTagOffset);
4774
+ const actualAuthTag = cipherText.subarray(authTagOffset);
4775
+ const expectedAuthTag = this.generateSrtpAuthTag(
4776
+ rolloverCounter,
4777
+ encryptedPacket.subarray(0, header.payloadOffset),
4778
+ encryptedPacket.subarray(header.payloadOffset)
4779
+ );
4780
+ assertAuthTag(
4781
+ actualAuthTag,
4782
+ expectedAuthTag,
4783
+ "Failed to authenticate SRTP packet"
4784
+ );
4702
4785
  const counter = this.generateCounter(
4703
4786
  header.sequenceNumber,
4704
4787
  rolloverCounter,
@@ -4710,14 +4793,16 @@ var CipherAesCtr = class extends CipherAesBase {
4710
4793
  this.srtpSessionKey,
4711
4794
  counter
4712
4795
  );
4713
- const payload = cipherText.subarray(header.payloadOffset);
4796
+ const payload = encryptedPacket.subarray(header.payloadOffset);
4714
4797
  const buf = cipher.update(payload);
4715
4798
  const dst = Buffer.concat([
4716
- cipherText.subarray(0, header.payloadOffset),
4717
- buf,
4718
- Buffer.alloc(size - header.payloadOffset - buf.length)
4799
+ encryptedPacket.subarray(0, header.payloadOffset),
4800
+ buf
4719
4801
  ]);
4720
- return [dst, header];
4802
+ return [
4803
+ dst,
4804
+ finalizeSrtpRtpHeader(header, dst, "Failed to authenticate SRTP packet")
4805
+ ];
4721
4806
  }
4722
4807
  encryptRTCP(rtcpPacket, srtcpIndex) {
4723
4808
  let out = Buffer.from(rtcpPacket);
@@ -4739,15 +4824,29 @@ var CipherAesCtr = class extends CipherAesBase {
4739
4824
  return out;
4740
4825
  }
4741
4826
  decryptRTCP(encrypted) {
4742
- const header = RtcpHeader.deSerialize(encrypted);
4827
+ const header = parseSrtcpHeader(
4828
+ encrypted,
4829
+ this.authTagLength,
4830
+ srtcpIndexSize
4831
+ );
4743
4832
  const tailOffset = encrypted.length - (this.authTagLength + srtcpIndexSize);
4833
+ const authenticatedPortion = encrypted.subarray(
4834
+ 0,
4835
+ encrypted.length - this.authTagLength
4836
+ );
4837
+ const actualTag = encrypted.subarray(encrypted.length - this.authTagLength);
4838
+ const expectedTag = this.generateSrtcpAuthTag(authenticatedPortion);
4839
+ assertAuthTag(
4840
+ actualTag,
4841
+ expectedTag,
4842
+ "Failed to authenticate SRTCP packet"
4843
+ );
4744
4844
  const out = Buffer.from(encrypted).slice(0, tailOffset);
4745
- const isEncrypted = encrypted[tailOffset] >> 7;
4845
+ const isEncrypted = encrypted[tailOffset] >>> 7;
4746
4846
  if (isEncrypted === 0) return [out, header];
4747
4847
  let srtcpIndex = encrypted.readUInt32BE(tailOffset);
4748
4848
  srtcpIndex &= ~(1 << 31);
4749
4849
  const ssrc = encrypted.readUInt32BE(4);
4750
- const actualTag = encrypted.subarray(encrypted.length - 10);
4751
4850
  const counter = this.generateCounter(
4752
4851
  srtcpIndex & 65535,
4753
4852
  srtcpIndex >> 16,
@@ -4788,6 +4887,11 @@ var CipherAesCtr = class extends CipherAesBase {
4788
4887
  }
4789
4888
  };
4790
4889
  var srtcpIndexSize = 4;
4890
+ function assertAuthTag(actual, expected, message) {
4891
+ if (actual.length !== expected.length || !timingSafeEqual(actual, expected)) {
4892
+ throw new SrtpAuthenticationError(message);
4893
+ }
4894
+ }
4791
4895
 
4792
4896
  // ../rtp/src/srtp/cipher/gcm.ts
4793
4897
  import { createCipheriv as createCipheriv3, createDecipheriv as createDecipheriv3 } from "crypto";
@@ -4810,20 +4914,25 @@ var CipherAesGcm = class extends CipherAesBase {
4810
4914
  const dst = Buffer.concat([hdr, enc, authTag]);
4811
4915
  return dst;
4812
4916
  }
4813
- decryptRtp(cipherText, rolloverCounter) {
4814
- const header = RtpHeader.deSerialize(cipherText);
4917
+ decryptRtp(cipherText, rolloverCounter, header = parseSrtpRtpHeader(cipherText, this.aeadAuthTagLen)) {
4918
+ const headerBuffer = cipherText.subarray(0, header.payloadOffset);
4919
+ const authTagOffset = cipherText.length - this.aeadAuthTagLen;
4920
+ const authTag = cipherText.subarray(authTagOffset);
4815
4921
  let dst = Buffer.from([]);
4816
4922
  dst = growBufferSize(dst, cipherText.length - this.aeadAuthTagLen);
4817
- cipherText.slice(0, header.payloadOffset).copy(dst);
4923
+ headerBuffer.copy(dst);
4818
4924
  const iv = this.rtpInitializationVector(header, rolloverCounter);
4819
- const enc = cipherText.slice(
4820
- header.payloadOffset,
4821
- cipherText.length - this.aeadAuthTagLen
4822
- );
4823
- const cipher = createDecipheriv3("aes-128-gcm", this.srtpSessionKey, iv);
4824
- const dec = cipher.update(enc);
4925
+ const enc = cipherText.slice(header.payloadOffset, authTagOffset);
4926
+ const decipher = createDecipheriv3("aes-128-gcm", this.srtpSessionKey, iv);
4927
+ decipher.setAAD(headerBuffer);
4928
+ decipher.setAuthTag(authTag);
4929
+ const dec = decipher.update(enc);
4930
+ finalizeAuthenticatedDecryption(decipher, "SRTP");
4825
4931
  dec.copy(dst, header.payloadOffset);
4826
- return [dst, header];
4932
+ return [
4933
+ dst,
4934
+ finalizeSrtpRtpHeader(header, dst, "Failed to authenticate SRTP packet")
4935
+ ];
4827
4936
  }
4828
4937
  encryptRTCP(rtcpPacket, srtcpIndex) {
4829
4938
  const ssrc = rtcpPacket.readUInt32BE(4);
@@ -4844,19 +4953,38 @@ var CipherAesGcm = class extends CipherAesBase {
4844
4953
  return dst;
4845
4954
  }
4846
4955
  decryptRTCP(encrypted) {
4847
- const header = RtcpHeader.deSerialize(encrypted);
4848
- const aadPos = encrypted.length - srtcpIndexSize2;
4849
- const dst = Buffer.alloc(aadPos - this.aeadAuthTagLen);
4850
- encrypted.slice(0, 8).copy(dst);
4956
+ const header = parseSrtcpHeader(
4957
+ encrypted,
4958
+ this.aeadAuthTagLen,
4959
+ srtcpIndexSize2
4960
+ );
4961
+ const srtcpIndexOffset = encrypted.length - srtcpIndexSize2;
4962
+ const authTagOffset = srtcpIndexOffset - this.aeadAuthTagLen;
4851
4963
  const ssrc = encrypted.readUInt32BE(4);
4852
- let srtcpIndex = encrypted.readUInt32BE(encrypted.length - 4);
4853
- srtcpIndex &= ~(rtcpEncryptionFlag << 24);
4964
+ const encodedSrtcpIndex = encrypted.readUInt32BE(srtcpIndexOffset);
4965
+ const isEncrypted = encodedSrtcpIndex >>> 31 === 1;
4966
+ const srtcpIndex = encodedSrtcpIndex & ~(rtcpEncryptionFlag << 24);
4854
4967
  const iv = this.rtcpInitializationVector(ssrc, srtcpIndex);
4855
- const aad = this.rtcpAdditionalAuthenticatedData(encrypted, srtcpIndex);
4856
- const cipher = createDecipheriv3("aes-128-gcm", this.srtcpSessionKey, iv);
4857
- cipher.setAAD(aad);
4858
- const dec = cipher.update(encrypted.slice(8, aadPos));
4859
- dec.copy(dst, 8);
4968
+ const aad = isEncrypted ? Buffer.concat([
4969
+ encrypted.subarray(0, 8),
4970
+ encrypted.subarray(srtcpIndexOffset)
4971
+ ]) : Buffer.concat([
4972
+ encrypted.subarray(0, authTagOffset),
4973
+ encrypted.subarray(srtcpIndexOffset)
4974
+ ]);
4975
+ const cipherText = isEncrypted ? encrypted.slice(8, authTagOffset) : Buffer.alloc(0);
4976
+ const dst = isEncrypted ? Buffer.alloc(authTagOffset) : Buffer.from(encrypted.subarray(0, authTagOffset));
4977
+ if (isEncrypted) {
4978
+ encrypted.slice(0, 8).copy(dst);
4979
+ }
4980
+ const decipher = createDecipheriv3("aes-128-gcm", this.srtcpSessionKey, iv);
4981
+ decipher.setAAD(aad);
4982
+ decipher.setAuthTag(encrypted.subarray(authTagOffset, srtcpIndexOffset));
4983
+ const dec = decipher.update(cipherText);
4984
+ finalizeAuthenticatedDecryption(decipher, "SRTCP");
4985
+ if (isEncrypted) {
4986
+ dec.copy(dst, 8);
4987
+ }
4860
4988
  return [dst, header];
4861
4989
  }
4862
4990
  // https://tools.ietf.org/html/rfc7714#section-8.1
@@ -4892,6 +5020,15 @@ var CipherAesGcm = class extends CipherAesBase {
4892
5020
  };
4893
5021
  var srtcpIndexSize2 = 4;
4894
5022
  var rtcpEncryptionFlag = 128;
5023
+ function finalizeAuthenticatedDecryption(decipher, packetType) {
5024
+ try {
5025
+ decipher.final();
5026
+ } catch {
5027
+ throw new SrtpAuthenticationError(
5028
+ `Failed to authenticate ${packetType} packet`
5029
+ );
5030
+ }
5031
+ }
4895
5032
 
4896
5033
  // ../rtp/src/srtp/context/context.ts
4897
5034
  var Context = class {
@@ -5149,12 +5286,29 @@ var SrtpContext2 = class extends Context {
5149
5286
  return enc;
5150
5287
  }
5151
5288
  decryptRtp(cipherText) {
5152
- const header = RtpHeader.deSerialize(cipherText);
5153
- const s = this.getSrtpSsrcState(header.ssrc);
5154
- this.updateRolloverCount(header.sequenceNumber, s);
5155
- const dec = this.cipher.decryptRtp(cipherText, s.rolloverCounter);
5289
+ const header = parseSrtpRtpHeader(cipherText, this.rtpAuthTagLength);
5290
+ const existingState = this.srtpSSRCStates[header.ssrc];
5291
+ const nextState = existingState ? { ...existingState } : {
5292
+ ssrc: header.ssrc,
5293
+ rolloverCounter: 0,
5294
+ lastSequenceNumber: 0
5295
+ };
5296
+ this.updateRolloverCount(header.sequenceNumber, nextState);
5297
+ const dec = this.cipher.decryptRtp(
5298
+ cipherText,
5299
+ nextState.rolloverCounter,
5300
+ header
5301
+ );
5302
+ if (existingState) {
5303
+ Object.assign(existingState, nextState);
5304
+ } else {
5305
+ this.srtpSSRCStates[header.ssrc] = nextState;
5306
+ }
5156
5307
  return dec;
5157
5308
  }
5309
+ get rtpAuthTagLength() {
5310
+ return this.profile === ProtectionProfileAeadAes128Gcm ? 16 : 10;
5311
+ }
5158
5312
  };
5159
5313
 
5160
5314
  // ../rtp/src/srtp/srtp.ts
@@ -5896,6 +6050,9 @@ var DtlsSocket = class {
5896
6050
  this.sessionType === SessionType.CLIENT
5897
6051
  );
5898
6052
  }
6053
+ get remoteCertificate() {
6054
+ return this.cipher.remoteCertificate;
6055
+ }
5899
6056
  };
5900
6057
 
5901
6058
  // ../dtls/src/client.ts
@@ -6284,6 +6441,16 @@ handlers2[16 /* client_key_exchange_16 */] = ({ cipher, dtls }) => (message) =>
6284
6441
  );
6285
6442
  log16(dtls.sessionId, "setup cipher", cipher.cipher.summary);
6286
6443
  };
6444
+ handlers2[11 /* certificate_11 */] = ({ cipher, dtls }) => (message) => {
6445
+ log16(dtls.sessionId, "handshake certificate", message);
6446
+ cipher.remoteCertificate = message.certificateList[0];
6447
+ };
6448
+ handlers2[15 /* certificate_verify_15 */] = ({ cipher, dtls }) => (message) => {
6449
+ if (!cipher.remoteCertificate) {
6450
+ throw new Error("client certificate missing before certificate verify");
6451
+ }
6452
+ log16(dtls.sessionId, "certificate_verify", message.algorithm);
6453
+ };
6287
6454
  handlers2[20 /* finished_20 */] = ({ dtls }) => (message) => {
6288
6455
  log16(dtls.sessionId, "finished", message);
6289
6456
  };
@@ -6350,7 +6517,14 @@ var DtlsServer = class extends DtlsSocket {
6350
6517
  {
6351
6518
  await this.waitForReady(() => !!this.flight6);
6352
6519
  this.flight6?.handleHandshake(handshake);
6353
- await this.waitForReady(() => this.dtls.checkHandshakesExist([16]));
6520
+ const requiredHandshakes = [
6521
+ 16,
6522
+ this.options.certificateRequest && 11,
6523
+ this.options.certificateRequest && 15
6524
+ ].filter((type) => typeof type === "number");
6525
+ await this.waitForReady(
6526
+ () => this.dtls.checkHandshakesExist(requiredHandshakes)
6527
+ );
6354
6528
  await this.flight6?.exec();
6355
6529
  this.connected = true;
6356
6530
  this.onConnect.execute();
@@ -8600,6 +8774,9 @@ var EventTarget = class extends EventEmitter {
8600
8774
 
8601
8775
  // src/dataChannel.ts
8602
8776
  var log23 = debug("werift:packages/webrtc/src/dataChannel.ts");
8777
+ function getDataChannelMessageSize(data) {
8778
+ return Buffer.isBuffer(data) ? data.length : Buffer.byteLength(data);
8779
+ }
8603
8780
  var RTCDataChannel = class extends EventTarget {
8604
8781
  constructor(sctp, parameters, sendOpen = true) {
8605
8782
  super();
@@ -8705,10 +8882,9 @@ var RTCDataChannel = class extends EventTarget {
8705
8882
  }
8706
8883
  }
8707
8884
  send(data) {
8708
- const size = Buffer.isBuffer(data) ? data.length : Buffer.byteLength(data);
8885
+ const size = this.sctp.datachannelSend(this, data);
8709
8886
  this.messagesSent++;
8710
8887
  this.bytesSent += size;
8711
- this.sctp.datachannelSend(this, data);
8712
8888
  }
8713
8889
  close() {
8714
8890
  this.sctp.dataChannelClose(this);
@@ -9163,6 +9339,24 @@ function fingerprint(file, hashName) {
9163
9339
  const hash2 = createHash4(hashName).update(file).digest("hex");
9164
9340
  return colon(upper(hash2));
9165
9341
  }
9342
+ var fingerprintHashAlgorithms = {
9343
+ sha1: "sha1",
9344
+ "sha-1": "sha1",
9345
+ sha224: "sha224",
9346
+ "sha-224": "sha224",
9347
+ sha256: "sha256",
9348
+ "sha-256": "sha256",
9349
+ sha384: "sha384",
9350
+ "sha-384": "sha384",
9351
+ sha512: "sha512",
9352
+ "sha-512": "sha512"
9353
+ };
9354
+ function normalizeFingerprintAlgorithm(algorithm) {
9355
+ return fingerprintHashAlgorithms[algorithm.trim().toLowerCase()];
9356
+ }
9357
+ function normalizeFingerprintValue(value) {
9358
+ return value.replace(/[^0-9a-f]/gi, "").toLowerCase();
9359
+ }
9166
9360
  function isDtls(buf) {
9167
9361
  const firstByte = buf[0];
9168
9362
  return firstByte > 19 && firstByte < 64;
@@ -9619,13 +9813,18 @@ var RTCDtlsTransport = class _RTCDtlsTransport {
9619
9813
  return this.localCertificatePromise;
9620
9814
  }
9621
9815
  setRemoteParams(remoteParameters) {
9622
- this.remoteParameters = remoteParameters;
9816
+ const fingerprints = deduplicateFingerprints([
9817
+ ...this.remoteParameters?.fingerprints ?? [],
9818
+ ...remoteParameters.fingerprints
9819
+ ]);
9820
+ const role = remoteParameters.role === "auto" && this.remoteParameters?.role ? this.remoteParameters.role : remoteParameters.role;
9821
+ this.remoteParameters = new RTCDtlsParameters(fingerprints, role);
9623
9822
  }
9624
9823
  async start() {
9625
9824
  if (this.state !== "new") {
9626
9825
  throw new Error("state must be new");
9627
9826
  }
9628
- if (this.remoteParameters?.fingerprints.length === 0) {
9827
+ if (!this.remoteParameters || this.remoteParameters.fingerprints.length === 0) {
9629
9828
  throw new Error("remote fingerprint not exist");
9630
9829
  }
9631
9830
  if (this.role === "auto") {
@@ -9644,8 +9843,8 @@ var RTCDtlsTransport = class _RTCDtlsTransport {
9644
9843
  signatureHash: this.localCertificate?.signatureHash,
9645
9844
  transport: createIceTransport(this.iceTransport.connection),
9646
9845
  srtpProfiles: this.srtpProfiles,
9647
- extendedMasterSecret: true
9648
- // certificateRequest: true,
9846
+ extendedMasterSecret: true,
9847
+ certificateRequest: true
9649
9848
  });
9650
9849
  } else {
9651
9850
  this.dtls = new DtlsClient({
@@ -9664,7 +9863,9 @@ var RTCDtlsTransport = class _RTCDtlsTransport {
9664
9863
  this.dataReceiver(buf);
9665
9864
  });
9666
9865
  this.dtls.onClose.subscribe(() => {
9667
- this.setState("closed");
9866
+ if (this.state !== "failed") {
9867
+ this.setState("closed");
9868
+ }
9668
9869
  });
9669
9870
  this.dtls.onConnect.once(r);
9670
9871
  this.dtls.onError.once((error) => {
@@ -9681,16 +9882,70 @@ var RTCDtlsTransport = class _RTCDtlsTransport {
9681
9882
  });
9682
9883
  }
9683
9884
  });
9885
+ try {
9886
+ this.verifyRemoteCertificateFingerprint();
9887
+ } catch (error) {
9888
+ this.setState("failed");
9889
+ this.dtls?.close();
9890
+ throw error;
9891
+ }
9684
9892
  if (this.srtpProfiles.length > 0) {
9685
9893
  this.startSrtp();
9686
9894
  }
9687
- this.dtls.onConnect.subscribe(() => {
9688
- this.updateSrtpSession();
9689
- this.setState("connected");
9690
- });
9691
9895
  this.setState("connected");
9692
9896
  log27("dtls connected");
9693
9897
  }
9898
+ verifyRemoteCertificateFingerprint() {
9899
+ if (!this.remoteParameters || this.remoteParameters.fingerprints.length === 0) {
9900
+ throw new Error("remote fingerprint not exist");
9901
+ }
9902
+ const remoteCertificate = this.dtls?.remoteCertificate;
9903
+ if (!remoteCertificate) {
9904
+ throw new Error("remote certificate not available");
9905
+ }
9906
+ const supportedFingerprints = this.remoteParameters.fingerprints.flatMap(
9907
+ ({ algorithm, value }) => {
9908
+ const normalizedAlgorithm = normalizeFingerprintAlgorithm(algorithm);
9909
+ if (!normalizedAlgorithm) {
9910
+ return [];
9911
+ }
9912
+ const normalizedValue = normalizeFingerprintValue(value);
9913
+ if (!normalizedValue) {
9914
+ throw new Error("remote fingerprint value is empty");
9915
+ }
9916
+ return [{ normalizedAlgorithm, normalizedValue }];
9917
+ }
9918
+ );
9919
+ if (supportedFingerprints.length === 0) {
9920
+ throw new Error("no supported remote fingerprint algorithms");
9921
+ }
9922
+ const preferredAlgorithm = selectPreferredFingerprintAlgorithm(
9923
+ supportedFingerprints
9924
+ );
9925
+ const expectedFingerprints = supportedFingerprints.filter(
9926
+ ({ normalizedAlgorithm }) => normalizedAlgorithm === preferredAlgorithm
9927
+ );
9928
+ const actualFingerprints = expectedFingerprints.reduce(
9929
+ (acc, { normalizedAlgorithm }) => {
9930
+ if (!acc.has(normalizedAlgorithm)) {
9931
+ acc.set(
9932
+ normalizedAlgorithm,
9933
+ normalizeFingerprintValue(
9934
+ fingerprint(remoteCertificate, normalizedAlgorithm)
9935
+ )
9936
+ );
9937
+ }
9938
+ return acc;
9939
+ },
9940
+ /* @__PURE__ */ new Map()
9941
+ );
9942
+ const matched = expectedFingerprints.some(
9943
+ ({ normalizedAlgorithm, normalizedValue }) => actualFingerprints.get(normalizedAlgorithm) === normalizedValue
9944
+ );
9945
+ if (!matched) {
9946
+ throw new Error("remote certificate fingerprint mismatch");
9947
+ }
9948
+ }
9694
9949
  updateSrtpSession() {
9695
9950
  if (!this.dtls) throw new Error();
9696
9951
  const profile = this.dtls.srtp.srtpProfile;
@@ -9723,8 +9978,23 @@ var RTCDtlsTransport = class _RTCDtlsTransport {
9723
9978
  this.bytesReceived += data.length;
9724
9979
  this.packetsReceived++;
9725
9980
  if (isRtcp(data)) {
9726
- const dec = this.srtcp.decrypt(data);
9727
- const rtcpPackets = RtcpPacketConverter.deSerialize(dec);
9981
+ let dec;
9982
+ try {
9983
+ dec = this.srtcp.decrypt(data);
9984
+ } catch (error) {
9985
+ if (error instanceof SrtpAuthenticationError) {
9986
+ log27("dropping invalid SRTCP packet", error);
9987
+ return;
9988
+ }
9989
+ throw error;
9990
+ }
9991
+ let rtcpPackets;
9992
+ try {
9993
+ rtcpPackets = RtcpPacketConverter.deSerialize(dec);
9994
+ } catch (error) {
9995
+ log27("dropping malformed SRTCP packet", error);
9996
+ return;
9997
+ }
9728
9998
  for (const rtcp of rtcpPackets) {
9729
9999
  try {
9730
10000
  this.onRtcp.execute(rtcp);
@@ -9733,8 +10003,23 @@ var RTCDtlsTransport = class _RTCDtlsTransport {
9733
10003
  }
9734
10004
  }
9735
10005
  } else {
9736
- const dec = this.srtp.decrypt(data);
9737
- const rtp = RtpPacket.deSerialize(dec);
10006
+ let dec;
10007
+ try {
10008
+ dec = this.srtp.decrypt(data);
10009
+ } catch (error) {
10010
+ if (error instanceof SrtpAuthenticationError) {
10011
+ log27("dropping invalid SRTP packet", error);
10012
+ return;
10013
+ }
10014
+ throw error;
10015
+ }
10016
+ let rtp;
10017
+ try {
10018
+ rtp = RtpPacket.deSerialize(dec);
10019
+ } catch (error) {
10020
+ log27("dropping malformed SRTP packet", error);
10021
+ return;
10022
+ }
9738
10023
  try {
9739
10024
  this.onRtp.execute(rtp);
9740
10025
  } catch (error) {
@@ -9887,6 +10172,31 @@ var RTCDtlsParameters = class {
9887
10172
  this.role = role;
9888
10173
  }
9889
10174
  };
10175
+ var deduplicateFingerprints = (fingerprints) => {
10176
+ const seen = /* @__PURE__ */ new Set();
10177
+ return fingerprints.filter(({ algorithm, value }) => {
10178
+ const key = `${normalizeFingerprintAlgorithm(algorithm) ?? algorithm.trim().toLowerCase()}:${normalizeFingerprintValue(value)}`;
10179
+ if (seen.has(key)) {
10180
+ return false;
10181
+ }
10182
+ seen.add(key);
10183
+ return true;
10184
+ });
10185
+ };
10186
+ var preferredFingerprintAlgorithms = [
10187
+ "sha512",
10188
+ "sha384",
10189
+ "sha256",
10190
+ "sha224",
10191
+ "sha1"
10192
+ ];
10193
+ var selectPreferredFingerprintAlgorithm = (fingerprints) => {
10194
+ return preferredFingerprintAlgorithms.find(
10195
+ (algorithm) => fingerprints.some(
10196
+ ({ normalizedAlgorithm }) => normalizedAlgorithm === algorithm
10197
+ )
10198
+ ) ?? fingerprints[0].normalizedAlgorithm;
10199
+ };
9890
10200
  var IceTransport = class {
9891
10201
  constructor(ice) {
9892
10202
  this.ice = ice;
@@ -12064,9 +12374,11 @@ function tsnPlusOne(a) {
12064
12374
 
12065
12375
  // src/transport/sctp.ts
12066
12376
  var log30 = debug("werift:packages/webrtc/src/transport/sctp.ts");
12067
- var RTCSctpTransport = class {
12068
- constructor(port = 5e3) {
12377
+ var DEFAULT_MAX_MESSAGE_SIZE = 65536;
12378
+ var RTCSctpTransport = class _RTCSctpTransport {
12379
+ constructor(port = 5e3, maxMessageSize = DEFAULT_MAX_MESSAGE_SIZE) {
12069
12380
  this.port = port;
12381
+ this.maxMessageSize = maxMessageSize;
12070
12382
  }
12071
12383
  dtlsTransport;
12072
12384
  sctp;
@@ -12076,6 +12388,7 @@ var RTCSctpTransport = class {
12076
12388
  mLineIndex;
12077
12389
  bundled = false;
12078
12390
  dataChannels = {};
12391
+ remoteMaxMessageSize = DEFAULT_MAX_MESSAGE_SIZE;
12079
12392
  dataChannelQueue = [];
12080
12393
  dataChannelId;
12081
12394
  eventDisposer = [];
@@ -12304,18 +12617,35 @@ var RTCSctpTransport = class {
12304
12617
  }
12305
12618
  this.dataChannelQueue = [];
12306
12619
  }
12620
+ assertSendableMessageSize(size) {
12621
+ if (this.remoteMaxMessageSize !== 0 && size > this.remoteMaxMessageSize) {
12622
+ throw new Error(
12623
+ `max-message-size exceeded: ${size} > ${this.remoteMaxMessageSize}`
12624
+ );
12625
+ }
12626
+ }
12307
12627
  datachannelSend = (channel, data) => {
12308
- channel.addBufferedAmount(data.length);
12628
+ const userData = Buffer.isBuffer(data) ? data : Buffer.from(data);
12629
+ const size = getDataChannelMessageSize(data);
12630
+ this.assertSendableMessageSize(size);
12631
+ channel.addBufferedAmount(size);
12309
12632
  this.dataChannelQueue.push(
12310
- typeof data === "string" ? [channel, WEBRTC_STRING, Buffer.from(data)] : [channel, WEBRTC_BINARY, data]
12633
+ typeof data === "string" ? [channel, WEBRTC_STRING, userData] : [channel, WEBRTC_BINARY, userData]
12311
12634
  );
12312
12635
  if (this.sctp.associationState !== 4 /* ESTABLISHED */) {
12313
12636
  log30("sctp not established", this.sctp.associationState);
12314
12637
  }
12315
12638
  this.dataChannelFlush();
12639
+ return size;
12316
12640
  };
12317
- static getCapabilities() {
12318
- return new RTCSctpCapabilities2(65536);
12641
+ getCapabilities() {
12642
+ return _RTCSctpTransport.getCapabilities(this.maxMessageSize);
12643
+ }
12644
+ static getCapabilities(maxMessageSize = DEFAULT_MAX_MESSAGE_SIZE) {
12645
+ return new RTCSctpCapabilities2(maxMessageSize);
12646
+ }
12647
+ setRemoteMaxMessageSize(maxMessageSize) {
12648
+ this.remoteMaxMessageSize = maxMessageSize ?? DEFAULT_MAX_MESSAGE_SIZE;
12319
12649
  }
12320
12650
  setRemotePort(port) {
12321
12651
  this.sctp.setRemotePort(port);
@@ -14006,8 +14336,8 @@ var SctpTransportManager = class {
14006
14336
  onDataChannel = new Event();
14007
14337
  constructor() {
14008
14338
  }
14009
- createSctpTransport() {
14010
- const sctp = new RTCSctpTransport();
14339
+ createSctpTransport(maxMessageSize) {
14340
+ const sctp = new RTCSctpTransport(5e3, maxMessageSize);
14011
14341
  sctp.mid = void 0;
14012
14342
  sctp.onDataChannel.subscribe((channel) => {
14013
14343
  this.dataChannelsOpened++;
@@ -14065,6 +14395,9 @@ var SctpTransportManager = class {
14065
14395
  if (!this.sctpTransport) {
14066
14396
  return;
14067
14397
  }
14398
+ this.sctpTransport.setRemoteMaxMessageSize(
14399
+ remoteMedia.sctpCapabilities?.maxMessageSize
14400
+ );
14068
14401
  this.sctpRemotePort = remoteMedia.sctpPort;
14069
14402
  if (!this.sctpRemotePort) {
14070
14403
  throw new Error("sctpRemotePort not exist");
@@ -14205,7 +14538,7 @@ var SDPManager = class {
14205
14538
  );
14206
14539
  media.sctpPort = sctp.port;
14207
14540
  media.rtp.muxId = sctp.mid;
14208
- media.sctpCapabilities = RTCSctpTransport.getCapabilities();
14541
+ media.sctpCapabilities = sctp.getCapabilities();
14209
14542
  this.addTransportDescription(media, sctp.dtlsTransport);
14210
14543
  return media;
14211
14544
  }
@@ -14343,7 +14676,7 @@ var SDPManager = class {
14343
14676
  description.media.push(this.createMediaDescriptionForSctp(sctpTransport));
14344
14677
  }
14345
14678
  if (this.bundlePolicy !== "disable") {
14346
- const mids = description.media.map((m) => m.direction !== "inactive" ? m.rtp.muxId : void 0).filter((v) => v);
14679
+ const mids = description.media.map((m) => m.rtp.muxId).filter((v) => v);
14347
14680
  if (mids.length) {
14348
14681
  const bundle = new GroupDescription("BUNDLE", mids);
14349
14682
  description.group.push(bundle);
@@ -14413,7 +14746,7 @@ var SDPManager = class {
14413
14746
  if (this.bundlePolicy !== "disable") {
14414
14747
  const bundle = new GroupDescription("BUNDLE", []);
14415
14748
  for (const media of description.media) {
14416
- if (media.direction !== "inactive") {
14749
+ if (media.rtp.muxId) {
14417
14750
  bundle.items.push(media.rtp.muxId);
14418
14751
  }
14419
14752
  }
@@ -14538,7 +14871,9 @@ var SecureTransportManager = class {
14538
14871
  );
14539
14872
  }
14540
14873
  createTransport() {
14541
- const [existing] = this.iceTransports;
14874
+ const existing = this.iceTransports.find(
14875
+ (transport) => transport.state !== "closed"
14876
+ );
14542
14877
  const iceGatherer = new RTCIceGatherer({
14543
14878
  ...parseIceServers(this.config.iceServers),
14544
14879
  forceTurn: this.config.iceTransportPolicy === "relay",
@@ -14793,6 +15128,7 @@ var RTCPeerConnection = class extends EventTarget {
14793
15128
  secureManager;
14794
15129
  isClosed = false;
14795
15130
  shouldNegotiationneeded = false;
15131
+ remoteBundleNegotiated = false;
14796
15132
  iceGatheringStateChange = new Event();
14797
15133
  iceConnectionStateChange = new Event();
14798
15134
  signalingStateChange = new Event();
@@ -14942,6 +15278,12 @@ var RTCPeerConnection = class extends EventTarget {
14942
15278
  if (min === max) throw new Error("should not be same value");
14943
15279
  if (min >= max) throw new Error("The min must be less than max");
14944
15280
  }
15281
+ if (!Number.isInteger(this.config.maxMessageSize) || this.config.maxMessageSize < 0) {
15282
+ throw new Error("maxMessageSize must be a non-negative integer");
15283
+ }
15284
+ if (this.sctpManager?.sctpTransport) {
15285
+ this.sctpManager.sctpTransport.maxMessageSize = this.config.maxMessageSize;
15286
+ }
14945
15287
  for (const [i, codecParams] of enumerate2([
14946
15288
  ...this.config.codecs.audio || [],
14947
15289
  ...this.config.codecs.video || []
@@ -14998,7 +15340,9 @@ var RTCPeerConnection = class extends EventTarget {
14998
15340
  return description.toJSON();
14999
15341
  }
15000
15342
  createSctpTransport() {
15001
- const sctp = this.sctpManager.createSctpTransport();
15343
+ const sctp = this.sctpManager.createSctpTransport(
15344
+ this.config.maxMessageSize
15345
+ );
15002
15346
  const dtlsTransport = this.findOrCreateTransport();
15003
15347
  sctp.setDtlsTransport(dtlsTransport);
15004
15348
  return sctp;
@@ -15035,10 +15379,13 @@ var RTCPeerConnection = class extends EventTarget {
15035
15379
  });
15036
15380
  };
15037
15381
  findOrCreateTransport() {
15038
- const [existing] = this.iceTransports;
15039
- if (this.sdpManager.bundlePolicy === "max-bundle" || this.sdpManager.bundlePolicy !== "disable" && this.remoteIsBundled) {
15040
- if (existing) {
15041
- return this.dtlsTransports[0];
15382
+ const existingDtlsTransport = this.dtlsTransports.find(
15383
+ (transport) => transport.state !== "closed"
15384
+ );
15385
+ const existing = existingDtlsTransport?.iceTransport;
15386
+ if (this.sdpManager.bundlePolicy === "max-bundle" || this.sdpManager.bundlePolicy !== "disable" && (this.remoteIsBundled || this.remoteBundleNegotiated)) {
15387
+ if (existingDtlsTransport) {
15388
+ return existingDtlsTransport;
15042
15389
  }
15043
15390
  }
15044
15391
  const dtlsTransport = this.secureManager.createTransport();
@@ -15210,6 +15557,11 @@ var RTCPeerConnection = class extends EventTarget {
15210
15557
  sessionDescription,
15211
15558
  this.signalingState
15212
15559
  );
15560
+ if (remoteSdp.group.some(
15561
+ (group) => group.semantic === "BUNDLE" && group.items.length > 0
15562
+ )) {
15563
+ this.remoteBundleNegotiated = true;
15564
+ }
15213
15565
  let bundleTransport;
15214
15566
  const matchTransceiverWithMedia = (transceiver, media) => transceiver.kind === media.kind && [void 0, media.rtp.muxId].includes(transceiver.mid);
15215
15567
  let transports = remoteSdp.media.map((remoteMedia, i) => {
@@ -15427,7 +15779,8 @@ function generateDefaultPeerConfig() {
15427
15779
  bundlePolicy: "max-compat",
15428
15780
  debug: {},
15429
15781
  midSuffix: false,
15430
- forceTurnTCP: false
15782
+ forceTurnTCP: false,
15783
+ maxMessageSize: DEFAULT_MAX_MESSAGE_SIZE
15431
15784
  };
15432
15785
  }
15433
15786
  var defaultPeerConfig = generateDefaultPeerConfig();
@@ -15480,14 +15833,15 @@ var TransceiverManager = class {
15480
15833
  newTransceiver.options = options;
15481
15834
  this.router.registerRtpSender(newTransceiver.sender);
15482
15835
  const inactiveTransceiverIndex = this.transceivers.findIndex(
15483
- (t) => t.currentDirection === "inactive"
15836
+ (t) => t.currentDirection === "inactive" && !t.usedForSender
15484
15837
  );
15485
15838
  const inactiveTransceiver = this.transceivers.find(
15486
- (t) => t.currentDirection === "inactive"
15839
+ (t) => t.currentDirection === "inactive" && !t.usedForSender
15487
15840
  );
15488
15841
  if (inactiveTransceiverIndex > -1 && inactiveTransceiver) {
15489
15842
  this.replaceTransceiver(newTransceiver, inactiveTransceiverIndex);
15490
15843
  newTransceiver.mLineIndex = inactiveTransceiver.mLineIndex;
15844
+ newTransceiver.mid = inactiveTransceiver.mid;
15491
15845
  inactiveTransceiver.setCurrentDirection(void 0);
15492
15846
  } else {
15493
15847
  this.pushTransceiver(newTransceiver);
@@ -15774,6 +16128,7 @@ export {
15774
16128
  Connection,
15775
16129
  ConnectionStates,
15776
16130
  CurveType,
16131
+ DEFAULT_MAX_MESSAGE_SIZE,
15777
16132
  DePacketizerBase,
15778
16133
  Directions,
15779
16134
  DtlsClient,
@@ -15877,6 +16232,7 @@ export {
15877
16232
  SourceDescriptionChunk,
15878
16233
  SourceDescriptionItem,
15879
16234
  SrtcpSession,
16235
+ SrtpAuthenticationError,
15880
16236
  SrtpContext,
15881
16237
  SrtpSession,
15882
16238
  SsrcDescription,
@@ -15935,6 +16291,7 @@ export {
15935
16291
  fingerprint,
15936
16292
  generateStatsId,
15937
16293
  getBit,
16294
+ getDataChannelMessageSize,
15938
16295
  getGlobalIp,
15939
16296
  getHostAddresses,
15940
16297
  getStatsTimestamp,
@@ -15953,6 +16310,8 @@ export {
15953
16310
  milliTime,
15954
16311
  nodeIpAddress,
15955
16312
  normalizeFamilyNodeV18,
16313
+ normalizeFingerprintAlgorithm,
16314
+ normalizeFingerprintValue,
15956
16315
  ntpTime,
15957
16316
  ntpTime2Sec,
15958
16317
  paddingBits,