Difference between revisions of "ARCFOUR Strong Encryption Implementation"

From Second Life Wiki
Jump to navigation Jump to search
(New page: {{LSL Header}}{{RightToc}} ARCFOUR Strong Encryption Implementation - Linden Scripting Language (LSL) Version 1.0 = Contributors = *{{User|Nekow42 Zarf}} (nekow42ATgmail.com) *{{User|Str...)
 
m (<lsl> tag to <source>)
 
(14 intermediate revisions by 3 users not shown)
Line 7: Line 7:
*{{User|Nekow42 Zarf}} (nekow42ATgmail.com)
*{{User|Nekow42 Zarf}} (nekow42ATgmail.com)
*{{User|Strife Onizuka}} -- Combined Library
*{{User|Strife Onizuka}} -- Combined Library
*{{User|Nardok Corrimal}} -- Memory optimization and help with the hex2byte function
= License =
= License =
This work is licensed under a [http://creativecommons.org/licenses/by/3.0/ Creative Commons Attribution 3.0 Unported License]
This work is licensed under a [http://creativecommons.org/licenses/by/3.0/ Creative Commons Attribution 3.0 Unported License]
Line 27: Line 29:
The same password can be used more than once, but the nonce always has to be different. (something random). You can use this to secure inter-object communication by having a shared (but secret) password, and have the two objects exchange a nonce beforehand. A warning, there is no garbage fitering, so the state array could be polluted by someone injecting fake strings. To prevent this, the first thing exchanged encrypted could be a negative port number, and the two objects could listen only for each other's key. For proper decryption, messages must arrive in the exact order they were encrypted.
The same password can be used more than once, but the nonce always has to be different. (something random). You can use this to secure inter-object communication by having a shared (but secret) password, and have the two objects exchange a nonce beforehand. A warning, there is no garbage fitering, so the state array could be polluted by someone injecting fake strings. To prevent this, the first thing exchanged encrypted could be a negative port number, and the two objects could listen only for each other's key. For proper decryption, messages must arrive in the exact order they were encrypted.


The key setup is very slow, but encryption itself is quite fast. The script should be reset before performing a new key setup.
The key setup is very slow, but encryption itself is quite fast.


Note: The EncryptText() and DecryptText() functions are NOT compatible. They CANNOT be used in the same script.


== About ARCFOUR ==
== About ARCFOUR ==
Line 37: Line 40:


== About this implementation ==
== About this implementation ==
This implementation of ARCFOUR performs 128-bit encryption. It uses the MD5sum of the password and the nonce to prevent all known related key attacks against ARCFOUR.
Each script contains two functions. One to set up the key, and another to perform the actual encryption and decryption. Additional calls to the encrypt or decrypt function will continue to encrypt with the same key stream.
Each script contains two functions. One to set up the key, and another to perform the actual encryption and decryption. Additional calls to the encrypt or decrypt function will continue to encrypt with the same key stream.


To encrypt, call the following functions:
To encrypt, call the following functions:
<lsl>
<source lang="lsl2">
KeySetup(string password, string nonce)
KeySetup(string password, string nonce)
string EncryptText(string plaintext)
string EncryptText(string plaintext)
</lsl>
</source>
The nonce should be a randomly generated string and should only be used once.
The nonce should be a randomly generated string and should only be used once.


This will return a CSV of the bytes represented as integers.
This will return a hex string.


To decrypt, call the following functions:
To decrypt, call the following functions:


<lsl>
<source lang="lsl2">
KeySetup(string password, string nonce)
KeySetup(string password, string nonce)
string DecryptText(string ciphertext)
string DecryptText(string ciphertext)
</lsl>
</source>
using the same nonce.
using the same nonce.


==Changes==
===Version 1.3===
*Corrected typecasting error. Anyone using this script prior to 2009-08-22 MUST update their copy to address this issue.


A usage example is contained in the code itself.
===Version 1.2===
*Outputs to Hex instead of a CSV of bytes


==Changes==
===Version 1.1===
*Hashes the key and the nonce with MD5, as per the [http://www.rsa.com/rsalabs/node.asp?id=2009 RSA Laboratories suggestion]. This prevents all related key attacks against ARCFOUR.
*Memory optimizations


===Version 1.0===
===Version 1.0===
Line 66: Line 77:
== Future Recommendations ==
== Future Recommendations ==
*Support [[UTF-8]]
*Support [[UTF-8]]
*Output to [[Hex]] instead of CSV of bytes
*By version 2.0, have a full protocol with automatic nonce generation, exchange, and object key filtering.
*Hash the key and the nonce instead of appending


= Code =
= Code =
Line 75: Line 85:


== Encryption ==
== Encryption ==
<lsl>
<source lang="lsl2">
//This is an exact implementation of ARCFOUR that passes the test vectors.
//This is an exact implementation of ARCFOUR that passes the test vectors.
//Since this script is public, please give it a glance over and see if you can spot the error.
//Note: Key setup chows memory. Reset this script before setting up a new key. Key setup is also a long
//Note: Key setup is a long
//process taking over 20 seconds.
//process taking over 20 seconds.
 
//Notes on use: The same password can be used more than once, but the nonce always has to be different
//Notes on use: The same password can be used more than once, but the nonce always has to be different
//(something random). You can use this to secure inter-object communication by having a shared (but secret)
//(something random). You can use this to secure inter-object communication by having a shared (but secret)
Line 88: Line 98:
//could be a negative port number, and the two objects could listen only for each other's key. For proper
//could be a negative port number, and the two objects could listen only for each other's key. For proper
//decryption, messages must arrive in the exact order they were encrypted.
//decryption, messages must arrive in the exact order they were encrypted.
 
//This work is licensed under a http://creativecommons.org/licenses/by/3.0/ Creative Commons Attribution 3.0 Unported License
//This work is licensed under a http://creativecommons.org/licenses/by/3.0/ Creative Commons Attribution 3.0 Unported License
//Please credit Nekow42 Zarf.
//Please credit Nekow42 Zarf.
//Nardok Corrimal, and Strife Onizuka contributed and need credit too.
list theState;
list theState;
integer i;
integer i;
Line 110: Line 121:
}
}
   
   
 
string UnicodeIntegerToUTF8(integer input)//Mono Safe, LSO Safe
string UnicodeIntegerToUTF8(integer input)//Mono Safe, LSO Safe
{
{
Line 120: Line 131:
     return llUnescapeURL(result);
     return llUnescapeURL(result);
}
}
 
string byte2hex(integer x)//Mono Safe, LSO Safe
string byte2hex(integer x)//Mono Safe, LSO Safe
{//Helper function for use with unicode characters.
{//Helper function for use with unicode characters.
Line 126: Line 137:
     return llGetSubString(hexc, y, y) + llGetSubString(hexc, x & 0xF, x & 0xF);
     return llGetSubString(hexc, y, y) + llGetSubString(hexc, x & 0xF, x & 0xF);
}//This function would benifit greatly from the DUP opcode, it would remove 19 bytes.
}//This function would benifit greatly from the DUP opcode, it would remove 19 bytes.
 
string hexc="0123456789ABCDEF";
string hexc="0123456789ABCDEF";
 
//} Combined Library
//} Combined Library
KeySetup(string password, string nonce)
KeySetup(string password, string nonce)
Line 138: Line 149:
     list statePartThree = [151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225];
     list statePartThree = [151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225];
     list statePartFour = [226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255];
     list statePartFour = [226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255];
     theState = statePartOne + statePartTwo + statePartThree + statePartFour;
     theState = (statePartOne = statePartTwo = statePartThree = statePartFour = []) + statePartOne + statePartTwo + statePartThree + statePartFour;
    statePartOne = [];
    statePartTwo = [];
    statePartThree = [];
    statePartFour = [];
     integer swapper;
     integer swapper;
     //setup the plaintext list
     //setup the plaintext list
     integer ThePerpetualMax;
     integer ThePerpetualMax;
     //setup the password. Use i since we already have it.
     //setup the password. Use i since we already have it.
     ThePerpetualMax = llStringLength(password);
     //New addition in 1.1, use an MD5 hashing function on the password
     for(i = 0; i < ThePerpetualMax; i++)  
    password = llMD5String(password + nonce,0);
     for(i = 0; i < 31; i+=2)  
     {
     {
         WholePassword = (WholePassword = []) + WholePassword + [UTF8ToUnicodeInteger(llGetSubString(password,i,i))];
         WholePassword = (WholePassword = []) + WholePassword + (integer)("0x"+llGetSubString(password,i,i+1));
     }
     }
    ThePerpetualMax = llStringLength(nonce);
     //Now WholePassword is equal to a list of the hex digits of the MD5 of the password plus the nonce
    for(i = 0; i < ThePerpetualMax; i++)
    {
        WholePassword = (WholePassword = []) + WholePassword + [UTF8ToUnicodeInteger(llGetSubString(nonce,i,i))];
    }
     //Now WholePassword is equal to a list of the ASCII values of the password + the nonce
    //setup the state array. Use i since we already have it.
    //for(i = 0; i < 256; i++)
    //{
    //    theState = (theState = []) + theState + [i]; //Equates to theState = theState + [i] but is more memory efficient (thanks Nardok)
    //}
     i=0;
     i=0;
     j=0;
     j=0;
Line 180: Line 178:
     j=0;
     j=0;
     //Key setup is complete!
     //Key setup is complete!
   
}
}
string EncryptText(string plaintext)
string EncryptText(string plaintext)
Line 186: Line 184:
{
{
     //All addition is done modulous
     //All addition is done modulous
    string Output;
     list PlaintextBytes;
     list PlaintextBytes;
     list CipheredBytes;
     string CipheredBytes;
     integer n;
     integer n;
     integer counter;
     integer counter;
Line 199: Line 196:
     {
     {
         PlaintextBytes = (PlaintextBytes = []) + PlaintextBytes + [UTF8ToUnicodeInteger(llGetSubString(plaintext,counter,counter))];
         PlaintextBytes = (PlaintextBytes = []) + PlaintextBytes + [UTF8ToUnicodeInteger(llGetSubString(plaintext,counter,counter))];
       
     }
     }
     ThePerpetualMax = llGetListLength(PlaintextBytes);
     ThePerpetualMax = llGetListLength(PlaintextBytes);
Line 215: Line 212:
             //The nth element of the state array is then combined with the message byte, using a bit by bit exclusive-or operation, to form the output byte.
             //The nth element of the state array is then combined with the message byte, using a bit by bit exclusive-or operation, to form the output byte.
             ByteToAdd = llList2Integer(theState, n) ^ llList2Integer(PlaintextBytes, counter);
             ByteToAdd = llList2Integer(theState, n) ^ llList2Integer(PlaintextBytes, counter);
             CipheredBytes = (CipheredBytes = []) + CipheredBytes + [ByteToAdd];
             CipheredBytes = CipheredBytes + byte2hex(ByteToAdd);
     }
     }
   
   
     return llList2CSV(CipheredBytes);
     return CipheredBytes;
}
}
default
default
{
{
 
     touch_start(integer total_number)
     touch_start(integer total_number)
     {
     {
         llSay(0, "Key is 'Key', plaintext is 'Plaintext'. Output:");
         llOwnerSay("Key is 'Key', plaintext is 'Plaintext'. Output:");
         KeySetup("Key","");
         KeySetup("Key","");
         llSay(0,(string)llGetFreeMemory());
         llOwnerSay("Key setup complete. Encrypting now...");
        llSay(0,"Key setup complete. Encrypting now...");
         llOwnerSay(EncryptText("Plaintext"));
         llSay(0,EncryptText("Plaintext"));
        llSay(0,(string)llGetFreeMemory());
        llSay(0,"Testing more encryption, for mem loss and speed.");
        llSay(0,EncryptText("More"));
        llSay(0,(string)llGetFreeMemory());
        // 187, 243, 22, 232, 217, 64, 175, 10, 211
     }
     }
}
}
</lsl>
</source>


