Migrate AES to Native Client

Up to 4x faster upload files
Up to 2x faster download files
This commit is contained in:
Igor Zhukov 2014-10-24 22:37:43 +04:00
parent 2c49f28b7d
commit 5661533fc4
11 changed files with 218 additions and 163 deletions

View File

@ -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;
}

View File

@ -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:

View File

@ -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');
}

View File

@ -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) {

View File

@ -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);

View File

@ -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.

View File

@ -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);
// }
}
};

View File

@ -2,8 +2,9 @@
"program": {
"portable": {
"pnacl-translate": {
"url": "mtproto_crypto.pexe?13"
"url": "mtproto_crypto.pexe?55",
"optlevel": 2
}
}
}
}
}

Binary file not shown.

View File

@ -89,7 +89,8 @@ StaticServlet.MimeMap = {
  'svg': 'image/svg+xml',
  'wav': 'audio/wav',
'ico': 'image/vnd.microsoft.icon',
'pexe': 'application/x-pnacl'
'pexe': 'application/x-pnacl',
'bc': 'application/x-pnacl'
};
StaticServlet.prototype.handleRequest = function(req, res) {