AES LSL+ Implementation

From Second Life Wiki
Jump to navigation Jump to search

Description

The following is an LSL+ version of the LSL AES Engine by Haravikk Mistral. It allows a developer to generate an optimised AES Engine using The Eclipse IDE.

This version has a number of advantages including over 8kb of memory right-away, and up to a further 12kb of memory through the use of the SUPPORTED_MODES(), SUPPORTED_PADS(), and SUPPORTS_SETUP() constants, which can be adjusted to enabled/disable modes of operation, padding schemes, and dynamic set-up.

With this amount of free-memory it is possible to integrate some scripts directly within the broker, eliminating the need to send it link-messages, such optimisations may be made in future to more easily facilitate this.

Required Modules

The LSL Plus project as constructed here uses a common module for constants, and one for helper-functions, to make things as easy-to-use as possible. It assumes the following folder structure:

  • AES
    • Broker
      • AES_Broker.lslp
      • AES_Constants.lslm
    • AES_Helper.lslm

AES_Constants.lslm

<lsl>$module ()

// These variables are used to build communications. Commands are sent as // combined bits in the integer argument of a link-message, and are // recovered using masks, you may wish to read about bit-masks before // editing these values. These are used so the string argument is // kept free for data only. // // Commands take the following form (in hex): // 0xFFMMIOvv // Where the letters are: // F Filter, used to quickly determine if a message is for us. // C Command; encrypt/decrypt etc. // I Type of data provided (hex, base64, etc.). // O Desired type of data to be returned (hex, base64, etc.), // this is unused in replies as the reply's value for I will // be the request's value for O. // v Variable, depends on mode.

// This mask allows the filter byte to be retrieved quickly integer LSLAES_FILTER_MASK() { return 0xFF000000; } // This mask allows the mask byte to be retrieved quickly integer LSLAES_COMMAND_MASK() { return 0x00FF0000; } // This mask allows the input type to be retrieved quickly integer LSLAES_INPUT_TYPE_MASK() { return 0x0000F000; } // This mask allows the output type to be retireved quickly integer LSLAES_OUTPUT_TYPE_MASK() { return 0x00000F00; } // This mask allows the variable to retrieved quickly integer LSLAES_VARIABLE_MASK() { return 0x000000FF; } // How many bits right variable must be shifted integer LSLAES_VARIABLE_SHIFT() { return 0; }

// A request integer LSLAES_FILTER_REQUEST() { return 0x81000000; } // A reply integer LSLAES_FILTER_REPLY() { return 0x82000000; }

// An error occurred integer LSLAES_COMMAND_ERROR() { return 0x00000000; } // Prime engine with key integer LSLAES_COMMAND_PRIME() { return 0x00010000; } // Encrypt message using expanded key integer LSLAES_COMMAND_ENCRYPT() { return 0x00020000; } // Decrypt message using expanded key integer LSLAES_COMMAND_DECRYPT() { return 0x00030000; } // Sets-up the engine by specifying comma-separated flags integer LSLAES_COMMAND_SETUP() { return 0x00050000; } // Initialise the engine with an input-vector integer LSLAES_COMMAND_INIT() { return 0x00060000; }

// Input type is hex integer LSLAES_INPUT_HEX() { return 0x00000000; } // Input type is base64 integer LSLAES_INPUT_BASE64() { return 0x00001000; }

// Output type is hex integer LSLAES_OUTPUT_HEX() { return 0x00000000; } // Output type is base64 integer LSLAES_OUTPUT_BASE64() { return 0x00000100; }

// Refuse any data longer than this many characters integer LSLAES_MAX_SIZE() { return 1536; }

string LSLAES_HEX_CHARS() { return "0123456789abcdef"; } string LSLAES_BASE64_CHARS() { return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; }

// The following constants define modes of operation string LSLAES_MODE_CBC() { return "MODE_CBC"; } string LSLAES_MODE_CFB() { return "MODE_CFB"; }

// Used to set mode list LSLAES_MODES() { return [ LSLAES_MODE_CBC(), LSLAES_MODE_CFB() ]; }

// pragma inline integer LSLAES_MODE_LOOKUP(string mode) { return llListFindList(LSLAES_MODES(), [mode]); }

integer LSLAES_MODE_CBC_ID() { return LSLAES_MODE_LOOKUP(LSLAES_MODE_CBC()); } integer LSLAES_MODE_CFB_ID() { return LSLAES_MODE_LOOKUP(LSLAES_MODE_CFB()); }

// The following contstants define types of padding string LSLAES_PAD_NONE() { return "PAD_NONE"; } string LSLAES_PAD_RBT() { return "PAD_RBT"; } string LSLAES_PAD_NULLS() { return "PAD_NULLS"; } string LSLAES_PAD_RANDOM() { return "PAD_RANDOM"; } string LSLAES_PAD_ZEROES() { return "PAD_ZEROES"; }

// Used to set padding type list LSLAES_PADS() { return [ LSLAES_PAD_NONE(), LSLAES_PAD_RBT(), LSLAES_PAD_NULLS(), LSLAES_PAD_RANDOM(), LSLAES_PAD_ZEROES() ]; }

// pragma inline integer LSLAES_PAD_LOOKUP(string pad) { return llListFindList(LSLAES_PADS(), [pad]); }

integer LSLAES_PAD_NONE_ID() { // Only compatible with CBF and OBF modes return LSLAES_PAD_LOOKUP(LSLAES_PAD_NONE()); } integer LSLAES_PAD_RBT_ID() { // XOR leftover bytes with re-encrypted

                               	// first-block. Length remains the same

return LSLAES_PAD_LOOKUP(LSLAES_PAD_RBT()); } integer LSLAES_PAD_NULLS_ID() { // Adds zeroes (null characters) to the

                               	// end, which are trimmed afterwards.
                               	// Padding creates blocks of lslAESPadSize

return LSLAES_PAD_LOOKUP(LSLAES_PAD_NULLS()); } integer LSLAES_PAD_RANDOM_ID() { // Adds random bytes to the end of the

                               	// data until it reaches a multiple of 
                               	// lslAESPadSize in length. Final byte 
                               	// identifies how many were added. This 
                               	// scheme causes padding to ALWAYS be 
                               	// added.

return LSLAES_PAD_LOOKUP(LSLAES_PAD_RANDOM()); } integer LSLAES_PAD_ZEROES_ID() { // Identical to LSLAES_PAD_RANDOM_ID() except

                               	// that zero-bytes are added.

return LSLAES_PAD_LOOKUP(LSLAES_PAD_ZEROES()); }

string LSLAES_PAD_SIZE() { return "PAD_SIZE"; } integer LSLAES_PAD_SIZE_DEFAULT() { return 512; }</lsl>

AES_Helper.lslm

<lsl>$module ()

$import AES.Broker.AES_Constants.lslm();

// The following extra variables are used to track our messages key requestID = NULL_KEY;

// Sets-up the AES engine. Flags is a comma-separated list with the // following possible entries: // MODE_ECB - Sets Electronic Code-Book mode, a little faster but // not especially secure // MODE_CBC - Cipher-Block-Chaining mode, most commonly used, good // security. // MODE_CFB - Ciphertext Feed-Back mode. Similar to CBC, but does // not require an inverse-cipher to decrypt. // MODE_NOFB - Output Feed-Back mode. Similar to CFB. // // PAD_RBT - Residual Block Termination padding is a method of // encrypting data that does not fit correctly within // into blocks. // PAD_NULLS - Mainly added to provide support for PHP's mcrypt // library. Null-characters (zero-bytes) are added to // pad the length. ALL nulls are removed from the end // after decryption, so be careful if null-characters // occur within the text naturally. // PAD_ZEROES - Adds zero-bytes, with the final byte describing the // number of bytes added. If data fits within padSize // then an extra padSize bits is added. // PAD_RANDOM - Identical to PAD_ZEROES except that random bytes are // generated for padding. // // PAD_SIZE - Defines the length of padding for NULLS, ZEROES, and // random to align on. After this should be an integer // value defining the size. Must be a multiple of 128. // pragma inline lslAESSetup(integer targetLink, string flags, key id) {

   llMessageLinked(
       targetLink,
       LSLAES_FILTER_REQUEST() | LSLAES_COMMAND_SETUP(),
       (flags = "") + flags,
       requestID = id
   );

}

// Sends a link message to targetLink, requesting that aesKey be used to // prime the AES engine. aesKey should be a hexadecimal string representing // a value that is 128, 192, or 256-bits in length. // pragma inline lslAESPrimeHexKey(integer targetLink, string aesKey, key id) {

   llMessageLinked(
       targetLink,
       LSLAES_FILTER_REQUEST() | LSLAES_COMMAND_PRIME() | LSLAES_INPUT_HEX(),
       (aesKey = "") + aesKey,
       requestID = id
   );

}

// Initialises a 128-bit input-vector to be used by the AES engine // pragma inline lslAESInitHexIV(integer targetLink, string iv, key id) {

   llMessageLinked(
       targetLink,
       LSLAES_FILTER_REQUEST() | LSLAES_COMMAND_INIT() | LSLAES_INPUT_HEX(),
       (iv = "") + iv,
       requestID = id
   );

}

// Sends hexadecimal data and gets encrypted hexadecimal data back // pragma inline lslAESEncryptHexToHex(integer targetLink, string hexData, key id) {

   llMessageLinked(
       targetLink,
       LSLAES_FILTER_REQUEST() | LSLAES_COMMAND_ENCRYPT() | 
           LSLAES_INPUT_HEX() | LSLAES_OUTPUT_HEX(),
       (hexData = "") + hexData,
       requestID = id
   );

}

