formatting code (ctrl + k + d)

This commit is contained in:
ahmadali shafiee 2016-04-18 15:20:57 +04:30
parent d330f9614b
commit 504b63a6d7
38 changed files with 16805 additions and 16280 deletions

View file

@ -3,35 +3,35 @@ using TLSharp.Core.Network;
namespace TLSharp.Core.Auth namespace TLSharp.Core.Auth
{ {
public static class Authenticator public static class Authenticator
{ {
public static async Task<Step3_Response> DoAuthentication(TcpTransport transport) public static async Task<Step3_Response> DoAuthentication(TcpTransport transport)
{ {
var sender = new MtProtoPlainSender(transport); var sender = new MtProtoPlainSender(transport);
var step1 = new Step1_PQRequest(); var step1 = new Step1_PQRequest();
await sender.Send(step1.ToBytes()); await sender.Send(step1.ToBytes());
var step1Response = step1.FromBytes(await sender.Recieve()); var step1Response = step1.FromBytes(await sender.Recieve());
var step2 = new Step2_DHExchange(); var step2 = new Step2_DHExchange();
await sender.Send(step2.ToBytes( await sender.Send(step2.ToBytes(
step1Response.Nonce, step1Response.Nonce,
step1Response.ServerNonce, step1Response.ServerNonce,
step1Response.Fingerprints, step1Response.Fingerprints,
step1Response.Pq)); step1Response.Pq));
var step2Response = step2.FromBytes(await sender.Recieve()); var step2Response = step2.FromBytes(await sender.Recieve());
var step3 = new Step3_CompleteDHExchange(); var step3 = new Step3_CompleteDHExchange();
await sender.Send(step3.ToBytes( await sender.Send(step3.ToBytes(
step2Response.Nonce, step2Response.Nonce,
step2Response.ServerNonce, step2Response.ServerNonce,
step2Response.NewNonce, step2Response.NewNonce,
step2Response.EncryptedAnswer)); step2Response.EncryptedAnswer));
var step3Response = step3.FromBytes(await sender.Recieve()); var step3Response = step3.FromBytes(await sender.Recieve());
return step3Response; return step3Response;
} }
} }
} }

View file

@ -7,90 +7,90 @@ using TLSharp.Core.MTProto.Crypto;
namespace TLSharp.Core.Auth namespace TLSharp.Core.Auth
{ {
public class Step1_Response public class Step1_Response
{ {
public byte[] Nonce { get; set; } public byte[] Nonce { get; set; }
public byte[] ServerNonce { get; set; } public byte[] ServerNonce { get; set; }
public BigInteger Pq { get; set; } public BigInteger Pq { get; set; }
public List<byte[]> Fingerprints { get; set; } public List<byte[]> Fingerprints { get; set; }
} }
public class Step1_PQRequest public class Step1_PQRequest
{ {
private byte[] nonce; private byte[] nonce;
public Step1_PQRequest() public Step1_PQRequest()
{ {
nonce = new byte[16]; nonce = new byte[16];
} }
public byte[] ToBytes() public byte[] ToBytes()
{ {
new Random().NextBytes(nonce); new Random().NextBytes(nonce);
const int constructorNumber = 0x60469778; const int constructorNumber = 0x60469778;
using (var memoryStream = new MemoryStream()) using (var memoryStream = new MemoryStream())
{ {
using (var binaryWriter = new BinaryWriter(memoryStream)) using (var binaryWriter = new BinaryWriter(memoryStream))
{ {
binaryWriter.Write(constructorNumber); binaryWriter.Write(constructorNumber);
binaryWriter.Write(nonce); binaryWriter.Write(nonce);
return memoryStream.ToArray(); return memoryStream.ToArray();
} }
} }
} }
public Step1_Response FromBytes(byte[] bytes) public Step1_Response FromBytes(byte[] bytes)
{ {
var fingerprints = new List<byte[]>(); var fingerprints = new List<byte[]>();
using (var memoryStream = new MemoryStream(bytes, false)) using (var memoryStream = new MemoryStream(bytes, false))
{ {
using (var binaryReader = new BinaryReader(memoryStream)) using (var binaryReader = new BinaryReader(memoryStream))
{ {
const int responseConstructorNumber = 0x05162463; const int responseConstructorNumber = 0x05162463;
var responseCode = binaryReader.ReadInt32(); var responseCode = binaryReader.ReadInt32();
if (responseCode != responseConstructorNumber) if (responseCode != responseConstructorNumber)
{ {
throw new InvalidOperationException($"invalid response code: {responseCode}"); throw new InvalidOperationException($"invalid response code: {responseCode}");
} }
var nonceFromServer = binaryReader.ReadBytes(16); var nonceFromServer = binaryReader.ReadBytes(16);
if (!nonceFromServer.SequenceEqual(nonce))
{
throw new InvalidOperationException("invalid nonce from server");
}
var serverNonce = binaryReader.ReadBytes(16);
byte[] pqbytes = Serializers.Bytes.read(binaryReader); if (!nonceFromServer.SequenceEqual(nonce))
var pq = new BigInteger(1, pqbytes); {
throw new InvalidOperationException("invalid nonce from server");
}
var vectorId = binaryReader.ReadInt32(); var serverNonce = binaryReader.ReadBytes(16);
const int vectorConstructorNumber = 0x1cb5c415;
if (vectorId != vectorConstructorNumber)
{
throw new InvalidOperationException($"Invalid vector constructor number {vectorId}");
}
var fingerprintCount = binaryReader.ReadInt32(); byte[] pqbytes = Serializers.Bytes.read(binaryReader);
for (var i = 0; i < fingerprintCount; i++) var pq = new BigInteger(1, pqbytes);
{
byte[] fingerprint = binaryReader.ReadBytes(8);
fingerprints.Add(fingerprint);
}
return new Step1_Response var vectorId = binaryReader.ReadInt32();
{ const int vectorConstructorNumber = 0x1cb5c415;
Fingerprints = fingerprints, if (vectorId != vectorConstructorNumber)
Nonce = nonce, {
Pq = pq, throw new InvalidOperationException($"Invalid vector constructor number {vectorId}");
ServerNonce = serverNonce }
};
} var fingerprintCount = binaryReader.ReadInt32();
} for (var i = 0; i < fingerprintCount; i++)
} {
} byte[] fingerprint = binaryReader.ReadBytes(8);
fingerprints.Add(fingerprint);
}
return new Step1_Response
{
Fingerprints = fingerprints,
Nonce = nonce,
Pq = pq,
ServerNonce = serverNonce
};
}
}
}
}
} }

View file

