Difference between revisions of "AES Strong Encryption"

From Second Life Wiki
Jump to navigation Jump to search
Line 117: Line 117:
|}
|}


=== Code ===
integer LSLAES_COMMS_BASE      = 32768;
Below is the code. It will only compile within Mono at the moment due to the memory requirements. You may edit the constants used for communication at the top. It should be noted that for speed, this implementation uses a set of 16 integers as the state, and unrolls all loops into a set of instructions for speed. Also, data is processed as a list of integers rather than bytes to conserve memory, and where relevant a bit-header is attached to the top of a list to aid with serialisation.
 
<lsl>integer LSLAES_COMMS_BASE      = 32768;
integer LSLAES_COMMS_REPLY_BASE = 65536;
integer LSLAES_COMMS_REPLY_BASE = 65536;
 
integer LSLAES_COMMS_BASE64    = 1;
integer LSLAES_COMMS_BASE64    = 1;
integer LSLAES_COMMS_HASH      = 256;
integer LSLAES_COMMS_PRIME      = 1024;
integer LSLAES_COMMS_PRIME      = 1024;
integer LSLAES_COMMS_ENCRYPT    = 4096;
integer LSLAES_COMMS_ENCRYPT    = 4096;
integer LSLAES_COMMS_DECRYPT    = 16384;
integer LSLAES_COMMS_DECRYPT    = 16384;


integer LSLAES_HASH_SIZE        = 512;
// Defines how "wide" the range of comms values are
// Defines how "wide" the range of comms values are
integer LSLAES_COMMS_WIDTH      = 32767;
integer LSLAES_COMMS_WIDTH      = 32767;
 
// Refuse any data longer than this many characters
// Refuse any data longer than this many characters
integer LSLAES_MAX_SIZE        = 1536;
integer LSLAES_MAX_SIZE        = 1536;
 
string  LSLAES_HEX_CHARS        = "0123456789ABCDEF";
string  LSLAES_HEX_CHARS        = "0123456789ABCDEF";
string  LSLAES_BASE64_CHARS    = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
string  LSLAES_BASE64_CHARS    = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
// The inverted Rijndael SBox, values in the state are substituted with these
// The inverted Rijndael SBox, values in the state are substituted with these
list    LSLAES_SBOX_INVERTED    = [
list    LSLAES_SBOX_INVERTED    = [
Line 153: Line 153:
     0x172b047e, 0xba77d626, 0xe1691463, 0x55210c7d
     0x172b047e, 0xba77d626, 0xe1691463, 0x55210c7d
];
];
 
integer lslAESSBoxInvertedCached = -1;
integer lslAESSBoxInvertedCached = -1;
integer lslAESSBoxInvertedCache  = 0;
integer lslAESSBoxInvertedCache  = 0;
 
// Treats the above list as a byte-array, retrieving the desired byte value.
// Treats the above list as a byte-array, retrieving the desired byte value.
integer lslAESGetSBoxInvertedByte(integer n) {     
integer lslAESGetSBoxInvertedByte(integer n) {     
Line 162: Line 162:
             n /= 4;    // Integer
             n /= 4;    // Integer
     b = 3 - b;
     b = 3 - b;
 
     if (lslAESSBoxInvertedCached == n) n = lslAESSBoxInvertedCache;
     if (lslAESSBoxInvertedCached == n) n = lslAESSBoxInvertedCache;
     else {
     else {
Line 170: Line 170:
     return (lslAESSBoxInvertedCache >> (b << 3)) & 0xFF;
     return (lslAESSBoxInvertedCache >> (b << 3)) & 0xFF;
}
}
 
// The Rijndael SBox, values in the state are substituted with these
// The Rijndael SBox, values in the state are substituted with these
list    LSLAES_SBOX            = [
list    LSLAES_SBOX            = [
Line 187: Line 187:
     0x8ca1890d, 0xbfe64268, 0x41992d0f, 0xb054bb16
     0x8ca1890d, 0xbfe64268, 0x41992d0f, 0xb054bb16
];
];
 
integer lslAESSBoxCached        = -1;
integer lslAESSBoxCached        = -1;
integer lslAESSBoxCache        = 0;
integer lslAESSBoxCache        = 0;
 
// Treats the above list as a byte-array, retrieving the desired byte value.
// Treats the above list as a byte-array, retrieving the desired byte value.
integer lslAESGetSBoxByte(integer n) {     
integer lslAESGetSBoxByte(integer n) {     
Line 196: Line 196:
             n /= 4;    // Integer
             n /= 4;    // Integer
     b = 3 - b;
     b = 3 - b;
 
     if (lslAESSBoxCached == n) n = lslAESSBoxCache;
     if (lslAESSBoxCached == n) n = lslAESSBoxCache;
     else {
     else {
Line 204: Line 204:
     return (lslAESSBoxCache >> (b << 3)) & 0xFF;
     return (lslAESSBoxCache >> (b << 3)) & 0xFF;
}
}
 
// Used for the actual encryption, generated from Key
// Used for the actual encryption, generated from Key
list    lslAESRoundKey          = [];
list    lslAESRoundKey          = [];
// The number of rounds to perform (bigger key == more rounds)
// The number of rounds to perform (bigger key == more rounds)
integer lslAESRounds            = 0;
integer lslAESRounds            = 0;
 
// Used for padding to a multiple of key-length
integer lslAESPadSize          = 128;
// The following are used for the state instead of a list
// The following are used for the state instead of a list
integer lslAESStateX0Y0        = 0;
integer lslAESStateX0Y0        = 0;
Line 227: Line 229:
integer lslAESStateX3Y2        = 0;
integer lslAESStateX3Y2        = 0;
integer lslAESStateX3Y3        = 0;
integer lslAESStateX3Y3        = 0;
 
//##########################################################################//
//##########################################################################//
//                            HIGH-LEVEL FUNCTIONS                          //
//                            HIGH-LEVEL FUNCTIONS                          //
Line 233: Line 235:
//  The following functions are the ones to call to encrypt/decrypt        //
//  The following functions are the ones to call to encrypt/decrypt        //
//##########################################################################//
//##########################################################################//
// Calculate a length-bit hash using the currently expanded key. Length MUST
// be a multiple of 32.
list lslAESHash(list data, integer length) {
    // First pad data to a length * 2
    integer l = llList2Integer(data, 0);
    integer i = 0;
    integer j = length << 1;
   
    // If too short, then add values
    if (l < j) {
        integer a = (j - l) / 32;
        do data = (data = []) + data + [llList2Integer(data, i)];
        while ((++i) < a);
        l = j;
    }
   
    // Irreversibly reduce to length by XORing
    j = l / length;
    integer w = j - 1;
    l = data != [];
    list t = [length];
    integer b = 0;
    i = 1;
    do {
        b = b ^ llList2Integer(data, i);
        if ((i % j) == w) {
            t = (t = []) + t + [b];
            b = 0;
        }
    } while ((++i) < l);
    data = (data = t = []) + t;
   
    // Add any extra data
    if ((data != []) < (length / 32))
        data = (data = []) + data + [b];
   
    // Calculate hash   
    data = lslAESCipher((data = []) + data);
   
    // Reduce to size
    l = (data != []) - (length / 32);
    return [length] + llList2List((data = []) + data, 1, -l);
}
// Decrypts a list of integers into a list of decrypted integers. Padding is  
// Decrypts a list of integers into a list of decrypted integers. Padding is  
// automatically removed as-per the method used by lslAESCipher.
// automatically removed as-per the method used by lslAESCipher.
Line 239: Line 285:
     integer bits = llList2Integer(data, 0);
     integer bits = llList2Integer(data, 0);
     data = llList2List((data = []) + data, 1, -1);
     data = llList2List((data = []) + data, 1, -1);
   
     integer i = 0; integer j = 0;
     integer i = 0; integer j = 0;
     integer l = (data != []);
     integer l = (data != []);
Line 265: Line 311:
         lslAESStateX3Y2 = ((j >> 8)  & 0xFF);
         lslAESStateX3Y2 = ((j >> 8)  & 0xFF);
         lslAESStateX3Y3 = (j        & 0xFF);
         lslAESStateX3Y3 = (j        & 0xFF);
       
         // Add last round key before rounds begin
         // Add last round key before rounds begin
         lslAESAddRoundKey(lslAESRounds);
         lslAESAddRoundKey(lslAESRounds);
       
         // There will be Rounds - 1 identical rounds
         // There will be Rounds - 1 identical rounds
         j = lslAESRounds - 1;
         j = lslAESRounds - 1;
Line 277: Line 323:
             lslAESInvertMixColumns();
             lslAESInvertMixColumns();
         }
         }
       
         // The last round requires no mixing
         // The last round requires no mixing
         lslAESInvertShiftRows();
         lslAESInvertShiftRows();
         lslAESInvertSubBytes();
         lslAESInvertSubBytes();
         lslAESAddRoundKey(0);
         lslAESAddRoundKey(0);
       
         if (l > 4)  
         if (l > 4)  
             data = llList2List((data = []) + data, 4, -1);
             data = llList2List((data = []) + data, 4, -1);
         else data = [];
         else data = [];
         l -= 4;
         l -= 4;
       
         // XOR with i
         // XOR with i
         output = (output = []) + output + [
         output = (output = []) + output + [
Line 301: Line 347:
         i = (++i % 256);
         i = (++i % 256);
     }
     }
    // Get bit length
    bits = j = llList2Integer(output, 0);
    if ((j < 0) || (j > 8192)) return (output = []) + [0];
   
    // Discard padding and return
    j /= 32;
    if (bits % 32) ++j;
      
      
    // Remove padding bytes and add bit-header
     return llList2List((output = []) + output, 0, j);
    j = llList2Integer(output, -1) & 0xFF;
     return [bits - j] +
        llList2List((output = []) + output, 0, -((j / 32) + 1));
}
}
 
