Proof Spine Assembly Algorithm

Sprint: SPRINT_0501_0004_0001 Module: Attestor / ProofChain

Overview

The Proof Spine is the cryptographic backbone of StellaOps’ proof chain. It aggregates evidence, reasoning, and VEX statements into a single merkle-rooted bundle that can be verified independently.

Architecture

┌─────────────────────────────────────────────────────────────────────────────┐
│                         PROOF SPINE STRUCTURE                               │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐    │
│  │ SBOMEntryID  │  │ EvidenceID[] │  │ ReasoningID  │  │ VEXVerdictID │    │
│  │ (leaf 0)     │  │ (leaves 1-N) │  │ (leaf N+1)   │  │ (leaf N+2)   │    │
│  └──────┬───────┘  └──────┬───────┘  └──────┬───────┘  └──────┬───────┘    │
│         │                 │                 │                 │            │
│         └─────────────────┴─────────────────┴─────────────────┘            │
│                                    │                                        │
│                                    ▼                                        │
│                    ┌───────────────────────────────┐                        │
│                    │     MERKLE TREE BUILDER       │                        │
│                    │  - SHA-256 hash function      │                        │
│                    │  - Lexicographic sorting      │                        │
│                    │  - Power-of-2 padding         │                        │
│                    └───────────────┬───────────────┘                        │
│                                    │                                        │
│                                    ▼                                        │
│                    ┌───────────────────────────────┐                        │
│                    │      ProofBundleID (Root)     │                        │
│                    │  sha256:<64-hex-chars>        │                        │
│                    └───────────────────────────────┘                        │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

Algorithm Specification

Input

ParameterTypeDescription
sbomEntryIdstringContent-addressed ID of the SBOM entry
evidenceIdsstring[]Array of evidence statement IDs
reasoningIdstringID of the reasoning/policy match statement
vexVerdictIdstringID of the VEX verdict statement

Output

ParameterTypeDescription
proofBundleIdstringMerkle root in format sha256:<64-hex>

Pseudocode

FUNCTION BuildProofBundleMerkle(sbomEntryId, evidenceIds[], reasoningId, vexVerdictId):
    
    // Step 1: Prepare leaves in deterministic order
    leaves = []
    leaves.append(SHA256(UTF8.GetBytes(sbomEntryId)))
    
    // Step 2: Sort evidence IDs lexicographically
    sortedEvidenceIds = evidenceIds.Sort(StringComparer.Ordinal)
    FOR EACH evidenceId IN sortedEvidenceIds:
        leaves.append(SHA256(UTF8.GetBytes(evidenceId)))
    
    leaves.append(SHA256(UTF8.GetBytes(reasoningId)))
    leaves.append(SHA256(UTF8.GetBytes(vexVerdictId)))
    
    // Step 3: Pad to power of 2 (duplicate last leaf)
    WHILE NOT IsPowerOfTwo(leaves.Length):
        leaves.append(leaves[leaves.Length - 1])
    
    // Step 4: Build tree bottom-up
    currentLevel = leaves
    WHILE currentLevel.Length > 1:
        nextLevel = []
        FOR i = 0 TO currentLevel.Length STEP 2:
            left = currentLevel[i]
            right = currentLevel[i + 1]
            parent = SHA256(left || right)  // Concatenate then hash
            nextLevel.append(parent)
        currentLevel = nextLevel
    
    // Step 5: Return root as formatted ID
    RETURN "sha256:" + HexEncode(currentLevel[0])

Determinism Invariants

InvariantRuleRationale
Evidence OrderingLexicographic (byte comparison)Reproducible across platforms
Hash FunctionSHA-256 onlyNo algorithm negotiation
PaddingDuplicate last leafNot zeros, preserves tree structure
ConcatenationLeft || RightConsistent ordering
String EncodingUTF-8Cross-platform compatibility
ID Formatsha256:<lowercase-hex>Canonical representation

Example

Input

{
  "sbomEntryId": "sha256:abc123...",
  "evidenceIds": [
    "sha256:evidence-cve-2024-0001...",
    "sha256:evidence-reachability...",
    "sha256:evidence-sbom-component..."
  ],
  "reasoningId": "sha256:reasoning-policy...",
  "vexVerdictId": "sha256:vex-not-affected..."
}

Processing

  1. Leaf 0: SHA256("sha256:abc123...") → SBOM
  2. Leaf 1: SHA256("sha256:evidence-cve-2024-0001...") → Evidence (sorted first)
  3. Leaf 2: SHA256("sha256:evidence-reachability...") → Evidence
  4. Leaf 3: SHA256("sha256:evidence-sbom-component...") → Evidence
  5. Leaf 4: SHA256("sha256:reasoning-policy...") → Reasoning
  6. Leaf 5: SHA256("sha256:vex-not-affected...") → VEX
  7. Padding: Duplicate leaf 5 to get 8 leaves (power of 2)

Tree Structure

                    ROOT
                   /    \
                  H1     H2
                 / \    / \
               H3   H4  H5  H6
              / \  / \ / \ / \
             L0 L1 L2 L3 L4 L5 L5 L5  (padded)

Output

sha256:7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069

Cross-Platform Verification

Test Vector

For cross-platform compatibility testing, use this known test vector:

Input:

{
  "sbomEntryId": "sha256:0000000000000000000000000000000000000000000000000000000000000001",
  "evidenceIds": [
    "sha256:0000000000000000000000000000000000000000000000000000000000000002",
    "sha256:0000000000000000000000000000000000000000000000000000000000000003"
  ],
  "reasoningId": "sha256:0000000000000000000000000000000000000000000000000000000000000004",
  "vexVerdictId": "sha256:0000000000000000000000000000000000000000000000000000000000000005"
}

All implementations (C#, Go, Rust, TypeScript) must produce the same root hash.

Verification

To verify a proof bundle:

  1. Obtain all constituent statements (SBOM, Evidence, Reasoning, VEX)
  2. Extract their content-addressed IDs
  3. Re-compute the merkle root using the algorithm above
  4. Compare with the claimed proofBundleId

If the roots match, the bundle is valid and all statements are bound to this proof.

API

C# Interface

public interface IProofSpineAssembler
{
    /// <summary>
    /// Assembles a proof spine from its constituent statements.
    /// </summary>
    ProofSpineResult Assemble(ProofSpineInput input);
}

public record ProofSpineInput
{
    public required string SbomEntryId { get; init; }
    public required IReadOnlyList<string> EvidenceIds { get; init; }
    public required string ReasoningId { get; init; }
    public required string VexVerdictId { get; init; }
}

public record ProofSpineResult
{
    public required string ProofBundleId { get; init; }
    public required byte[] MerkleRoot { get; init; }
    public required IReadOnlyList<byte[]> LeafHashes { get; init; }
}