Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding Second factor #201

Open
wants to merge 25 commits into
base: master
Choose a base branch
from
Open
Changes from 1 commit
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
479f7a3
began Implementing SecondFactor
Dec 30, 2024
ba19a3d
began Implementing a new way for loading/saving the Database Files
Jan 2, 2025
e05d271
implemented the logic for the MainDatabaseLoader
Jan 2, 2025
915a5ad
implemented the Loading for the epdb v1 implementation
Jan 2, 2025
3c6166a
added additional Property for the File
Jan 2, 2025
6c4c579
added DatabaseFormatHelper which will be used to update the Formats
Jan 2, 2025
2b1411d
.
Jan 2, 2025
923635c
Merge branch 'FrozenAssassine:master' into SecondFactor
Flamifly Jan 3, 2025
1a405af
implemented the new Databasefileformat
Jan 3, 2025
1fcafa4
Merge branch 'FrozenAssassine:master' into SecondFactor
Flamifly Jan 4, 2025
474d62c
began implementing the Hashing for the MasterPassword to encrypt it w…
Jan 4, 2025
17d48dd
Merge branch 'SecondFactor' of https://github.com/Flamifly/EasePass i…
Jan 4, 2025
875f05d
added Documentation
Jan 4, 2025
64a5d54
added Token Generator
Jan 5, 2025
4f6a371
Merge branch 'FrozenAssassine:master' into SecondFactor
Flamifly Jan 7, 2025
23c7b75
added Argon2 Hash algorithm to the MainDatabaseLoader
Jan 7, 2025
37e6dc0
- changed Version of the MainDatabaseLoader of the EPDB Format
Jan 11, 2025
d8644e3
- added CopyTokenBox (A TextBox which allows copy and show functional…
Jan 11, 2025
27a339a
deleted unused Methods
Jan 11, 2025
c61ed54
Fixed Bug: Exporting the Database did not export the Settings of a Da…
Jan 12, 2025
4b464b2
BugFix: The Argon2 Algorithm did not release the used memory. Because…
Jan 12, 2025
fc31d66
made the hash length longer
Jan 31, 2025
eeb5333
changed the way the hash will be generated
Feb 5, 2025
42016a1
Added additional logic for the Database File encryption
Feb 6, 2025
9488da8
removed Rfc2898DeriveBytes for the encryption / decryption since we d…
Feb 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
implemented the logic for the MainDatabaseLoader
flamy committed Jan 2, 2025
commit e05d271a6d61e0d0ce3fad76400708dadd8fa6d8
40 changes: 38 additions & 2 deletions EasePass/Core/Database/Serialization/DatabaseFile.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
namespace EasePass.Core.Database.Serialization
using EasePass.Models;
using System.Collections.ObjectModel;
using System.Text.Json.Serialization;

namespace EasePass.Core.Database.Serialization
{
/// <summary>
/// The Fileformat of the Database which will be used for Serialization/Deserialization
/// </summary>
public class DatabaseFile
{
#region Properties
/// <summary>
/// The Settings of the Database
/// </summary>
@@ -15,6 +20,14 @@ public class DatabaseFile
/// </summary>
public byte[] Data { get; set; }

/// <summary>
/// The Passwords in the Database
/// </summary>
[JsonIgnore]
public ObservableCollection<PasswordManagerItem> Items { get; set; } = null;
#endregion

#region Deserialize
/// <summary>
/// Deserialize the given <paramref name="json"/> to the <see cref="DatabaseFile"/>
/// </summary>
@@ -24,10 +37,33 @@ public static DatabaseFile Deserialize(string json)
{
try
{
System.Text.Json.JsonSerializer.Deserialize<DatabaseFile>(json);
return System.Text.Json.JsonSerializer.Deserialize<DatabaseFile>(json);
}
catch { }
return default;
}
#endregion

#region Serialize
/// <summary>
/// Serialize the current Instance of <see cref="DatabaseFile"/> to a <see cref="string"/>
/// </summary>
/// <returns>Returns an Instance of <see cref="string"/> if the Serialization was successfull, otherwise <see cref="string.Empty"/> will be returned</returns>
public string Serialize() => Serialize(this);
/// <summary>
/// Serialize the given <paramref name="settings"/> to a <see cref="string"/>
/// </summary>
/// <param name="settings">The JSON String, which should be serialized to a <see cref="string"/></param>
/// <returns>Returns an Instance of <see cref="string"/> if the Serialization was successfull, otherwise <see cref="string.Empty"/> will be returned</returns>
public static string Serialize(DatabaseFile settings)
{
try
{
return System.Text.Json.JsonSerializer.Serialize(settings);
}
catch { }
return string.Empty;
}
#endregion
}
}
32 changes: 29 additions & 3 deletions EasePass/Core/Database/Serialization/DatabaseSettings.cs
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@ namespace EasePass.Core.Database.Serialization
/// </summary>
public class DatabaseSettings
{
#region Properties
/// <summary>
/// Specifies if the Database uses a SecondFactor
/// </summary>
@@ -22,20 +23,45 @@ public class DatabaseSettings
/// The Version of the Database, this will be used to check if the File needs to be converted in the Future
/// </summary>
public double Version { get; set; }
#endregion

#region Deserialize
/// <summary>
/// Deserialize the given <paramref name="json"/> to the <see cref="DatabaseSettings"/>
/// </summary>
/// <param name="json">The JSON String, which should be deserialized to a <see cref="DatabaseSettings"/> object</param>
/// <returns>Returns an Instance of <see cref="DatabaseSettings"/> if the Deserialization was successfull, otherwise <see cref="default"/> will be returned</returns>
public static DatabaseFile Deserialize(string json)
public static DatabaseSettings Deserialize(string json)
{
try
{
System.Text.Json.JsonSerializer.Deserialize<DatabaseFile>(json);
return System.Text.Json.JsonSerializer.Deserialize<DatabaseSettings>(json);
}
catch { }
return default;
}
#endregion

#region Serialize
/// <summary>
/// Serialize the current Instance of <see cref="DatabaseSettings"/> to a <see cref="string"/>
/// </summary>
/// <returns>Returns an Instance of <see cref="string"/> if the Serialization was successfull, otherwise <see cref="string.Empty"/> will be returned</returns>
public string Serialize() => Serialize(this);
/// <summary>
/// Serialize the given <paramref name="settings"/> to a <see cref="string"/>
/// </summary>
/// <param name="settings">The JSON String, which should be serialized to a <see cref="string"/></param>
/// <returns>Returns an Instance of <see cref="string"/> if the Serialization was successfull, otherwise <see cref="string.Empty"/> will be returned</returns>
public static string Serialize(DatabaseSettings settings)
{
try
{
return System.Text.Json.JsonSerializer.Serialize(settings);
}
catch { }
return string.Empty;
}
#endregion
}
}
}
227 changes: 227 additions & 0 deletions EasePass/Core/Database/epdb/ADatabaseLoader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
using EasePass.Core.Database.Serialization;
using EasePass.Dialogs;
using EasePass.Helper;
using EasePass.Models;
using Newtonsoft.Json;
using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Security;

