Retire legacy sources and verify managed iOS deployment
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
using Microsoft.Data.Sqlite;
|
||||
using System.Diagnostics;
|
||||
using VoiceCat.Server.Data;
|
||||
|
||||
namespace VoiceCat.Tests;
|
||||
@@ -27,71 +26,6 @@ public sealed class AccountStoreTests
|
||||
finally { File.Delete(path); File.Delete(path + "-wal"); File.Delete(path + "-shm"); }
|
||||
}
|
||||
|
||||
[NativeDatabaseFact]
|
||||
public async Task ExistingCppDatabaseAndManagedAccountsWorkInBothImplementations()
|
||||
{
|
||||
string directory = Path.Combine(Path.GetTempPath(), "voicecat-import-" + Guid.NewGuid().ToString("N"));
|
||||
Directory.CreateDirectory(directory);
|
||||
string path = Path.Combine(directory, "voicecat.db");
|
||||
try
|
||||
{
|
||||
await RunOracleAsync("create", path);
|
||||
using (var store = new AccountStore(path))
|
||||
{
|
||||
Account account = Assert.IsType<Account>(await store.AuthenticateAsync("legacy", "legacy password"));
|
||||
Assert.True(account.IsAdmin);
|
||||
var channel = Assert.Single(store.LoadChannels());
|
||||
Assert.Equal("Preserved native topic", channel.Topic);
|
||||
Assert.Equal(7U, channel.MaxUsers);
|
||||
Assert.Equal(32000U, channel.Audio.BitrateBps);
|
||||
await store.CreateAccountAsync("managed", "managed password", true);
|
||||
}
|
||||
await RunOracleAsync("verify", path);
|
||||
}
|
||||
finally { Directory.Delete(directory, true); }
|
||||
}
|
||||
|
||||
private static async Task RunOracleAsync(string mode, string path)
|
||||
{
|
||||
var start = new ProcessStartInfo(Environment.GetEnvironmentVariable("VOICECAT_DATABASE_ORACLE")!) { UseShellExecute = false, CreateNoWindow = true };
|
||||
start.ArgumentList.Add(mode);
|
||||
start.ArgumentList.Add(path);
|
||||
using var process = Process.Start(start)!;
|
||||
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(30));
|
||||
try { await process.WaitForExitAsync(timeout.Token); Assert.Equal(0, process.ExitCode); }
|
||||
finally { if (!process.HasExited) { process.Kill(true); await process.WaitForExitAsync(); } }
|
||||
}
|
||||
|
||||
[NativeDatabaseFact]
|
||||
public async Task ChannelPasswordHashesWorkInBothImplementations()
|
||||
{
|
||||
string directory = Path.Combine(Path.GetTempPath(), "voicecat-channels-" + Guid.NewGuid().ToString("N"));
|
||||
Directory.CreateDirectory(directory);
|
||||
string path = Path.Combine(directory, "voicecat.db");
|
||||
try
|
||||
{
|
||||
await RunOracleAsync("create-protected", path);
|
||||
using (var store = new AccountStore(path))
|
||||
{
|
||||
var channel = Assert.Single(store.LoadChannels());
|
||||
Assert.True(store.CheckChannelPassword(channel.Id, "channel password"));
|
||||
Assert.False(store.CheckChannelPassword(channel.Id, "wrong"));
|
||||
channel.Name = "Managed protected";
|
||||
store.SaveChannel(channel, "channel password", true);
|
||||
}
|
||||
await RunOracleAsync("verify-protected", path);
|
||||
}
|
||||
finally { Directory.Delete(directory, true); }
|
||||
}
|
||||
|
||||
private sealed class NativeDatabaseFactAttribute : FactAttribute
|
||||
{
|
||||
public NativeDatabaseFactAttribute()
|
||||
{
|
||||
if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("VOICECAT_DATABASE_ORACLE"))) Skip = "Set VOICECAT_DATABASE_ORACLE to the native database oracle.";
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AccountsSurviveRestartAndFailedAuthDoesNotChangeLastLogin()
|
||||
{
|
||||
|
||||
@@ -8,41 +8,6 @@ namespace VoiceCat.Tests;
|
||||
|
||||
public class AdministrationTests
|
||||
{
|
||||
[NativeCliFact]
|
||||
public async Task ExistingCppCliCreatesProtectedChannelsAndAdministersAccounts()
|
||||
{
|
||||
await using var fixture = new ServerFixture();
|
||||
await using var admin = await AdminAsync(fixture);
|
||||
var start = new System.Diagnostics.ProcessStartInfo(Environment.GetEnvironmentVariable("VOICECAT_VCCLI")!)
|
||||
{
|
||||
WorkingDirectory = fixture.Directory, UseShellExecute = false, CreateNoWindow = true,
|
||||
RedirectStandardOutput = true, RedirectStandardError = true
|
||||
};
|
||||
foreach (string argument in new[] { "--host", "127.0.0.1", "--port", fixture.Server.EndPoint.Port.ToString(), "--username", "Admin", "--password", "secret",
|
||||
"--create-channel", "--new-channel-name", "Native room", "--new-channel-password", "protected", "--create-account", "native", "secret", "--list-accounts", "--wait-ms", "10000" })
|
||||
start.ArgumentList.Add(argument);
|
||||
using var process = System.Diagnostics.Process.Start(start)!;
|
||||
Task<string> stdout = process.StandardOutput.ReadToEndAsync(), stderr = process.StandardError.ReadToEndAsync();
|
||||
try
|
||||
{
|
||||
await process.WaitForExitAsync(admin.Timeout.Token);
|
||||
Assert.True(process.ExitCode == 0, await stdout + await stderr);
|
||||
using var store = new AccountStore(Path.Combine(fixture.Directory, "voicecat.db"));
|
||||
var room = store.LoadChannels().Single(c => c.Name == "Native room");
|
||||
Assert.True(store.CheckChannelPassword(room.Id, "protected"));
|
||||
Assert.NotNull(await store.AuthenticateAsync("native", "secret"));
|
||||
}
|
||||
finally { if (!process.HasExited) { process.Kill(true); await process.WaitForExitAsync(); } }
|
||||
}
|
||||
|
||||
private sealed class NativeCliFactAttribute : FactAttribute
|
||||
{
|
||||
public NativeCliFactAttribute()
|
||||
{
|
||||
if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("VOICECAT_VCCLI"))) Skip = "Set VOICECAT_VCCLI to the existing native CLI.";
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AccountAdministrationIsPermissionGatedAndPersistsPasswordChanges()
|
||||
{
|
||||
|
||||
@@ -20,7 +20,7 @@ public sealed class DspTests
|
||||
if (frame >= 60) foreach (short value in pcm) outputEnergy += (double)value * value;
|
||||
}
|
||||
Assert.True(Math.Sqrt(outputEnergy / inputEnergy) < 0.2);
|
||||
using var fixture = JsonDocument.Parse(File.ReadAllText(Path.Combine(AppContext.BaseDirectory, "Fixtures", "cpp-noise.json")));
|
||||
using var fixture = JsonDocument.Parse(File.ReadAllText(Path.Combine(AppContext.BaseDirectory, "Fixtures", "rnnoise.json")));
|
||||
short[] expected = fixture.RootElement.GetProperty("samples").EnumerateArray().Select(value => value.GetInt16()).ToArray();
|
||||
Assert.Equal(pcm.Length, expected.Length);
|
||||
for (int i = 0; i < pcm.Length; i++) Assert.InRange(Math.Abs(pcm[i] - expected[i]), 0, 1);
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace VoiceCat.Tests;
|
||||
public class GoldenTests
|
||||
{
|
||||
[Fact]
|
||||
public void EnvelopeMatchesCppFixture()
|
||||
public void EnvelopeMatchesCanonicalWireVector()
|
||||
{
|
||||
using var fixture = Load();
|
||||
var expected = Convert.FromHexString(fixture.RootElement.GetProperty("envelope").GetString()!);
|
||||
@@ -23,7 +23,7 @@ public class GoldenTests
|
||||
[Theory]
|
||||
[InlineData(false)]
|
||||
[InlineData(true)]
|
||||
public void MediaPacketsMatchCppFixtures(bool managed)
|
||||
public void MediaPacketsMatchCanonicalWireVectors(bool managed)
|
||||
{
|
||||
using var fixture = Load();
|
||||
foreach (var vector in fixture.RootElement.GetProperty("media").EnumerateArray())
|
||||
@@ -46,5 +46,5 @@ public class GoldenTests
|
||||
}
|
||||
}
|
||||
|
||||
private static JsonDocument Load() => JsonDocument.Parse(File.ReadAllText(Path.Combine(AppContext.BaseDirectory, "Fixtures", "cpp-wire.json")));
|
||||
private static JsonDocument Load() => JsonDocument.Parse(File.ReadAllText(Path.Combine(AppContext.BaseDirectory, "Fixtures", "wire.json")));
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ public class IdentityTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TofuRequiresExplicitPinAndPreservesCppFileFormat()
|
||||
public void TofuRequiresExplicitPinAndPreservesFileFormat()
|
||||
{
|
||||
string directory = Path.Combine(Path.GetTempPath(), "voicecat-pins-" + Guid.NewGuid());
|
||||
Directory.CreateDirectory(directory);
|
||||
|
||||
@@ -5,30 +5,6 @@ namespace VoiceCat.Tests;
|
||||
|
||||
public class ManagedCliTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task ManagedCliInteroperatesWithExistingCppCli()
|
||||
{
|
||||
string? nativeCli = Environment.GetEnvironmentVariable("VOICECAT_VCCLI");
|
||||
if (string.IsNullOrEmpty(nativeCli)) return; // Full conformance runs set this explicitly.
|
||||
await using var fixture = new ServerFixture();
|
||||
string cli = Path.Combine(FindRoot(), "dotnet", "src", "VoiceCat.Cli", "bin", "Release", "net10.0", "VoiceCat.Cli.dll");
|
||||
using Process managed = Start(cli, fixture, "Managed", "Managed checkpoint", "Native checkpoint", 2);
|
||||
await Task.Delay(750); // Native vccli sends its one-shot text immediately after auth.
|
||||
var nativeStart = new ProcessStartInfo(nativeCli) { WorkingDirectory = fixture.Directory, RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true };
|
||||
foreach (string argument in new[] { "--host", "127.0.0.1", "--port", fixture.Server.EndPoint.Port.ToString(), "--nick", "Native",
|
||||
"--channel", "2", "--text", "Native checkpoint", "--test-tone-ms", "5000" }) nativeStart.ArgumentList.Add(argument);
|
||||
using Process native = Process.Start(nativeStart)!;
|
||||
Task<string> managedOut = managed.StandardOutput.ReadToEndAsync(), managedError = managed.StandardError.ReadToEndAsync();
|
||||
Task<string> nativeOut = native.StandardOutput.ReadToEndAsync(), nativeError = native.StandardError.ReadToEndAsync();
|
||||
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(35));
|
||||
await Task.WhenAll(managed.WaitForExitAsync(timeout.Token), native.WaitForExitAsync(timeout.Token));
|
||||
string mout = await managedOut, nout = await nativeOut;
|
||||
Assert.True(managed.ExitCode == 0, await managedError + Environment.NewLine + mout);
|
||||
Assert.True(native.ExitCode == 0, await nativeError + Environment.NewLine + nout);
|
||||
Assert.Contains("Native checkpoint", mout); Assert.Contains("Managed checkpoint", nout);
|
||||
Assert.Contains("[test-tone] received=", nout); Assert.DoesNotContain("\"voiceEnergy\":0", mout);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(1u)]
|
||||
[InlineData(2u)]
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using VoiceCat.Transport;
|
||||
using System.Net;
|
||||
using System.Diagnostics;
|
||||
using System.Net.Sockets;
|
||||
using VoiceCat.Protocol;
|
||||
using VoiceCat.Server.Transport;
|
||||
@@ -11,89 +10,6 @@ namespace VoiceCat.Tests;
|
||||
|
||||
public sealed class MediaRelayTests
|
||||
{
|
||||
[CppCliVoiceTheory]
|
||||
[InlineData(1)]
|
||||
[InlineData(2)]
|
||||
public async Task TwoCppCliProcessesJoinChatAndExchangeVoice(int channel)
|
||||
{
|
||||
await using var fixture = new ServerFixture();
|
||||
await using var observer = await fixture.ConnectAsync();
|
||||
await observer.LoginAsync("Observer");
|
||||
observer.Send(new() { JoinChannel = new() { ChannelId = checked((uint)channel) } });
|
||||
Assert.True((await observer.ReadUntilAsync(e => e.JoinChannelResult is not null)).JoinChannelResult.Ok);
|
||||
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(30));
|
||||
await Task.WhenAll(RunAsync("Cli Alice"), RunAsync("Cli Bob"));
|
||||
var first = (await observer.ReadUntilAsync(e => e.TextMessage is not null)).TextMessage;
|
||||
var second = (await observer.ReadUntilAsync(e => e.TextMessage is not null)).TextMessage;
|
||||
Assert.Equal("CLI voice checkpoint", first.Body);
|
||||
Assert.Equal(first.Body, second.Body);
|
||||
Assert.NotEqual(first.SenderId, second.SenderId);
|
||||
|
||||
async Task RunAsync(string nickname)
|
||||
{
|
||||
var start = new ProcessStartInfo(Environment.GetEnvironmentVariable("VOICECAT_VCCLI")!)
|
||||
{
|
||||
WorkingDirectory = fixture.Directory, UseShellExecute = false, CreateNoWindow = true,
|
||||
RedirectStandardOutput = true, RedirectStandardError = true
|
||||
};
|
||||
foreach (string argument in new[] { "--host", "127.0.0.1", "--port", fixture.Server.EndPoint.Port.ToString(System.Globalization.CultureInfo.InvariantCulture),
|
||||
"--nick", nickname, "--channel", channel.ToString(System.Globalization.CultureInfo.InvariantCulture), "--text", "CLI voice checkpoint", "--test-tone-ms", "4000" })
|
||||
start.ArgumentList.Add(argument);
|
||||
using var process = Process.Start(start)!;
|
||||
Task<string> stdout = process.StandardOutput.ReadToEndAsync();
|
||||
Task<string> stderr = process.StandardError.ReadToEndAsync();
|
||||
try
|
||||
{
|
||||
await process.WaitForExitAsync(timeout.Token);
|
||||
string log = await stdout + await stderr;
|
||||
Assert.True(process.ExitCode == 0, log);
|
||||
Assert.Contains("[test-tone] received=", log);
|
||||
}
|
||||
finally { if (!process.HasExited) { process.Kill(true); await process.WaitForExitAsync(); } }
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class CppCliVoiceTheoryAttribute : TheoryAttribute
|
||||
{
|
||||
public CppCliVoiceTheoryAttribute()
|
||||
{
|
||||
if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("VOICECAT_VCCLI"))) Skip = "Set VOICECAT_VCCLI to the existing native CLI.";
|
||||
}
|
||||
}
|
||||
|
||||
[VoiceOracleTheory]
|
||||
[InlineData(1)]
|
||||
[InlineData(2)]
|
||||
public async Task ExistingCppClientsExchangeBidirectionalVoiceThroughManagedServer(int channel)
|
||||
{
|
||||
await using var fixture = new ServerFixture();
|
||||
var start = new ProcessStartInfo(Environment.GetEnvironmentVariable("VOICECAT_VOICE_ORACLE")!)
|
||||
{
|
||||
WorkingDirectory = fixture.Directory, UseShellExecute = false, CreateNoWindow = true,
|
||||
RedirectStandardOutput = true, RedirectStandardError = true
|
||||
};
|
||||
start.ArgumentList.Add(fixture.Server.EndPoint.Port.ToString(System.Globalization.CultureInfo.InvariantCulture));
|
||||
start.ArgumentList.Add(channel.ToString(System.Globalization.CultureInfo.InvariantCulture));
|
||||
using var process = Process.Start(start)!;
|
||||
Task<string> output = process.StandardOutput.ReadToEndAsync();
|
||||
Task<string> error = process.StandardError.ReadToEndAsync();
|
||||
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(45));
|
||||
try
|
||||
{
|
||||
await process.WaitForExitAsync(timeout.Token);
|
||||
Assert.True(process.ExitCode == 0, await output + await error);
|
||||
}
|
||||
finally { if (!process.HasExited) { process.Kill(true); await process.WaitForExitAsync(); } }
|
||||
}
|
||||
|
||||
private sealed class VoiceOracleTheoryAttribute : TheoryAttribute
|
||||
{
|
||||
public VoiceOracleTheoryAttribute()
|
||||
{
|
||||
if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("VOICECAT_VOICE_ORACLE"))) Skip = "Set VOICECAT_VOICE_ORACLE to the native voice conformance executable.";
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DisconnectInvalidatesBothBindingAndActiveStreams()
|
||||
{
|
||||
|
||||
@@ -10,7 +10,7 @@ public sealed class PasswordTests
|
||||
public void VerifiesLibsodiumHashesWithoutPasswordNormalization()
|
||||
{
|
||||
var hasher = new PasswordHasher();
|
||||
using var fixture = JsonDocument.Parse(File.ReadAllText(Path.Combine(AppContext.BaseDirectory, "Fixtures", "cpp-passwords.json")));
|
||||
using var fixture = JsonDocument.Parse(File.ReadAllText(Path.Combine(AppContext.BaseDirectory, "Fixtures", "argon2id.json")));
|
||||
foreach (var item in fixture.RootElement.GetProperty("hashes").EnumerateArray())
|
||||
{
|
||||
string encodedPassword = item.GetProperty("passwordBase64").GetString()!;
|
||||
|
||||
@@ -95,43 +95,6 @@ public sealed class ServerTests
|
||||
Assert.NotEqual(0U, (await client.ReadUntilAsync(e => e.Disconnect is not null)).Disconnect.Code);
|
||||
}
|
||||
|
||||
[CppCliFact]
|
||||
public async Task ExistingCppCliAuthenticatesAndChatsThroughManagedServer()
|
||||
{
|
||||
await using var fixture = new ServerFixture();
|
||||
await using var receiver = await fixture.ConnectAsync();
|
||||
User self = await receiver.LoginAsync("Managed");
|
||||
var start = new ProcessStartInfo(Environment.GetEnvironmentVariable("VOICECAT_VCCLI")!)
|
||||
{
|
||||
WorkingDirectory = fixture.Directory, UseShellExecute = false,
|
||||
RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true
|
||||
};
|
||||
foreach (string argument in new[] { "--host", "127.0.0.1", "--port", fixture.Server.EndPoint.Port.ToString(), "--nick", "Cpp", "--text", "native interoperability", "--wait-ms", "10000" })
|
||||
start.ArgumentList.Add(argument);
|
||||
using var process = Process.Start(start)!;
|
||||
Task<string> output = process.StandardOutput.ReadToEndAsync();
|
||||
Task<string> error = process.StandardError.ReadToEndAsync();
|
||||
try
|
||||
{
|
||||
await process.WaitForExitAsync(receiver.Timeout.Token);
|
||||
string log = await output + await error;
|
||||
Assert.True(process.ExitCode == 0, log);
|
||||
TextMessage text = (await receiver.ReadUntilAsync(e => e.TextMessage is not null)).TextMessage;
|
||||
Assert.Equal("native interoperability", text.Body);
|
||||
Assert.NotEqual(self.Id, text.SenderId);
|
||||
Assert.Contains("native interoperability", log);
|
||||
}
|
||||
finally { if (!process.HasExited) { process.Kill(true); await process.WaitForExitAsync(); } }
|
||||
}
|
||||
|
||||
private sealed class CppCliFactAttribute : FactAttribute
|
||||
{
|
||||
public CppCliFactAttribute()
|
||||
{
|
||||
if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("VOICECAT_VCCLI"))) Skip = "Set VOICECAT_VCCLI to the existing native CLI.";
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class ServerFixture : IAsyncDisposable
|
||||
{
|
||||
public string Directory { get; } = Path.Combine(Path.GetTempPath(), "voicecat-server-" + Guid.NewGuid().ToString("N"));
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
using System.Diagnostics;
|
||||
using System.Net.Sockets;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using VoiceCat.Crypto;
|
||||
using VoiceCat.Protocol;
|
||||
|
||||
namespace VoiceCat.Tests;
|
||||
|
||||
public class TlsInteropTests
|
||||
{
|
||||
[TlsOracleFact]
|
||||
public async Task ManagedClientAndCppServerAgreeOnExporterKeysAndCertificate()
|
||||
{
|
||||
string? oracle = Environment.GetEnvironmentVariable("VOICECAT_TLS_ORACLE");
|
||||
string directory = Path.Combine(Path.GetTempPath(), "voicecat-tls-" + Guid.NewGuid());
|
||||
Directory.CreateDirectory(directory);
|
||||
var start = new ProcessStartInfo(oracle!) { UseShellExecute = false, CreateNoWindow = true, RedirectStandardError = true, RedirectStandardOutput = true };
|
||||
start.ArgumentList.Add(directory);
|
||||
using var process = Process.Start(start)!;
|
||||
var error = process.StandardError.ReadToEndAsync();
|
||||
var stdout = process.StandardOutput.ReadToEndAsync();
|
||||
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(30));
|
||||
try
|
||||
{
|
||||
int port = 0;
|
||||
while (!int.TryParse(File.Exists(Path.Combine(directory, "port.txt")) ? await File.ReadAllTextAsync(Path.Combine(directory, "port.txt"), timeout.Token) : "", out port))
|
||||
{
|
||||
Assert.False(process.HasExited, "C++ TLS oracle exited before listening.");
|
||||
await Task.Delay(20, timeout.Token);
|
||||
}
|
||||
using var certificate = X509Certificate2.CreateFromPem(await File.ReadAllTextAsync(Path.Combine(directory, "server.crt"), timeout.Token));
|
||||
string fingerprint = Convert.ToHexString(SHA256.HashData(certificate.RawData));
|
||||
using var credentials = ServerCredentials.LoadOrCreate(directory, "existing C++ identity");
|
||||
Assert.Equal(fingerprint, credentials.CertificateFingerprint);
|
||||
Assert.Equal(32, credentials.Identity.PublicKey.Length);
|
||||
using var client = TlsSession.CreateClient(value => value == fingerprint);
|
||||
using var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
|
||||
await socket.ConnectAsync("127.0.0.1", port, timeout.Token);
|
||||
byte[] buffer = new byte[16384];
|
||||
async Task Flush()
|
||||
{
|
||||
while (client.PendingCiphertextBytes > 0)
|
||||
{
|
||||
int count = client.DrainCiphertext(buffer);
|
||||
int sent = 0;
|
||||
while (sent < count) sent += await socket.SendAsync(buffer.AsMemory(sent, count - sent), SocketFlags.None, timeout.Token);
|
||||
}
|
||||
}
|
||||
async Task Receive()
|
||||
{
|
||||
int count = await socket.ReceiveAsync(buffer, SocketFlags.None, timeout.Token);
|
||||
Assert.True(count > 0, "TLS oracle closed unexpectedly.");
|
||||
client.ReceiveCiphertext(buffer.AsSpan(0, count));
|
||||
}
|
||||
while (!client.IsReady) { await Flush(); await Receive(); }
|
||||
await Flush();
|
||||
byte[] packet = new byte[41];
|
||||
int received = 0;
|
||||
while (received < packet.Length)
|
||||
{
|
||||
int count = client.ReadPlaintext(packet.AsSpan(received));
|
||||
received += count;
|
||||
if (count == 0) { await Flush(); await Receive(); }
|
||||
}
|
||||
using var decryptor = client.CreateMediaDecryptor();
|
||||
byte[] plaintext = new byte[5];
|
||||
Assert.True(decryptor.TryDecrypt(packet, plaintext, out var header, out _));
|
||||
Assert.Equal("hello"u8.ToArray(), plaintext);
|
||||
Assert.Equal(fingerprint, client.PeerCertificateFingerprint);
|
||||
using var encryptor = client.CreateMediaEncryptor();
|
||||
encryptor.Encrypt(header, plaintext, packet);
|
||||
client.WritePlaintext(packet);
|
||||
await Flush();
|
||||
byte[] ack = new byte[1];
|
||||
while (client.ReadPlaintext(ack) == 0) { await Flush(); await Receive(); }
|
||||
Assert.Equal(1, ack[0]);
|
||||
await process.WaitForExitAsync(timeout.Token);
|
||||
Assert.True(process.ExitCode == 0, await error);
|
||||
await stdout;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!process.HasExited) { process.Kill(entireProcessTree: true); await process.WaitForExitAsync(); }
|
||||
Directory.Delete(directory, recursive: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class TlsOracleFactAttribute : FactAttribute
|
||||
{
|
||||
public TlsOracleFactAttribute()
|
||||
{
|
||||
if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("VOICECAT_TLS_ORACLE")))
|
||||
Skip = "Build the native TLS oracle and set VOICECAT_TLS_ORACLE to its executable path.";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user