// Encrypts a list of integers into a list of encrypted integers. Input is  
// Encrypts a list of integers into a list of encrypted integers.
// padded using the number of required pad entries. Thus, after decryption
// Input is padded to be a multiple of lslAESPadSize (key-length rounded
// you will need to use the last byte of the decrypted string, to find how
// up to nearest 128 multiple) using zero-bytes, with the first 32-bit word
// many bytes in total were added as padding, so you may trim them. e.g - if
// of ciphertext indicating how many bits long the data is. If the input is
// the byte  is of value 12 then a total of 12 bytes were added, including the  
// already a multiple then a full lslAESPadSize bits are added.
// one you read. Padding is ALWAYS added, even if the input is correctly
// In addition, blocks are XORed with a counter to reduce patterns, this
// divisible (in which case a full-block of padding is added).
// counter is numbered such that all the bytes of the first block are
// XORed with 0, the bytes of the second block are XORed with 1, etc., the  
// counter resets to zero after 255
list lslAESCipher(list data) {
list lslAESCipher(list data) {
     // Preserve the bit-count header for output
     // Preserve the bit-count header for output
     integer bits = llList2Integer(data, 0);
     integer bits = llList2Integer(data, 0);
     data = llList2List((data = []) + data, 1, -1);
     data = llDeleteSubList((data = []) + data, 0, 0);
   
     // Add padding integers
     // Add padding integers
     integer l = data != [];
     integer l = data != [];
     integer j = 128 - (bits % 128);
     integer j = lslAESPadSize - (bits % lslAESPadSize);
   
     integer t = j / 32;
     integer t = j / 32;
    integer i = 1;
     while ((--t) >= 1) data = (data = []) + data + [0];
     while (i < t) data = (data = []) + data + [++i];
     data = (data = []) + [bits] + data;
     data = (data = []) + data + [j];
 
    list output = [bits + j];
   
    i = 0;
     l = (data != []);
     l = (data != []);
    list output = [l << 5]; // Total bits
    integer i = 0;
     while (l > 0) {
     while (l > 0) {
         // Copy some input into state variables, and XOR with i
         // Copy some input into state variables, and XOR with i
Line 355: Line 407:
         lslAESStateX3Y2 = ((j >> 8)  & 0xFF) ^ i;
         lslAESStateX3Y2 = ((j >> 8)  & 0xFF) ^ i;
         lslAESStateX3Y3 = (j        & 0xFF) ^ i;
         lslAESStateX3Y3 = (j        & 0xFF) ^ i;
       
         // Add first round-key before starting rounds
         // Add first round-key before starting rounds
         lslAESAddRoundKey(0);
         lslAESAddRoundKey(0);
       
         // There will be Rounds iterations, the first  
         // There will be Rounds iterations, the first  
         // Rounds - 1 are identical.
         // Rounds - 1 are identical.
Line 368: Line 420:
             lslAESAddRoundKey(j++);
             lslAESAddRoundKey(j++);
         }
         }
       
         // The last column performs no mix
         // The last column performs no mix
         lslAESSubBytes();
         lslAESSubBytes();
         lslAESShiftRows();
         lslAESShiftRows();
         lslAESAddRoundKey(lslAESRounds);
         lslAESAddRoundKey(lslAESRounds);
       
         if (l > 4)  
         if (l > 4)  
             data = llList2List((data = []) + data, 4, -1);
             data = llList2List((data = []) + data, 4, -1);
         else data = [];
         else data = [];
         l -= 4;
         l -= 4;
       
         output = (output = []) + output + [
         output = (output = []) + output + [
             (lslAESStateX0Y0 << 24) | (lslAESStateX0Y1 << 16) |  
             (lslAESStateX0Y0 << 24) | (lslAESStateX0Y1 << 16) |  
Line 391: Line 443:
         i = (++i % 256);
         i = (++i % 256);
     }
     }
   
     return (output = []) + output;
     return (output = []) + output;
}
}
 
 
//##########################################################################//
//##########################################################################//
//                            PROCESSING FUNCTIONS                          //
//                            PROCESSING FUNCTIONS                          //
Line 405: Line 457:
lslAESAddRoundKey(integer round) {
lslAESAddRoundKey(integer round) {
     round = round << 2;
     round = round << 2;
   
     integer t = llList2Integer(lslAESRoundKey, round);
     integer t = llList2Integer(lslAESRoundKey, round);
     lslAESStateX0Y0 = lslAESStateX0Y0 ^ ((t >> 24) & 0xFF);
     lslAESStateX0Y0 = lslAESStateX0Y0 ^ ((t >> 24) & 0xFF);
Line 411: Line 463:
     lslAESStateX0Y2 = lslAESStateX0Y2 ^ ((t >> 8)  & 0xFF);
     lslAESStateX0Y2 = lslAESStateX0Y2 ^ ((t >> 8)  & 0xFF);
     lslAESStateX0Y3 = lslAESStateX0Y3 ^ (t        & 0xFF);
     lslAESStateX0Y3 = lslAESStateX0Y3 ^ (t        & 0xFF);
   
     t = llList2Integer(lslAESRoundKey, ++round);
     t = llList2Integer(lslAESRoundKey, ++round);
     lslAESStateX1Y0 = lslAESStateX1Y0 ^ ((t >> 24) & 0xFF);
     lslAESStateX1Y0 = lslAESStateX1Y0 ^ ((t >> 24) & 0xFF);
Line 417: Line 469:
     lslAESStateX1Y2 = lslAESStateX1Y2 ^ ((t >> 8)  & 0xFF);
     lslAESStateX1Y2 = lslAESStateX1Y2 ^ ((t >> 8)  & 0xFF);
     lslAESStateX1Y3 = lslAESStateX1Y3 ^ (t        & 0xFF);
     lslAESStateX1Y3 = lslAESStateX1Y3 ^ (t        & 0xFF);
   
     t = llList2Integer(lslAESRoundKey, ++round);
     t = llList2Integer(lslAESRoundKey, ++round);
     lslAESStateX2Y0 = lslAESStateX2Y0 ^ ((t >> 24) & 0xFF);
     lslAESStateX2Y0 = lslAESStateX2Y0 ^ ((t >> 24) & 0xFF);
Line 423: Line 475:
     lslAESStateX2Y2 = lslAESStateX2Y2 ^ ((t >> 8)  & 0xFF);
     lslAESStateX2Y2 = lslAESStateX2Y2 ^ ((t >> 8)  & 0xFF);
     lslAESStateX2Y3 = lslAESStateX2Y3 ^ (t        & 0xFF);
     lslAESStateX2Y3 = lslAESStateX2Y3 ^ (t        & 0xFF);
   
     t = llList2Integer(lslAESRoundKey, ++round);
     t = llList2Integer(lslAESRoundKey, ++round);
     lslAESStateX3Y0 = lslAESStateX3Y0 ^ ((t >> 24) & 0xFF);
     lslAESStateX3Y0 = lslAESStateX3Y0 ^ ((t >> 24) & 0xFF);
Line 430: Line 482:
     lslAESStateX3Y3 = lslAESStateX3Y3 ^ (t        & 0xFF);
     lslAESStateX3Y3 = lslAESStateX3Y3 ^ (t        & 0xFF);
}
}
 
// Performs a substitution using SBox
// Performs a substitution using SBox
lslAESSubBytes() {
lslAESSubBytes() {
Line 450: Line 502:
     lslAESStateX3Y3 = lslAESGetSBoxByte(lslAESStateX3Y3);
     lslAESStateX3Y3 = lslAESGetSBoxByte(lslAESStateX3Y3);
}
}
 
// Performs a substition using SBoxInverted
// Performs a substition using SBoxInverted
lslAESInvertSubBytes() {
lslAESInvertSubBytes() {
Line 470: Line 522:
     lslAESStateX3Y3 = lslAESGetSBoxInvertedByte(lslAESStateX3Y3);
     lslAESStateX3Y3 = lslAESGetSBoxInvertedByte(lslAESStateX3Y3);
}
}
   
 
// Performs row shifts
// Performs row shifts
lslAESShiftRows() {
lslAESShiftRows() {
     integer t = 0;
     integer t = 0;
   
     // Rotate first row 1 columns to left
     // Rotate first row 1 columns to left
     t = lslAESStateX1Y0;
     t = lslAESStateX1Y0;
Line 482: Line 534:
     lslAESStateX1Y2 = lslAESStateX1Y3;
     lslAESStateX1Y2 = lslAESStateX1Y3;
     lslAESStateX1Y3 = t;
     lslAESStateX1Y3 = t;
   
     // Rotate second row 2 columns to left
     // Rotate second row 2 columns to left
     t = lslAESStateX2Y0;
     t = lslAESStateX2Y0;
     lslAESStateX2Y0 = lslAESStateX2Y2;
     lslAESStateX2Y0 = lslAESStateX2Y2;
     lslAESStateX2Y2 = t;
     lslAESStateX2Y2 = t;
   
     t = lslAESStateX2Y1;
     t = lslAESStateX2Y1;
     lslAESStateX2Y1 = lslAESStateX2Y3;
     lslAESStateX2Y1 = lslAESStateX2Y3;
     lslAESStateX2Y3 = t;
     lslAESStateX2Y3 = t;
   
     // Rotate third row 3 columns to left
     // Rotate third row 3 columns to left
     t = lslAESStateX3Y0;
     t = lslAESStateX3Y0;
Line 499: Line 551:
     lslAESStateX3Y1 = t;
     lslAESStateX3Y1 = t;
}
}
 
