Implemented several of the TODOs:

- Encrypt session file
- Export/Import user authorization on DC migration.
- Implemented SignUpRequired
- function requests moved to class Fn, generation fix
This commit is contained in:
Wizou 2021-08-06 07:28:54 +02:00
parent 61077c380c
commit 6315b78803
7 changed files with 2621 additions and 2544 deletions

108
Client.cs
View file

@ -14,8 +14,6 @@ using System.Threading.Tasks;
using TL;
using static WTelegram.Encryption;
//TODO: include XML comments in nuget
namespace WTelegram
{
public sealed class Client : IDisposable
@ -40,7 +38,7 @@ namespace WTelegram
_updateHandler = updateHandler;
_apiId = int.Parse(Config("api_id"));
_apiHash = Config("api_hash");
_session = Session.LoadOrCreate(Config("session_pathname"));
_session = Session.LoadOrCreate(Config("session_pathname"), Convert.FromHexString(_apiHash));
}
public string Config(string config)
@ -76,6 +74,12 @@ namespace WTelegram
[System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1822")]
public void LoadPublicKey(string pem) => Encryption.LoadPublicKey(pem);
public void Reset() // disconnect and reset session (forget server address, current user and authkey)
{
_tcpClient.Close();
_session.Reset();
}
public async Task ConnectAsync()
{
var endpoint = _session.DataCenter == null ? IPEndPoint.Parse(Config("server_address"))
@ -89,10 +93,10 @@ namespace WTelegram
if (_session.AuthKey == null)
await CreateAuthorizationKey(this, _session);
TLConfig = await CallAsync(new InvokeWithLayer<Config>
TLConfig = await CallAsync(new Fn.InvokeWithLayer<Config>
{
layer = Schema.Layer,
query = new InitConnection<Config>
query = new Fn.InitConnection<Config>
{
api_id = _apiId,
device_model = Config("device_model"),
@ -101,7 +105,7 @@ namespace WTelegram
system_lang_code = Config("system_lang_code"),
lang_pack = Config("lang_pack"),
lang_code = Config("lang_code"),
query = new Help_GetConfig()
query = new Fn.Help_GetConfig()
}
});
}
@ -109,7 +113,9 @@ namespace WTelegram
private async Task MigrateDCAsync(int dcId)
{
Helpers.Log(2, $"Migrate to DC {dcId}...");
//TODO: Export/Import client authorization?
Auth_ExportedAuthorization exported = null;
if (_session.User != null)
exported = await CallAsync(new Fn.Auth_ExportAuthorization { dc_id = dcId });
var prevFamily = _tcpClient.Client.RemoteEndPoint.AddressFamily;
_tcpClient.Close();
var dcOptions = TLConfig.dc_options.Where(dc => dc.id == dcId && (dc.flags & (DcOption.Flags.media_only | DcOption.Flags.cdn)) == 0);
@ -118,10 +124,15 @@ namespace WTelegram
else
dcOptions = dcOptions.OrderBy(dc => dc.flags & DcOption.Flags.ipv6); // list ipv4 first
var dcOption = dcOptions.FirstOrDefault();
_session.DataCenter = dcOption ?? throw new ApplicationException($"Could not find adequate dcOption for DC {dcId}");
_session.AuthKeyID = _session.Salt = _session.Seqno = 0;
_session.AuthKey = null;
_session.Reset(dcOption ?? throw new ApplicationException($"Could not find adequate dcOption for DC {dcId}"));
await ConnectAsync();
if (exported != null)
{
var authorization = await CallAsync(new Fn.Auth_ImportAuthorization { id = exported.id, bytes = exported.bytes });
if (authorization is not Auth_Authorization { user: User user })
throw new ApplicationException("Failed to get Authorization: " + authorization.GetType().Name);
_session.User = Schema.Serialize(user);
}
}
public void Dispose()
@ -134,7 +145,7 @@ namespace WTelegram
public async Task SendAsync(ITLObject msg, bool isContent = true)
{
if (_session.AuthKeyID != 0) await CheckMsgsToAck();
using var memStream = new MemoryStream(1024); //TODO: choose a useful capacity
using var memStream = new MemoryStream(1024);
using var writer = new BinaryWriter(memStream, Encoding.UTF8);
writer.Write(0); // int32 frame_len (to be patched with full frame length)
writer.Write(_frame_seqTx++); // int32 frame_seq
@ -153,8 +164,8 @@ namespace WTelegram
else
{
Helpers.Log(1, $"Sending {msg.GetType().Name,-50} #{(short)msgId.GetHashCode():X4}");
//TODO: Implement MTProto 2.0
using var clearStream = new MemoryStream(1024); //TODO: choose a useful capacity
//TODO: implement MTProto 2.0
using var clearStream = new MemoryStream(1024);
using var clearWriter = new BinaryWriter(clearStream, Encoding.UTF8);
clearWriter.Write(_session.Salt); // int64 salt
clearWriter.Write(_session.Id); // int64 session_id
@ -193,7 +204,10 @@ namespace WTelegram
{
var data = await RecvFrameAsync();
if (data.Length == 4 && data[3] == 0xFF)
throw new ApplicationException($"Server replied with error code: {TransportError(-BinaryPrimitives.ReadInt32LittleEndian(data))}");
{
int error_code = -BinaryPrimitives.ReadInt32LittleEndian(data);
throw new RpcException(error_code, TransportError(error_code));
}
if (data.Length < 24) // authKeyId+msgId+length+ctorNb | authKeyId+msgKey
throw new ApplicationException($"Packet payload too small: {data.Length}");
@ -246,7 +260,7 @@ namespace WTelegram
throw new ApplicationException($"Cannot find type for ctor #{ctorNb:x}");
Helpers.Log(1, $"Receiving {realType.Name,-50} timestamp={_session.MsgIdToStamp(msgId)} isResponse={(msgId & 2) != 0} {(seqno == -1 ? "clearText" : "isContent")}={(seqno & 1) != 0}");
if (realType == typeof(RpcResult))
return DeserializeRpcResult(reader); // hack necessary because some RPC return bare types like bool or int[]
return DeserializeRpcResult(reader); // necessary hack because some RPC return bare types like bool or int[]
else
return Schema.DeserializeObject(reader, realType);
}
@ -317,6 +331,8 @@ namespace WTelegram
public async Task<X> CallAsync<X>(ITLFunction<X> request)
{
await SendAsync(request);
// TODO: create a background reactor system that handles incoming packets and wake up awaiting tasks when their result has arrived
// This would allow parallelization of Send task and avoid the risk of calling RecvInternal concurrently
_lastRpcResultType = typeof(X);
for (; ;)
{
@ -383,7 +399,7 @@ namespace WTelegram
throw new ApplicationException($"Got RpcResult({rpcResult.result.GetType().Name}) for unknown msgId {rpcResult.req_msg_id}");
break; // silently ignore results for msg_id from previous sessions
default:
_updateHandler?.Invoke(obj);
if (_updateHandler != null) await _updateHandler?.Invoke(obj);
break;
}
}
@ -393,7 +409,7 @@ namespace WTelegram
if (_session.User != null)
return Schema.Deserialize<User>(_session.User);
string phone_number = Config("phone_number");
var sentCode = await CallAsync(new Auth_SendCode
var sentCode = await CallAsync(new Fn.Auth_SendCode
{
phone_number = phone_number,
api_id = _apiId,
@ -402,16 +418,36 @@ namespace WTelegram
});
Helpers.Log(3, $"A verification code has been sent via {sentCode.type.GetType().Name[17..]}");
var verification_code = Config("verification_code");
var authorization = await CallAsync(new Auth_SignIn
Auth_AuthorizationBase authorization;
try
{
phone_number = phone_number,
phone_code_hash = sentCode.phone_code_hash,
phone_code = verification_code
});
if (authorization is not Auth_Authorization { user: User user } auth_success)
authorization = await CallAsync(new Fn.Auth_SignIn
{
phone_number = phone_number,
phone_code_hash = sentCode.phone_code_hash,
phone_code = verification_code
});
}
catch (RpcException e) when (e.Code == 400 && e.Message == "SESSION_PASSWORD_NEEDED")
{
throw new NotImplementedException("Library does not support 2FA yet"); //TODO: support 2FA
}
if (authorization is Auth_AuthorizationSignUpRequired signUpRequired)
{
if (signUpRequired.terms_of_service != null && _updateHandler != null)
await _updateHandler?.Invoke(signUpRequired.terms_of_service); // give caller the possibility to read and accept TOS
authorization = await CallAsync(new Fn.Auth_SignUp
{
phone_number = phone_number,
phone_code_hash = sentCode.phone_code_hash,
first_name = Config("first_name"),
last_name = Config("last_name"),
});
}
if (authorization is not Auth_Authorization { user: User user })
throw new ApplicationException("Failed to get Authorization: " + authorization.GetType().Name);
//TODO: support Auth_AuthorizationSignUpRequired?
_session.User = Schema.Serialize(user);
_session.Save();
return user;
}
@ -436,11 +472,11 @@ namespace WTelegram
int file_part = 0, read;
for (long bytesLeft = length; bytesLeft != 0; file_part++)
{
// TODO: parallelize several parts sending through a N-semaphore?
//TODO: parallelize several parts sending through a N-semaphore? (needs a reactor first)
read = await FullReadAsync(stream, bytes, (int)Math.Min(partSize, bytesLeft));
await CallAsync<bool>(isBig
? new Upload_SaveBigFilePart { bytes = bytes, file_id = file_id, file_part = file_part, file_total_parts = file_total_parts }
: new Upload_SaveFilePart { bytes = bytes, file_id = file_id, file_part = file_part });
? new Fn.Upload_SaveBigFilePart { bytes = bytes, file_id = file_id, file_part = file_part, file_total_parts = file_total_parts }
: new Fn.Upload_SaveFilePart { bytes = bytes, file_id = file_id, file_part = file_part });
if (!isBig) md5.TransformBlock(bytes, 0, read, null, 0);
bytesLeft -= read;
if (read < partSize && bytesLeft != 0) throw new ApplicationException($"Failed to fully read stream ({read},{bytesLeft})");
@ -451,6 +487,8 @@ namespace WTelegram
}
}
//TODO: include XML comments in nuget?
/// <summary>Helper function to send a text or media message more easily</summary>
/// <param name="peer">destination of message</param>
/// <param name="caption">media caption</param>
@ -486,7 +524,7 @@ namespace WTelegram
public Task<UpdatesBase> SendMessageAsync(InputPeer peer, string text, InputMedia media = null, int reply_to_msg_id = 0, MessageEntity[] entities = null, DateTime schedule_date = default, bool disable_preview = false)
{
ITLFunction<UpdatesBase> request = (media == null)
? new Messages_SendMessage
? new Fn.Messages_SendMessage
{
flags = GetFlags(),
peer = peer,
@ -496,9 +534,9 @@ namespace WTelegram
entities = entities,
schedule_date = schedule_date
}
: new Messages_SendMedia
: new Fn.Messages_SendMedia
{
flags = (Messages_SendMedia.Flags)GetFlags(),
flags = (Fn.Messages_SendMedia.Flags)GetFlags(),
peer = peer,
reply_to_msg_id = reply_to_msg_id,
media = media,
@ -509,13 +547,13 @@ namespace WTelegram
};
return CallAsync(request);
Messages_SendMessage.Flags GetFlags()
Fn.Messages_SendMessage.Flags GetFlags()
{
return ((reply_to_msg_id != 0) ? Messages_SendMessage.Flags.has_reply_to_msg_id : 0)
| (disable_preview ? Messages_SendMessage.Flags.no_webpage : 0)
return ((reply_to_msg_id != 0) ? Fn.Messages_SendMessage.Flags.has_reply_to_msg_id : 0)
| (disable_preview ? Fn.Messages_SendMessage.Flags.no_webpage : 0)
// | (reply_markup != null ? Messages_SendMessage.Flags.has_reply_markup : 0)
| (entities != null ? Messages_SendMessage.Flags.has_entities : 0)
| (schedule_date != default ? Messages_SendMessage.Flags.has_schedule_date : 0);
| (entities != null ? Fn.Messages_SendMessage.Flags.has_entities : 0)
| (schedule_date != default ? Fn.Messages_SendMessage.Flags.has_schedule_date : 0);
}
}
#endregion

View file

@ -21,7 +21,7 @@ namespace WTelegram
if (PublicKeys.Count == 0) LoadDefaultPublicKey();
//1)
var reqPQ = new ReqPQ() { nonce = new Int128(RNG) };
var reqPQ = new Fn.ReqPQ() { nonce = new Int128(RNG) };
await client.SendAsync(reqPQ, false);
//2)
var reply = await client.RecvInternalAsync();
@ -153,7 +153,7 @@ namespace WTelegram
// We recommend checking that g_a and g_b are between 2^{2048-64} and dh_prime - 2^{2048-64} as well.
}
private static ReqDHParams MakeReqDHparam(long publicKey_fingerprint, RSAPublicKey publicKey, PQInnerData pqInnerData)
private static Fn.ReqDHParams MakeReqDHparam(long publicKey_fingerprint, RSAPublicKey publicKey, PQInnerData pqInnerData)
{
// the following code was the way TDLib did it (and seems still accepted) until they changed on 8 July 2021
using var clearStream = new MemoryStream(255);
@ -168,7 +168,7 @@ namespace WTelegram
var encrypted_data = BigInteger.ModPow(new BigInteger(clearBuffer, true, true), // encrypt with RSA key
new BigInteger(publicKey.e, true, true), new BigInteger(publicKey.n, true, true)).ToByteArray(true, true);
return new ReqDHParams
return new Fn.ReqDHParams
{
nonce = pqInnerData.nonce,
server_nonce = pqInnerData.server_nonce,
@ -179,10 +179,10 @@ namespace WTelegram
};
}
private static SetClientDHParams MakeClientDHparams(byte[] tmp_aes_key, byte[] tmp_aes_iv, ClientDHInnerData clientDHinnerData)
private static Fn.SetClientDHParams MakeClientDHparams(byte[] tmp_aes_key, byte[] tmp_aes_iv, ClientDHInnerData clientDHinnerData)
{
// the following code was the way TDLib did it (and seems still accepted) until they changed on 8 July 2021
using var clearStream = new MemoryStream(512); //TODO: choose a useful capacity
using var clearStream = new MemoryStream(384);
clearStream.Position = 20; // skip SHA1 area (to be patched)
using var writer = new BinaryWriter(clearStream, Encoding.UTF8);
Schema.Serialize(writer, clientDHinnerData);
@ -194,7 +194,7 @@ namespace WTelegram
SHA1.HashData(clearBuffer.AsSpan(20..clearLength), clearBuffer);
var encrypted_data = AES_IGE_EncryptDecrypt(clearBuffer.AsSpan(0, clearLength + padding), tmp_aes_key, tmp_aes_iv, true);
return new SetClientDHParams
return new Fn.SetClientDHParams
{
nonce = clientDHinnerData.nonce,
server_nonce = clientDHinnerData.server_nonce,

View file

@ -20,6 +20,7 @@ namespace WTelegram
sw.WriteLine();
sw.WriteLine("namespace TL");
sw.WriteLine("{");
string tabIndent = "\t";
Dictionary<string, TypeInfo> typeInfos = new();
foreach (var ctor in schema.constructors)
{
@ -69,8 +70,9 @@ namespace WTelegram
foreach (var typeInfo in typeInfos.Values)
WriteTypeInfo(sw, typeInfo);
sw.WriteLine("\t// ---functions---");
sw.WriteLine();
sw.WriteLine("\tpublic static partial class Fn // ---functions---");
sw.WriteLine("\t{");
tabIndent = "\t\t";
var methods = new List<TypeInfo>();
foreach (var method in schema.methods)
{
@ -78,6 +80,7 @@ namespace WTelegram
typeInfo.Structs.Add(new Constructor { id = method.id, @params = method.@params, predicate = method.method, type = method.type });
WriteTypeInfo(sw, typeInfo, true);
}
sw.WriteLine("\t}");
sw.WriteLine("}");
void WriteTypeInfo(StreamWriter sw, TypeInfo typeInfo, bool isMethod = false)
@ -87,21 +90,21 @@ namespace WTelegram
if (isMethod)
parentClass = $"ITLFunction<{MapType(typeInfo.ReturnName, "")}>";
if (typeInfo.NeedAbstract == -1)
sw.WriteLine($"\tpublic abstract class {parentClass} : ITLObject {{ }}");
sw.WriteLine($"{tabIndent}public abstract class {parentClass} : ITLObject {{ }}");
int skipParams = 0;
foreach (var ctor in typeInfo.Structs)
{
string className = CSharpName(ctor.predicate) + genericType;
if (ctor.id == null)
sw.Write($"\tpublic abstract class {className} : ITLObject");
sw.Write($"{tabIndent}public abstract class {className} : ITLObject");
else
{
int ctorId = int.Parse(ctor.id);
sw.Write($"\t[TLDef(0x{ctorId:X}, \"{ctor.predicate}#{ctorId:x8} ");
sw.Write($"{tabIndent}[TLDef(0x{ctorId:X}, \"{ctor.predicate}#{ctorId:x8} ");
if (genericType != null) sw.Write($"{{{typeInfo.ReturnName}:Type}} ");
foreach (var parm in ctor.@params) sw.Write($"{parm.name}:{parm.type} ");
sw.WriteLine($"= {ctor.type}\")]");
sw.Write($"\tpublic class {className} : ");
sw.Write($"{tabIndent}public class {className} : ");
sw.Write(skipParams == 0 && typeInfo.NeedAbstract > 0 ? "ITLObject" : parentClass);
}
var parms = ctor.@params.Skip(skipParams).ToArray();
@ -110,14 +113,15 @@ namespace WTelegram
sw.WriteLine(" { }");
continue;
}
if (parms.Length == 1)
sw.Write(" { ");
else
var hasFlagEnum = parms.Any(p => p.type.StartsWith("flags."));
bool multiline = hasFlagEnum || parms.Length > 1;
if (multiline)
{
sw.WriteLine();
sw.WriteLine("\t{");
sw.WriteLine(tabIndent + "{");
}
var hasFlagEnum = parms.Any(p => p.type.StartsWith("flags."));
else
sw.Write(" { ");
if (hasFlagEnum)
{
var list = new SortedList<int, string>();
@ -136,21 +140,19 @@ namespace WTelegram
if (list.Values.Contains(name)) name += "_field";
list[mask] = name;
}
sw.Write("\t\t[Flags] public enum Flags { ");
int lineLen = 36;
string line = tabIndent + "\t[Flags] public enum Flags { ";
foreach (var (mask, name) in list)
{
var str = $"{name} = 0x{mask:X}, ";
if (lineLen + str.Length >= 140) { sw.WriteLine(); sw.Write("\t\t\t"); lineLen = 12; }
sw.Write(str);
lineLen += str.Length;
if (line.Length + str.Length + tabIndent.Length * 3 >= 134) { sw.WriteLine(line); line = tabIndent + "\t\t"; }
line += str;
}
sw.WriteLine("}");
sw.WriteLine(line.TrimEnd(',', ' ') + " }");
}
foreach (var parm in parms)
{
if (parm.type.EndsWith("?true")) continue;
if (parms.Length > 1) sw.Write("\t\t");
if (multiline) sw.Write(tabIndent + "\t");
if (parm.type == "#")
sw.Write($"public {(hasFlagEnum ? "Flags" : "int")} {parm.name};");
else
@ -163,15 +165,15 @@ namespace WTelegram
else
sw.Write($"public {MapType(parm.type, parm.name)} {MapName(parm.name)};");
}
if (parms.Length > 1) sw.WriteLine();
if (multiline) sw.WriteLine();
}
if (ctorNeedClone.Contains(className))
sw.WriteLine($"\t\tpublic {className} Clone() => ({className})MemberwiseClone();");
if (parms.Length == 1)
sw.WriteLine(" }");
sw.WriteLine($"{tabIndent}\tpublic {className} Clone() => ({className})MemberwiseClone();");
if (multiline)
sw.WriteLine(tabIndent + "}");
else
sw.WriteLine("\t}");
sw.WriteLine(" }");
skipParams = typeInfo.NeedAbstract;
}
sw.WriteLine();

