Files
git.stella-ops.org/docs/security/offline-verification-crypto-provider.md
master 5590a99a1a Add tests for SBOM generation determinism across multiple formats
- Created `StellaOps.TestKit.Tests` project for unit tests related to determinism.
- Implemented `DeterminismManifestTests` to validate deterministic output for canonical bytes and strings, file read/write operations, and error handling for invalid schema versions.
- Added `SbomDeterminismTests` to ensure identical inputs produce consistent SBOMs across SPDX 3.0.1 and CycloneDX 1.6/1.7 formats, including parallel execution tests.
- Updated project references in `StellaOps.Integration.Determinism` to include the new determinism testing library.
2025-12-23 23:51:58 +02:00

21 KiB

Offline Verification Crypto Provider - Security Guide

Document Version: 1.0 Last Updated: 2025-12-23 Status: Active Audience: Security Engineers, Platform Operators, DevOps Teams Sprint: SPRINT_1000_0007_0002

Table of Contents

  1. Overview
  2. Architecture
  3. Security Model
  4. Algorithm Support
  5. Deployment Scenarios
  6. API Reference
  7. Trust Establishment
  8. Threat Model
  9. Compliance
  10. Best Practices
  11. Troubleshooting

Overview

The OfflineVerificationCryptoProvider is a cryptographic abstraction layer that wraps .NET BCL (System.Security.Cryptography) to enable configuration-driven cryptography in offline, air-gapped, and sovereignty-constrained environments.

Purpose

  • Offline Operations: Function without network access to external cryptographic services
  • Deterministic Behavior: Reproducible signatures and hashes for compliance auditing
  • Zero External Dependencies: No cloud KMS, HSMs, or online certificate authorities required
  • Regional Neutrality: NIST-approved algorithms without regional compliance constraints

Key Features

  • ECDSA (ES256/384/512) and RSA (RS256/384/512, PS256/384/512) signing/verification
  • SHA-2 family hashing (SHA-256/384/512)
  • Ephemeral verification for public-key-only scenarios (DSSE, JWT, JWS)
  • Configuration-driven plugin architecture with priority-based selection
  • Zero-cost abstraction over .NET BCL primitives

Architecture

Component Hierarchy

┌─────────────────────────────────────────────────────────┐
│  Production Code (AirGap, Scanner, Attestor)            │
│  ├── Uses: ICryptoProvider abstraction                  │
│  └── Never touches: System.Security.Cryptography        │
└─────────────────────────────────────────────────────────┘
                          ▼
┌─────────────────────────────────────────────────────────┐
│  StellaOps.Cryptography (Core Abstraction)              │
│  ├── ICryptoProvider interface                          │
│  ├── ICryptoSigner interface                            │
│  ├── ICryptoHasher interface                            │
│  └── CryptoProviderRegistry                             │
└─────────────────────────────────────────────────────────┘
                          ▼
┌─────────────────────────────────────────────────────────┐
│  OfflineVerificationCryptoProvider (Plugin)             │
│  ├── BclHasher (SHA-256/384/512)                        │
│  ├── EcdsaSigner (ES256/384/512)                        │
│  ├── RsaSigner (RS/PS 256/384/512)                      │
│  ├── EcdsaEphemeralVerifier (public-key-only)           │
│  └── RsaEphemeralVerifier (public-key-only)             │
└─────────────────────────────────────────────────────────┘
                          ▼
┌─────────────────────────────────────────────────────────┐
│  System.Security.Cryptography (.NET BCL)                │
│  ├── ECDsa (NIST P-256/384/521)                         │
│  ├── RSA (2048/3072/4096-bit)                           │
│  └── SHA256/SHA384/SHA512                               │
└─────────────────────────────────────────────────────────┘

Isolation Boundaries

