Better endianness code

This commit is contained in:
Wizou 2021-08-06 01:54:29 +02:00
parent 072928aca7
commit 710016c388
7 changed files with 27 additions and 48 deletions

View file

@ -1,4 +1,5 @@
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
@ -149,11 +150,11 @@ namespace WTelegram
writer.Write(msgId); // int64 message_id
writer.Write(0); // int32 message_data_length (to be patched)
Schema.Serialize(writer, msg); // bytes message_data
Helpers.LittleEndian(memStream.GetBuffer(), 24, (int)memStream.Length - 28); // patch message_data_length
BinaryPrimitives.WriteInt32LittleEndian(memStream.GetBuffer().AsSpan(24), (int)memStream.Length - 28); // patch message_data_length
}
else
{
Helpers.Log(1, $"Sending {msg.GetType().Name,-50} #{(short)msgId.GetHashCode():X4})");
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
using var clearWriter = new BinaryWriter(clearStream, Encoding.UTF8);
@ -167,7 +168,7 @@ namespace WTelegram
int padding = (0x7FFFFFF0 - clearLength) % 16;
clearStream.SetLength(clearLength + padding);
byte[] clearBuffer = clearStream.GetBuffer();
Helpers.LittleEndian(clearBuffer, 28, clearLength - 32); // patch message_data_length
BinaryPrimitives.WriteInt32LittleEndian(clearBuffer.AsSpan(28), clearLength - 32); // patch message_data_length
RNG.GetBytes(clearBuffer, clearLength, padding);
var clearSha1 = SHA1.HashData(clearBuffer.AsSpan(0, clearLength)); // padding excluded from computation!
byte[] encrypted_data = EncryptDecryptMessage(clearBuffer.AsSpan(0, clearLength + padding), true, _session.AuthKey, clearSha1);
@ -180,7 +181,7 @@ namespace WTelegram
var buffer = memStream.GetBuffer();
int frameLength = (int)memStream.Length;
//TODO: support Quick Ack?
Helpers.LittleEndian(buffer, 0, frameLength + 4); // patch frame_len with correct value
BinaryPrimitives.WriteInt32LittleEndian(buffer, frameLength + 4); // patch frame_len with correct value
uint crc = Force.Crc32.Crc32Algorithm.Compute(buffer, 0, frameLength);
writer.Write(crc); // int32 frame_crc
var frame = memStream.GetBuffer().AsMemory(0, frameLength + 4);
@ -194,12 +195,12 @@ namespace WTelegram
{
var data = await RecvFrameAsync();
if (data.Length == 4 && data[3] == 0xFF)
throw new ApplicationException($"Server replied with error code: {TransportError(-BitConverter.ToInt32(data))}");
throw new ApplicationException($"Server replied with error code: {TransportError(-BinaryPrimitives.ReadInt32LittleEndian(data))}");
if (data.Length < 24) // authKeyId+msgId+length+ctorNb | authKeyId+msgKey
throw new ApplicationException($"Packet payload too small: {data.Length}");
//TODO: ignore msgId <= lastRecvMsgId, ignore MsgId >= 30 sec in the future or < 300 sec in the past
long authKeyId = BitConverter.ToInt64(data);
long authKeyId = BinaryPrimitives.ReadInt64LittleEndian(data);
if (authKeyId == 0) // Unencrypted message
{
using var reader = new BinaryReader(new MemoryStream(data, 8, data.Length - 8));
@ -265,10 +266,10 @@ namespace WTelegram
byte[] frame = new byte[8];
if (await FullReadAsync(_networkStream, frame, 8) != 8)
throw new ApplicationException("Could not read frame prefix : Connection shut down");
int length = BitConverter.ToInt32(frame) - 12;
int length = BinaryPrimitives.ReadInt32LittleEndian(frame) - 12;
if (length <= 0 || length >= 0x10000)
throw new ApplicationException("Invalid frame_len");
int seqno = BitConverter.ToInt32(frame, 4);
int seqno = BinaryPrimitives.ReadInt32LittleEndian(frame.AsSpan(4));
if (seqno != _frame_seqRx++)
{
Trace.TraceWarning($"Unexpected frame_seq received: {seqno} instead of {_frame_seqRx}");
@ -281,7 +282,7 @@ namespace WTelegram
crc32 = Force.Crc32.Crc32Algorithm.Append(crc32, payload);
if (await FullReadAsync(_networkStream, frame, 4) != 4)
throw new ApplicationException("Could not read frame CRC : Connection shut down");
if (crc32 != BitConverter.ToUInt32(frame))
if (crc32 != BinaryPrimitives.ReadUInt32LittleEndian(frame))
throw new ApplicationException("Invalid envelope CRC32");
return payload;
}
@ -305,7 +306,7 @@ namespace WTelegram
rpcResult.result = Schema.DeserializeValue(reader, _lastRpcResultType);
else
rpcResult.result = Schema.Deserialize<ITLObject>(reader);
Helpers.Log(1, $" → {rpcResult.result.GetType().Name,-50} #{(short)reqMsgId.GetHashCode():X4})");
Helpers.Log(1, $" → {rpcResult.result.GetType().Name,-50} #{(short)reqMsgId.GetHashCode():X4}");
return rpcResult;
}

View file

@ -1,4 +1,5 @@
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@ -92,8 +93,8 @@ namespace WTelegram
//8)
var authKeyHash = SHA1.HashData(authKey);
retry_id = BitConverter.ToInt64(authKeyHash); // (auth_key_aux_hash)
//9)
retry_id = BinaryPrimitives.ReadInt64LittleEndian(authKeyHash); // (auth_key_aux_hash)
//9)
reply = await client.RecvInternalAsync();
if (reply is not SetClientDHParamsAnswer setClientDHparamsAnswer) throw new ApplicationException($"Expected SetClientDHParamsAnswer but got {reply.GetType().Name}");
if (setClientDHparamsAnswer is not DHGenOk) throw new ApplicationException("not dh_gen_ok");
@ -106,9 +107,9 @@ namespace WTelegram
if (!Enumerable.SequenceEqual(setClientDHparamsAnswer.new_nonce_hashN.raw, SHA1.HashData(expected_new_nonceN).Skip(4)))
throw new ApplicationException("setClientDHparamsAnswer.new_nonce_hashN mismatch");
session.AuthKeyID = BitConverter.ToInt64(authKeyHash, 12);
session.AuthKeyID = BinaryPrimitives.ReadInt64LittleEndian(authKeyHash.AsSpan(12));
session.AuthKey = authKey;
session.Salt = BitConverter.ToInt64(new_nonce, 0) ^ BitConverter.ToInt64(resPQ.server_nonce, 0);
session.Salt = BinaryPrimitives.ReadInt64LittleEndian(new_nonce.raw) ^ BinaryPrimitives.ReadInt64LittleEndian(resPQ.server_nonce.raw);
session.Save();
static (byte[] key, byte[] iv) ConstructTmpAESKeyIV(Int128 server_nonce, Int256 new_nonce)
@ -208,7 +209,7 @@ namespace WTelegram
var rsaParam = rsa.ExportParameters(false);
var publicKey = new RSAPublicKey { n = rsaParam.Modulus, e = rsaParam.Exponent };
var bareData = Schema.Serialize(publicKey).AsSpan(4); // bare serialization
var fingerprint = BitConverter.ToInt64(SHA1.HashData(bareData), 12); // 64 lower-order bits of SHA1
var fingerprint = BinaryPrimitives.ReadInt64LittleEndian(SHA1.HashData(bareData).AsSpan(12)); // 64 lower-order bits of SHA1
PublicKeys[fingerprint] = publicKey;
Helpers.Log(1, $"Loaded a public key with fingerprint {fingerprint:X}");
}

View file

@ -221,7 +221,7 @@ namespace WTelegram
}
}
static readonly HashSet<string> ctorNeedClone = new() { "User" };
static readonly HashSet<string> ctorNeedClone = new() { /*"User"*/ };
private static bool HasPrefix(Constructor ctor, IList<Param> prefixParams)
{

View file

@ -5,13 +5,13 @@ namespace WTelegram
{
public static class Helpers
{
public static Action<int, string> Log { get; set; } = DefaultLogger;
public static readonly System.Text.Json.JsonSerializerOptions JsonOptions = new(System.Text.Json.JsonSerializerDefaults.Web) { IncludeFields = true, WriteIndented = true };
public static V GetOrCreate<K, V>(this Dictionary<K, V> dictionary, K key) where V : new()
=> dictionary.TryGetValue(key, out V value) ? value : dictionary[key] = new V();
public static Action<int, string> Log { get; set; } = DefaultLogger;
private static readonly ConsoleColor[] LogLevelToColor = new[] { ConsoleColor.DarkGray, ConsoleColor.DarkCyan, ConsoleColor.Cyan,
ConsoleColor.Yellow, ConsoleColor.Red, ConsoleColor.Magenta, ConsoleColor.DarkBlue };
private static void DefaultLogger(int level, string message)
@ -28,27 +28,7 @@ namespace WTelegram
return span[0];
}
public static void LittleEndian(byte[] buffer, int offset, int value)
{
buffer[offset + 0] = (byte)value;
buffer[offset + 1] = (byte)(value >> 8);
buffer[offset + 2] = (byte)(value >> 16);
buffer[offset + 3] = (byte)(value >> 24);
}
public static void LittleEndian(byte[] buffer, int offset, long value)
{
buffer[offset + 0] = (byte)value;
buffer[offset + 1] = (byte)(value >> 8);
buffer[offset + 2] = (byte)(value >> 16);
buffer[offset + 3] = (byte)(value >> 24);
buffer[offset + 4] = (byte)(value >> 32);
buffer[offset + 5] = (byte)(value >> 40);
buffer[offset + 6] = (byte)(value >> 48);
buffer[offset + 7] = (byte)(value >> 56);
}
public static byte[] ToBigEndian(ulong value)
public static byte[] ToBigEndian(ulong value) // variable-size buffer
{
int i;
var temp = value;
@ -58,16 +38,16 @@ namespace WTelegram
return result;
}
public static ulong FromBigEndian(byte[] bytes)
public static ulong FromBigEndian(byte[] bytes) // variable-size buffer
{
if (bytes.Length > 8) throw new ArgumentException($"expected bytes length <=8 but got {bytes.Length}");
if (bytes.Length > 8) throw new ArgumentException($"expected bytes length <= 8 but got {bytes.Length}");
ulong result = 0;
foreach (byte b in bytes)
result = (result << 8) + b;
return result;
}
public static ulong PQFactorize(ulong pq)
public static ulong PQFactorize(ulong pq) // ported from https://github.com/tdlib/td/blob/master/tdutils/td/utils/crypto.cpp#L90
{
if (pq < 2) return 1;
var random = new Random();

View file

@ -39,9 +39,7 @@ namespace WTelegram
Helpers.Log(4, $"Exception while reading session file: {ex.Message}");
}
}
var sessionId = new byte[8];
Encryption.RNG.GetBytes(sessionId);
return new Session { _pathname = pathname, Id = BitConverter.ToInt64(sessionId) };
return new Session { _pathname = pathname, Id = Helpers.RandomLong() };
}
internal void Save()

3
TL.cs
View file

@ -197,8 +197,7 @@ namespace TL
writer.Write((byte)length);
else
{
writer.Write((byte)254);
writer.Write(BitConverter.GetBytes(length)[0..3]);
writer.Write(length << 8 | 254);
length += 3;
}
writer.Write(bytes);

View file

@ -4,7 +4,7 @@
<OutputType>Library</OutputType>
<TargetFramework>net5.0</TargetFramework>
<RootNamespace>WTelegram</RootNamespace>
<Description>Telegram client library written 100% in C#</Description>
<Description>Telegram client library written 100% in C# and .NET Core</Description>
<Authors>Wizou</Authors>
<PackageProjectUrl>https://github.com/wiz0u/WTelegramClient</PackageProjectUrl>
</PropertyGroup>