# StellaOps Configuration-Driven Crypto Architecture ## Zero Hardcoded Crypto - Full Runtime Plugin Selection **Date:** 2025-12-23 **Status:** 📋 PLANNING **Priority:** CRITICAL - Foundational Architecture --- ## Executive Summary **GOAL:** Eliminate ALL hardcoded crypto implementations. Make crypto provider selection 100% configuration-driven at runtime. **PRINCIPLES:** 1. **Zero Direct Crypto in Code** - All modules use ICryptoProvider abstraction 2. **Build Once, Deploy Everywhere** - CI builds ALL plugins unconditionally 3. **Configuration-Driven Selection** - appsettings.yaml/env vars select active plugins 4. **Regional Docker Bundles** - Separate docker-compose files + crypto profile images per jurisdiction 5. **Fail-Safe Compliance** - Strict mode prevents accidental use of non-compliant providers --- ## Current State vs. Target State ### Current State (❌ Problems) ```csharp // Problem 1: Direct crypto in AirGap module using System.Security.Cryptography; var hash = SHA256.HashData(data); // ❌ Hardcoded crypto var ecdsa = ECDsa.Create(); // ❌ Hardcoded crypto ``` ```csharp // Problem 2: Hardcoded provider registration public static IServiceCollection AddStellaOpsCrypto(...) { services.AddDefaultCryptoProvider(); // ❌ Always included services.AddBouncyCastleEd25519Provider(); // ❌ Always included services.AddOpenSslGostProvider(); // ❌ Always included // ...12 more providers always registered } ``` ```yaml # Problem 3: No regional Docker bundles # Single docker-compose.yml for all deployments ``` ### Target State (✅ Solution) ```csharp // Solution 1: NO direct crypto - everything through abstraction public class AirGapVerificationService { private readonly ICryptoHasher _hasher; // ✅ Injected private readonly ICryptoSigner _signer; // ✅ Injected public async Task VerifyAsync(byte[] data) { var hash = await _hasher.HashAsync(data); // ✅ Plugin-based return await _signer.VerifyAsync(...); // ✅ Plugin-based } } ``` ```csharp // Solution 2: Configuration-driven plugin loading public static IServiceCollection AddStellaOpsCrypto( IServiceProvider services, IConfiguration configuration) { var config = configuration.GetSection("Crypto:Plugins").Get(); // Load ONLY configured plugins foreach (var pluginConfig in config.EnabledPlugins) { RegisterPlugin(services, pluginConfig.Name, pluginConfig.Options); } // Fail if no plugins configured if (config.EnabledPlugins.Count == 0) throw new InvalidOperationException("No crypto plugins configured"); } ``` ```yaml # Solution 3: Regional Docker bundles docker-compose.russia.yml # GOST crypto profile docker-compose.china.yml # SM crypto profile docker-compose.eu.yml # eIDAS crypto profile docker-compose.international.yml # Default crypto profile ``` --- ## Architecture Design ### 1. Plugin Discovery & Loading System #### 1.1 Plugin Manifest Schema **File:** `etc/crypto-plugins-manifest.json` ```json { "version": "1.0", "plugins": [ { "id": "default", "name": "DefaultCryptoProvider", "assembly": "StellaOps.Cryptography.Providers.Default.dll", "type": "StellaOps.Cryptography.Providers.Default.DefaultCryptoProvider", "capabilities": ["signing:ES256", "signing:ES384", "hashing:SHA256", "hashing:SHA384", "hashing:SHA512"], "jurisdiction": "international", "compliance": ["FIPS-140-3"], "dependencies": [] }, { "id": "cryptopro.gost", "name": "CryptoProGostCryptoProvider", "assembly": "StellaOps.Cryptography.Providers.Gost.CryptoPro.dll", "type": "StellaOps.Cryptography.Providers.Gost.CryptoPro.CryptoProGostCryptoProvider", "capabilities": ["signing:GOST-R-34.10-2012-256", "signing:GOST-R-34.10-2012-512", "hashing:GOST-R-34.11-2012-256"], "jurisdiction": "russia", "compliance": ["GOST-R"], "dependencies": ["CryptoPro CSP 5.0+"], "platforms": ["windows"], "license": "commercial" }, { "id": "openssl.gost", "name": "OpenSslGostCryptoProvider", "assembly": "StellaOps.Cryptography.Providers.Gost.OpenSsl.dll", "type": "StellaOps.Cryptography.Providers.Gost.OpenSsl.OpenSslGostCryptoProvider", "capabilities": ["signing:GOST-R-34.10-2012-256", "hashing:GOST-R-34.11-2012-256"], "jurisdiction": "russia", "compliance": ["GOST-R"], "dependencies": ["OpenSSL 1.1.1+", "gost-engine.so"], "platforms": ["linux"], "license": "open-source" }, { "id": "sm.soft", "name": "SmSoftCryptoProvider", "assembly": "StellaOps.Cryptography.Providers.Sm.Soft.dll", "type": "StellaOps.Cryptography.Providers.Sm.Soft.SmSoftCryptoProvider", "capabilities": ["signing:SM2", "hashing:SM3"], "jurisdiction": "china", "compliance": ["GM/T"], "dependencies": ["GmSSL 3.0+"], "platforms": ["linux", "windows"], "license": "open-source" }, { "id": "eidas.soft", "name": "EidasSoftCryptoProvider", "assembly": "StellaOps.Cryptography.Providers.Eidas.dll", "type": "StellaOps.Cryptography.Providers.Eidas.EidasSoftCryptoProvider", "capabilities": ["signing:ES256-QES", "signing:ES384-QES"], "jurisdiction": "eu", "compliance": ["eIDAS", "ETSI-TS-119-312"], "dependencies": [], "platforms": ["linux", "windows"], "license": "open-source" } ] } ``` #### 1.2 Runtime Configuration Schema **File:** `appsettings.crypto.yaml` ```yaml StellaOps: Crypto: # Plugin loading configuration Plugins: # Discovery mode: "manifest", "assembly-scan", "explicit" DiscoveryMode: "manifest" # Path to plugin manifest ManifestPath: "/etc/stellaops/crypto-plugins-manifest.json" # Explicit plugin list (overrides manifest if DiscoveryMode=explicit) Enabled: - id: "openssl.gost" priority: 100 options: enginePath: "/usr/lib/x86_64-linux-gnu/engines-3/gost.so" - id: "pkcs11.gost" priority: 90 options: libraryPath: "/usr/lib/librtpkcs11ecp.so" slotId: 0 pinEnvVar: "PKCS11_PIN" - id: "default" priority: 10 # Low priority fallback options: {} # Disabled plugins (explicitly exclude) Disabled: - "cryptopro.gost" # Not licensed - "sm.soft" # Wrong jurisdiction # Fail on missing plugin FailOnMissingPlugin: true # Fail if no plugins loaded RequireAtLeastOne: true # Compliance profile Compliance: ProfileId: "gost" StrictValidation: true # Jurisdiction enforcement EnforceJurisdiction: true AllowedJurisdictions: - "russia" - "international" # Allow international as fallback # Algorithm overrides PurposeOverrides: graph: "GOST-R-34.11-2012-256" content: "SHA-256" # International interop symbol: "GOST-R-34.11-2012-256" password: "Argon2id" # Provider resolution Registry: # Provider selection strategy Strategy: "priority-order" # "priority-order", "capability-match", "explicit-only" # Fallback behavior AllowFallback: false # Fail if primary provider unavailable # Logging LogResolution: true LogLevel: "Information" ``` #### 1.3 Plugin Loader Implementation **File:** `src/__Libraries/StellaOps.Cryptography.PluginLoader/CryptoPluginLoader.cs` ```csharp namespace StellaOps.Cryptography.PluginLoader; public class CryptoPluginLoader { private readonly ILogger _logger; private readonly CryptoPluginConfiguration _config; public async Task> LoadPluginsAsync( CancellationToken cancellationToken = default) { var manifest = await LoadManifestAsync(_config.ManifestPath, cancellationToken); var providers = new List(); foreach (var pluginConfig in _config.Enabled) { var pluginManifest = manifest.Plugins.FirstOrDefault(p => p.Id == pluginConfig.Id); if (pluginManifest == null) { if (_config.FailOnMissingPlugin) throw new CryptoPluginException($"Plugin '{pluginConfig.Id}' not found in manifest"); _logger.LogWarning("Plugin {PluginId} not found in manifest, skipping", pluginConfig.Id); continue; } // Check platform compatibility if (!IsPlatformSupported(pluginManifest.Platforms)) { _logger.LogWarning( "Plugin {PluginId} not supported on platform {Platform}, skipping", pluginConfig.Id, Environment.OSVersion.Platform); continue; } // Check jurisdiction compliance if (_config.Compliance.EnforceJurisdiction) { if (!_config.Compliance.AllowedJurisdictions.Contains(pluginManifest.Jurisdiction)) { _logger.LogWarning( "Plugin {PluginId} jurisdiction '{Jurisdiction}' not allowed, skipping", pluginConfig.Id, pluginManifest.Jurisdiction); continue; } } // Load plugin assembly var provider = await LoadPluginAsync(pluginManifest, pluginConfig, cancellationToken); providers.Add(provider); _logger.LogInformation( "Loaded crypto plugin: {PluginId} ({Name}) with priority {Priority}", pluginConfig.Id, pluginManifest.Name, pluginConfig.Priority); } if (providers.Count == 0 && _config.RequireAtLeastOne) { throw new CryptoPluginException("No crypto plugins loaded - at least one required"); } // Sort by priority (descending) return providers.OrderByDescending(p => GetPriority(p)).ToList(); } private async Task LoadPluginAsync( PluginManifest manifest, PluginConfiguration config, CancellationToken cancellationToken) { // Load assembly var assemblyPath = Path.Combine(_config.PluginDirectory, manifest.Assembly); var assembly = Assembly.LoadFrom(assemblyPath); // Get plugin type var pluginType = assembly.GetType(manifest.Type); if (pluginType == null) throw new CryptoPluginException($"Plugin type '{manifest.Type}' not found in assembly '{manifest.Assembly}'"); // Create instance with DI var provider = ActivatorUtilities.CreateInstance(_serviceProvider, pluginType) as ICryptoProvider; if (provider == null) throw new CryptoPluginException($"Plugin type '{manifest.Type}' does not implement ICryptoProvider"); // Configure plugin if (config.Options != null && provider is IConfigurableCryptoProvider configurable) { await configurable.ConfigureAsync(config.Options, cancellationToken); } return provider; } private bool IsPlatformSupported(string[] platforms) { if (platforms == null || platforms.Length == 0) return true; // No platform restriction var currentPlatform = OperatingSystem.IsWindows() ? "windows" : OperatingSystem.IsLinux() ? "linux" : OperatingSystem.IsMacOS() ? "macos" : "unknown"; return platforms.Contains(currentPlatform); } } ``` #### 1.4 DI Registration (Configuration-Driven) **File:** `src/__Libraries/StellaOps.Cryptography.DependencyInjection/CryptoServiceCollectionExtensions.cs` ```csharp namespace StellaOps.Cryptography.DependencyInjection; public static class CryptoServiceCollectionExtensions { public static IServiceCollection AddStellaOpsCrypto( this IServiceCollection services, IConfiguration configuration) { // Bind configuration services.Configure( configuration.GetSection("StellaOps:Crypto:Plugins")); services.Configure( configuration.GetSection("StellaOps:Crypto:Compliance")); services.Configure( configuration.GetSection("StellaOps:Crypto:Registry")); // Register plugin loader services.AddSingleton(); // Register provider registry services.AddSingleton(sp => { var loader = sp.GetRequiredService(); var providers = loader.LoadPluginsAsync().GetAwaiter().GetResult(); return new CryptoProviderRegistry( providers, sp.GetRequiredService>(), sp.GetRequiredService>()); }); // Register compliance service services.AddSingleton(); return services; } } ``` **CRITICAL CHANGE:** No hardcoded provider registration. Everything loaded from configuration. --- ### 2. Eliminate Direct Crypto Usage #### 2.1 AirGap Module Refactoring **Current (❌ Problem):** ```csharp // AirGap.Importer/EvidenceGraphDsseSigner.cs using System.Security.Cryptography; public class EvidenceGraphDsseSigner { public async Task SignAsync(byte[] payload) { using var ecdsa = ECDsa.Create(ECCurve.NamedCurves.nistP256); // ❌ Hardcoded ecdsa.ImportECPrivateKey(keyBytes, out _); var signature = ecdsa.SignData(payload, HashAlgorithmName.SHA256); // ❌ Hardcoded // ... } } ``` **Refactored (✅ Solution):** ```csharp // AirGap.Importer/EvidenceGraphDsseSigner.cs public class EvidenceGraphDsseSigner { private readonly ICryptoProviderRegistry _cryptoRegistry; public EvidenceGraphDsseSigner(ICryptoProviderRegistry cryptoRegistry) { _cryptoRegistry = cryptoRegistry; } public async Task SignAsync(byte[] payload, string algorithmId) { // Resolve signer from configuration var signerResolution = _cryptoRegistry.ResolveSigner( CryptoCapability.Signing, algorithmId, // From configuration: "ES256", "GOST-R-34.10-2012-256", etc. keyReference, providerHint: null); var signature = await signerResolution.Signer.SignAsync(payload); // ... } } ``` **Configuration for AirGap:** ```yaml # appsettings.airgap.yaml StellaOps: AirGap: # Crypto configuration for offline operations Crypto: # Use lightweight offline-verification provider DefaultAlgorithm: "ES256" AllowedAlgorithms: - "ES256" - "ES384" - "EdDSA" # Plugin for offline verification OfflineProvider: "offline-verification" ``` #### 2.2 Create Offline Verification Plugin **New Plugin:** `StellaOps.Cryptography.Providers.OfflineVerification` ```csharp namespace StellaOps.Cryptography.Providers.OfflineVerification; /// /// Lightweight crypto provider for offline/air-gapped verification. /// Uses .NET crypto internally but exposes through ICryptoProvider abstraction. /// public class OfflineVerificationCryptoProvider : ICryptoProvider { public string Name => "offline-verification"; public bool Supports(CryptoCapability capability, string algorithmId) { if (capability == CryptoCapability.Signing) return algorithmId is "ES256" or "ES384" or "EdDSA"; if (capability == CryptoCapability.Hashing) return algorithmId is "SHA-256" or "SHA-384" or "SHA-512"; return false; } public async Task GetSigner(string algorithmId, CryptoKeyReference keyReference) { // Wrap .NET crypto in ICryptoSigner abstraction return algorithmId switch { "ES256" => new EcdsaSigner(ECCurve.NamedCurves.nistP256, HashAlgorithmName.SHA256, keyReference), "ES384" => new EcdsaSigner(ECCurve.NamedCurves.nistP384, HashAlgorithmName.SHA384, keyReference), "EdDSA" => new Ed25519Signer(keyReference), _ => throw new NotSupportedException($"Algorithm {algorithmId} not supported") }; } public async Task GetHasher(string algorithmId) { return algorithmId switch { "SHA-256" => new DotNetHasher(HashAlgorithmName.SHA256), "SHA-384" => new DotNetHasher(HashAlgorithmName.SHA384), "SHA-512" => new DotNetHasher(HashAlgorithmName.SHA512), _ => throw new NotSupportedException($"Algorithm {algorithmId} not supported") }; } // Internal wrapper for .NET ECDsa private class EcdsaSigner : ICryptoSigner { private readonly ECCurve _curve; private readonly HashAlgorithmName _hashAlgorithm; private readonly CryptoKeyReference _keyReference; public async Task SignAsync(byte[] data, CancellationToken cancellationToken = default) { using var ecdsa = ECDsa.Create(_curve); // Load key from keyReference var keyBytes = await LoadKeyAsync(_keyReference); ecdsa.ImportECPrivateKey(keyBytes, out _); return ecdsa.SignData(data, _hashAlgorithm); } } } ``` **Result:** AirGap module now uses plugin abstraction, but implementation can use .NET crypto internally. #### 2.3 Audit & Remove All Direct Crypto **Automated Detection Script:** ```bash #!/bin/bash # scripts/audit-direct-crypto.sh echo "Scanning for direct crypto usage..." # Find all System.Security.Cryptography usage echo "" echo "=== System.Security.Cryptography usage ===" rg "using System\.Security\.Cryptography;" src/ \ --type csharp \ --files-with-matches # Find all SHA256/ECDsa/RSA.Create() calls echo "" echo "=== Direct crypto API calls ===" rg "(SHA256|SHA384|SHA512|ECDsa|RSA|AES)\.Create\(" src/ \ --type csharp \ --context 2 # Find all BouncyCastle direct usage echo "" echo "=== BouncyCastle direct usage ===" rg "using Org\.BouncyCastle" src/ \ --type csharp \ --files-with-matches # Exclude allowed locations (plugin implementations) echo "" echo "Excluding allowed locations:" echo " - src/__Libraries/StellaOps.Cryptography.Providers.*/" echo " - src/__Tests/" # Final check VIOLATIONS=$(rg "(SHA256|ECDsa|RSA)\.Create\(" src/ \ --type csharp \ --files-with-matches \ | grep -v "Providers\." \ | grep -v "__Tests" \ | wc -l) if [ "$VIOLATIONS" -gt 0 ]; then echo "" echo "❌ FAIL: Found $VIOLATIONS files with direct crypto usage outside plugins" exit 1 else echo "" echo "✅ PASS: No direct crypto usage outside plugin implementations" exit 0 fi ``` **CI Integration:** ```yaml # .gitea/workflows/crypto-audit.yml name: Crypto Architecture Audit on: [push, pull_request] jobs: audit-direct-crypto: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install ripgrep run: sudo apt-get install -y ripgrep - name: Audit direct crypto usage run: ./scripts/audit-direct-crypto.sh - name: Fail if violations found if: failure() run: | echo "Direct crypto usage detected outside plugin implementations" echo "All crypto operations must go through ICryptoProvider abstraction" exit 1 ``` --- ### 3. CI/CD Build Strategy #### 3.1 Multi-Stage Docker Build **File:** `deploy/docker/Dockerfile.platform` ```dockerfile # Stage 1: Build ALL crypto plugins (unconditional) FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build WORKDIR /src # Copy source COPY src/ ./src/ COPY nuget.config Directory.Build.props ./ # Build all crypto provider plugins RUN dotnet build src/__Libraries/StellaOps.Cryptography.Providers.Default/ --configuration Release RUN dotnet build src/__Libraries/StellaOps.Cryptography.Providers.Gost.CryptoPro/ --configuration Release RUN dotnet build src/__Libraries/StellaOps.Cryptography.Providers.Gost.OpenSsl/ --configuration Release RUN dotnet build src/__Libraries/StellaOps.Cryptography.Providers.Gost.Pkcs11/ --configuration Release RUN dotnet build src/__Libraries/StellaOps.Cryptography.Providers.Sm.Soft/ --configuration Release RUN dotnet build src/__Libraries/StellaOps.Cryptography.Providers.Sm.Remote/ --configuration Release RUN dotnet build src/__Libraries/StellaOps.Cryptography.Providers.Eidas/ --configuration Release RUN dotnet build src/__Libraries/StellaOps.Cryptography.Providers.Fips/ --configuration Release RUN dotnet build src/__Libraries/StellaOps.Cryptography.Providers.PostQuantum/ --configuration Release RUN dotnet build src/__Libraries/StellaOps.Cryptography.Providers.OfflineVerification/ --configuration Release # Build platform services RUN dotnet publish src/Authority/StellaOps.Authority.WebService/ --configuration Release --output /app/authority RUN dotnet publish src/Scanner/StellaOps.Scanner.WebService/ --configuration Release --output /app/scanner RUN dotnet publish src/Signer/StellaOps.Signer.WebService/ --configuration Release --output /app/signer RUN dotnet publish src/Attestor/StellaOps.Attestor.WebService/ --configuration Release --output /app/attestor RUN dotnet publish src/Concelier/StellaOps.Concelier.WebService/ --configuration Release --output /app/concelier # Stage 2: Base runtime image (ALL plugins included) FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS base-runtime WORKDIR /app/plugins # Copy ALL crypto provider DLLs COPY --from=build /app/authority/StellaOps.Cryptography.Providers.*.dll ./ COPY --from=build /app/scanner/StellaOps.Cryptography.Providers.*.dll ./ COPY --from=build /app/signer/StellaOps.Cryptography.Providers.*.dll ./ # Copy plugin manifest COPY deploy/crypto-plugins-manifest.json /etc/stellaops/ # Install native crypto libraries (all regions) RUN apt-get update && apt-get install -y \ openssl \ libssl-dev \ # GOST support openssl-gost-engine \ # SM support libgmssl-dev \ && rm -rf /var/lib/apt/lists/* WORKDIR /app # Stage 3: Authority service FROM base-runtime AS authority COPY --from=build /app/authority ./ ENTRYPOINT ["dotnet", "StellaOps.Authority.WebService.dll"] # Stage 4: Scanner service FROM base-runtime AS scanner COPY --from=build /app/scanner ./ ENTRYPOINT ["dotnet", "StellaOps.Scanner.WebService.dll"] # Stage 5: Signer service FROM base-runtime AS signer COPY --from=build /app/signer ./ ENTRYPOINT ["dotnet", "StellaOps.Signer.WebService.dll"] # Stage 6: Attestor service FROM base-runtime AS attestor COPY --from=build /app/attestor ./ ENTRYPOINT ["dotnet", "StellaOps.Attestor.WebService.dll"] # Stage 7: Concelier service FROM base-runtime AS concelier COPY --from=build /app/concelier ./ ENTRYPOINT ["dotnet", "StellaOps.Concelier.WebService.dll"] ``` **Key Points:** - ✅ Builds ALL crypto plugins unconditionally - ✅ Base runtime includes all plugin DLLs - ✅ Services select plugins via configuration at runtime - ✅ No hardcoded crypto in any service image #### 3.2 Regional Configuration Images **File:** `deploy/docker/Dockerfile.crypto-profile` ```dockerfile # Base: Empty configuration image FROM alpine:latest AS crypto-profile-base RUN apk add --no-cache ca-certificates WORKDIR /config # International crypto profile FROM crypto-profile-base AS crypto-profile-international COPY deploy/config/crypto/international/appsettings.crypto.yaml ./appsettings.crypto.yaml COPY deploy/config/crypto/international/crypto-plugins-manifest.json ./crypto-plugins-manifest.json # Russia crypto profile (GOST) FROM crypto-profile-base AS crypto-profile-russia COPY deploy/config/crypto/russia/appsettings.crypto.yaml ./appsettings.crypto.yaml COPY deploy/config/crypto/russia/crypto-plugins-manifest.json ./crypto-plugins-manifest.json # EU crypto profile (eIDAS) FROM crypto-profile-base AS crypto-profile-eu COPY deploy/config/crypto/eu/appsettings.crypto.yaml ./appsettings.crypto.yaml COPY deploy/config/crypto/eu/crypto-plugins-manifest.json ./crypto-plugins-manifest.json # China crypto profile (SM) FROM crypto-profile-base AS crypto-profile-china COPY deploy/config/crypto/china/appsettings.crypto.yaml ./appsettings.crypto.yaml COPY deploy/config/crypto/china/crypto-plugins-manifest.json ./crypto-plugins-manifest.json ``` **Regional Configuration Files:** **File:** `deploy/config/crypto/russia/appsettings.crypto.yaml` ```yaml StellaOps: Crypto: Plugins: DiscoveryMode: "explicit" Enabled: # Primary: CryptoPro GOST (if licensed and Windows) - id: "cryptopro.gost" priority: 100 options: containerName: "StellaOps-GOST-2024" providerType: 80 # Fallback: OpenSSL GOST - id: "openssl.gost" priority: 90 options: enginePath: "/usr/lib/x86_64-linux-gnu/engines-3/gost.so" # Fallback: PKCS#11 GOST - id: "pkcs11.gost" priority: 80 options: libraryPath: "/usr/lib/librtpkcs11ecp.so" slotId: 0 pinEnvVar: "PKCS11_PIN" Disabled: - "default" # ❌ Not compliant with GOST requirements - "bouncycastle.*" # ❌ Not FSTEC-certified - "sm.*" # ❌ Wrong jurisdiction - "eidas.*" # ❌ Wrong jurisdiction FailOnMissingPlugin: true RequireAtLeastOne: true Compliance: ProfileId: "gost" StrictValidation: true EnforceJurisdiction: true AllowedJurisdictions: - "russia" PurposeOverrides: graph: "GOST-R-34.11-2012-256" content: "GOST-R-34.11-2012-256" symbol: "GOST-R-34.11-2012-256" password: "Argon2id" Registry: Strategy: "priority-order" AllowFallback: true # Allow fallback within GOST providers LogResolution: true ``` **File:** `deploy/config/crypto/china/appsettings.crypto.yaml` ```yaml StellaOps: Crypto: Plugins: DiscoveryMode: "explicit" Enabled: # Primary: SM software provider - id: "sm.soft" priority: 100 options: libraryPath: "/usr/lib/libgmssl.so" # Fallback: SM remote service - id: "sm.remote" priority: 90 options: serviceUrl: "${SM_REMOTE_SERVICE_URL}" apiKey: "${SM_REMOTE_API_KEY}" Disabled: - "default" - "gost.*" - "eidas.*" FailOnMissingPlugin: true RequireAtLeastOne: true Compliance: ProfileId: "sm" StrictValidation: true EnforceJurisdiction: true AllowedJurisdictions: - "china" PurposeOverrides: graph: "SM3" content: "SM3" symbol: "SM3" password: "Argon2id" ``` #### 3.3 CI Build Pipeline **File:** `.gitea/workflows/build-crypto-bundles.yml` ```yaml name: Build Crypto Bundles on: push: branches: [main, develop] pull_request: jobs: build-platform-images: name: Build Platform Images (All Crypto Plugins) runs-on: ubuntu-latest strategy: matrix: service: [authority, scanner, signer, attestor, concelier] steps: - uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Build ${{ matrix.service }} image run: | docker buildx build \ --file deploy/docker/Dockerfile.platform \ --target ${{ matrix.service }} \ --tag stellaops/${{ matrix.service }}:latest \ --tag stellaops/${{ matrix.service }}:${{ github.sha }} \ --build-arg BUILD_VERSION=${{ github.sha }} \ --cache-from type=gha \ --cache-to type=gha,mode=max \ --push \ . - name: Verify all crypto plugins included run: | docker run --rm stellaops/${{ matrix.service }}:latest \ ls /app/plugins/StellaOps.Cryptography.Providers.*.dll # Expect to see: # - StellaOps.Cryptography.Providers.Default.dll # - StellaOps.Cryptography.Providers.Gost.*.dll # - StellaOps.Cryptography.Providers.Sm.*.dll # - StellaOps.Cryptography.Providers.Eidas.dll # - etc. build-crypto-profiles: name: Build Crypto Profile Images runs-on: ubuntu-latest needs: build-platform-images strategy: matrix: profile: [international, russia, eu, china] steps: - uses: actions/checkout@v4 - name: Build crypto-profile-${{ matrix.profile }} image run: | docker buildx build \ --file deploy/docker/Dockerfile.crypto-profile \ --target crypto-profile-${{ matrix.profile }} \ --tag stellaops/crypto-profile-${{ matrix.profile }}:latest \ --tag stellaops/crypto-profile-${{ matrix.profile }}:${{ github.sha }} \ --push \ . - name: Validate configuration run: | docker run --rm stellaops/crypto-profile-${{ matrix.profile }}:latest \ cat /config/appsettings.crypto.yaml validate-regional-deployments: name: Validate Regional Deployment runs-on: ubuntu-latest needs: [build-platform-images, build-crypto-profiles] strategy: matrix: region: [international, russia, eu, china] steps: - uses: actions/checkout@v4 - name: Start regional deployment run: | docker-compose \ -f deploy/compose/docker-compose.${{ matrix.region }}.yml \ up -d - name: Wait for services to start run: sleep 30 - name: Verify crypto providers run: | # Call health endpoint to verify crypto provider configuration curl -f http://localhost:5000/health/crypto - name: Validate jurisdiction enforcement run: | # Attempt to use non-compliant algorithm (should fail) ./scripts/test-crypto-compliance.sh ${{ matrix.region }} - name: Tear down if: always() run: | docker-compose \ -f deploy/compose/docker-compose.${{ matrix.region }}.yml \ down -v ``` --- ### 4. Docker Compose Regional Bundles #### 4.1 International Deployment **File:** `deploy/compose/docker-compose.international.yml` ```yaml version: '3.9' services: # Crypto configuration sidecar crypto-config: image: stellaops/crypto-profile-international:latest volumes: - crypto-config:/config:ro # Authority service authority: image: stellaops/authority:latest depends_on: - crypto-config - postgres environment: - ASPNETCORE_ENVIRONMENT=Production - STELLAOPS__CRYPTO__PLUGINS__MANIFESTPATH=/config/crypto-plugins-manifest.json - STELLAOPS__CRYPTO__COMPLIANCE__PROFILEID=world - STELLAOPS__CRYPTO__COMPLIANCE__STRICTVALIDATION=false volumes: - crypto-config:/config:ro ports: - "5000:8080" networks: - stellaops # Scanner service scanner: image: stellaops/scanner:latest depends_on: - crypto-config - postgres environment: - ASPNETCORE_ENVIRONMENT=Production - STELLAOPS__CRYPTO__PLUGINS__MANIFESTPATH=/config/crypto-plugins-manifest.json - STELLAOPS__CRYPTO__COMPLIANCE__PROFILEID=world volumes: - crypto-config:/config:ro networks: - stellaops # Signer service signer: image: stellaops/signer:latest depends_on: - crypto-config - postgres environment: - ASPNETCORE_ENVIRONMENT=Production - STELLAOPS__CRYPTO__PLUGINS__MANIFESTPATH=/config/crypto-plugins-manifest.json - STELLAOPS__CRYPTO__COMPLIANCE__PROFILEID=world volumes: - crypto-config:/config:ro networks: - stellaops # Attestor service attestor: image: stellaops/attestor:latest depends_on: - crypto-config - postgres environment: - ASPNETCORE_ENVIRONMENT=Production - STELLAOPS__CRYPTO__PLUGINS__MANIFESTPATH=/config/crypto-plugins-manifest.json - STELLAOPS__CRYPTO__COMPLIANCE__PROFILEID=world volumes: - crypto-config:/config:ro networks: - stellaops # PostgreSQL postgres: image: postgres:16 environment: - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} volumes: - postgres-data:/var/lib/postgresql/data networks: - stellaops volumes: crypto-config: postgres-data: networks: stellaops: ``` #### 4.2 Russia Deployment (GOST) **File:** `deploy/compose/docker-compose.russia.yml` ```yaml version: '3.9' services: # GOST crypto configuration sidecar crypto-config: image: stellaops/crypto-profile-russia:latest volumes: - crypto-config:/config:ro # Authority service (with GOST) authority: image: stellaops/authority:latest depends_on: - crypto-config - postgres environment: - ASPNETCORE_ENVIRONMENT=Production - STELLAOPS__CRYPTO__PLUGINS__MANIFESTPATH=/config/crypto-plugins-manifest.json - STELLAOPS__CRYPTO__COMPLIANCE__PROFILEID=gost - STELLAOPS__CRYPTO__COMPLIANCE__STRICTVALIDATION=true - STELLAOPS__CRYPTO__COMPLIANCE__ENFORCEJURISDICTION=true # PKCS#11 PIN from secret - PKCS11_PIN=${PKCS11_PIN} volumes: - crypto-config:/config:ro # Mount PKCS#11 library - /usr/lib/librtpkcs11ecp.so:/usr/lib/librtpkcs11ecp.so:ro # Mount OpenSSL GOST engine - /usr/lib/x86_64-linux-gnu/engines-3:/usr/lib/x86_64-linux-gnu/engines-3:ro devices: # Access to hardware security modules - /dev/bus/usb:/dev/bus/usb ports: - "5000:8080" networks: - stellaops # Scanner service (with GOST) scanner: image: stellaops/scanner:latest depends_on: - crypto-config - postgres environment: - ASPNETCORE_ENVIRONMENT=Production - STELLAOPS__CRYPTO__PLUGINS__MANIFESTPATH=/config/crypto-plugins-manifest.json - STELLAOPS__CRYPTO__COMPLIANCE__PROFILEID=gost - STELLAOPS__CRYPTO__COMPLIANCE__STRICTVALIDATION=true - PKCS11_PIN=${PKCS11_PIN} volumes: - crypto-config:/config:ro - /usr/lib/librtpkcs11ecp.so:/usr/lib/librtpkcs11ecp.so:ro - /usr/lib/x86_64-linux-gnu/engines-3:/usr/lib/x86_64-linux-gnu/engines-3:ro devices: - /dev/bus/usb:/dev/bus/usb networks: - stellaops # Signer service (with GOST) signer: image: stellaops/signer:latest depends_on: - crypto-config - postgres environment: - ASPNETCORE_ENVIRONMENT=Production - STELLAOPS__CRYPTO__PLUGINS__MANIFESTPATH=/config/crypto-plugins-manifest.json - STELLAOPS__CRYPTO__COMPLIANCE__PROFILEID=gost - STELLAOPS__CRYPTO__COMPLIANCE__STRICTVALIDATION=true - PKCS11_PIN=${PKCS11_PIN} volumes: - crypto-config:/config:ro - /usr/lib/librtpkcs11ecp.so:/usr/lib/librtpkcs11ecp.so:ro - /usr/lib/x86_64-linux-gnu/engines-3:/usr/lib/x86_64-linux-gnu/engines-3:ro devices: - /dev/bus/usb:/dev/bus/usb networks: - stellaops # Attestor service (with GOST) attestor: image: stellaops/attestor:latest depends_on: - crypto-config - postgres environment: - ASPNETCORE_ENVIRONMENT=Production - STELLAOPS__CRYPTO__PLUGINS__MANIFESTPATH=/config/crypto-plugins-manifest.json - STELLAOPS__CRYPTO__COMPLIANCE__PROFILEID=gost - STELLAOPS__CRYPTO__COMPLIANCE__STRICTVALIDATION=true - PKCS11_PIN=${PKCS11_PIN} volumes: - crypto-config:/config:ro - /usr/lib/librtpkcs11ecp.so:/usr/lib/librtpkcs11ecp.so:ro - /usr/lib/x86_64-linux-gnu/engines-3:/usr/lib/x86_64-linux-gnu/engines-3:ro devices: - /dev/bus/usb:/dev/bus/usb networks: - stellaops # PostgreSQL postgres: image: postgres:16 environment: - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} volumes: - postgres-data:/var/lib/postgresql/data networks: - stellaops volumes: crypto-config: postgres-data: networks: stellaops: ``` #### 4.3 China Deployment (SM) **File:** `deploy/compose/docker-compose.china.yml` ```yaml version: '3.9' services: # SM crypto configuration sidecar crypto-config: image: stellaops/crypto-profile-china:latest volumes: - crypto-config:/config:ro # SM remote signing service (if using remote HSM) sm-remote-signer: image: stellaops/sm-remote-signer:latest environment: - SM_HSM_URL=${SM_HSM_URL} - SM_HSM_API_KEY=${SM_HSM_API_KEY} networks: - stellaops # Authority service (with SM) authority: image: stellaops/authority:latest depends_on: - crypto-config - sm-remote-signer - postgres environment: - ASPNETCORE_ENVIRONMENT=Production - STELLAOPS__CRYPTO__PLUGINS__MANIFESTPATH=/config/crypto-plugins-manifest.json - STELLAOPS__CRYPTO__COMPLIANCE__PROFILEID=sm - STELLAOPS__CRYPTO__COMPLIANCE__STRICTVALIDATION=true - STELLAOPS__CRYPTO__COMPLIANCE__ENFORCEJURISDICTION=true # SM remote service configuration - SM_REMOTE_SERVICE_URL=http://sm-remote-signer:8080 - SM_REMOTE_API_KEY=${SM_REMOTE_API_KEY} # Enable SM algorithms - SM_SOFT_ALLOWED=1 volumes: - crypto-config:/config:ro # Mount GmSSL library - /usr/lib/libgmssl.so:/usr/lib/libgmssl.so:ro ports: - "5000:8080" networks: - stellaops # Scanner service (with SM) scanner: image: stellaops/scanner:latest depends_on: - crypto-config - sm-remote-signer - postgres environment: - ASPNETCORE_ENVIRONMENT=Production - STELLAOPS__CRYPTO__PLUGINS__MANIFESTPATH=/config/crypto-plugins-manifest.json - STELLAOPS__CRYPTO__COMPLIANCE__PROFILEID=sm - STELLAOPS__CRYPTO__COMPLIANCE__STRICTVALIDATION=true - SM_REMOTE_SERVICE_URL=http://sm-remote-signer:8080 - SM_REMOTE_API_KEY=${SM_REMOTE_API_KEY} - SM_SOFT_ALLOWED=1 volumes: - crypto-config:/config:ro - /usr/lib/libgmssl.so:/usr/lib/libgmssl.so:ro networks: - stellaops # Signer service (with SM) signer: image: stellaops/signer:latest depends_on: - crypto-config - sm-remote-signer - postgres environment: - ASPNETCORE_ENVIRONMENT=Production - STELLAOPS__CRYPTO__PLUGINS__MANIFESTPATH=/config/crypto-plugins-manifest.json - STELLAOPS__CRYPTO__COMPLIANCE__PROFILEID=sm - STELLAOPS__CRYPTO__COMPLIANCE__STRICTVALIDATION=true - SM_REMOTE_SERVICE_URL=http://sm-remote-signer:8080 - SM_REMOTE_API_KEY=${SM_REMOTE_API_KEY} - SM_SOFT_ALLOWED=1 volumes: - crypto-config:/config:ro - /usr/lib/libgmssl.so:/usr/lib/libgmssl.so:ro networks: - stellaops # Attestor service (with SM) attestor: image: stellaops/attestor:latest depends_on: - crypto-config - sm-remote-signer - postgres environment: - ASPNETCORE_ENVIRONMENT=Production - STELLAOPS__CRYPTO__PLUGINS__MANIFESTPATH=/config/crypto-plugins-manifest.json - STELLAOPS__CRYPTO__COMPLIANCE__PROFILEID=sm - STELLAOPS__CRYPTO__COMPLIANCE__STRICTVALIDATION=true - SM_REMOTE_SERVICE_URL=http://sm-remote-signer:8080 - SM_REMOTE_API_KEY=${SM_REMOTE_API_KEY} - SM_SOFT_ALLOWED=1 volumes: - crypto-config:/config:ro - /usr/lib/libgmssl.so:/usr/lib/libgmssl.so:ro networks: - stellaops # PostgreSQL postgres: image: postgres:16 environment: - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} volumes: - postgres-data:/var/lib/postgresql/data networks: - stellaops volumes: crypto-config: postgres-data: networks: stellaops: ``` --- ### 5. Deployment & Operations #### 5.1 Deployment Script **File:** `deploy/scripts/deploy-regional.sh` ```bash #!/bin/bash # deploy/scripts/deploy-regional.sh set -euo pipefail REGION=${1:-} ENVIRONMENT=${2:-production} if [ -z "$REGION" ]; then echo "Usage: $0 [environment]" echo "Regions: international, russia, eu, china" echo "Environment: production, staging, development" exit 1 fi echo "Deploying StellaOps to region: $REGION (environment: $ENVIRONMENT)" # Validate region case "$REGION" in international|russia|eu|china) ;; *) echo "❌ Invalid region: $REGION" echo "Valid regions: international, russia, eu, china" exit 1 ;; esac # Load environment-specific secrets echo "Loading secrets for $ENVIRONMENT..." source ./secrets/$ENVIRONMENT.env # Pull latest images echo "Pulling latest images..." docker-compose -f docker-compose.$REGION.yml pull # Validate crypto configuration echo "Validating crypto configuration..." docker run --rm -v $(pwd)/config/$REGION:/config:ro \ stellaops/crypto-validator:latest \ validate /config/appsettings.crypto.yaml # Start services echo "Starting services..." docker-compose -f docker-compose.$REGION.yml up -d # Wait for health checks echo "Waiting for services to be healthy..." timeout 120 bash -c 'until docker-compose -f docker-compose.'"$REGION"'.yml ps | grep -q "(healthy)"; do sleep 5; done' # Verify crypto provider configuration echo "Verifying crypto provider configuration..." AUTHORITY_URL=$(docker-compose -f docker-compose.$REGION.yml port authority 8080) curl -f "http://$AUTHORITY_URL/health/crypto" | jq . echo "✅ Deployment complete" echo "" echo "Region: $REGION" echo "Environment: $ENVIRONMENT" echo "Services:" docker-compose -f docker-compose.$REGION.yml ps ``` #### 5.2 Crypto Compliance Validation **File:** `scripts/test-crypto-compliance.sh` ```bash #!/bin/bash # scripts/test-crypto-compliance.sh set -euo pipefail REGION=$1 AUTHORITY_URL=${2:-http://localhost:5000} echo "Testing crypto compliance for region: $REGION" # Test 1: Verify active providers echo "" echo "=== Test 1: Verify Active Crypto Providers ===" PROVIDERS=$(curl -s "$AUTHORITY_URL/api/v1/admin/crypto/providers" | jq -r '.providers[].id') echo "Active providers: $PROVIDERS" case "$REGION" in international) if echo "$PROVIDERS" | grep -q "default"; then echo "✅ PASS: Default provider active" else echo "❌ FAIL: Default provider not active" exit 1 fi if echo "$PROVIDERS" | grep -q "gost\|sm\|eidas"; then echo "❌ FAIL: Regional provider active in international deployment" exit 1 fi ;; russia) if echo "$PROVIDERS" | grep -q "gost"; then echo "✅ PASS: GOST provider active" else echo "❌ FAIL: GOST provider not active" exit 1 fi if echo "$PROVIDERS" | grep -q "sm\|eidas"; then echo "❌ FAIL: Non-GOST regional provider active" exit 1 fi ;; china) if echo "$PROVIDERS" | grep -q "sm"; then echo "✅ PASS: SM provider active" else echo "❌ FAIL: SM provider not active" exit 1 fi if echo "$PROVIDERS" | grep -q "gost\|eidas"; then echo "❌ FAIL: Non-SM regional provider active" exit 1 fi ;; eu) if echo "$PROVIDERS" | grep -q "eidas"; then echo "✅ PASS: eIDAS provider active" else echo "❌ FAIL: eIDAS provider not active" exit 1 fi if echo "$PROVIDERS" | grep -q "gost\|sm"; then echo "❌ FAIL: Non-eIDAS regional provider active" exit 1 fi ;; esac # Test 2: Verify strict validation enforcement echo "" echo "=== Test 2: Verify Strict Validation ===" STRICT=$(curl -s "$AUTHORITY_URL/api/v1/admin/crypto/config" | jq -r '.strictValidation') if [ "$STRICT" = "true" ] || [ "$REGION" = "international" ]; then echo "✅ PASS: Strict validation: $STRICT" else echo "❌ FAIL: Strict validation should be enabled for region $REGION" exit 1 fi # Test 3: Attempt non-compliant signature (should fail in strict mode) echo "" echo "=== Test 3: Attempt Non-Compliant Signature ===" case "$REGION" in russia) # Try to use ES256 (should fail in Russia - GOST required) if curl -s -w "%{http_code}" "$AUTHORITY_URL/api/v1/test/sign" \ -H "Content-Type: application/json" \ -d '{"algorithm":"ES256","data":"dGVzdA=="}' | grep -q "403\|400"; then echo "✅ PASS: Non-GOST algorithm rejected" else echo "❌ FAIL: Non-GOST algorithm accepted (should be rejected)" exit 1 fi ;; china) # Try to use ES256 (should fail in China - SM required) if curl -s -w "%{http_code}" "$AUTHORITY_URL/api/v1/test/sign" \ -H "Content-Type: application/json" \ -d '{"algorithm":"ES256","data":"dGVzdA=="}' | grep -q "403\|400"; then echo "✅ PASS: Non-SM algorithm rejected" else echo "❌ FAIL: Non-SM algorithm accepted (should be rejected)" exit 1 fi ;; esac echo "" echo "✅ All crypto compliance tests passed for region: $REGION" ``` --- ### 6. Implementation Roadmap #### Phase 1: Foundation (Week 1-2) **Sprint:** CRYPTO_CONFIG_DRIVEN_PHASE1 1. **Create plugin loader infrastructure** - [ ] Design plugin manifest schema - [ ] Implement `CryptoPluginLoader` class - [ ] Add plugin configuration schema - [ ] Create `IConfigurableCryptoProvider` interface 2. **Refactor DI registration** - [ ] Remove hardcoded provider registration from `AddStellaOpsCrypto()` - [ ] Implement configuration-driven loading - [ ] Add jurisdiction enforcement - [ ] Add plugin validation 3. **Create offline verification plugin** - [ ] Implement `OfflineVerificationCryptoProvider` - [ ] Wrap .NET crypto in ICryptoProvider abstraction - [ ] Add to plugin manifest #### Phase 2: Code Refactoring (Week 3-4) **Sprint:** CRYPTO_CONFIG_DRIVEN_PHASE2 4. **Eliminate direct crypto usage** - [ ] Refactor AirGap module to use ICryptoProvider - [ ] Refactor test fixtures to use test providers - [ ] Remove all System.Security.Cryptography direct calls - [ ] Add audit script to CI 5. **Update all modules** - [ ] Verify Authority uses plugin registry - [ ] Verify Scanner uses plugin registry - [ ] Verify Signer uses plugin registry - [ ] Verify Attestor uses plugin registry - [ ] Add integration tests #### Phase 3: Docker & CI/CD (Week 5-6) **Sprint:** CRYPTO_CONFIG_DRIVEN_PHASE3 6. **Create Docker infrastructure** - [ ] Create multi-stage Dockerfile.platform - [ ] Create Dockerfile.crypto-profile - [ ] Build all crypto provider plugins - [ ] Create base runtime with all plugins 7. **Create regional configurations** - [ ] Create international crypto config - [ ] Create Russia (GOST) crypto config - [ ] Create EU (eIDAS) crypto config - [ ] Create China (SM) crypto config 8. **Create Docker Compose files** - [ ] Create docker-compose.international.yml - [ ] Create docker-compose.russia.yml - [ ] Create docker-compose.eu.yml - [ ] Create docker-compose.china.yml #### Phase 4: Validation & Testing (Week 7-8) **Sprint:** CRYPTO_CONFIG_DRIVEN_PHASE4 9. **Add validation infrastructure** - [ ] Create crypto compliance test script - [ ] Add CI validation for each region - [ ] Create deployment validation script - [ ] Add health check endpoints 10. **Integration testing** - [ ] Test international deployment - [ ] Test Russia (GOST) deployment - [ ] Test EU (eIDAS) deployment - [ ] Test China (SM) deployment - [ ] Test jurisdiction enforcement 11. **Documentation** - [ ] Update operator documentation - [ ] Create deployment runbooks - [ ] Document regional configurations - [ ] Create troubleshooting guide --- ### 7. Success Criteria ✅ **Zero Direct Crypto in Code** - All System.Security.Cryptography usage eliminated from production code - All modules use ICryptoProvider abstraction - AirGap module uses offline-verification plugin - CI audit script enforces zero direct crypto ✅ **Configuration-Driven Plugin Selection** - Plugins loaded from manifest or configuration - No hardcoded provider registration - Runtime selection via appsettings.yaml or environment variables - Fail-safe: services won't start with invalid configuration ✅ **Regional Docker Bundles** - One Docker Compose file per region (international, russia, eu, china) - Each bundle includes crypto profile configuration sidecar - All service images identical (selection via config) - Jurisdiction enforcement prevents accidental misuse ✅ **CI Builds All Plugins** - All 13+ crypto providers built unconditionally - Single base image with all plugins - Regional images select plugins via configuration - Validation tests ensure compliance ✅ **Operational Excellence** - Deployment script validates crypto configuration - Health checks verify active providers - Compliance tests enforce jurisdiction rules - Monitoring shows which providers are active --- ## Estimated Effort **Total:** 8 weeks (2 months) **Team:** - 2 senior engineers (crypto/architecture) - 1 DevOps engineer (Docker/CI) - 1 QA engineer (compliance testing) **Milestones:** - Week 2: Plugin loader complete - Week 4: All direct crypto eliminated - Week 6: Docker bundles complete - Week 8: Full validation and deployment ready --- ## Risks & Mitigations | Risk | Impact | Mitigation | |------|--------|------------| | Breaking change for existing deployments | HIGH | Provide migration tool; maintain backward compatibility mode | | Plugin loading performance | MEDIUM | Cache loaded plugins; lazy initialization | | Configuration complexity | MEDIUM | Validation tooling; clear error messages | | Regional compliance violations | CRITICAL | Strict validation; automated compliance tests; CI enforcement | | Native library dependencies | MEDIUM | Document dependencies; provide installation scripts | --- **Document Status:** READY FOR REVIEW **Next Step:** Approval from architecture review board