Reference-Request

這是正確的 PBKDF2 密鑰派生函式算法嗎?

  • September 15, 2015

我最近開始實現 PBKDF2 算法,由於我是加密新手,我想問一下我的實現是否好。

我查閱了一些文件,並嘗試遵循其中的許多文件,但現在我真的很困惑。

我遵循的文件:

https://en.wikipedia.org/wiki/PBKDF2

https://en.wikipedia.org/wiki/Hash-based_message_authentication_code

crackstation(.)net/hashing-security.htm#properhashing // (抱歉,我不能發布超過 2 個連結。)還有更多我沒有連結的連結。

這是我的 C/C++ 程式碼:

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);
       }
   }
};

該類是不穩定的,因為有時我遇到了優化問題。我還沒有弄清楚問題可能出在哪裡。

所以說清楚,我最後的問題是:

  • 這是一個合適的 PBKDF2 算法嗎?
  • PBKDF2 是否應該包含 HMAC 函式或只是用給定的迭代次數一遍又一遍地迭代一些雜湊?
  • 如何增強系統?

似乎沒有 PBKDF2-HMAC-SHA256 的官方測試向量。StackOverflow 使用者發布了一些其他人已經驗證的測試向量。這是您也應該執行的 HMAC-SHA2的測試向量。

也就是說,如果你“實現了一些其他的東西,會使最終輸出不同”,那麼你就沒有 PBKDF2。PBKDF2 是一個標準。如果偏離標準,則沒有 PBKDF2。除非您真的知道自己在做什麼並且有充分的理由這樣做,否則您永遠不應該偏離標準。

如何增強系統?

取決於你的目標是什麼。如果您想嘗試增強 PBKDF2 的安全性,您首先必須定義您認為 PBKDF2 不足以滿足您的需求的地方。

引用自:https://crypto.stackexchange.com/questions/29163