# Fase 2 — Cifrado nuevo seguro para `?c=`

## Objetivo
Crear `core/crypto.php` con un cifrado moderno (AES-256-GCM, autenticado) para encriptar/desencriptar el `id_colegio` que viaja en `?c=`. **No** reusar las funciones `encriptar()/desencriptar()` heredadas.

## Por qué cifrado autenticado
El `?c=` lo genera el portal origen y lo recibe `micolegio.jesistemas.com.ve`. Necesitas:
- **Confidencialidad**: no revelar el `id_colegio`.
- **Integridad/autenticidad**: que nadie altere el valor para apuntar a otro colegio. GCM (o sodium secretbox) detecta manipulación; un cifrado simple no.

## Tareas

1. Crear `core/crypto.php` con:
   - `cifrar_tenant(int $idColegio): string` → token URL-safe (base64url de IV+tag+ciphertext).
   - `descifrar_tenant(string $token): ?int` → `id_colegio` o `null` si inválido/manipulado.
2. La clave NO va en el código. Leerla de variable de entorno `TENANT_KEY` (32 bytes en base64) o de un archivo fuera del webroot (`/etc/plataforma/tenant.key`). Documentar cómo generarla.
3. Incluir un **TTL opcional**: empacar timestamp en el payload y rechazar tokens viejos (p. ej. > 5 min) para evitar reuso de enlaces filtrados.
4. Entregar también un mini-script CLI `gen-token.php <id_colegio>` para que el portal origen (o pruebas) genere tokens.

## Esqueleto sugerido (openssl, disponible en todo PHP)

```php
<?php
// core/crypto.php
function _tenant_key(): string {
    $b64 = getenv('TENANT_KEY') ?: @file_get_contents('/etc/plataforma/tenant.key');
    if (!$b64) throw new RuntimeException('TENANT_KEY no configurada');
    $k = base64_decode(trim($b64), true);
    if ($k === false || strlen($k) !== 32) throw new RuntimeException('Clave inválida (32 bytes)');
    return $k;
}

function cifrar_tenant(int $idColegio): string {
    $key = _tenant_key();
    $payload = json_encode(['id' => $idColegio, 'ts' => time()]);
    $iv  = random_bytes(12);
    $tag = '';
    $ct  = openssl_encrypt($payload, 'aes-256-gcm', $key, OPENSSL_RAW_DATA, $iv, $tag);
    return rtrim(strtr(base64_encode($iv.$tag.$ct), '+/', '-_'), '=');
}

function descifrar_tenant(string $token, int $ttl = 300): ?int {
    try {
        $raw = base64_decode(strtr($token, '-_', '+/'), true);
        if ($raw === false || strlen($raw) < 28) return null;
        $iv  = substr($raw, 0, 12);
        $tag = substr($raw, 12, 16);
        $ct  = substr($raw, 28);
        $pt  = openssl_decrypt($ct, 'aes-256-gcm', _tenant_key(), OPENSSL_RAW_DATA, $iv, $tag);
        if ($pt === false) return null;            // manipulado o clave incorrecta
        $d = json_decode($pt, true);
        if (!isset($d['id'], $d['ts'])) return null;
        if ($ttl > 0 && (time() - (int)$d['ts']) > $ttl) return null; // expirado
        return (int)$d['id'];
    } catch (Throwable $e) {
        return null;
    }
}
```

## Generar la clave (una sola vez)
```bash
php -r "echo base64_encode(random_bytes(32)).PHP_EOL;" | sudo tee /etc/plataforma/tenant.key
# o exportarla: export TENANT_KEY="...."
```

> El mismo `tenant.key` debe estar disponible en el portal ORIGEN (quien genera el `?c=`) y en `micolegio` (quien lo lee). Misma clave, ambos lados.

## Decisión TTL
Si el `?c=` puede llegar de enlaces guardados/marcadores, un TTL corto los romperá. Como tras resolver guardas `id_colegio` en sesión, el TTL solo afecta la primera entrada → un TTL de 5 min es razonable. Confirmar con el flujo del portal origen.

## Criterio de aceptación
- `cifrar_tenant(7)` → token; `descifrar_tenant(token)` === 7.
- Alterar un carácter del token → `descifrar_tenant` devuelve `null`.
- Token con `ts` viejo → `null`.
- La clave no aparece en ningún archivo versionado.
