summaryrefslogtreecommitdiff
path: root/frontend/beta
authorGiulio Cesare Solaroli <giulio.cesare@clipperz.com>2013-04-19 15:09:28 (UTC)
committer Giulio Cesare Solaroli <giulio.cesare@clipperz.com>2013-04-19 15:09:28 (UTC)
commit074e70457c90344b3c1cb236105638d692a0066b (patch) (side-by-side diff)
treec5ffabd3eaf74cbeb69974beacdb5a5f8c235adc /frontend/beta
parent48c9280c9a255f2a85ad5729830df884e64a9c5d (diff)
downloadclipperz-074e70457c90344b3c1cb236105638d692a0066b.zip
clipperz-074e70457c90344b3c1cb236105638d692a0066b.tar.gz
clipperz-074e70457c90344b3c1cb236105638d692a0066b.tar.bz2
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).
Diffstat (limited to 'frontend/beta') (more/less context) (ignore whitespace changes)
-rw-r--r--frontend/beta/js/Clipperz/Crypto/AES_2.js829
-rw-r--r--frontend/beta/js/Clipperz/PM/Crypto.js91
-rw-r--r--frontend/beta/properties/beta.properties.json1
3 files changed, 889 insertions, 32 deletions
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<c; i++) {
+ result[i] = aWord[i] ^ aKey.byteAtIndex(aPreviousWordIndex + i);
+ }
+
+ return result;
+ },
+
+ //-----------------------------------------------------------------------------
+
+ 'sboxShakeup': function(aWord) {
+ var result;
+ var sbox;
+ var i,c;
+
+ result = [];
+ sbox = Clipperz.Crypto.AES_2.sbox();
+ c =4;
+ for (i=0; i<c; i++) {
+ result[i] = sbox[aWord[i]];
+ }
+
+ return result;
+ },
+
+ //-----------------------------------------------------------------------------
+
+ 'stretchKey': function(aKey) {
+ var currentWord;
+ var keyLength;
+ var previousStretchIndex;
+ var i,c;
+
+ keyLength = aKey.length();
+ previousStretchIndex = keyLength - this.keySize();
+
+ currentWord = [ aKey.byteAtIndex(keyLength - 4),
+ aKey.byteAtIndex(keyLength - 3),
+ aKey.byteAtIndex(keyLength - 2),
+ aKey.byteAtIndex(keyLength - 1) ];
+ currentWord = this.keyScheduleCore(currentWord, keyLength / this.keySize());
+
+ if (this.keySize() == 256/8) {
+ c = 8;
+ } else if (this.keySize() == 128/8){
+ c = 4;
+ }
+
+ for (i=0; i<c; i++) {
+ if (i == 4) {
+ // fifth streatch word
+ currentWord = this.sboxShakeup(currentWord);
+ }
+
+ currentWord = this.xorWithPreviousStretchValues(aKey, currentWord, previousStretchIndex + (i*4));
+ aKey.appendBytes(currentWord);
+ }
+
+ return aKey;
+ },
+
+ //-----------------------------------------------------------------------------
+
+ 'stretchedKey': function() {
+ if (this._stretchedKey == null) {
+ var stretchedKey;
+
+ stretchedKey = this.key().clone();
+
+ while (stretchedKey.length() < this.keySize()) {
+ stretchedKey.appendByte(0);
+ }
+
+ while (stretchedKey.length() < this.b()) {
+ stretchedKey = this.stretchKey(stretchedKey);
+ }
+
+ this._stretchedKey = stretchedKey.split(0, this.b());
+ }
+
+ return this._stretchedKey;
+ },
+
+ //=========================================================================
+ __syntaxFix__: "syntax fix"
+});
+
+//#############################################################################
+
+Clipperz.Crypto.AES_2.State = function(args) {
+ args = args || {};
+
+ this._data = args.block.slice(0);
+ this._key = args.key;
+
+ return this;
+}
+
+Clipperz.Crypto.AES_2.State.prototype = MochiKit.Base.update(null, {
+
+ 'key': function() {
+ return this._key;
+ },
+
+ //-----------------------------------------------------------------------------
+
+ 'data': function() {
+ return this._data;
+ },
+
+ 'setData': function(aValue) {
+ this._data = aValue;
+ },
+
+ //=========================================================================
+
+ 'addRoundKey': function(aRoundNumber) {
+ // each byte of the state is combined with the round key; each round key is derived from the cipher key using a key schedule.
+ var data;
+ var stretchedKey;
+ var firstStretchedKeyIndex;
+ var i,c;
+
+ data = this.data();
+ stretchedKey = this.key().stretchedKey();
+ firstStretchedKeyIndex = aRoundNumber * (128/8);
+ c = 128/8;
+ for (i=0; i<c; i++) {
+ data[i] = data[i] ^ stretchedKey.byteAtIndex(firstStretchedKeyIndex + i);
+ }
+ },
+
+ //-----------------------------------------------------------------------------
+
+ 'subBytes': function() {
+ // a non-linear substitution step where each byte is replaced with another according to a lookup table.
+ var i,c;
+ var data;
+ var sbox;
+
+ data = this.data();
+ sbox = Clipperz.Crypto.AES_2.sbox();
+
+ c = 16;
+ for (i=0; i<c; i++) {
+ data[i] = sbox[data[i]];
+ }
+ },
+
+ //-----------------------------------------------------------------------------
+
+ 'shiftRows': function() {
+ // a transposition step where each row of the state is shifted cyclically a certain number of steps.
+ var newValue;
+ var data;
+ var shiftMapping;
+ var i,c;
+
+ newValue = new Array(16);
+ data = this.data();
+ shiftMapping = Clipperz.Crypto.AES_2.shiftRowMapping();
+// [0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, 1, 6, 11];
+ c = 16;
+ for (i=0; i<c; i++) {
+ newValue[i] = data[shiftMapping[i]];
+ }
+ for (i=0; i<c; i++) {
+ data[i] = newValue[i];
+ }
+ },
+
+ //-----------------------------------------------------------------------------
+/*
+ 'mixColumnsWithValues': function(someValues) {
+ var result;
+ var a;
+ var i,c;
+
+ c = 4;
+ result = [];
+ a = [];
+ for (i=0; i<c; i++) {
+ a[i] = [];
+ a[i][1] = someValues[i]
+ if ((a[i][1] & 0x80) == 0x80) {
+ a[i][2] = (a[i][1] << 1) ^ 0x11b;
+ } else {
+ a[i][2] = a[i][1] << 1;
+ }
+
+ a[i][3] = a[i][2] ^ a[i][1];
+ }
+
+ for (i=0; i<c; i++) {
+ var x;
+
+ x = Clipperz.Crypto.AES_2.mixColumnsMatrix()[i];
+ result[i] = a[0][x[0]] ^ a[1][x[1]] ^ a[2][x[2]] ^ a[3][x[3]];
+ }
+
+ return result;
+ },
+
+ 'mixColumns': function() {
+ // a mixing operation which operates on the columns of the state, combining the four bytes in each column using a linear transformation.
+ var data;
+ var i, c;
+
+ data = this.data();
+ c = 4;
+ for(i=0; i<c; i++) {
+ var blockIndex;
+ var mixedValues;
+
+ blockIndex = i * 4;
+ mixedValues = this.mixColumnsWithValues([ data[blockIndex + 0],
+ data[blockIndex + 1],
+ data[blockIndex + 2],
+ data[blockIndex + 3]]);
+ data[blockIndex + 0] = mixedValues[0];
+ data[blockIndex + 1] = mixedValues[1];
+ data[blockIndex + 2] = mixedValues[2];
+ data[blockIndex + 3] = mixedValues[3];
+ }
+ },
+*/
+
+ 'mixColumns': function() {
+ // a mixing operation which operates on the columns of the state, combining the four bytes in each column using a linear transformation.
+ var data;
+ var i, c;
+ var a_1;
+ var a_2;
+
+ a_1 = new Array(4);
+ a_2 = new Array(4);
+
+ data = this.data();
+ c = 4;
+ for(i=0; i<c; i++) {
+ var blockIndex;
+ var ii, cc;
+
+ blockIndex = i * 4;
+
+ cc = 4;
+ for (ii=0; ii<cc; ii++) {
+ var value;
+
+ value = data[blockIndex + ii];
+ a_1[ii] = value;
+ a_2[ii] = (value & 0x80) ? ((value << 1) ^ 0x011b) : (value << 1);
+ }
+
+ data[blockIndex + 0] = a_2[0] ^ a_1[1] ^ a_2[1] ^ a_1[2] ^ a_1[3];
+ data[blockIndex + 1] = a_1[0] ^ a_2[1] ^ a_1[2] ^ a_2[2] ^ a_1[3];
+ data[blockIndex + 2] = a_1[0] ^ a_1[1] ^ a_2[2] ^ a_1[3] ^ a_2[3];
+ data[blockIndex + 3] = a_1[0] ^ a_2[0] ^ a_1[1] ^ a_1[2] ^ a_2[3];
+ }
+ },
+
+ //=========================================================================
+
+ 'spinRound': function(aRoundNumber) {
+ this.addRoundKey(aRoundNumber);
+ this.subBytes();
+ this.shiftRows();
+ this.mixColumns();
+ },
+
+ 'spinLastRound': function() {
+ this.addRoundKey(this.key().numberOfRounds() - 1);
+ this.subBytes();
+ this.shiftRows();
+ this.addRoundKey(this.key().numberOfRounds());
+ },
+
+ //=========================================================================
+
+ 'encrypt': function() {
+ var i,c;
+
+ c = this.key().numberOfRounds() - 1;
+ for (i=0; i<c; i++) {
+ this.spinRound(i);
+ }
+
+ this.spinLastRound();
+ },
+
+ //=========================================================================
+ __syntaxFix__: "syntax fix"
+});
+
+//#############################################################################
+
+Clipperz.Crypto.AES_2.VERSION = "0.1";
+Clipperz.Crypto.AES_2.NAME = "Clipperz.Crypto.AES_2";
+
+MochiKit.Base.update(Clipperz.Crypto.AES_2, {
+
+// http://www.cs.eku.edu/faculty/styer/460/Encrypt/JS-AES.html
+// http://en.wikipedia.org/wiki/Advanced_Encryption_Standard
+// http://en.wikipedia.org/wiki/Rijndael_key_schedule
+// http://en.wikipedia.org/wiki/Rijndael_S-box
+
+ '__repr__': function () {
+ return "[" + this.NAME + " " + this.VERSION + "]";
+ },
+
+ 'toString': function () {
+ return this.__repr__();
+ },
+
+ //=============================================================================
+
+ '_sbox': null,
+ 'sbox': function() {
+ if (Clipperz.Crypto.AES_2._sbox == null) {
+ Clipperz.Crypto.AES_2._sbox = [
+0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
+0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
+0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
+0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
+0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
+0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
+0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
+0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
+0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
+0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
+0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
+0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
+0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
+0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
+0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
+0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
+ ];
+ }
+
+ return Clipperz.Crypto.AES_2._sbox;
+ },
+
+ //-----------------------------------------------------------------------------
+ //
+ // 0 4 8 12 0 4 8 12
+ // 1 5 9 13 => 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<c; i++) {
+ result[messageIndex + i] = result[messageIndex + i] ^ encryptedBlock[i];
+ }
+
+ messageIndex += blockSize;
+ self.incrementNonce(nonce);
+ }
+
+ return result;
+ },
+
+ //-----------------------------------------------------------------------------
+
+ 'encrypt': function(aKey, someData, aNonce) {
+ var result;
+ var nonce;
+ var encryptedData;
+ var key;
+
+ key = new Clipperz.Crypto.AES_2.Key({key:aKey});
+ nonce = aNonce ? aNonce.clone() : Clipperz.Crypto.PRNG.defaultRandomGenerator().getRandomBytes(128/8);
+
+ encryptedData = Clipperz.Crypto.AES_2.encryptBlocks(key, someData.arrayValues(), nonce.arrayValues());
+
+ result = nonce.appendBytes(encryptedData);
+
+ return result;
+ },
+
+ //-----------------------------------------------------------------------------
+
+ 'decrypt': function(aKey, someData) {
+ var result;
+ var nonce;
+ var encryptedData;
+ var decryptedData;
+ var dataIterator;
+ var key;
+
+ key = new Clipperz.Crypto.AES_2.Key({key:aKey});
+
+ encryptedData = someData.arrayValues();
+ nonce = encryptedData.slice(0, (128/8));
+ encryptedData = encryptedData.slice(128/8);
+ decryptedData = Clipperz.Crypto.AES_2.encryptBlocks(key, encryptedData, nonce);
+
+ result = new Clipperz.ByteArray(decryptedData);
+
+ return result;
+ },
+
+ //=============================================================================
+
+ 'deferredEncryptExecutionChunk': function(anExecutionContext) {
+ var result;
+ var nonce;
+ var self;
+ var messageIndex;
+ var messageLength;
+ var blockSize;
+ var executionLimit;
+
+ self = Clipperz.Crypto.AES_2;
+ blockSize = 128/8;
+ messageLength = anExecutionContext.messageArray().length;
+ nonce = anExecutionContext.nonceArray();
+ result = anExecutionContext.resultArray();
+
+ messageIndex = anExecutionContext.executionStep();
+ executionLimit = messageIndex + anExecutionContext.elaborationChunkSize();
+ executionLimit = Math.min(executionLimit, messageLength);
+
+ while (messageIndex < executionLimit) {
+ var encryptedBlock;
+ var i,c;
+
+ encryptedBlock = self.encryptBlock(anExecutionContext.key(), nonce);
+
+ if ((executionLimit - messageIndex) > blockSize) {
+ c = blockSize;
+ } else {
+ c = executionLimit - messageIndex;
+ }
+
+ for (i=0; i<c; i++) {
+ result[messageIndex + i] = result[messageIndex + i] ^ encryptedBlock[i];
+ }
+
+ messageIndex += blockSize;
+ self.incrementNonce(nonce);
+ }
+ anExecutionContext.setExecutionStep(messageIndex);
+
+ return anExecutionContext;
+ },
+
+ //-----------------------------------------------------------------------------
+
+ 'deferredEncryptBlocks': function(anExecutionContext) {
+ var deferredResult;
+ var messageSize;
+ var i,c;
+ var now;
+
+ messageSize = anExecutionContext.messageLength();
+
+ deferredResult = new MochiKit.Async.Deferred();
+//deferredResult.addBoth(function(res) {MochiKit.Logging.logDebug("Clipperz.Crypto.AES_2.deferredEncryptBlocks - START: " + res); return res;});
+// deferredResult.addCallback(MochiKit.Base.method(anExecutionContext, 'pause'));
+
+ c = Math.ceil(messageSize / anExecutionContext.elaborationChunkSize());
+ for (i=0; i<c; i++) {
+//deferredResult.addBoth(function(res) {now = new Date(); return res;});
+//deferredResult.addBoth(function(res) {MochiKit.Logging.logDebug("Clipperz.Crypto.AES_2.deferredEncryptBlocks - : (" + i + ") - " + res); return res;});
+ deferredResult.addCallback(Clipperz.Crypto.AES_2.deferredEncryptExecutionChunk);
+//deferredResult.addBoth(function(res) {MochiKit.Logging.logDebug("[" + (new Date() - now) + "]Clipperz.Crypto.AES_2.deferredEncryptBlocks"); return res;});
+//deferredResult.addBoth(function(res) {MochiKit.Logging.logDebug("Clipperz.Crypto.AES_2.deferredEncryptBlocks - : (" + i + ") -- " + res); return res;});
+ deferredResult.addCallback(MochiKit.Base.method(anExecutionContext, 'pause'));
+//deferredResult.addBoth(function(res) {MochiKit.Logging.logDebug("Clipperz.Crypto.AES_2.deferredEncryptBlocks - : (" + i + ") --- " + res); return res;});
+ }
+//deferredResult.addBoth(function(res) {MochiKit.Logging.logDebug("Clipperz.Crypto.AES_2.deferredEncryptBlocks - END: " + res); return res;});
+
+ deferredResult.callback(anExecutionContext);
+
+ return deferredResult;
+ },
+
+ //-----------------------------------------------------------------------------
+
+ 'deferredEncrypt': function(aKey, someData, aNonce) {
+ var deferredResult;
+ var executionContext;
+ var result;
+ var nonce;
+ var key;
+
+ key = new Clipperz.Crypto.AES_2.Key({key:aKey});
+ nonce = aNonce ? aNonce.clone() : Clipperz.Crypto.PRNG.defaultRandomGenerator().getRandomBytes(128/8);
+
+ executionContext = new Clipperz.Crypto.AES_2.DeferredExecutionContext({key:key, message:someData, nonce:nonce});
+
+ deferredResult = new MochiKit.Async.Deferred();
+//deferredResult.addBoth(function(res) {MochiKit.Logging.logDebug("Clipperz.Crypto.AES_2.deferredEncrypt - 1: " + res); return res;});
+ deferredResult.addCallback(Clipperz.Crypto.AES_2.deferredEncryptBlocks);
+//deferredResult.addBoth(function(res) {MochiKit.Logging.logDebug("Clipperz.Crypto.AES_2.deferredEncrypt - 2: " + res); return res;});
+ deferredResult.addCallback(function(anExecutionContext) {
+ var result;
+
+ result = anExecutionContext.nonce().clone();
+ result.appendBytes(anExecutionContext.resultArray());
+
+ return result;
+ });
+//deferredResult.addBoth(function(res) {MochiKit.Logging.logDebug("Clipperz.Crypto.AES_2.deferredEncrypt - 3: " + res); return res;});
+ deferredResult.callback(executionContext)
+
+ return deferredResult;
+ },
+
+ //-----------------------------------------------------------------------------
+
+ 'deferredDecrypt': function(aKey, someData) {
+ var deferredResult
+ var nonce;
+ var message;
+ var key;
+
+ key = new Clipperz.Crypto.AES_2.Key({key:aKey});
+ nonce = someData.split(0, (128/8));
+ message = someData.split(128/8);
+ executionContext = new Clipperz.Crypto.AES_2.DeferredExecutionContext({key:key, message:message, nonce:nonce});
+
+ deferredResult = new MochiKit.Async.Deferred();
+ deferredResult.addCallback(Clipperz.Crypto.AES_2.deferredEncryptBlocks);
+ deferredResult.addCallback(function(anExecutionContext) {
+ return anExecutionContext.result();
+ });
+ deferredResult.callback(executionContext);
+
+ return deferredResult;
+ },
+
+ //-----------------------------------------------------------------------------
+ __syntaxFix__: "syntax fix"
+
+});
+
+//#############################################################################
+
+Clipperz.Crypto.AES_2.DeferredExecution = {
+ 'chunkSize': 4096, // 1024 4096 8192 16384 32768;
+ 'pauseTime': 0.2
+}
+
+Clipperz.Crypto.AES_2.exception = {
+ 'UnsupportedKeySize': new MochiKit.Base.NamedError("Clipperz.Crypto.AES_2.exception.UnsupportedKeySize")
+};
diff --git a/frontend/beta/js/Clipperz/PM/Crypto.js b/frontend/beta/js/Clipperz/PM/Crypto.js
index ad16ff0..e1e87ec 100644
--- a/frontend/beta/js/Clipperz/PM/Crypto.js
+++ b/frontend/beta/js/Clipperz/PM/Crypto.js
@@ -13,97 +13,97 @@ refer to http://www.clipperz.com.
* 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/.
*/
if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; }
if (typeof(Clipperz.PM.Crypto) == 'undefined') { Clipperz.PM.Crypto = {}; }
Clipperz.PM.Crypto.VERSION = "0.2";
Clipperz.PM.Crypto.NAME = "Clipperz.PM.Crypto";
MochiKit.Base.update(Clipperz.PM.Crypto, {
'__repr__': function () {
return "[" + this.NAME + " " + this.VERSION + "]";
},
//-------------------------------------------------------------------------
'toString': function () {
return this.__repr__();
},
//-------------------------------------------------------------------------
'communicationProtocol': {
'currentVersion': '0.2',
'versions': {
'0.1': Clipperz.PM.Connection.SRP['1.0'], //Clipperz.Crypto.SRP.versions['1.0'].Connection,
'0.2': Clipperz.PM.Connection.SRP['1.1'] //Clipperz.Crypto.SRP.versions['1.1'].Connection,
},
'fallbackVersions': {
'current': '0.1',
'0.2': '0.1',
'0.1': null
}
},
//-------------------------------------------------------------------------
'encryptingFunctions': {
- 'currentVersion': '0.3',
+ 'currentVersion': '0.4',
'versions': {
//#####################################################################
'0.1': {
'encrypt': function(aKey, aValue) {
return Clipperz.Crypto.Base.encryptUsingSecretKey(aKey, Clipperz.Base.serializeJSON(aValue));
},
'deferredEncrypt': function(aKey, aValue) {
var deferredResult;
deferredResult = new MochiKit.Async.Deferred();
deferredResult.addCallback(Clipperz.PM.Crypto.encryptingFunctions.versions['0.1'].encrypt, aKey, aValue);
deferredResult.callback();
return deferredResult;
},
'decrypt': function(aKey, aValue) {
var result;
if (aValue != null) {
result = Clipperz.Base.evalJSON(Clipperz.Crypto.Base.decryptUsingSecretKey(aKey, aValue));
} else {
result = null;
}
return result;
},
'deferredDecrypt': function(aKey, aValue) {
var deferredResult;
deferredResult = new MochiKit.Async.Deferred();
deferredResult.addCallback(Clipperz.PM.Crypto.encryptingFunctions.versions['0.1'].decrypt, aKey, aValue);
deferredResult.callback();
return deferredResult;
},
'hash': function(aValue) {
var result;
var strngResult;
stringResult = Clipperz.Crypto.Base.computeHashValue(aValue.asString()); // !!!!!!!
result = new Clipperz.ByteArray("0x" + stringResult);
@@ -250,215 +250,242 @@ MochiKit.Base.update(Clipperz.PM.Crypto, {
value = Clipperz.Base.serializeJSON(aValue);
data = new Clipperz.ByteArray(value);
deferredResult = new MochiKit.Async.Deferred()
//deferredResult.addBoth(function(res) {MochiKit.Logging.logDebug("Clipperz.PM.Crypto.deferredEncrypt - 1: " + res); return res;});
deferredResult.addCallback(Clipperz.Crypto.AES.deferredEncrypt, key, data, aNonce);
//deferredResult.addBoth(function(res) {MochiKit.Logging.logDebug("Clipperz.PM.Crypto.deferredEncrypt - 2: " + res); return res;});
deferredResult.addCallback(function(aResult) {
return aResult.toBase64String();
})
//deferredResult.addBoth(function(res) {MochiKit.Logging.logDebug("Clipperz.PM.Crypto.deferredEncrypt - 3: " + res); return res;});
deferredResult.callback();
return deferredResult;
},
'decrypt': function(aKey, aValue) {
var result;
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);
decryptedData = Clipperz.Crypto.AES.decrypt(key, value);
value = decryptedData.asString();
try {
result = Clipperz.Base.evalJSON(value);
} catch (exception) {
MochiKit.Logging.logError("Error while decrypting data");
throw Clipperz.Crypto.Base.exception.CorruptedMessage;
}
} else {
result = null;
}
return result;
},
'deferredDecrypt': function(aKey, aValue) {
var deferredResult;
// var now;
deferredResult = new MochiKit.Async.Deferred();
- now = new Date;
+// now = new Date;
//deferredResult.addBoth(function(res) {MochiKit.Logging.logDebug("[" + (new Date() - now) + "] Clipperz.PM.Crypto.deferredDecrypt - 1: " + res); return res;});
if (aValue != null) {
var key, value;
var decryptedData;
var decryptedValue;
key = Clipperz.Crypto.SHA.sha_d256(new Clipperz.ByteArray(aKey));
//MochiKit.Logging.logDebug("[" + (new Date() - now) + "] computed key");
value = new Clipperz.ByteArray().appendBase64String(aValue);
//MochiKit.Logging.logDebug("[" + (new Date() - now) + "] appendedBase64String");
//deferredResult.addBoth(function(res) {MochiKit.Logging.logDebug("[" + (new Date() - now) + "] Clipperz.PM.Crypto.deferredDecrypt - 1.1: " /* + res*/); return res;});
deferredResult.addCallback(Clipperz.Crypto.AES.deferredDecrypt, key, value);
//deferredResult.addBoth(function(res) {MochiKit.Logging.logDebug("[" + (new Date() - now) + "] Clipperz.PM.Crypto.deferredDecrypt - 2: " /* + res*/); return res;});
deferredResult.addCallback(MochiKit.Async.wait, 0.1);
//deferredResult.addBoth(function(res) {MochiKit.Logging.logDebug("[" + (new Date() - now) + "] Clipperz.PM.Crypto.deferredDecrypt - 3: " /* + res*/); return res;});
deferredResult.addCallback(function(aResult) {
return aResult.asString();
});
//deferredResult.addBoth(function(res) {MochiKit.Logging.logDebug("[" + (new Date() - now) + "] Clipperz.PM.Crypto.deferredDecrypt - 4: " /* + res*/); return res;});
deferredResult.addCallback(MochiKit.Async.wait, 0.1);
//deferredResult.addBoth(function(res) {MochiKit.Logging.logDebug("[" + (new Date() - now) + "] Clipperz.PM.Crypto.deferredDecrypt - 5: " /* + res*/); return res;});
deferredResult.addCallback(Clipperz.Base.evalJSON);
//deferredResult.addBoth(function(res) {MochiKit.Logging.logDebug("[" + (new Date() - now) + "] Clipperz.PM.Crypto.deferredDecrypt - 6: " /* + res*/); return res;});
deferredResult.addErrback(function(anError) {
MochiKit.Logging.logError("Error while decrypting data");
throw Clipperz.Crypto.Base.exception.CorruptedMessage;
})
//deferredResult.addBoth(function(res) {MochiKit.Logging.logDebug("[" + (new Date() - now) + "] Clipperz.PM.Crypto.deferredDecrypt - 7: " /* + res*/); return res;});
} else {
deferredResult.addCallback(function() {
return null;
});
}
deferredResult.callback();
return deferredResult;
},
'hash': Clipperz.Crypto.SHA.sha_d256
},
//#####################################################################
-/*
+
'0.4': {
'encrypt': function(aKey, aValue, aNonce) {
var result;
var key, value;
var data;
var dataToEncrypt;
var encryptedData;
-//MochiKit.Logging.logDebug(">>> [" + (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;
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);
- 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;
}
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"
}
},
//-------------------------------------------------------------------------
'encrypt': function(aKey, aValue, aVersion) {
return Clipperz.PM.Crypto.encryptingFunctions.versions[aVersion].encrypt(aKey, aValue);
},
'deferredEncrypt': function(aKey, aValue, aVersion) {
return Clipperz.PM.Crypto.encryptingFunctions.versions[aVersion].deferredEncrypt(aKey, aValue);
},
'encryptWithCurrentVersion': function(aKey, aValue) {
return Clipperz.PM.Crypto.encrypt(aKey, aValue, Clipperz.PM.Crypto.encryptingFunctions.currentVersion);
},
'deferredEncryptWithCurrentVersion': function(aKey, aValue) {
return Clipperz.PM.Crypto.deferredEncrypt(aKey, aValue, Clipperz.PM.Crypto.encryptingFunctions.currentVersion);
},
//.........................................................................
'decrypt': function(aKey, aValue, aVersion) {
return Clipperz.PM.Crypto.encryptingFunctions.versions[aVersion].decrypt(aKey, aValue);
},
'deferredDecrypt': function(aKey, aValue, aVersion) {
return Clipperz.PM.Crypto.encryptingFunctions.versions[aVersion].deferredDecrypt(aKey, aValue);
},
//-------------------------------------------------------------------------
'randomKey': function() {
return Clipperz.Crypto.PRNG.defaultRandomGenerator().getRandomBytes(32).toHexString().substring(2);
},
//-------------------------------------------------------------------------
'passwordEntropy': function(aValue) {
var result;
var bitPerChar;
bitPerChar = 4;
if (/[a-z]/.test(aValue)) {
bitPerChar ++;
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
@@ -32,96 +32,97 @@
"YUI-extensions/Element.js",
"YUI-extensions/CompositeElement.js",
"YUI-extensions/State.js",
"YUI-extensions/EventManager.js",
"YUI-extensions/KeyMap.js",
"YUI-extensions/Layer.js",
"YUI-extensions/MixedCollection.js",
"YUI-extensions/State.js",
"YUI-extensions/UpdateManager.js",
"YUI-extensions/anim/Actor.js",
"YUI-extensions/anim/Animator.js",
"YUI-extensions/dd/Registry.js",
"YUI-extensions/dd/ScrollManager.js",
"YUI-extensions/dd/StatusProxy.js",
"YUI-extensions/layout/ContentPanels.js",
"YUI-extensions/layout/LayoutManager.js",
"YUI-extensions/layout/BorderLayout.js",
"YUI-extensions/layout/BasicLayoutRegion.js",
"YUI-extensions/layout/LayoutRegion.js",
"YUI-extensions/layout/LayoutStateManager.js",
"YUI-extensions/layout/SplitLayoutRegion.js",
"YUI-extensions/layout/BorderLayoutRegions.js",
"YUI-extensions/widgets/BasicDialog.js",
"YUI-extensions/widgets/Button.js",
"YUI-extensions/widgets/MessageBox.js",
"YUI-extensions/widgets/Resizable.js",
"YUI-extensions/widgets/SplitBar.js",
"YUI-extensions/widgets/TabPanel.js",
"YUI-extensions/widgets/TemplateView.js",
"YUI-extensions/widgets/Toolbar.js",
"YUI-extensions/widgets/InlineEditor.js",
"YUI-extensions/widgets/QuickTips.js",
"YUI-extensions/CSS.js",
"JSON/json2.js",
"Clipperz/ByteArray.js",
"Clipperz/Base.js",
"Clipperz/CSVProcessor.js",
"Clipperz/KeePassExportProcessor.js",
"Clipperz/Date.js",
"Clipperz/DOM.js",
"Clipperz/Signal.js",
"Clipperz/Style.js",
"Clipperz/Set.js",
"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",
"Clipperz/Crypto/SRP.js",
"Clipperz/Crypto/RSA.js",
"Clipperz/PM/Strings/Strings_en-US.js",
"Clipperz/PM/Strings/Strings_it-IT.js",
"Clipperz/PM/Strings/Strings_pt-BR.js",
"Clipperz/PM/Strings/Strings_ja-JP.js",
"Clipperz/PM/Strings/Strings_zh-CN.js",
"Clipperz/PM/Strings/Strings_es-ES.js",
"Clipperz/PM/Strings/Strings_fr-FR.js",
"Clipperz/PM/Strings/Strings_ru-RU.js",
"Clipperz/PM/Strings.js",
"Clipperz/PM/Strings/MessagePanelConfigurations.js",
"Clipperz/PM/Date.js",
"Clipperz/PM/Components/BaseComponent.js",
"Clipperz/PM/Components/MessageBox.js",
"Clipperz/PM/Components/TextFormField.js",
"Clipperz/PM/Components/PasswordEntropyDisplay.js",
"Clipperz/PM/Components/PasswordGenerator.js",
"Clipperz/PM/Components/Panels/BasePanel.js",
"Clipperz/PM/Components/Panels/LoginPanel.js",
"Clipperz/PM/Components/Panels/MainPanel.js",
"Clipperz/PM/Components/Panels/AccountPanel.js",
"Clipperz/PM/Components/Panels/DataPanel.js",
"Clipperz/PM/Components/Panels/ContactsPanel.js",
"Clipperz/PM/Components/Panels/ToolsPanel.js",
"Clipperz/PM/Components/Panels/LogoutPanel.js",
"Clipperz/PM/Components/RecordDetail/MainComponent.js",
"Clipperz/PM/Components/RecordDetail/AbstractComponent.js",
"Clipperz/PM/Components/RecordDetail/HeaderComponent.js",
"Clipperz/PM/Components/RecordDetail/TitleComponent.js",
"Clipperz/PM/Components/RecordDetail/NotesComponent.js",
"Clipperz/PM/Components/RecordDetail/FieldComponent.js",
"Clipperz/PM/Components/RecordDetail/AbstractFieldSubComponent.js",
"Clipperz/PM/Components/RecordDetail/FieldButtonComponent.js",
"Clipperz/PM/Components/RecordDetail/FieldLabelComponent.js",
"Clipperz/PM/Components/RecordDetail/FieldDragHandler.js",
"Clipperz/PM/Components/RecordDetail/FieldValueComponent.js",
"Clipperz/PM/Components/RecordDetail/FieldTypeComponent.js",
"Clipperz/PM/Components/RecordDetail/DirectLoginsComponent.js",
"Clipperz/PM/Components/RecordDetail/DirectLoginComponent.js",
"Clipperz/PM/Components/RecordDetail/DirectLoginBindingComponent.js",
"Clipperz/PM/Components/RecordDetail/DirectLoginValueComponent.js",
"Clipperz/PM/Components/RecordDetail/CreationWizard.js",
"Clipperz/PM/Components/TabPanel/TabPanelController.js",
"Clipperz/PM/Components/Import/MainComponent.js",