// Undoes a set of row shifts
// Undoes a set of row shifts
lslAESInvertShiftRows() {
lslAESInvertShiftRows() {
     integer t = 0;
     integer t = 0;
   
     // Rotate first row 1 columns to right
     // Rotate first row 1 columns to right
     t = lslAESStateX1Y3;
     t = lslAESStateX1Y3;
Line 510: Line 562:
     lslAESStateX1Y1 = lslAESStateX1Y0;
     lslAESStateX1Y1 = lslAESStateX1Y0;
     lslAESStateX1Y0 = t;
     lslAESStateX1Y0 = t;
   
     // Rotate second row 2 columns to right
     // Rotate second row 2 columns to right
     t = lslAESStateX2Y0;
     t = lslAESStateX2Y0;
     lslAESStateX2Y0 = lslAESStateX2Y2;
     lslAESStateX2Y0 = lslAESStateX2Y2;
     lslAESStateX2Y2 = t;
     lslAESStateX2Y2 = t;
   
     t = lslAESStateX2Y1;
     t = lslAESStateX2Y1;
     lslAESStateX2Y1 = lslAESStateX2Y3;
     lslAESStateX2Y1 = lslAESStateX2Y3;
     lslAESStateX2Y3 = t;
     lslAESStateX2Y3 = t;
   
     // Rotate third row 3 columns to right
     // Rotate third row 3 columns to right
     t = lslAESStateX3Y0;
     t = lslAESStateX3Y0;
Line 527: Line 579:
     lslAESStateX3Y3 = t;
     lslAESStateX3Y3 = t;
}
}
 
// Mixes columns of the state
// Mixes columns of the state
lslAESMixColumns() {     
lslAESMixColumns() {     
Line 533: Line 585:
     integer t1 = lslAESStateX0Y0 ^ lslAESStateX1Y0 ^  
     integer t1 = lslAESStateX0Y0 ^ lslAESStateX1Y0 ^  
         lslAESStateX2Y0 ^ lslAESStateX3Y0;
         lslAESStateX2Y0 ^ lslAESStateX3Y0;
   
     integer t2 = lslAESXTimes(lslAESStateX0Y0 ^ lslAESStateX1Y0);
     integer t2 = lslAESXTimes(lslAESStateX0Y0 ^ lslAESStateX1Y0);
     lslAESStateX0Y0 = lslAESStateX0Y0 ^ t2 ^ t1;
     lslAESStateX0Y0 = lslAESStateX0Y0 ^ t2 ^ t1;
   
     t2 = lslAESXTimes(lslAESStateX1Y0 ^ lslAESStateX2Y0);
     t2 = lslAESXTimes(lslAESStateX1Y0 ^ lslAESStateX2Y0);
     lslAESStateX1Y0 = lslAESStateX1Y0 ^ t2 ^ t1;
     lslAESStateX1Y0 = lslAESStateX1Y0 ^ t2 ^ t1;
   
     t2 = lslAESXTimes(lslAESStateX2Y0 ^ lslAESStateX3Y0);
     t2 = lslAESXTimes(lslAESStateX2Y0 ^ lslAESStateX3Y0);
     lslAESStateX2Y0 = lslAESStateX2Y0 ^ t2 ^ t1;
     lslAESStateX2Y0 = lslAESStateX2Y0 ^ t2 ^ t1;
   
     t2 = lslAESXTimes(lslAESStateX3Y0 ^ t);
     t2 = lslAESXTimes(lslAESStateX3Y0 ^ t);
     lslAESStateX3Y0 = lslAESStateX3Y0 ^ t2 ^ t1;
     lslAESStateX3Y0 = lslAESStateX3Y0 ^ t2 ^ t1;
   
     t = lslAESStateX0Y1;
     t = lslAESStateX0Y1;
     t1 = lslAESStateX0Y1 ^ lslAESStateX1Y1 ^ lslAESStateX2Y1 ^ lslAESStateX3Y1;
     t1 = lslAESStateX0Y1 ^ lslAESStateX1Y1 ^ lslAESStateX2Y1 ^ lslAESStateX3Y1;
   
     t2 = lslAESXTimes(lslAESStateX0Y1 ^ lslAESStateX1Y1);
     t2 = lslAESXTimes(lslAESStateX0Y1 ^ lslAESStateX1Y1);
     lslAESStateX0Y1 = lslAESStateX0Y1 ^ t2 ^ t1;
     lslAESStateX0Y1 = lslAESStateX0Y1 ^ t2 ^ t1;
   
     t2 = lslAESXTimes(lslAESStateX1Y1 ^ lslAESStateX2Y1);
     t2 = lslAESXTimes(lslAESStateX1Y1 ^ lslAESStateX2Y1);
     lslAESStateX1Y1 = lslAESStateX1Y1 ^ t2 ^ t1;
     lslAESStateX1Y1 = lslAESStateX1Y1 ^ t2 ^ t1;
   
     t2 = lslAESXTimes(lslAESStateX2Y1 ^ lslAESStateX3Y1);
     t2 = lslAESXTimes(lslAESStateX2Y1 ^ lslAESStateX3Y1);
     lslAESStateX2Y1 = lslAESStateX2Y1 ^ t2 ^ t1;
     lslAESStateX2Y1 = lslAESStateX2Y1 ^ t2 ^ t1;
   
     t2 = lslAESXTimes(lslAESStateX3Y1 ^ t);
     t2 = lslAESXTimes(lslAESStateX3Y1 ^ t);
     lslAESStateX3Y1 = lslAESStateX3Y1 ^ t2 ^ t1;
     lslAESStateX3Y1 = lslAESStateX3Y1 ^ t2 ^ t1;
   
     t = lslAESStateX0Y2;
     t = lslAESStateX0Y2;
     t1 = lslAESStateX0Y2 ^ lslAESStateX1Y2 ^ lslAESStateX2Y2 ^ lslAESStateX3Y2;
     t1 = lslAESStateX0Y2 ^ lslAESStateX1Y2 ^ lslAESStateX2Y2 ^ lslAESStateX3Y2;
   
     t2 = lslAESXTimes(lslAESStateX0Y2 ^ lslAESStateX1Y2);
     t2 = lslAESXTimes(lslAESStateX0Y2 ^ lslAESStateX1Y2);
     lslAESStateX0Y2 = lslAESStateX0Y2 ^ t2 ^ t1;
     lslAESStateX0Y2 = lslAESStateX0Y2 ^ t2 ^ t1;
   
     t2 = lslAESXTimes(lslAESStateX1Y2 ^ lslAESStateX2Y2);
     t2 = lslAESXTimes(lslAESStateX1Y2 ^ lslAESStateX2Y2);
     lslAESStateX1Y2 = lslAESStateX1Y2 ^ t2 ^ t1;
     lslAESStateX1Y2 = lslAESStateX1Y2 ^ t2 ^ t1;
   
     t2 = lslAESXTimes(lslAESStateX2Y2 ^ lslAESStateX3Y2);
     t2 = lslAESXTimes(lslAESStateX2Y2 ^ lslAESStateX3Y2);
     lslAESStateX2Y2 = lslAESStateX2Y2 ^ t2 ^ t1;
     lslAESStateX2Y2 = lslAESStateX2Y2 ^ t2 ^ t1;
   
     t2 = lslAESXTimes(lslAESStateX3Y2 ^ t);
     t2 = lslAESXTimes(lslAESStateX3Y2 ^ t);
     lslAESStateX3Y2 = lslAESStateX3Y2 ^ t2 ^ t1;
     lslAESStateX3Y2 = lslAESStateX3Y2 ^ t2 ^ t1;
   
     t = lslAESStateX0Y3;
     t = lslAESStateX0Y3;
     t1 = lslAESStateX0Y3 ^ lslAESStateX1Y3 ^ lslAESStateX2Y3 ^ lslAESStateX3Y3;
     t1 = lslAESStateX0Y3 ^ lslAESStateX1Y3 ^ lslAESStateX2Y3 ^ lslAESStateX3Y3;
   
     t2 = lslAESXTimes(lslAESStateX0Y3 ^ lslAESStateX1Y3);
     t2 = lslAESXTimes(lslAESStateX0Y3 ^ lslAESStateX1Y3);
     lslAESStateX0Y3 = lslAESStateX0Y3 ^ t2 ^ t1;
     lslAESStateX0Y3 = lslAESStateX0Y3 ^ t2 ^ t1;
   
     t2 = lslAESXTimes(lslAESStateX1Y3 ^ lslAESStateX2Y3);
     t2 = lslAESXTimes(lslAESStateX1Y3 ^ lslAESStateX2Y3);
     lslAESStateX1Y3 = lslAESStateX1Y3 ^ t2 ^ t1;
     lslAESStateX1Y3 = lslAESStateX1Y3 ^ t2 ^ t1;
   
     t2 = lslAESXTimes(lslAESStateX2Y3 ^ lslAESStateX3Y3);
     t2 = lslAESXTimes(lslAESStateX2Y3 ^ lslAESStateX3Y3);
     lslAESStateX2Y3 = lslAESStateX2Y3 ^ t2 ^ t1;
     lslAESStateX2Y3 = lslAESStateX2Y3 ^ t2 ^ t1;
   
     t2 = lslAESXTimes(lslAESStateX3Y3 ^ t);
     t2 = lslAESXTimes(lslAESStateX3Y3 ^ t);
     lslAESStateX3Y3 = lslAESStateX3Y3 ^ t2 ^ t1;
     lslAESStateX3Y3 = lslAESStateX3Y3 ^ t2 ^ t1;
}
}
 
