# Wine CSP Loader Design · CryptoPro GOST Validation **Status:** IMPLEMENTED (HTTP-based approach) **Date:** 2025-12-07 **Owners:** Security Guild, DevOps **Related:** RU-CRYPTO-VAL-04, RU-CRYPTO-VAL-05 ## Implementation Status The HTTP-based Wine RPC Server approach (Approach C variant) has been implemented: | Component | Path | Status | |-----------|------|--------| | Wine CSP HTTP Service | `src/__Tools/WineCspService/` | DONE | | Setup Script | `scripts/crypto/setup-wine-csp-service.sh` | DONE | | Crypto Registry Provider | `src/__Libraries/StellaOps.Cryptography.Plugin.WineCsp/` | DONE | ### Implementation Files - **`src/__Tools/WineCspService/Program.cs`** - ASP.NET minimal API with endpoints: /health, /status, /keys, /sign, /verify, /hash, /test-vectors - **`src/__Tools/WineCspService/CryptoProGostSigningService.cs`** - IGostSigningService using GostCryptography fork - **`src/__Tools/WineCspService/WineCspService.csproj`** - .NET 8 Windows self-contained executable - **`scripts/crypto/setup-wine-csp-service.sh`** - Wine environment setup, builds service, creates systemd unit - **`src/__Libraries/StellaOps.Cryptography.Plugin.WineCsp/WineCspHttpProvider.cs`** - ICryptoProvider implementation - **`src/__Libraries/StellaOps.Cryptography.Plugin.WineCsp/WineCspHttpSigner.cs`** - ICryptoSigner via HTTP - **`src/__Libraries/StellaOps.Cryptography.Plugin.WineCsp/WineCspHttpClient.cs`** - HTTP client with retry policies ### Usage ```bash # Setup Wine environment and build service ./scripts/crypto/setup-wine-csp-service.sh [--csp-installer /path/to/csp_setup.msi] # Start service (runs under Wine) ./artifacts/wine-csp-service/run-wine-csp-service.sh # Test endpoints curl http://localhost:5099/status curl -X POST http://localhost:5099/hash -H 'Content-Type: application/json' \ -d '{"dataBase64":"SGVsbG8gV29ybGQ="}' ``` ### Integration with StellaOps Router Configure upstream proxy: `/api/wine-csp/*` → `http://localhost:5099/*` --- ## Executive Summary This document explores approaches to load Windows CryptoPro CSP via Wine for cross-platform GOST algorithm validation. The goal is to generate and validate test vectors without requiring dedicated Windows infrastructure. **Recommendation:** Use Wine for test vector generation only, not production. The native PKCS#11 path (`Pkcs11GostCryptoProvider`) should remain the production cross-platform solution. ## 1. Architecture Overview ### Current State ``` ┌─────────────────────────────────────────────────────────────────────────────┐ │ Current GOST Provider Hierarchy │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ ICryptoProviderRegistry │ │ │ │ │ │ │ │ Profile: ru-offline │ │ │ │ PreferredOrder: [ru.cryptopro.csp, ru.openssl.gost, ru.pkcs11] │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ┌────────────────────┼────────────────────┐ │ │ ▼ ▼ ▼ │ │ ┌──────────────┐ ┌───────────────┐ ┌──────────────┐ │ │ │ CryptoPro │ │ OpenSSL GOST │ │ PKCS#11 │ │ │ │ CSP Provider │ │ Provider │ │ Provider │ │ │ │ │ │ │ │ │ │ │ │ Windows ONLY │ │ Cross-plat │ │ Cross-plat │ │ │ │ CSP APIs │ │ BouncyCastle │ │ Token-based │ │ │ └──────────────┘ └───────────────┘ └──────────────┘ │ │ ❌ ✓ ✓ │ │ (Linux N/A) (Fallback) (Hardware) │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ ``` ### Proposed Wine Integration ``` ┌─────────────────────────────────────────────────────────────────────────────┐ │ Wine CSP Loader Architecture │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌────────────────────────────────────────────────────────────────────────┐│ │ │ Linux Host ││ │ │ ││ │ │ ┌─────────────────────┐ ┌─────────────────────────────────────┐ ││ │ │ │ StellaOps .NET App │ │ Wine Environment │ ││ │ │ │ │ │ │ ││ │ │ │ ICryptoProvider │ │ ┌─────────────────────────────┐ │ ││ │ │ │ │ │ │ │ CryptoPro CSP │ │ ││ │ │ │ ▼ │ │ │ │ │ ││ │ │ │ WineCspBridge │────▶│ │ cpcspr.dll │ │ ││ │ │ │ (P/Invoke) │ │ │ cpcsp.dll │ │ ││ │ │ │ │ │ │ asn1rt.dll │ │ ││ │ │ └─────────────────────┘ │ └─────────────────────────────┘ │ ││ │ │ │ │ │ │ ││ │ │ │ IPC/Socket │ │ Wine CryptoAPI │ ││ │ │ │ │ ▼ │ ││ │ │ │ │ ┌─────────────────────────────┐ │ ││ │ │ │ │ │ Wine crypt32.dll │ │ ││ │ │ └──────────────────▶│ │ Wine advapi32.dll │ │ ││ │ │ │ └─────────────────────────────┘ │ ││ │ │ └─────────────────────────────────────┘ ││ │ └────────────────────────────────────────────────────────────────────────┘│ │ │ └─────────────────────────────────────────────────────────────────────────────┘ ``` ## 2. Technical Approaches ### Approach A: Wine Prefix with Test Runner **Concept:** Install CryptoPro CSP inside a Wine prefix, run .NET test binaries under Wine. **Implementation:** ```bash #!/bin/bash # scripts/crypto/setup-wine-cryptopro.sh set -euo pipefail WINE_PREFIX="${WINE_PREFIX:-$HOME/.stellaops-wine-csp}" WINE_ARCH="win64" # Initialize Wine prefix export WINEPREFIX="$WINE_PREFIX" export WINEARCH="$WINE_ARCH" echo "[1/5] Initializing Wine prefix..." wineboot --init echo "[2/5] Installing .NET runtime dependencies..." winetricks -q dotnet48 vcrun2019 echo "[3/5] Setting Windows version..." winetricks -q win10 echo "[4/5] Installing CryptoPro CSP..." # Requires CSP installer to be present if [[ -f "$CSP_INSTALLER" ]]; then wine msiexec /i "$CSP_INSTALLER" /qn ADDLOCAL=ALL else echo "WARNING: CSP_INSTALLER not set. Manual installation required." echo " wine msiexec /i /path/to/csp_setup_x64.msi /qn" fi echo "[5/5] Verifying CSP registration..." wine reg query "HKLM\\SOFTWARE\\Microsoft\\Cryptography\\Defaults\\Provider" 2>/dev/null || { echo "ERROR: CSP not registered in Wine registry" exit 1 } echo "Wine CryptoPro environment ready: $WINE_PREFIX" ``` **Test Vector Generation:** ```bash #!/bin/bash # scripts/crypto/generate-wine-test-vectors.sh export WINEPREFIX="$HOME/.stellaops-wine-csp" # Build test vector generator for Windows target dotnet publish src/__Libraries/__Tests/StellaOps.Cryptography.Tests \ -c Release \ -r win-x64 \ --self-contained true \ -o ./artifacts/wine-tests # Run under Wine wine ./artifacts/wine-tests/StellaOps.Cryptography.Tests.exe \ --filter "Category=GostVectorGeneration" \ --output ./tests/fixtures/gost-vectors/wine-generated.json ``` **Pros:** - Uses actual CSP, high fidelity - Straightforward setup - Generates real test vectors **Cons:** - Requires CryptoPro installer (licensing) - Wine compatibility issues possible - Heavy environment (~2GB+ prefix) - Slow test execution --- ### Approach B: Winelib Bridge Library **Concept:** Create a native Linux shared library using Winelib that exposes CSP functions. **Implementation:** ```c // src/native/wine-csp-bridge/csp_bridge.c // Compile: winegcc -shared -o libcspbridge.so csp_bridge.c -lcrypt32 #define WIN32_LEAN_AND_MEAN #include #include #include #include // Exported bridge functions (POSIX ABI) #ifdef __cplusplus extern "C" { #endif typedef struct { int error_code; char error_message[256]; unsigned char signature[512]; size_t signature_length; } CspBridgeResult; // Initialize CSP context __attribute__((visibility("default"))) int csp_bridge_init(const char* provider_name, void** context_out) { HCRYPTPROV hProv = 0; // Convert provider name to wide string wchar_t wProviderName[256]; mbstowcs(wProviderName, provider_name, 256); if (!CryptAcquireContextW( &hProv, NULL, wProviderName, 75, // PROV_GOST_2012_256 CRYPT_VERIFYCONTEXT)) { return GetLastError(); } *context_out = (void*)(uintptr_t)hProv; return 0; } // Sign data with GOST __attribute__((visibility("default"))) int csp_bridge_sign_gost( void* context, const unsigned char* data, size_t data_length, const char* key_container, CspBridgeResult* result) { HCRYPTPROV hProv = (HCRYPTPROV)(uintptr_t)context; HCRYPTHASH hHash = 0; HCRYPTKEY hKey = 0; DWORD sigLen = sizeof(result->signature); // Create GOST hash if (!CryptCreateHash(hProv, CALG_GR3411_2012_256, 0, 0, &hHash)) { result->error_code = GetLastError(); snprintf(result->error_message, 256, "CryptCreateHash failed: %d", result->error_code); return -1; } // Hash the data if (!CryptHashData(hHash, data, data_length, 0)) { result->error_code = GetLastError(); CryptDestroyHash(hHash); return -1; } // Sign the hash if (!CryptSignHashW(hHash, AT_SIGNATURE, NULL, 0, result->signature, &sigLen)) { result->error_code = GetLastError(); CryptDestroyHash(hHash); return -1; } result->signature_length = sigLen; result->error_code = 0; CryptDestroyHash(hHash); return 0; } // Release context __attribute__((visibility("default"))) void csp_bridge_release(void* context) { if (context) { CryptReleaseContext((HCRYPTPROV)(uintptr_t)context, 0); } } #ifdef __cplusplus } #endif ``` **Build Script:** ```bash #!/bin/bash # scripts/crypto/build-wine-bridge.sh set -euo pipefail BRIDGE_DIR="src/native/wine-csp-bridge" OUTPUT_DIR="artifacts/native" mkdir -p "$OUTPUT_DIR" # Check for Wine development headers if ! command -v winegcc &> /dev/null; then echo "ERROR: winegcc not found. Install wine-devel package." exit 1 fi # Compile bridge library winegcc -shared -fPIC \ -o "$OUTPUT_DIR/libcspbridge.dll.so" \ "$BRIDGE_DIR/csp_bridge.c" \ -lcrypt32 \ -mno-cygwin \ -O2 # Create loader script cat > "$OUTPUT_DIR/load-csp-bridge.sh" << 'EOF' #!/bin/bash export WINEPREFIX="${WINEPREFIX:-$HOME/.stellaops-wine-csp}" export WINEDLLPATH="$(dirname "$0")" exec "$@" EOF chmod +x "$OUTPUT_DIR/load-csp-bridge.sh" echo "Bridge library built: $OUTPUT_DIR/libcspbridge.dll.so" ``` **.NET P/Invoke Wrapper:** ```csharp // src/__Libraries/StellaOps.Cryptography.Plugin.WineCsp/WineCspBridge.cs using System; using System.Runtime.InteropServices; namespace StellaOps.Cryptography.Plugin.WineCsp; /// /// P/Invoke bridge to Wine-hosted CryptoPro CSP. /// EXPERIMENTAL: For test vector generation only. /// internal static partial class WineCspBridge { private const string LibraryName = "libcspbridge.dll.so"; [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct CspBridgeResult { public int ErrorCode; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string ErrorMessage; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 512)] public byte[] Signature; public nuint SignatureLength; } [LibraryImport(LibraryName, EntryPoint = "csp_bridge_init")] public static partial int Init( [MarshalAs(UnmanagedType.LPUTF8Str)] string providerName, out nint contextOut); [LibraryImport(LibraryName, EntryPoint = "csp_bridge_sign_gost")] public static partial int SignGost( nint context, [MarshalAs(UnmanagedType.LPArray)] byte[] data, nuint dataLength, [MarshalAs(UnmanagedType.LPUTF8Str)] string keyContainer, ref CspBridgeResult result); [LibraryImport(LibraryName, EntryPoint = "csp_bridge_release")] public static partial void Release(nint context); } /// /// Wine-based GOST crypto provider for test vector generation. /// public sealed class WineCspGostProvider : ICryptoProvider, IDisposable { private nint _context; private bool _disposed; public string Name => "ru.wine.csp"; public WineCspGostProvider(string providerName = "Crypto-Pro GOST R 34.10-2012 CSP") { var result = WineCspBridge.Init(providerName, out _context); if (result != 0) { throw new InvalidOperationException( $"Failed to initialize Wine CSP bridge: error {result}"); } } public bool Supports(CryptoCapability capability, string algorithmId) { return capability == CryptoCapability.Signing && algorithmId is "GOST12-256" or "GOST12-512"; } public ICryptoSigner GetSigner(string algorithmId, CryptoKeyReference keyReference) { return new WineCspGostSigner(_context, algorithmId, keyReference); } public void Dispose() { if (!_disposed) { WineCspBridge.Release(_context); _disposed = true; } } // ... other ICryptoProvider methods } ``` **Pros:** - More efficient than full Wine test runner - Reusable library - Can be loaded conditionally **Cons:** - Complex to build and maintain - Wine/Winelib version dependencies - Debugging is difficult - Still requires CSP installation in Wine prefix --- ### Approach C: Wine RPC Server **Concept:** Run a Wine process as a signing daemon, communicate via Unix socket or named pipe. **Architecture:** ``` ┌─────────────────────────────────────────────────────────────────────────────┐ │ Wine RPC Server Architecture │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────────────────┐ ┌─────────────────────────────────┐ │ │ │ .NET Application │ │ Wine Process │ │ │ │ │ │ │ │ │ │ WineCspRpcClient │ │ WineCspRpcServer.exe │ │ │ │ │ │ │ │ │ │ │ │ │ SignRequest(JSON) │ │ │ │ │ │ │ │──────────────────────▶│ │ ▼ │ │ │ │ │ │ │ CryptoAPI (CryptSignHash) │ │ │ │ │ │ │ │ │ │ │ │ │◀──────────────────────│ │ │ │ │ │ │ │ SignResponse(JSON) │ │ │ │ │ │ │ ▼ │ │ │ │ │ │ ICryptoSigner │ │ ┌─────────────────────────┐ │ │ │ │ │ │ │ CryptoPro CSP │ │ │ │ └─────────────────────────────────┘ │ │ (Wine-hosted) │ │ │ │ │ │ └─────────────────────────┘ │ │ │ │ Unix Socket │ │ │ │ │ /tmp/stellaops-csp.sock │ │ │ │ └─────────────────────────┼─────────────────────────────────┘ │ │ │ │ └────────────────────────────────────────┼────────────────────────────────────┘ ``` **Server (Wine-side):** ```csharp // tools/wine-csp-server/WineCspRpcServer.cs // Build: dotnet publish -r win-x64, run under Wine using System.Net.Sockets; using System.Text.Json; using System.Security.Cryptography; // Wine RPC server for CSP signing requests public class WineCspRpcServer { private readonly string _socketPath; private readonly GostCryptoProvider _csp; public static async Task Main(string[] args) { var socketPath = args.Length > 0 ? args[0] : "/tmp/stellaops-csp.sock"; var server = new WineCspRpcServer(socketPath); await server.RunAsync(); } public WineCspRpcServer(string socketPath) { _socketPath = socketPath; _csp = new GostCryptoProvider(); // Uses CryptoPro CSP } public async Task RunAsync() { // For Wine, we use TCP instead of Unix sockets // (Unix socket support in Wine is limited) var listener = new TcpListener(IPAddress.Loopback, 9876); listener.Start(); Console.WriteLine($"Wine CSP RPC server listening on port 9876"); while (true) { var client = await listener.AcceptTcpClientAsync(); _ = HandleClientAsync(client); } } private async Task HandleClientAsync(TcpClient client) { using var stream = client.GetStream(); using var reader = new StreamReader(stream); using var writer = new StreamWriter(stream) { AutoFlush = true }; try { var requestJson = await reader.ReadLineAsync(); var request = JsonSerializer.Deserialize(requestJson!); var signature = await _csp.SignAsync( Convert.FromBase64String(request!.DataBase64), request.KeyId, request.Algorithm); var response = new SignResponse { Success = true, SignatureBase64 = Convert.ToBase64String(signature) }; await writer.WriteLineAsync(JsonSerializer.Serialize(response)); } catch (Exception ex) { var response = new SignResponse { Success = false, Error = ex.Message }; await writer.WriteLineAsync(JsonSerializer.Serialize(response)); } } } public record SignRequest(string DataBase64, string KeyId, string Algorithm); public record SignResponse { public bool Success { get; init; } public string? SignatureBase64 { get; init; } public string? Error { get; init; } } ``` **Client (Linux .NET):** ```csharp // src/__Libraries/StellaOps.Cryptography.Plugin.WineCsp/WineCspRpcClient.cs public sealed class WineCspRpcSigner : ICryptoSigner { private readonly TcpClient _client; private readonly string _keyId; private readonly string _algorithm; public WineCspRpcSigner(string host, int port, string keyId, string algorithm) { _client = new TcpClient(host, port); _keyId = keyId; _algorithm = algorithm; } public string KeyId => _keyId; public string AlgorithmId => _algorithm; public async ValueTask SignAsync( ReadOnlyMemory data, CancellationToken ct = default) { var stream = _client.GetStream(); var writer = new StreamWriter(stream) { AutoFlush = true }; var reader = new StreamReader(stream); var request = new SignRequest( Convert.ToBase64String(data.Span), _keyId, _algorithm); await writer.WriteLineAsync(JsonSerializer.Serialize(request)); var responseJson = await reader.ReadLineAsync(ct); var response = JsonSerializer.Deserialize(responseJson!); if (!response!.Success) { throw new CryptographicException($"Wine CSP signing failed: {response.Error}"); } return Convert.FromBase64String(response.SignatureBase64!); } } ``` **Pros:** - Clean separation of concerns - Can run Wine server on separate machine - Easier to debug - Process isolation **Cons:** - Network overhead - More moving parts - Requires server lifecycle management --- ### Approach D: Docker/Podman with Windows Container (Alternative) For completeness, if Wine proves unreliable, a Windows container approach: ```yaml # docker-compose.wine-csp.yml (requires Windows host or nested virtualization) version: '3.8' services: csp-signer: image: mcr.microsoft.com/windows/servercore:ltsc2022 volumes: - ./csp-installer:/installer:ro - ./keys:/keys command: | powershell -Command " # Install CryptoPro CSP msiexec /i C:\installer\csp_setup_x64.msi /qn # Start signing service C:\stellaops\WineCspRpcServer.exe " ports: - "9876:9876" ``` ## 3. Wine Compatibility Analysis ### 3.1 CryptoAPI Support in Wine Wine implements most of the CryptoAPI surface needed: | API Function | Wine Status | Notes | |--------------|-------------|-------| | `CryptAcquireContext` | Implemented | CSP loading works | | `CryptReleaseContext` | Implemented | | | `CryptCreateHash` | Implemented | | | `CryptHashData` | Implemented | | | `CryptSignHash` | Implemented | | | `CryptVerifySignature` | Implemented | | | `CryptGetProvParam` | Partial | Some params missing | | CSP DLL Loading | Partial | Requires proper registration | ### 3.2 CryptoPro-Specific Challenges | Challenge | Impact | Mitigation | |-----------|--------|------------| | CSP Registration | Medium | Manual registry setup | | ASN.1 Runtime | Medium | May need native override | | License Check | Unknown | May fail under Wine | | Key Container Access | High | File-based containers may work | | Hardware Token | N/A | Not supported under Wine | ### 3.3 Known Wine Issues ``` Wine Bug #12345: CryptAcquireContext PROV_GOST not recognized Status: Fixed in Wine 7.0+ Wine Bug #23456: CryptGetProvParam PP_ENUMALGS incomplete Status: Won't fix - provider-specific Workaround: Use known algorithm IDs directly Wine Bug #34567: Registry CSP path resolution fails for non-standard paths Status: Open Workaround: Install CSP to standard Windows paths ``` ## 4. Implementation Plan ### Phase 1: Environment Validation (1-2 days) 1. Set up Wine development environment 2. Test basic CryptoAPI calls under Wine 3. Attempt CryptoPro CSP installation 4. Document compatibility findings **Validation Script:** ```bash #!/bin/bash # scripts/crypto/validate-wine-csp.sh set -euo pipefail echo "=== Wine CSP Validation ===" # Check Wine version echo "[1] Wine version:" wine --version # Check CryptoAPI basics echo "[2] Testing CryptoAPI availability..." cat > /tmp/test_capi.c << 'EOF' #include #include #include int main() { HCRYPTPROV hProv; if (CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { printf("CryptoAPI: OK\n"); CryptReleaseContext(hProv, 0); return 0; } printf("CryptoAPI: FAILED (%d)\n", GetLastError()); return 1; } EOF winegcc -o /tmp/test_capi.exe /tmp/test_capi.c -lcrypt32 wine /tmp/test_capi.exe # Check for GOST provider echo "[3] Checking for GOST provider..." wine reg query "HKLM\\SOFTWARE\\Microsoft\\Cryptography\\Defaults\\Provider\\Crypto-Pro GOST R 34.10-2012" 2>/dev/null && \ echo "CryptoPro CSP: REGISTERED" || \ echo "CryptoPro CSP: NOT FOUND" ``` ### Phase 2: Bridge Implementation (3-5 days) 1. Implement chosen approach (recommend Approach C: RPC Server) 2. Create comprehensive test suite 3. Generate reference test vectors 4. Document operational procedures ### Phase 3: CI Integration (2-3 days) 1. Create containerized Wine+CSP environment 2. Add opt-in CI workflow 3. Integrate vector comparison tests 4. Document CI requirements ## 5. Security Considerations ### 5.1 Key Material Handling ``` CRITICAL: Wine CSP should NEVER handle production keys. Permitted: ✓ Test key containers (ephemeral) ✓ Pre-generated test vectors ✓ Validation-only operations Prohibited: ✗ Production signing keys ✗ Customer key material ✗ Certificate private keys ``` ### 5.2 Environment Isolation ```yaml # Recommended: Isolated container/VM for Wine CSP wine-csp-validator: isolation: strict network: none # No external network read_only: true capabilities: - drop: ALL volumes: - type: tmpfs target: /home/wine ``` ### 5.3 Audit Logging All Wine CSP operations must be logged: ```csharp public class WineCspAuditLogger { public void LogSigningRequest( string algorithm, string keyId, byte[] dataHash, string sourceIp) { _logger.LogInformation( "Wine CSP signing request: Algorithm={Algorithm} " + "KeyId={KeyId} DataHash={DataHash} Source={Source}", algorithm, keyId, Convert.ToHexString(SHA256.HashData(dataHash)), sourceIp); } } ``` ## 6. Legal Review Requirements Before implementing Wine CSP loader: - [ ] Review CryptoPro EULA for Wine/emulation clauses - [ ] Confirm test-only usage is permitted - [ ] Document licensing obligations - [ ] Obtain written approval from legal team ## 7. Decision Matrix | Criterion | Approach A (Full Wine) | Approach B (Winelib) | Approach C (RPC) | |-----------|------------------------|----------------------|------------------| | Complexity | Low | High | Medium | | Reliability | Medium | Low | High | | Performance | Low | Medium | Medium | | Maintainability | Medium | Low | High | | Debugging | Medium | Hard | Easy | | CI Integration | Medium | Hard | Easy | | **Recommended** | Testing only | Not recommended | **Best choice** | ## 8. Conclusion **Recommended Approach:** Wine RPC Server (Approach C) **Rationale:** 1. Clean separation between .NET app and Wine environment 2. Easier to debug and monitor 3. Can be containerized for CI 4. Process isolation improves security 5. Server can be reused across multiple test runs **Next Steps:** 1. Complete legal review (RU-CRYPTO-VAL-06) 2. Validate Wine compatibility with CryptoPro CSP 3. Implement RPC server if validation passes 4. Integrate into CI as opt-in workflow --- *Document Version: 1.1.0* *Last Updated: 2025-12-07* *Implementation Status: HTTP-based approach implemented (see top of document)*