--MESSENGER with rnd auth and key exchange v2
-- '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 = "05172018b"
	
	targetid = {id = "quater", name = "quater"} -- WRITE IN WHO you want to talk to - id = real identity & name = current 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"));	if not keys then keys = {} end
	-- 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(minetest.colorize("orange", "key management menu: say ,1 to view existing keys  ,2 to delete all keys ,0 for main menu."))
		else
			say(minetest.colorize("red", "#MESSENGER v" .. msgerver .. ". hold w+s to establish authenticated secure connection with ") .. minetest.colorize("yellow",targetid.id) .. minetest.colorize("red"," or wait to receive one."))
			say(minetest.colorize("orange", "main menu: say,1 to enter key management (generate & send, view, remove keys)."))
		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 = {36222995, 13022155, 782542, 57085150, 34349392, 42951044, 1291249, 4532514, 19578226, 29447373, 19317561, 30168555, 65023782, 32892404, 31515044, 2992175, 6872481, 14451562, 34815131, 198478}
	}

	barrett512 = {
		["k"] = 42, ["m"] = {["base"] = 67108864, ["sgn"] = 1, 
		["digits"] = {28949715, 54423101, 2288892, 15960629, 25093079, 30961036, 9893219, 18572583, 6060506, 53345934, 62824245, 21175570, 51585074, 64634085, 50626132, 23908947, 34461644, 49242303, 44848557, 11882353, 4640537, 7818826, 338}}, ["n"] = {["base"] = 67108864, ["sgn"] = 1, ["digits"] = {36222995, 13022155, 782542, 57085150, 34349392, 42951044, 1291249, 4532514, 19578226, 29447373, 19317561, 30168555, 65023782, 32892404, 31515044, 2992175, 6872481, 14451562, 34815131, 198478}}
	}

	--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"] = 67108864, ["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
						local part = string.sub(dec,2,2);
						if part~="1" and part~="2" then 
							say("ERROR receiving Gb.") step = 0; mode = 0;
						else
							Gb[tonumber(part)] = string.sub(dec,3)
						end
					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
					local ret = {}
					for k,v in pairs(keys) do ret[#ret+1] = k .. ":\n    private = {" .. table.concat(keys[k][1],",").."}\n    public = {" .. table.concat(keys[k][2],",").."}".."\n" end
					msg = table.concat(ret)
					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!")
				elseif msg == "0" then
					keygen = 0; state = 0; step = 0; welcomemsg()
				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] = {{},edigits}; -- store key
						self.mod_storage:set_string("messenger_keys", minetest.serialize(keys)) -- save keys in mod_storage
						say(minetest.colorize("orange","PUBLIC KEY FOR " .. targetid.id .. " RECEIVED AND SAVED. RESTART .b 1"))
						
						send = nil; Gbc = nil; c = nil; Gb = nil; Gc = nil;  --cleanup
						self.remove()
					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
					local part = string.sub(dec,2,2);
					if part~="1" and part~="2" then say("ERROR, RECEIVED CORRUPTED Gc. ABORTING."); step = 0;mode = 0;
					else
						Gc[tonumber(part)] = string.sub(dec,3)
					end
					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);
						self.remove()
						
					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 ..". both player should enter ,1 (key management) and let " .. myid.id .. " hold w+s to (generate) and send you his public key."); 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 private/public key for " .. targetid.id ..". both player should enter ,1 (key management) and let " .. targetid.id .. " hold w+s to (generate) and send you his public key."); 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