Files
voice-cat/dotnet/tests/VoiceCat.Tests/IdentityTests.cs
T

69 lines
3.7 KiB
C#
Raw Normal View History

using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Formats.Asn1;
using VoiceCat.Crypto;
namespace VoiceCat.Tests;
public class IdentityTests
{
[Fact]
public void CredentialsSurviveRestartAndBindIdentityIntoCertificate()
{
string directory = Path.Combine(Path.GetTempPath(), "voicecat-credentials-" + Guid.NewGuid());
try
{
string identityFingerprint, certificateFingerprint;
using (var credentials = ServerCredentials.LoadOrCreate(directory, "Server, with punctuation"))
{
identityFingerprint = credentials.Identity.Fingerprint;
certificateFingerprint = credentials.CertificateFingerprint;
using var tls = credentials.CreateTlsSession();
Assert.False(tls.IsReady);
byte[] identity = File.ReadAllBytes(Path.Combine(directory, "identity.key"));
Assert.Equal(96, identity.Length);
Assert.Equal(identity[..32], identity[64..]);
using var certificate = X509Certificate2.CreateFromPem(File.ReadAllText(Path.Combine(directory, "server.crt")));
var san = new AsnReader(certificate.Extensions["2.5.29.17"]!.RawData, AsnEncodingRules.DER).ReadSequence();
Assert.Equal("urn:voicecat:identity:ed25519:" + Convert.ToHexString(credentials.Identity.PublicKey).ToLowerInvariant(),
san.ReadCharacterString(UniversalTagNumber.IA5String, new Asn1Tag(TagClass.ContextSpecific, 6)));
Assert.False(san.HasData);
}
using var restored = ServerCredentials.LoadOrCreate(directory, "ignored after creation");
Assert.Equal(identityFingerprint, restored.Identity.Fingerprint);
Assert.Equal(certificateFingerprint, restored.CertificateFingerprint);
File.Delete(Path.Combine(directory, "server.key"));
Assert.Throws<InvalidDataException>(() => ServerCredentials.LoadOrCreate(directory, "unchanged"));
using var stillPresent = ServerIdentity.Load(Path.Combine(directory, "identity.key"));
Assert.Equal(identityFingerprint, stillPresent.Fingerprint);
}
finally { if (Directory.Exists(directory)) Directory.Delete(directory, true); }
}
[Fact]
public void TofuRequiresExplicitPinAndPreservesFileFormat()
{
string directory = Path.Combine(Path.GetTempPath(), "voicecat-pins-" + Guid.NewGuid());
Directory.CreateDirectory(directory);
string path = Path.Combine(directory, "pins.txt");
string fingerprint = Convert.ToHexString(RandomNumberGenerator.GetBytes(32));
try
{
var store = new TofuStore(path);
Assert.Equal(TofuStatus.FirstConnect, store.Check("localhost", 9987, fingerprint));
Assert.False(File.Exists(path));
store.Pin("localhost", 9987, fingerprint);
Assert.Equal($"localhost:9987 {fingerprint.ToLowerInvariant()}\n", File.ReadAllText(path));
store = new(path);
Assert.Equal(TofuStatus.Matched, store.Check("localhost", 9987, fingerprint));
Assert.Equal(TofuStatus.Mismatch, store.Check("localhost", 9987, new string('0', 64)));
Assert.Equal(TofuStatus.Matched, new TofuStore(path).Check("localhost", 9987, fingerprint));
store.Remove("localhost", 9987);
Assert.Equal(TofuStatus.FirstConnect, new TofuStore(path).Check("localhost", 9987, fingerprint));
File.WriteAllText(path, "localhost:9987 " + new string('g', 64));
Assert.Throws<InvalidDataException>(() => new TofuStore(path));
}
finally { Directory.Delete(directory, true); }
}
}