Files
system-docs/v4-archive/contratos-comunes/docs/KEY_VAULT.md
2025-12-24 17:28:34 +00:00

28 KiB

07c. KEY VAULT Y CRIPTOGRAFÍA

Manual de Arquitectura Técnica — Documento 07c
Versión: 1.2
Dependencia: S-CONTRACT.md
Estado: Enterprise Standard


C.1 Introducción

El Key Vault es el tercer espacio de almacenamiento del ecosistema, separado de:

  • Libro Mayor (SFE): Datos de negocio
  • Almacenamiento de archivos: Hostinger/S3
  • Key Vault: Llaves, credenciales y secretos
┌─────────────────────────────────────────────────────────────────┐
│                    PRINCIPIO DE SEPARACIÓN                      │
│                                                                 │
│   "Las llaves nunca viajan con los datos que protegen"         │
└─────────────────────────────────────────────────────────────────┘

C.2 Arquitectura del Key Vault

C.2.1 Componentes

┌─────────────────────────────────────────────────────────────────┐
│                         KEY VAULT                               │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌─────────────┐    ┌─────────────┐    ┌─────────────┐         │
│  │   SECRETS   │    │    KEYS     │    │    CERTS    │         │
│  │   STORE     │    │   STORE     │    │   STORE     │         │
│  └─────────────┘    └─────────────┘    └─────────────┘         │
│         │                  │                  │                 │
│         └──────────────────┼──────────────────┘                 │
│                            │                                    │
│                     ┌──────┴──────┐                             │
│                     │   ACCESS    │                             │
│                     │   CONTROL   │                             │
│                     └──────┬──────┘                             │
│                            │                                    │
│                     ┌──────┴──────┐                             │
│                     │  AUDIT LOG  │                             │
│                     └─────────────┘                             │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

C.2.2 Tipos de Secretos

Tipo Descripción Ejemplo Rotación
API_KEY Llaves de APIs externas OpenRouter, Groq 90 días
ENCRYPTION_KEY Llaves de cifrado de datos AES-256 keys 365 días
DB_CREDENTIAL Credenciales de bases de datos NocoDB, PostgreSQL 30 días
SERVICE_TOKEN Tokens de servicios internos n8n, Nextcloud 7 días
USER_SECRET Secretos específicos de usuario OAuth tokens Variable
CERTIFICATE Certificados TLS/mTLS SSL certs 90 días
SIGNING_KEY Llaves de firma digital JWT signing 180 días

C.3 Modelo de Datos

C.3.1 Tabla VAULT_SECRETS

CREATE TABLE VAULT_SECRETS (
  -- ═══════════════════════════════════════════════════════════
  -- IDENTIFICACIÓN
  -- ═══════════════════════════════════════════════════════════
  id                    UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  secret_id             VARCHAR(100) UNIQUE NOT NULL,
  version               INTEGER DEFAULT 1,
  
  -- ═══════════════════════════════════════════════════════════
  -- CLASIFICACIÓN
  -- ═══════════════════════════════════════════════════════════
  secret_type           VARCHAR(50) NOT NULL,
  category              VARCHAR(50),
  tags                  VARCHAR(50)[],
  
  -- ═══════════════════════════════════════════════════════════
  -- VALOR (CIFRADO)
  -- ═══════════════════════════════════════════════════════════
  encrypted_value       BYTEA NOT NULL,
  encryption_algorithm  VARCHAR(50) DEFAULT 'AES-256-GCM',
  key_encryption_key_id VARCHAR(100),
  nonce                 BYTEA,
  
  -- ═══════════════════════════════════════════════════════════
  -- METADATA
  -- ═══════════════════════════════════════════════════════════
  description           TEXT,
  owner_id              UUID,
  owner_type            VARCHAR(50),
  
  -- ═══════════════════════════════════════════════════════════
  -- CICLO DE VIDA
  -- ═══════════════════════════════════════════════════════════
  status                VARCHAR(20) DEFAULT 'ACTIVE',
  created_at            TIMESTAMPTZ DEFAULT NOW(),
  updated_at            TIMESTAMPTZ DEFAULT NOW(),
  expires_at            TIMESTAMPTZ,
  last_rotated_at       TIMESTAMPTZ,
  rotation_interval_days INTEGER,
  
  -- ═══════════════════════════════════════════════════════════
  -- ACCESO
  -- ═══════════════════════════════════════════════════════════
  access_policy         JSONB DEFAULT '{}',
  allowed_modules       VARCHAR(50)[],
  allowed_players       UUID[],
  
  -- ═══════════════════════════════════════════════════════════
  -- AUDITORÍA
  -- ═══════════════════════════════════════════════════════════
  access_count          INTEGER DEFAULT 0,
  last_accessed_at      TIMESTAMPTZ,
  last_accessed_by      VARCHAR(100),
  
  -- ═══════════════════════════════════════════════════════════
  -- CONSTRAINTS
  -- ═══════════════════════════════════════════════════════════
  CONSTRAINT valid_status CHECK (
    status IN ('ACTIVE', 'DISABLED', 'EXPIRED', 'ROTATING', 'DELETED')
  ),
  CONSTRAINT valid_type CHECK (
    secret_type IN ('API_KEY', 'ENCRYPTION_KEY', 'DB_CREDENTIAL', 
                    'SERVICE_TOKEN', 'USER_SECRET', 'CERTIFICATE', 'SIGNING_KEY')
  )
);

