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

@@ -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)