public void VerifySecretBoxEncryptedMessageCanBeDecrypted()
        {
            var sb = new SecretBox();

            // Generate a key
            var key = new byte[SecretBox.KeyBytes];

            sb.GenerateKey(key);

            // Generate a message to encrypt
            var          message   = Encoding.UTF8.GetBytes("You are old Father William, the young man said");
            const int    messageId = 1;
            const string context   = "test";

            // Buffer to hold the ciphertext
            var ciphertext = new byte[sb.CalculateCiphertextLength(message.Length)];

            // Encrypt using SecretBox
            sb.Encrypt(ciphertext, message, message.Length, key, context, messageId);

            // Verify that some ciphertext was generated
            Assert.That(ciphertext, Is.Not.All.Zero);

            // Decrypt using libhydrogen
            var decryptedMessage = new byte[message.Length];
            var result           = hydro_secretbox_decrypt(
                decryptedMessage, ciphertext, ciphertext.Length, messageId, context, key);

            // Verify the decrypt was successful
            Assert.That(result, Is.EqualTo(0));
            Assert.That(decryptedMessage, Is.EqualTo(message));
        }
예제 #2
0
        public void VerifyDecryptFailsWithInvalidParameters()
        {
            // Encrypt a message
            var sb  = new SecretBox();
            var key = new byte[KeyBytes];

            sb.GenerateKey(key);
            var          message    = Encoding.UTF8.GetBytes("You are old Father William, the young man said");
            const int    messageId  = 1;
            const string context    = "test";
            var          ciphertext = new byte[sb.CalculateCiphertextLength(message.Length)];

            sb.Encrypt(ciphertext, message, message.Length, key, context, messageId);

            // Buffer to hold decrypted message
            var decryptedMessage = new byte[message.Length];

            // CiphertextLength is incorrect
            Assert.That(
                () => sb.Decrypt(decryptedMessage, ciphertext, HeaderBytes, key, context, messageId),
                Throws.TypeOf <CryptographicException>().With.Message.EqualTo("MAC check failed"));
            Assert.That(
                sb.TryDecrypt(decryptedMessage, ciphertext, HeaderBytes, key, context, messageId),
                Is.False);

            // MessageId is incorrect
            Assert.That(
                () => sb.Decrypt(decryptedMessage, ciphertext, ciphertext.Length, key, context, 2),
                Throws.TypeOf <CryptographicException>().With.Message.EqualTo("MAC check failed"));

            // Verify the decrypted message is not equal to the message, as a failed MAC check should not
            // leak the plaintext
            Assert.That(decryptedMessage, Is.Not.EqualTo(message));

            Assert.That(
                sb.TryDecrypt(decryptedMessage, ciphertext, ciphertext.Length, key, context, 2),
                Is.False);
            Assert.That(decryptedMessage, Is.Not.EqualTo(message));

            // Key is invalid
            key[0]++;
            Assert.That(
                () => sb.Decrypt(decryptedMessage, ciphertext, ciphertext.Length, key, context, messageId),
                Throws.TypeOf <CryptographicException>().With.Message.EqualTo("MAC check failed"));
            Assert.That(
                sb.TryDecrypt(decryptedMessage, ciphertext, ciphertext.Length, key, context, messageId),
                Is.False);
            key[0]--;

            // Ciphertext is invalid
            ciphertext[12]++;
            Assert.That(
                () => sb.Decrypt(decryptedMessage, ciphertext, ciphertext.Length, key, context, messageId),
                Throws.TypeOf <CryptographicException>().With.Message.EqualTo("MAC check failed"));
            Assert.That(
                sb.TryDecrypt(decryptedMessage, ciphertext, ciphertext.Length, key, context, messageId),
                Is.False);
        }
예제 #3
0
        public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancelToken)
        {
            cancelToken.ThrowIfCancellationRequested();

            if (_client.SecretKey == null)
            {
                return;
            }

            Buffer.BlockCopy(buffer, offset, _nonce, 0, 12); //Copy nonce from RTP header
            count = SecretBox.Encrypt(buffer, offset + 12, count - 12, buffer, 12, _nonce, _client.SecretKey);
            await _next.WriteAsync(buffer, 0, count + 12, cancelToken).ConfigureAwait(false);
        }
예제 #4
0
        public void Encrypt_ValidateContextLength_TooLong()
        {
            var       sb   = new SecretBox();
            var       key  = new byte[KeyBytes];
            const int mlen = 12;
            var       m    = new byte[mlen];
            var       c    = new byte[sb.CalculateCiphertextLength(mlen)];
            var       ctx  = "you are old father william";

            Assert.That(
                () => sb.Encrypt(c, m, mlen, key, ctx),
                Throws.ArgumentException.With.Message.EqualTo(
                    $"'context' must be at most {ContextBytes} characters"));
        }
