From b22f710b35bd50c9e34129f0dac20dbbb4faea4b Mon Sep 17 00:00:00 2001 From: viztea Date: Tue, 12 Dec 2023 03:35:44 -0800 Subject: [PATCH 01/12] feat(voice): overhaul encryption + implement aes --- .../kotlin/dev/kord/voice/EncryptionMode.kt | 8 + voice/src/main/kotlin/EncryptionMode.kt | 4 +- voice/src/main/kotlin/VoiceConnection.kt | 3 +- .../src/main/kotlin/VoiceConnectionBuilder.kt | 21 ++- .../main/kotlin/encryption/VoiceEncryption.kt | 163 ++++++++++++++++++ .../encryption/XSalsa20Poly1305Codec.kt | 11 +- .../strategies/LiteNonceStrategy.kt | 10 +- .../encryption/strategies/NonceStrategy.kt | 19 +- .../strategies/NormalNonceStrategy.kt | 12 +- .../strategies/SuffixNonceStrategy.kt | 22 ++- .../kotlin/handlers/UdpLifeCycleHandler.kt | 14 +- .../src/main/kotlin/streams/DefaultStreams.kt | 30 ++-- .../main/kotlin/udp/AudioPacketProvider.kt | 21 +-- .../kotlin/udp/DefaultAudioFrameSender.kt | 5 +- 14 files changed, 268 insertions(+), 75 deletions(-) create mode 100644 voice/src/main/kotlin/encryption/VoiceEncryption.kt diff --git a/voice/build/generated/ksp/main/kotlin/dev/kord/voice/EncryptionMode.kt b/voice/build/generated/ksp/main/kotlin/dev/kord/voice/EncryptionMode.kt index 62711149222..36d5cbb3f8c 100644 --- a/voice/build/generated/ksp/main/kotlin/dev/kord/voice/EncryptionMode.kt +++ b/voice/build/generated/ksp/main/kotlin/dev/kord/voice/EncryptionMode.kt @@ -52,6 +52,10 @@ public sealed class EncryptionMode( public constructor(`value`: String) : this(value, null) } + public object AeadAes256Gcm : EncryptionMode("aead_aes256_gcm") + + public object AeadAes128GcmRtpSize : EncryptionMode("aead_aes128_gcm_rtpsize") + public object XSalsa20Poly1305 : EncryptionMode("xsalsa20_poly1305") public object XSalsa20Poly1305Suffix : EncryptionMode("xsalsa20_poly1305_suffix") @@ -75,6 +79,8 @@ public sealed class EncryptionMode( */ public val entries: List by lazy(mode = PUBLICATION) { listOf( + AeadAes256Gcm, + AeadAes128GcmRtpSize, XSalsa20Poly1305, XSalsa20Poly1305Suffix, XSalsa20Poly1305Lite, @@ -87,6 +93,8 @@ public sealed class EncryptionMode( * specified [value]. */ public fun from(`value`: String): EncryptionMode = when (value) { + "aead_aes256_gcm" -> AeadAes256Gcm + "aead_aes128_gcm_rtpsize" -> AeadAes128GcmRtpSize "xsalsa20_poly1305" -> XSalsa20Poly1305 "xsalsa20_poly1305_suffix" -> XSalsa20Poly1305Suffix "xsalsa20_poly1305_lite" -> XSalsa20Poly1305Lite diff --git a/voice/src/main/kotlin/EncryptionMode.kt b/voice/src/main/kotlin/EncryptionMode.kt index d634833c4f7..b6ae121181c 100644 --- a/voice/src/main/kotlin/EncryptionMode.kt +++ b/voice/src/main/kotlin/EncryptionMode.kt @@ -2,9 +2,11 @@ STRING_KORD_ENUM, name = "EncryptionMode", docUrl = "https://discord.com/developers/docs/topics/voice-connections#establishing-a-voice-udp-connection-encryption-modes", entries = [ + Entry("AeadAes256Gcm", stringValue = "aead_aes256_gcm"), + Entry("AeadAes128GcmRtpSize", stringValue = "aead_aes128_gcm_rtpsize"), Entry("XSalsa20Poly1305", stringValue = "xsalsa20_poly1305"), Entry("XSalsa20Poly1305Suffix", stringValue = "xsalsa20_poly1305_suffix"), - Entry("XSalsa20Poly1305Lite", stringValue = "xsalsa20_poly1305_lite") + Entry("XSalsa20Poly1305Lite", stringValue = "xsalsa20_poly1305_lite"), ] ) diff --git a/voice/src/main/kotlin/VoiceConnection.kt b/voice/src/main/kotlin/VoiceConnection.kt index c034484c90b..1b1e490cda0 100644 --- a/voice/src/main/kotlin/VoiceConnection.kt +++ b/voice/src/main/kotlin/VoiceConnection.kt @@ -4,6 +4,7 @@ import dev.kord.common.annotation.KordVoice import dev.kord.common.entity.Snowflake import dev.kord.gateway.Gateway import dev.kord.gateway.UpdateVoiceStatus +import dev.kord.voice.encryption.VoiceEncryption import dev.kord.voice.encryption.strategies.NonceStrategy import dev.kord.voice.gateway.VoiceGateway import dev.kord.voice.gateway.VoiceGatewayConfiguration @@ -54,7 +55,7 @@ public class VoiceConnection( public val audioProvider: AudioProvider, public val frameInterceptor: FrameInterceptor, public val frameSender: AudioFrameSender, - public val nonceStrategy: NonceStrategy, + public val encryption: VoiceEncryption, connectionDetachDuration: Duration ) { public val scope: CoroutineScope = diff --git a/voice/src/main/kotlin/VoiceConnectionBuilder.kt b/voice/src/main/kotlin/VoiceConnectionBuilder.kt index 62765a73701..060f04a13e7 100644 --- a/voice/src/main/kotlin/VoiceConnectionBuilder.kt +++ b/voice/src/main/kotlin/VoiceConnectionBuilder.kt @@ -7,8 +7,8 @@ import dev.kord.gateway.Gateway import dev.kord.gateway.UpdateVoiceStatus import dev.kord.gateway.VoiceServerUpdate import dev.kord.gateway.VoiceStateUpdate +import dev.kord.voice.encryption.VoiceEncryption import dev.kord.voice.encryption.strategies.LiteNonceStrategy -import dev.kord.voice.encryption.strategies.NonceStrategy import dev.kord.voice.exception.VoiceConnectionInitializationException import dev.kord.voice.gateway.DefaultVoiceGatewayBuilder import dev.kord.voice.gateway.VoiceGateway @@ -31,7 +31,7 @@ public class VoiceConnectionBuilder( public var gateway: Gateway, public var selfId: Snowflake, public var channelId: Snowflake, - public var guildId: Snowflake + public var guildId: Snowflake, ) { /** * The amount in milliseconds to wait for the events required to create a [VoiceConnection]. Default is 5000, or 5 seconds. @@ -65,9 +65,10 @@ public class VoiceConnectionBuilder( /** * The nonce strategy to be used for the encryption of audio packets. - * If `null`, [dev.kord.voice.encryption.strategies.LiteNonceStrategy] will be used. + * If `null` & voice receive if disabled, [VoiceEncryption.AeadAes256Gcm] will be used, + * otherwise [VoiceEncryption.XSalsaPoly1305] with the Lite strategy will be used. */ - public var nonceStrategy: NonceStrategy? = null + public var encryption: VoiceEncryption? = null /** * A boolean indicating whether your voice state will be muted. @@ -166,7 +167,11 @@ public class VoiceConnectionBuilder( .build() val udpSocket = udpSocket ?: GlobalVoiceUdpSocket val audioProvider = audioProvider ?: EmptyAudioPlayerProvider - val nonceStrategy = nonceStrategy ?: LiteNonceStrategy() + val encryption = if ((receiveVoice || streams != null) && encryption?.supportsDecryption != true) { + VoiceEncryption.XSalsaPoly1305() + } else { + encryption ?: VoiceEncryption.AeadAes256Gcm + } val frameInterceptor = frameInterceptor ?: DefaultFrameInterceptor() val audioSender = audioSender ?: DefaultAudioFrameSender( @@ -174,11 +179,11 @@ public class VoiceConnectionBuilder( udpSocket, frameInterceptor, audioProvider, - nonceStrategy + encryption ) ) val streams = - streams ?: if (receiveVoice) DefaultStreams(voiceGateway, udpSocket, nonceStrategy) else NOPStreams + streams ?: if (receiveVoice) DefaultStreams(voiceGateway, udpSocket, encryption) else NOPStreams return VoiceConnection( voiceConnectionData, @@ -190,7 +195,7 @@ public class VoiceConnectionBuilder( audioProvider, frameInterceptor, audioSender, - nonceStrategy, + encryption, connectionDetachDuration ) } diff --git a/voice/src/main/kotlin/encryption/VoiceEncryption.kt b/voice/src/main/kotlin/encryption/VoiceEncryption.kt new file mode 100644 index 00000000000..7a6d84aecf9 --- /dev/null +++ b/voice/src/main/kotlin/encryption/VoiceEncryption.kt @@ -0,0 +1,163 @@ +package dev.kord.voice.encryption + +import com.iwebpp.crypto.TweetNaclFast +import dev.kord.voice.EncryptionMode +import dev.kord.voice.encryption.strategies.LiteNonceStrategy +import dev.kord.voice.encryption.strategies.NonceStrategy +import dev.kord.voice.io.ByteArrayView +import dev.kord.voice.io.MutableByteArrayCursor +import dev.kord.voice.io.mutableCursor +import dev.kord.voice.io.view +import dev.kord.voice.udp.RTPPacket +import javax.crypto.Cipher +import javax.crypto.spec.GCMParameterSpec +import javax.crypto.spec.SecretKeySpec + +public sealed interface VoiceEncryption { + public val mode: EncryptionMode + + public val nonceLength: Int + + public val supportsDecryption: Boolean get() = true + + public fun createBox(key: ByteArray): Box + + public fun createUnbox(key: ByteArray): Unbox + + @JvmInline + public value class XSalsaPoly1305(public val nsf: NonceStrategy.Factory = LiteNonceStrategy) : VoiceEncryption { + override val mode: EncryptionMode + get() = nsf.mode + + override val nonceLength: Int + get() = 24 + + override fun createBox(key: ByteArray): Box = object : Box { + private val codec: XSalsa20Poly1305Codec = XSalsa20Poly1305Codec(key) + private val nonceStrategy: NonceStrategy = nsf.create() + + override val overhead: Int + get() = TweetNaclFast.SecretBox.boxzerobytesLength + nsf.nonceLength + + override fun encrypt(src: ByteArray, nonce: ByteArray, dst: MutableByteArrayCursor): Boolean { + return codec.encrypt(src, 0, src.size, nonce, dst) + } + + override fun generateNonce(header: () -> ByteArrayView): ByteArrayView { + return nonceStrategy.generate(header) + } + + override fun appendNonce(nonce: ByteArrayView, dst: MutableByteArrayCursor) { + nonceStrategy.append(nonce, dst) + } + } + + override fun createUnbox(key: ByteArray): Unbox = object : Unbox { + private val codec: XSalsa20Poly1305Codec = XSalsa20Poly1305Codec(key) + private val nonceStrategy: NonceStrategy = nsf.create() + + override fun decrypt( + src: ByteArray, + srcOff: Int, + srcLen: Int, + nonce: ByteArray, + dst: MutableByteArrayCursor, + ): Boolean = codec.decrypt(src, srcOff, srcLen, nonce, dst) + + override fun getNonce(packet: RTPPacket): ByteArrayView = nonceStrategy.strip(packet) + } + } + + public data object AeadAes256Gcm : VoiceEncryption { + private const val AUTH_TAG_LEN = 16 + private const val NONCE_LEN = 4 + private const val IV_LEN = 12 + + override val mode: EncryptionMode + get() = EncryptionMode.AeadAes256Gcm + + override val nonceLength: Int + get() = 4 + + override val supportsDecryption: Boolean + get() = false + + override fun createBox(key: ByteArray): Box = object : Box { + // + private val iv = ByteArray(IV_LEN) + private val ivCursor = iv.mutableCursor() + + // + private var nonce = 0u + private val nonceBuffer: ByteArray = ByteArray(NONCE_LEN) + private val nonceCursor = nonceBuffer.mutableCursor() + private val nonceView = nonceBuffer.view() + + // + val secretKey = SecretKeySpec(key, "AES") + val cipher = Cipher.getInstance("AES/GCM/NoPadding") + + override val overhead: Int + get() = AUTH_TAG_LEN + NONCE_LEN + + override fun encrypt(src: ByteArray, nonce: ByteArray, dst: MutableByteArrayCursor): Boolean { + iv.fill(0) + ivCursor.reset() + ivCursor.writeByteArray(nonce) + + cipher.init( + Cipher.ENCRYPT_MODE, secretKey, + GCMParameterSpec(AUTH_TAG_LEN * 8, iv, 0, IV_LEN) + ) + cipher.updateAAD(dst.data.copyOfRange(0, dst.cursor)) + dst.cursor += cipher.doFinal(src, 0, src.size, dst.data, dst.cursor) + + return true + } + + override fun appendNonce(nonce: ByteArrayView, dst: MutableByteArrayCursor) { + dst.writeByteView(nonce) + } + + override fun generateNonce(header: () -> ByteArrayView): ByteArrayView { + nonceCursor.reset() + nonceCursor.writeInt(nonce++.toInt()) + return nonceView + } + } + + override fun createUnbox(key: ByteArray): Unbox { + throw UnsupportedOperationException() + } + } + + public interface Box { + public val overhead: Int + + public fun encrypt(src: ByteArray, nonce: ByteArray, dst: MutableByteArrayCursor): Boolean + + public fun generateNonce(header: () -> ByteArrayView): ByteArrayView + + public fun appendNonce(nonce: ByteArrayView, dst: MutableByteArrayCursor) + } + + public interface Unbox { + /** + * Decrypt the packet. + */ + public fun decrypt( + src: ByteArray, + srcOff: Int, + srcLen: Int, + nonce: ByteArray, + dst: MutableByteArrayCursor, + ): Boolean + + /** + * Strip the nonce from the [RTP packet][packet]. + * + * @return the nonce. + */ + public fun getNonce(packet: RTPPacket): ByteArrayView + } +} \ No newline at end of file diff --git a/voice/src/main/kotlin/encryption/XSalsa20Poly1305Codec.kt b/voice/src/main/kotlin/encryption/XSalsa20Poly1305Codec.kt index 5e1083e2c29..25974dbb67c 100644 --- a/voice/src/main/kotlin/encryption/XSalsa20Poly1305Codec.kt +++ b/voice/src/main/kotlin/encryption/XSalsa20Poly1305Codec.kt @@ -12,7 +12,7 @@ public class XSalsa20Poly1305Codec(public val key: ByteArray) { mOffset: Int = 0, mLength: Int = message.size, nonce: ByteArray, - output: MutableByteArrayCursor + output: MutableByteArrayCursor, ): Boolean = encryption.box(message, mOffset, mLength, nonce, output) @@ -21,16 +21,15 @@ public class XSalsa20Poly1305Codec(public val key: ByteArray) { boxOffset: Int = 0, boxLength: Int = box.size, nonce: ByteArray, - output: MutableByteArrayCursor - ): Boolean = - encryption.open(box, boxOffset, boxLength, nonce, output) + output: MutableByteArrayCursor, + ): Boolean = encryption.open(box, boxOffset, boxLength, nonce, output) } public fun XSalsa20Poly1305Codec.encrypt( message: ByteArray, mOffset: Int = 0, mLength: Int = message.size, - nonce: ByteArray + nonce: ByteArray, ): ByteArray? { val buffer = ByteArray(mLength + TweetNaclFast.SecretBox.boxzerobytesLength) if (!encrypt(message, mOffset, mLength, nonce, buffer.mutableCursor())) return null @@ -41,7 +40,7 @@ public fun XSalsa20Poly1305Codec.decrypt( box: ByteArray, boxOffset: Int = 0, boxLength: Int = box.size, - nonce: ByteArray + nonce: ByteArray, ): ByteArray? { val buffer = ByteArray(boxLength - TweetNaclFast.SecretBox.boxzerobytesLength) if (!decrypt(box, boxOffset, boxLength, nonce, buffer.mutableCursor())) return null diff --git a/voice/src/main/kotlin/encryption/strategies/LiteNonceStrategy.kt b/voice/src/main/kotlin/encryption/strategies/LiteNonceStrategy.kt index 1e20812698b..09a9ee54818 100644 --- a/voice/src/main/kotlin/encryption/strategies/LiteNonceStrategy.kt +++ b/voice/src/main/kotlin/encryption/strategies/LiteNonceStrategy.kt @@ -1,5 +1,6 @@ package dev.kord.voice.encryption.strategies +import dev.kord.voice.EncryptionMode import dev.kord.voice.io.ByteArrayView import dev.kord.voice.io.MutableByteArrayCursor import dev.kord.voice.io.mutableCursor @@ -8,8 +9,6 @@ import dev.kord.voice.udp.RTPPacket import kotlinx.atomicfu.atomic public class LiteNonceStrategy : NonceStrategy { - override val nonceLength: Int = 4 - private var count: Int by atomic(0) private val nonceBuffer: ByteArray = ByteArray(4) private val nonceView = nonceBuffer.view() @@ -33,4 +32,11 @@ public class LiteNonceStrategy : NonceStrategy { override fun append(nonce: ByteArrayView, cursor: MutableByteArrayCursor) { cursor.writeByteView(nonce) } + + public companion object Factory : NonceStrategy.Factory { + override val mode: EncryptionMode get() = EncryptionMode.XSalsa20Poly1305Lite + override val nonceLength: Int get() = 4 + + override fun create(): NonceStrategy = LiteNonceStrategy() + } } diff --git a/voice/src/main/kotlin/encryption/strategies/NonceStrategy.kt b/voice/src/main/kotlin/encryption/strategies/NonceStrategy.kt index 97a89e924e2..8144a3ca2e6 100644 --- a/voice/src/main/kotlin/encryption/strategies/NonceStrategy.kt +++ b/voice/src/main/kotlin/encryption/strategies/NonceStrategy.kt @@ -1,5 +1,6 @@ package dev.kord.voice.encryption.strategies +import dev.kord.voice.EncryptionMode import dev.kord.voice.io.ByteArrayView import dev.kord.voice.io.MutableByteArrayCursor import dev.kord.voice.udp.RTPPacket @@ -8,11 +9,6 @@ import dev.kord.voice.udp.RTPPacket * An [encryption mode, regarding the nonce](https://discord.com/developers/docs/topics/voice-connections#establishing-a-voice-udp-connection-encryption-modes), supported by Discord. */ public sealed interface NonceStrategy { - /** - * The amount of bytes this nonce will take up. - */ - public val nonceLength: Int - /** * Reads the nonce from this [packet] (also removes it if it resides in the payload), and returns a [ByteArrayView] of it. */ @@ -27,4 +23,17 @@ public sealed interface NonceStrategy { * Writes the [nonce] to [cursor] in the correct relative position. */ public fun append(nonce: ByteArrayView, cursor: MutableByteArrayCursor) + + public interface Factory { + /** + */ + public val mode: EncryptionMode + + /** + * The amount of bytes this nonce will take up. + */ + public val nonceLength: Int + + public fun create(): NonceStrategy + } } \ No newline at end of file diff --git a/voice/src/main/kotlin/encryption/strategies/NormalNonceStrategy.kt b/voice/src/main/kotlin/encryption/strategies/NormalNonceStrategy.kt index a01940ffd9b..da57f5ae200 100644 --- a/voice/src/main/kotlin/encryption/strategies/NormalNonceStrategy.kt +++ b/voice/src/main/kotlin/encryption/strategies/NormalNonceStrategy.kt @@ -1,5 +1,6 @@ package dev.kord.voice.encryption.strategies +import dev.kord.voice.EncryptionMode import dev.kord.voice.io.ByteArrayView import dev.kord.voice.io.MutableByteArrayCursor import dev.kord.voice.io.mutableCursor @@ -8,9 +9,6 @@ import dev.kord.voice.udp.RTPPacket import dev.kord.voice.udp.RTP_HEADER_LENGTH public class NormalNonceStrategy : NonceStrategy { - // the nonce is already a part of the rtp header, which means this will take up no extra space. - override val nonceLength: Int = 0 - private val rtpHeaderBuffer = ByteArray(RTP_HEADER_LENGTH) private val rtpHeaderCursor = rtpHeaderBuffer.mutableCursor() private val rtpHeaderView = rtpHeaderBuffer.view() @@ -28,4 +26,12 @@ public class NormalNonceStrategy : NonceStrategy { override fun append(nonce: ByteArrayView, cursor: MutableByteArrayCursor) { /* the nonce is the rtp header which means this should do nothing */ } + + public companion object Factory : NonceStrategy.Factory { + override val nonceLength: Int = 0 + + override val mode: EncryptionMode get() = EncryptionMode.XSalsa20Poly1305 + + override fun create(): NonceStrategy = NormalNonceStrategy() + } } \ No newline at end of file diff --git a/voice/src/main/kotlin/encryption/strategies/SuffixNonceStrategy.kt b/voice/src/main/kotlin/encryption/strategies/SuffixNonceStrategy.kt index a43a73c315d..32a55fbf60e 100644 --- a/voice/src/main/kotlin/encryption/strategies/SuffixNonceStrategy.kt +++ b/voice/src/main/kotlin/encryption/strategies/SuffixNonceStrategy.kt @@ -1,23 +1,21 @@ package dev.kord.voice.encryption.strategies +import com.iwebpp.crypto.TweetNaclFast +import dev.kord.voice.EncryptionMode import dev.kord.voice.io.ByteArrayView import dev.kord.voice.io.MutableByteArrayCursor import dev.kord.voice.io.view import dev.kord.voice.udp.RTPPacket import kotlin.random.Random -private const val SUFFIX_NONCE_LENGTH = 24 - public class SuffixNonceStrategy : NonceStrategy { - override val nonceLength: Int = SUFFIX_NONCE_LENGTH - - private val nonceBuffer: ByteArray = ByteArray(SUFFIX_NONCE_LENGTH) + private val nonceBuffer: ByteArray = ByteArray(nonceLength) private val nonceView = nonceBuffer.view() override fun strip(packet: RTPPacket): ByteArrayView { return with(packet.payload) { - val nonce = view(dataEnd - SUFFIX_NONCE_LENGTH, dataEnd)!! - resize(dataStart, dataEnd - SUFFIX_NONCE_LENGTH) + val nonce = view(dataEnd - nonceLength, dataEnd)!! + resize(dataStart, dataEnd - nonceLength) nonce } } @@ -30,4 +28,12 @@ public class SuffixNonceStrategy : NonceStrategy { override fun append(nonce: ByteArrayView, cursor: MutableByteArrayCursor) { cursor.writeByteView(nonce) } -} \ No newline at end of file + + public companion object Factory : NonceStrategy.Factory { + + override val nonceLength: Int = TweetNaclFast.SecretBox.nonceLength + override val mode: EncryptionMode get() = EncryptionMode.XSalsa20Poly1305Suffix + + override fun create(): NonceStrategy = SuffixNonceStrategy() + } +} diff --git a/voice/src/main/kotlin/handlers/UdpLifeCycleHandler.kt b/voice/src/main/kotlin/handlers/UdpLifeCycleHandler.kt index f0c33017a61..b10fba7e6ca 100644 --- a/voice/src/main/kotlin/handlers/UdpLifeCycleHandler.kt +++ b/voice/src/main/kotlin/handlers/UdpLifeCycleHandler.kt @@ -1,11 +1,7 @@ package dev.kord.voice.handlers -import dev.kord.voice.EncryptionMode import dev.kord.voice.FrameInterceptorConfiguration import dev.kord.voice.VoiceConnection -import dev.kord.voice.encryption.strategies.LiteNonceStrategy -import dev.kord.voice.encryption.strategies.NormalNonceStrategy -import dev.kord.voice.encryption.strategies.SuffixNonceStrategy import dev.kord.voice.gateway.* import dev.kord.voice.udp.AudioFrameSenderConfiguration import io.ktor.network.sockets.* @@ -20,7 +16,7 @@ private val udpLifeCycleLogger = KotlinLogging.logger { } internal class UdpLifeCycleHandler( flow: Flow, - private val connection: VoiceConnection + private val connection: VoiceConnection, ) : ConnectionEventHandler(flow, "UdpInterceptor") { private var ssrc: UInt? by atomic(null) private var server: InetSocketAddress? by atomic(null) @@ -37,18 +33,12 @@ internal class UdpLifeCycleHandler( udpLifeCycleLogger.trace { "ip discovered for voice successfully" } - val encryptionMode = when (connection.nonceStrategy) { - is LiteNonceStrategy -> EncryptionMode.XSalsa20Poly1305Lite - is NormalNonceStrategy -> EncryptionMode.XSalsa20Poly1305 - is SuffixNonceStrategy -> EncryptionMode.XSalsa20Poly1305Suffix - } - val selectProtocol = SelectProtocol( protocol = "udp", data = SelectProtocol.Data( address = ip.hostname, port = ip.port, - mode = encryptionMode + mode = connection.encryption.mode ) ) diff --git a/voice/src/main/kotlin/streams/DefaultStreams.kt b/voice/src/main/kotlin/streams/DefaultStreams.kt index 8a44186c350..072c12dce50 100644 --- a/voice/src/main/kotlin/streams/DefaultStreams.kt +++ b/voice/src/main/kotlin/streams/DefaultStreams.kt @@ -4,8 +4,7 @@ import com.iwebpp.crypto.TweetNaclFast import dev.kord.common.annotation.KordVoice import dev.kord.common.entity.Snowflake import dev.kord.voice.AudioFrame -import dev.kord.voice.encryption.XSalsa20Poly1305Codec -import dev.kord.voice.encryption.strategies.NonceStrategy +import dev.kord.voice.encryption.VoiceEncryption import dev.kord.voice.gateway.Speaking import dev.kord.voice.gateway.VoiceGateway import dev.kord.voice.io.* @@ -28,14 +27,14 @@ private val defaultStreamsLogger = KotlinLogging.logger { } public class DefaultStreams( private val voiceGateway: VoiceGateway, private val udp: VoiceUdpSocket, - private val nonceStrategy: NonceStrategy + private val encryption: VoiceEncryption, ) : Streams { private fun CoroutineScope.listenForIncoming(key: ByteArray, server: SocketAddress) { udp.incoming .filter { it.address == server } .mapNotNull { RTPPacket.fromPacket(it.packet) } .filter { it.payloadType == PayloadType.Audio.raw } - .decrypt(nonceStrategy, key) + .decrypt(encryption, key) .clean() .onEach { _incomingAudioPackets.emit(it) } .launchIn(this) @@ -86,36 +85,37 @@ public class DefaultStreams( override val ssrcToUser: Map get() = _ssrcToUser.value } -private fun Flow.decrypt(nonceStrategy: NonceStrategy, key: ByteArray): Flow { - val codec = XSalsa20Poly1305Codec(key) - val nonceBuffer = ByteArray(TweetNaclFast.SecretBox.nonceLength).mutableCursor() +private fun Flow.decrypt(encryption: VoiceEncryption, key: ByteArray): Flow { + val unbox = encryption.createUnbox(key) + val nonceBuffer = ByteArray(encryption.nonceLength).mutableCursor() val decryptedBuffer = ByteArray(512) val decryptedCursor = decryptedBuffer.mutableCursor() val decryptedView = decryptedBuffer.view() - return mapNotNull { + return mapNotNull { packet -> nonceBuffer.reset() decryptedCursor.reset() - nonceBuffer.writeByteView(nonceStrategy.strip(it)) + nonceBuffer.writeByteView(unbox.getNonce(packet)) - val decrypted = with(it.payload) { - codec.decrypt(data, dataStart, viewSize, nonceBuffer.data, decryptedCursor) + println(packet.payload.data.contentToString()) + val decrypted = with(packet.payload) { + unbox.decrypt(data, dataStart, viewSize, nonceBuffer.data, decryptedCursor) } if (!decrypted) { - defaultStreamsLogger.trace { "failed to decrypt the packet with data ${it.payload.data.contentToString()} at offset ${it.payload.dataStart} and length ${it.payload.viewSize - 4}" } + defaultStreamsLogger.trace { "failed to decrypt the packet with data ${packet.payload.data.contentToString()} at offset ${packet.payload.dataStart} and length ${packet.payload.viewSize - 4}" } return@mapNotNull null } decryptedView.resize(0, decryptedCursor.cursor) // mutate the payload data and update the view - it.payload.data.mutableCursor().writeByteViewOrResize(decryptedView) - it.payload.resize(0, decryptedView.viewSize) + packet.payload.data.mutableCursor().writeByteViewOrResize(decryptedView) + packet.payload.resize(0, decryptedView.viewSize) - it + packet } } diff --git a/voice/src/main/kotlin/udp/AudioPacketProvider.kt b/voice/src/main/kotlin/udp/AudioPacketProvider.kt index 05f2e522d4c..90821c674aa 100644 --- a/voice/src/main/kotlin/udp/AudioPacketProvider.kt +++ b/voice/src/main/kotlin/udp/AudioPacketProvider.kt @@ -1,23 +1,20 @@ package dev.kord.voice.udp -import com.iwebpp.crypto.TweetNaclFast -import dev.kord.voice.encryption.XSalsa20Poly1305Codec -import dev.kord.voice.encryption.strategies.NonceStrategy +import dev.kord.voice.encryption.VoiceEncryption import dev.kord.voice.io.ByteArrayView import dev.kord.voice.io.MutableByteArrayCursor import dev.kord.voice.io.mutableCursor import dev.kord.voice.io.view -public abstract class AudioPacketProvider(public val key: ByteArray, public val nonceStrategy: NonceStrategy) { +public abstract class AudioPacketProvider(public val key: ByteArray, public val encryption: VoiceEncryption) { public abstract fun provide(sequence: UShort, timestamp: UInt, ssrc: UInt, data: ByteArray): ByteArrayView } private class CouldNotEncryptDataException(data: ByteArray) : RuntimeException("Couldn't encrypt the following data: [${data.joinToString(", ")}]") -public class DefaultAudioPacketProvider(key: ByteArray, nonceStrategy: NonceStrategy) : - AudioPacketProvider(key, nonceStrategy) { - private val codec = XSalsa20Poly1305Codec(key) +public class DefaultAudioPacketProvider(key: ByteArray, encryption: VoiceEncryption) : AudioPacketProvider(key, encryption) { + private val box = encryption.createBox(key) private val packetBuffer = ByteArray(2048) private val packetBufferCursor: MutableByteArrayCursor = packetBuffer.mutableCursor() @@ -25,7 +22,7 @@ public class DefaultAudioPacketProvider(key: ByteArray, nonceStrategy: NonceStra private val rtpHeaderView: ByteArrayView = packetBuffer.view(0, RTP_HEADER_LENGTH)!! - private val nonceBuffer: MutableByteArrayCursor = ByteArray(TweetNaclFast.SecretBox.nonceLength).mutableCursor() + private val nonceBuffer: MutableByteArrayCursor = ByteArray(encryption.nonceLength).mutableCursor() private val lock: Any = Any() @@ -44,20 +41,20 @@ public class DefaultAudioPacketProvider(key: ByteArray, nonceStrategy: NonceStra nonceBuffer.reset() // make sure we enough room in this buffer - resize(RTP_HEADER_LENGTH + (data.size + TweetNaclFast.SecretBox.boxzerobytesLength) + nonceStrategy.nonceLength) + resize(RTP_HEADER_LENGTH + data.size + box.overhead) // write header and generate nonce writeHeader(sequence.toShort(), timestamp.toInt(), ssrc.toInt()) - val rawNonce = nonceStrategy.generate { rtpHeaderView } + val rawNonce = box.generateNonce { rtpHeaderView } nonceBuffer.writeByteView(rawNonce) // encrypt data and write into our buffer - val encrypted = codec.encrypt(data, nonce = nonceBuffer.data, output = this) + val encrypted = box.encrypt(data, nonceBuffer.data, this) if (!encrypted) throw CouldNotEncryptDataException(data) - nonceStrategy.append(rawNonce, this) + box.appendNonce(rawNonce, this) // let's make sure we have the correct view of the packet if (!packetBufferView.resize(0, cursor)) error("couldn't resize packet buffer view?!") diff --git a/voice/src/main/kotlin/udp/DefaultAudioFrameSender.kt b/voice/src/main/kotlin/udp/DefaultAudioFrameSender.kt index d713cff99fb..09542112571 100644 --- a/voice/src/main/kotlin/udp/DefaultAudioFrameSender.kt +++ b/voice/src/main/kotlin/udp/DefaultAudioFrameSender.kt @@ -4,6 +4,7 @@ import dev.kord.common.annotation.KordVoice import dev.kord.voice.AudioFrame import dev.kord.voice.AudioProvider import dev.kord.voice.FrameInterceptor +import dev.kord.voice.encryption.VoiceEncryption import dev.kord.voice.encryption.strategies.NonceStrategy import io.ktor.network.sockets.* import io.ktor.utils.io.core.* @@ -21,7 +22,7 @@ public data class DefaultAudioFrameSenderData( val udp: VoiceUdpSocket, val interceptor: FrameInterceptor, val provider: AudioProvider, - val nonceStrategy: NonceStrategy, + val encryption: VoiceEncryption, ) @KordVoice @@ -31,7 +32,7 @@ public class DefaultAudioFrameSender( override suspend fun start(configuration: AudioFrameSenderConfiguration): Unit = coroutineScope { var sequence: UShort = Random.nextBits(UShort.SIZE_BITS).toUShort() - val packetProvider = DefaultAudioPacketProvider(configuration.key, data.nonceStrategy) + val packetProvider = DefaultAudioPacketProvider(configuration.key, data.encryption) val frames = Channel(Channel.RENDEZVOUS) with(data.provider) { launch { provideFrames(frames) } } From a6356cd532e098a2d86d7175482c040b248115d5 Mon Sep 17 00:00:00 2001 From: viztea Date: Tue, 12 Dec 2023 06:01:00 -0800 Subject: [PATCH 02/12] chore(voice): api dump --- voice/api/voice.api | 126 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 109 insertions(+), 17 deletions(-) diff --git a/voice/api/voice.api b/voice/api/voice.api index 186b2bbf5d6..af801bb313b 100644 --- a/voice/api/voice.api +++ b/voice/api/voice.api @@ -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$AeadAes128GcmRtpSize : dev/kord/voice/EncryptionMode { + public static final field INSTANCE Ldev/kord/voice/EncryptionMode$AeadAes128GcmRtpSize; +} + +public final class dev/kord/voice/EncryptionMode$AeadAes256Gcm : dev/kord/voice/EncryptionMode { + public static final field INSTANCE Ldev/kord/voice/EncryptionMode$AeadAes256Gcm; +} + 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; @@ -331,16 +339,16 @@ public final class dev/kord/voice/SpeakingFlags$Companion { } public final class dev/kord/voice/VoiceConnection { - public synthetic fun (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 synthetic fun (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/VoiceEncryption;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; public final fun disconnect (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public final fun getAudioProvider ()Ldev/kord/voice/AudioProvider; public final fun getData ()Ldev/kord/voice/VoiceConnectionData; + public final fun getEncryption ()Ldev/kord/voice/encryption/VoiceEncryption; public final fun getFrameInterceptor ()Ldev/kord/voice/FrameInterceptor; public final fun getFrameSender ()Ldev/kord/voice/udp/AudioFrameSender; public final fun getGateway ()Ldev/kord/gateway/Gateway; - public final fun getNonceStrategy ()Ldev/kord/voice/encryption/strategies/NonceStrategy; public final fun getScope ()Lkotlinx/coroutines/CoroutineScope; public final fun getSocket ()Ldev/kord/voice/udp/VoiceUdpSocket; public final fun getStreams ()Ldev/kord/voice/streams/Streams; @@ -362,10 +370,10 @@ public final class dev/kord/voice/VoiceConnectionBuilder { public final fun getAudioSender ()Ldev/kord/voice/udp/AudioFrameSender; public final fun getChannelId ()Ldev/kord/common/entity/Snowflake; public final fun getConnectionDetachDuration-UwyO8pc ()J + public final fun getEncryption ()Ldev/kord/voice/encryption/VoiceEncryption; public final fun getFrameInterceptor ()Ldev/kord/voice/FrameInterceptor; public final fun getGateway ()Ldev/kord/gateway/Gateway; public final fun getGuildId ()Ldev/kord/common/entity/Snowflake; - public final fun getNonceStrategy ()Ldev/kord/voice/encryption/strategies/NonceStrategy; public final fun getReceiveVoice ()Z public final fun getSelfDeaf ()Z public final fun getSelfId ()Ldev/kord/common/entity/Snowflake; @@ -377,10 +385,10 @@ public final class dev/kord/voice/VoiceConnectionBuilder { public final fun setAudioSender (Ldev/kord/voice/udp/AudioFrameSender;)V public final fun setChannelId (Ldev/kord/common/entity/Snowflake;)V public final fun setConnectionDetachDuration-LRDsOJo (J)V + public final fun setEncryption (Ldev/kord/voice/encryption/VoiceEncryption;)V public final fun setFrameInterceptor (Ldev/kord/voice/FrameInterceptor;)V public final fun setGateway (Ldev/kord/gateway/Gateway;)V public final fun setGuildId (Ldev/kord/common/entity/Snowflake;)V - public final fun setNonceStrategy (Ldev/kord/voice/encryption/strategies/NonceStrategy;)V public final fun setReceiveVoice (Z)V public final fun setSelfDeaf (Z)V public final fun setSelfId (Ldev/kord/common/entity/Snowflake;)V @@ -411,6 +419,67 @@ public final class dev/kord/voice/VoiceConnectionKt { public static synthetic fun VoiceConnection$default (Ldev/kord/gateway/Gateway;Ldev/kord/common/entity/Snowflake;Ldev/kord/common/entity/Snowflake;Ldev/kord/common/entity/Snowflake;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; } +public abstract interface class dev/kord/voice/encryption/VoiceEncryption { + public abstract fun createBox ([B)Ldev/kord/voice/encryption/VoiceEncryption$Box; + public abstract fun createUnbox ([B)Ldev/kord/voice/encryption/VoiceEncryption$Unbox; + public abstract fun getMode ()Ldev/kord/voice/EncryptionMode; + public abstract fun getNonceLength ()I + public abstract fun getSupportsDecryption ()Z +} + +public final class dev/kord/voice/encryption/VoiceEncryption$AeadAes256Gcm : dev/kord/voice/encryption/VoiceEncryption { + public static final field INSTANCE Ldev/kord/voice/encryption/VoiceEncryption$AeadAes256Gcm; + public fun createBox ([B)Ldev/kord/voice/encryption/VoiceEncryption$Box; + public fun createUnbox ([B)Ldev/kord/voice/encryption/VoiceEncryption$Unbox; + public fun equals (Ljava/lang/Object;)Z + public fun getMode ()Ldev/kord/voice/EncryptionMode; + public fun getNonceLength ()I + public fun getSupportsDecryption ()Z + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public abstract interface class dev/kord/voice/encryption/VoiceEncryption$Box { + public abstract fun appendNonce (Ldev/kord/voice/io/ByteArrayView;Ldev/kord/voice/io/MutableByteArrayCursor;)V + public abstract fun encrypt ([B[BLdev/kord/voice/io/MutableByteArrayCursor;)Z + public abstract fun generateNonce (Lkotlin/jvm/functions/Function0;)Ldev/kord/voice/io/ByteArrayView; + public abstract fun getOverhead ()I +} + +public final class dev/kord/voice/encryption/VoiceEncryption$DefaultImpls { + public static fun getSupportsDecryption (Ldev/kord/voice/encryption/VoiceEncryption;)Z +} + +public abstract interface class dev/kord/voice/encryption/VoiceEncryption$Unbox { + public abstract fun decrypt ([BII[BLdev/kord/voice/io/MutableByteArrayCursor;)Z + public abstract fun getNonce (Ldev/kord/voice/udp/RTPPacket;)Ldev/kord/voice/io/ByteArrayView; +} + +public final class dev/kord/voice/encryption/VoiceEncryption$XSalsaPoly1305 : dev/kord/voice/encryption/VoiceEncryption { + public static final synthetic fun box-impl (Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory;)Ldev/kord/voice/encryption/VoiceEncryption$XSalsaPoly1305; + public static fun constructor-impl (Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory;)Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory; + public static synthetic fun constructor-impl$default (Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory;ILkotlin/jvm/internal/DefaultConstructorMarker;)Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory; + public fun createBox ([B)Ldev/kord/voice/encryption/VoiceEncryption$Box; + public static fun createBox-impl (Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory;[B)Ldev/kord/voice/encryption/VoiceEncryption$Box; + public fun createUnbox ([B)Ldev/kord/voice/encryption/VoiceEncryption$Unbox; + public static fun createUnbox-impl (Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory;[B)Ldev/kord/voice/encryption/VoiceEncryption$Unbox; + public fun equals (Ljava/lang/Object;)Z + public static fun equals-impl (Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory;Ljava/lang/Object;)Z + public static final fun equals-impl0 (Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory;Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory;)Z + public fun getMode ()Ldev/kord/voice/EncryptionMode; + public static fun getMode-impl (Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory;)Ldev/kord/voice/EncryptionMode; + public fun getNonceLength ()I + public static fun getNonceLength-impl (Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory;)I + public final fun getNsf ()Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory; + public fun getSupportsDecryption ()Z + public static fun getSupportsDecryption-impl (Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory;)Z + public fun hashCode ()I + public static fun hashCode-impl (Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory;)I + public fun toString ()Ljava/lang/String; + public static fun toString-impl (Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory;)Ljava/lang/String; + public final synthetic fun unbox-impl ()Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory; +} + public final class dev/kord/voice/encryption/XSalsa20Poly1305Codec { public fun ([B)V public final fun decrypt ([BII[BLdev/kord/voice/io/MutableByteArrayCursor;)Z @@ -428,36 +497,59 @@ public final class dev/kord/voice/encryption/XSalsa20Poly1305CodecKt { } public final class dev/kord/voice/encryption/strategies/LiteNonceStrategy : dev/kord/voice/encryption/strategies/NonceStrategy { + public static final field Factory Ldev/kord/voice/encryption/strategies/LiteNonceStrategy$Factory; public fun ()V public fun append (Ldev/kord/voice/io/ByteArrayView;Ldev/kord/voice/io/MutableByteArrayCursor;)V public fun generate (Lkotlin/jvm/functions/Function0;)Ldev/kord/voice/io/ByteArrayView; - public fun getNonceLength ()I public fun strip (Ldev/kord/voice/udp/RTPPacket;)Ldev/kord/voice/io/ByteArrayView; } +public final class dev/kord/voice/encryption/strategies/LiteNonceStrategy$Factory : dev/kord/voice/encryption/strategies/NonceStrategy$Factory { + public fun create ()Ldev/kord/voice/encryption/strategies/NonceStrategy; + public fun getMode ()Ldev/kord/voice/EncryptionMode; + public fun getNonceLength ()I +} + public abstract interface class dev/kord/voice/encryption/strategies/NonceStrategy { public abstract fun append (Ldev/kord/voice/io/ByteArrayView;Ldev/kord/voice/io/MutableByteArrayCursor;)V public abstract fun generate (Lkotlin/jvm/functions/Function0;)Ldev/kord/voice/io/ByteArrayView; - public abstract fun getNonceLength ()I public abstract fun strip (Ldev/kord/voice/udp/RTPPacket;)Ldev/kord/voice/io/ByteArrayView; } +public abstract interface class dev/kord/voice/encryption/strategies/NonceStrategy$Factory { + public abstract fun create ()Ldev/kord/voice/encryption/strategies/NonceStrategy; + public abstract fun getMode ()Ldev/kord/voice/EncryptionMode; + public abstract fun getNonceLength ()I +} + public final class dev/kord/voice/encryption/strategies/NormalNonceStrategy : dev/kord/voice/encryption/strategies/NonceStrategy { + public static final field Factory Ldev/kord/voice/encryption/strategies/NormalNonceStrategy$Factory; public fun ()V public fun append (Ldev/kord/voice/io/ByteArrayView;Ldev/kord/voice/io/MutableByteArrayCursor;)V public fun generate (Lkotlin/jvm/functions/Function0;)Ldev/kord/voice/io/ByteArrayView; - public fun getNonceLength ()I public fun strip (Ldev/kord/voice/udp/RTPPacket;)Ldev/kord/voice/io/ByteArrayView; } +public final class dev/kord/voice/encryption/strategies/NormalNonceStrategy$Factory : dev/kord/voice/encryption/strategies/NonceStrategy$Factory { + public fun create ()Ldev/kord/voice/encryption/strategies/NonceStrategy; + public fun getMode ()Ldev/kord/voice/EncryptionMode; + public fun getNonceLength ()I +} + public final class dev/kord/voice/encryption/strategies/SuffixNonceStrategy : dev/kord/voice/encryption/strategies/NonceStrategy { + public static final field Factory Ldev/kord/voice/encryption/strategies/SuffixNonceStrategy$Factory; public fun ()V public fun append (Ldev/kord/voice/io/ByteArrayView;Ldev/kord/voice/io/MutableByteArrayCursor;)V public fun generate (Lkotlin/jvm/functions/Function0;)Ldev/kord/voice/io/ByteArrayView; - public fun getNonceLength ()I public fun strip (Ldev/kord/voice/udp/RTPPacket;)Ldev/kord/voice/io/ByteArrayView; } +public final class dev/kord/voice/encryption/strategies/SuffixNonceStrategy$Factory : dev/kord/voice/encryption/strategies/NonceStrategy$Factory { + public fun create ()Ldev/kord/voice/encryption/strategies/NonceStrategy; + public fun getMode ()Ldev/kord/voice/EncryptionMode; + public fun getNonceLength ()I +} + public final class dev/kord/voice/exception/VoiceConnectionInitializationException : java/lang/Exception { public fun (Ljava/lang/String;)V public fun (Ljava/lang/String;Ljava/lang/Throwable;)V @@ -1081,7 +1173,7 @@ public final class dev/kord/voice/io/ReadableByteArrayCursor { } public final class dev/kord/voice/streams/DefaultStreams : dev/kord/voice/streams/Streams { - public fun (Ldev/kord/voice/gateway/VoiceGateway;Ldev/kord/voice/udp/VoiceUdpSocket;Ldev/kord/voice/encryption/strategies/NonceStrategy;)V + public fun (Ldev/kord/voice/gateway/VoiceGateway;Ldev/kord/voice/udp/VoiceUdpSocket;Ldev/kord/voice/encryption/VoiceEncryption;)V public fun getIncomingAudioFrames ()Lkotlinx/coroutines/flow/Flow; public synthetic fun getIncomingAudioPackets ()Lkotlinx/coroutines/flow/Flow; public fun getIncomingAudioPackets ()Lkotlinx/coroutines/flow/SharedFlow; @@ -1130,9 +1222,9 @@ public final class dev/kord/voice/udp/AudioFrameSenderConfiguration { } public abstract class dev/kord/voice/udp/AudioPacketProvider { - public fun ([BLdev/kord/voice/encryption/strategies/NonceStrategy;)V + public fun ([BLdev/kord/voice/encryption/VoiceEncryption;)V + public final fun getEncryption ()Ldev/kord/voice/encryption/VoiceEncryption; 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; } @@ -1143,16 +1235,16 @@ public final class dev/kord/voice/udp/DefaultAudioFrameSender : dev/kord/voice/u } public final class dev/kord/voice/udp/DefaultAudioFrameSenderData { - public fun (Ldev/kord/voice/udp/VoiceUdpSocket;Ldev/kord/voice/FrameInterceptor;Ldev/kord/voice/AudioProvider;Ldev/kord/voice/encryption/strategies/NonceStrategy;)V + public fun (Ldev/kord/voice/udp/VoiceUdpSocket;Ldev/kord/voice/FrameInterceptor;Ldev/kord/voice/AudioProvider;Ldev/kord/voice/encryption/VoiceEncryption;)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/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;Ldev/kord/voice/encryption/strategies/NonceStrategy;ILjava/lang/Object;)Ldev/kord/voice/udp/DefaultAudioFrameSenderData; + public final fun component4 ()Ldev/kord/voice/encryption/VoiceEncryption; + public final fun copy (Ldev/kord/voice/udp/VoiceUdpSocket;Ldev/kord/voice/FrameInterceptor;Ldev/kord/voice/AudioProvider;Ldev/kord/voice/encryption/VoiceEncryption;)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/VoiceEncryption;ILjava/lang/Object;)Ldev/kord/voice/udp/DefaultAudioFrameSenderData; public fun equals (Ljava/lang/Object;)Z + public final fun getEncryption ()Ldev/kord/voice/encryption/VoiceEncryption; public final fun getInterceptor ()Ldev/kord/voice/FrameInterceptor; - public final fun getNonceStrategy ()Ldev/kord/voice/encryption/strategies/NonceStrategy; public final fun getProvider ()Ldev/kord/voice/AudioProvider; public final fun getUdp ()Ldev/kord/voice/udp/VoiceUdpSocket; public fun hashCode ()I @@ -1160,7 +1252,7 @@ public final class dev/kord/voice/udp/DefaultAudioFrameSenderData { } public final class dev/kord/voice/udp/DefaultAudioPacketProvider : dev/kord/voice/udp/AudioPacketProvider { - public fun ([BLdev/kord/voice/encryption/strategies/NonceStrategy;)V + public fun ([BLdev/kord/voice/encryption/VoiceEncryption;)V public fun provide-jfaDVJw (SII[B)Ldev/kord/voice/io/ByteArrayView; } From 39528de83183a6b64bcfa2af58b9e92cd8c33bc1 Mon Sep 17 00:00:00 2001 From: viztea Date: Wed, 13 Dec 2023 07:49:43 -0800 Subject: [PATCH 03/12] fix(voice): aes128 -> aes256 --- .../ksp/main/kotlin/dev/kord/voice/EncryptionMode.kt | 6 +++--- voice/src/main/kotlin/EncryptionMode.kt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/voice/build/generated/ksp/main/kotlin/dev/kord/voice/EncryptionMode.kt b/voice/build/generated/ksp/main/kotlin/dev/kord/voice/EncryptionMode.kt index 36d5cbb3f8c..3d8f2158873 100644 --- a/voice/build/generated/ksp/main/kotlin/dev/kord/voice/EncryptionMode.kt +++ b/voice/build/generated/ksp/main/kotlin/dev/kord/voice/EncryptionMode.kt @@ -54,7 +54,7 @@ public sealed class EncryptionMode( public object AeadAes256Gcm : EncryptionMode("aead_aes256_gcm") - public object AeadAes128GcmRtpSize : EncryptionMode("aead_aes128_gcm_rtpsize") + public object AeadAes256GcmRtpSize : EncryptionMode("aead_aes256_gcm_rtpsize") public object XSalsa20Poly1305 : EncryptionMode("xsalsa20_poly1305") @@ -80,7 +80,7 @@ public sealed class EncryptionMode( public val entries: List by lazy(mode = PUBLICATION) { listOf( AeadAes256Gcm, - AeadAes128GcmRtpSize, + AeadAes256GcmRtpSize, XSalsa20Poly1305, XSalsa20Poly1305Suffix, XSalsa20Poly1305Lite, @@ -94,7 +94,7 @@ public sealed class EncryptionMode( */ public fun from(`value`: String): EncryptionMode = when (value) { "aead_aes256_gcm" -> AeadAes256Gcm - "aead_aes128_gcm_rtpsize" -> AeadAes128GcmRtpSize + "aead_aes256_gcm_rtpsize" -> AeadAes256GcmRtpSize "xsalsa20_poly1305" -> XSalsa20Poly1305 "xsalsa20_poly1305_suffix" -> XSalsa20Poly1305Suffix "xsalsa20_poly1305_lite" -> XSalsa20Poly1305Lite diff --git a/voice/src/main/kotlin/EncryptionMode.kt b/voice/src/main/kotlin/EncryptionMode.kt index b6ae121181c..6fb0431eb4a 100644 --- a/voice/src/main/kotlin/EncryptionMode.kt +++ b/voice/src/main/kotlin/EncryptionMode.kt @@ -3,7 +3,7 @@ docUrl = "https://discord.com/developers/docs/topics/voice-connections#establishing-a-voice-udp-connection-encryption-modes", entries = [ Entry("AeadAes256Gcm", stringValue = "aead_aes256_gcm"), - Entry("AeadAes128GcmRtpSize", stringValue = "aead_aes128_gcm_rtpsize"), + Entry("AeadAes256GcmRtpSize", stringValue = "aead_aes256_gcm_rtpsize"), Entry("XSalsa20Poly1305", stringValue = "xsalsa20_poly1305"), Entry("XSalsa20Poly1305Suffix", stringValue = "xsalsa20_poly1305_suffix"), Entry("XSalsa20Poly1305Lite", stringValue = "xsalsa20_poly1305_lite"), From e8e44022171710560d71e0ea9c9f6523dcfd2ab4 Mon Sep 17 00:00:00 2001 From: viztea Date: Wed, 13 Dec 2023 07:49:52 -0800 Subject: [PATCH 04/12] fix(voice): remove println --- voice/src/main/kotlin/streams/DefaultStreams.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/voice/src/main/kotlin/streams/DefaultStreams.kt b/voice/src/main/kotlin/streams/DefaultStreams.kt index 072c12dce50..3c7fa0ed5cc 100644 --- a/voice/src/main/kotlin/streams/DefaultStreams.kt +++ b/voice/src/main/kotlin/streams/DefaultStreams.kt @@ -99,7 +99,6 @@ private fun Flow.decrypt(encryption: VoiceEncryption, key: ByteArray) nonceBuffer.writeByteView(unbox.getNonce(packet)) - println(packet.payload.data.contentToString()) val decrypted = with(packet.payload) { unbox.decrypt(data, dataStart, viewSize, nonceBuffer.data, decryptedCursor) } From 5f87178a0e64a7a9965d5196881ed910f89bf937 Mon Sep 17 00:00:00 2001 From: viztea Date: Wed, 13 Dec 2023 07:51:27 -0800 Subject: [PATCH 05/12] chore: api dump --- voice/api/voice.api | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/voice/api/voice.api b/voice/api/voice.api index af801bb313b..07870298634 100644 --- a/voice/api/voice.api +++ b/voice/api/voice.api @@ -204,14 +204,14 @@ public abstract class dev/kord/voice/EncryptionMode { public final fun toString ()Ljava/lang/String; } -public final class dev/kord/voice/EncryptionMode$AeadAes128GcmRtpSize : dev/kord/voice/EncryptionMode { - public static final field INSTANCE Ldev/kord/voice/EncryptionMode$AeadAes128GcmRtpSize; -} - public final class dev/kord/voice/EncryptionMode$AeadAes256Gcm : dev/kord/voice/EncryptionMode { public static final field INSTANCE Ldev/kord/voice/EncryptionMode$AeadAes256Gcm; } +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$Companion { public final fun from (Ljava/lang/String;)Ldev/kord/voice/EncryptionMode; public final fun getEntries ()Ljava/util/List; From 137a36d469ab76f201c89dc20a561cf6a56d8582 Mon Sep 17 00:00:00 2001 From: viztea Date: Mon, 15 Jan 2024 06:04:53 -0800 Subject: [PATCH 06/12] chore: rename nsf var, make xsalsa data class --- .../main/kotlin/encryption/VoiceEncryption.kt | 16 +++++++--------- .../encryption/strategies/LiteNonceStrategy.kt | 2 +- .../encryption/strategies/NonceStrategy.kt | 3 ++- .../encryption/strategies/NormalNonceStrategy.kt | 2 +- .../encryption/strategies/SuffixNonceStrategy.kt | 8 ++++---- 5 files changed, 15 insertions(+), 16 deletions(-) diff --git a/voice/src/main/kotlin/encryption/VoiceEncryption.kt b/voice/src/main/kotlin/encryption/VoiceEncryption.kt index 7a6d84aecf9..2564ded0676 100644 --- a/voice/src/main/kotlin/encryption/VoiceEncryption.kt +++ b/voice/src/main/kotlin/encryption/VoiceEncryption.kt @@ -24,20 +24,21 @@ public sealed interface VoiceEncryption { public fun createUnbox(key: ByteArray): Unbox - @JvmInline - public value class XSalsaPoly1305(public val nsf: NonceStrategy.Factory = LiteNonceStrategy) : VoiceEncryption { + public data class XSalsaPoly1305( + public val nonceStrategyFactory: NonceStrategy.Factory = LiteNonceStrategy, + ) : VoiceEncryption { override val mode: EncryptionMode - get() = nsf.mode + get() = nonceStrategyFactory.mode override val nonceLength: Int get() = 24 override fun createBox(key: ByteArray): Box = object : Box { private val codec: XSalsa20Poly1305Codec = XSalsa20Poly1305Codec(key) - private val nonceStrategy: NonceStrategy = nsf.create() + private val nonceStrategy: NonceStrategy = nonceStrategyFactory.create() override val overhead: Int - get() = TweetNaclFast.SecretBox.boxzerobytesLength + nsf.nonceLength + get() = TweetNaclFast.SecretBox.boxzerobytesLength + nonceStrategyFactory.length override fun encrypt(src: ByteArray, nonce: ByteArray, dst: MutableByteArrayCursor): Boolean { return codec.encrypt(src, 0, src.size, nonce, dst) @@ -54,7 +55,7 @@ public sealed interface VoiceEncryption { override fun createUnbox(key: ByteArray): Unbox = object : Unbox { private val codec: XSalsa20Poly1305Codec = XSalsa20Poly1305Codec(key) - private val nonceStrategy: NonceStrategy = nsf.create() + private val nonceStrategy: NonceStrategy = nonceStrategyFactory.create() override fun decrypt( src: ByteArray, @@ -83,17 +84,14 @@ public sealed interface VoiceEncryption { get() = false override fun createBox(key: ByteArray): Box = object : Box { - // private val iv = ByteArray(IV_LEN) private val ivCursor = iv.mutableCursor() - // private var nonce = 0u private val nonceBuffer: ByteArray = ByteArray(NONCE_LEN) private val nonceCursor = nonceBuffer.mutableCursor() private val nonceView = nonceBuffer.view() - // val secretKey = SecretKeySpec(key, "AES") val cipher = Cipher.getInstance("AES/GCM/NoPadding") diff --git a/voice/src/main/kotlin/encryption/strategies/LiteNonceStrategy.kt b/voice/src/main/kotlin/encryption/strategies/LiteNonceStrategy.kt index 09a9ee54818..508e1d3e4c9 100644 --- a/voice/src/main/kotlin/encryption/strategies/LiteNonceStrategy.kt +++ b/voice/src/main/kotlin/encryption/strategies/LiteNonceStrategy.kt @@ -35,7 +35,7 @@ public class LiteNonceStrategy : NonceStrategy { public companion object Factory : NonceStrategy.Factory { override val mode: EncryptionMode get() = EncryptionMode.XSalsa20Poly1305Lite - override val nonceLength: Int get() = 4 + override val length: Int get() = 4 override fun create(): NonceStrategy = LiteNonceStrategy() } diff --git a/voice/src/main/kotlin/encryption/strategies/NonceStrategy.kt b/voice/src/main/kotlin/encryption/strategies/NonceStrategy.kt index 8144a3ca2e6..18fe2a26c03 100644 --- a/voice/src/main/kotlin/encryption/strategies/NonceStrategy.kt +++ b/voice/src/main/kotlin/encryption/strategies/NonceStrategy.kt @@ -26,13 +26,14 @@ public sealed interface NonceStrategy { public interface Factory { /** + * The [EncryptionMode] this nonce strategy is for. */ public val mode: EncryptionMode /** * The amount of bytes this nonce will take up. */ - public val nonceLength: Int + public val length: Int public fun create(): NonceStrategy } diff --git a/voice/src/main/kotlin/encryption/strategies/NormalNonceStrategy.kt b/voice/src/main/kotlin/encryption/strategies/NormalNonceStrategy.kt index da57f5ae200..723f5a978b0 100644 --- a/voice/src/main/kotlin/encryption/strategies/NormalNonceStrategy.kt +++ b/voice/src/main/kotlin/encryption/strategies/NormalNonceStrategy.kt @@ -28,7 +28,7 @@ public class NormalNonceStrategy : NonceStrategy { } public companion object Factory : NonceStrategy.Factory { - override val nonceLength: Int = 0 + override val length: Int = 0 override val mode: EncryptionMode get() = EncryptionMode.XSalsa20Poly1305 diff --git a/voice/src/main/kotlin/encryption/strategies/SuffixNonceStrategy.kt b/voice/src/main/kotlin/encryption/strategies/SuffixNonceStrategy.kt index 32a55fbf60e..91eaca451f3 100644 --- a/voice/src/main/kotlin/encryption/strategies/SuffixNonceStrategy.kt +++ b/voice/src/main/kotlin/encryption/strategies/SuffixNonceStrategy.kt @@ -9,13 +9,13 @@ import dev.kord.voice.udp.RTPPacket import kotlin.random.Random public class SuffixNonceStrategy : NonceStrategy { - private val nonceBuffer: ByteArray = ByteArray(nonceLength) + private val nonceBuffer: ByteArray = ByteArray(length) private val nonceView = nonceBuffer.view() override fun strip(packet: RTPPacket): ByteArrayView { return with(packet.payload) { - val nonce = view(dataEnd - nonceLength, dataEnd)!! - resize(dataStart, dataEnd - nonceLength) + val nonce = view(dataEnd - length, dataEnd)!! + resize(dataStart, dataEnd - length) nonce } } @@ -31,7 +31,7 @@ public class SuffixNonceStrategy : NonceStrategy { public companion object Factory : NonceStrategy.Factory { - override val nonceLength: Int = TweetNaclFast.SecretBox.nonceLength + override val length: Int = TweetNaclFast.SecretBox.nonceLength override val mode: EncryptionMode get() = EncryptionMode.XSalsa20Poly1305Suffix override fun create(): NonceStrategy = SuffixNonceStrategy() From 1a171b9ee798a59967b99945d13990504cd09947 Mon Sep 17 00:00:00 2001 From: viztea Date: Mon, 15 Jan 2024 06:07:15 -0800 Subject: [PATCH 07/12] chore: remove default value --- .../main/kotlin/encryption/VoiceEncryption.kt | 28 ++++++++----------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/voice/src/main/kotlin/encryption/VoiceEncryption.kt b/voice/src/main/kotlin/encryption/VoiceEncryption.kt index 2564ded0676..b5ddb2088f6 100644 --- a/voice/src/main/kotlin/encryption/VoiceEncryption.kt +++ b/voice/src/main/kotlin/encryption/VoiceEncryption.kt @@ -14,11 +14,14 @@ import javax.crypto.spec.GCMParameterSpec import javax.crypto.spec.SecretKeySpec public sealed interface VoiceEncryption { - public val mode: EncryptionMode - public val nonceLength: Int - public val supportsDecryption: Boolean get() = true + public val mode: EncryptionMode + + /** + * Whether this encryption mode supports decryption. + */ + public val supportsDecryption: Boolean public fun createBox(key: ByteArray): Box @@ -27,11 +30,9 @@ public sealed interface VoiceEncryption { public data class XSalsaPoly1305( public val nonceStrategyFactory: NonceStrategy.Factory = LiteNonceStrategy, ) : VoiceEncryption { - override val mode: EncryptionMode - get() = nonceStrategyFactory.mode - - override val nonceLength: Int - get() = 24 + override val supportsDecryption: Boolean get() = true + override val mode: EncryptionMode get() = nonceStrategyFactory.mode + override val nonceLength: Int get() = 24 override fun createBox(key: ByteArray): Box = object : Box { private val codec: XSalsa20Poly1305Codec = XSalsa20Poly1305Codec(key) @@ -74,14 +75,9 @@ public sealed interface VoiceEncryption { private const val NONCE_LEN = 4 private const val IV_LEN = 12 - override val mode: EncryptionMode - get() = EncryptionMode.AeadAes256Gcm - - override val nonceLength: Int - get() = 4 - - override val supportsDecryption: Boolean - get() = false + override val supportsDecryption: Boolean get() = false + override val mode: EncryptionMode get() = EncryptionMode.AeadAes256Gcm + override val nonceLength: Int get() = 4 override fun createBox(key: ByteArray): Box = object : Box { private val iv = ByteArray(IV_LEN) From 5000114eb7d32b52c43b9e02b76bdf0d7c945fde Mon Sep 17 00:00:00 2001 From: viztea Date: Mon, 15 Jan 2024 06:07:53 -0800 Subject: [PATCH 08/12] chore: fix formatting --- .../src/main/kotlin/encryption/strategies/LiteNonceStrategy.kt | 3 ++- .../main/kotlin/encryption/strategies/SuffixNonceStrategy.kt | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/voice/src/main/kotlin/encryption/strategies/LiteNonceStrategy.kt b/voice/src/main/kotlin/encryption/strategies/LiteNonceStrategy.kt index 508e1d3e4c9..e0a3dbebcbd 100644 --- a/voice/src/main/kotlin/encryption/strategies/LiteNonceStrategy.kt +++ b/voice/src/main/kotlin/encryption/strategies/LiteNonceStrategy.kt @@ -34,9 +34,10 @@ public class LiteNonceStrategy : NonceStrategy { } public companion object Factory : NonceStrategy.Factory { - override val mode: EncryptionMode get() = EncryptionMode.XSalsa20Poly1305Lite override val length: Int get() = 4 + override val mode: EncryptionMode get() = EncryptionMode.XSalsa20Poly1305Lite + override fun create(): NonceStrategy = LiteNonceStrategy() } } diff --git a/voice/src/main/kotlin/encryption/strategies/SuffixNonceStrategy.kt b/voice/src/main/kotlin/encryption/strategies/SuffixNonceStrategy.kt index 91eaca451f3..cc357b004a8 100644 --- a/voice/src/main/kotlin/encryption/strategies/SuffixNonceStrategy.kt +++ b/voice/src/main/kotlin/encryption/strategies/SuffixNonceStrategy.kt @@ -30,8 +30,8 @@ public class SuffixNonceStrategy : NonceStrategy { } public companion object Factory : NonceStrategy.Factory { - override val length: Int = TweetNaclFast.SecretBox.nonceLength + override val mode: EncryptionMode get() = EncryptionMode.XSalsa20Poly1305Suffix override fun create(): NonceStrategy = SuffixNonceStrategy() From fafa5c140b7b5591ff378d46608051e6e1c84f3b Mon Sep 17 00:00:00 2001 From: viztea Date: Mon, 15 Jan 2024 06:11:08 -0800 Subject: [PATCH 09/12] chore: api dump --- voice/api/voice.api | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/voice/api/voice.api b/voice/api/voice.api index 07870298634..05493ebe64e 100644 --- a/voice/api/voice.api +++ b/voice/api/voice.api @@ -446,38 +446,27 @@ public abstract interface class dev/kord/voice/encryption/VoiceEncryption$Box { public abstract fun getOverhead ()I } -public final class dev/kord/voice/encryption/VoiceEncryption$DefaultImpls { - public static fun getSupportsDecryption (Ldev/kord/voice/encryption/VoiceEncryption;)Z -} - public abstract interface class dev/kord/voice/encryption/VoiceEncryption$Unbox { public abstract fun decrypt ([BII[BLdev/kord/voice/io/MutableByteArrayCursor;)Z public abstract fun getNonce (Ldev/kord/voice/udp/RTPPacket;)Ldev/kord/voice/io/ByteArrayView; } public final class dev/kord/voice/encryption/VoiceEncryption$XSalsaPoly1305 : dev/kord/voice/encryption/VoiceEncryption { - public static final synthetic fun box-impl (Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory;)Ldev/kord/voice/encryption/VoiceEncryption$XSalsaPoly1305; - public static fun constructor-impl (Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory;)Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory; - public static synthetic fun constructor-impl$default (Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory;ILkotlin/jvm/internal/DefaultConstructorMarker;)Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory; + public fun ()V + public fun (Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory;)V + public synthetic fun (Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory; + public final fun copy (Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory;)Ldev/kord/voice/encryption/VoiceEncryption$XSalsaPoly1305; + public static synthetic fun copy$default (Ldev/kord/voice/encryption/VoiceEncryption$XSalsaPoly1305;Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory;ILjava/lang/Object;)Ldev/kord/voice/encryption/VoiceEncryption$XSalsaPoly1305; public fun createBox ([B)Ldev/kord/voice/encryption/VoiceEncryption$Box; - public static fun createBox-impl (Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory;[B)Ldev/kord/voice/encryption/VoiceEncryption$Box; public fun createUnbox ([B)Ldev/kord/voice/encryption/VoiceEncryption$Unbox; - public static fun createUnbox-impl (Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory;[B)Ldev/kord/voice/encryption/VoiceEncryption$Unbox; public fun equals (Ljava/lang/Object;)Z - public static fun equals-impl (Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory;Ljava/lang/Object;)Z - public static final fun equals-impl0 (Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory;Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory;)Z public fun getMode ()Ldev/kord/voice/EncryptionMode; - public static fun getMode-impl (Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory;)Ldev/kord/voice/EncryptionMode; public fun getNonceLength ()I - public static fun getNonceLength-impl (Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory;)I - public final fun getNsf ()Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory; + public final fun getNonceStrategyFactory ()Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory; public fun getSupportsDecryption ()Z - public static fun getSupportsDecryption-impl (Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory;)Z public fun hashCode ()I - public static fun hashCode-impl (Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory;)I public fun toString ()Ljava/lang/String; - public static fun toString-impl (Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory;)Ljava/lang/String; - public final synthetic fun unbox-impl ()Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory; } public final class dev/kord/voice/encryption/XSalsa20Poly1305Codec { @@ -506,8 +495,8 @@ public final class dev/kord/voice/encryption/strategies/LiteNonceStrategy : dev/ public final class dev/kord/voice/encryption/strategies/LiteNonceStrategy$Factory : dev/kord/voice/encryption/strategies/NonceStrategy$Factory { public fun create ()Ldev/kord/voice/encryption/strategies/NonceStrategy; + public fun getLength ()I public fun getMode ()Ldev/kord/voice/EncryptionMode; - public fun getNonceLength ()I } public abstract interface class dev/kord/voice/encryption/strategies/NonceStrategy { @@ -518,8 +507,8 @@ public abstract interface class dev/kord/voice/encryption/strategies/NonceStrate public abstract interface class dev/kord/voice/encryption/strategies/NonceStrategy$Factory { public abstract fun create ()Ldev/kord/voice/encryption/strategies/NonceStrategy; + public abstract fun getLength ()I public abstract fun getMode ()Ldev/kord/voice/EncryptionMode; - public abstract fun getNonceLength ()I } public final class dev/kord/voice/encryption/strategies/NormalNonceStrategy : dev/kord/voice/encryption/strategies/NonceStrategy { @@ -532,8 +521,8 @@ public final class dev/kord/voice/encryption/strategies/NormalNonceStrategy : de public final class dev/kord/voice/encryption/strategies/NormalNonceStrategy$Factory : dev/kord/voice/encryption/strategies/NonceStrategy$Factory { public fun create ()Ldev/kord/voice/encryption/strategies/NonceStrategy; + public fun getLength ()I public fun getMode ()Ldev/kord/voice/EncryptionMode; - public fun getNonceLength ()I } public final class dev/kord/voice/encryption/strategies/SuffixNonceStrategy : dev/kord/voice/encryption/strategies/NonceStrategy { @@ -546,8 +535,8 @@ public final class dev/kord/voice/encryption/strategies/SuffixNonceStrategy : de public final class dev/kord/voice/encryption/strategies/SuffixNonceStrategy$Factory : dev/kord/voice/encryption/strategies/NonceStrategy$Factory { public fun create ()Ldev/kord/voice/encryption/strategies/NonceStrategy; + public fun getLength ()I public fun getMode ()Ldev/kord/voice/EncryptionMode; - public fun getNonceLength ()I } public final class dev/kord/voice/exception/VoiceConnectionInitializationException : java/lang/Exception { From 65de96802d4dd41baae7b4eb729c9d5a6170f589 Mon Sep 17 00:00:00 2001 From: viztea Date: Wed, 5 Jun 2024 00:51:53 -0700 Subject: [PATCH 10/12] feat(voice): cleanup + aes decryption --- voice/api/voice.api | 71 ++++---- voice/src/main/kotlin/VoiceConnection.kt | 6 +- .../src/main/kotlin/VoiceConnectionBuilder.kt | 83 +++++---- .../main/kotlin/encryption/AeadAes256Gcm.kt | 97 +++++++++++ .../main/kotlin/encryption/VoiceEncryption.kt | 157 ++++-------------- .../kotlin/encryption/XSalsa20Poly1305.kt | 95 +++++++++++ .../encryption/XSalsa20Poly1305Codec.kt | 48 ------ .../encryption/XSalsa20Poly1305Encryption.kt | 55 ------ .../src/main/kotlin/streams/DefaultStreams.kt | 6 +- .../main/kotlin/udp/AudioPacketProvider.kt | 8 +- .../kotlin/udp/DefaultAudioFrameSender.kt | 1 - 11 files changed, 314 insertions(+), 313 deletions(-) create mode 100644 voice/src/main/kotlin/encryption/AeadAes256Gcm.kt create mode 100644 voice/src/main/kotlin/encryption/XSalsa20Poly1305.kt delete mode 100644 voice/src/main/kotlin/encryption/XSalsa20Poly1305Codec.kt delete mode 100644 voice/src/main/kotlin/encryption/XSalsa20Poly1305Encryption.kt diff --git a/voice/api/voice.api b/voice/api/voice.api index 4308e2895d7..50c6f861f92 100644 --- a/voice/api/voice.api +++ b/voice/api/voice.api @@ -405,70 +405,79 @@ public final class dev/kord/voice/VoiceConnectionKt { public static synthetic fun VoiceConnection$default (Ldev/kord/gateway/Gateway;Ldev/kord/common/entity/Snowflake;Ldev/kord/common/entity/Snowflake;Ldev/kord/common/entity/Snowflake;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; } -public abstract interface class dev/kord/voice/encryption/VoiceEncryption { - public abstract fun createBox ([B)Ldev/kord/voice/encryption/VoiceEncryption$Box; - public abstract fun createUnbox ([B)Ldev/kord/voice/encryption/VoiceEncryption$Unbox; - public abstract fun getMode ()Ldev/kord/voice/EncryptionMode; - public abstract fun getNonceLength ()I - public abstract fun getSupportsDecryption ()Z -} - -public final class dev/kord/voice/encryption/VoiceEncryption$AeadAes256Gcm : dev/kord/voice/encryption/VoiceEncryption { - public static final field INSTANCE Ldev/kord/voice/encryption/VoiceEncryption$AeadAes256Gcm; +public final class dev/kord/voice/encryption/AeadAes256Gcm : dev/kord/voice/encryption/VoiceEncryption { + public static final field INSTANCE Ldev/kord/voice/encryption/AeadAes256Gcm; public fun createBox ([B)Ldev/kord/voice/encryption/VoiceEncryption$Box; public fun createUnbox ([B)Ldev/kord/voice/encryption/VoiceEncryption$Unbox; public fun equals (Ljava/lang/Object;)Z public fun getMode ()Ldev/kord/voice/EncryptionMode; public fun getNonceLength ()I - public fun getSupportsDecryption ()Z public fun hashCode ()I public fun toString ()Ljava/lang/String; } -public abstract interface class dev/kord/voice/encryption/VoiceEncryption$Box { +public abstract interface class dev/kord/voice/encryption/VoiceEncryption { + public abstract fun createBox ([B)Ldev/kord/voice/encryption/VoiceEncryption$Box; + public abstract fun createUnbox ([B)Ldev/kord/voice/encryption/VoiceEncryption$Unbox; + public abstract fun getMode ()Ldev/kord/voice/EncryptionMode; + public abstract fun getNonceLength ()I +} + +public abstract interface class dev/kord/voice/encryption/VoiceEncryption$Box : dev/kord/voice/encryption/VoiceEncryption$Method { public abstract fun appendNonce (Ldev/kord/voice/io/ByteArrayView;Ldev/kord/voice/io/MutableByteArrayCursor;)V - public abstract fun encrypt ([B[BLdev/kord/voice/io/MutableByteArrayCursor;)Z public abstract fun generateNonce (Lkotlin/jvm/functions/Function0;)Ldev/kord/voice/io/ByteArrayView; public abstract fun getOverhead ()I } -public abstract interface class dev/kord/voice/encryption/VoiceEncryption$Unbox { - public abstract fun decrypt ([BII[BLdev/kord/voice/io/MutableByteArrayCursor;)Z +public abstract interface class dev/kord/voice/encryption/VoiceEncryption$Method { + public abstract fun apply (Ldev/kord/voice/io/ByteArrayView;[BLdev/kord/voice/io/MutableByteArrayCursor;)Z +} + +public abstract interface class dev/kord/voice/encryption/VoiceEncryption$Unbox : dev/kord/voice/encryption/VoiceEncryption$Method { public abstract fun getNonce (Ldev/kord/voice/udp/RTPPacket;)Ldev/kord/voice/io/ByteArrayView; } -public final class dev/kord/voice/encryption/VoiceEncryption$XSalsaPoly1305 : dev/kord/voice/encryption/VoiceEncryption { +public final class dev/kord/voice/encryption/XSalsa20Poly1305 : dev/kord/voice/encryption/VoiceEncryption { + public static final field Companion Ldev/kord/voice/encryption/XSalsa20Poly1305$Companion; public fun ()V public fun (Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory;)V public synthetic fun (Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory; - public final fun copy (Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory;)Ldev/kord/voice/encryption/VoiceEncryption$XSalsaPoly1305; - public static synthetic fun copy$default (Ldev/kord/voice/encryption/VoiceEncryption$XSalsaPoly1305;Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory;ILjava/lang/Object;)Ldev/kord/voice/encryption/VoiceEncryption$XSalsaPoly1305; + public final fun copy (Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory;)Ldev/kord/voice/encryption/XSalsa20Poly1305; + public static synthetic fun copy$default (Ldev/kord/voice/encryption/XSalsa20Poly1305;Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory;ILjava/lang/Object;)Ldev/kord/voice/encryption/XSalsa20Poly1305; public fun createBox ([B)Ldev/kord/voice/encryption/VoiceEncryption$Box; public fun createUnbox ([B)Ldev/kord/voice/encryption/VoiceEncryption$Unbox; public fun equals (Ljava/lang/Object;)Z public fun getMode ()Ldev/kord/voice/EncryptionMode; public fun getNonceLength ()I public final fun getNonceStrategyFactory ()Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory; - public fun getSupportsDecryption ()Z public fun hashCode ()I public fun toString ()Ljava/lang/String; } -public final class dev/kord/voice/encryption/XSalsa20Poly1305Codec { - public fun ([B)V - public final fun decrypt ([BII[BLdev/kord/voice/io/MutableByteArrayCursor;)Z - public static synthetic fun decrypt$default (Ldev/kord/voice/encryption/XSalsa20Poly1305Codec;[BII[BLdev/kord/voice/io/MutableByteArrayCursor;ILjava/lang/Object;)Z - public final fun encrypt ([BII[BLdev/kord/voice/io/MutableByteArrayCursor;)Z - public static synthetic fun encrypt$default (Ldev/kord/voice/encryption/XSalsa20Poly1305Codec;[BII[BLdev/kord/voice/io/MutableByteArrayCursor;ILjava/lang/Object;)Z - public final fun getKey ()[B +public final class dev/kord/voice/encryption/XSalsa20Poly1305$Companion { +} + +public abstract class dev/kord/voice/encryption/XSalsa20Poly1305$Impl { + public synthetic fun (Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory;Lkotlin/jvm/internal/DefaultConstructorMarker;)V + protected final fun getC ()[B + protected final fun getM ()[B + protected final fun getNonceStrategy ()Ldev/kord/voice/encryption/strategies/NonceStrategy; + protected final fun getNonceStrategyFactory ()Ldev/kord/voice/encryption/strategies/NonceStrategy$Factory; +} + +public final class dev/kord/voice/encryption/XSalsa20Poly1305$Impl$Box : dev/kord/voice/encryption/XSalsa20Poly1305$Impl, dev/kord/voice/encryption/VoiceEncryption$Box { + public fun ([BLdev/kord/voice/encryption/strategies/NonceStrategy$Factory;)V + public fun appendNonce (Ldev/kord/voice/io/ByteArrayView;Ldev/kord/voice/io/MutableByteArrayCursor;)V + public fun apply (Ldev/kord/voice/io/ByteArrayView;[BLdev/kord/voice/io/MutableByteArrayCursor;)Z + public fun generateNonce (Lkotlin/jvm/functions/Function0;)Ldev/kord/voice/io/ByteArrayView; + public fun getOverhead ()I } -public final class dev/kord/voice/encryption/XSalsa20Poly1305CodecKt { - public static final fun decrypt (Ldev/kord/voice/encryption/XSalsa20Poly1305Codec;[BII[B)[B - public static synthetic fun decrypt$default (Ldev/kord/voice/encryption/XSalsa20Poly1305Codec;[BII[BILjava/lang/Object;)[B - public static final fun encrypt (Ldev/kord/voice/encryption/XSalsa20Poly1305Codec;[BII[B)[B - public static synthetic fun encrypt$default (Ldev/kord/voice/encryption/XSalsa20Poly1305Codec;[BII[BILjava/lang/Object;)[B +public final class dev/kord/voice/encryption/XSalsa20Poly1305$Impl$Unbox : dev/kord/voice/encryption/XSalsa20Poly1305$Impl, dev/kord/voice/encryption/VoiceEncryption$Unbox { + public fun ([BLdev/kord/voice/encryption/strategies/NonceStrategy$Factory;)V + public fun apply (Ldev/kord/voice/io/ByteArrayView;[BLdev/kord/voice/io/MutableByteArrayCursor;)Z + public fun getNonce (Ldev/kord/voice/udp/RTPPacket;)Ldev/kord/voice/io/ByteArrayView; } public final class dev/kord/voice/encryption/strategies/LiteNonceStrategy : dev/kord/voice/encryption/strategies/NonceStrategy { diff --git a/voice/src/main/kotlin/VoiceConnection.kt b/voice/src/main/kotlin/VoiceConnection.kt index 1b1e490cda0..6aae3a6662f 100644 --- a/voice/src/main/kotlin/VoiceConnection.kt +++ b/voice/src/main/kotlin/VoiceConnection.kt @@ -5,7 +5,6 @@ import dev.kord.common.entity.Snowflake import dev.kord.gateway.Gateway import dev.kord.gateway.UpdateVoiceStatus import dev.kord.voice.encryption.VoiceEncryption -import dev.kord.voice.encryption.strategies.NonceStrategy import dev.kord.voice.gateway.VoiceGateway import dev.kord.voice.gateway.VoiceGatewayConfiguration import dev.kord.voice.handlers.StreamsHandler @@ -42,7 +41,6 @@ public data class VoiceConnectionData( * @param audioProvider a [AudioProvider] that will provide [AudioFrame] when required. * @param frameInterceptor a [FrameInterceptor] that will intercept all outgoing [AudioFrame]s. * @param frameSender the [AudioFrameSender] that will handle the sending of audio packets. - * @param nonceStrategy the [NonceStrategy] that is used during encryption of audio. */ @KordVoice public class VoiceConnection( @@ -56,7 +54,7 @@ public class VoiceConnection( public val frameInterceptor: FrameInterceptor, public val frameSender: AudioFrameSender, public val encryption: VoiceEncryption, - connectionDetachDuration: Duration + connectionDetachDuration: Duration, ) { public val scope: CoroutineScope = CoroutineScope(SupervisorJob() + CoroutineName("kord-voice-connection[${data.guildId.value}]")) @@ -149,7 +147,7 @@ public suspend inline fun VoiceConnection( selfId: Snowflake, channelId: Snowflake, guildId: Snowflake, - builder: VoiceConnectionBuilder.() -> Unit = {} + builder: VoiceConnectionBuilder.() -> Unit = {}, ): VoiceConnection { contract { callsInPlace(builder, InvocationKind.EXACTLY_ONCE) } return VoiceConnectionBuilder(gateway, selfId, channelId, guildId).apply(builder).build() diff --git a/voice/src/main/kotlin/VoiceConnectionBuilder.kt b/voice/src/main/kotlin/VoiceConnectionBuilder.kt index 060f04a13e7..fb0718ad3fa 100644 --- a/voice/src/main/kotlin/VoiceConnectionBuilder.kt +++ b/voice/src/main/kotlin/VoiceConnectionBuilder.kt @@ -7,8 +7,8 @@ import dev.kord.gateway.Gateway import dev.kord.gateway.UpdateVoiceStatus import dev.kord.gateway.VoiceServerUpdate import dev.kord.gateway.VoiceStateUpdate +import dev.kord.voice.encryption.AeadAes256Gcm import dev.kord.voice.encryption.VoiceEncryption -import dev.kord.voice.encryption.strategies.LiteNonceStrategy import dev.kord.voice.exception.VoiceConnectionInitializationException import dev.kord.voice.gateway.DefaultVoiceGatewayBuilder import dev.kord.voice.gateway.VoiceGateway @@ -65,8 +65,8 @@ public class VoiceConnectionBuilder( /** * The nonce strategy to be used for the encryption of audio packets. - * If `null` & voice receive if disabled, [VoiceEncryption.AeadAes256Gcm] will be used, - * otherwise [VoiceEncryption.XSalsaPoly1305] with the Lite strategy will be used. + * If `null` & voice receive if disabled, [dev.kord.voice.encryption.AeadAes256Gcm] will be used, + * otherwise [dev.kord.voice.encryption.XSalsa20Poly1305] with the Lite strategy will be used. */ public var encryption: VoiceEncryption? = null @@ -112,49 +112,50 @@ public class VoiceConnectionBuilder( this.voiceGatewayBuilder = builder } - private suspend fun Gateway.updateVoiceState(): Pair = coroutineScope { - val voiceStateDeferred = async { - withTimeoutOrNull(timeout) { - gateway.events.filterIsInstance() - .filter { it.voiceState.guildId.value == guildId && it.voiceState.userId == selfId } - .first() - .voiceState + private suspend fun Gateway.updateVoiceState(): Pair = + coroutineScope { + val voiceStateDeferred = async { + withTimeoutOrNull(timeout) { + gateway.events.filterIsInstance() + .filter { it.voiceState.guildId.value == guildId && it.voiceState.userId == selfId } + .first() + .voiceState + } } - } - val voiceServerDeferred = async { - withTimeoutOrNull(timeout) { - gateway.events.filterIsInstance() - .filter { it.voiceServerUpdateData.guildId == guildId } - .first() - .voiceServerUpdateData + val voiceServerDeferred = async { + withTimeoutOrNull(timeout) { + gateway.events.filterIsInstance() + .filter { it.voiceServerUpdateData.guildId == guildId } + .first() + .voiceServerUpdateData + } } - } - send( - UpdateVoiceStatus( - guildId = guildId, - channelId = channelId, - selfMute = selfMute, - selfDeaf = selfDeaf, + send( + UpdateVoiceStatus( + guildId = guildId, + channelId = channelId, + selfMute = selfMute, + selfDeaf = selfDeaf, + ) ) - ) - val voiceServer = voiceServerDeferred.await() - val voiceState = voiceStateDeferred.await() + val voiceServer = voiceServerDeferred.await() + val voiceState = voiceStateDeferred.await() - if (voiceServer == null || voiceState == null) - throw VoiceConnectionInitializationException("Did not receive a VoiceStateUpdate and or a VoiceServerUpdate in time!") + if (voiceServer == null || voiceState == null) + throw VoiceConnectionInitializationException("Did not receive a VoiceStateUpdate and or a VoiceServerUpdate in time!") - VoiceConnectionData( - selfId, - guildId, - voiceState.sessionId - ) to VoiceGatewayConfiguration( - voiceServer.token, - "wss://${voiceServer.endpoint}/?v=${KordConfiguration.VOICE_GATEWAY_VERSION}", - ) - } + VoiceConnectionData( + selfId, + guildId, + voiceState.sessionId + ) to VoiceGatewayConfiguration( + voiceServer.token, + "wss://${voiceServer.endpoint}/?v=${KordConfiguration.VOICE_GATEWAY_VERSION}", + ) + } /** * @throws dev.kord.voice.exception.VoiceConnectionInitializationException when there was a problem retrieving voice information from Discord. @@ -167,11 +168,7 @@ public class VoiceConnectionBuilder( .build() val udpSocket = udpSocket ?: GlobalVoiceUdpSocket val audioProvider = audioProvider ?: EmptyAudioPlayerProvider - val encryption = if ((receiveVoice || streams != null) && encryption?.supportsDecryption != true) { - VoiceEncryption.XSalsaPoly1305() - } else { - encryption ?: VoiceEncryption.AeadAes256Gcm - } + val encryption = encryption ?: AeadAes256Gcm val frameInterceptor = frameInterceptor ?: DefaultFrameInterceptor() val audioSender = audioSender ?: DefaultAudioFrameSender( diff --git a/voice/src/main/kotlin/encryption/AeadAes256Gcm.kt b/voice/src/main/kotlin/encryption/AeadAes256Gcm.kt new file mode 100644 index 00000000000..da34c796a58 --- /dev/null +++ b/voice/src/main/kotlin/encryption/AeadAes256Gcm.kt @@ -0,0 +1,97 @@ +package dev.kord.voice.encryption + +import dev.kord.voice.EncryptionMode +import dev.kord.voice.encryption.VoiceEncryption.Box +import dev.kord.voice.encryption.VoiceEncryption.Unbox +import dev.kord.voice.io.ByteArrayView +import dev.kord.voice.io.MutableByteArrayCursor +import dev.kord.voice.io.mutableCursor +import dev.kord.voice.io.view +import dev.kord.voice.udp.RTPPacket +import javax.crypto.Cipher +import javax.crypto.spec.GCMParameterSpec +import javax.crypto.spec.SecretKeySpec + +/** + * An [encryption method][VoiceEncryption] that uses the AES-256 GCM cipher. + */ +public data object AeadAes256Gcm : VoiceEncryption { + private const val AUTH_TAG_LEN = 16 + private const val NONCE_LEN = 4 + private const val IV_LEN = 12 + + override val mode: EncryptionMode get() = EncryptionMode.AeadAes256Gcm + + override val nonceLength: Int get() = 4 + + override fun createBox(key: ByteArray): Box = BoxImpl(key) + + override fun createUnbox(key: ByteArray): Unbox = UnboxImpl(key) + + private abstract class Common(key: ByteArray) { + protected val iv = ByteArray(IV_LEN) + protected val ivCursor = iv.mutableCursor() + + protected open val nonceBuffer: ByteArray = ByteArray(NONCE_LEN) + protected val nonceCursor by lazy { nonceBuffer.mutableCursor() } + protected val nonceView by lazy { nonceBuffer.view() } + + protected val cipherKey = SecretKeySpec(key, "AES") + protected val cipher: Cipher = Cipher.getInstance("AES/GCM/NoPadding") + + fun apply( + mode: Int, + src: ByteArrayView, + dst: MutableByteArrayCursor, + nonce: ByteArray, + writeNonce: MutableByteArrayCursor.(nonce: ByteArray) -> Unit, + ): Boolean { + iv.fill(0) + ivCursor.reset() + ivCursor.apply { writeNonce(nonce) } + + init(mode) + cipher.updateAAD(dst.data.copyOfRange(0, dst.cursor)) + dst.cursor += cipher.doFinal(src.data, src.dataStart, src.viewSize, dst.data, dst.cursor) + + return true + } + + fun init(mode: Int) { + cipher.init(mode, cipherKey, GCMParameterSpec(AUTH_TAG_LEN * 8, iv, 0, IV_LEN)) + } + } + + private class BoxImpl(key: ByteArray) : Box, Common(key) { + override val overhead: Int + get() = AUTH_TAG_LEN + NONCE_LEN + + override fun apply(src: ByteArrayView, nonce: ByteArray, dst: MutableByteArrayCursor): Boolean = + apply(Cipher.ENCRYPT_MODE, src, dst, nonce, MutableByteArrayCursor::writeByteArray) + + override fun appendNonce(nonce: ByteArrayView, dst: MutableByteArrayCursor) { + dst.writeByteView(nonce) + } + + override fun generateNonce(header: () -> ByteArrayView): ByteArrayView { + nonceCursor.reset() + nonceCursor.writeByteView(header().view(0, NONCE_LEN)!!) + return nonceView + } + } + + private class UnboxImpl(key: ByteArray) : Unbox, Common(key) { + // Since RTPPacket expects a container big enough to fit the entire header this will + // need to be 12 bytes (or the min length of an RTP header). We will only use the + // first NONCE_LEN bytes, though. + override val nonceBuffer: ByteArray = ByteArray(12) + + override fun apply(src: ByteArrayView, nonce: ByteArray, dst: MutableByteArrayCursor): Boolean = + apply(Cipher.DECRYPT_MODE, src, dst, nonce) { writeByteView(it.view(0, NONCE_LEN)!!) } + + override fun getNonce(packet: RTPPacket): ByteArrayView { + packet.writeHeader(nonceCursor) + return nonceView + } + } +} \ No newline at end of file diff --git a/voice/src/main/kotlin/encryption/VoiceEncryption.kt b/voice/src/main/kotlin/encryption/VoiceEncryption.kt index b5ddb2088f6..958208af9b1 100644 --- a/voice/src/main/kotlin/encryption/VoiceEncryption.kt +++ b/voice/src/main/kotlin/encryption/VoiceEncryption.kt @@ -1,157 +1,68 @@ package dev.kord.voice.encryption -import com.iwebpp.crypto.TweetNaclFast import dev.kord.voice.EncryptionMode -import dev.kord.voice.encryption.strategies.LiteNonceStrategy -import dev.kord.voice.encryption.strategies.NonceStrategy import dev.kord.voice.io.ByteArrayView import dev.kord.voice.io.MutableByteArrayCursor -import dev.kord.voice.io.mutableCursor -import dev.kord.voice.io.view import dev.kord.voice.udp.RTPPacket -import javax.crypto.Cipher -import javax.crypto.spec.GCMParameterSpec -import javax.crypto.spec.SecretKeySpec -public sealed interface VoiceEncryption { +// TODO: improve kdoc +// TODO: improve nonce methods to reduce unnecessary byte copying. +// TODO: commonize nonce methods. + +public interface VoiceEncryption { public val nonceLength: Int + /** + * The [EncryptionMode] this encryption strategy fulfills. + */ public val mode: EncryptionMode /** - * Whether this encryption mode supports decryption. + * Creates a [Box] instance for the specified [key bytes][key] + * + * @param key A byte array containing the 256-bit key material. */ - public val supportsDecryption: Boolean - public fun createBox(key: ByteArray): Box + /** + * Creates a [Box] instance for the specified [key bytes][key] + * + * @param key A byte array containing the 256-bit key material. + */ public fun createUnbox(key: ByteArray): Unbox - public data class XSalsaPoly1305( - public val nonceStrategyFactory: NonceStrategy.Factory = LiteNonceStrategy, - ) : VoiceEncryption { - override val supportsDecryption: Boolean get() = true - override val mode: EncryptionMode get() = nonceStrategyFactory.mode - override val nonceLength: Int get() = 24 - - override fun createBox(key: ByteArray): Box = object : Box { - private val codec: XSalsa20Poly1305Codec = XSalsa20Poly1305Codec(key) - private val nonceStrategy: NonceStrategy = nonceStrategyFactory.create() - - override val overhead: Int - get() = TweetNaclFast.SecretBox.boxzerobytesLength + nonceStrategyFactory.length - - override fun encrypt(src: ByteArray, nonce: ByteArray, dst: MutableByteArrayCursor): Boolean { - return codec.encrypt(src, 0, src.size, nonce, dst) - } - - override fun generateNonce(header: () -> ByteArrayView): ByteArrayView { - return nonceStrategy.generate(header) - } - - override fun appendNonce(nonce: ByteArrayView, dst: MutableByteArrayCursor) { - nonceStrategy.append(nonce, dst) - } - } - - override fun createUnbox(key: ByteArray): Unbox = object : Unbox { - private val codec: XSalsa20Poly1305Codec = XSalsa20Poly1305Codec(key) - private val nonceStrategy: NonceStrategy = nonceStrategyFactory.create() - - override fun decrypt( - src: ByteArray, - srcOff: Int, - srcLen: Int, - nonce: ByteArray, - dst: MutableByteArrayCursor, - ): Boolean = codec.decrypt(src, srcOff, srcLen, nonce, dst) - - override fun getNonce(packet: RTPPacket): ByteArrayView = nonceStrategy.strip(packet) - } - } - - public data object AeadAes256Gcm : VoiceEncryption { - private const val AUTH_TAG_LEN = 16 - private const val NONCE_LEN = 4 - private const val IV_LEN = 12 - - override val supportsDecryption: Boolean get() = false - override val mode: EncryptionMode get() = EncryptionMode.AeadAes256Gcm - override val nonceLength: Int get() = 4 - - override fun createBox(key: ByteArray): Box = object : Box { - private val iv = ByteArray(IV_LEN) - private val ivCursor = iv.mutableCursor() - - private var nonce = 0u - private val nonceBuffer: ByteArray = ByteArray(NONCE_LEN) - private val nonceCursor = nonceBuffer.mutableCursor() - private val nonceView = nonceBuffer.view() - - val secretKey = SecretKeySpec(key, "AES") - val cipher = Cipher.getInstance("AES/GCM/NoPadding") - - override val overhead: Int - get() = AUTH_TAG_LEN + NONCE_LEN - - override fun encrypt(src: ByteArray, nonce: ByteArray, dst: MutableByteArrayCursor): Boolean { - iv.fill(0) - ivCursor.reset() - ivCursor.writeByteArray(nonce) - - cipher.init( - Cipher.ENCRYPT_MODE, secretKey, - GCMParameterSpec(AUTH_TAG_LEN * 8, iv, 0, IV_LEN) - ) - cipher.updateAAD(dst.data.copyOfRange(0, dst.cursor)) - dst.cursor += cipher.doFinal(src, 0, src.size, dst.data, dst.cursor) - - return true - } - - override fun appendNonce(nonce: ByteArrayView, dst: MutableByteArrayCursor) { - dst.writeByteView(nonce) - } - - override fun generateNonce(header: () -> ByteArrayView): ByteArrayView { - nonceCursor.reset() - nonceCursor.writeInt(nonce++.toInt()) - return nonceView - } - } + /** + * A common interface for (un)boxing voice packets. + */ + public sealed interface Method { - override fun createUnbox(key: ByteArray): Unbox { - throw UnsupportedOperationException() - } + public fun apply(src: ByteArrayView, nonce: ByteArray, dst: MutableByteArrayCursor): Boolean } - public interface Box { + public interface Box : Method { + /** + * The number of extra bytes this [Box] generates. + */ public val overhead: Int - public fun encrypt(src: ByteArray, nonce: ByteArray, dst: MutableByteArrayCursor): Boolean - + /** + * + */ public fun generateNonce(header: () -> ByteArrayView): ByteArrayView + /** + * Appends the specified [nonce buffer][nonce] to the [destination cursor][dst] + */ public fun appendNonce(nonce: ByteArrayView, dst: MutableByteArrayCursor) } - public interface Unbox { - /** - * Decrypt the packet. - */ - public fun decrypt( - src: ByteArray, - srcOff: Int, - srcLen: Int, - nonce: ByteArray, - dst: MutableByteArrayCursor, - ): Boolean + public interface Unbox : Method { /** - * Strip the nonce from the [RTP packet][packet]. + * Returns the nonce from the given [RTP packet][packet]. * * @return the nonce. */ public fun getNonce(packet: RTPPacket): ByteArrayView } -} \ No newline at end of file +} diff --git a/voice/src/main/kotlin/encryption/XSalsa20Poly1305.kt b/voice/src/main/kotlin/encryption/XSalsa20Poly1305.kt new file mode 100644 index 00000000000..50c65a47881 --- /dev/null +++ b/voice/src/main/kotlin/encryption/XSalsa20Poly1305.kt @@ -0,0 +1,95 @@ +package dev.kord.voice.encryption + +import com.iwebpp.crypto.TweetNaclFast +import com.iwebpp.crypto.TweetNaclFast.SecretBox.boxzerobytesLength +import com.iwebpp.crypto.TweetNaclFast.SecretBox.zerobytesLength +import dev.kord.voice.EncryptionMode +import dev.kord.voice.encryption.strategies.LiteNonceStrategy +import dev.kord.voice.encryption.strategies.NonceStrategy +import dev.kord.voice.io.ByteArrayView +import dev.kord.voice.io.MutableByteArrayCursor +import dev.kord.voice.udp.RTPPacket + +/** + * An [encryption method][VoiceEncryption] that uses the XSalsa20 stream cipher and Poly1035 hash function. + */ +public data class XSalsa20Poly1305(public val nonceStrategyFactory: NonceStrategy.Factory = LiteNonceStrategy) : VoiceEncryption { + override val mode: EncryptionMode get() = nonceStrategyFactory.mode + + override val nonceLength: Int get() = 24 + + override fun createBox(key: ByteArray): VoiceEncryption.Box = Impl.Box(key, nonceStrategyFactory) + + override fun createUnbox(key: ByteArray): VoiceEncryption.Unbox = Impl.Unbox(key, nonceStrategyFactory) + + public sealed class Impl(protected val nonceStrategyFactory: NonceStrategy.Factory) { + protected val nonceStrategy: NonceStrategy = nonceStrategyFactory.create() + + // this class is only used internally and is used for encrypting opus packets. + // we can know the maximum sized buffer required to store any opus packet. + protected val m: ByteArray = ByteArray(OPUS_MAX_LENGTH + zerobytesLength) + protected val c: ByteArray = ByteArray(OPUS_MAX_LENGTH + zerobytesLength) + + public class Box( + private val key: ByteArray, + nonceStrategyFactory: NonceStrategy.Factory, + ) : VoiceEncryption.Box, Impl(nonceStrategyFactory) { + override val overhead: Int + get() = boxzerobytesLength + nonceStrategyFactory.length + + override fun apply(src: ByteArrayView, nonce: ByteArray, dst: MutableByteArrayCursor): Boolean { + m.fill(0) + c.fill(0) + + for (i in 0.. ByteArrayView): ByteArrayView = + nonceStrategy.generate(header) + + override fun appendNonce(nonce: ByteArrayView, dst: MutableByteArrayCursor): Unit = + nonceStrategy.append(nonce, dst) + } + + public class Unbox( + private val key: ByteArray, + nonceStrategyFactory: NonceStrategy.Factory, + ) : VoiceEncryption.Unbox, Impl(nonceStrategyFactory) { + override fun apply(src: ByteArrayView, nonce: ByteArray, dst: MutableByteArrayCursor): Boolean { + c.fill(0) + m.fill(0) + + for (i in 0...decrypt(encryption: VoiceEncryption, key: ByteArray) nonceBuffer.writeByteView(unbox.getNonce(packet)) - val decrypted = with(packet.payload) { - unbox.decrypt(data, dataStart, viewSize, nonceBuffer.data, decryptedCursor) - } - + val decrypted = unbox.apply(packet.payload, nonceBuffer.data, decryptedCursor) if (!decrypted) { defaultStreamsLogger.trace { "failed to decrypt the packet with data ${packet.payload.data.contentToString()} at offset ${packet.payload.dataStart} and length ${packet.payload.viewSize - 4}" } return@mapNotNull null diff --git a/voice/src/main/kotlin/udp/AudioPacketProvider.kt b/voice/src/main/kotlin/udp/AudioPacketProvider.kt index 90821c674aa..656f9f65079 100644 --- a/voice/src/main/kotlin/udp/AudioPacketProvider.kt +++ b/voice/src/main/kotlin/udp/AudioPacketProvider.kt @@ -13,7 +13,10 @@ public abstract class AudioPacketProvider(public val key: ByteArray, public val private class CouldNotEncryptDataException(data: ByteArray) : RuntimeException("Couldn't encrypt the following data: [${data.joinToString(", ")}]") -public class DefaultAudioPacketProvider(key: ByteArray, encryption: VoiceEncryption) : AudioPacketProvider(key, encryption) { +public class DefaultAudioPacketProvider(key: ByteArray, encryption: VoiceEncryption) : AudioPacketProvider( + key, + encryption +) { private val box = encryption.createBox(key) private val packetBuffer = ByteArray(2048) @@ -50,8 +53,7 @@ public class DefaultAudioPacketProvider(key: ByteArray, encryption: VoiceEncrypt nonceBuffer.writeByteView(rawNonce) // encrypt data and write into our buffer - val encrypted = box.encrypt(data, nonceBuffer.data, this) - + val encrypted = box.apply(data.view(), nonceBuffer.data, this) if (!encrypted) throw CouldNotEncryptDataException(data) box.appendNonce(rawNonce, this) diff --git a/voice/src/main/kotlin/udp/DefaultAudioFrameSender.kt b/voice/src/main/kotlin/udp/DefaultAudioFrameSender.kt index e7c44b9ec75..8a59cce29f9 100644 --- a/voice/src/main/kotlin/udp/DefaultAudioFrameSender.kt +++ b/voice/src/main/kotlin/udp/DefaultAudioFrameSender.kt @@ -5,7 +5,6 @@ import dev.kord.voice.AudioFrame import dev.kord.voice.AudioProvider import dev.kord.voice.FrameInterceptor import dev.kord.voice.encryption.VoiceEncryption -import dev.kord.voice.encryption.strategies.NonceStrategy import io.github.oshai.kotlinlogging.KotlinLogging import io.ktor.network.sockets.* import io.ktor.utils.io.core.* From 999685cfcf144ef288685534e0e407b8794f0276 Mon Sep 17 00:00:00 2001 From: viztea Date: Wed, 5 Jun 2024 01:45:42 -0700 Subject: [PATCH 11/12] fix(voice): working decryption --- .../main/kotlin/encryption/AeadAes256Gcm.kt | 42 ++++++++++++------- .../main/kotlin/encryption/VoiceEncryption.kt | 2 +- .../kotlin/encryption/XSalsa20Poly1305.kt | 4 +- .../src/main/kotlin/streams/DefaultStreams.kt | 16 +++++-- .../main/kotlin/udp/AudioPacketProvider.kt | 2 +- 5 files changed, 42 insertions(+), 24 deletions(-) diff --git a/voice/src/main/kotlin/encryption/AeadAes256Gcm.kt b/voice/src/main/kotlin/encryption/AeadAes256Gcm.kt index da34c796a58..fa08d955480 100644 --- a/voice/src/main/kotlin/encryption/AeadAes256Gcm.kt +++ b/voice/src/main/kotlin/encryption/AeadAes256Gcm.kt @@ -32,10 +32,6 @@ public data object AeadAes256Gcm : VoiceEncryption { protected val iv = ByteArray(IV_LEN) protected val ivCursor = iv.mutableCursor() - protected open val nonceBuffer: ByteArray = ByteArray(NONCE_LEN) - protected val nonceCursor by lazy { nonceBuffer.mutableCursor() } - protected val nonceView by lazy { nonceBuffer.view() } - protected val cipherKey = SecretKeySpec(key, "AES") protected val cipher: Cipher = Cipher.getInstance("AES/GCM/NoPadding") @@ -43,6 +39,7 @@ public data object AeadAes256Gcm : VoiceEncryption { mode: Int, src: ByteArrayView, dst: MutableByteArrayCursor, + aead: ByteArrayView, nonce: ByteArray, writeNonce: MutableByteArrayCursor.(nonce: ByteArray) -> Unit, ): Boolean { @@ -51,7 +48,7 @@ public data object AeadAes256Gcm : VoiceEncryption { ivCursor.apply { writeNonce(nonce) } init(mode) - cipher.updateAAD(dst.data.copyOfRange(0, dst.cursor)) + cipher.updateAAD(aead.data, aead.dataStart, aead.viewSize) dst.cursor += cipher.doFinal(src.data, src.dataStart, src.viewSize, dst.data, dst.cursor) return true @@ -63,11 +60,19 @@ public data object AeadAes256Gcm : VoiceEncryption { } private class BoxImpl(key: ByteArray) : Box, Common(key) { + val nonceBuffer: ByteArray = ByteArray(NONCE_LEN) + val nonceCursor by lazy { nonceBuffer.mutableCursor() } + val nonceView by lazy { nonceBuffer.view() } + override val overhead: Int get() = AUTH_TAG_LEN + NONCE_LEN - override fun apply(src: ByteArrayView, nonce: ByteArray, dst: MutableByteArrayCursor): Boolean = - apply(Cipher.ENCRYPT_MODE, src, dst, nonce, MutableByteArrayCursor::writeByteArray) + override fun apply( + src: ByteArrayView, + dst: MutableByteArrayCursor, + aead: ByteArrayView, + nonce: ByteArray, + ): Boolean = apply(Cipher.ENCRYPT_MODE, src, dst, aead, nonce, MutableByteArrayCursor::writeByteArray) override fun appendNonce(nonce: ByteArrayView, dst: MutableByteArrayCursor) { dst.writeByteView(nonce) @@ -81,17 +86,22 @@ public data object AeadAes256Gcm : VoiceEncryption { } private class UnboxImpl(key: ByteArray) : Unbox, Common(key) { - // Since RTPPacket expects a container big enough to fit the entire header this will - // need to be 12 bytes (or the min length of an RTP header). We will only use the - // first NONCE_LEN bytes, though. - override val nonceBuffer: ByteArray = ByteArray(12) + override fun apply( + src: ByteArrayView, + dst: MutableByteArrayCursor, + aead: ByteArrayView, + nonce: ByteArray, + ): Boolean = apply(Cipher.DECRYPT_MODE, src, dst, aead, nonce) { writeByteView(it.view(0, NONCE_LEN)!!) } - override fun apply(src: ByteArrayView, nonce: ByteArray, dst: MutableByteArrayCursor): Boolean = - apply(Cipher.DECRYPT_MODE, src, dst, nonce) { writeByteView(it.view(0, NONCE_LEN)!!) } + override fun getNonce(packet: RTPPacket): ByteArrayView = with(packet.payload) { + // grab the last NONCE_LEN bytes of the packet payload. + val nonce = view(dataEnd - NONCE_LEN, dataEnd) + ?: error("Failed to strip nonce from RTP packet payload.") - override fun getNonce(packet: RTPPacket): ByteArrayView { - packet.writeHeader(nonceCursor) - return nonceView + // resize the payload view to exclude the nonce. + resize(0, dataEnd - NONCE_LEN) + + return nonce } } } \ No newline at end of file diff --git a/voice/src/main/kotlin/encryption/VoiceEncryption.kt b/voice/src/main/kotlin/encryption/VoiceEncryption.kt index 958208af9b1..9e3bf449283 100644 --- a/voice/src/main/kotlin/encryption/VoiceEncryption.kt +++ b/voice/src/main/kotlin/encryption/VoiceEncryption.kt @@ -36,7 +36,7 @@ public interface VoiceEncryption { */ public sealed interface Method { - public fun apply(src: ByteArrayView, nonce: ByteArray, dst: MutableByteArrayCursor): Boolean + public fun apply(src: ByteArrayView, dst: MutableByteArrayCursor, aead: ByteArrayView, nonce: ByteArray): Boolean } public interface Box : Method { diff --git a/voice/src/main/kotlin/encryption/XSalsa20Poly1305.kt b/voice/src/main/kotlin/encryption/XSalsa20Poly1305.kt index 50c65a47881..ee5e9774650 100644 --- a/voice/src/main/kotlin/encryption/XSalsa20Poly1305.kt +++ b/voice/src/main/kotlin/encryption/XSalsa20Poly1305.kt @@ -37,7 +37,7 @@ public data class XSalsa20Poly1305(public val nonceStrategyFactory: NonceStrateg override val overhead: Int get() = boxzerobytesLength + nonceStrategyFactory.length - override fun apply(src: ByteArrayView, nonce: ByteArray, dst: MutableByteArrayCursor): Boolean { + override fun apply(src: ByteArrayView, dst: MutableByteArrayCursor, aead: ByteArrayView, nonce: ByteArray): Boolean { m.fill(0) c.fill(0) @@ -65,7 +65,7 @@ public data class XSalsa20Poly1305(public val nonceStrategyFactory: NonceStrateg private val key: ByteArray, nonceStrategyFactory: NonceStrategy.Factory, ) : VoiceEncryption.Unbox, Impl(nonceStrategyFactory) { - override fun apply(src: ByteArrayView, nonce: ByteArray, dst: MutableByteArrayCursor): Boolean { + override fun apply(src: ByteArrayView, dst: MutableByteArrayCursor, aead: ByteArrayView, nonce: ByteArray): Boolean { c.fill(0) m.fill(0) diff --git a/voice/src/main/kotlin/streams/DefaultStreams.kt b/voice/src/main/kotlin/streams/DefaultStreams.kt index 907deb53a79..5a8956f2d9a 100644 --- a/voice/src/main/kotlin/streams/DefaultStreams.kt +++ b/voice/src/main/kotlin/streams/DefaultStreams.kt @@ -88,18 +88,26 @@ private fun Flow.decrypt(encryption: VoiceEncryption, key: ByteArray) val unbox = encryption.createUnbox(key) val nonceBuffer = ByteArray(encryption.nonceLength).mutableCursor() + val aeadBuffer = ByteArray(12) + val aeadCursor = aeadBuffer.mutableCursor() + val aeadView = aeadBuffer.view() + val decryptedBuffer = ByteArray(512) val decryptedCursor = decryptedBuffer.mutableCursor() val decryptedView = decryptedBuffer.view() return mapNotNull { packet -> + // strip and write the nonce. nonceBuffer.reset() - decryptedCursor.reset() - nonceBuffer.writeByteView(unbox.getNonce(packet)) - val decrypted = unbox.apply(packet.payload, nonceBuffer.data, decryptedCursor) - if (!decrypted) { + // write the RTP packet header to the AEAD buffer. + aeadCursor.reset() + packet.writeHeader(aeadCursor) + + // decrypt the RTP packet payload. + decryptedCursor.reset() + if (!unbox.apply(packet.payload, decryptedCursor, aeadView, nonceBuffer.data)) { defaultStreamsLogger.trace { "failed to decrypt the packet with data ${packet.payload.data.contentToString()} at offset ${packet.payload.dataStart} and length ${packet.payload.viewSize - 4}" } return@mapNotNull null } diff --git a/voice/src/main/kotlin/udp/AudioPacketProvider.kt b/voice/src/main/kotlin/udp/AudioPacketProvider.kt index 656f9f65079..5f23de7641e 100644 --- a/voice/src/main/kotlin/udp/AudioPacketProvider.kt +++ b/voice/src/main/kotlin/udp/AudioPacketProvider.kt @@ -53,7 +53,7 @@ public class DefaultAudioPacketProvider(key: ByteArray, encryption: VoiceEncrypt nonceBuffer.writeByteView(rawNonce) // encrypt data and write into our buffer - val encrypted = box.apply(data.view(), nonceBuffer.data, this) + val encrypted = box.apply(data.view(), this, rtpHeaderView, nonceBuffer.data) if (!encrypted) throw CouldNotEncryptDataException(data) box.appendNonce(rawNonce, this) From 63b938eb63ba4df4a63a888cdd5f18382d0f145b Mon Sep 17 00:00:00 2001 From: viztea Date: Wed, 5 Jun 2024 01:46:32 -0700 Subject: [PATCH 12/12] chore: api dump --- voice/api/voice.api | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/voice/api/voice.api b/voice/api/voice.api index 50c6f861f92..2285ba5b51f 100644 --- a/voice/api/voice.api +++ b/voice/api/voice.api @@ -430,7 +430,7 @@ public abstract interface class dev/kord/voice/encryption/VoiceEncryption$Box : } public abstract interface class dev/kord/voice/encryption/VoiceEncryption$Method { - public abstract fun apply (Ldev/kord/voice/io/ByteArrayView;[BLdev/kord/voice/io/MutableByteArrayCursor;)Z + public abstract fun apply (Ldev/kord/voice/io/ByteArrayView;Ldev/kord/voice/io/MutableByteArrayCursor;Ldev/kord/voice/io/ByteArrayView;[B)Z } public abstract interface class dev/kord/voice/encryption/VoiceEncryption$Unbox : dev/kord/voice/encryption/VoiceEncryption$Method { @@ -469,14 +469,14 @@ public abstract class dev/kord/voice/encryption/XSalsa20Poly1305$Impl { public final class dev/kord/voice/encryption/XSalsa20Poly1305$Impl$Box : dev/kord/voice/encryption/XSalsa20Poly1305$Impl, dev/kord/voice/encryption/VoiceEncryption$Box { public fun ([BLdev/kord/voice/encryption/strategies/NonceStrategy$Factory;)V public fun appendNonce (Ldev/kord/voice/io/ByteArrayView;Ldev/kord/voice/io/MutableByteArrayCursor;)V - public fun apply (Ldev/kord/voice/io/ByteArrayView;[BLdev/kord/voice/io/MutableByteArrayCursor;)Z + public fun apply (Ldev/kord/voice/io/ByteArrayView;Ldev/kord/voice/io/MutableByteArrayCursor;Ldev/kord/voice/io/ByteArrayView;[B)Z public fun generateNonce (Lkotlin/jvm/functions/Function0;)Ldev/kord/voice/io/ByteArrayView; public fun getOverhead ()I } public final class dev/kord/voice/encryption/XSalsa20Poly1305$Impl$Unbox : dev/kord/voice/encryption/XSalsa20Poly1305$Impl, dev/kord/voice/encryption/VoiceEncryption$Unbox { public fun ([BLdev/kord/voice/encryption/strategies/NonceStrategy$Factory;)V - public fun apply (Ldev/kord/voice/io/ByteArrayView;[BLdev/kord/voice/io/MutableByteArrayCursor;)Z + public fun apply (Ldev/kord/voice/io/ByteArrayView;Ldev/kord/voice/io/MutableByteArrayCursor;Ldev/kord/voice/io/ByteArrayView;[B)Z public fun getNonce (Ldev/kord/voice/udp/RTPPacket;)Ldev/kord/voice/io/ByteArrayView; }