==Decryption==
==Decryption==


<lsl>
<source lang="lsl2">
//This is an exact implementation of ARCFOUR that passes the test vectors.
//This is an exact implementation of ARCFOUR that passes the test vectors.
//Since this script is public, please give it a glance over and see if you can spot the error.
//Note: Key setup chows memory. Reset this script before setting up a new key. Key setup is also a long
//Note: Key setup is a long
//process taking over 20 seconds.
//process taking over 20 seconds.
 
//Notes on use: The same password can be used more than once, but the nonce always has to be different
//Notes on use: The same password can be used more than once, but the nonce always has to be different
//(something random). You can use this to secure inter-object communication by having a shared (but secret)
//(something random). You can use this to secure inter-object communication by having a shared (but secret)
Line 255: Line 246:
//could be a negative port number, and the two objects could listen only for each other's key. For proper
//could be a negative port number, and the two objects could listen only for each other's key. For proper
//decryption, messages must arrive in the exact order they were encrypted.
//decryption, messages must arrive in the exact order they were encrypted.
 
//This work is licensed under a http://creativecommons.org/licenses/by/3.0/ Creative Commons Attribution 3.0 Unported License
//This work is licensed under a http://creativecommons.org/licenses/by/3.0/ Creative Commons Attribution 3.0 Unported License
//Please credit Nekow42 Zarf.
//Please credit Nekow42 Zarf.
//Nardok Corrimal, and Strife Onizuka contributed and need credit too.
list theState;
integer i;
integer j;
//===================================================//
//                Combined Library                  //
//            "Nov  3 2007", "00:46:15"            //
//  Copyright (C) 2004-2007, Strife Onizuka (cc-by)  //
//    http://creativecommons.org/licenses/by/3.0/    //
//===================================================//
//{
integer UTF8ToUnicodeInteger(string input)//Mono Safe, LSO Safe
{
    integer result = llBase64ToInteger(llStringToBase64(input = llGetSubString(input,0,0)));
    if(result & 0x80000000){
        return 0;
    }
    return result >> 24;
}
string UnicodeIntegerToUTF8(integer input)//Mono Safe, LSO Safe
{
    integer bytes = llCeil(llLog(input) / 0.69314718055994530941723212145818);
    bytes = (input >= 0x80) * (bytes + ~(((1 << bytes) - input) > 0)) / 5;//adjust
    string result = "%" + byte2hex((input >> (6 * bytes)) | ((0x3F80 >> bytes) << !bytes));
    while (bytes)
        result += "%" + byte2hex((((input >> (6 * (bytes = ~-bytes))) | 0x80) & 0xBF));
    return llUnescapeURL(result);
}
string byte2hex(integer x)//Mono Safe, LSO Safe
{//Helper function for use with unicode characters.
    integer y = (x >> 4) & 0xF;
    return llGetSubString(hexc, y, y) + llGetSubString(hexc, x & 0xF, x & 0xF);
}//This function would benefit greatly from the DUP opcode, it would remove 19 bytes.
string hexc="0123456789ABCDEF";
//} Combined Library
KeySetup(string password, string nonce)
{
    list WholePassword;
    list statePartOne = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75];
    list statePartTwo = [76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150];
    list statePartThree = [151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225];
    list statePartFour = [226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255];
    theState = (statePartOne = statePartTwo = statePartThree = statePartFour = []) + statePartOne + statePartTwo + statePartThree + statePartFour;
    integer swapper;
    //setup the plaintext list
    integer ThePerpetualMax;
    //setup the password. Use i since we already have it.
    //New addition in 1.1, use an MD5 hashing function on the password
    password = llMD5String(password + nonce,0);
    for(i = 0; i < 31; i+=2)
    {
        WholePassword = (WholePassword = []) + WholePassword + (integer)("0x"+llGetSubString(password,i,i+1));
    }
    //Now WholePassword is equal to a list of the hex digits of the MD5 of the password plus the nonce
    i=0;
    j=0;
    //mix init, this sets up the ARCFOUR PRNG
        ThePerpetualMax = llGetListLength(WholePassword);
        for(i=0 ; i < 256; i++)
        {
            //Mix step one
            //Add to the variable j the contents of the ith element of the state array and the nth element of the key, where n is equal to i modulo the length of the key.
            j = (j + llList2Integer(theState, i) + llList2Integer(WholePassword,i % ThePerpetualMax)) % 256;
            //Mix step two. Swap the ith element and the jth element of the state array
            swapper = llList2Integer(theState, i);
            theState=llListReplaceList(theState, [llList2Integer(theState,j)] ,i,i);
            theState=llListReplaceList(theState, [swapper],j,j);
        }
    i=0;
    j=0;
    //Key setup is complete!
}
string DecryptText(string ciphertext)
{
//This does not repeat key setup, but continues with the same state array and values for i and j
    list CiphertextBytes;
    string DecryptedString;
    integer n;
    integer counter;
    integer swapper;
    integer ByteToAdd;
    //setup the plaintext list
    integer ThePerpetualMax;
        ThePerpetualMax = llStringLength(ciphertext) - 1;
        for(counter = 0; counter < ThePerpetualMax; counter+=2)
        {
            CiphertextBytes = (CiphertextBytes = []) + CiphertextBytes + (integer)("0x"+llGetSubString(ciphertext,counter,counter+1));
        }
    //setup the password. Use i since we already have it.
    ThePerpetualMax = llGetListLength(CiphertextBytes);
    for(counter=0;counter < ThePerpetualMax;counter++)
    {
        //The variable i is incremented by one
        i = (i + 1) % 256;
        //The contents of the ith element of the state array is then added to j
        j = (j + llList2Integer(theState, i)) % 256;
        //The ith and jth elements of the state array are swapped and their contents are added together to form a new value n.
            swapper = llList2Integer(theState, i);
            theState=llListReplaceList(theState, [llList2Integer(theState,j)] ,i,i);
            theState=llListReplaceList(theState, [swapper],j,j);
            n = (llList2Integer(theState, i) + llList2Integer(theState, j)) % 256;
            //The nth element of the state array is then combined with the message byte, using a bit by bit exclusive-or operation, to form the output byte.
            ByteToAdd = llList2Integer(theState, n) ^ llList2Integer(CiphertextBytes, counter);
            DecryptedString = DecryptedString + UnicodeIntegerToUTF8(ByteToAdd);
    }
    return DecryptedString;
}
default
{
    touch_start(integer total_number)
    {
        llOwnerSay(  "Key Setup");
        KeySetup("Key","");
        llOwnerSay( "Decrypting test string.");
        llOwnerSay(DecryptText("506C61696E74657874"));
    }
}
</source>
==Test Vectors for ARCFOUR==
Test vectors are from [http://www.bookrags.com/wiki/RC4 RC4 Summary]
This vectors are not official, but are handy for anyone testing an ARCFOUR implementation.
RC4( "Key", "Plaintext" ) == BBF316E8D940AF0AD3
In decimal, the output is "187, 243, 22, 232, 217, 64, 175, 10, 211"
This code shows that it passes the test vector:
<source lang="lsl2">
list theState;
list theState;
integer i;
integer i;
Line 299: Line 426:
KeySetup(string password, string nonce)
KeySetup(string password, string nonce)
{
{
    //All addition is done modulous
     list WholePassword;
     list WholePassword;
     list statePartOne = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75];
     list statePartOne = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75];
Line 324: Line 452:
     }
     }
     //Now WholePassword is equal to a list of the ASCII values of the password + the nonce
     //Now WholePassword is equal to a list of the ASCII values of the password + the nonce
    //setup the state array. Use i since we already have it.
    //for(i = 0; i < 256; i++)
    //{
    //    theState = (theState = []) + theState + [i]; //Equates to theState = theState + [i] but is more memory efficient (thanks Nardok)
    //}
     i=0;
     i=0;
     j=0;
     j=0;
