packer.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "packer.h"
  8. bool BetaChannel = false;
  9. quint64 AlphaVersion = 0;
  10. bool OnlyAlphaKey = false;
  11. const char *PublicKey = "\
  12. -----BEGIN RSA PUBLIC KEY-----\n\
  13. MIGJAoGBAMA4ViQrjkPZ9xj0lrer3r23JvxOnrtE8nI69XLGSr+sRERz9YnUptnU\n\
  14. BZpkIfKaRcl6XzNJiN28cVwO1Ui5JSa814UAiDHzWUqCaXUiUEQ6NmNTneiGx2sQ\n\
  15. +9PKKlb8mmr3BB9A45ZNwLT6G9AK3+qkZLHojeSA+m84/a6GP4svAgMBAAE=\n\
  16. -----END RSA PUBLIC KEY-----\
  17. ";
  18. const char *PublicBetaKey = "\
  19. -----BEGIN RSA PUBLIC KEY-----\n\
  20. MIGJAoGBALWu9GGs0HED7KG7BM73CFZ6o0xufKBRQsdnq3lwA8nFQEvmdu+g/I1j\n\
  21. 0LQ+0IQO7GW4jAgzF/4+soPDb6uHQeNFrlVx1JS9DZGhhjZ5rf65yg11nTCIHZCG\n\
  22. w/CVnbwQOw0g5GBwwFV3r0uTTvy44xx8XXxk+Qknu4eBCsmrAFNnAgMBAAE=\n\
  23. -----END RSA PUBLIC KEY-----\
  24. ";
  25. extern const char *PrivateKey;
  26. extern const char *PrivateBetaKey;
  27. #include "../../../../DesktopPrivate/packer_private.h" // RSA PRIVATE KEYS for update signing
  28. #include "../../../../DesktopPrivate/alpha_private.h" // private key for alpha version file generation
  29. QString countAlphaVersionSignature(quint64 version);
  30. // sha1 hash
  31. typedef unsigned char uchar;
  32. typedef unsigned int uint32;
  33. typedef signed int int32;
  34. namespace{
  35. struct BIODeleter {
  36. void operator()(BIO *value) {
  37. BIO_free(value);
  38. }
  39. };
  40. inline auto makeBIO(const void *buf, int len) {
  41. return std::unique_ptr<BIO, BIODeleter>{
  42. BIO_new_mem_buf(buf, len),
  43. };
  44. }
  45. inline uint32 sha1Shift(uint32 v, uint32 shift) {
  46. return ((v << shift) | (v >> (32 - shift)));
  47. }
  48. void sha1PartHash(uint32 *sha, uint32 *temp) {
  49. uint32 a = sha[0], b = sha[1], c = sha[2], d = sha[3], e = sha[4], round = 0;
  50. #define _shiftswap(f, v) { \
  51. uint32 t = sha1Shift(a, 5) + (f) + e + v + temp[round]; \
  52. e = d; \
  53. d = c; \
  54. c = sha1Shift(b, 30); \
  55. b = a; \
  56. a = t; \
  57. ++round; \
  58. }
  59. #define _shiftshiftswap(f, v) { \
  60. temp[round] = sha1Shift((temp[round - 3] ^ temp[round - 8] ^ temp[round - 14] ^ temp[round - 16]), 1); \
  61. _shiftswap(f, v) \
  62. }
  63. while (round < 16) _shiftswap((b & c) | (~b & d), 0x5a827999)
  64. while (round < 20) _shiftshiftswap((b & c) | (~b & d), 0x5a827999)
  65. while (round < 40) _shiftshiftswap(b ^ c ^ d, 0x6ed9eba1)
  66. while (round < 60) _shiftshiftswap((b & c) | (b & d) | (c & d), 0x8f1bbcdc)
  67. while (round < 80) _shiftshiftswap(b ^ c ^ d, 0xca62c1d6)
  68. #undef _shiftshiftswap
  69. #undef _shiftswap
  70. sha[0] += a;
  71. sha[1] += b;
  72. sha[2] += c;
  73. sha[3] += d;
  74. sha[4] += e;
  75. }
  76. } // namespace
  77. int32 *hashSha1(const void *data, uint32 len, void *dest) {
  78. const uchar *buf = (const uchar *)data;
  79. uint32 temp[80], block = 0, end;
  80. uint32 sha[5] = {0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0};
  81. for (end = block + 64; block + 64 <= len; end = block + 64) {
  82. for (uint32 i = 0; block < end; block += 4) {
  83. temp[i++] = (uint32) buf[block + 3]
  84. | (((uint32) buf[block + 2]) << 8)
  85. | (((uint32) buf[block + 1]) << 16)
  86. | (((uint32) buf[block]) << 24);
  87. }
  88. sha1PartHash(sha, temp);
  89. }
  90. end = len - block;
  91. memset(temp, 0, sizeof(uint32) * 16);
  92. uint32 last = 0;
  93. for (; last < end; ++last) {
  94. temp[last >> 2] |= (uint32)buf[last + block] << ((3 - (last & 0x03)) << 3);
  95. }
  96. temp[last >> 2] |= 0x80 << ((3 - (last & 3)) << 3);
  97. if (end >= 56) {
  98. sha1PartHash(sha, temp);
  99. memset(temp, 0, sizeof(uint32) * 16);
  100. }
  101. temp[15] = len << 3;
  102. sha1PartHash(sha, temp);
  103. uchar *sha1To = (uchar*)dest;
  104. for (int32 i = 19; i >= 0; --i) {
  105. sha1To[i] = (sha[i >> 2] >> (((3 - i) & 0x03) << 3)) & 0xFF;
  106. }
  107. return (int32*)sha1To;
  108. }
  109. QString AlphaSignature;
  110. int writeAlphaKey() {
  111. if (!AlphaVersion) {
  112. return 0;
  113. }
  114. QString keyName(QString("talpha_%1_key").arg(AlphaVersion));
  115. QFile key(keyName);
  116. if (!key.open(QIODevice::WriteOnly)) {
  117. cout << "Can't open '" << keyName.toUtf8().constData() << "' for write..\n";
  118. return -1;
  119. }
  120. key.write(AlphaSignature.toUtf8());
  121. key.close();
  122. return 0;
  123. }
  124. int main(int argc, char *argv[])
  125. {
  126. QString workDir;
  127. QString remove;
  128. int version = 0;
  129. [[maybe_unused]] bool targetwin64 = false;
  130. [[maybe_unused]] bool targetwinarm = false;
  131. [[maybe_unused]] bool targetarmac = false;
  132. QFileInfoList files;
  133. for (int i = 0; i < argc; ++i) {
  134. if (string("-path") == argv[i] && i + 1 < argc) {
  135. QString path = workDir + QString(argv[i + 1]);
  136. QFileInfo info(path);
  137. files.push_back(info);
  138. if (remove.isEmpty()) remove = info.canonicalPath() + "/";
  139. } else if (string("-target") == argv[i] && i + 1 < argc) {
  140. targetwin64 = (string("win64") == argv[i + 1]);
  141. targetwinarm = (string("winarm") == argv[i + 1]);
  142. } else if (string("-arch") == argv[i] && i + 1 < argc) {
  143. targetarmac = (string("arm64") == argv[i + 1]);
  144. if (!targetarmac && string("x86_64") != argv[i + 1]) {
  145. cout << "Bad -arch param value passed: " << argv[i + 1] << "\n";
  146. return -1;
  147. }
  148. } else if (string("-version") == argv[i] && i + 1 < argc) {
  149. version = QString(argv[i + 1]).toInt();
  150. } else if (string("-beta") == argv[i]) {
  151. BetaChannel = true;
  152. } else if (string("-alphakey") == argv[i]) {
  153. OnlyAlphaKey = true;
  154. } else if (string("-alpha") == argv[i] && i + 1 < argc) {
  155. AlphaVersion = QString(argv[i + 1]).toULongLong();
  156. if (AlphaVersion > version * 1000ULL && AlphaVersion < (version + 1) * 1000ULL) {
  157. BetaChannel = false;
  158. AlphaSignature = countAlphaVersionSignature(AlphaVersion);
  159. if (AlphaSignature.isEmpty()) {
  160. return -1;
  161. }
  162. } else {
  163. cout << "Bad -alpha param value passed, should be for the same version: " << version << ", alpha: " << AlphaVersion << "\n";
  164. return -1;
  165. }
  166. }
  167. }
  168. if (OnlyAlphaKey) {
  169. return writeAlphaKey();
  170. }
  171. if (files.isEmpty() || remove.isEmpty() || version <= 1016 || version > 999999999) {
  172. #ifdef Q_OS_WIN
  173. cout << "Usage: Packer.exe -path {file} -version {version} OR Packer.exe -path {dir} -version {version}\n";
  174. #elif defined Q_OS_MAC
  175. cout << "Usage: Packer.app -path {file} -version {version} OR Packer.app -path {dir} -version {version}\n";
  176. #else
  177. cout << "Usage: Packer -path {file} -version {version} OR Packer -path {dir} -version {version}\n";
  178. #endif
  179. return -1;
  180. }
  181. bool hasDirs = true;
  182. while (hasDirs) {
  183. hasDirs = false;
  184. for (QFileInfoList::iterator i = files.begin(); i != files.end(); ++i) {
  185. QFileInfo info(*i);
  186. QString fullPath = info.canonicalFilePath();
  187. if (info.isDir()) {
  188. hasDirs = true;
  189. files.erase(i);
  190. QDir d = QDir(info.absoluteFilePath());
  191. QString fullDir = d.canonicalPath();
  192. QStringList entries = d.entryList(QDir::Files | QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot);
  193. files.append(d.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot));
  194. break;
  195. } else if (!info.isReadable()) {
  196. cout << "Can't read: " << info.absoluteFilePath().toUtf8().constData() << "\n";
  197. return -1;
  198. } else if (info.isHidden()) {
  199. hasDirs = true;
  200. files.erase(i);
  201. break;
  202. }
  203. }
  204. }
  205. for (QFileInfoList::iterator i = files.begin(); i != files.end(); ++i) {
  206. QFileInfo info(*i);
  207. if (!info.canonicalFilePath().startsWith(remove)) {
  208. cout << "Can't find '" << remove.toUtf8().constData() << "' in file '" << info.canonicalFilePath().toUtf8().constData() << "' :(\n";
  209. return -1;
  210. }
  211. }
  212. QByteArray result;
  213. {
  214. QBuffer buffer(&result);
  215. buffer.open(QIODevice::WriteOnly);
  216. QDataStream stream(&buffer);
  217. stream.setVersion(QDataStream::Qt_5_1);
  218. if (AlphaVersion) {
  219. stream << quint32(0x7FFFFFFF);
  220. stream << quint64(AlphaVersion);
  221. } else {
  222. stream << quint32(version);
  223. }
  224. stream << quint32(files.size());
  225. cout << "Found " << files.size() << " file" << (files.size() == 1 ? "" : "s") << "..\n";
  226. for (QFileInfoList::iterator i = files.begin(); i != files.end(); ++i) {
  227. QFileInfo info(*i);
  228. QString fullName = info.canonicalFilePath();
  229. QString name = fullName.mid(remove.length());
  230. cout << name.toUtf8().constData() << " (" << info.size() << ")\n";
  231. QFile f(fullName);
  232. if (!f.open(QIODevice::ReadOnly)) {
  233. cout << "Can't open '" << fullName.toUtf8().constData() << "' for read..\n";
  234. return -1;
  235. }
  236. QByteArray inner = f.readAll();
  237. stream << name << quint32(inner.size()) << inner;
  238. #ifndef Q_OS_WIN
  239. stream << (QFileInfo(fullName).isExecutable() ? true : false);
  240. #endif
  241. }
  242. if (stream.status() != QDataStream::Ok) {
  243. cout << "Stream status is bad: " << stream.status() << "\n";
  244. return -1;
  245. }
  246. }
  247. int32 resultSize = result.size();
  248. cout << "Compression start, size: " << resultSize << "\n";
  249. QByteArray compressed, resultCheck;
  250. #if defined Q_OS_WIN && !defined TDESKTOP_USE_PACKAGED // use Lzma SDK for win
  251. const int32 hSigLen = 128, hShaLen = 20, hPropsLen = LZMA_PROPS_SIZE, hOriginalSizeLen = sizeof(int32), hSize = hSigLen + hShaLen + hPropsLen + hOriginalSizeLen; // header
  252. compressed.resize(hSize + resultSize + 1024 * 1024); // rsa signature + sha1 + lzma props + max compressed size
  253. size_t compressedLen = compressed.size() - hSize;
  254. size_t outPropsSize = LZMA_PROPS_SIZE;
  255. uchar *_dest = (uchar*)(compressed.data() + hSize);
  256. size_t *_destLen = &compressedLen;
  257. const uchar *_src = (const uchar*)(result.constData());
  258. size_t _srcLen = result.size();
  259. uchar *_outProps = (uchar*)(compressed.data() + hSigLen + hShaLen);
  260. int res = LzmaCompress(_dest, _destLen, _src, _srcLen, _outProps, &outPropsSize, 9, 64 * 1024 * 1024, 4, 0, 2, 273, 2);
  261. if (res != SZ_OK) {
  262. cout << "Error in compression: " << res << "\n";
  263. return -1;
  264. }
  265. compressed.resize(int(hSize + compressedLen));
  266. memcpy(compressed.data() + hSigLen + hShaLen + hPropsLen, &resultSize, hOriginalSizeLen);
  267. cout << "Compressed to size: " << compressedLen << "\n";
  268. cout << "Checking uncompressed..\n";
  269. int32 resultCheckLen;
  270. memcpy(&resultCheckLen, compressed.constData() + hSigLen + hShaLen + hPropsLen, hOriginalSizeLen);
  271. if (resultCheckLen <= 0 || resultCheckLen > 1024 * 1024 * 1024) {
  272. cout << "Bad result len: " << resultCheckLen << "\n";
  273. return -1;
  274. }
  275. resultCheck.resize(resultCheckLen);
  276. size_t resultLen = resultCheck.size();
  277. SizeT srcLen = compressedLen;
  278. int uncompressRes = LzmaUncompress((uchar*)resultCheck.data(), &resultLen, (const uchar*)(compressed.constData() + hSize), &srcLen, (const uchar*)(compressed.constData() + hSigLen + hShaLen), LZMA_PROPS_SIZE);
  279. if (uncompressRes != SZ_OK) {
  280. cout << "Uncompress failed: " << uncompressRes << "\n";
  281. return -1;
  282. }
  283. if (resultLen != size_t(result.size())) {
  284. cout << "Uncompress bad size: " << resultLen << ", was: " << result.size() << "\n";
  285. return -1;
  286. }
  287. #else // use liblzma for others
  288. const int32 hSigLen = 128, hShaLen = 20, hPropsLen = 0, hOriginalSizeLen = sizeof(int32), hSize = hSigLen + hShaLen + hOriginalSizeLen; // header
  289. compressed.resize(hSize + resultSize + 1024 * 1024); // rsa signature + sha1 + lzma props + max compressed size
  290. size_t compressedLen = compressed.size() - hSize;
  291. lzma_stream stream = LZMA_STREAM_INIT;
  292. int preset = 9 | LZMA_PRESET_EXTREME;
  293. lzma_ret ret = lzma_easy_encoder(&stream, preset, LZMA_CHECK_CRC64);
  294. if (ret != LZMA_OK) {
  295. const char *msg;
  296. switch (ret) {
  297. case LZMA_MEM_ERROR: msg = "Memory allocation failed"; break;
  298. case LZMA_OPTIONS_ERROR: msg = "Specified preset is not supported"; break;
  299. case LZMA_UNSUPPORTED_CHECK: msg = "Specified integrity check is not supported"; break;
  300. default: msg = "Unknown error, possibly a bug"; break;
  301. }
  302. cout << "Error initializing the encoder: " << msg << " (error code " << ret << ")\n";
  303. return -1;
  304. }
  305. stream.avail_in = resultSize;
  306. stream.next_in = (uint8_t*)result.constData();
  307. stream.avail_out = compressedLen;
  308. stream.next_out = (uint8_t*)(compressed.data() + hSize);
  309. lzma_ret res = lzma_code(&stream, LZMA_FINISH);
  310. compressedLen -= stream.avail_out;
  311. lzma_end(&stream);
  312. if (res != LZMA_OK && res != LZMA_STREAM_END) {
  313. const char *msg;
  314. switch (res) {
  315. case LZMA_MEM_ERROR: msg = "Memory allocation failed"; break;
  316. case LZMA_DATA_ERROR: msg = "File size limits exceeded"; break;
  317. default: msg = "Unknown error, possibly a bug"; break;
  318. }
  319. cout << "Error in compression: " << msg << " (error code " << res << ")\n";
  320. return -1;
  321. }
  322. compressed.resize(int(hSize + compressedLen));
  323. memcpy(compressed.data() + hSigLen + hShaLen, &resultSize, hOriginalSizeLen);
  324. cout << "Compressed to size: " << compressedLen << "\n";
  325. cout << "Checking uncompressed..\n";
  326. int32 resultCheckLen;
  327. memcpy(&resultCheckLen, compressed.constData() + hSigLen + hShaLen, hOriginalSizeLen);
  328. if (resultCheckLen <= 0 || resultCheckLen > 1024 * 1024 * 1024) {
  329. cout << "Bad result len: " << resultCheckLen << "\n";
  330. return -1;
  331. }
  332. resultCheck.resize(resultCheckLen);
  333. size_t resultLen = resultCheck.size();
  334. stream = LZMA_STREAM_INIT;
  335. ret = lzma_stream_decoder(&stream, UINT64_MAX, LZMA_CONCATENATED);
  336. if (ret != LZMA_OK) {
  337. const char *msg;
  338. switch (ret) {
  339. case LZMA_MEM_ERROR: msg = "Memory allocation failed"; break;
  340. case LZMA_OPTIONS_ERROR: msg = "Specified preset is not supported"; break;
  341. case LZMA_UNSUPPORTED_CHECK: msg = "Specified integrity check is not supported"; break;
  342. default: msg = "Unknown error, possibly a bug"; break;
  343. }
  344. cout << "Error initializing the decoder: " << msg << " (error code " << ret << ")\n";
  345. return -1;
  346. }
  347. stream.avail_in = compressedLen;
  348. stream.next_in = (uint8_t*)(compressed.constData() + hSize);
  349. stream.avail_out = resultLen;
  350. stream.next_out = (uint8_t*)resultCheck.data();
  351. res = lzma_code(&stream, LZMA_FINISH);
  352. if (stream.avail_in) {
  353. cout << "Error in decompression, " << stream.avail_in << " bytes left in _in of " << compressedLen << " whole.\n";
  354. return -1;
  355. } else if (stream.avail_out) {
  356. cout << "Error in decompression, " << stream.avail_out << " bytes free left in _out of " << resultLen << " whole.\n";
  357. return -1;
  358. }
  359. lzma_end(&stream);
  360. if (res != LZMA_OK && res != LZMA_STREAM_END) {
  361. const char *msg;
  362. switch (res) {
  363. case LZMA_MEM_ERROR: msg = "Memory allocation failed"; break;
  364. case LZMA_FORMAT_ERROR: msg = "The input data is not in the .xz format"; break;
  365. case LZMA_OPTIONS_ERROR: msg = "Unsupported compression options"; break;
  366. case LZMA_DATA_ERROR: msg = "Compressed file is corrupt"; break;
  367. case LZMA_BUF_ERROR: msg = "Compressed data is truncated or otherwise corrupt"; break;
  368. default: msg = "Unknown error, possibly a bug"; break;
  369. }
  370. cout << "Error in decompression: " << msg << " (error code " << res << ")\n";
  371. return -1;
  372. }
  373. #endif
  374. if (memcmp(result.constData(), resultCheck.constData(), resultLen)) {
  375. cout << "Data differ :(\n";
  376. return -1;
  377. }
  378. /**/
  379. result = resultCheck = QByteArray();
  380. cout << "Counting SHA1 hash..\n";
  381. uchar sha1Buffer[20];
  382. memcpy(compressed.data() + hSigLen, hashSha1(compressed.constData() + hSigLen + hShaLen, uint32(compressedLen + hPropsLen + hOriginalSizeLen), sha1Buffer), hShaLen); // count sha1
  383. uint32 siglen = 0;
  384. cout << "Signing..\n";
  385. RSA *prKey = [] {
  386. const auto bio = makeBIO(
  387. const_cast<char*>(
  388. (BetaChannel || AlphaVersion)
  389. ? PrivateBetaKey
  390. : PrivateKey),
  391. -1);
  392. return PEM_read_bio_RSAPrivateKey(bio.get(), 0, 0, 0);
  393. }();
  394. if (!prKey) {
  395. cout << "Could not read RSA private key!\n";
  396. return -1;
  397. }
  398. if (RSA_size(prKey) != hSigLen) {
  399. cout << "Bad private key, size: " << RSA_size(prKey) << "\n";
  400. RSA_free(prKey);
  401. return -1;
  402. }
  403. if (RSA_sign(NID_sha1, (const uchar*)(compressed.constData() + hSigLen), hShaLen, (uchar*)(compressed.data()), &siglen, prKey) != 1) { // count signature
  404. cout << "Signing failed!\n";
  405. RSA_free(prKey);
  406. return -1;
  407. }
  408. RSA_free(prKey);
  409. if (siglen != hSigLen) {
  410. cout << "Bad signature length: " << siglen << "\n";
  411. return -1;
  412. }
  413. cout << "Checking signature..\n";
  414. RSA *pbKey = [] {
  415. const auto bio = makeBIO(
  416. const_cast<char*>(
  417. (BetaChannel || AlphaVersion)
  418. ? PublicBetaKey
  419. : PublicKey),
  420. -1);
  421. return PEM_read_bio_RSAPublicKey(bio.get(), 0, 0, 0);
  422. }();
  423. if (!pbKey) {
  424. cout << "Could not read RSA public key!\n";
  425. return -1;
  426. }
  427. if (RSA_verify(NID_sha1, (const uchar*)(compressed.constData() + hSigLen), hShaLen, (const uchar*)(compressed.constData()), siglen, pbKey) != 1) { // verify signature
  428. RSA_free(pbKey);
  429. cout << "Signature verification failed!\n";
  430. return -1;
  431. }
  432. cout << "Signature verified!\n";
  433. RSA_free(pbKey);
  434. #ifdef Q_OS_WIN
  435. QString outName((targetwinarm ? QString("tarm64upd%1") : targetwin64 ? QString("tx64upd%1") : QString("tupdate%1")).arg(AlphaVersion ? AlphaVersion : version));
  436. #elif defined Q_OS_MAC
  437. QString outName((targetarmac ? QString("tarmacupd%1") : QString("tmacupd%1")).arg(AlphaVersion ? AlphaVersion : version));
  438. #else
  439. QString outName(QString("tlinuxupd%1").arg(AlphaVersion ? AlphaVersion : version));
  440. #endif
  441. if (AlphaVersion) {
  442. outName += "_" + AlphaSignature;
  443. }
  444. QFile out(outName);
  445. if (!out.open(QIODevice::WriteOnly)) {
  446. cout << "Can't open '" << outName.toUtf8().constData() << "' for write..\n";
  447. return -1;
  448. }
  449. out.write(compressed);
  450. out.close();
  451. cout << "Update file '" << outName.toUtf8().constData() << "' written successfully!\n";
  452. return writeAlphaKey();
  453. }
  454. QString countAlphaVersionSignature(quint64 version) { // duplicated in autoupdater.cpp
  455. QByteArray cAlphaPrivateKey(AlphaPrivateKey);
  456. if (cAlphaPrivateKey.isEmpty()) {
  457. cout << "Error: Trying to count alpha version signature without alpha private key!\n";
  458. return QString();
  459. }
  460. QByteArray signedData = (QLatin1String("TelegramBeta_") + QString::number(version, 16).toLower()).toUtf8();
  461. static const int32 shaSize = 20, keySize = 128;
  462. uchar sha1Buffer[shaSize];
  463. hashSha1(signedData.constData(), signedData.size(), sha1Buffer); // count sha1
  464. uint32 siglen = 0;
  465. RSA *prKey = [&] {
  466. const auto bio = makeBIO(
  467. const_cast<char*>(cAlphaPrivateKey.constData()),
  468. -1);
  469. return PEM_read_bio_RSAPrivateKey(bio.get(), 0, 0, 0);
  470. }();
  471. if (!prKey) {
  472. cout << "Error: Could not read alpha private key!\n";
  473. return QString();
  474. }
  475. if (RSA_size(prKey) != keySize) {
  476. cout << "Error: Bad alpha private key size: " << RSA_size(prKey) << "\n";
  477. RSA_free(prKey);
  478. return QString();
  479. }
  480. QByteArray signature;
  481. signature.resize(keySize);
  482. if (RSA_sign(NID_sha1, (const uchar*)(sha1Buffer), shaSize, (uchar*)(signature.data()), &siglen, prKey) != 1) { // count signature
  483. cout << "Error: Counting alpha version signature failed!\n";
  484. RSA_free(prKey);
  485. return QString();
  486. }
  487. RSA_free(prKey);
  488. if (siglen != keySize) {
  489. cout << "Error: Bad alpha version signature length: " << siglen << "\n";
  490. return QString();
  491. }
  492. signature = signature.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
  493. signature = signature.replace('-', '8').replace('_', 'B');
  494. return QString::fromUtf8(signature.mid(19, 32));
  495. }