mediasoup 3.20.5 → 3.20.7

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 (113) hide show
  1. package/node/lib/Worker.d.ts +1 -1
  2. package/node/lib/Worker.d.ts.map +1 -1
  3. package/node/lib/Worker.js +1 -11
  4. package/node/lib/WorkerTypes.d.ts +0 -9
  5. package/node/lib/WorkerTypes.d.ts.map +1 -1
  6. package/node/lib/fbs/plain-transport/connect-response.d.ts.map +1 -1
  7. package/node/lib/fbs/plain-transport/connect-response.js +0 -1
  8. package/node/lib/fbs/worker/dump-response.d.ts +1 -5
  9. package/node/lib/fbs/worker/dump-response.d.ts.map +1 -1
  10. package/node/lib/fbs/worker/dump-response.js +3 -16
  11. package/node/lib/index.d.ts +1 -1
  12. package/node/lib/index.d.ts.map +1 -1
  13. package/node/lib/index.js +1 -2
  14. package/node/lib/test/test-PlainTransport.js +30 -0
  15. package/node/lib/test/test-Worker.js +0 -1
  16. package/npm-scripts.mjs +13 -8
  17. package/package.json +8 -8
  18. package/worker/fbs/meson.build +0 -1
  19. package/worker/fbs/plainTransport.fbs +1 -1
  20. package/worker/fbs/worker.fbs +0 -2
  21. package/worker/include/RTC/Consumer.hpp +80 -45
  22. package/worker/include/RTC/ICE/StunPacket.hpp +8 -9
  23. package/worker/include/RTC/PipeProducerStreamManager.hpp +78 -0
  24. package/worker/include/RTC/ProducerStreamManager.hpp +181 -0
  25. package/worker/include/RTC/RTP/Packet.hpp +1 -1
  26. package/worker/include/RTC/RTP/ProbationGenerator.hpp +1 -1
  27. package/worker/include/RTC/SCTP/association/Association.hpp +24 -0
  28. package/worker/include/RTC/SCTP/association/StateCookie.hpp +104 -11
  29. package/worker/include/RTC/SCTP/packet/Chunk.hpp +1 -1
  30. package/worker/include/RTC/SCTP/packet/ErrorCause.hpp +1 -1
  31. package/worker/include/RTC/SCTP/packet/Packet.hpp +1 -2
  32. package/worker/include/RTC/SCTP/packet/Parameter.hpp +1 -1
  33. package/worker/include/RTC/SCTP/packet/TLV.hpp +1 -1
  34. package/worker/include/RTC/SCTP/packet/chunks/DataChunk.hpp +1 -1
  35. package/worker/include/RTC/SCTP/packet/chunks/ForwardTsnChunk.hpp +1 -1
  36. package/worker/include/RTC/SCTP/packet/chunks/IDataChunk.hpp +1 -1
  37. package/worker/include/RTC/SCTP/packet/chunks/IForwardTsnChunk.hpp +1 -1
  38. package/worker/include/RTC/SCTP/packet/chunks/InitAckChunk.hpp +1 -1
  39. package/worker/include/RTC/SCTP/packet/chunks/InitChunk.hpp +1 -1
  40. package/worker/include/RTC/SCTP/packet/chunks/SackChunk.hpp +1 -1
  41. package/worker/include/RTC/SCTP/packet/chunks/ShutdownChunk.hpp +1 -1
  42. package/worker/include/RTC/SCTP/packet/errorCauses/InvalidStreamIdentifierErrorCause.hpp +1 -1
  43. package/worker/include/RTC/SCTP/packet/errorCauses/MissingMandatoryParameterErrorCause.hpp +1 -1
  44. package/worker/include/RTC/SCTP/packet/errorCauses/NoUserDataErrorCause.hpp +1 -1
  45. package/worker/include/RTC/SCTP/packet/errorCauses/StaleCookieErrorCause.hpp +1 -1
  46. package/worker/include/RTC/SCTP/packet/parameters/AddIncomingStreamsRequestParameter.hpp +1 -1
  47. package/worker/include/RTC/SCTP/packet/parameters/AddOutgoingStreamsRequestParameter.hpp +1 -1
  48. package/worker/include/RTC/SCTP/packet/parameters/CookiePreservativeParameter.hpp +1 -1
  49. package/worker/include/RTC/SCTP/packet/parameters/IPv4AddressParameter.hpp +1 -1
  50. package/worker/include/RTC/SCTP/packet/parameters/IPv6AddressParameter.hpp +1 -1
  51. package/worker/include/RTC/SCTP/packet/parameters/IncomingSsnResetRequestParameter.hpp +1 -1
  52. package/worker/include/RTC/SCTP/packet/parameters/OutgoingSsnResetRequestParameter.hpp +1 -1
  53. package/worker/include/RTC/SCTP/packet/parameters/ReconfigurationResponseParameter.hpp +2 -2
  54. package/worker/include/RTC/SCTP/packet/parameters/SsnTsnResetRequestParameter.hpp +1 -1
  55. package/worker/include/RTC/SCTP/packet/parameters/StateCookieParameter.hpp +4 -1
  56. package/worker/include/RTC/SCTP/packet/parameters/ZeroChecksumAcceptableParameter.hpp +1 -1
  57. package/worker/include/RTC/SCTP/public/SctpOptions.hpp +13 -0
  58. package/worker/include/RTC/SimpleProducerStreamManager.hpp +72 -0
  59. package/worker/include/RTC/SimulcastProducerStreamManager.hpp +93 -0
  60. package/worker/include/RTC/SvcProducerStreamManager.hpp +72 -0
  61. package/worker/include/RTC/Transport.hpp +7 -1
  62. package/worker/include/Settings.hpp +0 -1
  63. package/worker/include/handles/TcpConnectionHandle.hpp +0 -4
  64. package/worker/include/handles/UdpSocketHandle.hpp +0 -4
  65. package/worker/meson.build +9 -39
  66. package/worker/meson_options.txt +0 -1
  67. package/worker/src/RTC/Consumer.cpp +1404 -30
  68. package/worker/src/RTC/DirectTransport.cpp +4 -1
  69. package/worker/src/RTC/PipeProducerStreamManager.cpp +266 -0
  70. package/worker/src/RTC/PipeTransport.cpp +9 -6
  71. package/worker/src/RTC/PlainTransport.cpp +9 -6
  72. package/worker/src/RTC/RTP/Packet.cpp +0 -2
  73. package/worker/src/RTC/RTP/RtpStreamSend.cpp +0 -20
  74. package/worker/src/RTC/Router.cpp +0 -37
  75. package/worker/src/RTC/SCTP/association/Association.cpp +150 -8
  76. package/worker/src/RTC/SCTP/association/StateCookie.cpp +96 -31
  77. package/worker/src/RTC/SCTP/packet/Packet.cpp +1 -1
  78. package/worker/src/RTC/SCTP/packet/parameters/StateCookieParameter.cpp +12 -3
  79. package/worker/src/RTC/SCTP/public/SctpOptions.cpp +4 -0
  80. package/worker/src/RTC/SimpleProducerStreamManager.cpp +343 -0
  81. package/worker/src/RTC/SimulcastProducerStreamManager.cpp +1068 -0
  82. package/worker/src/RTC/SrtpSession.cpp +0 -24
  83. package/worker/src/RTC/SvcProducerStreamManager.cpp +664 -0
  84. package/worker/src/RTC/Transport.cpp +10 -66
  85. package/worker/src/RTC/WebRtcTransport.cpp +13 -7
  86. package/worker/src/Settings.cpp +0 -14
  87. package/worker/src/Utils/Crypto.cpp +3 -3
  88. package/worker/src/Utils/String.cpp +1 -1
  89. package/worker/src/Worker.cpp +5 -38
  90. package/worker/src/handles/TcpConnectionHandle.cpp +1 -42
  91. package/worker/src/handles/UdpSocketHandle.cpp +1 -42
  92. package/worker/src/lib.cpp +1 -10
  93. package/worker/test/include/RTC/SCTP/sctpCommon.hpp +1 -1
  94. package/worker/test/src/RTC/SCTP/association/TestAssociation.cpp +115 -0
  95. package/worker/test/src/RTC/SCTP/association/TestStateCookie.cpp +123 -0
  96. package/worker/test/src/RTC/SCTP/packet/TestPacket.cpp +4 -4
  97. package/worker/test/src/RTC/{TestSimpleConsumer.cpp → TestConsumer.cpp} +6 -7
  98. package/worker/test/src/RTC/TestPipeProducerStreamManager.cpp +471 -0
  99. package/worker/test/src/RTC/TestSimpleProducerStreamManager.cpp +531 -0
  100. package/worker/test/src/RTC/TestSimulcastProducerStreamManager.cpp +1040 -0
  101. package/worker/test/src/RTC/TestSvcProducerStreamManager.cpp +1278 -0
  102. package/worker/fbs/liburing.fbs +0 -7
  103. package/worker/include/DepLibUring.hpp +0 -143
  104. package/worker/include/RTC/PipeConsumer.hpp +0 -95
  105. package/worker/include/RTC/SimpleConsumer.hpp +0 -102
  106. package/worker/include/RTC/SimulcastConsumer.hpp +0 -141
  107. package/worker/include/RTC/SvcConsumer.hpp +0 -118
  108. package/worker/src/DepLibUring.cpp +0 -638
  109. package/worker/src/RTC/PipeConsumer.cpp +0 -874
  110. package/worker/src/RTC/SimpleConsumer.cpp +0 -882
  111. package/worker/src/RTC/SimulcastConsumer.cpp +0 -1887
  112. package/worker/src/RTC/SvcConsumer.cpp +0 -1384
  113. package/worker/subprojects/liburing.wrap +0 -14