namespace EasePass.Core.Database.epdb
{
public abstract class ADatabaseLoader
{
#region ConvertToDatabaseFile
private protected static (PasswordValidationResult result, DatabaseFile database) ConvertToDatabaseFile((PasswordValidationResult result, string decryptedData) content)
{
DatabaseSettings settings = new DatabaseSettings();
settings.SecondFactorType = Enums.SecondFactorType.None;
settings.UseSecondFactor = false;
settings.Version = MainDatabaseLoader.Version;

DatabaseFile databaseFile = new DatabaseFile();
databaseFile.Settings = settings;
databaseFile.Items = GetItems(content.decryptedData);
databaseFile.Data = null;

return (content.result, databaseFile);
}
private static ObservableCollection<PasswordManagerItem> GetItems(string json)
{
try
{
return JsonConvert.DeserializeObject<ObservableCollection<PasswordManagerItem>>(json);
}
catch
{
InfoMessages.DatabaseInvalidData();
return null;
}
}
#endregion

#region DecryptData
/// <summary>
/// Checks if the <paramref name="content"/> can be decrypted with the given <paramref name="password"/>
/// </summary>
/// <param name="content">The Content, which should be decrypted</param>
/// <param name="password">The Password, which should be used for the decryption</param>
/// <param name="showWrongPasswordError">Specifies if a Message will be shown if the Password is wrong</param>
/// <param name="decryptedData">Contains the decrypted Data if the <paramref name="content"/> could be decrypted, otherwise contain <see cref="string.Empty"/></param>
/// <returns>Returns <see langword="true"/> if the <paramref name="content"/> could be encrypted, otherwise <see langword="false"/></returns>
private protected static bool DecryptData(byte[] content, SecureString password, bool showWrongPasswordError, out string decryptedData)
{
decryptedData = string.Empty;
var result = EncryptDecryptHelper.DecryptStringAES(content, password);

if (!result.correctPassword)
{
if (showWrongPasswordError)
{
InfoMessages.ImportDBWrongPassword();
}
return false;
}

decryptedData = result.decryptedString;
return true;
}
#endregion

#region Deserialize/SerializePasswordManagerItems
/// <summary>
/// Deserialize the given <paramref name="json"/> to the <see cref="ObservableCollection{PasswordManagerItem}"/>
/// </summary>
/// <param name="json">The JSON String, which should be deserialized to a <see cref="ObservableCollection{PasswordManagerItem}"/> object</param>
/// <returns>Returns an Instance of <see cref="ObservableCollection{PasswordManagerItem}"/> if the Deserialization was successfull, otherwise <see cref="default"/> will be returned</returns>
[Obsolete]
private protected static ObservableCollection<PasswordManagerItem> DeserializePasswordManagerItems(string json)
{
try
{
return System.Text.Json.JsonSerializer.Deserialize<ObservableCollection<PasswordManagerItem>>(json);
}
catch { }
return default;
}
/// <summary>
/// Serialize the given <paramref name="items"/> to a <see cref="string"/>
/// </summary>
/// <param name="json">The JSON String, which should be serialized to a <see cref="string"/></param>
/// <returns>Returns an Instance of <see cref="string"/> if the Serialization was successfull, otherwise <see cref="string.Empty"/> will be returned</returns>
[Obsolete]
private protected static string SerializePasswordManagerItems(ObservableCollection<PasswordManagerItem> items)
{
try
{
return System.Text.Json.JsonSerializer.Serialize(items);
}
catch { }
return string.Empty;
}
#endregion

#region GetDatabaseLoader
/// <summary>
/// Gets the DatabaseLoader as <see cref="Type"/>
/// </summary>
/// <param name="version">The Version of the Database</param>
/// <returns>Returns the <see cref="Type"/> of the DatabaseLoader</returns>
public static Type GetDatabaseLoader(double version)
{
return IDatabaseLoader.GetDatabaseLoader(version);
}
#endregion

#region Load
/// <summary>
/// Loads the given Database in the <paramref name="path"/>
/// </summary>
/// <param name="path">The Path to the Database</param>
/// <param name="password">The Password of the Database</param>
/// <param name="showWrongPasswordError">Specifies if an Error should occure if the Password is wrong</param>
/// <returns>Returns </returns>
public static (PasswordValidationResult result, DatabaseFile database) Load(string path, SecureString password, bool showWrongPasswordError)
{
if (password == null)
return (PasswordValidationResult.WrongPassword, null);

if (!File.Exists(path))
return (PasswordValidationResult.DatabaseNotFound, null);

var oldImporterRes = OldDatabaseImporter.CheckValidPassword(path, password, showWrongPasswordError);
if (oldImporterRes.result == PasswordValidationResult.Success)
return ConvertToDatabaseFile(oldImporterRes);

var (data, success) = Database.ReadFile(path, password, showWrongPasswordError);
if (success)
return ConvertToDatabaseFile((PasswordValidationResult.Success, data));



return (PasswordValidationResult.WrongPassword, null);
}
#endregion

#region ReadFile
/// <summary>
/// Reads the <see cref="byte"/>[] of the File in the given <paramref name="path"/>
/// </summary>
/// <param name="path">The Path to the Database</param>
/// <returns>Returns the <see cref="byte"/>[] of the Databasefile. If a Error occures <see cref="Array.Empty{T}"/> will be returned</returns>
private protected static byte[] ReadFile(string path)
{
try
{
return File.ReadAllBytes(path);
}
catch (Exception ex) when (ex is FileNotFoundException || ex is DirectoryNotFoundException)
{
InfoMessages.DatabaseFileNotFoundAt(path);
}
catch (UnauthorizedAccessException)
{
InfoMessages.NoAccessToPathDatabaseNotLoaded(path);
}
return Array.Empty<byte>();
}
#endregion

#region SaveFile
/// <summary>
/// Saves the <paramref name="content"/> to the File given in the <paramref name="path"/>
/// </summary>
/// <param name="path">The Path to the File, which should be saved</param>
/// <param name="content">The Content, which should be written in the File</param>
/// <returns>Returns <see langword="true"/> if the File was saved successfully, otherwise <see langword="false"/> will be returned</returns>
private protected static bool SaveFile(string path, byte[] content)
{
try
{
File.WriteAllBytes(path, content);
return true;
}
catch (UnauthorizedAccessException)
{
InfoMessages.NoAccessToPathDatabaseNotSaved(path);
}
catch (IOException)
{
InfoMessages.DatabaseSaveToFileError(path);
}
return false;
}
#endregion

#region Save
/// <summary>
/// Saves the Database to the given <paramref name="path"/> and encrypts the content with the <paramref name="password"/>
/// </summary>
/// <param name="path">The Path to the Database</param>
/// <returns>Returns the <see langword="true"/> if the Database was saved successfully</returns>
public static bool Save(string path, SecureString password, SecureString secondFactor, DatabaseSettings settings)
{
return MainDatabaseLoader.Save(path, password, secondFactor, settings);
}
/// <summary>
/// Saves the Database to the given <paramref name="path"/> and encrypts the content with the <paramref name="password"/>
/// </summary>
/// <param name="path">The Path to the Database</param>
/// <returns>Returns the <see langword="true"/> if the Database was saved successfully</returns>
public static bool Save(string path, SecureString password, ObservableCollection<PasswordManagerItem> items)
{
return MainDatabaseLoader.Save(path, password, items);
}
/// <summary>
/// Saves the Database to the given <paramref name="path"/> and encrypts the content with the <paramref name="password"/>
/// </summary>
/// <param name="path">The Path to the Database</param>
/// <returns>Returns the <see langword="true"/> if the Database was saved successfully</returns>
public static bool Save(string path, SecureString password, SecureString secondFactor, ObservableCollection<PasswordManagerItem> items)
{
return MainDatabaseLoader.Save(path, password, secondFactor, items);
}
#endregion
}
}
87 changes: 33 additions & 54 deletions EasePass/Core/Database/epdb/IDatabaseLoader.cs
Original file line number Diff line number Diff line change
@@ -1,72 +1,51 @@
using EasePass.Core.Database.Serialization;
using EasePass.Models;
using System.Collections.ObjectModel;
using System.IO;
using System.Security;
using System;
using System.Linq;

