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.
This commit is contained in:
@@ -1,319 +1,598 @@
|
||||
# Offline Verification Crypto Provider
|
||||
# Offline Verification Crypto Provider - Security Guide
|
||||
|
||||
**Provider ID:** `offline-verification`
|
||||
**Version:** 1.0
|
||||
**Status:** Production
|
||||
**Last Updated:** 2025-12-23
|
||||
**Sprint:** SPRINT_1000_0007_0002
|
||||
**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 provider designed for offline and air-gapped environments. It wraps .NET BCL cryptography (`System.Security.Cryptography`) within the `ICryptoProvider` abstraction, enabling configuration-driven crypto while maintaining offline verification capabilities.
|
||||
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.
|
||||
|
||||
This provider is particularly useful for:
|
||||
- **Air-gapped deployments** where hardware security modules (HSMs) are unavailable
|
||||
- **Offline bundle verification** in disconnected environments
|
||||
- **Development and testing** environments
|
||||
- **Fallback scenarios** when regional crypto providers are unavailable
|
||||
### Purpose
|
||||
|
||||
## When to Use This Provider
|
||||
- **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
|
||||
|
||||
### ✅ Recommended Use Cases
|
||||
### Key Features
|
||||
|
||||
1. **Air-Gapped Bundle Verification**
|
||||
- Verifying DSSE-signed evidence bundles in disconnected environments
|
||||
- Validating attestations without external connectivity
|
||||
- Offline policy verification
|
||||
- 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
|
||||
|
||||
2. **Development & Testing**
|
||||
- Local development without HSM dependencies
|
||||
- CI/CD pipelines for automated testing
|
||||
- Integration test environments
|
||||
---
|
||||
|
||||
3. **Fallback Provider**
|
||||
- When regional providers (GOST, SM, eIDAS) are unavailable
|
||||
- Default offline verification path
|
||||
## Architecture
|
||||
|
||||
### ❌ NOT Recommended For
|
||||
### Component Hierarchy
|
||||
|
||||
1. **Production Signing Operations** - Use HSM-backed providers instead
|
||||
2. **Compliance-Critical Scenarios** - Use certified providers (FIPS, eIDAS, etc.)
|
||||
3. **High-Value Key Storage** - Use hardware-backed key storage
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ 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 │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Supported Algorithms
|
||||
### 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 | Notes |
|
||||
|-----------|----------------|------|---------|-------|
|
||||
| ES256 | NIST P-256 | SHA-256 | N/A | ECDSA with SHA-256 |
|
||||
| ES384 | NIST P-384 | SHA-384 | N/A | ECDSA with SHA-384 |
|
||||
| ES512 | NIST P-521 | SHA-512 | N/A | ECDSA with SHA-512 |
|
||||
| RS256 | RSA 2048+ | SHA-256 | PKCS1 | RSA with PKCS#1 v1.5 padding |
|
||||
| RS384 | RSA 2048+ | SHA-384 | PKCS1 | RSA with PKCS#1 v1.5 padding |
|
||||
| RS512 | RSA 2048+ | SHA-512 | PKCS1 | RSA with PKCS#1 v1.5 padding |
|
||||
| PS256 | RSA 2048+ | SHA-256 | PSS | RSA-PSS with SHA-256 |
|
||||
| PS384 | RSA 2048+ | SHA-384 | PSS | RSA-PSS with SHA-384 |
|
||||
| PS512 | RSA 2048+ | SHA-512 | PSS | RSA-PSS with SHA-512 |
|
||||
| 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 | Aliases |
|
||||
|-----------|-------------|---------|
|
||||
| SHA-256 | 32 bytes | SHA256 |
|
||||
| SHA-384 | 48 bytes | SHA384 |
|
||||
| SHA-512 | 64 bytes | SHA512 |
|
||||
| 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.** The offline verification provider does not implement password hashing. Use dedicated password hashers:
|
||||
**Not Supported.** Use dedicated password hashers:
|
||||
- `Argon2idPasswordHasher` for modern password hashing
|
||||
- `Pbkdf2PasswordHasher` for legacy compatibility
|
||||
|
||||
## API Reference
|
||||
---
|
||||
|
||||
### Basic Usage
|
||||
## Deployment Scenarios
|
||||
|
||||
```csharp
|
||||
using StellaOps.Cryptography;
|
||||
using StellaOps.Cryptography.Plugin.OfflineVerification;
|
||||
### Scenario 1: Air-Gapped Container Scanning
|
||||
|
||||
// Create provider instance
|
||||
var provider = new OfflineVerificationCryptoProvider();
|
||||
**Environment**: Offline network segment, no internet access
|
||||
|
||||
// Check algorithm support
|
||||
bool supportsES256 = provider.Supports(CryptoCapability.Signing, "ES256");
|
||||
// Returns: true
|
||||
|
||||
// Get a hasher
|
||||
var hasher = provider.GetHasher("SHA-256");
|
||||
var hash = hasher.ComputeHash(dataBytes);
|
||||
|
||||
// Get a signer (requires key reference)
|
||||
var keyRef = new CryptoKeyReference("my-signing-key");
|
||||
var signer = provider.GetSigner("ES256", keyRef);
|
||||
var signature = await signer.SignAsync(dataBytes);
|
||||
```
|
||||
|
||||
### Ephemeral Verification (New in v1.0)
|
||||
|
||||
For verification-only scenarios where you have raw public key bytes (e.g., DSSE verification):
|
||||
|
||||
```csharp
|
||||
// Create ephemeral verifier from SubjectPublicKeyInfo bytes
|
||||
byte[] publicKeyBytes = LoadPublicKeyFromDsse();
|
||||
var verifier = provider.CreateEphemeralVerifier("ES256", publicKeyBytes);
|
||||
|
||||
// Verify signature (no private key required)
|
||||
var isValid = await verifier.VerifyAsync(dataBytes, signatureBytes);
|
||||
```
|
||||
|
||||
**When to use ephemeral verification:**
|
||||
- DSSE envelope verification with inline public keys
|
||||
- One-time verification operations
|
||||
- No need to persist keys in provider's key store
|
||||
|
||||
### Dependency Injection Setup
|
||||
|
||||
```csharp
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using StellaOps.Cryptography;
|
||||
using StellaOps.Cryptography.Plugin.OfflineVerification;
|
||||
|
||||
// Add to DI container
|
||||
services.AddSingleton<ICryptoProvider, OfflineVerificationCryptoProvider>();
|
||||
|
||||
// Or use with crypto provider registry
|
||||
services.AddSingleton<ICryptoProviderRegistry>(sp =>
|
||||
**Configuration**:
|
||||
```json
|
||||
{
|
||||
var registry = new CryptoProviderRegistry();
|
||||
registry.RegisterProvider(new OfflineVerificationCryptoProvider());
|
||||
return registry;
|
||||
});
|
||||
```
|
||||
|
||||
### Air-Gapped Bundle Verification Example
|
||||
|
||||
```csharp
|
||||
using StellaOps.Cryptography;
|
||||
using StellaOps.Cryptography.Plugin.OfflineVerification;
|
||||
using StellaOps.AirGap.Importer.Validation;
|
||||
|
||||
// Initialize provider
|
||||
var cryptoRegistry = new CryptoProviderRegistry([
|
||||
new OfflineVerificationCryptoProvider()
|
||||
]);
|
||||
|
||||
// Create DSSE verifier with crypto provider
|
||||
var dsseVerifier = new DsseVerifier(cryptoRegistry);
|
||||
|
||||
// Verify bundle signature
|
||||
var trustRoots = new TrustRootConfig
|
||||
{
|
||||
PublicKeys = new Dictionary<string, byte[]>
|
||||
{
|
||||
["airgap-signer"] = LoadPublicKeyBytes()
|
||||
},
|
||||
TrustedKeyFingerprints = new HashSet<string>
|
||||
{
|
||||
ComputeFingerprint(LoadPublicKeyBytes())
|
||||
}
|
||||
};
|
||||
|
||||
var result = dsseVerifier.Verify(dsseEnvelope, trustRoots);
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
Console.WriteLine("Bundle signature verified successfully!");
|
||||
"cryptoProvider": "offline-verification",
|
||||
"algorithms": {
|
||||
"signing": "ES256",
|
||||
"hashing": "SHA-256"
|
||||
},
|
||||
"trustRoots": {
|
||||
"fingerprints": [
|
||||
"sha256:a1b2c3d4e5f6....",
|
||||
"sha256:f6e5d4c3b2a1...."
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration
|
||||
**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
|
||||
|
||||
### crypto-plugins-manifest.json
|
||||
**Workflow**:
|
||||
```
|
||||
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
|
||||
│ Scan │──▶│ Generate │──▶│ Sign │──▶│ Verify │
|
||||
│ Container│ │ SBOM │ │ with ES256│ │ Signature│
|
||||
└──────────┘ └──────────┘ └──────────┘ └──────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
OfflineVerificationCryptoProvider
|
||||
```
|
||||
|
||||
The offline verification provider is typically enabled by default:
|
||||
### 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<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**:
|
||||
```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<byte> 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<byte[]> SignAsync(ReadOnlyMemory<byte> data, CancellationToken ct = default)
|
||||
ValueTask<bool> VerifyAsync(ReadOnlyMemory<byte> data, ReadOnlyMemory<byte> 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
|
||||
{
|
||||
"plugins": [
|
||||
"version": "1.0",
|
||||
"createdAt": "2025-12-23T00:00:00Z",
|
||||
"expiresAt": "2026-12-23T00:00:00Z",
|
||||
"trustAnchors": [
|
||||
{
|
||||
"name": "offline-verification",
|
||||
"assembly": "StellaOps.Cryptography.Plugin.OfflineVerification.dll",
|
||||
"type": "StellaOps.Cryptography.Plugin.OfflineVerification.OfflineVerificationCryptoProvider",
|
||||
"enabled": true,
|
||||
"priority": 45,
|
||||
"config": {}
|
||||
"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=="
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Priority:** `45` - Higher than default (50), lower than regional providers (10-40)
|
||||
### Fingerprint Computation
|
||||
|
||||
### Environment Variables
|
||||
|
||||
No environment variables required. The provider is self-contained.
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### ✅ Safe for Verification
|
||||
|
||||
The offline verification provider is **safe for verification operations** in offline environments:
|
||||
- Public key verification
|
||||
- Signature validation
|
||||
- Hash computation
|
||||
- Bundle integrity checks
|
||||
|
||||
### ⚠️ Signing Key Protection
|
||||
|
||||
**Private keys used with this provider MUST be protected:**
|
||||
1. **Key Storage:**
|
||||
- Use encrypted key files with strong passphrases
|
||||
- Store in secure filesystem locations with restricted permissions
|
||||
- Consider using OS-level key storage (Windows DPAPI, macOS Keychain)
|
||||
|
||||
2. **Key Rotation:**
|
||||
- Rotate signing keys periodically
|
||||
- Maintain key version tracking for bundle verification
|
||||
|
||||
3. **Access Control:**
|
||||
- Limit file system permissions on private keys (chmod 600 on Unix)
|
||||
- Use separate keys for dev/test/prod environments
|
||||
|
||||
### Deterministic Operations
|
||||
|
||||
The provider ensures deterministic operations where required:
|
||||
- **Hash computation:** SHA-256/384/512 are deterministic
|
||||
- **Signature verification:** Deterministic for given signature and public key
|
||||
- **ECDSA signing:** Uses deterministic nonce generation (RFC 6979) when available
|
||||
|
||||
## Limitations
|
||||
|
||||
1. **No HSM Support:** Keys are software-based, not hardware-backed
|
||||
2. **No Compliance Certification:** Not FIPS 140-2, eIDAS, or other certified implementations
|
||||
3. **Algorithm Limitations:** Only supports algorithms in .NET BCL
|
||||
4. **No Password Hashing:** Use dedicated password hashers instead
|
||||
|
||||
## Migration Guide
|
||||
|
||||
### From Direct System.Security.Cryptography
|
||||
|
||||
**Before:**
|
||||
```csharp
|
||||
using System.Security.Cryptography;
|
||||
|
||||
var hash = SHA256.HashData(dataBytes); // ❌ Direct BCL usage
|
||||
private string ComputeFingerprint(byte[] publicKeyBytes)
|
||||
{
|
||||
var hasher = cryptoProvider.GetHasher("SHA-256");
|
||||
var hash = hasher.ComputeHash(publicKeyBytes);
|
||||
return "sha256:" + Convert.ToHexString(hash).ToLowerInvariant();
|
||||
}
|
||||
```
|
||||
|
||||
**After:**
|
||||
### 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
|
||||
using StellaOps.Cryptography;
|
||||
// Cache hashers (thread-safe, reusable)
|
||||
private readonly ICryptoHasher _sha256Hasher;
|
||||
|
||||
var hasher = cryptoRegistry.ResolveHasher("SHA-256");
|
||||
var hash = hasher.Hasher.ComputeHash(dataBytes); // ✅ Provider abstraction
|
||||
public MyService(ICryptoProviderRegistry registry)
|
||||
{
|
||||
_sha256Hasher = registry.ResolveHasher("SHA-256").Hasher;
|
||||
}
|
||||
```
|
||||
|
||||
### From Legacy Crypto Plugins
|
||||
---
|
||||
|
||||
Replace legacy plugin references with OfflineVerificationCryptoProvider:
|
||||
## Troubleshooting
|
||||
|
||||
1. Update `crypto-plugins-manifest.json`
|
||||
2. Replace plugin DI registration
|
||||
3. Update algorithm IDs to standard names (ES256, RS256, etc.)
|
||||
### Common Issues
|
||||
|
||||
## Testing
|
||||
**Issue**: `NotSupportedException: Algorithm 'RS256' is not supported`
|
||||
|
||||
Comprehensive unit tests are available in:
|
||||
`src/__Libraries/__Tests/StellaOps.Cryptography.Tests/OfflineVerificationCryptoProviderTests.cs`
|
||||
**Resolution**:
|
||||
- Verify algorithm ID is exactly `RS256` (case-sensitive)
|
||||
- Check provider supports: `provider.Supports(CryptoCapability.Signing, "RS256")`
|
||||
|
||||
Run tests:
|
||||
```bash
|
||||
dotnet test src/__Libraries/__Tests/StellaOps.Cryptography.Tests/
|
||||
```
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
**Issue**: `CryptographicException: Public key parsing failed`
|
||||
|
||||
- [Crypto Provider Registry](../contracts/crypto-provider-registry.md)
|
||||
- [Crypto Plugin Development Guide](../cli/crypto-plugins.md)
|
||||
- [Air-Gapped Bundle Verification](../airgap/bundle-verification.md)
|
||||
- [DSSE Signature Verification](../contracts/dsse-envelope.md)
|
||||
**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`
|
||||
|
||||
## Support & Troubleshooting
|
||||
---
|
||||
|
||||
### Provider Not Found
|
||||
**Issue**: Signature verification always returns `false`
|
||||
|
||||
```
|
||||
Error: Crypto provider 'offline-verification' not found
|
||||
```
|
||||
**Resolution**:
|
||||
1. Verify algorithm matches
|
||||
2. Ensure message is identical (byte-for-byte)
|
||||
3. Check public key matches private key
|
||||
4. Enable debug logging
|
||||
|
||||
**Solution:** Ensure plugin is registered in `crypto-plugins-manifest.json` with `enabled: true`
|
||||
---
|
||||
|
||||
### Algorithm Not Supported
|
||||
## References
|
||||
|
||||
```
|
||||
Error: Algorithm 'ES256K' is not supported
|
||||
```
|
||||
### Related Documentation
|
||||
|
||||
**Solution:** Check [Supported Algorithms](#supported-algorithms) table. The offline provider only supports .NET BCL algorithms.
|
||||
- [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)
|
||||
|
||||
### Ephemeral Verifier Creation Fails
|
||||
### External Standards
|
||||
|
||||
```
|
||||
Error: Failed to create ephemeral verifier
|
||||
```
|
||||
- [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)
|
||||
|
||||
**Causes:**
|
||||
1. Invalid public key format (must be SubjectPublicKeyInfo DER-encoded)
|
||||
2. Unsupported algorithm
|
||||
3. Corrupted public key bytes
|
||||
---
|
||||
|
||||
**Solution:** Verify public key format and algorithm compatibility.
|
||||
**Document Control**
|
||||
|
||||
## Changelog
|
||||
| Version | Date | Author | Changes |
|
||||
|---------|------|--------|---------|
|
||||
| 1.0 | 2025-12-23 | StellaOps Platform Team | Initial release with CreateEphemeralVerifier API |
|
||||
|
||||
### Version 1.0 (2025-12-23)
|
||||
- Initial release
|
||||
- Support for ES256/384/512, RS256/384/512, PS256/384/512
|
||||
- SHA-256/384/512 content hashing
|
||||
- Ephemeral verifier creation from raw public key bytes
|
||||
- Comprehensive unit test coverage (39 tests)
|
||||
**License**: AGPL-3.0-or-later
|
||||
|
||||
Reference in New Issue
Block a user