內聯加密

背景

內聯加密硬體邏輯上位於記憶體和磁碟之間,可以在資料進出磁碟時對資料進行加密/解密。對於每個 I/O 請求,軟體可以精確控制內聯加密硬體如何加密/解密資料,包括金鑰、演算法、資料單元大小(加密/解密的粒度)和資料單元號(確定初始化向量的值)。

一些內聯加密硬體接受所有加密引數,包括低階 I/O 請求中的原始金鑰。然而,大多數內聯加密硬體都有固定數量的“金鑰槽”,並且要求首先將金鑰、演算法和資料單元大小程式設計到金鑰槽中。然後,每個低階 I/O 請求只包含一個金鑰槽索引和資料單元號。

請注意,內聯加密硬體與傳統的加密加速器非常不同,後者透過核心加密 API 支援。傳統的加密加速器對記憶體區域進行操作,而內聯加密硬體對 I/O 請求進行操作。因此,內聯加密硬體需要由塊層管理,而不是核心加密 API。

內聯加密硬體也與“自加密驅動器”非常不同,例如基於 TCG Opal 或 ATA 安全標準的驅動器。自加密驅動器不提供對加密的細粒度控制,並且無法驗證生成的密文的正確性。內聯加密硬體提供對加密的細粒度控制,包括為每個扇區選擇金鑰和初始化向量,並且可以測試其正確性。

目標

我們希望在核心中支援內聯加密。為了使測試更容易,我們還希望在沒有實際內聯加密硬體時,支援回退到核心加密 API。我們還希望內聯加密能夠與分層裝置(如 device-mapper 和 loopback)一起工作(即,我們希望能夠使用底層裝置(如果存在)的內聯加密硬體,否則回退到加密 API 加密/解密)。

約束和說明

  • 我們需要一種方法讓上層(例如檔案系統)指定用於加密/解密 bio 的加密上下文,並且裝置驅動程式(例如 UFSHCD)在處理請求時需要能夠使用該加密上下文。加密上下文還對 bio 合併引入了約束;塊層需要知道這些約束。

  • 不同的內聯加密硬體具有不同的支援演算法、支援的資料單元大小、最大資料單元號等。我們將這些屬性稱為“加密能力”。我們需要一種方法讓裝置驅動程式以通用方式向上層通告加密能力。

  • 內聯加密硬體通常(但並非總是)要求在使用前將金鑰程式設計到金鑰槽中。由於程式設計金鑰槽可能很慢,並且可能沒有很多金鑰槽,因此我們不應該只為每個 I/O 請求程式設計金鑰,而是應該跟蹤哪些金鑰在金鑰槽中,並在可能的情況下重用已程式設計的金鑰槽。

  • 上層通常定義加密金鑰的特定生命週期結束,例如,當加密目錄被鎖定或加密對映被刪除時。在這些時候,金鑰會從記憶體中擦除。我們必須提供一種方法讓上層也從它們所在的任何金鑰槽中逐出金鑰。

  • 在可能的情況下,device-mapper 裝置必須能夠傳遞其底層裝置的內聯加密支援。但是,device-mapper 裝置本身擁有金鑰槽是沒有意義的。

基本設計

我們引入 struct blk_crypto_key 來表示內聯加密金鑰以及如何使用它。這包括金鑰的型別(原始或硬體封裝);金鑰的實際位元組;金鑰的大小;金鑰將使用的演算法和資料單元大小;以及表示金鑰將使用的最大資料單元號所需的位元組數。

我們引入 struct bio_crypt_ctx 來表示加密上下文。它包含一個數據單元號和一個指向 blk_crypto_key 的指標。我們將指向 bio_crypt_ctx 的指標新增到 struct biostruct request;這允許塊層的使用者(例如檔案系統)在建立 bio 時提供加密上下文,並將其傳遞到堆疊中,以供塊層和裝置驅動程式處理。請注意,加密上下文沒有明確說明是加密還是解密,因為這隱含在 bio 的方向中;WRITE 表示加密,READ 表示解密。

