Questo articolo sulla crittografia end-to-End di MTProto è pensato per gli utenti avanzati.Se vuoi saperne di più sulle chat segrete da una fonte meno intimidatoria, consulta le nostre FAQ generali.
Si noti che a partire dalla versione 4.6, i principali client di Telegram utilizzano MTProto 2.0.MTProto v. 1.0 è deprecato ed è attualmente in fase di eliminazione.
Le chat segrete sono chat one-to-one in cui i messaggi sono crittografati con una chiave detenuta solo dai partecipanti alla chat. Si noti che lo schema per queste chat segrete crittografate end-to-end è diverso da quello utilizzato per le chat cloud:
Una nota su MTProto 2.0
Questo articolo descrive il livello di crittografia end-to-end nel protocollo MTProto versione 2.0.Le principali differenze rispetto alla versione 1.0 (qui descritte per riferimento) sono le seguenti:
- SHA-256 viene utilizzato al posto di SHA-1;
- I byte padding sono coinvolti nel calcolo di msg_key;
- msg_key dipende non solo dal messaggio da crittografare, ma anche da una parte della chiave di chat segreta;
- 12..1024 byte di riempimento sono usati invece di 0..15 byte di riempimento in v. 1.0.
Vedi anche: MTProto 2.0: Chat cloud, crittografia server-client
Generazione di chiavi
Le chiavi vengono generate utilizzando il protocollo Diffie-Hellman.
Consideriamo il seguente scenario: L’Utente A vorrebbe avviare una comunicazione crittografata end-to-end con l’Utente B.
Invio di una richiesta
L’utente A esegue i messaggi.getDhConfig per ottenere i parametri Diffie-Hellman: un primo p e un elemento di ordine elevato g.
L’esecuzione di questo metodo prima di ogni nuova procedura di generazione di chiavi è di vitale importanza. Ha senso memorizzare nella cache i valori dei parametri insieme alla versione per evitare di dover ricevere tutti i valori ogni volta. Se la versione memorizzata sul client è ancora aggiornata, il server restituirà i messaggi del costruttore.dhConfigNotModified.
Cliente è tenuto a verificare se p è una cassaforte a 2048 bit prime (il che significa che p e (p-1)/2 sono primi, e che la 2^2047 < p < 2^2048), e che g genera un sottogruppo ciclico di primo ordine (p-1)/2, cioè è un residuo quadratico mod p. Dato che g è sempre uguale a 2, 3, 4, 5, 6 o 7, questo è fatto facilmente utilizzando la reciprocità quadratica legge, producendo una condizione semplice mod p 4g — vale a dire, p mod 8 = 7 g = 2; p mod 3 = 2 g = 3; nessuna condizione per g = 4; p mod 5 = 1 o 4 per g = 5; p mod 24 = 19 o 23 g = 6; e p mod 7 = 3, 5 o 6 per g = 7. Dopo che g e p sono stati controllati dal client, ha senso memorizzare nella cache il risultato, in modo da evitare di ripetere lunghi calcoli in futuro. Questa cache potrebbe essere condivisa con una utilizzata per la generazione della chiave di autorizzazione.
Se il client ha un generatore di numeri casuali inadeguato, ha senso passare il parametro random_length (random_length> 0) in modo che il server generi la propria sequenza casuale casuale della lunghezza appropriata.Importante: l’utilizzo della sequenza casuale del server nella sua forma grezza potrebbe non essere sicuro. Deve essere combinato con una sequenza client, ad esempio, generando un numero casuale client della stessa lunghezza (client_random) e utilizzando final_random := random XOR client_random
.
Il client A calcola un numero a 2048 bit (usando entropia sufficiente o casuale del server; vedi sopra) ed esegue i messaggi.requestEncryption dopo aver passato g_a := pow(g, a) mod dh_prime
.
L’utente B riceve l’aggiornamento updateEncryption per tutte le chiavi di autorizzazione associate (tutti i dispositivi autorizzati) con il costruttore di chat encryptedChatRequested. All’utente devono essere mostrate le informazioni di base sull’utente A e deve essere richiesto di accettare o rifiutare la richiesta.
Entrambi i client devono verificare che g, g_a e g_b siano maggiori di uno e minori di p-1. Si consiglia di verificare che g_a e g_b siano compresi tra 2 ^ {2048-64} e p-2 ^ {2048-64}.
Accettare una richiesta
Dopo che l’Utente B conferma la creazione di una chat segreta con A nell’interfaccia client, il Client B riceve anche i parametri di configurazione aggiornati per il metodo Diffie-Hellman. Successivamente, genera un numero casuale a 2048 bit, b, utilizzando regole simili a quelle per a.
Avendo ricevuto g_a dall’aggiornamento con encryptedChatRequested, può generare immediatamente la chiave condivisa finale:key = (pow(g_a, b) mod dh_prime)
. Se la lunghezza della chiave < 256 byte, aggiungere diversi byte zero iniziali come padding, in modo che la chiave sia lunga esattamente 256 byte. La sua impronta digitale, key_fingerprint, è uguale agli ultimi 64 bit di SHA1 (chiave).
Nota 1: in questo caso particolare SHA1 è usato qui anche per le chat segrete MTProto 2.0.
Nota 2: questa impronta digitale viene utilizzata come controllo di integrità per la procedura di scambio delle chiavi per rilevare i bug durante lo sviluppo di software client — non è collegata alla visualizzazione delle chiavi utilizzata sui client come mezzo di autenticazione esterna nelle chat segrete. Le visualizzazioni chiave sui client vengono generate utilizzando i primi 128 bit di SHA1 (chiave intial) seguiti dai primi 160 bit di SHA256 (chiave utilizzata quando la chat segreta è stata aggiornata al livello 46).
Il client B esegue i messaggi.acceptEncryption dopo averlo passatog_b := pow(g, b) mod dh_prime
e key_fingerprint.
Per tutti i dispositivi autorizzati del Client B, ad eccezione di quello corrente, gli aggiornamenti updateEncryption vengono inviati con il costruttore encryptedChatDiscarded. Successivamente, l’unico dispositivo che sarà in grado di accedere alla chat segreta è il dispositivo B, che ha effettuato la chiamata ai messaggi.acceptEncryption.
All’utente A verrà inviato un aggiornamento updateEncryption con il costruttore encryptedChat, per la chiave di autorizzazione che ha avviato la chat.
Con g_b dall’aggiornamento, il Client A può anche calcolare la chiave condivisakey = (pow(g_b, a) mod dh_prime)
. Se la lunghezza della chiave < 256 byte, aggiungere diversi byte zero iniziali come padding, in modo che la chiave sia lunga esattamente 256 byte. Se l’impronta digitale per la chiave ricevuta è identica a quella passata a encryptedChat, i messaggi in arrivo possono essere inviati ed elaborati. Altrimenti, messaggi.discardEncryption deve essere eseguito e l’utente notificato.
Perfect Forward Secrecy
al fine di mantenere le precedenti comunicazioni cassetta di sicurezza, ufficiale Telegramma client di avviare re-keying una volta che un tasto è stato utilizzato per decifrare e crittografare più di 100 messaggi, o è stato in uso per più di una settimana, a condizione che la chiave è stata utilizzata per crittografare almeno un messaggio. Le vecchie chiavi vengono quindi scartate in modo sicuro e non possono essere ricostruite, anche con l’accesso alle nuove chiavi attualmente in uso.
Il protocollo di re-keying è ulteriormente descritto in questo articolo: Perfect Forward Secrecy in Secret Chat.
Si prega di notare che il cliente deve supportare la segretezza in avanti nelle chat segrete per essere compatibile con i client ufficiali di Telegram.
Invio e ricezione di messaggi in una chat segreta
Serializzazione e crittografia dei messaggi in uscita
Viene creato un oggetto TL di tipo DecryptedMessage e contiene il messaggio in testo normale. Per la compatibilità con le versioni precedenti, l’oggetto deve essere avvolto nel costruttore decryptedMessageLayer con un’indicazione del livello supportato (a partire da 46).
Lo schema TL per il contenuto dei messaggi crittografati end-to-end è disponibile qui “
Il costrutto risultante è serializzato come un array di byte usando regole TL generiche. L’array risultante viene anteposto di 4 byte contenenti la lunghezza dell’array senza contare questi 4 byte.
L’array di byte è riempito con 12 a 1024 byte di riempimento casuale per rendere la sua lunghezza divisibile per 16 byte. (Nella vecchia crittografia MTProto 1.0, sono stati utilizzati solo da 0 a 15 byte di padding.)
La chiave del messaggio, msg_key, è calcolata come i 128 bit centrali dello SHA256 dei dati ottenuti nel passaggio precedente, anteposti di 32 byte dalla chiave chiave condivisa. (Per la vecchia crittografia MTProto 1.0, msg_key è stato calcolato in modo diverso, come i 128 bit inferiori di SHA1 dei dati ottenuti nei passaggi precedenti, esclusi i byte di padding.)
Per MTProto 2.0, la chiave AES aes_key e il vettore di inizializzazione aes_iv sono calcolati (key è la chiave condivisa ottenuta durante la generazione della chiave) come segue:
- msg_key_large = SHA256 ( substr (key, 88 + x, 32) + testo in chiaro + random_padding);
- msg_key = substr (msg_key_large, 8, 16);
- sha256_a = SHA256 (msg_key + substr (tasto x, 36));
- sha256_b = SHA256 (substr (chiave, 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);
Per MTProto 2.0, x=0 per i messaggi dal mittente del segreto di chat, x=8 per i messaggi nella direzione opposta.
Per l’obsoleto MTProto 1.0, msg_key, aes_key e aes_iv sono stati calcolati in modo diverso (vedere questo documento per riferimento).
I dati vengono crittografati con una chiave a 256 bit, aes_key, e un vettore di inizializzazione a 256 bit, aes-iv, utilizzando la crittografia AES-256 con infinite garble Extension (IGE). Chiave di crittografia impronta digitale key_fingerprint e la chiave del messaggio msg_key vengono aggiunti nella parte superiore della matrice di byte risultante.
I dati crittografati sono incorporati in un messaggio.sendEncrypted chiamata API e passato al server Telegram per la consegna all’altra parte della Chat segreta.
Aggiornamento a MTProto 2.0 da MTProto 1.0
Non appena entrambe le parti in una chat segreta utilizzano almeno il livello 73, dovrebbero utilizzare solo MTProto 2.0 per tutti i messaggi in uscita. Alcuni dei primi messaggi ricevuti possono utilizzare MTProto 1.0, se un livello di partenza sufficientemente alto non è stato negoziato durante la creazione della chat segreta. Dopo aver ricevuto il primo messaggio crittografato con MTProto 2.0 (o il primo messaggio con Layer 73 o superiore), tutti i messaggi con numeri di sequenza più alti devono essere crittografati anche con MTProto 2.0.
Finché il livello corrente è inferiore a 73, ciascuna parte dovrebbe provare a decifrare i messaggi ricevuti con MTProto 1.0, e se questo non ha successo (msg_key non corrisponde), prova MTProto 2.0. Una volta che arriva il primo messaggio crittografato MTProto 2.0 (o il livello viene aggiornato a 73), non è necessario provare la decrittografia MTProto 1.0 per nessuno degli ulteriori messaggi (a meno che il client non stia ancora aspettando che vengano chiuse alcune lacune).
Decrittografia di un messaggio in arrivo
I passaggi precedenti vengono eseguiti in ordine inverso. Quando viene ricevuto un messaggio crittografato, è necessario verificare che msg_key sia effettivamente uguale ai 128 bit centrali dell’hash SHA256 del messaggio decifrato, anteposto ai 32 byte presi dalla chiave condivisa.Se il livello del messaggio è maggiore di quello supportato dal client, all’utente deve essere notificato che la versione del client non è aggiornata e richiesto l’aggiornamento.
Numeri di sequenza
È necessario interpretare tutti i messaggi nel loro ordine originale per proteggersi da possibili manipolazioni. Le chat segrete supportano un meccanismo speciale per gestire i contatori seq_no indipendentemente dal server.
La corretta gestione di questi contatori è ulteriormente descritta in questo articolo: Numeri di sequenza nelle chat segrete.
Si prega di notare che il client deve supportare i numeri di sequenza nelle chat segrete per essere compatibile con i client ufficiali di Telegram.
Invio di file crittografati
Tutti i file inviati alle chat segrete sono crittografati con chiavi una tantum che non sono in alcun modo correlate alla chiave condivisa della chat. Prima di inviare un file crittografato, si presume che l’indirizzo del file crittografato verrà allegato all’esterno di un messaggio crittografato utilizzando il parametro file dei messaggi.Metodo sendEncryptedFile e che la chiave per la decrittografia diretta verrà inviata nel corpo del messaggio (il parametro chiave nei costruttori decryptedMessageMediaPhoto, decryptedMessageMediaVideo e decryptedMessageMediaFile.
Prima che un file venga inviato a una chat segreta, vengono calcolati 2 numeri casuali a 256 bit che fungeranno da chiave AES e vettore di inizializzazione utilizzato per crittografare il file. La crittografia AES-256 con infinite garble Extension (IGE) viene utilizzata in modo simile.
L’impronta digitale della chiave è calcolata come segue:
- digest = md5(key + iv)
- fingerprint = substr(digest, 0, 4) XOR substr(digest, 4, 4)
Il contenuto crittografato di un file viene memorizzato sul server più o meno allo stesso modo di quelli di un file nelle chat cloud: pezzo per pezzo usando le chiamate da caricare.Salvafileparto.Una chiamata successiva ai messaggi.sendEncryptedFile assegnerà un identificatore al file memorizzato e invierà l’indirizzo insieme al messaggio. Il destinatario riceverà un aggiornamento con encryptedMessage e il parametro file conterrà informazioni sul file.
I file crittografati in entrata e in uscita possono essere inoltrati ad altre chat segrete utilizzando il costruttore inputEncryptedFile per evitare di salvare lo stesso contenuto sul server due volte.
Lavorare con una casella di aggiornamento
Le chat segrete sono associate a dispositivi specifici (o meglio a chiavi di autorizzazione), non agli utenti. Una finestra di messaggio convenzionale, che utilizza pts per descrivere lo stato del cliente, non è adatta, perché è progettata per l’archiviazione a lungo termine dei messaggi e l’accesso ai messaggi da dispositivi diversi.
Una coda di messaggi temporanea aggiuntiva viene introdotta come soluzione a questo problema. Quando viene inviato un aggiornamento relativo a un messaggio da una chat segreta, viene inviato un nuovo valore di qts, che aiuta a ricostruire la differenza se c’è stata una lunga interruzione nella connessione o in caso di perdita di un aggiornamento.
All’aumentare del numero di eventi, il valore di qts aumenta di 1 ad ogni nuovo evento. Il valore iniziale non può (e non sarà) essere uguale a 0.
Il fatto che gli eventi dalla coda temporanea siano stati ricevuti e memorizzati dal client viene riconosciuto esplicitamente da una chiamata ai messaggi.Metodo receivedQueue o implicitamente da una chiamata agli aggiornamenti.getDifference (il valore di qts passato, non lo stato finale). Tutti i messaggi riconosciuti come consegnati dal client, così come tutti i messaggi più vecchi di 7 giorni, possono (e saranno) essere cancellati dal server.
Al momento della de-autorizzazione, la coda di eventi del dispositivo corrispondente verrà cancellata forzatamente e il valore di qts diventerà irrilevante.
Aggiornamento a nuovi livelli
Il tuo client dovrebbe sempre memorizzare il livello massimo che è noto per essere supportato dal client dall’altra parte di una chat segreta. Quando viene creata la chat segreta, questo valore deve essere inizializzato a 46. Questo valore di livello remoto deve essere sempre aggiornato immediatamente dopo aver ricevuto qualsiasi pacchetto contenente informazioni di un livello superiore, ad es.:
- nessun segreto chat messaggio contenente layer_no nella sua
decryptedMessageLayer
con strato>=46, o - un decryptedMessageActionNotifyLayer messaggio di servizio, avvolto come se fosse la decryptedMessageService costruttore del obsoleti con i layer 8 (costruttore
decryptedMessageService#aa48327d
).
Notifica al client remoto del proprio livello locale
Per notificare al client remoto il proprio livello locale, il client deve inviare un messaggio del tipodecryptedMessageActionNotifyLayer
. Questa notifica deve essere racchiusa in un costruttore di un livello appropriato.
Ci sono due casi in cui il client deve informare il client remoto del suo livello locale:
- Non appena è stata creata una nuova chat segreta, immediatamente dopo che la chiave segreta è stata scambiata con successo.
- Subito dopo che il client locale è stato aggiornato per supportare un nuovo livello di chat segreta. In questo caso le notifiche devono essere inviate a tutte le chat segrete attualmente esistenti. Si noti che questo è necessario solo quando si aggiorna a nuovi livelli che contengono modifiche nell’implementazione delle chat segrete (ad esempio non è necessario farlo quando il client viene aggiornato dal livello 46 al livello 47).