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)); }
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); }
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); }
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")); }
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")); }
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)}")); }
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'")); }
/// <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); }
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); }
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 }
#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); } }