Welcome to the Second Life Forums Archive

These forums are CLOSED. Please visit the new forums HERE

Looking for 'real' encryption? Start here.

Morse Dillon
Lifetime Member
Join date: 11 Dec 2003
Posts: 142
03-13-2007 21:31
Scripters in SL commonly need encryption for objects such as vendors, et cetera. Most seem to come up with a sort of pseudo-encryption that relies primarily upon 'security through obscurity' and call it good enough. During the development of a recent project, my first requiring secure communications, I decided that I wouldn't settle for false security and wrote an LSL implementation of XTEA (eXtended Tiny Encryption Algorithm).

XTEA is considered as secure as other block ciphers such as IDEA, but has vastly lower computational requirements. As such, it is a good match for the execution speed of LSL. Where 'real' encryption is concerned, this is about as low-overhead as it gets and I've found that although there is some delay while the crypt occurs, it is plenty fast enough to be useful.

The source code can be found on the Second Life Wiki's LSL Portal. Documentation explaining the algorithm and the implementation is included in the source and I would strongly encourage anyone wishing to use this code to read it carefully.

You can find the source at:
https://wiki.secondlife.com/wiki/XTEA_Strong_Encryption_Implementation

Thanks, and I hope that you all can find this as valuable as I have.
_____________________
King Morse Dillon
King of Second Life
FlipperPA Peregrine
Magically Delicious!
Join date: 14 Nov 2003
Posts: 3,703
03-14-2007 05:49
Awesome work, Morse - this will definitely come in handy for passing data around the grid, and in and out of the grid. Thanks for putting this together!
_____________________
Peregrine Salon: www.PeregrineSalon.com - my consulting company
Second Blogger: www.SecondBlogger.com - free, fully integrated Second Life blogging for all avatars!
Escort DeFarge
Together
Join date: 18 Nov 2004
Posts: 681
03-14-2007 09:07
Observation: That's a lot of code to put in every script involved with secure comms.
Question: Have you done an encrypt/decrypt cycle load test on this... how many cycles per second can it do?
Observation: Nice code style. Very clear :)
_____________________
http://slurl.com/secondlife/Together
Ged Larsen
thwarted by quaternions
Join date: 4 Dec 2006
Posts: 294
03-14-2007 09:28
Nice work!

As your encryption code is released under GPL, are you requiring that all products that use it that are meant for public distribution also be open source?

For people who wish to comply with the terms of licensing, that would dramatically decrease interest in use of the code.

Or is there any other way to use the encryption code acceptably, without making GPL open source all the rest of a product? Perhaps by somehow including the encryption script in a MOD / COPY / TRANSFER script, but the remaining scripts that pass messages to it could remain closed source?

Please clarify :)

Edited to add: come to think of it, requiring all parts of all publicly distributed products to be open source defeats the purpose of the encryption, as the keys would also be viewable in the code, unless hand entered by the user each time. So, for the GPL'd encryption code to be useful, under strict licensing terms, it could only be used in products NOT meant for distribution.
_____________________
- LoopRez, flexi prim skirt generating tool
- LinkRez, a necklace chain generator
Morse Dillon
Lifetime Member
Join date: 11 Dec 2003
Posts: 142
03-14-2007 09:39
From: Escort DeFarge
Observation: That's a lot of code to put in every script involved with secure comms.
Question: Have you done an encrypt/decrypt cycle load test on this... how many cycles per second can it do?
Observation: Nice code style. Very clear :)



If script length becomes an issue, one can wrap this code in a link message handler. See the documentation in the code for caveats.

Thanks :)
_____________________
King Morse Dillon
King of Second Life
Morse Dillon
Lifetime Member
Join date: 11 Dec 2003
Posts: 142
03-14-2007 12:03
From: Ged Larsen
Nice work!
As your encryption code is released under GPL, are you requiring that all products that use it that are meant for public distribution also be open source?


I would say that using the code in its native form, by including it in the same source file as Non-GPL'd code would be contraindicated by the GPL. However, using the link message wrapper method that I mention in the documentation would be OK because it is functioning as a more-or-less monolithic piece of software.

Basically, putting your proprietary code in a separate script file and communicating with my code via a link message wrapper would satisfy the GPL. One caveat - if you do modify my code, any modifications must be re-released under the GPL as well.

From: someone

Or is there any other way to use the encryption code acceptably, without making GPL open source all the rest of a product? Perhaps by somehow including the encryption script in a MOD / COPY / TRANSFER script, but the remaining scripts that pass messages to it could remain closed source?

Yep, you're thinking along the right lines :)

From: someone

Edited to add: come to think of it, requiring all parts of all publicly distributed products to be open source defeats the purpose of the encryption, as the keys would also be viewable in the code, unless hand entered by the user each time. So, for the GPL'd encryption code to be useful, under strict licensing terms, it could only be used in products NOT meant for distribution.

First off, those keys are hard-coded but as I mention in the documentation any true usage of this XTEA implmentation will provide some form of key generation and exchange. This key could also be passed via link message and actually should not be hardcoded into my source.

To clarify what I think is a misunderstanding of the GPL requirements on your part though:
GPL'ed code used in a project isn't required to be readable by the end user (think about it - who can read a compiled binary executable?), but if it is used then the end user must have a means to obtain the GPL'ed code. This means that you must include a copy of the source with your project (a notecard would suffice) or tell the user where to go to download it.

