SharpHash - Full hashing library

Greetings and thank you for the reply. However, I think your statement was a little presumptuous. I was in fact trying to understand who PBKDF2 works. For me, this is just a self learning project.

However, I was also trying to see if there was a way using Array.Copy to get the salt from the hash similar to the way "h**ps://dev.to/demg_dev/pbkdf2-hash-a-secure-password-5f8l" does it. What is done is these salt is included in the hash and it is retrieved from that same hash. This way, you don't have to store the hash and the salt because both would be included in the one hash.

Anyway, I do have a functional validation checker that I created but, it's not what I was looking to do. I was having a little difficulty figuring out how to get the Array.Copy to work coping the salt from the hash correctly with my code.

C#:
        public bool IsValid(string testPassword, string origHash)
        {
            var TestPassword2 = Encoding.ASCII.GetBytes(testPassword);
            var origSalt = Encoding.ASCII.GetBytes("7E2E837180B587ECC26795CFE0B7C4600B03F816"); // Test Salt
            // Generate hash from test password and original test salt and iteration 2048.
            // Compute the hash on the password the user entered.
            IHash hash1 = HashHotel.Crypto.CreateSHA1();
            IPBKDF2_HMAC pbkdf2 = HashHotel.KDF.PBKDF2_HMAC.CreatePBKDF2_HMAC(hash1, TestPassword2, origSalt, 2048);
            byte[] Key = pbkdf2.GetBytes(24);
            pbkdf2.Clear();
            // Check Original hash against newly created hash using the test salt.
            // Convert to Hex and if hash values match then return true.
            var chString = Converters.ConvertBytesToHexString(Key, false);
            if (chString == origHash)
            {
                return true;
            }
            else
            {
                // Nothing matches; return false.
                return false;
            }
        }
 
Copying the original salt is a problem that is orthogonal to the problem of using PBKDF2. You would have the same problem with any other password encryption algorithm that uses a salt.
 
Notice that in the link you provided, the author creates a 40 byte array. The first 20 bytes contains the salt, and the last 20 bytes contains the derived key. He then stores this 40 bytes for later checks. When it's time to check a users password, he takes the first 20 bytes of the 40 bytes to use the same salt. Then he passes in the password attempt into the algorithm to get a new derived key. If the new derived key matches the last 20 bytes of the 40 byte array, then the user entered the correct password.
 
I think that part of the issue is that Encoding.ASCII.GetBytes() may not be returning what you were expecting to get back:
C#:
var origSalt = Encoding.ASCII.GetBytes("7E2E837180B587ECC26795CFE0B7C4600B03F816"); // Test Salt
will return these bytes:
C#:
37 45 32 45 38 33 37 31 38 30 42 35 38 37 45 43 43 32 36 37 39 35 43 46 45 30 42 37 43 34 36 30 30 42 30 33 46 38 31 36

Was your expectation for it to return:
C#:
7E 2E 83 71 80 B5 87 EC C2 67 95 CF E0 B7 C4 60 0B 03 F8 16
instead?

Or is the real problem here that origHash contains just the derived key and not both the derived key and the salt used to generate the derived key?
 
No, the Expected Hash Results: 651187022238DCB4D290E37D5D76681E6C3932D703E1C1E6.

To achieve this my test password was "password" and the test salt "7E2E837180B587ECC26795CFE0B7C4600B03F816"; IHash SHA1, Iterations 2048 and PBKDF2.GetBytes(24); Bytes converted to hex string.

Question… In your reply "He then stores this 40 bytes for later checks. When it's time to check a users password, he takes the first 20 bytes of the 40 bytes to use the same salt." He is saving those bytes in the same hash, correct? So, what I was thinking is there a way to do that with SharpHash?

I know you can save the salt and the hash as part of the same hash by using let's say ":" added to separate them, and then split the string by the ":". But I thought that the way he does this makes it easier to store and retrieve the salt.
 
Yes, I agree. Why be stringly typed when you can be strongly typed? So in the case of the link, storing bytes as bytes and in a single array is better in my opinion. It also adds an extra layer of defense. Let say a bad guy manages to get a copy of the database, but not the code. He'll have have to determine whether the full 40 bytes is the hashed password, or if the it's bytes 1-n is the hash and n-40 is the salt, or vice versa for values of n 2 to 39. Or if the link author were even more devious, he could have also interleaved the hash and salt bytes with each other to make things even harder.
 