我們還引入 struct blk_crypto_profile 來包含特定內聯加密裝置的所有通用內聯加密相關狀態。blk_crypto_profile 用作內聯加密硬體驅動程式通告其加密能力並向上層提供某些函式(例如,程式設計和逐出金鑰的函式)的方式。想要支援內聯加密的每個裝置驅動程式都將構造一個 blk_crypto_profile,然後將其與磁碟的 request_queue 相關聯。

blk_crypto_profile 還管理硬體的金鑰槽(如果適用)。這發生在塊層中,以便塊層的使用者可以只指定加密上下文,而無需瞭解金鑰槽,裝置驅動程式也無需關心金鑰槽管理的大部分細節。

具體來說,對於每個金鑰槽,塊層(透過 blk_crypto_profile)跟蹤該金鑰槽包含哪個 blk_crypto_key(如果有),以及有多少正在進行的 I/O 請求正在使用它。當塊層為具有加密上下文的 bio 建立一個 struct request 時,它會獲取一個已經包含金鑰的金鑰槽(如果可能)。否則,它會等待一個空閒金鑰槽(一個未被任何 I/O 使用的金鑰槽),然後使用裝置驅動程式提供的函式將金鑰程式設計到最近最少使用的空閒金鑰槽中。在這兩種情況下,生成的金鑰槽都儲存在 request 的 crypt_keyslot 欄位中,裝置驅動程式可以在其中訪問它,並在請求完成後釋放它。

struct request 還包含一個指向原始 bio_crypt_ctx 的指標。請求可以由多個 bio 構建,塊層在嘗試合併 bio 和請求時必須考慮加密上下文。要合併兩個 bio/請求,它們必須具有相容的加密上下文:都未加密,或者都使用相同的金鑰和連續的資料單元號進行加密。只保留請求中第一個 bio 的加密上下文,因為已驗證剩餘的 bio 與第一個 bio 具有合併相容性。

為了使內聯加密能夠與基於 request_queue 的分層裝置一起工作,當克隆請求時,其加密上下文也會被克隆。當提交克隆的請求時,它會像往常一樣被處理;這包括從克隆的目標裝置獲取金鑰槽(如果需要)。

blk-crypto-fallback

上層(例如檔案系統)的內聯加密支援最好能在沒有真正的內聯加密硬體的情況下進行測試,塊層的金鑰槽管理邏輯也是如此。最好還允許上層始終只使用內聯加密,而不必以多種方式實現加密。

因此,我們還引入了 blk-crypto-fallback,它是使用核心加密 API 實現的內聯加密。blk-crypto-fallback 內置於塊層中,因此它可以在任何塊裝置上工作,而無需任何特殊設定。本質上,當具有加密上下文的 bio 被提交到不支援該加密上下文的 block_device 時,塊層將使用 blk-crypto-fallback 處理 bio 的加密/解密。

對於加密,資料不能就地加密,因為呼叫者通常依賴於它未被修改。相反,blk-crypto-fallback 會分配反彈頁面,用這些反彈頁面填充一個新的 bio,將資料加密到這些反彈頁面中,並提交該“反彈”bio。當反彈 bio 完成時,blk-crypto-fallback 會完成原始 bio。如果原始 bio 太大,可能需要多個反彈 bio;請參閱程式碼以瞭解詳細資訊。

對於解密,blk-crypto-fallback 用自己的回撥函式包裝 bio 的完成回撥函式(bi_complete)和私有資料(bi_private),取消設定 bio 的加密上下文,然後提交 bio。如果讀取成功完成,blk-crypto-fallback 會恢復 bio 的原始完成回撥函式和私有資料,然後使用核心加密 API 就地解密 bio 的資料。解密從工作佇列發生,因為它可能會休眠。之後,blk-crypto-fallback 會完成 bio。

在這兩種情況下,blk-crypto-fallback 提交的 bio 都不再具有加密上下文。因此,較低層只看到標準的未加密 I/O。

