Skip to content

Commit 8e2185c

Browse files
NeloBlivionpre-commit-ci[bot]IcebluewolfPaillat-dev
authored
fix: support new voice encryption modes (#2651)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Ice Wolfy <[email protected]> Co-authored-by: Paillat <[email protected]>
1 parent b46c81a commit 8e2185c

File tree

3 files changed

+58
-10
lines changed

3 files changed

+58
-10
lines changed

discord/sinks/core.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,11 +107,22 @@ def __init__(self, data, client):
107107
self.data = bytearray(data)
108108
self.client = client
109109

110-
self.header = data[:12]
111-
self.data = self.data[12:]
112-
113110
unpacker = struct.Struct(">xxHII")
114-
self.sequence, self.timestamp, self.ssrc = unpacker.unpack_from(self.header)
111+
self.sequence, self.timestamp, self.ssrc = unpacker.unpack_from(self.data[:12])
112+
113+
# RFC3550 5.1: RTP Fixed Header Fields
114+
if self.client.mode.endswith("_rtpsize"):
115+
# If It Has CSRC Chunks
116+
cutoff = 12 + (data[0] & 0b00_0_0_1111) * 4
117+
# If It Has A Extension
118+
if data[0] & 0b00_0_1_0000:
119+
cutoff += 4
120+
else:
121+
cutoff = 12
122+
123+
self.header = data[:cutoff]
124+
self.data = self.data[cutoff:]
125+
115126
self.decrypted_data = getattr(self.client, f"_decrypt_{self.client.mode}")(
116127
self.header, self.data
117128
)

discord/types/voice.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,10 @@
3333
from .snowflake import Snowflake
3434

3535
SupportedModes = Literal[
36-
"xsalsa20_poly1305_lite", "xsalsa20_poly1305_suffix", "xsalsa20_poly1305"
36+
"xsalsa20_poly1305_lite",
37+
"xsalsa20_poly1305_suffix",
38+
"xsalsa20_poly1305",
39+
"aead_xchacha20_poly1305_rtpsize",
3740
]
3841

3942

discord/voice_client.py

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ def __init__(self, client: Client, channel: abc.Connectable):
271271
"xsalsa20_poly1305_lite",
272272
"xsalsa20_poly1305_suffix",
273273
"xsalsa20_poly1305",
274+
"aead_xchacha20_poly1305_rtpsize",
274275
)
275276

276277
@property
@@ -576,19 +577,22 @@ def _get_voice_packet(self, data):
576577
return encrypt_packet(header, data)
577578

578579
def _encrypt_xsalsa20_poly1305(self, header: bytes, data) -> bytes:
580+
# Deprecated, remove in 2.7
579581
box = nacl.secret.SecretBox(bytes(self.secret_key))
580582
nonce = bytearray(24)
581583
nonce[:12] = header
582584

583585
return header + box.encrypt(bytes(data), bytes(nonce)).ciphertext
584586

585587
def _encrypt_xsalsa20_poly1305_suffix(self, header: bytes, data) -> bytes:
588+
# Deprecated, remove in 2.7
586589
box = nacl.secret.SecretBox(bytes(self.secret_key))
587590
nonce = nacl.utils.random(nacl.secret.SecretBox.NONCE_SIZE)
588591

589592
return header + box.encrypt(bytes(data), nonce).ciphertext + nonce
590593

591594
def _encrypt_xsalsa20_poly1305_lite(self, header: bytes, data) -> bytes:
595+
# Deprecated, remove in 2.7
592596
box = nacl.secret.SecretBox(bytes(self.secret_key))
593597
nonce = bytearray(24)
594598

@@ -597,7 +601,22 @@ def _encrypt_xsalsa20_poly1305_lite(self, header: bytes, data) -> bytes:
597601

598602
return header + box.encrypt(bytes(data), bytes(nonce)).ciphertext + nonce[:4]
599603

604+
def _encrypt_aead_xchacha20_poly1305_rtpsize(self, header: bytes, data) -> bytes:
605+
# Required as of Nov 18 2024
606+
box = nacl.secret.Aead(bytes(self.secret_key))
607+
nonce = bytearray(24)
608+
609+
nonce[:4] = struct.pack(">I", self._lite_nonce)
610+
self.checked_add("_lite_nonce", 1, 4294967295)
611+
612+
return (
613+
header
614+
+ box.encrypt(bytes(data), bytes(header), bytes(nonce)).ciphertext
615+
+ nonce[:4]
616+
)
617+
600618
def _decrypt_xsalsa20_poly1305(self, header, data):
619+
# Deprecated, remove in 2.7
601620
box = nacl.secret.SecretBox(bytes(self.secret_key))
602621

603622
nonce = bytearray(24)
@@ -606,6 +625,7 @@ def _decrypt_xsalsa20_poly1305(self, header, data):
606625
return self.strip_header_ext(box.decrypt(bytes(data), bytes(nonce)))
607626

608627
def _decrypt_xsalsa20_poly1305_suffix(self, header, data):
628+
# Deprecated, remove in 2.7
609629
box = nacl.secret.SecretBox(bytes(self.secret_key))
610630

611631
nonce_size = nacl.secret.SecretBox.NONCE_SIZE
@@ -614,6 +634,7 @@ def _decrypt_xsalsa20_poly1305_suffix(self, header, data):
614634
return self.strip_header_ext(box.decrypt(bytes(data[:-nonce_size]), nonce))
615635

616636
def _decrypt_xsalsa20_poly1305_lite(self, header, data):
637+
# Deprecated, remove in 2.7
617638
box = nacl.secret.SecretBox(bytes(self.secret_key))
618639

619640
nonce = bytearray(24)
@@ -622,6 +643,18 @@ def _decrypt_xsalsa20_poly1305_lite(self, header, data):
622643

623644
return self.strip_header_ext(box.decrypt(bytes(data), bytes(nonce)))
624645

646+
def _decrypt_aead_xchacha20_poly1305_rtpsize(self, header, data):
647+
# Required as of Nov 18 2024
648+
box = nacl.secret.Aead(bytes(self.secret_key))
649+
650+
nonce = bytearray(24)
651+
nonce[:4] = data[-4:]
652+
data = data[:-4]
653+
654+
return self.strip_header_ext(
655+
box.decrypt(bytes(data), bytes(header), bytes(nonce))
656+
)
657+
625658
@staticmethod
626659
def strip_header_ext(data):
627660
if len(data) > 4 and data[0] == 0xBE and data[1] == 0xDE:
@@ -740,11 +773,12 @@ def unpack_audio(self, data):
740773
data: :class:`bytes`
741774
Bytes received by Discord via the UDP connection used for sending and receiving voice data.
742775
"""
743-
if 200 <= data[1] <= 204:
744-
# RTCP received.
745-
# RTCP provides information about the connection
746-
# as opposed to actual audio data, so it's not
747-
# important at the moment.
776+
if data[1] != 0x78:
777+
# We Should Ignore Any Payload Types We Do Not Understand
778+
# Ref RFC 3550 5.1 payload type
779+
# At Some Point We Noted That We Should Ignore Only Types 200 - 204 inclusive.
780+
# They Were Marked As RTCP: Provides Information About The Connection
781+
# This Was Too Broad Of A Whitelist, It Is Unclear If This Is Too Narrow Of A Whitelist
748782
return
749783
if self.paused:
750784
return

0 commit comments

Comments
 (0)