// Sends hexadecimal data and gets encrypted base64 data back // pragma inline lslAESEncryptHexToBase64(integer targetLink, string hexData, key id) {

   llMessageLinked(
       targetLink,
       LSLAES_FILTER_REQUEST() | LSLAES_COMMAND_ENCRYPT() | 
           LSLAES_INPUT_HEX() | LSLAES_OUTPUT_BASE64(),
       (hexData = "") + hexData,
       requestID = id
   );

}

// Send base64 data and gets encrypted hexadecimal data back // pragma inline lslAESEncryptBase64ToHex(integer targetLink, string b64Data, key id) {

   llMessageLinked(
       targetLink,
       LSLAES_FILTER_REQUEST() | LSLAES_COMMAND_ENCRYPT() | 
           LSLAES_INPUT_BASE64() | LSLAES_OUTPUT_HEX(),
       (b64Data = "") + b64Data,
       requestID = id
   );    

}

// Send base64 data and gets encrypted hexadecimal data back // pragma inline lslAESEncryptBase64ToBase64(integer targetLink, string b64Data, key id) {

   llMessageLinked(
       targetLink,
       LSLAES_FILTER_REQUEST() | LSLAES_COMMAND_ENCRYPT() | 
           LSLAES_INPUT_BASE64() | LSLAES_OUTPUT_BASE64(),
       (b64Data = "") + b64Data,
       requestID = id
   );    

}

// Sends hexadecimal data and gets decrypted hexadecimal data back // pragma inline lslAESDecryptHexToHex(integer targetLink, string hexData, key id) {

   llMessageLinked(
       targetLink,
       LSLAES_FILTER_REQUEST() | LSLAES_COMMAND_DECRYPT() | 
           LSLAES_INPUT_HEX() | LSLAES_OUTPUT_HEX(),
       (hexData = "") + hexData,
       requestID = id
   );

}

// Sends hexadecimal data and gets decrypted base64 data back // pragma inline lslAESDecryptHexToBase64(integer targetLink, string hexData, key id) {

   llMessageLinked(
       targetLink,
       LSLAES_FILTER_REQUEST() | LSLAES_COMMAND_DECRYPT() | 
           LSLAES_INPUT_HEX() | LSLAES_OUTPUT_BASE64(),
       (hexData = "") + hexData,
       requestID = id
   );

}

// Send base64 data and gets decrypted hexadecimal data back // pragma inline lslAESDecryptBase64ToHex(integer targetLink, string b64Data, key id) {

   llMessageLinked(
       targetLink,
       LSLAES_FILTER_REQUEST() | LSLAES_COMMAND_DECRYPT() | 
           LSLAES_INPUT_BASE64() | LSLAES_OUTPUT_HEX(),
       (b64Data = "") + b64Data,
       requestID = id
   );    

}

// Send base64 data and gets decrypted hexadecimal data back // pragma inline lslAESDecryptBase64ToBase64(integer targetLink, string b64Data, key id) {

   llMessageLinked(
       targetLink,
       LSLAES_FILTER_REQUEST() | LSLAES_COMMAND_DECRYPT() | 
           LSLAES_INPUT_BASE64() | LSLAES_OUTPUT_BASE64(),
       (b64Data = "") + b64Data,
       requestID = id
   );    

}

// Tests to see if a message is a reply or not (TRUE/FALSE) // pragma inline integer lslAESIsReply(integer int, key id) {

   return (
       ((int & LSLAES_FILTER_MASK()) == LSLAES_FILTER_REPLY()) && 
       (id == requestID)
   );

}

// Grabs the mode of this reply. Should be one of the LSLAES_COMMAND_* constants // pragma inline integer lslAESGetReplyMode(integer int) {

   return (int & LSLAES_COMMAND_MASK());

}

// Grabs the data type of this reply. Should be one of the LSLAES_INPUT_* // constants. // pragma inline integer lslAESGetReplyDataType(integer int) {

   return (int & LSLAES_INPUT_TYPE_MASK());

}</lsl>

AES_Broker.lslp

<lsl>$import AES.Broker.AES_Constants.lslm();

integer SUPPORTS_SETUP() { return TRUE; }

list SUPPORTED_MODES() { return [ LSLAES_MODE_CBC(), LSLAES_MODE_CBC_ID(), LSLAES_MODE_CFB(), LSLAES_MODE_CFB_ID() ]; }

integer LSLAES_MODE_DEFAULT() { return llList2Integer(SUPPORTED_MODES(), 1); }

// pragma inline integer SUPPORTS_MODE(string mode) { return (~llListFindList(SUPPORTED_MODES(), [mode])); }

list SUPPORTED_PADS() { return [ LSLAES_PAD_NONE(), LSLAES_PAD_NONE_ID(), LSLAES_PAD_RBT(), LSLAES_PAD_RBT_ID(), LSLAES_PAD_NULLS(), LSLAES_PAD_NULLS_ID(), LSLAES_PAD_RANDOM(), LSLAES_PAD_RANDOM_ID(), LSLAES_PAD_ZEROES(), LSLAES_PAD_ZEROES_ID() ]; }

integer LSLAES_PAD_DEFAULT() { return llList2Integer(SUPPORTED_PADS(), 1); }

// pragma inline integer SUPPORTS_PAD(string pad) { return (~llListFindList(SUPPORTED_PADS(), [pad])); }

// Treats the above list as a byte-array, retrieving the desired byte value. integer lslAESGetSBoxInvertedByte(integer n) Template:Return lslAESMultInverse(lslAESInverseAffine(n));

// Treats the above list as a byte-array, retrieving the desired byte value. integer lslAESGetSBoxByte(integer n) Template:Return lslAESAffine(lslAESMultInverse(n));

// Calculates high-bit of x / 2 in a finite field integer lslAESHibit(integer x) {{

   x = (x >> 1) | (x >> 2);
   x = x | (x >> 2);
   x = x | (x >> 4);
   return (++x) >> 1;

}}

// Calculates a multipilicative inverse in a finite field integer lslAESMultInverse(integer p1) {{

   if(p1 < 2) return p1;

   integer p2 = 0x1b;
   integer n1 = lslAESHibit(p1);
   integer n2 = 0x80;
   integer v1 = 1;
   integer v2 = 0;

   do {
       if(n1)
           while(n2 >= n1) {                 // Divide polynomial p2 by p1
               n2 /= n1;                     // shift smaller polynomial left 
               p2 = p2 ^ ((p1 * n2) & 0xFF); // and remove from larger one
               v2 = v2 ^ ((v1 * n2) & 0xFF); // shift accumulated value and
               n2 = lslAESHibit(p2);               // add into result
           }
       else return v1;

       if(n2)                                // repeat with values swapped 
           while(n1 >= n2) {
               n1 /= n2;
               p1 = p1 ^ ((p2 * n1) & 0xFF);
               v1 = v1 ^ ((v2 * n1) & 0xFF);
               n1 = lslAESHibit(p1);
           }
       else return v2;
   } while (TRUE);
   return 0;

}}

// Affine function for sbox integer lslAESAffine(integer x) {{

   x = x ^ (x << 1) ^ (x << 2) ^ (x << 3) ^ (x << 4);
   return 0x63 ^ ((x ^ (x >> 8)) & 0xFF);

}}

// Inverse affine function for sbox inversion integer lslAESInverseAffine(integer x) {{

   x = (x << 1) ^ (x << 3) ^ (x << 6);
   return 0x05 ^ ((x ^ (x >> 8)) & 0xFF);

}}

integer lslAESMode; // Set in default state_entry() integer lslAESPad; // Set in default state_entry() integer lslAESPadSize; // Set in default state_entry()

// Used for the actual encryption, generated from Key list lslAESRoundKey = []; // The number of rounds to perform (bigger key == more rounds) integer lslAESRounds = 0;

// The following are used for the state instead of a list integer lslAESStateX0Y0 = 0; integer lslAESStateX0Y1 = 0; integer lslAESStateX0Y2 = 0; integer lslAESStateX0Y3 = 0; integer lslAESStateX1Y0 = 0; integer lslAESStateX1Y1 = 0; integer lslAESStateX1Y2 = 0; integer lslAESStateX1Y3 = 0; integer lslAESStateX2Y0 = 0; integer lslAESStateX2Y1 = 0; integer lslAESStateX2Y2 = 0; integer lslAESStateX2Y3 = 0; integer lslAESStateX3Y0 = 0; integer lslAESStateX3Y1 = 0; integer lslAESStateX3Y2 = 0; integer lslAESStateX3Y3 = 0;

// Used to initialise state for CBC and other mode integer lslAESInputVector0 = 0; integer lslAESInputVector1 = 0; integer lslAESInputVector2 = 0; integer lslAESInputVector3 = 0;

