jueves, 19 de diciembre de 2013

Entendiendo el protocolo de WhatsApp: FunXMPP

Han sido muchas personas las que después de dar la charla "Defeating WhatsApp's Lack of Privacy" nos han enviado correos y mensajes por preguntando más información sobre el funcionamiento del protocolo y una explicación algo más extensa de los problemas del cifrado en WhatsApp.

Empezaremos con lo básico.  Como todos sabéis, WhatsApp es una aplicación de mensajería multiplataforma que permite enviar y recibir mensajes a través Internet de manera gratuita.

Ha sustituido a los servicios tradicionales de mensajes cortos o sistema de mensajería multimedia y está disponible para los sistemas operativos iOS, Android, Windows Phone, BlackBerry OS y Symbian.

WhatsApp utiliza una versión propia/modificada de XMPP llamada FunXMPP. Sin entrar en más detalles técnicos, es un protocolo de mensajería que utiliza como sintaxis el lenguaje XML. Los RFCs que definen el protocolo son los siguientes:
          • RFC 3920, especifica las características principales del protocolo XMPP
          • RFC 3921, define los servicios de mensajería instantánea y de presencia previstos en XMPP. 
          RFC 3922, que define una transposición de XMPP a CPIM (RFC 3860, Common 
Profile for Instant Messaging); 
          RFC 3923, que define un mecanismo extremo a extremo de firma y cifrado de los objetos. 

Pero existen algunas diferencias de la versión original. Las primeras que pude encontrar navegando por Internet al principio fueron las siguientes:

Observando los comentarios vemos existen diferencias puntuales en la forma de comportarse frente al protocolo original. Prácticamente todas siguen siendo ciertas en la actualidad, a excepción  del mecanismo SASL utilizado, ya que en la versión anterior era DIGEST-MD5.

Pongamos como ejemplo el siguiente paquete para poder comprenderlo más fácilmente:

El Jabber ID (JID) está compuesto por el número de teléfono que hemos registrado y el dominio s.whatsapp.net. El id del mensaje permite diferenciarlo de forma única del resto, así no habrá problemas con el procesado posterior. Además se encuentran otra serie de campos, como el timestamp de cuando se envió el mensaje, el cuerpo etc.

Esto nos sirve para hacernos una idea del formato de mensajes que utiliza la aplicación. Pero continuemos la explicación general de cómo funciona el protocolo en sí.

Al ser una aplicación para móviles, la gente que trabaja en WhatsApp ha intentado reducir el tamaño de los mensajes y la carga del protocolo a lo mínimo. ¿Cómo lo han hecho?

Todas las palabras reservadas utilizadas (como message, from, body etc.), han sido sustituidas exactamente por un byte, logrando reducir considerablemente la carga. FunXMPP utilizada una tabla hash de conversión para todas las palabras reservadas.

Basándonos en esto, utilizando bytes con el formato \xnn (nn representa un número hexadecimal) y sustituyéndolos por las palabras reservadas, el ejemplo anterior podría quedar de la siguiente forma:

Como podemos ver, las variables como NcN, o el texto del mensaje, no pueden ser sustituidas por representaciones tipo byte al no ser palabras reservadas. Existe una relación completa byte-palabra, aunque veamos a ver algunas de ellas necesarios para entender lo más básico:
           Byte \xfc: representa una secuencia de caracteres ASCII que será utilizada como valor. La longitud de la cadena se encuentra en el siguiente byte (longitud máxima de 255 caracteres).
           Byte \xfd: representa secuencia de caracteres ASCII, con la diferencia de que la longitud viene expresada en los siguientes tres bytes (longitud máxima de 16777215 caracteres).
           Byte \xf8 y \xf9: representación de tipo especial ‘lista’. El número de elementos se encuentra a continuación. Como estamos tratando como un lenguaje XML, deben abrirse y cerrarse las etiquetas, lo cual nos ayudará a la hora de entender el código.

Los objetos se cuentan de la siguiente forma:
Esta representación es muy sencilla, era fácil de ver en la versión 1.1 y anteriores. A partir de la versión 1.2, los mensajes van cifrados, por lo que necesitaremos más esfuerzo (os lo explicaré en otro post más adelante).


Teniendo todo esto en cuenta, veamos un mensaje real de autenticación de la aplicación (ya descifrado), corriendo sobre un dispositivo iPhone. Mostraremos los bytes correspondientes, así como las palabras reservadas a las que corresponden:

Como veis, la conexión comienza siempre con el identificación de la versión de protocolo que vamos a utilizar. En este caso se trata de WhatsApp 1.2Se solicita la conexión contra los servidores de la aplicación (s.whatsapp.net) y se realiza el intercambio de características soportadas por el dispositivo y la versión ejecutada de WhatsApp.

Finalmente se selecciona el mecanismo de autenticación (WAUTH-1) a través de SASL, indicando como usuario el número de teléfono que hayamos registrado. SASL es un framework para autenticación y autorización en protocolos de Internet, que separa los mecanismos de autenticación de los protocolos de la aplicación permitiendo, en teoría, a cualquier protocolo de aplicación que use SASL usar cualquier mecanismo de autenticación soportado por SASL. A partir de este momento, el cliente intentará autenticarse para acceder al servicio.


Por el momento creo que es suficiente para poder entender el protocolo :)