| RFC-0157:Fxfs 加密和多卷宗支援 | |
|---|---|
| 狀態 | 已接受 |
| 區域 |
|
| 說明 | 說明 Fxfs 中的加密和多卷支援。 |
| 問題 | |
| Gerrit 變更 | |
| 作者 | |
| 審查人員 | |
| 提交日期 (年-月-日) | 2022-04-07 |
| 審查日期 (年-月-日) | 2022-04-28 |
摘要
這份 RFC 說明 Fxfs 中的加密和多卷宗支援功能。
提振精神
Fuchsia 必須能夠支援多個磁碟區,這些磁碟區使用繫結至不同使用者密碼的不同金鑰加密。
利害關係人
審查人員:abarth@google.com、enoharemaien@google.com、palmer@google.com、 jfsulliv@google.com、jsankey@google.com、zarvox@google.com
諮詢對象:Fuchsia 的安全、儲存空間和隱私權團隊。
社交化:在開始這項 RFC 程序之前,這項設計已流通並由儲存團隊和上述審查人員審查。
設計
Fxfs 的說明請參閱 RFC-0136。
需求條件
應該可以建立、列舉及刪除磁碟區。
每個磁碟區都應使用不同的金鑰加密。
檔案名稱、大小和時間戳記等物件中繼資料應加密。
如果沒有金鑰,就必須能夠刪除磁碟區。
應可支援金鑰輪替,而不需耗費大量心力進行遷移。
磁碟區大小應設有限制 (但這項設計留待日後處理)。
即使沒有金鑰存取權,也應該可以查詢磁碟區大小。
應根據區塊計數防範指紋攻擊。這類攻擊會利用一組檔案中多個檔案的加密大小 (向上取整至最接近的區塊),判斷該組檔案是否存在於檔案系統。
不在涵蓋範圍內的事項
這項設計未涵蓋下列領域:
在裝置 (包括主機和目標裝置) 之間雙向傳輸加密圖片。雖然這有助於偵錯,但不太可能在生產裝置上實現,而且沒有先例。
加密服務的實作方式。這項設計已在其他地方說明。
設計應支援金鑰輪替,但元件之間使用的確切 API 不在範圍內。
總覽
Fxfs 加密
Fxfs 內建加密功能將支援個別檔案金鑰。檔案通常只會有一個加密金鑰,但為了支援金鑰輪替和檔案複製,格式會支援每個檔案有多個金鑰。金鑰會由 Fxfs 通訊的加密服務包裝及解除包裝。
根父項和根存放區中的下列物件會採用某種形式的加密:
(存在於根父項商店中的) 日誌會包含子項商店中繼資料的變動,這些變動會使用串流加密法加密。這個密碼的加密金鑰會依商店而異。
子商店的圖層檔案 (位於根商店內) 會經過加密。加密這些檔案的機制與所有其他檔案相同。金鑰會使用每家商店的金鑰包裝,並與其他未加密的商店資訊一起儲存。
根父項和根存放區中的其他物件不會加密,包括超級區塊、所有與分配器相關的物件、支援根物件存放區的層檔案 (其中包含根存放區物件的中繼資料),以及包含子存放區基本資訊的物件。這些都不是使用者資料。
因此,最終結果是:
- 使用者資料會經過加密
- 所有中繼資料 (包括檔案名稱、檔案大小、範圍資訊和目錄資訊) 都會加密。
部分資訊不會加密:
- 磁碟區中的檔案數量。
- 分配給磁碟區的範圍集。
物件 ID
在 Fxfs 中,物件 ID 目前是以單調遞增的方式分配,可用做側管道。為解決這個問題,系統會使用 ff1 加密方式,加密物件 ID 最不重要的 32 位元 (在分配時間)。在循環使用 32 位元值的物件 ID 後,金鑰就會輪替。金鑰會經過包裝並與其餘未加密的商店資訊一併儲存。每次金鑰輪替時,物件 ID 的前 32 位元都會單調遞增。
磁碟區內的物件數量和使用的空間也會提供側邊管道 (基本上是透過 statvfs 提供的所有資訊)。解決這個問題超出這個設計的範圍 (Fxfs 並非新功能)。
金鑰管理
金鑰包裝和解除包裝作業由另一個「加密」服務負責,該服務會提供類似下列通訊協定的內容:
/// Designates the purpose of a key.
type KeyPurpose = flexible enum {
/// The key will be used to encrypt metadata.
METADATA = 1;
/// The key will be used to encrypt data.
DATA = 2;
};
protocol Crypt {
/// Creates a new wrapped key. `owner`, effectively a nonce, identifies the
/// owner (an object ID) of the key and must be supplied to `UnwrapKey`.
/// `metadata` indicates that the key will be used to encrypt metadata which
/// might influence the choice of wrapping key used by the service. Returns
/// the wrapping key ID used to wrap this key along with the wrapped and
/// unwrapped key. Errors:
/// ZX_ERR_INVALID_ARGS: purpose is not recognised.
/// ZX_ERR_BAD_STATE: the crypt service has not been correctly initialized
/// with a wrapping key for the specified purpose.
CreateKey(struct {
owner uint64;
purpose KeyPurpose;
}) -> (struct {
wrapping_key_id uint64;
wrapped_key bytes:48;
unwrapped_key bytes:32;
}) error zx.status;
/// Unwraps keys that are wrapped by the key identified by `wrapping_key_id`.
/// `owner` must be the same as that passed to `CreateKey`. This can fail due
/// to permission reasons or if an incorrect key is supplied.
UnwrapKey(struct {
wrapping_key_id uint64;
owner uint64;
key bytes:48;
}) -> (struct {
unwrapped_key bytes:32;
}) error zx.status;
};
wrapping_key_id的意義取決於加密服務的實作者。Fxfs 不會對其值套用任何顯著性。預期每個加密的 Fxfs 磁碟區都會有獨立連線,因此伺服器可位於不同程序中 (如有需要)。
目前支援 256 位元金鑰 (與 Zxcrypt 一致)。
預計加密服務會使用 AEAD 包裝金鑰,這表示包裝後的金鑰大小可能為 48 個位元組。
purpose用於分隔中繼資料使用的金鑰和資料使用的金鑰,並用於簡化金鑰輪替 (請參閱下文)。
加密服務的確切實作方式和金鑰管理政策,不在本設計的範圍內。
磁碟格式
每個檔案都會有類似下列的內容:
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub enum EncryptionKeys {
None,
AES256XTS(WrappedKeys),
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct WrappedKeys {
/// The keys (wrapped). To support key rolling and clones, there can be more
/// than one key. Each of the keys is given an identifier. The identifier is
/// unique to the object. AES256-XTS requires a 512 bit key, which is made
/// of two 256 bit keys, one for the data and one for the tweak. Both those
/// keys are derived from the single 256 bit key we have here.
pub keys: Vec<(/* wrapping_key_id= */ u64, /* id= */ u64, [u8; 48])>,
}
每個範圍都會類似下列內容:
pub enum ExtentValue {
/// Indicates a deleted extent; that is, the logical range described by the
/// extent key is considered to be deleted.
None,
/// The location of the extent and other related information. `key_id`
/// identifies which of the object's keys should be used. `checksums` hold
/// post-encryption checksums.
Some { device_offset: u64, checksums: Checksums, key_id: u64 },
}
資料區塊會使用 AES-XTS-256 加密 (與 Zxcrypt 使用的加密方式相同)。系統會使用檔案中的邏輯偏移量進行調整。
中繼資料
物件儲存空間會維護記錄結構合併 (LSM) 樹狀結構,以保留中繼資料,這些資料會以與儲存空間內檔案相同的方式加密。系統會建立圖層檔案使用的金鑰,並將用途設為「中繼資料」。
交易提交後,系統會將變異寫入日誌。這些變動會套用至 LSM 樹狀結構的記憶體內層。一段時間後,系統會將記憶體內層排清至永久層。物件中繼資料樹狀結構的任何變動都必須加密,才能寫入記錄檔。為支援這項功能,Fxfs 使用了新的突變:
EncryptedObjectStore(Box<[u8]>),
系統會使用串流加密 (不適合使用 AES-XTS-256 等區塊加密),以 Chacha20 加密這些變動。這個金鑰會經過包裝,並與其他未加密的商店資料一起儲存。
重播時可能無法使用金鑰,因此這些變動會以加密形式保留在記憶體中。如有需要清除記憶體內資料 (釋出日誌中的空間),這些加密變動會寫入根存放區中的物件。
金鑰可用時,系統會解密並套用加密的變動 (可能位於記憶體中或上述檔案中)。如要解密變動,必須使用密文串流中的位移;這會與商店的未加密資訊一併儲存。
以下是新增至 StoreInfo 的欄位 (商店的未加密資訊):
// The (wrapped) key that encrypted mutations should use.
mutations_key: Option<Box<[u8]>>,
// Mutations for the store are encrypted using a stream cipher. To decrypt the
// mutations, we need to know the offset in the cipher stream to start it.
mutations_cipher_offset: u64,
// If we have to flush the store whilst we do not have the key, we need to
// write the encrypted mutations to an object. This is the object ID of that
// file if it exists.
encrypted_mutations_object_id: u64,
刪除磁碟區
為支援刪除磁碟區,分配中繼資料會包含擁有中繼資料的儲存空間 ID 物件 ID:
pub struct AllocatorKey {
pub device_range: Range<u64>,
}
pub enum AllocationRefCount {
// Tombstone variant indicating an extent is no longer allocated.
None,
// Used when we know there are no possible allocations below us in the stack.
// (e.g. on the first allocation of an extent.)
// This variant also tracks the owning ObjectStore for the extent.
Abs { count: u64, owner_object_id: u64 },
}
pub struct AllocatorValue {
/// Reference count for the allocated range.
/// (A zero reference count is treated as a 'tombstone', indicating that older
/// values in the LSM Tree for this range should be ignored).
pub refs: AllocationRefCount,
}
刪除磁碟區時,系統會變更分配器,將屬於已刪除磁碟區的所有記錄視為可用。完成主要壓縮作業後,我們就能確定這些記錄已不存在,因此可以忘記該磁碟區的存在。配置器會將已刪除磁碟區的清單資訊與其餘中繼資料一併保留。
內嵌資料
Fxfs 目前不支援內嵌資料,但日後支援時,會使用與加密中繼資料相同的金鑰加密內嵌資料。這些金鑰會隨著新層檔案的寫入而有效輪替。
安全清除
如要安全地清除檔案,即使金鑰 (硬體中的金鑰除外) 後續遭到入侵,檔案也必須無法復原。由於檔案系統通常在快閃裝置上執行 (採用垃圾收集機制),因此清除所有資料出現位置並非易事。唯一可行的解決方案是捲動包裝金鑰 (不應儲存在快閃記憶體中)。
我們目前沒有實作安全清除功能的計畫,但應該可以透過下列程序達成:
開始使用新的包裝金鑰包裝新的中繼資料金鑰。
Fxfs 會使用舊的中繼資料金鑰重寫所有物件。
舊的中繼資料包裝金鑰會遭到銷毀 (通常是直接或間接使用 TPM 功能,這不在本設計的範圍內)。
在 Fxfs 中執行主要壓縮作業,即可相對輕鬆地完成步驟 2。由於這是自然發生的情況,因此這項程序可以定期執行,也可以視需要執行。可以安排 Fxfs 每週保證進行一次主要壓縮,但這取決於可用的鍵。
這項程序需要使用與資料不同的包裝金鑰來包裝中繼資料金鑰 (否則會強制重新編寫所有資料,這是不允許的),因此提供給 create_key 方法的中繼資料引數。
請注意,這項程序需要重新編寫卷冊的所有中繼資料,因此不應經常執行。
金鑰輪替
如要安全清除資料,請按照上述步驟,為中繼資料包裝金鑰進行金鑰輪替。您應可使用下列程序,輪替用於包裝資料金鑰的金鑰:
使用新的包裝金鑰包裝新金鑰。傳回以這種方式包裝的金鑰的新 wrapping_key_id。
請 Fxfs 重新包裝所有與指定 wrapping_key_id 相符的金鑰。這項功能的 API 留待後續設計,不應要求變更磁碟格式。
按照安全清除程序包裝中繼資料金鑰。由於金鑰只會寫入中繼資料檔案,因此在舊的包裝金鑰遭到銷毀後,應可確保資料包裝金鑰順利輪替。
請注意,步驟 2 和 3 可以合併執行,也就是說,您應該可以同時執行主要壓縮作業和重新包裝金鑰。
與安全清除功能一樣,我們最初並未計畫實作金鑰滾動支援功能。
Fsck
如果沒有金鑰,fsck 只能執行有限的檢查。顯然無法確保加密商店的一致性,但可以檢查所有其他未加密商店的範圍和一致性。
取得金鑰後,即可執行完整一致性檢查,或只檢查個別卷冊的中繼資料。
支援多個音量
fshost 會匯出新目錄:volumes。目錄中的節點會代表 fshost 匯出的磁碟區,並支援新的磁碟區通訊協定 (節點不支援其他通訊協定,也就是說,節點不支援 fuchsia.io 節點通訊協定,因此無法複製)。通訊協定會類似如下:
type MountOptions = table {
// A handle to the crypt service. Unencrypted volumes will ignore this option.
crypt client_end:Crypt;
}
protocol Volume {
// Mounts the volume and starts serving the filesystem. An error will be
// returned if the volume is currently being served from a previous call
// to Mount.
Mount(resource struct {
export_root server_end:Directory;
options MountOptions;
}) -> () error zx.status;
}
如果提供的加密服務有誤,掛接作業就會失敗,但這種情況應該很少發生;使用者應呼叫 Mount,並預期 Mount 會成功,不應將其做為測試憑證的方法。
匯出根目錄看起來會與檔案系統目前公開的根目錄完全相同。系統會公開 fs.Admin 服務,並可使用 Shutdown 方法鎖定/關閉磁碟區。如果所有磁碟區連線都已關閉,磁碟區也會遭到鎖定。磁碟區鎖定後,系統會謹慎處理,確保所有未包裝的金鑰都會遭到捨棄。
列舉和移除作業將透過磁碟區目錄上的 fuchsia.io 目錄通訊協定完成。移除作業可能為非同步,但系統會等到確定磁碟區最終會移除,才會傳回成功訊息。名稱可立即重複使用,但空間可能需要一段時間才能使用。
新卷宗無法使用 Directory 通訊協定的 open 方法,因為我們需要提供加密服務和其他選項。而是新增以下通訊協定:
type CreateVolumeOptions = table {
// Reserved for future use.
}
protocol Manager {
// Creates a new data volume.
CreateVolume(resource struct {
name string:MAX_FILENAME;
crypt client_end:Crypt;
export_root server_end: Directory;
options CreateVolumeOptions;
}) -> () error zx.status;;
}
一開始,實作作業會假設 Fxfs 將管理所有這些磁碟區,但日後應可提供與 Zxcrypt 支援的檔案系統搭配使用的元件。
磁碟區上可能存在的磁碟區名稱和確切磁碟區集,將根據產品決策而定,不屬於本 RFC 的範圍。
實作
支援多個磁碟區和加密功能,將以一般方式實作。
目前沒有支援金鑰輪替、安全清除和內嵌資料的計畫。
AES-XTS-256、ChaCha20 和 ff1 加密功能將由第三方 Crate 提供。
您必須接受安全稽核。
效能
加密會影響效能。系統會使用現有的檔案系統基準來評估效能。實作完成後,即可消除 Zxcrypt,這應可抵銷因實作而造成的任何損失。我們會調查目前效能的任何回歸情形。
回溯相容性
這項變更會破壞 Fxfs,因此需要重新格式化。
安全性考量
資安團隊已審查這項 RFC。
以下規定是基於安全考量而制定:
每個磁碟區都應以不同的金鑰加密。
檔案資料和中繼資料應經過加密。
應難以將物件 ID 做為旁側管道。
實作時需要進行安全審查。
隱私權注意事項
隱私權團隊已審查這項 RFC。
主要隱私權考量是以下資料不會加密:
- 磁碟區中的檔案數量。
- 分配給磁碟區的範圍集 (包括分配給磁碟區的空間量)。
- 磁碟區名稱。
其他所有資料和中繼資料都應加密。這應該足以滿足需求,包括指紋攻擊。
實作作業的安全審查應驗證設計是否符合這些規定。
測試
我們會使用單元、整合和端對端測試的常見組合進行測試。Fxfs 將用於 CQ 下執行的許多測試的資料分割區,因此會以這種方式曝光。
說明文件
在某個階段,系統設計人員需要文件,協助他們為特定產品選擇不同的儲存空間選項。不過,設定方式尚未決定,且不屬於本 RFC 的範圍。
本 RFC 中介紹的 API 最初 (很可能無限期) 會在樹狀結構中,目前會記錄為 FIDL 的一部分。
缺點、替代方案和未知事項
本 RFC 中說明的加密設計,比 Zxcrypt 使用的磁碟分割區加密複雜許多。
系統設計人員仍可使用以磁碟分割為基礎的加密方式,但這會限制磁碟區之間的空間共用:以磁碟分割為基礎的彈性空間配置需要磁碟區管理員 (因此會因額外的間接性而影響效能),且可能會發生片段化問題 (在磁碟分割之間移動空間可能需要重組)。此外,以磁碟分割為基礎的配置也難以支援安全清除和金鑰輪替。
既有技術和參考資料
Zxcrypt 使用 AES-256 XTS 加密區塊,方式與這項 RFC 類似,但使用的微調有所不同。
此處的設計大多符合 Android 12 的加密規定。其中一項重大差異是,通過 Fxfs 日誌的中繼資料必須使用串流加密法加密,但這並未明確提及為可接受的加密法。