private void CheckNonce(Math.BigInteger pNum0, Math.BigInteger pNum1)
 {
     if (pNum0 != pNum1)
     {
         throw new Exception("Invalid nonce");
     }
 }
Example #2
0
    public static PQPair DecomposeToPrimeFactors(Math.BigInteger pNum)
    {
        Math.BigInteger        pOriginalNum = pNum;
        List <Math.BigInteger> pPrimes      = new List <Math.BigInteger> ();

        pNum = pNum / 2;
        for ( ; pNum > 1; pNum--)
        {
            bool pIsPrime = true;
            for (Math.BigInteger i = 2; i < pNum - 1; i++)
            {
                if (pNum % i == 0)
                {
                    pIsPrime = false;
                    break;
                }
            }
            if (pIsPrime)
            {
                for (int q = 0; q < pPrimes.Count; q++)
                {
                    Math.BigInteger pProduct = pPrimes[q] * pNum;
                    if (pProduct == pOriginalNum)
                    {
                        return(new PQPair(pNum, pPrimes[q]));
                    }
                }
                pPrimes.Insert(0, pNum);
            }
        }
        return(null);
    }
Example #3
0
            public StoredPublicKeyList()
            {
                Math.BigInteger pModulus0  = new Math.BigInteger("c150023e2f70db7985ded064759cfecf0af328e69a41daf4d6f01b538135a6f91f8f8b2a0ec9ba9720ce352efcf6c5680ffc424bd634864902de0b4bd6d49f4e580230e3ae97d95c8b19442b3c0a10d8f5633fecedd6926a7f6dab0ddb7d457f9ea81b8465fcd6fffeed114011df91c059caedaf97625f6c96ecc74725556934ef781d866b34f011fce4d835a090196e9a5f0e4449af7eb697ddb9076494ca5f81104a305b6dd27665722c46b60e5df680fb16b210607ef217652e60236c255f6a28315f4083a96791d7214bf64c1df4fd0db1944fb26a2a57031b32eee64ad15a8ba68885cde74a5bfc920f6abf59ba5c75506373e7130f9042da922179251f", 16);
                Math.BigInteger pExponent0 = new Math.BigInteger(10001);
                PublicRSAKey    pKey0      = new PublicRSAKey(pModulus0, pExponent0, 0xc3b42b026ce86b21UL);

                this.Add(pKey0);
            }
Example #4
0
 public static bool IsPrime(Math.BigInteger pPotentialPrime)
 {
     for (Math.BigInteger x = pPotentialPrime / 2; x > 1; x--)
     {
         if (pPotentialPrime % x == 0)
         {
             return(false);
         }
     }
     return(true);
 }
Example #5
0
            public PublicRSAKey(Math.BigInteger pArgModulus, Math.BigInteger pArgExponent, ulong pFingerprint = 0)
            {
                this.pModulus    = pArgModulus;
                this.pExponent   = pArgExponent;
                pServerPublicKey = new RSACryptoServiceProvider();
                RSAParameters pParams = new RSAParameters();

                pParams.Exponent = this.pExponent.getBytes();
                pParams.Modulus  = pModulus.getBytes();
                pServerPublicKey.ImportParameters(pParams);
                if (pFingerprint != 0)
                {
                    this.Fingerprint = pFingerprint;
                }
                else
                {
                    this.CalculateFingerprint();
                }
            }
