From e179f25fe93799041471adebbd6062b2072b13cf Mon Sep 17 00:00:00 2001 From: vvaltman Date: Fri, 29 Aug 2014 17:33:15 +0400 Subject: [PATCH] Added PFS support. Parallel auth_key generation. Small bug fixes --- append.tl | 2 + loop.c | 31 ++--- lua-tg.c | 6 + main.c | 9 +- mtproto-client.c | 340 ++++++++++++++++++++++++++++++++++++----------- mtproto-client.h | 3 +- mtproto-common.h | 3 + queries.c | 85 ++++++++++-- queries.h | 3 + tgl-layout.h | 11 ++ tgl.c | 6 +- tgl.h | 8 ++ updates.c | 1 + 13 files changed, 399 insertions(+), 109 deletions(-) diff --git a/append.tl b/append.tl index 0b5711b..0158358 100644 --- a/append.tl +++ b/append.tl @@ -1,2 +1,4 @@ decryptedMessageMediaVideoL12#4cee6ef3 thumb:bytes thumb_w:int thumb_h:int duration:int w:int h:int size:int key:bytes iv:bytes = DecryptedMessageMedia; decryptedMessageMediaAudioL12#6080758f duration:int size:int key:bytes iv:bytes = DecryptedMessageMedia; + +auth.bindTempAuthKey perm_auth_key_id:long nonce:long expires_at:int encrypted_message:string = Bool; diff --git a/loop.c b/loop.c index ade71a7..a31cc2b 100644 --- a/loop.c +++ b/loop.c @@ -226,6 +226,16 @@ int is_authorized (void) { return tgl_authorized_dc (cur_a_dc); } +int all_authorized (void) { + int i; + for (i = 0; i <= tgl_state.max_dc_num; i++) if (tgl_state.DC_list[i]) { + if (!tgl_authorized_dc (tgl_state.DC_list[i])) { + return 0; + } + } + return 1; +} + int config_got; int got_config (void) { @@ -491,25 +501,12 @@ int loop (void) { lua_binlog_end (); #endif update_prompt (); - - if (!tgl_authorized_dc (tgl_state.DC_working)) { - cur_a_dc = tgl_state.DC_working; - tgl_dc_authorize (tgl_state.DC_working); - net_loop (0, is_authorized); - } - - tgl_do_help_get_config (on_get_config, 0); - net_loop (0, got_config); - - if (verbosity >= E_DEBUG) { - logprintf ("DC_info: %d new DC got\n", new_dc_num); - } + + net_loop (0, all_authorized); int i; for (i = 0; i <= tgl_state.max_dc_num; i++) if (tgl_state.DC_list[i] && !tgl_authorized_dc (tgl_state.DC_list[i])) { - cur_a_dc = tgl_state.DC_list[i]; - tgl_dc_authorize (cur_a_dc); - net_loop (0, is_authorized); + assert (0); } if (!tgl_signed_dc (tgl_state.DC_working)) { @@ -610,7 +607,7 @@ int loop (void) { assert (tgl_signed_dc (tgl_state.DC_list[i])); } write_auth_file (); - + fflush (stdout); fflush (stderr); diff --git a/lua-tg.c b/lua-tg.c index 44decd7..5e1058c 100644 --- a/lua-tg.c +++ b/lua-tg.c @@ -477,6 +477,7 @@ enum lua_query_type { lq_load_document_thumb, lq_delete_msg, lq_restore_msg, + lq_accept_secret_chat }; struct lua_query_extra { @@ -1000,6 +1001,10 @@ void lua_do_all (void) { tgl_do_delete_msg ((long)lua_ptr[p + 1], lua_empty_cb, lua_ptr[p]); p += 2; break; + case lq_accept_secret_chat: + tgl_do_accept_encr_chat_request (lua_ptr[p + 1], lua_secret_chat_cb, lua_ptr[p]); + p += 2; + break; /* lq_delete_msg, lq_restore_msg, @@ -1076,6 +1081,7 @@ struct lua_function functions[] = { {"create_group_chat", lq_create_group_chat, { lfp_user, lfp_string, lfp_none }}, {"delete_msg", lq_delete_msg, { lfp_msg, lfp_none }}, {"restore_msg", lq_restore_msg, { lfp_positive_number, lfp_none }}, + {"accept_secret_chat", lq_accept_secret_chat, { lfp_secret_chat, lfp_none }}, { 0, 0, { lfp_none}} }; diff --git a/main.c b/main.c index 953622e..0e49d09 100644 --- a/main.c +++ b/main.c @@ -330,6 +330,13 @@ void parse_config (void) { strcpy (buf + l, "binlog_enabled"); config_lookup_bool (&conf, buf, &binlog_enabled); + + int pfs_enabled; + strcpy (buf + l, "pfs_enabled"); + config_lookup_bool (&conf, buf, &pfs_enabled); + if (pfs_enabled) { + tgl_enable_pfs (); + } if (binlog_enabled) { parse_config_val (&conf, &binlog_file_name, "binlog", BINLOG_FILE, config_directory); @@ -534,7 +541,7 @@ int main (int argc, char **argv) { running_for_first_time (); parse_config (); - tgl_set_rsa_key ("/etc/ " PROG_NAME "/server.pub"); + tgl_set_rsa_key ("/etc/" PROG_NAME "/server.pub"); tgl_set_rsa_key ("tg-server.pub"); diff --git a/mtproto-client.c b/mtproto-client.c index 4ca1357..66f5426 100644 --- a/mtproto-client.c +++ b/mtproto-client.c @@ -78,9 +78,6 @@ //int verbosity; static int auth_success; //static enum tgl_dc_state c_state; -static char nonce[256]; -static char new_nonce[256]; -static char server_nonce[256]; //extern int binlog_enabled; //extern int disable_auto_accept; //extern int allow_weak_random; @@ -122,7 +119,7 @@ static char Response[MAX_RESPONSE_SIZE]; */ #define TG_SERVER_PUBKEY_FILENAME "tg-server.pub" -static char *rsa_public_key_name; // = TG_SERVER_PUBKEY_FILENAME; +//static char *rsa_public_key_name; // = TG_SERVER_PUBKEY_FILENAME; static RSA *pubKey; static long long pk_fingerprint; @@ -140,7 +137,7 @@ static int rsa_load_public_key (const char *public_key_name) { return -1; } - vlogprintf (E_WARNING, "public key '%s' loaded successfully\n", rsa_public_key_name); + vlogprintf (E_WARNING, "public key '%s' loaded successfully\n", public_key_name); return 0; } @@ -235,17 +232,32 @@ static int send_req_pq_packet (struct connection *c) { struct tgl_dc *D = tgl_state.net_methods->get_dc (c); assert (D->state == st_init); - tglt_secure_random (nonce, 16); + tglt_secure_random (D->nonce, 16); unenc_msg_header.out_msg_id = 0; clear_packet (); out_int (CODE_req_pq); - out_ints ((int *)nonce, 4); + out_ints ((int *)D->nonce, 4); rpc_send_packet (c); D->state = st_reqpq_sent; return 1; } +static int send_req_pq_temp_packet (struct connection *c) { + struct tgl_dc *D = tgl_state.net_methods->get_dc (c); + assert (D->state == st_authorized); + + tglt_secure_random (D->nonce, 16); + unenc_msg_header.out_msg_id = 0; + clear_packet (); + out_int (CODE_req_pq); + out_ints ((int *)D->nonce, 4); + rpc_send_packet (c); + + D->state = st_reqpq_sent_temp; + return 1; +} + static unsigned long long gcd (unsigned long long a, unsigned long long b) { return b ? gcd (b, a % b) : a; @@ -253,7 +265,8 @@ static unsigned long long gcd (unsigned long long a, unsigned long long b) { //typedef unsigned int uint128_t __attribute__ ((mode(TI))); -static int process_respq_answer (struct connection *c, char *packet, int len) { +static int process_respq_answer (struct connection *c, char *packet, int len, int temp_key) { + struct tgl_dc *D = tgl_state.net_methods->get_dc (c); unsigned long long what; unsigned p1, p2; int i; @@ -263,8 +276,8 @@ static int process_respq_answer (struct connection *c, char *packet, int len) { assert (*(int *) (packet + 16) == len - 20); assert (!(len & 3)); assert (*(int *) (packet + 20) == CODE_resPQ); - assert (!memcmp (packet + 24, nonce, 16)); - memcpy (server_nonce, packet + 40, 16); + assert (!memcmp (packet + 24, D->nonce, 16)); + memcpy (D->server_nonce, packet + 40, 16); char *from = packet + 56; int clen = *from++; assert (clen <= 8); @@ -340,7 +353,7 @@ static int process_respq_answer (struct connection *c, char *packet, int len) { // create inner part (P_Q_inner_data) clear_packet (); packet_ptr += 5; - out_int (CODE_p_q_inner_data); + out_int (temp_key ? CODE_p_q_inner_data_temp : CODE_p_q_inner_data); out_cstring (packet + 57, clen); //out_int (0x0f01); // pq=15 @@ -372,18 +385,21 @@ static int process_respq_answer (struct connection *c, char *packet, int len) { //out_int (0x0301); // p=3 //out_int (0x0501); // q=5 - out_ints ((int *) nonce, 4); - out_ints ((int *) server_nonce, 4); - tglt_secure_random (new_nonce, 32); - out_ints ((int *) new_nonce, 8); + out_ints ((int *) D->nonce, 4); + out_ints ((int *) D->server_nonce, 4); + tglt_secure_random (D->new_nonce, 32); + out_ints ((int *) D->new_nonce, 8); + if (temp_key) { + out_int (tgl_state.temp_key_expire_time); + } sha1 ((unsigned char *) (packet_buffer + 5), (packet_ptr - packet_buffer - 5) * 4, (unsigned char *) packet_buffer); int l = encrypt_packet_buffer (); clear_packet (); out_int (CODE_req_DH_params); - out_ints ((int *) nonce, 4); - out_ints ((int *) server_nonce, 4); + out_ints ((int *) D->nonce, 4); + out_ints ((int *) D->server_nonce, 4); //out_int (0x0301); // p=3 //out_int (0x0501); // q=5 if (p1 < 256) { @@ -414,7 +430,7 @@ static int process_respq_answer (struct connection *c, char *packet, int len) { out_long (pk_fingerprint); out_cstring ((char *) encrypt_buffer, l); - tgl_state.net_methods->get_dc (c)->state = st_reqdh_sent; + D->state = temp_key ? st_reqdh_sent_temp : st_reqdh_sent; return rpc_send_packet (c); } @@ -515,7 +531,8 @@ int tglmp_check_g_bn (BIGNUM *p, BIGNUM *g) { return tglmp_check_g (s, g); } -static int process_dh_answer (struct connection *c, char *packet, int len) { +static int process_dh_answer (struct connection *c, char *packet, int len, int temp_key) { + struct tgl_dc *D = tgl_state.net_methods->get_dc (c); vlogprintf (E_DEBUG, "process_dh_answer(), len=%d\n", len); //if (len < 116) { // vlogprintf (E_ERROR, "%u * %u = %llu", p1, p2, what); @@ -525,9 +542,9 @@ static int process_dh_answer (struct connection *c, char *packet, int len) { assert (*(int *) (packet + 16) == len - 20); assert (!(len & 3)); assert (*(int *) (packet + 20) == (int)CODE_server_DH_params_ok); - assert (!memcmp (packet + 24, nonce, 16)); - assert (!memcmp (packet + 40, server_nonce, 16)); - tgl_init_aes_unauth (server_nonce, new_nonce, AES_DECRYPT); + assert (!memcmp (packet + 24, D->nonce, 16)); + assert (!memcmp (packet + 40, D->server_nonce, 16)); + tgl_init_aes_unauth (D->server_nonce, D->new_nonce, AES_DECRYPT); in_ptr = (int *)(packet + 56); in_end = (int *)(packet + len); int l = prefetch_strlen (); @@ -536,8 +553,8 @@ static int process_dh_answer (struct connection *c, char *packet, int len) { assert (in_ptr == in_end); assert (l >= 60); assert (decrypt_buffer[5] == (int)CODE_server_DH_inner_data); - assert (!memcmp (decrypt_buffer + 6, nonce, 16)); - assert (!memcmp (decrypt_buffer + 10, server_nonce, 16)); + assert (!memcmp (decrypt_buffer + 6, D->nonce, 16)); + assert (!memcmp (decrypt_buffer + 10, D->server_nonce, 16)); int g = decrypt_buffer[14]; in_ptr = decrypt_buffer + 15; in_end = decrypt_buffer + (l >> 2); @@ -556,7 +573,6 @@ static int process_dh_answer (struct connection *c, char *packet, int len) { assert (!memcmp (decrypt_buffer, sha1_buffer, 20)); assert ((char *) in_end - (char *) in_ptr < 16); - struct tgl_dc *D = tgl_state.net_methods->get_dc (c); D->server_time_delta = server_time - time (0); D->server_time_udelta = server_time - get_utime (CLOCK_MONOTONIC); //logprintf ( "server time is %d, delta = %d\n", server_time, server_time_delta); @@ -565,8 +581,8 @@ static int process_dh_answer (struct connection *c, char *packet, int len) { clear_packet (); packet_ptr += 5; out_int (CODE_client_DH_inner_data); - out_ints ((int *) nonce, 4); - out_ints ((int *) server_nonce, 4); + out_ints ((int *) D->nonce, 4); + out_ints ((int *) D->server_nonce, 4); out_long (0LL); BN_init (&dh_g); @@ -586,8 +602,8 @@ static int process_dh_answer (struct connection *c, char *packet, int len) { ensure (BN_mod_exp (&auth_key_num, &g_a, dh_power, &dh_prime, tgl_state.BN_ctx)); l = BN_num_bytes (&auth_key_num); assert (l >= 250 && l <= 256); - assert (BN_bn2bin (&auth_key_num, (unsigned char *)D->auth_key)); - memset (D->auth_key + l, 0, 256 - l); + assert (BN_bn2bin (&auth_key_num, (unsigned char *)(temp_key ? D->temp_auth_key : D->auth_key))); + memset (temp_key ? D->temp_auth_key + l : D->auth_key + l, 0, 256 - l); BN_free (dh_power); BN_free (&auth_key_num); BN_free (&dh_g); @@ -600,53 +616,103 @@ static int process_dh_answer (struct connection *c, char *packet, int len) { //hexdump ((char *)packet_buffer, (char *)packet_ptr); - l = encrypt_packet_buffer_aes_unauth (server_nonce, new_nonce); + l = encrypt_packet_buffer_aes_unauth (D->server_nonce, D->new_nonce); clear_packet (); out_int (CODE_set_client_DH_params); - out_ints ((int *) nonce, 4); - out_ints ((int *) server_nonce, 4); + out_ints ((int *) D->nonce, 4); + out_ints ((int *) D->server_nonce, 4); out_cstring ((char *) encrypt_buffer, l); - tgl_state.net_methods->get_dc (c)->state = st_client_dh_sent; + D->state = temp_key ? st_client_dh_sent_temp : st_client_dh_sent;; return rpc_send_packet (c); } +static void create_temp_auth_key (struct connection *c) { + send_req_pq_temp_packet (c); +} -static int process_auth_complete (struct connection *c UU, char *packet, int len) { +int tglmp_encrypt_inner_temp (struct connection *c, int *msg, int msg_ints, int useful, void *data, long long msg_id); +static long long generate_next_msg_id (struct tgl_dc *DC); +static long long msg_id_override; +static void mpc_on_get_config (void *extra, int success); +static int process_auth_complete (struct connection *c UU, char *packet, int len, int temp_key) { + struct tgl_dc *D = tgl_state.net_methods->get_dc (c); vlogprintf (E_DEBUG, "process_dh_answer(), len=%d\n", len); assert (len == 72); assert (!*(long long *) packet); assert (*(int *) (packet + 16) == len - 20); assert (!(len & 3)); assert (*(int *) (packet + 20) == CODE_dh_gen_ok); - assert (!memcmp (packet + 24, nonce, 16)); - assert (!memcmp (packet + 40, server_nonce, 16)); + assert (!memcmp (packet + 24, D->nonce, 16)); + assert (!memcmp (packet + 40, D->server_nonce, 16)); static unsigned char tmp[44], sha1_buffer[20]; - memcpy (tmp, new_nonce, 32); + memcpy (tmp, D->new_nonce, 32); tmp[32] = 1; //GET_DC(c)->auth_key_id = *(long long *)(sha1_buffer + 12); - struct tgl_dc *D = tgl_state.net_methods->get_dc (c); - bl_do_set_auth_key_id (D->id, (unsigned char *)D->auth_key); - sha1 ((unsigned char *)D->auth_key, 256, sha1_buffer); + if (!temp_key) { + bl_do_set_auth_key_id (D->id, (unsigned char *)D->auth_key); + sha1 ((unsigned char *)D->auth_key, 256, sha1_buffer); + } else { + sha1 ((unsigned char *)D->temp_auth_key, 256, sha1_buffer); + D->temp_auth_key_id = *(long long *)(sha1_buffer + 12); + } memcpy (tmp + 33, sha1_buffer, 8); sha1 (tmp, 41, sha1_buffer); assert (!memcmp (packet + 56, sha1_buffer + 4, 16)); - D->server_salt = *(long long *)server_nonce ^ *(long long *)new_nonce; + D->server_salt = *(long long *)D->server_nonce ^ *(long long *)D->new_nonce; //kprintf ("OK\n"); //c->status = conn_error; //sleep (1); - tgl_state.net_methods->get_dc (c)->state = st_authorized; + D->state = st_authorized; //return 1; vlogprintf (E_DEBUG, "Auth success\n"); auth_success ++; - D->flags |= 1; + if (temp_key) { + //D->flags |= 2; + + long long msg_id = generate_next_msg_id (D); + clear_packet (); + out_int (CODE_bind_auth_key_inner); + long long rand; + tglt_secure_random (&rand, 8); + out_long (rand); + out_long (D->temp_auth_key_id); + out_long (D->auth_key_id); + + struct tgl_session *S = tgl_state.net_methods->get_session (c); + if (!S->session_id) { + tglt_secure_random (&S->session_id, 8); + } + out_long (S->session_id); + int expires = time (0) + D->server_time_delta + tgl_state.temp_key_expire_time; + out_int (expires); + + static int data[1000]; + int len = tglmp_encrypt_inner_temp (c, packet_buffer, packet_ptr - packet_buffer, 0, data, msg_id); + msg_id_override = msg_id; + tgl_do_send_bind_temp_key (D, rand, expires, (void *)data, len, msg_id); + msg_id_override = 0; + } else { + D->flags |= 1; + if (tgl_state.enable_pfs) { + create_temp_auth_key (c); + } else { + D->temp_auth_key_id = D->auth_key_id; + memcpy (D->temp_auth_key, D->auth_key, 256); + D->flags |= 2; + if (!(D->flags & 4)) { + tgl_do_help_get_config_dc (D, mpc_on_get_config, D); + } + } + } + //write_auth_file (); return 1; @@ -681,8 +747,11 @@ static long long generate_next_msg_id (struct tgl_dc *DC) { static void init_enc_msg (struct tgl_session *S, int useful) { struct tgl_dc *DC = S->dc; - assert (DC->auth_key_id); - enc_msg.auth_key_id = DC->auth_key_id; + assert (DC->state == st_authorized); + //assert (DC->flags & 2); + assert (DC->temp_auth_key_id); + vlogprintf (E_DEBUG, "temp_auth_key_id = 0x%016llx, auth_key_id = 0x%016llx\n", DC->temp_auth_key_id, DC->auth_key_id); + enc_msg.auth_key_id = DC->temp_auth_key_id; // assert (DC->server_salt); enc_msg.server_salt = DC->server_salt; if (!S->session_id) { @@ -690,7 +759,7 @@ static void init_enc_msg (struct tgl_session *S, int useful) { } enc_msg.session_id = S->session_id; //enc_msg.auth_key_id2 = auth_key_id; - enc_msg.msg_id = generate_next_msg_id (DC); + enc_msg.msg_id = msg_id_override ? msg_id_override : generate_next_msg_id (DC); //enc_msg.msg_id -= 0x10000000LL * (lrand48 () & 15); //kprintf ("message id %016llx\n", enc_msg.msg_id); enc_msg.seq_no = S->seq_no; @@ -700,7 +769,16 @@ static void init_enc_msg (struct tgl_session *S, int useful) { S->seq_no += 2; }; -static int aes_encrypt_message (struct tgl_dc *DC, struct encrypted_message *enc) { +static void init_enc_msg_inner_temp (struct tgl_dc *DC, long long msg_id) { + enc_msg.auth_key_id = DC->auth_key_id; + tglt_secure_random (&enc_msg.server_salt, 8); + tglt_secure_random (&enc_msg.session_id, 8); + enc_msg.msg_id = msg_id; + enc_msg.seq_no = 0; +}; + + +static int aes_encrypt_message (char *key, struct encrypted_message *enc) { unsigned char sha1_buffer[20]; const int MINSZ = offsetof (struct encrypted_message, message); const int UNENCSZ = offsetof (struct encrypted_message, server_salt); @@ -710,13 +788,16 @@ static int aes_encrypt_message (struct tgl_dc *DC, struct encrypted_message *enc //printf ("enc_len is %d\n", enc_len); vlogprintf (E_DEBUG, "sending message with sha1 %08x\n", *(int *)sha1_buffer); memcpy (enc->msg_key, sha1_buffer + 4, 16); - tgl_init_aes_auth (DC->auth_key, enc->msg_key, AES_ENCRYPT); + tgl_init_aes_auth (key, enc->msg_key, AES_ENCRYPT); //hexdump ((char *)enc, (char *)enc + enc_len + 24); return tgl_pad_aes_encrypt ((char *) &enc->server_salt, enc_len, (char *) &enc->server_salt, MAX_MESSAGE_INTS * 4 + (MINSZ - UNENCSZ)); } -long long tglmp_encrypt_send_message (struct connection *c, int *msg, int msg_ints, int useful) { +long long tglmp_encrypt_send_message (struct connection *c, int *msg, int msg_ints, int flags) { struct tgl_dc *DC = tgl_state.net_methods->get_dc (c); + if (!(DC->flags & 4) && !(flags & 2)) { + return generate_next_msg_id (DC); + } struct tgl_session *S = tgl_state.net_methods->get_session (c); assert (S); @@ -732,17 +813,41 @@ long long tglmp_encrypt_send_message (struct connection *c, int *msg, int msg_in return -1; } } - init_enc_msg (S, useful); + init_enc_msg (S, flags & 1); //hexdump ((char *)msg, (char *)msg + (msg_ints * 4)); - int l = aes_encrypt_message (DC, &enc_msg); + int l = aes_encrypt_message (DC->temp_auth_key, &enc_msg); //hexdump ((char *)&enc_msg, (char *)&enc_msg + l + 24); assert (l > 0); + vlogprintf (E_DEBUG, "Sending message to DC%d: %s:%d with key_id=%lld\n", DC->id, DC->ip, DC->port, enc_msg.auth_key_id); rpc_send_message (c, &enc_msg, l + UNENCSZ); + return client_last_msg_id; } +int tglmp_encrypt_inner_temp (struct connection *c, int *msg, int msg_ints, int useful, void *data, long long msg_id) { + struct tgl_dc *DC = tgl_state.net_methods->get_dc (c); + struct tgl_session *S = tgl_state.net_methods->get_session (c); + assert (S); + + const int UNENCSZ = offsetof (struct encrypted_message, server_salt); + if (msg_ints <= 0 || msg_ints > MAX_MESSAGE_INTS - 4) { + return -1; + } + memcpy (enc_msg.message, msg, msg_ints * 4); + enc_msg.msg_len = msg_ints * 4; + + init_enc_msg_inner_temp (DC, msg_id); + + int l = aes_encrypt_message (DC->auth_key, &enc_msg); + assert (l > 0); + //rpc_send_message (c, &enc_msg, l + UNENCSZ); + memcpy (data, &enc_msg, l + UNENCSZ); + + return l + UNENCSZ; +} + static int good_messages; static void rpc_execute_answer (struct connection *c, long long msg_id UU); @@ -930,9 +1035,19 @@ static int process_rpc_message (struct connection *c UU, struct encrypted_messag vlogprintf (E_DEBUG, "process_rpc_message(), len=%d\n", len); assert (len >= MINSZ && (len & 15) == (UNENCSZ & 15)); struct tgl_dc *DC = tgl_state.net_methods->get_dc (c); - assert (enc->auth_key_id == DC->auth_key_id); - assert (DC->auth_key_id); - tgl_init_aes_auth (DC->auth_key + 8, enc->msg_key, AES_DECRYPT); + if (enc->auth_key_id != DC->temp_auth_key_id && enc->auth_key_id != DC->auth_key_id) { + vlogprintf (E_ERROR, "received msg from dc %d with auth_key_id %lld (perm_auth_key_id %lld temp_auth_key_id %lld)\n", + DC->id, enc->auth_key_id, DC->auth_key_id, DC->temp_auth_key_id); + } + if (enc->auth_key_id == DC->temp_auth_key_id) { + assert (enc->auth_key_id == DC->temp_auth_key_id); + assert (DC->temp_auth_key_id); + tgl_init_aes_auth (DC->temp_auth_key + 8, enc->msg_key, AES_DECRYPT); + } else { + assert (enc->auth_key_id == DC->auth_key_id); + assert (DC->auth_key_id); + tgl_init_aes_auth (DC->auth_key + 8, enc->msg_key, AES_DECRYPT); + } int l = tgl_pad_aes_decrypt ((char *)&enc->server_salt, len - UNENCSZ, (char *)&enc->server_salt, len - UNENCSZ); assert (l == len - UNENCSZ); //assert (enc->auth_key_id2 == enc->auth_key_id); @@ -945,6 +1060,7 @@ static int process_rpc_message (struct connection *c UU, struct encrypted_messag DC->server_salt = enc->server_salt; //write_auth_file (); } + int this_server_time = enc->msg_id >> 32LL; if (!DC->server_time_delta) { @@ -972,6 +1088,20 @@ static int process_rpc_message (struct connection *c UU, struct encrypted_messag in_ptr = enc->message; in_end = in_ptr + (enc->msg_len / 4); + + /*{ + assert (len <= 10000); + static char s[1 << 20]; + int p = 0; + int i; + //static int buf[10000]; + //assert (tgl_state.net_methods->read_in_lookup (c, buf, len) == len); + + for (i = 0; i < in_end - in_ptr; i++) { + p += sprintf (s + p, "%08x ", *(int *)(in_ptr + i)); + } + vlogprintf (E_DEBUG, "%s\n", s); + }*/ struct tgl_session *S = tgl_state.net_methods->get_session (c); if (enc->msg_id & 1) { @@ -985,8 +1115,23 @@ static int process_rpc_message (struct connection *c UU, struct encrypted_messag static int rpc_execute (struct connection *c, int op, int len) { - vlogprintf (E_DEBUG, "outbound rpc connection from dc #%d : received rpc answer %d with %d content bytes\n", tgl_state.net_methods->get_dc(c)->id, op, len); -/* if (op < 0) { + struct tgl_dc *D = tgl_state.net_methods->get_dc (c); + vlogprintf (E_DEBUG, "outbound rpc connection from dc #%d (%s:%d) : received rpc answer %d with %d content bytes\n", D->id, D->ip, D->port, op, len); + + /*{ + assert (len <= 10000); + static char s[1 << 20]; + int p = 0; + int i; + static int buf[10000]; + assert (tgl_state.net_methods->read_in_lookup (c, buf, len) == len); + + for (i = 0; i < len / 4; i++) { + p += sprintf (s + p, "%08x ", *(int *)(buf + i)); + } + vlogprintf (E_DEBUG, "%s\n", s); + }*/ + /* if (op < 0) { assert (tgl_state.net_methods->read_in (c, Response, Response_len) == Response_len); return 0; }*/ @@ -1005,27 +1150,26 @@ static int rpc_execute (struct connection *c, int op, int len) { #if !defined(__MACH__) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined (__CYGWIN__) // setsockopt (c->fd, IPPROTO_TCP, TCP_QUICKACK, (int[]){0}, 4); #endif - struct tgl_dc *D = tgl_state.net_methods->get_dc (c); int o = D->state; - if (D->flags & 1) { o = st_authorized;} + //if (D->flags & 1) { o = st_authorized;} switch (o) { case st_reqpq_sent: - process_respq_answer (c, Response/* + 8*/, Response_len/* - 12*/); -#if !defined(__MACH__) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined (__CYGWIN__) -// setsockopt (c->fd, IPPROTO_TCP, TCP_QUICKACK, (int[]){0}, 4); -#endif + process_respq_answer (c, Response/* + 8*/, Response_len/* - 12*/, 0); return 0; case st_reqdh_sent: - process_dh_answer (c, Response/* + 8*/, Response_len/* - 12*/); -#if !defined(__MACH__) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined (__CYGWIN__) -// setsockopt (c->fd, IPPROTO_TCP, TCP_QUICKACK, (int[]){0}, 4); -#endif + process_dh_answer (c, Response/* + 8*/, Response_len/* - 12*/, 0); return 0; case st_client_dh_sent: - process_auth_complete (c, Response/* + 8*/, Response_len/* - 12*/); -#if !defined(__MACH__) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined (__CYGWIN__) -// setsockopt (c->fd, IPPROTO_TCP, TCP_QUICKACK, (int[]){0}, 4); -#endif + process_auth_complete (c, Response/* + 8*/, Response_len/* - 12*/, 0); + return 0; + case st_reqpq_sent_temp: + process_respq_answer (c, Response/* + 8*/, Response_len/* - 12*/, 1); + return 0; + case st_reqdh_sent_temp: + process_dh_answer (c, Response/* + 8*/, Response_len/* - 12*/, 1); + return 0; + case st_client_dh_sent_temp: + process_auth_complete (c, Response/* + 8*/, Response_len/* - 12*/, 1); return 0; case st_authorized: if (op < 0 && op >= -999) { @@ -1033,9 +1177,6 @@ static int rpc_execute (struct connection *c, int op, int len) { } else { process_rpc_message (c, (void *)(Response/* + 8*/), Response_len/* - 12*/); } -#if !defined(__MACH__) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined (__CYGWIN__) -// setsockopt (c->fd, IPPROTO_TCP, TCP_QUICKACK, (int[]){0}, 4); -#endif return 0; default: vlogprintf (E_ERROR, "fatal: cannot receive answer in state %d\n", D->state); @@ -1051,23 +1192,37 @@ static int tc_close (struct connection *c, int who) { return 0; } +static void mpc_on_get_config (void *extra, int success) { + assert (success); + struct tgl_dc *D = extra; + D->flags |= 4; +} + static int tc_becomes_ready (struct connection *c) { vlogprintf (E_DEBUG, "outbound rpc connection from dc #%d becomed ready\n", tgl_state.net_methods->get_dc(c)->id); //char byte = 0xef; //assert (tgl_state.net_methods->write_out (c, &byte, 1) == 1); //tgl_state.net_methods->flush_out (c); -#if !defined(__MACH__) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined (__CYGWIN__) -// setsockopt (c->fd, IPPROTO_TCP, TCP_QUICKACK, (int[]){0}, 4); -#endif struct tgl_dc *D = tgl_state.net_methods->get_dc (c); + if (D->flags & 1) { D->state = st_authorized; } int o = D->state; - if (D->flags & 1) { o = st_authorized; } + if (o == st_authorized && !tgl_state.enable_pfs) { + D->temp_auth_key_id = D->auth_key_id; + memcpy (D->temp_auth_key, D->auth_key, 256); + D->flags |= 2; + } switch (o) { case st_init: send_req_pq_packet (c); break; case st_authorized: + if (!(D->flags & 2)) { + assert (!D->temp_auth_key_id); + create_temp_auth_key (c); + } else if (!(D->flags & 4)) { + tgl_do_help_get_config_dc (D, mpc_on_get_config, D); + } break; default: vlogprintf (E_DEBUG, "c_state = %d\n", D->state); @@ -1158,6 +1313,11 @@ void tgln_insert_msg_id (struct tgl_session *S, long long id) { //extern struct tgl_dc *DC_list[]; + +static void regen_temp_key_gw (evutil_socket_t fd, short what, void *arg) { + tglmp_regenerate_temp_auth_key (arg); +} + struct tgl_dc *tglmp_alloc_dc (int id, char *ip, int port UU) { assert (!tgl_state.DC_list[id]); struct tgl_dc *DC = talloc0 (sizeof (*DC)); @@ -1168,6 +1328,9 @@ struct tgl_dc *tglmp_alloc_dc (int id, char *ip, int port UU) { if (id > tgl_state.max_dc_num) { tgl_state.max_dc_num = id; } + DC->ev = evtimer_new (tgl_state.ev_base, regen_temp_key_gw, DC); + static struct timeval p; + event_add (DC->ev, &p); return DC; } @@ -1211,3 +1374,26 @@ void tgl_dc_iterator_ex (void (*iterator)(struct tgl_dc *DC, void *extra), void iterator (tgl_state.DC_list[i], extra); } } + + +void tglmp_regenerate_temp_auth_key (struct tgl_dc *D) { + D->flags &= ~6; + D->temp_auth_key_id = 0; + memset (D->temp_auth_key, 0, 256); + + if (!D->sessions[0]) { + tgl_dc_authorize (D); + return; + } + + struct tgl_session *S = D->sessions[0]; + tglt_secure_random (&S->session_id, 8); + S->seq_no = 0; + + event_del (S->ev); + S->ack_tree = tree_clear_long (S->ack_tree); + + if (S->c) { + create_temp_auth_key (S->c); + } +} diff --git a/mtproto-client.h b/mtproto-client.h index eb4d4cf..bf4fd47 100644 --- a/mtproto-client.h +++ b/mtproto-client.h @@ -45,11 +45,12 @@ struct tgl_dc; struct connection; -long long tglmp_encrypt_send_message (struct connection *c, int *msg, int msg_ints, int useful); +long long tglmp_encrypt_send_message (struct connection *c, int *msg, int msg_ints, int flags); void tglmp_dc_create_session (struct tgl_dc *DC); int tglmp_check_g (unsigned char p[256], BIGNUM *g); int tglmp_check_DH_params (BIGNUM *p, int g); struct tgl_dc *tglmp_alloc_dc (int id, char *ip, int port); +void tglmp_regenerate_temp_auth_key (struct tgl_dc *D); void tgln_insert_msg_id (struct tgl_session *S, long long id); void tglmp_on_start (void); diff --git a/mtproto-common.h b/mtproto-common.h index b74c66d..7ccee61 100644 --- a/mtproto-common.h +++ b/mtproto-common.h @@ -39,6 +39,7 @@ #define CODE_resPQ 0x05162463 #define CODE_req_DH_params 0xd712e4be #define CODE_p_q_inner_data 0x83c95aec +#define CODE_p_q_inner_data_temp 0x3c6a84d4 #define CODE_server_DH_inner_data 0xb5890dba #define CODE_server_DH_params_fail 0x79cb045d #define CODE_server_DH_params_ok 0xd0e8075c @@ -48,6 +49,8 @@ #define CODE_dh_gen_retry 0x46dc1fb9 #define CODE_dh_gen_fail 0xa69dae02 +#define CODE_bind_auth_key_inner 0x75a3f765 + /* service messages */ #define CODE_rpc_result 0xf35c6d01 #define CODE_rpc_error 0x2144ca19 diff --git a/queries.c b/queries.c index 0892700..d19213b 100644 --- a/queries.c +++ b/queries.c @@ -101,15 +101,23 @@ static int alarm_query (struct query *q) { return 0; }*/ - clear_packet (); - out_int (CODE_msg_container); - out_int (1); - out_long (q->msg_id); - out_int (q->seq_no); - out_int (4 * q->data_len); - out_ints (q->data, q->data_len); + if (q->session->session_id == q->session_id) { + clear_packet (); + out_int (CODE_msg_container); + out_int (1); + out_long (q->msg_id); + out_int (q->seq_no); + out_int (4 * q->data_len); + out_ints (q->data, q->data_len); - tglmp_encrypt_send_message (q->session->c, packet_buffer, packet_ptr - packet_buffer, 0); + tglmp_encrypt_send_message (q->session->c, packet_buffer, packet_ptr - packet_buffer, q->flags & QUERY_FORCE_SEND); + } else { + q->msg_id = tglmp_encrypt_send_message (q->session->c, q->data, q->data_len, (q->flags & QUERY_FORCE_SEND) | 1); + q->session_id = q->session->session_id; + if (!(q->session->dc->flags & 4) && !(q->flags & QUERY_FORCE_SEND)) { + q->session_id = 0; + } + } return 0; } @@ -126,7 +134,7 @@ static void alarm_query_gateway (evutil_socket_t fd, short what, void *arg) { } -struct query *tglq_send_query (struct tgl_dc *DC, int ints, void *data, struct query_methods *methods, void *extra, void *callback, void *callback_extra) { +struct query *tglq_send_query_ex (struct tgl_dc *DC, int ints, void *data, struct query_methods *methods, void *extra, void *callback, void *callback_extra, int flags) { assert (DC); assert (DC->auth_key_id); if (!DC->sessions[0]) { @@ -137,12 +145,17 @@ struct query *tglq_send_query (struct tgl_dc *DC, int ints, void *data, struct q q->data_len = ints; q->data = talloc (4 * ints); memcpy (q->data, data, 4 * ints); - q->msg_id = tglmp_encrypt_send_message (DC->sessions[0]->c, data, ints, 1); + q->msg_id = tglmp_encrypt_send_message (DC->sessions[0]->c, data, ints, 1 | (flags & QUERY_FORCE_SEND)); q->session = DC->sessions[0]; - q->seq_no = DC->sessions[0]->seq_no - 1; + q->seq_no = q->session->seq_no - 1; + q->session_id = q->session->session_id; + if (!(DC->flags & 4) && !(flags & QUERY_FORCE_SEND)) { + q->session_id = 0; + } vlogprintf (E_DEBUG, "Msg_id is %lld %p\n", q->msg_id, q); q->methods = methods; q->DC = DC; + q->flags = flags & QUERY_FORCE_SEND; if (queries_tree) { vlogprintf (E_DEBUG + 2, "%lld %lld\n", q->msg_id, queries_tree->x->msg_id); } @@ -164,6 +177,10 @@ struct query *tglq_send_query (struct tgl_dc *DC, int ints, void *data, struct q return q; } +struct query *tglq_send_query (struct tgl_dc *DC, int ints, void *data, struct query_methods *methods, void *extra, void *callback, void *callback_extra) { + return tglq_send_query_ex (DC, ints, data, methods, extra, callback, callback_extra, 0); +} + static int fail_on_error (struct query *q UU, int error_code UU, int l UU, char *error UU) { fprintf (stderr, "error #%d: %.*s\n", error_code, l, error); assert (0); @@ -364,6 +381,13 @@ void tgl_do_help_get_config (void (*callback)(void *, int), void *callback_extra out_int (CODE_help_get_config); tglq_send_query (tgl_state.DC_working, packet_ptr - packet_buffer, packet_buffer, &help_get_config_methods, 0, callback, callback_extra); } + +void tgl_do_help_get_config_dc (struct tgl_dc *D, void (*callback)(void *, int), void *callback_extra) { + clear_packet (); + tgl_do_insert_header (); + out_int (CODE_help_get_config); + tglq_send_query_ex (D, packet_ptr - packet_buffer, packet_buffer, &help_get_config_methods, 0, callback, callback_extra, 2); +} /* }}} */ /* {{{ Send code */ @@ -2821,7 +2845,8 @@ static int get_difference_on_answer (struct query *q UU) { bl_do_set_date (fetch_int ()); bl_do_set_seq (fetch_int ()); //difference_got = 1; - + + vlogprintf (E_DEBUG, "Empty difference. Seq = %d\n", tgl_state.seq); if (q->callback) { ((void (*)(void *, int))q->callback) (q->callback_extra, 1); } @@ -2862,6 +2887,7 @@ static int get_difference_on_answer (struct query *q UU) { bl_do_set_date (fetch_int ()); if (x == CODE_updates_difference) { bl_do_set_seq (fetch_int ()); + vlogprintf (E_DEBUG, "Difference end. New seq = %d\n", tgl_state.seq); } else { fetch_int (); } @@ -3172,6 +3198,41 @@ void tgl_do_restore_msg (long long id, void (*callback)(void *callback_extra, in } /* }}} */ +static void set_flag_4 (void *_D, int success) { + struct tgl_dc *D = _D; + assert (success); + D->flags |= 4; + + static struct timeval ptimeout; + ptimeout.tv_sec = tgl_state.temp_key_expire_time * 0.9; + event_add (D->ev, &ptimeout); +} + +static int send_bind_temp_on_answer (struct query *q UU) { + assert (fetch_int () == (int)CODE_bool_true); + struct tgl_dc *D = q->extra; + D->flags |= 2; + tgl_do_help_get_config_dc (D, set_flag_4, D); + vlogprintf (E_DEBUG, "Bind successful in dc %d\n", D->id); + return 0; +} + +static struct query_methods send_bind_temp_methods = { + .on_answer = send_bind_temp_on_answer, + .type = TYPE_TO_PARAM (bool) +}; + +void tgl_do_send_bind_temp_key (struct tgl_dc *D, long long nonce, int expires_at, void *data, int len, long long msg_id) { + clear_packet (); + out_int (CODE_auth_bind_temp_auth_key); + out_long (D->auth_key_id); + out_long (nonce); + out_int (expires_at); + out_cstring (data, len); + struct query *q = tglq_send_query_ex (D, packet_ptr - packet_buffer, packet_buffer, &send_bind_temp_methods, D, 0, 0, 2); + assert (q->msg_id == msg_id); +} + static int update_status_on_answer (struct query *q UU) { fetch_bool (); diff --git a/queries.h b/queries.h index 407b34b..7bf746f 100644 --- a/queries.h +++ b/queries.h @@ -27,6 +27,7 @@ struct event; #define QUERY_ACK_RECEIVED 1 +#define QUERY_FORCE_SEND 2 struct query; struct query_methods { @@ -41,6 +42,7 @@ struct query { int data_len; int flags; int seq_no; + long long session_id; void *data; struct query_methods *methods; struct event *ev; @@ -65,6 +67,7 @@ void work_timers (void); double get_double_time (void); +void tgl_do_send_bind_temp_key (struct tgl_dc *D, long long nonce, int expires_at, void *data, int len, long long msg_id); // For binlog diff --git a/tgl-layout.h b/tgl-layout.h index 5cd9800..b903f72 100644 --- a/tgl-layout.h +++ b/tgl-layout.h @@ -46,6 +46,10 @@ enum tgl_dc_state { st_reqpq_sent, st_reqdh_sent, st_client_dh_sent, + st_init_temp, + st_reqpq_sent_temp, + st_reqdh_sent_temp, + st_client_dh_sent_temp, st_authorized, st_error }; @@ -71,8 +75,15 @@ struct tgl_dc { char *user; struct tgl_session *sessions[MAX_DC_SESSIONS]; char auth_key[256]; + char temp_auth_key[256]; + char nonce[256]; + char new_nonce[256]; + char server_nonce[256]; long long auth_key_id; + long long temp_auth_key_id; + long long server_salt; + struct event *ev; int server_time_delta; double server_time_udelta; diff --git a/tgl.c b/tgl.c index c6bc345..5bc157b 100644 --- a/tgl.c +++ b/tgl.c @@ -69,12 +69,16 @@ void tgl_init (void) { if (!tgl_state.callback.create_print_name) { tgl_state.callback.create_print_name = tgls_default_create_print_name; } + if (!tgl_state.temp_key_expire_time) { + tgl_state.temp_key_expire_time = 60; //100000; + } + tglmp_on_start (); } int tgl_authorized_dc (struct tgl_dc *DC) { assert (DC); - return DC->auth_key_id; + return (DC->flags & 4) != 0;//DC->auth_key_id; } int tgl_signed_dc (struct tgl_dc *DC) { diff --git a/tgl.h b/tgl.h index 2444a89..119a94e 100644 --- a/tgl.h +++ b/tgl.h @@ -136,6 +136,8 @@ struct tgl_state { struct tgl_dc *DC_working; int max_dc_num; int dc_working_num; + int enable_pfs; + int temp_key_expire_time; long long cur_uploading_bytes; long long cur_uploaded_bytes; @@ -219,6 +221,10 @@ static inline void tgl_set_verbosity (int val) { tgl_state.verbosity = val; } +static inline void tgl_enable_pfs (void) { + tgl_state.enable_pfs = 1; +} + static inline void tgl_set_test_mode (void) { tgl_state.test_mode ++; } @@ -267,6 +273,8 @@ void tgl_do_create_group_chat (tgl_peer_id_t id, char *chat_topic, void (*callba void tgl_do_delete_msg (long long id, void (*callback)(void *callback_extra, int success), void *callback_extra); void tgl_do_restore_msg (long long id, void (*callback)(void *callback_extra, int success), void *callback_extra); void tgl_do_update_status (int online, void (*callback)(void *callback_extra, int success), void *callback_extra); +void tgl_do_help_get_config_dc (struct tgl_dc *D, void (*callback)(void *, int), void *callback_extra); + void tgl_do_visualize_key (tgl_peer_id_t id, unsigned char buf[16]); diff --git a/updates.c b/updates.c index ab07b62..34511e1 100644 --- a/updates.c +++ b/updates.c @@ -427,6 +427,7 @@ static int do_skip_seq (int seq) { } if (seq > tgl_state.seq + 1) { vlogprintf (E_NOTICE, "Hole in seq (seq = %d, cur_seq = %d)\n", seq, tgl_state.seq); + //vlogprintf (E_NOTICE, "lock_diff = %s\n", (tgl_state.locks & TGL_LOCK_DIFF) ? "true" : "false"); tgl_do_get_difference (0, 0, 0); return -1; }