Files
git.stella-ops.org/src/__Libraries/StellaOps.Cryptography.Plugin.BouncyCastle/BouncyCastleEd25519CryptoProvider.cs

112 lines
3.9 KiB
C#

using Org.BouncyCastle.Crypto.Parameters;
using StellaOps.Cryptography;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using static StellaOps.Localization.T;
namespace StellaOps.Cryptography.Plugin.BouncyCastle;
/// <summary>
/// Ed25519 signing provider backed by BouncyCastle primitives.
/// </summary>
public sealed partial class BouncyCastleEd25519CryptoProvider : ICryptoProvider
{
private static readonly HashSet<string> _supportedAlgorithms = new(StringComparer.OrdinalIgnoreCase)
{
SignatureAlgorithms.Ed25519,
SignatureAlgorithms.EdDsa
};
private static readonly string[] _defaultKeyOps = { "sign", "verify" };
private readonly ConcurrentDictionary<string, KeyEntry> _signingKeys = new(StringComparer.Ordinal);
public string Name => "bouncycastle.ed25519";
public bool Supports(CryptoCapability capability, string algorithmId)
{
if (string.IsNullOrWhiteSpace(algorithmId))
{
return false;
}
return capability switch
{
CryptoCapability.Signing or CryptoCapability.Verification => _supportedAlgorithms.Contains(algorithmId),
_ => false
};
}
public ICryptoHasher GetHasher(string algorithmId)
=> throw new NotSupportedException(_t("crypto.ed25519.no_hashing"));
public IPasswordHasher GetPasswordHasher(string algorithmId)
=> throw new NotSupportedException(_t("crypto.provider.no_password_hashing", Name));
public ICryptoSigner GetSigner(string algorithmId, CryptoKeyReference keyReference)
{
ArgumentException.ThrowIfNullOrWhiteSpace(algorithmId);
ArgumentNullException.ThrowIfNull(keyReference);
if (!_signingKeys.TryGetValue(keyReference.KeyId, out var entry))
{
throw new KeyNotFoundException(_t("crypto.provider.key_not_registered", keyReference.KeyId, Name));
}
EnsureAlgorithmSupported(algorithmId);
var normalized = NormalizeAlgorithm(algorithmId);
if (!string.Equals(entry.Descriptor.AlgorithmId, normalized, StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException(
_t("crypto.provider.key_algorithm_mismatch", keyReference.KeyId, entry.Descriptor.AlgorithmId, algorithmId));
}
return new Ed25519SignerWrapper(entry);
}
public void UpsertSigningKey(CryptoSigningKey signingKey)
{
ArgumentNullException.ThrowIfNull(signingKey);
EnsureAlgorithmSupported(signingKey.AlgorithmId);
if (signingKey.Kind != CryptoSigningKeyKind.Raw)
{
throw new InvalidOperationException(_t("crypto.ed25519.raw_key_required", Name));
}
var privateKey = NormalizePrivateKey(signingKey.PrivateKey);
var publicKey = NormalizePublicKey(signingKey.PublicKey, privateKey);
var privateKeyParameters = new Ed25519PrivateKeyParameters(privateKey, 0);
var publicKeyParameters = new Ed25519PublicKeyParameters(publicKey, 0);
var descriptor = new CryptoSigningKey(
signingKey.Reference,
NormalizeAlgorithm(signingKey.AlgorithmId),
privateKey,
signingKey.CreatedAt,
signingKey.ExpiresAt,
publicKey,
signingKey.Metadata);
_signingKeys.AddOrUpdate(
signingKey.Reference.KeyId,
_ => new KeyEntry(descriptor, privateKeyParameters, publicKeyParameters),
(_, _) => new KeyEntry(descriptor, privateKeyParameters, publicKeyParameters));
}
public bool RemoveSigningKey(string keyId)
{
if (string.IsNullOrWhiteSpace(keyId))
{
return false;
}
return _signingKeys.TryRemove(keyId, out _);
}
public IReadOnlyCollection<CryptoSigningKey> GetSigningKeys()
=> _signingKeys.Values.Select(static entry => entry.Descriptor).ToArray();
}