
Telegram crittografia end-to-end
Mentre in un altro articolo abbiamo racconto l’architettura Telegram, qui parleremo della Telegram crittografia end-to-end di MTProto ed è un articolo è pensato tecnici e sviluppatori .
Si noti che a partire dalla versione 4.6, i principali client Telegram utilizzano MTProto 2.0 .
MTProto v.1.0 è obsoleto e attualmente è in fase di eliminazione.
MTProto 2.0 è un algoritmo di crittografia, ovvero un protocollo proprietario appositamente progettato da Telegram per l’accesso ad una server API da applicazioni attive su device mobile
Le chat segrete sono chat one-to-one in cui i messaggi sono criptati con una chiave detenuta solo dai partecipanti alla chat. Nota che lo schema per queste chat segrete Telegram crittografia end-to-end criptate è diverso da quello utilizzato per le chat cloud :

Una nota su MTProto 2.0 – Telegram crittografia end-to-end
Questo articolo descrive il livello di crittografia end-to-end nel protocollo MTProto versione 2.0 .
Le principali differenze rispetto alla versione 1.0 ( descritta qui per riferimento) sono le seguenti:
- Al posto di SHA-1 viene utilizzato SHA-256;
- I byte di riempimento sono coinvolti nel calcolo di msg_key;
- msg_key dipende non solo dal messaggio da crittografare, ma anche da una parte della chiave segreta della chat;
- Nella versione 1.0 vengono utilizzati 12..1024 byte di padding invece di 0..15 byte di padding.
Generazione di chiavi – Telegram crittografia end-to-end
Le chiavi vengono generate utilizzando il protocollo Diffie-Hellman .
Consideriamo il seguente scenario: l’utente A desidera avviare una comunicazione crittografata end-to-end con l’ utente B.
Invio di una richiesta
L’utente A esegue messages.getDhConfig per ottenere i parametri Diffie-Hellman: un elemento primo p e un elemento di ordine superiore g .
Eseguire questo metodo prima di ogni nuova procedura di generazione di chiavi è di vitale importanza. Ha senso mettere in 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 .
Ci si aspetta che il client verifichi se p è un numero primo sicuro a 2048 bit (il che significa che sia p che (p-1)/2 sono primi e che 2 2047 < p < 2 2048) e che g genera un sottogruppo ciclico di ordine primo (p-1)/2 , ovvero è un residuo quadratico mod p . Poiché g è sempre uguale a 2, 3, 4, 5, 6 o 7, ciò si ottiene facilmente utilizzando la legge di reciprocità quadratica, ottenendo una semplice condizione su p mod 4g , ovvero p mod 8 = 7 per g = 2 ; p mod 3 = 2 per g = 3 ; nessuna condizione aggiuntiva per g = 4 ; p mod 5 = 1 o 4 per g = 5 ; p mod 24 = 19 o 23 per 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 mettere in cache il risultato, in modo da evitare di ripetere calcoli lunghi in futuro. Questa cache potrebbe essere condivisa con una usata per la generazione della chiave di autorizzazione .
Se il client necessita di entropia aggiuntiva per il generatore di numeri casuali, può passare il parametro random_length (random_length> 0) in modo che il server generi la propria sequenza casuale random della lunghezza appropriata.
Importante : usare la sequenza casuale del server nella sua forma grezza potrebbe non essere sicuro, deve essere combinata con una sequenza client.
Il client A calcola un numero a 2048 bit (utilizzando entropia sufficiente o casuale del server ; vedere sopra) ed esegue messages.requestEncryption dopo averlo 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 controllare che g , g_a e g_b siano maggiori di uno e minori di p-1 . Consigliamo di controllare anche che g_a e g_b siano compresi tra 2^{2048-64} e p – 2^{2048-64} .
Accettazione di 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 .
Dopo aver 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 viene utilizzato anche per le chat segrete MTProto 2.0.
Nota 2: questa impronta digitale viene utilizzata come controllo di integrità per la procedura di scambio di chiavi per rilevare 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 delle chiavi sui client vengono generate utilizzando i primi 128 bit di SHA1 (chiave iniziale) seguiti dai primi 160 bit di SHA256 (chiave utilizzata quando la chat segreta è stata aggiornata al livello 46).
Il client B esegue messages.acceptEncryption dopo averlo passato g_b := pow(g, b) mod dh_primee key_fingerprint .
Per tutti i dispositivi autorizzati del Client B , eccetto quello attuale, gli aggiornamenti di 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 a messages.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 condivisa key = (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. In caso contrario, messages.discardEncryption deve essere eseguito e l’utente deve essere informato.
Segretezza perfetta in avanti – Telegram crittografia end-to-end
Per mantenere al sicuro le comunicazioni passate, i client ufficiali di Telegram avvieranno la re-keying una volta che una chiave è stata usata per decifrare e crittografare più di 100 messaggi, o è stata usata per più di una settimana, a condizione che la chiave sia stata usata 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.
Tieniamo presente che il nostro client deve supportare Forward Secrecy nelle chat segrete per essere compatibile con i client Telegram ufficiali.
Inviare e ricevere messaggi in una chat segreta
Serializzazione e crittografia dei messaggi in uscita
Viene creato un oggetto TL di tipo DecryptedMessage che contiene il messaggio in testo normale. Per compatibilità con le versioni precedenti, l’oggetto deve essere racchiuso nel costruttore decryptedMessageLayer con un’indicazione del layer supportato (a partire da 46).
Il costrutto risultante viene serializzato come un array di byte usando regole TL generiche. L’array risultante viene preceduto da 4 byte contenenti la lunghezza dell’array senza contare questi 4 byte.
L’array di byte viene riempito con un padding byte casuale compreso tra 12 e 1024 per rendere la sua lunghezza divisibile per 16 byte. (Nella vecchia crittografia MTProto 1.0 venivano utilizzati solo un padding byte compreso tra 0 e 15.)
La chiave del messaggio, msg_key , viene calcolata come i 128 bit centrali dello SHA256 dei dati ottenuti nel passaggio precedente, anteposti da 32 byte della chiave condivisa key . (Per la vecchia crittografia MTProto 1.0, msg_key veniva calcolato in modo diverso, come i 128 bit inferiori dello SHA1 dei dati ottenuti nei passaggi precedenti, escludendo i byte di riempimento .)
Per MTProto 2.0, la chiave AES aes_key e il vettore di inizializzazione aes_iv ( la chiave è la chiave condivisa ottenuta durante la generazione della chiave ) vengono calcolati come segue:
- msg_key_large = SHA256 (substr (chiave, 88+x, 32) + testo normale + random_padding);
- msg_key = substr (msg_key_large, 8, 16);
- sha256_a = SHA256 (msg_key + substr (chiave, 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 provenienti dall’autore della chat segreta, x=8 per i messaggi nella direzione opposta.
Per l’obsoleto MTProto 1.0, msg_key, aes_key e aes_iv venivano calcolati in modo diverso.
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 estensione garble infinita (IGE). L’impronta digitale della chiave di crittografia key_fingerprint e la chiave del messaggio msg_key vengono aggiunte in cima all’array di byte risultante.
I dati crittografati vengono incorporati in una chiamata API messages.sendEncrypted e trasmessi al server Telegram per essere recapitati all’altra parte della chat segreta.
Aggiornamento da MTProto 1.0 a MTProto 2.0 – Telegram crittografia end-to-end
Non appena entrambe le parti in una chat segreta utilizzano almeno il Layer 73, dovrebbero utilizzare solo MTProto 2.0 per tutti i messaggi in uscita. Alcuni dei primi messaggi ricevuti potrebbero utilizzare MTProto 1.0, se non è stato negoziato un layer di partenza sufficientemente alto durante la creazione della chat segreta. Dopo che il primo messaggio crittografato con MTProto 2.0 (o il primo messaggio con Layer 73 o superiore) è stato ricevuto, 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 ciò non riesce (msg_key non corrisponde), provare MTProto 2.0. Una volta che arriva il primo messaggio crittografato con MTProto 2.0 (o il livello viene aggiornato a 73), non c’è bisogno di provare la decifratura con MTProto 1.0 per nessuno dei messaggi successivi (a meno che il client non stia ancora aspettando che vengano chiusi alcuni gap).
Decifrare un messaggio in arrivo
I passaggi precedenti vengono eseguiti in ordine inverso.
Quando si riceve un messaggio crittografato, è necessario verificare che msg_key sia effettivamente uguale ai 128 bit centrali dell’hash SHA256 del messaggio decrittografato, preceduti da 32 byte presi dalla chiave condivisa .
Se il livello del messaggio è maggiore di quello supportato dal client, l’utente deve essere informato che la versione del client è obsoleta e invitato ad aggiornare.
Numeri di sequenza
È necessario interpretare tutti i messaggi nel loro ordine originale per proteggersi da possibili manipolazioni. Le chat segrete supportano uno speciale meccanismo per gestire i contatori seq_no indipendentemente dal server.
Tieni presente che il client deve supportare i numeri sequenziali nelle chat segrete per essere compatibile con i client Telegram ufficiali.
Invio di file crittografati
Tutti i file inviati alle chat segrete vengono crittografati con chiavi monouso 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 venga allegato all’esterno di un messaggio crittografato utilizzando il parametro file del metodo messages.sendEncryptedFile e che la chiave per la decifratura diretta venga inviata nel corpo del messaggio (il parametro key nei costruttori decryptedMessageMediaPhoto , decryptedMessageMediaVideo e decryptedMessageMediaFile .
Prima che un file venga inviato a una chat segreta, vengono calcolati 2 numeri casuali da 256 bit che fungeranno da chiave AES e vettore di inizializzazione utilizzati per crittografare il file. La crittografia AES-256 con estensione garble infinita (IGE) viene utilizzata in modo simile.
L’impronta digitale della chiave viene calcolata come segue:
- digest = md5(chiave + iv)
- impronta digitale = substr(digest, 0, 4) XOR substr(digest, 4, 4)
I contenuti crittografati di un file vengono archiviati sul server in modo molto simile a quelli di un file nelle chat cloud : pezzo per pezzo tramite chiamate a upload.saveFilePart .
Una successiva chiamata a messages.sendEncryptedFile assegnerà un identificatore al file archiviato e invierà l’indirizzo insieme al messaggio. Il destinatario riceverà un aggiornamento con encryptedMessage e il parametro file conterrà le 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 due volte lo stesso contenuto sul server.
Lavorare con una casella di aggiornamento
Le chat segrete sono associate a dispositivi specifici (o meglio a chiavi di autorizzazione ), non a utenti. Una casella di messaggio convenzionale, che usa pts per descrivere lo stato del client, non è adatta, perché è progettata per l’archiviazione dei messaggi a lungo termine e l’accesso ai messaggi da dispositivi diversi.
Una coda di messaggi temporanea aggiuntiva è stata introdotta come soluzione a questo problema. Quando viene inviato un aggiornamento riguardante 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.
Man mano che il numero di eventi aumenta, il valore di qts aumenta di 1 con ogni nuovo evento. Il valore iniziale potrebbe non essere (e non sarà) uguale a 0.
Il fatto che gli eventi dalla coda temporanea siano stati ricevuti e archiviati dal client viene riconosciuto esplicitamente da una chiamata al metodo messages.receivedQueue o implicitamente da una chiamata a updates.getDifference (il valore di qts passato, non lo stato finale). Tutti i messaggi riconosciuti come recapitati dal client, così come tutti i messaggi più vecchi di 7 giorni, possono (e saranno) eliminati dal server.
Dopo la revoca dell’autorizzazione, la coda degli 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 essere supportato dal client dall’altro lato di una chat segreta. Quando la chat segreta viene creata per la prima volta, questo valore dovrebbe essere inizializzato a 46. Questo valore del livello remoto deve sempre essere aggiornato immediatamente dopo aver ricevuto un pacchetto contenente informazioni di un livello superiore, ad esempio:
- qualsiasi messaggio di chat segreto contenente layer_no con layerdecryptedMessageLayer > =46, o
- un messaggio del servizio decryptedMessageActionNotifyLayer , racchiuso come se fosse il costruttore decryptedMessageService del livello obsoleto 8 (costruttore decryptedMessageService#aa48327d).
Notifica al client remoto del tuo livello locale
Per notificare al client remoto il tuo layer locale, il tuo client deve inviare un messaggio del decryptedMessageActionNotifyLayertipo . Questa notifica deve essere racchiusa in un costruttore di un layer appropriato.
Ci sono due casi in cui il client deve notificare al client remoto il suo livello locale:
- Non appena viene creata una nuova chat segreta, subito 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. Nota che questo è necessario solo quando si esegue l’aggiornamento 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).
(fonte)
Innovaformazione, scuola informatica specialistica promuove la cultura dello sviluppo software in sicurezza e della crittografia. Se siete un’azienda IT trovate l’elenco corsi sul nostro sito QUI.
INFO: info@innovaformazione.net – tel. 3471012275 (Dario Carrassi)