Fix build and code structure improvements. New but essential UI functionality. CI improvements. Documentation improvements. AI module improvements.

This commit is contained in:
StellaOps Bot
2025-12-26 21:54:17 +02:00
parent 335ff7da16
commit c2b9cd8d1f
3717 changed files with 264714 additions and 48202 deletions

View File

@@ -8,6 +8,48 @@ Assemble deterministic SBOM artifacts (inventory, usage, BOM index) from analyze
- Generate BOM index sidecars with roaring bitmap acceleration and usage flags.
- Package artifacts with stable naming, hashing, and manifests for downstream storage and attestations.
- Surface helper APIs for Scanner Worker/WebService to request compositions and exports.
- **CBOM Support**: Generate CycloneDX 1.7 Cryptographic BOM with `cryptographicProperties` for crypto asset inventory.
## CBOM (Cryptographic BOM) Support
The Emit module supports CycloneDX 1.7 CBOM generation for cryptographic asset inventory:
### Key Components
| Component | Path | Purpose |
|-----------|------|---------|
| `ICryptoAssetExtractor` | `Cbom/ICryptoAssetExtractor.cs` | Interface for language-specific crypto extraction |
| `CryptoProperties` | `Cbom/CryptoProperties.cs` | CycloneDX 1.7 crypto schema types |
| `CbomAggregationService` | `Cbom/CbomAggregationService.cs` | Aggregates crypto assets with risk assessment |
| `CycloneDxCbomWriter` | `Composition/CycloneDxCbomWriter.cs` | Injects cryptographicProperties into CycloneDX JSON |
### Crypto Extractors
Language-specific extractors implement `ICryptoAssetExtractor`:
- `DotNetCryptoExtractor`: System.Security.Cryptography patterns
- `JavaCryptoExtractor`: BouncyCastle, JWT libraries, JCA patterns
- `NodeCryptoExtractor`: npm crypto packages (bcrypt, crypto-js, sodium, etc.)
### Usage Pattern
```csharp
// 1. Aggregate crypto assets from components
var cbomService = new CbomAggregationService(extractors, logger);
var cbomResult = await cbomService.AggregateAsync(components, context);
// 2. Inject into CycloneDX output
var enhancedJson = CycloneDxCbomWriter.InjectCbom(
cycloneDxJson,
cbomResult.ByComponent);
```
### Risk Assessment
The aggregation service automatically assesses crypto risk:
- **Deprecated**: MD5, SHA-1, DES, RC2, RC4
- **Weak**: Small key sizes, ECB mode, unauthenticated encryption
- **Quantum Vulnerable**: RSA, DSA, ECDSA, ECDH, DH
- **Post-Quantum Ready**: ML-KEM, ML-DSA, SLH-DSA, SPHINCS+
## Interfaces & Dependencies
- Consumes analyzer outputs (OS, language, native) and EntryTrace usage annotations.

View File

