basic_robot_csm/scripts/messenger_v2.lua

459 lines
22 KiB
Lua

--MESSENGER with rnd auth and key exchange v2
-- v05162018b
-- 'public' key is exchanged securely (2048 bit diffie-hellman in safe-prime group). After that chat will be secure in future.
-- Every session key is randomized and securely exchanged with srp (secure remote password) like protocol.
if not init then
msgerver = "05172018a"
targetid = {id = "qtest", name = "qtest"} -- WHO you want to talk to - real identity & playername
myid = {id = "rnd", name = minetest.localplayer:get_name()} -- your identity
-- targetid = {id = "rnd", name = "rnd"} -- real identity & playername
-- myid = {id = "qtest", name = minetest.localplayer:get_name()}
------------------------------------------------------
DEBUG = false;
-- SECRET KEYS: for yourself write in: private key, public key. for other player write {} in place of private key.
-- keys are also loaded from mod_storage.
keys = minetest.deserialize(self.mod_storage:get_string("messenger_keys"));
-- keys = {
-- ["qtest"] ={ -- example key
-- {48956649,6888054,39208061,49336825,13139894,28782086,33649864,52015757,41052181,6366916,19073280,28595777,9004077,49422303,3197447,56119809,34730143,60487773,51346526,36586011},
-- {10668758,19566477,29269435,16175584,54357471,16537273,10758806,19610241,2888871,9931787,22143488,49139611,41680003,63337704,54062559,9659600,2850950,62931196,37706701,11112986}
-- },
-- }
keygen = 0; -- both players set this to 1 to generate keys, 0 normal operation
timeout = 5; -- unused (yet)
chatchar1 = "''";
chatchar0 = ": ";
self.msg_filter(targetid.name .. chatchar0 .. chatchar1) -- PM from name to name: @@xxxx
init = true
mode = 0;
state = 0;
step = 0;
welcomemsg = function()
if keygen == 1 then
say(minetest.colorize("red", "#MESSENGER v" .. msgerver .. ". hold w+s to generate and exchange private/public key or wait to receive one. say ,1 to view existing keys and ,2 to delete all keys."))
else
say(minetest.colorize("red", "#MESSENGER v" .. msgerver .. ". hold w+s to establish authenticated secure connection with " .. targetid.id .. " or wait to receive one.say ,1 to enter key management."))
end
end
welcomemsg()
crypto = _G.crypto; bignum = _G.bignum;
crypto.randomseed(os.time()) -- weak, TODO: use better random generator with better randomness source
-- generate safe prime in openssl: openssl prime -generate -safe -bits 512 -hex
-- then import it here
local importsshprime = function(GH, base)
local G1 = bignum.importhex(GH); local G2 = bignum.base2binary(G1);
local G3 = bignum.binary2base(G2,base);return minetest.serialize(G3.digits)
end
--GH = "";local code = importsshprime(GH,2^26)
--local form = "size[5,5] textarea[0,0;6,6;MSG;MESSAGE;" .. minetest.formspec_escape(code) .. "]"
--local t = os.clock();local barrett = bignum.get_barrett(p512);say(os.clock()-t) -- precompute barrett form
--local code = minetest.serialize(barrett)
--minetest.show_formspec("robot", form)
--512 bit
--c1d3a133c9b3720da868dda10b6a0bde0e1a47d797d3e02f2673157ad26c33970553352abd72114a48813b3f1a3d86120c2150d9c33780bf0ce31acf2e28b813
p512 = {
base = 2^26, sgn = 1,
digits = {198478,34815131,14451562,6872481,2992175,31515044,32892404,65023782,30168555,19317561,29447373,19578226,4532514,1291249,42951044,34349392,57085150,782542,13022155,36222995}
}
barrett512 = {
["k"] = 42, ["m"] = {["base"] = 67108864, ["sgn"] = 1,
["digits"] = {35898149, 41794076, 18684097, 46016930, 38889135, 63030343, 34383953, 50385002, 5837211, 26975470, 58748693, 26637596, 30851364, 25453708, 59604851, 52048498, 48309457, 2932823, 30408552, 42076151, 59509517, 57220987, 1}}, ["n"] = {["base"] = 67108864, ["sgn"] = 1, ["digits"] = {198478, 34815131, 14451562, 6872481, 2992175, 31515044, 32892404, 65023782, 30168555, 19317561, 29447373, 19578226, 4532514, 1291249, 42951044, 34349392, 57085150, 782542, 13022155, 36222995}}
}
--2048 bit
--db726369acb4a51666ee14e0dc4305afc11692cc0dfa9d06b399ebc7b541b095ca3f48633ed936e0d4633af1c8b72886829c5fdd98861c44acdadc54075dc3beeb3d4a4bf9fb13b2c943e8bcb8c8df4440a84753c87d1512ff3db5083941ac88764c674da50771fdd7f4db99d7281e653253191df1f0137004b81488ecf9d15c49462c5438d4c060fa5a36e3e8e73ca1969d312b1b11df3e6fa3ce0a87641f4007884470d4911da45df914143c2c446a51443d6595c84cf83467825cf08007546e04c5137acac3ec0f413c522f5904d3b5230b4f5f2a26a0a8ad318ab541d1cd69079b0cb040827e2eb48f4fb7bc76623b96c7d38c603a186e24ae70a3bd66b3
p2048 = {
base = 2^26, sgn = 1,
digits = {62744243, 19635240, 60917474, 55456128, 37459655, 32447896, 55112955, 34207930, 51163200, 56246758, 55844124, 45401642, 36085928, 47437770, 20664880, 12411923, 54606930, 45153027, 5322668, 22132755, 15761415, 18473111, 8703875, 16094807, 6967620, 17763089, 31428929, 38041233, 4485332, 63963618, 11040321, 29265720, 51502910, 55331526, 63576425, 59745180, 16407094, 37040152, 6473027, 54882597, 8973561, 77317, 52363575, 21783671, 1991986, 48657866, 64847693, 43261383, 38561613, 7021085, 55608212, 4979958, 63470869, 2757076, 9303108, 61010659, 62048579, 50233028, 45339812, 24579835, 47993863, 51456822, 31033441, 34238847, 12003462, 13548658, 57544006, 26016612, 30031688, 22047781, 27180155, 41163470, 46927354, 66078116, 29634650, 62411651, 10819174, 14314285, 898854}
}
barrett2048 = {
["k"] = 160, ["m"] = {["base"] = 2^26, ["sgn"] = 1,
["digits"] = {52173231, 58926316, 7134135, 58005281, 29642925, 1996, 43704342, 19181367, 51834951, 40753938, 5670116, 25083444, 53681109, 39194353, 21018693, 16186348, 52641345, 34474171, 45582158, 65632598, 6663244, 17715531, 46582518, 715815, 35941432, 62741031, 14063905, 37500214, 33724930, 25815530, 29521098, 42349641, 30021354, 56331771, 20197595, 44642351, 39774, 15935544, 18538053, 54085894, 20262092, 170794, 877950, 7075184, 15922733, 42275553, 19627281, 61124663, 6351068, 20488035, 52369744, 26751026, 17905178, 25990200, 47243983, 42954366, 65859731, 23375626, 40610711, 9951837, 9139091, 55630155, 4911180, 17490059, 43409254, 37055369, 1417704, 12145177, 9055946, 41623298, 33230333, 1111792, 21966597, 20877280, 44498082, 6831928, 22418430, 4437100, 27282748, 12568457, 44322344, 74}}, ["n"] = {["base"] = 67108864, ["sgn"] = 1, ["digits"] = {62744243, 19635240, 60917474, 55456128, 37459655, 32447896, 55112955, 34207930, 51163200, 56246758, 55844124, 45401642, 36085928, 47437770, 20664880, 12411923, 54606930, 45153027, 5322668, 22132755, 15761415, 18473111, 8703875, 16094807, 6967620, 17763089, 31428929, 38041233, 4485332, 63963618, 11040321, 29265720, 51502910, 55331526, 63576425, 59745180, 16407094, 37040152, 6473027, 54882597, 8973561, 77317, 52363575, 21783671, 1991986, 48657866, 64847693, 43261383, 38561613, 7021085, 55608212, 4979958, 63470869, 2757076, 9303108, 61010659, 62048579, 50233028, 45339812, 24579835, 47993863, 51456822, 31033441, 34238847, 12003462, 13548658, 57544006, 26016612, 30031688, 22047781, 27180155, 41163470, 46927354, 66078116, 29634650, 62411651, 10819174, 14314285, 898854}}
}
password0 = {123456789}; -- cosmetics only
get_randomness = function() -- ~ 100 bits. TODO: improve and use this to create better random integers
say(os.clock() .. minetest.serialize(minetest.localplayer:get_pos()) .. minetest.serialize(minetest.camera:get_look_dir()))
end
--get_randomness()
local DH_2048_test = function()
local base = 2^26
-- order of element in Z_p* must divide |Z_p*|= p-1. since p is safe prime, p-1=2q for prime q. so either order is 2 or q.
local g = bignum.new(base, 1, {2}) -- order of this is obviously not 2, so its (p-1)/2
local m = 80; -- 80*26 = 2080 bit exponent
local b = bignum.rnd(base, 1, m)
local c = bignum.rnd(base, 1, m)
local t = os.clock();
local resb = bignum.modpow(g,b, barrett2048); -- g^b mod p2048
say("g^b time " .. os.clock()-t)
local resc = bignum.modpow(g,c, barrett2048); -- g^c mod p2048
say("g^c time " .. os.clock()-t)
local resbc = bignum.modpow(resb,c, barrett2048); -- g^bc mod p2048
say("g^bc time " .. os.clock()-t)
local rescb = bignum.modpow(resc,b, barrett2048); -- g^cb mod p2048
say("g^cb time " .. os.clock()-t)
if bignum.is_equal(resbc,rescb) then say("equality check g^bc = g^cb PASSED.") else say("equality check g^bc = g^cb FAILED.")end
say(os.clock()-t)
end
--DH_2048_test()
local rndexchange_test = function()
local base = 2^26;
local g = bignum.new(base, 1, {2})
local m = 20; -- 20*26 = 520 bits
local t = os.clock();
local x = bignum.rnd(base, 1, m) -- use better randomseeds for real one
local v = bignum.modpow(g,x,barrett512); -- 'public' key that A(client alice) and B(server) both know.
say("RND KEY EXCHANGE PROTOCOL v2")
say("0. PUBLIC KEY v = " .. bignum.tostring(v))
-- 1. B picks random r and tells A y=g^r+v.
local r = bignum.rnd(base,1,m); local gr = bignum.modpow(g,r,barrett512);
local y = bignum.new(base,1,{}); bignum._add(gr,v,y); -- send y to other party
say(" 1.1 B sends A: y = " .. bignum.tostring(v))
say("2. IF CONFIRM IDENTIY : A confirms identity by sending back hash((y-v)^x) = hash(v^r) = hash(g^rx) to prove to B he know x.")
local temp = bignum.new(base,1,{}); bignum._sub(y,v,temp);
local yvx = bignum.modpow(temp, x, barrett512)
say(" 2.1 A computes (y-v)^x = " .. bignum.tostring(yvx))
say(" hash = " .. crypto.rndhash(bignum.tostring(yvx),512))
--
local vr = bignum.modpow(v, r, barrett512)
say(" 2.2 B computes v^r = " .. bignum.tostring(vr))
say(" hash = " .. crypto.rndhash(bignum.tostring(vr),512))
if bignum.is_equal(vr,yvx) then say("equality check PASSED.") else say("equality check FAILED.") end
say("3. SESSION KEY K=g^rx = (y-v)^x = v^r = " .. crypto.rndhash(" " .. bignum.tostring(vr),256))
say("time " .. os.clock()-t)
end
--rndexchange_test()
empty_chat_buffer = function()
local msg = ""; while msg do msg = self.listen_msg() end
end
empty_chat_buffer()
self.listen_msg(); -- empty sent msg buffer
extract_digits = function(msg)
local digits = {}
for word in string.gmatch(msg, "([^']+)") do
local c = tonumber(word);
if not c then return nil end
digits[#digits+1] = c
end
return digits
end
--self.remove()
end
msg = self.listen_msg();
if msg then msg = minetest.strip_colors(msg) end;
if keygen == 1 then -- generating & exchanging 'public' key for one of the clients
-- state 0: mode 0: receive G^b in 2 parts (step 1,2), compute session key -> mode 1: send G^c in 2 parts (step 1,2)
-- -> mode 2: receive v = g^x -> keygen = 0, state = 0 (key computed, ready to proceed with chat operations)
if state == 0 then -- receiving (state 0, mode 0 = idle )
if mode == 0 then
if msg then
local i = string.find(msg,chatchar1);
local dec = crypto.decrypt(string.sub(msg,i+string.len(chatchar1)), password0);
if string.sub(dec,1,1) ~= " " then
say("ERROR receiving Gb. cant decrypt. resetting."); step = 0
else
step = step+1;
if step<=2 then
if not Gb then Gb = {} end --dec = " sxxxxx", s = step
Gb[tonumber(string.sub(dec,2,2))] = string.sub(dec,3)
else
say("ERROR receiving Gb. step " .. step .. ". aborting."); step =0; mode = 0;
end
if step == 2 then -- received 2 parts of Gb
step = 0
mode = 1 -- sending Gc
-- extract & compute session key TODO
local digits = {};
for i = 1,2 do
local edigits = extract_digits(Gb[i]);
if not edigits then
say("ERROR, RECEIVED CORRUPTED Gb. ABORTING."); step = 0;mode = 0; i=3; break;
else
for i = 1,#edigits do
digits[#digits+1] = edigits[i]
end
end
end
if mode == 1 then -- was receipt ok?
Gb = {sgn=1,base = 2^26, digits = digits};
local m = 80; local base = 2^26
c = bignum.rnd(base, 1, m)
local G = bignum.new(base, 1, {2})
Gc = bignum.modpow(G,c, barrett2048)
Gbc = bignum.modpow(Gb,c, barrett2048) -- session key
local Gc_ = Gc.digits;
local n = math.floor(#Gc_/2); -- break in 2 parts
send = {}; local ret = {}; -- send = string of digits to send
for i = 1, n do ret[i] = Gc_[i] end
send[1] = table.concat(ret,"'"); ret = {};
for i = n+1, #Gc_ do ret[i-n] = Gc_[i] end
send[2] = table.concat(ret,"'");
say("RECEIVED Gb and computed Gc, session key Gbc. Sending Gc ")
end
end
end
else
local keypressed = minetest.localplayer:get_key_pressed()
--say("KEY " .. keypressed)
if keypressed == 3 then
if keys[myid.id] then
say("WARNING! key for " .. myid.id .. " already exists. hold SHIFT+w+s send existing key. hold shift+a+d to create new key.")
else
say("GENERATING PUBLIC/PRIVATE KEY PAIR & ESTABLISHING SECURE 2048 bit CONNECTION...")
state = 1; mode = 0; step = 0 --> sending Gb
end
elseif keypressed == 67 then
say("GENERATING PUBLIC/PRIVATE KEY PAIR & ESTABLISHING SECURE 2048 bit CONNECTION...")
state = 1; mode = 0; step = 0 --> sending Gb
elseif keypressed == 76 then
say("GENERATING PUBLIC/PRIVATE KEY PAIR & ESTABLISHING SECURE 2048 bit CONNECTION...")
keys[myid.id] = nil
state = 1; mode = 0; step = 0 --> sending Gb
end
end
msg = self.sent_msg();
if msg then
if msg == "1" then
msg = minetest.serialize(keys)
local form = "size[10.5,10] textarea[0,0;11,12;MSG;KEYS;" .. minetest.formspec_escape(msg) .. "]"
minetest.show_formspec("robot", form);
elseif msg == "2" then
keys = {};
self.mod_storage:set_string("messenger_keys", "return {}")
say("ALL KEYS DELETED!")
end
end
elseif mode == 1 then -- sending Gc
step = step + 1
say("/msg " .. targetid.name .. " " .. chatchar1 .. crypto.encrypt(" " .. step..send[step], password0),true)
if step == 2 then mode = 2 say("Gc sent. Waiting to receive v= g^x") end -- receive public key g^x
elseif mode == 2 then -- receiving v
if msg then
local i = string.find(msg,chatchar1);
local dec = crypto.decrypt(string.sub(msg,i+string.len(chatchar1)), Gbc.digits);
if string.sub(dec,1,1) ~= " " then
say("ERROR receiving public key v = g^x. cant decrypt. ABORTING."); state = 0; mode = 0;
else
local digits = {};
local edigits = extract_digits(string.sub(dec,2));
if not edigits then
say("ERROR, RECEIVED CORRUPTED v");
else
msg = "public key = {"..table.concat(edigits,",").."}";
local form = "size[5,5] textarea[0,0;6,6;MSG;PUBLIC KEY FROM " .. targetid.name .. ";" .. minetest.formspec_escape(msg) .. "]"
minetest.show_formspec("robot", form);
keys[targetid.id] = {{},v.digits}; -- store key
self.mod_storage:set_string("messenger_keys", minetest.serialize(keys)) -- save keys in mod_storage
send = nil; Gbc = nil; c = nil; Gb = nil; Gc = nil; --cleanup
keygen = 0; mode = 0; state = 0;
end
end
end
end
-- state 1: mode 0: send G^b in 2 steps (step 1,2)-> mode 1: wait for G^c in 2 steps (step 1,2)->
-- compute session key and send encrypted public key v = g^x -> display public and private key, state = 0, keygen = 0
elseif state == 1 then -- sending
if mode == 0 then
step = step + 1
if step == 1 then
local m = 80; local base = 2^26
b = bignum.rnd(base, 1, m)
local G = bignum.new(base, 1, {2})
Gb = bignum.modpow(G,b, barrett2048)
local Gb_ = Gb.digits;
local n = math.floor(#Gb_/2); -- break in 2 parts
send = {}; local ret = {}; -- send = string of digits to send
for i = 1, n do ret[i] = Gb_[i] end
send[1] = table.concat(ret,"'"); ret = {};
for i = n+1, #Gb_ do ret[i-n] = Gb_[i] end
send[2] = table.concat(ret,"'");
say("Sending Gb ")
say("/msg " .. targetid.name .. " " .. chatchar1 .. crypto.encrypt(" " .. step..send[step], password0),true)
elseif step <= 2 then
say("/msg " .. targetid.name .. " " .. chatchar1 .. crypto.encrypt(" " .. step..send[step], password0),true)
if step == 2 then mode = 1;step = 0 say("waiting for Gc") end-- wait for Gc
end
elseif mode == 1 then
if msg then
local i = string.find(msg,chatchar1);
local dec = crypto.decrypt(string.sub(msg,i+string.len(chatchar1)), password0);
if string.sub(dec,1,1) ~= " " then
say("ERROR receiving G^c. cant decrypt. ABORTING."); state = 0; mode = 0;
else
step = step + 1
if not Gc then Gc = {} end -- dec = " sxxxxx", s = step
Gc[tonumber(string.sub(dec,2,2))] = string.sub(dec,3)
if step == 2 then
local digits = {};
for i = 1,2 do
local edigits = extract_digits(Gc[i]);
if not edigits then
say("ERROR, RECEIVED CORRUPTED Gc. ABORTING."); step = 0;mode = 0; i=3; break;
else
for i = 1,#edigits do
digits[#digits+1] = edigits[i]
end
end
end
Gc = {sgn=1,base = 2^26, digits = digits};
local m = 80; local base = 2^26
local G = bignum.new(base, 1, {2})
local Gcb = bignum.modpow(Gc,b, barrett2048) -- session key
if not keys[myid.id] then
-- compute v = g^x and send it
x = bignum.rnd(base, 1, m/4) -- private key: m=20, 520 bit!
local G = bignum.new(base, 1, {2})
v = bignum.modpow(G,x, barrett512) -- public key v = g^x
else -- take existing key
x = bignum.new(base, 1, keys[myid.id][1])
v = bignum.new(base, 1, keys[myid.id][2])
end
-- password is Gcb.digits - 2048 bit
say("/msg " .. targetid.name .. " " .. chatchar1 .. crypto.encrypt(" " .. table.concat(v.digits,"'"), Gcb.digits),true)
keygen = 0; mode = 0; state = 0 -- normal chat operation
-- display keys so they can be written down
msg = "private x = {"..table.concat(x.digits,",").."}\n public v = {" .. table.concat(v.digits,",").."}";
if not keys[myid.id] then
keys[myid.id] = {x.digits,v.digits}; -- store key
self.mod_storage:set_string("messenger_keys", minetest.serialize(keys)) -- save keys in mod_storage
end
local form = "size[5,5] textarea[0,0;6,6;MSG;PRIVATE/PUBLIC KEY;" .. minetest.formspec_escape(msg) .. "]"
send = nil; Gc = nil; Gcb = nil; x = nil; v = nil; b = nil; Gb = nil; --cleanup
minetest.show_formspec("robot", form);
end
end
end
end
end
else -- END OF KEYGEN & DH KEY EXCHANGE
-- 1. B picks random r and tells A y=g^r+v.
-- Note here that listeners only see g^r+v so they have no clue what v is or what g^r is. Even from multiple sessions they
-- learn nothing if g is generator of whole group and r truly random, since then g^r can be 'anything' with same probability.
-- 2. OPTIONAL: A confirms identity by sending back hash(g^rx) = hash(v^r) = hash((y-v)^x) to prove you know a.
-- 3. session key is then K=g^rx = (g^r)^x = (g^x)^r.
if state == 0 then -- idle
if mode == 0 then
if msg then -- received y
local i = string.find(msg,chatchar1);
msg = string.sub(msg,i+string.len(chatchar1));
msg = crypto.decrypt(msg, password0)
if string.sub(msg,1,1) == " " then
local base = 2^26;
local y = bignum.new(base,1,extract_digits(msg));
local key = keys[myid.id];
if not key or not key[2] or not key[1] then say("ERROR: you need to add private/public key for " .. myid.id ..". put keygen = 1 and let " .. myid.id .. " hold w+s."); self.remove() end
local x = bignum.new(base,1,key[1]);
local v = bignum.new(base,1,key[2]);
local yv = bignum.new(base,1,{});bignum._sub(y,v,yv); -- yv = y-v
local t = os.clock()
sessionkey = bignum.modpow(yv,x, barrett512).digits -- yv^x
if DEBUG then say("time " .. os.clock()-t) end
--say("DEBUG SESSION KEY " .. minetest.serialize(sessionkey))
say(minetest.colorize("yellow", "MESSENGER " .. msgerver .. " READY."))
--local response = crypto.rndhash(table.concat(sessionkey,"'"),512) -- OPTIONAL
state = 1;
else
say("ERROR: wrong init packet. resetting. ")
init = false
end
elseif minetest.localplayer:get_key_pressed() == 3 then
say(minetest.colorize("red","GENERATING challenge and sending it to " .. targetid.name))
local key = keys[targetid.id];
if not key or not key[2] then say("ERROR: you need to add public key for " .. targetid.id..". put keygen = 1 and let " .. targetid.id .. " hold w+s."); self.remove() end
local base = 2^26; local m = 20;
local v = bignum.new(base,1,key[2]);
local r = bignum.rnd(base, 1, m)
local g = bignum.new(base, 1, {2})
local t = os.clock()
local gr = bignum.modpow(g,r, barrett512)
if DEBUG then say("time " .. os.clock()-t) end
local y = bignum.new(base,1,{});
bignum._add(gr,v,y) -- y = g^r+v
say("/msg " .. targetid.name .. " " .. chatchar1.. crypto.encrypt(" " .. table.concat(y.digits,"'"),password0),true) -- send challenge
sessionkey = bignum.modpow(v,r, barrett512).digits; -- v^r
say(minetest.colorize("yellow", "MESSENGER " .. msgerver .. " READY."))
--say("DEBUG SESSION KEY " .. minetest.serialize(sessionkey))
--response = crypto.rndhash(table.concat(sessionkey,"'"),512) -- OPTIONAL
state = 1; -- normal operation
end
msg = self.sent_msg();
if msg and msg == "1" then
keygen = 1; welcomemsg()
end
end
elseif state == 1 then
if msg then -- received message + decrypt it
local i = string.find(msg,chatchar1);
msg = string.sub(msg,i+string.len(chatchar1));
local dec = crypto.decrypt(msg, sessionkey);
if string.sub(dec,1,1)~=" " then
say(minetest.colorize("red", "WARNING: " .. targetid.name .. " is using different session key! Resetting." ))
-- reset target messenger by sending random garbage
minetest.after(crypto.random(3),
function() say("/msg " .. targetid.name .. " " .. chatchar1 .. crypto.rndhash(" " .. crypto.random(2^30)),true) end)
init = false;
else
say(minetest.colorize("lawngreen", "[" .. targetid.id .. "]" .. dec ))
end
end
msg = self.sent_msg(); -- sending encrypted message
if msg then
say(minetest.colorize("orange", "[" .. myid.id .. "] " .. msg ))
local length = string.len(msg); if length < 32 then msg = msg .. string.rep(" ",32-length) end
local t = os.clock()
say("/msg " .. targetid.name .. " " .. chatchar1 .. crypto.encrypt(" " .. msg,sessionkey),true)
if DEBUG then say("time " .. os.clock()-t) end
end
end
end