@@ -7,6 +7,7 @@
7
7
  #include "RTC/SCTP/packet/errorCauses/NoUserDataErrorCause.hpp"
8
8
  #include "RTC/SCTP/packet/errorCauses/OutOfResourceErrorCause.hpp"
9
9
  #include "RTC/SCTP/packet/errorCauses/ProtocolViolationErrorCause.hpp"
10
+ #include "RTC/SCTP/packet/errorCauses/StaleCookieErrorCause.hpp"
10
11
  #include "RTC/SCTP/packet/errorCauses/UnrecognizedChunkTypeErrorCause.hpp"
11
12
  #include "RTC/SCTP/packet/errorCauses/UserInitiatedAbortErrorCause.hpp"
12
13
  #include "RTC/SCTP/packet/parameters/ForwardTsnSupportedParameter.hpp"
@@ -83,6 +84,15 @@ namespace RTC
83
84
  mayConnectOnReceivedSctpData(mayConnectOnReceivedSctpData)
84
85
  {
85
86
  MS_TRACE();
87
+
88
+ // Generate the per-association secret used to authenticate the State
89
+ // Cookies we generate.
90
+ //
91
+ // @see RFC 9260 section 5.1.3.
92
+ if (this->sctpOptions.requireAuthenticatedCookie)
93
+ {
94
+ Utils::Crypto::WriteRandomBytes(this->stateCookieSecret, Association::StateCookieSecretLength);
95
+ }
86
96
  }