@@ -0,0 +1,364 @@
using System.Collections.Immutable;
using Microsoft.Extensions.Logging;
using StellaOps.Scanner.Core.Contracts;
namespace StellaOps.Scanner.Emit.Cbom;
/// <summary>
/// Service for aggregating crypto assets from all extractors into a unified CBOM.
/// </summary>
public interface ICbomAggregationService
{
/// <summary>
/// Aggregates crypto assets from all components.
/// </summary>
Task<CbomAggregationResult> AggregateAsync(
ImmutableArray<AggregatedComponent> components,
CryptoAnalysisContext context,
CancellationToken cancellationToken = default);
/// <summary>
/// Computes risk assessment for crypto assets.
/// </summary>
CryptoRiskAssessment AssessRisk(ImmutableArray<CryptoAsset> assets);
}
/// <summary>
/// Result of CBOM aggregation.
/// </summary>
public sealed record CbomAggregationResult
{
/// <summary>All discovered crypto assets.</summary>
public required ImmutableArray<CryptoAsset> Assets { get; init; }
/// <summary>Assets grouped by component.</summary>
public required ImmutableDictionary<string, ImmutableArray<CryptoAsset>> ByComponent { get; init; }
/// <summary>Unique algorithms discovered.</summary>
public required ImmutableArray<string> UniqueAlgorithms { get; init; }
/// <summary>Risk assessment summary.</summary>
public CryptoRiskAssessment? RiskAssessment { get; init; }
/// <summary>Timestamp of aggregation (UTC ISO 8601).</summary>
public required string GeneratedAt { get; init; }
}
/// <summary>
/// Crypto risk assessment for the entire CBOM.
/// </summary>
public sealed record CryptoRiskAssessment
{
/// <summary>Overall risk score (0-100).</summary>
public double OverallScore { get; init; }
/// <summary>Count of critical risk items.</summary>
public int CriticalCount { get; init; }
/// <summary>Count of high risk items.</summary>
public int HighCount { get; init; }
/// <summary>Count of medium risk items.</summary>
public int MediumCount { get; init; }
/// <summary>Count of low risk items.</summary>
public int LowCount { get; init; }
/// <summary>Deprecated algorithms found.</summary>
public ImmutableArray<string> DeprecatedAlgorithms { get; init; } = ImmutableArray<string>.Empty;
/// <summary>Weak algorithms found.</summary>
public ImmutableArray<string> WeakAlgorithms { get; init; } = ImmutableArray<string>.Empty;
/// <summary>Quantum-vulnerable algorithms found.</summary>
public ImmutableArray<string> QuantumVulnerableAlgorithms { get; init; } = ImmutableArray<string>.Empty;
/// <summary>Post-quantum ready algorithms found.</summary>
public ImmutableArray<string> PostQuantumAlgorithms { get; init; } = ImmutableArray<string>.Empty;
/// <summary>Migration recommendations.</summary>
public ImmutableArray<CryptoMigrationRecommendation> MigrationRecommendations { get; init; } = ImmutableArray<CryptoMigrationRecommendation>.Empty;
}
/// <summary>
/// Migration recommendation for crypto modernization.
/// </summary>
public sealed record CryptoMigrationRecommendation
{
/// <summary>Current algorithm/protocol.</summary>
public required string From { get; init; }
/// <summary>Recommended replacement.</summary>
public required string To { get; init; }
/// <summary>Priority level.</summary>
public required CryptoRiskSeverity Priority { get; init; }
/// <summary>Reason for migration.</summary>
public required string Reason { get; init; }
}
/// <summary>
/// Default implementation of CBOM aggregation service.
/// </summary>
public sealed class CbomAggregationService : ICbomAggregationService
{
private readonly IEnumerable<ICryptoAssetExtractor> _extractors;
private readonly ILogger<CbomAggregationService> _logger;
public CbomAggregationService(
IEnumerable<ICryptoAssetExtractor> extractors,
ILogger<CbomAggregationService> logger)
{
_extractors = extractors;
_logger = logger;
}
public async Task<CbomAggregationResult> AggregateAsync(
ImmutableArray<AggregatedComponent> components,
CryptoAnalysisContext context,
CancellationToken cancellationToken = default)
{
var allAssets = new List<CryptoAsset>();
var byComponent = new Dictionary<string, List<CryptoAsset>>();
foreach (var component in components)
{
var componentAssets = new List<CryptoAsset>();
foreach (var extractor in _extractors)
{
try
{
var assets = await extractor.ExtractAsync(component, context, cancellationToken);
componentAssets.AddRange(assets);
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Crypto extraction failed for {Component} using {Extractor}",
component.Identity.Key, extractor.GetType().Name);
}
}
if (componentAssets.Count > 0)
{
allAssets.AddRange(componentAssets);
byComponent[component.Identity.Key] = componentAssets;
}
}
var assetsArray = allAssets.ToImmutableArray();
var uniqueAlgorithms = assetsArray
.Where(a => !string.IsNullOrEmpty(a.AlgorithmName))
.Select(a => a.AlgorithmName!)
.Distinct(StringComparer.OrdinalIgnoreCase)
.OrderBy(s => s, StringComparer.Ordinal)
.ToImmutableArray();
var byComponentImmutable = byComponent
.ToImmutableDictionary(
kv => kv.Key,
kv => kv.Value.ToImmutableArray(),
StringComparer.Ordinal);
return new CbomAggregationResult
{
Assets = assetsArray,
ByComponent = byComponentImmutable,
UniqueAlgorithms = uniqueAlgorithms,
RiskAssessment = AssessRisk(assetsArray),
GeneratedAt = DateTimeOffset.UtcNow.ToString("o")
};
}
public CryptoRiskAssessment AssessRisk(ImmutableArray<CryptoAsset> assets)
{
var deprecated = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
var weak = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
var quantumVulnerable = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
var postQuantum = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
var recommendations = new List<CryptoMigrationRecommendation>();
int criticalCount = 0, highCount = 0, mediumCount = 0, lowCount = 0;
foreach (var asset in assets)
{
var alg = asset.AlgorithmName?.ToUpperInvariant() ?? string.Empty;
// Check for deprecated algorithms
if (IsDeprecatedAlgorithm(alg))
{
deprecated.Add(asset.AlgorithmName ?? alg);
criticalCount++;
AddMigrationRecommendation(recommendations, asset.AlgorithmName ?? alg, CryptoRiskSeverity.Critical);
}
// Check for weak algorithms
else if (IsWeakAlgorithm(alg, asset.KeySizeBits))
{
weak.Add(asset.AlgorithmName ?? alg);
highCount++;
AddMigrationRecommendation(recommendations, asset.AlgorithmName ?? alg, CryptoRiskSeverity.High);
}
// Check quantum vulnerability
else if (IsQuantumVulnerable(alg, asset.Primitive))
{
quantumVulnerable.Add(asset.AlgorithmName ?? alg);
mediumCount++;
}
// Check post-quantum readiness
else if (IsPostQuantumReady(alg, asset.Primitive))
{
postQuantum.Add(asset.AlgorithmName ?? alg);
}
else
{
lowCount++;
}
// Count existing risk flags
foreach (var flag in asset.RiskFlags)
{
switch (flag.Severity)
{
case CryptoRiskSeverity.Critical: criticalCount++; break;
case CryptoRiskSeverity.High: highCount++; break;
case CryptoRiskSeverity.Medium: mediumCount++; break;
case CryptoRiskSeverity.Low: lowCount++; break;
}
}
}
// Compute overall score (0 = best, 100 = worst)
double overallScore = 0;
overallScore += criticalCount * 25;
overallScore += highCount * 10;
overallScore += mediumCount * 5;
overallScore += lowCount * 1;
overallScore = Math.Min(100, overallScore);
return new CryptoRiskAssessment
{
OverallScore = overallScore,
CriticalCount = criticalCount,
HighCount = highCount,
MediumCount = mediumCount,
LowCount = lowCount,
DeprecatedAlgorithms = deprecated.Order().ToImmutableArray(),
WeakAlgorithms = weak.Order().ToImmutableArray(),
QuantumVulnerableAlgorithms = quantumVulnerable.Order().ToImmutableArray(),
PostQuantumAlgorithms = postQuantum.Order().ToImmutableArray(),
MigrationRecommendations = recommendations.ToImmutableArray()
};
}
private static bool IsDeprecatedAlgorithm(string alg)
{
return alg switch
{
"MD5" or "MD4" or "MD2" => true,
"SHA1" or "SHA-1" => true,
"DES" or "3DES" or "TRIPLEDES" or "TRIPLE-DES" => true,
"RC2" or "RC4" => true,
"BLOWFISH" => true,
_ when alg.Contains("MD5") => true,
_ when alg.Contains("SHA1") || alg.Contains("SHA-1") => true,
_ when alg.Contains("DES") && !alg.Contains("AES") => true,
_ => false
};
}
private static bool IsWeakAlgorithm(string alg, int? keySize)
{
// RSA with key size < 2048
if ((alg.Contains("RSA") || alg == "RSA") && keySize.HasValue && keySize.Value < 2048)
return true;
// AES with key size < 128
if ((alg.Contains("AES") || alg == "AES") && keySize.HasValue && keySize.Value < 128)
return true;
// ECDSA/ECDH with curve < 256 bits
if ((alg.Contains("ECD") || alg.Contains("EC-")) && keySize.HasValue && keySize.Value < 256)
return true;
return false;
}
private static bool IsQuantumVulnerable(string alg, CryptoPrimitive? primitive)
{
// All asymmetric algorithms based on factoring or discrete log are quantum-vulnerable
if (primitive is CryptoPrimitive.Rsa or CryptoPrimitive.Dlog or CryptoPrimitive.Ec)
return true;
return alg switch
{
"RSA" or "DSA" or "ECDSA" or "ECDH" or "DH" or "ECDHE" or "DHE" => true,
_ when alg.Contains("RSA") => true,
_ when alg.Contains("ECDSA") || alg.Contains("ECDH") => true,
_ when alg.Contains("DSA") && !alg.Contains("ML-DSA") => true,
_ => false
};
}
private static bool IsPostQuantumReady(string alg, CryptoPrimitive? primitive)
{
if (primitive is CryptoPrimitive.Lattice)
return true;
// NIST post-quantum standards
return alg switch
{
"ML-KEM" or "ML-DSA" or "SLH-DSA" or "FALCON" => true,
"KYBER" or "DILITHIUM" or "SPHINCS+" => true,
_ when alg.Contains("ML-KEM") || alg.Contains("ML-DSA") => true,
_ when alg.Contains("KYBER") || alg.Contains("DILITHIUM") => true,
_ => false
};
}
private static void AddMigrationRecommendation(
List<CryptoMigrationRecommendation> recommendations,
string fromAlg,
CryptoRiskSeverity severity)
{
var algUpper = fromAlg.ToUpperInvariant();
string? toAlg = null;
string? reason = null;
if (algUpper.Contains("MD5") || algUpper.Contains("SHA1") || algUpper.Contains("SHA-1"))
{
toAlg = "SHA-256 or SHA-3";
reason = "Algorithm is cryptographically broken and should not be used for security purposes";
}
else if (algUpper.Contains("DES") && !algUpper.Contains("AES"))
{
toAlg = "AES-256-GCM";
reason = "DES and 3DES are deprecated due to small key/block size";
}
else if (algUpper.Contains("RC2") || algUpper.Contains("RC4"))
{
toAlg = "AES-256-GCM or ChaCha20-Poly1305";
reason = "RC2/RC4 are deprecated due to known weaknesses";
}
else if (algUpper.Contains("RSA") && !algUpper.Contains("2048") && !algUpper.Contains("4096"))
{
toAlg = "RSA-2048+ or ECDSA P-256+";
reason = "RSA key size should be at least 2048 bits";
}
if (toAlg != null && reason != null)
{
// Avoid duplicates
if (!recommendations.Any(r => r.From.Equals(fromAlg, StringComparison.OrdinalIgnoreCase)))
{
recommendations.Add(new CryptoMigrationRecommendation
{
From = fromAlg,
To = toAlg,
Priority = severity,
Reason = reason
});
}
}
}
}

View File

