commit f16b75e3054f4097abadf4ca968c1cab9c70a5e6
Author: david.sorber <david.sorber@jacobs.com>
Date:   Fri Mar 1 15:26:50 2024 -0500

    Significant improvements including reading input from encrypted file.
    Also added crypto utility to en/decrypt a file.

diff --git a/CMakeLists.txt b/CMakeLists.txt
index f1cb994..eae33c1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -74,11 +74,20 @@ SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COMPILE_FLAGS}")
 # Dependencies
 ################################################################################
 
+# Add FindSodium.cmake to the CMake module path
+SET(CMAKE_MODULE_PATH
+    "${CMAKE_CURRENT_SOURCE_DIR}/cmake"
+    ${CMAKE_MODULE_PATH})
+
+
 #FIND_FILE(SDL2_INCLUDE_DIR NAME SDL.h HINTS SDL2)
 #FIND_LIBRARY(SDL2_LIBRARY NAME SDL2)
 FIND_PACKAGE(SDL2 REQUIRED)
 MESSAGE(STATUS "SDL2 include path: ${SDL2_INCLUDE_DIRS}")
 
+FIND_PACKAGE(Sodium REQUIRED)
+MESSAGE(STATUS "libsodium include path: ${sodium_INCLUDE_DIR}")
+
 
 ################################################################################
 # Include paths
@@ -120,4 +129,16 @@ ADD_EXECUTABLE(pwmgr
     ${code_sources})
 
 TARGET_INCLUDE_DIRECTORIES(pwmgr PUBLIC ${SDL2_INCLUDE_DIRS})
-TARGET_LINK_LIBRARIES(pwmgr ${SDL2_LIBRARIES})
+TARGET_LINK_LIBRARIES(pwmgr ${SDL2_LIBRARIES}
+                            sodium)
+
+
+################################################################################
+# Target: crypt_util
+################################################################################
+
+ADD_EXECUTABLE(crypt_util
+    ${CMAKE_CURRENT_SOURCE_DIR}/src/crypt_util.cc)
+
+TARGET_INCLUDE_DIRECTORIES(crypt_util PUBLIC ${sodium_INCLUDE_DIR})
+TARGET_LINK_LIBRARIES(crypt_util sodium)
\ No newline at end of file
diff --git a/src/Catalog.cc b/src/Catalog.cc
index 03f311c..9628bc9 100644
--- a/src/Catalog.cc
+++ b/src/Catalog.cc
@@ -1,51 +1,145 @@
 #include <algorithm>
+#include <cstring>
 #include <fstream>
 #include <iostream>
+#include <sstream>
+
+#include <sodium.h>
 
 #include "Catalog.h"
 
 Catalog::Catalog()
