Example #1
0
  public static Serializable safeDeserialize(ObjectInputStream in) throws IOException {
    if (!in.readBoolean()) {
      return null;
    }

    int length = in.readInt();

    if (length > buffer.length - 4) {
      buffer = new byte[length];
    }

    in.read(buffer, 0, length);
    buffer[length] = (byte) 0xFF;
    buffer[length + 1] = (byte) 0xFF;
    buffer[length + 2] = (byte) 0xFF;
    buffer[length + 3] = (byte) 0xFF;

    Serializable object;

    try {
      object = deserializeRaw(buffer);
    } catch (ClassNotFoundException cnfe) {
      SerializedData dummy = new SerializedData();
      dummy.serialData = Arrays.copyOf(buffer, length);
      object = dummy;
    }

    return object;
  }
Example #2
0
 static {
   density = ApplicationLoader.applicationContext.getResources().getDisplayMetrics().density;
   SharedPreferences preferences =
       ApplicationLoader.applicationContext.getSharedPreferences("primes", Context.MODE_PRIVATE);
   String primes = preferences.getString("primes", null);
   if (primes == null) {
     goodPrimes.add(
         "C71CAEB9C6B1C9048E6C522F70F13F73980D40238E3E21C14934D037563D930F48198A0AA7C14058229493D22530F4DBFA336F6E0AC925139543AED44CCE7C3720FD51F69458705AC68CD4FE6B6B13ABDC9746512969328454F18FAF8C595F642477FE96BB2A941D5BCD1D4AC8CC49880708FA9B378E3C4F3A9060BEE67CF9A4A4A695811051907E162753B56B0F6B410DBA74D8A84B2A14B3144E0EF1284754FD17ED950D5965B4B9DD46582DB1178D169C6BC465B0D6FF9CA3928FEF5B9AE4E418FC15E83EBEA0F87FA9FF5EED70050DED2849F47BF959D956850CE929851F0D8115F635B105EE2E4E15D04B2454BF6F4FADF034B10403119CD8E3B92FCC5B");
   } else {
     try {
       byte[] bytes = Base64.decode(primes, Base64.DEFAULT);
       if (bytes != null) {
         SerializedData data = new SerializedData(bytes);
         int count = data.readInt32();
         for (int a = 0; a < count; a++) {
           goodPrimes.add(data.readString());
         }
       }
     } catch (Exception e) {
       FileLog.e("tmessages", e);
       goodPrimes.clear();
       goodPrimes.add(
           "C71CAEB9C6B1C9048E6C522F70F13F73980D40238E3E21C14934D037563D930F48198A0AA7C14058229493D22530F4DBFA336F6E0AC925139543AED44CCE7C3720FD51F69458705AC68CD4FE6B6B13ABDC9746512969328454F18FAF8C595F642477FE96BB2A941D5BCD1D4AC8CC49880708FA9B378E3C4F3A9060BEE67CF9A4A4A695811051907E162753B56B0F6B410DBA74D8A84B2A14B3144E0EF1284754FD17ED950D5965B4B9DD46582DB1178D169C6BC465B0D6FF9CA3928FEF5B9AE4E418FC15E83EBEA0F87FA9FF5EED70050DED2849F47BF959D956850CE929851F0D8115F635B105EE2E4E15D04B2454BF6F4FADF034B10403119CD8E3B92FCC5B");
     }
   }
   System.loadLibrary("tmessages");
 }
Example #3
0
  @Override
  public void tcpConnectionReceivedData(TcpConnection connection, byte[] data) {
    SerializedData is = new SerializedData(data);

    long keyId = is.readInt64();

    if (keyId == 0) {
      long messageId = is.readInt64();
      if (processedMessageIds.contains(messageId)) {
        Log.d(
            "tmessages",
            String.format("===== Duplicate message id %d received, ignoring", messageId));
        return;
      }
      int messageLength = is.readInt32();

      int constructor = is.readInt32();
      TLObject object = TLClassStore.Instance().TLdeserialize(is, constructor);

      if (object != null) {
        processedMessageIds.add(messageId);
      }
      processMessage(object, messageId);
    } else {
      Log.d("tmessages", "***** Received encrypted message while in handshake, restarting");
      beginHandshake(true);
    }
  }