@@ -0,0 +1,373 @@
using System.Collections.Immutable;
using System.Text.Json;
using System.Text.Json.Nodes;
namespace StellaOps.Scanner.Emit.Cbom;
/// <summary>
/// Serializer for CycloneDX 1.7 CBOM (Cryptographic Bill of Materials) extension.
/// Injects cryptographicProperties into CycloneDX JSON output.
/// </summary>
public static class CbomSerializer
{
private static readonly JsonSerializerOptions SerializerOptions = new()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull,
WriteIndented = false
};
/// <summary>
/// Injects CBOM cryptographicProperties into a CycloneDX 1.7 JSON string.
/// </summary>
/// <param name="cycloneDxJson">The CycloneDX JSON (v1.7).</param>
/// <param name="cbomResult">The CBOM aggregation result with crypto assets by component.</param>
/// <returns>Enhanced CycloneDX JSON with cryptographicProperties.</returns>
public static string InjectCbom(string cycloneDxJson, CbomAggregationResult cbomResult)
{
ArgumentNullException.ThrowIfNull(cycloneDxJson);
ArgumentNullException.ThrowIfNull(cbomResult);
if (cbomResult.Assets.IsDefaultOrEmpty || cbomResult.Assets.Length == 0)
{
return cycloneDxJson;
}
var root = JsonNode.Parse(cycloneDxJson);
if (root is not JsonObject rootObj)
{
return cycloneDxJson;
}
var components = rootObj["components"] as JsonArray;
if (components is null || components.Count == 0)
{
return cycloneDxJson;
}
// Index crypto assets by component key (bom-ref)
var assetsByComponent = cbomResult.ByComponent;
foreach (var componentNode in components)
{
if (componentNode is not JsonObject componentObj)
{
continue;
}
var bomRef = componentObj["bom-ref"]?.GetValue<string>();
if (string.IsNullOrEmpty(bomRef))
{
continue;
}
if (!assetsByComponent.TryGetValue(bomRef, out var cryptoAssets) || cryptoAssets.IsDefaultOrEmpty)
{
continue;
}
// Convert crypto assets to CycloneDX cryptographicProperties format
var cryptoPropsArray = new JsonArray();
foreach (var asset in cryptoAssets)
{
var cryptoProp = SerializeCryptoAsset(asset);
if (cryptoProp is not null)
{
cryptoPropsArray.Add(cryptoProp);
}
}
if (cryptoPropsArray.Count > 0)
{
componentObj["cryptographicProperties"] = cryptoPropsArray;
}
}
// Add CBOM metadata properties
AddCbomMetadata(rootObj, cbomResult);
return rootObj.ToJsonString(SerializerOptions);
}
/// <summary>
/// Serializes a CryptoAsset to CycloneDX 1.7 cryptographicProperties format.
/// </summary>
private static JsonObject? SerializeCryptoAsset(CryptoAsset asset)
{
var cryptoProp = new JsonObject
{
["assetType"] = asset.AssetType.ToString().ToLowerInvariant()
};
// Add OID if available
if (!string.IsNullOrEmpty(asset.Oid))
{
cryptoProp["oid"] = asset.Oid;
}
// Serialize based on asset type
switch (asset.AssetType)
{
case CryptoAssetType.Algorithm:
cryptoProp["algorithmProperties"] = SerializeAlgorithmProperties(asset);
break;
case CryptoAssetType.Certificate:
if (!string.IsNullOrEmpty(asset.CertificateSubject) || !string.IsNullOrEmpty(asset.CertificateIssuer))
{
cryptoProp["certificateProperties"] = SerializeCertificateProperties(asset);
}
break;
case CryptoAssetType.Protocol:
if (!string.IsNullOrEmpty(asset.ProtocolName))
{
cryptoProp["protocolProperties"] = SerializeProtocolProperties(asset);
}
break;
case CryptoAssetType.RelatedCryptoMaterial:
cryptoProp["relatedCryptoMaterialProperties"] = SerializeRelatedCryptoMaterialProperties(asset);
break;
}
return cryptoProp;
}
private static JsonObject SerializeAlgorithmProperties(CryptoAsset asset)
{
var props = new JsonObject();
if (asset.Primitive.HasValue)
{
props["primitive"] = asset.Primitive.Value.ToString().ToLowerInvariant();
}
if (asset.Mode.HasValue)
{
props["mode"] = asset.Mode.Value.ToString().ToLowerInvariant();
}
if (asset.Padding.HasValue)
{
props["padding"] = asset.Padding.Value.ToString().ToLowerInvariant();
}
if (!asset.Functions.IsDefaultOrEmpty && asset.Functions.Length > 0)
{
var funcsArray = new JsonArray();
foreach (var func in asset.Functions)
{
funcsArray.Add(func.ToString().ToLowerInvariant());
}
props["cryptoFunctions"] = funcsArray;
}
if (asset.KeySizeBits.HasValue)
{
props["parameterSetIdentifier"] = $"{asset.KeySizeBits.Value}-bit";
}
if (!string.IsNullOrEmpty(asset.Curve))
{
props["curve"] = asset.Curve;
}
if (asset.ExecutionEnvironment.HasValue)
{
props["executionEnvironment"] = asset.ExecutionEnvironment.Value.ToString().ToLowerInvariant();
}
if (!string.IsNullOrEmpty(asset.ImplementationPlatform))
{
props["implementationPlatform"] = asset.ImplementationPlatform;
}
if (asset.NistQuantumSecurityLevel.HasValue)
{
props["nistQuantumSecurityLevel"] = asset.NistQuantumSecurityLevel.Value;
}
if (asset.ClassicalSecurityLevel.HasValue)
{
props["classicalSecurityLevel"] = asset.ClassicalSecurityLevel.Value;
}
return props;
}
private static JsonObject SerializeCertificateProperties(CryptoAsset asset)
{
var props = new JsonObject();
if (!string.IsNullOrEmpty(asset.CertificateSubject))
{
props["subjectName"] = asset.CertificateSubject;
}
if (!string.IsNullOrEmpty(asset.CertificateIssuer))
{
props["issuerName"] = asset.CertificateIssuer;
}
if (!string.IsNullOrEmpty(asset.CertificateNotBefore))
{
props["notValidBefore"] = asset.CertificateNotBefore;
}
if (!string.IsNullOrEmpty(asset.CertificateNotAfter))
{
props["notValidAfter"] = asset.CertificateNotAfter;
}
if (!string.IsNullOrEmpty(asset.SignatureAlgorithmRef))
{
props["signatureAlgorithmRef"] = asset.SignatureAlgorithmRef;
}
if (asset.CertificateFormat.HasValue)
{
props["certificateFormat"] = asset.CertificateFormat.Value.ToString().ToLowerInvariant();
}
return props;
}
private static JsonObject SerializeProtocolProperties(CryptoAsset asset)
{
var props = new JsonObject();
if (!string.IsNullOrEmpty(asset.ProtocolName))
{
props["type"] = asset.ProtocolName.ToLowerInvariant();
}
if (!string.IsNullOrEmpty(asset.ProtocolVersion))
{
props["version"] = asset.ProtocolVersion;
}
if (!asset.CipherSuites.IsDefaultOrEmpty && asset.CipherSuites.Length > 0)
{
var suitesArray = new JsonArray();
foreach (var suite in asset.CipherSuites)
{
suitesArray.Add(new JsonObject { ["name"] = suite });
}
props["cipherSuites"] = suitesArray;
}
return props;
}
private static JsonObject SerializeRelatedCryptoMaterialProperties(CryptoAsset asset)
{
var props = new JsonObject();
if (asset.MaterialType.HasValue)
{
props["type"] = asset.MaterialType.Value.ToString().ToLowerInvariant();
}
if (!string.IsNullOrEmpty(asset.MaterialId))
{
props["id"] = asset.MaterialId;
}
if (asset.MaterialState.HasValue)
{
props["state"] = asset.MaterialState.Value.ToString().ToLowerInvariant();
}
if (!string.IsNullOrEmpty(asset.AlgorithmRef))
{
props["algorithmRef"] = asset.AlgorithmRef;
}
if (asset.KeySizeBits.HasValue)
{
props["size"] = asset.KeySizeBits.Value;
}
return props;
}
private static void AddCbomMetadata(JsonObject rootObj, CbomAggregationResult cbomResult)
{
var metadata = rootObj["metadata"] as JsonObject;
if (metadata is null)
{
return;
}
var properties = metadata["properties"] as JsonArray ?? new JsonArray();
// Add CBOM summary properties
properties.Add(new JsonObject
{
["name"] = "stellaops:cbom.generatedAt",
["value"] = cbomResult.GeneratedAt
});
properties.Add(new JsonObject
{
["name"] = "stellaops:cbom.totalAssets",
["value"] = cbomResult.Assets.Length.ToString()
});
properties.Add(new JsonObject
{
["name"] = "stellaops:cbom.uniqueAlgorithms",
["value"] = cbomResult.UniqueAlgorithms.Length.ToString()
});
if (cbomResult.RiskAssessment is not null)
{
var risk = cbomResult.RiskAssessment;
properties.Add(new JsonObject
{
["name"] = "stellaops:cbom.riskScore",
["value"] = risk.OverallScore.ToString("F2", System.Globalization.CultureInfo.InvariantCulture)
});
if (risk.DeprecatedAlgorithms.Length > 0)
{
properties.Add(new JsonObject
{
["name"] = "stellaops:cbom.deprecatedAlgorithms",
["value"] = string.Join(",", risk.DeprecatedAlgorithms)
});
}
if (risk.WeakAlgorithms.Length > 0)
{
properties.Add(new JsonObject
{
["name"] = "stellaops:cbom.weakAlgorithms",
["value"] = string.Join(",", risk.WeakAlgorithms)
});
}
if (risk.QuantumVulnerableAlgorithms.Length > 0)
{
properties.Add(new JsonObject
{
["name"] = "stellaops:cbom.quantumVulnerable",
["value"] = string.Join(",", risk.QuantumVulnerableAlgorithms)
});
}
if (risk.PostQuantumAlgorithms.Length > 0)
{
properties.Add(new JsonObject
{
["name"] = "stellaops:cbom.postQuantumReady",
["value"] = string.Join(",", risk.PostQuantumAlgorithms)
});
}
}
metadata["properties"] = properties;
}
}

