Files
git.stella-ops.org/docs/implplan/CRYPTO_CONFIGURATION_DRIVEN_ARCHITECTURE.md
master dac8e10e36 feat(crypto): Complete Phase 2 - Configuration-driven crypto architecture with 100% compliance
## Summary

This commit completes Phase 2 of the configuration-driven crypto architecture, achieving
100% crypto compliance by eliminating all hardcoded cryptographic implementations.

## Key Changes

### Phase 1: Plugin Loader Infrastructure
- **Plugin Discovery System**: Created StellaOps.Cryptography.PluginLoader with manifest-based loading
- **Configuration Model**: Added CryptoPluginConfiguration with regional profiles support
- **Dependency Injection**: Extended DI to support plugin-based crypto provider registration
- **Regional Configs**: Created appsettings.crypto.{international,russia,eu,china}.yaml
- **CI Workflow**: Added .gitea/workflows/crypto-compliance.yml for audit enforcement

### Phase 2: Code Refactoring
- **API Extension**: Added ICryptoProvider.CreateEphemeralVerifier for verification-only scenarios
- **Plugin Implementation**: Created OfflineVerificationCryptoProvider with ephemeral verifier support
  - Supports ES256/384/512, RS256/384/512, PS256/384/512
  - SubjectPublicKeyInfo (SPKI) public key format
- **100% Compliance**: Refactored DsseVerifier to remove all BouncyCastle cryptographic usage
- **Unit Tests**: Created OfflineVerificationProviderTests with 39 passing tests
- **Documentation**: Created comprehensive security guide at docs/security/offline-verification-crypto-provider.md
- **Audit Infrastructure**: Created scripts/audit-crypto-usage.ps1 for static analysis

### Testing Infrastructure (TestKit)
- **Determinism Gate**: Created DeterminismGate for reproducibility validation
- **Test Fixtures**: Added PostgresFixture and ValkeyFixture using Testcontainers
- **Traits System**: Implemented test lane attributes for parallel CI execution
- **JSON Assertions**: Added CanonicalJsonAssert for deterministic JSON comparisons
- **Test Lanes**: Created test-lanes.yml workflow for parallel test execution

### Documentation
- **Architecture**: Created CRYPTO_CONFIGURATION_DRIVEN_ARCHITECTURE.md master plan
- **Sprint Tracking**: Created SPRINT_1000_0007_0002_crypto_refactoring.md (COMPLETE)
- **API Documentation**: Updated docs2/cli/crypto-plugins.md and crypto.md
- **Testing Strategy**: Created testing strategy documents in docs/implplan/SPRINT_5100_0007_*

## Compliance & Testing

-  Zero direct System.Security.Cryptography usage in production code
-  All crypto operations go through ICryptoProvider abstraction
-  39/39 unit tests passing for OfflineVerificationCryptoProvider
-  Build successful (AirGap, Crypto plugin, DI infrastructure)
-  Audit script validates crypto boundaries

## Files Modified

**Core Crypto Infrastructure:**
- src/__Libraries/StellaOps.Cryptography/CryptoProvider.cs (API extension)
- src/__Libraries/StellaOps.Cryptography/CryptoSigningKey.cs (verification-only constructor)
- src/__Libraries/StellaOps.Cryptography/EcdsaSigner.cs (fixed ephemeral verifier)

**Plugin Implementation:**
- src/__Libraries/StellaOps.Cryptography.Plugin.OfflineVerification/ (new)
- src/__Libraries/StellaOps.Cryptography.PluginLoader/ (new)

**Production Code Refactoring:**
- src/AirGap/StellaOps.AirGap.Importer/Validation/DsseVerifier.cs (100% compliant)

**Tests:**
- src/__Libraries/__Tests/StellaOps.Cryptography.Plugin.OfflineVerification.Tests/ (new, 39 tests)
- src/__Libraries/__Tests/StellaOps.Cryptography.PluginLoader.Tests/ (new)

**Configuration:**
- etc/crypto-plugins-manifest.json (plugin registry)
- etc/appsettings.crypto.*.yaml (regional profiles)

**Documentation:**
- docs/security/offline-verification-crypto-provider.md (600+ lines)
- docs/implplan/CRYPTO_CONFIGURATION_DRIVEN_ARCHITECTURE.md (master plan)
- docs/implplan/SPRINT_1000_0007_0002_crypto_refactoring.md (Phase 2 complete)

## Next Steps

Phase 3: Docker & CI/CD Integration
- Create multi-stage Dockerfiles with all plugins
- Build regional Docker Compose files
- Implement runtime configuration selection
- Add deployment validation scripts

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-23 18:20:00 +02:00

50 KiB

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)

// Problem 1: Direct crypto in AirGap module
using System.Security.Cryptography;

var hash = SHA256.HashData(data);  // ❌ Hardcoded crypto
var ecdsa = ECDsa.Create();        // ❌ Hardcoded crypto
// 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
}
# Problem 3: No regional Docker bundles
# Single docker-compose.yml for all deployments

