/ GUNRPG.Tests / AuthoritySecurityTests.cs
AuthoritySecurityTests.cs
1 using GUNRPG.Security; 2 3 namespace GUNRPG.Tests; 4 5 public sealed class AuthoritySecurityTests 6 { 7 private static readonly DateTimeOffset ReferenceNow = new(2026, 03, 15, 04, 00, 00, TimeSpan.Zero); 8 9 [Fact] 10 public void VerifyServerCertificate_ReturnsTrue_ForRootSignedUnexpiredCertificate() 11 { 12 var rootPrivateKey = CertificateIssuer.GeneratePrivateKey(); 13 var certificateIssuer = new CertificateIssuer(rootPrivateKey); 14 var authorityRoot = new AuthorityRoot(certificateIssuer.RootPublicKey); 15 var serverId = Guid.NewGuid(); 16 var serverPublicKey = ServerIdentity.GetPublicKey(ServerIdentity.GeneratePrivateKey()); 17 var issuedAt = ReferenceNow.AddMinutes(-5); 18 var validUntil = ReferenceNow.AddMinutes(30); 19 20 var certificate = certificateIssuer.IssueServerCertificate(serverId, serverPublicKey, issuedAt, validUntil); 21 22 Assert.True(authorityRoot.VerifyServerCertificate(certificate, ReferenceNow)); 23 } 24 25 [Fact] 26 public void VerifyServerCertificate_ReturnsFalse_ForExpiredCertificate() 27 { 28 var rootPrivateKey = CertificateIssuer.GeneratePrivateKey(); 29 var certificateIssuer = new CertificateIssuer(rootPrivateKey); 30 var authorityRoot = new AuthorityRoot(certificateIssuer.RootPublicKey); 31 var serverId = Guid.NewGuid(); 32 var serverPublicKey = ServerIdentity.GetPublicKey(ServerIdentity.GeneratePrivateKey()); 33 34 var certificate = certificateIssuer.IssueServerCertificate( 35 serverId, 36 serverPublicKey, 37 ReferenceNow.AddHours(-2), 38 ReferenceNow.AddHours(-1)); 39 40 Assert.False(authorityRoot.VerifyServerCertificate(certificate, ReferenceNow)); 41 } 42 43 [Fact] 44 public void VerifyRunSignature_ReturnsTrue_ForValidSignedValidation() 45 { 46 var serverIdentity = CreateServerIdentity(out var authorityRoot); 47 var verifier = new SignatureVerifier(authorityRoot); 48 49 var validation = serverIdentity.SignRunValidation(Guid.NewGuid(), Guid.NewGuid(), CreateHash(1)); 50 51 Assert.True(verifier.VerifyRunSignature(validation, serverIdentity.Certificate, ReferenceNow)); 52 } 53 54 [Fact] 55 public void VerifyRunSignature_ReturnsFalse_WhenFinalStateHashIsTampered() 56 { 57 var serverIdentity = CreateServerIdentity(out var authorityRoot); 58 var verifier = new SignatureVerifier(authorityRoot); 59 var validation = serverIdentity.SignRunValidation(Guid.NewGuid(), Guid.NewGuid(), CreateHash(10)); 60 var tampered = new RunValidationSignature( 61 validation.RunId, 62 validation.PlayerId, 63 CreateHash(11), 64 validation.ServerId, 65 validation.Signature); 66 67 Assert.False(verifier.VerifyRunSignature(tampered, serverIdentity.Certificate, ReferenceNow)); 68 } 69 70 [Fact] 71 public void VerifyRunSignature_ReturnsFalse_WhenServerIdDoesNotMatchCertificate() 72 { 73 var serverIdentity = CreateServerIdentity(out var authorityRoot); 74 var verifier = new SignatureVerifier(authorityRoot); 75 var validation = serverIdentity.SignRunValidation(Guid.NewGuid(), Guid.NewGuid(), CreateHash(7)); 76 var mismatched = new RunValidationSignature( 77 validation.RunId, 78 validation.PlayerId, 79 validation.FinalStateHash, 80 Guid.NewGuid(), 81 validation.Signature); 82 83 Assert.False(verifier.VerifyRunSignature(mismatched, serverIdentity.Certificate, ReferenceNow)); 84 } 85 86 [Fact] 87 public void VerifySignedRunValidation_Succeeds_WhenCertificateAndSignatureValid() 88 { 89 var serverIdentity = CreateServerIdentity(out var authorityRoot); 90 var verifier = new SignatureVerifier(authorityRoot); 91 92 var signedValidation = serverIdentity.SignSignedRunValidation(Guid.NewGuid(), Guid.NewGuid(), CreateHash(11)); 93 94 Assert.True(verifier.Verify(signedValidation, ReferenceNow)); 95 } 96 97 [Fact] 98 public void SignRunValidation_Throws_WhenFinalStateHashIsNotSha256Length() 99 { 100 var serverIdentity = CreateServerIdentity(out _); 101 102 Assert.Throws<ArgumentException>(() => 103 serverIdentity.SignRunValidation(Guid.NewGuid(), Guid.NewGuid(), [1, 2, 3, 4])); 104 } 105 106 private static ServerIdentity CreateServerIdentity(out AuthorityRoot authorityRoot) 107 { 108 var rootPrivateKey = CertificateIssuer.GeneratePrivateKey(); 109 var certificateIssuer = new CertificateIssuer(rootPrivateKey); 110 authorityRoot = new AuthorityRoot(certificateIssuer.RootPublicKey); 111 112 var serverPrivateKey = ServerIdentity.GeneratePrivateKey(); 113 var certificate = certificateIssuer.IssueServerCertificate( 114 Guid.NewGuid(), 115 ServerIdentity.GetPublicKey(serverPrivateKey), 116 ReferenceNow.AddMinutes(-5), 117 ReferenceNow.AddMinutes(30)); 118 119 return new ServerIdentity(certificate, serverPrivateKey); 120 } 121 122 private static byte[] CreateHash(byte seed) 123 { 124 var value = new byte[32]; 125 for (var i = 0; i < value.Length; i++) 126 { 127 value[i] = (byte)(seed + i); 128 } 129 130 return value; 131 } 132 }