View File

@@ -0,0 +1,467 @@
using System.Collections.Immutable;
using System.Text.Json.Serialization;
namespace StellaOps.Scanner.Emit.Cbom;
/// <summary>
/// CycloneDX 1.7 Cryptographic Properties (CBOM).
/// Per CycloneDX 1.7 specification for cryptographic asset inventory.
/// </summary>
public sealed record CryptoProperties
{
/// <summary>Type of cryptographic asset.</summary>
[JsonPropertyName("assetType")]
public required CryptoAssetType AssetType { get; init; }
/// <summary>Algorithm reference when asset type is Algorithm.</summary>
[JsonPropertyName("algorithmProperties")]
public AlgorithmProperties? AlgorithmProperties { get; init; }
/// <summary>Certificate reference when asset type is Certificate.</summary>
[JsonPropertyName("certificateProperties")]
public CertificateProperties? CertificateProperties { get; init; }
/// <summary>Protocol reference when asset type is Protocol.</summary>
[JsonPropertyName("protocolProperties")]
public ProtocolProperties? ProtocolProperties { get; init; }
/// <summary>Key properties when asset type is Key.</summary>
[JsonPropertyName("relatedCryptoMaterialProperties")]
public RelatedCryptoMaterialProperties? RelatedCryptoMaterialProperties { get; init; }
/// <summary>Object Identifier per IANA/ISO.</summary>
[JsonPropertyName("oid")]
public string? Oid { get; init; }
}
/// <summary>
/// CycloneDX 1.7 Algorithm Properties.
/// </summary>
public sealed record AlgorithmProperties
{
/// <summary>Algorithm primitive (block-cipher, stream-cipher, hash, etc.).</summary>
[JsonPropertyName("primitive")]
public CryptoPrimitive? Primitive { get; init; }
/// <summary>Algorithm mode (CBC, GCM, CTR, etc.).</summary>
[JsonPropertyName("mode")]
public CryptoMode? Mode { get; init; }
/// <summary>Padding scheme (PKCS7, OAEP, etc.).</summary>
[JsonPropertyName("padding")]
public CryptoPadding? Padding { get; init; }
/// <summary>Cryptographic functions this algorithm performs.</summary>
[JsonPropertyName("cryptoFunctions")]
public ImmutableArray<CryptoFunction> CryptoFunctions { get; init; } = ImmutableArray<CryptoFunction>.Empty;
/// <summary>Key size in bits.</summary>
[JsonPropertyName("parameterSetIdentifier")]
public string? ParameterSetIdentifier { get; init; }
/// <summary>Elliptic curve identifier for EC algorithms.</summary>
[JsonPropertyName("curve")]
public string? Curve { get; init; }
/// <summary>Execution environment (software, hardware, HSM, TEE).</summary>
[JsonPropertyName("executionEnvironment")]
public ExecutionEnvironment? ExecutionEnvironment { get; init; }
/// <summary>Implementation platform (native, OpenSSL, BouncyCastle, etc.).</summary>
[JsonPropertyName("implementationPlatform")]
public string? ImplementationPlatform { get; init; }
/// <summary>NIST post-quantum security level (1-5).</summary>
[JsonPropertyName("nistQuantumSecurityLevel")]
public int? NistQuantumSecurityLevel { get; init; }
/// <summary>Classical security level equivalent in bits.</summary>
[JsonPropertyName("classicalSecurityLevel")]
public int? ClassicalSecurityLevel { get; init; }
}
/// <summary>
/// CycloneDX 1.7 Certificate Properties.
/// </summary>
public sealed record CertificateProperties
{
/// <summary>Certificate subject distinguished name.</summary>
[JsonPropertyName("subjectName")]
public string? SubjectName { get; init; }
/// <summary>Certificate issuer distinguished name.</summary>
[JsonPropertyName("issuerName")]
public string? IssuerName { get; init; }
/// <summary>Certificate not valid before date (ISO 8601).</summary>
[JsonPropertyName("notValidBefore")]
public string? NotValidBefore { get; init; }
/// <summary>Certificate not valid after date (ISO 8601).</summary>
[JsonPropertyName("notValidAfter")]
public string? NotValidAfter { get; init; }
/// <summary>Signature algorithm OID.</summary>
[JsonPropertyName("signatureAlgorithmRef")]
public string? SignatureAlgorithmRef { get; init; }
/// <summary>Subject public key algorithm OID.</summary>
[JsonPropertyName("subjectPublicKeyRef")]
public string? SubjectPublicKeyRef { get; init; }
/// <summary>Certificate format (X.509, PGP, etc.).</summary>
[JsonPropertyName("certificateFormat")]
public CertificateFormat? CertificateFormat { get; init; }
/// <summary>Certificate extension key usages.</summary>
[JsonPropertyName("certificateExtension")]
public string? CertificateExtension { get; init; }
}
/// <summary>
/// CycloneDX 1.7 Protocol Properties.
/// </summary>
public sealed record ProtocolProperties
{
/// <summary>Protocol type (TLS, SSH, IPsec, etc.).</summary>
[JsonPropertyName("type")]
public ProtocolType? Type { get; init; }
/// <summary>Protocol version (e.g., "1.3" for TLS 1.3).</summary>
[JsonPropertyName("version")]
public string? Version { get; init; }
/// <summary>Cipher suites supported.</summary>
[JsonPropertyName("cipherSuites")]
public ImmutableArray<CipherSuite> CipherSuites { get; init; } = ImmutableArray<CipherSuite>.Empty;
/// <summary>IKE version for IPsec.</summary>
[JsonPropertyName("ikev2TransformTypes")]
public IkeV2TransformTypes? IkeV2TransformTypes { get; init; }
}
/// <summary>
/// CycloneDX 1.7 Related Crypto Material Properties.
/// Describes keys, nonces, salts, IVs, etc.
/// </summary>
public sealed record RelatedCryptoMaterialProperties
{
/// <summary>Type of crypto material.</summary>
[JsonPropertyName("type")]
public RelatedCryptoMaterialType? Type { get; init; }
/// <summary>Material identifier/reference.</summary>
[JsonPropertyName("id")]
public string? Id { get; init; }
/// <summary>State of the crypto material.</summary>
[JsonPropertyName("state")]
public CryptoMaterialState? State { get; init; }
/// <summary>Algorithm reference this material is used with.</summary>
[JsonPropertyName("algorithmRef")]
public string? AlgorithmRef { get; init; }
/// <summary>Creation date (ISO 8601).</summary>
[JsonPropertyName("creationDate")]
public string? CreationDate { get; init; }
/// <summary>Activation date (ISO 8601).</summary>
[JsonPropertyName("activationDate")]
public string? ActivationDate { get; init; }
/// <summary>Expiration date (ISO 8601).</summary>
[JsonPropertyName("expirationDate")]
public string? ExpirationDate { get; init; }
/// <summary>Size in bits.</summary>
[JsonPropertyName("size")]
public int? Size { get; init; }
/// <summary>Format of the crypto material.</summary>
[JsonPropertyName("format")]
public string? Format { get; init; }
/// <summary>Secured by reference to another component.</summary>
[JsonPropertyName("securedBy")]
public SecuredBy? SecuredBy { get; init; }
}
/// <summary>
/// Cipher suite representation for protocols.
/// </summary>
public sealed record CipherSuite
{
/// <summary>IANA cipher suite name.</summary>
[JsonPropertyName("name")]
public string? Name { get; init; }
/// <summary>Algorithms used in this suite.</summary>
[JsonPropertyName("algorithms")]
public ImmutableArray<string> Algorithms { get; init; } = ImmutableArray<string>.Empty;
/// <summary>Identifiers (hex codes).</summary>
[JsonPropertyName("identifiers")]
public ImmutableArray<string> Identifiers { get; init; } = ImmutableArray<string>.Empty;
}
/// <summary>
/// IKEv2 transform types for IPsec.
/// </summary>
public sealed record IkeV2TransformTypes
{
[JsonPropertyName("encr")]
public ImmutableArray<string> Encr { get; init; } = ImmutableArray<string>.Empty;
[JsonPropertyName("prf")]
public ImmutableArray<string> Prf { get; init; } = ImmutableArray<string>.Empty;
[JsonPropertyName("integ")]
public ImmutableArray<string> Integ { get; init; } = ImmutableArray<string>.Empty;
[JsonPropertyName("ke")]
public ImmutableArray<string> Ke { get; init; } = ImmutableArray<string>.Empty;
[JsonPropertyName("esn")]
public bool? Esn { get; init; }
[JsonPropertyName("auth")]
public ImmutableArray<string> Auth { get; init; } = ImmutableArray<string>.Empty;
}
/// <summary>
/// Reference to security mechanism protecting crypto material.
/// </summary>
public sealed record SecuredBy
{
[JsonPropertyName("mechanism")]
public string? Mechanism { get; init; }
[JsonPropertyName("algorithmRef")]
public string? AlgorithmRef { get; init; }
}
#region Enums
/// <summary>
/// Type of cryptographic asset per CycloneDX 1.7.
/// </summary>
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum CryptoAssetType
{
/// <summary>Cryptographic algorithm.</summary>
Algorithm,
/// <summary>X.509 or other certificate.</summary>
Certificate,
/// <summary>Cryptographic protocol.</summary>
Protocol,
/// <summary>Related cryptographic material (keys, nonces, etc.).</summary>
RelatedCryptoMaterial
}
/// <summary>
/// Cryptographic primitive types.
/// </summary>
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum CryptoPrimitive
{
/// <summary>Discrete logarithm (DH, DSA).</summary>
Dlog,
/// <summary>Elliptic curve cryptography.</summary>
Ec,
/// <summary>RSA family.</summary>
Rsa,
/// <summary>Lattice-based cryptography.</summary>
Lattice,
/// <summary>Hash-based cryptography.</summary>
Hash,
/// <summary>Block cipher.</summary>
BlockCipher,
/// <summary>Stream cipher.</summary>
StreamCipher,
/// <summary>Authenticated encryption with associated data.</summary>
Aead,
/// <summary>Message authentication code.</summary>
Mac,
/// <summary>Key derivation function.</summary>
Kdf,
/// <summary>Key encapsulation mechanism.</summary>
Kem,
/// <summary>Password-based key derivation.</summary>
Pbkdf,
/// <summary>Digital signature.</summary>
Signature,
/// <summary>Key agreement.</summary>
KeyAgree,
/// <summary>Pseudorandom number generator.</summary>
Prng,
/// <summary>Unknown or other.</summary>
Other
}
/// <summary>
/// Block cipher modes of operation.
/// </summary>
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum CryptoMode
{
Cbc,
Ecb,
Ccm,
Gcm,
Cfb,
Ofb,
Ctr,
Xts,
Wrap,
Other
}
/// <summary>
/// Padding schemes.
/// </summary>
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum CryptoPadding
{
Pkcs7,
Oaep,
Pkcs1v15,
Pss,
X923,
Raw,
None,
Other
}
/// <summary>
/// Cryptographic functions an algorithm can perform.
/// </summary>
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum CryptoFunction
{
/// <summary>Random number generation.</summary>
Generate,
/// <summary>Key generation.</summary>
Keygen,
/// <summary>Key derivation.</summary>
Derive,
/// <summary>Digital signature creation.</summary>
Sign,
/// <summary>Signature verification.</summary>
Verify,
/// <summary>Encryption.</summary>
Encrypt,
/// <summary>Decryption.</summary>
Decrypt,
/// <summary>Authenticated encryption.</summary>
Encapsulate,
/// <summary>Authenticated decryption.</summary>
Decapsulate,
/// <summary>Hashing/digest.</summary>
Digest,
/// <summary>Message authentication.</summary>
Tag,
/// <summary>Key wrapping.</summary>
KeyWrap,
/// <summary>Key unwrapping.</summary>
KeyUnwrap,
/// <summary>Key agreement.</summary>
KeyAgree,
/// <summary>Other function.</summary>
Other
}
/// <summary>
/// Execution environment for crypto operations.
/// </summary>
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum ExecutionEnvironment
{
/// <summary>Software implementation.</summary>
Software,
/// <summary>Hardware security module.</summary>
HardwareSecurityModule,
/// <summary>Trusted execution environment.</summary>
TrustedExecutionEnvironment,
/// <summary>Hardware accelerator.</summary>
Hardware,
/// <summary>Unknown environment.</summary>
Unknown
}
/// <summary>
/// Certificate formats.
/// </summary>
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum CertificateFormat
{
X509,
Pgp,
Pkcs7,
Other
}
/// <summary>
/// Cryptographic protocol types.
/// </summary>
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum ProtocolType
{
Tls,
Ssh,
Ipsec,
Ike,
Sstp,
Wpa,
Other
}
/// <summary>
/// Types of related cryptographic material.
/// </summary>
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum RelatedCryptoMaterialType
{
/// <summary>Private key.</summary>
PrivateKey,
/// <summary>Public key.</summary>
PublicKey,
/// <summary>Secret/symmetric key.</summary>
SecretKey,
/// <summary>Session key.</summary>
Key,
/// <summary>Cryptographic nonce.</summary>
Nonce,
/// <summary>Cryptographic seed.</summary>
Seed,
/// <summary>Initialization vector.</summary>
Iv,
/// <summary>Salt for key derivation.</summary>
Salt,
/// <summary>Shared secret (DH/ECDH).</summary>
SharedSecret,
/// <summary>Other material.</summary>
Other
}
/// <summary>
/// State of cryptographic material.
/// </summary>
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum CryptoMaterialState
{
/// <summary>Pre-activation.</summary>
PreActivation,
/// <summary>Active/in-use.</summary>
Active,
/// <summary>Suspended.</summary>
Suspended,
/// <summary>Deactivated.</summary>
Deactivated,
/// <summary>Compromised.</summary>
Compromised,
/// <summary>Destroyed.</summary>
Destroyed
}
#endregion