View file

@ -1,7 +1,7 @@
using System;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Linq;
using System.Security.Cryptography;
using System.Text.Json;
namespace WTelegram
@ -16,36 +16,57 @@ namespace WTelegram
public long ServerTicksOffset;
public long LastSentMsgId;
public TL.DcOption DataCenter;
public byte[] User; // serialization of TL.User
public byte[] User; // serialization of TL.User
public DateTime SessionStart => _sessionStart;
private readonly DateTime _sessionStart = DateTime.UtcNow;
private string _pathname;
private byte[] _apiHash; // used as AES key for encryption of session file
internal static Session LoadOrCreate(string pathname)
internal static Session LoadOrCreate(string pathname, byte[] apiHash)
{
if (File.Exists(pathname))
{
try
{
var json = File.ReadAllText(pathname);
var session = JsonSerializer.Deserialize<Session>(json, Helpers.JsonOptions);
var session = Load(pathname, apiHash);
session._pathname = pathname;
session._apiHash = apiHash;
Helpers.Log(2, "Loaded previous session");
return session;
}
catch (Exception ex)
{
Helpers.Log(4, $"Exception while reading session file: {ex.Message}");
throw new ApplicationException($"Exception while reading session file: {ex.Message}\nDelete the file to start a new session", ex);
}
}
return new Session { _pathname = pathname, Id = Helpers.RandomLong() };
return new Session { _pathname = pathname, _apiHash = apiHash, Id = Helpers.RandomLong() };
}
internal static Session Load(string pathname, byte[] apiHash)
{
var input = File.ReadAllBytes(pathname);
using var aes = Aes.Create();
using var decryptor = aes.CreateDecryptor(apiHash, input[0..16]);
var utf8Json = decryptor.TransformFinalBlock(input, 16, input.Length - 16);
if (!SHA256.HashData(utf8Json.AsSpan(32)).SequenceEqual(utf8Json[0..32]))
throw new ApplicationException("Integrity check failed in session loading");
return JsonSerializer.Deserialize<Session>(utf8Json.AsSpan(32), Helpers.JsonOptions);
}
internal void Save()
{
//TODO: Add some encryption (with prepended SHA256) to prevent from stealing the key
File.WriteAllText(_pathname, JsonSerializer.Serialize(this, Helpers.JsonOptions));
var utf8Json = JsonSerializer.SerializeToUtf8Bytes(this, Helpers.JsonOptions);
var finalBlock = new byte[16];
var output = new byte[(16 + 32 + utf8Json.Length + 15) & ~15];
Encryption.RNG.GetBytes(output, 0, 16);
using var aes = Aes.Create();
using var encryptor = aes.CreateEncryptor(_apiHash, output[0..16]);
encryptor.TransformBlock(SHA256.HashData(utf8Json), 0, 32, output, 16);
encryptor.TransformBlock(utf8Json, 0, utf8Json.Length & ~15, output, 48);
utf8Json.AsSpan(utf8Json.Length & ~15).CopyTo(finalBlock);
encryptor.TransformFinalBlock(finalBlock, 0, utf8Json.Length & 15).CopyTo(output.AsMemory(48 + utf8Json.Length & ~15));
File.WriteAllBytes(_pathname, output);
}
internal (long msgId, int seqno) NewMsg(bool isContent)
@ -61,5 +82,12 @@ namespace WTelegram
internal DateTime MsgIdToStamp(long serverMsgId)
=> new((serverMsgId >> 32) * 10000000 - ServerTicksOffset + 621355968000000000L, DateTimeKind.Utc);
internal void Reset(TL.DcOption newDC = null)
{
DataCenter = newDC;
AuthKeyID = Salt = Seqno = 0;
AuthKey = User = null;
}
}
}

