From bd7492de28963b7e74e8e5d3f17ec9a5a287d9c3 Mon Sep 17 00:00:00 2001 From: Ethan Yonker Date: Wed, 7 Dec 2016 13:55:01 -0600 Subject: Support File Based Encryption Change-Id: Ib688ddd0c32d3999590cacd86b6d9b18eac336e9 --- Android.mk | 8 + crypto/ext4crypt/Android.mk | 33 +++ crypto/ext4crypt/Decrypt.cpp | 160 +++++++++++ crypto/ext4crypt/Decrypt.h | 34 +++ crypto/ext4crypt/Ext4Crypt.cpp | 521 ++++++++++++++++++++++++++++++++++ crypto/ext4crypt/Ext4Crypt.h | 43 +++ crypto/ext4crypt/HashPassword.cpp | 53 ++++ crypto/ext4crypt/HashPassword.h | 24 ++ crypto/ext4crypt/KeyStorage.cpp | 349 +++++++++++++++++++++++ crypto/ext4crypt/KeyStorage.h | 53 ++++ crypto/ext4crypt/Keymaster.cpp | 254 +++++++++++++++++ crypto/ext4crypt/Keymaster.h | 110 +++++++ crypto/ext4crypt/ScryptParameters.cpp | 50 ++++ crypto/ext4crypt/ScryptParameters.h | 32 +++ crypto/ext4crypt/Utils.cpp | 297 +++++++++++++++++++ crypto/ext4crypt/Utils.h | 65 +++++ crypto/ext4crypt/main.cpp | 36 +++ gui/theme/common/languages/en.xml | 1 + partition.cpp | 62 +++- partitionmanager.cpp | 105 ++++--- partitions.hpp | 3 + prebuilt/Android.mk | 12 + variables.h | 1 + 23 files changed, 2271 insertions(+), 35 deletions(-) create mode 100644 crypto/ext4crypt/Android.mk create mode 100644 crypto/ext4crypt/Decrypt.cpp create mode 100644 crypto/ext4crypt/Decrypt.h create mode 100644 crypto/ext4crypt/Ext4Crypt.cpp create mode 100644 crypto/ext4crypt/Ext4Crypt.h create mode 100644 crypto/ext4crypt/HashPassword.cpp create mode 100644 crypto/ext4crypt/HashPassword.h create mode 100644 crypto/ext4crypt/KeyStorage.cpp create mode 100644 crypto/ext4crypt/KeyStorage.h create mode 100644 crypto/ext4crypt/Keymaster.cpp create mode 100644 crypto/ext4crypt/Keymaster.h create mode 100644 crypto/ext4crypt/ScryptParameters.cpp create mode 100644 crypto/ext4crypt/ScryptParameters.h create mode 100644 crypto/ext4crypt/Utils.cpp create mode 100644 crypto/ext4crypt/Utils.h create mode 100644 crypto/ext4crypt/main.cpp diff --git a/Android.mk b/Android.mk index 467ce6187..582b710ea 100644 --- a/Android.mk +++ b/Android.mk @@ -296,6 +296,11 @@ ifeq ($(TW_INCLUDE_CRYPTO), true) LOCAL_CFLAGS += -DTW_INCLUDE_CRYPTO LOCAL_SHARED_LIBRARIES += libcryptfslollipop libgpt_twrp LOCAL_C_INCLUDES += external/boringssl/src/include + ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 24; echo $$?),0) + TW_INCLUDE_CRYPTO_FBE := true + LOCAL_CFLAGS += -DTW_INCLUDE_FBE + LOCAL_SHARED_LIBRARIES += libe4crypt + endif endif ifeq ($(TW_USE_MODEL_HARDWARE_ID_FOR_DEVICE_ID), true) LOCAL_CFLAGS += -DTW_USE_MODEL_HARDWARE_ID_FOR_DEVICE_ID @@ -669,6 +674,9 @@ endif ifeq ($(TW_INCLUDE_CRYPTO), true) include $(commands_recovery_local_path)/crypto/lollipop/Android.mk include $(commands_recovery_local_path)/crypto/scrypt/Android.mk + ifeq ($(TW_INCLUDE_CRYPTO_FBE), true) + include $(commands_recovery_local_path)/crypto/ext4crypt/Android.mk + endif include $(commands_recovery_local_path)/gpt/Android.mk endif ifeq ($(BUILD_ID), GINGERBREAD) diff --git a/crypto/ext4crypt/Android.mk b/crypto/ext4crypt/Android.mk new file mode 100644 index 000000000..531aca199 --- /dev/null +++ b/crypto/ext4crypt/Android.mk @@ -0,0 +1,33 @@ +LOCAL_PATH := $(call my-dir) +ifeq ($(TW_INCLUDE_CRYPTO), true) +include $(CLEAR_VARS) + +LOCAL_MODULE := libe4crypt +LOCAL_MODULE_TAGS := eng optional +LOCAL_CFLAGS := +LOCAL_SRC_FILES := Decrypt.cpp Ext4Crypt.cpp Keymaster.cpp KeyStorage.cpp ScryptParameters.cpp Utils.cpp HashPassword.cpp +LOCAL_SHARED_LIBRARIES := libselinux libc libc++ libext4_utils libsoftkeymaster libbase libcrypto libcutils libkeymaster_messages libhardware libprotobuf-cpp-lite +LOCAL_STATIC_LIBRARIES := libscrypt_static +LOCAL_C_INCLUDES := system/extras/ext4_utils external/scrypt/lib/crypto system/security/keystore hardware/libhardware/include/hardware system/security/softkeymaster/include/keymaster system/keymaster/include + +ifneq ($(wildcard hardware/libhardware/include/hardware/keymaster0.h),) + LOCAL_CFLAGS += -DTW_CRYPTO_HAVE_KEYMASTERX + LOCAL_C_INCLUDES += external/boringssl/src/include +endif + +include $(BUILD_SHARED_LIBRARY) + + + +include $(CLEAR_VARS) +LOCAL_MODULE := twrpfbe +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +LOCAL_SRC_FILES := main.cpp +LOCAL_SHARED_LIBRARIES := libe4crypt +#LOCAL_LDFLAGS += -Wl,-dynamic-linker,/sbin/linker64 + +include $(BUILD_EXECUTABLE) + +endif diff --git a/crypto/ext4crypt/Decrypt.cpp b/crypto/ext4crypt/Decrypt.cpp new file mode 100644 index 000000000..6c47add0c --- /dev/null +++ b/crypto/ext4crypt/Decrypt.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2016 The Team Win Recovery Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Decrypt.h" +#include "Ext4Crypt.h" + +#include + +#include +#include +#include +#include + +#include "ext4_crypt.h" +#include "key_control.h" + +#include +#include "HashPassword.h" + +#include + +int gatekeeper_device_initialize(gatekeeper_device_t **dev) { + int ret; + const hw_module_t *mod; + ret = hw_get_module_by_class(GATEKEEPER_HARDWARE_MODULE_ID, NULL, &mod); + + if (ret!=0) { + printf("failed to get hw module\n"); + return ret; + } + + ret = gatekeeper_open(mod, dev); + + if (ret!=0) + printf("failed to open gatekeeper\n"); + return ret; +} + +int Get_Password_Type(const userid_t user_id, std::string& filename) { + std::string path; + if (user_id == 0) { + path = "/data/system/"; + } else { + char user_id_str[5]; + sprintf(user_id_str, "%i", user_id); + path = "/data/system/users/"; + path += user_id_str; + path += "/"; + } + filename = path + "gatekeeper.password.key"; + struct stat st; + if (stat(filename.c_str(), &st) == 0 && st.st_size > 0) + return 1; + filename = path + "gatekeeper.pattern.key"; + if (stat(filename.c_str(), &st) == 0 && st.st_size > 0) + return 2; + printf("Unable to locate gatekeeper password file '%s'\n", filename.c_str()); + filename = ""; + return 0; +} + +bool Decrypt_DE() { + if (!e4crypt_initialize_global_de()) { // this deals with the overarching device encryption + printf("e4crypt_initialize_global_de returned fail\n"); + return false; + } + if (!e4crypt_init_user0()) { + printf("e4crypt_init_user0 returned fail\n"); + return false; + } + return true; +} + +bool Decrypt_User(const userid_t user_id, const std::string& Password) { + uint8_t *auth_token; + uint32_t auth_token_len; + int ret; + + struct stat st; + if (user_id > 9999) { + printf("user_id is too big\n"); + return false; + } + std::string filename; + bool Default_Password = (Password == "!"); + if (Get_Password_Type(user_id, filename) == 0 && !Default_Password) { + printf("Unknown password type\n"); + return false; + } + int flags = FLAG_STORAGE_DE; + if (user_id == 0) + flags = FLAG_STORAGE_DE; + else + flags = FLAG_STORAGE_CE; + gatekeeper_device_t *device; + ret = gatekeeper_device_initialize(&device); + if (Default_Password) { + if (!e4crypt_unlock_user_key(user_id, 0, "!", "!")) { + printf("e4crypt_unlock_user_key returned fail\n"); + return false; + } + if (!e4crypt_prepare_user_storage(nullptr, user_id, 0, flags)) { + printf("failed to e4crypt_prepare_user_storage\n"); + return false; + } + printf("Decrypted Successfully!\n"); + return true; + } + if (ret!=0) + return false; + printf("password filename is '%s'\n", filename.c_str()); + if (stat(filename.c_str(), &st) != 0) { + printf("error stat'ing key file: %s\n", strerror(errno)); + return false; + } + std::string handle; + if (!android::base::ReadFileToString(filename, &handle)) { + printf("Failed to read '%s'\n", filename.c_str()); + return false; + } + bool should_reenroll; + ret = device->verify(device, user_id, 0, (const uint8_t *)handle.c_str(), st.st_size, + (const uint8_t *)Password.c_str(), (uint32_t)Password.size(), &auth_token, &auth_token_len, + &should_reenroll); + if (ret !=0) { + printf("failed to verify\n"); + return false; + } + char token_hex[(auth_token_len*2)+1]; + token_hex[(auth_token_len*2)] = 0; + uint32_t i; + for (i=0;i +#include + +#include + +#include + +__BEGIN_DECLS + +// NOTE: keep in sync with StorageManager +static constexpr int FLAG_STORAGE_DE = 1 << 0; +static constexpr int FLAG_STORAGE_CE = 1 << 1; + +int Get_Password_Type(const userid_t user_id, std::string& filename); +bool Decrypt_DE(); +bool Decrypt_User(const userid_t user_id, const std::string& Password); + +__END_DECLS diff --git a/crypto/ext4crypt/Ext4Crypt.cpp b/crypto/ext4crypt/Ext4Crypt.cpp new file mode 100644 index 000000000..423147d66 --- /dev/null +++ b/crypto/ext4crypt/Ext4Crypt.cpp @@ -0,0 +1,521 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Ext4Crypt.h" +#include "Decrypt.h" + +#include "KeyStorage.h" +#include "Utils.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ext4_crypt.h" +#include "key_control.h" + +#include +#include "HashPassword.h" + +#define EMULATED_USES_SELINUX 0 +#define MANAGE_MISC_DIRS 0 + +#include + +#include +//#include +#include + +#define LOG(x) std::cout +#define PLOG(x) std::cout +#define DATA_MNT_POINT "/data" + +using android::base::StringPrintf; +using android::vold::kEmptyAuthentication; + +// NOTE: keep in sync with StorageManager +//static constexpr int FLAG_STORAGE_DE = 1 << 0; // moved to Decrypt.h +//static constexpr int FLAG_STORAGE_CE = 1 << 1; + +namespace { +const std::string device_key_dir = std::string() + DATA_MNT_POINT + e4crypt_unencrypted_folder; +const std::string device_key_path = device_key_dir + "/key"; +const std::string device_key_temp = device_key_dir + "/temp"; + +const std::string user_key_dir = std::string() + DATA_MNT_POINT + "/misc/vold/user_keys"; +const std::string user_key_temp = user_key_dir + "/temp"; + +bool s_global_de_initialized = false; + +// Some users are ephemeral, don't try to wipe their keys from disk +std::set s_ephemeral_users; + +// Map user ids to key references +std::map s_de_key_raw_refs; +std::map s_ce_key_raw_refs; +// TODO abolish this map. Keys should not be long-lived in user memory, only kernel memory. +// See b/26948053 +std::map s_ce_keys; + +// ext4enc:TODO get this const from somewhere good +const int EXT4_KEY_DESCRIPTOR_SIZE = 8; + +// ext4enc:TODO Include structure from somewhere sensible +// MUST be in sync with ext4_crypto.c in kernel +constexpr int EXT4_ENCRYPTION_MODE_AES_256_XTS = 1; +constexpr int EXT4_AES_256_XTS_KEY_SIZE = 64; +constexpr int EXT4_MAX_KEY_SIZE = 64; +struct ext4_encryption_key { + uint32_t mode; + char raw[EXT4_MAX_KEY_SIZE]; + uint32_t size; +}; +} + +static bool e4crypt_is_emulated() { + return false; //property_get_bool("persist.sys.emulate_fbe", false); +} + +static const char* escape_null(const char* value) { + return (value == nullptr) ? "null" : value; +} + +// Get raw keyref - used to make keyname and to pass to ioctl +static std::string generate_key_ref(const char* key, int length) { + SHA512_CTX c; + + SHA512_Init(&c); + SHA512_Update(&c, key, length); + unsigned char key_ref1[SHA512_DIGEST_LENGTH]; + SHA512_Final(key_ref1, &c); + + SHA512_Init(&c); + SHA512_Update(&c, key_ref1, SHA512_DIGEST_LENGTH); + unsigned char key_ref2[SHA512_DIGEST_LENGTH]; + SHA512_Final(key_ref2, &c); + + static_assert(EXT4_KEY_DESCRIPTOR_SIZE <= SHA512_DIGEST_LENGTH, + "Hash too short for descriptor"); + return std::string((char*)key_ref2, EXT4_KEY_DESCRIPTOR_SIZE); +} + +static bool fill_key(const std::string& key, ext4_encryption_key* ext4_key) { + if (key.size() != EXT4_AES_256_XTS_KEY_SIZE) { + LOG(ERROR) << "Wrong size key " << key.size(); + return false; + } + static_assert(EXT4_AES_256_XTS_KEY_SIZE <= sizeof(ext4_key->raw), "Key too long!"); + ext4_key->mode = EXT4_ENCRYPTION_MODE_AES_256_XTS; + ext4_key->size = key.size(); + memset(ext4_key->raw, 0, sizeof(ext4_key->raw)); + memcpy(ext4_key->raw, key.data(), key.size()); + return true; +} + +static std::string keyname(const std::string& raw_ref) { + std::ostringstream o; + o << "ext4:"; + for (auto i : raw_ref) { + o << std::hex << std::setw(2) << std::setfill('0') << (int)i; + } + LOG(INFO) << "keyname is " << o.str() << "\n"; + return o.str(); +} + +// Get the keyring we store all keys in +static bool e4crypt_keyring(key_serial_t* device_keyring) { + *device_keyring = keyctl_search(KEY_SPEC_SESSION_KEYRING, "keyring", "e4crypt", 0); + if (*device_keyring == -1) { + PLOG(ERROR) << "Unable to find device keyring\n"; + return false; + } + return true; +} + +// Install password into global keyring +// Return raw key reference for use in policy +static bool install_key(const std::string& key, std::string* raw_ref) { + ext4_encryption_key ext4_key; + if (!fill_key(key, &ext4_key)) return false; + *raw_ref = generate_key_ref(ext4_key.raw, ext4_key.size); + auto ref = keyname(*raw_ref); + key_serial_t device_keyring; + if (!e4crypt_keyring(&device_keyring)) return false; + key_serial_t key_id = + add_key("logon", ref.c_str(), (void*)&ext4_key, sizeof(ext4_key), device_keyring); + if (key_id == -1) { + PLOG(ERROR) << "Failed to insert key into keyring " << device_keyring << "\n"; + return false; + } + LOG(DEBUG) << "Added key " << key_id << " (" << ref << ") to keyring " << device_keyring + << " in process " << getpid() << "\n"; + return true; +} + +static std::string get_de_key_path(userid_t user_id) { +LOG(INFO) << "get_de_key_path " << user_id << " " << StringPrintf("%s/de/%d", user_key_dir.c_str(), user_id) << "\n"; + return StringPrintf("%s/de/%d", user_key_dir.c_str(), user_id); +} + +static std::string get_ce_key_directory_path(userid_t user_id) { +LOG(INFO) << "get_ce_key_directory_path " << user_id << ": " << StringPrintf("%s/ce/%d", user_key_dir.c_str(), user_id) << "\n"; + return StringPrintf("%s/ce/%d", user_key_dir.c_str(), user_id); +} + +// Returns the keys newest first +static std::vector get_ce_key_paths(const std::string& directory_path) { + auto dirp = std::unique_ptr(opendir(directory_path.c_str()), closedir); + if (!dirp) { + PLOG(ERROR) << "Unable to open ce key directory: " + directory_path; + return std::vector(); + } + std::vector result; + for (;;) { + errno = 0; + auto const entry = readdir(dirp.get()); + if (!entry) { + if (errno) { + PLOG(ERROR) << "Unable to read ce key directory: " + directory_path; + return std::vector(); + } + break; + } + if (entry->d_type != DT_DIR || entry->d_name[0] != 'c') { + LOG(DEBUG) << "Skipping non-key " << entry->d_name; + continue; + } + result.emplace_back(directory_path + "/" + entry->d_name); + LOG(INFO) << "get_ce_key_paths adding: " << directory_path + "/" + entry->d_name << "\n"; + } + std::sort(result.begin(), result.end()); + std::reverse(result.begin(), result.end()); + return result; +} + +static std::string get_ce_key_current_path(const std::string& directory_path) { +LOG(INFO) << "get_ce_key_current_path: " << directory_path + "/current\n"; + return directory_path + "/current"; +} + +// Discard all keys but the named one; rename it to canonical name. +// No point in acting on errors in this; ignore them. +static void fixate_user_ce_key(const std::string& directory_path, const std::string &to_fix, + const std::vector& paths) { + for (auto const other_path: paths) { + if (other_path != to_fix) { + android::vold::destroyKey(other_path); + } + } + auto const current_path = get_ce_key_current_path(directory_path); + if (to_fix != current_path) { + LOG(DEBUG) << "Renaming " << to_fix << " to " << current_path; + if (rename(to_fix.c_str(), current_path.c_str()) != 0) { + PLOG(WARNING) << "Unable to rename " << to_fix << " to " << current_path; + } + } +} + +static bool read_and_fixate_user_ce_key(userid_t user_id, + const android::vold::KeyAuthentication& auth, + std::string *ce_key) { + auto const directory_path = get_ce_key_directory_path(user_id); + auto const paths = get_ce_key_paths(directory_path); + for (auto const ce_key_path: paths) { + LOG(DEBUG) << "Trying user CE key " << ce_key_path; + if (android::vold::retrieveKey(ce_key_path, auth, ce_key)) { + LOG(DEBUG) << "Successfully retrieved key"; + fixate_user_ce_key(directory_path, ce_key_path, paths); + return true; + } + } + LOG(ERROR) << "Failed to find working ce key for user " << user_id; + return false; +} + +static bool read_and_install_user_ce_key(userid_t user_id, + const android::vold::KeyAuthentication& auth) { + if (s_ce_key_raw_refs.count(user_id) != 0) return true; + std::string ce_key; + if (!read_and_fixate_user_ce_key(user_id, auth, &ce_key)) return false; + std::string ce_raw_ref; + if (!install_key(ce_key, &ce_raw_ref)) return false; + s_ce_keys[user_id] = ce_key; + s_ce_key_raw_refs[user_id] = ce_raw_ref; + LOG(DEBUG) << "Installed ce key for user " << user_id; + return true; +} + +static bool prepare_dir(const std::string& dir, mode_t mode, uid_t uid, gid_t gid) { + LOG(DEBUG) << "Preparing: " << dir << "\n"; + return true; + return access(dir.c_str(), F_OK) == 0; // we don't want recovery creating directories or changing permissions at this point, so we will just return true if the path already exists + if (fs_prepare_dir(dir.c_str(), mode, uid, gid) != 0) { + PLOG(ERROR) << "Failed to prepare " << dir; + return false; + } + return true; +} + +static bool path_exists(const std::string& path) { + return access(path.c_str(), F_OK) == 0; +} + +static bool lookup_key_ref(const std::map& key_map, userid_t user_id, + std::string* raw_ref) { + auto refi = key_map.find(user_id); + if (refi == key_map.end()) { + LOG(ERROR) << "Cannot find key for " << user_id; + return false; + } + *raw_ref = refi->second; + return true; +} + +static bool ensure_policy(const std::string& raw_ref, const std::string& path) { + LOG(INFO) << "ensure_policy '" << path << "'\n"; + return true; + return access(path.c_str(), F_OK) == 0; // ensure policy will set a policy if one is not set on an empty folder - we don't want to do this in recovery + /*if (e4crypt_policy_ensure(path.c_str(), raw_ref.data(), raw_ref.size()) != 0) { + LOG(ERROR) << "Failed to set policy on: " << path << "\n"; + return false; + } + return true;*/ +} + +static bool is_numeric(const char* name) { + for (const char* p = name; *p != '\0'; p++) { + if (!isdigit(*p)) return false; + } + return true; +} + +static bool load_all_de_keys() { + auto de_dir = user_key_dir + "/de"; + auto dirp = std::unique_ptr(opendir(de_dir.c_str()), closedir); + if (!dirp) { + PLOG(ERROR) << "Unable to read de key directory"; + return false; + } + for (;;) { + errno = 0; + auto entry = readdir(dirp.get()); + if (!entry) { + if (errno) { + PLOG(ERROR) << "Unable to read de key directory"; + return false; + } + break; + } + if (entry->d_type != DT_DIR || !is_numeric(entry->d_name)) { + LOG(DEBUG) << "Skipping non-de-key " << entry->d_name; + continue; + } + userid_t user_id = atoi(entry->d_name); + if (s_de_key_raw_refs.count(user_id) == 0) { + auto key_path = de_dir + "/" + entry->d_name; + std::string key; + if (!android::vold::retrieveKey(key_path, kEmptyAuthentication, &key)) return false; + std::string raw_ref; + if (!install_key(key, &raw_ref)) return false; + s_de_key_raw_refs[user_id] = raw_ref; + LOG(DEBUG) << "Installed de key for user " << user_id; + } + } + // ext4enc:TODO: go through all DE directories, ensure that all user dirs have the + // correct policy set on them, and that no rogue ones exist. + return true; +} + +bool e4crypt_initialize_global_de() { + + if (s_global_de_initialized) { + LOG(INFO) << "Already initialized\n"; + return true; + } + + std::string device_key; + if (path_exists(device_key_path)) { + if (!android::vold::retrieveKey(device_key_path, + kEmptyAuthentication, &device_key)) return false; + } else { + LOG(INFO) << "NOT Creating new key\n"; + return false; + } + + std::string device_key_ref; + if (!install_key(device_key, &device_key_ref)) { + LOG(ERROR) << "Failed to install device key\n"; + return false; + } + + s_global_de_initialized = true; + return true; +} + +bool e4crypt_init_user0() { + if (e4crypt_is_native()) { + if (!prepare_dir(user_key_dir, 0700, AID_ROOT, AID_ROOT)) return false; + if (!prepare_dir(user_key_dir + "/ce", 0700, AID_ROOT, AID_ROOT)) return false; + if (!prepare_dir(user_key_dir + "/de", 0700, AID_ROOT, AID_ROOT)) return false; + if (!path_exists(get_de_key_path(0))) { + //if (!create_and_install_user_keys(0, false)) return false; + printf("de key path not found\n"); + return false; + } + // TODO: switch to loading only DE_0 here once framework makes + // explicit calls to install DE keys for secondary users + if (!load_all_de_keys()) return false; + } + // We can only safely prepare DE storage here, since CE keys are probably + // entangled with user credentials. The framework will always prepare CE + // storage once CE keys are installed. + if (!e4crypt_prepare_user_storage(nullptr, 0, 0, FLAG_STORAGE_DE)) { + LOG(ERROR) << "Failed to prepare user 0 storage"; + return false; + } + + // If this is a non-FBE device that recently left an emulated mode, + // restore user data directories to known-good state. + if (!e4crypt_is_native() && !e4crypt_is_emulated()) { + e4crypt_unlock_user_key(0, 0, "!", "!"); + } + + return true; +} + +static bool parse_hex(const char* hex, std::string* result) { + if (strcmp("!", hex) == 0) { + *result = ""; + return true; + } + if (android::vold::HexToStr(hex, *result) != 0) { + LOG(ERROR) << "Invalid FBE hex string"; // Don't log the string for security reasons + return false; + } + return true; +} + +// TODO: rename to 'install' for consistency, and take flags to know which keys to install +bool e4crypt_unlock_user_key(userid_t user_id, int serial, const char* token_hex, + const char* secret_hex) { + if (e4crypt_is_native()) { + if (s_ce_key_raw_refs.count(user_id) != 0) { + LOG(WARNING) << "Tried to unlock already-unlocked key for user " << user_id; + return true; + } + std::string token, secret; + if (!parse_hex(token_hex, &token)) return false; + if (!parse_hex(secret_hex, &secret)) return false; + android::vold::KeyAuthentication auth(token, secret); + if (!read_and_install_user_ce_key(user_id, auth)) { + LOG(ERROR) << "Couldn't read key for " << user_id; + return false; + } + } else { + printf("Emulation mode not supported in TWRP\n"); + // When in emulation mode, we just use chmod. However, we also + // unlock directories when not in emulation mode, to bring devices + // back into a known-good state. + /*if (!emulated_unlock(android::vold::BuildDataSystemCePath(user_id), 0771) || + !emulated_unlock(android::vold::BuildDataMiscCePath(user_id), 01771) || + !emulated_unlock(android::vold::BuildDataMediaCePath(nullptr, user_id), 0770) || + !emulated_unlock(android::vold::BuildDataUserCePath(nullptr, user_id), 0771)) { + LOG(ERROR) << "Failed to unlock user " << user_id; + return false; + }*/ + } + return true; +} + +bool e4crypt_prepare_user_storage(const char* volume_uuid, userid_t user_id, int serial, + int flags) { + + if (flags & FLAG_STORAGE_DE) { + // DE_sys key + auto system_legacy_path = android::vold::BuildDataSystemLegacyPath(user_id); + auto misc_legacy_path = android::vold::BuildDataMiscLegacyPath(user_id); + auto profiles_de_path = android::vold::BuildDataProfilesDePath(user_id); + auto foreign_de_path = android::vold::BuildDataProfilesForeignDexDePath(user_id); + + // DE_n key + auto system_de_path = android::vold::BuildDataSystemDePath(user_id); + auto misc_de_path = android::vold::BuildDataMiscDePath(user_id); + auto user_de_path = android::vold::BuildDataUserDePath(volume_uuid, user_id); + + if (!prepare_dir(system_legacy_path, 0700, AID_SYSTEM, AID_SYSTEM)) return false; +#if MANAGE_MISC_DIRS + if (!prepare_dir(misc_legacy_path, 0750, multiuser_get_uid(user_id, AID_SYSTEM), + multiuser_get_uid(user_id, AID_EVERYBODY))) return false; +#endif + if (!prepare_dir(profiles_de_path, 0771, AID_SYSTEM, AID_SYSTEM)) return false; + if (!prepare_dir(foreign_de_path, 0773, AID_SYSTEM, AID_SYSTEM)) return false; + + if (!prepare_dir(system_de_path, 0770, AID_SYSTEM, AID_SYSTEM)) return false; + if (!prepare_dir(misc_de_path, 01771, AID_SYSTEM, AID_MISC)) return false; + if (!prepare_dir(user_de_path, 0771, AID_SYSTEM, AID_SYSTEM)) return false; + + // For now, FBE is only supported on internal storage + if (e4crypt_is_native() && volume_uuid == nullptr) { + std::string de_raw_ref; + if (!lookup_key_ref(s_de_key_raw_refs, user_id, &de_raw_ref)) return false; + if (!ensure_policy(de_raw_ref, system_de_path)) return false; + if (!ensure_policy(de_raw_ref, misc_de_path)) return false; + if (!ensure_policy(de_raw_ref, user_de_path)) return false; + } + } + + if (flags & FLAG_STORAGE_CE) { + // CE_n key + auto system_ce_path = android::vold::BuildDataSystemCePath(user_id); + auto misc_ce_path = android::vold::BuildDataMiscCePath(user_id); + auto media_ce_path = android::vold::BuildDataMediaCePath(volume_uuid, user_id); + auto user_ce_path = android::vold::BuildDataUserCePath(volume_uuid, user_id); + + if (!prepare_dir(system_ce_path, 0770, AID_SYSTEM, AID_SYSTEM)) return false; + if (!prepare_dir(misc_ce_path, 01771, AID_SYSTEM, AID_MISC)) return false; + if (!prepare_dir(media_ce_path, 0770, AID_MEDIA_RW, AID_MEDIA_RW)) return false; + if (!prepare_dir(user_ce_path, 0771, AID_SYSTEM, AID_SYSTEM)) return false; + + // For now, FBE is only supported on internal storage + if (e4crypt_is_native() && volume_uuid == nullptr) { + std::string ce_raw_ref; + if (!lookup_key_ref(s_ce_key_raw_refs, user_id, &ce_raw_ref)) return false; + if (!ensure_policy(ce_raw_ref, system_ce_path)) return false; + if (!ensure_policy(ce_raw_ref, misc_ce_path)) return false; + if (!ensure_policy(ce_raw_ref, media_ce_path)) return false; + if (!ensure_policy(ce_raw_ref, user_ce_path)) return false; + } + } + + return true; +} diff --git a/crypto/ext4crypt/Ext4Crypt.h b/crypto/ext4crypt/Ext4Crypt.h new file mode 100644 index 000000000..b05ff4ddb --- /dev/null +++ b/crypto/ext4crypt/Ext4Crypt.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include + +#include + +__BEGIN_DECLS + +// General functions +bool e4crypt_is_native(); +bool e4crypt_initialize_global_de(); + +bool e4crypt_init_user0(); +//bool e4crypt_vold_create_user_key(userid_t user_id, int serial, bool ephemeral); +//bool e4crypt_destroy_user_key(userid_t user_id); +//bool e4crypt_add_user_key_auth(userid_t user_id, int serial, const char* token, +// const char* secret); +//bool e4crypt_fixate_newest_user_key_auth(userid_t user_id); + +bool e4crypt_unlock_user_key(userid_t user_id, int serial, const char* token, const char* secret); +//bool e4crypt_lock_user_key(userid_t user_id); + +bool e4crypt_prepare_user_storage(const char* volume_uuid, userid_t user_id, int serial, int flags); +//bool e4crypt_destroy_user_storage(const char* volume_uuid, userid_t user_id, int flags); + +__END_DECLS diff --git a/crypto/ext4crypt/HashPassword.cpp b/crypto/ext4crypt/HashPassword.cpp new file mode 100644 index 000000000..86e067ebb --- /dev/null +++ b/crypto/ext4crypt/HashPassword.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2016 Team Win Recovery Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * This computes the "secret" used by Android as one of the parameters + * to decrypt File Based Encryption. The secret is prefixed with + * "Android FBE credential hash" padded with 0s to 128 bytes then the + * user's password is appended to the end of the 128 bytes. This string + * is then hashed with sha512 and the sha512 value is then converted to + * hex with upper-case characters. + */ + +#include +#include +#include +#include + +#define PASS_PADDING_SIZE 128 +#define SHA512_HEX_SIZE SHA512_DIGEST_LENGTH * 2 + +std::string HashPassword(const std::string& Password) { + size_t size = PASS_PADDING_SIZE + Password.size(); + unsigned char* buffer = (unsigned char*)calloc(1, size); + const char* prefix = "Android FBE credential hash"; + memcpy((void*)buffer, (void*)prefix, strlen(prefix)); + unsigned char* ptr = buffer + PASS_PADDING_SIZE; + memcpy((void*)ptr, Password.c_str(), Password.size()); + unsigned char hash[SHA512_DIGEST_LENGTH]; + SHA512_CTX sha512; + SHA512_Init(&sha512); + SHA512_Update(&sha512, buffer, size); + SHA512_Final(hash, &sha512); + int index = 0; + char hex_hash[SHA512_HEX_SIZE + 1]; + for(index = 0; index < SHA512_DIGEST_LENGTH; index++) + sprintf(hex_hash + (index * 2), "%02X", hash[index]); + hex_hash[128] = 0; + std::string ret = hex_hash; + return ret; +} diff --git a/crypto/ext4crypt/HashPassword.h b/crypto/ext4crypt/HashPassword.h new file mode 100644 index 000000000..d9b5ce5f1 --- /dev/null +++ b/crypto/ext4crypt/HashPassword.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2016 Team Win Recovery Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HASH_PASSWORD_H +#define __HASH_PASSWORD_H + +#include + +std::string HashPassword(const std::string& Password); + +#endif diff --git a/crypto/ext4crypt/KeyStorage.cpp b/crypto/ext4crypt/KeyStorage.cpp new file mode 100644 index 000000000..199520e9d --- /dev/null +++ b/crypto/ext4crypt/KeyStorage.cpp @@ -0,0 +1,349 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "KeyStorage.h" + +#include "Keymaster.h" +#include "ScryptParameters.h" +#include "Utils.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +//#include + +#include + +#include + +#include + +extern "C" { + +#include "crypto_scrypt.h" +} + +#define ERROR 1 +#define LOG(x) std::cout +#define PLOG(x) std::cout + +namespace android { +namespace vold { + +const KeyAuthentication kEmptyAuthentication{"", ""}; + +static constexpr size_t AES_KEY_BYTES = 32; +static constexpr size_t GCM_NONCE_BYTES = 12; +static constexpr size_t GCM_MAC_BYTES = 16; +static constexpr size_t SALT_BYTES = 1 << 4; +static constexpr size_t SECDISCARDABLE_BYTES = 1 << 14; +static constexpr size_t STRETCHED_BYTES = 1 << 6; + +static constexpr uint32_t AUTH_TIMEOUT = 30; // Seconds + +static const char* kCurrentVersion = "1"; +static const char* kRmPath = "/system/bin/rm"; +static const char* kSecdiscardPath = "/system/bin/secdiscard"; +static const char* kStretch_none = "none"; +static const char* kStretch_nopassword = "nopassword"; +static const std::string kStretchPrefix_scrypt = "scrypt "; +static const char* kFn_encrypted_key = "encrypted_key"; +static const char* kFn_keymaster_key_blob = "keymaster_key_blob"; +static const char* kFn_salt = "salt"; +static const char* kFn_secdiscardable = "secdiscardable"; +static const char* kFn_stretching = "stretching"; +static const char* kFn_version = "version"; + +static bool checkSize(const std::string& kind, size_t actual, size_t expected) { + if (actual != expected) { + LOG(ERROR) << "Wrong number of bytes in " << kind << ", expected " << expected << " got " + << actual; + return false; + } + return true; +} + +static std::string hashSecdiscardable(const std::string& secdiscardable) { + SHA512_CTX c; + + SHA512_Init(&c); + // Personalise the hashing by introducing a fixed prefix. + // Hashing applications should use personalization except when there is a + // specific reason not to; see section 4.11 of https://www.schneier.com/skein1.3.pdf + std::string secdiscardableHashingPrefix = "Android secdiscardable SHA512"; + secdiscardableHashingPrefix.resize(SHA512_CBLOCK); + SHA512_Update(&c, secdiscardableHashingPrefix.data(), secdiscardableHashingPrefix.size()); + SHA512_Update(&c, secdiscardable.data(), secdiscardable.size()); + std::string res(SHA512_DIGEST_LENGTH, '\0'); + SHA512_Final(reinterpret_cast(&res[0]), &c); + return res; +} + +/*static bool generateKeymasterKey(Keymaster& keymaster, const KeyAuthentication& auth, + const std::string& appId, std::string* key) { + auto paramBuilder = keymaster::AuthorizationSetBuilder() + .AesEncryptionKey(AES_KEY_BYTES * 8) + .Authorization(keymaster::TAG_BLOCK_MODE, KM_MODE_GCM) + .Authorization(keymaster::TAG_MIN_MAC_LENGTH, GCM_MAC_BYTES * 8) + .Authorization(keymaster::TAG_PADDING, KM_PAD_NONE); + addStringParam(¶mBuilder, keymaster::TAG_APPLICATION_ID, appId); + if (auth.token.empty()) { + LOG(DEBUG) << "Creating key that doesn't need auth token"; + paramBuilder.Authorization(keymaster::TAG_NO_AUTH_REQUIRED); + } else { + LOG(DEBUG) << "Auth token required for key"; + if (auth.token.size() != sizeof(hw_auth_token_t)) { + LOG(ERROR) << "Auth token should be " << sizeof(hw_auth_token_t) << " bytes, was " + << auth.token.size() << " bytes"; + return false; + } + const hw_auth_token_t* at = reinterpret_cast(auth.token.data()); + paramBuilder.Authorization(keymaster::TAG_USER_SECURE_ID, at->user_id); + paramBuilder.Authorization(keymaster::TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD); + paramBuilder.Authorization(keymaster::TAG_AUTH_TIMEOUT, AUTH_TIMEOUT); + } + return keymaster.generateKey(paramBuilder.build(), key); +}*/ + +static keymaster::AuthorizationSetBuilder beginParams(const KeyAuthentication& auth, + const std::string& appId) { + auto paramBuilder = keymaster::AuthorizationSetBuilder() + .Authorization(keymaster::TAG_BLOCK_MODE, KM_MODE_GCM) + .Authorization(keymaster::TAG_MAC_LENGTH, GCM_MAC_BYTES * 8) + .Authorization(keymaster::TAG_PADDING, KM_PAD_NONE); + addStringParam(¶mBuilder, keymaster::TAG_APPLICATION_ID, appId); + if (!auth.token.empty()) { + LOG(DEBUG) << "Supplying auth token to Keymaster"; + addStringParam(¶mBuilder, keymaster::TAG_AUTH_TOKEN, auth.token); + } + return paramBuilder; +} + +/*static bool encryptWithKeymasterKey(Keymaster& keymaster, const std::string& key, + const KeyAuthentication& auth, const std::string& appId, + const std::string& message, std::string* ciphertext) { + auto params = beginParams(auth, appId).build(); + keymaster::AuthorizationSet outParams; + auto opHandle = keymaster.begin(KM_PURPOSE_ENCRYPT, key, params, &outParams); + if (!opHandle) return false; + keymaster_blob_t nonceBlob; + if (!outParams.GetTagValue(keymaster::TAG_NONCE, &nonceBlob)) { + LOG(ERROR) << "GCM encryption but no nonce generated"; + return false; + } + // nonceBlob here is just a pointer into existing data, must not be freed + std::string nonce(reinterpret_cast(nonceBlob.data), nonceBlob.data_length); + if (!checkSize("nonce", nonce.size(), GCM_NONCE_BYTES)) return false; + std::string body; + if (!opHandle.updateCompletely(message, &body)) return false; + + std::string mac; + if (!opHandle.finishWithOutput(&mac)) return false; + if (!checkSize("mac", mac.size(), GCM_MAC_BYTES)) return false; + *ciphertext = nonce + body + mac; + return true; +}*/ + +static bool decryptWithKeymasterKey(Keymaster& keymaster, const std::string& key, + const KeyAuthentication& auth, const std::string& appId, + const std::string& ciphertext, std::string* message) { + auto nonce = ciphertext.substr(0, GCM_NONCE_BYTES); + auto bodyAndMac = ciphertext.substr(GCM_NONCE_BYTES); + auto params = addStringParam(beginParams(auth, appId), keymaster::TAG_NONCE, nonce).build(); + auto opHandle = keymaster.begin(KM_PURPOSE_DECRYPT, key, params); + if (!opHandle) return false; + if (!opHandle.updateCompletely(bodyAndMac, message)) return false; + if (!opHandle.finish()) return false; + return true; +} + +static bool readFileToString(const std::string& filename, std::string* result) { + if (!android::base::ReadFileToString(filename, result)) { + PLOG(ERROR) << "Failed to read from " << filename; + return false; + } + return true; +} + +/*static bool writeStringToFile(const std::string& payload, const std::string& filename) { + if (!android::base::WriteStringToFile(payload, filename)) { + PLOG(ERROR) << "Failed to write to " << filename; + return false; + } + return true; +}*/ + +static std::string getStretching() { + char paramstr[PROPERTY_VALUE_MAX]; + + property_get(SCRYPT_PROP, paramstr, SCRYPT_DEFAULTS); + return std::string() + kStretchPrefix_scrypt + paramstr; +} + +static bool stretchingNeedsSalt(const std::string& stretching) { + return stretching != kStretch_nopassword && stretching != kStretch_none; +} + +static bool stretchSecret(const std::string& stretching, const std::string& secret, + const std::string& salt, std::string* stretched) { + if (stretching == kStretch_nopassword) { + if (!secret.empty()) { + LOG(WARNING) << "Password present but stretching is nopassword"; + // Continue anyway + } + stretched->clear(); + } else if (stretching == kStretch_none) { + *stretched = secret; + } else if (std::equal(kStretchPrefix_scrypt.begin(), kStretchPrefix_scrypt.end(), + stretching.begin())) { + int Nf, rf, pf; + if (!parse_scrypt_parameters(stretching.substr(kStretchPrefix_scrypt.size()).c_str(), &Nf, + &rf, &pf)) { + LOG(ERROR) << "Unable to parse scrypt params in stretching: " << stretching; + return false; + } + stretched->assign(STRETCHED_BYTES, '\0'); + if (crypto_scrypt(reinterpret_cast(secret.data()), secret.size(), + reinterpret_cast(salt.data()), salt.size(), + 1 << Nf, 1 << rf, 1 << pf, + reinterpret_cast(&(*stretched)[0]), stretched->size()) != 0) { + LOG(ERROR) << "scrypt failed with params: " << stretching; + return false; + } + } else { + LOG(ERROR) << "Unknown stretching type: " << stretching; + return false; + } + return true; +} + +static bool generateAppId(const KeyAuthentication& auth, const std::string& stretching, + const std::string& salt, const std::string& secdiscardable, + std::string* appId) { + std::string stretched; + if (!stretchSecret(stretching, auth.secret, salt, &stretched)) return false; + *appId = hashSecdiscardable(secdiscardable) + stretched; + return true; +} + +/*bool storeKey(const std::string& dir, const KeyAuthentication& auth, const std::string& key) { + if (TEMP_FAILURE_RETRY(mkdir(dir.c_str(), 0700)) == -1) { + PLOG(ERROR) << "key mkdir " << dir; + return false; + } + if (!writeStringToFile(kCurrentVersion, dir + "/" + kFn_version)) return false; + std::string secdiscardable; + if (ReadRandomBytes(SECDISCARDABLE_BYTES, secdiscardable) != OK) { + // TODO status_t plays badly with PLOG, fix it. + LOG(ERROR) << "Random read failed"; + return false; + } + if (!writeStringToFile(secdiscardable, dir + "/" + kFn_secdiscardable)) return false; + std::string stretching = auth.secret.empty() ? kStretch_nopassword : getStretching(); + if (!writeStringToFile(stretching, dir + "/" + kFn_stretching)) return false; + std::string salt; + if (stretchingNeedsSalt(stretching)) { + if (ReadRandomBytes(SALT_BYTES, salt) != OK) { + LOG(ERROR) << "Random read failed"; + return false; + } + if (!writeStringToFile(salt, dir + "/" + kFn_salt)) return false; + } + std::string appId; + if (!generateAppId(auth, stretching, salt, secdiscardable, &appId)) return false; + Keymaster keymaster; + if (!keymaster) return false; + std::string kmKey; + if (!generateKeymasterKey(keymaster, auth, appId, &kmKey)) return false; + if (!writeStringToFile(kmKey, dir + "/" + kFn_keymaster_key_blob)) return false; + std::string encryptedKey; + if (!encryptWithKeymasterKey(keymaster, kmKey, auth, appId, key, &encryptedKey)) return false; + if (!writeStringToFile(encryptedKey, dir + "/" + kFn_encrypted_key)) return false; + return true; +}*/ + +bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, std::string* key) { + std::string version; + if (!readFileToString(dir + "/" + kFn_version, &version)) return false; + if (version != kCurrentVersion) { + LOG(ERROR) << "Version mismatch, expected " << kCurrentVersion << " got " << version; + return false; + } + std::string secdiscardable; + if (!readFileToString(dir + "/" + kFn_secdiscardable, &secdiscardable)) return false; + std::string stretching; + if (!readFileToString(dir + "/" + kFn_stretching, &stretching)) return false; + std::string salt; + if (stretchingNeedsSalt(stretching)) { + if (!readFileToString(dir + "/" + kFn_salt, &salt)) return false; + } + std::string appId; + if (!generateAppId(auth, stretching, salt, secdiscardable, &appId)) return false; + std::string kmKey; + if (!readFileToString(dir + "/" + kFn_keymaster_key_blob, &kmKey)) return false; + std::string encryptedMessage; + if (!readFileToString(dir + "/" + kFn_encrypted_key, &encryptedMessage)) return false; + Keymaster keymaster; + if (!keymaster) return false; + return decryptWithKeymasterKey(keymaster, kmKey, auth, appId, encryptedMessage, key); +} + +static bool deleteKey(const std::string& dir) { + std::string kmKey; + if (!readFileToString(dir + "/" + kFn_keymaster_key_blob, &kmKey)) return false; + Keymaster keymaster; + if (!keymaster) return false; + if (!keymaster.deleteKey(kmKey)) return false; + return true; +} + +static bool secdiscardSecdiscardable(const std::string& dir) { + if (ForkExecvp( + std::vector{kSecdiscardPath, "--", dir + "/" + kFn_secdiscardable}) != 0) { + LOG(ERROR) << "secdiscard failed"; + return false; + } + return true; +} + +static bool recursiveDeleteKey(const std::string& dir) { + if (ForkExecvp(std::vector{kRmPath, "-rf", dir}) != 0) { + LOG(ERROR) << "recursive delete failed"; + return false; + } + return true; +} + +bool destroyKey(const std::string& dir) { + bool success = true; + // Try each thing, even if previous things failed. + success &= deleteKey(dir); + success &= secdiscardSecdiscardable(dir); + success &= recursiveDeleteKey(dir); + return success; +} + +} // namespace vold +} // namespace android diff --git a/crypto/ext4crypt/KeyStorage.h b/crypto/ext4crypt/KeyStorage.h new file mode 100644 index 000000000..63d38da25 --- /dev/null +++ b/crypto/ext4crypt/KeyStorage.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_VOLD_KEYSTORAGE_H +#define ANDROID_VOLD_KEYSTORAGE_H + +#include + +namespace android { +namespace vold { + +// Represents the information needed to decrypt a disk encryption key. +// If "token" is nonempty, it is passed in as a required Gatekeeper auth token. +// If "secret" is nonempty, it is appended to the application-specific +// binary needed to unlock. +class KeyAuthentication { + public: + KeyAuthentication(std::string t, std::string s) : token{t}, secret{s} {}; + const std::string token; + const std::string secret; +}; + +extern const KeyAuthentication kEmptyAuthentication; + +// Create a directory at the named path, and store "key" in it, +// in such a way that it can only be retrieved via Keymaster and +// can be securely deleted. +// It's safe to move/rename the directory after creation. +//bool storeKey(const std::string& dir, const KeyAuthentication& auth, const std::string& key); + +// Retrieve the key from the named directory. +bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, std::string* key); + +// Securely destroy the key stored in the named directory and delete the directory. +bool destroyKey(const std::string& dir); + +} // namespace vold +} // namespace android + +#endif diff --git a/crypto/ext4crypt/Keymaster.cpp b/crypto/ext4crypt/Keymaster.cpp new file mode 100644 index 000000000..3c21aa26d --- /dev/null +++ b/crypto/ext4crypt/Keymaster.cpp @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Keymaster.h" + +//#include +#include +#include +#include + +#include +#define ERROR 1 +#define LOG(x) std::cout + +namespace android { +namespace vold { + +class IKeymasterDevice { + public: + IKeymasterDevice() {} + virtual ~IKeymasterDevice() {} + /*virtual keymaster_error_t generate_key(const keymaster_key_param_set_t* params, + keymaster_key_blob_t* key_blob) const = 0;*/ + virtual keymaster_error_t delete_key(const keymaster_key_blob_t* key) const = 0; + virtual keymaster_error_t begin(keymaster_purpose_t purpose, const keymaster_key_blob_t* key, + const keymaster_key_param_set_t* in_params, + keymaster_key_param_set_t* out_params, + keymaster_operation_handle_t* operation_handle) const = 0; + virtual keymaster_error_t update(keymaster_operation_handle_t operation_handle, + const keymaster_key_param_set_t* in_params, + const keymaster_blob_t* input, size_t* input_consumed, + keymaster_key_param_set_t* out_params, + keymaster_blob_t* output) const = 0; + virtual keymaster_error_t finish(keymaster_operation_handle_t operation_handle, + const keymaster_key_param_set_t* in_params, + const keymaster_blob_t* signature, + keymaster_key_param_set_t* out_params, + keymaster_blob_t* output) const = 0; + virtual keymaster_error_t abort(keymaster_operation_handle_t operation_handle) const = 0; + + protected: + DISALLOW_COPY_AND_ASSIGN(IKeymasterDevice); +}; + +template class KeymasterDevice : public IKeymasterDevice { + public: + KeymasterDevice(T* d) : mDevice{d} {} + /*keymaster_error_t generate_key(const keymaster_key_param_set_t* params, + keymaster_key_blob_t* key_blob) const override final { + return mDevice->generate_key(mDevice, params, key_blob, nullptr); + }*/ + keymaster_error_t delete_key(const keymaster_key_blob_t* key) const override final { + if (mDevice->delete_key == nullptr) return KM_ERROR_OK; + return mDevice->delete_key(mDevice, key); + } + keymaster_error_t begin(keymaster_purpose_t purpose, const keymaster_key_blob_t* key, + const keymaster_key_param_set_t* in_params, + keymaster_key_param_set_t* out_params, + keymaster_operation_handle_t* operation_handle) const override final { + return mDevice->begin(mDevice, purpose, key, in_params, out_params, operation_handle); + } + keymaster_error_t update(keymaster_operation_handle_t operation_handle, + const keymaster_key_param_set_t* in_params, + const keymaster_blob_t* input, size_t* input_consumed, + keymaster_key_param_set_t* out_params, + keymaster_blob_t* output) const override final { + return mDevice->update(mDevice, operation_handle, in_params, input, input_consumed, + out_params, output); + } + keymaster_error_t abort(keymaster_operation_handle_t operation_handle) const override final { + return mDevice->abort(mDevice, operation_handle); + } + + protected: + T* const mDevice; +}; + +class Keymaster1Device : public KeymasterDevice { + public: + Keymaster1Device(keymaster1_device_t* d) : KeymasterDevice{d} {} + ~Keymaster1Device() override final { keymaster1_close(mDevice); } + keymaster_error_t finish(keymaster_operation_handle_t operation_handle, + const keymaster_key_param_set_t* in_params, + const keymaster_blob_t* signature, + keymaster_key_param_set_t* out_params, + keymaster_blob_t* output) const override final { + return mDevice->finish(mDevice, operation_handle, in_params, signature, out_params, output); + } +}; + +class Keymaster2Device : public KeymasterDevice { + public: + Keymaster2Device(keymaster2_device_t* d) : KeymasterDevice{d} {} + ~Keymaster2Device() override final { keymaster2_close(mDevice); } + keymaster_error_t finish(keymaster_operation_handle_t operation_handle, + const keymaster_key_param_set_t* in_params, + const keymaster_blob_t* signature, + keymaster_key_param_set_t* out_params, + keymaster_blob_t* output) const override final { + return mDevice->finish(mDevice, operation_handle, in_params, nullptr, signature, out_params, + output); + } +}; + +KeymasterOperation::~KeymasterOperation() { + if (mDevice) mDevice->abort(mOpHandle); +} + +bool KeymasterOperation::updateCompletely(const std::string& input, std::string* output) { + output->clear(); + auto it = input.begin(); + while (it != input.end()) { + size_t toRead = static_cast(input.end() - it); + keymaster_blob_t inputBlob{reinterpret_cast(&*it), toRead}; + keymaster_blob_t outputBlob; + size_t inputConsumed; + auto error = + mDevice->update(mOpHandle, nullptr, &inputBlob, &inputConsumed, nullptr, &outputBlob); + if (error != KM_ERROR_OK) { + LOG(ERROR) << "update failed, code " << error; + mDevice = nullptr; + return false; + } + output->append(reinterpret_cast(outputBlob.data), outputBlob.data_length); + free(const_cast(outputBlob.data)); + if (inputConsumed > toRead) { + LOG(ERROR) << "update reported too much input consumed"; + mDevice = nullptr; + return false; + } + it += inputConsumed; + } + return true; +} + +bool KeymasterOperation::finish() { + auto error = mDevice->finish(mOpHandle, nullptr, nullptr, nullptr, nullptr); + mDevice = nullptr; + if (error != KM_ERROR_OK) { + LOG(ERROR) << "finish failed, code " << error; + return false; + } + return true; +} + +bool KeymasterOperation::finishWithOutput(std::string* output) { + keymaster_blob_t outputBlob; + auto error = mDevice->finish(mOpHandle, nullptr, nullptr, nullptr, &outputBlob); + mDevice = nullptr; + if (error != KM_ERROR_OK) { + LOG(ERROR) << "finish failed, code " << error; + return false; + } + output->assign(reinterpret_cast(outputBlob.data), outputBlob.data_length); + free(const_cast(outputBlob.data)); + return true; +} + +Keymaster::Keymaster() { + mDevice = nullptr; + const hw_module_t* module; + int ret = hw_get_module_by_class(KEYSTORE_HARDWARE_MODULE_ID, NULL, &module); + if (ret != 0) { + LOG(ERROR) << "hw_get_module_by_class returned " << ret; + return; + } + if (module->module_api_version == KEYMASTER_MODULE_API_VERSION_1_0) { + keymaster1_device_t* device; + ret = keymaster1_open(module, &device); + if (ret != 0) { + LOG(ERROR) << "keymaster1_open returned " << ret; + return; + } + mDevice = std::make_shared(device); + } else if (module->module_api_version == KEYMASTER_MODULE_API_VERSION_2_0) { + keymaster2_device_t* device; + ret = keymaster2_open(module, &device); + if (ret != 0) { + LOG(ERROR) << "keymaster2_open returned " << ret; + return; + } + mDevice = std::make_shared(device); + } else { + LOG(ERROR) << "module_api_version is " << module->module_api_version; + return; + } +} + +/*bool Keymaster::generateKey(const keymaster::AuthorizationSet& inParams, std::string* key) { + keymaster_key_blob_t keyBlob; + auto error = mDevice->generate_key(&inParams, &keyBlob); + if (error != KM_ERROR_OK) { + LOG(ERROR) << "generate_key failed, code " << error; + return false; + } + key->assign(reinterpret_cast(keyBlob.key_material), keyBlob.key_material_size); + free(const_cast(keyBlob.key_material)); + return true; +}*/ + +bool Keymaster::deleteKey(const std::string& key) { + keymaster_key_blob_t keyBlob{reinterpret_cast(key.data()), key.size()}; + auto error = mDevice->delete_key(&keyBlob); + if (error != KM_ERROR_OK) { + LOG(ERROR) << "delete_key failed, code " << error; + return false; + } + return true; +} + +KeymasterOperation Keymaster::begin(keymaster_purpose_t purpose, const std::string& key, + const keymaster::AuthorizationSet& inParams, + keymaster::AuthorizationSet* outParams) { + keymaster_key_blob_t keyBlob{reinterpret_cast(key.data()), key.size()}; + keymaster_operation_handle_t mOpHandle; + keymaster_key_param_set_t outParams_set; + auto error = mDevice->begin(purpose, &keyBlob, &inParams, &outParams_set, &mOpHandle); + if (error != KM_ERROR_OK) { + LOG(ERROR) << "begin failed, code " << error; + return KeymasterOperation(nullptr, mOpHandle); + } + outParams->Clear(); + outParams->push_back(outParams_set); + keymaster_free_param_set(&outParams_set); + return KeymasterOperation(mDevice, mOpHandle); +} + +KeymasterOperation Keymaster::begin(keymaster_purpose_t purpose, const std::string& key, + const keymaster::AuthorizationSet& inParams) { + keymaster_key_blob_t keyBlob{reinterpret_cast(key.data()), key.size()}; + keymaster_operation_handle_t mOpHandle; + auto error = mDevice->begin(purpose, &keyBlob, &inParams, nullptr, &mOpHandle); + if (error != KM_ERROR_OK) { + LOG(ERROR) << "begin failed, code " << error; + return KeymasterOperation(nullptr, mOpHandle); + } + return KeymasterOperation(mDevice, mOpHandle); +} + +} // namespace vold +} // namespace android diff --git a/crypto/ext4crypt/Keymaster.h b/crypto/ext4crypt/Keymaster.h new file mode 100644 index 000000000..11b3532ad --- /dev/null +++ b/crypto/ext4crypt/Keymaster.h @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_VOLD_KEYMASTER_H +#define ANDROID_VOLD_KEYMASTER_H + +#include +#include +#include + +#include + +namespace android { +namespace vold { + +using namespace keymaster; + +// C++ wrappers to the Keymaster C interface. +// This is tailored to the needs of KeyStorage, but could be extended to be +// a more general interface. + +// Class that wraps a keymaster1_device_t or keymaster2_device_t and provides methods +// they have in common. Also closes the device on destruction. +class IKeymasterDevice; + +// Wrapper for a keymaster_operation_handle_t representing an +// ongoing Keymaster operation. Aborts the operation +// in the destructor if it is unfinished. Methods log failures +// to LOG(ERROR). +class KeymasterOperation { + public: + ~KeymasterOperation(); + // Is this instance valid? This is false if creation fails, and becomes + // false on finish or if an update fails. + explicit operator bool() { return mDevice != nullptr; } + // Call "update" repeatedly until all of the input is consumed, and + // concatenate the output. Return true on success. + bool updateCompletely(const std::string& input, std::string* output); + // Finish; pass nullptr for the "output" param. + bool finish(); + // Finish and write the output to this string. + bool finishWithOutput(std::string* output); + // Move constructor + KeymasterOperation(KeymasterOperation&& rhs) { + mOpHandle = std::move(rhs.mOpHandle); + mDevice = std::move(rhs.mDevice); + } + + private: + KeymasterOperation(std::shared_ptr d, keymaster_operation_handle_t h) + : mDevice{d}, mOpHandle{h} {} + std::shared_ptr mDevice; + keymaster_operation_handle_t mOpHandle; + DISALLOW_COPY_AND_ASSIGN(KeymasterOperation); + friend class Keymaster; +}; + +// Wrapper for a Keymaster device for methods that start a KeymasterOperation or are not +// part of one. +class Keymaster { + public: + Keymaster(); + // false if we failed to open the keymaster device. + explicit operator bool() { return mDevice != nullptr; } + // Generate a key in the keymaster from the given params. + //bool generateKey(const AuthorizationSet& inParams, std::string* key); + // If the keymaster supports it, permanently delete a key. + bool deleteKey(const std::string& key); + // Begin a new cryptographic operation, collecting output parameters. + KeymasterOperation begin(keymaster_purpose_t purpose, const std::string& key, + const AuthorizationSet& inParams, AuthorizationSet* outParams); + // Begin a new cryptographic operation; don't collect output parameters. + KeymasterOperation begin(keymaster_purpose_t purpose, const std::string& key, + const AuthorizationSet& inParams); + + private: + std::shared_ptr mDevice; + DISALLOW_COPY_AND_ASSIGN(Keymaster); +}; + +template +inline AuthorizationSetBuilder& addStringParam(AuthorizationSetBuilder&& params, + TypedTag tag, + const std::string& val) { + return params.Authorization(tag, val.data(), val.size()); +} + +template +inline void addStringParam(AuthorizationSetBuilder* params, TypedTag tag, + const std::string& val) { + params->Authorization(tag, val.data(), val.size()); +} + +} // namespace vold +} // namespace android + +#endif diff --git a/crypto/ext4crypt/ScryptParameters.cpp b/crypto/ext4crypt/ScryptParameters.cpp new file mode 100644 index 000000000..669809b9f --- /dev/null +++ b/crypto/ext4crypt/ScryptParameters.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ScryptParameters.h" + +#include +#include + +bool parse_scrypt_parameters(const char* paramstr, int *Nf, int *rf, int *pf) { + int params[3]; + char *token; + char *saveptr; + int i; + + /* + * The token we're looking for should be three integers separated by + * colons (e.g., "12:8:1"). Scan the property to make sure it matches. + */ + for (i = 0, token = strtok_r(const_cast(paramstr), ":", &saveptr); + token != nullptr && i < 3; + i++, token = strtok_r(nullptr, ":", &saveptr)) { + char *endptr; + params[i] = strtol(token, &endptr, 10); + + /* + * Check that there was a valid number and it's 8-bit. + */ + if ((*token == '\0') || (*endptr != '\0') || params[i] < 0 || params[i] > 255) { + return false; + } + } + if (token != nullptr) { + return false; + } + *Nf = params[0]; *rf = params[1]; *pf = params[2]; + return true; +} diff --git a/crypto/ext4crypt/ScryptParameters.h b/crypto/ext4crypt/ScryptParameters.h new file mode 100644 index 000000000..1b43ea574 --- /dev/null +++ b/crypto/ext4crypt/ScryptParameters.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_VOLD_SCRYPT_PARAMETERS_H +#define ANDROID_VOLD_SCRYPT_PARAMETERS_H + +#include +#include + +#define SCRYPT_PROP "ro.crypto.scrypt_params" +#define SCRYPT_DEFAULTS "15:3:1" + +__BEGIN_DECLS + +bool parse_scrypt_parameters(const char* paramstr, int *Nf, int *rf, int *pf); + +__END_DECLS + +#endif diff --git a/crypto/ext4crypt/Utils.cpp b/crypto/ext4crypt/Utils.cpp new file mode 100644 index 000000000..f0bf029b9 --- /dev/null +++ b/crypto/ext4crypt/Utils.cpp @@ -0,0 +1,297 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Utils.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using android::base::ReadFileToString; +using android::base::StringPrintf; + +namespace android { +namespace vold { + +static const char* kKeyPath = "/data/misc/vold"; + +status_t ForkExecvp(const std::vector& args) { + return ForkExecvp(args, nullptr); +} + +status_t ForkExecvp(const std::vector& args, security_context_t context) { + size_t argc = args.size(); + char** argv = (char**) calloc(argc, sizeof(char*)); + for (size_t i = 0; i < argc; i++) { + argv[i] = (char*) args[i].c_str(); + if (i == 0) { + LOG(VERBOSE) << args[i]; + } else { + LOG(VERBOSE) << " " << args[i]; + } + } + + if (setexeccon(context)) { + LOG(ERROR) << "Failed to setexeccon"; + abort(); + } + abort(); + status_t res = 1;//android_fork_execvp(argc, argv, NULL, false, true); + if (setexeccon(nullptr)) { + LOG(ERROR) << "Failed to setexeccon"; + abort(); + } + + free(argv); + return res; +} + +status_t ForkExecvp(const std::vector& args, + std::vector& output) { + return ForkExecvp(args, output, nullptr); +} + +status_t ForkExecvp(const std::vector& args, + std::vector& output, security_context_t context) { + std::string cmd; + for (size_t i = 0; i < args.size(); i++) { + cmd += args[i] + " "; + if (i == 0) { + LOG(VERBOSE) << args[i]; + } else { + LOG(VERBOSE) << " " << args[i]; + } + } + output.clear(); + + if (setexeccon(context)) { + LOG(ERROR) << "Failed to setexeccon"; + abort(); + } + FILE* fp = popen(cmd.c_str(), "r"); + if (setexeccon(nullptr)) { + LOG(ERROR) << "Failed to setexeccon"; + abort(); + } + + if (!fp) { + PLOG(ERROR) << "Failed to popen " << cmd; + return -errno; + } + char line[1024]; + while (fgets(line, sizeof(line), fp) != nullptr) { + LOG(VERBOSE) << line; + output.push_back(std::string(line)); + } + if (pclose(fp) != 0) { + PLOG(ERROR) << "Failed to pclose " << cmd; + return -errno; + } + + return OK; +} + +pid_t ForkExecvpAsync(const std::vector& args) { + size_t argc = args.size(); + char** argv = (char**) calloc(argc + 1, sizeof(char*)); + for (size_t i = 0; i < argc; i++) { + argv[i] = (char*) args[i].c_str(); + if (i == 0) { + LOG(VERBOSE) << args[i]; + } else { + LOG(VERBOSE) << " " << args[i]; + } + } + + pid_t pid = fork(); + if (pid == 0) { + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + + if (execvp(argv[0], argv)) { + PLOG(ERROR) << "Failed to exec"; + } + + _exit(1); + } + + if (pid == -1) { + PLOG(ERROR) << "Failed to exec"; + } + + free(argv); + return pid; +} + +status_t ReadRandomBytes(size_t bytes, std::string& out) { + out.clear(); + + int fd = TEMP_FAILURE_RETRY(open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOFOLLOW)); + if (fd == -1) { + return -errno; + } + + char buf[BUFSIZ]; + size_t n; + while ((n = TEMP_FAILURE_RETRY(read(fd, &buf[0], std::min(sizeof(buf), bytes)))) > 0) { + out.append(buf, n); + bytes -= n; + } + close(fd); + + if (bytes == 0) { + return OK; + } else { + return -EIO; + } +} + +status_t HexToStr(const std::string& hex, std::string& str) { + str.clear(); + bool even = true; + char cur = 0; + for (size_t i = 0; i < hex.size(); i++) { + int val = 0; + switch (hex[i]) { + case ' ': case '-': case ':': continue; + case 'f': case 'F': val = 15; break; + case 'e': case 'E': val = 14; break; + case 'd': case 'D': val = 13; break; + case 'c': case 'C': val = 12; break; + case 'b': case 'B': val = 11; break; + case 'a': case 'A': val = 10; break; + case '9': val = 9; break; + case '8': val = 8; break; + case '7': val = 7; break; + case '6': val = 6; break; + case '5': val = 5; break; + case '4': val = 4; break; + case '3': val = 3; break; + case '2': val = 2; break; + case '1': val = 1; break; + case '0': val = 0; break; + default: return -EINVAL; + } + + if (even) { + cur = val << 4; + } else { + cur += val; + str.push_back(cur); + cur = 0; + } + even = !even; + } + return even ? OK : -EINVAL; +} + +static bool isValidFilename(const std::string& name) { + if (name.empty() || (name == ".") || (name == "..") + || (name.find('/') != std::string::npos)) { + return false; + } else { + return true; + } +} + +std::string BuildKeyPath(const std::string& partGuid) { + return StringPrintf("%s/expand_%s.key", kKeyPath, partGuid.c_str()); +} + +std::string BuildDataSystemLegacyPath(userid_t userId) { + return StringPrintf("%s/system/users/%u", BuildDataPath(nullptr).c_str(), userId); +} + +std::string BuildDataSystemCePath(userid_t userId) { + return StringPrintf("%s/system_ce/%u", BuildDataPath(nullptr).c_str(), userId); +} + +std::string BuildDataSystemDePath(userid_t userId) { + return StringPrintf("%s/system_de/%u", BuildDataPath(nullptr).c_str(), userId); +} + +std::string BuildDataMiscLegacyPath(userid_t userId) { + return StringPrintf("%s/misc/user/%u", BuildDataPath(nullptr).c_str(), userId); +} + +std::string BuildDataMiscCePath(userid_t userId) { + return StringPrintf("%s/misc_ce/%u", BuildDataPath(nullptr).c_str(), userId); +} + +std::string BuildDataMiscDePath(userid_t userId) { + return StringPrintf("%s/misc_de/%u", BuildDataPath(nullptr).c_str(), userId); +} + +// Keep in sync with installd (frameworks/native/cmds/installd/utils.h) +std::string BuildDataProfilesDePath(userid_t userId) { + return StringPrintf("%s/misc/profiles/cur/%u", BuildDataPath(nullptr).c_str(), userId); +} + +std::string BuildDataProfilesForeignDexDePath(userid_t userId) { + std::string profiles_path = BuildDataProfilesDePath(userId); + return StringPrintf("%s/foreign-dex", profiles_path.c_str()); +} + +std::string BuildDataPath(const char* volumeUuid) { + // TODO: unify with installd path generation logic + if (volumeUuid == nullptr) { + return "/data"; + } else { + CHECK(isValidFilename(volumeUuid)); + return StringPrintf("/mnt/expand/%s", volumeUuid); + } +} + +std::string BuildDataMediaCePath(const char* volumeUuid, userid_t userId) { + // TODO: unify with installd path generation logic + std::string data(BuildDataPath(volumeUuid)); + return StringPrintf("%s/media/%u", data.c_str(), userId); +} + +std::string BuildDataUserCePath(const char* volumeUuid, userid_t userId) { + // TODO: unify with installd path generation logic + std::string data(BuildDataPath(volumeUuid)); + if (volumeUuid == nullptr) { + if (userId == 0) { + return StringPrintf("%s/data", data.c_str()); + } else { + return StringPrintf("%s/user/%u", data.c_str(), userId); + } + } else { + return StringPrintf("%s/user/%u", data.c_str(), userId); + } +} + +std::string BuildDataUserDePath(const char* volumeUuid, userid_t userId) { + // TODO: unify with installd path generation logic + std::string data(BuildDataPath(volumeUuid)); + return StringPrintf("%s/user_de/%u", data.c_str(), userId); +} + +} // namespace vold +} // namespace android diff --git a/crypto/ext4crypt/Utils.h b/crypto/ext4crypt/Utils.h new file mode 100644 index 000000000..8d0445d89 --- /dev/null +++ b/crypto/ext4crypt/Utils.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TWRP_VOLD_UTILS_H +#define TWRP_VOLD_UTILS_H + +#include +#include +#include + +#include +#include + +namespace android { +namespace vold { + +/* Returns either WEXITSTATUS() status, or a negative errno */ +status_t ForkExecvp(const std::vector& args); +status_t ForkExecvp(const std::vector& args, security_context_t context); + +status_t ForkExecvp(const std::vector& args, + std::vector& output); +status_t ForkExecvp(const std::vector& args, + std::vector& output, security_context_t context); + +pid_t ForkExecvpAsync(const std::vector& args); + +status_t ReadRandomBytes(size_t bytes, std::string& out); + +/* Converts hex string to raw bytes, ignoring [ :-] */ +status_t HexToStr(const std::string& hex, std::string& str); + +std::string BuildKeyPath(const std::string& partGuid); + +std::string BuildDataSystemLegacyPath(userid_t userid); +std::string BuildDataSystemCePath(userid_t userid); +std::string BuildDataSystemDePath(userid_t userid); +std::string BuildDataMiscLegacyPath(userid_t userid); +std::string BuildDataMiscCePath(userid_t userid); +std::string BuildDataMiscDePath(userid_t userid); +std::string BuildDataProfilesDePath(userid_t userid); +std::string BuildDataProfilesForeignDexDePath(userid_t userid); + +std::string BuildDataPath(const char* volumeUuid); +std::string BuildDataMediaCePath(const char* volumeUuid, userid_t userid); +std::string BuildDataUserCePath(const char* volumeUuid, userid_t userid); +std::string BuildDataUserDePath(const char* volumeUuid, userid_t userid); + +} // namespace vold +} // namespace android + +#endif diff --git a/crypto/ext4crypt/main.cpp b/crypto/ext4crypt/main.cpp new file mode 100644 index 000000000..f0266ae10 --- /dev/null +++ b/crypto/ext4crypt/main.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2016 Team Win Recovery Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include "Decrypt.h" + +int main(int argc, char *argv[]) { + bool ret = false; + if (argc < 2) { + Decrypt_DE(); + ret = Decrypt_User(0, "0000"); + } else if (argc < 3) { + Decrypt_DE(); + ret = Decrypt_User(0, argv[1]); + } else { + ret = Decrypt_User(atoi(argv[1]), argv[2]); + } + if (!ret) + printf("Failed to decrypt\n"); + return 0; +} diff --git a/gui/theme/common/languages/en.xml b/gui/theme/common/languages/en.xml index ee772bfd9..77164184a 100644 --- a/gui/theme/common/languages/en.xml +++ b/gui/theme/common/languages/en.xml @@ -540,6 +540,7 @@ Failed to decrypt data. No crypto support was compiled into this build. Data successfully decrypted, new block device: '{1}' + Data successfully decrypted Done. Partitioning SD Card... Unable to locate device to partition. diff --git a/partition.cpp b/partition.cpp index 00b23b79f..b764d9479 100644 --- a/partition.cpp +++ b/partition.cpp @@ -58,6 +58,10 @@ extern "C" { #ifdef TW_INCLUDE_CRYPTO #include "crypto/lollipop/cryptfs.h" #include "gpt/gpt.h" + #ifdef TW_INCLUDE_FBE + #include "crypto/ext4crypt/Decrypt.h" + //#include "crypto/ext4crypt/Ext4Crypt.h" + #endif #else #define CRYPT_FOOTER_OFFSET 0x4000 #endif @@ -204,6 +208,7 @@ TWPartition::TWPartition() { Can_Be_Encrypted = false; Is_Encrypted = false; Is_Decrypted = false; + Is_FBE = false; Mount_To_Decrypt = false; Decrypted_Block_Device = ""; Display_Name = ""; @@ -474,6 +479,11 @@ void TWPartition::Partition_Post_Processing(bool Display_Error) { Setup_Cache_Partition(Display_Error); } +void TWPartition::ExcludeAll(const string& path) { + backup_exclusions.add_absolute_dir(path); + wipe_exclusions.add_absolute_dir(path); +} + void TWPartition::Setup_Data_Partition(bool Display_Error) { if (Mount_Point != "/data") return; @@ -491,6 +501,8 @@ void TWPartition::Setup_Data_Partition(bool Display_Error) { DataManager::SetValue(TW_IS_DECRYPTED, 1); Is_Encrypted = true; Is_Decrypted = true; + Is_FBE = false; + DataManager::SetValue(TW_IS_FBE, 0); Decrypted_Block_Device = crypto_blkdev; LOGINFO("Data already decrypted, new block device: '%s'\n", crypto_blkdev); } else if (!Mount(false)) { @@ -513,9 +525,49 @@ void TWPartition::Setup_Data_Partition(bool Display_Error) { LOGERR("Primary block device '%s' for mount point '%s' is not present!\n", Primary_Block_Device.c_str(), Mount_Point.c_str()); } } else { - // Filesystem is not encrypted and the mount succeeded, so return to - // the original unmounted state - UnMount(false); + if (TWFunc::Path_Exists("/data/unencrypted/key/version")) { + LOGINFO("File Based Encryption is present\n"); +#ifdef TW_INCLUDE_FBE + ExcludeAll(Mount_Point + "/convert_fbe"); + ExcludeAll(Mount_Point + "/unencrypted"); + //ExcludeAll(Mount_Point + "/system/users/0"); // we WILL need to retain some of this if multiple users are present or we just need to delete more folders for the extra users somewhere else + ExcludeAll(Mount_Point + "/misc/vold/user_keys"); + ExcludeAll(Mount_Point + "/system_ce"); + ExcludeAll(Mount_Point + "/system_de"); + ExcludeAll(Mount_Point + "/misc_ce"); + ExcludeAll(Mount_Point + "/misc_de"); + ExcludeAll(Mount_Point + "/system/gatekeeper.password.key"); + ExcludeAll(Mount_Point + "/system/gatekeeper.pattern.key"); + ExcludeAll(Mount_Point + "/system/locksettings.db"); + //ExcludeAll(Mount_Point + "/system/locksettings.db-shm"); // don't seem to need this one, but the other 2 are needed + ExcludeAll(Mount_Point + "/system/locksettings.db-wal"); + ExcludeAll(Mount_Point + "/user_de"); + //ExcludeAll(Mount_Point + "/misc/profiles/cur/0"); // might be important later + ExcludeAll(Mount_Point + "/misc/gatekeeper"); + ExcludeAll(Mount_Point + "/drm/kek.dat"); + int retry_count = 3; + while (!Decrypt_DE() && --retry_count) + usleep(2000); + if (retry_count > 0) { + property_set("ro.crypto.state", "encrypted"); + Is_Encrypted = true; + Is_Decrypted = false; + Is_FBE = true; + DataManager::SetValue(TW_IS_FBE, 1); + DataManager::SetValue(TW_IS_ENCRYPTED, 1); + string filename; + DataManager::SetValue(TW_CRYPTO_PWTYPE, Get_Password_Type(0, filename)); + DataManager::SetValue(TW_CRYPTO_PASSWORD, ""); + DataManager::SetValue("tw_crypto_display", ""); + } +#else + LOGERR("FBE found but FBE support not present in TWRP\n"); +#endif + } else { + // Filesystem is not encrypted and the mount succeeded, so return to + // the original unmounted state + UnMount(false); + } } if (datamedia && (!Is_Encrypted || (Is_Encrypted && Is_Decrypted))) { Setup_Data_Media(); @@ -2555,6 +2607,10 @@ void TWPartition::Recreate_Media_Folder(void) { string Command; string Media_Path = Mount_Point + "/media"; + if (Is_FBE) { + LOGINFO("Not recreating media folder on FBE\n"); + return; + } if (!Mount(true)) { gui_msg(Msg(msg::kError, "recreate_folder_err=Unable to recreate {1} folder.")(Media_Path)); } else if (!TWFunc::Path_Exists(Media_Path)) { diff --git a/partitionmanager.cpp b/partitionmanager.cpp index 9e23ccb94..26f48d844 100644 --- a/partitionmanager.cpp +++ b/partitionmanager.cpp @@ -62,6 +62,9 @@ extern "C" { #include "crypto/lollipop/cryptfs.h" #include "gui/rapidxml.hpp" #include "gui/pages.hpp" + #ifdef TW_INCLUDE_FBE + #include "crypto/ext4crypt/Decrypt.h" + #endif #endif #ifdef AB_OTA_UPDATER @@ -177,17 +180,28 @@ int TWPartitionManager::Process_Fstab(string Fstab_Filename, bool Display_Error) #ifdef TW_INCLUDE_CRYPTO TWPartition* Decrypt_Data = Find_Partition_By_Path("/data"); if (Decrypt_Data && Decrypt_Data->Is_Encrypted && !Decrypt_Data->Is_Decrypted) { - int password_type = cryptfs_get_password_type(); - if (password_type == CRYPT_TYPE_DEFAULT) { - LOGINFO("Device is encrypted with the default password, attempting to decrypt.\n"); - if (Decrypt_Device("default_password") == 0) { - gui_msg("decrypt_success=Successfully decrypted with default password."); - DataManager::SetValue(TW_IS_ENCRYPTED, 0); - } else { - gui_err("unable_to_decrypt=Unable to decrypt with default password."); + if (Decrypt_Data->Is_FBE) { + if (DataManager::GetIntValue(TW_CRYPTO_PWTYPE) == 0) { + if (Decrypt_Device("!") == 0) { + gui_msg("decrypt_success=Successfully decrypted with default password."); + DataManager::SetValue(TW_IS_ENCRYPTED, 0); + } else { + gui_err("unable_to_decrypt=Unable to decrypt with default password."); + } } } else { - DataManager::SetValue("TW_CRYPTO_TYPE", password_type); + int password_type = cryptfs_get_password_type(); + if (password_type == CRYPT_TYPE_DEFAULT) { + LOGINFO("Device is encrypted with the default password, attempting to decrypt.\n"); + if (Decrypt_Device("default_password") == 0) { + gui_msg("decrypt_success=Successfully decrypted with default password."); + DataManager::SetValue(TW_IS_ENCRYPTED, 0); + } else { + gui_err("unable_to_decrypt=Unable to decrypt with default password."); + } + } else { + DataManager::SetValue("TW_CRYPTO_TYPE", password_type); + } } } if (Decrypt_Data && (!Decrypt_Data->Is_Encrypted || Decrypt_Data->Is_Decrypted) && Decrypt_Data->Mount(false)) { @@ -1473,6 +1487,36 @@ void TWPartitionManager::Update_System_Details(void) { return; } +void TWPartitionManager::Post_Decrypt(const string& Block_Device) { + TWPartition* dat = Find_Partition_By_Path("/data"); + if (dat != NULL) { + DataManager::SetValue(TW_IS_DECRYPTED, 1); + dat->Is_Decrypted = true; + if (!Block_Device.empty()) { + dat->Decrypted_Block_Device = Block_Device; + gui_msg(Msg("decrypt_success_dev=Data successfully decrypted, new block device: '{1}'")(Block_Device)); + } else { + gui_msg("decrypt_success_nodev=Data successfully decrypted"); + } + dat->Setup_File_System(false); + dat->Current_File_System = dat->Fstab_File_System; // Needed if we're ignoring blkid because encrypted devices start out as emmc + + // Sleep for a bit so that the device will be ready + sleep(1); + if (dat->Has_Data_Media && dat->Mount(false) && TWFunc::Path_Exists("/data/media/0")) { + dat->Storage_Path = "/data/media/0"; + dat->Symlink_Path = dat->Storage_Path; + DataManager::SetValue("tw_storage_path", "/data/media/0"); + DataManager::SetValue("tw_settings_path", "/data/media/0"); + dat->UnMount(false); + Output_Partition(dat); + } + Update_System_Details(); + UnMount_Main_Partitions(); + } else + LOGERR("Unable to locate data partition.\n"); +} + int TWPartitionManager::Decrypt_Device(string Password) { #ifdef TW_INCLUDE_CRYPTO int ret_val, password_len; @@ -1494,6 +1538,25 @@ int TWPartitionManager::Decrypt_Device(string Password) { sleep(1); } + if (DataManager::GetIntValue(TW_IS_FBE)) { +#ifdef TW_INCLUDE_FBE + if (!Mount_By_Path("/data", true)) // /data has to be mounted for FBE + return -1; + int retry_count = 10; + while (!TWFunc::Path_Exists("/data/system/users/gatekeeper.password.key") && --retry_count) + usleep(2000); // A small sleep is needed after mounting /data to ensure reliable decrypt... maybe because of DE? + int user_id = DataManager::GetIntValue("tw_decrypt_user_id"); + LOGINFO("Decrypting FBE for user %i\n", user_id); + if (Decrypt_User(user_id, Password)) { + Post_Decrypt(""); + return 0; + } +#else + LOGERR("FBE support is not present\n"); +#endif + return -1; + } + strcpy(cPassword, Password.c_str()); int pwret = cryptfs_check_passwd(cPassword); @@ -1513,29 +1576,7 @@ int TWPartitionManager::Decrypt_Device(string Password) { if (strcmp(crypto_blkdev, "error") == 0) { LOGERR("Error retrieving decrypted data block device.\n"); } else { - TWPartition* dat = Find_Partition_By_Path("/data"); - if (dat != NULL) { - DataManager::SetValue(TW_IS_DECRYPTED, 1); - dat->Is_Decrypted = true; - dat->Decrypted_Block_Device = crypto_blkdev; - dat->Setup_File_System(false); - dat->Current_File_System = dat->Fstab_File_System; // Needed if we're ignoring blkid because encrypted devices start out as emmc - gui_msg(Msg("decrypt_success_dev=Data successfully decrypted, new block device: '{1}'")(crypto_blkdev)); - - // Sleep for a bit so that the device will be ready - sleep(1); - if (dat->Has_Data_Media && dat->Mount(false) && TWFunc::Path_Exists("/data/media/0")) { - dat->Storage_Path = "/data/media/0"; - dat->Symlink_Path = dat->Storage_Path; - DataManager::SetValue("tw_storage_path", "/data/media/0"); - DataManager::SetValue("tw_settings_path", "/data/media/0"); - dat->UnMount(false); - Output_Partition(dat); - } - Update_System_Details(); - UnMount_Main_Partitions(); - } else - LOGERR("Unable to locate data partition.\n"); + Post_Decrypt(crypto_blkdev); } return 0; #else diff --git a/partitions.hpp b/partitions.hpp index a6af5b1a4..450e8815b 100644 --- a/partitions.hpp +++ b/partitions.hpp @@ -166,6 +166,7 @@ private: bool Is_Sparse_Image(const string& Filename); // Determines if a file is in sparse image format bool Flash_Sparse_Image(const string& Filename); // Flashes a sparse image using simg2img bool Flash_Image_FI(const string& Filename, ProgressTracking *progress); // Flashes an image to the partition using flash_image for mtd nand + void ExcludeAll(const string& path); // Adds an exclusion for path to both the backup and wipe exclusion lists private: bool Can_Be_Mounted; // Indicates that the partition can be mounted @@ -194,6 +195,7 @@ private: bool Can_Be_Encrypted; // This partition might be encrypted, affects error handling, can only be true if crypto support is compiled in bool Is_Encrypted; // This partition is thought to be encrypted -- it wouldn't mount for some reason, only avialble with crypto support bool Is_Decrypted; // This partition has successfully been decrypted + bool Is_FBE; // File Based Encryption is present bool Mount_To_Decrypt; // Mount this partition during decrypt (/vendor, /firmware, etc in case we need proprietary libs or firmware files) string Display_Name; // Display name for the GUI string Backup_Name; // Backup name -- used for backup filenames @@ -304,6 +306,7 @@ private: bool Add_Remove_MTP_Storage(TWPartition* Part, int message_type); // Adds or removes an MTP Storage partition TWPartition* Find_Next_Storage(string Path, bool Exclude_Data_Media); int Open_Lun_File(string Partition_Path, string Lun_File); + void Post_Decrypt(const string& Block_Device); // Completes various post-decrypt tasks pid_t mtppid; bool mtp_was_enabled; int mtp_write_fd; diff --git a/prebuilt/Android.mk b/prebuilt/Android.mk index 390c3f1f0..67aa86b4d 100644 --- a/prebuilt/Android.mk +++ b/prebuilt/Android.mk @@ -176,6 +176,18 @@ ifeq ($(TW_INCLUDE_CRYPTO), true) ifeq ($(TARGET_HW_DISK_ENCRYPTION),true) RELINK_SOURCE_FILES += $(TARGET_OUT_VENDOR_SHARED_LIBRARIES)/libcryptfs_hw.so endif + # FBE files + ifeq ($(TW_INCLUDE_CRYPTO_FBE), true) + RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libe4crypt.so + RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libgatekeeper.so + RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libsoftkeymaster.so + RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libkeymaster_messages.so + RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libkeystore_binder.so + RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libbinder.so + RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libprotobuf-cpp-lite.so + RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libsoftkeymasterdevice.so + RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libkeymaster1.so + endif endif ifeq ($(AB_OTA_UPDATER), true) RELINK_SOURCE_FILES += $(TARGET_OUT_EXECUTABLES)/bootctl diff --git a/variables.h b/variables.h index 241c7dc66..5c9b40ef0 100644 --- a/variables.h +++ b/variables.h @@ -132,6 +132,7 @@ #define TW_IS_DECRYPTED "tw_is_decrypted" #define TW_CRYPTO_PWTYPE "tw_crypto_pwtype" #define TW_HAS_CRYPTO "tw_has_crypto" +#define TW_IS_FBE "tw_is_fbe" #define TW_CRYPTO_PASSWORD "tw_crypto_password" #define TW_SDEXT_DISABLE_EXT4 "tw_sdext_disable_ext4" #define TW_MILITARY_TIME "tw_military_time" -- cgit v1.2.3