# #region APIKeyUtilities [C:2] [TYPE Module] [SEMANTICS auth, api_key, crypto, generation] # @BRIEF API key generation and hashing utilities for service-to-service authentication. # @LAYER Core # @RELATION DEPENDS_ON -> [EXT:Python:hashlib] # @RELATION DEPENDS_ON -> [EXT:Python:secrets] # @INVARIANT generate_api_key() always returns (raw_key, prefix, key_hash) where prefix is "ssk_" + 7 chars. # @INVARIANT hash_api_key() produces SHA-256 hex digest for lookup and storage. import hashlib import secrets # #region generate_api_key [C:2] [TYPE Function] # @BRIEF Generate a new API key in ssk_ format with SHA-256 hash. # @POST Returns (raw_key, prefix, key_hash) — raw_key shown ONCE to caller, never stored. # @SIDE_EFFECT Uses secrets.token_urlsafe(32) for cryptographic randomness. # @RELATION DEPENDS_ON -> [EXT:Python:secrets.token_urlsafe] # @RELATION DEPENDS_ON -> [EXT:Python:hashlib.sha256] def generate_api_key() -> tuple[str, str, str]: """Generate a new API key. Returns: tuple[str, str, str]: (raw_key, prefix, key_hash) - raw_key: Full key string starting with "ssk_", shown once - prefix: First 11 characters ("ssk_" + 7 chars), stored for identification - key_hash: SHA-256 hex digest, stored in database """ raw = f"ssk_{secrets.token_urlsafe(32)}" # 32 bytes -> 43 base64 chars prefix = raw[:11] # "ssk_" + 7 chars key_hash = hashlib.sha256(raw.encode()).hexdigest() return raw, prefix, key_hash # #endregion generate_api_key # #region hash_api_key [C:1] [TYPE Function] # @BRIEF Hash an API key string to SHA-256 hex digest for lookup. # @PRE raw_key is a non-empty string. # @POST Returns 64-character hex digest. # @RELATION DEPENDS_ON -> [EXT:Python:hashlib.sha256] def hash_api_key(raw_key: str) -> str: """Hash an API key using SHA-256. Args: raw_key: The full API key string (e.g. ssk_...) Returns: str: 64-character hex digest """ return hashlib.sha256(raw_key.encode()).hexdigest() # #endregion hash_api_key # #endregion APIKeyUtilities