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
Added additional logic for the Database File encryption
- Added Method for converting a Byte[] to a Char[] to be able to do not allocate a String

The First implementation Hashed the Password with associated Data because of that we wasn't able to get the Version of the Database
To encrypt the Passwords we will now convert the password bytes to a base64 char array and reverse it
by that we can change the Hash result to add an extra step for an attacker
Philipp Eckert authored and Philipp Eckert committed Feb 6, 2025
commit 42016a1b48bf05e7f6620ae9db7eb2b434e7512c
3 changes: 0 additions & 3 deletions EasePass/Core/Database/Database.cs
Original file line number Diff line number Diff line change
@@ -15,11 +15,8 @@ copies or substantial portions of the Software.
*/

using EasePass.Core.Database.Format;
using EasePass.Dialogs;
using EasePass.Helper;
using EasePass.Models;
using EasePass.Settings;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
20 changes: 14 additions & 6 deletions EasePass/Core/Database/Format/epdb/MainDatabaseLoader.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
using EasePass.Core.Database.Format.Serialization;
using EasePass.Dialogs;
using EasePass.Extensions;
using EasePass.Helper;
using EasePass.Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;
using System.Threading.Tasks;
@@ -53,9 +56,10 @@ internal class MainDatabaseLoader : IDatabaseLoader
}
else
{


//pass = HashHelper.HashPasswordWithArgon2id()
char[] base64 = password.ToBytes().ToBase64();
Array.Reverse(base64);
pass = HashHelper.HashPasswordWithArgon2id(base64, salt, associatedData);
base64.ZeroOut();
}

if (!IDatabaseLoader.DecryptData(database.Data, pass, showWrongPasswordError, out data))
@@ -90,7 +94,9 @@ internal class MainDatabaseLoader : IDatabaseLoader
}
else
{
pass = HashHelper.HashPasswordWithArgon2id(password, salt, associatedData);
char[] base64 = password.ToBytes().ToBase64();
Array.Reverse(base64);
pass = HashHelper.HashPasswordWithArgon2id(base64, salt, associatedData);
}

if (!IDatabaseLoader.DecryptData(database.Data, pass, showWrongPasswordError, out string data))
@@ -116,7 +122,9 @@ public static bool Save(string path, SecureString password, SecureString secondF

if (secondFactor == null)
{
pass = HashHelper.HashPasswordWithArgon2id(password, salt, associatedData);
char[] base64 = password.ToBytes().ToBase64();
Array.Reverse(base64);
pass = HashHelper.HashPasswordWithArgon2id(base64, salt, associatedData);
data = EncryptDecryptHelper.EncryptStringAES(json, pass);
}
else
@@ -135,7 +143,7 @@ public static bool Save(string path, SecureString password, SecureString secondF

if (secondFactor != null)
{
pass = HashHelper.HashPasswordWithArgon2id(password, salt, associatedData);
pass = HashHelper.HashPasswordWithArgon2id(password, salt);
}
data = EncryptDecryptHelper.EncryptStringAES(json, pass);

62 changes: 62 additions & 0 deletions EasePass/Extensions/ByteArrayExtension.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
using System.Collections.Generic;
using System;
using System.Linq;

namespace EasePass.Extensions
{
internal static class ByteArrayExtension
{
private readonly static char[] base64ByteTo = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".ToCharArray();

/// <summary>
/// Converts the <paramref name="input"/> to a Base64 <see cref="char"/>[]
/// </summary>
/// <param name="input">The <see cref="byte"/>[], which should be converted to Base64</param>
/// <returns>Returns the <paramref name="input"/> as Base64 <see cref="char"/>[]</returns>
public static char[] ToBase64(this byte[] input)
{
// Base64 encodes in blocks of 3 bytes. Each block becomes 4 base64 characters.
int nChars = (int)Math.Ceiling((double)input.Length / 3) * 4;
char[] outBase64Chars = new char[nChars];

int iByte = 0, iBit = 7, iBase64Bits = 0, iBase64Chars = 0;
byte base64Byte = 0;

// Walk byte by byte on the input, traversing bits from 7 to 0. Along the way, convert every group of 6 into base64
int length = input.Length;
while (iByte < length)
{
base64Byte = (byte)(
(byte)(base64Byte << 1) |
(Convert.ToBoolean(input[iByte] & (byte)(1 << iBit)) ? (byte)1 : (byte)0));

iBase64Bits++;
iBit--;
// If we have gotten 6 base64 bits, convert them into a char.
if (iBase64Bits % 6 == 0)
{
outBase64Chars[iBase64Chars] = base64ByteTo[base64Byte];
base64Byte = 0;
iBase64Bits = 0;
iBase64Chars++;
}
// If we are done with bits in current byte, advance to next byte
if (iBit < 0)
{
iByte++;
iBit = 7;
}
}

// Padding if last block has less than 3 bytes.
// if block has only 1 byte, give 1 char padding.
// if block has 2 bytes, give one more char padding
if (iByte % 3 > 0 || iByte % 3 > 1)
{
outBase64Chars[iBase64Chars] = '=';
iBase64Chars++;
}
return outBase64Chars;
}
}
}
25 changes: 25 additions & 0 deletions EasePass/Extensions/CharArrayExtension.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
namespace EasePass.Extensions
{
/// <summary>
/// Includes every Extension for the <see cref="char"/>[]
/// </summary>
internal static class CharArrayExtension
{
#region ZeroOut
/// <summary>
/// Zeros out the given <paramref name="chars"/>
/// </summary>
/// <param name="chars">The <see cref="char"/>[], which should be zero out</param>
/// <returns>Returns the Zero out Array</returns>
public static char[] ZeroOut(this char[] chars)
{
int length = chars.Length;
for (int i = 0; i < length; i++)
{
chars[i] = '\0';
}
return chars;
}
#endregion
}
}
6 changes: 4 additions & 2 deletions EasePass/Extensions/CharExtension.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
namespace EasePass.Extensions
{
/// <summary>
/// Includes every Extension for the <see cref="char"/>[]
/// Includes every Extension for the <see cref="char"/>
/// </summary>
internal static class CharExtension
{
#region IsSpecialChar
/// <summary>
/// Checks if the given <paramref name="c"/> is a Special Character
/// </summary>
@@ -54,5 +55,6 @@ public static bool IsSpecialChar(this char c)
return false;
}
}
#endregion
}
}
}
41 changes: 39 additions & 2 deletions EasePass/Helper/HashHelper.cs
Original file line number Diff line number Diff line change
@@ -15,7 +15,6 @@ copies or substantial portions of the Software.
*/

