ComputerCraft Archive

Ringchat 1.2 - Encrypted chat via Ring LWE!

computer networking unknown forum

Description

@version 1.2

Installation

Copy one of these commands into your ComputerCraft terminal:

Pastebin:pastebin get p94WHguU ringchat_1.2_-_encrypted_chat_via_ring_lwe!
wget:wget https://pastebin.com/raw/p94WHguU ringchat_1.2_-_encrypted_chat_via_ring_lwe!
Archive:wget https://cc.shobie.xyz/cc/get/pb-p94WHguU ringchat_1.2_-_encrypted_chat_via_ring_lwe!
Quick Install: wget https://cc.shobie.xyz/cc/get/pb-p94WHguU Ringchat 1.2 - Encrypted chat via Ring LWE!

Usage

Run the program after downloading

Tags

networkingforumprograms

Source

View Original Source

Code Preview

--@version 1.2

local version = 1.2
local ht_up = http.get("https://pastebin.com/raw/p94WHguU")
local up_A = ht_up.readLine()
local up_B = ht_up.readAll()
local up = up_A .. up_B
ht_up.close()
local their_version = tonumber(up_A:sub(12))
if (their_version > version) then
	local fi_up = fs.open(shell.getRunningProgram(), "w")
	fi_up.write(up)
	fi_up.close()
	shell.run(shell.getRunningProgram())
	return
end

--stack trace builder by apemanzilla
local function buildStackTrace(rootErr)
	local trace = {}
	local i, hitEnd, _, e = 4, false
	repeat
		_, e =
			pcall(
			function()
				error("<tracemarker>", i)
			end
		)
		i = i + 1
		if e == "xpcall: <tracemarker>" or e == "pcall: <tracemarker>" then
			hitEnd = true
			break
		end
		table.insert(trace, e)
	until i > 10
	table.remove(trace)
	table.remove(trace, 1)
	if rootErr:match("^" .. trace[1]:match("^(.-:%d+)")) then
		table.remove(trace, 1)
	end
	local out = {}
	table.insert(out, rootErr)
	for i, v in ipairs(trace) do
		table.insert(out, "  at " .. v:match("^(.-:%d+)"))
	end
	if not hitEnd then
		table.insert(out, "  ...")
	end
	return table.concat(out, "\n")
end