Example #4
0
 public static void saveConfig(boolean withFile, File oldFile) {
   synchronized (sync) {
     try {
       SharedPreferences preferences =
           ApplicationLoader.applicationContext.getSharedPreferences(
               "userconfing", Context.MODE_PRIVATE);
       SharedPreferences.Editor editor = preferences.edit();
       if (currentUser != null) {
         editor.putBoolean("registeredForPush", registeredForPush);
         editor.putString("pushString", pushString);
         editor.putInt("lastSendMessageId", lastSendMessageId);
         editor.putInt("lastLocalId", lastLocalId);
         editor.putString("contactsHash", contactsHash);
         editor.putString("importHash", importHash);
         editor.putBoolean("saveIncomingPhotos", saveIncomingPhotos);
         if (withFile) {
           SerializedData data = new SerializedData();
           currentUser.serializeToStream(data);
           clientUserId = currentUser.id;
           clientActivated = true;
           String userString = Base64.encodeToString(data.toByteArray(), Base64.DEFAULT);
           editor.putString("user", userString);
         }
       } else {
         editor.putBoolean("registeredForPush", registeredForPush);
         editor.putString("pushString", pushString);
         editor.putInt("lastSendMessageId", lastSendMessageId);
         editor.putInt("lastLocalId", lastLocalId);
         editor.putString("contactsHash", contactsHash);
         editor.putString("importHash", importHash);
         editor.putBoolean("saveIncomingPhotos", saveIncomingPhotos);
         editor.remove("user");
       }
       editor.commit();
       if (oldFile != null) {
         oldFile.delete();
       }
     } catch (Exception e) {
       FileLog.e("tmessages", e);
     }
   }
 }
Example #5
0
 public static TLObject decompress(byte[] data, TLObject parentObject) {
   final int BUFFER_SIZE = 512;
   ByteArrayInputStream is = new ByteArrayInputStream(data);
   GZIPInputStream gis;
   try {
     gis = new GZIPInputStream(is, BUFFER_SIZE);
     ByteArrayOutputStream bytesOutput = new ByteArrayOutputStream();
     data = new byte[BUFFER_SIZE];
     int bytesRead;
     while ((bytesRead = gis.read(data)) != -1) {
       bytesOutput.write(data, 0, bytesRead);
     }
     gis.close();
     is.close();
     SerializedData stream = new SerializedData(bytesOutput.toByteArray());
     return TLClassStore.Instance().TLdeserialize(stream, stream.readInt32(), parentObject);
   } catch (IOException e) {
     FileLog.e("tmessages", e);
   }
   return null;
 }
Example #6
0
  byte[] sendMessageData(TLObject message, long messageId) {
    byte[] messageData;
    SerializedData innerOs = new SerializedData();
    message.serializeToStream(innerOs);
    messageData = innerOs.toByteArray();

    SerializedData messageOs = new SerializedData();
    messageOs.writeInt64(0);
    messageOs.writeInt64(messageId);
    messageOs.writeInt32(messageData.length);
    messageOs.writeRaw(messageData);

    byte[] transportData = messageOs.toByteArray();

    datacenter.connection.sendData(transportData, false, false);

    return transportData;
  }
Example #7
0
  public static MessageKeyData generateMessageKeyData(
      byte[] authKey, byte[] messageKey, boolean incoming) {
    MessageKeyData keyData = new MessageKeyData();
    if (authKey == null || authKey.length == 0) {
      keyData.aesIv = null;
      keyData.aesKey = null;
      return keyData;
    }

    int x = incoming ? 8 : 0;

    SerializedData data = new SerializedData();
    data.writeRaw(messageKey);
    data.writeRaw(authKey, x, 32);
    byte[] sha1_a = Utilities.computeSHA1(data.toByteArray());

    data = new SerializedData();
    data.writeRaw(authKey, 32 + x, 16);
    data.writeRaw(messageKey);
    data.writeRaw(authKey, 48 + x, 16);
    byte[] sha1_b = Utilities.computeSHA1(data.toByteArray());

    data = new SerializedData();
    data.writeRaw(authKey, 64 + x, 32);
    data.writeRaw(messageKey);
    byte[] sha1_c = Utilities.computeSHA1(data.toByteArray());

    data = new SerializedData();
    data.writeRaw(messageKey);
    data.writeRaw(authKey, 96 + x, 32);
    byte[] sha1_d = Utilities.computeSHA1(data.toByteArray());

    SerializedData aesKey = new SerializedData();
    aesKey.writeRaw(sha1_a, 0, 8);
    aesKey.writeRaw(sha1_b, 8, 12);
    aesKey.writeRaw(sha1_c, 4, 12);
    keyData.aesKey = aesKey.toByteArray();

    SerializedData aesIv = new SerializedData();
    aesIv.writeRaw(sha1_a, 8, 12);
    aesIv.writeRaw(sha1_b, 0, 8);
    aesIv.writeRaw(sha1_c, 16, 4);
    aesIv.writeRaw(sha1_d, 0, 8);
    keyData.aesIv = aesIv.toByteArray();

    return keyData;
  }
