diff --git a/src/Discord.Net.Net45/Discord.Net.csproj b/src/Discord.Net.Net45/Discord.Net.csproj
index 9b15aac0c..40678a4d4 100644
--- a/src/Discord.Net.Net45/Discord.Net.csproj
+++ b/src/Discord.Net.Net45/Discord.Net.csproj
@@ -62,6 +62,9 @@
PreserveNewest
+
+ PreserveNewest
+
PreserveNewest
@@ -101,6 +104,9 @@
Audio\OpusEncoder.cs
+
+ Audio\Sodium.cs
+
Collections\AsyncCollection.cs
diff --git a/src/Discord.Net.Net45/lib/libsodium.dll b/src/Discord.Net.Net45/lib/libsodium.dll
new file mode 100644
index 000000000..a9ab5078e
Binary files /dev/null and b/src/Discord.Net.Net45/lib/libsodium.dll differ
diff --git a/src/Discord.Net/Audio/Opus.cs b/src/Discord.Net/Audio/Opus.cs
index b2c550e93..021165cfd 100644
--- a/src/Discord.Net/Audio/Opus.cs
+++ b/src/Discord.Net/Audio/Opus.cs
@@ -3,7 +3,7 @@ using System.Runtime.InteropServices;
namespace Discord.Audio
{
- internal unsafe class Opus
+ internal static unsafe class Opus
{
[DllImport("lib/opus", EntryPoint = "opus_encoder_create", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr CreateEncoder(int Fs, int channels, int application, out Error error);
diff --git a/src/Discord.Net/Audio/Sodium.cs b/src/Discord.Net/Audio/Sodium.cs
new file mode 100644
index 000000000..793eb61a9
--- /dev/null
+++ b/src/Discord.Net/Audio/Sodium.cs
@@ -0,0 +1,15 @@
+using System.Runtime.InteropServices;
+
+namespace Discord.Audio
+{
+ internal static class Sodium
+ {
+ [DllImport("lib/libsodium", EntryPoint = "crypto_stream_xor", CallingConvention = CallingConvention.Cdecl)]
+ private static extern int StreamXOR(byte[] output, byte[] msg, long msgLength, byte[] nonce, byte[] secret);
+
+ public static int Encrypt(byte[] buffer, int inputLength, byte[] output, byte[] nonce, byte[] secret)
+ {
+ return StreamXOR(output, buffer, inputLength, nonce, secret);
+ }
+ }
+}
diff --git a/src/Discord.Net/DiscordClientConfig.cs b/src/Discord.Net/DiscordClientConfig.cs
index 7c623ae3e..a483b8b2a 100644
--- a/src/Discord.Net/DiscordClientConfig.cs
+++ b/src/Discord.Net/DiscordClientConfig.cs
@@ -4,7 +4,7 @@ namespace Discord
{
public class DiscordClientConfig
{
- /// Specifies the minimum log level severity that will be sent to the LogMessage event. Warning: setting this to verbose will hinder performance but should help investigate any internal issues.
+ /// Specifies the minimum log level severity that will be sent to the LogMessage event. Warning: setting this to debug will really hurt performance but should help investigate any internal issues.
public LogMessageSeverity LogLevel { get { return _logLevel; } set { SetValue(ref _logLevel, value); } }
private LogMessageSeverity _logLevel = LogMessageSeverity.Info;
@@ -36,8 +36,12 @@ namespace Discord
/// (Experimental) Enables the voice websocket and UDP client. This option requires the opus .dll or .so be in the local lib/ folder.
public bool EnableVoice { get { return _enableVoice; } set { SetValue(ref _enableVoice, value); } }
private bool _enableVoice = false;
+ /// (Experimental) Enables the voice websocket and UDP client. This option requires the libsodium .dll or .so be in the local lib/ folder.
+ public bool EnableVoiceEncryption { get { return _enableVoiceEncryption; } set { SetValue(ref _enableVoiceEncryption, value); } }
+ private bool _enableVoiceEncryption = false;
#else
internal bool EnableVoice => false;
+ internal bool EnableVoiceEncryption => false;
#endif
/// (Experimental) Enables or disables the internal message queue. This will allow SendMessage to return immediately and handle messages internally. Messages will set the IsQueued and HasFailed properties to show their progress.
public bool UseMessageQueue { get { return _useMessageQueue; } set { SetValue(ref _useMessageQueue, value); } }
diff --git a/src/Discord.Net/WebSockets/Voice/VoiceWebSocket.cs b/src/Discord.Net/WebSockets/Voice/VoiceWebSocket.cs
index 054bd0882..a27cd4732 100644
--- a/src/Discord.Net/WebSockets/Voice/VoiceWebSocket.cs
+++ b/src/Discord.Net/WebSockets/Voice/VoiceWebSocket.cs
@@ -33,7 +33,7 @@ namespace Discord.WebSockets.Voice
private byte[] _secretKey;
private ushort _sequence;
private byte[] _encodingBuffer;
- private string _serverId, _userId, _sessionId, _token;
+ private string _serverId, _userId, _sessionId, _token, _encryptionMode;
#if USE_THREAD
private Thread _sendThread;
@@ -213,12 +213,18 @@ namespace Discord.WebSockets.Voice
Stopwatch sw = Stopwatch.StartNew();
byte[] rtpPacket = new byte[_encodingBuffer.Length + 12];
+ byte[] nonce = null;
rtpPacket[0] = 0x80; //Flags;
rtpPacket[1] = 0x78; //Payload Type
rtpPacket[8] = (byte)((_ssrc >> 24) & 0xFF);
rtpPacket[9] = (byte)((_ssrc >> 16) & 0xFF);
rtpPacket[10] = (byte)((_ssrc >> 8) & 0xFF);
rtpPacket[11] = (byte)((_ssrc >> 0) & 0xFF);
+ if (_isEncrypted)
+ {
+ nonce = new byte[24];
+ Buffer.BlockCopy(rtpPacket, 0, nonce, 0, 12);
+ }
while (!cancelToken.IsCancellationRequested)
{
@@ -238,6 +244,13 @@ namespace Discord.WebSockets.Voice
rtpPacket[5] = (byte)((timestamp >> 16) & 0xFF);
rtpPacket[6] = (byte)((timestamp >> 8) & 0xFF);
rtpPacket[7] = (byte)((timestamp >> 0) & 0xFF);
+ if (_isEncrypted)
+ {
+ Buffer.BlockCopy(rtpPacket, 2, nonce, 2, 6); //Update nonce
+ int ret = Sodium.Encrypt(packet, packet.Length, packet, nonce, _secretKey);
+ if (ret != 0)
+ continue;
+ }
Buffer.BlockCopy(packet, 0, rtpPacket, 12, packet.Length);
#if USE_THREAD
_udp.Send(rtpPacket, packet.Length + 12);
@@ -298,8 +311,22 @@ namespace Discord.WebSockets.Voice
_heartbeatInterval = payload.HeartbeatInterval;
_ssrc = payload.SSRC;
_endpoint = new IPEndPoint((await Dns.GetHostAddressesAsync(Host.Replace("wss://", "")).ConfigureAwait(false)).FirstOrDefault(), payload.Port);
- //_mode = payload.Modes.LastOrDefault();
- _isEncrypted = !payload.Modes.Contains("plain");
+
+ if (_client.Config.EnableVoiceEncryption)
+ {
+ if (payload.Modes.Contains(EncryptedMode))
+ {
+ _encryptionMode = EncryptedMode;
+ _isEncrypted = true;
+ }
+ else
+ throw new InvalidOperationException("Unexpected encryption format.");
+ }
+ else
+ {
+ _encryptionMode = UnencryptedMode;
+ _isEncrypted = false;
+ }
_udp.Connect(_endpoint);
_sequence = (ushort)_rand.Next(0, ushort.MaxValue);
@@ -361,7 +388,7 @@ namespace Discord.WebSockets.Voice
var login2 = new Login2Command();
login2.Payload.Protocol = "udp";
login2.Payload.SocketData.Address = ip;
- login2.Payload.SocketData.Mode = _isEncrypted ? EncryptedMode : UnencryptedMode;
+ login2.Payload.SocketData.Mode = _encryptionMode;
login2.Payload.SocketData.Port = port;
QueueMessage(login2);
}
diff --git a/src/Discord.Net/lib/libsodium.dll b/src/Discord.Net/lib/libsodium.dll
new file mode 100644
index 000000000..a9ab5078e
Binary files /dev/null and b/src/Discord.Net/lib/libsodium.dll differ