I do think that perhaps I will go ahead and create a wrapper for this tonight, seeing as how it's quickly become a significant point of interest. I'll let you all know when it's ready :)
_____________________
King Morse Dillon
King of Second Life
RobbyRacoon Olmstead
Red warrior is hungry!
Join date: 20 Sep 2006
Posts: 1,821
03-14-2007 12:12
I have not played with this yet, but I am quite curious about verification...

Does the code (as it appears to) assume correctly encrypted data as input? In other words, if I am using this code and it receives bad data from an attempt at hacking it, what are the results?
_____________________
Escort DeFarge
Together
Join date: 18 Nov 2004
Posts: 681
03-14-2007 12:26
From: Morse Dillon
If script length becomes an issue, one can wrap this code in a link message handler. See the documentation in the code for caveats.

...which would slow it down further, surely :( Anyways, if you didn't get time to do a load test I may do a quick check on it to see how it performs. I"ll let you know what I find out.
/regs

BTW where can I buy a sig that's as funny as Robby's?
_____________________
http://slurl.com/secondlife/Together
Morse Dillon
Lifetime Member
Join date: 11 Dec 2003
Posts: 142
03-14-2007 12:40
From: Escort DeFarge
...which would slow it down further, surely :( Anyways, if you didn't get time to do a load test I may do a quick check on it to see how it performs. I"ll let you know what I find out.


That would be great. I don't think the link messages would slow it down much (no in-built delay for example), but also remember that despite its relative simplicity this algorithm is still fairly computationally intensive in LSL terms. On the other hand, it's not like you're going to find anything significantly faster in terms of something that handles a 'real' algorithm and in fact most others would be vastly slower. Like everything else there are trade-offs to be had, and security vs. speed is certainly one that is in full effect in the world of Second Life.
_____________________
King Morse Dillon
King of Second Life
Morse Dillon
Lifetime Member
Join date: 11 Dec 2003
Posts: 142
03-14-2007 12:44
From: RobbyRacoon Olmstead
Does the code (as it appears to) assume correctly encrypted data as input? In other words, if I am using this code and it receives bad data from an attempt at hacking it, what are the results?


It does assume. On the other hand, there are many examples of code that pass over input verification in lieu of speed when there is no public interface involved. A crypt/decrypt function would certainly fall into this category for inter-object communications. If you're looking at building a 'secure walkie-talkie' or something else where the end-user can inject input, of course that's another story.

Verification would be nice to have in many circumstances, and I guess that's what "v 1.0" is all about. If you'd like to submit your patches please let me know.

Thanks for the input! (no pun intended) :)
_____________________
King Morse Dillon
King of Second Life
RobbyRacoon Olmstead
Red warrior is hungry!
Join date: 20 Sep 2006
Posts: 1,821
03-14-2007 13:06
From: Morse Dillon
It does assume. On the other hand, there are many examples of code that pass over input verification in lieu of speed when there is no public interface involved. A crypt/decrypt function would certainly fall into this category for inter-object communications. If you're looking at building a 'secure walkie-talkie' or something else where the end-user can inject input, of course that's another story.

Verification would be nice to have in many circumstances, and I guess that's what "v 1.0" is all about. If you'd like to submit your patches please let me know.

Thanks for the input! (no pun intended) :)


hehe :)

I dont have a specific usage in mind, but that is one of the first things that comes to mind when I think about encryption, because at least in the occasions where I have had to use encryption in RL projects invalid input can cause catastrophic failure, necessitating a validation scheme or ongoing sanity check to at least ensure that the data is encrypted even if not with the correct key.

Thank you for providing the code, it is quite interesting and seems very well done!
_____________________
Ged Larsen
thwarted by quaternions
Join date: 4 Dec 2006
Posts: 294
03-14-2007 15:41
For optimizing speed, I wonder whether Strife Onizuka could be convinced to work his obfuscating but supposedly speed-improving wizardry on the code.

For example, I can never remember which of i++ or ++i is quicker / less memory intensive, but I think this code has the one that is slower.

Thanks for the GPL clarification -- just wanted to make sure that within a product intended for public distribution, you didn't want even scripts merely communicating with the GPL'd encryption module, also to be GPL.
_____________________
- LoopRez, flexi prim skirt generating tool
- LinkRez, a necklace chain generator
Morse Dillon
Lifetime Member
Join date: 11 Dec 2003
Posts: 142
03-14-2007 17:05
From: Ged Larsen
For optimizing speed, I wonder whether Strife Onizuka could be convinced to work his obfuscating but supposedly speed-improving wizardry on the code.


As you can tell, I'm one that prefers very clear code. If, for example, someone wants to obfuscate for a 1% speed increase feel free - but I would prefer not to wrap anything that doesn't make a fairly significant increase back into my 'official' source on the Wiki :)

From: someone
Thanks for the GPL clarification -- just wanted to make sure that within a product intended for public distribution, you didn't want even scripts merely communicating with the GPL'd encryption module, also to be GPL.


Well obviously the GPL hasn't been well-parsed when it comes to an environment such as SL, but I think that the way I've chosen to apply it is appropriate. To me, this is no different than piping output from a non-GPL command line app to a GPLed one.
_____________________
King Morse Dillon
King of Second Life
Talarus Luan
Ancient Archaean Dragon
Join date: 18 Mar 2006
Posts: 4,831
03-14-2007 19:05
Still way too slow for general comms use. Takes anywhere from 10-30 seconds to encrypt/decrypt a single 100-char message.

