From Second Life Wiki
Jump to navigation Jump to search


Function: string llXorBase64( string str1, string str2 );

Correctly performs an exclusive or on two Base64 strings.
Returns a string that is a Base64 XOR of str1 and str2.

• string str1 Base64 string
• string str2 Base64 string

str2 repeats if it is shorter than str1. If the inputs are not Base64 strings the result will be erratic.
Be sure to read the Cryptography section before designing a cryptographic algorithm.


  • During the conversion to a byte array the last (bitcount % 8) are discarded from both str1 and str2. See Implementation for details.

Important Issues

~ All Issues ~ Search JIRA for related Bugs
   llXorBase64 returns wrong result after second str2 repeat


        // Use a HARD password ! with caps nocaps numbers and symbols !
        string pass = "P4s5Wo_rD";
        string data = "I am some ver important data.";
        // Enccrypting the data:
        string crypt = llXorBase64(llStringToBase64(data), llStringToBase64(pass));
        // Say the mess you made to Owner
        // DeCrypting the data and say back to owner:
        llOwnerSay(llBase64ToString(llXorBase64(crypt, llStringToBase64(pass))));
// Stronger encryption - generates a random encrypted string.
//safe character set
string ASCII = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

//convert integer to characters
string chr(integer i)
    return llGetSubString(ASCII, i, i);

//for generating a random string.
string salt(integer amount) {
    string salt = "";
    integer i;
    //for length of salt , generate characters
    for(i = 0; i < amount; i++) {
        salt += chr((integer)llFrand(llStringLength(ASCII)));
    return salt;

    touch_start(integer n) {
        //generates a random salt to add to the string.
        //put salt on the end so that even if the data is corrupted by SVC-6362, it is unimportant data
        string data = "I am some very important data." + salt((integer)llFrand(5)+ 5);

        string pass = "password";

        // Encrypting the data:
        string crypt = llXorBase64(llStringToBase64(data), llStringToBase64(pass));
        // Say the mess you made to Owner
        // DeCrypting the data and say back to owner: Remember to remove the salt when needed ;)
        llOwnerSay(llBase64ToString(llXorBase64(crypt, llStringToBase64(pass))));




The XOR is performed by converting each Base64 strings str1 and str2 into a byte arrays and then XORing the two byte arrays. Finally converting the resulting byte array back into Base64 and returning it to the user.

However during the conversion to byte arrays the last (bitcount % 8) are discard. LSL treats Base64 strings as 8-bit byte arrays, not arrays of 6-bit bytes.


While the information provided in this section is by no means exhaustive, it should give the reader enough information that they can ask the right questions and guide their future reading. Writing cryptographic algorithms should not be attempted without an understanding of cryptography as the results may otherwise be disastrous.

In cryptography, a secret is a bit of input information that is used to transform the cryptographic algorithm. The secret is not mutated with subsequent runs of the algorithm. A seed is also used to transform the algorithm but unlike the secret it does mutate; the method of mutation is part of the algorithm.

To effectively use a secret and a seed you need to keep secret at-least two of the following: 1) the algorithm, 2) the seed, or 3) the secret.

Believe it (or not) that keeping your algorithm secret will not protect you. The attack vectors that can be used against XOR algorithms require little or no knowledge of the algorithm. The best protection is a strong algorithm that uses multiple secrets/seeds. In this way you can still satisfy the prior precondition by interpreting the numbered items as categories. Keep in mind how the secrets and seeds are used, an attacker need not determine the secrets, only intermediate static values and the relationship between intermediate values for the algorithm to be broken. Do not give the attacker secrets and seeds that render blocks of living code, static. The more information provide to the attacker, the simpler your algorithm becomes.

Best Practices

As a cryptographic technique, XOR is weak and there are several attacks that can be leveraged to determine the XOR inputs. Depending upon how the secrets are used cracking a single message could expose the input secrets, resulting in the derived algorithm being broken.

Keep your secrets secret. Use a seeded trap door function to shake up the bits of the secret before using with the XOR and change the seed often.

