From 074e70457c90344b3c1cb236105638d692a0066b Mon Sep 17 00:00:00 2001 From: Giulio Cesare Solaroli Date: Fri, 19 Apr 2013 15:09:28 +0000 Subject: Fixed an issue on the AES-CTR block mode The previous version of the CTR encoding was incrementing the counter in a weird way, mixing up data from the previous block. The current fix can correctly decrypt data encoded with AES-CTR using other libraries/languages (currently tested only with Python). --- (limited to 'frontend/beta') diff --git a/frontend/beta/js/Clipperz/Crypto/AES_2.js b/frontend/beta/js/Clipperz/Crypto/AES_2.js new file mode 100644 index 0000000..9735d17 --- a/dev/null +++ b/frontend/beta/js/Clipperz/Crypto/AES_2.js @@ -0,0 +1,829 @@ +/* + +Copyright 2008-2013 Clipperz Srl + +This file is part of Clipperz, the online password manager. +For further information about its features and functionalities please +refer to http://www.clipperz.com. + +* Clipperz is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + +* Clipperz is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Affero General Public License for more details. + +* You should have received a copy of the GNU Affero General Public + License along with Clipperz. If not, see http://www.gnu.org/licenses/. + +*/ + +try { if (typeof(Clipperz.ByteArray) == 'undefined') { throw ""; }} catch (e) { + throw "Clipperz.Crypto.AES_2 depends on Clipperz.ByteArray!"; +} + +// Dependency commented to avoid a circular reference +//try { if (typeof(Clipperz.Crypto.PRNG) == 'undefined') { throw ""; }} catch (e) { +// throw "Clipperz.Crypto.AES_2 depends on Clipperz.Crypto.PRNG!"; +//} + +if (typeof(Clipperz.Crypto.AES_2) == 'undefined') { Clipperz.Crypto.AES_2 = {}; } + +//############################################################################# + +Clipperz.Crypto.AES_2.DeferredExecutionContext = function(args) { + args = args || {}; + + this._key = args.key; + this._message = args.message; + this._result = args.message.clone(); + this._nonce = args.nonce; + this._messageLength = this._message.length(); + + this._messageArray = this._message.arrayValues(); + this._resultArray = this._result.arrayValues(); + this._nonceArray = this._nonce.arrayValues(); + + this._executionStep = 0; + + return this; +} + +Clipperz.Crypto.AES_2.DeferredExecutionContext.prototype = MochiKit.Base.update(null, { + + 'key': function() { + return this._key; + }, + + 'message': function() { + return this._message; + }, + + 'messageLength': function() { + return this._messageLength; + }, + + 'result': function() { + return new Clipperz.ByteArray(this.resultArray()); + }, + + 'nonce': function() { + return this._nonce; + }, + + 'messageArray': function() { + return this._messageArray; + }, + + 'resultArray': function() { + return this._resultArray; + }, + + 'nonceArray': function() { + return this._nonceArray; + }, + + 'elaborationChunkSize': function() { + return Clipperz.Crypto.AES_2.DeferredExecution.chunkSize; + }, + + 'executionStep': function() { + return this._executionStep; + }, + + 'setExecutionStep': function(aValue) { + this._executionStep = aValue; + }, + + 'pause': function(aValue) { + return MochiKit.Async.wait(Clipperz.Crypto.AES_2.DeferredExecution.pauseTime, aValue); + }, + + //----------------------------------------------------------------------------- + __syntaxFix__: "syntax fix" + +}); + +//############################################################################# + +Clipperz.Crypto.AES_2.Key = function(args) { + args = args || {}; + + this._key = args.key; + this._keySize = args.keySize || this.key().length(); + + if (this.keySize() == 128/8) { + this._b = 176; + this._numberOfRounds = 10; + } else if (this.keySize() == 256/8) { + this._b = 240; + this._numberOfRounds = 14; + } else { + MochiKit.Logging.logError("AES unsupported key size: " + (this.keySize() * 8) + " bits"); + throw Clipperz.Crypto.AES_2.exception.UnsupportedKeySize; + } + + this._stretchedKey = null; + + return this; +} + +Clipperz.Crypto.AES_2.Key.prototype = MochiKit.Base.update(null, { + + 'asString': function() { + return "Clipperz.Crypto.AES_2.Key (" + this.key().toHexString() + ")"; + }, + + //----------------------------------------------------------------------------- + + 'key': function() { + return this._key; + }, + + 'keySize': function() { + return this._keySize; + }, + + 'b': function() { + return this._b; + }, + + 'numberOfRounds': function() { + return this._numberOfRounds; + }, + //========================================================================= + + 'keyScheduleCore': function(aWord, aRoundConstantsIndex) { + var result; + var sbox; + + sbox = Clipperz.Crypto.AES_2.sbox(); + + result = [ sbox[aWord[1]] ^ Clipperz.Crypto.AES_2.roundConstants()[aRoundConstantsIndex], + sbox[aWord[2]], + sbox[aWord[3]], + sbox[aWord[0]] ]; + + return result; + }, + + //----------------------------------------------------------------------------- + + 'xorWithPreviousStretchValues': function(aKey, aWord, aPreviousWordIndex) { + var result; + var i,c; + + result = []; + c = 4; + for (i=0; i 5 9 13 1 + // 2 6 10 14 10 14 2 6 + // 3 7 11 15 15 3 7 11 + // + '_shiftRowMapping': null, + 'shiftRowMapping': function() { + if (Clipperz.Crypto.AES_2._shiftRowMapping == null) { + Clipperz.Crypto.AES_2._shiftRowMapping = [0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, 1, 6, 11]; + } + + return Clipperz.Crypto.AES_2._shiftRowMapping; + }, + + //----------------------------------------------------------------------------- + + '_mixColumnsMatrix': null, + 'mixColumnsMatrix': function() { + if (Clipperz.Crypto.AES_2._mixColumnsMatrix == null) { + Clipperz.Crypto.AES_2._mixColumnsMatrix = [ [2, 3, 1 ,1], + [1, 2, 3, 1], + [1, 1, 2, 3], + [3, 1, 1, 2] ]; + } + + return Clipperz.Crypto.AES_2._mixColumnsMatrix; + }, + + '_roundConstants': null, + 'roundConstants': function() { + if (Clipperz.Crypto.AES_2._roundConstants == null) { + Clipperz.Crypto.AES_2._roundConstants = [ , 1, 2, 4, 8, 16, 32, 64, 128, 27, 54, 108, 216, 171, 77, 154]; +// Clipperz.Crypto.AES_2._roundConstants = [ , 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a]; + } + + return Clipperz.Crypto.AES_2._roundConstants; + }, + + //============================================================================= + + 'incrementNonce': function(nonce) { + var i; + var done; + + done = false; + i = nonce.length - 1; + + while ((i>=0) && (done == false)) { + var currentByteValue; + + currentByteValue = nonce[i]; + + if (currentByteValue == 0xff) { + nonce[i] = 0; + if (i>= 0) { + i --; + } else { + done = true; + } + } else { + nonce[i] = currentByteValue + 1; + done = true; + } + } + }, + + //----------------------------------------------------------------------------- + + 'encryptBlock': function(aKey, aBlock) { + var result; + var state; + + state = new Clipperz.Crypto.AES_2.State({block:aBlock, key:aKey}); +//is(state.data(), 'before'); + state.encrypt(); + result = state.data(); + + return result; + }, + + //----------------------------------------------------------------------------- + + 'encryptBlocks': function(aKey, aMessage, aNonce) { + var result; + var nonce; + var self; + var messageIndex; + var messageLength; + var blockSize; + + self = Clipperz.Crypto.AES_2; + blockSize = 128/8; + messageLength = aMessage.length; + nonce = aNonce; + + result = aMessage; + messageIndex = 0; + while (messageIndex < messageLength) { + var encryptedBlock; + var i,c; + + encryptedBlock = self.encryptBlock(aKey, nonce); + + if ((messageLength - messageIndex) > blockSize) { + c = blockSize; + } else { + c = messageLength - messageIndex; + } + + for (i=0; i blockSize) { + c = blockSize; + } else { + c = executionLimit - messageIndex; + } + + for (i=0; i>> [" + (new Date()).valueOf() + "] Clipperz.PM.Crypto.versions[0.3].encrypt"); key = Clipperz.Crypto.SHA.sha_d256(new Clipperz.ByteArray(aKey)); -//MochiKit.Logging.logDebug("--- [" + (new Date()).valueOf() + "] Clipperz.PM.Crypto.versions[0.3].encrypt - 1"); value = Clipperz.Base.serializeJSON(aValue); -//MochiKit.Logging.logDebug("--- [" + (new Date()).valueOf() + "] Clipperz.PM.Crypto.versions[0.3].encrypt - 2"); -/ * -//MochiKit.Logging.logDebug("--> encrypt.fullSize: " + value.length); - value = value.replace(/":{"label":"/g, '":{l:"'); - value = value.replace(/":{"key":"/g, '":{k:"'); - value = value.replace(/":{"notes":"/g, '":{n:"'); - value = value.replace(/":{"record":"/g, '":{r:"'); - value = value.replace(/", "label":"/g, '",l:"'); - value = value.replace(/", "favicon":"/g, '",f:"'); -//MochiKit.Logging.logDebug("<-- encrypt.compressed: " + value.length); -* / data = new Clipperz.ByteArray(value); -//MochiKit.Logging.logDebug("--- [" + (new Date()).valueOf() + "] Clipperz.PM.Crypto.versions[0.3].encrypt - 3"); - encryptedData = Clipperz.Crypto.AES.encrypt(key, data, aNonce); -//MochiKit.Logging.logDebug("--- [" + (new Date()).valueOf() + "] Clipperz.PM.Crypto.versions[0.3].encrypt - 4"); + encryptedData = Clipperz.Crypto.AES_2.encrypt(key, data, aNonce); result = encryptedData.toBase64String(); -//MochiKit.Logging.logDebug("<<< [" + (new Date()).valueOf() + "] Clipperz.PM.Crypto.versions[0.3].encrypt"); return result; }, + + 'deferredEncrypt': function(aKey, aValue, aNonce) { + var deferredResult; + var key, value; + var data; + var dataToEncrypt; + var encryptedData; + + key = Clipperz.Crypto.SHA.sha_d256(new Clipperz.ByteArray(aKey)); + value = Clipperz.Base.serializeJSON(aValue); + data = new Clipperz.ByteArray(value); + + deferredResult = new MochiKit.Async.Deferred() + deferredResult.addCallback(Clipperz.Crypto.AES_2.deferredEncrypt, key, data, aNonce); + deferredResult.addCallback(function(aResult) { + return aResult.toBase64String(); + }) + deferredResult.callback(); + + return deferredResult; + }, 'decrypt': function(aKey, aValue) { var result; @@ -385,25 +390,15 @@ MochiKit.Base.update(Clipperz.PM.Crypto, { key = Clipperz.Crypto.SHA.sha_d256(new Clipperz.ByteArray(aKey)); value = new Clipperz.ByteArray().appendBase64String(aValue); - decryptedData = Clipperz.Crypto.AES.decrypt(key, value); + decryptedData = Clipperz.Crypto.AES_2.decrypt(key, value); value = decryptedData.asString(); -/ * - value = value.replace(/":{l:"/g, '":{"label":"'); - value = value.replace(/":{k:"/g, '":{"key":"'); - value = value.replace(/":{n:"/g, '":{"notes":"'); - value = value.replace(/":{r:"/g, '":{"record":"'); - value = value.replace(/",l:"/g, '", "label":"'); - value = value.replace(/",f:"/g, '", "favicon":"'); -* / try { result = Clipperz.Base.evalJSON(value); } catch (exception) { MochiKit.Logging.logError("Error while decrypting data"); throw Clipperz.Crypto.Base.exception.CorruptedMessage; } - - } else { result = null; } @@ -411,9 +406,41 @@ MochiKit.Base.update(Clipperz.PM.Crypto, { return result; }, + 'deferredDecrypt': function(aKey, aValue) { + var deferredResult; + + deferredResult = new MochiKit.Async.Deferred(); + if (aValue != null) { + var key, value; + var decryptedData; + var decryptedValue; + + key = Clipperz.Crypto.SHA.sha_d256(new Clipperz.ByteArray(aKey)); + value = new Clipperz.ByteArray().appendBase64String(aValue); + deferredResult.addCallback(Clipperz.Crypto.AES_2.deferredDecrypt, key, value); + deferredResult.addCallback(MochiKit.Async.wait, 0.1); + deferredResult.addCallback(function(aResult) { + return aResult.asString(); + }); + deferredResult.addCallback(MochiKit.Async.wait, 0.1); + deferredResult.addCallback(Clipperz.Base.evalJSON); + deferredResult.addErrback(function(anError) { + MochiKit.Logging.logError("Error while decrypting data"); + throw Clipperz.Crypto.Base.exception.CorruptedMessage; + }) + } else { + deferredResult.addCallback(function() { + return null; + }); + } + deferredResult.callback(); + + return deferredResult; + }, + 'hash': Clipperz.Crypto.SHA.sha_d256 }, -*/ + //##################################################################### __syntaxFix__: "syntax fix" } diff --git a/frontend/beta/properties/beta.properties.json b/frontend/beta/properties/beta.properties.json index bfa152d..7d34677 100644 --- a/frontend/beta/properties/beta.properties.json +++ b/frontend/beta/properties/beta.properties.json @@ -77,6 +77,7 @@ "Clipperz/NotificationCenter.js", "Clipperz/Crypto/SHA.js", "Clipperz/Crypto/AES.js", + "Clipperz/Crypto/AES_2.js", "Clipperz/Crypto/PRNG.js", "Clipperz/Crypto/BigInt.js", "Clipperz/Crypto/Base.js", -- cgit v0.9.0.2