Even mine, which implements the recommended 32 cycles averages around 10-20 seconds each way.

However, if you really want the security, and are willing to wait for it, then you can get it.
Morse Dillon
Lifetime Member
Join date: 11 Dec 2003
Posts: 142
03-14-2007 19:16
From: Talarus Luan
Still way too slow for general comms use. Takes anywhere from 10-30 seconds to encrypt/decrypt a single 100-char message.

Even mine, which implements the recommended 32 cycles averages around 10-20 seconds each way.

However, if you really want the security, and are willing to wait for it, then you can get it.


One interesting note - I found that the number of cycles does increase encryption time linearly, but it's a pretty flat line. The script spends much more time in the packing/unpacking functions than the actual crypto functions themselves. Ergo, there's not a whole pile of difference between 6 cycles and 32 cycles, but I left it low as default to get every last bit out of it.
_____________________
King Morse Dillon
King of Second Life
Talarus Luan
Ancient Archaean Dragon
Join date: 18 Mar 2006
Posts: 4,831
03-14-2007 19:35
It is true, though it is probably best to use the recommended 32 cycles for proper security. If you're going to wait, go ahead and wait the extra time to get some security to make the wait worth it. 6 cycles is really too low and can easily be broken by even basic cryptanalysis.

I used base64 instead of hex for my ciphertext; works quite a bit faster since you can use the base64 functions to help you do the conversion, which is where I am probably getting my speed from.
Strife Onizuka
Moonchild
Join date: 3 Mar 2004
Posts: 5,887
03-14-2007 20:01
Why not release it under LGPL?

I'll take a look at the code and see if i can make an improvements.
_____________________
Truth is a river that is always splitting up into arms that reunite. Islanded between the arms, the inhabitants argue for a lifetime as to which is the main river.
- Cyril Connolly

Without the political will to find common ground, the continual friction of tactic and counter tactic, only creates suspicion and hatred and vengeance, and perpetuates the cycle of violence.
- James Nachtwey
Masakazu Kojima
ケロ
Join date: 23 Apr 2004
Posts: 232
03-14-2007 20:45
Here is my implementation. It is not based on Morse's and it is public domain. It is somewhat faster, but the bytes are ordered differently and the encrypted output format is CSV. Morse's implementation fails my tests because of sign extension (simple fix).

CODE
// XTEA encryption/decryption - http://en.wikipedia.org/wiki/XTEA
// This code is public domain.
// masa was here 20070314

//************************************************//
//* XTEA IMPLEMENTATION *//
//************************************************//

integer XTEA_DELTA = 0x9E3779B9; // (sqrt(5) - 1) * 2^31

// Convert a string to a list of big-endian integers. If there are not
// enough characters in the string to complete the last integer,
// it will be padded with spaces.
// ex: xtea_str_to_int_list("abcdefg") == [ 0x61626364, 0x65666720 ]
list xtea_str_to_int_list( string str ) {
integer len = llStringLength(str);
list result = [];
integer i = 0;

str += " "; // padding

// for ( i = 0; i < len; i += 4 )
@loop;
result += llBase64ToInteger(
llStringToBase64(
llGetSubString(str, i, i += 3)
)
);
if ( ++i < len )
jump loop;

return result;
}

// Convert a list of big-endian integers to a string.
// ex: xtea_int_list_to_str([0x74657374, 0x696e6720]) == "testing "
string xtea_int_list_to_str( list l ) {
string result;
integer len = llGetListLength(l);
integer i = 0;

// for ( i = 0; i < len; ++i )
@loop;
result += llBase64ToString(
llIntegerToBase64(
llList2Integer(l, i)
)
);
if ( ++i < len )
jump loop;

return result;
}

// encipher first 2 integers in v using first 4 integers in k
list xtea_encipher( integer num_rounds, list v, list k ) {
integer v0 = llList2Integer(v,0);
integer v1 = llList2Integer(v,1);
integer sum = 0;

// for ( i = 0; i < num_rounds; ++i )
@loop;
// LSL does not have unsigned integers, so when shifting right we
// have to mask out sign-extension bits.
v0 += (((v1 << 4) ^ ((v1 >> 5) & 0x07FFFFFF)) + v1) ^
(sum + llList2Integer(k, sum & 3));
sum += XTEA_DELTA;
v1 += (((v0 << 4) ^ ((v0 >> 5) & 0x07FFFFFF)) + v0) ^
(sum + llList2Integer(k, (sum>>11) & 3));

if ( --num_rounds )
jump loop;

return [v0, v1];
}

// decipher first 2 integers in v using first 4 integers in k
list xtea_decipher( integer num_rounds, list v, list k ) {
integer v0 = llList2Integer(v,0);
integer v1 = llList2Integer(v,1);
integer sum = XTEA_DELTA*num_rounds;

// for ( i = 0; i < num_rounds; ++i )
@loop;
// LSL does not have unsigned integers, so when shifting right we
// have to mask out sign-extension bits.
v1 -= (((v0 << 4) ^ ((v0 >> 5) & 0x07FFFFFF)) + v0) ^
(sum + llList2Integer(k, (sum>>11) & 3));
sum -= XTEA_DELTA;
v0 -= (((v1 << 4) ^ ((v1 >> 5) & 0x07FFFFFF)) + v1) ^
(sum + llList2Integer(k, sum & 3));

if ( --num_rounds )
jump loop;

return [v0, v1];
}

