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