TypeDialog/docs/encryption/encryption-unified-architecture.md

451 lines
11 KiB
Markdown
Raw Permalink Normal View History

2025-12-24 03:11:32 +00:00
# Unified Encryption Architecture
This document explains the updated encryption architecture for typedialog and how it integrates with the unified encryption system from prov-ecosystem.
## Overview
typedialog now uses a **unified encryption API** from the `encrypt` crate, eliminating backend-specific code and enabling support for multiple encryption backends (Age, SOPS, SecretumVault, AWS/GCP/Azure KMS) through a single API.
**Key Benefits:**
- Single code path supports all backends
- Configuration-driven backend selection
- Multi-backend support in TOML and Nickel schemas
- Post-quantum cryptography ready (via SecretumVault)
- Cleaner, more maintainable code
## Architecture Changes
### Before: Direct Backend Instantiation
```rust
// OLD: Direct Age backend instantiation
use encrypt::backend::age::AgeBackend;
let backend = AgeBackend::with_defaults()?;
let ciphertext = backend.encrypt(&plaintext)?;
// To support other backends, need separate code paths
#[match]
"sops" => { /* SOPS code */ }
"rustyvault" => { /* RustyVault code */ }
```
### After: Unified API with BackendSpec
```rust
// NEW: Configuration-driven, backend-agnostic
use encrypt::{encrypt, BackendSpec};
// Same code for all backends
let spec = BackendSpec::age_default();
let ciphertext = encrypt(&plaintext, &spec)?;
// Or SOPS
let spec = BackendSpec::sops();
let ciphertext = encrypt(&plaintext, &spec)?;
// Or KMS
let spec = BackendSpec::aws_kms(region, key_id);
let ciphertext = encrypt(&plaintext, &spec)?;
```
## Integration Points
### 1. TOML Form Configuration
No changes required for existing TOML configurations. The system transparently uses the new API:
```toml
[[fields]]
name = "password"
type = "password"
sensitive = true
encryption_backend = "age"
encryption_config = { key_file = "~/.age/key.txt" }
```
The `encryption_bridge` module automatically converts this to `BackendSpec::age(...)` and uses the unified API.
### 2. Nickel Schema Integration
Nickel support remains unchanged but now uses the unified backend system:
```nickel
{
# Age backend for development
dev_password | Sensitive Backend="age" Key="~/.age/key.txt" = "",
# SOPS for staging
staging_secret | Sensitive Backend="sops" = "",
# SecretumVault Transit Engine for production (post-quantum)
prod_token | Sensitive Backend="secretumvault"
Vault="https://vault.internal:8200"
Key="app-key" = "",
# AWS KMS for cloud-native deployments
aws_secret | Sensitive Backend="awskms"
Region="us-east-1"
KeyId="arn:aws:kms:..." = "",
}
```
### 3. Internal Encryption Function
The `transform_sensitive_value()` function now uses the unified API:
```rust
// File: crates/typedialog-core/src/helpers.rs
fn transform_sensitive_value(
value: &Value,
field: &FieldDefinition,
context: &EncryptionContext,
_global_config: Option<&EncryptionDefaults>,
) -> Result<Value> {
// Convert field definition to BackendSpec
let spec = crate::encryption_bridge::field_to_backend_spec(field, None)?;
// Use unified API
let plaintext = serde_json::to_string(value)?;
let ciphertext = encrypt::encrypt(&plaintext, &spec)?;
Ok(Value::String(ciphertext))
}
```
## New Bridge Module
New `encryption_bridge.rs` module provides seamless conversion:
```rust
// File: crates/typedialog-core/src/encryption_bridge.rs
pub fn field_to_backend_spec(
field: &FieldDefinition,
default_backend: Option<&str>,
) -> Result<encrypt::BackendSpec>
```
**Conversion Logic:**
1. Reads `field.encryption_backend` (or uses default)
2. Extracts `field.encryption_config` (backend-specific settings)
3. Validates required configuration for the backend
4. Returns `BackendSpec` ready for use with `encrypt::encrypt()`
**Supported Backends:**
- ✓ Age (with custom key paths)
- ✓ SOPS (minimal config)
- ✓ SecretumVault (vault_addr, vault_token, key_name)
- ✓ AWS KMS (region, key_id)
- ✓ GCP KMS (project_id, key_ring, crypto_key, location)
- ✓ Azure KMS (vault_name, tenant_id)
## Configuration Changes
### Cargo.toml
No changes required - encryption support includes all commonly used backends by default.
To customize backends:
```toml
[dependencies]
# Default: age + other major backends
typedialog-core = { path = "...", features = ["encryption"] }
# Only Age
typedialog-core = { path = "...", features = ["encryption"] }
# (Age is default in encrypt crate)
# Custom selection
typedialog-core = { path = "...", features = ["encryption"] }
# Depends on encrypt crate configuration
```
### Environment Variables
Backend-specific configuration via environment:
**Age:**
```bash
# Uses ~/.age/key.txt by default
# Or specify via field config: encryption_config = { key_file = "/custom/path" }
```
**SOPS:**
```bash
# Uses .sops.yaml in current/parent directories
```
**SecretumVault:**
```bash
export VAULT_ADDR="https://vault.internal:8200"
export VAULT_TOKEN="hvs.CAAA..."
```
**AWS KMS:**
```bash
export AWS_REGION="us-east-1"
# AWS credentials from standard chain (env vars, ~/.aws/credentials, IAM roles)
```
**GCP KMS:**
```bash
export GOOGLE_APPLICATION_CREDENTIALS="/path/to/service-account.json"
```
**Azure KMS:**
```bash
# Azure CLI authentication or environment variables
```
## Error Handling
Enhanced error messages guide users through troubleshooting:
```text
Error: Encryption failed: Backend 'age' not available.
Enable feature 'age' in Cargo.toml
Error: Encryption failed: SecretumVault backend requires
vault_addr in encryption_config
Error: Encryption failed: AWS KMS backend requires region
in encryption_config
```
## Testing
### Running Encryption Tests
```bash
# All encryption tests
cargo test --features encryption
# Only encryption integration tests
cargo test --features encryption --test encryption_integration
# Specific test
cargo test --features encryption test_age_roundtrip_encrypt_decrypt
```
### Test Results
All 15 encryption integration tests pass:
- ✓ 7 encryption behavior tests
- ✓ 8 Age backend roundtrip tests
```text
running 15 tests
test encryption_tests::test_explicit_non_sensitive_overrides_password_type ... ok
test encryption_tests::test_auto_detect_password_field_as_sensitive ... ok
test encryption_tests::test_redaction_preserves_non_sensitive ... ok
test encryption_tests::test_multiple_sensitive_fields ... ok
test encryption_tests::test_redaction_in_json_output ... ok
test encryption_tests::test_unknown_backend_error ... ok
test encryption_tests::test_redaction_in_yaml_output ... ok
test age_roundtrip_tests::test_age_backend_availability ... ok
test age_roundtrip_tests::test_age_invalid_ciphertext_fails ... ok
test age_roundtrip_tests::test_age_encryption_produces_ciphertext ... ok
test age_roundtrip_tests::test_age_roundtrip_encrypt_decrypt ... ok
test age_roundtrip_tests::test_age_handles_empty_string ... ok
test age_roundtrip_tests::test_age_handles_unicode ... ok
test age_roundtrip_tests::test_age_encryption_different_ciphertexts ... ok
test age_roundtrip_tests::test_age_handles_large_values ... ok
test result: ok. 15 passed; 0 failed
```
## Migration Path
### For Existing Users
**No action required.** The system is backward compatible:
1. Existing TOML forms work unchanged
2. Existing Nickel schemas work unchanged
3. Internal implementation now uses unified API
4. No visible changes to users
### For New Deployments
Can now use additional backends:
```toml
# Now supports more backends in single codebase
[[fields]]
name = "secret"
type = "text"
sensitive = true
encryption_backend = "secretumvault" # Or awskms, gcpkms, azurekms
encryption_config = { vault_addr = "...", vault_token = "..." }
```
### For Code Extending typedialog
If extending typedialog with custom encryption logic:
```rust
// OLD: Manual backend instantiation (still works)
use encrypt::backend::age::AgeBackend;
let backend = AgeBackend::new(pub_key, priv_key)?;
// NEW: Use bridge module + unified API (recommended)
use encrypt::encrypt;
use typedialog_core::encryption_bridge;
let spec = encryption_bridge::field_to_backend_spec(&field, None)?;
let ciphertext = encrypt(&plaintext, &spec)?;
```
## Multi-Backend Support
### Same Code, Different Configs
```toml
# Development (Age)
[[fields]]
name = "db_password"
sensitive = true
encryption_backend = "age"
# Production (SecretumVault - post-quantum)
[[fields]]
name = "db_password"
sensitive = true
encryption_backend = "secretumvault"
encryption_config = { vault_addr = "https://vault.prod:8200", vault_token = "..." }
```
Same Rust code handles both without changes.
### CLI Overrides
```bash
# Override backend from command line
typedialog form config.toml --encrypt --backend secretumvault
# Use with environment variables
export VAULT_ADDR="https://vault.internal:8200"
export VAULT_TOKEN="hvs.CAAA..."
typedialog form config.toml --encrypt --backend secretumvault
```
## Feature Flags
Backends are feature-gated in the encrypt crate:
```rust
// With feature enabled
#[cfg(feature = "age")]
{
let spec = BackendSpec::age_default();
encrypt(&plaintext, &spec)?; // Works
}
// Without feature
#[cfg(not(feature = "age"))]
{
let spec = BackendSpec::age_default();
encrypt(&plaintext, &spec)?; // Returns: Backend 'age' not available
}
```
## Post-Quantum Cryptography
### SecretumVault Transit Engine
For post-quantum cryptography support, use SecretumVault with ML-KEM/ML-DSA:
```toml
[[fields]]
name = "pqc_secret"
sensitive = true
encryption_backend = "secretumvault"
encryption_config = {
vault_addr = "https://pq-vault.internal:8200",
vault_token = "hvs.CAAA...",
key_name = "pqc-key" # Uses ML-KEM encapsulation, ML-DSA signatures
}
```
**Requirements:**
- SecretumVault server configured with Transit Engine
- Post-quantum crypto backend enabled (aws-lc-rs or Tongsuo)
## Troubleshooting
### "Backend not available" Error
**Problem:** Encryption fails with "Backend 'age' not available"
**Solution:** Feature may not be enabled in encrypt crate. Check:
```bash
# Check available backends
cargo build --features encryption --verbose
# Look for feature compilation
# It should show: "Compiling encrypt ... with features: age,..."
```
### "Invalid ciphertext" After Update
**Problem:** Old ciphertexts fail to decrypt
**Solution:** Age format hasn't changed. Verify:
1. Same Age key is used
2. Ciphertext format is valid (hex-encoded)
3. Key file permissions: `chmod 600 ~/.age/key.txt`
### Form TOML Backward Compatibility
**Problem:** Existing TOML forms stop working after update
**Solution:** No breaking changes. Forms should work as-is. If not:
1. Verify encryption_backend name is valid
2. Check encryption_config required fields
3. Test with: `cargo test --features encryption`
## Testing with Mock Backend
For faster tests without real encryption keys:
```bash
# In development/testing
cargo test --features test-util
```
MockBackend provides deterministic encryption for CI/CD:
```rust
#[cfg(test)]
use encrypt::test_util::MockBackend;
let backend = MockBackend::new();
let ct = backend.encrypt("secret")?;
// Fast, reproducible, no real keys needed
```
## See Also
- [encryption-quick-start.md](encryption-quick-start.md) - Getting started with encryption
- [encryption-services-setup.md](encryption-services-setup.md) - Setting up encryption services
- [../../prov-ecosystem/docs/guides/ENCRYPTION.md](../../prov-ecosystem/docs/guides/ENCRYPTION.md) - Comprehensive encryption guide
- [encryption_bridge.rs](crates/typedialog-core/src/encryption_bridge.rs) - Bridge module source
- [../../prov-ecosystem/crates/encrypt](../../prov-ecosystem/crates/encrypt) - encrypt crate source