View File

@@ -0,0 +1,196 @@
using System.Collections.Immutable;
using StellaOps.Scanner.Core.Contracts;
namespace StellaOps.Scanner.Emit.Cbom;
/// <summary>
/// Interface for crypto asset extractors that analyze components for cryptographic usage.
/// Each language analyzer implements this to detect crypto patterns.
/// </summary>
public interface ICryptoAssetExtractor
{
/// <summary>
/// Ecosystems this extractor supports.
/// </summary>
ImmutableArray<string> SupportedEcosystems { get; }
/// <summary>
/// Extracts cryptographic assets from a component.
/// </summary>
/// <param name="component">The component to analyze.</param>
/// <param name="analysisContext">Analysis context with file access.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>Extracted crypto assets.</returns>
Task<ImmutableArray<CryptoAsset>> ExtractAsync(
AggregatedComponent component,
CryptoAnalysisContext analysisContext,
CancellationToken cancellationToken = default);
}
/// <summary>
/// Context for crypto analysis providing access to component artifacts.
/// </summary>
public sealed record CryptoAnalysisContext
{
/// <summary>Layer digest for file access.</summary>
public required string LayerDigest { get; init; }
/// <summary>File system path mappings.</summary>
public ImmutableDictionary<string, string> FilePaths { get; init; } = ImmutableDictionary<string, string>.Empty;
/// <summary>Optional: Pre-analyzed metadata from language analyzer.</summary>
public ImmutableDictionary<string, object> Metadata { get; init; } = ImmutableDictionary<string, object>.Empty;
}
/// <summary>
/// Extracted cryptographic asset from analysis.
/// </summary>
public sealed record CryptoAsset
{
/// <summary>Unique identifier for this crypto asset.</summary>
public required string Id { get; init; }
/// <summary>Component key this asset belongs to.</summary>
public required string ComponentKey { get; init; }
/// <summary>Type of crypto asset.</summary>
public required CryptoAssetType AssetType { get; init; }
/// <summary>Algorithm name (e.g., "AES-256-GCM", "RSA-2048", "SHA-256").</summary>
public string? AlgorithmName { get; init; }
/// <summary>OID if available (e.g., "2.16.840.1.101.3.4.1.46" for AES-256-GCM).</summary>
public string? Oid { get; init; }
/// <summary>Key size in bits if applicable.</summary>
public int? KeySizeBits { get; init; }
/// <summary>Cryptographic primitive category.</summary>
public CryptoPrimitive? Primitive { get; init; }
/// <summary>Functions this algorithm performs.</summary>
public ImmutableArray<CryptoFunction> Functions { get; init; } = ImmutableArray<CryptoFunction>.Empty;
/// <summary>Mode of operation for block ciphers.</summary>
public CryptoMode? Mode { get; init; }
/// <summary>Padding scheme if applicable.</summary>
public CryptoPadding? Padding { get; init; }
/// <summary>Elliptic curve name for EC algorithms.</summary>
public string? Curve { get; init; }
/// <summary>Execution environment.</summary>
public ExecutionEnvironment? ExecutionEnvironment { get; init; }
/// <summary>Implementation library (e.g., "OpenSSL", "BouncyCastle", "System.Security.Cryptography").</summary>
public string? ImplementationPlatform { get; init; }
/// <summary>Confidence of detection (0.0 - 1.0).</summary>
public double Confidence { get; init; } = 1.0;
/// <summary>Source evidence (file path, method, etc.).</summary>
public ImmutableArray<string> Evidence { get; init; } = ImmutableArray<string>.Empty;
/// <summary>Risk flags identified for this crypto asset.</summary>
public ImmutableArray<CryptoRiskFlag> RiskFlags { get; init; } = ImmutableArray<CryptoRiskFlag>.Empty;
/// <summary>Full CycloneDX crypto properties for serialization.</summary>
public CryptoProperties? CryptoProperties { get; init; }
#region Certificate Properties (for AssetType.Certificate)
/// <summary>Certificate subject distinguished name.</summary>
public string? CertificateSubject { get; init; }
/// <summary>Certificate issuer distinguished name.</summary>
public string? CertificateIssuer { get; init; }
/// <summary>Certificate not valid before (ISO 8601).</summary>
public string? CertificateNotBefore { get; init; }
/// <summary>Certificate not valid after (ISO 8601).</summary>
public string? CertificateNotAfter { get; init; }
/// <summary>Signature algorithm reference.</summary>
public string? SignatureAlgorithmRef { get; init; }
/// <summary>Certificate format.</summary>
public CertificateFormat? CertificateFormat { get; init; }
#endregion
#region Protocol Properties (for AssetType.Protocol)
/// <summary>Protocol name (TLS, SSH, etc.).</summary>
public string? ProtocolName { get; init; }
/// <summary>Protocol version.</summary>
public string? ProtocolVersion { get; init; }
/// <summary>Cipher suites supported.</summary>
public ImmutableArray<string> CipherSuites { get; init; } = ImmutableArray<string>.Empty;
#endregion
#region Related Crypto Material Properties (for AssetType.RelatedCryptoMaterial)
/// <summary>Material type (key, nonce, salt, etc.).</summary>
public RelatedCryptoMaterialType? MaterialType { get; init; }
/// <summary>Material identifier.</summary>
public string? MaterialId { get; init; }
/// <summary>Material state.</summary>
public CryptoMaterialState? MaterialState { get; init; }
/// <summary>Algorithm reference for this material.</summary>
public string? AlgorithmRef { get; init; }
#endregion
#region Post-Quantum Properties
/// <summary>NIST post-quantum security level (1-5).</summary>
public int? NistQuantumSecurityLevel { get; init; }
/// <summary>Classical security level equivalent in bits.</summary>
public int? ClassicalSecurityLevel { get; init; }
#endregion
}
/// <summary>
/// Risk flags for cryptographic assets.
/// </summary>
public sealed record CryptoRiskFlag
{
/// <summary>Risk identifier.</summary>
public required string RiskId { get; init; }
/// <summary>Risk severity (Low, Medium, High, Critical).</summary>
public required CryptoRiskSeverity Severity { get; init; }
/// <summary>Human-readable description.</summary>
public required string Description { get; init; }
/// <summary>Recommended action.</summary>
public string? Recommendation { get; init; }
}
/// <summary>
/// Crypto risk severity levels.
/// </summary>
public enum CryptoRiskSeverity
{
/// <summary>Informational only.</summary>
Info,
/// <summary>Low risk, may need future attention.</summary>
Low,
/// <summary>Medium risk, should be addressed.</summary>
Medium,
/// <summary>High risk, needs prompt attention.</summary>
High,
/// <summary>Critical risk, immediate action required.</summary>
Critical
}

