Project Table infect Source code


Source code for the login system I wrote for the system. As the project were closed source and there were other members I can only post my own code and not the entire project.

LoginSignupSystem.cs

The code for handling login and signup attempts

using System;
using System.Text;
using System.Text.RegularExpressions;
using System.Security.Cryptography;
using System.Security;
using System.Net;
using System.Data.SQLite;
using System.Runtime.InteropServices;

namespace Project_Table_Infect
{
    /// <summary>
    /// Enum containing different signup results.
    /// </summary>
    public enum SignupState { Succesfull, UserExists, PasswordsDoNotMatch, InvalidUsername}
    /// <summary>
    /// Class for handling login
    /// </summary>
    public static class LoginSignupSystem
    {
        private const string CONNECTIONSTRING = @"Data Source=Project_Table_Infect_DB;version=3;New=true;Compress=true";
        private readonly static SQLiteConnection connection = new SQLiteConnection(CONNECTIONSTRING);
        private const int HASHITERATIONS = 60000;

        /// <summary>
        /// Tries to login.
        /// </summary>
        /// <param name="usernameInput">Username to login with.</param>
        /// <param name="pwInput">The password to login with.</param>
        /// <returns>Returns if the login were sucessful</returns>
        public static bool Login(string usernameInput, SecureString pwInput)
        {
            string username = usernameInput;
            SecureString pw = pwInput;
            bool result = false;
            try
            {
                connection.Open();
                if (!CheckIfUserExists(username)) return false;
                SQLiteCommand liteCommand = connection.CreateCommand();
                liteCommand.CommandText = @"SELECT * FROM Users WHERE Username = @username COLLATE NOCASE;";
                liteCommand.Parameters.AddWithValue("@username", username);
                SQLiteDataReader liteDataReader = liteCommand.ExecuteReader();
                string pwHash ="";
                string salt = "";
                while (liteDataReader.Read())
                {
                    pwHash = liteDataReader.GetString(2);
                    salt = liteDataReader.GetString(3);
                }
                result = pwHash == HashPassword(pw, salt);
            }
            finally
            {
                if (pw != null) pw.Dispose();
                connection.Close();
            }
            return result;
        }

        public static SignupState Signup(string usernameInput, SecureString pwInput)
        {
            string username = usernameInput;
            string passwordHash = "";
            SecureString pw = pwInput;
            try
            {
                //Open db connection
                connection.Open();
                //Generate salt
                byte[] saltByteArray = new byte[64];
                new RNGCryptoServiceProvider().GetBytes(saltByteArray);
                string salt = Encoding.Unicode.GetString(saltByteArray);
                //checks username
                if (!ValidateUsername(username)) return SignupState.InvalidUsername;
                if (CheckIfUserExists(username)) return SignupState.UserExists;
                //Hashes the password
                passwordHash = HashPassword(pw, salt);
                //Sets up sql command
                SQLiteCommand liteCommand = connection.CreateCommand();
                string commandString = @"INSERT INTO Users (Username, Password, Salt) VALUES(@username,@passwordHash,@salt);";
                liteCommand.CommandText = commandString;
                //Sets parameters
                liteCommand.Parameters.AddWithValue("@username", username);
                liteCommand.Parameters.AddWithValue("@passwordHash", passwordHash);
                liteCommand.Parameters.AddWithValue("@salt", salt);
                //Execute SQL command
                liteCommand.ExecuteNonQuery();
                liteCommand.Dispose();
            }
            finally
            {
                if (pw != null) pw.Dispose();
                connection.Close();
            }

            return SignupState.Succesfull;
        }

