LSL Wiki Mirror : llXorBase64Strings

HomePage :: PageIndex :: RecentChanges :: RecentlyCommented :: UserSettings ::
This function has been deprecated in favour of llXorBase64StringsCorrect.

string llXorBase64Strings(string s1, string s2)

Performs an xor on two Base64 strings and returns a Base64 string. s2 repeats if it is shorter than s1.

This function is for encrypting data. Put your data in s1 and your key in s2 to encrypt or put the encrypted data into s1 with the same key in s2 to decrypt again.

Use llStringToBase64 and llBase64ToString for converting to and from Base64 respectively.

While fine for most SL encryption purposes, Xor'ing a string with a password is considered a very weak encryption algorithm. Do not use this for anything that will cost you RL $ if compromised (such as an automated RL - SL currency exchanger). Also, consider carefully how much damage a malicious party might be able to wreak if your encryption is hacked.

This type of encryption is similar to something called a one time pad that is if s2 is the same length as s1, is generated randomly (not pseudo randomly), and s2 is used only once. When used in this way this function is provably unbreakable. However implementing this function in this way is difficult to say the least. However with a little ingenuity and some creative problem solving we might be able to create different variations of this with acceptable reductions in security with minimal resource requirements(execution time, memory). IE have a new key created for each message maybe based on some universally changing constant like the server timestamp.

And as it turns out the quickest way to weaken xor type encryption is to use the key more than once, either by repeating a small key across a longer message or to use the same key on more than one message. It is only strong if the key is used only once. So this type of encryption when used with the same key over and over is supposedly rather trivial to break.

Example:
Note: this example code always produces the same result as llXorBase64StringsCorrect, but only the same result as llXorBase64Strings when s2 is longer than s1 (unless s2 is a multiple of 3 bytes in length -see discussion at end of page).

default {
    state_entry() {
        string  data = "I like bananas."; // the data we want to encrypt
        string  pass = "monkey"; // the encryption key
        
        string  crypt;  // for storing the encrypted data
        
        // convert both data and key to base64 and encrypt the data with the key
        // then store the encrypted data in 'crypt'
        crypt = llXorBase64Strings(llStringToBase64(data), llStringToBase64(pass));
        
        llSay(0, crypt); // this could now be used to email the data around
        // someone without the correct key wouldn't be able to (easily) get the plaintext
        
        // decrypt it again, using the same key, and convert back to plaintext
        llSay(0, llBase64ToString(llXorBase64Strings(crypt, llStringToBase64(pass))));
        // as you can see, applying the same key to encrypted data again decrypts it
    }
}

Tip: If you see an "=" character in the middle of your encrypted string, you probably forgot to first convert your data to Base64.


If you want to exchange data that passed through this function from outside of LSL, the following Python snippet might be helpful to you:

from base64 import b64encode, b64decode

def XorBase64Strings(s1, s2):
	"""Emulates the LSL function llXorBase64Strings:
	Performs an exclusive or on two Base64 strings and returns a Base64 string. s2 repeats if it is shorter than s1."""
	s1 = b64decode(s1)		# internally this works with
	s2 = b64decode(s2)		# the decoded strings
	while len(s2) < len(s1): s2 += s2		# repeat s2 until it's at least as long as s1
	out = ""
	for index in range(len(s1)):		# take each character from s1
		out += chr( ord(s1[index]) ^ ord(s2[index]) )		# and XOR it with one from s2
	return b64encode(out)

data = "I like bananas."		# the data we want to encrypt
ckey = "monkey"		# the encryption key

crypt = XorBase64Strings(b64encode(data), b64encode(ckey))		# encode & encrypt

print crypt	# the base64 encoded result

decrypt = b64decode(XorBase64Strings(crypt, b64encode(ckey)))		# decrypt & decode

print decrypt		# the decrypted and base64 decoded data

# verify
if decrypt == data: print "This function seems to work..."
# this string is from calling the LSL function with the same parameters
if crypt == "JE8CAg4cTQ0PBQQXDBxA": print "... in the same way as the LSL function."

This function does the same thing as the one above, only in PHP:
<?php
function XorBase64Strings($s1$s2)
{
    
$s1 base64_decode($s1);
    
$s2 base64_decode($s2);

    while(
strlen($s2) < strlen($s1))
        
$s2 .= $s2;

    return 
base64_encode($s1 $s2);
}
?>


'1.9.0(18)' vs 'pre/post-1.9.0(18)' implementation


The 1.9.0(18) Grid update introducted a new implementation of this function. It was re-written to fix a sim crash bug, however, the output deviates from the original pre-update version. The 1.9.0(18) implementation is simpler. The difference is shown below through pseudo-code. The 1.9.0(21) update reverted this function's behavior to the pre-1.9.0(18) behavior and introduced a new llXorBase64StringsCorrect with the 1.9.0(18) behavior.

1.9.0(18) implementation


1. Decode s1 and s2 from Base64 into byte strings
2. Bit-wise XOR corresponding bytes of s1 and s2 in sequence. If s2 is shorter than s1, it is simply repeated (i.e. the index into s2 is the index into s1 % length of s2).
3. Re-encode the result byte string into Base64 encoding and return as result

Pre/Post-1.9.0(18) implementation


This function's original implementation is more efficient, but gives a different result than the new one, only if s2 is shorter than s1 and s2 isn't a multiple of 3 bytes/chars long. As of 1.9.0(21), this is the behavior of this function.

1. Convert the Base64 encoding of s1 and s2 into a sequence of 6bit values. (by converting each Base64 character into its 6bit index - see Wikipedia Base64).
2. Bit-wise XOR corresponding 6bit values of s1 and s2 in sequence. If s2 is shorter than s1, it is simply repeated (i.e. the index into s2 is the index into s1 % length of s2).
3. Re-convert the resulting sequence of 6bit values into their Base64 characters (by using the 6bit values as indices into the Base64 character set)

The reason this differs from the new implemenation, is that the length of the byte string represented by s2 may not be a multiple of 3 bytes long. The initial encoding of s2 into Base64 form involves taking each set of 3 bytes (3x8=24bits) 6 bits at a time and converting to 4 Base64 characters (4x6=24bits). However, if not a multiple of 3 bytes there would have been either 2bytes (16bits) or 1byte(8bits) left over at the end. The Base64 encoding scheme dictates padding to a multiple of 6bits with a 0 bit to obtain the final Base64 character. Hence the Base64 encoded form of s2 may have either 4bits (16 - 6x2) or 2 bits (8 - 1x6) of 0 padding at its end. This padding is included when s2 is repeated above in the case when s2 is shorter than s1. Hence the different output.

Here is an example of different output.

If you XOR these two strings using llXorBase64Strings:
llXorBase64Strings("OnAxODcyMzRhYmNkZWZnaGkgYWJjZEBlZmdoLmlqa2wubW5v", "QUJDREVGR0hJSktMTQ==")

the 'pre/post-1.9.0(18)' implementation will produce
"ezJyfHJ0dHwoKCgoKGJzTF1kNQYX4NTB0qO4byspLyloKiYm"

the '1.9.0(18)' implementation gave:
"ezJyfHJ0dHwoKCgoKCclKy1lJyUrLQouKiopbCouLippJScl"

(which is the same result you'll get from the new llXorBase64StringsCorrect function)

(If you really need code compatible with the current ('pre/post-1.9.0(18)') implementation of llXorBase64Strings, IM Cenji Neutra for a C# implementation)


Functions | DeprecatedFunctions | Crypto | Communications
There are 9 comments on this page. [Display comments/form]