Difference between revisions of "AES LSL Implementation"
(New page: = AES LSL Implementation = == Description == The following is an LSL implementation of the AES Strong Encryption AES symmetric-key, block-cipher encryption scheme. == License == You ...) |
m (<lsl> tag to <source>) |
||
(29 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
{{LSL Header}} | |||
= AES LSL Implementation = | = AES LSL Implementation = | ||
== Description == | == Description == | ||
== License == | === Overview === | ||
You may use the following code freely in your own work, provided you credit the author ([[User:Haravikk Mistral|Haravikk Mistral]]) and | The following is an LSL implementation of the [[AES Strong Encryption|AES]] symmetric-key, block-cipher encryption scheme. It has been designed to be [http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf FIPS-197] compliant, and will be updated where possible to introduce better compatibility with other AES implementations. | ||
This script will '''ONLY''' compile in [[Mono]], therefore it must currently be compiled within an object, rather than within a script in your inventory (you can drag the object-script into inventory if you wish). | |||
=== License === | |||
You may use the following code freely in your own work, provided you credit the author ([[User:Haravikk Mistral|Haravikk Mistral]]) and supply a link to this wiki article where feasible. You may use this implementation in commercial works. If you add enhanced support for alternate modes of operation, padding schemes or produce other enhancements to the engine, then you are asked to please contribute them to this wiki-article, letting me know your intention first though is appreciated to help keep the code clean as possible and the article(s) uncluttered. | |||
I provide no warranty or guarantees about the correctness of this code, or any liability for damages incurred while using it. As with any security solution it is the responsibility of the developer to properly evaluate it, and design its correct integration into a system, [[AES Java Implementation|Java examples]] are provided to help you evaluate the correctness of the code. If you have any issues, questions or problems, or find any potential bug(s) then I will as always endeavour to help, but do not guarantee that I can or will. | |||
=== Thanks === | |||
* Many thanks to [[User:Strife Onizuka|Strife Onizuka]] for helping with several optimisations to the original code! | |||
* Thanks also to [[User:bobby30 Swashbuckler|bobby30 Swashbuckler]] for providing resources that helped reduce the memory footprint of the code (allowing bigger messages to be encrypted)! | |||
== Details == | == Details == | ||
Line 16: | Line 28: | ||
!Mode | !Mode | ||
!Description | !Description | ||
|- | |- | ||
|MODE_CBC | |MODE_CBC | ||
|[http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29 Cipher Block Chaining] mode is the most commonly used mode. It works by XORing previous blocks of ciphertext with new blocks of input, making it unlikely that repeating patterns will occur. | |[http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29 Cipher Block Chaining] mode is the most commonly used mode. It works by XORing previous blocks of ciphertext with new blocks of input, making it unlikely that repeating patterns will occur. It is the most well supported by other programming languages. | ||
|- | |- | ||
|MODE_CFB | |MODE_CFB | ||
|[http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29 Cipher Feed-Back] mode is very similar to CBC, except that it does not require cipher inversion to decrypt, meaning you can (if you wish) remove all decryption code to increase available memory. | |[http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29 Cipher Feed-Back] mode is very similar to CBC, except that it does not require cipher inversion to decrypt, meaning you can (if you wish) remove all decryption code to increase available memory. '''Note''': For compatibility with other languages it should be noted that this implementation is actually nCFB or block-wide/128-bit CFB, so be careful when communicating with non LSL sources to be sure you select the correct mode of operation. | ||
|} | |} | ||
Line 39: | Line 45: | ||
|- | |- | ||
|PAD_NONE | |PAD_NONE | ||
|Only usable with MODE_CFB | |Only usable with MODE_CFB above, this mode indicates that encryption is able to be performed on incomplete blocks of data, rather than padding them to 128-bits. Other modes of operation cannot be padded in this way, and will switch to PAD_RBT. When using MODE_CFB, if you want to maintain data-length, you should use this mode instead of PAD_RBT. | ||
|- | |- | ||
|PAD_RBT | |PAD_RBT | ||
|[http://en.wikipedia.org/wiki/Residual_block_termination Residual Block Termination] is a type of padding which essentially switches to CFB mode temporarily in order to encrypt any leftover-data that did not fit correctly within a block. The advantage of this mode is that it allows | |[http://en.wikipedia.org/wiki/Residual_block_termination Residual Block Termination] is a type of padding which essentially switches to CFB mode temporarily in order to encrypt any leftover-data that did not fit correctly within a block. The advantage of this mode is that it allows MODE_CBC to be performed without having to increase the size of the data. | ||
|- | |- | ||
|PAD_NULLS | |PAD_NULLS | ||
|This mode is intended to allow compatibility with PHP's mcrypt implementation. It simply adds as many null-characters (zero-bytes) to the input as required to complete any incomplete blocks. When decrypting ALL trailing null-characters are removed so you must be careful with your inputs. | |This mode is intended to allow compatibility with PHP's mcrypt implementation. It simply adds as many null-characters (zero-bytes) to the input as required to complete any incomplete blocks. When decrypting ALL trailing null-characters are removed so you must be careful with your inputs, as legitimate nulls may be trimmed. | ||
|- | |||
|PAD_NULLS_SAFE | |||
|This mode is a more standards compliant null-padding scheme, and is all bit-safe, making it ideal for use with this implementation of AES due to the tricky nature of base64 strings sometimes resulting in unexpected discrepancies. It simply adds a '1' bit, then pads zeroes until it reaches a block-boundary, adding an additional block if required. Decrypting is able to recognise the added '1' and thus safely trim the padding bits without any risk of corruption to the original data. | |||
|- | |- | ||
|PAD_ZEROES | |PAD_ZEROES | ||
|Adds zero-bytes to the end of the input, with the final byte describing the number of padding-bytes that were added. For this reason, at least one byte must be added to the input, so inputs that fit correctly into blocks will be extended anyway by a full-block. | |Adds zero-bytes to the end of the input, with the final byte describing the number of padding-bytes that were added. For this reason, at least one byte must be added to the input, so inputs that fit correctly into blocks will be extended anyway by a full-block. This padding method is compatible with the [http://www.w3.org/TR/xmlenc-core/#sec-Alg-Block XML Encryption Standard]. | ||
|- | |- | ||
|PAD_RANDOM | |PAD_RANDOM | ||
|Identical to PAD_ZEROES except that random bytes are used instead of zeroes. | |Identical to PAD_ZEROES except that random bytes are used instead of zeroes (and also compatible with the XML Encryption Standard). At slightly added cost this mode creates random data to aid encryption, though with non ECB modes this is typically unnecessary. | ||
|} | |} | ||
=== Notes on security === | |||
For security you are recommended to use MODE_CBC (the default) as it is widely used, and good at destroying any recognisable patterns in your input-data. | |||
When using MODE_CBC you should provide an input-vector (using the init command), and strive to change this regularly, in many cases doing so after every interaction is recommended, but you must ensure both the encrypting party, and the decrypting party always have the same input-vector, or decryption will fail. An easy way to do this is to encrypt the input-vector after every-pass, using PAD_RBC or PAD_NONE to ensure the length of the input-vector does not change. Make sure your input-vector is always handled as hexadecimal characters for correctness! | |||
An alternative to changing the input-vector is to simply ensure the first block of your input is always different, as this will cause all subsequent blocks to appear different too. This could be achieved by placing a time-stamp at the start of your messages for example. The AES algorithm itself will provide extremely different results for only slightly different inputs so a [[llGetUnixTime|unix-timestamp]] could change your ciphertext completely. | |||
The one thing this library does not provide is a way to produce keys securely, other libraries may provide functionality to this end. Getting keys to your clients/servers without revealing them to an attacker is extremely important, as the key used to encrypt a message is also used to decrypt it. | |||
=== Script === | === Script === | ||
< | <source lang="lsl2">// These variables are used to build communications. Commands are sent as | ||
// combined bits in the integer argument of a link-message, and are | // combined bits in the integer argument of a link-message, and are | ||
// recovered using masks, you may wish to read about bit-masks before | // recovered using masks, you may wish to read about bit-masks before | ||
Line 98: | Line 116: | ||
// Decrypt message using expanded key | // Decrypt message using expanded key | ||
integer LSLAES_COMMAND_DECRYPT = 0x00030000; | integer LSLAES_COMMAND_DECRYPT = 0x00030000; | ||
// Sets-up the engine by specifying comma-separated flags | // Sets-up the engine by specifying comma-separated flags | ||
integer LSLAES_COMMAND_SETUP = 0x00050000; | integer LSLAES_COMMAND_SETUP = 0x00050000; | ||
Line 116: | Line 130: | ||
// Output type is base64 | // Output type is base64 | ||
integer LSLAES_OUTPUT_BASE64 = 0x00000100; | integer LSLAES_OUTPUT_BASE64 = 0x00000100; | ||
// Refuse any data longer than this many characters | // Refuse any data longer than this many characters | ||
integer LSLAES_MAX_SIZE = | integer LSLAES_MAX_SIZE = 1664; | ||
string LSLAES_HEX_CHARS = " | string LSLAES_HEX_CHARS = "0123456789abcdef"; | ||
string LSLAES_BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | string LSLAES_BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | ||
// Treats the above list as a byte-array, retrieving the desired byte value. | // Treats the above list as a byte-array, retrieving the desired byte value. | ||
integer lslAESGetSBoxInvertedByte(integer n) { | integer lslAESGetSBoxInvertedByte(integer n) { | ||
return lslAESMultInverse(lslAESInverseAffine(n)); | |||
} | } | ||
// Treats the above list as a byte-array, retrieving the desired byte value. | // Treats the above list as a byte-array, retrieving the desired byte value. | ||
integer lslAESGetSBoxByte(integer n) { | integer lslAESGetSBoxByte(integer n) { | ||
return lslAESAffine(lslAESMultInverse(n)); | |||
} | |||
// Calculates high-bit of x / 2 in a finite field | |||
if ( | integer lslAESHibit(integer x) { | ||
x = (x >> 1) | (x >> 2); | |||
x = x | (x >> 2); | |||
x = x | (x >> 4); | |||
} | return (++x) >> 1; | ||
return ( | } | ||
// 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); | |||
} | } | ||
// The following constants define modes of operation | // The following constants define modes of operation | ||
integer LSLAES_MODE_CBC = 1; | integer LSLAES_MODE_CBC = 1; | ||
integer LSLAES_MODE_CFB = 2; | integer LSLAES_MODE_CFB = 2; | ||
// Used to set mode | // Used to set mode | ||
list LSLAES_MODES = [ | list LSLAES_MODES = [ | ||
"MODE_CBC", LSLAES_MODE_CBC, | "MODE_CBC", LSLAES_MODE_CBC, | ||
"MODE_CFB", LSLAES_MODE_CFB | "MODE_CFB", LSLAES_MODE_CFB | ||
]; | ]; | ||
Line 215: | Line 216: | ||
// end, which are trimmed afterwards. | // end, which are trimmed afterwards. | ||
// Padding creates blocks of lslAESPadSize | // Padding creates blocks of lslAESPadSize | ||
integer LSLAES_PAD_RANDOM = | integer LSLAES_PAD_NULLS_SAFE = 3; // Safe null-pad | ||
integer LSLAES_PAD_RANDOM = 4; // Adds random bytes to the end of the | |||
// data until it reaches a multiple of | // data until it reaches a multiple of | ||
// lslAESPadSize in length. Final byte | // lslAESPadSize in length. Final byte | ||
Line 221: | Line 223: | ||
// scheme causes padding to ALWAYS be | // scheme causes padding to ALWAYS be | ||
// added. | // added. | ||
integer LSLAES_PAD_ZEROES = | integer LSLAES_PAD_ZEROES = 5; // Identical to LSLAES_PAD_RANDOM except | ||
// that zero-bytes are added. | // that zero-bytes are added. | ||
Line 227: | Line 229: | ||
list LSLAES_PADS = [ | list LSLAES_PADS = [ | ||
"PAD_NONE", LSLAES_PAD_NONE, | "PAD_NONE", LSLAES_PAD_NONE, | ||
"PAD_RBT", | "PAD_RBT", LSLAES_PAD_RBT, | ||
"PAD_NULLS", | "PAD_NULLS", LSLAES_PAD_NULLS, | ||
"PAD_RANDOM", | "PAD_NULLS_SAFE", LSLAES_PAD_NULLS_SAFE, | ||
"PAD_ZEROES", | "PAD_RANDOM", LSLAES_PAD_RANDOM, | ||
"PAD_ZEROES", LSLAES_PAD_ZEROES | |||
]; | ]; | ||
Line 280: | Line 283: | ||
integer padding = lslAESPad; | integer padding = lslAESPad; | ||
if (padding == LSLAES_PAD_NONE) { | if (padding == LSLAES_PAD_NONE) { | ||
if | if (lslAESMode == LSLAES_MODE_CFB) | ||
return [bits] + lslAESCipher((data = []) + data); | return [bits] + lslAESCipher((data = []) + data); | ||
padding = LSLAES_PAD_RBT; | padding = LSLAES_PAD_RBT; | ||
Line 293: | Line 295: | ||
if (padding == LSLAES_PAD_RBT) { | if (padding == LSLAES_PAD_RBT) { | ||
// This scheme takes the last encrypted block, encrypts it again and | // This scheme takes the last encrypted block, encrypts it again and | ||
// it with any leftover data, maintaining data-length. If input | // XORs it with any leftover data, maintaining data-length. If input | ||
// than a block in size then the current input-vector is used. | // is less than a block in size then the current input-vector is used. | ||
list final = []; | list final = []; | ||
if (extra > 0) { | if (extra > 0) { | ||
integer | integer words = extra / 32; | ||
if ((words * 32) < extra) ++words; | |||
// Grab leftover words | // Grab leftover words | ||
Line 309: | Line 311: | ||
// If not enough for a block, we generate lb using | // If not enough for a block, we generate lb using | ||
// a double cipher of input vector. | // a double cipher of input vector. | ||
lb = lslAESCipher( | lb = lslAESCipher( | ||
lslAESCipher((data = []) + [ | lslAESCipher((data = []) + [ | ||
Line 322: | Line 323: | ||
llDeleteSubList((data = []) + data, -words, -1) | llDeleteSubList((data = []) + data, -words, -1) | ||
); | ); | ||
lb = lslAESCipher(llList2List(data, - | lb = lslAESCipher(llList2List(data, -4, -1)); | ||
} | } | ||
Line 334: | Line 335: | ||
return [bits] + (data = final = []) + data + final; | return [bits] + (data = final = []) + data + final; | ||
} | } | ||
return lslAESCipher((data = []) + data); | return [bits] + lslAESCipher((data = []) + data); | ||
} else { | } else { | ||
// This scheme works by adding bytes until the data is a | // This scheme works by adding bytes until the data is a | ||
Line 343: | Line 344: | ||
// final byte. | // final byte. | ||
extra = blockSize - extra; // Bits to add | extra = blockSize - extra; // Bits to add | ||
if (padding == LSLAES_PAD_NULLS_SAFE) { | |||
++bits; | |||
integer words = bits / 32; | |||
integer bit = bits % 32; | |||
if (words < (data != [])) { | |||
integer word = llList2Integer(data, words); | |||
data = llListReplaceList( | |||
(data = []) + data, | |||
[word | (1 << (31 - bit))], | |||
words, | |||
words | |||
); | |||
} else data += [0x80000000]; | |||
if ((--extra) < 0) extra += blockSize; | |||
padding = LSLAES_PAD_NULLS; | |||
} | |||
integer bytes = extra / 8; // Bytes to add | integer bytes = extra / 8; // Bytes to add | ||
if (bytes <= 0) { | if (bytes <= 0) { | ||
Line 416: | Line 437: | ||
integer padding = lslAESPad; | integer padding = lslAESPad; | ||
if (padding == LSLAES_PAD_NONE) { | if (padding == LSLAES_PAD_NONE) { | ||
if | if (lslAESMode == LSLAES_MODE_CFB) | ||
return [bits] + lslAESInvertCipher((data = []) + data); | return [bits] + lslAESInvertCipher((data = []) + data); | ||
padding = LSLAES_PAD_RBT; | padding = LSLAES_PAD_RBT; | ||
Line 434: | Line 454: | ||
list final = []; | list final = []; | ||
if (extra > 0) { | if (extra > 0) { | ||
integer words = llCeil((float)extra / 32.0); | |||
integer words = llCeil((float) | |||
// Grab leftover words | // Grab leftover words | ||
Line 445: | Line 464: | ||
// If not enough for a block, we generate lb using | // If not enough for a block, we generate lb using | ||
// a double cipher of input vector. | // a double cipher of input vector. | ||
lb = lslAESCipher( | lb = lslAESCipher( | ||
lslAESCipher((data = []) + [ | lslAESCipher((data = []) + [ | ||
Line 455: | Line 473: | ||
// If there are blocks, then we double-encrypt the | // If there are blocks, then we double-encrypt the | ||
// last full block to generate lb, then decrypt normally | // last full block to generate lb, then decrypt normally | ||
lb = lslAESCipher(llList2List(data, -( | lb = lslAESCipher( | ||
llList2List(data, -(4 + words), -(words + 1)) | |||
); | |||
data = lslAESInvertCipher( | data = lslAESInvertCipher( | ||
llDeleteSubList((data = []) + data, -words, -1) | llDeleteSubList((data = []) + data, -words, -1) | ||
Line 495: | Line 515: | ||
data = lslAESInvertCipher((data = []) + data); | data = lslAESInvertCipher((data = []) + data); | ||
integer bytes = 0; integer words = 0; | integer bytes = 0; integer words = 0; integer excessBits = 0; | ||
// Remove extra bytes as required | // Remove extra bytes as required | ||
if (padding == LSLAES_PAD_NULLS) { | if ((padding == LSLAES_PAD_NULLS) || | ||
(padding == LSLAES_PAD_NULLS_SAFE)) { | |||
// We remove all zero-bytes at the end of the data | // We remove all zero-bytes at the end of the data | ||
integer l = data != []; | integer l = data != []; | ||
integer v = 0; | integer v = 0; integer j = 0; | ||
while (words < l) { | while (words < l) { | ||
Line 510: | Line 531: | ||
bytes += 4; | bytes += 4; | ||
} else { | } else { | ||
j = 0; integer byte = 0; | |||
do { | do { | ||
byte = (v >> (j << 3)) & 0xFF; | byte = (v >> (j << 3)) & 0xFF; | ||
Line 520: | Line 541: | ||
} | } | ||
@skip; | @skip; | ||
if (padding == LSLAES_PAD_NULLS_SAFE) { | |||
integer byte = (v >> (j << 3)) & 0xFF; | |||
integer i = 1; | |||
while (i < 0xFF) { | |||
++excessBits; | |||
if (byte & i) jump skip2; | |||
i = i << 1; | |||
} | |||
} | |||
@skip2; | |||
} else { | } else { | ||
// Get the number of bytes to remove from the final byte | // Get the number of bytes to remove from the final byte | ||
Line 532: | Line 564: | ||
// Correct bit-header for output | // Correct bit-header for output | ||
bits -= bytes << 3; | bits -= (bytes << 3) + excessBits; | ||
return [bits] + (data = []) + data; | return [bits] + (data = []) + data; | ||
} | } | ||
} | } | ||
Line 591: | Line 575: | ||
list lslAESInvertCipher(list data) { | list lslAESInvertCipher(list data) { | ||
// The following are used to pass blocks forward | // 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 j = 0; | ||
Line 618: | Line 590: | ||
while (l > 0) { | while (l > 0) { | ||
// Different modes treat blocks differently | // Different modes treat blocks differently | ||
if (lslAESMode == LSLAES_MODE_CBC) { | |||
// For CBC we load it, and must keep a copy | // For CBC we load it, and must keep a copy | ||
nextBlock0 = llList2Integer(data, 0); | nextBlock0 = llList2Integer(data, 0); | ||
Line 684: | Line 634: | ||
} | } | ||
if | if (lslAESMode == LSLAES_MODE_CFB) | ||
lslAESPerformCipher(); // CFB doesn't need inverse cipher | |||
lslAESPerformCipher(); // CFB | |||
else { | else { | ||
// Add last round key before rounds begin | // Add last round key before rounds begin | ||
Line 707: | Line 656: | ||
// Ciphertext is generated differently by different modes | // Ciphertext is generated differently by different modes | ||
if (lslAESMode == LSLAES_MODE_CBC) { | |||
// For CBC we XOR with previous block before output | // For CBC we XOR with previous block before output | ||
output = (output = []) + output + [ | output = (output = []) + output + [ | ||
Line 740: | Line 677: | ||
prevBlock2 = nextBlock2; | prevBlock2 = nextBlock2; | ||
prevBlock3 = nextBlock3; | prevBlock3 = nextBlock3; | ||
} else | } else { // CFB | ||
// For CBF we XOR input block for output and carry | |||
// input for encryption next-block | // input for encryption next-block | ||
prevBlock0 = llList2Integer(data, 0); | prevBlock0 = llList2Integer(data, 0); | ||
Line 759: | Line 696: | ||
(lslAESStateX2Y2 << 8) | (lslAESStateX2Y3)), | (lslAESStateX2Y2 << 8) | (lslAESStateX2Y3)), | ||
prevBlock3 ^ | prevBlock3 ^ | ||
((lslAESStateX3Y0 << 24) | (lslAESStateX3Y1 << 16) | | ((lslAESStateX3Y0 << 24) | (lslAESStateX3Y1 << 16) | | ||
(lslAESStateX3Y2 << 8) | (lslAESStateX3Y3)) | (lslAESStateX3Y2 << 8) | (lslAESStateX3Y3)) | ||
Line 795: | Line 715: | ||
// 128-bits long. | // 128-bits long. | ||
list lslAESCipher(list data) { | list lslAESCipher(list data) { | ||
// | // We must prime the state with the input vector | ||
lslAESLoadInputVector(); | |||
integer l = (data != []); | integer l = (data != []); | ||
Line 804: | Line 724: | ||
while (l > 0) { | while (l > 0) { | ||
// Different modes treat blocks differently | // Different modes treat blocks differently | ||
if (lslAESMode == LSLAES_MODE_CBC) { | |||
// For CBC we XOR with the previous block to reduce | // For CBC we XOR with the previous block to reduce | ||
// chances of patterns occurring | // chances of patterns occurring | ||
Line 853: | Line 751: | ||
lslAESPerformCipher(); | lslAESPerformCipher(); | ||
if (lslAESMode == | if (lslAESMode == 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 | // Reduce input | ||
Line 922: | Line 801: | ||
// order to decrypt data). | // order to decrypt data). | ||
lslAESPerformCipher() { | lslAESPerformCipher() { | ||
lslAESAddRoundKey(0); | lslAESAddRoundKey(0); | ||
Line 977: | Line 855: | ||
lslAESStateX0Y0 = lslAESStateX0Y0 ^ ((t >> 24) & 0xFF); | lslAESStateX0Y0 = lslAESStateX0Y0 ^ ((t >> 24) & 0xFF); | ||
lslAESStateX0Y1 = lslAESStateX0Y1 ^ ((t >> 16) & 0xFF); | lslAESStateX0Y1 = lslAESStateX0Y1 ^ ((t >> 16) & 0xFF); | ||
lslAESStateX0Y2 = lslAESStateX0Y2 ^ ((t >> 8) | lslAESStateX0Y2 = lslAESStateX0Y2 ^ ((t >> 8 ) & 0xFF); | ||
lslAESStateX0Y3 = lslAESStateX0Y3 ^ (t | lslAESStateX0Y3 = lslAESStateX0Y3 ^ ((t ) & 0xFF); | ||
t = llList2Integer(lslAESRoundKey, ++round); | t = llList2Integer(lslAESRoundKey, ++round); | ||
lslAESStateX1Y0 = lslAESStateX1Y0 ^ ((t >> 24) & 0xFF); | lslAESStateX1Y0 = lslAESStateX1Y0 ^ ((t >> 24) & 0xFF); | ||
lslAESStateX1Y1 = lslAESStateX1Y1 ^ ((t >> 16) & 0xFF); | lslAESStateX1Y1 = lslAESStateX1Y1 ^ ((t >> 16) & 0xFF); | ||
lslAESStateX1Y2 = lslAESStateX1Y2 ^ ((t >> 8) | lslAESStateX1Y2 = lslAESStateX1Y2 ^ ((t >> 8 ) & 0xFF); | ||
lslAESStateX1Y3 = lslAESStateX1Y3 ^ (t | lslAESStateX1Y3 = lslAESStateX1Y3 ^ ((t ) & 0xFF); | ||
t = llList2Integer(lslAESRoundKey, ++round); | t = llList2Integer(lslAESRoundKey, ++round); | ||
lslAESStateX2Y0 = lslAESStateX2Y0 ^ ((t >> 24) & 0xFF); | lslAESStateX2Y0 = lslAESStateX2Y0 ^ ((t >> 24) & 0xFF); | ||
lslAESStateX2Y1 = lslAESStateX2Y1 ^ ((t >> 16) & 0xFF); | lslAESStateX2Y1 = lslAESStateX2Y1 ^ ((t >> 16) & 0xFF); | ||
lslAESStateX2Y2 = lslAESStateX2Y2 ^ ((t >> 8) | lslAESStateX2Y2 = lslAESStateX2Y2 ^ ((t >> 8 ) & 0xFF); | ||
lslAESStateX2Y3 = lslAESStateX2Y3 ^ (t | lslAESStateX2Y3 = lslAESStateX2Y3 ^ ((t ) & 0xFF); | ||
t = llList2Integer(lslAESRoundKey, ++round); | t = llList2Integer(lslAESRoundKey, ++round); | ||
lslAESStateX3Y0 = lslAESStateX3Y0 ^ ((t >> 24) & 0xFF); | lslAESStateX3Y0 = lslAESStateX3Y0 ^ ((t >> 24) & 0xFF); | ||
lslAESStateX3Y1 = lslAESStateX3Y1 ^ ((t >> 16) & 0xFF); | lslAESStateX3Y1 = lslAESStateX3Y1 ^ ((t >> 16) & 0xFF); | ||
lslAESStateX3Y2 = lslAESStateX3Y2 ^ ((t >> 8) | lslAESStateX3Y2 = lslAESStateX3Y2 ^ ((t >> 8 ) & 0xFF); | ||
lslAESStateX3Y3 = lslAESStateX3Y3 ^ (t | lslAESStateX3Y3 = lslAESStateX3Y3 ^ ((t ) & 0xFF); | ||
} | } | ||
Line 1,041: | Line 919: | ||
// Performs row shifts | // Performs row shifts | ||
lslAESShiftRows() { | lslAESShiftRows() { | ||
// Rotate first row 1 columns to left | // Rotate first row 1 columns to left | ||
t = | integer t = lslAESStateX0Y1; | ||
lslAESStateX0Y1 = lslAESStateX1Y1; | |||
lslAESStateX1Y1 = | lslAESStateX1Y1 = lslAESStateX2Y1; | ||
lslAESStateX2Y1 = lslAESStateX3Y1; | |||
lslAESStateX3Y1 = t; | |||
// Rotate second row 2 columns to left | // Rotate second row 2 columns to left | ||
t = | t = lslAESStateX0Y2; | ||
lslAESStateX0Y2 = lslAESStateX2Y2; | |||
lslAESStateX2Y2 = t; | lslAESStateX2Y2 = t; | ||
t = | t = lslAESStateX1Y2; | ||
lslAESStateX1Y2 = lslAESStateX3Y2; | |||
lslAESStateX3Y2 = t; | |||
// Rotate third row 3 columns to left | // Rotate third row 3 columns to left | ||
t = | t = lslAESStateX0Y3; | ||
lslAESStateX0Y3 = lslAESStateX3Y3; | |||
lslAESStateX3Y3 = | lslAESStateX3Y3 = lslAESStateX2Y3; | ||
lslAESStateX2Y3 = lslAESStateX1Y3; | |||
lslAESStateX1Y3 = t; | |||
} | } | ||
// Undoes a set of row shifts | // Undoes a set of row shifts | ||
lslAESInvertShiftRows() { | lslAESInvertShiftRows() { | ||
// Rotate first row 1 columns to right | // Rotate first row 1 columns to right | ||
t = | integer t = lslAESStateX3Y1; | ||
lslAESStateX3Y1 = lslAESStateX2Y1; | |||
lslAESStateX2Y1 = lslAESStateX1Y1; | |||
lslAESStateX1Y1 = | lslAESStateX1Y1 = lslAESStateX0Y1; | ||
lslAESStateX0Y1 = t; | |||
// Rotate second row 2 columns to right | // Rotate second row 2 columns to right | ||
t = | t = lslAESStateX0Y2; | ||
lslAESStateX0Y2 = lslAESStateX2Y2; | |||
lslAESStateX2Y2 = t; | lslAESStateX2Y2 = t; | ||
t = | t = lslAESStateX1Y2; | ||
lslAESStateX1Y2 = lslAESStateX3Y2; | |||
lslAESStateX3Y2 = t; | |||
// Rotate third row 3 columns to right | // Rotate third row 3 columns to right | ||
t = | t = lslAESStateX0Y3; | ||
lslAESStateX0Y3 = lslAESStateX1Y3; | |||
lslAESStateX1Y3 = lslAESStateX2Y3; | |||
lslAESStateX2Y3 = lslAESStateX3Y3; | |||
lslAESStateX3Y3 = t; | lslAESStateX3Y3 = t; | ||
} | } | ||
Line 1,099: | Line 973: | ||
lslAESMixColumns() { | lslAESMixColumns() { | ||
integer t = lslAESStateX0Y0; | integer t = lslAESStateX0Y0; | ||
integer t1 = lslAESStateX0Y0 ^ | integer t1 = lslAESStateX0Y0 ^ lslAESStateX0Y1 ^ | ||
lslAESStateX0Y2 ^ lslAESStateX0Y3; | |||
integer t2 = lslAESXTimes(lslAESStateX0Y0 ^ | integer t2 = lslAESXTimes(lslAESStateX0Y0 ^ lslAESStateX0Y1); | ||
lslAESStateX0Y0 = lslAESStateX0Y0 ^ t2 ^ t1; | lslAESStateX0Y0 = lslAESStateX0Y0 ^ t2 ^ t1; | ||
t2 = lslAESXTimes( | t2 = lslAESXTimes(lslAESStateX0Y1 ^ lslAESStateX0Y2); | ||
lslAESStateX0Y1 = lslAESStateX0Y1 ^ t2 ^ t1; | |||
t2 = lslAESXTimes( | t2 = lslAESXTimes(lslAESStateX0Y2 ^ lslAESStateX0Y3); | ||
lslAESStateX0Y2 = lslAESStateX0Y2 ^ t2 ^ t1; | |||
t2 = lslAESXTimes( | t2 = lslAESXTimes(lslAESStateX0Y3 ^ t); | ||
lslAESStateX0Y3 = lslAESStateX0Y3 ^ t2 ^ t1; | |||
t = | t = lslAESStateX1Y0; | ||
t1 = | t1 = lslAESStateX1Y0 ^ lslAESStateX1Y1 ^ lslAESStateX1Y2 ^ lslAESStateX1Y3; | ||
t2 = lslAESXTimes( | t2 = lslAESXTimes(lslAESStateX1Y0 ^ lslAESStateX1Y1); | ||
lslAESStateX1Y0 = lslAESStateX1Y0 ^ t2 ^ t1; | |||
t2 = lslAESXTimes(lslAESStateX1Y1 ^ | t2 = lslAESXTimes(lslAESStateX1Y1 ^ lslAESStateX1Y2); | ||
lslAESStateX1Y1 = lslAESStateX1Y1 ^ t2 ^ t1; | lslAESStateX1Y1 = lslAESStateX1Y1 ^ t2 ^ t1; | ||
t2 = lslAESXTimes( | t2 = lslAESXTimes(lslAESStateX1Y2 ^ lslAESStateX1Y3); | ||
lslAESStateX1Y2 = lslAESStateX1Y2 ^ t2 ^ t1; | |||
t2 = lslAESXTimes( | t2 = lslAESXTimes(lslAESStateX1Y3 ^ t); | ||
lslAESStateX1Y3 = lslAESStateX1Y3 ^ t2 ^ t1; | |||
t = | t = lslAESStateX2Y0; | ||
t1 = | t1 = lslAESStateX2Y0 ^ lslAESStateX2Y1 ^ lslAESStateX2Y2 ^ lslAESStateX2Y3; | ||
t2 = lslAESXTimes( | t2 = lslAESXTimes(lslAESStateX2Y0 ^ lslAESStateX2Y1); | ||
lslAESStateX2Y0 = lslAESStateX2Y0 ^ t2 ^ t1; | |||
t2 = lslAESXTimes( | t2 = lslAESXTimes(lslAESStateX2Y1 ^ lslAESStateX2Y2); | ||
lslAESStateX2Y1 = lslAESStateX2Y1 ^ t2 ^ t1; | |||
t2 = lslAESXTimes(lslAESStateX2Y2 ^ | t2 = lslAESXTimes(lslAESStateX2Y2 ^ lslAESStateX2Y3); | ||
lslAESStateX2Y2 = lslAESStateX2Y2 ^ t2 ^ t1; | lslAESStateX2Y2 = lslAESStateX2Y2 ^ t2 ^ t1; | ||
t2 = lslAESXTimes( | t2 = lslAESXTimes(lslAESStateX2Y3 ^ t); | ||
lslAESStateX2Y3 = lslAESStateX2Y3 ^ t2 ^ t1; | |||
t = | t = lslAESStateX3Y0; | ||
t1 = | t1 = lslAESStateX3Y0 ^ lslAESStateX3Y1 ^ lslAESStateX3Y2 ^ lslAESStateX3Y3; | ||
t2 = lslAESXTimes( | t2 = lslAESXTimes(lslAESStateX3Y0 ^ lslAESStateX3Y1); | ||
lslAESStateX3Y0 = lslAESStateX3Y0 ^ t2 ^ t1; | |||
t2 = lslAESXTimes( | t2 = lslAESXTimes(lslAESStateX3Y1 ^ lslAESStateX3Y2); | ||
lslAESStateX3Y1 = lslAESStateX3Y1 ^ t2 ^ t1; | |||
t2 = lslAESXTimes( | t2 = lslAESXTimes(lslAESStateX3Y2 ^ lslAESStateX3Y3); | ||
lslAESStateX3Y2 = lslAESStateX3Y2 ^ t2 ^ t1; | |||
t2 = lslAESXTimes(lslAESStateX3Y3 ^ t); | t2 = lslAESXTimes(lslAESStateX3Y3 ^ t); | ||
Line 1,179: | Line 1,053: | ||
lslAESInvertMixColumns() { | lslAESInvertMixColumns() { | ||
integer a = lslAESStateX0Y0; | integer a = lslAESStateX0Y0; | ||
integer b = | integer b = lslAESStateX0Y1; | ||
integer c = | integer c = lslAESStateX0Y2; | ||
integer d = | integer d = lslAESStateX0Y3; | ||
lslAESStateX0Y0 = lslAESMultiply(a, 0x0e) ^ lslAESMultiply(b, 0x0b) ^ | lslAESStateX0Y0 = lslAESMultiply(a, 0x0e) ^ lslAESMultiply(b, 0x0b) ^ | ||
lslAESMultiply(c, 0x0d) ^ lslAESMultiply(d, 0x09); | lslAESMultiply(c, 0x0d) ^ lslAESMultiply(d, 0x09); | ||
lslAESStateX0Y1 = lslAESMultiply(a, 0x09) ^ lslAESMultiply(b, 0x0e) ^ | |||
lslAESMultiply(c, 0x0b) ^ lslAESMultiply(d, 0x0d); | lslAESMultiply(c, 0x0b) ^ lslAESMultiply(d, 0x0d); | ||
lslAESStateX0Y2 = lslAESMultiply(a, 0x0d) ^ lslAESMultiply(b, 0x09) ^ | |||
lslAESMultiply(c, 0x0e) ^ lslAESMultiply(d, 0x0b); | lslAESMultiply(c, 0x0e) ^ lslAESMultiply(d, 0x0b); | ||
lslAESStateX0Y3 = lslAESMultiply(a, 0x0b) ^ lslAESMultiply(b, 0x0d) ^ | |||
lslAESMultiply(c, 0x09) ^ lslAESMultiply(d, 0x0e); | lslAESMultiply(c, 0x09) ^ lslAESMultiply(d, 0x0e); | ||
a = | a = lslAESStateX1Y0; | ||
b = lslAESStateX1Y1; | b = lslAESStateX1Y1; | ||
c = | c = lslAESStateX1Y2; | ||
d = | d = lslAESStateX1Y3; | ||
lslAESStateX1Y0 = lslAESMultiply(a, 0x0e) ^ lslAESMultiply(b, 0x0b) ^ | |||
lslAESMultiply(c, 0x0d) ^ lslAESMultiply(d, 0x09); | lslAESMultiply(c, 0x0d) ^ lslAESMultiply(d, 0x09); | ||
lslAESStateX1Y1 = lslAESMultiply(a, 0x09) ^ lslAESMultiply(b, 0x0e) ^ | lslAESStateX1Y1 = lslAESMultiply(a, 0x09) ^ lslAESMultiply(b, 0x0e) ^ | ||
lslAESMultiply(c, 0x0b) ^ lslAESMultiply(d, 0x0d); | lslAESMultiply(c, 0x0b) ^ lslAESMultiply(d, 0x0d); | ||
lslAESStateX1Y2 = lslAESMultiply(a, 0x0d) ^ lslAESMultiply(b, 0x09) ^ | |||
lslAESMultiply(c, 0x0e) ^ lslAESMultiply(d, 0x0b); | lslAESMultiply(c, 0x0e) ^ lslAESMultiply(d, 0x0b); | ||
lslAESStateX1Y3 = lslAESMultiply(a, 0x0b) ^ lslAESMultiply(b, 0x0d) ^ | |||
lslAESMultiply(c, 0x09) ^ lslAESMultiply(d, 0x0e); | lslAESMultiply(c, 0x09) ^ lslAESMultiply(d, 0x0e); | ||
a = | a = lslAESStateX2Y0; | ||
b = | b = lslAESStateX2Y1; | ||
c = lslAESStateX2Y2; | c = lslAESStateX2Y2; | ||
d = | d = lslAESStateX2Y3; | ||
lslAESStateX2Y0 = lslAESMultiply(a, 0x0e) ^ lslAESMultiply(b, 0x0b) ^ | |||
lslAESMultiply(c, 0x0d) ^ lslAESMultiply(d, 0x09); | lslAESMultiply(c, 0x0d) ^ lslAESMultiply(d, 0x09); | ||
lslAESStateX2Y1 = lslAESMultiply(a, 0x09) ^ lslAESMultiply(b, 0x0e) ^ | |||
lslAESMultiply(c, 0x0b) ^ lslAESMultiply(d, 0x0d); | lslAESMultiply(c, 0x0b) ^ lslAESMultiply(d, 0x0d); | ||
lslAESStateX2Y2 = lslAESMultiply(a, 0x0d) ^ lslAESMultiply(b, 0x09) ^ | lslAESStateX2Y2 = lslAESMultiply(a, 0x0d) ^ lslAESMultiply(b, 0x09) ^ | ||
lslAESMultiply(c, 0x0e) ^ lslAESMultiply(d, 0x0b); | lslAESMultiply(c, 0x0e) ^ lslAESMultiply(d, 0x0b); | ||
lslAESStateX2Y3 = lslAESMultiply(a, 0x0b) ^ lslAESMultiply(b, 0x0d) ^ | |||
lslAESMultiply(c, 0x09) ^ lslAESMultiply(d, 0x0e); | lslAESMultiply(c, 0x09) ^ lslAESMultiply(d, 0x0e); | ||
a = | a = lslAESStateX3Y0; | ||
b = | b = lslAESStateX3Y1; | ||
c = | c = lslAESStateX3Y2; | ||
d = lslAESStateX3Y3; | d = lslAESStateX3Y3; | ||
lslAESStateX3Y0 = lslAESMultiply(a, 0x0e) ^ lslAESMultiply(b, 0x0b) ^ | |||
lslAESMultiply(c, 0x0d) ^ lslAESMultiply(d, 0x09); | lslAESMultiply(c, 0x0d) ^ lslAESMultiply(d, 0x09); | ||
lslAESStateX3Y1 = lslAESMultiply(a, 0x09) ^ lslAESMultiply(b, 0x0e) ^ | |||
lslAESMultiply(c, 0x0b) ^ lslAESMultiply(d, 0x0d); | lslAESMultiply(c, 0x0b) ^ lslAESMultiply(d, 0x0d); | ||
lslAESStateX3Y2 = lslAESMultiply(a, 0x0d) ^ lslAESMultiply(b, 0x09) ^ | |||
lslAESMultiply(c, 0x0e) ^ lslAESMultiply(d, 0x0b); | lslAESMultiply(c, 0x0e) ^ lslAESMultiply(d, 0x0b); | ||
lslAESStateX3Y3 = lslAESMultiply(a, 0x0b) ^ lslAESMultiply(b, 0x0d) ^ | lslAESStateX3Y3 = lslAESMultiply(a, 0x0b) ^ lslAESMultiply(b, 0x0d) ^ | ||
Line 1,238: | Line 1,112: | ||
// ENCRYPTION SET-UP FUNCTIONS // | // ENCRYPTION SET-UP FUNCTIONS // | ||
//##########################################################################// | //##########################################################################// | ||
// The following functions are used to set-up the AES encryption engine | // 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 | // Takes the key bytes provided and sets up the engine ready to encrypt or | ||
// decrypt using | // decrypt using them. | ||
// | |||
// Thanks to Strife Onizuka for providing optimisations to this function. | |||
list lslAESKeyExpansion(list keyBytes) { | list lslAESKeyExpansion(list keyBytes) { | ||
// Don't need the bit-count and the first round key is the key itself | // Don't need the bit-count and the first round key is the key itself | ||
Line 1,318: | Line 1,193: | ||
// byte data for use as a key, or to encrypt/decrypt. // | // byte data for use as a key, or to encrypt/decrypt. // | ||
//##########################################################################// | //##########################################################################// | ||
// | // Converts a base64-string into a list of integers (with bit-count header) | ||
list lslAESBase64ToBytes(string base64Data) { | list lslAESBase64ToBytes(string base64Data) { | ||
integer x = llSubStringIndex(base64Data, "="); | integer x = llSubStringIndex(base64Data, "="); | ||
if (x > 0) | if (x > 0) | ||
base64Data = | base64Data = llDeleteSubString( | ||
(base64Data = "") + base64Data, | (base64Data = "") + base64Data, | ||
x, | |||
- | -1 | ||
); | ); | ||
return lslAESStringToBytes( | return lslAESStringToBytes( | ||
Line 1,334: | Line 1,209: | ||
} | } | ||
// | // Converts a hex-string into a list of integers (with bit-count header) | ||
list lslAESHexToBytes(string hexData) { | list lslAESHexToBytes(string hexData) { | ||
if (llGetSubString(hexData, 0, 1) == "0x") | if (llGetSubString(hexData, 0, 1) == "0x") | ||
hexData = | hexData = llDeleteSubString((hexData = "") + hexData, 0, 1); | ||
return lslAESStringToBytes( | return lslAESStringToBytes( | ||
(hexData = "") + hexData, | llToLower((hexData = "") + hexData), | ||
4, | 4, | ||
LSLAES_HEX_CHARS | LSLAES_HEX_CHARS | ||
Line 1,345: | Line 1,220: | ||
} | } | ||
// | // 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. | |||
list lslAESStringToBytes(string s, integer width, string alphabet) { | list lslAESStringToBytes(string s, integer width, string alphabet) { | ||
integer l = llStringLength(s); | integer l = llStringLength(s); | ||
Line 1,382: | Line 1,263: | ||
} | } | ||
// Returns a hex string | // Returns a hex string from a list of integers (with bit-count header), | ||
// preceded with "0x". | |||
string lslAESBytesToHex(list b) { | string lslAESBytesToHex(list b) { | ||
return "0x" + lslAESBytesToString((b = []) + b, 4, LSLAES_HEX_CHARS); | return "0x" + lslAESBytesToString((b = []) + b, 4, LSLAES_HEX_CHARS); | ||
} | } | ||
// Returns a base64 string | // Returns a base64 string from a list of integers (with bit-count header), | ||
// with padding equals signs appended as required. | |||
string lslAESBytesToBase64(list b) { | string lslAESBytesToBase64(list b) { | ||
string s = lslAESBytesToString((b = []) + b, 6, LSLAES_BASE64_CHARS); | string s = lslAESBytesToString((b = []) + b, 6, LSLAES_BASE64_CHARS); | ||
integer l = llStringLength(s) % | integer l = llStringLength(s) % 4; | ||
if (l) { | if (l) { | ||
if (l == | if (l == 2) return (s = "") + s + "=="; | ||
return (s = "") + s + "="; | return (s = "") + s + "="; | ||
} | } | ||
Line 1,398: | Line 1,281: | ||
} | } | ||
// | // 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. | |||
string lslAESBytesToString(list b, integer width, string alphabet) { | string lslAESBytesToString(list b, integer width, string alphabet) { | ||
integer bits = llList2Integer(b, 0); | integer bits = llList2Integer(b, 0); | ||
Line 1,558: | Line 1,446: | ||
else if (type == LSLAES_COMMAND_DECRYPT) | else if (type == LSLAES_COMMAND_DECRYPT) | ||
data = lslAESInvertPadCipher((data = []) + data); | data = lslAESInvertPadCipher((data = []) + data); | ||
else if (type == LSLAES_COMMAND_INIT) | |||
data = lslAESSetInputVector((data = []) + data); | data = lslAESSetInputVector((data = []) + data); | ||
else data = ["Unsupported mode"]; | else data = ["Unsupported mode"]; | ||
Line 1,606: | Line 1,482: | ||
} | } | ||
} | } | ||
}</ | }</source> |
Latest revision as of 10:13, 25 January 2015
LSL Portal | Functions | Events | Types | Operators | Constants | Flow Control | Script Library | Categorized Library | Tutorials |
AES LSL Implementation
Description
Overview
The following is an LSL implementation of the AES symmetric-key, block-cipher encryption scheme. It has been designed to be FIPS-197 compliant, and will be updated where possible to introduce better compatibility with other AES implementations.
This script will ONLY compile in Mono, therefore it must currently be compiled within an object, rather than within a script in your inventory (you can drag the object-script into inventory if you wish).
License
You may use the following code freely in your own work, provided you credit the author (Haravikk Mistral) and supply a link to this wiki article where feasible. You may use this implementation in commercial works. If you add enhanced support for alternate modes of operation, padding schemes or produce other enhancements to the engine, then you are asked to please contribute them to this wiki-article, letting me know your intention first though is appreciated to help keep the code clean as possible and the article(s) uncluttered.
I provide no warranty or guarantees about the correctness of this code, or any liability for damages incurred while using it. As with any security solution it is the responsibility of the developer to properly evaluate it, and design its correct integration into a system, Java examples are provided to help you evaluate the correctness of the code. If you have any issues, questions or problems, or find any potential bug(s) then I will as always endeavour to help, but do not guarantee that I can or will.
Thanks
- Many thanks to Strife Onizuka for helping with several optimisations to the original code!
- Thanks also to bobby30 Swashbuckler for providing resources that helped reduce the memory footprint of the code (allowing bigger messages to be encrypted)!
Details
Basic usage
This script is designed to operate as an encryption "server", in that you communicate with it using linked-messages to set it up, and then ultimately encrypt/decrypt your text. To learn how to do this, you are perhaps best off looking at the helper functions and examples available for this script.
Modes of operation
This AES script supports a number of standard modes of operation, which determine how individual blocks of input data are processed. Some are quicker than others, while others are more secure. Here is a summary of the different modes of operation:
Mode | Description |
---|---|
MODE_CBC | Cipher Block Chaining mode is the most commonly used mode. It works by XORing previous blocks of ciphertext with new blocks of input, making it unlikely that repeating patterns will occur. It is the most well supported by other programming languages. |
MODE_CFB | Cipher Feed-Back mode is very similar to CBC, except that it does not require cipher inversion to decrypt, meaning you can (if you wish) remove all decryption code to increase available memory. Note: For compatibility with other languages it should be noted that this implementation is actually nCFB or block-wide/128-bit CFB, so be careful when communicating with non LSL sources to be sure you select the correct mode of operation. |
Padding modes
This AES engine supports a number of different modes, you must be careful when using this engine with other AES implementations to be sure that you are able to select a supported padding method.
Mode | Description |
---|---|
PAD_NONE | Only usable with MODE_CFB above, this mode indicates that encryption is able to be performed on incomplete blocks of data, rather than padding them to 128-bits. Other modes of operation cannot be padded in this way, and will switch to PAD_RBT. When using MODE_CFB, if you want to maintain data-length, you should use this mode instead of PAD_RBT. |
PAD_RBT | Residual Block Termination is a type of padding which essentially switches to CFB mode temporarily in order to encrypt any leftover-data that did not fit correctly within a block. The advantage of this mode is that it allows MODE_CBC to be performed without having to increase the size of the data. |
PAD_NULLS | This mode is intended to allow compatibility with PHP's mcrypt implementation. It simply adds as many null-characters (zero-bytes) to the input as required to complete any incomplete blocks. When decrypting ALL trailing null-characters are removed so you must be careful with your inputs, as legitimate nulls may be trimmed. |
PAD_NULLS_SAFE | This mode is a more standards compliant null-padding scheme, and is all bit-safe, making it ideal for use with this implementation of AES due to the tricky nature of base64 strings sometimes resulting in unexpected discrepancies. It simply adds a '1' bit, then pads zeroes until it reaches a block-boundary, adding an additional block if required. Decrypting is able to recognise the added '1' and thus safely trim the padding bits without any risk of corruption to the original data. |
PAD_ZEROES | Adds zero-bytes to the end of the input, with the final byte describing the number of padding-bytes that were added. For this reason, at least one byte must be added to the input, so inputs that fit correctly into blocks will be extended anyway by a full-block. This padding method is compatible with the XML Encryption Standard. |
PAD_RANDOM | Identical to PAD_ZEROES except that random bytes are used instead of zeroes (and also compatible with the XML Encryption Standard). At slightly added cost this mode creates random data to aid encryption, though with non ECB modes this is typically unnecessary. |
Notes on security
For security you are recommended to use MODE_CBC (the default) as it is widely used, and good at destroying any recognisable patterns in your input-data.
When using MODE_CBC you should provide an input-vector (using the init command), and strive to change this regularly, in many cases doing so after every interaction is recommended, but you must ensure both the encrypting party, and the decrypting party always have the same input-vector, or decryption will fail. An easy way to do this is to encrypt the input-vector after every-pass, using PAD_RBC or PAD_NONE to ensure the length of the input-vector does not change. Make sure your input-vector is always handled as hexadecimal characters for correctness!
An alternative to changing the input-vector is to simply ensure the first block of your input is always different, as this will cause all subsequent blocks to appear different too. This could be achieved by placing a time-stamp at the start of your messages for example. The AES algorithm itself will provide extremely different results for only slightly different inputs so a unix-timestamp could change your ciphertext completely.
The one thing this library does not provide is a way to produce keys securely, other libraries may provide functionality to this end. Getting keys to your clients/servers without revealing them to an attacker is extremely important, as the key used to encrypt a message is also used to decrypt it.
Script
// 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 = 0xFF000000;
// This mask allows the mask byte to be retrieved quickly
integer LSLAES_COMMAND_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_COMMAND_ERROR = 0x00000000;
// Prime engine with key
integer LSLAES_COMMAND_PRIME = 0x00010000;
// Encrypt message using expanded key
integer LSLAES_COMMAND_ENCRYPT = 0x00020000;
// Decrypt message using expanded key
integer LSLAES_COMMAND_DECRYPT = 0x00030000;
// Sets-up the engine by specifying comma-separated flags
integer LSLAES_COMMAND_SETUP = 0x00050000;
// Initialise the engine with an input-vector
integer LSLAES_COMMAND_INIT = 0x00060000;
// 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;
// Refuse any data longer than this many characters
integer LSLAES_MAX_SIZE = 1664;
string LSLAES_HEX_CHARS = "0123456789abcdef";
string LSLAES_BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
// Treats the above list as a byte-array, retrieving the desired byte value.
integer lslAESGetSBoxInvertedByte(integer n) {
return lslAESMultInverse(lslAESInverseAffine(n));
}
// Treats the above list as a byte-array, retrieving the desired byte value.
integer lslAESGetSBoxByte(integer n) {
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);
}
// The following constants define modes of operation
integer LSLAES_MODE_CBC = 1;
integer LSLAES_MODE_CFB = 2;
// Used to set mode
list LSLAES_MODES = [
"MODE_CBC", LSLAES_MODE_CBC,
"MODE_CFB", LSLAES_MODE_CFB
];
// The following contstants define types of padding
integer LSLAES_PAD_NONE = 0; // Only compatible with CBF and OBF modes
integer LSLAES_PAD_RBT = 1; // XOR leftover bytes with re-encrypted
// first-block. Length remains the same
integer LSLAES_PAD_NULLS = 2; // Adds zeroes (null characters) to the
// end, which are trimmed afterwards.
// Padding creates blocks of lslAESPadSize
integer LSLAES_PAD_NULLS_SAFE = 3; // Safe null-pad
integer LSLAES_PAD_RANDOM = 4; // 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.
integer LSLAES_PAD_ZEROES = 5; // Identical to LSLAES_PAD_RANDOM except
// that zero-bytes are added.
// Used to set padding type
list LSLAES_PADS = [
"PAD_NONE", LSLAES_PAD_NONE,
"PAD_RBT", LSLAES_PAD_RBT,
"PAD_NULLS", LSLAES_PAD_NULLS,
"PAD_NULLS_SAFE", LSLAES_PAD_NULLS_SAFE,
"PAD_RANDOM", LSLAES_PAD_RANDOM,
"PAD_ZEROES", LSLAES_PAD_ZEROES
];
integer LSLAES_PAD_SIZE = 512;
integer lslAESMode = LSLAES_MODE_CBC;
integer lslAESPad = LSLAES_PAD_RBT;
integer lslAESPadSize = LSLAES_PAD_SIZE;
// 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
list lslAESPadCipher(list data) {
integer bits = llList2Integer(data, 0);
data = llDeleteSubList((data = []) + data, 0, 0);
integer padding = lslAESPad;
if (padding == LSLAES_PAD_NONE) {
if (lslAESMode == LSLAES_MODE_CFB)
return [bits] + lslAESCipher((data = []) + data);
padding = LSLAES_PAD_RBT;
}
integer blockSize = lslAESPadSize;
if (padding == LSLAES_PAD_RBT) blockSize = 128;
integer blocks = bits / blockSize;
integer extra = bits % blockSize;
if (padding == 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 {
// This scheme works by adding bytes until the data is a
// multiple of lslAESPadSize bits long. In the case of
// PAD_NULLS this will only add extra data if needed,
// while the other types must always add at least one
// byte, as they also leave a note of bytes added in the
// final byte.
extra = blockSize - extra; // Bits to add
if (padding == LSLAES_PAD_NULLS_SAFE) {
++bits;
integer words = bits / 32;
integer bit = bits % 32;
if (words < (data != [])) {
integer word = llList2Integer(data, words);
data = llListReplaceList(
(data = []) + data,
[word | (1 << (31 - bit))],
words,
words
);
} else data += [0x80000000];
if ((--extra) < 0) extra += blockSize;
padding = LSLAES_PAD_NULLS;
}
integer bytes = extra / 8; // Bytes to add
if (bytes <= 0) {
if (padding == LSLAES_PAD_NULLS)
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)) {
v = v | bytes;
i = 1;
}
integer byte = 0;
while (i < extra) {
if (padding == LSLAES_PAD_RANDOM)
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)
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) &&
(i == final) && !j)
byte = bytes;
else if (padding == LSLAES_PAD_RANDOM)
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);
}
}
// Performs an inverse cipher with appropriate padding handling performed
list lslAESInvertPadCipher(list data) {
integer bits = llList2Integer(data, 0);
data = llDeleteSubList((data = []) + data, 0, 0);
integer padding = lslAESPad;
if (padding == LSLAES_PAD_NONE) {
if (lslAESMode == LSLAES_MODE_CFB)
return [bits] + lslAESInvertCipher((data = []) + data);
padding = LSLAES_PAD_RBT;
}
integer blockSize = lslAESPadSize;
if (padding == LSLAES_PAD_RBT) blockSize = 128;
integer blocks = bits / blockSize;
integer extra = bits % blockSize;
if (padding == 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 = llCeil((float)extra / 32.0);
// 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 {
// 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;
extra = llCeil((float)extra / 32.0);
if (extra <= 0) extra = 1;
if (extra > (data != [])) return [0];
data = llDeleteSubList((data = []) + data, -extra, -extra);
}
// Perform the decryption
data = lslAESInvertCipher((data = []) + data);
integer bytes = 0; integer words = 0; integer excessBits = 0;
// Remove extra bytes as required
if ((padding == LSLAES_PAD_NULLS) ||
(padding == LSLAES_PAD_NULLS_SAFE)) {
// We remove all zero-bytes at the end of the data
integer l = data != [];
integer v = 0; integer j = 0;
while (words < l) {
v = llList2Integer(data, -(words + 1));
if (v == 0) { // Four null-bytes
++words;
bytes += 4;
} else {
j = 0; integer byte = 0;
do {
byte = (v >> (j << 3)) & 0xFF;
if (byte == 0) ++bytes;
else jump skip;
} while ((++j) < 4);
}
}
@skip;
if (padding == LSLAES_PAD_NULLS_SAFE) {
integer byte = (v >> (j << 3)) & 0xFF;
integer i = 1;
while (i < 0xFF) {
++excessBits;
if (byte & i) jump skip2;
i = i << 1;
}
}
@skip2;
} else {
// Get the number of bytes to remove from the final byte
bytes = llList2Integer(data, -1) & 0xFF;
if ((bytes << 3) >= bits) return [0];
words = bytes / 4;
}
// Lop-off words, excess bytes are accounted for later
if (words > 0)
data = llDeleteSubList((data = []) + data, -words, -1);
// Correct bit-header for output
bits -= (bytes << 3) + excessBits;
return [bits] + (data = []) + data;
}
}
// 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) {
// 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) {
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)
lslAESPerformCipher(); // CFB doesn't need inverse cipher
else {
// 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);
}
// Ciphertext is generated differently by different modes
if (lslAESMode == 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 { // 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) {
// 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) {
// 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() {
lslAESAddRoundKey(0);
// There will be Rounds iterations, the first
// Rounds - 1 are identical.
integer j = 1;
while (j < lslAESRounds) {
lslAESSubBytes();
lslAESShiftRows();
lslAESMixColumns();
lslAESAddRoundKey(j++);
}
// The last column performs no mix
lslAESSubBytes();
lslAESShiftRows();
lslAESAddRoundKey(lslAESRounds);
}
// Expands the input vector for use in block differentiation
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
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() {
// 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
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
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
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 = 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.
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)
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 base64-string into a list of integers (with bit-count header)
list lslAESBase64ToBytes(string base64Data) {
integer x = llSubStringIndex(base64Data, "=");
if (x > 0)
base64Data = llDeleteSubString(
(base64Data = "") + base64Data,
x,
-1
);
return lslAESStringToBytes(
(base64Data = "") + base64Data,
6,
LSLAES_BASE64_CHARS
);
}
// Converts a hex-string into a list of integers (with bit-count header)
list lslAESHexToBytes(string hexData) {
if (llGetSubString(hexData, 0, 1) == "0x")
hexData = llDeleteSubString((hexData = "") + hexData, 0, 1);
return lslAESStringToBytes(
llToLower((hexData = "") + hexData),
4,
LSLAES_HEX_CHARS
);
}
// 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.
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 from a list of integers (with bit-count header),
// preceded with "0x".
string lslAESBytesToHex(list b) {
return "0x" + lslAESBytesToString((b = []) + b, 4, LSLAES_HEX_CHARS);
}
// Returns a base64 string from a list of integers (with bit-count header),
// with padding equals signs appended as required.
string lslAESBytesToBase64(list b) {
string s = lslAESBytesToString((b = []) + b, 6, LSLAES_BASE64_CHARS);
integer l = llStringLength(s) % 4;
if (l) {
if (l == 2) return (s = "") + s + "==";
return (s = "") + s + "=";
}
return (s = "") + s;
}
// 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.
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_COMMAND_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;
}
// Special case for COMMAND_SETUP
if ((y & LSLAES_COMMAND_MASK) == LSLAES_COMMAND_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)) >= 0)
lslAESMode = llList2Integer(LSLAES_MODES, ++j);
else if ((j = llListFindList(LSLAES_PADS, flag)) >= 0)
lslAESPad = llList2Integer(LSLAES_PADS, ++j);
else if ((string)flag == "PAD_SIZE") {
j = llList2Integer(flags, ++i); // Next value should be pad-size
if (j <= 0) j = LSLAES_PAD_SIZE;
else if (j > LSLAES_PAD_SIZE) {
error(x, "Maximum pad-size is "+(string)LSLAES_PAD_SIZE+" 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
);
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_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_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
);
}
}
}