Authenticator.js 8.5 KB

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