// Encrypt a full string using XTEA.
// Returns a comma-separated list of integers containing the result of the
// encryption. It can be converted to another format later if needed.
// Incomplete blocks are space-padded.
string xtea_encrypt_string( integer num_rounds, string str, string k ) {
list str_il = xtea_str_to_int_list(str) + [0x20202020];
list k_il = xtea_str_to_int_list(k);
integer len = llGetListLength(str_il) - 1;
integer i = 0;
list result;

// for ( i = 0; i < len; i += 2 )
@loop;
result += xtea_encipher(
num_rounds,
llList2List(str_il, i, i+=2),
k_il
);

if ( i < len )
jump loop;

return llList2CSV(result);
}

// Decrypt a full string using XTEA
string xtea_decrypt_string( integer num_rounds, string str, string k ) {
list str_il = llCSV2List(str);
list k_il = xtea_str_to_int_list(k);
integer len = llGetListLength(str_il) - 1;
integer i = 0;
string result;

// for ( i = 0; i < len; i += 2 )
@loop;
result += xtea_int_list_to_str(
xtea_decipher(
num_rounds,
llList2List(str_il, i, i += 2),
k_il
)
);

if ( i < len )
jump loop;

return result;
}

//************************************************//
//* TESTS *//
//************************************************//

integer test_assertions = 0;
integer test_failures = 0;

test_assert( integer condition, string description ) {
++test_assertions;
if ( !condition ) {
++test_failures;
llOwnerSay( "ASSERTION FAILED: " + description );
}
}

test_assert_equal_list( list v1, list v2, string desc ) {
test_assert( compare_list(v1, v2), desc +
"\nACTUAL=" + llList2CSV(v1) +
"\nEXPECTED=" + llList2CSV(v2) );
}

integer compare_list( list l1, list l2 ) {
if ( l1 != l2 ) // != only compares lengths
return FALSE;
return llListFindList(l1, l2) == 0;
}

test_xtea_str_to_int_list() {
test_assert_equal_list(
xtea_str_to_int_list("abcdefg"),
[0x61626364, 0x65666720],
"str_to_int_list abcdefg"
);
test_assert_equal_list(
xtea_str_to_int_list("abcdef"),
[0x61626364, 0x65662020],
"str_to_int_list abcdef"
);
test_assert_equal_list(
xtea_str_to_int_list("abcde"),
[0x61626364, 0x65202020],
"str_to_int_list abcde"
);
test_assert_equal_list(
xtea_str_to_int_list("abcd"),
[0x61626364],
"str_to_int_list abcd"
);
test_assert_equal_list(
xtea_str_to_int_list("small test"),
[0x736d616c, 0x6c207465, 0x73742020],
"str_to_int_list 'small test'"
);
}

test_xtea_int_list_to_str() {
test_assert(
xtea_int_list_to_str( [0x61626364, 0x65666720] ) == "abcdefg ",
"xtea_int_list_to_str( [0x61626364, 0x65666720] ) == \"abcdefg \""
);
test_assert(
xtea_int_list_to_str( [0x61626364, 0x65202020] ) == "abcde ",
"xtea_int_list_to_str( [0x61626364, 0x65202020] ) == \"abcde \""
);
}

test_xtea_encipher() {
test_assert_equal_list(
xtea_encipher(
6,
[0x62626161, 0x64646363],
[0x74736574, 0x2c676e69, 0x74736574, 0x2e676e69]
),
[0x5117f04b, 0xb9f38d3c],
"xtea_encipher(6, \"aabbccdd\", \"testing,testing.\") vs std"
);

test_assert_equal_list(
xtea_encipher(
16,
[0x74536554, 0x23476e49],
[0x24234021, 0x2a265e25, 0x2b5f2928, 0x5d5b7d7b]
),
[0xe9306ed2, 0x8f983d5a],
"xtea_encipher(16, \"TeStInG#\", \"!@#$%^&*()_+{}[]\") vs std"
);

test_assert_equal_list(
xtea_encipher(
32,
[0x20202020, 0x20202020],
[0x20202020, 0x20202020, 0x20202020, 0x20202020]
),
[0xb63a89e3, 0x8631c79c],
"xtea_encipher(32, \" \", \" \") vs std"
);
}

test_timed_encrypt(integer num_rounds, string str, string k) {
string encrypted;
string decrypted;
integer len = llStringLength(str);
string info = "rounds=" + (string)num_rounds + ", len=" + (string)len;

llResetTime();
encrypted = xtea_encrypt_string(num_rounds, str, k);
llOwnerSay( "enc(" + info + "): " + (string)llGetTime() );

llResetTime();
decrypted = xtea_decrypt_string(num_rounds, encrypted, k);
llOwnerSay( "dec(" + info + "): " + (string)llGetTime() );

// decrypted version may have extra spaces for padding
string padded_str = str;
while ( len < llStringLength(decrypted) ) {
padded_str += " ";
len += 1;
}

test_assert(
padded_str == decrypted,
"test_timed_encrypt(" + (string)num_rounds + "," +
"\"" + str + "\", \"" + k + "\") -- \"" + decrypted + "\""
);
}

