Crypto Provider Registry Contract

Contract ID: CONTRACT-CRYPTO-PROVIDER-REGISTRY-010 Version: 1.1 Status: Published Last Updated: 2026-05-30

Overview

This contract defines the ICryptoProviderRegistry interface for managing cryptographic providers across StellaOps modules. It supports pluggable crypto implementations including the in-process .NET default provider, FIPS 140-2/140-3, GOST (Russian), Chinese SM, and EU eIDAS algorithm families.

Audience: Module engineers resolving a hasher or signer through the registry, and crypto-plugin authors implementing a regional algorithm pack. Several sections call out members and endpoints that earlier revisions documented but that do not exist in src/ — treat those >-quoted notes as authoritative corrections.

StellaOps cryptography is organised into two cooperating layers, and this contract covers both:

  1. Registry / resolution layer (src/__Libraries/StellaOps.Cryptography/) — the ICryptoProvider / ICryptoProviderRegistry types that other modules consume to resolve a hasher or signer for a given capability and algorithm using deterministic provider ordering.
  2. Plugin layer (src/Cryptography/StellaOps.Cryptography.Plugin.*) — regional algorithm packs (FIPS, GOST, SM, eIDAS, HSM, OpenPGP) implemented on CryptoPluginBase / ICryptoCapability, loaded dynamically by the plugin loader.

The two layers use different abstractions (the registry layer is capability-oriented; the plugin layer is operation-oriented). Do not assume a single uniform interface across both.

Implementation References

Note: Earlier revisions of this contract referenced src/Security/StellaOps.Security.Crypto/ and src/Security/StellaOps.Security.Crypto.Providers/. Those paths do not exist — the SPRINT_20260430_017 consolidation moved all canonical crypto types into src/__Libraries/StellaOps.Cryptography/.

Interface Definition (Registry / Resolution Layer)

ICryptoProviderRegistry

Source: src/__Libraries/StellaOps.Cryptography/CryptoProvider.cs

public interface ICryptoProviderRegistry
{
    /// <summary>All registered providers (deterministic order).</summary>
    IReadOnlyCollection<ICryptoProvider> Providers { get; }

    /// <summary>Resolves a provider by name (case-insensitive).</summary>
    bool TryResolve(string preferredProvider, out ICryptoProvider provider);

    /// <summary>
    /// Resolves the first provider that supports the capability/algorithm
    /// (honours preferred-provider ordering); throws if none match.
    /// </summary>
    ICryptoProvider ResolveOrThrow(CryptoCapability capability, string algorithmId);

    /// <summary>Resolves a signer for the algorithm and key reference.</summary>
    CryptoSignerResolution ResolveSigner(
        CryptoCapability capability,
        string algorithmId,
        CryptoKeyReference keyReference,
        string? preferredProvider = null);

    /// <summary>Resolves a content hasher for the algorithm.</summary>
    CryptoHasherResolution ResolveHasher(string algorithmId, string? preferredProvider = null);
}

public sealed record CryptoSignerResolution(ICryptoSigner Signer, string ProviderName);
public sealed record CryptoHasherResolution(ICryptoHasher Hasher, string ProviderName);

There are no RegisterProvider, GetProvider, GetDefaultProvider, ListProviders, or HasProvider members. Provider sets are supplied via DI (constructor injection of IEnumerable<ICryptoProvider>); preferred ordering is configured through CryptoProviderRegistryOptions. There is no mutable “default provider” concept — selection is by capability/algorithm support plus the configured preferred order.

ICryptoProvider

Source: src/__Libraries/StellaOps.Cryptography/CryptoProvider.cs

public interface ICryptoProvider
{
    /// <summary>Provider name, e.g. "default", "libsodium".</summary>
    string Name { get; }

    /// <summary>Returns true if this provider can perform the capability with the algorithm.</summary>
    bool Supports(CryptoCapability capability, string algorithmId);

    IPasswordHasher GetPasswordHasher(string algorithmId);

