mirror of
https://github.com/wiz0u/WTelegramClient.git
synced 2025-12-06 06:52:01 +01:00
Add compatibility with .NET Standard 2.0 (multi-target)
This commit is contained in:
commit
866c562d81
|
|
@ -32,7 +32,6 @@ namespace WTelegram
|
|||
private long _lastRecvMsgId;
|
||||
private readonly List<long> _msgsToAck = new();
|
||||
private readonly Random _random = new();
|
||||
private readonly SHA256 _sha256 = SHA256.Create();
|
||||
private int _unexpectedSaltChange;
|
||||
private Task _reactorTask;
|
||||
private long _bareRequest;
|
||||
|
|
@ -103,7 +102,7 @@ namespace WTelegram
|
|||
{
|
||||
if (_reactorTask != null)
|
||||
throw new ApplicationException("Already connected!");
|
||||
var endpoint = _session.DataCenter == null ? IPEndPoint.Parse(Config("server_address"))
|
||||
var endpoint = _session.DataCenter == null ? Compat.IPEndPoint_Parse(Config("server_address"))
|
||||
: new IPEndPoint(IPAddress.Parse(_session.DataCenter.ip_address), _session.DataCenter.port);
|
||||
Helpers.Log(2, $"Connecting to {endpoint}...");
|
||||
//TODO: maintain different connections/sessions to different DCs for main API/file download/file upload?
|
||||
|
|
@ -162,6 +161,7 @@ namespace WTelegram
|
|||
return msg.GetType().Name;
|
||||
};
|
||||
|
||||
#pragma warning disable CA1835 // necessary for .NET Standard 2.0 compilation
|
||||
private async Task<long> SendAsync(ITLFunction func, bool isContent)
|
||||
{
|
||||
if (_session.AuthKeyID != 0 && isContent && CheckMsgsToAck() is MsgsAck msgsAck)
|
||||
|
|
@ -219,10 +219,10 @@ namespace WTelegram
|
|||
BinaryPrimitives.WriteInt32LittleEndian(clearBuffer.AsSpan(prepend + 28), clearLength - 32); // patch message_data_length
|
||||
RNG.GetBytes(clearBuffer, prepend + clearLength, padding);
|
||||
#if MTPROTO1
|
||||
var msgKeyLarge = SHA1.HashData(clearBuffer.AsSpan(0, clearLength)); // padding excluded from computation!
|
||||
var msgKeyLarge = Sha1.ComputeHash(clearBuffer, 0, clearLength); // padding excluded from computation!
|
||||
const int msgKeyOffset = 4; // msg_key = low 128-bits of SHA1(plaintext)
|
||||
#else
|
||||
var msgKeyLarge = SHA256.HashData(clearBuffer.AsSpan(0, prepend + clearLength + padding));
|
||||
var msgKeyLarge = Sha256.ComputeHash(clearBuffer, 0, prepend + clearLength + padding);
|
||||
const int msgKeyOffset = 8; // msg_key = middle 128-bits of SHA256(authkey_part+plaintext+padding)
|
||||
#endif
|
||||
byte[] encrypted_data = EncryptDecryptMessage(clearBuffer.AsSpan(prepend, clearLength + padding), true, _session.AuthKey, msgKeyLarge);
|
||||
|
|
@ -237,10 +237,9 @@ namespace WTelegram
|
|||
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);
|
||||
//TODO: support Transport obfuscation?
|
||||
|
||||
await _networkStream.WriteAsync(frame);
|
||||
await _networkStream.WriteAsync(memStream.GetBuffer(), 0, frameLength + 4);
|
||||
_lastSentMsg = func;
|
||||
}
|
||||
finally
|
||||
|
|
@ -254,12 +253,13 @@ namespace WTelegram
|
|||
{
|
||||
for (int offset = 0; offset != length;)
|
||||
{
|
||||
var read = await stream.ReadAsync(buffer.AsMemory(offset, length - offset), ct);
|
||||
var read = await stream.ReadAsync(buffer, offset, length - offset, ct);
|
||||
if (read == 0) return offset;
|
||||
offset += read;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
#pragma warning restore CA1835
|
||||
|
||||
private async Task<byte[]> RecvFrameAsync(CancellationToken ct)
|
||||
{
|
||||
|
|
@ -343,13 +343,13 @@ namespace WTelegram
|
|||
if ((seqno & 1) != 0) lock(_msgsToAck) _msgsToAck.Add(msgId);
|
||||
#if MTPROTO1
|
||||
if (decrypted_data.Length - 32 - length is < 0 or > 15) throw new ApplicationException($"Unexpected decrypted message_data_length {length} / {decrypted_data.Length - 32}");
|
||||
if (!data.AsSpan(8, 16).SequenceEqual(SHA1.HashData(decrypted_data.AsSpan(0, 32 + length)).AsSpan(4)))
|
||||
if (!data.AsSpan(8, 16).SequenceEqual(Sha1Recv.ComputeHash(decrypted_data, 0, 32 + length).AsSpan(4)))
|
||||
throw new ApplicationException($"Mismatch between MsgKey & decrypted SHA1");
|
||||
#else
|
||||
if (decrypted_data.Length - 32 - length is < 12 or > 1024) throw new ApplicationException($"Unexpected decrypted message_data_length {length} / {decrypted_data.Length - 32}");
|
||||
_sha256.TransformBlock(_session.AuthKey, 96, 32, null, 0);
|
||||
_sha256.TransformFinalBlock(decrypted_data, 0, decrypted_data.Length);
|
||||
if (!data.AsSpan(8, 16).SequenceEqual(_sha256.Hash.AsSpan(8, 16)))
|
||||
Sha256Recv.TransformBlock(_session.AuthKey, 96, 32, null, 0);
|
||||
Sha256Recv.TransformFinalBlock(decrypted_data, 0, decrypted_data.Length);
|
||||
if (!data.AsSpan(8, 16).SequenceEqual(Sha256Recv.Hash.AsSpan(8, 16)))
|
||||
throw new ApplicationException($"Mismatch between MsgKey & decrypted SHA1");
|
||||
#endif
|
||||
var ctorNb = reader.ReadUInt32();
|
||||
|
|
@ -587,7 +587,7 @@ namespace WTelegram
|
|||
if (_bareRequest != 0)
|
||||
{
|
||||
var (type, tcs) = PullPendingRequest(_bareRequest);
|
||||
if (obj.GetType().IsAssignableTo(type))
|
||||
if (type.IsAssignableFrom(obj.GetType()))
|
||||
{
|
||||
_bareRequest = 0;
|
||||
_ = Task.Run(() => tcs.SetResult(obj));
|
||||
|
|
|
|||
85
src/Compat.cs
Normal file
85
src/Compat.cs
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Formats.Asn1;
|
||||
using System.Net;
|
||||
using System.Numerics;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace WTelegram
|
||||
{
|
||||
static class Compat
|
||||
{
|
||||
#if NETCOREAPP2_1_OR_GREATER
|
||||
internal static IPEndPoint IPEndPoint_Parse(string addr)
|
||||
=> IPEndPoint.Parse(addr);
|
||||
internal static BigInteger BigEndianInteger(byte[] value)
|
||||
=> new(value, true, true);
|
||||
#else
|
||||
internal static BigInteger BigEndianInteger(byte[] value)
|
||||
{
|
||||
var data = new byte[value.Length + 1];
|
||||
value.CopyTo(data, 1);
|
||||
Array.Reverse(data);
|
||||
return new BigInteger(data);
|
||||
}
|
||||
|
||||
internal static byte[] ToByteArray(this BigInteger bigInteger, bool isUnsigned = false, bool isBigEndian = false)
|
||||
{
|
||||
if (!isBigEndian || !isUnsigned) throw new ArgumentException("Unexpected parameters to BigInteger");
|
||||
var result = bigInteger.ToByteArray();
|
||||
if (result[^1] == 0) result = result[0..^1];
|
||||
Array.Reverse(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static int GetBitLength(this BigInteger bigInteger)
|
||||
{
|
||||
var bytes = bigInteger.ToByteArray();
|
||||
var length = bytes.Length * 8;
|
||||
int lastByte = bytes[^1];
|
||||
while ((lastByte & 0x80) == 0) { length--; lastByte = (lastByte << 1) + 1; }
|
||||
return length;
|
||||
}
|
||||
|
||||
public static V GetValueOrDefault<K, V>(this Dictionary<K, V> dictionary, K key, V defaultValue = default)
|
||||
=> dictionary.TryGetValue(key, out V value) ? value : defaultValue;
|
||||
|
||||
public static void Deconstruct<K, V>(this KeyValuePair<K, V> kvp, out K key, out V value)
|
||||
{
|
||||
key = kvp.Key;
|
||||
value = kvp.Value;
|
||||
}
|
||||
|
||||
internal static IPEndPoint IPEndPoint_Parse(string addr)
|
||||
{
|
||||
int colon = addr.IndexOf(':');
|
||||
return new IPEndPoint(IPAddress.Parse(addr[0..colon]), int.Parse(addr[(colon + 1)..]));
|
||||
}
|
||||
|
||||
internal static void ImportFromPem(this RSA rsa, string pem)
|
||||
{
|
||||
var header = pem.IndexOf("-----BEGIN RSA PUBLIC KEY-----");
|
||||
var footer = pem.IndexOf("-----END RSA PUBLIC KEY-----");
|
||||
if (header == -1 || footer <= header) throw new ArgumentException("Invalid RSA Public Key");
|
||||
byte[] bytes = System.Convert.FromBase64String(pem[(header+30)..footer]);
|
||||
var reader = new AsnReader(bytes, AsnEncodingRules.BER);
|
||||
reader = reader.ReadSequence(Asn1Tag.Sequence);
|
||||
var m = reader.ReadIntegerBytes();
|
||||
var e = reader.ReadIntegerBytes();
|
||||
rsa.ImportParameters(new RSAParameters { Modulus = m.ToArray(), Exponent = e.ToArray() });
|
||||
}
|
||||
}
|
||||
|
||||
static class Convert
|
||||
{
|
||||
internal static byte[] FromHexString(string hex)
|
||||
{
|
||||
int NumberChars = hex.Length;
|
||||
byte[] bytes = new byte[NumberChars / 2];
|
||||
for (int i = 0; i < NumberChars; i += 2)
|
||||
bytes[i / 2] = System.Convert.ToByte(hex.Substring(i, 2), 16);
|
||||
return bytes;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
@ -8,12 +8,20 @@ using System.Security.Cryptography;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TL;
|
||||
using static WTelegram.Compat;
|
||||
|
||||
namespace WTelegram
|
||||
{
|
||||
internal static class Encryption
|
||||
{
|
||||
internal static readonly RNGCryptoServiceProvider RNG = new();
|
||||
internal static readonly SHA1 Sha1 = SHA1.Create();
|
||||
internal static readonly SHA256 Sha256 = SHA256.Create();
|
||||
#if MTPROTO1
|
||||
internal static readonly SHA1 Sha1Recv = SHA1.Create();
|
||||
#else
|
||||
internal static readonly SHA256 Sha256Recv = SHA256.Create();
|
||||
#endif
|
||||
private static readonly Dictionary<long, RSAPublicKey> PublicKeys = new();
|
||||
|
||||
internal static async Task CreateAuthorizationKey(Client client, Session session)
|
||||
|
|
@ -59,9 +67,9 @@ namespace WTelegram
|
|||
if (clearLength > 255) throw new ApplicationException("PQInnerData too big");
|
||||
byte[] clearBuffer = clearStream.GetBuffer();
|
||||
RNG.GetBytes(clearBuffer, clearLength, 255 - clearLength);
|
||||
SHA1.HashData(clearBuffer.AsSpan(20..clearLength), clearBuffer); // patch with SHA1
|
||||
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);
|
||||
Sha1.ComputeHash(clearBuffer, 20, clearLength - 20).CopyTo(clearBuffer, 0); // patch with SHA1
|
||||
encrypted_data = BigInteger.ModPow(BigEndianInteger(clearBuffer), // encrypt with RSA key
|
||||
BigEndianInteger(publicKey.e), BigEndianInteger(publicKey.n)).ToByteArray(true, true);
|
||||
}
|
||||
var serverDHparams = await client.ReqDHParams(pqInnerData.nonce, pqInnerData.server_nonce, pqInnerData.p, pqInnerData.q, fingerprint, encrypted_data);
|
||||
//5)
|
||||
|
|
@ -78,19 +86,19 @@ namespace WTelegram
|
|||
if (answerObj is not ServerDHInnerData serverDHinnerData) throw new ApplicationException("not server_DH_inner_data");
|
||||
long padding = encryptedReader.BaseStream.Length - encryptedReader.BaseStream.Position;
|
||||
if (padding >= 16) throw new ApplicationException("Too much pad");
|
||||
if (!Enumerable.SequenceEqual(SHA1.HashData(answer.AsSpan(20..^(int)padding)), answerHash))
|
||||
if (!Enumerable.SequenceEqual(Sha1.ComputeHash(answer, 20, answer.Length - (int)padding - 20), answerHash))
|
||||
throw new ApplicationException("Answer SHA1 mismatch");
|
||||
if (serverDHinnerData.nonce != nonce) throw new ApplicationException("Nonce mismatch");
|
||||
if (serverDHinnerData.server_nonce != resPQ.server_nonce) throw new ApplicationException("Server Nonce mismatch");
|
||||
var g_a = new BigInteger(serverDHinnerData.g_a, true, true);
|
||||
var dh_prime = new BigInteger(serverDHinnerData.dh_prime, true, true);
|
||||
var g_a = BigEndianInteger(serverDHinnerData.g_a);
|
||||
var dh_prime = BigEndianInteger(serverDHinnerData.dh_prime);
|
||||
ValidityChecks(dh_prime, serverDHinnerData.g);
|
||||
Helpers.Log(1, $"Server time: {serverDHinnerData.server_time} UTC");
|
||||
session.ServerTicksOffset = (serverDHinnerData.server_time - localTime).Ticks;
|
||||
//6)
|
||||
var bData = new byte[256];
|
||||
RNG.GetBytes(bData);
|
||||
var b = new BigInteger(bData, true, true);
|
||||
var b = BigEndianInteger(bData);
|
||||
var g_b = BigInteger.ModPow(serverDHinnerData.g, b, dh_prime);
|
||||
var clientDHinnerData = new ClientDHInnerData
|
||||
{
|
||||
|
|
@ -109,7 +117,7 @@ namespace WTelegram
|
|||
clearStream.SetLength(clearLength + paddingToAdd);
|
||||
byte[] clearBuffer = clearStream.GetBuffer();
|
||||
RNG.GetBytes(clearBuffer, clearLength, paddingToAdd);
|
||||
SHA1.HashData(clearBuffer.AsSpan(20..clearLength), clearBuffer);
|
||||
Sha1.ComputeHash(clearBuffer, 20, clearLength - 20).CopyTo(clearBuffer, 0);
|
||||
|
||||
encrypted_data = AES_IGE_EncryptDecrypt(clearBuffer.AsSpan(0, clearLength + paddingToAdd), tmp_aes_key, tmp_aes_iv, true);
|
||||
}
|
||||
|
|
@ -118,7 +126,7 @@ namespace WTelegram
|
|||
var gab = BigInteger.ModPow(g_a, b, dh_prime);
|
||||
var authKey = gab.ToByteArray(true, true);
|
||||
//8)
|
||||
var authKeyHash = SHA1.HashData(authKey);
|
||||
var authKeyHash = Sha1.ComputeHash(authKey);
|
||||
retry_id = BinaryPrimitives.ReadInt64LittleEndian(authKeyHash); // (auth_key_aux_hash)
|
||||
//9)
|
||||
if (setClientDHparamsAnswer is not DhGenOk dhGenOk) throw new ApplicationException("not dh_gen_ok");
|
||||
|
|
@ -128,7 +136,7 @@ namespace WTelegram
|
|||
pqInnerData.new_nonce.raw.CopyTo(expected_new_nonceN, 0);
|
||||
expected_new_nonceN[32] = 1;
|
||||
Array.Copy(authKeyHash, 0, expected_new_nonceN, 33, 8); // (auth_key_aux_hash)
|
||||
if (!Enumerable.SequenceEqual(dhGenOk.new_nonce_hash1.raw, SHA1.HashData(expected_new_nonceN).Skip(4)))
|
||||
if (!Enumerable.SequenceEqual(dhGenOk.new_nonce_hash1.raw, Sha1.ComputeHash(expected_new_nonceN).Skip(4)))
|
||||
throw new ApplicationException("setClientDHparamsAnswer.new_nonce_hashN mismatch");
|
||||
|
||||
session.AuthKeyID = BinaryPrimitives.ReadInt64LittleEndian(authKeyHash.AsSpan(12));
|
||||
|
|
@ -186,8 +194,8 @@ namespace WTelegram
|
|||
rsa.ImportFromPem(pem);
|
||||
var rsaParam = rsa.ExportParameters(false);
|
||||
var publicKey = new RSAPublicKey { n = rsaParam.Modulus, e = rsaParam.Exponent };
|
||||
var bareData = publicKey.Serialize().AsSpan(4); // bare serialization
|
||||
var fingerprint = BinaryPrimitives.ReadInt64LittleEndian(SHA1.HashData(bareData).AsSpan(12)); // 64 lower-order bits of SHA1
|
||||
var bareData = publicKey.Serialize(); // bare serialization
|
||||
var fingerprint = BinaryPrimitives.ReadInt64LittleEndian(Sha1.ComputeHash(bareData, 4, bareData.Length - 4).AsSpan(12)); // 64 lower-order bits of SHA1
|
||||
PublicKeys[fingerprint] = publicKey;
|
||||
Helpers.Log(1, $"Loaded a public key with fingerprint {fingerprint:X}");
|
||||
}
|
||||
|
|
@ -210,7 +218,7 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
|
|||
int x = encrypt ? 0 : 8;
|
||||
byte[] aes_key = new byte[32], aes_iv = new byte[32];
|
||||
#if MTPROTO1
|
||||
using var sha1 = SHA1.Create();
|
||||
var sha1 = encrypt ? Sha1 : Sha1Recv;
|
||||
sha1.TransformBlock(msgKeyLarge, 4, 16, null, 0); // msgKey
|
||||
sha1.TransformFinalBlock(authKey, x, 32); // authKey[x:32]
|
||||
var sha1_a = sha1.Hash;
|
||||
|
|
@ -232,7 +240,7 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
|
|||
Array.Copy(sha1_c, 16, aes_iv, 20, 4);
|
||||
Array.Copy(sha1_d, 0, aes_iv, 24, 8);
|
||||
#else
|
||||
using var sha256 = SHA256.Create();
|
||||
var sha256 = encrypt ? Sha256 : Sha256Recv;
|
||||
sha256.TransformBlock(msgKeyLarge, 8, 16, null, 0); // msgKey
|
||||
sha256.TransformFinalBlock(authKey, x, 36); // authKey[x:36]
|
||||
var sha256_a = sha256.Hash;
|
||||
|
|
@ -289,58 +297,114 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
|
|||
|
||||
var passwordBytes = Encoding.UTF8.GetBytes(password);
|
||||
var g = new BigInteger(algo.g);
|
||||
var p = new BigInteger(algo.p, true, true);
|
||||
var g_b = new BigInteger(accountPassword.srp_B, true, true);
|
||||
var p = BigEndianInteger(algo.p);
|
||||
var g_b = BigEndianInteger(accountPassword.srp_B);
|
||||
var g_b_256 = g_b.To256Bytes();
|
||||
var g_256 = g.To256Bytes();
|
||||
ValidityChecks(p, algo.g);
|
||||
|
||||
var sha256 = SHA256.Create();
|
||||
sha256.TransformBlock(algo.salt1, 0, algo.salt1.Length, null, 0);
|
||||
sha256.TransformBlock(passwordBytes, 0, passwordBytes.Length, null, 0);
|
||||
sha256.TransformFinalBlock(algo.salt1, 0, algo.salt1.Length);
|
||||
var hash = sha256.Hash;
|
||||
sha256.TransformBlock(algo.salt2, 0, algo.salt2.Length, null, 0);
|
||||
sha256.TransformBlock(hash, 0, 32, null, 0);
|
||||
sha256.TransformFinalBlock(algo.salt2, 0, algo.salt2.Length);
|
||||
hash = sha256.Hash;
|
||||
Sha256.TransformBlock(algo.salt1, 0, algo.salt1.Length, null, 0);
|
||||
Sha256.TransformBlock(passwordBytes, 0, passwordBytes.Length, null, 0);
|
||||
Sha256.TransformFinalBlock(algo.salt1, 0, algo.salt1.Length);
|
||||
var hash = Sha256.Hash;
|
||||
Sha256.TransformBlock(algo.salt2, 0, algo.salt2.Length, null, 0);
|
||||
Sha256.TransformBlock(hash, 0, 32, null, 0);
|
||||
Sha256.TransformFinalBlock(algo.salt2, 0, algo.salt2.Length);
|
||||
hash = Sha256.Hash;
|
||||
#if NETCOREAPP2_0_OR_GREATER
|
||||
var pbkdf2 = new Rfc2898DeriveBytes(hash, algo.salt1, 100000, HashAlgorithmName.SHA512).GetBytes(64);
|
||||
sha256.TransformBlock(algo.salt2, 0, algo.salt2.Length, null, 0);
|
||||
sha256.TransformBlock(pbkdf2, 0, 64, null, 0);
|
||||
sha256.TransformFinalBlock(algo.salt2, 0, algo.salt2.Length);
|
||||
var x = new BigInteger(sha256.Hash, true, true);
|
||||
#else
|
||||
var pbkdf2 = PBKDF2_SHA512(hash, algo.salt1, 100000, 64);
|
||||
#endif
|
||||
Sha256.TransformBlock(algo.salt2, 0, algo.salt2.Length, null, 0);
|
||||
Sha256.TransformBlock(pbkdf2, 0, 64, null, 0);
|
||||
Sha256.TransformFinalBlock(algo.salt2, 0, algo.salt2.Length);
|
||||
var x = BigEndianInteger(Sha256.Hash);
|
||||
|
||||
sha256.TransformBlock(algo.p, 0, 256, null, 0);
|
||||
sha256.TransformFinalBlock(g_256, 0, 256);
|
||||
var k = new BigInteger(sha256.Hash, true, true);
|
||||
Sha256.TransformBlock(algo.p, 0, 256, null, 0);
|
||||
Sha256.TransformFinalBlock(g_256, 0, 256);
|
||||
var k = BigEndianInteger(Sha256.Hash);
|
||||
|
||||
var v = BigInteger.ModPow(g, x, p);
|
||||
var k_v = (k * v) % p;
|
||||
var a = new BigInteger(new Int256(RNG).raw, true, true);
|
||||
var a = BigEndianInteger(new Int256(RNG).raw);
|
||||
var g_a = BigInteger.ModPow(g, a, p);
|
||||
var g_a_256 = g_a.To256Bytes();
|
||||
|
||||
sha256.TransformBlock(g_a_256, 0, 256, null, 0);
|
||||
sha256.TransformFinalBlock(g_b_256, 0, 256);
|
||||
var u = new BigInteger(sha256.Hash, true, true);
|
||||
Sha256.TransformBlock(g_a_256, 0, 256, null, 0);
|
||||
Sha256.TransformFinalBlock(g_b_256, 0, 256);
|
||||
var u = BigEndianInteger(Sha256.Hash);
|
||||
|
||||
var t = (g_b - k_v) % p; //(positive modulo, if the result is negative increment by p)
|
||||
if (t.Sign < 0) t += p;
|
||||
var s_a = BigInteger.ModPow(t, a + u * x, p);
|
||||
var k_a = SHA256.HashData(s_a.To256Bytes());
|
||||
var k_a = Sha256.ComputeHash(s_a.To256Bytes());
|
||||
|
||||
hash = SHA256.HashData(algo.p);
|
||||
var h2 = SHA256.HashData(g_256);
|
||||
hash = Sha256.ComputeHash(algo.p);
|
||||
var h2 = Sha256.ComputeHash(g_256);
|
||||
for (int i = 0; i < 32; i++) hash[i] ^= h2[i];
|
||||
sha256.TransformBlock(hash, 0, 32, null, 0);
|
||||
sha256.TransformBlock(SHA256.HashData(algo.salt1), 0, 32, null, 0);
|
||||
sha256.TransformBlock(SHA256.HashData(algo.salt2), 0, 32, null, 0);
|
||||
sha256.TransformBlock(g_a_256, 0, 256, null, 0);
|
||||
sha256.TransformBlock(g_b_256, 0, 256, null, 0);
|
||||
sha256.TransformFinalBlock(k_a, 0, 32);
|
||||
var m1 = sha256.Hash;
|
||||
var hs1 = Sha256.ComputeHash(algo.salt1);
|
||||
var hs2 = Sha256.ComputeHash(algo.salt2);
|
||||
Sha256.TransformBlock(hash, 0, 32, null, 0);
|
||||
Sha256.TransformBlock(hs1, 0, 32, null, 0);
|
||||
Sha256.TransformBlock(hs2, 0, 32, null, 0);
|
||||
Sha256.TransformBlock(g_a_256, 0, 256, null, 0);
|
||||
Sha256.TransformBlock(g_b_256, 0, 256, null, 0);
|
||||
Sha256.TransformFinalBlock(k_a, 0, 32);
|
||||
var m1 = Sha256.Hash;
|
||||
|
||||
return new InputCheckPasswordSRP { A = g_a_256, M1 = m1, srp_id = accountPassword.srp_id };
|
||||
}
|
||||
|
||||
#if !NETCOREAPP2_0_OR_GREATER
|
||||
// adapted from https://github.com/dotnet/aspnetcore/blob/main/src/DataProtection/Cryptography.KeyDerivation/src/PBKDF2/ManagedPbkdf2Provider.cs
|
||||
public static byte[] PBKDF2_SHA512(byte[] password, byte[] salt, int iterationCount, int numBytesRequested)
|
||||
{
|
||||
// PBKDF2 is defined in NIST SP800-132, Sec. 5.3.
|
||||
// http://csrc.nist.gov/publications/nistpubs/800-132/nist-sp800-132.pdf
|
||||
|
||||
byte[] retVal = new byte[numBytesRequested];
|
||||
int numBytesWritten = 0;
|
||||
int numBytesRemaining = numBytesRequested;
|
||||
|
||||
// For each block index, U_0 := Salt || block_index
|
||||
byte[] saltWithBlockIndex = new byte[checked(salt.Length + sizeof(uint))];
|
||||
Buffer.BlockCopy(salt, 0, saltWithBlockIndex, 0, salt.Length);
|
||||
|
||||
using (var hashAlgorithm = new HMACSHA512(password))
|
||||
{
|
||||
for (uint blockIndex = 1; numBytesRemaining > 0; blockIndex++)
|
||||
{
|
||||
// write the block index out as big-endian
|
||||
saltWithBlockIndex[^4] = (byte)(blockIndex >> 24);
|
||||
saltWithBlockIndex[^3] = (byte)(blockIndex >> 16);
|
||||
saltWithBlockIndex[^2] = (byte)(blockIndex >> 8);
|
||||
saltWithBlockIndex[^1] = (byte)blockIndex;
|
||||
|
||||
// U_1 = PRF(U_0) = PRF(Salt || block_index)
|
||||
// T_blockIndex = U_1
|
||||
byte[] U_iter = hashAlgorithm.ComputeHash(saltWithBlockIndex); // this is U_1
|
||||
byte[] T_blockIndex = U_iter;
|
||||
|
||||
for (int iter = 1; iter < iterationCount; iter++)
|
||||
{
|
||||
U_iter = hashAlgorithm.ComputeHash(U_iter);
|
||||
for (int j = U_iter.Length - 1; j >= 0; j--)
|
||||
T_blockIndex[j] ^= U_iter[j];
|
||||
// At this point, the 'U_iter' variable actually contains U_{iter+1} (due to indexing differences).
|
||||
}
|
||||
|
||||
// At this point, we're done iterating on this block, so copy the transformed block into retVal.
|
||||
int numBytesToCopy = Math.Min(numBytesRemaining, T_blockIndex.Length);
|
||||
Buffer.BlockCopy(T_blockIndex, 0, retVal, numBytesWritten, numBytesToCopy);
|
||||
numBytesWritten += numBytesToCopy;
|
||||
numBytesRemaining -= numBytesToCopy;
|
||||
}
|
||||
}
|
||||
|
||||
// retVal := T_1 || T_2 || ... || T_n, where T_n may be truncated to meet the desired output length
|
||||
return retVal;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -413,7 +413,7 @@ namespace WTelegram
|
|||
parmType = parmType[(qm + 1)..];
|
||||
sw.WriteLine($"{tabIndent}\tif ({parmName} != null)");
|
||||
sw.Write('\t');
|
||||
if (MapOptionalType(parmType, parm.name).EndsWith('?'))
|
||||
if (MapOptionalType(parmType, parm.name).EndsWith("?"))
|
||||
parmName += ".Value";
|
||||
}
|
||||
switch (parmType)
|
||||
|
|
|
|||
|
|
@ -24,9 +24,15 @@ namespace WTelegram
|
|||
|
||||
public static long RandomLong()
|
||||
{
|
||||
#if NETCOREAPP2_1_OR_GREATER
|
||||
Span<long> span = stackalloc long[1];
|
||||
System.Security.Cryptography.RandomNumberGenerator.Fill(System.Runtime.InteropServices.MemoryMarshal.AsBytes(span));
|
||||
return span[0];
|
||||
#else
|
||||
var span = new byte[8];
|
||||
Encryption.RNG.GetBytes(span);
|
||||
return BitConverter.ToInt64(span, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static byte[] ToBigEndian(ulong value) // variable-size buffer
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ namespace WTelegram
|
|||
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]))
|
||||
if (!Encryption.Sha256.ComputeHash(utf8Json, 32, utf8Json.Length - 32).SequenceEqual(utf8Json[0..32]))
|
||||
throw new ApplicationException("Integrity check failed in session loading");
|
||||
return JsonSerializer.Deserialize<Session>(utf8Json.AsSpan(32), Helpers.JsonOptions);
|
||||
}
|
||||
|
|
@ -62,7 +62,7 @@ namespace WTelegram
|
|||
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(Encryption.Sha256.ComputeHash(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));
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<TargetFrameworks>netstandard2.0;net5.0</TargetFrameworks>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<RootNamespace>WTelegram</RootNamespace>
|
||||
<Deterministic>true</Deterministic>
|
||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||
|
|
@ -10,7 +11,7 @@
|
|||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||
<PackageId>WTelegramClient</PackageId>
|
||||
<Description>Telegram client library written 100% in C# and .NET Core</Description>
|
||||
<Description>Telegram client library written 100% in C# and .NET Standard</Description>
|
||||
<Authors>Wizou</Authors>
|
||||
<Copyright>Copyright © Olivier Marcoux 2021</Copyright>
|
||||
<PackageTags>Telegram;Client;Api;UserBot;MTProto</PackageTags>
|
||||
|
|
@ -21,8 +22,8 @@
|
|||
<!--<PackageReadmeFile>README.md</PackageReadmeFile> see https://github.com/NuGet/Home/issues/10791 -->
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<NoWarn>1701;1702;1573;1591;0419</NoWarn>
|
||||
<PropertyGroup>
|
||||
<NoWarn>IDE0079;1701;1702;1573;1591;0419</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
@ -35,5 +36,17 @@
|
|||
<PackageReference Include="Crc32.NET" Version="1.2.0" />
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
|
||||
<PackageReference Include="IndexRange" Version="1.0.0" />
|
||||
<PackageReference Include="Microsoft.Bcl.HashCode" Version="1.1.1" />
|
||||
<PackageReference Include="System.Formats.Asn1" Version="5.0.0" />
|
||||
<PackageReference Include="System.Memory" Version="4.5.4" />
|
||||
<PackageReference Include="System.Text.Json" Version="5.0.2" />
|
||||
<PackageReference Include="TA.System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray" Version="1.0.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
Loading…
Reference in a new issue