+    : m_loaded(false)
 {
-    // Temporarily fill the catalog with some test data
-#if 0
-    m_catalog.push_back({"Target", "baranovich@gmail.com\nF-B-3-7"});
-    m_catalog.push_back({"United", "david.sorber@gmail.com\nF-B-3-7"});
-    m_catalog.push_back({"Pepco", "david.sorber@gmail.com\nNZ-98-O"});
-    m_catalog.push_back({"Washington Gas", "baranovich@gmail.com\nS-N-718"});
-#else
-    const std::string pwFile("/home/dsorber/Documents/chicken_soup/chicken_soup.txt");
-
-    std::ifstream infile(pwFile);
-    if (! infile)
+    if (sodium_init() != 0)
     {
-        return;
+        // TODO: throw exception here
+//        return 1;
     }
+}
+
+Catalog::~Catalog()
+{
+}
+
+int Catalog::load(
+    char* filePath,
+    char* passwordBuffer,
+    uint32_t passwordBufferSize,
+    float* complete)
+{
+    const uint32_t CHUNK_SIZE{4096};
+    const uint32_t saltLen{crypto_pwhash_SALTBYTES};
+    const uint32_t keyLen{crypto_secretstream_xchacha20poly1305_KEYBYTES};
+    int ret = 0;
+
+    // Setup salt and key buffers
+    uint8_t salt[saltLen]{};
+    uint8_t key[keyLen]{};
+    uint8_t inBuffer[CHUNK_SIZE + crypto_secretstream_xchacha20poly1305_ABYTES]{};
+    uint8_t outBuffer[CHUNK_SIZE]{};
+    uint8_t header[crypto_secretstream_xchacha20poly1305_HEADERBYTES]{};
+    crypto_secretstream_xchacha20poly1305_state state;
+    std::stringstream fileContents;
+    std::string decryptedData;
+    uint32_t decryptedDataSize = 0;
 
-    // Iterate over the PW file and parse it (key is first line of each stanza
-    // and the value are the remaining lines)
     std::string line;
-    std::string key;
+    std::string valueKey;
     std::string value;
     bool firstLine = true;
-    while (std::getline(infile, line))
+
+    decryptedData.reserve(8 * CHUNK_SIZE);
+
+    // Read in salt value
+    FILE* ctFile = std::fopen(filePath, "rb");
+    std::fread(salt, 1, saltLen, ctFile);
+
+    // Generate key from password and salt
+    if (crypto_pwhash(key, sizeof key, passwordBuffer,
+                      std::strlen(passwordBuffer), salt,
+                      crypto_pwhash_OPSLIMIT_INTERACTIVE,
+                      crypto_pwhash_MEMLIMIT_INTERACTIVE,
+                      crypto_pwhash_ALG_ARGON2ID13) != 0)
+    {
+        ret = ERR_PWHASH_OUT_OF_MEMORY;
+        goto end_load;
+    }
+
+    // Explicitly zeroize the password buffer
+    explicit_bzero(passwordBuffer, passwordBufferSize);
+
+    // Read in header and init crypto algorithm state
+    std::fread(header, 1, sizeof header, ctFile);
+    if (crypto_secretstream_xchacha20poly1305_init_pull(&state, header, key) != 0)
+    {
+        ret = ERR_DECRYPT_INCOMPLETE_HEADER;
+        goto end_load;
+    }
+
+    // Decrypt the CT file chunk by chunk
+    size_t readlen;
+    long long unsigned numOutBytes;
+    int eof;
+    uint8_t tag;
+    do
+    {
+        readlen = fread(inBuffer, 1, sizeof inBuffer, ctFile);
+        eof = feof(ctFile);
+
+        int rc = crypto_secretstream_xchacha20poly1305_pull(&state, outBuffer,
+                                                            &numOutBytes,
+                                                            &tag,inBuffer,
+                                                            readlen, nullptr,
+                                                            0);
+        if (rc != 0)
+        {
+            ret = ERR_DECRYPT_CORRUPTED_CHUNK;
+            goto end_load;
+        }
+
+        if (tag == crypto_secretstream_xchacha20poly1305_TAG_FINAL && ! eof)
+        {
+            ret = ERR_DECRYPT_PREMATURE_END;
+            goto end_load;
+        }
+
+        // Append the decryted buffer to the decrypted data
+        decryptedData.resize(decryptedDataSize + numOutBytes);
+        std::memcpy(decryptedData.data() + decryptedDataSize, outBuffer, numOutBytes);
+        decryptedDataSize += numOutBytes;
+    }
+    while (! eof);
+
+    // Zero out the crypto buffers that are no longer needed
+    explicit_bzero(salt, saltLen);
+    explicit_bzero(key, keyLen);
+    explicit_bzero(inBuffer, CHUNK_SIZE);
+    explicit_bzero(outBuffer, CHUNK_SIZE);
+
+    // Create a stringstream in order to iterate line by line
+    fileContents << decryptedData;
+
+    // Iterate line by line and parse the simple file format
+    while (std::getline(fileContents, line))
     {
         if (line.size() == 0)
         {
             // Debuggery
-//            std::cout << "KEY: " << key << std::endl;
+//            std::cout << "KEY: " << valueKey << std::endl;
 //            std::cout << "VAL: " << value << "\n" << std::endl;
 
-            m_catalog.push_back({key, value});
+            m_catalog.push_back({valueKey, value});
             firstLine = true;
         }
         else
         {
             if (firstLine)
             {
-                key.clear();
+                valueKey.clear();
                 value.clear();
 
-                key.swap(line);
+                valueKey.swap(line);
                 firstLine = false;
             }
             else
@@ -56,30 +150,23 @@ Catalog::Catalog()
     }
 
     // Debuggery
-//    std::cout << "KEY: " << key << std::endl;
+//    std::cout << "KEY: " << valueKey << std::endl;
 //    std::cout << "VAL: " << value << "\n" << std::endl;
-    m_catalog.push_back({key, value});
+    m_catalog.push_back({valueKey, value});
 
-    // Close the input file
-    infile.close();
+    sort();
 
+    // Now we loaded
+    m_loaded = true;
 
-#endif
 
-    // Sort the catalog (A -> Z) for display purposes
-    std::sort(
-        m_catalog.begin(),
-        m_catalog.end(),
-        [](auto const& lhs, auto const& rhs)
-        {
-            return lhs.getLcKey() < rhs.getLcKey();
-        });
 
+    // End of the line
+end_load:
+    std::fclose(ctFile);
+    return ret;
 }
 
-Catalog::~Catalog()
-{
-}
 
 void Catalog::setAllVisible()
 {
@@ -103,4 +190,16 @@ void Catalog::filter(const std::string& criteria)
             item.setVisibilty(false);
         }
     }
