- Introduced a new document outlining the inline DSSE provenance for SBOM, VEX, scan, and derived events. - Defined the Mongo schema for event patches, including key fields for provenance and trust verification. - Documented the write path for ingesting provenance metadata and backfilling historical events. - Created CI/CD snippets for uploading DSSE attestations and generating provenance metadata. - Established Mongo indexes for efficient provenance queries and provided query recipes for various use cases. - Outlined policy gates for managing VEX decisions based on provenance verification. - Included UI nudges for displaying provenance information and implementation tasks for future enhancements. --- Implement reachability lattice and scoring model - Developed a comprehensive document detailing the reachability lattice and scoring model. - Defined core types for reachability states, evidence, and mitigations with corresponding C# models. - Established a scoring policy with base score contributions from various evidence classes. - Mapped reachability states to VEX gates and provided a clear overview of evidence sources. - Documented the event graph schema for persisting reachability data in MongoDB. - Outlined the integration of runtime probes for evidence collection and defined a roadmap for future tasks. --- Introduce uncertainty states and entropy scoring - Created a draft document for tracking uncertainty states and their impact on risk scoring. - Defined core uncertainty states with associated entropy values and evidence requirements. - Established a schema for storing uncertainty states alongside findings. - Documented the risk score calculation incorporating uncertainty and its effect on final risk assessments. - Provided policy guidelines for handling uncertainty in decision-making processes. - Outlined UI guidelines for displaying uncertainty information and suggested remediation actions. --- Add Ruby package inventory management - Implemented Ruby package inventory management with corresponding data models and storage mechanisms. - Created C# records for Ruby package inventory, artifacts, provenance, and runtime details. - Developed a repository for managing Ruby package inventory documents in MongoDB. - Implemented a service for storing and retrieving Ruby package inventories. - Added unit tests for the Ruby package inventory store to ensure functionality and data integrity.
9.1 KiB
Build-Time DSSE Attestation Walkthrough
Status: Draft — aligns with the November 2025 advisory “Embed in-toto attestations (DSSE-wrapped) into .NET 10/C# builds.”
Owners: Attestor Guild · DevOps Guild · Docs Guild.
This guide shows how to emit signed, in-toto compliant DSSE envelopes for every container build step (scan → package → push) using Stella Ops Authority keys. The same primitives power our Signer/Attestor services, but this walkthrough targets developer pipelines (GitHub/GitLab, dotnet builds, container scanners).
1. Concepts refresher
| Term | Meaning |
|---|---|
| In-toto Statement | JSON document describing what happened (predicate) to which artifact (subject). |
| DSSE | Dead Simple Signing Envelope: wraps the statement, base64 payload, and signatures. |
| Authority Signer | Stella Ops client that signs data via file-based keys, HSM/KMS, or keyless Fulcio certs. |
| PAE | Pre-Authentication Encoding: canonical “DSSEv1 ” byte layout that is signed. |
Requirements:
- .NET 10 SDK (preview) for C# helper code.
- Authority key material (dev: file-based Ed25519; prod: Authority/KMS signer).
- Artifact digest (e.g.,
pkg:docker/registry/app@sha256:...) per step.
2. Helpers (drop-in library)
Create src/StellaOps.Attestation with:
public sealed record InTotoStatement(
string _type,
IReadOnlyList<Subject> subject,
string predicateType,
object predicate);
public sealed record Subject(string name, IReadOnlyDictionary<string,string> digest);
public sealed record DsseEnvelope(
string payloadType,
string payload,
IReadOnlyList<Signature> signatures);
public sealed record Signature(string keyid, string sig);
public interface IAuthoritySigner
{
Task<string> GetKeyIdAsync(CancellationToken ct = default);
Task<byte[]> SignAsync(ReadOnlyMemory<byte> pae, CancellationToken ct = default);
}
DSSE helper:
public static class DsseHelper
{
public static async Task<DsseEnvelope> WrapAsync(
InTotoStatement statement,
IAuthoritySigner signer,
string payloadType = "application/vnd.in-toto+json",
CancellationToken ct = default)
{
var payloadBytes = JsonSerializer.SerializeToUtf8Bytes(statement,
new JsonSerializerOptions { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull });
var pae = PreAuthEncode(payloadType, payloadBytes);
var signature = await signer.SignAsync(pae, ct).ConfigureAwait(false);
var keyId = await signer.GetKeyIdAsync(ct).ConfigureAwait(false);
return new DsseEnvelope(
payloadType,
Convert.ToBase64String(payloadBytes),
new[] { new Signature(keyId, Convert.ToBase64String(signature)) });
}
private static byte[] PreAuthEncode(string payloadType, byte[] payload)
{
static byte[] Cat(params byte[][] parts)
{
var len = parts.Sum(p => p.Length);
var buf = new byte[len];
var offset = 0;
foreach (var part in parts)
{
Buffer.BlockCopy(part, 0, buf, offset, part.Length);
offset += part.Length;
}
return buf;
}
var header = Encoding.UTF8.GetBytes("DSSEv1");
var pt = Encoding.UTF8.GetBytes(payloadType);
var lenPt = Encoding.UTF8.GetBytes(pt.Length.ToString(CultureInfo.InvariantCulture));
var lenPayload = Encoding.UTF8.GetBytes(payload.Length.ToString(CultureInfo.InvariantCulture));
var space = Encoding.UTF8.GetBytes(" ");
return Cat(header, space, lenPt, space, pt, space, lenPayload, space, payload);
}
}
Authority signer examples:
/dev (file Ed25519):
public sealed class FileEd25519Signer : IAuthoritySigner, IDisposable
{
private readonly Ed25519 _ed;
private readonly string _keyId;
public FileEd25519Signer(byte[] privateKeySeed, string keyId)
{
_ed = new Ed25519(privateKeySeed);
_keyId = keyId;
}
public Task<string> GetKeyIdAsync(CancellationToken ct) => Task.FromResult(_keyId);
public Task<byte[]> SignAsync(ReadOnlyMemory<byte> pae, CancellationToken ct)
=> Task.FromResult(_ed.Sign(pae.Span.ToArray()));
public void Dispose() => _ed.Dispose();
}
Prod (Authority KMS):
Reuse the existing StellaOps.Signer.KmsSigner adapter—wrap it behind IAuthoritySigner.
3. Emitting attestations per step
Subject helper:
static Subject ImageSubject(string imageDigest) => new(
name: imageDigest,
digest: new Dictionary<string,string>{{"sha256", imageDigest.Replace("sha256:", "", StringComparison.Ordinal)}});
3.1 Scan
var scanStmt = new InTotoStatement(
_type: "https://in-toto.io/Statement/v1",
subject: new[]{ ImageSubject(imageDigest) },
predicateType: "https://stella.ops/predicates/scanner-evidence/v1",
predicate: new {
scanner = "StellaOps.Scanner 0.9.0",
findingsSha256 = scanResultsHash,
startedAt = startedIso,
finishedAt = finishedIso,
rulePack = "lattice:default@2025-11-01"
});
var scanEnvelope = await DsseHelper.WrapAsync(scanStmt, signer);
await File.WriteAllTextAsync("artifacts/attest-scan.dsse.json", JsonSerializer.Serialize(scanEnvelope));
3.2 Package (SLSA provenance)
var pkgStmt = new InTotoStatement(
"https://in-toto.io/Statement/v1",
new[]{ ImageSubject(imageDigest) },
"https://slsa.dev/provenance/v1",
new {
builder = new { id = "stella://builder/dockerfile" },
buildType = "dockerfile/v1",
invocation = new { configSource = repoUrl, entryPoint = dockerfilePath },
materials = new[] { new { uri = repoUrl, digest = new { git = gitSha } } }
});
var pkgEnvelope = await DsseHelper.WrapAsync(pkgStmt, signer);
await File.WriteAllTextAsync("artifacts/attest-package.dsse.json", JsonSerializer.Serialize(pkgEnvelope));
3.3 Push
var pushStmt = new InTotoStatement(
"https://in-toto.io/Statement/v1",
new[]{ ImageSubject(imageDigest) },
"https://stella.ops/predicates/push/v1",
new { registry = registryUrl, repository = repoName, tags, pushedAt = DateTimeOffset.UtcNow });
var pushEnvelope = await DsseHelper.WrapAsync(pushStmt, signer);
await File.WriteAllTextAsync("artifacts/attest-push.dsse.json", JsonSerializer.Serialize(pushEnvelope));
4. CI integration
4.1 GitLab example
.attest-template: &attest
image: mcr.microsoft.com/dotnet/sdk:10.0-preview
before_script:
- dotnet build src/StellaOps.Attestation/StellaOps.Attestation.csproj
variables:
AUTHORITY_KEY_FILE: "$CI_PROJECT_DIR/secrets/ed25519.key"
IMAGE_DIGEST: "$CI_REGISTRY_IMAGE@${CI_COMMIT_SHA}"
attest:scan:
stage: scan
script:
- dotnet run --project tools/StellaOps.Attestor.Tool -- step scan --subject "$IMAGE_DIGEST" --out artifacts/attest-scan.dsse.json
artifacts:
paths: [artifacts/attest-scan.dsse.json]
attest:package:
stage: package
script:
- dotnet run --project tools/StellaOps.Attestor.Tool -- step package --subject "$IMAGE_DIGEST" --out artifacts/attest-package.dsse.json
attest:push:
stage: push
script:
- dotnet run --project tools/StellaOps.Attestor.Tool -- step push --subject "$IMAGE_DIGEST" --registry "$CI_REGISTRY" --tags "$CI_COMMIT_REF_NAME"
4.2 GitHub Actions snippet
jobs:
attest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with: { dotnet-version: '10.0.x' }
- name: Build attestor helpers
run: dotnet build src/StellaOps.Attestation/StellaOps.Attestation.csproj
- name: Emit scan attestation
run: dotnet run --project tools/StellaOps.Attestor.Tool -- step scan --subject "${{ env.IMAGE_DIGEST }}" --out artifacts/attest-scan.dsse.json
env:
AUTHORITY_KEY_REF: ${{ secrets.AUTHORITY_KEY_REF }}
5. Verification
stella attest verify --file artifacts/attest-scan.dsse.json(CLI planned underDSSE-CLI-401-021).- Manual validation:
- Base64 decode payload → ensure
_type=https://in-toto.io/Statement/v1,subject[].digest.sha256matches artifact. - Recompute PAE and verify signature with the Authority public key.
- Attach envelope to Rekor (optional) via existing Attestor API.
- Base64 decode payload → ensure
6. Storage conventions
Store DSSE files next to build outputs:
artifacts/
attest-scan.dsse.json
attest-package.dsse.json
attest-push.dsse.json
Include the SHA-256 digest of each envelope in promotion manifests (docs/release/promotion-attestations.md) so downstream verifiers can trace chain of custody.
7. References
- In-toto Statement v1
- DSSE specification
docs/modules/signer/architecture.mddocs/modules/attestor/architecture.mddocs/release/promotion-attestations.md
Keep this file updated alongside DSSE-LIB-401-020 and DSSE-CLI-401-021. When the bench repo publishes sample attestations, link them here.