Improved Secret Chats:

Support layer 144 : big documents, silent
(layer not yet supported by other clients)
List of ISecretChat with detailed properties
Fix PFS issue losing a message...
This commit is contained in:
Wizou 2022-11-11 00:33:29 +01:00
parent 8fa00a8cc6
commit a038be87af
9 changed files with 326 additions and 275 deletions

2
.github/dev.yml vendored
View file

@ -2,7 +2,7 @@ pr: none
trigger: trigger:
- master - master
name: 3.0.4-dev.$(Rev:r) name: 3.1.1-dev.$(Rev:r)
pool: pool:
vmImage: ubuntu-latest vmImage: ubuntu-latest

2
.github/release.yml vendored
View file

@ -1,7 +1,7 @@
pr: none pr: none
trigger: none trigger: none
name: 3.0.$(Rev:r) name: 3.1.$(Rev:r)
pool: pool:
vmImage: ubuntu-latest vmImage: ubuntu-latest

View file

@ -4,23 +4,24 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using TL; using TL;
using WTelegram;
namespace WTelegramClientTest namespace WTelegramClientTest
{ {
static class Program_SecretChats static class Program_SecretChats
{ {
static WTelegram.Client Client; static Client Client;
static WTelegram.SecretChats Secrets; static SecretChats Secrets;
static InputEncryptedChat ActiveChat; // the secret chat currently selected static ISecretChat ActiveChat; // the secret chat currently selected
static readonly Dictionary<long, User> Users = new(); static readonly Dictionary<long, User> Users = new();
static readonly Dictionary<long, ChatBase> Chats = new(); static readonly Dictionary<long, ChatBase> Chats = new();
// go to Project Properties > Debug > Environment variables and add at least these: api_id, api_hash, phone_number // go to Project Properties > Debug > Environment variables and add at least these: api_id, api_hash, phone_number
static async Task Main() static async Task Main()
{ {
WTelegram.Helpers.Log = (l, s) => System.Diagnostics.Debug.WriteLine(s); Helpers.Log = (l, s) => System.Diagnostics.Debug.WriteLine(s);
Client = new WTelegram.Client(Environment.GetEnvironmentVariable); Client = new Client(Environment.GetEnvironmentVariable);
Secrets = new WTelegram.SecretChats(Client, "Secrets.bin"); Secrets = new SecretChats(Client, "Secrets.bin");
AppDomain.CurrentDomain.ProcessExit += (s, e) => { Secrets.Dispose(); Client.Dispose(); }; AppDomain.CurrentDomain.ProcessExit += (s, e) => { Secrets.Dispose(); Client.Dispose(); };
SelectActiveChat(); SelectActiveChat();
@ -46,9 +47,9 @@ Type a command, or a message to send to the active secret chat:");
var line = Console.ReadLine(); var line = Console.ReadLine();
if (line.StartsWith('/')) if (line.StartsWith('/'))
{ {
if (line == "/discard delete") { await Secrets.Discard(ActiveChat, true); SelectActiveChat(); } if (line == "/discard delete") { await Secrets.Discard(ActiveChat.ChatId, true); SelectActiveChat(); }
else if (line == "/discard") { await Secrets.Discard(ActiveChat, false); SelectActiveChat(); } else if (line == "/discard") { await Secrets.Discard(ActiveChat.ChatId, false); SelectActiveChat(); }
else if (line == "/read") await Client.Messages_ReadEncryptedHistory(ActiveChat, DateTime.UtcNow); else if (line == "/read") await Client.Messages_ReadEncryptedHistory(ActiveChat.Peer, DateTime.UtcNow);
else if (line == "/users") foreach (var user in Users.Values) Console.WriteLine($"{user.id,-10} {user}"); else if (line == "/users") foreach (var user in Users.Values) Console.WriteLine($"{user.id,-10} {user}");
else if (line.StartsWith("/select ")) SelectActiveChat(int.Parse(line[8..])); else if (line.StartsWith("/select ")) SelectActiveChat(int.Parse(line[8..]));
else if (line.StartsWith("/request ")) else if (line.StartsWith("/request "))
@ -58,15 +59,15 @@ Type a command, or a message to send to the active secret chat:");
Console.WriteLine("User not found"); Console.WriteLine("User not found");
else if (line.StartsWith("/photo ")) else if (line.StartsWith("/photo "))
{ {
var media = new TL.Layer45.DecryptedMessageMediaPhoto { caption = line[7..] }; var media = new TL.Layer46.DecryptedMessageMediaPhoto { caption = line[7..] };
var file = await Secrets.UploadFile(File.OpenRead(line[7..]), media); var file = await Secrets.UploadFile(File.OpenRead(line[7..]), media);
var sent = await Secrets.SendMessage(ActiveChat, new TL.Layer73.DecryptedMessage { random_id = WTelegram.Helpers.RandomLong(), var sent = await Secrets.SendMessage(ActiveChat.ChatId, new TL.Layer73.DecryptedMessage { random_id = Helpers.RandomLong(),
media = media, flags = TL.Layer73.DecryptedMessage.Flags.has_media }, file: file); media = media, flags = TL.Layer73.DecryptedMessage.Flags.has_media }, file: file);
} }
else Console.WriteLine("Unrecognized command"); else Console.WriteLine("Unrecognized command");
} }
else if (ActiveChat == null) Console.WriteLine("No active secret chat"); else if (ActiveChat == null) Console.WriteLine("No active secret chat");
else await Secrets.SendMessage(ActiveChat, new TL.Layer73.DecryptedMessage { message = line, random_id = WTelegram.Helpers.RandomLong() }); else await Secrets.SendMessage(ActiveChat.ChatId, new TL.Layer73.DecryptedMessage { message = line, random_id = Helpers.RandomLong() });
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -86,7 +87,7 @@ Type a command, or a message to send to the active secret chat:");
await Secrets.HandleUpdate(ue); await Secrets.HandleUpdate(ue);
break; break;
case UpdateNewEncryptedMessage unem: // Encrypted message or service message: case UpdateNewEncryptedMessage unem: // Encrypted message or service message:
if (unem.message.ChatId != ActiveChat?.chat_id) SelectActiveChat(unem.message.ChatId); if (unem.message.ChatId != ActiveChat?.ChatId) SelectActiveChat(unem.message.ChatId);
foreach (var msg in Secrets.DecryptMessage(unem.message)) foreach (var msg in Secrets.DecryptMessage(unem.message))
{ {
if (msg.Media != null && unem.message is EncryptedMessage { file: EncryptedFile ef }) if (msg.Media != null && unem.message is EncryptedMessage { file: EncryptedFile ef })
@ -110,8 +111,8 @@ Type a command, or a message to send to the active secret chat:");
private static void SelectActiveChat(int newActiveChat = 0) private static void SelectActiveChat(int newActiveChat = 0)
{ {
ActiveChat = Secrets.Peers.FirstOrDefault(sc => newActiveChat == 0 || sc.chat_id == newActiveChat); ActiveChat = Secrets.Chats.FirstOrDefault(sc => newActiveChat == 0 || sc.ChatId == newActiveChat);
Console.WriteLine("Active secret chat ID: " + ActiveChat?.chat_id); Console.WriteLine("Active secret chat ID: " + ActiveChat?.ChatId);
} }
} }
} }

2
FAQ.md
View file

@ -140,7 +140,7 @@ Some additional advices from me:
5. Avoid repetitive polling or repetitive sequence of actions/requests: Save the initial results of your queries, and update those results when you're informed of a change through `OnUpdate` events. 5. Avoid repetitive polling or repetitive sequence of actions/requests: Save the initial results of your queries, and update those results when you're informed of a change through `OnUpdate` events.
6. Don't buy fake user accounts/sessions and don't extract api_id/hash/authkey/sessions from official clients, this is [specifically forbidden by API TOS](https://core.telegram.org/api/terms#2-transparency). You must use your own api_id and create your own sessions associated with it. 6. Don't buy fake user accounts/sessions and don't extract api_id/hash/authkey/sessions from official clients, this is [specifically forbidden by API TOS](https://core.telegram.org/api/terms#2-transparency). You must use your own api_id and create your own sessions associated with it.
7. If a phone number is brand new, it will be closely monitored by Telegram for abuse, and it can even already be considered a bad user due to bad behavior from the previous owner of that phone number (which may happens often with VoIP or other easy-to-buy-online numbers, so expect fast ban) 7. If a phone number is brand new, it will be closely monitored by Telegram for abuse, and it can even already be considered a bad user due to bad behavior from the previous owner of that phone number (which may happen often with VoIP or other easy-to-buy-online numbers, so expect fast ban)
8. You may want to use your new phone number account with an official Telegram client and act like a normal user for some time (some weeks/months), before using it for automation with WTelegramClient. 8. You may want to use your new phone number account with an official Telegram client and act like a normal user for some time (some weeks/months), before using it for automation with WTelegramClient.
9. When creating a new API ID/Hash, I recommend you use your own phone number with long history of normal Telegram usage, rather than a brand new phone number with short history. 9. When creating a new API ID/Hash, I recommend you use your own phone number with long history of normal Telegram usage, rather than a brand new phone number with short history.
In particular, DON'T create an API ID/Hash for every phone numbers you will control. One API ID/Hash represents your application, which can be used to control several user accounts. In particular, DON'T create an API ID/Hash for every phone numbers you will control. One API ID/Hash represents your application, which can be used to control several user accounts.

View file

@ -531,7 +531,7 @@ j4WcDuXc2CTHgH8gFTNhp/Y8/SpDOhvn9QIDAQAB
private readonly ICryptoTransform aesCrypto; private readonly ICryptoTransform aesCrypto;
private readonly byte[] prevBytes; private readonly byte[] prevBytes;
public AES_IGE_Stream(Stream stream, int size, byte[] key, byte[] iv) : this(stream, key, iv, false) { ContentLength = size; } public AES_IGE_Stream(Stream stream, long size, byte[] key, byte[] iv) : this(stream, key, iv, false) { ContentLength = size; }
public AES_IGE_Stream(Stream stream, byte[] key, byte[] iv, bool encrypt) : base(stream) public AES_IGE_Stream(Stream stream, byte[] key, byte[] iv, bool encrypt) : base(stream)
{ {
aesCrypto = encrypt ? Encryption.AesECB.CreateEncryptor(key, null) : Encryption.AesECB.CreateDecryptor(key, null); aesCrypto = encrypt ? Encryption.AesECB.CreateEncryptor(key, null) : Encryption.AesECB.CreateDecryptor(key, null);

View file

@ -13,6 +13,14 @@ using static WTelegram.Encryption;
namespace WTelegram namespace WTelegram
{ {
public interface ISecretChat
{
int ChatId { get; }
long RemoteUserId { get; }
InputEncryptedChat Peer { get; }
int RemoteLayer { get; }
}
public sealed class SecretChats : IDisposable public sealed class SecretChats : IDisposable
{ {
public event Action OnChanged; public event Action OnChanged;
@ -28,7 +36,7 @@ namespace WTelegram
private const int ThresholdPFS = 100; private const int ThresholdPFS = 100;
[TLDef(0xFEFEFEFE)] [TLDef(0xFEFEFEFE)]
internal class SecretChat : IObject internal class SecretChat : IObject, ISecretChat
{ {
[Flags] public enum Flags : uint { requestChat = 1, renewKey = 2, acceptKey = 4, originator = 8, commitKey = 16 } [Flags] public enum Flags : uint { requestChat = 1, renewKey = 2, acceptKey = 4, originator = 8, commitKey = 16 }
public Flags flags; public Flags flags;
@ -43,8 +51,12 @@ namespace WTelegram
public long exchange_id; public long exchange_id;
public int ChatId => peer.chat_id; public int ChatId => peer.chat_id;
public long RemoteUserId => participant_id;
public InputEncryptedChat Peer => peer;
public int RemoteLayer => remoteLayer;
internal long key_fingerprint; internal long key_fingerprint;
internal SortedList<int, TL.Layer17.DecryptedMessageLayer> pendingMsgs = new(); internal SortedList<int, TL.Layer23.DecryptedMessageLayer> pendingMsgs = new();
internal void Discarded() // clear out fields for more security internal void Discarded() // clear out fields for more security
{ {
Array.Clear(authKey, 0, authKey.Length); Array.Clear(authKey, 0, authKey.Length);
@ -67,13 +79,7 @@ namespace WTelegram
} }
public void Dispose() { OnChanged?.Invoke(); storage?.Dispose(); sha256.Dispose(); sha1.Dispose(); } public void Dispose() { OnChanged?.Invoke(); storage?.Dispose(); sha256.Dispose(); sha1.Dispose(); }
public List<InputEncryptedChat> Peers => chats.Values.Select(sc => sc.peer).ToList(); public List<ISecretChat> Chats => chats.Values.ToList<ISecretChat>();
/// <summary>Return secret chats with the given remote user ID</summary>
/// <param name="participant_id">remote user ID</param>
/// <returns>List of matching secret chat ids/access_hash</returns>
public List<InputEncryptedChat> FindChatsByParticipant(long participant_id)
=> chats.Where(kvp => kvp.Value.participant_id == participant_id).Select(kvp => kvp.Value.peer).ToList();
public bool IsChatActive(int chat_id) => !(chats.GetValueOrDefault(chat_id)?.flags.HasFlag(SecretChat.Flags.requestChat) ?? true); public bool IsChatActive(int chat_id) => !(chats.GetValueOrDefault(chat_id)?.flags.HasFlag(SecretChat.Flags.requestChat) ?? true);
@ -253,16 +259,16 @@ namespace WTelegram
private async Task SendNotifyLayer(SecretChat chat) private async Task SendNotifyLayer(SecretChat chat)
{ {
await SendMessage(chat.ChatId, new TL.Layer17.DecryptedMessageService { random_id = Helpers.RandomLong(), await SendMessage(chat.ChatId, new TL.Layer23.DecryptedMessageService { random_id = Helpers.RandomLong(),
action = new TL.Layer17.DecryptedMessageActionNotifyLayer { layer = Layer.SecretChats } }); action = new TL.Layer23.DecryptedMessageActionNotifyLayer { layer = Layer.SecretChats } });
if (chat.remoteLayer < Layer.MTProto2) chat.remoteLayer = Layer.MTProto2; if (chat.remoteLayer < Layer.MTProto2) chat.remoteLayer = Layer.MTProto2;
} }
/// <summary>Encrypt and send a message on a secret chat</summary> /// <summary>Encrypt and send a message on a secret chat</summary>
/// <remarks>You would typically pass an instance of <see cref="TL.Layer73.DecryptedMessage"/> or <see cref="TL.Layer17.DecryptedMessageService"/> that you created and filled /// <remarks>You would typically pass an instance of <see cref="TL.Layer73.DecryptedMessage"/> or <see cref="TL.Layer23.DecryptedMessageService"/> that you created and filled
/// <br/>Remember to fill <c>random_id</c> with <see cref="WTelegram.Helpers.RandomLong"/>, and the <c>flags</c> field if necessary</remarks> /// <br/>Remember to fill <c>random_id</c> with <see cref="WTelegram.Helpers.RandomLong"/>, and the <c>flags</c> field if necessary</remarks>
/// <param name="chatId">Secret Chat ID</param> /// <param name="chatId">Secret Chat ID</param>
/// <param name="msg">The pre-filled <see cref="TL.Layer73.DecryptedMessage">DecryptedMessage</see> or <see cref="TL.Layer17.DecryptedMessageService">DecryptedMessageService </see> to send</param> /// <param name="msg">The pre-filled <see cref="TL.Layer73.DecryptedMessage">DecryptedMessage</see> or <see cref="TL.Layer23.DecryptedMessageService">DecryptedMessageService </see> to send</param>
/// <param name="silent">Send encrypted message without a notification</param> /// <param name="silent">Send encrypted message without a notification</param>
/// <param name="file">Optional file attachment. See method <see cref="UploadFile">UploadFile</see></param> /// <param name="file">Optional file attachment. See method <see cref="UploadFile">UploadFile</see></param>
/// <returns>Confirmation of sent message</returns> /// <returns>Confirmation of sent message</returns>
@ -271,7 +277,7 @@ namespace WTelegram
if (!chats.TryGetValue(chatId, out var chat)) throw new ApplicationException("Secret chat not found"); if (!chats.TryGetValue(chatId, out var chat)) throw new ApplicationException("Secret chat not found");
try try
{ {
var dml = new TL.Layer17.DecryptedMessageLayer var dml = new TL.Layer23.DecryptedMessageLayer
{ {
layer = Math.Min(chat.remoteLayer, Layer.SecretChats), layer = Math.Min(chat.remoteLayer, Layer.SecretChats),
random_bytes = new byte[15], random_bytes = new byte[15],
@ -290,7 +296,7 @@ namespace WTelegram
} }
} }
private async Task<Messages_SentEncryptedMessage> SendMessage(SecretChat chat, TL.Layer17.DecryptedMessageLayer dml, bool silent = false, InputEncryptedFileBase file = null) private async Task<Messages_SentEncryptedMessage> SendMessage(SecretChat chat, TL.Layer23.DecryptedMessageLayer dml, bool silent = false, InputEncryptedFileBase file = null)
{ {
RNG.GetBytes(dml.random_bytes); RNG.GetBytes(dml.random_bytes);
int x = 8 - (int)(chat.flags & SecretChat.Flags.originator); int x = 8 - (int)(chat.flags & SecretChat.Flags.originator);
@ -321,7 +327,7 @@ namespace WTelegram
CheckPFS(chat); CheckPFS(chat);
if (file != null) if (file != null)
return await client.Messages_SendEncryptedFile(chat.peer, dml.message.RandomId, data, file, silent); return await client.Messages_SendEncryptedFile(chat.peer, dml.message.RandomId, data, file, silent);
else if (dml.message is TL.Layer17.DecryptedMessageService or TL.Layer8.DecryptedMessageService) else if (dml.message is TL.Layer23.DecryptedMessageService or TL.Layer8.DecryptedMessageService)
return await client.Messages_SendEncryptedService(chat.peer, dml.message.RandomId, data); return await client.Messages_SendEncryptedService(chat.peer, dml.message.RandomId, data);
else else
return await client.Messages_SendEncrypted(chat.peer, dml.message.RandomId, data, silent); return await client.Messages_SendEncrypted(chat.peer, dml.message.RandomId, data, silent);
@ -360,7 +366,7 @@ namespace WTelegram
/// <summary>Decrypt an encrypted message obtained in <see cref="UpdateNewEncryptedMessage"/></summary> /// <summary>Decrypt an encrypted message obtained in <see cref="UpdateNewEncryptedMessage"/></summary>
/// <param name="msg">Encrypted <see cref="UpdateNewEncryptedMessage.message"/></param> /// <param name="msg">Encrypted <see cref="UpdateNewEncryptedMessage.message"/></param>
/// <param name="fillGaps">If messages are missing or received in wrong order, automatically request to resend missing messages</param> /// <param name="fillGaps">If messages are missing or received in wrong order, automatically request to resend missing messages</param>
/// <returns>An array of <see cref="TL.Layer73.DecryptedMessage">DecryptedMessage</see> or <see cref="TL.Layer17.DecryptedMessageService">DecryptedMessageService </see> from various TL.LayerXX namespaces.<br/> /// <returns>An array of <see cref="TL.Layer73.DecryptedMessage">DecryptedMessage</see> or <see cref="TL.Layer23.DecryptedMessageService">DecryptedMessageService </see> from various TL.LayerXX namespaces.<br/>
/// You can use the generic properties to access their fields /// You can use the generic properties to access their fields
/// <para>May return an empty array if msg was already previously received or is not the next message in sequence. /// <para>May return an empty array if msg was already previously received or is not the next message in sequence.
/// <br/>May return multiple messages if missing messages are finally received (using <paramref name="fillGaps"/> = true)</para></returns> /// <br/>May return multiple messages if missing messages are finally received (using <paramref name="fillGaps"/> = true)</para></returns>
@ -371,7 +377,7 @@ namespace WTelegram
try try
{ {
var obj = Decrypt(chat, msg.Bytes, msg.Bytes.Length); var obj = Decrypt(chat, msg.Bytes, msg.Bytes.Length);
if (obj is not TL.Layer17.DecryptedMessageLayer dml) throw new ApplicationException("Decrypted object is not DecryptedMessageLayer"); if (obj is not TL.Layer23.DecryptedMessageLayer dml) throw new ApplicationException("Decrypted object is not DecryptedMessageLayer");
if (dml.random_bytes.Length < 15) throw new ApplicationException("Not enough random_bytes"); if (dml.random_bytes.Length < 15) throw new ApplicationException("Not enough random_bytes");
if (((dml.out_seq_no ^ dml.in_seq_no) & 1) != 1 || ((dml.out_seq_no ^ chat.in_seq_no) & 1) != 0) throw new ApplicationException("Invalid seq_no parities"); if (((dml.out_seq_no ^ dml.in_seq_no) & 1) != 1 || ((dml.out_seq_no ^ chat.in_seq_no) & 1) != 0) throw new ApplicationException("Invalid seq_no parities");
if (dml.layer > chat.remoteLayer) chat.remoteLayer = dml.layer; if (dml.layer > chat.remoteLayer) chat.remoteLayer = dml.layer;
@ -384,8 +390,8 @@ namespace WTelegram
if (lastPending == 0) lastPending = chat.in_seq_no; if (lastPending == 0) lastPending = chat.in_seq_no;
chat.pendingMsgs[dml.out_seq_no] = dml; chat.pendingMsgs[dml.out_seq_no] = dml;
if (dml.out_seq_no > lastPending + 2) // send request to resend missing gap asynchronously if (dml.out_seq_no > lastPending + 2) // send request to resend missing gap asynchronously
_ = SendMessage(chat.ChatId, new TL.Layer17.DecryptedMessageService { random_id = Helpers.RandomLong(), _ = SendMessage(chat.ChatId, new TL.Layer23.DecryptedMessageService { random_id = Helpers.RandomLong(),
action = new TL.Layer17.DecryptedMessageActionResend { start_seq_no = lastPending + 2, end_seq_no = dml.out_seq_no - 2 } }); action = new TL.Layer23.DecryptedMessageActionResend { start_seq_no = lastPending + 2, end_seq_no = dml.out_seq_no - 2 } });
return Array.Empty<DecryptedMessageBase>(); return Array.Empty<DecryptedMessageBase>();
} }
chat.in_seq_no = dml.out_seq_no; chat.in_seq_no = dml.out_seq_no;
@ -423,13 +429,13 @@ namespace WTelegram
{ {
switch (action) switch (action)
{ {
case TL.Layer17.DecryptedMessageActionNotifyLayer dmanl: case TL.Layer23.DecryptedMessageActionNotifyLayer dmanl:
chat.remoteLayer = dmanl.layer; chat.remoteLayer = dmanl.layer;
return true; return true;
case TL.Layer17.DecryptedMessageActionResend resend: case TL.Layer23.DecryptedMessageActionResend resend:
Helpers.Log(1, $"SC{(short)chat.ChatId:X4}> Resend {resend.start_seq_no}-{resend.end_seq_no}"); Helpers.Log(1, $"SC{(short)chat.ChatId:X4}> Resend {resend.start_seq_no}-{resend.end_seq_no}");
var msgSvc = new TL.Layer17.DecryptedMessageService { action = new TL.Layer20.DecryptedMessageActionNoop() }; var msgSvc = new TL.Layer23.DecryptedMessageService { action = new TL.Layer23.DecryptedMessageActionNoop() };
var dml = new TL.Layer17.DecryptedMessageLayer var dml = new TL.Layer23.DecryptedMessageLayer
{ {
layer = Math.Min(chat.remoteLayer, Layer.SecretChats), layer = Math.Min(chat.remoteLayer, Layer.SecretChats),
random_bytes = new byte[15], random_bytes = new byte[15],
@ -442,13 +448,13 @@ namespace WTelegram
_ = SendMessage(chat, dml); _ = SendMessage(chat, dml);
} }
return true; return true;
case TL.Layer20.DecryptedMessageActionNoop: case TL.Layer23.DecryptedMessageActionNoop:
Helpers.Log(1, $"SC{(short)chat.ChatId:X4}> Noop"); Helpers.Log(1, $"SC{(short)chat.ChatId:X4}> Noop");
return true; return true;
case TL.Layer20.DecryptedMessageActionRequestKey: case TL.Layer23.DecryptedMessageActionRequestKey:
case TL.Layer20.DecryptedMessageActionAcceptKey: case TL.Layer23.DecryptedMessageActionAcceptKey:
case TL.Layer20.DecryptedMessageActionCommitKey: case TL.Layer23.DecryptedMessageActionCommitKey:
case TL.Layer20.DecryptedMessageActionAbortKey: case TL.Layer23.DecryptedMessageActionAbortKey:
Helpers.Log(1, $"SC{(short)chat.ChatId:X4}> PFS {action.GetType().Name[22..]}"); Helpers.Log(1, $"SC{(short)chat.ChatId:X4}> PFS {action.GetType().Name[22..]}");
HandlePFS(chat, action); HandlePFS(chat, action);
return true; return true;
@ -465,16 +471,17 @@ namespace WTelegram
else { Helpers.Log(4, "SC{(short)chat.ChatId:X4}> PFS Failure"); _ = Discard(chat.ChatId); return; } else { Helpers.Log(4, "SC{(short)chat.ChatId:X4}> PFS Failure"); _ = Discard(chat.ChatId); return; }
try try
{ {
chat.flags |= SecretChat.Flags.renewKey;
Helpers.Log(1, $"SC{(short)chat.ChatId:X4}> PFS RenewKey"); Helpers.Log(1, $"SC{(short)chat.ChatId:X4}> PFS RenewKey");
await Task.Delay(100);
chat.salt = new byte[256]; chat.salt = new byte[256];
RNG.GetBytes(chat.salt); RNG.GetBytes(chat.salt);
var a = BigEndianInteger(chat.salt); var a = BigEndianInteger(chat.salt);
var g_a = BigInteger.ModPow(dh.g, a, dh_prime); var g_a = BigInteger.ModPow(dh.g, a, dh_prime);
CheckGoodGaAndGb(g_a, dh_prime); CheckGoodGaAndGb(g_a, dh_prime);
chat.flags |= SecretChat.Flags.renewKey;
chat.exchange_id = Helpers.RandomLong(); chat.exchange_id = Helpers.RandomLong();
await SendMessage(chat.ChatId, new TL.Layer17.DecryptedMessageService { random_id = Helpers.RandomLong(), await SendMessage(chat.ChatId, new TL.Layer23.DecryptedMessageService { random_id = Helpers.RandomLong(),
action = new TL.Layer20.DecryptedMessageActionRequestKey { exchange_id = chat.exchange_id, g_a = g_a.To256Bytes() } }); action = new TL.Layer23.DecryptedMessageActionRequestKey { exchange_id = chat.exchange_id, g_a = g_a.To256Bytes() } });
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -489,7 +496,7 @@ namespace WTelegram
{ {
switch (action) switch (action)
{ {
case TL.Layer20.DecryptedMessageActionRequestKey request: case TL.Layer23.DecryptedMessageActionRequestKey request:
switch (chat.flags & (SecretChat.Flags.requestChat | SecretChat.Flags.renewKey | SecretChat.Flags.acceptKey)) switch (chat.flags & (SecretChat.Flags.requestChat | SecretChat.Flags.renewKey | SecretChat.Flags.acceptKey))
{ {
case SecretChat.Flags.renewKey: // Concurrent Re-Keying case SecretChat.Flags.renewKey: // Concurrent Re-Keying
@ -517,10 +524,10 @@ namespace WTelegram
chat.salt = gab.To256Bytes(); chat.salt = gab.To256Bytes();
chat.exchange_id = request.exchange_id; chat.exchange_id = request.exchange_id;
var key_fingerprint = BinaryPrimitives.ReadInt64LittleEndian(sha1.ComputeHash(chat.salt).AsSpan(12)); var key_fingerprint = BinaryPrimitives.ReadInt64LittleEndian(sha1.ComputeHash(chat.salt).AsSpan(12));
await SendMessage(chat.ChatId, new TL.Layer17.DecryptedMessageService { random_id = Helpers.RandomLong(), await SendMessage(chat.ChatId, new TL.Layer23.DecryptedMessageService { random_id = Helpers.RandomLong(),
action = new TL.Layer20.DecryptedMessageActionAcceptKey { exchange_id = request.exchange_id, g_b = g_b.To256Bytes(), key_fingerprint = key_fingerprint } }); action = new TL.Layer23.DecryptedMessageActionAcceptKey { exchange_id = request.exchange_id, g_b = g_b.To256Bytes(), key_fingerprint = key_fingerprint } });
break; break;
case TL.Layer20.DecryptedMessageActionAcceptKey accept: case TL.Layer23.DecryptedMessageActionAcceptKey accept:
if ((chat.flags & (SecretChat.Flags.requestChat | SecretChat.Flags.renewKey | SecretChat.Flags.acceptKey)) != SecretChat.Flags.renewKey) if ((chat.flags & (SecretChat.Flags.requestChat | SecretChat.Flags.renewKey | SecretChat.Flags.acceptKey)) != SecretChat.Flags.renewKey)
throw new ApplicationException("Invalid AcceptKey"); throw new ApplicationException("Invalid AcceptKey");
if (accept.exchange_id != chat.exchange_id) if (accept.exchange_id != chat.exchange_id)
@ -533,13 +540,13 @@ namespace WTelegram
key_fingerprint = BinaryPrimitives.ReadInt64LittleEndian(sha1.ComputeHash(authKey).AsSpan(12)); key_fingerprint = BinaryPrimitives.ReadInt64LittleEndian(sha1.ComputeHash(authKey).AsSpan(12));
if (accept.key_fingerprint != key_fingerprint) if (accept.key_fingerprint != key_fingerprint)
throw new ApplicationException("AcceptKey: key_fingerprint mismatch"); throw new ApplicationException("AcceptKey: key_fingerprint mismatch");
_ = SendMessage(chat.ChatId, new TL.Layer17.DecryptedMessageService { random_id = Helpers.RandomLong(), _ = SendMessage(chat.ChatId, new TL.Layer23.DecryptedMessageService { random_id = Helpers.RandomLong(),
action = new TL.Layer20.DecryptedMessageActionCommitKey { exchange_id = accept.exchange_id, key_fingerprint = accept.key_fingerprint } }); action = new TL.Layer23.DecryptedMessageActionCommitKey { exchange_id = accept.exchange_id, key_fingerprint = accept.key_fingerprint } });
chat.salt = chat.authKey; // A may only discard the previous key after a message encrypted with the new key has been received. chat.salt = chat.authKey; // A may only discard the previous key after a message encrypted with the new key has been received.
SetAuthKey(chat, authKey); SetAuthKey(chat, authKey);
chat.flags = chat.flags & ~SecretChat.Flags.renewKey | SecretChat.Flags.commitKey; chat.flags = chat.flags & ~SecretChat.Flags.renewKey | SecretChat.Flags.commitKey;
break; break;
case TL.Layer20.DecryptedMessageActionCommitKey commit: case TL.Layer23.DecryptedMessageActionCommitKey commit:
if ((chat.flags & (SecretChat.Flags.requestChat | SecretChat.Flags.renewKey | SecretChat.Flags.acceptKey)) != SecretChat.Flags.acceptKey) if ((chat.flags & (SecretChat.Flags.requestChat | SecretChat.Flags.renewKey | SecretChat.Flags.acceptKey)) != SecretChat.Flags.acceptKey)
throw new ApplicationException("Invalid RequestKey"); throw new ApplicationException("Invalid RequestKey");
key_fingerprint = BinaryPrimitives.ReadInt64LittleEndian(sha1.ComputeHash(chat.salt).AsSpan(12)); key_fingerprint = BinaryPrimitives.ReadInt64LittleEndian(sha1.ComputeHash(chat.salt).AsSpan(12));
@ -549,10 +556,10 @@ namespace WTelegram
authKey = chat.authKey; authKey = chat.authKey;
SetAuthKey(chat, chat.salt); SetAuthKey(chat, chat.salt);
Array.Clear(authKey, 0, authKey.Length); // the old key must be securely discarded Array.Clear(authKey, 0, authKey.Length); // the old key must be securely discarded
await SendMessage(chat.ChatId, new TL.Layer17.DecryptedMessageService { random_id = Helpers.RandomLong(), await SendMessage(chat.ChatId, new TL.Layer23.DecryptedMessageService { random_id = Helpers.RandomLong(),
action = new TL.Layer20.DecryptedMessageActionNoop() }); action = new TL.Layer23.DecryptedMessageActionNoop() });
break; break;
case TL.Layer20.DecryptedMessageActionAbortKey abort: case TL.Layer23.DecryptedMessageActionAbortKey abort:
if ((chat.flags & (SecretChat.Flags.renewKey | SecretChat.Flags.acceptKey)) == 0 || if ((chat.flags & (SecretChat.Flags.renewKey | SecretChat.Flags.acceptKey)) == 0 ||
chat.flags.HasFlag(SecretChat.Flags.commitKey) || abort.exchange_id != chat.exchange_id) chat.flags.HasFlag(SecretChat.Flags.commitKey) || abort.exchange_id != chat.exchange_id)
return; return;
@ -579,7 +586,7 @@ namespace WTelegram
byte[] aes_key = new byte[32], aes_iv = new byte[32]; byte[] aes_key = new byte[32], aes_iv = new byte[32];
RNG.GetBytes(aes_key); RNG.GetBytes(aes_key);
RNG.GetBytes(aes_iv); RNG.GetBytes(aes_iv);
media.SizeKeyIV = (checked((int)stream.Length), aes_key, aes_iv); media.SizeKeyIV = (stream.Length, aes_key, aes_iv);
using var md5 = MD5.Create(); using var md5 = MD5.Create();
md5.TransformBlock(aes_key, 0, 32, null, 0); md5.TransformBlock(aes_key, 0, 32, null, 0);

View file

@ -13179,7 +13179,7 @@ namespace TL
public DocumentBase static_icon; public DocumentBase static_icon;
/// <summary>The animated sticker to show when the user opens the reaction dropdown</summary> /// <summary>The animated sticker to show when the user opens the reaction dropdown</summary>
public DocumentBase appear_animation; public DocumentBase appear_animation;
/// <summary>The animated sticker to show when the user selects this reaction</summary> /// <summary>The animated sticker to show when the user hovers over the reaction</summary>
public DocumentBase select_animation; public DocumentBase select_animation;
/// <summary>The animated sticker to show when the reaction is chosen and activated</summary> /// <summary>The animated sticker to show when the reaction is chosen and activated</summary>
public DocumentBase activate_animation; public DocumentBase activate_animation;

View file

@ -32,7 +32,7 @@ namespace TL
public abstract class DecryptedMessageMedia : IObject public abstract class DecryptedMessageMedia : IObject
{ {
public virtual string MimeType { get; } public virtual string MimeType { get; }
internal virtual (int size, byte[] key, byte[] iv) SizeKeyIV { get => default; set => throw new ApplicationException("Incompatible DecryptedMessageMedia"); } internal virtual (long size, byte[] key, byte[] iv) SizeKeyIV { get => default; set => throw new ApplicationException("Incompatible DecryptedMessageMedia"); }
} }
/// <summary>Object describes the action to which a service message is linked. <para>See <a href="https://corefork.telegram.org/type/DecryptedMessageAction"/></para></summary> /// <summary>Object describes the action to which a service message is linked. <para>See <a href="https://corefork.telegram.org/type/DecryptedMessageAction"/></para></summary>
@ -110,7 +110,7 @@ namespace TL
public byte[] iv; public byte[] iv;
public override string MimeType => "image/jpeg"; public override string MimeType => "image/jpeg";
internal override (int, byte[], byte[]) SizeKeyIV { get => (size, key, iv); set => (size, key, iv) = value; } internal override (long size, byte[] key, byte[] iv) SizeKeyIV { get => (size, key, iv); set => (size, key, iv) = (checked((int)value.size), value.key, value.iv); }
} }
/// <summary>Video attached to an encrypted message. <para>See <a href="https://corefork.telegram.org/constructor/decryptedMessageMediaVideo"/></para></summary> /// <summary>Video attached to an encrypted message. <para>See <a href="https://corefork.telegram.org/constructor/decryptedMessageMediaVideo"/></para></summary>
[TLDef(0x4CEE6EF3)] [TLDef(0x4CEE6EF3)]
@ -135,7 +135,7 @@ namespace TL
/// <summary>Initialization vector</summary> /// <summary>Initialization vector</summary>
public byte[] iv; public byte[] iv;
internal override (int, byte[], byte[]) SizeKeyIV { get => (size, key, iv); set => (size, key, iv) = value; } internal override (long size, byte[] key, byte[] iv) SizeKeyIV { get => (size, key, iv); set => (size, key, iv) = (checked((int)value.size), value.key, value.iv); }
} }
/// <summary>GeoPoint attached to an encrypted message. <para>See <a href="https://corefork.telegram.org/constructor/decryptedMessageMediaGeoPoint"/></para></summary> /// <summary>GeoPoint attached to an encrypted message. <para>See <a href="https://corefork.telegram.org/constructor/decryptedMessageMediaGeoPoint"/></para></summary>
[TLDef(0x35480A59)] [TLDef(0x35480A59)]
@ -182,7 +182,7 @@ namespace TL
/// <summary>File MIME-type</summary> /// <summary>File MIME-type</summary>
public override string MimeType => mime_type; public override string MimeType => mime_type;
internal override (int, byte[], byte[]) SizeKeyIV { get => (size, key, iv); set => (size, key, iv) = value; } internal override (long size, byte[] key, byte[] iv) SizeKeyIV { get => (size, key, iv); set => (size, key, iv) = (checked((int)value.size), value.key, value.iv); }
} }
/// <summary>Audio file attached to a secret chat message. <para>See <a href="https://corefork.telegram.org/constructor/decryptedMessageMediaAudio"/></para></summary> /// <summary>Audio file attached to a secret chat message. <para>See <a href="https://corefork.telegram.org/constructor/decryptedMessageMediaAudio"/></para></summary>
[TLDef(0x6080758F)] [TLDef(0x6080758F)]
@ -197,7 +197,7 @@ namespace TL
/// <summary>Initialization vector</summary> /// <summary>Initialization vector</summary>
public byte[] iv; public byte[] iv;
internal override (int, byte[], byte[]) SizeKeyIV { get => (size, key, iv); set => (size, key, iv) = value; } internal override (long size, byte[] key, byte[] iv) SizeKeyIV { get => (size, key, iv); set => (size, key, iv) = (checked((int)value.size), value.key, value.iv); }
} }
/// <summary>Setting of a message lifetime after reading. <para>See <a href="https://corefork.telegram.org/constructor/decryptedMessageActionSetMessageTTL"/></para></summary> /// <summary>Setting of a message lifetime after reading. <para>See <a href="https://corefork.telegram.org/constructor/decryptedMessageActionSetMessageTTL"/></para></summary>
@ -233,8 +233,43 @@ namespace TL
public class DecryptedMessageActionFlushHistory : DecryptedMessageAction { } public class DecryptedMessageActionFlushHistory : DecryptedMessageAction { }
} }
namespace Layer17 namespace Layer23
{ {
/// <summary>Image description. <para>See <a href="https://corefork.telegram.org/constructor/photoSize"/></para></summary>
[TLDef(0x77BFB61B)]
public partial class PhotoSize : PhotoSizeBase
{
/// <summary><a href="https://corefork.telegram.org/api/files#image-thumbnail-types">Thumbnail type »</a></summary>
public string type;
public FileLocationBase location;
/// <summary>Image width</summary>
public int w;
/// <summary>Image height</summary>
public int h;
/// <summary>File size</summary>
public int size;
/// <summary><a href="https://corefork.telegram.org/api/files#image-thumbnail-types">Thumbnail type »</a></summary>
public override string Type => type;
}
/// <summary>Description of an image and its content. <para>See <a href="https://corefork.telegram.org/constructor/photoCachedSize"/></para></summary>
[TLDef(0xE9A734FA)]
public partial class PhotoCachedSize : PhotoSizeBase
{
/// <summary>Thumbnail type</summary>
public string type;
public FileLocationBase location;
/// <summary>Image width</summary>
public int w;
/// <summary>Image height</summary>
public int h;
/// <summary>Binary data, file content</summary>
public byte[] bytes;
/// <summary>Thumbnail type</summary>
public override string Type => type;
}
/// <summary>User is uploading a video. <para>See <a href="https://corefork.telegram.org/constructor/sendMessageUploadVideoAction"/></para></summary> /// <summary>User is uploading a video. <para>See <a href="https://corefork.telegram.org/constructor/sendMessageUploadVideoAction"/></para></summary>
[TLDef(0x92042FF7)] [TLDef(0x92042FF7)]
public class SendMessageUploadVideoAction : SendMessageAction { } public class SendMessageUploadVideoAction : SendMessageAction { }
@ -248,6 +283,28 @@ namespace TL
[TLDef(0x8FAEE98E)] [TLDef(0x8FAEE98E)]
public class SendMessageUploadDocumentAction : SendMessageAction { } public class SendMessageUploadDocumentAction : SendMessageAction { }
/// <summary>Defines a sticker <para>See <a href="https://corefork.telegram.org/constructor/documentAttributeSticker"/></para></summary>
[TLDef(0xFB0A5727)]
public class DocumentAttributeSticker : DocumentAttribute { }
/// <summary>Defines a video <para>See <a href="https://corefork.telegram.org/constructor/documentAttributeVideo"/></para></summary>
[TLDef(0x5910CCCB)]
public class DocumentAttributeVideo : DocumentAttribute
{
/// <summary>Duration in seconds</summary>
public int duration;
/// <summary>Video width</summary>
public int w;
/// <summary>Video height</summary>
public int h;
}
/// <summary>Represents an audio file <para>See <a href="https://corefork.telegram.org/constructor/documentAttributeAudio"/></para></summary>
[TLDef(0x051448E5)]
public class DocumentAttributeAudio : DocumentAttribute
{
/// <summary>Duration in seconds</summary>
public int duration;
}
/// <summary>Contents of an encrypted message. <para>See <a href="https://corefork.telegram.org/constructor/decryptedMessage"/></para></summary> /// <summary>Contents of an encrypted message. <para>See <a href="https://corefork.telegram.org/constructor/decryptedMessage"/></para></summary>
[TLDef(0x204D3878)] [TLDef(0x204D3878)]
public class DecryptedMessage : DecryptedMessageBase public class DecryptedMessage : DecryptedMessageBase
@ -313,7 +370,7 @@ namespace TL
/// <summary>MIME-type of the video file<br/>Parameter added in Layer 17.</summary> /// <summary>MIME-type of the video file<br/>Parameter added in Layer 17.</summary>
public override string MimeType => mime_type; public override string MimeType => mime_type;
internal override (int, byte[], byte[]) SizeKeyIV { get => (size, key, iv); set => (size, key, iv) = value; } internal override (long size, byte[] key, byte[] iv) SizeKeyIV { get => (size, key, iv); set => (size, key, iv) = (checked((int)value.size), value.key, value.iv); }
} }
/// <summary>Audio file attached to a secret chat message. <para>See <a href="https://corefork.telegram.org/constructor/decryptedMessageMediaAudio"/></para></summary> /// <summary>Audio file attached to a secret chat message. <para>See <a href="https://corefork.telegram.org/constructor/decryptedMessageMediaAudio"/></para></summary>
[TLDef(0x57E0A9CB)] [TLDef(0x57E0A9CB)]
@ -333,7 +390,31 @@ namespace TL
/// <summary>MIME-type of the audio file<br/>Parameter added in Layer 13.</summary> /// <summary>MIME-type of the audio file<br/>Parameter added in Layer 13.</summary>
public override string MimeType => mime_type; public override string MimeType => mime_type;
internal override (int, byte[], byte[]) SizeKeyIV { get => (size, key, iv); set => (size, key, iv) = value; } internal override (long size, byte[] key, byte[] iv) SizeKeyIV { get => (size, key, iv); set => (size, key, iv) = (checked((int)value.size), value.key, value.iv); }
}
/// <summary>Non-e2e documented forwarded from non-secret chat <para>See <a href="https://corefork.telegram.org/constructor/decryptedMessageMediaExternalDocument"/></para></summary>
[TLDef(0xFA95B0DD)]
public class DecryptedMessageMediaExternalDocument : DecryptedMessageMedia
{
/// <summary>Document ID</summary>
public long id;
/// <summary>access hash</summary>
public long access_hash;
/// <summary>Date</summary>
public DateTime date;
/// <summary>Mime type</summary>
public string mime_type;
/// <summary>Size</summary>
public int size;
/// <summary>Thumbnail</summary>
public PhotoSizeBase thumb;
/// <summary>DC ID</summary>
public int dc_id;
/// <summary>Attributes for media types</summary>
public DocumentAttribute[] attributes;
/// <summary>Mime type</summary>
public override string MimeType => mime_type;
} }
/// <summary>Request for the other party in a Secret Chat to automatically resend a contiguous range of previously sent messages, as explained in <a href="https://corefork.telegram.org/api/end-to-end/seq_no">Sequence number is Secret Chats</a>. <para>See <a href="https://corefork.telegram.org/constructor/decryptedMessageActionResend"/></para></summary> /// <summary>Request for the other party in a Secret Chat to automatically resend a contiguous range of previously sent messages, as explained in <a href="https://corefork.telegram.org/api/end-to-end/seq_no">Sequence number is Secret Chats</a>. <para>See <a href="https://corefork.telegram.org/constructor/decryptedMessageActionResend"/></para></summary>
@ -359,6 +440,45 @@ namespace TL
/// <summary>Type of action</summary> /// <summary>Type of action</summary>
public SendMessageAction action; public SendMessageAction action;
} }
/// <summary>Request rekeying, see <a href="https://corefork.telegram.org/api/end-to-end/pfs">rekeying process</a> <para>See <a href="https://corefork.telegram.org/constructor/decryptedMessageActionRequestKey"/></para></summary>
[TLDef(0xF3C9611B)]
public class DecryptedMessageActionRequestKey : DecryptedMessageAction
{
/// <summary>Exchange ID</summary>
public long exchange_id;
/// <summary>g_a, see <a href="https://corefork.telegram.org/api/end-to-end/pfs">rekeying process</a></summary>
public byte[] g_a;
}
/// <summary>Accept new key <para>See <a href="https://corefork.telegram.org/constructor/decryptedMessageActionAcceptKey"/></para></summary>
[TLDef(0x6FE1735B)]
public class DecryptedMessageActionAcceptKey : DecryptedMessageAction
{
/// <summary>Exchange ID</summary>
public long exchange_id;
/// <summary>B parameter, see <a href="https://corefork.telegram.org/api/end-to-end/pfs">rekeying process</a></summary>
public byte[] g_b;
/// <summary>Key fingerprint, see <a href="https://corefork.telegram.org/api/end-to-end/pfs">rekeying process</a></summary>
public long key_fingerprint;
}
/// <summary>Abort rekeying <para>See <a href="https://corefork.telegram.org/constructor/decryptedMessageActionAbortKey"/></para></summary>
[TLDef(0xDD05EC6B)]
public class DecryptedMessageActionAbortKey : DecryptedMessageAction
{
/// <summary>Exchange ID</summary>
public long exchange_id;
}
/// <summary>Commit new key, see <a href="https://corefork.telegram.org/api/end-to-end/pfs">rekeying process</a> <para>See <a href="https://corefork.telegram.org/constructor/decryptedMessageActionCommitKey"/></para></summary>
[TLDef(0xEC2E0B9B)]
public class DecryptedMessageActionCommitKey : DecryptedMessageAction
{
/// <summary>Exchange ID, see <a href="https://corefork.telegram.org/api/end-to-end/pfs">rekeying process</a></summary>
public long exchange_id;
/// <summary>Key fingerprint, see <a href="https://corefork.telegram.org/api/end-to-end/pfs">rekeying process</a></summary>
public long key_fingerprint;
}
/// <summary>NOOP action <para>See <a href="https://corefork.telegram.org/constructor/decryptedMessageActionNoop"/></para></summary>
[TLDef(0xA82FDD63)]
public class DecryptedMessageActionNoop : DecryptedMessageAction { }
/// <summary>Sets the layer number for the contents of an encrypted message. <para>See <a href="https://corefork.telegram.org/constructor/decryptedMessageLayer"/></para></summary> /// <summary>Sets the layer number for the contents of an encrypted message. <para>See <a href="https://corefork.telegram.org/constructor/decryptedMessageLayer"/></para></summary>
[TLDef(0x1BE31789)] [TLDef(0x1BE31789)]
@ -375,19 +495,49 @@ namespace TL
/// <summary>The content of message itself</summary> /// <summary>The content of message itself</summary>
public DecryptedMessageBase message; public DecryptedMessageBase message;
} }
/// <summary>File is currently unavailable. <para>See <a href="https://corefork.telegram.org/constructor/fileLocationUnavailable"/></para></summary>
[TLDef(0x7C596B46)]
public class FileLocationUnavailable : FileLocationBase
{
/// <summary>Server volume</summary>
public long volume_id;
/// <summary>File ID</summary>
public int local_id;
/// <summary>Checksum to access the file</summary>
public long secret;
/// <summary>Server volume</summary>
public override long VolumeId => volume_id;
/// <summary>File ID</summary>
public override int LocalId => local_id;
/// <summary>Checksum to access the file</summary>
public override long Secret => secret;
}
/// <summary>File location. <para>See <a href="https://corefork.telegram.org/constructor/fileLocation"/></para></summary>
[TLDef(0x53D69076)]
public class FileLocation : FileLocationBase
{
/// <summary>Number of the data center holding the file</summary>
public int dc_id;
/// <summary>Server volume</summary>
public long volume_id;
/// <summary>File ID</summary>
public int local_id;
/// <summary>Checksum to access the file</summary>
public long secret;
/// <summary>Server volume</summary>
public override long VolumeId => volume_id;
/// <summary>File ID</summary>
public override int LocalId => local_id;
/// <summary>Checksum to access the file</summary>
public override long Secret => secret;
}
} }
namespace Layer45 namespace Layer45
{ {
/// <summary>Defines a sticker <para>See <a href="https://corefork.telegram.org/constructor/documentAttributeSticker"/></para></summary>
[TLDef(0x3A556302)]
public class DocumentAttributeSticker : DocumentAttribute
{
/// <summary>Alternative emoji representation of sticker</summary>
public string alt;
/// <summary>Associated stickerset</summary>
public InputStickerSet stickerset;
}
/// <summary>Represents an audio file <para>See <a href="https://corefork.telegram.org/constructor/documentAttributeAudio"/></para></summary> /// <summary>Represents an audio file <para>See <a href="https://corefork.telegram.org/constructor/documentAttributeAudio"/></para></summary>
[TLDef(0xDED218E0)] [TLDef(0xDED218E0)]
public class DocumentAttributeAudio : DocumentAttribute public class DocumentAttributeAudio : DocumentAttribute
@ -399,6 +549,27 @@ namespace TL
/// <summary>Performer</summary> /// <summary>Performer</summary>
public string performer; public string performer;
} }
}
namespace Layer46
{
/// <summary>Defines a sticker <para>See <a href="https://corefork.telegram.org/constructor/documentAttributeSticker"/></para></summary>
[TLDef(0x3A556302)]
public class DocumentAttributeSticker : DocumentAttribute
{
/// <summary>Alternative emoji representation of sticker</summary>
public string alt;
/// <summary>Associated stickerset</summary>
public InputStickerSet stickerset;
}
/// <summary>Message entity representing a <a href="https://corefork.telegram.org/api/mentions">user mention</a>: for <em>creating</em> a mention use <see cref="InputMessageEntityMentionName"/>. <para>See <a href="https://corefork.telegram.org/constructor/messageEntityMentionName"/></para></summary>
[TLDef(0x352DCA58, inheritBefore = true)]
public class MessageEntityMentionName : MessageEntityMention
{
/// <summary>Identifier of the user that was mentioned</summary>
public int user_id;
}
/// <summary>Contents of an encrypted message. <para>See <a href="https://corefork.telegram.org/constructor/decryptedMessage"/></para></summary> /// <summary>Contents of an encrypted message. <para>See <a href="https://corefork.telegram.org/constructor/decryptedMessage"/></para></summary>
[TLDef(0x36B091DE)] [TLDef(0x36B091DE)]
@ -475,7 +646,7 @@ namespace TL
public string caption; public string caption;
public override string MimeType => "image/jpeg"; public override string MimeType => "image/jpeg";
internal override (int, byte[], byte[]) SizeKeyIV { get => (size, key, iv); set => (size, key, iv) = value; } internal override (long size, byte[] key, byte[] iv) SizeKeyIV { get => (size, key, iv); set => (size, key, iv) = (checked((int)value.size), value.key, value.iv); }
} }
/// <summary>Video attached to an encrypted message. <para>See <a href="https://corefork.telegram.org/constructor/decryptedMessageMediaVideo"/></para></summary> /// <summary>Video attached to an encrypted message. <para>See <a href="https://corefork.telegram.org/constructor/decryptedMessageMediaVideo"/></para></summary>
[TLDef(0x970C8C0E)] [TLDef(0x970C8C0E)]
@ -507,7 +678,7 @@ namespace TL
/// <summary>MIME-type of the video file<br/>Parameter added in Layer 17.</summary> /// <summary>MIME-type of the video file<br/>Parameter added in Layer 17.</summary>
public override string MimeType => mime_type; public override string MimeType => mime_type;
internal override (int, byte[], byte[]) SizeKeyIV { get => (size, key, iv); set => (size, key, iv) = value; } internal override (long size, byte[] key, byte[] iv) SizeKeyIV { get => (size, key, iv); set => (size, key, iv) = (checked((int)value.size), value.key, value.iv); }
} }
/// <summary>Document attached to a message in a secret chat. <para>See <a href="https://corefork.telegram.org/constructor/decryptedMessageMediaDocument"/></para></summary> /// <summary>Document attached to a message in a secret chat. <para>See <a href="https://corefork.telegram.org/constructor/decryptedMessageMediaDocument"/></para></summary>
[TLDef(0x7AFE8AE2)] [TLDef(0x7AFE8AE2)]
@ -535,7 +706,7 @@ namespace TL
/// <summary>File MIME-type</summary> /// <summary>File MIME-type</summary>
public override string MimeType => mime_type; public override string MimeType => mime_type;
internal override (int, byte[], byte[]) SizeKeyIV { get => (size, key, iv); set => (size, key, iv) = value; } internal override (long size, byte[] key, byte[] iv) SizeKeyIV { get => (size, key, iv); set => (size, key, iv) = (checked((int)value.size), value.key, value.iv); }
} }
/// <summary>Venue <para>See <a href="https://corefork.telegram.org/constructor/decryptedMessageMediaVenue"/></para></summary> /// <summary>Venue <para>See <a href="https://corefork.telegram.org/constructor/decryptedMessageMediaVenue"/></para></summary>
[TLDef(0x8A0DF56F)] [TLDef(0x8A0DF56F)]
@ -563,6 +734,13 @@ namespace TL
} }
} }
namespace Layer66
{
/// <summary>User is uploading a round video <para>See <a href="https://corefork.telegram.org/constructor/sendMessageUploadRoundAction"/></para></summary>
[TLDef(0xBB718624)]
public class SendMessageUploadRoundAction : SendMessageAction { }
}
namespace Layer73 namespace Layer73
{ {
/// <summary>Contents of an encrypted message. <para>See <a href="https://corefork.telegram.org/constructor/decryptedMessage"/></para></summary> /// <summary>Contents of an encrypted message. <para>See <a href="https://corefork.telegram.org/constructor/decryptedMessage"/></para></summary>
@ -592,6 +770,8 @@ namespace TL
{ {
/// <summary>Field <see cref="reply_to_random_id"/> has a value</summary> /// <summary>Field <see cref="reply_to_random_id"/> has a value</summary>
has_reply_to_random_id = 0x8, has_reply_to_random_id = 0x8,
/// <summary>Whether this is a silent message (no notification triggered)</summary>
silent = 0x20,
/// <summary>Field <see cref="entities"/> has a value</summary> /// <summary>Field <see cref="entities"/> has a value</summary>
has_entities = 0x80, has_entities = 0x80,
/// <summary>Field <see cref="media"/> has a value</summary> /// <summary>Field <see cref="media"/> has a value</summary>
@ -623,180 +803,41 @@ namespace TL
} }
} }
namespace Layer20 namespace Layer101
{ }
namespace Layer143
{ {
/// <summary>Request rekeying, see <a href="https://corefork.telegram.org/api/end-to-end/pfs">rekeying process</a> <para>See <a href="https://corefork.telegram.org/constructor/decryptedMessageActionRequestKey"/></para></summary> /// <summary>Document attached to a message in a secret chat. <para>See <a href="https://corefork.telegram.org/constructor/decryptedMessageMediaDocument"/></para></summary>
[TLDef(0xF3C9611B)] [TLDef(0x6ABD9782)]
public class DecryptedMessageActionRequestKey : DecryptedMessageAction public class DecryptedMessageMediaDocument : DecryptedMessageMedia
{ {
/// <summary>Exchange ID</summary> /// <summary>Thumbnail-file contents (JPEG-file, quality 55, set in a 90x90 square)</summary>
public long exchange_id; public byte[] thumb;
/// <summary>g_a, see <a href="https://corefork.telegram.org/api/end-to-end/pfs">rekeying process</a></summary> /// <summary>Thumbnail width</summary>
public byte[] g_a; public int thumb_w;
} /// <summary>Thumbnail height</summary>
/// <summary>Accept new key <para>See <a href="https://corefork.telegram.org/constructor/decryptedMessageActionAcceptKey"/></para></summary> public int thumb_h;
[TLDef(0x6FE1735B)] /// <summary>File MIME-type</summary>
public class DecryptedMessageActionAcceptKey : DecryptedMessageAction
{
/// <summary>Exchange ID</summary>
public long exchange_id;
/// <summary>B parameter, see <a href="https://corefork.telegram.org/api/end-to-end/pfs">rekeying process</a></summary>
public byte[] g_b;
/// <summary>Key fingerprint, see <a href="https://corefork.telegram.org/api/end-to-end/pfs">rekeying process</a></summary>
public long key_fingerprint;
}
/// <summary>Abort rekeying <para>See <a href="https://corefork.telegram.org/constructor/decryptedMessageActionAbortKey"/></para></summary>
[TLDef(0xDD05EC6B)]
public class DecryptedMessageActionAbortKey : DecryptedMessageAction
{
/// <summary>Exchange ID</summary>
public long exchange_id;
}
/// <summary>Commit new key, see <a href="https://corefork.telegram.org/api/end-to-end/pfs">rekeying process</a> <para>See <a href="https://corefork.telegram.org/constructor/decryptedMessageActionCommitKey"/></para></summary>
[TLDef(0xEC2E0B9B)]
public class DecryptedMessageActionCommitKey : DecryptedMessageAction
{
/// <summary>Exchange ID, see <a href="https://corefork.telegram.org/api/end-to-end/pfs">rekeying process</a></summary>
public long exchange_id;
/// <summary>Key fingerprint, see <a href="https://corefork.telegram.org/api/end-to-end/pfs">rekeying process</a></summary>
public long key_fingerprint;
}
/// <summary>NOOP action <para>See <a href="https://corefork.telegram.org/constructor/decryptedMessageActionNoop"/></para></summary>
[TLDef(0xA82FDD63)]
public class DecryptedMessageActionNoop : DecryptedMessageAction { }
}
namespace Layer23
{
/// <summary>Image description. <para>See <a href="https://corefork.telegram.org/constructor/photoSize"/></para></summary>
[TLDef(0x77BFB61B)]
public partial class PhotoSize : PhotoSizeBase
{
/// <summary><a href="https://corefork.telegram.org/api/files#image-thumbnail-types">Thumbnail type »</a></summary>
public string type;
public FileLocationBase location;
/// <summary>Image width</summary>
public int w;
/// <summary>Image height</summary>
public int h;
/// <summary>File size</summary>
public int size;
/// <summary><a href="https://corefork.telegram.org/api/files#image-thumbnail-types">Thumbnail type »</a></summary>
public override string Type => type;
}
/// <summary>Description of an image and its content. <para>See <a href="https://corefork.telegram.org/constructor/photoCachedSize"/></para></summary>
[TLDef(0xE9A734FA)]
public partial class PhotoCachedSize : PhotoSizeBase
{
/// <summary>Thumbnail type</summary>
public string type;
public FileLocationBase location;
/// <summary>Image width</summary>
public int w;
/// <summary>Image height</summary>
public int h;
/// <summary>Binary data, file content</summary>
public byte[] bytes;
/// <summary>Thumbnail type</summary>
public override string Type => type;
}
/// <summary>Defines a sticker <para>See <a href="https://corefork.telegram.org/constructor/documentAttributeSticker"/></para></summary>
[TLDef(0xFB0A5727)]
public class DocumentAttributeSticker : DocumentAttribute { }
/// <summary>Defines a video <para>See <a href="https://corefork.telegram.org/constructor/documentAttributeVideo"/></para></summary>
[TLDef(0x5910CCCB)]
public class DocumentAttributeVideo : DocumentAttribute
{
/// <summary>Duration in seconds</summary>
public int duration;
/// <summary>Video width</summary>
public int w;
/// <summary>Video height</summary>
public int h;
}
/// <summary>Represents an audio file <para>See <a href="https://corefork.telegram.org/constructor/documentAttributeAudio"/></para></summary>
[TLDef(0x051448E5)]
public class DocumentAttributeAudio : DocumentAttribute
{
/// <summary>Duration in seconds</summary>
public int duration;
}
/// <summary>Non-e2e documented forwarded from non-secret chat <para>See <a href="https://corefork.telegram.org/constructor/decryptedMessageMediaExternalDocument"/></para></summary>
[TLDef(0xFA95B0DD)]
public class DecryptedMessageMediaExternalDocument : DecryptedMessageMedia
{
/// <summary>Document ID</summary>
public long id;
/// <summary>access hash</summary>
public long access_hash;
/// <summary>Date</summary>
public DateTime date;
/// <summary>Mime type</summary>
public string mime_type; public string mime_type;
/// <summary>Size</summary> /// <summary>Document size (<see cref="int"/> on layer &lt;143, <see cref="long"/> on layer &gt;=143)</summary>
public int size; public long size;
/// <summary>Thumbnail</summary> /// <summary>Key to decrypt the attached document file</summary>
public PhotoSizeBase thumb; public byte[] key;
/// <summary>DC ID</summary> /// <summary>Initialization</summary>
public int dc_id; public byte[] iv;
/// <summary>Attributes for media types</summary> /// <summary>Document attributes for media types</summary>
public DocumentAttribute[] attributes; public DocumentAttribute[] attributes;
/// <summary>Caption</summary>
public string caption;
/// <summary>Mime type</summary> /// <summary>File MIME-type</summary>
public override string MimeType => mime_type; public override string MimeType => mime_type;
}
/// <summary>File is currently unavailable. <para>See <a href="https://corefork.telegram.org/constructor/fileLocationUnavailable"/></para></summary> internal override (long size, byte[] key, byte[] iv) SizeKeyIV { get => (size, key, iv); set => (size, key, iv) = value; }
[TLDef(0x7C596B46)]
public class FileLocationUnavailable : FileLocationBase
{
/// <summary>Server volume</summary>
public long volume_id;
/// <summary>File ID</summary>
public int local_id;
/// <summary>Checksum to access the file</summary>
public long secret;
/// <summary>Server volume</summary>
public override long VolumeId => volume_id;
/// <summary>File ID</summary>
public override int LocalId => local_id;
/// <summary>Checksum to access the file</summary>
public override long Secret => secret;
}
/// <summary>File location. <para>See <a href="https://corefork.telegram.org/constructor/fileLocation"/></para></summary>
[TLDef(0x53D69076)]
public class FileLocation : FileLocationBase
{
/// <summary>Number of the data center holding the file</summary>
public int dc_id;
/// <summary>Server volume</summary>
public long volume_id;
/// <summary>File ID</summary>
public int local_id;
/// <summary>Checksum to access the file</summary>
public long secret;
/// <summary>Server volume</summary>
public override long VolumeId => volume_id;
/// <summary>File ID</summary>
public override int LocalId => local_id;
/// <summary>Checksum to access the file</summary>
public override long Secret => secret;
} }
} }
namespace Layer66 namespace Layer144
{
/// <summary>User is uploading a round video <para>See <a href="https://corefork.telegram.org/constructor/sendMessageUploadRoundAction"/></para></summary>
[TLDef(0xBB718624)]
public class SendMessageUploadRoundAction : SendMessageAction { }
}
namespace Layer46
{ } { }
} }

