UNPKG

@tgsnake/core

Version:

Pure Telegram MTProto library for nodejs

137 lines (136 loc) 5.87 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.kdf = kdf; exports.pack = pack; exports.unpack = unpack; const platform_node_js_1 = require("../platform.node.js"); const index_js_1 = require("../errors/index.js"); const index_js_2 = require("../raw/index.js"); const helpers_js_1 = require("../helpers.js"); const Aes_js_1 = require("./Aes.js"); const index_js_3 = require("../raw/index.js"); function sha256(data) { const hash = platform_node_js_1.crypto.createHash('sha256'); hash.update(data); return hash.digest(); } function sha1(data) { const hash = platform_node_js_1.crypto.createHash('sha1'); hash.update(data); return hash.digest(); } function kdf(sharedKey, msgKey, isAdmin, v1 = false) { const x = isAdmin ? 0 : 8; if (v1) { const sha1A = sha1(platform_node_js_1.Buffer.concat([ msgKey, sharedKey.subarray(x, x + 32), ])); const sha1B = sha1(platform_node_js_1.Buffer.concat([ sharedKey.subarray(x + 32, x + 48), msgKey, sharedKey.subarray(x + 48, x + 64), ])); const sha1C = sha1(platform_node_js_1.Buffer.concat([ sharedKey.subarray(x + 64, x + 96), msgKey, ])); const sha1D = sha1(platform_node_js_1.Buffer.concat([ msgKey, sharedKey.subarray(x + 96, x + 128), ])); const aesKey = platform_node_js_1.Buffer.concat([ sha1A.subarray(0, 8), sha1B.subarray(8, 20), sha1C.subarray(4, 16), ]); const aesIv = platform_node_js_1.Buffer.concat([ sha1A.subarray(8, 20), sha1B.subarray(0, 8), sha1C.subarray(16, 20), sha1D.subarray(0, 8), ]); return [aesKey, aesIv]; } else { const sha256A = sha256(platform_node_js_1.Buffer.concat([ msgKey, sharedKey.subarray(x, x + 36), ])); const sha256B = sha256(platform_node_js_1.Buffer.concat([ sharedKey.subarray(x + 40, x + 76), msgKey, ])); const aesKey = platform_node_js_1.Buffer.concat([ sha256A.subarray(0, 8), sha256B.subarray(8, 24), sha256A.subarray(24, 32), ]); const aesIv = platform_node_js_1.Buffer.concat([ sha256B.subarray(0, 8), sha256A.subarray(8, 24), sha256B.subarray(24, 32), ]); return [aesKey, aesIv]; } } function pack(message, sharedKey, inSeqNo, outSeqNo, isAdmin, layer, mtproto = 2) { let msg = message; if (layer > 8) { msg = new index_js_3.Raw.DecryptedMessageLayer17({ randomBytes: platform_node_js_1.crypto.randomBytes(15 + 4 * Math.floor(Math.random() * 3)), layer: layer, inSeqNo: inSeqNo, outSeqNo: outSeqNo, message: message, }); } const bytesmsg = msg.write(); const length = platform_node_js_1.Buffer.alloc(4); length.writeUInt32LE(platform_node_js_1.Buffer.byteLength(bytesmsg), 0); const data = platform_node_js_1.Buffer.concat([length, bytesmsg]); const padding = platform_node_js_1.Buffer.from(platform_node_js_1.crypto.randomBytes((0, helpers_js_1.mod)(-(platform_node_js_1.Buffer.byteLength(data) + 16), 16) + 16)); const paddedMsg = platform_node_js_1.Buffer.concat([ data, padding, ]); const msgKeyLarge = sha256(platform_node_js_1.Buffer.concat([ sharedKey.subarray(88 + (isAdmin ? 0 : 8), 88 + (isAdmin ? 0 : 8) + 32), paddedMsg, ])); const msgKey = mtproto === 1 ? sha1(data).subarray(-16) : msgKeyLarge.subarray(8, 8 + 16); const [aesKey, aesIv] = mtproto === 1 ? kdf(sharedKey, msgKey, isAdmin, true) : kdf(sharedKey, msgKey, isAdmin, false); const fingerprintKey = sha1(sharedKey).subarray(-8); return platform_node_js_1.Buffer.concat([ fingerprintKey, msgKey, (0, Aes_js_1.ige256Encrypt)(paddedMsg, aesKey, aesIv), ]); } async function unpack(message, sharedKey, isAdmin, mtproto = 2) { const data = new index_js_2.BytesIO(message.bytes); const serverFingerprintKey = data.readBigInt64LE(); const clientFingerprintKey = sha1(sharedKey).subarray(-8).readBigInt64LE(); if (serverFingerprintKey !== clientFingerprintKey) { throw new index_js_1.SecretChatError.FingerprintMismatch(); } const msgKey = data.read(16); const encryptedMsg = data.read(); const [aesKey, aesIv] = mtproto === 1 ? kdf(sharedKey, msgKey, isAdmin, true) : kdf(sharedKey, msgKey, !isAdmin, false); const decryptedMsg = new index_js_2.BytesIO((0, Aes_js_1.ige256Decrypt)(encryptedMsg, aesKey, aesIv)); const msgLength = decryptedMsg.readUInt32LE(); const payload = decryptedMsg.read(); const padding = payload.subarray(msgLength); const msgKeyLarge = sha256(platform_node_js_1.Buffer.concat([ sharedKey.subarray(88 + (isAdmin ? 8 : 0), 88 + (isAdmin ? 8 : 0) + 32), decryptedMsg.buffer, ])); const clientMsgKey = mtproto === 1 ? sha1(decryptedMsg.buffer.subarray(0, 4 + msgLength)).subarray(-16) : msgKeyLarge.subarray(8, 8 + 16); index_js_1.SecurityCheckMismatch.check(msgKey.equals(clientMsgKey), 'Given message key is not equal with client side'); index_js_1.SecurityCheckMismatch.check(platform_node_js_1.Buffer.byteLength(padding) >= 12 && platform_node_js_1.Buffer.byteLength(padding) <= 1024, 'Payload padding is lower than 12 or bigger than 1024'); index_js_1.SecurityCheckMismatch.check((0, helpers_js_1.mod)(platform_node_js_1.Buffer.byteLength(padding), 4) === 0, 'Mod of padding length with 4 is equal with zero'); const msg = await index_js_2.TLObject.read(new index_js_2.BytesIO(payload)); return msg; }