Resolve Concelier/Excititor merge conflicts
This commit is contained in:
		| @@ -1,129 +1,133 @@ | ||||
| using System; | ||||
| using System.Collections.Concurrent; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Security.Cryptography; | ||||
|  | ||||
| namespace StellaOps.Cryptography; | ||||
|  | ||||
| /// <summary> | ||||
| /// Default in-process crypto provider exposing password hashing capabilities. | ||||
| /// </summary> | ||||
| public sealed class DefaultCryptoProvider : ICryptoProvider | ||||
| { | ||||
|     private readonly ConcurrentDictionary<string, IPasswordHasher> passwordHashers; | ||||
|     private readonly ConcurrentDictionary<string, CryptoSigningKey> signingKeys; | ||||
|     private static readonly HashSet<string> SupportedSigningAlgorithms = new(StringComparer.OrdinalIgnoreCase) | ||||
|     { | ||||
|         SignatureAlgorithms.Es256 | ||||
|     }; | ||||
|  | ||||
|     public DefaultCryptoProvider() | ||||
|     { | ||||
|         passwordHashers = new ConcurrentDictionary<string, IPasswordHasher>(StringComparer.OrdinalIgnoreCase); | ||||
|         signingKeys = new ConcurrentDictionary<string, CryptoSigningKey>(StringComparer.Ordinal); | ||||
|  | ||||
|         var argon = new Argon2idPasswordHasher(); | ||||
|         var pbkdf2 = new Pbkdf2PasswordHasher(); | ||||
|  | ||||
|         passwordHashers.TryAdd(PasswordHashAlgorithm.Argon2id.ToString(), argon); | ||||
|         passwordHashers.TryAdd(PasswordHashAlgorithms.Argon2id, argon); | ||||
|         passwordHashers.TryAdd(PasswordHashAlgorithm.Pbkdf2.ToString(), pbkdf2); | ||||
|         passwordHashers.TryAdd(PasswordHashAlgorithms.Pbkdf2Sha256, pbkdf2); | ||||
|     } | ||||
|  | ||||
|     public string Name => "default"; | ||||
|  | ||||
|     public bool Supports(CryptoCapability capability, string algorithmId) | ||||
|     { | ||||
|         if (string.IsNullOrWhiteSpace(algorithmId)) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         return capability switch | ||||
|         { | ||||
|             CryptoCapability.PasswordHashing => passwordHashers.ContainsKey(algorithmId), | ||||
|             CryptoCapability.Signing or CryptoCapability.Verification => SupportedSigningAlgorithms.Contains(algorithmId), | ||||
|             _ => false | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     public IPasswordHasher GetPasswordHasher(string algorithmId) | ||||
|     { | ||||
|         if (!Supports(CryptoCapability.PasswordHashing, algorithmId)) | ||||
|         { | ||||
|             throw new InvalidOperationException($"Password hashing algorithm '{algorithmId}' is not supported by provider '{Name}'."); | ||||
|         } | ||||
|  | ||||
|         return passwordHashers[algorithmId]; | ||||
|     } | ||||
|  | ||||
|     public ICryptoSigner GetSigner(string algorithmId, CryptoKeyReference keyReference) | ||||
|     { | ||||
|         ArgumentNullException.ThrowIfNull(keyReference); | ||||
|  | ||||
|         if (!Supports(CryptoCapability.Signing, algorithmId)) | ||||
|         { | ||||
|             throw new InvalidOperationException($"Signing algorithm '{algorithmId}' is not supported by provider '{Name}'."); | ||||
|         } | ||||
|  | ||||
|         if (!signingKeys.TryGetValue(keyReference.KeyId, out var signingKey)) | ||||
|         { | ||||
|             throw new KeyNotFoundException($"Signing key '{keyReference.KeyId}' is not registered with provider '{Name}'."); | ||||
|         } | ||||
|  | ||||
|         if (!string.Equals(signingKey.AlgorithmId, algorithmId, StringComparison.OrdinalIgnoreCase)) | ||||
|         { | ||||
|             throw new InvalidOperationException( | ||||
|                 $"Signing key '{keyReference.KeyId}' is registered for algorithm '{signingKey.AlgorithmId}', not '{algorithmId}'."); | ||||
|         } | ||||
|  | ||||
|         return EcdsaSigner.Create(signingKey); | ||||
|     } | ||||
|  | ||||
|     public void UpsertSigningKey(CryptoSigningKey signingKey) | ||||
|     { | ||||
|         ArgumentNullException.ThrowIfNull(signingKey); | ||||
|         EnsureSigningSupported(signingKey.AlgorithmId); | ||||
|         ValidateSigningKey(signingKey); | ||||
|  | ||||
|         signingKeys.AddOrUpdate(signingKey.Reference.KeyId, signingKey, (_, _) => signingKey); | ||||
|     } | ||||
|  | ||||
|     public bool RemoveSigningKey(string keyId) | ||||
|     { | ||||
|         if (string.IsNullOrWhiteSpace(keyId)) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         return signingKeys.TryRemove(keyId, out _); | ||||
|     } | ||||
|  | ||||
|     public IReadOnlyCollection<CryptoSigningKey> GetSigningKeys() | ||||
|         => signingKeys.Values.ToArray(); | ||||
|  | ||||
|     private static void EnsureSigningSupported(string algorithmId) | ||||
|     { | ||||
|         if (!SupportedSigningAlgorithms.Contains(algorithmId)) | ||||
|         { | ||||
|             throw new InvalidOperationException($"Signing algorithm '{algorithmId}' is not supported by provider 'default'."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private static void ValidateSigningKey(CryptoSigningKey signingKey) | ||||
|     { | ||||
|         if (!string.Equals(signingKey.AlgorithmId, SignatureAlgorithms.Es256, StringComparison.OrdinalIgnoreCase)) | ||||
|         { | ||||
|             throw new InvalidOperationException($"Only ES256 signing keys are currently supported by provider 'default'."); | ||||
|         } | ||||
|  | ||||
|         var expected = ECCurve.NamedCurves.nistP256; | ||||
|         var curve = signingKey.PrivateParameters.Curve; | ||||
|         if (!curve.IsNamed || !string.Equals(curve.Oid.Value, expected.Oid.Value, StringComparison.Ordinal)) | ||||
|         { | ||||
|             throw new InvalidOperationException("ES256 signing keys must use the NIST P-256 curve."); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| using System; | ||||
| using System.Collections.Concurrent; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Security.Cryptography; | ||||
|  | ||||
| namespace StellaOps.Cryptography; | ||||
|  | ||||
| /// <summary> | ||||
| /// Default in-process crypto provider exposing password hashing capabilities. | ||||
| /// </summary> | ||||
| public sealed class DefaultCryptoProvider : ICryptoProvider | ||||
| { | ||||
|     private readonly ConcurrentDictionary<string, IPasswordHasher> passwordHashers; | ||||
|     private readonly ConcurrentDictionary<string, CryptoSigningKey> signingKeys; | ||||
|     private static readonly HashSet<string> SupportedSigningAlgorithms = new(StringComparer.OrdinalIgnoreCase) | ||||
|     { | ||||
|         SignatureAlgorithms.Es256 | ||||
|     }; | ||||
|  | ||||
|     public DefaultCryptoProvider() | ||||
|     { | ||||
|         passwordHashers = new ConcurrentDictionary<string, IPasswordHasher>(StringComparer.OrdinalIgnoreCase); | ||||
|         signingKeys = new ConcurrentDictionary<string, CryptoSigningKey>(StringComparer.Ordinal); | ||||
|  | ||||
|         var argon = new Argon2idPasswordHasher(); | ||||
|         var pbkdf2 = new Pbkdf2PasswordHasher(); | ||||
|  | ||||
|         passwordHashers.TryAdd(PasswordHashAlgorithm.Argon2id.ToString(), argon); | ||||
|         passwordHashers.TryAdd(PasswordHashAlgorithms.Argon2id, argon); | ||||
|         passwordHashers.TryAdd(PasswordHashAlgorithm.Pbkdf2.ToString(), pbkdf2); | ||||
|         passwordHashers.TryAdd(PasswordHashAlgorithms.Pbkdf2Sha256, pbkdf2); | ||||
|     } | ||||
|  | ||||
|     public string Name => "default"; | ||||
|  | ||||
|     public bool Supports(CryptoCapability capability, string algorithmId) | ||||
|     { | ||||
|         if (string.IsNullOrWhiteSpace(algorithmId)) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         return capability switch | ||||
|         { | ||||
|             CryptoCapability.PasswordHashing => passwordHashers.ContainsKey(algorithmId), | ||||
|             CryptoCapability.Signing or CryptoCapability.Verification => SupportedSigningAlgorithms.Contains(algorithmId), | ||||
|             _ => false | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     public IPasswordHasher GetPasswordHasher(string algorithmId) | ||||
|     { | ||||
|         if (!Supports(CryptoCapability.PasswordHashing, algorithmId)) | ||||
|         { | ||||
|             throw new InvalidOperationException($"Password hashing algorithm '{algorithmId}' is not supported by provider '{Name}'."); | ||||
|         } | ||||
|  | ||||
|         return passwordHashers[algorithmId]; | ||||
|     } | ||||
|  | ||||
|     public ICryptoSigner GetSigner(string algorithmId, CryptoKeyReference keyReference) | ||||
|     { | ||||
|         ArgumentNullException.ThrowIfNull(keyReference); | ||||
|  | ||||
|         if (!Supports(CryptoCapability.Signing, algorithmId)) | ||||
|         { | ||||
|             throw new InvalidOperationException($"Signing algorithm '{algorithmId}' is not supported by provider '{Name}'."); | ||||
|         } | ||||
|  | ||||
|         if (!signingKeys.TryGetValue(keyReference.KeyId, out var signingKey)) | ||||
|         { | ||||
|             throw new KeyNotFoundException($"Signing key '{keyReference.KeyId}' is not registered with provider '{Name}'."); | ||||
|         } | ||||
|  | ||||
|         if (!string.Equals(signingKey.AlgorithmId, algorithmId, StringComparison.OrdinalIgnoreCase)) | ||||
|         { | ||||
|             throw new InvalidOperationException( | ||||
|                 $"Signing key '{keyReference.KeyId}' is registered for algorithm '{signingKey.AlgorithmId}', not '{algorithmId}'."); | ||||
|         } | ||||
|  | ||||
|         return EcdsaSigner.Create(signingKey); | ||||
|     } | ||||
|  | ||||
|     public void UpsertSigningKey(CryptoSigningKey signingKey) | ||||
|     { | ||||
|         ArgumentNullException.ThrowIfNull(signingKey); | ||||
|         EnsureSigningSupported(signingKey.AlgorithmId); | ||||
|         if (signingKey.Kind != CryptoSigningKeyKind.Ec) | ||||
|         { | ||||
|             throw new InvalidOperationException($"Provider '{Name}' only accepts EC signing keys."); | ||||
|         } | ||||
|         ValidateSigningKey(signingKey); | ||||
|  | ||||
|         signingKeys.AddOrUpdate(signingKey.Reference.KeyId, signingKey, (_, _) => signingKey); | ||||
|     } | ||||
|  | ||||
|     public bool RemoveSigningKey(string keyId) | ||||
|     { | ||||
|         if (string.IsNullOrWhiteSpace(keyId)) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         return signingKeys.TryRemove(keyId, out _); | ||||
|     } | ||||
|  | ||||
|     public IReadOnlyCollection<CryptoSigningKey> GetSigningKeys() | ||||
|         => signingKeys.Values.ToArray(); | ||||
|  | ||||
|     private static void EnsureSigningSupported(string algorithmId) | ||||
|     { | ||||
|         if (!SupportedSigningAlgorithms.Contains(algorithmId)) | ||||
|         { | ||||
|             throw new InvalidOperationException($"Signing algorithm '{algorithmId}' is not supported by provider 'default'."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private static void ValidateSigningKey(CryptoSigningKey signingKey) | ||||
|     { | ||||
|         if (!string.Equals(signingKey.AlgorithmId, SignatureAlgorithms.Es256, StringComparison.OrdinalIgnoreCase)) | ||||
|         { | ||||
|             throw new InvalidOperationException($"Only ES256 signing keys are currently supported by provider 'default'."); | ||||
|         } | ||||
|  | ||||
|         var expected = ECCurve.NamedCurves.nistP256; | ||||
|         var curve = signingKey.PrivateParameters.Curve; | ||||
|         if (!curve.IsNamed || !string.Equals(curve.Oid.Value, expected.Oid.Value, StringComparison.Ordinal)) | ||||
|         { | ||||
|             throw new InvalidOperationException("ES256 signing keys must use the NIST P-256 curve."); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user