| ||||||||||||||||
|
|
Welcome,
Al Supercharge. You last visited: Yesterday at 11:53 PM |
|
Thread Tools | Search this Thread | Display Modes |
05-13-2008, 04:06 AM | #1 |
Registered User
Join Date: Dec 2007
Posts: 40
|
LSL to PHP HTTP Encryption Example
I needed an encryption system to handle in
world payments being sent to my webserver.
I needed a system that would also be safe from message spoofing. I needed to make sure that the datastream URL could not be simply copied and resent. It would be bad if someone somehow copied the datastream URL for a payment and resent it over and over again to the server. This system (when used with a MySQL DB to store the used keys) will ensure no message can be copied and sent twice. Since I'm no encryption "Expert", I would like some input into this here system I've developed. Simply put the LSL script in an object and it should hook-up with my webserver. If this script can be validated as being a decent solution, I would also like it to be made available in the script library as it would seem to be safe if the private keys I use are kept private. Script for in world object LSL ========================= LSL // Written by Zolen Giano //This script demonstrates passing encrypted data to a PHP webserver and back. //This script encrypts both the data and public key with a random number. //Then decrypts the returning data with same random number //private key for encrypting random seed string encryptionkey = "MyPrivateKey"; //User Variables integer MainTimer = 30; //does a cycle every 30 seconds integer SleepTimer = 5; // seconds to pause after a request string split="|"; //the field delimiter for parsing //HTTP info - Server and webpage (don't hammer on my server plz) string baseURL = "http://clubworks.co.cc/testcrypt.php?"; //dont forget the '?' on the end of pagename // System Variables integer channel; string seed; string secretkey; integer fails; integer goods; string OwnerName; integer DataCycles; key httprequest; default { state_entry() { channel=(integer)llFrand(100000)-1000000; OwnerName=llKey2Name(llGetOwner()); llSay(0, "Warming Up...Hello "+OwnerName+". Communicating on channel "+(string)channel+"."); llSetText("Warming up...",<1,0,0>,1); llSetTimerEvent(5); } on_rez(integer param) { llResetScript(); } touch_start(integer total_number) { if (llDetectedKey(0)!=llGetOwner()) llWhisper(0,"Sorry, controlled by "+OwnerName+" only."); else { llSetTimerEvent(MainTimer); llDialog(llDetectedKey(0),"Please Select", ["Stats", "Reset"],channel); llListen(channel, "", llGetOwner(), ""); } } listen(integer thsichannel, string name, key id, string msg) { if (msg=="Stats") { llOwnerSay("Free Memory: "+(string)llGetFreeMemory()+" Bytes Free."); llOwnerSay("Data Cycles: "+(string)DataCycles+". 1 cycle every "+(string)MainTimer+" seconds."); llOwnerSay("Good Requests: "+(string)goods); llOwnerSay("Fails: "+(string)fails); } if (msg=="Reset") { llResetScript(); } } timer() { // set the timer llSetTimerEvent(MainTimer); // count cycles for stats DataCycles++; //sample data string data1=(string)llKey2Name(llGetOwner()); string data2=(string)llGetRegionName(); string data3=(string)((integer)llGetRegionFPS()); //built the data string string datum=data1+split+data2+split+data3; //make a large random seed seed=(string)((integer)llFrand(100000000)+10000000 0); //add the seed number to the beginning of the data string for checking later datum=(string)seed+split+datum; //encrypt the data string with the random seed string secret=llXorBase64StringsCorrect(llStringToBase64( datum),llStringToBase64((string)seed)); //encrypt the random seed with our private encryption key string secretkey=llXorBase64StringsCorrect(llStringToBase 64((string)seed),llStringToBase64(encryptionkey)); //build our encrypted URL to send string thisurl="&k="+llEscapeURL((string)secretkey)+"&d="+llEscapeURL(secret); //Show progress report in yellow hovertext llSetText("Sending Data:\n"+(string)thisurl,<1,1,0>,1); llSleep(SleepTimer); //Send the request to webserver httprequest = llHTTPRequest(baseURL+thisurl,[HTTP_METHOD,"GET"],""); } //Webserver response http_response(key request_id, integer status, list metadata, string body) { if (request_id == httprequest) { //Show status in white llSetText("Server Response:\n\n"+body, <1,1,1>, 1); llSleep(SleepTimer); //Parse the webserver response list parsed = llParseString2List( body, [ split ], [] ); //pick out the returning encrypted seed string returnseed=llList2String( parsed, 1 ); //decrypt the returning seed with our private encryption key returnseed=llBase64ToString(llXorBase64Strings(ret urnseed, llStringToBase64(encryptionkey))); //Check if our returning seed is the same as our original random seed if ((string)seed==returnseed) { // add to statistics goods++; //pick out the encrypted message from the web server response. string returnsecret=llList2String( parsed, 2 ); //decrypt our returning message with our seed string returnmessage=llBase64ToString(llXorBase64Strings( returnsecret, llStringToBase64(seed))); //Show decrypted message in green hovertext llSetText("Decrypted Message:\n"+returnmessage,<0,1,0>,1); } else { // add to statistics fails++; //Show Failed Status in Red llSetText("Data Request Failed\nWaiting for Next Cycle",<1,0,0>,1); } //limit the number of cycles to 10. //remove this if you want to hammer on the web server forever if(DataCycles >10) {llOwnerSay("I'm tired. Sleeping...");llSetTimerEvent(0);} } } } /LSL=================/LSL Webserver script - testcrypt.php PHP=======================PHP <? //Round trip SL to PHP encryption example written by Zolen Giano //this PHP script decrypts and processes a message sent from SL, //and returns an encrypted response. //This system uses random public keys which are decrypted by the //internal private key to produce a unique data stream every time. // your private key $encryptionkey="MyPrivateKey"; //field seperator for parsing $split="|"; //Simple XOR encryption function function simplexor($s1, $s2) { $s1 = base64_decode($s1); $s2 = base64_decode($s2); while(strlen($s2) < strlen($s1)) $s2 .= $s2; return base64_encode($s1 ^ $s2); } //grabs the incomming variables $thiskey=$_GET['k']; $secret=$_GET['d']; //Here is a good spot to do your DB query for duplicate keys. //store the incomming thiskeys in your DB //If thiskey already exists in the DB means this is a spoofed or repeated message. //decrypt the public key using our private key to get the original random seed. $seed=base64_decode(simplexor($thiskey,base64_enco de($encryptionkey))); //decrypt the data using the random seed $datum=base64_decode(simplexor($secret,base64_enco de($seed))); //parse the data into an array $parsed = explode ($split,$datum); //Security check to make sure the outer seed exists and equals the inner seed if ($seed!="" and ((string)((integer)$seed)==$parsed[0])) { //do our processing... //this is where you can insert your parsed bits to your database //Send some unencrypted data back to SL for fun echo "Server Clubworks.co.cc Online\n".$split; //build a data string we want to encrypt. $datum = "\nWelcome ".$parsed[1]." from ".$parsed[2].".\n\n Your region FPS is ".$parsed[3]; //encrypt the seed and send it back to SL for checking echo (simplexor(base64_encode((string)$seed),base64_enc ode($encryptionkey)).$split); //encrypt our data stream and send it echo (simplexor(base64_encode($datum),base64_encode($se ed))); } else { //failed security test echo "Unauthorized Access"; die(); } ?> /PHP================/PHP Cheers! - zg
Last edited by Zolen Giano : 05-13-2008 at 08:57
AM. Reason: Slight update to the PHP part |
05-13-2008, 08:57 AM | #2 |
Moonchild
Join Date: Mar 2004
Location: !quit !quit !quit !quit
Posts: 5,572
|
I'm not an expert in all types of encryption
but I do know quite a bit about using XOR. Your data is not secure. There
are some serious problems with this. First of all, I know the xor is 9
characters and because of the XOR the key is effectively also 9
characters, because of how you generate the random number I can also
determine the first character of key and every 9th character starting at
the first character of the message. If your key contains a number in any
of it's 9 positions, given enough captures (about 10) I can determine
without a doubt that it is a number. Because I know the seed is all digits
I know the top four bits of every byte.
If you just look at the probabilities, the number of bits leaked by your alg are: 47.25 of the 72 bits. Which means that before doing any actual work, I know I only need to try about 28,212,474 seed combinations. Worst of all, if I manage to decrypt ONE MESSAGE, I can decrypt ALL MESSAGES. It only requires me to guess one seed value for me to know the key. An XOR relates it's inputs, so once we figure out one input we can determine via the relations all the other values. To some things up: *Your seed generator does a crappy job of generating random data, the way you are using it, it reveals too much information to the user and it will expose weak spots in the key. Because it's first character is always "1" we know the first character of your key. *You aren't protecting your secret with a seeded trapdoor function. Since the key is used directly, one cracked message means the key has been compromised.
Last edited by Strife Onizuka : 05-13-2008 at 09:01
AM. |
05-13-2008, 10:26 AM | #3 |
Registered User
Join Date: Dec 2007
Posts: 40
|
I kinda figured using all numbers for a key
wasn't good. I did that for simplicity. For my production script I was
actually planning on using string of random letters instead of random
numbers. I was also thinking of simply MD5 the random number to make a random string. I imagine that would help? |
05-14-2008, 12:25 PM | #4 |
Moonchild
Join Date: Mar 2004
Location: !quit !quit !quit !quit
Posts: 5,572
|
MD5 is a fine trapdoor function just be sure
that the inputs aren't easily guessed. If you use llMD5String, and the
input string is known but not the seed, the attacker can brute force the
seed in less then 24 hours. Lets say we do something like AB = XOR(A, B) and AC = XOR(A,C) and then ABAC = XOR(AB, AC). ABAC will have a pattern where it repeats ever Smallest Common Multiple of B and C. The number of uniquely linked fields is equal to the Largest Common Factor of B and C. I forgot to mention that the XOR doesn't hide the letter occurrence probabilities. Certain letters are used more then other letters in English, given a long enough message those probabilities hold out strong enough that guess about message values can be made. You can't quickly encrypt messages in LSL with any assurance they can't be decrypted but you can sign them quickly and if you were not worry about it being broken. If you ever write an alg that you would like reviewed, I do run a small service where I review encryption algs and provide feedback & suggestions for improvements. All informations and communications about such projects are kept in the strictest of secrecy. |
05-14-2008, 04:28 PM | #5 |
Registered User
Join Date: Jul 2006
Posts: 1,330
|
I suspect that if you implement your own
pseudo-random number generator and simply encrypt a seed (so long as you
think of how to use a decently long seed--perhaps breaking it up to
produce multiple pseudo-random number sequences), you might be able to
generate a mask without serious performance problems that will be quite a
lot better than a simple repeated number or string. |
05-14-2008, 04:59 PM | #6 | |
Registered User
Join Date: Jan 2007
Posts: 968
|
Quote:
This point is worth extra attention. Many people ask for encryption when what they really need is authentication. | |
05-15-2008, 05:46 AM | #7 |
Registered User
Join Date: Dec 2007
Posts: 40
|
Thank you very much to the folks that
responded. I've been reading up on a lot of this. Unfortunatly, I might as well be reading a passage of Lorum Ipsum for all the sense most of it makes. I was hoping to produce something open that peope can use...but it seems best practice here would be to keep your algos private since no matter what you do there is no strong encryption technique. Seems that obfuscation and confusion would be just as valid as anything else currently available. - zg |
05-15-2008, 04:46 PM | #8 | |
Registered User
Join Date: Jan 2007
Posts: 968
|
Quote:
There's been a fair bit written about encryption in the forum. You should read [url]http://forums.secondlife.com/showthread.php?t=171296[/url] if you haven't already. I haven't been following the discussions closely, but I think you may find an acceptable system based on XTEA. How much is at risk? If it's possible for someone to steal a significant amount of money, or violate important privacy, then obfuscation isn't enough. What's the worst that could happen if someone cracked one of your messages? All of them? | |
05-15-2008, 09:10 PM | #9 |
Moonchild
Join Date: Mar 2004
Location: !quit !quit !quit !quit
Posts: 5,572
|
Continue what I was saying before (Sorry for
being tired then and not making much sense). Fast and secure encryption in LSL can't happen. LSL is too slow. If your application needs strong encryption, should you really be running it on a machine you don't own and can't control? If your app needs strong encryption you are putting a lot of faith in LL and it's product. Your connection to the sim isn't encrypted, anyone could intercept the asset uploads meaning when you write the script the text could stolen. Most applications don't actually need encryption, they just need to ensure nobody is spoofing the sender and that the message is hard to read. |