namespace EasePass.Core.Database.epdb
{
internal interface IDatabaseLoader
public interface IDatabaseLoader
{
public static virtual double Version => 1.1;

#region Properties
/// <summary>
/// Loads the given Database in the <paramref name="path"/>
/// The Version of the File.
/// </summary>
/// <param name="path">The Path to the Database</param>
/// <returns>Returns </returns>
public static virtual (PasswordValidationResult result, DatabaseFile database) Load(string path, SecureString password, bool showWrongPasswordError)
{
if (password == null)
return (PasswordValidationResult.WrongPassword, null);

if (!File.Exists(path))
return (PasswordValidationResult.DatabaseNotFound, null);

var oldImporterRes = OldDatabaseImporter.CheckValidPassword(path, password, showWrongPasswordError);
if (oldImporterRes.result == PasswordValidationResult.Success)
{

}

var (data, success) = Database.ReadFile(path, password, showWrongPasswordError);
if (success)
//return (PasswordValidationResult.Success, data);
return (PasswordValidationResult.Success, null);

return (PasswordValidationResult.WrongPassword, null);

return default;
}
public static abstract double Version { get; }
#endregion

#region Decrypt/Encrypt
/// <summary>
///
/// Decrypts the Database content
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public static virtual bool Save(string path, SecureString password, ObservableCollection<PasswordManagerItem> items)
{


return false;
}
/// <returns>Returns the decrypted Databasecontent</returns>
public static abstract string Decrypt();

/// <summary>
///
/// Encrypts the Database content
/// </summary>
/// <returns></returns>
public static virtual string Decrypt()
{
return null;
}
/// <returns>Returns the encrypted Databasecontent</returns>
public static abstract byte[] Encrypt();
#endregion

#region GetDatabaseLoader
/// <summary>
///
/// Gets the DatabaseLoader as <see cref="Type"/>
/// </summary>
/// <returns></returns>
public static virtual byte[] Encrypt()
/// <param name="version">The Version of the Database</param>
/// <returns>Returns the <see cref="Type"/> of the DatabaseLoader</returns>
public static Type GetDatabaseLoader(double version)
{
return null;
var types = AppDomain.CurrentDomain.GetAssemblies().SelectMany(s => s.GetTypes())
.Where(p => typeof(IDatabaseLoader).IsAssignableFrom(p)).ToArray();

foreach (var type in types)
{
double versionValue = ((double?)type.GetProperty(nameof(Version)).GetValue(type, null)) ?? 0;

if (versionValue == version)
return type;
}
return typeof(MainDatabaseLoader);
}
#endregion
}
}
}
118 changes: 118 additions & 0 deletions EasePass/Core/Database/epdb/MainDatabaseLoader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
using EasePass.Core.Database.Serialization;
using EasePass.Dialogs;
using EasePass.Helper;
using EasePass.Models;
using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Security;