Example #8
0
  public Datacenter(SerializedData data, int version) {
    if (version == 0) {
      datacenterId = data.readInt32();
      String address = data.readString();
      addresses.add(address);
      int port = data.readInt32();
      ports.put(address, port);
      int len = data.readInt32();
      if (len != 0) {
        authKey = data.readData(len);
      }
      len = data.readInt32();
      if (len != 0) {
        authKeyId = data.readInt64();
      }
      authorized = data.readInt32() != 0;
      len = data.readInt32();
      for (int a = 0; a < len; a++) {
        ServerSalt salt = new ServerSalt();
        salt.validSince = data.readInt32();
        salt.validUntil = data.readInt32();
        salt.value = data.readInt64();
        if (authServerSaltSet == null) {
          authServerSaltSet = new ArrayList<ServerSalt>();
        }
        authServerSaltSet.add(salt);
      }
    } else if (version == 1) {
      int currentVersion = data.readInt32();
      if (currentVersion == 2 || currentVersion == 3 || currentVersion == 4) {
        datacenterId = data.readInt32();
        if (currentVersion >= 3) {
          lastInitVersion = data.readInt32();
        }
        int len = data.readInt32();
        for (int a = 0; a < len; a++) {
          String address = data.readString();
          addresses.add(address);
          ports.put(address, data.readInt32());
        }

        len = data.readInt32();
        if (len != 0) {
          authKey = data.readData(len);
        }
        if (currentVersion == 4) {
          authKeyId = data.readInt64();
        } else {
          len = data.readInt32();
          if (len != 0) {
            authKeyId = data.readInt64();
          }
        }
        authorized = data.readInt32() != 0;
        len = data.readInt32();
        for (int a = 0; a < len; a++) {
          ServerSalt salt = new ServerSalt();
          salt.validSince = data.readInt32();
          salt.validUntil = data.readInt32();
          salt.value = data.readInt64();
          if (authServerSaltSet == null) {
            authServerSaltSet = new ArrayList<ServerSalt>();
          }
          authServerSaltSet.add(salt);
        }
      }
    } else if (version == 2) {

    }
    readCurrentAddressAndPortNum();
  }
Example #9
0
 public void SerializeToStream(SerializedData stream) {
   stream.writeInt32(DATA_VERSION);
   stream.writeInt32(datacenterId);
   stream.writeInt32(lastInitVersion);
   stream.writeInt32(addresses.size());
   for (String address : addresses) {
     stream.writeString(address);
     stream.writeInt32(ports.get(address));
   }
   if (authKey != null) {
     stream.writeInt32(authKey.length);
     stream.writeRaw(authKey);
   } else {
     stream.writeInt32(0);
   }
   stream.writeInt64(authKeyId);
   stream.writeInt32(authorized ? 1 : 0);
   stream.writeInt32(authServerSaltSet.size());
   for (ServerSalt salt : authServerSaltSet) {
     stream.writeInt32(salt.validSince);
     stream.writeInt32(salt.validUntil);
     stream.writeInt64(salt.value);
   }
 }
