Skip to content

Commit

Permalink
Implement new voice encryption modes
Browse files Browse the repository at this point in the history
The new encryption modes are aead_aes256_gcm_rtpsize and
aead_xchacha20_poly1305_rtpsize.

XSalsa20 Poly1305 encryption is deprecated for Discord voice connections
and will be discontinued as of November 18th, 2024.

See https://discord.com/developers/docs/change-log#voice-encryption-modes
  • Loading branch information
lukellmann committed Nov 1, 2024
1 parent 32fb994 commit eed1131
Show file tree
Hide file tree
Showing 27 changed files with 1,210 additions and 66 deletions.
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ kord-cache = "0.5.4" # https://github.com/kordlib/cache
# implementation dependencies
kotlin-logging = "7.0.0" # https://github.com/oshai/kotlin-logging
slf4j = "2.0.16" # https://www.slf4j.org
tink = "1.15.0" # https://github.com/tink-crypto/tink-java
kotlin-node = "22.5.4-pre.818" # https://github.com/JetBrains/kotlin-wrappers
bignum = "0.3.10" # https://github.com/ionspin/kotlin-multiplatform-bignum
stately = "2.1.0" # https://github.com/touchlab/Stately
Expand Down Expand Up @@ -58,6 +59,7 @@ kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.
# other
kotlin-logging = { module = "io.github.oshai:kotlin-logging", version.ref = "kotlin-logging" }
slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
tink = { module = "com.google.crypto.tink:tink", version.ref = "tink" }
kotlin-node = { module = "org.jetbrains.kotlin-wrappers:kotlin-node", version.ref = "kotlin-node" }

# JDK replacements
Expand Down
46 changes: 45 additions & 1 deletion voice/api/voice.api
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,14 @@ public abstract class dev/kord/voice/EncryptionMode {
public final fun toString ()Ljava/lang/String;
}

public final class dev/kord/voice/EncryptionMode$AeadAes256GcmRtpSize : dev/kord/voice/EncryptionMode {
public static final field INSTANCE Ldev/kord/voice/EncryptionMode$AeadAes256GcmRtpSize;
}

public final class dev/kord/voice/EncryptionMode$AeadXChaCha20Poly1305RtpSize : dev/kord/voice/EncryptionMode {
public static final field INSTANCE Ldev/kord/voice/EncryptionMode$AeadXChaCha20Poly1305RtpSize;
}

