Add SBOM, symbols, traces, and VEX files for CVE-2022-21661 SQLi case
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled

- Created CycloneDX and SPDX SBOM files for both reachable and unreachable images.
- Added symbols.json detailing function entry and sink points in the WordPress code.
- Included runtime traces for function calls in both reachable and unreachable scenarios.
- Developed OpenVEX files indicating vulnerability status and justification for both cases.
- Updated README for evaluator harness to guide integration with scanner output.
This commit is contained in:
master
2025-11-08 20:53:45 +02:00
parent 515975edc5
commit 536f6249a6
837 changed files with 37279 additions and 14675 deletions

View File

@@ -0,0 +1,13 @@
namespace StellaOps.Cryptography;
/// <summary>
/// Factory helpers for creating crypto hash implementations outside of dependency injection scenarios.
/// </summary>
public static class CryptoHashFactory
{
/// <summary>
/// Creates the default ICryptoHash implementation.
/// </summary>
public static ICryptoHash CreateDefault(CryptoHashOptions? options = null)
=> new DefaultCryptoHash(options);
}

View File

@@ -0,0 +1,6 @@
namespace StellaOps.Cryptography;
public sealed class CryptoHashOptions
{
public string DefaultAlgorithm { get; set; } = HashAlgorithms.Sha256;
}

View File

@@ -0,0 +1,21 @@
using System.Collections.Generic;
namespace StellaOps.Cryptography;
/// <summary>
/// Describes key material surfaced by crypto providers for diagnostics.
/// </summary>
public sealed record CryptoProviderKeyDescriptor(
string Provider,
string KeyId,
string AlgorithmId,
IReadOnlyDictionary<string, string?> Metadata);
/// <summary>
/// Optional interface for providers that can expose key metadata without revealing private material.
/// </summary>
public interface ICryptoProviderDiagnostics
{
IEnumerable<CryptoProviderKeyDescriptor> DescribeKeys();
}

View File

@@ -0,0 +1,30 @@
using System.Collections.Generic;
using System.Diagnostics.Metrics;
namespace StellaOps.Cryptography;
internal static class CryptoProviderMetrics
{
private static readonly Meter Meter = new("stellaops.crypto", "1.0.0");
private static readonly Counter<long> ProviderResolutionCounter =
Meter.CreateCounter<long>("crypto_provider_resolutions_total", description: "Count of successful provider resolutions.");
private static readonly Counter<long> ProviderResolutionFailureCounter =
Meter.CreateCounter<long>("crypto_provider_resolution_failures_total", description: "Count of failed provider resolutions.");
public static void RecordProviderResolution(string providerName, CryptoCapability capability, string algorithmId)
{
ProviderResolutionCounter.Add(1,
new KeyValuePair<string, object?>("provider", providerName),
new KeyValuePair<string, object?>("capability", capability.ToString()),
new KeyValuePair<string, object?>("algorithm", algorithmId));
}
public static void RecordProviderResolutionFailure(CryptoCapability capability, string algorithmId)
{
ProviderResolutionFailureCounter.Add(1,
new KeyValuePair<string, object?>("capability", capability.ToString()),
new KeyValuePair<string, object?>("algorithm", algorithmId));
}
}

View File

@@ -64,10 +64,12 @@ public sealed class CryptoProviderRegistry : ICryptoProviderRegistry
{
if (provider.Supports(capability, algorithmId))
{
CryptoProviderMetrics.RecordProviderResolution(provider.Name, capability, algorithmId);
return provider;
}
}
CryptoProviderMetrics.RecordProviderResolutionFailure(capability, algorithmId);
throw new InvalidOperationException(
$"No crypto provider is registered for capability '{capability}' and algorithm '{algorithmId}'.");
}
@@ -88,11 +90,13 @@ public sealed class CryptoProviderRegistry : ICryptoProviderRegistry
}
var signer = hinted.GetSigner(algorithmId, keyReference);
CryptoProviderMetrics.RecordProviderResolution(hinted.Name, capability, algorithmId);
return new CryptoSignerResolution(signer, hinted.Name);
}
var provider = ResolveOrThrow(capability, algorithmId);
var resolved = provider.GetSigner(algorithmId, keyReference);
CryptoProviderMetrics.RecordProviderResolution(provider.Name, capability, algorithmId);
return new CryptoSignerResolution(resolved, provider.Name);
}

