Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
- Added WineCspHttpProvider class to interface with Wine-hosted CryptoPro CSP. - Implemented ICryptoProvider, ICryptoProviderDiagnostics, and IDisposable interfaces. - Introduced WineCspHttpSigner and WineCspHttpHasher for signing and hashing operations. - Created WineCspProviderOptions for configuration settings including service URL and key options. - Developed CryptoProGostSigningService to handle GOST signing operations and key management. - Implemented HTTP service for the Wine CSP with endpoints for signing, verification, and hashing. - Added Swagger documentation for API endpoints. - Included health checks and error handling for service availability. - Established DTOs for request and response models in the service.
864 lines
31 KiB
Markdown
864 lines
31 KiB
Markdown
# 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 <windows.h>
|
|
#include <wincrypt.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
// 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;
|
|
|
|
/// <summary>
|
|
/// P/Invoke bridge to Wine-hosted CryptoPro CSP.
|
|
/// EXPERIMENTAL: For test vector generation only.
|
|
/// </summary>
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Wine-based GOST crypto provider for test vector generation.
|
|
/// </summary>
|
|
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<SignRequest>(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<byte[]> SignAsync(
|
|
ReadOnlyMemory<byte> 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<SignResponse>(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 <windows.h>
|
|
#include <wincrypt.h>
|
|
#include <stdio.h>
|
|
|
|
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)*
|