View file

@ -7,7 +7,7 @@ namespace TL
public static class Layer public static class Layer
{ {
public const int Version = 148; // fetched 01/11/2022 17:33:23 public const int Version = 148; // fetched 01/11/2022 17:33:23
internal const int SecretChats = 101; internal const int SecretChats = 144;
internal const int MTProto2 = 73; internal const int MTProto2 = 73;
internal const uint VectorCtor = 0x1CB5C415; internal const uint VectorCtor = 0x1CB5C415;
internal const uint NullCtor = 0x56730BCC; internal const uint NullCtor = 0x56730BCC;
@ -1047,54 +1047,56 @@ namespace TL
[0x71701DA9] = typeof(ForumTopic), [0x71701DA9] = typeof(ForumTopic),
[0x367617D3] = typeof(Messages_ForumTopics), [0x367617D3] = typeof(Messages_ForumTopics),
// from TL.Secret: // from TL.Secret:
[0x6ABD9782] = typeof(Layer143.DecryptedMessageMediaDocument),
[0x91CC4674] = typeof(Layer73.DecryptedMessage),
[0xBB718624] = typeof(Layer66.SendMessageUploadRoundAction), [0xBB718624] = typeof(Layer66.SendMessageUploadRoundAction),
[0xE50511D8] = typeof(Layer45.DecryptedMessageMediaWebPage), [0xE50511D8] = typeof(Layer46.DecryptedMessageMediaWebPage),
[0x8A0DF56F] = typeof(Layer45.DecryptedMessageMediaVenue), [0x8A0DF56F] = typeof(Layer46.DecryptedMessageMediaVenue),
[0x352DCA58] = typeof(Layer46.MessageEntityMentionName),
[0x3A556302] = typeof(Layer46.DocumentAttributeSticker),
[0x7AFE8AE2] = typeof(Layer46.DecryptedMessageMediaDocument),
[0x970C8C0E] = typeof(Layer46.DecryptedMessageMediaVideo),
[0xF1FA8D78] = typeof(Layer46.DecryptedMessageMediaPhoto),
[0x36B091DE] = typeof(Layer46.DecryptedMessage),
[0xDED218E0] = typeof(Layer45.DocumentAttributeAudio),
[0xFA95B0DD] = typeof(Layer23.DecryptedMessageMediaExternalDocument), [0xFA95B0DD] = typeof(Layer23.DecryptedMessageMediaExternalDocument),
[0x53D69076] = typeof(Layer23.FileLocation), [0x53D69076] = typeof(Layer23.FileLocation),
[0x7C596B46] = typeof(Layer23.FileLocationUnavailable), [0x7C596B46] = typeof(Layer23.FileLocationUnavailable),
[0xE9A734FA] = typeof(Layer23.PhotoCachedSize), [0xE9A734FA] = typeof(Layer23.PhotoCachedSize),
[0x77BFB61B] = typeof(Layer23.PhotoSize), [0x77BFB61B] = typeof(Layer23.PhotoSize),
[0xDED218E0] = typeof(Layer45.DocumentAttributeAudio),
[0x051448E5] = typeof(Layer23.DocumentAttributeAudio), [0x051448E5] = typeof(Layer23.DocumentAttributeAudio),
[0x5910CCCB] = typeof(Layer23.DocumentAttributeVideo), [0x5910CCCB] = typeof(Layer23.DocumentAttributeVideo),
[0x3A556302] = typeof(Layer45.DocumentAttributeSticker),
[0xFB0A5727] = typeof(Layer23.DocumentAttributeSticker), [0xFB0A5727] = typeof(Layer23.DocumentAttributeSticker),
[0xA82FDD63] = typeof(Layer20.DecryptedMessageActionNoop), [0xA82FDD63] = typeof(Layer23.DecryptedMessageActionNoop),
[0xEC2E0B9B] = typeof(Layer20.DecryptedMessageActionCommitKey), [0xEC2E0B9B] = typeof(Layer23.DecryptedMessageActionCommitKey),
[0xDD05EC6B] = typeof(Layer20.DecryptedMessageActionAbortKey), [0xDD05EC6B] = typeof(Layer23.DecryptedMessageActionAbortKey),
[0x6FE1735B] = typeof(Layer20.DecryptedMessageActionAcceptKey), [0x6FE1735B] = typeof(Layer23.DecryptedMessageActionAcceptKey),
[0xF3C9611B] = typeof(Layer20.DecryptedMessageActionRequestKey), [0xF3C9611B] = typeof(Layer23.DecryptedMessageActionRequestKey),
[0xCCB27641] = typeof(Layer17.DecryptedMessageActionTyping), [0xCCB27641] = typeof(Layer23.DecryptedMessageActionTyping),
[0xF3048883] = typeof(Layer17.DecryptedMessageActionNotifyLayer), [0xF3048883] = typeof(Layer23.DecryptedMessageActionNotifyLayer),
[0x511110B0] = typeof(Layer17.DecryptedMessageActionResend), [0x511110B0] = typeof(Layer23.DecryptedMessageActionResend),
[0x8FAEE98E] = typeof(Layer17.SendMessageUploadDocumentAction), [0x8FAEE98E] = typeof(Layer23.SendMessageUploadDocumentAction),
[0x990A3C1A] = typeof(Layer17.SendMessageUploadPhotoAction), [0x990A3C1A] = typeof(Layer23.SendMessageUploadPhotoAction),
[0xE6AC8A6F] = typeof(Layer17.SendMessageUploadAudioAction), [0xE6AC8A6F] = typeof(Layer23.SendMessageUploadAudioAction),
[0x92042FF7] = typeof(Layer17.SendMessageUploadVideoAction), [0x92042FF7] = typeof(Layer23.SendMessageUploadVideoAction),
[0x1BE31789] = typeof(Layer17.DecryptedMessageLayer), [0x1BE31789] = typeof(Layer23.DecryptedMessageLayer),
[0x57E0A9CB] = typeof(Layer23.DecryptedMessageMediaAudio),
[0x524A415D] = typeof(Layer23.DecryptedMessageMediaVideo),
[0x73164160] = typeof(Layer23.DecryptedMessageService),
[0x204D3878] = typeof(Layer23.DecryptedMessage),
[0x6719E45C] = typeof(Layer8.DecryptedMessageActionFlushHistory), [0x6719E45C] = typeof(Layer8.DecryptedMessageActionFlushHistory),
[0x8AC1F475] = typeof(Layer8.DecryptedMessageActionScreenshotMessages), [0x8AC1F475] = typeof(Layer8.DecryptedMessageActionScreenshotMessages),
[0x65614304] = typeof(Layer8.DecryptedMessageActionDeleteMessages), [0x65614304] = typeof(Layer8.DecryptedMessageActionDeleteMessages),
[0x0C4F40BE] = typeof(Layer8.DecryptedMessageActionReadMessages), [0x0C4F40BE] = typeof(Layer8.DecryptedMessageActionReadMessages),
[0x57E0A9CB] = typeof(Layer17.DecryptedMessageMediaAudio),
[0x6080758F] = typeof(Layer8.DecryptedMessageMediaAudio), [0x6080758F] = typeof(Layer8.DecryptedMessageMediaAudio),
[0x7AFE8AE2] = typeof(Layer45.DecryptedMessageMediaDocument),
[0xB095434B] = typeof(Layer8.DecryptedMessageMediaDocument), [0xB095434B] = typeof(Layer8.DecryptedMessageMediaDocument),
[0xA1733AEC] = typeof(Layer8.DecryptedMessageActionSetMessageTTL), [0xA1733AEC] = typeof(Layer8.DecryptedMessageActionSetMessageTTL),
[0x588A0A97] = typeof(Layer8.DecryptedMessageMediaContact), [0x588A0A97] = typeof(Layer8.DecryptedMessageMediaContact),
[0x35480A59] = typeof(Layer8.DecryptedMessageMediaGeoPoint), [0x35480A59] = typeof(Layer8.DecryptedMessageMediaGeoPoint),
[0x970C8C0E] = typeof(Layer45.DecryptedMessageMediaVideo),
[0x524A415D] = typeof(Layer17.DecryptedMessageMediaVideo),
[0x4CEE6EF3] = typeof(Layer8.DecryptedMessageMediaVideo), [0x4CEE6EF3] = typeof(Layer8.DecryptedMessageMediaVideo),
[0xF1FA8D78] = typeof(Layer45.DecryptedMessageMediaPhoto),
[0x32798A8C] = typeof(Layer8.DecryptedMessageMediaPhoto), [0x32798A8C] = typeof(Layer8.DecryptedMessageMediaPhoto),
[0x089F5C4A] = null,//Layer8.DecryptedMessageMediaEmpty [0x089F5C4A] = null,//Layer8.DecryptedMessageMediaEmpty
[0x73164160] = typeof(Layer17.DecryptedMessageService),
[0xAA48327D] = typeof(Layer8.DecryptedMessageService), [0xAA48327D] = typeof(Layer8.DecryptedMessageService),
[0x91CC4674] = typeof(Layer73.DecryptedMessage),
[0x36B091DE] = typeof(Layer45.DecryptedMessage),
[0x204D3878] = typeof(Layer17.DecryptedMessage),
[0x1F814F1F] = typeof(Layer8.DecryptedMessage), [0x1F814F1F] = typeof(Layer8.DecryptedMessage),
}; };