Files
git.stella-ops.org/docs/modules/scanner/signed-sbom-archive-spec.md

8.1 KiB

Signed SBOM Archive Specification

Version: 1.0.0 Status: Draft Last Updated: 2026-01-15

Overview

This specification defines a self-contained, cryptographically signed SBOM archive format that bundles:

  • The SBOM document (SPDX or CycloneDX)
  • DSSE signature envelope
  • Verification materials (certificates, transparency proofs)
  • Metadata (tool versions, timestamps)
  • Offline verification resources

Archive Structure

signed-sbom-{digest_short}-{timestamp}.tar.gz
|
+-- sbom.spdx.json            # OR sbom.cdx.json (CycloneDX)
+-- sbom.dsse.json            # DSSE envelope containing signature
+-- manifest.json             # Archive inventory with hashes
+-- metadata.json             # Generation metadata
+-- certs/
|   +-- signing-cert.pem      # Signing certificate
|   +-- signing-chain.pem     # Full certificate chain
|   +-- fulcio-root.pem       # Fulcio root CA (for keyless)
+-- rekor-proof/              # Optional: transparency log proof
|   +-- inclusion-proof.json
|   +-- checkpoint.sig
|   +-- rekor-public.pem
+-- schemas/                  # Bundled validation schemas
|   +-- spdx-2.3.schema.json
|   +-- spdx-3.0.1.schema.json
|   +-- cyclonedx-1.7.schema.json
|   +-- dsse.schema.json
+-- VERIFY.md                 # Human-readable verification guide

File Specifications

sbom.spdx.json / sbom.cdx.json

The primary SBOM document in either:

  • SPDX: Versions 2.3 or 3.0.1 (JSON format)
  • CycloneDX: Versions 1.4, 1.5, 1.6, or 1.7 (JSON format)

Requirements:

  • UTF-8 encoding without BOM
  • Canonical JSON formatting (RFC 8785 compliant)
  • No trailing whitespace or newlines

sbom.dsse.json

DSSE envelope containing the SBOM signature:

{
  "payloadType": "application/vnd.stellaops.sbom+json",
  "payload": "<base64-encoded-sbom>",
  "signatures": [
    {
      "keyid": "SHA256:abc123...",
      "sig": "<base64-encoded-signature>"
    }
  ]
}

manifest.json

Archive inventory with integrity hashes:

{
  "schemaVersion": "1.0.0",
  "archiveId": "signed-sbom-abc123-20260115T123456Z",
  "generatedAt": "2026-01-15T12:34:56Z",
  "files": [
    {
      "path": "sbom.spdx.json",
      "sha256": "abc123...",
      "size": 45678,
      "mediaType": "application/spdx+json"
    },
    {
      "path": "sbom.dsse.json",
      "sha256": "def456...",
      "size": 1234,
      "mediaType": "application/vnd.dsse+json"
    }
  ],
  "merkleRoot": "sha256:789abc...",
  "totalFiles": 12,
  "totalSize": 98765
}

metadata.json

Generation and tool metadata:

{
  "schemaVersion": "1.0.0",
  "stellaOps": {
    "suiteVersion": "2027.Q1",
    "scannerVersion": "1.2.3",
    "scannerDigest": "sha256:scanner-image-digest",
    "signerVersion": "1.0.0",
    "sbomServiceVersion": "1.1.0"
  },
  "generation": {
    "timestamp": "2026-01-15T12:34:56Z",
    "hlcTimestamp": "1737000000000000000",
    "operator": "build@company.com"
  },
  "input": {
    "imageRef": "registry.company.com/app:v1.0.0",
    "imageDigest": "sha256:image-digest-here",
    "platform": "linux/amd64"
  },
  "sbom": {
    "format": "spdx-2.3",
    "componentCount": 142,
    "packageCount": 89,
    "fileCount": 1247
  },
  "signature": {
    "type": "keyless",
    "issuer": "https://accounts.google.com",
    "subject": "build@company.com",
    "signedAt": "2026-01-15T12:34:57Z"
  },
  "reproducibility": {
    "deterministic": true,
    "expectedDigest": "sha256:expected-sbom-digest"
  }
}

VERIFY.md

Human-readable verification instructions:

# SBOM Archive Verification

## Quick Verification

