Bcrypt

bcrypt - 對所選鹽和工作因子的抗碰撞性?

  • August 8, 2018

會不會很難找到(cost, salt, input, password0, password1)這樣的

bcrypt(cost, salt, password0, input) = bcrypt(workfactor, salt, password1, input)

password0 != password1

哪裡cost不算小?

(如果不是,那麼這個答案中描述的 bcrypt 與 PAKE 的組合將是不安全的。)

原始問題的答案是:workfactor的,要展示, salt, password0,password1這樣是非常困難的bcrypt(workfactor, salt, password0) = bcrypt(workfactor, salt, password1);但即使這是可行的,也沒什麼大不了的,因為在正常使用中,只有在某些協議中,對手才知道至少一個密碼是未知的,在這種協議中,它可以允許一次嘗試排除兩個密碼。

至於現在措辭的問題:,展示workfactor, salt, password0,不會太困難;但這只是因為它具有延展性,而不是設置為! 在任何合理的使用中, ,被建構為三個不同的64 位常量(或至少值在問題的程度上不可延展)。password1``input``bcrypt(workfactor, salt, password0, input) = bcrypt(workfactor, salt, password1, input)``input``"OrpheanBeholderScryDoubt"``bcrypt``input


bcrypt的原始參考資料如下:

bcrypt(cost, salt, pwd)
   state :=­ EksBlowshSetup(cost, salt, key)
   ctext := "OrpheanBeholderScryDoubt"
   repeat (64)
       ctext := EncryptECB (state, ctext)
   return Concatenate(cost, salt, ctext)

周圍的文字表明pwd輸入映射到key(具體如何未指定),並且 24 個字元"OrpheanBeholderScryDoubt"映射到三個 64 位塊,構成ctext. 可以預見的是,實現在從pwd到的映射上有所不同key,包括以引入嚴重弱點的方式。也許 的初始值ctext也有所不同。此外,Concatenate輸出的格式如何變化(包括努力對其餘部分中使用的變化進行編碼)。

如果Blowfish是一個很好的密碼,那麼 Blowfish 的 64 次迭代也是一個很好的密碼(在某些指標上更好,在循環結構上可能稍微差一些),因此對於任何三個不同的常量 64 位明文,每個 Blowfish 64的密文應該是三個與隨機無法區分的值,除了不同。通過 Blowfish 和 bcrypt 的設計,發現與隨機的碰撞salt並且key應該預期需要超過 $ 2^{96} $ 的評估EksBlowshSetup,因此 $ 2^{97+\mathtt{cost}} $ ExpandKey操作,這是不可行的。


但是無論出於何種原因,當我閱讀時,bcrypt 的維基百科條目現在指出 bcrypt從給定的輸入計算雜湊值如下

bcrypt(cost, salt, key, input)
   state := EksBlowfishSetup(cost, salt, key)
   ctext := input
   repeat (64)
       ctext := EncryptECB(state, ctext) //encrypt using standard Blowfish in ECB mode
   return Concatenate(cost, salt, ctext)

請注意第四個參數input以及密碼是否輸入key(原始意圖)或的不確定性input。在下文中,我假設這個 4 參數定義,password在問題中映射到key.

關鍵是問題的目前陳述(不是最初預期的和可能通常的河豚設置)留下了 的選擇input,包括重複三次的 64 位值,這使得碰撞搜尋可行:

  • 設置cost為可能的最低值;

  • 設置salt為一些短的可接受常數;

  • j從_ $ 0 $ 到 $ 2^{20}-1 $ ,計算並儲存state[j] := EksBlowfishSetup(cost, salt, key)對應keypassword獲得的j轉換為可接受password的 5 個十六進制數字和適當後綴的位置;這將需要合理的 4 GiB RAM 和可承受的 CPU 時間。

  • 重複i增加,從 $ 0 $ :

    • j0 到 $ 2^{20}-1 $ :

      • 設置ctexti轉換為 64 位位串;

      • 重複 64 次:

        • 設置ctext := EncryptECB(state[j], ctext);加密在單個塊上;
      • 如果ctext是之前獲得的相同i和更早的jpassword0則從之前的 j 導出password1,從目前的導出j,並設置input為由 3 次i轉換為 64 位位串形成的 192 位位串;我們發現了碰撞!

      • 否則,請記住一些容易搜尋的表中ctext的對應j(這將只需要以前使用的 RAM 的一小部分);

    • 沒有運氣i,清空桌子,繼續下一步i

對於每個i測試,我們正在尋找之間的碰撞 $ 2^{20} $ 類似隨機的 64 位值,並且機率約為 $ 2^{20+20-1-64}=2^{-25} $ . 因此,我們預計在大約 $ 2^{25} $ 循環i,即 $ 2^{45} $ 循環ij,即 $ 2^{51} $ 加密,這是相當可觀但可行的。兩倍的記憶體使機器的效率幾乎提高了一倍,並且在某種程度上可以用硬碟或SSD代替用於儲存的 RAM 部分,但state[j]有一些小的複雜性。此外,這可以簡單地分佈,每台機器使用盡可能多的可用記憶體。


更新:我檢查了 bcrypt 的兩個實現。

jBCrypt符合原始 bcrypt 定義,但有兩個問題:因為它是用 Java 編寫的(至少在最常見的實踐中沒有完全編譯,並且缺少用於無符號 8 位變數的內置習語),因此速度可能比原生的東西要差得多,在特定的上下文中意味著不那麼安全;此外,至少在那個版本中,31 的成本是可以接受的,但會退化到比最低成本 4 更低的安全性,因為 1<<31 在 Java 中是負數。哦等等,我不是第一個踩到它的,這裡有一個更正的版本

在 PHP 中,公認的做法是將 bcrypt 密碼散列委託給crypt(參見這個Bcrypt.php或那個高度讚揚的答案)的本機實現,這是解決速度問題的好方法。有人告訴我 cryptcrypt.c那時crypt_blowfish.c。我現在已經找到"OrpheanBeholderScryDoubt"了,並且引用它的程式碼是按照原始 bcrypt 定義的。速度可能相當不錯。我喜歡在執行時使用快速已知答案測試檢查程式碼的想法,這也很有可能覆蓋敏感值;以及減少其他洩漏源的一些適度但可能有用的嘗試。

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