+}
+
+void Catalog::sort()
+{
+    // Sort the catalog (A -> Z) for display purposes
+    std::sort(
+            m_catalog.begin(),
+            m_catalog.end(),
+            [](auto const& lhs, auto const& rhs)
+            {
+                return lhs.getLcKey() < rhs.getLcKey();
+            });
 }
\ No newline at end of file
diff --git a/src/Catalog.h b/src/Catalog.h
index 93b0995..71c8459 100644
--- a/src/Catalog.h
+++ b/src/Catalog.h
@@ -1,6 +1,7 @@
 #ifndef __CATALOG_H__
 #define __CATALOG_H__
 
+#include <climits>
 #include <cstdint>
 #include <string>
 #include <vector>
@@ -16,6 +17,17 @@ public:
 
     ~Catalog();
 
+    int load(
+        char* filePath,
+        char* passwordBuffer,
+        uint32_t passwordBufferSize,
+        float* complete);
+
+    inline bool getLoaded() const
+    {
+        return m_loaded;
+    }
+
     inline std::vector<CatalogItem>::iterator begin()
     {
         return m_catalog.begin();
@@ -30,10 +42,38 @@ public:
 
     void filter(const std::string& criteria);
 
+    static const char* errToString(int err)
+    {
+        switch (err)
+        {
+            case ERR_PWHASH_OUT_OF_MEMORY:
+                return "pwhash: out of memory";
+
+            case ERR_DECRYPT_INCOMPLETE_HEADER:
+                return "decrypt: incomplete header";
+
+            case ERR_DECRYPT_CORRUPTED_CHUNK:
+                return "decrypt: corrupted chunk";
+
+            case ERR_DECRYPT_PREMATURE_END:
+                return "decrypt: premature end of file";
+
+            default:
+                return "unknown error";
+        }
+    }
 
 
 private:
 
+    static inline const int ERR_PWHASH_OUT_OF_MEMORY         = INT_MIN + 19;
+    static inline const int ERR_DECRYPT_INCOMPLETE_HEADER    = INT_MIN + 20;
+    static inline const int ERR_DECRYPT_CORRUPTED_CHUNK      = INT_MIN + 21;
+    static inline const int ERR_DECRYPT_PREMATURE_END        = INT_MIN + 22;
+
+    void sort();
+
+    bool m_loaded;
     std::vector<CatalogItem> m_catalog;
 
 };
