From 3dfb094cb26cf37e14b3bbf81e31248b913b3e41 Mon Sep 17 00:00:00 2001 From: Barani Muthukumaran Date: Mon, 3 Feb 2020 13:06:45 -0800 Subject: [PATCH] vold: Support Storage keys for FBE To prevent keys from being compromised if an attacker acquires read access to kernel memory, some inline encryption hardware supports protecting the keys in hardware without software having access to or the ability to set the plaintext keys. Instead, software only sees "wrapped keys", which may differ on every boot. 'wrappedkey_v0' fileencryption flag is used to denote that the device supports inline encryption hardware that supports this feature. On such devices keymaster is used to generate keys with STORAGE_KEY tag and export a per-boot ephemerally wrapped storage key to install it in the kernel. The wrapped key framework in the linux kernel ensures the wrapped key is provided to the inline encryption hardware where it is unwrapped and the file contents key is derived to encrypt contents without revealing the plaintext key in the clear. Test: FBE validation with Fscrypt v2 + inline crypt + wrapped key changes kernel. Bug: 147733587 Change-Id: I1f0de61b56534ec1df9baef075acb74bacd00758 --- FsCrypt.cpp | 40 ++++++++++++++++++++++++++-------------- KeyStorage.cpp | 24 ++++++++++++++++++++++++ KeyStorage.h | 5 +++++ KeyUtil.cpp | 15 ++++++++++++--- KeyUtil.h | 6 ++++-- Keymaster.cpp | 21 +++++++++++++++++++++ Keymaster.h | 2 ++ MetadataCrypt.cpp | 21 ++++++++++++++++++--- fscrypt_uapi.h | 19 +++++++++++++++++++ 9 files changed, 131 insertions(+), 22 deletions(-) create mode 100644 fscrypt_uapi.h diff --git a/FsCrypt.cpp b/FsCrypt.cpp index 1f7faac..21495be 100644 --- a/FsCrypt.cpp +++ b/FsCrypt.cpp @@ -208,6 +208,19 @@ static bool get_data_file_encryption_options(EncryptionOptions* options) { return true; } +static bool install_storage_key(const std::string& mountpoint, const EncryptionOptions& options, + const KeyBuffer& key, EncryptionPolicy* policy) { + KeyBuffer ephemeral_wrapped_key; + if (options.use_hw_wrapped_key) { + if (!exportWrappedStorageKey(key, &ephemeral_wrapped_key)) { + LOG(ERROR) << "Failed to get ephemeral wrapped key"; + return false; + } + } + return installKey(mountpoint, options, options.use_hw_wrapped_key ? ephemeral_wrapped_key : key, + policy); +} + // Retrieve the options to use for encryption policies on adoptable storage. static bool get_volume_file_encryption_options(EncryptionOptions* options) { auto contents_mode = @@ -231,7 +244,7 @@ static bool read_and_install_user_ce_key(userid_t user_id, KeyBuffer ce_key; if (!read_and_fixate_user_ce_key(user_id, auth, &ce_key)) return false; EncryptionPolicy ce_policy; - if (!installKey(DATA_MNT_POINT, options, ce_key, &ce_policy)) return false; + if (!install_storage_key(DATA_MNT_POINT, options, ce_key, &ce_policy)) return false; s_ce_policies[user_id] = ce_policy; LOG(DEBUG) << "Installed ce key for user " << user_id; return true; @@ -261,8 +274,8 @@ static bool create_and_install_user_keys(userid_t user_id, bool create_ephemeral EncryptionOptions options; if (!get_data_file_encryption_options(&options)) return false; KeyBuffer de_key, ce_key; - if (!android::vold::randomKey(&de_key)) return false; - if (!android::vold::randomKey(&ce_key)) return false; + if (!generateStorageKey(options, &de_key)) return false; + if (!generateStorageKey(options, &ce_key)) return false; if (create_ephemeral) { // If the key should be created as ephemeral, don't store it. s_ephemeral_users.insert(user_id); @@ -282,10 +295,10 @@ static bool create_and_install_user_keys(userid_t user_id, bool create_ephemeral return false; } EncryptionPolicy de_policy; - if (!installKey(DATA_MNT_POINT, options, de_key, &de_policy)) return false; + if (!install_storage_key(DATA_MNT_POINT, options, de_key, &de_policy)) return false; s_de_policies[user_id] = de_policy; EncryptionPolicy ce_policy; - if (!installKey(DATA_MNT_POINT, options, ce_key, &ce_policy)) return false; + if (!install_storage_key(DATA_MNT_POINT, options, ce_key, &ce_policy)) return false; s_ce_policies[user_id] = ce_policy; LOG(DEBUG) << "Created keys for user " << user_id; return true; @@ -338,7 +351,7 @@ static bool load_all_de_keys() { KeyBuffer de_key; if (!android::vold::retrieveKey(key_path, kEmptyAuthentication, &de_key)) return false; EncryptionPolicy de_policy; - if (!installKey(DATA_MNT_POINT, options, de_key, &de_policy)) return false; + if (!install_storage_key(DATA_MNT_POINT, options, de_key, &de_policy)) return false; s_de_policies[user_id] = de_policy; LOG(DEBUG) << "Installed de key for user " << user_id; } @@ -360,12 +373,11 @@ bool fscrypt_initialize_systemwide_keys() { KeyBuffer device_key; if (!android::vold::retrieveKey(true, kEmptyAuthentication, device_key_path, device_key_temp, - &device_key)) + options, &device_key)) return false; EncryptionPolicy device_policy; - if (!android::vold::installKey(DATA_MNT_POINT, options, device_key, &device_policy)) - return false; + if (!install_storage_key(DATA_MNT_POINT, options, device_key, &device_policy)) return false; std::string options_string; if (!OptionsToString(device_policy.options, &options_string)) { @@ -380,10 +392,9 @@ bool fscrypt_initialize_systemwide_keys() { LOG(INFO) << "Wrote system DE key reference to:" << ref_filename; KeyBuffer per_boot_key; - if (!android::vold::randomKey(&per_boot_key)) return false; + if (!generateStorageKey(options, &per_boot_key)) return false; EncryptionPolicy per_boot_policy; - if (!android::vold::installKey(DATA_MNT_POINT, options, per_boot_key, &per_boot_policy)) - return false; + if (!install_storage_key(DATA_MNT_POINT, options, per_boot_key, &per_boot_policy)) return false; std::string per_boot_ref_filename = std::string("/data") + fscrypt_key_per_boot_ref; if (!android::vold::writeStringToFile(per_boot_policy.key_raw_ref, per_boot_ref_filename)) return false; @@ -590,8 +601,9 @@ static bool read_or_create_volkey(const std::string& misc_path, const std::strin EncryptionOptions options; if (!get_volume_file_encryption_options(&options)) return false; KeyBuffer key; - if (!android::vold::retrieveKey(true, auth, key_path, key_path + "_tmp", &key)) return false; - if (!android::vold::installKey(BuildDataPath(volume_uuid), options, key, policy)) return false; + if (!android::vold::retrieveKey(true, auth, key_path, key_path + "_tmp", options, &key)) + return false; + if (!install_storage_key(BuildDataPath(volume_uuid), options, key, policy)) return false; return true; } diff --git a/KeyStorage.cpp b/KeyStorage.cpp index dbf190d..a7582c2 100644 --- a/KeyStorage.cpp +++ b/KeyStorage.cpp @@ -135,6 +135,30 @@ static bool generateKeymasterKey(Keymaster& keymaster, const KeyAuthentication& keymaster.generateKey(paramBuilder, key); } +bool generateWrappedStorageKey(KeyBuffer* key) { + Keymaster keymaster; + if (!keymaster) return false; + std::string key_temp; + auto paramBuilder = km::AuthorizationSetBuilder().AesEncryptionKey(AES_KEY_BYTES * 8); + paramBuilder.Authorization(km::TAG_ROLLBACK_RESISTANCE); + paramBuilder.Authorization(km::TAG_STORAGE_KEY); + if (!keymaster.generateKey(paramBuilder, &key_temp)) return false; + *key = KeyBuffer(key_temp.size()); + memcpy(reinterpret_cast(key->data()), key_temp.c_str(), key->size()); + return true; +} + +bool exportWrappedStorageKey(const KeyBuffer& kmKey, KeyBuffer* key) { + Keymaster keymaster; + if (!keymaster) return false; + std::string key_temp; + + if (!keymaster.exportKey(kmKey, &key_temp)) return false; + *key = KeyBuffer(key_temp.size()); + memcpy(reinterpret_cast(key->data()), key_temp.c_str(), key->size()); + return true; +} + static std::pair beginParams( const KeyAuthentication& auth, const std::string& appId) { auto paramBuilder = km::AuthorizationSetBuilder() diff --git a/KeyStorage.h b/KeyStorage.h index 276b6b9..f9d3ec6 100644 --- a/KeyStorage.h +++ b/KeyStorage.h @@ -68,6 +68,11 @@ bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, KeyBuffe bool destroyKey(const std::string& dir); bool runSecdiscardSingle(const std::string& file); + +// Generate wrapped storage key using keymaster. Uses STORAGE_KEY tag in keymaster. +bool generateWrappedStorageKey(KeyBuffer* key); +// Export the per-boot boot wrapped storage key using keymaster. +bool exportWrappedStorageKey(const KeyBuffer& kmKey, KeyBuffer* key); } // namespace vold } // namespace android diff --git a/KeyUtil.cpp b/KeyUtil.cpp index d4a653b..ae4d70b 100644 --- a/KeyUtil.cpp +++ b/KeyUtil.cpp @@ -29,6 +29,7 @@ #include #include +#include #include "KeyStorage.h" #include "Utils.h" @@ -45,6 +46,13 @@ bool randomKey(KeyBuffer* key) { return true; } +bool generateStorageKey(const EncryptionOptions& options, KeyBuffer* key) { + if (options.use_hw_wrapped_key) { + return generateWrappedStorageKey(key); + } + return randomKey(key); +} + // Return true if the kernel supports the ioctls to add/remove fscrypt keys // directly to/from the filesystem. bool isFsKeyringSupported(void) { @@ -222,6 +230,7 @@ bool installKey(const std::string& mountpoint, const EncryptionOptions& options, return false; } + if (options.use_hw_wrapped_key) arg->flags |= FSCRYPT_ADD_KEY_FLAG_WRAPPED; // Provide the raw key. arg->raw_size = key.size(); memcpy(arg->raw, key.data(), key.size()); @@ -307,8 +316,8 @@ bool evictKey(const std::string& mountpoint, const EncryptionPolicy& policy) { } bool retrieveKey(bool create_if_absent, const KeyAuthentication& key_authentication, - const std::string& key_path, const std::string& tmp_path, KeyBuffer* key, - bool keepOld) { + const std::string& key_path, const std::string& tmp_path, + const EncryptionOptions& options, KeyBuffer* key, bool keepOld) { if (pathExists(key_path)) { LOG(DEBUG) << "Key exists, using: " << key_path; if (!retrieveKey(key_path, key_authentication, key, keepOld)) return false; @@ -318,7 +327,7 @@ bool retrieveKey(bool create_if_absent, const KeyAuthentication& key_authenticat return false; } LOG(INFO) << "Creating new key in " << key_path; - if (!randomKey(key)) return false; + if (!generateStorageKey(options, key)) return false; if (!storeKeyAtomically(key_path, tmp_path, key_authentication, *key)) return false; } return true; diff --git a/KeyUtil.h b/KeyUtil.h index be5a2ed..878b4ab 100644 --- a/KeyUtil.h +++ b/KeyUtil.h @@ -32,6 +32,8 @@ using namespace android::fscrypt; bool randomKey(KeyBuffer* key); +bool generateStorageKey(const EncryptionOptions& options, KeyBuffer* key); + bool isFsKeyringSupported(void); // Install a file-based encryption key to the kernel, for use by encrypted files @@ -57,8 +59,8 @@ bool installKey(const std::string& mountpoint, const EncryptionOptions& options, bool evictKey(const std::string& mountpoint, const EncryptionPolicy& policy); bool retrieveKey(bool create_if_absent, const KeyAuthentication& key_authentication, - const std::string& key_path, const std::string& tmp_path, KeyBuffer* key, - bool keepOld = true); + const std::string& key_path, const std::string& tmp_path, + const EncryptionOptions& options, KeyBuffer* key, bool keepOld = true); } // namespace vold } // namespace android diff --git a/Keymaster.cpp b/Keymaster.cpp index abee9b2..c3f2912 100644 --- a/Keymaster.cpp +++ b/Keymaster.cpp @@ -138,6 +138,27 @@ bool Keymaster::generateKey(const km::AuthorizationSet& inParams, std::string* k return true; } +bool Keymaster::exportKey(const KeyBuffer& kmKey, std::string* key) { + auto kmKeyBlob = km::support::blob2hidlVec(std::string(kmKey.data(), kmKey.size())); + km::ErrorCode km_error; + auto hidlCb = [&](km::ErrorCode ret, const hidl_vec& exportedKeyBlob) { + km_error = ret; + if (km_error != km::ErrorCode::OK) return; + if (key) + key->assign(reinterpret_cast(&exportedKeyBlob[0]), exportedKeyBlob.size()); + }; + auto error = mDevice->exportKey(km::KeyFormat::RAW, kmKeyBlob, {}, {}, hidlCb); + if (!error.isOk()) { + LOG(ERROR) << "export_key failed: " << error.description(); + return false; + } + if (km_error != km::ErrorCode::OK) { + LOG(ERROR) << "export_key failed, code " << int32_t(km_error); + return false; + } + return true; +} + bool Keymaster::deleteKey(const std::string& key) { auto keyBlob = km::support::blob2hidlVec(key); auto error = mDevice->deleteKey(keyBlob); diff --git a/Keymaster.h b/Keymaster.h index 8ddd8f7..ea102f5 100644 --- a/Keymaster.h +++ b/Keymaster.h @@ -114,6 +114,8 @@ class Keymaster { explicit operator bool() { return mDevice.get() != nullptr; } // Generate a key in the keymaster from the given params. bool generateKey(const km::AuthorizationSet& inParams, std::string* key); + // Exports a keymaster key with STORAGE_KEY tag wrapped with a per-boot ephemeral key + bool exportKey(const KeyBuffer& kmKey, std::string* key); // If the keymaster supports it, permanently delete a key. bool deleteKey(const std::string& key); // Replace stored key blob in response to KM_ERROR_KEY_REQUIRES_UPGRADE. diff --git a/MetadataCrypt.cpp b/MetadataCrypt.cpp index acd5b59..76ea9eb 100644 --- a/MetadataCrypt.cpp +++ b/MetadataCrypt.cpp @@ -113,6 +113,23 @@ static void commit_key(const std::string& dir) { LOG(INFO) << "Old Key deleted: " << dir; } +static bool retrieveMetadataKey(bool create_if_absent, const std::string& key_path, + const std::string& tmp_path, KeyBuffer* key, bool keepOld) { + if (pathExists(key_path)) { + LOG(DEBUG) << "Key exists, using: " << key_path; + if (!retrieveKey(key_path, kEmptyAuthentication, key, keepOld)) return false; + } else { + if (!create_if_absent) { + LOG(ERROR) << "No key found in " << key_path; + return false; + } + LOG(INFO) << "Creating new key in " << key_path; + if (!randomKey(key)) return false; + if (!storeKeyAtomically(key_path, tmp_path, kEmptyAuthentication, *key)) return false; + } + return true; +} + static bool read_key(const FstabEntry& data_rec, bool create_if_absent, KeyBuffer* key) { if (data_rec.metadata_key_dir.empty()) { LOG(ERROR) << "Failed to get metadata_key_dir"; @@ -143,9 +160,7 @@ static bool read_key(const FstabEntry& data_rec, bool create_if_absent, KeyBuffe unlink(newKeyPath.c_str()); } bool needs_cp = cp_needsCheckpoint(); - if (!android::vold::retrieveKey(create_if_absent, kEmptyAuthentication, dir, temp, key, - needs_cp)) - return false; + if (!retrieveMetadataKey(create_if_absent, dir, temp, key, needs_cp)) return false; if (needs_cp && pathExists(newKeyPath)) std::thread(commit_key, dir).detach(); return true; } diff --git a/fscrypt_uapi.h b/fscrypt_uapi.h new file mode 100644 index 0000000..08592e0 --- /dev/null +++ b/fscrypt_uapi.h @@ -0,0 +1,19 @@ +#ifndef _UAPI_LINUX_FSCRYPT_VOLD_H +#define _UAPI_LINUX_FSCRYPT_VOLD_H + +#include +#include + +#define FSCRYPT_ADD_KEY_FLAG_WRAPPED 0x01 + +struct sys_fscrypt_add_key_arg { + struct fscrypt_key_specifier key_spec; + __u32 raw_size; + __u32 __reserved[8]; + __u32 flags; + __u8 raw[]; +}; + +#define fscrypt_add_key_arg sys_fscrypt_add_key_arg + +#endif //_UAPI_LINUX_FSCRYPT_VOLD_H