Posted 31 August 2016 - 07:53 PM
So, I've seen a lot of OSes and programs that use passwords to make sure that the user is who they say they are. The problem with storing passwords is that it can be dangerous to the integrity of your program if done incorrectly. So what we're going to do is make a simple login system that stores all the users into one file, and makes the passwords unreadable to humans, and almost uncrackable.
First thing we need to talk about is hashing. Hashing is taking a string, putting it through an algorithm, and making it into a string of jumbled letters and numbers. For this example, we're going to use SHA, the Secure Hashing Algorithm. We're going to use the adaptation found here. SHA, for example, will take "Hello!" and turn it into "334d016f755cd6dc58c53a86e183882f8ec14f52fb05345887c8a5edd42c87b7".
So, another problem arises is that if someone gets into your password store, if people have the same password, their hashes are going to be the same. Since the algorithm is just math, it's going to produce the same output every time you put in the same string. So how do we combat this? Salting. Salting ensures that each users' hash is different, so that way it's almost impossible to get the password from the hash.
So, here's what we need:
Here's how we start our program:
I'm going to write this code in API form, so that way, you can use this in scripts other than login scripts.
Final code:
Brief explanation on how this works:
User enters username and password -> program checks to see if user even exists -> if so, it takes the salt, appends it to the end of what the user entered, and check to see if that equals the password.
Why the salt? As previously stated, it prevents two hashes being the same. So we add a salt to the password upon creation (see the script below) so that they do not have remotely similar hashes.
Before we can test, we need to make users. Here's a quick user maker I whipped up:
Run that script a couple times, make the users, and see your passwords file. It should look something like this:
Try the login script!
In that gif, both users Eric and tutorials have the same password, but have different hashes. That way, a potential intruder won't know their passwords are the same!
Hopefully, this helped someone. If I need to revise something, let me know!
First thing we need to talk about is hashing. Hashing is taking a string, putting it through an algorithm, and making it into a string of jumbled letters and numbers. For this example, we're going to use SHA, the Secure Hashing Algorithm. We're going to use the adaptation found here. SHA, for example, will take "Hello!" and turn it into "334d016f755cd6dc58c53a86e183882f8ec14f52fb05345887c8a5edd42c87b7".
So, another problem arises is that if someone gets into your password store, if people have the same password, their hashes are going to be the same. Since the algorithm is just math, it's going to produce the same output every time you put in the same string. So how do we combat this? Salting. Salting ensures that each users' hash is different, so that way it's almost impossible to get the password from the hash.
So, here's what we need:
- ComputerCraft Computer
- SHA API (I saved mine to the file "/sha")
- Time and patience
Here's how we start our program:
os.loadAPI("sha")
local passPath = "passwords" --Change this if you want a different password file
if not fs.exists(passPath) then --Create a password file
local f = fs.open(passPath, "w")
f.write(textutils.serialize({}))
f.close()
end
This first code block makes a password file if not given yet. Now we need to make sure that we have a secure input, meaning the user cannot terminate it. I tend to write my own, so I'll include it here. If I remember correctly, you can securely use the read() function if you set os.pullEvent to os.pullEventRaw before the read function.
os.loadAPI("sha")
local passPath = "passwords" --Change this if you want a different password file
if not fs.exists(passPath) then --Create a password file
local f = fs.open(passPath, "w")
f.write(textutils.serialize({}))
f.close()
end
local function secureInput( mask, prestring )
local l = true
term.setCursorBlink(true)
local prestring = prestring or ""
local str = ""
local sx, sy = term.getCursorPos()
while l do
local e, a, b, c, d = os.pullEventRaw()
if e == "char" then
str = str .. a
elseif e == "key" then
if a == 14 then
str = str:sub(1, -2)
elseif a == 28 then
l = false
term.setBackgroundColour( colours.black )
term.setTextColour( colours.white )
print()
return str
end
end
term.setCursorPos(sx, sy)
term.setBackgroundColour( colours.white )
term.setTextColour( colours.black )
term.clearLine()
if mask then
write( prestring .. " > " .. string.rep(mask, #str) )
else
write( prestring .. " > " .. str )
end
end
end
I'm going to write this code in API form, so that way, you can use this in scripts other than login scripts.
Final code:
Spoiler
os.loadAPI("sha")
local passPath = "passwords" --Change this if you want a different password file
if not fs.exists(passPath) then --Create a password file
local f = fs.open(passPath, "w")
f.write(textutils.serialize({}))
f.close()
end
local function secureInput( mask, prestring )
local l = true
term.setCursorBlink(true)
local prestring = prestring or ""
local str = ""
local sx, sy = term.getCursorPos()
while l do
local e, a, b, c, d = os.pullEventRaw()
if e == "char" then
str = str .. a
elseif e == "key" then
if a == 14 then
str = str:sub(1, -2)
elseif a == 28 then
l = false
term.setBackgroundColour( colours.black )
term.setTextColour( colours.white )
print()
return str
end
end
term.setCursorPos(sx, sy)
term.setBackgroundColour( colours.white )
term.setTextColour( colours.black )
term.clearLine()
if mask then
write( prestring .. " > " .. string.rep(mask, #str) )
else
write( prestring .. " > " .. str )
end
end
end
local function getPass( usr )
local f = fs.open(passPath,"r")
local pwds = textutils.unserialize( f.readAll() )
f.close()
if pwds[ usr ] then
return pwds[ usr ]
else
return false
end
end
local function login( usr, pwd )
local info = getPass( usr )
if info then
if sha.sha256( pwd .. info.salt ) == info.pwd then
return true
else
return false
end
else
return false
end
end
--Login script--
local loop = true
while loop do
local username = secureInput( nil, "username" )
local password = secureInput( "x", "password" )
if login( username, password ) then
print("Login successfull!")
loop = false
break
--DO SOMETHING
else
print("Login failed.")
--REMOVE these 2 lines below so that the loop continues after incorrect login, once proven program works.
loop = false
break
end
end
Brief explanation on how this works:
User enters username and password -> program checks to see if user even exists -> if so, it takes the salt, appends it to the end of what the user entered, and check to see if that equals the password.
Why the salt? As previously stated, it prevents two hashes being the same. So we add a salt to the password upon creation (see the script below) so that they do not have remotely similar hashes.
Before we can test, we need to make users. Here's a quick user maker I whipped up:
os.loadAPI("sha")
write("username :: ")
local u = read()
write("password :: ")
local p = read()
--Again, change passPath here
local passPath = "passwords"
local f = fs.open(passPath,"r")
local usrs = textutils.unserialize(f.readAll())
f.close()
if not usrs[u] then
local salt = os.time()
usrs[u] = {
pwd = sha.sha256( p .. salt ),
salt = salt,
}
local f = fs.open(passPath, "w")
f.write( textutils.serialize( usrs ) )
f.close()
end
As you can see, upon creation, passwords are salted using the os.time(). The salt can be anything, as long as its unique. Though technically in theory os.time() could be similar, it's quite unlikely that there'll be a collision, so it's good enough for now.Run that script a couple times, make the users, and see your passwords file. It should look something like this:
Spoiler
Try the login script!
In that gif, both users Eric and tutorials have the same password, but have different hashes. That way, a potential intruder won't know their passwords are the same!
Hopefully, this helped someone. If I need to revise something, let me know!
Edited on 31 August 2016 - 05:54 PM