diff options
Diffstat (limited to 'protocols/Telegram/src/tgl/mtproto-common.c')
-rw-r--r-- | protocols/Telegram/src/tgl/mtproto-common.c | 475 |
1 files changed, 475 insertions, 0 deletions
diff --git a/protocols/Telegram/src/tgl/mtproto-common.c b/protocols/Telegram/src/tgl/mtproto-common.c new file mode 100644 index 0000000000..32a9597639 --- /dev/null +++ b/protocols/Telegram/src/tgl/mtproto-common.c @@ -0,0 +1,475 @@ +/* + This file is part of tgl-library + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + Copyright Nikolay Durov, Andrey Lopatin 2012-2013 + Vitaly Valtman 2013-2015 +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define _FILE_OFFSET_BITS 64 + +#include <assert.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#ifdef _MSC_VER +#include <io.h> +#include <stdint.h> +#include <string.h> +#include <intrin.h> +#include <process.h> +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#undef WIN32_LEAN_AND_MEAN +#include <tlhelp32.h> +#include <sys/stat.h> +#include "tgl.h" +#elif defined(__MINGW32__) +#include "tgl.h" ++#include <winsock2.h> +#include <unistd.h> +#else +#include <unistd.h> +#include <netdb.h> +#endif +#include <fcntl.h> +#include <share.h> +#include <sys/types.h> +#include <openssl/bn.h> +#include <openssl/rand.h> +#include <openssl/pem.h> +#include <openssl/aes.h> +#include <openssl/sha.h> +#include <openssl/rand.h> + +#include "mtproto-common.h" +#include "tools.h" + +#ifdef __MACH__ +#include <mach/clock.h> +#include <mach/mach.h> +#endif + + +static int __packet_buffer[PACKET_BUFFER_SIZE + 16]; +int *tgl_packet_ptr; +int *tgl_packet_buffer = __packet_buffer + 16; + +static long long rsa_encrypted_chunks, rsa_decrypted_chunks; + +//int verbosity; + +static int get_random_bytes (struct tgl_state *TLS, unsigned char *buf, int n) { +#if defined(WIN32) || defined(_WIN32) + if (RAND_bytes(buf, n) != 1) + return 0; + + vlogprintf(E_DEBUG, "added %d bytes of real entropy to secure random numbers seed\n", n); + *(long *)buf ^= lrand48(); + srand48(*(long *)buf); + return n; +#else + int r = 0, h = open ("/dev/random", O_RDONLY | O_NONBLOCK); + if (h >= 0) { + r = read (h, buf, n); + if (r > 0) { + vlogprintf (E_DEBUG, "added %d bytes of real entropy to secure random numbers seed\n", r); + } else { + r = 0; + } + close (h); + } + + if (r < n) { + h = open ("/dev/urandom", O_RDONLY); + if (h < 0) { + return r; + } + int s = read (h, buf + r, n - r); + close (h); + if (s > 0) { + r += s; + } + } + + if (r >= (int) sizeof (long)) { + *(long *)buf ^= lrand48 (); + srand48 (*(long *)buf); + } + + return r; +#endif +} + + +/* RDTSC */ +#ifdef _MSC_VER +#define HAVE_RDTSC +#pragma intrinsic(__rdtsc) +static __inline unsigned long long rdtsc(void) { + return __rdtsc(); +} + +inline DWORD getppid() { + HANDLE hSnapshot = INVALID_HANDLE_VALUE; + PROCESSENTRY32 pe32; + DWORD ppid = 0, pid = GetCurrentProcessId(); + + hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + __try { + if (hSnapshot == INVALID_HANDLE_VALUE) __leave; + ZeroMemory(&pe32, sizeof(pe32)); + pe32.dwSize = sizeof(pe32); + if (!Process32First(hSnapshot, &pe32)) __leave; + + do { + if (pe32.th32ProcessID == pid) { + ppid = pe32.th32ParentProcessID; + break; + } + } while (Process32Next(hSnapshot, &pe32)); + + } + __finally { + if (hSnapshot != INVALID_HANDLE_VALUE) CloseHandle(hSnapshot); + } + return ppid; +} + +#elif defined(__i386__) +#define HAVE_RDTSC +static __inline__ unsigned long long rdtsc (void) { + unsigned long long int x; + __asm__ volatile ("rdtsc" : "=A" (x)); + return x; +} +#elif defined(__x86_64__) +#define HAVE_RDTSC +static __inline__ unsigned long long rdtsc (void) { + unsigned hi, lo; + __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi)); + return ((unsigned long long) lo) | (((unsigned long long) hi) << 32); +} +#endif + +void tgl_prng_seed (struct tgl_state *TLS, const char *password_filename, int password_length) { + struct timespec T; + tgl_my_clock_gettime (CLOCK_REALTIME, &T); + RAND_add (&T, sizeof (T), 4.0); +#ifdef HAVE_RDTSC + unsigned long long r = rdtsc (); + RAND_add (&r, 8, 4.0); +#endif +#if defined(_MSC_VER) + DWORD p = GetCurrentProcessId (); +#else + unsigned short p = getpid (); +#endif + RAND_add (&p, sizeof (p), 0.0); + p = getppid (); + RAND_add (&p, sizeof (p), 0.0); + unsigned char rb[32]; + int s = get_random_bytes (TLS, rb, 32); + if (s > 0) { + RAND_add (rb, s, s); + } + memset (rb, 0, sizeof (rb)); + if (password_filename && password_length > 0) { +#if defined(_MSC_VER) && _MSC_VER >= 1400 + int fd = 0; + errno_t err = _sopen_s(&fd, password_filename, _O_RDONLY | _O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE); + if (err != 0) { + vlogprintf(E_WARNING, "Warning: fail to open password file - \"%s\", %s.\n", password_filename, GetErrnoStr (errno)); +#elif defined(WIN32) || defined(_WIN32) + int fd = open(password_filename, O_RDONLY | O_BINARY); + if (fd < 0) { + vlogprintf (E_WARNING, "Warning: fail to open password file - \"%s\", %s.\n", password_filename, GetErrnoStr (errno)); +#else + int fd = open (password_filename, O_RDONLY); + if (fd < 0) { + vlogprintf (E_WARNING, "Warning: fail to open password file - \"%s\", %m.\n", password_filename); +#endif + } else { + unsigned char *a = talloc0 (password_length); + int l = read (fd, a, password_length); + if (l < 0) { +#if defined(WIN32) || defined(_WIN32) + vlogprintf (E_WARNING, "Warning: fail to read password file - \"%s\", %s.\n", password_filename, GetErrnoStr (errno)); +#else + vlogprintf (E_WARNING, "Warning: fail to read password file - \"%s\", %m.\n", password_filename); +#endif + } else { + vlogprintf (E_DEBUG, "read %d bytes from password file.\n", l); + RAND_add (a, l, l); + } + close (fd); + tfree_secure (a, password_length); + } + } + TLS->BN_ctx = BN_CTX_new (); + ensure_ptr (TLS->BN_ctx); +} + +int tgl_serialize_bignum (BIGNUM *b, char *buffer, int maxlen) { + int itslen = BN_num_bytes (b); + int reqlen; + if (itslen < 254) { + reqlen = itslen + 1; + } else { + reqlen = itslen + 4; + } + int newlen = (reqlen + 3) & -4; + int pad = newlen - reqlen; + reqlen = newlen; + if (reqlen > maxlen) { + return -reqlen; + } + if (itslen < 254) { + *buffer++ = itslen; + } else { + *(int *)buffer = (itslen << 8) + 0xfe; + buffer += 4; + } + int l = BN_bn2bin (b, (unsigned char *)buffer); + assert (l == itslen); + buffer += l; + while (pad --> 0) { + *buffer++ = 0; + } + return reqlen; +} + + +long long tgl_do_compute_rsa_key_fingerprint (RSA *key) { + static char tempbuff[4096]; + static unsigned char sha[20]; + assert (key->n && key->e); + int l1 = tgl_serialize_bignum (key->n, tempbuff, 4096); + assert (l1 > 0); + int l2 = tgl_serialize_bignum (key->e, tempbuff + l1, 4096 - l1); + assert (l2 > 0 && l1 + l2 <= 4096); + SHA1 ((unsigned char *)tempbuff, l1 + l2, sha); + return *(long long *)(sha + 12); +} + +void tgl_out_cstring (const char *str, long len) { + assert (len >= 0 && len < (1 << 24)); + assert ((char *) packet_ptr + len + 8 < (char *) (packet_buffer + PACKET_BUFFER_SIZE)); + char *dest = (char *) packet_ptr; + if (len < 254) { + *dest++ = len; + } else { + *packet_ptr = (len << 8) + 0xfe; + dest += 4; + } + memcpy (dest, str, len); + dest += len; + while ((long) dest & 3) { + *dest++ = 0; + } + packet_ptr = (int *) dest; +} + +void tgl_out_cstring_careful (const char *str, long len) { + assert (len >= 0 && len < (1 << 24)); + assert ((char *) packet_ptr + len + 8 < (char *) (packet_buffer + PACKET_BUFFER_SIZE)); + char *dest = (char *) packet_ptr; + if (len < 254) { + dest++; + if (dest != str) { + memmove (dest, str, len); + } + dest[-1] = len; + } else { + dest += 4; + if (dest != str) { + memmove (dest, str, len); + } + *packet_ptr = (len << 8) + 0xfe; + } + dest += len; + while ((long) dest & 3) { + *dest++ = 0; + } + packet_ptr = (int *) dest; +} + + +void tgl_out_data (const void *data, long len) { + assert (len >= 0 && len < (1 << 24) && !(len & 3)); + assert ((char *) packet_ptr + len + 8 < (char *) (packet_buffer + PACKET_BUFFER_SIZE)); + memcpy (packet_ptr, data, len); + packet_ptr += len >> 2; +} + +int *tgl_in_ptr, *tgl_in_end; + +int tgl_fetch_bignum (BIGNUM *x) { + int l = prefetch_strlen (); + if (l < 0) { + return l; + } + char *str = fetch_str (l); + assert (BN_bin2bn ((unsigned char *) str, l, x) == x); + return l; +} + +int tgl_pad_rsa_encrypt (struct tgl_state *TLS, char *from, int from_len, char *to, int size, BIGNUM *N, BIGNUM *E) { + int pad = (255000 - from_len - 32) % 255 + 32; + int chunks = (from_len + pad) / 255; + int bits = BN_num_bits (N); + assert (bits >= 2041 && bits <= 2048); + assert (from_len > 0 && from_len <= 2550); + assert (size >= chunks * 256); + assert (RAND_pseudo_bytes ((unsigned char *) from + from_len, pad) >= 0); + int i; + BIGNUM x, y; + BN_init (&x); + BN_init (&y); + rsa_encrypted_chunks += chunks; + for (i = 0; i < chunks; i++) { + BN_bin2bn ((unsigned char *) from, 255, &x); + assert (BN_mod_exp (&y, &x, E, N, TLS->BN_ctx) == 1); + unsigned l = 256 - BN_num_bytes (&y); + assert (l <= 256); + memset (to, 0, l); + BN_bn2bin (&y, (unsigned char *) to + l); + to += 256; + } + BN_free (&x); + BN_free (&y); + return chunks * 256; +} + +int tgl_pad_rsa_decrypt (struct tgl_state *TLS, char *from, int from_len, char *to, int size, BIGNUM *N, BIGNUM *D) { + if (from_len < 0 || from_len > 0x1000 || (from_len & 0xff)) { + return -1; + } + int chunks = (from_len >> 8); + int bits = BN_num_bits (N); + assert (bits >= 2041 && bits <= 2048); + assert (size >= chunks * 255); + int i; + BIGNUM x, y; + BN_init (&x); + BN_init (&y); + for (i = 0; i < chunks; i++) { + ++rsa_decrypted_chunks; + BN_bin2bn ((unsigned char *) from, 256, &x); + assert (BN_mod_exp (&y, &x, D, N, TLS->BN_ctx) == 1); + int l = BN_num_bytes (&y); + if (l > 255) { + BN_free (&x); + BN_free (&y); + return -1; + } + assert (l >= 0 && l <= 255); + memset (to, 0, 255 - l); + BN_bn2bin (&y, (unsigned char *) to + 255 - l); + to += 255; + } + BN_free (&x); + BN_free (&y); + return chunks * 255; +} + +static unsigned char aes_key_raw[32], aes_iv[32]; +static AES_KEY aes_key; + +void tgl_init_aes_unauth (const char server_nonce[16], const char hidden_client_nonce[32], int encrypt) { + static unsigned char buffer[64], hash[20]; + memcpy (buffer, hidden_client_nonce, 32); + memcpy (buffer + 32, server_nonce, 16); + SHA1 (buffer, 48, aes_key_raw); + memcpy (buffer + 32, hidden_client_nonce, 32); + SHA1 (buffer, 64, aes_iv + 8); + memcpy (buffer, server_nonce, 16); + memcpy (buffer + 16, hidden_client_nonce, 32); + SHA1 (buffer, 48, hash); + memcpy (aes_key_raw + 20, hash, 12); + memcpy (aes_iv, hash + 12, 8); + memcpy (aes_iv + 28, hidden_client_nonce, 4); + if (encrypt == AES_ENCRYPT) { + AES_set_encrypt_key (aes_key_raw, 32*8, &aes_key); + } else { + AES_set_decrypt_key (aes_key_raw, 32*8, &aes_key); + } + memset (aes_key_raw, 0, sizeof (aes_key_raw)); +} + +void tgl_init_aes_auth (char auth_key[192], char msg_key[16], int encrypt) { + static unsigned char buffer[48], hash[20]; + // sha1_a = SHA1 (msg_key + substr (auth_key, 0, 32)); + // sha1_b = SHA1 (substr (auth_key, 32, 16) + msg_key + substr (auth_key, 48, 16)); + // sha1_с = SHA1 (substr (auth_key, 64, 32) + msg_key); + // sha1_d = SHA1 (msg_key + substr (auth_key, 96, 32)); + // aes_key = substr (sha1_a, 0, 8) + substr (sha1_b, 8, 12) + substr (sha1_c, 4, 12); + // aes_iv = substr (sha1_a, 8, 12) + substr (sha1_b, 0, 8) + substr (sha1_c, 16, 4) + substr (sha1_d, 0, 8); + memcpy (buffer, msg_key, 16); + memcpy (buffer + 16, auth_key, 32); + SHA1 (buffer, 48, hash); + memcpy (aes_key_raw, hash, 8); + memcpy (aes_iv, hash + 8, 12); + + memcpy (buffer, auth_key + 32, 16); + memcpy (buffer + 16, msg_key, 16); + memcpy (buffer + 32, auth_key + 48, 16); + SHA1 (buffer, 48, hash); + memcpy (aes_key_raw + 8, hash + 8, 12); + memcpy (aes_iv + 12, hash, 8); + + memcpy (buffer, auth_key + 64, 32); + memcpy (buffer + 32, msg_key, 16); + SHA1 (buffer, 48, hash); + memcpy (aes_key_raw + 20, hash + 4, 12); + memcpy (aes_iv + 20, hash + 16, 4); + + memcpy (buffer, msg_key, 16); + memcpy (buffer + 16, auth_key + 96, 32); + SHA1 (buffer, 48, hash); + memcpy (aes_iv + 24, hash, 8); + + if (encrypt == AES_ENCRYPT) { + AES_set_encrypt_key (aes_key_raw, 32*8, &aes_key); + } else { + AES_set_decrypt_key (aes_key_raw, 32*8, &aes_key); + } + memset (aes_key_raw, 0, sizeof (aes_key_raw)); +} + +int tgl_pad_aes_encrypt (char *from, int from_len, char *to, int size) { + int padded_size = (from_len + 15) & -16; + assert (from_len > 0 && padded_size <= size); + if (from_len < padded_size) { + assert (RAND_pseudo_bytes ((unsigned char *) from + from_len, padded_size - from_len) >= 0); + } + AES_ige_encrypt ((unsigned char *) from, (unsigned char *) to, padded_size, &aes_key, aes_iv, AES_ENCRYPT); + return padded_size; +} + +int tgl_pad_aes_decrypt (char *from, int from_len, char *to, int size) { + if (from_len <= 0 || from_len > size || (from_len & 15)) { + return -1; + } + AES_ige_encrypt ((unsigned char *) from, (unsigned char *) to, from_len, &aes_key, aes_iv, AES_DECRYPT); + return from_len; +} + + |