-- Índices
CREATE INDEX idx_vault_secret_id ON VAULT_SECRETS(secret_id);
CREATE INDEX idx_vault_type ON VAULT_SECRETS(secret_type);
CREATE INDEX idx_vault_status ON VAULT_SECRETS(status);
CREATE INDEX idx_vault_expires ON VAULT_SECRETS(expires_at) WHERE expires_at IS NOT NULL;
CREATE INDEX idx_vault_owner ON VAULT_SECRETS(owner_id);

C.3.2 Tabla VAULT_ACCESS_LOG

CREATE TABLE VAULT_ACCESS_LOG (
  -- ═══════════════════════════════════════════════════════════
  -- IDENTIFICACIÓN
  -- ═══════════════════════════════════════════════════════════
  id                    UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  
  -- ═══════════════════════════════════════════════════════════
  -- SECRETO ACCEDIDO
  -- ═══════════════════════════════════════════════════════════
  secret_id             VARCHAR(100) NOT NULL,
  secret_version        INTEGER,
  
  -- ═══════════════════════════════════════════════════════════
  -- CONTEXTO DE ACCESO
  -- ═══════════════════════════════════════════════════════════
  trace_id              UUID,
  step_id               UUID,
  module_name           VARCHAR(50),
  operation             VARCHAR(50) NOT NULL,
  
  -- ═══════════════════════════════════════════════════════════
  -- ACTOR
  -- ═══════════════════════════════════════════════════════════
  accessor_type         VARCHAR(50) NOT NULL,
  accessor_id           VARCHAR(100) NOT NULL,
  accessor_ip           INET,
  
  -- ═══════════════════════════════════════════════════════════
  -- RESULTADO
  -- ═══════════════════════════════════════════════════════════
  status                VARCHAR(20) NOT NULL,
  error_code            VARCHAR(50),
  error_message         TEXT,
  
  -- ═══════════════════════════════════════════════════════════
  -- TEMPORALIDAD
  -- ═══════════════════════════════════════════════════════════
  timestamp             TIMESTAMPTZ DEFAULT NOW(),
  duration_ms           INTEGER,
  
  -- ═══════════════════════════════════════════════════════════
  -- SEGURIDAD
  -- ═══════════════════════════════════════════════════════════
  encryption_profile    VARCHAR(20),
  purpose               TEXT,
  
  -- ═══════════════════════════════════════════════════════════
  -- CONSTRAINTS
  -- ═══════════════════════════════════════════════════════════
  CONSTRAINT valid_operation CHECK (
    operation IN ('READ', 'CREATE', 'UPDATE', 'DELETE', 'ROTATE', 
                  'ENABLE', 'DISABLE', 'LIST', 'DECRYPT', 'ENCRYPT')
  ),
  CONSTRAINT valid_access_status CHECK (
    status IN ('SUCCESS', 'DENIED', 'ERROR', 'EXPIRED', 'NOT_FOUND')
  ),
  CONSTRAINT valid_accessor CHECK (
    accessor_type IN ('MODULE', 'PLAYER', 'SYSTEM', 'ADMIN', 'SERVICE')
  )
);