local ok, err =
	xpcall(
	function()
		---------------------------------
		-- BLITWRAP BY ELDIDIDESTROYER --
		---------------------------------

		local explode = function(div, str, replstr, includeDiv)
			if (div == "") then
				return false
			end
			local pos, arr = 0, {}
			for st, sp in function()
				return string.find(str, div, pos, false)
			end do
				table.insert(arr, string.sub(replstr or str, pos, st - 1 + (includeDiv and #div or 0)))
				pos = sp + 1
			end
			table.insert(arr, string.sub(replstr or str, pos))
			return arr
		end

		local blitwrap = function(char, text, back, noWrite)
			local cWords = explode(" ", char, nil, true)
			local tWords = explode(" ", char, text, true)
			local bWords = explode(" ", char, back, true)
			local ox, oy = term.getCursorPos()
			local cx, cy, ty = ox, oy, 1
			local scr_x, scr_y = term.getSize()
			local output = {}
			for a = 1, #cWords do
				if ((cx + #cWords[a]) > scr_x) then
					cx = 1
					if (cy == scr_y) then
						term.scroll(1)
					end
					cy = math.min(cy + 1, scr_y)
					ty = ty + 1
				end
				if not noWrite then
					term.setCursorPos(cx, cy)
					term.blit(cWords[a], tWords[a], bWords[a])
				end
				cx = cx + #cWords[a]
				output[ty] = output[ty] or {"", "", ""}
				output[ty][1] = output[ty][1] .. cWords[a]
				output[ty][2] = output[ty][2] .. tWords[a]
				output[ty][3] = output[ty][3] .. bWords[a]
			end
			return output
		end

		------------------
		-- BLITWRAP END --
		------------------

		local pingdata_channel = 895
		local connect_channel = 885

		local d = false
		local mod = nil
		local our_rids = {}
		for k, v in pairs(peripheral.getNames()) do
			if peripheral.getType(v) == "modem" then
				rednet.open(v)
				mod = peripheral.wrap(v)
				d = true
			end
		end
		if not d or not mod then
			error("A modem is required!")
		end

		local dump_log = {}

		local function download_file(url, target)
			local ht = http.get(url)
			local data = ht.readAll()
			ht.close()
			local file = fs.open(target, "w")
			file.write(data)
			file.close()
		end

		local function read_file(target)
			local file = fs.open(target, "r")
			local data = file.readAll()
			file.close()
			return data
		end

		local function isOurRid(id)
			for k, v in pairs(our_rids) do
				if v == id then
					return true
				end
			end
			return false
		end

		local servers = {}
		local serber = nil
		local truelog = {}
		--------------
		-- UI stuff --
		--------------
		local s_offset = 0
		local current_server = 1
		local chat_win = nil
		local type_win = nil

		local chat_w, chat_h

		local function cwrite(t, str, y)
			local w, h = t.getSize()
			t.setCursorPos(math.floor((w / 2) - (str:len() / 2)), y or (h / 2))
			t.write(str)
		end

		local function setup_win()
			local tn = term.native()
			local w, h = tn.getSize()
			chat_win = window.create(tn, 1, 1, w, h - 1, true)
			chat_win.setCursorBlink(false)
			chat_win.setBackgroundColor(colors.black)
			chat_win.setTextColor(colors.white)
			chat_win.clear()
			chat_w, chat_h = chat_win.getSize()
			type_win = window.create(tn, 1, h, w, 1, true)
			type_win.setBackgroundColor(colors.black)
			type_win.setTextColor(colors.lightGray)
			type_win.clear()
			term.redirect(type_win)
		end

		local function kill(terminated)
			if (type(term.native) == "function") then
				term = m
				term.redirect(term.native())
				term = term.native()
			else
				term.redirect(term)
			end
			term.setBackgroundColor(colors.black)
			term.setTextColor(colors.white)
			term.clear()
			chat_win = nil
			type_win = nil

			if (serber and serber.cid) then
				ringnet.sendData(
					serber.cid,
					textutils.serialize(
						{
							type = "CHAT_LOGOUT"
						}
					)
				)
			end
		end

		local function writeRight(text, y)
			local w, h = term.getSize()
			term.setCursorPos(w - #text, y)
			term.write(text)
		end

		local function draw_list()
			if #servers < 1 then
				error("No servers found.", 0)
			end
			term.setBackgroundColor(colors.black)
			term.clear()
			for i = 1, #servers do
				local serv = servers[i]
				if i == current_server then
					term.setBackgroundColor(colors.white)
					term.setTextColor(colors.black)
				else
					term.setBackgroundColor(colors.black)
					term.setTextColor(colors.white)
				end
				term.setCursorPos(1, i)
				term.clearLine()
				term.write(serv.name)
				writeRight(tostring(serv.fingerprint):sub(1, 8), i)
			end
			local _w, _h = term.getSize()
			term.setBackgroundColor(colors.black)
			term.setTextColor(colors.white)
			cwrite(term, "Press Enter to join a server.", _h)
		end

		local function draw_chat()
			chat_win.setBackgroundColor(colors.black)
			chat_win.setTextColor(colors.white)
			chat_win.clear()
			for y = 1, chat_h do
				local o = truelog[(#truelog + s_offset - chat_h) + y]
				if (type(o) == "table") then
					chat_win.setCursorPos(1, y)
					chat_win.blit(o[1], o[2], o[3])
				end
				y = y + 1
			end
		end

		--------------
		--  UI End  --
		--------------

		-- API Loading
		local function getAPI(name, url)
			local apiloc = "/" .. name
			if (not fs.exists(apiloc)) then
				local newapiloc = shell and shell.resolveProgram(name) or nil
				if type(newapiloc) ~= "string" then
					download_file(url, apiloc)
				else
					apiloc = newapiloc
				end
			end
			return apiloc
		end

		local ringnet = require(getAPI("ringnet", "https://pastebin.com/raw/tUe94t9J"))
		getAPI("aeslua", "https://git.io/aeslua")
		os.loadAPI("aeslua")
		ringnet.openChannel(pingdata_channel)
		ringnet.openChannel(connect_channel)

		local tid = nil

		term.setBackgroundColor(colors.black)
		term.setTextColor(colors.white)
		term.clear()
		cwrite(term, "Scanning for servers...")

		local rid = math.random(100000, 999999)
		table.insert(our_rids, rid)
		mod.transmit(
			pingdata_channel,
			pingdata_channel,
			textutils.serialize(
				{
					type = "SERVER_DATA",
					rid = rid
				}
			)
		)

		parallel.waitForAll(
			ringnet.connectionHandler,
			function()
				sleep(1.75)
				os.queueEvent("timer_chat")
				return
			end,
			function()
				local mode = 1
				local timer_tripped = false
				while true do
					local ev = {os.pullEvent()}
					table.insert(dump_log, {mode, ev[1]})
					if (ev[1] == "modem_message" and not timer_tripped) then
						local mes = ev[5]
						if (type(mes) == "string") then
							mes = textutils.unserialize(mes)
						end
						if
							(mes.type == "SERVER_RETURNDATA" and isOurRid(mes.to) and type(mes.hid) == "string" and string.len(mes.hid) == 8)
						 then
							--print("Server! Name: " .. tostring(mes.name) .. " | Fingerprint: " .. tostring(mes.fingerprint):sub(1, 12))
							table.insert(
								servers,
								{
									name = mes.name,
									fingerprint = mes.fingerprint,
									hid = mes.hid,
									locked = mes.locked or false
								}
							)
						end
					elseif (mode == 1 and ev[1] == "timer_chat") then
						mode = 2
						timer_tripped = true
						draw_list()
					elseif (mode == 2 and ev[1] == "key") then
						if (ev[2] == 208 or ev[2] == 205) then
							current_server = current_server + 1
							if (current_server > #servers) then
								current_server = 1
							end
						elseif (ev[2] == 200 or ev[2] == 203) then
							current_server = current_server - 1
							if (current_server < 1) then
								current_server = #servers
							end
						elseif (ev[2] == 28) then
							mode = 3
							serber = servers[current_server]
							print(serber.hid)
						end
						if (ev[2] ~= 28) then
							draw_list()
						end
					elseif (mode == 3) then
						ringnet.openTunnel(serber.hid, connect_channel)
						mode = 4
					elseif (mode == 4 and ev[1] == "tunnel_finish" and ev[3] == serber.hid) then
						--elseif (mode == 5 and ev[1] == "mouse_scroll") then
						--	s_offset = (s_offset) + ev[2]
						--	s_offset = math.min(math.max(s_offset, 0), #truelog)
						--	draw_chat()
						serber.cid = ev[2]
						setup_win()
						mode = 5
					elseif (mode == 5 and ev[1] == "secure_receive" and ev[2] == serber.cid) then
						local cid = ev[2]
						local data = ev[3]
						if (serber.password) then
							data = aeslua.decrypt(serber.password, data, aeslua.AES128, aeslua.CFBMODE)
						end
						if (type(data) == "string") then
							data = textutils.unserialize(data)
						end
						if (type(data) == "table") then
							table.insert(dump_log, textutils.serialize(data))
							if (data.type == "CHAT_LOG" and type(data.log) == "table") then
								truelog = {}
								for i = 1, #data.log do
									local a = data.log[i]
									if ((type(a.name) == "string" or a.raw == true) and type(a.mes) == "string") then
										if not a.raw then
											local prefix = (a.name .. ": ")
											local dje = (a.mes)
											local combined = (prefix .. dje)
											local output =
												blitwrap(combined, (string.rep("0", #prefix) .. string.rep("8", #dje)), string.rep("f", #combined), true)
											for k, v in pairs(output) do
												table.insert(truelog, v)
											end
										else
											local output = blitwrap(a.mes, string.rep("8", #a.mes), string.rep("f", #a.mes), true)
											for k, v in pairs(output) do
												table.insert(truelog, v)
											end
										end
									end
								end
								local k = fs.open("/.TESTDUMP", "w")
								k.write(textutils.serialize(truelog))
								k.close()
								draw_chat()
							end
						end
					end
				end
			end,
			function()
				local named = false
				local still_locked = true
				while true do
					if (type_win ~= nil) then
						if not (serber.locked) then
							still_locked = false
						end
						type_win.setBackgroundColor(colors.black)
						type_win.setTextColor(colors.lightGray)
						type_win.clear()
						term.redirect(type_win)
						if (serber.locked and still_locked and not named) then
							cwrite(chat_win, "Please enter this server's password")
						elseif (not still_locked and not named) then
							cwrite(chat_win, "Please type the name you want below")
						end
						local a = tostring(read())
						if a == "_EXIT" then
							kill()
							error("Exited.", 0)
						elseif (serber.locked and still_locked) then
							if (aeslua.decrypt(a, serber.locked, aeslua.AES128, aeslua.CFBMODE) == "_A_COMMON_ENCRYPTION_STRING") then
								still_locked = false
								serber.password = a
							end
						elseif (not still_locked) then
							if named then
								if a:lower() == "/exit" or a:lower() == "/logout" then
									ringnet.sendData(
										serber.cid,
										textutils.serialize(
											{
												type = "CHAT_LOGOUT"
											}
										)
									)
									sleep(1.5)
									kill()
									error()
								elseif a:lower() == "/forceupdate" or a:lower() == "/update" then
									ringnet.sendData(
										serber.cid,
										textutils.serialize(
											{
												type = "CHAT_FORCEUPDATE"
											}
										)
									)
								else
									ringnet.sendData(
										serber.cid,
										textutils.serialize(
											{
												type = "CHAT_CHAT",
												message = a
											}
										)
									)
								end
							else
								ringnet.sendData(
									serber.cid,
									textutils.serialize(
										{
											type = "CHAT_LOGIN",
											name = a
										}
									)
								)
								chat_win.setBackgroundColor(colors.black)
								chat_win.setTextColor(colors.white)
								chat_win.clear()
								named = true
							end
						end
					end
					sleep(0.1)
				end
			end
		)
	end,
	function(err)
		term.redirect(term.native and term.native() or term.current())
		local stack = buildStackTrace(err)
		printError("\nRingchat has crashed! Stack trace:")
		printError(stack)
		local file = fs.open("/chat.err", "w")
		file.write(tostring(stack))
		file.close()
	end
)