87
97
 
88
98
  Association::~Association()
@@ -650,6 +660,8 @@ namespace RTC
650
660
 
651
661
  const auto prevState = this->state;
652
662
 
663
+ // Notice that, contrary to what happens in dcsctp, here the association
664
+ // never transitions back to NEW state.
653
665
  SetState(State::CLOSED, message);
654
666
 
655
667
  if (prevState == State::COOKIE_WAIT || prevState == State::COOKIE_ECHOED)
@@ -1057,6 +1069,32 @@ namespace RTC
1057
1069
  {
1058
1070
  MS_TRACE();
1059
1071
 
1072
+ // https://datatracker.ietf.org/doc/html/rfc9653#section-5.3
1073
+ //
1074
+ // "If an endpoint has sent the Zero Checksum Acceptable Chunk Parameter
1075
+ // indicating the support of an alternate error detection method in an INIT
1076
+ // or INIT ACK chunk, in addition to SCTP packets containing the correct
1077
+ // CRC32c checksum value it MUST accept SCTP packets that have an incorrect
1078
+ // checksum value of zero [...]"
1079
+ const bool acceptZeroChecksum =
1080
+ this->sctpOptions.zeroChecksumAlternateErrorDetectionMethod !=
1081
+ ZeroChecksumAcceptableParameter::AlternateErrorDetectionMethod::NONE;
1082
+
1083
+ // Validate the CRC32c checksum unless this is a zero checksum packet that we
1084
+ // announced we are willing to accept.
1085
+ if (!acceptZeroChecksum || receivedPacket->GetChecksum() != 0)
1086
+ {
1087
+ if (!receivedPacket->ValidateCRC32cChecksum())
1088
+ {
1089
+ MS_WARN_TAG(sctp, "invalid CRC32c checksum, packet discarded");
1090
+
1091
+ this->associationListenerDeferrer.OnAssociationError(
1092
+ Types::ErrorKind::PARSE_FAILED, "invalid CRC32c checksum");
1093
+
1094
+ return false;
1095
+ }
1096
+ }
1097
+
1060
1098
  const uint32_t localVerificationTag = this->tcb ? this->tcb->GetLocalVerificationTag() : 0;
1061
1099
 
1062
1100
  // https://datatracker.ietf.org/doc/html/rfc9260#section-8.5.1
@@ -1113,7 +1151,7 @@ namespace RTC
1113
1151
  {
1114
1152
  MS_WARN_TAG(
1115
1153
  sctp,
1116
- "receievd ABORT chunk has invalid verification tag %" PRIu32 ", packet discarded",
1154
+ "received ABORT chunk has invalid verification tag %" PRIu32 ", packet discarded",
1117
1155
  receivedPacket->GetVerificationTag());
1118
1156
 
1119
1157
  this->associationListenerDeferrer.OnAssociationError(
@@ -1537,6 +1575,12 @@ namespace RTC
1537
1575
  const auto negotiatedCapabilities =
1538
1576
  NegotiatedCapabilities::Factory(this->sctpOptions, receivedInitChunk);
1539
1577
 
1578
+ // When authentication is enabled, generate an authenticated cookie with a
1579
+ // creation timestamp and a MAC keyed with our per-association secret.
1580
+ //
1581
+ // @see RFC 9260 section 5.1.3.
1582
+ const bool authenticateCookie = this->sctpOptions.requireAuthenticatedCookie;
1583
+
1540
1584
  // Write the StateCookie in place in the parameter.
1541
1585
  stateCookieParameter->WriteStateCookieInPlace(
1542
1586
  localVerificationTag,
@@ -1545,7 +1589,10 @@ namespace RTC
1545
1589
  receivedInitChunk->GetInitialTsn(),
1546
1590
  receivedInitChunk->GetAdvertisedReceiverWindowCredit(),
1547
1591
  tieTag,
1548
- negotiatedCapabilities);
1592
+ negotiatedCapabilities,
1593
+ /*creationTimestampMs*/ authenticateCookie ? this->shared->GetTimeMs() : 0,
1594
+ /*macKey*/ authenticateCookie ? this->stateCookieSecret : nullptr,
1595
+ /*macKeyLength*/ authenticateCookie ? Association::StateCookieSecretLength : 0);
1549
1596
 
1550
1597
  stateCookieParameter->Consolidate();
1551
1598
 
@@ -1678,6 +1725,18 @@ namespace RTC
1678
1725
  return;
1679
1726
  }
1680
1727
 
1728
+ // When authentication is enabled, verify that the cookie was generated by
1729
+ // us (valid MAC) and is not stale before trusting any of its contents.
1730
+ //
1731
+ // @see RFC 9260 section 5.1.4.
1732
+ if (this->sctpOptions.requireAuthenticatedCookie)
1733
+ {
1734
+ if (!VerifyReceivedStateCookie(cookie.get()))
1735
+ {
1736
+ return;
1737
+ }
1738
+ }
1739
+
1681
1740
  if (this->tcb)
1682
1741
  {
1683
1742
  if (!HandleReceivedCookieEchoChunkWithTcb(receivedPacket, cookie.get()))
@@ -2073,6 +2132,10 @@ namespace RTC
2073
2132
  errorCausesStr.append(errorCause->ToString());
2074
2133
  }
2075
2134
 
2135
+ // https://datatracker.ietf.org/doc/html/rfc9260#section-3.3.7
2136
+ //
2137
+ // "If an endpoint receives an ABORT with a format error or no TCB is
2138
+ // found, it MUST silently discard it."
2076
2139
  if (!this->tcb)
2077
2140
  {
2078
2141
  MS_DEBUG_TAG(
@@ -2083,7 +2146,7 @@ namespace RTC
2083
2146
  return;
2084
2147
  }
2085
2148
 
2086
- MS_WARN_TAG(sctp, "received ABORT chunk, closing association: %s", errorCausesStr.c_str());
2149
+ MS_DEBUG_TAG(sctp, "received ABORT chunk, closing association: %s", errorCausesStr.c_str());
2087
2150
 
2088
2151
  InternalClose(Types::ErrorKind::PEER_REPORTED, errorCausesStr);
2089
2152
  }
@@ -2306,11 +2369,11 @@ namespace RTC
2306
2369
  // to deliver messages.
2307
2370
  if (this->tcb->GetReassemblyQueue().IsAboveWatermark())
2308
2371
  {
2309
- MS_WARN_TAG(sctp, "reassembly queue is above watermark");
2372
+ MS_WARN_DEV("reassembly queue is above watermark");
2310
2373
 
2311
2374
  if (!this->tcb->GetDataTracker().WillIncreaseCumAckTsn(tsn))
2312
2375
  {
2313
- MS_WARN_TAG(sctp, "reassembly queue is above watermark");
2376
+ MS_WARN_TAG(sctp, "rejecting received data because reassembly queue is above watermark");
2314
2377
 
2315
2378
  this->tcb->GetDataTracker().ForceImmediateSack();
2316
2379
 
@@ -2320,7 +2383,7 @@ namespace RTC
2320
2383
 
2321
2384
  if (!this->tcb->GetDataTracker().IsTsnValid(tsn))
2322
2385
  {
2323
- MS_WARN_TAG(sctp, "data rejected because of failing TSN validity");
2386
+ MS_WARN_TAG(sctp, "rejecting received data because of failing TSN validity");
2324
2387
 
2325
2388
  return;
2326
2389
  }
@@ -2371,8 +2434,7 @@ namespace RTC
2371
2434
  }
2372
2435
  else
2373
2436
  {
2374
- MS_WARN_TAG(
2375
- sctp,
2437
+ MS_WARN_DEV(
2376
2438
  "dropping received out-of-order SACK [TSN:%" PRIu32 "]",
2377
2439
  receivedSackChunk->GetCumulativeTsnAck());
2378
2440
  }
@@ -2432,6 +2494,86 @@ namespace RTC
2432
2494
  return !skipProcessing;
2433
2495
  }
2434
2496
 
2497
+ bool Association::VerifyReceivedStateCookie(const StateCookie* cookie)
2498
+ {
2499
+ MS_TRACE();
2500
+
2501
+ // https://datatracker.ietf.org/doc/html/rfc9260#section-5.1.4
2502
+ //
2503
+ // "Authenticate the State Cookie as one that it previously generated by
2504
+ // comparing the computed MAC against the one carried in the State Cookie.
2505
+ // If this comparison fails, the SCTP packet, including the COOKIE ECHO and
2506
+ // any DATA chunks, should be silently discarded."
2507
+ //
2508
+ // NOTE: The Verification Tag check (RFC 9260 section 5.1.4) is not done
2509
+ // here but in the COOKIE-ECHO handling logic (with and without TCB), which
2510
+ // is performed regardless of whether authentication is enabled.
2511
+ if (!StateCookie::VerifyMac(
2512
+ cookie->GetBuffer(),
2513
+ cookie->GetLength(),
2514
+ this->stateCookieSecret,
2515
+ Association::StateCookieSecretLength))
2516
+ {
2517
+ MS_WARN_TAG(
2518
+ sctp,
2519
+ "received COOKIE-ECHO chunk with State Cookie that failed MAC authentication, discarding");
2520
+
2521
+ this->associationListenerDeferrer.OnAssociationError(
2522
+ Types::ErrorKind::PARSE_FAILED,
2523
+ "received COOKIE-ECHO chunk with unauthenticated State Cookie");
2524
+
2525
+ return false;
2526
+ }
2527
+
2528
+ // https://datatracker.ietf.org/doc/html/rfc9260#section-5.1.4
2529
+ //
2530
+ // "Compare the creation timestamp in the State Cookie to the current local
2531
+ // time. If the elapsed time is longer than the lifespan carried in the
2532
+ // State Cookie, then the packet, including the COOKIE ECHO and any attached
2533
+ // DATA chunks, SHOULD be discarded, and the endpoint MUST transmit an ERROR
2534
+ // chunk with a 'Stale Cookie' error cause to the peer endpoint."
2535
+ const uint64_t nowMs = this->shared->GetTimeMs();
2536
+ const uint64_t creationMs = cookie->GetCreationTimestampMs();
2537
+
2538
+ if (nowMs > creationMs && nowMs - creationMs > StateCookie::ValidCookieLifeMs)
2539
+ {
2540
+ const uint64_t stalenessMs = nowMs - creationMs - StateCookie::ValidCookieLifeMs;
2541
+
2542
+ MS_WARN_TAG(
2543
+ sctp,
2544
+ "received COOKIE-ECHO chunk with stale State Cookie, sending Stale Cookie error [staleness:%" PRIu64
2545
+ "ms]",
2546
+ stalenessMs);
2547
+
2548
+ // Respond with an ERROR chunk carrying a Stale Cookie error cause. The
2549
+ // packet must use the verification tag the peer expects (the remote
2550
+ // verification tag stored in the cookie we generated).
2551
+ auto packet = CreatePacketWithVerificationTag(cookie->GetRemoteVerificationTag());
2552
+ auto* operationErrorChunk = packet->BuildChunkInPlace<OperationErrorChunk>();
2553
+ auto* staleCookieErrorCause =
2554
+ operationErrorChunk->BuildErrorCauseInPlace<StaleCookieErrorCause>();
2555
+
2556
+ // The Measure of Staleness is expressed in microseconds.
2557
+ const uint64_t stalenessUs = stalenessMs * 1000;
2558
+
2559
+ staleCookieErrorCause->SetMeasureOfStaleness(
2560
+ stalenessUs > std::numeric_limits<uint32_t>::max() ? std::numeric_limits<uint32_t>::max()
2561
+ : static_cast<uint32_t>(stalenessUs));
2562
+
2563
+ staleCookieErrorCause->Consolidate();
2564
+ operationErrorChunk->Consolidate();
2565
+
2566
+ this->packetSender.SendPacket(packet.get());
2567
+
2568
+ this->associationListenerDeferrer.OnAssociationError(
2569
+ Types::ErrorKind::WRONG_SEQUENCE, "received COOKIE-ECHO chunk with stale State Cookie");
2570
+
2571
+ return false;
2572
+ }
2573
+
2574
+ return true;
2575
+ }
2576
+
2435
2577
  void Association::OnT1InitTimer(uint64_t& /*baseTimeoutMs*/, bool& /*stop*/)
2436
2578
  {
2437
2579
  MS_TRACE();
@@ -4,6 +4,8 @@
4
4
  #include "RTC/SCTP/association/StateCookie.hpp"
5
5
  #include "Logger.hpp"
6
6
  #include "MediaSoupErrors.hpp"
7
+ #include <cstring> // std::memcpy(), std::memcmp()
8
+ #include <string_view>
7
9
 
8
10
  namespace RTC
9
11
  {
@@ -11,31 +13,6 @@ namespace RTC
11
13
  {
12
14
  /* Class methods. */
13
15
 
14
- bool StateCookie::IsMediasoupStateCookie(const uint8_t* buffer, size_t bufferLength)
15
- {
16
- MS_TRACE();
17
-
18
- if (bufferLength != StateCookie::StateCookieLength)
19
- {
20
- return false;
21
- }
22
-
23
- if (Utils::Byte::Get8Bytes(buffer, 0) != StateCookie::Magic1)
24
- {
25
- return false;
26
- }
27
-
28
- auto* negotiatedCapabilitiesField = reinterpret_cast<NegotiatedCapabilitiesField*>(
29
- const_cast<uint8_t*>(buffer) + StateCookie::NegotiatedCapabilitiesOffset);
30
-
31
- if (ntohs(negotiatedCapabilitiesField->magic2) != StateCookie::Magic2)
32
- {
33
- return false;
34
- }
35
-
36
- return true;
37
- }
38
-
39
16
  StateCookie* StateCookie::Parse(const uint8_t* buffer, size_t bufferLength)
40
17
  {
41
18
  MS_TRACE();
@@ -61,7 +38,10 @@ namespace RTC
61
38
  uint32_t remoteInitialTsn,
62
39
  uint32_t remoteAdvertisedReceiverWindowCredit,
63
40
  uint64_t tieTag,
64
- const NegotiatedCapabilities& negotiatedCapabilities)
41
+ const NegotiatedCapabilities& negotiatedCapabilities,
42
+ uint64_t creationTimestampMs,
43
+ const uint8_t* macKey,
44
+ size_t macKeyLength)
65
45
  {
66
46
  MS_TRACE();
67
47
 
@@ -75,9 +55,15 @@ namespace RTC
75
55
  remoteInitialTsn,
76
56
  remoteAdvertisedReceiverWindowCredit,
77
57
  tieTag,
78
- negotiatedCapabilities);
58
+ negotiatedCapabilities,
59
+ creationTimestampMs,
60
+ macKey,
61
+ macKeyLength);
62
+
63
+ const size_t cookieLength = macKey != nullptr ? StateCookie::AuthenticatedStateCookieLength
64
+ : StateCookie::StateCookieLength;
79
65
 
80
- return new StateCookie(buffer, StateCookie::StateCookieLength);
66
+ return new StateCookie(buffer, cookieLength);
81
67
  }
82
68
 
83
69
  void StateCookie::Write(
@@ -89,11 +75,18 @@ namespace RTC
89
75
  uint32_t remoteInitialTsn,
90
76
  uint32_t remoteAdvertisedReceiverWindowCredit,
91
77
  uint64_t tieTag,
92
- const NegotiatedCapabilities& negotiatedCapabilities)
78
+ const NegotiatedCapabilities& negotiatedCapabilities,
79
+ uint64_t creationTimestampMs,
80
+ const uint8_t* macKey,
81
+ size_t macKeyLength)
93
82
  {
94
83
  MS_TRACE();
95
84
 
96
- if (bufferLength < StateCookie::StateCookieLength)
85
+ const bool authenticate = macKey != nullptr;
86
+ const size_t cookieLength =
87
+ authenticate ? StateCookie::AuthenticatedStateCookieLength : StateCookie::StateCookieLength;
88
+
89
+ if (bufferLength < cookieLength)
97
90
  {
98
91
  MS_THROW_TYPE_ERROR("buffer too small");
99
92
  }
@@ -119,6 +112,65 @@ namespace RTC
119
112
  htons(negotiatedCapabilities.negotiatedMaxOutboundStreams);
120
113
  negotiatedCapabilitiesField->negotiatedMaxInboundStreams =
121
114
  htons(negotiatedCapabilities.negotiatedMaxInboundStreams);
115
+
116
+ if (!authenticate)
117
+ {
118
+ return;
119
+ }
120
+
121
+ // Append the creation timestamp and the MAC computed over all preceding
122
+ // bytes (including the timestamp).
123
+ //
124
+ // @see RFC 9260 section 5.1.3.
125
+ Utils::Byte::Set8Bytes(buffer, StateCookie::TimestampOffset, creationTimestampMs);
126
+
127
+ const uint8_t* mac = Utils::Crypto::GetHmacSha1(
128
+ reinterpret_cast<const char*>(macKey), macKeyLength, buffer, StateCookie::MacOffset);
129
+
130
+ std::memcpy(buffer + StateCookie::MacOffset, mac, StateCookie::MacLength);
131
+ }
132
+
133
+ bool StateCookie::IsMediasoupStateCookie(const uint8_t* buffer, size_t bufferLength)
134
+ {
135
+ MS_TRACE();
136
+
137
+ if (bufferLength != StateCookie::StateCookieLength && bufferLength != StateCookie::AuthenticatedStateCookieLength)
138
+ {
139
+ return false;
140
+ }
141
+
142
+ if (Utils::Byte::Get8Bytes(buffer, 0) != StateCookie::Magic1)
143
+ {
144
+ return false;
145
+ }
146
+
147
+ auto* negotiatedCapabilitiesField = reinterpret_cast<NegotiatedCapabilitiesField*>(
148
+ const_cast<uint8_t*>(buffer) + StateCookie::NegotiatedCapabilitiesOffset);
149
+
150
+ if (ntohs(negotiatedCapabilitiesField->magic2) != StateCookie::Magic2)
151
+ {
152
+ return false;
153
+ }
154
+
155
+ return true;
156
+ }
157
+
158
+ bool StateCookie::VerifyMac(
159
+ const uint8_t* buffer, size_t bufferLength, const uint8_t* macKey, size_t macKeyLength)
160
+ {
161
+ MS_TRACE();
162
+
163
+ // An authenticated cookie has a fixed length.
164
+ if (bufferLength != StateCookie::AuthenticatedStateCookieLength)
165
+ {
166
+ return false;
167
+ }
168
+
169
+ // Recompute the MAC over all bytes preceding the MAC field.
170
+ const uint8_t* expectedMac = Utils::Crypto::GetHmacSha1(
171
+ reinterpret_cast<const char*>(macKey), macKeyLength, buffer, StateCookie::MacOffset);
172
+
173
+ return std::memcmp(buffer + StateCookie::MacOffset, expectedMac, StateCookie::MacLength) == 0;
122
174
  }
123
175
 
124
176
  Types::SctpImplementation StateCookie::DetermineSctpImplementation(
@@ -158,7 +210,13 @@ namespace RTC
158
210
  {
159
211
  MS_TRACE();
160
212
 
161
- SetLength(StateCookie::StateCookieLength);
213
+ // The cookie length is determined by whether it carries a MAC. When the
214
+ // cookie is cloned into a larger buffer, `CloneInto()` will set the
215
+ // proper length afterwards.
216
+ SetLength(
217
+ bufferLength == StateCookie::AuthenticatedStateCookieLength
218
+ ? StateCookie::AuthenticatedStateCookieLength
219
+ : StateCookie::StateCookieLength);
162
220
  }
163
221
 
164
222
  StateCookie::~StateCookie()
@@ -183,6 +241,13 @@ namespace RTC
183
241
  " remote advertised receiver window credit: %" PRIu32,
184
242
  GetRemoteAdvertisedReceiverWindowCredit());
185
243
  MS_DUMP_CLEAN(indentation, " tie-tag: %" PRIu64, GetTieTag());
244
+ MS_DUMP_CLEAN(indentation, " authenticated: %s", IsAuthenticated() ? "yes" : "no");
245
+
246
+ if (IsAuthenticated())
247
+ {
248
+ MS_DUMP_CLEAN(indentation, " creation timestamp (ms): %" PRIu64, GetCreationTimestampMs());
249
+ }
250
+
186
251
  negotiatedCapabilities.Dump(indentation + 1);
187
252
  MS_DUMP_CLEAN(indentation, "</SCTP::StateCookie>");
188
253
  }
