mirror of
https://github.com/MikaylaFischler/cc-mek-scada.git
synced 2024-08-30 18:22:34 +00:00
4
.github/workflows/manifest.yml
vendored
4
.github/workflows/manifest.yml
vendored
@ -1,5 +1,5 @@
|
|||||||
# Simple workflow for deploying static content to GitHub Pages
|
# Deploy installation manifests and shields versions
|
||||||
name: Deploy Installation Manifests and Versions
|
name: Deploy Installation Data
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,2 +1,2 @@
|
|||||||
_notes/
|
_notes/
|
||||||
program.sh
|
/*program.sh
|
561
ccmsi.lua
561
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 println(message) print(tostring(message)) end
|
||||||
local function print(message) term.write(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 install_dir = "/.install-cache"
|
||||||
local manifest_path = "https://mikaylafischler.github.io/cc-mek-scada/manifests/"
|
local manifest_path = "https://mikaylafischler.github.io/cc-mek-scada/manifests/"
|
||||||
local repo_path = "http://raw.githubusercontent.com/MikaylaFischler/cc-mek-scada/"
|
local repo_path = "http://raw.githubusercontent.com/MikaylaFischler/cc-mek-scada/"
|
||||||
|
|
||||||
local opts = { ... }
|
local opts = { ... }
|
||||||
local mode = nil
|
local mode, app, target
|
||||||
local app = nil
|
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
|
-- record the local installation manifest
|
||||||
---@param manifest table
|
|
||||||
---@param dependencies table
|
|
||||||
local function write_install_manifest(manifest, dependencies)
|
local function write_install_manifest(manifest, dependencies)
|
||||||
local versions = {}
|
local versions = {}
|
||||||
for key, value in pairs(manifest.versions) do
|
for key, value in pairs(manifest.versions) do
|
||||||
local is_dependency = false
|
local is_dependency = false
|
||||||
for _, dependency in pairs(dependencies) do
|
for _, dependency in pairs(dependencies) do
|
||||||
if key == "bootloader" and dependency == "system" then
|
if (key == "bootloader" and dependency == "system") or key == dependency then
|
||||||
is_dependency = true
|
is_dependency = true; break
|
||||||
break
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if key == app or key == "comms" or is_dependency then versions[key] = value end
|
if key == app or key == "comms" or is_dependency then versions[key] = value end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -54,116 +120,66 @@ local function write_install_manifest(manifest, dependencies)
|
|||||||
imfile.close()
|
imfile.close()
|
||||||
end
|
end
|
||||||
|
|
||||||
--
|
|
||||||
-- get and validate command line options
|
-- get and validate command line options
|
||||||
--
|
|
||||||
|
|
||||||
println("-- CC Mekanism SCADA Installer " .. CCMSI_VERSION .. " --")
|
println("-- CC Mekanism SCADA Installer " .. CCMSI_VERSION .. " --")
|
||||||
|
|
||||||
if #opts == 0 or opts[1] == "help" then
|
if #opts == 0 or opts[1] == "help" then
|
||||||
println("usage: ccmsi <mode> <app> <tag/branch>")
|
println("usage: ccmsi <mode> <app> <branch>")
|
||||||
println("<mode>")
|
println("<mode>")
|
||||||
term.setTextColor(colors.lightGray)
|
lgray()
|
||||||
println(" check - check latest versions avilable")
|
println(" check - check latest versions avilable")
|
||||||
term.setTextColor(colors.yellow)
|
yellow()
|
||||||
println(" ccmsi check <tag/branch> for target")
|
println(" ccmsi check <branch> for target")
|
||||||
term.setTextColor(colors.lightGray)
|
lgray()
|
||||||
println(" install - fresh install, overwrites config")
|
println(" install - fresh install, overwrites config")
|
||||||
println(" update - update files EXCEPT for config/logs")
|
println(" update - update files EXCEPT for config/logs")
|
||||||
println(" remove - delete files EXCEPT for config/logs")
|
println(" remove - delete files EXCEPT for config/logs")
|
||||||
println(" purge - delete files INCLUDING config/logs")
|
println(" purge - delete files INCLUDING config/logs")
|
||||||
term.setTextColor(colors.white)
|
white(); println("<app>"); lgray()
|
||||||
println("<app>")
|
|
||||||
term.setTextColor(colors.lightGray)
|
|
||||||
println(" reactor-plc - reactor PLC firmware")
|
println(" reactor-plc - reactor PLC firmware")
|
||||||
println(" rtu - RTU firmware")
|
println(" rtu - RTU firmware")
|
||||||
println(" supervisor - supervisor server application")
|
println(" supervisor - supervisor server application")
|
||||||
println(" coordinator - coordinator application")
|
println(" coordinator - coordinator application")
|
||||||
println(" pocket - pocket application")
|
println(" pocket - pocket application")
|
||||||
term.setTextColor(colors.white)
|
white(); println("<branch>"); yellow()
|
||||||
println("<tag/branch>")
|
|
||||||
term.setTextColor(colors.yellow)
|
|
||||||
println(" second parameter when used with check")
|
println(" second parameter when used with check")
|
||||||
term.setTextColor(colors.lightGray)
|
lgray(); println(" main (default) | latest | devel"); white()
|
||||||
println(" note: defaults to main")
|
|
||||||
println(" target GitHub tag or branch name")
|
|
||||||
return
|
return
|
||||||
else
|
else
|
||||||
for _, v in pairs({ "check", "install", "update", "remove", "purge" }) do
|
mode = get_opt(opts[1], { "check", "install", "update", "remove", "purge" })
|
||||||
if opts[1] == v then
|
|
||||||
mode = v
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if mode == nil then
|
if mode == nil then
|
||||||
println("unrecognized mode")
|
red(); println("Unrecognized mode."); white()
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, v in pairs({ "reactor-plc", "rtu", "supervisor", "coordinator", "pocket" }) do
|
app = get_opt(opts[2], { "reactor-plc", "rtu", "supervisor", "coordinator", "pocket" })
|
||||||
if opts[2] == v then
|
|
||||||
app = v
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if app == nil and mode ~= "check" then
|
if app == nil and mode ~= "check" then
|
||||||
println("unrecognized application")
|
red(); println("Unrecognized application."); white()
|
||||||
return
|
return
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
--
|
|
||||||
-- run selected mode
|
-- run selected mode
|
||||||
--
|
|
||||||
|
|
||||||
if mode == "check" then
|
if mode == "check" then
|
||||||
-------------------------
|
local ok, manifest = get_remote_manifest()
|
||||||
-- GET REMOTE MANIFEST --
|
if not ok then return end
|
||||||
-------------------------
|
|
||||||
|
|
||||||
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 local_ok, local_manifest = read_local_manifest()
|
||||||
if not local_ok then
|
if not local_ok then
|
||||||
term.setTextColor(colors.yellow)
|
yellow(); println("failed to load local installation information"); white()
|
||||||
println("failed to load local installation information")
|
|
||||||
term.setTextColor(colors.white)
|
|
||||||
|
|
||||||
local_manifest = { versions = { installer = CCMSI_VERSION } }
|
local_manifest = { versions = { installer = CCMSI_VERSION } }
|
||||||
else
|
else
|
||||||
local_manifest.versions.installer = CCMSI_VERSION
|
local_manifest.versions.installer = CCMSI_VERSION
|
||||||
@ -174,191 +190,95 @@ if mode == "check" then
|
|||||||
term.setTextColor(colors.purple)
|
term.setTextColor(colors.purple)
|
||||||
print(string.format("%-14s", "[" .. key .. "]"))
|
print(string.format("%-14s", "[" .. key .. "]"))
|
||||||
if key == "installer" or (local_ok and (local_manifest.versions[key] ~= nil)) then
|
if key == "installer" or (local_ok and (local_manifest.versions[key] ~= nil)) then
|
||||||
term.setTextColor(colors.blue)
|
blue(); print(local_manifest.versions[key])
|
||||||
print(local_manifest.versions[key])
|
|
||||||
if value ~= local_manifest.versions[key] then
|
if value ~= local_manifest.versions[key] then
|
||||||
term.setTextColor(colors.white)
|
white(); print(" (")
|
||||||
print(" (")
|
|
||||||
term.setTextColor(colors.cyan)
|
term.setTextColor(colors.cyan)
|
||||||
print(value)
|
print(value); white(); println(" available)")
|
||||||
term.setTextColor(colors.white)
|
else green(); println(" (up to date)") end
|
||||||
println(" available)")
|
|
||||||
else
|
else
|
||||||
term.setTextColor(colors.green)
|
lgray(); print("not installed"); white(); print(" (latest ")
|
||||||
println(" (up to date)")
|
|
||||||
end
|
|
||||||
else
|
|
||||||
term.setTextColor(colors.lightGray)
|
|
||||||
print("not installed")
|
|
||||||
term.setTextColor(colors.white)
|
|
||||||
print(" (latest ")
|
|
||||||
term.setTextColor(colors.cyan)
|
term.setTextColor(colors.cyan)
|
||||||
print(value)
|
print(value); white(); println(")")
|
||||||
term.setTextColor(colors.white)
|
|
||||||
println(")")
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
elseif mode == "install" or mode == "update" then
|
elseif mode == "install" or mode == "update" then
|
||||||
-------------------------
|
local ok, manifest = get_remote_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
|
local ver = {
|
||||||
if opts[3] then manifest_path = manifest_path .. opts[3] .. "/" else manifest_path = manifest_path .. "main/" end
|
app = { v_local = nil, v_remote = nil, changed = false },
|
||||||
local install_manifest = manifest_path .. "install_manifest.json"
|
boot = { v_local = nil, v_remote = nil, changed = false },
|
||||||
|
comms = { v_local = nil, v_remote = nil, changed = false },
|
||||||
local response, error = http.get(install_manifest)
|
graphics = { v_local = nil, v_remote = nil, changed = false },
|
||||||
|
lockbox = { v_local = nil, v_remote = nil, changed = false }
|
||||||
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
|
|
||||||
|
|
||||||
-- try to find local versions
|
-- try to find local versions
|
||||||
|
local local_ok, local_manifest = read_local_manifest()
|
||||||
if not local_ok then
|
if not local_ok then
|
||||||
if mode == "update" then
|
if mode == "update" then
|
||||||
term.setTextColor(colors.red)
|
red(); println("failed to load local installation information, cannot update"); white()
|
||||||
println("failed to load local installation information, cannot update")
|
|
||||||
term.setTextColor(colors.white)
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
local_app_version = local_manifest.versions[app]
|
ver.boot.v_local = local_manifest.versions.bootloader
|
||||||
local_comms_version = local_manifest.versions.comms
|
ver.app.v_local = local_manifest.versions[app]
|
||||||
local_boot_version = local_manifest.versions.bootloader
|
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
|
if local_manifest.versions[app] == nil then
|
||||||
term.setTextColor(colors.red)
|
red(); println("another application is already installed, please purge it before installing a new application"); white()
|
||||||
println("another application is already installed, please purge it before installing a new application")
|
|
||||||
term.setTextColor(colors.white)
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local_manifest.versions.installer = CCMSI_VERSION
|
local_manifest.versions.installer = CCMSI_VERSION
|
||||||
if manifest.versions.installer ~= CCMSI_VERSION then
|
if manifest.versions.installer ~= CCMSI_VERSION then
|
||||||
term.setTextColor(colors.yellow)
|
yellow(); println("a newer version of the installer is available, it is recommended to download it"); white()
|
||||||
println("a newer version of the installer is available, consider downloading it")
|
|
||||||
term.setTextColor(colors.white)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local remote_app_version = manifest.versions[app]
|
ver.boot.v_remote = manifest.versions.bootloader
|
||||||
local remote_comms_version = manifest.versions.comms
|
ver.app.v_remote = manifest.versions[app]
|
||||||
local remote_boot_version = manifest.versions.bootloader
|
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
|
if mode == "install" then
|
||||||
println("installing " .. app .. " files...")
|
println("Installing " .. app .. " files...")
|
||||||
elseif mode == "update" then
|
elseif mode == "update" then
|
||||||
println("updating " .. app .. " files... (keeping old config.lua)")
|
println("Updating " .. app .. " files... (keeping old config.lua)")
|
||||||
end
|
end
|
||||||
term.setTextColor(colors.white)
|
white()
|
||||||
|
|
||||||
-- display bootloader version change information
|
-- display bootloader version change information
|
||||||
if local_boot_version ~= nil then
|
show_pkg_change("bootldr", ver.boot.v_local, ver.boot.v_remote)
|
||||||
if local_boot_version ~= remote_boot_version then
|
ver.boot.changed = ver.boot.v_local ~= ver.boot.v_remote
|
||||||
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
|
|
||||||
|
|
||||||
-- display app version change information
|
-- display app version change information
|
||||||
if local_app_version ~= nil then
|
show_pkg_change(app, ver.app.v_local, ver.app.v_remote)
|
||||||
if local_app_version ~= remote_app_version then
|
ver.app.changed = ver.app.v_local ~= ver.app.v_remote
|
||||||
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
|
|
||||||
|
|
||||||
-- display comms version change information
|
-- display comms version change information
|
||||||
if local_comms_version ~= nil then
|
show_pkg_change("comms", ver.comms.v_local, ver.comms.v_remote)
|
||||||
if local_comms_version ~= remote_comms_version then
|
ver.comms.changed = ver.comms.v_local ~= ver.comms.v_remote
|
||||||
print("[comms] updating ")
|
if ver.comms.changed and ver.comms.v_local ~= nil then
|
||||||
term.setTextColor(colors.blue)
|
print("[comms] "); yellow(); println("other devices on the network will require an update"); white()
|
||||||
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)
|
|
||||||
end
|
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 --
|
-- START INSTALL/UPDATE --
|
||||||
--------------------------
|
--------------------------
|
||||||
@ -382,24 +302,27 @@ elseif mode == "install" or mode == "update" then
|
|||||||
-- check space constraints
|
-- check space constraints
|
||||||
if space_available < space_required then
|
if space_available < space_required then
|
||||||
single_file_mode = true
|
single_file_mode = true
|
||||||
term.setTextColor(colors.yellow)
|
yellow(); println("WARNING: Insufficient space available for a full download!"); white()
|
||||||
println("WARNING: Insufficient space available for a full download!")
|
|
||||||
term.setTextColor(colors.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("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)")
|
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
|
||||||
local confirm = read()
|
println("Operation cancelled.")
|
||||||
if confirm ~= "y" and confirm ~= "Y" then
|
|
||||||
println("installation cancelled")
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
---@diagnostic disable-next-line: undefined-field
|
|
||||||
os.sleep(2)
|
|
||||||
|
|
||||||
local success = true
|
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 not single_file_mode then
|
||||||
if fs.exists(install_dir) then
|
if fs.exists(install_dir) then
|
||||||
fs.delete(install_dir)
|
fs.delete(install_dir)
|
||||||
@ -408,28 +331,19 @@ elseif mode == "install" or mode == "update" then
|
|||||||
|
|
||||||
-- download all dependencies
|
-- download all dependencies
|
||||||
for _, dependency in pairs(dependencies) do
|
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
|
if mode == "update" and unchanged(dependency) then
|
||||||
-- skip system package if unchanged, skip app package if not changed
|
pkg_message("skipping download of unchanged package", dependency)
|
||||||
-- 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)
|
|
||||||
else
|
else
|
||||||
term.setTextColor(colors.white)
|
pkg_message("downloading package", dependency)
|
||||||
print("downloading package ")
|
lgray()
|
||||||
term.setTextColor(colors.blue)
|
|
||||||
println(dependency)
|
|
||||||
|
|
||||||
term.setTextColor(colors.lightGray)
|
|
||||||
local files = file_list[dependency]
|
local files = file_list[dependency]
|
||||||
for _, file in pairs(files) do
|
for _, file in pairs(files) do
|
||||||
println("GET " .. file)
|
println("GET " .. file)
|
||||||
local dl, err = http.get(repo_path .. file)
|
local dl, err = http.get(repo_path .. file)
|
||||||
|
|
||||||
if dl == nil then
|
if dl == nil then
|
||||||
term.setTextColor(colors.red)
|
red(); println("GET HTTP Error " .. err)
|
||||||
println("GET HTTP Error " .. err)
|
|
||||||
success = false
|
success = false
|
||||||
break
|
break
|
||||||
else
|
else
|
||||||
@ -444,20 +358,12 @@ elseif mode == "install" or mode == "update" then
|
|||||||
-- copy in downloaded files (installation)
|
-- copy in downloaded files (installation)
|
||||||
if success then
|
if success then
|
||||||
for _, dependency in pairs(dependencies) do
|
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
|
if mode == "update" and unchanged(dependency) then
|
||||||
-- skip system package if unchanged, skip app package if not changed
|
pkg_message("skipping install of unchanged package", dependency)
|
||||||
-- 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)
|
|
||||||
else
|
else
|
||||||
term.setTextColor(colors.white)
|
pkg_message("installing package", dependency)
|
||||||
print("installing package ")
|
lgray()
|
||||||
term.setTextColor(colors.blue)
|
|
||||||
println(dependency)
|
|
||||||
|
|
||||||
term.setTextColor(colors.lightGray)
|
|
||||||
local files = file_list[dependency]
|
local files = file_list[dependency]
|
||||||
for _, file in pairs(files) do
|
for _, file in pairs(files) do
|
||||||
if mode == "install" or file ~= config_file then
|
if mode == "install" or file ~= config_file then
|
||||||
@ -473,41 +379,25 @@ elseif mode == "install" or mode == "update" then
|
|||||||
fs.delete(install_dir)
|
fs.delete(install_dir)
|
||||||
|
|
||||||
if success then
|
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)
|
write_install_manifest(manifest, dependencies)
|
||||||
term.setTextColor(colors.green)
|
green()
|
||||||
if mode == "install" then
|
if mode == "install" then
|
||||||
println("installation completed successfully")
|
println("Installation completed successfully.")
|
||||||
else
|
else println("Update completed successfully.") end
|
||||||
println("update completed successfully")
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
if mode == "install" then
|
if mode == "install" then
|
||||||
term.setTextColor(colors.red)
|
red(); println("Installation failed.")
|
||||||
println("installation failed")
|
else orange(); println("Update failed, existing files unmodified.") end
|
||||||
else
|
|
||||||
term.setTextColor(colors.orange)
|
|
||||||
println("update failed, existing files unmodified")
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
-- go through all files and replace one by one
|
-- go through all files and replace one by one
|
||||||
for _, dependency in pairs(dependencies) do
|
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
|
if mode == "update" and unchanged(dependency) then
|
||||||
-- skip system package if unchanged, skip app package if not changed
|
pkg_message("skipping install of unchanged package", dependency)
|
||||||
-- 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)
|
|
||||||
else
|
else
|
||||||
term.setTextColor(colors.white)
|
pkg_message("installing package", dependency)
|
||||||
print("installing package ")
|
lgray()
|
||||||
term.setTextColor(colors.blue)
|
|
||||||
println(dependency)
|
|
||||||
|
|
||||||
term.setTextColor(colors.lightGray)
|
|
||||||
local files = file_list[dependency]
|
local files = file_list[dependency]
|
||||||
for _, file in pairs(files) do
|
for _, file in pairs(files) do
|
||||||
if mode == "install" or file ~= config_file then
|
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)
|
local dl, err = http.get(repo_path .. file)
|
||||||
|
|
||||||
if dl == nil then
|
if dl == nil then
|
||||||
println("GET HTTP Error " .. err)
|
red(); println("GET HTTP Error " .. err)
|
||||||
success = false
|
success = false
|
||||||
break
|
break
|
||||||
else
|
else
|
||||||
@ -529,55 +419,37 @@ elseif mode == "install" or mode == "update" then
|
|||||||
end
|
end
|
||||||
|
|
||||||
if success then
|
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)
|
write_install_manifest(manifest, dependencies)
|
||||||
term.setTextColor(colors.green)
|
green()
|
||||||
if mode == "install" then
|
if mode == "install" then
|
||||||
println("installation completed successfully")
|
println("Installation completed successfully.")
|
||||||
|
else println("Update completed successfully.") end
|
||||||
else
|
else
|
||||||
println("update completed successfully")
|
red()
|
||||||
end
|
|
||||||
else
|
|
||||||
term.setTextColor(colors.red)
|
|
||||||
if mode == "install" then
|
if mode == "install" then
|
||||||
println("installation failed, files may have been skipped")
|
println("Installation failed, files may have been skipped.")
|
||||||
else
|
else println("Update failed, files may have been skipped.") end
|
||||||
println("update failed, files may have been skipped")
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
elseif mode == "remove" or mode == "purge" then
|
elseif mode == "remove" or mode == "purge" then
|
||||||
local imfile = fs.open("install_manifest.json", "r")
|
local ok, manifest = read_local_manifest()
|
||||||
local ok = false
|
|
||||||
local manifest = {}
|
|
||||||
|
|
||||||
if imfile ~= nil then
|
|
||||||
ok, manifest = pcall(function () return textutils.unserializeJSON(imfile.readAll()) end)
|
|
||||||
imfile.close()
|
|
||||||
end
|
|
||||||
|
|
||||||
if not ok then
|
if not ok then
|
||||||
term.setTextColor(colors.red)
|
red(); println("Error parsing local installation manifest."); white()
|
||||||
println("error parsing local installation manifest")
|
|
||||||
term.setTextColor(colors.white)
|
|
||||||
return
|
return
|
||||||
elseif mode == "remove" and manifest.versions[app] == nil then
|
elseif mode == "remove" and manifest.versions[app] == nil then
|
||||||
term.setTextColor(colors.red)
|
red(); println(app .. " is not installed, cannot remove."); white()
|
||||||
println(app .. " is not installed")
|
|
||||||
term.setTextColor(colors.white)
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
term.setTextColor(colors.orange)
|
orange()
|
||||||
if mode == "remove" then
|
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
|
elseif mode == "purge" then
|
||||||
println("purging all " .. app .. " files...")
|
println("Purging all " .. app .. " files including config and log...")
|
||||||
end
|
end
|
||||||
|
|
||||||
---@diagnostic disable-next-line: undefined-field
|
-- ask for confirmation
|
||||||
os.sleep(2)
|
if not ask_y_n("Continue?", false) then return end
|
||||||
|
|
||||||
local file_list = manifest.files
|
local file_list = manifest.files
|
||||||
local dependencies = manifest.depends[app]
|
local dependencies = manifest.depends[app]
|
||||||
@ -585,9 +457,8 @@ elseif mode == "remove" or mode == "purge" then
|
|||||||
|
|
||||||
table.insert(dependencies, app)
|
table.insert(dependencies, app)
|
||||||
|
|
||||||
term.setTextColor(colors.lightGray)
|
|
||||||
|
|
||||||
-- delete log file if purging
|
-- delete log file if purging
|
||||||
|
lgray()
|
||||||
if mode == "purge" and fs.exists(config_file) then
|
if mode == "purge" and fs.exists(config_file) then
|
||||||
local log_deleted = pcall(function ()
|
local log_deleted = pcall(function ()
|
||||||
local config = require(app .. ".config")
|
local config = require(app .. ".config")
|
||||||
@ -598,11 +469,9 @@ elseif mode == "remove" or mode == "purge" then
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
if not log_deleted then
|
if not log_deleted then
|
||||||
term.setTextColor(colors.red)
|
red(); println("failed to delete log file")
|
||||||
println("failed to delete log file")
|
white(); println("press enter to continue...")
|
||||||
term.setTextColor(colors.lightGray)
|
read(); lgray()
|
||||||
---@diagnostic disable-next-line: undefined-field
|
|
||||||
os.sleep(1)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -623,11 +492,7 @@ elseif mode == "remove" or mode == "purge" then
|
|||||||
local folder = files[1]
|
local folder = files[1]
|
||||||
while true do
|
while true do
|
||||||
local dir = fs.getDir(folder)
|
local dir = fs.getDir(folder)
|
||||||
if dir == "" or dir == ".." then
|
if dir == "" or dir == ".." then break else folder = dir end
|
||||||
break
|
|
||||||
else
|
|
||||||
folder = dir
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if fs.isDir(folder) then
|
if fs.isDir(folder) then
|
||||||
@ -635,14 +500,11 @@ elseif mode == "remove" or mode == "purge" then
|
|||||||
println("deleted directory " .. folder)
|
println("deleted directory " .. folder)
|
||||||
end
|
end
|
||||||
elseif dependency == app then
|
elseif dependency == app then
|
||||||
|
-- delete individual subdirectories so we can leave the config
|
||||||
for _, folder in pairs(files) do
|
for _, folder in pairs(files) do
|
||||||
while true do
|
while true do
|
||||||
local dir = fs.getDir(folder)
|
local dir = fs.getDir(folder)
|
||||||
if dir == "" or dir == ".." or dir == app then
|
if dir == "" or dir == ".." or dir == app then break else folder = dir end
|
||||||
break
|
|
||||||
else
|
|
||||||
folder = dir
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if folder ~= app and fs.isDir(folder) then
|
if folder ~= app and fs.isDir(folder) then
|
||||||
@ -660,13 +522,12 @@ elseif mode == "remove" or mode == "purge" then
|
|||||||
else
|
else
|
||||||
-- remove all data from versions list to show nothing is installed
|
-- remove all data from versions list to show nothing is installed
|
||||||
manifest.versions = {}
|
manifest.versions = {}
|
||||||
imfile = fs.open("install_manifest.json", "w")
|
local imfile = fs.open("install_manifest.json", "w")
|
||||||
imfile.write(textutils.serializeJSON(manifest))
|
imfile.write(textutils.serializeJSON(manifest))
|
||||||
imfile.close()
|
imfile.close()
|
||||||
end
|
end
|
||||||
|
|
||||||
term.setTextColor(colors.green)
|
green(); println("Done!")
|
||||||
println("done!")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
term.setTextColor(colors.white)
|
white()
|
||||||
|
@ -11,6 +11,10 @@ config.TRUSTED_RANGE = 0
|
|||||||
-- time in seconds (>= 2) before assuming a remote device is no longer active
|
-- time in seconds (>= 2) before assuming a remote device is no longer active
|
||||||
config.SV_TIMEOUT = 5
|
config.SV_TIMEOUT = 5
|
||||||
config.API_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
|
-- expected number of reactor units, used only to require that number of unit monitors
|
||||||
config.NUM_UNITS = 4
|
config.NUM_UNITS = 4
|
||||||
|
@ -183,7 +183,8 @@ local function log_dmesg(message, dmesg_tag, working)
|
|||||||
GRAPHICS = colors.green,
|
GRAPHICS = colors.green,
|
||||||
SYSTEM = colors.cyan,
|
SYSTEM = colors.cyan,
|
||||||
BOOT = colors.blue,
|
BOOT = colors.blue,
|
||||||
COMMS = colors.purple
|
COMMS = colors.purple,
|
||||||
|
CRYPTO = colors.yellow
|
||||||
}
|
}
|
||||||
|
|
||||||
if working then
|
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_sys(message) log_dmesg(message, "SYSTEM") end
|
||||||
function coordinator.log_boot(message) log_dmesg(message, "BOOT") end
|
function coordinator.log_boot(message) log_dmesg(message, "BOOT") end
|
||||||
function coordinator.log_comms(message) log_dmesg(message, "COMMS") 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
|
-- log a message for communications connecting, providing access to progress indication control functions
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
@ -212,13 +214,13 @@ end
|
|||||||
-- coordinator communications
|
-- coordinator communications
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param version string coordinator version
|
---@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 crd_channel integer port of configured supervisor
|
||||||
---@param svr_channel integer listening port for supervisor replys
|
---@param svr_channel integer listening port for supervisor replys
|
||||||
---@param pkt_channel integer listening port for pocket API
|
---@param pkt_channel integer listening port for pocket API
|
||||||
---@param range integer trusted device connection range
|
---@param range integer trusted device connection range
|
||||||
---@param sv_watchdog watchdog
|
---@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 = {
|
local self = {
|
||||||
sv_linked = false,
|
sv_linked = false,
|
||||||
sv_addr = comms.BROADCAST,
|
sv_addr = comms.BROADCAST,
|
||||||
@ -234,16 +236,12 @@ function coordinator.comms(version, modem, crd_channel, svr_channel, pkt_channel
|
|||||||
|
|
||||||
-- PRIVATE FUNCTIONS --
|
-- PRIVATE FUNCTIONS --
|
||||||
|
|
||||||
-- configure modem channels
|
-- configure network channels
|
||||||
local function _conf_channels()
|
nic.closeAll()
|
||||||
modem.closeAll()
|
nic.open(crd_channel)
|
||||||
modem.open(crd_channel)
|
|
||||||
end
|
|
||||||
|
|
||||||
_conf_channels()
|
-- link nic to apisessions
|
||||||
|
apisessions.init(nic)
|
||||||
-- link modem to apisessions
|
|
||||||
apisessions.init(modem)
|
|
||||||
|
|
||||||
-- send a packet to the supervisor
|
-- send a packet to the supervisor
|
||||||
---@param msg_type SCADA_MGMT_TYPE|SCADA_CRDN_TYPE
|
---@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)
|
pkt.make(msg_type, msg)
|
||||||
s_pkt.make(self.sv_addr, self.sv_seq_num, protocol, pkt.raw_sendable())
|
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
|
self.sv_seq_num = self.sv_seq_num + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -277,7 +275,7 @@ function coordinator.comms(version, modem, crd_channel, svr_channel, pkt_channel
|
|||||||
m_pkt.make(SCADA_MGMT_TYPE.ESTABLISH, { ack })
|
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())
|
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
|
self.last_api_est_acks[packet.src_addr()] = ack
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -297,14 +295,6 @@ function coordinator.comms(version, modem, crd_channel, svr_channel, pkt_channel
|
|||||||
---@class coord_comms
|
---@class coord_comms
|
||||||
local public = {}
|
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
|
-- close the connection to the server
|
||||||
function public.close()
|
function public.close()
|
||||||
sv_watchdog.cancel()
|
sv_watchdog.cancel()
|
||||||
@ -402,13 +392,10 @@ function coordinator.comms(version, modem, crd_channel, svr_channel, pkt_channel
|
|||||||
---@param distance integer
|
---@param distance integer
|
||||||
---@return mgmt_frame|crdn_frame|capi_frame|nil packet
|
---@return mgmt_frame|crdn_frame|capi_frame|nil packet
|
||||||
function public.parse_packet(side, sender, reply_to, message, distance)
|
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 pkt = nil
|
||||||
local s_pkt = comms.scada_packet()
|
|
||||||
|
|
||||||
-- parse packet as generic SCADA packet
|
if s_pkt then
|
||||||
s_pkt.receive(side, sender, reply_to, message, distance)
|
|
||||||
|
|
||||||
if s_pkt.is_valid() then
|
|
||||||
-- get as SCADA management packet
|
-- get as SCADA management packet
|
||||||
if s_pkt.protocol() == PROTOCOL.SCADA_MGMT then
|
if s_pkt.protocol() == PROTOCOL.SCADA_MGMT then
|
||||||
local mgmt_pkt = comms.mgmt_packet()
|
local mgmt_pkt = comms.mgmt_packet()
|
||||||
|
@ -10,7 +10,7 @@ local pocket = require("coordinator.session.pocket")
|
|||||||
local apisessions = {}
|
local apisessions = {}
|
||||||
|
|
||||||
local self = {
|
local self = {
|
||||||
modem = nil,
|
nic = nil,
|
||||||
next_id = 0,
|
next_id = 0,
|
||||||
sessions = {}
|
sessions = {}
|
||||||
}
|
}
|
||||||
@ -31,7 +31,7 @@ local function _api_handle_outq(session)
|
|||||||
if msg ~= nil then
|
if msg ~= nil then
|
||||||
if msg.qtype == mqueue.TYPE.PACKET then
|
if msg.qtype == mqueue.TYPE.PACKET then
|
||||||
-- handle a packet to be sent
|
-- 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
|
elseif msg.qtype == mqueue.TYPE.COMMAND then
|
||||||
-- handle instruction/notification
|
-- handle instruction/notification
|
||||||
elseif msg.qtype == mqueue.TYPE.DATA then
|
elseif msg.qtype == mqueue.TYPE.DATA then
|
||||||
@ -58,7 +58,7 @@ local function _shutdown(session)
|
|||||||
while session.out_queue.ready() do
|
while session.out_queue.ready() do
|
||||||
local msg = session.out_queue.pop()
|
local msg = session.out_queue.pop()
|
||||||
if msg ~= nil and msg.qtype == mqueue.TYPE.PACKET then
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -68,15 +68,9 @@ end
|
|||||||
-- PUBLIC FUNCTIONS --
|
-- PUBLIC FUNCTIONS --
|
||||||
|
|
||||||
-- initialize apisessions
|
-- initialize apisessions
|
||||||
---@param modem table
|
---@param nic nic
|
||||||
function apisessions.init(modem)
|
function apisessions.init(nic)
|
||||||
self.modem = modem
|
self.nic = nic
|
||||||
end
|
|
||||||
|
|
||||||
-- re-link the modem
|
|
||||||
---@param modem table
|
|
||||||
function apisessions.relink_modem(modem)
|
|
||||||
self.modem = modem
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- find a session by remote port
|
-- find a session by remote port
|
||||||
|
@ -6,6 +6,7 @@ require("/initenv").init_env()
|
|||||||
|
|
||||||
local crash = require("scada-common.crash")
|
local crash = require("scada-common.crash")
|
||||||
local log = require("scada-common.log")
|
local log = require("scada-common.log")
|
||||||
|
local network = require("scada-common.network")
|
||||||
local ppm = require("scada-common.ppm")
|
local ppm = require("scada-common.ppm")
|
||||||
local tcd = require("scada-common.tcd")
|
local tcd = require("scada-common.tcd")
|
||||||
local util = require("scada-common.util")
|
local util = require("scada-common.util")
|
||||||
@ -20,7 +21,7 @@ local sounder = require("coordinator.sounder")
|
|||||||
|
|
||||||
local apisessions = require("coordinator.session.apisessions")
|
local apisessions = require("coordinator.session.apisessions")
|
||||||
|
|
||||||
local COORDINATOR_VERSION = "v0.16.1"
|
local COORDINATOR_VERSION = "v0.17.1"
|
||||||
|
|
||||||
local println = util.println
|
local println = util.println
|
||||||
local println_ts = util.println_ts
|
local println_ts = util.println_ts
|
||||||
@ -30,6 +31,7 @@ local log_sys = coordinator.log_sys
|
|||||||
local log_boot = coordinator.log_boot
|
local log_boot = coordinator.log_boot
|
||||||
local log_comms = coordinator.log_comms
|
local log_comms = coordinator.log_comms
|
||||||
local log_comms_connecting = coordinator.log_comms_connecting
|
local log_comms_connecting = coordinator.log_comms_connecting
|
||||||
|
local log_crypto = coordinator.log_crypto
|
||||||
|
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
-- config validation
|
-- config validation
|
||||||
@ -131,6 +133,12 @@ local function main()
|
|||||||
-- setup communications
|
-- 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
|
-- get the communications modem
|
||||||
local modem = ppm.get_wireless_modem()
|
local modem = ppm.get_wireless_modem()
|
||||||
if modem == nil then
|
if modem == nil then
|
||||||
@ -147,8 +155,9 @@ local function main()
|
|||||||
conn_watchdog.cancel()
|
conn_watchdog.cancel()
|
||||||
log.debug("startup> conn watchdog created")
|
log.debug("startup> conn watchdog created")
|
||||||
|
|
||||||
-- start comms, open all channels
|
-- create network interface then setup comms
|
||||||
local coord_comms = coordinator.comms(COORDINATOR_VERSION, modem, config.CRD_CHANNEL, config.SVR_CHANNEL,
|
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)
|
config.PKT_CHANNEL, config.TRUSTED_RANGE, conn_watchdog)
|
||||||
log.debug("startup> comms init")
|
log.debug("startup> comms init")
|
||||||
log_comms("comms initialized")
|
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 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
|
if ui_ok then
|
||||||
-- start connection watchdog
|
-- start connection watchdog
|
||||||
conn_watchdog.feed()
|
conn_watchdog.feed()
|
||||||
@ -239,8 +246,9 @@ local function main()
|
|||||||
if type ~= nil and device ~= nil then
|
if type ~= nil and device ~= nil then
|
||||||
if type == "modem" then
|
if type == "modem" then
|
||||||
-- we only really care if this is our wireless modem
|
-- we only really care if this is our wireless modem
|
||||||
if device == modem then
|
-- if it is another modem, handle other peripheral losses separately
|
||||||
no_modem = true
|
if nic.is_modem(device) then
|
||||||
|
nic.disconnect()
|
||||||
log_sys("comms modem disconnected")
|
log_sys("comms modem disconnected")
|
||||||
println_ts("wireless modem disconnected!")
|
println_ts("wireless modem disconnected!")
|
||||||
|
|
||||||
@ -254,6 +262,7 @@ local function main()
|
|||||||
end
|
end
|
||||||
elseif type == "monitor" then
|
elseif type == "monitor" then
|
||||||
if renderer.is_monitor_used(device) then
|
if renderer.is_monitor_used(device) then
|
||||||
|
---@todo will be handled properly in #249
|
||||||
-- "halt and catch fire" style handling
|
-- "halt and catch fire" style handling
|
||||||
local msg = "lost a configured monitor, system will now exit"
|
local msg = "lost a configured monitor, system will now exit"
|
||||||
println_ts(msg)
|
println_ts(msg)
|
||||||
@ -275,9 +284,7 @@ local function main()
|
|||||||
if type == "modem" then
|
if type == "modem" then
|
||||||
if device.isWireless() then
|
if device.isWireless() then
|
||||||
-- reconnected modem
|
-- reconnected modem
|
||||||
no_modem = false
|
nic.connect(device)
|
||||||
modem = device
|
|
||||||
coord_comms.reconnect_modem(modem)
|
|
||||||
|
|
||||||
log_sys("comms modem reconnected")
|
log_sys("comms modem reconnected")
|
||||||
println_ts("wireless modem reconnected.")
|
println_ts("wireless modem reconnected.")
|
||||||
@ -289,6 +296,7 @@ local function main()
|
|||||||
log_sys("wired modem reconnected")
|
log_sys("wired modem reconnected")
|
||||||
end
|
end
|
||||||
-- elseif type == "monitor" then
|
-- elseif type == "monitor" then
|
||||||
|
---@todo will be handled properly in #249
|
||||||
-- not supported, system will exit on loss of in-use monitors
|
-- not supported, system will exit on loss of in-use monitors
|
||||||
elseif type == "speaker" then
|
elseif type == "speaker" then
|
||||||
local msg = "alarm sounder speaker reconnected"
|
local msg = "alarm sounder speaker reconnected"
|
||||||
@ -322,7 +330,7 @@ local function main()
|
|||||||
renderer.close_ui()
|
renderer.close_ui()
|
||||||
sounder.stop()
|
sounder.stop()
|
||||||
|
|
||||||
if not no_modem then
|
if nic.connected() then
|
||||||
-- try to re-connect to the supervisor
|
-- try to re-connect to the supervisor
|
||||||
if not init_connect_sv() then break end
|
if not init_connect_sv() then break end
|
||||||
ui_ok = init_start_ui()
|
ui_ok = init_start_ui()
|
||||||
@ -350,7 +358,7 @@ local function main()
|
|||||||
renderer.close_ui()
|
renderer.close_ui()
|
||||||
sounder.stop()
|
sounder.stop()
|
||||||
|
|
||||||
if not no_modem then
|
if nic.connected() then
|
||||||
-- try to re-connect to the supervisor
|
-- try to re-connect to the supervisor
|
||||||
if not init_connect_sv() then break end
|
if not init_connect_sv() then break end
|
||||||
ui_ok = init_start_ui()
|
ui_ok = init_start_ui()
|
||||||
|
@ -7,6 +7,8 @@ local flasher = require("graphics.flasher")
|
|||||||
|
|
||||||
local core = {}
|
local core = {}
|
||||||
|
|
||||||
|
core.version = "1.0.0"
|
||||||
|
|
||||||
core.flasher = flasher
|
core.flasher = flasher
|
||||||
core.events = events
|
core.events = events
|
||||||
|
|
||||||
|
18
imgen.py
18
imgen.py
@ -23,11 +23,11 @@ def dir_size(path):
|
|||||||
return total
|
return total
|
||||||
|
|
||||||
# get the version of an application at the provided path
|
# 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 = ""
|
ver = ""
|
||||||
string = "comms.version = \""
|
string = ".version = \""
|
||||||
|
|
||||||
if not is_comms:
|
if not is_lib:
|
||||||
string = "_VERSION = \""
|
string = "_VERSION = \""
|
||||||
|
|
||||||
f = open(path, "r")
|
f = open(path, "r")
|
||||||
@ -49,6 +49,8 @@ def make_manifest(size):
|
|||||||
"installer" : get_version("./ccmsi.lua"),
|
"installer" : get_version("./ccmsi.lua"),
|
||||||
"bootloader" : get_version("./startup.lua"),
|
"bootloader" : get_version("./startup.lua"),
|
||||||
"comms" : get_version("./scada-common/comms.lua", True),
|
"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"),
|
"reactor-plc" : get_version("./reactor-plc/startup.lua"),
|
||||||
"rtu" : get_version("./rtu/startup.lua"),
|
"rtu" : get_version("./rtu/startup.lua"),
|
||||||
"supervisor" : get_version("./supervisor/startup.lua"),
|
"supervisor" : get_version("./supervisor/startup.lua"),
|
||||||
@ -69,11 +71,11 @@ def make_manifest(size):
|
|||||||
"pocket" : list_files("./pocket"),
|
"pocket" : list_files("./pocket"),
|
||||||
},
|
},
|
||||||
"depends" : {
|
"depends" : {
|
||||||
"reactor-plc" : [ "system", "common", "graphics" ],
|
"reactor-plc" : [ "system", "common", "graphics", "lockbox" ],
|
||||||
"rtu" : [ "system", "common", "graphics" ],
|
"rtu" : [ "system", "common", "graphics", "lockbox" ],
|
||||||
"supervisor" : [ "system", "common", "graphics" ],
|
"supervisor" : [ "system", "common", "graphics", "lockbox" ],
|
||||||
"coordinator" : [ "system", "common", "graphics" ],
|
"coordinator" : [ "system", "common", "graphics", "lockbox" ],
|
||||||
"pocket" : [ "system", "common", "graphics" ]
|
"pocket" : [ "system", "common", "graphics", "lockbox" ]
|
||||||
},
|
},
|
||||||
"sizes" : {
|
"sizes" : {
|
||||||
# manifest file estimate
|
# manifest file estimate
|
||||||
|
File diff suppressed because one or more lines are too long
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
||||||
|
|
@ -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;
|
|
@ -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;
|
|
||||||
|
|
@ -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;
|
|
201
lockbox/digest/md5.lua
Normal file
201
lockbox/digest/md5.lua
Normal file
@ -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;
|
@ -1,5 +1,8 @@
|
|||||||
local Lockbox = {};
|
local Lockbox = {};
|
||||||
|
|
||||||
|
-- cc-mek-scada lockbox version
|
||||||
|
Lockbox.version = "1.0"
|
||||||
|
|
||||||
--[[
|
--[[
|
||||||
package.path = "./?.lua;"
|
package.path = "./?.lua;"
|
||||||
.. "./cipher/?.lua;"
|
.. "./cipher/?.lua;"
|
||||||
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -1,25 +1,19 @@
|
|||||||
local ok, e
|
-- modified (simplified) for ComputerCraft
|
||||||
ok = nil
|
|
||||||
if not ok then
|
local ok, e = nil, nil
|
||||||
ok, e = pcall(require, "bit") -- the LuaJIT one ?
|
|
||||||
end
|
|
||||||
if not ok then
|
if not ok then
|
||||||
ok, e = pcall(require, "bit32") -- Lua 5.2
|
ok, e = pcall(require, "bit32") -- Lua 5.2
|
||||||
end
|
end
|
||||||
|
|
||||||
if not ok then
|
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
|
end
|
||||||
|
|
||||||
if not ok then
|
if not ok then
|
||||||
error("no bitwise support found", 2)
|
error("no bitwise support found", 2)
|
||||||
end
|
end
|
||||||
|
|
||||||
assert(type(e) == "table", "invalid bit module")
|
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
|
return e
|
||||||
|
@ -10,6 +10,10 @@ config.PKT_CHANNEL = 16244
|
|||||||
config.TRUSTED_RANGE = 0
|
config.TRUSTED_RANGE = 0
|
||||||
-- time in seconds (>= 2) before assuming a remote device is no longer active
|
-- time in seconds (>= 2) before assuming a remote device is no longer active
|
||||||
config.COMMS_TIMEOUT = 5
|
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
|
-- log path
|
||||||
config.LOG_PATH = "/log.txt"
|
config.LOG_PATH = "/log.txt"
|
||||||
|
@ -17,14 +17,14 @@ local pocket = {}
|
|||||||
-- pocket coordinator + supervisor communications
|
-- pocket coordinator + supervisor communications
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param version string pocket version
|
---@param version string pocket version
|
||||||
---@param modem table modem device
|
---@param nic nic network interface device
|
||||||
---@param pkt_channel integer pocket comms channel
|
---@param pkt_channel integer pocket comms channel
|
||||||
---@param svr_channel integer supervisor access channel
|
---@param svr_channel integer supervisor access channel
|
||||||
---@param crd_channel integer coordinator access channel
|
---@param crd_channel integer coordinator access channel
|
||||||
---@param range integer trusted device connection range
|
---@param range integer trusted device connection range
|
||||||
---@param sv_watchdog watchdog
|
---@param sv_watchdog watchdog
|
||||||
---@param api_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 = {
|
local self = {
|
||||||
sv = {
|
sv = {
|
||||||
linked = false,
|
linked = false,
|
||||||
@ -47,13 +47,9 @@ function pocket.comms(version, modem, pkt_channel, svr_channel, crd_channel, ran
|
|||||||
|
|
||||||
-- PRIVATE FUNCTIONS --
|
-- PRIVATE FUNCTIONS --
|
||||||
|
|
||||||
-- configure modem channels
|
-- configure network channels
|
||||||
local function _conf_channels()
|
nic.closeAll()
|
||||||
modem.closeAll()
|
nic.open(pkt_channel)
|
||||||
modem.open(pkt_channel)
|
|
||||||
end
|
|
||||||
|
|
||||||
_conf_channels()
|
|
||||||
|
|
||||||
-- send a management packet to the supervisor
|
-- send a management packet to the supervisor
|
||||||
---@param msg_type SCADA_MGMT_TYPE
|
---@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)
|
pkt.make(msg_type, msg)
|
||||||
s_pkt.make(self.sv.addr, self.sv.seq_num, PROTOCOL.SCADA_MGMT, pkt.raw_sendable())
|
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
|
self.sv.seq_num = self.sv.seq_num + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -79,7 +75,7 @@ function pocket.comms(version, modem, pkt_channel, svr_channel, crd_channel, ran
|
|||||||
pkt.make(msg_type, msg)
|
pkt.make(msg_type, msg)
|
||||||
s_pkt.make(self.api.addr, self.api.seq_num, PROTOCOL.SCADA_MGMT, pkt.raw_sendable())
|
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
|
self.api.seq_num = self.api.seq_num + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -93,7 +89,7 @@ function pocket.comms(version, modem, pkt_channel, svr_channel, crd_channel, ran
|
|||||||
-- pkt.make(msg_type, msg)
|
-- pkt.make(msg_type, msg)
|
||||||
-- s_pkt.make(self.api.addr, self.api.seq_num, PROTOCOL.COORD_API, pkt.raw_sendable())
|
-- 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
|
-- self.api.seq_num = self.api.seq_num + 1
|
||||||
-- end
|
-- end
|
||||||
|
|
||||||
@ -124,13 +120,6 @@ function pocket.comms(version, modem, pkt_channel, svr_channel, crd_channel, ran
|
|||||||
---@class pocket_comms
|
---@class pocket_comms
|
||||||
local public = {}
|
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
|
-- close connection to the supervisor
|
||||||
function public.close_sv()
|
function public.close_sv()
|
||||||
sv_watchdog.cancel()
|
sv_watchdog.cancel()
|
||||||
@ -189,13 +178,10 @@ function pocket.comms(version, modem, pkt_channel, svr_channel, crd_channel, ran
|
|||||||
---@param distance integer
|
---@param distance integer
|
||||||
---@return mgmt_frame|capi_frame|nil packet
|
---@return mgmt_frame|capi_frame|nil packet
|
||||||
function public.parse_packet(side, sender, reply_to, message, distance)
|
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 pkt = nil
|
||||||
local s_pkt = comms.scada_packet()
|
|
||||||
|
|
||||||
-- parse packet as generic SCADA packet
|
if s_pkt then
|
||||||
s_pkt.receive(side, sender, reply_to, message, distance)
|
|
||||||
|
|
||||||
if s_pkt.is_valid() then
|
|
||||||
-- get as SCADA management packet
|
-- get as SCADA management packet
|
||||||
if s_pkt.protocol() == PROTOCOL.SCADA_MGMT then
|
if s_pkt.protocol() == PROTOCOL.SCADA_MGMT then
|
||||||
local mgmt_pkt = comms.mgmt_packet()
|
local mgmt_pkt = comms.mgmt_packet()
|
||||||
|
@ -6,6 +6,7 @@ require("/initenv").init_env()
|
|||||||
|
|
||||||
local crash = require("scada-common.crash")
|
local crash = require("scada-common.crash")
|
||||||
local log = require("scada-common.log")
|
local log = require("scada-common.log")
|
||||||
|
local network = require("scada-common.network")
|
||||||
local ppm = require("scada-common.ppm")
|
local ppm = require("scada-common.ppm")
|
||||||
local tcd = require("scada-common.tcd")
|
local tcd = require("scada-common.tcd")
|
||||||
local util = require("scada-common.util")
|
local util = require("scada-common.util")
|
||||||
@ -17,7 +18,7 @@ local coreio = require("pocket.coreio")
|
|||||||
local pocket = require("pocket.pocket")
|
local pocket = require("pocket.pocket")
|
||||||
local renderer = require("pocket.renderer")
|
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 = util.println
|
||||||
local println_ts = util.println_ts
|
local println_ts = util.println_ts
|
||||||
@ -67,6 +68,11 @@ local function main()
|
|||||||
-- setup communications & clocks
|
-- 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)
|
coreio.report_link_state(coreio.LINK_STATE.UNLINKED)
|
||||||
|
|
||||||
-- get the communications modem
|
-- get the communications modem
|
||||||
@ -88,8 +94,9 @@ local function main()
|
|||||||
|
|
||||||
log.debug("startup> conn watchdogs created")
|
log.debug("startup> conn watchdogs created")
|
||||||
|
|
||||||
-- start comms, open all channels
|
-- create network interface then setup comms
|
||||||
local pocket_comms = pocket.comms(POCKET_VERSION, modem, config.PKT_CHANNEL, config.SVR_CHANNEL,
|
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)
|
config.CRD_CHANNEL, config.TRUSTED_RANGE, conn_wd.sv, conn_wd.api)
|
||||||
log.debug("startup> comms init")
|
log.debug("startup> comms init")
|
||||||
|
|
||||||
|
@ -17,6 +17,10 @@ config.PLC_CHANNEL = 16241
|
|||||||
config.TRUSTED_RANGE = 0
|
config.TRUSTED_RANGE = 0
|
||||||
-- time in seconds (>= 2) before assuming a remote device is no longer active
|
-- time in seconds (>= 2) before assuming a remote device is no longer active
|
||||||
config.COMMS_TIMEOUT = 5
|
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
|
-- log path
|
||||||
config.LOG_PATH = "/log.txt"
|
config.LOG_PATH = "/log.txt"
|
||||||
|
@ -445,14 +445,14 @@ end
|
|||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param id integer reactor ID
|
---@param id integer reactor ID
|
||||||
---@param version string PLC version
|
---@param version string PLC version
|
||||||
---@param modem table modem device
|
---@param nic nic network interface device
|
||||||
---@param plc_channel integer PLC comms channel
|
---@param plc_channel integer PLC comms channel
|
||||||
---@param svr_channel integer supervisor server channel
|
---@param svr_channel integer supervisor server channel
|
||||||
---@param range integer trusted device connection range
|
---@param range integer trusted device connection range
|
||||||
---@param reactor table reactor device
|
---@param reactor table reactor device
|
||||||
---@param rps rps RPS reference
|
---@param rps rps RPS reference
|
||||||
---@param conn_watchdog watchdog watchdog 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 = {
|
local self = {
|
||||||
sv_addr = comms.BROADCAST,
|
sv_addr = comms.BROADCAST,
|
||||||
seq_num = 0,
|
seq_num = 0,
|
||||||
@ -470,13 +470,9 @@ function plc.comms(id, version, modem, plc_channel, svr_channel, range, reactor,
|
|||||||
|
|
||||||
-- PRIVATE FUNCTIONS --
|
-- PRIVATE FUNCTIONS --
|
||||||
|
|
||||||
-- configure modem channels
|
-- configure network channels
|
||||||
local function _conf_channels()
|
nic.closeAll()
|
||||||
modem.closeAll()
|
nic.open(plc_channel)
|
||||||
modem.open(plc_channel)
|
|
||||||
end
|
|
||||||
|
|
||||||
_conf_channels()
|
|
||||||
|
|
||||||
-- send an RPLC packet
|
-- send an RPLC packet
|
||||||
---@param msg_type RPLC_TYPE
|
---@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)
|
r_pkt.make(id, msg_type, msg)
|
||||||
s_pkt.make(self.sv_addr, self.seq_num, PROTOCOL.RPLC, r_pkt.raw_sendable())
|
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
|
self.seq_num = self.seq_num + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -502,7 +498,7 @@ function plc.comms(id, version, modem, plc_channel, svr_channel, range, reactor,
|
|||||||
m_pkt.make(msg_type, msg)
|
m_pkt.make(msg_type, msg)
|
||||||
s_pkt.make(self.sv_addr, self.seq_num, PROTOCOL.SCADA_MGMT, m_pkt.raw_sendable())
|
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
|
self.seq_num = self.seq_num + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -639,7 +635,7 @@ function plc.comms(id, version, modem, plc_channel, svr_channel, range, reactor,
|
|||||||
|
|
||||||
parallel.waitForAll(table.unpack(tasks))
|
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)
|
_send(RPLC_TYPE.MEK_STRUCT, mek_data)
|
||||||
self.resend_build = false
|
self.resend_build = false
|
||||||
end
|
end
|
||||||
@ -650,13 +646,6 @@ function plc.comms(id, version, modem, plc_channel, svr_channel, range, reactor,
|
|||||||
---@class plc_comms
|
---@class plc_comms
|
||||||
local public = {}
|
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
|
-- reconnect a newly connected reactor
|
||||||
---@param new_reactor table
|
---@param new_reactor table
|
||||||
function public.reconnect_reactor(new_reactor)
|
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
|
---@param distance integer
|
||||||
---@return rplc_frame|mgmt_frame|nil packet
|
---@return rplc_frame|mgmt_frame|nil packet
|
||||||
function public.parse_packet(side, sender, reply_to, message, distance)
|
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 pkt = nil
|
||||||
local s_pkt = comms.scada_packet()
|
|
||||||
|
|
||||||
-- parse packet as generic SCADA packet
|
if s_pkt then
|
||||||
s_pkt.receive(side, sender, reply_to, message, distance)
|
|
||||||
|
|
||||||
if s_pkt.is_valid() then
|
|
||||||
-- get as RPLC packet
|
-- get as RPLC packet
|
||||||
if s_pkt.protocol() == PROTOCOL.RPLC then
|
if s_pkt.protocol() == PROTOCOL.RPLC then
|
||||||
local rplc_pkt = comms.rplc_packet()
|
local rplc_pkt = comms.rplc_packet()
|
||||||
@ -836,7 +822,7 @@ function plc.comms(id, version, modem, plc_channel, svr_channel, range, reactor,
|
|||||||
success = true
|
success = true
|
||||||
else
|
else
|
||||||
reactor.setBurnRate(burn_rate)
|
reactor.setBurnRate(burn_rate)
|
||||||
success = not reactor.__p_is_faulted()
|
success = reactor.__p_is_ok()
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
log.debug(burn_rate .. " rate outside of 0 < x <= " .. self.max_burn_rate)
|
log.debug(burn_rate .. " rate outside of 0 < x <= " .. self.max_burn_rate)
|
||||||
|
@ -8,6 +8,7 @@ local comms = require("scada-common.comms")
|
|||||||
local crash = require("scada-common.crash")
|
local crash = require("scada-common.crash")
|
||||||
local log = require("scada-common.log")
|
local log = require("scada-common.log")
|
||||||
local mqueue = require("scada-common.mqueue")
|
local mqueue = require("scada-common.mqueue")
|
||||||
|
local network = require("scada-common.network")
|
||||||
local ppm = require("scada-common.ppm")
|
local ppm = require("scada-common.ppm")
|
||||||
local rsio = require("scada-common.rsio")
|
local rsio = require("scada-common.rsio")
|
||||||
local util = require("scada-common.util")
|
local util = require("scada-common.util")
|
||||||
@ -18,7 +19,7 @@ local plc = require("reactor-plc.plc")
|
|||||||
local renderer = require("reactor-plc.renderer")
|
local renderer = require("reactor-plc.renderer")
|
||||||
local threads = require("reactor-plc.threads")
|
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 = util.println
|
||||||
local println_ts = util.println_ts
|
local println_ts = util.println_ts
|
||||||
@ -79,6 +80,11 @@ local function main()
|
|||||||
-- mount connected devices
|
-- mount connected devices
|
||||||
ppm.mount_all()
|
ppm.mount_all()
|
||||||
|
|
||||||
|
-- message authentication init
|
||||||
|
if type(config.AUTH_KEY) == "string" then
|
||||||
|
network.init_mac(config.AUTH_KEY)
|
||||||
|
end
|
||||||
|
|
||||||
-- shared memory across threads
|
-- shared memory across threads
|
||||||
---@class plc_shared_memory
|
---@class plc_shared_memory
|
||||||
local __shared_memory = {
|
local __shared_memory = {
|
||||||
@ -91,10 +97,10 @@ local function main()
|
|||||||
init_ok = true,
|
init_ok = true,
|
||||||
fp_ok = false,
|
fp_ok = false,
|
||||||
shutdown = false,
|
shutdown = false,
|
||||||
degraded = false,
|
degraded = true,
|
||||||
reactor_formed = true,
|
reactor_formed = true,
|
||||||
no_reactor = false,
|
no_reactor = true,
|
||||||
no_modem = false
|
no_modem = true
|
||||||
},
|
},
|
||||||
|
|
||||||
-- control setpoints
|
-- control setpoints
|
||||||
@ -113,6 +119,7 @@ local function main()
|
|||||||
-- system objects
|
-- system objects
|
||||||
plc_sys = {
|
plc_sys = {
|
||||||
rps = nil, ---@type rps
|
rps = nil, ---@type rps
|
||||||
|
nic = nil, ---@type nic
|
||||||
plc_comms = nil, ---@type plc_comms
|
plc_comms = nil, ---@type plc_comms
|
||||||
conn_watchdog = nil ---@type watchdog
|
conn_watchdog = nil ---@type watchdog
|
||||||
},
|
},
|
||||||
@ -130,14 +137,17 @@ local function main()
|
|||||||
|
|
||||||
local plc_state = __shared_memory.plc_state
|
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
|
-- 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");
|
println("init> fission reactor not found");
|
||||||
log.warning("init> no reactor on startup")
|
log.warning("init> no reactor on startup")
|
||||||
|
|
||||||
plc_state.init_ok = false
|
plc_state.init_ok = false
|
||||||
plc_state.degraded = true
|
plc_state.degraded = true
|
||||||
plc_state.no_reactor = true
|
|
||||||
elseif not smem_dev.reactor.isFormed() then
|
elseif not smem_dev.reactor.isFormed() then
|
||||||
println("init> fission reactor not formed");
|
println("init> fission reactor not formed");
|
||||||
log.warning("init> reactor logic adapter present, but reactor is not formed")
|
log.warning("init> reactor logic adapter present, but reactor is not formed")
|
||||||
@ -147,7 +157,7 @@ local function main()
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- modem is required if networked
|
-- 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")
|
println("init> wireless modem not found")
|
||||||
log.warning("init> no wireless modem on startup")
|
log.warning("init> no wireless modem on startup")
|
||||||
|
|
||||||
@ -158,7 +168,6 @@ local function main()
|
|||||||
|
|
||||||
plc_state.init_ok = false
|
plc_state.init_ok = false
|
||||||
plc_state.degraded = true
|
plc_state.degraded = true
|
||||||
plc_state.no_modem = true
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- print a log message to the terminal as long as the UI isn't running
|
-- 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)
|
smem_sys.conn_watchdog = util.new_watchdog(config.COMMS_TIMEOUT)
|
||||||
log.debug("init> conn watchdog started")
|
log.debug("init> conn watchdog started")
|
||||||
|
|
||||||
-- start comms
|
-- create network interface then setup comms
|
||||||
smem_sys.plc_comms = plc.comms(config.REACTOR_ID, R_PLC_VERSION, smem_dev.modem, config.PLC_CHANNEL, config.SVR_CHANNEL,
|
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)
|
config.TRUSTED_RANGE, smem_dev.reactor, smem_sys.rps, smem_sys.conn_watchdog)
|
||||||
log.debug("init> comms init")
|
log.debug("init> comms init")
|
||||||
else
|
else
|
||||||
|
@ -59,6 +59,7 @@ function threads.thread__main(smem, init)
|
|||||||
while true do
|
while true do
|
||||||
-- get plc_sys fields (may have been set late due to degraded boot)
|
-- get plc_sys fields (may have been set late due to degraded boot)
|
||||||
local rps = smem.plc_sys.rps
|
local rps = smem.plc_sys.rps
|
||||||
|
local nic = smem.plc_sys.nic
|
||||||
local plc_comms = smem.plc_sys.plc_comms
|
local plc_comms = smem.plc_sys.plc_comms
|
||||||
local conn_watchdog = smem.plc_sys.conn_watchdog
|
local conn_watchdog = smem.plc_sys.conn_watchdog
|
||||||
|
|
||||||
@ -66,6 +67,7 @@ function threads.thread__main(smem, init)
|
|||||||
|
|
||||||
-- handle event
|
-- handle event
|
||||||
if event == "timer" and loop_clock.is_clock(param1) then
|
if event == "timer" and loop_clock.is_clock(param1) then
|
||||||
|
-- note: loop clock is only running if init_ok = true
|
||||||
-- blink heartbeat indicator
|
-- blink heartbeat indicator
|
||||||
databus.heartbeat()
|
databus.heartbeat()
|
||||||
|
|
||||||
@ -75,7 +77,7 @@ function threads.thread__main(smem, init)
|
|||||||
loop_clock.start()
|
loop_clock.start()
|
||||||
|
|
||||||
-- send updated data
|
-- send updated data
|
||||||
if not plc_state.no_modem then
|
if nic.connected() then
|
||||||
if plc_comms.is_linked() then
|
if plc_comms.is_linked() then
|
||||||
smem.q.mq_comms_tx.push_command(MQ__COMM_CMD.SEND_STATUS)
|
smem.q.mq_comms_tx.push_command(MQ__COMM_CMD.SEND_STATUS)
|
||||||
else
|
else
|
||||||
@ -114,7 +116,7 @@ function threads.thread__main(smem, init)
|
|||||||
smem.q.mq_rps.push_command(MQ__RPS_CMD.SCRAM)
|
smem.q.mq_rps.push_command(MQ__RPS_CMD.SCRAM)
|
||||||
|
|
||||||
-- determine if we are still in a degraded state
|
-- 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
|
plc_state.degraded = false
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -144,7 +146,7 @@ function threads.thread__main(smem, init)
|
|||||||
|
|
||||||
-- update indicators
|
-- update indicators
|
||||||
databus.tx_hw_status(plc_state)
|
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
|
-- got a packet
|
||||||
local packet = plc_comms.parse_packet(param1, param2, param3, param4, param5)
|
local packet = plc_comms.parse_packet(param1, param2, param3, param4, param5)
|
||||||
if packet ~= nil then
|
if packet ~= nil then
|
||||||
@ -171,7 +173,9 @@ function threads.thread__main(smem, init)
|
|||||||
plc_state.degraded = true
|
plc_state.degraded = true
|
||||||
elseif networked and type == "modem" then
|
elseif networked and type == "modem" then
|
||||||
-- we only care if this is our wireless modem
|
-- 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!")
|
println_ts("comms modem disconnected!")
|
||||||
log.error("comms modem disconnected")
|
log.error("comms modem disconnected")
|
||||||
|
|
||||||
@ -199,12 +203,11 @@ function threads.thread__main(smem, init)
|
|||||||
if type == "fissionReactorLogicAdapter" then
|
if type == "fissionReactorLogicAdapter" then
|
||||||
-- reconnected reactor
|
-- reconnected reactor
|
||||||
plc_dev.reactor = device
|
plc_dev.reactor = device
|
||||||
|
plc_state.no_reactor = false
|
||||||
|
|
||||||
println_ts("reactor reconnected.")
|
println_ts("reactor reconnected.")
|
||||||
log.info("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
|
-- 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
|
-- RPS will identify if it isn't and this will get set false later
|
||||||
plc_state.reactor_formed = true
|
plc_state.reactor_formed = true
|
||||||
@ -230,14 +233,12 @@ function threads.thread__main(smem, init)
|
|||||||
if device.isWireless() then
|
if device.isWireless() then
|
||||||
-- reconnected modem
|
-- reconnected modem
|
||||||
plc_dev.modem = device
|
plc_dev.modem = device
|
||||||
|
plc_state.no_modem = false
|
||||||
|
|
||||||
if plc_state.init_ok then
|
if plc_state.init_ok then nic.connect(device) end
|
||||||
plc_comms.reconnect_modem(plc_dev.modem)
|
|
||||||
end
|
|
||||||
|
|
||||||
println_ts("wireless modem reconnected.")
|
println_ts("wireless modem reconnected.")
|
||||||
log.info("comms modem reconnected")
|
log.info("comms modem reconnected")
|
||||||
plc_state.no_modem = false
|
|
||||||
|
|
||||||
-- determine if we are still in a degraded state
|
-- determine if we are still in a degraded state
|
||||||
if not plc_state.no_reactor then
|
if not plc_state.no_reactor then
|
||||||
@ -709,9 +710,7 @@ function threads.thread__setpoint_control(smem)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- if ramping completed or was aborted, reset last burn setpoint so that if it is requested again it will be re-attempted
|
-- 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
|
if not setpoints.burn_rate_en then last_burn_sp = 0 end
|
||||||
last_burn_sp = 0
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- check for termination request
|
-- check for termination request
|
||||||
|
@ -10,6 +10,10 @@ config.RTU_CHANNEL = 16242
|
|||||||
config.TRUSTED_RANGE = 0
|
config.TRUSTED_RANGE = 0
|
||||||
-- time in seconds (>= 2) before assuming a remote device is no longer active
|
-- time in seconds (>= 2) before assuming a remote device is no longer active
|
||||||
config.COMMS_TIMEOUT = 5
|
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
|
-- log path
|
||||||
config.LOG_PATH = "/log.txt"
|
config.LOG_PATH = "/log.txt"
|
||||||
|
@ -10,6 +10,7 @@ function boilerv_rtu.new(boiler)
|
|||||||
local unit = rtu.init_unit()
|
local unit = rtu.init_unit()
|
||||||
|
|
||||||
-- disable auto fault clearing
|
-- disable auto fault clearing
|
||||||
|
boiler.__p_clear_fault()
|
||||||
boiler.__p_disable_afc()
|
boiler.__p_disable_afc()
|
||||||
|
|
||||||
-- discrete inputs --
|
-- discrete inputs --
|
||||||
|
@ -10,6 +10,7 @@ function envd_rtu.new(envd)
|
|||||||
local unit = rtu.init_unit()
|
local unit = rtu.init_unit()
|
||||||
|
|
||||||
-- disable auto fault clearing
|
-- disable auto fault clearing
|
||||||
|
envd.__p_clear_fault()
|
||||||
envd.__p_disable_afc()
|
envd.__p_disable_afc()
|
||||||
|
|
||||||
-- discrete inputs --
|
-- discrete inputs --
|
||||||
|
@ -10,6 +10,7 @@ function imatrix_rtu.new(imatrix)
|
|||||||
local unit = rtu.init_unit()
|
local unit = rtu.init_unit()
|
||||||
|
|
||||||
-- disable auto fault clearing
|
-- disable auto fault clearing
|
||||||
|
imatrix.__p_clear_fault()
|
||||||
imatrix.__p_disable_afc()
|
imatrix.__p_disable_afc()
|
||||||
|
|
||||||
-- discrete inputs --
|
-- discrete inputs --
|
||||||
|
@ -10,6 +10,7 @@ function sna_rtu.new(sna)
|
|||||||
local unit = rtu.init_unit()
|
local unit = rtu.init_unit()
|
||||||
|
|
||||||
-- disable auto fault clearing
|
-- disable auto fault clearing
|
||||||
|
sna.__p_clear_fault()
|
||||||
sna.__p_disable_afc()
|
sna.__p_disable_afc()
|
||||||
|
|
||||||
-- discrete inputs --
|
-- discrete inputs --
|
||||||
|
@ -10,6 +10,7 @@ function sps_rtu.new(sps)
|
|||||||
local unit = rtu.init_unit()
|
local unit = rtu.init_unit()
|
||||||
|
|
||||||
-- disable auto fault clearing
|
-- disable auto fault clearing
|
||||||
|
sps.__p_clear_fault()
|
||||||
sps.__p_disable_afc()
|
sps.__p_disable_afc()
|
||||||
|
|
||||||
-- discrete inputs --
|
-- discrete inputs --
|
||||||
|
@ -10,6 +10,7 @@ function turbinev_rtu.new(turbine)
|
|||||||
local unit = rtu.init_unit()
|
local unit = rtu.init_unit()
|
||||||
|
|
||||||
-- disable auto fault clearing
|
-- disable auto fault clearing
|
||||||
|
turbine.__p_clear_fault()
|
||||||
turbine.__p_disable_afc()
|
turbine.__p_disable_afc()
|
||||||
|
|
||||||
-- discrete inputs --
|
-- discrete inputs --
|
||||||
|
30
rtu/rtu.lua
30
rtu/rtu.lua
@ -158,12 +158,12 @@ end
|
|||||||
-- RTU Communications
|
-- RTU Communications
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param version string RTU version
|
---@param version string RTU version
|
||||||
---@param modem table modem device
|
---@param nic nic network interface device
|
||||||
---@param rtu_channel integer PLC comms channel
|
---@param rtu_channel integer PLC comms channel
|
||||||
---@param svr_channel integer supervisor server channel
|
---@param svr_channel integer supervisor server channel
|
||||||
---@param range integer trusted device connection range
|
---@param range integer trusted device connection range
|
||||||
---@param conn_watchdog watchdog watchdog reference
|
---@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 = {
|
local self = {
|
||||||
sv_addr = comms.BROADCAST,
|
sv_addr = comms.BROADCAST,
|
||||||
seq_num = 0,
|
seq_num = 0,
|
||||||
@ -179,12 +179,8 @@ function rtu.comms(version, modem, rtu_channel, svr_channel, range, conn_watchdo
|
|||||||
-- PRIVATE FUNCTIONS --
|
-- PRIVATE FUNCTIONS --
|
||||||
|
|
||||||
-- configure modem channels
|
-- configure modem channels
|
||||||
local function _conf_channels()
|
nic.closeAll()
|
||||||
modem.closeAll()
|
nic.open(rtu_channel)
|
||||||
modem.open(rtu_channel)
|
|
||||||
end
|
|
||||||
|
|
||||||
_conf_channels()
|
|
||||||
|
|
||||||
-- send a scada management packet
|
-- send a scada management packet
|
||||||
---@param msg_type SCADA_MGMT_TYPE
|
---@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)
|
m_pkt.make(msg_type, msg)
|
||||||
s_pkt.make(self.sv_addr, self.seq_num, PROTOCOL.SCADA_MGMT, m_pkt.raw_sendable())
|
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
|
self.seq_num = self.seq_num + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -240,17 +236,10 @@ function rtu.comms(version, modem, rtu_channel, svr_channel, range, conn_watchdo
|
|||||||
function public.send_modbus(m_pkt)
|
function public.send_modbus(m_pkt)
|
||||||
local s_pkt = comms.scada_packet()
|
local s_pkt = comms.scada_packet()
|
||||||
s_pkt.make(self.sv_addr, self.seq_num, PROTOCOL.MODBUS_TCP, m_pkt.raw_sendable())
|
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
|
self.seq_num = self.seq_num + 1
|
||||||
end
|
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
|
-- unlink from the server
|
||||||
---@param rtu_state rtu_state
|
---@param rtu_state rtu_state
|
||||||
function public.unlink(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
|
---@param distance integer
|
||||||
---@return modbus_frame|mgmt_frame|nil packet
|
---@return modbus_frame|mgmt_frame|nil packet
|
||||||
function public.parse_packet(side, sender, reply_to, message, distance)
|
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 pkt = nil
|
||||||
local s_pkt = comms.scada_packet()
|
|
||||||
|
|
||||||
-- parse packet as generic SCADA packet
|
if s_pkt then
|
||||||
s_pkt.receive(side, sender, reply_to, message, distance)
|
|
||||||
|
|
||||||
if s_pkt.is_valid() then
|
|
||||||
-- get as MODBUS TCP packet
|
-- get as MODBUS TCP packet
|
||||||
if s_pkt.protocol() == PROTOCOL.MODBUS_TCP then
|
if s_pkt.protocol() == PROTOCOL.MODBUS_TCP then
|
||||||
local m_pkt = comms.modbus_packet()
|
local m_pkt = comms.modbus_packet()
|
||||||
|
@ -8,6 +8,7 @@ local comms = require("scada-common.comms")
|
|||||||
local crash = require("scada-common.crash")
|
local crash = require("scada-common.crash")
|
||||||
local log = require("scada-common.log")
|
local log = require("scada-common.log")
|
||||||
local mqueue = require("scada-common.mqueue")
|
local mqueue = require("scada-common.mqueue")
|
||||||
|
local network = require("scada-common.network")
|
||||||
local ppm = require("scada-common.ppm")
|
local ppm = require("scada-common.ppm")
|
||||||
local rsio = require("scada-common.rsio")
|
local rsio = require("scada-common.rsio")
|
||||||
local types = require("scada-common.types")
|
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 sps_rtu = require("rtu.dev.sps_rtu")
|
||||||
local turbinev_rtu = require("rtu.dev.turbinev_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_TYPE = types.RTU_UNIT_TYPE
|
||||||
local RTU_UNIT_HW_STATE = databus.RTU_UNIT_HW_STATE
|
local RTU_UNIT_HW_STATE = databus.RTU_UNIT_HW_STATE
|
||||||
@ -81,6 +82,19 @@ local function main()
|
|||||||
-- mount connected devices
|
-- mount connected devices
|
||||||
ppm.mount_all()
|
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
|
---@class rtu_shared_memory
|
||||||
local __shared_memory = {
|
local __shared_memory = {
|
||||||
-- RTU system state flags
|
-- RTU system state flags
|
||||||
@ -91,16 +105,12 @@ local function main()
|
|||||||
shutdown = false
|
shutdown = false
|
||||||
},
|
},
|
||||||
|
|
||||||
-- core RTU devices
|
|
||||||
rtu_dev = {
|
|
||||||
modem = ppm.get_wireless_modem()
|
|
||||||
},
|
|
||||||
|
|
||||||
-- system objects
|
-- system objects
|
||||||
rtu_sys = {
|
rtu_sys = {
|
||||||
|
nic = network.nic(modem),
|
||||||
rtu_comms = nil, ---@type rtu_comms
|
rtu_comms = nil, ---@type rtu_comms
|
||||||
conn_watchdog = nil, ---@type watchdog
|
conn_watchdog = nil, ---@type watchdog
|
||||||
units = {} ---@type table
|
units = {}
|
||||||
},
|
},
|
||||||
|
|
||||||
-- message queues
|
-- message queues
|
||||||
@ -109,16 +119,8 @@ local function main()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
local smem_dev = __shared_memory.rtu_dev
|
|
||||||
local smem_sys = __shared_memory.rtu_sys
|
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)
|
databus.tx_hw_modem(true)
|
||||||
|
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
@ -244,6 +246,7 @@ local function main()
|
|||||||
device = capabilities, ---@type table use device field for redstone ports
|
device = capabilities, ---@type table use device field for redstone ports
|
||||||
is_multiblock = false, ---@type boolean
|
is_multiblock = false, ---@type boolean
|
||||||
formed = nil, ---@type boolean|nil
|
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
|
rtu = rs_rtu, ---@type rtu_device|rtu_rs_device
|
||||||
modbus_io = modbus.new(rs_rtu, false),
|
modbus_io = modbus.new(rs_rtu, false),
|
||||||
pkt_queue = nil, ---@type mqueue|nil
|
pkt_queue = nil, ---@type mqueue|nil
|
||||||
@ -261,7 +264,7 @@ local function main()
|
|||||||
|
|
||||||
unit.uid = #units
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -403,6 +406,7 @@ local function main()
|
|||||||
device = device, ---@type table
|
device = device, ---@type table
|
||||||
is_multiblock = is_multiblock, ---@type boolean
|
is_multiblock = is_multiblock, ---@type boolean
|
||||||
formed = formed, ---@type boolean|nil
|
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
|
rtu = rtu_iface, ---@type rtu_device|rtu_rs_device
|
||||||
modbus_io = modbus.new(rtu_iface, true),
|
modbus_io = modbus.new(rtu_iface, true),
|
||||||
pkt_queue = mqueue.new(), ---@type mqueue|nil
|
pkt_queue = mqueue.new(), ---@type mqueue|nil
|
||||||
@ -422,19 +426,21 @@ local function main()
|
|||||||
|
|
||||||
rtu_unit.uid = #units
|
rtu_unit.uid = #units
|
||||||
|
|
||||||
-- report hardware status
|
-- determine hardware status
|
||||||
if rtu_unit.type == RTU_UNIT_TYPE.VIRTUAL then
|
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
|
else
|
||||||
if rtu_unit.is_multiblock then
|
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
|
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
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- report hardware status
|
||||||
|
databus.tx_unit_hw_status(rtu_unit.uid, rtu_unit.hw_state)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- we made it through all that trusting-user-to-write-a-config-file chaos
|
-- 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")
|
log.debug("startup> conn watchdog started")
|
||||||
|
|
||||||
-- setup comms
|
-- 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)
|
config.TRUSTED_RANGE, smem_sys.conn_watchdog)
|
||||||
log.debug("startup> comms init")
|
log.debug("startup> comms init")
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ function threads.thread__main(smem)
|
|||||||
|
|
||||||
-- load in from shared memory
|
-- load in from shared memory
|
||||||
local rtu_state = smem.rtu_state
|
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 rtu_comms = smem.rtu_sys.rtu_comms
|
||||||
local conn_watchdog = smem.rtu_sys.conn_watchdog
|
local conn_watchdog = smem.rtu_sys.conn_watchdog
|
||||||
local units = smem.rtu_sys.units
|
local units = smem.rtu_sys.units
|
||||||
@ -93,7 +93,9 @@ function threads.thread__main(smem)
|
|||||||
if type ~= nil and device ~= nil then
|
if type ~= nil and device ~= nil then
|
||||||
if type == "modem" then
|
if type == "modem" then
|
||||||
-- we only care if this is our wireless modem
|
-- 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!")
|
println_ts("wireless modem disconnected!")
|
||||||
log.warning("comms modem disconnected!")
|
log.warning("comms modem disconnected!")
|
||||||
|
|
||||||
@ -105,13 +107,15 @@ function threads.thread__main(smem)
|
|||||||
for i = 1, #units do
|
for i = 1, #units do
|
||||||
-- find disconnected device
|
-- find disconnected device
|
||||||
if units[i].device == device then
|
if units[i].device == device then
|
||||||
-- we are going to let the PPM prevent crashes
|
-- will let the PPM prevent crashes, which will indicate failures in MODBUS queries
|
||||||
-- return fault flags/codes to MODBUS queries
|
|
||||||
local unit = units[i] ---@type rtu_unit_registry_entry
|
local unit = units[i] ---@type rtu_unit_registry_entry
|
||||||
local type_name = types.rtu_type_to_string(unit.type)
|
local type_name = types.rtu_type_to_string(unit.type)
|
||||||
|
|
||||||
println_ts(util.c("lost the ", type_name, " on interface ", unit.name))
|
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))
|
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
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -125,8 +129,7 @@ function threads.thread__main(smem)
|
|||||||
if type == "modem" then
|
if type == "modem" then
|
||||||
if device.isWireless() then
|
if device.isWireless() then
|
||||||
-- reconnected modem
|
-- reconnected modem
|
||||||
rtu_dev.modem = device
|
nic.connect(device)
|
||||||
rtu_comms.reconnect_modem(rtu_dev.modem)
|
|
||||||
|
|
||||||
println_ts("wireless modem reconnected.")
|
println_ts("wireless modem reconnected.")
|
||||||
log.info("comms 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
|
-- note: cannot check isFormed as that would yield this coroutine and consume events
|
||||||
if unit.name == param1 then
|
if unit.name == param1 then
|
||||||
local resend_advert = false
|
local resend_advert = false
|
||||||
|
local faulted = false
|
||||||
|
local unknown = false
|
||||||
|
|
||||||
-- found, re-link
|
-- found, re-link
|
||||||
unit.device = device
|
unit.device = device
|
||||||
@ -177,46 +182,46 @@ function threads.thread__main(smem)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if unit.type == RTU_UNIT_TYPE.BOILER_VALVE then
|
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
|
-- if not formed, indexing the multiblock functions would have resulted in a PPM fault
|
||||||
unit.formed = util.trinary(device.__p_is_faulted(), false, nil)
|
unit.formed = util.trinary(faulted, false, nil)
|
||||||
databus.tx_unit_hw_status(unit.uid, UNIT_HW_STATE.UNFORMED)
|
|
||||||
elseif unit.type == RTU_UNIT_TYPE.TURBINE_VALVE then
|
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
|
-- if not formed, indexing the multiblock functions would have resulted in a PPM fault
|
||||||
unit.formed = util.trinary(device.__p_is_faulted(), false, nil)
|
unit.formed = util.trinary(faulted, false, nil)
|
||||||
databus.tx_unit_hw_status(unit.uid, UNIT_HW_STATE.UNFORMED)
|
|
||||||
elseif unit.type == RTU_UNIT_TYPE.IMATRIX then
|
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
|
-- if not formed, indexing the multiblock functions would have resulted in a PPM fault
|
||||||
unit.formed = util.trinary(device.__p_is_faulted(), false, nil)
|
unit.formed = util.trinary(faulted, false, nil)
|
||||||
databus.tx_unit_hw_status(unit.uid, UNIT_HW_STATE.UNFORMED)
|
|
||||||
elseif unit.type == RTU_UNIT_TYPE.SPS then
|
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
|
-- if not formed, indexing the multiblock functions would have resulted in a PPM fault
|
||||||
unit.formed = util.trinary(device.__p_is_faulted(), false, nil)
|
unit.formed = util.trinary(faulted, false, nil)
|
||||||
databus.tx_unit_hw_status(unit.uid, UNIT_HW_STATE.UNFORMED)
|
|
||||||
elseif unit.type == RTU_UNIT_TYPE.SNA then
|
elseif unit.type == RTU_UNIT_TYPE.SNA then
|
||||||
unit.rtu = sna_rtu.new(device)
|
unit.rtu, faulted = sna_rtu.new(device)
|
||||||
databus.tx_unit_hw_status(unit.uid, UNIT_HW_STATE.OK)
|
|
||||||
elseif unit.type == RTU_UNIT_TYPE.ENV_DETECTOR then
|
elseif unit.type == RTU_UNIT_TYPE.ENV_DETECTOR then
|
||||||
unit.rtu = envd_rtu.new(device)
|
unit.rtu, faulted = envd_rtu.new(device)
|
||||||
databus.tx_unit_hw_status(unit.uid, UNIT_HW_STATE.OK)
|
|
||||||
else
|
else
|
||||||
|
unknown = true
|
||||||
log.error(util.c("failed to identify reconnected RTU unit type (", unit.name, ")"), true)
|
log.error(util.c("failed to identify reconnected RTU unit type (", unit.name, ")"), true)
|
||||||
end
|
end
|
||||||
|
|
||||||
if unit.is_multiblock then
|
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"))
|
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
|
end
|
||||||
elseif device.__p_is_faulted() then
|
elseif faulted then
|
||||||
databus.tx_unit_hw_status(unit.uid, UNIT_HW_STATE.FAULTED)
|
unit.hw_state = UNIT_HW_STATE.FAULTED
|
||||||
|
elseif not unknown then
|
||||||
|
unit.hw_state = UNIT_HW_STATE.OK
|
||||||
else
|
else
|
||||||
databus.tx_unit_hw_status(unit.uid, UNIT_HW_STATE.OK)
|
unit.hw_state = UNIT_HW_STATE.OFFLINE
|
||||||
end
|
end
|
||||||
|
|
||||||
|
databus.tx_unit_hw_status(unit.uid, unit.hw_state)
|
||||||
|
|
||||||
|
if not unknown then
|
||||||
unit.modbus_io = modbus.new(unit.rtu, true)
|
unit.modbus_io = modbus.new(unit.rtu, true)
|
||||||
|
|
||||||
local type_name = types.rtu_type_to_string(unit.type)
|
local type_name = types.rtu_type_to_string(unit.type)
|
||||||
@ -233,6 +238,7 @@ function threads.thread__main(smem)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
elseif event == "mouse_click" or event == "mouse_up" or event == "mouse_drag" or event == "mouse_scroll" then
|
elseif event == "mouse_click" or event == "mouse_up" or event == "mouse_drag" or event == "mouse_scroll" then
|
||||||
-- handle a mouse event
|
-- handle a mouse event
|
||||||
renderer.handle_mouse(core.events.new_mouse_event(event, param1, param2, param3))
|
renderer.handle_mouse(core.events.new_mouse_event(event, param1, param2, param3))
|
||||||
@ -391,13 +397,6 @@ function threads.thread__unit_comms(smem, unit)
|
|||||||
-- received a packet
|
-- received a packet
|
||||||
local _, reply = unit.modbus_io.handle_packet(msg.message)
|
local _, reply = unit.modbus_io.handle_packet(msg.message)
|
||||||
rtu_comms.send_modbus(reply)
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -413,12 +412,10 @@ function threads.thread__unit_comms(smem, unit)
|
|||||||
|
|
||||||
if unit.formed == nil then
|
if unit.formed == nil then
|
||||||
unit.formed = is_formed
|
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
|
end
|
||||||
|
|
||||||
if not unit.formed then
|
if not unit.formed then unit.hw_state = UNIT_HW_STATE.UNFORMED end
|
||||||
databus.tx_unit_hw_status(unit.uid, UNIT_HW_STATE.UNFORMED)
|
|
||||||
end
|
|
||||||
|
|
||||||
if (not unit.formed) and is_formed then
|
if (not unit.formed) and is_formed then
|
||||||
-- newly re-formed
|
-- newly re-formed
|
||||||
@ -463,11 +460,11 @@ function threads.thread__unit_comms(smem, unit)
|
|||||||
if unit.formed and faulted then
|
if unit.formed and faulted then
|
||||||
-- something is still wrong = can't mark as formed yet
|
-- something is still wrong = can't mark as formed yet
|
||||||
unit.formed = false
|
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"))
|
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
|
else
|
||||||
|
unit.hw_state = UNIT_HW_STATE.OK
|
||||||
rtu_comms.send_remounted(unit.uid)
|
rtu_comms.send_remounted(unit.uid)
|
||||||
databus.tx_unit_hw_status(unit.uid, UNIT_HW_STATE.OK)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local type_name = types.rtu_type_to_string(unit.type)
|
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
|
unit.formed = is_formed
|
||||||
end
|
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
|
-- check for termination request
|
||||||
if rtu_state.shutdown then
|
if rtu_state.shutdown then
|
||||||
log.info("rtu unit thread exiting -> " .. short_name)
|
log.info("rtu unit thread exiting -> " .. short_name)
|
||||||
|
@ -7,14 +7,14 @@ local log = require("scada-common.log")
|
|||||||
local insert = table.insert
|
local insert = table.insert
|
||||||
|
|
||||||
---@diagnostic disable-next-line: undefined-field
|
---@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
|
---@class comms
|
||||||
local comms = {}
|
local comms = {}
|
||||||
|
|
||||||
comms.version = "2.0.0"
|
comms.version = "2.1.0"
|
||||||
|
|
||||||
---@enum PROTOCOL
|
---@enum PROTOCOL
|
||||||
local PROTOCOL = {
|
local PROTOCOL = {
|
||||||
@ -163,8 +163,7 @@ function comms.scada_packet()
|
|||||||
---@param payload table
|
---@param payload table
|
||||||
function public.make(dest_addr, seq_num, protocol, payload)
|
function public.make(dest_addr, seq_num, protocol, payload)
|
||||||
self.valid = true
|
self.valid = true
|
||||||
---@diagnostic disable-next-line: undefined-field
|
self.src_addr = COMPUTER_ID
|
||||||
self.src_addr = C_ID
|
|
||||||
self.dest_addr = dest_addr
|
self.dest_addr = dest_addr
|
||||||
self.seq_num = seq_num
|
self.seq_num = seq_num
|
||||||
self.protocol = protocol
|
self.protocol = protocol
|
||||||
@ -219,10 +218,14 @@ function comms.scada_packet()
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- check if this packet is destined for this device
|
-- 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
|
self.valid = is_destination and
|
||||||
type(self.seq_num) == "number" and type(self.protocol) == "number" and type(self.payload) == "table"
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -260,6 +263,112 @@ function comms.scada_packet()
|
|||||||
return public
|
return public
|
||||||
end
|
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<br>
|
-- MODBUS packet<br>
|
||||||
-- modeled after MODBUS TCP packet
|
-- modeled after MODBUS TCP packet
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
|
@ -6,6 +6,8 @@ local comms = require("scada-common.comms")
|
|||||||
local log = require("scada-common.log")
|
local log = require("scada-common.log")
|
||||||
local util = require("scada-common.util")
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
|
local core = require("graphics.core")
|
||||||
|
|
||||||
local crash = {}
|
local crash = {}
|
||||||
|
|
||||||
local app = "unknown"
|
local app = "unknown"
|
||||||
@ -32,6 +34,7 @@ function crash.handler(error)
|
|||||||
log.info(util.c("APPLICATION: ", app))
|
log.info(util.c("APPLICATION: ", app))
|
||||||
log.info(util.c("FIRMWARE VERSION: ", ver))
|
log.info(util.c("FIRMWARE VERSION: ", ver))
|
||||||
log.info(util.c("COMMS VERSION: ", comms.version))
|
log.info(util.c("COMMS VERSION: ", comms.version))
|
||||||
|
log.info(util.c("GRAPHICS VERSION: ", core.version))
|
||||||
log.info("----------------------------------")
|
log.info("----------------------------------")
|
||||||
log.info(debug.traceback("--- begin debug trace ---", 1))
|
log.info(debug.traceback("--- begin debug trace ---", 1))
|
||||||
log.info("--- end debug trace ---")
|
log.info("--- end debug trace ---")
|
||||||
|
@ -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
|
|
233
scada-common/network.lua
Normal file
233
scada-common/network.lua
Normal file
@ -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<br>
|
||||||
|
-- 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<br>
|
||||||
|
-- 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
|
@ -101,22 +101,31 @@ local function peri_init(iface)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- fault management functions
|
-- fault management & monitoring functions
|
||||||
|
|
||||||
local function clear_fault() self.faulted = false end
|
local function clear_fault() self.faulted = false end
|
||||||
local function get_last_fault() return self.last_fault end
|
local function get_last_fault() return self.last_fault end
|
||||||
local function is_faulted() return self.faulted end
|
local function is_faulted() return self.faulted end
|
||||||
local function is_ok() return not self.faulted end
|
local function is_ok() return not self.faulted end
|
||||||
|
|
||||||
|
-- check if a peripheral has any faulted functions<br>
|
||||||
|
-- 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 enable_afc() self.auto_cf = true end
|
||||||
local function disable_afc() self.auto_cf = false 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_clear_fault = clear_fault
|
||||||
self.device.__p_last_fault = get_last_fault
|
self.device.__p_last_fault = get_last_fault
|
||||||
self.device.__p_is_faulted = is_faulted
|
self.device.__p_is_faulted = is_faulted
|
||||||
self.device.__p_is_ok = is_ok
|
self.device.__p_is_ok = is_ok
|
||||||
|
self.device.__p_is_healthy = is_healthy
|
||||||
self.device.__p_enable_afc = enable_afc
|
self.device.__p_enable_afc = enable_afc
|
||||||
self.device.__p_disable_afc = disable_afc
|
self.device.__p_disable_afc = disable_afc
|
||||||
|
|
||||||
|
@ -17,6 +17,10 @@ config.PLC_TIMEOUT = 5
|
|||||||
config.RTU_TIMEOUT = 5
|
config.RTU_TIMEOUT = 5
|
||||||
config.CRD_TIMEOUT = 5
|
config.CRD_TIMEOUT = 5
|
||||||
config.PKT_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
|
-- expected number of reactors
|
||||||
config.NUM_REACTORS = 4
|
config.NUM_REACTORS = 4
|
||||||
|
@ -75,7 +75,7 @@ function facility.new(num_reactors, cooling_conf)
|
|||||||
burn_target = 0.1, -- burn rate target for aggregate burn mode
|
burn_target = 0.1, -- burn rate target for aggregate burn mode
|
||||||
charge_setpoint = 0, -- FE charge target setpoint
|
charge_setpoint = 0, -- FE charge target setpoint
|
||||||
gen_rate_setpoint = 0, -- FE/t charge rate 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)
|
prio_defs = { {}, {}, {}, {} }, -- priority definitions (each level is a table of units)
|
||||||
at_max_burn = false,
|
at_max_burn = false,
|
||||||
ascram = false,
|
ascram = false,
|
||||||
@ -109,6 +109,7 @@ function facility.new(num_reactors, cooling_conf)
|
|||||||
-- create units
|
-- create units
|
||||||
for i = 1, num_reactors do
|
for i = 1, num_reactors do
|
||||||
table.insert(self.units, unit.new(i, cooling_conf[i].BOILERS, cooling_conf[i].TURBINES))
|
table.insert(self.units, unit.new(i, cooling_conf[i].BOILERS, cooling_conf[i].TURBINES))
|
||||||
|
table.insert(self.group_map, 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- init redstone RTU I/O controller
|
-- init redstone RTU I/O controller
|
||||||
@ -790,7 +791,7 @@ function facility.new(num_reactors, cooling_conf)
|
|||||||
---@param unit_id integer unit ID
|
---@param unit_id integer unit ID
|
||||||
---@param group integer group ID or 0 for independent
|
---@param group integer group ID or 0 for independent
|
||||||
function public.set_group(unit_id, group)
|
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
|
-- remove from old group if previously assigned
|
||||||
local old_group = self.group_map[unit_id]
|
local old_group = self.group_map[unit_id]
|
||||||
if old_group ~= 0 then
|
if old_group ~= 0 then
|
||||||
|
@ -120,7 +120,7 @@ function rtu.new_session(id, s_addr, in_queue, out_queue, timeout, advertisement
|
|||||||
|
|
||||||
if u_type == false then
|
if u_type == false then
|
||||||
-- validation fail
|
-- validation fail
|
||||||
log.debug(log_header .. "advertisement unit validation failure")
|
log.debug(log_header .. "_handle_advertisement(): advertisement unit validation failure")
|
||||||
else
|
else
|
||||||
if unit_advert.reactor > 0 then
|
if unit_advert.reactor > 0 then
|
||||||
local target_unit = self.fac_units[unit_advert.reactor] ---@type reactor_unit
|
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
|
-- skip virtual units
|
||||||
log.debug(util.c(log_header, "skipping virtual RTU unit #", i))
|
log.debug(util.c(log_header, "skipping virtual RTU unit #", i))
|
||||||
else
|
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
|
end
|
||||||
else
|
else
|
||||||
-- facility RTUs
|
-- facility RTUs
|
||||||
@ -172,7 +172,7 @@ function rtu.new_session(id, s_addr, in_queue, out_queue, timeout, advertisement
|
|||||||
-- skip virtual units
|
-- skip virtual units
|
||||||
log.debug(util.c(log_header, "skipping virtual RTU unit #", i))
|
log.debug(util.c(log_header, "skipping virtual RTU unit #", i))
|
||||||
else
|
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
|
end
|
||||||
end
|
end
|
||||||
@ -181,9 +181,7 @@ function rtu.new_session(id, s_addr, in_queue, out_queue, timeout, advertisement
|
|||||||
self.units[i] = unit
|
self.units[i] = unit
|
||||||
unit_count = unit_count + 1
|
unit_count = unit_count + 1
|
||||||
elseif u_type ~= RTU_UNIT_TYPE.VIRTUAL then
|
elseif u_type ~= RTU_UNIT_TYPE.VIRTUAL then
|
||||||
_reset_config()
|
log.warning(util.c(log_header, "_handle_advertisement(): problem occured while creating a unit (type is ", type_string, ")"))
|
||||||
log.error(util.c(log_header, "bad advertisement: error occured while creating a unit (type is ", type_string, ")"))
|
|
||||||
break
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ local SESSION_TYPE = {
|
|||||||
svsessions.SESSION_TYPE = SESSION_TYPE
|
svsessions.SESSION_TYPE = SESSION_TYPE
|
||||||
|
|
||||||
local self = {
|
local self = {
|
||||||
modem = nil, ---@type table|nil
|
nic = nil, ---@type nic|nil
|
||||||
fp_ok = false,
|
fp_ok = false,
|
||||||
num_reactors = 0,
|
num_reactors = 0,
|
||||||
facility = nil, ---@type facility|nil
|
facility = nil, ---@type facility|nil
|
||||||
@ -60,7 +60,7 @@ local function _sv_handle_outq(session)
|
|||||||
if msg ~= nil then
|
if msg ~= nil then
|
||||||
if msg.qtype == mqueue.TYPE.PACKET then
|
if msg.qtype == mqueue.TYPE.PACKET then
|
||||||
-- handle a packet to be sent
|
-- 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
|
elseif msg.qtype == mqueue.TYPE.COMMAND then
|
||||||
-- handle instruction/notification
|
-- handle instruction/notification
|
||||||
elseif msg.qtype == mqueue.TYPE.DATA then
|
elseif msg.qtype == mqueue.TYPE.DATA then
|
||||||
@ -135,7 +135,7 @@ local function _shutdown(session)
|
|||||||
while session.out_queue.ready() do
|
while session.out_queue.ready() do
|
||||||
local msg = session.out_queue.pop()
|
local msg = session.out_queue.pop()
|
||||||
if msg ~= nil and msg.qtype == mqueue.TYPE.PACKET then
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -195,23 +195,17 @@ end
|
|||||||
-- PUBLIC FUNCTIONS --
|
-- PUBLIC FUNCTIONS --
|
||||||
|
|
||||||
-- initialize svsessions
|
-- initialize svsessions
|
||||||
---@param modem table modem device
|
---@param nic nic network interface device
|
||||||
---@param fp_ok boolean front panel active
|
---@param fp_ok boolean front panel active
|
||||||
---@param num_reactors integer number of reactors
|
---@param num_reactors integer number of reactors
|
||||||
---@param cooling_conf table cooling configuration definition
|
---@param cooling_conf table cooling configuration definition
|
||||||
function svsessions.init(modem, fp_ok, num_reactors, cooling_conf)
|
function svsessions.init(nic, fp_ok, num_reactors, cooling_conf)
|
||||||
self.modem = modem
|
self.nic = nic
|
||||||
self.fp_ok = fp_ok
|
self.fp_ok = fp_ok
|
||||||
self.num_reactors = num_reactors
|
self.num_reactors = num_reactors
|
||||||
self.facility = facility.new(num_reactors, cooling_conf)
|
self.facility = facility.new(num_reactors, cooling_conf)
|
||||||
end
|
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
|
-- find an RTU session by the computer ID
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param source_addr integer
|
---@param source_addr integer
|
||||||
|
@ -7,6 +7,7 @@ require("/initenv").init_env()
|
|||||||
local crash = require("scada-common.crash")
|
local crash = require("scada-common.crash")
|
||||||
local comms = require("scada-common.comms")
|
local comms = require("scada-common.comms")
|
||||||
local log = require("scada-common.log")
|
local log = require("scada-common.log")
|
||||||
|
local network = require("scada-common.network")
|
||||||
local ppm = require("scada-common.ppm")
|
local ppm = require("scada-common.ppm")
|
||||||
local tcd = require("scada-common.tcd")
|
local tcd = require("scada-common.tcd")
|
||||||
local util = require("scada-common.util")
|
local util = require("scada-common.util")
|
||||||
@ -20,7 +21,7 @@ local supervisor = require("supervisor.supervisor")
|
|||||||
|
|
||||||
local svsessions = require("supervisor.session.svsessions")
|
local svsessions = require("supervisor.session.svsessions")
|
||||||
|
|
||||||
local SUPERVISOR_VERSION = "v0.17.7"
|
local SUPERVISOR_VERSION = "v0.18.0"
|
||||||
|
|
||||||
local println = util.println
|
local println = util.println
|
||||||
local println_ts = util.println_ts
|
local println_ts = util.println_ts
|
||||||
@ -94,6 +95,12 @@ local function main()
|
|||||||
-- mount connected devices
|
-- mount connected devices
|
||||||
ppm.mount_all()
|
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()
|
local modem = ppm.get_wireless_modem()
|
||||||
if modem == nil then
|
if modem == nil then
|
||||||
println("startup> wireless modem not found")
|
println("startup> wireless modem not found")
|
||||||
@ -115,8 +122,9 @@ local function main()
|
|||||||
println_ts = function (_) end
|
println_ts = function (_) end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- start comms
|
-- create network interface then setup comms
|
||||||
local superv_comms = supervisor.comms(SUPERVISOR_VERSION, modem, fp_ok)
|
local nic = network.nic(modem)
|
||||||
|
local superv_comms = supervisor.comms(SUPERVISOR_VERSION, nic, fp_ok)
|
||||||
|
|
||||||
-- base loop clock (6.67Hz, 3 ticks)
|
-- base loop clock (6.67Hz, 3 ticks)
|
||||||
local MAIN_CLOCK = 0.15
|
local MAIN_CLOCK = 0.15
|
||||||
@ -139,9 +147,12 @@ local function main()
|
|||||||
if type ~= nil and device ~= nil then
|
if type ~= nil and device ~= nil then
|
||||||
if type == "modem" then
|
if type == "modem" then
|
||||||
-- we only care if this is our wireless modem
|
-- 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!")
|
println_ts("wireless modem disconnected!")
|
||||||
log.warning("comms modem disconnected")
|
log.warning("comms modem disconnected")
|
||||||
|
|
||||||
databus.tx_hw_modem(false)
|
databus.tx_hw_modem(false)
|
||||||
else
|
else
|
||||||
log.warning("non-comms modem disconnected")
|
log.warning("non-comms modem disconnected")
|
||||||
@ -153,10 +164,9 @@ local function main()
|
|||||||
|
|
||||||
if type ~= nil and device ~= nil then
|
if type ~= nil and device ~= nil then
|
||||||
if type == "modem" then
|
if type == "modem" then
|
||||||
if device.isWireless() then
|
if device.isWireless() and not nic.connected() then
|
||||||
-- reconnected modem
|
-- reconnected modem
|
||||||
modem = device
|
nic.connect(device)
|
||||||
superv_comms.reconnect_modem(modem)
|
|
||||||
|
|
||||||
println_ts("wireless modem reconnected.")
|
println_ts("wireless modem reconnected.")
|
||||||
log.info("comms modem reconnected")
|
log.info("comms modem reconnected")
|
||||||
|
@ -16,10 +16,10 @@ local SCADA_MGMT_TYPE = comms.SCADA_MGMT_TYPE
|
|||||||
-- supervisory controller communications
|
-- supervisory controller communications
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param _version string supervisor version
|
---@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
|
---@param fp_ok boolean if the front panel UI is running
|
||||||
---@diagnostic disable-next-line: unused-local
|
---@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
|
-- 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
|
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 --
|
-- PRIVATE FUNCTIONS --
|
||||||
|
|
||||||
-- configure modem channels
|
-- configure modem channels
|
||||||
local function _conf_channels()
|
nic.closeAll()
|
||||||
modem.closeAll()
|
nic.open(svr_channel)
|
||||||
modem.open(svr_channel)
|
|
||||||
end
|
|
||||||
|
|
||||||
_conf_channels()
|
|
||||||
|
|
||||||
-- pass modem, status, and config data to svsessions
|
-- 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
|
-- send an establish request response
|
||||||
---@param packet scada_packet
|
---@param packet scada_packet
|
||||||
@ -64,7 +60,7 @@ function supervisor.comms(_version, modem, fp_ok)
|
|||||||
m_pkt.make(SCADA_MGMT_TYPE.ESTABLISH, { ack, data })
|
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())
|
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
|
self.last_est_acks[packet.src_addr()] = ack
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -73,14 +69,6 @@ function supervisor.comms(_version, modem, fp_ok)
|
|||||||
---@class superv_comms
|
---@class superv_comms
|
||||||
local public = {}
|
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
|
-- parse a packet
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param side string
|
---@param side string
|
||||||
@ -90,13 +78,10 @@ function supervisor.comms(_version, modem, fp_ok)
|
|||||||
---@param distance integer
|
---@param distance integer
|
||||||
---@return modbus_frame|rplc_frame|mgmt_frame|crdn_frame|nil packet
|
---@return modbus_frame|rplc_frame|mgmt_frame|crdn_frame|nil packet
|
||||||
function public.parse_packet(side, sender, reply_to, message, distance)
|
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 pkt = nil
|
||||||
local s_pkt = comms.scada_packet()
|
|
||||||
|
|
||||||
-- parse packet as generic SCADA packet
|
if s_pkt then
|
||||||
s_pkt.receive(side, sender, reply_to, message, distance)
|
|
||||||
|
|
||||||
if s_pkt.is_valid() then
|
|
||||||
-- get as MODBUS TCP packet
|
-- get as MODBUS TCP packet
|
||||||
if s_pkt.protocol() == PROTOCOL.MODBUS_TCP then
|
if s_pkt.protocol() == PROTOCOL.MODBUS_TCP then
|
||||||
local m_pkt = comms.modbus_packet()
|
local m_pkt = comms.modbus_packet()
|
||||||
|
@ -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)
|
|
193
test/lockbox_benchmark.lua
Normal file
193
test/lockbox_benchmark.lua
Normal file
@ -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)
|
Reference in New Issue
Block a user