> ## Documentation Index
> Fetch the complete documentation index at: https://woku.app/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Cifrado en reposo y firmas de integridad

> Cómo Woku protege datos sensibles almacenados y cómo asegura que documentos críticos no se modifiquen sin detectarlo

Woku usa dos primitivas criptográficas para proteger los datos que
guarda en su base:

* **Cifrado AES-256-GCM** para campos sensibles en reposo.
* **HMAC-SHA256** para firmar documentos críticos y detectar
  cualquier modificación posterior.

Ambas primitivas viven en un único `EncryptionService` del backend y
usan la misma clave maestra (`ENCRYPTION_KEY`) derivada vía `scrypt`.

## Cifrado en reposo

### Cuándo se cifra

Todo campo que contenga información personal o credenciales se cifra
antes de persistirse en MongoDB. En particular:

* **Datos de cliente** en respuestas anónimas: email y teléfono se
  almacenan cifrados (`form-submission`).
* **Credenciales de integraciones** y tokens de OAuth (cuando una
  empresa conecta Woku con Salesforce, Sperant, etc.).
* **Secrets de webhooks** que la empresa configura para recibir
  eventos.

### Cómo funciona

|                    | Valor                                              |
| ------------------ | -------------------------------------------------- |
| Algoritmo          | AES-256-GCM (autenticación integrada vía auth tag) |
| Llave              | Derivada con `scrypt(ENCRYPTION_KEY, salt, 32)`    |
| IV                 | Aleatorio de 12 bytes por cada cifrado             |
| Formato persistido | `<iv-hex>:<authTag-hex>:<ciphertext-hex>`          |

Cada cifrado usa un IV nuevo, así que cifrar dos veces el mismo valor
produce dos ciphertexts distintos (no se puede inferir igualdad por
inspección). El `authTag` se valida en cada descifrado: si el
ciphertext fue alterado, el descifrado lanza un error en vez de
devolver basura.

### Búsqueda sobre campos cifrados

Para poder buscar por email sin descifrar todos los registros, Woku
guarda un hash SHA-256 paralelo (`generateSearchHash`) en lowercase.
La búsqueda compara hash contra hash, no texto contra texto.

## Firmas de integridad (HMAC-SHA256)

### Para qué sirven

Cuando una empresa exige que sus respuestas de formularios no puedan
ser modificadas después de almacenarse (auditoría legal, compliance
de sector regulado), Woku puede firmar cada documento con
**HMAC-SHA256** al momento de escribirlo y verificar la firma al
leerlo. Si alguien modifica el documento en la base directamente,
la verificación falla y la lectura devuelve error.

### Cómo funciona

```
firma = HMAC-SHA256(ENCRYPTION_KEY, "<scope>:<json canonical del doc>")
```

|              | Detalle                                   |
| ------------ | ----------------------------------------- |
| Algoritmo    | HMAC-SHA256                               |
| Llave        | La misma `ENCRYPTION_KEY` derivada arriba |
| Entrada      | Concatenación `<scope>:<json>`            |
| Salida       | 64 caracteres hex                         |
| Verificación | Constant-time (`crypto.timingSafeEqual`)  |

**Scope = separación de dominio**: cada caso de uso firma con un
scope distinto (ej: `form-submission`, `form-response`). Una firma
generada para un scope **no es válida** en otro, así un atacante no
puede mover firmas entre tipos de documentos.

**JSON canónico**: Woku ordena recursivamente las llaves de los
objetos antes de firmar, así dos representaciones lógicamente iguales
del mismo documento producen la misma firma (independiente de en qué
orden las recibió la API o las escribió Mongo). Los arrays NO se
reordenan, el orden posicional es semánticamente significativo.

### Activar para tu empresa

Hay un flag por empresa: `integrityHashEnabled`. Cuando se activa,
las nuevas escrituras de documentos críticos se firman; las lecturas
verifican y fallan si la firma no coincide.

> **Estado actual:** la primitiva (`signDocument` / `verifySignature`)
> ya está disponible en el backend y el flag existe en el modelo de
> empresa. La integración automática con `form-submission` y
> `form-response` (cuando `integrityHashEnabled` está activo) llega
> en un release posterior. Los clientes con compliance estricto que
> necesiten esto antes pueden coordinarlo con soporte.

## Gestión de llave

* `ENCRYPTION_KEY` vive en SSM Parameter Store como `SecureString`
  bajo `/shared/prod/secrets/ENCRYPTION_KEY` y se inyecta al
  container al arrancar (ver [Gestión de secrets](/seguridad/secrets-management)).
* Rotación de la llave es manual hoy. Implica:
  1. Generar la llave nueva.
  2. Re-cifrar todos los registros (script de migración) con la nueva
     llave antes de switchear `ENCRYPTION_KEY` en SSM, en una ventana
     de mantenimiento.
  3. Descartar la vieja.
* No hay HSM en V1; la llave vive en variables de entorno del proceso
  (en memoria) y nunca se loggea.

## Garantías

* **Cifrado autenticado**: si un atacante modifica el ciphertext en
  la base, el `authTag` no valida y el descifrado lanza error → el
  registro alterado no se devuelve como si fuera válido.
* **Firma constant-time**: `verifySignature` usa comparación
  resistente a timing attacks.
* **Domain separation**: el scope en HMAC evita reutilización de
  firmas entre tipos de documentos.
* **JSON determinístico**: la canonicalización elimina ambigüedad
  por orden de llaves.

## Limitaciones conocidas

* La rotación de `ENCRYPTION_KEY` requiere ventana de mantenimiento
  (no hay key versioning aún).
* La firma de `form-submission` / `form-response` no está cableada
  automáticamente; llega en el release de Stage 10.
* La aplicación a credenciales de integraciones de terceros depende
  de que esas integraciones se construyan (Stage 6).