blk-crypto-fallback 還定義了自己的 blk_crypto_profile,並擁有自己的“金鑰槽”;它的金鑰槽包含 struct crypto_skcipher 物件。這樣做的原因有兩個。首先,它允許在沒有實際內聯加密硬體的情況下測試金鑰槽管理邏輯。其次,與實際的內聯加密硬體類似,加密 API 不直接在請求中接受金鑰,而是要求提前設定金鑰,並且設定金鑰可能會很昂貴;此外,由於它佔用的鎖,因此根本無法在 I/O 路徑上分配 crypto_skcipher。因此,金鑰槽的概念對於 blk-crypto-fallback 仍然有意義。

請注意,無論使用真正的內聯加密硬體還是 blk-crypto-fallback,寫入磁碟的密文(以及因此資料的磁碟格式)都將是相同的(假設內聯加密硬體的實現和核心加密 API 使用的演算法的實現都符合規範並正常執行)。

blk-crypto-fallback 是可選的,由 CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK 核心配置選項控制。

呈現給塊層使用者的API

blk_crypto_config_supported() 允許使用者提前檢查使用特定加密設定的內聯加密是否可以在特定的 block_device 上工作 -- 透過硬體或透過 blk-crypto-fallback。此函式接受一個 struct blk_crypto_config,它類似於 blk_crypto_key,但省略了金鑰的實際位元組,而只包含演算法、資料單元大小等。如果停用了 blk-crypto-fallback,則此函式可能很有用。

blk_crypto_init_key() 允許使用者初始化 blk_crypto_key。

使用者必須在實際開始在 block_device 上使用 blk_crypto_key 之前呼叫 blk_crypto_start_using_key()(即使之前呼叫了 blk_crypto_config_supported())。這是初始化 blk-crypto-fallback(如果需要)所必需的。這不能從資料路徑呼叫,因為這可能必須分配資源,在這種情況下可能會死鎖。

接下來,要將加密上下文附加到 bio,使用者應呼叫 bio_crypt_set_ctx()。此函式分配一個 bio_crypt_ctx 並將其附加到 bio,給定 blk_crypto_key 和將用於加密/解密的資料單元號。使用者無需擔心稍後釋放 bio_crypt_ctx,因為這會在 bio 被釋放或重置時自動發生。

最後,當完成在 block_device 上使用帶有 blk_crypto_key 的內聯加密時,使用者必須呼叫 blk_crypto_evict_key()。這確保了金鑰從它可能被程式設計到的所有金鑰槽中逐出,並且與它可能連結到的任何核心資料結構取消連結。

總之,對於塊層的使用者,blk_crypto_key 的生命週期如下

  1. blk_crypto_config_supported()(可選)

  2. blk_crypto_init_key()

  3. blk_crypto_start_using_key()

  4. bio_crypt_set_ctx()(可能多次)

  5. blk_crypto_evict_key()(在所有 I/O 完成後)

  6. 將 blk_crypto_key 歸零(這沒有專用函式)

如果 blk_crypto_key 正在多個 block_device 上使用,則必須在每個 block_device 上呼叫 blk_crypto_config_supported()(如果使用)、blk_crypto_start_using_key()blk_crypto_evict_key()

呈現給裝置驅動程式的API

想要支援內聯加密的裝置驅動程式必須在其裝置的 request_queue 中設定一個 blk_crypto_profile。為此,它首先必須呼叫 blk_crypto_profile_init()(或其資源管理變體 devm_blk_crypto_profile_init()),提供金鑰槽的數量。

接下來,它必須透過設定 blk_crypto_profile 中的欄位來通告其加密能力,例如 modes_supportedmax_dun_bytes_supported

然後,它必須在 blk_crypto_profile 的 ll_ops 欄位中設定函式指標,以告知上層如何控制內聯加密硬體,例如如何程式設計和逐出金鑰槽。大多數驅動程式都需要實現 keyslot_programkeyslot_evict。有關詳細資訊,請參閱 struct blk_crypto_ll_ops 的註釋。

