Authenticator.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. const AES = require("../crypto/AES");
  2. const AuthKey = require("../crypto/AuthKey");
  3. const Factorizator = require("../crypto/Factorizator");
  4. const RSA = require("../crypto/RSA");
  5. const MtProtoPlainSender = require("./MTProtoPlainSender");
  6. const Helpers = require("../utils/Helpers");
  7. const BigIntBuffer = require("bigint-buffer");
  8. /**
  9. * Executes the authentication process with the Telegram servers.
  10. *@param sender: a connected `MTProtoPlainSender`.
  11. *@return: returns a (authorization key, time offset) tuple.
  12. */
  13. async function doAuthentication(sender) {
  14. // Step 1 sending: PQ Request, endianness doesn't matter since it's random
  15. let nonce = Helpers.generateRandomBytes(16);
  16. let resPQ = await sender.send(ReqPqMultiRequest(nonce));
  17. if (!(resPQ instanceof ResPQ)) {
  18. throw new Error(`Step 1 answer was ${resPQ}`)
  19. }
  20. if (!resPQ.nonce.equals(nonce)) {
  21. throw new SecurityError("Step 1 invalid nonce from server'")
  22. }
  23. pq = BigIntBuffer.toBigIntBE(resPQ.pq);
  24. // Step 2 sending: DH Exchange
  25. let {p, q} = Factorizator.factorize(pq);
  26. p = getByteArray(p);
  27. q = getByteArray(q);
  28. let newNonce = Helpers.generateRandomBytes(32);
  29. let pqInnerData = PQInnerData({
  30. pq: rsa.get_byte_array(pq),
  31. p: p,
  32. q: q,
  33. nonce: resPQ.nonce,
  34. server_nonce: resPQ.server_nonce,
  35. new_nonce: newNonce
  36. }
  37. );
  38. // sha_digest + data + random_bytes
  39. let cipherText = null;
  40. let targetFingerprint = null;
  41. for (let fingerprint of resPQ.serverPublicKeyFingerprints) {
  42. cipherText = RSA.encrypt(getFingerprintText(fingerprint), pqInnerData);
  43. if (cipherText !== null) {
  44. targetFingerprint = fingerprint;
  45. break
  46. }
  47. }
  48. if (cipherText != null) {
  49. throw new SecurityError(
  50. 'Step 2 could not find a valid key for fingerprints');
  51. }
  52. let serverDhParams = await sender.send(ReqDHParamsRequest({
  53. nonce: resPQ.nonce,
  54. server_nonce: resPQ.server_nonce,
  55. p: p, q: q,
  56. public_key_fingerprint: targetFingerprint,
  57. encrypted_data: cipherText
  58. }
  59. ));
  60. if (!(serverDhParams instanceof ServerDHParamsOk || serverDhParams instanceof ServerDHParamsFail)) {
  61. throw new Error(`Step 2.1 answer was ${serverDhParams}`)
  62. }
  63. if (!serverDhParams.nonce.equals(resPQ.nonce)) {
  64. throw new SecurityError('Step 2 invalid nonce from server');
  65. }
  66. if (!serverDhParams.server_nonce.equals(resPQ.server_nonce)) {
  67. throw new SecurityError('Step 2 invalid server nonce from server')
  68. }
  69. if (serverDhParams instanceof ServerDHParamsFail) {
  70. let sh = Helpers.sha1(BigIntBuffer.toBufferLE(newNonce, 32).slice(4, 20));
  71. let nnh = BigIntBuffer.toBigIntLE(sh);
  72. if (serverDhParams.newNonceHash !== nnh) {
  73. throw new SecurityError('Step 2 invalid DH fail nonce from server')
  74. }
  75. }
  76. if (!(serverDhParams instanceof ServerDHParamsOk)) {
  77. console.log(`Step 2.2 answer was ${serverDhParams}`);
  78. }
  79. // Step 3 sending: Complete DH Exchange
  80. let {key, iv} = Helpers.generateKeyDataFromNonces(resPQ.server_nonce, newNonce);
  81. if (serverDhParams.encryptedAnswer.length % 16 !== 0) {
  82. // See PR#453
  83. throw new SecurityError('Step 3 AES block size mismatch')
  84. }
  85. let plainTextAnswer = AES.decryptIge(
  86. serverDhParams.encryptedAnswer, key, iv
  87. );
  88. let reader = new BinaryReader(plainTextAnswer);
  89. reader.read(20); // hash sum
  90. let serverDhInner = reader.tgReadObject();
  91. if (!(serverDhInner instanceof ServerDHInnerData)) {
  92. throw new Error(`Step 3 answer was ${serverDhInner}`)
  93. }
  94. if (!serverDhInner.nonce.equals(resPQ.nonce)) {
  95. throw new SecurityError('Step 3 Invalid nonce in encrypted answer')
  96. }
  97. if (!serverDhInner.serverNonce.equals(resSQ.serverNonce)) {
  98. throw new SecurityError('Step 3 Invalid server nonce in encrypted answer')
  99. }
  100. let dhPrime = BigIntBuffer.toBigIntLE(serverDhInner.dhPrime);
  101. let ga = BigIntBuffer.toBigIntLE(serverDhInner.ga);
  102. let timeOffset = serverDhInner.serverTime - Math.floor(new Date().getTime() / 1000);
  103. let b = BigIntBuffer.toBigIntLE(Helpers.generateRandomBytes(256));
  104. let gb = Helpers.modExp(serverDhInner.g, b, dhPrime);
  105. let gab = Helpers.modExp(ga, b, dhPrime);
  106. // Prepare client DH Inner Data
  107. let clientDhInner = new ClientDHInnerData({
  108. nonce: res_pq.nonce,
  109. server_nonce: res_pq.server_nonce,
  110. retry_id: 0, // TODO Actual retry ID
  111. g_b: getByteArray(gb, false)
  112. }
  113. ).toBytes();
  114. let clientDdhInnerHashed = Buffer.concat([
  115. Helpers.sha1(clientDhInner),
  116. clientDhInner
  117. ]);
  118. // Encryption
  119. let clientDhEncrypted = AES.encryptIge(clientDdhInnerHashed, key, iv);
  120. let dhGen = await sender.send(new SetClientDHParamsRequest({
  121. nonce: resPQ.nonce,
  122. server_nonce: resPQ.server_nonce,
  123. encrypted_data: clientDhEncrypted,
  124. }
  125. ));
  126. let nonceTypes = [DhGenOk, DhGenRetry, DhGenFail];
  127. if (!(dhGen instanceof nonceTypes[0] || dhGen instanceof nonceTypes[1] || dhGen instanceof nonceTypes[2])) {
  128. throw new Error(`Step 3.1 answer was ${dhGen}`)
  129. }
  130. let name = dhGen.constructor.name;
  131. if (!dhGen.nonce.equals(resPQ.nonce)) {
  132. throw new SecurityError(`Step 3 invalid ${name} nonce from server`)
  133. }
  134. if (!dhGen.server_nonce.equals(resPQ.server_nonce)) {
  135. throw new SecurityError(`Step 3 invalid ${name} server nonce from server`)
  136. }
  137. let authKey = new AuthKey(getByteArray(gab));
  138. let nonceNumber = 1 + nonceTypes.indexOf(typeof (dhGen));
  139. let newNonceHash = authKey.calcNewNonceHash(newNonce, nonceNumber);
  140. let dhHash = dhGen[`new_nonce_hash${nonceNumber}`];
  141. if (!dhHash.equals(newNonceHash)) {
  142. throw new SecurityError('Step 3 invalid new nonce hash');
  143. }
  144. if (!(dhGen instanceof DhGenOk)) {
  145. throw new Error(`Step 3.2 answer was ${dhGen}`)
  146. }
  147. return {authKey, timeOffset};
  148. }
  149. function rightJustify(string, length, char) {
  150. let fill = [];
  151. while (fill.length + string.length < length) {
  152. fill[fill.length] = char;
  153. }
  154. return fill.join('') + string;
  155. }
  156. /**
  157. * Gets a fingerprint text in 01-23-45-67-89-AB-CD-EF format (no hyphens)
  158. * @param fingerprint {Array}
  159. * @returns {string}
  160. */
  161. function getFingerprintText(fingerprint) {
  162. return fingerprint.toString("hex");
  163. }
  164. /**
  165. * Gets the arbitrary-length byte array corresponding to the given integer
  166. * @param integer {number,BigInt}
  167. * @param signed {boolean}
  168. * @returns {Buffer}
  169. */
  170. function getByteArray(integer, signed) {
  171. let bits = integer.toString(2).length;
  172. let byteLength = Math.floor((bits + 8 - 1) / 8);
  173. let f;
  174. if (signed) {
  175. f = BigIntBuffer.toBufferBE(BigInt(integer), byteLength);
  176. } else {
  177. f = BigIntBuffer.toBufferBE(BigInt(integer), byteLength);
  178. }
  179. return f;
  180. }
  181. module.exports = doAuthentication;