test_xtea_encrypt_string() {
test_timed_encrypt(64, "hello", "test");
test_timed_encrypt(64, "small test", "small key");
test_timed_encrypt(64, ",xaYGp:?iThL *+\"I8!6qGA0eSXCUG1fXxNl'\"T0_" +
"Un.P:u/= N{2{!L -]'mg-89p.on9zjfEJ<lZW;gJ;d" +
"3:u CFK+|qMqmhaI0", "here is a key");
test_timed_encrypt(32, "hello", "test");
test_timed_encrypt(32, "small test", "small key");
test_timed_encrypt(32, ",xaYGp:?iThL *+\"I8!6qGA0eSXCUG1fXxNl'\"T0_" +
"Un.P:u/= N{2{!L -]'mg-89p.on9zjfEJ<lZW;gJ;d" +
"3:u CFK+|qMqmhaI0", "here is a key");
test_timed_encrypt( 6, "hello", "test");
test_timed_encrypt( 6, "small test", "small key");
test_timed_encrypt( 6, ",xaYGp:?iThL *+\"I8!6qGA0eSXCUG1fXxNl'\"T0_" +
"Un.P:u/= N{2{!L -]'mg-89p.on9zjfEJ<lZW;gJ;d" +
"3:u CFK+|qMqmhaI0", "here is a key");
}

//************************************************//
//* TEST DRIVER *//
//************************************************//

default {
state_entry() {
test_xtea_str_to_int_list();
test_xtea_int_list_to_str();
test_xtea_encipher();
test_xtea_encrypt_string();
llOwnerSay( "assertions: " + (string)test_assertions + " / " +
"failures: " + (string)test_failures );

llListen(0, "", llGetOwner(), "");
}

listen(integer channel, string name, key id, string msg) {
test_timed_encrypt( 64, msg, id );
test_timed_encrypt( 32, msg, id );
test_timed_encrypt( 6, msg, id );
}
}


Here are timings. I ran them in parallel but reordered the results to be easier to follow:
CODE
PRIVATE ISLAND (~3 agents, ~500 active scripts):

[20:09] You: small test
[20:09] XTEA.masa: enc(rounds=64, len=10): 0.511423
[20:09] XTEA.masa: dec(rounds=64, len=10): 0.511431
[20:09] XTEA.masa: enc(rounds=32, len=10): 0.311310
[20:09] XTEA.masa: dec(rounds=32, len=10): 0.311292
[20:09] XTEA.masa: enc(rounds=6, len=10): 0.133463
[20:09] XTEA.masa: dec(rounds=6, len=10): 0.111145
[20:09] XTEA.morse: enc(rounds=64, len=10): 0.689131
[20:09] XTEA.morse: dec(rounds=64, len=10): 0.689359
[20:09] XTEA.morse: enc(rounds=32, len=10): 0.466955
[20:09] XTEA.morse: dec(rounds=32, len=10): 0.511823
[20:09] XTEA.morse: enc(rounds=6, len=10): 0.311163
[20:09] XTEA.morse: dec(rounds=6, len=10): 0.311015
[20:10] You: big test big test big test big test big test big test big test big test big test big test big test b
[20:10] XTEA.masa: enc(rounds=64, len=100): 3.115898
[20:10] XTEA.masa: dec(rounds=64, len=100): 3.046467
[20:10] XTEA.masa: enc(rounds=32, len=100): 1.732771
[20:10] XTEA.masa: dec(rounds=32, len=100): 1.645381
[20:10] XTEA.masa: enc(rounds=6, len=100): 0.578033
[20:10] XTEA.masa: dec(rounds=6, len=100): 0.533587
[20:10] XTEA.morse: enc(rounds=64, len=100): 4.627972
[20:10] XTEA.morse: dec(rounds=64, len=100): 4.513800
[20:10] XTEA.morse: enc(rounds=32, len=100): 3.179665
[20:10] XTEA.morse: dec(rounds=32, len=100): 3.179778
[20:10] XTEA.morse: enc(rounds=6, len=100): 2.112271
[20:10] XTEA.morse: dec(rounds=6, len=100): 2.045777

BAKU (~24 agents, ~2500 active scripts):

[20:14] You: small test
[20:15] XTEA.masa: enc(rounds=64, len=10): 15.306687
[20:15] XTEA.masa: dec(rounds=64, len=10): 15.268655
[20:15] XTEA.masa: enc(rounds=32, len=10): 7.233666
[20:15] XTEA.masa: dec(rounds=32, len=10): 8.511871
[20:15] XTEA.masa: enc(rounds=6, len=10): 2.733759
[20:15] XTEA.masa: dec(rounds=6, len=10): 2.838602
[20:15] XTEA.morse: enc(rounds=64, len=10): 20.067862
[20:15] XTEA.morse: dec(rounds=64, len=10): 17.456011
[20:15] XTEA.morse: enc(rounds=32, len=10): 11.517085
[20:15] XTEA.morse: dec(rounds=32, len=10): 12.385174
[20:15] XTEA.morse: enc(rounds=6, len=10): 8.747997
[20:16] XTEA.morse: dec(rounds=6, len=10): 13.263769
[20:16] You: big test big test big test big test big test big test big test big test big test big test big test b
[20:18] XTEA.masa: enc(rounds=64, len=100): 107.941353
[20:20] XTEA.masa: dec(rounds=64, len=100): 106.056046
[20:21] XTEA.masa: enc(rounds=32, len=100): 49.256821
[20:22] XTEA.masa: dec(rounds=32, len=100): 64.948914
[20:22] XTEA.masa: enc(rounds=6, len=100): 18.163019
[20:22] XTEA.masa: dec(rounds=6, len=100): 13.989554
[20:19] XTEA.morse: enc(rounds=64, len=100): 139.570786
[20:21] XTEA.morse: dec(rounds=64, len=100): 140.785095
[20:23] XTEA.morse: enc(rounds=32, len=100): 95.164322
[20:24] XTEA.morse: dec(rounds=32, len=100): 79.124123
[20:25] XTEA.morse: enc(rounds=6, len=100): 41.315472
[20:25] XTEA.morse: dec(rounds=6, len=100): 39.475845
Strife Onizuka
Moonchild
Join date: 3 Mar 2004
Posts: 5,887
03-14-2007 22:23
Q: Why pad the end with spaces? Why not nulls?
_____________________
Truth is a river that is always splitting up into arms that reunite. Islanded between the arms, the inhabitants argue for a lifetime as to which is the main river.
- Cyril Connolly