예제 #5
0
        public void Encrypt_ValidateKeyLength(int keyLength)
        {
            var       sb   = new SecretBox();
            var       key  = new byte[keyLength];
            const int mlen = 12;
            var       m    = new byte[mlen];
            var       c    = new byte[sb.CalculateCiphertextLength(mlen)];
            var       ctx  = "test";

            Assert.That(
                () => sb.Encrypt(c, m, mlen, key, ctx),
                Throws.ArgumentException.With.Message.EqualTo(
                    $"'key' length must be {KeyBytes} bytes"));
        }
예제 #6
0
        public void Encrypt_ValidateCiphertextLength_TooShort()
        {
            var       key  = new byte[KeyBytes];
            const int mlen = 12;
            var       m    = new byte[mlen];
            var       c    = new byte[mlen];
            var       ctx  = "test";

            var sb = new SecretBox();

            Assert.That(
                () => sb.Encrypt(c, m, mlen, key, ctx),
                Throws.ArgumentException.With.Message.EqualTo(
                    $"'ciphertext' length must be at least messageLength + {nameof(HeaderBytes)}"));
        }
예제 #7
0
        public void Encrypt_ValidateMessageLength_TooLong()
        {
            var       sb         = new SecretBox();
            var       key        = new byte[KeyBytes];
            const int mlen       = 44;
            const int mlenActual = 12;
            var       m          = new byte[mlenActual];
            var       c          = new byte[sb.CalculateCiphertextLength(mlenActual)];
            var       ctx        = "test";

            Assert.That(
                () => sb.Encrypt(c, m, mlen, key, ctx),
                Throws.ArgumentException.With.Message.EqualTo(
                    $"'messageLength' must be at most the length of 'message'"));
        }
예제 #8
0
        /// <exception cref="InvalidOperationException">Received payload without an RTP header.</exception>
        /// <exception cref="OperationCanceledException">The token has had cancellation requested.</exception>
        /// <exception cref="ObjectDisposedException">The associated <see cref="T:System.Threading.CancellationTokenSource" /> has been disposed.</exception>
        public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancelToken)
        {
            cancelToken.ThrowIfCancellationRequested();
            if (count < 12)
            {
                throw new InvalidOperationException("Received payload without an RTP header.");
            }

            if (_client.SecretKey == null)
            {
                return;
            }

            Buffer.BlockCopy(buffer, offset, _nonce, 0, 12); //Copy nonce from RTP header
            Buffer.BlockCopy(buffer, offset, _buffer, 0, 12);
            count = SecretBox.Encrypt(buffer, offset + 12, count - 12, _buffer, 12, _nonce, _client.SecretKey);
            await _next.WriteAsync(_buffer, 0, count + 12, cancelToken).ConfigureAwait(false);
        }
예제 #9
0
        public void VerifyMessageCanBeEncryptedAndDecrypted()
        {
            var sb = new SecretBox();

            // Generate a key
            var key = new byte[KeyBytes];

            sb.GenerateKey(key);

            // Generate a message to encrypt
            var          message   = Encoding.UTF8.GetBytes("You are old Father William, the young man said");
            const int    messageId = 1;
            const string context   = "test";

            // Buffer to hold the ciphertext
            var ciphertext = new byte[sb.CalculateCiphertextLength(message.Length)];

            // Encrypt
            sb.Encrypt(ciphertext, message, message.Length, key, context, messageId);

            // Buffer to hold decrypted message
            var decryptedMessage = new byte[message.Length];

            // Decrypt
            sb.Decrypt(decryptedMessage, ciphertext, ciphertext.Length, key, context, messageId);

            // Verify the decrypted message
            Assert.That(decryptedMessage, Is.EqualTo(message));

            // Decrypt using TryDecrypt
            Array.Clear(decryptedMessage, 0, decryptedMessage.Length);
            var result = sb.TryDecrypt(decryptedMessage, ciphertext, ciphertext.Length, key, context, messageId);

            // Verify the decrypted message
            Assert.That(decryptedMessage, Is.EqualTo(message));
            Assert.That(result, Is.True);
        }