@@ -419,7 +419,7 @@ namespace RTC
419
419
 
420
420
  auto crc32c = GetChecksum();
421
421
 
422
- // NOTE: Cannot use SetChecksum() because its a `const` method.
422
+ // NOTE: Cannot use `SetChecksum()` because this is a `const` method.
423
423
  GetHeaderPointer()->checksum = 0;
424
424
 
425
425
  auto computedCrc32c = Utils::Crypto::GetCRC32c(GetBuffer(), GetLength());
@@ -122,7 +122,10 @@ namespace RTC
122
122
  uint32_t remoteInitialTsn,
123
123
  uint32_t remoteAdvertisedReceiverWindowCredit,
124
124
  uint64_t tieTag,
125
- const NegotiatedCapabilities& negotiatedCapabilities)
125
+ const NegotiatedCapabilities& negotiatedCapabilities,
126
+ uint64_t creationTimestampMs,
127
+ const uint8_t* macKey,
128
+ size_t macKeyLength)
126
129
  {
127
130
  MS_TRACE();
128
131
 
@@ -144,9 +147,15 @@ namespace RTC
144
147
  remoteInitialTsn,
145
148
  remoteAdvertisedReceiverWindowCredit,
146
149
  tieTag,
147
- negotiatedCapabilities);
150
+ negotiatedCapabilities,
151
+ creationTimestampMs,
152
+ macKey,
153
+ macKeyLength);
148
154
 
149
- SetVariableLengthValueLength(StateCookie::StateCookieLength);
155
+ const size_t cookieLength = macKey != nullptr ? StateCookie::AuthenticatedStateCookieLength
156
+ : StateCookie::StateCookieLength;
157
+
158
+ SetVariableLengthValueLength(cookieLength);
150
159
  }
151
160
 
152
161
  StateCookieParameter* StateCookieParameter::SoftClone(const uint8_t* buffer) const
@@ -84,6 +84,10 @@ namespace RTC
84
84
  ZeroChecksumAcceptableParameter::AlternateErrorDetectionMethodToString(
85
85
  this->zeroChecksumAlternateErrorDetectionMethod)
86
86
  .c_str());
87
+ MS_DUMP_CLEAN(
88
+ indentation,
89
+ " require authenticated cookie: %s",
90
+ this->requireAuthenticatedCookie ? "yes" : "no");
87
91
  MS_DUMP_CLEAN(indentation, "</SCTP::SctpOptions>");
88
92
  }
89
93
  } // namespace SCTP