View file

@ -238,60 +238,61 @@ namespace TL
[TLDef(0xEA109B13, "destroy_auth_key_fail#ea109b13 = DestroyAuthKeyRes")]
public class DestroyAuthKeyFail : DestroyAuthKeyRes { }
// ---functions---
[TLDef(0X60469778, "req_pq#60469778 nonce:int128 = ResPQ")]
public class ReqPQ : ITLFunction<ResPQ> { public Int128 nonce; }
[TLDef(0xBE7E8EF1, "req_pq_multi#be7e8ef1 nonce:int128 = ResPQ")]
public class ReqPQmulti : ResPQ { }
[TLDef(0xD712E4BE, "req_DH_params#d712e4be nonce:int128 server_nonce:int128 p:bytes q:bytes public_key_fingerprint:long encrypted_data:bytes = Server_DH_Params")]
public class ReqDHParams : ITLFunction<ServerDHParams>
public static partial class Fn // ---functions---
{
public Int128 nonce;
public Int128 server_nonce;
public byte[] p;
public byte[] q;
public long public_key_fingerprint;
public byte[] encrypted_data;
[TLDef(0X60469778, "req_pq#60469778 nonce:int128 = ResPQ")]
public class ReqPQ : ITLFunction<ResPQ> { public Int128 nonce; }
[TLDef(0xBE7E8EF1, "req_pq_multi#be7e8ef1 nonce:int128 = ResPQ")]
public class ReqPQmulti : ResPQ { }
[TLDef(0xD712E4BE, "req_DH_params#d712e4be nonce:int128 server_nonce:int128 p:bytes q:bytes public_key_fingerprint:long encrypted_data:bytes = Server_DH_Params")]
public class ReqDHParams : ITLFunction<ServerDHParams>
{
public Int128 nonce;
public Int128 server_nonce;
public byte[] p;
public byte[] q;
public long public_key_fingerprint;
public byte[] encrypted_data;
}
[TLDef(0xF5045F1F, "set_client_DH_params#f5045f1f nonce:int128 server_nonce:int128 encrypted_data:bytes = Set_client_DH_params_answer")]
public class SetClientDHParams : ITLFunction<SetClientDHParamsAnswer>
{
public Int128 nonce;
public Int128 server_nonce;
public byte[] encrypted_data;
}
[TLDef(0x58E4A740, "rpc_drop_answer#58e4a740 req_msg_id:long = RpcDropAnswer")]
public class ReqRpcDropAnswer : ITLFunction<RpcDropAnswer> { public long req_msg_id; }
[TLDef(0xB921BD04, "get_future_salts#b921bd04 num:int = FutureSalts")]
public class GetFutureSalts : ITLFunction<FutureSalts> { public int num; }
[TLDef(0x7ABE77EC, "ping#7abe77ec ping_id:long = Pong")]
public class Ping : ITLFunction<Pong> { public long ping_id; }
[TLDef(0xF3427B8C, "ping_delay_disconnect#f3427b8c ping_id:long disconnect_delay:int = Pong")]
public class PingDelayDisconnect : ITLFunction<Pong>
{
public long ping_id;
public int disconnect_delay; // seconds
}
[TLDef(0xE7512126, "destroy_session#e7512126 session_id:long = DestroySessionRes")]
public class DestroySession : ITLFunction<DestroySessionRes> { public long session_id; }
[TLDef(0x9299359F, "http_wait#9299359f max_delay:int wait_after:int max_wait:int = HttpWait")]
public class HttpWait : ITLObject
{
public int max_delay; // ms
public int wait_after; // ms
public int max_wait; // ms
}
[TLDef(0xD1435160, "destroy_auth_key#d1435160 = DestroyAuthKeyRes")]
public class DestroyAuthKey : ITLFunction<DestroyAuthKeyRes> { }
}
[TLDef(0xF5045F1F, "set_client_DH_params#f5045f1f nonce:int128 server_nonce:int128 encrypted_data:bytes = Set_client_DH_params_answer")]
public class SetClientDHParams : ITLFunction<SetClientDHParamsAnswer>
{
public Int128 nonce;
public Int128 server_nonce;
public byte[] encrypted_data;
}
[TLDef(0x58E4A740, "rpc_drop_answer#58e4a740 req_msg_id:long = RpcDropAnswer")]
public class ReqRpcDropAnswer : ITLFunction<RpcDropAnswer> { public long req_msg_id; }
[TLDef(0xB921BD04, "get_future_salts#b921bd04 num:int = FutureSalts")]
public class GetFutureSalts : ITLFunction<FutureSalts> { public int num; }
[TLDef(0x7ABE77EC, "ping#7abe77ec ping_id:long = Pong")]
public class Ping : ITLFunction<Pong> { public long ping_id; }
[TLDef(0xF3427B8C, "ping_delay_disconnect#f3427b8c ping_id:long disconnect_delay:int = Pong")]
public class PingDelayDisconnect : ITLFunction<Pong>
{
public long ping_id;
public int disconnect_delay; // seconds
}
[TLDef(0xE7512126, "destroy_session#e7512126 session_id:long = DestroySessionRes")]
public class DestroySession : ITLFunction<DestroySessionRes> { public long session_id; }
[TLDef(0x9299359F, "http_wait#9299359f max_delay:int wait_after:int max_wait:int = HttpWait")]
public class HttpWait : ITLObject
{
public int max_delay; // ms
public int wait_after; // ms
public int max_wait; // ms
}
[TLDef(0xD1435160, "destroy_auth_key#d1435160 = DestroyAuthKeyRes")]
public class DestroyAuthKey : ITLFunction<DestroyAuthKeyRes> { }
}

File diff suppressed because it is too large Load diff

View file

@ -177,6 +177,7 @@ namespace TL
public long secret;
}
// ---functions---
public static partial class Fn // ---functions---
{
}
}