//##########################################################################// // HIGH-LEVEL FUNCTIONS // //##########################################################################// // The following functions are the ones to call to encrypt/decrypt // //##########################################################################// // Performs a cipher with necessary padding performed before execution // pragma inline list lslAESPadCipher(list data) {{

   integer bits = llList2Integer(data, 0);
   data = llDeleteSubList((data = []) + data, 0, 0);

   integer padding = lslAESPad;
   if (padding == LSLAES_PAD_NONE_ID()) {
       if (lslAESMode == LSLAES_MODE_CFB_ID()) 
           return [bits] + lslAESCipher((data = []) + data);
       padding = LSLAES_PAD_RBT_ID();
   }

   integer blockSize = lslAESPadSize;
   if (padding == LSLAES_PAD_RBT_ID()) blockSize = 128;

   integer blocks = bits / blockSize;
   integer extra  = bits % blockSize;

   if (padding == LSLAES_PAD_RBT_ID()) {
   	if (SUPPORTS_PAD(LSLAES_PAD_RBT())) {

// This scheme takes the last encrypted block, encrypts it again and // XORs it with any leftover data, maintaining data-length. If input // is less than a block in size then the current input-vector is used. list final = []; if (extra > 0) { integer words = extra / 32; if ((words * 32) < extra) ++words;

// Grab leftover words list t = llList2List(data, -words, -1);

// Encrypt all other data list lb = []; if (blocks < 1) { // If not enough for a block, we generate lb using // a double cipher of input vector. lb = lslAESCipher( lslAESCipher((data = []) + [ lslAESInputVector0, lslAESInputVector1, lslAESInputVector2, lslAESInputVector3 ]) ); } else { // If there are blocks, we encrypt normally, then // double-encrypt the final block for lb data = lslAESCipher( llDeleteSubList((data = []) + data, -words, -1) ); lb = lslAESCipher(llList2List(data, -4, -1)); }

// XOR lb with t integer i = 0; integer l = (t != []); do final = (final = []) + final + [llList2Integer(t, i) ^ llList2Integer(lb, i)]; while ((++i) < l);

return [bits] + (data = final = []) + data + final; } return [bits] + lslAESCipher((data = []) + data);

   	}
   } else if (SUPPORTS_PAD(LSLAES_PAD_NULLS()) || 
   		   SUPPORTS_PAD(LSLAES_PAD_RANDOM()) || 
   		   SUPPORTS_PAD(LSLAES_PAD_ZEROES())) {
       // This scheme works by adding bytes until the data is a 
       // multiple of lslAESPadSize bits long. In the case of 
       // PAD_NULLS this will only add extra data if needed, 
       // while the other types must always add at least one 
       // byte, as they also leave a note of bytes added in the 
       // final byte.
       extra = blockSize - extra; // Bits to add
       integer bytes = extra / 8; // Bytes to add
       if (bytes <= 0) {
           if (padding == LSLAES_PAD_NULLS_ID()) 
               jump skip; // Doesn't need to add anything

           bytes = blockSize / 8;
           extra += blockSize;
       }

       bits += extra;

       integer words = bytes / 4; // Words to add

       // First add bytes to end-word
       extra = bytes % 4;
       if (extra > 0) {
           integer i = 0; integer v = llList2Integer(data, -1);
           if ((extra == bytes) && (padding != LSLAES_PAD_NULLS_ID())) {
               v = v | bytes;
               i = 1;
           }

           integer byte = 0;
           while (i < extra) {
               if (padding == LSLAES_PAD_RANDOM_ID()) 
                   byte = (integer)llFrand(256.0) & 0xFF;

               v = v | (byte << (i << 3));
               ++i;
           }

           data = llListReplaceList((data = []) + data, [v], -1, -1);
       }

       // Now, if needed, add words to end of data
       if (words > 0) {
           integer final = -1;
           if (padding != LSLAES_PAD_NULLS_ID()) 
               final = words - 1;

           integer word = 0; integer byte = 0;
           integer i = 0;
           integer j = 0; list w = [];
           do {
               word = j = 0; // New word
               do {
                   if ((padding != LSLAES_PAD_NULLS_ID()) && 
                       (i == final) && !j) 
                       byte = bytes;
                   else if (padding == LSLAES_PAD_RANDOM_ID()) 
                       byte = (integer)llFrand(256.0) & 0xFF;

                   word = word | (byte << (j << 3));
               } while ((++j) < 4);

               w = (w = []) + w + [word];
           } while ((++i) < words);

           data = (data = w = []) + data + w;
       }

       @skip;
       return [bits] + lslAESCipher((data = []) + data);
   }
   return [];

}}

// Performs an inverse cipher with appropriate padding handling performed // pragma inline list lslAESInvertPadCipher(list data) {{

   integer bits = llList2Integer(data, 0);
   data = llDeleteSubList((data = []) + data, 0, 0);

   integer padding = lslAESPad;
   if (padding == LSLAES_PAD_NONE_ID()) {
       if (lslAESMode == LSLAES_MODE_CFB_ID()) 
           return [bits] + lslAESInvertCipher((data = []) + data);
       padding = LSLAES_PAD_RBT_ID();
   }

   integer blockSize = lslAESPadSize;
   if (padding == LSLAES_PAD_RBT_ID()) blockSize = 128;

   integer blocks = bits / blockSize;
   integer extra  = bits % blockSize;

   if (padding == LSLAES_PAD_RBT_ID()) {
   	if (SUPPORTS_PAD(LSLAES_PAD_RBT())) {

// This scheme takes the last encrypted block, encrypts it again and // XORs it with any leftover data, maintaining data-length. If input // is less than a block in size then the current input-vector is used. list final = []; if (extra > 0) { integer words = extra / 32; if ((words * 32) < extra) ++words;

// Grab leftover words list t = llList2List(data, -words, -1);

// Decrypt all other data list lb = []; if (blocks < 1) { // If not enough for a block, we generate lb using // a double cipher of input vector. lb = lslAESCipher( lslAESCipher((data = []) + [ lslAESInputVector0, lslAESInputVector1, lslAESInputVector2, lslAESInputVector3 ]) ); } else { // If there are blocks, then we double-encrypt the // last full block to generate lb, then decrypt normally lb = lslAESCipher( llList2List(data, -(4 + words), -(words + 1)) ); data = lslAESInvertCipher( llDeleteSubList((data = []) + data, -words, -1) ); }

// XOR lb with t integer i = 0; integer l = (t != []); do final = (final = []) + final + [llList2Integer(t, i) ^ llList2Integer(lb, i)]; while ((++i) < l);

return [bits] + (data = final = []) + data + final; } return [bits] + lslAESInvertCipher((data = []) + data);

   	}
   } else if (SUPPORTS_PAD(LSLAES_PAD_NULLS()) || 
   		   SUPPORTS_PAD(LSLAES_PAD_RANDOM()) || 
   		   SUPPORTS_PAD(LSLAES_PAD_ZEROES())) {
       // This scheme works by adding bytes until the data is a 
       // multiple of lslAESPadSize bits long. In the case of 
       // PAD_NULLS this will only add extra data if needed, 
       // while the other types must always add at least one 
       // byte, as they also leave a note of bytes added in the 
       // final byte.

       // If the data is not on a block boundary, then extra bits 
       // have snuck in (usually due to base64 conversion). We 
       // assume here that padding was done correctly, and ignore 
       // extra bits.
       if (extra > 0) {
           bits -= extra;
           
           integer e = extra / 32;
           if ((e * 32) < extra) ++e;
           extra = e;
           
           if (extra <= 0) extra = 1;

           if (extra > (data != [])) return [0];
           data = llDeleteSubList((data = []) + data, -extra, -1);
       }

       // Perform the decryption
       data = lslAESInvertCipher((data = []) + data);

       integer bytes = 0; integer words = 0;

       // Remove extra bytes as required
       if (padding == LSLAES_PAD_NULLS_ID()) {
           // We remove all zero-bytes at the end of the data
           integer l = data != [];
           integer v = 0;

           while (words < l) {
               v = llList2Integer(data, -(words + 1));

               if (v == 0) { // Four null-bytes
                   ++words;
                   bytes += 4;
               } else {
                   integer j = 0; integer byte = 0;
                   do {
                       byte = (v >> (j << 3)) & 0xFF;

                       if (byte == 0) ++bytes;
                       else jump skip;
                   } while ((++j) < 4);
               }
           }
           @skip;
       } else {
           // Get the number of bytes to remove from the final byte
           bytes = llList2Integer(data, -1) & 0xFF;
           if ((bytes << 3) >= bits) return [0];
           words = bytes / 4;
       }

       // Lop-off words, excess bytes are accounted for later
       if (words > 0) 
           data = llDeleteSubList((data = []) + data, -words, -1);

       // Correct bit-header for output
       bits -= bytes << 3;

       return [bits] + (data = []) + data;
   }
   return [];

}}