using EasePass.Extensions;
using EasePass.Helper;
using Konscious.Security.Cryptography;
using System;
using System.IO;
@@ -40,12 +39,50 @@ internal class HashHelper
/// <returns>Returns the Hash of the <paramref name="password"/> with the given <paramref name="hashLength"/>.
/// If the <paramref name="password"/> is equal to <see langword="null"/> <see cref="Array.Empty{T}"/> will be returned.</returns>
public static byte[] HashPasswordWithArgon2id(SecureString password, byte[] salt, byte[] associatedData = null, int degreeOfParallelism = 10, int iterations = 10, int memorySize = 256_000, int hashLength = 256)
{
if (password == null)
return Array.Empty<byte>();

return HashPasswordWithArgon2id(password.ToBytes(), salt, associatedData);
}
/// <summary>
/// Hashes the given <paramref name="password"/> with the Argon2id Algorithm
/// </summary>
/// <param name="password">The Password, which should be hashed</param>
/// <param name="salt">The Salt, which should be used for the hash</param>
/// <param name="associatedData">The Associated Data for the Hash</param>
/// <param name="degreeOfParallelism">The degree of Parallelism</param>
/// <param name="iterations">The amount of iterations for the hash</param>
/// <param name="memorySize">The amount of Memory, which should be uused</param>
/// <param name="hashLength">The length of the Hash</param>
/// <returns>Returns the Hash of the <paramref name="password"/> with the given <paramref name="hashLength"/>.
/// If the <paramref name="password"/> is equal to <see langword="null"/> <see cref="Array.Empty{T}"/> will be returned.</returns>
public static byte[] HashPasswordWithArgon2id(char[] password, byte[] salt, byte[] associatedData = null, int degreeOfParallelism = 10, int iterations = 10, int memorySize = 256_000, int hashLength = 256)
{
if (password == null)
return Array.Empty<byte>();

return HashPasswordWithArgon2id(Encoding.UTF8.GetBytes(password), salt, associatedData);
}
/// <summary>
/// Hashes the given <paramref name="password"/> with the Argon2id Algorithm
/// </summary>
/// <param name="password">The Password, which should be hashed</param>
/// <param name="salt">The Salt, which should be used for the hash</param>
/// <param name="associatedData">The Associated Data for the Hash</param>
/// <param name="degreeOfParallelism">The degree of Parallelism</param>
/// <param name="iterations">The amount of iterations for the hash</param>
/// <param name="memorySize">The amount of Memory, which should be uused</param>
/// <param name="hashLength">The length of the Hash</param>
/// <returns>Returns the Hash of the <paramref name="password"/> with the given <paramref name="hashLength"/>.
/// If the <paramref name="password"/> is equal to <see langword="null"/> <see cref="Array.Empty{T}"/> will be returned.</returns>
public static byte[] HashPasswordWithArgon2id(byte[] password, byte[] salt, byte[] associatedData = null, int degreeOfParallelism = 10, int iterations = 10, int memorySize = 256_000, int hashLength = 256)
{
if (password == null)
return Array.Empty<byte>();

byte[] hash;
using (Argon2id Argon2id = new Argon2id(password.ToBytes()))
using (Argon2id Argon2id = new Argon2id(password))
{
Argon2id.Salt = salt;
Argon2id.DegreeOfParallelism = degreeOfParallelism;