    /// <summary>Content hasher for the supplied algorithm (e.g., SHA-256).</summary>
    ICryptoHasher GetHasher(string algorithmId);

    /// <summary>Signer for the algorithm and registered key reference.</summary>
    ICryptoSigner GetSigner(string algorithmId, CryptoKeyReference keyReference);

    /// <summary>Verification-only signer built from raw public key bytes (SPKI/DER). Optional; default throws.</summary>
    ICryptoSigner CreateEphemeralVerifier(string algorithmId, ReadOnlySpan<byte> publicKeyBytes);

    void UpsertSigningKey(CryptoSigningKey signingKey);
    bool RemoveSigningKey(string keyId);
    IReadOnlyCollection<CryptoSigningKey> GetSigningKeys();
}

This is not the ProviderId / DisplayName / SupportedAlgorithms / CreateHashAlgorithm / CreateSignatureAlgorithm / CreateKdf surface that older revisions documented. Those members do not exist on ICryptoProvider. The provider does not return BCL HashAlgorithm / AsymmetricAlgorithm / KeyDerivationPrf instances; it returns the StellaOps abstractions ICryptoHasher and ICryptoSigner.

CryptoCapability

Source: src/__Libraries/StellaOps.Cryptography/CryptoProvider.cs

public enum CryptoCapability
{
    PasswordHashing,
    Signing,
    Verification,
    SymmetricEncryption,
    KeyDerivation,
    ContentHashing
}

CryptoKeyReference

public sealed record CryptoKeyReference(string KeyId, string? ProviderHint = null);

ICryptoSigner

Source: src/__Libraries/StellaOps.Cryptography/ICryptoSigner.cs

public interface ICryptoSigner
{
    string KeyId { get; }
    string AlgorithmId { get; }                          // e.g., ES256
    ValueTask<byte[]> SignAsync(ReadOnlyMemory<byte> data, CancellationToken ct = default);
    ValueTask<bool> VerifyAsync(ReadOnlyMemory<byte> data, ReadOnlyMemory<byte> signature, CancellationToken ct = default);
    JsonWebKey ExportPublicJsonWebKey();                 // public components only
}

ICryptoHasher

Source: src/__Libraries/StellaOps.Cryptography/CryptoProvider.cs

public interface ICryptoHasher
{
    string AlgorithmId { get; }                          // e.g., SHA-256, GOST-R-34.11-2012-256
    byte[] ComputeHash(ReadOnlySpan<byte> data);
    string ComputeHashHex(ReadOnlySpan<byte> data);      // lowercase hex
}

Interface Definition (Plugin Layer)

Regional algorithm packs are implemented on CryptoPluginBase, which implements both IPlugin and ICryptoCapability. This is an operation-oriented contract (not the capability/algorithm resolution model of the registry layer).

Source: src/Plugin/StellaOps.Plugin.Abstractions/Capabilities/ICryptoCapability.cs, src/Cryptography/StellaOps.Cryptography.Plugin/CryptoPluginBase.cs

public interface ICryptoCapability
{
    IReadOnlyList<string> SupportedAlgorithms { get; }
    bool CanHandle(CryptoOperation operation, string algorithm);
    Task<byte[]> SignAsync(ReadOnlyMemory<byte> data, CryptoSignOptions options, CancellationToken ct);
    Task<bool>   VerifyAsync(ReadOnlyMemory<byte> data, ReadOnlyMemory<byte> signature, CryptoVerifyOptions options, CancellationToken ct);
    Task<byte[]> EncryptAsync(ReadOnlyMemory<byte> data, CryptoEncryptOptions options, CancellationToken ct);
    Task<byte[]> DecryptAsync(ReadOnlyMemory<byte> data, CryptoDecryptOptions options, CancellationToken ct);
    Task<byte[]> HashAsync(ReadOnlyMemory<byte> data, string algorithm, CancellationToken ct);
}

public enum CryptoOperation { Sign, Verify, Encrypt, Decrypt, Hash }