// Used when column mixing
// Used when column mixing
integer lslAESXTimes(integer x) {
integer lslAESXTimes(integer x) {
     return ((x << 1) ^ (((x >> 7) & 1) * 0x1b)) & 0xFF;
     return ((x << 1) ^ (((x >> 7) & 1) * 0x1b)) & 0xFF;
}
}
 
// Used when column mixing
// Used when column mixing
integer lslAESMultiply(integer x, integer y) {
integer lslAESMultiply(integer x, integer y) {
Line 602: Line 654:
     integer xT2 = lslAESXTimes(xT);
     integer xT2 = lslAESXTimes(xT);
     integer xT3 = lslAESXTimes(xT2);
     integer xT3 = lslAESXTimes(xT2);
   
     return (((y & 1) * x) ^ (((y >> 1) & 1) * xT) ^  
     return (((y & 1) * x) ^ (((y >> 1) & 1) * xT) ^  
             (((y >> 2) & 1) * xT2) ^ (((y >> 3) & 1) * xT3) ^  
             (((y >> 2) & 1) * xT2) ^ (((y >> 3) & 1) * xT3) ^  
             (((y >> 4) & 1) * lslAESXTimes(xT3))) & 0xFF;
             (((y >> 4) & 1) * lslAESXTimes(xT3))) & 0xFF;
}
}
 
// Try to understand this at your own peril!
// Try to understand this at your own peril!
lslAESInvertMixColumns() {
lslAESInvertMixColumns() {
Line 614: Line 666:
     integer c = lslAESStateX2Y0;
     integer c = lslAESStateX2Y0;
     integer d = lslAESStateX3Y0;
     integer d = lslAESStateX3Y0;
   
     lslAESStateX0Y0 = lslAESMultiply(a, 0x0e) ^ lslAESMultiply(b, 0x0b) ^  
     lslAESStateX0Y0 = lslAESMultiply(a, 0x0e) ^ lslAESMultiply(b, 0x0b) ^  
         lslAESMultiply(c, 0x0d) ^ lslAESMultiply(d, 0x09);
         lslAESMultiply(c, 0x0d) ^ lslAESMultiply(d, 0x09);
Line 623: Line 675:
     lslAESStateX3Y0 = lslAESMultiply(a, 0x0b) ^ lslAESMultiply(b, 0x0d) ^  
     lslAESStateX3Y0 = lslAESMultiply(a, 0x0b) ^ lslAESMultiply(b, 0x0d) ^  
         lslAESMultiply(c, 0x09) ^ lslAESMultiply(d, 0x0e);
         lslAESMultiply(c, 0x09) ^ lslAESMultiply(d, 0x0e);
       
     a = lslAESStateX0Y1;
     a = lslAESStateX0Y1;
     b = lslAESStateX1Y1;
     b = lslAESStateX1Y1;
     c = lslAESStateX2Y1;
     c = lslAESStateX2Y1;
     d = lslAESStateX3Y1;
     d = lslAESStateX3Y1;
   
     lslAESStateX0Y1 = lslAESMultiply(a, 0x0e) ^ lslAESMultiply(b, 0x0b) ^  
     lslAESStateX0Y1 = lslAESMultiply(a, 0x0e) ^ lslAESMultiply(b, 0x0b) ^  
         lslAESMultiply(c, 0x0d) ^ lslAESMultiply(d, 0x09);
         lslAESMultiply(c, 0x0d) ^ lslAESMultiply(d, 0x09);
Line 637: Line 689:
     lslAESStateX3Y1 = lslAESMultiply(a, 0x0b) ^ lslAESMultiply(b, 0x0d) ^  
     lslAESStateX3Y1 = lslAESMultiply(a, 0x0b) ^ lslAESMultiply(b, 0x0d) ^  
         lslAESMultiply(c, 0x09) ^ lslAESMultiply(d, 0x0e);
         lslAESMultiply(c, 0x09) ^ lslAESMultiply(d, 0x0e);
       
     a = lslAESStateX0Y2;
     a = lslAESStateX0Y2;
     b = lslAESStateX1Y2;
     b = lslAESStateX1Y2;
     c = lslAESStateX2Y2;
     c = lslAESStateX2Y2;
     d = lslAESStateX3Y2;
     d = lslAESStateX3Y2;
   
     lslAESStateX0Y2 = lslAESMultiply(a, 0x0e) ^ lslAESMultiply(b, 0x0b) ^  
     lslAESStateX0Y2 = lslAESMultiply(a, 0x0e) ^ lslAESMultiply(b, 0x0b) ^  
         lslAESMultiply(c, 0x0d) ^ lslAESMultiply(d, 0x09);
         lslAESMultiply(c, 0x0d) ^ lslAESMultiply(d, 0x09);
Line 651: Line 703:
     lslAESStateX3Y2 = lslAESMultiply(a, 0x0b) ^ lslAESMultiply(b, 0x0d) ^  
     lslAESStateX3Y2 = lslAESMultiply(a, 0x0b) ^ lslAESMultiply(b, 0x0d) ^  
         lslAESMultiply(c, 0x09) ^ lslAESMultiply(d, 0x0e);
         lslAESMultiply(c, 0x09) ^ lslAESMultiply(d, 0x0e);
       
     a = lslAESStateX0Y3;
     a = lslAESStateX0Y3;
     b = lslAESStateX1Y3;
     b = lslAESStateX1Y3;
     c = lslAESStateX2Y3;
     c = lslAESStateX2Y3;
     d = lslAESStateX3Y3;
     d = lslAESStateX3Y3;
   
     lslAESStateX0Y3 = lslAESMultiply(a, 0x0e) ^ lslAESMultiply(b, 0x0b) ^  
     lslAESStateX0Y3 = lslAESMultiply(a, 0x0e) ^ lslAESMultiply(b, 0x0b) ^  
         lslAESMultiply(c, 0x0d) ^ lslAESMultiply(d, 0x09);
         lslAESMultiply(c, 0x0d) ^ lslAESMultiply(d, 0x09);
Line 666: Line 718:
         lslAESMultiply(c, 0x09) ^ lslAESMultiply(d, 0x0e);
         lslAESMultiply(c, 0x09) ^ lslAESMultiply(d, 0x0e);
}
}
 
//##########################################################################//
//##########################################################################//
//                        ENCRYPTION SET-UP FUNCTIONS                      //
//                        ENCRYPTION SET-UP FUNCTIONS                      //
Line 676: Line 728:
// decrypt using it.
// decrypt using it.
string lslAESKeyExpansion(list keyBytes) {
string lslAESKeyExpansion(list keyBytes) {
    // Don't need the bit-count and the first round key is the key itself
    // Don't need the bit-count and the first round key is the key itself
     integer len = (
     integer len = (
         (
         (
Line 685: Line 737:
             )
             )
         ) != []); // Get the length
         ) != []); // Get the length
 
       
    // Calculate padding-size
    lslAESPadSize = len << 5;
    integer i = lslAESPadSize % 128;
    if (i) lslAESPadSize += 128 - i;
     // Check that we are within reasonable limits
     // Check that we are within reasonable limits
     if ((len < 4) || (len > 8)) {
     if ((len < 4) || (len > 8)) {
Line 694: Line 751:
     // Calculate the number of required rounds
     // Calculate the number of required rounds
     lslAESRounds = len + 6;
     lslAESRounds = len + 6;
   
     // All others are found from previous keys
     // All others are found from previous keys
     integer i = 0;
     i = 0;
     integer x = (len * 3) + 28;
     integer x = (len * 3) + 28;
   
   
     integer t = llList2Integer(lslAESRoundKey, -1);
     integer t = llList2Integer(lslAESRoundKey, -1);
   
     do {
     do {
         if (!(i % len)) {
         if (!(i % len)) {
             // Rotate by 1, SubWord and Nudge [0x01020408, 0x01020408, 0x1b366cd8]
             // Rotate by 1, SubWord and Nudge  
            // [0x01020408, 0x01020408, 0x1b366cd8]
             t = ((lslAESGetSBoxByte((t >> 16) & 0xFF) ^  
             t = ((lslAESGetSBoxByte((t >> 16) & 0xFF) ^  
                     (0x000D8080 >> (7 ^ (i / len)))) << 24) |  
                     (0x000D8080 >> (7 ^ (i / len)))) << 24) |  
Line 716: Line 774:
                 (lslAESGetSBoxByte((t      ) & 0xFF)      );
                 (lslAESGetSBoxByte((t      ) & 0xFF)      );
         }
         }
       
         // XOR k with four previous RoundKey values And add the new entries, yay!
         // XOR k with four previous RoundKey values And add the  
        // new entries, yay!
         lslAESRoundKey = (lslAESRoundKey = []) + lslAESRoundKey +  
         lslAESRoundKey = (lslAESRoundKey = []) + lslAESRoundKey +  
             (t = (t ^ llList2Integer(lslAESRoundKey, i)));
             (t = (t ^ llList2Integer(lslAESRoundKey, i)));
Line 725: Line 784:
     return "";
     return "";
}
}
 
//##########################################################################//
//##########################################################################//
//                        SERIALISATION FUNCTIONS                          //
//                        SERIALISATION FUNCTIONS                          //
Line 747: Line 806:
     );
     );
}
}
 
