Files
git.stella-ops.org/src/__Libraries/StellaOps.Cryptography.DependencyInjection/CryptoServiceCollectionExtensions.cs
StellaOps Bot f0662dd45f feat: Implement DefaultCryptoHmac for compliance-aware HMAC operations
- Added DefaultCryptoHmac class implementing ICryptoHmac interface.
- Introduced purpose-based HMAC computation methods.
- Implemented verification methods for HMACs with constant-time comparison.
- Created HmacAlgorithms and HmacPurpose classes for well-known identifiers.
- Added compliance profile support for HMAC algorithms.
- Included asynchronous methods for HMAC computation from streams.
2025-12-06 00:41:04 +02:00

201 lines
7.4 KiB
C#

using System;
using System.Collections.Generic;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
using StellaOps.Cryptography;
#if STELLAOPS_CRYPTO_PRO
using StellaOps.Cryptography.Plugin.CryptoPro;
#endif
using StellaOps.Cryptography.Plugin.Pkcs11Gost;
using StellaOps.Cryptography.Plugin.OpenSslGost;
namespace StellaOps.Cryptography.DependencyInjection;
/// <summary>
/// Dependency injection helpers for registering StellaOps cryptography services.
/// </summary>
public static class CryptoServiceCollectionExtensions
{
/// <summary>
/// Registers the default crypto provider and registry.
/// </summary>
/// <param name="services">Service collection.</param>
/// <param name="configureRegistry">Optional registry ordering configuration.</param>
/// <param name="configureProvider">Optional provider-level configuration (e.g. key registration).</param>
/// <returns>The service collection.</returns>
public static IServiceCollection AddStellaOpsCrypto(
this IServiceCollection services,
Action<CryptoProviderRegistryOptions>? configureRegistry = null,
Action<DefaultCryptoProvider>? configureProvider = null)
{
ArgumentNullException.ThrowIfNull(services);
if (configureRegistry is not null)
{
services.Configure(configureRegistry);
}
// Register compliance options with default profile
services.TryAddSingleton<IOptionsMonitor<CryptoComplianceOptions>>(sp =>
{
var configuration = sp.GetService<IConfiguration>();
var options = new CryptoComplianceOptions();
configuration?.GetSection(CryptoComplianceOptions.SectionKey).Bind(options);
options.ApplyEnvironmentOverrides();
return new StaticComplianceOptionsMonitor(options);
});
// Register compliance service
services.TryAddSingleton<ICryptoComplianceService, CryptoComplianceService>();
services.TryAddSingleton<DefaultCryptoProvider>(sp =>
{
var provider = new DefaultCryptoProvider();
configureProvider?.Invoke(provider);
return provider;
});
services.TryAddEnumerable(ServiceDescriptor.Singleton<ICryptoProvider, DefaultCryptoProvider>());
#if STELLAOPS_CRYPTO_SODIUM
services.TryAddSingleton<LibsodiumCryptoProvider>();
services.TryAddEnumerable(ServiceDescriptor.Singleton<ICryptoProvider, LibsodiumCryptoProvider>());
#endif
services.TryAddSingleton<ICryptoHash, DefaultCryptoHash>();
services.TryAddSingleton<ICryptoHmac, DefaultCryptoHmac>();
services.TryAddSingleton<ICryptoProviderRegistry>(sp =>
{
var providers = sp.GetServices<ICryptoProvider>();
var options = sp.GetService<IOptionsMonitor<CryptoProviderRegistryOptions>>();
IEnumerable<string>? preferred = options?.CurrentValue?.ResolvePreferredProviders();
return new CryptoProviderRegistry(providers, preferred);
});
return services;
}
/// <summary>
/// Registers crypto services with compliance profile configuration.
/// </summary>
/// <param name="services">Service collection.</param>
/// <param name="configuration">Configuration root.</param>
/// <param name="configureCompliance">Optional compliance configuration.</param>
/// <returns>The service collection.</returns>
public static IServiceCollection AddStellaOpsCryptoWithCompliance(
this IServiceCollection services,
IConfiguration configuration,
Action<CryptoComplianceOptions>? configureCompliance = null)
{
ArgumentNullException.ThrowIfNull(services);
ArgumentNullException.ThrowIfNull(configuration);
// Bind compliance options from configuration
services.Configure<CryptoComplianceOptions>(options =>
{
configuration.GetSection(CryptoComplianceOptions.SectionKey).Bind(options);
configureCompliance?.Invoke(options);
options.ApplyEnvironmentOverrides();
});
// Register compliance service with options monitor
services.TryAddSingleton<ICryptoComplianceService, CryptoComplianceService>();
// Register base crypto services
services.AddStellaOpsCrypto();
return services;
}
/// <summary>
/// Helper class for static options monitoring.
/// </summary>
private sealed class StaticComplianceOptionsMonitor : IOptionsMonitor<CryptoComplianceOptions>
{
private readonly CryptoComplianceOptions _options;
public StaticComplianceOptionsMonitor(CryptoComplianceOptions options)
=> _options = options;
public CryptoComplianceOptions CurrentValue => _options;
public CryptoComplianceOptions Get(string? name) => _options;
public IDisposable OnChange(Action<CryptoComplianceOptions, string> listener)
=> NullDisposable.Instance;
private sealed class NullDisposable : IDisposable
{
public static readonly NullDisposable Instance = new();
public void Dispose() { }
}
}
public static IServiceCollection AddStellaOpsCryptoRu(
this IServiceCollection services,
IConfiguration configuration,
Action<CryptoProviderRegistryOptions>? configureRegistry = null)
{
ArgumentNullException.ThrowIfNull(services);
ArgumentNullException.ThrowIfNull(configuration);
var baseSection = configuration.GetSection("StellaOps:Crypto");
services.Configure<StellaOpsCryptoOptions>(baseSection);
services.Configure<CryptoProviderRegistryOptions>(baseSection.GetSection("Registry"));
#if STELLAOPS_CRYPTO_PRO
services.Configure<CryptoProGostProviderOptions>(baseSection.GetSection("CryptoPro"));
#endif
services.Configure<Pkcs11GostProviderOptions>(baseSection.GetSection("Pkcs11"));
services.Configure<OpenSslGostProviderOptions>(baseSection.GetSection("OpenSsl"));
services.AddStellaOpsCrypto(configureRegistry);
services.AddOpenSslGostProvider();
services.AddPkcs11GostProvider();
#if STELLAOPS_CRYPTO_PRO
if (OperatingSystem.IsWindows())
{
services.AddCryptoProGostProvider();
}
#endif
services.PostConfigure<CryptoProviderRegistryOptions>(options =>
{
EnsurePreferred(options.PreferredProviders);
foreach (var profile in options.Profiles.Values)
{
EnsurePreferred(profile.PreferredProviders);
}
});
return services;
static void EnsurePreferred(IList<string> providers)
{
InsertIfMissing(providers, "ru.pkcs11");
InsertIfMissing(providers, "ru.openssl.gost");
#if STELLAOPS_CRYPTO_PRO
if (OperatingSystem.IsWindows())
{
InsertIfMissing(providers, "ru.cryptopro.csp");
}
#endif
}
static void InsertIfMissing(IList<string> providers, string name)
{
for (var i = 0; i < providers.Count; i++)
{
if (string.Equals(providers[i], name, StringComparison.OrdinalIgnoreCase))
{
return;
}
}
providers.Insert(0, name);
}
}
}