finished product without tests yet

This commit is contained in:
yanvoi 2024-05-26 15:34:09 +02:00
parent 5599850c59
commit 673a455ada
5 changed files with 149 additions and 20 deletions

View File

@ -1 +1,8 @@
# HasswordManager # HasswordManager
A simple password manager that uses a master password to encrypt and decrypt passwords. \
The master password is hashed with salt and pepper. \
The passwords are stored encrypted in sqlite3 database and are decrypted only when needed.
## Authors:
- Jan Wojciechowski
- Sebastian Jerzykiewicz

View File

@ -51,7 +51,7 @@ create_mpass conn = do
create_mpass conn create_mpass conn
Ut.TooShort -> do Ut.TooShort -> do
Ut.set_red Ut.set_red
putStrLn "Password is too short!!!\n" putStrLn "Password is too short (min 8 symbols)!!!\n"
Ut.reset_color Ut.reset_color
create_mpass conn create_mpass conn
Ut.Empty -> do Ut.Empty -> do
@ -62,7 +62,7 @@ create_mpass conn = do
input_new_valid_mpass :: IO (Ut.MasterPasswordValidationCases) input_new_valid_mpass :: IO (Ut.MasterPasswordValidationCases)
input_new_valid_mpass = do input_new_valid_mpass = do
(password1:password2:_) <- sequence [putStrLn "Please Enter new MASTER PASSWORD:" >> Ut.get_password, putStrLn "Confirm password:" >> Ut.get_password] (password1:password2:_) <- sequence [putStrLn "Please Enter new MASTER PASSWORD:" >> Ut.get_password, putStrLn "Enter the password again:" >> Ut.get_password]
return $ Ut.validate_password password1 password2 return $ Ut.validate_password password1 password2
open_hassword_book :: Connection -> IO (String) open_hassword_book :: Connection -> IO (String)
@ -83,10 +83,10 @@ get_action_choice :: IO (Ut.ActionChoice)
get_action_choice = do get_action_choice = do
putStrLn "\nWhat would you like to do?" putStrLn "\nWhat would you like to do?"
putStrLn "1. List all entries" putStrLn "1. List all entries"
putStrLn "2. Copy entry password" putStrLn "2. Copy an entry password"
putStrLn "3. Add a new entry" putStrLn "3. Add a new entry"
putStrLn "4. Delete an entry" putStrLn "4. Delete an entry"
putStrLn "5. Update an entry" putStrLn "5. Update an entry password"
putStrLn "6. Change Master Password" putStrLn "6. Change Master Password"
putStrLn "7. Close the application" putStrLn "7. Close the application"
choice <- getLine choice <- getLine
@ -105,25 +105,64 @@ application_loop conn mpass = do
choice <- get_action_choice choice <- get_action_choice
case choice of case choice of
Ut.ListAllEntries -> do Ut.ListAllEntries -> do
entries <- Db.get_all_entries conn
if null entries
then do
Ut.clear_screen
Ut.set_red
putStrLn "No entries to list!!!"
Ut.reset_color
application_loop conn mpass
else do
list_all_entries conn list_all_entries conn
application_loop conn mpass application_loop conn mpass
Ut.CopyEntryPassword -> do Ut.CopyEntryPassword -> do
entries <- Db.get_all_entries conn
if null entries
then do
Ut.clear_screen
Ut.set_red
putStrLn "No entries to copy a password from!!!"
Ut.reset_color
application_loop conn mpass
else do
copy_entry_pass conn mpass copy_entry_pass conn mpass
application_loop conn mpass application_loop conn mpass
Ut.AddNewEntry -> do Ut.AddNewEntry -> do
add_entry conn mpass add_entry conn mpass
application_loop conn mpass application_loop conn mpass
Ut.DeleteEntry -> do Ut.DeleteEntry -> do
entries <- Db.get_all_entries conn
if null entries
then do
Ut.clear_screen
Ut.set_red
putStrLn "No entries to delete!!!"
Ut.reset_color
application_loop conn mpass
else do
delete_entry conn delete_entry conn
application_loop conn mpass application_loop conn mpass
Ut.UpdateEntry -> do Ut.UpdateEntry -> do
entries <- Db.get_all_entries conn
if null entries
then do
Ut.clear_screen
Ut.set_red
putStrLn "No entries to update!!!"
Ut.reset_color
application_loop conn mpass
else do
update_entry conn mpass update_entry conn mpass
application_loop conn mpass application_loop conn mpass
Ut.ChangeMasterPassword -> do Ut.ChangeMasterPassword -> do
return () mpass <- change_mpass conn mpass
application_loop conn mpass application_loop conn mpass
Ut.InvalidAction -> do Ut.InvalidAction -> do
Ut.clear_screen
Ut.set_red
putStrLn "Invalid choice!!!" putStrLn "Invalid choice!!!"
Ut.reset_color
application_loop conn mpass application_loop conn mpass
Ut.Exit -> do Ut.Exit -> do
return () return ()
@ -145,10 +184,12 @@ copy_entry_pass conn mpass = do
Just (service, login) -> do Just (service, login) -> do
entry <- Db.get_entry conn (T.pack service) (T.pack login) entry <- Db.get_entry conn (T.pack service) (T.pack login)
setClipboard (Cr.decrypt' mpass (T.unpack $ Db.entryPassword entry)) setClipboard (Cr.decrypt' mpass (T.unpack $ Db.entryPassword entry))
Ut.clear_screen
Ut.set_green Ut.set_green
putStrLn "Password copied to clipboard!!!" putStrLn "Password copied to clipboard!!!"
Ut.reset_color Ut.reset_color
Nothing -> do Nothing -> do
Ut.clear_screen
Ut.set_red Ut.set_red
putStrLn "Invalid entry ID!!!" putStrLn "Invalid entry ID!!!"
Ut.reset_color Ut.reset_color
@ -156,13 +197,15 @@ copy_entry_pass conn mpass = do
add_entry :: Connection -> String -> IO () add_entry :: Connection -> String -> IO ()
add_entry conn mpass = do add_entry conn mpass = do
putStrLn "Please Enter the service name:" Ut.clear_screen
putStrLn "Please Enter the service name for the new entry:"
service <- getLine service <- getLine
putStrLn "Please Enter the login:" putStrLn "Please Enter the login for the new entry:"
login <- getLine login <- getLine
entry_exists <- Db.entry_already_exists conn (T.pack service) (T.pack login) entry_exists <- Db.entry_already_exists conn (T.pack service) (T.pack login)
if entry_exists if entry_exists
then do then do
Ut.clear_screen
Ut.set_red Ut.set_red
putStrLn "Entry already exists!!!" putStrLn "Entry already exists!!!"
Ut.reset_color Ut.reset_color
@ -171,6 +214,10 @@ add_entry conn mpass = do
putStrLn "Please Enter the password:" putStrLn "Please Enter the password:"
password <- Ut.get_password password <- Ut.get_password
Db.add_entry conn (T.pack service) (T.pack login) (T.pack (Cr.encrypt' mpass password)) Db.add_entry conn (T.pack service) (T.pack login) (T.pack (Cr.encrypt' mpass password))
Ut.clear_screen
Ut.set_green
putStrLn "Entry added successfully!!!"
Ut.reset_color
update_entry :: Connection -> String -> IO () update_entry :: Connection -> String -> IO ()
update_entry conn mpass = do update_entry conn mpass = do
@ -183,7 +230,12 @@ update_entry conn mpass = do
putStrLn "Please Enter the new password:" putStrLn "Please Enter the new password:"
password <- Ut.get_password password <- Ut.get_password
Db.update_entry conn (T.pack service) (T.pack login) (T.pack (Cr.encrypt' mpass password)) Db.update_entry conn (T.pack service) (T.pack login) (T.pack (Cr.encrypt' mpass password))
Ut.clear_screen
Ut.set_green
putStrLn "Entry updated successfully!!!"
Ut.reset_color
Nothing -> do Nothing -> do
Ut.clear_screen
Ut.set_red Ut.set_red
putStrLn "Invalid entry ID!!!" putStrLn "Invalid entry ID!!!"
Ut.reset_color Ut.reset_color
@ -197,8 +249,56 @@ delete_entry conn = do
case result of case result of
Just (service, login) -> do Just (service, login) -> do
Db.delete_entry conn (T.pack service) (T.pack login) Db.delete_entry conn (T.pack service) (T.pack login)
Ut.clear_screen
Ut.set_green
putStrLn "Entry deleted successfully!!!"
Ut.reset_color
Nothing -> do Nothing -> do
Ut.clear_screen
Ut.set_red Ut.set_red
putStrLn "Invalid entry ID!!!" putStrLn "Invalid entry ID!!!"
Ut.reset_color Ut.reset_color
return () return ()
change_mpass :: Connection -> String -> IO (String)
change_mpass conn mpass = do
putStrLn "Please Enter your current MASTER PASSWORD:"
current_mpass <- Ut.get_password
mpass_valid <- Db.check_if_mpass_valid conn (Cr.hash' $ fromString current_mpass)
if mpass_valid
then do
res <- input_new_valid_mpass
case res of
Ut.Valid new_mpass -> do
Db.insert_new_mpass conn (Cr.hash' $ fromString new_mpass)
entries <- Db.get_all_entries conn
mapM_ (\entry -> Db.update_entry conn (Db.entryService entry) (Db.entryLogin entry) (T.pack (Cr.encrypt' new_mpass (Cr.decrypt' mpass (T.unpack (Db.entryPassword entry)))))) entries
Ut.clear_screen
Ut.set_green
putStrLn "Master Password changed successfully!!!\n"
Ut.reset_color
return new_mpass
Ut.DoNotMatch -> do
Ut.clear_screen
Ut.set_red
putStrLn "Passwords do not match!!!\n"
Ut.reset_color
return mpass
Ut.TooShort -> do
Ut.clear_screen
Ut.set_red
putStrLn "Password is too short (min 8 symbols)!!!\n"
Ut.reset_color
return mpass
Ut.Empty -> do
Ut.clear_screen
Ut.set_red
putStrLn "Password cannot be empty!!!\n"
Ut.reset_color
return mpass
else do
Ut.clear_screen
Ut.set_red
putStrLn "Invalid MASTER PASSWORD!!!\n"
Ut.reset_color
return mpass

View File

@ -1,6 +1,9 @@
module UserInterface where module UserInterface where
import qualified Database as Db import qualified Database as Db
import qualified Data.Text as T
import qualified Utils as Ut
import Text.Read (readMaybe)
print_entries :: [Db.Entry] -> IO () print_entries :: [Db.Entry] -> IO ()
print_entries entries = do print_entries entries = do
@ -9,10 +12,18 @@ print_entries entries = do
print_entry :: (Int, Db.Entry) -> IO () print_entry :: (Int, Db.Entry) -> IO ()
print_entry (num, entry) = do print_entry (num, entry) = do
putStrLn $ show num ++ " " ++ show (Db.entryService entry) ++ " " ++ show (Db.entryLogin entry) ++ " " ++ "********" putStrLn $ show num ++ ". " ++ T.unpack (Db.entryService entry) ++ " " ++ T.unpack (Db.entryLogin entry) ++ " " ++ "********"
choose_entry :: String -> IO Int choose_entry :: String -> IO Int
choose_entry command = do choose_entry command = do
putStrLn $ command putStrLn command
entry_id <- getLine entry_id <- getLine
return (read entry_id :: Int) let res = readMaybe entry_id :: Maybe Int
case res of
Just x -> return x
Nothing -> do
Ut.set_red
putStrLn "Invalid entry ID!!!"
Ut.reset_color
choose_entry command

View File

@ -20,7 +20,8 @@ validate_password p1 p2
| otherwise = Valid p1 | otherwise = Valid p1
clear_screen :: IO () clear_screen :: IO ()
clear_screen = clearScreen clear_screen = do
clearFromCursorToScreenBeginning
set_red :: IO () set_red :: IO ()
set_red = setSGR [SetColor Foreground Vivid Red] set_red = setSGR [SetColor Foreground Vivid Red]

View File

@ -1,2 +1,12 @@
import Test.Hspec
import qualified Crypto as Cr
main :: IO () main :: IO ()
main = putStrLn "Test suite not yet implemented" main = hspec $ do
describe "Crypto" $ do
it "should encrypt and decrypt a message correctly" $ do
let originalMessage = "Hello, World!"
masterPassword = "mysecretpassword"
encryptedMessage = Cr.encrypt' masterPassword originalMessage
decryptedMessage = Cr.decrypt' masterPassword encryptedMessage
decryptedMessage `shouldBe` originalMessage