Authenticator.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  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. function doAuthentication(transport) {
  8. let sender = MtProtoPlainSender(transport);
  9. // Step 1 sending: PQ request
  10. let nonce = Helpers.generateRandomBytes(16);
  11. let buffer = Buffer.alloc(32);
  12. buffer.writeUInt32LE(0x60469778, 0);
  13. buffer = Buffer.concat([buffer, nonce]);
  14. sender.send(buffer);
  15. // Step 1 response: PQ request
  16. let pq = null;
  17. let serverNonce = null;
  18. let fingerprints = Array();
  19. buffer = sender.receive();
  20. let responseCode = buffer.readUInt32LE(0);
  21. if (responseCode !== 0x05162463) {
  22. throw Error("invalid response code");
  23. }
  24. let nonceFromServer = buffer.read(16, 8);
  25. if (nonce !== nonceFromServer) {
  26. throw Error("Invalid nonce from server");
  27. }
  28. serverNonce = buffer.read(16, 12);
  29. let {pqBytes, newOffset} = Helpers.tgReadByte(buffer, 12);
  30. pq = buffer.readBigInt64BE(newOffset);
  31. newOffset += 8;
  32. let vectorId = buffer.readInt8(newOffset);
  33. newOffset += 1;
  34. if (vectorId !== 0x1cb5c415) {
  35. throw Error("vector error");
  36. }
  37. let fingerprints_count = buffer.readInt8(newOffset);
  38. for (let i = 0; i < fingerprints_count; i++) {
  39. fingerprints.push(buffer.readInt32LE(newOffset));
  40. newOffset += 8;
  41. }
  42. // Step 2 sending: DH Exchange
  43. let newNonce = Helpers.generateRandomBytes(32);
  44. let {p, q} = Factorizator.factorize(pq);
  45. let tempBuffer = Buffer.alloc(8);
  46. tempBuffer.writeUIntLE(0x83c95aec, 0, 8);
  47. let pqInnerData = Buffer.concat([
  48. tempBuffer,
  49. Helpers.tgWriteBytes(getByteArray(pq, false)),
  50. Helpers.tgWriteBytes(getByteArray(Math.min(p, q), false)),
  51. Helpers.tgWriteBytes(getByteArray(Math.max(p, q), false)),
  52. nonce,
  53. serverNonce,
  54. newNonce,
  55. ]);
  56. let cipherText, targetFingerprint;
  57. for (let fingerprint of fingerprints) {
  58. cipherText = RSA.encrypt(getFingerprintText(fingerprint), pqInnerData);
  59. if (cipherText !== undefined) {
  60. targetFingerprint = fingerprint;
  61. break;
  62. }
  63. }
  64. if (cipherText === undefined) {
  65. throw Error("Could not find a valid key for fingerprints");
  66. }
  67. tempBuffer = Buffer.alloc(8);
  68. tempBuffer.writeUIntLE(0xd712e4be, 0, 8);
  69. let reqDhParams = Buffer.concat([
  70. tempBuffer,
  71. nonce,
  72. serverNonce,
  73. Helpers.tgWriteBytes(getByteArray(Math.min(p, q), false)),
  74. Helpers.tgWriteBytes(getByteArray(Math.max(p, q), false)),
  75. targetFingerprint,
  76. Helpers.tgWriteBytes(cipherText)
  77. ]);
  78. sender.send(reqDhParams);
  79. // Step 2 response: DH Exchange
  80. newOffset = 0;
  81. let reader = sender.receive();
  82. responseCode = reader.readInt32LE(newOffset);
  83. newOffset += 4;
  84. if (responseCode === 0x79cb045d) {
  85. throw Error("Server DH params fail: TODO ");
  86. }
  87. if (responseCode !== 0xd0e8075c) {
  88. throw Error("Invalid response code: TODO ");
  89. }
  90. nonceFromServer = reader.readIntLE(newOffset, 16);
  91. newOffset += 16;
  92. if (nonceFromServer !== nonce) {
  93. throw Error("Invalid nonce from server");
  94. }
  95. let serverNonceFromServer = reader.readIntLE(newOffset, 16);
  96. if (serverNonceFromServer !== nonceFromServer) {
  97. throw Error("Invalid server nonce from server");
  98. }
  99. newOffset += 16;
  100. let encryptedAnswer = Helpers.tgReadByte(reader, newOffset).data;
  101. // Step 3 sending: Complete DH Exchange
  102. let {key, iv} = Helpers.generateKeyDataFromNonces(serverNonce, newNonce);
  103. let plainTextAnswer = AES.decryptIge(encryptedAnswer, key, iv);
  104. let g, dhPrime, ga, timeOffset;
  105. let dhInnerData = plainTextAnswer;
  106. newOffset = 20;
  107. let code = dhInnerData.readUInt32BE(newOffset);
  108. if (code !== 0xb5890dba) {
  109. throw Error("Invalid DH Inner Data code:")
  110. }
  111. newOffset += 4;
  112. let nonceFromServer1 = dhInnerData.readIntLE(newOffset, 16);
  113. if (nonceFromServer1 !== nonceFromServer) {
  114. throw Error("Invalid nonce in encrypted answer");
  115. }
  116. newOffset += 16;
  117. let serverNonceFromServer1 = dhInnerData.readIntLE(newOffset, 16);
  118. if (serverNonceFromServer1 !== serverNonce) {
  119. throw Error("Invalid server nonce in encrypted answer");
  120. }
  121. newOffset += 16;
  122. g = dhInnerData.readInt32LE(newOffset);
  123. newOffset += 4;
  124. let temp = Helpers.tgReadByte(dhInnerData, newOffset);
  125. newOffset += temp.offset;
  126. dhPrime = temp.data.readUInt32BE(0);
  127. temp = Helpers.tgReadByte(dhInnerData, newOffset);
  128. newOffset += temp.offset;
  129. ga = temp.data.readUInt32BE(0);
  130. let serverTime = dhInnerData.readInt32LE(newOffset);
  131. timeOffset = serverTime - Math.floor(new Date().getTime() / 1000);
  132. let b = Helpers.generateRandomBytes(2048).readUInt32BE(0);
  133. let gb = (g ** b) % dhPrime;
  134. let gab = (ga * b) % dhPrime;
  135. // Prepare client DH Inner Data
  136. tempBuffer = Buffer.alloc(8);
  137. tempBuffer.writeUIntLE(0x6643b654, 0, 8);
  138. let clientDHInnerData = Buffer.concat([
  139. tempBuffer,
  140. nonce,
  141. serverNonce,
  142. Buffer.alloc(8).fill(0),
  143. Helpers.tgWriteBytes(getByteArray(gb, false)),
  144. ]);
  145. let clientDhInnerData = Buffer.concat([
  146. Helpers.sha1(clientDHInnerData),
  147. clientDHInnerData
  148. ]);
  149. // Encryption
  150. let clientDhInnerDataEncrypted = AES.encryptIge(clientDhInnerData, key, iv);
  151. // Prepare Set client DH params
  152. tempBuffer = Buffer.alloc(8);
  153. tempBuffer.writeUIntLE(0xf5045f1f, 0, 8);
  154. let setClientDhParams = Buffer.concat([
  155. tempBuffer,
  156. nonce,
  157. serverNonce,
  158. Helpers.tgWriteBytes(clientDhInnerDataEncrypted),
  159. ]);
  160. sender.send(setClientDhParams);
  161. // Step 3 response: Complete DH Exchange
  162. reader = sender.receive();
  163. newOffset = 0;
  164. code = reader.readUInt32LE(newOffset);
  165. newOffset += 4;
  166. if (code === 0x3bcbf734) { // DH Gen OK
  167. nonceFromServer = reader.readIntLE(newOffset, 16);
  168. newOffset += 16;
  169. if (nonceFromServer !== nonce) {
  170. throw Error("Invalid nonce from server");
  171. }
  172. serverNonceFromServer = reader.readIntLE(newOffset, 16);
  173. newOffset += 16;
  174. if (serverNonceFromServer !== serverNonce) {
  175. throw Error("Invalid server nonce from server");
  176. }
  177. let newNonceHash1 = reader.readIntLE(newOffset, 16);
  178. let authKey = AuthKey(getByteArray(gab, false));
  179. let newNonceHashCalculated = authKey.calcNewNonceHash(newNonce, 1);
  180. if (newNonceHash1 !== newNonceHashCalculated) {
  181. throw Error("Invalid new nonce hash");
  182. }
  183. return {authKey, timeOffset};
  184. } else if (code === 0x46dc1fb9) {
  185. throw Error("dh_gen_retry");
  186. } else if (code === 0x46dc1fb9) {
  187. throw Error("dh_gen_fail");
  188. } else {
  189. throw Error("DH Gen unknown");
  190. }
  191. }
  192. function rightJustify(string, length, char) {
  193. let fill = [];
  194. while (fill.length + string.length < length) {
  195. fill[fill.length] = char;
  196. }
  197. return fill.join('') + string;
  198. }
  199. /**
  200. * Gets a fingerprint text in 01-23-45-67-89-AB-CD-EF format (no hyphens)
  201. * @param fingerprint {Array}
  202. * @returns {string}
  203. */
  204. function getFingerprintText(fingerprint) {
  205. let res = "";
  206. for (let b of fingerprint) {
  207. res += rightJustify(b.toString(16), 2, '0').toUpperCase();
  208. }
  209. return res;
  210. }
  211. /**
  212. *
  213. * @param integer {number,BigInt}
  214. * @param signed {boolean}
  215. * @returns {number}
  216. */
  217. function getByteArray(integer, signed) {
  218. let bits = integer.toString(2).length;
  219. let byteLength = Math.floor((bits + 8 - 1) / 8);
  220. let buffer = Buffer.alloc(byteLength);
  221. if (signed) {
  222. return buffer.readIntLE(0, byteLength);
  223. } else {
  224. return buffer.readUIntLE(0, byteLength);
  225. }
  226. }
  227. module.exports = doAuthentication;