release orchestrator v1 draft and build fixes

This commit is contained in:
master
2026-01-12 12:24:17 +02:00
parent f3de858c59
commit 9873f80830
1598 changed files with 240385 additions and 5944 deletions

View File

@@ -0,0 +1,342 @@
namespace StellaOps.Cryptography.Plugin.Gost;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Signers;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Math.EC;
using Org.BouncyCastle.Security;
using StellaOps.Plugin.Abstractions;
using StellaOps.Plugin.Abstractions.Capabilities;
using StellaOps.Plugin.Abstractions.Context;
using StellaOps.Plugin.Abstractions.Health;
using StellaOps.Plugin.Abstractions.Lifecycle;
/// <summary>
/// GOST cryptography plugin providing Russian Federal cryptographic standards.
/// Implements GOST R 34.10-2012 (signatures) and GOST R 34.11-2012 (hashes).
/// </summary>
public sealed class GostPlugin : CryptoPluginBase
{
private GostOptions? _options;
private AsymmetricCipherKeyPair? _keyPair;
private readonly SecureRandom _random = new();
/// <inheritdoc />
public override PluginInfo Info => new(
Id: "com.stellaops.crypto.gost",
Name: "GOST Cryptography Provider",
Version: "1.0.0",
Vendor: "Stella Ops",
Description: "Russian GOST R 34.10-2012 and R 34.11-2012 cryptographic algorithms",
LicenseId: "AGPL-3.0-or-later");
/// <inheritdoc />
public override IReadOnlyList<string> SupportedAlgorithms => new[]
{
"GOST-R34.10-2012-256",
"GOST-R34.10-2012-512",
"GOST-R34.11-2012-256",
"GOST-R34.11-2012-512",
"GOST-28147-89"
};
/// <inheritdoc />
protected override Task InitializeCryptoServiceAsync(IPluginContext context, CancellationToken ct)
{
_options = context.Configuration.Bind<GostOptions>() ?? new GostOptions();
// Generate or load key pair if configured
if (!string.IsNullOrEmpty(_options.PrivateKeyBase64))
{
// Load existing key - implementation depends on key format
Context?.Logger.Info("GOST provider initialized with configured key");
}
else if (_options.GenerateKeyOnInit)
{
// Generate new GOST-R34.10-2012-256 key pair
_keyPair = GenerateGost2012KeyPair(_options.KeySize);
Context?.Logger.Info("GOST provider initialized with generated {KeySize}-bit key", _options.KeySize);
}
return Task.CompletedTask;
}
/// <inheritdoc />
public override bool CanHandle(CryptoOperation operation, string algorithm)
{
return algorithm.StartsWith("GOST", StringComparison.OrdinalIgnoreCase) &&
SupportedAlgorithms.Contains(algorithm, StringComparer.OrdinalIgnoreCase);
}
/// <inheritdoc />
public override Task<byte[]> SignAsync(ReadOnlyMemory<byte> data, CryptoSignOptions options, CancellationToken ct)
{
EnsureActive();
ct.ThrowIfCancellationRequested();
if (_keyPair == null)
{
throw new InvalidOperationException("No signing key available. Configure a key or enable GenerateKeyOnInit.");
}
var algorithm = options.Algorithm;
var digestBits = algorithm.Contains("512") ? 512 : 256;
// Create GOST R 34.11-2012 digest
var digest = CreateGost2012Digest(digestBits);
// Create GOST R 34.10-2012 signer
var signer = new ECGost3410Signer();
signer.Init(true, _keyPair.Private);
// Hash the data
digest.BlockUpdate(data.Span.ToArray(), 0, data.Length);
var hash = new byte[digest.GetDigestSize()];
digest.DoFinal(hash, 0);
// Sign the hash
var signature = signer.GenerateSignature(hash);
var sigBytes = EncodeSignature(signature);
Context?.Logger.Debug("Signed {DataLength} bytes with {Algorithm}", data.Length, algorithm);
return Task.FromResult(sigBytes);
}
/// <inheritdoc />
public override Task<bool> VerifyAsync(ReadOnlyMemory<byte> data, ReadOnlyMemory<byte> signature, CryptoVerifyOptions options, CancellationToken ct)
{
EnsureActive();
ct.ThrowIfCancellationRequested();
if (_keyPair == null)
{
throw new InvalidOperationException("No verification key available.");
}
var algorithm = options.Algorithm;
var digestBits = algorithm.Contains("512") ? 512 : 256;
// Create GOST R 34.11-2012 digest
var digest = CreateGost2012Digest(digestBits);
// Create GOST R 34.10-2012 verifier
var verifier = new ECGost3410Signer();
verifier.Init(false, _keyPair.Public);
// Hash the data
digest.BlockUpdate(data.Span.ToArray(), 0, data.Length);
var hash = new byte[digest.GetDigestSize()];
digest.DoFinal(hash, 0);
// Decode and verify signature
var sigComponents = DecodeSignature(signature.ToArray());
var isValid = verifier.VerifySignature(hash, sigComponents[0], sigComponents[1]);
Context?.Logger.Debug("Verified signature: {IsValid}", isValid);
return Task.FromResult(isValid);
}
/// <inheritdoc />
public override Task<byte[]> EncryptAsync(ReadOnlyMemory<byte> data, CryptoEncryptOptions options, CancellationToken ct)
{
EnsureActive();
ct.ThrowIfCancellationRequested();
if (!options.Algorithm.Contains("28147", StringComparison.Ordinal))
{
throw new NotSupportedException($"Encryption algorithm {options.Algorithm} not supported. Use GOST-28147-89.");
}
// GOST 28147-89 block cipher encryption
var engine = new Gost28147Engine();
var keyBytes = GetEncryptionKey(options.KeyId);
engine.Init(true, new KeyParameter(keyBytes));
var encrypted = ProcessBlocks(engine, data.ToArray());
Context?.Logger.Debug("Encrypted {DataLength} bytes with GOST-28147-89", data.Length);
return Task.FromResult(encrypted);
}
/// <inheritdoc />
public override Task<byte[]> DecryptAsync(ReadOnlyMemory<byte> data, CryptoDecryptOptions options, CancellationToken ct)
{
EnsureActive();
ct.ThrowIfCancellationRequested();
// GOST 28147-89 block cipher decryption
var engine = new Gost28147Engine();
var keyBytes = GetEncryptionKey(options.KeyId);
engine.Init(false, new KeyParameter(keyBytes));
var decrypted = ProcessBlocks(engine, data.ToArray());
Context?.Logger.Debug("Decrypted {DataLength} bytes with GOST-28147-89", data.Length);
return Task.FromResult(decrypted);
}
/// <inheritdoc />
public override Task<byte[]> HashAsync(ReadOnlyMemory<byte> data, string algorithm, CancellationToken ct)
{
EnsureActive();
ct.ThrowIfCancellationRequested();
var digestBits = algorithm.Contains("512") ? 512 : 256;
var digest = CreateGost2012Digest(digestBits);
digest.BlockUpdate(data.Span.ToArray(), 0, data.Length);
var hash = new byte[digest.GetDigestSize()];
digest.DoFinal(hash, 0);
Context?.Logger.Debug("Computed {Algorithm} hash of {DataLength} bytes", algorithm, data.Length);
return Task.FromResult(hash);
}
/// <inheritdoc />
public override ValueTask DisposeAsync()
{
_keyPair = null;
State = PluginLifecycleState.Stopped;
return ValueTask.CompletedTask;
}
private static IDigest CreateGost2012Digest(int bits)
{
return bits switch
{
256 => new Gost3411_2012_256Digest(),
512 => new Gost3411_2012_512Digest(),
_ => throw new ArgumentException($"Unsupported digest size: {bits}")
};
}
private AsymmetricCipherKeyPair GenerateGost2012KeyPair(int keySize)
{
// GOST R 34.10-2012 uses specific elliptic curve parameters
// For 256-bit: id-tc26-gost-3410-2012-256-paramSetA
// For 512-bit: id-tc26-gost-3410-2012-512-paramSetA
var generator = new ECKeyPairGenerator("ECGOST3410");
var domainParams = GetGost2012DomainParameters(keySize);
var keyGenParams = new ECKeyGenerationParameters(domainParams, _random);
generator.Init(keyGenParams);
return generator.GenerateKeyPair();
}
private static ECDomainParameters GetGost2012DomainParameters(int keySize)
{
// Simplified: use predefined GOST parameters
// In production, load from OID: 1.2.643.7.1.2.1.1.1 (256-bit) or 1.2.643.7.1.2.1.2.1 (512-bit)
if (keySize == 256)
{
// id-tc26-gost-3410-2012-256-paramSetA
var p = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD97", 16);
var a = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD94", 16);
var b = new BigInteger("00000000000000000000000000000000000000000000000000000000000000A6", 16);
var n = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6C611070995AD10045841B09B761B893", 16);
var h = BigInteger.One;
var gx = new BigInteger("0000000000000000000000000000000000000000000000000000000000000001", 16);
var gy = new BigInteger("8D91E471E0989CDA27DF505A453F2B7635294F2DDF23E3B122ACC99C9E9F1E14", 16);
var curve = new FpCurve(p, a, b, n, h);
var g = curve.CreatePoint(gx, gy);
return new ECDomainParameters(curve, g, n, h);
}
else
{
// id-tc26-gost-3410-2012-512-paramSetA (simplified)
throw new NotImplementedException("512-bit GOST parameters not implemented in this example");
}
}
private static byte[] EncodeSignature(BigInteger[] signature)
{
// Encode r and s as fixed-length byte arrays concatenated
var r = signature[0].ToByteArrayUnsigned();
var s = signature[1].ToByteArrayUnsigned();
// Pad to 32 bytes each for 256-bit
var encoded = new byte[64];
Array.Copy(r, 0, encoded, 32 - r.Length, r.Length);
Array.Copy(s, 0, encoded, 64 - s.Length, s.Length);
return encoded;
}
private static BigInteger[] DecodeSignature(byte[] signature)
{
var r = new BigInteger(1, signature.Take(32).ToArray());
var s = new BigInteger(1, signature.Skip(32).Take(32).ToArray());
return new[] { r, s };
}
private byte[] GetEncryptionKey(string keyId)
{
// In production, retrieve from secure key store
// For now, derive a key from the key ID
var digest = new Gost3411_2012_256Digest();
var keyIdBytes = System.Text.Encoding.UTF8.GetBytes(keyId);
digest.BlockUpdate(keyIdBytes, 0, keyIdBytes.Length);
var key = new byte[32];
digest.DoFinal(key, 0);
return key;
}
private static byte[] ProcessBlocks(IBlockCipher engine, byte[] data)
{
var blockSize = engine.GetBlockSize();
var paddedLength = ((data.Length + blockSize - 1) / blockSize) * blockSize;
var padded = new byte[paddedLength];
Array.Copy(data, padded, data.Length);
var output = new byte[paddedLength];
for (var i = 0; i < paddedLength; i += blockSize)
{
engine.ProcessBlock(padded, i, output, i);
}
return output;
}
}
/// <summary>
/// Configuration options for GOST cryptography plugin.
/// </summary>
public sealed class GostOptions
{
/// <summary>
/// Path to key store file.
/// </summary>
public string? KeyStorePath { get; init; }
/// <summary>
/// Default key identifier for signing operations.
/// </summary>
public string? DefaultKeyId { get; init; }
/// <summary>
/// Base64-encoded private key (if not using key store).
/// </summary>
public string? PrivateKeyBase64 { get; init; }
/// <summary>
/// Generate a new key pair on initialization if no key is configured.
/// </summary>
public bool GenerateKeyOnInit { get; init; } = true;
/// <summary>
/// Key size in bits (256 or 512).
/// </summary>
public int KeySize { get; init; } = 256;
}