一旦驅動程式向 request_queue 註冊了一個 blk_crypto_profile,驅動程式透過該佇列接收的 I/O 請求可能具有加密上下文。所有加密上下文都將與 blk_crypto_profile 中宣告的加密能力相容,因此驅動程式無需擔心處理不受支援的請求。此外,如果在 blk_crypto_profile 中聲明瞭非零數量的金鑰槽,則所有具有加密上下文的 I/O 請求也將具有一個金鑰槽,該金鑰槽已程式設計了適當的金鑰。

如果驅動程式實現了執行時掛起,並且其 blk_crypto_ll_ops 在裝置執行時掛起時不起作用,則驅動程式還必須設定 blk_crypto_profile 的 dev 欄位,以指向將在任何低階操作被呼叫之前恢復的 struct device

如果存在內聯加密硬體丟失其金鑰槽內容的情況(例如裝置重置),則驅動程式必須處理重新程式設計金鑰槽。為此,驅動程式可以呼叫 blk_crypto_reprogram_all_keys()

最後,如果驅動程式使用了 blk_crypto_profile_init() 而不是 devm_blk_crypto_profile_init(),則它負責在不再需要加密配置檔案時呼叫 blk_crypto_profile_destroy()

分層裝置

基於請求佇列的分層裝置(如 dm-rq)想要支援內聯加密需要為其 request_queue 建立自己的 blk_crypto_profile,並公開他們選擇的任何功能。當分層裝置想要將該請求的克隆傳遞到另一個 request_queue 時,blk-crypto 將根據需要初始化和準備該克隆。

內聯加密和 blk 完整性之間的互動

在本文件釋出之時,沒有真正的硬體同時支援這兩個功能。但是,這些功能確實相互互動,並且使它們能夠正確地協同工作並非完全瑣碎。特別是,當 WRITE bio 想要在使用內聯加密的裝置上使用內聯加密時,將指定 bio 的加密上下文,之後計算其完整性資訊(使用明文資料,因為加密將在寫入資料時發生),並將資料和完整性資訊傳送到裝置。顯然,必須在加密資料之前驗證完整性資訊。加密資料後,裝置不得儲存它收到的帶有明文資料的完整性資訊,因為這可能會洩露有關明文資料的資訊。因此,它必須從密文資料重新生成完整性資訊,並將其儲存在磁碟上。儲存明文資料的完整性資訊的另一個問題是,它會根據是否存在硬體內聯加密支援或是否使用核心加密 API 回退來更改磁碟格式(因為如果使用回退,裝置將接收密文的完整性資訊,而不是明文的完整性資訊)。

因為還沒有任何真正的硬體,所以假設硬體實現可能無法正確地一起實現這兩個功能,並且暫時禁止這種組合似乎是明智的。每當裝置支援完整性時,核心將假裝該裝置不支援硬體內聯加密(透過將裝置的 request_queue 中的 blk_crypto_profile 設定為 NULL)。當啟用加密 API 回退時,這意味著所有具有加密上下文的 bio 都將使用回退,並且 IO 將像往常一樣完成。當停用回退時,具有加密上下文的 bio 將失敗。

硬體封裝金鑰

動機和威脅模型

Linux 儲存加密(dm-crypt、fscrypt、eCryptfs 等)傳統上依賴於原始加密金鑰存在於核心記憶體中,以便可以執行加密。傳統上,這不被視為一個問題,因為在離線攻擊期間金鑰不會存在,而這正是儲存加密旨在防止的主要攻擊型別。

但是,越來越希望(在可能的範圍內)保護使用者的資料免受其他型別的攻擊,包括

  • 冷啟動攻擊,攻擊者可以物理訪問系統,突然關閉電源,然後立即轉儲系統記憶體以提取最近使用的加密金鑰,然後使用這些金鑰解密磁碟上的使用者資料。

  • 線上攻擊,攻擊者能夠在不完全破壞系統的情況下讀取核心記憶體,然後進行離線攻擊,其中提取的任何金鑰都可用於解密磁碟上的使用者資料。這種線上攻擊的一個例子是,如果攻擊者能夠在系統上執行一些程式碼,該程式碼利用了類似 Meltdown 的漏洞,但無法提升許可權。

  • 線上攻擊,攻擊者完全破壞了系統,但其資料洩露在時間上受到嚴重限制和/或頻寬受到限制,因此為了完全洩露資料,他們需要提取加密金鑰以供以後進行離線攻擊。

