/** * Trust Label and Principal - Trust algebra components. * Sprint: SPRINT_3600_0001_0001 (Trust Algebra and Lattice Engine) * Tasks: TRUST-005, TRUST-006 * * Trust is not a single number; it must represent: * - Cryptographic verification * - Identity assurance * - Authority scope * - Freshness/revocation * - Evidence strength * * These models enable policy-driven trust evaluation that is * deterministic and explainable. */ namespace StellaOps.Policy.TrustLattice; /// /// Assurance level for cryptographic and identity verification. /// Increasing levels from A0 (weakest) to A4 (strongest). /// public enum AssuranceLevel { /// /// A0: Unsigned or unverifiable assertion. /// No cryptographic backing. /// A0_Unsigned = 0, /// /// A1: Signed, but weak identity binding. /// Key is known but identity not strongly verified. /// A1_WeakIdentity = 1, /// /// A2: Signed with verified identity. /// Certificate chain or keyless identity (OIDC) verified. /// A2_VerifiedIdentity = 2, /// /// A3: Signed with provenance binding. /// Signature bound to artifact digest via attestation. /// A3_ProvenanceBound = 3, /// /// A4: Full transparency log inclusion. /// Signed + provenance + Rekor/transparency log entry. /// A4_TransparencyLog = 4, } /// /// Freshness class for temporal validity of assertions. /// public enum FreshnessClass { /// /// Unknown or missing timestamp. /// Unknown = 0, /// /// Expired assertion (past valid_until). /// Expired = 1, /// /// Stale assertion (older than freshness threshold). /// Stale = 2, /// /// Fresh assertion (within freshness threshold). /// Fresh = 3, /// /// Live assertion (just issued or real-time). /// Live = 4, } /// /// Evidence class describing the strength of supporting evidence. /// public enum EvidenceClass { /// /// E0: Statement only (no supporting evidence refs). /// E0_StatementOnly = 0, /// /// E1: SBOM linkage evidence. /// Component present + version evidence. /// E1_SbomLinkage = 1, /// /// E2: Reachability/mitigation evidence. /// Call paths, config snapshots, runtime proofs. /// E2_ReachabilityMitigation = 2, /// /// E3: Remediation evidence. /// Patch diffs, pedigree/commit chain, fix verification. /// E3_Remediation = 3, } /// /// Role that a principal can play in the trust model. /// [Flags] public enum PrincipalRole { None = 0, /// /// Vendor: Original software vendor. /// Authoritative for their own products. /// Vendor = 1 << 0, /// /// Distributor: OS/distro package maintainer. /// Authoritative for packages in their repositories. /// Distributor = 1 << 1, /// /// Scanner: Automated vulnerability scanner. /// Provides detection evidence. /// Scanner = 1 << 2, /// /// Auditor: Security auditor or penetration tester. /// Provides expert assessment evidence. /// Auditor = 1 << 3, /// /// InternalSecurity: Internal security team. /// Authoritative for internal artifact reachability/mitigation. /// InternalSecurity = 1 << 4, /// /// BuildSystem: CI/CD build system. /// Provides provenance and build evidence. /// BuildSystem = 1 << 5, /// /// RuntimeMonitor: Runtime observability system. /// Provides runtime behavior evidence. /// RuntimeMonitor = 1 << 6, } /// /// Authority scope defining what subjects a principal is authoritative for. /// public sealed record AuthorityScope { /// /// Scope type for canonical serialization. /// public string Type { get; init; } = "default"; /// /// Constraint expression for the scope. /// public string? Constraint { get; init; } /// /// Product namespace patterns (e.g., "vendor.example/*"). /// Principal is authoritative for these products. /// public IReadOnlyList? Products { get; init; } /// /// Package namespace patterns (e.g., "pkg:npm/*", "pkg:maven/org.example/*"). /// public IReadOnlyList? Packages { get; init; } /// /// Artifact digest patterns (e.g., "sha256:*" for internal artifacts). /// public IReadOnlyList? Artifacts { get; init; } /// /// Vulnerability source patterns (e.g., "nvd", "osv"). /// public IReadOnlyList? VulnerabilitySources { get; init; } /// /// Checks if this scope covers a given subject. /// public bool Covers(Subject subject) { // Check artifacts if (Artifacts is { Count: > 0 }) { if (!MatchesAny(subject.Artifact.Digest, Artifacts)) return false; } // Check packages if (Packages is { Count: > 0 }) { var componentId = subject.Component.Purl ?? subject.Component.Id; if (!MatchesAny(componentId, Packages)) return false; } // Check vulnerability sources if (VulnerabilitySources is { Count: > 0 }) { var source = subject.Vulnerability.Source ?? ""; if (!MatchesAny(source, VulnerabilitySources)) return false; } return true; } /// /// Checks if this scope covers (is a superset of) another scope. /// public bool Covers(AuthorityScope other) { // A scope covers another if all patterns in other are covered by patterns in this scope // Universal scope (*) covers everything if (Artifacts is { Count: > 0 } && Artifacts.Contains("*")) return true; // Check that we cover all artifact patterns from the other scope if (other.Artifacts is { Count: > 0 }) { if (Artifacts is null || Artifacts.Count == 0) return false; foreach (var pattern in other.Artifacts) { if (!Artifacts.Any(a => PatternCovers(a, pattern))) return false; } } // Check that we cover all package patterns from the other scope if (other.Packages is { Count: > 0 }) { if (Packages is null || Packages.Count == 0) return false; foreach (var pattern in other.Packages) { if (!Packages.Any(p => PatternCovers(p, pattern))) return false; } } // Check vulnerability sources if (other.VulnerabilitySources is { Count: > 0 }) { if (VulnerabilitySources is null || VulnerabilitySources.Count == 0) return false; foreach (var source in other.VulnerabilitySources) { if (!VulnerabilitySources.Any(s => PatternCovers(s, source))) return false; } } return true; } private static bool PatternCovers(string coveringPattern, string coveredPattern) { // Universal pattern covers everything if (coveringPattern == "*") return true; // Exact match if (coveringPattern.Equals(coveredPattern, StringComparison.OrdinalIgnoreCase)) return true; // Prefix pattern (e.g., "pkg:npm/*" covers "pkg:npm/express") if (coveringPattern.EndsWith("/*")) { var prefix = coveringPattern[..^1]; if (coveredPattern.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) return true; // Also check if covered pattern is a more specific prefix pattern if (coveredPattern.EndsWith("/*")) { var otherPrefix = coveredPattern[..^1]; if (otherPrefix.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) return true; } } return false; } private static bool MatchesAny(string value, IReadOnlyList patterns) { foreach (var pattern in patterns) { if (pattern == "*") return true; if (pattern.EndsWith("/*")) { var prefix = pattern[..^1]; if (value.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) return true; } else if (pattern.Equals(value, StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } /// /// Universal scope that covers all subjects. /// public static AuthorityScope Universal { get; } = new() { Artifacts = ["*"], }; } /// /// A principal is an issuer identity with verifiable keys. /// public sealed record Principal { /// /// Principal identifier (URI-like, e.g., "did:web:vendor.example"). /// public required string Id { get; init; } /// /// Principal type for canonical serialization. /// public string Type { get; init; } = "identity"; /// /// Key identifiers for verification. /// public IReadOnlyList? KeyIds { get; init; } /// /// Identity claims (e.g., cert SANs, OIDC subject, org, repo). /// public IReadOnlyDictionary? IdentityClaims { get; init; } /// /// Roles this principal can play. /// public PrincipalRole Roles { get; init; } = PrincipalRole.None; /// /// Display name for human readability. /// public string? DisplayName { get; init; } /// /// An unknown principal used as a fallback when no issuer is specified. /// public static Principal Unknown { get; } = new Principal { Id = "urn:stellaops:principal:unknown", DisplayName = "Unknown" }; } /// /// Trust label computed from policy and verification. /// Affects decision selection without destroying underlying knowledge. /// public sealed record TrustLabel : IComparable { /// /// Cryptographic and identity verification strength. /// public required AssuranceLevel AssuranceLevel { get; init; } /// /// Scope of subjects this trust applies to. /// public required AuthorityScope AuthorityScope { get; init; } /// /// Temporal validity of the assertion. /// public required FreshnessClass Freshness { get; init; } /// /// Strength of attached evidence. /// public required EvidenceClass EvidenceClass { get; init; } /// /// The principal providing this trust. /// public Principal? Principal { get; init; } /// /// Computes an overall trust score for ordering. /// Higher is more trustworthy. /// public int ComputeScore() { // Weighted combination (can be policy-configurable) return (int)AssuranceLevel * 100 + (int)EvidenceClass * 10 + (int)Freshness; } /// /// Compares trust labels by overall score. /// public int CompareTo(TrustLabel? other) { if (other is null) return 1; return ComputeScore().CompareTo(other.ComputeScore()); } /// /// Returns the higher trust label (join operation). /// public static TrustLabel Max(TrustLabel a, TrustLabel b) => a.CompareTo(b) >= 0 ? a : b; /// /// Returns the lower trust label (meet operation). /// public static TrustLabel Min(TrustLabel a, TrustLabel b) => a.CompareTo(b) <= 0 ? a : b; /// /// Creates a minimal trust label (unsigned, no evidence). /// public static TrustLabel Minimal { get; } = new() { AssuranceLevel = AssuranceLevel.A0_Unsigned, AuthorityScope = new AuthorityScope(), Freshness = FreshnessClass.Unknown, EvidenceClass = EvidenceClass.E0_StatementOnly, }; }