Without the political will to find common ground, the continual friction of tactic and counter tactic, only creates suspicion and hatred and vengeance, and perpetuates the cycle of violence.
- James Nachtwey
Masakazu Kojima
ケロ
Join date: 23 Apr 2004
Posts: 232
03-14-2007 22:56
In my case it is cause the base64 functions turn nulls into question marks.
Neal McAlpine
Registered User
Join date: 24 Dec 2006
Posts: 33
03-15-2007 00:08
Hi.
Tho this looks very interesting, it is way too slow for my current project in which I want to encrypt communication between a node and a HUD. Now since all the encrypting pros seem to be gathered here :) let me ask how (un)safe my current solution is:
CODE
integer   hud_receive = -1234;
integer hud_send = -5678;
string cryptokey;

// ---------- functions ----------
string encrypt(string s1) {
return llXorBase64StringsCorrect(llStringToBase64(s1), cryptokey);
}

string decrypt(string s1) {
return llBase64ToString(llXorBase64StringsCorrect(s1, cryptokey));
}

// ---------- main program ----------
default {
state_entry() {
cryptokey = llStringToBase64("string-with-134-random-characters");
l_handle = llListen(hud_receive, "", NULL_KEY, "");
}

listen(integer channel, string name, key id, string message) {
message = decrypt(message);
list data = llParseString2List(message, ["@"], []);
string command = llList2String(data, 0);
...
}
}
Peekay Semyorka
Registered User
Join date: 18 Nov 2006
Posts: 337
03-15-2007 00:22
From a cryptographic perspective, the above algorithm is not secure at all.

However, practical security depends on the value of what you're protecting, and how determined and resourceful your adversary is. If you just want to deter the most casual griefer from messing with your HUD, then the above method may be sufficient. For protecting nuclear secrets, then even the (much stronger) XTEA algorithm presented here remains inadequate.

-peekay
Neal McAlpine
Registered User
Join date: 24 Dec 2006
Posts: 33
03-15-2007 00:43
Thanks for that very fast answer, Peekay (PK? We may have met several times in Ultima Online *g*)

You're right, I think I somehow was hoping for an answer like "Oh yes, that's quite good for that purpose" but your answer is the only suitable.
*sigh* Since that game I'm working on pays out money, I'd better run it with an alt that has no more money then what's needed for running the game.

Thanks again.
Escort DeFarge
Together
Join date: 18 Nov 2004
Posts: 681
03-15-2007 03:56
Here's my results from a hasty test of Morse's implementation...

I copy/pasted the algorithm into a script and drove it with the below.
The cycle averaged about 40 seconds, BUT I got a *100% error rate* on decrypt!
i.e. decoded != text
Any comments/offers?
*edit* ok just realised it's padding with spaces, bah, can i say usability? sim lag? it works but it's not really usable, i'm afraid (a second run averaged over 60 seconds per cycle :()

CODE
integer index; 
integer number = 100;
integer errors = 0;
float total = 0.0;
for (index = 1; index <= number; index++) {
string text = llGetTimestamp();

// cuddle the timer around the call
float start = llGetTime();
string decoded = Decrypt(Encrypt(text));
float end = llGetTime();
// then report...
total += (end - start);
string time = (string)(end - start);
if (decoded != text) {
errors++;
}

llSetText((string)index + "/" + time
+ "\navg=" + (string)(total / (float)index)
+ "\nerrors=" + (string)errors, <1,1,1>, 1);
}
}
_____________________
http://slurl.com/secondlife/Together
Strife Onizuka
Moonchild
Join date: 3 Mar 2004
Posts: 5,887
03-15-2007 04:28
To show I believe in equal opportunities for abu-... optimizing LSL, here is Masakazu's implementation optimized. For grins i rewrote it not to use padding. I'm rather pleased with the math used to cut off the last extra characters. I haven't tested either optimization.

My hands are hurting (they have been for the last few days) so I'm off for the day.

PS: Both implementation are going to break if fed UTF-8 characters. Adding UTF-8 support would be a chore but not impossible. The encoding and decoding would be difficult. The issue with encoding being you don't know how many bytes each character uses. The issue with the decoder is you need to decode the integers into characters, meaning byte awareness of character as otherwise your decoding function will not return correctly on characters split across multiple dwords (highly probable). The decoding issue wouldn't exist if llUnescapeURL had no limits.