Thanks for the reply. As this is my first attempt at understanding PBKDF2. Looks like I'll be answering my own question and piecing together some code based on some examples I've seen. However, I'm always open to some suggestions. For example, should the iteration be included in the generated hash with a "|" or ":" so that it can be separated upon password verification or even used upon verification.

The code on supplying is the smaller implementation. I created overloads and one of these overloads allows for customization of the hash algorithm, salt and iteration.

C#:
        private static Int32 Salt256bit { get; } = 256 / 8; // 256 bits = 32 Bytes

        public static string GetHashPBKDF2Password(string password)
        {
            // Notes:
            // Create a 32-byte secure salt and the same size of the key. This 32-byte produces 256 bits key output.
            // Add the same 32-byte size to the pbkdf2.GetBytes.
            // KDF.PBKDF2_HMAC.CreatePBKDF2_HMAC hashes these values using the crypto SHA presented.
            // Double the hashBytes bytes then add the salt and pbkdf2.GetBytes value.
            // Copy each of the 32-bytes to the hashBytes bytes from the hashed salt and hashed value.
            //
            byte[] Password = Encoding.ASCII.GetBytes(password);
            string hashPass = string.Empty;

            // Create the salt value with a cryptographic PRNG
            byte[] salt;
            new RNGCryptoServiceProvider().GetBytes(salt = new byte[ByteSize]); // 32 Bytes = 256 bits.

            // Create the KDF.PBKDF2_HMAC.CreatePBKDF2_HMAC and get the hash value using the SHA presented.
            // // I know SHA1 is not that secure at all anymore. Just using it to test with. :-)
            IHash sha = HashFactory.Crypto.CreateSHA1();
            IPBKDF2_HMAC pbkdf2 = HashFactory.KDF.PBKDF2_HMAC.CreatePBKDF2_HMAC(sha, Password, salt, Pbkdf2Iterations);
            byte[] hash = pbkdf2.GetBytes(ByteSize); // 32 Bytes = 256 bits.

            // Double the size of the byte array to include the "pbkdf2.GetBytes" and salt.
            var ByteNumber = salt.Length;
            if (ByteNumber > 3)
            {
                hashBytes = new byte[ByteNumber * 2];
            }

            // Combine the salt and password bytes.
            Array.Copy(salt, 0, hashBytes, 0, ByteSize);
            Array.Copy(hash, 0, hashBytes, ByteSize, ByteSize);

            // Turn the combined salt+hash into a string for storage
            hashPass = Convert.ToBase64String(hashBytes);

            return hashPass;
        }

        public static bool ValidatePBKDF2Password(string password, string hashPass)
        {
            try
            {
                byte[] Password = Encoding.ASCII.GetBytes(password);
                bool result = true;

                // Extract the bytes
                byte[] hashBytes = Convert.FromBase64String(hashPass);
                // Get the salt
                byte[] salt = new byte[32];
                Array.Copy(hashBytes, 0, salt, 0, 32);
                // Compute the hash on the password that user entered.
                // I know SHA1 is not that secure at all anymore. Just using it to test with. :-)
                IHash hash1 = HashFactory.Crypto.CreateSHA1();
                IPBKDF2_HMAC pbkdf2 = HashFactory.KDF.PBKDF2_HMAC.CreatePBKDF2_HMAC(hash1, Password, salt, Pbkdf2Iterations);
                byte[] hash = pbkdf2.GetBytes(32);
                // compare the results
                for (int i = 0; i < 32; i++)
                {
                    if (hashBytes[i + 32] != hash[i])
                    {
                        result = false;
                    }
                }
                return result;
            }
            catch (Exception)
            {
                return false;
            }
        }
       
        How to use:     string GeneratedHash = PBKDF2Helper.GetHashPBKDF2Password("password");
        Results: hv6t8N4rrVSKYFm80cCoVUEiUk2o11xLBc6lJb5kBXKTcwcKwl9dZwSdce01X0bi8BBhJY/QGGnNVAcR7ZhSvQ==
       
        Verify Password:  Boolean tester = PBKDF2Helper.ValidatePBKDF2Password("password", GeneratedHash);
                        txtVerificationResults.Text = tester.ToString();



Hope this helps someone along the way.
 
What happens to your validate meeting when ByteSize is not 32?

What your generator when ByteSize is 3 or less?
 
What happens when your users use non-ASCII characters for their password?
 
