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(&paramBuilder, 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(&paramBuilder, keymaster::TAG_APPLICATION_ID, appId);
+    if (!auth.token.empty()) {
+        LOG(DEBUG) << "Supplying auth token to Keymaster";
+        addStringParam(&paramBuilder, 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"