Beispiel #1
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;
  }
 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);
   }
 }
Beispiel #3
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;
  }
Beispiel #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());
    }
  }