docs consolidation

This commit is contained in:
StellaOps Bot
2025-12-25 12:16:13 +02:00
parent deb82b4f03
commit 223843f1d1
34 changed files with 2141 additions and 106 deletions

View File

@@ -26,7 +26,7 @@
* **Signer** (caller) — authenticated via **mTLS** and **Authority** OpToks.
* **Rekor v2** — tilebacked transparency log endpoint(s).
* **MinIO (S3)** — optional archive store for DSSE envelopes & verification bundles.
* **RustFS (S3-compatible)** — optional archive store for DSSE envelopes & verification bundles.
* **PostgreSQL** — local cache of `{uuid, index, proof, artifactSha256, bundleSha256}`; job state; audit.
* **Valkey** — dedupe/idempotency keys and shortlived ratelimit buckets.
* **Licensing Service (optional)** — “endorse” call for crosslog publishing when customer optsin.
@@ -615,7 +615,7 @@ attestor:
connectionString: "Host=postgres;Port=5432;Database=attestor;Username=stellaops;Password=secret"
s3:
enabled: true
endpoint: "http://minio:9000"
endpoint: "http://rustfs:8080"
bucket: "stellaops"
prefix: "attest/"
objectLock: "governance"

View File

@@ -0,0 +1,239 @@
# Sigstore Bundle Format
This document describes the Sigstore Bundle v0.3 format implementation in StellaOps for offline DSSE envelope verification.
## Overview
A Sigstore bundle is a self-contained package that includes all verification material needed to verify a DSSE envelope without network access. This enables offline verification scenarios critical for air-gapped environments.
## Bundle Structure
```json
{
"mediaType": "application/vnd.dev.sigstore.bundle.v0.3+json",
"verificationMaterial": {
"certificate": { ... },
"tlogEntries": [ ... ],
"timestampVerificationData": { ... }
},
"dsseEnvelope": {
"payloadType": "application/vnd.in-toto+json",
"payload": "<base64-encoded-payload>",
"signatures": [
{
"sig": "<base64-encoded-signature>",
"keyid": "<optional-key-id>"
}
]
}
}
```
## Components
### Media Type
- **v0.3**: `application/vnd.dev.sigstore.bundle.v0.3+json` (current)
- **v0.2**: `application/vnd.dev.sigstore.bundle.v0.2+json` (legacy)
### Verification Material
Contains cryptographic material for verification:
| Field | Type | Description |
|-------|------|-------------|
| `certificate` | Object | X.509 signing certificate (keyless signing) |
| `publicKey` | Object | Public key (keyful signing, alternative to certificate) |
| `tlogEntries` | Array | Transparency log entries from Rekor |
| `timestampVerificationData` | Object | RFC 3161 timestamps |
#### Certificate
```json
{
"certificate": {
"rawBytes": "<base64-DER-certificate>"
}
}
```
#### Public Key (alternative to certificate)
```json
{
"publicKey": {
"hint": "<optional-key-hint>",
"rawBytes": "<base64-public-key>"
}
}
```
### Transparency Log Entry
```json
{
"logIndex": "12345",
"logId": {
"keyId": "<base64-log-key-id>"
},
"kindVersion": {
"kind": "dsse",
"version": "0.0.1"
},
"integratedTime": "1703500000",
"inclusionPromise": {
"signedEntryTimestamp": "<base64-SET>"
},
"inclusionProof": {
"logIndex": "12345",
"rootHash": "<base64-merkle-root>",
"treeSize": "100000",
"hashes": ["<base64-hash>", ...],
"checkpoint": {
"envelope": "<checkpoint-note>"
}
},
"canonicalizedBody": "<base64-canonical-body>"
}
```
### DSSE Envelope
Standard DSSE envelope format:
```json
{
"payloadType": "application/vnd.in-toto+json",
"payload": "<base64-encoded-attestation>",
"signatures": [
{
"sig": "<base64-signature>",
"keyid": ""
}
]
}
```
## Usage
### Building a Bundle
```csharp
using StellaOps.Attestor.Bundle.Builder;
using StellaOps.Attestor.Bundle.Models;
var bundle = new SigstoreBundleBuilder()
.WithDsseEnvelope(payloadType, payload, signatures)
.WithCertificate(certificateBytes)
.WithRekorEntry(
logIndex: "12345",
logIdKeyId: logKeyId,
integratedTime: "1703500000",
canonicalizedBody: body)
.WithInclusionProof(inclusionProof)
.Build();
// Serialize to JSON
var json = bundle.BuildJson();
File.WriteAllText("attestation.bundle", json);
```
### Deserializing a Bundle
```csharp
using StellaOps.Attestor.Bundle.Serialization;
var json = File.ReadAllText("attestation.bundle");
var bundle = SigstoreBundleSerializer.Deserialize(json);
// Or with error handling
if (SigstoreBundleSerializer.TryDeserialize(json, out var bundle))
{
// Use bundle
}
```
### Verifying a Bundle
```csharp
using StellaOps.Attestor.Bundle.Verification;
var verifier = new SigstoreBundleVerifier();
var result = await verifier.VerifyAsync(bundle);
if (result.IsValid)
{
Console.WriteLine("Bundle verified successfully");
Console.WriteLine($"DSSE Signature: {result.Checks.DsseSignature}");
Console.WriteLine($"Certificate Chain: {result.Checks.CertificateChain}");
Console.WriteLine($"Inclusion Proof: {result.Checks.InclusionProof}");
}
else
{
foreach (var error in result.Errors)
{
Console.WriteLine($"Error: {error.Code} - {error.Message}");
}
}
```
### Verification Options
```csharp
var options = new BundleVerificationOptions
{
VerifyInclusionProof = true,
VerifyTimestamps = false,
VerificationTime = DateTimeOffset.UtcNow,
TrustedRoots = trustedCertificates
};
var result = await verifier.VerifyAsync(bundle, options);
```
## Verification Checks
| Check | Description |
|-------|-------------|
| `DsseSignature` | Verifies DSSE signature using PAE encoding |
| `CertificateChain` | Validates certificate validity period |
| `InclusionProof` | Verifies Merkle inclusion proof (RFC 6962) |
| `TransparencyLog` | Validates transparency log entry |
| `Timestamp` | Verifies RFC 3161 timestamps (optional) |
## Error Codes
| Code | Description |
|------|-------------|
| `InvalidBundleStructure` | Bundle JSON structure is invalid |
| `MissingDsseEnvelope` | DSSE envelope is required |
| `DsseSignatureInvalid` | Signature verification failed |
| `MissingCertificate` | No certificate or public key |
| `CertificateChainInvalid` | Certificate chain validation failed |
| `CertificateExpired` | Certificate has expired |
| `CertificateNotYetValid` | Certificate not yet valid |
| `InclusionProofInvalid` | Merkle proof verification failed |
| `RootHashMismatch` | Computed root doesn't match expected |
## Cosign Compatibility
Bundles created by StellaOps are compatible with cosign verification:
```bash
# Verify bundle with cosign
cosign verify-attestation \
--bundle attestation.bundle \
--certificate-identity="subject@example.com" \
--certificate-oidc-issuer="https://issuer.example.com" \
--type=slsaprovenance \
registry.example.com/image:tag
```
See [Cosign Verification Examples](./cosign-verification-examples.md) for more details.
## References
- [Sigstore Bundle Specification](https://github.com/sigstore/cosign/blob/main/specs/BUNDLE_SPEC.md)
- [Sigstore Protobuf Specs](https://github.com/sigstore/protobuf-specs)
- [DSSE Specification](https://github.com/secure-systems-lab/dsse)
- [RFC 6962 - Certificate Transparency](https://www.rfc-editor.org/rfc/rfc6962)

View File

@@ -0,0 +1,374 @@
# Cosign Verification Examples
This document provides examples for verifying StellaOps DSSE attestations using Sigstore cosign.
## Prerequisites
### Install Cosign
```bash
# macOS
brew install cosign
# Linux (download latest release)
curl -sSfL https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64 -o cosign
chmod +x cosign
sudo mv cosign /usr/local/bin/
# Windows (download from releases page)
# https://github.com/sigstore/cosign/releases
# Verify installation
cosign version
```
### Required Files
| File | Description |
|------|-------------|
| `attestation.json` | DSSE envelope exported from StellaOps |
| `public.key` | Public key for keyful verification |
| `trusted_root.json` | Sigstore TUF root for keyless verification |
## Export Attestation from StellaOps
```bash
# Export attestation for a specific artifact
stellaops attestation export \
--artifact sha256:abc123... \
--output attestation.json
# Export with certificate chain
stellaops attestation export \
--artifact sha256:abc123... \
--include-certificate-chain \
--output attestation-bundle.json
# Export as Sigstore bundle
stellaops attestation export \
--artifact sha256:abc123... \
--format sigstore-bundle \
--output attestation.sigstore.json
```
## Keyful Verification (KMS/HSM Keys)
### Verify with Public Key
```bash
# Basic verification
cosign verify-attestation \
--key public.key \
--type custom \
sha256:abc123...
# Verify from exported attestation file
cosign verify-attestation \
--key public.key \
--type custom \
--attestation attestation.json \
sha256:abc123...
```
### Verify with KMS Key
```bash
# AWS KMS
cosign verify-attestation \
--key awskms:///arn:aws:kms:us-east-1:123456789:key/abc-123 \
--type custom \
sha256:abc123...
# GCP KMS
cosign verify-attestation \
--key gcpkms://projects/my-project/locations/global/keyRings/my-ring/cryptoKeys/my-key \
--type custom \
sha256:abc123...
# Azure Key Vault
cosign verify-attestation \
--key azurekms://mykeyvault.vault.azure.net/keys/mykey \
--type custom \
sha256:abc123...
# HashiCorp Vault
cosign verify-attestation \
--key hashivault://transit/keys/my-key \
--type custom \
sha256:abc123...
```
## Keyless Verification (Fulcio/OIDC)
### Verify with Certificate Identity
```bash
# Verify with issuer and subject
cosign verify-attestation \
--certificate-identity "signer@example.com" \
--certificate-oidc-issuer "https://accounts.google.com" \
--type custom \
sha256:abc123...
# Verify with identity regex
cosign verify-attestation \
--certificate-identity-regexp ".*@stellaops\.io" \
--certificate-oidc-issuer "https://github.com/login/oauth" \
--type custom \
sha256:abc123...
```
### Verify GitHub Actions Workload Identity
```bash
cosign verify-attestation \
--certificate-identity "https://github.com/org/repo/.github/workflows/build.yml@refs/heads/main" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
--type custom \
sha256:abc123...
```
## Verify Specific Predicate Types
### StellaOps Attestation Types
```bash
# Verify SBOM attestation
cosign verify-attestation \
--key public.key \
--type "https://spdx.dev/Document" \
sha256:abc123...
# Verify SLSA Provenance
cosign verify-attestation \
--key public.key \
--type "https://slsa.dev/provenance/v1" \
sha256:abc123...
# Verify StellaOps scan results
cosign verify-attestation \
--key public.key \
--type "https://stella-ops.org/attestation/scan-results/v1" \
sha256:abc123...
# Verify StellaOps policy evaluation
cosign verify-attestation \
--key public.key \
--type "https://stella-ops.org/attestation/policy-evaluation/v1" \
sha256:abc123...
# Verify graph root attestation
cosign verify-attestation \
--key public.key \
--type "https://stella-ops.org/attestation/graph-root/v1" \
sha256:abc123...
```
## Offline Verification
### Verify with Cached Bundle
```bash
# Verify using a Sigstore bundle (includes certificate and Rekor entry)
cosign verify-attestation \
--bundle attestation.sigstore.json \
--certificate-identity "signer@example.com" \
--certificate-oidc-issuer "https://accounts.google.com" \
sha256:abc123...
```
### Verify with Local TUF Root
```bash
# Initialize TUF root (run once)
cosign initialize --mirror https://tuf-repo.sigstore.dev --root root.json
# Verify using local TUF data
SIGSTORE_ROOT_FILE=trusted_root.json \
cosign verify-attestation \
--certificate-identity "signer@example.com" \
--certificate-oidc-issuer "https://accounts.google.com" \
sha256:abc123...
```
### Air-Gapped Verification
```bash
# 1. On connected machine: download required artifacts
cosign download attestation sha256:abc123... > attestation.json
cosign download signature sha256:abc123... > signature.sig
# 2. Transfer files to air-gapped environment
# 3. On air-gapped machine: verify with public key
cosign verify-attestation \
--key public.key \
--offline \
--type custom \
--attestation attestation.json \
sha256:abc123...
```
## Verify with Policy
### CUE Policy
```cue
// policy.cue
package attestation
predicateType: "https://stella-ops.org/attestation/scan-results/v1"
predicate: {
severity: *"low" | "medium" | "high" | "critical"
vulnerabilities: [...{
id: =~"^CVE-"
severity: !="critical"
}]
}
```
```bash
cosign verify-attestation \
--key public.key \
--type custom \
--policy policy.cue \
sha256:abc123...
```
### Rego Policy
```rego
# policy.rego
package attestation
default allow = false
allow {
input.predicateType == "https://stella-ops.org/attestation/policy-evaluation/v1"
input.predicate.verdict == "PASS"
input.predicate.score >= 7.0
}
```
```bash
cosign verify-attestation \
--key public.key \
--type custom \
--policy policy.rego \
sha256:abc123...
```
## Multi-Signature Verification
```bash
# Verify that multiple signatures are present
cosign verify-attestation \
--key builder.pub \
--type custom \
sha256:abc123... && \
cosign verify-attestation \
--key witness.pub \
--type custom \
sha256:abc123...
```
## Output Formats
### JSON Output
```bash
cosign verify-attestation \
--key public.key \
--type custom \
--output-file verification-result.json \
sha256:abc123...
```
### Text Output with Details
```bash
cosign verify-attestation \
--key public.key \
--type custom \
-v \
sha256:abc123...
```
## Troubleshooting
### Common Errors
| Error | Cause | Solution |
|-------|-------|----------|
| `no matching attestation found` | No attestation attached to image | Verify attestation was uploaded |
| `key verification failed` | Wrong key or corrupted signature | Check key matches signer |
| `certificate expired` | Signing certificate past validity | Use Rekor timestamp verification |
| `OIDC issuer mismatch` | Wrong issuer in verify command | Check certificate's issuer field |
| `predicate type mismatch` | Wrong --type argument | Use correct predicate URI |
### Debug Commands
```bash
# List all attestations on an image
cosign tree sha256:abc123...
# Download and inspect attestation
cosign download attestation sha256:abc123... | jq .
# Verify with verbose output
cosign verify-attestation \
--key public.key \
--type custom \
-v \
sha256:abc123... 2>&1 | tee verify.log
# Check certificate chain
cosign download attestation sha256:abc123... | \
jq -r '.payload' | base64 -d | jq -r '.subject'
```
### Verify Certificate Details
```bash
# Extract and inspect the signing certificate
cosign download attestation sha256:abc123... | \
jq -r '.signatures[0].cert' | base64 -d | \
openssl x509 -noout -text
```
## Integration with CI/CD
### GitHub Actions
```yaml
- name: Verify attestation
uses: sigstore/cosign-installer@main
- name: Verify StellaOps attestation
run: |
cosign verify-attestation \
--certificate-identity "https://github.com/${{ github.repository }}/.github/workflows/build.yml@${{ github.ref }}" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
--type "https://stella-ops.org/attestation/scan-results/v1" \
${{ env.IMAGE_DIGEST }}
```
### GitLab CI
```yaml
verify-attestation:
image: bitnami/cosign:latest
script:
- cosign verify-attestation
--certificate-identity "https://gitlab.com/${CI_PROJECT_PATH}/.gitlab-ci.yml@${CI_COMMIT_REF_NAME}"
--certificate-oidc-issuer "https://gitlab.com"
--type "https://stella-ops.org/attestation/scan-results/v1"
${IMAGE_DIGEST}
```
## Related Documentation
- [DSSE Round-Trip Verification](./dsse-roundtrip-verification.md)
- [Transparency Log Integration](./transparency.md)
- [Air-Gap Operation](./airgap.md)
- [Sigstore Documentation](https://docs.sigstore.dev)

View File

@@ -0,0 +1,292 @@
# DSSE Round-Trip Verification
This document describes the DSSE (Dead Simple Signing Envelope) round-trip verification process in StellaOps, including bundling, offline verification, and cosign compatibility.
## Overview
DSSE round-trip verification ensures that attestations can be:
1. Created and signed
2. Serialized to persistent storage
3. Deserialized and rebundled
4. Verified offline without network access
5. Verified by external tools (cosign)
## Round-Trip Process
### Standard Flow
```
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ Payload │───>│ Sign │───>│ DSSE │
│ (in-toto) │ │ │ │ Envelope │
└─────────────┘ └──────────────┘ └──────┬──────┘
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ Verify │<───│ Deserialize │<───│ Bundle │
│ (Pass) │ │ │ │ (JSON) │
└──────┬──────┘ └──────────────┘ └─────────────┘
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ Re-bundle │───>│ Serialize │───>│ Archive │
│ │ │ │ │ (.tar.gz) │
└──────┬──────┘ └──────────────┘ └─────────────┘
┌─────────────┐
│ Re-verify │
│ (Pass) │
└─────────────┘
```
### Steps
1. **Create Payload**: Build an in-toto statement with subject digests and predicate
2. **Sign**: Create DSSE envelope with signature(s)
3. **Bundle**: Wrap envelope in Sigstore-compatible bundle format
4. **Serialize**: Convert to JSON bytes
5. **Deserialize**: Parse JSON back to bundle structure
6. **Extract**: Get DSSE envelope from bundle
7. **Re-bundle**: Create new bundle from extracted envelope
8. **Re-verify**: Confirm signature validity
## Verification Types
### Signature Verification
Verifies the cryptographic signature against the payload:
```csharp
var result = await signatureService.VerifyAsync(envelope, cancellationToken);
if (!result.IsValid)
{
throw new VerificationException(result.Error);
}
```
### Payload Integrity
Verifies the payload hash matches the signed content:
```csharp
var computedHash = SHA256.HashData(envelope.Payload.Span);
var declaredHash = ParseDigest(envelope.PayloadDigest);
if (!computedHash.SequenceEqual(declaredHash))
{
throw new IntegrityException("Payload hash mismatch");
}
```
### Certificate Chain Validation
For keyless (Fulcio) signatures:
```csharp
var chain = envelope.Signatures[0].CertificateChain;
var result = await certificateValidator.ValidateAsync(chain, options);
// Checks: expiry, revocation, issuer, extended key usage
```
## Determinism Requirements
Round-trip verification requires deterministic serialization:
| Property | Requirement |
|----------|-------------|
| Key Order | Alphabetical |
| Whitespace | No trailing whitespace, single space after colons |
| Encoding | UTF-8, no BOM |
| Numbers | No unnecessary trailing zeros |
| Arrays | Stable ordering |
### Verification
```csharp
var bytes1 = CanonJson.CanonicalizeVersioned(envelope);
var envelope2 = JsonSerializer.Deserialize<DsseEnvelope>(bytes1);
var bytes2 = CanonJson.CanonicalizeVersioned(envelope2);
// bytes1 and bytes2 must be identical
Assert.Equal(bytes1.ToArray(), bytes2.ToArray());
```
## Multi-Signature Support
DSSE envelopes can contain multiple signatures from different signers:
```csharp
var envelope = new DsseEnvelope(
payloadType: "application/vnd.in-toto+json",
payload: canonicalPayload,
signatures:
[
signerA.Sign(canonicalPayload), // Builder signature
signerB.Sign(canonicalPayload), // Witness signature
signerC.Sign(canonicalPayload) // Approver signature
]);
```
Verification checks all signatures:
```csharp
foreach (var signature in envelope.Signatures)
{
var result = await VerifySignatureAsync(envelope.Payload, signature);
if (!result.IsValid)
{
return VerificationResult.Failure(
$"Signature {signature.KeyId} failed: {result.Error}");
}
}
```
## Cosign Compatibility
StellaOps DSSE envelopes are compatible with Sigstore cosign for verification.
### Envelope Format
```json
{
"payloadType": "application/vnd.in-toto+json",
"payload": "<base64-encoded-payload>",
"signatures": [
{
"keyid": "SHA256:abc123...",
"sig": "<base64-encoded-signature>"
}
]
}
```
### Cosign Verification Commands
See [Cosign Verification Examples](./cosign-verification-examples.md) for detailed commands.
## Offline Verification
For air-gapped environments, verification can proceed without network access:
### Prerequisites
1. **Trusted Root**: Pre-downloaded Sigstore TUF root
2. **Cached Certificates**: Fulcio certificate chain
3. **Rekor Entry** (optional): Cached transparency log entry
### Offline Flow
```csharp
var verifier = new OfflineVerifier(
trustedRoot: TrustedRoot.LoadFromFile("trusted_root.json"),
rekorEntries: RekorCache.LoadFromDirectory("rekor-cache/"));
var result = await verifier.VerifyAsync(envelope);
```
### Bundle for Offline Use
```bash
# Export attestation bundle with all dependencies
stellaops export attestation-bundle \
--artifact sha256:abc123... \
--include-certificate-chain \
--include-rekor-entry \
--output bundle.tar.gz
```
## Error Handling
### Common Verification Failures
| Error | Cause | Resolution |
|-------|-------|------------|
| `SignatureInvalid` | Signature doesn't match payload | Re-sign with correct key |
| `CertificateExpired` | Signing certificate expired | Use Rekor entry timestamp |
| `PayloadTampered` | Payload modified after signing | Restore original payload |
| `KeyNotTrusted` | Key not in trusted set | Add key to trust policy |
| `ParseError` | Malformed envelope JSON | Validate envelope format |
### Example Error Handling
```csharp
try
{
var result = await verifier.VerifyAsync(envelope);
if (!result.IsValid)
{
logger.LogWarning("Verification failed: {Reason}", result.FailureReason);
// Handle policy violation
}
}
catch (CertificateExpiredException ex)
{
// Fall back to Rekor timestamp verification
var result = await verifier.VerifyWithRekorTimestampAsync(
envelope, ex.Certificate, rekorEntry);
}
catch (JsonException ex)
{
logger.LogError(ex, "Failed to parse envelope");
throw new VerificationException("Malformed envelope", ex);
}
```
## Testing Round-Trip Verification
### Unit Test Example
```csharp
[Fact]
public async Task RoundTrip_SignVerifyRebundle_Succeeds()
{
// Arrange
var payload = CreateInTotoStatement();
var signer = CreateTestSigner();
// Act - Sign
var envelope = await signer.SignAsync(payload);
// Act - Serialize and deserialize
var json = JsonSerializer.Serialize(envelope);
var restored = JsonSerializer.Deserialize<DsseEnvelope>(json);
// Act - Verify restored envelope
var result = await signer.VerifyAsync(restored);
// Assert
result.IsValid.Should().BeTrue();
restored.Payload.ToArray().Should().Equal(envelope.Payload.ToArray());
}
```
### Integration Test Example
```csharp
[Fact]
public async Task RoundTrip_ArchiveExtractVerify_Succeeds()
{
// Arrange
var envelope = await CreateSignedEnvelope();
var archive = new AttestationArchive();
// Act - Archive
var archivePath = Path.GetTempFileName() + ".tar.gz";
await archive.WriteAsync(envelope, archivePath);
// Act - Extract and verify
var extracted = await archive.ReadAsync(archivePath);
var result = await verifier.VerifyAsync(extracted);
// Assert
result.IsValid.Should().BeTrue();
}
```
## Related Documentation
- [Proof Chain Specification](./proof-chain-specification.md)
- [Graph Root Attestation](./graph-root-attestation.md)
- [Transparency Log Integration](./transparency.md)
- [Air-Gap Operation](./airgap.md)
- [Cosign Verification Examples](./cosign-verification-examples.md)