View File

@@ -0,0 +1,169 @@
using System;
using System.Buffers;
using System.IO;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Org.BouncyCastle.Crypto;
namespace StellaOps.Cryptography;
public sealed class DefaultCryptoHash : ICryptoHash
{
private readonly IOptionsMonitor<CryptoHashOptions> options;
private readonly ILogger<DefaultCryptoHash> logger;
[ActivatorUtilitiesConstructor]
public DefaultCryptoHash(
IOptionsMonitor<CryptoHashOptions> options,
ILogger<DefaultCryptoHash>? logger = null)
{
this.options = options ?? throw new ArgumentNullException(nameof(options));
this.logger = logger ?? NullLogger<DefaultCryptoHash>.Instance;
}
internal DefaultCryptoHash(CryptoHashOptions? options = null)
: this(new StaticOptionsMonitor(options ?? new CryptoHashOptions()), NullLogger<DefaultCryptoHash>.Instance)
{
}
public byte[] ComputeHash(ReadOnlySpan<byte> data, string? algorithmId = null)
{
var algorithm = NormalizeAlgorithm(algorithmId);
return algorithm switch
{
HashAlgorithms.Sha256 => ComputeSha256(data),
HashAlgorithms.Sha512 => ComputeSha512(data),
HashAlgorithms.Gost3411_2012_256 => GostDigestUtilities.ComputeDigest(data, use256: true),
HashAlgorithms.Gost3411_2012_512 => GostDigestUtilities.ComputeDigest(data, use256: false),
_ => throw new InvalidOperationException($"Unsupported hash algorithm {algorithm}.")
};
}
public string ComputeHashHex(ReadOnlySpan<byte> data, string? algorithmId = null)
=> Convert.ToHexString(ComputeHash(data, algorithmId)).ToLowerInvariant();
public string ComputeHashBase64(ReadOnlySpan<byte> data, string? algorithmId = null)
=> Convert.ToBase64String(ComputeHash(data, algorithmId));
public async ValueTask<byte[]> ComputeHashAsync(Stream stream, string? algorithmId = null, CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(stream);
cancellationToken.ThrowIfCancellationRequested();
var algorithm = NormalizeAlgorithm(algorithmId);
return algorithm switch
{
HashAlgorithms.Sha256 => await ComputeShaStreamAsync(HashAlgorithmName.SHA256, stream, cancellationToken).ConfigureAwait(false),
HashAlgorithms.Sha512 => await ComputeShaStreamAsync(HashAlgorithmName.SHA512, stream, cancellationToken).ConfigureAwait(false),
HashAlgorithms.Gost3411_2012_256 => await ComputeGostStreamAsync(use256: true, stream, cancellationToken).ConfigureAwait(false),
HashAlgorithms.Gost3411_2012_512 => await ComputeGostStreamAsync(use256: false, stream, cancellationToken).ConfigureAwait(false),
_ => throw new InvalidOperationException($"Unsupported hash algorithm {algorithm}.")
};
}
public async ValueTask<string> ComputeHashHexAsync(Stream stream, string? algorithmId = null, CancellationToken cancellationToken = default)
{
var bytes = await ComputeHashAsync(stream, algorithmId, cancellationToken).ConfigureAwait(false);
return Convert.ToHexString(bytes).ToLowerInvariant();
}
private static byte[] ComputeSha256(ReadOnlySpan<byte> data)
{
Span<byte> buffer = stackalloc byte[32];
SHA256.HashData(data, buffer);
return buffer.ToArray();
}
private static byte[] ComputeSha512(ReadOnlySpan<byte> data)
{
Span<byte> buffer = stackalloc byte[64];
SHA512.HashData(data, buffer);
return buffer.ToArray();
}
private static async ValueTask<byte[]> ComputeShaStreamAsync(HashAlgorithmName name, Stream stream, CancellationToken cancellationToken)
{
using var incremental = IncrementalHash.CreateHash(name);
var buffer = ArrayPool<byte>.Shared.Rent(128 * 1024);
try
{
int bytesRead;
while ((bytesRead = await stream.ReadAsync(buffer.AsMemory(0, buffer.Length), cancellationToken).ConfigureAwait(false)) > 0)
{
incremental.AppendData(buffer, 0, bytesRead);
}
return incremental.GetHashAndReset();
}
finally
{
ArrayPool<byte>.Shared.Return(buffer);
}
}
private static async ValueTask<byte[]> ComputeGostStreamAsync(bool use256, Stream stream, CancellationToken cancellationToken)
{
var digest = GostDigestUtilities.CreateDigest(use256);
var buffer = ArrayPool<byte>.Shared.Rent(128 * 1024);
try
{
int bytesRead;
while ((bytesRead = await stream.ReadAsync(buffer.AsMemory(0, buffer.Length), cancellationToken).ConfigureAwait(false)) > 0)
{
digest.BlockUpdate(buffer, 0, bytesRead);
}
var output = new byte[digest.GetDigestSize()];
digest.DoFinal(output, 0);
return output;
}
finally
{
ArrayPool<byte>.Shared.Return(buffer);
}
}
private string NormalizeAlgorithm(string? algorithmId)
{
var defaultAlgorithm = options.CurrentValue?.DefaultAlgorithm;
if (!string.IsNullOrWhiteSpace(algorithmId))
{
return algorithmId.Trim().ToUpperInvariant();
}
if (!string.IsNullOrWhiteSpace(defaultAlgorithm))
{
return defaultAlgorithm.Trim().ToUpperInvariant();
}
return HashAlgorithms.Sha256;
}
private sealed class StaticOptionsMonitor : IOptionsMonitor<CryptoHashOptions>
{
private readonly CryptoHashOptions options;
public StaticOptionsMonitor(CryptoHashOptions options)
=> this.options = options;
public CryptoHashOptions CurrentValue => options;
public CryptoHashOptions Get(string? name) => options;
public IDisposable OnChange(Action<CryptoHashOptions, string> listener)
=> NullDisposable.Instance;
private sealed class NullDisposable : IDisposable
{
public static readonly NullDisposable Instance = new();
public void Dispose()
{
}
}
}
}

