## 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>
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:
- Zero Direct Crypto in Code - All modules use ICryptoProvider abstraction
- Build Once, Deploy Everywhere - CI builds ALL plugins unconditionally
- Configuration-Driven Selection - appsettings.yaml/env vars select active plugins
- Regional Docker Bundles - Separate docker-compose files + crypto profile images per jurisdiction
- 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
-
Create plugin loader infrastructure
- Design plugin manifest schema
- Implement
CryptoPluginLoaderclass - Add plugin configuration schema
- Create
IConfigurableCryptoProviderinterface
-
Refactor DI registration
- Remove hardcoded provider registration from
AddStellaOpsCrypto() - Implement configuration-driven loading
- Add jurisdiction enforcement
- Add plugin validation
- Remove hardcoded provider registration from
-
Create offline verification plugin
- Implement
OfflineVerificationCryptoProvider - Wrap .NET crypto in ICryptoProvider abstraction
- Add to plugin manifest
- Implement
Phase 2: Code Refactoring (Week 3-4)
Sprint: CRYPTO_CONFIG_DRIVEN_PHASE2
-
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
-
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
-
Create Docker infrastructure
- Create multi-stage Dockerfile.platform
- Create Dockerfile.crypto-profile
- Build all crypto provider plugins
- Create base runtime with all plugins
-
Create regional configurations
- Create international crypto config
- Create Russia (GOST) crypto config
- Create EU (eIDAS) crypto config
- Create China (SM) crypto config
-
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
-
Add validation infrastructure
- Create crypto compliance test script
- Add CI validation for each region
- Create deployment validation script
- Add health check endpoints
-
Integration testing
- Test international deployment
- Test Russia (GOST) deployment
- Test EU (eIDAS) deployment
- Test China (SM) deployment
- Test jurisdiction enforcement
-
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