-- Índices para auditoría eficiente
CREATE INDEX idx_vault_log_secret ON VAULT_ACCESS_LOG(secret_id);
CREATE INDEX idx_vault_log_trace ON VAULT_ACCESS_LOG(trace_id) WHERE trace_id IS NOT NULL;
CREATE INDEX idx_vault_log_timestamp ON VAULT_ACCESS_LOG(timestamp);
CREATE INDEX idx_vault_log_accessor ON VAULT_ACCESS_LOG(accessor_type, accessor_id);
CREATE INDEX idx_vault_log_denied ON VAULT_ACCESS_LOG(status, timestamp) 
  WHERE status = 'DENIED';

C.3.3 Tabla VAULT_ENCRYPTION_KEYS (KEK)

CREATE TABLE VAULT_ENCRYPTION_KEYS (
  -- ═══════════════════════════════════════════════════════════
  -- IDENTIFICACIÓN
  -- ═══════════════════════════════════════════════════════════
  id                    UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  key_id                VARCHAR(100) UNIQUE NOT NULL,
  version               INTEGER DEFAULT 1,
  
  -- ═══════════════════════════════════════════════════════════
  -- PROPIEDADES
  -- ═══════════════════════════════════════════════════════════
  algorithm             VARCHAR(50) NOT NULL DEFAULT 'AES-256-GCM',
  key_size_bits         INTEGER NOT NULL DEFAULT 256,
  purpose               VARCHAR(50) NOT NULL,
  
  -- ═══════════════════════════════════════════════════════════
  -- VALOR (protegido por Master Key externa)
  -- ═══════════════════════════════════════════════════════════
  encrypted_key_material BYTEA NOT NULL,
  master_key_ref        VARCHAR(100),
  
  -- ═══════════════════════════════════════════════════════════
  -- CICLO DE VIDA
  -- ═══════════════════════════════════════════════════════════
  status                VARCHAR(20) DEFAULT 'ACTIVE',
  created_at            TIMESTAMPTZ DEFAULT NOW(),
  activated_at          TIMESTAMPTZ,
  expires_at            TIMESTAMPTZ,
  deactivated_at        TIMESTAMPTZ,
  
  -- ═══════════════════════════════════════════════════════════
  -- CONSTRAINTS
  -- ═══════════════════════════════════════════════════════════
  CONSTRAINT valid_kek_status CHECK (
    status IN ('PENDING', 'ACTIVE', 'DEACTIVATED', 'DESTROYED')
  ),
  CONSTRAINT valid_purpose CHECK (
    purpose IN ('DATA_ENCRYPTION', 'SECRET_ENCRYPTION', 'SIGNING', 'KEY_WRAPPING')
  )
);

C.4 Perfiles de Cifrado

C.4.1 Perfil NONE

profile: NONE
description: "Sin cifrado gestionado por el sistema"
transport: TLS 1.3 (obligatorio)
at_rest: Sin cifrar
use_cases:
  - Datos públicos
  - Logs técnicos no sensibles
  - Métricas de rendimiento
key_vault: No requerido

C.4.2 Perfil E2E_BASIC

profile: E2E_BASIC
description: "Cifrado estándar para datos internos"
transport: TLS 1.3
at_rest:
  algorithm: AES-256-GCM
  key_derivation: PBKDF2-SHA256
  key_rotation: Anual
use_cases:
  - Documentos internos
  - Emails corporativos
  - Datos de configuración
key_vault:
  required: true
  key_type: ENCRYPTION_KEY
  access_policy: module_based

C.4.3 Perfil E2E_STRICT

