- 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.
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
- Overview
- Architecture
- Security Model
- Algorithm Support
- Deployment Scenarios
- API Reference
- Trust Establishment
- Threat Model
- Compliance
- Best Practices
- 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:
- Static Analysis:
scripts/audit-crypto-usage.ps1 - CI Validation:
.gitea/workflows/crypto-compliance.yml - 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:
- Pre-distribution: Public key fingerprints embedded in airgap bundle
- Out-of-Band Verification: Manual verification via secure channel
- 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:
Argon2idPasswordHasherfor modern password hashingPbkdf2PasswordHasherfor 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:
- Pre-distribute trust bundle via USB/DVD:
offline-kit.tar.gz - Bundle contains:
- Public key fingerprints (
trust-anchors.json) - Root CA certificates (if applicable)
- Offline crypto provider plugin
- Public key fingerprints (
- 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:
- Build produces identical artifact hash
- Offline provider signs with deterministic ECDSA (RFC 6979)
- CI stores signature alongside artifact
- 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)- Returnstrueif signature validSignAsync(data)- ThrowsNotSupportedExceptionKeyId- Returns"ephemeral"AlgorithmId- Returns the specified algorithm
Throws:
NotSupportedException: Algorithm not supported or public key format invalidCryptographicException: 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
- Bundle Reception: Operator receives
offline-kit.tar.gzvia physical media - Checksum Verification: Compare SHA-256 hash against value published via secure channel
sha256sum offline-kit.tar.gz # Compare with published value: a1b2c3d4e5f6... - Bundle Signature Verification: Extract bundle, verify self-signature using bootstrap public key
- Trust Anchor Review: Manual review of trust-anchors.json entries
- 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:
- ES256 (ECDSA P-256/SHA-256) - Best balance
- PS256 (RSA-PSS 2048-bit/SHA-256) - For RSA-required scenarios
- 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:
- Verify algorithm matches
- Ensure message is identical (byte-for-byte)
- Check public key matches private key
- Enable debug logging
References
Related Documentation
- Crypto Architecture Overview
- ICryptoProvider Interface
- Plugin Manifest Schema
- AirGap Module Architecture
- Sprint Documentation
External Standards
- NIST FIPS 186-4: Digital Signature Standard
- NIST FIPS 180-4: Secure Hash Standard
- RFC 8017: PKCS #1 v2.2
- RFC 6979: Deterministic ECDSA
- RFC 7515: JSON Web Signature
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