View File

@@ -0,0 +1,508 @@
using System.Collections.Immutable;
using System.Text.Json;
using System.Text.Json.Nodes;
using StellaOps.Scanner.Emit.Cbom;
namespace StellaOps.Scanner.Emit.Composition;
/// <summary>
/// Enhances CycloneDX 1.7 JSON with CBOM (Cryptographic BOM) properties.
/// This is a post-processor that injects cryptographicProperties into components
/// since CycloneDX.Core doesn't natively support 1.7 CBOM yet.
/// </summary>
public static class CycloneDxCbomWriter
{
/// <summary>
/// Enhances a CycloneDX JSON with CBOM data.
/// </summary>
/// <param name="cycloneDxJson">The CycloneDX JSON (should be 1.7 format).</param>
/// <param name="cryptoAssetsByComponent">Crypto assets indexed by component bom-ref.</param>
/// <returns>Enhanced JSON with cryptographicProperties.</returns>
public static string InjectCbom(
string cycloneDxJson,
ImmutableDictionary<string, ImmutableArray<CryptoAsset>> cryptoAssetsByComponent)
{
if (string.IsNullOrEmpty(cycloneDxJson) || cryptoAssetsByComponent.IsEmpty)
{
return cycloneDxJson;
}
var root = JsonNode.Parse(cycloneDxJson);
if (root is not JsonObject bomObj)
{
return cycloneDxJson;
}
// Ensure specVersion is 1.7
if (bomObj["specVersion"]?.GetValue<string>() is not "1.7")
{
bomObj["specVersion"] = "1.7";
}
// Process components array
if (bomObj["components"] is JsonArray componentsArray)
{
foreach (var componentNode in componentsArray)
{
if (componentNode is not JsonObject componentObj)
continue;
var bomRef = componentObj["bom-ref"]?.GetValue<string>();
if (string.IsNullOrEmpty(bomRef))
continue;
if (cryptoAssetsByComponent.TryGetValue(bomRef, out var assets) && !assets.IsEmpty)
{
var cryptoProps = BuildCryptoProperties(assets);
if (cryptoProps != null)
{
componentObj["cryptographicProperties"] = cryptoProps;
}
}
}
}
return bomObj.ToJsonString(new JsonSerializerOptions
{
WriteIndented = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull
});
}
/// <summary>
/// Enhances CycloneDX artifact bytes with CBOM data.
/// </summary>
public static byte[] InjectCbomBytes(
byte[] cycloneDxJsonBytes,
ImmutableDictionary<string, ImmutableArray<CryptoAsset>> cryptoAssetsByComponent)
{
var json = System.Text.Encoding.UTF8.GetString(cycloneDxJsonBytes);
var enhanced = InjectCbom(json, cryptoAssetsByComponent);
return System.Text.Encoding.UTF8.GetBytes(enhanced);
}
private static JsonArray? BuildCryptoProperties(ImmutableArray<CryptoAsset> assets)
{
if (assets.IsDefaultOrEmpty)
return null;
var cryptoArray = new JsonArray();
foreach (var asset in assets)
{
var cryptoObj = new JsonObject
{
["assetType"] = MapAssetType(asset.AssetType)
};
// Add OID if present
if (!string.IsNullOrEmpty(asset.Oid))
{
cryptoObj["oid"] = asset.Oid;
}
// Build algorithm properties
if (asset.AssetType == CryptoAssetType.Algorithm)
{
var algProps = BuildAlgorithmProperties(asset);
if (algProps != null)
{
cryptoObj["algorithmProperties"] = algProps;
}
}
// Build related crypto material properties
else if (asset.AssetType == CryptoAssetType.RelatedCryptoMaterial && asset.CryptoProperties?.RelatedCryptoMaterialProperties != null)
{
var matProps = BuildRelatedCryptoMaterialProperties(asset.CryptoProperties.RelatedCryptoMaterialProperties);
if (matProps != null)
{
cryptoObj["relatedCryptoMaterialProperties"] = matProps;
}
}
// Build certificate properties
else if (asset.AssetType == CryptoAssetType.Certificate && asset.CryptoProperties?.CertificateProperties != null)
{
var certProps = BuildCertificateProperties(asset.CryptoProperties.CertificateProperties);
if (certProps != null)
{
cryptoObj["certificateProperties"] = certProps;
}
}
// Build protocol properties
else if (asset.AssetType == CryptoAssetType.Protocol && asset.CryptoProperties?.ProtocolProperties != null)
{
var protoProps = BuildProtocolProperties(asset.CryptoProperties.ProtocolProperties);
if (protoProps != null)
{
cryptoObj["protocolProperties"] = protoProps;
}
}
cryptoArray.Add(cryptoObj);
}
return cryptoArray.Count > 0 ? cryptoArray : null;
}
private static JsonObject? BuildAlgorithmProperties(CryptoAsset asset)
{
var props = new JsonObject();
// Primitive
if (asset.Primitive.HasValue)
{
props["primitive"] = MapPrimitive(asset.Primitive.Value);
}
// Crypto functions
if (!asset.Functions.IsDefaultOrEmpty)
{
var funcsArray = new JsonArray();
foreach (var func in asset.Functions)
{
funcsArray.Add(MapFunction(func));
}
props["cryptoFunctions"] = funcsArray;
}
// Mode
if (asset.Mode.HasValue)
{
props["mode"] = MapMode(asset.Mode.Value);
}
// Padding
if (asset.Padding.HasValue)
{
props["padding"] = MapPadding(asset.Padding.Value);
}
// Curve
if (!string.IsNullOrEmpty(asset.Curve))
{
props["curve"] = asset.Curve;
}
// Key size as parameter set identifier
if (asset.KeySizeBits.HasValue)
{
props["parameterSetIdentifier"] = $"{asset.KeySizeBits.Value}";
props["classicalSecurityLevel"] = asset.KeySizeBits.Value;
}
// Execution environment
if (asset.ExecutionEnvironment.HasValue)
{
props["executionEnvironment"] = MapExecutionEnvironment(asset.ExecutionEnvironment.Value);
}
// Implementation platform
if (!string.IsNullOrEmpty(asset.ImplementationPlatform))
{
props["implementationPlatform"] = asset.ImplementationPlatform;
}
// Check for post-quantum and add NIST security level
if (IsPostQuantumAlgorithm(asset.AlgorithmName))
{
// Typical NIST level for ML-KEM/ML-DSA is 1, 3, or 5
props["nistQuantumSecurityLevel"] = GetNistQuantumLevel(asset.AlgorithmName);
}
return props.Count > 0 ? props : null;
}
private static JsonObject? BuildRelatedCryptoMaterialProperties(RelatedCryptoMaterialProperties material)
{
var props = new JsonObject();
if (material.Type.HasValue)
{
props["type"] = MapRelatedMaterialType(material.Type.Value);
}
if (!string.IsNullOrEmpty(material.Id))
{
props["id"] = material.Id;
}
if (material.State.HasValue)
{
props["state"] = MapMaterialState(material.State.Value);
}
if (material.Size.HasValue)
{
props["size"] = material.Size.Value;
}
if (!string.IsNullOrEmpty(material.AlgorithmRef))
{
props["algorithmRef"] = material.AlgorithmRef;
}
if (!string.IsNullOrEmpty(material.CreationDate))
{
props["creationDate"] = material.CreationDate;
}
if (!string.IsNullOrEmpty(material.ActivationDate))
{
props["activationDate"] = material.ActivationDate;
}
if (!string.IsNullOrEmpty(material.ExpirationDate))
{
props["expirationDate"] = material.ExpirationDate;
}
if (!string.IsNullOrEmpty(material.Format))
{
props["format"] = material.Format;
}
return props.Count > 0 ? props : null;
}
private static JsonObject? BuildCertificateProperties(CertificateProperties cert)
{
var props = new JsonObject();
if (!string.IsNullOrEmpty(cert.SubjectName))
{
props["subjectName"] = cert.SubjectName;
}
if (!string.IsNullOrEmpty(cert.IssuerName))
{
props["issuerName"] = cert.IssuerName;
}
if (!string.IsNullOrEmpty(cert.NotValidBefore))
{
props["notValidBefore"] = cert.NotValidBefore;
}
if (!string.IsNullOrEmpty(cert.NotValidAfter))
{
props["notValidAfter"] = cert.NotValidAfter;
}
if (!string.IsNullOrEmpty(cert.SignatureAlgorithmRef))
{
props["signatureAlgorithmRef"] = cert.SignatureAlgorithmRef;
}
if (!string.IsNullOrEmpty(cert.SubjectPublicKeyRef))
{
props["subjectPublicKeyRef"] = cert.SubjectPublicKeyRef;
}
if (cert.CertificateFormat.HasValue)
{
props["certificateFormat"] = MapCertificateFormat(cert.CertificateFormat.Value);
}
return props.Count > 0 ? props : null;
}
private static JsonObject? BuildProtocolProperties(ProtocolProperties protocol)
{
var props = new JsonObject();
if (protocol.Type.HasValue)
{
props["type"] = MapProtocolType(protocol.Type.Value);
}
if (!string.IsNullOrEmpty(protocol.Version))
{
props["version"] = protocol.Version;
}
if (!protocol.CipherSuites.IsDefaultOrEmpty)
{
var suitesArray = new JsonArray();
foreach (var suite in protocol.CipherSuites)
{
var suiteObj = new JsonObject();
if (!string.IsNullOrEmpty(suite.Name))
{
suiteObj["name"] = suite.Name;
}
if (!suite.Algorithms.IsDefaultOrEmpty)
{
var algsArray = new JsonArray();
foreach (var alg in suite.Algorithms)
{
algsArray.Add(alg);
}
suiteObj["algorithms"] = algsArray;
}
suitesArray.Add(suiteObj);
}
props["cipherSuites"] = suitesArray;
}
return props.Count > 0 ? props : null;
}
private static string MapAssetType(CryptoAssetType type) => type switch
{
CryptoAssetType.Algorithm => "algorithm",
CryptoAssetType.Certificate => "certificate",
CryptoAssetType.Protocol => "protocol",
CryptoAssetType.RelatedCryptoMaterial => "related-crypto-material",
_ => "unknown"
};
private static string MapPrimitive(CryptoPrimitive primitive) => primitive switch
{
CryptoPrimitive.Dlog => "dlog",
CryptoPrimitive.Ec => "ec",
CryptoPrimitive.Rsa => "rsa",
CryptoPrimitive.Lattice => "lattice",
CryptoPrimitive.Hash => "hash",
CryptoPrimitive.BlockCipher => "block-cipher",
CryptoPrimitive.StreamCipher => "stream-cipher",
CryptoPrimitive.Aead => "aead",
CryptoPrimitive.Mac => "mac",
CryptoPrimitive.Kdf => "kdf",
CryptoPrimitive.Kem => "kem",
CryptoPrimitive.Pbkdf => "pbkdf",
CryptoPrimitive.Signature => "signature",
CryptoPrimitive.KeyAgree => "key-agree",
CryptoPrimitive.Prng => "prng",
_ => "other"
};
private static string MapFunction(CryptoFunction func) => func switch
{
CryptoFunction.Generate => "generate",
CryptoFunction.Keygen => "keygen",
CryptoFunction.Derive => "derive",
CryptoFunction.Sign => "sign",
CryptoFunction.Verify => "verify",
CryptoFunction.Encrypt => "encrypt",
CryptoFunction.Decrypt => "decrypt",
CryptoFunction.Encapsulate => "encapsulate",
CryptoFunction.Decapsulate => "decapsulate",
CryptoFunction.Digest => "digest",
CryptoFunction.Tag => "tag",
CryptoFunction.KeyWrap => "key-wrap",
CryptoFunction.KeyUnwrap => "key-unwrap",
CryptoFunction.KeyAgree => "key-agree",
_ => "other"
};
private static string MapMode(CryptoMode mode) => mode switch
{
CryptoMode.Cbc => "cbc",
CryptoMode.Ecb => "ecb",
CryptoMode.Ccm => "ccm",
CryptoMode.Gcm => "gcm",
CryptoMode.Cfb => "cfb",
CryptoMode.Ofb => "ofb",
CryptoMode.Ctr => "ctr",
CryptoMode.Xts => "xts",
CryptoMode.Wrap => "wrap",
_ => "other"
};
private static string MapPadding(CryptoPadding padding) => padding switch
{
CryptoPadding.Pkcs7 => "pkcs7",
CryptoPadding.Oaep => "oaep",
CryptoPadding.Pkcs1v15 => "pkcs1v15",
CryptoPadding.Pss => "pss",
CryptoPadding.X923 => "x923",
CryptoPadding.Raw => "raw",
CryptoPadding.None => "none",
_ => "other"
};
private static string MapExecutionEnvironment(ExecutionEnvironment env) => env switch
{
ExecutionEnvironment.Software => "software",
ExecutionEnvironment.HardwareSecurityModule => "hardware-security-module",
ExecutionEnvironment.TrustedExecutionEnvironment => "trusted-execution-environment",
ExecutionEnvironment.Hardware => "hardware",
_ => "unknown"
};
private static string MapRelatedMaterialType(RelatedCryptoMaterialType type) => type switch
{
RelatedCryptoMaterialType.PrivateKey => "private-key",
RelatedCryptoMaterialType.PublicKey => "public-key",
RelatedCryptoMaterialType.SecretKey => "secret-key",
RelatedCryptoMaterialType.Key => "key",
RelatedCryptoMaterialType.Nonce => "nonce",
RelatedCryptoMaterialType.Seed => "seed",
RelatedCryptoMaterialType.Iv => "iv",
RelatedCryptoMaterialType.Salt => "salt",
RelatedCryptoMaterialType.SharedSecret => "shared-secret",
_ => "other"
};
private static string MapMaterialState(CryptoMaterialState state) => state switch
{
CryptoMaterialState.PreActivation => "pre-activation",
CryptoMaterialState.Active => "active",
CryptoMaterialState.Suspended => "suspended",
CryptoMaterialState.Deactivated => "deactivated",
CryptoMaterialState.Compromised => "compromised",
CryptoMaterialState.Destroyed => "destroyed",
_ => "unknown"
};
private static string MapCertificateFormat(CertificateFormat format) => format switch
{
CertificateFormat.X509 => "X.509",
CertificateFormat.Pgp => "PGP",
CertificateFormat.Pkcs7 => "PKCS#7",
_ => "other"
};
private static string MapProtocolType(ProtocolType type) => type switch
{
ProtocolType.Tls => "tls",
ProtocolType.Ssh => "ssh",
ProtocolType.Ipsec => "ipsec",
ProtocolType.Ike => "ike",
ProtocolType.Sstp => "sstp",
ProtocolType.Wpa => "wpa",
_ => "other"
};
private static bool IsPostQuantumAlgorithm(string? algorithmName)
{
if (string.IsNullOrEmpty(algorithmName))
return false;
var upper = algorithmName.ToUpperInvariant();
return upper.Contains("KYBER") || upper.Contains("ML-KEM") ||
upper.Contains("DILITHIUM") || upper.Contains("ML-DSA") ||
upper.Contains("SPHINCS") || upper.Contains("SLH-DSA") ||
upper.Contains("FALCON") || upper.Contains("NTRU") ||
upper.Contains("FRODO") || upper.Contains("SABER");
}
private static int GetNistQuantumLevel(string? algorithmName)
{
if (string.IsNullOrEmpty(algorithmName))
return 1;
var upper = algorithmName.ToUpperInvariant();
// ML-KEM-768 / Dilithium3 => Level 3
// ML-KEM-1024 / Dilithium5 => Level 5
if (upper.Contains("1024") || upper.Contains("5"))
return 5;
if (upper.Contains("768") || upper.Contains("3"))
return 3;
// Default to Level 1 (ML-KEM-512 / Dilithium2)
return 1;
}
}

View File

@@ -14,8 +14,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="CycloneDX.Core" Version="10.0.2" />
<PackageReference Include="RoaringBitmap" Version="0.0.9" />
<PackageReference Include="CycloneDX.Core" />
<PackageReference Include="RoaringBitmap" />
</ItemGroup>
<ItemGroup>