예제 #10
0
        private async Task SendVoiceAsync(CancellationToken cancelToken)
        {
            try
            {
                while (!cancelToken.IsCancellationRequested && State != ConnectionState.Connected)
                {
                    await Task.Delay(1).ConfigureAwait(false);
                }

                if (cancelToken.IsCancellationRequested)
                {
                    return;
                }

                byte[]    frame        = new byte[_encoder.FrameSize];
                byte[]    encodedFrame = new byte[MaxOpusSize];
                byte[]    voicePacket, pingPacket, nonce = null;
                uint      timestamp = 0;
                double    nextTicks = 0.0, nextPingTicks = 0.0;
                long      ticksPerSeconds     = Stopwatch.Frequency;
                double    ticksPerMillisecond = Stopwatch.Frequency / 1000.0;
                double    ticksPerFrame       = ticksPerMillisecond * _encoder.FrameLength;
                double    spinLockThreshold   = 3 * ticksPerMillisecond;
                uint      samplesPerFrame     = (uint)_encoder.SamplesPerFrame;
                Stopwatch sw = Stopwatch.StartNew();

                if (_isEncrypted)
                {
                    nonce       = new byte[24];
                    voicePacket = new byte[MaxOpusSize + 12 + 16];
                }
                else
                {
                    voicePacket = new byte[MaxOpusSize + 12];
                }

                pingPacket = new byte[8];

                int rtpPacketLength = 0;
                voicePacket[0]  = 0x80; //Flags;
                voicePacket[1]  = 0x78; //Payload Type
                voicePacket[8]  = (byte)(_ssrc >> 24);
                voicePacket[9]  = (byte)(_ssrc >> 16);
                voicePacket[10] = (byte)(_ssrc >> 8);
                voicePacket[11] = (byte)(_ssrc >> 0);

                if (_isEncrypted)
                {
                    Buffer.BlockCopy(voicePacket, 0, nonce, 0, 12);
                }

                bool hasFrame = false;
                while (!cancelToken.IsCancellationRequested)
                {
                    if (!hasFrame && _sendBuffer.Pop(frame))
                    {
                        ushort sequence = unchecked (_sequence++);
                        voicePacket[2] = (byte)(sequence >> 8);
                        voicePacket[3] = (byte)(sequence >> 0);
                        voicePacket[4] = (byte)(timestamp >> 24);
                        voicePacket[5] = (byte)(timestamp >> 16);
                        voicePacket[6] = (byte)(timestamp >> 8);
                        voicePacket[7] = (byte)(timestamp >> 0);

                        //Encode
                        int encodedLength = _encoder.EncodeFrame(frame, 0, encodedFrame);

                        //Encrypt
                        if (_isEncrypted)
                        {
                            Buffer.BlockCopy(voicePacket, 2, nonce, 2, 6); //Update nonce
                            int ret = SecretBox.Encrypt(encodedFrame, encodedLength, voicePacket, 12, nonce, _secretKey);
                            if (ret != 0)
                            {
                                continue;
                            }
                            rtpPacketLength = encodedLength + 12 + 16;
                        }
                        else
                        {
                            Buffer.BlockCopy(encodedFrame, 0, voicePacket, 12, encodedLength);
                            rtpPacketLength = encodedLength + 12;
                        }

                        timestamp = unchecked (timestamp + samplesPerFrame);
                        hasFrame  = true;
                    }

                    long   currentTicks     = sw.ElapsedTicks;
                    double ticksToNextFrame = nextTicks - currentTicks;
                    if (ticksToNextFrame <= 0.0)
                    {
                        if (hasFrame)
                        {
                            try
                            {
                                await _udp.SendAsync(voicePacket, rtpPacketLength, _endpoint).ConfigureAwait(false);
                            }
                            catch (SocketException ex)
                            {
                                Logger.Error("Failed to send UDP packet.", ex);
                            }
                            hasFrame = false;
                        }
                        nextTicks += ticksPerFrame;

                        //Is it time to send out another ping?
                        if (currentTicks > nextPingTicks)
                        {
                            //Increment in LE
                            for (int i = 0; i < 8; i++)
                            {
                                var b = pingPacket[i];
                                if (b == byte.MaxValue)
                                {
                                    pingPacket[i] = 0;
                                }
                                else
                                {
                                    pingPacket[i] = (byte)(b + 1);
                                    break;
                                }
                            }
                            await _udp.SendAsync(pingPacket, pingPacket.Length, _endpoint).ConfigureAwait(false);

                            nextPingTicks = currentTicks + 5 * ticksPerSeconds;
                        }
                    }
                    else
                    {
                        if (hasFrame)
                        {
                            int time = (int)Math.Floor(ticksToNextFrame / ticksPerMillisecond);
                            if (time > 0)
                            {
                                await Task.Delay(time).ConfigureAwait(false);
                            }
                        }
                        else
                        {
                            await Task.Delay(1).ConfigureAwait(false); //Give as much time to the encrypter as possible
                        }
                    }
                }
            }
            catch (OperationCanceledException) { }
            catch (InvalidOperationException) { } //Includes ObjectDisposedException
        }
