mirror of
https://github.com/MikaylaFischler/cc-mek-scada.git
synced 2024-08-30 18:22:34 +00:00
153 lines
4.7 KiB
Lua
153 lines
4.7 KiB
Lua
--
|
|
-- Cryptographic Communications Engine
|
|
--
|
|
|
|
local md5 = require("lockbox.digest.md5")
|
|
local sha2_256 = 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 crypto = {}
|
|
|
|
local c_eng = {
|
|
key = nil,
|
|
hmac = nil
|
|
}
|
|
|
|
-- initialize cryptographic system
|
|
function crypto.init(password)
|
|
local key_deriv = pbkdf2()
|
|
|
|
-- setup PBKDF2
|
|
key_deriv.setPassword(password)
|
|
key_deriv.setSalt("pepper")
|
|
key_deriv.setIterations(32)
|
|
key_deriv.setBlockLen(8)
|
|
key_deriv.setDKeyLen(16)
|
|
|
|
local start = util.time_ms()
|
|
|
|
key_deriv.setPRF(hmac().setBlockSize(64).setDigest(sha2_256))
|
|
key_deriv.finish()
|
|
|
|
local message = "pbkdf2 key derivation took " .. (util.time_ms() - start) .. "ms"
|
|
log.dmesg(message, "CRYPTO", colors.yellow)
|
|
log.info("crypto.init: " .. message)
|
|
|
|
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)
|
|
|
|
message = "init: completed in " .. (util.time_ms() - start) .. "ms"
|
|
log.dmesg(message, "CRYPTO", colors.yellow)
|
|
log.info("crypto." .. message)
|
|
end
|
|
|
|
-- generate HMAC of message
|
|
---@nodiscard
|
|
---@param message string initial value concatenated with ciphertext
|
|
function crypto.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("crypto.hmac: hmac-md5 took " .. (util.time_ms() - start) .. "ms")
|
|
log.debug("crypto.hmac: 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
|
|
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 message authentication
|
|
---@param packet scada_packet packet raw_sendable
|
|
function public.transmit(packet)
|
|
local start = util.time_ms()
|
|
local message = textutils.serialize(packet.raw_verifiable(), { allow_repetitions = true, compact = true })
|
|
local computed_hmac = crypto.hmac(message)
|
|
|
|
packet.set_mac(computed_hmac)
|
|
|
|
log.debug("crypto.transmit: data processing took " .. (util.time_ms() - start) .. "ms")
|
|
|
|
modem.transmit(packet.remote_channel(), packet.local_channel(), packet.raw_sendable())
|
|
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 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
|
|
local s_packet = comms.scada_packet()
|
|
|
|
-- parse packet as generic SCADA packet
|
|
s_packet.receive(side, sender, reply_to, message, distance)
|
|
|
|
if s_packet.is_valid() then
|
|
local start = util.time_ms()
|
|
local packet_hmac = s_packet.mac()
|
|
local computed_hmac = crypto.hmac(textutils.serialize(s_packet.raw_verifiable(), { allow_repetitions = true, compact = true }))
|
|
|
|
if packet_hmac == computed_hmac then
|
|
log.debug("crypto.secure_modem.receive: HMAC verified in " .. (util.time_ms() - start) .. "ms")
|
|
packet = s_packet
|
|
else
|
|
log.debug("crypto.secure_modem.receive: HMAC failed verification in " .. (util.time_ms() - start) .. "ms")
|
|
end
|
|
end
|
|
|
|
return packet
|
|
end
|
|
|
|
return public
|
|
end
|
|
|
|
return crypto
|