Files
bigbrother/utils/credential_encryption.py
T
Cobra ffd384f64b Strip OPSEC tool identity fingerprints
Replace all sensor.* logger namespaces with __name__ (generic module
identifiers instead of discoverable 'sensor.*' prefixes).

Change hardcoded 'bb' API user to 'admin' in config and code defaults.

Change hardcoded relay_user 'bb' to 'operator' — prevents network
profiling from exposing tool identity via SSH config.

Fixes #457, #458, #459
2026-04-08 22:18:35 -04:00

121 lines
4.0 KiB
Python

#!/usr/bin/env python3
"""Helper to get encryption key from Infisical and encrypt credentials before emission."""
import subprocess
import json
import logging
from typing import Optional
logger = logging.getLogger(__name__)
# Cache the key in memory during module lifetime
_cached_key: Optional[bytes] = None
def get_credential_encryption_key() -> Optional[bytes]:
"""Retrieve credential encryption key from Infisical via creds CLI.
Returns None if key cannot be retrieved (in which case plaintext fallback).
Caches key in memory for the lifetime of the module process.
"""
global _cached_key
if _cached_key is not None:
return _cached_key
try:
# Try to get CREDENTIAL_ENCRYPTION_KEY from Infisical via creds CLI
result = subprocess.run(
["/home/n0mad1k/.local/bin/creds", "get", "CREDENTIAL_ENCRYPTION_KEY"],
capture_output=True,
timeout=5,
text=True,
)
if result.returncode != 0:
logger.warning("Failed to retrieve CREDENTIAL_ENCRYPTION_KEY from Infisical: %s", result.stderr)
return None
key_hex = result.stdout.strip()
if not key_hex:
logger.warning("CREDENTIAL_ENCRYPTION_KEY is empty in Infisical")
return None
# Convert hex string to bytes
_cached_key = bytes.fromhex(key_hex)
if len(_cached_key) != 32:
logger.error("CREDENTIAL_ENCRYPTION_KEY must be 32 bytes (256-bit), got %d", len(_cached_key))
_cached_key = None
return None
logger.debug("Successfully loaded CREDENTIAL_ENCRYPTION_KEY from Infisical")
return _cached_key
except FileNotFoundError:
logger.error("creds CLI not found at ~/.local/bin/creds")
return None
except subprocess.TimeoutExpired:
logger.error("Timeout retrieving CREDENTIAL_ENCRYPTION_KEY from Infisical")
return None
except Exception as e:
logger.error("Error retrieving CREDENTIAL_ENCRYPTION_KEY: %s", e)
return None
def emit_credential_found(bus, source_module: str, payload: dict) -> None:
"""Emit CREDENTIAL_FOUND event with encrypted sensitive fields.
Sensitive fields are encrypted before emission. If encryption fails,
plaintext fallback is used with a warning logged.
Args:
bus: EventBus instance
source_module: Module name emitting the event
payload: Credential payload dict with fields like username, password, etc.
"""
from utils.crypto import encrypt_credential_dict
key = get_credential_encryption_key()
if key:
try:
encrypted_payload = encrypt_credential_dict(payload, key)
bus.emit("CREDENTIAL_FOUND", encrypted_payload, source_module=source_module)
return
except Exception as e:
logger.warning("Failed to encrypt credential payload: %s — using plaintext fallback", e)
# Fallback: emit plaintext with warning
bus.emit("CREDENTIAL_FOUND", payload, source_module=source_module)
def decrypt_credential_payload(payload: dict) -> dict:
"""Decrypt sensitive fields in credential event payload.
Looks for fields marked with __encrypted__ prefix and decrypts them.
Returns a new dict with decrypted fields.
Args:
payload: Credential dict with possibly encrypted fields
Returns:
New dict with decrypted values
"""
from utils.crypto import decrypt_credential_field
key = get_credential_encryption_key()
if not key:
return payload
decrypted = {}
for field, value in payload.items():
if isinstance(value, str) and value.startswith("__encrypted__"):
try:
decrypted[field] = decrypt_credential_field(value, key)
except Exception as e:
logger.warning("Failed to decrypt field %s: %s", field, e)
decrypted[field] = value
else:
decrypted[field] = value
return decrypted