硬體封裝金鑰是內聯加密硬體的一個功能,旨在(在可能的範圍內)保護使用者的資料免受上述攻擊,而不會引入諸如最大金鑰數量之類的限制。

請注意,不可能完全保護使用者的資料免受這些攻擊。即使在攻擊者“只是”獲得對核心記憶體的讀取訪問許可權的攻擊中,他們仍然可以提取記憶體中存在的任何使用者資料,包括加密檔案的明文 pagecache 頁面。這裡的重點只是保護加密金鑰,因為這些金鑰會立即訪問任何後續離線攻擊中的所有使用者資料,而不僅僅是一些資料(其中哪些資料包含在該“一些”中可能不受攻擊者控制)。

解決方案概述

內聯加密硬體通常具有“金鑰槽”,軟體可以將金鑰程式設計到其中以供硬體使用;金鑰槽的內容通常無法被軟體讀回。因此,如果核心在將金鑰程式設計到金鑰槽中後簡單地擦除了其金鑰副本,並且此後僅透過金鑰槽號引用它們,則可以實現上述安全目標。

但是,這種幼稚的方法會遇到幾個問題

  • 它將解鎖的金鑰數量限制為金鑰槽的數量,這通常是一個很小的數字。在整個系統中只有一個加密金鑰的情況下(例如,全盤加密金鑰),這是可以容忍的。但是,通常可能有很多登入使用者,他們有許多不同的金鑰,並且/或者有很多執行的應用程式,它們具有特定於應用程式的加密儲存區域。如果正在使用基於檔案的加密(例如 fscrypt),則尤其如此。

  • 如果儲存控制器(通常是 UFS 或 eMMC)被重置,內聯加密引擎通常會丟失其金鑰槽的內容。重置儲存控制器是標準錯誤恢復程式,如果發生某些型別的儲存錯誤,就會執行該程式,並且此類錯誤可能隨時發生。因此,當使用內聯加密時,作業系統必須始終準備好在沒有使用者干預的情況下重新程式設計金鑰槽。

因此,核心仍然需要一種方法來“提醒”硬體關於金鑰,而實際上沒有原始金鑰本身。

稍微不重要的是,原始金鑰也永遠不會對軟體可見,即使在最初解鎖時也是如此。這將確保對系統記憶體的只讀破壞永遠不會允許提取金鑰以供系統外使用,即使它發生在解鎖金鑰時也是如此。

為了解決所有這些問題,一些內聯加密硬體供應商使其硬體支援 硬體封裝金鑰。硬體封裝金鑰是加密的金鑰,只能由硬體解包(解密)和使用 -- 無論是透過內聯加密硬體本身,還是透過可以直接向內聯加密硬體提供金鑰的專用硬體塊。

(我們稱它們為“硬體封裝金鑰”,而不是簡單地稱為“封裝金鑰”,以便在可能存在其他型別的封裝金鑰(例如在基於檔案的加密中)的情況下增加一些清晰度。金鑰封裝是一種常用的技術。)

封裝(加密)硬體封裝金鑰的金鑰是硬體內部金鑰,永遠不會暴露給軟體;它要麼是持久金鑰(“長期封裝金鑰”),要麼是每次啟動金鑰(“臨時封裝金鑰”)。金鑰的長期封裝形式是最初解鎖的形式,但一旦它被轉換為臨時封裝金鑰,就會從記憶體中擦除。使用中的硬體封裝金鑰始終是臨時封裝的,而不是長期封裝的。

