Establecimiento e inicialización de circuito
La cadena de OR´s que
forman un circuito es construida de forma telescópica, esto es, el
OP negocia las claves de sesión con cada sucesivo OR. Una ventaja de
esta forma de operar es que se dejen una vez que éstas claves de
sesión se dejen de utilizar y sean borradas, aunque un nodo sea
comprometido no se podrá descifrar células viejas (perfect forward
secrecy).
El esquema se realiza con siguientes pasos:
- El OP escoge un OR como exit node (ORn), de acuerdo con su exit policy.
- EL OP escoge una cadena de OR's para constituir el path del circuito (OR1....ORn) de forma que ningún OR aparece dos veces en el path.
- El OP abre una conexión TLS con el entry-node (OR1 ) si es que no la tenía abierta
- El OP escoge aleatoriamente un ID de circuito (circID) que no se esté utilizando en la conexión con el entry-node
- El OP inicia un intercambio de clave Diffie-Hellman con el entry-node envíando una celda CREATE
- El entry-node le devuelve una celúla CREATED y de esta forma se obtiene una clave compartida Diffie-Hellman (D-H). De esta clave se derivan dos claves simétricas, una para cada sentido:
- kf1 (forward key). Se usará en la comunicación OP->OR1 del circuito.
- kb1 (backward key). Se usará en la comunicación OR1->OP.
- A continuación el OP envía una petición a el OR1 para extender el circuito mediante una célula RELAY_EXTEND. Esta célula le indica al OR1 cual va a se el nuevo nodo OR2, su puerto y también contiene estructuras de datos que le tendrá que reenviar el OR1 al OR2 y que permitirán establecer un protocolo D-H de intercambio de claves entre el OP y OR2 a través de OR1 sin que el OR1 se entere de nada
- El OR1 coge la célula y realiza el procedimiento Diffie-Hellman escogiendo él mismo un nuevo circID apropiado. Observar que ahora la células CREATE/CREATED se intercambian entre el OR1 y el OR2.
- El OR1 manda al OP una célula RELAY_EXTENDED para mandarle lo que ha respondido el OR2 y así el OP tiene conocimiento de la clave D-H.La información está cifrada de tal forma que el nodo OR1 no puede acceder la información que se están intercambiando indirectamente el OP y OR2. De esta forma se establecen las nuevas claves simétricas: kf2 y kb2.
- De forma similar se continúa hasta conseguir que el OP tenga establecidas las claves kf1,kb1,kf2,kb2,...,kfn,kbn con los respectivos nodos OR.
En cada establecimiento
de clave Diffie-Hellman se obtiene un valor g^xy que es usado para
calcular un valor K obtenido con la siguiente fórmula:
K=H(
g^xy | [00] ) | H( g^xy | [01] )....| H( g^xy | [04] )
Donde:
- | es la concatenación
- [NN] es un byte cuyo valor es NN
- H(x) es el SHA-1 de x
A partir de K se sacan
una serie de valores derivados que se van usando en el proceso:
- derivative key (KH): Primer HASH_LEN bytes. Usado en la respuesta del handshare para demostrar el conocimiento de la clave compartida.
- digest forward (Df): Segundo HASH_LEN bytes. Es usado como semilla de la comprobación de integridad para flujos de datos desde OP a OR.
- backguard digest (Db):Tercer HASH_LEN bytes. Es usado como semilla de la comprobación de integridad para flujos de datos desde OR a OP.
- forward key (Kf): Es usado para encriptar los flujos de datos desde OP a OR.
- backguard key (Kb): Es usado para encriptar los flujos de datos desde OR a OP.
Célula CREATE
Sobre el formato de la
célula CREATE cabe señalar que el cifrado de g^x se hace de forma
híbrida (con RSA-OAEP se cifra una clave de sesión y parte de g^x,
con la clave de sesión se cifra el resto de g^x) para permitir que
en una sola célula podamos hacer el intercambio del g^x completo.
Cuando inicializamos el
primer salto de un circuito, el OP puede tener ya establecida la
identidad del OR y negociado una clave secreta usando TLS. Por tanto
no siempre es necesario para el OP realizar operaciones de clave
pública para crear el circuito. En este caso el OP puede enviar una
célula CREATE_FAST en lugar de una célula CREATE sólo para el
primer salto. El OR responde con un CREATED_FAST, cuando el circuito
está creado. La célula CREATE_FAST contiene una clave X generada
aleatoriamente.
La célula CREATED_FAST
contiene una clave Y generada aleatoriamente y un hash de la clave
derivada construida a partir de X e Y. La clave compartida entre el
OP y el OR será la concatenación de X e Y.
Por tanto cuando
detectamos un CREATE_FAST podemos estar seguros de que el nodo OR es
el primero del circuito. Por tanto los ORs deberían rechazar
intentos de crear streams con RELAY_BEGIN (que sirven para indicar a
los nodos que son exit node y por tanto tienen que enviar el
contenido a cierta IP) saliendo del circuito en el primer OR. De esta
forma se evitan cierto tipo de ataques.
Célula CREATED
El valor resumen de la
clave establecida se manda para poder hacer una verificación de que
ambos extremos de la comunicación comparten la misma clave.Célula RELAY_EXTEND
Son de especial interés
los siguientes campos:
- La dirección (address) IPv4 y el puerto (port) del próximo OR del circuito.
- El campo streamID vale 0 porque este tipo de células relay sirven para control.
- El campo SHA-1(PCKS#1(PKOR1_ID)) es el hash SHA-1 del PKCS#1 con codificación ASN.1 de la clave de identidad (PKOR_ID) del próximo OR. Esto permite prevenir cierto tipo de ataques man-in-the-middle.
Observar que cuando el
OR1 descifra con su clave AES obtiene distintos campos y entre ellos
obtiene la siguiente piel de cebolla que puede utilizar directamente
en la célula CREATE que tiene que construir para enviársela al OR2.
Célula RELAY_EXTENDED
Observar que consiste en
la comunicación de la respuesta al protocolo Diffie-Hellman que hace
el OR2 al OP usando al OR1 como intermediario sin que éste pueda
inferir información alguna. El campo streamID vale 0 porque este
tipo de células relay sirven para control.
Encapsulamiento de streams TCP
Una vez que un circuito
se ha establecido se procede con el encapsulamiento de los streams
TCP que contienen los datos de la aplicación. Un circuito ya
establecido se puede aprovechar para multiplexar en él varios
streams TCP provenientes de la misma o de distintas aplicaciones. De
hecho, por eficiencia, Tor usa el mismo circuito para nuevos streams
TCP durante diez minutos siempre que sea posible por la exit policy
del exit node.
Una vez pasados los diez
minutos las peticiones será dadas a un nuevo circuito. Esto mejora
la protección y evita que un atacante vincule tráficos antiguos con
los nuevos. El circuito permanecerá abierto hasta que todos los TCP
streams que multiplexe hayan cerrado. Esta característica es
necesaria para soportar muchos protocolos como SSH, los cuales son
incapaces de mantener una sesión si se cambian los circuitos (y por
tanto el exit node) durante la sesión. Los pasos para encapsular el
tráfico TCP es el siguiente:
- El OP escoge un circuito abierto que tenga un exit-node que le permita conectarse de la formada adecuada con el servidor. Si no existe lo crea según el algoritmo visto anteriormente.
- El OP selecciona un StreamID arbitrario que no haya sido utilizado todavía en el circuito.
- El OP construye una célula RELAY_BEGIN que contiene, cifrado adecuadamente para que sólo sea visto por el exit-node, la dirección y el puerto destino de la comunicación.
- Esta célula es procesada por los nodos intermedios y el contenido útil es propagada hasta llegar en una célula RELAY_BEGIN que llega al exit-node.
- El exit-node obtiene la dirección y el puerto destino e intenta abrir una nueva conexión TCP con el host destino. Pueden suceder dos cosas.
- Si el exit-node no puede establecer la conexión devuelve una célula RELAY_END.
- Si el exit-node sí se puede estalbecer la conexión entonces el exit-node devuelve una célula RELAY_CONNECTED que se irá propagando por la lista de OR´s hasta llegar al OP.Una vez llega la célula RELAY_CONNECTED a el OP, el OP y el exit-node empaquetan el stream de datos TCP en células RELAY_DATA.
Si un OP quiere cerrar un
stream TCP anonimizado envía una célula RELAY_END la cual se
propaga a lo largo del circuito y los nodos van cerrando ese stream
para su circuito. Cuando llega al exit-node éste cierre la conexión
TCP. El exit-node genera una célula RELAY_ENDED que se propaga hasta
el OP.
Si el stream TCP que
mantiene el exit-node con el destino se cierra de forma abrupta
entonces el exit-node envía a lo largo del circuito hasta el OP una
célula RELAY_TEARDOWN que indica el cierre inesperado de la
conexión. Por el camino se van cerrando los streams de circuito
asociados a stream TCP.
Una vez que un OR recibe
una célula RELAY, éste chequea el circID de la célula y determina
si tiene un circuito que se corresponda con esta a lo largo de la
conexión. Si no lo tiene descarta la célula. Por otra parte si el
OR no es exit-node, entonces cifra/descifra el carga útil del stream
usando la claves kb/kf correspondiente con el OP. El OR entonces
decide si reconoce la célula está completamente descifrada (ya
visto más arriba como se decide). Si el OR reconoce la célula, la
procesa. En otro caso la pasa a lo largo del circuito. Si el
exit-node no reconoce una célula RELAY entonces ocurre un error y el
OR envía una célula DESTROY para cerrar el circuito.
Cuando una célula RELAY
llega al OP, el OP descifra la carga útil con kbi. Si la carga útil
está completamente descifrada (ya visto más arriba como se decide)
entonces procesa la carga útil.
Cierre y modificación de un circuito
Un circuito es cerrado
cuando ocurre un error irrecuperable a lo largo del circuito o cuando
l OP ve que todos los streams de un circuito están cerrados y el
tiempo de vida del circuito ha terminado. Para cerrar un circutio el
OR o el OP envían una célula DESTROY que se va propagando y
cerrando los circID asociados al circuito.
Una vez que se ha
establecido un circuito y se está utilizando el OP puede decidir
truncarlo (por ejemplo porque se ha caído) y crear uno nuevo a
partir de donde trunque. Se procede con los siguientes pasos:
- El OP escoge el OR donde se va a truncar y el OR nuevo que se quiere usar en el path.
- El OP construye una célula RELAY_TRUNCATE que llega hasta el OR a partir del cual se va a truncar.
- El OR desde el que se trunca comienza un proceso de cierre de circuito enviando una célula DESTROY.
- El OR desde el que se trunca lanza una célula RELAY_TRUNCATED hacia el OP.
- El OP cuando le llega el RELAY_TRUNCATED envía un RELAY_EXTEND para construir el nuevo circuito.
Cuando un error
irrecuperable ocurre a lo largo de la conexión de un circuito, el OR
debería, si está habilitado para ello, enviar al OP una célula
RELAY_TRUNCATED; el nodo más lejano basta con enviar una célula
DESTROY para cerrar el circuito.
También se cierra el
circuito por parte de los ORs cuando ha habido algún error
irrecuperable a lo largo del circuito.
La carga útil de las
células DESTROY o RELAY_TRUNCATED contienen sólo un octeto que
indican el motivo por el que el circuito debería ser cerrado o
truncado. Este motivo es propagado a lo largo de los nodos.












0 comentarios:
Publicar un comentario