From a468e182eb2c243a0532e81f9e116abc00dd6841 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Santos=20Garrido?= Date: Sun, 21 Sep 2025 19:20:03 +0200 Subject: [PATCH] Fix voice receiving (#3190) * Update voice API to version 8 * Fix voice receiving * Reuse RTP Header array during decryption --- .../Audio/Sodium/SecretBox.cs | 4 +-- .../Audio/Streams/SodiumDecryptStream.cs | 25 ++++++++++++------- .../Audio/Streams/SodiumEncryptStream.cs | 15 +++++------ 3 files changed, 26 insertions(+), 18 deletions(-) diff --git a/src/Discord.Net.WebSocket/Audio/Sodium/SecretBox.cs b/src/Discord.Net.WebSocket/Audio/Sodium/SecretBox.cs index 7d9d76ef..c17d8248 100644 --- a/src/Discord.Net.WebSocket/Audio/Sodium/SecretBox.cs +++ b/src/Discord.Net.WebSocket/Audio/Sodium/SecretBox.cs @@ -31,7 +31,7 @@ namespace Discord.Audio } } - public static int Decrypt(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset, byte[] header, byte[] nonce, byte[] key) + public static int Decrypt(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset, byte[] header, int headerSize, byte[] nonce, byte[] key) { fixed (byte* inPtr = input) fixed (byte* outPtr = output) @@ -41,7 +41,7 @@ namespace Discord.Audio outPtr + outputOffset, out ulong plainLen, null, inPtr + inputOffset, (ulong)inputLength, - adPtr, (ulong)header.Length, + adPtr, (ulong)headerSize, nonce, key ); diff --git a/src/Discord.Net.WebSocket/Audio/Streams/SodiumDecryptStream.cs b/src/Discord.Net.WebSocket/Audio/Streams/SodiumDecryptStream.cs index 34646df2..6ebbc010 100644 --- a/src/Discord.Net.WebSocket/Audio/Streams/SodiumDecryptStream.cs +++ b/src/Discord.Net.WebSocket/Audio/Streams/SodiumDecryptStream.cs @@ -10,11 +10,13 @@ namespace Discord.Audio.Streams public class SodiumDecryptStream : AudioOutStream { private const int RtpHeaderSize = 12; + private const int ExtendedRtpHeaderSize = RtpHeaderSize + 4; private const int NonceSize = 24; private const int NonceCounterSize = 4; private readonly AudioClient _client; private readonly AudioStream _next; + private readonly byte[] _rtpHeader; private readonly byte[] _nonce; public override bool CanRead => true; @@ -25,6 +27,7 @@ namespace Discord.Audio.Streams { _next = next; _client = (AudioClient)client; + _rtpHeader = new byte[ExtendedRtpHeaderSize]; _nonce = new byte[NonceSize]; } @@ -35,26 +38,30 @@ namespace Discord.Audio.Streams if (_client.SecretKey == null) return Task.CompletedTask; - // Extract nonce from the payload. - for (int i = 0; i < NonceCounterSize; i++ ) - _nonce[i] = buffer[offset + count + NonceCounterSize - i - 1]; // Big-endian to little-endian + // Extract nonce counter from the end of the payload. + for (int i = 0; i < NonceCounterSize; i++) + _nonce[i] = buffer[offset + count - NonceCounterSize + i]; + + // Extract RTP header + bool hasExtendedHeader = (buffer[0] & 0x10) != 0; + int rtpHeaderSize = hasExtendedHeader ? ExtendedRtpHeaderSize : RtpHeaderSize; + Buffer.BlockCopy(buffer, offset, _rtpHeader, 0, rtpHeaderSize); // Decrypt payload - byte[] rtpHeader = new byte[RtpHeaderSize]; - Buffer.BlockCopy(buffer, offset, rtpHeader, 0, rtpHeader.Length); - int payloadOffset = offset + rtpHeader.Length; - int payloadLength = count - rtpHeader.Length - NonceCounterSize; + int payloadOffset = offset + rtpHeaderSize; + int payloadLength = count - offset - rtpHeaderSize - NonceCounterSize; int decryptedLength = SecretBox.Decrypt( buffer, payloadOffset, payloadLength, buffer, payloadOffset, - rtpHeader, + _rtpHeader, + rtpHeaderSize, _nonce, _client.SecretKey); - int packageLength = rtpHeader.Length + decryptedLength; + int packageLength = rtpHeaderSize + decryptedLength; return _next.WriteAsync(buffer, offset, packageLength, cancelToken); } diff --git a/src/Discord.Net.WebSocket/Audio/Streams/SodiumEncryptStream.cs b/src/Discord.Net.WebSocket/Audio/Streams/SodiumEncryptStream.cs index c9ee9fb7..839ca43d 100644 --- a/src/Discord.Net.WebSocket/Audio/Streams/SodiumEncryptStream.cs +++ b/src/Discord.Net.WebSocket/Audio/Streams/SodiumEncryptStream.cs @@ -14,6 +14,7 @@ namespace Discord.Audio.Streams private readonly AudioClient _client; private readonly AudioStream _next; + private readonly byte[] _rtpHeader; private readonly byte[] _nonce; private bool _hasHeader; private ushort _nextSeq; @@ -24,6 +25,7 @@ namespace Discord.Audio.Streams { _next = next; _client = (AudioClient)client; + _rtpHeader = new byte[RtpHeaderSize]; _nonce = new byte[NonceSize]; _nonceCounter = 0; } @@ -59,24 +61,23 @@ namespace Discord.Audio.Streams if (++_nonceCounter >= uint.MaxValue) _nonceCounter = 0; - // Encrypt payload - byte[] rtpHeader = new byte[RtpHeaderSize]; - Buffer.BlockCopy(buffer, offset, rtpHeader, 0, rtpHeader.Length); - int payloadOffset = offset + rtpHeader.Length; - int payloadLength = count - rtpHeader.Length; + // Encrypt payload + Buffer.BlockCopy(buffer, offset, _rtpHeader, 0, _rtpHeader.Length); + int payloadOffset = offset + _rtpHeader.Length; + int payloadLength = count - offset - _rtpHeader.Length; int encryptedLength = SecretBox.Encrypt( buffer, payloadOffset, payloadLength, buffer, payloadOffset, - rtpHeader, + _rtpHeader, _nonce, _client.SecretKey); // Append nonce to encripted payload Buffer.BlockCopy(counterBytes, 0, buffer, payloadOffset + encryptedLength, counterBytes.Length); - int packageLength = rtpHeader.Length + encryptedLength + counterBytes.Length; + int packageLength = _rtpHeader.Length + encryptedLength + counterBytes.Length; _next.WriteHeader(_nextSeq, _nextTimestamp, false); await _next.WriteAsync(buffer, offset, packageLength, cancelToken).ConfigureAwait(false);