diff --git a/src/StreamUtils.h b/src/StreamUtils.h
new file mode 100644
index 0000000..f092709
--- /dev/null
+++ b/src/StreamUtils.h
@@ -0,0 +1,93 @@
+#include <cmath>
+#include <cstdint>
+#include <iomanip>
+#include <iostream>
+#include <ostream>
+
+struct format_hex
+{
+    uint64_t value;
+    const uint32_t width;
+    const char fill;
+
+    format_hex(uint64_t val, uint32_t width_=4, char fill_='0')
+            : value(val),
+              width(width_),
+              fill(fill_)
+    {
+    }
+
+    friend inline std::ostream& operator<<(
+            std::ostream& os,
+            const format_hex& rhs)
+    {
+        return os << std::hex << std::setfill(rhs.fill) << std::setw(rhs.width)
+                  << std::uppercase << static_cast<long>(rhs.value) << std::dec;
+    }
+};
+
+
+/**
+ * Stream version of hex bytes inline little endian
+ */
+struct HexBytesInlineLE
+{
+    const uint8_t* data;
+    double numBytes;
+    const char* sep;
+
+    HexBytesInlineLE(const char* data_, double numBytes_, const char* sep_=" ")
+            : data(reinterpret_cast<const uint8_t*>(data_)), numBytes(numBytes_), sep(sep_)
+    {
+    }
+
+    HexBytesInlineLE(const uint8_t* data_, double numBytes_, const char* sep_=" ")
+            : data(data_), numBytes(numBytes_), sep(sep_)
+    {
+    }
+
+    friend inline std::ostream& operator<<(
+            std::ostream& os,
+            const HexBytesInlineLE& rhs)
+    {
+        for (int32_t idx = rhs.numBytes - 1; idx > -1; --idx)
+        {
+            os << format_hex(rhs.data[idx], 2) << ((idx <= (rhs.numBytes - 1)) ? rhs.sep : "");
+        }
+
+        return os;
+    }
+};
+
+
+/**
+ * Stream version of hex bytes inline big endian
+ */
+struct HexBytesInlineBE
+{
+    const uint8_t* data;
+    double numBytes;
+    const char* sep;
+
+    HexBytesInlineBE(const char* data_, double numBytes_, const char* sep_=" ")
+            : data(reinterpret_cast<const uint8_t*>(data_)), numBytes(numBytes_), sep(sep_)
+    {
+    }
+
+    HexBytesInlineBE(const uint8_t* data_, double numBytes_, const char* sep_=" ")
+            : data(data_), numBytes(numBytes_), sep(sep_)
+    {
+    }
+
+    friend inline std::ostream& operator<<(
+            std::ostream& os,
+            const HexBytesInlineBE& rhs)
+    {
+        for (uint32_t idx = 0; idx < rhs.numBytes; ++idx)
+        {
+            os << format_hex(rhs.data[idx], 2) << ((idx < (rhs.numBytes - 1)) ? rhs.sep : "");
+        }
+
+        return os;
+    }
+};
diff --git a/src/crypt_util.cc b/src/crypt_util.cc
new file mode 100644
index 0000000..5fec366
--- /dev/null
+++ b/src/crypt_util.cc
@@ -0,0 +1,296 @@
+// Modifiy from source found here: https://gist.github.com/leto/1a984b1daa710ed55464c093640d6769
+
+#include <cstdint>
+#include <cstdio>
+#include <cstring>
+#include <iostream>
+#include <string>
+#include <termios.h>
+#include <unistd.h>
+
+#include <sodium.h>
+
+#include "StreamUtils.h"
+
+const std::string BOLD{"\033[1m"};
+const std::string ENDC{"\033[0m"};
+const std::string RED{"\033[31m"};
+const std::string YELLOW{"\033[33m"};
+const std::string PURPLE{"\033[35m"};
+const std::string LBLUE{"\033[1;34m"};
+const std::string UP_ONE = "\033[1A";
+const uint32_t CHUNK_SIZE{4096};
+
+#define ERROR_MSG       BOLD << RED << "ERROR: " << ENDC
+
+
+void get_password(char* password)
+{
+    static struct termios old_terminal;
+    static struct termios new_terminal;
+
+    // get settings of the actual terminal
+    tcgetattr(STDIN_FILENO, &old_terminal);
+
+    // do not echo the characters
+    new_terminal = old_terminal;
+    new_terminal.c_lflag &= ~(ECHO);
+
+    // set this as the new terminal options
+    tcsetattr(STDIN_FILENO, TCSANOW, &new_terminal);
+
+    // get the password
+    // the user can add chars and delete if he puts it wrong
+    // the input process is done when he hits the enter
+    // the \n is stored, we replace it with \0
+    if (fgets(password, BUFSIZ, stdin) == NULL)
+    {
+        password[0] = '\0';
+    }
+    else
+    {
+        password[strlen(password) - 1] = '\0';
+    }
+
+    // go back to the old settings
+    tcsetattr(STDIN_FILENO, TCSANOW, &old_terminal);
+}
+
+static int encrypt(
+    const char *target_file,
+    const char *source_file,
+    const uint8_t key[crypto_secretstream_xchacha20poly1305_KEYBYTES],
+    const uint8_t salt[crypto_pwhash_SALTBYTES])
+{
+    // NOTE: the salt value is not used directly in encrpytion but is written
+    // to the first XYZ bytes of the output file
+
+    uint8_t buf_in[CHUNK_SIZE]{};
+    uint8_t buf_out[CHUNK_SIZE + crypto_secretstream_xchacha20poly1305_ABYTES]{};
+    uint8_t header[crypto_secretstream_xchacha20poly1305_HEADERBYTES]{};
+    crypto_secretstream_xchacha20poly1305_state st;
+
+    FILE* fp_s = std::fopen(source_file, "rb");
+    FILE* fp_t = std::fopen(target_file, "wb");
+
+    // First write out salt bytes to the output file
+    std::fwrite(salt, 1, crypto_pwhash_SALTBYTES, fp_t);
+
+    // Next write out header
+    crypto_secretstream_xchacha20poly1305_init_push(&st, header, key);
+    std::fwrite(header, 1, sizeof(header), fp_t);
+
+    // Iterate over the input file a chunk at a time, encrypt each, and then
+    // write it to the output file
+    size_t rlen;
+    long long unsigned out_len;
+    int eof;
+    uint8_t tag;
+    do
+    {
+        rlen = std::fread(buf_in, 1, sizeof(buf_in), fp_s);
+        eof = std::feof(fp_s);
+        tag = eof ? crypto_secretstream_xchacha20poly1305_TAG_FINAL : 0;
+        crypto_secretstream_xchacha20poly1305_push(&st, buf_out, &out_len, buf_in,
+                                                   rlen,nullptr, 0, tag);
+        std::fwrite(buf_out, 1, (size_t)out_len, fp_t);
+    }
+    while (! eof);
+
+    std::fclose(fp_t);
+    std::fclose(fp_s);
+
+    return 0;
+}
+
+static int decrypt(
+    const char *target_file,
+    const char *source_file,
+    const uint8_t key[crypto_secretstream_xchacha20poly1305_KEYBYTES],
+    uint32_t saltLength)
+{
+    // NOTE: the "saltLength" parameter is used to skip past the salt value at
+    // the beginning of the file
+
+    int ret = -1;
+    uint8_t buf_in[CHUNK_SIZE + crypto_secretstream_xchacha20poly1305_ABYTES]{};
+    uint8_t buf_out[CHUNK_SIZE]{};
+    uint8_t header[crypto_secretstream_xchacha20poly1305_HEADERBYTES]{};
+    crypto_secretstream_xchacha20poly1305_state st;
+
+    FILE* fp_s = std::fopen(source_file, "rb");
+    FILE* fp_t = std::fopen(target_file, "wb");
+
+    // Skip past the salt value at the beginning of the file
+    std::fseek(fp_s, saltLength, SEEK_SET);
+
+    std::fread(header, 1, sizeof header, fp_s);
+
+    if (crypto_secretstream_xchacha20poly1305_init_pull(&st, header, key) != 0)
+    {
+        std::cerr << ERROR_MSG << "during decrypt: incomplete header" << std::endl;
+        goto ret;
+    }
+
+    size_t rlen;
+    long long unsigned out_len;
+    int eof;
+    uint8_t tag;
+    do
+    {
+        rlen = fread(buf_in, 1, sizeof buf_in, fp_s);
+        eof = feof(fp_s);
+
+        int rc = crypto_secretstream_xchacha20poly1305_pull(&st, buf_out,
+                                                            &out_len, &tag,
+                                                            buf_in, rlen,
+                                                            nullptr, 0);
+        if (rc != 0)
+        {
+            std::cerr << ERROR_MSG << "during decrypt: corrupted chunk" << std::endl;
+            goto ret;
+        }
+
+        if (tag == crypto_secretstream_xchacha20poly1305_TAG_FINAL && ! eof)
+        {
+            std::cerr << ERROR_MSG << "during decrypt: premature end of file" << std::endl;
+            goto ret;
+        }
+        std::fwrite(buf_out, 1, (size_t) out_len, fp_t);
+    }
+    while (! eof);
+
+    ret = 0;
+
+ret:
+    std::fclose(fp_t);
+    std::fclose(fp_s);
+    return ret;
+}
+
+int main(int argc, char** argv)
+{
+    const uint32_t saltLen{crypto_pwhash_SALTBYTES};
+    const uint32_t keyLen{crypto_secretstream_xchacha20poly1305_KEYBYTES};
+    char passwordBuffer[BUFSIZ]{};
+
+    if (sodium_init() != 0)
+    {
+        return 1;
+    }
+
+    uint8_t key[keyLen]{};
+#if 1
+    uint8_t salt[saltLen]{};
+#else
+    uint8_t salt[saltLen]{0x1A, 0x19, 0x2F, 0x62, 0x65, 0x9A,
+                          0x9C, 0x64, 0x99, 0xA7, 0xD1, 0x3B,
+                          0x02, 0xC4, 0xA1, 0xC9};
+#endif
+
+    // DEBUGGERY
+//    std::cout << "SALT: " << HexBytesInlineBE(salt, saltLen) << std::endl;
+//    std::cout << "KEY: " << HexBytesInlineBE(key, keyLen) << std::endl;
+
+    if (argc < 2)
+    {
+        std::cerr << ERROR_MSG << "please provide correct number of arguments!"
+                  << std::endl;
+        return -1;
+    }
+
+
+    if (std::strlen(argv[1]) == 3 && std::strncmp(argv[1], "enc", 3) == 0)
+    {
+        // Encrypt the file
+
+        // TODO: confirm input file exists
+
+        // Generate random salt
+        randombytes_buf(salt, saltLen);
+
+        std::cout << "Password: ";
+        get_password(passwordBuffer);
+        std::cout << std::endl;
+
+        // Generate key from password and salt
+        if (crypto_pwhash(key, sizeof key, passwordBuffer,
+                          std::strlen(passwordBuffer), salt,
+                          crypto_pwhash_OPSLIMIT_INTERACTIVE,
+                          crypto_pwhash_MEMLIMIT_INTERACTIVE,
+                          crypto_pwhash_ALG_ARGON2ID13) != 0)
+        {
+            std::cerr << ERROR_MSG << "crypto_pwhash - out of memory" << std::endl;
+            return -1;
+        }
+
+        // Explicitly zeroize the password buffer
+        explicit_bzero(passwordBuffer, BUFSIZ);
+
+        // Create a name for the cipher text file by appending ".enc" to the
+        // name of the plain text file
+        std::string plainTextFile(argv[2]);
+        std::string cipherTextFile(plainTextFile + ".enc");
+
+        std::cout << "PT: " << plainTextFile << std::endl;
+        std::cout << "CT: " << cipherTextFile << std::endl;
+
+        if (encrypt(cipherTextFile.c_str(),
+                    plainTextFile.c_str(),
+                    key, salt) != 0)
+        {
+            return 1;
+        }
+    }
+    else if (std::strlen(argv[1]) == 3 && std::strncmp(argv[1], "dec", 3) == 0)
+    {
+        // Decrypt the file
+
+        // TODO: confirm input file exists
+
+        // Create a name for the cipher text file by appending ".enc" to the
+        // name of the plain text file
+        std::string cipherTextFile(argv[2]);
+        std::string plainTextFile(cipherTextFile + ".dec");
+
+        std::cout << "CT: " << cipherTextFile << std::endl;
+        std::cout << "PT: " << plainTextFile << std::endl;
+
+        std::cout << "Password: ";
+        get_password(passwordBuffer);
+        std::cout << std::endl;
+
+        // Read in salt value
+        FILE* ctFile = std::fopen(cipherTextFile.c_str(), "rb");
+        std::fread(salt, 1, saltLen, ctFile);
+        std::fclose(ctFile);
+
+        // Generate key from password and salt
+        if (crypto_pwhash(key, sizeof key, passwordBuffer,
+                          std::strlen(passwordBuffer), salt,
+                          crypto_pwhash_OPSLIMIT_INTERACTIVE,
+                          crypto_pwhash_MEMLIMIT_INTERACTIVE,
+                          crypto_pwhash_ALG_ARGON2ID13) != 0)
+        {
+            std::cerr << ERROR_MSG << "crypto_pwhash - out of memory" << std::endl;
+            return -1;
+        }
+
+        // Explicitly zeroize the password buffer
+        explicit_bzero(passwordBuffer, BUFSIZ);
+
+        if (decrypt(plainTextFile.c_str(),
+                    cipherTextFile.c_str(),
+                    key, saltLen) != 0)
+        {
+            return 1;
+        }
+    }
+    else
+    {
+        std::cerr << ERROR_MSG << "invalid mode \"" << argv[1] << "\"!" << std::endl;
+        return 2;
+    }
+
+    return 0;
+}
\ No newline at end of file
diff --git a/src/main.cpp b/src/main.cpp
index 48516b0..214e0bd 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -10,6 +10,7 @@
 
 #include <cstdio>
 #include <cstdint>