// Decrypts a list of integers into a list of decrypted integers. // Padding adjustment must be performed as this function will only except data // that is a multiple of 128-bits long. list lslAESInvertCipher(list data) {{

   // The following are used to pass blocks forward    
   integer prevBlock0 = lslAESInputVector0;
   integer prevBlock1 = lslAESInputVector1;
   integer prevBlock2 = lslAESInputVector2;
   integer prevBlock3 = lslAESInputVector3;

   integer nextBlock0 = 0;
   integer nextBlock1 = 0;
   integer nextBlock2 = 0;
   integer nextBlock3 = 0;

   integer j = 0;
   integer l = (data != []);
   list output = [];
   while (l > 0) {
       // Different modes treat blocks differently
       if (lslAESMode == LSLAES_MODE_CBC_ID()) {
       	if (SUPPORTS_MODE(LSLAES_MODE_CBC())) {

// For CBC we load it, and must keep a copy nextBlock0 = llList2Integer(data, 0); lslAESStateX0Y0 = ((nextBlock0 >> 24) & 0xFF); lslAESStateX0Y1 = ((nextBlock0 >> 16) & 0xFF); lslAESStateX0Y2 = ((nextBlock0 >> 8 ) & 0xFF); lslAESStateX0Y3 = ((nextBlock0 ) & 0xFF); nextBlock1 = llList2Integer(data, 1); lslAESStateX1Y0 = ((nextBlock1 >> 24) & 0xFF); lslAESStateX1Y1 = ((nextBlock1 >> 16) & 0xFF); lslAESStateX1Y2 = ((nextBlock1 >> 8 ) & 0xFF); lslAESStateX1Y3 = ((nextBlock1 ) & 0xFF); nextBlock2 = llList2Integer(data, 2); lslAESStateX2Y0 = ((nextBlock2 >> 24) & 0xFF); lslAESStateX2Y1 = ((nextBlock2 >> 16) & 0xFF); lslAESStateX2Y2 = ((nextBlock2 >> 8 ) & 0xFF); lslAESStateX2Y3 = ((nextBlock2 ) & 0xFF); nextBlock3 = llList2Integer(data, 3); lslAESStateX3Y0 = ((nextBlock3 >> 24) & 0xFF); lslAESStateX3Y1 = ((nextBlock3 >> 16) & 0xFF); lslAESStateX3Y2 = ((nextBlock3 >> 8 ) & 0xFF); lslAESStateX3Y3 = ((nextBlock3 ) & 0xFF);

       	}
       } else if (lslAESMode == LSLAES_MODE_CFB_ID()) {
       	if (SUPPORTS_MODE(LSLAES_MODE_CFB())) {

lslAESStateX0Y0 = ((prevBlock0 >> 24) & 0xFF); lslAESStateX0Y1 = ((prevBlock0 >> 16) & 0xFF); lslAESStateX0Y2 = ((prevBlock0 >> 8 ) & 0xFF); lslAESStateX0Y3 = ((prevBlock0 ) & 0xFF);

lslAESStateX1Y0 = ((prevBlock1 >> 24) & 0xFF); lslAESStateX1Y1 = ((prevBlock1 >> 16) & 0xFF); lslAESStateX1Y2 = ((prevBlock1 >> 8 ) & 0xFF); lslAESStateX1Y3 = ((prevBlock1 ) & 0xFF);

lslAESStateX2Y0 = ((prevBlock2 >> 24) & 0xFF); lslAESStateX2Y1 = ((prevBlock2 >> 16) & 0xFF); lslAESStateX2Y2 = ((prevBlock2 >> 8 ) & 0xFF); lslAESStateX2Y3 = ((prevBlock2 ) & 0xFF);

lslAESStateX3Y0 = ((prevBlock3 >> 24) & 0xFF); lslAESStateX3Y1 = ((prevBlock3 >> 16) & 0xFF); lslAESStateX3Y2 = ((prevBlock3 >> 8 ) & 0xFF); lslAESStateX3Y3 = ((prevBlock3 ) & 0xFF);

       	}
       }

       if (lslAESMode == LSLAES_MODE_CFB_ID()) {
       	if (SUPPORTS_MODE(LSLAES_MODE_CFB())) {

lslAESPerformCipher(); // CFB doesn't need inverse cipher

       	}
       } else if (SUPPORTS_MODE(LSLAES_MODE_CBC())) {
           j = lslAESRounds;
           do {
               if (j < lslAESRounds) {
               	lslAESInvertShiftRows();
               	lslAESInvertSubBytes();
               }
               lslAESAddRoundKey(j);
               if (j && (j < lslAESRounds)) {
               	lslAESInvertMixColumns();
               }
           } while ((--j) >= 0);
       }

       // Ciphertext is generated differently by different modes
       if (lslAESMode == LSLAES_MODE_CBC_ID()) {
       	if (SUPPORTS_MODE(LSLAES_MODE_CBC())) {

// For CBC we XOR with previous block before output output = (output = []) + output + [ prevBlock0 ^ ((lslAESStateX0Y0 << 24) | (lslAESStateX0Y1 << 16) | (lslAESStateX0Y2 << 8) | (lslAESStateX0Y3)), prevBlock1 ^ ((lslAESStateX1Y0 << 24) | (lslAESStateX1Y1 << 16) | (lslAESStateX1Y2 << 8) | (lslAESStateX1Y3)), prevBlock2 ^ ((lslAESStateX2Y0 << 24) | (lslAESStateX2Y1 << 16) | (lslAESStateX2Y2 << 8) | (lslAESStateX2Y3)), prevBlock3 ^ ((lslAESStateX3Y0 << 24) | (lslAESStateX3Y1 << 16) | (lslAESStateX3Y2 << 8) | (lslAESStateX3Y3)) ];

prevBlock0 = nextBlock0; prevBlock1 = nextBlock1; prevBlock2 = nextBlock2; prevBlock3 = nextBlock3;

       	}
       } else if (SUPPORTS_MODE(LSLAES_MODE_CFB())) { // CFB
           // For CBF we XOR input block for output and carry 
           // input for encryption next-block
           prevBlock0 = llList2Integer(data, 0);
           prevBlock1 = llList2Integer(data, 1);
           prevBlock2 = llList2Integer(data, 2);
           prevBlock3 = llList2Integer(data, 3);

           output = (output = []) + output + [
               prevBlock0 ^ 
               ((lslAESStateX0Y0 << 24) | (lslAESStateX0Y1 << 16) | 
                   (lslAESStateX0Y2 << 8) | (lslAESStateX0Y3)),
               prevBlock1 ^ 
               ((lslAESStateX1Y0 << 24) | (lslAESStateX1Y1 << 16) | 
                   (lslAESStateX1Y2 << 8) | (lslAESStateX1Y3)),
               prevBlock2 ^ 
               ((lslAESStateX2Y0 << 24) | (lslAESStateX2Y1 << 16) | 
                   (lslAESStateX2Y2 << 8) | (lslAESStateX2Y3)),
               prevBlock3 ^ 
               ((lslAESStateX3Y0 << 24) | (lslAESStateX3Y1 << 16) | 
                   (lslAESStateX3Y2 << 8) | (lslAESStateX3Y3))
           ];
       }

        // Reduce input
       if (l > 4) 
           data = llList2List((data = []) + data, 4, -1);
       else data = [];
       l -= 4;
   }

   return (output = []) + output;

}}

// Encrypts a list of integers into a list of encrypted integers. // Padding must be performed before being called so that data is a multiple of // 128-bits long. list lslAESCipher(list data) {{

   // We must prime the state with the input vector
   lslAESLoadInputVector();

   integer l = (data != []);
   integer j = 0;
   list output = [];

   while (l > 0) {
       // Different modes treat blocks differently
       if (lslAESMode == LSLAES_MODE_CBC_ID()) {
       	if (SUPPORTS_MODE(LSLAES_MODE_CBC())) {

// For CBC we XOR with the previous block to reduce // chances of patterns occurring j = llList2Integer(data, 0); lslAESStateX0Y0 = lslAESStateX0Y0 ^ ((j >> 24) & 0xFF); lslAESStateX0Y1 = lslAESStateX0Y1 ^ ((j >> 16) & 0xFF); lslAESStateX0Y2 = lslAESStateX0Y2 ^ ((j >> 8 ) & 0xFF); lslAESStateX0Y3 = lslAESStateX0Y3 ^ ((j ) & 0xFF); j = llList2Integer(data, 1); lslAESStateX1Y0 = lslAESStateX1Y0 ^ ((j >> 24) & 0xFF); lslAESStateX1Y1 = lslAESStateX1Y1 ^ ((j >> 16) & 0xFF); lslAESStateX1Y2 = lslAESStateX1Y2 ^ ((j >> 8 ) & 0xFF); lslAESStateX1Y3 = lslAESStateX1Y3 ^ ((j ) & 0xFF); j = llList2Integer(data, 2); lslAESStateX2Y0 = lslAESStateX2Y0 ^ ((j >> 24) & 0xFF); lslAESStateX2Y1 = lslAESStateX2Y1 ^ ((j >> 16) & 0xFF); lslAESStateX2Y2 = lslAESStateX2Y2 ^ ((j >> 8 ) & 0xFF); lslAESStateX2Y3 = lslAESStateX2Y3 ^ ((j ) & 0xFF); j = llList2Integer(data, 3); lslAESStateX3Y0 = lslAESStateX3Y0 ^ ((j >> 24) & 0xFF); lslAESStateX3Y1 = lslAESStateX3Y1 ^ ((j >> 16) & 0xFF); lslAESStateX3Y2 = lslAESStateX3Y2 ^ ((j >> 8 ) & 0xFF); lslAESStateX3Y3 = lslAESStateX3Y3 ^ ((j ) & 0xFF);

       	}
       }

       lslAESPerformCipher();

       if (lslAESMode == LSLAES_MODE_CFB_ID()) {
       	if (SUPPORTS_MODE(LSLAES_MODE_CFB())) {

// For CFB we XOR blocks and carry them to the next // stage (by keeping the result in state) j = llList2Integer(data, 0); lslAESStateX0Y0 = lslAESStateX0Y0 ^ ((j >> 24) & 0xFF); lslAESStateX0Y1 = lslAESStateX0Y1 ^ ((j >> 16) & 0xFF); lslAESStateX0Y2 = lslAESStateX0Y2 ^ ((j >> 8 ) & 0xFF); lslAESStateX0Y3 = lslAESStateX0Y3 ^ ((j ) & 0xFF); j = llList2Integer(data, 1); lslAESStateX1Y0 = lslAESStateX1Y0 ^ ((j >> 24) & 0xFF); lslAESStateX1Y1 = lslAESStateX1Y1 ^ ((j >> 16) & 0xFF); lslAESStateX1Y2 = lslAESStateX1Y2 ^ ((j >> 8 ) & 0xFF); lslAESStateX1Y3 = lslAESStateX1Y3 ^ ((j ) & 0xFF); j = llList2Integer(data, 2); lslAESStateX2Y0 = lslAESStateX2Y0 ^ ((j >> 24) & 0xFF); lslAESStateX2Y1 = lslAESStateX2Y1 ^ ((j >> 16) & 0xFF); lslAESStateX2Y2 = lslAESStateX2Y2 ^ ((j >> 8 ) & 0xFF); lslAESStateX2Y3 = lslAESStateX2Y3 ^ ((j ) & 0xFF); j = llList2Integer(data, 3); lslAESStateX3Y0 = lslAESStateX3Y0 ^ ((j >> 24) & 0xFF); lslAESStateX3Y1 = lslAESStateX3Y1 ^ ((j >> 16) & 0xFF); lslAESStateX3Y2 = lslAESStateX3Y2 ^ ((j >> 8 ) & 0xFF); lslAESStateX3Y3 = lslAESStateX3Y3 ^ ((j ) & 0xFF);

       	}
       }

       output = (output = []) + output + [
           (lslAESStateX0Y0 << 24) | (lslAESStateX0Y1 << 16) | 
               (lslAESStateX0Y2 << 8) | lslAESStateX0Y3,
           (lslAESStateX1Y0 << 24) | (lslAESStateX1Y1 << 16) | 
               (lslAESStateX1Y2 << 8) | lslAESStateX1Y3,
           (lslAESStateX2Y0 << 24) | (lslAESStateX2Y1 << 16) | 
               (lslAESStateX2Y2 << 8) | lslAESStateX2Y3,
           (lslAESStateX3Y0 << 24) | (lslAESStateX3Y1 << 16) | 
               (lslAESStateX3Y2 << 8) | lslAESStateX3Y3
       ];

        // Reduce input
       if (l > 4) 
           data = llList2List((data = []) + data, 4, -1);
       else data = [];
       l -= 4;
   }

   return (output = []) + output;

}}

