# 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](#overview) 2. [Architecture](#architecture) 3. [Security Model](#security-model) 4. [Algorithm Support](#algorithm-support) 5. [Deployment Scenarios](#deployment-scenarios) 6. [API Reference](#api-reference) 7. [Trust Establishment](#trust-establishment) 8. [Threat Model](#threat-model) 9. [Compliance](#compliance) 10. [Best Practices](#best-practices) 11. [Troubleshooting](#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**: ```json { "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**: ```json { "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**: ```json { "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**: ```csharp ICryptoSigner CreateEphemeralVerifier( string algorithmId, ReadOnlySpan 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**: ```csharp // 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**: ```csharp byte[] ComputeHash(ReadOnlySpan data) ``` **Usage Example**: ```csharp var hasher = cryptoProvider.GetHasher("SHA-256"); var hash = hasher.ComputeHash(fileBytes); var hex = Convert.ToHexString(hash).ToLowerInvariant(); ``` ### ICryptoSigner.SignAsync / VerifyAsync **Signatures**: ```csharp ValueTask SignAsync(ReadOnlyMemory data, CancellationToken ct = default) ValueTask VerifyAsync(ReadOnlyMemory data, ReadOnlyMemory signature, CancellationToken ct = default) ``` **Usage Example**: ```csharp // 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 ```json { "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 ```csharp 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 ```bash 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**: ```csharp // 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 ### Related Documentation - [Crypto Architecture Overview](../modules/platform/crypto-architecture.md) - [ICryptoProvider Interface](../../src/__Libraries/StellaOps.Cryptography/CryptoProvider.cs) - [Plugin Manifest Schema](../../etc/crypto-plugins-manifest.json) - [AirGap Module Architecture](../modules/airgap/architecture.md) - [Sprint Documentation](../implplan/SPRINT_1000_0007_0002_crypto_refactoring.md) ### External Standards - [NIST FIPS 186-4: Digital Signature Standard](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf) - [NIST FIPS 180-4: Secure Hash Standard](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf) - [RFC 8017: PKCS #1 v2.2](https://www.rfc-editor.org/rfc/rfc8017) - [RFC 6979: Deterministic ECDSA](https://www.rfc-editor.org/rfc/rfc6979) - [RFC 7515: JSON Web Signature](https://www.rfc-editor.org/rfc/rfc7515) --- **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