diff --git a/sys/apps/shell.lua b/sys/apps/shell.lua index 5d7f067..c5dbf28 100644 --- a/sys/apps/shell.lua +++ b/sys/apps/shell.lua @@ -71,6 +71,7 @@ local function run(env, ...) tProgramStack[#tProgramStack + 1] = path end + env[ "arg" ] = { [0] = path, table.unpack(args) } local r = { fn(table.unpack(args)) } tProgramStack[#tProgramStack] = nil diff --git a/sys/autorun/upgraded.lua b/sys/autorun/upgraded.lua index f988ba1..d3a2737 100644 --- a/sys/autorun/upgraded.lua +++ b/sys/autorun/upgraded.lua @@ -7,5 +7,6 @@ if fs.exists('sys/network') then fs.delete('sys/network') end if fs.exists('startup') then fs.delete('startup') end if fs.exists('sys/autorun/gps.lua') then fs.delete('sys/autorun/gps.lua') end +if fs.exists('sys/autorun/gpsHost.lua') then fs.delete('sys/autorun/gpsHost.lua') end if fs.exists('sys/apps/network/redserver.lua') then fs.delete('sys/apps/network/redserver.lua') end if fs.exists('sys/apis') then fs.delete('sys/apis') end diff --git a/sys/boot/opus.boot b/sys/boot/opus.boot index f3b5ffb..2a02d14 100644 --- a/sys/boot/opus.boot +++ b/sys/boot/opus.boot @@ -1,68 +1,28 @@ --- Loads the Opus environment regardless if the file system is local or not -local fs = _G.fs -local http = _G.http - -_G.OPUS_BRANCH = 'develop-1.8' -local GIT_REPO = 'kepler155c/opus/' .. _G.OPUS_BRANCH -local BASE = 'https://raw.githubusercontent.com/' .. GIT_REPO +local fs = _G.fs local sandboxEnv = setmetatable({ }, { __index = _G }) for k,v in pairs(_ENV) do sandboxEnv[k] = v end -_G._syslog = function() end - -local function makeEnv() +local function run(file, ...) local env = setmetatable({ }, { __index = _G }) for k,v in pairs(sandboxEnv) do env[k] = v end - return env -end -local function run(file, ...) - local s, m = loadfile(file, makeEnv()) + local s, m = loadfile(file, env) if s then return s(...) end error('Error loading ' .. file .. '\n' .. m) end -local function runUrl(file, ...) - local url = BASE .. '/' .. file - - local u = http.get(url) - if u then - local fn = load(u.readAll(), url, nil, makeEnv()) - u.close() - if fn then - return fn(...) - end - end - error('Failed to download ' .. url) -end +_G._syslog = function() end +_G.OPUS_BRANCH = 'develop-1.8' -- Install require shim -if fs.exists('sys/modules/opus/injector.lua') then - _G.requireInjector = run('sys/modules/opus/injector.lua') -else - -- not local, run the file system directly from git - if package and package.path then - package.path = package.path .. ';' .. BASE .. '/sys/modules/opus' - else - sandboxEnv.package = { - path = BASE .. '/sys/modules/opus' - } - end - - _G.requireInjector = runUrl('sys/modules/opus/injector.lua') - - runUrl('sys/init/2.vfs.lua') - - -- install file system - fs.mount('', 'gitfs', GIT_REPO) -end +_G.requireInjector = run('sys/modules/opus/injector.lua') local s, m = pcall(run, 'sys/apps/shell.lua', 'sys/kernel.lua', ...) diff --git a/sys/modules/opus/crypto/chacha20.lua b/sys/modules/opus/crypto/chacha20.lua index 8302d1b..b08d09a 100644 --- a/sys/modules/opus/crypto/chacha20.lua +++ b/sys/modules/opus/crypto/chacha20.lua @@ -115,6 +115,7 @@ local function crypt(data, key, nonce, cntr, round) cntr = tonumber(cntr) or 1 round = tonumber(round) or 20 + local throttle = util.throttle() local out = {} local state = initState(key, nonce, cntr) local blockAmt = math.floor(#data/64) @@ -131,8 +132,9 @@ local function crypt(data, key, nonce, cntr, round) end if i % 1000 == 0 then - os.queueEvent("") - os.pullEvent("") + throttle() + --os.queueEvent("") + --os.pullEvent("") end end return setmetatable(out, mt) diff --git a/sys/modules/opus/crypto/sha2.lua b/sys/modules/opus/crypto/sha2.lua index a4f29f3..631488a 100644 --- a/sys/modules/opus/crypto/sha2.lua +++ b/sys/modules/opus/crypto/sha2.lua @@ -162,12 +162,25 @@ local function hmac(data, key) return digest(padded_key) end +local function throttler() + local ts = os.clock() + local timeout = .095 + return function() + local nts = os.clock() + if nts > ts + timeout then + os.sleep(0) + ts = os.clock() + end + end +end + local function pbkdf2(pass, salt, iter, dklen) salt = type(salt) == "table" and salt or {tostring(salt):byte(1,-1)} local hashlen = 32 dklen = dklen or 32 local block = 1 local out = {} + local throttle = throttler() while dklen > 0 do local ikey = {} @@ -182,7 +195,10 @@ local function pbkdf2(pass, salt, iter, dklen) for j = 1, iter do isalt = hmac(isalt, pass) for k = 1, clen do ikey[k] = bxor(isalt[k], ikey[k] or 0) end - if j % 200 == 0 then os.queueEvent("PBKDF2", j) coroutine.yield("PBKDF2") end + if j % 200 == 0 then + throttle() + --os.queueEvent("PBKDF2", j) coroutine.yield("PBKDF2") + end end dklen = dklen - clen block = block+1 diff --git a/sys/modules/opus/injector.lua b/sys/modules/opus/injector.lua index 0835c6b..e93e5d1 100644 --- a/sys/modules/opus/injector.lua +++ b/sys/modules/opus/injector.lua @@ -1,6 +1,3 @@ -local PASTEBIN_URL = 'http://pastebin.com/raw' -local GIT_URL = 'https://raw.githubusercontent.com' - local function split(str, pattern) local t = { } local function helper(line) table.insert(t, line) return "" end @@ -11,7 +8,7 @@ end local hasMain local luaPaths = package and package.path and split(package.path, '(.-);') or { } for i = 1, #luaPaths do - if luaPaths[i] == '?' or luaPaths[i] == '?.lua' then + if luaPaths[i] == '?' or luaPaths[i] == '?.lua' or luaPaths[i] == '?/init.lua' then luaPaths[i] = nil elseif string.find(luaPaths[i], '/rom/modules/main') then hasMain = true @@ -22,70 +19,20 @@ table.insert(luaPaths, 1, '?.lua') table.insert(luaPaths, 2, '?/init.lua') table.insert(luaPaths, 3, '/usr/modules/?.lua') table.insert(luaPaths, 4, '/usr/modules/?/init.lua') -table.insert(luaPaths, 5, '/sys/modules/?.lua') -table.insert(luaPaths, 6, '/sys/modules/?/init.lua') +if not hasMain then + table.insert(luaPaths, 5, '/rom/modules/main/?') + table.insert(luaPaths, 6, '/rom/modules/main/?.lua') + table.insert(luaPaths, 7, '/rom/modules/main/?/init.lua') +end +table.insert(luaPaths, '/sys/modules/?.lua') +table.insert(luaPaths, '/sys/modules/?/init.lua') local DEFAULT_PATH = table.concat(luaPaths, ';') -if not hasMain then - DEFAULT_PATH = DEFAULT_PATH .. ';/rom/modules/main/?;/rom/modules/main/?.lua;/rom/modules/main/?/init.lua' -end local fs = _G.fs -local http = _G.http local os = _G.os local string = _G.string ---[[ -if not http._patched then - -- fix broken http get (http.get is not coroutine safe) - local syncLocks = { } - - local function sync(obj, fn) - local key = tostring(obj) - if syncLocks[key] then - local cos = tostring(coroutine.running()) - table.insert(syncLocks[key], cos) - repeat - local _, co = os.pullEvent('sync_lock') - until co == cos - else - syncLocks[key] = { } - end - fn() - local co = table.remove(syncLocks[key], 1) - if co then - os.queueEvent('sync_lock', co) - else - syncLocks[key] = nil - end - end - - -- todo -- completely replace http.get with function that - -- checks for success on permanent redirects (minecraft 1.75 bug) - - http._patched = http.get - function http.get(url, headers) - local s, m - sync(url, function() - s, m = http._patched(url, headers) - end) - return s, m - end -end ---]] - -local function loadUrl(url) - local c - local h = http.get(url) - if h then - c = h.readAll() - h.close() - end - if c and #c > 0 then - return c - end -end - -- Add require and package to the environment return function(env) local function preloadSearcher(modname) @@ -118,45 +65,14 @@ return function(env) local sPath = string.gsub(pattern, "%?", fname) -- TODO: if there's no shell, we should not be checking relative paths below -- as they will resolve to root directory - if sPath:match("^(https?:)") then - local c = loadUrl(sPath) - if c then - return load(c, modname, nil, env) - end - else - if env.shell and - type(env.shell.getRunningProgram) == 'function' and - sPath:sub(1, 1) ~= "/" then + if env.shell and + type(env.shell.getRunningProgram) == 'function' and + sPath:sub(1, 1) ~= "/" then - sPath = fs.combine(fs.getDir(env.shell.getRunningProgram() or ''), sPath) - end - if fs.exists(sPath) and not fs.isDir(sPath) then - return loadfile(sPath, env) - end + sPath = fs.combine(fs.getDir(env.shell.getRunningProgram() or ''), sPath) end - end - end - - -- require('BniCQPVf') - local function pastebinSearcher(modname) - if #modname == 8 and not modname:match('%W') then - local url = PASTEBIN_URL .. '/' .. modname - local c = loadUrl(url) - if c then - return load(c, modname, nil, env) - end - end - end - - -- require('kepler155c.opus.master.sys.apis.util') - local function gitSearcher(modname) - local fname = modname:gsub('%.', '/') .. '.lua' - local _, count = fname:gsub("/", "") - if count >= 3 then - local url = GIT_URL .. '/' .. fname - local c = loadUrl(url) - if c then - return load(c, modname, nil, env) + if fs.exists(sPath) and not fs.isDir(sPath) then + return loadfile(sPath, env) end end end @@ -178,8 +94,6 @@ return function(env) preloadSearcher, loadedSearcher, pathSearcher, - pastebinSearcher, - gitSearcher, } } diff --git a/sys/modules/opus/security.lua b/sys/modules/opus/security.lua index 725d864..ce194f9 100644 --- a/sys/modules/opus/security.lua +++ b/sys/modules/opus/security.lua @@ -13,13 +13,28 @@ function Security.hasPassword() return not not Security.getPassword() end +local function genKey() + local key = { } + for _ = 1, 32 do + table.insert(key, ("%02x"):format(math.random(0, 0xFF))) + end + return table.concat(key) +end + +function Security.generateKeyPair() + local privateKey = Util.hexToByteArray(genKey()) + return privateKey, ECC.publicKey(privateKey) +end + +function Security.getIdentifier() + return Security.geetPublicKey() +end + +-- deprecate - will use getIdentifier function Security.getSecretKey() local config = Config.load('os') if not config.secretKey then - config.secretKey = "" - for _ = 1, 32 do - config.secretKey = config.secretKey .. ("%02x"):format(math.random(0, 0xFF)) - end + config.secretKey = genKey() Config.update('os', config) end return Util.hexToByteArray(config.secretKey) diff --git a/sys/modules/opus/socket.lua b/sys/modules/opus/socket.lua index 5e83424..da8ceff 100644 --- a/sys/modules/opus/socket.lua +++ b/sys/modules/opus/socket.lua @@ -1,5 +1,7 @@ local Crypto = require('opus.crypto.chacha20') +local ECC = require('opus.crypto.ecc') local Security = require('opus.security') +local SHA = require('opus.crypto.sha2') local Util = require('opus.util') local device = _G.device @@ -60,6 +62,14 @@ function socketClass:ping() end end +function socketClass:setupEncryption() + self.sharedKey = ECC.exchange(self.privKey, self.remotePubKey) + self.enckey = SHA.pbkdf2(self.sharedKey, "1enc", 1) + self.hmackey = SHA.pbkdf2(self.sharedKey, "2hmac", 1) + self.rseed = SHA.pbkdf2(self.sharedKey, "3rseed", 1) + self.wseed = SHA.pbkdf2(self.sharedKey, "4sseed", 1) +end + function socketClass:close() if self.connected then self.transmit(self.dport, self.dhost, { @@ -110,14 +120,20 @@ function Socket.connect(host, port) local socket = newSocket(host == os.getComputerID()) socket.dhost = tonumber(host) + socket.privKey, socket.pubKey = Security.generateKeyPair() socket.transmit(port, socket.sport, { type = 'OPEN', shost = socket.shost, dhost = socket.dhost, - t = Crypto.encrypt({ ts = os.time(), seq = socket.seq, nts = os.epoch('utc') }, Security.getPublicKey()), rseq = socket.wseq, wseq = socket.rseq, + t = Crypto.encrypt({ + ts = os.time(), + seq = socket.seq, + nts = os.epoch('utc'), + pk = Util.byteArrayToHex(socket.pubKey), + }, Security.getPublicKey()), }) local timerId = os.startTimer(3) @@ -133,6 +149,8 @@ function Socket.connect(host, port) if msg.type == 'CONN' then socket.dport = dport socket.connected = true + socket.remotePubKey = Util.hexToByteArray(msg.pk) + socket:setupEncryption() -- Logger.log('socket', 'connection established to %d %d->%d', -- host, socket.sport, socket.dport) _G.transport.open(socket) @@ -153,7 +171,7 @@ function Socket.connect(host, port) return false, 'Connection timed out', 'TIMEOUT' end -local function trusted(msg, port) +local function trusted(socket, msg, port) if port == 19 or msg.shost == os.getComputerID() then -- no auth for trust server or loopback return true @@ -168,11 +186,12 @@ local function trusted(msg, port) local pubKey = trustList[msg.shost] if pubKey and msg.t then - pubKey = Util.hexToByteArray(pubKey) - local data = Crypto.decrypt(msg.t, pubKey) + local data = Crypto.decrypt(msg.t, Util.hexToByteArray(pubKey)) if data and data.nts then -- upgraded security - return data.nts and tonumber(data.nts) and math.abs(os.epoch('utc') - data.nts) < 1024 + if data.nts and tonumber(data.nts) and math.abs(os.epoch('utc') - data.nts) < 1024 then + socket.remotePubKey = Util.hexToByteArray(data.pk) + end end --local sharedKey = modexp(pubKey, exchange.secretKey, public.primeMod) @@ -207,13 +226,17 @@ function Socket.server(port) }) socket:close() - elseif trusted(msg, port) then + elseif trusted(socket, msg, port) then socket.connected = true + socket.privKey, socket.pubKey = Security.generateKeyPair() + socket:setupEncryption() socket.transmit(socket.dport, socket.sport, { type = 'CONN', dhost = socket.dhost, shost = socket.shost, + pk = Util.byteArrayToHex(socket.pubKey), }) + -- Logger.log('socket', 'Connection established %d->%d', socket.sport, socket.dport) _G.transport.open(socket)