335 lines
8.1 KiB
Markdown
335 lines
8.1 KiB
Markdown
# 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:
|
|
|
|
```json
|
|
{
|
|
"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:
|
|
|
|
```json
|
|
{
|
|
"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:
|
|
|
|
```json
|
|
{
|
|
"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:
|
|
|
|
```markdown
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```python
|
|
# 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
|
|
|
|
```python
|
|
# 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)
|
|
|
|
```python
|
|
# 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
|
|
|
|
## Related Specifications
|
|
|
|
- [DSSE Specification](https://github.com/secure-systems-lab/dsse)
|
|
- [Sigstore Signing Specification](https://docs.sigstore.dev)
|
|
- [SPDX Specification](https://spdx.github.io/spdx-spec/)
|
|
- [CycloneDX Specification](https://cyclonedx.org/specification/)
|
|
- [Rekor Transparency Log](https://docs.sigstore.dev/rekor/)
|