// Simply performs the cipher operation on current state, separated // for convenience with CBF and OBF modes (which perform a cipher in // order to decrypt data). lslAESPerformCipher() {{ integer j = 0; do { if (j) {

       	lslAESSubBytes();
   		lslAESShiftRows();
   		if (j < lslAESRounds) {
       		lslAESMixColumns();
   		}

}

   	lslAESAddRoundKey(j);

} while ((++j) <= lslAESRounds); }}

// Expands the input vector for use in block differentiation // pragma inline lslAESLoadInputVector() {{

   lslAESStateX0Y0 = (lslAESInputVector0 >> 24) & 0xFF;
   lslAESStateX0Y1 = (lslAESInputVector0 >> 16) & 0xFF;
   lslAESStateX0Y2 = (lslAESInputVector0 >> 8 ) & 0xFF;
   lslAESStateX0Y3 = (lslAESInputVector0      ) & 0xFF;

   lslAESStateX1Y0 = (lslAESInputVector1 >> 24) & 0xFF;
   lslAESStateX1Y1 = (lslAESInputVector1 >> 16) & 0xFF;
   lslAESStateX1Y2 = (lslAESInputVector1 >> 8 ) & 0xFF;
   lslAESStateX1Y3 = (lslAESInputVector1      ) & 0xFF;

   lslAESStateX2Y0 = (lslAESInputVector2 >> 24) & 0xFF;
   lslAESStateX2Y1 = (lslAESInputVector2 >> 16) & 0xFF;
   lslAESStateX2Y2 = (lslAESInputVector2 >> 8 ) & 0xFF;
   lslAESStateX2Y3 = (lslAESInputVector2      ) & 0xFF;

   lslAESStateX3Y0 = (lslAESInputVector3 >> 24) & 0xFF;
   lslAESStateX3Y1 = (lslAESInputVector3 >> 16) & 0xFF;
   lslAESStateX3Y2 = (lslAESInputVector3 >> 8 ) & 0xFF;
   lslAESStateX3Y3 = (lslAESInputVector3      ) & 0xFF;

}}

//##########################################################################// // PROCESSING FUNCTIONS // //##########################################################################// // The following functions are used to process data that is being // // encrypted or decrypted. // //##########################################################################// // XORs the value with RoundKey values // pragma inline lslAESAddRoundKey(integer round) {{

   round = round << 2;

   integer t = llList2Integer(lslAESRoundKey, round);
   lslAESStateX0Y0 = lslAESStateX0Y0 ^ ((t >> 24) & 0xFF);
   lslAESStateX0Y1 = lslAESStateX0Y1 ^ ((t >> 16) & 0xFF);
   lslAESStateX0Y2 = lslAESStateX0Y2 ^ ((t >> 8 ) & 0xFF);
   lslAESStateX0Y3 = lslAESStateX0Y3 ^ ((t      ) & 0xFF);

   t = llList2Integer(lslAESRoundKey, ++round);
   lslAESStateX1Y0 = lslAESStateX1Y0 ^ ((t >> 24) & 0xFF);
   lslAESStateX1Y1 = lslAESStateX1Y1 ^ ((t >> 16) & 0xFF);
   lslAESStateX1Y2 = lslAESStateX1Y2 ^ ((t >> 8 ) & 0xFF);
   lslAESStateX1Y3 = lslAESStateX1Y3 ^ ((t      ) & 0xFF);

   t = llList2Integer(lslAESRoundKey, ++round);
   lslAESStateX2Y0 = lslAESStateX2Y0 ^ ((t >> 24) & 0xFF);
   lslAESStateX2Y1 = lslAESStateX2Y1 ^ ((t >> 16) & 0xFF);
   lslAESStateX2Y2 = lslAESStateX2Y2 ^ ((t >> 8 ) & 0xFF);
   lslAESStateX2Y3 = lslAESStateX2Y3 ^ ((t      ) & 0xFF);

   t = llList2Integer(lslAESRoundKey, ++round);
   lslAESStateX3Y0 = lslAESStateX3Y0 ^ ((t >> 24) & 0xFF);
   lslAESStateX3Y1 = lslAESStateX3Y1 ^ ((t >> 16) & 0xFF);
   lslAESStateX3Y2 = lslAESStateX3Y2 ^ ((t >> 8 ) & 0xFF);
   lslAESStateX3Y3 = lslAESStateX3Y3 ^ ((t      ) & 0xFF);

}}

// Performs a substitution using SBox // pragma inline lslAESSubBytes() {{

   lslAESStateX0Y0 = lslAESGetSBoxByte(lslAESStateX0Y0);
   lslAESStateX0Y1 = lslAESGetSBoxByte(lslAESStateX0Y1);
   lslAESStateX0Y2 = lslAESGetSBoxByte(lslAESStateX0Y2);
   lslAESStateX0Y3 = lslAESGetSBoxByte(lslAESStateX0Y3);
   lslAESStateX1Y0 = lslAESGetSBoxByte(lslAESStateX1Y0);
   lslAESStateX1Y1 = lslAESGetSBoxByte(lslAESStateX1Y1);
   lslAESStateX1Y2 = lslAESGetSBoxByte(lslAESStateX1Y2);
   lslAESStateX1Y3 = lslAESGetSBoxByte(lslAESStateX1Y3);
   lslAESStateX2Y0 = lslAESGetSBoxByte(lslAESStateX2Y0);
   lslAESStateX2Y1 = lslAESGetSBoxByte(lslAESStateX2Y1);
   lslAESStateX2Y2 = lslAESGetSBoxByte(lslAESStateX2Y2);
   lslAESStateX2Y3 = lslAESGetSBoxByte(lslAESStateX2Y3);
   lslAESStateX3Y0 = lslAESGetSBoxByte(lslAESStateX3Y0);
   lslAESStateX3Y1 = lslAESGetSBoxByte(lslAESStateX3Y1);
   lslAESStateX3Y2 = lslAESGetSBoxByte(lslAESStateX3Y2);
   lslAESStateX3Y3 = lslAESGetSBoxByte(lslAESStateX3Y3);

}}

// Performs a substition using SBoxInverted // pragma inline lslAESInvertSubBytes() {{

   lslAESStateX0Y0 = lslAESGetSBoxInvertedByte(lslAESStateX0Y0);
   lslAESStateX0Y1 = lslAESGetSBoxInvertedByte(lslAESStateX0Y1);
   lslAESStateX0Y2 = lslAESGetSBoxInvertedByte(lslAESStateX0Y2);
   lslAESStateX0Y3 = lslAESGetSBoxInvertedByte(lslAESStateX0Y3);
   lslAESStateX1Y0 = lslAESGetSBoxInvertedByte(lslAESStateX1Y0);
   lslAESStateX1Y1 = lslAESGetSBoxInvertedByte(lslAESStateX1Y1);
   lslAESStateX1Y2 = lslAESGetSBoxInvertedByte(lslAESStateX1Y2);
   lslAESStateX1Y3 = lslAESGetSBoxInvertedByte(lslAESStateX1Y3);
   lslAESStateX2Y0 = lslAESGetSBoxInvertedByte(lslAESStateX2Y0);
   lslAESStateX2Y1 = lslAESGetSBoxInvertedByte(lslAESStateX2Y1);
   lslAESStateX2Y2 = lslAESGetSBoxInvertedByte(lslAESStateX2Y2);
   lslAESStateX2Y3 = lslAESGetSBoxInvertedByte(lslAESStateX2Y3);
   lslAESStateX3Y0 = lslAESGetSBoxInvertedByte(lslAESStateX3Y0);
   lslAESStateX3Y1 = lslAESGetSBoxInvertedByte(lslAESStateX3Y1);
   lslAESStateX3Y2 = lslAESGetSBoxInvertedByte(lslAESStateX3Y2);
   lslAESStateX3Y3 = lslAESGetSBoxInvertedByte(lslAESStateX3Y3);

}}