Line 346: Line 469:
     j=0;
     j=0;
     //Key setup is complete!
     //Key setup is complete!
   
}
}
string DecryptText(string ciphertext)
string EncryptText(string plaintext)
//This does not repeat key setup, but continues with the same state array and values for i and j
{
{
//This does not repeat key setup, but continues with the same state array and values for i and j
    //All addition is done modulous
     string Output;
     string Output;
     list CiphertextBytes;
     list PlaintextBytes;
     string DecryptedString;
     list CipheredBytes;
     integer n;
     integer n;
     integer counter;
     integer counter;
Line 359: Line 484:
     //setup the plaintext list
     //setup the plaintext list
     integer ThePerpetualMax;
     integer ThePerpetualMax;
 
    ThePerpetualMax = llStringLength(plaintext);
         CiphertextBytes = llCSV2List(ciphertext);
    for(counter = 0; counter < ThePerpetualMax; counter++)
     //setup the password. Use i since we already have it.
    {
     ThePerpetualMax = llGetListLength(CiphertextBytes);
         PlaintextBytes = (PlaintextBytes = []) + PlaintextBytes + [UTF8ToUnicodeInteger(llGetSubString(plaintext,counter,counter))];
       
     }
     ThePerpetualMax = llGetListLength(PlaintextBytes);
     for(counter=0;counter < ThePerpetualMax;counter++)
     for(counter=0;counter < ThePerpetualMax;counter++)
     {
     {
Line 375: Line 503:
             n = (llList2Integer(theState, i) + llList2Integer(theState, j)) % 256;
             n = (llList2Integer(theState, i) + llList2Integer(theState, j)) % 256;
             //The nth element of the state array is then combined with the message byte, using a bit by bit exclusive-or operation, to form the output byte.
             //The nth element of the state array is then combined with the message byte, using a bit by bit exclusive-or operation, to form the output byte.
             ByteToAdd = llList2Integer(theState, n) ^ llList2Integer(CiphertextBytes, counter);
             ByteToAdd = llList2Integer(theState, n) ^ llList2Integer(PlaintextBytes, counter);
             DecryptedString = DecryptedString + UnicodeIntegerToUTF8(ByteToAdd);
             CipheredBytes = (CipheredBytes = []) + CipheredBytes + [ByteToAdd];
     }
     }
     return DecryptedString;
   
   
     return llList2CSV(CipheredBytes);
}
}
default
default
Line 385: Line 515:
     touch_start(integer total_number)
     touch_start(integer total_number)
     {
     {
        llSay(0, (string)llGetFreeMemory());
         llSay(0, "Key is 'Key', plaintext is 'Plaintext'. Output:");
         llSay(0, "Key Setup");
         KeySetup("Key","");
         KeySetup("Key","");
         llSay(0, (string)llGetFreeMemory());
         llSay(0,"Key setup complete. Encrypting now...");
         llSay(0,"Decrypting test string.");
         llSay(0,EncryptText("Plaintext"));
        llSay(0,DecryptText("187, 243, 22, 232, 217, 64, 175, 10, 211"));
        //will output 187, 243, 22, 232, 217, 64, 175, 10, 211
        llSay(0, (string)llGetFreeMemory());
     }
     }
}
}
</lsl>
</source>
==Test Vectors for ARCFOUR==
Test vectors are from [http://www.bookrags.com/wiki/RC4 RC4 Summary]
 
This vectors are not official, but are handy for anyone testing an ARCFOUR implementation.
 
RC4( "Key", "Plaintext" ) == BBF316E8D940AF0AD3
In decimal, the output is "187, 243, 22, 232, 217, 64, 175, 10, 211"


{{#vardefine:sort|ARCFOUR Strong Encryption Implementation}}{{LSLC|Library}}{{LSLC|Examples}}
{{#vardefine:sort|ARCFOUR Strong Encryption Implementation}}{{LSLC|Library}}{{LSLC|Examples}}
  [[Category:LSL Encryption]]
  [[Category:LSL Encryption]]

Latest revision as of 13:58, 24 January 2015

ARCFOUR Strong Encryption Implementation - Linden Scripting Language (LSL) Version 1.0

Contributors

License

This work is licensed under a Creative Commons Attribution 3.0 Unported License

You are free:

  • to Share — to copy, distribute and transmit the work
  • to Remix — to adapt the work

Under the following conditions

  • Attribution. You must attribute the work in the manner specified by the author or licensor (but not in any way that suggests that they endorse you or your use of the work).
  1. For any reuse or distribution, you must make clear to others the license terms of this work. The best way to do this is with a link to this web page.
  2. Any of the above conditions can be waived if you get permission from the copyright holder.
  3. Nothing in this license impairs or restricts the author's moral rights.

Disclaimer

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Documentation

The same password can be used more than once, but the nonce always has to be different. (something random). You can use this to secure inter-object communication by having a shared (but secret) password, and have the two objects exchange a nonce beforehand. A warning, there is no garbage fitering, so the state array could be polluted by someone injecting fake strings. To prevent this, the first thing exchanged encrypted could be a negative port number, and the two objects could listen only for each other's key. For proper decryption, messages must arrive in the exact order they were encrypted.

The key setup is very slow, but encryption itself is quite fast.

Note: The EncryptText() and DecryptText() functions are NOT compatible. They CANNOT be used in the same script.

About ARCFOUR

ARCFOUR (also known by the trademarked name RC4) is the most popular stream cipher still in use today. It was invented in 1987 by Ron Rivest of RSA Security. It is used in high security protocals such as Secure Sockets Layer (SSL).

ARCFOUR works by creating a pseudorandom stream of bits, and then XORing that stream with the plaintext to produce the ciphertext. Since it operates in this manner, the same identical key should never be used twice. This implementation works to prevent that by adding a nonce to the end of the key.

About this implementation

This implementation of ARCFOUR performs 128-bit encryption. It uses the MD5sum of the password and the nonce to prevent all known related key attacks against ARCFOUR.

Each script contains two functions. One to set up the key, and another to perform the actual encryption and decryption. Additional calls to the encrypt or decrypt function will continue to encrypt with the same key stream.

To encrypt, call the following functions:

KeySetup(string password, string nonce)
string EncryptText(string plaintext)

The nonce should be a randomly generated string and should only be used once.

This will return a hex string.

To decrypt, call the following functions:

KeySetup(string password, string nonce)
string DecryptText(string ciphertext)

using the same nonce.

Changes

Version 1.3

  • Corrected typecasting error. Anyone using this script prior to 2009-08-22 MUST update their copy to address this issue.

Version 1.2

  • Outputs to Hex instead of a CSV of bytes

Version 1.1

  • Hashes the key and the nonce with MD5, as per the RSA Laboratories suggestion. This prevents all related key attacks against ARCFOUR.
  • Memory optimizations

Version 1.0

  • Initial Release

Future Recommendations

  • Support UTF-8
  • By version 2.0, have a full protocol with automatic nonce generation, exchange, and object key filtering.

Code

Encryption

//This is an exact implementation of ARCFOUR that passes the test vectors.
 
//Note: Key setup is a long
//process taking over 20 seconds.
 
//Notes on use: The same password can be used more than once, but the nonce always has to be different
//(something random). You can use this to secure inter-object communication by having a shared (but secret)
//password, and have the two objects exchange a nonce beforehand. A warning, there is no garbage fitering,
//so the state array could be polluted by someone injecting fake strings. 
//To prevent this, the first thing exchanged encrypted
//could be a negative port number, and the two objects could listen only for each other's key. For proper
//decryption, messages must arrive in the exact order they were encrypted.
 
//This work is licensed under a http://creativecommons.org/licenses/by/3.0/ Creative Commons Attribution 3.0 Unported License
//Please credit Nekow42 Zarf.
//Nardok Corrimal, and Strife Onizuka contributed and need credit too.
list theState;
integer i;
integer j;
//===================================================//
//                 Combined Library                  //
//             "Nov  3 2007", "00:46:15"             //
//  Copyright (C) 2004-2007, Strife Onizuka (cc-by)  //
//    http://creativecommons.org/licenses/by/3.0/    //
//===================================================//
//{
integer UTF8ToUnicodeInteger(string input)//Mono Safe, LSO Safe
{
    integer result = llBase64ToInteger(llStringToBase64(input = llGetSubString(input,0,0)));
    if(result & 0x80000000){
        return 0;
    }
    return result >> 24;
}
 
 
string UnicodeIntegerToUTF8(integer input)//Mono Safe, LSO Safe
{
    integer bytes = llCeil(llLog(input) / 0.69314718055994530941723212145818);
    bytes = (input >= 0x80) * (bytes + ~(((1 << bytes) - input) > 0)) / 5;//adjust
    string result = "%" + byte2hex((input >> (6 * bytes)) | ((0x3F80 >> bytes) << !bytes));
    while (bytes)
        result += "%" + byte2hex((((input >> (6 * (bytes = ~-bytes))) | 0x80) & 0xBF));
    return llUnescapeURL(result);
}
 
string byte2hex(integer x)//Mono Safe, LSO Safe
{//Helper function for use with unicode characters.
    integer y = (x >> 4) & 0xF;
    return llGetSubString(hexc, y, y) + llGetSubString(hexc, x & 0xF, x & 0xF);
}//This function would benifit greatly from the DUP opcode, it would remove 19 bytes.
 
string hexc="0123456789ABCDEF";
 
//} Combined Library
KeySetup(string password, string nonce)
{
    //All addition is done modulous
    list WholePassword;
    list statePartOne = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75];
    list statePartTwo = [76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150];
    list statePartThree = [151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225];
    list statePartFour = [226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255];
    theState = (statePartOne = statePartTwo = statePartThree = statePartFour = []) + statePartOne + statePartTwo + statePartThree + statePartFour;
    integer swapper;
    //setup the plaintext list
    integer ThePerpetualMax;
    //setup the password. Use i since we already have it.
    //New addition in 1.1, use an MD5 hashing function on the password
    password = llMD5String(password + nonce,0);
    for(i = 0; i < 31; i+=2) 
    {
        WholePassword = (WholePassword = []) + WholePassword + (integer)("0x"+llGetSubString(password,i,i+1));
    }
    //Now WholePassword is equal to a list of the hex digits of the MD5 of the password plus the nonce
    i=0;
    j=0;
    //mix init, this sets up the ARCFOUR PRNG
        ThePerpetualMax = llGetListLength(WholePassword);
        for(i=0 ; i < 256; i++)
        {
            //Mix step one
            //Add to the variable j the contents of the ith element of the state array and the nth element of the key, where n is equal to i modulo the length of the key.
            j = (j + llList2Integer(theState, i) + llList2Integer(WholePassword,i % ThePerpetualMax)) % 256;
            //Mix step two. Swap the ith element and the jth element of the state array
            swapper = llList2Integer(theState, i);
            theState=llListReplaceList(theState, [llList2Integer(theState,j)] ,i,i);
            theState=llListReplaceList(theState, [swapper],j,j);
        }
    i=0;
    j=0;
    //Key setup is complete!
 
}
string EncryptText(string plaintext)
//This does not repeat key setup, but continues with the same state array and values for i and j
{
    //All addition is done modulous
    list PlaintextBytes;
    string CipheredBytes;
    integer n;
    integer counter;
    integer swapper;
    integer ByteToAdd;
    //setup the plaintext list
    integer ThePerpetualMax;
    ThePerpetualMax = llStringLength(plaintext);
    for(counter = 0; counter < ThePerpetualMax; counter++) 
    {
        PlaintextBytes = (PlaintextBytes = []) + PlaintextBytes + [UTF8ToUnicodeInteger(llGetSubString(plaintext,counter,counter))];
 
    }
    ThePerpetualMax = llGetListLength(PlaintextBytes);
    for(counter=0;counter < ThePerpetualMax;counter++)
    {
        //The variable i is incremented by one
        i = (i + 1) % 256;
        //The contents of the ith element of the state array is then added to j
        j = (j + llList2Integer(theState, i)) % 256;
        //The ith and jth elements of the state array are swapped and their contents are added together to form a new value n.
            swapper = llList2Integer(theState, i);
            theState=llListReplaceList(theState, [llList2Integer(theState,j)] ,i,i);
            theState=llListReplaceList(theState, [swapper],j,j);
            n = (llList2Integer(theState, i) + llList2Integer(theState, j)) % 256;
            //The nth element of the state array is then combined with the message byte, using a bit by bit exclusive-or operation, to form the output byte.
            ByteToAdd = llList2Integer(theState, n) ^ llList2Integer(PlaintextBytes, counter);
            CipheredBytes = CipheredBytes + byte2hex(ByteToAdd);
    }
 
 
    return CipheredBytes;
}
default
{
 
    touch_start(integer total_number)
    {
        llOwnerSay("Key is 'Key', plaintext is 'Plaintext'. Output:");
        KeySetup("Key","");
        llOwnerSay("Key setup complete. Encrypting now...");
        llOwnerSay(EncryptText("Plaintext"));
    }
}

Decryption

//This is an exact implementation of ARCFOUR that passes the test vectors.
 
//Note: Key setup is a long
//process taking over 20 seconds.
 
//Notes on use: The same password can be used more than once, but the nonce always has to be different
//(something random). You can use this to secure inter-object communication by having a shared (but secret)
//password, and have the two objects exchange a nonce beforehand. A warning, there is no garbage fitering,
//so the state array could be polluted by someone injecting fake strings. 
//To prevent this, the first thing exchanged encrypted
//could be a negative port number, and the two objects could listen only for each other's key. For proper
//decryption, messages must arrive in the exact order they were encrypted.
 
//This work is licensed under a http://creativecommons.org/licenses/by/3.0/ Creative Commons Attribution 3.0 Unported License
//Please credit Nekow42 Zarf.
//Nardok Corrimal, and Strife Onizuka contributed and need credit too.
list theState;
integer i;
integer j;
//===================================================//
//                 Combined Library                  //
//             "Nov  3 2007", "00:46:15"             //
//  Copyright (C) 2004-2007, Strife Onizuka (cc-by)  //
//    http://creativecommons.org/licenses/by/3.0/    //
//===================================================//
//{
integer UTF8ToUnicodeInteger(string input)//Mono Safe, LSO Safe
{
    integer result = llBase64ToInteger(llStringToBase64(input = llGetSubString(input,0,0)));
    if(result & 0x80000000){
        return 0;
    }
    return result >> 24;
}
 
 
string UnicodeIntegerToUTF8(integer input)//Mono Safe, LSO Safe
{
    integer bytes = llCeil(llLog(input) / 0.69314718055994530941723212145818);
    bytes = (input >= 0x80) * (bytes + ~(((1 << bytes) - input) > 0)) / 5;//adjust
    string result = "%" + byte2hex((input >> (6 * bytes)) | ((0x3F80 >> bytes) << !bytes));
    while (bytes)
        result += "%" + byte2hex((((input >> (6 * (bytes = ~-bytes))) | 0x80) & 0xBF));
    return llUnescapeURL(result);
}
 
string byte2hex(integer x)//Mono Safe, LSO Safe
{//Helper function for use with unicode characters.
    integer y = (x >> 4) & 0xF;
    return llGetSubString(hexc, y, y) + llGetSubString(hexc, x & 0xF, x & 0xF);
}//This function would benefit greatly from the DUP opcode, it would remove 19 bytes.
 
string hexc="0123456789ABCDEF";
 
//} Combined Library
KeySetup(string password, string nonce)
{
    list WholePassword;
    list statePartOne = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75];
    list statePartTwo = [76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150];
    list statePartThree = [151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225];
    list statePartFour = [226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255];
    theState = (statePartOne = statePartTwo = statePartThree = statePartFour = []) + statePartOne + statePartTwo + statePartThree + statePartFour;
    integer swapper;
    //setup the plaintext list
    integer ThePerpetualMax;
    //setup the password. Use i since we already have it.
    //New addition in 1.1, use an MD5 hashing function on the password
    password = llMD5String(password + nonce,0);
    for(i = 0; i < 31; i+=2) 
    {
        WholePassword = (WholePassword = []) + WholePassword + (integer)("0x"+llGetSubString(password,i,i+1));
    }
    //Now WholePassword is equal to a list of the hex digits of the MD5 of the password plus the nonce
    i=0;
    j=0;
    //mix init, this sets up the ARCFOUR PRNG
        ThePerpetualMax = llGetListLength(WholePassword);
        for(i=0 ; i < 256; i++)
        {
            //Mix step one
            //Add to the variable j the contents of the ith element of the state array and the nth element of the key, where n is equal to i modulo the length of the key.
            j = (j + llList2Integer(theState, i) + llList2Integer(WholePassword,i % ThePerpetualMax)) % 256;
            //Mix step two. Swap the ith element and the jth element of the state array
            swapper = llList2Integer(theState, i);
            theState=llListReplaceList(theState, [llList2Integer(theState,j)] ,i,i);
            theState=llListReplaceList(theState, [swapper],j,j);
        }
    i=0;
    j=0;
    //Key setup is complete!
}
string DecryptText(string ciphertext)
{
//This does not repeat key setup, but continues with the same state array and values for i and j
    list CiphertextBytes;
    string DecryptedString;
    integer n;
    integer counter;
    integer swapper;
    integer ByteToAdd;
    //setup the plaintext list
    integer ThePerpetualMax;
        ThePerpetualMax = llStringLength(ciphertext) - 1;
        for(counter = 0; counter < ThePerpetualMax; counter+=2)
        {
            CiphertextBytes = (CiphertextBytes = []) + CiphertextBytes + (integer)("0x"+llGetSubString(ciphertext,counter,counter+1));
        }
    //setup the password. Use i since we already have it.
    ThePerpetualMax = llGetListLength(CiphertextBytes);
    for(counter=0;counter < ThePerpetualMax;counter++)
    {
        //The variable i is incremented by one
        i = (i + 1) % 256;
        //The contents of the ith element of the state array is then added to j
        j = (j + llList2Integer(theState, i)) % 256;
        //The ith and jth elements of the state array are swapped and their contents are added together to form a new value n.
            swapper = llList2Integer(theState, i);
            theState=llListReplaceList(theState, [llList2Integer(theState,j)] ,i,i);
            theState=llListReplaceList(theState, [swapper],j,j);
            n = (llList2Integer(theState, i) + llList2Integer(theState, j)) % 256;
            //The nth element of the state array is then combined with the message byte, using a bit by bit exclusive-or operation, to form the output byte.
            ByteToAdd = llList2Integer(theState, n) ^ llList2Integer(CiphertextBytes, counter);
            DecryptedString = DecryptedString + UnicodeIntegerToUTF8(ByteToAdd);
    }
    return DecryptedString;
}
default
{
 
    touch_start(integer total_number)
    {
        llOwnerSay(  "Key Setup");
        KeySetup("Key","");
        llOwnerSay( "Decrypting test string.");
        llOwnerSay(DecryptText("506C61696E74657874"));
    }
}

Test Vectors for ARCFOUR

Test vectors are from RC4 Summary

This vectors are not official, but are handy for anyone testing an ARCFOUR implementation.

RC4( "Key", "Plaintext" ) == BBF316E8D940AF0AD3

In decimal, the output is "187, 243, 22, 232, 217, 64, 175, 10, 211"

This code shows that it passes the test vector:

list theState;
integer i;
integer j;
//===================================================//
//                 Combined Library                  //
//             "Nov  3 2007", "00:46:15"             //
//  Copyright (C) 2004-2007, Strife Onizuka (cc-by)  //
//    http://creativecommons.org/licenses/by/3.0/    //
//===================================================//
//{
integer UTF8ToUnicodeInteger(string input)//Mono Safe, LSO Safe
{
    integer result = llBase64ToInteger(llStringToBase64(input = llGetSubString(input,0,0)));
    if(result & 0x80000000){
        return 0;
    }
    return result >> 24;
}
 

string UnicodeIntegerToUTF8(integer input)//Mono Safe, LSO Safe
{
    integer bytes = llCeil(llLog(input) / 0.69314718055994530941723212145818);
    bytes = (input >= 0x80) * (bytes + ~(((1 << bytes) - input) > 0)) / 5;//adjust
    string result = "%" + byte2hex((input >> (6 * bytes)) | ((0x3F80 >> bytes) << !bytes));
    while (bytes)
        result += "%" + byte2hex((((input >> (6 * (bytes = ~-bytes))) | 0x80) & 0xBF));
    return llUnescapeURL(result);
}

string byte2hex(integer x)//Mono Safe, LSO Safe
{//Helper function for use with unicode characters.
    integer y = (x >> 4) & 0xF;
    return llGetSubString(hexc, y, y) + llGetSubString(hexc, x & 0xF, x & 0xF);
}//This function would benifit greatly from the DUP opcode, it would remove 19 bytes.

string hexc="0123456789ABCDEF";

//} Combined Library
KeySetup(string password, string nonce)
{
    //All addition is done modulous
    list WholePassword;
    list statePartOne = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75];
    list statePartTwo = [76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150];
    list statePartThree = [151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225];
    list statePartFour = [226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255];
    theState = statePartOne + statePartTwo + statePartThree + statePartFour;
    statePartOne = [];
    statePartTwo = [];
    statePartThree = [];
    statePartFour = [];
    integer swapper;
    //setup the plaintext list
    integer ThePerpetualMax;
    //setup the password. Use i since we already have it.
    ThePerpetualMax = llStringLength(password);
    for(i = 0; i < ThePerpetualMax; i++) 
    {
        WholePassword = (WholePassword = []) + WholePassword + [UTF8ToUnicodeInteger(llGetSubString(password,i,i))];
    }
    ThePerpetualMax = llStringLength(nonce);
    for(i = 0; i < ThePerpetualMax; i++) 
    {
        WholePassword = (WholePassword = []) + WholePassword + [UTF8ToUnicodeInteger(llGetSubString(nonce,i,i))];
    }
    //Now WholePassword is equal to a list of the ASCII values of the password + the nonce
    i=0;
    j=0;
    //mix init, this sets up the ARCFOUR PRNG
        ThePerpetualMax = llGetListLength(WholePassword);
        for(i=0 ; i < 256; i++)
        {
            //Mix step one
            //Add to the variable j the contents of the ith element of the state array and the nth element of the key, where n is equal to i modulo the length of the key.
            j = (j + llList2Integer(theState, i) + llList2Integer(WholePassword,i % ThePerpetualMax)) % 256;
            //Mix step two. Swap the ith element and the jth element of the state array
            swapper = llList2Integer(theState, i);
            theState=llListReplaceList(theState, [llList2Integer(theState,j)] ,i,i);
            theState=llListReplaceList(theState, [swapper],j,j);
        }
    i=0;
    j=0;
    //Key setup is complete!
    
}
string EncryptText(string plaintext)
//This does not repeat key setup, but continues with the same state array and values for i and j
{
    //All addition is done modulous
    string Output;
    list PlaintextBytes;
    list CipheredBytes;
    integer n;
    integer counter;
    integer swapper;
    integer ByteToAdd;
    //setup the plaintext list
    integer ThePerpetualMax;
    ThePerpetualMax = llStringLength(plaintext);
    for(counter = 0; counter < ThePerpetualMax; counter++) 
    {
        PlaintextBytes = (PlaintextBytes = []) + PlaintextBytes + [UTF8ToUnicodeInteger(llGetSubString(plaintext,counter,counter))];
        
    }
    ThePerpetualMax = llGetListLength(PlaintextBytes);
    for(counter=0;counter < ThePerpetualMax;counter++)
    {
        //The variable i is incremented by one
        i = (i + 1) % 256;
        //The contents of the ith element of the state array is then added to j
        j = (j + llList2Integer(theState, i)) % 256;
        //The ith and jth elements of the state array are swapped and their contents are added together to form a new value n.
            swapper = llList2Integer(theState, i);
            theState=llListReplaceList(theState, [llList2Integer(theState,j)] ,i,i);
            theState=llListReplaceList(theState, [swapper],j,j);
            n = (llList2Integer(theState, i) + llList2Integer(theState, j)) % 256;
            //The nth element of the state array is then combined with the message byte, using a bit by bit exclusive-or operation, to form the output byte.
            ByteToAdd = llList2Integer(theState, n) ^ llList2Integer(PlaintextBytes, counter);
            CipheredBytes = (CipheredBytes = []) + CipheredBytes + [ByteToAdd];
    }
    
    
    return llList2CSV(CipheredBytes);
}
default
{

    touch_start(integer total_number)
    {
        llSay(0, "Key is 'Key', plaintext is 'Plaintext'. Output:");
        KeySetup("Key","");
        llSay(0,"Key setup complete. Encrypting now...");
        llSay(0,EncryptText("Plaintext"));
         //will output 187, 243, 22, 232, 217, 64, 175, 10, 211
    }
}