diff --git a/EXAMPLES.md b/EXAMPLES.md index 3b64eca..f663838 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -469,6 +469,9 @@ var msg = history.Messages[0] as Message; // last message of source chat // • Forward the message (only the source message id is necessary) await client.Messages_ForwardMessages(from_chat, new[] { msg.ID }, new[] { WTelegram.Helpers.RandomLong() }, to_chat); -// • Copy the message (without the "Forwarded" header) +// • Copy the message without the "Forwarded" header (only the source message id is necessary) +await client.Messages_ForwardMessages(from_chat, new[] { msg.ID }, new[] { WTelegram.Helpers.RandomLong() }, to_chat, drop_author: true); + +// • Alternative solution to copy the message (the full message is needed) await client.SendMessageAsync(to_chat, msg.message, msg.media?.ToInputMedia(), entities: msg.entities); -``` \ No newline at end of file +``` diff --git a/src/Client.cs b/src/Client.cs index abfcea9..10c61c4 100644 --- a/src/Client.cs +++ b/src/Client.cs @@ -1180,7 +1180,7 @@ namespace WTelegram try { using var memStream = new MemoryStream(1024); - using var writer = new BinaryWriter(memStream, Encoding.UTF8); + using var writer = new BinaryWriter(memStream); writer.Write(0); // int32 payload_len (to be patched with payload length) if (_dcSession.AuthKeyID == 0) // send unencrypted message @@ -1196,7 +1196,7 @@ namespace WTelegram else { using var clearStream = new MemoryStream(1024); - using var clearWriter = new BinaryWriter(clearStream, Encoding.UTF8); + using var clearWriter = new BinaryWriter(clearStream); clearWriter.Write(_dcSession.AuthKey, 88, 32); clearWriter.Write(_dcSession.Salt); // int64 salt clearWriter.Write(_dcSession.Id); // int64 session_id @@ -1210,7 +1210,7 @@ namespace WTelegram clearWriter.WriteTLObject(msg); // bytes message_data int clearLength = (int)clearStream.Length - 32; // length before padding (= 32 + message_data_length) int padding = (0x7FFFFFF0 - clearLength) % 16; - padding += _random.Next(1, 64) * 16; // MTProto 2.0 padding must be between 12..1024 with total length divisible by 16 + padding += _random.Next(2, 16) * 16; // MTProto 2.0 padding must be between 12..1024 with total length divisible by 16 clearStream.SetLength(32 + clearLength + padding); byte[] clearBuffer = clearStream.GetBuffer(); BinaryPrimitives.WriteInt32LittleEndian(clearBuffer.AsSpan(60), clearLength - 32); // patch message_data_length diff --git a/src/Encryption.cs b/src/Encryption.cs index 774df0d..8cec009 100644 --- a/src/Encryption.cs +++ b/src/Encryption.cs @@ -62,7 +62,7 @@ namespace WTelegram { //4.1) RSA_PAD(data, server_public_key) using var clearStream = new MemoryStream(256); - using var writer = new BinaryWriter(clearStream, Encoding.UTF8); + using var writer = new BinaryWriter(clearStream); byte[] aes_key = new byte[32], zero_iv = new byte[32]; var n = BigEndianInteger(publicKey.n); do @@ -108,16 +108,17 @@ namespace WTelegram if (serverDHinnerData.server_nonce != resPQ.server_nonce) throw new ApplicationException("Server Nonce mismatch"); var g_a = BigEndianInteger(serverDHinnerData.g_a); var dh_prime = BigEndianInteger(serverDHinnerData.dh_prime); - ValidityChecks(dh_prime, serverDHinnerData.g); + CheckGoodPrime(dh_prime, serverDHinnerData.g); session.LastSentMsgId = 0; session.ServerTicksOffset = (serverDHinnerData.server_time - localTime).Ticks; Helpers.Log(1, $"Time offset: {session.ServerTicksOffset} | Server: {serverDHinnerData.server_time.TimeOfDay} UTC | Local: {localTime.TimeOfDay} UTC"); //6) - var bData = new byte[256]; - RNG.GetBytes(bData); - var b = BigEndianInteger(bData); + var salt = new byte[256]; + RNG.GetBytes(salt); + var b = BigEndianInteger(salt); var g_b = BigInteger.ModPow(serverDHinnerData.g, b, dh_prime); - ValidityChecksDH(g_a, g_b, dh_prime); + CheckGoodGaAndGb(g_a, dh_prime); + CheckGoodGaAndGb(g_b, dh_prime); var clientDHinnerData = new ClientDHInnerData { nonce = nonce, @@ -128,7 +129,7 @@ namespace WTelegram { using var clearStream = new MemoryStream(384); clearStream.Position = 20; // skip SHA1 area (to be patched) - using var writer = new BinaryWriter(clearStream, Encoding.UTF8); + using var writer = new BinaryWriter(clearStream); writer.WriteTLObject(clientDHinnerData); int clearLength = (int)clearStream.Length; // length before padding (= 20 + message_data_length) int paddingToAdd = (0x7FFFFFF0 - clearLength) % 16; @@ -182,21 +183,20 @@ namespace WTelegram } } - internal static void ValidityChecks(BigInteger p, int g) + internal static void CheckGoodPrime(BigInteger p, int g) { Helpers.Log(2, "Verifying encryption key safety... (this should happen only once per DC)"); // check that 2^2047 <= p < 2^2048 if (p.GetBitLength() != 2048) throw new ApplicationException("p is not 2048-bit number"); // check that g generates a cyclic subgroup of prime order (p - 1) / 2, i.e. is a quadratic residue mod p. - BigInteger mod_r; if (g switch { 2 => p % 8 != 7, 3 => p % 3 != 2, 4 => false, - 5 => (mod_r = p % 5) != 1 && mod_r != 4, - 6 => (mod_r = p % 24) != 19 && mod_r != 23, - 7 => (mod_r = p % 7) != 3 && mod_r != 5 && mod_r != 6, + 5 => (int)(p % 5) is not 1 and not 4, + 6 => (int)(p % 24) is not 19 and not 23, + 7 => (int)(p % 7) is not 3 and not 5 and not 6, _ => true, }) throw new ApplicationException("Bad prime mod 4g"); @@ -227,13 +227,11 @@ namespace WTelegram 0x73, 0x3F, 0xF1, 0x70, 0x2F, 0x52, 0x6C, 0x8E, 0x04, 0xC9, 0xB1, 0xC6, 0xB9, 0xAE, 0x1C, 0xC7, 0x00 })}; - internal static void ValidityChecksDH(BigInteger g_a, BigInteger g_b, BigInteger dh_prime) + internal static void CheckGoodGaAndGb(BigInteger g, BigInteger dh_prime) { // check that g, g_a and g_b are greater than 1 and less than dh_prime - 1. // We recommend checking that g_a and g_b are between 2^{2048-64} and dh_prime - 2^{2048-64} as well. - var l = BigInteger.One << (2048 - 64); - var r = dh_prime - l; - if (g_a < l || g_a > r || g_b < l || g_b > r) + if (g.GetBitLength() < 2048 - 64 || (dh_prime - g).GetBitLength() < 2048 - 64) throw new ApplicationException("g^a or g^b is not between 2^{2048-64} and dh_prime - 2^{2048-64}"); } @@ -405,7 +403,7 @@ j4WcDuXc2CTHgH8gFTNhp/Y8/SpDOhvn9QIDAQAB var g = new BigInteger(algo.g); var p = BigEndianInteger(algo.p); - var validTask = Task.Run(() => ValidityChecks(p, algo.g)); + var validTask = Task.Run(() => CheckGoodPrime(p, algo.g)); System.Threading.Thread.Sleep(100); Helpers.Log(3, $"This account has enabled 2FA. A password is needed. {accountPassword.hint}"); diff --git a/src/TL.Schema.cs b/src/TL.Schema.cs index c930ea4..bcce540 100644 --- a/src/TL.Schema.cs +++ b/src/TL.Schema.cs @@ -4699,6 +4699,7 @@ namespace TL cdn = 0x8, /// If set, this IP should be used when connecting through a proxy static_ = 0x10, + /// If set, clients must connect using only the specified port, without trying any other port. this_port_only = 0x20, /// Field has a value has_secret = 0x400, @@ -4832,7 +4833,7 @@ namespace TL has_static_maps_provider = 0x1000, /// Whether pfs was used pfs_enabled = 0x2000, - /// Whether to forcefully try connecting using IPv6 + /// Whether to forcefully connect using IPv6 , even if the client knows that IPv4 is available. force_try_ipv6 = 0x4000, /// Field has a value has_reactions_default = 0x8000, @@ -12215,7 +12216,6 @@ namespace TL has_reply_to_peer_id = 0x1, /// Field has a value has_reply_to_top_id = 0x2, - /// Whether this message replies to a scheduled message reply_to_scheduled = 0x4, } } @@ -13505,7 +13505,7 @@ namespace TL [TLDef(0x661D4037)] public class ChatReactionsSome : ChatReactions { - /// Allowed reactions + /// Allowed set of reactions: the reactions_in_chat_max configuration field indicates the maximum number of reactions that can be specified in this field. public Reaction[] reactions; } diff --git a/src/TL.SchemaFuncs.cs b/src/TL.SchemaFuncs.cs index d6c90b4..4a02c8c 100644 --- a/src/TL.SchemaFuncs.cs +++ b/src/TL.SchemaFuncs.cs @@ -869,7 +869,7 @@ namespace TL /// Get theme information See Possible codes: 400 (details) /// Theme format, a string that identifies the theming engines supported by the client /// Theme - /// Document ID + /// Deprecated: should always be 0 public static Task Account_GetTheme(this Client client, string format, InputThemeBase theme, long document_id) => client.Invoke(new Account_GetTheme { @@ -1931,7 +1931,7 @@ namespace TL unsave = unsave, }); - /// Query an inline bot See Possible codes: -503,400 (details) + /// Query an inline bot See Possible codes: 400,-503 (details) /// The bot to query /// The currently opened chat /// The geolocation, if requested @@ -2042,7 +2042,7 @@ namespace TL entities = entities, }); - /// Press an inline callback button and get a callback answer from the bot See Possible codes: -503,400 (details) + /// Press an inline callback button and get a callback answer from the bot See Possible codes: 400,-503 (details) /// Whether this is a "play game" button /// Where was the inline keyboard sent /// ID of the Message with the inline keyboard @@ -2408,7 +2408,7 @@ namespace TL /// Whether to move used stickersets to top, see here for more info on this flag » /// The destination chat /// The message to reply to - /// The medias to send + /// The medias to send: note that they must be separately uploaded using messages.uploadMedia first, using raw inputMediaUploaded* constructors is not supported. /// Scheduled message date for scheduled messages /// Send this message as the specified peer public static Task Messages_SendMultiMedia(this Client client, InputPeer peer, InputSingleMedia[] multi_media, bool silent = false, bool background = false, bool clear_draft = false, bool noforwards = false, bool update_stickersets_order = false, int? reply_to_msg_id = null, DateTime? schedule_date = null, InputPeer send_as = null) @@ -3115,7 +3115,7 @@ namespace TL hash = hash, }); - /// Change default emoji reaction to use in the quick reaction menu: the value is synced across devices and can be fetched using help.getAppConfig, reactions_default field. See Possible codes: 400 (details) + /// Change default emoji reaction to use in the quick reaction menu: the value is synced across devices and can be fetched using help.getConfig, reactions_default field. See Possible codes: 400 (details) /// New emoji reaction public static Task Messages_SetDefaultReaction(this Client client, Reaction reaction) => client.Invoke(new Messages_SetDefaultReaction