View File

@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>preview</LangVersion>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BouncyCastle.Cryptography" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\StellaOps.Cryptography.Plugin\StellaOps.Cryptography.Plugin.csproj" />
</ItemGroup>
<ItemGroup>
<None Include="plugin.yaml" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,44 @@
plugin:
id: com.stellaops.crypto.gost
name: GOST Cryptography Provider
version: 1.0.0
vendor: Stella Ops
description: Russian GOST R 34.10-2012 and R 34.11-2012 cryptographic algorithms
license: AGPL-3.0-or-later
entryPoint: StellaOps.Cryptography.Plugin.Gost.GostPlugin
minPlatformVersion: 1.0.0
capabilities:
- type: crypto
id: gost
algorithms:
- GOST-R34.10-2012-256
- GOST-R34.10-2012-512
- GOST-R34.11-2012-256
- GOST-R34.11-2012-512
- GOST-28147-89
configSchema:
type: object
properties:
keyStorePath:
type: string
description: Path to GOST key store
defaultKeyId:
type: string
description: Default key identifier for signing
privateKeyBase64:
type: string
description: Base64-encoded private key
generateKeyOnInit:
type: boolean
default: true
description: Generate new key pair on initialization if no key configured
keySize:
type: integer
enum: [256, 512]
default: 256
description: Key size in bits
required: []