Skip to content

Instantly share code, notes, and snippets.

@Rulexec
Created January 27, 2018 21:39
Show Gist options
  • Save Rulexec/f4ddf6e7a859b837f996b3f50097153a to your computer and use it in GitHub Desktop.
Save Rulexec/f4ddf6e7a859b837f996b3f50097153a to your computer and use it in GitHub Desktop.
Telegram crypto AES 256 IGE node.js implementation
var crypto = require('crypto');
/*
msg_key_large = SHA256 (substr (auth_key, 88+x, 32) + plaintext + random_padding);
msg_key = substr (msg_key_large, 8, 16);
sha256_a = SHA256 (msg_key + substr (auth_key, x, 36));
sha256_b = SHA256 (substr (auth_key, 40+x, 36) + msg_key);
aes_key = substr (sha256_a, 0, 8) + substr (sha256_b, 8, 16) + substr (sha256_a, 24, 8);
aes_iv = substr (sha256_b, 0, 8) + substr (sha256_a, 8, 16) + substr (sha256_b, 24, 8);
*/
exports.encrypt = function(buffer, options, callback) {
var {authKey, isReceived} = options;
let padding = buffer.length % 16;
if (padding) {
padding = 16 - padding;
let newBuffer = Buffer.allocUnsafe(buffer.length + padding);
buffer.copy(newBuffer);
for (let i = buffer.length; i < newBuffer.length; i++) {
let rand = (Math.random() * 256) | 0;
newBuffer.writeUInt8(rand, i);
}
buffer = newBuffer;
}
var x = isReceived ? 8 : 0;
var msgKeyLargeHash = crypto.createHash('sha256');
msgKeyLargeHash.update(authKey.slice(88 + x, 88 + x + 32));
msgKeyLargeHash.update(buffer);
var msgKeyLarge = msgKeyLargeHash.digest(),
msgKey = msgKeyLarge.slice(8, 8 + 16);
crypt(buffer, options, msgKey, true, function(error, buffer) {
if (error) callback(error);
else callback(null, {msgKey, buffer});
});
};
exports.decrypt = function(buffer, options, callback) {
crypt(buffer, options, options.msgKey, false, callback);
};
function crypt(buffer, options, msgKey, isEncrypt, callback) {
var {authKey, isReceived} = options;
var x = isReceived;
var sha256_aHash = crypto.createHash('sha256');
sha256_aHash.update(msgKey);
sha256_aHash.update(authKey.slice(x, x + 36));
var sha256_a = sha256_aHash.digest();
var sha256_bHash = crypto.createHash('sha256');
sha256_bHash.update(authKey.slice(40 + x, 40 + x + 36));
sha256_bHash.update(msgKey);
var sha256_b = sha256_bHash.digest();
var aesKey = Buffer.allocUnsafe(32),
aesIV = Buffer.allocUnsafe(32);
sha256_a.copy(aesKey, 0, 0, 8);
sha256_b.copy(aesKey, 8, 8, 8 + 16);
sha256_a.copy(aesKey, 24, 24, 24 + 8);
sha256_b.copy(aesIV, 0, 0, 8);
sha256_a.copy(aesIV, 8, 8, 8 + 16);
sha256_b.copy(aesIV, 24, 24, 24 + 8);
// Start AES 256 IGE
var cipher;
if (isEncrypt) cipher = crypto.createCipher('aes-256-ecb', aesKey);
else cipher = crypto.createDecipher('aes-256-ecb', aesKey);
cipher.setAutoPadding(false);
var result = Buffer.allocUnsafe(buffer.length);
var prevTop, prevBottom;
if (isEncrypt) {
prevTop = aesIV.slice(0, 16);
prevBottom = aesIV.slice(16, 32);
} else {
prevTop = aesIV.slice(16, 32);
prevBottom = aesIV.slice(0, 16);
}
var current = Buffer.allocUnsafe(16);
for (let offset = 0; offset < buffer.length; offset += 16) {
buffer.copy(current, 0, offset, offset + 16);
xorBuffer(current, prevTop);
let crypted = cipher.update(current);
xorBuffer(crypted, prevBottom);
crypted.copy(result, offset, 0, 16);
prevTop = crypted;
prevBottom = buffer.slice(offset, offset + 16);
}
callback(null, result);
}
function xorBuffer(buffer, xor) {
for (let i = 0; i < buffer.length; i++) {
let a = buffer.readUInt8(i),
b = xor.readUInt8(i);
buffer.writeUInt8(a ^ b, i);
}
}
if (require.main === module) {
let authKey = Buffer.from(
'a7847ff672456c275807318f50dcd4e04696278a9531292826effa887ee9f987' +
'86c2028ed0196701df48d1dee8e6a4635be700d9a3bb8d35b0a739d2a4455c38' +
'aa29393e2af0151d716f14ca4dc115877fcdc01689cd89e0fc56dad72da8967a' +
'88204ff1f56b952565796d906922222144a06b0eec897f500876f252f94f2b30' +
'934329511ff513a03b4189e20914e080d6f5b6ab95b9c6169d53be808ecfa98e' +
'6681e60e9dfe780cb00535b0c1a6a8ac1fe78391130cb7936d1e9c359b2a4744' +
'9e127b3364cc25b5fa017fec6b27e6b9b14ff9eaae684e3743f9f374bc80c87e' +
'e4443da1dbd180cc3a20dcad17cc4513585c1eaa7f4c7aecc8851931f814c681',
'hex'
);
var buffer = Buffer.from('Test passed 42 ]', 'ascii');
exports.encrypt(buffer, {authKey}, function(error, result) {
if (error) { console.log(error); return; }
exports.decrypt(result.buffer, {authKey, msgKey: result.msgKey}, function(error, result) {
if (error) { console.log(error); return; }
console.log(result.toString('ascii'));
});
});
exports.encrypt(buffer, {authKey, isReceived: true}, function(error, result) {
if (error) { console.log(error); return; }
exports.decrypt(result.buffer, {authKey, isReceived: true, msgKey: result.msgKey}, function(error, result) {
if (error) { console.log(error); return; }
console.log(result.toString('ascii'));
});
});
var start = Date.now();
var ns = 0,
ops = 0;
while (Date.now() - start < 5000) {
ops++;
var w = process.hrtime();
exports.encrypt(buffer, {authKey}, function(){});
w = process.hrtime(w);
ns += w[0] * 1000000000;
ns += w[1];
}
var elapsed = ns / 1000000000;
console.log('Elapsed ' + elapsed + ', Ops: ' + ops + ', ops per second: ' + (ops / elapsed));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment