Add support for ГОСТ Р 34.10 digital signatures

- Implemented the GostKeyValue class for handling public key parameters in ГОСТ Р 34.10 digital signatures.
- Created the GostSignedXml class to manage XML signatures using ГОСТ 34.10, including methods for computing and checking signatures.
- Developed the GostSignedXmlImpl class to encapsulate the signature computation logic and public key retrieval.
- Added specific key value classes for ГОСТ Р 34.10-2001, ГОСТ Р 34.10-2012/256, and ГОСТ Р 34.10-2012/512 to support different signature algorithms.
- Ensured compatibility with existing XML signature standards while integrating ГОСТ cryptography.
This commit is contained in:
master
2025-11-09 21:59:57 +02:00
parent 75c2bcafce
commit cef4cb2c5a
486 changed files with 32952 additions and 801 deletions

View File

@@ -0,0 +1,126 @@
using System;
using System.Security.Cryptography;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Math;
namespace StellaOps.Cryptography;
public static class GostSignatureEncoding
{
public static bool IsDer(ReadOnlySpan<byte> signature)
{
if (signature.Length < 2 || signature[0] != 0x30)
{
return false;
}
var lengthByte = signature[1];
if ((lengthByte & 0x80) == 0)
{
var total = lengthByte + 2;
return total == signature.Length;
}
var lengthBytes = lengthByte & 0x7F;
if (lengthBytes is 0 or > 4 || signature.Length < 2 + lengthBytes)
{
return false;
}
var totalLength = 0;
for (var i = 0; i < lengthBytes; i++)
{
totalLength = (totalLength << 8) | signature[2 + i];
}
return totalLength + 2 + lengthBytes == signature.Length;
}
public static byte[] ToRaw(ReadOnlySpan<byte> der, int coordinateLength)
{
if (!IsDer(der))
{
throw new CryptographicException("Signature is not DER encoded.");
}
var sequence = Asn1Sequence.GetInstance(Asn1Object.FromByteArray(der.ToArray()));
if (sequence.Count != 2)
{
throw new CryptographicException("Invalid DER structure for GOST signature.");
}
var r = NormalizeCoordinate(((DerInteger)sequence[0]).PositiveValue.ToByteArrayUnsigned(), coordinateLength);
var s = NormalizeCoordinate(((DerInteger)sequence[1]).PositiveValue.ToByteArrayUnsigned(), coordinateLength);
var raw = new byte[coordinateLength * 2];
s.CopyTo(raw.AsSpan(0, coordinateLength));
r.CopyTo(raw.AsSpan(coordinateLength));
return raw;
}
public static byte[] ToDer(ReadOnlySpan<byte> raw, int coordinateLength)
{
if (raw.Length != coordinateLength * 2)
{
throw new CryptographicException($"Raw GOST signature must be {coordinateLength * 2} bytes.");
}
var s = raw[..coordinateLength].ToArray();
var r = raw[coordinateLength..].ToArray();
var derSequence = new DerSequence(
new DerInteger(new BigInteger(1, r)),
new DerInteger(new BigInteger(1, s)));
return derSequence.GetDerEncoded();
}
public static (BigInteger r, BigInteger s) DecodeComponents(ReadOnlySpan<byte> signature, int coordinateLength)
{
if (IsDer(signature))
{
var sequence = Asn1Sequence.GetInstance(Asn1Object.FromByteArray(signature.ToArray()));
if (sequence.Count != 2)
{
throw new CryptographicException("Invalid DER structure for GOST signature.");
}
return (((DerInteger)sequence[0]).PositiveValue, ((DerInteger)sequence[1]).PositiveValue);
}
if (signature.Length == coordinateLength * 2)
{
var s = new byte[coordinateLength];
var r = new byte[coordinateLength];
signature[..coordinateLength].CopyTo(s);
signature[coordinateLength..].CopyTo(r);
return (new BigInteger(1, r), new BigInteger(1, s));
}
throw new CryptographicException("Signature payload is neither DER nor raw GOST format.");
}
private static byte[] NormalizeCoordinate(ReadOnlySpan<byte> value, int coordinateLength)
{
var trimmed = TrimLeadingZeros(value);
if (trimmed.Length > coordinateLength)
{
throw new CryptographicException("Coordinate exceeds expected length.");
}
var output = new byte[coordinateLength];
trimmed.CopyTo(output.AsSpan(coordinateLength - trimmed.Length));
return output;
}
private static ReadOnlySpan<byte> TrimLeadingZeros(ReadOnlySpan<byte> value)
{
var index = 0;
while (index < value.Length - 1 && value[index] == 0)
{
index++;
}
return value[index..];
}
}