```bash
# Verify archive integrity
sha256sum -c <<EOF
abc123...  sbom.spdx.json
def456...  sbom.dsse.json
EOF

# Verify signature using cosign
cosign verify-blob \
  --signature sbom.dsse.json \
  --certificate certs/signing-cert.pem \
  --certificate-chain certs/signing-chain.pem \
  sbom.spdx.json

Offline Verification

# Using bundled Fulcio root
cosign verify-blob \
  --signature sbom.dsse.json \
  --certificate certs/signing-cert.pem \
  --certificate-chain certs/signing-chain.pem \
  --certificate-oidc-issuer https://accounts.google.com \
  --offline \
  sbom.spdx.json

Rekor Inclusion Proof

# Verify transparency log inclusion
rekor-cli verify \
  --artifact sbom.spdx.json \
  --signature sbom.dsse.json \
  --public-key certs/signing-cert.pem \
  --rekor-server https://rekor.sigstore.dev

## Cryptographic Requirements

### Hash Algorithms

| Purpose | Algorithm | Format |
|---------|-----------|--------|
| File hashes | SHA-256 | Lowercase hex |
| Merkle tree | SHA-256 | Lowercase hex with `sha256:` prefix |
| Certificate fingerprint | SHA-256 | Uppercase hex with colons |

### Signature Algorithms

Supported signature algorithms:
- **ECDSA-P256**: Recommended for keyless (Fulcio)
- **ECDSA-P384**: High-security environments
- **RSA-PSS-4096**: Legacy compatibility
- **Ed25519**: High-performance signing

### DSSE Envelope

DSSE (Dead Simple Signing Envelope) per specification:
- PAE (Pre-Authentication Encoding) for signing
- Base64 encoding for payload and signatures
- Multiple signatures supported for threshold signing

## Verification Process

### Step 1: Archive Integrity

```python
# Verify tar.gz integrity
import tarfile
import hashlib

with tarfile.open("signed-sbom.tar.gz", "r:gz") as tar:
    manifest = json.load(tar.extractfile("manifest.json"))

    for file_entry in manifest["files"]:
        content = tar.extractfile(file_entry["path"]).read()
        actual_hash = hashlib.sha256(content).hexdigest()
        assert actual_hash == file_entry["sha256"]

Step 2: Signature Verification

# Verify DSSE signature
from sigstore.verify import Verifier

verifier = Verifier.production()
result = verifier.verify(
    artifact=sbom_content,
    signature=dsse_envelope,
    certificate=signing_cert
)
assert result.success

Step 3: Certificate Chain Validation

# Validate certificate chain
from cryptography import x509

chain = load_certificate_chain("certs/signing-chain.pem")
root = load_certificate("certs/fulcio-root.pem")

validate_chain(chain, root)

Step 4: Transparency Log (Optional)

# Verify Rekor inclusion
from rekor_client import verify_inclusion

result = verify_inclusion(
    artifact_hash=sbom_hash,
    proof=inclusion_proof,
    checkpoint=checkpoint
)
assert result.verified

Compatibility

SBOM Formats

Format Version Status
SPDX 2.3 Supported
SPDX 3.0.1 Supported
CycloneDX 1.4 Supported
CycloneDX 1.5 Supported
CycloneDX 1.6 Supported
CycloneDX 1.7 Supported

Compression

Format Extension Status
gzip .tar.gz Default
zstd .tar.zst Recommended (smaller)
none .tar Supported

API Endpoint

GET /scans/{scanId}/exports/signed-sbom-archive

Query Parameters:

Parameter Type Default Description
format string spdx-2.3 SBOM format
compression string gzip Compression type
includeRekor bool true Include Rekor proof
includeSchemas bool true Bundle JSON schemas

Response Headers:

Header Description
Content-Type application/gzip or application/zstd
Content-Disposition attachment; filename="signed-sbom-{digest}.tar.gz"
X-SBOM-Digest SHA-256 of SBOM content
X-Archive-Merkle-Root Merkle root of archive
X-Rekor-Log-Index Rekor log index (if applicable)

Security Considerations

  1. Determinism: All outputs must be reproducible given same inputs
  2. Canonicalization: JSON must be RFC 8785 canonical before signing
  3. Time sources: Use injected TimeProvider, not system clock
  4. Key material: Never include private keys in archives
  5. Offline verification: Bundle all necessary verification materials