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 會成功為前提呼叫 Mount;請勿將其用於測試憑證。
匯出根目錄現在會與檔案系統公開的根目錄類似。系統會公開 fs.Admin 服務,並可使用 Shutdown 方法鎖定/關閉磁碟分割區。如果所有與磁碟區的連線都已關閉,磁碟區也會上鎖。當裝置鎖定時,系統會小心處理,確保所有展開的金鑰都會遭到捨棄。
系統會透過 volumes 目錄中的 fuchsia.io 目錄通訊協定進行列舉和移除作業。移除作業可能會非同步,也可能會同步,但除非系統確定最終會移除音量,否則不會傳回成功訊息。名稱可以立即重複使用,但空間可能需要一些時間才能開始使用。
新的磁碟區無法使用目錄通訊協定的開啟方法,因為我們需要提供加密服務和其他選項。而是新增新的通訊協定:
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。
主要隱私權考量是,下列資料「不會」加密:
- 磁碟區中的檔案數量。
- 分配給磁碟區的邊界集合 (包括分配給磁碟區的空間量)。
- 磁碟區的名稱。
所有其他資料和中繼資料都應加密。這應該足以因應指紋攻擊等相關規定。
實作項目的安全性審查應驗證設計是否符合這些規定。
測試
這項測試會使用單元、整合和端對端測試的常見組合進行。在 CQ 下執行的許多測試都會使用 Fxfs 做為資料分割區,因此會因此曝光。
說明文件
在某個階段,您需要提供說明文件,協助系統設計人員為特定產品選擇不同的儲存空間選項。不過,設定方式尚未決定,且不在本 RFC 的範圍內。
本 RFC 中推出的 API 一開始 (並且很可能無限期) 會是樹狀結構內,目前會以 FIDL 的一部分形式記錄。
缺點、替代方案和未知事項
本 RFC 中所述的加密設計比 Zxcrypt 使用的分割區加密更為複雜。
系統設計人員仍可使用以分區為基礎的加密技術,但這會導致磁碟區之間的空間共用方式受到限制:以分區為基礎的方案需要使用磁碟區管理員,才能靈活運用空間 (因此會因額外的間接方式而影響效能),且可能會發生碎片化問題 (在分區之間移動空間可能需要進行重組)。在以分割區為基礎的配置中,要支援安全擦除和金鑰輪替功能也相當困難。
既有技術與參考資料
Zxcrypt 使用 AES-256 XTS 加密區塊,方法與此 RFC 相似,但使用的調整方式有所不同。
這項設計大多與 Android 12 的加密需求相容。其中一個重大差異是,透過 Fxfs 日誌的中繼資料必須使用串流密碼加密,但並未明確提及可接受的密碼。