diff --git a/.github/workflows/manifest.yml b/.github/workflows/manifest.yml index 73586d1..a631058 100644 --- a/.github/workflows/manifest.yml +++ b/.github/workflows/manifest.yml @@ -1,5 +1,5 @@ -# Simple workflow for deploying static content to GitHub Pages -name: Deploy Installation Manifests and Versions +# Deploy installation manifests and shields versions +name: Deploy Installation Data on: workflow_dispatch: diff --git a/.gitignore b/.gitignore index 17ae161..0688a64 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ _notes/ -program.sh \ No newline at end of file +/*program.sh \ No newline at end of file diff --git a/ccmsi.lua b/ccmsi.lua index a8fff8a..6d5e7e8 100644 --- a/ccmsi.lua +++ b/ccmsi.lua @@ -20,30 +20,96 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. local function println(message) print(tostring(message)) end local function print(message) term.write(tostring(message)) end -local CCMSI_VERSION = "v1.2" +local CCMSI_VERSION = "v1.5a" local install_dir = "/.install-cache" local manifest_path = "https://mikaylafischler.github.io/cc-mek-scada/manifests/" local repo_path = "http://raw.githubusercontent.com/MikaylaFischler/cc-mek-scada/" local opts = { ... } -local mode = nil -local app = nil +local mode, app, target +local install_manifest = manifest_path .. "main/install_manifest.json" + +local function red() term.setTextColor(colors.red) end +local function orange() term.setTextColor(colors.orange) end +local function yellow() term.setTextColor(colors.yellow) end +local function green() term.setTextColor(colors.green) end +local function blue() term.setTextColor(colors.blue) end +local function white() term.setTextColor(colors.white) end +local function lgray() term.setTextColor(colors.lightGray) end + +-- get command line option in list +local function get_opt(opt, options) + for _, v in pairs(options) do if opt == v then return v end end + return nil +end + +-- ask the user yes or no +local function ask_y_n(question, default) + print(question) + if default == true then print(" (Y/n)? ") else print(" (y/N)? ") end + local response = read() + if response == "" then return default + elseif response == "Y" or response == "y" then return true + elseif response == "N" or response == "n" then return false + else return nil end +end + +-- print out a white + blue text message +local function pkg_message(message, package) white(); print(message .. " "); blue(); println(package); white() end + +-- indicate actions to be taken based on package differences for installs/updates +local function show_pkg_change(name, v_local, v_remote) + if v_local ~= nil then + if v_local ~= v_remote then + print("[" .. name .. "] updating "); blue(); print(v_local); white(); print(" \xbb "); blue(); println(v_remote); white() + elseif mode == "install" then + pkg_message("[" .. name .. "] reinstalling", v_local) + end + else + pkg_message("[" .. name .. "] new install of", v_remote) + end +end + +-- read the local manifest file +local function read_local_manifest() + local local_ok = false + local local_manifest = {} + local imfile = fs.open("install_manifest.json", "r") + if imfile ~= nil then + local_ok, local_manifest = pcall(function () return textutils.unserializeJSON(imfile.readAll()) end) + imfile.close() + end + return local_ok, local_manifest +end + +-- get the manifest from GitHub +local function get_remote_manifest() + local response, error = http.get(install_manifest) + if response == nil then + orange(); println("failed to get installation manifest from GitHub, cannot update or install") + red(); println("HTTP error: " .. error); white() + return false, {} + end + + local ok, manifest = pcall(function () return textutils.unserializeJSON(response.readAll()) end) + if not ok then + red(); println("error parsing remote installation manifest"); white() + end + + return ok, manifest +end -- record the local installation manifest ----@param manifest table ----@param dependencies table local function write_install_manifest(manifest, dependencies) local versions = {} for key, value in pairs(manifest.versions) do local is_dependency = false for _, dependency in pairs(dependencies) do - if key == "bootloader" and dependency == "system" then - is_dependency = true - break + if (key == "bootloader" and dependency == "system") or key == dependency then + is_dependency = true; break end end - if key == app or key == "comms" or is_dependency then versions[key] = value end end @@ -54,116 +120,66 @@ local function write_install_manifest(manifest, dependencies) imfile.close() end --- -- get and validate command line options --- println("-- CC Mekanism SCADA Installer " .. CCMSI_VERSION .. " --") if #opts == 0 or opts[1] == "help" then - println("usage: ccmsi ") + println("usage: ccmsi ") println("") - term.setTextColor(colors.lightGray) + lgray() println(" check - check latest versions avilable") - term.setTextColor(colors.yellow) - println(" ccmsi check for target") - term.setTextColor(colors.lightGray) + yellow() + println(" ccmsi check for target") + lgray() println(" install - fresh install, overwrites config") println(" update - update files EXCEPT for config/logs") println(" remove - delete files EXCEPT for config/logs") println(" purge - delete files INCLUDING config/logs") - term.setTextColor(colors.white) - println("") - term.setTextColor(colors.lightGray) + white(); println(""); lgray() println(" reactor-plc - reactor PLC firmware") println(" rtu - RTU firmware") println(" supervisor - supervisor server application") println(" coordinator - coordinator application") println(" pocket - pocket application") - term.setTextColor(colors.white) - println("") - term.setTextColor(colors.yellow) + white(); println(""); yellow() println(" second parameter when used with check") - term.setTextColor(colors.lightGray) - println(" note: defaults to main") - println(" target GitHub tag or branch name") + lgray(); println(" main (default) | latest | devel"); white() return else - for _, v in pairs({ "check", "install", "update", "remove", "purge" }) do - if opts[1] == v then - mode = v - break - end - end - + mode = get_opt(opts[1], { "check", "install", "update", "remove", "purge" }) if mode == nil then - println("unrecognized mode") + red(); println("Unrecognized mode."); white() return end - for _, v in pairs({ "reactor-plc", "rtu", "supervisor", "coordinator", "pocket" }) do - if opts[2] == v then - app = v - break - end - end - + app = get_opt(opts[2], { "reactor-plc", "rtu", "supervisor", "coordinator", "pocket" }) if app == nil and mode ~= "check" then - println("unrecognized application") + red(); println("Unrecognized application."); white() return end + + -- determine target + if mode == "check" then target = opts[2] else target = opts[3] end + if (target ~= "main") and (target ~= "latest") and (target ~= "devel") then + if (target and target ~= "") then yellow(); println("Unknown target, defaulting to 'main'"); white() end + target = "main" + end + + -- set paths + install_manifest = manifest_path .. target .. "/install_manifest.json" + repo_path = repo_path .. target .. "/" end --- -- run selected mode --- if mode == "check" then - ------------------------- - -- GET REMOTE MANIFEST -- - ------------------------- - - if opts[2] then manifest_path = manifest_path .. opts[2] .. "/" else manifest_path = manifest_path .. "main/" end - local install_manifest = manifest_path .. "install_manifest.json" - - local response, error = http.get(install_manifest) - - if response == nil then - term.setTextColor(colors.orange) - println("failed to get installation manifest from GitHub, cannot update or install") - term.setTextColor(colors.red) - println("HTTP error: " .. error) - term.setTextColor(colors.white) - return - end - - local ok, manifest = pcall(function () return textutils.unserializeJSON(response.readAll()) end) - - if not ok then - term.setTextColor(colors.red) - println("error parsing remote installation manifest") - term.setTextColor(colors.white) - return - end - - ------------------------ - -- GET LOCAL MANIFEST -- - ------------------------ - - local imfile = fs.open("install_manifest.json", "r") - local local_ok = false - local local_manifest = {} - - if imfile ~= nil then - local_ok, local_manifest = pcall(function () return textutils.unserializeJSON(imfile.readAll()) end) - imfile.close() - end + local ok, manifest = get_remote_manifest() + if not ok then return end + local local_ok, local_manifest = read_local_manifest() if not local_ok then - term.setTextColor(colors.yellow) - println("failed to load local installation information") - term.setTextColor(colors.white) - + yellow(); println("failed to load local installation information"); white() local_manifest = { versions = { installer = CCMSI_VERSION } } else local_manifest.versions.installer = CCMSI_VERSION @@ -174,191 +190,95 @@ if mode == "check" then term.setTextColor(colors.purple) print(string.format("%-14s", "[" .. key .. "]")) if key == "installer" or (local_ok and (local_manifest.versions[key] ~= nil)) then - term.setTextColor(colors.blue) - print(local_manifest.versions[key]) + blue(); print(local_manifest.versions[key]) if value ~= local_manifest.versions[key] then - term.setTextColor(colors.white) - print(" (") + white(); print(" (") term.setTextColor(colors.cyan) - print(value) - term.setTextColor(colors.white) - println(" available)") - else - term.setTextColor(colors.green) - println(" (up to date)") - end + print(value); white(); println(" available)") + else green(); println(" (up to date)") end else - term.setTextColor(colors.lightGray) - print("not installed") - term.setTextColor(colors.white) - print(" (latest ") + lgray(); print("not installed"); white(); print(" (latest ") term.setTextColor(colors.cyan) - print(value) - term.setTextColor(colors.white) - println(")") + print(value); white(); println(")") end end elseif mode == "install" or mode == "update" then - ------------------------- - -- GET REMOTE MANIFEST -- - ------------------------- + local ok, manifest = get_remote_manifest() + if not ok then return end - if opts[3] then repo_path = repo_path .. opts[3] .. "/" else repo_path = repo_path .. "main/" end - if opts[3] then manifest_path = manifest_path .. opts[3] .. "/" else manifest_path = manifest_path .. "main/" end - local install_manifest = manifest_path .. "install_manifest.json" - - local response, error = http.get(install_manifest) - - if response == nil then - term.setTextColor(colors.orange) - println("failed to get installation manifest from GitHub, cannot update or install") - term.setTextColor(colors.red) - println("HTTP error: " .. error) - term.setTextColor(colors.white) - return - end - - local ok, manifest = pcall(function () return textutils.unserializeJSON(response.readAll()) end) - - if not ok then - term.setTextColor(colors.red) - println("error parsing remote installation manifest") - term.setTextColor(colors.white) - end - - ------------------------ - -- GET LOCAL MANIFEST -- - ------------------------ - - local imfile = fs.open("install_manifest.json", "r") - local local_ok = false - local local_manifest = {} - - if imfile ~= nil then - local_ok, local_manifest = pcall(function () return textutils.unserializeJSON(imfile.readAll()) end) - imfile.close() - end - - local local_app_version = nil - local local_comms_version = nil - local local_boot_version = nil + local ver = { + app = { v_local = nil, v_remote = nil, changed = false }, + boot = { v_local = nil, v_remote = nil, changed = false }, + comms = { v_local = nil, v_remote = nil, changed = false }, + graphics = { v_local = nil, v_remote = nil, changed = false }, + lockbox = { v_local = nil, v_remote = nil, changed = false } + } -- try to find local versions + local local_ok, local_manifest = read_local_manifest() if not local_ok then if mode == "update" then - term.setTextColor(colors.red) - println("failed to load local installation information, cannot update") - term.setTextColor(colors.white) + red(); println("failed to load local installation information, cannot update"); white() return end else - local_app_version = local_manifest.versions[app] - local_comms_version = local_manifest.versions.comms - local_boot_version = local_manifest.versions.bootloader + ver.boot.v_local = local_manifest.versions.bootloader + ver.app.v_local = local_manifest.versions[app] + ver.comms.v_local = local_manifest.versions.comms + ver.graphics.v_local = local_manifest.versions.graphics + ver.lockbox.v_local = local_manifest.versions.lockbox if local_manifest.versions[app] == nil then - term.setTextColor(colors.red) - println("another application is already installed, please purge it before installing a new application") - term.setTextColor(colors.white) + red(); println("another application is already installed, please purge it before installing a new application"); white() return end local_manifest.versions.installer = CCMSI_VERSION if manifest.versions.installer ~= CCMSI_VERSION then - term.setTextColor(colors.yellow) - println("a newer version of the installer is available, consider downloading it") - term.setTextColor(colors.white) + yellow(); println("a newer version of the installer is available, it is recommended to download it"); white() end end - local remote_app_version = manifest.versions[app] - local remote_comms_version = manifest.versions.comms - local remote_boot_version = manifest.versions.bootloader + ver.boot.v_remote = manifest.versions.bootloader + ver.app.v_remote = manifest.versions[app] + ver.comms.v_remote = manifest.versions.comms + ver.graphics.v_remote = manifest.versions.graphics + ver.lockbox.v_remote = manifest.versions.lockbox - term.setTextColor(colors.green) + green() if mode == "install" then - println("installing " .. app .. " files...") + println("Installing " .. app .. " files...") elseif mode == "update" then - println("updating " .. app .. " files... (keeping old config.lua)") + println("Updating " .. app .. " files... (keeping old config.lua)") end - term.setTextColor(colors.white) + white() -- display bootloader version change information - if local_boot_version ~= nil then - if local_boot_version ~= remote_boot_version then - print("[bootldr] updating ") - term.setTextColor(colors.blue) - print(local_boot_version) - term.setTextColor(colors.white) - print(" \xbb ") - term.setTextColor(colors.blue) - println(remote_boot_version) - term.setTextColor(colors.white) - elseif mode == "install" then - print("[bootldr] reinstalling ") - term.setTextColor(colors.blue) - println(local_boot_version) - term.setTextColor(colors.white) - end - else - print("[bootldr] new install of ") - term.setTextColor(colors.blue) - println(remote_boot_version) - term.setTextColor(colors.white) - end + show_pkg_change("bootldr", ver.boot.v_local, ver.boot.v_remote) + ver.boot.changed = ver.boot.v_local ~= ver.boot.v_remote -- display app version change information - if local_app_version ~= nil then - if local_app_version ~= remote_app_version then - print("[" .. app .. "] updating ") - term.setTextColor(colors.blue) - print(local_app_version) - term.setTextColor(colors.white) - print(" \xbb ") - term.setTextColor(colors.blue) - println(remote_app_version) - term.setTextColor(colors.white) - elseif mode == "install" then - print("[" .. app .. "] reinstalling ") - term.setTextColor(colors.blue) - println(local_app_version) - term.setTextColor(colors.white) - end - else - print("[" .. app .. "] new install of ") - term.setTextColor(colors.blue) - println(remote_app_version) - term.setTextColor(colors.white) - end + show_pkg_change(app, ver.app.v_local, ver.app.v_remote) + ver.app.changed = ver.app.v_local ~= ver.app.v_remote -- display comms version change information - if local_comms_version ~= nil then - if local_comms_version ~= remote_comms_version then - print("[comms] updating ") - term.setTextColor(colors.blue) - print(local_comms_version) - term.setTextColor(colors.white) - print(" \xbb ") - term.setTextColor(colors.blue) - println(remote_comms_version) - term.setTextColor(colors.white) - print("[comms] ") - term.setTextColor(colors.yellow) - println("other devices on the network will require an update") - term.setTextColor(colors.white) - elseif mode == "install" then - print("[comms] reinstalling ") - term.setTextColor(colors.blue) - println(local_comms_version) - term.setTextColor(colors.white) - end - else - print("[comms] new install of ") - term.setTextColor(colors.blue) - println(remote_comms_version) - term.setTextColor(colors.white) + show_pkg_change("comms", ver.comms.v_local, ver.comms.v_remote) + ver.comms.changed = ver.comms.v_local ~= ver.comms.v_remote + if ver.comms.changed and ver.comms.v_local ~= nil then + print("[comms] "); yellow(); println("other devices on the network will require an update"); white() end + -- display graphics version change information + show_pkg_change("graphics", ver.graphics.v_local, ver.graphics.v_remote) + ver.graphics.changed = ver.graphics.v_local ~= ver.graphics.v_remote + + -- display lockbox version change information + show_pkg_change("lockbox", ver.lockbox.v_local, ver.lockbox.v_remote) + ver.lockbox.changed = ver.lockbox.v_local ~= ver.lockbox.v_remote + + -- ask for confirmation + if not ask_y_n("Continue?", false) then return end + -------------------------- -- START INSTALL/UPDATE -- -------------------------- @@ -382,24 +302,27 @@ elseif mode == "install" or mode == "update" then -- check space constraints if space_available < space_required then single_file_mode = true - term.setTextColor(colors.yellow) - println("WARNING: Insufficient space available for a full download!") - term.setTextColor(colors.white) + yellow(); println("WARNING: Insufficient space available for a full download!"); white() println("Files can be downloaded one by one, so if you are replacing a current install this will not be a problem unless installation fails.") - println("Do you wish to continue? (y/N)") - - local confirm = read() - if confirm ~= "y" and confirm ~= "Y" then - println("installation cancelled") + if mode == "update" then println("If installation still fails, delete this device's log file or uninstall the app (not purge) and try again.") end + if not ask_y_n("Do you wish to continue?", false) then + println("Operation cancelled.") return end end ----@diagnostic disable-next-line: undefined-field - os.sleep(2) - local success = true + -- helper function to check if a dependency is unchanged + local function unchanged(dependency) + if dependency == "system" then return not ver.boot.changed + elseif dependency == "graphics" then return not ver.graphics.changed + elseif dependency == "lockbox" then return not ver.lockbox.changed + elseif dependency == "common" then return not (ver.app.changed or ver.comms.changed) + elseif dependency == app then return not ver.app.changed + else return true end + end + if not single_file_mode then if fs.exists(install_dir) then fs.delete(install_dir) @@ -408,28 +331,19 @@ elseif mode == "install" or mode == "update" then -- download all dependencies for _, dependency in pairs(dependencies) do - if mode == "update" and ((dependency == "system" and local_boot_version == remote_boot_version) or (local_app_version == remote_app_version)) then - -- skip system package if unchanged, skip app package if not changed - -- skip packages that have no version if app version didn't change - term.setTextColor(colors.white) - print("skipping download of unchanged package ") - term.setTextColor(colors.blue) - println(dependency) + if mode == "update" and unchanged(dependency) then + pkg_message("skipping download of unchanged package", dependency) else - term.setTextColor(colors.white) - print("downloading package ") - term.setTextColor(colors.blue) - println(dependency) + pkg_message("downloading package", dependency) + lgray() - term.setTextColor(colors.lightGray) local files = file_list[dependency] for _, file in pairs(files) do println("GET " .. file) local dl, err = http.get(repo_path .. file) if dl == nil then - term.setTextColor(colors.red) - println("GET HTTP Error " .. err) + red(); println("GET HTTP Error " .. err) success = false break else @@ -444,20 +358,12 @@ elseif mode == "install" or mode == "update" then -- copy in downloaded files (installation) if success then for _, dependency in pairs(dependencies) do - if mode == "update" and ((dependency == "system" and local_boot_version == remote_boot_version) or (local_app_version == remote_app_version)) then - -- skip system package if unchanged, skip app package if not changed - -- skip packages that have no version if app version didn't change - term.setTextColor(colors.white) - print("skipping install of unchanged package ") - term.setTextColor(colors.blue) - println(dependency) + if mode == "update" and unchanged(dependency) then + pkg_message("skipping install of unchanged package", dependency) else - term.setTextColor(colors.white) - print("installing package ") - term.setTextColor(colors.blue) - println(dependency) + pkg_message("installing package", dependency) + lgray() - term.setTextColor(colors.lightGray) local files = file_list[dependency] for _, file in pairs(files) do if mode == "install" or file ~= config_file then @@ -473,41 +379,25 @@ elseif mode == "install" or mode == "update" then fs.delete(install_dir) if success then - -- if we made it here, then none of the file system functions threw exceptions - -- that means everything is OK write_install_manifest(manifest, dependencies) - term.setTextColor(colors.green) + green() if mode == "install" then - println("installation completed successfully") - else - println("update completed successfully") - end + println("Installation completed successfully.") + else println("Update completed successfully.") end else if mode == "install" then - term.setTextColor(colors.red) - println("installation failed") - else - term.setTextColor(colors.orange) - println("update failed, existing files unmodified") - end + red(); println("Installation failed.") + else orange(); println("Update failed, existing files unmodified.") end end else -- go through all files and replace one by one for _, dependency in pairs(dependencies) do - if mode == "update" and ((dependency == "system" and local_boot_version == remote_boot_version) or (local_app_version == remote_app_version)) then - -- skip system package if unchanged, skip app package if not changed - -- skip packages that have no version if app version didn't change - term.setTextColor(colors.white) - print("skipping install of unchanged package ") - term.setTextColor(colors.blue) - println(dependency) + if mode == "update" and unchanged(dependency) then + pkg_message("skipping install of unchanged package", dependency) else - term.setTextColor(colors.white) - print("installing package ") - term.setTextColor(colors.blue) - println(dependency) + pkg_message("installing package", dependency) + lgray() - term.setTextColor(colors.lightGray) local files = file_list[dependency] for _, file in pairs(files) do if mode == "install" or file ~= config_file then @@ -515,7 +405,7 @@ elseif mode == "install" or mode == "update" then local dl, err = http.get(repo_path .. file) if dl == nil then - println("GET HTTP Error " .. err) + red(); println("GET HTTP Error " .. err) success = false break else @@ -529,55 +419,37 @@ elseif mode == "install" or mode == "update" then end if success then - -- if we made it here, then none of the file system functions threw exceptions - -- that means everything is OK write_install_manifest(manifest, dependencies) - term.setTextColor(colors.green) + green() if mode == "install" then - println("installation completed successfully") - else - println("update completed successfully") - end + println("Installation completed successfully.") + else println("Update completed successfully.") end else - term.setTextColor(colors.red) + red() if mode == "install" then - println("installation failed, files may have been skipped") - else - println("update failed, files may have been skipped") - end + println("Installation failed, files may have been skipped.") + else println("Update failed, files may have been skipped.") end end end elseif mode == "remove" or mode == "purge" then - local imfile = fs.open("install_manifest.json", "r") - local ok = false - local manifest = {} - - if imfile ~= nil then - ok, manifest = pcall(function () return textutils.unserializeJSON(imfile.readAll()) end) - imfile.close() - end - + local ok, manifest = read_local_manifest() if not ok then - term.setTextColor(colors.red) - println("error parsing local installation manifest") - term.setTextColor(colors.white) + red(); println("Error parsing local installation manifest."); white() return elseif mode == "remove" and manifest.versions[app] == nil then - term.setTextColor(colors.red) - println(app .. " is not installed") - term.setTextColor(colors.white) + red(); println(app .. " is not installed, cannot remove."); white() return end - term.setTextColor(colors.orange) + orange() if mode == "remove" then - println("removing all " .. app .. " files except for config.lua and log.txt...") + println("Removing all " .. app .. " files except for config and log...") elseif mode == "purge" then - println("purging all " .. app .. " files...") + println("Purging all " .. app .. " files including config and log...") end ----@diagnostic disable-next-line: undefined-field - os.sleep(2) + -- ask for confirmation + if not ask_y_n("Continue?", false) then return end local file_list = manifest.files local dependencies = manifest.depends[app] @@ -585,9 +457,8 @@ elseif mode == "remove" or mode == "purge" then table.insert(dependencies, app) - term.setTextColor(colors.lightGray) - -- delete log file if purging + lgray() if mode == "purge" and fs.exists(config_file) then local log_deleted = pcall(function () local config = require(app .. ".config") @@ -598,11 +469,9 @@ elseif mode == "remove" or mode == "purge" then end) if not log_deleted then - term.setTextColor(colors.red) - println("failed to delete log file") - term.setTextColor(colors.lightGray) ----@diagnostic disable-next-line: undefined-field - os.sleep(1) + red(); println("failed to delete log file") + white(); println("press enter to continue...") + read(); lgray() end end @@ -623,11 +492,7 @@ elseif mode == "remove" or mode == "purge" then local folder = files[1] while true do local dir = fs.getDir(folder) - if dir == "" or dir == ".." then - break - else - folder = dir - end + if dir == "" or dir == ".." then break else folder = dir end end if fs.isDir(folder) then @@ -635,14 +500,11 @@ elseif mode == "remove" or mode == "purge" then println("deleted directory " .. folder) end elseif dependency == app then + -- delete individual subdirectories so we can leave the config for _, folder in pairs(files) do while true do local dir = fs.getDir(folder) - if dir == "" or dir == ".." or dir == app then - break - else - folder = dir - end + if dir == "" or dir == ".." or dir == app then break else folder = dir end end if folder ~= app and fs.isDir(folder) then @@ -660,13 +522,12 @@ elseif mode == "remove" or mode == "purge" then else -- remove all data from versions list to show nothing is installed manifest.versions = {} - imfile = fs.open("install_manifest.json", "w") + local imfile = fs.open("install_manifest.json", "w") imfile.write(textutils.serializeJSON(manifest)) imfile.close() end - term.setTextColor(colors.green) - println("done!") + green(); println("Done!") end -term.setTextColor(colors.white) +white() diff --git a/coordinator/config.lua b/coordinator/config.lua index ecb7599..7ea6ea2 100644 --- a/coordinator/config.lua +++ b/coordinator/config.lua @@ -11,6 +11,10 @@ config.TRUSTED_RANGE = 0 -- time in seconds (>= 2) before assuming a remote device is no longer active config.SV_TIMEOUT = 5 config.API_TIMEOUT = 5 +-- facility authentication key (do NOT use one of your passwords) +-- this enables verifying that messages are authentic +-- all devices on the same network must use the same key +-- config.AUTH_KEY = "SCADAfacility123" -- expected number of reactor units, used only to require that number of unit monitors config.NUM_UNITS = 4 diff --git a/coordinator/coordinator.lua b/coordinator/coordinator.lua index f6e8038..ff81e5d 100644 --- a/coordinator/coordinator.lua +++ b/coordinator/coordinator.lua @@ -183,7 +183,8 @@ local function log_dmesg(message, dmesg_tag, working) GRAPHICS = colors.green, SYSTEM = colors.cyan, BOOT = colors.blue, - COMMS = colors.purple + COMMS = colors.purple, + CRYPTO = colors.yellow } if working then @@ -197,6 +198,7 @@ function coordinator.log_graphics(message) log_dmesg(message, "GRAPHICS") end function coordinator.log_sys(message) log_dmesg(message, "SYSTEM") end function coordinator.log_boot(message) log_dmesg(message, "BOOT") end function coordinator.log_comms(message) log_dmesg(message, "COMMS") end +function coordinator.log_crypto(message) log_dmesg(message, "CRYPTO") end -- log a message for communications connecting, providing access to progress indication control functions ---@nodiscard @@ -212,13 +214,13 @@ end -- coordinator communications ---@nodiscard ---@param version string coordinator version ----@param modem table modem device +---@param nic nic network interface device ---@param crd_channel integer port of configured supervisor ---@param svr_channel integer listening port for supervisor replys ---@param pkt_channel integer listening port for pocket API ---@param range integer trusted device connection range ---@param sv_watchdog watchdog -function coordinator.comms(version, modem, crd_channel, svr_channel, pkt_channel, range, sv_watchdog) +function coordinator.comms(version, nic, crd_channel, svr_channel, pkt_channel, range, sv_watchdog) local self = { sv_linked = false, sv_addr = comms.BROADCAST, @@ -234,16 +236,12 @@ function coordinator.comms(version, modem, crd_channel, svr_channel, pkt_channel -- PRIVATE FUNCTIONS -- - -- configure modem channels - local function _conf_channels() - modem.closeAll() - modem.open(crd_channel) - end + -- configure network channels + nic.closeAll() + nic.open(crd_channel) - _conf_channels() - - -- link modem to apisessions - apisessions.init(modem) + -- link nic to apisessions + apisessions.init(nic) -- send a packet to the supervisor ---@param msg_type SCADA_MGMT_TYPE|SCADA_CRDN_TYPE @@ -263,7 +261,7 @@ function coordinator.comms(version, modem, crd_channel, svr_channel, pkt_channel pkt.make(msg_type, msg) s_pkt.make(self.sv_addr, self.sv_seq_num, protocol, pkt.raw_sendable()) - modem.transmit(svr_channel, crd_channel, s_pkt.raw_sendable()) + nic.transmit(svr_channel, crd_channel, s_pkt) self.sv_seq_num = self.sv_seq_num + 1 end @@ -277,7 +275,7 @@ function coordinator.comms(version, modem, crd_channel, svr_channel, pkt_channel m_pkt.make(SCADA_MGMT_TYPE.ESTABLISH, { ack }) s_pkt.make(packet.src_addr(), packet.seq_num() + 1, PROTOCOL.SCADA_MGMT, m_pkt.raw_sendable()) - modem.transmit(pkt_channel, crd_channel, s_pkt.raw_sendable()) + nic.transmit(pkt_channel, crd_channel, s_pkt) self.last_api_est_acks[packet.src_addr()] = ack end @@ -297,14 +295,6 @@ function coordinator.comms(version, modem, crd_channel, svr_channel, pkt_channel ---@class coord_comms local public = {} - -- reconnect a newly connected modem - ---@param new_modem table - function public.reconnect_modem(new_modem) - modem = new_modem - apisessions.relink_modem(new_modem) - _conf_channels() - end - -- close the connection to the server function public.close() sv_watchdog.cancel() @@ -402,13 +392,10 @@ function coordinator.comms(version, modem, crd_channel, svr_channel, pkt_channel ---@param distance integer ---@return mgmt_frame|crdn_frame|capi_frame|nil packet function public.parse_packet(side, sender, reply_to, message, distance) + local s_pkt = nic.receive(side, sender, reply_to, message, distance) local pkt = nil - local s_pkt = comms.scada_packet() - -- parse packet as generic SCADA packet - s_pkt.receive(side, sender, reply_to, message, distance) - - if s_pkt.is_valid() then + if s_pkt then -- get as SCADA management packet if s_pkt.protocol() == PROTOCOL.SCADA_MGMT then local mgmt_pkt = comms.mgmt_packet() diff --git a/coordinator/session/apisessions.lua b/coordinator/session/apisessions.lua index 17988f5..1ea1beb 100644 --- a/coordinator/session/apisessions.lua +++ b/coordinator/session/apisessions.lua @@ -10,7 +10,7 @@ local pocket = require("coordinator.session.pocket") local apisessions = {} local self = { - modem = nil, + nic = nil, next_id = 0, sessions = {} } @@ -31,7 +31,7 @@ local function _api_handle_outq(session) if msg ~= nil then if msg.qtype == mqueue.TYPE.PACKET then -- handle a packet to be sent - self.modem.transmit(config.PKT_CHANNEL, config.CRD_CHANNEL, msg.message.raw_sendable()) + self.nic.transmit(config.PKT_CHANNEL, config.CRD_CHANNEL, msg.message) elseif msg.qtype == mqueue.TYPE.COMMAND then -- handle instruction/notification elseif msg.qtype == mqueue.TYPE.DATA then @@ -58,7 +58,7 @@ local function _shutdown(session) while session.out_queue.ready() do local msg = session.out_queue.pop() if msg ~= nil and msg.qtype == mqueue.TYPE.PACKET then - self.modem.transmit(config.PKT_CHANNEL, config.CRD_CHANNEL, msg.message.raw_sendable()) + self.nic.transmit(config.PKT_CHANNEL, config.CRD_CHANNEL, msg.message) end end @@ -68,15 +68,9 @@ end -- PUBLIC FUNCTIONS -- -- initialize apisessions ----@param modem table -function apisessions.init(modem) - self.modem = modem -end - --- re-link the modem ----@param modem table -function apisessions.relink_modem(modem) - self.modem = modem +---@param nic nic +function apisessions.init(nic) + self.nic = nic end -- find a session by remote port diff --git a/coordinator/startup.lua b/coordinator/startup.lua index 5a68afc..8bbc550 100644 --- a/coordinator/startup.lua +++ b/coordinator/startup.lua @@ -6,6 +6,7 @@ require("/initenv").init_env() local crash = require("scada-common.crash") local log = require("scada-common.log") +local network = require("scada-common.network") local ppm = require("scada-common.ppm") local tcd = require("scada-common.tcd") local util = require("scada-common.util") @@ -20,7 +21,7 @@ local sounder = require("coordinator.sounder") local apisessions = require("coordinator.session.apisessions") -local COORDINATOR_VERSION = "v0.16.1" +local COORDINATOR_VERSION = "v0.17.1" local println = util.println local println_ts = util.println_ts @@ -30,6 +31,7 @@ local log_sys = coordinator.log_sys local log_boot = coordinator.log_boot local log_comms = coordinator.log_comms local log_comms_connecting = coordinator.log_comms_connecting +local log_crypto = coordinator.log_crypto ---------------------------------------- -- config validation @@ -131,6 +133,12 @@ local function main() -- setup communications ---------------------------------------- + -- message authentication init + if type(config.AUTH_KEY) == "string" then + local init_time = network.init_mac(config.AUTH_KEY) + log_crypto("HMAC init took " .. init_time .. "ms") + end + -- get the communications modem local modem = ppm.get_wireless_modem() if modem == nil then @@ -147,8 +155,9 @@ local function main() conn_watchdog.cancel() log.debug("startup> conn watchdog created") - -- start comms, open all channels - local coord_comms = coordinator.comms(COORDINATOR_VERSION, modem, config.CRD_CHANNEL, config.SVR_CHANNEL, + -- create network interface then setup comms + local nic = network.nic(modem) + local coord_comms = coordinator.comms(COORDINATOR_VERSION, nic, config.CRD_CHANNEL, config.SVR_CHANNEL, config.PKT_CHANNEL, config.TRUSTED_RANGE, conn_watchdog) log.debug("startup> comms init") log_comms("comms initialized") @@ -218,8 +227,6 @@ local function main() local date_format = util.trinary(config.TIME_24_HOUR, "%X \x04 %A, %B %d %Y", "%r \x04 %A, %B %d %Y") - local no_modem = false - if ui_ok then -- start connection watchdog conn_watchdog.feed() @@ -239,8 +246,9 @@ local function main() if type ~= nil and device ~= nil then if type == "modem" then -- we only really care if this is our wireless modem - if device == modem then - no_modem = true + -- if it is another modem, handle other peripheral losses separately + if nic.is_modem(device) then + nic.disconnect() log_sys("comms modem disconnected") println_ts("wireless modem disconnected!") @@ -254,6 +262,7 @@ local function main() end elseif type == "monitor" then if renderer.is_monitor_used(device) then + ---@todo will be handled properly in #249 -- "halt and catch fire" style handling local msg = "lost a configured monitor, system will now exit" println_ts(msg) @@ -275,9 +284,7 @@ local function main() if type == "modem" then if device.isWireless() then -- reconnected modem - no_modem = false - modem = device - coord_comms.reconnect_modem(modem) + nic.connect(device) log_sys("comms modem reconnected") println_ts("wireless modem reconnected.") @@ -289,6 +296,7 @@ local function main() log_sys("wired modem reconnected") end -- elseif type == "monitor" then + ---@todo will be handled properly in #249 -- not supported, system will exit on loss of in-use monitors elseif type == "speaker" then local msg = "alarm sounder speaker reconnected" @@ -322,7 +330,7 @@ local function main() renderer.close_ui() sounder.stop() - if not no_modem then + if nic.connected() then -- try to re-connect to the supervisor if not init_connect_sv() then break end ui_ok = init_start_ui() @@ -350,7 +358,7 @@ local function main() renderer.close_ui() sounder.stop() - if not no_modem then + if nic.connected() then -- try to re-connect to the supervisor if not init_connect_sv() then break end ui_ok = init_start_ui() diff --git a/graphics/core.lua b/graphics/core.lua index 58b6b8c..d99ef3b 100644 --- a/graphics/core.lua +++ b/graphics/core.lua @@ -7,6 +7,8 @@ local flasher = require("graphics.flasher") local core = {} +core.version = "1.0.0" + core.flasher = flasher core.events = events diff --git a/imgen.py b/imgen.py index bd34822..046fc36 100644 --- a/imgen.py +++ b/imgen.py @@ -23,11 +23,11 @@ def dir_size(path): return total # get the version of an application at the provided path -def get_version(path, is_comms = False): +def get_version(path, is_lib = False): ver = "" - string = "comms.version = \"" + string = ".version = \"" - if not is_comms: + if not is_lib: string = "_VERSION = \"" f = open(path, "r") @@ -49,6 +49,8 @@ def make_manifest(size): "installer" : get_version("./ccmsi.lua"), "bootloader" : get_version("./startup.lua"), "comms" : get_version("./scada-common/comms.lua", True), + "graphics" : get_version("./graphics/core.lua", True), + "lockbox" : get_version("./lockbox/init.lua", True), "reactor-plc" : get_version("./reactor-plc/startup.lua"), "rtu" : get_version("./rtu/startup.lua"), "supervisor" : get_version("./supervisor/startup.lua"), @@ -69,11 +71,11 @@ def make_manifest(size): "pocket" : list_files("./pocket"), }, "depends" : { - "reactor-plc" : [ "system", "common", "graphics" ], - "rtu" : [ "system", "common", "graphics" ], - "supervisor" : [ "system", "common", "graphics" ], - "coordinator" : [ "system", "common", "graphics" ], - "pocket" : [ "system", "common", "graphics" ] + "reactor-plc" : [ "system", "common", "graphics", "lockbox" ], + "rtu" : [ "system", "common", "graphics", "lockbox" ], + "supervisor" : [ "system", "common", "graphics", "lockbox" ], + "coordinator" : [ "system", "common", "graphics", "lockbox" ], + "pocket" : [ "system", "common", "graphics", "lockbox" ] }, "sizes" : { # manifest file estimate diff --git a/install_manifest.json b/install_manifest.json index c75c072..b93e2b4 100644 --- a/install_manifest.json +++ b/install_manifest.json @@ -1 +1 @@ -{"versions": {"installer": "v1.2", "bootloader": "0.2", "comms": "2.0.0", "reactor-plc": "v1.4.6", "rtu": "v1.3.6", "supervisor": "v0.17.7", "coordinator": "v0.16.1", "pocket": "alpha-v0.4.5"}, "files": {"system": ["initenv.lua", "startup.lua"], "common": ["scada-common/crypto.lua", "scada-common/ppm.lua", "scada-common/comms.lua", "scada-common/psil.lua", "scada-common/rsio.lua", "scada-common/constants.lua", "scada-common/mqueue.lua", "scada-common/tcd.lua", "scada-common/crash.lua", "scada-common/log.lua", "scada-common/types.lua", "scada-common/util.lua"], "graphics": ["graphics/element.lua", "graphics/events.lua", "graphics/flasher.lua", "graphics/core.lua", "graphics/elements/listbox.lua", "graphics/elements/textbox.lua", "graphics/elements/displaybox.lua", "graphics/elements/pipenet.lua", "graphics/elements/rectangle.lua", "graphics/elements/div.lua", "graphics/elements/multipane.lua", "graphics/elements/tiling.lua", "graphics/elements/colormap.lua", "graphics/elements/indicators/alight.lua", "graphics/elements/indicators/icon.lua", "graphics/elements/indicators/power.lua", "graphics/elements/indicators/rad.lua", "graphics/elements/indicators/state.lua", "graphics/elements/indicators/light.lua", "graphics/elements/indicators/vbar.lua", "graphics/elements/indicators/led.lua", "graphics/elements/indicators/coremap.lua", "graphics/elements/indicators/data.lua", "graphics/elements/indicators/ledpair.lua", "graphics/elements/indicators/hbar.lua", "graphics/elements/indicators/trilight.lua", "graphics/elements/indicators/ledrgb.lua", "graphics/elements/controls/switch_button.lua", "graphics/elements/controls/spinbox_numeric.lua", "graphics/elements/controls/hazard_button.lua", "graphics/elements/controls/push_button.lua", "graphics/elements/controls/radio_button.lua", "graphics/elements/controls/multi_button.lua", "graphics/elements/controls/tabbar.lua", "graphics/elements/controls/sidebar.lua", "graphics/elements/animations/waiting.lua"], "lockbox": ["lockbox/init.lua", "lockbox/LICENSE", "lockbox/kdf/pbkdf2.lua", "lockbox/util/bit.lua", "lockbox/util/array.lua", "lockbox/util/stream.lua", "lockbox/util/queue.lua", "lockbox/digest/sha2_224.lua", "lockbox/digest/sha1.lua", "lockbox/digest/sha2_256.lua", "lockbox/cipher/aes128.lua", "lockbox/cipher/aes256.lua", "lockbox/cipher/aes192.lua", "lockbox/cipher/mode/ofb.lua", "lockbox/cipher/mode/cbc.lua", "lockbox/cipher/mode/ctr.lua", "lockbox/cipher/mode/cfb.lua", "lockbox/mac/hmac.lua", "lockbox/padding/ansix923.lua", "lockbox/padding/pkcs7.lua", "lockbox/padding/zero.lua", "lockbox/padding/isoiec7816.lua"], "reactor-plc": ["reactor-plc/renderer.lua", "reactor-plc/threads.lua", "reactor-plc/databus.lua", "reactor-plc/plc.lua", "reactor-plc/config.lua", "reactor-plc/startup.lua", "reactor-plc/panel/front_panel.lua", "reactor-plc/panel/style.lua"], "rtu": ["rtu/renderer.lua", "rtu/threads.lua", "rtu/rtu.lua", "rtu/databus.lua", "rtu/modbus.lua", "rtu/config.lua", "rtu/startup.lua", "rtu/panel/front_panel.lua", "rtu/panel/style.lua", "rtu/dev/sps_rtu.lua", "rtu/dev/envd_rtu.lua", "rtu/dev/boilerv_rtu.lua", "rtu/dev/redstone_rtu.lua", "rtu/dev/sna_rtu.lua", "rtu/dev/imatrix_rtu.lua", "rtu/dev/turbinev_rtu.lua"], "supervisor": ["supervisor/renderer.lua", "supervisor/databus.lua", "supervisor/supervisor.lua", "supervisor/unit.lua", "supervisor/config.lua", "supervisor/startup.lua", "supervisor/unitlogic.lua", "supervisor/facility.lua", "supervisor/panel/pgi.lua", "supervisor/panel/front_panel.lua", "supervisor/panel/style.lua", "supervisor/panel/components/rtu_entry.lua", "supervisor/panel/components/pdg_entry.lua", "supervisor/session/coordinator.lua", "supervisor/session/svqtypes.lua", "supervisor/session/pocket.lua", "supervisor/session/svsessions.lua", "supervisor/session/rtu.lua", "supervisor/session/plc.lua", "supervisor/session/rsctl.lua", "supervisor/session/rtu/boilerv.lua", "supervisor/session/rtu/txnctrl.lua", "supervisor/session/rtu/unit_session.lua", "supervisor/session/rtu/turbinev.lua", "supervisor/session/rtu/envd.lua", "supervisor/session/rtu/imatrix.lua", "supervisor/session/rtu/sps.lua", "supervisor/session/rtu/qtypes.lua", "supervisor/session/rtu/sna.lua", "supervisor/session/rtu/redstone.lua"], "coordinator": ["coordinator/coordinator.lua", "coordinator/renderer.lua", "coordinator/iocontrol.lua", "coordinator/sounder.lua", "coordinator/config.lua", "coordinator/startup.lua", "coordinator/process.lua", "coordinator/ui/dialog.lua", "coordinator/ui/style.lua", "coordinator/ui/layout/main_view.lua", "coordinator/ui/layout/unit_view.lua", "coordinator/ui/components/reactor.lua", "coordinator/ui/components/processctl.lua", "coordinator/ui/components/unit_overview.lua", "coordinator/ui/components/boiler.lua", "coordinator/ui/components/unit_detail.lua", "coordinator/ui/components/imatrix.lua", "coordinator/ui/components/turbine.lua", "coordinator/session/pocket.lua", "coordinator/session/apisessions.lua"], "pocket": ["pocket/pocket.lua", "pocket/renderer.lua", "pocket/config.lua", "pocket/coreio.lua", "pocket/startup.lua", "pocket/ui/main.lua", "pocket/ui/style.lua", "pocket/ui/components/conn_waiting.lua", "pocket/ui/pages/turbine_page.lua", "pocket/ui/pages/reactor_page.lua", "pocket/ui/pages/home_page.lua", "pocket/ui/pages/unit_page.lua", "pocket/ui/pages/boiler_page.lua"]}, "depends": {"reactor-plc": ["system", "common", "graphics"], "rtu": ["system", "common", "graphics"], "supervisor": ["system", "common", "graphics"], "coordinator": ["system", "common", "graphics"], "pocket": ["system", "common", "graphics"]}, "sizes": {"manifest": 5781, "system": 1991, "common": 92736, "graphics": 144532, "lockbox": 100797, "reactor-plc": 97335, "rtu": 102240, "supervisor": 315402, "coordinator": 197991, "pocket": 37581}} \ No newline at end of file +{"versions": {"installer": "v1.5a", "bootloader": "0.2", "comms": "2.1.0", "graphics": "1.0.0", "lockbox": "1.0", "reactor-plc": "v1.5.0", "rtu": "v1.4.0", "supervisor": "v0.18.0", "coordinator": "v0.17.1", "pocket": "alpha-v0.5.1"}, "files": {"system": ["initenv.lua", "startup.lua"], "common": ["scada-common/ppm.lua", "scada-common/comms.lua", "scada-common/psil.lua", "scada-common/rsio.lua", "scada-common/constants.lua", "scada-common/mqueue.lua", "scada-common/tcd.lua", "scada-common/crash.lua", "scada-common/log.lua", "scada-common/types.lua", "scada-common/util.lua", "scada-common/network.lua"], "graphics": ["graphics/element.lua", "graphics/events.lua", "graphics/flasher.lua", "graphics/core.lua", "graphics/elements/listbox.lua", "graphics/elements/textbox.lua", "graphics/elements/displaybox.lua", "graphics/elements/pipenet.lua", "graphics/elements/rectangle.lua", "graphics/elements/div.lua", "graphics/elements/multipane.lua", "graphics/elements/tiling.lua", "graphics/elements/colormap.lua", "graphics/elements/indicators/alight.lua", "graphics/elements/indicators/icon.lua", "graphics/elements/indicators/power.lua", "graphics/elements/indicators/rad.lua", "graphics/elements/indicators/state.lua", "graphics/elements/indicators/light.lua", "graphics/elements/indicators/vbar.lua", "graphics/elements/indicators/led.lua", "graphics/elements/indicators/coremap.lua", "graphics/elements/indicators/data.lua", "graphics/elements/indicators/ledpair.lua", "graphics/elements/indicators/hbar.lua", "graphics/elements/indicators/trilight.lua", "graphics/elements/indicators/ledrgb.lua", "graphics/elements/controls/switch_button.lua", "graphics/elements/controls/spinbox_numeric.lua", "graphics/elements/controls/hazard_button.lua", "graphics/elements/controls/push_button.lua", "graphics/elements/controls/radio_button.lua", "graphics/elements/controls/multi_button.lua", "graphics/elements/controls/tabbar.lua", "graphics/elements/controls/sidebar.lua", "graphics/elements/animations/waiting.lua"], "lockbox": ["lockbox/init.lua", "lockbox/LICENSE", "lockbox/kdf/pbkdf2.lua", "lockbox/util/bit.lua", "lockbox/util/array.lua", "lockbox/util/stream.lua", "lockbox/util/queue.lua", "lockbox/digest/sha2_224.lua", "lockbox/digest/sha1.lua", "lockbox/digest/sha2_256.lua", "lockbox/digest/md5.lua", "lockbox/mac/hmac.lua"], "reactor-plc": ["reactor-plc/renderer.lua", "reactor-plc/threads.lua", "reactor-plc/databus.lua", "reactor-plc/plc.lua", "reactor-plc/config.lua", "reactor-plc/startup.lua", "reactor-plc/panel/front_panel.lua", "reactor-plc/panel/style.lua"], "rtu": ["rtu/renderer.lua", "rtu/threads.lua", "rtu/rtu.lua", "rtu/databus.lua", "rtu/modbus.lua", "rtu/config.lua", "rtu/startup.lua", "rtu/panel/front_panel.lua", "rtu/panel/style.lua", "rtu/dev/sps_rtu.lua", "rtu/dev/envd_rtu.lua", "rtu/dev/boilerv_rtu.lua", "rtu/dev/redstone_rtu.lua", "rtu/dev/sna_rtu.lua", "rtu/dev/imatrix_rtu.lua", "rtu/dev/turbinev_rtu.lua"], "supervisor": ["supervisor/renderer.lua", "supervisor/databus.lua", "supervisor/supervisor.lua", "supervisor/unit.lua", "supervisor/config.lua", "supervisor/startup.lua", "supervisor/unitlogic.lua", "supervisor/facility.lua", "supervisor/panel/pgi.lua", "supervisor/panel/front_panel.lua", "supervisor/panel/style.lua", "supervisor/panel/components/rtu_entry.lua", "supervisor/panel/components/pdg_entry.lua", "supervisor/session/coordinator.lua", "supervisor/session/svqtypes.lua", "supervisor/session/pocket.lua", "supervisor/session/svsessions.lua", "supervisor/session/rtu.lua", "supervisor/session/plc.lua", "supervisor/session/rsctl.lua", "supervisor/session/rtu/boilerv.lua", "supervisor/session/rtu/txnctrl.lua", "supervisor/session/rtu/unit_session.lua", "supervisor/session/rtu/turbinev.lua", "supervisor/session/rtu/envd.lua", "supervisor/session/rtu/imatrix.lua", "supervisor/session/rtu/sps.lua", "supervisor/session/rtu/qtypes.lua", "supervisor/session/rtu/sna.lua", "supervisor/session/rtu/redstone.lua"], "coordinator": ["coordinator/coordinator.lua", "coordinator/renderer.lua", "coordinator/iocontrol.lua", "coordinator/sounder.lua", "coordinator/config.lua", "coordinator/startup.lua", "coordinator/process.lua", "coordinator/ui/dialog.lua", "coordinator/ui/style.lua", "coordinator/ui/layout/main_view.lua", "coordinator/ui/layout/unit_view.lua", "coordinator/ui/components/reactor.lua", "coordinator/ui/components/processctl.lua", "coordinator/ui/components/unit_overview.lua", "coordinator/ui/components/boiler.lua", "coordinator/ui/components/unit_detail.lua", "coordinator/ui/components/imatrix.lua", "coordinator/ui/components/turbine.lua", "coordinator/session/pocket.lua", "coordinator/session/apisessions.lua"], "pocket": ["pocket/pocket.lua", "pocket/renderer.lua", "pocket/config.lua", "pocket/coreio.lua", "pocket/startup.lua", "pocket/ui/main.lua", "pocket/ui/style.lua", "pocket/ui/components/conn_waiting.lua", "pocket/ui/pages/turbine_page.lua", "pocket/ui/pages/reactor_page.lua", "pocket/ui/pages/home_page.lua", "pocket/ui/pages/unit_page.lua", "pocket/ui/pages/boiler_page.lua"]}, "depends": {"reactor-plc": ["system", "common", "graphics", "lockbox"], "rtu": ["system", "common", "graphics", "lockbox"], "supervisor": ["system", "common", "graphics", "lockbox"], "coordinator": ["system", "common", "graphics", "lockbox"], "pocket": ["system", "common", "graphics", "lockbox"]}, "sizes": {"manifest": 5568, "system": 1991, "common": 97109, "graphics": 144556, "lockbox": 34900, "reactor-plc": 97595, "rtu": 102247, "supervisor": 315425, "coordinator": 198188, "pocket": 37633}} \ No newline at end of file diff --git a/lockbox/cipher/aes128.lua b/lockbox/cipher/aes128.lua deleted file mode 100644 index 0726ac4..0000000 --- a/lockbox/cipher/aes128.lua +++ /dev/null @@ -1,415 +0,0 @@ -local Array = require("lockbox.util.array"); -local Bit = require("lockbox.util.bit"); - -local XOR = Bit.bxor; - -local SBOX = { - [0] = 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, - 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, - 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, - 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, - 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, - 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, - 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, - 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, - 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, - 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, - 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, - 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, - 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, - 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, - 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, - 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16}; - -local ISBOX = { - [0] = 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB, - 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, - 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E, - 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25, - 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, - 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84, - 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06, - 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, - 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73, - 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E, - 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, - 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4, - 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F, - 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, - 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61, - 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D}; - -local ROW_SHIFT = { 1, 6, 11, 16, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, }; -local IROW_SHIFT = { 1, 14, 11, 8, 5, 2, 15, 12, 9, 6, 3, 16, 13, 10, 7, 4, }; - -local ETABLE = { - [0] = 0x01, 0x03, 0x05, 0x0F, 0x11, 0x33, 0x55, 0xFF, 0x1A, 0x2E, 0x72, 0x96, 0xA1, 0xF8, 0x13, 0x35, - 0x5F, 0xE1, 0x38, 0x48, 0xD8, 0x73, 0x95, 0xA4, 0xF7, 0x02, 0x06, 0x0A, 0x1E, 0x22, 0x66, 0xAA, - 0xE5, 0x34, 0x5C, 0xE4, 0x37, 0x59, 0xEB, 0x26, 0x6A, 0xBE, 0xD9, 0x70, 0x90, 0xAB, 0xE6, 0x31, - 0x53, 0xF5, 0x04, 0x0C, 0x14, 0x3C, 0x44, 0xCC, 0x4F, 0xD1, 0x68, 0xB8, 0xD3, 0x6E, 0xB2, 0xCD, - 0x4C, 0xD4, 0x67, 0xA9, 0xE0, 0x3B, 0x4D, 0xD7, 0x62, 0xA6, 0xF1, 0x08, 0x18, 0x28, 0x78, 0x88, - 0x83, 0x9E, 0xB9, 0xD0, 0x6B, 0xBD, 0xDC, 0x7F, 0x81, 0x98, 0xB3, 0xCE, 0x49, 0xDB, 0x76, 0x9A, - 0xB5, 0xC4, 0x57, 0xF9, 0x10, 0x30, 0x50, 0xF0, 0x0B, 0x1D, 0x27, 0x69, 0xBB, 0xD6, 0x61, 0xA3, - 0xFE, 0x19, 0x2B, 0x7D, 0x87, 0x92, 0xAD, 0xEC, 0x2F, 0x71, 0x93, 0xAE, 0xE9, 0x20, 0x60, 0xA0, - 0xFB, 0x16, 0x3A, 0x4E, 0xD2, 0x6D, 0xB7, 0xC2, 0x5D, 0xE7, 0x32, 0x56, 0xFA, 0x15, 0x3F, 0x41, - 0xC3, 0x5E, 0xE2, 0x3D, 0x47, 0xC9, 0x40, 0xC0, 0x5B, 0xED, 0x2C, 0x74, 0x9C, 0xBF, 0xDA, 0x75, - 0x9F, 0xBA, 0xD5, 0x64, 0xAC, 0xEF, 0x2A, 0x7E, 0x82, 0x9D, 0xBC, 0xDF, 0x7A, 0x8E, 0x89, 0x80, - 0x9B, 0xB6, 0xC1, 0x58, 0xE8, 0x23, 0x65, 0xAF, 0xEA, 0x25, 0x6F, 0xB1, 0xC8, 0x43, 0xC5, 0x54, - 0xFC, 0x1F, 0x21, 0x63, 0xA5, 0xF4, 0x07, 0x09, 0x1B, 0x2D, 0x77, 0x99, 0xB0, 0xCB, 0x46, 0xCA, - 0x45, 0xCF, 0x4A, 0xDE, 0x79, 0x8B, 0x86, 0x91, 0xA8, 0xE3, 0x3E, 0x42, 0xC6, 0x51, 0xF3, 0x0E, - 0x12, 0x36, 0x5A, 0xEE, 0x29, 0x7B, 0x8D, 0x8C, 0x8F, 0x8A, 0x85, 0x94, 0xA7, 0xF2, 0x0D, 0x17, - 0x39, 0x4B, 0xDD, 0x7C, 0x84, 0x97, 0xA2, 0xFD, 0x1C, 0x24, 0x6C, 0xB4, 0xC7, 0x52, 0xF6, 0x01}; - -local LTABLE = { - [0] = 0x00, 0x00, 0x19, 0x01, 0x32, 0x02, 0x1A, 0xC6, 0x4B, 0xC7, 0x1B, 0x68, 0x33, 0xEE, 0xDF, 0x03, - 0x64, 0x04, 0xE0, 0x0E, 0x34, 0x8D, 0x81, 0xEF, 0x4C, 0x71, 0x08, 0xC8, 0xF8, 0x69, 0x1C, 0xC1, - 0x7D, 0xC2, 0x1D, 0xB5, 0xF9, 0xB9, 0x27, 0x6A, 0x4D, 0xE4, 0xA6, 0x72, 0x9A, 0xC9, 0x09, 0x78, - 0x65, 0x2F, 0x8A, 0x05, 0x21, 0x0F, 0xE1, 0x24, 0x12, 0xF0, 0x82, 0x45, 0x35, 0x93, 0xDA, 0x8E, - 0x96, 0x8F, 0xDB, 0xBD, 0x36, 0xD0, 0xCE, 0x94, 0x13, 0x5C, 0xD2, 0xF1, 0x40, 0x46, 0x83, 0x38, - 0x66, 0xDD, 0xFD, 0x30, 0xBF, 0x06, 0x8B, 0x62, 0xB3, 0x25, 0xE2, 0x98, 0x22, 0x88, 0x91, 0x10, - 0x7E, 0x6E, 0x48, 0xC3, 0xA3, 0xB6, 0x1E, 0x42, 0x3A, 0x6B, 0x28, 0x54, 0xFA, 0x85, 0x3D, 0xBA, - 0x2B, 0x79, 0x0A, 0x15, 0x9B, 0x9F, 0x5E, 0xCA, 0x4E, 0xD4, 0xAC, 0xE5, 0xF3, 0x73, 0xA7, 0x57, - 0xAF, 0x58, 0xA8, 0x50, 0xF4, 0xEA, 0xD6, 0x74, 0x4F, 0xAE, 0xE9, 0xD5, 0xE7, 0xE6, 0xAD, 0xE8, - 0x2C, 0xD7, 0x75, 0x7A, 0xEB, 0x16, 0x0B, 0xF5, 0x59, 0xCB, 0x5F, 0xB0, 0x9C, 0xA9, 0x51, 0xA0, - 0x7F, 0x0C, 0xF6, 0x6F, 0x17, 0xC4, 0x49, 0xEC, 0xD8, 0x43, 0x1F, 0x2D, 0xA4, 0x76, 0x7B, 0xB7, - 0xCC, 0xBB, 0x3E, 0x5A, 0xFB, 0x60, 0xB1, 0x86, 0x3B, 0x52, 0xA1, 0x6C, 0xAA, 0x55, 0x29, 0x9D, - 0x97, 0xB2, 0x87, 0x90, 0x61, 0xBE, 0xDC, 0xFC, 0xBC, 0x95, 0xCF, 0xCD, 0x37, 0x3F, 0x5B, 0xD1, - 0x53, 0x39, 0x84, 0x3C, 0x41, 0xA2, 0x6D, 0x47, 0x14, 0x2A, 0x9E, 0x5D, 0x56, 0xF2, 0xD3, 0xAB, - 0x44, 0x11, 0x92, 0xD9, 0x23, 0x20, 0x2E, 0x89, 0xB4, 0x7C, 0xB8, 0x26, 0x77, 0x99, 0xE3, 0xA5, - 0x67, 0x4A, 0xED, 0xDE, 0xC5, 0x31, 0xFE, 0x18, 0x0D, 0x63, 0x8C, 0x80, 0xC0, 0xF7, 0x70, 0x07}; - -local MIXTABLE = { - 0x02, 0x03, 0x01, 0x01, - 0x01, 0x02, 0x03, 0x01, - 0x01, 0x01, 0x02, 0x03, - 0x03, 0x01, 0x01, 0x02}; - -local IMIXTABLE = { - 0x0E, 0x0B, 0x0D, 0x09, - 0x09, 0x0E, 0x0B, 0x0D, - 0x0D, 0x09, 0x0E, 0x0B, - 0x0B, 0x0D, 0x09, 0x0E}; - -local RCON = { -[0] = 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, -0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, -0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, -0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, -0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, -0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, -0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, -0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, -0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, -0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, -0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, -0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, -0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, -0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, -0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, -0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d}; - - -local GMUL = function(A, B) - if(A == 0x01) then return B; end - if(B == 0x01) then return A; end - if(A == 0x00) then return 0; end - if(B == 0x00) then return 0; end - - local LA = LTABLE[A]; - local LB = LTABLE[B]; - - local sum = LA + LB; - if (sum > 0xFF) then sum = sum - 0xFF; end - - return ETABLE[sum]; -end - -local byteSub = Array.substitute; - -local shiftRow = Array.permute; - -local mixCol = function(i, mix) - local out = {}; - - local a, b, c, d; - - a = GMUL(i[ 1], mix[ 1]); - b = GMUL(i[ 2], mix[ 2]); - c = GMUL(i[ 3], mix[ 3]); - d = GMUL(i[ 4], mix[ 4]); - out[ 1] = XOR(XOR(a, b), XOR(c, d)); - a = GMUL(i[ 1], mix[ 5]); - b = GMUL(i[ 2], mix[ 6]); - c = GMUL(i[ 3], mix[ 7]); - d = GMUL(i[ 4], mix[ 8]); - out[ 2] = XOR(XOR(a, b), XOR(c, d)); - a = GMUL(i[ 1], mix[ 9]); - b = GMUL(i[ 2], mix[10]); - c = GMUL(i[ 3], mix[11]); - d = GMUL(i[ 4], mix[12]); - out[ 3] = XOR(XOR(a, b), XOR(c, d)); - a = GMUL(i[ 1], mix[13]); - b = GMUL(i[ 2], mix[14]); - c = GMUL(i[ 3], mix[15]); - d = GMUL(i[ 4], mix[16]); - out[ 4] = XOR(XOR(a, b), XOR(c, d)); - - - a = GMUL(i[ 5], mix[ 1]); - b = GMUL(i[ 6], mix[ 2]); - c = GMUL(i[ 7], mix[ 3]); - d = GMUL(i[ 8], mix[ 4]); - out[ 5] = XOR(XOR(a, b), XOR(c, d)); - a = GMUL(i[ 5], mix[ 5]); - b = GMUL(i[ 6], mix[ 6]); - c = GMUL(i[ 7], mix[ 7]); - d = GMUL(i[ 8], mix[ 8]); - out[ 6] = XOR(XOR(a, b), XOR(c, d)); - a = GMUL(i[ 5], mix[ 9]); - b = GMUL(i[ 6], mix[10]); - c = GMUL(i[ 7], mix[11]); - d = GMUL(i[ 8], mix[12]); - out[ 7] = XOR(XOR(a, b), XOR(c, d)); - a = GMUL(i[ 5], mix[13]); - b = GMUL(i[ 6], mix[14]); - c = GMUL(i[ 7], mix[15]); - d = GMUL(i[ 8], mix[16]); - out[ 8] = XOR(XOR(a, b), XOR(c, d)); - - - a = GMUL(i[ 9], mix[ 1]); - b = GMUL(i[10], mix[ 2]); - c = GMUL(i[11], mix[ 3]); - d = GMUL(i[12], mix[ 4]); - out[ 9] = XOR(XOR(a, b), XOR(c, d)); - a = GMUL(i[ 9], mix[ 5]); - b = GMUL(i[10], mix[ 6]); - c = GMUL(i[11], mix[ 7]); - d = GMUL(i[12], mix[ 8]); - out[10] = XOR(XOR(a, b), XOR(c, d)); - a = GMUL(i[ 9], mix[ 9]); - b = GMUL(i[10], mix[10]); - c = GMUL(i[11], mix[11]); - d = GMUL(i[12], mix[12]); - out[11] = XOR(XOR(a, b), XOR(c, d)); - a = GMUL(i[ 9], mix[13]); - b = GMUL(i[10], mix[14]); - c = GMUL(i[11], mix[15]); - d = GMUL(i[12], mix[16]); - out[12] = XOR(XOR(a, b), XOR(c, d)); - - - a = GMUL(i[13], mix[ 1]); - b = GMUL(i[14], mix[ 2]); - c = GMUL(i[15], mix[ 3]); - d = GMUL(i[16], mix[ 4]); - out[13] = XOR(XOR(a, b), XOR(c, d)); - a = GMUL(i[13], mix[ 5]); - b = GMUL(i[14], mix[ 6]); - c = GMUL(i[15], mix[ 7]); - d = GMUL(i[16], mix[ 8]); - out[14] = XOR(XOR(a, b), XOR(c, d)); - a = GMUL(i[13], mix[ 9]); - b = GMUL(i[14], mix[10]); - c = GMUL(i[15], mix[11]); - d = GMUL(i[16], mix[12]); - out[15] = XOR(XOR(a, b), XOR(c, d)); - a = GMUL(i[13], mix[13]); - b = GMUL(i[14], mix[14]); - c = GMUL(i[15], mix[15]); - d = GMUL(i[16], mix[16]); - out[16] = XOR(XOR(a, b), XOR(c, d)); - - return out; -end - -local keyRound = function(key, round) - local out = {}; - - out[ 1] = XOR(key[ 1], XOR(SBOX[key[14]], RCON[round])); - out[ 2] = XOR(key[ 2], SBOX[key[15]]); - out[ 3] = XOR(key[ 3], SBOX[key[16]]); - out[ 4] = XOR(key[ 4], SBOX[key[13]]); - - out[ 5] = XOR(out[ 1], key[ 5]); - out[ 6] = XOR(out[ 2], key[ 6]); - out[ 7] = XOR(out[ 3], key[ 7]); - out[ 8] = XOR(out[ 4], key[ 8]); - - out[ 9] = XOR(out[ 5], key[ 9]); - out[10] = XOR(out[ 6], key[10]); - out[11] = XOR(out[ 7], key[11]); - out[12] = XOR(out[ 8], key[12]); - - out[13] = XOR(out[ 9], key[13]); - out[14] = XOR(out[10], key[14]); - out[15] = XOR(out[11], key[15]); - out[16] = XOR(out[12], key[16]); - - return out; -end - -local keyExpand = function(key) - local keys = {}; - - local temp = key; - - keys[1] = temp; - - for i = 1, 10 do - temp = keyRound(temp, i); - keys[i + 1] = temp; - end - - return keys; - -end - -local addKey = Array.XOR; - - - -local AES = {}; - -AES.blockSize = 16; - -AES.encrypt = function(_key, block) - - local key = keyExpand(_key); - - --round 0 - block = addKey(block, key[1]); - - --round 1 - block = byteSub(block, SBOX); - block = shiftRow(block, ROW_SHIFT); - block = mixCol(block, MIXTABLE); - block = addKey(block, key[2]); - - --round 2 - block = byteSub(block, SBOX); - block = shiftRow(block, ROW_SHIFT); - block = mixCol(block, MIXTABLE); - block = addKey(block, key[3]); - - --round 3 - block = byteSub(block, SBOX); - block = shiftRow(block, ROW_SHIFT); - block = mixCol(block, MIXTABLE); - block = addKey(block, key[4]); - - --round 4 - block = byteSub(block, SBOX); - block = shiftRow(block, ROW_SHIFT); - block = mixCol(block, MIXTABLE); - block = addKey(block, key[5]); - - --round 5 - block = byteSub(block, SBOX); - block = shiftRow(block, ROW_SHIFT); - block = mixCol(block, MIXTABLE); - block = addKey(block, key[6]); - - --round 6 - block = byteSub(block, SBOX); - block = shiftRow(block, ROW_SHIFT); - block = mixCol(block, MIXTABLE); - block = addKey(block, key[7]); - - --round 7 - block = byteSub(block, SBOX); - block = shiftRow(block, ROW_SHIFT); - block = mixCol(block, MIXTABLE); - block = addKey(block, key[8]); - - --round 8 - block = byteSub(block, SBOX); - block = shiftRow(block, ROW_SHIFT); - block = mixCol(block, MIXTABLE); - block = addKey(block, key[9]); - - --round 9 - block = byteSub(block, SBOX); - block = shiftRow(block, ROW_SHIFT); - block = mixCol(block, MIXTABLE); - block = addKey(block, key[10]); - - --round 10 - block = byteSub(block, SBOX); - block = shiftRow(block, ROW_SHIFT); - block = addKey(block, key[11]); - - return block; - -end - -AES.decrypt = function(_key, block) - - local key = keyExpand(_key); - - --round 0 - block = addKey(block, key[11]); - - --round 1 - block = shiftRow(block, IROW_SHIFT); - block = byteSub(block, ISBOX); - block = addKey(block, key[10]); - block = mixCol(block, IMIXTABLE); - - --round 2 - block = shiftRow(block, IROW_SHIFT); - block = byteSub(block, ISBOX); - block = addKey(block, key[9]); - block = mixCol(block, IMIXTABLE); - - --round 3 - block = shiftRow(block, IROW_SHIFT); - block = byteSub(block, ISBOX); - block = addKey(block, key[8]); - block = mixCol(block, IMIXTABLE); - - --round 4 - block = shiftRow(block, IROW_SHIFT); - block = byteSub(block, ISBOX); - block = addKey(block, key[7]); - block = mixCol(block, IMIXTABLE); - - --round 5 - block = shiftRow(block, IROW_SHIFT); - block = byteSub(block, ISBOX); - block = addKey(block, key[6]); - block = mixCol(block, IMIXTABLE); - - --round 6 - block = shiftRow(block, IROW_SHIFT); - block = byteSub(block, ISBOX); - block = addKey(block, key[5]); - block = mixCol(block, IMIXTABLE); - - --round 7 - block = shiftRow(block, IROW_SHIFT); - block = byteSub(block, ISBOX); - block = addKey(block, key[4]); - block = mixCol(block, IMIXTABLE); - - --round 8 - block = shiftRow(block, IROW_SHIFT); - block = byteSub(block, ISBOX); - block = addKey(block, key[3]); - block = mixCol(block, IMIXTABLE); - - --round 9 - block = shiftRow(block, IROW_SHIFT); - block = byteSub(block, ISBOX); - block = addKey(block, key[2]); - block = mixCol(block, IMIXTABLE); - - --round 10 - block = shiftRow(block, IROW_SHIFT); - block = byteSub(block, ISBOX); - block = addKey(block, key[1]); - - return block; -end - -return AES; diff --git a/lockbox/cipher/aes192.lua b/lockbox/cipher/aes192.lua deleted file mode 100644 index 5f55b0e..0000000 --- a/lockbox/cipher/aes192.lua +++ /dev/null @@ -1,462 +0,0 @@ - -local Array = require("lockbox.util.array"); -local Bit = require("lockbox.util.bit"); - -local XOR = Bit.bxor; - -local SBOX = { - [0] = 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, - 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, - 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, - 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, - 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, - 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, - 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, - 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, - 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, - 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, - 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, - 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, - 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, - 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, - 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, - 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16}; - -local ISBOX = { - [0] = 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB, - 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, - 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E, - 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25, - 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, - 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84, - 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06, - 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, - 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73, - 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E, - 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, - 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4, - 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F, - 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, - 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61, - 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D}; - -local ROW_SHIFT = { 1, 6, 11, 16, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, }; -local IROW_SHIFT = { 1, 14, 11, 8, 5, 2, 15, 12, 9, 6, 3, 16, 13, 10, 7, 4, }; - -local ETABLE = { - [0] = 0x01, 0x03, 0x05, 0x0F, 0x11, 0x33, 0x55, 0xFF, 0x1A, 0x2E, 0x72, 0x96, 0xA1, 0xF8, 0x13, 0x35, - 0x5F, 0xE1, 0x38, 0x48, 0xD8, 0x73, 0x95, 0xA4, 0xF7, 0x02, 0x06, 0x0A, 0x1E, 0x22, 0x66, 0xAA, - 0xE5, 0x34, 0x5C, 0xE4, 0x37, 0x59, 0xEB, 0x26, 0x6A, 0xBE, 0xD9, 0x70, 0x90, 0xAB, 0xE6, 0x31, - 0x53, 0xF5, 0x04, 0x0C, 0x14, 0x3C, 0x44, 0xCC, 0x4F, 0xD1, 0x68, 0xB8, 0xD3, 0x6E, 0xB2, 0xCD, - 0x4C, 0xD4, 0x67, 0xA9, 0xE0, 0x3B, 0x4D, 0xD7, 0x62, 0xA6, 0xF1, 0x08, 0x18, 0x28, 0x78, 0x88, - 0x83, 0x9E, 0xB9, 0xD0, 0x6B, 0xBD, 0xDC, 0x7F, 0x81, 0x98, 0xB3, 0xCE, 0x49, 0xDB, 0x76, 0x9A, - 0xB5, 0xC4, 0x57, 0xF9, 0x10, 0x30, 0x50, 0xF0, 0x0B, 0x1D, 0x27, 0x69, 0xBB, 0xD6, 0x61, 0xA3, - 0xFE, 0x19, 0x2B, 0x7D, 0x87, 0x92, 0xAD, 0xEC, 0x2F, 0x71, 0x93, 0xAE, 0xE9, 0x20, 0x60, 0xA0, - 0xFB, 0x16, 0x3A, 0x4E, 0xD2, 0x6D, 0xB7, 0xC2, 0x5D, 0xE7, 0x32, 0x56, 0xFA, 0x15, 0x3F, 0x41, - 0xC3, 0x5E, 0xE2, 0x3D, 0x47, 0xC9, 0x40, 0xC0, 0x5B, 0xED, 0x2C, 0x74, 0x9C, 0xBF, 0xDA, 0x75, - 0x9F, 0xBA, 0xD5, 0x64, 0xAC, 0xEF, 0x2A, 0x7E, 0x82, 0x9D, 0xBC, 0xDF, 0x7A, 0x8E, 0x89, 0x80, - 0x9B, 0xB6, 0xC1, 0x58, 0xE8, 0x23, 0x65, 0xAF, 0xEA, 0x25, 0x6F, 0xB1, 0xC8, 0x43, 0xC5, 0x54, - 0xFC, 0x1F, 0x21, 0x63, 0xA5, 0xF4, 0x07, 0x09, 0x1B, 0x2D, 0x77, 0x99, 0xB0, 0xCB, 0x46, 0xCA, - 0x45, 0xCF, 0x4A, 0xDE, 0x79, 0x8B, 0x86, 0x91, 0xA8, 0xE3, 0x3E, 0x42, 0xC6, 0x51, 0xF3, 0x0E, - 0x12, 0x36, 0x5A, 0xEE, 0x29, 0x7B, 0x8D, 0x8C, 0x8F, 0x8A, 0x85, 0x94, 0xA7, 0xF2, 0x0D, 0x17, - 0x39, 0x4B, 0xDD, 0x7C, 0x84, 0x97, 0xA2, 0xFD, 0x1C, 0x24, 0x6C, 0xB4, 0xC7, 0x52, 0xF6, 0x01}; - -local LTABLE = { - [0] = 0x00, 0x00, 0x19, 0x01, 0x32, 0x02, 0x1A, 0xC6, 0x4B, 0xC7, 0x1B, 0x68, 0x33, 0xEE, 0xDF, 0x03, - 0x64, 0x04, 0xE0, 0x0E, 0x34, 0x8D, 0x81, 0xEF, 0x4C, 0x71, 0x08, 0xC8, 0xF8, 0x69, 0x1C, 0xC1, - 0x7D, 0xC2, 0x1D, 0xB5, 0xF9, 0xB9, 0x27, 0x6A, 0x4D, 0xE4, 0xA6, 0x72, 0x9A, 0xC9, 0x09, 0x78, - 0x65, 0x2F, 0x8A, 0x05, 0x21, 0x0F, 0xE1, 0x24, 0x12, 0xF0, 0x82, 0x45, 0x35, 0x93, 0xDA, 0x8E, - 0x96, 0x8F, 0xDB, 0xBD, 0x36, 0xD0, 0xCE, 0x94, 0x13, 0x5C, 0xD2, 0xF1, 0x40, 0x46, 0x83, 0x38, - 0x66, 0xDD, 0xFD, 0x30, 0xBF, 0x06, 0x8B, 0x62, 0xB3, 0x25, 0xE2, 0x98, 0x22, 0x88, 0x91, 0x10, - 0x7E, 0x6E, 0x48, 0xC3, 0xA3, 0xB6, 0x1E, 0x42, 0x3A, 0x6B, 0x28, 0x54, 0xFA, 0x85, 0x3D, 0xBA, - 0x2B, 0x79, 0x0A, 0x15, 0x9B, 0x9F, 0x5E, 0xCA, 0x4E, 0xD4, 0xAC, 0xE5, 0xF3, 0x73, 0xA7, 0x57, - 0xAF, 0x58, 0xA8, 0x50, 0xF4, 0xEA, 0xD6, 0x74, 0x4F, 0xAE, 0xE9, 0xD5, 0xE7, 0xE6, 0xAD, 0xE8, - 0x2C, 0xD7, 0x75, 0x7A, 0xEB, 0x16, 0x0B, 0xF5, 0x59, 0xCB, 0x5F, 0xB0, 0x9C, 0xA9, 0x51, 0xA0, - 0x7F, 0x0C, 0xF6, 0x6F, 0x17, 0xC4, 0x49, 0xEC, 0xD8, 0x43, 0x1F, 0x2D, 0xA4, 0x76, 0x7B, 0xB7, - 0xCC, 0xBB, 0x3E, 0x5A, 0xFB, 0x60, 0xB1, 0x86, 0x3B, 0x52, 0xA1, 0x6C, 0xAA, 0x55, 0x29, 0x9D, - 0x97, 0xB2, 0x87, 0x90, 0x61, 0xBE, 0xDC, 0xFC, 0xBC, 0x95, 0xCF, 0xCD, 0x37, 0x3F, 0x5B, 0xD1, - 0x53, 0x39, 0x84, 0x3C, 0x41, 0xA2, 0x6D, 0x47, 0x14, 0x2A, 0x9E, 0x5D, 0x56, 0xF2, 0xD3, 0xAB, - 0x44, 0x11, 0x92, 0xD9, 0x23, 0x20, 0x2E, 0x89, 0xB4, 0x7C, 0xB8, 0x26, 0x77, 0x99, 0xE3, 0xA5, - 0x67, 0x4A, 0xED, 0xDE, 0xC5, 0x31, 0xFE, 0x18, 0x0D, 0x63, 0x8C, 0x80, 0xC0, 0xF7, 0x70, 0x07}; - -local MIXTABLE = { - 0x02, 0x03, 0x01, 0x01, - 0x01, 0x02, 0x03, 0x01, - 0x01, 0x01, 0x02, 0x03, - 0x03, 0x01, 0x01, 0x02}; - -local IMIXTABLE = { - 0x0E, 0x0B, 0x0D, 0x09, - 0x09, 0x0E, 0x0B, 0x0D, - 0x0D, 0x09, 0x0E, 0x0B, - 0x0B, 0x0D, 0x09, 0x0E}; - -local RCON = { -[0] = 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, -0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, -0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, -0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, -0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, -0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, -0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, -0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, -0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, -0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, -0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, -0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, -0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, -0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, -0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, -0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d}; - - -local GMUL = function(A, B) - if(A == 0x01) then return B; end - if(B == 0x01) then return A; end - if(A == 0x00) then return 0; end - if(B == 0x00) then return 0; end - - local LA = LTABLE[A]; - local LB = LTABLE[B]; - - local sum = LA + LB; - if (sum > 0xFF) then sum = sum - 0xFF; end - - return ETABLE[sum]; -end - -local byteSub = Array.substitute; - -local shiftRow = Array.permute; - -local mixCol = function(i, mix) - local out = {}; - - local a, b, c, d; - - a = GMUL(i[ 1], mix[ 1]); - b = GMUL(i[ 2], mix[ 2]); - c = GMUL(i[ 3], mix[ 3]); - d = GMUL(i[ 4], mix[ 4]); - out[ 1] = XOR(XOR(a, b), XOR(c, d)); - a = GMUL(i[ 1], mix[ 5]); - b = GMUL(i[ 2], mix[ 6]); - c = GMUL(i[ 3], mix[ 7]); - d = GMUL(i[ 4], mix[ 8]); - out[ 2] = XOR(XOR(a, b), XOR(c, d)); - a = GMUL(i[ 1], mix[ 9]); - b = GMUL(i[ 2], mix[10]); - c = GMUL(i[ 3], mix[11]); - d = GMUL(i[ 4], mix[12]); - out[ 3] = XOR(XOR(a, b), XOR(c, d)); - a = GMUL(i[ 1], mix[13]); - b = GMUL(i[ 2], mix[14]); - c = GMUL(i[ 3], mix[15]); - d = GMUL(i[ 4], mix[16]); - out[ 4] = XOR(XOR(a, b), XOR(c, d)); - - - a = GMUL(i[ 5], mix[ 1]); - b = GMUL(i[ 6], mix[ 2]); - c = GMUL(i[ 7], mix[ 3]); - d = GMUL(i[ 8], mix[ 4]); - out[ 5] = XOR(XOR(a, b), XOR(c, d)); - a = GMUL(i[ 5], mix[ 5]); - b = GMUL(i[ 6], mix[ 6]); - c = GMUL(i[ 7], mix[ 7]); - d = GMUL(i[ 8], mix[ 8]); - out[ 6] = XOR(XOR(a, b), XOR(c, d)); - a = GMUL(i[ 5], mix[ 9]); - b = GMUL(i[ 6], mix[10]); - c = GMUL(i[ 7], mix[11]); - d = GMUL(i[ 8], mix[12]); - out[ 7] = XOR(XOR(a, b), XOR(c, d)); - a = GMUL(i[ 5], mix[13]); - b = GMUL(i[ 6], mix[14]); - c = GMUL(i[ 7], mix[15]); - d = GMUL(i[ 8], mix[16]); - out[ 8] = XOR(XOR(a, b), XOR(c, d)); - - - a = GMUL(i[ 9], mix[ 1]); - b = GMUL(i[10], mix[ 2]); - c = GMUL(i[11], mix[ 3]); - d = GMUL(i[12], mix[ 4]); - out[ 9] = XOR(XOR(a, b), XOR(c, d)); - a = GMUL(i[ 9], mix[ 5]); - b = GMUL(i[10], mix[ 6]); - c = GMUL(i[11], mix[ 7]); - d = GMUL(i[12], mix[ 8]); - out[10] = XOR(XOR(a, b), XOR(c, d)); - a = GMUL(i[ 9], mix[ 9]); - b = GMUL(i[10], mix[10]); - c = GMUL(i[11], mix[11]); - d = GMUL(i[12], mix[12]); - out[11] = XOR(XOR(a, b), XOR(c, d)); - a = GMUL(i[ 9], mix[13]); - b = GMUL(i[10], mix[14]); - c = GMUL(i[11], mix[15]); - d = GMUL(i[12], mix[16]); - out[12] = XOR(XOR(a, b), XOR(c, d)); - - - a = GMUL(i[13], mix[ 1]); - b = GMUL(i[14], mix[ 2]); - c = GMUL(i[15], mix[ 3]); - d = GMUL(i[16], mix[ 4]); - out[13] = XOR(XOR(a, b), XOR(c, d)); - a = GMUL(i[13], mix[ 5]); - b = GMUL(i[14], mix[ 6]); - c = GMUL(i[15], mix[ 7]); - d = GMUL(i[16], mix[ 8]); - out[14] = XOR(XOR(a, b), XOR(c, d)); - a = GMUL(i[13], mix[ 9]); - b = GMUL(i[14], mix[10]); - c = GMUL(i[15], mix[11]); - d = GMUL(i[16], mix[12]); - out[15] = XOR(XOR(a, b), XOR(c, d)); - a = GMUL(i[13], mix[13]); - b = GMUL(i[14], mix[14]); - c = GMUL(i[15], mix[15]); - d = GMUL(i[16], mix[16]); - out[16] = XOR(XOR(a, b), XOR(c, d)); - - return out; -end - -local keyRound = function(key, round) - local i = (round - 1) * 24; - local out = key; - - out[25 + i] = XOR(key[ 1 + i], XOR(SBOX[key[22 + i]], RCON[round])); - out[26 + i] = XOR(key[ 2 + i], SBOX[key[23 + i]]); - out[27 + i] = XOR(key[ 3 + i], SBOX[key[24 + i]]); - out[28 + i] = XOR(key[ 4 + i], SBOX[key[21 + i]]); - - out[29 + i] = XOR(out[25 + i], key[ 5 + i]); - out[30 + i] = XOR(out[26 + i], key[ 6 + i]); - out[31 + i] = XOR(out[27 + i], key[ 7 + i]); - out[32 + i] = XOR(out[28 + i], key[ 8 + i]); - - out[33 + i] = XOR(out[29 + i], key[ 9 + i]); - out[34 + i] = XOR(out[30 + i], key[10 + i]); - out[35 + i] = XOR(out[31 + i], key[11 + i]); - out[36 + i] = XOR(out[32 + i], key[12 + i]); - - out[37 + i] = XOR(out[33 + i], key[13 + i]); - out[38 + i] = XOR(out[34 + i], key[14 + i]); - out[39 + i] = XOR(out[35 + i], key[15 + i]); - out[40 + i] = XOR(out[36 + i], key[16 + i]); - - out[41 + i] = XOR(out[37 + i], key[17 + i]); - out[42 + i] = XOR(out[38 + i], key[18 + i]); - out[43 + i] = XOR(out[39 + i], key[19 + i]); - out[44 + i] = XOR(out[40 + i], key[20 + i]); - - out[45 + i] = XOR(out[41 + i], key[21 + i]); - out[46 + i] = XOR(out[42 + i], key[22 + i]); - out[47 + i] = XOR(out[43 + i], key[23 + i]); - out[48 + i] = XOR(out[44 + i], key[24 + i]); - - return out; -end - -local keyExpand = function(key) - local bytes = Array.copy(key); - - for i = 1, 8 do - keyRound(bytes, i); - end - - local keys = {}; - - keys[ 1] = Array.slice(bytes, 1, 16); - keys[ 2] = Array.slice(bytes, 17, 32); - keys[ 3] = Array.slice(bytes, 33, 48); - keys[ 4] = Array.slice(bytes, 49, 64); - keys[ 5] = Array.slice(bytes, 65, 80); - keys[ 6] = Array.slice(bytes, 81, 96); - keys[ 7] = Array.slice(bytes, 97, 112); - keys[ 8] = Array.slice(bytes, 113, 128); - keys[ 9] = Array.slice(bytes, 129, 144); - keys[10] = Array.slice(bytes, 145, 160); - keys[11] = Array.slice(bytes, 161, 176); - keys[12] = Array.slice(bytes, 177, 192); - keys[13] = Array.slice(bytes, 193, 208); - - return keys; - -end - -local addKey = Array.XOR; - - - -local AES = {}; - -AES.blockSize = 16; - -AES.encrypt = function(_key, block) - - local key = keyExpand(_key); - - --round 0 - block = addKey(block, key[1]); - - --round 1 - block = byteSub(block, SBOX); - block = shiftRow(block, ROW_SHIFT); - block = mixCol(block, MIXTABLE); - block = addKey(block, key[2]); - - --round 2 - block = byteSub(block, SBOX); - block = shiftRow(block, ROW_SHIFT); - block = mixCol(block, MIXTABLE); - block = addKey(block, key[3]); - - --round 3 - block = byteSub(block, SBOX); - block = shiftRow(block, ROW_SHIFT); - block = mixCol(block, MIXTABLE); - block = addKey(block, key[4]); - - --round 4 - block = byteSub(block, SBOX); - block = shiftRow(block, ROW_SHIFT); - block = mixCol(block, MIXTABLE); - block = addKey(block, key[5]); - - --round 5 - block = byteSub(block, SBOX); - block = shiftRow(block, ROW_SHIFT); - block = mixCol(block, MIXTABLE); - block = addKey(block, key[6]); - - --round 6 - block = byteSub(block, SBOX); - block = shiftRow(block, ROW_SHIFT); - block = mixCol(block, MIXTABLE); - block = addKey(block, key[7]); - - --round 7 - block = byteSub(block, SBOX); - block = shiftRow(block, ROW_SHIFT); - block = mixCol(block, MIXTABLE); - block = addKey(block, key[8]); - - --round 8 - block = byteSub(block, SBOX); - block = shiftRow(block, ROW_SHIFT); - block = mixCol(block, MIXTABLE); - block = addKey(block, key[9]); - - --round 9 - block = byteSub(block, SBOX); - block = shiftRow(block, ROW_SHIFT); - block = mixCol(block, MIXTABLE); - block = addKey(block, key[10]); - - --round 10 - block = byteSub(block, SBOX); - block = shiftRow(block, ROW_SHIFT); - block = mixCol(block, MIXTABLE); - block = addKey(block, key[11]); - - --round 11 - block = byteSub(block, SBOX); - block = shiftRow(block, ROW_SHIFT); - block = mixCol(block, MIXTABLE); - block = addKey(block, key[12]); - - --round 12 - block = byteSub(block, SBOX); - block = shiftRow(block, ROW_SHIFT); - block = addKey(block, key[13]); - - return block; - -end - -AES.decrypt = function(_key, block) - - local key = keyExpand(_key); - - --round 0 - block = addKey(block, key[13]); - - --round 1 - block = shiftRow(block, IROW_SHIFT); - block = byteSub(block, ISBOX); - block = addKey(block, key[12]); - block = mixCol(block, IMIXTABLE); - - --round 2 - block = shiftRow(block, IROW_SHIFT); - block = byteSub(block, ISBOX); - block = addKey(block, key[11]); - block = mixCol(block, IMIXTABLE); - - --round 3 - block = shiftRow(block, IROW_SHIFT); - block = byteSub(block, ISBOX); - block = addKey(block, key[10]); - block = mixCol(block, IMIXTABLE); - - --round 4 - block = shiftRow(block, IROW_SHIFT); - block = byteSub(block, ISBOX); - block = addKey(block, key[9]); - block = mixCol(block, IMIXTABLE); - - --round 5 - block = shiftRow(block, IROW_SHIFT); - block = byteSub(block, ISBOX); - block = addKey(block, key[8]); - block = mixCol(block, IMIXTABLE); - - --round 6 - block = shiftRow(block, IROW_SHIFT); - block = byteSub(block, ISBOX); - block = addKey(block, key[7]); - block = mixCol(block, IMIXTABLE); - - --round 7 - block = shiftRow(block, IROW_SHIFT); - block = byteSub(block, ISBOX); - block = addKey(block, key[6]); - block = mixCol(block, IMIXTABLE); - - --round 8 - block = shiftRow(block, IROW_SHIFT); - block = byteSub(block, ISBOX); - block = addKey(block, key[5]); - block = mixCol(block, IMIXTABLE); - - --round 9 - block = shiftRow(block, IROW_SHIFT); - block = byteSub(block, ISBOX); - block = addKey(block, key[4]); - block = mixCol(block, IMIXTABLE); - - --round 10 - block = shiftRow(block, IROW_SHIFT); - block = byteSub(block, ISBOX); - block = addKey(block, key[3]); - block = mixCol(block, IMIXTABLE); - - --round 11 - block = shiftRow(block, IROW_SHIFT); - block = byteSub(block, ISBOX); - block = addKey(block, key[2]); - block = mixCol(block, IMIXTABLE); - - --round 12 - block = shiftRow(block, IROW_SHIFT); - block = byteSub(block, ISBOX); - block = addKey(block, key[1]); - - return block; -end - -return AES; diff --git a/lockbox/cipher/aes256.lua b/lockbox/cipher/aes256.lua deleted file mode 100644 index 854bae9..0000000 --- a/lockbox/cipher/aes256.lua +++ /dev/null @@ -1,498 +0,0 @@ -local Array = require("lockbox.util.array"); -local Bit = require("lockbox.util.bit"); - -local XOR = Bit.bxor; - -local SBOX = { - [0] = 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, - 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, - 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, - 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, - 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, - 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, - 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, - 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, - 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, - 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, - 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, - 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, - 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, - 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, - 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, - 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16}; - -local ISBOX = { - [0] = 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB, - 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, - 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E, - 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25, - 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, - 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84, - 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06, - 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, - 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73, - 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E, - 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, - 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4, - 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F, - 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, - 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61, - 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D}; - -local ROW_SHIFT = { 1, 6, 11, 16, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, }; -local IROW_SHIFT = { 1, 14, 11, 8, 5, 2, 15, 12, 9, 6, 3, 16, 13, 10, 7, 4, }; - -local ETABLE = { - [0] = 0x01, 0x03, 0x05, 0x0F, 0x11, 0x33, 0x55, 0xFF, 0x1A, 0x2E, 0x72, 0x96, 0xA1, 0xF8, 0x13, 0x35, - 0x5F, 0xE1, 0x38, 0x48, 0xD8, 0x73, 0x95, 0xA4, 0xF7, 0x02, 0x06, 0x0A, 0x1E, 0x22, 0x66, 0xAA, - 0xE5, 0x34, 0x5C, 0xE4, 0x37, 0x59, 0xEB, 0x26, 0x6A, 0xBE, 0xD9, 0x70, 0x90, 0xAB, 0xE6, 0x31, - 0x53, 0xF5, 0x04, 0x0C, 0x14, 0x3C, 0x44, 0xCC, 0x4F, 0xD1, 0x68, 0xB8, 0xD3, 0x6E, 0xB2, 0xCD, - 0x4C, 0xD4, 0x67, 0xA9, 0xE0, 0x3B, 0x4D, 0xD7, 0x62, 0xA6, 0xF1, 0x08, 0x18, 0x28, 0x78, 0x88, - 0x83, 0x9E, 0xB9, 0xD0, 0x6B, 0xBD, 0xDC, 0x7F, 0x81, 0x98, 0xB3, 0xCE, 0x49, 0xDB, 0x76, 0x9A, - 0xB5, 0xC4, 0x57, 0xF9, 0x10, 0x30, 0x50, 0xF0, 0x0B, 0x1D, 0x27, 0x69, 0xBB, 0xD6, 0x61, 0xA3, - 0xFE, 0x19, 0x2B, 0x7D, 0x87, 0x92, 0xAD, 0xEC, 0x2F, 0x71, 0x93, 0xAE, 0xE9, 0x20, 0x60, 0xA0, - 0xFB, 0x16, 0x3A, 0x4E, 0xD2, 0x6D, 0xB7, 0xC2, 0x5D, 0xE7, 0x32, 0x56, 0xFA, 0x15, 0x3F, 0x41, - 0xC3, 0x5E, 0xE2, 0x3D, 0x47, 0xC9, 0x40, 0xC0, 0x5B, 0xED, 0x2C, 0x74, 0x9C, 0xBF, 0xDA, 0x75, - 0x9F, 0xBA, 0xD5, 0x64, 0xAC, 0xEF, 0x2A, 0x7E, 0x82, 0x9D, 0xBC, 0xDF, 0x7A, 0x8E, 0x89, 0x80, - 0x9B, 0xB6, 0xC1, 0x58, 0xE8, 0x23, 0x65, 0xAF, 0xEA, 0x25, 0x6F, 0xB1, 0xC8, 0x43, 0xC5, 0x54, - 0xFC, 0x1F, 0x21, 0x63, 0xA5, 0xF4, 0x07, 0x09, 0x1B, 0x2D, 0x77, 0x99, 0xB0, 0xCB, 0x46, 0xCA, - 0x45, 0xCF, 0x4A, 0xDE, 0x79, 0x8B, 0x86, 0x91, 0xA8, 0xE3, 0x3E, 0x42, 0xC6, 0x51, 0xF3, 0x0E, - 0x12, 0x36, 0x5A, 0xEE, 0x29, 0x7B, 0x8D, 0x8C, 0x8F, 0x8A, 0x85, 0x94, 0xA7, 0xF2, 0x0D, 0x17, - 0x39, 0x4B, 0xDD, 0x7C, 0x84, 0x97, 0xA2, 0xFD, 0x1C, 0x24, 0x6C, 0xB4, 0xC7, 0x52, 0xF6, 0x01}; - -local LTABLE = { - [0] = 0x00, 0x00, 0x19, 0x01, 0x32, 0x02, 0x1A, 0xC6, 0x4B, 0xC7, 0x1B, 0x68, 0x33, 0xEE, 0xDF, 0x03, - 0x64, 0x04, 0xE0, 0x0E, 0x34, 0x8D, 0x81, 0xEF, 0x4C, 0x71, 0x08, 0xC8, 0xF8, 0x69, 0x1C, 0xC1, - 0x7D, 0xC2, 0x1D, 0xB5, 0xF9, 0xB9, 0x27, 0x6A, 0x4D, 0xE4, 0xA6, 0x72, 0x9A, 0xC9, 0x09, 0x78, - 0x65, 0x2F, 0x8A, 0x05, 0x21, 0x0F, 0xE1, 0x24, 0x12, 0xF0, 0x82, 0x45, 0x35, 0x93, 0xDA, 0x8E, - 0x96, 0x8F, 0xDB, 0xBD, 0x36, 0xD0, 0xCE, 0x94, 0x13, 0x5C, 0xD2, 0xF1, 0x40, 0x46, 0x83, 0x38, - 0x66, 0xDD, 0xFD, 0x30, 0xBF, 0x06, 0x8B, 0x62, 0xB3, 0x25, 0xE2, 0x98, 0x22, 0x88, 0x91, 0x10, - 0x7E, 0x6E, 0x48, 0xC3, 0xA3, 0xB6, 0x1E, 0x42, 0x3A, 0x6B, 0x28, 0x54, 0xFA, 0x85, 0x3D, 0xBA, - 0x2B, 0x79, 0x0A, 0x15, 0x9B, 0x9F, 0x5E, 0xCA, 0x4E, 0xD4, 0xAC, 0xE5, 0xF3, 0x73, 0xA7, 0x57, - 0xAF, 0x58, 0xA8, 0x50, 0xF4, 0xEA, 0xD6, 0x74, 0x4F, 0xAE, 0xE9, 0xD5, 0xE7, 0xE6, 0xAD, 0xE8, - 0x2C, 0xD7, 0x75, 0x7A, 0xEB, 0x16, 0x0B, 0xF5, 0x59, 0xCB, 0x5F, 0xB0, 0x9C, 0xA9, 0x51, 0xA0, - 0x7F, 0x0C, 0xF6, 0x6F, 0x17, 0xC4, 0x49, 0xEC, 0xD8, 0x43, 0x1F, 0x2D, 0xA4, 0x76, 0x7B, 0xB7, - 0xCC, 0xBB, 0x3E, 0x5A, 0xFB, 0x60, 0xB1, 0x86, 0x3B, 0x52, 0xA1, 0x6C, 0xAA, 0x55, 0x29, 0x9D, - 0x97, 0xB2, 0x87, 0x90, 0x61, 0xBE, 0xDC, 0xFC, 0xBC, 0x95, 0xCF, 0xCD, 0x37, 0x3F, 0x5B, 0xD1, - 0x53, 0x39, 0x84, 0x3C, 0x41, 0xA2, 0x6D, 0x47, 0x14, 0x2A, 0x9E, 0x5D, 0x56, 0xF2, 0xD3, 0xAB, - 0x44, 0x11, 0x92, 0xD9, 0x23, 0x20, 0x2E, 0x89, 0xB4, 0x7C, 0xB8, 0x26, 0x77, 0x99, 0xE3, 0xA5, - 0x67, 0x4A, 0xED, 0xDE, 0xC5, 0x31, 0xFE, 0x18, 0x0D, 0x63, 0x8C, 0x80, 0xC0, 0xF7, 0x70, 0x07}; - -local MIXTABLE = { - 0x02, 0x03, 0x01, 0x01, - 0x01, 0x02, 0x03, 0x01, - 0x01, 0x01, 0x02, 0x03, - 0x03, 0x01, 0x01, 0x02}; - -local IMIXTABLE = { - 0x0E, 0x0B, 0x0D, 0x09, - 0x09, 0x0E, 0x0B, 0x0D, - 0x0D, 0x09, 0x0E, 0x0B, - 0x0B, 0x0D, 0x09, 0x0E}; - -local RCON = { -[0] = 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, -0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, -0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, -0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, -0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, -0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, -0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, -0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, -0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, -0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, -0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, -0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, -0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, -0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, -0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, -0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d}; - - -local GMUL = function(A, B) - if(A == 0x01) then return B; end - if(B == 0x01) then return A; end - if(A == 0x00) then return 0; end - if(B == 0x00) then return 0; end - - local LA = LTABLE[A]; - local LB = LTABLE[B]; - - local sum = LA + LB; - if (sum > 0xFF) then sum = sum - 0xFF; end - - return ETABLE[sum]; -end - -local byteSub = Array.substitute; - -local shiftRow = Array.permute; - -local mixCol = function(i, mix) - local out = {}; - - local a, b, c, d; - - a = GMUL(i[ 1], mix[ 1]); - b = GMUL(i[ 2], mix[ 2]); - c = GMUL(i[ 3], mix[ 3]); - d = GMUL(i[ 4], mix[ 4]); - out[ 1] = XOR(XOR(a, b), XOR(c, d)); - a = GMUL(i[ 1], mix[ 5]); - b = GMUL(i[ 2], mix[ 6]); - c = GMUL(i[ 3], mix[ 7]); - d = GMUL(i[ 4], mix[ 8]); - out[ 2] = XOR(XOR(a, b), XOR(c, d)); - a = GMUL(i[ 1], mix[ 9]); - b = GMUL(i[ 2], mix[10]); - c = GMUL(i[ 3], mix[11]); - d = GMUL(i[ 4], mix[12]); - out[ 3] = XOR(XOR(a, b), XOR(c, d)); - a = GMUL(i[ 1], mix[13]); - b = GMUL(i[ 2], mix[14]); - c = GMUL(i[ 3], mix[15]); - d = GMUL(i[ 4], mix[16]); - out[ 4] = XOR(XOR(a, b), XOR(c, d)); - - - a = GMUL(i[ 5], mix[ 1]); - b = GMUL(i[ 6], mix[ 2]); - c = GMUL(i[ 7], mix[ 3]); - d = GMUL(i[ 8], mix[ 4]); - out[ 5] = XOR(XOR(a, b), XOR(c, d)); - a = GMUL(i[ 5], mix[ 5]); - b = GMUL(i[ 6], mix[ 6]); - c = GMUL(i[ 7], mix[ 7]); - d = GMUL(i[ 8], mix[ 8]); - out[ 6] = XOR(XOR(a, b), XOR(c, d)); - a = GMUL(i[ 5], mix[ 9]); - b = GMUL(i[ 6], mix[10]); - c = GMUL(i[ 7], mix[11]); - d = GMUL(i[ 8], mix[12]); - out[ 7] = XOR(XOR(a, b), XOR(c, d)); - a = GMUL(i[ 5], mix[13]); - b = GMUL(i[ 6], mix[14]); - c = GMUL(i[ 7], mix[15]); - d = GMUL(i[ 8], mix[16]); - out[ 8] = XOR(XOR(a, b), XOR(c, d)); - - - a = GMUL(i[ 9], mix[ 1]); - b = GMUL(i[10], mix[ 2]); - c = GMUL(i[11], mix[ 3]); - d = GMUL(i[12], mix[ 4]); - out[ 9] = XOR(XOR(a, b), XOR(c, d)); - a = GMUL(i[ 9], mix[ 5]); - b = GMUL(i[10], mix[ 6]); - c = GMUL(i[11], mix[ 7]); - d = GMUL(i[12], mix[ 8]); - out[10] = XOR(XOR(a, b), XOR(c, d)); - a = GMUL(i[ 9], mix[ 9]); - b = GMUL(i[10], mix[10]); - c = GMUL(i[11], mix[11]); - d = GMUL(i[12], mix[12]); - out[11] = XOR(XOR(a, b), XOR(c, d)); - a = GMUL(i[ 9], mix[13]); - b = GMUL(i[10], mix[14]); - c = GMUL(i[11], mix[15]); - d = GMUL(i[12], mix[16]); - out[12] = XOR(XOR(a, b), XOR(c, d)); - - - a = GMUL(i[13], mix[ 1]); - b = GMUL(i[14], mix[ 2]); - c = GMUL(i[15], mix[ 3]); - d = GMUL(i[16], mix[ 4]); - out[13] = XOR(XOR(a, b), XOR(c, d)); - a = GMUL(i[13], mix[ 5]); - b = GMUL(i[14], mix[ 6]); - c = GMUL(i[15], mix[ 7]); - d = GMUL(i[16], mix[ 8]); - out[14] = XOR(XOR(a, b), XOR(c, d)); - a = GMUL(i[13], mix[ 9]); - b = GMUL(i[14], mix[10]); - c = GMUL(i[15], mix[11]); - d = GMUL(i[16], mix[12]); - out[15] = XOR(XOR(a, b), XOR(c, d)); - a = GMUL(i[13], mix[13]); - b = GMUL(i[14], mix[14]); - c = GMUL(i[15], mix[15]); - d = GMUL(i[16], mix[16]); - out[16] = XOR(XOR(a, b), XOR(c, d)); - - return out; -end - -local keyRound = function(key, round) - local i = (round - 1) * 32; - local out = key; - - out[33 + i] = XOR(key[ 1 + i], XOR(SBOX[key[30 + i]], RCON[round])); - out[34 + i] = XOR(key[ 2 + i], SBOX[key[31 + i]]); - out[35 + i] = XOR(key[ 3 + i], SBOX[key[32 + i]]); - out[36 + i] = XOR(key[ 4 + i], SBOX[key[29 + i]]); - - out[37 + i] = XOR(out[33 + i], key[ 5 + i]); - out[38 + i] = XOR(out[34 + i], key[ 6 + i]); - out[39 + i] = XOR(out[35 + i], key[ 7 + i]); - out[40 + i] = XOR(out[36 + i], key[ 8 + i]); - - out[41 + i] = XOR(out[37 + i], key[ 9 + i]); - out[42 + i] = XOR(out[38 + i], key[10 + i]); - out[43 + i] = XOR(out[39 + i], key[11 + i]); - out[44 + i] = XOR(out[40 + i], key[12 + i]); - - out[45 + i] = XOR(out[41 + i], key[13 + i]); - out[46 + i] = XOR(out[42 + i], key[14 + i]); - out[47 + i] = XOR(out[43 + i], key[15 + i]); - out[48 + i] = XOR(out[44 + i], key[16 + i]); - - - out[49 + i] = XOR(SBOX[out[45 + i]], key[17 + i]); - out[50 + i] = XOR(SBOX[out[46 + i]], key[18 + i]); - out[51 + i] = XOR(SBOX[out[47 + i]], key[19 + i]); - out[52 + i] = XOR(SBOX[out[48 + i]], key[20 + i]); - - out[53 + i] = XOR(out[49 + i], key[21 + i]); - out[54 + i] = XOR(out[50 + i], key[22 + i]); - out[55 + i] = XOR(out[51 + i], key[23 + i]); - out[56 + i] = XOR(out[52 + i], key[24 + i]); - - out[57 + i] = XOR(out[53 + i], key[25 + i]); - out[58 + i] = XOR(out[54 + i], key[26 + i]); - out[59 + i] = XOR(out[55 + i], key[27 + i]); - out[60 + i] = XOR(out[56 + i], key[28 + i]); - - out[61 + i] = XOR(out[57 + i], key[29 + i]); - out[62 + i] = XOR(out[58 + i], key[30 + i]); - out[63 + i] = XOR(out[59 + i], key[31 + i]); - out[64 + i] = XOR(out[60 + i], key[32 + i]); - - return out; -end - -local keyExpand = function(key) - local bytes = Array.copy(key); - - for i = 1, 7 do - keyRound(bytes, i); - end - - local keys = {}; - - keys[ 1] = Array.slice(bytes, 1, 16); - keys[ 2] = Array.slice(bytes, 17, 32); - keys[ 3] = Array.slice(bytes, 33, 48); - keys[ 4] = Array.slice(bytes, 49, 64); - keys[ 5] = Array.slice(bytes, 65, 80); - keys[ 6] = Array.slice(bytes, 81, 96); - keys[ 7] = Array.slice(bytes, 97, 112); - keys[ 8] = Array.slice(bytes, 113, 128); - keys[ 9] = Array.slice(bytes, 129, 144); - keys[10] = Array.slice(bytes, 145, 160); - keys[11] = Array.slice(bytes, 161, 176); - keys[12] = Array.slice(bytes, 177, 192); - keys[13] = Array.slice(bytes, 193, 208); - keys[14] = Array.slice(bytes, 209, 224); - keys[15] = Array.slice(bytes, 225, 240); - - return keys; - -end - -local addKey = Array.XOR; - - - -local AES = {}; - -AES.blockSize = 16; - -AES.encrypt = function(_key, block) - - local key = keyExpand(_key); - - --round 0 - block = addKey(block, key[1]); - - --round 1 - block = byteSub(block, SBOX); - block = shiftRow(block, ROW_SHIFT); - block = mixCol(block, MIXTABLE); - block = addKey(block, key[2]); - - --round 2 - block = byteSub(block, SBOX); - block = shiftRow(block, ROW_SHIFT); - block = mixCol(block, MIXTABLE); - block = addKey(block, key[3]); - - --round 3 - block = byteSub(block, SBOX); - block = shiftRow(block, ROW_SHIFT); - block = mixCol(block, MIXTABLE); - block = addKey(block, key[4]); - - --round 4 - block = byteSub(block, SBOX); - block = shiftRow(block, ROW_SHIFT); - block = mixCol(block, MIXTABLE); - block = addKey(block, key[5]); - - --round 5 - block = byteSub(block, SBOX); - block = shiftRow(block, ROW_SHIFT); - block = mixCol(block, MIXTABLE); - block = addKey(block, key[6]); - - --round 6 - block = byteSub(block, SBOX); - block = shiftRow(block, ROW_SHIFT); - block = mixCol(block, MIXTABLE); - block = addKey(block, key[7]); - - --round 7 - block = byteSub(block, SBOX); - block = shiftRow(block, ROW_SHIFT); - block = mixCol(block, MIXTABLE); - block = addKey(block, key[8]); - - --round 8 - block = byteSub(block, SBOX); - block = shiftRow(block, ROW_SHIFT); - block = mixCol(block, MIXTABLE); - block = addKey(block, key[9]); - - --round 9 - block = byteSub(block, SBOX); - block = shiftRow(block, ROW_SHIFT); - block = mixCol(block, MIXTABLE); - block = addKey(block, key[10]); - - --round 10 - block = byteSub(block, SBOX); - block = shiftRow(block, ROW_SHIFT); - block = mixCol(block, MIXTABLE); - block = addKey(block, key[11]); - - --round 11 - block = byteSub(block, SBOX); - block = shiftRow(block, ROW_SHIFT); - block = mixCol(block, MIXTABLE); - block = addKey(block, key[12]); - - --round 12 - block = byteSub(block, SBOX); - block = shiftRow(block, ROW_SHIFT); - block = mixCol(block, MIXTABLE); - block = addKey(block, key[13]); - - --round 13 - block = byteSub(block, SBOX); - block = shiftRow(block, ROW_SHIFT); - block = mixCol(block, MIXTABLE); - block = addKey(block, key[14]); - - --round 14 - block = byteSub(block, SBOX); - block = shiftRow(block, ROW_SHIFT); - block = addKey(block, key[15]); - - return block; - -end - -AES.decrypt = function(_key, block) - - local key = keyExpand(_key); - - --round 0 - block = addKey(block, key[15]); - - --round 1 - block = shiftRow(block, IROW_SHIFT); - block = byteSub(block, ISBOX); - block = addKey(block, key[14]); - block = mixCol(block, IMIXTABLE); - - --round 2 - block = shiftRow(block, IROW_SHIFT); - block = byteSub(block, ISBOX); - block = addKey(block, key[13]); - block = mixCol(block, IMIXTABLE); - - --round 3 - block = shiftRow(block, IROW_SHIFT); - block = byteSub(block, ISBOX); - block = addKey(block, key[12]); - block = mixCol(block, IMIXTABLE); - - --round 4 - block = shiftRow(block, IROW_SHIFT); - block = byteSub(block, ISBOX); - block = addKey(block, key[11]); - block = mixCol(block, IMIXTABLE); - - --round 5 - block = shiftRow(block, IROW_SHIFT); - block = byteSub(block, ISBOX); - block = addKey(block, key[10]); - block = mixCol(block, IMIXTABLE); - - --round 6 - block = shiftRow(block, IROW_SHIFT); - block = byteSub(block, ISBOX); - block = addKey(block, key[9]); - block = mixCol(block, IMIXTABLE); - - --round 7 - block = shiftRow(block, IROW_SHIFT); - block = byteSub(block, ISBOX); - block = addKey(block, key[8]); - block = mixCol(block, IMIXTABLE); - - --round 8 - block = shiftRow(block, IROW_SHIFT); - block = byteSub(block, ISBOX); - block = addKey(block, key[7]); - block = mixCol(block, IMIXTABLE); - - --round 9 - block = shiftRow(block, IROW_SHIFT); - block = byteSub(block, ISBOX); - block = addKey(block, key[6]); - block = mixCol(block, IMIXTABLE); - - --round 10 - block = shiftRow(block, IROW_SHIFT); - block = byteSub(block, ISBOX); - block = addKey(block, key[5]); - block = mixCol(block, IMIXTABLE); - - --round 11 - block = shiftRow(block, IROW_SHIFT); - block = byteSub(block, ISBOX); - block = addKey(block, key[4]); - block = mixCol(block, IMIXTABLE); - - --round 12 - block = shiftRow(block, IROW_SHIFT); - block = byteSub(block, ISBOX); - block = addKey(block, key[3]); - block = mixCol(block, IMIXTABLE); - - --round 13 - block = shiftRow(block, IROW_SHIFT); - block = byteSub(block, ISBOX); - block = addKey(block, key[2]); - block = mixCol(block, IMIXTABLE); - - --round 14 - block = shiftRow(block, IROW_SHIFT); - block = byteSub(block, ISBOX); - block = addKey(block, key[1]); - - return block; -end - -return AES; diff --git a/lockbox/cipher/mode/cbc.lua b/lockbox/cipher/mode/cbc.lua deleted file mode 100644 index a02ff2e..0000000 --- a/lockbox/cipher/mode/cbc.lua +++ /dev/null @@ -1,164 +0,0 @@ -local Array = require("lockbox.util.array"); -local Stream = require("lockbox.util.stream"); -local Queue = require("lockbox.util.queue"); - -local CBC = {}; - -CBC.Cipher = function() - - local public = {}; - - local key; - local blockCipher; - local padding; - local inputQueue; - local outputQueue; - local iv; - - public.setKey = function(keyBytes) - key = keyBytes; - return public; - end - - public.setBlockCipher = function(cipher) - blockCipher = cipher; - return public; - end - - public.setPadding = function(paddingMode) - padding = paddingMode; - return public; - end - - public.init = function() - inputQueue = Queue(); - outputQueue = Queue(); - iv = nil; - return public; - end - - public.update = function(messageStream) - local byte = messageStream(); - while (byte ~= nil) do - inputQueue.push(byte); - if(inputQueue.size() >= blockCipher.blockSize) then - local block = Array.readFromQueue(inputQueue, blockCipher.blockSize); - - if(iv == nil) then - iv = block; - else - local out = Array.XOR(iv, block); - out = blockCipher.encrypt(key, out); - Array.writeToQueue(outputQueue, out); - iv = out; - end - end - byte = messageStream(); - end - return public; - end - - public.finish = function() - local paddingStream = padding(blockCipher.blockSize, inputQueue.getHead()); - public.update(paddingStream); - - return public; - end - - public.getOutputQueue = function() - return outputQueue; - end - - public.asHex = function() - return Stream.toHex(outputQueue.pop); - end - - public.asBytes = function() - return Stream.toArray(outputQueue.pop); - end - - return public; - -end - - -CBC.Decipher = function() - - local public = {}; - - local key; - local blockCipher; - local padding; - local inputQueue; - local outputQueue; - local iv; - - public.setKey = function(keyBytes) - key = keyBytes; - return public; - end - - public.setBlockCipher = function(cipher) - blockCipher = cipher; - return public; - end - - public.setPadding = function(paddingMode) - padding = paddingMode; - return public; - end - - public.init = function() - inputQueue = Queue(); - outputQueue = Queue(); - iv = nil; - return public; - end - - public.update = function(messageStream) - local byte = messageStream(); - while (byte ~= nil) do - inputQueue.push(byte); - if(inputQueue.size() >= blockCipher.blockSize) then - local block = Array.readFromQueue(inputQueue, blockCipher.blockSize); - - if(iv == nil) then - iv = block; - else - local out = block; - out = blockCipher.decrypt(key, out); - out = Array.XOR(iv, out); - Array.writeToQueue(outputQueue, out); - iv = block; - end - end - byte = messageStream(); - end - return public; - end - - public.finish = function() - local paddingStream = padding(blockCipher.blockSize, inputQueue.getHead()); - public.update(paddingStream); - - return public; - end - - public.getOutputQueue = function() - return outputQueue; - end - - public.asHex = function() - return Stream.toHex(outputQueue.pop); - end - - public.asBytes = function() - return Stream.toArray(outputQueue.pop); - end - - return public; - -end - -return CBC; - diff --git a/lockbox/cipher/mode/cfb.lua b/lockbox/cipher/mode/cfb.lua deleted file mode 100644 index c736d52..0000000 --- a/lockbox/cipher/mode/cfb.lua +++ /dev/null @@ -1,163 +0,0 @@ -local Array = require("lockbox.util.array"); -local Stream = require("lockbox.util.stream"); -local Queue = require("lockbox.util.queue"); - -local CFB = {}; - -CFB.Cipher = function() - - local public = {}; - - local key; - local blockCipher; - local padding; - local inputQueue; - local outputQueue; - local iv; - - public.setKey = function(keyBytes) - key = keyBytes; - return public; - end - - public.setBlockCipher = function(cipher) - blockCipher = cipher; - return public; - end - - public.setPadding = function(paddingMode) - padding = paddingMode; - return public; - end - - public.init = function() - inputQueue = Queue(); - outputQueue = Queue(); - iv = nil; - return public; - end - - public.update = function(messageStream) - local byte = messageStream(); - while (byte ~= nil) do - inputQueue.push(byte); - if(inputQueue.size() >= blockCipher.blockSize) then - local block = Array.readFromQueue(inputQueue, blockCipher.blockSize); - - if(iv == nil) then - iv = block; - else - local out = iv; - out = blockCipher.encrypt(key, out); - out = Array.XOR(out, block); - Array.writeToQueue(outputQueue, out); - iv = out; - end - end - byte = messageStream(); - end - return public; - end - - public.finish = function() - local paddingStream = padding(blockCipher.blockSize, inputQueue.getHead()); - public.update(paddingStream); - - return public; - end - - public.getOutputQueue = function() - return outputQueue; - end - - public.asHex = function() - return Stream.toHex(outputQueue.pop); - end - - public.asBytes = function() - return Stream.toArray(outputQueue.pop); - end - - return public; - -end - -CFB.Decipher = function() - - local public = {}; - - local key; - local blockCipher; - local padding; - local inputQueue; - local outputQueue; - local iv; - - public.setKey = function(keyBytes) - key = keyBytes; - return public; - end - - public.setBlockCipher = function(cipher) - blockCipher = cipher; - return public; - end - - public.setPadding = function(paddingMode) - padding = paddingMode; - return public; - end - - public.init = function() - inputQueue = Queue(); - outputQueue = Queue(); - iv = nil; - return public; - end - - public.update = function(messageStream) - local byte = messageStream(); - while (byte ~= nil) do - inputQueue.push(byte); - if(inputQueue.size() >= blockCipher.blockSize) then - local block = Array.readFromQueue(inputQueue, blockCipher.blockSize); - - if(iv == nil) then - iv = block; - else - local out = iv; - out = blockCipher.encrypt(key, out); - out = Array.XOR(out, block); - Array.writeToQueue(outputQueue, out); - iv = block; - end - end - byte = messageStream(); - end - return public; - end - - public.finish = function() - local paddingStream = padding(blockCipher.blockSize, inputQueue.getHead()); - public.update(paddingStream); - - return public; - end - - public.getOutputQueue = function() - return outputQueue; - end - - public.asHex = function() - return Stream.toHex(outputQueue.pop); - end - - public.asBytes = function() - return Stream.toArray(outputQueue.pop); - end - - return public; - -end - -return CFB; diff --git a/lockbox/cipher/mode/ctr.lua b/lockbox/cipher/mode/ctr.lua deleted file mode 100644 index beb8ef0..0000000 --- a/lockbox/cipher/mode/ctr.lua +++ /dev/null @@ -1,248 +0,0 @@ -local Array = require("lockbox.util.array"); -local Stream = require("lockbox.util.stream"); -local Queue = require("lockbox.util.queue"); - -local Bit = require("lockbox.util.bit"); - -local AND = Bit.band; - -local CTR = {}; - -CTR.Cipher = function() - - local public = {}; - - local key; - local blockCipher; - local padding; - local inputQueue; - local outputQueue; - local iv; - - public.setKey = function(keyBytes) - key = keyBytes; - return public; - end - - public.setBlockCipher = function(cipher) - blockCipher = cipher; - return public; - end - - public.setPadding = function(paddingMode) - padding = paddingMode; - return public; - end - - public.init = function() - inputQueue = Queue(); - outputQueue = Queue(); - iv = nil; - return public; - end - - local updateIV = function() - iv[16] = iv[16] + 1; - if iv[16] <= 0xFF then return; end - iv[16] = AND(iv[16], 0xFF); - - iv[15] = iv[15] + 1; - if iv[15] <= 0xFF then return; end - iv[15] = AND(iv[15], 0xFF); - - iv[14] = iv[14] + 1; - if iv[14] <= 0xFF then return; end - iv[14] = AND(iv[14], 0xFF); - - iv[13] = iv[13] + 1; - if iv[13] <= 0xFF then return; end - iv[13] = AND(iv[13], 0xFF); - - iv[12] = iv[12] + 1; - if iv[12] <= 0xFF then return; end - iv[12] = AND(iv[12], 0xFF); - - iv[11] = iv[11] + 1; - if iv[11] <= 0xFF then return; end - iv[11] = AND(iv[11], 0xFF); - - iv[10] = iv[10] + 1; - if iv[10] <= 0xFF then return; end - iv[10] = AND(iv[10], 0xFF); - - iv[9] = iv[9] + 1; - if iv[9] <= 0xFF then return; end - iv[9] = AND(iv[9], 0xFF); - - return; - end - - public.update = function(messageStream) - local byte = messageStream(); - while (byte ~= nil) do - inputQueue.push(byte); - - if(inputQueue.size() >= blockCipher.blockSize) then - local block = Array.readFromQueue(inputQueue, blockCipher.blockSize); - - if(iv == nil) then - iv = block; - else - local out = iv; - out = blockCipher.encrypt(key, out); - - out = Array.XOR(out, block); - Array.writeToQueue(outputQueue, out); - updateIV(); - end - end - byte = messageStream(); - end - return public; - end - - public.finish = function() - local paddingStream = padding(blockCipher.blockSize, inputQueue.getHead()); - public.update(paddingStream); - - return public; - end - - public.getOutputQueue = function() - return outputQueue; - end - - public.asHex = function() - return Stream.toHex(outputQueue.pop); - end - - public.asBytes = function() - return Stream.toArray(outputQueue.pop); - end - - return public; - -end - - -CTR.Decipher = function() - - local public = {}; - - local key; - local blockCipher; - local padding; - local inputQueue; - local outputQueue; - local iv; - - public.setKey = function(keyBytes) - key = keyBytes; - return public; - end - - public.setBlockCipher = function(cipher) - blockCipher = cipher; - return public; - end - - public.setPadding = function(paddingMode) - padding = paddingMode; - return public; - end - - public.init = function() - inputQueue = Queue(); - outputQueue = Queue(); - iv = nil; - return public; - end - - local updateIV = function() - iv[16] = iv[16] + 1; - if iv[16] <= 0xFF then return; end - iv[16] = AND(iv[16], 0xFF); - - iv[15] = iv[15] + 1; - if iv[15] <= 0xFF then return; end - iv[15] = AND(iv[15], 0xFF); - - iv[14] = iv[14] + 1; - if iv[14] <= 0xFF then return; end - iv[14] = AND(iv[14], 0xFF); - - iv[13] = iv[13] + 1; - if iv[13] <= 0xFF then return; end - iv[13] = AND(iv[13], 0xFF); - - iv[12] = iv[12] + 1; - if iv[12] <= 0xFF then return; end - iv[12] = AND(iv[12], 0xFF); - - iv[11] = iv[11] + 1; - if iv[11] <= 0xFF then return; end - iv[11] = AND(iv[11], 0xFF); - - iv[10] = iv[10] + 1; - if iv[10] <= 0xFF then return; end - iv[10] = AND(iv[10], 0xFF); - - iv[9] = iv[9] + 1; - if iv[9] <= 0xFF then return; end - iv[9] = AND(iv[9], 0xFF); - - return; - end - - public.update = function(messageStream) - local byte = messageStream(); - while (byte ~= nil) do - inputQueue.push(byte); - - if(inputQueue.size() >= blockCipher.blockSize) then - local block = Array.readFromQueue(inputQueue, blockCipher.blockSize); - - if(iv == nil) then - iv = block; - else - local out = iv; - out = blockCipher.encrypt(key, out); - - out = Array.XOR(out, block); - Array.writeToQueue(outputQueue, out); - updateIV(); - end - end - byte = messageStream(); - end - return public; - end - - public.finish = function() - local paddingStream = padding(blockCipher.blockSize, inputQueue.getHead()); - public.update(paddingStream); - - return public; - end - - public.getOutputQueue = function() - return outputQueue; - end - - public.asHex = function() - return Stream.toHex(outputQueue.pop); - end - - public.asBytes = function() - return Stream.toArray(outputQueue.pop); - end - - return public; - -end - - - - -return CTR; - diff --git a/lockbox/cipher/mode/ofb.lua b/lockbox/cipher/mode/ofb.lua deleted file mode 100644 index a824846..0000000 --- a/lockbox/cipher/mode/ofb.lua +++ /dev/null @@ -1,164 +0,0 @@ -local Array = require("lockbox.util.array"); -local Stream = require("lockbox.util.stream"); -local Queue = require("lockbox.util.queue"); - -local OFB = {}; - -OFB.Cipher = function() - - local public = {}; - - local key; - local blockCipher; - local padding; - local inputQueue; - local outputQueue; - local iv; - - public.setKey = function(keyBytes) - key = keyBytes; - return public; - end - - public.setBlockCipher = function(cipher) - blockCipher = cipher; - return public; - end - - public.setPadding = function(paddingMode) - padding = paddingMode; - return public; - end - - public.init = function() - inputQueue = Queue(); - outputQueue = Queue(); - iv = nil; - return public; - end - - public.update = function(messageStream) - local byte = messageStream(); - while (byte ~= nil) do - inputQueue.push(byte); - if(inputQueue.size() >= blockCipher.blockSize) then - local block = Array.readFromQueue(inputQueue, blockCipher.blockSize); - - if(iv == nil) then - iv = block; - else - local out = iv; - out = blockCipher.encrypt(key, out); - iv = out; - out = Array.XOR(out, block); - Array.writeToQueue(outputQueue, out); - end - end - byte = messageStream(); - end - return public; - end - - public.finish = function() - local paddingStream = padding(blockCipher.blockSize, inputQueue.getHead()); - public.update(paddingStream); - - return public; - end - - public.getOutputQueue = function() - return outputQueue; - end - - public.asHex = function() - return Stream.toHex(outputQueue.pop); - end - - public.asBytes = function() - return Stream.toArray(outputQueue.pop); - end - - return public; - -end - -OFB.Decipher = function() - - local public = {}; - - local key; - local blockCipher; - local padding; - local inputQueue; - local outputQueue; - local iv; - - public.setKey = function(keyBytes) - key = keyBytes; - return public; - end - - public.setBlockCipher = function(cipher) - blockCipher = cipher; - return public; - end - - public.setPadding = function(paddingMode) - padding = paddingMode; - return public; - end - - public.init = function() - inputQueue = Queue(); - outputQueue = Queue(); - iv = nil; - return public; - end - - public.update = function(messageStream) - local byte = messageStream(); - while (byte ~= nil) do - inputQueue.push(byte); - if(inputQueue.size() >= blockCipher.blockSize) then - local block = Array.readFromQueue(inputQueue, blockCipher.blockSize); - - if(iv == nil) then - iv = block; - else - local out = iv; - out = blockCipher.encrypt(key, out); - iv = out; - out = Array.XOR(out, block); - Array.writeToQueue(outputQueue, out); - end - end - byte = messageStream(); - end - return public; - end - - public.finish = function() - local paddingStream = padding(blockCipher.blockSize, inputQueue.getHead()); - public.update(paddingStream); - - return public; - end - - public.getOutputQueue = function() - return outputQueue; - end - - public.asHex = function() - return Stream.toHex(outputQueue.pop); - end - - public.asBytes = function() - return Stream.toArray(outputQueue.pop); - end - - return public; - -end - - -return OFB; diff --git a/lockbox/digest/md5.lua b/lockbox/digest/md5.lua new file mode 100644 index 0000000..6ce1df2 --- /dev/null +++ b/lockbox/digest/md5.lua @@ -0,0 +1,201 @@ +require("lockbox").insecure(); + +local Bit = require("lockbox.util.bit"); +local String = require("string"); +local Math = require("math"); +local Queue = require("lockbox.util.queue"); + +local SHIFT = { + 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, + 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, + 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, + 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21}; + +local CONSTANTS = { + 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, + 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, + 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, + 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, + 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, + 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, + 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, + 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, + 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, + 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, + 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, + 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, + 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, + 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, + 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, + 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391}; + +local AND = Bit.band; +local OR = Bit.bor; +local NOT = Bit.bnot; +local XOR = Bit.bxor; +local LROT = Bit.lrotate; +local LSHIFT = Bit.lshift; +local RSHIFT = Bit.rshift; + +--MD5 is little-endian +local bytes2word = function(b0, b1, b2, b3) + local i = b3; i = LSHIFT(i, 8); + i = OR(i, b2); i = LSHIFT(i, 8); + i = OR(i, b1); i = LSHIFT(i, 8); + i = OR(i, b0); + return i; +end + +local word2bytes = function(word) + local b0, b1, b2, b3; + b0 = AND(word, 0xFF); word = RSHIFT(word, 8); + b1 = AND(word, 0xFF); word = RSHIFT(word, 8); + b2 = AND(word, 0xFF); word = RSHIFT(word, 8); + b3 = AND(word, 0xFF); + return b0, b1, b2, b3; +end + +local dword2bytes = function(i) + local b4, b5, b6, b7 = word2bytes(Math.floor(i / 0x100000000)); + local b0, b1, b2, b3 = word2bytes(i); + return b0, b1, b2, b3, b4, b5, b6, b7; +end + +local F = function(x, y, z) return OR(AND(x, y), AND(NOT(x), z)); end +local G = function(x, y, z) return OR(AND(x, z), AND(y, NOT(z))); end +local H = function(x, y, z) return XOR(x, XOR(y, z)); end +local I = function(x, y, z) return XOR(y, OR(x, NOT(z))); end + +local MD5 = function() + + local queue = Queue(); + + local A = 0x67452301; + local B = 0xefcdab89; + local C = 0x98badcfe; + local D = 0x10325476; + local public = {}; + + local processBlock = function() + local a = A; + local b = B; + local c = C; + local d = D; + + local X = {}; + + for i = 1, 16 do + X[i] = bytes2word(queue.pop(), queue.pop(), queue.pop(), queue.pop()); + end + + for i = 0, 63 do + local f, g, temp; + + if (0 <= i) and (i <= 15) then + f = F(b, c, d); + g = i; + elseif (16 <= i) and (i <= 31) then + f = G(b, c, d); + g = (5 * i + 1) % 16; + elseif (32 <= i) and (i <= 47) then + f = H(b, c, d); + g = (3 * i + 5) % 16; + elseif (48 <= i) and (i <= 63) then + f = I(b, c, d); + g = (7 * i) % 16; + end + temp = d; + d = c; + c = b; + b = b + LROT((a + f + CONSTANTS[i + 1] + X[g + 1]), SHIFT[i + 1]); + a = temp; + end + + A = AND(A + a, 0xFFFFFFFF); + B = AND(B + b, 0xFFFFFFFF); + C = AND(C + c, 0xFFFFFFFF); + D = AND(D + d, 0xFFFFFFFF); + end + + public.init = function() + queue.reset(); + + A = 0x67452301; + B = 0xefcdab89; + C = 0x98badcfe; + D = 0x10325476; + + return public; + end + + public.update = function(bytes) + for b in bytes do + queue.push(b); + if(queue.size() >= 64) then processBlock(); end + end + + return public; + end + + public.finish = function() + local bits = queue.getHead() * 8; + + queue.push(0x80); + while ((queue.size() + 7) % 64) < 63 do + queue.push(0x00); + end + + local b0, b1, b2, b3, b4, b5, b6, b7 = dword2bytes(bits); + + queue.push(b0); + queue.push(b1); + queue.push(b2); + queue.push(b3); + queue.push(b4); + queue.push(b5); + queue.push(b6); + queue.push(b7); + + while queue.size() > 0 do + processBlock(); + end + + return public; + end + + public.asBytes = function() + local b0, b1, b2, b3 = word2bytes(A); + local b4, b5, b6, b7 = word2bytes(B); + local b8, b9, b10, b11 = word2bytes(C); + local b12, b13, b14, b15 = word2bytes(D); + + return {b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15}; + end + + public.asHex = function() + local b0, b1, b2, b3 = word2bytes(A); + local b4, b5, b6, b7 = word2bytes(B); + local b8, b9, b10, b11 = word2bytes(C); + local b12, b13, b14, b15 = word2bytes(D); + + return String.format("%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15); + end + + public.asString = function() + local b0, b1, b2, b3 = word2bytes(A); + local b4, b5, b6, b7 = word2bytes(B); + local b8, b9, b10, b11 = word2bytes(C); + local b12, b13, b14, b15 = word2bytes(D); + + return string.pack(string.rep('B', 16), + b0, b1, b2, b3, b4, b5, b6, b7, b8, + b9, b10, b11, b12, b13, b14, b15 + ) + end + + return public; + +end + +return MD5; diff --git a/lockbox/init.lua b/lockbox/init.lua index 0031a50..caee6c0 100644 --- a/lockbox/init.lua +++ b/lockbox/init.lua @@ -1,5 +1,8 @@ local Lockbox = {}; +-- cc-mek-scada lockbox version +Lockbox.version = "1.0" + --[[ package.path = "./?.lua;" .. "./cipher/?.lua;" diff --git a/lockbox/padding/ansix923.lua b/lockbox/padding/ansix923.lua deleted file mode 100644 index 83702c6..0000000 --- a/lockbox/padding/ansix923.lua +++ /dev/null @@ -1,22 +0,0 @@ -local ANSIX923Padding = function(blockSize, byteCount) - - local paddingCount = blockSize - (byteCount % blockSize); - local bytesLeft = paddingCount; - - local stream = function() - if bytesLeft > 1 then - bytesLeft = bytesLeft - 1; - return 0x00; - elseif bytesLeft > 0 then - bytesLeft = bytesLeft - 1; - return paddingCount; - else - return nil; - end - end - - return stream; - -end - -return ANSIX923Padding; diff --git a/lockbox/padding/isoiec7816.lua b/lockbox/padding/isoiec7816.lua deleted file mode 100644 index 3dc255d..0000000 --- a/lockbox/padding/isoiec7816.lua +++ /dev/null @@ -1,22 +0,0 @@ -local ISOIEC7816Padding = function(blockSize, byteCount) - - local paddingCount = blockSize - (byteCount % blockSize); - local bytesLeft = paddingCount; - - local stream = function() - if bytesLeft == paddingCount then - bytesLeft = bytesLeft - 1; - return 0x80; - elseif bytesLeft > 0 then - bytesLeft = bytesLeft - 1; - return 0x00; - else - return nil; - end - end - - return stream; - -end - -return ISOIEC7816Padding; diff --git a/lockbox/padding/pkcs7.lua b/lockbox/padding/pkcs7.lua deleted file mode 100644 index 3b635ab..0000000 --- a/lockbox/padding/pkcs7.lua +++ /dev/null @@ -1,18 +0,0 @@ -local PKCS7Padding = function(blockSize, byteCount) - - local paddingCount = blockSize - ((byteCount -1) % blockSize) + 1; - local bytesLeft = paddingCount; - - local stream = function() - if bytesLeft > 0 then - bytesLeft = bytesLeft - 1; - return paddingCount; - else - return nil; - end - end - - return stream; -end - -return PKCS7Padding; diff --git a/lockbox/padding/zero.lua b/lockbox/padding/zero.lua deleted file mode 100644 index d42a9b7..0000000 --- a/lockbox/padding/zero.lua +++ /dev/null @@ -1,19 +0,0 @@ -local ZeroPadding = function(blockSize, byteCount) - - local paddingCount = blockSize - ((byteCount -1) % blockSize) + 1; - local bytesLeft = paddingCount; - - local stream = function() - if bytesLeft > 0 then - bytesLeft = bytesLeft - 1; - return 0x00; - else - return nil; - end - end - - return stream; - -end - -return ZeroPadding; diff --git a/lockbox/util/bit.lua b/lockbox/util/bit.lua index b17238e..1de5f8b 100644 --- a/lockbox/util/bit.lua +++ b/lockbox/util/bit.lua @@ -1,25 +1,19 @@ -local ok, e -ok = nil -if not ok then - ok, e = pcall(require, "bit") -- the LuaJIT one ? -end +-- modified (simplified) for ComputerCraft + +local ok, e = nil, nil + if not ok then ok, e = pcall(require, "bit32") -- Lua 5.2 end + if not ok then - ok, e = pcall(require, "bit.numberlua") -- for Lua 5.1, https://github.com/tst2005/lua-bit-numberlua/ + ok, e = pcall(require, "bit") end + if not ok then error("no bitwise support found", 2) end + assert(type(e) == "table", "invalid bit module") --- Workaround to support Lua 5.2 bit32 API with the LuaJIT bit one -if e.rol and not e.lrotate then - e.lrotate = e.rol -end -if e.ror and not e.rrotate then - e.rrotate = e.ror -end - return e diff --git a/pocket/config.lua b/pocket/config.lua index 0c35b59..72625f4 100644 --- a/pocket/config.lua +++ b/pocket/config.lua @@ -10,6 +10,10 @@ config.PKT_CHANNEL = 16244 config.TRUSTED_RANGE = 0 -- time in seconds (>= 2) before assuming a remote device is no longer active config.COMMS_TIMEOUT = 5 +-- facility authentication key (do NOT use one of your passwords) +-- this enables verifying that messages are authentic +-- all devices on the same network must use the same key +-- config.AUTH_KEY = "SCADAfacility123" -- log path config.LOG_PATH = "/log.txt" diff --git a/pocket/pocket.lua b/pocket/pocket.lua index 0281e92..b0432cf 100644 --- a/pocket/pocket.lua +++ b/pocket/pocket.lua @@ -17,14 +17,14 @@ local pocket = {} -- pocket coordinator + supervisor communications ---@nodiscard ---@param version string pocket version ----@param modem table modem device +---@param nic nic network interface device ---@param pkt_channel integer pocket comms channel ---@param svr_channel integer supervisor access channel ---@param crd_channel integer coordinator access channel ---@param range integer trusted device connection range ---@param sv_watchdog watchdog ---@param api_watchdog watchdog -function pocket.comms(version, modem, pkt_channel, svr_channel, crd_channel, range, sv_watchdog, api_watchdog) +function pocket.comms(version, nic, pkt_channel, svr_channel, crd_channel, range, sv_watchdog, api_watchdog) local self = { sv = { linked = false, @@ -47,13 +47,9 @@ function pocket.comms(version, modem, pkt_channel, svr_channel, crd_channel, ran -- PRIVATE FUNCTIONS -- - -- configure modem channels - local function _conf_channels() - modem.closeAll() - modem.open(pkt_channel) - end - - _conf_channels() + -- configure network channels + nic.closeAll() + nic.open(pkt_channel) -- send a management packet to the supervisor ---@param msg_type SCADA_MGMT_TYPE @@ -65,7 +61,7 @@ function pocket.comms(version, modem, pkt_channel, svr_channel, crd_channel, ran pkt.make(msg_type, msg) s_pkt.make(self.sv.addr, self.sv.seq_num, PROTOCOL.SCADA_MGMT, pkt.raw_sendable()) - modem.transmit(svr_channel, pkt_channel, s_pkt.raw_sendable()) + nic.transmit(svr_channel, pkt_channel, s_pkt) self.sv.seq_num = self.sv.seq_num + 1 end @@ -79,7 +75,7 @@ function pocket.comms(version, modem, pkt_channel, svr_channel, crd_channel, ran pkt.make(msg_type, msg) s_pkt.make(self.api.addr, self.api.seq_num, PROTOCOL.SCADA_MGMT, pkt.raw_sendable()) - modem.transmit(crd_channel, pkt_channel, s_pkt.raw_sendable()) + nic.transmit(crd_channel, pkt_channel, s_pkt) self.api.seq_num = self.api.seq_num + 1 end @@ -93,7 +89,7 @@ function pocket.comms(version, modem, pkt_channel, svr_channel, crd_channel, ran -- pkt.make(msg_type, msg) -- s_pkt.make(self.api.addr, self.api.seq_num, PROTOCOL.COORD_API, pkt.raw_sendable()) - -- modem.transmit(crd_channel, pkt_channel, s_pkt.raw_sendable()) + -- nic.transmit(crd_channel, pkt_channel, s_pkt) -- self.api.seq_num = self.api.seq_num + 1 -- end @@ -124,13 +120,6 @@ function pocket.comms(version, modem, pkt_channel, svr_channel, crd_channel, ran ---@class pocket_comms local public = {} - -- reconnect a newly connected modem - ---@param new_modem table - function public.reconnect_modem(new_modem) - modem = new_modem - _conf_channels() - end - -- close connection to the supervisor function public.close_sv() sv_watchdog.cancel() @@ -189,13 +178,10 @@ function pocket.comms(version, modem, pkt_channel, svr_channel, crd_channel, ran ---@param distance integer ---@return mgmt_frame|capi_frame|nil packet function public.parse_packet(side, sender, reply_to, message, distance) + local s_pkt = nic.receive(side, sender, reply_to, message, distance) local pkt = nil - local s_pkt = comms.scada_packet() - -- parse packet as generic SCADA packet - s_pkt.receive(side, sender, reply_to, message, distance) - - if s_pkt.is_valid() then + if s_pkt then -- get as SCADA management packet if s_pkt.protocol() == PROTOCOL.SCADA_MGMT then local mgmt_pkt = comms.mgmt_packet() diff --git a/pocket/startup.lua b/pocket/startup.lua index 7afd208..843686e 100644 --- a/pocket/startup.lua +++ b/pocket/startup.lua @@ -6,6 +6,7 @@ require("/initenv").init_env() local crash = require("scada-common.crash") local log = require("scada-common.log") +local network = require("scada-common.network") local ppm = require("scada-common.ppm") local tcd = require("scada-common.tcd") local util = require("scada-common.util") @@ -17,7 +18,7 @@ local coreio = require("pocket.coreio") local pocket = require("pocket.pocket") local renderer = require("pocket.renderer") -local POCKET_VERSION = "alpha-v0.4.5" +local POCKET_VERSION = "alpha-v0.5.1" local println = util.println local println_ts = util.println_ts @@ -67,6 +68,11 @@ local function main() -- setup communications & clocks ---------------------------------------- + -- message authentication init + if type(config.AUTH_KEY) == "string" then + network.init_mac(config.AUTH_KEY) + end + coreio.report_link_state(coreio.LINK_STATE.UNLINKED) -- get the communications modem @@ -88,8 +94,9 @@ local function main() log.debug("startup> conn watchdogs created") - -- start comms, open all channels - local pocket_comms = pocket.comms(POCKET_VERSION, modem, config.PKT_CHANNEL, config.SVR_CHANNEL, + -- create network interface then setup comms + local nic = network.nic(modem) + local pocket_comms = pocket.comms(POCKET_VERSION, nic, config.PKT_CHANNEL, config.SVR_CHANNEL, config.CRD_CHANNEL, config.TRUSTED_RANGE, conn_wd.sv, conn_wd.api) log.debug("startup> comms init") diff --git a/reactor-plc/config.lua b/reactor-plc/config.lua index 3462b2c..066ccf6 100644 --- a/reactor-plc/config.lua +++ b/reactor-plc/config.lua @@ -17,6 +17,10 @@ config.PLC_CHANNEL = 16241 config.TRUSTED_RANGE = 0 -- time in seconds (>= 2) before assuming a remote device is no longer active config.COMMS_TIMEOUT = 5 +-- facility authentication key (do NOT use one of your passwords) +-- this enables verifying that messages are authentic +-- all devices on the same network must use the same key +-- config.AUTH_KEY = "SCADAfacility123" -- log path config.LOG_PATH = "/log.txt" diff --git a/reactor-plc/plc.lua b/reactor-plc/plc.lua index 02e592a..2bba4ee 100644 --- a/reactor-plc/plc.lua +++ b/reactor-plc/plc.lua @@ -445,14 +445,14 @@ end ---@nodiscard ---@param id integer reactor ID ---@param version string PLC version ----@param modem table modem device +---@param nic nic network interface device ---@param plc_channel integer PLC comms channel ---@param svr_channel integer supervisor server channel ---@param range integer trusted device connection range ---@param reactor table reactor device ---@param rps rps RPS reference ---@param conn_watchdog watchdog watchdog reference -function plc.comms(id, version, modem, plc_channel, svr_channel, range, reactor, rps, conn_watchdog) +function plc.comms(id, version, nic, plc_channel, svr_channel, range, reactor, rps, conn_watchdog) local self = { sv_addr = comms.BROADCAST, seq_num = 0, @@ -470,13 +470,9 @@ function plc.comms(id, version, modem, plc_channel, svr_channel, range, reactor, -- PRIVATE FUNCTIONS -- - -- configure modem channels - local function _conf_channels() - modem.closeAll() - modem.open(plc_channel) - end - - _conf_channels() + -- configure network channels + nic.closeAll() + nic.open(plc_channel) -- send an RPLC packet ---@param msg_type RPLC_TYPE @@ -488,7 +484,7 @@ function plc.comms(id, version, modem, plc_channel, svr_channel, range, reactor, r_pkt.make(id, msg_type, msg) s_pkt.make(self.sv_addr, self.seq_num, PROTOCOL.RPLC, r_pkt.raw_sendable()) - modem.transmit(svr_channel, plc_channel, s_pkt.raw_sendable()) + nic.transmit(svr_channel, plc_channel, s_pkt) self.seq_num = self.seq_num + 1 end @@ -502,7 +498,7 @@ function plc.comms(id, version, modem, plc_channel, svr_channel, range, reactor, m_pkt.make(msg_type, msg) s_pkt.make(self.sv_addr, self.seq_num, PROTOCOL.SCADA_MGMT, m_pkt.raw_sendable()) - modem.transmit(svr_channel, plc_channel, s_pkt.raw_sendable()) + nic.transmit(svr_channel, plc_channel, s_pkt) self.seq_num = self.seq_num + 1 end @@ -639,7 +635,7 @@ function plc.comms(id, version, modem, plc_channel, svr_channel, range, reactor, parallel.waitForAll(table.unpack(tasks)) - if not reactor.__p_is_faulted() then + if reactor.__p_is_ok() then _send(RPLC_TYPE.MEK_STRUCT, mek_data) self.resend_build = false end @@ -650,13 +646,6 @@ function plc.comms(id, version, modem, plc_channel, svr_channel, range, reactor, ---@class plc_comms local public = {} - -- reconnect a newly connected modem - ---@param new_modem table - function public.reconnect_modem(new_modem) - modem = new_modem - _conf_channels() - end - -- reconnect a newly connected reactor ---@param new_reactor table function public.reconnect_reactor(new_reactor) @@ -743,13 +732,10 @@ function plc.comms(id, version, modem, plc_channel, svr_channel, range, reactor, ---@param distance integer ---@return rplc_frame|mgmt_frame|nil packet function public.parse_packet(side, sender, reply_to, message, distance) + local s_pkt = nic.receive(side, sender, reply_to, message, distance) local pkt = nil - local s_pkt = comms.scada_packet() - -- parse packet as generic SCADA packet - s_pkt.receive(side, sender, reply_to, message, distance) - - if s_pkt.is_valid() then + if s_pkt then -- get as RPLC packet if s_pkt.protocol() == PROTOCOL.RPLC then local rplc_pkt = comms.rplc_packet() @@ -836,7 +822,7 @@ function plc.comms(id, version, modem, plc_channel, svr_channel, range, reactor, success = true else reactor.setBurnRate(burn_rate) - success = not reactor.__p_is_faulted() + success = reactor.__p_is_ok() end else log.debug(burn_rate .. " rate outside of 0 < x <= " .. self.max_burn_rate) diff --git a/reactor-plc/startup.lua b/reactor-plc/startup.lua index 76185e9..132c7aa 100644 --- a/reactor-plc/startup.lua +++ b/reactor-plc/startup.lua @@ -8,6 +8,7 @@ local comms = require("scada-common.comms") local crash = require("scada-common.crash") local log = require("scada-common.log") local mqueue = require("scada-common.mqueue") +local network = require("scada-common.network") local ppm = require("scada-common.ppm") local rsio = require("scada-common.rsio") local util = require("scada-common.util") @@ -18,7 +19,7 @@ local plc = require("reactor-plc.plc") local renderer = require("reactor-plc.renderer") local threads = require("reactor-plc.threads") -local R_PLC_VERSION = "v1.4.6" +local R_PLC_VERSION = "v1.5.0" local println = util.println local println_ts = util.println_ts @@ -79,6 +80,11 @@ local function main() -- mount connected devices ppm.mount_all() + -- message authentication init + if type(config.AUTH_KEY) == "string" then + network.init_mac(config.AUTH_KEY) + end + -- shared memory across threads ---@class plc_shared_memory local __shared_memory = { @@ -88,13 +94,13 @@ local function main() -- PLC system state flags ---@class plc_state plc_state = { - init_ok = true, - fp_ok = false, - shutdown = false, - degraded = false, + init_ok = true, + fp_ok = false, + shutdown = false, + degraded = true, reactor_formed = true, - no_reactor = false, - no_modem = false + no_reactor = true, + no_modem = true }, -- control setpoints @@ -113,6 +119,7 @@ local function main() -- system objects plc_sys = { rps = nil, ---@type rps + nic = nil, ---@type nic plc_comms = nil, ---@type plc_comms conn_watchdog = nil ---@type watchdog }, @@ -130,14 +137,17 @@ local function main() local plc_state = __shared_memory.plc_state + -- initial state evaluation + plc_state.no_reactor = smem_dev.reactor == nil + plc_state.no_modem = smem_dev.modem == nil + -- we need a reactor, can at least do some things even if it isn't formed though - if smem_dev.reactor == nil then + if plc_state.no_reactor then println("init> fission reactor not found"); log.warning("init> no reactor on startup") plc_state.init_ok = false plc_state.degraded = true - plc_state.no_reactor = true elseif not smem_dev.reactor.isFormed() then println("init> fission reactor not formed"); log.warning("init> reactor logic adapter present, but reactor is not formed") @@ -147,7 +157,7 @@ local function main() end -- modem is required if networked - if __shared_memory.networked and smem_dev.modem == nil then + if __shared_memory.networked and plc_state.no_modem then println("init> wireless modem not found") log.warning("init> no wireless modem on startup") @@ -158,7 +168,6 @@ local function main() plc_state.init_ok = false plc_state.degraded = true - plc_state.no_modem = true end -- print a log message to the terminal as long as the UI isn't running @@ -196,8 +205,9 @@ local function main() smem_sys.conn_watchdog = util.new_watchdog(config.COMMS_TIMEOUT) log.debug("init> conn watchdog started") - -- start comms - smem_sys.plc_comms = plc.comms(config.REACTOR_ID, R_PLC_VERSION, smem_dev.modem, config.PLC_CHANNEL, config.SVR_CHANNEL, + -- create network interface then setup comms + smem_sys.nic = network.nic(smem_dev.modem) + smem_sys.plc_comms = plc.comms(config.REACTOR_ID, R_PLC_VERSION, smem_sys.nic, config.PLC_CHANNEL, config.SVR_CHANNEL, config.TRUSTED_RANGE, smem_dev.reactor, smem_sys.rps, smem_sys.conn_watchdog) log.debug("init> comms init") else diff --git a/reactor-plc/threads.lua b/reactor-plc/threads.lua index b9c986d..cf18963 100644 --- a/reactor-plc/threads.lua +++ b/reactor-plc/threads.lua @@ -59,6 +59,7 @@ function threads.thread__main(smem, init) while true do -- get plc_sys fields (may have been set late due to degraded boot) local rps = smem.plc_sys.rps + local nic = smem.plc_sys.nic local plc_comms = smem.plc_sys.plc_comms local conn_watchdog = smem.plc_sys.conn_watchdog @@ -66,6 +67,7 @@ function threads.thread__main(smem, init) -- handle event if event == "timer" and loop_clock.is_clock(param1) then + -- note: loop clock is only running if init_ok = true -- blink heartbeat indicator databus.heartbeat() @@ -75,7 +77,7 @@ function threads.thread__main(smem, init) loop_clock.start() -- send updated data - if not plc_state.no_modem then + if nic.connected() then if plc_comms.is_linked() then smem.q.mq_comms_tx.push_command(MQ__COMM_CMD.SEND_STATUS) else @@ -114,7 +116,7 @@ function threads.thread__main(smem, init) smem.q.mq_rps.push_command(MQ__RPS_CMD.SCRAM) -- determine if we are still in a degraded state - if not networked or not plc_state.no_modem then + if (not networked) or nic.connected() then plc_state.degraded = false end @@ -144,7 +146,7 @@ function threads.thread__main(smem, init) -- update indicators databus.tx_hw_status(plc_state) - elseif event == "modem_message" and networked and plc_state.init_ok and not plc_state.no_modem then + elseif event == "modem_message" and networked and plc_state.init_ok and nic.connected() then -- got a packet local packet = plc_comms.parse_packet(param1, param2, param3, param4, param5) if packet ~= nil then @@ -171,7 +173,9 @@ function threads.thread__main(smem, init) plc_state.degraded = true elseif networked and type == "modem" then -- we only care if this is our wireless modem - if device == plc_dev.modem then + if nic.is_modem(device) then + nic.disconnect() + println_ts("comms modem disconnected!") log.error("comms modem disconnected") @@ -199,12 +203,11 @@ function threads.thread__main(smem, init) if type == "fissionReactorLogicAdapter" then -- reconnected reactor plc_dev.reactor = device + plc_state.no_reactor = false println_ts("reactor reconnected.") log.info("reactor reconnected") - plc_state.no_reactor = false - -- we need to assume formed here as we cannot check in this main loop -- RPS will identify if it isn't and this will get set false later plc_state.reactor_formed = true @@ -230,14 +233,12 @@ function threads.thread__main(smem, init) if device.isWireless() then -- reconnected modem plc_dev.modem = device + plc_state.no_modem = false - if plc_state.init_ok then - plc_comms.reconnect_modem(plc_dev.modem) - end + if plc_state.init_ok then nic.connect(device) end println_ts("wireless modem reconnected.") log.info("comms modem reconnected") - plc_state.no_modem = false -- determine if we are still in a degraded state if not plc_state.no_reactor then @@ -709,9 +710,7 @@ function threads.thread__setpoint_control(smem) end -- if ramping completed or was aborted, reset last burn setpoint so that if it is requested again it will be re-attempted - if not setpoints.burn_rate_en then - last_burn_sp = 0 - end + if not setpoints.burn_rate_en then last_burn_sp = 0 end end -- check for termination request diff --git a/rtu/config.lua b/rtu/config.lua index 2279759..a0d3e68 100644 --- a/rtu/config.lua +++ b/rtu/config.lua @@ -10,6 +10,10 @@ config.RTU_CHANNEL = 16242 config.TRUSTED_RANGE = 0 -- time in seconds (>= 2) before assuming a remote device is no longer active config.COMMS_TIMEOUT = 5 +-- facility authentication key (do NOT use one of your passwords) +-- this enables verifying that messages are authentic +-- all devices on the same network must use the same key +-- config.AUTH_KEY = "SCADAfacility123" -- log path config.LOG_PATH = "/log.txt" diff --git a/rtu/dev/boilerv_rtu.lua b/rtu/dev/boilerv_rtu.lua index 46ac7c2..6815a3c 100644 --- a/rtu/dev/boilerv_rtu.lua +++ b/rtu/dev/boilerv_rtu.lua @@ -10,6 +10,7 @@ function boilerv_rtu.new(boiler) local unit = rtu.init_unit() -- disable auto fault clearing + boiler.__p_clear_fault() boiler.__p_disable_afc() -- discrete inputs -- diff --git a/rtu/dev/envd_rtu.lua b/rtu/dev/envd_rtu.lua index 2894e2c..2d576bc 100644 --- a/rtu/dev/envd_rtu.lua +++ b/rtu/dev/envd_rtu.lua @@ -10,6 +10,7 @@ function envd_rtu.new(envd) local unit = rtu.init_unit() -- disable auto fault clearing + envd.__p_clear_fault() envd.__p_disable_afc() -- discrete inputs -- diff --git a/rtu/dev/imatrix_rtu.lua b/rtu/dev/imatrix_rtu.lua index 3b72a12..a20d1a5 100644 --- a/rtu/dev/imatrix_rtu.lua +++ b/rtu/dev/imatrix_rtu.lua @@ -10,6 +10,7 @@ function imatrix_rtu.new(imatrix) local unit = rtu.init_unit() -- disable auto fault clearing + imatrix.__p_clear_fault() imatrix.__p_disable_afc() -- discrete inputs -- diff --git a/rtu/dev/sna_rtu.lua b/rtu/dev/sna_rtu.lua index 16c0cfd..a3e678e 100644 --- a/rtu/dev/sna_rtu.lua +++ b/rtu/dev/sna_rtu.lua @@ -10,6 +10,7 @@ function sna_rtu.new(sna) local unit = rtu.init_unit() -- disable auto fault clearing + sna.__p_clear_fault() sna.__p_disable_afc() -- discrete inputs -- diff --git a/rtu/dev/sps_rtu.lua b/rtu/dev/sps_rtu.lua index 349550c..428f7bb 100644 --- a/rtu/dev/sps_rtu.lua +++ b/rtu/dev/sps_rtu.lua @@ -10,6 +10,7 @@ function sps_rtu.new(sps) local unit = rtu.init_unit() -- disable auto fault clearing + sps.__p_clear_fault() sps.__p_disable_afc() -- discrete inputs -- diff --git a/rtu/dev/turbinev_rtu.lua b/rtu/dev/turbinev_rtu.lua index 4f2ee48..f8d6d5e 100644 --- a/rtu/dev/turbinev_rtu.lua +++ b/rtu/dev/turbinev_rtu.lua @@ -10,6 +10,7 @@ function turbinev_rtu.new(turbine) local unit = rtu.init_unit() -- disable auto fault clearing + turbine.__p_clear_fault() turbine.__p_disable_afc() -- discrete inputs -- diff --git a/rtu/rtu.lua b/rtu/rtu.lua index 831e231..3832986 100644 --- a/rtu/rtu.lua +++ b/rtu/rtu.lua @@ -158,12 +158,12 @@ end -- RTU Communications ---@nodiscard ---@param version string RTU version ----@param modem table modem device +---@param nic nic network interface device ---@param rtu_channel integer PLC comms channel ---@param svr_channel integer supervisor server channel ---@param range integer trusted device connection range ---@param conn_watchdog watchdog watchdog reference -function rtu.comms(version, modem, rtu_channel, svr_channel, range, conn_watchdog) +function rtu.comms(version, nic, rtu_channel, svr_channel, range, conn_watchdog) local self = { sv_addr = comms.BROADCAST, seq_num = 0, @@ -179,12 +179,8 @@ function rtu.comms(version, modem, rtu_channel, svr_channel, range, conn_watchdo -- PRIVATE FUNCTIONS -- -- configure modem channels - local function _conf_channels() - modem.closeAll() - modem.open(rtu_channel) - end - - _conf_channels() + nic.closeAll() + nic.open(rtu_channel) -- send a scada management packet ---@param msg_type SCADA_MGMT_TYPE @@ -196,7 +192,7 @@ function rtu.comms(version, modem, rtu_channel, svr_channel, range, conn_watchdo m_pkt.make(msg_type, msg) s_pkt.make(self.sv_addr, self.seq_num, PROTOCOL.SCADA_MGMT, m_pkt.raw_sendable()) - modem.transmit(svr_channel, rtu_channel, s_pkt.raw_sendable()) + nic.transmit(svr_channel, rtu_channel, s_pkt) self.seq_num = self.seq_num + 1 end @@ -240,17 +236,10 @@ function rtu.comms(version, modem, rtu_channel, svr_channel, range, conn_watchdo function public.send_modbus(m_pkt) local s_pkt = comms.scada_packet() s_pkt.make(self.sv_addr, self.seq_num, PROTOCOL.MODBUS_TCP, m_pkt.raw_sendable()) - modem.transmit(svr_channel, rtu_channel, s_pkt.raw_sendable()) + nic.transmit(svr_channel, rtu_channel, s_pkt) self.seq_num = self.seq_num + 1 end - -- reconnect a newly connected modem - ---@param new_modem table - function public.reconnect_modem(new_modem) - modem = new_modem - _conf_channels() - end - -- unlink from the server ---@param rtu_state rtu_state function public.unlink(rtu_state) @@ -295,13 +284,10 @@ function rtu.comms(version, modem, rtu_channel, svr_channel, range, conn_watchdo ---@param distance integer ---@return modbus_frame|mgmt_frame|nil packet function public.parse_packet(side, sender, reply_to, message, distance) + local s_pkt = nic.receive(side, sender, reply_to, message, distance) local pkt = nil - local s_pkt = comms.scada_packet() - -- parse packet as generic SCADA packet - s_pkt.receive(side, sender, reply_to, message, distance) - - if s_pkt.is_valid() then + if s_pkt then -- get as MODBUS TCP packet if s_pkt.protocol() == PROTOCOL.MODBUS_TCP then local m_pkt = comms.modbus_packet() diff --git a/rtu/startup.lua b/rtu/startup.lua index 769e4d7..9eecace 100644 --- a/rtu/startup.lua +++ b/rtu/startup.lua @@ -8,6 +8,7 @@ local comms = require("scada-common.comms") local crash = require("scada-common.crash") local log = require("scada-common.log") local mqueue = require("scada-common.mqueue") +local network = require("scada-common.network") local ppm = require("scada-common.ppm") local rsio = require("scada-common.rsio") local types = require("scada-common.types") @@ -28,7 +29,7 @@ local sna_rtu = require("rtu.dev.sna_rtu") local sps_rtu = require("rtu.dev.sps_rtu") local turbinev_rtu = require("rtu.dev.turbinev_rtu") -local RTU_VERSION = "v1.3.6" +local RTU_VERSION = "v1.4.0" local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE local RTU_UNIT_HW_STATE = databus.RTU_UNIT_HW_STATE @@ -81,6 +82,19 @@ local function main() -- mount connected devices ppm.mount_all() + -- message authentication init + if type(config.AUTH_KEY) == "string" then + network.init_mac(config.AUTH_KEY) + end + + -- get modem + local modem = ppm.get_wireless_modem() + if modem == nil then + println("boot> wireless modem not found") + log.fatal("no wireless modem on startup") + return + end + ---@class rtu_shared_memory local __shared_memory = { -- RTU system state flags @@ -91,16 +105,12 @@ local function main() shutdown = false }, - -- core RTU devices - rtu_dev = { - modem = ppm.get_wireless_modem() - }, - -- system objects rtu_sys = { + nic = network.nic(modem), rtu_comms = nil, ---@type rtu_comms conn_watchdog = nil, ---@type watchdog - units = {} ---@type table + units = {} }, -- message queues @@ -109,16 +119,8 @@ local function main() } } - local smem_dev = __shared_memory.rtu_dev local smem_sys = __shared_memory.rtu_sys - -- get modem - if smem_dev.modem == nil then - println("boot> wireless modem not found") - log.fatal("no wireless modem on startup") - return - end - databus.tx_hw_modem(true) ---------------------------------------- @@ -236,18 +238,19 @@ local function main() ---@class rtu_unit_registry_entry local unit = { - uid = 0, ---@type integer - name = "redstone_io", ---@type string - type = RTU_UNIT_TYPE.REDSTONE, ---@type RTU_UNIT_TYPE - index = entry_idx, ---@type integer - reactor = io_reactor, ---@type integer - device = capabilities, ---@type table use device field for redstone ports - is_multiblock = false, ---@type boolean - formed = nil, ---@type boolean|nil - rtu = rs_rtu, ---@type rtu_device|rtu_rs_device + uid = 0, ---@type integer + name = "redstone_io", ---@type string + type = RTU_UNIT_TYPE.REDSTONE, ---@type RTU_UNIT_TYPE + index = entry_idx, ---@type integer + reactor = io_reactor, ---@type integer + device = capabilities, ---@type table use device field for redstone ports + is_multiblock = false, ---@type boolean + formed = nil, ---@type boolean|nil + hw_state = RTU_UNIT_HW_STATE.OK, ---@type RTU_UNIT_HW_STATE + rtu = rs_rtu, ---@type rtu_device|rtu_rs_device modbus_io = modbus.new(rs_rtu, false), - pkt_queue = nil, ---@type mqueue|nil - thread = nil ---@type parallel_thread|nil + pkt_queue = nil, ---@type mqueue|nil + thread = nil ---@type parallel_thread|nil } table.insert(units, unit) @@ -261,7 +264,7 @@ local function main() unit.uid = #units - databus.tx_unit_hw_status(unit.uid, RTU_UNIT_HW_STATE.OK) + databus.tx_unit_hw_status(unit.uid, unit.hw_state) end end @@ -403,6 +406,7 @@ local function main() device = device, ---@type table is_multiblock = is_multiblock, ---@type boolean formed = formed, ---@type boolean|nil + hw_state = RTU_UNIT_HW_STATE.OFFLINE, ---@type RTU_UNIT_HW_STATE rtu = rtu_iface, ---@type rtu_device|rtu_rs_device modbus_io = modbus.new(rtu_iface, true), pkt_queue = mqueue.new(), ---@type mqueue|nil @@ -422,19 +426,21 @@ local function main() rtu_unit.uid = #units - -- report hardware status + -- determine hardware status if rtu_unit.type == RTU_UNIT_TYPE.VIRTUAL then - databus.tx_unit_hw_status(rtu_unit.uid, RTU_UNIT_HW_STATE.OFFLINE) + rtu_unit.hw_state = RTU_UNIT_HW_STATE.OFFLINE else if rtu_unit.is_multiblock then - databus.tx_unit_hw_status(rtu_unit.uid, util.trinary(rtu_unit.formed == true, RTU_UNIT_HW_STATE.OK, RTU_UNIT_HW_STATE.UNFORMED)) + rtu_unit.hw_state = util.trinary(rtu_unit.formed == true, RTU_UNIT_HW_STATE.OK, RTU_UNIT_HW_STATE.UNFORMED) elseif faulted then - databus.tx_unit_hw_status(rtu_unit.uid, RTU_UNIT_HW_STATE.FAULTED) + rtu_unit.hw_state = RTU_UNIT_HW_STATE.FAULTED else - databus.tx_unit_hw_status(rtu_unit.uid, RTU_UNIT_HW_STATE.OK) + rtu_unit.hw_state = RTU_UNIT_HW_STATE.OK end end + -- report hardware status + databus.tx_unit_hw_status(rtu_unit.uid, rtu_unit.hw_state) end -- we made it through all that trusting-user-to-write-a-config-file chaos @@ -467,7 +473,7 @@ local function main() log.debug("startup> conn watchdog started") -- setup comms - smem_sys.rtu_comms = rtu.comms(RTU_VERSION, smem_dev.modem, config.RTU_CHANNEL, config.SVR_CHANNEL, + smem_sys.rtu_comms = rtu.comms(RTU_VERSION, smem_sys.nic, config.RTU_CHANNEL, config.SVR_CHANNEL, config.TRUSTED_RANGE, smem_sys.conn_watchdog) log.debug("startup> comms init") diff --git a/rtu/threads.lua b/rtu/threads.lua index fdb82b3..c27052d 100644 --- a/rtu/threads.lua +++ b/rtu/threads.lua @@ -46,7 +46,7 @@ function threads.thread__main(smem) -- load in from shared memory local rtu_state = smem.rtu_state - local rtu_dev = smem.rtu_dev + local nic = smem.rtu_sys.nic local rtu_comms = smem.rtu_sys.rtu_comms local conn_watchdog = smem.rtu_sys.conn_watchdog local units = smem.rtu_sys.units @@ -93,7 +93,9 @@ function threads.thread__main(smem) if type ~= nil and device ~= nil then if type == "modem" then -- we only care if this is our wireless modem - if device == rtu_dev.modem then + if nic.is_modem(device) then + nic.disconnect() + println_ts("wireless modem disconnected!") log.warning("comms modem disconnected!") @@ -105,13 +107,15 @@ function threads.thread__main(smem) for i = 1, #units do -- find disconnected device if units[i].device == device then - -- we are going to let the PPM prevent crashes - -- return fault flags/codes to MODBUS queries + -- will let the PPM prevent crashes, which will indicate failures in MODBUS queries local unit = units[i] ---@type rtu_unit_registry_entry local type_name = types.rtu_type_to_string(unit.type) + println_ts(util.c("lost the ", type_name, " on interface ", unit.name)) log.warning(util.c("lost the ", type_name, " unit peripheral on interface ", unit.name)) - databus.tx_unit_hw_status(unit.uid, UNIT_HW_STATE.OFFLINE) + + unit.hw_state = UNIT_HW_STATE.OFFLINE + databus.tx_unit_hw_status(unit.uid, unit.hw_state) break end end @@ -125,8 +129,7 @@ function threads.thread__main(smem) if type == "modem" then if device.isWireless() then -- reconnected modem - rtu_dev.modem = device - rtu_comms.reconnect_modem(rtu_dev.modem) + nic.connect(device) println_ts("wireless modem reconnected.") log.info("comms modem reconnected") @@ -144,6 +147,8 @@ function threads.thread__main(smem) -- note: cannot check isFormed as that would yield this coroutine and consume events if unit.name == param1 then local resend_advert = false + local faulted = false + local unknown = false -- found, re-link unit.device = device @@ -177,57 +182,58 @@ function threads.thread__main(smem) end if unit.type == RTU_UNIT_TYPE.BOILER_VALVE then - unit.rtu = boilerv_rtu.new(device) + unit.rtu, faulted = boilerv_rtu.new(device) -- if not formed, indexing the multiblock functions would have resulted in a PPM fault - unit.formed = util.trinary(device.__p_is_faulted(), false, nil) - databus.tx_unit_hw_status(unit.uid, UNIT_HW_STATE.UNFORMED) + unit.formed = util.trinary(faulted, false, nil) elseif unit.type == RTU_UNIT_TYPE.TURBINE_VALVE then - unit.rtu = turbinev_rtu.new(device) + unit.rtu, faulted = turbinev_rtu.new(device) -- if not formed, indexing the multiblock functions would have resulted in a PPM fault - unit.formed = util.trinary(device.__p_is_faulted(), false, nil) - databus.tx_unit_hw_status(unit.uid, UNIT_HW_STATE.UNFORMED) + unit.formed = util.trinary(faulted, false, nil) elseif unit.type == RTU_UNIT_TYPE.IMATRIX then - unit.rtu = imatrix_rtu.new(device) + unit.rtu, faulted = imatrix_rtu.new(device) -- if not formed, indexing the multiblock functions would have resulted in a PPM fault - unit.formed = util.trinary(device.__p_is_faulted(), false, nil) - databus.tx_unit_hw_status(unit.uid, UNIT_HW_STATE.UNFORMED) + unit.formed = util.trinary(faulted, false, nil) elseif unit.type == RTU_UNIT_TYPE.SPS then - unit.rtu = sps_rtu.new(device) + unit.rtu, faulted = sps_rtu.new(device) -- if not formed, indexing the multiblock functions would have resulted in a PPM fault - unit.formed = util.trinary(device.__p_is_faulted(), false, nil) - databus.tx_unit_hw_status(unit.uid, UNIT_HW_STATE.UNFORMED) + unit.formed = util.trinary(faulted, false, nil) elseif unit.type == RTU_UNIT_TYPE.SNA then - unit.rtu = sna_rtu.new(device) - databus.tx_unit_hw_status(unit.uid, UNIT_HW_STATE.OK) + unit.rtu, faulted = sna_rtu.new(device) elseif unit.type == RTU_UNIT_TYPE.ENV_DETECTOR then - unit.rtu = envd_rtu.new(device) - databus.tx_unit_hw_status(unit.uid, UNIT_HW_STATE.OK) + unit.rtu, faulted = envd_rtu.new(device) else + unknown = true log.error(util.c("failed to identify reconnected RTU unit type (", unit.name, ")"), true) end if unit.is_multiblock then - if (unit.formed == false) then + unit.hw_state = UNIT_HW_STATE.UNFORMED + if unit.formed == false then log.info(util.c("assuming ", unit.name, " is not formed due to PPM faults while initializing")) - databus.tx_unit_hw_status(unit.uid, UNIT_HW_STATE.UNFORMED) end - elseif device.__p_is_faulted() then - databus.tx_unit_hw_status(unit.uid, UNIT_HW_STATE.FAULTED) + elseif faulted then + unit.hw_state = UNIT_HW_STATE.FAULTED + elseif not unknown then + unit.hw_state = UNIT_HW_STATE.OK else - databus.tx_unit_hw_status(unit.uid, UNIT_HW_STATE.OK) + unit.hw_state = UNIT_HW_STATE.OFFLINE end - unit.modbus_io = modbus.new(unit.rtu, true) + databus.tx_unit_hw_status(unit.uid, unit.hw_state) - local type_name = types.rtu_type_to_string(unit.type) - local message = util.c("reconnected the ", type_name, " on interface ", unit.name) - println_ts(message) - log.info(message) + if not unknown then + unit.modbus_io = modbus.new(unit.rtu, true) - if resend_advert then - rtu_comms.send_advertisement(units) - else - rtu_comms.send_remounted(unit.uid) + local type_name = types.rtu_type_to_string(unit.type) + local message = util.c("reconnected the ", type_name, " on interface ", unit.name) + println_ts(message) + log.info(message) + + if resend_advert then + rtu_comms.send_advertisement(units) + else + rtu_comms.send_remounted(unit.uid) + end end end end @@ -391,13 +397,6 @@ function threads.thread__unit_comms(smem, unit) -- received a packet local _, reply = unit.modbus_io.handle_packet(msg.message) rtu_comms.send_modbus(reply) - - -- check if there was a problem and update the hardware state if so - local frame = reply.get() - if unit.formed and (bit.band(frame.func_code, types.MODBUS_FCODE.ERROR_FLAG) ~= 0) and - (frame.data[1] == types.MODBUS_EXCODE.SERVER_DEVICE_FAIL) then - databus.tx_unit_hw_status(unit.uid, UNIT_HW_STATE.FAULTED) - end end end @@ -413,12 +412,10 @@ function threads.thread__unit_comms(smem, unit) if unit.formed == nil then unit.formed = is_formed - if is_formed then databus.tx_unit_hw_status(unit.uid, UNIT_HW_STATE.OK) end + if is_formed then unit.hw_state = UNIT_HW_STATE.OK end end - if not unit.formed then - databus.tx_unit_hw_status(unit.uid, UNIT_HW_STATE.UNFORMED) - end + if not unit.formed then unit.hw_state = UNIT_HW_STATE.UNFORMED end if (not unit.formed) and is_formed then -- newly re-formed @@ -463,11 +460,11 @@ function threads.thread__unit_comms(smem, unit) if unit.formed and faulted then -- something is still wrong = can't mark as formed yet unit.formed = false + unit.hw_state = UNIT_HW_STATE.UNFORMED log.info(util.c("assuming ", unit.name, " is not formed due to PPM faults while initializing")) - databus.tx_unit_hw_status(unit.uid, UNIT_HW_STATE.UNFORMED) else + unit.hw_state = UNIT_HW_STATE.OK rtu_comms.send_remounted(unit.uid) - databus.tx_unit_hw_status(unit.uid, UNIT_HW_STATE.OK) end local type_name = types.rtu_type_to_string(unit.type) @@ -484,6 +481,16 @@ function threads.thread__unit_comms(smem, unit) unit.formed = is_formed end + -- check hardware status + if unit.device.__p_is_healthy() then + if unit.hw_state == UNIT_HW_STATE.FAULTED then unit.hw_state = UNIT_HW_STATE.OK end + else + if unit.hw_state == UNIT_HW_STATE.OK then unit.hw_state = UNIT_HW_STATE.FAULTED end + end + + -- update hw status + databus.tx_unit_hw_status(unit.uid, unit.hw_state) + -- check for termination request if rtu_state.shutdown then log.info("rtu unit thread exiting -> " .. short_name) diff --git a/scada-common/comms.lua b/scada-common/comms.lua index c5e9fd2..88e8631 100644 --- a/scada-common/comms.lua +++ b/scada-common/comms.lua @@ -7,14 +7,14 @@ local log = require("scada-common.log") local insert = table.insert ---@diagnostic disable-next-line: undefined-field -local C_ID = os.getComputerID() ---@type integer computer ID +local COMPUTER_ID = os.getComputerID() ---@type integer computer ID -local max_distance = nil ---@type number|nil maximum acceptable transmission distance +local max_distance = nil ---@type number|nil maximum acceptable transmission distance ---@class comms local comms = {} -comms.version = "2.0.0" +comms.version = "2.1.0" ---@enum PROTOCOL local PROTOCOL = { @@ -163,8 +163,7 @@ function comms.scada_packet() ---@param payload table function public.make(dest_addr, seq_num, protocol, payload) self.valid = true ----@diagnostic disable-next-line: undefined-field - self.src_addr = C_ID + self.src_addr = COMPUTER_ID self.dest_addr = dest_addr self.seq_num = seq_num self.protocol = protocol @@ -219,10 +218,14 @@ function comms.scada_packet() end -- check if this packet is destined for this device - local is_destination = (self.dest_addr == comms.BROADCAST) or (self.dest_addr == C_ID) + local is_destination = (self.dest_addr == comms.BROADCAST) or (self.dest_addr == COMPUTER_ID) - self.valid = is_destination and type(self.src_addr) == "number" and type(self.dest_addr) == "number" and - type(self.seq_num) == "number" and type(self.protocol) == "number" and type(self.payload) == "table" + self.valid = is_destination and + type(self.src_addr) == "number" and + type(self.dest_addr) == "number" and + type(self.seq_num) == "number" and + type(self.protocol) == "number" and + type(self.payload) == "table" end end @@ -260,6 +263,112 @@ function comms.scada_packet() return public end +-- authenticated SCADA packet object +---@nodiscard +function comms.authd_packet() + local self = { + modem_msg_in = nil, ---@type modem_message|nil + valid = false, + raw = {}, + src_addr = comms.BROADCAST, + dest_addr = comms.BROADCAST, + mac = "", + payload = "" + } + + ---@class authd_packet + local public = {} + + -- make an authenticated SCADA packet + ---@param s_packet scada_packet scada packet to authenticate + ---@param mac function message authentication function + function public.make(s_packet, mac) + self.valid = true + self.src_addr = s_packet.src_addr() + self.dest_addr = s_packet.dest_addr() + self.payload = textutils.serialize(s_packet.raw_sendable(), { allow_repetitions = true, compact = true }) + self.mac = mac(self.payload) + self.raw = { self.src_addr, self.dest_addr, self.mac, self.payload } + end + + -- parse in a modem message as an authenticated SCADA packet + ---@param side string modem side + ---@param sender integer sender channel + ---@param reply_to integer reply channel + ---@param message any message body + ---@param distance integer transmission distance + ---@return boolean valid valid message received + function public.receive(side, sender, reply_to, message, distance) + ---@class modem_message + self.modem_msg_in = { + iface = side, + s_channel = sender, + r_channel = reply_to, + msg = message, + dist = distance + } + + self.valid = false + self.raw = self.modem_msg_in.msg + + if (type(max_distance) == "number") and (distance > max_distance) then + -- outside of maximum allowable transmission distance + -- log.debug("comms.authd_packet.receive(): discarding packet with distance " .. distance .. " outside of trusted range") + else + if type(self.raw) == "table" then + if #self.raw == 4 then + self.src_addr = self.raw[1] + self.dest_addr = self.raw[2] + self.mac = self.raw[3] + self.payload = self.raw[4] + else + self.src_addr = nil + self.dest_addr = nil + self.mac = "" + self.payload = "" + end + + -- check if this packet is destined for this device + local is_destination = (self.dest_addr == comms.BROADCAST) or (self.dest_addr == COMPUTER_ID) + + self.valid = is_destination and + type(self.src_addr) == "number" and + type(self.dest_addr) == "number" and + type(self.mac) == "string" and + type(self.payload) == "string" + end + end + + return self.valid + end + + -- public accessors -- + + ---@nodiscard + function public.modem_event() return self.modem_msg_in end + ---@nodiscard + function public.raw_sendable() return self.raw end + + ---@nodiscard + function public.local_channel() return self.modem_msg_in.s_channel end + ---@nodiscard + function public.remote_channel() return self.modem_msg_in.r_channel end + + ---@nodiscard + function public.is_valid() return self.valid end + + ---@nodiscard + function public.src_addr() return self.src_addr end + ---@nodiscard + function public.dest_addr() return self.dest_addr end + ---@nodiscard + function public.mac() return self.mac end + ---@nodiscard + function public.data() return self.payload end + + return public +end + -- MODBUS packet
-- modeled after MODBUS TCP packet ---@nodiscard diff --git a/scada-common/crash.lua b/scada-common/crash.lua index 8d02728..45a3874 100644 --- a/scada-common/crash.lua +++ b/scada-common/crash.lua @@ -6,6 +6,8 @@ local comms = require("scada-common.comms") local log = require("scada-common.log") local util = require("scada-common.util") +local core = require("graphics.core") + local crash = {} local app = "unknown" @@ -32,6 +34,7 @@ function crash.handler(error) log.info(util.c("APPLICATION: ", app)) log.info(util.c("FIRMWARE VERSION: ", ver)) log.info(util.c("COMMS VERSION: ", comms.version)) + log.info(util.c("GRAPHICS VERSION: ", core.version)) log.info("----------------------------------") log.info(debug.traceback("--- begin debug trace ---", 1)) log.info("--- end debug trace ---") diff --git a/scada-common/crypto.lua b/scada-common/crypto.lua deleted file mode 100644 index 4417ae3..0000000 --- a/scada-common/crypto.lua +++ /dev/null @@ -1,244 +0,0 @@ --- --- Cryptographic Communications Engine --- - -local aes128 = require("lockbox.cipher.aes128") -local ctr_mode = require("lockbox.cipher.mode.ctr") -local sha1 = require("lockbox.digest.sha1") -local sha2_256 = require("lockbox.digest.sha2_256") -local pbkdf2 = require("lockbox.kdf.pbkdf2") -local hmac = require("lockbox.mac.hmac") -local zero_pad = require("lockbox.padding.zero") -local stream = require("lockbox.util.stream") -local array = require("lockbox.util.array") - -local log = require("scada-common.log") -local util = require("scada-common.util") - -local crypto = {} - -local c_eng = { - key = nil, - cipher = nil, - decipher = nil, - hmac = nil -} - ----@alias hex string - --- initialize cryptographic system -function crypto.init(password, server_port) - local key_deriv = pbkdf2() - - -- setup PBKDF2 - -- the primary goal is to just turn our password into a 16 byte key - key_deriv.setPassword(password) - key_deriv.setSalt("salty_salt_at_" .. server_port) - key_deriv.setIterations(32) - key_deriv.setBlockLen(8) - key_deriv.setDKeyLen(16) - - local start = util.time() - - key_deriv.setPRF(hmac().setBlockSize(64).setDigest(sha2_256)) - key_deriv.finish() - - log.dmesg("pbkdf2: key derivation took " .. (util.time() - start) .. "ms", "CRYPTO", colors.yellow) - - c_eng.key = array.fromHex(key_deriv.asHex()) - - -- initialize cipher - c_eng.cipher = ctr_mode.Cipher() - c_eng.cipher.setKey(c_eng.key) - c_eng.cipher.setBlockCipher(aes128) - c_eng.cipher.setPadding(zero_pad) - - -- initialize decipher - c_eng.decipher = ctr_mode.Decipher() - c_eng.decipher.setKey(c_eng.key) - c_eng.decipher.setBlockCipher(aes128) - c_eng.decipher.setPadding(zero_pad) - - -- initialize HMAC - c_eng.hmac = hmac() - c_eng.hmac.setBlockSize(64) - c_eng.hmac.setDigest(sha1) - c_eng.hmac.setKey(c_eng.key) - - log.dmesg("init: completed in " .. (util.time() - start) .. "ms", "CRYPTO", colors.yellow) -end - --- encrypt plaintext ----@nodiscard ----@param plaintext string ----@return table initial_value, string ciphertext -function crypto.encrypt(plaintext) - local start = util.time() - - -- initial value - local iv = { - math.random(0, 255), - math.random(0, 255), - math.random(0, 255), - math.random(0, 255), - math.random(0, 255), - math.random(0, 255), - math.random(0, 255), - math.random(0, 255), - math.random(0, 255), - math.random(0, 255), - math.random(0, 255), - math.random(0, 255), - math.random(0, 255), - math.random(0, 255), - math.random(0, 255), - math.random(0, 255) - } - - log.debug("crypto.random: iv random took " .. (util.time() - start) .. "ms") - - start = util.time() - - c_eng.cipher.init() - c_eng.cipher.update(stream.fromArray(iv)) - c_eng.cipher.update(stream.fromString(plaintext)) - c_eng.cipher.finish() - - local ciphertext = c_eng.cipher.asHex() ---@type hex - - log.debug("crypto.encrypt: aes128-ctr-mode took " .. (util.time() - start) .. "ms") - log.debug("ciphertext: " .. util.strval(ciphertext)) - - return iv, ciphertext -end - --- decrypt ciphertext ----@nodiscard ----@param iv string CTR initial value ----@param ciphertext string ciphertext hex ----@return string plaintext -function crypto.decrypt(iv, ciphertext) - local start = util.time() - - c_eng.decipher.init() - c_eng.decipher.update(stream.fromArray(iv)) - c_eng.decipher.update(stream.fromHex(ciphertext)) - c_eng.decipher.finish() - - local plaintext_hex = c_eng.decipher.asHex() ---@type hex - - local plaintext = stream.toString(stream.fromHex(plaintext_hex)) - - log.debug("crypto.decrypt: aes128-ctr-mode took " .. (util.time() - start) .. "ms") - log.debug("plaintext: " .. util.strval(plaintext)) - - return plaintext -end - --- generate HMAC of message ----@nodiscard ----@param message_hex string initial value concatenated with ciphertext -function crypto.hmac(message_hex) - local start = util.time() - - c_eng.hmac.init() - c_eng.hmac.update(stream.fromHex(message_hex)) - c_eng.hmac.finish() - - local hash = c_eng.hmac.asHex() ---@type hex - - log.debug("crypto.hmac: hmac-sha1 took " .. (util.time() - start) .. "ms") - log.debug("hmac: " .. util.strval(hash)) - - return hash -end - --- wrap a modem as a secure modem to send encrypted traffic ----@param modem table modem to wrap -function crypto.secure_modem(modem) - ---@class secure_modem - ---@field open function - ---@field isOpen function - ---@field close function - ---@field closeAll function - ---@field isWireless function - ---@field getNamesRemote function - ---@field isPresentRemote function - ---@field getTypeRemote function - ---@field hasTypeRemote function - ---@field getMethodsRemote function - ---@field callRemote function - ---@field getNameLocal function - local public = {} - - -- wrap a modem - ---@param reconnected_modem table ----@diagnostic disable-next-line: redefined-local - function public.wrap(reconnected_modem) - modem = reconnected_modem - for key, func in pairs(modem) do - public[key] = func - end - end - - -- wrap modem functions, then we replace transmit - public.wrap(modem) - - -- send a packet with encryption - ---@param channel integer - ---@param reply_channel integer - ---@param payload table packet raw_sendable - function public.transmit(channel, reply_channel, payload) - local plaintext = textutils.serialize(payload, { allow_repetitions = true, compact = true }) - - local iv, ciphertext = crypto.encrypt(plaintext) ----@diagnostic disable-next-line: redefined-local - local computed_hmac = crypto.hmac(iv .. ciphertext) - - modem.transmit(channel, reply_channel, { computed_hmac, iv, ciphertext }) - end - - -- parse in a modem message as a network packet - ---@nodiscard - ---@param side string modem side - ---@param sender integer sender port - ---@param reply_to integer reply port - ---@param message any encrypted packet sent with secure_modem.transmit - ---@param distance integer transmission distance - ---@return string side, integer sender, integer reply_to, any plaintext_message, integer distance - function public.receive(side, sender, reply_to, message, distance) - local body = "" - - if type(message) == "table" then - if #message == 3 then ----@diagnostic disable-next-line: redefined-local - local rx_hmac = message[1] - local iv = message[2] - local ciphertext = message[3] - - local computed_hmac = crypto.hmac(iv .. ciphertext) - - if rx_hmac == computed_hmac then - -- message intact - local plaintext = crypto.decrypt(iv, ciphertext) - body = textutils.unserialize(plaintext) - - if body == nil then - -- failed decryption - log.debug("crypto.secure_modem: decryption failed") - body = "" - end - else - -- something went wrong - log.debug("crypto.secure_modem: hmac mismatch violation") - end - end - end - - return side, sender, reply_to, body, distance - end - - return public -end - -return crypto diff --git a/scada-common/network.lua b/scada-common/network.lua new file mode 100644 index 0000000..dbb3e75 --- /dev/null +++ b/scada-common/network.lua @@ -0,0 +1,233 @@ +-- +-- Network Communications +-- + +local md5 = require("lockbox.digest.md5") +local sha256 = require("lockbox.digest.sha2_256") +local pbkdf2 = require("lockbox.kdf.pbkdf2") +local hmac = require("lockbox.mac.hmac") +local stream = require("lockbox.util.stream") +local array = require("lockbox.util.array") +local comms = require("scada-common.comms") + +local log = require("scada-common.log") +local util = require("scada-common.util") + +local network = {} + +local c_eng = { + key = nil, + hmac = nil +} + +-- initialize message authentication system +---@param passkey string facility passkey +---@return integer init_time milliseconds init took +function network.init_mac(passkey) + local start = util.time_ms() + + local key_deriv = pbkdf2() + + -- setup PBKDF2 + key_deriv.setPassword(passkey) + key_deriv.setSalt("pepper") + key_deriv.setIterations(32) + key_deriv.setBlockLen(8) + key_deriv.setDKeyLen(16) + key_deriv.setPRF(hmac().setBlockSize(64).setDigest(sha256)) + key_deriv.finish() + + c_eng.key = array.fromHex(key_deriv.asHex()) + + -- initialize HMAC + c_eng.hmac = hmac() + c_eng.hmac.setBlockSize(64) + c_eng.hmac.setDigest(md5) + c_eng.hmac.setKey(c_eng.key) + + local init_time = util.time_ms() - start + log.info("network.init_mac completed in " .. init_time .. "ms") + + return init_time +end + +-- generate HMAC of message +---@nodiscard +---@param message string initial value concatenated with ciphertext +local function compute_hmac(message) + -- local start = util.time_ms() + + c_eng.hmac.init() + c_eng.hmac.update(stream.fromString(message)) + c_eng.hmac.finish() + + local hash = c_eng.hmac.asHex() + + -- log.debug("compute_hmac(): hmac-md5 = " .. util.strval(hash) .. " (took " .. (util.time_ms() - start) .. "ms)") + + return hash +end + +-- NIC: Network Interface Controller
+-- utilizes HMAC-MD5 for message authentication, if enabled +---@param modem table modem to use +function network.nic(modem) + local self = { + connected = true, -- used to avoid costly MAC calculations if modem isn't even present + channels = {} + } + + ---@class nic + ---@field open function + ---@field isOpen function + ---@field close function + ---@field closeAll function + ---@field isWireless function + ---@field getNameLocal function + ---@field getNamesRemote function + ---@field isPresentRemote function + ---@field getTypeRemote function + ---@field hasTypeRemote function + ---@field getMethodsRemote function + ---@field callRemote function + local public = {} + + -- check if this NIC has a connected modem + ---@nodiscard + function public.connected() return self.connected end + + -- connect to a modem peripheral + ---@param reconnected_modem table + function public.connect(reconnected_modem) + modem = reconnected_modem + self.connected = true + + -- open previously opened channels + for _, channel in ipairs(self.channels) do + modem.open(channel) + end + + -- link all public functions except for transmit + for key, func in pairs(modem) do + if key ~= "transmit" and key ~= "open" and key ~= "close" and key ~= "closeAll" then public[key] = func end + end + end + + -- flag this NIC as no longer having a connected modem (usually do to peripheral disconnect) + function public.disconnect() self.connected = false end + + -- check if a peripheral is this modem + ---@nodiscard + ---@param device table + function public.is_modem(device) return device == modem end + + -- wrap modem functions, then create custom functions + public.connect(modem) + + -- open a channel on the modem
+ -- if disconnected *after* opening, previousy opened channels will be re-opened on reconnection + ---@param channel integer + function public.open(channel) + modem.open(channel) + + local already_open = false + for i = 1, #self.channels do + if self.channels[i] == channel then + already_open = true + break + end + end + + if not already_open then + table.insert(self.channels, channel) + end + end + + -- close a channel on the modem + ---@param channel integer + function public.close(channel) + modem.close(channel) + + for i = 1, #self.channels do + if self.channels[i] == channel then + table.remove(self.channels, i) + return + end + end + end + + -- close all channels on the modem + function public.closeAll() + modem.closeAll() + self.channels = {} + end + + -- send a packet, with message authentication if configured + ---@param dest_channel integer destination channel + ---@param local_channel integer local channel + ---@param packet scada_packet packet + function public.transmit(dest_channel, local_channel, packet) + if self.connected then + local tx_packet = packet ---@type authd_packet|scada_packet + + if c_eng.hmac ~= nil then + -- local start = util.time_ms() + tx_packet = comms.authd_packet() + + ---@cast tx_packet authd_packet + tx_packet.make(packet, compute_hmac) + + -- log.debug("crypto.modem.transmit: data processing took " .. (util.time_ms() - start) .. "ms") + end + + modem.transmit(dest_channel, local_channel, tx_packet.raw_sendable()) + end + end + + -- parse in a modem message as a network packet + ---@nodiscard + ---@param side string modem side + ---@param sender integer sender channel + ---@param reply_to integer reply channel + ---@param message any packet sent with or without message authentication + ---@param distance integer transmission distance + ---@return scada_packet|nil packet received packet if valid and passed authentication check + function public.receive(side, sender, reply_to, message, distance) + local packet = nil + + if self.connected then + local s_packet = comms.scada_packet() + + if c_eng.hmac ~= nil then + -- parse packet as an authenticated SCADA packet + local a_packet = comms.authd_packet() + a_packet.receive(side, sender, reply_to, message, distance) + + if a_packet.is_valid() then + -- local start = util.time_ms() + local packet_hmac = a_packet.mac() + local msg = a_packet.data() + local computed_hmac = compute_hmac(msg) + + if packet_hmac == computed_hmac then + -- log.debug("crypto.modem.receive: HMAC verified in " .. (util.time_ms() - start) .. "ms") + s_packet.receive(side, sender, reply_to, textutils.unserialize(msg), distance) + else + -- log.debug("crypto.modem.receive: HMAC failed verification in " .. (util.time_ms() - start) .. "ms") + end + end + else + -- parse packet as a generic SCADA packet + s_packet.receive(side, sender, reply_to, message, distance) + end + + if s_packet.is_valid() then packet = s_packet end + end + + return packet + end + + return public +end + +return network diff --git a/scada-common/ppm.lua b/scada-common/ppm.lua index fe9e026..90f0b17 100644 --- a/scada-common/ppm.lua +++ b/scada-common/ppm.lua @@ -101,22 +101,31 @@ local function peri_init(iface) end end - -- fault management functions + -- fault management & monitoring functions local function clear_fault() self.faulted = false end local function get_last_fault() return self.last_fault end local function is_faulted() return self.faulted end local function is_ok() return not self.faulted end + -- check if a peripheral has any faulted functions
+ -- contrasted with is_faulted() and is_ok() as those only check if the last operation failed, + -- unless auto fault clearing is disabled, at which point faults become sticky faults + local function is_healthy() + for _, v in pairs(self.fault_counts) do if v > 0 then return false end end + return true + end + local function enable_afc() self.auto_cf = true end local function disable_afc() self.auto_cf = false end - -- append to device functions + -- append PPM functions to device functions self.device.__p_clear_fault = clear_fault self.device.__p_last_fault = get_last_fault self.device.__p_is_faulted = is_faulted self.device.__p_is_ok = is_ok + self.device.__p_is_healthy = is_healthy self.device.__p_enable_afc = enable_afc self.device.__p_disable_afc = disable_afc @@ -156,7 +165,7 @@ local function peri_init(iface) return { type = self.type, - dev = self.device + dev = self.device } end diff --git a/supervisor/config.lua b/supervisor/config.lua index a4a595b..2373f0d 100644 --- a/supervisor/config.lua +++ b/supervisor/config.lua @@ -17,6 +17,10 @@ config.PLC_TIMEOUT = 5 config.RTU_TIMEOUT = 5 config.CRD_TIMEOUT = 5 config.PKT_TIMEOUT = 5 +-- facility authentication key (do NOT use one of your passwords) +-- this enables verifying that messages are authentic +-- all devices on the same network must use the same key +-- config.AUTH_KEY = "SCADAfacility123" -- expected number of reactors config.NUM_REACTORS = 4 diff --git a/supervisor/facility.lua b/supervisor/facility.lua index e3b8059..5864ea5 100644 --- a/supervisor/facility.lua +++ b/supervisor/facility.lua @@ -75,7 +75,7 @@ function facility.new(num_reactors, cooling_conf) burn_target = 0.1, -- burn rate target for aggregate burn mode charge_setpoint = 0, -- FE charge target setpoint gen_rate_setpoint = 0, -- FE/t charge rate target setpoint - group_map = { 0, 0, 0, 0 }, -- units -> group IDs + group_map = {}, -- units -> group IDs prio_defs = { {}, {}, {}, {} }, -- priority definitions (each level is a table of units) at_max_burn = false, ascram = false, @@ -109,6 +109,7 @@ function facility.new(num_reactors, cooling_conf) -- create units for i = 1, num_reactors do table.insert(self.units, unit.new(i, cooling_conf[i].BOILERS, cooling_conf[i].TURBINES)) + table.insert(self.group_map, 0) end -- init redstone RTU I/O controller @@ -790,7 +791,7 @@ function facility.new(num_reactors, cooling_conf) ---@param unit_id integer unit ID ---@param group integer group ID or 0 for independent function public.set_group(unit_id, group) - if group >= 0 and group <= 4 and self.mode == PROCESS.INACTIVE then + if (group >= 0 and group <= 4) and (unit_id > 0 and unit_id <= num_reactors) and self.mode == PROCESS.INACTIVE then -- remove from old group if previously assigned local old_group = self.group_map[unit_id] if old_group ~= 0 then diff --git a/supervisor/session/rtu.lua b/supervisor/session/rtu.lua index d1fbaec..69ba796 100644 --- a/supervisor/session/rtu.lua +++ b/supervisor/session/rtu.lua @@ -120,7 +120,7 @@ function rtu.new_session(id, s_addr, in_queue, out_queue, timeout, advertisement if u_type == false then -- validation fail - log.debug(log_header .. "advertisement unit validation failure") + log.debug(log_header .. "_handle_advertisement(): advertisement unit validation failure") else if unit_advert.reactor > 0 then local target_unit = self.fac_units[unit_advert.reactor] ---@type reactor_unit @@ -146,7 +146,7 @@ function rtu.new_session(id, s_addr, in_queue, out_queue, timeout, advertisement -- skip virtual units log.debug(util.c(log_header, "skipping virtual RTU unit #", i)) else - log.error(util.c(log_header, "bad advertisement: encountered unsupported reactor-specific RTU type ", type_string)) + log.warning(util.c(log_header, "_handle_advertisement(): encountered unsupported reactor-specific RTU type ", type_string)) end else -- facility RTUs @@ -172,7 +172,7 @@ function rtu.new_session(id, s_addr, in_queue, out_queue, timeout, advertisement -- skip virtual units log.debug(util.c(log_header, "skipping virtual RTU unit #", i)) else - log.error(util.c(log_header, "bad advertisement: encountered unsupported reactor-independent RTU type ", type_string)) + log.warning(util.c(log_header, "_handle_advertisement(): encountered unsupported facility RTU type ", type_string)) end end end @@ -181,9 +181,7 @@ function rtu.new_session(id, s_addr, in_queue, out_queue, timeout, advertisement self.units[i] = unit unit_count = unit_count + 1 elseif u_type ~= RTU_UNIT_TYPE.VIRTUAL then - _reset_config() - log.error(util.c(log_header, "bad advertisement: error occured while creating a unit (type is ", type_string, ")")) - break + log.warning(util.c(log_header, "_handle_advertisement(): problem occured while creating a unit (type is ", type_string, ")")) end end diff --git a/supervisor/session/svsessions.lua b/supervisor/session/svsessions.lua index a311ab9..075f090 100644 --- a/supervisor/session/svsessions.lua +++ b/supervisor/session/svsessions.lua @@ -34,7 +34,7 @@ local SESSION_TYPE = { svsessions.SESSION_TYPE = SESSION_TYPE local self = { - modem = nil, ---@type table|nil + nic = nil, ---@type nic|nil fp_ok = false, num_reactors = 0, facility = nil, ---@type facility|nil @@ -60,7 +60,7 @@ local function _sv_handle_outq(session) if msg ~= nil then if msg.qtype == mqueue.TYPE.PACKET then -- handle a packet to be sent - self.modem.transmit(session.r_chan, config.SVR_CHANNEL, msg.message.raw_sendable()) + self.nic.transmit(session.r_chan, config.SVR_CHANNEL, msg.message) elseif msg.qtype == mqueue.TYPE.COMMAND then -- handle instruction/notification elseif msg.qtype == mqueue.TYPE.DATA then @@ -135,7 +135,7 @@ local function _shutdown(session) while session.out_queue.ready() do local msg = session.out_queue.pop() if msg ~= nil and msg.qtype == mqueue.TYPE.PACKET then - self.modem.transmit(session.r_chan, config.SVR_CHANNEL, msg.message.raw_sendable()) + self.nic.transmit(session.r_chan, config.SVR_CHANNEL, msg.message) end end @@ -195,23 +195,17 @@ end -- PUBLIC FUNCTIONS -- -- initialize svsessions ----@param modem table modem device +---@param nic nic network interface device ---@param fp_ok boolean front panel active ---@param num_reactors integer number of reactors ---@param cooling_conf table cooling configuration definition -function svsessions.init(modem, fp_ok, num_reactors, cooling_conf) - self.modem = modem +function svsessions.init(nic, fp_ok, num_reactors, cooling_conf) + self.nic = nic self.fp_ok = fp_ok self.num_reactors = num_reactors self.facility = facility.new(num_reactors, cooling_conf) end --- re-link the modem ----@param modem table -function svsessions.relink_modem(modem) - self.modem = modem -end - -- find an RTU session by the computer ID ---@nodiscard ---@param source_addr integer diff --git a/supervisor/startup.lua b/supervisor/startup.lua index f03fe1e..3dc850e 100644 --- a/supervisor/startup.lua +++ b/supervisor/startup.lua @@ -7,6 +7,7 @@ require("/initenv").init_env() local crash = require("scada-common.crash") local comms = require("scada-common.comms") local log = require("scada-common.log") +local network = require("scada-common.network") local ppm = require("scada-common.ppm") local tcd = require("scada-common.tcd") local util = require("scada-common.util") @@ -20,7 +21,7 @@ local supervisor = require("supervisor.supervisor") local svsessions = require("supervisor.session.svsessions") -local SUPERVISOR_VERSION = "v0.17.7" +local SUPERVISOR_VERSION = "v0.18.0" local println = util.println local println_ts = util.println_ts @@ -94,6 +95,12 @@ local function main() -- mount connected devices ppm.mount_all() + -- message authentication init + if type(config.AUTH_KEY) == "string" then + network.init_mac(config.AUTH_KEY) + end + + -- get modem local modem = ppm.get_wireless_modem() if modem == nil then println("startup> wireless modem not found") @@ -115,8 +122,9 @@ local function main() println_ts = function (_) end end - -- start comms - local superv_comms = supervisor.comms(SUPERVISOR_VERSION, modem, fp_ok) + -- create network interface then setup comms + local nic = network.nic(modem) + local superv_comms = supervisor.comms(SUPERVISOR_VERSION, nic, fp_ok) -- base loop clock (6.67Hz, 3 ticks) local MAIN_CLOCK = 0.15 @@ -139,9 +147,12 @@ local function main() if type ~= nil and device ~= nil then if type == "modem" then -- we only care if this is our wireless modem - if device == modem then + if nic.is_modem(device) then + nic.disconnect() + println_ts("wireless modem disconnected!") log.warning("comms modem disconnected") + databus.tx_hw_modem(false) else log.warning("non-comms modem disconnected") @@ -153,10 +164,9 @@ local function main() if type ~= nil and device ~= nil then if type == "modem" then - if device.isWireless() then + if device.isWireless() and not nic.connected() then -- reconnected modem - modem = device - superv_comms.reconnect_modem(modem) + nic.connect(device) println_ts("wireless modem reconnected.") log.info("comms modem reconnected") diff --git a/supervisor/supervisor.lua b/supervisor/supervisor.lua index cfc52d8..fb33b06 100644 --- a/supervisor/supervisor.lua +++ b/supervisor/supervisor.lua @@ -16,10 +16,10 @@ local SCADA_MGMT_TYPE = comms.SCADA_MGMT_TYPE -- supervisory controller communications ---@nodiscard ---@param _version string supervisor version ----@param modem table modem device +---@param nic nic network interface device ---@param fp_ok boolean if the front panel UI is running ---@diagnostic disable-next-line: unused-local -function supervisor.comms(_version, modem, fp_ok) +function supervisor.comms(_version, nic, fp_ok) -- print a log message to the terminal as long as the UI isn't running local function println(message) if not fp_ok then util.println_ts(message) end end @@ -43,15 +43,11 @@ function supervisor.comms(_version, modem, fp_ok) -- PRIVATE FUNCTIONS -- -- configure modem channels - local function _conf_channels() - modem.closeAll() - modem.open(svr_channel) - end - - _conf_channels() + nic.closeAll() + nic.open(svr_channel) -- pass modem, status, and config data to svsessions - svsessions.init(modem, fp_ok, num_reactors, cooling_conf) + svsessions.init(nic, fp_ok, num_reactors, cooling_conf) -- send an establish request response ---@param packet scada_packet @@ -64,7 +60,7 @@ function supervisor.comms(_version, modem, fp_ok) m_pkt.make(SCADA_MGMT_TYPE.ESTABLISH, { ack, data }) s_pkt.make(packet.src_addr(), packet.seq_num() + 1, PROTOCOL.SCADA_MGMT, m_pkt.raw_sendable()) - modem.transmit(packet.remote_channel(), svr_channel, s_pkt.raw_sendable()) + nic.transmit(packet.remote_channel(), svr_channel, s_pkt) self.last_est_acks[packet.src_addr()] = ack end @@ -73,14 +69,6 @@ function supervisor.comms(_version, modem, fp_ok) ---@class superv_comms local public = {} - -- reconnect a newly connected modem - ---@param new_modem table - function public.reconnect_modem(new_modem) - modem = new_modem - svsessions.relink_modem(new_modem) - _conf_channels() - end - -- parse a packet ---@nodiscard ---@param side string @@ -90,13 +78,10 @@ function supervisor.comms(_version, modem, fp_ok) ---@param distance integer ---@return modbus_frame|rplc_frame|mgmt_frame|crdn_frame|nil packet function public.parse_packet(side, sender, reply_to, message, distance) + local s_pkt = nic.receive(side, sender, reply_to, message, distance) local pkt = nil - local s_pkt = comms.scada_packet() - -- parse packet as generic SCADA packet - s_pkt.receive(side, sender, reply_to, message, distance) - - if s_pkt.is_valid() then + if s_pkt then -- get as MODBUS TCP packet if s_pkt.protocol() == PROTOCOL.MODBUS_TCP then local m_pkt = comms.modbus_packet() diff --git a/test/lockbox-benchmark.lua b/test/lockbox-benchmark.lua deleted file mode 100644 index 7c6ae55..0000000 --- a/test/lockbox-benchmark.lua +++ /dev/null @@ -1,105 +0,0 @@ -require("/initenv").init_env() - -local pbkdf2 = require("lockbox.kdf.pbkdf2") -local AES128Cipher = require("lockbox.cipher.aes128") -local HMAC = require("lockbox.mac.hmac") -local SHA1 = require("lockbox.digest.sha1") --- local SHA2_224 = require("lockbox.digest.sha2_224") -local SHA2_256 = require("lockbox.digest.sha2_256") -local Stream = require("lockbox.util.stream") -local Array = require("lockbox.util.array") - --- local CBCMode = require("lockbox.cipher.mode.cbc") --- local CFBMode = require("lockbox.cipher.mode.cfb") --- local OFBMode = require("lockbox.cipher.mode.ofb") -local CTRMode = require("lockbox.cipher.mode.ctr") - -local ZeroPadding = require("lockbox.padding.zero") - -local comms = require("scada-common.comms") -local util = require("scada-common.util") - -local start = util.time() - -local keyd = pbkdf2() - -keyd.setPassword("mypassword") -keyd.setSalt("no_salt_thanks") -keyd.setIterations(16) -keyd.setBlockLen(4) -keyd.setDKeyLen(16) -keyd.setPRF(HMAC().setBlockSize(64).setDigest(SHA2_256)) -keyd.finish() - -util.println("pbkdf2: took " .. (util.time() - start) .. "ms") -util.println(keyd.asHex()) - -local pkt = comms.modbus_packet() ----@diagnostic disable-next-line: param-type-mismatch -pkt.make(1, 2, 7, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}) -local spkt = comms.scada_packet() -spkt.make(0, 1, 1, pkt.raw_sendable()) - -start = util.time() -local data = textutils.serialize(spkt.raw_sendable(), { allow_repetitions = true, compact = true }) - -util.println("packet serialize: took " .. (util.time() - start) .. "ms") -util.println("message: " .. data) - -start = util.time() -local v = { - cipher = CTRMode.Cipher, - decipher = CTRMode.Decipher, - iv = Array.fromHex("000102030405060708090A0B0C0D0E0F"), - key = Array.fromHex(keyd.asHex()), - padding = ZeroPadding -} -util.println("v init: took " .. (util.time() - start) .. "ms") - -start = util.time() -local cipher = v.cipher() -.setKey(v.key) -.setBlockCipher(AES128Cipher) -.setPadding(v.padding); -util.println("cipher init: took " .. (util.time() - start) .. "ms") - -start = util.time() -local cipherOutput = cipher - .init() - .update(Stream.fromArray(v.iv)) - .update(Stream.fromString(data)) - .asHex(); -util.println("encrypt: took " .. (util.time() - start) .. "ms") -util.println("ciphertext: " .. cipherOutput) - -start = util.time() -local decipher = v.decipher() - .setKey(v.key) - .setBlockCipher(AES128Cipher) - .setPadding(v.padding); -util.println("decipher init: took " .. (util.time() - start) .. "ms") - -start = util.time() -local plainOutput = decipher - .init() - .update(Stream.fromArray(v.iv)) - .update(Stream.fromHex(cipherOutput)) - .asHex(); -util.println("decrypt: took " .. (util.time() - start) .. "ms") -local a = Stream.fromHex(plainOutput) -local b = Stream.toString(a) -util.println("plaintext: " .. b) - -local msg = "000102030405060708090A0B0C0D0E0F" .. cipherOutput - -start = util.time() -local hash = HMAC() - .setBlockSize(64) - .setDigest(SHA1) - .setKey(keyd) - .init() - .update(Stream.fromHex(msg)) - .finish() - .asHex(); -util.println("hmac: took " .. (util.time() - start) .. "ms") -util.println("hash: " .. hash) diff --git a/test/lockbox_benchmark.lua b/test/lockbox_benchmark.lua new file mode 100644 index 0000000..9cdfd73 --- /dev/null +++ b/test/lockbox_benchmark.lua @@ -0,0 +1,193 @@ +require("/initenv").init_env() + +local pbkdf2 = require("lockbox.kdf.pbkdf2") +-- local AES128Cipher = require("lockbox.cipher.aes128") +local HMAC = require("lockbox.mac.hmac") +local MD5 = require("lockbox.digest.md5") +local SHA1 = require("lockbox.digest.sha1") +local SHA2_224 = require("lockbox.digest.sha2_224") +local SHA2_256 = require("lockbox.digest.sha2_256") +local Stream = require("lockbox.util.stream") +-- local Array = require("lockbox.util.array") + +-- local CBCMode = require("lockbox.cipher.mode.cbc") +-- local CFBMode = require("lockbox.cipher.mode.cfb") +-- local OFBMode = require("lockbox.cipher.mode.ofb") +-- local CTRMode = require("lockbox.cipher.mode.ctr") + +-- local ZeroPadding = require("lockbox.padding.zero") + +local comms = require("scada-common.comms") +local util = require("scada-common.util") + +local start = util.time() + +local keyd = pbkdf2() + +keyd.setPassword("mypassword") +keyd.setSalt("no_salt_thanks") +keyd.setIterations(16) +keyd.setBlockLen(4) +keyd.setDKeyLen(16) +keyd.setPRF(HMAC().setBlockSize(64).setDigest(SHA2_256)) +keyd.finish() + +util.println("pbkdf2: took " .. (util.time() - start) .. "ms") +util.println(keyd.asHex()) + +local pkt = comms.modbus_packet() +---@diagnostic disable-next-line: param-type-mismatch +pkt.make(1, 2, 7, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}) +local spkt = comms.scada_packet() +spkt.make(0, 1, 1, pkt.raw_sendable()) + +start = util.time() +local data = textutils.serialize(spkt.raw_sendable(), { allow_repetitions = true, compact = true }) + +util.println("packet serialize: took " .. (util.time() - start) .. "ms") +util.println("message: " .. data) + +--[[ +start = util.time() +local v = { + cipher = CTRMode.Cipher, + decipher = CTRMode.Decipher, + iv = Array.fromHex("000102030405060708090A0B0C0D0E0F"), + key = Array.fromHex(keyd.asHex()), + padding = ZeroPadding +} +util.println("v init: took " .. (util.time() - start) .. "ms") + +start = util.time() +local cipher = v.cipher() +.setKey(v.key) +.setBlockCipher(AES128Cipher) +.setPadding(v.padding); +util.println("cipher init: took " .. (util.time() - start) .. "ms") + +start = util.time() +local cipherOutput = cipher + .init() + .update(Stream.fromArray(v.iv)) + .update(Stream.fromString(data)) + .asHex(); +util.println("encrypt: took " .. (util.time() - start) .. "ms") +util.println("ciphertext: " .. cipherOutput) + +start = util.time() +local decipher = v.decipher() + .setKey(v.key) + .setBlockCipher(AES128Cipher) + .setPadding(v.padding); +util.println("decipher init: took " .. (util.time() - start) .. "ms") + +start = util.time() +local plainOutput = decipher + .init() + .update(Stream.fromArray(v.iv)) + .update(Stream.fromHex(cipherOutput)) + .asHex(); +util.println("decrypt: took " .. (util.time() - start) .. "ms") +local a = Stream.fromHex(plainOutput) +local b = Stream.toString(a) +util.println("plaintext: " .. b) + +local msg = "000102030405060708090A0B0C0D0E0F" .. cipherOutput +]]-- + +-- local testmsg = "{1,0,42,3,{5,{{},{boilers={},turbines={},rad_mon={},},{TurbineOnline={false,},AutoControl=false,TurbineTrip={false,},HeatingRateLow={false,},HighStartupRate=false,BoilRateMismatch=false,ManualReactorSCRAM=false,FuelInputRateLow=false,PLCHeartbeat=false,MaxWaterReturnFeed=false,RCSFault=false,PLCOnline=false,RadiationMonitor=1,TurbineOverSpeed={false,},CoolantFeedMismatch=false,BoilerOnline={false,},ReactorTempHigh=false,SteamDumpOpen={1,},RCSFlowLow=false,RadiationWarning=false,WasteLineOcclusion=false,SteamFeedMismatch=false,ReactorSCRAM=false,EmergencyCoolant=1,CoolantLevelLow=false,ReactorHighDeltaT=false,AutoReactorSCRAM=false,WaterLevelLow={},RCPTrip=false,GeneratorTrip={false,},},{1,1,1,1,1,1,1,1,1,1,1,1,},{\"REACTOR OFF-LINE\",\"awaiting connection...\",1,false,true,},},{{},{boilers={},turbines={},rad_mon={},},{TurbineOnline={false,},AutoControl=false,TurbineTrip={false,},HeatingRateLow={false,},HighStartupRate=false,BoilRateMismatch=false,ManualReactorSCRAM=false,FuelInputRateLow=false,PLCHeartbeat=false,MaxWaterReturnFeed=false,RCSFault=false,PLCOnline=false,RadiationMonitor=1,TurbineOverSpeed={false,},CoolantFeedMismatch=false,BoilerOnline={false,},ReactorTempHigh=false,SteamDumpOpen={1,},RCSFlowLow=false,RadiationWarning=false,WasteLineOcclusion=false,SteamFeedMismatch=false,ReactorSCRAM=false,EmergencyCoolant=1,CoolantLevelLow=false,ReactorHighDeltaT=false,AutoReactorSCRAM=false,WaterLevelLow={},RCPTrip=false,GeneratorTrip={false,},},{1,1,1,1,1,1,1,1,1,1,1,1,},{\"REACTOR OFF-LINE\",\"awaiting connection...\",1,false,true,},},{{},{boilers={},turbines={},rad_mon={},},{TurbineOnline={false,},AutoControl=false,TurbineTrip={false,},HeatingRateLow={false,},HighStartupRate=false,BoilRateMismatch=false,ManualReactorSCRAM=false,FuelInputRateLow=false,PLCHeartbeat=false,MaxWaterReturnFeed=false,RCSFault=false,PLCOnline=false,RadiationMonitor=1,TurbineOverSpeed={false,},CoolantFeedMismatch=false,BoilerOnline={false,},ReactorTempHigh=false,SteamDumpOpen={1,},RCSFlowLow=false,RadiationWarning=false,WasteLineOcclusion=false,SteamFeedMismatch=false,ReactorSCRAM=false,EmergencyCoolant=1,CoolantLevelLow=false,ReactorHighDeltaT=false,AutoReactorSCRAM=false,WaterLevelLow={},RCPTrip=false,GeneratorTrip={false,},},{1,1,1,1,1,1,1,1,1,1,1,1,},{\"REACTOR OFF-LINE\",\"awaiting connection...\",1,false,true,},},{{},{boilers={},turbines={},rad_mon={},},{TurbineOnline={false,},AutoControl=false,TurbineTrip={false,},HeatingRateLow={false,},HighStartupRate=false,BoilRateMismatch=false,ManualReactorSCRAM=false,FuelInputRateLow=false,PLCHeartbeat=false,MaxWaterReturnFeed=false,RCSFault=false,PLCOnline=false,RadiationMonitor=1,TurbineOverSpeed={false,},CoolantFeedMismatch=false,BoilerOnline={false,},ReactorTempHigh=false,SteamDumpOpen={1,},RCSFlowLow=false,RadiationWarning=false,WasteLineOcclusion=false,SteamFeedMismatch=false,ReactorSCRAM=false,EmergencyCoolant=1,CoolantLevelLow=false,ReactorHighDeltaT=false,AutoReactorSCRAM=false,WaterLevelLow={},RCPTrip=false,GeneratorTrip={false,},},{1,1,1,1,1,1,1,1,1,1,1,1,},{\"REACTOR OFF-LINE\",\"awaiting connection...\",1,false,true,},},},}" +local testmsg = "{1,0,42,3,{5,{{},{boilers={},turbines={},rad_mon={},},{TurbineOnline={false,},AutoControl=false,TurbineTrip={false,},HeatingRateLow={false,},HighStartupRate=false,BoilRateMismatch=false,ManualReactorSCRAM=false,FuelInputRateLow=false}" +local n = 1000 + +---@diagnostic disable: undefined-field + +local hash +local hmac = HMAC().setBlockSize(64).setDigest(MD5).setKey(keyd).init() + +os.sleep(0) +start = util.time() +for _ = 1, n do + hash = hmac.update(Stream.fromHex(testmsg)).finish().asHex(); +end +util.println("hmac-md5: took " .. (util.time() - start) .. "ms") +util.println("hash: " .. hash) +os.sleep(0) +start = util.time() +for _ = 1, n do + hash = hmac.update(Stream.fromHex(testmsg)).finish().asHex(); +end +util.println("hmac-md5: took " .. (util.time() - start) .. "ms") +os.sleep(0) +start = util.time() +for _ = 1, n do + hash = hmac.update(Stream.fromHex(testmsg)).finish().asHex(); +end +util.println("hmac-md5: took " .. (util.time() - start) .. "ms") +os.sleep(0) +start = util.time() +for _ = 1, n do + hash = hmac.update(Stream.fromHex(testmsg)).finish().asHex(); +end +util.println("hmac-md5: took " .. (util.time() - start) .. "ms") +os.sleep(0) +start = util.time() +for _ = 1, n do + hash = hmac.update(Stream.fromHex(testmsg)).finish().asHex(); +end +util.println("hmac-md5: took " .. (util.time() - start) .. "ms") + +hmac = HMAC().setBlockSize(64).setDigest(SHA1).setKey(keyd).init() + +os.sleep(0) +start = util.time() +for _ = 1, n do + hash = hmac.update(Stream.fromHex(testmsg)).finish().asHex(); +end +util.println("hmac-sha1: took " .. (util.time() - start) .. "ms") +util.println("hash: " .. hash) +os.sleep(0) +start = util.time() +for _ = 1, n do + hash = hmac.update(Stream.fromHex(testmsg)).finish().asHex(); +end +util.println("hmac-sha1: took " .. (util.time() - start) .. "ms") +os.sleep(0) +start = util.time() +for _ = 1, n do + hash = hmac.update(Stream.fromHex(testmsg)).finish().asHex(); +end +util.println("hmac-sha1: took " .. (util.time() - start) .. "ms") +os.sleep(0) +start = util.time() +for _ = 1, n do + hash = hmac.update(Stream.fromHex(testmsg)).finish().asHex(); +end +util.println("hmac-sha1: took " .. (util.time() - start) .. "ms") +os.sleep(0) +start = util.time() +for _ = 1, n do + hash = hmac.update(Stream.fromHex(testmsg)).finish().asHex(); +end +util.println("hmac-sha1: took " .. (util.time() - start) .. "ms") + +os.sleep(0) + +hmac = HMAC().setBlockSize(64).setDigest(SHA2_224).setKey(keyd).init() + +start = util.time() +for _ = 1, n do + hash = hmac.update(Stream.fromHex(testmsg)).finish().asHex(); +end +util.println("hmac-sha224: took " .. (util.time() - start) .. "ms") +util.println("hash: " .. hash) + +os.sleep(0) + +hmac = HMAC().setBlockSize(64).setDigest(SHA2_256).setKey(keyd).init() + +start = util.time() +for _ = 1, n do + hash = hmac.update(Stream.fromHex(testmsg)).finish().asHex(); +end +util.println("hmac-sha256: took " .. (util.time() - start) .. "ms") +util.println("hash: " .. hash)