diff --git a/generator/MTProtoGenerator.cs b/generator/MTProtoGenerator.cs index 26011b6..c9e71f0 100644 --- a/generator/MTProtoGenerator.cs +++ b/generator/MTProtoGenerator.cs @@ -83,7 +83,7 @@ public class MTProtoGenerator : IIncrementalGenerator ns = symbol.ContainingNamespace.ToString(); name = symbol.Name; if (!namespaces.TryGetValue(ns, out var classes)) namespaces[ns] = classes = []; - if (name is "_Message" or "RpcResult" or "MsgCopy") + if (name is "_Message" or "MsgCopy") { classes[name] = "\t\tpublic void WriteTL(BinaryWriter writer) => throw new NotSupportedException();"; continue; @@ -93,7 +93,7 @@ public class MTProtoGenerator : IIncrementalGenerator else if (name != "Null") { if (ns == "TL.Methods") - methodsTL.AppendLine($"\t\t\t[0x{id:X8}] = {(ns == "TL" ? "" : ns + '.')}{name}{(symbol.IsGenericType ? "" : "")}.ReadTL,"); + methodsTL.AppendLine($"\t\t\t[0x{id:X8}] = {(ns == "TL" ? "" : ns + '.')}{name}{(symbol.IsGenericType ? "" : "")}.ReadTL,"); if (ns != "TL.Methods" || name == "Ping") tableTL.AppendLine($"\t\t\t[0x{id:X8}] = {(ns == "TL" ? "" : ns + '.')}{name}.ReadTL,"); } @@ -176,7 +176,7 @@ public class MTProtoGenerator : IIncrementalGenerator writeTl.AppendLine($"writer.WriteTLMessages({member.Name});"); break; case "TL.IObject": case "TL.IMethod": - readTL.AppendLine($"r.{member.Name} = {(memberType == "TL.IObject" ? "" : $"({memberType})")}reader.ReadTLObject();"); + readTL.AppendLine($"r.{member.Name} = {(memberType == "TL.IObject" ? "reader.ReadTLObject()" : "reader.ReadTLMethod()")};"); writeTl.AppendLine($"{member.Name}.WriteTL(writer);"); break; case "System.Collections.Generic.Dictionary": @@ -187,14 +187,23 @@ public class MTProtoGenerator : IIncrementalGenerator readTL.AppendLine($"r.{member.Name} = reader.ReadTLDictionary();"); writeTl.AppendLine($"writer.WriteTLVector({member.Name}.Values.ToArray());"); break; + case "object": + readTL.AppendLine($"r.{member.Name} = reader.ReadTLObject();"); + writeTl.AppendLine($"writer.WriteTLValue({member.Name}, {member.Name}.GetType());"); + break; default: if (member.Type is IArrayTypeSymbol arrayType) { if (name is "FutureSalts") + { readTL.AppendLine($"r.{member.Name} = reader.ReadTLRawVector<{memberType.Substring(0, memberType.Length - 2)}>(0x0949D9DC).ToArray();"); + writeTl.AppendLine($"writer.WriteTLRawVector({member.Name}, 16);"); + } else + { readTL.AppendLine($"r.{member.Name} = reader.ReadTLVector<{memberType.Substring(0, memberType.Length - 2)}>();"); - writeTl.AppendLine($"writer.WriteTLVector({member.Name});"); + writeTl.AppendLine($"writer.WriteTLVector({member.Name});"); + } } else if (member.Type.BaseType.SpecialType == SpecialType.System_Enum) { diff --git a/src/Encryption.cs b/src/Encryption.cs index 09f6caa..4025d9e 100644 --- a/src/Encryption.cs +++ b/src/Encryption.cs @@ -94,7 +94,7 @@ namespace WTelegram if (serverDHparams is not ServerDHParamsOk serverDHparamsOk) throw new WTException("not server_DH_params_ok"); if (serverDHparamsOk.nonce != nonce) throw new WTException("Nonce mismatch"); if (serverDHparamsOk.server_nonce != resPQ.server_nonce) throw new WTException("Server Nonce mismatch"); - var (tmp_aes_key, tmp_aes_iv) = ConstructTmpAESKeyIV(resPQ.server_nonce, pqInnerData.new_nonce); + var (tmp_aes_key, tmp_aes_iv) = ConstructTmpAESKeyIV(sha1, resPQ.server_nonce, pqInnerData.new_nonce); var answer = AES_IGE_EncryptDecrypt(serverDHparamsOk.encrypted_answer, tmp_aes_key, tmp_aes_iv, false); using var answerReader = new BinaryReader(new MemoryStream(answer)); @@ -163,26 +163,26 @@ namespace WTelegram session.AuthKey = authKey; session.Salt = BinaryPrimitives.ReadInt64LittleEndian(pqInnerData.new_nonce.raw) ^ BinaryPrimitives.ReadInt64LittleEndian(resPQ.server_nonce.raw); session.OldSalt = session.Salt; + } - (byte[] key, byte[] iv) ConstructTmpAESKeyIV(TL.Int128 server_nonce, Int256 new_nonce) - { - byte[] tmp_aes_key = new byte[32], tmp_aes_iv = new byte[32]; - sha1.TransformBlock(new_nonce, 0, 32, null, 0); - sha1.TransformFinalBlock(server_nonce, 0, 16); - sha1.Hash.CopyTo(tmp_aes_key, 0); // tmp_aes_key := SHA1(new_nonce + server_nonce) - sha1.Initialize(); - sha1.TransformBlock(server_nonce, 0, 16, null, 0); - sha1.TransformFinalBlock(new_nonce, 0, 32); - Array.Copy(sha1.Hash, 0, tmp_aes_key, 20, 12); // + SHA1(server_nonce, new_nonce)[0:12] - Array.Copy(sha1.Hash, 12, tmp_aes_iv, 0, 8); // tmp_aes_iv != SHA1(server_nonce, new_nonce)[12:8] - sha1.Initialize(); - sha1.TransformBlock(new_nonce, 0, 32, null, 0); - sha1.TransformFinalBlock(new_nonce, 0, 32); - sha1.Hash.CopyTo(tmp_aes_iv, 8); // + SHA(new_nonce + new_nonce) - Array.Copy(new_nonce, 0, tmp_aes_iv, 28, 4); // + new_nonce[0:4] - sha1.Initialize(); - return (tmp_aes_key, tmp_aes_iv); - } + public static (byte[] key, byte[] iv) ConstructTmpAESKeyIV(SHA1 sha1, TL.Int128 server_nonce, Int256 new_nonce) + { + byte[] tmp_aes_key = new byte[32], tmp_aes_iv = new byte[32]; + sha1.TransformBlock(new_nonce, 0, 32, null, 0); + sha1.TransformFinalBlock(server_nonce, 0, 16); + sha1.Hash.CopyTo(tmp_aes_key, 0); // tmp_aes_key := SHA1(new_nonce + server_nonce) + sha1.Initialize(); + sha1.TransformBlock(server_nonce, 0, 16, null, 0); + sha1.TransformFinalBlock(new_nonce, 0, 32); + Array.Copy(sha1.Hash, 0, tmp_aes_key, 20, 12); // + SHA1(server_nonce, new_nonce)[0:12] + Array.Copy(sha1.Hash, 12, tmp_aes_iv, 0, 8); // tmp_aes_iv != SHA1(server_nonce, new_nonce)[12:8] + sha1.Initialize(); + sha1.TransformBlock(new_nonce, 0, 32, null, 0); + sha1.TransformFinalBlock(new_nonce, 0, 32); + sha1.Hash.CopyTo(tmp_aes_iv, 8); // + SHA(new_nonce + new_nonce) + Array.Copy(new_nonce, 0, tmp_aes_iv, 28, 4); // + new_nonce[0:4] + sha1.Initialize(); + return (tmp_aes_key, tmp_aes_iv); } internal static void CheckGoodPrime(BigInteger p, int g) @@ -278,7 +278,7 @@ j4WcDuXc2CTHgH8gFTNhp/Y8/SpDOhvn9QIDAQAB -----END RSA PUBLIC KEY-----"); } - internal static byte[] EncryptDecryptMessage(Span input, bool encrypt, int x, byte[] authKey, byte[] msgKey, int msgKeyOffset, SHA256 sha256) + public static byte[] EncryptDecryptMessage(Span input, bool encrypt, int x, byte[] authKey, byte[] msgKey, int msgKeyOffset, SHA256 sha256) { // first, construct AES key & IV byte[] aes_key = new byte[32], aes_iv = new byte[32]; @@ -299,7 +299,7 @@ j4WcDuXc2CTHgH8gFTNhp/Y8/SpDOhvn9QIDAQAB return AES_IGE_EncryptDecrypt(input, aes_key, aes_iv, encrypt); } - internal static byte[] AES_IGE_EncryptDecrypt(Span input, byte[] aes_key, byte[] aes_iv, bool encrypt) + public static byte[] AES_IGE_EncryptDecrypt(Span input, byte[] aes_key, byte[] aes_iv, bool encrypt) { if (input.Length % 16 != 0) throw new WTException("AES_IGE input size not divisible by 16"); diff --git a/src/TL.cs b/src/TL.cs index 67342ea..7c4b65d 100644 --- a/src/TL.cs +++ b/src/TL.cs @@ -16,7 +16,7 @@ namespace TL #else public interface IObject { } #endif - public interface IMethod : IObject { } + public interface IMethod : IObject { } public interface IPeerResolver { IPeerInfo UserOrChat(Peer peer); } [AttributeUsage(AttributeTargets.Class)] @@ -105,6 +105,17 @@ namespace TL #endif } + public static IMethod ReadTLMethod(this BinaryReader reader) + { + uint ctorNb = reader.ReadUInt32(); + if (!Layer.Methods.TryGetValue(ctorNb, out var ctor)) + throw new WTelegram.WTException($"Cannot find method for ctor #{ctorNb:x}"); + var method = ctor?.Invoke(reader); + if (method is IMethod && typeof(X) == typeof(object)) + method = new BoolMethod { query = method }; + return (IMethod)method; + } + internal static void WriteTLValue(this BinaryWriter writer, object value, Type valueType) { if (value == null) @@ -222,6 +233,21 @@ namespace TL writer.WriteTLValue(array.GetValue(i), elementType); } + internal static void WriteTLRawVector(this BinaryWriter writer, Array array, int elementSize) + { + var startPos = writer.BaseStream.Position; + int count = array.Length; + var elementType = array.GetType().GetElementType(); + for (int i = count - 1; i >= 0; i--) + { + writer.BaseStream.Position = startPos + i * elementSize; + writer.WriteTLValue(array.GetValue(i), elementType); + } + writer.BaseStream.Position = startPos; + writer.Write(count); + writer.BaseStream.Position = startPos + count * elementSize + 4; + } + internal static List ReadTLRawVector(this BinaryReader reader, uint ctorNb) { int count = reader.ReadInt32(); @@ -443,4 +469,16 @@ namespace TL [TLDef(0x3072CFA1)] //gzip_packed#3072cfa1 packed_data:bytes = Object public sealed partial class GzipPacked : IObject { public byte[] packed_data; } + + public sealed class Null : IObject + { + public readonly static Null Instance = new(); + public void WriteTL(BinaryWriter writer) => writer.WriteTLNull(typeof(X)); + } + + public sealed class BoolMethod : IMethod + { + public IObject query; + public void WriteTL(BinaryWriter writer) => query.WriteTL(writer); + } }