// Performs row shifts // pragma inline lslAESShiftRows() {{

   // Rotate first row 1 columns to left
   integer t = lslAESStateX0Y1;
   lslAESStateX0Y1 = lslAESStateX1Y1;
   lslAESStateX1Y1 = lslAESStateX2Y1;
   lslAESStateX2Y1 = lslAESStateX3Y1;
   lslAESStateX3Y1 = t;

   // Rotate second row 2 columns to left
   t = lslAESStateX0Y2;
   lslAESStateX0Y2 = lslAESStateX2Y2;
   lslAESStateX2Y2 = t;

   t = lslAESStateX1Y2;
   lslAESStateX1Y2 = lslAESStateX3Y2;
   lslAESStateX3Y2 = t;

   // Rotate third row 3 columns to left
   t = lslAESStateX0Y3;
   lslAESStateX0Y3 = lslAESStateX3Y3;
   lslAESStateX3Y3 = lslAESStateX2Y3;
   lslAESStateX2Y3 = lslAESStateX1Y3;
   lslAESStateX1Y3 = t;

}}

// Undoes a set of row shifts // pragma inline lslAESInvertShiftRows() {{

   // Rotate first row 1 columns to right
   integer t = lslAESStateX3Y1;
   lslAESStateX3Y1 = lslAESStateX2Y1;
   lslAESStateX2Y1 = lslAESStateX1Y1;
   lslAESStateX1Y1 = lslAESStateX0Y1;
   lslAESStateX0Y1 = t;

   // Rotate second row 2 columns to right
   t = lslAESStateX0Y2;
   lslAESStateX0Y2 = lslAESStateX2Y2;
   lslAESStateX2Y2 = t;

   t = lslAESStateX1Y2;
   lslAESStateX1Y2 = lslAESStateX3Y2;
   lslAESStateX3Y2 = t;

   // Rotate third row 3 columns to right
   t = lslAESStateX0Y3;
   lslAESStateX0Y3 = lslAESStateX1Y3;
   lslAESStateX1Y3 = lslAESStateX2Y3;
   lslAESStateX2Y3 = lslAESStateX3Y3;
   lslAESStateX3Y3 = t;

}}

// Mixes columns of the state // pragma inline lslAESMixColumns() {{

   integer t = lslAESStateX0Y0;
   integer t1 = lslAESStateX0Y0 ^ lslAESStateX0Y1 ^ 
       lslAESStateX0Y2 ^ lslAESStateX0Y3;

   integer t2 = lslAESXTimes(lslAESStateX0Y0 ^ lslAESStateX0Y1);
   lslAESStateX0Y0 = lslAESStateX0Y0 ^ t2 ^ t1;

   t2 = lslAESXTimes(lslAESStateX0Y1 ^ lslAESStateX0Y2);
   lslAESStateX0Y1 = lslAESStateX0Y1 ^ t2 ^ t1;

   t2 = lslAESXTimes(lslAESStateX0Y2 ^ lslAESStateX0Y3);
   lslAESStateX0Y2 = lslAESStateX0Y2 ^ t2 ^ t1;

   t2 = lslAESXTimes(lslAESStateX0Y3 ^ t);
   lslAESStateX0Y3 = lslAESStateX0Y3 ^ t2 ^ t1;

   t = lslAESStateX1Y0;
   t1 = lslAESStateX1Y0 ^ lslAESStateX1Y1 ^ lslAESStateX1Y2 ^ lslAESStateX1Y3;

   t2 = lslAESXTimes(lslAESStateX1Y0 ^ lslAESStateX1Y1);
   lslAESStateX1Y0 = lslAESStateX1Y0 ^ t2 ^ t1;

   t2 = lslAESXTimes(lslAESStateX1Y1 ^ lslAESStateX1Y2);
   lslAESStateX1Y1 = lslAESStateX1Y1 ^ t2 ^ t1;

   t2 = lslAESXTimes(lslAESStateX1Y2 ^ lslAESStateX1Y3);
   lslAESStateX1Y2 = lslAESStateX1Y2 ^ t2 ^ t1;

   t2 = lslAESXTimes(lslAESStateX1Y3 ^ t);
   lslAESStateX1Y3 = lslAESStateX1Y3 ^ t2 ^ t1;

   t = lslAESStateX2Y0;
   t1 = lslAESStateX2Y0 ^ lslAESStateX2Y1 ^ lslAESStateX2Y2 ^ lslAESStateX2Y3;

   t2 = lslAESXTimes(lslAESStateX2Y0 ^ lslAESStateX2Y1);
   lslAESStateX2Y0 = lslAESStateX2Y0 ^ t2 ^ t1;

   t2 = lslAESXTimes(lslAESStateX2Y1 ^ lslAESStateX2Y2);
   lslAESStateX2Y1 = lslAESStateX2Y1 ^ t2 ^ t1;

   t2 = lslAESXTimes(lslAESStateX2Y2 ^ lslAESStateX2Y3);
   lslAESStateX2Y2 = lslAESStateX2Y2 ^ t2 ^ t1;

   t2 = lslAESXTimes(lslAESStateX2Y3 ^ t);
   lslAESStateX2Y3 = lslAESStateX2Y3 ^ t2 ^ t1;

   t = lslAESStateX3Y0;
   t1 = lslAESStateX3Y0 ^ lslAESStateX3Y1 ^ lslAESStateX3Y2 ^ lslAESStateX3Y3;

   t2 = lslAESXTimes(lslAESStateX3Y0 ^ lslAESStateX3Y1);
   lslAESStateX3Y0 = lslAESStateX3Y0 ^ t2 ^ t1;

   t2 = lslAESXTimes(lslAESStateX3Y1 ^ lslAESStateX3Y2);
   lslAESStateX3Y1 = lslAESStateX3Y1 ^ t2 ^ t1;

   t2 = lslAESXTimes(lslAESStateX3Y2 ^ lslAESStateX3Y3);
   lslAESStateX3Y2 = lslAESStateX3Y2 ^ t2 ^ t1;

   t2 = lslAESXTimes(lslAESStateX3Y3 ^ t);
   lslAESStateX3Y3 = lslAESStateX3Y3 ^ t2 ^ t1;

}}

// Used when column mixing // pragma inline integer lslAESXTimes(integer x) {{

   return ((x << 1) ^ (((x >> 7) & 1) * 0x1b)) & 0xFF;

}}

// Used when column mixing integer lslAESMultiply(integer x, integer y) {{

   integer xT  = lslAESXTimes(x);
   integer xT2 = lslAESXTimes(xT);
   integer xT3 = lslAESXTimes(xT2);

   return (((y & 1) * x) ^ (((y >> 1) & 1) * xT) ^ 
           (((y >> 2) & 1) * xT2) ^ (((y >> 3) & 1) * xT3) ^ 
           (((y >> 4) & 1) * lslAESXTimes(xT3))) & 0xFF;

}}

// Try to understand this at your own peril! // pragma inline lslAESInvertMixColumns() {{

   integer a = lslAESStateX0Y0;
   integer b = lslAESStateX0Y1;
   integer c = lslAESStateX0Y2;
   integer d = lslAESStateX0Y3;

   lslAESStateX0Y0 = lslAESMultiply(a, 0x0e) ^ lslAESMultiply(b, 0x0b) ^ 
       lslAESMultiply(c, 0x0d) ^ lslAESMultiply(d, 0x09);
   lslAESStateX0Y1 = lslAESMultiply(a, 0x09) ^ lslAESMultiply(b, 0x0e) ^ 
       lslAESMultiply(c, 0x0b) ^ lslAESMultiply(d, 0x0d);
   lslAESStateX0Y2 = lslAESMultiply(a, 0x0d) ^ lslAESMultiply(b, 0x09) ^ 
       lslAESMultiply(c, 0x0e) ^ lslAESMultiply(d, 0x0b);
   lslAESStateX0Y3 = lslAESMultiply(a, 0x0b) ^ lslAESMultiply(b, 0x0d) ^ 
       lslAESMultiply(c, 0x09) ^ lslAESMultiply(d, 0x0e);

   a = lslAESStateX1Y0;
   b = lslAESStateX1Y1;
   c = lslAESStateX1Y2;
   d = lslAESStateX1Y3;

   lslAESStateX1Y0 = lslAESMultiply(a, 0x0e) ^ lslAESMultiply(b, 0x0b) ^ 
       lslAESMultiply(c, 0x0d) ^ lslAESMultiply(d, 0x09);
   lslAESStateX1Y1 = lslAESMultiply(a, 0x09) ^ lslAESMultiply(b, 0x0e) ^ 
       lslAESMultiply(c, 0x0b) ^ lslAESMultiply(d, 0x0d);
   lslAESStateX1Y2 = lslAESMultiply(a, 0x0d) ^ lslAESMultiply(b, 0x09) ^ 
       lslAESMultiply(c, 0x0e) ^ lslAESMultiply(d, 0x0b);
   lslAESStateX1Y3 = lslAESMultiply(a, 0x0b) ^ lslAESMultiply(b, 0x0d) ^ 
       lslAESMultiply(c, 0x09) ^ lslAESMultiply(d, 0x0e);

   a = lslAESStateX2Y0;
   b = lslAESStateX2Y1;
   c = lslAESStateX2Y2;
   d = lslAESStateX2Y3;

   lslAESStateX2Y0 = lslAESMultiply(a, 0x0e) ^ lslAESMultiply(b, 0x0b) ^ 
       lslAESMultiply(c, 0x0d) ^ lslAESMultiply(d, 0x09);
   lslAESStateX2Y1 = lslAESMultiply(a, 0x09) ^ lslAESMultiply(b, 0x0e) ^ 
       lslAESMultiply(c, 0x0b) ^ lslAESMultiply(d, 0x0d);
   lslAESStateX2Y2 = lslAESMultiply(a, 0x0d) ^ lslAESMultiply(b, 0x09) ^ 
       lslAESMultiply(c, 0x0e) ^ lslAESMultiply(d, 0x0b);
   lslAESStateX2Y3 = lslAESMultiply(a, 0x0b) ^ lslAESMultiply(b, 0x0d) ^ 
       lslAESMultiply(c, 0x09) ^ lslAESMultiply(d, 0x0e);

   a = lslAESStateX3Y0;
   b = lslAESStateX3Y1;
   c = lslAESStateX3Y2;
   d = lslAESStateX3Y3;

   lslAESStateX3Y0 = lslAESMultiply(a, 0x0e) ^ lslAESMultiply(b, 0x0b) ^ 
       lslAESMultiply(c, 0x0d) ^ lslAESMultiply(d, 0x09);
   lslAESStateX3Y1 = lslAESMultiply(a, 0x09) ^ lslAESMultiply(b, 0x0e) ^ 
       lslAESMultiply(c, 0x0b) ^ lslAESMultiply(d, 0x0d);
   lslAESStateX3Y2 = lslAESMultiply(a, 0x0d) ^ lslAESMultiply(b, 0x09) ^ 
       lslAESMultiply(c, 0x0e) ^ lslAESMultiply(d, 0x0b);
   lslAESStateX3Y3 = lslAESMultiply(a, 0x0b) ^ lslAESMultiply(b, 0x0d) ^ 
       lslAESMultiply(c, 0x09) ^ lslAESMultiply(d, 0x0e);

}}