Thank you for your input. For this function there is no settings to adjust the byte size; it will always be 32. Now, I created an overload where one would be able to change the byte size and all areas where the byte size is required to change would change. As for the non-ASCII characters I tested Hebrew (פשדד'םרג) and Greek (πασσςορδ) with different hash algorithms such as SHA3-SHA384 and the validation return true and false when I removed one character.

SHA3-SHA384
UserSalt160bit
Iterations=10000

Hebrew Results: XCOY0qEgp7tFppjagmJCXJpiKYWbo/qM96fAqTlLz4XrBHE8tqtDcw==
Greek Results: e/7jXc5Uhv7Fu1gcI0HkCkgJ4tRWIUwejWhJATjn8ammpMZye3PZhw==

As for the byte size being less than three. I think that was just me confusing bits and bytes. It should have been zero because one byte is eight bits and that is proper format. Remember, this was my first attempt. :)

In my design thinking cap. The idea was to match the verification method with the Hash password generating method. It really wasn't to create just one verification method that matched everything; although this could be done. I think that's something the vendor should try to accomplish sense I was ultimately just trying to understand the dynamics of PBKDF2 in C#.
 
The problem is that with non-ASCII characters is that you are losing data when because of your use of Encoding.ASCII.GetBytes(). Notice the output of the following code:
C#:
using System;
using System.Linq;
using System.Text;

namespace ConsoleTest
{
    class Program
    {
        static void PrintCharsAndBytes(string text)
        {
            Console.WriteLine(text);
            foreach (var ch in text)
                Console.Write($"{(int)ch:x} ");
            Console.WriteLine();

            var bytes = Encoding.ASCII.GetBytes(text);
            foreach (var b in bytes)
                Console.Write($"{(int)b:x} ");
            Console.WriteLine();
        }

        static void Main()
        {
            PrintCharsAndBytes("פשדד'םרג");
            PrintCharsAndBytes("????'???");
        }
    }
}

Output:
C#:
????'???
5e4 5e9 5d3 5d3 27 5dd 5e8 5d2
3f 3f 3f 3f 27 3f 3f 3f
????'???
3f 3f 3f 3f 27 3f 3f 3f
3f 3f 3f 3f 27 3f 3f 3f
Notice each of the Hebrew Unicode characters are just mapped to the ASCII question mark (0x3f). So what that means that even if the user enters the password "פשדד'םרג", a hacker can type in "????'???" and get in because your hash computation and validation will match.

I suggest using Encoding.Unicode.GetBytes() because C# using Unicode strings internally.
 
For this function there is no settings to adjust the byte size; it will always be 32. Now, I created an overload where one would be able to change the byte size and all areas where the byte size is required to change would change.
And the reason why you need to change multiple places is because of your use of magic numbers in ValidatePBKDF2Password() but correctly use constant identifiers in GetHashPBKDF2Password(). If you used the ByteSize in ValidatePBKDF2Password(), then all you would need to do is a one line change for the initialization of ByteSize and you are done. Avoid using magic numbers.
 
I see... It looks like "Converters.ConvertStringToBytes" and Encoding.Unicode.GetBytes have the same output and are able to handle non-ASCII characters. I noticed that "Encoding.UTF8.GetBytes" is also able to handle non-ASCII characters but having different output. Good catch, thank you. I'll change it to "Encoding.Unicode.GetBytes".

Now to answer your other posts. Magic numbers wasn't in the thinking cap. But I like the idea of initializing one veritable and I'm done. Actually looks like there would be two veritable's to initialize; salt and iterations.

The only problem I see from that point is how to make the hash selectable. Example: "IHash sha = HashFactory.Crypto.CreateSHA1()" or "HashFactory.Crypto.CreateSHA3_256()" etc.
 
Okay, I've taken your advice and changed a couple of things. Now, I'm only using the hash password method and the verification for any hash algorithm. I initialized (ByteSize, SHAhashAlgorithm and Pbkdf2Iterations). Each of these have a default setting in case the user does not initialize them.

However, although I'm able to set the SHA algorithm. It's not an efficient way that I'm doing this. At this stage of the game I'm having a little difficulty trying to figure out a more efficient way to handle the algorithm selection.

