Research for SpawnDev.RTLink SIPSorcery fork planning.
The RTLink project bundles a modified copy of SIPSorcery v6.0.11 (the "OLD stack") that uses Portable.BouncyCastle 1.9.0 and the original Rafael Soares DTLS/SRTP implementation. This stack successfully connects to browser WebRTC peers.
SIPSorcery 10.0.3+ (the "NEW stack") contains a complete DTLS/SRTP rewrite called "SharpSRTP"
by Lukas Volf (GitHub: jimm98y), merged Dec 30 2025 via PR #1486. It uses
BouncyCastle.Cryptography 2.6.2 and the new Org.BouncyCastle.Tls namespace. This rewrite
has multiple known bugs and interoperability failures with browser peers.
Recommendation: Fork SIPSorcery, keep the OLD stack architecture with its proven BouncyCastle
Org.BouncyCastle.Crypto.Tls API surface, selectively upgrade only what is needed (ECDSA certs,
modern cipher suites), and avoid the SharpSRTP rewrite entirely.
- SIPSorcery version: 6.0.11
- BouncyCastle: Portable.BouncyCastle 1.9.0
- Namespace:
Org.BouncyCastle.Crypto.Tls - Author: Rafael Soares (raf.csoares@kyubinteractive.com), July 2020
- Origin: Ported from RestComm/media-core Java implementation
- License: BSD 3-Clause (with AGPL-3.0 origin for some derived files)
- SIPSorcery version: 10.0.4-pre (source at D:\users\tj\Projects\sipsorcery-master)
- BouncyCastle: BouncyCastle.Cryptography 2.6.2
- Namespace:
Org.BouncyCastle.Tls(completely different API) - SharpSRTP Author: Lukas Volf (jimm98y), December 2025
- License: MIT (SharpSRTP), BSD 3-Clause (SIPSorcery wrapper)
- PR: #1486 "New DTLS-SRTP implementation based upon SharpSRTP" merged 2026-01-04
- Follow-up PRs:
- #1495 "DTLS-SRTP code cleanup and fix for infinite DtlsServer handshake timeout" (merged 2026-01-19)
- #1499 "SRTP unprotect failed after ~4-5 min: receiver ROC never updates" (merged 2026-01-29)
- #1555 "SRTP Performance improvements and bugfixes" (merged 2026-04-14)
- Source repo is NOT a git clone - downloaded zip, no git history available
Location: D:\users\tj\Projects\SpawnDev.RTLink\SpawnDev.RTLink\SIPSorcery\net\DtlsSrtp\
Core DTLS:
DtlsSrtpClient.cs(453 lines) - DTLS client, extendsDefaultTlsClientDtlsSrtpServer.cs(584 lines) - DTLS server, extendsDefaultTlsServerDtlsSrtpTransport.cs(616 lines) - Transport layer, implementsDatagramTransportDtlsUtils.cs(683 lines) - Certificate generation, fingerprints, credential loading
SRTP:
SrtpParameters.cs(105 lines) - Protection profile configsSrtpPolicy.cs(96 lines) - Encryption/auth policySrtpHandler.cs(249 lines) - SDES-SRTP handlerSecureContext.cs(38 lines) - RTP session secure context
Transform (SRTP crypto engine):
Transform/IPackerTransformer.cs- Packet transformer interfaceTransform/ITransformEngine.cs- Transform engine interfaceTransform/RawPacket.cs- Raw packet representationTransform/SrtpCipherCTR.cs- AES-CTR cipherTransform/SrtpCipherF8.cs- AES-F8 cipherTransform/SrtpCryptoContext.cs- RTP crypto contextTransform/SrtcpCryptoContext.cs- RTCP crypto contextTransform/SrtpTransformEngine.cs- Engine factoryTransform/SrtpTransformer.cs- RTP transformerTransform/SrtcpTransformer.cs- RTCP transformer
Location: D:\users\tj\Projects\sipsorcery-master\src\SIPSorcery\net\DtlsSrtp\
Wrapper layer (kept from old):
DtlsSrtpTransport.cs(177 lines) - Heavily rewritten, delegates to SharpSRTPDtlsUtils.cs(79 lines) - Stripped to thin wrapper around SharpSRTPSecureContext.cs(35 lines) - UnchangedSrtpHandler.cs(155 lines) - Rewritten to use SharpSRTP
SharpSRTP Lib/DTLS:
Lib/Log.cs- Custom logging bridgeLib/DTLS/DtlsClient.cs(428 lines) - New client, extendsDefaultTlsClientLib/DTLS/DtlsServer.cs(425 lines) - New server, extendsDefaultTlsServerLib/DTLS/IDtlsPeer.cs(128 lines) - Interface + enums + event argsLib/DTLS/DtlsCertificateUtils.cs(215 lines) - Certificate generation
SharpSRTP Lib/DTLSSRTP:
Lib/DTLSSRTP/DtlsSrtpClient.cs(162 lines) - SRTP-aware clientLib/DTLSSRTP/DtlsSrtpServer.cs(131 lines) - SRTP-aware serverLib/DTLSSRTP/IDtlsSrtpPeer.cs(48 lines) - Interface + eventsLib/DTLSSRTP/DtlsSrtpKeys.cs(51 lines) - Key containerLib/DTLSSRTP/DtlsSrtpProtocol.cs(201 lines) - Key derivation + session creation
SharpSRTP Lib/SRTP:
Lib/SRTP/ISrtpContext.cs(33 lines)Lib/SRTP/SrtpContext.cs(600+ lines) - Main SRTP protect/unprotectLib/SRTP/SrtpKeys.cs(46 lines)Lib/SRTP/SrtpProtectionProfile.cs(83 lines)Lib/SRTP/SrtpProtocol.cs(144 lines)Lib/SRTP/SrtpSessionContext.cs(69 lines)Lib/SRTP/Authentication/HMAC.csLib/SRTP/Encryption/AEAD.cs(68 lines) - GCM AEAD encryptionLib/SRTP/Encryption/CTR.cs(133 lines) - AES-CTR encryptionLib/SRTP/Encryption/F8.csLib/SRTP/Readers/RtcpReader.csLib/SRTP/Readers/RtpReader.cs
DtlsSrtpTransport.DoHandshake()determines client/server role- Creates
DtlsClientProtocolorDtlsServerProtocol(BouncyCastle) - Client path:
clientProtocol.Connect(client, this)- blocking call - Server path:
serverProtocol.Accept(server, this)- blocking call - Transport implements
DatagramTransportinterface for packet I/O - Uses
BlockingCollection<byte[]>for incoming chunks - efficient take/wait - On handshake complete:
NotifyHandshakeComplete()fires - Master secret is copied immediately (cleared by BC after callback)
PrepareSrtpSharedSecret()derives SRTP keys via RFC 5764- SRTP encoders/decoders created from
SrtpTransformEngine - Handshake state tracked via
_handshakeComplete,_handshakeFailed,_handshakingflags
Key details:
- Timeout: 20 seconds total from start, not per-receive
- Retransmission: DTLS 1.3 exponential backoff (100ms to 6000ms) with random jitter
- Secure renegotiation override:
NotifySecureRenegotiation(bool)overridden to NOT throw on missing renegotiation support - this is required for Pion/other WebRTC libs - Protocol version: Offers DTLSv12, minimum DTLSv10
- NO HelloVerifyRequest - goes straight to handshake
DtlsSrtpTransport.DoHandshake()delegates to_connection.DoHandshake()- Client path:
DtlsClientProtocol.Connect(this, datagramTransport)- blocking - Server path: Uses
DtlsVerifierfor HelloVerifyRequest cookie exchange first, thenserverProtocol.Accept(this, clientDatagramTransport, request) - Transport uses
ConcurrentQueue<byte[]>with polling sleep loop (25ms intervals) - On handshake complete:
OnHandshakeCompletedevent fires - Security parameters passed via event args
DtlsSrtpProtocol.CreateMasterKeys()derives keys usingTlsUtilities.Prf()SrtpSessionContextcreated with separate encode/decodeSrtpContextobjects
Key differences:
- HelloVerifyRequest was initially ENABLED in the server - this broke Firefox (Firefox does not support HelloVerifyRequest for WebRTC). It was later disabled for WebRTC.
- Uses
ConcurrentQueue+Thread.Sleep(25)polling instead ofBlockingCollection.TryTake(timeout)- less efficient, more CPU burn - No secure renegotiation override - relies on BouncyCastle 2.x default behavior
- Protocol version: DTLSv12 only (DTLSv10 removed, DTLSv13 not yet supported by BC)
- Handshake timeout is passed to BouncyCastle via
GetHandshakeTimeoutMillis()override
- Algorithm: RSA 2048-bit (hardcoded
DEFAULT_KEY_SIZE = 2048) - Signature: SHA256WITHRSA
- Subject: CN=localhost, Issuer: CN=root
- SAN: localhost, 127.0.0.1
- EKU: Server Authentication (1.3.6.1.5.5.7.3.1)
- Validity: 70 years from now
- Generation:
DtlsUtils.CreateSelfSignedTlsCert()- creates BouncyCastle X509Certificate, wraps inOrg.BouncyCastle.Crypto.Tls.Certificate - Fingerprint: SHA-256, colon-separated hex
- Default Algorithm: ECDSA P-256 (secp256r1) - SHA256WITHECDSA
- RSA option: RSA 2048-bit available via
useRsaparameter - Subject: CN=WebRTC (for DTLS-SRTP), CN=DTLS (for base DTLS)
- No SAN, No EKU - simpler certificate
- Validity: 30 days (NOT 70 years)
- Generation:
DtlsCertificateUtils.GenerateECDSACertificate()- creates BC X509Certificate, wraps inOrg.BouncyCastle.Tls.Certificateviacrypto.CreateCertificate() - Fingerprint: SHA-256, colon-separated hex (same format)
- ECDSA is the correct direction for WebRTC (per W3C spec recommendation)
- The old RSA-only approach worked because browsers still accept RSA, but some implementations (notably older Firefox) had issues with RSA certificates
- The 30-day validity in the new stack is more appropriate for ephemeral WebRTC certs
- For the fork: Switch to ECDSA by default while keeping RSA as fallback
- Client offers:
SRTP_AES128_CM_HMAC_SHA1_80only (single profile) - Server accepts: First match from
{_80, _32, NULL_80, NULL_32} - Profile selection: Server iterates client profiles, picks first recognized one
- Bug: Server picks LAST match, not first - if client offers multiple, the lowest priority one wins. Confirmed in PR #1486 comments by Lukas Volf.
- MKI handling: Client generates 3-byte MKI (due to a bug - divides byte lengths by 8
again, producing
(16 + 14) / 8 = 3bytes). Server echoes client's MKI back.
- Client offers 12 profiles (ordered by preference):
- DOUBLE_AEAD_AES_256_GCM_AEAD_AES_256_GCM
- DOUBLE_AEAD_AES_128_GCM_AEAD_AES_128_GCM
- SRTP_AEAD_AES_256_GCM
- SRTP_AEAD_ARIA_256_GCM
- SRTP_AEAD_AES_128_GCM
- SRTP_AEAD_ARIA_128_GCM
- SRTP_ARIA_256_CTR_HMAC_SHA1_80
- SRTP_AES128_CM_HMAC_SHA1_80
- SRTP_ARIA_128_CTR_HMAC_SHA1_80
- SRTP_AES128_CM_HMAC_SHA1_32
- SRTP_ARIA_256_CTR_HMAC_SHA1_32
- SRTP_ARIA_128_CTR_HMAC_SHA1_32
- NULL profiles excluded (per RFC 8827: "MUST NOT negotiate NULL encryption")
- Server selects: Highest server-priority profile from mutual set
- MKI: Default 0-byte length (no MKI).
ForceDisableMKIproperty on server.
- Browsers typically support:
SRTP_AES128_CM_HMAC_SHA1_80,SRTP_AEAD_AES_128_GCM,SRTP_AEAD_AES_256_GCM - The old stack's single-profile approach is safe but limiting
- The new stack's ARIA profiles are unusual - most browsers don't support them
- For the fork: Offer
SRTP_AEAD_AES_128_GCM,SRTP_AEAD_AES_256_GCM,SRTP_AES128_CM_HMAC_SHA1_80in that order. No MKI. No NULL. No ARIA.
- Keys derived in
PrepareSrtpSharedSecret()called fromNotifyHandshakeComplete() - Master secret copied before BC clears it
- Uses
TlsUtilities.PRF()with labelExporterLabel.dtls_srtp - Seed:
ClientRandom || ServerRandom - Length:
2 * (keyLen + saltLen)where lengths come fromSrtpParameters - Layout:
[clientKey | serverKey | clientSalt | serverSalt] - Lengths are in BYTES (14-byte salt for AES128_CM)
- Keys derived in
DtlsSrtpProtocol.CreateMasterKeys() - Called from
OnHandshakeCompletedevent handler - Uses
TlsUtilities.Prf()with same label and seed - Critical difference: Lengths are in BITS in
SrtpProtectionProfileConfiguration, divided by 8 at derivation time - AES128_CM: key=128 bits (16 bytes), salt=112 bits (14 bytes) - same final values
- Layout: Same
[clientKey | serverKey | clientSalt | serverSalt]for standard profiles - Double AEAD (RFC 8723): Special layout maintaining inner/outer key separation
- The actual key derivation math is equivalent for standard profiles
- The new stack's bit-vs-byte representation is confusing but produces correct results
- For the fork: Keep the old stack's byte-based approach for clarity
This is the most significant technical difference and the primary reason a simple upgrade is difficult.
- DefaultTlsClient / DefaultTlsServer (non-generic IDictionary)
- TlsAuthentication.NotifyServerCertificate(Certificate)
- TlsAuthentication.GetClientCredentials(CertificateRequest)
- DefaultTlsSignerCredentials, DefaultTlsEncryptionCredentials
- DtlsClientProtocol(SecureRandom), DtlsServerProtocol(SecureRandom)
- TlsSRTPUtils (SRTP extension utility)
- ProtocolVersion.DTLSv12 / DTLSv10
- byte alertLevel/alertDescription parameters
- Certificate wraps X509CertificateStructure[]
- DefaultTlsClient / DefaultTlsServer (generic IDictionary<int, byte[]>)
- TlsAuthentication.NotifyServerCertificate(TlsServerCertificate)
- TlsAuthentication.GetClientCredentials(CertificateRequest) - returns TlsCredentials
- BcDefaultTlsCredentialedSigner (requires TlsCryptoParameters + BcTlsCrypto)
- DtlsClientProtocol() - no SecureRandom parameter
- TlsSrtpUtilities (note: different class name, same purpose)
- ProtocolVersion.DTLSv12.Only() - new API for version selection
- short alertLevel/alertDescription parameters
- Certificate wraps TlsCertificate[] (different type)
- DtlsVerifier for HelloVerifyRequest cookie exchange
- SecurityParameters accessible during handshake completion
- m_context instead of mContext field naming
The old and new BouncyCastle TLS APIs are NOT compatible. Every class, interface, and
method signature has changed. A migration from Org.BouncyCastle.Crypto.Tls to
Org.BouncyCastle.Tls requires rewriting every DTLS file.
For the fork: Stay on Portable.BouncyCastle if it works. Only migrate to
BouncyCastle.Cryptography if a specific feature requires it. If migration is needed,
port the OLD stack's architecture (which is proven) to the new API rather than adopting
SharpSRTP.
- Client: Inherits from
DefaultTlsClient- offers whatever BC defaults provide - Server: Uses
base.GetCipherSuites()- BC default TLS 1.2 suites - No explicit cipher suite filtering by certificate type
- Works because RSA certs match RSA cipher suites by default
- Explicit cipher suite lists based on certificate type:
- ECDSA:
ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,..._256_GCM_SHA384,..._128_CBC_SHA256,..._256_CBC_SHA384,..._CHACHA20_POLY1305_SHA256 - RSA:
ECDHE_RSA_WITH_AES_128_GCM_SHA256,..._256_GCM_SHA384,..._128_CBC_SHA256,..._256_CBC_SHA384,..._CHACHA20_POLY1305_SHA256
- ECDSA:
- TLS 1.3 suites commented out (BC doesn't support DTLS 1.3 yet)
- The new stack's explicit cipher suite lists are cleaner and more correct
- GCM suites should be preferred over CBC for performance
- For the fork: Use explicit cipher suite lists matching the certificate type
OnAlertevent withAlertLevelsEnum/AlertTypesEnumenums- Detailed logging of alert descriptions
close_notifytreated as info, everything else as warningTlsFatalAlertcaught and error message extracted- Timeout tracked internally with remaining-time calculation
- NotifySecureRenegotiation override - gracefully handles clients without renegotiation (required for Pion compatibility)
OnAlertevent withTlsAlertLevelsEnum/TlsAlertTypesEnumenums (more comprehensive enum with IANA assignments)- Logging via custom
Logclass bridged to SIPSorcery'sILogger - Debug logging gated behind
Log.DebugEnabled - Exception-based error flow (no error codes, just exception messages)
- Timeout delegated to BouncyCastle via
GetHandshakeTimeoutMillis() - NO NotifySecureRenegotiation override - relies on BC 2.x default
These were discovered during PR #1486 review and subsequent PRs:
- Symptom:
System.InvalidOperationException: GCM cipher cannot be reused for encryption - Cause: Multiple media streams (audio + video) sharing the same SRTP encryption context without locking
- Fix: Added locking when RTP/RTCP multiplexing shares the rtpChannel
- Symptom: DTLS handshake fails with Firefox
- Cause: DtlsServer used
DtlsVerifierfor HelloVerifyRequest cookie exchange. Firefox does not support HelloVerifyRequest for WebRTC. - Reference: w3c/webrtc-pc#749
- Fix: Disabled HelloVerifyRequest for WebRTC
- Symptom:
SRTP unprotect failed, result -1when connecting to old SIPSorcery - Cause: Old stack had a bug generating 3-byte MKI (dividing byte lengths by 8 again). New stack correctly handles MKI but old stack's Unprotect does not strip MKI bytes from authenticated portion.
- Fix: Disable MKI entirely for WebRTC per RFC 8827: "An SRTP Master Key Identifier (MKI) MUST NOT be used."
- Symptom: NULL encryption negotiated when it should not be
- Cause: Old stack's
ProcessClientExtensionspicked the LAST matching profile instead of the FIRST. When SharpSRTP offered NULL profiles, they got selected. - Fix: Removed NULL profiles from offered list. RFC 8827: "MUST NOT negotiate NULL encryption modes."
- Symptom:
SRTCP unprotect failed for audio track, result -4(replay) - Cause: With multiplexed audio/video, RTCP indexes for both start from 1 but share the same SrtpContext, causing replay detection false positives.
- Fix: Per-SSRC context tracking
- Symptom:
SRTP unprotect failed for video, result -3after ~65535 packets - Cause:
context.Roconly incremented inProtectRtp(send path), never inUnprotectRtp(receive path). After RTP sequence number wraps, HMAC computed with wrong ROC. - Fix: Derive ROC from per-SSRC last accepted index before HMAC verification
- Symptom: DtlsServer handshake hangs forever
- Cause: Default handshake timeout was infinite
- Fix: Set default timeout to 20 seconds
- Symptom: DTLS handshake fails with aiortc (Python WebRTC)
- Cause:
IsHashSupportedonly returned true for SHA-256, but aiortc uses SHA-512 - Fix: Use BouncyCastle's
DigestUtilities.GetDigest()to check support dynamically
- Symptom:
System.IndexOutOfRangeExceptioninSrtcpCryptoContext.ComputeIvwhen closing connection - Cause: SRTCP context accessed after transport close, null/empty key state
Tested by Aaron Clauson (sipsorcery maintainer) using Docker echo servers:
- Janus WebRTC
- libdatachannel
- libwebrtc (Chrome's lib, ~12 months old image)
- Pion WebRTC
- webrtc-rs
- werift WebRTC
- aiortc (after SHA-512 fix)
- Firefox (after HelloVerifyRequest disabled)
- Chrome/Edge browsers
- sipsorcery master as server / SharpSRTP as client: Auth failures due to MKI bug in old code
- kurento: Requires specific client switch not wired up
- gstreamer: Failed (no details on cause)
- Ported from Jitsi SRTP (Java) via RestComm/media-core
SrtpCryptoContext/SrtcpCryptoContext- stateful per-SSRC contextsSrtpTransformEngine- creates RTP/RTCP transformersIPacketTransformerinterface withTransform()/ReverseTransform()RawPacketwrapper for packet manipulation- Ciphers: AES-CM (CTR mode), AES-F8
- Auth: HMAC-SHA1
- No AEAD support (no AES-GCM)
- Thread safety: Callers lock on encoder/decoder objects
- Written from scratch based on RFCs
SrtpContext- unified context for both RTP and RTCPSrtpSessionContext- groups encode/decode contextsISrtpContextinterface withProtectRtp()/UnprotectRtp()etc.- Direct byte array manipulation (no RawPacket wrapper)
- Ciphers: AES-CM, AES-F8, AEAD AES-GCM, AEAD ARIA-GCM, Double AEAD
- Auth: HMAC-SHA1, NONE (for AEAD modes with built-in auth)
- Per-SSRC replay window tracking
- Thread safety: Locking added post-merge for multiplexed streams
The AEAD (AES-GCM) support is the single genuinely valuable addition. Modern browsers prefer GCM cipher suites and will negotiate them when available. The old stack only supports AES-CM with HMAC-SHA1, which works but is not optimal.
- DtlsSrtpTransport architecture - proven, efficient (BlockingCollection vs polling)
- DtlsSrtpClient/Server structure - clean, simple, works with browsers
- NotifySecureRenegotiation override on server - essential for Pion compatibility
- Key derivation in NotifyHandshakeComplete - correct timing, proven approach
- SrtpTransformEngine / IPacketTransformer pattern - well-tested
- DtlsUtils credential loading - comprehensive and correct
- Certificate generation - switch from RSA to ECDSA P-256 (use new stack's approach)
- SRTP profile list - offer GCM profiles, remove NULL, fix server selection order
- MKI handling - set to empty byte array per RFC 8827
- Certificate validity - reduce from 70 years to 30 days
- AEAD encryption (
Lib/SRTP/Encryption/AEAD.cs) - for AES-GCM support - Extended protection profiles -
SrtpProtectionProfileConfigurationconcept - Per-SSRC replay window - better than old stack's per-context approach
- ECDSA certificate generation -
DtlsCertificateUtils.GenerateECDSACertificate() - ForceDisableMKI pattern - clean way to enforce RFC 8827
- SharpSRTP's DtlsClient/DtlsServer - over-engineered, event-driven, not needed
- DtlsVerifier / HelloVerifyRequest - not supported by Firefox for WebRTC
- ConcurrentQueue + Thread.Sleep polling - worse than BlockingCollection
- Lib/DTLSSRTP layer - unnecessary abstraction, adds complexity
- Double AEAD support - browsers don't use it, adds complexity
- ARIA cipher support - browsers don't support it
- Pro: Zero migration work, proven working
- Con: Old library, no new features, may have security fixes missing
- Con:
Org.BouncyCastle.Crypto.Tlsnamespace is deprecated
- Pro: Active development, security fixes, ECDSA support improved
- Pro:
Org.BouncyCastle.Tlsnamespace is the future - Con: Every DTLS class must be rewritten to new API
- Con: Risk of introducing new bugs during migration
- Pro: Best of both worlds - new crypto, proven DTLS flow
- Con: Most work - port old design to new API
- Con: Old
DefaultTlsClient/DefaultTlsServerpatterns need updating to new signatures
Option A for now (stay on Portable.BouncyCastle 1.9.0), with targeted cherry-picks:
- Add ECDSA cert generation using BouncyCastle's existing EC key gen (which IS in 1.9.0)
- Fix the SRTP profile selection order bug
- Remove MKI negotiation
- Add explicit cipher suite lists
Only migrate to BouncyCastle.Cryptography 2.x if a browser stops supporting the cipher suites available with the old stack, or if a security vulnerability is found in 1.9.0.
- Start from RTLink's SIPSorcery v6.0.11 bundled copy
- Fix SRTP profile selection (first match, not last)
- Generate ECDSA certs instead of RSA (BC 1.9.0 supports this)
- Remove MKI negotiation (set empty byte array)
- Add SRTP_AEAD_AES_128_GCM and SRTP_AEAD_AES_256_GCM to offered profiles
- Port AEAD encryption from SharpSRTP for GCM support
- Add per-SSRC replay window tracking
- Reduce cert validity to 30 days
- Keep NotifySecureRenegotiation override
- Keep BlockingCollection transport
- Test against Chrome, Firefox, Edge, Pion, libdatachannel
- PR #1486: New DTLS-SRTP implementation based upon SharpSRTP
- PR #1495: DTLS-SRTP code cleanup and fix for infinite DtlsServer handshake timeout
- PR #1499: SRTP unprotect failed after ~4-5 min: receiver ROC never updates
- PR #1555: SRTP Performance improvements and bugfixes
- SharpSRTP Repository
- RFC 5764: DTLS-SRTP
- RFC 7714: AES-GCM for SRTP
- RFC 8827: WebRTC Security Architecture
- RFC 8723: Double AEAD
- Issue #1104: Chrome 124 DTLS Handshake Break
- W3C WebRTC HelloVerifyRequest Discussion