- Introduced `ReachabilityState`, `RuntimeHit`, `ExploitabilitySignal`, `ReachabilitySignal`, `SignalEnvelope`, `SignalType`, `TrustSignal`, and `UnknownSymbolSignal` records to define various signal types and their properties. - Implemented JSON serialization attributes for proper data interchange. - Created project files for the new signal contracts library and corresponding test projects. - Added deterministic test fixtures for micro-interaction testing. - Included cryptographic keys for secure operations with cosign.
7.2 KiB
7.2 KiB
Competitor SBOM/Scan Signature Verification (CM2)
Status: Draft · Date: 2025-12-04 Scope: Specify signature and provenance verification requirements for accepting external SBOM and scan outputs, including rejection/flag policies.
Objectives
- Define acceptable signature algorithms and formats.
- Establish trust root management for external signers.
- Specify verification workflow and failure modes.
- Enable offline verification with bundled trust roots.
Acceptable Signatures
Signature Formats
| Format | Algorithm | Key Type | Status |
|---|---|---|---|
| DSSE | Ed25519 | Asymmetric | Preferred |
| DSSE | ECDSA P-256 | Asymmetric | Accepted |
| DSSE | RSA-2048+ | Asymmetric | Accepted |
| COSE | EdDSA | Asymmetric | Accepted |
| JWS | ES256 | Asymmetric | Accepted |
| JWS | RS256 | Asymmetric | Deprecated |
Hash Algorithms
| Algorithm | Usage | Status |
|---|---|---|
| SHA-256 | Primary | Required |
| BLAKE3-256 | Secondary | Preferred |
| SHA-384 | Alternative | Accepted |
| SHA-512 | Alternative | Accepted |
| SHA-1 | Legacy | Rejected |
| MD5 | Legacy | Rejected |
Trust Root Management
Bundled Trust Roots
out/offline/competitor-ingest-kit-v1/trust/
├── root-ca.pem # CA for signed SBOMs
├── keyring.json # Known signing keys
├── cosign-keys/ # Cosign public keys
│ ├── syft-release.pub
│ ├── trivy-release.pub
│ └── clair-release.pub
└── fulcio-root.pem # Sigstore Fulcio CA
Keyring Format
{
"keys": [
{
"id": "syft-release-2025",
"type": "ecdsa-p256",
"publicKey": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----",
"issuer": "https://github.com/anchore/syft",
"validFrom": "2025-01-01T00:00:00Z",
"validTo": "2026-01-01T00:00:00Z",
"purposes": ["sbom-signing", "attestation-signing"]
}
],
"trustedIssuers": [
"https://github.com/anchore/syft",
"https://github.com/aquasecurity/trivy",
"https://github.com/quay/clair"
]
}
Verification Workflow
┌─────────────┐
│ Receive │
│ SBOM │
└─────────────┘
│
▼
┌─────────────┐ ┌─────────────┐
│ Has DSSE? │──No─► Has JWS? │──No─► Unsigned
└─────────────┘ └─────────────┘ │
│ │ │
Yes Yes │
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Verify DSSE │ │ Verify JWS │ │ Apply CM6 │
│ Signature │ │ Signature │ │ Fallback │
└─────────────┘ └─────────────┘ └─────────────┘
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Valid? │ │ Valid? │ │ Provenance: │
└─────────────┘ └─────────────┘ │ unknown │
│ │ │ │ └─────────────┘
Yes No Yes No
│ │ │ │
▼ ▼ ▼ ▼
Accept Reject Accept Reject
Verification Steps
-
Format Detection
- Check for DSSE envelope wrapper
- Check for detached signature file (
.sig) - Check for inline JWS header
-
Signature Extraction
- Parse envelope/signature structure
- Extract signer key ID and algorithm
-
Key Lookup
- Search bundled keyring for key ID
- Verify key is within validity period
- Check key purpose matches usage
-
Cryptographic Verification
- Verify signature over payload
- Verify hash matches content
- Check for signature expiry
-
Provenance Validation
- Extract signer identity
- Verify issuer is trusted
- Check build metadata if present
Failure Modes
Rejection Reasons
| Code | Reason | Action |
|---|---|---|
sig_missing |
No signature present | Apply fallback (CM6) |
sig_invalid |
Signature verification failed | Reject |
sig_expired |
Signature validity period exceeded | Reject |
key_unknown |
Signing key not in keyring | Reject |
key_expired |
Signing key validity exceeded | Reject |
key_revoked |
Signing key has been revoked | Reject |
issuer_untrusted |
Issuer not in trusted list | Reject |
alg_unsupported |
Algorithm not acceptable | Reject |
hash_mismatch |
Content hash doesn't match | Reject |
Flag Policy
When --allow-unsigned is set:
| Condition | Behavior |
|---|---|
| Signature missing | Accept with provenance=unknown, emit warning |
| Signature invalid | Reject (flag doesn't override invalid) |
| Key unknown | Accept with provenance=unverified, emit warning |
Verification API
Endpoint
POST /api/v1/ingest/verify
Content-Type: application/json
{
"sbom": "<base64-encoded-sbom>",
"signature": "<base64-encoded-signature>",
"options": {
"allowUnsigned": false,
"requireProvenance": true
}
}
Response
{
"verification": {
"status": "valid",
"signature": {
"format": "dsse",
"algorithm": "ecdsa-p256",
"keyId": "syft-release-2025",
"signedAt": "2025-12-04T00:00:00Z"
},
"provenance": {
"issuer": "https://github.com/anchore/syft",
"buildId": "build-12345",
"sourceRepo": "https://github.com/example/app"
},
"hash": {
"algorithm": "sha256",
"value": "..."
}
}
}
Offline Verification
Requirements
- All trust roots bundled in offline kit
- No network calls during verification
- Keyring includes all expected signers
- CRL/OCSP checks disabled (use bundled revocation lists)
Revocation List Format
{
"revoked": [
{
"keyId": "compromised-key-2024",
"revokedAt": "2024-12-01T00:00:00Z",
"reason": "Key compromise"
}
],
"lastUpdated": "2025-12-04T00:00:00Z"
}
Integration with Normalization (CM1)
After successful verification:
- Extract tool metadata from signature/provenance
- Pass to normalization adapter
- Include verification result in normalized output
{
"source": {
"tool": "syft",
"version": "1.0.0",
"hash": "sha256:..."
},
"verification": {
"status": "verified",
"keyId": "syft-release-2025",
"signedAt": "2025-12-04T00:00:00Z"
},
"components": [...],
"normalized_hash": "blake3:..."
}
Links
- Sprint:
docs/implplan/SPRINT_0186_0001_0001_record_deterministic_execution.md(CM2) - Normalization:
docs/modules/scanner/design/competitor-ingest-normalization.md(CM1) - Fallback: See CM6 in this document series