An encrypted mode has been proposed by Fred Beckhusen using XTEA.lsl. Currently configuration (key) is via notecard but this is not a long-term solution, eventually the key will need to be in a no-mod script. It adds an integer to the listen protocol.
Fred's solution was to take the XTEA implementation library and add a small link_message wrapper that enables both encryption and decryption via link messages. A channel is supplied with the encryption requests so the cipher text can be sent to that channel via llRegionsSay().
An extension is to return the cipher text via link_message if no channel number is supplied.
The code Fred has in Control is very similar to that in the XTEA LSL <-> PHP section of that wiki page. The proposed implementation differs from Fred's:
These changes allow this script to be distributed in-world as no-modify/no-transfer and not violate any GPL/AGPL requirements. There is still a weakness in this scheme in that the script can be copied into other objects and used to decrypt intercepted communications.
// The name of the XTEA script
string XTEA_NAME = "r2-xtea";
// Set to encrypt 'message' and re-send on channel 'id'
integer XTEAENCRYPT = 13475896;
// Set in the reply to a received XTEAENCRYPT if the passed channel is 0 or ""
integer XTEAENCRYPTED = 8303877;
// Set to decrypt 'message' and reply vi llMessageLinked()
integer XTEADECRYPT = 4690862;
// Set in the reply to a received XTEADECRYPT
integer XTEADECRYPTED = 3450924;
integer haz_xtea = FALSE;
integer can_haz_xtea() {
// See if the XTEA script is present in object inventory
integer count = llGetInventoryNumber(INVENTORY_SCRIPT);
while (count--) {
if (llGetInventoryName(INVENTORY_SCRIPT, count) == XTEA_NAME) {
llOwnerSay("Found XTEA script");
return TRUE;
}
}
return FALSE;
}
send(string msg) {
if (haz_xtea) {
llMessageLinked(LINK_THIS, XTEAENCRYPT, msg, (string)r2channel);
} else {
llSay(r2channel, msg);
}
if (VERBOSE == 1) {
llOwnerSay("S: " + msg);
}
}
// ...
default {
state_entry() {
haz_xtea = can_haz_xtea();
// ...
}
link_message(integer sender_number, integer number, string message, key id) {
if (number == XTEAENCRYPTED) {
llOwnerSay("Ciphertext: " + message);
}
}
}
// The name of the XTEA script
string XTEA_NAME = "r2-xtea";
// Set to encrypt 'message' and re-send on channel 'id'
integer XTEAENCRYPT = 13475896;
// Set in the reply to a received XTEAENCRYPT if the passed channel is 0 or ""
integer XTEAENCRYPTED = 8303877;
// Set to decrypt 'message' and reply vi llMessageLinked()
integer XTEADECRYPT = 4690862;
// Set in the reply to a received XTEADECRYPT
integer XTEADECRYPTED = 3450924;
integer haz_xtea = FALSE;
integer can_haz_xtea() {
// See if the XTEA script is present in object inventory
integer count = llGetInventoryNumber(INVENTORY_SCRIPT);
while (count--) {
if (llGetInventoryName(INVENTORY_SCRIPT, count) == XTEA_NAME) {
llOwnerSay("Found XTEA script");
return TRUE;
}
}
return FALSE;
}
// ...
default {
state_entry() {
haz_xtea = can_haz_xtea();
// ...
}
listen(integer channel, string name, key id, string message) {
if (llGetOwnerKey(id) == llGetOwner()) {
if (channel == r2channel) {
// Check if message received is cleartext and handle
// ...
else {
if (haz_xtea) {
llMessageLinked(LINK_THIS, XTEADECRYPT, message, "");
}
}
}
}
link_message(integer sender_number, integer number, string message, key id) {
if (number == XTEADECRYPTED) {
list cmdargs = llCSV2List(message);
string command = llToUpper(llList2String(cmdargs, 0));
// handle decrypted message
// ...
}
}
}