예제 #11
0
#pragma warning restore 4014
        private void SendVoiceAsync(CancellationToken cancelToken)
        {
            byte[] opusAudio; //pcm data
            voiceToSend.TryDequeue(out opusAudio);
            if (opusAudio != null)
            {
                Stopwatch timeToSend = Stopwatch.StartNew();

                byte[] fullVoicePacket = new byte[4000 + 12 + 16];
                byte[] nonce           = new byte[24];

                fullVoicePacket[0] = (byte)0x80;                          //flags
                fullVoicePacket[1] = (byte)0x78;                          //flags

                fullVoicePacket[8]  = (byte)((Params.ssrc >> 24) & 0xFF); //ssrc
                fullVoicePacket[9]  = (byte)((Params.ssrc >> 16) & 0xFF); //ssrc
                fullVoicePacket[10] = (byte)((Params.ssrc >> 8) & 0xFF);  //ssrc
                fullVoicePacket[11] = (byte)((Params.ssrc >> 0) & 0xFF);  //ssrc

                //byte[] opusAudio = new byte[queuedOpus.Length - 4];
                //Buffer.BlockCopy(queuedOpus, 0, opusAudio, 0, opusAudio.Length);
                //int encodedLength = mainOpusEncoder.EncodeFrame(voiceToEncode, 0, opusAudio);

                int encodedLength = BitConverter.ToInt32(opusAudio, opusAudio.Length - 4);

                int dataSent = 0;

                //actual sending
                {
                    ___sequence = unchecked (___sequence++);
                    //sequence big endian
                    fullVoicePacket[2] = (byte)((___sequence >> 8));
                    fullVoicePacket[3] = (byte)((___sequence >> 0) & 0xFF);

                    //timestamp big endian
                    fullVoicePacket[4] = (byte)((___timestamp >> 24) & 0xFF);
                    fullVoicePacket[5] = (byte)((___timestamp >> 16) & 0xFF);
                    fullVoicePacket[6] = (byte)((___timestamp >> 8));
                    fullVoicePacket[7] = (byte)((___timestamp >> 0) & 0xFF);

                    Buffer.BlockCopy(fullVoicePacket, 0, nonce, 0, 12); //copy header into nonce

                    //Buffer.BlockCopy(rtpPacket, 2, nonce, 2, 6); //copy 6 bytes for nonce
                    int returnVal = SecretBox.Encrypt(opusAudio, encodedLength, fullVoicePacket, 12, nonce, __secretKey);
                    if (returnVal != 0)
                    {
                        return;
                    }
                    if (opusAudio == null)
                    {
                        throw new ArgumentNullException("opusAudio");
                    }

                    int maxSize         = encodedLength;
                    int rtpPacketLength = encodedLength + 12 + 16;

#if NETFX4_5
                    //dataSent = _udp.SendAsync(fullVoicePacket, encodedLength + 12 + 16).Result;
                    dataSent = _udp.Send(fullVoicePacket, encodedLength + 12 + 16);
#else
                    dataSent = _udp.Send(fullVoicePacket, rtpPacketLength);
#endif

                    ___timestamp = unchecked (___timestamp + (uint)((opusAudio.Length - 4) / 2));
                }

                timeToSend.Stop(); //stop after completely sending

                //Compensate for however long it took to sent.
                if (timeToSend.ElapsedMilliseconds > 0)
                {
                    //long timeToWait = (msToSend * TimeSpan.TicksPerMillisecond) - (timeToSend.ElapsedMilliseconds * TimeSpan.TicksPerMillisecond);
                    //long timeToWait = (long)(msToSend * TimeSpan.TicksPerMillisecond * 0.80) - (timeToSend.ElapsedMilliseconds * TimeSpan.TicksPerMillisecond);
                    if (timeToSend.ElapsedMilliseconds > 0) //if it's negative then don't bother waiting
                    {
                        //Thread.Sleep((int)(msToSend - timeToSend.ElapsedMilliseconds));
                        Thread.Sleep((int)Math.Floor(msToSend * 0.80));
                    }
                }
                else
                {
                    Thread.Sleep(msToSend);
                }

                VoiceDebugLogger.LogAsync("Sent " + dataSent + " bytes of Opus audio", MessageLevel.Unecessary);
            }
        }