diff --git a/discord/sinks/core.py b/discord/sinks/core.py index c8ea31a82a..90fdbf7d19 100644 --- a/discord/sinks/core.py +++ b/discord/sinks/core.py @@ -107,11 +107,22 @@ def __init__(self, data, client): self.data = bytearray(data) self.client = client - self.header = data[:12] - self.data = self.data[12:] - unpacker = struct.Struct(">xxHII") - self.sequence, self.timestamp, self.ssrc = unpacker.unpack_from(self.header) + self.sequence, self.timestamp, self.ssrc = unpacker.unpack_from(self.data[:12]) + + # RFC3550 5.1: RTP Fixed Header Fields + if self.client.mode.endswith("_rtpsize"): + # If It Has CSRC Chunks + cutoff = 12 + (data[0] & 0b00_0_0_1111) * 4 + # If It Has A Extension + if data[0] & 0b00_0_1_0000: + cutoff += 4 + else: + cutoff = 12 + + self.header = data[:cutoff] + self.data = self.data[cutoff:] + self.decrypted_data = getattr(self.client, f"_decrypt_{self.client.mode}")( self.header, self.data ) diff --git a/discord/types/voice.py b/discord/types/voice.py index 732f9826d7..68d99ccd48 100644 --- a/discord/types/voice.py +++ b/discord/types/voice.py @@ -33,7 +33,10 @@ from .snowflake import Snowflake SupportedModes = Literal[ - "xsalsa20_poly1305_lite", "xsalsa20_poly1305_suffix", "xsalsa20_poly1305" + "xsalsa20_poly1305_lite", + "xsalsa20_poly1305_suffix", + "xsalsa20_poly1305", + "aead_xchacha20_poly1305_rtpsize", ] diff --git a/discord/voice_client.py b/discord/voice_client.py index 3ed983e2b6..a60d730413 100644 --- a/discord/voice_client.py +++ b/discord/voice_client.py @@ -271,6 +271,7 @@ def __init__(self, client: Client, channel: abc.Connectable): "xsalsa20_poly1305_lite", "xsalsa20_poly1305_suffix", "xsalsa20_poly1305", + "aead_xchacha20_poly1305_rtpsize", ) @property @@ -576,6 +577,7 @@ def _get_voice_packet(self, data): return encrypt_packet(header, data) def _encrypt_xsalsa20_poly1305(self, header: bytes, data) -> bytes: + # Deprecated, remove in 2.7 box = nacl.secret.SecretBox(bytes(self.secret_key)) nonce = bytearray(24) nonce[:12] = header @@ -583,12 +585,14 @@ def _encrypt_xsalsa20_poly1305(self, header: bytes, data) -> bytes: return header + box.encrypt(bytes(data), bytes(nonce)).ciphertext def _encrypt_xsalsa20_poly1305_suffix(self, header: bytes, data) -> bytes: + # Deprecated, remove in 2.7 box = nacl.secret.SecretBox(bytes(self.secret_key)) nonce = nacl.utils.random(nacl.secret.SecretBox.NONCE_SIZE) return header + box.encrypt(bytes(data), nonce).ciphertext + nonce def _encrypt_xsalsa20_poly1305_lite(self, header: bytes, data) -> bytes: + # Deprecated, remove in 2.7 box = nacl.secret.SecretBox(bytes(self.secret_key)) nonce = bytearray(24) @@ -597,7 +601,22 @@ def _encrypt_xsalsa20_poly1305_lite(self, header: bytes, data) -> bytes: return header + box.encrypt(bytes(data), bytes(nonce)).ciphertext + nonce[:4] + def _encrypt_aead_xchacha20_poly1305_rtpsize(self, header: bytes, data) -> bytes: + # Required as of Nov 18 2024 + box = nacl.secret.Aead(bytes(self.secret_key)) + nonce = bytearray(24) + + nonce[:4] = struct.pack(">I", self._lite_nonce) + self.checked_add("_lite_nonce", 1, 4294967295) + + return ( + header + + box.encrypt(bytes(data), bytes(header), bytes(nonce)).ciphertext + + nonce[:4] + ) + def _decrypt_xsalsa20_poly1305(self, header, data): + # Deprecated, remove in 2.7 box = nacl.secret.SecretBox(bytes(self.secret_key)) nonce = bytearray(24) @@ -606,6 +625,7 @@ def _decrypt_xsalsa20_poly1305(self, header, data): return self.strip_header_ext(box.decrypt(bytes(data), bytes(nonce))) def _decrypt_xsalsa20_poly1305_suffix(self, header, data): + # Deprecated, remove in 2.7 box = nacl.secret.SecretBox(bytes(self.secret_key)) nonce_size = nacl.secret.SecretBox.NONCE_SIZE @@ -614,6 +634,7 @@ def _decrypt_xsalsa20_poly1305_suffix(self, header, data): return self.strip_header_ext(box.decrypt(bytes(data[:-nonce_size]), nonce)) def _decrypt_xsalsa20_poly1305_lite(self, header, data): + # Deprecated, remove in 2.7 box = nacl.secret.SecretBox(bytes(self.secret_key)) nonce = bytearray(24) @@ -622,6 +643,18 @@ def _decrypt_xsalsa20_poly1305_lite(self, header, data): return self.strip_header_ext(box.decrypt(bytes(data), bytes(nonce))) + def _decrypt_aead_xchacha20_poly1305_rtpsize(self, header, data): + # Required as of Nov 18 2024 + box = nacl.secret.Aead(bytes(self.secret_key)) + + nonce = bytearray(24) + nonce[:4] = data[-4:] + data = data[:-4] + + return self.strip_header_ext( + box.decrypt(bytes(data), bytes(header), bytes(nonce)) + ) + @staticmethod def strip_header_ext(data): if len(data) > 4 and data[0] == 0xBE and data[1] == 0xDE: @@ -740,11 +773,12 @@ def unpack_audio(self, data): data: :class:`bytes` Bytes received by Discord via the UDP connection used for sending and receiving voice data. """ - if 200 <= data[1] <= 204: - # RTCP received. - # RTCP provides information about the connection - # as opposed to actual audio data, so it's not - # important at the moment. + if data[1] != 0x78: + # We Should Ignore Any Payload Types We Do Not Understand + # Ref RFC 3550 5.1 payload type + # At Some Point We Noted That We Should Ignore Only Types 200 - 204 inclusive. + # They Were Marked As RTCP: Provides Information About The Connection + # This Was Too Broad Of A Whitelist, It Is Unclear If This Is Too Narrow Of A Whitelist return if self.paused: return