由於內聯加密硬體只能用於加密/解密磁碟上的資料,因此硬體還包括一個間接級別;它不直接使用解封的金鑰進行內聯加密,而是從中派生出一個內聯加密金鑰和一個“軟體秘密”。軟體可以使用“軟體秘密”來執行無法使用內聯加密硬體的任務,例如檔名加密。軟體秘密不受記憶體破壞的影響。

金鑰層次結構

這是硬體封裝金鑰的金鑰層次結構

               Hardware-wrapped key
                        |
                        |
                  <Hardware KDF>
                        |
          -----------------------------
          |                           |
Inline encryption key           Software secret

元件是

  • 硬體封裝金鑰:硬體 KDF(金鑰派生函式)的金鑰,以臨時封裝形式。金鑰封裝演算法是硬體實現細節,不會影響核心操作,但建議使用強大的經過身份驗證的加密演算法,例如 AES-256-GCM。

  • 硬體 KDF:KDF(金鑰派生函式),硬體在解封裝封裝金鑰後使用該函式來派生子金鑰。硬體選擇的 KDF 不會影響核心操作,但需要為了測試目的而瞭解它,並且還假定它至少具有 256 位的安全強度。所有已知的硬體都使用 AES-256-CMAC 在計數器模式下使用 SP800-108 KDF,並具有特定的標籤和上下文選擇;新的硬體應使用這種已經過審查的 KDF。

  • 內聯加密金鑰:硬體直接向內聯加密硬體的金鑰槽提供的派生金鑰,而不會將其暴露給軟體。在所有已知的硬體中,這將始終是一個 AES-256-XTS 金鑰。但是,原則上也可以支援其他加密演算法。硬體必須為每個支援的加密演算法派生不同的子金鑰。

  • 軟體秘密:硬體返回給軟體的派生金鑰,以便軟體可以將其用於無法使用內聯加密的加密任務。該值在密碼學上與內聯加密金鑰隔離,即,瞭解一個不會洩露另一個。(KDF 確保了這一點。)目前,軟體秘密始終為 32 位元組,因此適用於需要高達 256 位安全強度的加密應用程式。一些用例(例如全盤加密)不需要軟體秘密。

示例:在 fscrypt 的情況下,fscrypt 主金鑰(保護一組特定加密目錄的金鑰)被設為硬體封裝金鑰。內聯加密金鑰用作檔案內容加密金鑰,而軟體秘密(而不是直接使用主金鑰)用於為 fscrypt 的 KDF (HKDF-SHA512) 提供金鑰,以派生其他子金鑰,例如檔名加密金鑰。

請注意,目前此設計假定每個硬體封裝金鑰只有一個內聯加密金鑰,而沒有任何進一步的金鑰派生。因此,在 fscrypt 的情況下,目前硬體封裝金鑰僅與“內聯加密最佳化”設定相容,該設定對每個加密策略使用一個檔案內容加密金鑰,而不是每個檔案使用一個金鑰。可以擴充套件此設計,以使硬體使用傳遞到儲存堆疊的每個檔案 nonce 派生每個檔案金鑰,並且實際上一些硬體已經支援此功能;計劃在未來的工作中透過新增相應的核心支援來消除此限制。

核心支援

核心塊層(“blk-crypto”)的內聯加密支援已擴充套件為支援硬體封裝金鑰,作為原始金鑰的替代方案(在硬體支援可用時)。其工作方式如下

  • 一個 key_types_supported 欄位被新增到 struct blk_crypto_profile 中的加密能力中。這允許裝置驅動程式宣告它們支援原始金鑰、硬體封裝金鑰或兩者都支援。

  • struct blk_crypto_key 現在可以包含硬體封裝金鑰,作為原始金鑰的替代方案;一個 key_type 欄位被新增到 struct blk_crypto_config 中,以區分不同的金鑰型別。這允許 blk-crypto 的使用者以與使用原始金鑰非常相似的方式使用硬體封裝金鑰加密/解密資料。

  • 添加了一個新的方法 blk_crypto_ll_ops::derive_sw_secret。支援硬體封裝金鑰的裝置驅動程式必須實現此方法。blk-crypto 的使用者可以呼叫 blk_crypto_derive_sw_secret() 來訪問此方法。

  • 硬體封裝金鑰的程式設計和逐出透過 blk_crypto_ll_ops::keyslot_programblk_crypto_ll_ops::keyslot_evict 進行,就像對原始金鑰所做的那樣。如果驅動程式支援硬體封裝金鑰,則它必須處理傳遞給這些方法的硬體封裝金鑰。

