๐Ÿ” Muragala Password Manager

๐Ÿ” Muragala Password Manager

#04 - C# Library

ยท

7 min read

Hello All ๐Ÿ‘‹,

Today I wanted to discuss a bit about the C# library itself since it is an important item moving forward. The reason why I separately created it as a library is that this allows us to use the same codebase to create multiple types of applications. This library is first followed by the Command Line (CLI) application, which we discussed before. This application simply interfaces between the library and the user. All the heavy lifting is done by the library itself.

Introduction

Muragala library is a Windows DLL library that has all the functions related to the application. All of the functionalities in the Python edition of the application are available in this library. The target framework for it is .NET Core 3.1. However, I also created a separate .NET framework compatible edition based on .NET Framework 2.7.2 as well which can be used for .NET Core incompatible applications. Both will be updated simultaneously.

Class Library

The library contains a class of MuragalaLibrary which can be created with the optional parameters of a database. If a database is provided like this, the library automatically uses this as its database without using any external database. However, the best way to use the application is by letting the library load the database by itself which gives the option to automatically save the database by the library itself reducing the work that needs to be done from the applications side.

First Operations to Perform

Loading the Preferences and Database

Once the class is created, the two functions loadPreference() and loadDatabase() have to be called in that order with either the data itself or the location of the files as parameters. Location can be a string file with the file location. If you're providing the data itself, (Which is not the recommended way), the preference is of the form Dictionary<string, string> where the key is the name of the data (salt and hash) and the value of the data itself. The database is of the form Dictionary<string, List<Dictionary<string, List<string>>>> about which I discussed in a previous blog. If you provide them as strings, then the functions return the error as an integer. These error values can be compared with the return_error types as below:

  1. preference_load_failed - Failed to load the preferences
  2. database_created - The database didn't exist and the library automatically created it for you
  3. fail - The operation failed for an unknown reason
  4. success - The operation was successful

Creating the Preferences

If the loadPreference() returned a preference_load_failed error, it's best to use the createPreference() function to create a new preference file. This function must be called with the parameters:

  1. Location as a string - Location where the file is to be created
  2. Passcode as a string - The password that is to be used to encrypt the database
  3. Passcode as a string - Same password as above once again for confirmation

It will return the following errors:

  1. fail - An error occurred when creating the preference file
  2. passcode_mismatch - The two passcodes entered don't match
  3. success - The task was completed successfully.

If you called the createPreference(), then you need not call loadPreference() again as this function also loads the preferences into its memory after creating it.

Using the Library

Checking the User Entered Password

Since the database is encrypted, you need to provide the password used to createPreference() to get any of the tasks done. To check if the password entered is correct, we can use the checkPassword() function. This accepts the password entered as a string and returns true if the password is correct and false if incorrect, as a boolean.

Adding a New Profile

To add a new profile to the database, the addPassword() function has to be used. This function accepts parameters as:

  1. Passcode as a string - The authentication password to decrypt the database
  2. Platform as a string - Platform under which this profile exists
  3. Username as a string - Username of the profile
  4. Password as a string - The password to be saved for the profile

This function returns true if the profile was added successfully and false if it failed, as a boolean.

Editing an Existing Profile

To edit an existing profile in the database, we can use the editPassword() function which accepts the parameters as the addPassword() function. This will return:

  1. non_existent_username if the username provided doesn't exist under the platform
  2. same_password_to_edit if the provided password is the same as the password that is already stored
  3. non_existent_platform if the provided platform doesn't exist
  4. fail if the operation failed for an unknown reason
  5. success if the operation was successful

Deleting an Existing Profile

To delete an existing profile in the database, we can use the deletePassword() function which accepts the same parameters as the editPassword() function without the password parameter. It also returns the same errors except same_password_to_edit.

Retrieving the Data of a Profile

To retrieve the password and creation date, the getUserInformation() function can be used. It accepts the same parameters as the deletePassword() function and returns a list of strings where the first item is the password and the second item is the date and time of the creation in the form dd-MM-yyyy hh:mm:ss. If it returns an empty list, this means that the profile doesn't exist in the database.

In addition, the getUserData() function can be used to get the profile data without decrypting them. It is similar to getUserInformation(), which returns the same items in the same order in the same format but without decrypting them. This is useful if you only need to check if the profile exists for example, as the decrypting takes time.

Retrieving the Platforms

To retrieve the available platforms in the database, we can use the getPlatformNames() function. This accepts the parameters:

  1. Authentication password as a string
  2. Optional keyword to search for

It returns the list of platforms as a string that matches the provided keyword if the keyword is supplied, and all platforms if no keyword is provided.

A similar function, getEncPlatformNames(), will return the encrypted form of the provided platform name as a list of strings. It accepts the password and platform as strings.

Another function getPlatform() returns the data in its original structure back. (List<Dictionary<string, List<string>>>) It accepts the password and platform as strings.

Retrieving the Usernames

To retrieve the available usernames in the database under a provided platform, the searchUsernames() function can be used. This accepts the authentication password as a string and optionally accepts keywords for username and platform as a string. It will return List<Tuple<string, List<string>>> where each list item is a profile with a platform matching the keyword and the first string of the tuple represents the platform and the list of strings under it are the marching usernames under it.

Finishing Off

Once, done, you can call the dumpDatabase() function to store the database back into the database file. It accepts no arguments and returns the error as:

  1. error - If saving failed
  2. success - If successfully saved

All the functions that modify the database call this after changing the database and thus it are not necessary to call this function.

Additional Functions

Generating a Random Password

You can use the randomPassword() function to get a random password generated. It accepts optional arguments:

  1. Length as an integer - Length of the password to be generated (12 default)
  2. Uppercase, lowercase and symbol as boolean each to represent whether to include each in the password. (Default is false for each and lowercase is mandatory and thus is not an option)

Random Salt

The function randomSalt() generates a random salt.

Key

The function getKey() returns a key generated based on a salt and password provided as a string.

Hash

The function getHash() returns the hash of a password provided as a string according to a salt provided as a string. This is useful to validate the password entered.

Encrypt and Decrypt

The encrypt() and decrypt() functions can be used to encrypt and decrypt a message provided as a string with the help of a password provided as a string. It returns the encrypted or decrypted message as a string.

ย