Difference between revisions of "AES LSL+ Implementation"
Line 604: | Line 604: | ||
while (i < extra) { | while (i < extra) { | ||
if (padding == LSLAES_PAD_RANDOM_ID()) | if (padding == LSLAES_PAD_RANDOM_ID()) { | ||
byte = (integer)llFrand(256.0) & 0xFF; | byte = (integer)llFrand(256.0) & 0xFF; | ||
v = v | (byte << (i << 3)); | |||
} | |||
++i; | ++i; | ||
} | } |
Revision as of 08:37, 20 August 2009
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
- Broker
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_NULLS_SAFE() { return "PAD_NULLS_SAFE"; } 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_NULLS_SAFE(), 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_NULLS_SAFE_ID() {// Adds a single '1' bit before padding // with zero-bytes to the block-boundary. // This method is safer than normal PAD_NULLS return LSLAES_PAD_LOOKUP(LSLAES_PAD_NULLS_SAFE()); } 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_NULLS_SAFE(),LSLAES_PAD_NULLS_SAFE_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_NULLS_SAFE()) || 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 if (SUPPORTS_PAD(LSLAES_PAD_NULLS_SAFE())) {
if (padding == LSLAES_PAD_NULLS_SAFE_ID()) { // We want to add an extra '1' bit to halt unsafe null-trimming // Does an extra bit exist? ++bits; integer words = bits / 32; integer bit = bits % 32;
if (words < (data != [])) { integer word = llList2Integer(data, words); data = llListReplaceList( (data = []) + data, [word | (1 << (32 - bit))], words, words ); } else data += [0x80000000];
if ((--extra) < 0) extra += blockSize; // Now just let the function pad nulls as normal padding = LSLAES_PAD_NULLS_ID(); }
} 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); integer byte = 0; if ((extra == bytes) && (padding != LSLAES_PAD_NULLS_ID())) byte = bytes; 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_NULLS_SAFE()) || 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; integer excessBits = 0; // Remove extra bytes as required if ((padding == LSLAES_PAD_NULLS_ID()) || (padding == LSLAES_PAD_NULLS_SAFE_ID())) { if (SUPPORTS_PAD(LSLAES_PAD_NULLS()) || SUPPORTS_PAD(LSLAES_PAD_NULLS_SAFE())) {
// We remove all zero-bytes at the end of the data integer l = data != []; integer v = 0; integer j = 0;
while (words < l) { v = llList2Integer(data, -(words + 1));
if (v == 0) { // Four null-bytes ++words; bytes += 4; } else { integer byte = j = 0; do { byte = (v >> (j << 3)) & 0xFF;
if (byte == 0) ++bytes; else jump skip; } while ((++j) < 4); } } @skip;
if (SUPPORTS_PAD(LSLAES_PAD_NULLS_SAFE())) { if (padding == LSLAES_PAD_NULLS_SAFE_ID()) { // Now correct the bit-count of the final byte integer byte = (v >> (j << 3)) & 0xFF; integer i = 1; while (i < 0xFF) { ++excessBits; if (byte & i) jump skip2; i = i << 1; } @skip2; } }
} } 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) + excessBits; 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>