blk-crypto-fallback 不支援硬體封裝金鑰。因此,硬體封裝金鑰只能與真正的內聯加密硬體一起使用。

以上所有內容僅涉及臨時封裝形式的硬體封裝金鑰。為了首先獲得此類金鑰,添加了新的塊裝置 ioctl,以提供建立和準備此類金鑰的通用介面

  • BLKCRYPTOIMPORTKEY 將原始金鑰轉換為長期包裝形式。它接受指向 struct blk_crypto_import_key_arg 的指標。呼叫者必須將 raw_key_ptrraw_key_size 設定為要匯入的原始金鑰的指標和大小(以位元組為單位)。成功時,BLKCRYPTOIMPORTKEY 返回 0,並將生成的長期包裝金鑰 blob 寫入由 lt_key_ptr 指向的緩衝區,該緩衝區的最大大小為 lt_key_size。它還會更新 lt_key_size 為金鑰的實際大小。失敗時,它返回 -1 並設定 errno。 EOPNOTSUPP 的 errno 表示塊裝置不支援硬體包裝金鑰。 EOVERFLOW 的 errno 表示輸出緩衝區沒有足夠的空間來容納金鑰 blob。

  • BLKCRYPTOGENERATEKEY 類似於 BLKCRYPTOIMPORTKEY,但它讓硬體生成金鑰而不是匯入金鑰。它接受指向 struct blk_crypto_generate_key_arg 的指標。

  • BLKCRYPTOPREPAREKEY 將金鑰從長期包裝形式轉換為臨時包裝形式。它接受指向 struct blk_crypto_prepare_key_arg 的指標。呼叫者必須將 lt_key_ptrlt_key_size 設定為要轉換的長期包裝金鑰 blob 的指標和大小(以位元組為單位)。成功時,BLKCRYPTOPREPAREKEY 返回 0,並將生成的臨時包裝金鑰 blob 寫入由 eph_key_ptr 指向的緩衝區,該緩衝區的最大大小為 eph_key_size。它還會更新 eph_key_size 為金鑰的實際大小。失敗時,它返回 -1 並設定 errno。 EOPNOTSUPPEOVERFLOW 的 Errno 值含義與 BLKCRYPTOIMPORTKEY 相同。 EBADMSG 的 errno 表示長期包裝金鑰無效。

使用者空間需要使用 BLKCRYPTOIMPORTKEYBLKCRYPTOGENERATEKEY 一次來建立金鑰,然後在每次解鎖金鑰並將其新增到核心時使用 BLKCRYPTOPREPAREKEY。請注意,這些 ioctl 與原始金鑰無關;它們僅適用於硬體包裝金鑰。

可測試性

硬體 KDF 和內聯加密本身都是定義明確的演算法,除了未包裝的金鑰外,不依賴於任何秘密。因此,如果軟體已知未包裝的金鑰,則可以在軟體中重現這些演算法,以驗證內聯加密硬體寫入磁碟的密文。

但是,只有在使用“匯入”功能時,軟體才能知道未包裝的金鑰。在硬體自行生成金鑰的“生成”情況下,無法進行適當的測試。“生成”模式的正確操作因此依賴於硬體 RNG 的安全性和正確性及其用於生成金鑰的方式,以及“匯入”模式的測試,因為該測試應涵蓋除金鑰生成之外的所有部分。

有關驗證以“匯入”模式寫入磁碟的密文的測試示例,請參見 xfstests 中的 fscrypt 硬體包裝金鑰測試,或 Android 的 vts_kernel_encryption_test