Target State ( Solution)

// Solution 1: NO direct crypto - everything through abstraction
public class AirGapVerificationService
{
    private readonly ICryptoHasher _hasher;  // ✅ Injected
    private readonly ICryptoSigner _signer;  // ✅ Injected

    public async Task<bool> VerifyAsync(byte[] data)
    {
        var hash = await _hasher.HashAsync(data);  // ✅ Plugin-based
        return await _signer.VerifyAsync(...);     // ✅ Plugin-based
    }
}
// Solution 2: Configuration-driven plugin loading
public static IServiceCollection AddStellaOpsCrypto(
    IServiceProvider services,
    IConfiguration configuration)
{
    var config = configuration.GetSection("Crypto:Plugins").Get<CryptoPluginConfiguration>();

    // 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");
}
# 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

{
  "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

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

namespace StellaOps.Cryptography.PluginLoader;

public class CryptoPluginLoader
{
    private readonly ILogger<CryptoPluginLoader> _logger;
    private readonly CryptoPluginConfiguration _config;

    public async Task<List<ICryptoProvider>> LoadPluginsAsync(
        CancellationToken cancellationToken = default)
    {
        var manifest = await LoadManifestAsync(_config.ManifestPath, cancellationToken);
        var providers = new List<ICryptoProvider>();

        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<ICryptoProvider> 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

namespace StellaOps.Cryptography.DependencyInjection;

public static class CryptoServiceCollectionExtensions
{
    public static IServiceCollection AddStellaOpsCrypto(
        this IServiceCollection services,
        IConfiguration configuration)
    {
        // Bind configuration
        services.Configure<CryptoPluginConfiguration>(
            configuration.GetSection("StellaOps:Crypto:Plugins"));
        services.Configure<CryptoComplianceOptions>(
            configuration.GetSection("StellaOps:Crypto:Compliance"));
        services.Configure<CryptoRegistryOptions>(
            configuration.GetSection("StellaOps:Crypto:Registry"));

        // Register plugin loader
        services.AddSingleton<CryptoPluginLoader>();

        // Register provider registry
        services.AddSingleton<ICryptoProviderRegistry>(sp =>
        {
            var loader = sp.GetRequiredService<CryptoPluginLoader>();
            var providers = loader.LoadPluginsAsync().GetAwaiter().GetResult();

            return new CryptoProviderRegistry(
                providers,
                sp.GetRequiredService<IOptions<CryptoRegistryOptions>>(),
                sp.GetRequiredService<ILogger<CryptoProviderRegistry>>());
        });

        // Register compliance service
        services.AddSingleton<ICryptoComplianceService, CryptoComplianceService>();

        return services;
    }
}

CRITICAL CHANGE: No hardcoded provider registration. Everything loaded from configuration.


2. Eliminate Direct Crypto Usage

2.1 AirGap Module Refactoring

Current ( Problem):

// AirGap.Importer/EvidenceGraphDsseSigner.cs
using System.Security.Cryptography;

public class EvidenceGraphDsseSigner
{
    public async Task<DsseEnvelope> 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):

// AirGap.Importer/EvidenceGraphDsseSigner.cs
public class EvidenceGraphDsseSigner
{
    private readonly ICryptoProviderRegistry _cryptoRegistry;

    public EvidenceGraphDsseSigner(ICryptoProviderRegistry cryptoRegistry)
    {
        _cryptoRegistry = cryptoRegistry;
    }

    public async Task<DsseEnvelope> 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:

# 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

namespace StellaOps.Cryptography.Providers.OfflineVerification;

/// <summary>
/// Lightweight crypto provider for offline/air-gapped verification.
/// Uses .NET crypto internally but exposes through ICryptoProvider abstraction.
/// </summary>
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<ICryptoSigner> 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<ICryptoHasher> 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<byte[]> 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:

#!/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:

# .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

# 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

# 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

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

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

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

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

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

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

#!/bin/bash
# deploy/scripts/deploy-regional.sh

set -euo pipefail

REGION=${1:-}
ENVIRONMENT=${2:-production}

if [ -z "$REGION" ]; then
    echo "Usage: $0 <region> [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

#!/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

  1. 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
  2. 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

  1. Create Docker infrastructure

    • Create multi-stage Dockerfile.platform
    • Create Dockerfile.crypto-profile
    • Build all crypto provider plugins
    • Create base runtime with all plugins
  2. Create regional configurations

    • Create international crypto config
    • Create Russia (GOST) crypto config
    • Create EU (eIDAS) crypto config
    • Create China (SM) crypto config
  3. 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

  1. Add validation infrastructure

    • Create crypto compliance test script
    • Add CI validation for each region
    • Create deployment validation script
    • Add health check endpoints
  2. Integration testing

    • Test international deployment
    • Test Russia (GOST) deployment
    • Test EU (eIDAS) deployment
    • Test China (SM) deployment
    • Test jurisdiction enforcement
  3. 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