Esempio n. 1
0
        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);
            }
        }
Esempio n. 2
0
 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);
     }
 }
Esempio n. 3
0
        /// <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);
        }
Esempio n. 4
0
        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);
            }
        }
Esempio n. 5
0
        /// <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())
            })));
        }
Esempio n. 6
0
 /// <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());
 }
Esempio n. 7
0
        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;
            }
        }