|
|
(11 intermediate revisions by the same user not shown) |
Line 1: |
Line 1: |
| {{LSL Header}} | | {{LSL Header}} |
| = BETA! = | | = Description = |
| The code/project on this page is currently considered to be in beta, and should not be used in a production environment until proper testing has been completed
| |
| | |
| = AES Encryption =
| |
| The AES encryption standard is a cryptographically secure and widely supported symmetric key, stream encryption scheme. It takes a strong key and applies it to an input in a series of "rounds" to a 16-byte chunk of data (the state). The resulting cipher is so thoroughly different from the original that it is considered cryptographically secure. Indeed, there are as yet no reported cases of AES encryption being successfully broken except through side channel attacks, essentially using other available data or means to get at the original (not a fault of the algorithm itself). | | 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 [http://en.wikipedia.org/wiki/Advanced_Encryption_Standard Wikipedia page]. | | For more information regarding the algorithm you may view the [http://en.wikipedia.org/wiki/Advanced_Encryption_Standard Wikipedia page]. |
|
| |
|
| = Implementation = | | = Using AES = |
| == Usage == | | == HTTP communication == |
| 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 ([[User:Haravikk Mistral|Haravikk Mistral]]) for providing this code as a base, you are also asked to please provide a link to this wiki article.
| | AES is not intended for securing connections with a web-site on its own. If you wish to send data to a web-site then you should investigate using [http://en.wikipedia.org/wiki/Https HTTPS] with a valid [http://en.wikipedia.org/wiki/SSL_certificate SSL Certificate], as this will allow you to verify the authenticity of a web-site, and will provide greater, faster security than anything implemented in [[LSL]] is currently capable of. |
| | |
| == Description ==
| |
| This implementation is a fairly basic one, it utilises a 16-byte wide state for effective encryption for 128, 192, and 256-bit keys '''only'''. This script accepts messages larger than 16-bytes long, and applies a segment number to them to reduce the chances of repeated segments producing the same cipher block. Data is zero-padded to ensure that it can be divided into 16-byte segments, with the first 32-bit word of the (unencrypted) data describing how long the original message data was, allowing padding to be culled quickly. Padding is handled at the start in this way due to inaccuracies that can be introduced when using base64, as it does not always correctly align onto 128-bit blocks, but it is still the easiest way to manage string-data.
| |
| | |
| This script provides methods for serialising/de-serialising 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 (<code>lslAESBytesToString()</code> and <code>lslAESStringToBytes()</code>) are designed to be flexible.
| |
| | |
| The implementation is based on the freely available open-source C/C++ implementation at [http://www.hoozi.com/Articles/AESEncryption.htm Hoozi]. The implementation therefore is mostly a straight port of the AES algorithm itself, with the aforementioned adjustments to make it more secure.
| |
| | |
| == Future Development ==
| |
| Future development will be mostly performance-centric, aiming to further increase speed and reduce the memory footprint (allowing support for larger messages). This will be ongoing. Investigations will also be made into improving compatibility with widely available AES implementations in use.
| |
| | |
| Originally planned was to modify the algorithm for use for the [http://en.wikipedia.org/wiki/WHIRLPOOL WHIRLPOOL] hashing function, however this has higher memory requirements (precomputed data is approximately 64 times larger!). However, a hashing function using the already implemented algorithm may be adapted easily.
| |
| | |
| An exact port to PHP is also planned, for full compatibility with the LSL version.
| |
| | |
| == Thanks ==
| |
| Thanks to [[User:Strife Onizuka|Strife Onizuka]] for work in optimising/testing the initial implementation.
| |
| | |
| == Mono-LSL Code ==
| |
| The following code is provided as an encryption "client". That is, you can simply send it link-messages in order to encrypt your messages. You must first of all "prime" it with a key to use for encryption/decryption, this is very fast so you may swap keys in and out as required without much penalty. Once the engine is "primed" you may encrypt/decrypt as many messages as you wish, up to a size of 1.5kb, larger messages will be refused as they risk a stack/heap collision.
| |
| | |
| Link messages are identified by the integer argument by combining values. As these are not obvious for many users, a simple library of helper functions is also included beneath the main script.
| |
| | |
| === 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>// 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):
| |
| // 0xFFMMIOuu
| |
| // Where the letters are:
| |
| // F Filter, used to quickly determine if a message is for us.
| |
| // M Mode; 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 = 0xFF000000;
| |
| // This mask allows the mask byte to be retrieved quickly
| |
| integer LSLAES_MODE_MASK = 0x00FF0000;
| |
| // This mask allows the input type to be retrieved quickly
| |
| integer LSLAES_INPUT_TYPE_MASK = 0x0000F000;
| |
| // This mask allows the output type to be retireved quickly
| |
| integer LSLAES_OUTPUT_TYPE_MASK = 0x00000F00;
| |
| // This mask allows the variable to retrieved quickly
| |
| integer LSLAES_VARIABLE_MASK = 0x000000FF;
| |
| // How many bits right variable must be shifted
| |
| integer LSLAES_VARIABLE_SHIFT = 0;
| |
| | |
| // A request
| |
| integer LSLAES_FILTER_REQUEST = 0x81000000;
| |
| // A reply
| |
| integer LSLAES_FILTER_REPLY = 0x82000000;
| |
| | |
| // An error occurred
| |
| integer LSLAES_MODE_ERROR = 0x00000000;
| |
| // Prime engine with key
| |
| integer LSLAES_MODE_PRIME = 0x00010000;
| |
| // Encrypt message using expanded key
| |
| integer LSLAES_MODE_ENCRYPT = 0x00020000;
| |
| // Decrypt message using expanded key
| |
| integer LSLAES_MODE_DECRYPT = 0x00030000;
| |
| // Hash message using expanded key, variable is used to determine hash-
| |
| // length in 32-bit words. i.e - if var == 4 then length = 4 * 32 = 128.
| |
| // Max size is LSLAES_HASH_SIZE
| |
| integer LSLAES_MODE_HASH = 0x00040000;
| |
| | |
| // Input type is hex
| |
| integer LSLAES_INPUT_HEX = 0x00000000;
| |
| // Input type is base64
| |
| integer LSLAES_INPUT_BASE64 = 0x00001000;
| |
| | |
| // Output type is hex
| |
| integer LSLAES_OUTPUT_HEX = 0x00000000;
| |
| // Output type is base64
| |
| integer LSLAES_OUTPUT_BASE64 = 0x00000100;
| |
| | |
| // Maximum hash-size
| |
| integer LSLAES_HASH_SIZE = 512;
| |
|
| |
| // Refuse any data longer than this many characters
| |
| integer LSLAES_MAX_SIZE = 1536;
| |
|
| |
| string LSLAES_HEX_CHARS = "0123456789ABCDEF";
| |
| string LSLAES_BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
| |
|
| |
| // The inverted Rijndael SBox, values in the state are substituted with these
| |
| list LSLAES_SBOX_INVERTED = [
| |
| 0x52096ad5, 0x3036a538, 0xbf40a39e, 0x81f3d7fb, 0x7ce33982,
| |
| 0x9b2fff87, 0x348e4344, 0xc4dee9cb, 0x547b9432, 0xa6c2233d,
| |
| 0xee4c950b, 0x42fac34e, 0x082ea166, 0x28d924b2, 0x765ba249,
| |
| 0x6d8bd125, 0x72f8f664, 0x86689816, 0xd4a45ccc, 0x5d65b692,
| |
| 0x6c704850, 0xfdedb9da, 0x5e154657, 0xa78d9d84, 0x90d8ab00,
| |
| 0x8cbcd30a, 0xf7e45805, 0xb8b34506, 0xd02c1e8f, 0xca3f0f02,
| |
| 0xc1afbd03, 0x01138a6b, 0x3a911141, 0x4f67dcea, 0x97f2cfce,
| |
| 0xf0b4e673, 0x96ac7422, 0xe7ad3585, 0xe2f937e8, 0x1c75df6e,
| |
| 0x47f11a71, 0x1d29c589, 0x6fb7620e, 0xaa18be1b, 0xfc563e4b,
| |
| 0xc6d27920, 0x9adbc0fe, 0x78cd5af4, 0x1fdda833, 0x8807c731,
| |
| 0xb1121059, 0x2780ec5f, 0x60517fa9, 0x19b54a0d, 0x2de57a9f,
| |
| 0x93c99cef, 0xa0e03b4d, 0xae2af5b0, 0xc8ebbb3c, 0x83539961,
| |
| 0x172b047e, 0xba77d626, 0xe1691463, 0x55210c7d
| |
| ];
| |
|
| |
| integer lslAESSBoxInvertedCached = -1;
| |
| integer lslAESSBoxInvertedCache = 0;
| |
|
| |
| // Treats the above list as a byte-array, retrieving the desired byte value.
| |
| integer lslAESGetSBoxInvertedByte(integer n) {
| |
| integer b = n % 4; // Byte within integer
| |
| n /= 4; // Integer
| |
| b = 3 - b;
| |
|
| |
| if (lslAESSBoxInvertedCached == n) n = lslAESSBoxInvertedCache;
| |
| else {
| |
| lslAESSBoxInvertedCached = n;
| |
| lslAESSBoxInvertedCache = llList2Integer(LSLAES_SBOX_INVERTED, n);
| |
| }
| |
| return (lslAESSBoxInvertedCache >> (b << 3)) & 0xFF;
| |
| }
| |
|
| |
| // The Rijndael SBox, values in the state are substituted with these
| |
| list LSLAES_SBOX = [
| |
| 0x637c777b, 0xf26b6fc5, 0x3001672b, 0xfed7ab76, 0xca82c97d,
| |
| 0xfa5947f0, 0xadd4a2af, 0x9ca472c0, 0xb7fd9326, 0x363ff7cc,
| |
| 0x34a5e5f1, 0x71d83115, 0x04c723c3, 0x1896059a, 0x071280e2,
| |
| 0xeb27b275, 0x09832c1a, 0x1b6e5aa0, 0x523bd6b3, 0x29e32f84,
| |
| 0x53d100ed, 0x20fcb15b, 0x6acbbe39, 0x4a4c58cf, 0xd0efaafb,
| |
| 0x434d3385, 0x45f9027f, 0x503c9fa8, 0x51a3408f, 0x929d38f5,
| |
| 0xbcb6da21, 0x10fff3d2, 0xcd0c13ec, 0x5f974417, 0xc4a77e3d,
| |
| 0x645d1973, 0x60814fdc, 0x222a9088, 0x46eeb814, 0xde5e0bdb,
| |
| 0xe0323a0a, 0x4906245c, 0xc2d3ac62, 0x9195e479, 0xe7c8376d,
| |
| 0x8dd54ea9, 0x6c56f4ea, 0x657aae08, 0xba78252e, 0x1ca6b4c6,
| |
| 0xe8dd741f, 0x4bbd8b8a, 0x703eb566, 0x4803f60e, 0x613557b9,
| |
| 0x86c11d9e, 0xe1f89811, 0x69d98e94, 0x9b1e87e9, 0xce5528df,
| |
| 0x8ca1890d, 0xbfe64268, 0x41992d0f, 0xb054bb16
| |
| ]; | |
|
| |
| integer lslAESSBoxCached = -1;
| |
| integer lslAESSBoxCache = 0;
| |
|
| |
| // Treats the above list as a byte-array, retrieving the desired byte value.
| |
| integer lslAESGetSBoxByte(integer n) {
| |
| integer b = n % 4; // Byte within integer
| |
| n /= 4; // Integer
| |
| b = 3 - b;
| |
|
| |
| if (lslAESSBoxCached == n) n = lslAESSBoxCache;
| |
| else {
| |
| lslAESSBoxCached = n;
| |
| lslAESSBoxCache = llList2Integer(LSLAES_SBOX, n);
| |
| }
| |
| return (lslAESSBoxCache >> (b << 3)) & 0xFF;
| |
| }
| |
|
| |
| // Used for the actual encryption, generated from Key
| |
| list lslAESRoundKey = [];
| |
| // The number of rounds to perform (bigger key == more rounds)
| |
| integer lslAESRounds = 0;
| |
| // Used for padding to a multiple of key-length
| |
| integer lslAESPadSize = 128;
| |
|
| |
| // The following are used for the state instead of a list
| |
| integer lslAESStateX0Y0 = 0;
| |
| integer lslAESStateX0Y1 = 0;
| |
| integer lslAESStateX0Y2 = 0;
| |
| integer lslAESStateX0Y3 = 0;
| |
| integer lslAESStateX1Y0 = 0;
| |
| integer lslAESStateX1Y1 = 0;
| |
| integer lslAESStateX1Y2 = 0;
| |
| integer lslAESStateX1Y3 = 0;
| |
| integer lslAESStateX2Y0 = 0;
| |
| integer lslAESStateX2Y1 = 0;
| |
| integer lslAESStateX2Y2 = 0;
| |
| integer lslAESStateX2Y3 = 0;
| |
| integer lslAESStateX3Y0 = 0;
| |
| integer lslAESStateX3Y1 = 0;
| |
| integer lslAESStateX3Y2 = 0;
| |
| integer lslAESStateX3Y3 = 0;
| |
|
| |
| //##########################################################################//
| |
| // HIGH-LEVEL FUNCTIONS //
| |
| //##########################################################################//
| |
| // The following functions are the ones to call to encrypt/decrypt //
| |
| //##########################################################################//
| |
| // Calculate a length-bit hash using the currently expanded key. Length MUST
| |
| // be a multiple of 32.
| |
| list lslAESHash(list data, integer length) {
| |
| // First pad data to a length * 2
| |
| integer bits = llList2Integer(data, 0);
| |
| integer i = 0;
| |
| integer j = length << 1;
| |
|
| |
| // If too short, then add values
| |
| if (bits < j) {
| |
| integer a = (j - bits) / 32;
| |
| do data = (data = []) + data + llList2Integer(data, i);
| |
| while ((++i) < a);
| |
| bits = j;
| |
| }
| |
|
| |
| // Calculate a dirty 32-bit hash to give greater variation in results
| |
| integer c = 0; i = 0; integer l = data != [];
| |
| do c = c ^ llList2Integer(data, i);
| |
| while ((++i) < bits);
| |
|
| |
| // Irreversibly reduce to length by XORing
| |
| list out = [length];
| |
| integer b = c;
| |
|
| |
| // Use only the dirty-hash for 32-bit hashes
| |
| if (length == 32) out += c;
| |
| else {
| |
| integer w = (j = (bits / length)) - (i = 1);
| |
| do {
| |
| b = b ^ llList2Integer(data, i);
| |
| if ((i % j) == w) {
| |
| out = (out = []) + out + b;
| |
| b = c;
| |
| }
| |
| } while ((++i) < l);
| |
| }
| |
|
| |
| // Add any extra data if necessary and calculate hash
| |
| if ((out != []) < (length / 32))
| |
| out = lslAESCipher((out = data = []) + out + b);
| |
| else
| |
| out = lslAESCipher((out = data = []) + out);
| |
|
| |
| // Reduce to size
| |
| return length + llList2List(out, 1, (length / 32) + ((out = []) != out));
| |
| }
| |
| | |
| // Decrypts a list of integers into a list of decrypted integers. Padding is
| |
| // automatically removed as-per the method used by lslAESCipher.
| |
| list lslAESInvertCipher(list data) {
| |
| // Preserve the bit-count header for output
| |
| integer bits = llList2Integer(data, 0);
| |
| data = llList2List((data = []) + data, 1, -1);
| |
|
| |
| integer i = 0; integer j = 0;
| |
| integer l = (data != []);
| |
| list output = [];
| |
| while (l > 0) {
| |
| // Copy some input into state variables
| |
| j = llList2Integer(data, 0);
| |
| lslAESStateX0Y0 = ((j >> 24) & 0xFF);
| |
| lslAESStateX0Y1 = ((j >> 16) & 0xFF);
| |
| lslAESStateX0Y2 = ((j >> 8) & 0xFF);
| |
| lslAESStateX0Y3 = (j & 0xFF);
| |
| j = llList2Integer(data, 1);
| |
| lslAESStateX1Y0 = ((j >> 24) & 0xFF);
| |
| lslAESStateX1Y1 = ((j >> 16) & 0xFF);
| |
| lslAESStateX1Y2 = ((j >> 8) & 0xFF);
| |
| lslAESStateX1Y3 = (j & 0xFF);
| |
| j = llList2Integer(data, 2);
| |
| lslAESStateX2Y0 = ((j >> 24) & 0xFF);
| |
| lslAESStateX2Y1 = ((j >> 16) & 0xFF);
| |
| lslAESStateX2Y2 = ((j >> 8) & 0xFF);
| |
| lslAESStateX2Y3 = (j & 0xFF);
| |
| j = llList2Integer(data, 3);
| |
| lslAESStateX3Y0 = ((j >> 24) & 0xFF);
| |
| lslAESStateX3Y1 = ((j >> 16) & 0xFF);
| |
| lslAESStateX3Y2 = ((j >> 8) & 0xFF);
| |
| lslAESStateX3Y3 = (j & 0xFF);
| |
|
| |
| // Add last round key before rounds begin
| |
| lslAESAddRoundKey(lslAESRounds);
| |
|
| |
| // There will be Rounds - 1 identical rounds
| |
| j = lslAESRounds - 1;
| |
| while (j > 0) {
| |
| lslAESInvertShiftRows();
| |
| lslAESInvertSubBytes();
| |
| lslAESAddRoundKey(j--);
| |
| lslAESInvertMixColumns();
| |
| }
| |
|
| |
| // The last round requires no mixing
| |
| lslAESInvertShiftRows();
| |
| lslAESInvertSubBytes();
| |
| lslAESAddRoundKey(0);
| |
|
| |
| if (l > 4)
| |
| data = llList2List((data = []) + data, 4, -1);
| |
| else data = [];
| |
| l -= 4;
| |
|
| |
| // XOR with i
| |
| output = (output = []) + output + [
| |
| ((lslAESStateX0Y0 ^ i) << 24) | ((lslAESStateX0Y1 ^ i) << 16) |
| |
| ((lslAESStateX0Y2 ^ i) << 8) | (lslAESStateX0Y3 ^ i),
| |
| ((lslAESStateX1Y0 ^ i) << 24) | ((lslAESStateX1Y1 ^ i) << 16) |
| |
| ((lslAESStateX1Y2 ^ i) << 8) | (lslAESStateX1Y3 ^ i),
| |
| ((lslAESStateX2Y0 ^ i) << 24) | ((lslAESStateX2Y1 ^ i) << 16) |
| |
| ((lslAESStateX2Y2 ^ i) << 8) | (lslAESStateX2Y3 ^ i),
| |
| ((lslAESStateX3Y0 ^ i) << 24) | ((lslAESStateX3Y1 ^ i) << 16) |
| |
| ((lslAESStateX3Y2 ^ i) << 8) | (lslAESStateX3Y3 ^ i)
| |
| ];
| |
| i = (++i % 256);
| |
| }
| |
|
| |
| // Get bit length
| |
| bits = j = llList2Integer(output, 0);
| |
| if ((j < 0) || (j > 8192)) return (output = []) + [0];
| |
|
| |
| // Discard padding and return
| |
| j /= 32;
| |
| if (bits % 32) ++j;
| |
|
| |
| return llList2List((output = []) + output, 0, j);
| |
| }
| |
|
| |
| // Encrypts a list of integers into a list of encrypted integers.
| |
| // Input is padded to be a multiple of lslAESPadSize (key-length rounded
| |
| // up to nearest 128 multiple) using zero-bytes, with the first 32-bit word
| |
| // of ciphertext indicating how many bits long the data is. If the input is
| |
| // already a multiple then a full lslAESPadSize bits are added.
| |
| // In addition, blocks are XORed with a counter to reduce patterns, this
| |
| // counter is numbered such that all the bytes of the first block are
| |
| // XORed with 0, the bytes of the second block are XORed with 1, etc., the
| |
| // counter resets to zero after 255
| |
| list lslAESCipher(list data) {
| |
| // Preserve the bit-count header for output
| |
| integer bits = llList2Integer(data, 0);
| |
| data = llDeleteSubList((data = []) + data, 0, 0);
| |
|
| |
| // Add padding integers
| |
| integer l = data != [];
| |
| integer j = lslAESPadSize - (bits % lslAESPadSize);
| |
|
| |
| integer t = j / 32;
| |
| while ((--t) >= 1) data = (data = []) + data + [0];
| |
| data = (data = []) + [bits] + data;
| |
|
| |
| l = (data != []);
| |
| list output = [l << 5]; // Total bits
| |
|
| |
| integer i = 0;
| |
| while (l > 0) {
| |
| // Copy some input into state variables, and XOR with i
| |
| j = llList2Integer(data, 0);
| |
| lslAESStateX0Y0 = ((j >> 24) & 0xFF) ^ i;
| |
| lslAESStateX0Y1 = ((j >> 16) & 0xFF) ^ i;
| |
| lslAESStateX0Y2 = ((j >> 8) & 0xFF) ^ i;
| |
| lslAESStateX0Y3 = (j & 0xFF) ^ i;
| |
| j = llList2Integer(data, 1);
| |
| lslAESStateX1Y0 = ((j >> 24) & 0xFF) ^ i;
| |
| lslAESStateX1Y1 = ((j >> 16) & 0xFF) ^ i;
| |
| lslAESStateX1Y2 = ((j >> 8) & 0xFF) ^ i;
| |
| lslAESStateX1Y3 = (j & 0xFF) ^ i;
| |
| j = llList2Integer(data, 2);
| |
| lslAESStateX2Y0 = ((j >> 24) & 0xFF) ^ i;
| |
| lslAESStateX2Y1 = ((j >> 16) & 0xFF) ^ i;
| |
| lslAESStateX2Y2 = ((j >> 8) & 0xFF) ^ i;
| |
| lslAESStateX2Y3 = (j & 0xFF) ^ i;
| |
| j = llList2Integer(data, 3);
| |
| lslAESStateX3Y0 = ((j >> 24) & 0xFF) ^ i;
| |
| lslAESStateX3Y1 = ((j >> 16) & 0xFF) ^ i;
| |
| lslAESStateX3Y2 = ((j >> 8) & 0xFF) ^ i;
| |
| lslAESStateX3Y3 = (j & 0xFF) ^ i;
| |
|
| |
| // Add first round-key before starting rounds
| |
| lslAESAddRoundKey(0);
| |
|
| |
| // There will be Rounds iterations, the first
| |
| // Rounds - 1 are identical.
| |
| j = 1;
| |
| while (j < lslAESRounds) {
| |
| lslAESSubBytes();
| |
| lslAESShiftRows();
| |
| lslAESMixColumns();
| |
| lslAESAddRoundKey(j++);
| |
| }
| |
|
| |
| // The last column performs no mix
| |
| lslAESSubBytes();
| |
| lslAESShiftRows();
| |
| lslAESAddRoundKey(lslAESRounds);
| |
|
| |
| if (l > 4)
| |
| data = llList2List((data = []) + data, 4, -1);
| |
| else data = [];
| |
| l -= 4;
| |
|
| |
| output = (output = []) + output + [
| |
| (lslAESStateX0Y0 << 24) | (lslAESStateX0Y1 << 16) |
| |
| (lslAESStateX0Y2 << 8) | lslAESStateX0Y3,
| |
| (lslAESStateX1Y0 << 24) | (lslAESStateX1Y1 << 16) |
| |
| (lslAESStateX1Y2 << 8) | lslAESStateX1Y3,
| |
| (lslAESStateX2Y0 << 24) | (lslAESStateX2Y1 << 16) |
| |
| (lslAESStateX2Y2 << 8) | lslAESStateX2Y3,
| |
| (lslAESStateX3Y0 << 24) | (lslAESStateX3Y1 << 16) |
| |
| (lslAESStateX3Y2 << 8) | lslAESStateX3Y3
| |
| ];
| |
| i = (++i % 256);
| |
| }
| |
|
| |
| return (output = []) + output;
| |
| }
| |
|
| |
|
| |
| //##########################################################################//
| |
| // PROCESSING FUNCTIONS //
| |
| //##########################################################################//
| |
| // The following functions are used to process data that is being //
| |
| // encrypted or decrypted. //
| |
| //##########################################################################//
| |
| // XORs the value with RoundKey values
| |
| lslAESAddRoundKey(integer round) {
| |
| round = round << 2;
| |
|
| |
| integer t = llList2Integer(lslAESRoundKey, round);
| |
| lslAESStateX0Y0 = lslAESStateX0Y0 ^ ((t >> 24) & 0xFF);
| |
| lslAESStateX0Y1 = lslAESStateX0Y1 ^ ((t >> 16) & 0xFF);
| |
| lslAESStateX0Y2 = lslAESStateX0Y2 ^ ((t >> 8) & 0xFF);
| |
| lslAESStateX0Y3 = lslAESStateX0Y3 ^ (t & 0xFF);
| |
|
| |
| t = llList2Integer(lslAESRoundKey, ++round);
| |
| lslAESStateX1Y0 = lslAESStateX1Y0 ^ ((t >> 24) & 0xFF);
| |
| lslAESStateX1Y1 = lslAESStateX1Y1 ^ ((t >> 16) & 0xFF);
| |
| lslAESStateX1Y2 = lslAESStateX1Y2 ^ ((t >> 8) & 0xFF);
| |
| lslAESStateX1Y3 = lslAESStateX1Y3 ^ (t & 0xFF);
| |
|
| |
| t = llList2Integer(lslAESRoundKey, ++round);
| |
| lslAESStateX2Y0 = lslAESStateX2Y0 ^ ((t >> 24) & 0xFF);
| |
| lslAESStateX2Y1 = lslAESStateX2Y1 ^ ((t >> 16) & 0xFF);
| |
| lslAESStateX2Y2 = lslAESStateX2Y2 ^ ((t >> 8) & 0xFF);
| |
| lslAESStateX2Y3 = lslAESStateX2Y3 ^ (t & 0xFF);
| |
|
| |
| t = llList2Integer(lslAESRoundKey, ++round);
| |
| lslAESStateX3Y0 = lslAESStateX3Y0 ^ ((t >> 24) & 0xFF);
| |
| lslAESStateX3Y1 = lslAESStateX3Y1 ^ ((t >> 16) & 0xFF);
| |
| lslAESStateX3Y2 = lslAESStateX3Y2 ^ ((t >> 8) & 0xFF);
| |
| lslAESStateX3Y3 = lslAESStateX3Y3 ^ (t & 0xFF);
| |
| }
| |
|
| |
| // Performs a substitution using SBox
| |
| lslAESSubBytes() {
| |
| lslAESStateX0Y0 = lslAESGetSBoxByte(lslAESStateX0Y0);
| |
| lslAESStateX0Y1 = lslAESGetSBoxByte(lslAESStateX0Y1);
| |
| lslAESStateX0Y2 = lslAESGetSBoxByte(lslAESStateX0Y2);
| |
| lslAESStateX0Y3 = lslAESGetSBoxByte(lslAESStateX0Y3);
| |
| lslAESStateX1Y0 = lslAESGetSBoxByte(lslAESStateX1Y0);
| |
| lslAESStateX1Y1 = lslAESGetSBoxByte(lslAESStateX1Y1);
| |
| lslAESStateX1Y2 = lslAESGetSBoxByte(lslAESStateX1Y2);
| |
| lslAESStateX1Y3 = lslAESGetSBoxByte(lslAESStateX1Y3);
| |
| lslAESStateX2Y0 = lslAESGetSBoxByte(lslAESStateX2Y0);
| |
| lslAESStateX2Y1 = lslAESGetSBoxByte(lslAESStateX2Y1);
| |
| lslAESStateX2Y2 = lslAESGetSBoxByte(lslAESStateX2Y2);
| |
| lslAESStateX2Y3 = lslAESGetSBoxByte(lslAESStateX2Y3);
| |
| lslAESStateX3Y0 = lslAESGetSBoxByte(lslAESStateX3Y0);
| |
| lslAESStateX3Y1 = lslAESGetSBoxByte(lslAESStateX3Y1);
| |
| lslAESStateX3Y2 = lslAESGetSBoxByte(lslAESStateX3Y2);
| |
| lslAESStateX3Y3 = lslAESGetSBoxByte(lslAESStateX3Y3);
| |
| }
| |
|
| |
| // Performs a substition using SBoxInverted
| |
| lslAESInvertSubBytes() {
| |
| lslAESStateX0Y0 = lslAESGetSBoxInvertedByte(lslAESStateX0Y0);
| |
| lslAESStateX0Y1 = lslAESGetSBoxInvertedByte(lslAESStateX0Y1);
| |
| lslAESStateX0Y2 = lslAESGetSBoxInvertedByte(lslAESStateX0Y2);
| |
| lslAESStateX0Y3 = lslAESGetSBoxInvertedByte(lslAESStateX0Y3);
| |
| lslAESStateX1Y0 = lslAESGetSBoxInvertedByte(lslAESStateX1Y0);
| |
| lslAESStateX1Y1 = lslAESGetSBoxInvertedByte(lslAESStateX1Y1);
| |
| lslAESStateX1Y2 = lslAESGetSBoxInvertedByte(lslAESStateX1Y2);
| |
| lslAESStateX1Y3 = lslAESGetSBoxInvertedByte(lslAESStateX1Y3);
| |
| lslAESStateX2Y0 = lslAESGetSBoxInvertedByte(lslAESStateX2Y0);
| |
| lslAESStateX2Y1 = lslAESGetSBoxInvertedByte(lslAESStateX2Y1);
| |
| lslAESStateX2Y2 = lslAESGetSBoxInvertedByte(lslAESStateX2Y2);
| |
| lslAESStateX2Y3 = lslAESGetSBoxInvertedByte(lslAESStateX2Y3);
| |
| lslAESStateX3Y0 = lslAESGetSBoxInvertedByte(lslAESStateX3Y0);
| |
| lslAESStateX3Y1 = lslAESGetSBoxInvertedByte(lslAESStateX3Y1);
| |
| lslAESStateX3Y2 = lslAESGetSBoxInvertedByte(lslAESStateX3Y2);
| |
| lslAESStateX3Y3 = lslAESGetSBoxInvertedByte(lslAESStateX3Y3);
| |
| }
| |
|
| |
|
| |
| // Performs row shifts
| |
| lslAESShiftRows() {
| |
| integer t = 0;
| |
|
| |
| // Rotate first row 1 columns to left
| |
| t = lslAESStateX1Y0;
| |
| lslAESStateX1Y0 = lslAESStateX1Y1;
| |
| lslAESStateX1Y1 = lslAESStateX1Y2;
| |
| lslAESStateX1Y2 = lslAESStateX1Y3;
| |
| lslAESStateX1Y3 = t;
| |
|
| |
| // Rotate second row 2 columns to left
| |
| t = lslAESStateX2Y0;
| |
| lslAESStateX2Y0 = lslAESStateX2Y2;
| |
| lslAESStateX2Y2 = t;
| |
|
| |
| t = lslAESStateX2Y1;
| |
| lslAESStateX2Y1 = lslAESStateX2Y3;
| |
| lslAESStateX2Y3 = t;
| |
|
| |
| // Rotate third row 3 columns to left
| |
| t = lslAESStateX3Y0;
| |
| lslAESStateX3Y0 = lslAESStateX3Y3;
| |
| lslAESStateX3Y3 = lslAESStateX3Y2;
| |
| lslAESStateX3Y2 = lslAESStateX3Y1;
| |
| lslAESStateX3Y1 = t;
| |
| }
| |
|
| |
| // Undoes a set of row shifts
| |
| lslAESInvertShiftRows() {
| |
| integer t = 0;
| |
|
| |
| // Rotate first row 1 columns to right
| |
| t = lslAESStateX1Y3;
| |
| lslAESStateX1Y3 = lslAESStateX1Y2;
| |
| lslAESStateX1Y2 = lslAESStateX1Y1;
| |
| lslAESStateX1Y1 = lslAESStateX1Y0;
| |
| lslAESStateX1Y0 = t;
| |
|
| |
| // Rotate second row 2 columns to right
| |
| t = lslAESStateX2Y0;
| |
| lslAESStateX2Y0 = lslAESStateX2Y2;
| |
| lslAESStateX2Y2 = t;
| |
|
| |
| t = lslAESStateX2Y1;
| |
| lslAESStateX2Y1 = lslAESStateX2Y3;
| |
| lslAESStateX2Y3 = t;
| |
|
| |
| // Rotate third row 3 columns to right
| |
| t = lslAESStateX3Y0;
| |
| lslAESStateX3Y0 = lslAESStateX3Y1;
| |
| lslAESStateX3Y1 = lslAESStateX3Y2;
| |
| lslAESStateX3Y2 = lslAESStateX3Y3;
| |
| lslAESStateX3Y3 = t;
| |
| }
| |
|
| |
| // Mixes columns of the state
| |
| lslAESMixColumns() {
| |
| integer t = lslAESStateX0Y0;
| |
| integer t1 = lslAESStateX0Y0 ^ lslAESStateX1Y0 ^
| |
| lslAESStateX2Y0 ^ lslAESStateX3Y0;
| |
|
| |
| integer t2 = lslAESXTimes(lslAESStateX0Y0 ^ lslAESStateX1Y0);
| |
| lslAESStateX0Y0 = lslAESStateX0Y0 ^ t2 ^ t1;
| |
|
| |
| t2 = lslAESXTimes(lslAESStateX1Y0 ^ lslAESStateX2Y0);
| |
| lslAESStateX1Y0 = lslAESStateX1Y0 ^ t2 ^ t1;
| |
|
| |
| t2 = lslAESXTimes(lslAESStateX2Y0 ^ lslAESStateX3Y0);
| |
| lslAESStateX2Y0 = lslAESStateX2Y0 ^ t2 ^ t1;
| |
|
| |
| t2 = lslAESXTimes(lslAESStateX3Y0 ^ t);
| |
| lslAESStateX3Y0 = lslAESStateX3Y0 ^ t2 ^ t1;
| |
|
| |
| t = lslAESStateX0Y1;
| |
| t1 = lslAESStateX0Y1 ^ lslAESStateX1Y1 ^ lslAESStateX2Y1 ^ lslAESStateX3Y1;
| |
|
| |
| t2 = lslAESXTimes(lslAESStateX0Y1 ^ lslAESStateX1Y1);
| |
| lslAESStateX0Y1 = lslAESStateX0Y1 ^ t2 ^ t1;
| |
|
| |
| t2 = lslAESXTimes(lslAESStateX1Y1 ^ lslAESStateX2Y1);
| |
| lslAESStateX1Y1 = lslAESStateX1Y1 ^ t2 ^ t1;
| |
|
| |
| t2 = lslAESXTimes(lslAESStateX2Y1 ^ lslAESStateX3Y1);
| |
| lslAESStateX2Y1 = lslAESStateX2Y1 ^ t2 ^ t1;
| |
|
| |
| t2 = lslAESXTimes(lslAESStateX3Y1 ^ t);
| |
| lslAESStateX3Y1 = lslAESStateX3Y1 ^ t2 ^ t1;
| |
|
| |
| t = lslAESStateX0Y2;
| |
| t1 = lslAESStateX0Y2 ^ lslAESStateX1Y2 ^ lslAESStateX2Y2 ^ lslAESStateX3Y2;
| |
|
| |
| t2 = lslAESXTimes(lslAESStateX0Y2 ^ lslAESStateX1Y2);
| |
| lslAESStateX0Y2 = lslAESStateX0Y2 ^ t2 ^ t1;
| |
|
| |
| t2 = lslAESXTimes(lslAESStateX1Y2 ^ lslAESStateX2Y2);
| |
| lslAESStateX1Y2 = lslAESStateX1Y2 ^ t2 ^ t1;
| |
|
| |
| t2 = lslAESXTimes(lslAESStateX2Y2 ^ lslAESStateX3Y2);
| |
| lslAESStateX2Y2 = lslAESStateX2Y2 ^ t2 ^ t1;
| |
|
| |
| t2 = lslAESXTimes(lslAESStateX3Y2 ^ t);
| |
| lslAESStateX3Y2 = lslAESStateX3Y2 ^ t2 ^ t1;
| |
|
| |
| t = lslAESStateX0Y3;
| |
| t1 = lslAESStateX0Y3 ^ lslAESStateX1Y3 ^ lslAESStateX2Y3 ^ lslAESStateX3Y3;
| |
|
| |
| t2 = lslAESXTimes(lslAESStateX0Y3 ^ lslAESStateX1Y3);
| |
| lslAESStateX0Y3 = lslAESStateX0Y3 ^ t2 ^ t1;
| |
|
| |
| t2 = lslAESXTimes(lslAESStateX1Y3 ^ lslAESStateX2Y3);
| |
| lslAESStateX1Y3 = lslAESStateX1Y3 ^ t2 ^ t1;
| |
|
| |
| t2 = lslAESXTimes(lslAESStateX2Y3 ^ lslAESStateX3Y3);
| |
| lslAESStateX2Y3 = lslAESStateX2Y3 ^ t2 ^ t1;
| |
|
| |
| t2 = lslAESXTimes(lslAESStateX3Y3 ^ t);
| |
| lslAESStateX3Y3 = lslAESStateX3Y3 ^ t2 ^ t1;
| |
| }
| |
|
| |
| // Used when column mixing
| |
| integer lslAESXTimes(integer x) {
| |
| return ((x << 1) ^ (((x >> 7) & 1) * 0x1b)) & 0xFF;
| |
| }
| |
|
| |
| // Used when column mixing
| |
| integer lslAESMultiply(integer x, integer y) {
| |
| integer xT = lslAESXTimes(x);
| |
| integer xT2 = lslAESXTimes(xT);
| |
| integer xT3 = lslAESXTimes(xT2);
| |
|
| |
| return (((y & 1) * x) ^ (((y >> 1) & 1) * xT) ^
| |
| (((y >> 2) & 1) * xT2) ^ (((y >> 3) & 1) * xT3) ^
| |
| (((y >> 4) & 1) * lslAESXTimes(xT3))) & 0xFF;
| |
| }
| |
|
| |
| // Try to understand this at your own peril!
| |
| lslAESInvertMixColumns() {
| |
| integer a = lslAESStateX0Y0;
| |
| integer b = lslAESStateX1Y0;
| |
| integer c = lslAESStateX2Y0;
| |
| integer d = lslAESStateX3Y0;
| |
|
| |
| lslAESStateX0Y0 = lslAESMultiply(a, 0x0e) ^ lslAESMultiply(b, 0x0b) ^
| |
| lslAESMultiply(c, 0x0d) ^ lslAESMultiply(d, 0x09);
| |
| lslAESStateX1Y0 = lslAESMultiply(a, 0x09) ^ lslAESMultiply(b, 0x0e) ^
| |
| lslAESMultiply(c, 0x0b) ^ lslAESMultiply(d, 0x0d);
| |
| lslAESStateX2Y0 = lslAESMultiply(a, 0x0d) ^ lslAESMultiply(b, 0x09) ^
| |
| lslAESMultiply(c, 0x0e) ^ lslAESMultiply(d, 0x0b);
| |
| lslAESStateX3Y0 = lslAESMultiply(a, 0x0b) ^ lslAESMultiply(b, 0x0d) ^
| |
| lslAESMultiply(c, 0x09) ^ lslAESMultiply(d, 0x0e);
| |
|
| |
| a = lslAESStateX0Y1;
| |
| b = lslAESStateX1Y1;
| |
| c = lslAESStateX2Y1;
| |
| d = lslAESStateX3Y1;
| |
|
| |
| lslAESStateX0Y1 = lslAESMultiply(a, 0x0e) ^ lslAESMultiply(b, 0x0b) ^
| |
| lslAESMultiply(c, 0x0d) ^ lslAESMultiply(d, 0x09);
| |
| lslAESStateX1Y1 = lslAESMultiply(a, 0x09) ^ lslAESMultiply(b, 0x0e) ^
| |
| lslAESMultiply(c, 0x0b) ^ lslAESMultiply(d, 0x0d);
| |
| lslAESStateX2Y1 = lslAESMultiply(a, 0x0d) ^ lslAESMultiply(b, 0x09) ^
| |
| lslAESMultiply(c, 0x0e) ^ lslAESMultiply(d, 0x0b);
| |
| lslAESStateX3Y1 = lslAESMultiply(a, 0x0b) ^ lslAESMultiply(b, 0x0d) ^
| |
| lslAESMultiply(c, 0x09) ^ lslAESMultiply(d, 0x0e);
| |
|
| |
| a = lslAESStateX0Y2;
| |
| b = lslAESStateX1Y2;
| |
| c = lslAESStateX2Y2;
| |
| d = lslAESStateX3Y2;
| |
|
| |
| lslAESStateX0Y2 = lslAESMultiply(a, 0x0e) ^ lslAESMultiply(b, 0x0b) ^
| |
| lslAESMultiply(c, 0x0d) ^ lslAESMultiply(d, 0x09);
| |
| lslAESStateX1Y2 = lslAESMultiply(a, 0x09) ^ lslAESMultiply(b, 0x0e) ^
| |
| lslAESMultiply(c, 0x0b) ^ lslAESMultiply(d, 0x0d);
| |
| lslAESStateX2Y2 = lslAESMultiply(a, 0x0d) ^ lslAESMultiply(b, 0x09) ^
| |
| lslAESMultiply(c, 0x0e) ^ lslAESMultiply(d, 0x0b);
| |
| lslAESStateX3Y2 = lslAESMultiply(a, 0x0b) ^ lslAESMultiply(b, 0x0d) ^
| |
| lslAESMultiply(c, 0x09) ^ lslAESMultiply(d, 0x0e);
| |
|
| |
| a = lslAESStateX0Y3;
| |
| b = lslAESStateX1Y3;
| |
| c = lslAESStateX2Y3;
| |
| d = lslAESStateX3Y3;
| |
|
| |
| lslAESStateX0Y3 = lslAESMultiply(a, 0x0e) ^ lslAESMultiply(b, 0x0b) ^
| |
| lslAESMultiply(c, 0x0d) ^ lslAESMultiply(d, 0x09);
| |
| lslAESStateX1Y3 = lslAESMultiply(a, 0x09) ^ lslAESMultiply(b, 0x0e) ^
| |
| lslAESMultiply(c, 0x0b) ^ lslAESMultiply(d, 0x0d);
| |
| lslAESStateX2Y3 = lslAESMultiply(a, 0x0d) ^ lslAESMultiply(b, 0x09) ^
| |
| lslAESMultiply(c, 0x0e) ^ lslAESMultiply(d, 0x0b);
| |
| lslAESStateX3Y3 = lslAESMultiply(a, 0x0b) ^ lslAESMultiply(b, 0x0d) ^
| |
| lslAESMultiply(c, 0x09) ^ lslAESMultiply(d, 0x0e);
| |
| }
| |
|
| |
| //##########################################################################//
| |
| // ENCRYPTION SET-UP FUNCTIONS //
| |
| //##########################################################################//
| |
| // The following functions are used to set-up the AES encryption engine //
| |
| // using the value is Key. //
| |
| //##########################################################################//
| |
| // Takes the key bytes provided and sets up the engine ready to encrypt or
| |
| // decrypt using it.
| |
| 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
| |
|
| |
| // Calculate padding-size
| |
| lslAESPadSize = len << 5;
| |
| integer i = lslAESPadSize % 128;
| |
| if (i) lslAESPadSize += 128 - i;
| |
|
| |
| // Check that we are within reasonable limits
| |
| if ((len < 4) || (len > 8)) {
| |
| lslAESRoundKey = [];
| |
| return ["Invalid key size; must be 128, 192, or 256 bits!"];
| |
| }
| |
|
| |
| // Calculate the number of required rounds
| |
| lslAESRounds = len + 6;
| |
|
| |
| // All others are found from previous keys
| |
| i = 0;
| |
| integer x = (len * 3) + 28;
| |
|
| |
| integer t = llList2Integer(lslAESRoundKey, -1);
| |
|
| |
| do {
| |
| if (!(i % len)) {
| |
| // Rotate by 1, SubWord and Nudge
| |
| // [0x01020408, 0x01020408, 0x1b366cd8]
| |
| t = ((lslAESGetSBoxByte((t >> 16) & 0xFF) ^
| |
| (0x000D8080 >> (7 ^ (i / len)))) << 24) |
| |
| (lslAESGetSBoxByte((t >> 8) & 0xFF) << 16) |
| |
| (lslAESGetSBoxByte((t ) & 0xFF) << 8) |
| |
| lslAESGetSBoxByte((t >> 24) & 0xFF);
| |
| } else if ((len > 6) && (i % len) == 4) {
| |
| // SubWord
| |
| t = (lslAESGetSBoxByte((t >> 24) & 0xFF) << 24) |
| |
| (lslAESGetSBoxByte((t >> 16) & 0xFF) << 16) |
| |
| (lslAESGetSBoxByte((t >> 8) & 0xFF) << 8) |
| |
| (lslAESGetSBoxByte((t ) & 0xFF) );
| |
| }
| |
|
| |
| // XOR k with four previous RoundKey values And add the
| |
| // new entries, yay!
| |
| lslAESRoundKey = (lslAESRoundKey = []) + lslAESRoundKey +
| |
| (t = (t ^ llList2Integer(lslAESRoundKey, i)));
| |
| } while ((i = -~i) < x);
| |
|
| |
| // On success no error message is returned
| |
| return [0];
| |
| }
| |
|
| |
| //##########################################################################//
| |
| // SERIALISATION FUNCTIONS //
| |
| //##########################################################################//
| |
| // The following functions are used to serialise a string into a list of //
| |
| // byte data for use as a key, or to encrypt/decrypt. //
| |
| //##########################################################################//
| |
| // Load value as integers from a base64 string
| |
| list lslAESBase64ToBytes(string base64Data) {
| |
| integer x = llSubStringIndex(base64Data, "=");
| |
| if (x > 0)
| |
| base64Data = llGetSubString(
| |
| (base64Data = "") + base64Data,
| |
| 0,
| |
| --x
| |
| );
| |
| return lslAESStringToBytes(
| |
| (base64Data = "") + base64Data,
| |
| 6,
| |
| LSLAES_BASE64_CHARS
| |
| );
| |
| }
| |
|
| |
| // Load value as integers from a hex string
| |
| list lslAESHexToBytes(string hexData) {
| |
| if (llGetSubString(hexData, 0, 1) == "0x")
| |
| hexData = llGetSubString((hexData = "") + hexData, 2, -1);
| |
| return lslAESStringToBytes(
| |
| (hexData = "") + hexData,
| |
| 4,
| |
| LSLAES_HEX_CHARS
| |
| );
| |
| }
| |
|
| |
| // Loads a string as a list of integers, using the given bit <width> and
| |
| // appropriate <alphabet> into Key
| |
| list lslAESStringToBytes(string s, integer width, string alphabet) {
| |
| integer l = llStringLength(s);
| |
|
| |
| list n = [l * width]; // Add bit-length
| |
| integer bitbuf = 0;
| |
| integer adjust = 32;
| |
|
| |
| integer i = 0;
| |
| integer val;
| |
| while (i < l) {
| |
| val = llSubStringIndex(alphabet, llGetSubString(s, i, i));
| |
| if (val < 0) {
| |
| s = "";
| |
| return (n = []) +
| |
| ["Invalid character at index "+(string)i];
| |
| }
| |
|
| |
| if ((adjust -= width) <= 0) {
| |
| bitbuf = bitbuf | (val >> -adjust);
| |
| n = (n = []) + n + [bitbuf];
| |
|
| |
| adjust += 32;
| |
| if (adjust < 32) bitbuf = (val << adjust);
| |
| else bitbuf = 0;
| |
| } else bitbuf = bitbuf | (val << adjust);
| |
|
| |
| ++i;
| |
| }
| |
|
| |
| s = "";
| |
| if (adjust < 32)
| |
| return (n = []) + n + [bitbuf];
| |
| return (n = []) + n;
| |
| }
| |
|
| |
| // Returns a hex string representing integers b, preceeded with "0x".
| |
| string lslAESBytesToHex(list b) {
| |
| return "0x" + lslAESBytesToString((b = []) + b, 4, LSLAES_HEX_CHARS);
| |
| }
| |
|
| |
| // Returns a base64 string representing integers b, with padding equals signs
| |
| string lslAESBytesToBase64(list b) {
| |
| string s = lslAESBytesToString((b = []) + b, 6, LSLAES_BASE64_CHARS);
| |
| integer l = llStringLength(s) % 3;
| |
| if (l) {
| |
| if (l == 1) return (s = "") + s + "==";
| |
| return (s = "") + s + "=";
| |
| }
| |
| return (s = "") + s;
| |
| }
| |
|
| |
| // Outputs integer data from b as characters representing width bits taken from
| |
| // alphabet
| |
| string lslAESBytesToString(list b, integer width, string alphabet) {
| |
| integer bits = llList2Integer(b, 0);
| |
|
| |
| integer i = 0;
| |
| integer mask = ~(-1 << width);
| |
| integer shift = 32 - width;
| |
|
| |
| integer available = 0;
| |
| integer prev = 0;
| |
| integer buf;
| |
| integer extra;
| |
| integer value;
| |
|
| |
| string s = "";
| |
|
| |
| @lslAESBytesToStringLoop;
| |
| if((bits -= 32) > -32) {
| |
| available += 32 + (bits * (0 > bits));
| |
| buf = llList2Integer(b, ++i);
| |
| if (available >= width) {
| |
| if (prev) {
| |
| s = (s = "") + s +
| |
| llGetSubString(
| |
| alphabet,
| |
| value = (
| |
| extra |
| |
| (
| |
| (buf >> (shift + prev)) &
| |
| ~(-1 << (width - prev))
| |
| )
| |
| ),
| |
| value
| |
| );
| |
| buf = buf << (width - prev);
| |
| available -= width;
| |
| }
| |
| while(available >= width) {
| |
| s = (s = "") + s +
| |
| llGetSubString(
| |
| alphabet,
| |
| value = ((buf >> shift) & mask),
| |
| value
| |
| );
| |
| buf = buf << width;
| |
| available -= width;
| |
| }
| |
| if (prev = available) // Update prev
| |
| extra = (buf >> shift) & mask;
| |
| jump lslAESBytesToStringLoop;
| |
| }
| |
| }
| |
| if(available) {
| |
| mask = -1 << (width - prev);
| |
| return (s = "") + s +
| |
| llGetSubString(
| |
| alphabet,
| |
| value = ((extra & mask) |
| |
| (
| |
| (buf >> (shift + prev)) &
| |
| ((-1 << (width - available)) ^ mask))
| |
| ),
| |
| value
| |
| );
| |
| }
| |
| return (s = "") + s;
| |
| }
| |
| | |
| error(integer link, string str, key id) {
| |
| llMessageLinked(
| |
| link,
| |
| LSLAES_FILTER_REPLY | LSLAES_MODE_ERROR,
| |
| str,
| |
| id
| |
| );
| |
| }
| |
| | |
| default {
| |
| 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;
| |
| }
| |
|
| |
| // What type of data do we have?
| |
| integer type = y & LSLAES_INPUT_TYPE_MASK;
| |
| list data = [];
| |
| if (type == LSLAES_INPUT_HEX)
| |
| data = lslAESHexToBytes((msg = "") + msg);
| |
| else if (type == LSLAES_INPUT_BASE64)
| |
| data = lslAESBase64ToBytes((msg = "") + msg);
| |
| 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_MODE_MASK;
| |
| if (type == LSLAES_MODE_PRIME)
| |
| data = lslAESKeyExpansion((data = []) + data);
| |
| else if (type == LSLAES_MODE_ENCRYPT)
| |
| data = lslAESCipher((data = []) + data);
| |
| else if (type == LSLAES_MODE_DECRYPT)
| |
| data = lslAESInvertCipher((data = []) + data);
| |
| else if (type == LSLAES_MODE_HASH) {
| |
| // Get length of hash as 32-bit multiple
| |
| integer l = ((y & LSLAES_VARIABLE_MASK) >>
| |
| LSLAES_VARIABLE_SHIFT) << 5;
| |
| if (l > LSLAES_HASH_SIZE)
| |
| data = [
| |
| "Max hash-size is " +
| |
| (string)LSLAES_HASH_SIZE +
| |
| " bits"
| |
| ];
| |
| else if (l <= 0) l = LSLAES_HASH_SIZE;
| |
|
| |
| data = lslAESHash((data = []) + data, l);
| |
| } 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_MODE_PRIME) {
| |
| output = y & LSLAES_OUTPUT_TYPE_MASK;
| |
| if (output == LSLAES_OUTPUT_HEX) {
| |
| msg = lslAESBytesToHex((data = []) + data);
| |
| output = LSLAES_INPUT_HEX;
| |
| } else if (output == LSLAES_OUTPUT_BASE64) {
| |
| msg = lslAESBytesToBase64((data = []) + data);
| |
| output = LSLAES_INPUT_BASE64;
| |
| } else {
| |
| error(x, "Invalid output type", id);
| |
| return;
| |
| }
| |
| }
| |
|
| |
| // Construct reply
| |
| llMessageLinked(
| |
| x,
| |
| LSLAES_FILTER_REPLY | type | output,
| |
| (msg = "") + msg,
| |
| id
| |
| );
| |
| }
| |
| }
| |
| }</lsl>
| |
| | |
| === Helper functions ===
| |
| The following LSL code contains the constants you need in your scripts, along with a load of helper-functions you may use to easily use AES encryption. You do not need to copy all of the functions/variables, only the ones you need.
| |
| | |
| <lsl>// 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):
| |
| // 0xFFMMIOuu
| |
| // Where the letters are:
| |
| // F Filter, used to quickly determine if a message is for us.
| |
| // M Mode; 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.
| |
| //
| |
| // These variables MUST be identical to the ones in the AES engine!
| |
| | |
| // This mask allows the filter byte to be retrieved quickly
| |
| integer LSLAES_FILTER_MASK = 0xFF000000;
| |
| // This mask allows the mask byte to be retrieved quickly
| |
| integer LSLAES_MODE_MASK = 0x00FF0000;
| |
| // This mask allows the input type to be retrieved quickly
| |
| integer LSLAES_INPUT_TYPE_MASK = 0x0000F000;
| |
| // This mask allows the output type to be retireved quickly
| |
| integer LSLAES_OUTPUT_TYPE_MASK = 0x00000F00;
| |
| // This mask allows the variable to retrieved quickly
| |
| integer LSLAES_VARIABLE_MASK = 0x000000FF;
| |
| // How many bits right variable must be shifted
| |
| integer LSLAES_VARIABLE_SHIFT = 0;
| |
| | |
| // A request
| |
| integer LSLAES_FILTER_REQUEST = 0x81000000;
| |
| // A reply
| |
| integer LSLAES_FILTER_REPLY = 0x82000000;
| |
| | |
| // An error occurred
| |
| integer LSLAES_MODE_ERROR = 0x00000000;
| |
| // Prime engine with key
| |
| integer LSLAES_MODE_PRIME = 0x00010000;
| |
| // Encrypt message using expanded key
| |
| integer LSLAES_MODE_ENCRYPT = 0x00020000;
| |
| // Decrypt message using expanded key
| |
| integer LSLAES_MODE_DECRYPT = 0x00030000;
| |
| // Hash message using expanded key, variable is used to determine hash-
| |
| // length in 32-bit words. i.e - if var == 4 then length = 4 * 32 = 128.
| |
| // Max size is LSLAES_HASH_SIZE
| |
| integer LSLAES_MODE_HASH = 0x00040000;
| |
| | |
| // Input type is hex
| |
| integer LSLAES_INPUT_HEX = 0x00000000;
| |
| // Input type is base64
| |
| integer LSLAES_INPUT_BASE64 = 0x00001000;
| |
| | |
| // Output type is hex
| |
| integer LSLAES_OUTPUT_HEX = 0x00000000;
| |
| // Output type is base64
| |
| integer LSLAES_OUTPUT_BASE64 = 0x00000100;
| |
| | |
| // The following extra variables are used to track our messages
| |
| key requestID = NULL_KEY;
| |
| | |
| // 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.
| |
| lslAESPrimeHexKey(integer targetLink, string aesKey, key id) {
| |
| llMessageLinked(
| |
| targetLink,
| |
| LSLAES_FILTER_REQUEST | LSLAES_MODE_PRIME | LSLAES_INPUT_HEX,
| |
| (aesKey = "") + aesKey,
| |
| requestID = id
| |
| );
| |
| }
| |
| | |
| // Sends hexadecimal data and gets encrypted hexadecimal data back
| |
| lslAESEncryptHexToHex(integer targetLink, string hexData, key id) {
| |
| llMessageLinked(
| |
| targetLink,
| |
| LSLAES_FILTER_REQUEST | LSLAES_MODE_ENCRYPT |
| |
| LSLAES_INPUT_HEX | LSLAES_OUTPUT_HEX,
| |
| (hexData = "") + hexData,
| |
| requestID = id
| |
| );
| |
| }
| |
| | |
| // Sends hexadecimal data and gets encrypted base64 data back
| |
| lslAESEncryptHexToBase64(integer targetLink, string hexData, key id) {
| |
| llMessageLinked(
| |
| targetLink,
| |
| LSLAES_FILTER_REQUEST | LSLAES_MODE_ENCRYPT |
| |
| LSLAES_INPUT_HEX | LSLAES_OUTPUT_BASE64,
| |
| (hexData = "") + hexData,
| |
| requestID = id
| |
| );
| |
| }
| |
| | |
| // Send base64 data and gets encrypted hexadecimal data back
| |
| lslAESEncryptBase64ToHex(integer targetLink, string b64Data, key id) {
| |
| llMessageLinked(
| |
| targetLink,
| |
| LSLAES_FILTER_REQUEST | LSLAES_MODE_ENCRYPT |
| |
| LSLAES_INPUT_BASE64 | LSLAES_OUTPUT_HEX,
| |
| (b64Data = "") + b64Data,
| |
| requestID = id
| |
| );
| |
| }
| |
| | |
| // Send base64 data and gets encrypted hexadecimal data back
| |
| lslAESEncryptBase64ToBase64(integer targetLink, string b64Data, key id) {
| |
| llMessageLinked(
| |
| targetLink,
| |
| LSLAES_FILTER_REQUEST | LSLAES_MODE_ENCRYPT |
| |
| LSLAES_INPUT_BASE64 | LSLAES_OUTPUT_BASE64,
| |
| (b64Data = "") + b64Data,
| |
| requestID = id
| |
| );
| |
| }
| |
| | |
| // Sends hexadecimal data and gets decrypted hexadecimal data back
| |
| lslAESDecryptHexToHex(integer targetLink, string hexData, key id) {
| |
| llMessageLinked(
| |
| targetLink,
| |
| LSLAES_FILTER_REQUEST | LSLAES_MODE_DECRYPT |
| |
| LSLAES_INPUT_HEX | LSLAES_OUTPUT_HEX,
| |
| (hexData = "") + hexData,
| |
| requestID = id
| |
| );
| |
| }
| |
| | |
| // Sends hexadecimal data and gets decrypted base64 data back
| |
| lslAESDecryptHexToBase64(integer targetLink, string hexData, key id) {
| |
| llMessageLinked(
| |
| targetLink,
| |
| LSLAES_FILTER_REQUEST | LSLAES_MODE_DECRYPT |
| |
| LSLAES_INPUT_HEX | LSLAES_OUTPUT_BASE64,
| |
| (hexData = "") + hexData,
| |
| requestID = id
| |
| );
| |
| }
| |
| | |
| // Send base64 data and gets decrypted hexadecimal data back
| |
| lslAESDecryptBase64ToHex(integer targetLink, string b64Data, key id) {
| |
| llMessageLinked(
| |
| targetLink,
| |
| LSLAES_FILTER_REQUEST | LSLAES_MODE_DECRYPT |
| |
| LSLAES_INPUT_BASE64 | LSLAES_OUTPUT_HEX,
| |
| (b64Data = "") + b64Data,
| |
| requestID = id
| |
| );
| |
| }
| |
| | |
| // Send base64 data and gets decrypted hexadecimal data back
| |
| lslAESDecryptBase64ToBase64(integer targetLink, string b64Data, key id) {
| |
| llMessageLinked(
| |
| targetLink,
| |
| LSLAES_FILTER_REQUEST | LSLAES_MODE_DECRYPT |
| |
| LSLAES_INPUT_BASE64 | LSLAES_OUTPUT_BASE64,
| |
| (b64Data = "") + b64Data,
| |
| requestID = id
| |
| );
| |
| }
| |
| | |
| // Sends hexadecimal data and gets a length-bit hexadecimal hash back,
| |
| // where length is a multiple of 32-bits.
| |
| lslAESHashHexToHex(integer targetLink, string hexData, integer length, key id) {
| |
| llMessageLinked(
| |
| targetLink,
| |
| LSLAES_FILTER_REQUEST | LSLAES_MODE_HASH |
| |
| LSLAES_INPUT_HEX | LSLAES_OUTPUT_HEX | (length >> 5),
| |
| (hexData = "") + hexData,
| |
| requestID = id
| |
| );
| |
| }
| |
| | |
| // Sends hexadecimal data and gets a length-bit base64 hash back,
| |
| // where length is a multiple of 32-bits.
| |
| lslAESHashHexToBase64(integer targetLink, string hexData, integer length, key id) {
| |
| llMessageLinked(
| |
| targetLink,
| |
| LSLAES_FILTER_REQUEST | LSLAES_MODE_HASH |
| |
| LSLAES_INPUT_HEX | LSLAES_OUTPUT_BASE64 | (length >> 5),
| |
| (hexData = "") + hexData,
| |
| requestID = id
| |
| );
| |
| }
| |
| | |
| // Sends base64 data and gets a length-bit hexadecimal hash back,
| |
| // where length is a multiple of 32-bits.
| |
| lslAESHashBase64ToHex(integer targetLink, string b64Data, integer length, key id) {
| |
| llMessageLinked(
| |
| targetLink,
| |
| LSLAES_FILTER_REQUEST | LSLAES_MODE_HASH |
| |
| LSLAES_INPUT_BASE64 | LSLAES_OUTPUT_HEX | (length >> 5),
| |
| (b64Data = "") + b64Data,
| |
| requestID = id
| |
| );
| |
| }
| |
| | |
| // Sends base64 data and gets a length-bit hexadecimal hash back,
| |
| // where length is a multiple of 32-bits.
| |
| lslAESHashBase64ToBase64(integer targetLink, string b64Data, integer length, key id) {
| |
| llMessageLinked(
| |
| targetLink,
| |
| LSLAES_FILTER_REQUEST | LSLAES_MODE_HASH |
| |
| LSLAES_INPUT_BASE64 | LSLAES_OUTPUT_BASE64 | (length >> 5),
| |
| (b64Data = "") + b64Data,
| |
| requestID = id
| |
| );
| |
| }
| |
| | |
| // Tests to see if a message is a reply or not (TRUE/FALSE)
| |
| 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_MODE_* constants
| |
| integer lslAESGetReplyMode(integer int) {
| |
| return (int & LSLAES_MODE_MASK);
| |
| }
| |
| | |
| // Grabs the data type of this reply. Should be one of the LSLAES_INPUT_*
| |
| // constants.
| |
| integer lslAESGetReplyDataType(integer int) {
| |
| return (int & LSLAES_INPUT_TYPE_MASK);
| |
| }</lsl>
| |
| | |
| === Example use-cases ===
| |
| The following code-snippets contain basic example scripts which demonstrate how to use the AES encryption engine. You may edit these to better fit your code, or use the logic presented to implement it differently.
| |
| | |
| ==== Encrypt a message ====
| |
| This snippet takes a key and a message store in global variables and encrypts the message using the key. You should NOT use the same key represented, and should avoid using the same key all the time where possible, however, secure key generation is not an aspect of this library.
| |
| | |
| <lsl>string myKey = "56789ABCDEF0123456789ABCDEF01234"; // 128-bit key in hex
| |
| string message = "Hello world! I am a wonderful message in need of encryption!";
| |
|
| |
| ... // Add required helper functions and constants here.
| |
| | |
| default {
| |
| state_entry() { // First prime the engine with a key
| |
| lslAESPrimeHexKey(
| |
| LINK_THIS,
| |
| myKey,
| |
| llGetKey()
| |
| );
| |
| }
| |
|
| |
| link_message(integer x, integer y, string msg, key id) {
| |
| if (!lslAESIsReply(y, id)) return;
| |
|
| |
| y = lslAESGetReplyMode(y);
| |
| if (y == LSLAES_MODE_ERROR)
| |
| llOwnerSay("ERROR: "+msg);
| |
| else state encrypt;
| |
| }
| |
| }
| |
|
| |
| state encrypt {
| |
| state_entry() { // Send our message
| |
| lslAESEncryptBase64ToBase64(
| |
| LINK_THIS,
| |
| llStringToBase64(message),
| |
| llGetOwner()
| |
| );
| |
| }
| |
|
| |
| link_message(integer x, integer y, string msg, key id) {
| |
| if (!lslAESIsReply(y, id)) return;
| |
|
| |
| y = lslAESGetReplyMode(y);
| |
| if (y == LSLAES_MODE_ERROR)
| |
| llOwnerSay("ERROR: "+msg);
| |
| else llOwnerSay("CipherText = "+msg);
| |
| }
| |
| }</lsl>
| |
| | |
| ==== Decrypt a message ====
| |
| This snippet will take a message encrypted using the above code and decrypt it.
| |
| | |
| <lsl>string myKey = "56789ABCDEF0123456789ABCDEF01234"; // 128-bit key in hex
| |
| string ciphertext = "1k2bsQYTa89W9AdNZRMoxeraISj8/ZHyPEkJEeaAXzS2+kFUidIWRP54yb7u8UAsK0+wv71XYMMaxNlEDsYQYQ=";
| |
|
| |
| ... // Add required helper functions and constants here
| |
| | |
| default {
| |
| state_entry() { // First prime the engine with a key
| |
| lslAESPrimeHexKey(
| |
| LINK_THIS,
| |
| myKey,
| |
| llGetKey()
| |
| );
| |
| }
| |
|
| |
| link_message(integer x, integer y, string msg, key id) {
| |
| if (!lslAESIsReply(y, id)) return;
| |
|
| |
| y = lslAESGetReplyMode(y);
| |
| if (y == LSLAES_MODE_ERROR)
| |
| llOwnerSay("ERROR: "+msg);
| |
| else state decrypt;
| |
| }
| |
| }
| |
|
| |
| state decrypt {
| |
| state_entry() { // Send our message
| |
| lslAESDecryptBase64ToBase64(
| |
| LINK_THIS,
| |
| ciphertext,
| |
| llGetOwner()
| |
| );
| |
| }
| |
|
| |
| link_message(integer x, integer y, string msg, key id) {
| |
| if (!lslAESIsReply(y, id)) return;
| |
|
| |
| y = lslAESGetReplyMode(y);
| |
| if (y == LSLAES_MODE_ERROR)
| |
| llOwnerSay("ERROR: "+msg);
| |
| else llOwnerSay("Message = "+llBase64ToString(msg));
| |
| }
| |
| }</lsl>
| |
| | |
| ==== Get a hash of a message ====
| |
| This snippet will take a message, and calculate a 512-bit hash of the message using an AES key. Smaller hash-sizes may be used as required, but will reduce uniqueness, though 128-bit hashes are sufficient for most applications.
| |
|
| |
|
| <lsl>string myKey = "56789ABCDEF0123456789ABCDEF01234"; // 128-bit key in hex
| | However, AES can be used as a form of [http://en.wikipedia.org/wiki/Digital_signatures Digital Signature]. While a signature is supposed to use asymmetric encryption, this is currently unfeasible to do within LSL, however, AES is very strong, and if used carefully can be just as secure. This allows you to add signatures that make it possible for your web-site to verify the authenticity of the messages it receives, allowing you to figure out which in-world objects you can trust. |
| string message = "Hello world! I am a wonderful message in need of encryption!";
| |
|
| |
|
| ... // Add required helper functions and constants here | | To use AES as a digital signature, you can simply use [[llMD5String|llMD5String()]] or [[llSHA1String|llSHA1String()]] to produce a hash of your message contents, you then simply encrypt this hash and add it to your message so that the web-site can verify that the message was not forged, or tampered with. You should ensure that the key and/or input-vector you use when doing this changes regularly in a consistent and secure way so that the signature-scheme cannot be broken. |
|
| |
|
| default {
| | == In-world communication == |
| state_entry() { // First prime the engine with a key
| | AES is ideal for encrypting messages sent between objects via chat-channels, and is useful for objects that are part of a game, or anything involving monetary transactions. You must be aware however that you will require some method of providing your objects with the same key for encryption. The easiest way to do this is to put it into a notecard, and give each object a copy of this notecard, the objects can then use this key to generate new ones regularly, though you will need to ensure the objects remain in-sync which is beyond the scope of this article. |
| lslAESPrimeHexKey(
| |
| LINK_THIS,
| |
| myKey,
| |
| llGetKey()
| |
| );
| |
| }
| |
|
| |
| link_message(integer x, integer y, string msg, key id) {
| |
| if (!lslAESIsReply(y, id)) return;
| |
|
| |
| y = lslAESGetReplyMode(y);
| |
| if (y == LSLAES_MODE_ERROR)
| |
| llOwnerSay("ERROR: "+msg);
| |
| else state hash;
| |
| }
| |
| }
| |
|
| |
| state hash {
| |
| state_entry() { // Send our message
| |
| lslAESHashBase64ToHex(
| |
| LINK_THIS,
| |
| llStringToBase64(message),
| |
| 512,
| |
| llGetOwner()
| |
| );
| |
| }
| |
|
| |
| link_message(integer x, integer y, string msg, key id) {
| |
| if (!lslAESIsReply(y, id)) return;
| |
|
| |
| y = lslAESGetReplyMode(y);
| |
| if (y == LSLAES_MODE_ERROR)
| |
| llOwnerSay("ERROR: "+msg);
| |
| else llOwnerSay("Hash = "+msg);
| |
| }
| |
| }</lsl>
| |
|
| |
|
| == PHP Code == | | = Implementations = |
| | * [[AES LSL Implementation|LSL]] (or [[AES LSL+ Implementation|LSL+]]) |
| | * [[AES Java Implementation|Java]] |
| | * [[AES PHP Implementation|PHP]] |
|
| |
|
| An equivalent PHP solution is pending, and will be pretty much an exact port of the LSL code.
| | {{LSLC|Encryption}} |