Posted 09 October 2016 - 08:11 AM
Hello,
I'm currently writing two programs, one to run on a computer which sits next to a BioLock from Gopher's Peripherals. The other program serves as the authentication server. All communications are public-key encrypted with keys being generated and negotiated on-the-fly with the Advanced Cipher Block from Computronics.
However, when I try to authenticate myself, the script on the authentication server crashes with this error:
I've been over the code numerous times and tried a lot of different fixes. None work. If you were wondering, no, it's not a problem with the encryption, that has already been confirmed as working. The crash occurs sometime between when the server receives the biolock's data from the client and tries to authenticate it against the database, although I can't work out where.
I'm not very experienced with Lua, so some assistance would be great.
Here are the two programs (receive and access keys redacted for obvious reasons)
Server:
Client:
Thanks,
CitadelCore
I'm currently writing two programs, one to run on a computer which sits next to a BioLock from Gopher's Peripherals. The other program serves as the authentication server. All communications are public-key encrypted with keys being generated and negotiated on-the-fly with the Advanced Cipher Block from Computronics.
However, when I try to authenticate myself, the script on the authentication server crashes with this error:
textutils:295: attempt to concatentate string and table
I've been over the code numerous times and tried a lot of different fixes. None work. If you were wondering, no, it's not a problem with the encryption, that has already been confirmed as working. The crash occurs sometime between when the server receives the biolock's data from the client and tries to authenticate it against the database, although I can't work out where.
I'm not very experienced with Lua, so some assistance would be great.
Here are the two programs (receive and access keys redacted for obvious reasons)
Server:
Spoiler
recvkey = "[redacted]" -- Key used for authenticating recieved messages
recvkey = textutils.serialize(recvkey) -- Serialize the key
accesskey = "[redacted]"
looplisten = true
logfile = fs.open("/fusionauth.log", "w")
computerWhitelist = {"16", "17", "18", "19"}
cipher = peripheral.wrap("right")
users = {"citadelcore", "jonaharagon"}
hashes = {"nothinghereyet", "nothinghereyet"}
biohashes = {"[redacted]", "nothinghereyet"}
codehashes = {"[redacted]", "nothinghereyet"}
securitylevels = {"5", "4"}
function hash(str)
local s = 0
local p = ""
for c in str:gmatch(".") do
s = s + string.byte(c)
end
s = bit.bxor(65432895, s)
while s > 0 do
p = p .. string.char(s % 94 + 33)
s = bit.brshift(s, 1)
end
return string.sub(p, 1, p:len() - 1)
end
function isComputerInWhitelist(computerid, whitelist)
-- for i,v in ipairs(whitelist) do
-- if v == computerid then
-- return true
-- end
-- break
-- end
return true
end
function authUser(purpose, username, hash, securitylevel)
if purpose == "bioauth" then
for k,v in pairs(biohashes) do
if hash == v then
for k,v in pairs(securitylevels) do
if securitylevel == v then
return true
else
return false
end
end
break
else
return false
end
end
elseif purpose == "keypadauth" then
for i,v in ipairs(codehashes) do
if v == hash then
for i,v in ipairs(securitylevels) do
if v == securitylevel then
return true
else
return false
end
end
else
return false
end
break
end
return false
elseif purpose == "rfidauth" then
-- unimplemented
return false
elseif purpose == "magstripeauth" then
-- unimplemented
return false
else
logfile.writeLine("Unrecognized purpose\n")
logfile.flush()
return false
end
end
term.clear()
term.setTextColor(1)
term.setCursorPos(1,1)
term.write("Starting AuthServer 1.0 by CitadelCore.")
term.setCursorPos(1,2)
term.write("Server Module: AuthServer")
logfile.writeLine("Generating keypair...")
logfile.flush()
keySet = cipher.createRandomKeySet()
os.sleep(3)
logfile.writeLine("Done.")
logfile.flush()
rsaPublicKey, rsaPrivateKey = keySet.getKeys()
term.setCursorPos(1,3)
rednet.open("top")
rednet.host("authsec", "authserver")
term.write("Server is ready to recieve authentication requests.")
term.setCursorPos(1,4)
function sendSecureRednet(senderID, message, securePublicKey)
message = textutils.serialize(message)
message = cipher.encrypt(message, securePublicKey)
rednet.send(senderID, message, "authsec")
end
function recieveSecureRednet(securePrivateKey)
senderID, commandData, protocol = rednet.receive("authsec")
commandData = cipher.decrypt(commandData, securePrivateKey)
commandData = textutils.unserialize(commandData)
return senderID, commandData
end
while looplisten == true do
senderID, clientPublicKey, protocol = rednet.receive("authsec")
logfile.writeLine("Got PUBKEY packet from client.")
logfile.flush()
computerInWhitelist = isComputerInWhitelist(senderID, computerWhitelist)
if computerInWhitelist == true then
rednet.send(senderID, rsaPublicKey, "authsec")
logfile.writeLine("Sent our public key packet to the client")
logfile.flush()
logfile.writeLine("Requesting client to authenticate.")
logfile.flush()
-- Request credentials from client
sendSecureRednet(senderID, "authenticate", clientPublicKey)
senderID, authkey = recieveSecureRednet(rsaPrivateKey)
-- Authkey checking
if authkey == recvkey then
logfile.writeLine("Client's authentication packet is valid.")
logfile.flush()
sendSecureRednet(senderID, "authsuccess", clientPublicKey)
logfile.writeLine("Sent authentication success packet.")
logfile.writeLine("Waiting for control packet.")
logfile.flush()
-- Wait for control packet.
senderID, keyCompare = recieveSecureRednet(rsaPrivateKey)
-- Custom auth logic here
logfile.writeLine("Got data from client.")
logfile.writeLine("Checking for a parameter match.")
logfile.flush()
if keyCompare == accesskey then
logfile.writeLine("Got accesskey parameter.")
logfile.flush()
sendSecureRednet(senderID, "correctkey", clientPublicKey)
elseif keyCompare == "bioauth" then
logfile.writeLine("Got bioauth parameter.")
logfile.flush()
sendSecureRednet(senderID, "sendbiodata", clientPublicKey)
logfile.writeLine("Requested data from client.")
logfile.flush()
senderID, biodata = recieveSecureRednet(rsaPrivateKey)
logfile.writeLine("Recieved data from client")
textutils.unserialize(biodata)
secureBiohash = textutils.serialize(biodata.biohash())
secureAccessLevel = textutils.serialize(biodata.accesslevel())
authResult = authUser("bioauth", "null", secureBiohash, secureAccessLevel)
if authResult == true then
sendSecureRednet(senderID, true, clientPublicKey)
elseif authResult == false then
sendSecureRednet(senderID, false, clientPublicKey)
else
sendSecureRednet(senderID, false, clientPublicKey)
end
elseif keyCompare == "keypadauth" then
logfile.writeLine("Got keypadauth parameter.\n")
logfile.flush()
sendSecureRednet(senderID, "sendkeypaddata", clientPublicKey)
logfile.writeLine("Requested data from client.\n")
senderID, keypaddata = recieveSecureRednet(rsaPrivateKey)
logfile.writeLine("Received data from client.\n")
logfile.flush()
textutils.unserialize(keypaddata)
codehash = keypaddata["codehash"]
accessLevel = keypaddata["accesslevel"]
authResult = authUser("keypadauth", nil, codehash, accessLevel)
if authResult == true then
sendSecureRednet(senderID, true, clientPublicKey)
elseif authResult == false then
sendSecureRednet(senderID, false, clientPublicKey)
else
sendSecureRednet(senderID, false, clientPublicKey)
end
elseif keyCompare == "rfidauth" then
logfile.writeLine("Got rfidauth parameter.\n")
logfile.flush()
-- RFID authentication methods. Unimplemented.
sendSecureRednet(senderID, false, clientPublicKey)
elseif keyCompare == "magstripeauth" then
logfile.writeLine("Got magstripeauth parameter.\n")
logfile.flush()
-- Magstripe authentication methods. Unimplemented.
sendSecureRednet(senderID, false, clientPublicKey)
else
--logfile.writeLine("Client sent invalid authentication key and was not authenticated. Authentication key: " .. keyCompare)
logfile.writeLine("Client sent invalid authentication key and was not authenticated.")
logfile.flush()
sendSecureRednet(senderID, "invalidkey", clientPublicKey)
end
else
-- Client sent an invalid Authkey
sendSecureRednet(senderID, "authfail", clientPublicKey)
--logfile.writeLine("Authentication from a remote server has failed! Authkey: " .. authkey)
logfile.writeLine("Authentication from a remote server has failed!")
logfile.flush()
end
else
sendSecureRednet(senderID, "notwhitelisted", clientPublicKey)
--logfile.writeLine("Client is not on the whitelist and was blocked. Client ID: " .. senderID)
logfile.writeLine("Client is not on the whitelist and was blocked.")
logfile.flush()
end
end
logfile.close()
Client:
Spoiler
modem = peripheral.wrap("top") -- Wrap the modem
authpass = "[redacted]" -- Key used for connecting to the authentication database
authpass = textutils.serialize(authpass) -- Serialize the key
recvkey = "[redacted]" -- Key used for authenticating recieved messages
recvkey = textutils.serialize(recvkey) -- Serialize the key
looplisten = true
logfile = fs.open("/fusionserver.log", "w")
cipher = peripheral.wrap("left")
modem.closeAll() -- Reset the modem, for integrity
lockPurpose = "biolock" -- The lock purpose. Can be biolock, keypad, rfid, or magstripe.
biolock = peripheral.wrap("right") -- Change this to your biolock's side
accessLevelRequired = 4 -- The access level required to enter.
redstoneSide = "back" -- Set this to your redstone side
redstone.setOutput(redstoneSide, true)
-- keypad = peripheral.wrap("right")
-- rfid = peripheral.wrap("right")
-- magstripe = peripheral.wrap("right")
function sendSecureRednet(senderID, message, cryptkey)
message = textutils.serialize(message)
message = cipher.encrypt(message, cryptkey)
rednet.send(senderID, message, "authsec")
end
function recieveSecureRednet(cryptkey)
senderID, commandData, protocol = rednet.receive("authsec")
commandData = cipher.decrypt(commandData, cryptkey)
commandData = textutils.unserialize(commandData)
return senderID, commandData
end
function authCommand(destinationhostname, password, functionCommand, publicKey, privateKey, logindata)
rednet.open("top")
destID = rednet.lookup("authsec", destinationhostname)
-- Send HELLO message to any listening FusionServer
rednet.send(destID, publicKey, "authsec")
logfile.writeLine("Sent our public key packet to the server\n")
logfile.flush()
-- Wait for command reply and instruction
senderID, clientPublicKey, protocol = rednet.receive("authsec")
senderID, commandReply = recieveSecureRednet(privateKey)
-- Is the command reply AUTHENTICATE?
if commandReply == "authenticate" then
logfile.writeLine("Got reply: AUTHENTICATE\n")
logfile.flush()
-- Send back the authentication passkey
sendSecureRednet(destID, password, clientPublicKey)
logfile.writeLine("Sending authentication packet to server\n")
logfile.flush()
-- Wait for authentication adknowlegement
senderID, authReply = recieveSecureRednet(privateKey)
if authReply == "authsuccess" then
logfile.writeLine("Got reply: AUTHSUCCESS\n")
logfile.writeLine("Requesting data from server\n")
logfile.flush()
-- Request auth from server
sendSecureRednet(destID, functionCommand, clientPublicKey)
senderID, returnData = recieveSecureRednet(privateKey)
if returnData == "sendbiodata" then
logfile.writeLine("Got reply: SENDBIODATA\n")
logfile.writeLine("Sending data\n")
logfile.flush()
sendSecureRednet(destID, logindata, clientPublicKey)
senderID, returnData = recieveSecureRednet(privateKey)
logfile.writeLine("Got reply:\n" .. returnData)
logfile.flush()
return returnData
elseif returnData == "sendkeypaddata" then
logfile.writeLine("Got reply: SENDKEYPADDATA\n")
logfile.writeLine("Sending data\n")
logfile.flush()
sendSecureRednet(destID, logindata, clientPublicKey)
senderID, returnData = recieveSecureRednet(privateKey)
logfile.writeLine("Got reply:\n" .. returnData)
logfile.flush()
return returnData
elseif returnData == "sendrfiddata" then
logfile.writeLine("Got reply: SENDRFIDDATA\n")
logfile.writeLine("Sending data\n")
logfile.flush()
sendSecureRednet(destID, logindata, clientPublicKey)
senderID, returnData = recieveSecureRednet(privateKey)
logfile.writeLine("Got reply:\n" .. returnData)
logfile.flush()
return returnData
elseif returnData == "sendmagstripedata" then
logfile.writeLine("Got reply: SENDMAGSTRIPEDATA\n")
logfile.writeLine("Sending data\n")
logfile.flush()
sendSecureRednet(destID, logindata, clientPublicKey)
senderID, returnData = recieveSecureRednet(privateKey)
logfile.writeLine("Got reply:\n" .. returnData)
logfile.flush()
return returnData
else
return returnData
end
elseif authReply == "authfail" then
-- Server gave invalid key message
logfile.writeLine("Got reply: AUTHFAIL\n")
logfile.writeLine("Authentication server denied our key!\n")
logfile.flush()
else
-- Server gave invalid response to key adknowlegement
logfile.writeLine("Got invalid parameter from authentication server!\n")
logfile.writeLine("Parameter:" .. authReply)
logfile.flush()
end
else
-- Server gave invalid response to command reply
logfile.writeLine("Got invalid parameter from authentication server!\n")
logfile.writeLine("Parameter:" .. commandReply)
logfile.flush()
end
-- Finish up communication with Auth Server
rednet.close("top")
term.redirect(rmon.restoreTo)
logfile.writeLine("Finished communication with server\n")
logfile.flush()
term.redirect(rmon)
end
function hash(str)
local s = 0
local p = ""
for c in str:gmatch(".") do
s = s + string.byte(c)
end
s = bit.bxor(65432895, s)
while s > 0 do
p = p .. string.char(s % 94 + 33)
s = bit.brshift(s, 1)
end
return string.sub(p, 1, p:len() - 1)
end
-- Print terminal information
term.clear()
term.setTextColor(1)
term.setCursorPos(1,1)
term.write("Starting FusionClient 1.0 by CitadelCore.")
term.setCursorPos(1,2)
term.write("Client Module: SmartLock")
term.setCursorPos(1,3)
term.write("Negotiating keypair")
term.setCursorPos(1,4)
logfile.writeLine("Generating keypair...\n")
logfile.flush()
keySet = cipher.createRandomKeySet()
os.sleep(3)
logfile.writeLine("Done.\n")
logfile.flush()
rsaPublicKey, rsaPrivateKey = keySet.getKeys()
term.setCursorPos(1,6)
while looplisten == true do
if lockPurpose == "biolock" then
biodata = {}
event, print, attachName, learnedName, accessLevel = os.pullEvent("biolock")
biodata["biohash"] = print
biodata["accesslevel"] = accessLevelRequired
textutils.serialize(biodata)
authCommand("authserver", authpass, "bioauth", rsaPublicKey, rsaPrivateKey, biodata)
logfile.writeLine("Sent bioauth control parameter to server..\n")
logfile.flush()
senderID, isPrintValid = recieveSecureRednet(rsaPrivateKey)
if isPrintValid == true then
logfile.writeLine("Bioprint is valid, opening door.\n")
-- Bioprint is valid, open door
redstone.setOutput(redstoneSide, false)
os.sleep(3)
redstone.setOutput(redstoneSide, true)
elseif isPrintValid == false then
logfile.writeLine("Server returned invalid bioprint!\n")
-- Bioprint is not valid, don't open door
else
-- Returned invalid string
logfile.writeLine("Got invalid parameter from authentication server!\n")
logfile.writeLine("Parameter:" .. commandReply)
end
elseif lockPurpose == "keypad" then
keypaddata = {}
event, attachName, button1 = os.pullEvent("keypad_button")
event, attachName, button2 = os.pullEvent("keypad_button")
event, attachName, button3 = os.pullEvent("keypad_button")
event, attachName, button4 = os.pullEvent("keypad_button")
event, attachName, button5 = os.pullEvent("keypad_button")
event, attachName, button6 = os.pullEvent("keypad_button")
keycode = (button1 .. button2 .. button3 .. button4 .. button5 .. button6)
keycode = hash(keycode)
keypaddata["codehash"] = keycode
keypaddata["accesslevel"] = accessLevelRequired
textutils.serialize(keypaddata)
authCommand("authserver", authpass, "keypadauth", rsaPublicKey, rsaPrivateKey, keypaddata)
logfile.writeLine("Sent keypadauth control parameter to server..\n")
logfile.flush()
senderID, isCodeValid = recieveSecureRednet(rsaPrivateKey)
if isCodeValid == true then
logfile.writeLine("Code is valid, opening door.\n")
-- Code is valid, open door
redstone.setOutput(redstoneSide, false)
os.sleep(3)
redstone.setOutput(redstoneSide, true)
elseif isCodeValid == false then
logfile.writeLine("Server returned that the code is invalid!\n")
-- Code is not valid, don't open door
else
-- Returned invalid string
logfile.writeLine("Got invalid parameter from authentication server!\n")
logfile.writeLine("Parameter:" .. commandReply)
end
elseif lockPurpose == "rfid" then
-- rfid functions
elseif lockPurpose == "magstripe" then
-- magstripe functions
else
term.write("Invalid lock purpose: " .. lockPurpose)
os.exit()
end
end
logfile.close()
Thanks,
CitadelCore
Edited on 09 October 2016 - 06:38 PM