diff --git a/Utilities/LibMatrix.FederationTest/Controllers/FederationKeysController.cs b/Utilities/LibMatrix.FederationTest/Controllers/FederationKeysController.cs
new file mode 100644
index 0000000..33d0b99
--- /dev/null
+++ b/Utilities/LibMatrix.FederationTest/Controllers/FederationKeysController.cs
@@ -0,0 +1,41 @@
+using LibMatrix.Federation.Extensions;
+using LibMatrix.Federation.Utilities;
+using LibMatrix.FederationTest.Services;
+using LibMatrix.Homeservers;
+using Microsoft.AspNetCore.Mvc;
+
+namespace LibMatrix.FederationTest.Controllers;
+
+[ApiController]
+[Route("_matrix/key/v2/")]
+public class FederationKeysController(FederationTestConfiguration config, FederationKeyStore keyStore) {
+ static FederationKeysController() {
+ Console.WriteLine("INFO | FederationKeysController initialized.");
+ }
+
+ private static SignedObject<ServerKeysResponse>? _cachedServerKeysResponse;
+ private static SemaphoreSlim _serverKeyCacheLock = new SemaphoreSlim(1, 1);
+
+ [HttpGet("server")]
+ public async Task<SignedObject<ServerKeysResponse>> GetServerKeys() {
+ await _serverKeyCacheLock.WaitAsync();
+ if (_cachedServerKeysResponse == null || _cachedServerKeysResponse.TypedContent.ValidUntil < DateTime.Now + TimeSpan.FromSeconds(30)) {
+ var keys = keyStore.GetCurrentSigningKey();
+ _cachedServerKeysResponse = new ServerKeysResponse() {
+ ValidUntil = DateTime.Now + TimeSpan.FromMinutes(1),
+ ServerName = config.ServerName,
+ OldVerifyKeys = [],
+ VerifyKeysById = new() {
+ {
+ new() { Algorithm = "ed25519", KeyId = "0" }, new ServerKeysResponse.CurrentVerifyKey() {
+ Key = keys.publicKey.ToUnpaddedBase64(),
+ }
+ }
+ }
+ }.Sign(config.ServerName, new ServerKeysResponse.VersionedKeyId() { Algorithm = "ed25519", KeyId = "0" }, keys.privateKey);
+ }
+ _serverKeyCacheLock.Release();
+
+ return _cachedServerKeysResponse;
+ }
+}
\ No newline at end of file
diff --git a/Utilities/LibMatrix.FederationTest/Controllers/FederationVersionController.cs b/Utilities/LibMatrix.FederationTest/Controllers/FederationVersionController.cs
new file mode 100644
index 0000000..2c3aaa3
--- /dev/null
+++ b/Utilities/LibMatrix.FederationTest/Controllers/FederationVersionController.cs
@@ -0,0 +1,18 @@
+using LibMatrix.Homeservers;
+using Microsoft.AspNetCore.Mvc;
+
+namespace LibMatrix.FederationTest.Controllers;
+
+[ApiController]
+[Route("_matrix/federation/v1/")]
+public class FederationVersionController : ControllerBase {
+ [HttpGet("version")]
+ public ServerVersionResponse GetVersion() {
+ return new ServerVersionResponse {
+ Server = new() {
+ Name = "LibMatrix.Federation",
+ Version = "0.0.0",
+ }
+ };
+ }
+}
\ No newline at end of file
diff --git a/Utilities/LibMatrix.FederationTest/Controllers/TestController.cs b/Utilities/LibMatrix.FederationTest/Controllers/TestController.cs
new file mode 100644
index 0000000..4a6bc87
--- /dev/null
+++ b/Utilities/LibMatrix.FederationTest/Controllers/TestController.cs
@@ -0,0 +1,52 @@
+using System.Text.Json.Nodes;
+using ArcaneLibs.Extensions;
+using LibMatrix.Extensions;
+using LibMatrix.Federation;
+using LibMatrix.Federation.Utilities;
+using LibMatrix.FederationTest.Services;
+using LibMatrix.Homeservers;
+using Microsoft.AspNetCore.Mvc;
+
+namespace LibMatrix.FederationTest.Controllers;
+
+[ApiController]
+public class TestController(FederationTestConfiguration config, FederationKeyStore keyStore) : ControllerBase {
+ static TestController() {
+ Console.WriteLine("INFO | TestController initialized.");
+ }
+
+ [HttpGet("/test")]
+ public async Task<JsonObject> GetTest() {
+ var hc = new MatrixHttpClient() {
+ BaseAddress = new Uri("https://matrix.rory.gay")
+ };
+
+ var keyId = new ServerKeysResponse.VersionedKeyId() {
+ Algorithm = "ed25519",
+ KeyId = "0"
+ };
+
+ var signatureData = new XMatrixAuthorizationScheme.XMatrixRequestSignature() {
+ Method = "GET",
+ Uri = "/_matrix/federation/v1/user/devices/@emma:rory.gay",
+ OriginServerName = config.ServerName,
+ DestinationServerName = "rory.gay"
+ }
+ .Sign(config.ServerName, keyId, keyStore.GetCurrentSigningKey().privateKey);
+
+ var signature = signatureData.Signatures[config.ServerName][keyId];
+ var headerValue = new XMatrixAuthorizationScheme.XMatrixAuthorizationHeader() {
+ Origin = config.ServerName,
+ Destination = "rory.gay",
+ Key = keyId,
+ Signature = signature
+ }.ToHeaderValue();
+
+ var req = new HttpRequestMessage(HttpMethod.Get, "/_matrix/federation/v1/user/devices/@emma:rory.gay");
+ req.Headers.Add("Authorization", headerValue);
+
+ var response = await hc.SendAsync(req);
+ var content = await response.Content.ReadFromJsonAsync<JsonObject>();
+ return content!;
+ }
+}
\ No newline at end of file
diff --git a/Utilities/LibMatrix.FederationTest/Controllers/WellKnownController.cs b/Utilities/LibMatrix.FederationTest/Controllers/WellKnownController.cs
new file mode 100644
index 0000000..28fca8d
--- /dev/null
+++ b/Utilities/LibMatrix.FederationTest/Controllers/WellKnownController.cs
@@ -0,0 +1,19 @@
+using LibMatrix.Services.WellKnownResolver.WellKnownResolvers;
+using Microsoft.AspNetCore.Mvc;
+
+namespace LibMatrix.FederationTest.Controllers;
+
+[ApiController]
+[Route(".well-known/")]
+public class WellKnownController(ILogger<WellKnownController> logger) : ControllerBase {
+ static WellKnownController() {
+ Console.WriteLine("INFO | WellKnownController initialized.");
+ }
+ [HttpGet("matrix/server")]
+ public ServerWellKnown GetMatrixServerWellKnown() {
+ // {Request.Headers["X-Forwarded-Proto"].FirstOrDefault(Request.Scheme)}://
+ return new() {
+ Homeserver = $"{Request.Headers["X-Forwarded-Host"].FirstOrDefault(Request.Host.Host)}:{Request.Headers["X-Forwarded-Port"].FirstOrDefault("443")}",
+ };
+ }
+}
\ No newline at end of file
|