Example #10
0
  public static void loadConfig() {
    synchronized (sync) {
      final File configFile =
          new File(ApplicationLoader.applicationContext.getFilesDir(), "user.dat");
      if (configFile.exists()) {
        try {
          SerializedData data = new SerializedData(configFile);
          int ver = data.readInt32();
          if (ver == 1) {
            int constructor = data.readInt32();
            currentUser =
                (TLRPC.TL_userSelf) TLClassStore.Instance().TLdeserialize(data, constructor);
            clientUserId = currentUser.id;
            clientActivated = true;
            MessagesStorage.lastDateValue = data.readInt32();
            MessagesStorage.lastPtsValue = data.readInt32();
            MessagesStorage.lastSeqValue = data.readInt32();
            registeredForPush = data.readBool();
            pushString = data.readString();
            lastSendMessageId = data.readInt32();
            lastLocalId = data.readInt32();
            contactsHash = data.readString();
            importHash = data.readString();
            saveIncomingPhotos = data.readBool();
            if (currentUser.status != null) {
              if (currentUser.status.expires != 0) {
                currentUser.status.was_online = currentUser.status.expires;
              } else {
                currentUser.status.expires = currentUser.status.was_online;
              }
            }
            MessagesStorage.lastQtsValue = data.readInt32();
            MessagesStorage.lastSecretVersion = data.readInt32();
            int val = data.readInt32();
            if (val == 1) {
              MessagesStorage.secretPBytes = data.readByteArray();
            }
            MessagesStorage.secretG = data.readInt32();
            Utilities.stageQueue.postRunnable(
                new Runnable() {
                  @Override
                  public void run() {
                    saveConfig(true, configFile);
                  }
                });
          } else if (ver == 2) {
            int constructor = data.readInt32();
            currentUser =
                (TLRPC.TL_userSelf) TLClassStore.Instance().TLdeserialize(data, constructor);
            clientUserId = currentUser.id;
            clientActivated = true;

            SharedPreferences preferences =
                ApplicationLoader.applicationContext.getSharedPreferences(
                    "userconfing", Context.MODE_PRIVATE);
            registeredForPush = preferences.getBoolean("registeredForPush", false);
            pushString = preferences.getString("pushString", "");
            lastSendMessageId = preferences.getInt("lastSendMessageId", -210000);
            lastLocalId = preferences.getInt("lastLocalId", -210000);
            contactsHash = preferences.getString("contactsHash", "");
            importHash = preferences.getString("importHash", "");
            saveIncomingPhotos = preferences.getBoolean("saveIncomingPhotos", false);
          }
          if (lastLocalId > -210000) {
            lastLocalId = -210000;
          }
          if (lastSendMessageId > -210000) {
            lastSendMessageId = -210000;
          }
          Utilities.stageQueue.postRunnable(
              new Runnable() {
                @Override
                public void run() {
                  saveConfig(true, configFile);
                }
              });
        } catch (Exception e) {
          FileLog.e("tmessages", e);
        }
      } else {
        SharedPreferences preferences =
            ApplicationLoader.applicationContext.getSharedPreferences(
                "userconfing", Context.MODE_PRIVATE);
        registeredForPush = preferences.getBoolean("registeredForPush", false);
        pushString = preferences.getString("pushString", "");
        lastSendMessageId = preferences.getInt("lastSendMessageId", -210000);
        lastLocalId = preferences.getInt("lastLocalId", -210000);
        contactsHash = preferences.getString("contactsHash", "");
        importHash = preferences.getString("importHash", "");
        saveIncomingPhotos = preferences.getBoolean("saveIncomingPhotos", false);
        String user = preferences.getString("user", null);
        if (user != null) {
          byte[] userBytes = Base64.decode(user, Base64.DEFAULT);
          if (userBytes != null) {
            SerializedData data = new SerializedData(userBytes);
            currentUser =
                (TLRPC.TL_userSelf) TLClassStore.Instance().TLdeserialize(data, data.readInt32());
            clientUserId = currentUser.id;
            clientActivated = true;
          }
        }
        if (currentUser == null) {
          clientActivated = false;
          clientUserId = 0;
        }
      }
    }
  }