CODE

// XTEA encryption/decryption - http://en.wikipedia.org/wiki/XTEA
// This code is public domain.
// masa was here 20070314
// so was strife 20070315

//************************************************//
//* XTEA IMPLEMENTATION *//
//************************************************//

integer XTEA_DELTA = 0x9E3779B9; // (sqrt(5) - 1) * 2^31

// Convert a string to a list of big-endian integers. If there are not
// enough characters in the string to complete the last integer,
// it will be padded with spaces.
// ex: xtea_str_to_int_list("abcdefg") == [ 0x61626364, 0x65666720 ]
list xtea_str_to_int_list( string str )
{
integer len = llStringLength(str);
integer i = len & 0xFFFFFFFC;
list result = [];

if(i)
{
integer n = 0;
do
result += llBase64ToInteger(
llStringToBase64(
llGetSubString(str, n, n += 3)
)
);
while((n = -~n) ^ i);
}
if(len ^ i)
{
return result + llBase64ToInteger(
llStringToBase64(
llGetSubString(str, i, len)
) + "======"//keeps short strings from causing corruption
);
}
return result;
}

// Convert a list of big-endian integers to a string.
// ex: xtea_int_list_to_str([0x74657374, 0x696e6720]) == "testing "
string xtea_int_list_to_str( list l )
{
integer len = [] != l;
if(len)
{
string result;
integer t;
do
result += llBase64ToString(
llIntegerToBase64(
t = llList2Integer(l, len)
)
);
while((len= -~len));
return llGetSubString(
result,
0,//the condition is to prevent log fouling
~(integer)(llLog(len - (0x80000000 == (len = (t ^ (t & ~-t))))) / 5.5451774444795624753378569716654)
);
}
return "";
}

// encipher first 2 integers in v using first 4 integers in k
list xtea_encipher( integer num_rounds, list v, list k )
{
integer v0 = llList2Integer(v,0);
integer v1 = llList2Integer(v,1);
if(num_rounds)
{
integer sum = 0;
do
{
// LSL does not have unsigned integers, so when shifting right we
// have to mask out sign-extension bits.
v0 += (((v1 << 4) ^ ((v1 >> 5) & 0x07FFFFFF)) + v1) ^
(sum + llList2Integer(k, sum & 3));
v1 += (((v0 << 4) ^ ((v0 >> 5) & 0x07FFFFFF)) + v0) ^
(sum + llList2Integer(k, ((sum += XTEA_DELTA)>>11) & 3));
}while( num_rounds = ~-num_rounds );
}

return [v0, v1];
}

// decipher first 2 integers in v using first 4 integers in k
list xtea_decipher( integer num_rounds, list v, list k )
{
integer v0 = llList2Integer(v,0);
integer v1 = llList2Integer(v,1);
if(num_rounds)
{
integer sum = XTEA_DELTA*num_rounds;
do
{
// LSL does not have unsigned integers, so when shifting right we
// have to mask out sign-extension bits.
v1 -= (((v0 << 4) ^ ((v0 >> 5) & 0x07FFFFFF)) + v0) ^
(sum + llList2Integer(k, (sum>>11) & 3));
v0 -= (((v1 << 4) ^ ((v1 >> 5) & 0x07FFFFFF)) + v1) ^
(sum + llList2Integer(k, (sum -= XTEA_DELTA) & 3));
}
while(num_rounds = ~-num_rounds);
}

return [v0, v1];
}

// Encrypt a full string using XTEA.
// Returns a comma-separated list of integers containing the result of the
// encryption. It can be converted to another format later if needed.
// Incomplete blocks are space-padded.
string xtea_encrypt_string( integer num_rounds, string str, string k )
{
list str_il = xtea_str_to_int_list(str);
list k_il = xtea_str_to_int_list(k);
integer len = [] != str_il;
list result;

if((len -= (1 & len)))
do
result += xtea_encipher(
num_rounds,
llList2List(str_il, len, len += 2),//hack, send 3 items
k_il
);
while(len);

return llList2CSV(result);
}

// Decrypt a full string using XTEA
string xtea_decrypt_string( integer num_rounds, string str, string k )
{
list str_il = llCSV2List(str);
list k_il = xtea_str_to_int_list(k);
integer len = [] != str_il;
string result;

if((len -= (1 & len)))
do
result += xtea_int_list_to_str(
xtea_decipher(
num_rounds,
llList2List(str_il, len, len += 2),//hack, send 3 items
k_il
)
);
while(len);

return result;
}

//************************************************//
//* TESTS *//
//************************************************//

integer test_assertions = 0;
integer test_failures = 0;

test_assert( integer condition, string description ) {
++test_assertions;
if ( !condition ) {
++test_failures;
llOwnerSay( "ASSERTION FAILED: " + description );
}
}

test_assert_equal_list( list v1, list v2, string desc ) {
test_assert( compare_list(v1, v2), desc +
"\nACTUAL=" + llList2CSV(v1) +
"\nEXPECTED=" + llList2CSV(v2) );
}

integer compare_list( list l1, list l2 ) {
if ( l1 != l2 ) // != only compares lengths
return FALSE;
return llListFindList(l1, l2) == 0;
}

