# in-toto Link Generation Guide This guide explains how to use StellaOps to generate in-toto link attestations for supply chain provenance. ## Overview in-toto links record the **materials** (inputs), **products** (outputs), and **command** executed for each step in a supply chain. They are essential for: - **SLSA compliance** - SLSA levels require provenance attestations - **Supply chain transparency** - Prove what went into a build/scan - **Audit trails** - Forensic analysis of build processes - **Policy enforcement** - Verify required steps were executed by authorized functionaries ## Quick Start ### CLI Usage Create an in-toto link for a scan operation: ```bash stella attest link \ --step scan \ --material "oci://docker.io/library/nginx@sha256:abc123" \ --product "file://sbom.cdx.json=sha256:def456" \ --product "file://vulns.json=sha256:789xyz" \ --command stella scan --image nginx:1.25 \ --return-value 0 \ --output scan-link.json ``` ### API Usage ```bash curl -X POST http://localhost:5050/api/v1/attestor/links \ -H "Content-Type: application/json" \ -d '{ "stepName": "scan", "materials": [ {"uri": "oci://docker.io/library/nginx@sha256:abc123", "sha256": "abc123..."} ], "products": [ {"uri": "file://sbom.cdx.json", "sha256": "def456..."} ], "command": ["stella", "scan", "--image", "nginx:1.25"], "returnValue": 0 }' ``` ## Concepts ### Materials Materials are **input artifacts** consumed by a supply chain step. Examples: - Container images being scanned - Source code being built - Dependencies being fetched Materials are specified as URIs with cryptographic digests: - `oci://registry.example.com/app@sha256:...` - Container image - `git://github.com/org/repo@abc123` - Git commit - `file://src/main.rs` - Local file ### Products Products are **output artifacts** produced by a supply chain step. Examples: - SBOMs generated from scanning - Vulnerability reports - Built binaries - Signed releases Products are also specified as URIs with digests: - `file://sbom.cdx.json` - SBOM file - `file://vulns.json` - Vulnerability report - `oci://registry.example.com/app:v1.0` - Built image ### Commands The command captures what was executed during the step: - The executable and all arguments - Used for audit and reproducibility ### By-Products Additional metadata about step execution: - `return-value` - Exit code of the command - `stdout` - Captured standard output (optional) - `stderr` - Captured standard error (optional) ### Environment Environment variables captured during execution. Useful for: - Recording build tool versions - Capturing CI/CD context (commit SHA, build number) - Documenting configuration ## Data Model ### InTotoLink Structure ```json { "_type": "https://in-toto.io/Statement/v1", "subject": [ { "name": "file://sbom.cdx.json", "digest": { "sha256": "..." } } ], "predicateType": "https://in-toto.io/Link/v1", "predicate": { "name": "scan", "command": ["stella", "scan", "--image", "nginx:1.25"], "materials": [ { "uri": "oci://docker.io/library/nginx@sha256:...", "digest": { "sha256": "..." } } ], "products": [ { "uri": "file://sbom.cdx.json", "digest": { "sha256": "..." } } ], "byproducts": { "return-value": 0 }, "environment": { "STELLAOPS_VERSION": "2026.01" } } } ``` ### DSSE Envelope Links are wrapped in Dead Simple Signing Envelopes (DSSE): ```json { "payloadType": "application/vnd.in-toto+json", "payload": "", "signatures": [ { "keyid": "key-identifier", "sig": "" } ] } ``` ## Programmatic Usage ### Using LinkBuilder (Fluent API) ```csharp using StellaOps.Attestor.Core.InToto; var link = new LinkBuilder("scan") .AddMaterial("oci://nginx:1.25@sha256:abc123", new ArtifactDigests { Sha256 = "abc123..." }) .AddProduct("file://sbom.cdx.json", new ArtifactDigests { Sha256 = "def456..." }) .WithCommand("stella", "scan", "--image", "nginx:1.25") .WithReturnValue(0) .WithEnvironment("CI", "true") .Build(); // Serialize to JSON var json = link.ToJson(indented: true); ``` ### Using LinkRecorder (Step Recording) ```csharp using StellaOps.Attestor.Core.InToto; var recorder = serviceProvider.GetRequiredService(); // Record a step with automatic digest computation var link = await recorder.RecordStepAsync( stepName: "build", action: async () => { // Execute your build step await BuildAsync(); return 0; // return value }, materials: new[] { MaterialSpec.WithLocalPath("git://repo", "/path/to/source") }, products: new[] { ProductSpec.File("/path/to/output/app.tar.gz") }, cancellationToken); ``` ### Using IInTotoLinkSigningService ```csharp using StellaOps.Attestor.Core.InToto; var signingService = serviceProvider.GetRequiredService(); // Sign a link var result = await signingService.SignLinkAsync( link, new InTotoLinkSigningOptions { KeyId = "my-signing-key", SubmitToRekor = true, CallerSubject = "build-agent@example.com", CallerAudience = "stellaops", CallerClientId = "build-system" }, cancellationToken); // result.Envelope contains the signed DSSE envelope // result.RekorEntry contains the transparency log entry (if submitted) ``` ## Layout Verification Layouts define the expected steps, their order, and required functionaries (signers). ### Defining a Layout ```json { "steps": [ { "name": "build", "expectedMaterials": ["git://*"], "expectedProducts": ["file://dist/*"], "threshold": 1 }, { "name": "scan", "expectedMaterials": ["oci://*"], "expectedProducts": ["file://sbom.*", "file://vulns.*"], "threshold": 1 }, { "name": "sign", "expectedMaterials": ["file://dist/*"], "expectedProducts": ["file://dist/*.sig"], "threshold": 2 } ], "keys": { "builder-key-1": { "allowedSteps": ["build"] }, "scanner-key-1": { "allowedSteps": ["scan"] }, "signer-key-1": { "allowedSteps": ["sign"] }, "signer-key-2": { "allowedSteps": ["sign"] } } } ``` ### Verifying Links Against a Layout ```csharp using StellaOps.Attestor.Core.InToto.Layout; var verifier = serviceProvider.GetRequiredService(); var result = verifier.Verify( layout, signedLinks, trustedKeys); if (!result.Success) { foreach (var violation in result.Violations) { Console.WriteLine($"Violation in {violation.StepName}: {violation.Message}"); } } ``` ## Integration Examples ### Scanner Integration When using the scanner, links can be automatically emitted: ```csharp public class ScanService : IInTotoLinkEmitter { public async Task ScanAsync(string imageRef, CancellationToken ct) { // Use extension methods to create specs var material = imageRef.ToMaterialSpec(); // Perform scan... // Create products var sbomProduct = ProductSpec.File(sbomPath, "sbom.cdx.json"); // Record and sign the link var signedLink = await _linkSigningService.RecordAndSignStepAsync( stepName: "scan", command: new[] { "stella", "scan", "--image", imageRef }, materials: new[] { material }, products: new[] { sbomProduct }, options: new InTotoLinkSigningOptions { KeyId = _keyId }, cancellationToken: ct); return new ScanResult { Link = signedLink }; } } ``` ### CI/CD Pipeline Integration Example GitHub Actions workflow: ```yaml jobs: build: steps: - uses: actions/checkout@v4 - name: Build run: make release - name: Create in-toto link run: | stella attest link \ --step build \ --material "git://${{ github.repository }}@${{ github.sha }}" \ --product "file://dist/app.tar.gz=$(sha256sum dist/app.tar.gz | cut -d' ' -f1)" \ --command make release \ --return-value $? \ --env GITHUB_SHA --env GITHUB_RUN_ID \ --key ${{ secrets.SIGNING_KEY }} \ --rekor \ --output build-link.json - name: Upload link uses: actions/upload-artifact@v4 with: name: provenance path: build-link.json ``` ## Best Practices 1. **Always include digests** - Materials and products should have cryptographic digests for integrity verification. 2. **Capture relevant environment** - Include CI/CD context, tool versions, and configuration that affects reproducibility. 3. **Use meaningful step names** - Step names should be consistent across your pipeline for layout verification. 4. **Sign with unique keys per role** - Different functionaries (builders, scanners, signers) should use different keys. 5. **Submit to transparency log** - Use `--rekor` to record links in the Sigstore transparency log for tamper evidence. 6. **Store links with artifacts** - Keep signed links alongside the artifacts they describe for audit purposes. ## Troubleshooting ### Link Validation Fails ``` Error: Product 'file://output.tar' has no digest ``` Ensure all products have digests computed. Use `ProductSpec.WithLocalPath` for automatic computation. ### Layout Verification Fails ``` Violation in sign: Threshold not met (1 of 2 required signatures) ``` The layout requires multiple signatures for the step. Collect more signed links from authorized functionaries. ### Signature Verification Fails ``` Error: Signature verification failed for key 'unknown-key' ``` Ensure the signing key is in the trusted keys list for layout verification. ## References - [in-toto Specification](https://github.com/in-toto/attestation) - [in-toto Link Predicate](https://github.com/in-toto/attestation/blob/main/spec/predicates/link.md) - [SLSA Provenance](https://slsa.dev/provenance/v1) - [DSSE Specification](https://github.com/secure-systems-lab/dsse)