//##########################################################################// // ENCRYPTION SET-UP FUNCTIONS // //##########################################################################// // The following functions are used to set-up the AES encryption engine. // //##########################################################################// // Takes the key bytes provided and sets up the engine ready to encrypt or // decrypt using them. // // Thanks to Strife Onizuka for providing optimisations to this function. // pragma inline list lslAESKeyExpansion(list keyBytes) {{

   // Don't need the bit-count and the first round key is the key itself
   integer len = (
       (
           lslAESRoundKey = llDeleteSubList( // Remove header, copy into rounds
               (lslAESRoundKey = keyBytes = []) + keyBytes, 
               0, 
               0
           )
       ) != []); // Get the length

   // Check that we are within reasonable limits
   if ((len < 4) || (len > 8)) {
       lslAESRoundKey = [];
       return ["Invalid key size; must be 128, 192, or 256 bits!"];
   }

   // Calculate the number of required rounds
   lslAESRounds = len + 6;

   // All others are found from previous keys
   integer i = 0;
   integer x = (len * 3) + 28;

   integer t = llList2Integer(lslAESRoundKey, -1);

   do {
       if (!(i % len)) {
           // Rotate by 1, SubWord and Nudge 
           // [0x01020408, 0x01020408, 0x1b366cd8]
           t = ((lslAESGetSBoxByte((t >> 16) & 0xFF) ^ 
                    (0x000D8080 >> (7 ^ (i / len)))) << 24) | 
                (lslAESGetSBoxByte((t >>  8) & 0xFF) << 16) | 
                (lslAESGetSBoxByte((t      ) & 0xFF) <<  8) |
                 lslAESGetSBoxByte((t >> 24) & 0xFF);
       } else if ((len > 6) && (i % len) == 4) {
           // SubWord
           t = (lslAESGetSBoxByte((t >> 24) & 0xFF) << 24) | 
               (lslAESGetSBoxByte((t >> 16) & 0xFF) << 16) | 
               (lslAESGetSBoxByte((t >>  8) & 0xFF) <<  8) |
               (lslAESGetSBoxByte((t      ) & 0xFF)      );
       }

       // XOR k with four previous RoundKey values And add the 
       // new entries, yay!
       lslAESRoundKey = (lslAESRoundKey = []) + lslAESRoundKey + 
           (t = (t ^ llList2Integer(lslAESRoundKey, i)));
   } while ((i = -~i) < x);

   // On success no error message is returned
   return [1];

}}

// Takes a list of 32-bit words and uses them to initialise the // input vector used by CBC and similar modes. Data must contain // at least 128-bits of data (all else is discarded) // pragma inline list lslAESSetInputVector(list data) {{

   if ((data != []) < 5) 
       return ["Input vector must be at least 128-bits long"];

   // Ignore index 0 (the header)
   lslAESInputVector0 = llList2Integer(data, 1);
   lslAESInputVector1 = llList2Integer(data, 2);
   lslAESInputVector2 = llList2Integer(data, 3);
   lslAESInputVector3 = llList2Integer(data, 4);

   return [1];

}}

//##########################################################################// // SERIALISATION FUNCTIONS // //##########################################################################// // The following functions are used to serialise a string into a list of // // byte data for use as a key, or to encrypt/decrypt. // //##########################################################################// // Converts a binary-string into a list of 32-bit integers, using the provided // alphabet and character width (in bits). See the helper functions // lslAESBase64ToBytes and lslAESHexToBytes for examples of this function's // usage. The list returned will contain as its first-entry an integer count // of the number of bits represented by the input string // (llStringLength(s) * width). // // Thanks to Strife Onizuka for providing optimisations to this function. // pragma inline list lslAESStringToBytes(string s, integer width, string alphabet) {{

   integer l = llStringLength(s);

   list n = [l * width]; // Add bit-length
   integer bitbuf = 0;
   integer adjust = 32;

   integer i = 0;
   integer val;
   while (i < l) {
       val = llSubStringIndex(alphabet, llGetSubString(s, i, i));
       if (val < 0) {
           s = "";
           return (n = []) + 
               ["Invalid character at index "+(string)i];
       }

       if ((adjust -= width) <= 0) {
           bitbuf = bitbuf | (val >> -adjust);
           n += [bitbuf];

           adjust += 32;
           if (adjust < 32) bitbuf = (val << adjust);
           else bitbuf = 0;
       } else bitbuf = bitbuf | (val << adjust);

       ++i;
   }

   s = "";
   if (adjust < 32) 
       return (n = []) + n + [bitbuf];
   return (n = []) + n;

}}

// Takes a list of integers (with a bit-count as the first entry) and outputs // it as a string using characters from the given alphabet, where each character // represents a number of bits as described by width. Please refer to the helper // functions lslAESBytesToBase64 and lslAESBytesToHex for examples of this // function's usage. // // Thanks to Strife Onizuka for providing optimisations to this function. // pragma inline string lslAESBytesToString(list b, integer width, string alphabet) {{

   integer bits = llList2Integer(b, 0);

   integer i = 0;
   integer mask = ~(-1 << width);
   integer shift = 32 - width;

   integer available = 0;
   integer prev = 0;
   integer buf;
   integer extra;
   integer value;

   string s = "";

   @lslAESBytesToStringLoop;
   if((bits -= 32) > -32) {
       available += 32 + (bits * (0 > bits));
       buf = llList2Integer(b, ++i);
       if (available >= width) {
           if (prev) {
               s += llGetSubString(
                   alphabet, 
                   value = (
                       extra | 
                       (
                           (buf >> (shift + prev)) & 
                           ~(-1 << (width - prev))
                       )
                   ), 
                   value
               );
               buf = buf << (width - prev);
               available -= width;
           }
           while(available >= width) {
               s += llGetSubString(
                   alphabet, 
                   value = ((buf >> shift) & mask),
                   value
               );
               buf = buf << width;
               available -= width;
           }
           if (prev = available) // Update prev
               extra = (buf >> shift) & mask;
           jump lslAESBytesToStringLoop;
       }
   }
   if(available) {
       mask = -1 << (width - prev);
       return (s = "") + s + 
           llGetSubString(
               alphabet, 
               value = ((extra & mask) | 
                       (
                           (buf >> (shift + prev)) & 
                           ((-1 << (width - available)) ^ mask))
                       ), 
               value
           );
   }
   return (s = "") + s;

}}

// pragma inline error(integer link, string str, key id) {{

   llMessageLinked(
       link,
       LSLAES_FILTER_REPLY() | LSLAES_COMMAND_ERROR(),
       str,
       id
   );

}}

