3
$\begingroup$

I've started implementing PBKDF2 algorithm recently and since I'm new in crypto, I would like to ask if my implementation is good.
I looked up some documentations and I tried to follow many of them and now I am really confused.

Documentations I've followed:
https://en.wikipedia.org/wiki/PBKDF2
https://en.wikipedia.org/wiki/Hash-based_message_authentication_code
crackstation(.)net/hashing-security.htm#properhashing // (Sorry, I can't post more than 2 links.) And many more to which I no more have got links.

Here is my C/C++ code:

typedef unsigned long UL;
volatile class KDF2
{
#define hLenSz 32 // Block size of sha256 in bytes
public:
    KDF2()
    {
    }

    //Password-Based Key Derivation Function 2
    std::string PBKDF2(string Password, string Salt, int c, int dkLen)
    {
        //Restrict max 128 bytes of Derivated Key
        if(dkLen > 128)
        {
            dkLen = 128;
        }

        const int BlockSize = hLenSz / sizeof(UL); // 8 bytes

        UL T[32] = {}; // Holds DK in 4 byte chunks
        UL L[BlockSize] = {};
        UL H[BlockSize] = {};
        string hash = "";
        string innerSalt = Salt + (char)dkLen;

        int l = ceil((float)dkLen / (float)hLenSz); // Compute the number of passes needed to get the desired DK length

        for(int i = 0; i < l; i++)
        {
            memset(L, 0, sizeof(L));

            //Iterate c times
            for(int j = 0; j < c; j++)
            {
                hash = HMAC(innerSalt, Password);
                innerSalt = hash;

                HexToLong(hash, H, BlockSize);
                //XOR function
                for(int p = 0; p < BlockSize; p++)
                {
                    L[p] = L[p] ^ H[p];
                }
            }

            for(int x = 0; x < BlockSize; x++)
            {
                T[BlockSize * i + x] = L[x];
            }
        }

        std::string output = "";
        for(int i = 0; i < dkLen / sizeof(UL); i++) // dkLen / 4 - unsigned long has 4 bytes
        {
            // hexify takes sizeof(T) and converts it to hex bytes
            output += hexify<UL>(T[i]);
        }
        return output;
    }

private:
    //Hash-based message authentication code
    std::string HMAC(string Salt, string Password)
    {
        char c;
        string s;
        UL Key[16] = {0};
        UL X[16] = {0};
        UL Y[16] = {0};
        UL ipad = 0x36363636; // 0x36 = 54 = '6'
        UL opad = 0x5c5c5c5c; // 0x5c = 92 = '\'
        int k;
        s = "";

        //Process string key into sub-key
        //Hash key in case it is less than 64 bytes
        if(Password.length() > 64)
        {
            string tmp = sha256(Password);

            HexToLong(tmp, Key, 5);
        }
        else
        {
            for(int i = 0; i < 16; i++)
            {
                for(int j = 0; j < 4; j++)
                {
                    if(4 * i + j <= Password.length())
                    {
                        k = Password[4 * i + j];
                    }
                    else
                    {
                        k = 0;
                    }
                    if(k < 0)
                    {
                        k = k + 256;
                    }
                    Key[i] += +k*pow(256, (double)3 - j);
                }
            }
        }

        for(int i = 0; i < 16; i++)
        {
            X[i] = Key[i] ^ ipad;
            Y[i] = Key[i] ^ opad;
        }

        //Turn X-Array into a String
        for(int i = 0; i < 16; i++)
        {
            for(int j = 0; j < 4; j++)
            {
                c = ((X[i] >> 8 * (3 - j)) % 256);
                s += c;
            }
        }

        //Append text to string
        s += Salt;

        string tmp = sha256(s);
        HexToLong(tmp, Key, 5);

        s = "";

        //Convert Y array to a string
        for(int i = 0; i < 16; i++)
        {
            for(int j = 0; j < 4; j++)
            {
                c = ((Y[i] >> 8 * (3 - j)) % 256);
                s += c;
            }
        }

        for(int i = 0; i < 5; i++)
        {
            for(int j = 0; j < 4; j++)
            {
                c = ((Key[i] >> 8 * (3 - j)) % 256);
                s += c;
            }
        }

        //Hash final aggregated string
        return sha256(s);
    }

    UL f(UL B, UL C, UL D, int t)
    {
        if(t < 20)
        {
            return ((B & C) ^ ((~B) & D));
        }
        if((t > 19) & (t < 40))
        {
            return (B ^ C ^ D);
        }
        if((t > 39) & (t < 60))
        {
            return ((B & C) ^ (B & D) ^ (C & D));
        }
        if(t > 59)
        {
            return (B ^ C ^ D);
        }
    }

    template< typename T >
    std::string hexify(T i)
    {
        std::stringbuf buf;
        std::ostream os(&buf);

        os << std::setfill('0') << std::setw(sizeof(T) * 2) << std::hex << i;

        return buf.str().c_str();
    }

    void HexToLong(string input, unsigned long* output, int outSize)
    {
        char hx[8] = {};

        for(int i = 0; i < outSize; i++)
        {
            for(int j = 0; j < 8; j++)
            {
                hx[j] = input[j + (8 * i)];
            }
            output[i] = strtoul(hx, nullptr, 16);
        }
    }
};

The class is volatile because sometimes I ran into a optimization problem. I haven't yet figured out where the problem might be.

So to make it clear, my final questions are:

  • Is this a proper PBKDF2 algorithm?

  • Should PBKDF2 contain HMAC function or just iterate some hash over and over again with given No. iterations?

  • How can I enhance the system?

$\endgroup$
  • $\begingroup$ Why don't you test your implementation $\endgroup$ – Maarten - reinstate Monica Sep 14 '15 at 23:11
  • 1
    $\begingroup$ This would be better placed at code review. It's not a question about crypto, except Q2, which I think we'll not answer. $\endgroup$ – Maarten - reinstate Monica Sep 14 '15 at 23:15
  • $\begingroup$ As I said, I'm new to crypto, I didn't know that I can test my implementation. Why can't you answer the second question? $\endgroup$ – ProXicT Sep 14 '15 at 23:21
  • $\begingroup$ @MaartenBodewes As I'm looking at the implementation test, that's not going to wrork for me. I'm using SHA256 instead of SHA1 and I've implemented some other things, that will make the final output different. $\endgroup$ – ProXicT Sep 14 '15 at 23:24
  • 1
    $\begingroup$ There seems to be some confusion about the term PBKDF2. A PBKDF is a generic term, meaning password based key derivation function. PBKDF1 and 2 are PBKDFs and so are bcrypt and scrypt. PBKDF2 on the other hand is a specific algorithm which has a single specification which you cannot deviate from. Now do you want to create your own PBKDF or do you want to implement PBKDF2? $\endgroup$ – Maarten - reinstate Monica Sep 15 '15 at 0:09
3
$\begingroup$

It appears there are no official test vectors for PBKDF2-HMAC-SHA256. A StackOverflow user has posted some test vectors that others have validated. Here are the test vectors for HMAC-SHA2 which you should also be running.

That said, if you have "implemented some other things that will make the final output different", then you do not have PBKDF2. PBKDF2 is a standard. If you deviate from the standard, you do not have PBKDF2. You should never deviate from the standard unless you really know what you are doing and have a very good reason to do so.

How can I enhance the system?

Depends on what your goals are. If you want to try to enhance the security of PBKDF2, you first have to define where you think PBKDF2 is not sufficient for your needs.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.