Add unit tests for PhpFrameworkSurface and PhpPharScanner
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
Findings Ledger CI / build-test (push) Has been cancelled
Findings Ledger CI / migration-validation (push) Has been cancelled
Manifest Integrity / Validate Schema Integrity (push) Has been cancelled
Manifest Integrity / Validate Contract Documents (push) Has been cancelled
Manifest Integrity / Validate Pack Fixtures (push) Has been cancelled
Manifest Integrity / Audit SHA256SUMS Files (push) Has been cancelled
Manifest Integrity / Verify Merkle Roots (push) Has been cancelled
Findings Ledger CI / generate-manifest (push) Has been cancelled
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
Findings Ledger CI / build-test (push) Has been cancelled
Findings Ledger CI / migration-validation (push) Has been cancelled
Manifest Integrity / Validate Schema Integrity (push) Has been cancelled
Manifest Integrity / Validate Contract Documents (push) Has been cancelled
Manifest Integrity / Validate Pack Fixtures (push) Has been cancelled
Manifest Integrity / Audit SHA256SUMS Files (push) Has been cancelled
Manifest Integrity / Verify Merkle Roots (push) Has been cancelled
Findings Ledger CI / generate-manifest (push) Has been cancelled
- Implement comprehensive tests for PhpFrameworkSurface, covering scenarios such as empty surfaces, presence of routes, controllers, middlewares, CLI commands, cron jobs, and event listeners. - Validate metadata creation for route counts, HTTP methods, protected and public routes, and route patterns. - Introduce tests for PhpPharScanner, including handling of non-existent files, null or empty paths, invalid PHAR files, and minimal PHAR structures. - Ensure correct computation of SHA256 for valid PHAR files and validate the properties of PhpPharArchive, PhpPharEntry, and PhpPharScanResult.
This commit is contained in:
821
docs/security/wine-csp-loader-design.md
Normal file
821
docs/security/wine-csp-loader-design.md
Normal file
@@ -0,0 +1,821 @@
|
||||
# Wine CSP Loader Design · CryptoPro GOST Validation
|
||||
|
||||
**Status:** EXPERIMENTAL / DESIGN
|
||||
**Date:** 2025-12-07
|
||||
**Owners:** Security Guild, DevOps
|
||||
**Related:** RU-CRYPTO-VAL-04, RU-CRYPTO-VAL-05
|
||||
|
||||
## 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.0.0*
|
||||
*Last Updated: 2025-12-07*
|
||||
Reference in New Issue
Block a user