import logging from dataclasses import dataclass from typing import Dict, Optional, Any import os import json from cryptography.hazmat.primitives.ciphers.aead import AESGCM from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC from security.rbac_engine import RBACEngine, ClientCertInfo logger = logging.getLogger('MemoryCore') class EncryptionError(Exception): pass class DecryptionError(Exception): pass class AccessDenied(Exception): pass class NotFound(Exception): pass @dataclass class AuditEntry: timestamp: str operation: str key_hash: str status: bool caller: str details: Optional[str] = None class MemoryCore: def __init__(self, encryption_key: bytes, rbac_engine: RBACEngine): # Initialize encryption salt = os.urandom(16) kdf = PBKDF2HMAC( algorithm=hashes.SHA256(), length=32, salt=salt, iterations=100000, ) self.aes_key = kdf.derive(encryption_key) self.salt = salt # Data storage self.data: Dict[str, bytes] = {} # RBAC integration self.rbac = rbac_engine # Audit log self.audit_log: list[AuditEntry] = [] def _encrypt(self, plaintext: bytes) -> bytes: """Encrypt data using AES-256-GCM""" nonce = os.urandom(12) aesgcm = AESGCM(self.aes_key) ciphertext = aesgcm.encrypt(nonce, plaintext, None) return nonce + ciphertext def _decrypt(self, ciphertext: bytes) -> bytes: """Decrypt data using AES-256-GCM""" nonce = ciphertext[:12] ciphertext = ciphertext[12:] aesgcm = AESGCM(self.aes_key) try: return aesgcm.decrypt(nonce, ciphertext, None) except Exception as e: raise DecryptionError(f"Decryption failed: {str(e)}") def _hash_key(self, key: str) -> str: """Create secure hash of key for audit logging""" return hashlib.sha256(key.encode()).hexdigest() def _audit(self, operation: str, key: str, status: bool, caller: Optional[str] = None, details: Optional[str] = None): """Record audit entry""" entry = AuditEntry( timestamp=datetime.now().isoformat(), operation=operation, key_hash=self._hash_key(key), status=status, caller=caller or "system", details=details ) self.audit_log.append(entry) logger.info(f"Audit: {entry}") def create(self, key: str, value: bytes, user: Optional[str] = None, cert_info: Optional[ClientCertInfo] = None) -> bool: """Create new encrypted entry with RBAC check""" if not self.rbac.validate_permission("memory", "create", user=user, client_cert_info=cert_info): self._audit("create", key, False, user or cert_info.subject.get('CN'), "RBAC check failed") raise AccessDenied("Create permission denied") try: encrypted = self._encrypt(value) self.data[key] = encrypted self._audit("create", key, True, user or cert_info.subject.get('CN')) return True except Exception as e: self._audit("create", key, False, user or cert_info.subject.get('CN'), str(e)) raise EncryptionError(f"Encryption failed: {str(e)}") def read(self, key: str, user: Optional[str] = None, cert_info: Optional[ClientCertInfo] = None) -> bytes: """Read and decrypt entry with RBAC check""" if not self.rbac.validate_permission("memory", "read", user=user, client_cert_info=cert_info): self._audit("read", key, False, user or cert_info.subject.get('CN'), "RBAC check failed") raise AccessDenied("Read permission denied") if key not in self.data: self._audit("read", key, False, user or cert_info.subject.get('CN'), "Key not found") raise NotFound(f"Key {key} not found") try: decrypted = self._decrypt(self.data[key]) self._audit("read", key, True, user or cert_info.subject.get('CN')) return decrypted except Exception as e: self._audit("read", key, False, user or cert_info.subject.get('CN'), str(e)) raise DecryptionError(f"Decryption failed: {str(e)}") def update(self, key: str, value: bytes, user: Optional[str] = None, cert_info: Optional[ClientCertInfo] = None) -> bool: """Update encrypted entry with RBAC check""" if not self.rbac.validate_permission("memory", "update", user=user, client_cert_info=cert_info): self._audit("update", key, False, user or cert_info.subject.get('CN'), "RBAC check failed") raise AccessDenied("Update permission denied") if key not in self.data: self._audit("update", key, False, user or cert_info.subject.get('CN'), "Key not found") raise NotFound(f"Key {key} not found") try: encrypted = self._encrypt(value) self.data[key] = encrypted self._audit("update", key, True, user or cert_info.subject.get('CN')) return True except Exception as e: self._audit("update", key, False, user or cert_info.subject.get('CN'), str(e)) raise EncryptionError(f"Encryption failed: {str(e)}") def delete(self, key: str, user: Optional[str] = None, cert_info: Optional[ClientCertInfo] = None) -> bool: """Delete entry with RBAC check""" if not self.rbac.validate_permission("memory", "delete", user=user, client_cert_info=cert_info): self._audit("delete", key, False, user or cert_info.subject.get('CN'), "RBAC check failed") raise AccessDenied("Delete permission denied") if key not in self.data: self._audit("delete", key, False, user or cert_info.subject.get('CN'), "Key not found") raise NotFound(f"Key {key} not found") del self.data[key] self._audit("delete", key, True, user or cert_info.subject.get('CN')) return True