Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
- Added MongoPackRunApprovalStore for managing approval states with MongoDB. - Introduced MongoPackRunArtifactUploader for uploading and storing artifacts. - Created MongoPackRunLogStore to handle logging of pack run events. - Developed MongoPackRunStateStore for persisting and retrieving pack run states. - Implemented unit tests for MongoDB stores to ensure correct functionality. - Added MongoTaskRunnerTestContext for setting up MongoDB test environment. - Enhanced PackRunStateFactory to correctly initialize state with gate reasons.
173 lines
6.1 KiB
C#
173 lines
6.1 KiB
C#
using Google.Cloud.Kms.V1;
|
|
using Google.Protobuf;
|
|
using Google.Protobuf.WellKnownTypes;
|
|
|
|
namespace StellaOps.Cryptography.Kms;
|
|
|
|
public interface IGcpKmsFacade : IDisposable
|
|
{
|
|
Task<GcpSignResult> SignAsync(string versionName, ReadOnlyMemory<byte> digest, CancellationToken cancellationToken);
|
|
|
|
Task<GcpCryptoKeyMetadata> GetCryptoKeyMetadataAsync(string keyName, CancellationToken cancellationToken);
|
|
|
|
Task<IReadOnlyList<GcpCryptoKeyVersionMetadata>> ListKeyVersionsAsync(string keyName, CancellationToken cancellationToken);
|
|
|
|
Task<GcpPublicKeyMaterial> GetPublicKeyAsync(string versionName, CancellationToken cancellationToken);
|
|
}
|
|
|
|
public sealed record GcpSignResult(string VersionName, byte[] Signature);
|
|
|
|
public sealed record GcpCryptoKeyMetadata(string KeyName, string? PrimaryVersionName, DateTimeOffset CreateTime);
|
|
|
|
public enum GcpCryptoKeyVersionState
|
|
{
|
|
Unspecified = 0,
|
|
PendingGeneration = 1,
|
|
Enabled = 2,
|
|
Disabled = 3,
|
|
DestroyScheduled = 4,
|
|
Destroyed = 5,
|
|
PendingImport = 6,
|
|
ImportFailed = 7,
|
|
GenerationFailed = 8,
|
|
}
|
|
|
|
public sealed record GcpCryptoKeyVersionMetadata(
|
|
string VersionName,
|
|
GcpCryptoKeyVersionState State,
|
|
DateTimeOffset CreateTime,
|
|
DateTimeOffset? DestroyTime);
|
|
|
|
public sealed record GcpPublicKeyMaterial(string VersionName, string Algorithm, string Pem);
|
|
|
|
internal sealed class GcpKmsFacade : IGcpKmsFacade
|
|
{
|
|
private readonly KeyManagementServiceClient _client;
|
|
private readonly bool _ownsClient;
|
|
|
|
public GcpKmsFacade(GcpKmsOptions options)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(options);
|
|
var builder = new KeyManagementServiceClientBuilder
|
|
{
|
|
Endpoint = string.IsNullOrWhiteSpace(options.Endpoint)
|
|
? KeyManagementServiceClient.DefaultEndpoint
|
|
: options.Endpoint,
|
|
};
|
|
|
|
_client = builder.Build();
|
|
_ownsClient = true;
|
|
}
|
|
|
|
public GcpKmsFacade(KeyManagementServiceClient client)
|
|
{
|
|
_client = client ?? throw new ArgumentNullException(nameof(client));
|
|
_ownsClient = false;
|
|
}
|
|
|
|
public async Task<GcpSignResult> SignAsync(string versionName, ReadOnlyMemory<byte> digest, CancellationToken cancellationToken)
|
|
{
|
|
ArgumentException.ThrowIfNullOrWhiteSpace(versionName);
|
|
|
|
var response = await _client.AsymmetricSignAsync(new AsymmetricSignRequest
|
|
{
|
|
Name = versionName,
|
|
Digest = new Digest
|
|
{
|
|
Sha256 = ByteString.CopyFrom(digest.ToArray()),
|
|
},
|
|
}, cancellationToken).ConfigureAwait(false);
|
|
|
|
return new GcpSignResult(response.Name ?? versionName, response.Signature.ToByteArray());
|
|
}
|
|
|
|
public async Task<GcpCryptoKeyMetadata> GetCryptoKeyMetadataAsync(string keyName, CancellationToken cancellationToken)
|
|
{
|
|
ArgumentException.ThrowIfNullOrWhiteSpace(keyName);
|
|
|
|
var response = await _client.GetCryptoKeyAsync(new GetCryptoKeyRequest
|
|
{
|
|
Name = keyName,
|
|
}, cancellationToken).ConfigureAwait(false);
|
|
|
|
return new GcpCryptoKeyMetadata(
|
|
response.Name,
|
|
response.Primary?.Name,
|
|
ToDateTimeOffsetOrUtcNow(response.CreateTime));
|
|
}
|
|
|
|
public async Task<IReadOnlyList<GcpCryptoKeyVersionMetadata>> ListKeyVersionsAsync(string keyName, CancellationToken cancellationToken)
|
|
{
|
|
ArgumentException.ThrowIfNullOrWhiteSpace(keyName);
|
|
|
|
var results = new List<GcpCryptoKeyVersionMetadata>();
|
|
var request = new ListCryptoKeyVersionsRequest
|
|
{
|
|
Parent = keyName,
|
|
};
|
|
|
|
await foreach (var version in _client.ListCryptoKeyVersionsAsync(request).WithCancellation(cancellationToken).ConfigureAwait(false))
|
|
{
|
|
results.Add(new GcpCryptoKeyVersionMetadata(
|
|
version.Name,
|
|
MapState(version.State),
|
|
ToDateTimeOffsetOrUtcNow(version.CreateTime),
|
|
version.DestroyTime is null ? null : ToDateTimeOffsetOrUtcNow(version.DestroyTime)));
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
public async Task<GcpPublicKeyMaterial> GetPublicKeyAsync(string versionName, CancellationToken cancellationToken)
|
|
{
|
|
ArgumentException.ThrowIfNullOrWhiteSpace(versionName);
|
|
|
|
var response = await _client.GetPublicKeyAsync(new GetPublicKeyRequest
|
|
{
|
|
Name = versionName,
|
|
}, cancellationToken).ConfigureAwait(false);
|
|
|
|
return new GcpPublicKeyMaterial(
|
|
response.Name ?? versionName,
|
|
response.Algorithm.ToString(),
|
|
response.Pem);
|
|
}
|
|
|
|
private static GcpCryptoKeyVersionState MapState(CryptoKeyVersion.Types.CryptoKeyVersionState state)
|
|
=> state switch
|
|
{
|
|
CryptoKeyVersion.Types.CryptoKeyVersionState.Enabled => GcpCryptoKeyVersionState.Enabled,
|
|
CryptoKeyVersion.Types.CryptoKeyVersionState.Disabled => GcpCryptoKeyVersionState.Disabled,
|
|
CryptoKeyVersion.Types.CryptoKeyVersionState.DestroyScheduled => GcpCryptoKeyVersionState.DestroyScheduled,
|
|
CryptoKeyVersion.Types.CryptoKeyVersionState.Destroyed => GcpCryptoKeyVersionState.Destroyed,
|
|
CryptoKeyVersion.Types.CryptoKeyVersionState.PendingGeneration => GcpCryptoKeyVersionState.PendingGeneration,
|
|
CryptoKeyVersion.Types.CryptoKeyVersionState.PendingImport => GcpCryptoKeyVersionState.PendingImport,
|
|
CryptoKeyVersion.Types.CryptoKeyVersionState.ImportFailed => GcpCryptoKeyVersionState.ImportFailed,
|
|
CryptoKeyVersion.Types.CryptoKeyVersionState.GenerationFailed => GcpCryptoKeyVersionState.GenerationFailed,
|
|
_ => GcpCryptoKeyVersionState.Unspecified,
|
|
};
|
|
|
|
public void Dispose()
|
|
{
|
|
if (_ownsClient && _client is IDisposable disposable)
|
|
{
|
|
disposable.Dispose();
|
|
}
|
|
}
|
|
|
|
private static DateTimeOffset ToDateTimeOffsetOrUtcNow(Timestamp? timestamp)
|
|
{
|
|
if (timestamp is null)
|
|
{
|
|
return DateTimeOffset.UtcNow;
|
|
}
|
|
|
|
if (timestamp.Seconds == 0 && timestamp.Nanos == 0)
|
|
{
|
|
return DateTimeOffset.UtcNow;
|
|
}
|
|
|
|
return timestamp.ToDateTimeOffset();
|
|
}
|
|
}
|