Each plugin advertises a PluginInfo record (CryptoPluginBase.Info) with Id, Name, Version, Vendor, Description, LicenseId. There is no provider_id / display_name / compliance / is_default JSON DTO emitted by the runtime — plugin metadata is carried by PluginInfo and the on-disk plugin.yaml manifest.

Plugin metadata example (plugin.yaml)

Source: src/Cryptography/StellaOps.Cryptography.Plugin.Fips/plugin.yaml

plugin:
  id: com.stellaops.crypto.fips
  name: FIPS 140-2 Cryptography Provider
  version: 1.0.0
  vendor: Stella Ops
  description: US FIPS 140-2 compliant cryptographic algorithms
  license: BUSL-1.1
entryPoint: StellaOps.Cryptography.Plugin.Fips.FipsPlugin
minPlatformVersion: 1.0.0
capabilities:
  - type: crypto
    id: fips
    algorithms: [ RSA-SHA256, ECDSA-P256-SHA256, AES-256-GCM, SHA-256, HMAC-SHA256, ... ]

Available Providers

Default Provider (registry layer)

Source: src/__Libraries/StellaOps.Cryptography/DefaultCryptoProvider.cs

Provider Namedefault
SigningES256 only
Content hashingSHA-256, SHA-384, SHA-512
Password hashingArgon2id, PBKDF2 (Pbkdf2-SHA256)
ComplianceNone (platform default)

The default provider does not advertise general RSA/ECDSA/EdDSA signing. EdDSA (Ed25519) is provided by the separate LibsodiumCryptoProvider (Name = "libsodium") and the BouncyCastle Ed25519 provider; broader RSA/ECDSA families are provided by the FIPS plugin (see below).

FIPS Provider (plugin layer)

Source: src/Cryptography/StellaOps.Cryptography.Plugin.Fips/FipsPlugin.cs

Plugin Idcom.stellaops.crypto.fips
Plugin NameFIPS 140-2 Cryptography Provider
AlgorithmsRSA-SHA256/384/512, RSA-PSS-SHA256/384/512, ECDSA-P256-SHA256, ECDSA-P384-SHA384, ECDSA-P521-SHA512, AES-128/256-CBC, AES-128/256-GCM, SHA-256/384/512, HMAC-SHA256/384/512
ComplianceFIPS 140-2 / 140-3 (compliance profile fips)
NotesRequireFipsMode verifies OS-level FIPS via IFipsModeProbe (Windows registry / Linux /proc/sys/crypto/fips_enabled).

GOST Provider (plugin layer)

Source: src/Cryptography/StellaOps.Cryptography.Plugin.Gost/GostPlugin.cs

Plugin Idcom.stellaops.crypto.gost
Plugin NameGOST Cryptography Provider
AlgorithmsGOST-R34.10-2012-256 (sign/verify), GOST-R34.11-2012-256, GOST-R34.11-2012-512 (hash), GOST-28147-89, GOST-GCM (symmetric)
ComplianceGOST (compliance profile gost)
NotesBouncyCastle software harness. Generated keys are limited to GOST-R34.10-2012-256; production GOST signing requires a CryptoPro/OpenSSL-GOST/PKCS#11 adapter. GOST-GCM is internally substituted with GOST-28147-89 CBC (no certified software AEAD).

SM Provider (plugin layer, China)

Source: src/Cryptography/StellaOps.Cryptography.Plugin.Sm/SmPlugin.cs

Plugin Idcom.stellaops.crypto.sm
Plugin NameChinese SM Cryptography Provider
AlgorithmsSM2-SM3, SM2-SHA256 (sign/verify), SM3 (hash), SM4-CBC, SM4-ECB, SM4-GCM (symmetric)
ComplianceGM/T 0003-0004 (compliance profile sm)

eIDAS Provider (plugin layer, EU)

Source: src/Cryptography/StellaOps.Cryptography.Plugin.Eidas/EidasPlugin.cs

