private static TLObject ParseMessageContainer(BinaryReader reader) { try { var RawObject = new JArray(); var MessageCount = IntegerUtil.Deserialize(reader); for (int i = 0; i < MessageCount; i++) { RawObject.Add(new JObject { ["msg_id"] = LongUtil.Deserialize(reader), ["seqno"] = IntegerUtil.Deserialize(reader), ["bytes"] = IntegerUtil.Deserialize(reader), ["body"] = TLObject.Deserialize(reader) }); } return(new TLObject(JObject.FromObject(new { _ = "msg_container", messages = RawObject }))); } catch { return(null); } }
private static TLObject ParseGZipPacked(BinaryReader reader) { try { using (var ms = new MemoryStream()) using (var packedStream = new MemoryStream(BytesUtil.Read(reader), false)) using (var zipStream = new System.IO.Compression.GZipStream(packedStream, System.IO.Compression.CompressionMode.Decompress)) using (var compressedReader = new BinaryReader(ms)) { zipStream.CopyTo(ms); ms.Position = 0; return(TLObject.Deserialize(compressedReader)); } } catch { return(null); } }
/// <summary> /// Deserilizes a Session object from a stream /// </summary> /// <param name="reader">The stream containing the raw Session data</param> public static Session Deserialize(string FileName, BinaryReader reader) { Network.MTProtoHelper Helper = null; if (BoolUtil.Deserialize(reader)) { Helper = MTProtoHelper.Deserialize(BytesUtil.Deserialize(reader)); } TLObject TLUser = null; if (BoolUtil.Deserialize(reader)) { TLUser = TLObject.Deserialize(reader); } var SessionExpires = IntegerUtil.Deserialize(reader); DataCenter DataCenter = null; if (BoolUtil.Deserialize(reader)) { DataCenter = DataCenter.Deserialize(BytesUtil.Deserialize(reader)); } var session = new Session(FileName) { Helper = Helper, TLUser = TLUser, SessionExpires = SessionExpires, DataCenter = DataCenter }; PeerManager.Deserialize(reader).ToList().ForEach(x => session.KnownPeers.AddOrUpdatePeer(x.AsTLObject())); return(session); }
private static TLObject ParseRPCResult(BinaryReader reader) { try { ulong LocalMsgID = reader.ReadUInt64(); uint RPCCode = reader.ReadUInt32(); var RawObject = JObject.FromObject(new { _ = "rpc_result", req_msg_id = LocalMsgID }); if (!Enum.IsDefined(typeof(RPCCodes), RPCCode)) { reader.BaseStream.Position -= 4; RawObject["result"] = TLObject.Deserialize(reader); return(new TLObject(RawObject)); } switch ((RPCCodes)RPCCode) { case RPCCodes.rpc_error: RawObject["result"] = JObject.FromObject(new { _ = "rpc_error", error_code = IntegerUtil.Deserialize(reader), error_message = StringUtil.Read(reader) }); //if (errorMessage.StartsWith("FLOOD_WAIT_")) //{ // var resultString = Regex.Match(errorMessage, @"\d+").Value; // var seconds = int.Parse(resultString); // throw new FloodException(TimeSpan.FromSeconds(seconds)); //} //else if (errorMessage.StartsWith("PHONE_MIGRATE_")) //{ // var resultString = Regex.Match(errorMessage, @"\d+").Value; // var dcIdx = int.Parse(resultString); // throw new PhoneMigrationException(dcIdx); //} //else if (errorMessage.StartsWith("FILE_MIGRATE_")) //{ // var resultString = Regex.Match(errorMessage, @"\d+").Value; // var dcIdx = int.Parse(resultString); // throw new FileMigrationException(dcIdx); //} //else if (errorMessage.StartsWith("USER_MIGRATE_")) //{ // var resultString = Regex.Match(errorMessage, @"\d+").Value; // var dcIdx = int.Parse(resultString); // throw new UserMigrationException(dcIdx); //} //else if (errorMessage.StartsWith("NETWORK_MIGRATE_")) //{ // var resultString = Regex.Match(errorMessage, @"\d+").Value; // var dcIdx = int.Parse(resultString); // throw new NetworkMigrationException(dcIdx); //} //else if (errorMessage == "AUTH_RESTART") //{ // throw new AuthRestartException("The session is already logged in but is trying to log in again"); //} //else if (errorMessage == "PHONE_CODE_INVALID") //{ // throw new InvalidPhoneCodeException("The numeric code used to authenticate does not match the numeric code sent by SMS/Telegram"); //} //else if (errorMessage == "SESSION_PASSWORD_NEEDED") //{ // throw new CloudPasswordNeededException("This Account has Cloud Password !"); //} //else //{ // throw new InvalidOperationException(errorMessage); //} break; case RPCCodes.gzip_packed: RawObject["result"] = JObject.Parse(ParseGZipPacked(reader).ToString()); break; default: reader.BaseStream.Position -= 4; RawObject["result"] = JObject.Parse(TLObject.Deserialize(reader).ToString()); break; } return(new TLObject(RawObject)); } catch { return(null); } }
/// <summary> /// Inverse of <see cref="EncryptMessageData"/> for incoming server messages. /// </summary> public TLObject DecryptMessageData(byte[] body, bool client = false) { if (body.Length < 8) { throw new Exception("Cannot decrypt a message of 8 bytes."); } using (var memory = new MemoryStream(body)) using (var reader = new BinaryReader(memory)) using (var sha = new SHA256Managed()) { var serverKeyID = reader.ReadUInt64(); if (serverKeyID != AuthInfo.AuthKey.KeyID) { throw new Exception($"Server replied with an invalid auth key: {serverKeyID}"); } var MessageKey = reader.ReadBytes(16); (var AES_Key, var AES_ID) = CalcKey(AuthInfo.AuthKey.Key, MessageKey, client); body = AES.DecryptIGE(reader.ReadBytes(body.Length - 24), AES_Key, AES_ID); // https://core.telegram.org/mtproto/security_guidelines // Sections "checking sha256 hash" and "message length" var SHAHash = sha.ComputeHash(AuthInfo.AuthKey.Key .Skip(client ? 88 : 96).Take(32) .Concat(body) .ToArray()); var SHASlice = SHAHash.Skip(8).Take(16); if (!SHASlice.SequenceEqual(MessageKey)) { throw new Exception("The message key could not be validated."); } } TLObject obj = null; byte[] RawObject = null; var remote_msg_id = -1L; var remote_sequence = -1L; using (var memory = new MemoryStream(body)) using (var reader = new BinaryReader(memory)) { // The salt could be 0 if we are starting anew and haven't received one yet if (Salt != LongUtil.Deserialize(reader) && Salt != 0) { throw new Exception("The salt could not be validated"); } if (ID != LongUtil.Deserialize(reader)) { throw new Exception("The session ID could not be validated"); } remote_msg_id = LongUtil.Deserialize(reader); // ToDo: Check sequence_number remote_sequence = IntegerUtil.Deserialize(reader); RawObject = reader.ReadBytes(IntegerUtil.Deserialize(reader)); obj = TLObject.Deserialize(RawObject); } return(new TLObject(JToken.FromObject(new { _ = "Message", msg_id = remote_msg_id, seqno = remote_sequence, bytes = RawObject, body = JToken.Parse(obj.ToString()) }))); }
/// <summary> /// Deserilizes a PeerManager object from a stream /// </summary> /// <param name="reader">The stream containing the raw PeerManager data</param> public static PeerInfo[] Deserialize(BinaryReader reader) { return(Enumerable.Range(0, IntegerUtil.Deserialize(reader)) .Select(x => new PeerInfo(TLObject.Deserialize(reader))) .ToArray()); }
private void Sender_TLObjectReceivedEvent(object sender, TLObjectEventArgs e) { // Ignore any data that we aren't expecting if (CurrentState == AuthenticationState.NotStarted) { Logger.Log(Logger.Level.Debug, $"Received authentication data unexpectedly. Skipping. Try restarting the connection."); return; } else if (MTSender == null) { // How did we get here if there's no sender? Hmm? var sad = new Exception("Unable to find a connection to Telegram. Skipping. Try restarting the connection."); Logger.Log(sad); Response.TrySetException(sad); CurrentState = AuthenticationState.NotStarted; return; } switch (CurrentState) { case AuthenticationState.PQRequest: // Deserialize the data var ResPQ = e.TLObject; Logger.Log(Logger.Level.Debug, $"Received TLObject {ResPQ["_"]}."); // Factorize the PQ into two prime factors and fail if we can't if (!Helpers.FindPQ((byte[])ResPQ["pq"], out JToken factorizedPair)) { var sad = new Exception($"Unable to find any valid factors of PQ: {ResPQ["pq"]}"); Logger.Log(sad); Response.TrySetException(sad); MTSender.TLObjectReceivedEvent -= Sender_TLObjectReceivedEvent; CurrentState = AuthenticationState.NotStarted; Logger.Log(Logger.Level.Debug, $"Unsubscribed from {nameof(MTSender.TLObjectReceivedEvent)} event"); Logger.Log(Logger.Level.Info, $"Auth aborted."); return; } // Compile the PQ information PQInnerData = schema.p_q_inner_data(new { pq = factorizedPair["pq"], p = factorizedPair["min"], q = factorizedPair["max"], nonce = ResPQ["nonce"], server_nonce = ResPQ["server_nonce"], new_nonce = Helpers.GenerateRandomBytes(32) }); // Encrypt the PQ information. // NOTE: Because the server responded with potentially more than one fingerprint, // this also returns the fingerprint that we are using. (var fingerpint, var EncryptedInnerData) = MTProto.Crypto.RSA.Encrypt(ResPQ["server_public_key_fingerprints"].ToObject <long[]>(), PQInnerData.Serialize()); Logger.Log(Logger.Level.Debug, $"Using Public RSA Key: {fingerpint}"); // Get ready for the next response CurrentState = AuthenticationState.ServerDHRequest; Logger.Log(Logger.Level.Debug, $"Submitting PQ factor to server"); // Compile the rest of the information and send to the server for grading MTSender.Send(schema.req_DH_params(new { nonce = ResPQ["nonce"], server_nonce = ResPQ["server_nonce"], p = factorizedPair["min"], q = factorizedPair["max"], public_key_fingerprint = fingerpint, encrypted_data = EncryptedInnerData })); break; case AuthenticationState.ServerDHRequest: // Make sure we have all the needed info if (PQInnerData == null) { var sad = new Exception("Unable to find the PQInnerData object from previous steps. Please restart the connection process"); Logger.Log(sad); Response.TrySetException(sad); MTSender.TLObjectReceivedEvent -= Sender_TLObjectReceivedEvent; CurrentState = AuthenticationState.NotStarted; Logger.Log(Logger.Level.Debug, $"Unsubscribed from {nameof(MTSender.TLObjectReceivedEvent)} event"); Logger.Log(Logger.Level.Info, $"Auth aborted."); return; } // Deserialize the data var ServerDHParams = e.TLObject; Logger.Log(Logger.Level.Debug, $"Received TLObject {ServerDHParams["_"]}."); // Determine the result if ((string)ServerDHParams["_"] != "server_DH_params_ok") { // Aaaand we failed to handle things correctly var sad = new Exception($"The server responded with a {ServerDHParams["_"]} response. We are unable to continue."); Logger.Log(sad); Response.TrySetException(sad); MTSender.TLObjectReceivedEvent -= Sender_TLObjectReceivedEvent; CurrentState = AuthenticationState.NotStarted; Logger.Log(Logger.Level.Debug, $"Unsubscribed from {nameof(MTSender.TLObjectReceivedEvent)} event"); Logger.Log(Logger.Level.Info, $"Auth aborted."); return; } Logger.Log(Logger.Level.Debug, $"Decrypting the DHInnerData"); var key = AES.GenerateKeyDataFromNonces((byte[])ServerDHParams["server_nonce"], (byte[])PQInnerData["new_nonce"]); var plaintextAnswer = AES.DecryptAES(key, (byte[])ServerDHParams["encrypted_answer"]); using (var memory = new MemoryStream(plaintextAnswer)) using (var reader = new BinaryReader(memory)) { var hashsum = reader.ReadBytes(20); ServerDHInnerData = TLObject.Deserialize(reader); } if (ServerDHInnerData == null) { var sad = new Exception("The server did not respond with valid DH Inner Data. We are unable to continue."); Logger.Log(sad); Response.TrySetException(sad); MTSender.TLObjectReceivedEvent -= Sender_TLObjectReceivedEvent; CurrentState = AuthenticationState.NotStarted; Logger.Log(Logger.Level.Debug, $"Unsubscribed from {nameof(MTSender.TLObjectReceivedEvent)} event"); Logger.Log(Logger.Level.Info, $"Auth aborted."); return; } if ((string)ServerDHInnerData["_"] != "server_DH_inner_data") { var sad = new Exception($"The server responded with a {ServerDHInnerData["_"]} response. We are unable to continue."); Logger.Log(sad); Response.TrySetException(sad); MTSender.TLObjectReceivedEvent -= Sender_TLObjectReceivedEvent; CurrentState = AuthenticationState.NotStarted; Logger.Log(Logger.Level.Debug, $"Unsubscribed from {nameof(MTSender.TLObjectReceivedEvent)} event"); Logger.Log(Logger.Level.Info, $"Auth aborted."); return; } Logger.Log(Logger.Level.Debug, $"Decrypted TLObject {ServerDHInnerData["_"]}."); TimeOffset = (int)ServerDHInnerData["server_time"] - (int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds; Logger.Log(Logger.Level.Debug, $"Updated Time Offset {TimeOffset}."); Logger.Log(Logger.Level.Debug, $"Calculating GAB"); var b = new BigInteger(1, Helpers.GenerateRandomBytes(256)); var gb = BigInteger.ValueOf((int)ServerDHInnerData["g"]).ModPow(b, new BigInteger(1, (byte[])ServerDHInnerData["dh_prime"])); gab = new BigInteger(1, (byte[])ServerDHInnerData["g_a"]).ModPow(b, new BigInteger(1, (byte[])ServerDHInnerData["dh_prime"])); TLObject ClientDHInnerData = schema.client_DH_inner_data(new { nonce = ServerDHInnerData["nonce"], server_nonce = ServerDHInnerData["server_nonce"], retry_id = 0L, g_b = gb.ToByteArrayUnsigned() }); var ClientDHInnerData_raw = ClientDHInnerData.Serialize(); byte[] ClientDHInnerDataHash = null; using (var memory = new MemoryStream()) using (var writer = new BinaryWriter(memory)) using (var sha = SHA1.Create()) { writer.Write(sha.ComputeHash(ClientDHInnerData_raw)); writer.Write(ClientDHInnerData_raw); ClientDHInnerDataHash = memory.ToArray(); } Logger.Log(Logger.Level.Debug, $"Sending ClientDHRequest"); // Get ready to the next response CurrentState = AuthenticationState.ClientDHRequest; MTSender.Send(schema.set_client_DH_params(new { nonce = ServerDHInnerData["nonce"], server_nonce = ServerDHInnerData["server_nonce"], encrypted_data = AES.EncryptAES(key, ClientDHInnerDataHash) })); break; case AuthenticationState.ClientDHRequest: // Make sure we have all the needed info if (gab == null) { var sad = new Exception("Unable to find the GAB object from previous steps. Please restart the connection process"); Logger.Log(sad); Response.TrySetException(sad); MTSender.TLObjectReceivedEvent -= Sender_TLObjectReceivedEvent; CurrentState = AuthenticationState.NotStarted; Logger.Log(Logger.Level.Debug, $"Unsubscribed from {nameof(MTSender.TLObjectReceivedEvent)} event"); Logger.Log(Logger.Level.Info, $"Auth aborted."); return; } else if (TimeOffset == null) { var sad = new Exception("Unable to find the TimeOffset from previous steps. Please restart the connection process"); Logger.Log(sad); Response.TrySetException(sad); MTSender.TLObjectReceivedEvent -= Sender_TLObjectReceivedEvent; CurrentState = AuthenticationState.NotStarted; Logger.Log(Logger.Level.Debug, $"Unsubscribed from {nameof(MTSender.TLObjectReceivedEvent)} event"); Logger.Log(Logger.Level.Info, $"Auth aborted."); return; } var SetClientDHParamsAnswer = e.TLObject; var AuthKey = new AuthKey(gab); var newNonceHashCalculated = AuthKey.CalcNewNonceHash((byte[])PQInnerData["new_nonce"], 1); Logger.Log(Logger.Level.Debug, $"Received TLObject {SetClientDHParamsAnswer["_"]}."); if (!((byte[])SetClientDHParamsAnswer["new_nonce_hash1"]).SequenceEqual(newNonceHashCalculated)) { var sad = new Exception("The server returned an invalid new nonce hash 1. Please restart the connection process"); Logger.Log(sad); Response.TrySetException(sad); MTSender.TLObjectReceivedEvent -= Sender_TLObjectReceivedEvent; CurrentState = AuthenticationState.NotStarted; Logger.Log(Logger.Level.Debug, $"Unsubscribed from {nameof(MTSender.TLObjectReceivedEvent)} event"); Logger.Log(Logger.Level.Info, $"Auth aborted."); return; } Logger.Log(Logger.Level.Info, $"Successfully negotiated authorization with the server"); MTSender.TLObjectReceivedEvent -= Sender_TLObjectReceivedEvent; Response.TrySetResult(new ServerAuthentication { AuthKey = AuthKey, TimeOffset = (int)TimeOffset }); break; } }