@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); } }
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; }
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; } } } }
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()); } }