View File

@@ -0,0 +1,13 @@
namespace StellaOps.Cryptography;
/// <summary>
/// Desired wire format for GOST 34.10 signatures.
/// </summary>
public enum GostSignatureFormat
{
/// <summary>DER-encoded ASN.1 sequence (default).</summary>
Der = 0,
/// <summary>Concatenated (s || r) raw bytes per RFC 9215.</summary>
Raw = 1
}

View File

@@ -1,11 +0,0 @@
# Active Tasks
| ID | Status | Owner | Description | Dependencies | Exit Criteria |
|----|--------|-------|-------------|--------------|---------------|
| SEC-CRYPTO-90-009 | TODO | Security Guild | Replace the placeholder CryptoPro plug-in with a true CryptoPro CSP implementation (GostCryptography driver, X509 store lookups, DER/raw normalization, deterministic logging). | SPRINT_514 | ✅ CryptoPro provider no longer depends on PKCS#11 core; ✅ Certificates resolved via thumbprint/container; ✅ Sign/verify + JWK export exercised in tests/harness. |
| SEC-CRYPTO-90-010 | TODO | Security Guild | Introduce `StellaOpsCryptoOptions` + configuration binding for registry profiles/keys and expose `AddStellaOpsCryptoRu(IConfiguration, …)` so hosts can enable `ru-offline` without handwritten wiring. | SPRINT_514 | ✅ Options bind from `StellaOps:Crypto` (registry, CryptoPro, PKCS#11); ✅ New DI helper registers providers + preferred order; ✅ Sample config (`etc/rootpack/ru/crypto.profile.yaml`) loads without custom code. |
| SEC-CRYPTO-90-011 | TODO | Security & Ops Guilds | Build the sovereign crypto CLI (`StellaOps.CryptoRu.Cli`) to list keys, perform test-sign operations, and emit determinism/audit payloads referenced by RootPack docs. | SPRINT_514 | ✅ CLI project under `src/Tools/`; ✅ `list` & `sign` commands hit provider registry; ✅ README/runbooks updated with usage examples. |
| SEC-CRYPTO-90-012 | TODO | Security Guild | Add CryptoPro + PKCS#11 integration tests (env/pin gated) and wire them into `scripts/crypto/run-rootpack-ru-tests.sh`, covering Streebog vectors and DER/raw signatures. | SPRINT_514 | ✅ New tests skip gracefully when env vars absent; ✅ Test harness logs include RU cases; ✅ CI instructions documented. |
| SEC-CRYPTO-90-013 | TODO | Security Guild | Extend the shared crypto stack with sovereign symmetric algorithms (Magma/Kuznyechik) so exports/data-at-rest can request Russian ciphers via the provider registry. | SPRINT_514 | ✅ Hash/service interfaces support symmetric operations; ✅ CryptoPro/PKCS#11 providers implement Magma/Kuznyechik; ✅ Sample usage documented/tests added. |
| SEC-CRYPTO-90-014 | TODO | Security Guild + Service Guilds | Update runtime hosts (Authority, Scanner WebService/Worker, Concelier, etc.) to register RU providers, bind `StellaOps:Crypto` profiles, and expose operator-facing configuration toggles. | SPRINT_514 | ✅ Each host calls the new DI helper/config binding; ✅ Default configs document `ru-offline`; ✅ Sovereign bundles verified end-to-end. |
| SEC-CRYPTO-90-015 | TODO | Security & Docs Guilds | Refresh RootPack/validation documentation once CLI/config/tests exist (remove TODO callouts, document final workflows). | SPRINT_514 | ✅ TODO sections removed from `rootpack_ru_*` docs; ✅ Final CLI/test steps published; ✅ Release checklist updated. |