@ -6,107 +6,107 @@ using TLSharp.Core.MTProto.Crypto;
namespace TLSharp.Core.Auth namespace TLSharp.Core.Auth
{ {
public class Step2_Response public class Step2_Response
{ {
public byte[] Nonce { get; set; } public byte[] Nonce { get; set; }
public byte[] ServerNonce { get; set; } public byte[] ServerNonce { get; set; }
public byte[] NewNonce { get; set; } public byte[] NewNonce { get; set; }
public byte[] EncryptedAnswer { get; set; } public byte[] EncryptedAnswer { get; set; }
} }
public class Step2_DHExchange public class Step2_DHExchange
{ {
public byte[] newNonce; public byte[] newNonce;
public Step2_DHExchange() public Step2_DHExchange()
{ {
newNonce = new byte[32]; newNonce = new byte[32];
} }
public byte[] ToBytes(byte[] nonce, byte[] serverNonce, List<byte[]> fingerprints, BigInteger pq) public byte[] ToBytes(byte[] nonce, byte[] serverNonce, List<byte[]> fingerprints, BigInteger pq)
{ {
new Random().NextBytes(newNonce); new Random().NextBytes(newNonce);
var pqPair = Factorizator.Factorize(pq);
byte[] reqDhParamsBytes; var pqPair = Factorizator.Factorize(pq);
using (MemoryStream pqInnerData = new MemoryStream(255)) byte[] reqDhParamsBytes;
{
using (BinaryWriter pqInnerDataWriter = new BinaryWriter(pqInnerData))
{
pqInnerDataWriter.Write(0x83c95aec); // pq_inner_data
Serializers.Bytes.write(pqInnerDataWriter, pq.ToByteArrayUnsigned());
Serializers.Bytes.write(pqInnerDataWriter, pqPair.Min.ToByteArrayUnsigned());
Serializers.Bytes.write(pqInnerDataWriter, pqPair.Max.ToByteArrayUnsigned());
pqInnerDataWriter.Write(nonce);
pqInnerDataWriter.Write(serverNonce);
pqInnerDataWriter.Write(newNonce);
byte[] ciphertext = null; using (MemoryStream pqInnerData = new MemoryStream(255))
byte[] targetFingerprint = null; {
foreach (byte[] fingerprint in fingerprints) using (BinaryWriter pqInnerDataWriter = new BinaryWriter(pqInnerData))
{ {
ciphertext = RSA.Encrypt(BitConverter.ToString(fingerprint).Replace("-", string.Empty), pqInnerDataWriter.Write(0x83c95aec); // pq_inner_data
pqInnerData.GetBuffer(), 0, (int)pqInnerData.Position); Serializers.Bytes.write(pqInnerDataWriter, pq.ToByteArrayUnsigned());
if (ciphertext != null) Serializers.Bytes.write(pqInnerDataWriter, pqPair.Min.ToByteArrayUnsigned());
{ Serializers.Bytes.write(pqInnerDataWriter, pqPair.Max.ToByteArrayUnsigned());
targetFingerprint = fingerprint; pqInnerDataWriter.Write(nonce);
break; pqInnerDataWriter.Write(serverNonce);
} pqInnerDataWriter.Write(newNonce);
}
if (ciphertext == null) byte[] ciphertext = null;
{ byte[] targetFingerprint = null;
throw new InvalidOperationException( foreach (byte[] fingerprint in fingerprints)
String.Format("not found valid key for fingerprints: {0}", String.Join(", ", fingerprints))); {
} ciphertext = RSA.Encrypt(BitConverter.ToString(fingerprint).Replace("-", string.Empty),
pqInnerData.GetBuffer(), 0, (int)pqInnerData.Position);
if (ciphertext != null)
{
targetFingerprint = fingerprint;
break;
}
}
using (MemoryStream reqDHParams = new MemoryStream(1024)) if (ciphertext == null)
{ {
using (BinaryWriter reqDHParamsWriter = new BinaryWriter(reqDHParams)) throw new InvalidOperationException(
{ String.Format("not found valid key for fingerprints: {0}", String.Join(", ", fingerprints)));
reqDHParamsWriter.Write(0xd712e4be); // req_dh_params }
reqDHParamsWriter.Write(nonce);
reqDHParamsWriter.Write(serverNonce);
Serializers.Bytes.write(reqDHParamsWriter, pqPair.Min.ToByteArrayUnsigned());
Serializers.Bytes.write(reqDHParamsWriter, pqPair.Max.ToByteArrayUnsigned());
reqDHParamsWriter.Write(targetFingerprint);
Serializers.Bytes.write(reqDHParamsWriter, ciphertext);
reqDhParamsBytes = reqDHParams.ToArray(); using (MemoryStream reqDHParams = new MemoryStream(1024))
} {
} using (BinaryWriter reqDHParamsWriter = new BinaryWriter(reqDHParams))
} {
return reqDhParamsBytes; reqDHParamsWriter.Write(0xd712e4be); // req_dh_params
} reqDHParamsWriter.Write(nonce);
} reqDHParamsWriter.Write(serverNonce);
Serializers.Bytes.write(reqDHParamsWriter, pqPair.Min.ToByteArrayUnsigned());
Serializers.Bytes.write(reqDHParamsWriter, pqPair.Max.ToByteArrayUnsigned());
reqDHParamsWriter.Write(targetFingerprint);
Serializers.Bytes.write(reqDHParamsWriter, ciphertext);
public Step2_Response FromBytes(byte[] response) reqDhParamsBytes = reqDHParams.ToArray();
{ }
byte[] encryptedAnswer; }
}
return reqDhParamsBytes;
}
}
using (MemoryStream responseStream = new MemoryStream(response, false)) public Step2_Response FromBytes(byte[] response)
{ {
using (BinaryReader responseReader = new BinaryReader(responseStream)) byte[] encryptedAnswer;
{
uint responseCode = responseReader.ReadUInt32();
if (responseCode == 0x79cb045d) using (MemoryStream responseStream = new MemoryStream(response, false))
{ {
// server_DH_params_fail using (BinaryReader responseReader = new BinaryReader(responseStream))
throw new InvalidOperationException("server_DH_params_fail: TODO"); {
} uint responseCode = responseReader.ReadUInt32();
if (responseCode != 0xd0e8075c) if (responseCode == 0x79cb045d)
{ {
throw new InvalidOperationException($"invalid response code: {responseCode}"); // server_DH_params_fail
} throw new InvalidOperationException("server_DH_params_fail: TODO");
}
byte[] nonceFromServer = responseReader.ReadBytes(16); if (responseCode != 0xd0e8075c)
{
throw new InvalidOperationException($"invalid response code: {responseCode}");
}
// TODO:! byte[] nonceFromServer = responseReader.ReadBytes(16);
/*
// TODO:!
/*
if (!nonceFromServer.SequenceEqual(nonce)) if (!nonceFromServer.SequenceEqual(nonce))
{ {
logger.debug("invalid nonce from server"); logger.debug("invalid nonce from server");
@ -115,10 +115,10 @@ namespace TLSharp.Core.Auth
*/ */
byte[] serverNonceFromServer = responseReader.ReadBytes(16); byte[] serverNonceFromServer = responseReader.ReadBytes(16);
// TODO: ! // TODO: !
/* /*
if (!serverNonceFromServer.SequenceEqual(serverNonce)) if (!serverNonceFromServer.SequenceEqual(serverNonce))
{ {
logger.error("invalid server nonce from server"); logger.error("invalid server nonce from server");
@ -126,17 +126,17 @@ namespace TLSharp.Core.Auth
} }
*/ */
encryptedAnswer = Serializers.Bytes.read(responseReader); encryptedAnswer = Serializers.Bytes.read(responseReader);
return new Step2_Response() return new Step2_Response()
{ {
EncryptedAnswer = encryptedAnswer, EncryptedAnswer = encryptedAnswer,
ServerNonce = serverNonceFromServer, ServerNonce = serverNonceFromServer,
Nonce = nonceFromServer, Nonce = nonceFromServer,
NewNonce = newNonce NewNonce = newNonce
}; };
} }
} }
} }
} }
} }

View file

@ -7,146 +7,146 @@ using TLSharp.Core.MTProto.Crypto;
namespace TLSharp.Core.Auth namespace TLSharp.Core.Auth
{ {
public class Step3_Response public class Step3_Response
{ {
public AuthKey AuthKey { get; set; } public AuthKey AuthKey { get; set; }
public int TimeOffset { get; set; } public int TimeOffset { get; set; }
} }
public class Step3_CompleteDHExchange public class Step3_CompleteDHExchange
{ {
private BigInteger _gab; private BigInteger _gab;
private byte[] newNonce; private byte[] newNonce;
private int timeOffset; private int timeOffset;
public byte[] ToBytes(byte[] nonce, byte[] serverNonce, byte[] newNonce, byte[] encryptedAnswer) public byte[] ToBytes(byte[] nonce, byte[] serverNonce, byte[] newNonce, byte[] encryptedAnswer)
{ {
this.newNonce = newNonce; this.newNonce = newNonce;
AESKeyData key = AES.GenerateKeyDataFromNonces(serverNonce, newNonce); AESKeyData key = AES.GenerateKeyDataFromNonces(serverNonce, newNonce);
byte[] plaintextAnswer = AES.DecryptAES(key, encryptedAnswer); byte[] plaintextAnswer = AES.DecryptAES(key, encryptedAnswer);
// logger.debug("plaintext answer: {0}", BitConverter.ToString(plaintextAnswer)); // logger.debug("plaintext answer: {0}", BitConverter.ToString(plaintextAnswer));
int g; int g;
BigInteger dhPrime; BigInteger dhPrime;
BigInteger ga; BigInteger ga;
using (MemoryStream dhInnerData = new MemoryStream(plaintextAnswer)) using (MemoryStream dhInnerData = new MemoryStream(plaintextAnswer))
{ {
using (BinaryReader dhInnerDataReader = new BinaryReader(dhInnerData)) using (BinaryReader dhInnerDataReader = new BinaryReader(dhInnerData))
{ {
byte[] hashsum = dhInnerDataReader.ReadBytes(20); byte[] hashsum = dhInnerDataReader.ReadBytes(20);
uint code = dhInnerDataReader.ReadUInt32(); uint code = dhInnerDataReader.ReadUInt32();
if (code != 0xb5890dba) if (code != 0xb5890dba)
{ {
throw new InvalidOperationException($"invalid dh_inner_data code: {code}"); throw new InvalidOperationException($"invalid dh_inner_data code: {code}");
} }
// logger.debug("valid code"); // logger.debug("valid code");
byte[] nonceFromServer1 = dhInnerDataReader.ReadBytes(16); byte[] nonceFromServer1 = dhInnerDataReader.ReadBytes(16);
if (!nonceFromServer1.SequenceEqual(nonce)) if (!nonceFromServer1.SequenceEqual(nonce))
{ {
throw new InvalidOperationException("invalid nonce in encrypted answer"); throw new InvalidOperationException("invalid nonce in encrypted answer");
} }
// logger.debug("valid nonce"); // logger.debug("valid nonce");
byte[] serverNonceFromServer1 = dhInnerDataReader.ReadBytes(16); byte[] serverNonceFromServer1 = dhInnerDataReader.ReadBytes(16);
if (!serverNonceFromServer1.SequenceEqual(serverNonce)) if (!serverNonceFromServer1.SequenceEqual(serverNonce))
{ {
throw new InvalidOperationException("invalid server nonce in encrypted answer"); throw new InvalidOperationException("invalid server nonce in encrypted answer");
} }
// logger.debug("valid server nonce"); // logger.debug("valid server nonce");
g = dhInnerDataReader.ReadInt32(); g = dhInnerDataReader.ReadInt32();
dhPrime = new BigInteger(1, Serializers.Bytes.read(dhInnerDataReader)); dhPrime = new BigInteger(1, Serializers.Bytes.read(dhInnerDataReader));
ga = new BigInteger(1, Serializers.Bytes.read(dhInnerDataReader)); ga = new BigInteger(1, Serializers.Bytes.read(dhInnerDataReader));
int serverTime = dhInnerDataReader.ReadInt32(); int serverTime = dhInnerDataReader.ReadInt32();
timeOffset = serverTime - (int)(Convert.ToInt64((DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalMilliseconds) / 1000); timeOffset = serverTime - (int)(Convert.ToInt64((DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalMilliseconds) / 1000);
// logger.debug("g: {0}, dhprime: {1}, ga: {2}", g, dhPrime, ga); // logger.debug("g: {0}, dhprime: {1}, ga: {2}", g, dhPrime, ga);
} }
} }
BigInteger b = new BigInteger(2048, new Random()); BigInteger b = new BigInteger(2048, new Random());
BigInteger gb = BigInteger.ValueOf(g).ModPow(b, dhPrime); BigInteger gb = BigInteger.ValueOf(g).ModPow(b, dhPrime);
_gab = ga.ModPow(b, dhPrime); _gab = ga.ModPow(b, dhPrime);
// logger.debug("gab: {0}", gab); // logger.debug("gab: {0}", gab);
// prepare client dh inner data // prepare client dh inner data
byte[] clientDHInnerDataBytes; byte[] clientDHInnerDataBytes;
using (MemoryStream clientDhInnerData = new MemoryStream()) using (MemoryStream clientDhInnerData = new MemoryStream())
{ {
using (BinaryWriter clientDhInnerDataWriter = new BinaryWriter(clientDhInnerData)) using (BinaryWriter clientDhInnerDataWriter = new BinaryWriter(clientDhInnerData))
{ {
clientDhInnerDataWriter.Write(0x6643b654); // client_dh_inner_data clientDhInnerDataWriter.Write(0x6643b654); // client_dh_inner_data
clientDhInnerDataWriter.Write(nonce); clientDhInnerDataWriter.Write(nonce);
clientDhInnerDataWriter.Write(serverNonce); clientDhInnerDataWriter.Write(serverNonce);
clientDhInnerDataWriter.Write((long)0); // TODO: retry_id clientDhInnerDataWriter.Write((long)0); // TODO: retry_id
Serializers.Bytes.write(clientDhInnerDataWriter, gb.ToByteArrayUnsigned()); Serializers.Bytes.write(clientDhInnerDataWriter, gb.ToByteArrayUnsigned());
using (MemoryStream clientDhInnerDataWithHash = new MemoryStream()) using (MemoryStream clientDhInnerDataWithHash = new MemoryStream())
{ {
using (BinaryWriter clientDhInnerDataWithHashWriter = new BinaryWriter(clientDhInnerDataWithHash)) using (BinaryWriter clientDhInnerDataWithHashWriter = new BinaryWriter(clientDhInnerDataWithHash))
{ {
using (SHA1 sha1 = new SHA1Managed()) using (SHA1 sha1 = new SHA1Managed())
{ {
clientDhInnerDataWithHashWriter.Write(sha1.ComputeHash(clientDhInnerData.GetBuffer(), 0, (int)clientDhInnerData.Position)); clientDhInnerDataWithHashWriter.Write(sha1.ComputeHash(clientDhInnerData.GetBuffer(), 0, (int)clientDhInnerData.Position));
clientDhInnerDataWithHashWriter.Write(clientDhInnerData.GetBuffer(), 0, (int)clientDhInnerData.Position); clientDhInnerDataWithHashWriter.Write(clientDhInnerData.GetBuffer(), 0, (int)clientDhInnerData.Position);
clientDHInnerDataBytes = clientDhInnerDataWithHash.ToArray(); clientDHInnerDataBytes = clientDhInnerDataWithHash.ToArray();
} }
} }
} }
} }
} }
// logger.debug("client dh inner data papared len {0}: {1}", clientDHInnerDataBytes.Length, BitConverter.ToString(clientDHInnerDataBytes).Replace("-", "")); // logger.debug("client dh inner data papared len {0}: {1}", clientDHInnerDataBytes.Length, BitConverter.ToString(clientDHInnerDataBytes).Replace("-", ""));
// encryption // encryption
byte[] clientDhInnerDataEncryptedBytes = AES.EncryptAES(key, clientDHInnerDataBytes); byte[] clientDhInnerDataEncryptedBytes = AES.EncryptAES(key, clientDHInnerDataBytes);
// logger.debug("inner data encrypted {0}: {1}", clientDhInnerDataEncryptedBytes.Length, BitConverter.ToString(clientDhInnerDataEncryptedBytes).Replace("-", "")); // logger.debug("inner data encrypted {0}: {1}", clientDhInnerDataEncryptedBytes.Length, BitConverter.ToString(clientDhInnerDataEncryptedBytes).Replace("-", ""));
// prepare set_client_dh_params // prepare set_client_dh_params
byte[] setclientDhParamsBytes; byte[] setclientDhParamsBytes;
using (MemoryStream setClientDhParams = new MemoryStream()) using (MemoryStream setClientDhParams = new MemoryStream())
{ {
using (BinaryWriter setClientDhParamsWriter = new BinaryWriter(setClientDhParams)) using (BinaryWriter setClientDhParamsWriter = new BinaryWriter(setClientDhParams))
{ {
setClientDhParamsWriter.Write(0xf5045f1f); setClientDhParamsWriter.Write(0xf5045f1f);
setClientDhParamsWriter.Write(nonce); setClientDhParamsWriter.Write(nonce);
setClientDhParamsWriter.Write(serverNonce); setClientDhParamsWriter.Write(serverNonce);
Serializers.Bytes.write(setClientDhParamsWriter, clientDhInnerDataEncryptedBytes); Serializers.Bytes.write(setClientDhParamsWriter, clientDhInnerDataEncryptedBytes);
setclientDhParamsBytes = setClientDhParams.ToArray(); setclientDhParamsBytes = setClientDhParams.ToArray();
} }
} }
// logger.debug("set client dh params prepared: {0}", BitConverter.ToString(setclientDhParamsBytes)); // logger.debug("set client dh params prepared: {0}", BitConverter.ToString(setclientDhParamsBytes));
return setclientDhParamsBytes; return setclientDhParamsBytes;
} }
public Step3_Response FromBytes(byte[] response) public Step3_Response FromBytes(byte[] response)
{ {
using (MemoryStream responseStream = new MemoryStream(response)) using (MemoryStream responseStream = new MemoryStream(response))
{ {
using (BinaryReader responseReader = new BinaryReader(responseStream)) using (BinaryReader responseReader = new BinaryReader(responseStream))
{ {
uint code = responseReader.ReadUInt32(); uint code = responseReader.ReadUInt32();
if (code == 0x3bcbf734) if (code == 0x3bcbf734)
{ // dh_gen_ok { // dh_gen_ok
//logger.debug("dh_gen_ok"); //logger.debug("dh_gen_ok");
byte[] nonceFromServer = responseReader.ReadBytes(16); byte[] nonceFromServer = responseReader.ReadBytes(16);
// TODO // TODO
/* /*
if (!nonceFromServer.SequenceEqual(nonce)) if (!nonceFromServer.SequenceEqual(nonce))
{ {
logger.error("invalid nonce"); logger.error("invalid nonce");
@ -154,11 +154,11 @@ namespace TLSharp.Core.Auth
} }
*/ */
byte[] serverNonceFromServer = responseReader.ReadBytes(16); byte[] serverNonceFromServer = responseReader.ReadBytes(16);
// TODO: // TODO:
/* /*
if (!serverNonceFromServer.SequenceEqual(serverNonce)) if (!serverNonceFromServer.SequenceEqual(serverNonce))
{ {
logger.error("invalid server nonce"); logger.error("invalid server nonce");
@ -166,44 +166,44 @@ namespace TLSharp.Core.Auth
} }
*/ */
byte[] newNonceHash1 = responseReader.ReadBytes(16); byte[] newNonceHash1 = responseReader.ReadBytes(16);
//logger.debug("new nonce hash 1: {0}", BitConverter.ToString(newNonceHash1)); //logger.debug("new nonce hash 1: {0}", BitConverter.ToString(newNonceHash1));
AuthKey authKey = new AuthKey(_gab); AuthKey authKey = new AuthKey(_gab);
byte[] newNonceHashCalculated = authKey.CalcNewNonceHash(newNonce, 1); byte[] newNonceHashCalculated = authKey.CalcNewNonceHash(newNonce, 1);
if (!newNonceHash1.SequenceEqual(newNonceHashCalculated)) if (!newNonceHash1.SequenceEqual(newNonceHashCalculated))
{ {
throw new InvalidOperationException("invalid new nonce hash"); throw new InvalidOperationException("invalid new nonce hash");
} }
//logger.info("generated new auth key: {0}", gab); //logger.info("generated new auth key: {0}", gab);
//logger.info("saving time offset: {0}", timeOffset); //logger.info("saving time offset: {0}", timeOffset);
//TelegramSession.Instance.TimeOffset = timeOffset; //TelegramSession.Instance.TimeOffset = timeOffset;
return new Step3_Response() return new Step3_Response()
{ {
AuthKey = authKey, AuthKey = authKey,
TimeOffset = timeOffset TimeOffset = timeOffset
}; };
} }
else if (code == 0x46dc1fb9) else if (code == 0x46dc1fb9)
{ // dh_gen_retry { // dh_gen_retry
throw new NotImplementedException("dh_gen_retry"); throw new NotImplementedException("dh_gen_retry");
} }
else if (code == 0xa69dae02) else if (code == 0xa69dae02)
{ {
// dh_gen_fail // dh_gen_fail
throw new NotImplementedException("dh_gen_fail"); throw new NotImplementedException("dh_gen_fail");
} }
else else
{ {
throw new InvalidOperationException($"dh_gen unknown: {code}"); throw new InvalidOperationException($"dh_gen unknown: {code}");
} }
} }
} }
} }
} }
} }

View file

@ -4,58 +4,34 @@ using System.Security.Cryptography;
namespace TLSharp.Core.MTProto.Crypto namespace TLSharp.Core.MTProto.Crypto
{ {
public class AESKeyData { public class AESKeyData
{
private readonly byte[] key; private readonly byte[] key;
private readonly byte[] iv; private readonly byte[] iv;
public AESKeyData(byte[] key, byte[] iv) { public AESKeyData(byte[] key, byte[] iv)
{
this.key = key; this.key = key;
this.iv = iv; this.iv = iv;
} }
public byte[] Key { public byte[] Key
{
get { return key; } get { return key; }
} }
public byte[] Iv { public byte[] Iv
{
get { return iv; } get { return iv; }
} }
} }
public class AES { public class AES
public static byte[] DecryptWithNonces(byte[] data, byte[] serverNonce, byte[] newNonce) { {
using(SHA1 hash = new SHA1Managed()) { public static byte[] DecryptWithNonces(byte[] data, byte[] serverNonce, byte[] newNonce)
var nonces = new byte[48]; {
using (SHA1 hash = new SHA1Managed())
newNonce.CopyTo(nonces, 0); {
serverNonce.CopyTo(nonces, 32);
byte[] hash1 = hash.ComputeHash(nonces);
serverNonce.CopyTo(nonces, 0);
newNonce.CopyTo(nonces, 16);
byte[] hash2 = hash.ComputeHash(nonces);
nonces = new byte[64];
newNonce.CopyTo(nonces, 0);
newNonce.CopyTo(nonces, 32);
byte[] hash3 = hash.ComputeHash(nonces);
using(var keyBuffer = new MemoryStream(32))
using(var ivBuffer = new MemoryStream(32)) {
keyBuffer.Write(hash1, 0, hash1.Length);
keyBuffer.Write(hash2, 0, 12);
ivBuffer.Write(hash2, 12, 8);
ivBuffer.Write(hash3, 0, hash3.Length);
ivBuffer.Write(newNonce, 0, 4);
return DecryptIGE(data, keyBuffer.ToArray(), ivBuffer.ToArray());
}
}
}
public static AESKeyData GenerateKeyDataFromNonces(byte[] serverNonce, byte[] newNonce) {
using (SHA1 hash = new SHA1Managed()) {
var nonces = new byte[48]; var nonces = new byte[48];
newNonce.CopyTo(nonces, 0); newNonce.CopyTo(nonces, 0);
@ -72,7 +48,42 @@ namespace TLSharp.Core.MTProto.Crypto
byte[] hash3 = hash.ComputeHash(nonces); byte[] hash3 = hash.ComputeHash(nonces);
using (var keyBuffer = new MemoryStream(32)) using (var keyBuffer = new MemoryStream(32))
using (var ivBuffer = new MemoryStream(32)) { using (var ivBuffer = new MemoryStream(32))
{
keyBuffer.Write(hash1, 0, hash1.Length);
keyBuffer.Write(hash2, 0, 12);
ivBuffer.Write(hash2, 12, 8);
ivBuffer.Write(hash3, 0, hash3.Length);
ivBuffer.Write(newNonce, 0, 4);
return DecryptIGE(data, keyBuffer.ToArray(), ivBuffer.ToArray());
}
}
}
public static AESKeyData GenerateKeyDataFromNonces(byte[] serverNonce, byte[] newNonce)
{
using (SHA1 hash = new SHA1Managed())
{
var nonces = new byte[48];
newNonce.CopyTo(nonces, 0);
serverNonce.CopyTo(nonces, 32);
byte[] hash1 = hash.ComputeHash(nonces);
serverNonce.CopyTo(nonces, 0);
newNonce.CopyTo(nonces, 16);
byte[] hash2 = hash.ComputeHash(nonces);
nonces = new byte[64];
newNonce.CopyTo(nonces, 0);
newNonce.CopyTo(nonces, 32);
byte[] hash3 = hash.ComputeHash(nonces);
using (var keyBuffer = new MemoryStream(32))
using (var ivBuffer = new MemoryStream(32))
{
keyBuffer.Write(hash1, 0, hash1.Length); keyBuffer.Write(hash1, 0, hash1.Length);
keyBuffer.Write(hash2, 0, 12); keyBuffer.Write(hash2, 0, 12);
@ -85,17 +96,20 @@ namespace TLSharp.Core.MTProto.Crypto
} }
} }
public static byte[] DecryptAES(AESKeyData key, byte[] ciphertext) { public static byte[] DecryptAES(AESKeyData key, byte[] ciphertext)
{
return DecryptIGE(ciphertext, key.Key, key.Iv); return DecryptIGE(ciphertext, key.Key, key.Iv);
} }
public static byte[] EncryptAES(AESKeyData key, byte[] plaintext) { public static byte[] EncryptAES(AESKeyData key, byte[] plaintext)
{
return EncryptIGE(plaintext, key.Key, key.Iv); return EncryptIGE(plaintext, key.Key, key.Iv);
} }
public static byte[] DecryptIGE(byte[] ciphertext, byte[] key, byte[] iv) { public static byte[] DecryptIGE(byte[] ciphertext, byte[] key, byte[] iv)
var iv1 = new byte[iv.Length/2]; {
var iv2 = new byte[iv.Length/2]; var iv1 = new byte[iv.Length / 2];
var iv2 = new byte[iv.Length / 2];
Array.Copy(iv, 0, iv1, 0, iv1.Length); Array.Copy(iv, 0, iv1, 0, iv1.Length);
Array.Copy(iv, iv1.Length, iv2, 0, iv2.Length); Array.Copy(iv, iv1.Length, iv2, 0, iv2.Length);
@ -104,18 +118,21 @@ namespace TLSharp.Core.MTProto.Crypto
aes.Init(false, key); aes.Init(false, key);
byte[] plaintext = new byte[ciphertext.Length]; byte[] plaintext = new byte[ciphertext.Length];
int blocksCount = ciphertext.Length/16; int blocksCount = ciphertext.Length / 16;
byte[] ciphertextBlock = new byte[16]; byte[] ciphertextBlock = new byte[16];
byte[] plaintextBlock = new byte[16]; byte[] plaintextBlock = new byte[16];
for(int blockIndex = 0; blockIndex < blocksCount; blockIndex++) { for (int blockIndex = 0; blockIndex < blocksCount; blockIndex++)
for(int i = 0; i < 16; i++) { {
ciphertextBlock[i] = (byte) (ciphertext[blockIndex*16 + i] ^ iv2[i]); for (int i = 0; i < 16; i++)
{
ciphertextBlock[i] = (byte)(ciphertext[blockIndex * 16 + i] ^ iv2[i]);
} }
aes.ProcessBlock(ciphertextBlock, 0, plaintextBlock, 0); aes.ProcessBlock(ciphertextBlock, 0, plaintextBlock, 0);
for(int i = 0; i < 16; i++) { for (int i = 0; i < 16; i++)
{
plaintextBlock[i] ^= iv1[i]; plaintextBlock[i] ^= iv1[i];
} }
@ -128,22 +145,25 @@ namespace TLSharp.Core.MTProto.Crypto
return plaintext; return plaintext;
} }
public static byte[] EncryptIGE(byte[] originPlaintext, byte[] key, byte[] iv) { public static byte[] EncryptIGE(byte[] originPlaintext, byte[] key, byte[] iv)
{
byte[] plaintext; byte[] plaintext;
using (MemoryStream plaintextBuffer = new MemoryStream(originPlaintext.Length + 40)) { using (MemoryStream plaintextBuffer = new MemoryStream(originPlaintext.Length + 40))
//using(SHA1 hash = new SHA1Managed()) { {
//using(SHA1 hash = new SHA1Managed()) {
//byte[] hashsum = hash.ComputeHash(originPlaintext); //byte[] hashsum = hash.ComputeHash(originPlaintext);
//plaintextBuffer.Write(hashsum, 0, hashsum.Length); //plaintextBuffer.Write(hashsum, 0, hashsum.Length);
plaintextBuffer.Write(originPlaintext, 0, originPlaintext.Length); plaintextBuffer.Write(originPlaintext, 0, originPlaintext.Length);
while(plaintextBuffer.Position%16 != 0) { while (plaintextBuffer.Position % 16 != 0)
{
plaintextBuffer.WriteByte(0); // TODO: random padding plaintextBuffer.WriteByte(0); // TODO: random padding
} }
plaintext = plaintextBuffer.ToArray(); plaintext = plaintextBuffer.ToArray();
} }
var iv1 = new byte[iv.Length/2]; var iv1 = new byte[iv.Length / 2];
var iv2 = new byte[iv.Length/2]; var iv2 = new byte[iv.Length / 2];
Array.Copy(iv, 0, iv1, 0, iv1.Length); Array.Copy(iv, 0, iv1, 0, iv1.Length);
Array.Copy(iv, iv1.Length, iv2, 0, iv2.Length); Array.Copy(iv, iv1.Length, iv2, 0, iv2.Length);
@ -151,18 +171,20 @@ namespace TLSharp.Core.MTProto.Crypto
AesEngine aes = new AesEngine(); AesEngine aes = new AesEngine();
aes.Init(true, key); aes.Init(true, key);
int blocksCount = plaintext.Length/16; int blocksCount = plaintext.Length / 16;
byte[] ciphertext = new byte[plaintext.Length]; byte[] ciphertext = new byte[plaintext.Length];
byte[] ciphertextBlock = new byte[16]; byte[] ciphertextBlock = new byte[16];
byte[] plaintextBlock = new byte[16]; byte[] plaintextBlock = new byte[16];
for(int blockIndex = 0; blockIndex < blocksCount; blockIndex++) { for (int blockIndex = 0; blockIndex < blocksCount; blockIndex++)
Array.Copy(plaintext, 16*blockIndex, plaintextBlock, 0, 16); {
Array.Copy(plaintext, 16 * blockIndex, plaintextBlock, 0, 16);
//logger.info("plaintext block: {0} xor {1}", BitConverter.ToString(plaintextBlock).Replace("-", ""), BitConverter.ToString(iv1).Replace("-", "")); //logger.info("plaintext block: {0} xor {1}", BitConverter.ToString(plaintextBlock).Replace("-", ""), BitConverter.ToString(iv1).Replace("-", ""));
for(int i = 0; i < 16; i++) { for (int i = 0; i < 16; i++)
plaintextBlock[i] ^= iv1[i]; {
plaintextBlock[i] ^= iv1[i];
} }
//logger.info("xored plaintext: {0}", BitConverter.ToString(plaintextBlock).Replace("-", "")); //logger.info("xored plaintext: {0}", BitConverter.ToString(plaintextBlock).Replace("-", ""));
@ -171,14 +193,15 @@ namespace TLSharp.Core.MTProto.Crypto
//logger.info("encrypted plaintext: {0} xor {1}", BitConverter.ToString(ciphertextBlock).Replace("-", ""), BitConverter.ToString(iv2).Replace("-", "")); //logger.info("encrypted plaintext: {0} xor {1}", BitConverter.ToString(ciphertextBlock).Replace("-", ""), BitConverter.ToString(iv2).Replace("-", ""));
for(int i = 0; i < 16; i++) { for (int i = 0; i < 16; i++)
{
ciphertextBlock[i] ^= iv2[i]; ciphertextBlock[i] ^= iv2[i];
} }
//logger.info("xored ciphertext: {0}", BitConverter.ToString(ciphertextBlock).Replace("-", "")); //logger.info("xored ciphertext: {0}", BitConverter.ToString(ciphertextBlock).Replace("-", ""));
Array.Copy(ciphertextBlock, 0, iv1, 0, 16); Array.Copy(ciphertextBlock, 0, iv1, 0, 16);
Array.Copy(plaintext, 16*blockIndex, iv2, 0, 16); Array.Copy(plaintext, 16 * blockIndex, iv2, 0, 16);
Array.Copy(ciphertextBlock, 0, ciphertext, blockIndex * 16, 16); Array.Copy(ciphertextBlock, 0, ciphertext, blockIndex * 16, 16);
} }
@ -186,10 +209,11 @@ namespace TLSharp.Core.MTProto.Crypto
return ciphertext; return ciphertext;
} }
public static byte[] XOR(byte[] buffer1, byte[] buffer2) { public static byte[] XOR(byte[] buffer1, byte[] buffer2)
{
var result = new byte[buffer1.Length]; var result = new byte[buffer1.Length];
for(int i = 0; i < buffer1.Length; i++) for (int i = 0; i < buffer1.Length; i++)
result[i] = (byte) (buffer1[i] ^ buffer2[i]); result[i] = (byte)(buffer1[i] ^ buffer2[i]);
return result; return result;
} }
@ -199,7 +223,8 @@ namespace TLSharp.Core.MTProto.Crypto
// AES engine implementation // AES engine implementation
public class AesEngine { public class AesEngine
{
// The S box // The S box
private const uint m1 = 0x80808080; private const uint m1 = 0x80808080;
private const uint m2 = 0x7f7f7f7f; private const uint m2 = 0x7f7f7f7f;
@ -399,23 +424,27 @@ namespace TLSharp.Core.MTProto.Crypto
private uint[,] WorkingKey; private uint[,] WorkingKey;
private bool forEncryption; private bool forEncryption;
public string AlgorithmName { public string AlgorithmName
{
get { return "AES"; } get { return "AES"; }
} }
public bool IsPartialBlockOkay { public bool IsPartialBlockOkay
{
get { return false; } get { return false; }
} }
private uint Shift( private uint Shift(
uint r, uint r,
int shift) { int shift)
{
return (r >> shift) | (r << (32 - shift)); return (r >> shift) | (r << (32 - shift));
} }
private uint FFmulX( private uint FFmulX(
uint x) { uint x)
return ((x & m2) << 1) ^ (((x & m1) >> 7)*m3); {
return ((x & m2) << 1) ^ (((x & m1) >> 7) * m3);
} }
/* /*
@ -429,7 +458,8 @@ namespace TLSharp.Core.MTProto.Crypto
*/ */
private uint Inv_Mcol( private uint Inv_Mcol(
uint x) { uint x)
{
uint f2 = FFmulX(x); uint f2 = FFmulX(x);
uint f4 = FFmulX(f2); uint f4 = FFmulX(f2);
uint f8 = FFmulX(f4); uint f8 = FFmulX(f4);
@ -439,11 +469,12 @@ namespace TLSharp.Core.MTProto.Crypto
} }
private uint SubWord( private uint SubWord(
uint x) { uint x)
{
return S[x & 255] return S[x & 255]
| (((uint) S[(x >> 8) & 255]) << 8) | (((uint)S[(x >> 8) & 255]) << 8)
| (((uint) S[(x >> 16) & 255]) << 16) | (((uint)S[(x >> 16) & 255]) << 16)
| (((uint) S[(x >> 24) & 255]) << 24); | (((uint)S[(x >> 24) & 255]) << 24);
} }
/** /**
@ -455,11 +486,12 @@ namespace TLSharp.Core.MTProto.Crypto
private uint[,] GenerateWorkingKey( private uint[,] GenerateWorkingKey(
byte[] key, byte[] key,
bool forEncryption) { bool forEncryption)
int KC = key.Length/4; // key length in words {
int KC = key.Length / 4; // key length in words
int t; int t;
if((KC != 4) && (KC != 6) && (KC != 8)) if ((KC != 4) && (KC != 6) && (KC != 8))
throw new ArgumentException("Key length not 128/192/256 bits."); throw new ArgumentException("Key length not 128/192/256 bits.");
ROUNDS = KC + 6; // This is not always true for the generalized Rijndael that allows larger block sizes ROUNDS = KC + 6; // This is not always true for the generalized Rijndael that allows larger block sizes
@ -470,7 +502,8 @@ namespace TLSharp.Core.MTProto.Crypto
// //
t = 0; t = 0;
for(int i = 0; i < key.Length; t++) { for (int i = 0; i < key.Length; t++)
{
W[t >> 2, t & 3] = Pack.LE_To_UInt32(key, i); W[t >> 2, t & 3] = Pack.LE_To_UInt32(key, i);
i += 4; i += 4;
} }
@ -480,20 +513,27 @@ namespace TLSharp.Core.MTProto.Crypto
// calculate new values // calculate new values
// //
int k = (ROUNDS + 1) << 2; int k = (ROUNDS + 1) << 2;
for(int i = KC; (i < k); i++) { for (int i = KC; (i < k); i++)
{
uint temp = W[(i - 1) >> 2, (i - 1) & 3]; uint temp = W[(i - 1) >> 2, (i - 1) & 3];
if((i%KC) == 0) { if ((i % KC) == 0)
temp = SubWord(Shift(temp, 8)) ^ rcon[(i/KC) - 1]; {
} else if((KC > 6) && ((i%KC) == 4)) { temp = SubWord(Shift(temp, 8)) ^ rcon[(i / KC) - 1];
}
else if ((KC > 6) && ((i % KC) == 4))
{
temp = SubWord(temp); temp = SubWord(temp);
} }
W[i >> 2, i & 3] = W[(i - KC) >> 2, (i - KC) & 3] ^ temp; W[i >> 2, i & 3] = W[(i - KC) >> 2, (i - KC) & 3] ^ temp;
} }
if(!forEncryption) { if (!forEncryption)
for(int j = 1; j < ROUNDS; j++) { {
for(int i = 0; i < 4; i++) { for (int j = 1; j < ROUNDS; j++)
{
for (int i = 0; i < 4; i++)
{
W[j, i] = Inv_Mcol(W[j, i]); W[j, i] = Inv_Mcol(W[j, i]);
} }
} }
@ -502,33 +542,41 @@ namespace TLSharp.Core.MTProto.Crypto
return W; return W;
} }
public void Init(bool forEncryption, byte[] key) { public void Init(bool forEncryption, byte[] key)
{
WorkingKey = GenerateWorkingKey(key, forEncryption); WorkingKey = GenerateWorkingKey(key, forEncryption);
this.forEncryption = forEncryption; this.forEncryption = forEncryption;
} }
public int GetBlockSize() { public int GetBlockSize()
{
return BLOCK_SIZE; return BLOCK_SIZE;
} }
public int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff) { public int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff)
if(WorkingKey == null) { {
if (WorkingKey == null)
{
throw new InvalidOperationException("AES engine not initialised"); throw new InvalidOperationException("AES engine not initialised");
} }
if((inOff + (32/2)) > input.Length) { if ((inOff + (32 / 2)) > input.Length)
{
throw new InvalidOperationException("input buffer too short"); throw new InvalidOperationException("input buffer too short");
} }
if((outOff + (32/2)) > output.Length) { if ((outOff + (32 / 2)) > output.Length)
{
throw new InvalidOperationException("output buffer too short"); throw new InvalidOperationException("output buffer too short");
} }
UnPackBlock(input, inOff); UnPackBlock(input, inOff);
if(forEncryption) { if (forEncryption)
{
EncryptBlock(WorkingKey); EncryptBlock(WorkingKey);
} else { }
else {
DecryptBlock(WorkingKey); DecryptBlock(WorkingKey);
} }
@ -537,12 +585,14 @@ namespace TLSharp.Core.MTProto.Crypto
return BLOCK_SIZE; return BLOCK_SIZE;
} }
public void Reset() { public void Reset()
{
} }
private void UnPackBlock( private void UnPackBlock(
byte[] bytes, byte[] bytes,
int off) { int off)
{
C0 = Pack.LE_To_UInt32(bytes, off); C0 = Pack.LE_To_UInt32(bytes, off);
C1 = Pack.LE_To_UInt32(bytes, off + 4); C1 = Pack.LE_To_UInt32(bytes, off + 4);
C2 = Pack.LE_To_UInt32(bytes, off + 8); C2 = Pack.LE_To_UInt32(bytes, off + 8);
@ -551,7 +601,8 @@ namespace TLSharp.Core.MTProto.Crypto
private void PackBlock( private void PackBlock(
byte[] bytes, byte[] bytes,
int off) { int off)
{
Pack.UInt32_To_LE(C0, bytes, off); Pack.UInt32_To_LE(C0, bytes, off);
Pack.UInt32_To_LE(C1, bytes, off + 4); Pack.UInt32_To_LE(C1, bytes, off + 4);
Pack.UInt32_To_LE(C2, bytes, off + 8); Pack.UInt32_To_LE(C2, bytes, off + 8);
@ -559,7 +610,8 @@ namespace TLSharp.Core.MTProto.Crypto
} }
private void EncryptBlock( private void EncryptBlock(
uint[,] KW) { uint[,] KW)
{
uint r, r0, r1, r2, r3; uint r, r0, r1, r2, r3;
C0 ^= KW[0, 0]; C0 ^= KW[0, 0];
@ -567,7 +619,8 @@ namespace TLSharp.Core.MTProto.Crypto
C2 ^= KW[0, 2]; C2 ^= KW[0, 2];
C3 ^= KW[0, 3]; C3 ^= KW[0, 3];
for(r = 1; r < ROUNDS - 1;) { for (r = 1; r < ROUNDS - 1;)
{
r0 = T0[C0 & 255] ^ Shift(T0[(C1 >> 8) & 255], 24) ^ Shift(T0[(C2 >> 16) & 255], 16) ^ r0 = T0[C0 & 255] ^ Shift(T0[(C1 >> 8) & 255], 24) ^ Shift(T0[(C2 >> 16) & 255], 16) ^
Shift(T0[(C3 >> 24) & 255], 8) ^ KW[r, 0]; Shift(T0[(C3 >> 24) & 255], 8) ^ KW[r, 0];
r1 = T0[C1 & 255] ^ Shift(T0[(C2 >> 8) & 255], 24) ^ Shift(T0[(C3 >> 16) & 255], 16) ^ r1 = T0[C1 & 255] ^ Shift(T0[(C2 >> 8) & 255], 24) ^ Shift(T0[(C3 >> 16) & 255], 16) ^
@ -597,18 +650,19 @@ namespace TLSharp.Core.MTProto.Crypto
// the final round's table is a simple function of S so we don't use a whole other four tables for it // the final round's table is a simple function of S so we don't use a whole other four tables for it
C0 = S[r0 & 255] ^ (((uint) S[(r1 >> 8) & 255]) << 8) ^ (((uint) S[(r2 >> 16) & 255]) << 16) ^ C0 = S[r0 & 255] ^ (((uint)S[(r1 >> 8) & 255]) << 8) ^ (((uint)S[(r2 >> 16) & 255]) << 16) ^
(((uint) S[(r3 >> 24) & 255]) << 24) ^ KW[r, 0]; (((uint)S[(r3 >> 24) & 255]) << 24) ^ KW[r, 0];
C1 = S[r1 & 255] ^ (((uint) S[(r2 >> 8) & 255]) << 8) ^ (((uint) S[(r3 >> 16) & 255]) << 16) ^ C1 = S[r1 & 255] ^ (((uint)S[(r2 >> 8) & 255]) << 8) ^ (((uint)S[(r3 >> 16) & 255]) << 16) ^
(((uint) S[(r0 >> 24) & 255]) << 24) ^ KW[r, 1]; (((uint)S[(r0 >> 24) & 255]) << 24) ^ KW[r, 1];
C2 = S[r2 & 255] ^ (((uint) S[(r3 >> 8) & 255]) << 8) ^ (((uint) S[(r0 >> 16) & 255]) << 16) ^ C2 = S[r2 & 255] ^ (((uint)S[(r3 >> 8) & 255]) << 8) ^ (((uint)S[(r0 >> 16) & 255]) << 16) ^
(((uint) S[(r1 >> 24) & 255]) << 24) ^ KW[r, 2]; (((uint)S[(r1 >> 24) & 255]) << 24) ^ KW[r, 2];
C3 = S[r3 & 255] ^ (((uint) S[(r0 >> 8) & 255]) << 8) ^ (((uint) S[(r1 >> 16) & 255]) << 16) ^ C3 = S[r3 & 255] ^ (((uint)S[(r0 >> 8) & 255]) << 8) ^ (((uint)S[(r1 >> 16) & 255]) << 16) ^
(((uint) S[(r2 >> 24) & 255]) << 24) ^ KW[r, 3]; (((uint)S[(r2 >> 24) & 255]) << 24) ^ KW[r, 3];
} }
private void DecryptBlock( private void DecryptBlock(
uint[,] KW) { uint[,] KW)
{
int r; int r;
uint r0, r1, r2, r3; uint r0, r1, r2, r3;
@ -617,7 +671,8 @@ namespace TLSharp.Core.MTProto.Crypto
C2 ^= KW[ROUNDS, 2]; C2 ^= KW[ROUNDS, 2];
C3 ^= KW[ROUNDS, 3]; C3 ^= KW[ROUNDS, 3];
for(r = ROUNDS - 1; r > 1;) { for (r = ROUNDS - 1; r > 1;)
{
r0 = Tinv0[C0 & 255] ^ Shift(Tinv0[(C3 >> 8) & 255], 24) ^ Shift(Tinv0[(C2 >> 16) & 255], 16) ^ r0 = Tinv0[C0 & 255] ^ Shift(Tinv0[(C3 >> 8) & 255], 24) ^ Shift(Tinv0[(C2 >> 16) & 255], 16) ^
Shift(Tinv0[(C1 >> 24) & 255], 8) ^ KW[r, 0]; Shift(Tinv0[(C1 >> 24) & 255], 8) ^ KW[r, 0];
r1 = Tinv0[C1 & 255] ^ Shift(Tinv0[(C0 >> 8) & 255], 24) ^ Shift(Tinv0[(C3 >> 16) & 255], 16) ^ r1 = Tinv0[C1 & 255] ^ Shift(Tinv0[(C0 >> 8) & 255], 24) ^ Shift(Tinv0[(C3 >> 16) & 255], 16) ^
@ -647,124 +702,142 @@ namespace TLSharp.Core.MTProto.Crypto
// the final round's table is a simple function of Si so we don't use a whole other four tables for it // the final round's table is a simple function of Si so we don't use a whole other four tables for it
C0 = Si[r0 & 255] ^ (((uint) Si[(r3 >> 8) & 255]) << 8) ^ (((uint) Si[(r2 >> 16) & 255]) << 16) ^ C0 = Si[r0 & 255] ^ (((uint)Si[(r3 >> 8) & 255]) << 8) ^ (((uint)Si[(r2 >> 16) & 255]) << 16) ^
(((uint) Si[(r1 >> 24) & 255]) << 24) ^ KW[0, 0]; (((uint)Si[(r1 >> 24) & 255]) << 24) ^ KW[0, 0];
C1 = Si[r1 & 255] ^ (((uint) Si[(r0 >> 8) & 255]) << 8) ^ (((uint) Si[(r3 >> 16) & 255]) << 16) ^ C1 = Si[r1 & 255] ^ (((uint)Si[(r0 >> 8) & 255]) << 8) ^ (((uint)Si[(r3 >> 16) & 255]) << 16) ^
(((uint) Si[(r2 >> 24) & 255]) << 24) ^ KW[0, 1]; (((uint)Si[(r2 >> 24) & 255]) << 24) ^ KW[0, 1];
C2 = Si[r2 & 255] ^ (((uint) Si[(r1 >> 8) & 255]) << 8) ^ (((uint) Si[(r0 >> 16) & 255]) << 16) ^ C2 = Si[r2 & 255] ^ (((uint)Si[(r1 >> 8) & 255]) << 8) ^ (((uint)Si[(r0 >> 16) & 255]) << 16) ^
(((uint) Si[(r3 >> 24) & 255]) << 24) ^ KW[0, 2]; (((uint)Si[(r3 >> 24) & 255]) << 24) ^ KW[0, 2];
C3 = Si[r3 & 255] ^ (((uint) Si[(r2 >> 8) & 255]) << 8) ^ (((uint) Si[(r1 >> 16) & 255]) << 16) ^ C3 = Si[r3 & 255] ^ (((uint)Si[(r2 >> 8) & 255]) << 8) ^ (((uint)Si[(r1 >> 16) & 255]) << 16) ^
(((uint) Si[(r0 >> 24) & 255]) << 24) ^ KW[0, 3]; (((uint)Si[(r0 >> 24) & 255]) << 24) ^ KW[0, 3];
} }
} }
internal sealed class Pack { internal sealed class Pack
private Pack() { {
private Pack()
{
} }
internal static void UInt32_To_BE(uint n, byte[] bs) { internal static void UInt32_To_BE(uint n, byte[] bs)
bs[0] = (byte) (n >> 24); {
bs[1] = (byte) (n >> 16); bs[0] = (byte)(n >> 24);
bs[2] = (byte) (n >> 8); bs[1] = (byte)(n >> 16);
bs[3] = (byte) (n); bs[2] = (byte)(n >> 8);
bs[3] = (byte)(n);
} }
internal static void UInt32_To_BE(uint n, byte[] bs, int off) { internal static void UInt32_To_BE(uint n, byte[] bs, int off)
bs[off] = (byte) (n >> 24); {
bs[++off] = (byte) (n >> 16); bs[off] = (byte)(n >> 24);
bs[++off] = (byte) (n >> 8); bs[++off] = (byte)(n >> 16);
bs[++off] = (byte) (n); bs[++off] = (byte)(n >> 8);
bs[++off] = (byte)(n);
} }
internal static uint BE_To_UInt32(byte[] bs) { internal static uint BE_To_UInt32(byte[] bs)
uint n = (uint) bs[0] << 24; {
n |= (uint) bs[1] << 16; uint n = (uint)bs[0] << 24;
n |= (uint) bs[2] << 8; n |= (uint)bs[1] << 16;
n |= (uint)bs[2] << 8;
n |= bs[3]; n |= bs[3];
return n; return n;
} }
internal static uint BE_To_UInt32(byte[] bs, int off) { internal static uint BE_To_UInt32(byte[] bs, int off)
uint n = (uint) bs[off] << 24; {
n |= (uint) bs[++off] << 16; uint n = (uint)bs[off] << 24;
n |= (uint) bs[++off] << 8; n |= (uint)bs[++off] << 16;
n |= (uint)bs[++off] << 8;
n |= bs[++off]; n |= bs[++off];
return n; return n;
} }
internal static ulong BE_To_UInt64(byte[] bs) { internal static ulong BE_To_UInt64(byte[] bs)
{
uint hi = BE_To_UInt32(bs); uint hi = BE_To_UInt32(bs);
uint lo = BE_To_UInt32(bs, 4); uint lo = BE_To_UInt32(bs, 4);
return ((ulong) hi << 32) | lo; return ((ulong)hi << 32) | lo;
} }
internal static ulong BE_To_UInt64(byte[] bs, int off) { internal static ulong BE_To_UInt64(byte[] bs, int off)
{
uint hi = BE_To_UInt32(bs, off); uint hi = BE_To_UInt32(bs, off);
uint lo = BE_To_UInt32(bs, off + 4); uint lo = BE_To_UInt32(bs, off + 4);
return ((ulong) hi << 32) | lo; return ((ulong)hi << 32) | lo;
} }
internal static void UInt64_To_BE(ulong n, byte[] bs) { internal static void UInt64_To_BE(ulong n, byte[] bs)
UInt32_To_BE((uint) (n >> 32), bs); {
UInt32_To_BE((uint) (n), bs, 4); UInt32_To_BE((uint)(n >> 32), bs);
UInt32_To_BE((uint)(n), bs, 4);
} }
internal static void UInt64_To_BE(ulong n, byte[] bs, int off) { internal static void UInt64_To_BE(ulong n, byte[] bs, int off)
UInt32_To_BE((uint) (n >> 32), bs, off); {
UInt32_To_BE((uint) (n), bs, off + 4); UInt32_To_BE((uint)(n >> 32), bs, off);
UInt32_To_BE((uint)(n), bs, off + 4);
} }
internal static void UInt32_To_LE(uint n, byte[] bs) { internal static void UInt32_To_LE(uint n, byte[] bs)
bs[0] = (byte) (n); {
bs[1] = (byte) (n >> 8); bs[0] = (byte)(n);
bs[2] = (byte) (n >> 16); bs[1] = (byte)(n >> 8);
bs[3] = (byte) (n >> 24); bs[2] = (byte)(n >> 16);
bs[3] = (byte)(n >> 24);
} }
internal static void UInt32_To_LE(uint n, byte[] bs, int off) { internal static void UInt32_To_LE(uint n, byte[] bs, int off)
bs[off] = (byte) (n); {
bs[++off] = (byte) (n >> 8); bs[off] = (byte)(n);
bs[++off] = (byte) (n >> 16); bs[++off] = (byte)(n >> 8);
bs[++off] = (byte) (n >> 24); bs[++off] = (byte)(n >> 16);
bs[++off] = (byte)(n >> 24);
} }
internal static uint LE_To_UInt32(byte[] bs) { internal static uint LE_To_UInt32(byte[] bs)
{
uint n = bs[0]; uint n = bs[0];
n |= (uint) bs[1] << 8; n |= (uint)bs[1] << 8;
n |= (uint) bs[2] << 16; n |= (uint)bs[2] << 16;
n |= (uint) bs[3] << 24; n |= (uint)bs[3] << 24;
return n; return n;
} }
internal static uint LE_To_UInt32(byte[] bs, int off) { internal static uint LE_To_UInt32(byte[] bs, int off)
{
uint n = bs[off]; uint n = bs[off];
n |= (uint) bs[++off] << 8; n |= (uint)bs[++off] << 8;
n |= (uint) bs[++off] << 16; n |= (uint)bs[++off] << 16;
n |= (uint) bs[++off] << 24; n |= (uint)bs[++off] << 24;
return n; return n;
} }
internal static ulong LE_To_UInt64(byte[] bs) { internal static ulong LE_To_UInt64(byte[] bs)
{
uint lo = LE_To_UInt32(bs); uint lo = LE_To_UInt32(bs);
uint hi = LE_To_UInt32(bs, 4); uint hi = LE_To_UInt32(bs, 4);
return ((ulong) hi << 32) | lo; return ((ulong)hi << 32) | lo;
} }
internal static ulong LE_To_UInt64(byte[] bs, int off) { internal static ulong LE_To_UInt64(byte[] bs, int off)
{
uint lo = LE_To_UInt32(bs, off); uint lo = LE_To_UInt32(bs, off);
uint hi = LE_To_UInt32(bs, off + 4); uint hi = LE_To_UInt32(bs, off + 4);
return ((ulong) hi << 32) | lo; return ((ulong)hi << 32) | lo;
} }
internal static void UInt64_To_LE(ulong n, byte[] bs) { internal static void UInt64_To_LE(ulong n, byte[] bs)
UInt32_To_LE((uint) (n), bs); {
UInt32_To_LE((uint) (n >> 32), bs, 4); UInt32_To_LE((uint)(n), bs);
UInt32_To_LE((uint)(n >> 32), bs, 4);
} }
internal static void UInt64_To_LE(ulong n, byte[] bs, int off) { internal static void UInt64_To_LE(ulong n, byte[] bs, int off)
UInt32_To_LE((uint) (n), bs, off); {
UInt32_To_LE((uint) (n >> 32), bs, off + 4); UInt32_To_LE((uint)(n), bs, off);
UInt32_To_LE((uint)(n >> 32), bs, off + 4);
} }
} }
} }

View file

@ -2,16 +2,22 @@
using System.IO; using System.IO;
using System.Security.Cryptography; using System.Security.Cryptography;
namespace TLSharp.Core.MTProto.Crypto { namespace TLSharp.Core.MTProto.Crypto
public class AuthKey { {
public class AuthKey
{
private byte[] key; private byte[] key;
private ulong keyId; private ulong keyId;
private ulong auxHash; private ulong auxHash;
public AuthKey(BigInteger gab) { public AuthKey(BigInteger gab)
{
key = gab.ToByteArrayUnsigned(); key = gab.ToByteArrayUnsigned();
using(SHA1 hash = new SHA1Managed()) { using (SHA1 hash = new SHA1Managed())
using(MemoryStream hashStream = new MemoryStream(hash.ComputeHash(key), false)) { {
using(BinaryReader hashReader = new BinaryReader(hashStream)) { using (MemoryStream hashStream = new MemoryStream(hash.ComputeHash(key), false))
{
using (BinaryReader hashReader = new BinaryReader(hashStream))
{
auxHash = hashReader.ReadUInt64(); auxHash = hashReader.ReadUInt64();
hashReader.ReadBytes(4); hashReader.ReadBytes(4);
keyId = hashReader.ReadUInt64(); keyId = hashReader.ReadUInt64();
@ -20,11 +26,15 @@ namespace TLSharp.Core.MTProto.Crypto {
} }
} }
public AuthKey(byte[] data) { public AuthKey(byte[] data)
{
key = data; key = data;
using (SHA1 hash = new SHA1Managed()) { using (SHA1 hash = new SHA1Managed())
using (MemoryStream hashStream = new MemoryStream(hash.ComputeHash(key), false)) { {
using (BinaryReader hashReader = new BinaryReader(hashStream)) { using (MemoryStream hashStream = new MemoryStream(hash.ComputeHash(key), false))
{
using (BinaryReader hashReader = new BinaryReader(hashStream))
{
auxHash = hashReader.ReadUInt64(); auxHash = hashReader.ReadUInt64();
hashReader.ReadBytes(4); hashReader.ReadBytes(4);
keyId = hashReader.ReadUInt64(); keyId = hashReader.ReadUInt64();
@ -33,13 +43,17 @@ namespace TLSharp.Core.MTProto.Crypto {
} }
} }
public byte[] CalcNewNonceHash(byte[] newNonce, int number) { public byte[] CalcNewNonceHash(byte[] newNonce, int number)
using(MemoryStream buffer = new MemoryStream(100)) { {
using(BinaryWriter bufferWriter = new BinaryWriter(buffer)) { using (MemoryStream buffer = new MemoryStream(100))
{
using (BinaryWriter bufferWriter = new BinaryWriter(buffer))
{
bufferWriter.Write(newNonce); bufferWriter.Write(newNonce);
bufferWriter.Write((byte)number); bufferWriter.Write((byte)number);
bufferWriter.Write(auxHash); bufferWriter.Write(auxHash);
using(SHA1 sha1 = new SHA1Managed()) { using (SHA1 sha1 = new SHA1Managed())
{
byte[] hash = sha1.ComputeHash(buffer.GetBuffer(), 0, (int)buffer.Position); byte[] hash = sha1.ComputeHash(buffer.GetBuffer(), 0, (int)buffer.Position);
byte[] newNonceHash = new byte[16]; byte[] newNonceHash = new byte[16];
Array.Copy(hash, 4, newNonceHash, 0, 16); Array.Copy(hash, 4, newNonceHash, 0, 16);
@ -49,19 +63,24 @@ namespace TLSharp.Core.MTProto.Crypto {
} }
} }
public byte[] Data { public byte[] Data
get { {
get
{
return key; return key;
} }
} }
public ulong Id { public ulong Id
get { {
get
{
return keyId; return keyId;
} }
} }
public override string ToString() { public override string ToString()
{
return string.Format("(Key: {0}, KeyId: {1}, AuxHash: {2})", key, keyId, auxHash); return string.Format("(Key: {0}, KeyId: {1}, AuxHash: {2})", key, keyId, auxHash);
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -6,8 +6,10 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Ionic.Crc; using Ionic.Crc;
namespace TLSharp.Core.MTProto.Crypto { namespace TLSharp.Core.MTProto.Crypto
public class Crc32 : HashAlgorithm { {
public class Crc32 : HashAlgorithm
{
public const UInt32 DefaultPolynomial = 0xedb88320u; public const UInt32 DefaultPolynomial = 0xedb88320u;
public const UInt32 DefaultSeed = 0xffffffffu; public const UInt32 DefaultSeed = 0xffffffffu;
@ -16,23 +18,27 @@ namespace TLSharp.Core.MTProto.Crypto {
private UInt32[] table; private UInt32[] table;
private static UInt32[] defaultTable; private static UInt32[] defaultTable;
public Crc32() { public Crc32()
{
table = InitializeTable(DefaultPolynomial); table = InitializeTable(DefaultPolynomial);
seed = DefaultSeed; seed = DefaultSeed;
hash = seed; hash = seed;
} }
public Crc32(UInt32 polynomial, UInt32 seed) { public Crc32(UInt32 polynomial, UInt32 seed)
{
table = InitializeTable(polynomial); table = InitializeTable(polynomial);
this.seed = seed; this.seed = seed;
hash = seed; hash = seed;
} }
public override void Initialize() { public override void Initialize()
{
hash = seed; hash = seed;
} }
protected override void HashCore(byte[] buffer, int start, int length) { protected override void HashCore(byte[] buffer, int start, int length)
{
hash = CalculateHash(table, hash, buffer, start, length); hash = CalculateHash(table, hash, buffer, start, length);
} }
@ -40,34 +46,41 @@ namespace TLSharp.Core.MTProto.Crypto {
/// Возвращает хеш в BigEndian /// Возвращает хеш в BigEndian
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
protected override byte[] HashFinal() { protected override byte[] HashFinal()
{
byte[] hashBuffer = UInt32ToBigEndianBytes(~hash); byte[] hashBuffer = UInt32ToBigEndianBytes(~hash);
this.HashValue = hashBuffer; this.HashValue = hashBuffer;
return hashBuffer; return hashBuffer;
} }
public override int HashSize { public override int HashSize
{
get { return 32; } get { return 32; }
} }
public static UInt32 Compute(byte[] buffer) { public static UInt32 Compute(byte[] buffer)
{
return ~CalculateHash(InitializeTable(DefaultPolynomial), DefaultSeed, buffer, 0, buffer.Length); return ~CalculateHash(InitializeTable(DefaultPolynomial), DefaultSeed, buffer, 0, buffer.Length);
} }
public static UInt32 Compute(UInt32 seed, byte[] buffer) { public static UInt32 Compute(UInt32 seed, byte[] buffer)
{
return ~CalculateHash(InitializeTable(DefaultPolynomial), seed, buffer, 0, buffer.Length); return ~CalculateHash(InitializeTable(DefaultPolynomial), seed, buffer, 0, buffer.Length);
} }
public static UInt32 Compute(UInt32 polynomial, UInt32 seed, byte[] buffer) { public static UInt32 Compute(UInt32 polynomial, UInt32 seed, byte[] buffer)
{
return ~CalculateHash(InitializeTable(polynomial), seed, buffer, 0, buffer.Length); return ~CalculateHash(InitializeTable(polynomial), seed, buffer, 0, buffer.Length);
} }
private static UInt32[] InitializeTable(UInt32 polynomial) { private static UInt32[] InitializeTable(UInt32 polynomial)
{
if (polynomial == DefaultPolynomial && defaultTable != null) if (polynomial == DefaultPolynomial && defaultTable != null)
return defaultTable; return defaultTable;
UInt32[] createTable = new UInt32[256]; UInt32[] createTable = new UInt32[256];
for (int i = 0; i < 256; i++) { for (int i = 0; i < 256; i++)
{
UInt32 entry = (UInt32)i; UInt32 entry = (UInt32)i;
for (int j = 0; j < 8; j++) for (int j = 0; j < 8; j++)
if ((entry & 1) == 1) if ((entry & 1) == 1)
@ -83,22 +96,25 @@ namespace TLSharp.Core.MTProto.Crypto {
return createTable; return createTable;
} }
private static UInt32 CalculateHash(UInt32[] table, UInt32 seed, byte[] buffer, int start, int size) { private static UInt32 CalculateHash(UInt32[] table, UInt32 seed, byte[] buffer, int start, int size)
{
UInt32 crc = seed; UInt32 crc = seed;
for (int i = start; i < size; i++) for (int i = start; i < size; i++)
unchecked { unchecked
{
crc = (crc >> 8) ^ table[buffer[i] ^ crc & 0xff]; crc = (crc >> 8) ^ table[buffer[i] ^ crc & 0xff];
} }
return crc; return crc;
} }
private byte[] UInt32ToBigEndianBytes(UInt32 x) { private byte[] UInt32ToBigEndianBytes(UInt32 x)
{
return new byte[] { return new byte[] {
(byte)((x >> 24) & 0xff), (byte)((x >> 24) & 0xff),
(byte)((x >> 16) & 0xff), (byte)((x >> 16) & 0xff),
(byte)((x >> 8) & 0xff), (byte)((x >> 8) & 0xff),
(byte)(x & 0xff) (byte)(x & 0xff)
}; };
} }
} }
} }

View file

@ -1,55 +1,72 @@
using System; using System;
namespace TLSharp.Core.MTProto.Crypto { namespace TLSharp.Core.MTProto.Crypto
public class FactorizedPair { {
public class FactorizedPair
{
private readonly BigInteger p; private readonly BigInteger p;
private readonly BigInteger q; private readonly BigInteger q;
public FactorizedPair(BigInteger p, BigInteger q) { public FactorizedPair(BigInteger p, BigInteger q)
{
this.p = p; this.p = p;
this.q = q; this.q = q;
} }
public FactorizedPair(long p, long q) { public FactorizedPair(long p, long q)
{
this.p = BigInteger.ValueOf(p); this.p = BigInteger.ValueOf(p);
this.q = BigInteger.ValueOf(q); this.q = BigInteger.ValueOf(q);
} }
public BigInteger Min { public BigInteger Min
get { {
get
{
return p.Min(q); return p.Min(q);
} }
} }
public BigInteger Max { public BigInteger Max
get { {
get
{
return p.Max(q); return p.Max(q);
} }
} }
public override string ToString() { public override string ToString()
{
return string.Format("P: {0}, Q: {1}", p, q); return string.Format("P: {0}, Q: {1}", p, q);
} }
} }
public class Factorizator { public class Factorizator
{
public static Random random = new Random(); public static Random random = new Random();
public static long findSmallMultiplierLopatin(long what) { public static long findSmallMultiplierLopatin(long what)
{
long g = 0; long g = 0;
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++)
{
int q = (random.Next(128) & 15) + 17; int q = (random.Next(128) & 15) + 17;
long x = random.Next(1000000000) + 1, y = x; long x = random.Next(1000000000) + 1, y = x;
int lim = 1 << (i + 18); int lim = 1 << (i + 18);
for (int j = 1; j < lim; j++) { for (int j = 1; j < lim; j++)
{
long a = x, b = x, c = q; long a = x, b = x, c = q;
while (b != 0) { while (b != 0)
if ((b & 1) != 0) { {
if ((b & 1) != 0)
{
c += a; c += a;
if (c >= what) { if (c >= what)
{
c -= what; c -= what;
} }
} }
a += a; a += a;
if (a >= what) { if (a >= what)
{
a -= what; a -= what;
} }
b >>= 1; b >>= 1;
@ -57,14 +74,17 @@ namespace TLSharp.Core.MTProto.Crypto {
x = c; x = c;
long z = x < y ? y - x : x - y; long z = x < y ? y - x : x - y;
g = GCD(z, what); g = GCD(z, what);
if (g != 1) { if (g != 1)
{
break; break;
} }
if ((j & (j - 1)) == 0) { if ((j & (j - 1)) == 0)
{
y = x; y = x;
} }
} }
if (g > 1) { if (g > 1)
{
break; break;
} }
} }
@ -73,15 +93,20 @@ namespace TLSharp.Core.MTProto.Crypto {
return Math.Min(p, g); return Math.Min(p, g);
} }
public static long GCD(long a, long b) { public static long GCD(long a, long b)
while (a != 0 && b != 0) { {
while ((b & 1) == 0) { while (a != 0 && b != 0)
{
while ((b & 1) == 0)
{
b >>= 1; b >>= 1;
} }
while ((a & 1) == 0) { while ((a & 1) == 0)
{
a >>= 1; a >>= 1;
} }
if (a > b) { if (a > b)
{
a -= b; a -= b;
} }
else { else {
@ -91,16 +116,19 @@ namespace TLSharp.Core.MTProto.Crypto {
return b == 0 ? a : b; return b == 0 ? a : b;
} }
public static FactorizedPair Factorize(BigInteger pq) { public static FactorizedPair Factorize(BigInteger pq)
if(pq.BitLength < 64) { {
if (pq.BitLength < 64)
{
long pqlong = pq.LongValue; long pqlong = pq.LongValue;
long divisor = findSmallMultiplierLopatin(pqlong); long divisor = findSmallMultiplierLopatin(pqlong);
return new FactorizedPair(BigInteger.ValueOf(divisor), BigInteger.ValueOf(pqlong/divisor)); return new FactorizedPair(BigInteger.ValueOf(divisor), BigInteger.ValueOf(pqlong / divisor));
} else { }
else {
// TODO: port pollard factorization // TODO: port pollard factorization
throw new InvalidOperationException("pq too long; TODO: port the pollard algo"); throw new InvalidOperationException("pq too long; TODO: port the pollard algo");
// logger.error("pq too long; TODO: port the pollard algo"); // logger.error("pq too long; TODO: port the pollard algo");
// return null; // return null;
} }
} }

View file

@ -1,8 +1,10 @@
using System; using System;
using System.Text; using System.Text;
namespace TLSharp.Core.MTProto.Crypto { namespace TLSharp.Core.MTProto.Crypto
public interface IDigest { {
public interface IDigest
{
/** /**
* return the algorithm name * return the algorithm name
* *
@ -55,13 +57,16 @@ namespace TLSharp.Core.MTProto.Crypto {
void Reset(); void Reset();
} }
public class MD5 { public class MD5
{
public static string GetMd5String(string data) { public static string GetMd5String(string data)
{
return BitConverter.ToString(GetMd5Bytes(Encoding.UTF8.GetBytes(data))).Replace("-", "").ToLower(); return BitConverter.ToString(GetMd5Bytes(Encoding.UTF8.GetBytes(data))).Replace("-", "").ToLower();
} }
public static byte[] GetMd5Bytes(byte[] data) { public static byte[] GetMd5Bytes(byte[] data)
{
MD5Digest digest = new MD5Digest(); MD5Digest digest = new MD5Digest();
digest.BlockUpdate(data, 0, data.Length); digest.BlockUpdate(data, 0, data.Length);
byte[] hash = new byte[16]; byte[] hash = new byte[16];
@ -72,15 +77,18 @@ namespace TLSharp.Core.MTProto.Crypto {
private MD5Digest digest = new MD5Digest(); private MD5Digest digest = new MD5Digest();
public void Update(byte[] chunk) { public void Update(byte[] chunk)
{
digest.BlockUpdate(chunk, 0, chunk.Length); digest.BlockUpdate(chunk, 0, chunk.Length);
} }
public void Update(byte[] chunk, int offset, int limit) { public void Update(byte[] chunk, int offset, int limit)
{
digest.BlockUpdate(chunk, offset, limit); digest.BlockUpdate(chunk, offset, limit);
} }
public string FinalString() { public string FinalString()
{
byte[] hash = new byte[16]; byte[] hash = new byte[16];
digest.DoFinal(hash, 0); digest.DoFinal(hash, 0);
return BitConverter.ToString(hash).Replace("-", "").ToLower(); return BitConverter.ToString(hash).Replace("-", "").ToLower();
@ -88,7 +96,8 @@ namespace TLSharp.Core.MTProto.Crypto {
} }
public abstract class GeneralDigest public abstract class GeneralDigest
: IDigest { : IDigest
{
private const int BYTE_LENGTH = 64; private const int BYTE_LENGTH = 64;
private readonly byte[] xBuf; private readonly byte[] xBuf;
@ -96,11 +105,13 @@ namespace TLSharp.Core.MTProto.Crypto {
private long byteCount; private long byteCount;
private int xBufOff; private int xBufOff;
internal GeneralDigest() { internal GeneralDigest()
{
xBuf = new byte[4]; xBuf = new byte[4];
} }
internal GeneralDigest(GeneralDigest t) { internal GeneralDigest(GeneralDigest t)
{
xBuf = new byte[t.xBuf.Length]; xBuf = new byte[t.xBuf.Length];
Array.Copy(t.xBuf, 0, xBuf, 0, t.xBuf.Length); Array.Copy(t.xBuf, 0, xBuf, 0, t.xBuf.Length);
@ -108,10 +119,12 @@ namespace TLSharp.Core.MTProto.Crypto {
byteCount = t.byteCount; byteCount = t.byteCount;
} }
public void Update(byte input) { public void Update(byte input)
{
xBuf[xBufOff++] = input; xBuf[xBufOff++] = input;
if (xBufOff == xBuf.Length) { if (xBufOff == xBuf.Length)
{
ProcessWord(xBuf, 0); ProcessWord(xBuf, 0);
xBufOff = 0; xBufOff = 0;
} }
@ -122,11 +135,13 @@ namespace TLSharp.Core.MTProto.Crypto {
public void BlockUpdate( public void BlockUpdate(
byte[] input, byte[] input,
int inOff, int inOff,
int length) { int length)
{
// //
// fill the current word // fill the current word
// //
while ((xBufOff != 0) && (length > 0)) { while ((xBufOff != 0) && (length > 0))
{
Update(input[inOff]); Update(input[inOff]);
inOff++; inOff++;
length--; length--;
@ -135,7 +150,8 @@ namespace TLSharp.Core.MTProto.Crypto {
// //
// process whole words. // process whole words.
// //
while (length > xBuf.Length) { while (length > xBuf.Length)
{
ProcessWord(input, inOff); ProcessWord(input, inOff);
inOff += xBuf.Length; inOff += xBuf.Length;
@ -146,7 +162,8 @@ namespace TLSharp.Core.MTProto.Crypto {
// //
// load in the remainder. // load in the remainder.
// //
while (length > 0) { while (length > 0)
{
Update(input[inOff]); Update(input[inOff]);
inOff++; inOff++;
@ -154,13 +171,15 @@ namespace TLSharp.Core.MTProto.Crypto {
} }
} }
public virtual void Reset() { public virtual void Reset()
{
byteCount = 0; byteCount = 0;
xBufOff = 0; xBufOff = 0;
Array.Clear(xBuf, 0, xBuf.Length); Array.Clear(xBuf, 0, xBuf.Length);
} }
public int GetByteLength() { public int GetByteLength()
{
return BYTE_LENGTH; return BYTE_LENGTH;
} }
@ -168,7 +187,8 @@ namespace TLSharp.Core.MTProto.Crypto {
public abstract int GetDigestSize(); public abstract int GetDigestSize();
public abstract int DoFinal(byte[] output, int outOff); public abstract int DoFinal(byte[] output, int outOff);
public void Finish() { public void Finish()
{
long bitLength = (byteCount << 3); long bitLength = (byteCount << 3);
// //
@ -187,7 +207,8 @@ namespace TLSharp.Core.MTProto.Crypto {
} }
public class MD5Digest public class MD5Digest
: GeneralDigest { : GeneralDigest
{
private const int DigestLength = 16; private const int DigestLength = 16;
// //
@ -225,7 +246,8 @@ namespace TLSharp.Core.MTProto.Crypto {
private int H1, H2, H3, H4; // IV's private int H1, H2, H3, H4; // IV's
private int xOff; private int xOff;
public MD5Digest() { public MD5Digest()
{
Reset(); Reset();
} }
@ -235,7 +257,8 @@ namespace TLSharp.Core.MTProto.Crypto {
*/ */
public MD5Digest(MD5Digest t) public MD5Digest(MD5Digest t)
: base(t) { : base(t)
{
H1 = t.H1; H1 = t.H1;
H2 = t.H2; H2 = t.H2;
H3 = t.H3; H3 = t.H3;
@ -245,48 +268,56 @@ namespace TLSharp.Core.MTProto.Crypto {
xOff = t.xOff; xOff = t.xOff;
} }
public override string AlgorithmName { public override string AlgorithmName
{
get { return "MD5"; } get { return "MD5"; }
} }
public override int GetDigestSize() { public override int GetDigestSize()
{
return DigestLength; return DigestLength;
} }
internal override void ProcessWord( internal override void ProcessWord(
byte[] input, byte[] input,
int inOff) { int inOff)
{
X[xOff++] = (input[inOff] & 0xff) | ((input[inOff + 1] & 0xff) << 8) X[xOff++] = (input[inOff] & 0xff) | ((input[inOff + 1] & 0xff) << 8)
| ((input[inOff + 2] & 0xff) << 16) | ((input[inOff + 3] & 0xff) << 24); | ((input[inOff + 2] & 0xff) << 16) | ((input[inOff + 3] & 0xff) << 24);
if (xOff == 16) { if (xOff == 16)
{
ProcessBlock(); ProcessBlock();
} }
} }
internal override void ProcessLength( internal override void ProcessLength(
long bitLength) { long bitLength)
if (xOff > 14) { {
if (xOff > 14)
{
ProcessBlock(); ProcessBlock();
} }
X[14] = (int) (bitLength & 0xffffffff); X[14] = (int)(bitLength & 0xffffffff);
X[15] = (int) ((ulong) bitLength >> 32); X[15] = (int)((ulong)bitLength >> 32);
} }
private void UnpackWord( private void UnpackWord(
int word, int word,
byte[] outBytes, byte[] outBytes,
int outOff) { int outOff)
outBytes[outOff] = (byte) word; {
outBytes[outOff + 1] = (byte) ((uint) word >> 8); outBytes[outOff] = (byte)word;
outBytes[outOff + 2] = (byte) ((uint) word >> 16); outBytes[outOff + 1] = (byte)((uint)word >> 8);
outBytes[outOff + 3] = (byte) ((uint) word >> 24); outBytes[outOff + 2] = (byte)((uint)word >> 16);
outBytes[outOff + 3] = (byte)((uint)word >> 24);
} }
public override int DoFinal( public override int DoFinal(
byte[] output, byte[] output,
int outOff) { int outOff)
{
Finish(); Finish();
UnpackWord(H1, output, outOff); UnpackWord(H1, output, outOff);
@ -303,17 +334,19 @@ namespace TLSharp.Core.MTProto.Crypto {
* reset the chaining variables to the IV values. * reset the chaining variables to the IV values.
*/ */
public override void Reset() { public override void Reset()
{
base.Reset(); base.Reset();
H1 = unchecked(0x67452301); H1 = unchecked(0x67452301);
H2 = unchecked((int) 0xefcdab89); H2 = unchecked((int)0xefcdab89);
H3 = unchecked((int) 0x98badcfe); H3 = unchecked((int)0x98badcfe);
H4 = unchecked(0x10325476); H4 = unchecked(0x10325476);
xOff = 0; xOff = 0;
for (int i = 0; i != X.Length; i++) { for (int i = 0; i != X.Length; i++)
{
X[i] = 0; X[i] = 0;
} }
} }
@ -324,8 +357,9 @@ namespace TLSharp.Core.MTProto.Crypto {
private int RotateLeft( private int RotateLeft(
int x, int x,
int n) { int n)
return (x << n) | (int) ((uint) x >> (32 - n)); {
return (x << n) | (int)((uint)x >> (32 - n));
} }
/* /*
@ -335,32 +369,37 @@ namespace TLSharp.Core.MTProto.Crypto {
private int F( private int F(
int u, int u,
int v, int v,
int w) { int w)
{
return (u & v) | (~u & w); return (u & v) | (~u & w);
} }
private int G( private int G(
int u, int u,
int v, int v,
int w) { int w)
{
return (u & w) | (v & ~w); return (u & w) | (v & ~w);
} }
private int H( private int H(
int u, int u,
int v, int v,
int w) { int w)
{
return u ^ v ^ w; return u ^ v ^ w;
} }
private int K( private int K(
int u, int u,
int v, int v,
int w) { int w)
{
return v ^ (u | ~w); return v ^ (u | ~w);
} }
internal override void ProcessBlock() { internal override void ProcessBlock()
{
int a = H1; int a = H1;
int b = H2; int b = H2;
int c = H3; int c = H3;
@ -369,82 +408,82 @@ namespace TLSharp.Core.MTProto.Crypto {
// //
// Round 1 - F cycle, 16 times. // Round 1 - F cycle, 16 times.
// //
a = RotateLeft((a + F(b, c, d) + X[0] + unchecked((int) 0xd76aa478)), S11) + b; a = RotateLeft((a + F(b, c, d) + X[0] + unchecked((int)0xd76aa478)), S11) + b;
d = RotateLeft((d + F(a, b, c) + X[1] + unchecked((int) 0xe8c7b756)), S12) + a; d = RotateLeft((d + F(a, b, c) + X[1] + unchecked((int)0xe8c7b756)), S12) + a;
c = RotateLeft((c + F(d, a, b) + X[2] + unchecked(0x242070db)), S13) + d; c = RotateLeft((c + F(d, a, b) + X[2] + unchecked(0x242070db)), S13) + d;
b = RotateLeft((b + F(c, d, a) + X[3] + unchecked((int) 0xc1bdceee)), S14) + c; b = RotateLeft((b + F(c, d, a) + X[3] + unchecked((int)0xc1bdceee)), S14) + c;
a = RotateLeft((a + F(b, c, d) + X[4] + unchecked((int) 0xf57c0faf)), S11) + b; a = RotateLeft((a + F(b, c, d) + X[4] + unchecked((int)0xf57c0faf)), S11) + b;
d = RotateLeft((d + F(a, b, c) + X[5] + unchecked(0x4787c62a)), S12) + a; d = RotateLeft((d + F(a, b, c) + X[5] + unchecked(0x4787c62a)), S12) + a;
c = RotateLeft((c + F(d, a, b) + X[6] + unchecked((int) 0xa8304613)), S13) + d; c = RotateLeft((c + F(d, a, b) + X[6] + unchecked((int)0xa8304613)), S13) + d;
b = RotateLeft((b + F(c, d, a) + X[7] + unchecked((int) 0xfd469501)), S14) + c; b = RotateLeft((b + F(c, d, a) + X[7] + unchecked((int)0xfd469501)), S14) + c;
a = RotateLeft((a + F(b, c, d) + X[8] + unchecked(0x698098d8)), S11) + b; a = RotateLeft((a + F(b, c, d) + X[8] + unchecked(0x698098d8)), S11) + b;
d = RotateLeft((d + F(a, b, c) + X[9] + unchecked((int) 0x8b44f7af)), S12) + a; d = RotateLeft((d + F(a, b, c) + X[9] + unchecked((int)0x8b44f7af)), S12) + a;
c = RotateLeft((c + F(d, a, b) + X[10] + unchecked((int) 0xffff5bb1)), S13) + d; c = RotateLeft((c + F(d, a, b) + X[10] + unchecked((int)0xffff5bb1)), S13) + d;
b = RotateLeft((b + F(c, d, a) + X[11] + unchecked((int) 0x895cd7be)), S14) + c; b = RotateLeft((b + F(c, d, a) + X[11] + unchecked((int)0x895cd7be)), S14) + c;
a = RotateLeft((a + F(b, c, d) + X[12] + unchecked(0x6b901122)), S11) + b; a = RotateLeft((a + F(b, c, d) + X[12] + unchecked(0x6b901122)), S11) + b;
d = RotateLeft((d + F(a, b, c) + X[13] + unchecked((int) 0xfd987193)), S12) + a; d = RotateLeft((d + F(a, b, c) + X[13] + unchecked((int)0xfd987193)), S12) + a;
c = RotateLeft((c + F(d, a, b) + X[14] + unchecked((int) 0xa679438e)), S13) + d; c = RotateLeft((c + F(d, a, b) + X[14] + unchecked((int)0xa679438e)), S13) + d;
b = RotateLeft((b + F(c, d, a) + X[15] + unchecked(0x49b40821)), S14) + c; b = RotateLeft((b + F(c, d, a) + X[15] + unchecked(0x49b40821)), S14) + c;
// //
// Round 2 - G cycle, 16 times. // Round 2 - G cycle, 16 times.
// //
a = RotateLeft((a + G(b, c, d) + X[1] + unchecked((int) 0xf61e2562)), S21) + b; a = RotateLeft((a + G(b, c, d) + X[1] + unchecked((int)0xf61e2562)), S21) + b;
d = RotateLeft((d + G(a, b, c) + X[6] + unchecked((int) 0xc040b340)), S22) + a; d = RotateLeft((d + G(a, b, c) + X[6] + unchecked((int)0xc040b340)), S22) + a;
c = RotateLeft((c + G(d, a, b) + X[11] + unchecked(0x265e5a51)), S23) + d; c = RotateLeft((c + G(d, a, b) + X[11] + unchecked(0x265e5a51)), S23) + d;
b = RotateLeft((b + G(c, d, a) + X[0] + unchecked((int) 0xe9b6c7aa)), S24) + c; b = RotateLeft((b + G(c, d, a) + X[0] + unchecked((int)0xe9b6c7aa)), S24) + c;
a = RotateLeft((a + G(b, c, d) + X[5] + unchecked((int) 0xd62f105d)), S21) + b; a = RotateLeft((a + G(b, c, d) + X[5] + unchecked((int)0xd62f105d)), S21) + b;
d = RotateLeft((d + G(a, b, c) + X[10] + unchecked(0x02441453)), S22) + a; d = RotateLeft((d + G(a, b, c) + X[10] + unchecked(0x02441453)), S22) + a;
c = RotateLeft((c + G(d, a, b) + X[15] + unchecked((int) 0xd8a1e681)), S23) + d; c = RotateLeft((c + G(d, a, b) + X[15] + unchecked((int)0xd8a1e681)), S23) + d;
b = RotateLeft((b + G(c, d, a) + X[4] + unchecked((int) 0xe7d3fbc8)), S24) + c; b = RotateLeft((b + G(c, d, a) + X[4] + unchecked((int)0xe7d3fbc8)), S24) + c;
a = RotateLeft((a + G(b, c, d) + X[9] + unchecked(0x21e1cde6)), S21) + b; a = RotateLeft((a + G(b, c, d) + X[9] + unchecked(0x21e1cde6)), S21) + b;
d = RotateLeft((d + G(a, b, c) + X[14] + unchecked((int) 0xc33707d6)), S22) + a; d = RotateLeft((d + G(a, b, c) + X[14] + unchecked((int)0xc33707d6)), S22) + a;
c = RotateLeft((c + G(d, a, b) + X[3] + unchecked((int) 0xf4d50d87)), S23) + d; c = RotateLeft((c + G(d, a, b) + X[3] + unchecked((int)0xf4d50d87)), S23) + d;
b = RotateLeft((b + G(c, d, a) + X[8] + unchecked(0x455a14ed)), S24) + c; b = RotateLeft((b + G(c, d, a) + X[8] + unchecked(0x455a14ed)), S24) + c;
a = RotateLeft((a + G(b, c, d) + X[13] + unchecked((int) 0xa9e3e905)), S21) + b; a = RotateLeft((a + G(b, c, d) + X[13] + unchecked((int)0xa9e3e905)), S21) + b;
d = RotateLeft((d + G(a, b, c) + X[2] + unchecked((int) 0xfcefa3f8)), S22) + a; d = RotateLeft((d + G(a, b, c) + X[2] + unchecked((int)0xfcefa3f8)), S22) + a;
c = RotateLeft((c + G(d, a, b) + X[7] + unchecked(0x676f02d9)), S23) + d; c = RotateLeft((c + G(d, a, b) + X[7] + unchecked(0x676f02d9)), S23) + d;
b = RotateLeft((b + G(c, d, a) + X[12] + unchecked((int) 0x8d2a4c8a)), S24) + c; b = RotateLeft((b + G(c, d, a) + X[12] + unchecked((int)0x8d2a4c8a)), S24) + c;
// //
// Round 3 - H cycle, 16 times. // Round 3 - H cycle, 16 times.
// //
a = RotateLeft((a + H(b, c, d) + X[5] + unchecked((int) 0xfffa3942)), S31) + b; a = RotateLeft((a + H(b, c, d) + X[5] + unchecked((int)0xfffa3942)), S31) + b;
d = RotateLeft((d + H(a, b, c) + X[8] + unchecked((int) 0x8771f681)), S32) + a; d = RotateLeft((d + H(a, b, c) + X[8] + unchecked((int)0x8771f681)), S32) + a;
c = RotateLeft((c + H(d, a, b) + X[11] + unchecked(0x6d9d6122)), S33) + d; c = RotateLeft((c + H(d, a, b) + X[11] + unchecked(0x6d9d6122)), S33) + d;
b = RotateLeft((b + H(c, d, a) + X[14] + unchecked((int) 0xfde5380c)), S34) + c; b = RotateLeft((b + H(c, d, a) + X[14] + unchecked((int)0xfde5380c)), S34) + c;
a = RotateLeft((a + H(b, c, d) + X[1] + unchecked((int) 0xa4beea44)), S31) + b; a = RotateLeft((a + H(b, c, d) + X[1] + unchecked((int)0xa4beea44)), S31) + b;
d = RotateLeft((d + H(a, b, c) + X[4] + unchecked(0x4bdecfa9)), S32) + a; d = RotateLeft((d + H(a, b, c) + X[4] + unchecked(0x4bdecfa9)), S32) + a;
c = RotateLeft((c + H(d, a, b) + X[7] + unchecked((int) 0xf6bb4b60)), S33) + d; c = RotateLeft((c + H(d, a, b) + X[7] + unchecked((int)0xf6bb4b60)), S33) + d;
b = RotateLeft((b + H(c, d, a) + X[10] + unchecked((int) 0xbebfbc70)), S34) + c; b = RotateLeft((b + H(c, d, a) + X[10] + unchecked((int)0xbebfbc70)), S34) + c;
a = RotateLeft((a + H(b, c, d) + X[13] + unchecked(0x289b7ec6)), S31) + b; a = RotateLeft((a + H(b, c, d) + X[13] + unchecked(0x289b7ec6)), S31) + b;
d = RotateLeft((d + H(a, b, c) + X[0] + unchecked((int) 0xeaa127fa)), S32) + a; d = RotateLeft((d + H(a, b, c) + X[0] + unchecked((int)0xeaa127fa)), S32) + a;
c = RotateLeft((c + H(d, a, b) + X[3] + unchecked((int) 0xd4ef3085)), S33) + d; c = RotateLeft((c + H(d, a, b) + X[3] + unchecked((int)0xd4ef3085)), S33) + d;
b = RotateLeft((b + H(c, d, a) + X[6] + unchecked(0x04881d05)), S34) + c; b = RotateLeft((b + H(c, d, a) + X[6] + unchecked(0x04881d05)), S34) + c;
a = RotateLeft((a + H(b, c, d) + X[9] + unchecked((int) 0xd9d4d039)), S31) + b; a = RotateLeft((a + H(b, c, d) + X[9] + unchecked((int)0xd9d4d039)), S31) + b;
d = RotateLeft((d + H(a, b, c) + X[12] + unchecked((int) 0xe6db99e5)), S32) + a; d = RotateLeft((d + H(a, b, c) + X[12] + unchecked((int)0xe6db99e5)), S32) + a;
c = RotateLeft((c + H(d, a, b) + X[15] + unchecked(0x1fa27cf8)), S33) + d; c = RotateLeft((c + H(d, a, b) + X[15] + unchecked(0x1fa27cf8)), S33) + d;
b = RotateLeft((b + H(c, d, a) + X[2] + unchecked((int) 0xc4ac5665)), S34) + c; b = RotateLeft((b + H(c, d, a) + X[2] + unchecked((int)0xc4ac5665)), S34) + c;
// //
// Round 4 - K cycle, 16 times. // Round 4 - K cycle, 16 times.
// //
a = RotateLeft((a + K(b, c, d) + X[0] + unchecked((int) 0xf4292244)), S41) + b; a = RotateLeft((a + K(b, c, d) + X[0] + unchecked((int)0xf4292244)), S41) + b;
d = RotateLeft((d + K(a, b, c) + X[7] + unchecked(0x432aff97)), S42) + a; d = RotateLeft((d + K(a, b, c) + X[7] + unchecked(0x432aff97)), S42) + a;
c = RotateLeft((c + K(d, a, b) + X[14] + unchecked((int) 0xab9423a7)), S43) + d; c = RotateLeft((c + K(d, a, b) + X[14] + unchecked((int)0xab9423a7)), S43) + d;
b = RotateLeft((b + K(c, d, a) + X[5] + unchecked((int) 0xfc93a039)), S44) + c; b = RotateLeft((b + K(c, d, a) + X[5] + unchecked((int)0xfc93a039)), S44) + c;
a = RotateLeft((a + K(b, c, d) + X[12] + unchecked(0x655b59c3)), S41) + b; a = RotateLeft((a + K(b, c, d) + X[12] + unchecked(0x655b59c3)), S41) + b;
d = RotateLeft((d + K(a, b, c) + X[3] + unchecked((int) 0x8f0ccc92)), S42) + a; d = RotateLeft((d + K(a, b, c) + X[3] + unchecked((int)0x8f0ccc92)), S42) + a;
c = RotateLeft((c + K(d, a, b) + X[10] + unchecked((int) 0xffeff47d)), S43) + d; c = RotateLeft((c + K(d, a, b) + X[10] + unchecked((int)0xffeff47d)), S43) + d;
b = RotateLeft((b + K(c, d, a) + X[1] + unchecked((int) 0x85845dd1)), S44) + c; b = RotateLeft((b + K(c, d, a) + X[1] + unchecked((int)0x85845dd1)), S44) + c;
a = RotateLeft((a + K(b, c, d) + X[8] + unchecked(0x6fa87e4f)), S41) + b; a = RotateLeft((a + K(b, c, d) + X[8] + unchecked(0x6fa87e4f)), S41) + b;
d = RotateLeft((d + K(a, b, c) + X[15] + unchecked((int) 0xfe2ce6e0)), S42) + a; d = RotateLeft((d + K(a, b, c) + X[15] + unchecked((int)0xfe2ce6e0)), S42) + a;
c = RotateLeft((c + K(d, a, b) + X[6] + unchecked((int) 0xa3014314)), S43) + d; c = RotateLeft((c + K(d, a, b) + X[6] + unchecked((int)0xa3014314)), S43) + d;
b = RotateLeft((b + K(c, d, a) + X[13] + unchecked(0x4e0811a1)), S44) + c; b = RotateLeft((b + K(c, d, a) + X[13] + unchecked(0x4e0811a1)), S44) + c;
a = RotateLeft((a + K(b, c, d) + X[4] + unchecked((int) 0xf7537e82)), S41) + b; a = RotateLeft((a + K(b, c, d) + X[4] + unchecked((int)0xf7537e82)), S41) + b;
d = RotateLeft((d + K(a, b, c) + X[11] + unchecked((int) 0xbd3af235)), S42) + a; d = RotateLeft((d + K(a, b, c) + X[11] + unchecked((int)0xbd3af235)), S42) + a;
c = RotateLeft((c + K(d, a, b) + X[2] + unchecked(0x2ad7d2bb)), S43) + d; c = RotateLeft((c + K(d, a, b) + X[2] + unchecked(0x2ad7d2bb)), S43) + d;
b = RotateLeft((b + K(c, d, a) + X[9] + unchecked((int) 0xeb86d391)), S44) + c; b = RotateLeft((b + K(c, d, a) + X[9] + unchecked((int)0xeb86d391)), S44) + c;
H1 += a; H1 += a;
H2 += b; H2 += b;
@ -455,7 +494,8 @@ namespace TLSharp.Core.MTProto.Crypto {
// reset the offset and clean out the word buffer. // reset the offset and clean out the word buffer.
// //
xOff = 0; xOff = 0;
for (int i = 0; i != X.Length; i++) { for (int i = 0; i != X.Length; i++)
{
X[i] = 0; X[i] = 0;
} }
} }

View file

@ -3,31 +3,38 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Security.Cryptography; using System.Security.Cryptography;
namespace TLSharp.Core.MTProto.Crypto { namespace TLSharp.Core.MTProto.Crypto
{
class RSAServerKey { class RSAServerKey
{
private string fingerprint; private string fingerprint;
private BigInteger m; private BigInteger m;
private BigInteger e; private BigInteger e;
public RSAServerKey(string fingerprint, BigInteger m, BigInteger e) { public RSAServerKey(string fingerprint, BigInteger m, BigInteger e)
{
this.fingerprint = fingerprint; this.fingerprint = fingerprint;
this.m = m; this.m = m;
this.e = e; this.e = e;
} }
public byte[] Encrypt(byte[] data, int offset, int length) { public byte[] Encrypt(byte[] data, int offset, int length)
{
using(MemoryStream buffer = new MemoryStream(255))
using(BinaryWriter writer = new BinaryWriter(buffer)) { using (MemoryStream buffer = new MemoryStream(255))
using(SHA1 sha1 = new SHA1Managed()) { using (BinaryWriter writer = new BinaryWriter(buffer))
{
using (SHA1 sha1 = new SHA1Managed())
{
byte[] hashsum = sha1.ComputeHash(data, offset, length); byte[] hashsum = sha1.ComputeHash(data, offset, length);
writer.Write(hashsum); writer.Write(hashsum);
} }
buffer.Write(data, offset, length); buffer.Write(data, offset, length);
if(length < 235) { if (length < 235)
{
byte[] padding = new byte[235 - length]; byte[] padding = new byte[235 - length];
new Random().NextBytes(padding); new Random().NextBytes(padding);
buffer.Write(padding, 0, padding.Length); buffer.Write(padding, 0, padding.Length);
@ -35,29 +42,35 @@ namespace TLSharp.Core.MTProto.Crypto {
byte[] ciphertext = new BigInteger(1, buffer.ToArray()).ModPow(e, m).ToByteArrayUnsigned(); byte[] ciphertext = new BigInteger(1, buffer.ToArray()).ModPow(e, m).ToByteArrayUnsigned();
if(ciphertext.Length == 256) { if (ciphertext.Length == 256)
{
return ciphertext; return ciphertext;
} else { }
else {
byte[] paddedCiphertext = new byte[256]; byte[] paddedCiphertext = new byte[256];
int padding = 256 - ciphertext.Length; int padding = 256 - ciphertext.Length;
for(int i = 0; i < padding; i++) { for (int i = 0; i < padding; i++)
{
paddedCiphertext[i] = 0; paddedCiphertext[i] = 0;
} }
ciphertext.CopyTo(paddedCiphertext, padding); ciphertext.CopyTo(paddedCiphertext, padding);
return paddedCiphertext; return paddedCiphertext;
} }
} }
} }
} }
public class RSA { public class RSA
{
private static readonly Dictionary<string, RSAServerKey> serverKeys = new Dictionary<string, RSAServerKey>() { private static readonly Dictionary<string, RSAServerKey> serverKeys = new Dictionary<string, RSAServerKey>() {
{ "216be86c022bb4c3", new RSAServerKey("216be86c022bb4c3", new BigInteger("00C150023E2F70DB7985DED064759CFECF0AF328E69A41DAF4D6F01B538135A6F91F8F8B2A0EC9BA9720CE352EFCF6C5680FFC424BD634864902DE0B4BD6D49F4E580230E3AE97D95C8B19442B3C0A10D8F5633FECEDD6926A7F6DAB0DDB7D457F9EA81B8465FCD6FFFEED114011DF91C059CAEDAF97625F6C96ECC74725556934EF781D866B34F011FCE4D835A090196E9A5F0E4449AF7EB697DDB9076494CA5F81104A305B6DD27665722C46B60E5DF680FB16B210607EF217652E60236C255F6A28315F4083A96791D7214BF64C1DF4FD0DB1944FB26A2A57031B32EEE64AD15A8BA68885CDE74A5BFC920F6ABF59BA5C75506373E7130F9042DA922179251F", 16), new BigInteger("010001", 16)) } { "216be86c022bb4c3", new RSAServerKey("216be86c022bb4c3", new BigInteger("00C150023E2F70DB7985DED064759CFECF0AF328E69A41DAF4D6F01B538135A6F91F8F8B2A0EC9BA9720CE352EFCF6C5680FFC424BD634864902DE0B4BD6D49F4E580230E3AE97D95C8B19442B3C0A10D8F5633FECEDD6926A7F6DAB0DDB7D457F9EA81B8465FCD6FFFEED114011DF91C059CAEDAF97625F6C96ECC74725556934EF781D866B34F011FCE4D835A090196E9A5F0E4449AF7EB697DDB9076494CA5F81104A305B6DD27665722C46B60E5DF680FB16B210607EF217652E60236C255F6A28315F4083A96791D7214BF64C1DF4FD0DB1944FB26A2A57031B32EEE64AD15A8BA68885CDE74A5BFC920F6ABF59BA5C75506373E7130F9042DA922179251F", 16), new BigInteger("010001", 16)) }
}; };
public static byte[] Encrypt(string fingerprint, byte[] data, int offset, int length) { public static byte[] Encrypt(string fingerprint, byte[] data, int offset, int length)
{
string fingerprintLower = fingerprint.ToLower(); string fingerprintLower = fingerprint.ToLower();
if(!serverKeys.ContainsKey(fingerprintLower)) { if (!serverKeys.ContainsKey(fingerprintLower))
{
return null; return null;
} }
@ -66,5 +79,5 @@ namespace TLSharp.Core.MTProto.Crypto {
return key.Encrypt(data, offset, length); return key.Encrypt(data, offset, length);
} }
} }
} }

View file

@ -4,73 +4,90 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace TLSharp.Core.MTProto.Crypto { namespace TLSharp.Core.MTProto.Crypto
public class Salt : IComparable<Salt> { {
public class Salt : IComparable<Salt>
{
private int validSince; private int validSince;
private int validUntil; private int validUntil;
private ulong salt; private ulong salt;
public Salt(int validSince, int validUntil, ulong salt) { public Salt(int validSince, int validUntil, ulong salt)
{
this.validSince = validSince; this.validSince = validSince;
this.validUntil = validUntil; this.validUntil = validUntil;
this.salt = salt; this.salt = salt;
} }
public int ValidSince { public int ValidSince
{
get { return validSince; } get { return validSince; }
} }
public int ValidUntil { public int ValidUntil
{
get { return validUntil; } get { return validUntil; }
} }
public ulong Value { public ulong Value
{
get { return salt; } get { return salt; }
} }
public int CompareTo(Salt other) { public int CompareTo(Salt other)
{
return validUntil.CompareTo(other.validSince); return validUntil.CompareTo(other.validSince);
} }
} }
public class SaltCollection { public class SaltCollection
private SortedSet<Salt> salts; {
private SortedSet<Salt> salts;
public void Add(Salt salt) { public void Add(Salt salt)
{
salts.Add(salt); salts.Add(salt);
} }
public int Count { public int Count
get { {
get
{
return salts.Count; return salts.Count;
} }
} }
// TODO: get actual salt and other... // TODO: get actual salt and other...
} }
public class GetFutureSaltsResponse { public class GetFutureSaltsResponse
{
private ulong requestId; private ulong requestId;
private int now; private int now;
private SaltCollection salts; private SaltCollection salts;
public GetFutureSaltsResponse(ulong requestId, int now) { public GetFutureSaltsResponse(ulong requestId, int now)
{
this.requestId = requestId; this.requestId = requestId;
this.now = now; this.now = now;
} }
public void AddSalt(Salt salt) { public void AddSalt(Salt salt)
{
salts.Add(salt); salts.Add(salt);
} }
public ulong RequestId { public ulong RequestId
{
get { return requestId; } get { return requestId; }
} }
public int Now { public int Now
{
get { return now; } get { return now; }
} }
public SaltCollection Salts { public SaltCollection Salts
{
get { return salts; } get { return salts; }
} }
} }

View file

@ -4,22 +4,28 @@ using System.Text;
namespace TLSharp.Core.MTProto namespace TLSharp.Core.MTProto
{ {
public class Serializers { public class Serializers
{
public static class Bytes { public static class Bytes
public static byte[] read(BinaryReader binaryReader) { {
public static byte[] read(BinaryReader binaryReader)
{
byte firstByte = binaryReader.ReadByte(); byte firstByte = binaryReader.ReadByte();
int len, padding; int len, padding;
if(firstByte == 254) { if (firstByte == 254)
{
len = binaryReader.ReadByte() | (binaryReader.ReadByte() << 8) | (binaryReader.ReadByte() << 16); len = binaryReader.ReadByte() | (binaryReader.ReadByte() << 8) | (binaryReader.ReadByte() << 16);
padding = len%4; padding = len % 4;
} else { }
else {
len = firstByte; len = firstByte;
padding = (len + 1) % 4; padding = (len + 1) % 4;
} }
byte[] data = binaryReader.ReadBytes(len); byte[] data = binaryReader.ReadBytes(len);
if(padding > 0) { if (padding > 0)
{
padding = 4 - padding; padding = 4 - padding;
binaryReader.ReadBytes(padding); binaryReader.ReadBytes(padding);
} }
@ -27,19 +33,24 @@ namespace TLSharp.Core.MTProto
return data; return data;
} }
public static BinaryWriter write(BinaryWriter binaryWriter, byte[] data) { public static BinaryWriter write(BinaryWriter binaryWriter, byte[] data)
{
int padding; int padding;
if(data.Length < 254) { if (data.Length < 254)
padding = (data.Length + 1)%4; {
if(padding != 0) { padding = (data.Length + 1) % 4;
if (padding != 0)
{
padding = 4 - padding; padding = 4 - padding;
} }
binaryWriter.Write((byte) data.Length); binaryWriter.Write((byte)data.Length);
binaryWriter.Write(data); binaryWriter.Write(data);
} else { }
padding = (data.Length)%4; else {
if(padding != 0) { padding = (data.Length) % 4;
if (padding != 0)
{
padding = 4 - padding; padding = 4 - padding;
} }
@ -51,7 +62,8 @@ namespace TLSharp.Core.MTProto
} }
for(int i = 0; i < padding; i++) { for (int i = 0; i < padding; i++)
{
binaryWriter.Write((byte)0); binaryWriter.Write((byte)0);
} }
@ -59,20 +71,25 @@ namespace TLSharp.Core.MTProto
} }
} }
public static class String { public static class String
public static string read(BinaryReader reader) { {
public static string read(BinaryReader reader)
{
byte[] data = Bytes.read(reader); byte[] data = Bytes.read(reader);
return Encoding.UTF8.GetString(data, 0, data.Length); return Encoding.UTF8.GetString(data, 0, data.Length);
} }
public static BinaryWriter write(BinaryWriter writer, string str) { public static BinaryWriter write(BinaryWriter writer, string str)
{
return Bytes.write(writer, Encoding.UTF8.GetBytes(str)); return Bytes.write(writer, Encoding.UTF8.GetBytes(str));
} }
} }
public static string VectorToString<T>(List<T> list) { public static string VectorToString<T>(List<T> list)
{
string[] tokens = new string[list.Count]; string[] tokens = new string[list.Count];
for(int i = 0; i < list.Count; i++) { for (int i = 0; i < list.Count; i++)
{
tokens[i] = list[i].ToString(); tokens[i] = list[i].ToString();
} }
return "[" + System.String.Join(", ", tokens) + "]"; return "[" + System.String.Join(", ", tokens) + "]";

File diff suppressed because it is too large Load diff

View file

@ -4,74 +4,74 @@ using System.Threading.Tasks;
namespace TLSharp.Core.Network namespace TLSharp.Core.Network
{ {
public class MtProtoPlainSender public class MtProtoPlainSender
{ {
private int sequence = 0; private int sequence = 0;
private int timeOffset; private int timeOffset;
private long lastMessageId; private long lastMessageId;
private Random random; private Random random;
private TcpTransport _transport; private TcpTransport _transport;
public MtProtoPlainSender(TcpTransport transport) public MtProtoPlainSender(TcpTransport transport)
{ {
_transport = transport; _transport = transport;
random = new Random(); random = new Random();
} }
public async Task Send(byte[] data) public async Task Send(byte[] data)
{ {
using (var memoryStream = new MemoryStream()) using (var memoryStream = new MemoryStream())
{ {
using (var binaryWriter = new BinaryWriter(memoryStream)) using (var binaryWriter = new BinaryWriter(memoryStream))
{ {
binaryWriter.Write((long)0); binaryWriter.Write((long)0);
binaryWriter.Write(GetNewMessageId()); binaryWriter.Write(GetNewMessageId());
binaryWriter.Write(data.Length); binaryWriter.Write(data.Length);
binaryWriter.Write(data); binaryWriter.Write(data);
byte[] packet = memoryStream.ToArray(); byte[] packet = memoryStream.ToArray();
await _transport.Send(packet); await _transport.Send(packet);
} }
} }
} }
public async Task<byte[]> Recieve() public async Task<byte[]> Recieve()
{ {
var result = await _transport.Receieve(); var result = await _transport.Receieve();
using (var memoryStream = new MemoryStream(result.Body)) using (var memoryStream = new MemoryStream(result.Body))
{ {
using (BinaryReader binaryReader = new BinaryReader(memoryStream)) using (BinaryReader binaryReader = new BinaryReader(memoryStream))
{ {
long authKeyid = binaryReader.ReadInt64(); long authKeyid = binaryReader.ReadInt64();
long messageId = binaryReader.ReadInt64(); long messageId = binaryReader.ReadInt64();
int messageLength = binaryReader.ReadInt32(); int messageLength = binaryReader.ReadInt32();
byte[] response = binaryReader.ReadBytes(messageLength); byte[] response = binaryReader.ReadBytes(messageLength);
return response; return response;
} }
} }
} }
private long GetNewMessageId() private long GetNewMessageId()
{ {
long time = Convert.ToInt64((DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalMilliseconds); long time = Convert.ToInt64((DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalMilliseconds);
long newMessageId = ((time / 1000 + timeOffset) << 32) | long newMessageId = ((time / 1000 + timeOffset) << 32) |
((time % 1000) << 22) | ((time % 1000) << 22) |
(random.Next(524288) << 2); // 2^19 (random.Next(524288) << 2); // 2^19
// [ unix timestamp : 32 bit] [ milliseconds : 10 bit ] [ buffer space : 1 bit ] [ random : 19 bit ] [ msg_id type : 2 bit ] = [ msg_id : 64 bit ] // [ unix timestamp : 32 bit] [ milliseconds : 10 bit ] [ buffer space : 1 bit ] [ random : 19 bit ] [ msg_id type : 2 bit ] = [ msg_id : 64 bit ]
if (lastMessageId >= newMessageId) if (lastMessageId >= newMessageId)
{ {
newMessageId = lastMessageId + 4; newMessageId = lastMessageId + 4;
} }
lastMessageId = newMessageId; lastMessageId = newMessageId;
return newMessageId; return newMessageId;
} }
} }
} }

View file

@ -14,204 +14,204 @@ using TLSharp.Core.Utils;
namespace TLSharp.Core.Network namespace TLSharp.Core.Network
{ {
public class MtProtoSender public class MtProtoSender
{ {
//private ulong sessionId = GenerateRandomUlong(); //private ulong sessionId = GenerateRandomUlong();
private TcpTransport _transport;
private Session _session;
public List<ulong> needConfirmation = new List<ulong>(); private TcpTransport _transport;
private Session _session;
public MtProtoSender(TcpTransport transport, Session session) public List<ulong> needConfirmation = new List<ulong>();
{
_transport = transport;
_session = session;
}
public void ChangeTransport(TcpTransport transport) public MtProtoSender(TcpTransport transport, Session session)
{ {
_transport = transport; _transport = transport;
} _session = session;
}
private int GenerateSequence(bool confirmed) public void ChangeTransport(TcpTransport transport)
{ {
return confirmed ? _session.Sequence++ * 2 + 1 : _session.Sequence * 2; _transport = transport;
} }
public async Task Send(MTProtoRequest request) private int GenerateSequence(bool confirmed)
{ {
// TODO: refactor return confirmed ? _session.Sequence++ * 2 + 1 : _session.Sequence * 2;
if (needConfirmation.Any()) }
{
var ackRequest = new AckRequest(needConfirmation);
using (var memory = new MemoryStream())
using (var writer = new BinaryWriter(memory))
{
ackRequest.OnSend(writer);
await Send(memory.ToArray(), ackRequest);
needConfirmation.Clear();
}
}
using (var memory = new MemoryStream()) public async Task Send(MTProtoRequest request)
using (var writer = new BinaryWriter(memory)) {
{ // TODO: refactor
request.OnSend(writer); if (needConfirmation.Any())
await Send(memory.ToArray(), request); {
} var ackRequest = new AckRequest(needConfirmation);
using (var memory = new MemoryStream())
using (var writer = new BinaryWriter(memory))
{
ackRequest.OnSend(writer);
await Send(memory.ToArray(), ackRequest);
needConfirmation.Clear();
}
}
_session.Save();
}
public async Task Send(byte[] packet, MTProtoRequest request) using (var memory = new MemoryStream())
{ using (var writer = new BinaryWriter(memory))
request.MessageId = _session.GetNewMessageId(); {
request.OnSend(writer);
byte[] msgKey; await Send(memory.ToArray(), request);
byte[] ciphertext; }
using (MemoryStream plaintextPacket = makeMemory(8 + 8 + 8 + 4 + 4 + packet.Length))
{
using (BinaryWriter plaintextWriter = new BinaryWriter(plaintextPacket))
{
plaintextWriter.Write(_session.Salt);
plaintextWriter.Write(_session.Id);
plaintextWriter.Write(request.MessageId);
plaintextWriter.Write(GenerateSequence(request.Confirmed));
plaintextWriter.Write(packet.Length);
plaintextWriter.Write(packet);
msgKey = Helpers.CalcMsgKey(plaintextPacket.GetBuffer()); _session.Save();
ciphertext = AES.EncryptAES(Helpers.CalcKey(_session.AuthKey.Data, msgKey, true), plaintextPacket.GetBuffer()); }
}
}
using (MemoryStream ciphertextPacket = makeMemory(8 + 16 + ciphertext.Length)) public async Task Send(byte[] packet, MTProtoRequest request)
{ {
using (BinaryWriter writer = new BinaryWriter(ciphertextPacket)) request.MessageId = _session.GetNewMessageId();
{
writer.Write(_session.AuthKey.Id);
writer.Write(msgKey);
writer.Write(ciphertext);
await _transport.Send(ciphertextPacket.GetBuffer()); byte[] msgKey;
} byte[] ciphertext;
} using (MemoryStream plaintextPacket = makeMemory(8 + 8 + 8 + 4 + 4 + packet.Length))
} {
using (BinaryWriter plaintextWriter = new BinaryWriter(plaintextPacket))
{
plaintextWriter.Write(_session.Salt);
plaintextWriter.Write(_session.Id);
plaintextWriter.Write(request.MessageId);
plaintextWriter.Write(GenerateSequence(request.Confirmed));
plaintextWriter.Write(packet.Length);
plaintextWriter.Write(packet);
private Tuple<byte[], ulong, int> DecodeMessage(byte[] body) msgKey = Helpers.CalcMsgKey(plaintextPacket.GetBuffer());
{ ciphertext = AES.EncryptAES(Helpers.CalcKey(_session.AuthKey.Data, msgKey, true), plaintextPacket.GetBuffer());
byte[] message; }
ulong remoteMessageId; }
int remoteSequence;
using (var inputStream = new MemoryStream(body)) using (MemoryStream ciphertextPacket = makeMemory(8 + 16 + ciphertext.Length))
using (var inputReader = new BinaryReader(inputStream)) {
{ using (BinaryWriter writer = new BinaryWriter(ciphertextPacket))
if (inputReader.BaseStream.Length < 8) {
throw new InvalidOperationException($"Can't decode packet"); writer.Write(_session.AuthKey.Id);
writer.Write(msgKey);
ulong remoteAuthKeyId = inputReader.ReadUInt64(); // TODO: check auth key id writer.Write(ciphertext);
byte[] msgKey = inputReader.ReadBytes(16); // TODO: check msg_key correctness
AESKeyData keyData = Helpers.CalcKey(_session.AuthKey.Data, msgKey, false);
byte[] plaintext = AES.DecryptAES(keyData, inputReader.ReadBytes((int)(inputStream.Length - inputStream.Position))); await _transport.Send(ciphertextPacket.GetBuffer());
}
}
}
using (MemoryStream plaintextStream = new MemoryStream(plaintext)) private Tuple<byte[], ulong, int> DecodeMessage(byte[] body)
using (BinaryReader plaintextReader = new BinaryReader(plaintextStream)) {
{ byte[] message;
var remoteSalt = plaintextReader.ReadUInt64(); ulong remoteMessageId;
var remoteSessionId = plaintextReader.ReadUInt64(); int remoteSequence;
remoteMessageId = plaintextReader.ReadUInt64();
remoteSequence = plaintextReader.ReadInt32();
int msgLen = plaintextReader.ReadInt32();
message = plaintextReader.ReadBytes(msgLen);
}
}
return new Tuple<byte[], ulong, int>(message, remoteMessageId, remoteSequence);
}
public async Task<byte[]> Recieve(MTProtoRequest request) using (var inputStream = new MemoryStream(body))
{ using (var inputReader = new BinaryReader(inputStream))
while (!request.ConfirmReceived) {
{ if (inputReader.BaseStream.Length < 8)
var result = DecodeMessage((await _transport.Receieve()).Body); throw new InvalidOperationException($"Can't decode packet");
using (var messageStream = new MemoryStream(result.Item1, false)) ulong remoteAuthKeyId = inputReader.ReadUInt64(); // TODO: check auth key id
using (var messageReader = new BinaryReader(messageStream)) byte[] msgKey = inputReader.ReadBytes(16); // TODO: check msg_key correctness
{ AESKeyData keyData = Helpers.CalcKey(_session.AuthKey.Data, msgKey, false);
processMessage(result.Item2, result.Item3, messageReader, request);
}
}
return null; byte[] plaintext = AES.DecryptAES(keyData, inputReader.ReadBytes((int)(inputStream.Length - inputStream.Position)));
}
private bool processMessage(ulong messageId, int sequence, BinaryReader messageReader, MTProtoRequest request) using (MemoryStream plaintextStream = new MemoryStream(plaintext))
{ using (BinaryReader plaintextReader = new BinaryReader(plaintextStream))
// TODO: check salt {
// TODO: check sessionid var remoteSalt = plaintextReader.ReadUInt64();
// TODO: check seqno var remoteSessionId = plaintextReader.ReadUInt64();
remoteMessageId = plaintextReader.ReadUInt64();
remoteSequence = plaintextReader.ReadInt32();
int msgLen = plaintextReader.ReadInt32();
message = plaintextReader.ReadBytes(msgLen);
}
}
return new Tuple<byte[], ulong, int>(message, remoteMessageId, remoteSequence);
}
//logger.debug("processMessage: msg_id {0}, sequence {1}, data {2}", BitConverter.ToString(((MemoryStream)messageReader.BaseStream).GetBuffer(), (int) messageReader.BaseStream.Position, (int) (messageReader.BaseStream.Length - messageReader.BaseStream.Position)).Replace("-","").ToLower()); public async Task<byte[]> Recieve(MTProtoRequest request)
needConfirmation.Add(messageId); {
while (!request.ConfirmReceived)
uint code = messageReader.ReadUInt32(); {
messageReader.BaseStream.Position -= 4; var result = DecodeMessage((await _transport.Receieve()).Body);
switch (code)
{
case 0x73f1f8dc: // container
//logger.debug("MSG container");
return HandleContainer(messageId, sequence, messageReader, request);
case 0x7abe77ec: // ping
//logger.debug("MSG ping");
return HandlePing(messageId, sequence, messageReader);
case 0x347773c5: // pong
//logger.debug("MSG pong");
return HandlePong(messageId, sequence, messageReader);
case 0xae500895: // future_salts
//logger.debug("MSG future_salts");
return HandleFutureSalts(messageId, sequence, messageReader);
case 0x9ec20908: // new_session_created
//logger.debug("MSG new_session_created");
return HandleNewSessionCreated(messageId, sequence, messageReader);
case 0x62d6b459: // msgs_ack
//logger.debug("MSG msds_ack");
return HandleMsgsAck(messageId, sequence, messageReader);
case 0xedab447b: // bad_server_salt
//logger.debug("MSG bad_server_salt");
return HandleBadServerSalt(messageId, sequence, messageReader, request);
case 0xa7eff811: // bad_msg_notification
//logger.debug("MSG bad_msg_notification");
return HandleBadMsgNotification(messageId, sequence, messageReader);
case 0x276d3ec6: // msg_detailed_info
//logger.debug("MSG msg_detailed_info");
return HandleMsgDetailedInfo(messageId, sequence, messageReader);
case 0xf35c6d01: // rpc_result
//logger.debug("MSG rpc_result");
return HandleRpcResult(messageId, sequence, messageReader, request);
case 0x3072cfa1: // gzip_packed
//logger.debug("MSG gzip_packed");
return HandleGzipPacked(messageId, sequence, messageReader, request);
case 0xe317af7e:
case 0xd3f45784:
case 0x2b2fbd4e:
case 0x78d4dec1:
case 0x725b04c3:
case 0x74ae4240:
return HandleUpdate(messageId, sequence, messageReader);
default:
//logger.debug("unknown message: {0}", code);
return false;
}
}
private bool HandleUpdate(ulong messageId, int sequence, BinaryReader messageReader) using (var messageStream = new MemoryStream(result.Item1, false))
{ using (var messageReader = new BinaryReader(messageStream))
return false; {
processMessage(result.Item2, result.Item3, messageReader, request);
/* }
}
return null;
}
private bool processMessage(ulong messageId, int sequence, BinaryReader messageReader, MTProtoRequest request)
{
// TODO: check salt
// TODO: check sessionid
// TODO: check seqno
//logger.debug("processMessage: msg_id {0}, sequence {1}, data {2}", BitConverter.ToString(((MemoryStream)messageReader.BaseStream).GetBuffer(), (int) messageReader.BaseStream.Position, (int) (messageReader.BaseStream.Length - messageReader.BaseStream.Position)).Replace("-","").ToLower());
needConfirmation.Add(messageId);
uint code = messageReader.ReadUInt32();
messageReader.BaseStream.Position -= 4;
switch (code)
{
case 0x73f1f8dc: // container
//logger.debug("MSG container");
return HandleContainer(messageId, sequence, messageReader, request);
case 0x7abe77ec: // ping
//logger.debug("MSG ping");
return HandlePing(messageId, sequence, messageReader);
case 0x347773c5: // pong
//logger.debug("MSG pong");
return HandlePong(messageId, sequence, messageReader);
case 0xae500895: // future_salts
//logger.debug("MSG future_salts");
return HandleFutureSalts(messageId, sequence, messageReader);
case 0x9ec20908: // new_session_created
//logger.debug("MSG new_session_created");
return HandleNewSessionCreated(messageId, sequence, messageReader);
case 0x62d6b459: // msgs_ack
//logger.debug("MSG msds_ack");
return HandleMsgsAck(messageId, sequence, messageReader);
case 0xedab447b: // bad_server_salt
//logger.debug("MSG bad_server_salt");
return HandleBadServerSalt(messageId, sequence, messageReader, request);
case 0xa7eff811: // bad_msg_notification
//logger.debug("MSG bad_msg_notification");
return HandleBadMsgNotification(messageId, sequence, messageReader);
case 0x276d3ec6: // msg_detailed_info
//logger.debug("MSG msg_detailed_info");
return HandleMsgDetailedInfo(messageId, sequence, messageReader);
case 0xf35c6d01: // rpc_result
//logger.debug("MSG rpc_result");
return HandleRpcResult(messageId, sequence, messageReader, request);
case 0x3072cfa1: // gzip_packed
//logger.debug("MSG gzip_packed");
return HandleGzipPacked(messageId, sequence, messageReader, request);
case 0xe317af7e:
case 0xd3f45784:
case 0x2b2fbd4e:
case 0x78d4dec1:
case 0x725b04c3:
case 0x74ae4240:
return HandleUpdate(messageId, sequence, messageReader);
default:
//logger.debug("unknown message: {0}", code);
return false;
}
}
private bool HandleUpdate(ulong messageId, int sequence, BinaryReader messageReader)
{
return false;
/*
try try
{ {
UpdatesEvent(TL.Parse<Updates>(messageReader)); UpdatesEvent(TL.Parse<Updates>(messageReader));
@ -223,31 +223,31 @@ namespace TLSharp.Core.Network
return false; return false;
} }
*/ */
} }
private bool HandleGzipPacked(ulong messageId, int sequence, BinaryReader messageReader, MTProtoRequest request) private bool HandleGzipPacked(ulong messageId, int sequence, BinaryReader messageReader, MTProtoRequest request)
{ {
uint code = messageReader.ReadUInt32(); uint code = messageReader.ReadUInt32();
byte[] packedData = GZipStream.UncompressBuffer(Serializers.Bytes.read(messageReader)); byte[] packedData = GZipStream.UncompressBuffer(Serializers.Bytes.read(messageReader));
using (MemoryStream packedStream = new MemoryStream(packedData, false)) using (MemoryStream packedStream = new MemoryStream(packedData, false))
using (BinaryReader compressedReader = new BinaryReader(packedStream)) using (BinaryReader compressedReader = new BinaryReader(packedStream))
{ {
processMessage(messageId, sequence, compressedReader, request); processMessage(messageId, sequence, compressedReader, request);
} }
return true; return true;
} }
private bool HandleRpcResult(ulong messageId, int sequence, BinaryReader messageReader, MTProtoRequest request) private bool HandleRpcResult(ulong messageId, int sequence, BinaryReader messageReader, MTProtoRequest request)
{ {
uint code = messageReader.ReadUInt32(); uint code = messageReader.ReadUInt32();
ulong requestId = messageReader.ReadUInt64(); ulong requestId = messageReader.ReadUInt64();
if (requestId == (ulong) request.MessageId) if (requestId == (ulong)request.MessageId)
request.ConfirmReceived = true; request.ConfirmReceived = true;
//throw new NotImplementedException(); //throw new NotImplementedException();
/* /*
lock (runningRequests) lock (runningRequests)
{ {
if (!runningRequests.ContainsKey(requestId)) if (!runningRequests.ContainsKey(requestId))
@ -262,105 +262,105 @@ namespace TLSharp.Core.Network
} }
*/ */
uint innerCode = messageReader.ReadUInt32(); uint innerCode = messageReader.ReadUInt32();
if (innerCode == 0x2144ca19) if (innerCode == 0x2144ca19)
{ // rpc_error { // rpc_error
int errorCode = messageReader.ReadInt32(); int errorCode = messageReader.ReadInt32();
string errorMessage = Serializers.String.read(messageReader); string errorMessage = Serializers.String.read(messageReader);
if (errorMessage.StartsWith("FLOOD_WAIT_")) if (errorMessage.StartsWith("FLOOD_WAIT_"))
{ {
var resultString = Regex.Match(errorMessage, @"\d+").Value; var resultString = Regex.Match(errorMessage, @"\d+").Value;
var seconds = int.Parse(resultString); var seconds = int.Parse(resultString);
Debug.WriteLine($"Should wait {seconds} sec."); Debug.WriteLine($"Should wait {seconds} sec.");
Thread.Sleep(1000*seconds); Thread.Sleep(1000 * seconds);
} }
else if (errorMessage.StartsWith("PHONE_MIGRATE_")) else if (errorMessage.StartsWith("PHONE_MIGRATE_"))
{ {
var resultString = Regex.Match(errorMessage, @"\d+").Value; var resultString = Regex.Match(errorMessage, @"\d+").Value;
var dcIdx = int.Parse(resultString); var dcIdx = int.Parse(resultString);
var exception = new InvalidOperationException($"Your phone number registered to {dcIdx} dc. Please update settings. See https://github.com/sochix/TLSharp#i-get-an-error-migrate_x for details."); var exception = new InvalidOperationException($"Your phone number registered to {dcIdx} dc. Please update settings. See https://github.com/sochix/TLSharp#i-get-an-error-migrate_x for details.");
exception.Data.Add("dcId", dcIdx); exception.Data.Add("dcId", dcIdx);
throw exception; throw exception;
} }
else else
{ {
throw new InvalidOperationException(errorMessage); throw new InvalidOperationException(errorMessage);
} }
}
else if (innerCode == 0x3072cfa1)
{
try
{
// gzip_packed
byte[] packedData = Serializers.Bytes.read(messageReader);
using (var packedStream = new MemoryStream(packedData, false))
using (var zipStream = new GZipStream(packedStream, CompressionMode.Decompress))
using (var compressedReader = new BinaryReader(zipStream))
{
request.OnResponse(compressedReader);
}
}
catch (ZlibException ex)
{
}
}
else
{
messageReader.BaseStream.Position -= 4;
request.OnResponse(messageReader); }
} else if (innerCode == 0x3072cfa1)
{
try
{
// gzip_packed
byte[] packedData = Serializers.Bytes.read(messageReader);
using (var packedStream = new MemoryStream(packedData, false))
using (var zipStream = new GZipStream(packedStream, CompressionMode.Decompress))
using (var compressedReader = new BinaryReader(zipStream))
{
request.OnResponse(compressedReader);
}
}
catch (ZlibException ex)
{
return false; }
} }
else
{
messageReader.BaseStream.Position -= 4;
private bool HandleMsgDetailedInfo(ulong messageId, int sequence, BinaryReader messageReader) request.OnResponse(messageReader);
{ }
return false;
}
private bool HandleBadMsgNotification(ulong messageId, int sequence, BinaryReader messageReader) return false;
{ }
uint code = messageReader.ReadUInt32();
ulong requestId = messageReader.ReadUInt64();
int requestSequence = messageReader.ReadInt32();
int errorCode = messageReader.ReadInt32();
switch (errorCode) private bool HandleMsgDetailedInfo(ulong messageId, int sequence, BinaryReader messageReader)
{ {
case 16: return false;
throw new InvalidOperationException(" msg_id too low (most likely, client time is wrong; it would be worthwhile to synchronize it using msg_id notifications and re-send the original message with the “correct” msg_id or wrap it in a container with a new msg_id if the original message had waited too long on the client to be transmitted)"); }
case 17:
throw new InvalidOperationException(" msg_id too high (similar to the previous case, the client time has to be synchronized, and the message re-sent with the correct msg_id)");
case 18:
throw new InvalidOperationException("incorrect two lower order msg_id bits (the server expects client message msg_id to be divisible by 4)");
case 19:
throw new InvalidOperationException("container msg_id is the same as msg_id of a previously received message (this must never happen)");
case 20:
throw new InvalidOperationException("message too old, and it cannot be verified whether the server has received a message with this msg_id or not");
case 32:
throw new InvalidOperationException("msg_seqno too low (the server has already received a message with a lower msg_id but with either a higher or an equal and odd seqno)");
case 33:
throw new InvalidOperationException(" msg_seqno too high (similarly, there is a message with a higher msg_id but with either a lower or an equal and odd seqno)");
case 34:
throw new InvalidOperationException("an even msg_seqno expected (irrelevant message), but odd received");
case 35:
throw new InvalidOperationException("odd msg_seqno expected (relevant message), but even received");
case 48:
throw new InvalidOperationException("incorrect server salt (in this case, the bad_server_salt response is received with the correct salt, and the message is to be re-sent with it)");
case 64:
throw new InvalidOperationException("invalid container");
} private bool HandleBadMsgNotification(ulong messageId, int sequence, BinaryReader messageReader)
throw new NotImplementedException("This should never happens"); {
/* uint code = messageReader.ReadUInt32();
ulong requestId = messageReader.ReadUInt64();
int requestSequence = messageReader.ReadInt32();
int errorCode = messageReader.ReadInt32();
switch (errorCode)
{
case 16:
throw new InvalidOperationException(" msg_id too low (most likely, client time is wrong; it would be worthwhile to synchronize it using msg_id notifications and re-send the original message with the “correct” msg_id or wrap it in a container with a new msg_id if the original message had waited too long on the client to be transmitted)");
case 17:
throw new InvalidOperationException(" msg_id too high (similar to the previous case, the client time has to be synchronized, and the message re-sent with the correct msg_id)");
case 18:
throw new InvalidOperationException("incorrect two lower order msg_id bits (the server expects client message msg_id to be divisible by 4)");
case 19:
throw new InvalidOperationException("container msg_id is the same as msg_id of a previously received message (this must never happen)");
case 20:
throw new InvalidOperationException("message too old, and it cannot be verified whether the server has received a message with this msg_id or not");
case 32:
throw new InvalidOperationException("msg_seqno too low (the server has already received a message with a lower msg_id but with either a higher or an equal and odd seqno)");
case 33:
throw new InvalidOperationException(" msg_seqno too high (similarly, there is a message with a higher msg_id but with either a lower or an equal and odd seqno)");
case 34:
throw new InvalidOperationException("an even msg_seqno expected (irrelevant message), but odd received");
case 35:
throw new InvalidOperationException("odd msg_seqno expected (relevant message), but even received");
case 48:
throw new InvalidOperationException("incorrect server salt (in this case, the bad_server_salt response is received with the correct salt, and the message is to be re-sent with it)");
case 64:
throw new InvalidOperationException("invalid container");
}
throw new NotImplementedException("This should never happens");
/*
logger.debug("bad_msg_notification: msgid {0}, seq {1}, errorcode {2}", requestId, requestSequence, logger.debug("bad_msg_notification: msgid {0}, seq {1}, errorcode {2}", requestId, requestSequence,
errorCode); errorCode);
*/ */
/* /*
if (!runningRequests.ContainsKey(requestId)) if (!runningRequests.ContainsKey(requestId))
{ {
logger.debug("bad msg notification on unknown request"); logger.debug("bad msg notification on unknown request");
@ -368,28 +368,28 @@ namespace TLSharp.Core.Network
} }
*/ */
//OnBrokenSessionEvent(); //OnBrokenSessionEvent();
//MTProtoRequest request = runningRequests[requestId]; //MTProtoRequest request = runningRequests[requestId];
//request.OnException(new MTProtoBadMessageException(errorCode)); //request.OnException(new MTProtoBadMessageException(errorCode));
return true; return true;
} }
private bool HandleBadServerSalt(ulong messageId, int sequence, BinaryReader messageReader, MTProtoRequest request) private bool HandleBadServerSalt(ulong messageId, int sequence, BinaryReader messageReader, MTProtoRequest request)
{ {
uint code = messageReader.ReadUInt32(); uint code = messageReader.ReadUInt32();
ulong badMsgId = messageReader.ReadUInt64(); ulong badMsgId = messageReader.ReadUInt64();
int badMsgSeqNo = messageReader.ReadInt32(); int badMsgSeqNo = messageReader.ReadInt32();
int errorCode = messageReader.ReadInt32(); int errorCode = messageReader.ReadInt32();
ulong newSalt = messageReader.ReadUInt64(); ulong newSalt = messageReader.ReadUInt64();
//logger.debug("bad_server_salt: msgid {0}, seq {1}, errorcode {2}, newsalt {3}", badMsgId, badMsgSeqNo, errorCode, newSalt); //logger.debug("bad_server_salt: msgid {0}, seq {1}, errorcode {2}, newsalt {3}", badMsgId, badMsgSeqNo, errorCode, newSalt);
_session.Salt = newSalt; _session.Salt = newSalt;
//resend //resend
Send(request); Send(request);
/* /*
if(!runningRequests.ContainsKey(badMsgId)) { if(!runningRequests.ContainsKey(badMsgId)) {
logger.debug("bad server salt on unknown message"); logger.debug("bad server salt on unknown message");
return true; return true;
@ -397,31 +397,31 @@ namespace TLSharp.Core.Network
*/ */
//MTProtoRequest request = runningRequests[badMsgId]; //MTProtoRequest request = runningRequests[badMsgId];
//request.OnException(new MTProtoBadServerSaltException(salt)); //request.OnException(new MTProtoBadServerSaltException(salt));
return true; return true;
} }
private bool HandleMsgsAck(ulong messageId, int sequence, BinaryReader messageReader) private bool HandleMsgsAck(ulong messageId, int sequence, BinaryReader messageReader)
{ {
return false; return false;
} }
private bool HandleNewSessionCreated(ulong messageId, int sequence, BinaryReader messageReader) private bool HandleNewSessionCreated(ulong messageId, int sequence, BinaryReader messageReader)
{ {
return false; return false;
} }
private bool HandleFutureSalts(ulong messageId, int sequence, BinaryReader messageReader) private bool HandleFutureSalts(ulong messageId, int sequence, BinaryReader messageReader)
{ {
uint code = messageReader.ReadUInt32(); uint code = messageReader.ReadUInt32();
ulong requestId = messageReader.ReadUInt64(); ulong requestId = messageReader.ReadUInt64();
messageReader.BaseStream.Position -= 12; messageReader.BaseStream.Position -= 12;
throw new NotImplementedException("Handle future server salts function isn't implemented."); throw new NotImplementedException("Handle future server salts function isn't implemented.");
/* /*
if (!runningRequests.ContainsKey(requestId)) if (!runningRequests.ContainsKey(requestId))
{ {
logger.info("future salts on unknown request"); logger.info("future salts on unknown request");
@ -429,53 +429,53 @@ namespace TLSharp.Core.Network
} }
*/ */
// MTProtoRequest request = runningRequests[requestId]; // MTProtoRequest request = runningRequests[requestId];
// runningRequests.Remove(requestId); // runningRequests.Remove(requestId);
// request.OnResponse(messageReader); // request.OnResponse(messageReader);
return true; return true;
} }
private bool HandlePong(ulong messageId, int sequence, BinaryReader messageReader) private bool HandlePong(ulong messageId, int sequence, BinaryReader messageReader)
{ {
return false; return false;
} }
private bool HandlePing(ulong messageId, int sequence, BinaryReader messageReader) private bool HandlePing(ulong messageId, int sequence, BinaryReader messageReader)
{ {
return false; return false;
} }
private bool HandleContainer(ulong messageId, int sequence, BinaryReader messageReader, MTProtoRequest request) private bool HandleContainer(ulong messageId, int sequence, BinaryReader messageReader, MTProtoRequest request)
{ {
uint code = messageReader.ReadUInt32(); uint code = messageReader.ReadUInt32();
int size = messageReader.ReadInt32(); int size = messageReader.ReadInt32();
for (int i = 0; i < size; i++) for (int i = 0; i < size; i++)
{ {
ulong innerMessageId = messageReader.ReadUInt64(); ulong innerMessageId = messageReader.ReadUInt64();
int innerSequence = messageReader.ReadInt32(); int innerSequence = messageReader.ReadInt32();
int innerLength = messageReader.ReadInt32(); int innerLength = messageReader.ReadInt32();
long beginPosition = messageReader.BaseStream.Position; long beginPosition = messageReader.BaseStream.Position;
try try
{ {
if (!processMessage(innerMessageId, sequence, messageReader, request)) if (!processMessage(innerMessageId, sequence, messageReader, request))
{ {
messageReader.BaseStream.Position = beginPosition + innerLength; messageReader.BaseStream.Position = beginPosition + innerLength;
} }
} }
catch (Exception e) catch (Exception e)
{ {
// logger.error("failed to process message in contailer: {0}", e); // logger.error("failed to process message in contailer: {0}", e);
messageReader.BaseStream.Position = beginPosition + innerLength; messageReader.BaseStream.Position = beginPosition + innerLength;
} }
} }
return false; return false;
} }
private MemoryStream makeMemory(int len) private MemoryStream makeMemory(int len)
{ {
return new MemoryStream(new byte[len], 0, len, true, true); return new MemoryStream(new byte[len], 0, len, true, true);
} }
} }
} }

View file

@ -4,83 +4,83 @@ using Ionic.Crc;
namespace TLSharp.Core.Network namespace TLSharp.Core.Network
{ {
public class TcpMessage public class TcpMessage
{ {
public int SequneceNumber { get; private set; } public int SequneceNumber { get; private set; }
public byte[] Body { get; private set; } public byte[] Body { get; private set; }
public TcpMessage(int seqNumber, byte[] body) public TcpMessage(int seqNumber, byte[] body)
{ {
if (body == null) if (body == null)
throw new ArgumentNullException(nameof(body)); throw new ArgumentNullException(nameof(body));
SequneceNumber = seqNumber; SequneceNumber = seqNumber;
Body = body; Body = body;
} }
public byte[] Encode() public byte[] Encode()
{ {
using (var memoryStream = new MemoryStream()) using (var memoryStream = new MemoryStream())
{ {
using (var binaryWriter = new BinaryWriter(memoryStream)) using (var binaryWriter = new BinaryWriter(memoryStream))
{ {
// https://core.telegram.org/mtproto#tcp-transport // https://core.telegram.org/mtproto#tcp-transport
/* /*
4 length bytes are added at the front 4 length bytes are added at the front
(to include the length, the sequence number, and CRC32; always divisible by 4) (to include the length, the sequence number, and CRC32; always divisible by 4)
and 4 bytes with the packet sequence number within this TCP connection and 4 bytes with the packet sequence number within this TCP connection
(the first packet sent is numbered 0, the next one 1, etc.), (the first packet sent is numbered 0, the next one 1, etc.),
and 4 CRC32 bytes at the end (length, sequence number, and payload together). and 4 CRC32 bytes at the end (length, sequence number, and payload together).
*/ */
binaryWriter.Write(Body.Length + 12); binaryWriter.Write(Body.Length + 12);
binaryWriter.Write(SequneceNumber); binaryWriter.Write(SequneceNumber);
binaryWriter.Write(Body); binaryWriter.Write(Body);
var crc32 = new CRC32(); var crc32 = new CRC32();
crc32.SlurpBlock(memoryStream.GetBuffer(), 0, 8 + Body.Length); crc32.SlurpBlock(memoryStream.GetBuffer(), 0, 8 + Body.Length);
binaryWriter.Write(crc32.Crc32Result); binaryWriter.Write(crc32.Crc32Result);
var transportPacket = memoryStream.ToArray(); var transportPacket = memoryStream.ToArray();
// Debug.WriteLine("Tcp packet #{0}\n{1}", SequneceNumber, BitConverter.ToString(transportPacket)); // Debug.WriteLine("Tcp packet #{0}\n{1}", SequneceNumber, BitConverter.ToString(transportPacket));
return transportPacket; return transportPacket;
} }
} }
} }
public static TcpMessage Decode(byte[] body) public static TcpMessage Decode(byte[] body)
{ {
if (body == null) if (body == null)
throw new ArgumentNullException(nameof(body)); throw new ArgumentNullException(nameof(body));
if (body.Length < 12) if (body.Length < 12)
throw new InvalidOperationException("Ops, wrong size of input packet"); throw new InvalidOperationException("Ops, wrong size of input packet");
using (var memoryStream = new MemoryStream(body)) using (var memoryStream = new MemoryStream(body))
{ {
using (var binaryReader = new BinaryReader(memoryStream)) using (var binaryReader = new BinaryReader(memoryStream))
{ {
var packetLength = binaryReader.ReadInt32(); var packetLength = binaryReader.ReadInt32();
if (packetLength < 12) if (packetLength < 12)
throw new InvalidOperationException(string.Format("invalid packet length: {0}", packetLength)); throw new InvalidOperationException(string.Format("invalid packet length: {0}", packetLength));
var seq = binaryReader.ReadInt32(); var seq = binaryReader.ReadInt32();
byte[] packet = binaryReader.ReadBytes(packetLength - 12); byte[] packet = binaryReader.ReadBytes(packetLength - 12);
var checksum = (int)binaryReader.ReadInt32(); var checksum = (int)binaryReader.ReadInt32();
var crc32 = new CRC32(); var crc32 = new CRC32();
crc32.SlurpBlock(body, 0, packetLength - 4); crc32.SlurpBlock(body, 0, packetLength - 4);
var validChecksum = crc32.Crc32Result; var validChecksum = crc32.Crc32Result;
if (checksum != validChecksum) if (checksum != validChecksum)
{ {
throw new InvalidOperationException("invalid checksum! skip"); throw new InvalidOperationException("invalid checksum! skip");
} }
return new TcpMessage(seq, packet); return new TcpMessage(seq, packet);
} }
} }
} }
} }
} }

View file

@ -6,30 +6,30 @@ using System.Threading.Tasks;
namespace TLSharp.Core.Network namespace TLSharp.Core.Network
{ {
public class TcpTransport : IDisposable public class TcpTransport : IDisposable
{ {
private readonly TcpClient _tcpClient; private readonly TcpClient _tcpClient;
private int sendCounter = 0; private int sendCounter = 0;
public TcpTransport(string address, int port) public TcpTransport(string address, int port)
{ {
_tcpClient = new TcpClient(); _tcpClient = new TcpClient();
var ipAddress = IPAddress.Parse(address);
_tcpClient.Connect(ipAddress, port);
}
public async Task Send(byte[] packet) var ipAddress = IPAddress.Parse(address);
{ _tcpClient.Connect(ipAddress, port);
if (!_tcpClient.Connected) }
throw new InvalidOperationException("Client not connected to server.");
var tcpMessage = new TcpMessage(sendCounter, packet); public async Task Send(byte[] packet)
{
if (!_tcpClient.Connected)
throw new InvalidOperationException("Client not connected to server.");
var tcpMessage = new TcpMessage(sendCounter, packet);
await _tcpClient.GetStream().WriteAsync(tcpMessage.Encode(), 0, tcpMessage.Encode().Length);
sendCounter++;
}
await _tcpClient.GetStream().WriteAsync(tcpMessage.Encode(), 0, tcpMessage.Encode().Length);
sendCounter++;
}
public async Task<TcpMessage> Receieve() public async Task<TcpMessage> Receieve()
{ {
var stream = _tcpClient.GetStream(); var stream = _tcpClient.GetStream();
@ -59,7 +59,7 @@ namespace TLSharp.Core.Network
while (readBytes != packetLength - 12); while (readBytes != packetLength - 12);
var crcBytes = new byte[4]; var crcBytes = new byte[4];
if(await stream.ReadAsync(crcBytes, 0, 4) != 4) if (await stream.ReadAsync(crcBytes, 0, 4) != 4)
throw new InvalidOperationException("Couldn't read the crc"); throw new InvalidOperationException("Couldn't read the crc");
int checksum = BitConverter.ToInt32(crcBytes, 0); int checksum = BitConverter.ToInt32(crcBytes, 0);
@ -80,10 +80,10 @@ namespace TLSharp.Core.Network
return new TcpMessage(seq, body); return new TcpMessage(seq, body);
} }
public void Dispose() public void Dispose()
{ {
if (_tcpClient.Connected) if (_tcpClient.Connected)
_tcpClient.Close(); _tcpClient.Close();
} }
} }
} }

View file

@ -4,36 +4,36 @@ using System.IO;
namespace TLSharp.Core.Requests namespace TLSharp.Core.Requests
{ {
public class AckRequest : MTProtoRequest public class AckRequest : MTProtoRequest
{ {
private readonly List<ulong> _msgs; private readonly List<ulong> _msgs;
public AckRequest(List<ulong> msgs) public AckRequest(List<ulong> msgs)
{ {
_msgs = msgs; _msgs = msgs;
} }
public override void OnSend(BinaryWriter writer) public override void OnSend(BinaryWriter writer)
{ {
writer.Write(0x62d6b459); // msgs_ack writer.Write(0x62d6b459); // msgs_ack
writer.Write(0x1cb5c415); // Vector writer.Write(0x1cb5c415); // Vector
writer.Write(_msgs.Count); writer.Write(_msgs.Count);
foreach (ulong messageId in _msgs) foreach (ulong messageId in _msgs)
{ {
writer.Write(messageId); writer.Write(messageId);
} }
} }
public override void OnResponse(BinaryReader reader) public override void OnResponse(BinaryReader reader)
{ {
//throw new NotImplementedException(); //throw new NotImplementedException();
} }
public override void OnException(Exception exception) public override void OnException(Exception exception)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public override bool Confirmed => false; public override bool Confirmed => false;
public override bool Responded { get; } public override bool Responded { get; }
} }
} }

View file

@ -12,7 +12,7 @@ namespace TLSharp.Core.Requests
private InputUserContactConstructor _user; private InputUserContactConstructor _user;
private string _title; private string _title;
public Messages_statedMessageConstructor message; public Messages_statedMessageConstructor message;
public AddChatUserRequest(int chatID, InputUserContactConstructor user) public AddChatUserRequest(int chatID, InputUserContactConstructor user)
{ {

View file

@ -4,36 +4,36 @@ using TLSharp.Core.MTProto;
namespace TLSharp.Core.Requests namespace TLSharp.Core.Requests
{ {
public class AuthCheckPhoneRequest : MTProtoRequest public class AuthCheckPhoneRequest : MTProtoRequest
{ {
private string _phoneNumber; private string _phoneNumber;
public bool _phoneRegistered; public bool _phoneRegistered;
private bool _phoneInvited; private bool _phoneInvited;
public AuthCheckPhoneRequest(string phoneNumber) public AuthCheckPhoneRequest(string phoneNumber)
{ {
_phoneNumber = phoneNumber; _phoneNumber = phoneNumber;
} }
public override void OnSend(BinaryWriter writer) public override void OnSend(BinaryWriter writer)
{ {
writer.Write(0x6fe51dfb); writer.Write(0x6fe51dfb);
Serializers.String.write(writer, _phoneNumber); Serializers.String.write(writer, _phoneNumber);
} }
public override void OnResponse(BinaryReader reader) public override void OnResponse(BinaryReader reader)
{ {
var dataCode = reader.ReadUInt32(); // #e300cc3b var dataCode = reader.ReadUInt32(); // #e300cc3b
this._phoneRegistered = reader.ReadUInt32() == 0x997275b5; this._phoneRegistered = reader.ReadUInt32() == 0x997275b5;
this._phoneInvited = reader.ReadUInt32() == 0x997275b5; this._phoneInvited = reader.ReadUInt32() == 0x997275b5;
} }
public override void OnException(Exception exception) public override void OnException(Exception exception)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public override bool Confirmed => true; public override bool Confirmed => true;
public override bool Responded { get; } public override bool Responded { get; }
} }
} }

View file

@ -4,51 +4,51 @@ using TLSharp.Core.MTProto;
namespace TLSharp.Core.Requests namespace TLSharp.Core.Requests
{ {
public class AuthSendCodeRequest : MTProtoRequest public class AuthSendCodeRequest : MTProtoRequest
{ {
private readonly string _phoneNumber; private readonly string _phoneNumber;
private readonly int _smsType; private readonly int _smsType;
private readonly int _apiId; private readonly int _apiId;
private readonly string _apiHash; private readonly string _apiHash;
private readonly string _langCode; private readonly string _langCode;
public bool _phoneRegistered; public bool _phoneRegistered;
public string _phoneCodeHash; public string _phoneCodeHash;
public AuthSendCodeRequest(string phoneNumber, int smsType, int apiId, string apiHash, string langCode) public AuthSendCodeRequest(string phoneNumber, int smsType, int apiId, string apiHash, string langCode)
{ {
_phoneNumber = phoneNumber; _phoneNumber = phoneNumber;
_smsType = smsType; _smsType = smsType;
_apiId = apiId; _apiId = apiId;
_apiHash = apiHash; _apiHash = apiHash;
_langCode = langCode; _langCode = langCode;
} }
public override void OnSend(BinaryWriter writer) public override void OnSend(BinaryWriter writer)
{ {
writer.Write(0x768d5f4d); writer.Write(0x768d5f4d);
Serializers.String.write(writer, _phoneNumber); Serializers.String.write(writer, _phoneNumber);
writer.Write(_smsType); writer.Write(_smsType);
writer.Write(_apiId); writer.Write(_apiId);
Serializers.String.write(writer, _apiHash); Serializers.String.write(writer, _apiHash);
Serializers.String.write(writer, _langCode); Serializers.String.write(writer, _langCode);
} }
public override void OnResponse(BinaryReader reader) public override void OnResponse(BinaryReader reader)
{ {
var boolTrue = 0x997275b5; var boolTrue = 0x997275b5;
var dataCode = reader.ReadUInt32(); // 0x2215bcbd var dataCode = reader.ReadUInt32(); // 0x2215bcbd
_phoneRegistered = reader.ReadUInt32() == boolTrue; _phoneRegistered = reader.ReadUInt32() == boolTrue;
_phoneCodeHash = Serializers.String.read(reader); _phoneCodeHash = Serializers.String.read(reader);
var sendCodeTimeout = reader.ReadInt32(); var sendCodeTimeout = reader.ReadInt32();
var isPassword = reader.ReadUInt32() == boolTrue; var isPassword = reader.ReadUInt32() == boolTrue;
} }
public override void OnException(Exception exception) public override void OnException(Exception exception)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public override bool Confirmed => true; public override bool Confirmed => true;
public override bool Responded { get; } public override bool Responded { get; }
} }
} }

View file

@ -4,42 +4,42 @@ using TLSharp.Core.MTProto;
namespace TLSharp.Core.Requests namespace TLSharp.Core.Requests
{ {
public class AuthSignInRequest : MTProtoRequest public class AuthSignInRequest : MTProtoRequest
{ {
private readonly string _phoneNumber; private readonly string _phoneNumber;
private readonly string _phoneCodeHash; private readonly string _phoneCodeHash;
private readonly string _code; private readonly string _code;
public User user; public User user;
public int SessionExpires; public int SessionExpires;
public AuthSignInRequest(string phoneNumber, string phoneCodeHash, string code) public AuthSignInRequest(string phoneNumber, string phoneCodeHash, string code)
{ {
_phoneNumber = phoneNumber; _phoneNumber = phoneNumber;
_phoneCodeHash = phoneCodeHash; _phoneCodeHash = phoneCodeHash;
_code = code; _code = code;
} }
public override void OnSend(BinaryWriter writer) public override void OnSend(BinaryWriter writer)
{ {
writer.Write(0xbcd51581); writer.Write(0xbcd51581);
Serializers.String.write(writer, _phoneNumber); Serializers.String.write(writer, _phoneNumber);
Serializers.String.write(writer, _phoneCodeHash); Serializers.String.write(writer, _phoneCodeHash);
Serializers.String.write(writer, _code); Serializers.String.write(writer, _code);
} }
public override void OnResponse(BinaryReader reader) public override void OnResponse(BinaryReader reader)
{ {
var dataCode = reader.ReadUInt32(); //0xf6b673a4 var dataCode = reader.ReadUInt32(); //0xf6b673a4
var expires = reader.ReadInt32(); var expires = reader.ReadInt32();
user = TL.Parse<User>(reader); user = TL.Parse<User>(reader);
} }
public override void OnException(Exception exception) public override void OnException(Exception exception)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public override bool Confirmed => true; public override bool Confirmed => true;
public override bool Responded { get; } public override bool Responded { get; }
} }
} }

View file

@ -12,7 +12,7 @@ namespace TLSharp.Core.Requests
private InputUserContactConstructor _user; private InputUserContactConstructor _user;
private string _title; private string _title;
public Messages_statedMessageConstructor message; public Messages_statedMessageConstructor message;
public DeleteChatUserRequest(int chatID, InputUserContactConstructor user) public DeleteChatUserRequest(int chatID, InputUserContactConstructor user)
{ {

View file

@ -22,7 +22,7 @@ namespace TLSharp.Core.Requests
_max_id = max_id; _max_id = max_id;
_limit = limit; _limit = limit;
} }
public override void OnSend(BinaryWriter writer) public override void OnSend(BinaryWriter writer)
{ {
writer.Write(0xeccf1df6); writer.Write(0xeccf1df6);
@ -30,7 +30,7 @@ namespace TLSharp.Core.Requests
writer.Write(_max_id); writer.Write(_max_id);
writer.Write(_limit); writer.Write(_limit);
} }
public override void OnResponse(BinaryReader reader) public override void OnResponse(BinaryReader reader)
{ {
bool dialogsSlice = reader.ReadUInt32() == 0x71e094f3; // else dialogs#15ba6c40 bool dialogsSlice = reader.ReadUInt32() == 0x71e094f3; // else dialogs#15ba6c40

View file

@ -28,11 +28,11 @@ namespace TLSharp.Core.Requests
writer.Write(_offset); writer.Write(_offset);
writer.Write(_limit); writer.Write(_limit);
} }
public override void OnResponse(BinaryReader reader) public override void OnResponse(BinaryReader reader)
{ {
var code = reader.ReadUInt32(); // upload.file#96a18d5 var code = reader.ReadUInt32(); // upload.file#96a18d5
type = TL.Parse<storage_FileType>(reader); type = TL.Parse<storage_FileType>(reader);
mtime = reader.ReadInt32(); mtime = reader.ReadInt32();
bytes = reader.ReadBytes(_limit); bytes = reader.ReadBytes(_limit);

View file

@ -5,59 +5,59 @@ using TLSharp.Core.MTProto;
namespace TLSharp.Core.Requests namespace TLSharp.Core.Requests
{ {
class GetHistoryRequest : MTProtoRequest class GetHistoryRequest : MTProtoRequest
{ {
InputPeer _peer; InputPeer _peer;
int _offset; int _offset;
int _max_id; int _max_id;
int _limit; int _limit;
public List<Message> messages; public List<Message> messages;
public List<Chat> chats; public List<Chat> chats;
public List<User> users; public List<User> users;
public GetHistoryRequest(InputPeer peer, int offset, int max_id, int limit) public GetHistoryRequest(InputPeer peer, int offset, int max_id, int limit)
{ {
_peer = peer; _peer = peer;
_offset = offset; _offset = offset;
_max_id = max_id; _max_id = max_id;
_limit = limit; _limit = limit;
} }
public override void OnSend(BinaryWriter writer) public override void OnSend(BinaryWriter writer)
{ {
writer.Write(0x92a1df2f); writer.Write(0x92a1df2f);
_peer.Write(writer); _peer.Write(writer);
writer.Write(_offset); writer.Write(_offset);
writer.Write(_max_id); writer.Write(_max_id);
writer.Write(_limit); writer.Write(_limit);
} }
public override void OnResponse(BinaryReader reader) public override void OnResponse(BinaryReader reader)
{ {
bool messagesSlice = reader.ReadUInt32() == 0xb446ae3; // else messages#8c718e87 bool messagesSlice = reader.ReadUInt32() == 0xb446ae3; // else messages#8c718e87
if (messagesSlice) reader.ReadInt32(); // count if (messagesSlice) reader.ReadInt32(); // count
// messages // messages
var result = reader.ReadUInt32(); // vector#1cb5c415 var result = reader.ReadUInt32(); // vector#1cb5c415
int messages_len = reader.ReadInt32(); int messages_len = reader.ReadInt32();
messages = new List<Message>(messages_len); messages = new List<Message>(messages_len);
for (var i = 0; i < messages_len; i++) for (var i = 0; i < messages_len; i++)
{ {
var msgEl = TL.Parse<Message>(reader); var msgEl = TL.Parse<Message>(reader);
messages.Add(msgEl); messages.Add(msgEl);
} }
// chats // chats
reader.ReadUInt32(); reader.ReadUInt32();
int chats_len = reader.ReadInt32(); int chats_len = reader.ReadInt32();
chats = new List<Chat>(chats_len); chats = new List<Chat>(chats_len);
for (int i = 0; i < chats_len; i++) for (int i = 0; i < chats_len; i++)
chats.Add(TL.Parse<Chat>(reader)); chats.Add(TL.Parse<Chat>(reader));
/* /*
// users // users
reader.ReadUInt32(); reader.ReadUInt32();
int users_len = reader.ReadInt32(); int users_len = reader.ReadInt32();
@ -65,14 +65,14 @@ namespace TLSharp.Core.Requests
for (int i = 0; i < users_len; i++) for (int i = 0; i < users_len; i++)
users.Add(TL.Parse<User>(reader)); users.Add(TL.Parse<User>(reader));
*/ */
} }
public override void OnException(Exception exception) public override void OnException(Exception exception)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public override bool Confirmed => true; public override bool Confirmed => true;
public override bool Responded { get; } public override bool Responded { get; }
} }
} }

View file

@ -4,33 +4,33 @@ using TLSharp.Core.MTProto;
namespace TLSharp.Core.Requests namespace TLSharp.Core.Requests
{ {
public class ImportByUserName : MTProtoRequest public class ImportByUserName : MTProtoRequest
{ {
private readonly string _userName; private readonly string _userName;
public int id { get; private set; } public int id { get; private set; }
public ImportByUserName(string userName) public ImportByUserName(string userName)
{ {
_userName = userName; _userName = userName;
} }
public override void OnSend(BinaryWriter writer) public override void OnSend(BinaryWriter writer)
{ {
writer.Write(0xBF0131C); writer.Write(0xBF0131C);
Serializers.String.write(writer, _userName); Serializers.String.write(writer, _userName);
} }
public override void OnResponse(BinaryReader reader) public override void OnResponse(BinaryReader reader)
{ {
var code = reader.ReadUInt32(); var code = reader.ReadUInt32();
id = reader.ReadInt32(); id = reader.ReadInt32();
} }
public override void OnException(Exception exception) public override void OnException(Exception exception)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public override bool Confirmed => true; public override bool Confirmed => true;
public override bool Responded { get; } public override bool Responded { get; }
} }
} }

View file

@ -5,58 +5,58 @@ using TLSharp.Core.MTProto;
namespace TLSharp.Core.Requests namespace TLSharp.Core.Requests
{ {
public class ImportContactRequest : MTProtoRequest public class ImportContactRequest : MTProtoRequest
{ {
private InputContact Contact { get; set; } private InputContact Contact { get; set; }
private bool Replace { get; set; } private bool Replace { get; set; }
public List<ImportedContact> imported; public List<ImportedContact> imported;
public List<User> users; public List<User> users;
public ImportContactRequest(InputContact contact, bool shouldReplace = true) public ImportContactRequest(InputContact contact, bool shouldReplace = true)
{ {
Contact = contact; Contact = contact;
Replace = shouldReplace; Replace = shouldReplace;
} }
public override void OnSend(BinaryWriter writer)
{
writer.Write(0xda30b32d);
writer.Write(0x1cb5c415);
writer.Write(1);
Contact.Write(writer);
writer.Write(Replace ? 0x997275b5 : 0xbc799737);
}
public override void OnResponse(BinaryReader reader) public override void OnSend(BinaryWriter writer)
{ {
var code = reader.ReadUInt32(); writer.Write(0xda30b32d);
var result = reader.ReadInt32(); // vector code writer.Write(0x1cb5c415);
int imported_len = reader.ReadInt32(); writer.Write(1);
this.imported = new List<ImportedContact>(imported_len); Contact.Write(writer);
for (int imported_index = 0; imported_index < imported_len; imported_index++) writer.Write(Replace ? 0x997275b5 : 0xbc799737);
{ }
ImportedContact imported_element;
imported_element = TL.Parse<ImportedContact>(reader);
this.imported.Add(imported_element);
}
reader.ReadInt32(); // vector code
int users_len = reader.ReadInt32();
this.users = new List<User>(users_len);
for (int users_index = 0; users_index < users_len; users_index++)
{
User users_element;
users_element = TL.Parse<User>(reader);
this.users.Add(users_element);
}
}
public override void OnException(Exception exception) public override void OnResponse(BinaryReader reader)
{ {
throw new NotImplementedException(); var code = reader.ReadUInt32();
} var result = reader.ReadInt32(); // vector code
int imported_len = reader.ReadInt32();
this.imported = new List<ImportedContact>(imported_len);
for (int imported_index = 0; imported_index < imported_len; imported_index++)
{
ImportedContact imported_element;
imported_element = TL.Parse<ImportedContact>(reader);
this.imported.Add(imported_element);
}
reader.ReadInt32(); // vector code
int users_len = reader.ReadInt32();
this.users = new List<User>(users_len);
for (int users_index = 0; users_index < users_len; users_index++)
{
User users_element;
users_element = TL.Parse<User>(reader);
this.users.Add(users_element);
}
}
public override bool Confirmed => true; public override void OnException(Exception exception)
public override bool Responded { get; } {
} throw new NotImplementedException();
}
public override bool Confirmed => true;
public override bool Responded { get; }
}
} }

View file

@ -4,53 +4,53 @@ using TLSharp.Core.MTProto;
namespace TLSharp.Core.Requests namespace TLSharp.Core.Requests
{ {
public class InitConnectionRequest : MTProtoRequest public class InitConnectionRequest : MTProtoRequest
{ {
private int _apiId; private int _apiId;
public ConfigConstructor ConfigConstructor { get; set; } public ConfigConstructor ConfigConstructor { get; set; }
public InitConnectionRequest(int apiId) public InitConnectionRequest(int apiId)
{ {
_apiId = apiId; _apiId = apiId;
} }
public override void OnSend(BinaryWriter writer) public override void OnSend(BinaryWriter writer)
{ {
writer.Write(0xda9b0d0d); writer.Write(0xda9b0d0d);
writer.Write(23);// invokeWithLayer23#1c900537 writer.Write(23);// invokeWithLayer23#1c900537
writer.Write(0x69796de9); // initConnection writer.Write(0x69796de9); // initConnection
writer.Write(_apiId); // api id writer.Write(_apiId); // api id
Serializers.String.write(writer, "WinPhone Emulator"); // device model Serializers.String.write(writer, "WinPhone Emulator"); // device model
Serializers.String.write(writer, "WinPhone 8.0"); // system version Serializers.String.write(writer, "WinPhone 8.0"); // system version
Serializers.String.write(writer, "1.0-SNAPSHOT"); // app version Serializers.String.write(writer, "1.0-SNAPSHOT"); // app version
Serializers.String.write(writer, "en"); // lang code Serializers.String.write(writer, "en"); // lang code
writer.Write(0xc4f9186b); // help.getConfig writer.Write(0xc4f9186b); // help.getConfig
} }
public override void OnResponse(BinaryReader reader) public override void OnResponse(BinaryReader reader)
{ {
uint code = reader.ReadUInt32(); uint code = reader.ReadUInt32();
ConfigConstructor config = new ConfigConstructor(); ConfigConstructor config = new ConfigConstructor();
config.Read(reader); config.Read(reader);
ConfigConstructor = config; ConfigConstructor = config;
} }
public override void OnException(Exception exception) public override void OnException(Exception exception)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public override bool Responded public override bool Responded
{ {
get { return true; } get { return true; }
} }
public override void OnSendSuccess() public override void OnSendSuccess()
{ {
} }
public override bool Confirmed => true; public override bool Confirmed => true;
} }
} }

View file

@ -3,44 +3,44 @@ using System.IO;
namespace TLSharp.Core.Requests namespace TLSharp.Core.Requests
{ {
public abstract class MTProtoRequest public abstract class MTProtoRequest
{ {
public MTProtoRequest() public MTProtoRequest()
{ {
Sended = false; Sended = false;
} }
public long MessageId { get; set; } public long MessageId { get; set; }
public int Sequence { get; set; } public int Sequence { get; set; }
public bool Dirty { get; set; } public bool Dirty { get; set; }
public bool Sended { get; private set; } public bool Sended { get; private set; }
public DateTime SendTime { get; private set; } public DateTime SendTime { get; private set; }
public bool ConfirmReceived { get; set; } public bool ConfirmReceived { get; set; }
public abstract void OnSend(BinaryWriter writer); public abstract void OnSend(BinaryWriter writer);
public abstract void OnResponse(BinaryReader reader); public abstract void OnResponse(BinaryReader reader);
public abstract void OnException(Exception exception); public abstract void OnException(Exception exception);
public abstract bool Confirmed { get; } public abstract bool Confirmed { get; }
public abstract bool Responded { get; } public abstract bool Responded { get; }
public virtual void OnSendSuccess() public virtual void OnSendSuccess()
{ {
SendTime = DateTime.Now; SendTime = DateTime.Now;
Sended = true; Sended = true;
} }
public virtual void OnConfirm() public virtual void OnConfirm()
{ {
ConfirmReceived = true; ConfirmReceived = true;
} }
public bool NeedResend public bool NeedResend
{ {
get get
{ {
return Dirty || (Confirmed && !ConfirmReceived && DateTime.Now - SendTime > TimeSpan.FromSeconds(3)); return Dirty || (Confirmed && !ConfirmReceived && DateTime.Now - SendTime > TimeSpan.FromSeconds(3));
} }
} }
} }
} }

View file

@ -22,7 +22,7 @@ namespace TLSharp.Core.Requests
this.inputPeer = inputPeer; this.inputPeer = inputPeer;
this.inputMedia = inputMedia; this.inputMedia = inputMedia;
} }
public override void OnSend(BinaryWriter writer) public override void OnSend(BinaryWriter writer)
{ {
writer.Write(0xa3c85d76); writer.Write(0xa3c85d76);
@ -34,7 +34,7 @@ namespace TLSharp.Core.Requests
public override void OnResponse(BinaryReader reader) public override void OnResponse(BinaryReader reader)
{ {
} }
public override void OnException(Exception exception) public override void OnException(Exception exception)

View file

@ -5,42 +5,42 @@ using TLSharp.Core.Utils;
namespace TLSharp.Core.Requests namespace TLSharp.Core.Requests
{ {
public class SendMessageRequest : MTProtoRequest public class SendMessageRequest : MTProtoRequest
{ {
private InputPeer _peer; private InputPeer _peer;
private string _message; private string _message;
public SendMessageRequest(InputPeer peer, string message) public SendMessageRequest(InputPeer peer, string message)
{ {
_peer = peer; _peer = peer;
_message = message; _message = message;
} }
public override void OnSend(BinaryWriter writer) public override void OnSend(BinaryWriter writer)
{ {
long random_id = Helpers.GenerateRandomLong(); long random_id = Helpers.GenerateRandomLong();
writer.Write(0x4cde0aab); writer.Write(0x4cde0aab);
_peer.Write(writer); _peer.Write(writer);
Serializers.String.write(writer, _message); Serializers.String.write(writer, _message);
writer.Write(random_id); writer.Write(random_id);
} }
public override void OnResponse(BinaryReader reader) public override void OnResponse(BinaryReader reader)
{ {
var code = reader.ReadUInt32(); var code = reader.ReadUInt32();
var id = reader.ReadInt32(); var id = reader.ReadInt32();
var date = reader.ReadInt32(); var date = reader.ReadInt32();
var pts = reader.ReadInt32(); var pts = reader.ReadInt32();
var seq = reader.ReadInt32(); var seq = reader.ReadInt32();
} }
public override void OnException(Exception exception) public override void OnException(Exception exception)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public override bool Confirmed => true; public override bool Confirmed => true;
public override bool Responded { get; } public override bool Responded { get; }
} }
} }

View file

@ -30,7 +30,7 @@ namespace TLSharp.Core.Requests
if (code != 0xbc799737 && code != 0x997275b5) if (code != 0xbc799737 && code != 0x997275b5)
throw new InvalidOperationException($"Expected Tl Bool type"); throw new InvalidOperationException($"Expected Tl Bool type");
Done = code == 0x997275b5 ? true : false; Done = code == 0x997275b5 ? true : false;
} }

View file

@ -5,194 +5,194 @@ using TLSharp.Core.MTProto.Crypto;
namespace TLSharp.Core namespace TLSharp.Core
{ {
public interface ISessionStore public interface ISessionStore
{ {
void Save(Session session); void Save(Session session);
Session Load(string sessionUserId); Session Load(string sessionUserId);
} }
public class FileSessionStore : ISessionStore public class FileSessionStore : ISessionStore
{ {
public void Save(Session session) public void Save(Session session)
{ {
using (var stream = new FileStream($"{session.SessionUserId}.dat", FileMode.OpenOrCreate)) using (var stream = new FileStream($"{session.SessionUserId}.dat", FileMode.OpenOrCreate))
{ {
var result = session.ToBytes(); var result = session.ToBytes();
stream.Write(result, 0, result.Length); stream.Write(result, 0, result.Length);
} }
} }
public Session Load(string sessionUserId) public Session Load(string sessionUserId)
{ {
using (var stream = new FileStream($"{sessionUserId}.dat", FileMode.Open)) using (var stream = new FileStream($"{sessionUserId}.dat", FileMode.Open))
{ {
var buffer = new byte[2048]; var buffer = new byte[2048];
stream.Read(buffer, 0, 2048); stream.Read(buffer, 0, 2048);
return Session.FromBytes(buffer, this, sessionUserId); return Session.FromBytes(buffer, this, sessionUserId);
} }
} }
} }
public class FakeSessionStore : ISessionStore public class FakeSessionStore : ISessionStore
{ {
public void Save(Session session) public void Save(Session session)
{ {
}
public Session Load(string sessionUserId) }
{
throw new NotImplementedException();
}
}
public class Session public Session Load(string sessionUserId)
{ {
private const string defaultConnectionAddress = "91.108.56.165"; throw new NotImplementedException();
private const int defaultConnectionPort = 443; }
}
public string SessionUserId { get; set; } public class Session
public string ServerAddress { get; set; } {
public int Port { get; set; } private const string defaultConnectionAddress = "91.108.56.165";
public AuthKey AuthKey { get; set; } private const int defaultConnectionPort = 443;
public ulong Id { get; set; }
public int Sequence { get; set; }
public ulong Salt { get; set; }
public int TimeOffset { get; set; }
public long LastMessageId { get; set; }
public int SessionExpires { get; set; }
public User User { get; set; }
private Random random;
private ISessionStore _store; public string SessionUserId { get; set; }
public string ServerAddress { get; set; }
public int Port { get; set; }
public AuthKey AuthKey { get; set; }
public ulong Id { get; set; }
public int Sequence { get; set; }
public ulong Salt { get; set; }
public int TimeOffset { get; set; }
public long LastMessageId { get; set; }
public int SessionExpires { get; set; }
public User User { get; set; }
private Random random;
private Session(ISessionStore store) private ISessionStore _store;
{
random = new Random();
_store = store;
}
public byte[] ToBytes() private Session(ISessionStore store)
{ {
using (var stream = new MemoryStream()) random = new Random();
using (var writer = new BinaryWriter(stream)) _store = store;
{ }
writer.Write(Id);
writer.Write(Sequence);
writer.Write(Salt);
writer.Write(LastMessageId);
writer.Write(TimeOffset);
Serializers.String.write(writer, ServerAddress);
writer.Write(Port);
if (User != null) public byte[] ToBytes()
{ {
writer.Write(1); using (var stream = new MemoryStream())
writer.Write(SessionExpires); using (var writer = new BinaryWriter(stream))
User.Write(writer); {
} writer.Write(Id);
else writer.Write(Sequence);
{ writer.Write(Salt);
writer.Write(0); writer.Write(LastMessageId);
} writer.Write(TimeOffset);
Serializers.String.write(writer, ServerAddress);
writer.Write(Port);
Serializers.Bytes.write(writer, AuthKey.Data); if (User != null)
{
writer.Write(1);
writer.Write(SessionExpires);
User.Write(writer);
}
else
{
writer.Write(0);
}
return stream.ToArray(); Serializers.Bytes.write(writer, AuthKey.Data);
}
}
public static Session FromBytes(byte[] buffer, ISessionStore store, string sessionUserId) return stream.ToArray();
{ }
using (var stream = new MemoryStream(buffer)) }
using (var reader = new BinaryReader(stream))
{
var id = reader.ReadUInt64();
var sequence = reader.ReadInt32();
var salt = reader.ReadUInt64();
var lastMessageId = reader.ReadInt64();
var timeOffset = reader.ReadInt32();
var serverAddress = Serializers.String.read(reader);
var port = reader.ReadInt32();
var isAuthExsist = reader.ReadInt32() == 1; public static Session FromBytes(byte[] buffer, ISessionStore store, string sessionUserId)
int sessionExpires = 0; {
User user = null; using (var stream = new MemoryStream(buffer))
if (isAuthExsist) using (var reader = new BinaryReader(stream))
{ {
sessionExpires = reader.ReadInt32(); var id = reader.ReadUInt64();
user = TL.Parse<User>(reader); var sequence = reader.ReadInt32();
} var salt = reader.ReadUInt64();
var lastMessageId = reader.ReadInt64();
var timeOffset = reader.ReadInt32();
var serverAddress = Serializers.String.read(reader);
var port = reader.ReadInt32();
var authData = Serializers.Bytes.read(reader); var isAuthExsist = reader.ReadInt32() == 1;
int sessionExpires = 0;
User user = null;
if (isAuthExsist)
{
sessionExpires = reader.ReadInt32();
user = TL.Parse<User>(reader);
}
return new Session(store) var authData = Serializers.Bytes.read(reader);
{
AuthKey = new AuthKey(authData),
Id = id,
Salt = salt,
Sequence = sequence,
LastMessageId = lastMessageId,
TimeOffset = timeOffset,
SessionExpires = sessionExpires,
User = user,
SessionUserId = sessionUserId,
ServerAddress = serverAddress,
Port = port
};
}
}
public void Save() return new Session(store)
{ {
_store.Save(this); AuthKey = new AuthKey(authData),
} Id = id,
Salt = salt,
Sequence = sequence,
LastMessageId = lastMessageId,
TimeOffset = timeOffset,
SessionExpires = sessionExpires,
User = user,
SessionUserId = sessionUserId,
ServerAddress = serverAddress,
Port = port
};
}
}
public static Session TryLoadOrCreateNew(ISessionStore store, string sessionUserId) public void Save()
{ {
Session session; _store.Save(this);
}
try public static Session TryLoadOrCreateNew(ISessionStore store, string sessionUserId)
{ {
session = store.Load(sessionUserId); Session session;
}
catch
{
session = new Session(store)
{
Id = GenerateRandomUlong(),
SessionUserId = sessionUserId,
ServerAddress = defaultConnectionAddress,
Port = defaultConnectionPort
};
}
return session; try
} {
session = store.Load(sessionUserId);
}
catch
{
session = new Session(store)
{
Id = GenerateRandomUlong(),
SessionUserId = sessionUserId,
ServerAddress = defaultConnectionAddress,
Port = defaultConnectionPort
};
}
private static ulong GenerateRandomUlong() return session;
{ }
var random = new Random();
ulong rand = (((ulong)random.Next()) << 32) | ((ulong)random.Next());
return rand;
}
public long GetNewMessageId() private static ulong GenerateRandomUlong()
{ {
long time = Convert.ToInt64((DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalMilliseconds); var random = new Random();
long newMessageId = ((time / 1000 + TimeOffset) << 32) | ulong rand = (((ulong)random.Next()) << 32) | ((ulong)random.Next());
((time % 1000) << 22) | return rand;
(random.Next(524288) << 2); // 2^19 }
// [ unix timestamp : 32 bit] [ milliseconds : 10 bit ] [ buffer space : 1 bit ] [ random : 19 bit ] [ msg_id type : 2 bit ] = [ msg_id : 64 bit ]
if (LastMessageId >= newMessageId) public long GetNewMessageId()
{ {
newMessageId = LastMessageId + 4; long time = Convert.ToInt64((DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalMilliseconds);
} long newMessageId = ((time / 1000 + TimeOffset) << 32) |
((time % 1000) << 22) |
(random.Next(524288) << 2); // 2^19
// [ unix timestamp : 32 bit] [ milliseconds : 10 bit ] [ buffer space : 1 bit ] [ random : 19 bit ] [ msg_id type : 2 bit ] = [ msg_id : 64 bit ]
LastMessageId = newMessageId; if (LastMessageId >= newMessageId)
return newMessageId; {
} newMessageId = LastMessageId + 4;
} }
LastMessageId = newMessageId;
return newMessageId;
}
}
} }

View file

@ -13,237 +13,237 @@ using MD5 = System.Security.Cryptography.MD5;
namespace TLSharp.Core namespace TLSharp.Core
{ {
public class TelegramClient public class TelegramClient
{ {
private MtProtoSender _sender; private MtProtoSender _sender;
private AuthKey _key; private AuthKey _key;
private TcpTransport _transport; private TcpTransport _transport;
private string _apiHash = "a2514f96431a228e4b9ee473f6c51945"; private string _apiHash = "a2514f96431a228e4b9ee473f6c51945";
private int _apiId = 19474; private int _apiId = 19474;
private Session _session; private Session _session;
private List<DcOption> dcOptions; private List<DcOption> dcOptions;
public TelegramClient(ISessionStore store, string sessionUserId) public TelegramClient(ISessionStore store, string sessionUserId)
{ {
if (_apiId == 0) if (_apiId == 0)
throw new InvalidOperationException("Your API_ID is invalid. Do a configuration first https://github.com/sochix/TLSharp#quick-configuration"); throw new InvalidOperationException("Your API_ID is invalid. Do a configuration first https://github.com/sochix/TLSharp#quick-configuration");
if (string.IsNullOrEmpty(_apiHash)) if (string.IsNullOrEmpty(_apiHash))
throw new InvalidOperationException("Your API_ID is invalid. Do a configuration first https://github.com/sochix/TLSharp#quick-configuration"); throw new InvalidOperationException("Your API_ID is invalid. Do a configuration first https://github.com/sochix/TLSharp#quick-configuration");
_session = Session.TryLoadOrCreateNew(store, sessionUserId); _session = Session.TryLoadOrCreateNew(store, sessionUserId);
_transport = new TcpTransport(_session.ServerAddress, _session.Port); _transport = new TcpTransport(_session.ServerAddress, _session.Port);
} }
public async Task<bool> Connect(bool reconnect = false)
{
if (_session.AuthKey == null || reconnect)
{
var result = await Authenticator.DoAuthentication(_transport);
_session.AuthKey = result.AuthKey;
_session.TimeOffset = result.TimeOffset;
}
_sender = new MtProtoSender(_transport, _session); public async Task<bool> Connect(bool reconnect = false)
{
if (_session.AuthKey == null || reconnect)
{
var result = await Authenticator.DoAuthentication(_transport);
_session.AuthKey = result.AuthKey;
_session.TimeOffset = result.TimeOffset;
}
if (!reconnect) _sender = new MtProtoSender(_transport, _session);
{
var request = new InitConnectionRequest(_apiId);
await _sender.Send(request); if (!reconnect)
await _sender.Recieve(request); {
var request = new InitConnectionRequest(_apiId);
dcOptions = request.ConfigConstructor.dc_options; await _sender.Send(request);
} await _sender.Recieve(request);
return true; dcOptions = request.ConfigConstructor.dc_options;
} }
private async Task ReconnectToDc(int dcId) return true;
{ }
if (dcOptions == null || !dcOptions.Any())
throw new InvalidOperationException($"Can't reconnect. Establish initial connection first.");
var dc = dcOptions.Cast<DcOptionConstructor>().First(d => d.id == dcId); private async Task ReconnectToDc(int dcId)
{
if (dcOptions == null || !dcOptions.Any())
throw new InvalidOperationException($"Can't reconnect. Establish initial connection first.");
_transport = new TcpTransport(dc.ip_address, dc.port); var dc = dcOptions.Cast<DcOptionConstructor>().First(d => d.id == dcId);
_session.ServerAddress = dc.ip_address;
_session.Port = dc.port;
await Connect(true); _transport = new TcpTransport(dc.ip_address, dc.port);
} _session.ServerAddress = dc.ip_address;
_session.Port = dc.port;
public bool IsUserAuthorized() await Connect(true);
{ }
return _session.User != null;
}
public async Task<bool> IsPhoneRegistered(string phoneNumber) public bool IsUserAuthorized()
{ {
if (_sender == null) return _session.User != null;
throw new InvalidOperationException("Not connected!"); }
var authCheckPhoneRequest = new AuthCheckPhoneRequest(phoneNumber); public async Task<bool> IsPhoneRegistered(string phoneNumber)
await _sender.Send(authCheckPhoneRequest); {
await _sender.Recieve(authCheckPhoneRequest); if (_sender == null)
throw new InvalidOperationException("Not connected!");
return authCheckPhoneRequest._phoneRegistered; var authCheckPhoneRequest = new AuthCheckPhoneRequest(phoneNumber);
} await _sender.Send(authCheckPhoneRequest);
await _sender.Recieve(authCheckPhoneRequest);
public async Task<string> SendCodeRequest(string phoneNumber) return authCheckPhoneRequest._phoneRegistered;
{ }
var completed = false;
AuthSendCodeRequest request = null; public async Task<string> SendCodeRequest(string phoneNumber)
{
var completed = false;
while (!completed) AuthSendCodeRequest request = null;
{
request = new AuthSendCodeRequest(phoneNumber, 5, _apiId, _apiHash, "en");
try
{
await _sender.Send(request); while (!completed)
await _sender.Recieve(request); {
request = new AuthSendCodeRequest(phoneNumber, 5, _apiId, _apiHash, "en");
try
{
completed = true;
}
catch (InvalidOperationException ex)
{
if (ex.Message.StartsWith("Your phone number registered to") && ex.Data["dcId"] != null)
{
await ReconnectToDc((int) ex.Data["dcId"]);
}
else
{
throw;
}
}
}
return request._phoneCodeHash; await _sender.Send(request);
} await _sender.Recieve(request);
public async Task<User> MakeAuth(string phoneNumber, string phoneHash, string code) completed = true;
{ }
var request = new AuthSignInRequest(phoneNumber, phoneHash, code); catch (InvalidOperationException ex)
await _sender.Send(request); {
await _sender.Recieve(request); if (ex.Message.StartsWith("Your phone number registered to") && ex.Data["dcId"] != null)
{
await ReconnectToDc((int)ex.Data["dcId"]);
}
else
{
throw;
}
}
}
_session.SessionExpires = request.SessionExpires; return request._phoneCodeHash;
_session.User = request.user; }
_session.Save(); public async Task<User> MakeAuth(string phoneNumber, string phoneHash, string code)
{
var request = new AuthSignInRequest(phoneNumber, phoneHash, code);
await _sender.Send(request);
await _sender.Recieve(request);
return request.user; _session.SessionExpires = request.SessionExpires;
} _session.User = request.user;
public async Task<InputFile> UploadFile(string name, byte[] data) _session.Save();
{
var partSize = 65536;
var file_id = DateTime.Now.Ticks; return request.user;
}
var partedData = new Dictionary<int, byte[]>(); public async Task<InputFile> UploadFile(string name, byte[] data)
var parts = Convert.ToInt32(Math.Ceiling(Convert.ToDouble(data.Length) / Convert.ToDouble(partSize))); {
var remainBytes = data.Length; var partSize = 65536;
for (int i = 0; i < parts; i++)
{
partedData.Add(i, data
.Skip(i * partSize)
.Take(remainBytes < partSize ? remainBytes : partSize)
.ToArray());
remainBytes -= partSize; var file_id = DateTime.Now.Ticks;
}
for (int i = 0; i < parts; i++) var partedData = new Dictionary<int, byte[]>();
{ var parts = Convert.ToInt32(Math.Ceiling(Convert.ToDouble(data.Length) / Convert.ToDouble(partSize)));
var saveFilePartRequest = new Upload_SaveFilePartRequest(file_id, i, partedData[i]); var remainBytes = data.Length;
await _sender.Send(saveFilePartRequest); for (int i = 0; i < parts; i++)
await _sender.Recieve(saveFilePartRequest); {
partedData.Add(i, data
.Skip(i * partSize)
.Take(remainBytes < partSize ? remainBytes : partSize)
.ToArray());
if (saveFilePartRequest.Done == false) remainBytes -= partSize;
throw new InvalidOperationException($"File part {i} does not uploaded"); }
}
string md5_checksum; for (int i = 0; i < parts; i++)
using (var md5 = MD5.Create()) {
{ var saveFilePartRequest = new Upload_SaveFilePartRequest(file_id, i, partedData[i]);
var hash = md5.ComputeHash(data); await _sender.Send(saveFilePartRequest);
var hashResult = new StringBuilder(hash.Length * 2); await _sender.Recieve(saveFilePartRequest);
for (int i = 0; i < hash.Length; i++) if (saveFilePartRequest.Done == false)
hashResult.Append(hash[i].ToString("x2")); throw new InvalidOperationException($"File part {i} does not uploaded");
}
md5_checksum = hashResult.ToString(); string md5_checksum;
} using (var md5 = MD5.Create())
{
var hash = md5.ComputeHash(data);
var hashResult = new StringBuilder(hash.Length * 2);
var inputFile = new InputFileConstructor(file_id, parts, name, md5_checksum); for (int i = 0; i < hash.Length; i++)
hashResult.Append(hash[i].ToString("x2"));
return inputFile; md5_checksum = hashResult.ToString();
} }
public async Task<bool> SendMediaMessage(int contactId, InputFile file) var inputFile = new InputFileConstructor(file_id, parts, name, md5_checksum);
{
var request = new Message_SendMediaRequest(
new InputPeerContactConstructor(contactId),
new InputMediaUploadedPhotoConstructor(file));
await _sender.Send(request); return inputFile;
await _sender.Recieve(request); }
return true; public async Task<bool> SendMediaMessage(int contactId, InputFile file)
} {
var request = new Message_SendMediaRequest(
new InputPeerContactConstructor(contactId),
new InputMediaUploadedPhotoConstructor(file));
public async Task<int?> ImportContactByPhoneNumber(string phoneNumber) await _sender.Send(request);
{ await _sender.Recieve(request);
if (!validateNumber(phoneNumber))
throw new InvalidOperationException("Invalid phone number. It should be only digit string, from 5 to 20 digits.");
var request = new ImportContactRequest(new InputPhoneContactConstructor(0, phoneNumber, "My Test Name", String.Empty)); return true;
await _sender.Send(request); }
await _sender.Recieve(request);
var importedUser = (ImportedContactConstructor)request.imported.FirstOrDefault(); public async Task<int?> ImportContactByPhoneNumber(string phoneNumber)
{
if (!validateNumber(phoneNumber))
throw new InvalidOperationException("Invalid phone number. It should be only digit string, from 5 to 20 digits.");
return importedUser?.user_id; var request = new ImportContactRequest(new InputPhoneContactConstructor(0, phoneNumber, "My Test Name", String.Empty));
} await _sender.Send(request);
await _sender.Recieve(request);
public async Task<int?> ImportByUserName(string username) var importedUser = (ImportedContactConstructor)request.imported.FirstOrDefault();
{
if (string.IsNullOrEmpty(username))
throw new InvalidOperationException("Username can't be null");
var request = new ImportByUserName(username); return importedUser?.user_id;
await _sender.Send(request); }
await _sender.Recieve(request);
return request.id; public async Task<int?> ImportByUserName(string username)
} {
if (string.IsNullOrEmpty(username))
throw new InvalidOperationException("Username can't be null");
public async Task SendMessage(int id, string message) var request = new ImportByUserName(username);
{ await _sender.Send(request);
var request = new SendMessageRequest(new InputPeerContactConstructor(id), message); await _sender.Recieve(request);
await _sender.Send(request); return request.id;
await _sender.Recieve(request); }
}
public async Task<List<Message>> GetMessagesHistoryForContact(int user_id, int offset, int limit, int max_id = -1) public async Task SendMessage(int id, string message)
{ {
var request = new GetHistoryRequest(new InputPeerContactConstructor(user_id), offset, max_id, limit); var request = new SendMessageRequest(new InputPeerContactConstructor(id), message);
await _sender.Send(request);
await _sender.Recieve(request);
return request.messages; await _sender.Send(request);
} await _sender.Recieve(request);
}
private bool validateNumber(string number) public async Task<List<Message>> GetMessagesHistoryForContact(int user_id, int offset, int limit, int max_id = -1)
{ {
var regex = new Regex("^\\d{7,20}$"); var request = new GetHistoryRequest(new InputPeerContactConstructor(user_id), offset, max_id, limit);
await _sender.Send(request);
await _sender.Recieve(request);
return regex.IsMatch(number); return request.messages;
} }
}
private bool validateNumber(string number)
{
var regex = new Regex("^\\d{7,20}$");
return regex.IsMatch(number);
}
}
} }

View file

@ -4,26 +4,31 @@ using TLSharp.Core.MTProto.Crypto;
namespace TLSharp.Core.Utils namespace TLSharp.Core.Utils
{ {
public class Helpers { public class Helpers
{
private static Random random = new Random(); private static Random random = new Random();
public static ulong GenerateRandomUlong() { public static ulong GenerateRandomUlong()
{
ulong rand = (((ulong)random.Next()) << 32) | ((ulong)random.Next()); ulong rand = (((ulong)random.Next()) << 32) | ((ulong)random.Next());
return rand; return rand;
} }
public static long GenerateRandomLong() { public static long GenerateRandomLong()
{
long rand = (((long)random.Next()) << 32) | ((long)random.Next()); long rand = (((long)random.Next()) << 32) | ((long)random.Next());
return rand; return rand;
} }
public static byte[] GenerateRandomBytes(int num) { public static byte[] GenerateRandomBytes(int num)
{
byte[] data = new byte[num]; byte[] data = new byte[num];
random.NextBytes(data); random.NextBytes(data);
return data; return data;
} }
public static AESKeyData CalcKey(byte[] sharedKey, byte[] msgKey, bool client) { public static AESKeyData CalcKey(byte[] sharedKey, byte[] msgKey, bool client)
{
int x = client ? 0 : 8; int x = client ? 0 : 8;
byte[] buffer = new byte[48]; byte[] buffer = new byte[48];
@ -58,26 +63,32 @@ namespace TLSharp.Core.Utils
return new AESKeyData(key, iv); return new AESKeyData(key, iv);
} }
public static byte[] CalcMsgKey(byte[] data) { public static byte[] CalcMsgKey(byte[] data)
{
byte[] msgKey = new byte[16]; byte[] msgKey = new byte[16];
Array.Copy(sha1(data), 4, msgKey, 0, 16); Array.Copy(sha1(data), 4, msgKey, 0, 16);
return msgKey; return msgKey;
} }
public static byte[] CalcMsgKey(byte[] data, int offset, int limit) { public static byte[] CalcMsgKey(byte[] data, int offset, int limit)
{
byte[] msgKey = new byte[16]; byte[] msgKey = new byte[16];
Array.Copy(sha1(data, offset, limit), 4, msgKey, 0, 16); Array.Copy(sha1(data, offset, limit), 4, msgKey, 0, 16);
return msgKey; return msgKey;
} }
public static byte[] sha1(byte[] data) { public static byte[] sha1(byte[] data)
using (SHA1 sha1 = new SHA1Managed()) { {
using (SHA1 sha1 = new SHA1Managed())
{
return sha1.ComputeHash(data); return sha1.ComputeHash(data);
} }
} }
public static byte[] sha1(byte[] data, int offset, int limit) { public static byte[] sha1(byte[] data, int offset, int limit)
using (SHA1 sha1 = new SHA1Managed()) { {
using (SHA1 sha1 = new SHA1Managed())
{
return sha1.ComputeHash(data, offset, limit); return sha1.ComputeHash(data, offset, limit);
} }
} }

View file

@ -10,188 +10,188 @@ using TLSharp.Core.Network;
namespace TLSharp.Tests namespace TLSharp.Tests
{ {
[TestClass] [TestClass]
public class TLSharpTests public class TLSharpTests
{ {
private string NumberToSendMessage { get; set; } private string NumberToSendMessage { get; set; }
private string NumberToAuthenticate { get; set; } private string NumberToAuthenticate { get; set; }
private string UserNameToSendMessage { get; set; } private string UserNameToSendMessage { get; set; }
[TestInitialize] [TestInitialize]
public void Init() public void Init()
{ {
// Setup your phone numbers in app.config // Setup your phone numbers in app.config
NumberToAuthenticate = ConfigurationManager.AppSettings["numberToAuthenticate"]; NumberToAuthenticate = ConfigurationManager.AppSettings["numberToAuthenticate"];
if (string.IsNullOrEmpty(NumberToAuthenticate)) if (string.IsNullOrEmpty(NumberToAuthenticate))
throw new InvalidOperationException("NumberToAuthenticate is null. Specify number in app.config"); throw new InvalidOperationException("NumberToAuthenticate is null. Specify number in app.config");
NumberToSendMessage = ConfigurationManager.AppSettings["numberToSendMessage"]; NumberToSendMessage = ConfigurationManager.AppSettings["numberToSendMessage"];
if (string.IsNullOrEmpty(NumberToSendMessage)) if (string.IsNullOrEmpty(NumberToSendMessage))
throw new InvalidOperationException("NumberToSendMessage is null. Specify number in app.config"); throw new InvalidOperationException("NumberToSendMessage is null. Specify number in app.config");
UserNameToSendMessage = ConfigurationManager.AppSettings["userNameToSendMessage"]; UserNameToSendMessage = ConfigurationManager.AppSettings["userNameToSendMessage"];
if (string.IsNullOrEmpty(UserNameToSendMessage)) if (string.IsNullOrEmpty(UserNameToSendMessage))
throw new InvalidOperationException("UserNameToSendMessage is null. Specify userName in app.config"); throw new InvalidOperationException("UserNameToSendMessage is null. Specify userName in app.config");
} }
[TestMethod] [TestMethod]
public async Task AuthUser() public async Task AuthUser()
{ {
var store = new FileSessionStore(); var store = new FileSessionStore();
var client = new TelegramClient(store, "session"); var client = new TelegramClient(store, "session");
await client.Connect(); await client.Connect();
var hash = await client.SendCodeRequest(NumberToAuthenticate); var hash = await client.SendCodeRequest(NumberToAuthenticate);
var code = "93463"; // you can change code in debugger var code = "93463"; // you can change code in debugger
var user = await client.MakeAuth(NumberToAuthenticate, hash, code); var user = await client.MakeAuth(NumberToAuthenticate, hash, code);
Assert.IsNotNull(user); Assert.IsNotNull(user);
} }
[TestMethod] [TestMethod]
public async Task CheckPhones() public async Task CheckPhones()
{ {
var store = new FileSessionStore(); var store = new FileSessionStore();
var client = new TelegramClient(store, "session"); var client = new TelegramClient(store, "session");
await client.Connect(); await client.Connect();
var result = await client.IsPhoneRegistered(NumberToAuthenticate); var result = await client.IsPhoneRegistered(NumberToAuthenticate);
Assert.IsTrue(result); Assert.IsTrue(result);
} }
[TestMethod] [TestMethod]
public async Task ImportContactByPhoneNumber() public async Task ImportContactByPhoneNumber()
{ {
// User should be already authenticated! // User should be already authenticated!
var store = new FileSessionStore(); var store = new FileSessionStore();
var client = new TelegramClient(store, "session"); var client = new TelegramClient(store, "session");
await client.Connect();
Assert.IsTrue(client.IsUserAuthorized()); await client.Connect();
var res = await client.ImportContactByPhoneNumber(NumberToSendMessage); Assert.IsTrue(client.IsUserAuthorized());
Assert.IsNotNull(res); var res = await client.ImportContactByPhoneNumber(NumberToSendMessage);
}
[TestMethod] Assert.IsNotNull(res);
public async Task ImportByUserName() }
{
var store = new FileSessionStore();
var client = new TelegramClient(store, "session");
await client.Connect(); [TestMethod]
public async Task ImportByUserName()
{
var store = new FileSessionStore();
var client = new TelegramClient(store, "session");
Assert.IsTrue(client.IsUserAuthorized()); await client.Connect();
var res = await client.ImportByUserName(UserNameToSendMessage); Assert.IsTrue(client.IsUserAuthorized());
Assert.IsNotNull(res); var res = await client.ImportByUserName(UserNameToSendMessage);
}
[TestMethod] Assert.IsNotNull(res);
public async Task ImportByUserNameAndSendMessage() }
{
var store = new FileSessionStore();
var client = new TelegramClient(store, "session");
await client.Connect(); [TestMethod]
public async Task ImportByUserNameAndSendMessage()
{
var store = new FileSessionStore();
var client = new TelegramClient(store, "session");
Assert.IsTrue(client.IsUserAuthorized()); await client.Connect();
var res = await client.ImportByUserName(UserNameToSendMessage); Assert.IsTrue(client.IsUserAuthorized());
Assert.IsNotNull(res); var res = await client.ImportByUserName(UserNameToSendMessage);
await client.SendMessage(res.Value, "Test message from TelegramClient"); Assert.IsNotNull(res);
}
[TestMethod] await client.SendMessage(res.Value, "Test message from TelegramClient");
public async Task ImportContactByPhoneNumberAndSendMessage() }
{
// User should be already authenticated!
var store = new FileSessionStore(); [TestMethod]
var client = new TelegramClient(store, "session"); public async Task ImportContactByPhoneNumberAndSendMessage()
await client.Connect(); {
// User should be already authenticated!
Assert.IsTrue(client.IsUserAuthorized()); var store = new FileSessionStore();
var client = new TelegramClient(store, "session");
await client.Connect();
var res = await client.ImportContactByPhoneNumber(NumberToSendMessage); Assert.IsTrue(client.IsUserAuthorized());
Assert.IsNotNull(res); var res = await client.ImportContactByPhoneNumber(NumberToSendMessage);
await client.SendMessage(res.Value, "Test message from TelegramClient"); Assert.IsNotNull(res);
}
[TestMethod] await client.SendMessage(res.Value, "Test message from TelegramClient");
public async Task GetHistory() }
{
var store = new FileSessionStore();
var client = new TelegramClient(store, "session");
await client.Connect();
Assert.IsTrue(client.IsUserAuthorized()); [TestMethod]
public async Task GetHistory()
{
var store = new FileSessionStore();
var client = new TelegramClient(store, "session");
await client.Connect();
var res = await client.ImportContactByPhoneNumber(NumberToSendMessage); Assert.IsTrue(client.IsUserAuthorized());
Assert.IsNotNull(res); var res = await client.ImportContactByPhoneNumber(NumberToSendMessage);
var hist = await client.GetMessagesHistoryForContact(res.Value, 0, 5); Assert.IsNotNull(res);
Assert.IsNotNull(hist); var hist = await client.GetMessagesHistoryForContact(res.Value, 0, 5);
}
[TestMethod] Assert.IsNotNull(hist);
public async Task UploadAndSendMedia() }
{
var store = new FileSessionStore();
var client = new TelegramClient(store, "session");
await client.Connect();
Assert.IsTrue(client.IsUserAuthorized()); [TestMethod]
public async Task UploadAndSendMedia()
{
var store = new FileSessionStore();
var client = new TelegramClient(store, "session");
await client.Connect();
var res = await client.ImportContactByPhoneNumber(NumberToSendMessage); Assert.IsTrue(client.IsUserAuthorized());
Assert.IsNotNull(res); var res = await client.ImportContactByPhoneNumber(NumberToSendMessage);
const string testFile = "TEST";
var file = File.ReadAllBytes("../../data/cat.jpg"); Assert.IsNotNull(res);
const string testFile = "TEST";
var mediaFile = await client.UploadFile("test_file.jpg", file); var file = File.ReadAllBytes("../../data/cat.jpg");
Assert.IsNotNull(mediaFile); var mediaFile = await client.UploadFile("test_file.jpg", file);
var state = await client.SendMediaMessage(res.Value, mediaFile); Assert.IsNotNull(mediaFile);
Assert.IsTrue(state); var state = await client.SendMediaMessage(res.Value, mediaFile);
}
[TestMethod] Assert.IsTrue(state);
public async Task TestConnection() }
{
var store = new FakeSessionStore();
var client = new TelegramClient(store, "");
Assert.IsTrue(await client.Connect()); [TestMethod]
} public async Task TestConnection()
{
var store = new FakeSessionStore();
var client = new TelegramClient(store, "");
[TestMethod] Assert.IsTrue(await client.Connect());
public async Task AuthenticationWorks() }
{
using (var transport = new TcpTransport("91.108.56.165", 443))
{
var authKey = await Authenticator.DoAuthentication(transport);
Assert.IsNotNull(authKey.AuthKey.Data); [TestMethod]
} public async Task AuthenticationWorks()
} {
} using (var transport = new TcpTransport("91.108.56.165", 443))
{
var authKey = await Authenticator.DoAuthentication(transport);
Assert.IsNotNull(authKey.AuthKey.Data);
}
}
}
} }