private void CheckNonce(Math.BigInteger pNum0, Math.BigInteger pNum1) { if (pNum0 != pNum1) { throw new Exception("Invalid nonce"); } }
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); }
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); }
public static bool IsPrime(Math.BigInteger pPotentialPrime) { for (Math.BigInteger x = pPotentialPrime / 2; x > 1; x--) { if (pPotentialPrime % x == 0) { return(false); } } return(true); }
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(); } }
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; }