View File

@@ -9,8 +9,8 @@ namespace StellaOps.Cryptography;
/// <summary>
/// Default in-process crypto provider exposing password hashing capabilities.
/// </summary>
public sealed class DefaultCryptoProvider : ICryptoProvider
{
public sealed class DefaultCryptoProvider : ICryptoProvider, ICryptoProviderDiagnostics
{
private readonly ConcurrentDictionary<string, IPasswordHasher> passwordHashers;
private readonly ConcurrentDictionary<string, CryptoSigningKey> signingKeys;
private static readonly HashSet<string> SupportedSigningAlgorithms = new(StringComparer.OrdinalIgnoreCase)
@@ -105,8 +105,38 @@ public sealed class DefaultCryptoProvider : ICryptoProvider
return signingKeys.TryRemove(keyId, out _);
}
public IReadOnlyCollection<CryptoSigningKey> GetSigningKeys()
=> signingKeys.Values.ToArray();
public IReadOnlyCollection<CryptoSigningKey> GetSigningKeys()
=> signingKeys.Values.ToArray();
public IEnumerable<CryptoProviderKeyDescriptor> DescribeKeys()
{
foreach (var key in signingKeys.Values)
{
var metadata = new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase)
{
["kind"] = key.Kind.ToString(),
["createdAt"] = key.CreatedAt.UtcDateTime.ToString("O"),
["providerHint"] = key.Reference.ProviderHint,
["provider"] = Name
};
if (key.ExpiresAt.HasValue)
{
metadata["expiresAt"] = key.ExpiresAt.Value.UtcDateTime.ToString("O");
}
foreach (var pair in key.Metadata)
{
metadata[$"meta.{pair.Key}"] = pair.Value;
}
yield return new CryptoProviderKeyDescriptor(
Name,
key.Reference.KeyId,
key.AlgorithmId,
metadata);
}
}
private static void EnsureSigningSupported(string algorithmId)
{

View File

@@ -0,0 +1,24 @@
using System;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Digests;
namespace StellaOps.Cryptography;
public static class GostDigestUtilities
{
public static byte[] ComputeDigest(ReadOnlySpan<byte> data, bool use256)
{
IDigest digest = CreateDigestInstance(use256);
var buffer = data.ToArray();
digest.BlockUpdate(buffer, 0, buffer.Length);
var output = new byte[digest.GetDigestSize()];
digest.DoFinal(output, 0);
return output;
}
public static IDigest CreateDigest(bool use256)
=> CreateDigestInstance(use256);
private static IDigest CreateDigestInstance(bool use256)
=> use256 ? new Gost3411_2012_256Digest() : new Gost3411_2012_512Digest();
}

View File

@@ -0,0 +1,12 @@
namespace StellaOps.Cryptography;
/// <summary>
/// Well-known digest algorithm identifiers supported by <see cref="ICryptoHash"/>.
/// </summary>
public static class HashAlgorithms
{
public const string Sha256 = "SHA256";
public const string Sha512 = "SHA512";
public const string Gost3411_2012_256 = "GOST3411-2012-256";
public const string Gost3411_2012_512 = "GOST3411-2012-512";
}

View File

@@ -0,0 +1,19 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace StellaOps.Cryptography;
public interface ICryptoHash
{
byte[] ComputeHash(ReadOnlySpan<byte> data, string? algorithmId = null);
string ComputeHashHex(ReadOnlySpan<byte> data, string? algorithmId = null);
string ComputeHashBase64(ReadOnlySpan<byte> data, string? algorithmId = null);
ValueTask<byte[]> ComputeHashAsync(Stream stream, string? algorithmId = null, CancellationToken cancellationToken = default);
ValueTask<string> ComputeHashHexAsync(Stream stream, string? algorithmId = null, CancellationToken cancellationToken = default);
}

View File

@@ -1,13 +1,16 @@
namespace StellaOps.Cryptography;
/// <summary>
/// Known signature algorithm identifiers.
/// </summary>
public static class SignatureAlgorithms
{
public const string Es256 = "ES256";
public const string Es384 = "ES384";
public const string Es512 = "ES512";
public const string Ed25519 = "ED25519";
public const string EdDsa = "EdDSA";
}
namespace StellaOps.Cryptography;
/// <summary>
/// Known signature algorithm identifiers.
/// </summary>
public static class SignatureAlgorithms
{
public const string Es256 = "ES256";
public const string Es384 = "ES384";
public const string Es512 = "ES512";
public const string Ed25519 = "ED25519";
public const string EdDsa = "EdDSA";
public const string GostR3410_2012_256 = "GOST12-256";
public const string GostR3410_2012_512 = "GOST12-512";
}

View File

@@ -9,8 +9,10 @@
<PropertyGroup Condition="'$(StellaOpsCryptoSodium)' == 'true'">
<DefineConstants>$(DefineConstants);STELLAOPS_CRYPTO_SODIUM</DefineConstants>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Konscious.Security.Cryptography.Argon2" Version="1.3.1" />
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.14.0" />
</ItemGroup>
</Project>
<ItemGroup>
<PackageReference Include="Konscious.Security.Cryptography.Argon2" Version="1.3.1" />
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.14.0" />
<PackageReference Include="BouncyCastle.Cryptography" Version="2.5.1" />
<PackageReference Include="Microsoft.Extensions.Options" Version="10.0.0-rc.2.25502.107" />
</ItemGroup>
</Project>

View File

@@ -2,6 +2,14 @@
| ID | Status | Owner | Description | Dependencies | Exit Criteria |
|----|--------|-------|-------------|--------------|---------------|
| SEC-CRYPTO-90-001 | DONE (2025-11-07) | Security Guild | Produce RootPack_RU sovereign crypto implementation plan, identify provider strategy (CryptoPro + PKCS#11), and slot work into Sprint 190 with task breakdown. | None | Plan captured in `SPRINT_190_ops_offline.md` + this board; risks/assumptions logged. |
| SEC-CRYPTO-90-002 | DONE (2025-11-07) | Security Guild | Extend signature/catalog constants and configuration schema to recognize `GOST12-256/512`, regional crypto profiles, and provider preference ordering. | SEC-CRYPTO-90-001 | New alg IDs wired, configs validated, docs updated. |
| SEC-CRYPTO-90-003 | DONE (2025-11-07) | Security Guild | Implement `StellaOps.Cryptography.Plugin.CryptoPro` provider (sign/verify/JWK export) using CryptoPro CSP/GostCryptography with deterministic logging + tests. | SEC-CRYPTO-90-002 | Provider registered, unit/integration tests (skippable if CSP absent) passing. |
| SEC-CRYPTO-90-004 | DONE (2025-11-07) | Security Guild | Implement `StellaOps.Cryptography.Plugin.Pkcs11Gost` provider (Rutoken/JaCarta) via Pkcs11Interop, configurable slot/pin/module management, and disposal safeguards. | SEC-CRYPTO-90-002 | Provider registered, token smoke tests + error handling documented. |
| SEC-CRYPTO-90-005 | DONE (2025-11-08) | Security Guild | Add configuration-driven provider selection (`crypto.regionalProfiles`), CLI/diagnostic verb to list providers/keys, and deterministic telemetry for usage. | SEC-CRYPTO-90-003, SEC-CRYPTO-90-004 | CLI lists providers, configs switch ordering without code changes, telemetry events emitted. |
| SEC-CRYPTO-90-006 | DONE (2025-11-08) | Security Guild | Build deterministic test harness (Streebog + signature vectors), manual runbooks for hardware validation, and capture RootPack audit metadata. | SEC-CRYPTO-90-003, SEC-CRYPTO-90-004 | `scripts/crypto/run-rootpack-ru-tests.sh` emits deterministic logs/TRX; validation runbook updated with harness + hardware guidance; audit metadata artifacts enumerated. |
| SEC-CRYPTO-90-007 | DONE (2025-11-08) | Security Guild | Package RootPack_RU artifacts (plugin binaries, config templates, trust anchors) and document deployment/install steps + compliance evidence. | SEC-CRYPTO-90-005, SEC-CRYPTO-90-006 | `scripts/crypto/package-rootpack-ru.sh` builds bundle with docs/config/trust anchors; `rootpack_ru_package.md` guides ops/air-gap workflows. |
| SEC-CRYPTO-90-008 | DONE (2025-11-08) | Security Guild | Audit repository for any cryptography usage bypassing `StellaOps.Cryptography` (direct libsodium/BouncyCastle callers, TLS custom code) and file remediation tasks to route via providers. | SEC-CRYPTO-90-002 | Audit report updated with remediation IDs; module TASKS boards now include `*-CRYPTO-90-001` follow-ups; backlog ready for implementation. |
> Remark (2025-10-14): Cleanup service wired to store; background sweep + invite audit tests added.
> Remark (2025-10-14): Token usage metadata persisted with replay audits + handler/unit coverage.
> Remark (2025-10-14): Analyzer surfaces warnings during CLI load; docs updated with mitigation steps.