Example #6
0
 public PQPair(Math.BigInteger pP, Math.BigInteger pQ)
 {
     this.P = pP;
     this.Q = pQ;
 }
 public PQPair(Math.BigInteger pP, Math.BigInteger pQ)
 {
     this.P = pP;
     this.Q = pQ;
 }
    public bool StartDHExchange()
    {
        byte[] pAppIdData = BitConverter.GetBytes((long)0);

        // #1
        byte[] pAuthRequest = new byte[40];
        Helper.SetData(ref pAuthRequest, pAppIdData, 0);         // auth_key_id
        long pUnixStamp = Helper.TimeNowUnix();

        Helper.SetData(ref pAuthRequest, BitConverter.GetBytes(pUnixStamp), 8);       // message_id
        Helper.SetData(ref pAuthRequest, BitConverter.GetBytes((int)20), 16);         // message length
        Helper.SetData(ref pAuthRequest, BitConverter.GetBytes((int)0x60469778), 20);
        byte[]          pClientNonce    = Helper.RandomNum(128).getBytes();
        Math.BigInteger pClientNonceNum = new Math.BigInteger(pClientNonce);
        Helper.SetData(ref pAuthRequest, pClientNonce, 24);         // nonce

        // #2
        byte[] pResponse = pTcpClient.Send(pAuthRequest).DataBodyContent;           // new byte[84];

        long pAuthKeyId  = BitConverter.ToInt64(pResponse, 0);
        long pMessageId  = BitConverter.ToInt64(pResponse, 8);
        int  pMessageLen = BitConverter.ToInt32(pResponse, 16);
        int  pResPQ      = BitConverter.ToInt32(pResponse, 20);

        Math.BigInteger pNonce = new Math.BigInteger(Helper.GetData(pResponse, 24, 16));
        CheckNonce(pClientNonceNum, pNonce);
        byte[]          pServerNonceBytes = Helper.GetData(pResponse, 40, 16);
        Math.BigInteger pServerNonce      = new Math.BigInteger(pServerNonceBytes);
        Math.BigInteger pPq     = new Math.BigInteger(Helper.GetData(pResponse, 57, 8));
        int             pVector = BitConverter.ToInt32(pResponse, 68);
        int             pCount  = BitConverter.ToInt32(pResponse, 72);

        ulong[] pFingerprints = new ulong[pCount];
        for (short x = 0; x < pCount; x++)
        {
            pFingerprints[x] = BitConverter.ToUInt64(pResponse, 76 + x * 8);
        }

        // #3 pq decomposed to primes
        Helper.PQPair   pPqDecomposed = Helper.parsePQ(pPq.getBytes());       //.DecomposeToPrimeFactors (pPq.getBytes());
        Math.BigInteger pBottomLimit  = Helper.GetPow2Number(2047);
        Math.BigInteger pTopLimit     = Helper.GetPow2Number(2048);
        if (!pPqDecomposed.P.isProbablePrime() || !pPqDecomposed.Q.isProbablePrime())
        {
            return(false);
        }

        /*
         * if(!(pBottomLimit < pPqDecomposed.P && pPqDecomposed.P < pTopLimit) || !Helper.IsPrime(pPqDecomposed.P) || !Helper.IsPrime((pPqDecomposed.P - 1) / 2))
         * {
         *      return false;
         * }
         */
        // #4 client presents proof to server
        byte[] pClientProofPlain = new byte[96];
        Helper.SetData(ref pClientProofPlain, BitConverter.GetBytes(0x83c95aec), 0);
        Helper.SetData(ref pClientProofPlain, pPq.getBytes(), 5);              // 1 denoting byte, 3 padding at the end = length 12 (1 + 8 + 3)
        Helper.SetData(ref pClientProofPlain, pPqDecomposed.P.getBytes(), 17); // 1 + 4 (p) + 3
        Helper.SetData(ref pClientProofPlain, pPqDecomposed.Q.getBytes(), 25); // 1 + 4 (q) + 3
        Helper.SetData(ref pClientProofPlain, pClientNonce, 32);
        Helper.SetData(ref pClientProofPlain, pServerNonce.getBytes(), 48);
        byte[] pClientProofNewNonce = Helper.RandomNum(256).getBytes();           // new nonce new_nonce newnonce
        Helper.SetData(ref pClientProofPlain, pClientProofNewNonce, 64);
        byte[] pDataSha1             = Helper.GetSha1(pClientProofPlain);
        byte[] pClientProofEncrypted = new byte[116];
        Helper.SetData(ref pClientProofEncrypted, pDataSha1, 0);         // length 64
        Helper.SetData(ref pClientProofEncrypted, pClientProofPlain, 20);

        MTProto.ServerInfo.PublicRSAKey pServerPublicKey = MTProto.ServerInfo.PublicKeys.GetKeyByFingerprint(pFingerprints[0]);
        if (pServerPublicKey == null)
        {
            throw new Exception("Server public key not found");
        }
        pClientProofEncrypted = pServerPublicKey.Encrypt(pClientProofEncrypted); // length 256

        // Request to Start Diffie-Hellman Key Exchange
        byte[] pHdExchange = new byte[340];
        Helper.SetData(ref pHdExchange, pAppIdData, 0);                                      // auth_key_id
        pUnixStamp = Helper.TimeNowUnix();
        Helper.SetData(ref pHdExchange, BitConverter.GetBytes(pUnixStamp), 8);               // message_id
        Helper.SetData(ref pHdExchange, BitConverter.GetBytes(pHdExchange.Length - 20), 16); // message_length = full length - 20 (for the header)
        Helper.SetData(ref pHdExchange, BitConverter.GetBytes(0xd712e4be), 20);              // req_DH_params
        Helper.SetData(ref pHdExchange, pClientNonce, 24);                                   // 16
        Helper.SetData(ref pHdExchange, pServerNonce.getBytes(), 40);                        // 16

        byte[] pPData = new byte[8];
        pPData[0] = 4;
        Helper.SetData(ref pPData, BitConverter.GetBytes(pPqDecomposed.P.IntValue()), 1);
        Helper.SetData(ref pHdExchange, pPData, 56);         // 1 + 4 (p) + 3

        byte[] pQData = new byte[8];
        pQData[0] = 4;
        Helper.SetData(ref pQData, BitConverter.GetBytes(pPqDecomposed.Q.IntValue()), 1);
        Helper.SetData(ref pHdExchange, pQData, 64); // 1 + 4 (q) + 3
        byte[] pPublicKeyFingerprint = BitConverter.GetBytes(pFingerprints[0]);
        Helper.SetData(ref pHdExchange, pPublicKeyFingerprint, 72);
        Helper.SetData(ref pHdExchange, pClientProofEncrypted, 80);

        // #5 server response (DH OK - d0e8075c, DH FAILED - 79cb045d)
        TTCPClient.ReceivedData pResponse0 = pTcpClient.Send(pHdExchange); // new byte[652];
        byte[] pServerProofResponse        = pResponse0.DataBodyContent;

        // long pResponseBadKey = BitConverter.ToInt64(pServerProofResponse, 0);
        // bool pMayBeBadKey = (pResponseBadKey == -404); // can happen if you use invalid public server key for encyption
        if (pResponse0.DataFullContent.Length != 652)
        {
            return(false);
        }

        int pResponseState = BitConverter.ToInt32(pServerProofResponse, 20);

        if (pResponseState == 0x79cb045d)        // failed
        {
            return(false);
        }

        long pAuthKeyIdS1 = BitConverter.ToInt64(pServerProofResponse, 0);
        long pMessageIdS1 = BitConverter.ToInt64(pServerProofResponse, 8);

        pMessageLen = BitConverter.ToInt32(pServerProofResponse, 16);
        byte[] pClientNonceCheck = Helper.GetData(pServerProofResponse, 24, 16);
        byte[] pServerNonceCheck = Helper.GetData(pServerProofResponse, 40, 16);
        CheckNonce(pClientNonce, pClientNonceCheck);
        CheckNonce(pServerNonce, new Math.BigInteger(pServerNonceCheck));
        byte[] pEncryptedAnswer = Helper.GetData(pServerProofResponse, 56, 596);

        // decrypt answer
        byte[] newNonceHash = Helper.GetData(Helper.GetSha1(pClientProofNewNonce), 0, 16);
        byte[] tmpAesKey    = new byte[1024];
        Helper.SetData(ref tmpAesKey, pClientProofNewNonce, 0);
        Helper.SetData(ref tmpAesKey, pServerNonce.getBytes(), pClientProofNewNonce.Length);
        Helper.SetData(ref tmpAesKey, pClientProofNewNonce, pClientProofNewNonce.Length + pServerNonce.dataLength);
        Helper.SetData(ref tmpAesKey, pClientProofNewNonce, pClientProofNewNonce.Length + pServerNonce.dataLength + pClientProofNewNonce.Length);
        byte[] pSha1ns = new byte[20];
        byte[] pSha1sn = new byte[20];
        byte[] pSha1nn = new byte[20];

        pSha1ns = Helper.GetSha1(Helper.GetData(tmpAesKey, 0, pClientProofNewNonce.Length + pServerNonce.dataLength));
        pSha1sn = Helper.GetSha1(Helper.GetData(tmpAesKey, pClientProofNewNonce.Length, pClientProofNewNonce.Length + pServerNonce.dataLength));
        pSha1nn = Helper.GetSha1(Helper.GetData(tmpAesKey, pClientProofNewNonce.Length + pServerNonce.dataLength, pClientProofNewNonce.Length + pClientProofNewNonce.Length));

        byte[] pAesKey = new byte[32];
        byte[] pAesIV  = new byte[32];

        Helper.SetData(ref pAesKey, pSha1ns, 0);
        Helper.SetData(ref pAesKey, Helper.GetData(pSha1sn, 0, 12), 20);

        Helper.SetData(ref pAesIV, Helper.GetData(pSha1sn, 12, 8), 0);
        Helper.SetData(ref pAesIV, pSha1nn, 8);
        Helper.SetData(ref pAesIV, Helper.GetData(pClientProofNewNonce, 0, 4), 28);

        byte[] pDecryptedAnswer = Helper.Aes256IgeDecrypt(pEncryptedAnswer, pAesKey, pAesIV);
        long   pAuthKeyId2      = BitConverter.ToInt64(pDecryptedAnswer, 0);

        byte[] pOriginalClientNonce = Helper.GetData(pDecryptedAnswer, 4, 16);
        byte[] pOriginalServerNonce = Helper.GetData(pDecryptedAnswer, 20, 16);
        int    pG = BitConverter.ToInt32(pDecryptedAnswer, 36);

        Math.BigInteger pDhPrime         = new Math.BigInteger(Helper.GetData(pDecryptedAnswer, 40, 260));
        Math.BigInteger pGa              = new Math.BigInteger(Helper.GetData(pDecryptedAnswer, 300, 260));
        long            pServerTime      = BitConverter.ToInt32(pDecryptedAnswer, 560);
        bool            pGCyclicSubgroup = (pPqDecomposed.P % 8 == 7 && pG == 2 || pPqDecomposed.P % 3 == 2 && pG == 3 ||
                                            (pPqDecomposed.P % 5 == 1 || pPqDecomposed.P % 5 == 4) && pG == 5 ||
                                            (pPqDecomposed.P % 24 == 19 || pPqDecomposed.P % 24 == 23) && pG == 6 ||
                                            (pPqDecomposed.P % 7 == 3 || pPqDecomposed.P % 7 == 5 || pPqDecomposed.P % 7 == 6) && pG == 7);

        if (pDhPrime != pPqDecomposed.P || !pGCyclicSubgroup)
        {
            return(false);
        }

        // #6 Random number b is computed
        Math.BigInteger pB                = new Math.BigInteger(Helper.RandomData(256));
        Math.BigInteger pGb               = (pG ^ pB) % pDhPrime;
        Math.BigInteger pMinusOnePrime    = pDhPrime - 1;
        Math.BigInteger pBottomPrimeLimit = 2 ^ (2048 - 64);
        Math.BigInteger pTopPrimeLimit    = pDhPrime - pBottomPrimeLimit;
        if (!(pG > 1 && pGa > 1 && pGb > 1 && pG < pMinusOnePrime && pGa < pMinusOnePrime && pGb < pMinusOnePrime &&
              (pGa > pBottomLimit && pGa < pTopPrimeLimit || pGa < pBottomLimit && pGa > pTopPrimeLimit) ||
              (pGb > pBottomLimit && pGb < pTopPrimeLimit || pGb < pBottomLimit && pGb > pTopPrimeLimit)))
        {
            return(false);
        }
        byte[] pClientEncrypedData = new byte[336];
        Helper.SetData(ref pClientEncrypedData, BitConverter.GetBytes((int)0x6643b654), 0);
        Helper.SetData(ref pClientEncrypedData, pClientNonce, 4);
        Helper.SetData(ref pClientEncrypedData, BitConverter.GetBytes(0L), 36);
        Helper.SetData(ref pClientEncrypedData, pGb.getBytes(), 44);

        int pDataWithHashLen = pClientEncrypedData.Length + 20;

        pDataWithHashLen += pClientEncrypedData.Length % 16;         // must be divisible by 16
        byte[] pDataWithHash = new byte[pDataWithHashLen];
        Helper.SetData(ref pDataWithHash, Helper.GetSha1(pClientEncrypedData), 0);
        Helper.SetData(ref pDataWithHash, pClientEncrypedData, 20);
        pClientEncrypedData = Helper.Aes256IgeEncrypt(pDataWithHash, pAesKey, pAesIV);

        // request
        byte[] pEncryptedRequest = new byte[396];
        Helper.SetData(ref pEncryptedRequest, pAppIdData, 0);
        Helper.SetData(ref pEncryptedRequest, BitConverter.GetBytes(Helper.TimeNowUnix()), 8);
        Helper.SetData(ref pEncryptedRequest, BitConverter.GetBytes(pEncryptedRequest.Length), 16);
        Helper.SetData(ref pEncryptedRequest, BitConverter.GetBytes((uint)0xf5045f1f), 20);
        Helper.SetData(ref pEncryptedRequest, pOriginalClientNonce, 24);
        Helper.SetData(ref pEncryptedRequest, pOriginalServerNonce, 40);
        Helper.SetData(ref pEncryptedRequest, pClientEncrypedData, 56);

        // #7 Computing auth_key using formula
        Math.BigInteger pAuthKey = (pGa ^ pB) % pDhPrime;
        this.pAuthorizationKey = pAuthKey;
        // #8 this is done on the server side "The server checks whether there already is another key with the same auth_key_hash and responds in one of the following ways"
        // byte[] pAuthKeyHash = Helper.GetData (Helper.GetSha1 (pAuthKey.getBytes ()), 0, 8);

        // #9 DH key exchange complete
        byte[] pAuthKeyAuxHash = Helper.GetData(Helper.GetSha1(pAuthKey.getBytes()), 0, 8);  // 64 higher-order bits of SHA1(auth_key)
        byte[] pServerResponse = pTcpClient.Send(pEncryptedRequest).DataBodyContent;         // new byte[52];
        int    pGenStatus      = BitConverter.ToInt32(pServerResponse, 0);

        byte[] pFirstClientNonce = Helper.GetData(pServerResponse, 4, 16);
        byte[] pFirstServerNonce = Helper.GetData(pServerResponse, 20, 16);
        CheckNonce(pClientNonce, pFirstClientNonce);
        CheckNonce(pServerNonceBytes, pFirstServerNonce);
        byte[] pNewNonceHash123      = Helper.GetData(pServerResponse, 36, 16);
        byte[] pComputedNewNonceHash = ComputeNewNonceHash(pClientProofNewNonce, 1, pAuthKeyAuxHash);
        if (pGenStatus != 0x3bcbf734 && !Helper.DataEquals(pNewNonceHash123, pComputedNewNonceHash))
        {
            return(false);
        }
        return(true);
    }
    public bool StartDHExchange()
    {
        byte[] pAppIdData = BitConverter.GetBytes((long)0);

        // #1
        byte[] pAuthRequest = new byte[40];
        Helper.SetData(ref pAuthRequest, pAppIdData, 0); // auth_key_id
        long pUnixStamp = Helper.TimeNowUnix ();
        Helper.SetData(ref pAuthRequest, BitConverter.GetBytes(pUnixStamp), 8); // message_id
        Helper.SetData(ref pAuthRequest, BitConverter.GetBytes((int)20), 16); // message length
        Helper.SetData(ref pAuthRequest, BitConverter.GetBytes((int)0x60469778), 20);
        byte[] pClientNonce = Helper.RandomNum (128).getBytes ();
        Math.BigInteger pClientNonceNum = new Math.BigInteger(pClientNonce);
        Helper.SetData(ref pAuthRequest, pClientNonce, 24); // nonce

        // #2
        byte[] pResponse = pTcpClient.Send (pAuthRequest).DataBodyContent;  // new byte[84];

        long pAuthKeyId = BitConverter.ToInt64 (pResponse, 0);
        long pMessageId = BitConverter.ToInt64 (pResponse, 8);
        int pMessageLen = BitConverter.ToInt32 (pResponse, 16);
        int pResPQ = BitConverter.ToInt32 (pResponse, 20);
        Math.BigInteger pNonce = new Math.BigInteger(Helper.GetData (pResponse, 24, 16));
        CheckNonce(pClientNonceNum, pNonce);
        byte[] pServerNonceBytes = Helper.GetData(pResponse, 40, 16);
        Math.BigInteger pServerNonce = new Math.BigInteger(pServerNonceBytes);
        Math.BigInteger pPq = new Math.BigInteger(Helper.GetData (pResponse, 57, 8));
        int pVector = BitConverter.ToInt32 (pResponse, 68);
        int pCount = BitConverter.ToInt32 (pResponse, 72);
        ulong[] pFingerprints = new ulong[pCount];
        for(short x = 0; x < pCount; x++)
        {
            pFingerprints[x] = BitConverter.ToUInt64(pResponse, 76 + x * 8);
        }

        // #3 pq decomposed to primes
        Helper.PQPair pPqDecomposed = Helper.parsePQ(pPq.getBytes()); //.DecomposeToPrimeFactors (pPq.getBytes());
        Math.BigInteger pBottomLimit = Helper.GetPow2Number(2047);
        Math.BigInteger pTopLimit = Helper.GetPow2Number(2048);
        if(!pPqDecomposed.P.isProbablePrime() || !pPqDecomposed.Q.isProbablePrime())
        {
            return false;
        }
        /*
        if(!(pBottomLimit < pPqDecomposed.P && pPqDecomposed.P < pTopLimit) || !Helper.IsPrime(pPqDecomposed.P) || !Helper.IsPrime((pPqDecomposed.P - 1) / 2))
        {
            return false;
        }
         */
        // #4 client presents proof to server
        byte[] pClientProofPlain = new byte[96];
        Helper.SetData(ref pClientProofPlain, BitConverter.GetBytes(0x83c95aec), 0);
        Helper.SetData(ref pClientProofPlain, pPq.getBytes(), 5); // 1 denoting byte, 3 padding at the end = length 12 (1 + 8 + 3)
        Helper.SetData(ref pClientProofPlain, pPqDecomposed.P.getBytes(), 17); // 1 + 4 (p) + 3
        Helper.SetData(ref pClientProofPlain, pPqDecomposed.Q.getBytes(), 25); // 1 + 4 (q) + 3
        Helper.SetData(ref pClientProofPlain, pClientNonce, 32);
        Helper.SetData(ref pClientProofPlain, pServerNonce.getBytes(), 48);
        byte[] pClientProofNewNonce = Helper.RandomNum (256).getBytes (); // new nonce new_nonce newnonce
        Helper.SetData(ref pClientProofPlain, pClientProofNewNonce, 64);
        byte[] pDataSha1 = Helper.GetSha1 (pClientProofPlain);
        byte[] pClientProofEncrypted = new byte[116];
        Helper.SetData(ref pClientProofEncrypted, pDataSha1, 0); // length 64
        Helper.SetData(ref pClientProofEncrypted, pClientProofPlain, 20);

        MTProto.ServerInfo.PublicRSAKey pServerPublicKey = MTProto.ServerInfo.PublicKeys.GetKeyByFingerprint(pFingerprints[0]);
        if(pServerPublicKey == null)
        {
            throw new Exception("Server public key not found");
        }
        pClientProofEncrypted = pServerPublicKey.Encrypt(pClientProofEncrypted); // length 256

        // Request to Start Diffie-Hellman Key Exchange
        byte[] pHdExchange = new byte[340];
        Helper.SetData(ref pHdExchange, pAppIdData, 0); // auth_key_id
        pUnixStamp = Helper.TimeNowUnix();
        Helper.SetData(ref pHdExchange, BitConverter.GetBytes(pUnixStamp), 8); // message_id
        Helper.SetData(ref pHdExchange, BitConverter.GetBytes(pHdExchange.Length - 20), 16); // message_length = full length - 20 (for the header)
        Helper.SetData(ref pHdExchange, BitConverter.GetBytes(0xd712e4be), 20); // req_DH_params
        Helper.SetData(ref pHdExchange, pClientNonce, 24); // 16
        Helper.SetData(ref pHdExchange, pServerNonce.getBytes(), 40); // 16

        byte[] pPData = new byte[8];
        pPData[0] = 4;
        Helper.SetData(ref pPData, BitConverter.GetBytes (pPqDecomposed.P.IntValue()), 1);
        Helper.SetData(ref pHdExchange, pPData, 56); // 1 + 4 (p) + 3

        byte[] pQData = new byte[8];
        pQData[0] = 4;
        Helper.SetData(ref pQData, BitConverter.GetBytes(pPqDecomposed.Q.IntValue()), 1);
        Helper.SetData(ref pHdExchange, pQData, 64); // 1 + 4 (q) + 3
        byte[] pPublicKeyFingerprint = BitConverter.GetBytes(pFingerprints[0]);
        Helper.SetData(ref pHdExchange, pPublicKeyFingerprint, 72);
        Helper.SetData(ref pHdExchange, pClientProofEncrypted, 80);

        // #5 server response (DH OK - d0e8075c, DH FAILED - 79cb045d)
        TTCPClient.ReceivedData pResponse0 = pTcpClient.Send(pHdExchange); // new byte[652];
        byte[] pServerProofResponse = pResponse0.DataBodyContent;

        // long pResponseBadKey = BitConverter.ToInt64(pServerProofResponse, 0);
        // bool pMayBeBadKey = (pResponseBadKey == -404); // can happen if you use invalid public server key for encyption
        if(pResponse0.DataFullContent.Length != 652)
        {
            return false;
        }

        int pResponseState = BitConverter.ToInt32(pServerProofResponse, 20);
        if(pResponseState == 0x79cb045d) // failed
        {
            return false;
        }

        long pAuthKeyIdS1 = BitConverter.ToInt64 (pServerProofResponse, 0);
        long pMessageIdS1 = BitConverter.ToInt64 (pServerProofResponse, 8);
        pMessageLen = BitConverter.ToInt32 (pServerProofResponse, 16);
        byte[] pClientNonceCheck = Helper.GetData (pServerProofResponse, 24, 16);
        byte[] pServerNonceCheck = Helper.GetData (pServerProofResponse, 40, 16);
        CheckNonce(pClientNonce, pClientNonceCheck);
        CheckNonce(pServerNonce, new Math.BigInteger(pServerNonceCheck));
        byte[] pEncryptedAnswer = Helper.GetData(pServerProofResponse, 56, 596);

        // decrypt answer
        byte[] newNonceHash = Helper.GetData(Helper.GetSha1 (pClientProofNewNonce), 0, 16);
        byte[] tmpAesKey = new byte[1024];
        Helper.SetData(ref tmpAesKey, pClientProofNewNonce, 0);
        Helper.SetData(ref tmpAesKey, pServerNonce.getBytes(), pClientProofNewNonce.Length);
        Helper.SetData(ref tmpAesKey, pClientProofNewNonce, pClientProofNewNonce.Length + pServerNonce.dataLength);
        Helper.SetData(ref tmpAesKey, pClientProofNewNonce, pClientProofNewNonce.Length + pServerNonce.dataLength + pClientProofNewNonce.Length);
        byte[] pSha1ns = new byte[20];
        byte[] pSha1sn = new byte[20];
        byte[] pSha1nn = new byte[20];

        pSha1ns = Helper.GetSha1 (Helper.GetData (tmpAesKey, 0, pClientProofNewNonce.Length + pServerNonce.dataLength));
        pSha1sn = Helper.GetSha1 (Helper.GetData (tmpAesKey, pClientProofNewNonce.Length, pClientProofNewNonce.Length + pServerNonce.dataLength));
        pSha1nn = Helper.GetSha1 (Helper.GetData (tmpAesKey, pClientProofNewNonce.Length + pServerNonce.dataLength, pClientProofNewNonce.Length + pClientProofNewNonce.Length));

        byte[] pAesKey = new byte[32];
        byte[] pAesIV = new byte[32];

        Helper.SetData(ref pAesKey, pSha1ns, 0);
        Helper.SetData(ref pAesKey, Helper.GetData (pSha1sn, 0, 12), 20);

        Helper.SetData(ref pAesIV, Helper.GetData (pSha1sn, 12, 8), 0);
        Helper.SetData(ref pAesIV, pSha1nn, 8);
        Helper.SetData(ref pAesIV, Helper.GetData(pClientProofNewNonce, 0, 4), 28);

        byte[] pDecryptedAnswer = Helper.Aes256IgeDecrypt(pEncryptedAnswer, pAesKey, pAesIV);
        long pAuthKeyId2 = BitConverter.ToInt64 (pDecryptedAnswer, 0);
        byte[] pOriginalClientNonce = Helper.GetData(pDecryptedAnswer, 4, 16);
        byte[] pOriginalServerNonce = Helper.GetData (pDecryptedAnswer, 20, 16);
        int pG = BitConverter.ToInt32(pDecryptedAnswer, 36);
        Math.BigInteger pDhPrime = new Math.BigInteger(Helper.GetData(pDecryptedAnswer, 40, 260));
        Math.BigInteger pGa = new Math.BigInteger(Helper.GetData(pDecryptedAnswer, 300, 260));
        long pServerTime = BitConverter.ToInt32 (pDecryptedAnswer, 560);
        bool pGCyclicSubgroup = (pPqDecomposed.P % 8 == 7 && pG == 2 || pPqDecomposed.P % 3 == 2 && pG == 3 ||
                                (pPqDecomposed.P % 5 == 1 || pPqDecomposed.P % 5 == 4) && pG == 5 ||
                                (pPqDecomposed.P % 24 == 19 || pPqDecomposed.P % 24 == 23 ) && pG == 6 ||
                                 (pPqDecomposed.P % 7 == 3 || pPqDecomposed.P % 7 == 5 || pPqDecomposed.P % 7 == 6) && pG == 7);

        if(pDhPrime != pPqDecomposed.P || !pGCyclicSubgroup)
        {
            return false;
        }

        // #6 Random number b is computed
        Math.BigInteger pB = new Math.BigInteger(Helper.RandomData (256));
        Math.BigInteger pGb = (pG ^ pB) % pDhPrime;
        Math.BigInteger pMinusOnePrime = pDhPrime - 1;
        Math.BigInteger pBottomPrimeLimit = 2 ^ (2048 - 64);
        Math.BigInteger pTopPrimeLimit = pDhPrime - pBottomPrimeLimit;
        if(!(pG > 1 && pGa > 1 && pGb > 1 && pG < pMinusOnePrime && pGa < pMinusOnePrime && pGb < pMinusOnePrime &&
             (pGa > pBottomLimit && pGa < pTopPrimeLimit || pGa < pBottomLimit && pGa > pTopPrimeLimit) ||
             (pGb > pBottomLimit && pGb < pTopPrimeLimit || pGb < pBottomLimit && pGb > pTopPrimeLimit)))
        {
            return false;
        }
        byte[] pClientEncrypedData = new byte[336];
        Helper.SetData(ref pClientEncrypedData, BitConverter.GetBytes ((int)0x6643b654), 0);
        Helper.SetData(ref pClientEncrypedData, pClientNonce, 4);
        Helper.SetData(ref pClientEncrypedData, BitConverter.GetBytes(0L), 36);
        Helper.SetData(ref pClientEncrypedData, pGb.getBytes(), 44);

        int pDataWithHashLen = pClientEncrypedData.Length + 20;
        pDataWithHashLen += pClientEncrypedData.Length % 16; // must be divisible by 16
        byte[] pDataWithHash = new byte[pDataWithHashLen];
        Helper.SetData(ref pDataWithHash, Helper.GetSha1 (pClientEncrypedData), 0);
        Helper.SetData(ref pDataWithHash, pClientEncrypedData, 20);
        pClientEncrypedData = Helper.Aes256IgeEncrypt(pDataWithHash, pAesKey, pAesIV);

        // request
        byte[] pEncryptedRequest = new byte[396];
        Helper.SetData(ref pEncryptedRequest, pAppIdData, 0);
        Helper.SetData(ref pEncryptedRequest, BitConverter.GetBytes (Helper.TimeNowUnix ()), 8);
        Helper.SetData(ref pEncryptedRequest, BitConverter.GetBytes(pEncryptedRequest.Length), 16);
        Helper.SetData(ref pEncryptedRequest, BitConverter.GetBytes((uint)0xf5045f1f), 20);
        Helper.SetData(ref pEncryptedRequest, pOriginalClientNonce, 24);
        Helper.SetData(ref pEncryptedRequest, pOriginalServerNonce, 40);
        Helper.SetData(ref pEncryptedRequest, pClientEncrypedData, 56);

        // #7 Computing auth_key using formula
        Math.BigInteger pAuthKey = (pGa ^ pB) % pDhPrime;
        this.pAuthorizationKey = pAuthKey;
        // #8 this is done on the server side "The server checks whether there already is another key with the same auth_key_hash and responds in one of the following ways"
        // byte[] pAuthKeyHash = Helper.GetData (Helper.GetSha1 (pAuthKey.getBytes ()), 0, 8);

        // #9 DH key exchange complete
        byte[] pAuthKeyAuxHash = Helper.GetData (Helper.GetSha1 (pAuthKey.getBytes ()), 0, 8); // 64 higher-order bits of SHA1(auth_key)
        byte[] pServerResponse = pTcpClient.Send(pEncryptedRequest).DataBodyContent; // new byte[52];
        int pGenStatus = BitConverter.ToInt32 (pServerResponse, 0);
        byte[] pFirstClientNonce = Helper.GetData (pServerResponse, 4, 16);
        byte[] pFirstServerNonce = Helper.GetData (pServerResponse, 20, 16);
        CheckNonce(pClientNonce, pFirstClientNonce);
        CheckNonce(pServerNonceBytes, pFirstServerNonce);
        byte[] pNewNonceHash123 = Helper.GetData (pServerResponse, 36, 16);
        byte[] pComputedNewNonceHash = ComputeNewNonceHash(pClientProofNewNonce, 1, pAuthKeyAuxHash);
        if (pGenStatus != 0x3bcbf734 && !Helper.DataEquals(pNewNonceHash123, pComputedNewNonceHash))
        {
            return false;
        }
        return true;
    }