test_xtea_str_to_int_list() {
test_assert_equal_list(
xtea_str_to_int_list("abcdefg"),
[0x61626364, 0x65666720],
"str_to_int_list abcdefg"
);
test_assert_equal_list(
xtea_str_to_int_list("abcdef"),
[0x61626364, 0x65662020],
"str_to_int_list abcdef"
);
test_assert_equal_list(
xtea_str_to_int_list("abcde"),
[0x61626364, 0x65202020],
"str_to_int_list abcde"
);
test_assert_equal_list(
xtea_str_to_int_list("abcd"),
[0x61626364],
"str_to_int_list abcd"
);
test_assert_equal_list(
xtea_str_to_int_list("small test"),
[0x736d616c, 0x6c207465, 0x73742020],
"str_to_int_list 'small test'"
);
}

test_xtea_int_list_to_str() {
test_assert(
xtea_int_list_to_str( [0x61626364, 0x65666720] ) == "abcdefg ",
"xtea_int_list_to_str( [0x61626364, 0x65666720] ) == \"abcdefg \""
);
test_assert(
xtea_int_list_to_str( [0x61626364, 0x65202020] ) == "abcde ",
"xtea_int_list_to_str( [0x61626364, 0x65202020] ) == \"abcde \""
);
}

test_xtea_encipher() {
test_assert_equal_list(
xtea_encipher(
6,
[0x62626161, 0x64646363],
[0x74736574, 0x2c676e69, 0x74736574, 0x2e676e69]
),
[0x5117f04b, 0xb9f38d3c],
"xtea_encipher(6, \"aabbccdd\", \"testing,testing.\") vs std"
);

test_assert_equal_list(
xtea_encipher(
16,
[0x74536554, 0x23476e49],
[0x24234021, 0x2a265e25, 0x2b5f2928, 0x5d5b7d7b]
),
[0xe9306ed2, 0x8f983d5a],
"xtea_encipher(16, \"TeStInG#\", \"!@#$%^&*()_+{}[]\") vs std"
);

test_assert_equal_list(
xtea_encipher(
32,
[0x20202020, 0x20202020],
[0x20202020, 0x20202020, 0x20202020, 0x20202020]
),
[0xb63a89e3, 0x8631c79c],
"xtea_encipher(32, \" \", \" \") vs std"
);
}

test_timed_encrypt(integer num_rounds, string str, string k) {
string encrypted;
string decrypted;
integer len = llStringLength(str);
string info = "rounds=" + (string)num_rounds + ", len=" + (string)len;

llResetTime();
encrypted = xtea_encrypt_string(num_rounds, str, k);
llOwnerSay( "enc(" + info + "): " + (string)llGetTime() );

llResetTime();
decrypted = xtea_decrypt_string(num_rounds, encrypted, k);
llOwnerSay( "dec(" + info + "): " + (string)llGetTime() );

// decrypted version may have extra spaces for padding
string padded_str = str;
while ( len < llStringLength(decrypted) ) {
padded_str += " ";
len += 1;
}

test_assert(
padded_str == decrypted,
"test_timed_encrypt(" + (string)num_rounds + "," +
"\"" + str + "\", \"" + k + "\") -- \"" + decrypted + "\""
);
}

test_xtea_encrypt_string() {
test_timed_encrypt(64, "hello", "test");
test_timed_encrypt(64, "small test", "small key");
test_timed_encrypt(64, ",xaYGp:?iThL *+\"I8!6qGA0eSXCUG1fXxNl'\"T0_" +
"Un.P:u/= N{2{!L -]'mg-89p.on9zjfEJ<lZW;gJ;d" +
"3:u CFK+|qMqmhaI0", "here is a key");
test_timed_encrypt(32, "hello", "test");
test_timed_encrypt(32, "small test", "small key");
test_timed_encrypt(32, ",xaYGp:?iThL *+\"I8!6qGA0eSXCUG1fXxNl'\"T0_" +
"Un.P:u/= N{2{!L -]'mg-89p.on9zjfEJ<lZW;gJ;d" +
"3:u CFK+|qMqmhaI0", "here is a key");
test_timed_encrypt( 6, "hello", "test");
test_timed_encrypt( 6, "small test", "small key");
test_timed_encrypt( 6, ",xaYGp:?iThL *+\"I8!6qGA0eSXCUG1fXxNl'\"T0_" +
"Un.P:u/= N{2{!L -]'mg-89p.on9zjfEJ<lZW;gJ;d" +
"3:u CFK+|qMqmhaI0", "here is a key");
}

//************************************************//
//* TEST DRIVER *//
//************************************************//

default {
state_entry() {
test_xtea_str_to_int_list();
test_xtea_int_list_to_str();
test_xtea_encipher();
test_xtea_encrypt_string();
llOwnerSay( "assertions: " + (string)test_assertions + " / " +
"failures: " + (string)test_failures );

llListen(0, "", llGetOwner(), "");
}

listen(integer channel, string name, key id, string msg) {
test_timed_encrypt( 64, msg, id );
test_timed_encrypt( 32, msg, id );
test_timed_encrypt( 6, msg, id );
}
}
_____________________
Truth is a river that is always splitting up into arms that reunite. Islanded between the arms, the inhabitants argue for a lifetime as to which is the main river.
- Cyril Connolly

Without the political will to find common ground, the continual friction of tactic and counter tactic, only creates suspicion and hatred and vengeance, and perpetuates the cycle of violence.
- James Nachtwey
1 2 3 4