For example I create many static (IHash) instances that the user can assign to the (IHash SHAhashAlgorithm) variable. However, it's not the most efficient way to do this.
C#:
 class
     //EXAMPLE:
     // For user use. Bwlow variables are to be used in the manual functions.
     public static Int32 UserSalt256bit { get; } = 256 / 8; // 256 bits = 32 Bytes

        //=====================================================================
        //                   IHash SHA algorithm
        //======================================================================
        public static IHash SHA2_SHA1ForPBKDF2() // Need a more efficient way to assign the SHA algorithm.
        {
            // SHA2-SHA1
            var x = HashFactory.Crypto.CreateSHA1();
            return x;
        }

        //=====================================================================
        //                   Assignments with Default
        //======================================================================

        public static Int32 ByteSize { get; set; } = Salt256bit; // Default
        public static IHash SHAhashAlgorithm { get; set; } = SHA3_SHA256ForPBKDF2(); // Default
        public static UInt32 Pbkdf2Iterations { get; set; } = 50000; // Default

        public static string GetHashPBKDF2Password(string password)
        {
            // Notes:
            // Create a 32-byte secure salt and the same size of the key. This 32-byte produces 256 bits key output.
            // Add the same 32-byte size to the pbkdf2.GetBytes.
            // KDF.PBKDF2_HMAC.CreatePBKDF2_HMAC hashes these values using the crypto SHA presented.
            // Double the hashBytes bytes then add the salt and pbkdf2.GetBytes value.
            // Copy each of the 32-bytes to the hashBytes bytes from the hashed salt and hashed value.
            //
            byte[] Password = Encoding.Unicode.GetBytes(password);
            string hashPass = string.Empty;

            // Create the salt value with a cryptographic PRNG
            byte[] salt;
            new RNGCryptoServiceProvider().GetBytes(salt = new byte[ByteSize]);

            // Create the KDF.PBKDF2_HMAC.CreatePBKDF2_HMAC and get the hash value using the SHA presented.
            IPBKDF2_HMAC pbkdf2 = HashHotel.KDF.PBKDF2_HMAC.CreatePBKDF2_HMAC(SHAhashAlgorithm, Password, salt, Pbkdf2Iterations);
            byte[] hash = pbkdf2.GetBytes(ByteSize);

            // Double the size of the byte array to include the "pbkdf2.GetBytes" and salt.
            Int32 g = hash.Length + salt.Length;
            byte[] hashBytes = new byte[g];

            // Combine the salt and password bytes for later use.
            Array.Copy(salt, 0, hashBytes, 0, ByteSize);
            Array.Copy(hash, 0, hashBytes, ByteSize, ByteSize);

            // Turn the combined salt+hash into a string for storage
            hashPass = Convert.ToBase64String(hashBytes);

            return hashPass;
        }

        public static bool ValidatePBKDF2Password(string password, string hashPass)
        {
            try
            {
                byte[] Password = Encoding.Unicode.GetBytes(password);
                bool result = true;

                // Extract the bytes
                byte[] hashBytes = Convert.FromBase64String(hashPass);
                // Get the salt
                // Half the salt bytes of the salt in the GetHashPBKDF2PasswordTest method.
                byte[] salt = new byte[ByteSize];
                Array.Copy(hashBytes, 0, salt, 0, ByteSize);
                // Compute the hash on the password the user entered
                IPBKDF2_HMAC pbkdf2 = HashHotel.KDF.PBKDF2_HMAC.CreatePBKDF2_HMAC(SHAhashAlgorithm, Password, salt, Pbkdf2Iterations);
                byte[] hash = pbkdf2.GetBytes(ByteSize);
                // compare the results
                for (int i = 0; i < ByteSize; i++)
                {
                    if (hashBytes[i + ByteSize] != hash[i])
                    {
                        result = false;
                    }
                }
                return result;
            }
            catch (Exception)
            {
                return false;
            }
        }

//===========================================================
            // Set custom variables:
            // PBKDF2Helper.SHAhashAlgorithm = PBKDF2Helper.SHA2_SHA1ForPBKDF2(); // Setting the SHA algorithm.
            // PBKDF2Helper.Pbkdf2Iterations = 10000; // Setting the Iteration.
            // PBKDF2Helper.ByteSize = PBKDF2Helper.UserSalt128bit; // Setting the salt value.

            // Test Password Hash.
            //txtMessageResults.Text = "";
            //txtMessageResults.Text = PBKDF2Helper.GetHashPBKDF2Password("password");
            //
            //Boolean x = PBKDF2Helper.ValidatePBKDF2Password("password", "BLAH, BLAH");
            //txtVerificationResults.Text = x.ToString();
 
I suggest you look into "dependency injection".
 
Back
Top Bottom