profile: E2E_STRICT
description: "Cifrado fuerte para datos sensibles"
transport: mTLS (mutual TLS)
at_rest:
  algorithm: AES-256-GCM
  key_derivation: Argon2id
  key_rotation: Trimestral
  envelope_encryption: true
additional:
  - PII masking antes de procesar
  - Audit logging obligatorio
  - Zero-knowledge donde sea posible
use_cases:
  - PII (datos personales)
  - Datos financieros
  - Información médica
  - Biometría
key_vault:
  required: true
  key_type: ENCRYPTION_KEY
  access_policy: per_record
  key_isolation: true

C.5 Operaciones del Key Vault

C.5.1 URIs del Key Vault

El contrato común usa referencias URI al Key Vault:

kv://production/encryption/player_abc123
kv://production/api-keys/openrouter
kv://staging/certificates/mtls-client

Formato:

kv://{environment}/{category}/{secret_id}

C.5.2 Integración con el Contrato

En el request:

"security": {
  "encryption_profile": "E2E_STRICT",
  "data_sensitivity": "HIGH",
  "key_vault_ref": "kv://production/encryption/player_abc123"
}

Alfred al procesar:

  1. Extrae key_vault_ref
  2. Solicita la llave al Key Vault (pasando trace_id)
  3. Descifra el payload si viene cifrado
  4. Procesa con GRACE
  5. Cifra el resultado con la misma llave (o una derivada)
  6. Almacena resultado cifrado

C.6 Flujo de Cifrado End-to-End

C.6.1 Envelope Encryption

┌─────────────────────────────────────────────────────────────────┐
│                    ENVELOPE ENCRYPTION                          │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌─────────────┐                                                │
│  │ Plaintext   │                                                │
│  │ Data        │                                                │
│  └──────┬──────┘                                                │
│         │                                                       │
│         ▼                                                       │
│  ┌─────────────┐    ┌─────────────┐                            │
│  │ Data        │◄───│ DEK         │  (Data Encryption Key)     │
│  │ Encrypted   │    │ (efímera)   │                            │
│  └──────┬──────┘    └──────┬──────┘                            │
│         │                  │                                    │
│         │                  ▼                                    │
│         │           ┌─────────────┐    ┌─────────────┐         │
│         │           │ DEK         │◄───│ KEK         │         │
│         │           │ Encrypted   │    │ (Key Vault) │         │
│         │           └──────┬──────┘    └─────────────┘         │
│         │                  │                                    │
│         ▼                  ▼                                    │
│  ┌──────────────────────────────────────┐                      │
│  │ Stored Package:                       │                      │
│  │ - encrypted_data                      │                      │
│  │ - encrypted_dek                       │                      │
│  │ - kek_id                              │                      │
│  │ - nonce                               │                      │
│  │ - algorithm                           │                      │
│  └──────────────────────────────────────┘                      │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

C.6.2 Implementación Python

import os
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import base64

class EnvelopeEncryption:
    """Implementa envelope encryption para E2E_STRICT."""
    
    def __init__(self, key_vault_client):
        self.vault = key_vault_client
    
    async def encrypt(self, plaintext: bytes, kek_id: str) -> dict:
        """Cifra datos usando envelope encryption."""
        # 1. Generar DEK efímera
        dek = os.urandom(32)
        data_nonce = os.urandom(12)
        
        # 2. Cifrar datos con DEK
        aesgcm = AESGCM(dek)
        encrypted_data = aesgcm.encrypt(data_nonce, plaintext, None)
        
        # 3. Obtener KEK y cifrar DEK
        kek = await self.vault.get_secret(kek_id)
        dek_nonce = os.urandom(12)
        kek_aesgcm = AESGCM(kek)
        encrypted_dek = kek_aesgcm.encrypt(dek_nonce, dek, kek_id.encode())
        
        return {
            "encrypted_data": base64.b64encode(encrypted_data).decode(),
            "encrypted_dek": base64.b64encode(encrypted_dek).decode(),
            "data_nonce": base64.b64encode(data_nonce).decode(),
            "dek_nonce": base64.b64encode(dek_nonce).decode(),
            "kek_id": kek_id,
            "algorithm": "AES-256-GCM"
        }
    
    async def decrypt(self, package: dict) -> bytes:
        """Descifra datos usando envelope encryption."""
        # 1. Obtener KEK
        kek = await self.vault.get_secret(package["kek_id"])
        
        # 2. Descifrar DEK
        kek_aesgcm = AESGCM(kek)
        dek = kek_aesgcm.decrypt(
            base64.b64decode(package["dek_nonce"]),
            base64.b64decode(package["encrypted_dek"]),
            package["kek_id"].encode()
        )
        
        # 3. Descifrar datos
        aesgcm = AESGCM(dek)
        return aesgcm.decrypt(
            base64.b64decode(package["data_nonce"]),
            base64.b64decode(package["encrypted_data"]),
            None
        )

