From 83a73d7d5025c5c3148ccf655ea68aacb4885160 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 30 Sep 2019 13:06:47 -0700 Subject: [PATCH] vold: support v2 encryption policies Add support for setting v2 encryption policies when configured in the fstab (for internal storage) or in system properties (for adoptable storage), and for installing and evicting the keys for such policies. v2 policies support the same encryption modes and flags as v1 policies, but internally they use a more standard, secure, and flexible KDF. Due to this, some future features will be supported by v2 policies only. Bug: 140500999 Test: Configured a device to use v2 encryption policies (applied the needed kernel patches and added "fileencryption=aes-256-xts:aes-256-cts:v2" to fstab, and set the corresponding system properties for adoptable storage). Wiped userdata, booted device and checked logs to verify that v2 policies were being used. Also enabled virtual SD card and formatted as adoptable storage; verified it works and that v2 policies were being used on it. Also created, started, and stopped a 2nd user and verified their keys were evicted. Also verified that the device comes up again after rebooting. Also verified that a device using v1 encryption policies continues to work, both with and without an updated kernel -- including stopping a user so that their keys get evicted. Change-Id: If64028d8580584b2c33c614cabd5d6b93657f608 --- FsCrypt.cpp | 94 +++++++++++++++++++++++++++++++++++++--------------- KeyUtil.cpp | 95 ++++++++++++++++++++++++++++++++++++++++++----------- KeyUtil.h | 8 +++-- 3 files changed, 148 insertions(+), 49 deletions(-) diff --git a/FsCrypt.cpp b/FsCrypt.cpp index e28002f..bea9328 100644 --- a/FsCrypt.cpp +++ b/FsCrypt.cpp @@ -57,6 +57,7 @@ #include #include #include +#include #include using android::base::StringPrintf; @@ -70,7 +71,10 @@ namespace { struct PolicyKeyRef { std::string contents_mode; std::string filenames_mode; + int policy_version; std::string key_raw_ref; + + PolicyKeyRef() : policy_version(0) {} }; const std::string device_key_dir = std::string() + DATA_MNT_POINT + fscrypt_unencrypted_folder; @@ -199,14 +203,57 @@ static bool read_and_fixate_user_ce_key(userid_t user_id, return false; } +// Retrieve the options to use for encryption policies on the /data filesystem. +static void get_data_file_encryption_options(PolicyKeyRef* key_ref) { + auto entry = GetEntryForMountPoint(&fstab_default, DATA_MNT_POINT); + if (entry == nullptr) { + return; + } + key_ref->contents_mode = entry->file_contents_mode; + key_ref->filenames_mode = entry->file_names_mode; + key_ref->policy_version = entry->file_policy_version; +} + +// Retrieve the version to use for encryption policies on the /data filesystem. +static int get_data_file_policy_version(void) { + auto entry = GetEntryForMountPoint(&fstab_default, DATA_MNT_POINT); + if (entry == nullptr) { + return 0; + } + return entry->file_policy_version; +} + +// Retrieve the options to use for encryption policies on adoptable storage. +static bool get_volume_file_encryption_options(PolicyKeyRef* key_ref) { + key_ref->contents_mode = + android::base::GetProperty("ro.crypto.volume.contents_mode", "aes-256-xts"); + key_ref->filenames_mode = + android::base::GetProperty("ro.crypto.volume.filenames_mode", "aes-256-heh"); + key_ref->policy_version = 1; + + std::string raw_flags = android::base::GetProperty("ro.crypto.volume.flags", ""); + auto flags = android::base::Split(raw_flags, "+"); + for (const auto& flag : flags) { + if (flag == "v1") { + key_ref->policy_version = 1; + } else if (flag == "v2") { + key_ref->policy_version = 2; + } else { + LOG(ERROR) << "Unknown flag in ro.crypto.volume.flags: " << flag; + return false; + } + } + return true; +} + // Install a key for use by encrypted files on the /data filesystem. static bool install_data_key(const KeyBuffer& key, std::string* raw_ref) { - return android::vold::installKey(key, DATA_MNT_POINT, raw_ref); + return android::vold::installKey(key, DATA_MNT_POINT, get_data_file_policy_version(), raw_ref); } // Evict a key for use by encrypted files on the /data filesystem. static bool evict_data_key(const std::string& raw_ref) { - return android::vold::evictKey(DATA_MNT_POINT, raw_ref); + return android::vold::evictKey(DATA_MNT_POINT, raw_ref, get_data_file_policy_version()); } static bool read_and_install_user_ce_key(userid_t user_id, @@ -286,19 +333,10 @@ static bool lookup_key_ref(const std::map& key_map, useri return true; } -static void get_data_file_encryption_modes(PolicyKeyRef* key_ref) { - auto entry = GetEntryForMountPoint(&fstab_default, DATA_MNT_POINT); - if (entry == nullptr) { - return; - } - key_ref->contents_mode = entry->file_contents_mode; - key_ref->filenames_mode = entry->file_names_mode; -} - static bool ensure_policy(const PolicyKeyRef& key_ref, const std::string& path) { return fscrypt_policy_ensure(path.c_str(), key_ref.key_raw_ref.data(), key_ref.key_raw_ref.size(), key_ref.contents_mode.c_str(), - key_ref.filenames_mode.c_str()) == 0; + key_ref.filenames_mode.c_str(), key_ref.policy_version) == 0; } static bool is_numeric(const char* name) { @@ -354,14 +392,18 @@ bool fscrypt_initialize_systemwide_keys() { } PolicyKeyRef device_ref; + get_data_file_encryption_options(&device_ref); + if (!android::vold::retrieveAndInstallKey(true, kEmptyAuthentication, device_key_path, - device_key_temp, "", &device_ref.key_raw_ref)) + device_key_temp, "", device_ref.policy_version, + &device_ref.key_raw_ref)) return false; - get_data_file_encryption_modes(&device_ref); - std::string modestring = device_ref.contents_mode + ":" + device_ref.filenames_mode; - std::string mode_filename = std::string("/data") + fscrypt_key_mode; - if (!android::vold::writeStringToFile(modestring, mode_filename)) return false; + std::string options_string = + StringPrintf("%s:%s:v%d", device_ref.contents_mode.c_str(), + device_ref.filenames_mode.c_str(), device_ref.policy_version); + std::string options_filename = std::string("/data") + fscrypt_key_mode; + if (!android::vold::writeStringToFile(options_string, options_filename)) return false; std::string ref_filename = std::string("/data") + fscrypt_key_ref; if (!android::vold::writeStringToFile(device_ref.key_raw_ref, ref_filename)) return false; @@ -560,14 +602,12 @@ static bool read_or_create_volkey(const std::string& misc_path, const std::strin return false; } android::vold::KeyAuthentication auth("", secdiscardable_hash); - if (!android::vold::retrieveAndInstallKey(true, auth, key_path, key_path + "_tmp", volume_uuid, - &key_ref->key_raw_ref)) - return false; - key_ref->contents_mode = - android::base::GetProperty("ro.crypto.volume.contents_mode", "aes-256-xts"); - key_ref->filenames_mode = - android::base::GetProperty("ro.crypto.volume.filenames_mode", "aes-256-heh"); - return true; + + if (!get_volume_file_encryption_options(key_ref)) return false; + + return android::vold::retrieveAndInstallKey(true, auth, key_path, key_path + "_tmp", + volume_uuid, key_ref->policy_version, + &key_ref->key_raw_ref); } static bool destroy_volkey(const std::string& misc_path, const std::string& volume_uuid) { @@ -715,7 +755,7 @@ bool fscrypt_prepare_user_storage(const std::string& volume_uuid, userid_t user_ PolicyKeyRef de_ref; if (volume_uuid.empty()) { if (!lookup_key_ref(s_de_key_raw_refs, user_id, &de_ref.key_raw_ref)) return false; - get_data_file_encryption_modes(&de_ref); + get_data_file_encryption_options(&de_ref); if (!ensure_policy(de_ref, system_de_path)) return false; if (!ensure_policy(de_ref, misc_de_path)) return false; if (!ensure_policy(de_ref, vendor_de_path)) return false; @@ -746,7 +786,7 @@ bool fscrypt_prepare_user_storage(const std::string& volume_uuid, userid_t user_ PolicyKeyRef ce_ref; if (volume_uuid.empty()) { if (!lookup_key_ref(s_ce_key_raw_refs, user_id, &ce_ref.key_raw_ref)) return false; - get_data_file_encryption_modes(&ce_ref); + get_data_file_encryption_options(&ce_ref); if (!ensure_policy(ce_ref, system_ce_path)) return false; if (!ensure_policy(ce_ref, misc_ce_path)) return false; if (!ensure_policy(ce_ref, vendor_ce_path)) return false; diff --git a/KeyUtil.cpp b/KeyUtil.cpp index c1a82fb..f822377 100644 --- a/KeyUtil.cpp +++ b/KeyUtil.cpp @@ -163,28 +163,80 @@ static bool installKeyLegacy(const KeyBuffer& key, const std::string& raw_ref) { return true; } +// Build a struct fscrypt_key_specifier for use in the key management ioctls. +static bool buildKeySpecifier(fscrypt_key_specifier* spec, const std::string& raw_ref, + int policy_version) { + switch (policy_version) { + case 1: + if (raw_ref.size() != FSCRYPT_KEY_DESCRIPTOR_SIZE) { + LOG(ERROR) << "Invalid key specifier size for v1 encryption policy: " + << raw_ref.size(); + return false; + } + spec->type = FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR; + memcpy(spec->u.descriptor, raw_ref.c_str(), FSCRYPT_KEY_DESCRIPTOR_SIZE); + return true; + case 2: + if (raw_ref.size() != FSCRYPT_KEY_IDENTIFIER_SIZE) { + LOG(ERROR) << "Invalid key specifier size for v2 encryption policy: " + << raw_ref.size(); + return false; + } + spec->type = FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER; + memcpy(spec->u.identifier, raw_ref.c_str(), FSCRYPT_KEY_IDENTIFIER_SIZE); + return true; + default: + LOG(ERROR) << "Invalid encryption policy version: " << policy_version; + return false; + } +} + // Install a file-based encryption key to the kernel, for use by encrypted files -// on the specified filesystem. +// on the specified filesystem using the specified encryption policy version. // -// We use FS_IOC_ADD_ENCRYPTION_KEY if the kernel supports it. Otherwise we add -// the key to the legacy global session keyring. +// For v1 policies, we use FS_IOC_ADD_ENCRYPTION_KEY if the kernel supports it. +// Otherwise we add the key to the legacy global session keyring. +// +// For v2 policies, we always use FS_IOC_ADD_ENCRYPTION_KEY; it's the only way +// the kernel supports. // // Returns %true on success, %false on failure. On success also sets *raw_ref // to the raw key reference for use in the encryption policy. -bool installKey(const KeyBuffer& key, const std::string& mountpoint, std::string* raw_ref) { - *raw_ref = generateKeyRef((const uint8_t*)key.data(), key.size()); - if (!isFsKeyringSupported()) { - return installKeyLegacy(key, *raw_ref); - } - +bool installKey(const KeyBuffer& key, const std::string& mountpoint, int policy_version, + std::string* raw_ref) { // Put the fscrypt_add_key_arg in an automatically-zeroing buffer, since we // have to copy the raw key into it. KeyBuffer arg_buf(sizeof(struct fscrypt_add_key_arg) + key.size(), 0); struct fscrypt_add_key_arg* arg = (struct fscrypt_add_key_arg*)arg_buf.data(); - arg->key_spec.type = FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR; - memcpy(arg->key_spec.u.descriptor, raw_ref.c_str(), FSCRYPT_KEY_DESCRIPTOR_SIZE); + // Initialize the "key specifier", which is like a name for the key. + switch (policy_version) { + case 1: + // A key for a v1 policy is specified by an arbitrary 8-byte + // "descriptor", which must be provided by userspace. We use the + // first 8 bytes from the double SHA-512 of the key itself. + *raw_ref = generateKeyRef((const uint8_t*)key.data(), key.size()); + if (!isFsKeyringSupported()) { + return installKeyLegacy(key, *raw_ref); + } + if (!buildKeySpecifier(&arg->key_spec, *raw_ref, policy_version)) { + return false; + } + break; + case 2: + // A key for a v2 policy is specified by an 16-byte "identifier", + // which is a cryptographic hash of the key itself which the kernel + // computes and returns. Any user-provided value is ignored; we + // just need to set the specifier type to indicate that we're adding + // this type of key. + arg->key_spec.type = FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER; + break; + default: + LOG(ERROR) << "Invalid encryption policy version: " << policy_version; + return false; + } + // Provide the raw key. arg->raw_size = key.size(); memcpy(arg->raw, key.data(), key.size()); @@ -195,11 +247,14 @@ bool installKey(const KeyBuffer& key, const std::string& mountpoint, std::string } if (ioctl(fd, FS_IOC_ADD_ENCRYPTION_KEY, arg) != 0) { - PLOG(ERROR) << "Failed to install fscrypt key with ref " << keyrefstring(*raw_ref) << " to " - << mountpoint; + PLOG(ERROR) << "Failed to install fscrypt key to " << mountpoint; return false; } + if (arg->key_spec.type == FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER) { + // Retrieve the key identifier that the kernel computed. + *raw_ref = std::string((char*)arg->key_spec.u.identifier, FSCRYPT_KEY_IDENTIFIER_SIZE); + } LOG(DEBUG) << "Installed fscrypt key with ref " << keyrefstring(*raw_ref) << " to " << mountpoint; return true; @@ -234,8 +289,8 @@ static bool evictKeyLegacy(const std::string& raw_ref) { // remove the key from the legacy global session keyring. // // In the latter case, the caller is responsible for dropping caches. -bool evictKey(const std::string& mountpoint, const std::string& raw_ref) { - if (!isFsKeyringSupported()) { +bool evictKey(const std::string& mountpoint, const std::string& raw_ref, int policy_version) { + if (policy_version == 1 && !isFsKeyringSupported()) { return evictKeyLegacy(raw_ref); } @@ -248,8 +303,9 @@ bool evictKey(const std::string& mountpoint, const std::string& raw_ref) { struct fscrypt_remove_key_arg arg; memset(&arg, 0, sizeof(arg)); - arg.key_spec.type = FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR; - memcpy(arg.key_spec.u.descriptor, raw_ref.c_str(), FSCRYPT_KEY_DESCRIPTOR_SIZE); + if (!buildKeySpecifier(&arg.key_spec, raw_ref, policy_version)) { + return false; + } std::string ref = keyrefstring(raw_ref); @@ -271,7 +327,8 @@ bool evictKey(const std::string& mountpoint, const std::string& raw_ref) { bool retrieveAndInstallKey(bool create_if_absent, const KeyAuthentication& key_authentication, const std::string& key_path, const std::string& tmp_path, - const std::string& volume_uuid, std::string* key_ref) { + const std::string& volume_uuid, int policy_version, + std::string* key_ref) { KeyBuffer key; if (pathExists(key_path)) { LOG(DEBUG) << "Key exists, using: " << key_path; @@ -286,7 +343,7 @@ bool retrieveAndInstallKey(bool create_if_absent, const KeyAuthentication& key_a if (!storeKeyAtomically(key_path, tmp_path, key_authentication, key)) return false; } - if (!installKey(key, BuildDataPath(volume_uuid), key_ref)) { + if (!installKey(key, BuildDataPath(volume_uuid), policy_version, key_ref)) { LOG(ERROR) << "Failed to install key in " << key_path; return false; } diff --git a/KeyUtil.h b/KeyUtil.h index 146f4d3..f6799d9 100644 --- a/KeyUtil.h +++ b/KeyUtil.h @@ -30,11 +30,13 @@ bool randomKey(KeyBuffer* key); bool isFsKeyringSupported(void); -bool installKey(const KeyBuffer& key, const std::string& mountpoint, std::string* raw_ref); -bool evictKey(const std::string& mountpoint, const std::string& raw_ref); +bool installKey(const KeyBuffer& key, const std::string& mountpoint, int policy_version, + std::string* raw_ref); +bool evictKey(const std::string& mountpoint, const std::string& raw_ref, int policy_version); bool retrieveAndInstallKey(bool create_if_absent, const KeyAuthentication& key_authentication, const std::string& key_path, const std::string& tmp_path, - const std::string& volume_uuid, std::string* key_ref); + const std::string& volume_uuid, int policy_version, + std::string* key_ref); bool retrieveKey(bool create_if_absent, const std::string& key_path, const std::string& tmp_path, KeyBuffer* key, bool keepOld = true);