Example #11
0
  void processMessage(TLObject message, long messageId) {
    if (message instanceof TLRPC.TL_resPQ) {
      if (processedPQRes) {
        TLRPC.TL_msgs_ack msgsAck = new TLRPC.TL_msgs_ack();
        msgsAck.msg_ids = new ArrayList<Long>();
        msgsAck.msg_ids.add(messageId);
        sendMessageData(msgsAck, generateMessageId());
        return;
      }

      processedPQRes = true;
      final TLRPC.TL_resPQ resPq = (TLRPC.TL_resPQ) message;
      if (Arrays.equals(authNonce, resPq.nonce)) {
        final HashMap<String, Object> publicKey =
            selectPublicKey(resPq.server_public_key_fingerprints);
        if (publicKey == null) {
          Log.e("tmessages", "***** Couldn't find valid server public key");
          beginHandshake(false);
          return;
        }

        authServerNonce = resPq.server_nonce;

        ByteBuffer data = ByteBuffer.wrap(resPq.pq);
        final long pqf = data.getLong();
        final long messageIdf = messageId;
        Utilities.globalQueue.postRunnable(
            new Runnable() {
              @Override
              public void run() {
                final Utilities.TPFactorizedValue factorizedPq = Utilities.getFactorizedValue(pqf);

                Utilities.stageQueue.postRunnable(
                    new Runnable() {
                      @Override
                      public void run() {
                        ByteBuffer pBytes = ByteBuffer.allocate(4);
                        pBytes.putInt((int) factorizedPq.p);
                        byte[] pData = pBytes.array();

                        ByteBuffer qBytes = ByteBuffer.allocate(4);
                        qBytes.putInt((int) factorizedPq.q);
                        byte[] qData = qBytes.array();

                        TLRPC.TL_req_DH_params reqDH = new TLRPC.TL_req_DH_params();
                        reqDH.nonce = authNonce;
                        reqDH.server_nonce = authServerNonce;
                        reqDH.p = pData;
                        reqDH.q = qData;
                        reqDH.public_key_fingerprint = (Long) publicKey.get("fingerprint");

                        SerializedData os = new SerializedData();

                        TLRPC.TL_p_q_inner_data innerData = new TLRPC.TL_p_q_inner_data();
                        innerData.nonce = authNonce;
                        innerData.server_nonce = authServerNonce;
                        innerData.pq = resPq.pq;
                        innerData.p = reqDH.p;
                        innerData.q = reqDH.q;

                        byte[] nonceBytes = new byte[32];
                        for (int a = 0; a < 32; a++) {
                          nonceBytes[a] = (byte) (MessagesController.random.nextDouble() * 255);
                        }
                        innerData.new_nonce = authNewNonce = nonceBytes;
                        innerData.serializeToStream(os);

                        byte[] innerDataBytes = os.toByteArray();

                        SerializedData dataWithHash = new SerializedData();
                        dataWithHash.writeRaw(Utilities.computeSHA1(innerDataBytes));
                        dataWithHash.writeRaw(innerDataBytes);
                        while (dataWithHash.length() < 255) {
                          dataWithHash.writeByte(0);
                        }

                        byte[] encryptedBytes =
                            Utilities.encryptWithRSA(
                                (BigInteger[]) publicKey.get("key"), dataWithHash.toByteArray());
                        SerializedData encryptedData = new SerializedData();
                        encryptedData.writeRaw(encryptedBytes);
                        if (encryptedData.length() < 256) {
                          SerializedData newEncryptedData = new SerializedData();
                          for (int i = 0; i < 256 - encryptedData.length(); i++) {
                            newEncryptedData.writeByte(0);
                          }
                          newEncryptedData.writeRaw(encryptedData.toByteArray());
                          encryptedData = newEncryptedData;
                        }
                        reqDH.encrypted_data = encryptedData.toByteArray();

                        TLRPC.TL_msgs_ack msgsAck = new TLRPC.TL_msgs_ack();
                        msgsAck.msg_ids = new ArrayList<Long>();
                        msgsAck.msg_ids.add(messageIdf);
                        sendMessageData(msgsAck, generateMessageId());

                        reqPQMsgData = null;
                        reqDHMsgData = sendMessageData(reqDH, generateMessageId());
                      }
                    });
              }
            });
      } else {
        Log.e("tmessages", "***** Error: invalid handshake nonce");
        beginHandshake(false);
      }
    } else if (message instanceof TLRPC.Server_DH_Params) {
      if (message instanceof TLRPC.TL_server_DH_params_ok) {
        TLRPC.TL_server_DH_params_ok serverDhParams = (TLRPC.TL_server_DH_params_ok) message;

        SerializedData tmpAesKey = new SerializedData();

        SerializedData newNonceAndServerNonce = new SerializedData();
        newNonceAndServerNonce.writeRaw(authNewNonce);
        newNonceAndServerNonce.writeRaw(authServerNonce);

        SerializedData serverNonceAndNewNonce = new SerializedData();
        serverNonceAndNewNonce.writeRaw(authServerNonce);
        serverNonceAndNewNonce.writeRaw(authNewNonce);
        tmpAesKey.writeRaw(Utilities.computeSHA1(newNonceAndServerNonce.toByteArray()));

        byte[] serverNonceAndNewNonceHash =
            Utilities.computeSHA1(serverNonceAndNewNonce.toByteArray());
        byte[] serverNonceAndNewNonceHash0_12 = new byte[12];
        System.arraycopy(serverNonceAndNewNonceHash, 0, serverNonceAndNewNonceHash0_12, 0, 12);

        tmpAesKey.writeRaw(serverNonceAndNewNonceHash0_12);

        SerializedData tmpAesIv = new SerializedData();

        byte[] serverNonceAndNewNonceHash12_8 = new byte[8];
        System.arraycopy(serverNonceAndNewNonceHash, 12, serverNonceAndNewNonceHash12_8, 0, 8);
        tmpAesIv.writeRaw(serverNonceAndNewNonceHash12_8);

        SerializedData newNonceAndNewNonce = new SerializedData();
        newNonceAndNewNonce.writeRaw(authNewNonce);
        newNonceAndNewNonce.writeRaw(authNewNonce);
        tmpAesIv.writeRaw(Utilities.computeSHA1(newNonceAndNewNonce.toByteArray()));

        byte[] newNonce0_4 = new byte[4];
        System.arraycopy(authNewNonce, 0, newNonce0_4, 0, 4);
        tmpAesIv.writeRaw(newNonce0_4);

        byte[] answerWithHash =
            Utilities.aesIgeEncryption(
                serverDhParams.encrypted_answer,
                tmpAesKey.toByteArray(),
                tmpAesIv.toByteArray(),
                false,
                false);
        byte[] answerHash = new byte[20];
        System.arraycopy(answerWithHash, 0, answerHash, 0, 20);

        byte[] answerData = new byte[answerWithHash.length - 20];
        System.arraycopy(answerWithHash, 20, answerData, 0, answerWithHash.length - 20);
        boolean hashVerified = false;
        for (int i = 0; i < 16; i++) {
          byte[] computedAnswerHash = Utilities.computeSHA1(answerData);
          if (Arrays.equals(computedAnswerHash, answerHash)) {
            hashVerified = true;
            break;
          }
          byte[] answerData2 = new byte[answerData.length - 1];
          System.arraycopy(answerData, 0, answerData2, 0, answerData.length - 1);
          answerData = answerData2;
        }

        if (!hashVerified) {
          Log.e("tmessages", "***** Couldn't decode DH params");
          beginHandshake(false);
          return;
        }

        SerializedData answerIs = new SerializedData(answerData);
        int constructor = answerIs.readInt32();
        TLRPC.TL_server_DH_inner_data dhInnerData =
            (TLRPC.TL_server_DH_inner_data)
                TLClassStore.Instance().TLdeserialize(answerIs, constructor);

        if (!(dhInnerData instanceof TLRPC.TL_server_DH_inner_data)) {
          Log.e("tmessages", "***** Couldn't parse decoded DH params");
          beginHandshake(false);
          return;
        }

        if (!Arrays.equals(authNonce, dhInnerData.nonce)) {
          Log.e("tmessages", "***** Invalid DH nonce");
          beginHandshake(false);
          return;
        }
        if (!Arrays.equals(authServerNonce, dhInnerData.server_nonce)) {
          Log.e("tmessages", "***** Invalid DH server nonce");
          beginHandshake(false);
          return;
        }

        byte[] b = new byte[256];
        for (int a = 0; a < 256; a++) {
          b[a] = (byte) (MessagesController.random.nextDouble() * 255);
        }

        BigInteger i_g_b = BigInteger.valueOf(dhInnerData.g);
        i_g_b = i_g_b.modPow(new BigInteger(1, b), new BigInteger(1, dhInnerData.dh_prime));
        byte[] g_b = i_g_b.toByteArray();

        BigInteger i_authKey = new BigInteger(1, dhInnerData.g_a);
        i_authKey = i_authKey.modPow(new BigInteger(1, b), new BigInteger(1, dhInnerData.dh_prime));

        authKey = i_authKey.toByteArray();
        if (authKey.length > 256) {
          byte[] correctedAuth = new byte[256];
          System.arraycopy(authKey, 1, correctedAuth, 0, 256);
          authKey = correctedAuth;
        } else if (authKey.length < 256) {
          byte[] correctedAuth = new byte[256];
          System.arraycopy(authKey, 0, correctedAuth, 256 - authKey.length, authKey.length);
          for (int a = 0; a < 256 - authKey.length; a++) {
            authKey[a] = 0;
          }
          authKey = correctedAuth;
        }
        byte[] authKeyHash = Utilities.computeSHA1(authKey);
        authKeyId = new byte[8];
        System.arraycopy(authKeyHash, authKeyHash.length - 8, authKeyId, 0, 8);

        SerializedData serverSaltData = new SerializedData();
        for (int i = 7; i >= 0; i--) {
          byte a_ = authNewNonce[i];
          byte b_ = authServerNonce[i];
          byte x = (byte) (a_ ^ b_);
          serverSaltData.writeByte(x);
        }
        ByteBuffer saltBuffer = ByteBuffer.wrap(serverSaltData.toByteArray());

        timeDifference = dhInnerData.server_time - (int) (System.currentTimeMillis() / 1000);

        serverSalt = new ServerSalt();
        serverSalt.validSince = (int) (System.currentTimeMillis() / 1000) + timeDifference;
        serverSalt.validUntil =
            (int) (System.currentTimeMillis() / 1000) + timeDifference + 30 * 60;
        serverSalt.value = saltBuffer.getLong();

        if (ConnectionsManager.DEBUG_VERSION) {
          Log.d("tmessages", String.format(Locale.US, "===== Time difference: %d", timeDifference));
        }

        TLRPC.TL_client_DH_inner_data clientInnerData = new TLRPC.TL_client_DH_inner_data();
        clientInnerData.nonce = authNonce;
        clientInnerData.server_nonce = authServerNonce;
        clientInnerData.g_b = g_b;
        clientInnerData.retry_id = 0;
        SerializedData os = new SerializedData();
        clientInnerData.serializeToStream(os);
        byte[] clientInnerDataBytes = os.toByteArray();

        SerializedData clientDataWithHash = new SerializedData();
        clientDataWithHash.writeRaw(Utilities.computeSHA1(clientInnerDataBytes));
        clientDataWithHash.writeRaw(clientInnerDataBytes);
        while (clientDataWithHash.length() % 16 != 0) {
          clientDataWithHash.writeByte(0);
        }

        TLRPC.TL_set_client_DH_params setClientDhParams = new TLRPC.TL_set_client_DH_params();
        setClientDhParams.nonce = authNonce;
        setClientDhParams.server_nonce = authServerNonce;
        setClientDhParams.encrypted_data =
            Utilities.aesIgeEncryption(
                clientDataWithHash.toByteArray(),
                tmpAesKey.toByteArray(),
                tmpAesIv.toByteArray(),
                true,
                false);

        TLRPC.TL_msgs_ack msgsAck = new TLRPC.TL_msgs_ack();
        msgsAck.msg_ids = new ArrayList<Long>();
        msgsAck.msg_ids.add(messageId);
        sendMessageData(msgsAck, generateMessageId());

        reqDHMsgData = null;
        setClientDHParamsMsgData = sendMessageData(setClientDhParams, generateMessageId());
      } else {
        Log.e("tmessages", "***** Couldn't set DH params");
        beginHandshake(false);
      }
    } else if (message instanceof TLRPC.Set_client_DH_params_answer) {
      TLRPC.Set_client_DH_params_answer dhAnswer = (TLRPC.Set_client_DH_params_answer) message;

      if (!Arrays.equals(authNonce, dhAnswer.nonce)) {
        Log.e("tmessages", "***** Invalid DH answer nonce");
        beginHandshake(false);
        return;
      }
      if (!Arrays.equals(authServerNonce, dhAnswer.server_nonce)) {
        Log.e("tmessages", "***** Invalid DH answer server nonce");
        beginHandshake(false);
        return;
      }

      reqDHMsgData = null;

      TLRPC.TL_msgs_ack msgsAck = new TLRPC.TL_msgs_ack();
      msgsAck.msg_ids = new ArrayList<Long>();
      msgsAck.msg_ids.add(messageId);
      sendMessageData(msgsAck, generateMessageId());

      byte[] authKeyAuxHashFull = Utilities.computeSHA1(authKey);
      byte[] authKeyAuxHash = new byte[8];
      System.arraycopy(authKeyAuxHashFull, 0, authKeyAuxHash, 0, 8);

      SerializedData newNonce1 = new SerializedData();
      newNonce1.writeRaw(authNewNonce);
      newNonce1.writeByte(1);
      newNonce1.writeRaw(authKeyAuxHash);
      byte[] newNonceHash1Full = Utilities.computeSHA1(newNonce1.toByteArray());
      byte[] newNonceHash1 = new byte[16];
      System.arraycopy(newNonceHash1Full, newNonceHash1Full.length - 16, newNonceHash1, 0, 16);

      SerializedData newNonce2 = new SerializedData();
      newNonce2.writeRaw(authNewNonce);
      newNonce2.writeByte(2);
      newNonce2.writeRaw(authKeyAuxHash);
      byte[] newNonceHash2Full = Utilities.computeSHA1(newNonce2.toByteArray());
      byte[] newNonceHash2 = new byte[16];
      System.arraycopy(newNonceHash2Full, newNonceHash2Full.length - 16, newNonceHash2, 0, 16);

      SerializedData newNonce3 = new SerializedData();
      newNonce3.writeRaw(authNewNonce);
      newNonce3.writeByte(3);
      newNonce3.writeRaw(authKeyAuxHash);
      byte[] newNonceHash3Full = Utilities.computeSHA1(newNonce3.toByteArray());
      byte[] newNonceHash3 = new byte[16];
      System.arraycopy(newNonceHash3Full, newNonceHash3Full.length - 16, newNonceHash3, 0, 16);

      if (message instanceof TLRPC.TL_dh_gen_ok) {
        TLRPC.TL_dh_gen_ok dhGenOk = (TLRPC.TL_dh_gen_ok) message;
        if (!Arrays.equals(newNonceHash1, dhGenOk.new_nonce_hash1)) {
          Log.e("tmessages", "***** Invalid DH answer nonce hash 1");
          beginHandshake(false);
          return;
        }

        Log.d("tmessages", String.format("Handshake with DC%d completed", datacenter.datacenterId));
        datacenter.connection.delegate = null;

        final Action parent = this;
        Utilities.stageQueue.postRunnable(
            new Runnable() {
              @Override
              public void run() {
                datacenter.authKey = authKey;
                datacenter.authKeyId = authKeyId;
                datacenter.addServerSalt(serverSalt);
                HashMap<String, Object> resultDict = new HashMap<String, Object>();
                resultDict.put("timeDifference", timeDifference);
                if (delegate != null) {
                  delegate.ActionDidFinishExecution(parent, resultDict);
                }
              }
            });
      } else if (message instanceof TLRPC.TL_dh_gen_retry) {
        TLRPC.TL_dh_gen_retry dhRetry = (TLRPC.TL_dh_gen_retry) message;
        if (!Arrays.equals(newNonceHash2, dhRetry.new_nonce_hash2)) {
          Log.e("tmessages", "***** Invalid DH answer nonce hash 2");
          beginHandshake(false);
          return;
        }
        Log.d("tmessages", "***** Retry DH");
        beginHandshake(false);
      } else if (message instanceof TLRPC.TL_dh_gen_fail) {
        TLRPC.TL_dh_gen_fail dhFail = (TLRPC.TL_dh_gen_fail) message;
        if (!Arrays.equals(newNonceHash3, dhFail.new_nonce_hash3)) {
          Log.e("tmessages", "***** Invalid DH answer nonce hash 3");
          beginHandshake(false);
          return;
        }
        Log.d("tmessages", "***** Server declined DH params");
        beginHandshake(false);
      } else {
        Log.e("tmessages", "***** Unknown DH params response");
        beginHandshake(false);
      }
    } else {
      TLRPC.TL_msgs_ack msgsAck = new TLRPC.TL_msgs_ack();
      msgsAck.msg_ids = new ArrayList<Long>();
      msgsAck.msg_ids.add(messageId);
      sendMessageData(msgsAck, generateMessageId());
    }
  }