TLSharp/TLSharp.Core/Auth/Step3_CompleteDHExchange.cs
2018-02-13 11:00:00 +03:30

209 lines
8.3 KiB
C#

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
{
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;
AESKeyData key = AES.GenerateKeyDataFromNonces(serverNonce, newNonce);
byte[] plaintextAnswer = AES.DecryptAES(key, encryptedAnswer);
// logger.debug("plaintext answer: {0}", BitConverter.ToString(plaintextAnswer));
int g;
BigInteger dhPrime;
BigInteger ga;
using (MemoryStream dhInnerData = new MemoryStream(plaintextAnswer))
{
using (BinaryReader dhInnerDataReader = new BinaryReader(dhInnerData))
{
byte[] hashsum = dhInnerDataReader.ReadBytes(20);
uint code = dhInnerDataReader.ReadUInt32();
if (code != 0xb5890dba)
{
throw new InvalidOperationException($"invalid dh_inner_data code: {code}");
}
// logger.debug("valid code");
byte[] nonceFromServer1 = dhInnerDataReader.ReadBytes(16);
if (!nonceFromServer1.SequenceEqual(nonce))
{
throw new InvalidOperationException("invalid nonce in encrypted answer");
}
// logger.debug("valid nonce");
byte[] serverNonceFromServer1 = dhInnerDataReader.ReadBytes(16);
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));
int serverTime = dhInnerDataReader.ReadInt32();
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);
}
}
BigInteger b = new BigInteger(2048, new Random());
BigInteger gb = BigInteger.ValueOf(g).ModPow(b, dhPrime);
_gab = ga.ModPow(b, dhPrime);
// logger.debug("gab: {0}", gab);
// prepare client dh inner data
byte[] clientDHInnerDataBytes;
using (MemoryStream clientDhInnerData = new MemoryStream())
{
using (BinaryWriter clientDhInnerDataWriter = new BinaryWriter(clientDhInnerData))
{
clientDhInnerDataWriter.Write(0x6643b654); // client_dh_inner_data
clientDhInnerDataWriter.Write(nonce);
clientDhInnerDataWriter.Write(serverNonce);
clientDhInnerDataWriter.Write((long)0); // TODO: retry_id
Serializers.Bytes.write(clientDhInnerDataWriter, gb.ToByteArrayUnsigned());
using (MemoryStream clientDhInnerDataWithHash = new MemoryStream())
{
using (BinaryWriter clientDhInnerDataWithHashWriter = new BinaryWriter(clientDhInnerDataWithHash))
{
using (SHA1 sha1 = new SHA1Managed())
{
clientDhInnerDataWithHashWriter.Write(sha1.ComputeHash(clientDhInnerData.GetBuffer(), 0, (int)clientDhInnerData.Position));
clientDhInnerDataWithHashWriter.Write(clientDhInnerData.GetBuffer(), 0, (int)clientDhInnerData.Position);
clientDHInnerDataBytes = clientDhInnerDataWithHash.ToArray();
}
}
}
}
}
// logger.debug("client dh inner data papared len {0}: {1}", clientDHInnerDataBytes.Length, BitConverter.ToString(clientDHInnerDataBytes).Replace("-", ""));
// encryption
byte[] clientDhInnerDataEncryptedBytes = AES.EncryptAES(key, clientDHInnerDataBytes);
// logger.debug("inner data encrypted {0}: {1}", clientDhInnerDataEncryptedBytes.Length, BitConverter.ToString(clientDhInnerDataEncryptedBytes).Replace("-", ""));
// prepare set_client_dh_params
byte[] setclientDhParamsBytes;
using (MemoryStream setClientDhParams = new MemoryStream())
{
using (BinaryWriter setClientDhParamsWriter = new BinaryWriter(setClientDhParams))
{
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)
{
using (MemoryStream responseStream = new MemoryStream(response))
{
using (BinaryReader responseReader = new BinaryReader(responseStream))
{
uint code = responseReader.ReadUInt32();
if (code == 0x3bcbf734)
{ // dh_gen_ok
//logger.debug("dh_gen_ok");
byte[] nonceFromServer = responseReader.ReadBytes(16);
// TODO
/*
if (!nonceFromServer.SequenceEqual(nonce))
{
logger.error("invalid nonce");
return null;
}
*/
byte[] serverNonceFromServer = responseReader.ReadBytes(16);
// TODO:
/*
if (!serverNonceFromServer.SequenceEqual(serverNonce))
{
logger.error("invalid server nonce");
return null;
}
*/
byte[] newNonceHash1 = responseReader.ReadBytes(16);
//logger.debug("new nonce hash 1: {0}", BitConverter.ToString(newNonceHash1));
AuthKey authKey = new AuthKey(_gab);
byte[] newNonceHashCalculated = authKey.CalcNewNonceHash(newNonce, 1);
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;
return new Step3_Response()
{
AuthKey = authKey,
TimeOffset = timeOffset
};
}
else if (code == 0x46dc1fb9)
{ // dh_gen_retry
throw new NotImplementedException("dh_gen_retry");
}
else if (code == 0xa69dae02)
{
// dh_gen_fail
throw new NotImplementedException("dh_gen_fail");
}
else
{
throw new InvalidOperationException($"dh_gen unknown: {code}");
}
}
}
}
}
}