Helpers.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. const crypto = require('crypto');
  2. const fs = require("fs").promises;
  3. class Helpers {
  4. /**
  5. * Generates a random long integer (8 bytes), which is optionally signed
  6. * @returns {number}
  7. */
  8. static generateRandomLong() {
  9. let buf = Buffer.from(this.generateRandomBytes(8)); // 0x12345678 = 305419896
  10. return buf.readUInt32BE(0);
  11. }
  12. /**
  13. * Generates a random bytes array
  14. * @param count
  15. * @returns {Buffer}
  16. */
  17. static generateRandomBytes(count) {
  18. return crypto.randomBytes(count);
  19. }
  20. /**
  21. * Loads the user settings located under `api/`
  22. * @param path
  23. * @returns {Promise<void>}
  24. */
  25. static async loadSettings(path = "../api/settings") {
  26. let settings = {};
  27. let left, right, value_pair;
  28. let data = await fs.readFile(path, 'utf-8');
  29. for (let line of data.toString().split('\n')) {
  30. value_pair = line.split("=");
  31. if (value_pair.length !== 2) {
  32. break;
  33. }
  34. left = value_pair[0].replace(/ \r?\n|\r/g, '');
  35. right = value_pair[1].replace(/ \r?\n|\r/g, '');
  36. if (!isNaN(right)) {
  37. settings[left] = Number.parseInt(right);
  38. } else {
  39. settings[left] = right;
  40. }
  41. }
  42. return settings;
  43. }
  44. /**
  45. * Calculate the key based on Telegram guidelines, specifying whether it's the client or not
  46. * @param shared_key
  47. * @param msg_key
  48. * @param client
  49. * @returns {[*, *]}
  50. */
  51. static calcKey(shared_key, msg_key, client) {
  52. let x = client !== null ? 0 : 8;
  53. let iv, key, sha1a, sha1b, sha1c, sha1d;
  54. sha1a = this.sha1((msg_key + shared_key.slice(x, (x + 32))));
  55. sha1b = this.sha1(((shared_key.slice((x + 32), (x + 48)) + msg_key) + shared_key.slice((x + 48), (x + 64))));
  56. sha1c = this.sha1((shared_key.slice((x + 64), (x + 96)) + msg_key));
  57. sha1d = this.sha1((msg_key + shared_key.slice((x + 96), (x + 128))));
  58. key = ((sha1a.slice(0, 8) + sha1b.slice(8, 20)) + sha1c.slice(4, 16));
  59. iv = (((sha1a.slice(8, 20) + sha1b.slice(0, 8)) + sha1c.slice(16, 20)) + sha1d.slice(0, 8));
  60. return [key, iv];
  61. }
  62. /**
  63. * Calculates the message key from the given data
  64. * @param data
  65. * @returns {Buffer}
  66. */
  67. static calcMsgKey(data) {
  68. return this.sha1(data).slice(4, 20);
  69. }
  70. /**
  71. * Generates the key data corresponding to the given nonces
  72. * @param serverNonce
  73. * @param newNonce
  74. * @returns {{ivBuffer: Buffer, keyBuffer: Buffer}}
  75. */
  76. static generateKeyDataFromNonces(serverNonce, newNonce) {
  77. let hash1 = this.sha1(Buffer.concat([newNonce, serverNonce]));
  78. let hash2 = this.sha1(Buffer.concat([serverNonce, newNonce]));
  79. let hash3 = this.sha1(Buffer.concat([newNonce, newNonce]));
  80. let keyBuffer = Buffer.concat([hash1, hash1.slice(0, 12)]);
  81. let ivBuffer = Buffer.concat([hash2.slice(12, 20), hash3, newNonce.slice(0, 4)]);
  82. return {keyBuffer: keyBuffer, ivBuffer: ivBuffer}
  83. }
  84. /**
  85. * Calculates the SHA1 digest for the given data
  86. * @param data
  87. * @returns {Buffer}
  88. */
  89. static sha1(data) {
  90. let shaSum = crypto.createHash('sha1');
  91. shaSum.update(data);
  92. return shaSum.digest();
  93. }
  94. /**
  95. *
  96. * @param buffer {Buffer}
  97. * @param offset {Number}
  98. * @returns {{data: {Buffer}, offset: {Number}}}
  99. */
  100. static tgReadByte(buffer, offset) {
  101. let firstByte = buffer.readInt8(offset);
  102. offset += 1;
  103. let padding, length;
  104. if (firstByte === 255) {
  105. length = buffer.readInt8(offset) | (buffer.readInt8(offset) << 8) | (buffer.readInt8(offset) << 16);
  106. offset += 1;
  107. padding = length % 4;
  108. } else {
  109. length = firstByte;
  110. padding = (length + 1) % 4;
  111. }
  112. let data = buffer.readInt8(offset);
  113. offset += 1;
  114. if (padding > 0) {
  115. padding = 4 - padding;
  116. offset += padding;
  117. }
  118. return {data, offset}
  119. }
  120. static tgWriteBytes(data) {
  121. let buffer;
  122. let padding;
  123. if (data.length < 254) {
  124. padding = (data.length + 1) % 4;
  125. if (padding !== 0) {
  126. padding = 4 - padding;
  127. }
  128. buffer = Buffer.from([data.length, data]);
  129. } else {
  130. padding = data.length % 4;
  131. if (padding !== 0) {
  132. padding = 4 - padding;
  133. }
  134. buffer = Buffer.concat([Buffer.from([254]),
  135. Buffer.from([data.length % 256]),
  136. Buffer.from([(data.length >> 8) % 256]),
  137. Buffer.from([(data.length >> 16) % 256]),
  138. Buffer.from([data]),
  139. Buffer.from([padding])
  140. ]);
  141. }
  142. return buffer;
  143. }
  144. /**
  145. * returns a random int from min (inclusive) and max (inclusive)
  146. * @param min
  147. * @param max
  148. * @returns {number}
  149. */
  150. static getRandomInt(min, max) {
  151. min = Math.ceil(min);
  152. max = Math.floor(max);
  153. return Math.floor(Math.random() * (max - min + 1)) + min;
  154. }
  155. /**
  156. * Sleeps a specified amount of time
  157. * @param ms time in milliseconds
  158. * @returns {Promise}
  159. */
  160. static sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
  161. }
  162. let l = Buffer.from(0x83c95aec);
  163. console.log(l.length);