default { state_entry() { lslAESMode = LSLAES_MODE_DEFAULT(); lslAESPad = LSLAES_PAD_DEFAULT(); lslAESPadSize = LSLAES_PAD_SIZE_DEFAULT();

llOwnerSay((string)llGetFreeMemory() + " bytes free."); }

   link_message(integer x, integer y, string msg, key id) {        
       // Is the message for us?
       if ((y & LSLAES_FILTER_MASK()) == LSLAES_FILTER_REQUEST()) {
           // Refuse overly large messages
           if (llStringLength(msg) > LSLAES_MAX_SIZE()) {
               error(
                   x, 
                   "Maxmimum message length is " + 
                   	(string)LSLAES_MAX_SIZE() + 
                   	" characters", 
                   id
               );
               return;
           }

           // Special case for COMMAND_SETUP
           if ((y & LSLAES_COMMAND_MASK()) == LSLAES_COMMAND_SETUP()) {

if (SUPPORTS_SETUP()) { // Break up flags if (msg != "") { list flags = llCSV2List((msg = "") + msg); integer i = 0; integer l = (flags != []); integer j = 0; list flag = []; do { flag = [llToUpper(llList2String(flags, i))];

if (~(j = llListFindList(LSLAES_MODES(), flag))) lslAESMode = j; else if (~(j = llListFindList(LSLAES_PADS(), flag))) lslAESPad = j; else if ((string)flag == LSLAES_PAD_SIZE()) { j = llList2Integer(flags, ++i); // Next value should be pad-size if (j <= 0) j = LSLAES_PAD_SIZE_DEFAULT(); else if (j > LSLAES_PAD_SIZE_DEFAULT()) { error(x, "Maximum pad-size is "+(string)LSLAES_PAD_SIZE_DEFAULT()+" bits", id); return; } else if (j % 128) { error(x, "Pad-size must be a multiple of 128-bits", id); return; }

lslAESPadSize = j; } else { error(x, "Unsupported flag '"+(string)flag+"'", id); return; } } while ((++i) < l); }

// Construct reply llMessageLinked( x, LSLAES_FILTER_REPLY() | LSLAES_COMMAND_SETUP(), "", id ); } else error(x, "Set-up not supported (single-mode and pad available)", id);

return;

           }

           // What type of data do we have?
           integer type = y & LSLAES_INPUT_TYPE_MASK();
           list data = [];
           if ((type == LSLAES_INPUT_HEX()) || (type == LSLAES_INPUT_BASE64())) {
           	string alphabet = LSLAES_BASE64_CHARS();
           	integer width   = 6;
           	
           	if (type == LSLAES_INPUT_BASE64()) {

integer e = llSubStringIndex(msg, "="); if (e > 0) msg = llDeleteSubString( (msg = "") + msg, e, -1 );

           	} else {

if (llGetSubString(msg, 0, 1) == "0x") msg = llDeleteSubString((msg = "") + msg, 0, 1); msg = llToLower((msg = "") + msg);

alphabet = LSLAES_HEX_CHARS(); width = 4;

           	}
           	
               data = lslAESStringToBytes(
               	(msg = "") + msg, 
               	width, 
               	(alphabet = "") + alphabet
               );
           } else data = [(msg = "") + "Unsupported input-type"];

           // Was data parsed successfully?
           if (llGetListEntryType(data, 0) != TYPE_INTEGER) {
               error(x, llList2String((data = []) + data, 0), id);
               return;
           }

           // Now determine mode of operation
           type = y & LSLAES_COMMAND_MASK();
           if (type == LSLAES_COMMAND_PRIME()) 
               data = lslAESKeyExpansion((data = []) + data);
           else if (type == LSLAES_COMMAND_ENCRYPT()) 
               data = lslAESPadCipher((data = []) + data);
           else if (type == LSLAES_COMMAND_DECRYPT()) 
               data = lslAESInvertPadCipher((data = []) + data);
           else if (type == LSLAES_COMMAND_INIT()) 
               data = lslAESSetInputVector((data = []) + data);
           else data = ["Unsupported mode"];

           // Was mode executed successfully?
           if (llGetListEntryType(data, 0) != TYPE_INTEGER) {
               error(x, llList2String((data = []) + data, 0), id);
               return;
           }

           // Convert into requested output type
           integer output = 0;

           if ((type != LSLAES_COMMAND_PRIME()) && (type != LSLAES_COMMAND_INIT())) {
               output = y & LSLAES_OUTPUT_TYPE_MASK();
               if ((output == LSLAES_OUTPUT_BASE64()) || (output == LSLAES_OUTPUT_HEX())) {
               	string alphabet = LSLAES_BASE64_CHARS();
               	integer width   = 6;
               	
               	if (output == LSLAES_OUTPUT_HEX()) {
               		alphabet = LSLAES_HEX_CHARS();
               		width = 4;
               	}
               	
               	msg = lslAESBytesToString(
               		(data = []) + data, 
               		width, 
               		(alphabet = "") + alphabet
               	);
               	
               	if (output == LSLAES_OUTPUT_BASE64()) {

integer l = llStringLength(msg) % 4; if (l && (l < 3)) { string add = ""; if (l == 2) add = "=="; else if (l == 1) add = "=";

msg = (msg = add = "") + msg + add; } output = LSLAES_INPUT_BASE64();

               	} else {
               		msg = (msg = "") + "0x" + msg;
               		output = LSLAES_INPUT_HEX();
               	}
               } else {
                   error(x, "Invalid output type", id);
                   return;
               }
           }

           // Construct reply
           llMessageLinked(
               x,
               LSLAES_FILTER_REPLY() | type | output,
               (msg = "") + msg,
               id
           );
       }
   }

}</lsl>

Examples

Encryption

<lsl>$import AES.AES_Helper.lslm();

integer aesLink = LINK_THIS;

string myKey = "1234567890ABCDEF0123456789ABCDEF"; // 128-bit key in hex string myMsg = "Hello world! I am a lovely message waiting to be encrypted!"; string myIV = "89ABCDEF0123456789ABCDEF01234567";

default {

   state_entry() { // Setup the engine for use
       lslAESSetup(
           aesLink,
           llDumpList2String(
           	[

LSLAES_MODE_CFB(), // CFB requires a less complex broker LSLAES_PAD_NONE(), // No padding LSLAES_PAD_SIZE(), // Pad into blocks of 512-bits 512 ], "," ),

           llGetKey()
       );
   }

   link_message(integer x, integer y, string msg, key id) {
       if (!lslAESIsReply(y, id)) return;

       y = lslAESGetReplyMode(y);
       if (y == LSLAES_COMMAND_ERROR()) 
           llOwnerSay("SETUP ERROR: "+msg);
       else if (y == LSLAES_COMMAND_SETUP()) 
           state prime;
   }

}

state prime {

   state_entry() { // First prime the engine with a key
       lslAESPrimeHexKey(
           aesLink,
           myKey,
           llGetKey()
       );
   }

   link_message(integer x, integer y, string msg, key id) {
       if (!lslAESIsReply(y, id)) return;

       y = lslAESGetReplyMode(y);
       if (y == LSLAES_COMMAND_ERROR()) 
           llOwnerSay("PRIME ERROR: "+msg);
       else if (y == LSLAES_COMMAND_PRIME()) 
           state init;
   }

}

state init {

   state_entry() { // Now init the engine with an input vector
       lslAESInitHexIV(
           aesLink,
           myIV,
           llGetKey()
       );
   }

   link_message(integer x, integer y, string msg, key id) {
       if (!lslAESIsReply(y, id)) return;

       y = lslAESGetReplyMode(y);
       if (y == LSLAES_COMMAND_ERROR()) 
           llOwnerSay("INIT ERROR: "+msg);
       else if (y == LSLAES_COMMAND_INIT()) 
           state encrypt;
   }

}

state encrypt {

   state_entry() { // Send our message
       lslAESEncryptBase64ToBase64(
           aesLink,
           llStringToBase64(myMsg),
           llGetOwner()
       );
   }

   link_message(integer x, integer y, string msg, key id) {
       if (!lslAESIsReply(y, id)) return;

       y = lslAESGetReplyMode(y);
       if (y == LSLAES_COMMAND_ERROR()) 
           llOwnerSay("ENCRYPT ERROR: "+msg);
       else if (y == LSLAES_COMMAND_ENCRYPT()) 
           llOwnerSay("Encrypted: "+msg);
   }

}</lsl>

Decryption

<lsl>$import AES.AES_Helper.lslm();

integer aesLink = LINK_THIS;

string myKey = "1234567890ABCDEF0123456789ABCDEF"; // 128-bit key in hex string myMsg = "Mdn6jGTwRPMOKTYTTdDKGm9KScz26LIz96KVOGAeMw3hpwByPfa07PDRHxRW4TIh5dmu5LlhKpTQChi="; string myIV = "89ABCDEF0123456789ABCDEF01234567";

default {

   state_entry() { // Setup the engine for use
   	state prime;
       lslAESSetup(
           aesLink,
           llDumpList2String(
           	[

LSLAES_MODE_CFB(), // CFB requires a less complex broker LSLAES_PAD_NONE(), // No padding LSLAES_PAD_SIZE(), // Pad into blocks of 512-bits 512 ], "," ),

           llGetKey()
       );
   }

   link_message(integer x, integer y, string msg, key id) {
       if (!lslAESIsReply(y, id)) return;

       y = lslAESGetReplyMode(y);
       if (y == LSLAES_COMMAND_ERROR()) 
           llOwnerSay("SETUP ERROR: "+msg);
       else if (y == LSLAES_COMMAND_SETUP()) 
           state prime;
   }

}

state prime {

   state_entry() { // First prime the engine with a key
       lslAESPrimeHexKey(
           aesLink,
           myKey,
           llGetKey()
       );
   }

   link_message(integer x, integer y, string msg, key id) {
       if (!lslAESIsReply(y, id)) return;

       y = lslAESGetReplyMode(y);
       if (y == LSLAES_COMMAND_ERROR()) 
           llOwnerSay("PRIME ERROR: "+msg);
       else if (y == LSLAES_COMMAND_PRIME()) 
           state init;
   }

}

state init {

   state_entry() { // Now init the engine with an input vector
       lslAESInitHexIV(
           aesLink,
           myIV,
           llGetKey()
       );
   }

   link_message(integer x, integer y, string msg, key id) {
       if (!lslAESIsReply(y, id)) return;

       y = lslAESGetReplyMode(y);
       if (y == LSLAES_COMMAND_ERROR()) 
           llOwnerSay("INIT ERROR: "+msg);
       else if (y == LSLAES_COMMAND_INIT()) 
           state decrypt;
   }

}

state decrypt {

   state_entry() { // Send our message
       lslAESDecryptBase64ToBase64(
           aesLink,
           myMsg,
           llGetOwner()
       );
   }

   link_message(integer x, integer y, string msg, key id) {
       if (!lslAESIsReply(y, id)) return;

       y = lslAESGetReplyMode(y);
       if (y == LSLAES_COMMAND_ERROR()) 
           llOwnerSay("DECRYPT ERROR: "+msg);
       else if (y == LSLAES_COMMAND_DECRYPT()) 
           llOwnerSay("Decrypted: "+llBase64ToString(msg));
   }

}</lsl>