// Load value as integers from a hex string
// Load value as integers from a hex string
list lslAESHexToBytes(string hexData) {
list lslAESHexToBytes(string hexData) {
Line 758: Line 817:
     );
     );
}
}
 
// Loads a string as a list of integers, using the given bit <width> and  
// Loads a string as a list of integers, using the given bit <width> and  
// appropriate <alphabet> into Key
// appropriate <alphabet> into Key
list lslAESStringToBytes(string s, integer width, string alphabet) {
list lslAESStringToBytes(string s, integer width, string alphabet) {
     integer l = llStringLength(s);
     integer l = llStringLength(s);
   
     list n = [l * width]; // Add bit-length
     list n = [l * width]; // Add bit-length
     integer bitbuf = 0;
     integer bitbuf = 0;
     integer adjust = 32;
     integer adjust = 32;
   
     integer i = 0;
     integer i = 0;
     integer val;
     integer val;
Line 777: Line 836:
                 ["Invalid character at index "+(string)i];
                 ["Invalid character at index "+(string)i];
         }
         }
 
         if ((adjust -= width) <= 0) {
         if ((adjust -= width) <= 0) {
             bitbuf = bitbuf | (val >> -adjust);
             bitbuf = bitbuf | (val >> -adjust);
             n = (n = []) + n + [bitbuf];
             n = (n = []) + n + [bitbuf];
           
             adjust += 32;
             adjust += 32;
             if (adjust < 32) bitbuf = (val << adjust);
             if (adjust < 32) bitbuf = (val << adjust);
             else bitbuf = 0;
             else bitbuf = 0;
         } else bitbuf = bitbuf | (val << adjust);
         } else bitbuf = bitbuf | (val << adjust);
 
         ++i;
         ++i;
     }
     }
   
     s = "";
     s = "";
     if (adjust < 32)  
     if (adjust < 32)  
Line 795: Line 854:
     return (n = []) + n;
     return (n = []) + n;
}
}
 
// Returns a hex string representing integers b, preceeded with "0x".
// Returns a hex string representing integers b, preceeded with "0x".
string lslAESBytesToHex(list b) {
string lslAESBytesToHex(list b) {
     return "0x" + lslAESBytesToString((b = []) + b, 4, LSLAES_HEX_CHARS);
     return "0x" + lslAESBytesToString((b = []) + b, 4, LSLAES_HEX_CHARS);
}
}
 
// Returns a base64 string representing integers b, with padding equals signs
// Returns a base64 string representing integers b, with padding equals signs
string lslAESBytesToBase64(list b) {
string lslAESBytesToBase64(list b) {
Line 811: Line 870:
     return (s = "") + s;
     return (s = "") + s;
}
}
 
// Outputs integer data from b as characters representing width bits taken from  
// Outputs integer data from b as characters representing width bits taken from  
// alphabet
// alphabet
string lslAESBytesToString(list b, integer width, string alphabet) {
string lslAESBytesToString(list b, integer width, string alphabet) {
     integer bits = llList2Integer(b, 0);
     integer bits = llList2Integer(b, 0);
   
     integer i = 0;
     integer i = 0;
     integer mask = ~(-1 << width);
     integer mask = ~(-1 << width);
     integer shift = 32 - width;
     integer shift = 32 - width;
   
     integer available = 0;
     integer available = 0;
     integer prev = 0;
     integer prev = 0;
Line 826: Line 885:
     integer extra;
     integer extra;
     integer value;
     integer value;
   
     string s = "";
     string s = "";
   
     @lslAESBytesToStringLoop;
     @lslAESBytesToStringLoop;
     if((bits -= 32) > -32) {
     if((bits -= 32) > -32) {
Line 881: Line 940:
}
}


default {
default {  
     link_message(integer x, integer y, string msg, key id) {         
     link_message(integer x, integer y, string msg, key id) {         
         // Is the message in our range?
         // Is the message in our range?
Line 887: Line 946:
             (y < (LSLAES_COMMS_BASE + LSLAES_COMMS_WIDTH))) {
             (y < (LSLAES_COMMS_BASE + LSLAES_COMMS_WIDTH))) {
             y -= LSLAES_COMMS_BASE;
             y -= LSLAES_COMMS_BASE;
           
             // Is input too large?
             // Is input too large?
             if (llStringLength(msg) > LSLAES_MAX_SIZE) {
             if (llStringLength(msg) > LSLAES_MAX_SIZE) {
Line 898: Line 957:
                 return;
                 return;
             }
             }
           
             // Load the data
             // Load the data
             list l = [];
             list l = [];
Line 904: Line 963:
                 l = lslAESBase64ToBytes((msg = "") + msg);
                 l = lslAESBase64ToBytes((msg = "") + msg);
             else l = lslAESHexToBytes((msg = "") + msg);
             else l = lslAESHexToBytes((msg = "") + msg);
           
             // Was the value parsed successfully?
             // Was the value parsed successfully?
             if (llGetListEntryType(l, 0) != TYPE_INTEGER) {
             if (llGetListEntryType(l, 0) != TYPE_INTEGER) {
Line 915: Line 974:
                 return;
                 return;
             }
             }
           
             if (y & LSLAES_COMMS_PRIME) {
             if (y & LSLAES_COMMS_PRIME) {
                 msg = lslAESKeyExpansion((l = []) + l);
                 msg = lslAESKeyExpansion((l = []) + l);
                 if (msg == "") y += LSLAES_COMMS_REPLY_BASE;
                 if (msg == "") y += LSLAES_COMMS_REPLY_BASE;
                 else y = LSLAES_COMMS_REPLY_BASE;
                 else y = LSLAES_COMMS_REPLY_BASE;
               
                 llMessageLinked(x, y, (msg = "") + msg, id);
                 llMessageLinked(x, y, (msg = "") + msg, id);
             } else {
             } else {
Line 927: Line 986:
                 else if (y & LSLAES_COMMS_DECRYPT)  
                 else if (y & LSLAES_COMMS_DECRYPT)  
                     l = lslAESInvertCipher((l = []) + l);
                     l = lslAESInvertCipher((l = []) + l);
                else if (y & LSLAES_COMMS_HASH)
                    l = lslAESHash((l = []) + l, LSLAES_HASH_SIZE);
                 else return;
                 else return;
               
                 y += LSLAES_COMMS_REPLY_BASE;
                 y += LSLAES_COMMS_REPLY_BASE;
               
                 if (y & LSLAES_COMMS_BASE64)  
                 if (y & LSLAES_COMMS_BASE64)  
                     msg = lslAESBytesToBase64((l = []) + l);
                     msg = lslAESBytesToBase64((l = []) + l);
                 else msg = lslAESBytesToHex((l = []) + l);
                 else msg = lslAESBytesToHex((l = []) + l);
               
                 llMessageLinked(x, y, (msg = "") + msg, id);
                 llMessageLinked(x, y, (msg = "") + msg, id);
             }
             }
         }
         }
     }
     }
}</lsl>
}


=== Example use-cases ===
=== Example use-cases ===

Revision as of 16:03, 31 August 2008

BETA!

The code/project on this page is currently considered to be in beta, and should not be used in a production environment until proper testing has been completed

AES Encryption

The AES encryption standard is a cryptographically secure and widely supported symmetric key, stream encryption scheme. It takes a strong key and applies it to an input in a series of "rounds" to a 16-byte chunk of data (the state). The resulting cipher is so thoroughly different from the original that it is considered cryptographically secure. Indeed, there are as yet no reported cases of AES encryption being successfully broken except through side channel attacks, essentially using other available data or means to get at the original (not a fault of the algorithm itself).

For more information regarding the algorithm you may view the Wikipedia page.

Implementation

Usage

The usual restrictions on open-source software apply to the following code; namely there are very few! You may use the provided code in any project you wish so long as you give credit to the script author (Haravikk Mistral) for providing this code as a base, you are also asked to please provide a link to this wiki article.

Description

This implementation is a fairly basic one, it utilises a 16-byte wide state for effective encryption for 128, 192, and 256-bit keys only. This script accepts messages larger than 16-bytes long, and applies a segment number to them to reduce the chances of repeated segments producing the same cipher block. Data is zero-padded to ensure that it can be divided into 16-byte segments, with the last byte of the (unencrypted) data providing the number of padding bits added to the data.

This script provides methods for serialising/unserialising byte-data to and from base64 and hexadecimal alphabets. Support for additional alphabets (e.g a base 256 one) is easily added as the functions (lslAESBytesToString() and lslAESStringToBytes()) are designed to be flexible.

The implementation is based on the freely available open-source C/C++ implementation at Hoozi. The implementation therefore is mostly a straight port of the AES algorithm itself, with the aforementioned adjustments to make it more secure.

Future Development

Future development will be mostly performance-centric, aiming to further increase speed and reduce the memory footprint (allowing support for larger messages). This will be ongoing. Investigations will also be made into improving compatibility with widely available AES implementations in use.

Originally planned was to modify the algorithm for use for the WHIRLPOOL hashing function, however this has higher memory requirements (precomputed data is approximately 64 times larger!). However, a hashing function using the already implemented algorithm may be adapted easily.

An exact port to PHP is also planned, for full compatibility with the LSL version.

Thanks

Thanks to Strife Onizuka for work in optimising/testing the initial implementation.

Mono-LSL Code

The following code is provided as an encryption "client". That is, you can simply send it link-messages in order to encrypt your messages. You must first of all "prime" it with a key to use for encryption/decryption, this is very fast so you may swap keys in and out as required without much penalty. Once the engine is "primed" you may encrypt/decrypt as many messages as you wish, up to a size of 1.5kb, larger messages will be refused as they risk a stack/heap collision.

