Add compatibility with .NET Standard 2.0 (multi-target)

This commit is contained in:
Wizou 2021-08-16 22:30:45 +02:00
commit 866c562d81
7 changed files with 233 additions and 65 deletions

View file

@ -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
View 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
}
}

View file

@ -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
}
}

View file

@ -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)

View file

@ -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

View file

@ -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));

View file

@ -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>