Crypto Operations Allowed:

  • Inside StellaOps.Cryptography.Plugin.* projects
  • Inside unit test projects (__Tests/**)
  • NEVER in production application code

Enforcement Mechanisms:

  1. Static Analysis: scripts/audit-crypto-usage.ps1
  2. CI Validation: .gitea/workflows/crypto-compliance.yml
  3. Code Review: Automated checks on pull requests

Security Model

Threat Categories

Threat Likelihood Impact Mitigation
Key Extraction Medium High In-memory keys only, minimize key lifetime
Side-Channel (Timing) Low Medium .NET BCL uses constant-time primitives
Algorithm Downgrade Very Low Critical Compile-time algorithm allowlist
Public Key Substitution Medium Critical Fingerprint verification, out-of-band trust
Replay Attack Medium Medium Include timestamps in signed payloads
Man-in-the-Middle Low (offline) N/A Physical media transport

Trust Boundaries

┌────────────────────────────────────────────────────────┐
│  Trusted Computing Base (TCB)                          │
│  ├── .NET Runtime (Microsoft-signed)                   │
│  ├── OfflineVerificationCryptoProvider (AGPL-3.0)      │
│  └── Pre-distributed Public Key Fingerprints           │
└────────────────────────────────────────────────────────┘
                        ▲
                        │ Trust Anchor
                        │
┌────────────────────────────────────────────────────────┐
│  Untrusted Zone                                        │
│  ├── Container Images (to be verified)                 │
│  ├── SBOMs (to be verified)                            │
│  └── VEX Documents (to be verified)                    │
└────────────────────────────────────────────────────────┘

Trust Establishment:

  1. Pre-distribution: Public key fingerprints embedded in airgap bundle
  2. Out-of-Band Verification: Manual verification via secure channel
  3. Chain of Trust: Each signature verified against trusted fingerprints

Algorithm Support

Signing & Verification

Algorithm Curve/Key Size Hash Padding Use Case
ES256 NIST P-256 SHA-256 N/A DSSE envelopes, in-toto attestations
ES384 NIST P-384 SHA-384 N/A High-security SBOM signatures
ES512 NIST P-521 SHA-512 N/A Long-term archival signatures
RS256 2048+ bits SHA-256 PKCS1 Legacy compatibility
RS384 2048+ bits SHA-384 PKCS1 Legacy compatibility
RS512 2048+ bits SHA-512 PKCS1 Legacy compatibility
PS256 2048+ bits SHA-256 PSS Recommended RSA (FIPS 186-4)
PS384 2048+ bits SHA-384 PSS Recommended RSA (FIPS 186-4)
PS512 2048+ bits SHA-512 PSS Recommended RSA (FIPS 186-4)

Content Hashing

Algorithm Output Size Performance Use Case
SHA-256 256 bits Fast Default for most use cases
SHA-384 384 bits Medium Medium-security requirements
SHA-512 512 bits Medium High-security requirements

Normalization: Both SHA-256 and SHA256 formats accepted, normalized to SHA-256.

Password Hashing

Not Supported. Use dedicated password hashers:

  • Argon2idPasswordHasher for modern password hashing
  • Pbkdf2PasswordHasher for legacy compatibility

Deployment Scenarios

Scenario 1: Air-Gapped Container Scanning

Environment: Offline network segment, no internet access

Configuration:

{
  "cryptoProvider": "offline-verification",
  "algorithms": {
    "signing": "ES256",
    "hashing": "SHA-256"
  },
  "trustRoots": {
    "fingerprints": [
      "sha256:a1b2c3d4e5f6....",
      "sha256:f6e5d4c3b2a1...."
    ]
  }
}

Trust Establishment:

  1. Pre-distribute trust bundle via USB/DVD: offline-kit.tar.gz
  2. Bundle contains:
    • Public key fingerprints (trust-anchors.json)
    • Root CA certificates (if applicable)
    • Offline crypto provider plugin
  3. Operator verifies bundle signature using out-of-band channel

Workflow:

┌──────────┐   ┌──────────┐   ┌──────────┐   ┌──────────┐
│ Scan     │──▶│ Generate │──▶│ Sign     │──▶│ Verify   │
│ Container│   │ SBOM     │   │ with ES256│  │ Signature│
└──────────┘   └──────────┘   └──────────┘   └──────────┘
                                     │              │
                                     ▼              ▼
                          OfflineVerificationCryptoProvider

Scenario 2: Sovereign Cloud Deployment

Environment: National cloud with data residency requirements

Configuration:

{
  "cryptoProvider": "offline-verification",
  "jurisdiction": "world",
  "compliance": ["NIST", "offline-airgap"],
  "keyRotation": {
    "enabled": true,
    "intervalDays": 90
  }
}

Key Considerations:

  • Keys generated and stored within sovereign boundary
  • No external KMS dependencies
  • Audit trail for all cryptographic operations
  • Compliance with local data protection laws

Scenario 3: CI/CD Pipeline with Reproducible Builds

Environment: Build server with deterministic signing

Configuration:

{
  "cryptoProvider": "offline-verification",
  "deterministicSigning": true,
  "algorithms": {
    "signing": "ES256",
    "hashing": "SHA-256"
  }
}

Workflow:

  1. Build produces identical artifact hash
  2. Offline provider signs with deterministic ECDSA (RFC 6979)
  3. CI stores signature alongside artifact
  4. Downstream consumers verify signature before deployment

API Reference

ICryptoProvider.CreateEphemeralVerifier (New in v1.0)

Signature:

ICryptoSigner CreateEphemeralVerifier(
    string algorithmId,
    ReadOnlySpan<byte> publicKeyBytes)

Purpose: Create a verification-only signer from raw public key bytes, without key persistence or management overhead.

Parameters:

  • algorithmId: Algorithm identifier (ES256, RS256, PS256, etc.)
  • publicKeyBytes: Public key in SubjectPublicKeyInfo (SPKI) format, DER-encoded

Returns: ICryptoSigner instance with:

  • VerifyAsync(data, signature) - Returns true if signature valid
  • SignAsync(data) - Throws NotSupportedException
  • KeyId - Returns "ephemeral"
  • AlgorithmId - Returns the specified algorithm

Throws:

  • NotSupportedException: Algorithm not supported or public key format invalid
  • CryptographicException: Public key parsing failed

Usage Example:

// DSSE envelope verification
var envelope = DsseEnvelope.Parse(envelopeJson);
var trustRoots = LoadTrustRoots();

foreach (var signature in envelope.Signatures)
{
    // Get public key from trust store
    if (!trustRoots.PublicKeys.TryGetValue(signature.KeyId, out var publicKeyBytes))
        continue;

    // Verify fingerprint
    var fingerprint = ComputeFingerprint(publicKeyBytes);
    if (!trustRoots.TrustedFingerprints.Contains(fingerprint))
        continue;

    // Create ephemeral verifier
    var verifier = cryptoProvider.CreateEphemeralVerifier("PS256", publicKeyBytes);

    // Build pre-authentication encoding (PAE)
    var pae = BuildPAE(envelope.PayloadType, envelope.Payload);

    // Verify signature
    var isValid = await verifier.VerifyAsync(pae, Convert.FromBase64String(signature.Signature));

    if (isValid)
        return ValidationResult.Success();
}

return ValidationResult.Failure("No valid signature found");

ICryptoHasher.ComputeHash

Signature:

byte[] ComputeHash(ReadOnlySpan<byte> data)

Usage Example:

var hasher = cryptoProvider.GetHasher("SHA-256");
var hash = hasher.ComputeHash(fileBytes);
var hex = Convert.ToHexString(hash).ToLowerInvariant();

ICryptoSigner.SignAsync / VerifyAsync

Signatures:

ValueTask<byte[]> SignAsync(ReadOnlyMemory<byte> data, CancellationToken ct = default)
ValueTask<bool> VerifyAsync(ReadOnlyMemory<byte> data, ReadOnlyMemory<byte> signature, CancellationToken ct = default)

Usage Example:

// Signing
var signingKey = new CryptoSigningKey(
    reference: new CryptoKeyReference("my-key"),
    algorithmId: "ES256",
    privateParameters: ecParameters,
    createdAt: DateTimeOffset.UtcNow);

cryptoProvider.UpsertSigningKey(signingKey);
var signer = cryptoProvider.GetSigner("ES256", new CryptoKeyReference("my-key"));
var signature = await signer.SignAsync(data);

// Verification
var isValid = await signer.VerifyAsync(data, signature);

Trust Establishment

Offline Trust Bundle Structure

offline-kit.tar.gz
├── trust-anchors.json          # Public key fingerprints
├── public-keys/                # Public keys in SPKI format
│   ├── scanner-key-001.pub
│   ├── scanner-key-002.pub
│   └── attestor-key-001.pub
├── metadata/
│   ├── bundle-manifest.json    # Bundle metadata
│   └── bundle-signature.sig    # Bundle self-signature
└── crypto-plugins/
    └── StellaOps.Cryptography.Plugin.OfflineVerification.dll

trust-anchors.json Format

{
  "version": "1.0",
  "createdAt": "2025-12-23T00:00:00Z",
  "expiresAt": "2026-12-23T00:00:00Z",
  "trustAnchors": [
    {
      "keyId": "scanner-key-001",
      "algorithmId": "ES256",
      "fingerprint": "sha256:a1b2c3d4e5f6...",
      "purpose": "container-scanning",
      "notBefore": "2025-01-01T00:00:00Z",
      "notAfter": "2026-01-01T00:00:00Z"
    }
  ],
  "bundleSignature": {
    "keyId": "bundle-signing-key",
    "algorithmId": "ES256",
    "signature": "base64encodedSignature=="
  }
}

Fingerprint Computation

private string ComputeFingerprint(byte[] publicKeyBytes)
{
    var hasher = cryptoProvider.GetHasher("SHA-256");
    var hash = hasher.ComputeHash(publicKeyBytes);
    return "sha256:" + Convert.ToHexString(hash).ToLowerInvariant();
}

Out-of-Band Verification Process

  1. Bundle Reception: Operator receives offline-kit.tar.gz via physical media
  2. Checksum Verification: Compare SHA-256 hash against value published via secure channel
    sha256sum offline-kit.tar.gz
    # Compare with published value: a1b2c3d4e5f6...
    
  3. Bundle Signature Verification: Extract bundle, verify self-signature using bootstrap public key
  4. Trust Anchor Review: Manual review of trust-anchors.json entries
  5. Deployment: Extract crypto plugin and trust anchors to deployment directory

Threat Model

Attack Surface Analysis

Attack Vector Likelihood Impact Mitigation
Memory Dump Medium High Use ephemeral keys, minimize key lifetime
Side-Channel (Timing) Low Medium .NET BCL uses constant-time primitives
Algorithm Substitution Very Low Critical Compile-time algorithm allowlist
Public Key Substitution Medium Critical Fingerprint verification, out-of-band trust
Replay Attack Medium Medium Include timestamps in signed payloads
Man-in-the-Middle Low (offline) N/A Physical media transport

Mitigations by Threat

T1: Private Key Extraction

  • Control: In-memory keys only, no disk persistence
  • Monitoring: Log key usage events
  • Response: Revoke compromised key, rotate to new key

T2: Public Key Substitution

  • Control: SHA-256 fingerprint verification before use
  • Monitoring: Alert on fingerprint mismatches
  • Response: Investigate trust bundle integrity

T3: Signature Replay

  • Control: Include timestamp and nonce in signed payloads
  • Monitoring: Detect signatures older than TTL
  • Response: Reject replayed signatures

T4: Algorithm Downgrade

  • Control: Hardcoded algorithm allowlist in provider
  • Monitoring: Log algorithm selection
  • Response: Reject unsupported algorithms

Compliance

NIST Standards

Standard Requirement Compliance
FIPS 186-4 Digital Signature Standard ECDSA with P-256/384/521, RSA-PSS
FIPS 180-4 Secure Hash Standard SHA-256/384/512
FIPS 140-2 Cryptographic Module Validation ⚠️ .NET BCL (software-only, not validated)

Notes:

  • For FIPS 140-2 Level 3+ compliance, use HSM-backed crypto provider
  • Software-only crypto acceptable for FIPS 140-2 Level 1

RFC Standards

RFC Title Compliance
RFC 8017 PKCS #1: RSA Cryptography v2.2 RSASSA-PKCS1-v1_5, RSASSA-PSS
RFC 6979 Deterministic DSA/ECDSA Via BouncyCastle fallback (optional)
RFC 5280 X.509 Public Key Infrastructure SubjectPublicKeyInfo format
RFC 7515 JSON Web Signature (JWS) ES256/384/512, RS256/384/512, PS256/384/512

Regional Standards

Region Standard Compliance
European Union eIDAS Regulation (EU) 910/2014 Use eIDAS plugin
Russia GOST R 34.10-2012 Use CryptoPro plugin
China SM2/SM3/SM4 (GM/T 0003-2012) Use SM crypto plugin

Best Practices

Key Management

DO:

  • Rotate signing keys every 90 days
  • Use separate keys for different purposes
  • Store private keys in memory only
  • Use ephemeral verifiers for public-key-only scenarios
  • Audit all key usage events

DON'T:

  • Reuse keys across environments
  • Store keys in configuration files
  • Use RSA keys smaller than 2048 bits
  • Use SHA-1 or MD5
  • Bypass fingerprint verification

Algorithm Selection

Recommended:

  1. ES256 (ECDSA P-256/SHA-256) - Best balance
  2. PS256 (RSA-PSS 2048-bit/SHA-256) - For RSA-required scenarios
  3. SHA-256 - Default hashing algorithm

Avoid:

  • ES512 / PS512 - Performance overhead
  • RS256 / RS384 / RS512 - Legacy PKCS1 padding

Performance Optimization

Caching:

// Cache hashers (thread-safe, reusable)
private readonly ICryptoHasher _sha256Hasher;

public MyService(ICryptoProviderRegistry registry)
{
    _sha256Hasher = registry.ResolveHasher("SHA-256").Hasher;
}

Troubleshooting

Common Issues

Issue: NotSupportedException: Algorithm 'RS256' is not supported

Resolution:

  • Verify algorithm ID is exactly RS256 (case-sensitive)
  • Check provider supports: provider.Supports(CryptoCapability.Signing, "RS256")

Issue: CryptographicException: Public key parsing failed

Resolution:

  • Ensure public key is DER-encoded SPKI format
  • Convert from PEM: openssl x509 -pubkey -noout -in cert.pem | openssl enc -base64 -d > pubkey.der

Issue: Signature verification always returns false

Resolution:

  1. Verify algorithm matches
  2. Ensure message is identical (byte-for-byte)
  3. Check public key matches private key
  4. Enable debug logging

References

External Standards


Document Control

Version Date Author Changes
1.0 2025-12-23 StellaOps Platform Team Initial release with CreateEphemeralVerifier API

License: AGPL-3.0-or-later