Link messages are identified by the integer argument by adding a command to the base constant. Here is a summary of the messages values, and the replies you will get:

Commands

Command Code Type Description
Prime 33792 Hex Primes engine with a key transmitted as a hexadecimal string. Invalid size will return an error.
33793 Base64 Primes engine with a key transmitted as a base64 string (note that base64 uses blocks of width 6-bits, only use for keys that are a factor of six (i.e - 192 bits). Invalid size will return an error.
Encrypt 36864 Hex Encrypts a hexadecimal string, returning the cipher-text as a padded hexadecimal string
36865 Base64 Encrypts a base64 strings, returning the cipher-text as a padded base64 string
Decrypt 49152 Hex Decrypts a hexadecimal string, removing the padding added by encryption and returning the decrypted hexadecimal data
49153 Base64 Decrypts a base64 string, removing the padding added by encryption and returning the decrypted base64 data

Replies

Reply Code Type Description
ERROR 65536 string An error occurred, described by the string-data of the reply
Prime 66560 void Indicates the AES engine was primed successfully using the hex key it was given
65561 void Indicates the AES engine was primed successfully using the base64 key it was given
Encrypt 69632 Hex Contains a hexadecimal representation of the encrypted data
69633 Base64 Contains a base64 representation of the encrypted data
Decrypt 81920 Hex Contains a hexadecimal representation of the decrypted data
81921 Base64 Contains a base64 representation of the decrypted data

integer LSLAES_COMMS_BASE = 32768; integer LSLAES_COMMS_REPLY_BASE = 65536;

integer LSLAES_COMMS_BASE64 = 1; integer LSLAES_COMMS_HASH = 256; integer LSLAES_COMMS_PRIME = 1024; integer LSLAES_COMMS_ENCRYPT = 4096; integer LSLAES_COMMS_DECRYPT = 16384;

integer LSLAES_HASH_SIZE = 512;

// Defines how "wide" the range of comms values are integer LSLAES_COMMS_WIDTH = 32767;

// Refuse any data longer than this many characters integer LSLAES_MAX_SIZE = 1536;

string LSLAES_HEX_CHARS = "0123456789ABCDEF"; string LSLAES_BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

// The inverted Rijndael SBox, values in the state are substituted with these list LSLAES_SBOX_INVERTED = [

   0x52096ad5, 0x3036a538, 0xbf40a39e, 0x81f3d7fb, 0x7ce33982, 
   0x9b2fff87, 0x348e4344, 0xc4dee9cb, 0x547b9432, 0xa6c2233d, 
   0xee4c950b, 0x42fac34e, 0x082ea166, 0x28d924b2, 0x765ba249, 
   0x6d8bd125, 0x72f8f664, 0x86689816, 0xd4a45ccc, 0x5d65b692, 
   0x6c704850, 0xfdedb9da, 0x5e154657, 0xa78d9d84, 0x90d8ab00, 
   0x8cbcd30a, 0xf7e45805, 0xb8b34506, 0xd02c1e8f, 0xca3f0f02, 
   0xc1afbd03, 0x01138a6b, 0x3a911141, 0x4f67dcea, 0x97f2cfce, 
   0xf0b4e673, 0x96ac7422, 0xe7ad3585, 0xe2f937e8, 0x1c75df6e, 
   0x47f11a71, 0x1d29c589, 0x6fb7620e, 0xaa18be1b, 0xfc563e4b, 
   0xc6d27920, 0x9adbc0fe, 0x78cd5af4, 0x1fdda833, 0x8807c731, 
   0xb1121059, 0x2780ec5f, 0x60517fa9, 0x19b54a0d, 0x2de57a9f, 
   0x93c99cef, 0xa0e03b4d, 0xae2af5b0, 0xc8ebbb3c, 0x83539961, 
   0x172b047e, 0xba77d626, 0xe1691463, 0x55210c7d

];

integer lslAESSBoxInvertedCached = -1; integer lslAESSBoxInvertedCache = 0;

// Treats the above list as a byte-array, retrieving the desired byte value. integer lslAESGetSBoxInvertedByte(integer n) {

   integer b = n % 4;  // Byte within integer
           n /= 4;     // Integer
   b = 3 - b;

   if (lslAESSBoxInvertedCached == n) n = lslAESSBoxInvertedCache;
   else {
       lslAESSBoxInvertedCached = n;
       lslAESSBoxInvertedCache = llList2Integer(LSLAES_SBOX_INVERTED, n);
   }
   return (lslAESSBoxInvertedCache >> (b << 3)) & 0xFF;

}

// The Rijndael SBox, values in the state are substituted with these list LSLAES_SBOX = [

   0x637c777b, 0xf26b6fc5, 0x3001672b, 0xfed7ab76, 0xca82c97d, 
   0xfa5947f0, 0xadd4a2af, 0x9ca472c0, 0xb7fd9326, 0x363ff7cc, 
   0x34a5e5f1, 0x71d83115, 0x04c723c3, 0x1896059a, 0x071280e2,
   0xeb27b275, 0x09832c1a, 0x1b6e5aa0, 0x523bd6b3, 0x29e32f84, 
   0x53d100ed, 0x20fcb15b, 0x6acbbe39, 0x4a4c58cf, 0xd0efaafb, 
   0x434d3385, 0x45f9027f, 0x503c9fa8, 0x51a3408f, 0x929d38f5, 
   0xbcb6da21, 0x10fff3d2, 0xcd0c13ec, 0x5f974417, 0xc4a77e3d, 
   0x645d1973, 0x60814fdc, 0x222a9088, 0x46eeb814, 0xde5e0bdb, 
   0xe0323a0a, 0x4906245c, 0xc2d3ac62, 0x9195e479, 0xe7c8376d, 
   0x8dd54ea9, 0x6c56f4ea, 0x657aae08, 0xba78252e, 0x1ca6b4c6, 
   0xe8dd741f, 0x4bbd8b8a, 0x703eb566, 0x4803f60e, 0x613557b9, 
   0x86c11d9e, 0xe1f89811, 0x69d98e94, 0x9b1e87e9, 0xce5528df, 
   0x8ca1890d, 0xbfe64268, 0x41992d0f, 0xb054bb16

];

integer lslAESSBoxCached = -1; integer lslAESSBoxCache = 0;

// Treats the above list as a byte-array, retrieving the desired byte value. integer lslAESGetSBoxByte(integer n) {

   integer b = n % 4;  // Byte within integer
           n /= 4;     // Integer
   b = 3 - b;

   if (lslAESSBoxCached == n) n = lslAESSBoxCache;
   else {
       lslAESSBoxCached = n;
       lslAESSBoxCache = llList2Integer(LSLAES_SBOX, n);
   }
   return (lslAESSBoxCache >> (b << 3)) & 0xFF;

}

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

// 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;

//##########################################################################// // HIGH-LEVEL FUNCTIONS // //##########################################################################// // The following functions are the ones to call to encrypt/decrypt // //##########################################################################// // Calculate a length-bit hash using the currently expanded key. Length MUST // be a multiple of 32. list lslAESHash(list data, integer length) {

   // First pad data to a length * 2
   integer l = llList2Integer(data, 0);
   integer i = 0;
   integer j = length << 1;
   
   // If too short, then add values
   if (l < j) {
       integer a = (j - l) / 32;
       do data = (data = []) + data + [llList2Integer(data, i)];
       while ((++i) < a);
       l = j;
   }
   
   // Irreversibly reduce to length by XORing
   j = l / length;
   integer w = j - 1;
   l = data != [];
   list t = [length];
   integer b = 0;
   i = 1;
   do {
       b = b ^ llList2Integer(data, i);
       if ((i % j) == w) {
           t = (t = []) + t + [b];
           b = 0;
       }
   } while ((++i) < l);
   data = (data = t = []) + t;
   
   // Add any extra data
   if ((data != []) < (length / 32)) 
       data = (data = []) + data + [b];
   
   // Calculate hash    
   data = lslAESCipher((data = []) + data);
   
   // Reduce to size
   l = (data != []) - (length / 32);
   return [length] + llList2List((data = []) + data, 1, -l);

}

