-
Notifications
You must be signed in to change notification settings - Fork 82
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement AES Encryption for Voice #897
Draft
viztea
wants to merge
13
commits into
kordlib:main
Choose a base branch
from
keia-bot:feat/voice-aes
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from 1 commit
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
b22f710
feat(voice): overhaul encryption + implement aes
viztea a6356cd
chore(voice): api dump
viztea 39528de
fix(voice): aes128 -> aes256
viztea e8e4402
fix(voice): remove println
viztea 5f87178
chore: api dump
viztea 137a36d
chore: rename nsf var, make xsalsa data class
viztea 1a171b9
chore: remove default value
viztea 5000114
chore: fix formatting
viztea fafa5c1
chore: api dump
viztea 7f0bc34
Merge branch 'main' into feat/voice-aes
viztea 65de968
feat(voice): cleanup + aes decryption
viztea 999685c
fix(voice): working decryption
viztea 63b938e
chore: api dump
viztea File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
viztea marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
public fun createBox(key: ByteArray): Box | ||
|
||
public fun createUnbox(key: ByteArray): Unbox | ||
|
||
@JvmInline | ||
public value class XSalsaPoly1305(public val nsf: NonceStrategy.Factory = LiteNonceStrategy) : VoiceEncryption { | ||
viztea marked this conversation as resolved.
Show resolved
Hide resolved
viztea marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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 { | ||
// | ||
viztea marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why do we need this factory? i'd rather not add it if not needed. |
||
/** | ||
*/ | ||
public val mode: EncryptionMode | ||
|
||
/** | ||
* The amount of bytes this nonce will take up. | ||
*/ | ||
public val nonceLength: Int | ||
|
||
public fun create(): NonceStrategy | ||
} | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be useful to log if the user's chosen encryption method ends up not being used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or even throw an exception? but logging a warning is probably best.