Example #1
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 #2
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 #3
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 #4
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());
    }
  }