public final class dev/kord/voice/EncryptionMode$Companion {
public final fun from (Ljava/lang/String;)Ldev/kord/voice/EncryptionMode;
public final fun getEntries ()Ljava/util/List;
Expand Down Expand Up @@ -317,6 +325,7 @@ public final class dev/kord/voice/SpeakingFlags$Companion {
}

public final class dev/kord/voice/VoiceConnection {
public synthetic fun <init> (Ldev/kord/voice/VoiceConnectionData;Ldev/kord/gateway/Gateway;Ldev/kord/voice/gateway/VoiceGateway;Ldev/kord/voice/udp/VoiceUdpSocket;Ldev/kord/voice/gateway/VoiceGatewayConfiguration;Ldev/kord/voice/streams/Streams;Ldev/kord/voice/AudioProvider;Ldev/kord/voice/FrameInterceptor;Ldev/kord/voice/udp/AudioFrameSender;JLkotlin/jvm/internal/DefaultConstructorMarker;)V
public synthetic fun <init> (Ldev/kord/voice/VoiceConnectionData;Ldev/kord/gateway/Gateway;Ldev/kord/voice/gateway/VoiceGateway;Ldev/kord/voice/udp/VoiceUdpSocket;Ldev/kord/voice/gateway/VoiceGatewayConfiguration;Ldev/kord/voice/streams/Streams;Ldev/kord/voice/AudioProvider;Ldev/kord/voice/FrameInterceptor;Ldev/kord/voice/udp/AudioFrameSender;Ldev/kord/voice/encryption/strategies/NonceStrategy;JLkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun connect (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun connect$default (Ldev/kord/voice/VoiceConnection;Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
Expand Down Expand Up @@ -1061,13 +1070,17 @@ public final class dev/kord/voice/io/ReadableByteArrayCursor {
}

public final class dev/kord/voice/streams/DefaultStreams : dev/kord/voice/streams/Streams {
public fun <init> (Ldev/kord/voice/gateway/VoiceGateway;Ldev/kord/voice/udp/VoiceUdpSocket;)V
public fun <init> (Ldev/kord/voice/gateway/VoiceGateway;Ldev/kord/voice/udp/VoiceUdpSocket;Ldev/kord/voice/encryption/strategies/NonceStrategy;)V
public fun getIncomingAudioFrames ()Lkotlinx/coroutines/flow/Flow;
public synthetic fun getIncomingAudioPackets ()Lkotlinx/coroutines/flow/Flow;
public fun getIncomingAudioPackets ()Lkotlinx/coroutines/flow/SharedFlow;
public synthetic fun getIncomingUserStreams ()Lkotlinx/coroutines/flow/Flow;
public fun getIncomingUserStreams ()Lkotlinx/coroutines/flow/SharedFlow;
public synthetic fun getIncomingVoicePackets ()Lkotlinx/coroutines/flow/Flow;
public fun getIncomingVoicePackets ()Lkotlinx/coroutines/flow/SharedFlow;
public fun getSsrcToUser ()Ljava/util/Map;
public fun listen ([BLio/ktor/network/sockets/SocketAddress;Ldev/kord/voice/EncryptionMode;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun listen ([BLio/ktor/network/sockets/SocketAddress;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

Expand All @@ -1076,15 +1089,19 @@ public final class dev/kord/voice/streams/NOPStreams : dev/kord/voice/streams/St
public fun getIncomingAudioFrames ()Lkotlinx/coroutines/flow/Flow;
public fun getIncomingAudioPackets ()Lkotlinx/coroutines/flow/Flow;
public fun getIncomingUserStreams ()Lkotlinx/coroutines/flow/Flow;
public fun getIncomingVoicePackets ()Lkotlinx/coroutines/flow/Flow;
public fun getSsrcToUser ()Ljava/util/Map;
public fun listen ([BLio/ktor/network/sockets/SocketAddress;Ldev/kord/voice/EncryptionMode;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun listen ([BLio/ktor/network/sockets/SocketAddress;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public abstract interface class dev/kord/voice/streams/Streams {
public abstract fun getIncomingAudioFrames ()Lkotlinx/coroutines/flow/Flow;
public abstract fun getIncomingAudioPackets ()Lkotlinx/coroutines/flow/Flow;
public abstract fun getIncomingUserStreams ()Lkotlinx/coroutines/flow/Flow;
public abstract fun getIncomingVoicePackets ()Lkotlinx/coroutines/flow/Flow;
public abstract fun getSsrcToUser ()Ljava/util/Map;
public abstract fun listen ([BLio/ktor/network/sockets/SocketAddress;Ldev/kord/voice/EncryptionMode;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun listen ([BLio/ktor/network/sockets/SocketAddress;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

Expand All @@ -1093,14 +1110,19 @@ public abstract interface class dev/kord/voice/udp/AudioFrameSender {
}

public final class dev/kord/voice/udp/AudioFrameSenderConfiguration {
public synthetic fun <init> (Lio/ktor/network/sockets/SocketAddress;I[BLdev/kord/voice/FrameInterceptorConfiguration;Ldev/kord/voice/EncryptionMode;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
public synthetic fun <init> (Lio/ktor/network/sockets/SocketAddress;I[BLdev/kord/voice/FrameInterceptorConfiguration;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Lio/ktor/network/sockets/SocketAddress;
public final fun component2-pVg5ArA ()I
public final fun component3 ()[B
public final fun component4 ()Ldev/kord/voice/FrameInterceptorConfiguration;
public final fun copy-Yuhug_o (Lio/ktor/network/sockets/SocketAddress;I[BLdev/kord/voice/FrameInterceptorConfiguration;)Ldev/kord/voice/udp/AudioFrameSenderConfiguration;
public final fun component5 ()Ldev/kord/voice/EncryptionMode;
public final synthetic fun copy-Yuhug_o (Lio/ktor/network/sockets/SocketAddress;I[BLdev/kord/voice/FrameInterceptorConfiguration;)Ldev/kord/voice/udp/AudioFrameSenderConfiguration;
public static synthetic fun copy-Yuhug_o$default (Ldev/kord/voice/udp/AudioFrameSenderConfiguration;Lio/ktor/network/sockets/SocketAddress;I[BLdev/kord/voice/FrameInterceptorConfiguration;ILjava/lang/Object;)Ldev/kord/voice/udp/AudioFrameSenderConfiguration;
public final fun copy-roUYKiI (Lio/ktor/network/sockets/SocketAddress;I[BLdev/kord/voice/FrameInterceptorConfiguration;Ldev/kord/voice/EncryptionMode;)Ldev/kord/voice/udp/AudioFrameSenderConfiguration;
public static synthetic fun copy-roUYKiI$default (Ldev/kord/voice/udp/AudioFrameSenderConfiguration;Lio/ktor/network/sockets/SocketAddress;I[BLdev/kord/voice/FrameInterceptorConfiguration;Ldev/kord/voice/EncryptionMode;ILjava/lang/Object;)Ldev/kord/voice/udp/AudioFrameSenderConfiguration;
public fun equals (Ljava/lang/Object;)Z
public final fun getEncryptionMode ()Ldev/kord/voice/EncryptionMode;
public final fun getInterceptorConfiguration ()Ldev/kord/voice/FrameInterceptorConfiguration;
public final fun getKey ()[B
public final fun getServer ()Lio/ktor/network/sockets/SocketAddress;
Expand All @@ -1110,25 +1132,46 @@ public final class dev/kord/voice/udp/AudioFrameSenderConfiguration {
}

public abstract class dev/kord/voice/udp/AudioPacketProvider {
public fun <init> ([B)V
public fun <init> ([BLdev/kord/voice/encryption/strategies/NonceStrategy;)V
public final fun getKey ()[B
public final fun getNonceStrategy ()Ldev/kord/voice/encryption/strategies/NonceStrategy;
public abstract fun provide-jfaDVJw (SII[B)Ldev/kord/voice/io/ByteArrayView;
}

public final class dev/kord/voice/udp/DecryptedVoicePacket {
public synthetic fun <init> (SIILdev/kord/voice/udp/DecryptedVoicePacket$HeaderExtension;[BLkotlin/jvm/internal/DefaultConstructorMarker;)V
public synthetic fun <init> (SII[ILdev/kord/voice/udp/DecryptedVoicePacket$HeaderExtension;[BLkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getCsrcs--hP7Qyg ()[I
public final fun getDecryptedAudio ()[B
public final fun getHeaderExtension ()Ldev/kord/voice/udp/DecryptedVoicePacket$HeaderExtension;
public final fun getSequenceNumber-Mh2AYeg ()S
public final fun getSsrc-pVg5ArA ()I
public final fun getTimestamp-pVg5ArA ()I
}

public final class dev/kord/voice/udp/DecryptedVoicePacket$HeaderExtension {
public synthetic fun <init> (S[ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getDefinedByProfile-Mh2AYeg ()S
public final fun getHeaderExtension--hP7Qyg ()[I
}

public final class dev/kord/voice/udp/DefaultAudioFrameSender : dev/kord/voice/udp/AudioFrameSender {
public fun <init> (Ldev/kord/voice/udp/DefaultAudioFrameSenderData;)V
public final fun getData ()Ldev/kord/voice/udp/DefaultAudioFrameSenderData;
public fun start (Ldev/kord/voice/udp/AudioFrameSenderConfiguration;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public final class dev/kord/voice/udp/DefaultAudioFrameSenderData {
public fun <init> (Ldev/kord/voice/udp/VoiceUdpSocket;Ldev/kord/voice/FrameInterceptor;Ldev/kord/voice/AudioProvider;)V
public fun <init> (Ldev/kord/voice/udp/VoiceUdpSocket;Ldev/kord/voice/FrameInterceptor;Ldev/kord/voice/AudioProvider;Ldev/kord/voice/encryption/strategies/NonceStrategy;)V
public final fun component1 ()Ldev/kord/voice/udp/VoiceUdpSocket;
public final fun component2 ()Ldev/kord/voice/FrameInterceptor;
public final fun component3 ()Ldev/kord/voice/AudioProvider;
public final fun component4 ()Ldev/kord/voice/encryption/strategies/NonceStrategy;
public final fun copy (Ldev/kord/voice/udp/VoiceUdpSocket;Ldev/kord/voice/FrameInterceptor;Ldev/kord/voice/AudioProvider;)Ldev/kord/voice/udp/DefaultAudioFrameSenderData;
public final fun copy (Ldev/kord/voice/udp/VoiceUdpSocket;Ldev/kord/voice/FrameInterceptor;Ldev/kord/voice/AudioProvider;Ldev/kord/voice/encryption/strategies/NonceStrategy;)Ldev/kord/voice/udp/DefaultAudioFrameSenderData;
public static synthetic fun copy$default (Ldev/kord/voice/udp/DefaultAudioFrameSenderData;Ldev/kord/voice/udp/VoiceUdpSocket;Ldev/kord/voice/FrameInterceptor;Ldev/kord/voice/AudioProvider;ILjava/lang/Object;)Ldev/kord/voice/udp/DefaultAudioFrameSenderData;
public static synthetic fun copy$default (Ldev/kord/voice/udp/DefaultAudioFrameSenderData;Ldev/kord/voice/udp/VoiceUdpSocket;Ldev/kord/voice/FrameInterceptor;Ldev/kord/voice/AudioProvider;Ldev/kord/voice/encryption/strategies/NonceStrategy;ILjava/lang/Object;)Ldev/kord/voice/udp/DefaultAudioFrameSenderData;
public fun equals (Ljava/lang/Object;)Z
public final fun getInterceptor ()Ldev/kord/voice/FrameInterceptor;
Expand All @@ -1140,6 +1183,7 @@ public final class dev/kord/voice/udp/DefaultAudioFrameSenderData {
}

public final class dev/kord/voice/udp/DefaultAudioPacketProvider : dev/kord/voice/udp/AudioPacketProvider {
public fun <init> ([BLdev/kord/voice/EncryptionMode;)V
public fun <init> ([BLdev/kord/voice/encryption/strategies/NonceStrategy;)V
public fun provide-jfaDVJw (SII[B)Ldev/kord/voice/io/ByteArrayView;
}
Expand Down
2 changes: 2 additions & 0 deletions voice/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ dependencies {
implementation(libs.kotlin.logging)
implementation(libs.slf4j.api)

implementation(libs.tink)

compileOnly(projects.kspAnnotations)

api(libs.ktor.network)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import kotlinx.serialization.encoding.Encoder

/**
* See [EncryptionMode]s in the
* [Discord Developer Documentation](https://discord.com/developers/docs/topics/voice-connections#establishing-a-voice-udp-connection-encryption-modes).
* [Discord Developer Documentation](https://discord.com/developers/docs/topics/voice-connections#transport-encryption-modes).
*/
@Serializable(with = EncryptionMode.Serializer::class)
public sealed class EncryptionMode(
Expand All @@ -42,10 +42,32 @@ public sealed class EncryptionMode(
`value`: String,
) : EncryptionMode(value)

public object AeadAes256GcmRtpSize : EncryptionMode("aead_aes256_gcm_rtpsize")

public object AeadXChaCha20Poly1305RtpSize : EncryptionMode("aead_xchacha20_poly1305_rtpsize")

@Deprecated(
message =
"Use 'EncryptionMode.from(\"xsalsa20_poly1305\")' if you need to keep using this deprecated 'EncryptionMode'. XSalsa20 Poly1305 encryption is deprecated for Discord voice connections and will be discontinued as of November 18th, 2024. As of this date, the voice gateway will not allow you to connect with one of the deprecated encryption modes. See https://discord.com/developers/docs/change-log#voice-encryption-modes for details. The deprecation level will be raised to ERROR in 0.17.0, to HIDDEN in 0.18.0, and this object will be removed in 0.19.0.",
replaceWith = ReplaceWith(expression = "EncryptionMode.from(\"xsalsa20_poly1305\")", imports
= arrayOf("dev.kord.voice.EncryptionMode")),
)
public object XSalsa20Poly1305 : EncryptionMode("xsalsa20_poly1305")

@Deprecated(
message =
"Use 'EncryptionMode.from(\"xsalsa20_poly1305_suffix\")' if you need to keep using this deprecated 'EncryptionMode'. XSalsa20 Poly1305 encryption is deprecated for Discord voice connections and will be discontinued as of November 18th, 2024. As of this date, the voice gateway will not allow you to connect with one of the deprecated encryption modes. See https://discord.com/developers/docs/change-log#voice-encryption-modes for details. The deprecation level will be raised to ERROR in 0.17.0, to HIDDEN in 0.18.0, and this object will be removed in 0.19.0.",
replaceWith = ReplaceWith(expression = "EncryptionMode.from(\"xsalsa20_poly1305_suffix\")",
imports = arrayOf("dev.kord.voice.EncryptionMode")),
)
public object XSalsa20Poly1305Suffix : EncryptionMode("xsalsa20_poly1305_suffix")

@Deprecated(
message =
"Use 'EncryptionMode.from(\"xsalsa20_poly1305_lite\")' if you need to keep using this deprecated 'EncryptionMode'. XSalsa20 Poly1305 encryption is deprecated for Discord voice connections and will be discontinued as of November 18th, 2024. As of this date, the voice gateway will not allow you to connect with one of the deprecated encryption modes. See https://discord.com/developers/docs/change-log#voice-encryption-modes for details. The deprecation level will be raised to ERROR in 0.17.0, to HIDDEN in 0.18.0, and this object will be removed in 0.19.0.",
replaceWith = ReplaceWith(expression = "EncryptionMode.from(\"xsalsa20_poly1305_lite\")",
imports = arrayOf("dev.kord.voice.EncryptionMode")),
)
public object XSalsa20Poly1305Lite : EncryptionMode("xsalsa20_poly1305_lite")

internal object Serializer : KSerializer<EncryptionMode> {
Expand All @@ -65,9 +87,11 @@ public sealed class EncryptionMode(
*/
public val entries: List<EncryptionMode> by lazy(mode = PUBLICATION) {
listOf(
XSalsa20Poly1305,
XSalsa20Poly1305Suffix,
XSalsa20Poly1305Lite,
AeadAes256GcmRtpSize,
AeadXChaCha20Poly1305RtpSize,
@Suppress("DEPRECATION") XSalsa20Poly1305,
@Suppress("DEPRECATION") XSalsa20Poly1305Suffix,
@Suppress("DEPRECATION") XSalsa20Poly1305Lite,
)
}

Expand All @@ -76,9 +100,11 @@ public sealed class EncryptionMode(
* specified [value].
*/
public fun from(`value`: String): EncryptionMode = when (value) {
"xsalsa20_poly1305" -> XSalsa20Poly1305
"xsalsa20_poly1305_suffix" -> XSalsa20Poly1305Suffix
"xsalsa20_poly1305_lite" -> XSalsa20Poly1305Lite
"aead_aes256_gcm_rtpsize" -> AeadAes256GcmRtpSize
"aead_xchacha20_poly1305_rtpsize" -> AeadXChaCha20Poly1305RtpSize
"xsalsa20_poly1305" -> @Suppress("DEPRECATION") XSalsa20Poly1305
"xsalsa20_poly1305_suffix" -> @Suppress("DEPRECATION") XSalsa20Poly1305Suffix
"xsalsa20_poly1305_lite" -> @Suppress("DEPRECATION") XSalsa20Poly1305Lite
else -> Unknown(value)
}
}
Expand Down
17 changes: 17 additions & 0 deletions voice/src/main/java/com/iwebpp/crypto/TweetNaclFast.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,23 @@
* @description
* TweetNacl.c Java porting
* */
/**
* @deprecated
* This class provides XSalsa20 Poly1305 encryption and is no longer used by default by Kord. If you need an
* implementation of TweetNaCl, provide your own instead. XSalsa20 Poly1305 encryption is deprecated for Discord voice
* connections and will be discontinued as of November 18th, 2024. As of this date, the voice gateway will not allow you
* to connect with one of the deprecated encryption modes. See
* <a href="https://discord.com/developers/docs/change-log#voice-encryption-modes">
* https://discord.com/developers/docs/change-log#voice-encryption-modes</a> for details. This class will be removed in
* 0.19.0.
*/
@Deprecated
@kotlin.Deprecated(
message = "This class provides XSalsa20 Poly1305 encryption and is no longer used by default by Kord. If you " +
"need an implementation of TweetNaCl, provide your own instead. " +
dev.kord.voice.VoiceConnectionKt.XSalsa20_CLASS_DEPRECATION,
level = kotlin.DeprecationLevel.WARNING
)
public final class TweetNaclFast {

private final static String TAG = "TweetNaclFast";
Expand Down
Loading

0 comments on commit eed1131

Please sign in to comment.