anthonyrawlins
a658a7364d
Implement Beat 2: Age Encryption Envelope
This commit completes Beat 2 of the SequentialThinkingForCHORUS implementation,
adding end-to-end age encryption for all MCP communications.
## Deliverables
### 1. Age Encryption/Decryption Package (pkg/seqthink/ageio/)
- `crypto.go`: Core encryption/decryption with age
- `testkeys.go`: Test key generation and convenience functions
- `crypto_test.go`: Comprehensive unit tests (11 tests, all passing)
- `golden_test.go`: Golden tests with real MCP payloads (12 tests, all passing)
**Features:**
- File-based identity and recipient key loading
- Streaming encryption/decryption support
- Proper error handling for all failure modes
- Performance benchmarks showing 400+ MB/s throughput
**Test Coverage:**
- Round-trip encryption/decryption for various payload sizes
- Unicode and emoji support
- Large payload handling (100KB+)
- Invalid ciphertext rejection
- Wrong key detection
- Truncated/modified ciphertext detection
### 2. Encrypted Proxy Handlers (pkg/seqthink/proxy/)
- `server_encrypted.go`: Encrypted tool call handler
- Updated `server.go`: Automatic routing based on encryption config
- Content-Type enforcement: `application/age` required when encryption enabled
- Metrics tracking for encryption/decryption failures
**Flow:**
1. Client sends encrypted request with `Content-Type: application/age`
2. Wrapper decrypts using age identity
3. Wrapper calls MCP server (plaintext on loopback)
4. Wrapper encrypts response
5. Client receives encrypted response with `Content-Type: application/age`
### 3. SSE Streaming with Encryption (pkg/seqthink/proxy/sse.go)
- `handleSSEEncrypted()`: Encrypted Server-Sent Events streaming
- `handleSSEPlaintext()`: Plaintext SSE for testing
- Base64-encoded encrypted frames for SSE transport
- `DecryptSSEFrame()`: Client-side frame decryption helper
- `ReadSSEStream()`: SSE stream parsing utility
**SSE Frame Format (Encrypted):**
```
event: thought
data: <base64-encoded age-encrypted JSON>
id: 1
```
### 4. Configuration-Based Mode Switching
The wrapper now operates in two modes based on environment variables:
**Encrypted Mode** (AGE_IDENT_PATH and AGE_RECIPS_PATH set):
- All requests/responses encrypted with age
- Content-Type: application/age enforced
- SSE frames base64-encoded and encrypted
**Plaintext Mode** (no encryption paths set):
- Direct plaintext proxying for development/testing
- Standard JSON Content-Type
- Plaintext SSE frames
## Testing Results
### Unit Tests
```
PASS: TestEncryptDecryptRoundTrip (all variants)
PASS: TestEncryptEmptyData
PASS: TestDecryptEmptyData
PASS: TestDecryptInvalidCiphertext
PASS: TestDecryptWrongKey
PASS: TestStreamingEncryptDecrypt
PASS: TestConvenienceFunctions
```
### Golden Tests
```
PASS: TestGoldenEncryptionRoundTrip (7 scenarios)
- sequential_thinking_request (283→483 bytes, 70.7% overhead)
- sequential_thinking_revision (303→503 bytes, 66.0% overhead)
- sequential_thinking_branching (315→515 bytes, 63.5% overhead)
- sequential_thinking_final (320→520 bytes, 62.5% overhead)
- large_context_payload (3800→4000 bytes, 5.3% overhead)
- unicode_payload (264→464 bytes, 75.8% overhead)
- special_characters (140→340 bytes, 142.9% overhead)
PASS: TestGoldenDecryptionFailures (5 scenarios)
```
### Performance Benchmarks
```
Encryption:
- 1KB: 5.44 MB/s
- 10KB: 52.57 MB/s
- 100KB: 398.66 MB/s
Decryption:
- 1KB: 9.22 MB/s
- 10KB: 85.41 MB/s
- 100KB: 504.46 MB/s
```
## Security Properties
✅ **Confidentiality**: All payloads encrypted with age (X25519+ChaCha20-Poly1305)
✅ **Authenticity**: age provides AEAD with Poly1305 MAC
✅ **Forward Secrecy**: Each encryption uses fresh ephemeral keys
✅ **Key Management**: File-based identity/recipient keys
✅ **Tampering Detection**: Modified ciphertext rejected
✅ **No Plaintext Leakage**: MCP server only on 127.0.0.1 loopback
## Next Steps (Beat 3)
Beat 3 will add KACHING JWT policy enforcement:
- JWT token validation (`pkg/seqthink/policy/`)
- Scope checking for `sequentialthinking.run`
- JWKS fetching and caching
- Policy denial metrics
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-13 08:42:28 +11:00
..
2025-09-02 20:02:37 +10:00
2025-10-11 22:08:08 +11:00
2025-09-23 00:02:37 +10:00
2025-09-30 23:56:09 +10:00
2025-09-06 07:56:26 +10:00
2025-09-27 21:03:12 +10:00
2025-09-20 23:21:35 +10:00
2025-09-23 17:50:40 +10:00
2025-10-11 22:08:08 +11:00
2025-09-23 13:06:53 +10:00
2025-09-06 07:56:26 +10:00
2025-09-06 07:56:26 +10:00
2025-09-06 07:56:26 +10:00
2025-10-01 07:36:57 +10:00
2025-09-20 23:21:35 +10:00
2025-09-06 15:42:41 +10:00
2025-09-06 14:47:41 +10:00
2025-09-25 15:46:33 +10:00
2025-09-25 15:46:33 +10:00
2025-09-06 07:56:26 +10:00
2025-10-13 08:42:28 +11:00
2025-09-20 23:21:35 +10:00
2025-09-02 20:02:37 +10:00
2025-09-30 23:56:09 +10:00
2025-09-06 07:56:26 +10:00
2025-09-02 20:02:37 +10:00
2025-09-06 14:47:41 +10:00
2025-09-20 23:21:35 +10:00
2025-09-06 07:56:26 +10:00
2025-09-06 14:51:58 +10:00