Plugin Idcom.stellaops.crypto.eidas
Plugin NameeIDAS Cryptography Provider
AlgorithmseIDAS-RSA-SHA256/384/512, eIDAS-ECDSA-SHA256/384, eIDAS-CAdES-BES/T/LT/LTA, eIDAS-XAdES-BES
ComplianceEU eIDAS / ETSI TS 119 312 (compliance profile eidas)

Additional plugin packs exist in the same directory: StellaOps.Cryptography.Plugin.Hsm and StellaOps.Cryptography.Plugin.OpenPgp. A StellaOps.Cryptography.Plugin.CryptoPro reference is gated behind the StellaOpsEnableCryptoPro MSBuild property but the project is not present in this repository tree (proprietary CryptoPro CSP pack).

Configuration

Registration at Startup

Source: src/__Libraries/StellaOps.Cryptography.DependencyInjection/CryptoPluginServiceCollectionExtensions.cs, CryptoServiceCollectionExtensions.PluginConfiguration.cs, CryptoServiceCollectionExtensions.Registry.cs

The recommended production registration is configuration-driven plugin loading:

// Bind StellaOps:Crypto:Plugins and StellaOps:Crypto:Compliance from IConfiguration,
// and register ICryptoProviderRegistry (CryptoPluginProviderRegistry).
services.AddStellaOpsCryptoWithPlugins(configuration, options =>
{
    // optional CryptoPluginConfiguration tweaks
});

// Or, with an explicit compliance profile:
services.AddStellaOpsCryptoFromConfiguration(
    configuration,
    complianceProfileId: "fips",   // "world" | "fips" | "gost" | "sm" | "kcmvp" | "eidas"
    strictValidation: true);

When no plugin loader is wired, the base registration registers ServiceRegisteredCryptoProviderRegistry as the ICryptoProviderRegistry singleton over the DI-registered ICryptoProvider instances.

There is no AddCryptoProviderRegistry(options => …) API and no options.RegisterProvider<T>("id") / options.DefaultProvider = … builder. Providers come from DI; preferred ordering is set via CryptoProviderRegistryOptions (PreferredProviders, ActiveProfile, per-profile Profiles).

Plugin loader configuration

Source: src/__Libraries/StellaOps.Cryptography.PluginLoader/CryptoPluginConfiguration.cs — bound from configuration section StellaOps:Crypto:Plugins.

KeyDefaultDescription
ManifestPath/etc/stellaops/crypto-plugins-manifest.jsonPlugin manifest path
DiscoveryModeexplicitexplicit (only configured plugins) or auto
Enabled[][]Enabled plugins (Id, optional Priority, Options)
Disabled[][]Plugin IDs/patterns to disable
FailOnMissingPlugintrueFail startup if a configured plugin is missing
RequireAtLeastOnetrueRequire at least one provider to load
ComplianceCryptoComplianceConfiguration (ProfileId, StrictValidation, EnforceJurisdiction, AllowedJurisdictions[])

Provider Selection

var registry = serviceProvider.GetRequiredService<ICryptoProviderRegistry>();

// All registered providers
var providers = registry.Providers;

// Resolve a provider by name (case-insensitive)
if (registry.TryResolve("default", out var provider)) { /* … */ }

// Resolve by capability + algorithm (honours preferred order)
var hashProvider = registry.ResolveOrThrow(CryptoCapability.ContentHashing, "SHA-256");

API Endpoints

NOT IMPLEMENTED (forward design only). No HTTP API for the crypto provider registry exists in the codebase. There are no GET /api/v1/crypto/providers, GET /api/v1/crypto/providers/{id}, or PUT /api/v1/crypto/providers/default routes (no controller or minimal-API mapping matches crypto/providers), and there is no associated Authority scope. The registry is consumed in-process via dependency injection only; provider/profile selection is configuration-driven (see Configuration). The sketch below is retained as a proposed future surface and must not be treated as an available API.

# PROPOSED (not implemented)
GET /api/v1/crypto/providers
GET /api/v1/crypto/providers/{provider_id}
PUT /api/v1/crypto/providers/default

