Support File Based Encryption
Change-Id: Ib688ddd0c32d3999590cacd86b6d9b18eac336e9
diff --git a/Android.mk b/Android.mk
index 467ce61..582b710 100644
--- a/Android.mk
+++ b/Android.mk
@@ -296,6 +296,11 @@
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 @@
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 0000000..531aca1
--- /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 0000000..6c47add
--- /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 <string>
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "ext4_crypt.h"
+#include "key_control.h"
+
+#include <hardware/gatekeeper.h>
+#include "HashPassword.h"
+
+#include <android-base/file.h>
+
+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<auth_token_len;i++) {
+ sprintf(&token_hex[2*i], "%02X", auth_token[i]);
+ }
+ // The secret is "Android FBE credential hash" plus appended 0x00 to reach 128 bytes then append the user's password then feed that to sha512sum
+ std::string secret = HashPassword(Password);
+ if (!e4crypt_unlock_user_key(user_id, 0, token_hex, secret.c_str())) {
+ 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;
+}
diff --git a/crypto/ext4crypt/Decrypt.h b/crypto/ext4crypt/Decrypt.h
new file mode 100644
index 0000000..c05ac69
--- /dev/null
+++ b/crypto/ext4crypt/Decrypt.h
@@ -0,0 +1,34 @@
+/*
+ * 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 <stdbool.h>
+#include <sys/cdefs.h>
+
+#include <cutils/multiuser.h>
+
+#include <string>
+
+__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 0000000..423147d
--- /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 <algorithm>
+#include <iomanip>
+#include <map>
+#include <set>
+#include <sstream>
+#include <string>
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <openssl/sha.h>
+#include <selinux/android.h>
+#include <stdio.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <iostream>
+
+#include <private/android_filesystem_config.h>
+
+#include "ext4_crypt.h"
+#include "key_control.h"
+
+#include <hardware/gatekeeper.h>
+#include "HashPassword.h"
+
+#define EMULATED_USES_SELINUX 0
+#define MANAGE_MISC_DIRS 0
+
+#include <cutils/fs.h>
+
+#include <android-base/file.h>
+//#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+#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<userid_t> s_ephemeral_users;
+
+// Map user ids to key references
+std::map<userid_t, std::string> s_de_key_raw_refs;
+std::map<userid_t, std::string> 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<userid_t, std::string> 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<std::string> get_ce_key_paths(const std::string& directory_path) {
+ auto dirp = std::unique_ptr<DIR, int (*)(DIR*)>(opendir(directory_path.c_str()), closedir);
+ if (!dirp) {
+ PLOG(ERROR) << "Unable to open ce key directory: " + directory_path;
+ return std::vector<std::string>();
+ }
+ std::vector<std::string> 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<std::string>();
+ }
+ 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<std::string>& 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<userid_t, std::string>& 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<DIR, int (*)(DIR*)>(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 0000000..b05ff4d
--- /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 <stdbool.h>
+#include <sys/cdefs.h>
+
+#include <cutils/multiuser.h>
+
+#include <string>
+
+__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 0000000..86e067e
--- /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 <stdio.h>
+#include <string>
+#include <stdlib.h>
+#include <openssl/sha.h>
+
+#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 0000000..d9b5ce5
--- /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 <string>
+
+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 0000000..199520e
--- /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 <vector>
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <iostream>
+
+#include <openssl/sha.h>
+
+#include <android-base/file.h>
+//#include <android-base/logging.h>
+
+#include <cutils/properties.h>
+
+#include <hardware/hw_auth_token.h>
+
+#include <keymaster/authorization_set.h>
+
+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<uint8_t*>(&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<const hw_auth_token_t*>(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<const char*>(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<const uint8_t*>(secret.data()), secret.size(),
+ reinterpret_cast<const uint8_t*>(salt.data()), salt.size(),
+ 1 << Nf, 1 << rf, 1 << pf,
+ reinterpret_cast<uint8_t*>(&(*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<std::string>{kSecdiscardPath, "--", dir + "/" + kFn_secdiscardable}) != 0) {
+ LOG(ERROR) << "secdiscard failed";
+ return false;
+ }
+ return true;
+}
+
+static bool recursiveDeleteKey(const std::string& dir) {
+ if (ForkExecvp(std::vector<std::string>{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 0000000..63d38da
--- /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 <string>
+
+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 0000000..3c21aa2
--- /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 <android-base/logging.h>
+#include <hardware/hardware.h>
+#include <hardware/keymaster1.h>
+#include <hardware/keymaster2.h>
+
+#include <iostream>
+#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 <typename T> 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<keymaster1_device_t> {
+ public:
+ Keymaster1Device(keymaster1_device_t* d) : KeymasterDevice<keymaster1_device_t>{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<keymaster2_device_t> {
+ public:
+ Keymaster2Device(keymaster2_device_t* d) : KeymasterDevice<keymaster2_device_t>{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<size_t>(input.end() - it);
+ keymaster_blob_t inputBlob{reinterpret_cast<const uint8_t*>(&*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<const char*>(outputBlob.data), outputBlob.data_length);
+ free(const_cast<uint8_t*>(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<const char*>(outputBlob.data), outputBlob.data_length);
+ free(const_cast<uint8_t*>(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<Keymaster1Device>(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<Keymaster2Device>(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<const char*>(keyBlob.key_material), keyBlob.key_material_size);
+ free(const_cast<uint8_t*>(keyBlob.key_material));
+ return true;
+}*/
+
+bool Keymaster::deleteKey(const std::string& key) {
+ keymaster_key_blob_t keyBlob{reinterpret_cast<const uint8_t*>(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<const uint8_t*>(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<const uint8_t*>(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 0000000..11b3532
--- /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 <memory>
+#include <string>
+#include <utility>
+
+#include <keymaster/authorization_set.h>
+
+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<IKeymasterDevice> d, keymaster_operation_handle_t h)
+ : mDevice{d}, mOpHandle{h} {}
+ std::shared_ptr<IKeymasterDevice> 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<IKeymasterDevice> mDevice;
+ DISALLOW_COPY_AND_ASSIGN(Keymaster);
+};
+
+template <keymaster_tag_t Tag>
+inline AuthorizationSetBuilder& addStringParam(AuthorizationSetBuilder&& params,
+ TypedTag<KM_BYTES, Tag> tag,
+ const std::string& val) {
+ return params.Authorization(tag, val.data(), val.size());
+}
+
+template <keymaster_tag_t Tag>
+inline void addStringParam(AuthorizationSetBuilder* params, TypedTag<KM_BYTES, Tag> 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 0000000..669809b
--- /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 <stdlib.h>
+#include <string.h>
+
+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<char *>(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 0000000..1b43ea5
--- /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 <stdbool.h>
+#include <sys/cdefs.h>
+
+#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 0000000..f0bf029
--- /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 <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/statvfs.h>
+
+#include <selinux/android.h>
+
+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<std::string>& args) {
+ return ForkExecvp(args, nullptr);
+}
+
+status_t ForkExecvp(const std::vector<std::string>& 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<std::string>& args,
+ std::vector<std::string>& output) {
+ return ForkExecvp(args, output, nullptr);
+}
+
+status_t ForkExecvp(const std::vector<std::string>& args,
+ std::vector<std::string>& 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<std::string>& 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 0000000..8d0445d
--- /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 <utils/Errors.h>
+#include <cutils/multiuser.h>
+#include <selinux/selinux.h>
+
+#include <vector>
+#include <string>
+
+namespace android {
+namespace vold {
+
+/* Returns either WEXITSTATUS() status, or a negative errno */
+status_t ForkExecvp(const std::vector<std::string>& args);
+status_t ForkExecvp(const std::vector<std::string>& args, security_context_t context);
+
+status_t ForkExecvp(const std::vector<std::string>& args,
+ std::vector<std::string>& output);
+status_t ForkExecvp(const std::vector<std::string>& args,
+ std::vector<std::string>& output, security_context_t context);
+
+pid_t ForkExecvpAsync(const std::vector<std::string>& 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 0000000..f0266ae
--- /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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#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 ee772bf..7716418 100644
--- a/gui/theme/common/languages/en.xml
+++ b/gui/theme/common/languages/en.xml
@@ -540,6 +540,7 @@
<string name="fail_decrypt">Failed to decrypt data.</string>
<string name="no_crypto_support">No crypto support was compiled into this build.</string>
<string name="decrypt_success_dev">Data successfully decrypted, new block device: '{1}'</string>
+ <string name="decrypt_success_nodev">Data successfully decrypted</string>
<string name="done">Done.</string>
<string name="start_partition_sd">Partitioning SD Card...</string>
<string name="partition_sd_locate">Unable to locate device to partition.</string>
diff --git a/partition.cpp b/partition.cpp
index 00b23b7..b764d94 100644
--- a/partition.cpp
+++ b/partition.cpp
@@ -58,6 +58,10 @@
#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 @@
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 @@
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 @@
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 @@
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 @@
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 9e23ccb..26f48d8 100644
--- a/partitionmanager.cpp
+++ b/partitionmanager.cpp
@@ -62,6 +62,9 @@
#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 @@
#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 @@
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 @@
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 @@
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 a6af5b1..450e881 100644
--- a/partitions.hpp
+++ b/partitions.hpp
@@ -166,6 +166,7 @@
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 @@
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 @@
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 390c3f1..67aa86b 100644
--- a/prebuilt/Android.mk
+++ b/prebuilt/Android.mk
@@ -176,6 +176,18 @@
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 241c7dc..5c9b40e 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"