Revocar es imposible: el problema abierto que cierra el nivel
Impedir el acceso futuro se consigue rotando claves; recuperar lo que alguien ya descifró no se consigue nunca, y saber decir esa diferencia en la interfaz separa un producto honesto de una promesa incumplible.
La primera lección de este nivel dejó anunciada una asimetría y prometió que la última se llamaría problema abierto por su culpa. El control de escritura sobrevive al cifrado sin dificultad, porque se comprueba con firmas y claves públicas que cualquiera puede validar. El control de lectura no sobrevive: en cuanto un sobre llega a un dispositivo y ese dispositivo tiene la clave, no existe política, protocolo ni actualización de software que impida abrirlo. Todo lo montado en las cuatro lecciones anteriores —épocas, material fresco, árboles de claves, paquetes publicados por adelantado— sirve para acotar el futuro con una precisión notable, y no toca en absoluto lo ya ocurrido. Esta lección hace tres cosas con esa frontera. Separa con nombre propio las tres operaciones distintas que en los productos se llaman todas revocar. Muestra que ni siquiera la parte alcanzable ocurre en un instante, porque sin árbitro la revocación es una operación concurrente más. Y termina en el sitio incómodo al que llevaba el nivel entero: hay productos para los que esta imposibilidad, y no el rendimiento ni el coste de ingeniería, es la razón definitiva de no replicar.
- Separar la revocación de escritura, la de lectura futura y la de lectura pasada, y saber cuál de las tres es alcanzable.
- Entender por qué la revocación de escritura es el permiso más fuerte de un sistema cifrado y por qué casi nunca se implementa.
- Reconocer que sin orden total la expulsión no tiene un instante, sino una ventana con operaciones legítimamente concurrentes.
- Redactar el texto de una interfaz de revocación que describa lo que el sistema hace y no lo que el usuario espera.
- Usar esta imposibilidad como criterio de descarte para decidir qué colecciones no deben replicarse nunca.
Tres operaciones distintas que se llaman todas revocar
El vocabulario es el primer problema, porque un único botón esconde tres cosas con propiedades opuestas. La primera es quitar la escritura: que lo que esa persona firme a partir de ahora sea rechazado por todas las réplicas honestas. Es la más fuerte de las tres y la única que se aplica sin servidor, porque cada par puede comprobar por su cuenta que una operación viene firmada por alguien que ya no figura en el grafo de acceso, y descartarla sin preguntarle a nadie. No hace falta ver el contenido para eso, así que el cifrado no la estorba.
La segunda es quitar la lectura futura: abrir una época nueva con material fresco, tal como se describió en la lección anterior, de modo que lo que se escriba a partir de ese salto quede fuera del alcance de quien salió. Es alcanzable y su coste está bien entendido, pero es condicional en un sentido que conviene no perder de vista: solo surte efecto cuando la rotación llega a los miembros que quedan y estos empiezan efectivamente a cifrar con la clave nueva. Hasta entonces, un rezagado bienintencionado sigue cifrando con la vieja y regalando lectura al expulsado sin enterarse.
La tercera es quitar la lectura pasada, y no existe. No es difícil, no es cara y no está pendiente de una biblioteca mejor: no existe. Los bytes se descifraron en un dispositivo ajeno, el texto claro pasó por su memoria, pudo copiarse, imprimirse, fotografiarse o simplemente recordarse. Recifrar el histórico no la recupera, porque el adversario de una revocación no es alguien que se llevó texto cifrado esperando obtener la clave algún día, sino alguien que tuvo la clave legítimamente y ya la usó.
flowchart TB R[el boton de revocar] --> W[rechazar lo que firme desde ahora] R --> F[abrir epoca nueva con material fresco] R --> P[lo que ya descifro en su dispositivo] W --> OK[alcanzable y comprobable por cada par] F --> CO[alcanzable en cuanto la rotacion se propaga] P --> NO[no alcanzable por ningun medio] style OK fill:#a6e3a1,color:#11111b style CO fill:#f9e2af,color:#11111b style NO fill:#f38ba8,color:#11111b
Hay una cuarta operación que nadie nombra y que decide cómo se puede hablar del asunto: demostrar que la revocación surtió efecto. En una arquitectura con servidor es trivial, porque el punto que niega el acceso es el mismo que registra la negativa, y esa traza se puede enseñar a un auditor. Aquí no hay tal punto. Se puede demostrar que se emitió la revocación, se puede demostrar que la época cambió y se puede demostrar qué se entregó antes; lo que ocurra dentro de un aparato que no controlas es indemostrable por construcción, y esa indemostrabilidad es un hecho jurídico además de técnico.
| Operación | Se puede | Cuándo surte efecto | Se puede demostrar |
|---|---|---|---|
| Rechazar sus escrituras | Sí, en cada par | Cuando cada réplica conoce el cambio | Sí, la firma se valida o no |
| Cerrarle la lectura futura | Sí, con material fresco | Cuando la rotación llega a los que quedan | Parcialmente, se ve la época |
| Cerrarle la lectura pasada | No | Nunca | No aplica |
| Comprobar su dispositivo | No | Nunca | No, no controlas ese aparato |
Conviene además separar dos casos que el vocabulario mezcla y que el modelo de la tercera lección permite distinguir con limpieza. Si la persona es un grupo cuyos miembros son sus dispositivos, expulsar a una persona y dar de baja un aparato son operaciones distintas con propiedades distintas. Dar de baja un teléfono robado puede ser bastante eficaz, porque ese aparato quizá solo había descifrado lo que cabía en su almacenamiento parcial y quizá ni siquiera había recibido la mitad del corpus; el daño está acotado por lo que ese dispositivo concreto llegó a bajar, y esa cifra se conoce. Expulsar a una persona es la operación mala, porque su daño es la unión de lo que descifraron todos sus aparatos durante todo el tiempo que fue miembro. Merece la pena medir las dos por separado en cualquier producto, porque una es un incidente acotado y la otra es una pérdida permanente.
// La unica revocacion que cada par puede hacer cumplir por su cuenta
function aceptarOperacion(op, grafoDeAcceso) {
if (!verificarFirma(op.autor, op.carga, op.firma)) return false;
const permiso = grafoDeAcceso.vigenteEn(op.autor, op.padres);
if (!permiso || permiso.revocadoAntesDe(op.padres)) return false;
return true; // no hizo falta servidor ni leer el contenido
}
Es un patrón reconocible en los equipos que llegan desde una arquitectura centralizada. Se implementa revocar como dejar de enviar: el servidor borra al usuario de la lista de destinatarios y la prueba de aceptación comprueba que ya no le llegan cambios. Con eso queda cubierta la mitad visible del problema y sin cubrir la mitad que importa, porque nadie ha tocado el grafo de acceso que los pares consultan al validar firmas. El expulsado conserva su clave de firma, y un cliente modificado —o el oficial de hace tres versiones— puede seguir inyectando operaciones que las demás réplicas aceptarán como legítimas, dado que la firma verifica y nadie les ha dicho lo contrario. Quitar la escritura es lo único que un sistema cifrado puede hacer cumplir de verdad y sin servidor. Merece implementarse primero, no último.
La expulsión tampoco tiene un instante
La lección anterior estableció que un estándar como el del IETF para mensajería en grupo, publicado como RFC 9420 y con implementaciones abiertas entre las que está OpenMLS, se apoya en una sucesión lineal de épocas, y que esa linealidad la produce un servicio de entrega que serializa las confirmaciones. Conviene ahora aplicar esa conclusión al caso concreto de la expulsión, porque es donde más duele. Con un serializador, quitar a alguien es un suceso puntual: la confirmación gana o pierde, hay una época anterior y una posterior, y todo el mundo acaba de acuerdo sobre cuál es cuál. Sin serializador no hay tal punto, y no por un fallo de implementación sino porque el orden causal deja sin ordenar precisamente lo que se hizo sin verse.
El caso mínimo se describe en dos líneas. Ana expulsa a Bruno el martes. Bruno, que lleva desde el lunes en un tren sin cobertura, escribe el martes por la tarde y sincroniza el miércoles. Sus operaciones son causalmente concurrentes con su propia expulsión: van firmadas bajo una época en la que era miembro de pleno derecho, y no existe ninguna verdad física sobre si ocurrieron antes o después. El sistema tiene que elegir, y las dos opciones son malas de formas distintas. Si las acepta, hay escrituras de un expulsado incorporadas después de la expulsión, que es exactamente lo que la operación pretendía impedir. Si las descarta, un miembro legítimo pierde una tarde de trabajo por un motivo que ocurrió mientras él no podía enterarse.
// Sin orden total, la expulsion y lo expulsado pueden ser concurrentes
const grafo = [
{ id: 'e1', padres: ['e0'], autor: 'ana', op: 'quitar a bruno' },
{ id: 'b1', padres: ['e0'], autor: 'bruno', op: 'editar seccion tres' }
];
// b1 no desciende de e1: bruno no habia visto su expulsion al escribir.
// Aceptar b1 y descartar b1 son ambas politicas defendibles, y hay que elegir una por escrito.
Ese es el nudo del que parte el trabajo de investigación de esta área. El proyecto de control de acceso local-first de Ink and Switch, llamado Keyhive, arranca de la premisa de que en un contexto local-first no cabe dejar el control de acceso en manos de un proceso central. La consecuencia directa de esa premisa es la que acabamos de ver: si la autorización deja de vivir en un proceso central, sus operaciones viven en el mismo grafo causal que todo lo demás y heredan su concurrencia, de modo que expulsar deja de ser un cambio de estado y pasa a ser una propuesta que otros verán tarde. Nada de esto es un defecto del planteamiento; es lo que significa quitar el árbitro, aplicado al permiso en lugar de al dato.
Hay además un agravante que enlaza directamente con el parámetro que cerraba la lección anterior. Tolerar particiones largas exigía que cada dispositivo conservase más de un secreto individual, para poder leer lo que se cifró en la rama del otro durante la separación. Ese material retenido es exactamente el que una expulsión querría inutilizar, así que cuanta más concurrencia tolera el sistema, más ancha es la ventana de lectura que el expulsado se lleva puesta. Secreto hacia atrás, tolerancia a la desconexión y eficacia de la revocación son, en este terreno, tres nombres del mismo presupuesto.
Y queda un caso peor que el del tren, que aparece en cuanto hay más de un administrador y que en un sistema con árbitro ni siquiera se puede plantear: la revocación mutua. Ana expulsa a Bruno mientras Bruno, concurrentemente, expulsa a Ana. Ninguno de los dos vio la operación del otro, las dos van firmadas por alguien que en su propia vista era administrador de pleno derecho, y el grafo causal no las ordena. Un serializador zanjaría el asunto declarando ganadora a la primera que llegó, y esa arbitrariedad sería perfectamente aceptable porque al menos todos coincidirían en ella. Sin serializador hacen falta reglas escritas de antemano: que la delegación más antigua prevalezca, que las expulsiones se acumulen en lugar de anularse y salgan ambos, o que ciertos permisos sean irrevocables por diseño precisamente para que este caso no pueda producirse. Cualquiera de las tres es defendible; ninguna es evidente, y la que no se escribe la acaba fijando el orden en que un bucle recorre una lista.
Existe una salida aparente a todo esto y merece nombrarla para descartarla con conocimiento de causa: hacer la revocación en dos fases, de modo que no se considere completa hasta que todos los miembros restantes hayan acusado recibo del cambio de época. Con eso se recupera un instante bien definido, la ventana de escrituras concurrentes se cierra y la promesa vuelve a ser limpia. El precio es exactamente el que este track lleva veinte niveles evitando: esperar a que todos contesten es coordinación, y un sistema que necesita coordinación para expulsar a alguien deja de funcionar en el momento en que un portátil lleva tres semanas apagado, que es el caso que motivaba la arquitectura entera. La elección honesta consiste en admitir que se está eligiendo entre una revocación determinista con disponibilidad frágil y una revocación eventual con disponibilidad robusta, y en escribir cuál de las dos se ha comprado.
Conviene enunciarlo sin adornos porque cambia el texto de cualquier promesa. La expulsión es un dato más que hay que propagar, y se propaga por la misma red poco fiable y asíncrona que el resto. Una réplica que aún no la ha recibido seguirá aceptando escrituras del expulsado y seguirá cifrándole contenido nuevo, y hará las dos cosas de buena fe y sin ningún fallo. Es decir: hasta la parte alcanzable de la revocación tiene consistencia eventual, y su tiempo de convergencia no lo fija el sistema sino el dispositivo más rezagado de la flota. Un producto que anuncia el acceso retirado de forma inmediata está describiendo el estado de su base de datos, no el de sus usuarios.
Escribirlo en la interfaz sin mentir
La honestidad de este tipo de sistema no se decide en la capa criptográfica sino en cuatro cadenas de texto, y conviene tratarlas como parte del diseño de seguridad. La primera regla es nombrar la operación por lo que hace. Revocar acceso afirma algo que el sistema no puede cumplir; dejar de compartir, retirar de entregas futuras o expulsar del espacio describen exactamente lo ocurrido y no sugieren ningún efecto retroactivo. La segunda es mostrar el corte: qué se lleva la otra parte, con fecha y con volumen, porque un usuario que ve la cifra entiende la situación mucho mejor que uno que lee un párrafo explicativo.
La tercera regla es negativa y cuesta defenderla en una reunión de producto: no ofrecer botones que finjan. Eliminar de sus dispositivos, revocar copias descargadas o caducar el contenido son funciones que solo funcionan si el software del otro extremo colabora, lo cual las convierte en cortesías y no en controles. Ofrecerlas produce un daño concreto y medible, que es que alguien tomará una decisión de negocio creyendo que el contenido ha desaparecido. La cuarta es registrar y conservar lo único demostrable: el instante de la revocación y el inventario de lo entregado antes de ella. Ese registro no impide nada y es exactamente el artefacto que las capas operativa y contractual necesitan el día que algo sale mal.
Hay una tentación intermedia que conviene desactivar porque suena razonable y no lo es: pedir cortésmente al cliente del otro extremo que borre su copia y, si contesta que lo ha hecho, marcar la revocación como completada en la interfaz. El mecanismo funciona el noventa y nueve por ciento de las veces, porque casi nadie modifica su cliente, y ese porcentaje es justamente lo que lo hace peligroso. Un control que falla solo ante quien se molesta en atacarlo no es un control, es una estadística sobre usuarios honrados, y presentarlo como completado convierte una funcionalidad benigna en una afirmación falsa. Si se implementa el borrado cooperativo —y hay buenos motivos de higiene para hacerlo— el texto correcto no es copia eliminada sino se ha solicitado la eliminación y el dispositivo ha respondido, que es exactamente lo que ocurrió.
// Las cadenas honestas describen el mecanismo, no el deseo del usuario
const textos = {
accion: 'Dejar de compartir con Bruno',
confirmacion: 'Bruno dejara de recibir cambios nuevos en cuanto su dispositivo se conecte.',
retencion: 'Conserva copia de las 412 notas descargadas hasta el 6 de agosto.',
imposible: null // no existe boton para borrar lo que ya esta en su disco
};
Nombrar el mecanismo
Dejar de compartir describe lo que ocurre. Revocar acceso describe lo que el usuario querría que ocurriese.
Enseñar el corte
Cuántos documentos y hasta qué fecha se lleva la otra parte. Una cifra concreta comunica mejor que un aviso legal.
Avisar de la latencia
El efecto llega cuando su dispositivo se conecta, no al pulsar. Decirlo evita la falsa sensación de corte inmediato.
Registrar lo demostrable
Instante de la revocación e inventario de lo entregado antes. Es lo único que se puede enseñar a un auditor.
Coge el texto de confirmación de tu diálogo de revocación y léeselo a alguien ajeno al equipo. Después pregúntale dos cosas: qué cree que puede seguir viendo la persona revocada y cuándo cree que ha dejado de ver lo demás. Si su respuesta a la primera es nada y tu sistema le deja una copia completa de lo anterior, el texto está mintiendo aunque cada palabra sea técnicamente cierta. Si su respuesta a la segunda es ahora mismo y tu efecto real depende de que un portátil apagado se encienda, ocurre lo mismo. La distancia entre esas dos respuestas y el comportamiento real de tu sistema es la deuda de honestidad de tu producto, y se paga entera el día que un cliente la descubra solo.
Cuándo esta imposibilidad es motivo suficiente para no replicar
El nivel 5 estableció el criterio y lo dejó formulado como una pregunta observable antes de escribir código: con qué frecuencia cambia la audiencia de cada colección. Con el aparato criptográfico ya montado se puede afinar ese criterio, porque ahora se sabe exactamente qué se compra con la rotación y qué no. Hay tres perfiles en los que la respuesta correcta es no replicar, y ninguno de los tres se arregla con mejor ingeniería.
El primero es el de las obligaciones de auditoría de lectura. Donde la norma exige registrar quién consultó qué y cuándo, la propiedad que da valor a este enfoque —leer del disco propio sin preguntarle a nadie— es exactamente la que impide cumplirla, y no hay versión parcial que sirva. El segundo es el de los datos cuyo valor no decae: un contrato, una lista de clientes, una tabla salarial o un expediente siguen valiendo lo mismo cinco años después de congelarse, así que la fotografía que se lleva el expulsado no pierde peligro con el tiempo. El tercero es el de las audiencias que cambian varias veces al año por razones organizativas, donde cada rotación de personal convierte la comodidad de tener el dato cerca en una fuga permanente más.
Los tres perfiles comparten una manera de detectarse antes de escribir una línea, y consiste en construir una magnitud que casi nadie calcula: la exposición residual. Para cada colección, multiplica el volumen que un miembro típico llega a descifrar por el número de bajas al año y por cuántos años sigue valiendo ese contenido. El resultado no tiene unidades interpretables y no importa, porque su utilidad es comparativa: ordena las colecciones y separa con bastante claridad las que se pueden replicar sin pensarlo de las que van a producir un incidente. Una colección con audiencia estable y contenido que caduca en semanas puntúa cerca de cero por muchos gigabytes que ocupe; una tabla salarial en una empresa con rotación puntúa altísimo aunque quepa en un fichero de texto. El tamaño, que es la variable en la que todo el mundo se fija primero, resulta ser la menos determinante de las tres.
Vale la pena mirar hacia atrás el arco completo de este nivel antes de cerrarlo, porque las cinco lecciones cuentan una sola historia y su moraleja no es la que se esperaba al empezar. Cifrar parecía una capa y resultó ser una redistribución del trabajo. Ocultar el contenido parecía ocultar la información y resultó dejar la silueta entera a la vista. Repartir claves parecía eliminar al tercero de confianza y resultó empujarlo hasta el acto de nombrar. Rotar claves parecía dar seguridad limpia y resultó cobrarse el secreto hacia atrás en cuanto hay concurrencia real. Y quitar el acceso, que es la operación que cualquier responsable daría por descontada, resulta que solo existe hacia adelante. Ninguna de las cinco es un fallo de las herramientas actuales; las cinco son consecuencias de la topología elegida, y el trabajo del que diseña no consiste en negarlas sino en decidir, colección a colección, si el producto puede vivir con ellas.
Y conviene cerrar con la inversión del signo, porque es lo que impide leer todo esto como una condena. Existe una familia entera de productos para los que la imposibilidad de revocar no es un defecto sino la característica principal. Un cuaderno personal, un archivo de investigación, un documento del que alguien es coautor, una copia de sus propios datos: en todos ellos el usuario quiere precisamente que nadie pueda quitárselos después, y ese es el argumento de longevidad que abrió el track. El mecanismo es el mismo, la propiedad física es la misma, y solo cambia quién ocupa el lado incómodo. Un producto que replica está eligiendo, para cada colección, si prefiere poder retirar o poder garantizar, y no hay ninguna combinación de algoritmos que le permita elegir las dos.
Los motivos habituales para descartar este enfoque —que la ingeniería es cara, que el equipo no tiene experiencia, que el conjunto de trabajo no cabe— comparten una propiedad tranquilizadora: todos se pueden pagar. Con presupuesto, tiempo y gente se resuelven, y por eso son argumentos de planificación y no de arquitectura. La revocación no es de esa clase. Si el producto que se está construyendo tiene como promesa central que la organización puede quitarle el acceso a alguien y demostrarlo, entonces no existe cantidad de dinero, de talento ni de criptografía que lo consiga sobre un dato que ya viajó a un dispositivo ajeno, y el proyecto no está eligiendo una técnica difícil sino prometiendo algo falso. Ese es el punto en el que la conversación deja de ser técnica: no se decide si se puede construir, se decide si la promesa que se vende es compatible con la topología que se elige. Y como el nivel 5 dejó claro que la unidad de decisión no es la aplicación sino la colección, la salida casi nunca es renunciar a todo, sino dejar en el servidor exactamente las colecciones cuya promesa incluya la palabra retirar.
- Localiza en tu código la función que se llama revocar y comprueba si toca el grafo de acceso o solo la lista de destinatarios.
- Implementa el rechazo de firmas de un expulsado en el propio par y verifica que funciona con el servidor apagado.
- Provoca el caso de Bruno en el tren: escritura concurrente con la expulsión, y decide por escrito si la aceptas o la descartas.
- Mide cuánto tarda una revocación en llegar al último dispositivo de tu flota real y compáralo con lo que promete tu interfaz.
- Calcula qué se lleva exactamente un expulsado hoy, en número de documentos y en fecha de corte, y enséñalo en el diálogo.
- Recorre tus colecciones y marca las tres que descartarías replicar por auditoría de lectura, por valor persistente o por audiencia inestable.