Do not XOR a value by two differing length values without knowing the implications. It may seem like a good idea but what it actually does is link the fields. While it will give you a longer key value (the "Wikipedia logo"Least Common Multiple in length), the fields will be linked such that there are really only as many fields as the "Wikipedia logo"Greatest Common Divisor. The number of unique fields determines the theoretical maximum number of keys an attacker has to try.

Unique_Key_Fields = Greatest_Common_Divisor(lengths_of_keys) * number_of_keys

Attack Vectors

First thing you need to know is that XOR is limited poly-alphabetic cipher. The attack vectors that work for poly-alphabetic ciphers work for XOR.

  • Probability: In English, letters have different probabilities of occurring because of grammar and spelling rules. XOR does not hide the letter probabilities. This attack only works when the keys are many times smaller than the message.
  • UTF-8 Rules: When you convert a string to Base64, UTF-8 encoding is used first. If you assume the inputs are valid UTF-8 encodes some bits can be determined purely upon examination. This is most useful in determining the length of the key.
  • Plain Text: The user captures outputs for known inputs can expose weaknesses in the key.
  • Brute force: Attacking the key, secret and/or seed

How to decode with php

PHP script can be found in LSL Wiki. An older version of the code can be found here: llXorBase64StringsCorrect in PHP.

Implementation of SignpostMarv's decoder in PHP.

 * Description: Implementation of SignpostMarv's php base64 decoder.
 * Author: Kopilo Hallard
 * License: http://creativecommons.org/licenses/by-sa/2.5/

/* Cipher (s1 data, s2 key) */
function llXorBase64($s1, $s2) {
    $s1 = base64_decode($s1); $l1 = strlen($s1);
    $s2 = base64_decode($s2);
    if($l1 > strlen($s2)) $s2 = str_pad($s2, $l1, $s2, STR_PAD_RIGHT);
    return base64_encode($s1 ^ $s2);

$Skey = "password";
$para1 = $_POST["para1"];

$result = llXorBase64($para1, base64_encode($Skey));

echo "Encrypted Data: ".$para1.PHP_EOL;
echo "Unencrypted ".base64_decode($result);


How to decode with java

Remember to URLEncode your BASE64 hash if you transfer it vie GET...

       String BASE64datahash = "error";
       String passhash = "error";

       try {
        //URLDecode the URL encoded encrypted data
        BASE64datahash = java.net.URLDecoder.decode("KhoFRRYaAUMbEVU%3D", "UTF-8"); //KhoFRRYaAUMbEVU%3D
        System.out.println("BASE64datahash: " + BASE64datahash); //KhoFRRYaAUMbEVU=

        //create an array of BASE64 data
        char[] BASE64data = BASE64datahash.toCharArray();
        char[] dataUB = new String(new BASE64Decoder().decodeBuffer(new String(BASE64data))).toCharArray(); //BASE64 decode the data
        System.out.println("encrypted data (but base64 decoded) [dataUB]: " + new String(dataUB));

        //Encode the secred key/password to BASE64 (Just to show how to use BASE64Encoder)
        //String BASE64password = new String(new BASE64Encoder().encodeBuffer("supersecretpassword".getBytes()));
        //System.out.println("BASE64password: " + new String(BASE64password));

        //create array of BASE64 key/password
        //char[] key = BASE64password.toCharArray();
        //char[] keyUB = new String(new BASE64Decoder().decodeBuffer(new String(key))).toCharArray();
        char[] keyUB = "supersecretpassword".toCharArray(); 
        System.out.println("plaintext key/password [keyUB]: " + new String(keyUB));

        //XOR data array chars with corresponding key/password array chars
        int k=0;
        for (int i = 0; i < dataUB.length; i++) {
            dataUB[i] = (char) (dataUB[i] ^ keyUB[k]);

            //Loop to start of the key if the key is too short
            if (k == keyUB.length)

        System.out.println("Decoded data [dataUB]: " + new String(dataUB));
        } catch (Exception ex) {

Deep Notes


All Issues

~ Search JIRA for related Issues
   llXorBase64StringsCorrect zero out last (bitcount % 8) bits of data.
   llXorBase64StringsCorrect returns wrong result when the 2nd string contains nulls
   llXorBase64 returns wrong result after second str2 repeat


function string llXorBase64( string str1, string str2 );