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:
preference_load_failed
- Failed to load the preferencesdatabase_created
- The database didn't exist and the library automatically created it for youfail
- The operation failed for an unknown reasonsuccess
- 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:
- Location as a string - Location where the file is to be created
- Passcode as a string - The password that is to be used to encrypt the database
- Passcode as a string - Same password as above once again for confirmation
It will return the following errors:
fail
- An error occurred when creating the preference filepasscode_mismatch
- The two passcodes entered don't matchsuccess
- 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:
- Passcode as a string - The authentication password to decrypt the database
- Platform as a string - Platform under which this profile exists
- Username as a string - Username of the profile
- 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:
non_existent_username
if the username provided doesn't exist under the platformsame_password_to_edit
if the provided password is the same as the password that is already storednon_existent_platform
if the provided platform doesn't existfail
if the operation failed for an unknown reasonsuccess
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:
- Authentication password as a string
- 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:
error
- If saving failedsuccess
- 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:
- Length as an integer - Length of the password to be generated (12 default)
- 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.