為什麼不在常數時間比較中使用 <
、>
或 ==
?
我正在比較儲存在數組中的秘密數據
a
,b
看看哪個值更大。我目前的(偽)程式碼如下所示:unsigned char smaller = 0, bigger = 0; for (i = 0; i < size; ++i) { smaller |= (!bigger) & (a[i] < b[i]); bigger |= (!smaller) & (a[i] > b[i]); } return bigger;
但是當我查看(例如)sodium_compare時,我發現他們沒有使用
<
or>
運算符。相反,它們減去值並執行一些位操作,其中將一個字節移動 8 位(作為旁注:這不會導致 C 中的未定義行為嗎?)。有沒有理由避免使用
<
and>
運算符?類似地,相等的恆定時間比較通常通過按字節進行 xor ( ) 並取所有值
^
的 or ( ) 來實現。在那種情況下|
是否有理由偏愛?^``==
如果條件滿足, C 比較運算符(嚴格關係
< <= > >=
和相等== !=
)產生 1,否則產生 0。在取決於 CPU 和有時選項的某些實現(編譯器)上,這可以通過以下程式碼實現:; int a = ..., b = ...; ; int x = a > b; move a, r0 compare r0, b ; sometimes subtract with value ignored and only flags used bgtr lab1 move 0, r1 br lab2 lab1: move 1, r1 lab2: move r1, x
這取決於條件是真還是假,執行不同的指令序列。取決於 CPU,如果它有今天很多(不是全部)的分支預測/推測,這取決於條件分支是否被正確預測,而這又取決於許多其他通常無法完全分析的因素,這可能需要可以檢測到的不同時間量(儘管通常很小)。
一些現代 CPU 有條件移動指令,甚至是邏輯化標誌指令,可以避免這個問題,但是在編寫要移植到未知編譯器版本和環境的原始碼時,您不能確定這些是否存在並且(如果所以)使用。
實際上,您不能保證即使按位運算也是恆定時間,但實際上它們是您最好的選擇。
類似地,您的程式碼中的邏輯否定運算符
!
可能由類似這樣的東西實現,這不是恆定時間:move x, r0 test r0 ; sometimes included in the move bzer lab1 ; (EDIT) move 0, r1 br lab2 lab1: move 1, r1 lab2: ; use r1 as result
沒有未定義的行為:
移位大於或等於左操作數寬度的計數是 UB。即使它來自
unsigned char
您連結的程式碼中的 as ,移位的左操作數也受 6.3.1.1 的整數提升(儘管不是 6.3.1.8 的通常算術轉換)的影響,因此左操作數始終具有寬度至少 16 並且他們移動 8 沒關係。同樣左移帶有負值或溢出值的有符號類型(即輸入的高 N 幅度位中的至少一個非零)是 UB。他們不會左移。
用負值右移有符號類型是實現**定義的:符號位可以傳播或移出。它們確實會右移有時為負的值,但它們會立即屏蔽結果,以便不使用可能受此 IB 影響的位。