search and ai stabilization work, localization stablized.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Cryptography.Kms;
|
||||
|
||||
@@ -11,7 +12,7 @@ public sealed partial class AwsKmsClient
|
||||
var digest = new byte[32];
|
||||
if (!SHA256.TryHashData(data.Span, digest, out _))
|
||||
{
|
||||
throw new InvalidOperationException("Failed to hash payload with SHA-256.");
|
||||
throw new InvalidOperationException(_t("crypto.kms.hash_failed"));
|
||||
}
|
||||
|
||||
return digest;
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Immutable;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Cryptography.Kms;
|
||||
|
||||
@@ -56,8 +57,8 @@ public sealed partial class AwsKmsClient
|
||||
KmsAlgorithms.Es256,
|
||||
ResolveCurveName(publicKey.Curve),
|
||||
Array.Empty<byte>(),
|
||||
parameters.Q.X ?? throw new InvalidOperationException("Public key missing X coordinate."),
|
||||
parameters.Q.Y ?? throw new InvalidOperationException("Public key missing Y coordinate."),
|
||||
parameters.Q.X ?? throw new InvalidOperationException(_t("crypto.kms.public_key_missing_x")),
|
||||
parameters.Q.Y ?? throw new InvalidOperationException(_t("crypto.kms.public_key_missing_y")),
|
||||
metadata.CreatedAt);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ using Microsoft.Extensions.Options;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading.Tasks;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Cryptography.Kms;
|
||||
|
||||
@@ -34,10 +35,10 @@ public sealed partial class AwsKmsClient : IKmsClient, IDisposable
|
||||
}
|
||||
|
||||
public Task<KmsKeyMetadata> RotateAsync(string keyId, CancellationToken cancellationToken = default)
|
||||
=> throw new NotSupportedException("AWS KMS rotation must be orchestrated via AWS KMS policies or schedules.");
|
||||
=> throw new NotSupportedException(_t("crypto.kms.rotation_via_policy", "AWS KMS", "AWS KMS"));
|
||||
|
||||
public Task RevokeAsync(string keyId, CancellationToken cancellationToken = default)
|
||||
=> throw new NotSupportedException("AWS KMS key revocation must be managed through AWS KMS APIs or console.");
|
||||
=> throw new NotSupportedException(_t("crypto.kms.revocation_via_policy", "AWS KMS key", "AWS KMS"));
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@ using Amazon.KeyManagementService.Model;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Cryptography.Kms;
|
||||
|
||||
@@ -16,7 +17,7 @@ internal sealed partial class AwsKmsFacade
|
||||
KeyId = keyId,
|
||||
}, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var metadata = response.KeyMetadata ?? throw new InvalidOperationException($"Key '{keyId}' was not found.");
|
||||
var metadata = response.KeyMetadata ?? throw new InvalidOperationException(_t("crypto.kms.key_not_found", keyId));
|
||||
var createdAt = metadata.CreationDate?.ToUniversalTime() ?? _timeProvider.GetUtcNow();
|
||||
|
||||
return new AwsKeyMetadata(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Cryptography.Kms;
|
||||
|
||||
@@ -18,8 +19,8 @@ public sealed partial class Fido2KmsClient
|
||||
metadata.Algorithm,
|
||||
_curveName,
|
||||
Array.Empty<byte>(),
|
||||
_publicParameters.Q.X ?? throw new InvalidOperationException("FIDO2 public key missing X coordinate."),
|
||||
_publicParameters.Q.Y ?? throw new InvalidOperationException("FIDO2 public key missing Y coordinate."),
|
||||
_publicParameters.Q.X ?? throw new InvalidOperationException(_t("crypto.fido2.missing_x")),
|
||||
_publicParameters.Q.Y ?? throw new InvalidOperationException(_t("crypto.fido2.missing_y")),
|
||||
_options.CreatedAt ?? _timeProvider.GetUtcNow());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Cryptography.Kms;
|
||||
|
||||
@@ -11,7 +12,7 @@ public sealed partial class Fido2KmsClient
|
||||
var digest = new byte[32];
|
||||
if (!SHA256.TryHashData(data.Span, digest, out _))
|
||||
{
|
||||
throw new InvalidOperationException("Failed to hash payload with SHA-256.");
|
||||
throw new InvalidOperationException(_t("crypto.kms.hash_failed"));
|
||||
}
|
||||
|
||||
return digest;
|
||||
@@ -33,7 +34,7 @@ public sealed partial class Fido2KmsClient
|
||||
"1.2.840.10045.3.1.7" => JsonWebKeyECTypes.P256,
|
||||
"1.3.132.0.34" => JsonWebKeyECTypes.P384,
|
||||
"1.3.132.0.35" => JsonWebKeyECTypes.P521,
|
||||
_ => throw new InvalidOperationException($"Unsupported FIDO2 curve OID '{oid}'."),
|
||||
_ => throw new InvalidOperationException(_t("crypto.fido2.curve_unsupported", oid ?? string.Empty)),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,17 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Cryptography.Kms;
|
||||
|
||||
public sealed partial class Fido2KmsClient
|
||||
{
|
||||
public Task<KmsKeyMetadata> RotateAsync(string keyId, CancellationToken cancellationToken = default)
|
||||
=> throw new NotSupportedException("FIDO2 credential rotation requires new enrolment.");
|
||||
=> throw new NotSupportedException(_t("crypto.fido2.rotation_required"));
|
||||
|
||||
public Task RevokeAsync(string keyId, CancellationToken cancellationToken = default)
|
||||
=> throw new NotSupportedException("FIDO2 credential revocation must be managed in the relying party.");
|
||||
=> throw new NotSupportedException(_t("crypto.fido2.revocation_relying_party"));
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text.Json;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Cryptography.Kms;
|
||||
|
||||
@@ -10,7 +11,7 @@ public sealed partial class FileKmsClient
|
||||
{
|
||||
if (!string.Equals(algorithm, KmsAlgorithms.Es256, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new NotSupportedException($"Algorithm '{algorithm}' is not supported by the file KMS driver.");
|
||||
throw new NotSupportedException(_t("crypto.kms.algorithm_unsupported", algorithm));
|
||||
}
|
||||
|
||||
using var ecdsa = ECDsa.Create(ECCurve.NamedCurves.nistP256);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Cryptography.Kms;
|
||||
|
||||
@@ -53,6 +54,6 @@ public sealed partial class FileKmsClient
|
||||
private static ECCurve ResolveCurve(string curveName) => curveName switch
|
||||
{
|
||||
"nistP256" or "P-256" or "ES256" => ECCurve.NamedCurves.nistP256,
|
||||
_ => throw new NotSupportedException($"Curve '{curveName}' is not supported."),
|
||||
_ => throw new NotSupportedException(_t("crypto.kms.curve_unsupported", curveName)),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Security.Cryptography;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Cryptography.Kms;
|
||||
|
||||
@@ -32,11 +33,11 @@ public sealed partial class FileKmsClient
|
||||
try
|
||||
{
|
||||
var record = await LoadOrCreateMetadataAsync(keyId, cancellationToken, createIfMissing: true).ConfigureAwait(false)
|
||||
?? throw new InvalidOperationException("Failed to create or load key metadata.");
|
||||
?? throw new InvalidOperationException(_t("crypto.kms.metadata_failed"));
|
||||
|
||||
if (!string.Equals(record.Algorithm, material.Algorithm, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new InvalidOperationException($"Algorithm mismatch. Expected '{record.Algorithm}', received '{material.Algorithm}'.");
|
||||
throw new InvalidOperationException(_t("crypto.kms.algorithm_mismatch", record.Algorithm, material.Algorithm));
|
||||
}
|
||||
|
||||
var versionId = string.IsNullOrWhiteSpace(material.VersionId)
|
||||
@@ -45,7 +46,7 @@ public sealed partial class FileKmsClient
|
||||
|
||||
if (record.Versions.Any(v => string.Equals(v.VersionId, versionId, StringComparison.Ordinal)))
|
||||
{
|
||||
throw new InvalidOperationException($"Key version '{versionId}' already exists for key '{record.KeyId}'.");
|
||||
throw new InvalidOperationException(_t("crypto.kms.version_exists", versionId, record.KeyId));
|
||||
}
|
||||
|
||||
var curveName = string.IsNullOrWhiteSpace(material.Curve) ? "nistP256" : material.Curve;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Cryptography.Kms;
|
||||
|
||||
@@ -14,7 +15,7 @@ public sealed partial class FileKmsClient
|
||||
try
|
||||
{
|
||||
var record = await LoadOrCreateMetadataAsync(keyId, cancellationToken, createIfMissing: false).ConfigureAwait(false)
|
||||
?? throw new InvalidOperationException($"Key '{keyId}' does not exist.");
|
||||
?? throw new InvalidOperationException(_t("crypto.kms.key_not_found", keyId));
|
||||
return ToMetadata(record);
|
||||
}
|
||||
finally
|
||||
@@ -31,12 +32,12 @@ public sealed partial class FileKmsClient
|
||||
try
|
||||
{
|
||||
var record = await LoadOrCreateMetadataAsync(keyId, cancellationToken, createIfMissing: false).ConfigureAwait(false)
|
||||
?? throw new InvalidOperationException($"Key '{keyId}' does not exist.");
|
||||
?? throw new InvalidOperationException(_t("crypto.kms.key_not_found", keyId));
|
||||
|
||||
var version = ResolveVersion(record, keyVersion);
|
||||
if (string.IsNullOrWhiteSpace(version.PublicKey))
|
||||
{
|
||||
throw new InvalidOperationException($"Key '{keyId}' version '{version.VersionId}' does not have public key material.");
|
||||
throw new InvalidOperationException(_t("crypto.kms.key_no_public_material", keyId, version.VersionId));
|
||||
}
|
||||
|
||||
var privateKey = await LoadPrivateKeyAsync(record, version, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Cryptography.Kms;
|
||||
|
||||
@@ -24,7 +25,7 @@ public sealed partial class FileKmsClient
|
||||
version = record.Versions.SingleOrDefault(v => string.Equals(v.VersionId, keyVersion, StringComparison.Ordinal));
|
||||
if (version is null)
|
||||
{
|
||||
throw new InvalidOperationException($"Key version '{keyVersion}' does not exist for key '{record.KeyId}'.");
|
||||
throw new InvalidOperationException(_t("crypto.kms.key_version_not_found", keyVersion, record.KeyId));
|
||||
}
|
||||
}
|
||||
else if (!string.IsNullOrWhiteSpace(record.ActiveVersion))
|
||||
@@ -39,7 +40,7 @@ public sealed partial class FileKmsClient
|
||||
|
||||
if (version is null)
|
||||
{
|
||||
throw new InvalidOperationException($"Key '{record.KeyId}' does not have an active version.");
|
||||
throw new InvalidOperationException(_t("crypto.kms.key_no_active_version", record.KeyId));
|
||||
}
|
||||
|
||||
return version;
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Security.Cryptography;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Cryptography.Kms;
|
||||
|
||||
@@ -70,18 +71,18 @@ public sealed partial class FileKmsClient
|
||||
var keyPath = Path.Combine(GetKeyDirectory(record.KeyId), version.FileName);
|
||||
if (!File.Exists(keyPath))
|
||||
{
|
||||
throw new InvalidOperationException($"Key material for version '{version.VersionId}' was not found.");
|
||||
throw new InvalidOperationException(_t("crypto.kms.material_not_found", version.VersionId));
|
||||
}
|
||||
|
||||
await using var stream = File.Open(keyPath, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
var envelope = await JsonSerializer.DeserializeAsync<KeyEnvelope>(stream, _jsonOptions, cancellationToken).ConfigureAwait(false)
|
||||
?? throw new InvalidOperationException("Key envelope could not be deserialized.");
|
||||
?? throw new InvalidOperationException(_t("crypto.kms.envelope_deserialize_failed"));
|
||||
|
||||
var payload = DecryptPrivateKey(envelope);
|
||||
try
|
||||
{
|
||||
return JsonSerializer.Deserialize<EcdsaPrivateKeyRecord>(payload, _jsonOptions)
|
||||
?? throw new InvalidOperationException("Key payload could not be deserialized.");
|
||||
?? throw new InvalidOperationException(_t("crypto.kms.payload_deserialize_failed"));
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Cryptography.Kms;
|
||||
|
||||
@@ -17,11 +18,11 @@ public sealed partial class FileKmsClient
|
||||
try
|
||||
{
|
||||
var record = await LoadOrCreateMetadataAsync(keyId, cancellationToken, createIfMissing: true).ConfigureAwait(false)
|
||||
?? throw new InvalidOperationException("Failed to create or load key metadata.");
|
||||
?? throw new InvalidOperationException(_t("crypto.kms.metadata_failed"));
|
||||
|
||||
if (record.State == KmsKeyState.Revoked)
|
||||
{
|
||||
throw new InvalidOperationException($"Key '{keyId}' has been revoked and cannot be rotated.");
|
||||
throw new InvalidOperationException(_t("crypto.kms.key_revoked", keyId));
|
||||
}
|
||||
|
||||
var timestamp = _timeProvider.GetUtcNow();
|
||||
@@ -76,7 +77,7 @@ public sealed partial class FileKmsClient
|
||||
try
|
||||
{
|
||||
var record = await LoadOrCreateMetadataAsync(keyId, cancellationToken, createIfMissing: false).ConfigureAwait(false)
|
||||
?? throw new InvalidOperationException($"Key '{keyId}' does not exist.");
|
||||
?? throw new InvalidOperationException(_t("crypto.kms.key_not_found", keyId));
|
||||
|
||||
var timestamp = _timeProvider.GetUtcNow();
|
||||
record.State = KmsKeyState.Revoked;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Cryptography.Kms;
|
||||
|
||||
@@ -22,17 +23,17 @@ public sealed partial class FileKmsClient
|
||||
try
|
||||
{
|
||||
var record = await LoadOrCreateMetadataAsync(keyId, cancellationToken, createIfMissing: false).ConfigureAwait(false)
|
||||
?? throw new InvalidOperationException($"Key '{keyId}' does not exist.");
|
||||
?? throw new InvalidOperationException(_t("crypto.kms.key_not_found", keyId));
|
||||
|
||||
if (record.State == KmsKeyState.Revoked)
|
||||
{
|
||||
throw new InvalidOperationException($"Key '{keyId}' is revoked and cannot be used for signing.");
|
||||
throw new InvalidOperationException(_t("crypto.kms.key_revoked_signing", keyId));
|
||||
}
|
||||
|
||||
var version = ResolveVersion(record, keyVersion);
|
||||
if (version.State != KmsKeyState.Active)
|
||||
{
|
||||
throw new InvalidOperationException($"Key version '{version.VersionId}' is not active. Current state: {version.State}");
|
||||
throw new InvalidOperationException(_t("crypto.kms.key_version_inactive", version.VersionId, version.State));
|
||||
}
|
||||
|
||||
var privateKey = await LoadPrivateKeyAsync(record, version, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
@@ -3,6 +3,7 @@ using System;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Cryptography.Kms;
|
||||
|
||||
@@ -30,7 +31,7 @@ public sealed partial class GcpKmsClient
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(pem))
|
||||
{
|
||||
throw new InvalidOperationException("Public key PEM cannot be empty.");
|
||||
throw new InvalidOperationException(_t("crypto.kms.pem_empty"));
|
||||
}
|
||||
|
||||
var builder = new StringBuilder(pem.Length);
|
||||
@@ -54,7 +55,7 @@ public sealed partial class GcpKmsClient
|
||||
var digest = new byte[32];
|
||||
if (!SHA256.TryHashData(data.Span, digest, out _))
|
||||
{
|
||||
throw new InvalidOperationException("Failed to hash payload with SHA-256.");
|
||||
throw new InvalidOperationException(_t("crypto.kms.hash_failed"));
|
||||
}
|
||||
|
||||
return digest;
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Immutable;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Cryptography.Kms;
|
||||
|
||||
@@ -64,8 +65,8 @@ public sealed partial class GcpKmsClient
|
||||
KmsAlgorithms.Es256,
|
||||
ResolveCurve(publicMaterial.Algorithm),
|
||||
Array.Empty<byte>(),
|
||||
parameters.Q.X ?? throw new InvalidOperationException("Public key missing X coordinate."),
|
||||
parameters.Q.Y ?? throw new InvalidOperationException("Public key missing Y coordinate."),
|
||||
parameters.Q.X ?? throw new InvalidOperationException(_t("crypto.kms.public_key_missing_x")),
|
||||
parameters.Q.Y ?? throw new InvalidOperationException(_t("crypto.kms.public_key_missing_y")),
|
||||
snapshot.Metadata.CreateTime);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Cryptography.Kms;
|
||||
|
||||
@@ -25,6 +26,6 @@ public sealed partial class GcpKmsClient
|
||||
return firstActive.VersionName;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException($"Crypto key '{keyId}' does not have an active primary version.");
|
||||
throw new InvalidOperationException(_t("crypto.kms.no_primary_version", keyId));
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ using Microsoft.Extensions.Options;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading.Tasks;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Cryptography.Kms;
|
||||
|
||||
@@ -35,10 +36,10 @@ public sealed partial class GcpKmsClient : IKmsClient, IDisposable
|
||||
}
|
||||
|
||||
public Task<KmsKeyMetadata> RotateAsync(string keyId, CancellationToken cancellationToken = default)
|
||||
=> throw new NotSupportedException("Google Cloud KMS rotation must be managed via Cloud KMS rotation schedules.");
|
||||
=> throw new NotSupportedException(_t("crypto.kms.rotation_via_policy", "Google Cloud KMS", "Cloud KMS rotation schedules"));
|
||||
|
||||
public Task RevokeAsync(string keyId, CancellationToken cancellationToken = default)
|
||||
=> throw new NotSupportedException("Google Cloud KMS key revocation must be managed via Cloud KMS destroy/disable operations.");
|
||||
=> throw new NotSupportedException(_t("crypto.kms.revocation_via_policy", "Google Cloud KMS key", "Cloud KMS destroy/disable"));
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using StellaOps.Cryptography;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Cryptography.Kms;
|
||||
|
||||
@@ -29,10 +30,10 @@ public sealed partial class KmsCryptoProvider : ICryptoProvider
|
||||
}
|
||||
|
||||
public IPasswordHasher GetPasswordHasher(string algorithmId)
|
||||
=> throw new InvalidOperationException($"Provider '{Name}' does not support password hashing.");
|
||||
=> throw new InvalidOperationException(_t("crypto.provider.no_password_hashing", Name));
|
||||
|
||||
public ICryptoHasher GetHasher(string algorithmId)
|
||||
=> throw new InvalidOperationException($"Provider '{Name}' does not support content hashing.");
|
||||
=> throw new InvalidOperationException(_t("crypto.provider.no_content_hashing", Name));
|
||||
|
||||
public ICryptoSigner GetSigner(string algorithmId, CryptoKeyReference keyReference)
|
||||
{
|
||||
@@ -40,12 +41,12 @@ public sealed partial class KmsCryptoProvider : ICryptoProvider
|
||||
|
||||
if (!Supports(CryptoCapability.Signing, algorithmId))
|
||||
{
|
||||
throw new InvalidOperationException($"Signing algorithm '{algorithmId}' is not supported by provider '{Name}'.");
|
||||
throw new InvalidOperationException(_t("crypto.provider.algorithm_not_supported", algorithmId, Name));
|
||||
}
|
||||
|
||||
if (!_registrations.TryGetValue(keyReference.KeyId, out var registration))
|
||||
{
|
||||
throw new KeyNotFoundException($"Signing key '{keyReference.KeyId}' is not registered with provider '{Name}'.");
|
||||
throw new KeyNotFoundException(_t("crypto.provider.key_not_registered", keyReference.KeyId, Name));
|
||||
}
|
||||
|
||||
return new KmsSigner(_kmsClient, registration);
|
||||
@@ -57,14 +58,14 @@ public sealed partial class KmsCryptoProvider : ICryptoProvider
|
||||
|
||||
if (!string.Equals(signingKey.AlgorithmId, KmsAlgorithms.Es256, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new InvalidOperationException($"Provider '{Name}' only supports {KmsAlgorithms.Es256} signing keys.");
|
||||
throw new InvalidOperationException(_t("crypto.kms.es256_only", Name));
|
||||
}
|
||||
|
||||
if (signingKey.Metadata is null ||
|
||||
!signingKey.Metadata.TryGetValue(KmsMetadataKeys.Version, out var versionId) ||
|
||||
string.IsNullOrWhiteSpace(versionId))
|
||||
{
|
||||
throw new InvalidOperationException("KMS signing keys must include metadata entry 'kms.version'.");
|
||||
throw new InvalidOperationException(_t("crypto.kms.version_metadata_required"));
|
||||
}
|
||||
|
||||
KmsPublicKey? publicKey = null;
|
||||
|
||||
@@ -3,6 +3,7 @@ using StellaOps.Cryptography;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Cryptography.Kms;
|
||||
|
||||
@@ -42,7 +43,7 @@ internal sealed class KmsSigner : ICryptoSigner
|
||||
public JsonWebKey ExportPublicJsonWebKey()
|
||||
{
|
||||
var publicKey = _registration.PublicKey
|
||||
?? throw new InvalidOperationException("KMS signing key is missing public key material.");
|
||||
?? throw new InvalidOperationException(_t("crypto.kms.missing_public_key"));
|
||||
var jwk = new JsonWebKey
|
||||
{
|
||||
Kid = _registration.Reference.KeyId,
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Cryptography.Kms;
|
||||
|
||||
internal sealed class MissingFido2Authenticator : IFido2Authenticator
|
||||
{
|
||||
public Task<byte[]> SignAsync(string credentialId, ReadOnlyMemory<byte> digest, CancellationToken cancellationToken = default)
|
||||
=> throw new InvalidOperationException("IFido2Authenticator must be registered to use FIDO2 KMS.");
|
||||
=> throw new InvalidOperationException(_t("crypto.fido2.authenticator_required"));
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Formats.Asn1;
|
||||
using System.Linq;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Cryptography.Kms;
|
||||
|
||||
@@ -90,7 +91,7 @@ internal sealed partial class Pkcs11InteropFacade
|
||||
"1.2.840.10045.3.1.7" => JsonWebKeyECTypes.P256,
|
||||
"1.3.132.0.34" => JsonWebKeyECTypes.P384,
|
||||
"1.3.132.0.35" => JsonWebKeyECTypes.P521,
|
||||
_ => throw new InvalidOperationException($"Unsupported EC curve OID '{oid}'."),
|
||||
_ => throw new InvalidOperationException(_t("crypto.pkcs11.curve_oid_unsupported", oid)),
|
||||
};
|
||||
|
||||
var coordinateSize = curve switch
|
||||
@@ -98,7 +99,7 @@ internal sealed partial class Pkcs11InteropFacade
|
||||
JsonWebKeyECTypes.P256 => 32,
|
||||
JsonWebKeyECTypes.P384 => 48,
|
||||
JsonWebKeyECTypes.P521 => 66,
|
||||
_ => throw new InvalidOperationException($"Unsupported EC curve '{curve}'."),
|
||||
_ => throw new InvalidOperationException(_t("crypto.pkcs11.curve_unsupported", curve)),
|
||||
};
|
||||
|
||||
return (curve, coordinateSize);
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace StellaOps.Cryptography.Kms;
|
||||
|
||||
internal sealed partial class Pkcs11InteropFacade
|
||||
{
|
||||
#pragma warning disable CS1998
|
||||
private async Task<SessionContext> OpenSessionAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
@@ -6,6 +6,7 @@ using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Cryptography.Kms;
|
||||
|
||||
@@ -30,7 +31,7 @@ internal sealed partial class Pkcs11InteropFacade : IPkcs11Facade
|
||||
_factories = new Pkcs11InteropFactories();
|
||||
_library = _factories.Pkcs11LibraryFactory.LoadPkcs11Library(_factories, _options.LibraryPath, AppType.MultiThreaded);
|
||||
_slot = ResolveSlot(_library, _options)
|
||||
?? throw new InvalidOperationException("Could not resolve PKCS#11 slot.");
|
||||
?? throw new InvalidOperationException(_t("crypto.pkcs11.slot_not_found"));
|
||||
}
|
||||
|
||||
public Pkcs11InteropFacade(IOptions<Pkcs11Options> options, TimeProvider timeProvider)
|
||||
@@ -45,7 +46,7 @@ internal sealed partial class Pkcs11InteropFacade : IPkcs11Facade
|
||||
var privateHandle = FindKey(session, CKO.CKO_PRIVATE_KEY, _options.PrivateKeyLabel);
|
||||
if (privateHandle is null)
|
||||
{
|
||||
throw new InvalidOperationException("PKCS#11 private key not found.");
|
||||
throw new InvalidOperationException(_t("crypto.pkcs11.private_key_not_found"));
|
||||
}
|
||||
|
||||
var labelAttr = GetAttribute(session, privateHandle, CKA.CKA_LABEL);
|
||||
@@ -64,20 +65,20 @@ internal sealed partial class Pkcs11InteropFacade : IPkcs11Facade
|
||||
var publicHandle = FindKey(session, CKO.CKO_PUBLIC_KEY, _options.PublicKeyLabel ?? _options.PrivateKeyLabel);
|
||||
if (publicHandle is null)
|
||||
{
|
||||
throw new InvalidOperationException("PKCS#11 public key not found.");
|
||||
throw new InvalidOperationException(_t("crypto.pkcs11.public_key_not_found"));
|
||||
}
|
||||
|
||||
var pointAttr = GetAttribute(session, publicHandle, CKA.CKA_EC_POINT)
|
||||
?? throw new InvalidOperationException("Public key missing EC point.");
|
||||
?? throw new InvalidOperationException(_t("crypto.pkcs11.missing_ec_point"));
|
||||
var paramsAttr = GetAttribute(session, publicHandle, CKA.CKA_EC_PARAMS)
|
||||
?? throw new InvalidOperationException("Public key missing EC parameters.");
|
||||
?? throw new InvalidOperationException(_t("crypto.pkcs11.missing_ec_params"));
|
||||
|
||||
var ecPoint = ExtractEcPoint(pointAttr.GetValueAsByteArray());
|
||||
var (curve, coordinateSize) = DecodeCurve(paramsAttr.GetValueAsByteArray());
|
||||
|
||||
if (ecPoint.Length != 1 + (coordinateSize * 2) || ecPoint[0] != 0x04)
|
||||
{
|
||||
throw new InvalidOperationException("Unsupported EC point format.");
|
||||
throw new InvalidOperationException(_t("crypto.pkcs11.unsupported_point_format"));
|
||||
}
|
||||
|
||||
var qx = ecPoint.AsSpan(1, coordinateSize).ToArray();
|
||||
@@ -98,7 +99,7 @@ internal sealed partial class Pkcs11InteropFacade : IPkcs11Facade
|
||||
using var context = await OpenSessionAsync(cancellationToken).ConfigureAwait(false);
|
||||
var session = context.Session;
|
||||
var privateHandle = FindKey(session, CKO.CKO_PRIVATE_KEY, _options.PrivateKeyLabel)
|
||||
?? throw new InvalidOperationException("PKCS#11 private key not found.");
|
||||
?? throw new InvalidOperationException(_t("crypto.pkcs11.private_key_not_found"));
|
||||
|
||||
var mechanism = _factories.MechanismFactory.Create(_options.MechanismId);
|
||||
return session.Sign(mechanism, privateHandle, digest.ToArray());
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Cryptography.Kms;
|
||||
|
||||
@@ -11,7 +12,7 @@ public sealed partial class Pkcs11KmsClient
|
||||
var digest = new byte[32];
|
||||
if (!SHA256.TryHashData(data.Span, digest, out _))
|
||||
{
|
||||
throw new InvalidOperationException("Failed to hash payload with SHA-256.");
|
||||
throw new InvalidOperationException(_t("crypto.kms.hash_failed"));
|
||||
}
|
||||
|
||||
return digest;
|
||||
@@ -23,7 +24,7 @@ public sealed partial class Pkcs11KmsClient
|
||||
JsonWebKeyECTypes.P256 => ECCurve.NamedCurves.nistP256,
|
||||
JsonWebKeyECTypes.P384 => ECCurve.NamedCurves.nistP384,
|
||||
JsonWebKeyECTypes.P521 => ECCurve.NamedCurves.nistP521,
|
||||
_ => throw new InvalidOperationException($"Unsupported EC curve '{curve}'."),
|
||||
_ => throw new InvalidOperationException(_t("crypto.pkcs11.curve_unsupported", curve)),
|
||||
};
|
||||
|
||||
private void ThrowIfDisposed()
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.Cryptography.Kms;
|
||||
|
||||
public sealed partial class Pkcs11KmsClient
|
||||
{
|
||||
public Task<KmsKeyMetadata> RotateAsync(string keyId, CancellationToken cancellationToken = default)
|
||||
=> throw new NotSupportedException("PKCS#11 rotation requires HSM administrative tooling.");
|
||||
=> throw new NotSupportedException(_t("crypto.pkcs11.rotation_hsm"));
|
||||
|
||||
public Task RevokeAsync(string keyId, CancellationToken cancellationToken = default)
|
||||
=> throw new NotSupportedException("PKCS#11 revocation must be handled by HSM policies.");
|
||||
=> throw new NotSupportedException(_t("crypto.pkcs11.revocation_hsm"));
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
@@ -15,5 +15,6 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../StellaOps.Cryptography/StellaOps.Cryptography.csproj" />
|
||||
<ProjectReference Include="../StellaOps.Localization/StellaOps.Localization.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
Reference in New Issue
Block a user