+#include <cstring>
 #include <iostream>
 #include <map>
 #include <string>
@@ -29,7 +30,8 @@
 #endif
 
 const std::string APP_NAME("pwmgr");
-const std::string VERSION("v0.0.1");
+const std::string VERSION("v0.1.0");
+const std::string DEFAULT_FILEPATH("/home/dsorber/Documents/chicken_soup/chicken_soup.txt.enc");
 
 const std::string BOLD{"\033[1m"};
 const std::string ENDC{"\033[0m"};
@@ -39,6 +41,8 @@ const std::string PURPLE{"\033[35m"};
 const std::string LBLUE{"\033[1;34m"};
 const std::string UP_ONE = "\033[1A";
 
+const uint32_t BUFFER_SIZE{2048};
+
 
 #define ERROR_MSG       BOLD << RED << "ERROR: " << ENDC
 
@@ -63,7 +67,7 @@ int catalogFilterCallback(ImGuiInputTextCallbackData* data)
         }
 
         // Debuggery
-//        std::cout << "BUFFER: " << data->Buf << std::endl;
+        // std::cout << "BUFFER: " << data->Buf << std::endl;
 
         // Now filter the catalog
         catalog.filter(data->Buf);
@@ -164,6 +168,14 @@ int main(int argc, char** argv)
 
     // Main loop
     bool done = false;