namespace EasePass.Core.Database.epdb
{
internal class MainDatabaseLoader : ADatabaseLoader, IDatabaseLoader
{
#region Properties
public static double Version => 1.1;
#endregion

#region Decrypt/Encrypt
public static string Decrypt()
{
return null;
}

public static byte[] Encrypt()
{
return null;
}
#endregion

#region Load
/// <summary>
/// Loads the given Database in the <paramref name="path"/>
/// </summary>
/// <param name="path">The Path to the Database</param>
/// <param name="password">The Password of the Database</param>
/// <param name="showWrongPasswordError">Specifies if an Error should occure if the Password is wrong</param>
/// <returns>Returns the <see cref="PasswordValidationResult"/> and the <see cref="DatabaseFile"/>.
/// If the <see cref="PasswordValidationResult"/> is not equal to <see cref="PasswordValidationResult.Success"/> the
/// <see cref="DatabaseFile"/> is equal to <see cref="default"/></returns>
public new static (PasswordValidationResult result, DatabaseFile database) Load(string path, SecureString password, bool showWrongPasswordError)
{
if (!File.Exists(path))
return (PasswordValidationResult.DatabaseNotFound, default);

if (!DecryptData(ReadFile(path), password, showWrongPasswordError, out string data))
return (PasswordValidationResult.WrongPassword, default);

DatabaseFile database = DatabaseFile.Deserialize(data);

// TODO:
// - Need to check if SecondFactor is in use
// - If SecondFactor is in use get the Token
// - After receiving the Token check the SecondFactor Type
// - Need to do that how the Decryption have to be done

if (!DecryptData(database.Data, password, showWrongPasswordError, out data))
return (PasswordValidationResult.WrongPassword, default);

database.Items = DeserializePasswordManagerItems(data);
database.Data = Array.Empty<byte>();

return (PasswordValidationResult.Success, database);
}
#endregion

#region Save
/// <summary>
/// Saves the Database to the given <paramref name="path"/> and encrypts the content with the <paramref name="password"/>
/// </summary>
/// <param name="path">The Path to the Database</param>
/// <returns>Returns the <see langword="true"/> if the Database was saved successfully</returns>
public static bool Save(string path, SecureString password, SecureString secondFactor, DatabaseSettings settings, ObservableCollection<PasswordManagerItem> items)
{
byte[] data;
string json = SerializePasswordManagerItems(items);
if (secondFactor == null)
{
data = EncryptDecryptHelper.EncryptStringAES(json, password);
}
else
{
data = EncryptDecryptHelper.EncryptStringAES(json, secondFactor);
}

DatabaseFile database = new DatabaseFile();
database.Settings = settings;
database.Data = data;
json = database.Serialize();
data = EncryptDecryptHelper.EncryptStringAES(json, password);

return SaveFile(path, data);
}

/// <summary>
/// Saves the Database to the given <paramref name="path"/> and encrypts the content with the <paramref name="password"/>
/// </summary>
/// <param name="path">The Path to the Database</param>
/// <returns>Returns the <see langword="true"/> if the Database was saved successfully</returns>
public static bool Save(string path, SecureString password, ObservableCollection<PasswordManagerItem> items)
{


return false;
}
/// <summary>
/// Saves the Database to the given <paramref name="path"/> and encrypts the content with the <paramref name="password"/>
/// </summary>
/// <param name="path">The Path to the Database</param>
/// <returns>Returns the <see langword="true"/> if the Database was saved successfully</returns>
public static bool Save(string path, SecureString password, SecureString secondFactor, ObservableCollection<PasswordManagerItem> items)
{


return false;
}
#endregion
}
}
14 changes: 7 additions & 7 deletions EasePass/Core/Database/epdb/v1/DatabaseLoader.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
namespace EasePass.Core.Database.epdb.v1_1
namespace EasePass.Core.Database.epdb.v1
{
internal class DatabaseLoader : IDatabaseLoader
internal class DatabaseLoader : ADatabaseLoader, IDatabaseLoader
{
public static double Version => 1.0;

public static bool Load(string path)
public static string Decrypt()
{
return false;
throw new System.NotImplementedException();
}

public static bool Save(string path)
public static byte[] Encrypt()
{
return false;
throw new System.NotImplementedException();
}
}
}
}
3 changes: 3 additions & 0 deletions EasePass/EasePass.csproj
Original file line number Diff line number Diff line change
@@ -267,4 +267,7 @@
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Folder Include="Core\Database\epeb\" />
</ItemGroup>
</Project>