Migrate AES to Native Client
Up to 4x faster upload files Up to 2x faster download files
This commit is contained in:
parent
2c49f28b7d
commit
5661533fc4
@ -112,11 +112,11 @@ function bytesXor (bytes1, bytes2) {
|
||||
}
|
||||
|
||||
function bytesToWords (bytes) {
|
||||
var len = bytes.byteLength || bytes.length,
|
||||
words = [], i;
|
||||
if (bytes instanceof ArrayBuffer) {
|
||||
bytes = new Uint8Array(bytes);
|
||||
}
|
||||
var len = bytes.length,
|
||||
words = [], i;
|
||||
for (i = 0; i < len; i++) {
|
||||
words[i >>> 2] |= bytes[i] << (24 - (i % 4) * 8);
|
||||
}
|
||||
@ -156,6 +156,24 @@ function bytesToArrayBuffer (b) {
|
||||
return (new Uint8Array(b)).buffer;
|
||||
}
|
||||
|
||||
function convertToArrayBuffer(bytes) {
|
||||
// Be careful with converting subarrays!!
|
||||
if (bytes instanceof ArrayBuffer) {
|
||||
return bytes;
|
||||
}
|
||||
if (bytes.buffer !== undefined) {
|
||||
return bytes.buffer;
|
||||
}
|
||||
return bytesToArrayBuffer(bytes);
|
||||
}
|
||||
|
||||
function convertToUint8Array(bytes) {
|
||||
if (bytes.buffer !== undefined) {
|
||||
return bytes;
|
||||
}
|
||||
return new Uint8Array(bytes);
|
||||
}
|
||||
|
||||
function bytesFromArrayBuffer (buffer) {
|
||||
var len = buffer.byteLength,
|
||||
byteView = new Uint8Array(buffer),
|
||||
@ -207,25 +225,24 @@ function uintToInt (val) {
|
||||
return val;
|
||||
}
|
||||
|
||||
function sha1Hash (bytes) {
|
||||
function sha1HashSync (bytes) {
|
||||
this.rushaInstance = this.rushaInstance || new Rusha(1024 * 1024);
|
||||
|
||||
// console.log(dT(), 'SHA-1 hash start', bytes.byteLength || bytes.length);
|
||||
var hashBytes = bytesFromArrayBuffer(rushaInstance.rawDigest(bytes).buffer);
|
||||
var hashBytes = rushaInstance.rawDigest(bytes).buffer;
|
||||
// console.log(dT(), 'SHA-1 hash finish');
|
||||
|
||||
return hashBytes;
|
||||
}
|
||||
|
||||
function sha1BytesSync (bytes) {
|
||||
return bytesFromArrayBuffer(sha1HashSync(bytes));
|
||||
}
|
||||
|
||||
|
||||
|
||||
function rsaEncrypt (publicKey, bytes) {
|
||||
var needPadding = 255 - bytes.length;
|
||||
if (needPadding > 0) {
|
||||
var padding = new Array(needPadding);
|
||||
(new SecureRandom()).nextBytes(padding);
|
||||
|
||||
bytes = bytes.concat(padding);
|
||||
}
|
||||
bytes = addPadding(bytes, 255);
|
||||
|
||||
// console.log('RSA encrypt start');
|
||||
var N = new BigInteger(publicKey.modulus, 16),
|
||||
@ -233,18 +250,16 @@ function rsaEncrypt (publicKey, bytes) {
|
||||
X = new BigInteger(bytes),
|
||||
encryptedBigInt = X.modPowInt(E, N),
|
||||
encryptedBytes = bytesFromBigInt(encryptedBigInt, 256);
|
||||
|
||||
// console.log('RSA encrypt finish');
|
||||
|
||||
return encryptedBytes;
|
||||
}
|
||||
|
||||
function aesEncrypt (bytes, keyBytes, ivBytes) {
|
||||
function addPadding(bytes, blockSize) {
|
||||
blockSize = blockSize || 16;
|
||||
var len = bytes.byteLength || bytes.length;
|
||||
console.log(dT(), 'AES encrypt start', len/*, bytesToHex(keyBytes), bytesToHex(ivBytes)*/);
|
||||
|
||||
var needPadding = 16 - (len % 16);
|
||||
if (needPadding > 0 && needPadding < 16) {
|
||||
var needPadding = blockSize - (len % blockSize);
|
||||
if (needPadding > 0 && needPadding < blockSize) {
|
||||
var padding = new Array(needPadding);
|
||||
(new SecureRandom()).nextBytes(padding);
|
||||
|
||||
@ -255,6 +270,15 @@ function aesEncrypt (bytes, keyBytes, ivBytes) {
|
||||
}
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
function aesEncryptSync (bytes, keyBytes, ivBytes) {
|
||||
var len = bytes.byteLength || bytes.length;
|
||||
|
||||
// console.log(dT(), 'AES encrypt start', len/*, bytesToHex(keyBytes), bytesToHex(ivBytes)*/);
|
||||
bytes = addPadding(bytes);
|
||||
|
||||
var encryptedWords = CryptoJS.AES.encrypt(bytesToWords(bytes), bytesToWords(keyBytes), {
|
||||
iv: bytesToWords(ivBytes),
|
||||
padding: CryptoJS.pad.NoPadding,
|
||||
@ -262,15 +286,14 @@ function aesEncrypt (bytes, keyBytes, ivBytes) {
|
||||
}).ciphertext;
|
||||
|
||||
var encryptedBytes = bytesFromWords(encryptedWords);
|
||||
|
||||
console.log(dT(), 'AES encrypt finish');
|
||||
// console.log(dT(), 'AES encrypt finish');
|
||||
|
||||
return encryptedBytes;
|
||||
}
|
||||
|
||||
function aesDecrypt (encryptedBytes, keyBytes, ivBytes) {
|
||||
// console.log('AES decrypt start', encryptedBytes.length/*, bytesToHex(keyBytes), bytesToHex(ivBytes)*/);
|
||||
function aesDecryptSync (encryptedBytes, keyBytes, ivBytes) {
|
||||
|
||||
// console.log(dT(), 'AES decrypt start', encryptedBytes.length);
|
||||
var decryptedWords = CryptoJS.AES.decrypt({ciphertext: bytesToWords(encryptedBytes)}, bytesToWords(keyBytes), {
|
||||
iv: bytesToWords(ivBytes),
|
||||
padding: CryptoJS.pad.NoPadding,
|
||||
@ -278,8 +301,7 @@ function aesDecrypt (encryptedBytes, keyBytes, ivBytes) {
|
||||
});
|
||||
|
||||
var bytes = bytesFromWords(decryptedWords);
|
||||
|
||||
// console.log('AES decrypt finish');
|
||||
// console.log(dT(), 'AES decrypt finish');
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
@ -29,15 +29,15 @@ onmessage = function (e) {
|
||||
break;
|
||||
|
||||
case 'sha1-hash':
|
||||
result = sha1Hash(e.data.bytes);
|
||||
result = sha1HashSync(e.data.bytes);
|
||||
break;
|
||||
|
||||
case 'aes-encrypt':
|
||||
result = aesEncrypt(e.data.bytes, e.data.keyBytes, e.data.ivBytes);
|
||||
result = aesEncryptSync(e.data.bytes, e.data.keyBytes, e.data.ivBytes);
|
||||
break;
|
||||
|
||||
case 'aes-decrypt':
|
||||
result = aesDecrypt(e.data.encryptedBytes, e.data.keyBytes, e.data.ivBytes);
|
||||
result = aesDecryptSync(e.data.encryptedBytes, e.data.keyBytes, e.data.ivBytes);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -82,7 +82,7 @@ angular.module('izhukov.mtproto', ['izhukov.utils'])
|
||||
|
||||
var buffer = RSAPublicKey.getBuffer();
|
||||
|
||||
var fingerprintBytes = sha1Hash(buffer).slice(-8);
|
||||
var fingerprintBytes = sha1BytesSync(buffer).slice(-8);
|
||||
fingerprintBytes.reverse();
|
||||
|
||||
publicKeysParsed[bytesToHex(fingerprintBytes)] = {
|
||||
@ -169,7 +169,11 @@ angular.module('izhukov.mtproto', ['izhukov.utils'])
|
||||
.factory('MtpAuthorizer', function (MtpDcConfigurator, MtpRsaKeysManager, MtpSecureRandom, MtpTimeManager, CryptoWorker, $http, $q, $timeout) {
|
||||
|
||||
var chromeMatches = navigator.userAgent.match(/Chrome\/(\d+(\.\d+)?)/),
|
||||
chromeVersion = chromeMatches && parseFloat(chromeMatches[1]) || false;
|
||||
chromeVersion = chromeMatches && parseFloat(chromeMatches[1]) || false,
|
||||
xhrSendBuffer = !('ArrayBufferView' in window) && (!chromeVersion || chromeVersion < 30);
|
||||
|
||||
delete $http.defaults.headers.post['Content-Type'];
|
||||
delete $http.defaults.headers.common['Accept'];
|
||||
|
||||
function mtpSendPlainRequest (dcID, requestBuffer) {
|
||||
var requestLength = requestBuffer.byteLength,
|
||||
@ -190,16 +194,10 @@ angular.module('izhukov.mtproto', ['izhukov.utils'])
|
||||
resultArray.set(headerArray);
|
||||
resultArray.set(requestArray, headerArray.length);
|
||||
|
||||
delete $http.defaults.headers.post['Content-Type'];
|
||||
delete $http.defaults.headers.common['Accept'];
|
||||
|
||||
if (!('ArrayBufferView' in window) && (!chromeVersion || chromeVersion < 30)) {
|
||||
resultArray = resultArray.buffer;
|
||||
}
|
||||
|
||||
var requestPromise;
|
||||
var requestData = xhrSendBuffer ? resultBuffer : resultArray,
|
||||
requestPromise;
|
||||
try {
|
||||
requestPromise = $http.post('http://' + MtpDcConfigurator.chooseServer(dcID) + '/apiw1', resultArray, {
|
||||
requestPromise = $http.post('http://' + MtpDcConfigurator.chooseServer(dcID) + '/apiw1', requestData, {
|
||||
responseType: 'arraybuffer',
|
||||
transformRequest: null
|
||||
});
|
||||
@ -305,7 +303,7 @@ angular.module('izhukov.mtproto', ['izhukov.utils'])
|
||||
new_nonce: auth.newNonce
|
||||
}, 'P_Q_inner_data', 'DECRYPTED_DATA');
|
||||
|
||||
var dataWithHash = sha1Hash(data.getBuffer()).concat(data.getBytes());
|
||||
var dataWithHash = sha1BytesSync(data.getBuffer()).concat(data.getBytes());
|
||||
|
||||
var request = new TLSerialization({mtproto: true});
|
||||
request.storeMethod('req_DH_params', {
|
||||
@ -337,7 +335,7 @@ angular.module('izhukov.mtproto', ['izhukov.utils'])
|
||||
}
|
||||
|
||||
if (response._ == 'server_DH_params_fail') {
|
||||
var newNonceHash = sha1Hash(auth.newNonce).slice(-16)
|
||||
var newNonceHash = sha1BytesSync(auth.newNonce).slice(-16);
|
||||
if (!bytesCmp (newNonceHash, response.new_nonce_hash)) {
|
||||
deferred.reject(new Error('server_DH_params_fail new_nonce_hash mismatch'));
|
||||
return false;
|
||||
@ -362,10 +360,10 @@ angular.module('izhukov.mtproto', ['izhukov.utils'])
|
||||
function mtpDecryptServerDhDataAnswer (auth, encryptedAnswer) {
|
||||
auth.localTime = tsNow();
|
||||
|
||||
auth.tmpAesKey = sha1Hash(auth.newNonce.concat(auth.serverNonce)).concat(sha1Hash(auth.serverNonce.concat(auth.newNonce)).slice(0, 12));
|
||||
auth.tmpAesIv = sha1Hash(auth.serverNonce.concat(auth.newNonce)).slice(12).concat(sha1Hash([].concat(auth.newNonce, auth.newNonce)), auth.newNonce.slice(0, 4));
|
||||
auth.tmpAesKey = sha1BytesSync(auth.newNonce.concat(auth.serverNonce)).concat(sha1BytesSync(auth.serverNonce.concat(auth.newNonce)).slice(0, 12));
|
||||
auth.tmpAesIv = sha1BytesSync(auth.serverNonce.concat(auth.newNonce)).slice(12).concat(sha1BytesSync([].concat(auth.newNonce, auth.newNonce)), auth.newNonce.slice(0, 4));
|
||||
|
||||
var answerWithHash = aesDecrypt(encryptedAnswer, auth.tmpAesKey, auth.tmpAesIv);
|
||||
var answerWithHash = aesDecryptSync(encryptedAnswer, auth.tmpAesKey, auth.tmpAesIv);
|
||||
|
||||
var hash = answerWithHash.slice(0, 20);
|
||||
var answerWithPadding = answerWithHash.slice(20);
|
||||
@ -395,7 +393,7 @@ angular.module('izhukov.mtproto', ['izhukov.utils'])
|
||||
|
||||
var offset = deserializer.getOffset();
|
||||
|
||||
if (!bytesCmp(hash, sha1Hash(answerWithPadding.slice(0, offset)))) {
|
||||
if (!bytesCmp(hash, sha1BytesSync(answerWithPadding.slice(0, offset)))) {
|
||||
throw new Error('server_DH_inner_data SHA1-hash mismatch');
|
||||
}
|
||||
|
||||
@ -420,9 +418,9 @@ angular.module('izhukov.mtproto', ['izhukov.utils'])
|
||||
g_b: gB,
|
||||
}, 'Client_DH_Inner_Data');
|
||||
|
||||
var dataWithHash = sha1Hash(data.getBuffer()).concat(data.getBytes());
|
||||
var dataWithHash = sha1BytesSync(data.getBuffer()).concat(data.getBytes());
|
||||
|
||||
var encryptedData = aesEncrypt(dataWithHash, auth.tmpAesKey, auth.tmpAesIv);
|
||||
var encryptedData = aesEncryptSync(dataWithHash, auth.tmpAesKey, auth.tmpAesIv);
|
||||
|
||||
var request = new TLSerialization({mtproto: true});
|
||||
request.storeMethod('set_client_DH_params', {
|
||||
@ -451,14 +449,14 @@ angular.module('izhukov.mtproto', ['izhukov.utils'])
|
||||
}
|
||||
|
||||
CryptoWorker.modPow(auth.gA, auth.b, auth.dhPrime).then(function (authKey) {
|
||||
var authKeyHash = sha1Hash(authKey),
|
||||
var authKeyHash = sha1BytesSync(authKey),
|
||||
authKeyAux = authKeyHash.slice(0, 8),
|
||||
authKeyID = authKeyHash.slice(-8);
|
||||
|
||||
console.log(dT(), 'Got Set_client_DH_params_answer', response._);
|
||||
switch (response._) {
|
||||
case 'dh_gen_ok':
|
||||
var newNonceHash1 = sha1Hash(auth.newNonce.concat([1], authKeyAux)).slice(-16);
|
||||
var newNonceHash1 = sha1BytesSync(auth.newNonce.concat([1], authKeyAux)).slice(-16);
|
||||
|
||||
if (!bytesCmp(newNonceHash1, response.new_nonce_hash1)) {
|
||||
deferred.reject(new Error('Set_client_DH_params_answer new_nonce_hash1 mismatch'));
|
||||
@ -476,7 +474,7 @@ angular.module('izhukov.mtproto', ['izhukov.utils'])
|
||||
break;
|
||||
|
||||
case 'dh_gen_retry':
|
||||
var newNonceHash2 = sha1Hash(auth.newNonce.concat([2], authKeyAux)).slice(-16);
|
||||
var newNonceHash2 = sha1BytesSync(auth.newNonce.concat([2], authKeyAux)).slice(-16);
|
||||
if (!bytesCmp(newNonceHash2, response.new_nonce_hash2)) {
|
||||
deferred.reject(new Error('Set_client_DH_params_answer new_nonce_hash2 mismatch'));
|
||||
return false;
|
||||
@ -485,7 +483,7 @@ angular.module('izhukov.mtproto', ['izhukov.utils'])
|
||||
return mtpSendSetClientDhParams(auth);
|
||||
|
||||
case 'dh_gen_fail':
|
||||
var newNonceHash3 = sha1Hash(auth.newNonce.concat([3], authKeyAux)).slice(-16);
|
||||
var newNonceHash3 = sha1BytesSync(auth.newNonce.concat([3], authKeyAux)).slice(-16);
|
||||
if (!bytesCmp(newNonceHash3, response.new_nonce_hash3)) {
|
||||
deferred.reject(new Error('Set_client_DH_params_answer new_nonce_hash3 mismatch'));
|
||||
return false;
|
||||
@ -553,7 +551,11 @@ angular.module('izhukov.mtproto', ['izhukov.utils'])
|
||||
offline,
|
||||
offlineInited = false,
|
||||
chromeMatches = navigator.userAgent.match(/Chrome\/(\d+(\.\d+)?)/),
|
||||
chromeVersion = chromeMatches && parseFloat(chromeMatches[1]) || false;
|
||||
chromeVersion = chromeMatches && parseFloat(chromeMatches[1]) || false,
|
||||
xhrSendBuffer = !('ArrayBufferView' in window) && (!chromeVersion || chromeVersion < 30);
|
||||
|
||||
delete $http.defaults.headers.post['Content-Type'];
|
||||
delete $http.defaults.headers.common['Accept'];
|
||||
|
||||
$rootScope.retryOnline = function () {
|
||||
$(document.body).trigger('online');
|
||||
@ -566,7 +568,9 @@ angular.module('izhukov.mtproto', ['izhukov.utils'])
|
||||
this.iii = iii++;
|
||||
|
||||
this.authKey = authKey;
|
||||
this.authKeyID = sha1Hash(authKey).slice(-8);
|
||||
this.authKeyUint8 = convertToUint8Array(authKey);
|
||||
this.authKeyBuffer = convertToArrayBuffer(authKey);
|
||||
this.authKeyID = sha1BytesSync(authKey).slice(-8);
|
||||
|
||||
this.serverSalt = serverSalt;
|
||||
|
||||
@ -827,19 +831,47 @@ angular.module('izhukov.mtproto', ['izhukov.utils'])
|
||||
};
|
||||
|
||||
MtpNetworker.prototype.getMsgKeyIv = function (msgKey, isOut) {
|
||||
var authKey = this.authKey,
|
||||
x = isOut ? 0 : 8;
|
||||
var authKey = this.authKeyUint8,
|
||||
x = isOut ? 0 : 8,
|
||||
sha1aText = new Uint8Array(48),
|
||||
sha1bText = new Uint8Array(48),
|
||||
sha1cText = new Uint8Array(48),
|
||||
sha1dText = new Uint8Array(48),
|
||||
promises = {};
|
||||
|
||||
var promises = {
|
||||
sha1a: CryptoWorker.sha1Hash(msgKey.concat(authKey.slice(x, x + 32))),
|
||||
sha1b: CryptoWorker.sha1Hash(authKey.slice(32 + x, 48 + x).concat(msgKey, authKey.slice(48 + x, 64 + x))),
|
||||
sha1c: CryptoWorker.sha1Hash(authKey.slice(64 + x, 96 + x).concat(msgKey)),
|
||||
sha1d: CryptoWorker.sha1Hash(msgKey.concat(authKey.slice(96 + x, 128 + x)))
|
||||
};
|
||||
sha1aText.set(msgKey, 0);
|
||||
sha1aText.set(authKey.subarray(x, x + 32), 16);
|
||||
promises.sha1a = CryptoWorker.sha1Hash(sha1aText);
|
||||
|
||||
sha1bText.set(authKey.subarray(x + 32, x + 48), 0);
|
||||
sha1bText.set(msgKey, 16);
|
||||
sha1bText.set(authKey.subarray(x + 48, x + 64), 32);
|
||||
promises.sha1b = CryptoWorker.sha1Hash(sha1bText);
|
||||
|
||||
sha1cText.set(authKey.subarray(x + 64, x + 96), 0);
|
||||
sha1cText.set(msgKey, 32);
|
||||
promises.sha1c = CryptoWorker.sha1Hash(sha1cText);
|
||||
|
||||
sha1dText.set(msgKey, 0);
|
||||
sha1dText.set(authKey.subarray(x + 96, x + 128), 16);
|
||||
promises.sha1d = CryptoWorker.sha1Hash(sha1dText);
|
||||
|
||||
return $q.all(promises).then(function (result) {
|
||||
var aesKey = result.sha1a.slice(0, 8).concat(result.sha1b.slice(8, 20), result.sha1c.slice(4, 16));
|
||||
var aesIv = result.sha1a.slice(8, 20).concat(result.sha1b.slice(0, 8), result.sha1c.slice(16, 20), result.sha1d.slice(0, 8));
|
||||
var aesKey = new Uint8Array(32),
|
||||
aesIv = new Uint8Array(32);
|
||||
sha1a = new Uint8Array(result.sha1a),
|
||||
sha1b = new Uint8Array(result.sha1b),
|
||||
sha1c = new Uint8Array(result.sha1c),
|
||||
sha1d = new Uint8Array(result.sha1d);
|
||||
|
||||
aesKey.set(sha1a.subarray(0, 8));
|
||||
aesKey.set(sha1b.subarray(8, 20), 8);
|
||||
aesKey.set(sha1c.subarray(4, 16), 20);
|
||||
|
||||
aesIv.set(sha1a.subarray(8, 20));
|
||||
aesIv.set(sha1b.subarray(0, 8), 12);
|
||||
aesIv.set(sha1c.subarray(16, 20), 20);
|
||||
aesIv.set(sha1d.subarray(0, 8), 24);
|
||||
|
||||
return [aesKey, aesIv];
|
||||
});
|
||||
@ -1076,14 +1108,14 @@ angular.module('izhukov.mtproto', ['izhukov.utils'])
|
||||
MtpNetworker.prototype.getEncryptedMessage = function (bytes) {
|
||||
var self = this;
|
||||
|
||||
console.log(dT(), 'Start encrypt', bytes.byteLength);
|
||||
// console.log(dT(), 'Start encrypt', bytes.byteLength);
|
||||
return CryptoWorker.sha1Hash(bytes).then(function (bytesHash) {
|
||||
console.log(dT(), 'after hash');
|
||||
var msgKey = bytesHash.slice(-16);
|
||||
// console.log(dT(), 'after hash');
|
||||
var msgKey = new Uint8Array(bytesHash).subarray(4, 20);
|
||||
return self.getMsgKeyIv(msgKey, true).then(function (keyIv) {
|
||||
console.log(dT(), 'after msg key iv');
|
||||
// console.log(dT(), 'after msg key iv');
|
||||
return CryptoWorker.aesEncrypt(bytes, keyIv[0], keyIv[1]).then(function (encryptedBytes) {
|
||||
console.log(dT(), 'Finish encrypt');
|
||||
// console.log(dT(), 'Finish encrypt');
|
||||
return {
|
||||
bytes: encryptedBytes,
|
||||
msgKey: msgKey
|
||||
@ -1117,18 +1149,12 @@ angular.module('izhukov.mtproto', ['izhukov.utils'])
|
||||
|
||||
return this.getEncryptedMessage(data.getBuffer()).then(function (encryptedResult) {
|
||||
// console.log(dT(), 'Got encrypted out message'/*, encryptedResult*/);
|
||||
var request = new TLSerialization({startMaxLength: encryptedResult.bytes.length + 256});
|
||||
var request = new TLSerialization({startMaxLength: encryptedResult.bytes.byteLength + 256});
|
||||
request.storeIntBytes(self.authKeyID, 64, 'auth_key_id');
|
||||
request.storeIntBytes(encryptedResult.msgKey, 128, 'msg_key');
|
||||
request.storeRawBytes(encryptedResult.bytes, 'encrypted_data');
|
||||
|
||||
delete $http.defaults.headers.post['Content-Type'];
|
||||
delete $http.defaults.headers.common['Accept'];
|
||||
|
||||
var resultArray = request.getArray();
|
||||
if (!('ArrayBufferView' in window) && (!chromeVersion || chromeVersion < 30)) {
|
||||
resultArray = resultArray.buffer;
|
||||
}
|
||||
var requestData = xhrSendBuffer ? request.getBuffer() : request.getArray();
|
||||
|
||||
var requestPromise;
|
||||
try {
|
||||
@ -1136,7 +1162,7 @@ angular.module('izhukov.mtproto', ['izhukov.utils'])
|
||||
responseType: 'arraybuffer',
|
||||
transformRequest: null
|
||||
});
|
||||
requestPromise = $http.post('http://' + MtpDcConfigurator.chooseServer(self.dcID) + '/apiw1', resultArray, options);
|
||||
requestPromise = $http.post('http://' + MtpDcConfigurator.chooseServer(self.dcID) + '/apiw1', requestData, options);
|
||||
} catch (e) {
|
||||
requestPromise = $q.reject(e);
|
||||
}
|
||||
@ -1172,32 +1198,29 @@ angular.module('izhukov.mtproto', ['izhukov.utils'])
|
||||
|
||||
var deserializer = new TLDeserialization(responseBuffer);
|
||||
|
||||
var authKeyID = deserializer.fetchIntBytes(64, 'auth_key_id');
|
||||
var authKeyID = deserializer.fetchIntBytes(64, false, 'auth_key_id');
|
||||
if (!bytesCmp(authKeyID, this.authKeyID)) {
|
||||
throw new Error('Invalid server auth_key_id: ' + bytesToHex(authKeyID));
|
||||
}
|
||||
var msgKey = deserializer.fetchIntBytes(128, 'msg_key');
|
||||
|
||||
var dataLength = responseBuffer.byteLength - deserializer.getOffset();
|
||||
var encryptedData = deserializer.fetchRawBytes(dataLength, 'encrypted_data');
|
||||
var msgKey = deserializer.fetchIntBytes(128, true, 'msg_key'),
|
||||
encryptedData = deserializer.fetchRawBytes(responseBuffer.byteLength - deserializer.getOffset(), true, 'encrypted_data');
|
||||
|
||||
return this.getDecryptedMessage(msgKey, encryptedData).then(function (dataWithPadding) {
|
||||
var buffer = bytesToArrayBuffer(dataWithPadding);
|
||||
var deserializer = new TLDeserialization(dataWithPadding, {mtproto: true});
|
||||
|
||||
var deserializer = new TLDeserialization(buffer, {mtproto: true});
|
||||
|
||||
var salt = deserializer.fetchIntBytes(64, 'salt');
|
||||
var sessionID = deserializer.fetchIntBytes(64, 'session_id');
|
||||
var salt = deserializer.fetchIntBytes(64, false, 'salt');
|
||||
var sessionID = deserializer.fetchIntBytes(64, false, 'session_id');
|
||||
var messageID = deserializer.fetchLong('message_id');
|
||||
|
||||
var seqNo = deserializer.fetchInt('seq_no');
|
||||
|
||||
var messageBody = deserializer.fetchRawBytes(false, 'message_data');
|
||||
var messageBody = deserializer.fetchRawBytes(false, true, 'message_data');
|
||||
|
||||
var offset = deserializer.getOffset();
|
||||
var hashData = convertToUint8Array(dataWithPadding).subarray(0, deserializer.getOffset());
|
||||
|
||||
return CryptoWorker.sha1Hash(dataWithPadding.slice(0, offset)).then(function (dataHashed) {
|
||||
if (!bytesCmp(msgKey, dataHashed.slice(-16))) {
|
||||
return CryptoWorker.sha1Hash(hashData).then(function (dataHash) {
|
||||
if (!bytesCmp(msgKey, bytesFromArrayBuffer(dataHash).slice(-16))) {
|
||||
console.warn(msgKey, bytesFromArrayBuffer(dataHash));
|
||||
throw new Error('server msgKey mismatch');
|
||||
}
|
||||
|
||||
|
@ -500,18 +500,16 @@ angular.module('izhukov.utils', [])
|
||||
};
|
||||
|
||||
if (navigator.mimeTypes['application/x-pnacl'] !== undefined) {
|
||||
var listener = $('<div id="nacl_listener"><embed id="mtproto_crypto" width="0" height="0" src="nacl/mtproto_crypto.nmf" type="application/x-pnacl" /></div>').appendTo($('body'))[0];
|
||||
var listener = $('<div id="nacl_listener"><embed id="mtproto_crypto" width="0" height="0" src="nacl/mtproto_crypto.nmf?'+Math.random()+'" type="application/x-pnacl" /></div>').appendTo($('body'))[0];
|
||||
listener.addEventListener('load', function (e) {
|
||||
aesNaClEmbed = listener.firstChild;
|
||||
console.warn('NaCl ready', aesNaClEmbed);
|
||||
console.log(dT(), 'NaCl ready');
|
||||
}, true);
|
||||
listener.addEventListener('message', function (e) {
|
||||
console.log(e.data.result);
|
||||
console.log(bytesFromArrayBuffer(e.data.result));
|
||||
finalizeTask(e.data.taskID, e.data.result);
|
||||
}, true);
|
||||
listener.addEventListener('error', function (e) {
|
||||
console.error(e);
|
||||
console.error('NaCl error', e);
|
||||
}, true);
|
||||
}
|
||||
|
||||
@ -543,57 +541,49 @@ angular.module('izhukov.utils', [])
|
||||
return {
|
||||
sha1Hash: function (bytes) {
|
||||
if (useSha1Crypto) {
|
||||
// We don't use buffer since typedArray.subarray(...).buffer gives the whole buffer and not sliced one. webCrypto.digest supports typed array
|
||||
var deferred = $q.defer(),
|
||||
buffer = bytes instanceof ArrayBuffer
|
||||
? bytes
|
||||
: bytesToArrayBuffer(bytes);
|
||||
|
||||
webCrypto.digest({name: 'SHA-1'}, buffer).then(function (digest) {
|
||||
deferred.resolve(bytesFromArrayBuffer(digest));
|
||||
bytesTyped = Array.isArray(bytes) ? convertToUint8Array(bytes) : bytes;
|
||||
// console.log(dT(), 'Native sha1 start');
|
||||
webCrypto.digest({name: 'SHA-1'}, bytesTyped).then(function (digest) {
|
||||
// console.log(dT(), 'Native sha1 done');
|
||||
deferred.resolve(digest);
|
||||
}, function (e) {
|
||||
console.error('Crypto digest error', e);
|
||||
useSha1Crypto = false;
|
||||
deferred.resolve(sha1Hash(bytes));
|
||||
deferred.resolve(sha1HashSync(bytes));
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
if (worker && false) { // due overhead for data transfer
|
||||
return performTaskWorker ('sha1-hash', {bytes: bytes});
|
||||
}
|
||||
return $timeout(function () {
|
||||
return sha1Hash(bytes);
|
||||
return sha1HashSync(bytes);
|
||||
});
|
||||
},
|
||||
aesEncrypt: function (bytes, keyBytes, ivBytes) {
|
||||
if (aesNaClEmbed) {
|
||||
// aesEncryptSync(bytes, keyBytes, ivBytes);
|
||||
return performTaskWorker('aes-encrypt', {
|
||||
bytes: bytes,
|
||||
keyBytes: bytesToArrayBuffer(keyBytes),
|
||||
ivBytes: bytesToArrayBuffer(ivBytes)
|
||||
}, aesNaClEmbed);
|
||||
}
|
||||
if (worker && false) { // due overhead for data transfer
|
||||
return performTaskWorker('aes-encrypt', {
|
||||
bytes: bytes,
|
||||
keyBytes: keyBytes,
|
||||
ivBytes: ivBytes
|
||||
bytes: addPadding(convertToArrayBuffer(bytes)),
|
||||
keyBytes: convertToArrayBuffer(keyBytes),
|
||||
ivBytes: convertToArrayBuffer(ivBytes)
|
||||
}, aesNaClEmbed);
|
||||
}
|
||||
return $timeout(function () {
|
||||
return aesEncrypt(bytes, keyBytes, ivBytes);
|
||||
return convertToArrayBuffer(aesEncryptSync(bytes, keyBytes, ivBytes));
|
||||
});
|
||||
},
|
||||
aesDecrypt: function (encryptedBytes, keyBytes, ivBytes) {
|
||||
if (worker && false) { // due overhead for data transfer
|
||||
if (aesNaClEmbed) {
|
||||
// aesDecryptSync(encryptedBytes, keyBytes, ivBytes);
|
||||
return performTaskWorker('aes-decrypt', {
|
||||
encryptedBytes: encryptedBytes,
|
||||
keyBytes: keyBytes,
|
||||
ivBytes: ivBytes
|
||||
});
|
||||
encryptedBytes: addPadding(convertToArrayBuffer(encryptedBytes)),
|
||||
keyBytes: convertToArrayBuffer(keyBytes),
|
||||
ivBytes: convertToArrayBuffer(ivBytes)
|
||||
}, aesNaClEmbed);
|
||||
}
|
||||
return $timeout(function () {
|
||||
return aesDecrypt(encryptedBytes, keyBytes, ivBytes);
|
||||
return convertToArrayBuffer(aesDecryptSync(encryptedBytes, keyBytes, ivBytes));
|
||||
});
|
||||
},
|
||||
factorize: function (bytes) {
|
||||
|
@ -170,6 +170,9 @@ TLSerialization.prototype.storeBytes = function (bytes, field) {
|
||||
}
|
||||
|
||||
TLSerialization.prototype.storeIntBytes = function (bytes, bits, field) {
|
||||
if (bytes instanceof ArrayBuffer) {
|
||||
bytes = new Uint8Array(bytes);
|
||||
}
|
||||
var len = bytes.length;
|
||||
if ((bits % 32) || (len * 8) != bits) {
|
||||
throw new Error('Invalid bits: ' + bits + ', ' + bytes.length);
|
||||
@ -178,13 +181,15 @@ TLSerialization.prototype.storeIntBytes = function (bytes, bits, field) {
|
||||
this.debug && console.log('>>>', bytesToHex(bytes), (field || '') + ':int' + bits);
|
||||
this.checkLength(len);
|
||||
|
||||
for (var i = 0; i < len; i++) {
|
||||
this.byteView[this.offset++] = bytes[i];
|
||||
}
|
||||
this.byteView.set(bytes, this.offset);
|
||||
this.offset += len;
|
||||
};
|
||||
|
||||
TLSerialization.prototype.storeRawBytes = function (bytes, field) {
|
||||
var len = bytes.byteLength || bytes.length;
|
||||
if (bytes instanceof ArrayBuffer) {
|
||||
bytes = new Uint8Array(bytes);
|
||||
}
|
||||
var len = bytes.length;
|
||||
|
||||
this.debug && console.log('>>>', bytesToHex(bytes), (field || ''));
|
||||
this.checkLength(len);
|
||||
@ -412,12 +417,18 @@ TLDeserialization.prototype.fetchBytes = function (field) {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
TLDeserialization.prototype.fetchIntBytes = function (bits, field) {
|
||||
TLDeserialization.prototype.fetchIntBytes = function (bits, typed, field) {
|
||||
if (bits % 32) {
|
||||
throw new Error('Invalid bits: ' + bits);
|
||||
}
|
||||
|
||||
var len = bits / 8;
|
||||
if (typed) {
|
||||
var result = this.byteView.subarray(this.offset, this.offset + len);
|
||||
this.offset += len;
|
||||
return result;
|
||||
}
|
||||
|
||||
var bytes = [];
|
||||
for (var i = 0; i < len; i++) {
|
||||
bytes.push(this.byteView[this.offset++]);
|
||||
@ -429,11 +440,18 @@ TLDeserialization.prototype.fetchIntBytes = function (bits, field) {
|
||||
};
|
||||
|
||||
|
||||
TLDeserialization.prototype.fetchRawBytes = function (len, field) {
|
||||
TLDeserialization.prototype.fetchRawBytes = function (len, typed, field) {
|
||||
if (len === false) {
|
||||
len = this.readInt((field || '') + '_length');
|
||||
}
|
||||
|
||||
if (typed) {
|
||||
var bytes = new Uint8Array(len);
|
||||
bytes.set(this.byteView.subarray(this.offset, this.offset + len));
|
||||
this.offset += len;
|
||||
return bytes;
|
||||
}
|
||||
|
||||
var bytes = [];
|
||||
for (var i = 0; i < len; i++) {
|
||||
bytes.push(this.byteView[this.offset++]);
|
||||
@ -448,9 +466,9 @@ TLDeserialization.prototype.fetchObject = function (type, field) {
|
||||
switch (type) {
|
||||
case 'int': return this.fetchInt(field);
|
||||
case 'long': return this.fetchLong(field);
|
||||
case 'int128': return this.fetchIntBytes(128, field);
|
||||
case 'int256': return this.fetchIntBytes(256, field);
|
||||
case 'int512': return this.fetchIntBytes(512, field);
|
||||
case 'int128': return this.fetchIntBytes(128, false, field);
|
||||
case 'int256': return this.fetchIntBytes(256, false, field);
|
||||
case 'int512': return this.fetchIntBytes(512, false, field);
|
||||
case 'string': return this.fetchString(field);
|
||||
case 'bytes': return this.fetchBytes(field);
|
||||
case 'double': return this.fetchDouble(field);
|
||||
|
@ -48,16 +48,7 @@ clean:
|
||||
|
||||
mtproto_crypto.bc: mtproto_crypto.cc aes_core.c aes_ige.c aes_misc.c
|
||||
$(PNACL_CXX) -o $@ $^ -O2 $(CXXFLAGS) $(LDFLAGS)
|
||||
# $(PNACL_CXX) -g -o $@ $^ $(CXXFLAGS) $(LDFLAGS)
|
||||
|
||||
mtproto_crypto.pexe: mtproto_crypto.bc
|
||||
$(PNACL_FINALIZE) -o $@ $<
|
||||
|
||||
|
||||
#
|
||||
# Makefile target to run the SDK's simple HTTP server and serve this example.
|
||||
#
|
||||
HTTPD_PY := python $(NACL_SDK_ROOT)/tools/httpd.py
|
||||
|
||||
.PHONY: serve
|
||||
serve: all
|
||||
$(HTTPD_PY) -C $(CURDIR)
|
||||
|
Binary file not shown.
@ -61,11 +61,6 @@ class MtprotoCryptoInstance : public pp::Instance {
|
||||
/// @param[in] var_message The message posted by the browser.
|
||||
virtual void HandleMessage(const pp::Var& var_message) {
|
||||
|
||||
// if (1) {
|
||||
// PostMessage(var_message);
|
||||
// return;
|
||||
// }
|
||||
|
||||
if (!var_message.is_dictionary()) {
|
||||
return;
|
||||
}
|
||||
@ -80,7 +75,7 @@ class MtprotoCryptoInstance : public pp::Instance {
|
||||
|
||||
int32_t intTaskID = varTaskID.AsInt();
|
||||
std::string strTask = varTask.AsString();
|
||||
pp::Var varResult;// = pp::Var::Var();
|
||||
pp::Var varResult;
|
||||
|
||||
if (strTask == "aes-encrypt") {
|
||||
pp::Var varData = request.Get(pp::Var::Var("bytes"));
|
||||
@ -95,21 +90,42 @@ class MtprotoCryptoInstance : public pp::Instance {
|
||||
pp::VarArrayBuffer abKey = pp::VarArrayBuffer::VarArrayBuffer(varKey);
|
||||
pp::VarArrayBuffer abIv = pp::VarArrayBuffer::VarArrayBuffer(varIv);
|
||||
|
||||
int length = abData.ByteLength();
|
||||
char* what = static_cast<char*>(abData.Map());
|
||||
char* keyBuff = static_cast<char*>(abKey.Map());
|
||||
char* ivBuff = static_cast<char*>(abIv.Map());
|
||||
int length = abData.ByteLength();
|
||||
|
||||
AES_KEY akey;
|
||||
AES_set_encrypt_key((const unsigned char *) keyBuff, 32 * 8, &akey);
|
||||
AES_ige_encrypt((const unsigned char *)what, (unsigned char *)what, length, &akey, (unsigned char *)ivBuff, AES_ENCRYPT);
|
||||
|
||||
varResult = abData;
|
||||
|
||||
}
|
||||
else if (strTask == "aes-decrypt") {
|
||||
pp::Var varData = request.Get(pp::Var::Var("encryptedBytes"));
|
||||
pp::Var varKey = request.Get(pp::Var::Var("keyBytes"));
|
||||
pp::Var varIv = request.Get(pp::Var::Var("ivBytes"));
|
||||
|
||||
if (!varData.is_array_buffer() || !varKey.is_array_buffer() || !varIv.is_array_buffer()) {
|
||||
return;
|
||||
}
|
||||
|
||||
pp::VarArrayBuffer abData = pp::VarArrayBuffer::VarArrayBuffer(varData);
|
||||
pp::VarArrayBuffer abKey = pp::VarArrayBuffer::VarArrayBuffer(varKey);
|
||||
pp::VarArrayBuffer abIv = pp::VarArrayBuffer::VarArrayBuffer(varIv);
|
||||
|
||||
char* what = static_cast<char*>(abData.Map());
|
||||
char* keyBuff = static_cast<char*>(abKey.Map());
|
||||
char* ivBuff = static_cast<char*>(abIv.Map());
|
||||
int length = abData.ByteLength();
|
||||
|
||||
AES_KEY akey;
|
||||
AES_set_decrypt_key((const unsigned char *) keyBuff, 32 * 8, &akey);
|
||||
AES_ige_encrypt((const unsigned char *)what, (unsigned char *)what, length, &akey, (unsigned char *)ivBuff, AES_DECRYPT);
|
||||
|
||||
// varResult = pp::Var::Var(what);
|
||||
// varResult = pp::VarArrayBuffer::VarArrayBuffer(pp::Var::Var(what));
|
||||
abData.Unmap();
|
||||
varResult = abData;
|
||||
// varResult = pp::VarArrayBuffer::VarArrayBuffer();
|
||||
// pp::VarArrayBuffer varResult(what);
|
||||
|
||||
} else {
|
||||
varResult = pp::Var::Var();
|
||||
}
|
||||
@ -119,13 +135,6 @@ class MtprotoCryptoInstance : public pp::Instance {
|
||||
response.Set(pp::Var::Var("result"), varResult);
|
||||
|
||||
PostMessage(response);
|
||||
|
||||
// std::string message = var_message.AsString();
|
||||
// pp::Var var_reply;
|
||||
// if (message == kHelloString) {
|
||||
// var_reply = pp::Var(kReplyString);
|
||||
// PostMessage(var_reply);
|
||||
// }
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -2,8 +2,9 @@
|
||||
"program": {
|
||||
"portable": {
|
||||
"pnacl-translate": {
|
||||
"url": "mtproto_crypto.pexe?13"
|
||||
"url": "mtproto_crypto.pexe?55",
|
||||
"optlevel": 2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user