Algorithm Mapping

Algorithm identifiers below are the actual tokens advertised by each provider/plugin (verified against SupportedAlgorithms / plugin.yaml). They are not normalised across families — callers pass the exact token.

Hash Algorithms

Algorithm tokendefaultFIPS pluginGOST pluginSM plugin
SHA-256 / SHA-384 / SHA-512YesYesNoNo
GOST-R34.11-2012-256NoNoYesNo
GOST-R34.11-2012-512NoNoYesNo
SM3NoNoNoYes

Signature Algorithms

Algorithm tokendefaultFIPS pluginGOST pluginSM plugin
ES256YesNo (uses ECDSA-P256-SHA256)NoNo
RSA-PSS-SHA256/384/512NoYesNoNo
ECDSA-P256-SHA256 / ECDSA-P384-SHA384 / ECDSA-P521-SHA512NoYesNoNo
RSA-SHA256/384/512NoYesNoNo
GOST-R34.10-2012-256NoNoYesNo
SM2-SM3 / SM2-SHA256NoNoNoYes

EdDSA (Ed25519) is not handled by the providers above; it is served by the libsodium/BouncyCastle Ed25519 registry-layer providers.

Usage Example

Resolving a hasher (registry layer)

var registry = services.GetRequiredService<ICryptoProviderRegistry>();
var resolution = registry.ResolveHasher("SHA-256");
var digestHex = resolution.Hasher.ComputeHashHex(data);   // resolution.ProviderName = "default"

Hashing with SM3 (plugin layer)

// SmPlugin implements ICryptoCapability; resolve the plugin and call HashAsync.
var digest = await smPlugin.HashAsync(data, "SM3", ct);

Signing with GOST (plugin layer)

// GostPlugin signs with GOST-R34.10-2012-256 (GOST R 34.11-2012 digest).
var signature = await gostPlugin.SignAsync(
    data,
    new CryptoSignOptions(Algorithm: "GOST-R34.10-2012-256", KeyId: keyId),
    ct);

Metrics

Source: src/__Libraries/StellaOps.Cryptography/CryptoProviderMetrics.cs

Meterstellaops.crypto (version 1.0.0)
crypto_provider_resolutions_totalCounter; successful provider resolutions. Tags: provider, capability, algorithm.
crypto_provider_resolution_failures_totalCounter; failed resolutions. Tags: capability, algorithm.

Environment Variables

Source: src/__Libraries/StellaOps.Cryptography/CryptoComplianceOptions.cs (ApplyEnvironmentOverrides)

VariableDescription
STELLAOPS_CRYPTO_COMPLIANCE_PROFILEOverrides the active compliance profile ID (world, fips, gost, sm, kcmvp, eidas).
STELLAOPS_CRYPTO_STRICT_VALIDATIONtrue/false — overrides strict compliance validation.

The following variables documented in earlier revisions are not runtime environment variables and do not exist as such in the codebase: STELLAOPS_CRYPTO_DEFAULT_PROVIDER, StellaOpsEnableSmCrypto, STELLAOPS_FIPS_MODE. StellaOpsEnableCryptoPro is an MSBuild build-time property (a <ProjectReference> condition in StellaOps.Cryptography.DependencyInjection.csproj), not a runtime toggle. FIPS-mode enforcement is driven by the FIPS plugin RequireFipsMode option probing the OS, not by an environment variable.

Compliance Profiles

Source: src/__Libraries/StellaOps.Cryptography/ComplianceProfiles.cs, CryptoComplianceOptions.cs

Valid profile IDs: world (default), fips, gost, sm, kcmvp, eidas. Each profile maps hash/HMAC purposes (Graph, Symbol, Content, Merkle, Attestation, Interop, Secret) to concrete algorithms (e.g. world uses BLAKE3 for graph content-addressing and SHA-256 elsewhere; fips uses SHA-256 throughout and PBKDF2 for secrets).