+    bool dataLoaded = false;
+    bool loading = false;
+    bool showOptionsWin = false;
+    char filterBuffer[128]{};
+    char filePathBuffer[BUFFER_SIZE]{};
+    std::memcpy(filePathBuffer, DEFAULT_FILEPATH.data(), DEFAULT_FILEPATH.size());
+
+    char passwdBuffer[BUFFER_SIZE]{};
     while (! done)
     {
         // Poll and handle events (inputs, window resize, etc.)
@@ -210,93 +222,177 @@ int main(int argc, char** argv)
 #endif
         ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
         ImGui::Begin("inner_window", nullptr,
-                     ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_MenuBar);
+                     ImGuiWindowFlags_NoDecoration |
+                     ImGuiWindowFlags_MenuBar |
+                     ImGuiWindowFlags_NoSavedSettings);
 
 
         ImGui::PushFont(font);
         {
-            //---[Menu bar]-----------------------------------------------------
-            if (ImGui::BeginMenuBar())
+            if (! dataLoaded)
             {
-                if (ImGui::BeginMenu("File"))
+                //---Data loading screen----------------------------------------
+                ImGui::Text("Hello from the loading screen");
+                ImGui::InputText("Input file", filePathBuffer,
+                                 IM_ARRAYSIZE(filePathBuffer));
+
+                // Set default focus on the password text box
+                // See: https://github.com/ocornut/imgui/issues/455
+                if (ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) &&
+                    !ImGui::IsAnyItemActive() &&
+                    !ImGui::IsMouseClicked(0))
                 {
-                    if (ImGui::MenuItem("Close", "Ctrl+W"))
-                    {
-                        std::cerr << ERROR_MSG << "Closing it out" << std::endl;
-                        done = true;
-                    }
-                    ImGui::EndMenu();
+                    ImGui::SetKeyboardFocusHere(0);
                 }
-                ImGui::EndMenuBar();
-            }
 