        /// <summary>
        /// Runs the signup procedure with password validation.
        /// </summary>
        /// <param name="usernameInput">The username to sign up with</param>
        /// <param name="pw1Input">First password input</param>
        /// <param name="pw2Input">Second password input for validation</param>
        /// <returns>Retruns the state of the signup, wheter is were succesful or not</returns>
        public static SignupState SignupWithPasswordValidation(string usernameInput, SecureString pw1Input, SecureString pw2Input)
        {
            string username = usernameInput;
            string passwordHash = "";
            SecureString pw1 = pw1Input;
            SecureString pw2 = pw2Input;
            try
            {
                //Open db connection
                connection.Open();
                //Generate salt
                byte[] saltByteArray = new byte[64];
                new RNGCryptoServiceProvider().GetBytes(saltByteArray);
                string salt = Encoding.Unicode.GetString(saltByteArray);
                //checks username
                if (!ValidateUsername(username)) return SignupState.InvalidUsername;
                if (CheckIfUserExists(username)) return SignupState.UserExists;
                //Checks password
                if (!CompareSecureStrings(pw1, pw2)) return SignupState.PasswordsDoNotMatch;
                //Hashes the password
                passwordHash = HashPassword(pw1, salt);
                //Sets up sql command
                SQLiteCommand liteCommand = connection.CreateCommand();
                string commandString = @"INSERT INTO Users (Username, Password, Salt) VALUES(@username,@passwordHash,@salt);";
                liteCommand.CommandText = commandString;
                //Sets parameters
                liteCommand.Parameters.AddWithValue("@username", username);
                liteCommand.Parameters.AddWithValue("@passwordHash", passwordHash);
                liteCommand.Parameters.AddWithValue("@salt", salt);
                //Execute SQL command
                liteCommand.ExecuteNonQuery();
                liteCommand.Dispose();
            }
            finally
            {
                if (pw1 != null) pw1.Dispose();
                if (pw2 != null) pw2.Dispose();
                connection.Close();
            }

            return SignupState.Succesfull;
        }

        /// <summary>
        /// Hashes the password with HMACSHA12
        /// </summary>
        /// <param name="password">The password</param>
        /// <param name="salt">The salt to use</param>
        /// <returns>Returns the password Hash</returns>
        private static string HashPassword(SecureString password, string salt)
        {
            string passwordHash = "";
            passwordHash = Encoding.Unicode.GetString(new Rfc2898DeriveBytes(new NetworkCredential("", password).Password, Encoding.Unicode.GetBytes(salt), HASHITERATIONS, HashAlgorithmName.SHA512).GetBytes(64));
            return passwordHash;
        }

        /// <summary>
        /// Checks if a user exists in the database
        /// </summary>
        /// <param name="username">The username to check</param>
        /// <returns>Returns true if a user exists</returns>
        private static bool CheckIfUserExists(string username)
        {
            bool opendConnection = false;
            bool result = false;
            if (connection != null && connection.State != System.Data.ConnectionState.Open)
            {
                connection.Open();
                opendConnection = true;
            }
            SQLiteCommand liteCommand = connection.CreateCommand();
            liteCommand.CommandText = $"SELECT EXISTS(SELECT 1 FROM Users WHERE Username = @username COLLATE NOCASE);";
            liteCommand.Parameters.AddWithValue("@username", username);
            SQLiteDataReader liteDataReader = liteCommand.ExecuteReader();
            while (liteDataReader.Read())
            {
                if (liteDataReader.GetInt16(0) == 1) result = true;
            }
            if (opendConnection)
            {
                connection.Close();
            }
            return result;
        }


        /// <summary>
        /// Validates a username to only use the following characters[a-zA-Z0-9], -, _
        /// </summary>
        /// <param name="stringToCheck">The username to check</param>
        /// <returns></returns>
        private static bool ValidateUsername(string stringToCheck)
        {
            return Regex.IsMatch(Regex.Escape(stringToCheck), @"(^(?![-_]))(^[a-zA-Z0-9_-]*$)");
        }

        /// <summary>
        /// Checks if two secure strings are the same
        /// </summary>
        /// <param name="ssPw1">The first securestring</param>
        /// <param name="ssPw2">The second securestring</param>
        /// <returns>Returns true if the secure strings are the same</returns>
        private static bool CompareSecureStrings(SecureString ssPw1, SecureString ssPw2)
        {
            IntPtr ptrPw1 = IntPtr.Zero;
            IntPtr ptrPw2 = IntPtr.Zero;
            try
            {
                ptrPw1 = Marshal.SecureStringToBSTR(ssPw1);
                ptrPw2 = Marshal.SecureStringToBSTR(ssPw2);
                bool result = true;
                if (ssPw1.Length != ssPw2.Length)
                {
                    return false;
                }
                else
                {
                    int ssPw1Length = Marshal.ReadInt32(ptrPw1, -4);
                    for (int i = 0; i < ssPw1Length; i++)
                    {
                        byte bPw1 = Marshal.ReadByte(ptrPw1, i);
                        byte bPw2 = Marshal.ReadByte(ptrPw2, i);
                        if (bPw1 != bPw2) result = false;

                    }
                    return result;
                }
            }
            finally
            {
                if (ptrPw1 != IntPtr.Zero) Marshal.ZeroFreeBSTR(ptrPw1);
                if (ptrPw2 != IntPtr.Zero) Marshal.ZeroFreeBSTR(ptrPw2);
            }
        }
    }
}