@tgsnake/core
Version:
Pure Telegram MTProto library for nodejs
137 lines (136 loc) • 5.87 kB
JavaScript
"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;
}