-            //---[Left side]----------------------------------------------------
-            static int selected = 0;
-            {
-                ImGui::BeginChild("left_pane", ImVec2(150, 0),
-                                  ImGuiChildFlags_Border | ImGuiChildFlags_ResizeX);
+                if (ImGui::InputText("Password", passwdBuffer,
+                                     IM_ARRAYSIZE(passwdBuffer),
+                                     ImGuiInputTextFlags_Password |
+                                     ImGuiInputTextFlags_EnterReturnsTrue))
+                {
+                    loading = true;
+                }
+
+
+                if (ImGui::Button("Exit"))
+                {
+                    done = true;
+                }
+                ImGui::SameLine();
 
-                for (auto& iter : catalog)
+                if (ImGui::Button("Load"))
                 {
-                    // ImGuiInputTextFlags_ReadOnly
-                    if (iter.visible() &&
-                        ImGui::CollapsingHeader(iter.getKey().c_str()))
+                    loading = true;
+                }
+                ImGui::SameLine();
+
+                if (loading)
+                {
+                    ImGui::Text("Loading...");
+
+                    // Load the catalog from the encrypted file
+                    int rc = catalog.load(filePathBuffer,
+                                          passwdBuffer,
+                                          BUFFER_SIZE,
+                                          nullptr);
+
+//                    ImGui::ProgressBar(
+//                            sinf((float) ImGui::GetTime()) * 0.5f + 0.5f,
+//                            ImVec2(ImGui::GetFontSize() * 25, 0.0f));
+
+                    // Switch modes once loaded
+                    if (rc == 0 && catalog.getLoaded())
+                    {
+                        dataLoaded = true;
+                    }
+                    else
                     {
-//                        ImGui::Text("%s", iter.second.c_str());
-                        ImGui::InputTextMultiline(
-                            iter.getLcKey().c_str(),
-                            iter.getValueBuffer(),
-                         iter.getValueBufferSize(),
-                            ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 4),
-                            MULTILINE_TEXT_FLAGS);
+                        // Display error
+                        std::cerr << ERROR_MSG << Catalog::errToString(rc) << std::endl;
                     }
                 }
 
-                ImGui::EndChild();
             }