// Decrypts a list of integers into a list of decrypted integers. Padding is // automatically removed as-per the method used by lslAESCipher. list lslAESInvertCipher(list data) {

   // Preserve the bit-count header for output
   integer bits = llList2Integer(data, 0);
   data = llList2List((data = []) + data, 1, -1);

   integer i = 0; integer j = 0;
   integer l = (data != []);
   list output = [];
   while (l > 0) {
       // Copy some input into state variables
       j = llList2Integer(data, 0);
       lslAESStateX0Y0 = ((j >> 24) & 0xFF);
       lslAESStateX0Y1 = ((j >> 16) & 0xFF);
       lslAESStateX0Y2 = ((j >> 8)  & 0xFF);
       lslAESStateX0Y3 = (j         & 0xFF);
       j = llList2Integer(data, 1);
       lslAESStateX1Y0 = ((j >> 24) & 0xFF);
       lslAESStateX1Y1 = ((j >> 16) & 0xFF);
       lslAESStateX1Y2 = ((j >> 8)  & 0xFF);
       lslAESStateX1Y3 = (j         & 0xFF);
       j = llList2Integer(data, 2);
       lslAESStateX2Y0 = ((j >> 24) & 0xFF);
       lslAESStateX2Y1 = ((j >> 16) & 0xFF);
       lslAESStateX2Y2 = ((j >> 8)  & 0xFF);
       lslAESStateX2Y3 = (j         & 0xFF);
       j = llList2Integer(data, 3);
       lslAESStateX3Y0 = ((j >> 24) & 0xFF);
       lslAESStateX3Y1 = ((j >> 16) & 0xFF);
       lslAESStateX3Y2 = ((j >> 8)  & 0xFF);
       lslAESStateX3Y3 = (j         & 0xFF);

       // Add last round key before rounds begin
       lslAESAddRoundKey(lslAESRounds);

       // There will be Rounds - 1 identical rounds
       j = lslAESRounds - 1;
       while (j > 0) {
           lslAESInvertShiftRows();
           lslAESInvertSubBytes();
           lslAESAddRoundKey(j--);
           lslAESInvertMixColumns();
       }

       // The last round requires no mixing
       lslAESInvertShiftRows();
       lslAESInvertSubBytes();
       lslAESAddRoundKey(0);

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

       // XOR with i
       output = (output = []) + output + [
           ((lslAESStateX0Y0 ^ i) << 24) | ((lslAESStateX0Y1 ^ i) << 16) | 
               ((lslAESStateX0Y2 ^ i) << 8) | (lslAESStateX0Y3 ^ i),
           ((lslAESStateX1Y0 ^ i) << 24) | ((lslAESStateX1Y1 ^ i) << 16) | 
               ((lslAESStateX1Y2 ^ i) << 8) | (lslAESStateX1Y3 ^ i),
           ((lslAESStateX2Y0 ^ i) << 24) | ((lslAESStateX2Y1 ^ i) << 16) | 
               ((lslAESStateX2Y2 ^ i) << 8) | (lslAESStateX2Y3 ^ i),
           ((lslAESStateX3Y0 ^ i) << 24) | ((lslAESStateX3Y1 ^ i) << 16) | 
               ((lslAESStateX3Y2 ^ i) << 8) | (lslAESStateX3Y3 ^ i)
       ];
       i = (++i % 256);
   }

   // Get bit length
   bits = j = llList2Integer(output, 0);
   if ((j < 0) || (j > 8192)) return (output = []) + [0];
   
   // Discard padding and return
   j /= 32;
   if (bits % 32) ++j;
   
   return llList2List((output = []) + output, 0, j);

}

// Encrypts a list of integers into a list of encrypted integers. // Input is padded to be a multiple of lslAESPadSize (key-length rounded // up to nearest 128 multiple) using zero-bytes, with the first 32-bit word // of ciphertext indicating how many bits long the data is. If the input is // already a multiple then a full lslAESPadSize bits are added. // In addition, blocks are XORed with a counter to reduce patterns, this // counter is numbered such that all the bytes of the first block are // XORed with 0, the bytes of the second block are XORed with 1, etc., the // counter resets to zero after 255 list lslAESCipher(list data) {

   // Preserve the bit-count header for output
   integer bits = llList2Integer(data, 0);
   data = llDeleteSubList((data = []) + data, 0, 0);

   // Add padding integers
   integer l = data != [];
   integer j = lslAESPadSize - (bits % lslAESPadSize);

   integer t = j / 32;
   while ((--t) >= 1) data = (data = []) + data + [0];
   data = (data = []) + [bits] + data;

   l = (data != []);
   list output = [l << 5]; // Total bits

   integer i = 0;
   while (l > 0) {
       // Copy some input into state variables, and XOR with i
       j = llList2Integer(data, 0);
       lslAESStateX0Y0 = ((j >> 24) & 0xFF) ^ i;
       lslAESStateX0Y1 = ((j >> 16) & 0xFF) ^ i;
       lslAESStateX0Y2 = ((j >> 8)  & 0xFF) ^ i;
       lslAESStateX0Y3 = (j         & 0xFF) ^ i;
       j = llList2Integer(data, 1);
       lslAESStateX1Y0 = ((j >> 24) & 0xFF) ^ i;
       lslAESStateX1Y1 = ((j >> 16) & 0xFF) ^ i;
       lslAESStateX1Y2 = ((j >> 8)  & 0xFF) ^ i;
       lslAESStateX1Y3 = (j         & 0xFF) ^ i;
       j = llList2Integer(data, 2);
       lslAESStateX2Y0 = ((j >> 24) & 0xFF) ^ i;
       lslAESStateX2Y1 = ((j >> 16) & 0xFF) ^ i;
       lslAESStateX2Y2 = ((j >> 8)  & 0xFF) ^ i;
       lslAESStateX2Y3 = (j         & 0xFF) ^ i;
       j = llList2Integer(data, 3);
       lslAESStateX3Y0 = ((j >> 24) & 0xFF) ^ i;
       lslAESStateX3Y1 = ((j >> 16) & 0xFF) ^ i;
       lslAESStateX3Y2 = ((j >> 8)  & 0xFF) ^ i;
       lslAESStateX3Y3 = (j         & 0xFF) ^ i;

       // Add first round-key before starting rounds
       lslAESAddRoundKey(0);

       // There will be Rounds iterations, the first 
       // Rounds - 1 are identical.
       j = 1;
       while (j < lslAESRounds) {
           lslAESSubBytes();
           lslAESShiftRows();
           lslAESMixColumns();
           lslAESAddRoundKey(j++);
       }

       // The last column performs no mix
       lslAESSubBytes();
       lslAESShiftRows();
       lslAESAddRoundKey(lslAESRounds);

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

       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
       ];
       i = (++i % 256);
   }

   return (output = []) + output;

}


//##########################################################################// // PROCESSING FUNCTIONS // //##########################################################################// // The following functions are used to process data that is being // // encrypted or decrypted. // //##########################################################################// // XORs the value with RoundKey values 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 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 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 lslAESShiftRows() {

   integer t = 0;

   // Rotate first row 1 columns to left
   t = lslAESStateX1Y0;
   lslAESStateX1Y0 = lslAESStateX1Y1;
   lslAESStateX1Y1 = lslAESStateX1Y2;
   lslAESStateX1Y2 = lslAESStateX1Y3;
   lslAESStateX1Y3 = t;

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

   t = lslAESStateX2Y1;
   lslAESStateX2Y1 = lslAESStateX2Y3;
   lslAESStateX2Y3 = t;

   // Rotate third row 3 columns to left
   t = lslAESStateX3Y0;
   lslAESStateX3Y0 = lslAESStateX3Y3;
   lslAESStateX3Y3 = lslAESStateX3Y2;
   lslAESStateX3Y2 = lslAESStateX3Y1;
   lslAESStateX3Y1 = t;

}

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

   integer t = 0;

   // Rotate first row 1 columns to right
   t = lslAESStateX1Y3;
   lslAESStateX1Y3 = lslAESStateX1Y2;
   lslAESStateX1Y2 = lslAESStateX1Y1;
   lslAESStateX1Y1 = lslAESStateX1Y0;
   lslAESStateX1Y0 = t;

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

   t = lslAESStateX2Y1;
   lslAESStateX2Y1 = lslAESStateX2Y3;
   lslAESStateX2Y3 = t;

   // Rotate third row 3 columns to right
   t = lslAESStateX3Y0;
   lslAESStateX3Y0 = lslAESStateX3Y1;
   lslAESStateX3Y1 = lslAESStateX3Y2;
   lslAESStateX3Y2 = lslAESStateX3Y3;
   lslAESStateX3Y3 = t;

}

// Mixes columns of the state lslAESMixColumns() {

   integer t = lslAESStateX0Y0;
   integer t1 = lslAESStateX0Y0 ^ lslAESStateX1Y0 ^ 
       lslAESStateX2Y0 ^ lslAESStateX3Y0;

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

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

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

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

   t = lslAESStateX0Y1;
   t1 = lslAESStateX0Y1 ^ lslAESStateX1Y1 ^ lslAESStateX2Y1 ^ lslAESStateX3Y1;

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

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

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

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

   t = lslAESStateX0Y2;
   t1 = lslAESStateX0Y2 ^ lslAESStateX1Y2 ^ lslAESStateX2Y2 ^ lslAESStateX3Y2;

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

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

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

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

   t = lslAESStateX0Y3;
   t1 = lslAESStateX0Y3 ^ lslAESStateX1Y3 ^ lslAESStateX2Y3 ^ lslAESStateX3Y3;

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

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

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

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

}

