Helpers.js 5.5 KB

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