"""
Wallet Master Key Utilities
Shared logic for lazily loading 32-byte AES-256-GCM master keys for wallet
encryption. Keys are loaded exclusively from the configured environment
variable; the legacy Redis persistence/generation path has been removed for
security, so a missing or invalid key raises ``ValueError`` and disables
wallet features rather than minting a new key.
"""
import os
import base64
import logging
import socket
from datetime import datetime
from observability import observability
logger = logging.getLogger(__name__)
[docs]
async def ensure_master_key(
current_key: bytes | None,
redis_client,
redis_key: str,
env_var: str,
) -> bytes:
"""Return a 32-byte AES master key, loading exclusively from the environment variable.
Resolution order:
1. *current_key* – already loaded in memory (fast path).
2. Environment variable (*env_var*) – decode, validate.
3. If missing, log structured ASCII warning and raise exception to prevent unsafe operations.
Args:
current_key: The key already held in memory, or ``None``.
redis_client: An async Redis client (ignored, as Redis key persistence is removed for security).
redis_key: Redis key under which the master key was previously stored (ignored).
env_var: Name of the environment variable to check.
Returns:
A 32-byte ``bytes`` master key.
"""
# 1. Already loaded
if current_key is not None:
return current_key
# 2. Environment variable
key_b64 = os.environ.get(env_var)
if not key_b64:
border = "#" * 80
platform_type = env_var.split("_")[1].lower() if "_" in env_var else "wallet"
msg = (
f"\n{border}\n"
f"SECURITY CRITICAL WARNING: Missing environment variable '{env_var}'.\n"
"Stargazer v3 will boot in DEGRADED STATE. Wallet features are completely deactivated.\n"
f"{border}"
)
logger.error(msg)
observability.alert(
"CRITICAL",
"Wallet Master Keys are missing. Startup proceeding with wallet features disabled.",
metadata={
"hostname": socket.gethostname(),
"platform": platform_type,
"timestamp": datetime.utcnow().isoformat() + "Z",
},
)
raise ValueError(
f"Wallet master key environment variable '{env_var}' is missing. Wallet features are deactivated."
)
try:
key = base64.urlsafe_b64decode(key_b64)
if len(key) != 32:
raise ValueError(
f"Invalid key length: must be exactly 32 bytes (256-bit). Got {len(key)} bytes."
)
logger.info("Successfully loaded %s from environment variable.", env_var)
return key
except Exception as e:
border = "#" * 80
logger.error(
f"\n{border}\n"
f"SECURITY CRITICAL WARNING: Failed to decode environment variable '{env_var}': {e}\n"
"Stargazer v3 will boot in DEGRADED STATE. Wallet features are completely deactivated.\n"
f"{border}",
exc_info=True,
)
observability.alert(
"CRITICAL",
"Wallet Master Keys are missing. Startup proceeding with wallet features disabled.",
metadata={
"hostname": socket.gethostname(),
"platform": (
env_var.split("_")[1].lower() if "_" in env_var else "wallet"
),
"timestamp": datetime.utcnow().isoformat() + "Z",
"error": str(e),
},
)
raise ValueError(f"Failed to decode wallet master key from '{env_var}': {e}")