C.7 Gestión de Llaves

C.7.1 Jerarquía de Llaves

┌─────────────────────────────────────────────────────────────────┐
│                    JERARQUÍA DE LLAVES                          │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │                    MASTER KEY (MK)                       │   │
│  │           Almacenada fuera del sistema                   │   │
│  │        (HSM / KMS externo / Variable de entorno)         │   │
│  └────────────────────────┬────────────────────────────────┘   │
│                           │                                     │
│           ┌───────────────┼───────────────┐                     │
│           ▼               ▼               ▼                     │
│  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐               │
│  │ KEK-DATA    │ │ KEK-SECRETS │ │ KEK-SIGNING │               │
│  └──────┬──────┘ └──────┬──────┘ └──────┬──────┘               │
│         │               │               │                       │
│         ▼               ▼               ▼                       │
│  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐               │
│  │ DEK-1..n    │ │ API Keys    │ │ JWT Keys    │               │
│  └─────────────┘ └─────────────┘ └─────────────┘               │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

C.7.2 Política de Rotación

Tipo de Llave Rotación Período de Gracia Automatizable
Master Key Anual 30 días No (manual)
KEK Semestral 14 días
DEK Por sesión N/A Automático
API Keys 90 días 7 días

C.8 Control de Acceso

C.8.1 Políticas de Acceso

{
  "secret_id": "openrouter-api-key",
  "access_policy": {
    "type": "MODULE_BASED",
    "rules": [
      {
        "principal_type": "MODULE",
        "principals": ["CLASSIFIER", "SUMMARIZER", "OCR_CORE"],
        "operations": ["READ"],
        "conditions": {
          "require_trace_id": true
        }
      },
      {
        "principal_type": "ADMIN",
        "principals": ["admin@tzzr.pro"],
        "operations": ["READ", "UPDATE", "ROTATE", "DELETE"],
        "conditions": {
          "require_mfa": true
        }
      }
    ],
    "default_deny": true
  }
}

C.9 Integración con SENTINEL

C.9.1 Reglas de Auditoría

rules:
  KV-001:
    name: "Accesos denegados excesivos"
    severity: HIGH
    condition: "denied_count_1h > 5 per accessor"
    action: ALERT_SECURITY
      
  KV-002:
    name: "Secreto próximo a expirar"
    severity: MEDIUM
    condition: "expires_at < NOW() + 7 days"
    action: NOTIFY_ADMIN
      
  KV-003:
    name: "Rotación pendiente"
    severity: MEDIUM
    condition: "last_rotated_at < NOW() - rotation_interval"
    action: SCHEDULE_ROTATION

C.10 Checklist de Implementación

Infraestructura:

  • Crear tablas VAULT_SECRETS, VAULT_ACCESS_LOG, VAULT_ENCRYPTION_KEYS
  • Configurar Master Key (variable de entorno o KMS)
  • Generar KEKs iniciales
  • Configurar backup automático

Integración:

  • Implementar cliente Key Vault
  • Integrar con ContractBuilder
  • Implementar envelope encryption

Seguridad:

  • Configurar políticas de acceso
  • Habilitar logging de accesos
  • Configurar alertas SENTINEL
  • Establecer rotación automática

Fin del Documento 07c — Key Vault y Criptografía

Referencia: S-CONTRACT.md v1.2