-            ImGui::SameLine();
-
-            //---[Right side]---------------------------------------------------
+            else
             {
-                ImGui::BeginGroup();
-                ImGui::BeginChild("item view",
-                                  ImVec2(0, -ImGui::GetFrameHeightWithSpacing()));
-
-                char filterBuffer[128]{};
-                ImGui::InputText("Filter", filterBuffer,
-                                 IM_ARRAYSIZE(filterBuffer),
-                                 ImGuiInputTextFlags_CallbackEdit,
-                                 catalogFilterCallback);
-
-                ImGui::Separator();
-                if (ImGui::BeginTabBar("##Tabs", ImGuiTabBarFlags_None))
+                //---[Menu bar]-----------------------------------------------------
+                if (ImGui::BeginMenuBar())
                 {
-                    if (ImGui::BeginTabItem("Options"))
+                    if (ImGui::BeginMenu("File"))
                     {
-                        ImGui::TextWrapped(
-                        "Lorem ipsum dolor sit amet, consectetur adipiscing "
-                        "elit, sed do eiusmod tempor incididunt ut labore et"
-                        " dolore magna aliqua. ");
-                        ImGui::EndTabItem();
+                        if (ImGui::MenuItem("Close", "Ctrl+W"))
+                        {
+                            std::cerr << ERROR_MSG << "Closing it out"
+                                      << std::endl;
+                            done = true;
+                        }
+                        ImGui::EndMenu();
                     }
 
-                    if (ImGui::BeginTabItem("About"))
+                    // Experimental.. maybe make this the "About" window?
+                    if (ImGui::BeginMenu("Misc"))
                     {
-                        ImGui::Text("%s version: %s", APP_NAME.c_str(),
-                                                          VERSION.c_str());
-                        ImGui::Text("Compiled on: %s ", __TIMESTAMP__);
-                        ImGui::Text("Created by: dsorber");
-                        ImGui::EndTabItem();
+                        if (ImGui::MenuItem("About", "Ctrl+M"))
+                        { ;
+                        }
+                        ImGui::EndMenu();
                     }
-                    ImGui::EndTabBar();
+                    ImGui::EndMenuBar();
                 }
-                ImGui::EndChild();
 
-                if (ImGui::Button("Revert")) {}
+                //---[Left side]----------------------------------------------------
+                static int selected = 0;
+                {
+                    ImGui::BeginChild("left_pane", ImVec2(150, 0),
+                                      ImGuiChildFlags_Border |
+                                      ImGuiChildFlags_ResizeX);
+
+                    for (auto &iter: catalog)
+                    {
+                        // ImGuiInputTextFlags_ReadOnly
+                        if (iter.visible() &&
+                            ImGui::CollapsingHeader(iter.getKey().c_str()))
+                        {
+                            ImGui::InputTextMultiline(
+                                    iter.getLcKey().c_str(),
+                                    iter.getValueBuffer(),
+                                    iter.getValueBufferSize(),
+                                    ImVec2(-FLT_MIN,
+                                           ImGui::GetTextLineHeight() * 4),
+                                    MULTILINE_TEXT_FLAGS);
+                        }
+                    }
+
+                    ImGui::EndChild();
+                }
                 ImGui::SameLine();
 
-                if (ImGui::Button("Save")) {}
-                ImGui::EndGroup();
+                //---[Right side]---------------------------------------------------
+                {
+                    ImGui::BeginGroup();
+                    ImGui::BeginChild("item view",
+                                      ImVec2(0,
+                                             -ImGui::GetFrameHeightWithSpacing()));
+
+                    ImGui::InputText("Filter", filterBuffer,
+                                     IM_ARRAYSIZE(filterBuffer),
+                                     ImGuiInputTextFlags_CallbackEdit,
+                                     catalogFilterCallback);
+
+                    ImGui::Separator();
+                    if (ImGui::BeginTabBar("##Tabs", ImGuiTabBarFlags_None))
+                    {
+                        if (ImGui::BeginTabItem("Options"))
+                        {
+                            ImGui::TextWrapped(
+                                    "Lorem ipsum dolor sit amet, consectetur adipiscing "
+                                    "elit, sed do eiusmod tempor incididunt ut labore et"
+                                    " dolore magna aliqua. ");
+                            ImGui::EndTabItem();
+                        }
+
+                        if (ImGui::BeginTabItem("About"))
+                        {
+                            ImGui::Text("%s version: %s", APP_NAME.c_str(),
+                                        VERSION.c_str());
+                            ImGui::Text("Compiled on: %s ", __TIMESTAMP__);
+                            ImGui::Text("Created by: dsorber");
+                            ImGui::EndTabItem();
+                        }
+                        ImGui::EndTabBar();
+                    }
+                    ImGui::EndChild();
+
+                    if (ImGui::Button("Clear Filter"))
+                    {
+                        filterBuffer[0] = 0;
+                    }
+                    ImGui::SameLine();
+
+                    if (ImGui::Button("Save"))
+                    {}
+                    ImGui::EndGroup();
+                }
             }
         }
 
