如何使用不受記憶體定時攻擊的查找表進行 AES 實現?
我最近使用查找表做了一個非常簡單的 AES 實現,但由於定時攻擊,它完全不安全。我對使用實際使用的 AES 處理器特定指令不感興趣,因為我想用 AES 做一個很酷的項目!有什麼方法可以防止我的 AES 實現不安全?
如果可能,一個解決方案是確保在數據相關地址上的表查找是恆定時間的,並且不對記憶體執行任何操作,方法是
- 在此類表查找期間禁用記憶體(使用和載入/無效)。不過,這在很大程度上取決於系統。一些 CPU 架構具有特殊的指令、定址模式或模式位,允許這樣做,或者允許控制可以記憶體哪些地址。
- 並確保沒有其他條件導致查表中的時序變化;由於在某些 CPU 上添加基地址和索引的內部進位,我特別想到了一個額外的周期。在許多 8 位 CPU 上,這意味著從 256 的地址倍數開始 256 字節表。
不太安全:
- 在查找表之前刷新記憶體,因為在某些攻擊場景中,攻擊者可能會檢查記憶體狀態。
- 在使用前用完整的表填充記憶體,因為在某些攻擊場景中,對手或巧合可能會部分刷新記憶體。
其他技術旨在使測量時序依賴性成為不可能。例如,我們可以在加密前啟動一個計時器,設置為比最壞情況加密持續時間更大的值,然後精確地等待直到加密後的計時器到期。使用周期精確的計時器,如果攻擊者不能引發中斷,使加密持續時間變化超出最大預測,以及許多其他條件,可以想像加密中的時間變化是完全隱藏的。請注意,這可能會加劇其他側通道(電源、電磁輻射)的洩漏。
這些都不是通用的和可移植的。其他解決方案沒有這樣的缺點:它們只是在數據相關索引處不使用表查找,而是在與數據無關的地址處使用常量時間表達式或表佔用來替換它們。使用bitslicing時,它可以變得可移植且相當快;請參閱此相關問題的答案。
如果速度不是問題,那麼一種緩慢但簡單且非常便攜的技術是:
uint8_t lookup(const uint8_t table[256], uint8_t index) { unsigned j, r; j = r = 255; do r ^= ((-(index ^ j)) >> 8) | table[j]; while(j--); return (uint8_t)r; }
這是有效的,因為
-(index ^ j)
有 8 到 15 位,每個 0 或 1 取決於是否index==j
,並且索引table[j]
不依賴於數據。
可能有必要禁止虛假編譯器警告或/通過插入0
before-
。也可以使用它來
(c >> j) & 1
查找索引j
中的 $ k $ -條目 1 位表定義為 $ k $ -位常數c
,在 CPU 上以恆定時間的方式,至少 - $ k $ -bit桶形移位器和使用它的編譯器。結合上述:我們可以根據索引的 5 位在每個 64 位的 32 個常量中進行選擇(也許展開循環),然後使用 64 位桶形移位器根據剩餘的值提取 8 位結果索引的 3 位。