AES Strong Encryption
LSL Portal | Functions | Events | Types | Operators | Constants | Flow Control | Script Library | Categorized Library | Tutorials |
AES Encryption
The AES encryption standard is a cryptographically secure and widely supported symmetric key, stream encryption scheme. It takes a strong key and applies it to an input in a series of "rounds" to a 16-byte chunk of data (the state). The resulting cipher is so thoroughly different from the original that it is considered cryptographically secure. Indeed, there are as yet no reported cases of AES encryption being successfully broken except through side channel attacks, essentially using other available data or means to get at the original (not a fault of the algorithm itself).
For more information regarding the algorithm you may view the Wikipedia page.
Implementation
Usage
The usual restrictions on open-source software apply to the following code; namely there are very few! You may use the provided code in any project you wish so long as you give credit to the script author (Haravikk Mistral) for providing this code as a base, you are also asked to please provide a link to this wiki article.
Description
This implementation is a fairly basic one, it utilises a 16-byte wide state for effective encryption for 128, 192, and 256-bit keys. You may provide larger keys, and it has been tested with keys as large as 2048-bits long, however, it is uncertain if these are much more secure than a 256-bit key due to the limited size of the state. This script accepts messages larger than 16-bytes long, and applies a segment number to them to reduce the chances of repeated segments producing the same cipher block. Data is zero-padded to ensure that it can be divided into 16-byte segments, with the last byte of the (unencrypted) data providing the number of padding bits added to the data.
This script provides methods for serialising/unserialising byte-data to and from base64 and hexadecimal alphabets. Support for additional alphabets (e.g a base 256 one) is easily added as the functions (lslAESBytesToString()
and lslAESStringToBytes()
) are designed to be flexible.
Mono-LSL Code
The following code is provided as an encryption "client". That is, you can simply send it link-messages in order to encrypt your messages. You must first of all "prime" it with a key to use for encryption/decryption, this is very fast so you may swap keys in and out as required without much penalty. Once the engine is "primed" you may encrypt/decrypt as many messages as you wish, up to a size of 1.5kb, larger messages will be refused as they risk a stack/heap collision.
Link messages are identified by the integer argument by adding a command to the base constant. Here is a summary of the messages values, and the replies you will get:
Commands
Command | Code | Type | Description |
---|---|---|---|
Prime | 33792 | Hex | Primes engine with a key transmitted as a hexadecimal string. Invalid size will return an error. |
33793 | Base64 | Primes engine with a key transmitted as a base64 string (note that base64 uses blocks of width 6-bits, only use for keys that are a factor of six (e.g 192, 768...). Invalid size will return an error. | |
Encrypt | 36864 | Hex | Encrypts a hexadecimal string, returning the cipher-text as a padded hexadecimal string |
36865 | Base64 | Encrypts a base64 strings, returning the cipher-text as a padded base64 string | |
Decrypt | 49152 | Hex | Decrypts a hexadecimal string, removing the padding added by encryption and returning the decrypted hexadecimal data |
49153 | Base64 | Decrypts a base64 string, removing the padding added by encryption and returning the decrypted base64 data |
Replies
Reply | Code | Type | Description |
---|---|---|---|
ERROR | 65536 | string | An error occurred, described by the string-data of the reply |
Prime | 66560 | void | Indicates the AES engine was primed successfully using the hex key it was given |
65560 | void | Indicates the AES engine was primed successfully using the base64 key it was given | |
Encrypt | 69632 | Hex | Contains a hexadecimal representation of the encrypted data |
69633 | Base64 | Contains a base64 representation of the encrypted data | |
Decrypt | 81920 | Hex | Contains a hexadecimal representation of the decrypted data |
81921 | Base64 | Contains a base64 representation of the decrypted data |
Code
Below is the code. It will only compile within Mono at the moment due to the memory requirements. You may edit the constants used for communication at the top. It should be noted that for speed, this implementation uses a set of 16 integers as the state, and unrolls all loops into a set of instructions for speed. Also, data is processed as a list of integers rather than bytes to conserve memory, and where relevant a bit-header is attached to the top of a list to aid with serialisation.
<lsl>integer LSLAES_COMMS_BASE = 32768; integer LSLAES_COMMS_REPLY_BASE = 65536;
integer LSLAES_COMMS_BASE64 = 1; integer LSLAES_COMMS_PRIME = 1024; integer LSLAES_COMMS_ENCRYPT = 4096; integer LSLAES_COMMS_DECRYPT = 16384;
// Defines how "wide" the range of comms values are integer LSLAES_COMMS_WIDTH = 32767;
// Refuse any data longer than this many characters integer LSLAES_MAX_SIZE = 1536;
string LSLAES_HEX_CHARS = "0123456789ABCDEF"; string LSLAES_BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
// The inverted Rijndael SBox, values in the state are substituted with these list LSLAES_SBOX_INVERTED = [
0x52096ad5, 0x3036a538, 0xbf40a39e, 0x81f3d7fb, 0x7ce33982, 0x9b2fff87, 0x348e4344, 0xc4dee9cb, 0x547b9432, 0xa6c2233d, 0xee4c950b, 0x42fac34e, 0x082ea166, 0x28d924b2, 0x765ba249, 0x6d8bd125, 0x72f8f664, 0x86689816, 0xd4a45ccc, 0x5d65b692, 0x6c704850, 0xfdedb9da, 0x5e154657, 0xa78d9d84, 0x90d8ab00, 0x8cbcd30a, 0xf7e45805, 0xb8b34506, 0xd02c1e8f, 0xca3f0f02, 0xc1afbd03, 0x01138a6b, 0x3a911141, 0x4f67dcea, 0x97f2cfce, 0xf0b4e673, 0x96ac7422, 0xe7ad3585, 0xe2f937e8, 0x1c75df6e, 0x47f11a71, 0x1d29c589, 0x6fb7620e, 0xaa18be1b, 0xfc563e4b, 0xc6d27920, 0x9adbc0fe, 0x78cd5af4, 0x1fdda833, 0x8807c731, 0xb1121059, 0x2780ec5f, 0x60517fa9, 0x19b54a0d, 0x2de57a9f, 0x93c99cef, 0xa0e03b4d, 0xae2af5b0, 0xc8ebbb3c, 0x83539961, 0x172b047e, 0xba77d626, 0xe1691463, 0x55210c7d
];
integer lslAESSBoxInvertedCached = -1; integer lslAESSBoxInvertedCache = 0;
// Treats the above list as a byte-array, retrieving the desired byte value. integer lslAESGetSBoxInvertedByte(integer n) {
integer b = n % 4; // Byte within integer n /= 4; // Integer b = 3 - b;
if (lslAESSBoxInvertedCached == n) n = lslAESSBoxInvertedCache; else { lslAESSBoxInvertedCached = n; lslAESSBoxInvertedCache = llList2Integer(LSLAES_SBOX_INVERTED, n); } return (lslAESSBoxInvertedCache >> (b << 3)) & 0xFF;
}
// The Rijndael SBox, values in the state are substituted with these list LSLAES_SBOX = [
0x637c777b, 0xf26b6fc5, 0x3001672b, 0xfed7ab76, 0xca82c97d, 0xfa5947f0, 0xadd4a2af, 0x9ca472c0, 0xb7fd9326, 0x363ff7cc, 0x34a5e5f1, 0x71d83115, 0x04c723c3, 0x1896059a, 0x071280e2, 0xeb27b275, 0x09832c1a, 0x1b6e5aa0, 0x523bd6b3, 0x29e32f84, 0x53d100ed, 0x20fcb15b, 0x6acbbe39, 0x4a4c58cf, 0xd0efaafb, 0x434d3385, 0x45f9027f, 0x503c9fa8, 0x51a3408f, 0x929d38f5, 0xbcb6da21, 0x10fff3d2, 0xcd0c13ec, 0x5f974417, 0xc4a77e3d, 0x645d1973, 0x60814fdc, 0x222a9088, 0x46eeb814, 0xde5e0bdb, 0xe0323a0a, 0x4906245c, 0xc2d3ac62, 0x9195e479, 0xe7c8376d, 0x8dd54ea9, 0x6c56f4ea, 0x657aae08, 0xba78252e, 0x1ca6b4c6, 0xe8dd741f, 0x4bbd8b8a, 0x703eb566, 0x4803f60e, 0x613557b9, 0x86c11d9e, 0xe1f89811, 0x69d98e94, 0x9b1e87e9, 0xce5528df, 0x8ca1890d, 0xbfe64268, 0x41992d0f, 0xb054bb16
];
integer lslAESSBoxCached = -1; integer lslAESSBoxCache = 0;
// Treats the above list as a byte-array, retrieving the desired byte value. integer lslAESGetSBoxByte(integer n) {
integer b = n % 4; // Byte within integer n /= 4; // Integer b = 3 - b;
if (lslAESSBoxCached == n) n = lslAESSBoxCache; else { lslAESSBoxCached = n; lslAESSBoxCache = llList2Integer(LSLAES_SBOX, n); } return (lslAESSBoxCache >> (b << 3)) & 0xFF;
}
// The round-constant word array, LSLAES_RCON[1] contains the values given // by x to the e power (i - 1) being powers of x in the field GF(28). Note // that i starts at 1, not 0. list LSLAES_RCON = [
0x8d010204, 0x08102040, 0x801b366c, 0xd8ab4d9a, 0x2f5ebc63, 0xc697356a, 0xd4b37dfa, 0xefc59139, 0x72e4d3bd, 0x61c29f25, 0x4a943366, 0xcc831d3a, 0x74e8cb8d, 0x01020408, 0x10204080, 0x1b366cd8, 0xab4d9a2f, 0x5ebc63c6, 0x97356ad4, 0xb37dfaef, 0xc5913972, 0xe4d3bd61, 0xc29f254a, 0x943366cc, 0x831d3a74, 0xe8cb8d01, 0x02040810, 0x2040801b, 0x366cd8ab, 0x4d9a2f5e, 0xbc63c697, 0x356ad4b3, 0x7dfaefc5, 0x913972e4, 0xd3bd61c2, 0x9f254a94, 0x3366cc83, 0x1d3a74e8, 0xcb8d0102, 0x04081020, 0x40801b36, 0x6cd8ab4d, 0x9a2f5ebc, 0x63c69735, 0x6ad4b37d, 0xfaefc591, 0x3972e4d3, 0xbd61c29f, 0x254a9433, 0x66cc831d, 0x3a74e8cb, 0x8d010204, 0x08102040, 0x801b366c, 0xd8ab4d9a, 0x2f5ebc63, 0xc697356a, 0xd4b37dfa, 0xefc59139, 0x72e4d3bd, 0x61c29f25, 0x4a943366, 0xcc831d3a, 0x74e8cb00
]; integer lslAESRConCached = -1; integer lslAESRConCache = 0;
// Treats the above list as a byte-array, retrieving the desired byte value. integer lslAESGetRConByte(integer n) {
integer b = n % 4; // Byte within integer n /= 4; // Integer b = 3 - b;
if (lslAESRConCached == n) n = lslAESRConCache; else { lslAESRConCached = n; lslAESRConCache = llList2Integer(LSLAES_RCON, n); } return (lslAESRConCache >> (b << 3)) & 0xFF;
}
// Used for the actual encryption, generated from Key list lslAESRoundKey = []; // The number of rounds to perform (bigger key == more rounds) integer lslAESRounds = 0;
// The following are used for the state instead of a list integer lslAESStateX0Y0 = 0; integer lslAESStateX0Y1 = 0; integer lslAESStateX0Y2 = 0; integer lslAESStateX0Y3 = 0; integer lslAESStateX1Y0 = 0; integer lslAESStateX1Y1 = 0; integer lslAESStateX1Y2 = 0; integer lslAESStateX1Y3 = 0; integer lslAESStateX2Y0 = 0; integer lslAESStateX2Y1 = 0; integer lslAESStateX2Y2 = 0; integer lslAESStateX2Y3 = 0; integer lslAESStateX3Y0 = 0; integer lslAESStateX3Y1 = 0; integer lslAESStateX3Y2 = 0; integer lslAESStateX3Y3 = 0;
//##########################################################################// // HIGH-LEVEL FUNCTIONS // //##########################################################################// // The following functions are the ones to call to encrypt/decrypt // //##########################################################################// // Decrypts a list of integers into a list of decrypted integers. Padding is // automatically removed as-per the method used by lslAESCipher. list lslAESInvertCipher(list data) {
// Preserve the bit-count header for output integer bits = llList2Integer(data, 0); data = llList2List((data = []) + data, 1, -1); integer i = 0; integer j = 0; integer l = (data != []); list output = []; while (l > 0) { // Copy some input into state variables j = llList2Integer(data, 0); lslAESStateX0Y0 = ((j >> 24) & 0xFF); lslAESStateX0Y1 = ((j >> 16) & 0xFF); lslAESStateX0Y2 = ((j >> 8) & 0xFF); lslAESStateX0Y3 = (j & 0xFF); j = llList2Integer(data, 1); lslAESStateX1Y0 = ((j >> 24) & 0xFF); lslAESStateX1Y1 = ((j >> 16) & 0xFF); lslAESStateX1Y2 = ((j >> 8) & 0xFF); lslAESStateX1Y3 = (j & 0xFF); j = llList2Integer(data, 2); lslAESStateX2Y0 = ((j >> 24) & 0xFF); lslAESStateX2Y1 = ((j >> 16) & 0xFF); lslAESStateX2Y2 = ((j >> 8) & 0xFF); lslAESStateX2Y3 = (j & 0xFF); j = llList2Integer(data, 3); lslAESStateX3Y0 = ((j >> 24) & 0xFF); lslAESStateX3Y1 = ((j >> 16) & 0xFF); lslAESStateX3Y2 = ((j >> 8) & 0xFF); lslAESStateX3Y3 = (j & 0xFF); // Add last round key before rounds begin lslAESAddRoundKey(lslAESRounds); // There will be Rounds - 1 identical rounds j = lslAESRounds - 1; while (j > 0) { lslAESInvertShiftRows(); lslAESInvertSubBytes(); lslAESAddRoundKey(j--); lslAESInvertMixColumns(); } // The last round requires no mixing lslAESInvertShiftRows(); lslAESInvertSubBytes(); lslAESAddRoundKey(0); if (l > 4) data = llList2List((data = []) + data, 4, -1); else data = []; l -= 4; // XOR with i output = (output = []) + output + [ ((lslAESStateX0Y0 ^ i) << 24) | ((lslAESStateX0Y1 ^ i) << 16) | ((lslAESStateX0Y2 ^ i) << 8) | (lslAESStateX0Y3 ^ i), ((lslAESStateX1Y0 ^ i) << 24) | ((lslAESStateX1Y1 ^ i) << 16) | ((lslAESStateX1Y2 ^ i) << 8) | (lslAESStateX1Y3 ^ i), ((lslAESStateX2Y0 ^ i) << 24) | ((lslAESStateX2Y1 ^ i) << 16) | ((lslAESStateX2Y2 ^ i) << 8) | (lslAESStateX2Y3 ^ i), ((lslAESStateX3Y0 ^ i) << 24) | ((lslAESStateX3Y1 ^ i) << 16) | ((lslAESStateX3Y2 ^ i) << 8) | (lslAESStateX3Y3 ^ i) ]; i = (++i % 256); } // Remove padding bytes and add bit-header j = llList2Integer(output, -1) & 0xFF; return [bits - j] + llList2List((output = []) + output, 0, -((j / 32) + 1));
}
// Encrypts a list of integers into a list of encrypted integers. Input is // padded using the number of required pad entries. Thus, after decryption // you will need to use the last byte of the decrypted string, to find how // many bytes in total were added as padding, so you may trim them. e.g - if // the byte is of value 12 then a total of 12 bytes were added, including the // one you read. Padding is ALWAYS added, even if the input is correctly // divisible (in which case a full-block of padding is added). list lslAESCipher(list data) {
// Preserve the bit-count header for output integer bits = llList2Integer(data, 0); data = llList2List((data = []) + data, 1, -1); // Add padding integers integer l = data != []; integer j = 128 - (bits % 128); integer t = j / 32; integer i = 1; while (i < t) data = (data = []) + data + [++i]; data = (data = []) + data + [j];
list output = [bits + j]; i = 0; l = (data != []); while (l > 0) { // Copy some input into state variables, and XOR with i j = llList2Integer(data, 0); lslAESStateX0Y0 = ((j >> 24) & 0xFF) ^ i; lslAESStateX0Y1 = ((j >> 16) & 0xFF) ^ i; lslAESStateX0Y2 = ((j >> 8) & 0xFF) ^ i; lslAESStateX0Y3 = (j & 0xFF) ^ i; j = llList2Integer(data, 1); lslAESStateX1Y0 = ((j >> 24) & 0xFF) ^ i; lslAESStateX1Y1 = ((j >> 16) & 0xFF) ^ i; lslAESStateX1Y2 = ((j >> 8) & 0xFF) ^ i; lslAESStateX1Y3 = (j & 0xFF) ^ i; j = llList2Integer(data, 2); lslAESStateX2Y0 = ((j >> 24) & 0xFF) ^ i; lslAESStateX2Y1 = ((j >> 16) & 0xFF) ^ i; lslAESStateX2Y2 = ((j >> 8) & 0xFF) ^ i; lslAESStateX2Y3 = (j & 0xFF) ^ i; j = llList2Integer(data, 3); lslAESStateX3Y0 = ((j >> 24) & 0xFF) ^ i; lslAESStateX3Y1 = ((j >> 16) & 0xFF) ^ i; lslAESStateX3Y2 = ((j >> 8) & 0xFF) ^ i; lslAESStateX3Y3 = (j & 0xFF) ^ i; // Add first round-key before starting rounds lslAESAddRoundKey(0); // There will be Rounds iterations, the first // Rounds - 1 are identical. j = 1; while (j < lslAESRounds) { lslAESSubBytes(); lslAESShiftRows(); lslAESMixColumns(); lslAESAddRoundKey(j++); } // The last column performs no mix lslAESSubBytes(); lslAESShiftRows(); lslAESAddRoundKey(lslAESRounds); if (l > 4) data = llList2List((data = []) + data, 4, -1); else data = []; l -= 4; output = (output = []) + output + [ (lslAESStateX0Y0 << 24) | (lslAESStateX0Y1 << 16) | (lslAESStateX0Y2 << 8) | lslAESStateX0Y3, (lslAESStateX1Y0 << 24) | (lslAESStateX1Y1 << 16) | (lslAESStateX1Y2 << 8) | lslAESStateX1Y3, (lslAESStateX2Y0 << 24) | (lslAESStateX2Y1 << 16) | (lslAESStateX2Y2 << 8) | lslAESStateX2Y3, (lslAESStateX3Y0 << 24) | (lslAESStateX3Y1 << 16) | (lslAESStateX3Y2 << 8) | lslAESStateX3Y3 ]; i = (++i % 256); } return (output = []) + output;
}
//##########################################################################//
// PROCESSING FUNCTIONS //
//##########################################################################//
// The following functions are used to process data that is being //
// encrypted or decrypted. //
//##########################################################################//
// XORs the value with RoundKey values
lslAESAddRoundKey(integer round) {
round = round << 2; integer t = llList2Integer(lslAESRoundKey, round); lslAESStateX0Y0 = lslAESStateX0Y0 ^ ((t >> 24) & 0xFF); lslAESStateX0Y1 = lslAESStateX0Y1 ^ ((t >> 16) & 0xFF); lslAESStateX0Y2 = lslAESStateX0Y2 ^ ((t >> 8) & 0xFF); lslAESStateX0Y3 = lslAESStateX0Y3 ^ (t & 0xFF); t = llList2Integer(lslAESRoundKey, ++round); lslAESStateX1Y0 = lslAESStateX1Y0 ^ ((t >> 24) & 0xFF); lslAESStateX1Y1 = lslAESStateX1Y1 ^ ((t >> 16) & 0xFF); lslAESStateX1Y2 = lslAESStateX1Y2 ^ ((t >> 8) & 0xFF); lslAESStateX1Y3 = lslAESStateX1Y3 ^ (t & 0xFF); t = llList2Integer(lslAESRoundKey, ++round); lslAESStateX2Y0 = lslAESStateX2Y0 ^ ((t >> 24) & 0xFF); lslAESStateX2Y1 = lslAESStateX2Y1 ^ ((t >> 16) & 0xFF); lslAESStateX2Y2 = lslAESStateX2Y2 ^ ((t >> 8) & 0xFF); lslAESStateX2Y3 = lslAESStateX2Y3 ^ (t & 0xFF); t = llList2Integer(lslAESRoundKey, ++round); lslAESStateX3Y0 = lslAESStateX3Y0 ^ ((t >> 24) & 0xFF); lslAESStateX3Y1 = lslAESStateX3Y1 ^ ((t >> 16) & 0xFF); lslAESStateX3Y2 = lslAESStateX3Y2 ^ ((t >> 8) & 0xFF); lslAESStateX3Y3 = lslAESStateX3Y3 ^ (t & 0xFF);
}
// Performs a substitution using SBox lslAESSubBytes() {
lslAESStateX0Y0 = lslAESGetSBoxByte(lslAESStateX0Y0); lslAESStateX0Y1 = lslAESGetSBoxByte(lslAESStateX0Y1); lslAESStateX0Y2 = lslAESGetSBoxByte(lslAESStateX0Y2); lslAESStateX0Y3 = lslAESGetSBoxByte(lslAESStateX0Y3); lslAESStateX1Y0 = lslAESGetSBoxByte(lslAESStateX1Y0); lslAESStateX1Y1 = lslAESGetSBoxByte(lslAESStateX1Y1); lslAESStateX1Y2 = lslAESGetSBoxByte(lslAESStateX1Y2); lslAESStateX1Y3 = lslAESGetSBoxByte(lslAESStateX1Y3); lslAESStateX2Y0 = lslAESGetSBoxByte(lslAESStateX2Y0); lslAESStateX2Y1 = lslAESGetSBoxByte(lslAESStateX2Y1); lslAESStateX2Y2 = lslAESGetSBoxByte(lslAESStateX2Y2); lslAESStateX2Y3 = lslAESGetSBoxByte(lslAESStateX2Y3); lslAESStateX3Y0 = lslAESGetSBoxByte(lslAESStateX3Y0); lslAESStateX3Y1 = lslAESGetSBoxByte(lslAESStateX3Y1); lslAESStateX3Y2 = lslAESGetSBoxByte(lslAESStateX3Y2); lslAESStateX3Y3 = lslAESGetSBoxByte(lslAESStateX3Y3);
}
// Performs a substition using SBoxInverted lslAESInvertSubBytes() {
lslAESStateX0Y0 = lslAESGetSBoxInvertedByte(lslAESStateX0Y0); lslAESStateX0Y1 = lslAESGetSBoxInvertedByte(lslAESStateX0Y1); lslAESStateX0Y2 = lslAESGetSBoxInvertedByte(lslAESStateX0Y2); lslAESStateX0Y3 = lslAESGetSBoxInvertedByte(lslAESStateX0Y3); lslAESStateX1Y0 = lslAESGetSBoxInvertedByte(lslAESStateX1Y0); lslAESStateX1Y1 = lslAESGetSBoxInvertedByte(lslAESStateX1Y1); lslAESStateX1Y2 = lslAESGetSBoxInvertedByte(lslAESStateX1Y2); lslAESStateX1Y3 = lslAESGetSBoxInvertedByte(lslAESStateX1Y3); lslAESStateX2Y0 = lslAESGetSBoxInvertedByte(lslAESStateX2Y0); lslAESStateX2Y1 = lslAESGetSBoxInvertedByte(lslAESStateX2Y1); lslAESStateX2Y2 = lslAESGetSBoxInvertedByte(lslAESStateX2Y2); lslAESStateX2Y3 = lslAESGetSBoxInvertedByte(lslAESStateX2Y3); lslAESStateX3Y0 = lslAESGetSBoxInvertedByte(lslAESStateX3Y0); lslAESStateX3Y1 = lslAESGetSBoxInvertedByte(lslAESStateX3Y1); lslAESStateX3Y2 = lslAESGetSBoxInvertedByte(lslAESStateX3Y2); lslAESStateX3Y3 = lslAESGetSBoxInvertedByte(lslAESStateX3Y3);
}
// Performs row shifts
lslAESShiftRows() {
integer t = 0; // Rotate first row 1 columns to left t = lslAESStateX1Y0; lslAESStateX1Y0 = lslAESStateX1Y1; lslAESStateX1Y1 = lslAESStateX1Y2; lslAESStateX1Y2 = lslAESStateX1Y3; lslAESStateX1Y3 = t; // Rotate second row 2 columns to left t = lslAESStateX2Y0; lslAESStateX2Y0 = lslAESStateX2Y2; lslAESStateX2Y2 = t; t = lslAESStateX2Y1; lslAESStateX2Y1 = lslAESStateX2Y3; lslAESStateX2Y3 = t; // Rotate third row 3 columns to left t = lslAESStateX3Y0; lslAESStateX3Y0 = lslAESStateX3Y3; lslAESStateX3Y3 = lslAESStateX3Y2; lslAESStateX3Y2 = lslAESStateX3Y1; lslAESStateX3Y1 = t;
}
// Undoes a set of row shifts lslAESInvertShiftRows() {
integer t = 0; // Rotate first row 1 columns to right t = lslAESStateX1Y3; lslAESStateX1Y3 = lslAESStateX1Y2; lslAESStateX1Y2 = lslAESStateX1Y1; lslAESStateX1Y1 = lslAESStateX1Y0; lslAESStateX1Y0 = t; // Rotate second row 2 columns to right t = lslAESStateX2Y0; lslAESStateX2Y0 = lslAESStateX2Y2; lslAESStateX2Y2 = t; t = lslAESStateX2Y1; lslAESStateX2Y1 = lslAESStateX2Y3; lslAESStateX2Y3 = t; // Rotate third row 3 columns to right t = lslAESStateX3Y0; lslAESStateX3Y0 = lslAESStateX3Y1; lslAESStateX3Y1 = lslAESStateX3Y2; lslAESStateX3Y2 = lslAESStateX3Y3; lslAESStateX3Y3 = t;
}
// Mixes columns of the state lslAESMixColumns() {
integer t = lslAESStateX0Y0; integer t1 = lslAESStateX0Y0 ^ lslAESStateX1Y0 ^ lslAESStateX2Y0 ^ lslAESStateX3Y0; integer t2 = lslAESXTimes(lslAESStateX0Y0 ^ lslAESStateX1Y0); lslAESStateX0Y0 = lslAESStateX0Y0 ^ t2 ^ t1; t2 = lslAESXTimes(lslAESStateX1Y0 ^ lslAESStateX2Y0); lslAESStateX1Y0 = lslAESStateX1Y0 ^ t2 ^ t1; t2 = lslAESXTimes(lslAESStateX2Y0 ^ lslAESStateX3Y0); lslAESStateX2Y0 = lslAESStateX2Y0 ^ t2 ^ t1; t2 = lslAESXTimes(lslAESStateX3Y0 ^ t); lslAESStateX3Y0 = lslAESStateX3Y0 ^ t2 ^ t1; t = lslAESStateX0Y1; t1 = lslAESStateX0Y1 ^ lslAESStateX1Y1 ^ lslAESStateX2Y1 ^ lslAESStateX3Y1; t2 = lslAESXTimes(lslAESStateX0Y1 ^ lslAESStateX1Y1); lslAESStateX0Y1 = lslAESStateX0Y1 ^ t2 ^ t1; t2 = lslAESXTimes(lslAESStateX1Y1 ^ lslAESStateX2Y1); lslAESStateX1Y1 = lslAESStateX1Y1 ^ t2 ^ t1; t2 = lslAESXTimes(lslAESStateX2Y1 ^ lslAESStateX3Y1); lslAESStateX2Y1 = lslAESStateX2Y1 ^ t2 ^ t1; t2 = lslAESXTimes(lslAESStateX3Y1 ^ t); lslAESStateX3Y1 = lslAESStateX3Y1 ^ t2 ^ t1; t = lslAESStateX0Y2; t1 = lslAESStateX0Y2 ^ lslAESStateX1Y2 ^ lslAESStateX2Y2 ^ lslAESStateX3Y2; t2 = lslAESXTimes(lslAESStateX0Y2 ^ lslAESStateX1Y2); lslAESStateX0Y2 = lslAESStateX0Y2 ^ t2 ^ t1; t2 = lslAESXTimes(lslAESStateX1Y2 ^ lslAESStateX2Y2); lslAESStateX1Y2 = lslAESStateX1Y2 ^ t2 ^ t1; t2 = lslAESXTimes(lslAESStateX2Y2 ^ lslAESStateX3Y2); lslAESStateX2Y2 = lslAESStateX2Y2 ^ t2 ^ t1; t2 = lslAESXTimes(lslAESStateX3Y2 ^ t); lslAESStateX3Y2 = lslAESStateX3Y2 ^ t2 ^ t1; t = lslAESStateX0Y3; t1 = lslAESStateX0Y3 ^ lslAESStateX1Y3 ^ lslAESStateX2Y3 ^ lslAESStateX3Y3; t2 = lslAESXTimes(lslAESStateX0Y3 ^ lslAESStateX1Y3); lslAESStateX0Y3 = lslAESStateX0Y3 ^ t2 ^ t1; t2 = lslAESXTimes(lslAESStateX1Y3 ^ lslAESStateX2Y3); lslAESStateX1Y3 = lslAESStateX1Y3 ^ t2 ^ t1; t2 = lslAESXTimes(lslAESStateX2Y3 ^ lslAESStateX3Y3); lslAESStateX2Y3 = lslAESStateX2Y3 ^ t2 ^ t1; t2 = lslAESXTimes(lslAESStateX3Y3 ^ t); lslAESStateX3Y3 = lslAESStateX3Y3 ^ t2 ^ t1;
}
// Used when column mixing integer lslAESXTimes(integer x) {
return ((x << 1) ^ (((x >> 7) & 1) * 0x1b)) & 0xFF;
}
// Used when column mixing integer lslAESMultiply(integer x, integer y) {
integer xT = lslAESXTimes(x); integer xT2 = lslAESXTimes(xT); integer xT3 = lslAESXTimes(xT2); return (((y & 1) * x) ^ (((y >> 1) & 1) * xT) ^ (((y >> 2) & 1) * xT2) ^ (((y >> 3) & 1) * xT3) ^ (((y >> 4) & 1) * lslAESXTimes(xT3))) & 0xFF;
}
// Try to understand this at your own peril! lslAESInvertMixColumns() {
integer a = lslAESStateX0Y0; integer b = lslAESStateX1Y0; integer c = lslAESStateX2Y0; integer d = lslAESStateX3Y0; lslAESStateX0Y0 = lslAESMultiply(a, 0x0e) ^ lslAESMultiply(b, 0x0b) ^ lslAESMultiply(c, 0x0d) ^ lslAESMultiply(d, 0x09); lslAESStateX1Y0 = lslAESMultiply(a, 0x09) ^ lslAESMultiply(b, 0x0e) ^ lslAESMultiply(c, 0x0b) ^ lslAESMultiply(d, 0x0d); lslAESStateX2Y0 = lslAESMultiply(a, 0x0d) ^ lslAESMultiply(b, 0x09) ^ lslAESMultiply(c, 0x0e) ^ lslAESMultiply(d, 0x0b); lslAESStateX3Y0 = lslAESMultiply(a, 0x0b) ^ lslAESMultiply(b, 0x0d) ^ lslAESMultiply(c, 0x09) ^ lslAESMultiply(d, 0x0e); a = lslAESStateX0Y1; b = lslAESStateX1Y1; c = lslAESStateX2Y1; d = lslAESStateX3Y1; lslAESStateX0Y1 = lslAESMultiply(a, 0x0e) ^ lslAESMultiply(b, 0x0b) ^ lslAESMultiply(c, 0x0d) ^ lslAESMultiply(d, 0x09); lslAESStateX1Y1 = lslAESMultiply(a, 0x09) ^ lslAESMultiply(b, 0x0e) ^ lslAESMultiply(c, 0x0b) ^ lslAESMultiply(d, 0x0d); lslAESStateX2Y1 = lslAESMultiply(a, 0x0d) ^ lslAESMultiply(b, 0x09) ^ lslAESMultiply(c, 0x0e) ^ lslAESMultiply(d, 0x0b); lslAESStateX3Y1 = lslAESMultiply(a, 0x0b) ^ lslAESMultiply(b, 0x0d) ^ lslAESMultiply(c, 0x09) ^ lslAESMultiply(d, 0x0e); a = lslAESStateX0Y2; b = lslAESStateX1Y2; c = lslAESStateX2Y2; d = lslAESStateX3Y2; lslAESStateX0Y2 = lslAESMultiply(a, 0x0e) ^ lslAESMultiply(b, 0x0b) ^ lslAESMultiply(c, 0x0d) ^ lslAESMultiply(d, 0x09); lslAESStateX1Y2 = lslAESMultiply(a, 0x09) ^ lslAESMultiply(b, 0x0e) ^ lslAESMultiply(c, 0x0b) ^ lslAESMultiply(d, 0x0d); lslAESStateX2Y2 = lslAESMultiply(a, 0x0d) ^ lslAESMultiply(b, 0x09) ^ lslAESMultiply(c, 0x0e) ^ lslAESMultiply(d, 0x0b); lslAESStateX3Y2 = lslAESMultiply(a, 0x0b) ^ lslAESMultiply(b, 0x0d) ^ lslAESMultiply(c, 0x09) ^ lslAESMultiply(d, 0x0e); a = lslAESStateX0Y3; b = lslAESStateX1Y3; c = lslAESStateX2Y3; d = lslAESStateX3Y3; lslAESStateX0Y3 = lslAESMultiply(a, 0x0e) ^ lslAESMultiply(b, 0x0b) ^ lslAESMultiply(c, 0x0d) ^ lslAESMultiply(d, 0x09); lslAESStateX1Y3 = lslAESMultiply(a, 0x09) ^ lslAESMultiply(b, 0x0e) ^ lslAESMultiply(c, 0x0b) ^ lslAESMultiply(d, 0x0d); lslAESStateX2Y3 = lslAESMultiply(a, 0x0d) ^ lslAESMultiply(b, 0x09) ^ lslAESMultiply(c, 0x0e) ^ lslAESMultiply(d, 0x0b); lslAESStateX3Y3 = lslAESMultiply(a, 0x0b) ^ lslAESMultiply(b, 0x0d) ^ lslAESMultiply(c, 0x09) ^ lslAESMultiply(d, 0x0e);
}
//##########################################################################// // ENCRYPTION SET-UP FUNCTIONS // //##########################################################################// // The following functions are used to set-up the AES encryption engine // // using the value is Key. // //##########################################################################// // Takes the key bytes provided and sets up the engine ready to encrypt or // decrypt using it. string lslAESKeyExpansion(list keyBytes) {
// Don't need the bit-count keyBytes = llList2List((keyBytes = []) + keyBytes, 1, -1); integer l = (keyBytes != []); if (l % 8) return "Invalid key size, must be a multiple of 64!"; // The first round key is the key itself lslAESRoundKey = (keyBytes = []) + keyBytes; // Calculate the number of required rounds lslAESRounds = l + 6; // All others are found from previous keys integer i = l; integer x = (l + 7) << 2; integer t = 0; integer t1 = 0; integer t2 = 0; integer t3 = 0; integer t4 = 0; while (i < x) { t = llList2Integer(lslAESRoundKey, i - 1); t1 = (t >> 24) & 0xFF; t2 = (t >> 16) & 0xFF; t3 = (t >> 8) & 0xFF; t4 = t & 0xFF; if (!(i % l)) { // Rotate temp by 1 t = t1; t1 = t2; t2 = t3; t3 = t4; t4 = t; // SubWord t1 = lslAESGetSBoxByte(t1) ^ lslAESGetRConByte(i / l); t2 = lslAESGetSBoxByte(t2); t3 = lslAESGetSBoxByte(t3); t4 = lslAESGetSBoxByte(t4); } else if ((l > 6) && ((i % l) == 4)) { // SubWord t1 = lslAESGetSBoxByte(t1); t2 = lslAESGetSBoxByte(t2); t3 = lslAESGetSBoxByte(t3); t4 = lslAESGetSBoxByte(t4); } // XOR k with four previous RoundKey values t = llList2Integer(lslAESRoundKey, i - l); t1 = ((t >> 24) & 0xFF) ^ t1; t2 = ((t >> 16) & 0xFF) ^ t2; t3 = ((t >> 8) & 0xFF) ^ t3; t4 = (t & 0xFF) ^ t4; // And add the new entries, yay! lslAESRoundKey = (lslAESRoundKey = []) + lslAESRoundKey + [(t1 << 24) | (t2 << 16) | (t3 << 8) | t4]; ++i; } return "";
}
//##########################################################################// // SERIALISATION FUNCTIONS // //##########################################################################// // The following functions are used to serialise a string into a list of // // byte data for use as a key, or to encrypt/decrypt. // //##########################################################################// // Load value as integers from a base64 string list lslAESBase64ToBytes(string base64Data) {
integer x = llSubStringIndex(base64Data, "="); if (x > 0) base64Data = llGetSubString( (base64Data = "") + base64Data, 0, --x ); return lslAESStringToBytes( (base64Data = "") + base64Data, 6, LSLAES_BASE64_CHARS );
}
// Load value as integers from a hex string list lslAESHexToBytes(string hexData) {
if (llGetSubString(hexData, 0, 1) == "0x") hexData = llGetSubString((hexData = "") + hexData, 2, -1); return lslAESStringToBytes( (hexData = "") + hexData, 4, LSLAES_HEX_CHARS );
}
// Loads a string as a list of integers, using the given bit <width> and // appropriate <alphabet> into Key list lslAESStringToBytes(string s, integer width, string alphabet) {
integer l = llStringLength(s); list n = [l * width]; // Add bit-length integer bitbuf = 0; integer adjust = 32; integer i = 0; integer val; while (i < l) { val = llSubStringIndex(alphabet, llGetSubString(s, i, i)); if (val < 0) { s = ""; return (n = []) + ["Invalid character at index "+(string)i]; }
if ((adjust -= width) <= 0) { bitbuf = bitbuf | (val >> -adjust); n = (n = []) + n + [bitbuf]; adjust += 32; if (adjust < 32) bitbuf = (val << adjust); else bitbuf = 0; } else bitbuf = bitbuf | (val << adjust);
++i; } s = ""; if (adjust < 32) return (n = []) + n + [bitbuf]; return (n = []) + n;
}
// Returns a hex string representing integers b, preceeded with "0x". string lslAESBytesToHex(list b) {
return "0x" + lslAESBytesToString((b = []) + b, 4, LSLAES_HEX_CHARS);
}
// Returns a base64 string representing integers b, with padding equals signs string lslAESBytesToBase64(list b) {
string s = lslAESBytesToString((b = []) + b, 6, LSLAES_BASE64_CHARS); integer l = llStringLength(s) % 3; if (l) { if (l == 1) return (s = "") + s + "=="; return (s = "") + s + "="; } return (s = "") + s;
}
// Outputs integer data from b as characters representing width bits taken from // alphabet string lslAESBytesToString(list b, integer width, string alphabet) {
integer bits = llList2Integer(b, 0); integer i = 1; integer l = b != []; integer buf = 0; integer bitcnt = 1; integer bitcpy = 0; integer bitbuf = 0; string s = ""; do { if (!(--bitcnt)) { if (i >= l) jump lslAESBytesToStringBreak; buf = llList2Integer(b, i); if ((++i) >= l) bitcnt = bits % 32; if (!bitcnt) bitcnt = 32; } ++bitcpy; if (buf & 0x80000000) bitbuf = bitbuf | (1 << (width - bitcpy)); buf = buf << 1; if (bitcpy == width) { s = (s = "") + s + llGetSubString(alphabet, bitbuf, bitbuf); bitbuf = bitcpy = 0; } } while (TRUE); @lslAESBytesToStringBreak; b = []; if (bitcpy > 0) return (s = "") + s + llGetSubString(alphabet, bitbuf, bitbuf); return (s = "") + s;
}
default {
link_message(integer x, integer y, string msg, key id) { // Is the message in our range? if ((y >= LSLAES_COMMS_BASE) && (y < (LSLAES_COMMS_BASE + LSLAES_COMMS_WIDTH))) { y -= LSLAES_COMMS_BASE; // Is input too large? if (llStringLength(msg) > LSLAES_MAX_SIZE) { llMessageLinked( x, LSLAES_COMMS_REPLY_BASE, (msg = "") + "Input too large!", id ); return; } // Load the data list l = []; if (y & LSLAES_COMMS_BASE64) l = lslAESBase64ToBytes((msg = "") + msg); else l = lslAESHexToBytes((msg = "") + msg); // Was the value parsed successfully? if (llGetListEntryType(l, 0) != TYPE_INTEGER) { llMessageLinked( x, LSLAES_COMMS_REPLY_BASE, llList2String((l = []) + l, 0), id ); return; } if (y & LSLAES_COMMS_PRIME) { msg = lslAESKeyExpansion((l = []) + l); if (msg == "") y += LSLAES_COMMS_REPLY_BASE; else y = LSLAES_COMMS_REPLY_BASE; llMessageLinked(x, y, (msg = "") + msg, id); } else { if (y & LSLAES_COMMS_ENCRYPT) l = lslAESCipher((l = []) + l); else if (y & LSLAES_COMMS_DECRYPT) l = lslAESInvertCipher((l = []) + l); else return; y += LSLAES_COMMS_REPLY_BASE; if (y & LSLAES_COMMS_BASE64) msg = lslAESBytesToBase64((l = []) + l); else msg = lslAESBytesToHex((l = []) + l); llMessageLinked(x, y, (msg = "") + msg, id); } } }
}</lsl>
PHP Code
An equivalent PHP solution is pending, and will be pretty much an exact port of the LSL code.