// Used when column mixing 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! lslAESInvertMixColumns() {

   integer a = lslAESStateX0Y0;
   integer b = lslAESStateX1Y0;
   integer c = lslAESStateX2Y0;
   integer d = lslAESStateX3Y0;

   lslAESStateX0Y0 = lslAESMultiply(a, 0x0e) ^ lslAESMultiply(b, 0x0b) ^ 
       lslAESMultiply(c, 0x0d) ^ lslAESMultiply(d, 0x09);
   lslAESStateX1Y0 = lslAESMultiply(a, 0x09) ^ lslAESMultiply(b, 0x0e) ^ 
       lslAESMultiply(c, 0x0b) ^ lslAESMultiply(d, 0x0d);
   lslAESStateX2Y0 = lslAESMultiply(a, 0x0d) ^ lslAESMultiply(b, 0x09) ^ 
       lslAESMultiply(c, 0x0e) ^ lslAESMultiply(d, 0x0b);
   lslAESStateX3Y0 = lslAESMultiply(a, 0x0b) ^ lslAESMultiply(b, 0x0d) ^ 
       lslAESMultiply(c, 0x09) ^ lslAESMultiply(d, 0x0e);

   a = lslAESStateX0Y1;
   b = lslAESStateX1Y1;
   c = lslAESStateX2Y1;
   d = lslAESStateX3Y1;

   lslAESStateX0Y1 = 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);
   lslAESStateX2Y1 = lslAESMultiply(a, 0x0d) ^ lslAESMultiply(b, 0x09) ^ 
       lslAESMultiply(c, 0x0e) ^ lslAESMultiply(d, 0x0b);
   lslAESStateX3Y1 = lslAESMultiply(a, 0x0b) ^ lslAESMultiply(b, 0x0d) ^ 
       lslAESMultiply(c, 0x09) ^ lslAESMultiply(d, 0x0e);

   a = lslAESStateX0Y2;
   b = lslAESStateX1Y2;
   c = lslAESStateX2Y2;
   d = lslAESStateX3Y2;

   lslAESStateX0Y2 = lslAESMultiply(a, 0x0e) ^ lslAESMultiply(b, 0x0b) ^ 
       lslAESMultiply(c, 0x0d) ^ lslAESMultiply(d, 0x09);
   lslAESStateX1Y2 = 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);
   lslAESStateX3Y2 = lslAESMultiply(a, 0x0b) ^ lslAESMultiply(b, 0x0d) ^ 
       lslAESMultiply(c, 0x09) ^ lslAESMultiply(d, 0x0e);

   a = lslAESStateX0Y3;
   b = lslAESStateX1Y3;
   c = lslAESStateX2Y3;
   d = lslAESStateX3Y3;

   lslAESStateX0Y3 = lslAESMultiply(a, 0x0e) ^ lslAESMultiply(b, 0x0b) ^ 
       lslAESMultiply(c, 0x0d) ^ lslAESMultiply(d, 0x09);
   lslAESStateX1Y3 = lslAESMultiply(a, 0x09) ^ lslAESMultiply(b, 0x0e) ^ 
       lslAESMultiply(c, 0x0b) ^ lslAESMultiply(d, 0x0d);
   lslAESStateX2Y3 = 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 // // using the value is Key. // //##########################################################################// // Takes the key bytes provided and sets up the engine ready to encrypt or // decrypt using it. string 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
       
   // Calculate padding-size
   lslAESPadSize = len << 5;
   integer i = lslAESPadSize % 128;
   if (i) lslAESPadSize += 128 - i;

   // 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
   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 "";

}

//##########################################################################// // 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. // //##########################################################################// // Load value as integers from a base64 string list lslAESBase64ToBytes(string base64Data) {

   integer x = llSubStringIndex(base64Data, "=");
   if (x > 0) 
       base64Data = llGetSubString(
           (base64Data = "") + base64Data, 
           0, 
           --x
       );
   return lslAESStringToBytes(
       (base64Data = "") + base64Data, 
       6, 
       LSLAES_BASE64_CHARS
   );

}

// Load value as integers from a hex string list lslAESHexToBytes(string hexData) {

   if (llGetSubString(hexData, 0, 1) == "0x") 
       hexData = llGetSubString((hexData = "") + hexData, 2, -1);
   return lslAESStringToBytes(
       (hexData = "") + hexData, 
       4, 
       LSLAES_HEX_CHARS
   );

}

// Loads a string as a list of integers, using the given bit <width> and // appropriate <alphabet> into Key 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 = (n = []) + 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;

}

// Returns a hex string representing integers b, preceeded with "0x". string lslAESBytesToHex(list b) {

   return "0x" + lslAESBytesToString((b = []) + b, 4, LSLAES_HEX_CHARS);

}

// Returns a base64 string representing integers b, with padding equals signs string lslAESBytesToBase64(list b) {

   string s = lslAESBytesToString((b = []) + b, 6, LSLAES_BASE64_CHARS);
   integer l = llStringLength(s) % 3;
   if (l) {
       if (l == 1) return (s = "") + s + "==";
       return (s = "") + s + "=";
   }
   return (s = "") + s;

}

// Outputs integer data from b as characters representing width bits taken from // alphabet 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 = (s = "") + s + 
                   llGetSubString(
                       alphabet, 
                       value = (
                           extra | 
                           (
                               (buf >> (shift + prev)) & 
                               ~(-1 << (width - prev))
                           )
                       ), 
                       value
                   );
               buf = buf << (width - prev);
               available -= width;
           }
           while(available >= width) {
               s = (s = "") + 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;

}

default {

   link_message(integer x, integer y, string msg, key id) {        
       // Is the message in our range?
       if ((y >= LSLAES_COMMS_BASE) && 
           (y < (LSLAES_COMMS_BASE + LSLAES_COMMS_WIDTH))) {
           y -= LSLAES_COMMS_BASE;

           // Is input too large?
           if (llStringLength(msg) > LSLAES_MAX_SIZE) {
               llMessageLinked(
                   x, 
                   LSLAES_COMMS_REPLY_BASE, 
                   (msg = "") + "Input too large!", 
                   id
               );
               return;
           }

           // Load the data
           list l = [];
           if (y & LSLAES_COMMS_BASE64) 
               l = lslAESBase64ToBytes((msg = "") + msg);
           else l = lslAESHexToBytes((msg = "") + msg);

           // Was the value parsed successfully?
           if (llGetListEntryType(l, 0) != TYPE_INTEGER) {
               llMessageLinked(
                   x,
                   LSLAES_COMMS_REPLY_BASE,
                   llList2String((l = []) + l, 0),
                   id
               );
               return;
           }

           if (y & LSLAES_COMMS_PRIME) {
               msg = lslAESKeyExpansion((l = []) + l);
               if (msg == "") y += LSLAES_COMMS_REPLY_BASE;
               else y = LSLAES_COMMS_REPLY_BASE;

               llMessageLinked(x, y, (msg = "") + msg, id);
           } else {
               if (y & LSLAES_COMMS_ENCRYPT) 
                   l = lslAESCipher((l = []) + l);
               else if (y & LSLAES_COMMS_DECRYPT) 
                   l = lslAESInvertCipher((l = []) + l);
               else if (y & LSLAES_COMMS_HASH) 
                   l = lslAESHash((l = []) + l, LSLAES_HASH_SIZE);
               else return;

               y += LSLAES_COMMS_REPLY_BASE;

               if (y & LSLAES_COMMS_BASE64) 
                   msg = lslAESBytesToBase64((l = []) + l);
               else msg = lslAESBytesToHex((l = []) + l);

               llMessageLinked(x, y, (msg = "") + msg, id);
           }
       }
   }

}

Example use-cases

The following code-snippets contain basic example scripts which demonstrate how to use the AES encryption engine. You may edit these to better fit your code, or use the logic presented to implement it differently.

Encrypt a message

This snippet takes a key and a message store in global variables and encrypts the message using the key. You should NOT use the same key represented, and should avoid using the same key all the time where possible, however, secure key generation is not an aspect of this library.

<lsl>string myKey = "0123456789ABCDEF0123456789ABCDEF"; // 128-bit key in hexadecimal string message = "Hello world! I am an encrypted message!";

default {

   state_entry() { // First prime the engine with a key
       llMessageLinked(
           LINK_THIS, // Change this to wherever your AES engine script is located
           33792, // Prime a hex-key
           myKey,
           llGetKey()
       );
   }

   link_message(integer x, integer y, string msg, key id) {
       if ((y < 65536) || (id != llGetKey())) 
           return; // Ignore other messages

       y -= 65536;
       if (!y) llOwnerSay("ERROR: "+msg);
       else if (y == 1024) state encrypt;
   }

}

state encrypt {

   state_entry() { // Send our message
       llMessageLinked(
           LINK_THIS, // Same as above
           36865, // Encrypt a base64 message
           llStringToBase64(message),
           llGetKey()
       );
   }

   link_message(integer x, integer y, string msg, key id) {
       if ((y < 65536) || (id != llGetKey())) 
           return; // Ignore other messages

       y -= 65536;
       if (!y) llOwnerSay("ERROR: "+msg);
       else if (y == 4097) 
           llOwnerSay("Encrypted = "+msg);
   }

}</lsl>

Decrypt a message

This snippet will take a message encrypted using the above code and decrypt it.

<lsl>string myKey = "0123456789ABCDEF0123456789ABCDEF"; // 128-bit key in hexadecimal string ciphertext = "hKzCZAO0jdHzhz+rUJN6HQdcTOB90PWnQm4vhHTb+/7Q+Hzwzih4KEx3vPJEYyMs==";

default {

   state_entry() { // First prime the engine with a key
       llMessageLinked(
           LINK_THIS, // Change this to wherever your AES engine script is located
           33792, // Prime a hex-key
           myKey,
           llGetKey()
       );
   }

   link_message(integer x, integer y, string msg, key id) {
       if ((y < 65536) || (id != llGetKey())) 
           return; // Ignore other messages

       y -= 65536;
       if (!y) llOwnerSay("ERROR: "+msg);
       else if (y == 1024) state decrypt;
   }

}

state decrypt {

   state_entry() { // Send our message
       llMessageLinked(
           LINK_THIS, // Same as above
           49153, // Decrypt a base64 message
           ciphertext,
           llGetKey()
       );
   }

   link_message(integer x, integer y, string msg, key id) {
       if ((y < 65536) || (id != llGetKey())) 
           return; // Ignore other messages

       y -= 65536;
       if (!y) llOwnerSay("ERROR: "+msg);
       else if (y == 16385) 
           llOwnerSay("Decrypted = "+llBase64ToString(msg));
   }

}</lsl>

PHP Code

An equivalent PHP solution is pending, and will be pretty much an exact port of the LSL code.