Este artículo sobre el cifrado de extremo a extremo de MTProto está destinado a usuarios avanzados.Si desea obtener más información sobre Chats secretos de una fuente menos intimidante, consulte nuestras preguntas frecuentes generales.
Tenga en cuenta que a partir de la versión 4.6, los principales clientes de Telegram utilizan MTProto 2.0.MTProto v. 1.0 está en desuso y actualmente se está eliminando gradualmente.
Los chats secretos son chats individuales en los que los mensajes se cifran con una clave que solo tienen los participantes del chat. Tenga en cuenta que el esquema para estos chats secretos cifrados de extremo a extremo es diferente del que se utiliza para los chats en la nube:
Una nota sobre MTProto 2.0
Este artículo describe la capa de cifrado de extremo a extremo en el protocolo MTProto versión 2.0.Las principales diferencias con respecto a la versión 1.0 (descrita aquí como referencia) son las siguientes:
- Se utiliza SHA-256 en lugar de SHA-1;
- Los bytes de relleno están involucrados en el cálculo de msg_key;
- msg_key depende no solo del mensaje a cifrar, sino también de una parte de la clave de chat secreta;
- 12..se utilizan 1024 bytes de relleno en lugar de 0..15 bytes de relleno en v. 1. 0.
Consulte también: MTProto 2.0: Chats en la nube, cifrado servidor-cliente
Generación de claves
Las claves se generan utilizando el protocolo Diffie-Hellman.
Consideremos el siguiente escenario: Al Usuario A le gustaría iniciar una comunicación cifrada de extremo a extremo con el Usuario B.
Enviando una solicitud
El usuario A ejecuta mensajes.getDhConfig para obtener los parámetros Diffie-Hellman: un p primo y un elemento de orden alto g.
Ejecutar este método antes de cada nuevo procedimiento de generación de claves es de vital importancia. Tiene sentido almacenar en caché los valores de los parámetros junto con la versión para evitar tener que recibir todos los valores cada vez. Si la versión almacenada en el cliente aún está actualizada, el servidor devolverá los mensajes del constructor.Configuración no modificada.Se espera que el cliente compruebe si p es un primo seguro de 2048 bits (lo que significa que tanto p como (p-1) /2 son primos, y que 2^2047 <p < 2^2048), y que g genera un subgrupo cíclico de orden primo (p-1)/2, es decir, es un residuo cuadrático mod p. Dado que g es siempre igual a 2, 3, 4, 5, 6 o 7, esto se hace fácilmente usando la ley de reciprocidad cuadrática, dando una condición simple en p mod 4g namely a saber, p mod 8 = 7 para g = 2; p mod 3 = 2 para g = 3; sin condición adicional para g = 4; p mod 5 = 1 o 4 para g = 5; p mod 24 = 19 o 23 para g = 6; y p mod 7 = 3, 5 o 6 para g = 7. Después de que g y p hayan sido comprobados por el cliente, tiene sentido almacenar en caché el resultado, para evitar repetir largos cálculos en el futuro. Esta caché puede compartirse con una utilizada para generar claves de autorización.
Si el cliente tiene un generador de números aleatorios inadecuado, tiene sentido pasar el parámetro random_length(random_length > 0) para que el servidor genere su propia secuencia aleatoria aleatoria de la longitud apropiada.Importante: usar la secuencia aleatoria del servidor en su forma cruda puede ser inseguro. Debe combinarse con una secuencia de cliente, por ejemplo, generando un número aleatorio de cliente de la misma longitud (client_random) y utilizando final_random := random XOR client_random
.
El cliente A calcula un número a de 2048 bits (utilizando suficiente entropía o el aleatorio del servidor; véase más arriba) y ejecuta mensajes.Cifrado de solicitudes después de pasar g_a := pow(g, a) mod dh_prime
.
El usuario B recibe el cifrado update updateEncryption para todas las claves de autorización asociadas (todos los dispositivos autorizados) con el constructor de chat encryptedChatRequested. Al usuario se le debe mostrar información básica sobre el Usuario A y se le debe solicitar que acepte o rechace la solicitud.
Ambos clientes deben comprobar que g, g_a y g_b son mayores que uno y menores que p-1. Recomendamos comprobar que g_a y g_b estén entre 2^{2048-64} y p-2^{2048-64} también.
Aceptar una solicitud
Después de que el Usuario B confirme la creación de un chat secreto con A en la interfaz del cliente, el Cliente B también recibe parámetros de configuración actualizados para el método Diffie-Hellman. A partir de entonces, genera un número aleatorio de 2048 bits, b, utilizando reglas similares a las de a.
Habiendo recibido g_a de la actualización con encryptedChatRequested, puede generar inmediatamente la clave compartida final: key = (pow(g_a, b) mod dh_prime)
. Si la longitud de la clave < 256 bytes, agregue varios bytes cero iniciales como relleno — de modo que la clave tenga exactamente 256 bytes de longitud. Su huella dactilar, key_fingerprint, es igual a los 64 últimos bits de SHA1 (clave).
Nota 1: en este caso particular, SHA1 se usa aquí incluso para chats secretos MTProto 2.0.
Nota 2: esta huella digital se utiliza como una comprobación de cordura para el procedimiento de intercambio de claves para detectar errores al desarrollar software de cliente: no está conectada a la visualización de claves utilizada en los clientes como medio de autenticación externa en chats secretos. Las visualizaciones de claves en los clientes se generan utilizando los primeros 128 bits de SHA1(clave inicial) seguidos de los primeros 160 bits de SHA256 (clave utilizada cuando el chat secreto se actualizó a la capa 46).
El cliente B ejecuta mensajes.Aceptar cifrado después de pasarlo g_b := pow(g, b) mod dh_prime
y key_fingerprint.
Para todos los dispositivos autorizados del Cliente B, excepto el actual, las actualizaciones de updateEncryption se envían con el constructor encryptedChatDiscarded. A partir de entonces, el único dispositivo que podrá acceder al chat secreto es el Dispositivo B, que hizo la llamada a los mensajes.Cifrado de aceptación.
Al usuario A se le enviará una actualización de updateEncryption con el constructor encryptedChat, para la clave de autorización que inició el chat.
Con g_b de la actualización, el Cliente A también puede calcular la clave compartida key = (pow(g_b, a) mod dh_prime)
. Si la longitud de la clave < 256 bytes, agregue varios bytes cero iniciales como relleno — de modo que la clave tenga exactamente 256 bytes de longitud. Si la huella digital de la clave recibida es idéntica a la que se pasó a encryptedChat, se pueden enviar y procesar los mensajes entrantes. De lo contrario, mensajes.El cifrado de descartes debe ejecutarse y el usuario debe ser notificado.
Perfect Forward Secrecy
Para mantener seguras las comunicaciones pasadas, los clientes oficiales de Telegram iniciarán la re-codificación una vez que una clave se haya utilizado para descifrar y cifrar más de 100 mensajes, o haya estado en uso durante más de una semana, siempre que la clave se haya utilizado para cifrar al menos un mensaje. Las llaves antiguas se desechan de forma segura y no se pueden reconstruir, incluso con acceso a las llaves nuevas que se están utilizando actualmente.
El protocolo de re-keying se describe con más detalle en este artículo: Perfect Forward Secrecy en Chats secretos.
Tenga en cuenta que su cliente debe admitir el Secreto directo en Chats secretos para ser compatible con los clientes oficiales de Telegram.
Envío y recepción de mensajes en un Chat Secreto
Serialización y Cifrado de mensajes Salientes
Se crea un objeto TL de tipo DecryptedMessage que contiene el mensaje en texto plano. Para la compatibilidad con versiones anteriores, el objeto debe estar envuelto en el constructor decryptedMessageLayer con una indicación de la capa admitida (comenzando por 46).
El esquema TL para el contenido de mensajes cifrados de extremo a extremo está disponible aquí»
La construcción resultante se serializa como una matriz de bytes utilizando reglas TL genéricas. La matriz resultante se antepone con 4 bytes que contienen la longitud de la matriz sin contar estos 4 bytes.
La matriz de bytes está acolchada con 12 a 1024 bytes de relleno aleatorios para que su longitud sea divisible por 16 bytes. (En el antiguo cifrado MTProto 1.0, solo se usaban de 0 a 15 bytes de relleno.)
La clave de mensaje, msg_key, se calcula como los 128 bits medios del SHA256 de los datos obtenidos en el paso anterior, precedidos por 32 bytes de la clave compartida. (Para el cifrado MTProto 1.0 más antiguo, msg_key se calculó de manera diferente, como los 128 bits más bajos de SHA1 de los datos obtenidos en los pasos anteriores, excluyendo los bytes de relleno.)
Para MTProto 2.0, la clave aes_key y el vector de inicialización aes_iv se calculan (la clave es la clave compartida obtenida durante la generación de la clave ) de la siguiente manera:
- msg_key_large = SHA256 (substr (clave, 88 + x, 32) + texto plano + random_padding);
- msg_key = substr (msg_key_large, 8, 16);
- sha256_a = SHA256 (msg_key + substr (clave, x, 36));
- sha256_b = SHA256 (substr (clave, 40+x, 36) + msg_key);
- aes_key = substr (sha256_a, 0, 8) + substr (sha256_b, 8, 16) + substr (sha256_a, 24, 8);
- aes_iv = substr (sha256_b, 0, 8) + substr (sha256_a, 8, 16) + substr (sha256_b, 24, 8);
Para MTProto 2.0, x=0 para los mensajes del autor de el secreto de chat, x=8 para los mensajes en la dirección opuesta.
Para el obsoleto MTProto 1.0, msg_key, aes_key y aes_iv se calcularon de manera diferente(consulte este documento para consultar).
Los datos se cifran con una clave de 256 bits, aes_key, y un vector de inicialización de 256 bits, aes-iv, utilizando cifrado AES-256 con extensión infinita de garble (IGE). Clave de cifrado huella dactilar key_fingerprint y la clave de mensaje msg_key se añaden en la parte superior de la matriz de bytes resultante.
Los datos cifrados están incrustados en un mensaje.Llamada API encriptada y pasada al servidor de Telegram para su entrega a la otra parte del Chat Secreto.
Actualización a MTProto 2.0 desde MTProto 1.0
Tan pronto como ambas partes en un chat secreto estén usando al menos la capa 73, solo deben usar MTProto 2.0 para todos los mensajes salientes. Algunos de los primeros mensajes recibidos pueden usar MTProto 1.0, si no se ha negociado una capa inicial suficientemente alta durante la creación del chat secreto. Después de recibir el primer mensaje cifrado con MTProto 2.0 (o el primer mensaje con Capa 73 o superior), todos los mensajes con números de secuencia superiores también deben cifrarse con MTProto 2.0.
Mientras la capa actual sea inferior a 73, cada parte debe intentar descifrar los mensajes recibidos con MTProto 1.0, y si esto no es exitoso (msg_key no coincide), pruebe MTProto 2.0. Una vez que llega el primer mensaje cifrado MTProto 2.0 (o la capa se actualiza a 73), no es necesario probar el descifrado MTProto 1.0 para ninguno de los mensajes adicionales (a menos que el cliente aún esté esperando que se cierren algunos huecos).
Descifrar un mensaje entrante
Los pasos anteriores se realizan en orden inverso. Cuando se recibe un mensaje cifrado, debe comprobar que msg_key es de hecho igual a los 128 bits medios del hash SHA256 del mensaje descifrado, precedido por 32 bytes tomados de la clave compartida.Si la capa de mensajes es mayor que la que admite el cliente, se debe notificar al usuario que la versión del cliente está desactualizada y se le debe solicitar que actualice.
Números de secuencia
Es necesario interpretar todos los mensajes en su orden original para protegerse de posibles manipulaciones. Los chats secretos soportan un mecanismo especial para manejar contadores seq_no independientemente del servidor.
El manejo adecuado de estos contadores se describe con más detalle en este artículo: Números de secuencia en Chats secretos.
Tenga en cuenta que su cliente debe admitir números de secuencia en Chats secretos para ser compatible con clientes oficiales de Telegram.
Envío de archivos cifrados
Todos los archivos enviados a chats secretos se cifran con claves de una sola vez que no están relacionadas de ninguna manera con la clave compartida del chat. Antes de enviar un archivo cifrado, se asume que la dirección del archivo cifrado se adjuntará al exterior de un mensaje cifrado utilizando el parámetro archivo de los mensajes.sendEncryptedFile y que la clave para el descifrado directo se enviará en el cuerpo del mensaje (el parámetro clave en los constructores decryptedMessageMediaPhoto, decryptedMessageMediaVideo y decryptedMessageMediaFile).
Antes de enviar un archivo a un chat secreto, se calculan 2 números aleatorios de 256 bits que servirán como clave AES y vector de inicialización utilizado para cifrar el archivo. El cifrado AES – 256 con extensión infinita de garble (IGE) se utiliza de la misma manera.
La huella dactilar de la clave se calcula de la siguiente manera:
- digest = md5(clave + iv)
- fingerprint = substr(digest, 0, 4) XOR substr(digest, 4, 4)
El contenido cifrado de un archivo se almacena en el servidor de la misma manera que el de un archivo en chats en la nube: pieza por pieza usando llamadas para cargar.saveFilePart.Una llamada posterior a los mensajes.sendEncryptedFile asignará un identificador al archivo almacenado y enviará la dirección junto con el mensaje. El destinatario recibirá una actualización con encryptedMessage, y el parámetro archivo contendrá información del archivo.
Los archivos cifrados entrantes y salientes se pueden reenviar a otros chats secretos utilizando el archivo inputEncryptedFile del constructor para evitar guardar el mismo contenido en el servidor dos veces.
Trabajar con un cuadro de actualización
Los chats secretos están asociados con dispositivos específicos (o más bien con claves de autorización), no con usuarios. Un cuadro de mensajes convencional, que usa pts para describir el estado del cliente, no es adecuado, porque está diseñado para el almacenamiento de mensajes a largo plazo y el acceso a mensajes desde diferentes dispositivos.
Se introduce una cola de mensajes temporales adicionales como solución a este problema. Cuando se envía una actualización con respecto a un mensaje de un chat secreto, se envía un nuevo valor de qts, que ayuda a reconstruir la diferencia si ha habido una interrupción larga en la conexión o en caso de pérdida de una actualización.
A medida que aumenta el número de eventos, el valor de qts aumenta en 1 con cada nuevo evento. El valor inicial puede no ser (y no será) igual a 0.
El hecho de que los eventos de la cola temporal hayan sido recibidos y almacenados por el cliente se reconoce explícitamente mediante una llamada a los mensajes.Método receivedQueue o implícitamente por una llamada a actualizaciones.getDifference (el valor de qts pasado, no el estado final). Todos los mensajes reconocidos como entregados por el cliente, así como cualquier mensaje anterior a 7 días, pueden (y serán) eliminados del servidor.
Tras la desautorización, la cola de eventos del dispositivo correspondiente se borrará por la fuerza y el valor de qts pasará a ser irrelevante.
Actualizar a nuevas capas
Su cliente siempre debe almacenar la capa máxima que se sabe que es compatible con el cliente en el otro lado de un chat secreto. Cuando se crea por primera vez el chat secreto, este valor debe inicializarse a 46. Este valor de capa remota debe actualizarse siempre inmediatamente después de recibir cualquier paquete que contenga información de una capa superior, p. ej.:
- cualquier mensaje de chat secreto que contenga layer_no en su
decryptedMessageLayer
con layer>=46, o - un mensaje de servicio de acción de mensaje descifrado Notifylayer, envuelto como si fuera el constructor de servicio de mensaje descifrado de la capa 8 obsoleta (constructor
decryptedMessageService#aa48327d
).
Notificar al cliente remoto sobre su capa local
Para notificar al cliente remoto de su capa local, su cliente debe enviar un mensaje de tipo decryptedMessageActionNotifyLayer
. Esta notificación debe estar envuelta en un constructor de una capa apropiada.
Hay dos casos en los que su cliente debe notificar al cliente remoto sobre su capa local:
- Tan pronto como se haya creado un nuevo chat secreto, inmediatamente después de que la clave secreta se haya intercambiado correctamente.
- Inmediatamente después de que el cliente local se haya actualizado para admitir una nueva capa de chat secreto. En este caso, las notificaciones deben enviarse a todos los chats secretos existentes actualmente. Tenga en cuenta que esto solo es necesario cuando se actualiza a nuevas capas que contienen cambios en la implementación de chats secretos (p. ej. no necesita hacer esto cuando su cliente se actualiza de la Capa 46 a la Capa 47).