API Key Distribution
Technical documentation describing the security architecture for distributing third-party API keys (Google Vertex AI, OpenAI) from inCamera's server infrastructure to client applications.
Executive Summary
This document describes the security architecture for distributing third-party API keys from inCamera's server infrastructure to client applications. The architecture addresses two competing security requirements: protecting our origin infrastructure from direct internet exposure, and guaranteeing the authenticity and confidentiality of sensitive credentials delivered to clients.
Our solution employs end-to-end encryption with cryptographic signature verification at the application layer, treating the network transport, including our Cloudflare Tunnel, as an untrusted channel. This approach provides defense against man-in-the-middle attacks, including theoretical attacks by infrastructure providers, while maintaining the operational security benefits of origin hiding.
The design philosophy is borrowed from secure messaging protocols: assume the network is hostile, and build security guarantees that hold regardless of transport layer properties.
Threat Model
Infrastructure Context
inCamera's server infrastructure operates behind Cloudflare Tunnel, which provides several operational benefits:
- No public IP address exposure for origin servers
- DDoS mitigation at Cloudflare's edge
- No inbound firewall ports required
- Simplified certificate management for general traffic
However, this architecture means Cloudflare terminates TLS connections from clients. Traffic between clients and our origin passes through Cloudflare infrastructure in plaintext (from Cloudflare's perspective) before being re-encrypted for the tunnel segment.
+----------+ +---------------+ +-------------------+
| Client |<---TLS--->| Cloudflare |<--Tunnel->| inCamera Origin |
+----------+ | Edge | +-------------------+
+---------------+
|
Cloudflare can inspect
plaintext at this point
Traditional TLS certificate pinning is ineffective in this architecture because clients never see our origin certificate; they see Cloudflare's edge certificate.
Threat Categories
Network-Level Adversaries
Attackers who control network infrastructure between the client and Cloudflare's edge. This includes ISPs, corporate network administrators, compromised routers, and nation-state surveillance programs.
Mitigation: TLS encryption to Cloudflare's edge prevents passive surveillance and active tampering on this segment.
Infrastructure Provider Access
Cloudflare, as our infrastructure provider, has technical capability to inspect traffic passing through their systems. While Cloudflare is a reputable provider with strong security practices, our threat model does not require trusting any third party with access to plaintext credentials.
Mitigation: Application-layer encryption ensures Cloudflare sees only ciphertext. Application-layer signatures ensure Cloudflare cannot forge or substitute payloads.
Key Substitution Attacks
An attacker (at any layer) intercepts credential delivery and substitutes malicious API keys pointing to logging proxies that mimic legitimate AI provider APIs. The client unknowingly sends privileged legal communications to attacker-controlled infrastructure.
Mitigation: Cryptographic signatures over payloads, verified against public keys embedded in the application binary at compile time.
Replay Attacks
An attacker captures a valid credential delivery and replays it later, potentially after legitimate keys have been rotated or to enable correlation attacks.
Mitigation: Client-generated nonces, server-generated nonces, and short validity windows make replayed payloads invalid.
Passive Credential Harvesting
An attacker with access to any point in the network path captures credentials for later use or sale.
Mitigation: Per-client ephemeral encryption ensures that even an attacker who captures traffic cannot decrypt credentials without the client's ephemeral private key, which exists only in memory and is never transmitted.
Compromised Client Devices: If an attacker has root access to the user's device, they can extract keys from memory, modify the application binary, or intercept decrypted communications. No software-only solution prevents this.
Compromised Build Pipeline: If an attacker compromises our build infrastructure, they could embed malicious public keys in the application binary. We mitigate this through code signing, secure build practices, and reproducible builds where feasible.
Compromise of AI Providers: If Google or OpenAI infrastructure is compromised, or if these providers are compelled to log data despite ZDR agreements, protections at our layer cannot prevent exposure.
Security Properties Achieved
| Property | Guarantee |
|---|---|
| Confidentiality | Only the intended client can decrypt credentials |
| Authenticity | Clients can verify credentials originated from inCamera |
| Integrity | Any modification to payloads is detected |
| Forward Secrecy | Ephemeral keys on both sides ensure no long-term encryption key exists to compromise; past sessions remain secure even under full server compromise |
| Replay Resistance | Captured payloads cannot be reused |
Cryptographic Architecture
Key Inventory
Long-Term Server Keys (inCamera Origin)
| Key | Algorithm | Purpose | Storage |
|---|---|---|---|
| Server Signing Key | Ed25519 | Signs all sensitive payloads | HSM / Secure enclave |
Embedded Client Keys (Compiled into Application)
| Key | Algorithm | Purpose | Storage |
|---|---|---|---|
| Server Signing Public Key | Ed25519 | Verifies payload signatures | Application binary |
Ephemeral Keys (Generated at Runtime)
| Key | Algorithm | Purpose | Storage |
|---|---|---|---|
| Client Ephemeral Key | X25519 | Per-request key agreement | Memory only, never persisted |
| Server Ephemeral Key | X25519 | Per-request key agreement | Memory only, never persisted |
The server generates fresh X25519 keypairs for each request rather than using a static identity key. This provides stronger forward secrecy: even if a server's ephemeral private key from one request were somehow compromised, only that single response would be affected. The only long-term key that needs rotation is the Ed25519 signing key.
Algorithm Selection Rationale
Ed25519 for Signatures
Ed25519 provides fast, secure digital signatures with 128-bit security level. Signatures are deterministic, eliminating vulnerabilities related to random number generation during signing. The small key and signature sizes (32 bytes and 64 bytes respectively) minimize payload overhead.
X25519 for Key Agreement
X25519 provides elliptic curve Diffie-Hellman key agreement with strong security properties and resistance to timing attacks. Both client and server generate fresh ephemeral X25519 keypairs for each request, providing true forward secrecy: there are no long-term encryption keys that could be compromised to expose past sessions.
Separation of Signing and Encryption Keys
Using distinct keys for signing and encryption follows cryptographic best practices. It limits the impact of key compromise, simplifies key rotation, and allows different protection levels for different keys if needed.
Trust Bootstrapping
The fundamental challenge in any cryptographic system is trust bootstrapping: how does the client know it has authentic public keys for the server?
Our solution embeds server public keys directly in the application binary at compile time:
// src/crypto/embedded_keys.rs // These keys are compiled into the binary and verified via code signing /// inCamera server Ed25519 signing public key /// Used to verify authenticity of all server payloads /// Note: No X25519 identity key is embedded. The server generates /// ephemeral X25519 keys per-request for stronger forward secrecy pub const SERVER_SIGNING_PUBLIC_KEY: [u8; 32] = [ 0x__, 0x__, 0x__, 0x__, 0x__, 0x__, 0x__, 0x__, 0x__, 0x__, 0x__, 0x__, 0x__, 0x__, 0x__, 0x__, 0x__, 0x__, 0x__, 0x__, 0x__, 0x__, 0x__, 0x__, 0x__, 0x__, 0x__, 0x__, 0x__, 0x__, 0x__, 0x__, ]; /// Key version identifier for rotation support pub const KEY_VERSION: u32 = 1;
The trust chain becomes:
Apple/Microsoft Code Signing Root
|
v
inCamera Developer Certificate
|
v
Signed Application Binary
|
v
Embedded Server Public Keys
An attacker cannot substitute keys without either compromising our developer signing certificate or compromising Apple/Microsoft's code signing infrastructure.
Key Rotation Strategy
Long-term keys require rotation to limit exposure from potential undetected compromise and to follow cryptographic hygiene practices.
| Key Type | Rotation Frequency | Transition Period |
|---|---|---|
| Server Signing Key | Annual | 90 days |
| Embedded Signing Public Key | Follows server rotation | Via app updates |
| Ephemeral X25519 Keys | Per-request | N/A (generated fresh) |
Because encryption uses ephemeral keys on both sides, only the Ed25519 signing key requires scheduled rotation. To support seamless rotation, the application binary contains slots for current and next signing public keys. During the transition period, the server signs payloads with both current and next keys, and clients accept either. After the transition period, the next key becomes current, and a new next key is provisioned.
Protocol Specification
Protocol Overview
The credential distribution protocol establishes a secure channel over an untrusted transport, delivers encrypted credentials, and provides cryptographic proof of authenticity.
+----------+ +----------+
| Client | | Server |
+----+-----+ +----+-----+
| |
| 1. Generate ephemeral X25519 keypair |
| 2. Generate 256-bit random nonce |
| |
| -------------- Key Request ------------------> |
| { client_ephemeral_public_key, |
| client_nonce, |
| client_version, |
| timestamp } |
| |
| 3. Validate request |
| 4. Generate ephemeral X25519 |
| keypair (per-request) |
| 5. Perform X25519 key agreement |
| 6. Derive encryption key (HKDF) |
| 7. Encrypt credentials |
| (ChaCha20-Poly1305) |
| 8. Sign entire response |
| (Ed25519) |
| |
| <------------- Key Response ------------------- |
| { server_ephemeral_public_key, |
| encrypted_payload, |
| server_nonce, |
| client_nonce_echo, |
| key_version, |
| signature } |
| |
| 9. Verify signature against embedded public key |
| 10. Verify client_nonce_echo matches |
| 11. Verify timestamp freshness |
| 12. Perform X25519 key agreement with server's |
| ephemeral public key |
| 13. Derive decryption key (HKDF) |
| 14. Decrypt and authenticate payload |
| 15. Securely store credentials |
| 16. Zero ephemeral private key from memory |
| |
Message Formats
Request Message
{
"protocol_version": 1,
"request": {
"client_ephemeral_public_key": "<base64, 32 bytes>",
"client_nonce": "<base64, 32 bytes>",
"timestamp": 1702134567,
"client_version": "1.2.3",
"platform": "macos-arm64"
}
}
| Field | Type | Description |
|---|---|---|
| protocol_version | integer | Protocol version for future compatibility |
| client_ephemeral_public_key | base64 string | X25519 public key, 32 bytes |
| client_nonce | base64 string | Cryptographically random, 32 bytes |
| timestamp | integer | Unix timestamp, seconds since epoch |
| client_version | string | Application version for compatibility checks |
| platform | string | Platform identifier for telemetry |
Response Message
{
"protocol_version": 1,
"response": {
"server_ephemeral_public_key": "<base64, 32 bytes>",
"encrypted_payload": "<base64>",
"encryption_nonce": "<base64, 24 bytes>",
"server_nonce": "<base64, 32 bytes>",
"client_nonce_echo": "<base64, 32 bytes>",
"key_version": 1,
"issued_at": 1702134567,
"expires_at": 1702138167
},
"signature": "<base64, 64 bytes>"
}
Decrypted Payload Structure
{
"credentials": {
"vertex_ai": {
"api_key": "<string>",
"project_id": "<string>",
"region": "<string>"
},
"openai": {
"api_key": "<string>",
"organization_id": "<string>"
}
},
"credential_metadata": {
"issued_at": 1702134567,
"rotation_hint": 1702220967
}
}
Cryptographic Operations
Key Agreement
Both client and server perform X25519 key agreement to derive a shared secret. The shared secret is never used directly; it is processed through HKDF to derive the encryption key.
shared_secret = X25519(local_private_key, remote_public_key)
Key Derivation
encryption_key = HKDF-SHA256(
ikm: shared_secret,
salt: client_nonce || server_nonce,
info: "inCamera credential encryption v1",
length: 32
)
Using both nonces in the salt ensures the derived key is unique to this specific request-response pair, even if the same ephemeral keys were somehow reused.
Encryption
ciphertext, tag = ChaCha20-Poly1305(
key: encryption_key,
nonce: encryption_nonce,
plaintext: credential_payload,
aad: key_version || issued_at || expires_at
)
The additional authenticated data (AAD) binds the ciphertext to the metadata, preventing an attacker from substituting metadata without detection.
Signature
signature = Ed25519-Sign(
signing_private_key,
canonical_json(response_without_signature)
)
Canonical JSON serialization ensures consistent byte representation across implementations.
Verification Steps
The client must perform all verification steps before using any credentials:
Signature Verification
Verify Ed25519 signature against embedded public key for the specified key_version
Nonce Binding
Verify client_nonce_echo exactly matches the nonce sent in the request
Timestamp Freshness
Verify issued_at is within acceptable clock skew (±30 seconds of current time)
Expiration Check
Verify current time is before expires_at
Decryption
Perform key agreement, derive key, decrypt payload. ChaCha20-Poly1305 verifies integrity automatically during decryption.
If any step fails, the entire response is rejected and credentials are not stored.
Security Analysis
Attack Resistance
Man-in-the-Middle (Including Cloudflare)
An attacker positioned between client and server (including Cloudflare itself) sees:
- Client's ephemeral public key (not secret)
- Encrypted credential payload (ciphertext only)
- Signature (verifiable but not forgeable)
The attacker cannot:
- Decrypt the payload (requires client's ephemeral private key, which is never transmitted)
- Forge a valid signature (requires server's signing private key)
- Substitute their own credentials (signature verification would fail)
Replay Attacks
Replaying a captured response fails because:
- The client_nonce_echo won't match the new request's nonce
- The issued_at timestamp will be stale
- Even if timing somehow aligned, the encryption key derivation uses both nonces, so a replayed response encrypted to a different ephemeral key cannot be decrypted
Forward Secrecy
An attacker who captures encrypted traffic cannot decrypt it later, even if they compromise server infrastructure, because:
- Both client and server use fresh ephemeral X25519 keys per-request
- The shared secret depends on both ephemeral keys
- Past ephemeral private keys no longer exist (zeroed from memory immediately after use)
- There is no long-term encryption key to compromise; only the signing key is long-term
Key Substitution
An attacker cannot substitute the embedded public keys without:
- Compromising our code signing certificate, or
- Compromising Apple/Microsoft's code signing infrastructure, or
- Convincing the user to install a modified, unsigned binary
Cryptographic Assumptions
This protocol's security relies on:
| Assumption | Implication if Broken |
|---|---|
| Ed25519 is secure | Signatures could be forged |
| X25519 is secure | Shared secrets could be computed by attackers |
| ChaCha20-Poly1305 is secure | Ciphertext could be decrypted or modified |
| HKDF-SHA256 is secure | Derived keys could be predictable |
| Platform CSPRNG is secure | Nonces and ephemeral keys could be predictable |
All of these are standard, well-analyzed primitives with no known practical attacks.
Operational Procedures
Key Generation
Server keys are generated in a secure environment with the following properties:
- Air-gapped machine or HSM
- Cryptographically secure random number generator
- Multiple witnesses for accountability
- Immediate secure backup of private keys
- Verification that public keys match private keys
Key Backup and Recovery
Private keys are backed up using Shamir's Secret Sharing with a 3-of-5 threshold. Key shares are distributed to geographically separated secure storage locations. Recovery requires coordination of at least three keyholders.
Incident Response
Suspected Credential Compromise (API Keys)
- Immediately rotate affected credentials with AI providers
- Push new credentials to server key vault
- Clients automatically receive new credentials on next refresh
- Review logs for anomalous API usage
- Notify affected users if data exposure is suspected
Suspected Signing Key Compromise
- Generate new signing keypair in secure environment
- Prepare emergency application update with new public key
- Begin signing responses with both old and new keys
- Push application update through all channels
- After transition period, revoke old signing key
- Conduct forensic investigation of compromise vector
Monitoring
The server logs (without logging sensitive values):
- Request timestamps and client versions
- Signature verification failures
- Decryption failures reported by clients
- Unusual request patterns
Alerts trigger on:
- Elevated signature verification failures (possible attack attempt)
- Requests with stale timestamps (possible replay attempt)
- Requests from deprecated client versions (possible vulnerability exploitation)
Compliance and Transparency
Third-Party Trust
This architecture minimizes required trust in third parties:
| Entity | What They See | What They Can Do |
|---|---|---|
| Cloudflare | Encrypted payloads, signatures | Nothing: cannot decrypt or forge |
| Network operators | TLS ciphertext to Cloudflare | Nothing: standard TLS protections |
| AI Providers | API calls from clients | Bound by ZDR agreements |
User Transparency
Users may request:
- This technical documentation
- The embedded public key fingerprints for independent verification
- Audit logs of credential issuance to their account
Independent Verification
Security researchers may verify the implementation by:
- Inspecting the application binary for embedded keys
- Capturing and analyzing protocol messages
- Verifying that payloads are encrypted and cannot be read without client cooperation
We welcome responsible disclosure of any identified vulnerabilities. Please contact our security team at [email protected].
Appendix
Cryptographic Parameter Reference
| Parameter | Value |
|---|---|
| Ed25519 public key size | 32 bytes |
| Ed25519 signature size | 64 bytes |
| X25519 public key size | 32 bytes |
| X25519 shared secret size | 32 bytes |
| ChaCha20-Poly1305 key size | 32 bytes |
| ChaCha20-Poly1305 nonce size | 24 bytes (XChaCha20) |
| ChaCha20-Poly1305 tag size | 16 bytes |
| HKDF hash | SHA-256 |
| Nonce size (protocol) | 32 bytes |
| Credential validity window | 3600 seconds (1 hour) |
| Clock skew tolerance | 30 seconds |
Future Considerations
Post-Quantum Cryptography
Current algorithms (X25519, Ed25519) are vulnerable to future quantum computers. When post-quantum standards mature, we will implement hybrid schemes that combine current algorithms with post-quantum alternatives, providing security against both classical and quantum adversaries.
Hardware Attestation
Future versions may incorporate platform attestation (Apple DeviceCheck, Android Play Integrity) to provide assurance that the client application has not been tampered with. This would strengthen the trust bootstrapping for embedded keys.
Formal Verification
We are evaluating formal verification of the protocol using tools like ProVerif or Tamarin to provide mathematical proof of security properties.
Related Documentation
Revision History
| Version | Date | Changes |
|---|---|---|
| 1.0 | December 2025 | Initial document with TLS pinning approach |
| 2.0 | December 2025 | Revised for end-to-end encryption over Cloudflare Tunnel |
| 2.1 | December 2025 | Clarified that server uses ephemeral X25519 keys per-request (no static identity key), providing stronger forward secrecy |