How OmniRoute Secures and Encrypts API Keys: HMAC Validation and AES-256-GCM Explained

OmniRoute implements a dual-layer security model that combines HMAC-based CRC checksums for integrity validation and AES-256-GCM encryption for data at rest, ensuring both tamper detection and cryptographic confidentiality.

Understanding how API keys are secured and encrypted in OmniRoute reveals a defense-in-depth architecture addressing credential protection at every layer. The system utilizes environment-derived secrets and industry-standard cryptographic primitives spanning from startup validation to persistent storage. This implementation is distributed across critical modules including src/shared/utils/apiKey.ts for checksum generation and src/lib/db/encryption.ts for database-level encryption.

HMAC-Based Integrity Validation

Every API key generated by OmniRoute contains an embedded CRC8-style checksum computed using a secret HMAC value. This mechanism ensures that forged or tampered keys are immediately rejected during validation.

The API_KEY_SECRET Mechanism

The integrity system relies on API_KEY_SECRET, a required environment variable that seeds the checksum algorithm. According to the source code in instrumentation-node.ts, this secret is either supplied via environment configuration or automatically generated and persisted on first launch. If the secret is missing at runtime, the system emits a security warning and disables CRC validation.

The validator in src/shared/utils/secretsValidator.ts aborts server startup if required secrets are malformed or missing, ensuring cryptographic operations never execute with weak parameters.

PBKDF2-Based CRC Generation

The CRC checksum is generated using PBKDF2 as an HMAC-like construction, satisfying modern security scanning requirements while producing deterministic 8-character hexadecimal checksums. The implementation in src/shared/utils/apiKey.ts follows this pattern:

function generateCrc(machineId: string, keyId: string): string {
  const secret = getApiKeySecret();
  // PBKDF2 used as HMAC-like construction (CodeQL compliant)
  return crypto
    .pbkdf2Sync(machineId + keyId, secret, 1000, 32, "sha256")
    .toString("hex")
    .slice(0, 8);
}

export function generateApiKeyWithMachine(machineId: string) {
  const keyId = generateKeyId();               // 6-character random identifier
  const crc = generateCrc(machineId, keyId);   // 8-character checksum
  const key = `sk-${machineId}-${keyId}-${crc}`;
  return { key, keyId };
}

The resulting API key follows the format sk-{machineId}-{keyId}-{crc}, where the CRC segment validates that the key was legitimately generated by an instance possessing the API_KEY_SECRET.

AES-256-GCM Encryption for At-Rest Data

While HMAC validation protects key integrity during transmission and validation, AES-256-GCM encryption protects credentials persisted to the SQLite database.

Storage Encryption Implementation

The encryption module at src/lib/db/encryption.ts handles all credential storage using authenticated encryption. The implementation uses a static salt combined with the STORAGE_ENCRYPTION_KEY environment variable to derive a 32-byte encryption key via scrypt:

const STATIC_SALT = "omniroute-field-encryption-v1";

export function encrypt(plaintext: string | null | undefined): string | null | undefined {
  if (plaintext == null) return plaintext;
  const key = crypto.scryptSync(process.env.STORAGE_ENCRYPTION_KEY!, STATIC_SALT, 32);
  const iv = crypto.randomBytes(12);
  const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
  
  let encrypted = cipher.update(plaintext, "utf8", "hex");
  encrypted += cipher.final("hex");
  const authTag = cipher.getAuthTag().toString("hex");
  
  // Format: enc:v1:{iv}:{ciphertext}:{authTag}
  return `enc:v1:${iv.toString("hex")}:${encrypted}:${authTag}`;
}

Key Derivation and Salt Strategy

The encryption system utilizes scrypt for key derivation with a static application-specific salt (omniroute-field-encryption-v1). Each encryption operation generates a random 12-byte IV (Initialization Vector) to ensure ciphertext uniqueness. The enc:v1: prefix marks encrypted values, allowing the system to distinguish ciphertext from plaintext during decryption operations.

This design ensures that even with database read access, an attacker cannot decrypt stored refresh tokens or relay authentication blobs without the STORAGE_ENCRYPTION_KEY.

Startup Validation and Secret Lifecycle

Both cryptographic systems are validated early in the server lifecycle by src/shared/utils/secretsValidator.ts. This module verifies that:

  • API_KEY_SECRET is present and non-empty for checksum operations
  • STORAGE_ENCRYPTION_KEY is available for database encryption
  • Generated secrets meet minimum entropy requirements

The validator aborts process startup if critical secrets are missing, preventing the server from running in a degraded security state.

Summary

  • Tamper detection is implemented via HMAC-based CRC8 checksums generated with PBKDF2, ensuring only instances possessing API_KEY_SECRET can create valid API keys.
  • Confidentiality at rest is enforced through AES-256-GCM encryption using scrypt-derived keys and random IVs, protecting stored credentials in the SQLite database.
  • Fail-secure defaults require both API_KEY_SECRET and STORAGE_ENCRYPTION_KEY at startup, with automatic generation available for the former during first launch.
  • CodeQL-compliant construction uses PBKDF2 as an HMAC alternative to satisfy static analysis tools while maintaining cryptographic strength.

Frequently Asked Questions

What happens if API_KEY_SECRET is not configured?

If API_KEY_SECRET is missing, OmniRoute emits a security warning to the console and disables CRC validation for API keys. However, the startup validator in src/shared/utils/secretsValidator.ts typically prevents this scenario by aborting the process or triggering automatic generation via instrumentation-node.ts on first launch.

How does OmniRoute encrypt stored provider credentials?

Provider refresh tokens and relay authentication blobs are encrypted using AES-256-GCM before being written to SQLite. The encryption function in src/lib/db/encryption.ts applies scrypt key derivation with a static salt, generates a random 12-byte IV for each operation, and stores the result in the format enc:v1:{iv}:{ciphertext}:{authTag}.

What cryptographic algorithms protect OmniRoute API keys?

OmniRoute uses PBKDF2-HMAC-SHA256 (1000 iterations, 32 bytes output) for generating the integrity checksums on API keys, and AES-256-GCM with scrypt key derivation for encrypting data at rest. The CRC checksum in API keys provides tamper detection without exposing the underlying secret.

Can encrypted database values be decrypted without the STORAGE_ENCRYPTION_KEY?

No. The STORAGE_ENCRYPTION_KEY is required to derive the decryption key via scrypt using the static salt defined in src/lib/db/encryption.ts. Without this environment variable, encrypted values in the database remain cryptographically secure and unreadable, even if an attacker gains direct database access.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →