This commit is contained in:
		| @@ -0,0 +1,112 @@ | ||||
| using System.Security.Cryptography; | ||||
| using StellaOps.Cryptography.Kms; | ||||
|  | ||||
| namespace StellaOps.Cryptography.Kms.Tests; | ||||
|  | ||||
| public sealed class FileKmsClientTests : IDisposable | ||||
| { | ||||
|     private readonly string _rootPath; | ||||
|  | ||||
|     public FileKmsClientTests() | ||||
|     { | ||||
|         _rootPath = Path.Combine(Path.GetTempPath(), $"kms-tests-{Guid.NewGuid():N}"); | ||||
|     } | ||||
|  | ||||
|     [Fact] | ||||
|     public async Task RotateSignVerifyLifecycle_Works() | ||||
|     { | ||||
|         using var client = CreateClient(); | ||||
|         var keyId = "kms-test-key"; | ||||
|  | ||||
|         // Initial rotate creates the key. | ||||
|         var metadata = await client.RotateAsync(keyId); | ||||
|         Assert.Equal(keyId, metadata.KeyId); | ||||
|         Assert.Single(metadata.Versions); | ||||
|         Assert.Equal(KmsKeyState.Active, metadata.State); | ||||
|         var version = metadata.Versions[0]; | ||||
|         Assert.Equal(KmsKeyState.Active, version.State); | ||||
|  | ||||
|         var firstData = RandomNumberGenerator.GetBytes(256); | ||||
|         var firstSignature = await client.SignAsync(keyId, null, firstData); | ||||
|         Assert.Equal(keyId, firstSignature.KeyId); | ||||
|         Assert.Equal(KmsAlgorithms.Es256, firstSignature.Algorithm); | ||||
|         Assert.True(await client.VerifyAsync(keyId, firstSignature.VersionId, firstData, firstSignature.Signature)); | ||||
|  | ||||
|         // Rotate again and ensure metadata reflects both versions. | ||||
|         var rotated = await client.RotateAsync(keyId); | ||||
|         Assert.Equal(2, rotated.Versions.Length); | ||||
|         var activeVersion = rotated.Versions.Single(v => v.State == KmsKeyState.Active); | ||||
|         Assert.Equal(rotated.Versions.Max(v => v.VersionId), activeVersion.VersionId); | ||||
|         var previousVersion = rotated.Versions.Single(v => v.State != KmsKeyState.Active); | ||||
|         Assert.Equal(KmsKeyState.PendingRotation, previousVersion.State); | ||||
|  | ||||
|         var newData = RandomNumberGenerator.GetBytes(128); | ||||
|         var activeSignature = await client.SignAsync(keyId, null, newData); | ||||
|         Assert.Equal(activeVersion.VersionId, activeSignature.VersionId); | ||||
|         Assert.True(await client.VerifyAsync(keyId, null, newData, activeSignature.Signature)); | ||||
|  | ||||
|         // Explicit version verify should still pass for previous version using the old signature. | ||||
|         Assert.True(await client.VerifyAsync(keyId, previousVersion.VersionId, firstData, firstSignature.Signature)); | ||||
|     } | ||||
|  | ||||
|     [Fact] | ||||
|     public async Task RevokePreventsSigning() | ||||
|     { | ||||
|         using var client = CreateClient(); | ||||
|         var keyId = "kms-revoke"; | ||||
|  | ||||
|         await client.RotateAsync(keyId); | ||||
|         await client.RevokeAsync(keyId); | ||||
|  | ||||
|         var metadata = await client.GetMetadataAsync(keyId); | ||||
|         Assert.Equal(KmsKeyState.Revoked, metadata.State); | ||||
|         Assert.All(metadata.Versions, v => Assert.Equal(KmsKeyState.Revoked, v.State)); | ||||
|  | ||||
|         var data = RandomNumberGenerator.GetBytes(32); | ||||
|         await Assert.ThrowsAsync<InvalidOperationException>(() => client.SignAsync(keyId, null, data)); | ||||
|     } | ||||
|  | ||||
|     [Fact] | ||||
|     public async Task ExportAsync_ReturnsKeyMaterial() | ||||
|     { | ||||
|         using var client = CreateClient(); | ||||
|         var keyId = "kms-export"; | ||||
|  | ||||
|         await client.RotateAsync(keyId); | ||||
|         var material = await client.ExportAsync(keyId, null); | ||||
|  | ||||
|         Assert.Equal(keyId, material.KeyId); | ||||
|         Assert.Equal(KmsAlgorithms.Es256, material.Algorithm); | ||||
|         Assert.Equal("nistP256", material.Curve); | ||||
|         Assert.NotEmpty(material.D); | ||||
|         Assert.NotEmpty(material.Qx); | ||||
|         Assert.NotEmpty(material.Qy); | ||||
|     } | ||||
|  | ||||
|     private FileKmsClient CreateClient() | ||||
|     { | ||||
|         var options = new FileKmsOptions | ||||
|         { | ||||
|             RootPath = _rootPath, | ||||
|             Password = "P@ssw0rd!", | ||||
|             Algorithm = KmsAlgorithms.Es256, | ||||
|         }; | ||||
|  | ||||
|         return new FileKmsClient(options); | ||||
|     } | ||||
|  | ||||
|     public void Dispose() | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             if (Directory.Exists(_rootPath)) | ||||
|             { | ||||
|                 Directory.Delete(_rootPath, recursive: true); | ||||
|             } | ||||
|         } | ||||
|         catch | ||||
|         { | ||||
|             // ignore cleanup errors | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,20 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
|   <PropertyGroup> | ||||
|     <TargetFramework>net10.0</TargetFramework> | ||||
|     <ImplicitUsings>enable</ImplicitUsings> | ||||
|     <Nullable>enable</Nullable> | ||||
|     <UseConcelierTestInfra>false</UseConcelierTestInfra> | ||||
|   </PropertyGroup> | ||||
|   <ItemGroup> | ||||
|     <PackageReference Include="coverlet.collector" Version="6.0.4" /> | ||||
|     <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" /> | ||||
|     <PackageReference Include="xunit" Version="2.9.2" /> | ||||
|     <PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" /> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <Using Include="Xunit" /> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <ProjectReference Include="../../StellaOps.Cryptography.Kms/StellaOps.Cryptography.Kms.csproj" /> | ||||
|   </ItemGroup> | ||||
| </Project> | ||||
		Reference in New Issue
	
	Block a user