AES LSL+ Implementation: Difference between revisions
| Line 106: | Line 106: | ||
string LSLAES_PAD_RBT() { return "PAD_RBT"; } | string LSLAES_PAD_RBT() { return "PAD_RBT"; } | ||
string LSLAES_PAD_NULLS() { return "PAD_NULLS"; } | 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_RANDOM() { return "PAD_RANDOM"; } | ||
string LSLAES_PAD_ZEROES() { return "PAD_ZEROES"; } | string LSLAES_PAD_ZEROES() { return "PAD_ZEROES"; } | ||
| Line 115: | Line 116: | ||
LSLAES_PAD_RBT(), | LSLAES_PAD_RBT(), | ||
LSLAES_PAD_NULLS(), | LSLAES_PAD_NULLS(), | ||
LSLAES_PAD_NULLS_SAFE(), | |||
LSLAES_PAD_RANDOM(), | LSLAES_PAD_RANDOM(), | ||
LSLAES_PAD_ZEROES() | LSLAES_PAD_ZEROES() | ||
| Line 136: | Line 138: | ||
// Padding creates blocks of lslAESPadSize | // Padding creates blocks of lslAESPadSize | ||
return LSLAES_PAD_LOOKUP(LSLAES_PAD_NULLS()); | 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 | integer LSLAES_PAD_RANDOM_ID() { // Adds random bytes to the end of the | ||
Revision as of 13:22, 19 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_RANDOM(), LSLAES_PAD_RANDOM_ID(), LSLAES_PAD_ZEROES(), LSLAES_PAD_ZEROES_ID() ]; }
integer LSLAES_PAD_DEFAULT() { return llList2Integer(SUPPORTED_PADS(), 1); }
// pragma inline integer SUPPORTS_PAD(string pad) { return (~llListFindList(SUPPORTED_PADS(), [pad])); }
// Treats the above list as a byte-array, retrieving the desired byte value. integer lslAESGetSBoxInvertedByte(integer n) Template:Return lslAESMultInverse(lslAESInverseAffine(n));
// Treats the above list as a byte-array, retrieving the desired byte value. integer lslAESGetSBoxByte(integer n) Template:Return lslAESAffine(lslAESMultInverse(n));
// Calculates high-bit of x / 2 in a finite field integer lslAESHibit(integer x) {{
x = (x >> 1) | (x >> 2); x = x | (x >> 2); x = x | (x >> 4); return (++x) >> 1;
}}
// Calculates a multipilicative inverse in a finite field integer lslAESMultInverse(integer p1) {{
if(p1 < 2) return p1;
integer p2 = 0x1b;
integer n1 = lslAESHibit(p1);
integer n2 = 0x80;
integer v1 = 1;
integer v2 = 0;
do {
if(n1)
while(n2 >= n1) { // Divide polynomial p2 by p1
n2 /= n1; // shift smaller polynomial left
p2 = p2 ^ ((p1 * n2) & 0xFF); // and remove from larger one
v2 = v2 ^ ((v1 * n2) & 0xFF); // shift accumulated value and
n2 = lslAESHibit(p2); // add into result
}
else return v1;
if(n2) // repeat with values swapped
while(n1 >= n2) {
n1 /= n2;
p1 = p1 ^ ((p2 * n1) & 0xFF);
v1 = v1 ^ ((v2 * n1) & 0xFF);
n1 = lslAESHibit(p1);
}
else return v2;
} while (TRUE);
return 0;
}}
// Affine function for sbox integer lslAESAffine(integer x) {{
x = x ^ (x << 1) ^ (x << 2) ^ (x << 3) ^ (x << 4); return 0x63 ^ ((x ^ (x >> 8)) & 0xFF);
}}
// Inverse affine function for sbox inversion integer lslAESInverseAffine(integer x) {{
x = (x << 1) ^ (x << 3) ^ (x << 6); return 0x05 ^ ((x ^ (x >> 8)) & 0xFF);
}}
integer lslAESMode; // Set in default state_entry() integer lslAESPad; // Set in default state_entry() integer lslAESPadSize; // Set in default state_entry()
// Used for the actual encryption, generated from Key list lslAESRoundKey = []; // The number of rounds to perform (bigger key == more rounds) integer lslAESRounds = 0;
// The following are used for the state instead of a list integer lslAESStateX0Y0 = 0; integer lslAESStateX0Y1 = 0; integer lslAESStateX0Y2 = 0; integer lslAESStateX0Y3 = 0; integer lslAESStateX1Y0 = 0; integer lslAESStateX1Y1 = 0; integer lslAESStateX1Y2 = 0; integer lslAESStateX1Y3 = 0; integer lslAESStateX2Y0 = 0; integer lslAESStateX2Y1 = 0; integer lslAESStateX2Y2 = 0; integer lslAESStateX2Y3 = 0; integer lslAESStateX3Y0 = 0; integer lslAESStateX3Y1 = 0; integer lslAESStateX3Y2 = 0; integer lslAESStateX3Y3 = 0;
// Used to initialise state for CBC and other mode integer lslAESInputVector0 = 0; integer lslAESInputVector1 = 0; integer lslAESInputVector2 = 0; integer lslAESInputVector3 = 0;
//##########################################################################// // HIGH-LEVEL FUNCTIONS // //##########################################################################// // The following functions are the ones to call to encrypt/decrypt // //##########################################################################// // Performs a cipher with necessary padding performed before execution // pragma inline list lslAESPadCipher(list data) {{
integer bits = llList2Integer(data, 0);
data = llDeleteSubList((data = []) + data, 0, 0);
integer padding = lslAESPad;
if (padding == LSLAES_PAD_NONE_ID()) {
if (lslAESMode == LSLAES_MODE_CFB_ID())
return [bits] + lslAESCipher((data = []) + data);
padding = LSLAES_PAD_RBT_ID();
}
integer blockSize = lslAESPadSize;
if (padding == LSLAES_PAD_RBT_ID()) blockSize = 128;
integer blocks = bits / blockSize;
integer extra = bits % blockSize;
if (padding == LSLAES_PAD_RBT_ID()) {
if (SUPPORTS_PAD(LSLAES_PAD_RBT())) {
// This scheme takes the last encrypted block, encrypts it again and // XORs it with any leftover data, maintaining data-length. If input // is less than a block in size then the current input-vector is used. list final = []; if (extra > 0) { integer words = extra / 32; if ((words * 32) < extra) ++words;
// Grab leftover words list t = llList2List(data, -words, -1);
// Encrypt all other data list lb = []; if (blocks < 1) { // If not enough for a block, we generate lb using // a double cipher of input vector. lb = lslAESCipher( lslAESCipher((data = []) + [ lslAESInputVector0, lslAESInputVector1, lslAESInputVector2, lslAESInputVector3 ]) ); } else { // If there are blocks, we encrypt normally, then // double-encrypt the final block for lb data = lslAESCipher( llDeleteSubList((data = []) + data, -words, -1) ); lb = lslAESCipher(llList2List(data, -4, -1)); }
// XOR lb with t integer i = 0; integer l = (t != []); do final = (final = []) + final + [llList2Integer(t, i) ^ llList2Integer(lb, i)]; while ((++i) < l);
return [bits] + (data = final = []) + data + final; } return [bits] + lslAESCipher((data = []) + data);
}
} else if (SUPPORTS_PAD(LSLAES_PAD_NULLS()) ||
SUPPORTS_PAD(LSLAES_PAD_RANDOM()) ||
SUPPORTS_PAD(LSLAES_PAD_ZEROES())) {
// This scheme works by adding bytes until the data is a
// multiple of lslAESPadSize bits long. In the case of
// PAD_NULLS this will only add extra data if needed,
// while the other types must always add at least one
// byte, as they also leave a note of bytes added in the
// final byte.
extra = blockSize - extra; // Bits to add
integer bytes = extra / 8; // Bytes to add
if (bytes <= 0) {
if (padding == LSLAES_PAD_NULLS_ID())
jump skip; // Doesn't need to add anything
bytes = blockSize / 8;
extra += blockSize;
}
bits += extra;
integer words = bytes / 4; // Words to add
// First add bytes to end-word
extra = bytes % 4;
if (extra > 0) {
integer i = 0; integer v = llList2Integer(data, -1);
if ((extra == bytes) && (padding != LSLAES_PAD_NULLS_ID())) {
v = v | bytes;
i = 1;
}
integer byte = 0;
while (i < extra) {
if (padding == LSLAES_PAD_RANDOM_ID())
byte = (integer)llFrand(256.0) & 0xFF;
v = v | (byte << (i << 3));
++i;
}
data = llListReplaceList((data = []) + data, [v], -1, -1);
}
// Now, if needed, add words to end of data
if (words > 0) {
integer final = -1;
if (padding != LSLAES_PAD_NULLS_ID())
final = words - 1;
integer word = 0; integer byte = 0;
integer i = 0;
integer j = 0; list w = [];
do {
word = j = 0; // New word
do {
if ((padding != LSLAES_PAD_NULLS_ID()) &&
(i == final) && !j)
byte = bytes;
else if (padding == LSLAES_PAD_RANDOM_ID())
byte = (integer)llFrand(256.0) & 0xFF;
word = word | (byte << (j << 3));
} while ((++j) < 4);
w = (w = []) + w + [word];
} while ((++i) < words);
data = (data = w = []) + data + w;
}
@skip;
return [bits] + lslAESCipher((data = []) + data);
}
return [];
}}
// Performs an inverse cipher with appropriate padding handling performed // pragma inline list lslAESInvertPadCipher(list data) {{
integer bits = llList2Integer(data, 0);
data = llDeleteSubList((data = []) + data, 0, 0);
integer padding = lslAESPad;
if (padding == LSLAES_PAD_NONE_ID()) {
if (lslAESMode == LSLAES_MODE_CFB_ID())
return [bits] + lslAESInvertCipher((data = []) + data);
padding = LSLAES_PAD_RBT_ID();
}
integer blockSize = lslAESPadSize;
if (padding == LSLAES_PAD_RBT_ID()) blockSize = 128;
integer blocks = bits / blockSize;
integer extra = bits % blockSize;
if (padding == LSLAES_PAD_RBT_ID()) {
if (SUPPORTS_PAD(LSLAES_PAD_RBT())) {
// This scheme takes the last encrypted block, encrypts it again and // XORs it with any leftover data, maintaining data-length. If input // is less than a block in size then the current input-vector is used. list final = []; if (extra > 0) { integer words = extra / 32; if ((words * 32) < extra) ++words;
// Grab leftover words list t = llList2List(data, -words, -1);
// Decrypt all other data list lb = []; if (blocks < 1) { // If not enough for a block, we generate lb using // a double cipher of input vector. lb = lslAESCipher( lslAESCipher((data = []) + [ lslAESInputVector0, lslAESInputVector1, lslAESInputVector2, lslAESInputVector3 ]) ); } else { // If there are blocks, then we double-encrypt the // last full block to generate lb, then decrypt normally lb = lslAESCipher( llList2List(data, -(4 + words), -(words + 1)) ); data = lslAESInvertCipher( llDeleteSubList((data = []) + data, -words, -1) ); }
// XOR lb with t integer i = 0; integer l = (t != []); do final = (final = []) + final + [llList2Integer(t, i) ^ llList2Integer(lb, i)]; while ((++i) < l);
return [bits] + (data = final = []) + data + final; } return [bits] + lslAESInvertCipher((data = []) + data);
}
} else if (SUPPORTS_PAD(LSLAES_PAD_NULLS()) ||
SUPPORTS_PAD(LSLAES_PAD_RANDOM()) ||
SUPPORTS_PAD(LSLAES_PAD_ZEROES())) {
// This scheme works by adding bytes until the data is a
// multiple of lslAESPadSize bits long. In the case of
// PAD_NULLS this will only add extra data if needed,
// while the other types must always add at least one
// byte, as they also leave a note of bytes added in the
// final byte.
// If the data is not on a block boundary, then extra bits
// have snuck in (usually due to base64 conversion). We
// assume here that padding was done correctly, and ignore
// extra bits.
if (extra > 0) {
bits -= extra;
integer e = extra / 32;
if ((e * 32) < extra) ++e;
extra = e;
if (extra <= 0) extra = 1;
if (extra > (data != [])) return [0];
data = llDeleteSubList((data = []) + data, -extra, -1);
}
// Perform the decryption
data = lslAESInvertCipher((data = []) + data);
integer bytes = 0; integer words = 0;
// Remove extra bytes as required
if (padding == LSLAES_PAD_NULLS_ID()) {
// We remove all zero-bytes at the end of the data
integer l = data != [];
integer v = 0;
while (words < l) {
v = llList2Integer(data, -(words + 1));
if (v == 0) { // Four null-bytes
++words;
bytes += 4;
} else {
integer j = 0; integer byte = 0;
do {
byte = (v >> (j << 3)) & 0xFF;
if (byte == 0) ++bytes;
else jump skip;
} while ((++j) < 4);
}
}
@skip;
} else {
// Get the number of bytes to remove from the final byte
bytes = llList2Integer(data, -1) & 0xFF;
if ((bytes << 3) >= bits) return [0];
words = bytes / 4;
}
// Lop-off words, excess bytes are accounted for later
if (words > 0)
data = llDeleteSubList((data = []) + data, -words, -1);
// Correct bit-header for output
bits -= bytes << 3;
return [bits] + (data = []) + data;
}
return [];
}}
// Decrypts a list of integers into a list of decrypted integers. // Padding adjustment must be performed as this function will only except data // that is a multiple of 128-bits long. list lslAESInvertCipher(list data) {{
// The following are used to pass blocks forward
integer prevBlock0 = lslAESInputVector0;
integer prevBlock1 = lslAESInputVector1;
integer prevBlock2 = lslAESInputVector2;
integer prevBlock3 = lslAESInputVector3;
integer nextBlock0 = 0;
integer nextBlock1 = 0;
integer nextBlock2 = 0;
integer nextBlock3 = 0;
integer j = 0;
integer l = (data != []);
list output = [];
while (l > 0) {
// Different modes treat blocks differently
if (lslAESMode == LSLAES_MODE_CBC_ID()) {
if (SUPPORTS_MODE(LSLAES_MODE_CBC())) {
// For CBC we load it, and must keep a copy nextBlock0 = llList2Integer(data, 0); lslAESStateX0Y0 = ((nextBlock0 >> 24) & 0xFF); lslAESStateX0Y1 = ((nextBlock0 >> 16) & 0xFF); lslAESStateX0Y2 = ((nextBlock0 >> 8 ) & 0xFF); lslAESStateX0Y3 = ((nextBlock0 ) & 0xFF); nextBlock1 = llList2Integer(data, 1); lslAESStateX1Y0 = ((nextBlock1 >> 24) & 0xFF); lslAESStateX1Y1 = ((nextBlock1 >> 16) & 0xFF); lslAESStateX1Y2 = ((nextBlock1 >> 8 ) & 0xFF); lslAESStateX1Y3 = ((nextBlock1 ) & 0xFF); nextBlock2 = llList2Integer(data, 2); lslAESStateX2Y0 = ((nextBlock2 >> 24) & 0xFF); lslAESStateX2Y1 = ((nextBlock2 >> 16) & 0xFF); lslAESStateX2Y2 = ((nextBlock2 >> 8 ) & 0xFF); lslAESStateX2Y3 = ((nextBlock2 ) & 0xFF); nextBlock3 = llList2Integer(data, 3); lslAESStateX3Y0 = ((nextBlock3 >> 24) & 0xFF); lslAESStateX3Y1 = ((nextBlock3 >> 16) & 0xFF); lslAESStateX3Y2 = ((nextBlock3 >> 8 ) & 0xFF); lslAESStateX3Y3 = ((nextBlock3 ) & 0xFF);
}
} else if (lslAESMode == LSLAES_MODE_CFB_ID()) {
if (SUPPORTS_MODE(LSLAES_MODE_CFB())) {
lslAESStateX0Y0 = ((prevBlock0 >> 24) & 0xFF); lslAESStateX0Y1 = ((prevBlock0 >> 16) & 0xFF); lslAESStateX0Y2 = ((prevBlock0 >> 8 ) & 0xFF); lslAESStateX0Y3 = ((prevBlock0 ) & 0xFF);
lslAESStateX1Y0 = ((prevBlock1 >> 24) & 0xFF); lslAESStateX1Y1 = ((prevBlock1 >> 16) & 0xFF); lslAESStateX1Y2 = ((prevBlock1 >> 8 ) & 0xFF); lslAESStateX1Y3 = ((prevBlock1 ) & 0xFF);
lslAESStateX2Y0 = ((prevBlock2 >> 24) & 0xFF); lslAESStateX2Y1 = ((prevBlock2 >> 16) & 0xFF); lslAESStateX2Y2 = ((prevBlock2 >> 8 ) & 0xFF); lslAESStateX2Y3 = ((prevBlock2 ) & 0xFF);
lslAESStateX3Y0 = ((prevBlock3 >> 24) & 0xFF); lslAESStateX3Y1 = ((prevBlock3 >> 16) & 0xFF); lslAESStateX3Y2 = ((prevBlock3 >> 8 ) & 0xFF); lslAESStateX3Y3 = ((prevBlock3 ) & 0xFF);
}
}
if (lslAESMode == LSLAES_MODE_CFB_ID()) {
if (SUPPORTS_MODE(LSLAES_MODE_CFB())) {
lslAESPerformCipher(); // CFB doesn't need inverse cipher
}
} else if (SUPPORTS_MODE(LSLAES_MODE_CBC())) {
j = lslAESRounds;
do {
if (j < lslAESRounds) {
lslAESInvertShiftRows();
lslAESInvertSubBytes();
}
lslAESAddRoundKey(j);
if (j && (j < lslAESRounds)) {
lslAESInvertMixColumns();
}
} while ((--j) >= 0);
}
// Ciphertext is generated differently by different modes
if (lslAESMode == LSLAES_MODE_CBC_ID()) {
if (SUPPORTS_MODE(LSLAES_MODE_CBC())) {
// For CBC we XOR with previous block before output output = (output = []) + output + [ prevBlock0 ^ ((lslAESStateX0Y0 << 24) | (lslAESStateX0Y1 << 16) | (lslAESStateX0Y2 << 8) | (lslAESStateX0Y3)), prevBlock1 ^ ((lslAESStateX1Y0 << 24) | (lslAESStateX1Y1 << 16) | (lslAESStateX1Y2 << 8) | (lslAESStateX1Y3)), prevBlock2 ^ ((lslAESStateX2Y0 << 24) | (lslAESStateX2Y1 << 16) | (lslAESStateX2Y2 << 8) | (lslAESStateX2Y3)), prevBlock3 ^ ((lslAESStateX3Y0 << 24) | (lslAESStateX3Y1 << 16) | (lslAESStateX3Y2 << 8) | (lslAESStateX3Y3)) ];
prevBlock0 = nextBlock0; prevBlock1 = nextBlock1; prevBlock2 = nextBlock2; prevBlock3 = nextBlock3;
}
} else if (SUPPORTS_MODE(LSLAES_MODE_CFB())) { // CFB
// For CBF we XOR input block for output and carry
// input for encryption next-block
prevBlock0 = llList2Integer(data, 0);
prevBlock1 = llList2Integer(data, 1);
prevBlock2 = llList2Integer(data, 2);
prevBlock3 = llList2Integer(data, 3);
output = (output = []) + output + [
prevBlock0 ^
((lslAESStateX0Y0 << 24) | (lslAESStateX0Y1 << 16) |
(lslAESStateX0Y2 << 8) | (lslAESStateX0Y3)),
prevBlock1 ^
((lslAESStateX1Y0 << 24) | (lslAESStateX1Y1 << 16) |
(lslAESStateX1Y2 << 8) | (lslAESStateX1Y3)),
prevBlock2 ^
((lslAESStateX2Y0 << 24) | (lslAESStateX2Y1 << 16) |
(lslAESStateX2Y2 << 8) | (lslAESStateX2Y3)),
prevBlock3 ^
((lslAESStateX3Y0 << 24) | (lslAESStateX3Y1 << 16) |
(lslAESStateX3Y2 << 8) | (lslAESStateX3Y3))
];
}
// Reduce input
if (l > 4)
data = llList2List((data = []) + data, 4, -1);
else data = [];
l -= 4;
}
return (output = []) + output;
}}
// Encrypts a list of integers into a list of encrypted integers. // Padding must be performed before being called so that data is a multiple of // 128-bits long. list lslAESCipher(list data) {{
// We must prime the state with the input vector
lslAESLoadInputVector();
integer l = (data != []);
integer j = 0;
list output = [];
while (l > 0) {
// Different modes treat blocks differently
if (lslAESMode == LSLAES_MODE_CBC_ID()) {
if (SUPPORTS_MODE(LSLAES_MODE_CBC())) {
// For CBC we XOR with the previous block to reduce // chances of patterns occurring j = llList2Integer(data, 0); lslAESStateX0Y0 = lslAESStateX0Y0 ^ ((j >> 24) & 0xFF); lslAESStateX0Y1 = lslAESStateX0Y1 ^ ((j >> 16) & 0xFF); lslAESStateX0Y2 = lslAESStateX0Y2 ^ ((j >> 8 ) & 0xFF); lslAESStateX0Y3 = lslAESStateX0Y3 ^ ((j ) & 0xFF); j = llList2Integer(data, 1); lslAESStateX1Y0 = lslAESStateX1Y0 ^ ((j >> 24) & 0xFF); lslAESStateX1Y1 = lslAESStateX1Y1 ^ ((j >> 16) & 0xFF); lslAESStateX1Y2 = lslAESStateX1Y2 ^ ((j >> 8 ) & 0xFF); lslAESStateX1Y3 = lslAESStateX1Y3 ^ ((j ) & 0xFF); j = llList2Integer(data, 2); lslAESStateX2Y0 = lslAESStateX2Y0 ^ ((j >> 24) & 0xFF); lslAESStateX2Y1 = lslAESStateX2Y1 ^ ((j >> 16) & 0xFF); lslAESStateX2Y2 = lslAESStateX2Y2 ^ ((j >> 8 ) & 0xFF); lslAESStateX2Y3 = lslAESStateX2Y3 ^ ((j ) & 0xFF); j = llList2Integer(data, 3); lslAESStateX3Y0 = lslAESStateX3Y0 ^ ((j >> 24) & 0xFF); lslAESStateX3Y1 = lslAESStateX3Y1 ^ ((j >> 16) & 0xFF); lslAESStateX3Y2 = lslAESStateX3Y2 ^ ((j >> 8 ) & 0xFF); lslAESStateX3Y3 = lslAESStateX3Y3 ^ ((j ) & 0xFF);
}
}
lslAESPerformCipher();
if (lslAESMode == LSLAES_MODE_CFB_ID()) {
if (SUPPORTS_MODE(LSLAES_MODE_CFB())) {
// For CFB we XOR blocks and carry them to the next // stage (by keeping the result in state) j = llList2Integer(data, 0); lslAESStateX0Y0 = lslAESStateX0Y0 ^ ((j >> 24) & 0xFF); lslAESStateX0Y1 = lslAESStateX0Y1 ^ ((j >> 16) & 0xFF); lslAESStateX0Y2 = lslAESStateX0Y2 ^ ((j >> 8 ) & 0xFF); lslAESStateX0Y3 = lslAESStateX0Y3 ^ ((j ) & 0xFF); j = llList2Integer(data, 1); lslAESStateX1Y0 = lslAESStateX1Y0 ^ ((j >> 24) & 0xFF); lslAESStateX1Y1 = lslAESStateX1Y1 ^ ((j >> 16) & 0xFF); lslAESStateX1Y2 = lslAESStateX1Y2 ^ ((j >> 8 ) & 0xFF); lslAESStateX1Y3 = lslAESStateX1Y3 ^ ((j ) & 0xFF); j = llList2Integer(data, 2); lslAESStateX2Y0 = lslAESStateX2Y0 ^ ((j >> 24) & 0xFF); lslAESStateX2Y1 = lslAESStateX2Y1 ^ ((j >> 16) & 0xFF); lslAESStateX2Y2 = lslAESStateX2Y2 ^ ((j >> 8 ) & 0xFF); lslAESStateX2Y3 = lslAESStateX2Y3 ^ ((j ) & 0xFF); j = llList2Integer(data, 3); lslAESStateX3Y0 = lslAESStateX3Y0 ^ ((j >> 24) & 0xFF); lslAESStateX3Y1 = lslAESStateX3Y1 ^ ((j >> 16) & 0xFF); lslAESStateX3Y2 = lslAESStateX3Y2 ^ ((j >> 8 ) & 0xFF); lslAESStateX3Y3 = lslAESStateX3Y3 ^ ((j ) & 0xFF);
}
}
output = (output = []) + output + [
(lslAESStateX0Y0 << 24) | (lslAESStateX0Y1 << 16) |
(lslAESStateX0Y2 << 8) | lslAESStateX0Y3,
(lslAESStateX1Y0 << 24) | (lslAESStateX1Y1 << 16) |
(lslAESStateX1Y2 << 8) | lslAESStateX1Y3,
(lslAESStateX2Y0 << 24) | (lslAESStateX2Y1 << 16) |
(lslAESStateX2Y2 << 8) | lslAESStateX2Y3,
(lslAESStateX3Y0 << 24) | (lslAESStateX3Y1 << 16) |
(lslAESStateX3Y2 << 8) | lslAESStateX3Y3
];
// Reduce input
if (l > 4)
data = llList2List((data = []) + data, 4, -1);
else data = [];
l -= 4;
}
return (output = []) + output;
}}
// Simply performs the cipher operation on current state, separated // for convenience with CBF and OBF modes (which perform a cipher in // order to decrypt data). lslAESPerformCipher() {{ integer j = 0; do { if (j) {
lslAESSubBytes();
lslAESShiftRows();
if (j < lslAESRounds) {
lslAESMixColumns();
}
}
lslAESAddRoundKey(j);
} while ((++j) <= lslAESRounds); }}
// Expands the input vector for use in block differentiation // pragma inline lslAESLoadInputVector() {{
lslAESStateX0Y0 = (lslAESInputVector0 >> 24) & 0xFF; lslAESStateX0Y1 = (lslAESInputVector0 >> 16) & 0xFF; lslAESStateX0Y2 = (lslAESInputVector0 >> 8 ) & 0xFF; lslAESStateX0Y3 = (lslAESInputVector0 ) & 0xFF; lslAESStateX1Y0 = (lslAESInputVector1 >> 24) & 0xFF; lslAESStateX1Y1 = (lslAESInputVector1 >> 16) & 0xFF; lslAESStateX1Y2 = (lslAESInputVector1 >> 8 ) & 0xFF; lslAESStateX1Y3 = (lslAESInputVector1 ) & 0xFF; lslAESStateX2Y0 = (lslAESInputVector2 >> 24) & 0xFF; lslAESStateX2Y1 = (lslAESInputVector2 >> 16) & 0xFF; lslAESStateX2Y2 = (lslAESInputVector2 >> 8 ) & 0xFF; lslAESStateX2Y3 = (lslAESInputVector2 ) & 0xFF; lslAESStateX3Y0 = (lslAESInputVector3 >> 24) & 0xFF; lslAESStateX3Y1 = (lslAESInputVector3 >> 16) & 0xFF; lslAESStateX3Y2 = (lslAESInputVector3 >> 8 ) & 0xFF; lslAESStateX3Y3 = (lslAESInputVector3 ) & 0xFF;
}}
//##########################################################################// // PROCESSING FUNCTIONS // //##########################################################################// // The following functions are used to process data that is being // // encrypted or decrypted. // //##########################################################################// // XORs the value with RoundKey values // pragma inline lslAESAddRoundKey(integer round) {{
round = round << 2; integer t = llList2Integer(lslAESRoundKey, round); lslAESStateX0Y0 = lslAESStateX0Y0 ^ ((t >> 24) & 0xFF); lslAESStateX0Y1 = lslAESStateX0Y1 ^ ((t >> 16) & 0xFF); lslAESStateX0Y2 = lslAESStateX0Y2 ^ ((t >> 8 ) & 0xFF); lslAESStateX0Y3 = lslAESStateX0Y3 ^ ((t ) & 0xFF); t = llList2Integer(lslAESRoundKey, ++round); lslAESStateX1Y0 = lslAESStateX1Y0 ^ ((t >> 24) & 0xFF); lslAESStateX1Y1 = lslAESStateX1Y1 ^ ((t >> 16) & 0xFF); lslAESStateX1Y2 = lslAESStateX1Y2 ^ ((t >> 8 ) & 0xFF); lslAESStateX1Y3 = lslAESStateX1Y3 ^ ((t ) & 0xFF); t = llList2Integer(lslAESRoundKey, ++round); lslAESStateX2Y0 = lslAESStateX2Y0 ^ ((t >> 24) & 0xFF); lslAESStateX2Y1 = lslAESStateX2Y1 ^ ((t >> 16) & 0xFF); lslAESStateX2Y2 = lslAESStateX2Y2 ^ ((t >> 8 ) & 0xFF); lslAESStateX2Y3 = lslAESStateX2Y3 ^ ((t ) & 0xFF); t = llList2Integer(lslAESRoundKey, ++round); lslAESStateX3Y0 = lslAESStateX3Y0 ^ ((t >> 24) & 0xFF); lslAESStateX3Y1 = lslAESStateX3Y1 ^ ((t >> 16) & 0xFF); lslAESStateX3Y2 = lslAESStateX3Y2 ^ ((t >> 8 ) & 0xFF); lslAESStateX3Y3 = lslAESStateX3Y3 ^ ((t ) & 0xFF);
}}
// Performs a substitution using SBox // pragma inline lslAESSubBytes() {{
lslAESStateX0Y0 = lslAESGetSBoxByte(lslAESStateX0Y0); lslAESStateX0Y1 = lslAESGetSBoxByte(lslAESStateX0Y1); lslAESStateX0Y2 = lslAESGetSBoxByte(lslAESStateX0Y2); lslAESStateX0Y3 = lslAESGetSBoxByte(lslAESStateX0Y3); lslAESStateX1Y0 = lslAESGetSBoxByte(lslAESStateX1Y0); lslAESStateX1Y1 = lslAESGetSBoxByte(lslAESStateX1Y1); lslAESStateX1Y2 = lslAESGetSBoxByte(lslAESStateX1Y2); lslAESStateX1Y3 = lslAESGetSBoxByte(lslAESStateX1Y3); lslAESStateX2Y0 = lslAESGetSBoxByte(lslAESStateX2Y0); lslAESStateX2Y1 = lslAESGetSBoxByte(lslAESStateX2Y1); lslAESStateX2Y2 = lslAESGetSBoxByte(lslAESStateX2Y2); lslAESStateX2Y3 = lslAESGetSBoxByte(lslAESStateX2Y3); lslAESStateX3Y0 = lslAESGetSBoxByte(lslAESStateX3Y0); lslAESStateX3Y1 = lslAESGetSBoxByte(lslAESStateX3Y1); lslAESStateX3Y2 = lslAESGetSBoxByte(lslAESStateX3Y2); lslAESStateX3Y3 = lslAESGetSBoxByte(lslAESStateX3Y3);
}}
// Performs a substition using SBoxInverted // pragma inline lslAESInvertSubBytes() {{
lslAESStateX0Y0 = lslAESGetSBoxInvertedByte(lslAESStateX0Y0); lslAESStateX0Y1 = lslAESGetSBoxInvertedByte(lslAESStateX0Y1); lslAESStateX0Y2 = lslAESGetSBoxInvertedByte(lslAESStateX0Y2); lslAESStateX0Y3 = lslAESGetSBoxInvertedByte(lslAESStateX0Y3); lslAESStateX1Y0 = lslAESGetSBoxInvertedByte(lslAESStateX1Y0); lslAESStateX1Y1 = lslAESGetSBoxInvertedByte(lslAESStateX1Y1); lslAESStateX1Y2 = lslAESGetSBoxInvertedByte(lslAESStateX1Y2); lslAESStateX1Y3 = lslAESGetSBoxInvertedByte(lslAESStateX1Y3); lslAESStateX2Y0 = lslAESGetSBoxInvertedByte(lslAESStateX2Y0); lslAESStateX2Y1 = lslAESGetSBoxInvertedByte(lslAESStateX2Y1); lslAESStateX2Y2 = lslAESGetSBoxInvertedByte(lslAESStateX2Y2); lslAESStateX2Y3 = lslAESGetSBoxInvertedByte(lslAESStateX2Y3); lslAESStateX3Y0 = lslAESGetSBoxInvertedByte(lslAESStateX3Y0); lslAESStateX3Y1 = lslAESGetSBoxInvertedByte(lslAESStateX3Y1); lslAESStateX3Y2 = lslAESGetSBoxInvertedByte(lslAESStateX3Y2); lslAESStateX3Y3 = lslAESGetSBoxInvertedByte(lslAESStateX3Y3);
}}
// Performs row shifts
// pragma inline
lslAESShiftRows() {{
// Rotate first row 1 columns to left integer t = lslAESStateX0Y1; lslAESStateX0Y1 = lslAESStateX1Y1; lslAESStateX1Y1 = lslAESStateX2Y1; lslAESStateX2Y1 = lslAESStateX3Y1; lslAESStateX3Y1 = t; // Rotate second row 2 columns to left t = lslAESStateX0Y2; lslAESStateX0Y2 = lslAESStateX2Y2; lslAESStateX2Y2 = t; t = lslAESStateX1Y2; lslAESStateX1Y2 = lslAESStateX3Y2; lslAESStateX3Y2 = t; // Rotate third row 3 columns to left t = lslAESStateX0Y3; lslAESStateX0Y3 = lslAESStateX3Y3; lslAESStateX3Y3 = lslAESStateX2Y3; lslAESStateX2Y3 = lslAESStateX1Y3; lslAESStateX1Y3 = t;
}}
// Undoes a set of row shifts // pragma inline lslAESInvertShiftRows() {{
// Rotate first row 1 columns to right integer t = lslAESStateX3Y1; lslAESStateX3Y1 = lslAESStateX2Y1; lslAESStateX2Y1 = lslAESStateX1Y1; lslAESStateX1Y1 = lslAESStateX0Y1; lslAESStateX0Y1 = t; // Rotate second row 2 columns to right t = lslAESStateX0Y2; lslAESStateX0Y2 = lslAESStateX2Y2; lslAESStateX2Y2 = t; t = lslAESStateX1Y2; lslAESStateX1Y2 = lslAESStateX3Y2; lslAESStateX3Y2 = t; // Rotate third row 3 columns to right t = lslAESStateX0Y3; lslAESStateX0Y3 = lslAESStateX1Y3; lslAESStateX1Y3 = lslAESStateX2Y3; lslAESStateX2Y3 = lslAESStateX3Y3; lslAESStateX3Y3 = t;
}}
// Mixes columns of the state // pragma inline lslAESMixColumns() {{
integer t = lslAESStateX0Y0;
integer t1 = lslAESStateX0Y0 ^ lslAESStateX0Y1 ^
lslAESStateX0Y2 ^ lslAESStateX0Y3;
integer t2 = lslAESXTimes(lslAESStateX0Y0 ^ lslAESStateX0Y1);
lslAESStateX0Y0 = lslAESStateX0Y0 ^ t2 ^ t1;
t2 = lslAESXTimes(lslAESStateX0Y1 ^ lslAESStateX0Y2);
lslAESStateX0Y1 = lslAESStateX0Y1 ^ t2 ^ t1;
t2 = lslAESXTimes(lslAESStateX0Y2 ^ lslAESStateX0Y3);
lslAESStateX0Y2 = lslAESStateX0Y2 ^ t2 ^ t1;
t2 = lslAESXTimes(lslAESStateX0Y3 ^ t);
lslAESStateX0Y3 = lslAESStateX0Y3 ^ t2 ^ t1;
t = lslAESStateX1Y0;
t1 = lslAESStateX1Y0 ^ lslAESStateX1Y1 ^ lslAESStateX1Y2 ^ lslAESStateX1Y3;
t2 = lslAESXTimes(lslAESStateX1Y0 ^ lslAESStateX1Y1);
lslAESStateX1Y0 = lslAESStateX1Y0 ^ t2 ^ t1;
t2 = lslAESXTimes(lslAESStateX1Y1 ^ lslAESStateX1Y2);
lslAESStateX1Y1 = lslAESStateX1Y1 ^ t2 ^ t1;
t2 = lslAESXTimes(lslAESStateX1Y2 ^ lslAESStateX1Y3);
lslAESStateX1Y2 = lslAESStateX1Y2 ^ t2 ^ t1;
t2 = lslAESXTimes(lslAESStateX1Y3 ^ t);
lslAESStateX1Y3 = lslAESStateX1Y3 ^ t2 ^ t1;
t = lslAESStateX2Y0;
t1 = lslAESStateX2Y0 ^ lslAESStateX2Y1 ^ lslAESStateX2Y2 ^ lslAESStateX2Y3;
t2 = lslAESXTimes(lslAESStateX2Y0 ^ lslAESStateX2Y1);
lslAESStateX2Y0 = lslAESStateX2Y0 ^ t2 ^ t1;
t2 = lslAESXTimes(lslAESStateX2Y1 ^ lslAESStateX2Y2);
lslAESStateX2Y1 = lslAESStateX2Y1 ^ t2 ^ t1;
t2 = lslAESXTimes(lslAESStateX2Y2 ^ lslAESStateX2Y3);
lslAESStateX2Y2 = lslAESStateX2Y2 ^ t2 ^ t1;
t2 = lslAESXTimes(lslAESStateX2Y3 ^ t);
lslAESStateX2Y3 = lslAESStateX2Y3 ^ t2 ^ t1;
t = lslAESStateX3Y0;
t1 = lslAESStateX3Y0 ^ lslAESStateX3Y1 ^ lslAESStateX3Y2 ^ lslAESStateX3Y3;
t2 = lslAESXTimes(lslAESStateX3Y0 ^ lslAESStateX3Y1);
lslAESStateX3Y0 = lslAESStateX3Y0 ^ t2 ^ t1;
t2 = lslAESXTimes(lslAESStateX3Y1 ^ lslAESStateX3Y2);
lslAESStateX3Y1 = lslAESStateX3Y1 ^ t2 ^ t1;
t2 = lslAESXTimes(lslAESStateX3Y2 ^ lslAESStateX3Y3);
lslAESStateX3Y2 = lslAESStateX3Y2 ^ t2 ^ t1;
t2 = lslAESXTimes(lslAESStateX3Y3 ^ t);
lslAESStateX3Y3 = lslAESStateX3Y3 ^ t2 ^ t1;
}}
// Used when column mixing // pragma inline integer lslAESXTimes(integer x) {{
return ((x << 1) ^ (((x >> 7) & 1) * 0x1b)) & 0xFF;
}}
// Used when column mixing integer lslAESMultiply(integer x, integer y) {{
integer xT = lslAESXTimes(x);
integer xT2 = lslAESXTimes(xT);
integer xT3 = lslAESXTimes(xT2);
return (((y & 1) * x) ^ (((y >> 1) & 1) * xT) ^
(((y >> 2) & 1) * xT2) ^ (((y >> 3) & 1) * xT3) ^
(((y >> 4) & 1) * lslAESXTimes(xT3))) & 0xFF;
}}
// Try to understand this at your own peril! // pragma inline lslAESInvertMixColumns() {{
integer a = lslAESStateX0Y0;
integer b = lslAESStateX0Y1;
integer c = lslAESStateX0Y2;
integer d = lslAESStateX0Y3;
lslAESStateX0Y0 = lslAESMultiply(a, 0x0e) ^ lslAESMultiply(b, 0x0b) ^
lslAESMultiply(c, 0x0d) ^ lslAESMultiply(d, 0x09);
lslAESStateX0Y1 = lslAESMultiply(a, 0x09) ^ lslAESMultiply(b, 0x0e) ^
lslAESMultiply(c, 0x0b) ^ lslAESMultiply(d, 0x0d);
lslAESStateX0Y2 = lslAESMultiply(a, 0x0d) ^ lslAESMultiply(b, 0x09) ^
lslAESMultiply(c, 0x0e) ^ lslAESMultiply(d, 0x0b);
lslAESStateX0Y3 = lslAESMultiply(a, 0x0b) ^ lslAESMultiply(b, 0x0d) ^
lslAESMultiply(c, 0x09) ^ lslAESMultiply(d, 0x0e);
a = lslAESStateX1Y0;
b = lslAESStateX1Y1;
c = lslAESStateX1Y2;
d = lslAESStateX1Y3;
lslAESStateX1Y0 = lslAESMultiply(a, 0x0e) ^ lslAESMultiply(b, 0x0b) ^
lslAESMultiply(c, 0x0d) ^ lslAESMultiply(d, 0x09);
lslAESStateX1Y1 = lslAESMultiply(a, 0x09) ^ lslAESMultiply(b, 0x0e) ^
lslAESMultiply(c, 0x0b) ^ lslAESMultiply(d, 0x0d);
lslAESStateX1Y2 = lslAESMultiply(a, 0x0d) ^ lslAESMultiply(b, 0x09) ^
lslAESMultiply(c, 0x0e) ^ lslAESMultiply(d, 0x0b);
lslAESStateX1Y3 = lslAESMultiply(a, 0x0b) ^ lslAESMultiply(b, 0x0d) ^
lslAESMultiply(c, 0x09) ^ lslAESMultiply(d, 0x0e);
a = lslAESStateX2Y0;
b = lslAESStateX2Y1;
c = lslAESStateX2Y2;
d = lslAESStateX2Y3;
lslAESStateX2Y0 = lslAESMultiply(a, 0x0e) ^ lslAESMultiply(b, 0x0b) ^
lslAESMultiply(c, 0x0d) ^ lslAESMultiply(d, 0x09);
lslAESStateX2Y1 = lslAESMultiply(a, 0x09) ^ lslAESMultiply(b, 0x0e) ^
lslAESMultiply(c, 0x0b) ^ lslAESMultiply(d, 0x0d);
lslAESStateX2Y2 = lslAESMultiply(a, 0x0d) ^ lslAESMultiply(b, 0x09) ^
lslAESMultiply(c, 0x0e) ^ lslAESMultiply(d, 0x0b);
lslAESStateX2Y3 = lslAESMultiply(a, 0x0b) ^ lslAESMultiply(b, 0x0d) ^
lslAESMultiply(c, 0x09) ^ lslAESMultiply(d, 0x0e);
a = lslAESStateX3Y0;
b = lslAESStateX3Y1;
c = lslAESStateX3Y2;
d = lslAESStateX3Y3;
lslAESStateX3Y0 = lslAESMultiply(a, 0x0e) ^ lslAESMultiply(b, 0x0b) ^
lslAESMultiply(c, 0x0d) ^ lslAESMultiply(d, 0x09);
lslAESStateX3Y1 = lslAESMultiply(a, 0x09) ^ lslAESMultiply(b, 0x0e) ^
lslAESMultiply(c, 0x0b) ^ lslAESMultiply(d, 0x0d);
lslAESStateX3Y2 = lslAESMultiply(a, 0x0d) ^ lslAESMultiply(b, 0x09) ^
lslAESMultiply(c, 0x0e) ^ lslAESMultiply(d, 0x0b);
lslAESStateX3Y3 = lslAESMultiply(a, 0x0b) ^ lslAESMultiply(b, 0x0d) ^
lslAESMultiply(c, 0x09) ^ lslAESMultiply(d, 0x0e);
}}
//##########################################################################// // ENCRYPTION SET-UP FUNCTIONS // //##########################################################################// // The following functions are used to set-up the AES encryption engine. // //##########################################################################// // Takes the key bytes provided and sets up the engine ready to encrypt or // decrypt using them. // // Thanks to Strife Onizuka for providing optimisations to this function. // pragma inline list lslAESKeyExpansion(list keyBytes) {{
// Don't need the bit-count and the first round key is the key itself
integer len = (
(
lslAESRoundKey = llDeleteSubList( // Remove header, copy into rounds
(lslAESRoundKey = keyBytes = []) + keyBytes,
0,
0
)
) != []); // Get the length
// Check that we are within reasonable limits
if ((len < 4) || (len > 8)) {
lslAESRoundKey = [];
return ["Invalid key size; must be 128, 192, or 256 bits!"];
}
// Calculate the number of required rounds
lslAESRounds = len + 6;
// All others are found from previous keys
integer i = 0;
integer x = (len * 3) + 28;
integer t = llList2Integer(lslAESRoundKey, -1);
do {
if (!(i % len)) {
// Rotate by 1, SubWord and Nudge
// [0x01020408, 0x01020408, 0x1b366cd8]
t = ((lslAESGetSBoxByte((t >> 16) & 0xFF) ^
(0x000D8080 >> (7 ^ (i / len)))) << 24) |
(lslAESGetSBoxByte((t >> 8) & 0xFF) << 16) |
(lslAESGetSBoxByte((t ) & 0xFF) << 8) |
lslAESGetSBoxByte((t >> 24) & 0xFF);
} else if ((len > 6) && (i % len) == 4) {
// SubWord
t = (lslAESGetSBoxByte((t >> 24) & 0xFF) << 24) |
(lslAESGetSBoxByte((t >> 16) & 0xFF) << 16) |
(lslAESGetSBoxByte((t >> 8) & 0xFF) << 8) |
(lslAESGetSBoxByte((t ) & 0xFF) );
}
// XOR k with four previous RoundKey values And add the
// new entries, yay!
lslAESRoundKey = (lslAESRoundKey = []) + lslAESRoundKey +
(t = (t ^ llList2Integer(lslAESRoundKey, i)));
} while ((i = -~i) < x);
// On success no error message is returned
return [1];
}}
// Takes a list of 32-bit words and uses them to initialise the // input vector used by CBC and similar modes. Data must contain // at least 128-bits of data (all else is discarded) // pragma inline list lslAESSetInputVector(list data) {{
if ((data != []) < 5)
return ["Input vector must be at least 128-bits long"];
// Ignore index 0 (the header)
lslAESInputVector0 = llList2Integer(data, 1);
lslAESInputVector1 = llList2Integer(data, 2);
lslAESInputVector2 = llList2Integer(data, 3);
lslAESInputVector3 = llList2Integer(data, 4);
return [1];
}}
//##########################################################################// // SERIALISATION FUNCTIONS // //##########################################################################// // The following functions are used to serialise a string into a list of // // byte data for use as a key, or to encrypt/decrypt. // //##########################################################################// // Converts a binary-string into a list of 32-bit integers, using the provided // alphabet and character width (in bits). See the helper functions // lslAESBase64ToBytes and lslAESHexToBytes for examples of this function's // usage. The list returned will contain as its first-entry an integer count // of the number of bits represented by the input string // (llStringLength(s) * width). // // Thanks to Strife Onizuka for providing optimisations to this function. // pragma inline list lslAESStringToBytes(string s, integer width, string alphabet) {{
integer l = llStringLength(s);
list n = [l * width]; // Add bit-length
integer bitbuf = 0;
integer adjust = 32;
integer i = 0;
integer val;
while (i < l) {
val = llSubStringIndex(alphabet, llGetSubString(s, i, i));
if (val < 0) {
s = "";
return (n = []) +
["Invalid character at index "+(string)i];
}
if ((adjust -= width) <= 0) {
bitbuf = bitbuf | (val >> -adjust);
n += [bitbuf];
adjust += 32;
if (adjust < 32) bitbuf = (val << adjust);
else bitbuf = 0;
} else bitbuf = bitbuf | (val << adjust);
++i;
}
s = "";
if (adjust < 32)
return (n = []) + n + [bitbuf];
return (n = []) + n;
}}
// Takes a list of integers (with a bit-count as the first entry) and outputs // it as a string using characters from the given alphabet, where each character // represents a number of bits as described by width. Please refer to the helper // functions lslAESBytesToBase64 and lslAESBytesToHex for examples of this // function's usage. // // Thanks to Strife Onizuka for providing optimisations to this function. // pragma inline string lslAESBytesToString(list b, integer width, string alphabet) {{
integer bits = llList2Integer(b, 0);
integer i = 0;
integer mask = ~(-1 << width);
integer shift = 32 - width;
integer available = 0;
integer prev = 0;
integer buf;
integer extra;
integer value;
string s = "";
@lslAESBytesToStringLoop;
if((bits -= 32) > -32) {
available += 32 + (bits * (0 > bits));
buf = llList2Integer(b, ++i);
if (available >= width) {
if (prev) {
s += llGetSubString(
alphabet,
value = (
extra |
(
(buf >> (shift + prev)) &
~(-1 << (width - prev))
)
),
value
);
buf = buf << (width - prev);
available -= width;
}
while(available >= width) {
s += llGetSubString(
alphabet,
value = ((buf >> shift) & mask),
value
);
buf = buf << width;
available -= width;
}
if (prev = available) // Update prev
extra = (buf >> shift) & mask;
jump lslAESBytesToStringLoop;
}
}
if(available) {
mask = -1 << (width - prev);
return (s = "") + s +
llGetSubString(
alphabet,
value = ((extra & mask) |
(
(buf >> (shift + prev)) &
((-1 << (width - available)) ^ mask))
),
value
);
}
return (s = "") + s;
}}
// pragma inline error(integer link, string str, key id) {{
llMessageLinked(
link,
LSLAES_FILTER_REPLY() | LSLAES_COMMAND_ERROR(),
str,
id
);
}}
default { state_entry() { lslAESMode = LSLAES_MODE_DEFAULT(); lslAESPad = LSLAES_PAD_DEFAULT(); lslAESPadSize = LSLAES_PAD_SIZE_DEFAULT();
llOwnerSay((string)llGetFreeMemory() + " bytes free."); }
link_message(integer x, integer y, string msg, key id) {
// Is the message for us?
if ((y & LSLAES_FILTER_MASK()) == LSLAES_FILTER_REQUEST()) {
// Refuse overly large messages
if (llStringLength(msg) > LSLAES_MAX_SIZE()) {
error(
x,
"Maxmimum message length is " +
(string)LSLAES_MAX_SIZE() +
" characters",
id
);
return;
}
// Special case for COMMAND_SETUP
if ((y & LSLAES_COMMAND_MASK()) == LSLAES_COMMAND_SETUP()) {
if (SUPPORTS_SETUP()) { // Break up flags if (msg != "") { list flags = llCSV2List((msg = "") + msg); integer i = 0; integer l = (flags != []); integer j = 0; list flag = []; do { flag = [llToUpper(llList2String(flags, i))];
if (~(j = llListFindList(LSLAES_MODES(), flag))) lslAESMode = j; else if (~(j = llListFindList(LSLAES_PADS(), flag))) lslAESPad = j; else if ((string)flag == LSLAES_PAD_SIZE()) { j = llList2Integer(flags, ++i); // Next value should be pad-size if (j <= 0) j = LSLAES_PAD_SIZE_DEFAULT(); else if (j > LSLAES_PAD_SIZE_DEFAULT()) { error(x, "Maximum pad-size is "+(string)LSLAES_PAD_SIZE_DEFAULT()+" bits", id); return; } else if (j % 128) { error(x, "Pad-size must be a multiple of 128-bits", id); return; }
lslAESPadSize = j; } else { error(x, "Unsupported flag '"+(string)flag+"'", id); return; } } while ((++i) < l); }
// Construct reply llMessageLinked( x, LSLAES_FILTER_REPLY() | LSLAES_COMMAND_SETUP(), "", id ); } else error(x, "Set-up not supported (single-mode and pad available)", id);
return;
}
// What type of data do we have?
integer type = y & LSLAES_INPUT_TYPE_MASK();
list data = [];
if ((type == LSLAES_INPUT_HEX()) || (type == LSLAES_INPUT_BASE64())) {
string alphabet = LSLAES_BASE64_CHARS();
integer width = 6;
if (type == LSLAES_INPUT_BASE64()) {
integer e = llSubStringIndex(msg, "="); if (e > 0) msg = llDeleteSubString( (msg = "") + msg, e, -1 );
} else {
if (llGetSubString(msg, 0, 1) == "0x") msg = llDeleteSubString((msg = "") + msg, 0, 1); msg = llToLower((msg = "") + msg);
alphabet = LSLAES_HEX_CHARS(); width = 4;
}
data = lslAESStringToBytes(
(msg = "") + msg,
width,
(alphabet = "") + alphabet
);
} else data = [(msg = "") + "Unsupported input-type"];
// Was data parsed successfully?
if (llGetListEntryType(data, 0) != TYPE_INTEGER) {
error(x, llList2String((data = []) + data, 0), id);
return;
}
// Now determine mode of operation
type = y & LSLAES_COMMAND_MASK();
if (type == LSLAES_COMMAND_PRIME())
data = lslAESKeyExpansion((data = []) + data);
else if (type == LSLAES_COMMAND_ENCRYPT())
data = lslAESPadCipher((data = []) + data);
else if (type == LSLAES_COMMAND_DECRYPT())
data = lslAESInvertPadCipher((data = []) + data);
else if (type == LSLAES_COMMAND_INIT())
data = lslAESSetInputVector((data = []) + data);
else data = ["Unsupported mode"];
// Was mode executed successfully?
if (llGetListEntryType(data, 0) != TYPE_INTEGER) {
error(x, llList2String((data = []) + data, 0), id);
return;
}
// Convert into requested output type
integer output = 0;
if ((type != LSLAES_COMMAND_PRIME()) && (type != LSLAES_COMMAND_INIT())) {
output = y & LSLAES_OUTPUT_TYPE_MASK();
if ((output == LSLAES_OUTPUT_BASE64()) || (output == LSLAES_OUTPUT_HEX())) {
string alphabet = LSLAES_BASE64_CHARS();
integer width = 6;
if (output == LSLAES_OUTPUT_HEX()) {
alphabet = LSLAES_HEX_CHARS();
width = 4;
}
msg = lslAESBytesToString(
(data = []) + data,
width,
(alphabet = "") + alphabet
);
if (output == LSLAES_OUTPUT_BASE64()) {
integer l = llStringLength(msg) % 4; if (l && (l < 3)) { string add = ""; if (l == 2) add = "=="; else if (l == 1) add = "=";
msg = (msg = add = "") + msg + add; } output = LSLAES_INPUT_BASE64();
} else {
msg = (msg = "") + "0x" + msg;
output = LSLAES_INPUT_HEX();
}
} else {
error(x, "Invalid output type", id);
return;
}
}
// Construct reply
llMessageLinked(
x,
LSLAES_FILTER_REPLY() | type | output,
(msg = "") + msg,
id
);
}
}
}</lsl>
Examples
Encryption
<lsl>$import AES.AES_Helper.lslm();
integer aesLink = LINK_THIS;
string myKey = "1234567890ABCDEF0123456789ABCDEF"; // 128-bit key in hex string myMsg = "Hello world! I am a lovely message waiting to be encrypted!"; string myIV = "89ABCDEF0123456789ABCDEF01234567";
default {
state_entry() { // Setup the engine for use
lslAESSetup(
aesLink,
llDumpList2String(
[
LSLAES_MODE_CFB(), // CFB requires a less complex broker LSLAES_PAD_NONE(), // No padding LSLAES_PAD_SIZE(), // Pad into blocks of 512-bits 512 ], "," ),
llGetKey()
);
}
link_message(integer x, integer y, string msg, key id) {
if (!lslAESIsReply(y, id)) return;
y = lslAESGetReplyMode(y);
if (y == LSLAES_COMMAND_ERROR())
llOwnerSay("SETUP ERROR: "+msg);
else if (y == LSLAES_COMMAND_SETUP())
state prime;
}
}
state prime {
state_entry() { // First prime the engine with a key
lslAESPrimeHexKey(
aesLink,
myKey,
llGetKey()
);
}
link_message(integer x, integer y, string msg, key id) {
if (!lslAESIsReply(y, id)) return;
y = lslAESGetReplyMode(y);
if (y == LSLAES_COMMAND_ERROR())
llOwnerSay("PRIME ERROR: "+msg);
else if (y == LSLAES_COMMAND_PRIME())
state init;
}
}
state init {
state_entry() { // Now init the engine with an input vector
lslAESInitHexIV(
aesLink,
myIV,
llGetKey()
);
}
link_message(integer x, integer y, string msg, key id) {
if (!lslAESIsReply(y, id)) return;
y = lslAESGetReplyMode(y);
if (y == LSLAES_COMMAND_ERROR())
llOwnerSay("INIT ERROR: "+msg);
else if (y == LSLAES_COMMAND_INIT())
state encrypt;
}
}
state encrypt {
state_entry() { // Send our message
lslAESEncryptBase64ToBase64(
aesLink,
llStringToBase64(myMsg),
llGetOwner()
);
}
link_message(integer x, integer y, string msg, key id) {
if (!lslAESIsReply(y, id)) return;
y = lslAESGetReplyMode(y);
if (y == LSLAES_COMMAND_ERROR())
llOwnerSay("ENCRYPT ERROR: "+msg);
else if (y == LSLAES_COMMAND_ENCRYPT())
llOwnerSay("Encrypted: "+msg);
}
}</lsl>
Decryption
<lsl>$import AES.AES_Helper.lslm();
integer aesLink = LINK_THIS;
string myKey = "1234567890ABCDEF0123456789ABCDEF"; // 128-bit key in hex string myMsg = "Mdn6jGTwRPMOKTYTTdDKGm9KScz26LIz96KVOGAeMw3hpwByPfa07PDRHxRW4TIh5dmu5LlhKpTQChi="; string myIV = "89ABCDEF0123456789ABCDEF01234567";
default {
state_entry() { // Setup the engine for use
state prime;
lslAESSetup(
aesLink,
llDumpList2String(
[
LSLAES_MODE_CFB(), // CFB requires a less complex broker LSLAES_PAD_NONE(), // No padding LSLAES_PAD_SIZE(), // Pad into blocks of 512-bits 512 ], "," ),
llGetKey()
);
}
link_message(integer x, integer y, string msg, key id) {
if (!lslAESIsReply(y, id)) return;
y = lslAESGetReplyMode(y);
if (y == LSLAES_COMMAND_ERROR())
llOwnerSay("SETUP ERROR: "+msg);
else if (y == LSLAES_COMMAND_SETUP())
state prime;
}
}
state prime {
state_entry() { // First prime the engine with a key
lslAESPrimeHexKey(
aesLink,
myKey,
llGetKey()
);
}
link_message(integer x, integer y, string msg, key id) {
if (!lslAESIsReply(y, id)) return;
y = lslAESGetReplyMode(y);
if (y == LSLAES_COMMAND_ERROR())
llOwnerSay("PRIME ERROR: "+msg);
else if (y == LSLAES_COMMAND_PRIME())
state init;
}
}
state init {
state_entry() { // Now init the engine with an input vector
lslAESInitHexIV(
aesLink,
myIV,
llGetKey()
);
}
link_message(integer x, integer y, string msg, key id) {
if (!lslAESIsReply(y, id)) return;
y = lslAESGetReplyMode(y);
if (y == LSLAES_COMMAND_ERROR())
llOwnerSay("INIT ERROR: "+msg);
else if (y == LSLAES_COMMAND_INIT())
state decrypt;
}
}
state decrypt {
state_entry() { // Send our message
lslAESDecryptBase64ToBase64(
aesLink,
myMsg,
llGetOwner()
);
}
link_message(integer x, integer y, string msg, key id) {
if (!lslAESIsReply(y, id)) return;
y = lslAESGetReplyMode(y);
if (y == LSLAES_COMMAND_ERROR())
llOwnerSay("DECRYPT ERROR: "+msg);
else if (y == LSLAES_COMMAND_DECRYPT())
llOwnerSay("Decrypted: "+llBase64ToString(msg));
}
}</lsl>