finish off sprint advisories and sprints

This commit is contained in:
master
2026-01-24 00:12:43 +02:00
parent 726d70dc7f
commit c70e83719e
266 changed files with 46699 additions and 1328 deletions

View File

@@ -503,3 +503,181 @@ webhooks:
- Health endpoints: `/health/liveness`, `/health/readiness`, `/status`, `/surface/fs/cache/status` (see runbook).
- Alert hints: deny spikes, latency > 800ms p99, cache freshness lag > 10m, any secrets failure.
---
## 17) Offline Witness Verification
> **Sprint:** SPRINT_20260122_038_Scanner_ebpf_probe_type (EBPF-004)
This section documents the deterministic replay verification algorithm for runtime witnesses, enabling air-gapped environments to independently verify witness attestations.
### 17.1 Input Canonicalization (RFC 8785 JCS)
All witness payloads MUST be canonicalized before hashing or signing using **JSON Canonicalization Scheme (JCS)** per RFC 8785:
1. **Property ordering**: Object properties are sorted lexicographically by key name (Unicode code point order).
2. **Number serialization**: Numbers are serialized without unnecessary precision; integers as integers, decimals with minimal representation.
3. **String encoding**: UTF-8 with no BOM; escape sequences normalized to `\uXXXX` form for control characters.
4. **Whitespace**: No insignificant whitespace between tokens.
5. **Null handling**: Explicit `null` values are preserved; absent keys are omitted.
**Canonicalization algorithm:**
```
function canonicalize(json_object):
if json_object is null:
return "null"
if json_object is boolean:
return "true" | "false"
if json_object is number:
return serialize_number(json_object) # RFC 8785 §3.2.2.3
if json_object is string:
return quote(escape(json_object))
if json_object is array:
return "[" + join(",", [canonicalize(elem) for elem in json_object]) + "]"
if json_object is object:
keys = sorted(json_object.keys(), key=unicode_codepoint_order)
pairs = [quote(key) + ":" + canonicalize(json_object[key]) for key in keys]
return "{" + join(",", pairs) + "}"
```
### 17.2 Observation Ordering Rules
When a witness contains multiple observations (e.g., from eBPF probes), they MUST be ordered deterministically before hashing:
1. **Primary sort**: By `observedAt` timestamp (UTC, ascending)
2. **Secondary sort**: By `nodeHash` (lexicographic ascending)
3. **Tertiary sort**: By `observationId` (lexicographic ascending, for tie-breaking)
**Observation hash computation:**
```
function compute_observations_hash(observations):
sorted_observations = sort(observations,
key=lambda o: (o.observedAt, o.nodeHash, o.observationId))
canonical_array = []
for obs in sorted_observations:
canonical_array.append({
"observedAt": obs.observedAt.toISOString(),
"nodeHash": obs.nodeHash,
"functionName": obs.functionName,
"probeType": obs.probeType, # EBPF-001: kprobe|uprobe|tracepoint|usdt|fentry|fexit
"containerHash": sha256(obs.containerId + obs.podName + obs.namespace)
})
return sha256(canonicalize(canonical_array))
```
### 17.3 Signature Verification Sequence
Offline verification MUST follow this exact sequence to ensure deterministic results:
1. **Parse DSSE envelope**: Extract `payloadType`, `payload` (base64-decoded), and `signatures[]`.
2. **Verify payload hash**:
```
expected_hash = sha256(payload_bytes)
assert envelope.payload_sha256 == expected_hash
```
3. **Verify DSSE signature(s)**: For each signature in `signatures[]`:
```
pae_string = "DSSEv1 " + len(payloadType) + " " + payloadType + " " + len(payload) + " " + payload
verify_signature(signature.sig, pae_string, get_public_key(signature.keyid))
```
4. **Verify Rekor inclusion** (if present):
```
fetch_or_load_checkpoint(rekor_log_id)
verify_merkle_inclusion(entry_hash, inclusion_proof, checkpoint.root_hash)
verify_checkpoint_signature(checkpoint, rekor_public_key)
```
5. **Verify timestamp** (if RFC 3161 TST present):
```
verify_tst_signature(tst, tsa_certificate)
assert tst.timestamp <= now() + allowed_skew
```
6. **Verify witness content**:
```
witness = parse_json(payload)
recomputed_observations_hash = compute_observations_hash(witness.observations)
assert witness.observationsDigest == recomputed_observations_hash
```
### 17.4 Offline Bundle Structure Requirements
A StellaBundle for offline witness verification MUST include:
```
bundle/
├── manifest.json # Bundle manifest v2.0.0
├── witnesses/
│ └── <claim_id>.witness.dsse.json # DSSE-signed witness
├── proofs/
│ ├── rekor-inclusion.json # Rekor inclusion proof
│ ├── checkpoint.json # Rekor checkpoint (signed)
│ └── rfc3161-tst.der # Optional RFC 3161 timestamp
├── observations/
│ └── observations.ndjson # Raw observations (for replay)
├── keys/
│ ├── signing-key.pub # Public key for DSSE verification
│ └── rekor-key.pub # Rekor log public key
└── trust/
└── trust-root.json # Trust anchors for key verification
```
**Manifest schema (witnesses section):**
```json
{
"schemaVersion": "2.0.0",
"artifacts": [
{
"type": "witness",
"path": "witnesses/<claim_id>.witness.dsse.json",
"digest": "sha256:...",
"predicateType": "https://stella.ops/predicates/runtime-witness/v1",
"proofs": {
"rekor": "proofs/rekor-inclusion.json",
"checkpoint": "proofs/checkpoint.json",
"tst": "proofs/rfc3161-tst.der"
},
"observationsRef": "observations/observations.ndjson"
}
]
}
```
### 17.5 Verification CLI Commands
```bash
# Verify a witness bundle offline
stella bundle verify --bundle witness-bundle.tar.gz --offline
# Verify with replay (recompute observations hash)
stella bundle verify --bundle witness-bundle.tar.gz --offline --replay
# Verify specific witness from bundle
stella witness verify --bundle witness-bundle.tar.gz --witness-id wit:sha256:abc123 --offline
# Export verification report
stella witness verify --bundle witness-bundle.tar.gz --offline --output report.json
```
### 17.6 Determinism Guarantees
The verification algorithm guarantees:
1. **Idempotent**: Running verification N times produces identical results.
2. **Reproducible**: Different systems with the same bundle produce identical verification outcomes.
3. **Isolated**: Verification requires no network access (fully air-gapped).
4. **Auditable**: Every step produces evidence that can be independently checked.
**Test criteria** (per advisory):
- Offline verifier reproduces the same mapping on 3 separate air-gapped runs.
- No randomness in canonicalization, ordering, or hash computation.
- Timestamps use UTC with fixed precision (milliseconds).