Merge tag 'android-11.0.0_r16' into android-10.0

Android 11.0.0 release 16 - twrp bringup patch
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 0000000..540e149
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,23 @@
+- [ ] I am running an official build of TWRP, downloaded from https://twrp.me/Devices/
+- [ ] I am running the latest version of TWRP
+- [ ] I have read the FAQ (https://twrp.me/FAQ/)
+- [ ] I have searched for my issue and it does not already exist
+
+**Device codename**: <!-- Device codename -->
+**TWRP version**: <!-- TWRP version installed -->
+
+#### WHAT STEPS WILL REPRODUCE THE PROBLEM?
+<!-- Explain the steps necessary to reproduce the problem, as completely as possible -->
+
+#### WHAT IS THE EXPECTED RESULT?
+<!-- Explain what the expected result is, as completely as possible -->
+
+#### WHAT HAPPENS INSTEAD?
+<!-- Explain what happens instead, as completely as possible -->
+
+#### ADDITIONAL INFORMATION
+<!-- Add any additional information you know about the issue, such as possible causes and solutions -->
+
+<!-- Use https://paste.omnirom.org/ and upload `/tmp/recovery.log` and the output of `dmesg` -->
+`/tmp/recovery.log`: <!-- Link here -->
+`dmesg`: <!-- Link here -->
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000..d04c212
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,7 @@
+# WE DO NOT MERGE PULL REQUESTS SUBMITTED HERE
+
+You will need to submit it through [OmniRom Gerrit](https://gerrit.omnirom.org/#/admin/projects/android_bootable_recovery/)
+
+For changes to device trees, use [TWRP Gerrit](https://gerrit.twrp.me/)
+
+This guide explani how to use [Gerrit code review](https://forum.xda-developers.com/general/xda-university/guide-using-gerrit-code-review-t3720802)
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..480f547
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+.*.swp
+*~
+tags
+.vscode
diff --git a/Android.bp b/Android.bp
old mode 100644
new mode 100755
index 8e29dc8..250ef9d
--- a/Android.bp
+++ b/Android.bp
@@ -1,203 +1,90 @@
-// Copyright (C) 2018 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.
+subdirs = [
+    "bootloader_message",
+]
 
 cc_defaults {
     name: "recovery_defaults",
-
     cflags: [
         "-D_FILE_OFFSET_BITS=64",
-
         // Must be the same as RECOVERY_API_VERSION.
         "-DRECOVERY_API_VERSION=3",
-
         "-Wall",
         "-Werror",
     ],
+    cpp_std: "c++17",
 }
 
-cc_library_static {
-    name: "librecovery_fastboot",
-    recovery_available: true,
-    defaults: [
-        "recovery_defaults",
+bootstrap_go_package {
+    name: "soong-libaosprecovery_defaults",
+    pkgPath: "bootable/recovery/libaosprecovery",
+    deps: [
+        "soong",
+        "soong-android",
+        "soong-cc"
     ],
-
     srcs: [
-        "fastboot/fastboot.cpp",
+        "libaosprecovery_defaults.go"
     ],
-
-    shared_libs: [
-        "libbase",
-        "libbootloader_message",
-        "libcutils",
-        "liblog",
-        "librecovery_ui",
-    ],
-
-    static_libs: [
-        "librecovery_ui_default",
-    ],
+    pluginFor: ["soong_build"]
 }
 
-cc_defaults {
-    name: "librecovery_defaults",
+libaosprecovery_defaults {
+    name: "libaosprecovery_defaults"
+}
 
-    defaults: [
-        "recovery_defaults",
+cc_library_shared {
+    name: "libaosprecovery",
+    defaults: ["libaosprecovery_defaults"],
+    cflags: [
+        "-std=gnu++2a",
+        "-DRECOVERY_API_VERSION=3"
     ],
-
+    include_dirs: [
+        "bootable/recovery/install/include",
+        "bootable/recovery/recovery_ui/include",
+        "bootable/recovery/recovery_utils/include",
+        "bootable/recovery/otautil/include",
+        "bootable/recovery/minadbd",
+        "bootable/recovery/minadbd/include",
+        "bootable/recovery/minzip",
+        "bootable/recovery/twrpinstall/include",
+        "system/libvintf/include"
+    ],
+    srcs: [
+        "install/adb_install.cpp",
+        "install/asn1_decoder.cpp",
+        "install/get_args.cpp", 
+        "install/install.cpp",
+        "install/package.cpp",
+        "install/verifier.cpp",
+        "install/wipe_data.cpp",
+        "install/set_metadata.cpp",
+        "install/ZipUtil.cpp"
+    ],
     shared_libs: [
-        "android.hardware.boot@1.0",
-        "android.hardware.boot@1.1",
         "libbase",
         "libbootloader_message",
         "libcrypto",
-        "libcutils",
-        "libfs_mgr",
-        "liblp",
+        "libext4_utils",
+        "libfs_mgr", 
+        "libfusesideload", 
+        "libhidl-gen-utils",
+        "libhidlbase",
         "liblog",
-        "libprotobuf-cpp-lite",
+        "libselinux",
+        "libtinyxml2",
+        "libutils",
+        "libz",
         "libziparchive",
+        "libcutils",
+        "libc++"
     ],
-
     static_libs: [
-        "libc++fs",
-        "libinstall",
-        "librecovery_fastboot",
-        "libminui",
-        "librecovery_utils",
         "libotautil",
-        "libsnapshot_nobinder",
-    ],
-}
-
-cc_library_static {
-    name: "librecovery",
-    recovery_available: true,
-
-    defaults: [
-        "librecovery_defaults",
-    ],
-
-    srcs: [
-        "recovery.cpp",
-    ],
-
-    shared_libs: [
-        "librecovery_ui",
-    ],
-}
-
-prebuilt_etc {
-    name: "init_recovery.rc",
-    filename: "init.rc",
-    src: "etc/init.rc",
-    sub_dir: "init/hw",
-    recovery: true,
-}
-
-cc_binary {
-    name: "recovery",
-    recovery: true,
-
-    defaults: [
-        "libinstall_defaults",
-        "librecovery_defaults",
-        "librecovery_utils_defaults",
-    ],
-
-    srcs: [
-        "recovery_main.cpp",
-    ],
-
-    shared_libs: [
-        "librecovery_ui",
-    ],
-
-    static_libs: [
-        "librecovery",
-        "librecovery_ui_default",
-    ],
-
-    required: [
-        "e2fsdroid.recovery",
-        "init_recovery.rc",
-        "librecovery_ui_ext",
-        "minadbd",
-        "mke2fs.conf.recovery",
-        "mke2fs.recovery",
-        "recovery_deps",
-        "ueventd.rc.recovery",
-    ],
-}
-
-// The dynamic executable that runs after /data mounts.
-cc_binary {
-    name: "recovery-persist",
-
-    defaults: [
-        "recovery_defaults",
-    ],
-
-    srcs: [
-        "recovery-persist.cpp",
-    ],
-
-    shared_libs: [
-        "libbase",
-        "liblog",
-    ],
-
-    static_libs: [
+        "libvintf", 
+        "libhidl-gen-utils",
         "librecovery_utils",
-    ],
-
-    init_rc: [
-        "recovery-persist.rc",
-    ],
+        "libc++fs"
+    ]
 }
 
-// The dynamic executable that runs at init.
-cc_binary {
-    name: "recovery-refresh",
-
-    defaults: [
-        "recovery_defaults",
-    ],
-
-    srcs: [
-        "recovery-refresh.cpp",
-    ],
-
-    shared_libs: [
-        "libbase",
-        "liblog",
-    ],
-
-    static_libs: [
-        "librecovery_utils",
-    ],
-
-    init_rc: [
-        "recovery-refresh.rc",
-    ],
-}
-
-filegroup {
-    name: "res-testdata",
-
-    srcs: [
-        "res-*/images/*_text.png",
-    ],
-}
diff --git a/Android.mk b/Android.mk
old mode 100644
new mode 100755
index 9f69153..f81d023
--- a/Android.mk
+++ b/Android.mk
@@ -13,66 +13,633 @@
 # limitations under the License.
 
 LOCAL_PATH := $(call my-dir)
+commands_TWRP_local_path := $(LOCAL_PATH)
 
-# Needed by build/make/core/Makefile. Must be consistent with the value in Android.bp.
+ifneq ($(project-path-for),)
+    ifeq ($(LOCAL_PATH),$(call project-path-for,recovery))
+        PROJECT_PATH_AGREES := true
+        BOARD_SEPOLICY_DIRS += $(call project-path-for,recovery)/sepolicy
+    endif
+else
+    ifeq ($(LOCAL_PATH),bootable/recovery)
+        PROJECT_PATH_AGREES := true
+        BOARD_SEPOLICY_DIRS += bootable/recovery/sepolicy
+    else
+        ifeq ($(LOCAL_PATH),bootable/recovery-twrp)
+            ifeq ($(RECOVERY_VARIANT),twrp)
+                PROJECT_PATH_AGREES := true
+                BOARD_SEPOLICY_DIRS += bootable/recovery-twrp/sepolicy
+            endif
+        endif
+    endif
+endif
+
+ifeq ($(PROJECT_PATH_AGREES),true)
+
+ifeq ($(CM_PLATFORM_SDK_VERSION),)
+    CM_PLATFORM_SDK_VERSION := 0
+endif
+
+include $(CLEAR_VARS)
+
+TWRES_PATH := /twres/
+TWHTCD_PATH := $(TWRES_PATH)htcd/
+
+TARGET_RECOVERY_GUI := true
+
+LOCAL_STATIC_LIBRARIES :=
+LOCAL_SHARED_LIBRARIES :=
+
+ifneq ($(TW_DEVICE_VERSION),)
+    LOCAL_CFLAGS += -DTW_DEVICE_VERSION='"-$(TW_DEVICE_VERSION)"'
+else
+    LOCAL_CFLAGS += -DTW_DEVICE_VERSION='"-0"'
+endif
+LOCAL_CFLAGS += -DPLATFORM_SDK_VERSION=$(PLATFORM_SDK_VERSION)
+
+LOCAL_SRC_FILES := \
+    twrp.cpp \
+    fixContexts.cpp \
+    twrpTar.cpp \
+    exclude.cpp \
+    find_file.cpp \
+    infomanager.cpp \
+    data.cpp \
+    partition.cpp \
+    partitionmanager.cpp \
+    progresstracking.cpp \
+    startupArgs.cpp \
+    twrp-functions.cpp \
+    twrpDigestDriver.cpp \
+    openrecoveryscript.cpp \
+    tarWrite.c \
+    twrpAdbBuFifo.cpp \
+    twrpApex.cpp \
+    twrpRepacker.cpp
+
+LOCAL_STATIC_LIBRARIES += libavb libtwrpinstall libminadbd_services libinit
+LOCAL_SHARED_LIBRARIES += libfs_mgr
+LOCAL_C_INCLUDES += \
+    system/core/fs_mgr/libfs_avb/include/ \
+    system/core/fs_mgr/include_fstab/ \
+    system/core/fs_mgr/include/ \
+    system/core/fs_mgr/libdm/include/ \
+    system/core/fs_mgr/liblp/include/ \
+    system/gsid/include/ \
+    system/core/init/ \
+    system/extras/ext4_utils/include \
+    $(LOCAL_PATH)/twinstall/include
+
+ifneq ($(TARGET_RECOVERY_REBOOT_SRC),)
+  LOCAL_SRC_FILES += $(TARGET_RECOVERY_REBOOT_SRC)
+endif
+
+LOCAL_MODULE := recovery
+
 RECOVERY_API_VERSION := 3
 RECOVERY_FSTAB_VERSION := 2
+LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION)
+LOCAL_CFLAGS += -Wno-unused-parameter -Wno-unused-function
+LOCAL_CLANG := true
 
-# TARGET_RECOVERY_UI_LIB should be one of librecovery_ui_{default,wear,vr,ethernet} or a
-# device-specific module that defines make_device() and the exact RecoveryUI class for the
-# target. It defaults to librecovery_ui_default, which uses ScreenRecoveryUI.
-TARGET_RECOVERY_UI_LIB ?= librecovery_ui_default
+LOCAL_C_INCLUDES += \
+    bionic \
+    system/vold \
+    system/extras \
+    system/core/adb \
+    system/core/libsparse \
+    external/zlib \
+    system/core/libpixelflinger/include \
+    system/core/libziparchive/include \
+    external/freetype/include \
+    external/boringssl/include \
+    external/libcxx/include \
+    external/libselinux/include \
+    $(LOCAL_PATH)/recovery_ui/include \
+    $(LOCAL_PATH)/otautil/include \
+    $(LOCAL_PATH)/install/include \
+    $(LOCAL_PATH)/fuse_sideload/include \
+    $(LOCAL_PATH)/install/include \
+    $(LOCAL_PATH)/twrpinstall/include \
+    $(LOCAL_PATH)/recovery_utils/include
 
-# librecovery_ui_ext (shared library)
-# ===================================
-include $(CLEAR_VARS)
+LOCAL_STATIC_LIBRARIES += libguitwrp
+LOCAL_SHARED_LIBRARIES += libz libc libcutils libstdc++ libtar libblkid libminuitwrp libmtdutils libtwadbbu 
+LOCAL_SHARED_LIBRARIES += libbootloader_message libcrecovery libtwrpdigest libc++ libaosprecovery libcrypto libbase 
+LOCAL_SHARED_LIBRARIES += libziparchive libselinux
 
-LOCAL_MODULE := librecovery_ui_ext
+ifneq ($(wildcard system/core/libsparse/Android.mk),)
+LOCAL_SHARED_LIBRARIES += libsparse
+endif
 
-# LOCAL_MODULE_PATH for shared libraries is unsupported in multiarch builds.
-LOCAL_MULTILIB := first
+ifeq ($(TW_OEM_BUILD),true)
+    LOCAL_CFLAGS += -DTW_OEM_BUILD
+    BOARD_HAS_NO_REAL_SDCARD := true
+    TW_USE_TOOLBOX := true
+    TW_EXCLUDE_MTP := true
+endif
 
-ifeq ($(TARGET_IS_64_BIT),true)
-LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/lib64
+ifeq ($(AB_OTA_UPDATER),true)
+    LOCAL_CFLAGS += -DAB_OTA_UPDATER=1
+    LOCAL_SHARED_LIBRARIES += libhardware android.hardware.boot@1.0
+    TWRP_REQUIRED_MODULES += libhardware android.hardware.boot@1.0-service android.hardware.boot@1.0-service.rc
+endif
+
+ifeq ($(PRODUCT_USE_DYNAMIC_PARTITIONS),true)
+    LOCAL_CFLAGS += -DPRODUCT_USE_DYNAMIC_PARTITIONS=1
+    TWRP_REQUIRED_MODULES += android.hardware.health@2.0-service android.hardware.health@2.0-service.rc
+endif
+
+ifeq ($(TW_USES_VENDOR_LIBS),true)
+    LOCAL_CFLAGS += -DUSE_VENDOR_LIBS=1
+endif
+
+ifneq ($(BOARD_GOOGLE_DYNAMIC_PARTITIONS_PARTITION_LIST),)
+	BOARD_DYNAMIC_PARTITIONS_PARTITION_LIST := $(BOARD_GOOGLE_DYNAMIC_PARTITIONS_PARTITION_LIST)
+else ifneq ($(BOARD_QTI_DYNAMIC_PARTITIONS_PARTITION_LIST),)
+	BOARD_DYNAMIC_PARTITIONS_PARTITION_LIST := $(BOARD_QTI_DYNAMIC_PARTITIONS_PARTITION_LIST)
+else ifneq ($(BOARD_SAMSUNG_DYNAMIC_PARTITIONS_PARTITION_LIST),)
+	BOARD_DYNAMIC_PARTITIONS_PARTITION_LIST := $(BOARD_SAMSUNG_DYNAMIC_PARTITIONS_PARTITION_LIST)
+endif
+ifneq ($(BOARD_DYNAMIC_PARTITIONS_PARTITION_LIST),)
+	LOCAL_CFLAGS += "-DBOARD_DYNAMIC_PARTITIONS_PARTITION_LIST=\"$(shell echo $(BOARD_DYNAMIC_PARTITIONS_PARTITION_LIST) | sed -r 's/\b(.)/\u\1/g' | sed -e 's/ \+/, /g')\""
+endif
+
+ifeq ($(TW_NO_BIND_SYSTEM),true)
+    LOCAL_CFLAGS += -DTW_NO_BIND_SYSTEM
+endif
+
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/bin
+
+ifeq ($(TARGET_RECOVERY_TWRP_LIB),)
+    LOCAL_SRC_FILES += BasePartition.cpp
 else
-LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/lib
+    LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_TWRP_LIB)
 endif
 
-LOCAL_WHOLE_STATIC_LIBRARIES := \
-    $(TARGET_RECOVERY_UI_LIB)
+LOCAL_C_INCLUDES += system/extras/ext4_utils
 
-LOCAL_SHARED_LIBRARIES := \
-    libbase \
-    liblog \
-    librecovery_ui.recovery
+tw_git_revision := $(shell git -C $(LOCAL_PATH) rev-parse --short=8 HEAD 2>/dev/null)
+ifeq ($(shell git -C $(LOCAL_PATH) diff --quiet; echo $$?),1)
+    tw_git_revision := $(tw_git_revision)-dirty
+endif
+LOCAL_CFLAGS += -DTW_GIT_REVISION='"$(tw_git_revision)"'
 
-include $(BUILD_SHARED_LIBRARY)
+ifeq ($(TW_FORCE_USE_BUSYBOX), true)
+    TW_USE_TOOLBOX := false
+else
+    TW_USE_TOOLBOX := true
+endif
+ifeq ($(TW_EXCLUDE_MTP),)
+    LOCAL_SHARED_LIBRARIES += libtwrpmtp-ffs
+endif
 
-# recovery_deps: A phony target that's depended on by `recovery`, which
-# builds additional modules conditionally based on Makefile variables.
-# ======================================================================
+#TWRP Build Flags
+ifeq ($(TW_EXCLUDE_MTP),)
+    LOCAL_CFLAGS += -DTW_HAS_MTP
+endif
+ifneq ($(TW_NO_SCREEN_TIMEOUT),)
+    LOCAL_CFLAGS += -DTW_NO_SCREEN_TIMEOUT
+endif
+ifeq ($(BOARD_HAS_NO_REAL_SDCARD), true)
+    LOCAL_CFLAGS += -DBOARD_HAS_NO_REAL_SDCARD
+endif
+ifneq ($(RECOVERY_SDCARD_ON_DATA),)
+	LOCAL_CFLAGS += -DRECOVERY_SDCARD_ON_DATA
+endif
+ifneq ($(TW_INCLUDE_DUMLOCK),)
+	LOCAL_CFLAGS += -DTW_INCLUDE_DUMLOCK
+endif
+ifneq ($(TW_INTERNAL_STORAGE_PATH),)
+	LOCAL_CFLAGS += -DTW_INTERNAL_STORAGE_PATH=$(TW_INTERNAL_STORAGE_PATH)
+endif
+ifneq ($(TW_INTERNAL_STORAGE_MOUNT_POINT),)
+	LOCAL_CFLAGS += -DTW_INTERNAL_STORAGE_MOUNT_POINT=$(TW_INTERNAL_STORAGE_MOUNT_POINT)
+endif
+ifneq ($(TW_EXTERNAL_STORAGE_PATH),)
+	LOCAL_CFLAGS += -DTW_EXTERNAL_STORAGE_PATH=$(TW_EXTERNAL_STORAGE_PATH)
+endif
+ifneq ($(TW_EXTERNAL_STORAGE_MOUNT_POINT),)
+	LOCAL_CFLAGS += -DTW_EXTERNAL_STORAGE_MOUNT_POINT=$(TW_EXTERNAL_STORAGE_MOUNT_POINT)
+endif
+ifeq ($(TW_HAS_NO_BOOT_PARTITION), true)
+    LOCAL_CFLAGS += -DTW_HAS_NO_BOOT_PARTITION
+endif
+ifeq ($(TW_NO_REBOOT_BOOTLOADER), true)
+    LOCAL_CFLAGS += -DTW_NO_REBOOT_BOOTLOADER
+endif
+ifeq ($(TW_NO_REBOOT_RECOVERY), true)
+    LOCAL_CFLAGS += -DTW_NO_REBOOT_RECOVERY
+endif
+ifeq ($(TW_NO_BATT_PERCENT), true)
+    LOCAL_CFLAGS += -DTW_NO_BATT_PERCENT
+endif
+ifeq ($(TW_NO_CPU_TEMP), true)
+    LOCAL_CFLAGS += -DTW_NO_CPU_TEMP
+endif
+ifneq ($(TW_CUSTOM_POWER_BUTTON),)
+	LOCAL_CFLAGS += -DTW_CUSTOM_POWER_BUTTON=$(TW_CUSTOM_POWER_BUTTON)
+endif
+ifeq ($(TW_ALWAYS_RMRF), true)
+    LOCAL_CFLAGS += -DTW_ALWAYS_RMRF
+endif
+ifeq ($(TW_NEVER_UNMOUNT_SYSTEM), true)
+    LOCAL_CFLAGS += -DTW_NEVER_UNMOUNT_SYSTEM
+endif
+ifeq ($(TW_NO_USB_STORAGE), true)
+    LOCAL_CFLAGS += -DTW_NO_USB_STORAGE
+endif
+ifeq ($(TW_INCLUDE_INJECTTWRP), true)
+    LOCAL_CFLAGS += -DTW_INCLUDE_INJECTTWRP
+endif
+ifeq ($(TW_INCLUDE_BLOBPACK), true)
+    LOCAL_CFLAGS += -DTW_INCLUDE_BLOBPACK
+endif
+ifneq ($(TARGET_USE_CUSTOM_LUN_FILE_PATH),)
+    LOCAL_CFLAGS += -DCUSTOM_LUN_FILE=\"$(TARGET_USE_CUSTOM_LUN_FILE_PATH)\"
+endif
+ifneq ($(BOARD_UMS_LUNFILE),)
+    LOCAL_CFLAGS += -DCUSTOM_LUN_FILE=\"$(BOARD_UMS_LUNFILE)\"
+endif
+ifeq ($(TW_HAS_DOWNLOAD_MODE), true)
+    LOCAL_CFLAGS += -DTW_HAS_DOWNLOAD_MODE
+endif
+ifeq ($(TW_HAS_EDL_MODE), true)
+    LOCAL_CFLAGS += -DTW_HAS_EDL_MODE
+endif
+ifeq ($(TW_NO_SCREEN_BLANK), true)
+    LOCAL_CFLAGS += -DTW_NO_SCREEN_BLANK
+endif
+ifeq ($(TW_SDEXT_NO_EXT4), true)
+    LOCAL_CFLAGS += -DTW_SDEXT_NO_EXT4
+endif
+ifeq ($(TW_FORCE_CPUINFO_FOR_DEVICE_ID), true)
+    LOCAL_CFLAGS += -DTW_FORCE_CPUINFO_FOR_DEVICE_ID
+endif
+ifeq ($(TW_NO_EXFAT_FUSE), true)
+    LOCAL_CFLAGS += -DTW_NO_EXFAT_FUSE
+endif
+ifeq ($(TW_NO_HAPTICS), true)
+    LOCAL_CFLAGS += -DTW_NO_HAPTICS
+endif
+ifeq ($(TW_INCLUDE_JB_CRYPTO), true)
+    TW_INCLUDE_CRYPTO := true
+endif
+ifeq ($(TW_INCLUDE_L_CRYPTO), true)
+    TW_INCLUDE_CRYPTO := true
+endif
+ifeq ($(TW_INCLUDE_CRYPTO), true)
+    LOCAL_CFLAGS += -DTW_INCLUDE_CRYPTO -DUSE_FSCRYPT -Wno-macro-redefined
+    # LOCAL_SHARED_LIBRARIES += libcryptfsfde
+    LOCAL_SHARED_LIBRARIES += libgpt_twrp
+    LOCAL_C_INCLUDES += external/boringssl/src/include bootable/recovery/crypto/fscrypt \
+        bootable/recovery/crypto
+    TW_INCLUDE_CRYPTO_FBE := true
+    LOCAL_CFLAGS += -DTW_INCLUDE_FBE
+    LOCAL_SHARED_LIBRARIES += libtwrpfscrypt android.frameworks.stats@1.0 android.hardware.authsecret@1.0 \
+        android.hardware.oemlock@1.0
+    LOCAL_CFLAGS += -DTW_INCLUDE_FBE_METADATA_DECRYPT
+    ifneq ($(TW_CRYPTO_USE_SYSTEM_VOLD),)
+    ifneq ($(TW_CRYPTO_USE_SYSTEM_VOLD),false)
+		TW_INCLUDE_LIBRESETPROP := true
+        LOCAL_CFLAGS += -DTW_CRYPTO_USE_SYSTEM_VOLD
+        LOCAL_STATIC_LIBRARIES += libvolddecrypt
+    endif
+    endif
+endif
+WITH_CRYPTO_UTILS := \
+    $(if $(wildcard system/core/libcrypto_utils/android_pubkey.c),true)
+ifeq ($(TW_USE_MODEL_HARDWARE_ID_FOR_DEVICE_ID), true)
+    LOCAL_CFLAGS += -DTW_USE_MODEL_HARDWARE_ID_FOR_DEVICE_ID
+endif
+ifeq ($(TW_USE_SERIALNO_PROPERTY_FOR_DEVICE_ID), true)
+    LOCAL_CFLAGS += -DTW_USE_SERIALNO_PROPERTY_FOR_DEVICE_ID
+endif
+ifneq ($(TW_BRIGHTNESS_PATH),)
+	LOCAL_CFLAGS += -DTW_BRIGHTNESS_PATH=$(TW_BRIGHTNESS_PATH)
+endif
+ifneq ($(TW_SECONDARY_BRIGHTNESS_PATH),)
+	LOCAL_CFLAGS += -DTW_SECONDARY_BRIGHTNESS_PATH=$(TW_SECONDARY_BRIGHTNESS_PATH)
+endif
+ifneq ($(TW_MAX_BRIGHTNESS),)
+	LOCAL_CFLAGS += -DTW_MAX_BRIGHTNESS=$(TW_MAX_BRIGHTNESS)
+endif
+ifneq ($(TW_DEFAULT_BRIGHTNESS),)
+	LOCAL_CFLAGS += -DTW_DEFAULT_BRIGHTNESS=$(TW_DEFAULT_BRIGHTNESS)
+endif
+ifneq ($(TW_CUSTOM_BATTERY_PATH),)
+	LOCAL_CFLAGS += -DTW_CUSTOM_BATTERY_PATH=$(TW_CUSTOM_BATTERY_PATH)
+endif
+ifneq ($(TW_CUSTOM_CPU_TEMP_PATH),)
+	LOCAL_CFLAGS += -DTW_CUSTOM_CPU_TEMP_PATH=$(TW_CUSTOM_CPU_TEMP_PATH)
+endif
+ifneq ($(TW_EXCLUDE_ENCRYPTED_BACKUPS), true)
+    LOCAL_SHARED_LIBRARIES += libopenaes
+else
+    LOCAL_CFLAGS += -DTW_EXCLUDE_ENCRYPTED_BACKUPS
+endif
+ifeq ($(TARGET_RECOVERY_QCOM_RTC_FIX),)
+  ifneq ($(filter msm8226 msm8x26 msm8610 msm8974 msm8x74 msm8084 msm8x84 apq8084 msm8909 msm8916 msm8992 msm8994 msm8952 msm8996 msm8937 msm8953 msm8998,$(TARGET_BOARD_PLATFORM)),)
+    LOCAL_CFLAGS += -DQCOM_RTC_FIX
+  else ifeq ($(TARGET_CPU_VARIANT),krait)
+    LOCAL_CFLAGS += -DQCOM_RTC_FIX
+  endif
+else ifeq ($(TARGET_RECOVERY_QCOM_RTC_FIX),true)
+    LOCAL_CFLAGS += -DQCOM_RTC_FIX
+endif
+ifneq ($(TW_NO_LEGACY_PROPS),)
+	LOCAL_CFLAGS += -DTW_NO_LEGACY_PROPS
+endif
+ifneq ($(wildcard bionic/libc/include/sys/capability.h),)
+    LOCAL_CFLAGS += -DHAVE_CAPABILITIES
+endif
+ifneq ($(TARGET_RECOVERY_INITRC),)
+    TW_EXCLUDE_DEFAULT_USB_INIT := true
+endif
+LOCAL_CFLAGS += -DTW_USE_NEW_MINADBD
+ifneq ($(TW_DEFAULT_LANGUAGE),)
+    LOCAL_CFLAGS += -DTW_DEFAULT_LANGUAGE=$(TW_DEFAULT_LANGUAGE)
+else
+    LOCAL_CFLAGS += -DTW_DEFAULT_LANGUAGE=en
+endif
+ifneq ($(TW_CLOCK_OFFSET),)
+	LOCAL_CFLAGS += -DTW_CLOCK_OFFSET=$(TW_CLOCK_OFFSET)
+endif
+ifneq ($(TW_OVERRIDE_SYSTEM_PROPS),)
+    TW_INCLUDE_LIBRESETPROP := true
+    LOCAL_CFLAGS += -DTW_OVERRIDE_SYSTEM_PROPS=$(TW_OVERRIDE_SYSTEM_PROPS)
+endif
+ifneq ($(TW_INCLUDE_LIBRESETPROP),)
+    LOCAL_SHARED_LIBRARIES += libresetprop
+    LOCAL_C_INCLUDES += external/magisk-prebuilt/include
+    LOCAL_CFLAGS += -DTW_INCLUDE_LIBRESETPROP
+endif
+
+TWRP_REQUIRED_MODULES += \
+    relink_libraries \
+    relink_binaries \
+    twrp_ramdisk \
+    dump_image \
+    erase_image \
+    flash_image \
+    mke2fs.conf \
+    pigz \
+    teamwin \
+    twrp \
+    fsck.fat \
+    fatlabel \
+    mkfs.fat \
+    permissive.sh \
+    simg2img_twrp \
+    libbootloader_message \
+    init.recovery.hlthchrg.rc \
+    init.recovery.service.rc \
+    init.recovery.ldconfig.rc \
+    awk \
+    toybox \
+    toolbox \
+    mkshrc_twrp \
+    plat_hwservice_contexts \
+    vendor_hwservice_contexts \
+    minadbd \
+    twrpbu
+
+ifneq ($(TW_INCLUDE_CRYPTO),)
+TWRP_REQUIRED_MODULES += \
+    hwservicemanager \
+    hwservicemanager.rc \
+    vndservicemanager \
+    vndservicemanager.rc \
+    vold_prepare_subdirs \
+    task_recovery_profiles.json \
+    fscryptpolicyget
+    ifneq ($(TW_INCLUDE_CRYPTO_FBE),)
+    TWRP_REQUIRED_MODULES += \
+        plat_service_contexts \
+        servicemanager \
+        servicemanager.rc
+    endif
+endif
+
+ifneq ($(wildcard external/zip/Android.mk),)
+    TWRP_REQUIRED_MODULES += zip
+endif
+ifneq ($(wildcard external/unzip/Android.mk),)
+    TWRP_REQUIRED_MODULES += unzip
+endif
+
+ifneq ($(TW_NO_EXFAT), true)
+    TWRP_REQUIRED_MODULES += mkexfatfs fsckexfat
+    ifneq ($(TW_NO_EXFAT_FUSE), true)
+        TWRP_REQUIRED_MODULES += exfat-fuse
+    endif
+endif
+ifeq ($(BOARD_HAS_NO_REAL_SDCARD),)
+    TWRP_REQUIRED_MODULES += sgdisk
+endif
+ifneq ($(TW_EXCLUDE_ENCRYPTED_BACKUPS), true)
+    TWRP_REQUIRED_MODULES += openaes openaes_license
+endif
+ifeq ($(TW_INCLUDE_DUMLOCK), true)
+    TWRP_REQUIRED_MODULES += \
+        htcdumlock htcdumlocksys flash_imagesys dump_imagesys libbmlutils.so \
+        libflashutils.so libmmcutils.so libmtdutils.so HTCDumlock.apk
+endif
+ifeq ($(TW_INCLUDE_FB2PNG), true)
+    TWRP_REQUIRED_MODULES += fb2png
+endif
+ifneq ($(TW_OEM_BUILD),true)
+    TWRP_REQUIRED_MODULES += orscmd
+endif
+ifeq ($(BOARD_USES_BML_OVER_MTD),true)
+    TWRP_REQUIRED_MODULES += bml_over_mtd
+endif
+ifeq ($(TW_INCLUDE_INJECTTWRP), true)
+    TWRP_REQUIRED_MODULES += injecttwrp
+endif
+ifneq ($(TW_EXCLUDE_DEFAULT_USB_INIT), true)
+    TWRP_REQUIRED_MODULES += init.recovery.usb.rc
+endif
+ifeq ($(TWRP_INCLUDE_LOGCAT), true)
+    TWRP_REQUIRED_MODULES += logcat event-log-tags
+    ifeq ($(TARGET_USES_LOGD), true)
+        TWRP_REQUIRED_MODULES += logd libsysutils libnl init.recovery.logd.rc
+    endif
+endif
+# Allow devices to specify device-specific recovery dependencies
+ifneq ($(TARGET_RECOVERY_DEVICE_MODULES),)
+    TWRP_REQUIRED_MODULES += $(TARGET_RECOVERY_DEVICE_MODULES)
+endif
+LOCAL_CFLAGS += -DTWRES=\"$(TWRES_PATH)\"
+LOCAL_CFLAGS += -DTWHTCD_PATH=\"$(TWHTCD_PATH)\"
+ifeq ($(TW_INCLUDE_NTFS_3G),true)
+    TWRP_REQUIRED_MODULES += \
+        mount.ntfs \
+        fsck.ntfs \
+        mkfs.ntfs
+endif
+ifeq ($(TARGET_USERIMAGES_USE_F2FS), true)
+    TWRP_REQUIRED_MODULES += sload.f2fs \
+        libfs_mgr \
+        fs_mgr \
+        libinit
+endif
+
+TWRP_REQUIRED_MODULES += file_contexts_text
+
+ifeq ($(BOARD_CACHEIMAGE_PARTITION_SIZE),)
+    TWRP_REQUIRED_MODULES += recovery-persist recovery-refresh
+endif
+
+LOCAL_REQUIRED_MODULES += $(TWRP_REQUIRED_MODULES)
+
+include $(BUILD_EXECUTABLE)
+
+# Symlink for file_contexts
 include $(CLEAR_VARS)
 
-LOCAL_MODULE := recovery_deps
+LOCAL_MODULE := file_contexts_text
+LOCAL_MODULE_TAGS := optional
+LOCAL_REQUIRED_MODULES := file_contexts.bin
 
-ifeq ($(TARGET_USERIMAGES_USE_F2FS),true)
-ifeq ($(HOST_OS),linux)
-LOCAL_REQUIRED_MODULES += \
-    make_f2fs.recovery \
-    sload_f2fs.recovery
-endif
-endif
-
-# On A/B devices recovery-persist reads the recovery related file from the persist storage and
-# copies them into /data/misc/recovery. Then, for both A/B and non-A/B devices, recovery-persist
-# parses the last_install file and reports the embedded update metrics. Also, the last_install file
-# will be deteleted after the report.
-LOCAL_REQUIRED_MODULES += recovery-persist
-ifeq ($(BOARD_CACHEIMAGE_PARTITION_SIZE),)
-LOCAL_REQUIRED_MODULES += recovery-refresh
-endif
+LOCAL_POST_INSTALL_CMD := \
+    $(hide) cp -f $(PRODUCT_OUT)/obj/ETC/file_contexts.bin_intermediates/file_contexts.concat.tmp $(TARGET_RECOVERY_ROOT_OUT)/file_contexts
 
 include $(BUILD_PHONY_PACKAGE)
 
+# recovery-persist (system partition dynamic executable run after /data mounts)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+    recovery-persist.cpp 
+LOCAL_MODULE := recovery-persist
+LOCAL_SHARED_LIBRARIES := liblog libbase 
+LOCAL_STATIC_LIBRARIES := libotautil librecovery_utils
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/otautil/include
+LOCAL_C_INCLUDES += system/core/libstats/include
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/recovery_utils/include
+LOCAL_CFLAGS := -Werror
+LOCAL_INIT_RC := recovery-persist.rc
+include $(BUILD_EXECUTABLE)
+
+# recovery-refresh (system partition dynamic executable run at init)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+    recovery-refresh.cpp
+LOCAL_MODULE := recovery-refresh
+LOCAL_SHARED_LIBRARIES := liblog libbase
+LOCAL_STATIC_LIBRARIES := libotautil librecovery_utils
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/otautil/include
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/recovery_utils/include
+LOCAL_CFLAGS := -Werror
+LOCAL_INIT_RC := recovery-refresh.rc
+include $(BUILD_EXECUTABLE)
+
+# libmounts (static library)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := mounts.cpp
+LOCAL_CFLAGS := \
+    -Wall \
+    -Werror
+LOCAL_MODULE := libmounts
+LOCAL_STATIC_LIBRARIES := libbase
+include $(BUILD_STATIC_LIBRARY)
+
+# librecovery (static library)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+    install.cpp
+LOCAL_CFLAGS := -Wall -Werror
+LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION)
+
+ifeq ($(AB_OTA_UPDATER),true)
+    LOCAL_CFLAGS += -DAB_OTA_UPDATER=1
+endif
+
+LOCAL_MODULE := librecovery
+LOCAL_STATIC_LIBRARIES := \
+    libminui \
+    libotautil \
+    libvintf \
+    libcrypto_utils \
+    libcrypto \
+    libbase \
+    libziparchive \
+
+include $(BUILD_STATIC_LIBRARY)
+
+commands_recovery_local_path := $(LOCAL_PATH)
+
 include \
-    $(LOCAL_PATH)/updater/Android.mk \
+    $(commands_TWRP_local_path)/updater/Android.mk
+
+include $(commands_TWRP_local_path)/mtp/ffs/Android.mk \
+    $(commands_TWRP_local_path)/minui/Android.mk
+
+#includes for TWRP
+include $(commands_TWRP_local_path)/injecttwrp/Android.mk \
+    $(commands_TWRP_local_path)/htcdumlock/Android.mk \
+    $(commands_TWRP_local_path)/mmcutils/Android.mk \
+    $(commands_TWRP_local_path)/bmlutils/Android.mk \
+    $(commands_TWRP_local_path)/prebuilt/Android.mk \
+    $(commands_TWRP_local_path)/mtdutils/Android.mk \
+    $(commands_TWRP_local_path)/flashutils/Android.mk \
+    $(commands_TWRP_local_path)/pigz/Android.mk \
+    $(commands_TWRP_local_path)/libtar/Android.mk \
+    $(commands_TWRP_local_path)/libcrecovery/Android.mk \
+    $(commands_TWRP_local_path)/libblkid/Android.mk \
+    $(commands_TWRP_local_path)/openaes/Android.mk \
+    $(commands_TWRP_local_path)/twrpTarMain/Android.mk \
+    $(commands_TWRP_local_path)/minzip/Android.mk \
+    $(commands_TWRP_local_path)/dosfstools/Android.mk \
+    $(commands_TWRP_local_path)/etc/Android.mk \
+    $(commands_TWRP_local_path)/simg2img/Android.mk \
+    $(commands_TWRP_local_path)/adbbu/Android.mk \
+    $(commands_TWRP_local_path)/twrpDigest/Android.mk \
+    $(commands_TWRP_local_path)/attr/Android.mk
+
+ifneq ($(TW_OZIP_DECRYPT_KEY),)
+    TWRP_REQUIRED_MODULES += ozip_decrypt
+    include $(commands_TWRP_local_path)/ozip_decrypt/Android.mk
+endif
+
+ifeq ($(TW_INCLUDE_CRYPTO), true)
+    # include $(commands_TWRP_local_path)/crypto/fde/Android.mk
+    include $(commands_TWRP_local_path)/crypto/scrypt/Android.mk
+    ifeq ($(TW_INCLUDE_CRYPTO_FBE), true)
+        include $(commands_TWRP_local_path)/crypto/fscrypt/Android.mk
+    endif
+    ifneq ($(TW_CRYPTO_USE_SYSTEM_VOLD),)
+    ifneq ($(TW_CRYPTO_USE_SYSTEM_VOLD),false)
+        include $(commands_TWRP_local_path)/crypto/vold_decrypt/Android.mk
+    endif
+    endif
+    include $(commands_TWRP_local_path)/gpt/Android.mk
+endif
+ifeq ($(BUILD_ID), GINGERBREAD)
+    TW_NO_EXFAT := true
+endif
+ifneq ($(TW_NO_EXFAT), true)
+    include $(commands_TWRP_local_path)/exfat/mkfs/Android.mk \
+            $(commands_TWRP_local_path)/exfat/fsck/Android.mk \
+            $(commands_TWRP_local_path)/fuse/Android.mk \
+            $(commands_TWRP_local_path)/exfat/libexfat/Android.mk
+    ifneq ($(TW_NO_EXFAT_FUSE), true)
+        include $(commands_TWRP_local_path)/exfat/fuse/Android.mk
+    endif
+endif
+ifneq ($(TW_OEM_BUILD),true)
+    include $(commands_TWRP_local_path)/orscmd/Android.mk
+endif
+
+# FB2PNG
+ifeq ($(TW_INCLUDE_FB2PNG), true)
+    include $(commands_TWRP_local_path)/fb2png/Android.mk
+endif
+
+endif
+
+commands_TWRP_local_path :=
diff --git a/BasePartition.cpp b/BasePartition.cpp
new file mode 100644
index 0000000..3b34d75
--- /dev/null
+++ b/BasePartition.cpp
@@ -0,0 +1,28 @@
+/*
+	Copyright 2018 TeamWin
+	This file is part of TWRP/TeamWin Recovery Project.
+
+	TWRP is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+
+	TWRP is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "partitions.hpp"
+
+// On devices that need it, this is supposed to be overridden using
+// TARGET_RECOVERY_TWRP_LIB to allow device-specific pre and post
+// wipe encryption calls.
+
+BasePartition* make_partition() {
+  return new BasePartition();
+}
diff --git a/NOTICE b/NOTICE
index c5b1efa..346e71a 100644
--- a/NOTICE
+++ b/NOTICE
@@ -1,190 +1,634 @@
 
-   Copyright (c) 2005-2008, The Android Open Source Project
+    Copyright (c) 2011-2016, Dees_Troy, bigbiff, Team Win
 
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
 
-   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 program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
 
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
 
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
 
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+                            Preamble
 
-   1. Definitions.
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
 
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
 
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
 
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
 
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
 
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
 
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
 
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
 
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
 
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
+  The precise terms and conditions for copying, distribution and
+modification follow.
 
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
+                       TERMS AND CONDITIONS
 
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
+  0. Definitions.
 
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
+  "This License" refers to version 3 of the GNU General Public License.
 
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
 
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
 
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
 
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
 
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
 
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
 
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
 
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
+  1. Source Code.
 
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
 
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
 
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
 
-   END OF TERMS AND CONDITIONS
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
 
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
diff --git a/README.md b/README.md
old mode 100644
new mode 100755
index bd1cf7d..820a8b7
--- a/README.md
+++ b/README.md
@@ -1,145 +1,3 @@
-The Recovery Image
-==================
+**Team Win Recovery Project (TWRP)**
 
-Quick turn-around testing
--------------------------
-
-* Devices using recovery-as-boot (e.g. Pixels, which set BOARD\_USES\_RECOVERY\_AS\_BOOT)
-
-      # After setting up environment and lunch.
-      m -j bootimage
-      adb reboot bootloader
-
-      # Pixel devices don't support booting into recovery mode with `fastboot boot`.
-      fastboot flash boot
-
-      # Manually choose `Recovery mode` from bootloader menu.
-
-* Devices with a separate recovery image (e.g. Nexus)
-
-      # After setting up environment and lunch.
-      mm -j && m ramdisk-nodeps && m recoveryimage-nodeps
-      adb reboot bootloader
-
-      # To boot into the new recovery image without flashing the recovery partition:
-      fastboot boot $ANDROID_PRODUCT_OUT/recovery.img
-
-Running the tests
------------------
-
-    # After setting up environment and lunch.
-    mmma -j bootable/recovery
-
-    # Running the tests on device (under normal boot).
-    adb root
-    adb sync data
-
-    # 32-bit device
-    adb shell /data/nativetest/recovery_unit_test/recovery_unit_test
-
-    # Or 64-bit device
-    adb shell /data/nativetest64/recovery_unit_test/recovery_unit_test
-
-Running the manual tests
-------------------------
-
-`recovery-refresh` and `recovery-persist` executables exist only on systems without
-/cache partition. And we need to follow special steps to run tests for them.
-
-- Execute the test on an A/B device first. The test should fail but it will log
-  some contents to pmsg.
-
-- Reboot the device immediately and run the test again. The test should save the
-  contents of pmsg buffer into /data/misc/recovery/inject.txt. Test will pass if
-  this file has expected contents.
-
-Using `adb` under recovery
---------------------------
-
-When running recovery image from debuggable builds (i.e. `-eng` or `-userdebug` build variants, or
-`ro.debuggable=1` in `/prop.default`), `adbd` service is enabled and started by default, which
-allows `adb` communication. A device should be listed under `adb devices`, either in `recovery` or
-`sideload` state.
-
-    $ adb devices
-    List of devices attached
-    1234567890abcdef    recovery
-
-Although `/system/bin/adbd` is built from the same code base as the one in the normal boot, only a
-subset of `adb` commands are meaningful under recovery, such as `adb root`, `adb shell`, `adb push`,
-`adb pull` etc. Since Android Q, `adb shell` no longer requires manually mounting `/system` from
-recovery menu.
-
-## Troubleshooting
-
-### `adb devices` doesn't show the device.
-
-    $ adb devices
-    List of devices attached
-
- * Ensure `adbd` is built and running.
-
-By default, `adbd` is always included into recovery image, as `/system/bin/adbd`. `init` starts
-`adbd` service automatically only in debuggable builds. This behavior is controlled by the recovery
-specific `/init.rc`, whose source code is at `bootable/recovery/etc/init.rc`.
-
-The best way to confirm a running `adbd` is by checking the serial output, which shows a service
-start log as below.
-
-    [   18.961986] c1      1 init: starting service 'adbd'...
-
- * Ensure USB gadget has been enabled.
-
-If `adbd` service has been started but device not shown under `adb devices`, use `lsusb(8)` (on
-host) to check if the device is visible to the host.
-
-`bootable/recovery/etc/init.rc` disables Android USB gadget (via sysfs) as part of the `fs` action
-trigger, and will only re-enable it in debuggable builds (the `on property` rule will always run
-_after_ `on fs`).
-
-    on fs
-        write /sys/class/android_usb/android0/enable 0
-
-    # Always start adbd on userdebug and eng builds
-    on property:ro.debuggable=1
-        write /sys/class/android_usb/android0/enable 1
-        start adbd
-
-If device is using [configfs](https://www.kernel.org/doc/Documentation/usb/gadget_configfs.txt),
-check if configfs has been properly set up in init rc scripts. See the [example
-configuration](https://android.googlesource.com/device/google/wahoo/+/master/init.recovery.hardware.rc)
-for Pixel 2 devices. Note that the flag set via sysfs (i.e. the one above) is no-op when using
-configfs.
-
-### `adb devices` shows the device, but in `unauthorized` state.
-
-    $ adb devices
-    List of devices attached
-    1234567890abcdef    unauthorized
-
-recovery image doesn't honor the USB debugging toggle and the authorizations added under normal boot
-(because such authorization data stays in /data, which recovery doesn't mount), nor does it support
-authorizing a host device under recovery. We can use one of the following options instead.
-
- * **Option 1 (Recommended):** Authorize a host device with adb vendor keys.
-
-For debuggable builds, an RSA keypair can be used to authorize a host device that has the private
-key. The public key, defined via `PRODUCT_ADB_KEYS`, will be copied to `/adb_keys`. When starting
-the host-side `adbd`, make sure the filename (or the directory) of the matching private key has been
-added to `$ADB_VENDOR_KEYS`.
-
-    $ export ADB_VENDOR_KEYS=/path/to/adb/private/key
-    $ adb kill-server
-    $ adb devices
-
-`-user` builds filter out `PRODUCT_ADB_KEYS`, so no `/adb_keys` will be included there.
-
-Note that this mechanism applies to both of normal boot and recovery modes.
-
- * **Option 2:** Allow `adbd` to connect without authentication.
-   * `adbd` is compiled with `ALLOW_ADBD_NO_AUTH` (only on debuggable builds).
-   * `ro.adb.secure` has a value of `0`.
-
-Both of the two conditions need to be satisfied. Although `ro.adb.secure` is a runtime property, its
-value is set at build time (written into `/prop.default`). It defaults to `1` on `-user` builds, and
-`0` for other build variants. The value is overridable via `PRODUCT_DEFAULT_PROPERTY_OVERRIDES`.
+You can find a compiling guide [here](http://forum.xda-developers.com/showthread.php?t=1943625 "Guide").
diff --git a/adbbu/Android.mk b/adbbu/Android.mk
new file mode 100755
index 0000000..c05f265
--- /dev/null
+++ b/adbbu/Android.mk
@@ -0,0 +1,47 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libtwadbbu
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS = -fno-strict-aliasing -D_LARGFILE_SOURCE #-D_DEBUG_ADB_BACKUP
+LOCAL_C_INCLUDES += bionic external/zlib
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0)
+    LOCAL_C_INCLUDES += external/stlport/stlport
+endif
+
+LOCAL_SRC_FILES = \
+        libtwadbbu.cpp \
+        twrpback.cpp
+
+LOCAL_SHARED_LIBRARIES += libz libc libstdc++ libtwrpdigest
+
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0)
+    LOCAL_SHARED_LIBRARIES += libstlport
+else
+    LOCAL_SHARED_LIBRARIES += libc++
+endif
+
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+        adbbumain.cpp
+
+LOCAL_SHARED_LIBRARIES += libstdc++ libz libtwadbbu
+
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0)
+    LOCAL_C_INCLUDES += external/stlport/stlport
+    LOCAL_SHARED_LIBRARIES += libstlport
+else
+    LOCAL_SHARED_LIBRARIES += libc++
+endif
+
+LOCAL_C_INCLUDES += bionic external/zlib
+LOCAL_CFLAGS:= -c -W
+LOCAL_MODULE:= twrpbu
+LOCAL_MODULE_STEM := bu
+LOCAL_MODULE_TAGS:= optional
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/bin
+include $(BUILD_EXECUTABLE)
diff --git a/adbbu/adbbumain.cpp b/adbbu/adbbumain.cpp
new file mode 100644
index 0000000..bd96b20
--- /dev/null
+++ b/adbbu/adbbumain.cpp
@@ -0,0 +1,95 @@
+/*
+		Copyright 2013 to 2017 TeamWin
+		TWRP is free software: you can redistribute it and/or modify
+		it under the terms of the GNU General Public License as published by
+		the Free Software Foundation, either version 3 of the License, or
+		(at your option) any later version.
+
+		TWRP is distributed in the hope that it will be useful,
+		but WITHOUT ANY WARRANTY; without even the implied warranty of
+		MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+		GNU General Public License for more details.
+
+		You should have received a copy of the GNU General Public License
+		along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+#include <string>
+#include <sstream>
+#include <algorithm>
+
+#include "twrpback.hpp"
+#include "twadbstream.h"
+
+
+int main(int argc, char **argv) {
+	int index;
+	size_t pos = 0;
+	bool ret = false;
+	size_t maxpos = strlen(TWRPARG) + 2;
+	std::string command;
+	twrpback tw;
+
+	tw.adblogwrite("Starting adb backup and restore\n");
+	command = argv[1];
+	for (index = 2; index < argc; index++) {
+		command = command + " " + argv[index];
+	}
+
+	pos = command.find(TWRP_BACKUP_ARG);
+	if (pos == std::string::npos || pos > (maxpos + strlen(TWRP_BACKUP_ARG) + 1)) {
+		pos = command.find(TWRP_RESTORE_ARG);
+	}
+	if (pos == std::string::npos || pos > maxpos + strlen(TWRP_STREAM_ARG) + 1) {
+		pos = command.find(TWRP_STREAM_ARG);
+	}
+
+	tw.adblogwrite("command: " + command + "\n");
+	command.erase(0, pos);
+	command.erase(std::remove(command.begin(), command.end(), '\''), command.end());
+
+	if (command.substr(0, sizeof(TWRP_BACKUP_ARG) - 1) == TWRP_BACKUP_ARG) {
+		tw.adblogwrite("Starting adb backup\n");
+		if (isdigit(*argv[1]))
+			tw.adbd_fd = atoi(argv[1]);
+		else
+			tw.adbd_fd = 1;
+		ret = tw.backup(command);
+	}
+	else if (command.substr(0, sizeof(TWRP_RESTORE_ARG) - 1) == TWRP_RESTORE_ARG) {
+		tw.adblogwrite("Starting adb restore\n");
+		if (isdigit(*argv[1]))
+			tw.adbd_fd = atoi(argv[1]);
+		else
+			tw.adbd_fd = 0;
+		ret = tw.restore();
+	}
+	else if (command.substr(0, sizeof(TWRP_STREAM_ARG) - 1) == TWRP_STREAM_ARG) {
+		tw.setStreamFileName(argv[3]);
+		tw.threadStream();
+		ret = true;
+	}
+	if (ret)
+		tw.adblogwrite("Adb backup/restore completed\n");
+	else
+		tw.adblogwrite("Adb backup/restore failed\n");
+
+	if (unlink(TW_ADB_BU_CONTROL) < 0) {
+		std::stringstream str;
+		str << strerror(errno);
+		tw.adblogwrite("Unable to remove TW_ADB_BU_CONTROL: " + str.str());
+	}
+	unlink(TW_ADB_TWRP_CONTROL);
+	if (ret)
+		return 0;
+	else
+		return -1;
+}
diff --git a/adbbu/libtwadbbu.cpp b/adbbu/libtwadbbu.cpp
new file mode 100644
index 0000000..39803b0
--- /dev/null
+++ b/adbbu/libtwadbbu.cpp
@@ -0,0 +1,307 @@
+/*
+		Copyright 2013 to 2017 TeamWin
+		TWRP is free software: you can redistribute it and/or modify
+		it under the terms of the GNU General Public License as published by
+		the Free Software Foundation, either version 3 of the License, or
+		(at your option) any later version.
+
+		TWRP is distributed in the hope that it will be useful,
+		but WITHOUT ANY WARRANTY; without even the implied warranty of
+		MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+		GNU General Public License for more details.
+
+		You should have received a copy of the GNU General Public License
+		along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <zlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <string>
+#include <vector>
+#include <fstream>
+#include <sstream>
+#include <assert.h>
+
+#include "twadbstream.h"
+#include "libtwadbbu.hpp"
+#include "twrpback.hpp"
+
+bool twadbbu::Check_ADB_Backup_File(std::string fname) {
+	struct AdbBackupStreamHeader adbbuhdr;
+	uint32_t crc, adbbuhdrcrc;
+	unsigned char buf[MAX_ADB_READ];
+	int bytes;
+
+	int fd = open(fname.c_str(), O_RDONLY);
+	if (fd < 0) {
+		printf("Unable to open %s for reading: %s.\n", fname.c_str(), strerror(errno));
+		close(fd);
+		return false;
+	}
+	bytes = read(fd, &buf, sizeof(buf));
+	close(fd);
+
+	if (memcpy(&adbbuhdr, buf, sizeof(adbbuhdr)) == NULL) {
+		printf("Unable to memcpy: %s (%s).\n", fname.c_str(), strerror(errno));
+		return false;
+	}
+	adbbuhdrcrc = adbbuhdr.crc;
+	memset(&adbbuhdr.crc, 0, sizeof(adbbuhdr.crc));
+	crc = crc32(0L, Z_NULL, 0);
+	crc = crc32(crc, (const unsigned char*) &adbbuhdr, sizeof(adbbuhdr));
+
+	return (crc == adbbuhdrcrc);
+}
+
+std::vector<std::string> twadbbu::Get_ADB_Backup_Files(std::string fname) {
+	unsigned char buf[MAX_ADB_READ];
+	struct AdbBackupControlType structcmd;
+	std::vector<std::string> adb_partitions;
+
+	int fd = open(fname.c_str(), O_RDONLY);
+	if (fd < 0) {
+		printf("Unable to open %s for reading: %s\n", fname.c_str(), strerror(errno));
+		close(fd);
+		return std::vector<std::string>();
+	}
+
+	while (true) {
+		std::string cmdstr;
+		int readbytes;
+		if ((readbytes = read(fd, &buf, sizeof(buf))) > 0) {
+			memcpy(&structcmd, buf, sizeof(structcmd));
+			assert(structcmd.type == TWENDADB || structcmd.type == TWIMG || structcmd.type == TWFN);
+			cmdstr = structcmd.type;
+			std::string cmdtype = cmdstr.substr(0, sizeof(structcmd.type) - 1);
+			if (cmdtype == TWENDADB) {
+				struct AdbBackupControlType endadb;
+				uint32_t crc, endadbcrc;
+
+				memcpy(&endadb, buf, sizeof(endadb));
+				endadbcrc = endadb.crc;
+				memset(&endadb.crc, 0, sizeof(endadb.crc));
+				crc = crc32(0L, Z_NULL, 0);
+				crc = crc32(crc, (const unsigned char*) &endadb, sizeof(endadb));
+
+				if (crc == endadbcrc) {
+					break;
+				}
+				else {
+					printf("ADB TWENDADB crc header doesn't match\n");
+					close(fd);
+					return std::vector<std::string>();
+				}
+			}
+			else if (cmdtype == TWIMG || cmdtype == TWFN) {
+				struct twfilehdr twfilehdr;
+				uint32_t crc, twfilehdrcrc;
+
+				memcpy(&twfilehdr, buf, sizeof(twfilehdr));
+				twfilehdrcrc = twfilehdr.crc;
+				memset(&twfilehdr.crc, 0, sizeof(twfilehdr.crc));
+
+				crc = crc32(0L, Z_NULL, 0);
+				crc = crc32(crc, (const unsigned char*) &twfilehdr, sizeof(twfilehdr));
+				if (crc == twfilehdrcrc) {
+					std::string adbfile = twfilehdr.name;
+					int pos = adbfile.find_last_of("/") + 1;
+					adbfile = adbfile.substr(pos, adbfile.size());
+					adb_partitions.push_back(adbfile);
+				}
+				else {
+					printf("ADB crc header doesn't match\n");
+					close(fd);
+					return std::vector<std::string>();
+				}
+			}
+		}
+	}
+	close(fd);
+	return adb_partitions;
+}
+
+bool twadbbu::Write_ADB_Stream_Header(uint64_t partition_count) {
+	struct AdbBackupStreamHeader twhdr;
+	int adb_control_bu_fd;
+
+	memset(&twhdr, 0, sizeof(twhdr));
+	adb_control_bu_fd = open(TW_ADB_BU_CONTROL, O_WRONLY | O_NONBLOCK);
+	if (adb_control_bu_fd < 0) {
+		printf("Cannot write to TW_ADB_BU_CONTROL: %s\n", strerror(errno));
+		return false;
+	}
+
+	strncpy(twhdr.start_of_header, TWRP, sizeof(twhdr.start_of_header));
+	strncpy(twhdr.type, TWSTREAMHDR, sizeof(twhdr.type));
+	twhdr.partition_count = partition_count;
+	twhdr.version = ADB_BACKUP_VERSION;
+	memset(twhdr.space, 0, sizeof(twhdr.space));
+	twhdr.crc = crc32(0L, Z_NULL, 0);
+	twhdr.crc = crc32(twhdr.crc, (const unsigned char*) &twhdr, sizeof(twhdr));
+	if (write(adb_control_bu_fd, &twhdr, sizeof(twhdr)) < 0) {
+		printf("Cannot write to adb control channel: %s\n", strerror(errno));
+		close(adb_control_bu_fd);
+		return false;
+	}
+	return true;
+}
+
+bool twadbbu::Write_ADB_Stream_Trailer() {
+	int adb_control_bu_fd;
+	struct AdbBackupControlType endadb;
+
+	memset(&endadb, 0, sizeof(endadb));
+
+	adb_control_bu_fd = open(TW_ADB_BU_CONTROL, O_WRONLY);
+	if (adb_control_bu_fd < 0) {
+		printf("Error opening adb_control_bu_fd: %s\n", strerror(errno));
+		return false;
+	}
+	strncpy(endadb.start_of_header, TWRP, sizeof(endadb.start_of_header));
+	strncpy(endadb.type, TWENDADB, sizeof(endadb.type));
+	endadb.crc = crc32(0L, Z_NULL, 0);
+	endadb.crc = crc32(endadb.crc, (const unsigned char*) &endadb, sizeof(endadb));
+	if (write(adb_control_bu_fd, &endadb, sizeof(endadb)) < 0) {
+		printf("Cannot write to ADB control: %s\n", strerror(errno));
+		close(adb_control_bu_fd);
+		return false;
+	}
+	close(adb_control_bu_fd);
+	return true;
+}
+
+bool twadbbu::Write_TWFN(std::string Backup_FileName, uint64_t file_size, bool use_compression) {
+	int adb_control_bu_fd;
+	adb_control_bu_fd = open(TW_ADB_BU_CONTROL, O_WRONLY | O_NONBLOCK);
+	struct twfilehdr twfilehdr;
+	strncpy(twfilehdr.start_of_header, TWRP, sizeof(twfilehdr.start_of_header));
+	strncpy(twfilehdr.type, TWFN, sizeof(twfilehdr.type));
+	strncpy(twfilehdr.name, Backup_FileName.c_str(), sizeof(twfilehdr.name));
+	twfilehdr.size = (file_size == 0 ? 1024 : file_size);
+	twfilehdr.compressed = use_compression;
+	twfilehdr.crc = crc32(0L, Z_NULL, 0);
+	twfilehdr.crc = crc32(twfilehdr.crc, (const unsigned char*) &twfilehdr, sizeof(twfilehdr));
+
+	printf("Sending TWFN to adb\n");
+	if (write(adb_control_bu_fd, &twfilehdr, sizeof(twfilehdr)) < 1) {
+		printf("Cannot that write to adb_control_bu_fd: %s\n", strerror(errno));
+		close(adb_control_bu_fd);
+		return false;
+	}
+	fsync(adb_control_bu_fd);
+	close(adb_control_bu_fd);
+	return true;
+}
+
+bool twadbbu::Write_TWIMG(std::string Backup_FileName, uint64_t file_size) {
+	int adb_control_bu_fd;
+	struct twfilehdr twimghdr;
+
+	adb_control_bu_fd = open(TW_ADB_BU_CONTROL, O_WRONLY | O_NONBLOCK);
+	strncpy(twimghdr.start_of_header, TWRP, sizeof(twimghdr.start_of_header));
+	strncpy(twimghdr.type, TWIMG, sizeof(twimghdr.type));
+	twimghdr.size = file_size;
+	strncpy(twimghdr.name, Backup_FileName.c_str(), sizeof(twimghdr.name));
+	twimghdr.crc = crc32(0L, Z_NULL, 0);
+	twimghdr.crc = crc32(twimghdr.crc, (const unsigned char*) &twimghdr, sizeof(twimghdr));
+	printf("Sending TWIMG to adb\n");
+	if (write(adb_control_bu_fd, &twimghdr, sizeof(twimghdr)) < 1) {
+		printf("Cannot write to adb control channel: %s\n", strerror(errno));
+		return false;
+	}
+
+	return true;
+}
+
+bool twadbbu::Write_TWEOF() {
+	struct AdbBackupControlType tweof;
+	int adb_control_bu_fd;
+	int errctr = 0;
+
+	printf("opening TW_ADB_BU_CONTROL\n");
+	adb_control_bu_fd = open(TW_ADB_BU_CONTROL, O_WRONLY | O_NONBLOCK);
+	while (adb_control_bu_fd < 0) {
+		printf("failed to open TW_ADB_BU_CONTROL. Retrying: %s\n", strerror(errno));
+		adb_control_bu_fd = open(TW_ADB_BU_CONTROL, O_WRONLY | O_NONBLOCK);
+		usleep(10000);
+		errctr++;
+		if (errctr > ADB_BU_MAX_ERROR) {
+			printf("Cannot write to adb_control_bu_fd: %s.\n", strerror(errno));
+			close(adb_control_bu_fd);
+			return false;
+		}
+	}
+	memset(&tweof, 0, sizeof(tweof));
+	strncpy(tweof.start_of_header, TWRP, sizeof(tweof.start_of_header));
+	strncpy(tweof.type, TWEOF, sizeof(tweof.type));
+	tweof.crc = crc32(0L, Z_NULL, 0);
+	tweof.crc = crc32(tweof.crc, (const unsigned char*) &tweof, sizeof(tweof));
+	printf("Sending TWEOF to adb backup\n");
+	if (write(adb_control_bu_fd, &tweof, sizeof(tweof)) < 0) {
+		printf("Cannot write to adb_control_bu_fd: %s.\n", strerror(errno));
+		close(adb_control_bu_fd);
+		return false;
+	}
+	close(adb_control_bu_fd);
+	return true;
+}
+
+bool twadbbu::Write_TWERROR() {
+	struct AdbBackupControlType twerror;
+	int adb_control_bu_fd = open(TW_ADB_BU_CONTROL, O_WRONLY | O_NONBLOCK);
+
+	strncpy(twerror.start_of_header, TWRP, sizeof(twerror.start_of_header));
+	strncpy(twerror.type, TWERROR, sizeof(twerror.type));
+	memset(twerror.space, 0, sizeof(twerror.space));
+	twerror.crc = crc32(0L, Z_NULL, 0);
+	twerror.crc = crc32(twerror.crc, (const unsigned char*) &twerror, sizeof(twerror));
+	if (write(adb_control_bu_fd, &twerror, sizeof(twerror)) < 0) {
+		printf("Cannot write to adb control channel: %s\n", strerror(errno));
+		return false;
+	}
+	close(adb_control_bu_fd);
+	return true;
+}
+
+bool twadbbu::Write_TWENDADB() {
+	struct AdbBackupControlType endadb;
+	int adb_control_bu_fd = open(TW_ADB_BU_CONTROL, O_WRONLY | O_NONBLOCK);
+
+	memset(&endadb, 0, sizeof(endadb));
+	strncpy(endadb.start_of_header, TWRP, sizeof(endadb.start_of_header));
+	strncpy(endadb.type, TWENDADB, sizeof(endadb.type));
+	endadb.crc = crc32(0L, Z_NULL, 0);
+	endadb.crc = crc32(endadb.crc, (const unsigned char*) &endadb, sizeof(endadb));
+
+	printf("Sending TWENDADB to ADB Backup\n");
+	if (write(adb_control_bu_fd, &endadb, sizeof(endadb)) < 1) {
+		printf("Cannot write to ADB_CONTROL_BU_FD: %s\n", strerror(errno));
+		return false;
+	}
+
+	close(adb_control_bu_fd);
+	return true;
+}
+
+bool twadbbu::Write_TWDATA(FILE* adbd_fp) {
+	struct AdbBackupControlType data_block;
+	memset(&data_block, 0, sizeof(data_block));
+	strncpy(data_block.start_of_header, TWRP, sizeof(data_block.start_of_header));
+	strncpy(data_block.type, TWDATA, sizeof(data_block.type));
+	data_block.crc = crc32(0L, Z_NULL, 0);
+	data_block.crc = crc32(data_block.crc, (const unsigned char*) &data_block, sizeof(data_block));
+	if (fwrite(&data_block, 1, sizeof(data_block), adbd_fp) != sizeof(data_block))  {
+		return false;
+	}
+	return true;
+}
diff --git a/adbbu/libtwadbbu.hpp b/adbbu/libtwadbbu.hpp
new file mode 100644
index 0000000..9244bb5
--- /dev/null
+++ b/adbbu/libtwadbbu.hpp
@@ -0,0 +1,51 @@
+/*
+		Copyright 2013 to 2017 TeamWin
+		TWRP is free software: you can redistribute it and/or modify
+		it under the terms of the GNU General Public License as published by
+		the Free Software Foundation, either version 3 of the License, or
+		(at your option) any later version.
+
+		TWRP is distributed in the hope that it will be useful,
+		but WITHOUT ANY WARRANTY; without even the implied warranty of
+		MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+		GNU General Public License for more details.
+
+		You should have received a copy of the GNU General Public License
+		along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef _LIBTWADBBU_HPP
+#define _LIBTWADBBU_HPP
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <string>
+#include <vector>
+#include <fstream>
+#include <sstream>
+
+#include "twadbstream.h"
+
+class twadbbu {
+public:
+	static bool Check_ADB_Backup_File(std::string fname);                                          //Check if file is ADB Backup file
+	static std::vector<std::string> Get_ADB_Backup_Files(std::string fname);                       //List ADB Files in String Vector
+	static bool Write_ADB_Stream_Header(uint64_t partition_count);                                 //Write ADB Stream Header to stream
+	static bool Write_ADB_Stream_Trailer();                                                        //Write ADB Stream Trailer to stream
+	static bool Write_TWFN(std::string Backup_FileName, uint64_t file_size, bool use_compression); //Write a tar image to stream
+	static bool Write_TWIMG(std::string Backup_FileName, uint64_t file_size);                      //Write a partition image to stream
+	static bool Write_TWEOF();                                                                     //Write ADB End-Of-File marker to stream
+	static bool Write_TWERROR();                                                                   //Write error message occurred to stream
+	static bool Write_TWENDADB();                                                                  //Write ADB End-Of-Stream command to stream
+	static bool Write_TWDATA(FILE* adbd_fp);                                                       //Write TWDATA separator
+};
+
+#endif //__LIBTWADBBU_HPP
diff --git a/adbbu/twadbstream.h b/adbbu/twadbstream.h
new file mode 100644
index 0000000..bef463c
--- /dev/null
+++ b/adbbu/twadbstream.h
@@ -0,0 +1,112 @@
+/*
+	TWRP is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+
+	TWRP is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __TWADBSTREAM_H
+#define __TWADBSTREAM_H
+
+#define TWRPARG "--twrp"
+#define TWRP_BACKUP_ARG "backup"
+#define TWRP_RESTORE_ARG "restore"
+#define TWRP_STREAM_ARG "stream"
+#define TW_ADB_BACKUP "/tmp/twadbbackup"		//FIFO for adb backup
+#define TW_ADB_RESTORE "/tmp/twadbrestore"		//FIFO for adb restore
+#define TW_ADB_BU_CONTROL "/tmp/twadbbucontrol"		//FIFO for sending control from TWRP to ADB Backup
+#define TW_ADB_TWRP_CONTROL "/tmp/twadbtwrpcontrol"	//FIFO for sending control from ADB Backup to TWRP
+#define TWRP "TWRP"					//Magic Value
+#define ADB_BU_MAX_ERROR 20				//Max amount of errors for while loops
+#define ADB_BACKUP_OP "adbbackup"
+#define ADB_RESTORE_OP "adbrestore"
+
+//ADB Backup Control Commands
+#define TWSTREAMHDR "twstreamheader"			//TWRP Parititon Count Control
+#define TWFN "twfilename"				//TWRP Filename Control
+#define TWIMG "twimage"					//TWRP Image name Control
+#define TWEOF "tweof"					//End of File for Image/File
+#define MD5TRAILER "md5trailer"				//Image/File MD5 Trailer
+#define TWDATA "twdatablock"				// twrp adb backup data block header
+#define TWMD5 "twverifymd5"				//This command is compared to the md5trailer by ORS to verify transfer
+#define TWENDADB "twendadb"				//End Protocol
+#define TWERROR "twerror"				//Send error
+#define ADB_BACKUP_VERSION 3				//Backup Version
+#define DATA_MAX_CHUNK_SIZE 1048576			//Maximum size between each data header
+#define MAX_ADB_READ 512				//align with default tar size for amount to read fom adb stream
+
+/*
+structs for adb backup need to align to 512 bytes for reading 512
+bytes at a time
+Each struct contains a crc field so that when we are checking for commands
+and the crc doesn't match we still assume it's data matching the command
+struct but not really a command
+*/
+
+/*  stream format:
+  | TW ADB Backup Header   |
+  | TW File Stream Header  |
+  | File Data              |
+  | File/Image MD5 Trailer |
+  | TW File Stream Header  |
+  | File Data              |
+  | File/Image MD5 Trailer |
+  | etc...                 |
+*/
+
+//determine whether struct is 512 bytes, if not fail compilation
+#define ADBSTRUCT_STATIC_ASSERT(structure) typedef char adb_assertion[( !!(structure) )*2-1 ]
+
+//generic cmd structure to align fields for sending commands to and from adb backup
+struct AdbBackupControlType {
+	char start_of_header[8];			//stores the magic value #define TWRP
+	char type[16];					//stores the type of command, TWENDADB, TWCNT, TWEOF, TWMD5, TWDATA and TWERROR
+	uint32_t crc;					//stores the zlib 32 bit crc of the AdbBackupControlType struct to allow for making sure we are processing metadata
+	char space[484];				//stores space to align the struct to 512 bytes
+
+	//return a C++ string while not reading outside the type char array
+	std::string get_type() {
+		return std::string(type, strnlen(type, sizeof(type)-1));
+	}
+};
+
+//general info for file metadata stored in adb backup header
+struct twfilehdr {
+	char start_of_header[8];			//stores the magic value #define TWRP
+	char type[16];					//stores the type of file header, TWFN or TWIMG
+	uint64_t size;					//stores the size of the file contained after this header in the backup file
+	uint64_t compressed;				//stores whether the file is compressed or not. 1 == compressed and 0 == uncompressed
+	uint32_t crc;					//stores the zlib 32 bit crc of the twfilehdr struct to allow for making sure we are processing metadata
+	char name[468];					//stores the filename of the file
+};
+
+//md5 for files stored as a trailer to files in the adb backup file to check
+//that they are restored correctly
+struct AdbBackupFileTrailer {
+	char start_of_trailer[8];			//stores the magic value #define TWRP
+	char type[16];					//stores the AdbBackupFileTrailer type MD5TRAILER
+	uint32_t crc;					//stores the zlib 32 bit crc of the AdbBackupFileTrailer struct to allow for making sure we are processing metadata
+	uint32_t ident;					//stores crc to determine if header is encapsulated in stream as data
+	char md5[40];					//stores the md5 computation of the file
+	char space[440];				//stores space to align the struct to 512 bytes
+};
+
+//info for version and number of partitions backed up
+struct AdbBackupStreamHeader {
+	char start_of_header[8];			//stores the magic value #define TWRP
+	char type[16];					//stores the AdbBackupStreamHeader value TWCNT
+	uint64_t partition_count;			//stores the number of partitions to restore in the stream
+	uint64_t version;				//stores the version of adb backup. increment ADB_BACKUP_VERSION each time the metadata is updated
+	uint32_t crc;					//stores the zlib 32 bit crc of the AdbBackupStreamHeader struct to allow for making sure we are processing metadata
+	char space[468];				//stores space to align the struct to 512 bytes
+};
+
+#endif //__TWADBSTREAM_H
diff --git a/adbbu/twrpback.cpp b/adbbu/twrpback.cpp
new file mode 100644
index 0000000..b3de76d
--- /dev/null
+++ b/adbbu/twrpback.cpp
@@ -0,0 +1,935 @@
+/*
+		Copyright 2013 to 2017 TeamWin
+		TWRP is free software: you can redistribute it and/or modify
+		it under the terms of the GNU General Public License as published by
+		the Free Software Foundation, either version 3 of the License, or
+		(at your option) any later version.
+
+		TWRP is distributed in the hope that it will be useful,
+		but WITHOUT ANY WARRANTY; without even the implied warranty of
+		MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+		GNU General Public License for more details.
+
+		You should have received a copy of the GNU General Public License
+		along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <zlib.h>
+#include <ctype.h>
+#include <semaphore.h>
+#include <string>
+#include <sstream>
+#include <fstream>
+#include <algorithm>
+#include <utils/threads.h>
+#include <pthread.h>
+
+#include "twadbstream.h"
+#include "twrpback.hpp"
+#include "libtwadbbu.hpp"
+#include "../twrpDigest/twrpDigest.hpp"
+#include "../twrpDigest/twrpMD5.hpp"
+#include "../twrpAdbBuFifo.hpp"
+
+twrpback::twrpback(void) {
+	adbd_fp = NULL;
+	read_fd = 0;
+	write_fd = 0;
+	adb_control_twrp_fd = 0;
+	adb_control_bu_fd = 0;
+	adb_read_fd = 0;
+	adb_write_fd = 0;
+	ors_fd = 0;
+	debug_adb_fd = 0;
+	firstPart = true;
+	createFifos();
+	adbloginit();
+}
+
+twrpback::~twrpback(void) {
+	adblogfile.close();
+	closeFifos();
+}
+
+void twrpback::printErrMsg(std::string msg, int errNum) {
+	std::stringstream str;
+	str << strerror(errNum);
+	adblogwrite(msg +  " " + str.str() + "\n");
+}
+
+void twrpback::createFifos(void) {
+        if (mkfifo(TW_ADB_BU_CONTROL, 0666) < 0) {
+                std::string msg = "Unable to create TW_ADB_BU_CONTROL fifo: ";
+		printErrMsg(msg, errno);
+        }
+        if (mkfifo(TW_ADB_TWRP_CONTROL, 0666) < 0) {
+                std::string msg = "Unable to create TW_ADB_TWRP_CONTROL fifo: ";
+		printErrMsg(msg, errno);
+                unlink(TW_ADB_BU_CONTROL);
+        }
+}
+
+void twrpback::closeFifos(void) {
+        if (unlink(TW_ADB_BU_CONTROL) < 0) {
+                std::string msg = "Unable to remove TW_ADB_BU_CONTROL: ";
+		printErrMsg(msg, errno);
+        }
+        if (unlink(TW_ADB_TWRP_CONTROL) < 0) {
+                std::string msg = "Unable to remove TW_ADB_TWRP_CONTROL: ";
+		printErrMsg(msg, errno);
+	}
+}
+
+void twrpback::adbloginit(void) {
+	adblogfile.open("/tmp/adb.log", std::fstream::app);
+}
+
+void twrpback::adblogwrite(std::string writemsg) {
+	adblogfile << writemsg << std::flush;
+}
+
+void twrpback::close_backup_fds() {
+	if (ors_fd > 0)
+		close(ors_fd);
+	if (write_fd > 0)
+		close(write_fd);
+	if (adb_read_fd > 0)
+		close(adb_read_fd);
+	if (adb_control_bu_fd > 0)
+		close(adb_control_bu_fd);
+	#ifdef _DEBUG_ADB_BACKUP
+		if (debug_adb_fd > 0)
+			close(debug_adb_fd);
+	#endif
+	if (adbd_fp != NULL)
+		fclose(adbd_fp);
+	if (access(TW_ADB_BACKUP, F_OK) == 0)
+		unlink(TW_ADB_BACKUP);
+}
+
+void twrpback::close_restore_fds() {
+	if (ors_fd > 0)
+		close(ors_fd);
+	if (write_fd > 0)
+		close(write_fd);
+	if (adb_control_bu_fd > 0)
+		close(adb_control_bu_fd);
+	if (adb_control_twrp_fd > 0)
+		close(adb_control_twrp_fd);
+	if (adbd_fp != NULL)
+		fclose(adbd_fp);
+	if (access(TW_ADB_RESTORE, F_OK) == 0)
+		unlink(TW_ADB_RESTORE);
+	#ifdef _DEBUG_ADB_BACKUP
+	if (debug_adb_fd > 0)
+		close(debug_adb_fd);
+	#endif
+}
+
+bool twrpback::backup(std::string command) {
+	twrpMD5 digest;
+	int bytes = 0, errctr = 0;
+	char adbReadStream[MAX_ADB_READ];
+	uint64_t totalbytes = 0, dataChunkBytes = 0, fileBytes = 0;
+	uint64_t md5fnsize = 0;
+	struct AdbBackupControlType endadb;
+
+	//ADBSTRUCT_STATIC_ASSERT(sizeof(endadb) == MAX_ADB_READ);
+
+	bool writedata = true;
+	bool compressed = false;
+	bool firstDataPacket = true;
+
+	adbd_fp = fdopen(adbd_fd, "w");
+	if (adbd_fp == NULL) {
+		adblogwrite("Unable to open adb_fp\n");
+		return false;
+	}
+
+	if (mkfifo(TW_ADB_BACKUP, 0666) < 0) {
+		adblogwrite("Unable to create TW_ADB_BACKUP fifo\n");
+		return false;
+	}
+
+	adblogwrite("opening TW_ADB_FIFO\n");
+
+	write_fd = open(TW_ADB_FIFO, O_WRONLY);
+	while (write_fd < 0) {
+		write_fd = open(TW_ADB_FIFO, O_WRONLY);
+		usleep(10000);
+		errctr++;
+		if (errctr > ADB_BU_MAX_ERROR) {
+			std::string msg = "Unable to open TW_ADB_FIFO";
+			printErrMsg(msg, errno);
+			close_backup_fds();
+			return false;
+		}
+	}
+
+	memset(operation, 0, sizeof(operation));
+	if (snprintf(operation, sizeof(operation), "adbbackup %s", command.c_str()) >= (int)sizeof(operation)) {
+		adblogwrite("Operation too big to write to ORS_INPUT_FILE\n");
+		close_backup_fds();
+		return false;
+	}
+	if (write(write_fd, operation, sizeof(operation)) != sizeof(operation)) {
+		adblogwrite("Unable to write to ORS_INPUT_FILE\n");
+		close_backup_fds();
+		return false;
+	}
+
+	memset(&adbReadStream, 0, sizeof(adbReadStream));
+	memset(&cmd, 0, sizeof(cmd));
+
+	adblogwrite("opening TW_ADB_BU_CONTROL\n");
+	adb_control_bu_fd = open(TW_ADB_BU_CONTROL, O_RDONLY | O_NONBLOCK);
+	if (adb_control_bu_fd < 0) {
+		adblogwrite("Unable to open TW_ADB_BU_CONTROL for reading.\n");
+		close_backup_fds();
+		return false;
+	}
+
+	adblogwrite("opening TW_ADB_BACKUP\n");
+	adb_read_fd = open(TW_ADB_BACKUP, O_RDONLY | O_NONBLOCK);
+	if (adb_read_fd < 0) {
+		adblogwrite("Unable to open TW_ADB_BACKUP for reading.\n");
+		close_backup_fds();
+		return false;
+	}
+
+	//loop until TWENDADB sent
+	while (true) {
+		if (read(adb_control_bu_fd, &cmd, sizeof(cmd)) > 0) {
+			struct AdbBackupControlType structcmd;
+
+			memcpy(&structcmd, cmd, sizeof(cmd));
+			std::string cmdtype = structcmd.get_type();
+
+			//we received an error, exit and unlink
+			if (cmdtype == TWERROR) {
+				writedata = false;
+				adblogwrite("Error received. Quitting...\n");
+				close_backup_fds();
+				return false;
+			}
+			//we received the end of adb backup stream so we should break the loop
+			else if (cmdtype == TWENDADB) {
+				writedata = false;
+				adblogwrite("Recieved TWENDADB\n");
+				memcpy(&endadb, cmd, sizeof(cmd));
+				std::stringstream str;
+				str << totalbytes;
+				adblogwrite(str.str() + " total bytes written\n");
+				break;
+			}
+			//we recieved the TWSTREAMHDR structure metadata to write to adb
+			else if (cmdtype == TWSTREAMHDR) {
+				writedata = false;
+				adblogwrite("writing TWSTREAMHDR\n");
+				if (fwrite(cmd, 1, sizeof(cmd), adbd_fp) != sizeof(cmd)) {
+					std::string msg = "Error writing TWSTREAMHDR to adbd";
+					printErrMsg(msg, errno);
+					close_backup_fds();
+					return false;
+				}
+				fflush(adbd_fp);
+			}
+			//we will be writing an image from TWRP
+			else if (cmdtype == TWIMG) {
+				struct twfilehdr twimghdr;
+
+				adblogwrite("writing TWIMG\n");
+				digest.init();
+				memset(&twimghdr, 0, sizeof(twimghdr));
+				memcpy(&twimghdr, cmd, sizeof(cmd));
+				md5fnsize = twimghdr.size;
+				compressed = false;
+
+				#ifdef _DEBUG_ADB_BACKUP
+				std::string debug_fname = "/data/media/";
+				debug_fname.append(basename(twimghdr.name));
+				debug_fname.append("-backup.img");
+				debug_adb_fd = open(debug_fname.c_str(), O_WRONLY | O_CREAT, 0666);
+				adblogwrite("Opening adb debug tar\n");
+				#endif
+
+				if (fwrite(cmd, 1, sizeof(cmd), adbd_fp) != sizeof(cmd)) {
+					adblogwrite("Error writing TWIMG to adbd\n");
+					close_backup_fds();
+					return false;
+				}
+				fflush(adbd_fp);
+				writedata = true;
+			}
+			//we will be writing a tar from TWRP
+			else if (cmdtype == TWFN) {
+				struct twfilehdr twfilehdr;
+
+				adblogwrite("writing TWFN\n");
+				digest.init();
+
+				//ADBSTRUCT_STATIC_ASSERT(sizeof(twfilehdr) == MAX_ADB_READ);
+
+				memset(&twfilehdr, 0, sizeof(twfilehdr));
+				memcpy(&twfilehdr, cmd, sizeof(cmd));
+				md5fnsize = twfilehdr.size;
+
+				compressed = twfilehdr.compressed == 1 ? true: false;
+
+				#ifdef _DEBUG_ADB_BACKUP
+				std::string debug_fname = "/data/media/";
+				debug_fname.append(basename(twfilehdr.name));
+				debug_fname.append("-backup.tar");
+				debug_adb_fd = open(debug_fname.c_str(), O_WRONLY | O_CREAT, 0666);
+				adblogwrite("Opening adb debug tar\n");
+				#endif
+
+				if (fwrite(cmd, 1, sizeof(cmd), adbd_fp) != sizeof(cmd)) {
+					adblogwrite("Error writing TWFN to adbd\n");
+					close_backup_fds();
+					return false;
+				}
+				fflush(adbd_fp);
+				writedata = true;
+			}
+			/*
+			We received the command that we are done with the file stream.
+			We will flush the remaining data stream.
+			Update md5 and write final results to adb stream.
+			If we need padding because the total bytes are not a multiple
+			of 512, we pad the end with 0s to we reach 512.
+			We also write the final md5 to the adb stream.
+			*/
+			else if (cmdtype == TWEOF) {
+				adblogwrite("received TWEOF\n");
+				while ((bytes = read(adb_read_fd, &adbReadStream, sizeof(adbReadStream)) != 0)) {
+					totalbytes += bytes;
+					fileBytes += bytes;
+					dataChunkBytes += bytes;
+
+					char *writeAdbReadStream = new char [bytes];
+					memcpy(writeAdbReadStream, adbReadStream, bytes);
+
+					digest.update((unsigned char *) writeAdbReadStream, bytes);
+					if (fwrite(writeAdbReadStream, 1, bytes, adbd_fp) < 0) {
+						std::string msg = "Cannot write to adbd stream: ";
+						printErrMsg(msg, errno);
+					}
+					#if defined(_DEBUG_ADB_BACKUP)
+					if (write(debug_adb_fd, writeAdbReadStream, bytes) < 1) {
+						std::string msg = "Cannot write to ADB_CONTROL_READ_FD: ";
+						printErrMsg(msg, errno);
+						close_restore_fds();
+						return false;
+					}
+					#endif
+					fflush(adbd_fp);
+					delete [] writeAdbReadStream;
+					memset(adbReadStream, 0, sizeof(adbReadStream));
+				}
+
+				if (fileBytes % DATA_MAX_CHUNK_SIZE != 0) {
+					int64_t count = fileBytes / DATA_MAX_CHUNK_SIZE + 1;
+					uint64_t ceilingBytes = count * DATA_MAX_CHUNK_SIZE;
+					char padding[ceilingBytes - fileBytes];
+					int paddingBytes = sizeof(padding);
+					memset(padding, 0, paddingBytes);
+					std::stringstream paddingStr;
+					paddingStr << paddingBytes;
+					adblogwrite("writing padding to stream: " + paddingStr.str() + " bytes\n");
+					if (fwrite(padding, 1, paddingBytes, adbd_fp) != sizeof(padding)) {
+						adblogwrite("Error writing padding to adbd\n");
+						close_backup_fds();
+						return false;
+					}
+					#if defined(_DEBUG_ADB_BACKUP)
+					if (write(debug_adb_fd, padding, paddingBytes) < 1) {
+						std::string msg = "Cannot write to ADB_CONTROL_READ_FD: ";
+						printErrMsg(msg, errno);
+						close_restore_fds();
+						return false;
+					}
+					#endif
+					totalbytes += paddingBytes;
+					digest.update((unsigned char *) padding, paddingBytes);
+					fflush(adbd_fp);
+				}
+
+				AdbBackupFileTrailer md5trailer;
+
+				memset(&md5trailer, 0, sizeof(md5trailer));
+
+				std::string md5string = digest.return_digest_string();
+
+				strncpy(md5trailer.start_of_trailer, TWRP, sizeof(md5trailer.start_of_trailer));
+				strncpy(md5trailer.type, MD5TRAILER, sizeof(md5trailer.type));
+				strncpy(md5trailer.md5, md5string.c_str(), sizeof(md5trailer.md5));
+
+				md5trailer.crc = crc32(0L, Z_NULL, 0);
+				md5trailer.crc = crc32(md5trailer.crc, (const unsigned char*) &md5trailer, sizeof(md5trailer));
+
+				md5trailer.ident = crc32(0L, Z_NULL, 0);
+				md5trailer.ident = crc32(md5trailer.ident, (const unsigned char*) &md5trailer, sizeof(md5trailer));
+				md5trailer.ident = crc32(md5trailer.ident, (const unsigned char*) &md5fnsize, sizeof(md5fnsize));
+
+				if (fwrite(&md5trailer, 1, sizeof(md5trailer), adbd_fp) != sizeof(md5trailer))  {
+					adblogwrite("Error writing md5trailer to adbd\n");
+					close_backup_fds();
+					return false;
+				}
+				fflush(adbd_fp);
+				writedata = false;
+				firstDataPacket = true;
+				fileBytes = 0;
+			}
+			memset(&cmd, 0, sizeof(cmd));
+			dataChunkBytes = 0;
+		}
+		//If we are to write data because of a new file stream, lets write all the data.
+		//This will allow us to not write data after a command structure has been written
+		//to the adb stream.
+		//If the stream is compressed, we need to always write the data.
+		if (writedata || compressed) {
+			while ((bytes = read(adb_read_fd, &adbReadStream, sizeof(adbReadStream))) > 0) {
+				if (firstDataPacket) {
+					if (!twadbbu::Write_TWDATA(adbd_fp)) {
+						close_backup_fds();
+						return false;
+					}
+					fileBytes += MAX_ADB_READ;
+					fflush(adbd_fp);
+					firstDataPacket = false;
+					dataChunkBytes += sizeof(adbReadStream);
+				}
+				char *writeAdbReadStream = new char [bytes];
+				memcpy(writeAdbReadStream, adbReadStream, bytes);
+
+				digest.update((unsigned char *) writeAdbReadStream, bytes);
+
+				totalbytes += bytes;
+				fileBytes += bytes;
+				dataChunkBytes += bytes;
+
+				if (fwrite(writeAdbReadStream, 1, bytes, adbd_fp) != (unsigned long long)bytes) {
+					adblogwrite("Error writing backup data to adbd\n");
+					close_backup_fds();
+					return false;
+				}
+				#ifdef _DEBUG_ADB_BACKUP
+				if (write(debug_adb_fd, writeAdbReadStream, bytes) < 1) {
+					std::string msg = "Cannot write to ADB_CONTROL_READ_FD: ";
+					printErrMsg(msg, errno);
+					close_restore_fds();
+					return false;
+				}
+				#endif
+				fflush(adbd_fp);
+				delete [] writeAdbReadStream;
+
+				memset(&adbReadStream, 0, sizeof(adbReadStream));
+
+				if (dataChunkBytes == DATA_MAX_CHUNK_SIZE) {
+					dataChunkBytes = 0;
+					firstDataPacket = true;
+				}
+				else if (dataChunkBytes > (DATA_MAX_CHUNK_SIZE - sizeof(adbReadStream))) {
+					int bytesLeft = DATA_MAX_CHUNK_SIZE - dataChunkBytes;
+					char extraData[bytesLeft];
+
+					memset(&extraData, 0, bytesLeft);
+					while ((bytes = read(adb_read_fd, &extraData, bytesLeft)) != 0) {
+						if (bytes > 0) {
+							totalbytes += bytes;
+							fileBytes += bytes;
+							dataChunkBytes += bytes;
+
+							bytesLeft -= bytes;
+							char *writeAdbReadStream = new char [bytes];
+							memcpy(writeAdbReadStream, extraData, bytes);
+
+							digest.update((unsigned char *) writeAdbReadStream, bytes);
+							if (fwrite(writeAdbReadStream, 1, bytes, adbd_fp) < 0) {
+								std::string msg = "Cannot write to adbd stream: ";
+								printErrMsg(msg, errno);
+								close_restore_fds();
+								return false;
+							}
+							#ifdef _DEBUG_ADB_BACKUP
+							if (write(debug_adb_fd, writeAdbReadStream, bytes) < 1) {
+								std::string msg = "Cannot write to ADB_CONTROL_READ_FD: ";
+								printErrMsg(msg, errno);
+								close_restore_fds();
+								return false;
+							}
+							#endif
+							fflush(adbd_fp);
+							delete [] writeAdbReadStream;
+						}
+						memset(&extraData, 0, bytesLeft);
+						if (bytesLeft == 0) {
+							break;
+						}
+					}
+
+					fflush(adbd_fp);
+					dataChunkBytes = 0;
+					firstDataPacket = true;
+				}
+			}
+		}
+	}
+
+	//Write the final end adb structure to the adb stream
+	if (fwrite(&endadb, 1, sizeof(endadb), adbd_fp) != sizeof(endadb)) {
+		adblogwrite("Error writing endadb to adbd\n");
+		close_backup_fds();
+		return false;
+	}
+	fflush(adbd_fp);
+	close_backup_fds();
+	return true;
+}
+
+bool twrpback::restore(void) {
+	twrpMD5 digest;
+	char cmd[MAX_ADB_READ];
+	char readAdbStream[MAX_ADB_READ];
+	struct AdbBackupControlType structcmd;
+	int errctr = 0;
+	uint64_t totalbytes = 0, dataChunkBytes = 0;
+	uint64_t md5fnsize = 0, fileBytes = 0;
+	bool read_from_adb;
+	bool md5sumdata;
+	bool compressed, tweofrcvd, extraData;
+
+	read_from_adb = true;
+
+	signal(SIGPIPE, SIG_IGN);
+	signal(SIGHUP, SIG_IGN);
+
+	adbd_fp = fdopen(adbd_fd, "r");
+	if (adbd_fp == NULL) {
+		adblogwrite("Unable to open adb_fp\n");
+		close_restore_fds();
+		return false;
+	}
+
+	if(mkfifo(TW_ADB_RESTORE, 0666)) {
+		adblogwrite("Unable to create TW_ADB_RESTORE fifo\n");
+		close_restore_fds();
+		return false;
+	}
+
+	adblogwrite("opening TW_ADB_FIFO\n");
+	write_fd = open(TW_ADB_FIFO, O_WRONLY);
+
+	while (write_fd < 0) {
+		write_fd = open(TW_ADB_FIFO, O_WRONLY);
+		errctr++;
+		if (errctr > ADB_BU_MAX_ERROR) {
+			std::string msg = "Unable to open TW_ADB_FIFO.";
+			printErrMsg(msg, errno);
+			close_restore_fds();
+			return false;
+		}
+	}
+
+	memset(operation, 0, sizeof(operation));
+	sprintf(operation, "adbrestore");
+	if (write(write_fd, operation, sizeof(operation)) != sizeof(operation)) {
+		adblogwrite("Unable to write to TW_ADB_FIFO\n");
+		close_restore_fds();
+		return false;
+	}
+
+	memset(&readAdbStream, 0, sizeof(readAdbStream));
+	memset(&cmd, 0, sizeof(cmd));
+
+	adblogwrite("opening TW_ADB_BU_CONTROL\n");
+	adb_control_bu_fd = open(TW_ADB_BU_CONTROL, O_RDONLY | O_NONBLOCK);
+	if (adb_control_bu_fd < 0) {
+		std::string msg = "Unable to open TW_ADB_BU_CONTROL for writing.";
+		printErrMsg(msg, errno);
+		close_restore_fds();
+		return false;
+	}
+
+	adblogwrite("opening TW_ADB_TWRP_CONTROL\n");
+	adb_control_twrp_fd = open(TW_ADB_TWRP_CONTROL, O_WRONLY | O_NONBLOCK);
+	if (adb_control_twrp_fd < 0) {
+		std::string msg = "Unable to open TW_ADB_TWRP_CONTROL for writing. Retrying...";
+		printErrMsg(msg, errno);
+		while (adb_control_twrp_fd < 0) {
+			adb_control_twrp_fd = open(TW_ADB_TWRP_CONTROL, O_WRONLY | O_NONBLOCK);
+			usleep(10000);
+			errctr++;
+			if (errctr > ADB_BU_MAX_ERROR) {
+				adblogwrite("Unable to open TW_ADB_TWRP_CONTROL\n");
+				close_backup_fds();
+				return false;
+			}
+		}
+	}
+
+	//Loop until we receive TWENDADB from TWRP
+	while (true) {
+		memset(&cmd, 0, sizeof(cmd));
+		if (read(adb_control_bu_fd, &cmd, sizeof(cmd)) > 0) {
+			struct AdbBackupControlType structcmd;
+			memcpy(&structcmd, cmd, sizeof(cmd));
+			std::string cmdtype = structcmd.get_type();
+
+			//If we receive TWEOF from TWRP close adb data fifo
+			if (cmdtype == TWEOF) {
+				adblogwrite("Received TWEOF\n");
+				read_from_adb = true;
+				tweofrcvd = true;
+				close(adb_write_fd);
+			}
+			//Break when TWRP sends TWENDADB
+			else if (cmdtype == TWENDADB) {
+				adblogwrite("Received TWENDADB\n");
+				break;
+			}
+			//we received an error, exit and unlink
+			else if (cmdtype == TWERROR) {
+				adblogwrite("Error received. Quitting...\n");
+				close_restore_fds();
+				return false;
+			}
+		}
+		//If we should read from the adb stream, write commands and data to TWRP
+		if (read_from_adb) {
+			int readbytes;
+			if ((readbytes = fread(readAdbStream, 1, sizeof(readAdbStream), adbd_fp)) == sizeof(readAdbStream)) {
+				memcpy(&structcmd, readAdbStream, sizeof(readAdbStream));
+				std::string cmdtype = structcmd.get_type();
+
+				//Tell TWRP we have read the entire adb stream
+				if (cmdtype == TWENDADB) {
+					struct AdbBackupControlType endadb;
+					uint32_t crc, endadbcrc;
+
+					md5sumdata = false;
+					memset(&endadb, 0, sizeof(endadb));
+					memcpy(&endadb, readAdbStream, sizeof(readAdbStream));
+					endadbcrc = endadb.crc;
+					memset(&endadb.crc, 0, sizeof(endadb.crc));
+					crc = crc32(0L, Z_NULL, 0);
+					crc = crc32(crc, (const unsigned char*) &endadb, sizeof(endadb));
+
+					if (crc == endadbcrc) {
+						adblogwrite("sending TWENDADB\n");
+						if (write(adb_control_twrp_fd, &endadb, sizeof(endadb)) < 1) {
+							std::string msg = "Cannot write to ADB_CONTROL_READ_FD: ";
+							printErrMsg(msg, errno);
+							close_restore_fds();
+							return false;
+						}
+						read_from_adb = false;
+					}
+					else {
+						adblogwrite("ADB TWENDADB crc header doesn't match\n");
+						close_restore_fds();
+						return false;
+					}
+				}
+				//Send TWRP partition metadata
+				else if (cmdtype == TWSTREAMHDR) {
+					struct AdbBackupStreamHeader cnthdr;
+					uint32_t crc, cnthdrcrc;
+
+					//ADBSTRUCT_STATIC_ASSERT(sizeof(cnthdr) == MAX_ADB_READ);
+
+					md5sumdata = false;
+					memset(&cnthdr, 0, sizeof(cnthdr));
+					memcpy(&cnthdr, readAdbStream, sizeof(readAdbStream));
+					cnthdrcrc = cnthdr.crc;
+					memset(&cnthdr.crc, 0, sizeof(cnthdr.crc));
+					crc = crc32(0L, Z_NULL, 0);
+					crc = crc32(crc, (const unsigned char*) &cnthdr, sizeof(cnthdr));
+
+					if (crc == cnthdrcrc) {
+						adblogwrite("Restoring TWSTREAMHDR\n");
+						if (write(adb_control_twrp_fd, readAdbStream, sizeof(readAdbStream)) < 0) {
+							std::string msg = "Cannot write to adb_control_twrp_fd: ";
+							printErrMsg(msg, errno);
+							close_restore_fds();
+							return false;
+						}
+					}
+					else {
+						adblogwrite("ADB TWSTREAMHDR crc header doesn't match\n");
+						close_restore_fds();
+						return false;
+					}
+				}
+				//Tell TWRP we are sending a partition image
+				else if (cmdtype == TWIMG) {
+					struct twfilehdr twimghdr;
+					uint32_t crc, twimghdrcrc;
+					md5sumdata = false;
+					fileBytes = 0;
+					read_from_adb = true;
+					dataChunkBytes = 0;
+					extraData = false;
+
+					digest.init();
+					adblogwrite("Restoring TWIMG\n");
+					memset(&twimghdr, 0, sizeof(twimghdr));
+					memcpy(&twimghdr, readAdbStream, sizeof(readAdbStream));
+					md5fnsize = twimghdr.size;
+					twimghdrcrc = twimghdr.crc;
+					memset(&twimghdr.crc, 0, sizeof(twimghdr.crc));
+
+					crc = crc32(0L, Z_NULL, 0);
+					crc = crc32(crc, (const unsigned char*) &twimghdr, sizeof(twimghdr));
+					if (crc == twimghdrcrc) {
+						if (write(adb_control_twrp_fd, readAdbStream, sizeof(readAdbStream)) < 1) {
+							std::string msg = "Cannot write to adb_control_twrp_fd: ";
+							printErrMsg(msg, errno);
+							close_restore_fds();
+							return false;
+						}
+					}
+					else {
+						adblogwrite("ADB TWIMG crc header doesn't match\n");
+						close_restore_fds();
+						return false;
+					}
+
+					#ifdef _DEBUG_ADB_BACKUP
+					std::string debug_fname = "/data/media/";
+					debug_fname.append(basename(twimghdr.name));
+					debug_fname.append("-restore.img");
+					adblogwrite("image: " + debug_fname + "\n");
+					debug_adb_fd = open(debug_fname.c_str(), O_WRONLY | O_CREAT, 0666);
+					adblogwrite("Opened restore image\n");
+					#endif
+
+					adblogwrite("opening TW_ADB_RESTORE\n");
+					adb_write_fd = open(TW_ADB_RESTORE, O_WRONLY);
+				}
+				//Tell TWRP we are sending a tar stream
+				else if (cmdtype == TWFN) {
+					struct twfilehdr twfilehdr;
+					uint32_t crc, twfilehdrcrc;
+					fileBytes = 0;
+					md5sumdata = false;
+					read_from_adb = true;
+					dataChunkBytes = 0;
+					extraData = false;
+
+					digest.init();
+					adblogwrite("Restoring TWFN\n");
+					memset(&twfilehdr, 0, sizeof(twfilehdr));
+					memcpy(&twfilehdr, readAdbStream, sizeof(readAdbStream));
+					md5fnsize = twfilehdr.size;
+					twfilehdrcrc = twfilehdr.crc;
+					memset(&twfilehdr.crc, 0, sizeof(twfilehdr.crc));
+
+					crc = crc32(0L, Z_NULL, 0);
+					crc = crc32(crc, (const unsigned char*) &twfilehdr, sizeof(twfilehdr));
+
+					if (crc == twfilehdrcrc) {
+						if (write(adb_control_twrp_fd, readAdbStream, sizeof(readAdbStream)) < 1) {
+							std::string msg = "Cannot write to adb_control_twrp_fd: ";
+							printErrMsg(msg, errno);
+							close_restore_fds();
+							return false;
+						}
+					}
+					else {
+						adblogwrite("ADB TWFN crc header doesn't match\n");
+						close_restore_fds();
+						return false;
+					}
+
+					#ifdef _DEBUG_ADB_BACKUP
+					std::string debug_fname = "/data/media/";
+					debug_fname.append(basename(twfilehdr.name));
+					debug_fname.append("-restore.tar");
+					adblogwrite("tar: " + debug_fname + "\n");
+					debug_adb_fd = open(debug_fname.c_str(), O_WRONLY | O_CREAT, 0666);
+					adblogwrite("Opened restore tar\n");
+					#endif
+
+					compressed = twfilehdr.compressed == 1 ? true: false;
+					adblogwrite("opening TW_ADB_RESTORE\n");
+					adb_write_fd = open(TW_ADB_RESTORE, O_WRONLY);
+				}
+				else if (cmdtype == MD5TRAILER) {
+					if (fileBytes >= md5fnsize)
+						close(adb_write_fd);
+					if (tweofrcvd) {
+						read_from_adb = true;
+						tweofrcvd = false;
+					}
+					else
+						read_from_adb = false; //don't read from adb until TWRP sends TWEOF
+					md5sumdata = false;
+					if (!checkMD5Trailer(readAdbStream, md5fnsize, &digest)) {
+						close_restore_fds();
+						break;
+					}
+					continue;
+				}
+				//Send the tar or partition image md5 to TWRP
+				else if (cmdtype == TWDATA) {
+					dataChunkBytes += sizeof(readAdbStream);
+					while (true) {
+						if ((readbytes = fread(readAdbStream, 1, sizeof(readAdbStream), adbd_fp)) != sizeof(readAdbStream)) {
+							close_restore_fds();
+							return false;
+						}
+						memcpy(&structcmd, readAdbStream, sizeof(readAdbStream));
+						std::string cmdtype = structcmd.get_type();
+
+						dataChunkBytes += readbytes;
+						totalbytes += readbytes;
+						fileBytes += readbytes;
+
+						if (cmdtype == MD5TRAILER) {
+							if (fileBytes >= md5fnsize)
+								close(adb_write_fd);
+							if (tweofrcvd) {
+								tweofrcvd = false;
+								read_from_adb = true;
+							}
+							else
+								read_from_adb = false; //don't read from adb until TWRP sends TWEOF
+							if (!checkMD5Trailer(readAdbStream, md5fnsize, &digest)) {
+								close_restore_fds();
+								break;
+							}
+							break;
+						}
+
+						digest.update((unsigned char*)readAdbStream, readbytes);
+
+						read_from_adb = true;
+
+						#ifdef _DEBUG_ADB_BACKUP
+						if (write(debug_adb_fd, readAdbStream, sizeof(readAdbStream)) < 0) {
+							std::string msg = "Cannot write to ADB_CONTROL_READ_FD: ";
+							printErrMsg(msg, errno);
+							close_restore_fds();
+							return false;
+						}
+						#endif
+
+						if (write(adb_write_fd, readAdbStream, sizeof(readAdbStream)) < 0) {
+							std::string msg = "Cannot write to TWRP ADB FIFO: ";
+							md5sumdata = true;
+							printErrMsg(msg, errno);
+							adblogwrite("end of stream reached.\n"); 
+							break;
+						}
+
+						if (dataChunkBytes == DATA_MAX_CHUNK_SIZE) {
+							dataChunkBytes = 0;
+							md5sumdata = false;
+							break;
+						}
+					}
+				}
+				else if (md5sumdata) {
+					digest.update((unsigned char*)readAdbStream, sizeof(readAdbStream));
+					md5sumdata = true;
+				}
+			}
+		}
+	}
+	std::stringstream str;
+	str << totalbytes;
+	close_restore_fds();
+	adblogwrite(str.str() + " bytes restored from adbbackup\n");
+	return true;
+}
+
+void twrpback::streamFileForTWRP(void) {
+	adblogwrite("streamFileForTwrp" + streamFn + "\n");
+}
+
+void twrpback::setStreamFileName(std::string fn) {
+	streamFn = fn;
+	adbd_fd = open(fn.c_str(), O_RDONLY);
+	if (adbd_fd < 0) {
+		adblogwrite("Unable to open adb_fd\n");
+		close(adbd_fd);
+		return;
+	}
+	restore();
+}
+
+void twrpback::threadStream(void) {
+	pthread_t thread;
+	ThreadPtr streamPtr = &twrpback::streamFileForTWRP;
+	PThreadPtr p = *(PThreadPtr*)&streamPtr;
+	pthread_create(&thread, NULL, p, this);
+	pthread_join(thread, NULL);
+}
+
+bool twrpback::checkMD5Trailer(char readAdbStream[], uint64_t md5fnsize, twrpMD5 *digest) {
+	struct AdbBackupFileTrailer md5tr;
+	uint32_t crc, md5trcrc, md5ident, md5identmatch;
+
+	//ADBSTRUCT_STATIC_ASSERT(sizeof(md5tr) == MAX_ADB_READ);
+	memcpy(&md5tr, readAdbStream, MAX_ADB_READ);
+	md5ident = md5tr.ident;
+
+	memset(&md5tr.ident, 0, sizeof(md5tr.ident));
+
+	md5identmatch = crc32(0L, Z_NULL, 0);
+	md5identmatch = crc32(md5identmatch, (const unsigned char*) &md5tr, sizeof(md5tr));
+	md5identmatch = crc32(md5identmatch, (const unsigned char*) &md5fnsize, sizeof(md5fnsize));
+
+	if (md5identmatch == md5ident) {
+		adblogwrite("checking MD5TRAILER\n");
+		md5trcrc = md5tr.crc;
+		memset(&md5tr.crc, 0, sizeof(md5tr.crc));
+		crc = crc32(0L, Z_NULL, 0);
+		crc = crc32(crc, (const unsigned char*) &md5tr, sizeof(md5tr));
+		if (crc == md5trcrc) {
+			if (write(adb_control_twrp_fd, &md5tr, sizeof(md5tr)) < 1) {
+				std::string msg = "Cannot write to adb_control_twrp_fd: ";
+				printErrMsg(msg, errno);
+				close_restore_fds();
+				return false;
+			}
+		}
+		else {
+			adblogwrite("ADB MD5TRAILER crc header doesn't match\n");
+			close_restore_fds();
+			return false;
+		}
+
+		AdbBackupFileTrailer md5;
+
+		memset(&md5, 0, sizeof(md5));
+		strncpy(md5.start_of_trailer, TWRP, sizeof(md5.start_of_trailer));
+		strncpy(md5.type, TWMD5, sizeof(md5.type));
+		std::string md5string = digest->return_digest_string();
+		strncpy(md5.md5, md5string.c_str(), sizeof(md5.md5));
+
+		adblogwrite("sending MD5 verification: " + md5string + "\n");
+		if (write(adb_control_twrp_fd, &md5, sizeof(md5)) < 1) {
+			std::string msg = "Cannot write to adb_control_twrp_fd: ";
+			printErrMsg(msg, errno);
+			close_restore_fds();
+			return false;
+		}
+		return true;
+	}
+	return false;
+}
diff --git a/adbbu/twrpback.hpp b/adbbu/twrpback.hpp
new file mode 100644
index 0000000..edc1626
--- /dev/null
+++ b/adbbu/twrpback.hpp
@@ -0,0 +1,61 @@
+/*
+		Copyright 2013 to 2017 TeamWin
+		TWRP is free software: you can redistribute it and/or modify
+		it under the terms of the GNU General Public License as published by
+		the Free Software Foundation, either version 3 of the License, or
+		(at your option) any later version.
+
+		TWRP is distributed in the hope that it will be useful,
+		but WITHOUT ANY WARRANTY; without even the implied warranty of
+		MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+		GNU General Public License for more details.
+
+		You should have received a copy of the GNU General Public License
+		along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _TWRPBACK_HPP
+#define _TWRPBACK_HPP
+
+#include <fstream>
+#include "../twrpDigest/twrpMD5.hpp"
+
+class twrpback {
+public:
+	int adbd_fd;                                                             // adbd data stream
+	twrpback(void);
+	virtual ~twrpback(void);
+	bool backup(std::string command);                                        // adb backup stream
+	bool restore(void);                                                      // adb restore stream
+	void adblogwrite(std::string writemsg);                                  // adb debugging log function
+	void createFifos(void);                                                  // create fifos needed for adb backup
+	void closeFifos(void);                                                   // close created fifos
+	void streamFileForTWRP(void);                                            // stream file to twrp via bu
+	void setStreamFileName(std::string fn);                                  // tell adb backup what file to load on storage
+	void threadStream(void);                                                 // thread bu for streaming
+
+private:
+	int read_fd;                                                             // ors input fd
+	int write_fd;                                                            // ors operation fd
+	int ors_fd;                                                              // ors output fd
+	int adb_control_twrp_fd;                                                 // fd for bu to twrp communication
+	int adb_control_bu_fd;                                                   // fd for twrp to bu communication
+	int adb_read_fd;                                                         // adb read data stream
+	int adb_write_fd;                                                        // adb write data stream
+	int debug_adb_fd;                                                        // fd to write debug tars
+	bool firstPart;                                                          // first partition in the stream
+	FILE *adbd_fp;                                                           // file pointer for adb stream
+	char cmd[512];                                                           // store result of commands
+	char operation[512];                                                     // operation to send to ors
+	std::ofstream adblogfile;                                                // adb stream log file
+	std::string streamFn;
+	typedef void (twrpback::*ThreadPtr)(void);
+	typedef void* (*PThreadPtr)(void *);
+	void adbloginit(void);                                                   // setup adb log stream file
+	void close_backup_fds();                                                 // close backup resources
+	void close_restore_fds();                                                // close restore resources
+	bool checkMD5Trailer(char adbReadStream[], uint64_t md5fnsize, twrpMD5* digest); // Check MD5 Trailer
+	void printErrMsg(std::string msg, int errNum);                          // print error msg to adb log
+};
+
+#endif // _TWRPBACK_HPP
diff --git a/applypatch/Android.mk b/applypatch/Android.mk
new file mode 100644
index 0000000..44112fd
--- /dev/null
+++ b/applypatch/Android.mk
@@ -0,0 +1,192 @@
+# Copyright (C) 2008 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.
+
+LOCAL_PATH := $(call my-dir)
+
+# libapplypatch (static library)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+    applypatch.cpp \
+    bspatch.cpp \
+    freecache.cpp \
+    imgpatch.cpp
+LOCAL_MODULE := libapplypatch
+LOCAL_MODULE_TAGS := optional
+LOCAL_C_INCLUDES := \
+    $(LOCAL_PATH)/include \
+    $(commands_recovery_local_path)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_STATIC_LIBRARIES := \
+    libotafault \
+    libbase \
+    libcrypto \
+    libbspatch \
+    libbz \
+    libz
+LOCAL_WHOLE_STATIC_LIBRARIES += libmtdutils
+LOCAL_CFLAGS := \
+    -DZLIB_CONST \
+    -Werror
+
+BOARD_RECOVERY_DEFINES := BOARD_BML_BOOT BOARD_BML_RECOVERY
+
+$(foreach board_define,$(BOARD_RECOVERY_DEFINES), \
+  $(if $($(board_define)), \
+    $(eval LOCAL_CFLAGS += -D$(board_define)=\"$($(board_define))\") \
+  ) \
+  )
+
+include $(BUILD_STATIC_LIBRARY)
+
+# libimgpatch (static library)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+    bspatch.cpp \
+    imgpatch.cpp
+LOCAL_MODULE := libimgpatch
+LOCAL_C_INCLUDES := \
+    $(LOCAL_PATH)/include \
+    $(commands_recovery_local_path)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_STATIC_LIBRARIES := \
+    libcrypto \
+    libbspatch \
+    libbase \
+    libbz \
+    libz
+LOCAL_CFLAGS := \
+    -DZLIB_CONST \
+    -Werror
+include $(BUILD_STATIC_LIBRARY)
+
+# libimgpatch (host static library)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+    bspatch.cpp \
+    imgpatch.cpp
+LOCAL_MODULE := libimgpatch
+LOCAL_MODULE_HOST_OS := linux
+LOCAL_C_INCLUDES := \
+    $(LOCAL_PATH)/include \
+    $(commands_recovery_local_path)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_STATIC_LIBRARIES := \
+    libcrypto \
+    libbspatch \
+    libbase \
+    libbz \
+    libz
+LOCAL_CFLAGS := \
+    -DZLIB_CONST \
+    -Werror
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+# libapplypatch_modes (static library)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+    applypatch_modes.cpp
+LOCAL_MODULE := libapplypatch_modes
+LOCAL_C_INCLUDES := $(commands_recovery_local_path)
+LOCAL_STATIC_LIBRARIES := \
+    libapplypatch \
+    libbase \
+    libedify \
+    libcrypto
+LOCAL_CFLAGS := -Werror
+include $(BUILD_STATIC_LIBRARY)
+
+# applypatch (target executable)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := applypatch_main.cpp
+LOCAL_MODULE := applypatch
+LOCAL_C_INCLUDES := $(commands_recovery_local_path)
+LOCAL_STATIC_LIBRARIES := \
+    libapplypatch_modes \
+    libapplypatch \
+    libbase \
+    libedify \
+    libotafault \
+    libcrypto \
+    libbspatch \
+    libbz
+LOCAL_SHARED_LIBRARIES := \
+    libbase \
+    libz \
+    libcutils
+LOCAL_CFLAGS := -Werror
+include $(BUILD_EXECUTABLE)
+
+libimgdiff_src_files := imgdiff.cpp
+
+# libbsdiff is compiled with -D_FILE_OFFSET_BITS=64.
+libimgdiff_cflags := \
+    -Werror \
+    -D_FILE_OFFSET_BITS=64
+
+libimgdiff_static_libraries := \
+    libbsdiff \
+    libdivsufsort \
+    libdivsufsort64 \
+    libziparchive \
+    libutils \
+    liblog \
+    libbase \
+    libz
+
+# libimgdiff (static library)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+    $(libimgdiff_src_files)
+LOCAL_MODULE := libimgdiff
+LOCAL_CFLAGS := \
+    $(libimgdiff_cflags)
+LOCAL_STATIC_LIBRARIES := \
+    $(libimgdiff_static_libraries)
+LOCAL_C_INCLUDES := \
+    $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+include $(BUILD_STATIC_LIBRARY)
+
+# libimgdiff (host static library)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+    $(libimgdiff_src_files)
+LOCAL_MODULE := libimgdiff
+LOCAL_CFLAGS := \
+    $(libimgdiff_cflags)
+LOCAL_STATIC_LIBRARIES := \
+    $(libimgdiff_static_libraries)
+LOCAL_C_INCLUDES := \
+    $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+# imgdiff (host static executable)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := imgdiff_main.cpp
+LOCAL_MODULE := imgdiff
+LOCAL_CFLAGS := -Werror
+LOCAL_STATIC_LIBRARIES := \
+    libimgdiff \
+    $(libimgdiff_static_libraries) \
+    libbz
+include $(BUILD_HOST_EXECUTABLE)
diff --git a/applypatch/applypatch.cpp b/applypatch/applypatch.cpp
old mode 100644
new mode 100755
index adda697..206a3a3
--- a/applypatch/applypatch.cpp
+++ b/applypatch/applypatch.cpp
@@ -40,6 +40,9 @@
 #include <android-base/unique_fd.h>
 #include <openssl/sha.h>
 
+#include "bmlutils/bmlutils.h"
+#include "mtdutils/mtdutils.h"
+
 #include "edify/expr.h"
 #include "otautil/paths.h"
 #include "otautil/print_sha1.h"
@@ -186,15 +189,8 @@
       return false;
     }
 
-    unsigned char buffer[4096];
-    start = len;
-    for (size_t p = 0; p < len; p += sizeof(buffer)) {
-      size_t to_read = len - p;
-      if (to_read > sizeof(buffer)) {
-        to_read = sizeof(buffer);
-      }
+    const char* partition = pieces[1].c_str();
 
-      if (!android::base::ReadFully(fd, buffer, to_read)) {
         PLOG(ERROR) << "Failed to verify-read " << partition << " at " << p;
         return false;
       }
diff --git a/attr/Android.mk b/attr/Android.mk
new file mode 100755
index 0000000..121ae02
--- /dev/null
+++ b/attr/Android.mk
@@ -0,0 +1,22 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := listxattr.c
+LOCAL_CFLAGS := -c -W
+LOCAL_MODULE := listxattr
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/
+LOCAL_PACK_MODULE_RELOCATIONS := false
+
+ifneq ($(TARGET_ARCH), arm64)
+    ifneq ($(TARGET_ARCH), x86_64)
+        LOCAL_LDFLAGS += -Wl,-dynamic-linker,/system/bin/linker
+    else
+        LOCAL_LDFLAGS += -Wl,-dynamic-linker,/system/bin/linker64
+    endif
+else
+    LOCAL_LDFLAGS += -Wl,-dynamic-linker,/system/bin/linker64
+endif
+
+include $(BUILD_EXECUTABLE)
diff --git a/attr/listxattr.c b/attr/listxattr.c
new file mode 100644
index 0000000..1aa8e14
--- /dev/null
+++ b/attr/listxattr.c
@@ -0,0 +1,202 @@
+/****************************************************************************
+ |  (C) Copyright 2008 Novell, Inc. All Rights Reserved.
+ |
+ |  GPLv2: This program is free software; you can redistribute it
+ |  and/or modify it under the terms of version 2 of the GNU General
+ |  Public License as published by the Free Software Foundation.
+ |
+ |  This program is distributed in the hope that it will be useful,
+ |  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ |  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ |  GNU General Public License for more details.
+ +-------------------------------------------------------------------------*/
+/*
+ * NOTE from Dees_Troy: modified source to display values along with xattr names
+ * Below code comments about usage are no longer accurate but came from the
+ * original source code from the chromium project and combine features of
+ * listxattr and getfattr:
+ * https://chromium.googlesource.com/chromiumos/platform/punybench/+/factory-1235.B/file.m/listxattr.c
+ * https://chromium.googlesource.com/chromiumos/platform/punybench/+/factory-1235.B/file.m/getxattr.c
+ */
+/*
+ * LISTXATTR(2)             Linux Programmer's Manual            LISTXATTR(2)
+ *
+ *
+ *
+ * NAME
+ *        listxattr, llistxattr, flistxattr - list extended attribute names
+ *
+ * SYNOPSIS
+ *        #include <sys/types.h>
+ *        #include <attr/xattr.h>
+ *
+ *        ssize_t listxattr (const char *path,
+ *                             char *list, size_t size);
+ *        ssize_t llistxattr (const char *path,
+ *                             char *list, size_t size);
+ *        ssize_t flistxattr (int filedes,
+ *                             char *list, size_t size);
+ *
+ * DESCRIPTION
+ *        Extended  attributes  are  name:value  pairs associated with inodes
+ *        (files, directories, symlinks, etc).  They are  extensions  to  the
+ *        normal  attributes which are associated with all inodes in the sys-
+ *        tem (i.e. the stat(2)  data).   A  complete  overview  of  extended
+ *        attributes concepts can be found in attr(5).
+ *
+ *        listxattr retrieves the list of extended attribute names associated
+ *        with the given path in the filesystem.  The  list  is  the  set  of
+ *        (NULL-terminated)  names,  one  after the other.  Names of extended
+ *        attributes to which the calling process does not have access may be
+ *        omitted  from  the  list.  The length of the attribute name list is
+ *        returned.
+ *
+ *        llistxattr is identical to listxattr, except in the case of a  sym-
+ *        bolic  link, where the list of names of extended attributes associ-
+ *        ated with the link itself is retrieved, not the file that it refers
+ *        to.
+ *
+ *        flistxattr is identical to listxattr, only the open file pointed to
+ *        by filedes (as returned by open(2)) is  interrogated  in  place  of
+ *        path.
+ *
+ *        A  single  extended  attribute  name  is  a  simple NULL-terminated
+ *        string.  The name includes a namespace prefix; there  may  be  sev-
+ *        eral, disjoint namespaces associated with an individual inode.
+ *
+ *        An  empty  buffer  of  size  zero can be passed into these calls to
+ *        return the current size of the list of  extended  attribute  names,
+ *        which  can be used to estimate the size of a buffer which is suffi-
+ *        ciently large to hold the list of names.
+ *
+ * EXAMPLES
+ *        The list of names is returned as an unordered array of  NULL-termi-
+ *        nated  character  strings  (attribute  names  are separated by NULL
+ *        characters), like this:
+ *               user.name1\0system.name1\0user.name2\0
+ *
+ *        Filesystems like ext2, ext3 and  XFS  which  implement  POSIX  ACLs
+ *        using extended attributes, might return a list like this:
+ *               system.posix_acl_access\0system.posix_acl_default\0
+ *
+ * RETURN VALUE
+ *        On  success,  a  positive number is returned indicating the size of
+ *        the extended attribute name list.  On failure, -1 is  returned  and
+ *        errno is set appropriately.
+ *
+ *        If  the  size  of  the list buffer is too small to hold the result,
+ *        errno is set to ERANGE.
+ *
+ *        If extended attributes are not supported by the filesystem, or  are
+ *        disabled, errno is set to ENOTSUP.
+ *
+ *        The errors documented for the stat(2) system call are also applica-
+ *        ble here.
+ *
+ * AUTHORS
+ *        Andreas Gruenbacher, <a.gruenbacher@computer.org> and the  SGI  XFS
+ *        development  team,  <linux-xfs@oss.sgi.com>.   Please  send any bug
+ *        reports or comments to these addresses.
+ *
+ * SEE ALSO
+ *        getfattr(1),  setfattr(1),  getxattr(2),  open(2),  removexattr(2),
+ *        setxattr(2), stat(2), attr(5)
+ *
+ *
+ *
+ * Dec 2001                    Extended Attributes               LISTXATTR(2)
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/xattr.h>
+//#include <eprintf.h>
+//#include <puny.h>
+
+/* dumpmem: dumps an n byte area of memory to screen */
+void dumpmem (const void *mem, unsigned int n)
+{
+	const char *c = mem;
+	unsigned i;
+	int all_text = 1;
+	if (n == 0) {
+		printf("<empty>");
+		return;
+	}
+	for (i = 0; i < n - 1; i++, c++) {
+		if (!isprint(*c)) {
+			all_text = 0;
+			break;
+		}
+	}
+	c = mem;
+	if (all_text) {
+		for (i = 0; i < n - 1; i++, c++) {
+			putchar(*c);
+		}
+		return;
+	} else {
+		char hex[(n * 2) + 1];
+		for(i = 0; i < n; i++, c++)
+			sprintf(hex + (i * 2), "%02X", *c);
+		hex[n] = 0;
+		printf("0x%s", hex);
+		return;
+	}
+}
+
+void dump_list (char *file, char *list, ssize_t size)
+{
+	int	c;
+	int	i;
+	int	first = 1;
+	int j = 0;
+	char xattr[1024];
+	char value[1024];
+	ssize_t	size2;
+	for (i = 0; i < size; i++) {
+		c = list[i];
+		if (c) {
+			if (first) {
+				putchar('\t');
+				first = 0;
+				j = 0;
+			}
+			putchar(c);
+			xattr[j++] = list[i];
+		} else {
+			xattr[j] = '\0';
+			size2 = getxattr(file, xattr, value, sizeof(value));
+			if (size2 < 0) {
+				printf("file=%s xattr=%s returned:", file, xattr);
+			} else {
+				putchar('=');
+				dumpmem(value, size2);
+			}
+			putchar('\n');
+			first = 1;
+		}
+	}
+}
+void usage (void)
+{
+	printf("listxattr <file>");
+}
+char	List[1<<17];
+int main (int argc, char *argv[])
+{
+	ssize_t	size;
+	if (argc < 2) {
+		usage();
+		exit(2);
+	}
+	size = listxattr(argv[1], List, sizeof(List));
+	if (size == -1) {
+		perror(argv[1]);
+		exit(2);
+	}
+	printf("xattrs for %s:\n", argv[1]);
+	dump_list(argv[1], List, size);
+	return 0;
+}
diff --git a/bmlutils/Android.mk b/bmlutils/Android.mk
new file mode 100644
index 0000000..9cbada0
--- /dev/null
+++ b/bmlutils/Android.mk
@@ -0,0 +1,34 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+BOARD_RECOVERY_DEFINES := BOARD_BML_BOOT BOARD_BML_RECOVERY
+LOCAL_STATIC_LIBRARY := libcrecovery
+
+$(foreach board_define,$(BOARD_RECOVERY_DEFINES), \
+  $(if $($(board_define)), \
+    $(eval LOCAL_CFLAGS += -D$(board_define)=\"$($(board_define))\") \
+  ) \
+  )
+
+LOCAL_SRC_FILES := bmlutils.c
+LOCAL_MODULE := libbmlutils
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_STATIC_LIBRARY)
+
+#Added for building TWRP dynamic:
+include $(CLEAR_VARS)
+
+BOARD_RECOVERY_DEFINES := BOARD_BML_BOOT BOARD_BML_RECOVERY
+LOCAL_SHARED_LIBRARIES := libcrecovery
+
+$(foreach board_define,$(BOARD_RECOVERY_DEFINES), \
+  $(if $($(board_define)), \
+    $(eval LOCAL_CFLAGS += -D$(board_define)=\"$($(board_define))\") \
+  ) \
+  )
+
+LOCAL_SRC_FILES := bmlutils.c
+LOCAL_MODULE := libbmlutils
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_SHARED_LIBRARY)
diff --git a/bmlutils/bmlutils.c b/bmlutils/bmlutils.c
new file mode 100755
index 0000000..382c0a5
--- /dev/null
+++ b/bmlutils/bmlutils.c
@@ -0,0 +1,197 @@
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <limits.h>
+#include <linux/input.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/reboot.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <sys/wait.h>
+#include <sys/limits.h>
+#include <dirent.h>
+#include <sys/stat.h>
+
+#include <signal.h>
+#include <sys/wait.h>
+
+#include <bmlutils.h>
+
+#include "../libcrecovery/common.h"
+
+static int restore_internal(const char* bml, const char* filename)
+{
+    char buf[4096];
+    int dstfd, srcfd, bytes_read, total_read = 0;
+    if (filename == NULL)
+        srcfd = 0;
+    else {
+        srcfd = open(filename, O_RDONLY | O_LARGEFILE);
+        if (srcfd < 0)
+            return 2;
+    }
+    dstfd = open(bml, O_RDWR | O_LARGEFILE);
+    if (dstfd < 0)
+        return 3;
+    if (ioctl(dstfd, BML_UNLOCK_ALL, 0))
+        return 4;
+    do {
+        total_read += bytes_read = read(srcfd, buf, 4096);
+        if (!bytes_read)
+            break;
+        if (bytes_read < 4096)
+            memset(&buf[bytes_read], 0, 4096 - bytes_read);
+        if (write(dstfd, buf, 4096) < 4096)
+            return 5;
+    } while(bytes_read == 4096);
+    
+    close(dstfd);
+    close(srcfd);
+    
+    return 0;
+}
+
+int cmd_bml_restore_raw_partition(const char *partition, const char *filename)
+{
+    if (strcmp(partition, "boot") != 0 && strcmp(partition, "recovery") != 0 && strcmp(partition, "recoveryonly") != 0 && partition[0] != '/')
+        return 6;
+
+    int ret = -1;
+    if (strcmp(partition, "recoveryonly") != 0) {
+        // always restore boot, regardless of whether recovery or boot is flashed.
+        // this is because boot and recovery are the same on some samsung phones.
+        // unless of course, recoveryonly is explictly chosen (bml8)
+        ret = restore_internal(BOARD_BML_BOOT, filename);
+        if (ret != 0)
+            return ret;
+    }
+
+    if (strcmp(partition, "recovery") == 0 || strcmp(partition, "recoveryonly") == 0)
+        ret = restore_internal(BOARD_BML_RECOVERY, filename);
+
+    // support explicitly provided device paths
+    if (partition[0] == '/')
+        ret = restore_internal(partition, filename);
+    return ret;
+}
+
+int cmd_bml_backup_raw_partition(const char *partition, const char *out_file)
+{
+    const char* bml;
+    if (strcmp("boot", partition) == 0)
+        bml = BOARD_BML_BOOT;
+    else if (strcmp("recovery", partition) == 0)
+        bml = BOARD_BML_RECOVERY;
+    else if (partition[0] == '/') {
+        // support explicitly provided device paths
+        bml = partition;
+    }
+    else {
+        printf("Invalid partition.\n");
+        return -1;
+    }
+
+    int ch;
+    FILE *in;
+    FILE *out;
+    char buf[512];
+    unsigned sz = 0;
+    unsigned i;
+    int ret = -1;
+    const char *in_file = bml;
+
+    in  = fopen ( in_file,  "r" );
+    if (in == NULL)
+        goto ERROR3;
+
+    out = fopen ( out_file,  "w" );
+    if (out == NULL)
+        goto ERROR2;
+
+    fseek(in, 0L, SEEK_END);
+    sz = ftell(in);
+    fseek(in, 0L, SEEK_SET);
+
+    if (sz % 512)
+    {
+        while ( ( ch = fgetc ( in ) ) != EOF )
+            fputc ( ch, out );
+    }
+    else
+    {
+        for (i=0; i< (sz/512); i++)
+        {
+            if ((fread(buf, 512, 1, in)) != 1)
+                goto ERROR1;
+            if ((fwrite(buf, 512, 1, out)) != 1)
+                goto ERROR1;
+        }
+    }
+
+    fsync(fileno(out));
+    ret = 0;
+ERROR1:
+    fclose ( out );
+ERROR2:
+    fclose ( in );
+ERROR3:
+    return ret;
+}
+
+int cmd_bml_erase_raw_partition(const char *partition __unused)
+{
+    // TODO: implement raw wipe
+    return 0;
+}
+
+int cmd_bml_erase_partition(const char *partition __unused, const char *filesystem __unused)
+{
+    return -1;
+}
+
+int cmd_bml_mount_partition(const char *partition __unused, const char *mount_point __unused, const char *filesystem __unused, int read_only __unused)
+{
+    return -1;
+}
+
+int cmd_bml_get_partition_device(const char *partition __unused, char *device __unused)
+{
+    return -1;
+}
+
+int format_rfs_device (const char *device, const char *path) {
+    const char *fatsize = "32";
+    const char *sectorsize = "1";
+
+    if (strcmp(path, "/datadata") == 0 || strcmp(path, "/cache") == 0) {
+        fatsize = "16";
+    }
+
+    // Just in case /data sector size needs to be altered
+    else if (strcmp(path, "/data") == 0 ) {
+        sectorsize = "1";
+    } 
+
+    // dump 10KB of zeros to partition before format due to fat.format bug
+    char cmd[PATH_MAX];
+
+    sprintf(cmd, "/system/bin/dd if=/dev/zero of=%s bs=4096 count=10", device);
+    if(__system(cmd)) {
+        printf("failure while zeroing rfs partition.\n");
+        return -1;
+    }
+
+    // Run fat.format
+    sprintf(cmd, "/system/bin/fat.format -F %s -S 4096 -s %s %s", fatsize, sectorsize, device);
+    if(__system(cmd)) {
+        printf("failure while running fat.format\n");
+        return -1;
+    }
+
+    return 0;
+}
diff --git a/bmlutils/bmlutils.h b/bmlutils/bmlutils.h
new file mode 100644
index 0000000..1e85f08
--- /dev/null
+++ b/bmlutils/bmlutils.h
@@ -0,0 +1,16 @@
+#ifndef BMLUTILS_H_
+#define BMLUTILS_H_
+
+int format_rfs_device (const char *device, const char *path);
+
+#define BML_UNLOCK_ALL				0x8A29		///< unlock all partition RO -> RW
+
+#ifndef BOARD_BML_BOOT
+#define BOARD_BML_BOOT              "/dev/block/bml7"
+#endif
+
+#ifndef BOARD_BML_RECOVERY
+#define BOARD_BML_RECOVERY          "/dev/block/bml8"
+#endif
+
+#endif // BMLUTILS_H_
diff --git a/bootloader_message/bootloader_message.cpp b/bootloader_message/bootloader_message.cpp
old mode 100644
new mode 100755
diff --git a/crypto/ext4crypt/Android.mk b/crypto/ext4crypt/Android.mk
new file mode 100755
index 0000000..8b1372b
--- /dev/null
+++ b/crypto/ext4crypt/Android.mk
@@ -0,0 +1,111 @@
+LOCAL_PATH := $(call my-dir)
+ifeq ($(TW_INCLUDE_CRYPTO), true)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libe4crypt
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS :=
+LOCAL_SRC_FILES := Decrypt.cpp ScryptParameters.cpp Utils.cpp HashPassword.cpp ext4_crypt.cpp
+LOCAL_SHARED_LIBRARIES := libselinux libc libc++ libext4_utils libbase libcrypto libcutils libkeymaster_messages libhardware libprotobuf-cpp-lite
+LOCAL_STATIC_LIBRARIES := libscrypt_static
+LOCAL_C_INCLUDES := system/extras/ext4_utils \
+    system/extras/ext4_utils/include/ext4_utils \
+    external/scrypt/lib/crypto \
+    system/security/keystore/include \
+    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
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0)
+    #8.0 or higher
+    LOCAL_CFLAGS += -DHAVE_GATEKEEPER1
+    ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 29; echo $$?),0)
+        LOCAL_SHARED_LIBRARIES += android.hardware.confirmationui@1.0
+    endif
+    LOCAL_SHARED_LIBRARIES += android.hardware.keymaster@3.0 libkeystore_binder libhidlbase libutils libbinder android.hardware.gatekeeper@1.0
+    ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 28; echo $$?),0)
+        #9.0 rules
+        LOCAL_CFLAGS += -DUSE_KEYSTORAGE_4 -Wno-unused-variable -Wno-sign-compare -Wno-unused-parameter -Wno-comment
+        LOCAL_SRC_FILES += Ext4CryptPie.cpp Keymaster4.cpp KeyStorage4.cpp KeyUtil.cpp MetadataCrypt.cpp KeyBuffer.cpp
+        LOCAL_SHARED_LIBRARIES += android.hardware.keymaster@4.0 libkeymaster4support
+        LOCAL_SHARED_LIBRARIES += android.hardware.gatekeeper@1.0 libkeystore_parcelables libkeystore_aidl
+        LOCAL_CFLAGS += -DHAVE_SYNTH_PWD_SUPPORT
+        LOCAL_SRC_FILES += Weaver1.cpp
+        LOCAL_SHARED_LIBRARIES += android.hardware.weaver@1.0
+        LOCAL_CFLAGS += -DHAVE_LIBKEYUTILS
+        LOCAL_SHARED_LIBRARIES += libkeyutils
+    else
+        #8.0 rules
+        LOCAL_CFLAGS += -DUSE_KEYSTORAGE_3
+        LOCAL_SRC_FILES += Ext4Crypt.cpp Keymaster3.cpp KeyStorage3.cpp
+        ifneq ($(wildcard hardware/interfaces/weaver/Android.bp),)
+            #only present in some 8.0 trees and should be in all 8.1 trees
+            LOCAL_CFLAGS += -DHAVE_SYNTH_PWD_SUPPORT
+            LOCAL_SRC_FILES += Weaver1.cpp
+            LOCAL_SHARED_LIBRARIES += android.hardware.weaver@1.0
+        endif
+        ifneq ($(wildcard system/core/libkeyutils/Android.bp),)
+            #only present in some 8.0 trees and should be in all 8.1 trees
+            LOCAL_CFLAGS += -DHAVE_LIBKEYUTILS
+            LOCAL_SHARED_LIBRARIES += libkeyutils
+        endif
+    endif
+    ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 28; echo $$?),0)
+        LOCAL_REQUIRED_MODULES := keystore_auth
+    else
+        LOCAL_ADDITIONAL_DEPENDENCIES := keystore_auth
+    endif
+else
+    #7.x rules
+    LOCAL_SRC_FILES += Ext4Crypt.cpp Keymaster.cpp KeyStorage.cpp
+endif
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 28; echo $$?),0)
+    LOCAL_SHARED_LIBRARIES += libsoftkeymaster
+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)/
+LOCAL_SRC_FILES := main.cpp
+LOCAL_SHARED_LIBRARIES := libe4crypt
+
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := e4policyget
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/
+LOCAL_SRC_FILES := e4policyget.cpp
+LOCAL_SHARED_LIBRARIES := libe4crypt
+LOCAL_LDFLAGS += -Wl,-dynamic-linker,/system/bin/linker64
+
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := keystore_auth
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/
+LOCAL_SRC_FILES := keystore_auth.cpp
+LOCAL_SHARED_LIBRARIES := libc libkeystore_binder libutils libbinder liblog
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 28; echo $$?),0)
+    #9.0
+    LOCAL_CFLAGS += -DUSE_SECURITY_NAMESPACE
+    LOCAL_SHARED_LIBRARIES += libkeystore_aidl
+endif
+LOCAL_LDFLAGS += -Wl,-dynamic-linker,/system/bin/linker64
+
+include $(BUILD_EXECUTABLE)
+
+endif
diff --git a/crypto/ext4crypt/Decrypt.cpp b/crypto/ext4crypt/Decrypt.cpp
new file mode 100755
index 0000000..d486aa4
--- /dev/null
+++ b/crypto/ext4crypt/Decrypt.cpp
@@ -0,0 +1,1359 @@
+/*
+ * 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"
+#ifdef USE_KEYSTORAGE_4
+#include "Ext4CryptPie.h"
+#else
+#include "Ext4Crypt.h"
+#endif
+
+#include <map>
+#include <string>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#ifndef HAVE_LIBKEYUTILS
+#include "key_control.h"
+#else
+#include <keyutils.h>
+#endif
+
+#ifdef HAVE_SYNTH_PWD_SUPPORT
+#include "Weaver1.h"
+#include "cutils/properties.h"
+
+#include <openssl/sha.h>
+#include <openssl/aes.h>
+#include <openssl/evp.h>
+#include <openssl/rand.h>
+
+#include <dirent.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+#include <fstream>
+
+#include "ext4_crypt.h"
+
+#ifdef USE_KEYSTORAGE_4
+#include <android/hardware/confirmationui/1.0/types.h>
+#include <android/security/BnConfirmationPromptCallback.h>
+#include <android/security/keystore/IKeystoreService.h>
+#else
+#include <keystore/IKeystoreService.h>
+#include <keystore/authorization_set.h>
+#endif
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+
+#include <keystore/keystore.h>
+
+#include <algorithm>
+extern "C" {
+#include "crypto_scrypt.h"
+}
+#else
+#include "ext4_crypt.h"
+#endif //ifdef HAVE_SYNTH_PWD_SUPPORT
+
+#ifdef HAVE_GATEKEEPER1
+#include <android/hardware/gatekeeper/1.0/IGatekeeper.h>
+#else
+#include <hardware/gatekeeper.h>
+#endif
+#include "HashPassword.h"
+
+#include <android-base/file.h>
+
+#ifdef USE_KEYSTORAGE_4
+using android::security::keystore::IKeystoreService;
+#endif
+
+// Store main DE raw ref / policy
+extern std::string de_raw_ref;
+extern std::map<userid_t, std::string> s_de_key_raw_refs;
+extern std::map<userid_t, std::string> s_ce_key_raw_refs;
+
+static bool lookup_ref_key_internal(std::map<userid_t, std::string>& key_map, const char* policy, userid_t* user_id) {
+    for (std::map<userid_t, std::string>::iterator it=key_map.begin(); it!=key_map.end(); ++it) {
+        if (strncmp(it->second.c_str(), policy, it->second.size()) == 0) {
+            *user_id = it->first;
+            return true;
+        }
+    }
+    return false;
+}
+
+extern "C" bool lookup_ref_key(const char* policy, char* policy_type) {
+    userid_t user_id = 0;
+    if (strncmp(de_raw_ref.c_str(), policy, de_raw_ref.size()) == 0) {
+        strcpy(policy_type, "1DK");
+        return true;
+    }
+    if (!lookup_ref_key_internal(s_de_key_raw_refs, policy, &user_id)) {
+        if (!lookup_ref_key_internal(s_ce_key_raw_refs, policy, &user_id)) {
+            return false;
+		} else
+		    sprintf(policy_type, "1CE%d", user_id);
+    } else
+        sprintf(policy_type, "1DE%d", user_id);
+    return true;
+}
+
+extern "C" bool lookup_ref_tar(const char* policy_type, char* policy) {
+    if (strncmp(policy_type, "1", 1) != 0) {
+        printf("Unexpected version %c\n", policy_type[0]);
+        return false;
+    }
+    const char* ptr = policy_type + 1; // skip past the version number
+    if (strncmp(ptr, "DK", 2) == 0) {
+        strncpy(policy, de_raw_ref.data(), de_raw_ref.size());
+        return true;
+    }
+    userid_t user_id = atoi(ptr + 2);
+    std::string raw_ref;
+    if (*ptr == 'D') {
+        if (lookup_key_ref(s_de_key_raw_refs, user_id, &raw_ref)) {
+            strncpy(policy, raw_ref.data(), raw_ref.size());
+        } else
+            return false;
+    } else if (*ptr == 'C') {
+        if (lookup_key_ref(s_ce_key_raw_refs, user_id, &raw_ref)) {
+            strncpy(policy, raw_ref.data(), raw_ref.size());
+        } else
+            return false;
+    } else {
+        printf("unknown policy type '%s'\n", policy_type);
+        return false;
+    }
+    return true;
+}
+
+#ifndef HAVE_GATEKEEPER1
+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;
+}
+#endif //ifndef HAVE_GATEKEEPER1
+
+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;
+}
+
+#ifdef HAVE_SYNTH_PWD_SUPPORT
+// Crappy functions for debugging, please ignore unless you need to debug
+/*void output_hex(const std::string& in) {
+	const char *buf = in.data();
+	char hex[in.size() * 2 + 1];
+	unsigned int index;
+	for (index = 0; index < in.size(); index++)
+		sprintf(&hex[2 * index], "%02X", buf[index]);
+	printf("%s", hex);
+}
+
+void output_hex(const char* buf, const int size) {
+	char hex[size * 2 + 1];
+	int index;
+	for (index = 0; index < size; index++)
+		sprintf(&hex[2 * index], "%02X", buf[index]);
+	printf("%s", hex);
+}
+
+void output_hex(const unsigned char* buf, const int size) {
+	char hex[size * 2 + 1];
+	int index;
+	for (index = 0; index < size; index++)
+		sprintf(&hex[2 * index], "%02X", buf[index]);
+	printf("%s", hex);
+}
+
+void output_hex(std::vector<uint8_t>* vec) {
+	char hex[3];
+	unsigned int index;
+	for (index = 0; index < vec->size(); index++) {
+		sprintf(&hex[0], "%02X", vec->at(index));
+		printf("%s", hex);
+	}
+}*/
+
+/* An alternative is to use:
+ * sqlite3 /data/system/locksettings.db "SELECT value FROM locksettings WHERE name='sp-handle' AND user=0;"
+ * but we really don't want to include the 1.1MB libsqlite in TWRP. We scan the spblob folder for the
+ * password data file (*.pwd) and get the handle from the filename instead. This is a replacement for
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/LockSettingsService.java#2017
+ * We never use this data as an actual long. We always use it as a string. */
+bool Find_Handle(const std::string& spblob_path, std::string& handle_str) {
+	DIR* dir = opendir(spblob_path.c_str());
+	if (!dir) {
+		printf("Error opening '%s'\n", spblob_path.c_str());
+		return false;
+	}
+
+	struct dirent* de = 0;
+
+	while ((de = readdir(dir)) != 0) {
+		if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
+			continue;
+		size_t len = strlen(de->d_name);
+		if (len <= 4)
+			continue;
+		char* p = de->d_name;
+		p += len - 4;
+		if (strncmp(p, ".pwd", 4) == 0) {
+			handle_str = de->d_name;
+			handle_str = handle_str.substr(0, len - 4);
+			//*handle = strtoull(handle_str.c_str(), 0 , 16);
+			closedir(dir);
+			return true;
+		}
+	}
+	closedir(dir);
+	return false;
+}
+
+/* This is the structure of the data in the password data (*.pwd) file which the structure can be found
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#187 */
+struct password_data_struct {
+	int password_type;
+	unsigned char scryptN;
+	unsigned char scryptR;
+	unsigned char scryptP;
+	int salt_len;
+	void* salt;
+	int handle_len;
+	void* password_handle;
+};
+
+/* C++ replacement for
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#764 */
+bool Get_Password_Data(const std::string& spblob_path, const std::string& handle_str, password_data_struct *pwd) {
+	std::string pwd_file = spblob_path + handle_str + ".pwd";
+	std::string pwd_data;
+	if (!android::base::ReadFileToString(pwd_file, &pwd_data)) {
+		printf("Failed to read '%s'\n", pwd_file.c_str());
+		return false;
+	}
+	//output_hex(pwd_data.data(), pwd_data.size());printf("\n");
+	const int* intptr = (const int*)pwd_data.data();
+	pwd->password_type = *intptr;
+	endianswap(&pwd->password_type);
+	//printf("password type %i\n", pwd->password_type); // 2 was PIN, 1 for pattern, 2 also for password, -1 for default password
+	const unsigned char* byteptr = (const unsigned char*)pwd_data.data() + sizeof(int);
+	pwd->scryptN = *byteptr;
+	byteptr++;
+	pwd->scryptR = *byteptr;
+	byteptr++;
+	pwd->scryptP = *byteptr;
+	byteptr++;
+	intptr = (const int*)byteptr;
+	pwd->salt_len = *intptr;
+	endianswap(&pwd->salt_len);
+	if (pwd->salt_len != 0) {
+		pwd->salt = malloc(pwd->salt_len);
+		if (!pwd->salt) {
+			printf("Get_Password_Data malloc salt\n");
+			return false;
+		}
+		memcpy(pwd->salt, intptr + 1, pwd->salt_len);
+		intptr++;
+		byteptr = (const unsigned char*)intptr;
+		byteptr += pwd->salt_len;
+	} else {
+		printf("Get_Password_Data salt_len is 0\n");
+		return false;
+	}
+	intptr = (const int*)byteptr;
+	pwd->handle_len = *intptr;
+	endianswap(&pwd->handle_len);
+	if (pwd->handle_len != 0) {
+		pwd->password_handle = malloc(pwd->handle_len);
+		if (!pwd->password_handle) {
+			printf("Get_Password_Data malloc password_handle\n");
+			return false;
+		}
+		memcpy(pwd->password_handle, intptr + 1, pwd->handle_len);
+	} else {
+		printf("Get_Password_Data handle_len is 0\n");
+		// Not an error if using weaver
+	}
+	return true;
+}
+
+/* C++ replacement for
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#765
+ * called here
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#1050 */
+bool Get_Password_Token(const password_data_struct *pwd, const std::string& Password, unsigned char* password_token) {
+	if (!password_token) {
+		printf("password_token is null\n");
+		return false;
+	}
+	unsigned int N = 1 << pwd->scryptN;
+	unsigned int r = 1 << pwd->scryptR;
+	unsigned int p = 1 << pwd->scryptP;
+	//printf("N %i r %i p %i\n", N, r, p);
+	int ret = crypto_scrypt(reinterpret_cast<const uint8_t*>(Password.data()), Password.size(),
+                          reinterpret_cast<const uint8_t*>(pwd->salt), pwd->salt_len,
+                          N, r, p,
+                          password_token, 32);
+	if (ret != 0) {
+		printf("scrypt error\n");
+		return false;
+	}
+	return true;
+}
+
+// Data structure for the *.weaver file, see Get_Weaver_Data below
+struct weaver_data_struct {
+	unsigned char version;
+	int slot;
+};
+
+/* C++ replacement for
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#501
+ * called here
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#768 */
+bool Get_Weaver_Data(const std::string& spblob_path, const std::string& handle_str, weaver_data_struct *wd) {
+	std::string weaver_file = spblob_path + handle_str + ".weaver";
+	std::string weaver_data;
+	if (!android::base::ReadFileToString(weaver_file, &weaver_data)) {
+		printf("Failed to read '%s'\n", weaver_file.c_str());
+		return false;
+	}
+	//output_hex(weaver_data.data(), weaver_data.size());printf("\n");
+	const unsigned char* byteptr = (const unsigned char*)weaver_data.data();
+	wd->version = *byteptr;
+	//printf("weaver version %i\n", wd->version);
+	const int* intptr = (const int*)weaver_data.data() + sizeof(unsigned char);
+	wd->slot = *intptr;
+	//endianswap(&wd->slot); not needed
+	//printf("weaver slot %i\n", wd->slot);
+	return true;
+}
+
+namespace android {
+
+// On Android 8.0 for some reason init can't seem to completely stop keystore
+// so we have to kill it too if it doesn't die on its own.
+static void kill_keystore() {
+    DIR* dir = opendir("/proc");
+    if (dir) {
+        struct dirent* de = 0;
+
+        while ((de = readdir(dir)) != 0) {
+            if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
+                continue;
+
+            int pid = -1;
+            int ret = sscanf(de->d_name, "%d", &pid);
+
+            if (ret == 1) {
+                char cmdpath[PATH_MAX];
+                sprintf(cmdpath, "/proc/%d/cmdline", pid);
+
+                FILE* file = fopen(cmdpath, "r");
+                size_t task_size = PATH_MAX;
+                char task[PATH_MAX];
+                char* p = task;
+                if (getline(&p, &task_size, file) > 0) {
+                    if (strstr(task, "keystore") != 0) {
+                        printf("keystore pid %d found, sending kill.\n", pid);
+                        kill(pid, SIGINT);
+                        usleep(5000);
+                        kill(pid, SIGKILL);
+                    }
+                }
+                fclose(file);
+            }
+        }
+        closedir(dir);
+    }
+}
+
+// The keystore holds a file open on /data so we have to stop / kill it
+// if we want to be able to unmount /data for things like formatting.
+static void stop_keystore() {
+    printf("Stopping keystore...\n");
+    property_set("ctl.stop", "keystore");
+    usleep(5000);
+    kill_keystore();
+}
+
+/* These next 2 functions try to get the keystore service 50 times because
+ * the keystore is not always ready when TWRP boots */
+sp<IBinder> getKeystoreBinder() {
+	sp<IServiceManager> sm = defaultServiceManager();
+    return sm->getService(String16("android.security.keystore"));
+}
+
+sp<IBinder> getKeystoreBinderRetry() {
+	printf("Starting keystore...\n");
+    property_set("ctl.start", "keystore");
+	int retry_count = 50;
+	sp<IBinder> binder = getKeystoreBinder();
+	while (binder == NULL && retry_count) {
+		printf("Waiting for keystore service... %i\n", retry_count--);
+		sleep(1);
+		binder = getKeystoreBinder();
+	}
+	return binder;
+}
+
+namespace keystore {
+
+#define SYNTHETIC_PASSWORD_VERSION_V1 1
+#define SYNTHETIC_PASSWORD_VERSION_V2 2
+#define SYNTHETIC_PASSWORD_VERSION_V3 3
+#define SYNTHETIC_PASSWORD_PASSWORD_BASED 0
+#define SYNTHETIC_PASSWORD_KEY_PREFIX "USRSKEY_synthetic_password_"
+#define USR_PRIVATE_KEY_PREFIX "USRPKEY_synthetic_password_"
+
+static std::string mKey_Prefix;
+
+/* The keystore alias subid is sometimes the same as the handle, but not always.
+ * In the case of handle 0c5303fd2010fe29, the alias subid used c5303fd2010fe29
+ * without the leading 0. We could try to parse the data from a previous
+ * keystore request, but I think this is an easier solution because there
+ * is little to no documentation on the format of data we get back from
+ * the keystore in this instance. We also want to copy everything to a temp
+ * folder so that any key upgrades that might take place do not actually
+ * upgrade the keys on the data partition. We rename all 1000 uid files to 0
+ * to pass the keystore permission checks. */
+bool Find_Keystore_Alias_SubID_And_Prep_Files(const userid_t user_id, std::string& keystoreid, const std::string& handle_str) {
+	char path_c[PATH_MAX];
+	sprintf(path_c, "/data/misc/keystore/user_%d", user_id);
+	char user_dir[PATH_MAX];
+	sprintf(user_dir, "user_%d", user_id);
+	std::string source_path = "/data/misc/keystore/";
+	source_path += user_dir;
+	std::string handle_sub = handle_str;
+	while (handle_sub.substr(0,1) == "0") {
+		std::string temp = handle_sub.substr(1);
+		handle_sub = temp;
+	}
+	mKey_Prefix = "";
+
+	mkdir("/tmp/misc", 0755);
+	mkdir("/tmp/misc/keystore", 0755);
+	std::string destination_path = "/tmp/misc/keystore/";
+	destination_path += user_dir;
+	if (mkdir(destination_path.c_str(), 0755) && errno != EEXIST) {
+		printf("failed to mkdir '%s' %s\n", destination_path.c_str(), strerror(errno));
+		return false;
+	}
+	destination_path += "/";
+
+	DIR* dir = opendir(source_path.c_str());
+	if (!dir) {
+		printf("Error opening '%s'\n", source_path.c_str());
+		return false;
+	}
+	source_path += "/";
+
+	struct dirent* de = 0;
+	size_t prefix_len = strlen(SYNTHETIC_PASSWORD_KEY_PREFIX);
+	bool found_subid = false;
+	bool has_pkey = false; // PKEY has priority over SKEY
+
+	while ((de = readdir(dir)) != 0) {
+		if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
+			continue;
+		if (!found_subid) {
+			size_t len = strlen(de->d_name);
+			if (len <= prefix_len)
+				continue;
+			if (strstr(de->d_name, SYNTHETIC_PASSWORD_KEY_PREFIX) && !has_pkey)
+				mKey_Prefix = SYNTHETIC_PASSWORD_KEY_PREFIX;
+			else if (strstr(de->d_name, USR_PRIVATE_KEY_PREFIX)) {
+				mKey_Prefix = USR_PRIVATE_KEY_PREFIX;
+				has_pkey = true;
+			} else
+				continue;
+			if (strstr(de->d_name, handle_sub.c_str())) {
+				keystoreid = handle_sub;
+				printf("keystoreid matched handle_sub: '%s'\n", keystoreid.c_str());
+				found_subid = true;
+			} else {
+				std::string file = de->d_name;
+				std::size_t found = file.find_last_of("_");
+				if (found != std::string::npos) {
+					keystoreid = file.substr(found + 1);
+					printf("possible keystoreid: '%s'\n", keystoreid.c_str());
+					//found_subid = true; // we'll keep going in hopes that we find a pkey or a match to the handle_sub
+				}
+			}
+		}
+		std::string src = source_path;
+		src += de->d_name;
+		std::ifstream srcif(src.c_str(), std::ios::binary);
+		std::string dst = destination_path;
+		dst += de->d_name;
+		std::size_t source_uid = dst.find("1000");
+		if (source_uid != std::string::npos)
+			dst.replace(source_uid, 4, "0");
+		std::ofstream dstof(dst.c_str(), std::ios::binary);
+		printf("copying '%s' to '%s'\n", src.c_str(), dst.c_str());
+		dstof << srcif.rdbuf();
+		srcif.close();
+		dstof.close();
+	}
+	closedir(dir);
+	if (!found_subid && !mKey_Prefix.empty() && !keystoreid.empty())
+		found_subid = true;
+	return found_subid;
+}
+
+/* C++ replacement for function of the same name
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#867
+ * returning an empty string indicates an error */
+std::string unwrapSyntheticPasswordBlob(const std::string& spblob_path, const std::string& handle_str, const userid_t user_id, const void* application_id, const size_t application_id_size, uint32_t auth_token_len) {
+	std::string disk_decryption_secret_key = "";
+
+	std::string keystore_alias_subid;
+	// Can be stored in user 0, so check for both.
+	if (!Find_Keystore_Alias_SubID_And_Prep_Files(user_id, keystore_alias_subid, handle_str) &&
+		!Find_Keystore_Alias_SubID_And_Prep_Files(0, keystore_alias_subid, handle_str)) 
+	{
+    	printf("failed to scan keystore alias subid and prep keystore files\n");
+    	return disk_decryption_secret_key;
+  	}
+
+	// First get the keystore service
+	sp<IBinder> binder = getKeystoreBinderRetry();
+#ifdef USE_KEYSTORAGE_4
+	sp<IKeystoreService> service = interface_cast<IKeystoreService>(binder);
+#else
+	sp<IKeystoreService> service = interface_cast<IKeystoreService>(binder);
+#endif
+	if (service == NULL) {
+		printf("error: could not connect to keystore service\n");
+		return disk_decryption_secret_key;
+	}
+
+	if (auth_token_len > 0) {
+		printf("Starting keystore_auth service...\n");
+		property_set("ctl.start", "keystore_auth");
+	}
+
+	// Read the data from the .spblob file per: https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#869
+	std::string spblob_file = spblob_path + handle_str + ".spblob";
+	std::string spblob_data;
+	if (!android::base::ReadFileToString(spblob_file, &spblob_data)) {
+		printf("Failed to read '%s'\n", spblob_file.c_str());
+		return disk_decryption_secret_key;
+	}
+	unsigned char* byteptr = (unsigned char*)spblob_data.data();
+	if (*byteptr != SYNTHETIC_PASSWORD_VERSION_V2 && *byteptr != SYNTHETIC_PASSWORD_VERSION_V1
+			&& *byteptr != SYNTHETIC_PASSWORD_VERSION_V3) {
+		printf("Unsupported synthetic password version %i\n", *byteptr);
+		return disk_decryption_secret_key;
+	}
+	const unsigned char* synthetic_password_version = byteptr;
+	byteptr++;
+	if (*byteptr != SYNTHETIC_PASSWORD_PASSWORD_BASED) {
+		printf("spblob data is not SYNTHETIC_PASSWORD_PASSWORD_BASED\n");
+		return disk_decryption_secret_key;
+	}
+	byteptr++; // Now we're pointing to the blob data itself
+	if (*synthetic_password_version == SYNTHETIC_PASSWORD_VERSION_V1) {
+		printf("spblob v1\n");
+		/* We're now going to handle decryptSPBlob: https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#115
+		 * Called from https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#879
+		 * This small function ends up being quite a headache. The call to get data from the keystore basically is not needed in TWRP at this time.
+		 * The keystore data seems to be the serialized data from an entire class in Java. Specifically I think it represents:
+		 * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
+		 * or perhaps
+		 * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java
+		 * but the only things we "need" from this keystore are a user ID and the keyAlias which ends up being USRSKEY_synthetic_password_{handle_str}
+		 * the latter of which we already have. We may need to figure out how to get the user ID if we ever support decrypting mulitple users.
+		 * There are 2 calls to a Java decrypt funcion that is overloaded. These 2 calls go in completely different directions despite the seemingly
+		 * similar use of decrypt() and decrypt parameters. To figure out where things were going, I added logging to:
+		 * https://android.googlesource.com/platform/libcore/+/android-8.0.0_r23/ojluni/src/main/java/javax/crypto/Cipher.java#2575
+		 * Logger.global.severe("Cipher tryCombinations " + prov.getName() + " - " + prov.getInfo());
+		 * To make logging work in libcore, import java.util.logging.Logger; and either set a better logging level or modify the framework to log everything
+		 * regardless of logging level. This will give you some strings that you can grep for and find the actual crypto provider in use. In our case there were
+		 * 2 different providers in use. The first stage to get the intermediate key used:
+		 * https://android.googlesource.com/platform/external/conscrypt/+/android-8.0.0_r23/common/src/main/java/org/conscrypt/OpenSSLProvider.java
+		 * which is a pretty straight-forward OpenSSL implementation of AES/GCM/NoPadding. */
+		// First we personalize as seen https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#102
+		void* personalized_application_id = PersonalizedHashBinary(PERSONALISATION_APPLICATION_ID, (const char*)application_id, application_id_size);
+		if (!personalized_application_id) {
+			printf("malloc personalized_application_id\n");
+			return disk_decryption_secret_key;
+		}
+		//printf("personalized application id: "); output_hex((unsigned char*)personalized_application_id, SHA512_DIGEST_LENGTH); printf("\n");
+		// Now we'll decrypt using openssl AES/GCM/NoPadding
+		OpenSSL_add_all_ciphers();
+		int actual_size=0, final_size=0;
+		EVP_CIPHER_CTX *d_ctx = EVP_CIPHER_CTX_new();
+		const unsigned char* iv = (const unsigned char*)byteptr; // The IV is the first 12 bytes of the spblob
+		//printf("iv: "); output_hex((const unsigned char*)iv, 12); printf("\n");
+		const unsigned char* cipher_text = (const unsigned char*)byteptr + 12; // The cipher text comes immediately after the IV
+		//printf("cipher_text: "); output_hex((const unsigned char*)cipher_text, spblob_data.size() - 2 - 12); printf("\n");
+		const unsigned char* key = (const unsigned char*)personalized_application_id; // The key is the now personalized copy of the application ID
+		//printf("key: "); output_hex((const unsigned char*)key, 32); printf("\n");
+		EVP_DecryptInit(d_ctx, EVP_aes_256_gcm(), key, iv);
+		std::vector<unsigned char> intermediate_key;
+		intermediate_key.resize(spblob_data.size() - 2 - 12, '\0');
+		EVP_DecryptUpdate(d_ctx, &intermediate_key[0], &actual_size, cipher_text, spblob_data.size() - 2 - 12);
+		unsigned char tag[AES_BLOCK_SIZE];
+		EVP_CIPHER_CTX_ctrl(d_ctx, EVP_CTRL_GCM_SET_TAG, 16, tag);
+		EVP_DecryptFinal_ex(d_ctx, &intermediate_key[actual_size], &final_size);
+		EVP_CIPHER_CTX_free(d_ctx);
+		free(personalized_application_id);
+		//printf("spblob_data size: %lu actual_size %i, final_size: %i\n", spblob_data.size(), actual_size, final_size);
+		intermediate_key.resize(actual_size + final_size - 16, '\0');// not sure why we have to trim the size by 16 as I don't see where this is done in Java side
+		//printf("intermediate key: "); output_hex((const unsigned char*)intermediate_key.data(), intermediate_key.size()); printf("\n");
+
+		// When using secdis (aka not weaver) you must supply an auth token to the keystore prior to the begin operation
+		if (auth_token_len > 0) {
+			/*::keystore::KeyStoreServiceReturnCode auth_result = service->addAuthToken(auth_token, auth_token_len);
+			if (!auth_result.isOk()) {
+				// The keystore checks the uid of the calling process and will return a permission denied on this operation for user 0
+				printf("keystore error adding auth token\n");
+				return disk_decryption_secret_key;
+			}*/
+			// The keystore refuses to allow the root user to supply auth tokens, so we write the auth token to a file earlier and
+			// run a separate service that runs user the system user to add the auth token. We wait for the auth token file to be
+			// deleted by the keymaster_auth service and check for a /auth_error file in case of errors. We quit after after a while if
+			// the /auth_token file never gets deleted.
+			int auth_wait_count = 20;
+			while (access("/auth_token", F_OK) == 0 && auth_wait_count-- > 0)
+				usleep(5000);
+			if (auth_wait_count == 0 || access("/auth_error", F_OK) == 0) {
+				printf("error during keymaster_auth service\n");
+				/* If you are getting this error, make sure that you have the keymaster_auth service defined in your init scripts, preferrably in init.recovery.{ro.hardware}.rc
+				 * service keystore_auth /system/bin/keystore_auth
+				 *     disabled
+				 *     oneshot
+				 *     user system
+				 *     group root
+				 *     seclabel u:r:recovery:s0
+				 *
+				 * And check dmesg for error codes regarding this service if needed. */
+				return disk_decryption_secret_key;
+			}
+		}
+
+		int32_t ret;
+
+		/* We only need a keyAlias which is USRSKEY_synthetic_password_b6f71045af7bd042 which we find and a uid which is -1 or 1000, I forget which
+		 * as the key data will be read again by the begin function later via the keystore.
+		 * The data is in a hidl_vec format which consists of a type and a value. */
+		/*::keystore::hidl_vec<uint8_t> data;
+		std::string keystoreid = SYNTHETIC_PASSWORD_KEY_PREFIX;
+		keystoreid += handle_str;
+
+		ret = service->get(String16(keystoreid.c_str()), user_id, &data);
+		if (ret < 0) {
+			printf("Could not connect to keystore service %i\n", ret);
+			return disk_decryption_secret_key;
+		} else if (ret != 1 /*android::keystore::ResponseCode::NO_ERROR*//*) {
+			printf("keystore error: (%d)\n", /*responses[ret],*//* ret);
+			return disk_decryption_secret_key;
+		} else {
+			printf("keystore returned: "); output_hex(&data[0], data.size()); printf("\n");
+		}*/
+
+		// Now we'll break up the intermediate key into the IV (first 12 bytes) and the cipher text (the rest of it).
+		std::vector<unsigned char> nonce = intermediate_key;
+		nonce.resize(12);
+		intermediate_key.erase (intermediate_key.begin(),intermediate_key.begin()+12);
+		//printf("nonce: "); output_hex((const unsigned char*)nonce.data(), nonce.size()); printf("\n");
+		//printf("cipher text: "); output_hex((const unsigned char*)intermediate_key.data(), intermediate_key.size()); printf("\n");
+
+		/* Now we will begin the second decrypt call found in
+		 * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#122
+		 * This time we will use https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
+		 * and https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java
+		 * First we set some algorithm parameters as seen in two places:
+		 * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java#297
+		 * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java#216 */
+		size_t maclen = 128;
+		::keystore::AuthorizationSetBuilder begin_params;
+		begin_params.Authorization(::keystore::TAG_ALGORITHM, ::keystore::Algorithm::AES);
+		begin_params.Authorization(::keystore::TAG_BLOCK_MODE, ::keystore::BlockMode::GCM);
+		begin_params.Padding(::keystore::PaddingMode::NONE);
+		begin_params.Authorization(::keystore::TAG_NONCE, nonce);
+		begin_params.Authorization(::keystore::TAG_MAC_LENGTH, maclen);
+		//keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES);
+		//keymasterArgs.addEnum(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockMode);
+		//keymasterArgs.addEnum(KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding);
+		//keymasterArgs.addUnsignedInt(KeymasterDefs.KM_TAG_MAC_LENGTH, mTagLengthBits);
+		::keystore::hidl_vec<uint8_t> entropy; // No entropy is needed for decrypt
+		entropy.resize(0);
+		std::string keystore_alias = mKey_Prefix;
+		keystore_alias += keystore_alias_subid;
+		String16 keystore_alias16(keystore_alias.c_str());
+#ifdef USE_KEYSTORAGE_4
+		android::hardware::keymaster::V4_0::KeyPurpose purpose = android::hardware::keymaster::V4_0::KeyPurpose::DECRYPT;
+		security::keymaster::OperationResult begin_result;
+		security::keymaster::OperationResult update_result;
+		security::keymaster::OperationResult finish_result;
+		::android::security::keymaster::KeymasterArguments empty_params;
+		// These parameters are mostly driven by the cipher.init call https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#63
+		service->begin(binder, keystore_alias16, (int32_t)purpose, true, android::security::keymaster::KeymasterArguments(begin_params.hidl_data()), entropy, -1, &begin_result);
+#else
+		::keystore::KeyPurpose purpose = ::keystore::KeyPurpose::DECRYPT;
+		OperationResult begin_result;
+		OperationResult update_result;
+		OperationResult finish_result;
+		::keystore::hidl_vec<::keystore::KeyParameter> empty_params;
+		empty_params.resize(0);
+		// These parameters are mostly driven by the cipher.init call https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#63
+		service->begin(binder, keystore_alias16, purpose, true, begin_params.hidl_data(), entropy, -1, &begin_result);
+#endif
+		ret = begin_result.resultCode;
+		if (ret != 1 /*android::keystore::ResponseCode::NO_ERROR*/) {
+			printf("keystore begin error: (%d)\n", /*responses[ret],*/ ret);
+			return disk_decryption_secret_key;
+		} else {
+			//printf("keystore begin operation successful\n");
+		}
+		// The cipher.doFinal call triggers an update to the keystore followed by a finish https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#64
+		// See also https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java#208
+		service->update(begin_result.token, empty_params, intermediate_key, &update_result);
+		ret = update_result.resultCode;
+		if (ret != 1 /*android::keystore::ResponseCode::NO_ERROR*/) {
+			printf("keystore update error: (%d)\n", /*responses[ret],*/ ret);
+			return disk_decryption_secret_key;
+		} else {
+			//printf("keystore update operation successful\n");
+			//printf("keystore update returned: "); output_hex(&update_result.data[0], update_result.data.size()); printf("\n"); // this ends up being the synthetic password
+		}
+		// We must use the data in update_data.data before we call finish below or the data will be gone
+		// The payload data from the keystore update is further personalized at https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#153
+		// We now have the disk decryption key!
+		disk_decryption_secret_key = PersonalizedHash(PERSONALIZATION_FBE_KEY, (const char*)&update_result.data[0], update_result.data.size());
+		//printf("disk_decryption_secret_key: '%s'\n", disk_decryption_secret_key.c_str());
+		::keystore::hidl_vec<uint8_t> signature;
+		service->finish(begin_result.token, empty_params, signature, entropy, &finish_result);
+		ret = finish_result.resultCode;
+		if (ret != 1 /*android::keystore::ResponseCode::NO_ERROR*/) {
+			printf("keystore finish error: (%d)\n", /*responses[ret],*/ ret);
+			return disk_decryption_secret_key;
+		} else {
+			//printf("keystore finish operation successful\n");
+		}
+		stop_keystore();
+		return disk_decryption_secret_key;
+	} else if (*synthetic_password_version == SYNTHETIC_PASSWORD_VERSION_V2
+			|| *synthetic_password_version == SYNTHETIC_PASSWORD_VERSION_V3) {
+		printf("spblob v2 / v3\n");
+		/* Version 2 / 3 of the spblob is basically the same as version 1, but the order of getting the intermediate key and disk decryption key have been flip-flopped
+		 * as seen in https://android.googlesource.com/platform/frameworks/base/+/5025791ac6d1538224e19189397de8d71dcb1a12
+		 */
+		/* First decrypt call found in
+		 * https://android.googlesource.com/platform/frameworks/base/+/android-8.1.0_r18/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#135
+		 * We will use https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
+		 * and https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java
+		 * First we set some algorithm parameters as seen in two places:
+		 * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java#297
+		 * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java#216 */
+		// When using secdis (aka not weaver) you must supply an auth token to the keystore prior to the begin operation
+		if (auth_token_len > 0) {
+			/*::keystore::KeyStoreServiceReturnCode auth_result = service->addAuthToken(auth_token, auth_token_len);
+			if (!auth_result.isOk()) {
+				// The keystore checks the uid of the calling process and will return a permission denied on this operation for user 0
+				printf("keystore error adding auth token\n");
+				return disk_decryption_secret_key;
+			}*/
+			// The keystore refuses to allow the root user to supply auth tokens, so we write the auth token to a file earlier and
+			// run a separate service that runs user the system user to add the auth token. We wait for the auth token file to be
+			// deleted by the keymaster_auth service and check for a /auth_error file in case of errors. We quit after after a while if
+			// the /auth_token file never gets deleted.
+			int auth_wait_count = 20;
+			while (access("/auth_token", F_OK) == 0 && auth_wait_count-- > 0)
+				usleep(5000);
+			if (auth_wait_count == 0 || access("/auth_error", F_OK) == 0) {
+				printf("error during keymaster_auth service\n");
+				/* If you are getting this error, make sure that you have the keymaster_auth service defined in your init scripts, preferrably in init.recovery.{ro.hardware}.rc
+				 * service keystore_auth /system/bin/keystore_auth
+				 *     disabled
+				 *     oneshot
+				 *     user system
+				 *     group root
+				 *     seclabel u:r:recovery:s0
+				 *
+				 * And check dmesg for error codes regarding this service if needed. */
+				return disk_decryption_secret_key;
+			}
+		}
+		int32_t ret;
+		size_t maclen = 128;
+		unsigned char* iv = (unsigned char*)byteptr; // The IV is the first 12 bytes of the spblob
+		::keystore::hidl_vec<uint8_t> iv_hidlvec;
+		iv_hidlvec.setToExternal((unsigned char*)byteptr, 12);
+		//printf("iv: "); output_hex((const unsigned char*)iv, 12); printf("\n");
+		unsigned char* cipher_text = (unsigned char*)byteptr + 12; // The cipher text comes immediately after the IV
+		::keystore::hidl_vec<uint8_t> cipher_text_hidlvec;
+		cipher_text_hidlvec.setToExternal(cipher_text, spblob_data.size() - 14 /* 1 each for version and SYNTHETIC_PASSWORD_PASSWORD_BASED and 12 for the iv */);
+		::keystore::AuthorizationSetBuilder begin_params;
+		begin_params.Authorization(::keystore::TAG_ALGORITHM, ::keystore::Algorithm::AES);
+		begin_params.Authorization(::keystore::TAG_BLOCK_MODE, ::keystore::BlockMode::GCM);
+		begin_params.Padding(::keystore::PaddingMode::NONE);
+		begin_params.Authorization(::keystore::TAG_NONCE, iv_hidlvec);
+		begin_params.Authorization(::keystore::TAG_MAC_LENGTH, maclen);
+		::keystore::hidl_vec<uint8_t> entropy; // No entropy is needed for decrypt
+		entropy.resize(0);
+		std::string keystore_alias = mKey_Prefix;
+		keystore_alias += keystore_alias_subid;
+		String16 keystore_alias16(keystore_alias.c_str());
+#ifdef USE_KEYSTORAGE_4
+		android::hardware::keymaster::V4_0::KeyPurpose purpose = android::hardware::keymaster::V4_0::KeyPurpose::DECRYPT;
+		security::keymaster::OperationResult begin_result;
+		security::keymaster::OperationResult update_result;
+		security::keymaster::OperationResult finish_result;
+		::android::security::keymaster::KeymasterArguments empty_params;
+		// These parameters are mostly driven by the cipher.init call https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#63
+		service->begin(binder, keystore_alias16, (int32_t)purpose, true, android::security::keymaster::KeymasterArguments(begin_params.hidl_data()), entropy, -1, &begin_result);
+#else
+		::keystore::KeyPurpose purpose = ::keystore::KeyPurpose::DECRYPT;
+		OperationResult begin_result;
+		OperationResult update_result;
+		OperationResult finish_result;
+		::keystore::hidl_vec<::keystore::KeyParameter> empty_params;
+		empty_params.resize(0);
+		// These parameters are mostly driven by the cipher.init call https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#63
+		service->begin(binder, keystore_alias16, purpose, true, begin_params.hidl_data(), entropy, -1, &begin_result);
+#endif
+		ret = begin_result.resultCode;
+		if (ret != 1 /*android::keystore::ResponseCode::NO_ERROR*/) {
+			printf("keystore begin error: (%d)\n", /*responses[ret],*/ ret);
+			return disk_decryption_secret_key;
+		} /*else {
+			printf("keystore begin operation successful\n");
+		}*/
+		// The cipher.doFinal call triggers an update to the keystore followed by a finish https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#64
+		// See also https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java#208
+		service->update(begin_result.token, empty_params, cipher_text_hidlvec, &update_result);
+		ret = update_result.resultCode;
+		if (ret != 1 /*android::keystore::ResponseCode::NO_ERROR*/) {
+			printf("keystore update error: (%d)\n", /*responses[ret],*/ ret);
+			return disk_decryption_secret_key;
+		} /*else {
+			printf("keystore update operation successful\n");
+			printf("keystore update returned: "); output_hex(&update_result.data[0], update_result.data.size()); printf("\n"); // this ends up being the synthetic password
+		}*/
+		//printf("keystore resulting data: "); output_hex((unsigned char*)&update_result.data[0], update_result.data.size()); printf("\n");
+		// We must copy the data in update_data.data before we call finish below or the data will be gone
+		size_t keystore_result_size = update_result.data.size();
+		unsigned char* keystore_result = (unsigned char*)malloc(keystore_result_size);
+		if (!keystore_result) {
+			printf("malloc on keystore_result\n");
+			return disk_decryption_secret_key;
+		}
+		memcpy(keystore_result, &update_result.data[0], update_result.data.size());
+		//printf("keystore_result data: "); output_hex(keystore_result, keystore_result_size); printf("\n");
+		::keystore::hidl_vec<uint8_t> signature;
+		service->finish(begin_result.token, empty_params, signature, entropy, &finish_result);
+		ret = finish_result.resultCode;
+		if (ret != 1 /*android::keystore::ResponseCode::NO_ERROR*/) {
+			printf("keystore finish error: (%d)\n", /*responses[ret],*/ ret);
+			free(keystore_result);
+			return disk_decryption_secret_key;
+		} /*else {
+			printf("keystore finish operation successful\n");
+		}*/
+		stop_keystore();
+
+		/* Now we do the second decrypt call as seen in:
+		 * https://android.googlesource.com/platform/frameworks/base/+/android-8.1.0_r18/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#136
+		 */
+		const unsigned char* intermediate_iv = keystore_result;
+		//printf("intermediate_iv: "); output_hex((const unsigned char*)intermediate_iv, 12); printf("\n");
+		const unsigned char* intermediate_cipher_text = (const unsigned char*)keystore_result + 12; // The cipher text comes immediately after the IV
+		int cipher_size = keystore_result_size - 12;
+		//printf("intermediate_cipher_text: "); output_hex((const unsigned char*)intermediate_cipher_text, cipher_size); printf("\n");
+		// First we personalize as seen https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#102
+		void* personalized_application_id = PersonalizedHashBinary(PERSONALISATION_APPLICATION_ID, (const char*)application_id, application_id_size);
+		if (!personalized_application_id) {
+			printf("malloc personalized_application_id\n");
+			free(keystore_result);
+			return disk_decryption_secret_key;
+		}
+		//printf("personalized application id: "); output_hex((unsigned char*)personalized_application_id, SHA512_DIGEST_LENGTH); printf("\n");
+		// Now we'll decrypt using openssl AES/GCM/NoPadding
+		OpenSSL_add_all_ciphers();
+		int actual_size=0, final_size=0;
+		EVP_CIPHER_CTX *d_ctx = EVP_CIPHER_CTX_new();
+		const unsigned char* key = (const unsigned char*)personalized_application_id; // The key is the now personalized copy of the application ID
+		//printf("key: "); output_hex((const unsigned char*)key, 32); printf("\n");
+		EVP_DecryptInit(d_ctx, EVP_aes_256_gcm(), key, intermediate_iv);
+		unsigned char* secret_key = (unsigned char*)malloc(cipher_size);
+		EVP_DecryptUpdate(d_ctx, secret_key, &actual_size, intermediate_cipher_text, cipher_size);
+		unsigned char tag[AES_BLOCK_SIZE];
+		EVP_CIPHER_CTX_ctrl(d_ctx, EVP_CTRL_GCM_SET_TAG, 16, tag);
+		EVP_DecryptFinal_ex(d_ctx, secret_key + actual_size, &final_size);
+		EVP_CIPHER_CTX_free(d_ctx);
+		free(personalized_application_id);
+		free(keystore_result);
+		int secret_key_real_size = actual_size - 16;
+		//printf("secret key:  "); output_hex((const unsigned char*)secret_key, secret_key_real_size); printf("\n");
+		// The payload data from the keystore update is further personalized at https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#153
+		// We now have the disk decryption key!
+		if (*synthetic_password_version == SYNTHETIC_PASSWORD_VERSION_V3) {
+			// V3 uses SP800 instead of SHA512
+			disk_decryption_secret_key = PersonalizedHashSP800(PERSONALIZATION_FBE_KEY, PERSONALISATION_CONTEXT, (const char*)secret_key, secret_key_real_size);
+		} else {
+			disk_decryption_secret_key = PersonalizedHash(PERSONALIZATION_FBE_KEY, (const char*)secret_key, secret_key_real_size);
+		}
+		//printf("disk_decryption_secret_key: '%s'\n", disk_decryption_secret_key.c_str());
+		free(secret_key);
+		return disk_decryption_secret_key;
+	}
+	return disk_decryption_secret_key;
+}
+
+}}
+
+#define PASSWORD_TOKEN_SIZE 32
+
+/* C++ replacement for
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#992
+ * called here
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#813 */
+bool Get_Secdis(const std::string& spblob_path, const std::string& handle_str, std::string& secdis_data) {
+	std::string secdis_file = spblob_path + handle_str + ".secdis";
+	if (!android::base::ReadFileToString(secdis_file, &secdis_data)) {
+		printf("Failed to read '%s'\n", secdis_file.c_str());
+		return false;
+	}
+	//output_hex(secdis_data.data(), secdis_data.size());printf("\n");
+	return true;
+}
+
+// C++ replacement for https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#1033
+userid_t fakeUid(const userid_t uid) {
+    return 100000 + uid;
+}
+
+bool Is_Weaver(const std::string& spblob_path, const std::string& handle_str) {
+	std::string weaver_file = spblob_path + handle_str + ".weaver";
+	struct stat st;
+	if (stat(weaver_file.c_str(), &st) == 0)
+		return true;
+	return false;
+}
+
+bool Free_Return(bool retval, void* weaver_key, password_data_struct* pwd) {
+	if (weaver_key)
+		free(weaver_key);
+	if (pwd->salt)
+		free(pwd->salt);
+	if (pwd->password_handle)
+		free(pwd->password_handle);
+	return retval;
+}
+
+/* Decrypt_User_Synth_Pass is the TWRP C++ equivalent to spBasedDoVerifyCredential
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/LockSettingsService.java#1998 */
+bool Decrypt_User_Synth_Pass(const userid_t user_id, const std::string& Password) {
+	bool retval = false;
+	void* weaver_key = NULL;
+	password_data_struct pwd;
+	pwd.salt = NULL;
+	pwd.salt_len = 0;
+	pwd.password_handle = NULL;
+	pwd.handle_len = 0;
+	char application_id[PASSWORD_TOKEN_SIZE + SHA512_DIGEST_LENGTH];
+
+    uint32_t auth_token_len = 0;
+
+	std::string secret; // this will be the disk decryption key that is sent to vold
+	std::string token = "!"; // there is no token used for this kind of decrypt, key escrow is handled by weaver
+	int flags = FLAG_STORAGE_DE;
+    if (user_id == 0)
+		flags = FLAG_STORAGE_DE;
+	else
+		flags = FLAG_STORAGE_CE;
+	char spblob_path_char[PATH_MAX];
+	sprintf(spblob_path_char, "/data/system_de/%d/spblob/", user_id);
+	std::string spblob_path = spblob_path_char;
+	long handle = 0;
+	std::string handle_str;
+	// Get the handle: https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/LockSettingsService.java#2017
+	if (!Find_Handle(spblob_path, handle_str)) {
+		printf("Error getting handle\n");
+		return Free_Return(retval, weaver_key, &pwd);
+	}
+	printf("Handle is '%s'\n", handle_str.c_str());
+	// Now we begin driving unwrapPasswordBasedSyntheticPassword from: https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#758
+	// First we read the password data which contains scrypt parameters
+	if (!Get_Password_Data(spblob_path, handle_str, &pwd)) {
+		printf("Failed to Get_Password_Data\n");
+		return Free_Return(retval, weaver_key, &pwd);
+	}
+	//printf("pwd N %i R %i P %i salt ", pwd.scryptN, pwd.scryptR, pwd.scryptP); output_hex((char*)pwd.salt, pwd.salt_len); printf("\n");
+	unsigned char password_token[PASSWORD_TOKEN_SIZE];
+	//printf("Password: '%s'\n", Password.c_str());
+	// The password token is the password scrypted with the parameters from the password data file
+	if (!Get_Password_Token(&pwd, Password, &password_token[0])) {
+		printf("Failed to Get_Password_Token\n");
+		return Free_Return(retval, weaver_key, &pwd);
+	}
+	//output_hex(&password_token[0], PASSWORD_TOKEN_SIZE);printf("\n");
+	if (Is_Weaver(spblob_path, handle_str)) {
+		printf("using weaver\n");
+		// BEGIN PIXEL 2 WEAVER
+		// Get the weaver data from the .weaver file which tells us which slot to use when we ask weaver for the escrowed key
+		// https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#768
+		weaver_data_struct wd;
+		if (!Get_Weaver_Data(spblob_path, handle_str, &wd)) {
+			printf("Failed to get weaver data\n");
+			return Free_Return(retval, weaver_key, &pwd);
+		}
+		// The weaver key is the the password token prefixed with "weaver-key" padded to 128 with nulls with the password token appended then SHA512
+		// https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#1059
+		weaver_key = PersonalizedHashBinary(PERSONALISATION_WEAVER_KEY, (char*)&password_token[0], PASSWORD_TOKEN_SIZE);
+		if (!weaver_key) {
+			printf("malloc error getting weaver_key\n");
+			return Free_Return(retval, weaver_key, &pwd);
+		}
+		// Now we start driving weaverVerify: https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#343
+		// Called from https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#776
+		android::vold::Weaver weaver;
+		if (!weaver) {
+			printf("Failed to get weaver service\n");
+			return Free_Return(retval, weaver_key, &pwd);
+		}
+		// Get the key size from weaver service
+		uint32_t weaver_key_size = 0;
+		if (!weaver.GetKeySize(&weaver_key_size)) {
+			printf("Failed to get weaver key size\n");
+			return Free_Return(retval, weaver_key, &pwd);
+		} else {
+			//printf("weaver key size is %u\n", weaver_key_size);
+		}
+		//printf("weaver key: "); output_hex((unsigned char*)weaver_key, weaver_key_size); printf("\n");
+		// Send the slot from the .weaver file, the computed weaver key, and get the escrowed key data
+		std::vector<uint8_t> weaver_payload;
+		// TODO: we should return more information about the status including time delays before the next retry
+		if (!weaver.WeaverVerify(wd.slot, weaver_key, &weaver_payload)) {
+			printf("failed to weaver verify\n");
+			return Free_Return(retval, weaver_key, &pwd);
+		}
+		//printf("weaver payload: "); output_hex(&weaver_payload); printf("\n");
+		// Done with weaverVerify
+		// Now we will compute the application ID
+		// https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#964
+		// Called from https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#780
+		// The escrowed weaver key data is prefixed with "weaver-pwd" padded to 128 with nulls with the weaver payload appended then SHA512
+		void* weaver_secret = PersonalizedHashBinary(PERSONALISATION_WEAVER_PASSWORD, (const char*)weaver_payload.data(), weaver_payload.size());
+		//printf("weaver secret: "); output_hex((unsigned char*)weaver_secret, SHA512_DIGEST_LENGTH); printf("\n");
+		// The application ID is the password token and weaver secret appended to each other
+		memcpy((void*)&application_id[0], (void*)&password_token[0], PASSWORD_TOKEN_SIZE);
+		memcpy((void*)&application_id[PASSWORD_TOKEN_SIZE], weaver_secret, SHA512_DIGEST_LENGTH);
+		//printf("application ID: "); output_hex((unsigned char*)application_id, PASSWORD_TOKEN_SIZE + SHA512_DIGEST_LENGTH); printf("\n");
+		// END PIXEL 2 WEAVER
+	} else {
+		printf("using secdis\n");
+		std::string secdis_data;
+		if (!Get_Secdis(spblob_path, handle_str, secdis_data)) {
+			printf("Failed to get secdis data\n");
+			return Free_Return(retval, weaver_key, &pwd);
+		}
+		void* secdiscardable = PersonalizedHashBinary(PERSONALISATION_SECDISCARDABLE, (char*)secdis_data.data(), secdis_data.size());
+		if (!secdiscardable) {
+			printf("malloc error getting secdiscardable\n");
+			return Free_Return(retval, weaver_key, &pwd);
+		}
+		memcpy((void*)&application_id[0], (void*)&password_token[0], PASSWORD_TOKEN_SIZE);
+		memcpy((void*)&application_id[PASSWORD_TOKEN_SIZE], secdiscardable, SHA512_DIGEST_LENGTH);
+
+		int ret = -1;
+		bool request_reenroll = false;
+		android::sp<android::hardware::gatekeeper::V1_0::IGatekeeper> gk_device;
+		gk_device = ::android::hardware::gatekeeper::V1_0::IGatekeeper::getService();
+		if (gk_device == nullptr) {
+			printf("failed to get gatekeeper service\n");
+			return Free_Return(retval, weaver_key, &pwd);
+		}
+		if (pwd.handle_len <= 0) {
+			printf("no password handle supplied\n");
+			return Free_Return(retval, weaver_key, &pwd);
+		}
+		android::hardware::hidl_vec<uint8_t> pwd_handle_hidl;
+		pwd_handle_hidl.setToExternal(const_cast<uint8_t *>((const uint8_t *)pwd.password_handle), pwd.handle_len);
+		void* gk_pwd_token = PersonalizedHashBinary(PERSONALIZATION_USER_GK_AUTH, (char*)&password_token[0], PASSWORD_TOKEN_SIZE);
+		if (!gk_pwd_token) {
+			printf("malloc error getting gatekeeper_key\n");
+			return Free_Return(retval, weaver_key, &pwd);
+		}
+		android::hardware::hidl_vec<uint8_t> gk_pwd_token_hidl;
+		gk_pwd_token_hidl.setToExternal(const_cast<uint8_t *>((const uint8_t *)gk_pwd_token), SHA512_DIGEST_LENGTH);
+		android::hardware::Return<void> hwRet =
+			gk_device->verify(fakeUid(user_id), 0 /* challange */,
+							  pwd_handle_hidl,
+							  gk_pwd_token_hidl,
+							  [&ret, &request_reenroll, &auth_token_len]
+								(const android::hardware::gatekeeper::V1_0::GatekeeperResponse &rsp) {
+									ret = static_cast<int>(rsp.code); // propagate errors
+									if (rsp.code >= android::hardware::gatekeeper::V1_0::GatekeeperStatusCode::STATUS_OK) {
+										auth_token_len = rsp.data.size();
+										request_reenroll = (rsp.code == android::hardware::gatekeeper::V1_0::GatekeeperStatusCode::STATUS_REENROLL);
+										ret = 0; // all success states are reported as 0
+										// The keystore refuses to allow the root user to supply auth tokens, so we write the auth token to a file here and later
+										// run a separate service that runs as the system user to add the auth token. We wait for the auth token file to be
+										// deleted by the keymaster_auth service and check for a /auth_error file in case of errors. We quit after a while seconds if
+										// the /auth_token file never gets deleted.
+										unlink("/auth_token");
+										FILE* auth_file = fopen("/auth_token","wb");
+										if (auth_file != NULL) {
+											fwrite(rsp.data.data(), sizeof(uint8_t), rsp.data.size(), auth_file);
+											fclose(auth_file);
+										} else {
+											printf("failed to open /auth_token for writing\n");
+											ret = -2;
+										}
+									} else if (rsp.code == android::hardware::gatekeeper::V1_0::GatekeeperStatusCode::ERROR_RETRY_TIMEOUT && rsp.timeout > 0) {
+										ret = rsp.timeout;
+									}
+								}
+							 );
+		free(gk_pwd_token);
+		if (!hwRet.isOk() || ret != 0) {
+			printf("gatekeeper verification failed\n");
+			return Free_Return(retval, weaver_key, &pwd);
+		}
+	}
+	// Now we will handle https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#816
+	// Plus we will include the last bit that computes the disk decrypt key found in:
+	// https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#153
+	secret = android::keystore::unwrapSyntheticPasswordBlob(spblob_path, handle_str, user_id, (const void*)&application_id[0], PASSWORD_TOKEN_SIZE + SHA512_DIGEST_LENGTH, auth_token_len);
+	if (!secret.size()) {
+		printf("failed to unwrapSyntheticPasswordBlob\n");
+		return Free_Return(retval, weaver_key, &pwd);
+	}
+	if (!e4crypt_unlock_user_key(user_id, 0, token.c_str(), secret.c_str())) {
+		printf("e4crypt_unlock_user_key returned fail\n");
+		return Free_Return(retval, weaver_key, &pwd);
+	}
+/*#ifdef USE_KEYSTORAGE_4
+	if (!e4crypt_prepare_user_storage("", user_id, 0, flags)) {
+#else
+	if (!e4crypt_prepare_user_storage(nullptr, user_id, 0, flags)) {
+#endif
+		printf("failed to e4crypt_prepare_user_storage\n");
+		return Free_Return(retval, weaver_key, &pwd);
+	}*/
+	printf("User %i Decrypted Successfully!\n", user_id);
+	retval = true;
+	return Free_Return(retval, weaver_key, &pwd);
+}
+#endif //HAVE_SYNTH_PWD_SUPPORT
+
+int Get_Password_Type(const userid_t user_id, std::string& filename) {
+	struct stat st;
+	char spblob_path_char[PATH_MAX];
+	sprintf(spblob_path_char, "/data/system_de/%d/spblob/", user_id);
+	if (stat(spblob_path_char, &st) == 0) {
+#ifdef HAVE_SYNTH_PWD_SUPPORT
+		printf("Using synthetic password method\n");
+		std::string spblob_path = spblob_path_char;
+		std::string handle_str;
+		if (!Find_Handle(spblob_path, handle_str)) {
+			printf("Error getting handle\n");
+			return 0;
+		}
+		printf("Handle is '%s'\n", handle_str.c_str());
+		password_data_struct pwd;
+		if (!Get_Password_Data(spblob_path, handle_str, &pwd)) {
+			printf("Failed to Get_Password_Data\n");
+			return 0;
+		}
+		if (pwd.password_type == 1) // In Android this means pattern
+			return 2; // In TWRP this means pattern
+		else if (pwd.password_type == 2) // In Android this means PIN or password
+			return 1; // In TWRP this means PIN or password
+		return 0; // We'll try the default password
+#else
+		printf("Synthetic password support not present in TWRP\n");
+		return -1;
+#endif
+	}
+	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";
+	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_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;
+	if (Default_Password) {
+		if (!e4crypt_unlock_user_key(user_id, 0, "!", "!")) {
+			printf("e4crypt_unlock_user_key returned fail\n");
+			return false;
+		}
+/*#ifdef USE_KEYSTORAGE_4
+		if (!e4crypt_prepare_user_storage("", user_id, 0, flags)) {
+#else
+		if (!e4crypt_prepare_user_storage(nullptr, user_id, 0, flags)) {
+#endif
+			printf("failed to e4crypt_prepare_user_storage\n");
+			return false;
+		}*/
+		printf("User %i Decrypted Successfully!\n", user_id);
+		return true;
+	}
+	if (stat("/data/system_de/0/spblob", &st) == 0) {
+#ifdef HAVE_SYNTH_PWD_SUPPORT
+		printf("Using synthetic password method\n");
+		return Decrypt_User_Synth_Pass(user_id, Password);
+#else
+		printf("Synthetic password support not present in TWRP\n");
+		return false;
+#endif
+	}
+	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;
+#ifdef HAVE_GATEKEEPER1
+	bool request_reenroll = false;
+	android::sp<android::hardware::gatekeeper::V1_0::IGatekeeper> gk_device;
+	gk_device = ::android::hardware::gatekeeper::V1_0::IGatekeeper::getService();
+	if (gk_device == nullptr)
+		return false;
+	android::hardware::hidl_vec<uint8_t> curPwdHandle;
+	curPwdHandle.setToExternal(const_cast<uint8_t *>((const uint8_t *)handle.c_str()), st.st_size);
+	android::hardware::hidl_vec<uint8_t> enteredPwd;
+	enteredPwd.setToExternal(const_cast<uint8_t *>((const uint8_t *)Password.c_str()), Password.size());
+
+	android::hardware::Return<void> hwRet =
+		gk_device->verify(user_id, 0 /* challange */,
+						  curPwdHandle,
+						  enteredPwd,
+						  [&ret, &request_reenroll, &auth_token, &auth_token_len]
+							(const android::hardware::gatekeeper::V1_0::GatekeeperResponse &rsp) {
+								ret = static_cast<int>(rsp.code); // propagate errors
+								if (rsp.code >= android::hardware::gatekeeper::V1_0::GatekeeperStatusCode::STATUS_OK) {
+									auth_token = new uint8_t[rsp.data.size()];
+									auth_token_len = rsp.data.size();
+									memcpy(auth_token, rsp.data.data(), auth_token_len);
+									request_reenroll = (rsp.code == android::hardware::gatekeeper::V1_0::GatekeeperStatusCode::STATUS_REENROLL);
+									ret = 0; // all success states are reported as 0
+								} else if (rsp.code == android::hardware::gatekeeper::V1_0::GatekeeperStatusCode::ERROR_RETRY_TIMEOUT && rsp.timeout > 0) {
+									ret = rsp.timeout;
+								}
+							}
+						 );
+	if (!hwRet.isOk()) {
+		return false;
+	}
+#else
+	gatekeeper_device_t *gk_device;
+	ret = gatekeeper_device_initialize(&gk_device);
+    if (ret!=0)
+		return false;
+    ret = gk_device->verify(gk_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;
+	}
+#endif
+	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;
+	}
+/*#ifdef USE_KEYSTORAGE_4
+		if (!e4crypt_prepare_user_storage("", user_id, 0, flags)) {
+#else
+		if (!e4crypt_prepare_user_storage(nullptr, user_id, 0, flags)) {
+#endif
+		printf("failed to e4crypt_prepare_user_storage\n");
+		return false;
+	}*/
+	printf("User %i Decrypted Successfully!\n", user_id);
+	return true;
+}
diff --git a/crypto/ext4crypt/Decrypt.h b/crypto/ext4crypt/Decrypt.h
new file mode 100644
index 0000000..56318e2
--- /dev/null
+++ b/crypto/ext4crypt/Decrypt.h
@@ -0,0 +1,38 @@
+/*
+ * 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;
+// For 9.0 Ext4CryptPie.cpp
+static constexpr int STORAGE_FLAG_DE = 1 << 0;
+static constexpr int STORAGE_FLAG_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..9360b1a
--- /dev/null
+++ b/crypto/ext4crypt/Ext4Crypt.cpp
@@ -0,0 +1,450 @@
+/*
+ * 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"
+
+#ifdef USE_KEYSTORAGE_3
+#include "KeyStorage3.h"
+#else
+#include "KeyStorage.h"
+#endif
+#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>
+
+#ifdef HAVE_SYNTH_PWD_SUPPORT
+#include <ext4_utils/ext4_crypt.h>
+#else
+#include "ext4_crypt.h"
+#endif
+#ifndef HAVE_LIBKEYUTILS
+#include "key_control.h"
+#else
+#include <keyutils.h>
+#endif
+
+#include <hardware/gatekeeper.h>
+#include "HashPassword.h"
+
+#define EMULATED_USES_SELINUX 0
+#define MANAGE_MISC_DIRS 0
+
+#include <cutils/fs.h>
+#include <cutils/properties.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;
+
+// Store main DE raw ref / policy
+std::string de_raw_ref;
+// 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;
+
+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;
+
+// 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;
+}
+
+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 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;
+
+            std::string user_prop = "twrp.user." + std::to_string(user_id) + ".decrypt";
+            property_set(user_prop.c_str(), "0");
+        }
+    }
+    // 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;
+    de_raw_ref = device_key_ref;
+    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;
+    }
+
+    // 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 __unused, 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");
+    }
+    return true;
+}
diff --git a/crypto/ext4crypt/Ext4Crypt.h b/crypto/ext4crypt/Ext4Crypt.h
new file mode 100644
index 0000000..beb5d8f
--- /dev/null
+++ b/crypto/ext4crypt/Ext4Crypt.h
@@ -0,0 +1,47 @@
+/*
+ * 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 <map>
+#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);
+
+bool lookup_key_ref(const std::map<userid_t, std::string>& key_map, userid_t user_id,
+                           std::string* raw_ref);
+
+__END_DECLS
diff --git a/crypto/ext4crypt/Ext4CryptPie.cpp b/crypto/ext4crypt/Ext4CryptPie.cpp
new file mode 100644
index 0000000..48b4ec9
--- /dev/null
+++ b/crypto/ext4crypt/Ext4CryptPie.cpp
@@ -0,0 +1,477 @@
+/*
+ * 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 "Ext4CryptPie.h"
+
+#include "Keymaster4.h"
+#include "KeyStorage4.h"
+#include "KeyUtil.h"
+#include "Utils.h"
+#include "Decrypt.h"
+//#include "VoldUtil.h"
+
+#include <algorithm>
+#include <map>
+#include <set>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <selinux/android.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <private/android_filesystem_config.h>
+
+//#include "android/os/IVold.h"
+
+//#include "cryptfs.h"
+
+#define EMULATED_USES_SELINUX 0
+#define MANAGE_MISC_DIRS 0
+
+#include <cutils/fs.h>
+#include <cutils/properties.h>
+
+#include <ext4_utils/ext4_crypt.h>
+#include <keyutils.h>
+
+#include <android-base/file.h>
+//#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+
+#include <iostream>
+#define LOG(x) std::cout
+#define PLOG(x) std::cout
+#define DATA_MNT_POINT "/data"
+
+using android::base::StringPrintf;
+using android::base::WriteStringToFile;
+using android::vold::kEmptyAuthentication;
+using android::vold::KeyBuffer;
+using android::vold::Keymaster;
+using android::hardware::keymaster::V4_0::KeyFormat;
+
+// Store main DE raw ref / policy
+std::string de_raw_ref;
+// 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, per b/26948053
+std::map<userid_t, KeyBuffer> s_ce_keys;
+
+namespace {
+
+struct PolicyKeyRef {
+    std::string contents_mode;
+    std::string filenames_mode;
+    std::string key_raw_ref;
+};
+
+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";
+const std::string prepare_subdirs_path = "/system/bin/vold_prepare_subdirs";
+
+const std::string systemwide_volume_key_dir =
+    std::string() + DATA_MNT_POINT + "/misc/vold/volume_keys";
+
+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;
+
+}
+
+static bool e4crypt_is_emulated() {
+    return property_get_bool("persist.sys.emulate_fbe", false);
+}
+
+/*static const char* escape_empty(const std::string& value) {
+    return value.empty() ? "null" : value.c_str();
+}*/
+
+static std::string get_de_key_path(userid_t user_id) {
+    return StringPrintf("%s/de/%d", user_key_dir.c_str(), user_id);
+}
+
+static std::string get_ce_key_directory_path(userid_t user_id) {
+    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 << std::endl;
+        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 << std::endl;
+                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 << std::endl;
+            continue;
+        }
+        result.emplace_back(directory_path + "/" + entry->d_name);
+    }
+    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) {
+    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 << std::endl;
+        if (rename(to_fix.c_str(), current_path.c_str()) != 0) {
+            PLOG(WARNING) << "Unable to rename " << to_fix << " to " << current_path << std::endl;
+        }
+    }
+}
+
+static bool read_and_fixate_user_ce_key(userid_t user_id,
+                                        const android::vold::KeyAuthentication& auth,
+                                        KeyBuffer *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 << std::endl;
+        if (android::vold::retrieveKey(ce_key_path, auth, ce_key)) {
+            LOG(DEBUG) << "Successfully retrieved key" << std::endl;
+            fixate_user_ce_key(directory_path, ce_key_path, paths);
+            return true;
+        }
+    }
+    LOG(ERROR) << "Failed to find working ce key for user " << user_id << std::endl;
+    return false;
+}
+
+static bool is_wrapped_key_supported_common(const std::string& mount_point) {
+    LOG(DEBUG) << "Determining wrapped-key support for " << mount_point << std::endl;
+    std::string wrapped_key_supported = android::base::GetProperty("fbe.data.wrappedkey", "false");
+    LOG(DEBUG) << "fbe.data.wrappedkey = " << wrapped_key_supported << std::endl;
+    if (mount_point == DATA_MNT_POINT && wrapped_key_supported == "true") {
+        LOG(DEBUG) << "Wrapped key supported on " << mount_point << std::endl;
+        return true;
+    } else {
+        return false;
+    }
+}
+
+bool is_wrapped_key_supported() {
+    return is_wrapped_key_supported_common(DATA_MNT_POINT);
+}
+
+bool is_wrapped_key_supported_external() {
+    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;
+    KeyBuffer ce_key;
+    if (!read_and_fixate_user_ce_key(user_id, auth, &ce_key)) return false;
+    std::string ce_raw_ref;
+
+    if (is_wrapped_key_supported()) {
+        KeyBuffer ephemeral_wrapped_key;
+        if (!getEphemeralWrappedKey(KeyFormat::RAW, ce_key, &ephemeral_wrapped_key)) {
+           LOG(ERROR) << "Failed to export ce key";
+           return false;
+        }
+
+        ce_key = std::move(ephemeral_wrapped_key);
+    }
+    if (!android::vold::installKey(ce_key, &ce_raw_ref)) return false;
+    s_ce_keys[user_id] = std::move(ce_key);
+    s_ce_key_raw_refs[user_id] = ce_raw_ref;
+    LOG(DEBUG) << "Installed ce key for user " << user_id << std::endl;
+    return true;
+}
+
+static bool prepare_dir(const std::string& dir, mode_t mode, uid_t uid, gid_t gid) {
+    LOG(DEBUG) << "Preparing: " << dir << std::endl;
+    if (fs_prepare_dir(dir.c_str(), mode, uid, gid) != 0) {
+        PLOG(ERROR) << "Failed to prepare " << dir << std::endl;
+        return false;
+    }
+    return true;
+}
+
+// NB this assumes that there is only one thread listening for crypt commands, because
+// it creates keys in a fixed location.
+static bool create_and_install_user_keys(userid_t user_id, bool create_ephemeral) {
+    /*KeyBuffer de_key, ce_key;
+    if (!android::vold::randomKey(&de_key)) return false;
+    if (!android::vold::randomKey(&ce_key)) return false;
+    if (create_ephemeral) {
+        // If the key should be created as ephemeral, don't store it.
+        s_ephemeral_users.insert(user_id);
+    } else {
+        auto const directory_path = get_ce_key_directory_path(user_id);
+        if (!prepare_dir(directory_path, 0700, AID_ROOT, AID_ROOT)) return false;
+        auto const paths = get_ce_key_paths(directory_path);
+        std::string ce_key_path;
+        if (!get_ce_key_new_path(directory_path, paths, &ce_key_path)) return false;
+        if (!android::vold::storeKeyAtomically(ce_key_path, user_key_temp,
+                kEmptyAuthentication, ce_key)) return false;
+        fixate_user_ce_key(directory_path, ce_key_path, paths);
+        // Write DE key second; once this is written, all is good.
+        if (!android::vold::storeKeyAtomically(get_de_key_path(user_id), user_key_temp,
+                kEmptyAuthentication, de_key)) return false;
+    }
+    std::string de_raw_ref;
+    if (!android::vold::installKey(de_key, &de_raw_ref)) return false;
+    s_de_key_raw_refs[user_id] = de_raw_ref;
+    std::string ce_raw_ref;
+    if (!android::vold::installKey(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) << "Created keys for user " << user_id;*/
+    LOG(DEBUG) << "TWRP not doing create_and_install_user_keys\n";
+    return true;
+}
+
+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 << std::endl;
+        return false;
+    }
+    *raw_ref = refi->second;
+    return true;
+}
+
+static void get_data_file_encryption_modes(PolicyKeyRef* key_ref) {
+    /*struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab_default, DATA_MNT_POINT);
+    char const* contents_mode = strdup("ice");
+    char const* filenames_mode = strdup("aes-256-heh");
+    fs_mgr_get_file_encryption_modes(rec, &contents_mode, &filenames_mode);
+    key_ref->contents_mode = contents_mode;
+    key_ref->filenames_mode = filenames_mode;*/
+    LOG(INFO) << "contents mode '" << android::base::GetProperty("fbe.contents", "aes-256-xts") << "' filenames '" << android::base::GetProperty("fbe.filenames", "aes-256-heh") << "'\n";
+    key_ref->contents_mode =
+        android::base::GetProperty("fbe.contents", "aes-256-xts");
+    key_ref->filenames_mode =
+        android::base::GetProperty("fbe.filenames", "aes-256-heh");
+}
+
+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" << std::endl;
+        return false;
+    }
+    for (;;) {
+        errno = 0;
+        auto entry = readdir(dirp.get());
+        if (!entry) {
+            if (errno) {
+                PLOG(ERROR) << "Unable to read de key directory" << std::endl;
+                return false;
+            }
+            break;
+        }
+        if (entry->d_type != DT_DIR || !is_numeric(entry->d_name)) {
+            LOG(DEBUG) << "Skipping non-de-key " << entry->d_name << std::endl;
+            continue;
+        }
+        userid_t user_id = std::stoi(entry->d_name);
+        if (s_de_key_raw_refs.count(user_id) == 0) {
+            auto key_path = de_dir + "/" + entry->d_name;
+            KeyBuffer key;
+            if (!android::vold::retrieveKey(key_path, kEmptyAuthentication, &key)) return false;
+            std::string raw_ref;
+            if (is_wrapped_key_supported()) {
+                KeyBuffer ephemeral_wrapped_key;
+                if (!getEphemeralWrappedKey(KeyFormat::RAW, key, &ephemeral_wrapped_key)) {
+                   LOG(ERROR) << "Failed to export de_key in create_and_install_user_keys";
+                   return false;
+                }
+                key = std::move(ephemeral_wrapped_key);
+            }
+            if (!android::vold::installKey(key, &raw_ref)) return false;
+            s_de_key_raw_refs[user_id] = raw_ref;
+            LOG(DEBUG) << "Installed de key for user " << user_id << std::endl;
+
+            std::string user_prop = "twrp.user." + std::to_string(user_id) + ".decrypt";
+            property_set(user_prop.c_str(), "0");
+        }
+    }
+    // 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() {
+    LOG(INFO) << "e4crypt_initialize_global_de" << std::endl;
+    bool wrapped_key_supported = false;
+
+    if (s_global_de_initialized) {
+        LOG(INFO) << "Already initialized" << std::endl;
+        return true;
+    }
+
+    PolicyKeyRef device_ref;
+    wrapped_key_supported = is_wrapped_key_supported();
+    LOG(INFO) << "calling retrieveAndInstallKey\n";
+    if (!android::vold::retrieveAndInstallKey(true, kEmptyAuthentication, device_key_path,
+                                              device_key_temp, &device_ref.key_raw_ref, wrapped_key_supported))
+        return false;
+    get_data_file_encryption_modes(&device_ref);
+
+    std::string modestring = device_ref.contents_mode + ":" + device_ref.filenames_mode;
+    std::string mode_filename = std::string("/data") + e4crypt_key_mode;
+    if (!android::base::WriteStringToFile(modestring, mode_filename)) {
+        PLOG(ERROR) << "Cannot save type" << std::endl;
+        return false;
+    }
+
+    std::string ref_filename = std::string("/data") + e4crypt_key_ref;
+    if (!android::base::WriteStringToFile(device_ref.key_raw_ref, ref_filename)) {
+        PLOG(ERROR) << "Cannot save key reference to:" << ref_filename << std::endl;
+        return false;
+    }
+    LOG(INFO) << "Wrote system DE key reference to:" << ref_filename << std::endl;
+
+    s_global_de_initialized = true;
+    de_raw_ref = device_ref.key_raw_ref;
+    return true;
+}
+
+bool e4crypt_init_user0() {
+    LOG(DEBUG) << "e4crypt_init_user0\n";
+    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 (!android::vold::pathExists(get_de_key_path(0))) {
+            if (!create_and_install_user_keys(0, false)) 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;
+    }
+
+    // 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 emulated_unlock(const std::string& path, mode_t mode) {
+    if (chmod(path.c_str(), mode) != 0) {
+        PLOG(ERROR) << "Failed to chmod " << path << std::endl;
+        // FIXME temporary workaround for b/26713622
+        if (e4crypt_is_emulated()) return false;
+    }
+#if EMULATED_USES_SELINUX
+    if (selinux_android_restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_FORCE) != 0) {
+        PLOG(WARNING) << "Failed to restorecon " << path << std::endl;
+        // FIXME temporary workaround for b/26713622
+        if (e4crypt_is_emulated()) return false;
+    }
+#endif
+    return true;
+}
+
+static bool parse_hex(const std::string& hex, std::string* result) {
+    if (hex == "!") {
+        *result = "";
+        return true;
+    }
+    if (android::vold::HexToStr(hex, *result) != 0) {
+        LOG(ERROR) << "Invalid FBE hex string" << std::endl;  // 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 std::string& token_hex,
+                             const std::string& secret_hex) {
+    LOG(DEBUG) << "e4crypt_unlock_user_key " << user_id << " serial=" << serial
+               << " token_present=" << (token_hex != "!") << std::endl;
+    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 << std::endl;
+            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 << std::endl;
+            return false;
+        }
+    } else {
+        // 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("", user_id), 0770) ||
+            !emulated_unlock(android::vold::BuildDataUserCePath("", user_id), 0771)) {
+            LOG(ERROR) << "Failed to unlock user " << user_id << std::endl;
+            return false;
+        }
+    }
+    return true;
+}
diff --git a/crypto/ext4crypt/Ext4CryptPie.h b/crypto/ext4crypt/Ext4CryptPie.h
new file mode 100644
index 0000000..7236bc0
--- /dev/null
+++ b/crypto/ext4crypt/Ext4CryptPie.h
@@ -0,0 +1,45 @@
+/*
+ * 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 <map>
+#include <string>
+
+#include <cutils/multiuser.h>
+
+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 std::string& token,
+                               const std::string& secret);
+bool e4crypt_fixate_newest_user_key_auth(userid_t user_id);*/
+
+bool e4crypt_unlock_user_key(userid_t user_id, int serial, const std::string& token,
+                             const std::string& secret);
+//bool e4crypt_lock_user_key(userid_t user_id);
+
+/*bool e4crypt_prepare_user_storage(const std::string& volume_uuid, userid_t user_id, int serial,
+                                  int flags);*/
+/*bool e4crypt_destroy_user_storage(const std::string& volume_uuid, userid_t user_id, int flags);
+
+bool e4crypt_destroy_volume_keys(const std::string& volume_uuid);*/
+
+bool is_wrapped_key_supported();
+bool is_wrapped_key_supported_external();
+
+bool lookup_key_ref(const std::map<userid_t, std::string>& key_map, userid_t user_id,
+                           std::string* raw_ref);
diff --git a/crypto/ext4crypt/HashPassword.cpp b/crypto/ext4crypt/HashPassword.cpp
new file mode 100644
index 0000000..07ecb1f
--- /dev/null
+++ b/crypto/ext4crypt/HashPassword.cpp
@@ -0,0 +1,117 @@
+/*
+ * 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>
+#include <openssl/hmac.h>
+
+#include "HashPassword.h"
+
+#define PASS_PADDING_SIZE 128
+#define SHA512_HEX_SIZE SHA512_DIGEST_LENGTH * 2
+#define SHA256_HEX_SIZE SHA256_DIGEST_LENGTH * 2
+
+void* PersonalizedHashBinary(const char* prefix, const char* key, const size_t key_size) {
+	size_t size = PASS_PADDING_SIZE + key_size;
+	unsigned char* buffer = (unsigned char*)calloc(1, size);
+	if (!buffer) return NULL; // failed to malloc
+	memcpy((void*)buffer, (void*)prefix, strlen(prefix));
+	unsigned char* ptr = buffer + PASS_PADDING_SIZE;
+	memcpy((void*)ptr, key, key_size);
+	unsigned char hash[SHA512_DIGEST_LENGTH];
+	SHA512_CTX sha512;
+	SHA512_Init(&sha512);
+	SHA512_Update(&sha512, buffer, size);
+	SHA512_Final(hash, &sha512);
+	free(buffer);
+	void* ret = malloc(SHA512_DIGEST_LENGTH);
+	if (!ret) return NULL; // failed to malloc
+	memcpy(ret, (void*)&hash[0], SHA512_DIGEST_LENGTH);
+	return ret;
+}
+
+std::string PersonalizedHash(const char* prefix, const char* key, const size_t key_size) {
+	size_t size = PASS_PADDING_SIZE + key_size;
+	unsigned char* buffer = (unsigned char*)calloc(1, size);
+	if (!buffer) return ""; // failed to malloc
+	memcpy((void*)buffer, (void*)prefix, strlen(prefix));
+	unsigned char* ptr = buffer + PASS_PADDING_SIZE;
+	memcpy((void*)ptr, key, key_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;
+	free(buffer);
+	return ret;
+}
+
+std::string PersonalizedHash(const char* prefix, const std::string& Password) {
+	return PersonalizedHash(prefix, Password.c_str(), Password.size());
+}
+
+std::string PersonalizedHashSP800(const char* label, const char* context, const char* key, const size_t key_size) {
+	HMAC_CTX ctx;
+	HMAC_CTX_init(&ctx);
+	HMAC_Init_ex(&ctx, key, key_size, EVP_sha256(), NULL);
+	unsigned int counter = 1;
+	endianswap(&counter);
+	HMAC_Update(&ctx, (const unsigned char*)&counter, 4);
+	HMAC_Update(&ctx, (const unsigned char*)label, strlen(label));
+	const unsigned char divider = 0;
+	HMAC_Update(&ctx, &divider, 1);
+	HMAC_Update(&ctx, (const unsigned char*)context, strlen(context));
+	unsigned int contextDisambiguation = strlen(context) * 8;
+	endianswap(&contextDisambiguation);
+	HMAC_Update(&ctx, (const unsigned char*)&contextDisambiguation, 4);
+	unsigned int finalValue = 256;
+	endianswap(&finalValue);
+	HMAC_Update(&ctx, (const unsigned char*)&finalValue, 4);
+
+	unsigned char output[SHA256_DIGEST_LENGTH];
+	unsigned int out_size = 0;
+	HMAC_Final(&ctx, output, &out_size);
+
+	int index = 0;
+	char hex_hash[SHA256_HEX_SIZE + 1];
+	for(index = 0; index < SHA256_DIGEST_LENGTH; index++)
+		sprintf(hex_hash + (index * 2), "%02x", output[index]);
+	hex_hash[SHA256_HEX_SIZE] = 0;
+	std::string ret = hex_hash;
+	return ret;
+}
+
+std::string HashPassword(const std::string& Password) {
+	const char* prefix = FBE_PERSONALIZATION;
+	return PersonalizedHash(prefix, Password);
+}
diff --git a/crypto/ext4crypt/HashPassword.h b/crypto/ext4crypt/HashPassword.h
new file mode 100644
index 0000000..73880b1
--- /dev/null
+++ b/crypto/ext4crypt/HashPassword.h
@@ -0,0 +1,44 @@
+/*
+ * 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>
+
+#define FBE_PERSONALIZATION "Android FBE credential hash"
+#define PERSONALISATION_WEAVER_KEY "weaver-key"
+#define PERSONALISATION_WEAVER_PASSWORD "weaver-pwd"
+#define PERSONALISATION_APPLICATION_ID "application-id"
+#define PERSONALIZATION_FBE_KEY "fbe-key"
+#define PERSONALIZATION_USER_GK_AUTH "user-gk-authentication"
+#define PERSONALISATION_SECDISCARDABLE "secdiscardable-transform"
+#define PERSONALISATION_CONTEXT "android-synthetic-password-personalization-context"
+
+void* PersonalizedHashBinary(const char* prefix, const char* key, const size_t key_size);
+
+std::string PersonalizedHash(const char* prefix, const char* key, const size_t key_size);
+std::string PersonalizedHash(const char* prefix, const std::string& Password);
+std::string PersonalizedHashSP800(const char* label, const char* context, const char* key, const size_t key_size);
+std::string HashPassword(const std::string& Password);
+
+template <class T>
+void endianswap(T *objp) {
+	unsigned char *memp = reinterpret_cast<unsigned char*>(objp);
+	std::reverse(memp, memp + sizeof(T));
+}
+
+#endif
diff --git a/crypto/ext4crypt/KeyBuffer.cpp b/crypto/ext4crypt/KeyBuffer.cpp
new file mode 100644
index 0000000..e7aede5
--- /dev/null
+++ b/crypto/ext4crypt/KeyBuffer.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 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 "KeyBuffer.h"
+
+#include <algorithm>
+#include <cstring>
+
+namespace android {
+namespace vold {
+
+KeyBuffer operator+(KeyBuffer&& lhs, const KeyBuffer& rhs) {
+    std::copy(rhs.begin(), rhs.end(), std::back_inserter(lhs));
+    return std::move(lhs);
+}
+
+KeyBuffer operator+(KeyBuffer&& lhs, const char* rhs) {
+    std::copy(rhs, rhs + strlen(rhs), std::back_inserter(lhs));
+    return std::move(lhs);
+}
+
+}  // namespace vold
+}  // namespace android
+
diff --git a/crypto/ext4crypt/KeyBuffer.h b/crypto/ext4crypt/KeyBuffer.h
new file mode 100644
index 0000000..2087187
--- /dev/null
+++ b/crypto/ext4crypt/KeyBuffer.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 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_KEYBUFFER_H
+#define ANDROID_VOLD_KEYBUFFER_H
+
+#include <cstring>
+#include <memory>
+#include <vector>
+
+namespace android {
+namespace vold {
+
+/**
+ * Variant of memset() that should never be optimized away. Borrowed from keymaster code.
+ */
+#ifdef __clang__
+#define OPTNONE __attribute__((optnone))
+#else  // not __clang__
+#define OPTNONE __attribute__((optimize("O0")))
+#endif  // not __clang__
+inline OPTNONE void* memset_s(void* s, int c, size_t n) {
+    if (!s)
+        return s;
+    return memset(s, c, n);
+}
+#undef OPTNONE
+
+// Allocator that delegates useful work to standard one but zeroes data before deallocating.
+class ZeroingAllocator : public std::allocator<char> {
+    public:
+    void deallocate(pointer p, size_type n)
+    {
+        memset_s(p, 0, n);
+        std::allocator<char>::deallocate(p, n);
+    }
+};
+
+// Char vector that zeroes memory when deallocating.
+using KeyBuffer = std::vector<char, ZeroingAllocator>;
+
+// Convenience methods to concatenate key buffers.
+KeyBuffer operator+(KeyBuffer&& lhs, const KeyBuffer& rhs);
+KeyBuffer operator+(KeyBuffer&& lhs, const char* rhs);
+
+}  // namespace vold
+}  // namespace android
+
+#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/KeyStorage3.cpp b/crypto/ext4crypt/KeyStorage3.cpp
new file mode 100644
index 0000000..a07212d
--- /dev/null
+++ b/crypto/ext4crypt/KeyStorage3.cpp
@@ -0,0 +1,526 @@
+/*
+ * 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 "KeyStorage3.h"
+
+#include "Keymaster3.h"
+#include "ScryptParameters.h"
+#include "Utils.h"
+
+#include <vector>
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#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 <keystore/authorization_set.h>
+#include <keystore/keystore_hidl_support.h>
+
+extern "C" {
+
+#include "crypto_scrypt.h"
+}
+
+#include <iostream>
+#define ERROR 1
+#define LOG(x) std::cout
+#define PLOG(x) std::cout
+
+namespace android {
+namespace vold {
+using namespace keystore;
+
+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* kHashPrefix_secdiscardable = "Android secdiscardable SHA512";
+static const char* kHashPrefix_keygen = "Android key wrapping key generation SHA512";
+static const char* kFn_encrypted_key = "encrypted_key";
+static const char* kFn_keymaster_key_blob = "keymaster_key_blob";
+static const char* kFn_keymaster_key_blob_upgraded = "keymaster_key_blob_upgraded";
+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 hashWithPrefix(char const* prefix, const std::string& tohash) {
+    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 hashingPrefix = prefix;
+    hashingPrefix.resize(SHA512_CBLOCK);
+    SHA512_Update(&c, hashingPrefix.data(), hashingPrefix.size());
+    SHA512_Update(&c, tohash.data(), tohash.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 = AuthorizationSetBuilder()
+                            .AesEncryptionKey(AES_KEY_BYTES * 8)
+                            .Authorization(TAG_BLOCK_MODE, BlockMode::GCM)
+                            .Authorization(TAG_MIN_MAC_LENGTH, GCM_MAC_BYTES * 8)
+                            .Authorization(TAG_PADDING, PaddingMode::NONE)
+                            .Authorization(TAG_APPLICATION_ID, blob2hidlVec(appId));
+    if (auth.token.empty()) {
+        LOG(DEBUG) << "Creating key that doesn't need auth token";
+        paramBuilder.Authorization(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(TAG_USER_SECURE_ID, at->user_id);
+        paramBuilder.Authorization(TAG_USER_AUTH_TYPE, HardwareAuthenticatorType::PASSWORD);
+        paramBuilder.Authorization(TAG_AUTH_TIMEOUT, AUTH_TIMEOUT);
+    }
+    return keymaster.generateKey(paramBuilder, key);
+}*/
+
+static AuthorizationSet beginParams(const KeyAuthentication& auth,
+                                               const std::string& appId) {
+    auto paramBuilder = AuthorizationSetBuilder()
+                            .Authorization(TAG_BLOCK_MODE, BlockMode::GCM)
+                            .Authorization(TAG_MAC_LENGTH, GCM_MAC_BYTES * 8)
+                            .Authorization(TAG_PADDING, PaddingMode::NONE)
+                            .Authorization(TAG_APPLICATION_ID, blob2hidlVec(appId));
+    if (!auth.token.empty()) {
+        LOG(DEBUG) << "Supplying auth token to Keymaster";
+        paramBuilder.Authorization(TAG_AUTH_TOKEN, blob2hidlVec(auth.token));
+    }
+    return paramBuilder;
+}
+
+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 KeymasterOperation begin(Keymaster& keymaster, const std::string& dir,
+                                KeyPurpose purpose,
+                                const AuthorizationSet &keyParams,
+                                const AuthorizationSet &opParams,
+                                AuthorizationSet* outParams) {
+    auto kmKeyPath = dir + "/" + kFn_keymaster_key_blob;
+    std::string kmKey;
+    if (!readFileToString(kmKeyPath, &kmKey)) return KeymasterOperation();
+    AuthorizationSet inParams(keyParams);
+    inParams.append(opParams.begin(), opParams.end());
+    for (;;) {
+        auto opHandle = keymaster.begin(purpose, kmKey, inParams, outParams);
+        if (opHandle) {
+            return opHandle;
+        }
+        if (opHandle.errorCode() != ErrorCode::KEY_REQUIRES_UPGRADE) return opHandle;
+        LOG(DEBUG) << "Upgrading key: " << dir;
+        std::string newKey;
+        if (!keymaster.upgradeKey(kmKey, keyParams, &newKey)) return KeymasterOperation();
+        // Upgrade the key in memory but do not replace the key in storage
+        /*auto newKeyPath = dir + "/" + kFn_keymaster_key_blob_upgraded;
+        if (!writeStringToFile(newKey, newKeyPath)) return KeymasterOperation();
+        if (rename(newKeyPath.c_str(), kmKeyPath.c_str()) != 0) {
+            PLOG(ERROR) << "Unable to move upgraded key to location: " << kmKeyPath;
+            return KeymasterOperation();
+        }
+        if (!keymaster.deleteKey(kmKey)) {
+            LOG(ERROR) << "Key deletion failed during upgrade, continuing anyway: " << dir;
+        }*/
+        kmKey = newKey;
+        LOG(INFO) << "Key upgraded: " << dir;
+    }
+}
+
+/*static bool encryptWithKeymasterKey(Keymaster& keymaster, const std::string& dir,
+                                    const AuthorizationSet &keyParams,
+                                    const std::string& message, std::string* ciphertext) {
+    AuthorizationSet opParams;
+    AuthorizationSet outParams;
+    auto opHandle = begin(keymaster, dir, KeyPurpose::ENCRYPT, keyParams, opParams, &outParams);
+    if (!opHandle) return false;
+    auto nonceBlob = outParams.GetTagValue(TAG_NONCE);
+    if (!nonceBlob.isOk()) {
+        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.value()[0]), nonceBlob.value().size());
+    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.finish(&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& dir,
+                                    const AuthorizationSet &keyParams,
+                                    const std::string& ciphertext, std::string* message) {
+    auto nonce = ciphertext.substr(0, GCM_NONCE_BYTES);
+    auto bodyAndMac = ciphertext.substr(GCM_NONCE_BYTES);
+    auto opParams = AuthorizationSetBuilder()
+            .Authorization(TAG_NONCE, blob2hidlVec(nonce));
+    auto opHandle = begin(keymaster, dir, KeyPurpose::DECRYPT, keyParams, opParams, nullptr);
+    if (!opHandle) return false;
+    if (!opHandle.updateCompletely(bodyAndMac, message)) return false;
+    if (!opHandle.finish(nullptr)) return false;
+    return true;
+}
+
+static std::string getStretching(const KeyAuthentication& auth) {
+    if (!auth.usesKeymaster()) {
+        return kStretch_none;
+    } else if (auth.secret.empty()) {
+        return kStretch_nopassword;
+    } else {
+        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 = hashWithPrefix(kHashPrefix_secdiscardable, secdiscardable) + stretched;
+    return true;
+}
+
+static bool readRandomBytesOrLog(size_t count, std::string* out) {
+    auto status = ReadRandomBytes(count, *out);
+    if (status != OK) {
+        LOG(ERROR) << "Random read failed with status: " << status;
+        return false;
+    }
+    return true;
+}
+
+static void logOpensslError() {
+    LOG(ERROR) << "Openssl error: " << ERR_get_error();
+}
+
+static bool encryptWithoutKeymaster(const std::string& preKey,
+                                    const std::string& plaintext, std::string* ciphertext) {
+    auto key = hashWithPrefix(kHashPrefix_keygen, preKey);
+    key.resize(AES_KEY_BYTES);
+    if (!readRandomBytesOrLog(GCM_NONCE_BYTES, ciphertext)) return false;
+    auto ctx = std::unique_ptr<EVP_CIPHER_CTX, decltype(&::EVP_CIPHER_CTX_free)>(
+        EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free);
+    if (!ctx) {
+        logOpensslError();
+        return false;
+    }
+    if (1 != EVP_EncryptInit_ex(ctx.get(), EVP_aes_256_gcm(), NULL,
+            reinterpret_cast<const uint8_t*>(key.data()),
+            reinterpret_cast<const uint8_t*>(ciphertext->data()))) {
+        logOpensslError();
+        return false;
+    }
+    ciphertext->resize(GCM_NONCE_BYTES + plaintext.size() + GCM_MAC_BYTES);
+    int outlen;
+    if (1 != EVP_EncryptUpdate(ctx.get(),
+        reinterpret_cast<uint8_t*>(&(*ciphertext)[0] + GCM_NONCE_BYTES), &outlen,
+        reinterpret_cast<const uint8_t*>(plaintext.data()), plaintext.size())) {
+        logOpensslError();
+        return false;
+    }
+    if (outlen != static_cast<int>(plaintext.size())) {
+        LOG(ERROR) << "GCM ciphertext length should be " << plaintext.size() << " was " << outlen;
+        return false;
+    }
+    if (1 != EVP_EncryptFinal_ex(ctx.get(),
+        reinterpret_cast<uint8_t*>(&(*ciphertext)[0] + GCM_NONCE_BYTES + plaintext.size()), &outlen)) {
+        logOpensslError();
+        return false;
+    }
+    if (outlen != 0) {
+        LOG(ERROR) << "GCM EncryptFinal should be 0, was " << outlen;
+        return false;
+    }
+    if (1 != EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_GET_TAG, GCM_MAC_BYTES,
+        reinterpret_cast<uint8_t*>(&(*ciphertext)[0] + GCM_NONCE_BYTES + plaintext.size()))) {
+        logOpensslError();
+        return false;
+    }
+    return true;
+}
+
+static bool decryptWithoutKeymaster(const std::string& preKey,
+                                    const std::string& ciphertext, std::string* plaintext) {
+    if (ciphertext.size() < GCM_NONCE_BYTES + GCM_MAC_BYTES) {
+        LOG(ERROR) << "GCM ciphertext too small: " << ciphertext.size();
+        return false;
+    }
+    auto key = hashWithPrefix(kHashPrefix_keygen, preKey);
+    key.resize(AES_KEY_BYTES);
+    auto ctx = std::unique_ptr<EVP_CIPHER_CTX, decltype(&::EVP_CIPHER_CTX_free)>(
+        EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free);
+    if (!ctx) {
+        logOpensslError();
+        return false;
+    }
+    if (1 != EVP_DecryptInit_ex(ctx.get(), EVP_aes_256_gcm(), NULL,
+            reinterpret_cast<const uint8_t*>(key.data()),
+            reinterpret_cast<const uint8_t*>(ciphertext.data()))) {
+        logOpensslError();
+        return false;
+    }
+    plaintext->resize(ciphertext.size() - GCM_NONCE_BYTES - GCM_MAC_BYTES);
+    int outlen;
+    if (1 != EVP_DecryptUpdate(ctx.get(),
+        reinterpret_cast<uint8_t*>(&(*plaintext)[0]), &outlen,
+        reinterpret_cast<const uint8_t*>(ciphertext.data() + GCM_NONCE_BYTES), plaintext->size())) {
+        logOpensslError();
+        return false;
+    }
+    if (outlen != static_cast<int>(plaintext->size())) {
+        LOG(ERROR) << "GCM plaintext length should be " << plaintext->size() << " was " << outlen;
+        return false;
+    }
+    if (1 != EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_TAG, GCM_MAC_BYTES,
+        const_cast<void *>(
+            reinterpret_cast<const void*>(ciphertext.data() + GCM_NONCE_BYTES + plaintext->size())))) {
+        logOpensslError();
+        return false;
+    }
+    if (1 != EVP_DecryptFinal_ex(ctx.get(),
+        reinterpret_cast<uint8_t*>(&(*plaintext)[0] + plaintext->size()), &outlen)) {
+        logOpensslError();
+        return false;
+    }
+    if (outlen != 0) {
+        LOG(ERROR) << "GCM EncryptFinal should be 0, was " << outlen;
+        return false;
+    }
+    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 (!readRandomBytesOrLog(SECDISCARDABLE_BYTES, &secdiscardable)) return false;
+    if (!writeStringToFile(secdiscardable, dir + "/" + kFn_secdiscardable)) return false;
+    std::string stretching = getStretching(auth);
+    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;
+    std::string encryptedKey;
+    if (auth.usesKeymaster()) {
+        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;
+        auto keyParams = beginParams(auth, appId);
+        if (!encryptWithKeymasterKey(keymaster, dir, keyParams, key, &encryptedKey)) return false;
+    } else {
+        if (!encryptWithoutKeymaster(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 encryptedMessage;
+    if (!readFileToString(dir + "/" + kFn_encrypted_key, &encryptedMessage)) return false;
+    if (auth.usesKeymaster()) {
+        Keymaster keymaster;
+        if (!keymaster) return false;
+        auto keyParams = beginParams(auth, appId);
+        if (!decryptWithKeymasterKey(keymaster, dir, keyParams, encryptedMessage, key)) return false;
+    } else {
+        if (!decryptWithoutKeymaster(appId, encryptedMessage, key)) return false;
+    }
+    return true;
+}
+
+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 runSecdiscard(const std::string& dir) {
+    if (ForkExecvp(
+            std::vector<std::string>{kSecdiscardPath, "--",
+                dir + "/" + kFn_encrypted_key,
+                dir + "/" + kFn_keymaster_key_blob,
+                dir + "/" + kFn_secdiscardable,
+                }) != 0) {
+        LOG(ERROR) << "secdiscard failed";
+        return false;
+    }
+    return true;
+}
+
+bool runSecdiscardSingle(const std::string& file) {
+    if (ForkExecvp(
+            std::vector<std::string>{kSecdiscardPath, "--",
+                file}) != 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 &= runSecdiscard(dir);
+    success &= recursiveDeleteKey(dir);
+    return success;
+}
+
+}  // namespace vold
+}  // namespace android
diff --git a/crypto/ext4crypt/KeyStorage3.h b/crypto/ext4crypt/KeyStorage3.h
new file mode 100644
index 0000000..bce6a99
--- /dev/null
+++ b/crypto/ext4crypt/KeyStorage3.h
@@ -0,0 +1,58 @@
+/*
+ * 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 "token" and "secret" are nonempty, "secret" is appended to the application-specific
+// binary needed to unlock.
+// If only "secret" is nonempty, it is used to decrypt in a non-Keymaster process.
+class KeyAuthentication {
+  public:
+    KeyAuthentication(std::string t, std::string s) : token{t}, secret{s} {};
+
+    bool usesKeymaster() const { return !token.empty() || secret.empty(); };
+
+    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);
+
+bool runSecdiscardSingle(const std::string& file);
+}  // namespace vold
+}  // namespace android
+
+#endif
diff --git a/crypto/ext4crypt/KeyStorage4.cpp b/crypto/ext4crypt/KeyStorage4.cpp
new file mode 100644
index 0000000..b086791
--- /dev/null
+++ b/crypto/ext4crypt/KeyStorage4.cpp
@@ -0,0 +1,656 @@
+/*
+ * 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 "KeyStorage4.h"
+
+#include "Keymaster4.h"
+#include "ScryptParameters.h"
+#include "Utils.h"
+
+#include <vector>
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/sha.h>
+
+#include <android-base/file.h>
+//#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+
+#include <cutils/properties.h>
+
+#include <hardware/hw_auth_token.h>
+#include <keymasterV4_0/authorization_set.h>
+#include <keymasterV4_0/keymaster_utils.h>
+
+#include <iostream>
+#define ERROR 1
+#define LOG(x) std::cout
+#define PLOG(x) std::cout
+
+extern "C" {
+
+#include "crypto_scrypt.h"
+}
+
+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
+constexpr int EXT4_AES_256_XTS_KEY_SIZE = 64;
+
+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* kHashPrefix_secdiscardable = "Android secdiscardable SHA512";
+static const char* kHashPrefix_keygen = "Android key wrapping key generation SHA512";
+static const char* kFn_encrypted_key = "encrypted_key";
+static const char* kFn_keymaster_key_blob = "keymaster_key_blob";
+static const char* kFn_keymaster_key_blob_upgraded = "keymaster_key_blob_upgraded";
+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 << std::endl;
+        return false;
+    }
+    return true;
+}
+
+static void hashWithPrefix(char const* prefix, const std::string& tohash, std::string* res) {
+    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 hashingPrefix = prefix;
+    hashingPrefix.resize(SHA512_CBLOCK);
+    SHA512_Update(&c, hashingPrefix.data(), hashingPrefix.size());
+    SHA512_Update(&c, tohash.data(), tohash.size());
+    res->assign(SHA512_DIGEST_LENGTH, '\0');
+    SHA512_Final(reinterpret_cast<uint8_t*>(&(*res)[0]), &c);
+}
+
+static bool generateKeymasterKey(Keymaster& keymaster, const KeyAuthentication& auth,
+                                 const std::string& appId, std::string* key) {
+    auto paramBuilder = km::AuthorizationSetBuilder()
+                            .AesEncryptionKey(AES_KEY_BYTES * 8)
+                            .GcmModeMinMacLen(GCM_MAC_BYTES * 8)
+                            .Authorization(km::TAG_APPLICATION_ID, km::support::blob2hidlVec(appId));
+    if (auth.token.empty()) {
+        LOG(DEBUG) << "Creating key that doesn't need auth token" << std::endl;
+        paramBuilder.Authorization(km::TAG_NO_AUTH_REQUIRED);
+    } else {
+        LOG(DEBUG) << "Auth token required for key" << std::endl;
+        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" << std::endl;
+            return false;
+        }
+        const hw_auth_token_t* at = reinterpret_cast<const hw_auth_token_t*>(auth.token.data());
+        paramBuilder.Authorization(km::TAG_USER_SECURE_ID, at->user_id);
+        paramBuilder.Authorization(km::TAG_USER_AUTH_TYPE, km::HardwareAuthenticatorType::PASSWORD);
+        paramBuilder.Authorization(km::TAG_AUTH_TIMEOUT, AUTH_TIMEOUT);
+    }
+    return keymaster.generateKey(paramBuilder, key);
+}
+
+bool generateWrappedKey(userid_t user_id, KeyType key_type,
+                                     KeyBuffer* key) {
+    Keymaster keymaster;
+    if (!keymaster) return false;
+    *key = KeyBuffer(EXT4_AES_256_XTS_KEY_SIZE);
+    std::string key_temp;
+    auto paramBuilder = km::AuthorizationSetBuilder()
+                               .AesEncryptionKey(AES_KEY_BYTES * 8)
+                               .GcmModeMinMacLen(GCM_MAC_BYTES * 8)
+                               .Authorization(km::TAG_USER_ID, user_id);
+    km::KeyParameter param1;
+    param1.tag = (km::Tag) (android::hardware::keymaster::V4_0::KM_TAG_FBE_ICE);
+    param1.f.boolValue = true;
+    paramBuilder.push_back(param1);
+    km::KeyParameter param2;
+    if ((key_type == KeyType::DE_USER) || (key_type == KeyType::DE_SYS)) {
+        param2.tag = (km::Tag) (android::hardware::keymaster::V4_0::KM_TAG_KEY_TYPE);
+        param2.f.integer = 0;
+    } else if (key_type == KeyType::CE_USER) {
+        param2.tag = (km::Tag) (android::hardware::keymaster::V4_0::KM_TAG_KEY_TYPE);
+        param2.f.integer = 1;
+    }
+    paramBuilder.push_back(param2);
+    if (!keymaster.generateKey(paramBuilder, &key_temp)) return false;
+    *key = KeyBuffer(key_temp.size());
+    memcpy(reinterpret_cast<void*>(key->data()), key_temp.c_str(), key->size());
+    return true;
+}
+
+bool getEphemeralWrappedKey(km::KeyFormat format, KeyBuffer& kmKey, KeyBuffer* key) {
+    std::string key_temp;
+    Keymaster keymaster;
+    if (!keymaster) return false;
+
+    //Export once, if upgrade needed, upgrade and export again
+    bool export_again = true;
+    while (export_again) {
+        export_again = false;
+        auto ret = keymaster.exportKey(format, kmKey, "!", "!", &key_temp);
+        if (ret == km::ErrorCode::OK) {
+            *key = KeyBuffer(key_temp.size());
+            memcpy(reinterpret_cast<void*>(key->data()), key_temp.c_str(), key->size());
+            return true;
+        }
+        if (ret != km::ErrorCode::KEY_REQUIRES_UPGRADE) return false;
+        LOG(DEBUG) << "Upgrading key" << std::endl;
+        std::string kmKeyStr(reinterpret_cast<const char*>(kmKey.data()), kmKey.size());
+        std::string newKey;
+        if (!keymaster.upgradeKey(kmKeyStr, km::AuthorizationSet(), &newKey)) return false;
+        memcpy(reinterpret_cast<void*>(kmKey.data()), newKey.c_str(), kmKey.size());
+        LOG(INFO) << "Key upgraded" << std::endl;
+        export_again = true;
+    }
+    //Should never come here
+    return false;
+}
+
+static std::pair<km::AuthorizationSet, km::HardwareAuthToken> beginParams(
+    const KeyAuthentication& auth, const std::string& appId) {
+    auto paramBuilder = km::AuthorizationSetBuilder()
+                            .GcmModeMacLen(GCM_MAC_BYTES * 8)
+                            .Authorization(km::TAG_APPLICATION_ID, km::support::blob2hidlVec(appId));
+    km::HardwareAuthToken authToken;
+    if (!auth.token.empty()) {
+        LOG(DEBUG) << "Supplying auth token to Keymaster" << std::endl;
+        authToken = km::support::hidlVec2AuthToken(km::support::blob2hidlVec(auth.token));
+    }
+    return {paramBuilder, authToken};
+}
+
+static bool readFileToString(const std::string& filename, std::string* result) {
+    if (!android::base::ReadFileToString(filename, result)) {
+        PLOG(ERROR) << "Failed to read from " << filename << std::endl;
+        return false;
+    }
+    return true;
+}
+
+static bool writeStringToFile(const std::string& payload, const std::string& filename) {
+	PLOG(ERROR) << __FUNCTION__ << " called for " << filename << " and being skipped\n";
+	return true;
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(
+        open(filename.c_str(), O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0666)));
+    if (fd == -1) {
+        PLOG(ERROR) << "Failed to open " << filename;
+        return false;
+    }
+    if (!android::base::WriteStringToFd(payload, fd)) {
+        PLOG(ERROR) << "Failed to write to " << filename;
+        unlink(filename.c_str());
+        return false;
+    }
+    // fsync as close won't guarantee flush data
+    // see close(2), fsync(2) and b/68901441
+    if (fsync(fd) == -1) {
+        if (errno == EROFS || errno == EINVAL) {
+            PLOG(WARNING) << "Skip fsync " << filename
+                          << " on a file system does not support synchronization";
+        } else {
+            PLOG(ERROR) << "Failed to fsync " << filename;
+            unlink(filename.c_str());
+            return false;
+        }
+    }
+    return true;
+}
+
+static bool readRandomBytesOrLog(size_t count, std::string* out) {
+    auto status = ReadRandomBytes(count, *out);
+    if (status != OK) {
+        LOG(ERROR) << "Random read failed with status: " << status << std::endl;
+        return false;
+    }
+    return true;
+}
+
+bool createSecdiscardable(const std::string& filename, std::string* hash) {
+    std::string secdiscardable;
+    if (!readRandomBytesOrLog(SECDISCARDABLE_BYTES, &secdiscardable)) return false;
+    if (!writeStringToFile(secdiscardable, filename)) return false;
+    hashWithPrefix(kHashPrefix_secdiscardable, secdiscardable, hash);
+    return true;
+}
+
+bool readSecdiscardable(const std::string& filename, std::string* hash) {
+    std::string secdiscardable;
+    if (!readFileToString(filename, &secdiscardable)) return false;
+    hashWithPrefix(kHashPrefix_secdiscardable, secdiscardable, hash);
+    return true;
+}
+
+static KeymasterOperation begin(Keymaster& keymaster, const std::string& dir,
+                                km::KeyPurpose purpose, const km::AuthorizationSet& keyParams,
+                                const km::AuthorizationSet& opParams,
+                                const km::HardwareAuthToken& authToken,
+                                km::AuthorizationSet* outParams) {
+    auto kmKeyPath = dir + "/" + kFn_keymaster_key_blob;
+    std::string kmKey;
+    if (!readFileToString(kmKeyPath, &kmKey)) return KeymasterOperation();
+    km::AuthorizationSet inParams(keyParams);
+    inParams.append(opParams.begin(), opParams.end());
+    for (;;) {
+        auto opHandle = keymaster.begin(purpose, kmKey, inParams, authToken, outParams);
+        if (opHandle) {
+            return opHandle;
+        }
+        if (opHandle.errorCode() != km::ErrorCode::KEY_REQUIRES_UPGRADE) return opHandle;
+        LOG(DEBUG) << "Upgrading key in memory only: " << dir << std::endl;
+        std::string newKey;
+        if (!keymaster.upgradeKey(kmKey, keyParams, &newKey)) return KeymasterOperation();
+        /*auto newKeyPath = dir + "/" + kFn_keymaster_key_blob_upgraded;
+        if (!writeStringToFile(newKey, newKeyPath)) return KeymasterOperation();
+        if (rename(newKeyPath.c_str(), kmKeyPath.c_str()) != 0) {
+            PLOG(ERROR) << "Unable to move upgraded key to location: " << kmKeyPath;
+            return KeymasterOperation();
+        }
+        if (!keymaster.deleteKey(kmKey)) {
+            LOG(ERROR) << "Key deletion failed during upgrade, continuing anyway: " << dir;
+        }*/
+        kmKey = newKey;
+        LOG(INFO) << "Key upgraded in memory but not updated in folder: " << dir << std::endl;
+    }
+}
+
+static bool encryptWithKeymasterKey(Keymaster& keymaster, const std::string& dir,
+                                    const km::AuthorizationSet& keyParams,
+                                    const km::HardwareAuthToken& authToken,
+                                    const KeyBuffer& message, std::string* ciphertext) {
+    km::AuthorizationSet opParams;
+    km::AuthorizationSet outParams;
+    auto opHandle =
+        begin(keymaster, dir, km::KeyPurpose::ENCRYPT, keyParams, opParams, authToken, &outParams);
+    if (!opHandle) return false;
+    auto nonceBlob = outParams.GetTagValue(km::TAG_NONCE);
+    if (!nonceBlob.isOk()) {
+        LOG(ERROR) << "GCM encryption but no nonce generated" << std::endl;
+        return false;
+    }
+    // nonceBlob here is just a pointer into existing data, must not be freed
+    std::string nonce(reinterpret_cast<const char*>(&nonceBlob.value()[0]),
+                      nonceBlob.value().size());
+    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.finish(&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& dir,
+                                    const km::AuthorizationSet& keyParams,
+                                    const km::HardwareAuthToken& authToken,
+                                    const std::string& ciphertext, KeyBuffer* message) {
+    auto nonce = ciphertext.substr(0, GCM_NONCE_BYTES);
+    auto bodyAndMac = ciphertext.substr(GCM_NONCE_BYTES);
+    auto opParams = km::AuthorizationSetBuilder().Authorization(km::TAG_NONCE,
+                                                                km::support::blob2hidlVec(nonce));
+    auto opHandle =
+        begin(keymaster, dir, km::KeyPurpose::DECRYPT, keyParams, opParams, authToken, nullptr);
+    if (!opHandle) return false;
+    if (!opHandle.updateCompletely(bodyAndMac, message)) return false;
+    if (!opHandle.finish(nullptr)) return false;
+    return true;
+}
+
+static std::string getStretching(const KeyAuthentication& auth) {
+    if (!auth.usesKeymaster()) {
+        return kStretch_none;
+    } else if (auth.secret.empty()) {
+        return kStretch_nopassword;
+    } else {
+        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" << std::endl;
+            // 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 << std::endl;
+            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 << std::endl;
+            return false;
+        }
+    } else {
+        LOG(ERROR) << "Unknown stretching type: " << stretching << std::endl;
+        return false;
+    }
+    return true;
+}
+
+static bool generateAppId(const KeyAuthentication& auth, const std::string& stretching,
+                          const std::string& salt, const std::string& secdiscardable_hash,
+                          std::string* appId) {
+    std::string stretched;
+    if (!stretchSecret(stretching, auth.secret, salt, &stretched)) return false;
+    *appId = secdiscardable_hash + stretched;
+    return true;
+}
+
+static void logOpensslError() {
+    LOG(ERROR) << "Openssl error: " << ERR_get_error() << std::endl;
+}
+
+static bool encryptWithoutKeymaster(const std::string& preKey, const KeyBuffer& plaintext,
+                                    std::string* ciphertext) {
+    std::string key;
+    hashWithPrefix(kHashPrefix_keygen, preKey, &key);
+    key.resize(AES_KEY_BYTES);
+    if (!readRandomBytesOrLog(GCM_NONCE_BYTES, ciphertext)) return false;
+    auto ctx = std::unique_ptr<EVP_CIPHER_CTX, decltype(&::EVP_CIPHER_CTX_free)>(
+        EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free);
+    if (!ctx) {
+        logOpensslError();
+        return false;
+    }
+    if (1 != EVP_EncryptInit_ex(ctx.get(), EVP_aes_256_gcm(), NULL,
+                                reinterpret_cast<const uint8_t*>(key.data()),
+                                reinterpret_cast<const uint8_t*>(ciphertext->data()))) {
+        logOpensslError();
+        return false;
+    }
+    ciphertext->resize(GCM_NONCE_BYTES + plaintext.size() + GCM_MAC_BYTES);
+    int outlen;
+    if (1 != EVP_EncryptUpdate(
+                 ctx.get(), reinterpret_cast<uint8_t*>(&(*ciphertext)[0] + GCM_NONCE_BYTES),
+                 &outlen, reinterpret_cast<const uint8_t*>(plaintext.data()), plaintext.size())) {
+        logOpensslError();
+        return false;
+    }
+    if (outlen != static_cast<int>(plaintext.size())) {
+        LOG(ERROR) << "GCM ciphertext length should be " << plaintext.size() << " was " << outlen << std::endl;
+        return false;
+    }
+    if (1 != EVP_EncryptFinal_ex(
+                 ctx.get(),
+                 reinterpret_cast<uint8_t*>(&(*ciphertext)[0] + GCM_NONCE_BYTES + plaintext.size()),
+                 &outlen)) {
+        logOpensslError();
+        return false;
+    }
+    if (outlen != 0) {
+        LOG(ERROR) << "GCM EncryptFinal should be 0, was " << outlen << std::endl;
+        return false;
+    }
+    if (1 != EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_GET_TAG, GCM_MAC_BYTES,
+                                 reinterpret_cast<uint8_t*>(&(*ciphertext)[0] + GCM_NONCE_BYTES +
+                                                            plaintext.size()))) {
+        logOpensslError();
+        return false;
+    }
+    return true;
+}
+
+static bool decryptWithoutKeymaster(const std::string& preKey, const std::string& ciphertext,
+                                    KeyBuffer* plaintext) {
+    if (ciphertext.size() < GCM_NONCE_BYTES + GCM_MAC_BYTES) {
+        LOG(ERROR) << "GCM ciphertext too small: " << ciphertext.size() << std::endl;
+        return false;
+    }
+    std::string key;
+    hashWithPrefix(kHashPrefix_keygen, preKey, &key);
+    key.resize(AES_KEY_BYTES);
+    auto ctx = std::unique_ptr<EVP_CIPHER_CTX, decltype(&::EVP_CIPHER_CTX_free)>(
+        EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free);
+    if (!ctx) {
+        logOpensslError();
+        return false;
+    }
+    if (1 != EVP_DecryptInit_ex(ctx.get(), EVP_aes_256_gcm(), NULL,
+                                reinterpret_cast<const uint8_t*>(key.data()),
+                                reinterpret_cast<const uint8_t*>(ciphertext.data()))) {
+        logOpensslError();
+        return false;
+    }
+    *plaintext = KeyBuffer(ciphertext.size() - GCM_NONCE_BYTES - GCM_MAC_BYTES);
+    int outlen;
+    if (1 != EVP_DecryptUpdate(ctx.get(), reinterpret_cast<uint8_t*>(&(*plaintext)[0]), &outlen,
+                               reinterpret_cast<const uint8_t*>(ciphertext.data() + GCM_NONCE_BYTES),
+                               plaintext->size())) {
+        logOpensslError();
+        return false;
+    }
+    if (outlen != static_cast<int>(plaintext->size())) {
+        LOG(ERROR) << "GCM plaintext length should be " << plaintext->size() << " was " << outlen << std::endl;
+        return false;
+    }
+    if (1 != EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_TAG, GCM_MAC_BYTES,
+                                 const_cast<void*>(reinterpret_cast<const void*>(
+                                     ciphertext.data() + GCM_NONCE_BYTES + plaintext->size())))) {
+        logOpensslError();
+        return false;
+    }
+    if (1 != EVP_DecryptFinal_ex(ctx.get(),
+                                 reinterpret_cast<uint8_t*>(&(*plaintext)[0] + plaintext->size()),
+                                 &outlen)) {
+        logOpensslError();
+        return false;
+    }
+    if (outlen != 0) {
+        LOG(ERROR) << "GCM EncryptFinal should be 0, was " << outlen << std::endl;
+        return false;
+    }
+    return true;
+}
+
+bool pathExists(const std::string& path) {
+    return access(path.c_str(), F_OK) == 0;
+}
+
+bool storeKey(const std::string& dir, const KeyAuthentication& auth, const KeyBuffer& key) {
+    if (TEMP_FAILURE_RETRY(mkdir(dir.c_str(), 0700)) == -1) {
+        PLOG(ERROR) << "key mkdir " << dir << std::endl;
+        return false;
+    }
+    if (!writeStringToFile(kCurrentVersion, dir + "/" + kFn_version)) return false;
+    std::string secdiscardable_hash;
+    if (!createSecdiscardable(dir + "/" + kFn_secdiscardable, &secdiscardable_hash)) return false;
+    std::string stretching = getStretching(auth);
+    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" << std::endl;
+            return false;
+        }
+        if (!writeStringToFile(salt, dir + "/" + kFn_salt)) return false;
+    }
+    std::string appId;
+    if (!generateAppId(auth, stretching, salt, secdiscardable_hash, &appId)) return false;
+    std::string encryptedKey;
+    if (auth.usesKeymaster()) {
+        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;
+        km::AuthorizationSet keyParams;
+        km::HardwareAuthToken authToken;
+        std::tie(keyParams, authToken) = beginParams(auth, appId);
+        if (!encryptWithKeymasterKey(keymaster, dir, keyParams, authToken, key, &encryptedKey))
+            return false;
+    } else {
+        if (!encryptWithoutKeymaster(appId, key, &encryptedKey)) return false;
+    }
+    if (!writeStringToFile(encryptedKey, dir + "/" + kFn_encrypted_key)) return false;
+    return true;
+}
+
+bool storeKeyAtomically(const std::string& key_path, const std::string& tmp_path,
+                        const KeyAuthentication& auth, const KeyBuffer& key) {
+    if (pathExists(key_path)) {
+        LOG(ERROR) << "Already exists, cannot create key at: " << key_path << std::endl;
+        return false;
+    }
+    if (pathExists(tmp_path)) {
+        LOG(DEBUG) << "Already exists, destroying: " << tmp_path << std::endl;
+        destroyKey(tmp_path);  // May be partially created so ignore errors
+    }
+    if (!storeKey(tmp_path, auth, key)) return false;
+    if (rename(tmp_path.c_str(), key_path.c_str()) != 0) {
+        PLOG(ERROR) << "Unable to move new key to location: " << key_path << std::endl;
+        return false;
+    }
+    LOG(DEBUG) << "Created key: " << key_path << std::endl;
+    return true;
+}
+
+bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, KeyBuffer* key) {
+    std::string version;
+    if (!readFileToString(dir + "/" + kFn_version, &version)) return false;
+    if (version != kCurrentVersion) {
+        LOG(ERROR) << "Version mismatch, expected " << kCurrentVersion << " got " << version << std::endl;
+        return false;
+    }
+    std::string secdiscardable_hash;
+    if (!readSecdiscardable(dir + "/" + kFn_secdiscardable, &secdiscardable_hash)) 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_hash, &appId)) return false;
+    std::string encryptedMessage;
+    if (!readFileToString(dir + "/" + kFn_encrypted_key, &encryptedMessage)) return false;
+    if (auth.usesKeymaster()) {
+        Keymaster keymaster;
+        if (!keymaster) return false;
+        km::AuthorizationSet keyParams;
+        km::HardwareAuthToken authToken;
+        std::tie(keyParams, authToken) = beginParams(auth, appId);
+        if (!decryptWithKeymasterKey(keymaster, dir, keyParams, authToken, encryptedMessage, key))
+            return false;
+    } else {
+        if (!decryptWithoutKeymaster(appId, encryptedMessage, key)) return false;
+    }
+    return true;
+}
+
+static bool deleteKey(const std::string& dir) {
+	LOG(DEBUG) << "not deleting key in " << __FILE__ << std::endl;
+	return true;
+    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;
+}
+
+bool runSecdiscardSingle(const std::string& file) {
+    if (ForkExecvp(std::vector<std::string>{kSecdiscardPath, "--", file}) != 0) {
+        LOG(ERROR) << "secdiscard failed" << std::endl;
+        return false;
+    }
+    return true;
+}
+
+static bool recursiveDeleteKey(const std::string& dir) {
+	LOG(DEBUG) << "not recursively deleting key in " << __FILE__ << std::endl;
+	return true;
+    if (ForkExecvp(std::vector<std::string>{kRmPath, "-rf", dir}) != 0) {
+        LOG(ERROR) << "recursive delete failed" << std::endl;
+        return false;
+    }
+    return true;
+}
+
+bool destroyKey(const std::string& dir) {
+	LOG(DEBUG) << "not destroying key in " << __FILE__ << std::endl;
+	return true;
+    bool success = true;
+    // Try each thing, even if previous things failed.
+    bool uses_km = pathExists(dir + "/" + kFn_keymaster_key_blob);
+    if (uses_km) {
+        success &= deleteKey(dir);
+    }
+    auto secdiscard_cmd = std::vector<std::string>{
+        kSecdiscardPath, "--", dir + "/" + kFn_encrypted_key, dir + "/" + kFn_secdiscardable,
+    };
+    if (uses_km) {
+        secdiscard_cmd.emplace_back(dir + "/" + kFn_keymaster_key_blob);
+    }
+    if (ForkExecvp(secdiscard_cmd) != 0) {
+        LOG(ERROR) << "secdiscard failed" << std::endl;
+        success = false;
+    }
+    success &= recursiveDeleteKey(dir);
+    return success;
+}
+
+}  // namespace vold
+}  // namespace android
diff --git a/crypto/ext4crypt/KeyStorage4.h b/crypto/ext4crypt/KeyStorage4.h
new file mode 100644
index 0000000..f74865f
--- /dev/null
+++ b/crypto/ext4crypt/KeyStorage4.h
@@ -0,0 +1,86 @@
+/*
+ * 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_TWRP_KEYSTORAGE_H
+#define ANDROID_TWRP_KEYSTORAGE_H
+
+#include "Keymaster4.h"
+#include "KeyBuffer.h"
+#include <ext4_utils/ext4_crypt.h>
+
+#include <string>
+
+namespace android {
+namespace vold {
+
+namespace km = ::android::hardware::keymaster::V4_0;
+
+// 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 "token" and "secret" are nonempty, "secret" is appended to the application-specific
+// binary needed to unlock.
+// If only "secret" is nonempty, it is used to decrypt in a non-Keymaster process.
+class KeyAuthentication {
+  public:
+    KeyAuthentication(std::string t, std::string s) : token{t}, secret{s} {};
+
+    bool usesKeymaster() const { return !token.empty() || secret.empty(); };
+
+    const std::string token;
+    const std::string secret;
+};
+
+enum class KeyType {
+    DE_SYS,
+    DE_USER,
+    CE_USER
+};
+
+extern const KeyAuthentication kEmptyAuthentication;
+
+// Checks if path "path" exists.
+bool pathExists(const std::string& path);
+
+bool createSecdiscardable(const std::string& path, std::string* hash);
+bool readSecdiscardable(const std::string& path, std::string* hash);
+
+// 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 KeyBuffer& key);
+
+// Create a directory at the named path, and store "key" in it as storeKey
+// This version creates the key in "tmp_path" then atomically renames "tmp_path"
+// to "key_path" thereby ensuring that the key is either stored entirely or
+// not at all.
+bool storeKeyAtomically(const std::string& key_path, const std::string& tmp_path,
+                        const KeyAuthentication& auth, const KeyBuffer& key);
+
+// Retrieve the key from the named directory.
+bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, KeyBuffer* key);
+
+// Securely destroy the key stored in the named directory and delete the directory.
+bool destroyKey(const std::string& dir);
+
+bool runSecdiscardSingle(const std::string& file);
+
+bool generateWrappedKey(userid_t user_id, KeyType key_type, KeyBuffer* key);
+bool getEphemeralWrappedKey(km::KeyFormat format, KeyBuffer& kmKey, KeyBuffer* key);
+}  // namespace vold
+}  // namespace android
+
+#endif
diff --git a/crypto/ext4crypt/KeyUtil.cpp b/crypto/ext4crypt/KeyUtil.cpp
new file mode 100644
index 0000000..3dc0500
--- /dev/null
+++ b/crypto/ext4crypt/KeyUtil.cpp
@@ -0,0 +1,241 @@
+/*
+ * 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 "KeyUtil.h"
+
+#include <iomanip>
+#include <sstream>
+#include <string>
+
+#include <openssl/sha.h>
+
+#include <android-base/file.h>
+//#include <android-base/logging.h>
+#include <keyutils.h>
+
+#include "KeyStorage4.h"
+#include "Ext4CryptPie.h"
+#include "Utils.h"
+
+#include <iostream>
+#define LOG(x) std::cout
+#define PLOG(x) std::cout
+#include <sys/types.h>
+#include <unistd.h>
+
+#define MAX_USER_ID 0xFFFFFFFF
+
+using android::hardware::keymaster::V4_0::KeyFormat;
+using android::vold::KeyType;
+
+namespace android {
+namespace vold {
+
+// 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;
+};
+
+bool randomKey(KeyBuffer* key) {
+    *key = KeyBuffer(EXT4_AES_256_XTS_KEY_SIZE);
+    if (ReadRandomBytes(key->size(), key->data()) != 0) {
+        // TODO status_t plays badly with PLOG, fix it.
+        LOG(ERROR) << "Random read failed" << std::endl;
+        return false;
+    }
+    return true;
+}
+
+// Get raw keyref - used to make keyname and to pass to ioctl
+static std::string generateKeyRef(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 fillKey(const KeyBuffer& 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 char const* const NAME_PREFIXES[] = {
+    "ext4",
+    "f2fs",
+    "fscrypt",
+    nullptr
+};
+
+static std::string keyname(const std::string& prefix, const std::string& raw_ref) {
+    std::ostringstream o;
+    o << prefix << ":";
+    for (unsigned char i : raw_ref) {
+        o << std::hex << std::setw(2) << std::setfill('0') << (int)i;
+    }
+    return o.str();
+}
+
+// Get the keyring we store all keys in
+static bool e4cryptKeyring(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" << std::endl;
+        return false;
+    }
+    return true;
+}
+
+// Install password into global keyring
+// Return raw key reference for use in policy
+bool installKey(const KeyBuffer& key, std::string* raw_ref) {
+    // Place ext4_encryption_key into automatically zeroing buffer.
+    KeyBuffer ext4KeyBuffer(sizeof(ext4_encryption_key));
+    ext4_encryption_key &ext4_key = *reinterpret_cast<ext4_encryption_key*>(ext4KeyBuffer.data());
+
+    if (!fillKey(key, &ext4_key)) return false;
+    if (is_wrapped_key_supported()) {
+        /* When wrapped key is supported, only the first 32 bytes are
+           the same per boot. The second 32 bytes can change as the ephemeral
+           key is different. */
+        *raw_ref = generateKeyRef(ext4_key.raw, (ext4_key.size)/2);
+    } else {
+        *raw_ref = generateKeyRef(ext4_key.raw, ext4_key.size);
+    }
+    key_serial_t device_keyring;
+    if (!e4cryptKeyring(&device_keyring)) return false;
+    for (char const* const* name_prefix = NAME_PREFIXES; *name_prefix != nullptr; name_prefix++) {
+        auto ref = keyname(*name_prefix, *raw_ref);
+        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 << std::endl;
+            return false;
+        }
+        LOG(DEBUG) << "Added key " << key_id << " (" << ref << ") to keyring " << device_keyring
+                   << " in process " << getpid() << std::endl;
+    }
+    return true;
+}
+
+bool evictKey(const std::string& raw_ref) {
+	LOG(ERROR) << "not actually evicting key\n";
+	return true;
+    key_serial_t device_keyring;
+    if (!e4cryptKeyring(&device_keyring)) return false;
+    bool success = true;
+    for (char const* const* name_prefix = NAME_PREFIXES; *name_prefix != nullptr; name_prefix++) {
+        auto ref = keyname(*name_prefix, raw_ref);
+        auto key_serial = keyctl_search(device_keyring, "logon", ref.c_str(), 0);
+
+        // Unlink the key from the keyring.  Prefer unlinking to revoking or
+        // invalidating, since unlinking is actually no less secure currently, and
+        // it avoids bugs in certain kernel versions where the keyring key is
+        // referenced from places it shouldn't be.
+        if (keyctl_unlink(key_serial, device_keyring) != 0) {
+            PLOG(ERROR) << "Failed to unlink key with serial " << key_serial << " ref " << ref;
+            success = false;
+        } else {
+            LOG(DEBUG) << "Unlinked key with serial " << key_serial << " ref " << ref;
+        }
+    }
+    return success;
+}
+
+bool retrieveAndInstallKey(bool create_if_absent, const KeyAuthentication& key_authentication,
+                           const std::string& key_path, const std::string& tmp_path,
+                           std::string* key_ref, bool wrapped_key_supported) {
+    KeyBuffer key;
+    if (pathExists(key_path)) {
+        LOG(DEBUG) << "Key exists, using: " << key_path << std::endl;
+        if (!retrieveKey(key_path, key_authentication, &key)) return false;
+    } else {
+        if (!create_if_absent) {
+           LOG(ERROR) << "No key found in " << key_path << std::endl;
+           return false;
+        }
+        LOG(INFO) << "Creating new key in " << key_path << std::endl;
+        if (wrapped_key_supported) {
+            if(!generateWrappedKey(MAX_USER_ID, KeyType::DE_SYS, &key)) return false;
+        } else {
+            if (!randomKey(&key)) return false;
+        }
+        if (!storeKeyAtomically(key_path, tmp_path, key_authentication, key)) return false;
+    }
+
+    if (wrapped_key_supported) {
+        KeyBuffer ephemeral_wrapped_key;
+        if (!getEphemeralWrappedKey(KeyFormat::RAW, key, &ephemeral_wrapped_key)) {
+            LOG(ERROR) << "Failed to export key in retrieveAndInstallKey";
+            return false;
+        }
+        key = std::move(ephemeral_wrapped_key);
+    }
+
+    if (!installKey(key, key_ref)) {
+        LOG(ERROR) << "Failed to install key in " << key_path << std::endl;
+        return false;
+    }
+    return true;
+}
+
+bool retrieveKey(bool create_if_absent, const std::string& key_path,
+                 const std::string& tmp_path, KeyBuffer* key) {
+    if (pathExists(key_path)) {
+        LOG(DEBUG) << "Key exists, using: " << key_path << std::endl;
+        if (!retrieveKey(key_path, kEmptyAuthentication, key)) return false;
+    } else {
+        if (!create_if_absent) {
+           LOG(ERROR) << "No key found in " << key_path << std::endl;
+           return false;
+        }
+        LOG(INFO) << "Creating new key in " << key_path << std::endl;
+        if (!randomKey(key)) return false;
+        if (!storeKeyAtomically(key_path, tmp_path,
+                kEmptyAuthentication, *key)) return false;
+    }
+    return true;
+}
+
+}  // namespace vold
+}  // namespace android
diff --git a/crypto/ext4crypt/KeyUtil.h b/crypto/ext4crypt/KeyUtil.h
new file mode 100644
index 0000000..46a3124
--- /dev/null
+++ b/crypto/ext4crypt/KeyUtil.h
@@ -0,0 +1,42 @@
+/*
+ * 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_KEYUTIL_H
+#define ANDROID_VOLD_KEYUTIL_H
+
+#include "KeyBuffer.h"
+#include "KeyStorage4.h"
+#include "Keymaster4.h"
+
+#include <string>
+#include <memory>
+
+namespace android {
+namespace vold {
+
+bool randomKey(KeyBuffer* key);
+bool installKey(const KeyBuffer& key, std::string* raw_ref);
+bool evictKey(const std::string& raw_ref);
+bool retrieveAndInstallKey(bool create_if_absent, const KeyAuthentication& key_authentication,
+                           const std::string& key_path, const std::string& tmp_path,
+                           std::string* key_ref, bool wrapped_key_supported);
+bool retrieveKey(bool create_if_absent, const std::string& key_path,
+                 const std::string& tmp_path, KeyBuffer* key);
+
+}  // 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..bd3f219
--- /dev/null
+++ b/crypto/ext4crypt/Keymaster.h
@@ -0,0 +1,111 @@
+/*
+ * 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>
+#include "Utils.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/Keymaster3.cpp b/crypto/ext4crypt/Keymaster3.cpp
new file mode 100644
index 0000000..7862044
--- /dev/null
+++ b/crypto/ext4crypt/Keymaster3.cpp
@@ -0,0 +1,348 @@
+/*
+ * 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 "Keymaster3.h"
+
+//#include <android-base/logging.h>
+#include <keystore/keymaster_tags.h>
+#include <keystore/authorization_set.h>
+#include <keystore/keystore_hidl_support.h>
+
+#include <iostream>
+#define ERROR 1
+#define LOG(x) std::cout
+
+using namespace ::keystore;
+using android::hardware::hidl_string;
+
+namespace android {
+namespace vold {
+
+KeymasterOperation::~KeymasterOperation() {
+    if (mDevice.get()) mDevice->abort(mOpHandle);
+}
+
+bool KeymasterOperation::updateCompletely(const std::string& input, std::string* output) {
+    if (output)
+        output->clear();
+    auto it = input.begin();
+    uint32_t inputConsumed;
+
+    ErrorCode km_error;
+    auto hidlCB = [&] (ErrorCode ret, uint32_t _inputConsumed,
+            const hidl_vec<KeyParameter>& /*ignored*/, const hidl_vec<uint8_t>& _output) {
+        km_error = ret;
+        if (km_error != ErrorCode::OK) return;
+        inputConsumed = _inputConsumed;
+        if (output)
+            output->append(reinterpret_cast<const char*>(&_output[0]), _output.size());
+    };
+
+    while (it != input.end()) {
+        size_t toRead = static_cast<size_t>(input.end() - it);
+        auto inputBlob = blob2hidlVec(reinterpret_cast<const uint8_t*>(&*it), toRead);
+        auto error = mDevice->update(mOpHandle, hidl_vec<KeyParameter>(), inputBlob, hidlCB);
+        if (!error.isOk()) {
+            LOG(ERROR) << "update failed: " << error.description();
+            mDevice = nullptr;
+            return false;
+        }
+        if (km_error != ErrorCode::OK) {
+            LOG(ERROR) << "update failed, code " << int32_t(km_error);
+            mDevice = nullptr;
+            return false;
+        }
+        if (inputConsumed > toRead) {
+            LOG(ERROR) << "update reported too much input consumed";
+            mDevice = nullptr;
+            return false;
+        }
+        it += inputConsumed;
+    }
+    return true;
+}
+
+bool KeymasterOperation::finish(std::string* output) {
+    ErrorCode km_error;
+    auto hidlCb = [&] (ErrorCode ret, const hidl_vec<KeyParameter>& /*ignored*/,
+            const hidl_vec<uint8_t>& _output) {
+        km_error = ret;
+        if (km_error != ErrorCode::OK) return;
+        if (output)
+            output->assign(reinterpret_cast<const char*>(&_output[0]), _output.size());
+    };
+    auto error = mDevice->finish(mOpHandle, hidl_vec<KeyParameter>(), hidl_vec<uint8_t>(),
+            hidl_vec<uint8_t>(), hidlCb);
+    mDevice = nullptr;
+    if (!error.isOk()) {
+        LOG(ERROR) << "finish failed: " << error.description();
+        return false;
+    }
+    if (km_error != ErrorCode::OK) {
+        LOG(ERROR) << "finish failed, code " << int32_t(km_error);
+        return false;
+    }
+    return true;
+}
+
+Keymaster::Keymaster() {
+    mDevice = ::android::hardware::keymaster::V3_0::IKeymasterDevice::getService();
+}
+
+/*bool Keymaster::generateKey(const AuthorizationSet& inParams, std::string* key) {
+    ErrorCode km_error;
+    auto hidlCb = [&] (ErrorCode ret, const hidl_vec<uint8_t>& keyBlob,
+            const KeyCharacteristics& /*ignored* /) {
+        km_error = ret;
+        if (km_error != ErrorCode::OK) return;
+        if (key)
+            key->assign(reinterpret_cast<const char*>(&keyBlob[0]), keyBlob.size());
+    };
+
+    auto error = mDevice->generateKey(inParams.hidl_data(), hidlCb);
+    if (!error.isOk()) {
+        LOG(ERROR) << "generate_key failed: " << error.description();
+        return false;
+    }
+    if (km_error != ErrorCode::OK) {
+        LOG(ERROR) << "generate_key failed, code " << int32_t(km_error);
+        return false;
+    }
+    return true;
+}*/
+
+bool Keymaster::deleteKey(const std::string& key) {
+	LOG(ERROR) << "NOT deleting key in TWRP";
+	return false;
+    /*auto keyBlob = blob2hidlVec(key);
+    auto error = mDevice->deleteKey(keyBlob);
+    if (!error.isOk()) {
+        LOG(ERROR) << "delete_key failed: " << error.description();
+        return false;
+    }
+    if (ErrorCode(error) != ErrorCode::OK) {
+        LOG(ERROR) << "delete_key failed, code " << uint32_t(ErrorCode(error));
+        return false;
+    }
+    return true;*/
+}
+
+bool Keymaster::upgradeKey(const std::string& oldKey, const AuthorizationSet& inParams,
+                           std::string* newKey) {
+    auto oldKeyBlob = blob2hidlVec(oldKey);
+    ErrorCode km_error;
+    auto hidlCb = [&] (ErrorCode ret, const hidl_vec<uint8_t>& upgradedKeyBlob) {
+        km_error = ret;
+        if (km_error != ErrorCode::OK) return;
+        if (newKey)
+            newKey->assign(reinterpret_cast<const char*>(&upgradedKeyBlob[0]),
+                    upgradedKeyBlob.size());
+    };
+    auto error = mDevice->upgradeKey(oldKeyBlob, inParams.hidl_data(), hidlCb);
+    if (!error.isOk()) {
+        LOG(ERROR) << "upgrade_key failed: " << error.description();
+        return false;
+    }
+    if (km_error != ErrorCode::OK) {
+        LOG(ERROR) << "upgrade_key failed, code " << int32_t(km_error);
+        return false;
+    }
+    return true;
+}
+
+KeymasterOperation Keymaster::begin(KeyPurpose purpose, const std::string& key,
+                                    const AuthorizationSet& inParams,
+                                    AuthorizationSet* outParams) {
+    auto keyBlob = blob2hidlVec(key);
+    uint64_t mOpHandle;
+    ErrorCode km_error;
+
+    auto hidlCb = [&] (ErrorCode ret, const hidl_vec<KeyParameter>& _outParams,
+            uint64_t operationHandle) {
+        km_error = ret;
+        if (km_error != ErrorCode::OK) return;
+        if (outParams)
+            *outParams = _outParams;
+        mOpHandle = operationHandle;
+    };
+
+    auto error = mDevice->begin(purpose, keyBlob, inParams.hidl_data(), hidlCb);
+    if (!error.isOk()) {
+        LOG(ERROR) << "begin failed: " << error.description();
+        return KeymasterOperation(ErrorCode::UNKNOWN_ERROR);
+    }
+    if (km_error != ErrorCode::OK) {
+        LOG(ERROR) << "begin failed, code " << int32_t(km_error);
+        return KeymasterOperation(km_error);
+    }
+    return KeymasterOperation(mDevice, mOpHandle);
+}
+bool Keymaster::isSecure() {
+    bool _isSecure = false;
+    auto rc = mDevice->getHardwareFeatures(
+            [&] (bool isSecure, bool, bool, bool, bool, const hidl_string&, const hidl_string&) {
+                _isSecure = isSecure; });
+    return rc.isOk() && _isSecure;
+}
+
+}  // namespace vold
+}  // namespace android
+
+using namespace ::android::vold;
+
+/*
+int keymaster_compatibility_cryptfs_scrypt() {
+    Keymaster dev;
+    if (!dev) {
+        LOG(ERROR) << "Failed to initiate keymaster session";
+        return -1;
+    }
+    return dev.isSecure();
+}
+*/
+
+/*int keymaster_create_key_for_cryptfs_scrypt(uint32_t rsa_key_size,
+                                            uint64_t rsa_exponent,
+                                            uint32_t ratelimit,
+                                            uint8_t* key_buffer,
+                                            uint32_t key_buffer_size,
+                                            uint32_t* key_out_size)
+{
+    Keymaster dev;
+    std::string key;
+    if (!dev) {
+        LOG(ERROR) << "Failed to initiate keymaster session";
+        return -1;
+    }
+    if (!key_buffer || !key_out_size) {
+        LOG(ERROR) << __FILE__ << ":" << __LINE__ << ":Invalid argument";
+        return -1;
+    }
+    if (key_out_size) {
+        *key_out_size = 0;
+    }
+
+    auto paramBuilder = AuthorizationSetBuilder()
+                            .Authorization(TAG_ALGORITHM, Algorithm::RSA)
+                            .Authorization(TAG_KEY_SIZE, rsa_key_size)
+                            .Authorization(TAG_RSA_PUBLIC_EXPONENT, rsa_exponent)
+                            .Authorization(TAG_PURPOSE, KeyPurpose::SIGN)
+                            .Authorization(TAG_PADDING, PaddingMode::NONE)
+                            .Authorization(TAG_DIGEST, Digest::NONE)
+                            .Authorization(TAG_BLOB_USAGE_REQUIREMENTS,
+                                    KeyBlobUsageRequirements::STANDALONE)
+                            .Authorization(TAG_NO_AUTH_REQUIRED)
+                            .Authorization(TAG_MIN_SECONDS_BETWEEN_OPS, ratelimit);
+
+    if (!dev.generateKey(paramBuilder, &key)) {
+        return -1;
+    }
+
+    if (key_out_size) {
+        *key_out_size = key.size();
+    }
+
+    if (key_buffer_size < key.size()) {
+        return -1;
+    }
+
+    std::copy(key.data(), key.data() + key.size(), key_buffer);
+    return 0;
+}*/
+
+int keymaster_sign_object_for_cryptfs_scrypt(const uint8_t* key_blob,
+                                             size_t key_blob_size,
+                                             uint32_t ratelimit,
+                                             const uint8_t* object,
+                                             const size_t object_size,
+                                             uint8_t** signature_buffer,
+                                             size_t* signature_buffer_size,
+                                             uint8_t* key_buffer,
+                                             uint32_t key_buffer_size,
+                                             uint32_t* key_out_size)
+{
+    Keymaster dev;
+    if (!dev) {
+        LOG(ERROR) << "Failed to initiate keymaster session";
+        return -1;
+    }
+    if (!key_blob || !object || !signature_buffer || !signature_buffer_size) {
+        LOG(ERROR) << __FILE__ << ":" << __LINE__ << ":Invalid argument";
+        return -1;
+    }
+
+    AuthorizationSet outParams;
+    std::string key(reinterpret_cast<const char*>(key_blob), key_blob_size);
+    std::string input(reinterpret_cast<const char*>(object), object_size);
+    std::string output;
+    KeymasterOperation op;
+
+    auto paramBuilder = AuthorizationSetBuilder()
+                            .Authorization(TAG_PADDING, PaddingMode::NONE)
+                            .Authorization(TAG_DIGEST, Digest::NONE);
+
+    while (true) {
+        op = dev.begin(KeyPurpose::SIGN, key, paramBuilder, &outParams);
+        if (op.errorCode() == ErrorCode::KEY_RATE_LIMIT_EXCEEDED) {
+            sleep(ratelimit);
+            continue;
+        } else if (op.errorCode() == ErrorCode::KEY_REQUIRES_UPGRADE) {
+            std::string newKey;
+            bool ret = dev.upgradeKey(key, paramBuilder, &newKey);
+            if(ret == false) {
+                LOG(ERROR) << "Error upgradeKey: ";
+                return -1;
+            }
+
+            if (key_out_size) {
+                *key_out_size = newKey.size();
+            }
+
+            if (key_buffer_size < newKey.size()) {
+                LOG(ERROR) << "key buffer size is too small";
+                return -1;
+            }
+
+            std::copy(newKey.data(), newKey.data() + newKey.size(), key_buffer);
+            key = newKey;
+        } else break;
+    }
+
+    if (op.errorCode() != ErrorCode::OK) {
+        LOG(ERROR) << "Error starting keymaster signature transaction: " << int32_t(op.errorCode());
+        return -1;
+    }
+
+    if (!op.updateCompletely(input, &output)) {
+        LOG(ERROR) << "Error sending data to keymaster signature transaction: "
+                   << uint32_t(op.errorCode());
+        return -1;
+    }
+
+    if (!op.finish(&output)) {
+        LOG(ERROR) << "Error finalizing keymaster signature transaction: " << int32_t(op.errorCode());
+        return -1;
+    }
+
+    *signature_buffer = reinterpret_cast<uint8_t*>(malloc(output.size()));
+    if (*signature_buffer == nullptr) {
+        LOG(ERROR) << "Error allocation buffer for keymaster signature";
+        return -1;
+    }
+    *signature_buffer_size = output.size();
+    std::copy(output.data(), output.data() + output.size(), *signature_buffer);
+    return 0;
+}
diff --git a/crypto/ext4crypt/Keymaster3.h b/crypto/ext4crypt/Keymaster3.h
new file mode 100644
index 0000000..cb5b644
--- /dev/null
+++ b/crypto/ext4crypt/Keymaster3.h
@@ -0,0 +1,151 @@
+/*
+ * 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
+
+#ifdef __cplusplus
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include <android/hardware/keymaster/3.0/IKeymasterDevice.h>
+#include <keystore/authorization_set.h>
+#include "Utils.h"
+
+namespace android {
+namespace vold {
+using ::android::hardware::keymaster::V3_0::IKeymasterDevice;
+using ::keystore::ErrorCode;
+using ::keystore::KeyPurpose;
+using ::keystore::AuthorizationSet;
+
+// C++ wrappers to the Keymaster hidl interface.
+// This is tailored to the needs of KeyStorage, but could be extended to be
+// a more general interface.
+
+// Wrapper for a Keymaster operation handle 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 mError == ErrorCode::OK; }
+    ErrorCode errorCode() { return mError; }
+    // 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 and write the output to this string, unless pointer is null.
+    bool finish(std::string* output);
+    // Move constructor
+    KeymasterOperation(KeymasterOperation&& rhs) {
+        mDevice = std::move(rhs.mDevice);
+        mOpHandle = std::move(rhs.mOpHandle);
+        mError = std::move(rhs.mError);
+    }
+    // Construct an object in an error state for error returns
+    KeymasterOperation()
+        : mDevice{nullptr}, mOpHandle{0},
+          mError {ErrorCode::UNKNOWN_ERROR} {}
+    // Move Assignment
+    KeymasterOperation& operator= (KeymasterOperation&& rhs) {
+        mDevice = std::move(rhs.mDevice);
+        mOpHandle = std::move(rhs.mOpHandle);
+        mError = std::move(rhs.mError);
+        rhs.mError = ErrorCode::UNKNOWN_ERROR;
+        rhs.mOpHandle = 0;
+        return *this;
+    }
+
+  private:
+    KeymasterOperation(const sp<IKeymasterDevice>& d, uint64_t h)
+        : mDevice{d}, mOpHandle{h}, mError {ErrorCode::OK} {}
+    KeymasterOperation(ErrorCode error)
+        : mDevice{nullptr}, mOpHandle{0},
+          mError {error} {}
+    sp<IKeymasterDevice> mDevice;
+    uint64_t mOpHandle;
+    ErrorCode mError;
+    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.get() != 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);
+    // Replace stored key blob in response to KM_ERROR_KEY_REQUIRES_UPGRADE.
+    bool upgradeKey(const std::string& oldKey, const AuthorizationSet& inParams,
+                    std::string* newKey);
+    // Begin a new cryptographic operation, collecting output parameters if pointer is non-null
+    KeymasterOperation begin(KeyPurpose purpose, const std::string& key,
+                             const AuthorizationSet& inParams, AuthorizationSet* outParams);
+    bool isSecure();
+
+  private:
+    sp<hardware::keymaster::V3_0::IKeymasterDevice> mDevice;
+    DISALLOW_COPY_AND_ASSIGN(Keymaster);
+};
+
+}  // namespace vold
+}  // namespace android
+
+#endif // __cplusplus
+
+
+/*
+ * The following functions provide C bindings to keymaster services
+ * needed by cryptfs scrypt. The compatibility check checks whether
+ * the keymaster implementation is considered secure, i.e., TEE backed.
+ * The create_key function generates an RSA key for signing.
+ * The sign_object function signes an object with the given keymaster
+ * key.
+ */
+__BEGIN_DECLS
+
+//int keymaster_compatibility_cryptfs_scrypt();
+/*int keymaster_create_key_for_cryptfs_scrypt(uint32_t rsa_key_size,
+                                            uint64_t rsa_exponent,
+                                            uint32_t ratelimit,
+                                            uint8_t* key_buffer,
+                                            uint32_t key_buffer_size,
+                                            uint32_t* key_out_size);*/
+
+int keymaster_sign_object_for_cryptfs_scrypt(const uint8_t* key_blob,
+                                             size_t key_blob_size,
+                                             uint32_t ratelimit,
+                                             const uint8_t* object,
+                                             const size_t object_size,
+                                             uint8_t** signature_buffer,
+                                             size_t* signature_buffer_size,
+                                             uint8_t* key_buffer,
+                                             uint32_t key_buffer_size,
+                                             uint32_t* key_out_size);
+
+__END_DECLS
+
+#endif
diff --git a/crypto/ext4crypt/Keymaster4.cpp b/crypto/ext4crypt/Keymaster4.cpp
new file mode 100644
index 0000000..6507bb1
--- /dev/null
+++ b/crypto/ext4crypt/Keymaster4.cpp
@@ -0,0 +1,382 @@
+/*
+ * 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 "Keymaster4.h"
+
+//#include <android-base/logging.h>
+#include <keymasterV4_0/authorization_set.h>
+#include <keymasterV4_0/keymaster_utils.h>
+
+#include <iostream>
+#define LOG(x) std::cout
+#define PLOG(x) std::cout
+
+namespace android {
+namespace vold {
+
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::keymaster::V4_0::SecurityLevel;
+
+KeymasterOperation::~KeymasterOperation() {
+    if (mDevice) mDevice->abort(mOpHandle);
+}
+
+bool KeymasterOperation::updateCompletely(const char* input, size_t inputLen,
+                                          const std::function<void(const char*, size_t)> consumer) {
+    uint32_t inputConsumed = 0;
+
+    km::ErrorCode km_error;
+    auto hidlCB = [&](km::ErrorCode ret, uint32_t inputConsumedDelta,
+                      const hidl_vec<km::KeyParameter>& /*ignored*/,
+                      const hidl_vec<uint8_t>& _output) {
+        km_error = ret;
+        if (km_error != km::ErrorCode::OK) return;
+        inputConsumed += inputConsumedDelta;
+        consumer(reinterpret_cast<const char*>(&_output[0]), _output.size());
+    };
+
+    while (inputConsumed != inputLen) {
+        size_t toRead = static_cast<size_t>(inputLen - inputConsumed);
+        auto inputBlob = km::support::blob2hidlVec(
+            reinterpret_cast<const uint8_t*>(&input[inputConsumed]), toRead);
+        auto error = mDevice->update(mOpHandle, hidl_vec<km::KeyParameter>(), inputBlob,
+                                     km::HardwareAuthToken(), km::VerificationToken(), hidlCB);
+        if (!error.isOk()) {
+            LOG(ERROR) << "update failed: " << error.description() << std::endl;
+            mDevice = nullptr;
+            return false;
+        }
+        if (km_error != km::ErrorCode::OK) {
+            LOG(ERROR) << "update failed, code " << int32_t(km_error) << std::endl;
+            mDevice = nullptr;
+            return false;
+        }
+        if (inputConsumed > inputLen) {
+            LOG(ERROR) << "update reported too much input consumed" << std::endl;
+            mDevice = nullptr;
+            return false;
+        }
+    }
+    return true;
+}
+
+bool KeymasterOperation::finish(std::string* output) {
+    km::ErrorCode km_error;
+    auto hidlCb = [&](km::ErrorCode ret, const hidl_vec<km::KeyParameter>& /*ignored*/,
+                      const hidl_vec<uint8_t>& _output) {
+        km_error = ret;
+        if (km_error != km::ErrorCode::OK) return;
+        if (output) output->assign(reinterpret_cast<const char*>(&_output[0]), _output.size());
+    };
+    auto error = mDevice->finish(mOpHandle, hidl_vec<km::KeyParameter>(), hidl_vec<uint8_t>(),
+                                 hidl_vec<uint8_t>(), km::HardwareAuthToken(),
+                                 km::VerificationToken(), hidlCb);
+    mDevice = nullptr;
+    if (!error.isOk()) {
+        LOG(ERROR) << "finish failed: " << error.description() << std::endl;
+        return false;
+    }
+    if (km_error != km::ErrorCode::OK) {
+        LOG(ERROR) << "finish failed, code " << int32_t(km_error) << std::endl;
+        return false;
+    }
+    return true;
+}
+
+/* static */ bool Keymaster::hmacKeyGenerated = false;
+
+Keymaster::Keymaster() {
+    auto devices = KmDevice::enumerateAvailableDevices();
+    if (!hmacKeyGenerated) {
+        KmDevice::performHmacKeyAgreement(devices);
+        hmacKeyGenerated = true;
+    }
+    for (auto& dev : devices) {
+        // Do not use StrongBox for device encryption / credential encryption.  If a security chip
+        // is present it will have Weaver, which already strengthens CE.  We get no additional
+        // benefit from using StrongBox here, so skip it.
+        if (dev->halVersion().securityLevel != SecurityLevel::STRONGBOX) {
+            mDevice = std::move(dev);
+            break;
+        }
+    }
+    if (!mDevice) return;
+    auto& version = mDevice->halVersion();
+    LOG(INFO) << "Using " << version.keymasterName << " from " << version.authorName
+              << " for encryption.  Security level: " << toString(version.securityLevel)
+              << ", HAL: " << mDevice->descriptor() << "/" << mDevice->instanceName() << std::endl;
+}
+
+bool Keymaster::generateKey(const km::AuthorizationSet& inParams, std::string* key) {
+    km::ErrorCode km_error;
+    auto hidlCb = [&](km::ErrorCode ret, const hidl_vec<uint8_t>& keyBlob,
+                      const km::KeyCharacteristics& /*ignored*/) {
+        km_error = ret;
+        if (km_error != km::ErrorCode::OK) return;
+        if (key) key->assign(reinterpret_cast<const char*>(&keyBlob[0]), keyBlob.size());
+    };
+
+    auto error = mDevice->generateKey(inParams.hidl_data(), hidlCb);
+    if (!error.isOk()) {
+        LOG(ERROR) << "generate_key failed: " << error.description() << std::endl;
+        return false;
+    }
+    if (km_error != km::ErrorCode::OK) {
+        LOG(ERROR) << "generate_key failed, code " << int32_t(km_error) << std::endl;
+        return false;
+    }
+    return true;
+}
+
+km::ErrorCode Keymaster::exportKey(km::KeyFormat format, KeyBuffer& kmKey, const std::string& clientId,
+                          const std::string& appData, std::string* key) {
+    auto kmKeyBlob = km::support::blob2hidlVec(std::string(kmKey.data(), kmKey.size()));
+    auto emptyAssign = NULL;
+    auto kmClientId = (clientId == "!") ? emptyAssign: km::support::blob2hidlVec(clientId);
+    auto kmAppData = (appData == "!") ? emptyAssign: km::support::blob2hidlVec(appData);
+    km::ErrorCode km_error;
+    auto hidlCb = [&](km::ErrorCode ret, const hidl_vec<uint8_t>& exportedKeyBlob) {
+        km_error = ret;
+        if (km_error != km::ErrorCode::OK) return;
+        if(key)
+            key->assign(reinterpret_cast<const char*>(&exportedKeyBlob[0]),
+                            exportedKeyBlob.size());
+    };
+    auto error = mDevice->exportKey(format, kmKeyBlob, kmClientId, kmAppData, hidlCb);
+    if (!error.isOk()) {
+        LOG(ERROR) << "export_key failed: " << error.description();
+        return km::ErrorCode::UNKNOWN_ERROR;
+    }
+    if (km_error != km::ErrorCode::OK) {
+        LOG(ERROR) << "export_key failed, code " << int32_t(km_error);
+        return km_error;
+    }
+    return km::ErrorCode::OK;
+}
+
+bool Keymaster::deleteKey(const std::string& key) {
+    LOG(ERROR) << "not actually deleting key\n";
+    return true;
+    auto keyBlob = km::support::blob2hidlVec(key);
+    auto error = mDevice->deleteKey(keyBlob);
+    if (!error.isOk()) {
+        LOG(ERROR) << "delete_key failed: " << error.description();
+        return false;
+    }
+    if (error != km::ErrorCode::OK) {
+        LOG(ERROR) << "delete_key failed, code " << int32_t(km::ErrorCode(error));
+        return false;
+    }
+    return true;
+}
+
+bool Keymaster::upgradeKey(const std::string& oldKey, const km::AuthorizationSet& inParams,
+                           std::string* newKey) {
+    auto oldKeyBlob = km::support::blob2hidlVec(oldKey);
+    km::ErrorCode km_error;
+    auto hidlCb = [&](km::ErrorCode ret, const hidl_vec<uint8_t>& upgradedKeyBlob) {
+        km_error = ret;
+        if (km_error != km::ErrorCode::OK) return;
+        if (newKey)
+            newKey->assign(reinterpret_cast<const char*>(&upgradedKeyBlob[0]),
+                           upgradedKeyBlob.size());
+    };
+    auto error = mDevice->upgradeKey(oldKeyBlob, inParams.hidl_data(), hidlCb);
+    if (!error.isOk()) {
+        LOG(ERROR) << "upgrade_key failed: " << error.description() << std::endl;
+        return false;
+    }
+    if (km_error != km::ErrorCode::OK) {
+        LOG(ERROR) << "upgrade_key failed, code " << int32_t(km_error) << std::endl;
+        return false;
+    }
+    return true;
+}
+
+KeymasterOperation Keymaster::begin(km::KeyPurpose purpose, const std::string& key,
+                                    const km::AuthorizationSet& inParams,
+                                    const km::HardwareAuthToken& authToken,
+                                    km::AuthorizationSet* outParams) {
+    auto keyBlob = km::support::blob2hidlVec(key);
+    uint64_t mOpHandle;
+    km::ErrorCode km_error;
+
+    auto hidlCb = [&](km::ErrorCode ret, const hidl_vec<km::KeyParameter>& _outParams,
+                      uint64_t operationHandle) {
+        km_error = ret;
+        if (km_error != km::ErrorCode::OK) return;
+        if (outParams) *outParams = _outParams;
+        mOpHandle = operationHandle;
+    };
+
+    auto error = mDevice->begin(purpose, keyBlob, inParams.hidl_data(), authToken, hidlCb);
+    if (!error.isOk()) {
+        LOG(ERROR) << "begin failed: " << error.description() << std::endl;
+        return KeymasterOperation(km::ErrorCode::UNKNOWN_ERROR);
+    }
+    if (km_error != km::ErrorCode::OK) {
+        LOG(ERROR) << "begin failed, code " << int32_t(km_error) << std::endl;
+        return KeymasterOperation(km_error);
+    }
+    return KeymasterOperation(mDevice.get(), mOpHandle);
+}
+
+bool Keymaster::isSecure() {
+    return mDevice->halVersion().securityLevel != km::SecurityLevel::SOFTWARE;
+}
+
+}  // namespace vold
+}  // namespace android
+
+using namespace ::android::vold;
+
+/*
+int keymaster_compatibility_cryptfs_scrypt() {
+    Keymaster dev;
+    if (!dev) {
+        LOG(ERROR) << "Failed to initiate keymaster session" << std::endl;
+        return -1;
+    }
+    return dev.isSecure();
+}
+*/
+
+static bool write_string_to_buf(const std::string& towrite, uint8_t* buffer, uint32_t buffer_size,
+                                uint32_t* out_size) {
+    if (!buffer || !out_size) {
+        LOG(ERROR) << "Missing target pointers" << std::endl;
+        return false;
+    }
+    *out_size = towrite.size();
+    if (buffer_size < towrite.size()) {
+        LOG(ERROR) << "Buffer too small " << buffer_size << " < " << towrite.size() << std::endl;
+        return false;
+    }
+    memset(buffer, '\0', buffer_size);
+    std::copy(towrite.begin(), towrite.end(), buffer);
+    return true;
+}
+
+static km::AuthorizationSet keyParams(uint32_t rsa_key_size, uint64_t rsa_exponent,
+                                      uint32_t ratelimit) {
+    return km::AuthorizationSetBuilder()
+        .RsaSigningKey(rsa_key_size, rsa_exponent)
+        .NoDigestOrPadding()
+        .Authorization(km::TAG_BLOB_USAGE_REQUIREMENTS, km::KeyBlobUsageRequirements::STANDALONE)
+        .Authorization(km::TAG_NO_AUTH_REQUIRED)
+        .Authorization(km::TAG_MIN_SECONDS_BETWEEN_OPS, ratelimit);
+}
+
+/*
+int keymaster_create_key_for_cryptfs_scrypt(uint32_t rsa_key_size, uint64_t rsa_exponent,
+                                            uint32_t ratelimit, uint8_t* key_buffer,
+                                            uint32_t key_buffer_size, uint32_t* key_out_size) {
+    if (key_out_size) {
+        *key_out_size = 0;
+    }
+    Keymaster dev;
+    if (!dev) {
+        LOG(ERROR) << "Failed to initiate keymaster session" << std::endl;
+        return -1;
+    }
+    std::string key;
+    if (!dev.generateKey(keyParams(rsa_key_size, rsa_exponent, ratelimit), &key)) return -1;
+    if (!write_string_to_buf(key, key_buffer, key_buffer_size, key_out_size)) return -1;
+    return 0;
+}
+*/
+
+int keymaster_upgrade_key_for_cryptfs_scrypt(uint32_t rsa_key_size, uint64_t rsa_exponent,
+                                             uint32_t ratelimit, const uint8_t* key_blob,
+                                             size_t key_blob_size, uint8_t* key_buffer,
+                                             uint32_t key_buffer_size, uint32_t* key_out_size) {
+    if (key_out_size) {
+        *key_out_size = 0;
+    }
+    Keymaster dev;
+    if (!dev) {
+        LOG(ERROR) << "Failed to initiate keymaster session" << std::endl;
+        return -1;
+    }
+    std::string old_key(reinterpret_cast<const char*>(key_blob), key_blob_size);
+    std::string new_key;
+    if (!dev.upgradeKey(old_key, keyParams(rsa_key_size, rsa_exponent, ratelimit), &new_key))
+        return -1;
+    if (!write_string_to_buf(new_key, key_buffer, key_buffer_size, key_out_size)) return -1;
+    return 0;
+}
+
+KeymasterSignResult keymaster_sign_object_for_cryptfs_scrypt(
+    const uint8_t* key_blob, size_t key_blob_size, uint32_t ratelimit, const uint8_t* object,
+    const size_t object_size, uint8_t** signature_buffer, size_t* signature_buffer_size) {
+    Keymaster dev;
+    if (!dev) {
+        LOG(ERROR) << "Failed to initiate keymaster session" << std::endl;
+        return KeymasterSignResult::error;
+    }
+    if (!key_blob || !object || !signature_buffer || !signature_buffer_size) {
+        LOG(ERROR) << __FILE__ << ":" << __LINE__ << ":Invalid argument" << std::endl;
+        return KeymasterSignResult::error;
+    }
+
+    km::AuthorizationSet outParams;
+    std::string key(reinterpret_cast<const char*>(key_blob), key_blob_size);
+    std::string input(reinterpret_cast<const char*>(object), object_size);
+    std::string output;
+    KeymasterOperation op;
+
+    auto paramBuilder = km::AuthorizationSetBuilder().NoDigestOrPadding();
+    while (true) {
+        op = dev.begin(km::KeyPurpose::SIGN, key, paramBuilder, km::HardwareAuthToken(), &outParams);
+        if (op.errorCode() == km::ErrorCode::KEY_RATE_LIMIT_EXCEEDED) {
+            sleep(ratelimit);
+            continue;
+        } else
+            break;
+    }
+
+    if (op.errorCode() == km::ErrorCode::KEY_REQUIRES_UPGRADE) {
+        LOG(ERROR) << "Keymaster key requires upgrade" << std::endl;
+        return KeymasterSignResult::upgrade;
+    }
+
+    if (op.errorCode() != km::ErrorCode::OK) {
+        LOG(ERROR) << "Error starting keymaster signature transaction: " << int32_t(op.errorCode()) << std::endl;
+        return KeymasterSignResult::error;
+    }
+
+    if (!op.updateCompletely(input, &output)) {
+        LOG(ERROR) << "Error sending data to keymaster signature transaction: "
+                   << uint32_t(op.errorCode()) << std::endl;
+        return KeymasterSignResult::error;
+    }
+
+    if (!op.finish(&output)) {
+        LOG(ERROR) << "Error finalizing keymaster signature transaction: "
+                   << int32_t(op.errorCode()) << std::endl;
+        return KeymasterSignResult::error;
+    }
+
+    *signature_buffer = reinterpret_cast<uint8_t*>(malloc(output.size()));
+    if (*signature_buffer == nullptr) {
+        LOG(ERROR) << "Error allocation buffer for keymaster signature" << std::endl;
+        return KeymasterSignResult::error;
+    }
+    *signature_buffer_size = output.size();
+    std::copy(output.data(), output.data() + output.size(), *signature_buffer);
+    return KeymasterSignResult::ok;
+}
diff --git a/crypto/ext4crypt/Keymaster4.h b/crypto/ext4crypt/Keymaster4.h
new file mode 100644
index 0000000..704cc2c
--- /dev/null
+++ b/crypto/ext4crypt/Keymaster4.h
@@ -0,0 +1,162 @@
+/*
+ * 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_TWRP_KEYMASTER_H
+#define ANDROID_TWRP_KEYMASTER_H
+
+#include "KeyBuffer.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include <android-base/macros.h>
+#include <keymasterV4_0/Keymaster.h>
+#include <keymasterV4_0/authorization_set.h>
+
+namespace android {
+namespace vold {
+
+namespace km = ::android::hardware::keymaster::V4_0;
+using KmDevice = km::support::Keymaster;
+
+// C++ wrappers to the Keymaster hidl interface.
+// This is tailored to the needs of KeyStorage, but could be extended to be
+// a more general interface.
+
+// Wrapper for a Keymaster operation handle 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 mError == km::ErrorCode::OK; }
+    km::ErrorCode errorCode() { return mError; }
+    // Call "update" repeatedly until all of the input is consumed, and
+    // concatenate the output. Return true on success.
+    template <class TI, class TO>
+    bool updateCompletely(TI& input, TO* output) {
+        if (output) output->clear();
+        return updateCompletely(input.data(), input.size(), [&](const char* b, size_t n) {
+            if (output) std::copy(b, b + n, std::back_inserter(*output));
+        });
+    }
+
+    // Finish and write the output to this string, unless pointer is null.
+    bool finish(std::string* output);
+    // Move constructor
+    KeymasterOperation(KeymasterOperation&& rhs) { *this = std::move(rhs); }
+    // Construct an object in an error state for error returns
+    KeymasterOperation() : mDevice{nullptr}, mOpHandle{0}, mError{km::ErrorCode::UNKNOWN_ERROR} {}
+    // Move Assignment
+    KeymasterOperation& operator=(KeymasterOperation&& rhs) {
+        mDevice = rhs.mDevice;
+        rhs.mDevice = nullptr;
+
+        mOpHandle = rhs.mOpHandle;
+        rhs.mOpHandle = 0;
+
+        mError = rhs.mError;
+        rhs.mError = km::ErrorCode::UNKNOWN_ERROR;
+
+        return *this;
+    }
+
+  private:
+    KeymasterOperation(KmDevice* d, uint64_t h)
+        : mDevice{d}, mOpHandle{h}, mError{km::ErrorCode::OK} {}
+    KeymasterOperation(km::ErrorCode error) : mDevice{nullptr}, mOpHandle{0}, mError{error} {}
+
+    bool updateCompletely(const char* input, size_t inputLen,
+                          const std::function<void(const char*, size_t)> consumer);
+
+    KmDevice* mDevice;
+    uint64_t mOpHandle;
+    km::ErrorCode mError;
+    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.get() != nullptr; }
+    // Generate a key in the keymaster from the given params.
+    bool generateKey(const km::AuthorizationSet& inParams, std::string* key);
+    // Export a key from keymaster.
+    km::ErrorCode exportKey(km::KeyFormat format, KeyBuffer& kmKey, const std::string& clientId,
+                   const std::string& appData, std::string* key);
+    // If the keymaster supports it, permanently delete a key.
+    bool deleteKey(const std::string& key);
+    // Replace stored key blob in response to KM_ERROR_KEY_REQUIRES_UPGRADE.
+    bool upgradeKey(const std::string& oldKey, const km::AuthorizationSet& inParams,
+                    std::string* newKey);
+    // Begin a new cryptographic operation, collecting output parameters if pointer is non-null
+    KeymasterOperation begin(km::KeyPurpose purpose, const std::string& key,
+                             const km::AuthorizationSet& inParams,
+                             const km::HardwareAuthToken& authToken,
+                             km::AuthorizationSet* outParams);
+    bool isSecure();
+
+  private:
+    std::unique_ptr<KmDevice> mDevice;
+    DISALLOW_COPY_AND_ASSIGN(Keymaster);
+    static bool hmacKeyGenerated;
+};
+
+}  // namespace vold
+}  // namespace android
+
+// FIXME no longer needed now cryptfs is in C++.
+
+/*
+ * The following functions provide C bindings to keymaster services
+ * needed by cryptfs scrypt. The compatibility check checks whether
+ * the keymaster implementation is considered secure, i.e., TEE backed.
+ * The create_key function generates an RSA key for signing.
+ * The sign_object function signes an object with the given keymaster
+ * key.
+ */
+
+/* Return values for keymaster_sign_object_for_cryptfs_scrypt */
+
+enum class KeymasterSignResult {
+    ok = 0,
+    error = -1,
+    upgrade = -2,
+};
+
+//int keymaster_compatibility_cryptfs_scrypt();
+/*int keymaster_create_key_for_cryptfs_scrypt(uint32_t rsa_key_size, uint64_t rsa_exponent,
+                                            uint32_t ratelimit, uint8_t* key_buffer,
+                                            uint32_t key_buffer_size, uint32_t* key_out_size);*/
+
+int keymaster_upgrade_key_for_cryptfs_scrypt(uint32_t rsa_key_size, uint64_t rsa_exponent,
+                                             uint32_t ratelimit, const uint8_t* key_blob,
+                                             size_t key_blob_size, uint8_t* key_buffer,
+                                             uint32_t key_buffer_size, uint32_t* key_out_size);
+
+KeymasterSignResult keymaster_sign_object_for_cryptfs_scrypt(
+    const uint8_t* key_blob, size_t key_blob_size, uint32_t ratelimit, const uint8_t* object,
+    const size_t object_size, uint8_t** signature_buffer, size_t* signature_buffer_size);
+
+#endif
diff --git a/crypto/ext4crypt/MetadataCrypt.cpp b/crypto/ext4crypt/MetadataCrypt.cpp
new file mode 100644
index 0000000..b195fa0
--- /dev/null
+++ b/crypto/ext4crypt/MetadataCrypt.cpp
@@ -0,0 +1,271 @@
+/*
+ * 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 "KeyBuffer.h"
+#include "MetadataCrypt.h"
+
+#include <string>
+#include <thread>
+#include <vector>
+#include <algorithm>
+
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <linux/dm-ioctl.h>
+
+//#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/unique_fd.h>
+#include <cutils/fs.h>
+//#include <fs_mgr.h>
+
+//#include "EncryptInplace.h"
+#include "KeyStorage4.h"
+#include "KeyUtil.h"
+//#include "secontext.h"
+#include "Utils.h"
+//#include "VoldUtil.h"
+
+#include <iostream>
+#define LOG(x) std::cout
+#define PLOG(x) std::cout
+#include <linux/fs.h>
+
+#define DM_CRYPT_BUF_SIZE 4096
+#define TABLE_LOAD_RETRIES 10
+#define DEFAULT_KEY_TARGET_TYPE "default-key"
+
+using android::vold::KeyBuffer;
+
+static const std::string kDmNameUserdata = "userdata";
+
+void get_blkdev_size(int fd, unsigned long* nr_sec) {
+  if ((ioctl(fd, BLKGETSIZE, nr_sec)) == -1) {
+    *nr_sec = 0;
+  }
+}
+
+static const char* kLookup = "0123456789abcdef";
+
+android::status_t StrToHex(const KeyBuffer& str, KeyBuffer& hex) {
+    hex.clear();
+    for (size_t i = 0; i < str.size(); i++) {
+        hex.push_back(kLookup[(str.data()[i] & 0xF0) >> 4]);
+        hex.push_back(kLookup[str.data()[i] & 0x0F]);
+    }
+    return android::OK;
+}
+
+/*static bool mount_via_fs_mgr(const char* mount_point, const char* blk_device) {
+    // fs_mgr_do_mount runs fsck. Use setexeccon to run trusted
+    // partitions in the fsck domain.
+    if (setexeccon(secontextFsck())) {
+        PLOG(ERROR) << "Failed to setexeccon";
+        return false;
+    }
+    auto mount_rc = fs_mgr_do_mount(fstab_default, const_cast<char*>(mount_point),
+                                    const_cast<char*>(blk_device), nullptr);
+    if (setexeccon(nullptr)) {
+        PLOG(ERROR) << "Failed to clear setexeccon";
+        return false;
+    }
+    if (mount_rc != 0) {
+        LOG(ERROR) << "fs_mgr_do_mount failed with rc " << mount_rc;
+        return false;
+    }
+    LOG(DEBUG) << "Mounted " << mount_point;
+    return true;
+}*/
+
+static bool read_key(const std::string& key_dir, bool create_if_absent, KeyBuffer* key) {
+    /*if (!data_rec->key_dir) {
+        LOG(ERROR) << "Failed to get key_dir";
+        return false;
+    }
+    std::string key_dir = data_rec->key_dir;*/
+    auto dir = key_dir + "/key";
+    LOG(DEBUG) << "key_dir/key: " << dir << "\n";
+    /*if (fs_mkdirs(dir.c_str(), 0700)) {
+        PLOG(ERROR) << "Creating directories: " << dir;
+        return false;
+    }*/
+    auto temp = key_dir + "/tmp";
+    if (!android::vold::retrieveKey(create_if_absent, dir, temp, key)) return false;
+    return true;
+}
+
+static KeyBuffer default_key_params(const std::string& real_blkdev, const KeyBuffer& key) {
+    KeyBuffer hex_key;
+    if (/*android::vold::*/StrToHex(key, hex_key) != android::OK) {
+        LOG(ERROR) << "Failed to turn key to hex\n";
+        return KeyBuffer();
+    }
+    auto res = KeyBuffer() + "AES-256-XTS " + hex_key + " " + real_blkdev.c_str() + " 0";
+    return res;
+}
+
+static bool get_number_of_sectors(const std::string& real_blkdev, uint64_t *nr_sec) {
+    android::base::unique_fd dev_fd(TEMP_FAILURE_RETRY(open(
+        real_blkdev.c_str(), O_RDONLY | O_CLOEXEC, 0)));
+    if (dev_fd == -1) {
+        PLOG(ERROR) << "Unable to open " << real_blkdev << " to measure size\n";
+        return false;
+    }
+    unsigned long res;
+    // TODO: should use BLKGETSIZE64
+    get_blkdev_size(dev_fd.get(), &res);
+    if (res == 0) {
+        PLOG(ERROR) << "Unable to measure size of " << real_blkdev << "\n";
+        return false;
+    }
+    *nr_sec = res;
+    return true;
+}
+
+static struct dm_ioctl* dm_ioctl_init(char *buffer, size_t buffer_size,
+                                      const std::string& dm_name) {
+    if (buffer_size < sizeof(dm_ioctl)) {
+        LOG(ERROR) << "dm_ioctl buffer too small\n";
+        return nullptr;
+    }
+
+    memset(buffer, 0, buffer_size);
+    struct dm_ioctl* io = (struct dm_ioctl*) buffer;
+    io->data_size = buffer_size;
+    io->data_start = sizeof(struct dm_ioctl);
+    io->version[0] = 4;
+    io->version[1] = 0;
+    io->version[2] = 0;
+    io->flags = 0;
+    dm_name.copy(io->name, sizeof(io->name));
+    return io;
+}
+
+static bool create_crypto_blk_dev(const std::string& dm_name, uint64_t nr_sec,
+                                  const std::string& target_type, const KeyBuffer& crypt_params,
+                                  std::string* crypto_blkdev) {
+    PLOG(INFO) << "starting create_crypto_blk_dev\n";
+    android::base::unique_fd dm_fd(TEMP_FAILURE_RETRY(open(
+        "/dev/device-mapper", O_RDWR | O_CLOEXEC, 0)));
+    if (dm_fd == -1) {
+        PLOG(ERROR) << "Cannot open device-mapper\n";
+        return false;
+    }
+    alignas(struct dm_ioctl) char buffer[DM_CRYPT_BUF_SIZE];
+    auto io = dm_ioctl_init(buffer, sizeof(buffer), dm_name);
+    if (!io || ioctl(dm_fd.get(), DM_DEV_CREATE, io) != 0) {
+        PLOG(ERROR) << "Cannot create dm-crypt device " << dm_name << "\n";
+        return false;
+    }
+
+    // Get the device status, in particular, the name of its device file
+    io = dm_ioctl_init(buffer, sizeof(buffer), dm_name);
+    if (ioctl(dm_fd.get(), DM_DEV_STATUS, io) != 0) {
+        PLOG(ERROR) << "Cannot retrieve dm-crypt device status " << dm_name << "\n";
+        return false;
+    }
+    *crypto_blkdev = std::string() + "/dev/block/dm-" + std::to_string(
+        (io->dev & 0xff) | ((io->dev >> 12) & 0xfff00));
+
+    io = dm_ioctl_init(buffer, sizeof(buffer), dm_name);
+    size_t paramix = io->data_start + sizeof(struct dm_target_spec);
+    size_t nullix = paramix + crypt_params.size();
+    size_t endix = (nullix + 1 + 7) & 8; // Add room for \0 and align to 8 byte boundary
+
+    if (endix > sizeof(buffer)) {
+        LOG(ERROR) << "crypt_params too big for DM_CRYPT_BUF_SIZE\n";
+        return false;
+    }
+
+    io->target_count = 1;
+    auto tgt = (struct dm_target_spec *) (buffer + io->data_start);
+    tgt->status = 0;
+    tgt->sector_start = 0;
+    tgt->length = nr_sec;
+    target_type.copy(tgt->target_type, sizeof(tgt->target_type));
+    memcpy(buffer + paramix, crypt_params.data(),
+            std::min(crypt_params.size(), sizeof(buffer) - paramix));
+    buffer[nullix] = '\0';
+    tgt->next = endix;
+
+    for (int i = 0; ; i++) {
+        if (ioctl(dm_fd.get(), DM_TABLE_LOAD, io) == 0) {
+            break;
+        }
+        if (i+1 >= TABLE_LOAD_RETRIES) {
+            PLOG(ERROR) << "DM_TABLE_LOAD ioctl failed\n";
+            return false;
+        }
+        PLOG(INFO) << "DM_TABLE_LOAD ioctl failed, retrying\n";
+        usleep(500000);
+    }
+
+    // Resume this device to activate it
+    io = dm_ioctl_init(buffer, sizeof(buffer), dm_name);
+    if (ioctl(dm_fd.get(), DM_DEV_SUSPEND, io)) {
+        PLOG(ERROR) << "Cannot resume dm-crypt device " << dm_name << "\n";
+        return false;
+    }
+    return true;
+}
+
+bool e4crypt_mount_metadata_encrypted(const std::string& mount_point, bool needs_encrypt, const std::string& key_dir, const std::string& blk_device, std::string* crypto_blkdev) {
+    LOG(DEBUG) << "e4crypt_mount_metadata_encrypted: " << mount_point << " " << needs_encrypt << "\n";
+    /*auto encrypted_state = android::base::GetProperty("ro.crypto.state", "");
+    if (encrypted_state != "") {
+        LOG(DEBUG) << "e4crypt_enable_crypto got unexpected starting state: " << encrypted_state;
+        return false;
+    }
+    auto data_rec = fs_mgr_get_entry_for_mount_point(fstab_default, mount_point);
+    if (!data_rec) {
+        LOG(ERROR) << "Failed to get data_rec";
+        return false;
+    }*/
+    KeyBuffer key;
+    if (!read_key(key_dir, needs_encrypt, &key)) return false;
+    uint64_t nr_sec;
+    if (!get_number_of_sectors(blk_device, &nr_sec)) return false;
+    //std::string crypto_blkdev;
+    if (!create_crypto_blk_dev(kDmNameUserdata, nr_sec, DEFAULT_KEY_TARGET_TYPE,
+                               default_key_params(blk_device, key), /*&*/crypto_blkdev))
+        return false;
+    // FIXME handle the corrupt case
+    /*if (needs_encrypt) {
+        LOG(INFO) << "Beginning inplace encryption, nr_sec: " << nr_sec;
+        off64_t size_already_done = 0;
+        auto rc =
+            cryptfs_enable_inplace(const_cast<char*>(crypto_blkdev.c_str()), data_rec->blk_device,
+                                   nr_sec, &size_already_done, nr_sec, 0, false);
+        if (rc != 0) {
+            LOG(ERROR) << "Inplace crypto failed with code: " << rc;
+            return false;
+        }
+        if (static_cast<uint64_t>(size_already_done) != nr_sec) {
+            LOG(ERROR) << "Inplace crypto only got up to sector: " << size_already_done;
+            return false;
+        }
+        LOG(INFO) << "Inplace encryption complete";
+    }
+
+    LOG(DEBUG) << "Mounting metadata-encrypted filesystem:" << mount_point;
+    mount_via_fs_mgr(data_rec->mount_point, crypto_blkdev.c_str());*/
+    LOG(DEBUG) << "crypto block device '" << *crypto_blkdev << "\n";
+    return true;
+}
diff --git a/crypto/ext4crypt/MetadataCrypt.h b/crypto/ext4crypt/MetadataCrypt.h
new file mode 100644
index 0000000..69addf3
--- /dev/null
+++ b/crypto/ext4crypt/MetadataCrypt.h
@@ -0,0 +1,25 @@
+/*
+ * 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 _METADATA_CRYPT_H
+#define _METADATA_CRYPT_H
+
+#include <string>
+__BEGIN_DECLS
+bool e4crypt_mount_metadata_encrypted(const std::string& mount_point, bool needs_encrypt, const std::string& key_dir, const std::string& blk_device, std::string* crypto_blkdev);
+__END_DECLS
+
+#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..9711cef
--- /dev/null
+++ b/crypto/ext4crypt/Utils.cpp
@@ -0,0 +1,307 @@
+/*
+ * 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" << std::endl;
+        abort();
+    }
+    abort();
+    status_t res = 1;//android_fork_execvp(argc, argv, NULL, false, true);
+    if (setexeccon(nullptr)) {
+        LOG(ERROR) << "Failed to setexeccon" << std::endl;
+        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" << std::endl;
+        abort();
+    }
+    FILE* fp = popen(cmd.c_str(), "r");
+    if (setexeccon(nullptr)) {
+        LOG(ERROR) << "Failed to setexeccon" << std::endl;
+        abort();
+    }
+
+    if (!fp) {
+        PLOG(ERROR) << "Failed to popen " << cmd << std::endl;
+        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 << std::endl;
+        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" << std::endl;
+        }
+
+        _exit(1);
+    }
+
+    if (pid == -1) {
+        PLOG(ERROR) << "Failed to exec" << std::endl;
+    }
+
+    free(argv);
+    return pid;
+}
+
+status_t ReadRandomBytes(size_t bytes, std::string& out) {
+    out.resize(bytes);
+    return ReadRandomBytes(bytes, &out[0]);
+}
+
+status_t ReadRandomBytes(size_t bytes, char* buf) {
+    int fd = TEMP_FAILURE_RETRY(open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
+    if (fd == -1) {
+        return -errno;
+    }
+
+    size_t n;
+    while ((n = TEMP_FAILURE_RETRY(read(fd, &buf[0], bytes))) > 0) {
+        bytes -= n;
+        buf += 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);
+}
+
+std::string BuildDataVendorCePath(userid_t userId) {
+    return StringPrintf("%s/vendor_ce/%u", BuildDataPath(nullptr).c_str(), userId);
+}
+
+std::string BuildDataVendorDePath(userid_t userId) {
+    return StringPrintf("%s/vendor_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..253541c
--- /dev/null
+++ b/crypto/ext4crypt/Utils.h
@@ -0,0 +1,76 @@
+/*
+ * 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>
+
+// DISALLOW_COPY_AND_ASSIGN disallows the copy and operator= functions. It goes in the private:
+// declarations in a class.
+#if !defined(DISALLOW_COPY_AND_ASSIGN)
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+    TypeName(const TypeName&) = delete;  \
+    void operator=(const TypeName&) = delete
+#endif
+
+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);
+status_t ReadRandomBytes(size_t bytes, char* buffer);
+
+/* 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 BuildDataVendorCePath(userid_t userid);
+std::string BuildDataVendorDePath(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/Weaver1.cpp b/crypto/ext4crypt/Weaver1.cpp
new file mode 100644
index 0000000..ea357ed
--- /dev/null
+++ b/crypto/ext4crypt/Weaver1.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+/* To the best of my knowledge there is no native implementation for
+ * Weaver so I made this by looking at the IWeaver.h file that gets
+ * compiled by the build system. I took the information from this header
+ * file and looked at keymaster source to get an idea of the proper way
+ * to write the functions.
+ */
+
+#include "Weaver1.h"
+
+//#include <android-base/logging.h>
+//#include <keystore/keymaster_tags.h>
+//#include <keystore/authorization_set.h>
+//#include <keystore/keystore_hidl_support.h>
+
+#include <android/hardware/weaver/1.0/IWeaver.h>
+
+#include <iostream>
+#define ERROR 1
+#define LOG(x) std::cout
+
+using namespace android::hardware::weaver;
+using android::hardware::hidl_string;
+using ::android::hardware::weaver::V1_0::IWeaver;
+using ::android::hardware::weaver::V1_0::WeaverConfig;
+using ::android::hardware::weaver::V1_0::WeaverReadStatus;
+using ::android::hardware::weaver::V1_0::WeaverReadResponse;
+using ::android::hardware::weaver::V1_0::WeaverStatus;
+using ::android::hardware::Return;
+using ::android::sp;
+
+namespace android {
+namespace vold {
+
+Weaver::Weaver() {
+	mDevice = ::android::hardware::weaver::V1_0::IWeaver::getService();
+	GottenConfig = false;
+}
+
+bool Weaver::GetConfig() {
+	if (GottenConfig)
+		return true;
+
+	WeaverStatus status;
+	WeaverConfig cfg;
+
+	bool callbackCalled = false;
+	auto ret = mDevice->getConfig([&](WeaverStatus s, WeaverConfig c) {
+		callbackCalled = true;
+		status = s;
+		cfg = c;
+	});
+	if (ret.isOk() && callbackCalled && status == WeaverStatus::OK) {
+		config = cfg;
+		GottenConfig = true;
+		return true;
+	}
+	return false;
+}
+
+bool Weaver::GetSlots(uint32_t* slots) {
+	if (!GetConfig())
+		return false;
+	*slots = config.slots;
+	return true;
+}
+
+bool Weaver::GetKeySize(uint32_t* keySize) {
+	if (!GetConfig())
+		return false;
+	*keySize = config.keySize;
+	return true;
+}
+
+bool Weaver::GetValueSize(uint32_t* valueSize) {
+	if (!GetConfig())
+		return false;
+	*valueSize = config.valueSize;
+	return true;
+}
+
+// TODO: we should return more information about the status including time delays before the next retry
+bool Weaver::WeaverVerify(const uint32_t slot, const void* weaver_key, std::vector<uint8_t>* payload) {
+	bool callbackCalled = false;
+	WeaverReadStatus status;
+	std::vector<uint8_t> readValue;
+	uint32_t timeout;
+	uint32_t keySize;
+	if (!GetKeySize(&keySize))
+		return false;
+	std::vector<uint8_t> key;
+	key.resize(keySize);
+	uint32_t index = 0;
+	unsigned char* ptr = (unsigned char*)weaver_key;
+	for (index = 0; index < keySize; index++) {
+		key[index] = *ptr;
+		ptr++;
+	}
+	const auto readRet = mDevice->read(slot, key, [&](WeaverReadStatus s, WeaverReadResponse r) {
+		callbackCalled = true;
+		status = s;
+		readValue = r.value;
+		timeout = r.timeout;
+	});
+	if (readRet.isOk() && callbackCalled && status == WeaverReadStatus::OK && timeout == 0) {
+		*payload = readValue;
+		return true;
+	}
+	return false;
+}
+
+}  // namespace vold
+}  // namespace android
diff --git a/crypto/ext4crypt/Weaver1.h b/crypto/ext4crypt/Weaver1.h
new file mode 100644
index 0000000..22f401e
--- /dev/null
+++ b/crypto/ext4crypt/Weaver1.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+/* To the best of my knowledge there is no native implementation for
+ * Weaver so I made this by looking at the IWeaver.h file that gets
+ * compiled by the build system. I took the information from this header
+ * file and looked at keymaster source to get an idea of the proper way
+ * to write the functions.
+ */
+
+#ifndef TWRP_WEAVER_H
+#define TWRP_WEAVER_H
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include <android/hardware/weaver/1.0/IWeaver.h>
+#include "Utils.h"
+
+namespace android {
+namespace vold {
+using ::android::hardware::weaver::V1_0::IWeaver;
+
+// Wrapper for a Weaver device
+class Weaver {
+	public:
+		Weaver();
+		// false if we failed to open the weaver device.
+		explicit operator bool() { return mDevice.get() != nullptr; }
+
+		bool GetSlots(uint32_t* slots);
+		bool GetKeySize(uint32_t* keySize);
+		bool GetValueSize(uint32_t* valueSize);
+		// TODO: we should return more information about the status including time delays before the next retry
+		bool WeaverVerify(const uint32_t slot, const void* weaver_key, std::vector<uint8_t>* payload);
+
+	private:
+		sp<hardware::weaver::V1_0::IWeaver> mDevice;
+		hardware::weaver::V1_0::WeaverConfig config;
+		bool GottenConfig;
+
+		bool GetConfig();
+
+		DISALLOW_COPY_AND_ASSIGN(Weaver);
+};
+
+}  // namespace vold
+}  // namespace android
+
+#endif
diff --git a/crypto/ext4crypt/e4policyget.cpp b/crypto/ext4crypt/e4policyget.cpp
new file mode 100644
index 0000000..05de86f
--- /dev/null
+++ b/crypto/ext4crypt/e4policyget.cpp
@@ -0,0 +1,41 @@
+/*
+ * 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 "ext4crypt_tar.h"
+
+#define EXT4_KEY_DESCRIPTOR_SIZE 8
+#define EXT4_KEY_DESCRIPTOR_SIZE_HEX 17
+
+int main(int argc, char *argv[]) {
+	bool ret = false;
+	if (argc != 2) {
+		printf("Must specify a path\n");
+		return -1;
+	} else  {
+		ext4_encryption_policy eep;
+		if (e4crypt_policy_get_struct(argv[1], &eep, sizeof(eep))) {
+			char policy_hex[EXT4_KEY_DESCRIPTOR_SIZE_HEX];
+			policy_to_hex(eep.master_key_descriptor, policy_hex);
+			printf("%s\n", policy_hex);
+		} else {
+			printf("No policy set\n");
+		}
+	}
+	return 0;
+}
diff --git a/crypto/ext4crypt/ext4_crypt.cpp b/crypto/ext4crypt/ext4_crypt.cpp
new file mode 100644
index 0000000..5a3b4b2
--- /dev/null
+++ b/crypto/ext4crypt/ext4_crypt.cpp
@@ -0,0 +1,207 @@
+/*
+ * 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.
+ */
+
+/* TWRP NOTE: Kanged from system/extras/ext4_utils/ext4_crypt.cpp
+ * because policy_to_hex, e4crypt_policy_set, and e4crypt_policy_get
+ * are not exposed to be used. There was also a bug in e4crypt_policy_get
+ * that may or may not be fixed in the user's local repo:
+ * https://android.googlesource.com/platform/system/extras/+/30b93dd5715abcabd621235733733c0503f9c552
+ */
+
+#include "ext4_crypt.h"
+#include "ext4crypt_tar.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <fcntl.h>
+#include <asm/ioctl.h>
+#include <sys/syscall.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <cutils/properties.h>
+
+#define XATTR_NAME_ENCRYPTION_POLICY "encryption.policy"
+#define EXT4_KEYREF_DELIMITER ((char)'.')
+
+#define EXT4_ENCRYPTION_MODE_AES_256_XTS    1
+#define EXT4_ENCRYPTION_MODE_AES_256_CTS    4
+#define EXT4_ENCRYPTION_MODE_AES_256_HEH    126
+#define EXT4_ENCRYPTION_MODE_PRIVATE        127
+
+static int encryption_mode = EXT4_ENCRYPTION_MODE_PRIVATE;
+
+#define HEX_LOOKUP "0123456789abcdef"
+
+extern "C" void policy_to_hex(const char* policy, char* hex) {
+    for (size_t i = 0, j = 0; i < EXT4_KEY_DESCRIPTOR_SIZE; i++) {
+        hex[j++] = HEX_LOOKUP[(policy[i] & 0xF0) >> 4];
+        hex[j++] = HEX_LOOKUP[policy[i] & 0x0F];
+    }
+    hex[EXT4_KEY_DESCRIPTOR_SIZE_HEX - 1] = '\0';
+}
+
+extern "C" bool e4crypt_policy_set(const char *directory, const char *policy,
+                               size_t policy_length, int contents_encryption_mode) {
+    if (contents_encryption_mode == 0)
+        contents_encryption_mode = encryption_mode;
+    if (policy_length != EXT4_KEY_DESCRIPTOR_SIZE) {
+		printf("policy wrong length\n");
+        LOG(ERROR) << "Policy wrong length: " << policy_length;
+        return false;
+    }
+    int fd = open(directory, O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
+    if (fd == -1) {
+		printf("failed to open %s\n", directory);
+        PLOG(ERROR) << "Failed to open directory " << directory;
+        return false;
+    }
+
+    ext4_encryption_policy eep;
+    eep.version = 0;
+    eep.contents_encryption_mode = contents_encryption_mode;
+    eep.filenames_encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_CTS;
+    eep.flags = 0;
+    memcpy(eep.master_key_descriptor, policy, EXT4_KEY_DESCRIPTOR_SIZE);
+    if (ioctl(fd, EXT4_IOC_SET_ENCRYPTION_POLICY, &eep)) {
+		printf("failed to set policy for '%s' '%s'\n", directory, policy);
+        PLOG(ERROR) << "Failed to set encryption policy for " << directory;
+        close(fd);
+        return false;
+    }
+    close(fd);
+
+    char policy_hex[EXT4_KEY_DESCRIPTOR_SIZE_HEX];
+    policy_to_hex(policy, policy_hex);
+    LOG(INFO) << "Policy for " << directory << " set to " << policy_hex;
+    return true;
+}
+
+extern "C" bool e4crypt_policy_get(const char *directory, char *policy,
+                               size_t policy_length, int contents_encryption_mode) {
+    if (contents_encryption_mode == 0)
+        contents_encryption_mode = encryption_mode;
+    if (policy_length != EXT4_KEY_DESCRIPTOR_SIZE) {
+        LOG(ERROR) << "Policy wrong length: " << policy_length;
+        return false;
+    }
+
+    int fd = open(directory, O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
+    if (fd == -1) {
+        PLOG(ERROR) << "Failed to open directory " << directory;
+        return false;
+    }
+
+    ext4_encryption_policy eep;
+    memset(&eep, 0, sizeof(ext4_encryption_policy));
+    if (ioctl(fd, EXT4_IOC_GET_ENCRYPTION_POLICY, &eep) != 0) {
+        PLOG(ERROR) << "Failed to get encryption policy for " << directory;
+        close(fd);
+        return false;
+    }
+    close(fd);
+
+    if ((eep.version != 0)
+            || (eep.contents_encryption_mode != contents_encryption_mode)
+            || (eep.filenames_encryption_mode != EXT4_ENCRYPTION_MODE_AES_256_CTS)
+            || (eep.flags != 0)) {
+        LOG(ERROR) << "Failed to find matching encryption policy for " << directory;
+        return false;
+    }
+    memcpy(policy, eep.master_key_descriptor, EXT4_KEY_DESCRIPTOR_SIZE);
+
+    return true;
+}
+
+extern "C" void e4crypt_policy_fill_default_struct(ext4_encryption_policy *eep) {
+	eep->version = 0;
+    eep->contents_encryption_mode = encryption_mode;
+    eep->filenames_encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_CTS;
+    eep->flags = 0;
+    memset((void*)&eep->master_key_descriptor[0], 0, EXT4_KEY_DESCRIPTOR_SIZE);
+}
+
+extern "C" bool e4crypt_policy_set_struct(const char *directory, const ext4_encryption_policy *eep) {
+    int fd = open(directory, O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
+    if (fd == -1) {
+		printf("failed to open %s\n", directory);
+        PLOG(ERROR) << "Failed to open directory " << directory;
+        return false;
+    }
+    if (ioctl(fd, EXT4_IOC_SET_ENCRYPTION_POLICY, eep)) {
+		printf("failed to set policy for '%s'\n", directory);
+        PLOG(ERROR) << "Failed to set encryption policy for " << directory;
+        close(fd);
+        return false;
+    }
+    close(fd);
+    return true;
+}
+
+extern "C" bool e4crypt_policy_get_struct(const char *directory, ext4_encryption_policy *eep) {
+    int fd = open(directory, O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
+    if (fd == -1) {
+        printf("Failed to open '%s'\n", directory);
+        PLOG(ERROR) << "Failed to open directory " << directory;
+        return false;
+    }
+    memset(eep, 0, sizeof(ext4_encryption_policy));
+    if (ioctl(fd, EXT4_IOC_GET_ENCRYPTION_POLICY, eep) != 0) {
+        PLOG(ERROR) << "Failed to get encryption policy for " << directory;
+        close(fd);
+        return false;
+    }
+    close(fd);
+    return true;
+}
+
+extern "C" bool e4crypt_set_mode() {
+    const char* mode_file = "/data/unencrypted/mode";
+    struct stat st;
+    if (stat(mode_file, &st) != 0 || st.st_size <= 0) {
+        printf("Invalid encryption mode file %s\n", mode_file);
+        return false;
+    }
+    size_t mode_size = st.st_size;
+    char contents_encryption_mode[mode_size + 1];
+    memset((void*)contents_encryption_mode, 0, mode_size + 1);
+    int fd = open(mode_file, O_RDONLY);
+    if (fd < 0) {
+        printf("error opening '%s': %s\n", mode_file, strerror(errno));
+        return false;
+    }
+    if (read(fd, contents_encryption_mode, mode_size) != mode_size) {
+        printf("read error on '%s': %s\n", mode_file, strerror(errno));
+        close(fd);
+        return false;
+    }
+    close(fd);
+    if (!strcmp(contents_encryption_mode, "software")) {
+        encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_XTS;
+    } else if (!strcmp(contents_encryption_mode, "ice")) {
+        encryption_mode = EXT4_ENCRYPTION_MODE_PRIVATE;
+    } else {
+        printf("Invalid encryption mode '%s'\n", contents_encryption_mode);
+        return false;
+    }
+    printf("set encryption mode to %i\n", encryption_mode);
+    return true;
+}
diff --git a/crypto/ext4crypt/ext4_crypt.h b/crypto/ext4crypt/ext4_crypt.h
new file mode 100644
index 0000000..d410ccf
--- /dev/null
+++ b/crypto/ext4crypt/ext4_crypt.h
@@ -0,0 +1,39 @@
+/*
+ * 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 _EXT4_CRYPT_H_
+#define _EXT4_CRYPT_H_
+
+#include <sys/cdefs.h>
+#include <stdbool.h>
+#include <cutils/multiuser.h>
+
+__BEGIN_DECLS
+
+bool e4crypt_is_native();
+
+int e4crypt_policy_ensure(const char *directory, const char *policy,
+                          size_t policy_length,
+                          const char *contents_encryption_mode,
+                          const char *filenames_encryption_mode);
+
+static const char* e4crypt_unencrypted_folder = "/unencrypted";
+static const char* e4crypt_key_ref = "/unencrypted/ref";
+static const char* e4crypt_key_mode = "/unencrypted/mode";
+
+__END_DECLS
+
+#endif // _EXT4_CRYPT_H_
diff --git a/crypto/ext4crypt/ext4crypt_tar.h b/crypto/ext4crypt/ext4crypt_tar.h
new file mode 100644
index 0000000..c35d115
--- /dev/null
+++ b/crypto/ext4crypt/ext4crypt_tar.h
@@ -0,0 +1,58 @@
+/*
+ * 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 __EXT4CRYPT_TAR_H
+#define __EXT4CRYPT_TAR_H
+
+#include <sys/cdefs.h>
+#include <stdbool.h>
+#include <cutils/multiuser.h>
+
+// ext4enc:TODO Include structure from somewhere sensible
+// MUST be in sync with ext4_crypto.c in kernel
+#define EXT4_KEY_DESCRIPTOR_SIZE 8
+#define EXT4_KEY_DESCRIPTOR_SIZE_HEX 17
+
+// ext4enc:TODO Get value from somewhere sensible
+#define EXT4_IOC_SET_ENCRYPTION_POLICY _IOR('f', 19, struct ext4_encryption_policy)
+#define EXT4_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct ext4_encryption_policy)
+
+__BEGIN_DECLS
+
+struct ext4_encryption_policy {
+    char version;
+    char contents_encryption_mode;
+    char filenames_encryption_mode;
+    char flags;
+    char master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE];
+} __attribute__((__packed__));
+
+bool lookup_ref_key(const char* policy, char* policy_type);
+bool lookup_ref_tar(const char* policy_type, char* policy);
+
+void policy_to_hex(const char* policy, char* hex);
+bool e4crypt_policy_set(const char *directory, const char *policy,
+                               size_t policy_length, int contents_encryption_mode);
+bool e4crypt_policy_get(const char *directory, char *policy,
+                               size_t policy_length, int contents_encryption_mode);
+void e4crypt_policy_fill_default_struct(struct ext4_encryption_policy *eep);
+bool e4crypt_policy_set_struct(const char *directory, const struct ext4_encryption_policy *eep);
+bool e4crypt_policy_get_struct(const char *directory, struct ext4_encryption_policy *eep);
+
+bool e4crypt_set_mode();
+__END_DECLS
+
+#endif
diff --git a/crypto/ext4crypt/keystore_auth.cpp b/crypto/ext4crypt/keystore_auth.cpp
new file mode 100644
index 0000000..40d890f
--- /dev/null
+++ b/crypto/ext4crypt/keystore_auth.cpp
@@ -0,0 +1,107 @@
+/*
+	Copyright 2018 bigbiff/Dees_Troy TeamWin
+	This file is part of TWRP/TeamWin Recovery Project.
+
+	TWRP is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+
+	TWRP is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* The keystore refuses to allow the root user to supply auth tokens, so
+ * we write the auth token to a file in TWRP and run a separate service
+ * (this) that runs as the system user to add the auth token. TWRP waits
+ * for /auth_token to be deleted and also looks for /auth_error to check
+ * for errors. TWRP will error out after a while if /auth_token does not
+ * get deleted. */
+
+#include <stdio.h>
+#include <string>
+
+#ifdef USE_SECURITY_NAMESPACE
+#include <android/security/IKeystoreService.h>
+#else
+#include <keystore/IKeystoreService.h>
+#include <keystore/authorization_set.h>
+#endif
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+
+#include <keystore/keystore.h>
+
+#ifndef LOG_TAG
+#define LOG_TAG "keystore_auth"
+#endif
+
+using namespace android;
+
+void create_error_file() {
+	FILE* error_file = fopen("/auth_error", "wb");
+	if (error_file == NULL) {
+		printf("Failed to open /auth_error\n");
+		ALOGE("Failed to open /auth_error\n");
+		return;
+	}
+	fwrite("1", 1, 1, error_file);
+	fclose(error_file);
+	unlink("/auth_token");
+}
+
+int main() {
+	unlink("/auth_error");
+	FILE* auth_file = fopen("/auth_token", "rb");
+	if (auth_file == NULL) {
+		printf("Failed to open /auth_token\n");
+		ALOGE("Failed to open /auth_token\n");
+		create_error_file();
+		return -1;
+	}
+	// Get the file size
+	fseek(auth_file, 0, SEEK_END);
+	int size = ftell(auth_file);
+	fseek(auth_file, 0, SEEK_SET);
+	uint8_t auth_token[size];
+	fread(auth_token , sizeof(uint8_t), size, auth_file);
+	fclose(auth_file);
+	// First get the keystore service
+	sp<IServiceManager> sm = defaultServiceManager();
+	sp<IBinder> binder = sm->getService(String16("android.security.keystore"));
+#ifdef USE_SECURITY_NAMESPACE
+	sp<security::IKeystoreService> service = interface_cast<security::IKeystoreService>(binder);
+#else
+	sp<IKeystoreService> service = interface_cast<IKeystoreService>(binder);
+#endif
+	if (service == NULL) {
+		printf("error: could not connect to keystore service\n");
+		ALOGE("error: could not connect to keystore service\n");
+		create_error_file();
+		return -2;
+	}
+#ifdef USE_SECURITY_NAMESPACE
+	std::vector<uint8_t> auth_token_vector(&auth_token[0], (&auth_token[0]) + size);
+	int result = 0;
+	auto binder_result = service->addAuthToken(auth_token_vector, &result);
+	if (!binder_result.isOk() || !keystore::KeyStoreServiceReturnCode(result).isOk()) {
+#else
+	::keystore::KeyStoreServiceReturnCode auth_result = service->addAuthToken(auth_token, size);
+	if (!auth_result.isOk()) {
+#endif
+		// The keystore checks the uid of the calling process and will return a permission denied on this operation for user 0
+		printf("keystore error adding auth token\n");
+		ALOGE("keystore error adding auth token\n");
+		create_error_file();
+		return -3;
+	}
+	printf("successfully added auth token to keystore\n");
+	ALOGD("successfully added auth token to keystore\n");
+	unlink("/auth_token");
+	return 0;
+}
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/crypto/fde/Android.mk b/crypto/fde/Android.mk
new file mode 100755
index 0000000..f78697f
--- /dev/null
+++ b/crypto/fde/Android.mk
@@ -0,0 +1,124 @@
+LOCAL_PATH := $(call my-dir)
+ifeq ($(TW_INCLUDE_CRYPTO), true)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libcryptfsfde
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := cryptfs.cpp
+LOCAL_SHARED_LIBRARIES := libcrypto libhardware libcutils libstdc++
+LOCAL_STATIC_LIBRARIES := libscrypttwrp_static
+LOCAL_C_INCLUDES := external/openssl/include $(commands_recovery_local_path)/crypto/scrypt/lib/crypto
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0)
+    LOCAL_C_INCLUDES += bionic external/stlport/stlport
+    LOCAL_SHARED_LIBRARIES += libstlport
+    LOCAL_CPPFLAGS := -std=c++11
+endif
+
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0)
+    #8.0 or higher
+    LOCAL_C_INCLUDES +=  external/boringssl/src/include
+    ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 29; echo $$?),0)
+        LOCAL_SHARED_LIBRARIES += libtwrpfscrypt
+    else
+        LOCAL_SHARED_LIBRARIES += libe4crypt
+    endif
+    LOCAL_SHARED_LIBRARIES += libselinux libc libc++ libbase libcrypto libcutils libkeymaster_messages libhardware libprotobuf-cpp-lite \
+        android.hardware.keymaster@3.0 libkeystore_binder libhidlbase libutils libbinder
+    ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 28; echo $$?),0)
+        #9.0 rules
+        LOCAL_CFLAGS += -Wno-unused-variable -Wno-sign-compare -Wno-unused-parameter -Wno-comment
+        LOCAL_SHARED_LIBRARIES += android.hardware.keymaster@4.0 libkeymaster4support libkeyutils
+        LOCAL_CFLAGS += -DTW_KEYMASTER_MAX_API=4
+    else
+        #8.x rules
+        ifneq ($(wildcard system/core/libkeyutils/Android.bp),)
+            #only present in some 8.0 trees and should be in all 8.1 trees
+            LOCAL_SHARED_LIBRARIES += libkeyutils
+        endif
+        LOCAL_SHARED_LIBRARIES += libsoftkeymaster
+        LOCAL_CFLAGS += -DTW_KEYMASTER_MAX_API=3
+    endif
+else
+    # <= 7.x rules
+    ifneq ($(wildcard hardware/libhardware/include/hardware/keymaster0.h),)
+        LOCAL_C_INCLUDES +=  external/boringssl/src/include
+        LOCAL_CFLAGS += -DTW_KEYMASTER_MAX_API=1
+    else
+        LOCAL_CFLAGS += -DTW_KEYMASTER_MAX_API=0
+    endif
+endif
+ifeq ($(TARGET_HW_DISK_ENCRYPTION),true)
+    ifeq ($(TARGET_CRYPTFS_HW_PATH),)
+        LOCAL_C_INCLUDES += device/qcom/common/cryptfs_hw
+    else
+        LOCAL_C_INCLUDES += $(TARGET_CRYPTFS_HW_PATH)
+    endif
+    LOCAL_SHARED_LIBRARIES += libcryptfs_hw
+    LOCAL_CFLAGS += -DCONFIG_HW_DISK_ENCRYPTION
+endif
+
+include $(BUILD_SHARED_LIBRARY)
+
+
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := twrpdec
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/
+LOCAL_SRC_FILES := main.cpp cryptfs.cpp
+LOCAL_SHARED_LIBRARIES := libcrypto libhardware libcutils libc libstdc++
+LOCAL_C_INCLUDES := external/openssl/include $(commands_recovery_local_path)/crypto/scrypt/lib/crypto
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0)
+    LOCAL_C_INCLUDES += bionic external/stlport/stlport
+    LOCAL_SHARED_LIBRARIES += libstlport
+    LOCAL_CPPFLAGS := -std=c++11
+endif
+
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0)
+    #8.0 or higher
+    LOCAL_C_INCLUDES +=  external/boringssl/src/include
+    ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 29; echo $$?),0)
+        LOCAL_SHARED_LIBRARIES += libtwrpfscrypt
+    else
+        LOCAL_SHARED_LIBRARIES += libe4crypt
+    endif
+    LOCAL_SHARED_LIBRARIES += libselinux libc libc++ libbase libcrypto libcutils libkeymaster_messages libhardware libprotobuf-cpp-lite \
+    android.hardware.keymaster@3.0 libkeystore_binder libhidlbase libutils libbinder
+    ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 28; echo $$?),0)
+        #9.0 rules
+        LOCAL_CFLAGS += -Wno-unused-variable -Wno-sign-compare -Wno-unused-parameter -Wno-comment
+        LOCAL_SHARED_LIBRARIES += android.hardware.keymaster@4.0 libkeymaster4support libkeyutils
+        LOCAL_CFLAGS += -DTW_KEYMASTER_MAX_API=4
+    else
+        #8.x rules
+        ifneq ($(wildcard system/core/libkeyutils/Android.bp),)
+            #only present in some 8.0 trees and should be in all 8.1 trees
+            LOCAL_SHARED_LIBRARIES += libkeyutils
+        endif
+        LOCAL_SHARED_LIBRARIES += libsoftkeymaster
+        LOCAL_CFLAGS += -DTW_KEYMASTER_MAX_API=3
+    endif
+else
+    # <= 7.x rules
+    ifneq ($(wildcard hardware/libhardware/include/hardware/keymaster0.h),)
+        LOCAL_C_INCLUDES +=  external/boringssl/src/include
+        LOCAL_CFLAGS += -DTW_KEYMASTER_MAX_API=1
+    else
+        LOCAL_CFLAGS += -DTW_KEYMASTER_MAX_API=0
+    endif
+endif
+ifeq ($(TARGET_HW_DISK_ENCRYPTION),true)
+    ifeq ($(TARGET_CRYPTFS_HW_PATH),)
+        LOCAL_C_INCLUDES += device/qcom/common/cryptfs_hw
+    else
+        LOCAL_C_INCLUDES += $(TARGET_CRYPTFS_HW_PATH)
+    endif
+    LOCAL_SHARED_LIBRARIES += libcryptfs_hw
+    LOCAL_CFLAGS += -DCONFIG_HW_DISK_ENCRYPTION
+endif
+
+LOCAL_WHOLE_STATIC_LIBRARIES += libscrypttwrp_static
+include $(BUILD_EXECUTABLE)
+
+endif
diff --git a/crypto/fde/cryptfs.cpp b/crypto/fde/cryptfs.cpp
new file mode 100644
index 0000000..7c30eef
--- /dev/null
+++ b/crypto/fde/cryptfs.cpp
@@ -0,0 +1,1767 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+/* TO DO:
+ *   1.  Perhaps keep several copies of the encrypted key, in case something
+ *       goes horribly wrong?
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <linux/dm-ioctl.h>
+#include <libgen.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <openssl/evp.h>
+#include <openssl/sha.h>
+#include <errno.h>
+//#include <ext4_utils/ext4_crypt.h>
+//#include <ext4_utils/ext4_utils.h>
+#include <linux/kdev_t.h>
+//#include <fs_mgr.h>
+#include <time.h>
+#include <math.h>
+//#include <selinux/selinux.h>
+#include "cryptfs.h"
+//#include "secontext.h"
+#define LOG_TAG "Cryptfs"
+//#include "cutils/log.h"
+#include "cutils/properties.h"
+//#include "cutils/android_reboot.h"
+//#include "hardware_legacy/power.h"
+//#include <logwrap/logwrap.h>
+//#include "ScryptParameters.h"
+//#include "VolumeManager.h"
+//#include "VoldUtil.h"
+//#include "Ext4Crypt.h"
+//#include "f2fs_sparseblock.h"
+//#include "EncryptInplace.h"
+//#include "Process.h"
+#if TW_KEYMASTER_MAX_API == 3
+#include "../ext4crypt/Keymaster3.h"
+#endif
+#if TW_KEYMASTER_MAX_API == 4
+#include "../ext4crypt/Keymaster4.h"
+#endif
+#if TW_KEYMASTER_MAX_API == 0
+#include <hardware/keymaster.h>
+#else // so far, all trees that have keymaster >= 1 have keymaster 1 support
+#include <stdbool.h>
+#include <openssl/evp.h>
+#include <openssl/sha.h>
+#include <hardware/keymaster0.h>
+#include <hardware/keymaster1.h>
+#endif
+//#include "android-base/properties.h"
+//#include <bootloader_message/bootloader_message.h>
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+#include <cryptfs_hw.h>
+#endif
+extern "C" {
+#include <crypto_scrypt.h>
+}
+#include <string>
+#include <vector>
+
+#define ALOGE(...) fprintf(stdout, "E:" __VA_ARGS__)
+#define SLOGE(...) fprintf(stdout, "E:" __VA_ARGS__)
+#define SLOGW(...) fprintf(stdout, "W:" __VA_ARGS__)
+#define SLOGI(...) fprintf(stdout, "I:" __VA_ARGS__)
+#define SLOGD(...) fprintf(stdout, "D:" __VA_ARGS__)
+
+#define UNUSED __attribute__((unused))
+
+#define DM_CRYPT_BUF_SIZE 4096
+
+#define HASH_COUNT 2000
+
+#ifndef min /* already defined by windows.h */
+#define min(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+constexpr size_t INTERMEDIATE_KEY_LEN_BYTES = 16;
+constexpr size_t INTERMEDIATE_IV_LEN_BYTES = 16;
+constexpr size_t INTERMEDIATE_BUF_SIZE =
+    (INTERMEDIATE_KEY_LEN_BYTES + INTERMEDIATE_IV_LEN_BYTES);
+
+// SCRYPT_LEN is used by struct crypt_mnt_ftr for its intermediate key.
+static_assert(INTERMEDIATE_BUF_SIZE == SCRYPT_LEN,
+              "Mismatch of intermediate key sizes");
+
+#define KEY_IN_FOOTER  "footer"
+
+#define DEFAULT_HEX_PASSWORD "64656661756c745f70617373776f7264"
+#define DEFAULT_PASSWORD "default_password"
+
+#define CRYPTO_BLOCK_DEVICE "userdata"
+
+#define TABLE_LOAD_RETRIES 10
+
+#define RSA_KEY_SIZE 2048
+#define RSA_KEY_SIZE_BYTES (RSA_KEY_SIZE / 8)
+#define RSA_EXPONENT 0x10001
+#define KEYMASTER_CRYPTFS_RATE_LIMIT 1  // Maximum one try per second
+#define KEY_LEN_BYTES 16
+
+#define RETRY_MOUNT_ATTEMPTS 10
+#define RETRY_MOUNT_DELAY_SECONDS 1
+
+#define CREATE_CRYPTO_BLK_DEV_FLAGS_ALLOW_ENCRYPT_OVERRIDE (1)
+
+static unsigned char saved_master_key[MAX_KEY_LEN];
+static char *saved_mount_point;
+static int  master_key_saved = 0;
+static struct crypt_persist_data *persist_data = NULL;
+
+static int previous_type;
+
+static char key_fname[PROPERTY_VALUE_MAX] = "";
+static char real_blkdev[PROPERTY_VALUE_MAX] = "";
+static char file_system[PROPERTY_VALUE_MAX] = "";
+
+static void get_blkdev_size(int fd, unsigned long *nr_sec)
+{
+  if ( (ioctl(fd, BLKGETSIZE, nr_sec)) == -1) {
+    *nr_sec = 0;
+  }
+}
+
+#if TW_KEYMASTER_MAX_API == 0
+static int keymaster_init(keymaster_device_t **keymaster_dev)
+{
+    int rc;
+
+    const hw_module_t* mod;
+    rc = hw_get_module_by_class(KEYSTORE_HARDWARE_MODULE_ID, NULL, &mod);
+    if (rc) {
+        printf("could not find any keystore module\n");
+        goto out;
+    }
+
+    rc = keymaster_open(mod, keymaster_dev);
+    if (rc) {
+        printf("could not open keymaster device in %s (%s)\n",
+            KEYSTORE_HARDWARE_MODULE_ID, strerror(-rc));
+        goto out;
+    }
+
+    return 0;
+
+out:
+    *keymaster_dev = NULL;
+    return rc;
+}
+#else //TW_KEYMASTER_MAX_API == 0
+static int keymaster_init(keymaster0_device_t **keymaster0_dev,
+                          keymaster1_device_t **keymaster1_dev)
+{
+    int rc;
+
+    const hw_module_t* mod;
+    rc = hw_get_module_by_class(KEYSTORE_HARDWARE_MODULE_ID, NULL, &mod);
+    if (rc) {
+        printf("could not find any keystore module\n");
+        goto err;
+    }
+
+    printf("keymaster module name is %s\n", mod->name);
+    printf("keymaster version is %d\n", mod->module_api_version);
+
+    *keymaster0_dev = NULL;
+    *keymaster1_dev = NULL;
+    if (mod->module_api_version == KEYMASTER_MODULE_API_VERSION_1_0) {
+        printf("Found keymaster1 module, using keymaster1 API.\n");
+        rc = keymaster1_open(mod, keymaster1_dev);
+    } else {
+        printf("Found keymaster0 module, using keymaster0 API.\n");
+        rc = keymaster0_open(mod, keymaster0_dev);
+    }
+
+    if (rc) {
+        printf("could not open keymaster device in %s (%s)\n",
+              KEYSTORE_HARDWARE_MODULE_ID, strerror(-rc));
+        goto err;
+    }
+
+    return 0;
+
+err:
+    *keymaster0_dev = NULL;
+    *keymaster1_dev = NULL;
+    return rc;
+}
+#endif //TW_KEYMASTER_MAX_API == 0
+
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+static int scrypt_keymaster(const char *passwd, const unsigned char *salt,
+                            unsigned char *ikey, void *params);
+static void convert_key_to_hex_ascii(const unsigned char *master_key,
+                                     unsigned int keysize, char *master_key_ascii);
+static int test_mount_hw_encrypted_fs(struct crypt_mnt_ftr* crypt_ftr,
+                const char *passwd, const char *mount_point, const char *label);
+int cryptfs_check_passwd_hw(char *passwd);
+int cryptfs_get_master_key(struct crypt_mnt_ftr* ftr, const char* password,
+                                   unsigned char* master_key);
+
+static void convert_key_to_hex_ascii_for_upgrade(const unsigned char *master_key,
+                                     unsigned int keysize, char *master_key_ascii)
+{
+    unsigned int i, a;
+    unsigned char nibble;
+
+    for (i = 0, a = 0; i < keysize; i++, a += 2) {
+        /* For each byte, write out two ascii hex digits */
+        nibble = (master_key[i] >> 4) & 0xf;
+        master_key_ascii[a] = nibble + (nibble > 9 ? 0x57 : 0x30);
+
+        nibble = master_key[i] & 0xf;
+        master_key_ascii[a + 1] = nibble + (nibble > 9 ? 0x57 : 0x30);
+    }
+
+    /* Add the null termination */
+    master_key_ascii[a] = '\0';
+}
+
+static int get_keymaster_hw_fde_passwd(const char* passwd, unsigned char* newpw,
+                                  unsigned char* salt,
+                                  const struct crypt_mnt_ftr *ftr)
+{
+    /* if newpw updated, return 0
+     * if newpw not updated return -1
+     */
+    int rc = -1;
+
+    if (should_use_keymaster()) {
+        if (scrypt_keymaster(passwd, salt, newpw, (void*)ftr)) {
+            SLOGE("scrypt failed");
+        } else {
+            rc = 0;
+        }
+    }
+
+    return rc;
+}
+
+static int verify_hw_fde_passwd(const char *passwd, struct crypt_mnt_ftr* crypt_ftr)
+{
+    unsigned char newpw[32] = {0};
+    int key_index;
+    SLOGI("starting verify_hw_fde_passwd\n");
+    if (get_keymaster_hw_fde_passwd(passwd, newpw, crypt_ftr->salt, crypt_ftr))
+        key_index = set_hw_device_encryption_key(passwd,
+                                           (char*) crypt_ftr->crypto_type_name);
+    else
+        key_index = set_hw_device_encryption_key((const char*)newpw,
+                                           (char*) crypt_ftr->crypto_type_name);
+    return key_index;
+}
+
+static int verify_and_update_hw_fde_passwd(const char *passwd,
+                                           struct crypt_mnt_ftr* crypt_ftr)
+{
+    char* new_passwd = NULL;
+    unsigned char newpw[32] = {0};
+    int key_index = -1;
+    int passwd_updated = -1;
+    int ascii_passwd_updated = (crypt_ftr->flags & CRYPT_ASCII_PASSWORD_UPDATED);
+
+    key_index = verify_hw_fde_passwd(passwd, crypt_ftr);
+    if (key_index < 0) {
+        ++crypt_ftr->failed_decrypt_count;
+
+        if (ascii_passwd_updated) {
+            SLOGI("Ascii password was updated");
+        } else {
+            /* Code in else part would execute only once:
+             * When device is upgraded from L->M release.
+             * Once upgraded, code flow should never come here.
+             * L release passed actual password in hex, so try with hex
+             * Each nible of passwd was encoded as a byte, so allocate memory
+             * twice of password len plus one more byte for null termination
+             */
+            if (crypt_ftr->crypt_type == CRYPT_TYPE_DEFAULT) {
+                new_passwd = (char*)malloc(strlen(DEFAULT_HEX_PASSWORD) + 1);
+                if (new_passwd == NULL) {
+                    SLOGE("System out of memory. Password verification  incomplete");
+                    goto out;
+                }
+                strlcpy(new_passwd, DEFAULT_HEX_PASSWORD, strlen(DEFAULT_HEX_PASSWORD) + 1);
+            } else {
+                new_passwd = (char*)malloc(strlen(passwd) * 2 + 1);
+                if (new_passwd == NULL) {
+                    SLOGE("System out of memory. Password verification  incomplete");
+                    goto out;
+                }
+                convert_key_to_hex_ascii_for_upgrade((const unsigned char*)passwd,
+                                       strlen(passwd), new_passwd);
+            }
+            key_index = set_hw_device_encryption_key((const char*)new_passwd,
+                                       (char*) crypt_ftr->crypto_type_name);
+            if (key_index >=0) {
+                crypt_ftr->failed_decrypt_count = 0;
+                SLOGI("Hex password verified...will try to update with Ascii value");
+                /* Before updating password, tie that with keymaster to tie with ROT */
+
+                if (get_keymaster_hw_fde_passwd(passwd, newpw,
+                                                crypt_ftr->salt, crypt_ftr)) {
+                    passwd_updated = update_hw_device_encryption_key(new_passwd,
+                                     passwd, (char*)crypt_ftr->crypto_type_name);
+                } else {
+                    passwd_updated = update_hw_device_encryption_key(new_passwd,
+                                     (const char*)newpw, (char*)crypt_ftr->crypto_type_name);
+                }
+
+                if (passwd_updated >= 0) {
+                    crypt_ftr->flags |= CRYPT_ASCII_PASSWORD_UPDATED;
+                    SLOGI("Ascii password recorded and updated");
+                } else {
+                    SLOGI("Passwd verified, could not update...Will try next time");
+                }
+            } else {
+                ++crypt_ftr->failed_decrypt_count;
+            }
+            free(new_passwd);
+        }
+    } else {
+        if (!ascii_passwd_updated)
+            crypt_ftr->flags |= CRYPT_ASCII_PASSWORD_UPDATED;
+    }
+out:
+    // update footer before leaving
+    //put_crypt_ftr_and_key(crypt_ftr);
+    return key_index;
+}
+#endif
+
+void set_partition_data(const char* block_device, const char* key_location, const char* fs)
+{
+  strcpy(key_fname, key_location);
+  strcpy(real_blkdev, block_device);
+  strcpy(file_system, fs);
+}
+
+/* This signs the given object using the keymaster key. */
+static int keymaster_sign_object(struct crypt_mnt_ftr *ftr,
+                                 const unsigned char *object,
+                                 const size_t object_size,
+                                 unsigned char **signature,
+                                 size_t *signature_size)
+{
+    SLOGI("TWRP keymaster max API: %i\n", TW_KEYMASTER_MAX_API);
+    unsigned char to_sign[RSA_KEY_SIZE_BYTES];
+    size_t to_sign_size = sizeof(to_sign);
+    memset(to_sign, 0, RSA_KEY_SIZE_BYTES);
+
+    // To sign a message with RSA, the message must satisfy two
+    // constraints:
+    //
+    // 1. The message, when interpreted as a big-endian numeric value, must
+    //    be strictly less than the public modulus of the RSA key.  Note
+    //    that because the most significant bit of the public modulus is
+    //    guaranteed to be 1 (else it's an (n-1)-bit key, not an n-bit
+    //    key), an n-bit message with most significant bit 0 always
+    //    satisfies this requirement.
+    //
+    // 2. The message must have the same length in bits as the public
+    //    modulus of the RSA key.  This requirement isn't mathematically
+    //    necessary, but is necessary to ensure consistency in
+    //    implementations.
+    switch (ftr->kdf_type) {
+        case KDF_SCRYPT_KEYMASTER_UNPADDED:
+            // This is broken: It produces a message which is shorter than
+            // the public modulus, failing criterion 2.
+            memcpy(to_sign, object, object_size);
+            to_sign_size = object_size;
+            SLOGI("Signing unpadded object\n");
+            break;
+        case KDF_SCRYPT_KEYMASTER_BADLY_PADDED:
+            // This is broken: Since the value of object is uniformly
+            // distributed, it produces a message that is larger than the
+            // public modulus with probability 0.25.
+            memcpy(to_sign, object, min(RSA_KEY_SIZE_BYTES, object_size));
+            SLOGI("Signing end-padded object\n");
+            break;
+        case KDF_SCRYPT_KEYMASTER:
+            // This ensures the most significant byte of the signed message
+            // is zero.  We could have zero-padded to the left instead, but
+            // this approach is slightly more robust against changes in
+            // object size.  However, it's still broken (but not unusably
+            // so) because we really should be using a proper deterministic
+            // RSA padding function, such as PKCS1.
+            memcpy(to_sign + 1, object, min((size_t)RSA_KEY_SIZE_BYTES - 1, object_size));
+            SLOGI("Signing safely-padded object");
+            break;
+        default:
+            SLOGE("Unknown KDF type %d", ftr->kdf_type);
+            return -1;
+    }
+
+    int rc = -1;
+
+#if TW_KEYMASTER_MAX_API >= 1
+    keymaster0_device_t *keymaster0_dev = 0;
+    keymaster1_device_t *keymaster1_dev = 0;
+    if (keymaster_init(&keymaster0_dev, &keymaster1_dev)) {
+#else
+    keymaster_device_t *keymaster0_dev = 0;
+    if (keymaster_init(&keymaster0_dev)) {
+#endif
+        printf("Failed to init keymaster 0/1\n");
+        goto initfail;
+    }
+    if (keymaster0_dev) {
+        keymaster_rsa_sign_params_t params;
+        params.digest_type = DIGEST_NONE;
+        params.padding_type = PADDING_NONE;
+
+        rc = keymaster0_dev->sign_data(keymaster0_dev,
+                                      &params,
+                                      ftr->keymaster_blob,
+                                      ftr->keymaster_blob_size,
+                                      to_sign,
+                                      to_sign_size,
+                                      signature,
+                                      signature_size);
+        goto out;
+    }
+#if TW_KEYMASTER_MAX_API >= 1
+    else if (keymaster1_dev) {
+        keymaster_key_blob_t key = { ftr->keymaster_blob, ftr->keymaster_blob_size };
+        keymaster_key_param_t params[] = {
+            keymaster_param_enum(KM_TAG_PADDING, KM_PAD_NONE),
+            keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_NONE),
+        };
+        keymaster_key_param_set_t param_set = { params, sizeof(params)/sizeof(*params) };
+        keymaster_operation_handle_t op_handle;
+        keymaster_error_t error = keymaster1_dev->begin(keymaster1_dev, KM_PURPOSE_SIGN, &key,
+                                                        &param_set, NULL /* out_params */,
+                                                        &op_handle);
+        if (error == KM_ERROR_KEY_RATE_LIMIT_EXCEEDED) {
+            // Key usage has been rate-limited.  Wait a bit and try again.
+            sleep(KEYMASTER_CRYPTFS_RATE_LIMIT);
+            error = keymaster1_dev->begin(keymaster1_dev, KM_PURPOSE_SIGN, &key,
+                                          &param_set, NULL /* out_params */,
+                                          &op_handle);
+        }
+        if (error != KM_ERROR_OK) {
+            printf("Error starting keymaster signature transaction: %d\n", error);
+            rc = -1;
+            goto out;
+        }
+
+        keymaster_blob_t input = { to_sign, to_sign_size };
+        size_t input_consumed;
+        error = keymaster1_dev->update(keymaster1_dev, op_handle, NULL /* in_params */,
+                                       &input, &input_consumed, NULL /* out_params */,
+                                       NULL /* output */);
+        if (error != KM_ERROR_OK) {
+            printf("Error sending data to keymaster signature transaction: %d\n", error);
+            rc = -1;
+            goto out;
+        }
+        if (input_consumed != to_sign_size) {
+            // This should never happen.  If it does, it's a bug in the keymaster implementation.
+            printf("Keymaster update() did not consume all data.\n");
+            keymaster1_dev->abort(keymaster1_dev, op_handle);
+            rc = -1;
+            goto out;
+        }
+
+        keymaster_blob_t tmp_sig;
+        error = keymaster1_dev->finish(keymaster1_dev, op_handle, NULL /* in_params */,
+                                       NULL /* verify signature */, NULL /* out_params */,
+                                       &tmp_sig);
+        if (error != KM_ERROR_OK) {
+            printf("Error finishing keymaster signature transaction: %d\n", error);
+            rc = -1;
+            goto out;
+        }
+
+        *signature = (uint8_t*)tmp_sig.data;
+        *signature_size = tmp_sig.data_length;
+        rc = 0;
+    }
+#endif // TW_KEYMASTER_API >= 1
+
+    out:
+#if TW_KEYMASTER_MAX_API >= 1
+        if (keymaster1_dev)
+            keymaster1_close(keymaster1_dev);
+#endif
+        if (keymaster0_dev)
+#if TW_KEYMASTER_MAX_API >= 1
+            keymaster0_close(keymaster0_dev);
+#else
+            keymaster_close(keymaster0_dev);
+#endif
+
+        if (rc == 0)
+            return 0; // otherwise we'll try for a newer keymaster API
+
+initfail:
+#if TW_KEYMASTER_MAX_API == 3
+    return keymaster_sign_object_for_cryptfs_scrypt(ftr->keymaster_blob, ftr->keymaster_blob_size,
+            KEYMASTER_CRYPTFS_RATE_LIMIT, to_sign, to_sign_size, signature, signature_size,
+            ftr->keymaster_blob, KEYMASTER_BLOB_SIZE, &ftr->keymaster_blob_size);
+#endif //TW_KEYMASTER_MAX_API == 3
+#if TW_KEYMASTER_MAX_API >= 4
+    for (int c = 1;c <= 20;c++) { // 20 tries are enough for signing keymaster
+        if (c > 2)
+            usleep(5000); // if failed in two tries lets rest
+        auto result = keymaster_sign_object_for_cryptfs_scrypt(
+            ftr->keymaster_blob, ftr->keymaster_blob_size, KEYMASTER_CRYPTFS_RATE_LIMIT, to_sign,
+            to_sign_size, signature, signature_size);
+        switch (result) {
+            case KeymasterSignResult::ok:
+                return 0;
+            case KeymasterSignResult::upgrade:
+                break;
+            default:
+                return -1;
+        }
+        SLOGD("Upgrading key\n");
+        if (keymaster_upgrade_key_for_cryptfs_scrypt(
+                RSA_KEY_SIZE, RSA_EXPONENT, KEYMASTER_CRYPTFS_RATE_LIMIT, ftr->keymaster_blob,
+                ftr->keymaster_blob_size, ftr->keymaster_blob, KEYMASTER_BLOB_SIZE,
+                &ftr->keymaster_blob_size) != 0) {
+            SLOGE("Failed to upgrade key\n");
+            return -1;
+        }
+        /*if (put_crypt_ftr_and_key(ftr) != 0) {
+            SLOGE("Failed to write upgraded key to disk");
+        }*/
+        SLOGD("Key upgraded successfully\n");
+    }
+#endif
+    return -1;
+}
+
+static void ioctl_init(struct dm_ioctl *io, size_t dataSize, const char *name, unsigned flags)
+{
+    memset(io, 0, dataSize);
+    io->data_size = dataSize;
+    io->data_start = sizeof(struct dm_ioctl);
+    io->version[0] = 4;
+    io->version[1] = 0;
+    io->version[2] = 0;
+    io->flags = flags;
+    if (name) {
+        strlcpy(io->name, name, sizeof(io->name));
+    }
+}
+
+namespace {
+
+struct CryptoType;
+
+// Use to get the CryptoType in use on this device.
+const CryptoType &get_crypto_type();
+
+struct CryptoType {
+    // We should only be constructing CryptoTypes as part of
+    // supported_crypto_types[].  We do it via this pseudo-builder pattern,
+    // which isn't pure or fully protected as a concession to being able to
+    // do it all at compile time.  Add new CryptoTypes in
+    // supported_crypto_types[] below.
+    constexpr CryptoType() : CryptoType(nullptr, nullptr, 0xFFFFFFFF) {}
+    constexpr CryptoType set_keysize(uint32_t size) const {
+        return CryptoType(this->property_name, this->crypto_name, size);
+    }
+    constexpr CryptoType set_property_name(const char *property) const {
+        return CryptoType(property, this->crypto_name, this->keysize);
+    }
+    constexpr CryptoType set_crypto_name(const char *crypto) const {
+        return CryptoType(this->property_name, crypto, this->keysize);
+    }
+
+    constexpr const char *get_property_name() const { return property_name; }
+    constexpr const char *get_crypto_name() const { return crypto_name; }
+    constexpr uint32_t get_keysize() const { return keysize; }
+
+ private:
+    const char *property_name;
+    const char *crypto_name;
+    uint32_t keysize;
+
+    constexpr CryptoType(const char *property, const char *crypto,
+                         uint32_t ksize)
+        : property_name(property), crypto_name(crypto), keysize(ksize) {}
+    friend const CryptoType &get_crypto_type();
+    static const CryptoType &get_device_crypto_algorithm();
+};
+
+// We only want to parse this read-only property once.  But we need to wait
+// until the system is initialized before we can read it.  So we use a static
+// scoped within this function to get it only once.
+const CryptoType &get_crypto_type() {
+    static CryptoType crypto_type = CryptoType::get_device_crypto_algorithm();
+    return crypto_type;
+}
+
+constexpr CryptoType default_crypto_type = CryptoType()
+    .set_property_name("AES-128-CBC")
+    .set_crypto_name("aes-cbc-essiv:sha256")
+    .set_keysize(16);
+
+constexpr CryptoType supported_crypto_types[] = {
+    default_crypto_type,
+    CryptoType()
+        .set_property_name("Speck128/128-XTS")
+        .set_crypto_name("speck128-xts-plain64")
+        .set_keysize(32),
+    // Add new CryptoTypes here.  Order is not important.
+};
+
+
+// ---------- START COMPILE-TIME SANITY CHECK BLOCK -------------------------
+// We confirm all supported_crypto_types have a small enough keysize and
+// had both set_property_name() and set_crypto_name() called.
+
+template <typename T, size_t N>
+constexpr size_t array_length(T (&)[N]) { return N; }
+
+constexpr bool indexOutOfBoundsForCryptoTypes(size_t index) {
+    return (index >= array_length(supported_crypto_types));
+}
+
+constexpr bool isValidCryptoType(const CryptoType &crypto_type) {
+    return ((crypto_type.get_property_name() != nullptr) &&
+            (crypto_type.get_crypto_name() != nullptr) &&
+            (crypto_type.get_keysize() <= MAX_KEY_LEN));
+}
+
+// Note in C++11 that constexpr functions can only have a single line.
+// So our code is a bit convoluted (using recursion instead of a loop),
+// but it's asserting at compile time that all of our key lengths are valid.
+constexpr bool validateSupportedCryptoTypes(size_t index) {
+    return indexOutOfBoundsForCryptoTypes(index) ||
+        (isValidCryptoType(supported_crypto_types[index]) &&
+         validateSupportedCryptoTypes(index + 1));
+}
+
+static_assert(validateSupportedCryptoTypes(0),
+              "We have a CryptoType with keysize > MAX_KEY_LEN or which was "
+              "incompletely constructed.");
+//  ---------- END COMPILE-TIME SANITY CHECK BLOCK -------------------------
+
+
+// Don't call this directly, use get_crypto_type(), which caches this result.
+const CryptoType &CryptoType::get_device_crypto_algorithm() {
+    constexpr char CRYPT_ALGO_PROP[] = "ro.crypto.fde_algorithm";
+    char paramstr[PROPERTY_VALUE_MAX];
+
+    property_get(CRYPT_ALGO_PROP, paramstr,
+                 default_crypto_type.get_property_name());
+    for (auto const &ctype : supported_crypto_types) {
+        if (strcmp(paramstr, ctype.get_property_name()) == 0) {
+            return ctype;
+        }
+    }
+    ALOGE("Invalid name (%s) for %s.  Defaulting to %s\n", paramstr,
+          CRYPT_ALGO_PROP, default_crypto_type.get_property_name());
+    return default_crypto_type;
+}
+
+}  // namespace
+
+#define SCRYPT_PROP "ro.crypto.scrypt_params"
+#define SCRYPT_DEFAULTS "15:3:1"
+
+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;
+}
+
+uint32_t cryptfs_get_keysize() {
+    return get_crypto_type().get_keysize();
+}
+
+const char *cryptfs_get_crypto_name() {
+    return get_crypto_type().get_crypto_name();
+}
+
+static int get_crypt_ftr_info(char **metadata_fname, off64_t *off)
+{
+  static int cached_data = 0;
+  static off64_t cached_off = 0;
+  static char cached_metadata_fname[PROPERTY_VALUE_MAX] = "";
+  int fd;
+  //char key_loc[PROPERTY_VALUE_MAX];
+  //char real_blkdev[PROPERTY_VALUE_MAX];
+  int rc = -1;
+
+  if (!cached_data) {
+    //fs_mgr_get_crypt_info(fstab_default, key_loc, real_blkdev, sizeof(key_loc));
+
+    if (!strcmp(key_fname, KEY_IN_FOOTER)) {
+      if ( (fd = open(real_blkdev, O_RDWR|O_CLOEXEC)) < 0) {
+        SLOGE("Cannot open real block device %s\n", real_blkdev);
+        return -1;
+      }
+
+      unsigned long nr_sec = 0;
+      get_blkdev_size(fd, &nr_sec);
+      if (nr_sec != 0) {
+        /* If it's an encrypted Android partition, the last 16 Kbytes contain the
+         * encryption info footer and key, and plenty of bytes to spare for future
+         * growth.
+         */
+        strlcpy(cached_metadata_fname, real_blkdev, sizeof(cached_metadata_fname));
+        cached_off = ((off64_t)nr_sec * 512) - CRYPT_FOOTER_OFFSET;
+        cached_data = 1;
+      } else {
+        SLOGE("Cannot get size of block device %s\n", real_blkdev);
+      }
+      close(fd);
+    } else {
+      strlcpy(cached_metadata_fname, key_fname, sizeof(cached_metadata_fname));
+      cached_off = 0;
+      cached_data = 1;
+    }
+  }
+
+  if (cached_data) {
+    if (metadata_fname) {
+        *metadata_fname = cached_metadata_fname;
+    }
+    if (off) {
+        *off = cached_off;
+    }
+    rc = 0;
+  }
+
+  return rc;
+}
+
+static int get_crypt_ftr_and_key(struct crypt_mnt_ftr *crypt_ftr)
+{
+  int fd;
+  unsigned int cnt;
+  off64_t starting_off;
+  int rc = -1;
+  char *fname = NULL;
+  struct stat statbuf;
+
+  if (get_crypt_ftr_info(&fname, &starting_off)) {
+    SLOGE("Unable to get crypt_ftr_info\n");
+    return -1;
+  }
+  if (fname[0] != '/') {
+    SLOGE("Unexpected value for crypto key location\n");
+    return -1;
+  }
+  if ( (fd = open(fname, O_RDWR|O_CLOEXEC)) < 0) {
+    SLOGE("Cannot open footer file %s for get\n", fname);
+    return -1;
+  }
+
+  /* Make sure it's 16 Kbytes in length */
+  fstat(fd, &statbuf);
+  if (S_ISREG(statbuf.st_mode) && (statbuf.st_size != 0x4000)) {
+    SLOGE("footer file %s is not the expected size!\n", fname);
+    goto errout;
+  }
+
+  /* Seek to the start of the crypt footer */
+  if (lseek64(fd, starting_off, SEEK_SET) == -1) {
+    SLOGE("Cannot seek to real block device footer\n");
+    goto errout;
+  }
+
+  if ( (cnt = read(fd, crypt_ftr, sizeof(struct crypt_mnt_ftr))) != sizeof(struct crypt_mnt_ftr)) {
+    SLOGE("Cannot read real block device footer\n");
+    goto errout;
+  }
+
+  if (crypt_ftr->magic != CRYPT_MNT_MAGIC) {
+    SLOGE("Bad magic for real block device %s\n", fname);
+    goto errout;
+  }
+
+  if (crypt_ftr->major_version != CURRENT_MAJOR_VERSION) {
+    SLOGE("Cannot understand major version %d real block device footer; expected %d\n",
+          crypt_ftr->major_version, CURRENT_MAJOR_VERSION);
+    goto errout;
+  }
+
+  // We risk buffer overflows with oversized keys, so we just reject them.
+  // 0-sized keys are problematic (essentially by-passing encryption), and
+  // AES-CBC key wrapping only works for multiples of 16 bytes.
+  if ((crypt_ftr->keysize == 0) || ((crypt_ftr->keysize % 16) != 0) ||
+      (crypt_ftr->keysize > MAX_KEY_LEN)) {
+    SLOGE("Invalid keysize (%u) for block device %s; Must be non-zero, "
+          "divisible by 16, and <= %d\n", crypt_ftr->keysize, fname,
+          MAX_KEY_LEN);
+    goto errout;
+  }
+
+  if (crypt_ftr->minor_version > CURRENT_MINOR_VERSION) {
+    SLOGW("Warning: crypto footer minor version %d, expected <= %d, continuing...\n",
+          crypt_ftr->minor_version, CURRENT_MINOR_VERSION);
+  }
+
+  /* Success! */
+  rc = 0;
+
+errout:
+  close(fd);
+  return rc;
+}
+
+int cryptfs_check_footer()
+{
+    int rc = -1;
+    struct crypt_mnt_ftr crypt_ftr;
+
+    rc = get_crypt_ftr_and_key(&crypt_ftr);
+
+    return rc;
+}
+
+/* Convert a binary key of specified length into an ascii hex string equivalent,
+ * without the leading 0x and with null termination
+ */
+static void convert_key_to_hex_ascii(const unsigned char *master_key,
+                                     unsigned int keysize, char *master_key_ascii) {
+    unsigned int i, a;
+    unsigned char nibble;
+
+    for (i=0, a=0; i<keysize; i++, a+=2) {
+        /* For each byte, write out two ascii hex digits */
+        nibble = (master_key[i] >> 4) & 0xf;
+        master_key_ascii[a] = nibble + (nibble > 9 ? 0x37 : 0x30);
+
+        nibble = master_key[i] & 0xf;
+        master_key_ascii[a+1] = nibble + (nibble > 9 ? 0x37 : 0x30);
+    }
+
+    /* Add the null termination */
+    master_key_ascii[a] = '\0';
+
+}
+
+static int load_crypto_mapping_table(struct crypt_mnt_ftr *crypt_ftr,
+        const unsigned char *master_key, const char *real_blk_name,
+        const char *name, int fd, const char *extra_params) {
+  alignas(struct dm_ioctl) char buffer[DM_CRYPT_BUF_SIZE];
+  struct dm_ioctl *io;
+  struct dm_target_spec *tgt;
+  char *crypt_params;
+  // We need two ASCII characters to represent each byte, and need space for
+  // the '\0' terminator.
+  char master_key_ascii[MAX_KEY_LEN * 2 + 1];
+  size_t buff_offset;
+  int i;
+
+  io = (struct dm_ioctl *) buffer;
+
+  /* Load the mapping table for this device */
+  tgt = (struct dm_target_spec *) &buffer[sizeof(struct dm_ioctl)];
+
+  ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0);
+  io->target_count = 1;
+  tgt->status = 0;
+  tgt->sector_start = 0;
+  tgt->length = crypt_ftr->fs_size;
+  crypt_params = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec);
+  buff_offset = crypt_params - buffer;
+  SLOGI(
+	"Creating crypto dev \"%s\"; cipher=%s, keysize=%u, real_dev=%s, len=%llu, params=\"%s\"\n",
+	name, crypt_ftr->crypto_type_name, crypt_ftr->keysize, real_blk_name, tgt->length * 512,
+	extra_params);
+
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+  if(is_hw_disk_encryption((char*)crypt_ftr->crypto_type_name)) {
+    strlcpy(tgt->target_type, "req-crypt",DM_MAX_TYPE_NAME);
+    if (is_ice_enabled())
+      convert_key_to_hex_ascii(master_key, sizeof(int), master_key_ascii);
+    else
+      convert_key_to_hex_ascii(master_key, crypt_ftr->keysize, master_key_ascii);
+  }
+  else {
+    convert_key_to_hex_ascii(master_key, crypt_ftr->keysize, master_key_ascii);
+    strlcpy(tgt->target_type, "crypt", DM_MAX_TYPE_NAME);
+  }
+  snprintf(crypt_params, sizeof(buffer) - buff_offset, "%s %s 0 %s 0 %s 0",
+           crypt_ftr->crypto_type_name, master_key_ascii,
+           real_blk_name, extra_params);
+
+  SLOGI("target_type = %s", tgt->target_type);
+  SLOGI("real_blk_name = %s, extra_params = %s", real_blk_name, extra_params);
+#else
+  convert_key_to_hex_ascii(master_key, crypt_ftr->keysize, master_key_ascii);
+  strlcpy(tgt->target_type, "crypt", DM_MAX_TYPE_NAME);
+  snprintf(crypt_params, sizeof(buffer) - buff_offset, "%s %s 0 %s 0 %s",
+           crypt_ftr->crypto_type_name, master_key_ascii, real_blk_name,
+           extra_params);
+#endif
+
+  crypt_params += strlen(crypt_params) + 1;
+  crypt_params = (char *) (((unsigned long)crypt_params + 7) & ~8); /* Align to an 8 byte boundary */
+  tgt->next = crypt_params - buffer;
+
+  for (i = 0; i < TABLE_LOAD_RETRIES; i++) {
+    if (! ioctl(fd, DM_TABLE_LOAD, io)) {
+      break;
+    }
+    usleep(500000);
+  }
+
+  if (i == TABLE_LOAD_RETRIES) {
+    /* We failed to load the table, return an error */
+    return -1;
+  } else {
+    return i + 1;
+  }
+}
+
+static int get_dm_crypt_version(int fd, const char *name,  int *version)
+{
+    char buffer[DM_CRYPT_BUF_SIZE];
+    struct dm_ioctl *io;
+    struct dm_target_versions *v;
+
+    io = (struct dm_ioctl *) buffer;
+
+    ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0);
+
+    if (ioctl(fd, DM_LIST_VERSIONS, io)) {
+        return -1;
+    }
+
+    /* Iterate over the returned versions, looking for name of "crypt".
+     * When found, get and return the version.
+     */
+    v = (struct dm_target_versions *) &buffer[sizeof(struct dm_ioctl)];
+    while (v->next) {
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+        if (! strcmp(v->name, "crypt") || ! strcmp(v->name, "req-crypt")) {
+#else
+        if (! strcmp(v->name, "crypt")) {
+#endif
+            /* We found the crypt driver, return the version, and get out */
+            version[0] = v->version[0];
+            version[1] = v->version[1];
+            version[2] = v->version[2];
+            return 0;
+        }
+        v = (struct dm_target_versions *)(((char *)v) + v->next);
+    }
+
+    return -1;
+}
+
+#ifndef CONFIG_HW_DISK_ENCRYPTION
+static std::string extra_params_as_string(const std::vector<std::string>& extra_params_vec) {
+    if (extra_params_vec.empty()) return "";
+    char temp[10];
+    snprintf(temp, sizeof(temp), "%zd", extra_params_vec.size());
+    std::string extra_params = temp; //std::to_string(extra_params_vec.size());
+    for (const auto& p : extra_params_vec) {
+        extra_params.append(" ");
+        extra_params.append(p);
+    }
+    return extra_params;
+}
+#endif
+
+static int create_crypto_blk_dev(struct crypt_mnt_ftr* crypt_ftr, const unsigned char* master_key,
+                                 const char* real_blk_name, char* crypto_blk_name, const char* name,
+                                 uint32_t flags) {
+    char buffer[DM_CRYPT_BUF_SIZE];
+    struct dm_ioctl* io;
+    unsigned int minor;
+    int fd = 0;
+    int err;
+    int retval = -1;
+    int version[3];
+    int load_count;
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+    char encrypted_state[PROPERTY_VALUE_MAX] = {0};
+    char progress[PROPERTY_VALUE_MAX] = {0};
+    const char *extra_params;
+#else
+    std::vector<std::string> extra_params_vec;
+#endif
+
+    if ((fd = open("/dev/device-mapper", O_RDWR | O_CLOEXEC)) < 0) {
+        SLOGE("Cannot open device-mapper\n");
+        goto errout;
+    }
+
+    io = (struct dm_ioctl*)buffer;
+
+    ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0);
+    err = ioctl(fd, DM_DEV_CREATE, io);
+    if (err) {
+        SLOGE("Cannot create dm-crypt device %s: %s\n", name, strerror(errno));
+        goto errout;
+    }
+
+    /* Get the device status, in particular, the name of it's device file */
+    ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0);
+    if (ioctl(fd, DM_DEV_STATUS, io)) {
+        SLOGE("Cannot retrieve dm-crypt device status\n");
+        goto errout;
+    }
+    minor = (io->dev & 0xff) | ((io->dev >> 12) & 0xfff00);
+    snprintf(crypto_blk_name, MAXPATHLEN, "/dev/block/dm-%u", minor);
+
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+    if(is_hw_disk_encryption((char*)crypt_ftr->crypto_type_name)) {
+      /* Set fde_enabled if either FDE completed or in-progress */
+      property_get("ro.crypto.state", encrypted_state, ""); /* FDE completed */
+      property_get("vold.encrypt_progress", progress, ""); /* FDE in progress */
+      if (!strcmp(encrypted_state, "encrypted") || strcmp(progress, "")) {
+        if (is_ice_enabled()) {
+          if (flags & CREATE_CRYPTO_BLK_DEV_FLAGS_ALLOW_ENCRYPT_OVERRIDE)
+            extra_params = "fde_enabled ice allow_encrypt_override";
+          else
+            extra_params = "fde_enabled ice";
+        } else {
+          if (flags & CREATE_CRYPTO_BLK_DEV_FLAGS_ALLOW_ENCRYPT_OVERRIDE)
+            extra_params = "fde_enabled allow_encrypt_override";
+          else
+            extra_params = "fde_enabled";
+        }
+      } else {
+          if (flags & CREATE_CRYPTO_BLK_DEV_FLAGS_ALLOW_ENCRYPT_OVERRIDE)
+            extra_params = "fde_enabled allow_encrypt_override";
+          else
+            extra_params = "fde_enabled";
+      }
+    } else {
+      extra_params = "";
+      if (! get_dm_crypt_version(fd, name, version)) {
+        /* Support for allow_discards was added in version 1.11.0 */
+        if ((version[0] >= 2) || ((version[0] == 1) && (version[1] >= 11))) {
+          if (flags & CREATE_CRYPTO_BLK_DEV_FLAGS_ALLOW_ENCRYPT_OVERRIDE)
+            extra_params = "2 allow_discards allow_encrypt_override";
+          else
+            extra_params = "1 allow_discards";
+          SLOGI("Enabling support for allow_discards in dmcrypt.\n");
+        }
+      }
+    }
+    load_count = load_crypto_mapping_table(crypt_ftr, master_key, real_blk_name, name, fd,
+                                           extra_params);
+#else
+    if (!get_dm_crypt_version(fd, name, version)) {
+        /* Support for allow_discards was added in version 1.11.0 */
+        if ((version[0] >= 2) || ((version[0] == 1) && (version[1] >= 11))) {
+            extra_params_vec.push_back(std::string("allow_discards")); // Used to be extra_params_vec.emplace_back("allow_discards"); but this won't compile in 5.1 trees
+        }
+    }
+    if (flags & CREATE_CRYPTO_BLK_DEV_FLAGS_ALLOW_ENCRYPT_OVERRIDE) {
+        extra_params_vec.push_back(std::string("allow_encrypt_override")); // Used to be extra_params_vec.emplace_back("allow_encrypt_override"); but this won't compile in 5.1 trees
+    }
+    load_count = load_crypto_mapping_table(crypt_ftr, master_key, real_blk_name, name, fd,
+                                           extra_params_as_string(extra_params_vec).c_str());
+#endif
+    if (load_count < 0) {
+        SLOGE("Cannot load dm-crypt mapping table.\n");
+        goto errout;
+    } else if (load_count > 1) {
+        SLOGI("Took %d tries to load dmcrypt table.\n", load_count);
+    }
+
+    /* Resume this device to activate it */
+    ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0);
+
+    if (ioctl(fd, DM_DEV_SUSPEND, io)) {
+        SLOGE("Cannot resume the dm-crypt device\n");
+        goto errout;
+    }
+
+    /* We made it here with no errors.  Woot! */
+    retval = 0;
+
+errout:
+  close(fd);   /* If fd is <0 from a failed open call, it's safe to just ignore the close error */
+
+  return retval;
+}
+
+int delete_crypto_blk_dev(const char *name)
+{
+  int fd;
+  char buffer[DM_CRYPT_BUF_SIZE];
+  struct dm_ioctl *io;
+  int retval = -1;
+
+  if ((fd = open("/dev/device-mapper", O_RDWR|O_CLOEXEC)) < 0 ) {
+    SLOGE("Cannot open device-mapper\n");
+    goto errout;
+  }
+
+  io = (struct dm_ioctl *) buffer;
+
+  ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0);
+  if (ioctl(fd, DM_DEV_REMOVE, io)) {
+    SLOGE("Cannot remove dm-crypt device\n");
+    goto errout;
+  }
+
+  /* We made it here with no errors.  Woot! */
+  retval = 0;
+
+errout:
+  close(fd);    /* If fd is <0 from a failed open call, it's safe to just ignore the close error */
+
+  return retval;
+
+}
+
+static int pbkdf2(const char *passwd, const unsigned char *salt,
+                  unsigned char *ikey, void *params UNUSED)
+{
+    SLOGI("Using pbkdf2 for cryptfs KDF\n");
+
+    /* Turn the password into a key and IV that can decrypt the master key */
+    return PKCS5_PBKDF2_HMAC_SHA1(passwd, strlen(passwd), salt, SALT_LEN,
+                                  HASH_COUNT, INTERMEDIATE_BUF_SIZE,
+                                  ikey) != 1;
+}
+
+static int scrypt(const char *passwd, const unsigned char *salt,
+                  unsigned char *ikey, void *params)
+{
+    SLOGI("Using scrypt for cryptfs KDF\n");
+
+    struct crypt_mnt_ftr *ftr = (struct crypt_mnt_ftr *) params;
+
+    int N = 1 << ftr->N_factor;
+    int r = 1 << ftr->r_factor;
+    int p = 1 << ftr->p_factor;
+
+    /* Turn the password into a key and IV that can decrypt the master key */
+    crypto_scrypt((const uint8_t*)passwd, strlen(passwd),
+                  salt, SALT_LEN, N, r, p, ikey,
+                  INTERMEDIATE_BUF_SIZE);
+
+   return 0;
+}
+
+static int scrypt_keymaster(const char *passwd, const unsigned char *salt,
+                            unsigned char *ikey, void *params)
+{
+    SLOGI("Using scrypt with keymaster for cryptfs KDF\n");
+
+    int rc;
+    size_t signature_size;
+    unsigned char* signature;
+    struct crypt_mnt_ftr *ftr = (struct crypt_mnt_ftr *) params;
+
+    int N = 1 << ftr->N_factor;
+    int r = 1 << ftr->r_factor;
+    int p = 1 << ftr->p_factor;
+
+    rc = crypto_scrypt((const uint8_t*)passwd, strlen(passwd),
+                       salt, SALT_LEN, N, r, p, ikey,
+                       INTERMEDIATE_BUF_SIZE);
+
+    if (rc) {
+        SLOGE("scrypt failed");
+        return -1;
+    }
+
+    if (keymaster_sign_object(ftr, ikey, INTERMEDIATE_BUF_SIZE,
+                              &signature, &signature_size)) {
+        SLOGE("Keymaster signing failed");
+        return -1;
+    }
+
+    rc = crypto_scrypt(signature, signature_size, salt, SALT_LEN,
+                       N, r, p, ikey, INTERMEDIATE_BUF_SIZE);
+    free(signature);
+
+    if (rc) {
+        SLOGE("scrypt failed");
+        return -1;
+    }
+
+    return 0;
+}
+
+static int decrypt_master_key_aux(const char *passwd, unsigned char *salt,
+                                  const unsigned char *encrypted_master_key,
+                                  size_t keysize,
+                                  unsigned char *decrypted_master_key,
+                                  kdf_func kdf, void *kdf_params,
+                                  unsigned char** intermediate_key,
+                                  size_t* intermediate_key_size)
+{
+  unsigned char ikey[INTERMEDIATE_BUF_SIZE] = { 0 };
+  EVP_CIPHER_CTX d_ctx;
+  int decrypted_len, final_len;
+
+  /* Turn the password into an intermediate key and IV that can decrypt the
+     master key */
+  if (kdf(passwd, salt, ikey, kdf_params)) {
+    SLOGE("kdf failed");
+    return -1;
+  }
+
+  /* Initialize the decryption engine */
+  EVP_CIPHER_CTX_init(&d_ctx);
+  if (! EVP_DecryptInit_ex(&d_ctx, EVP_aes_128_cbc(), NULL, ikey, ikey+INTERMEDIATE_KEY_LEN_BYTES)) {
+    return -1;
+  }
+  EVP_CIPHER_CTX_set_padding(&d_ctx, 0); /* Turn off padding as our data is block aligned */
+  /* Decrypt the master key */
+  if (! EVP_DecryptUpdate(&d_ctx, decrypted_master_key, &decrypted_len,
+                            encrypted_master_key, keysize)) {
+    return -1;
+  }
+  if (! EVP_DecryptFinal_ex(&d_ctx, decrypted_master_key + decrypted_len, &final_len)) {
+    return -1;
+  }
+
+  if (decrypted_len + final_len != static_cast<int>(keysize)) {
+    return -1;
+  }
+
+  /* Copy intermediate key if needed by params */
+  if (intermediate_key && intermediate_key_size) {
+    *intermediate_key = (unsigned char*) malloc(INTERMEDIATE_KEY_LEN_BYTES);
+    if (*intermediate_key) {
+      memcpy(*intermediate_key, ikey, INTERMEDIATE_KEY_LEN_BYTES);
+      *intermediate_key_size = INTERMEDIATE_KEY_LEN_BYTES;
+    }
+  }
+
+  EVP_CIPHER_CTX_cleanup(&d_ctx);
+
+  return 0;
+}
+
+static void get_kdf_func(struct crypt_mnt_ftr *ftr, kdf_func *kdf, void** kdf_params)
+{
+    if (ftr->kdf_type == KDF_SCRYPT_KEYMASTER) {
+        *kdf = scrypt_keymaster;
+        *kdf_params = ftr;
+    } else if (ftr->kdf_type == KDF_SCRYPT) {
+        *kdf = scrypt;
+        *kdf_params = ftr;
+    } else {
+        *kdf = pbkdf2;
+        *kdf_params = NULL;
+    }
+}
+
+static int decrypt_master_key(const char *passwd, unsigned char *decrypted_master_key,
+                              struct crypt_mnt_ftr *crypt_ftr,
+                              unsigned char** intermediate_key,
+                              size_t* intermediate_key_size)
+{
+    kdf_func kdf;
+    void *kdf_params;
+    int ret;
+
+    get_kdf_func(crypt_ftr, &kdf, &kdf_params);
+    ret = decrypt_master_key_aux(passwd, crypt_ftr->salt, crypt_ftr->master_key,
+                                 crypt_ftr->keysize,
+                                 decrypted_master_key, kdf, kdf_params,
+                                 intermediate_key, intermediate_key_size);
+    if (ret != 0) {
+        SLOGW("failure decrypting master key");
+    }
+
+    return ret;
+}
+
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+static int test_mount_hw_encrypted_fs(struct crypt_mnt_ftr* crypt_ftr,
+             const char *passwd, const char *mount_point, const char *label)
+{
+  /* Allocate enough space for a 256 bit key, but we may use less */
+  unsigned char decrypted_master_key[32];
+  char crypto_blkdev[MAXPATHLEN];
+  //char real_blkdev[MAXPATHLEN];
+  unsigned int orig_failed_decrypt_count;
+  int rc = 0;
+
+  SLOGD("crypt_ftr->fs_size = %lld\n", crypt_ftr->fs_size);
+  orig_failed_decrypt_count = crypt_ftr->failed_decrypt_count;
+
+  //fs_mgr_get_crypt_info(fstab_default, 0, real_blkdev, sizeof(real_blkdev));
+
+  int key_index = 0;
+  if(is_hw_disk_encryption((char*)crypt_ftr->crypto_type_name)) {
+    key_index = verify_and_update_hw_fde_passwd(passwd, crypt_ftr);
+    if (key_index < 0) {
+      rc = -1;
+      goto errout;
+    }
+    else {
+      if (is_ice_enabled()) {
+#ifndef CONFIG_HW_DISK_ENCRYPT_PERF
+        if (create_crypto_blk_dev(crypt_ftr, (unsigned char*)&key_index,
+                            real_blkdev, crypto_blkdev, label, 0)) {
+          SLOGE("Error creating decrypted block device");
+          rc = -1;
+          goto errout;
+        }
+#endif
+      } else {
+        if (create_crypto_blk_dev(crypt_ftr, decrypted_master_key,
+                            real_blkdev, crypto_blkdev, label, 0)) {
+          SLOGE("Error creating decrypted block device");
+          rc = -1;
+          goto errout;
+        }
+      }
+    }
+  }
+
+  if (rc == 0) {
+    /* Save the name of the crypto block device
+     * so we can mount it when restarting the framework. */
+#ifdef CONFIG_HW_DISK_ENCRYPT_PERF
+    if (!is_ice_enabled())
+#endif
+    property_set("ro.crypto.fs_crypto_blkdev", crypto_blkdev);
+    master_key_saved = 1;
+  }
+
+ errout:
+  return rc;
+}
+#endif
+
+static int try_mount_multiple_fs(const char *crypto_blkdev,
+                                 const char *mount_point,
+                                 const char *file_system)
+{
+    if (!mount(crypto_blkdev, mount_point, file_system, 0, NULL))
+        return 0;
+    if (strcmp(file_system, "ext4") &&
+        !mount(crypto_blkdev, mount_point, "ext4", 0, NULL))
+        return 0;
+    if (strcmp(file_system, "f2fs") &&
+        !mount(crypto_blkdev, mount_point, "f2fs", 0, NULL))
+        return 0;
+    return 1;
+}
+
+static int test_mount_encrypted_fs(struct crypt_mnt_ftr* crypt_ftr,
+                                   const char *passwd, const char *mount_point, const char *label)
+{
+  unsigned char decrypted_master_key[MAX_KEY_LEN];
+  char crypto_blkdev[MAXPATHLEN];
+  //char real_blkdev[MAXPATHLEN];
+  char tmp_mount_point[64];
+  unsigned int orig_failed_decrypt_count;
+  int rc;
+  int use_keymaster = 0;
+  unsigned char* intermediate_key = 0;
+  size_t intermediate_key_size = 0;
+  int N = 1 << crypt_ftr->N_factor;
+  int r = 1 << crypt_ftr->r_factor;
+  int p = 1 << crypt_ftr->p_factor;
+
+  SLOGD("crypt_ftr->fs_size = %lld\n", crypt_ftr->fs_size);
+  orig_failed_decrypt_count = crypt_ftr->failed_decrypt_count;
+
+  if (! (crypt_ftr->flags & CRYPT_MNT_KEY_UNENCRYPTED) ) {
+    if (decrypt_master_key(passwd, decrypted_master_key, crypt_ftr,
+                           &intermediate_key, &intermediate_key_size)) {
+      SLOGE("Failed to decrypt master key\n");
+      rc = -1;
+      goto errout;
+    }
+  }
+
+  //fs_mgr_get_crypt_info(fstab_default, 0, real_blkdev, sizeof(real_blkdev));
+
+  // Create crypto block device - all (non fatal) code paths
+  // need it
+  if (create_crypto_blk_dev(crypt_ftr, decrypted_master_key, real_blkdev, crypto_blkdev, label, 0)) {
+      SLOGE("Error creating decrypted block device\n");
+      rc = -1;
+      goto errout;
+  }
+
+  /* Work out if the problem is the password or the data */
+  unsigned char scrypted_intermediate_key[sizeof(crypt_ftr->
+                                                 scrypted_intermediate_key)];
+
+  rc = crypto_scrypt(intermediate_key, intermediate_key_size,
+                     crypt_ftr->salt, sizeof(crypt_ftr->salt),
+                     N, r, p, scrypted_intermediate_key,
+                     sizeof(scrypted_intermediate_key));
+
+  // Does the key match the crypto footer?
+  if (rc == 0 && memcmp(scrypted_intermediate_key,
+                        crypt_ftr->scrypted_intermediate_key,
+                        sizeof(scrypted_intermediate_key)) == 0) {
+    SLOGI("Password matches");
+    rc = 0;
+  } else {
+    /* Try mounting the file system anyway, just in case the problem's with
+     * the footer, not the key. */
+    snprintf(tmp_mount_point, sizeof(tmp_mount_point), "%s/tmp_mnt",
+             mount_point);
+    mkdir(tmp_mount_point, 0755);
+    if (try_mount_multiple_fs(crypto_blkdev, tmp_mount_point, file_system)) {
+      SLOGE("Error temp mounting decrypted block device\n");
+      delete_crypto_blk_dev(label);
+
+      rc = -1;
+    } else {
+      /* Success! */
+      SLOGI("Password did not match but decrypted drive mounted - continue");
+      umount(tmp_mount_point);
+      rc = 0;
+    }
+  }
+
+  if (rc == 0) {
+    /* Save the name of the crypto block device
+     * so we can mount it when restarting the framework. */
+    property_set("ro.crypto.fs_crypto_blkdev", crypto_blkdev);
+
+    /* Also save a the master key so we can reencrypted the key
+     * the key when we want to change the password on it. */
+    memcpy(saved_master_key, decrypted_master_key, crypt_ftr->keysize);
+    saved_mount_point = strdup(mount_point);
+    master_key_saved = 1;
+    SLOGD("%s(): Master key saved\n", __FUNCTION__);
+    rc = 0;
+  }
+
+ errout:
+  if (intermediate_key) {
+    memset(intermediate_key, 0, intermediate_key_size);
+    free(intermediate_key);
+  }
+  return rc;
+}
+
+/*
+ * Called by vold when it's asked to mount an encrypted external
+ * storage volume. The incoming partition has no crypto header/footer,
+ * as any metadata is been stored in a separate, small partition.  We
+ * assume it must be using our same crypt type and keysize.
+ *
+ * out_crypto_blkdev must be MAXPATHLEN.
+ */
+int cryptfs_setup_ext_volume(const char* label, const char* real_blkdev,
+        const unsigned char* key, int keysize, char* out_crypto_blkdev) {
+    int fd = open(real_blkdev, O_RDONLY|O_CLOEXEC);
+    if (fd == -1) {
+        SLOGE("Failed to open %s: %s", real_blkdev, strerror(errno));
+        return -1;
+    }
+
+    unsigned long nr_sec = 0;
+    get_blkdev_size(fd, &nr_sec);
+    close(fd);
+
+    if (nr_sec == 0) {
+        SLOGE("Failed to get size of %s: %s", real_blkdev, strerror(errno));
+        return -1;
+    }
+
+    struct crypt_mnt_ftr ext_crypt_ftr;
+    memset(&ext_crypt_ftr, 0, sizeof(ext_crypt_ftr));
+    ext_crypt_ftr.fs_size = nr_sec;
+    ext_crypt_ftr.keysize = cryptfs_get_keysize();
+    strlcpy((char*) ext_crypt_ftr.crypto_type_name, cryptfs_get_crypto_name(),
+            MAX_CRYPTO_TYPE_NAME_LEN);
+    uint32_t flags = 0;
+    /*if (e4crypt_is_native() &&
+        android::base::GetBoolProperty("ro.crypto.allow_encrypt_override", false))
+        flags |= CREATE_CRYPTO_BLK_DEV_FLAGS_ALLOW_ENCRYPT_OVERRIDE;*/
+
+    return create_crypto_blk_dev(&ext_crypt_ftr, key, real_blkdev, out_crypto_blkdev, label, flags);
+}
+
+/*
+ * Called by vold when it's asked to unmount an encrypted external
+ * storage volume.
+ */
+int cryptfs_revert_ext_volume(const char* label) {
+    return delete_crypto_blk_dev(label);
+}
+
+int check_unmounted_and_get_ftr(struct crypt_mnt_ftr* crypt_ftr)
+{
+    char encrypted_state[PROPERTY_VALUE_MAX];
+    property_get("ro.crypto.state", encrypted_state, "");
+    if ( master_key_saved || strcmp(encrypted_state, "encrypted") ) {
+        SLOGE("encrypted fs already validated or not running with encryption,"
+              " aborting");
+        return -1;
+    }
+
+    if (get_crypt_ftr_and_key(crypt_ftr)) {
+        SLOGE("Error getting crypt footer and key");
+        return -1;
+    }
+
+    return 0;
+}
+
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+int cryptfs_check_passwd_hw(const char* passwd)
+{
+    struct crypt_mnt_ftr crypt_ftr;
+    int rc;
+    unsigned char master_key[KEY_LEN_BYTES];
+    /* get key */
+    if (get_crypt_ftr_and_key(&crypt_ftr)) {
+        SLOGE("Error getting crypt footer and key");
+        return -1;
+    }
+
+    /*
+     * in case of manual encryption (from GUI), the encryption is done with
+     * default password
+     */
+    if (crypt_ftr.flags & CRYPT_FORCE_COMPLETE) {
+        /* compare scrypted_intermediate_key with stored scrypted_intermediate_key
+         * which was created with actual password before reboot.
+         */
+        rc = cryptfs_get_master_key(&crypt_ftr, passwd, master_key);
+        if (rc) {
+            SLOGE("password doesn't match");
+            return rc;
+        }
+
+        rc = test_mount_hw_encrypted_fs(&crypt_ftr, DEFAULT_PASSWORD,
+            DATA_MNT_POINT, CRYPTO_BLOCK_DEVICE);
+
+        if (rc) {
+            SLOGE("Default password did not match on reboot encryption");
+            return rc;
+        }
+    } else {
+        rc = test_mount_hw_encrypted_fs(&crypt_ftr, passwd,
+            DATA_MNT_POINT, CRYPTO_BLOCK_DEVICE);
+        SLOGE("test mount returned %i\n", rc);
+    }
+
+    return rc;
+}
+#endif
+
+int cryptfs_check_passwd(const char *passwd)
+{
+    /*if (e4crypt_is_native()) {
+        SLOGE("cryptfs_check_passwd not valid for file encryption");
+        return -1;
+    }*/
+
+    struct crypt_mnt_ftr crypt_ftr;
+    int rc;
+
+    rc = check_unmounted_and_get_ftr(&crypt_ftr);
+    if (rc) {
+        SLOGE("Could not get footer");
+        return rc;
+    }
+
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+    if (is_hw_disk_encryption((char*)crypt_ftr.crypto_type_name))
+        return cryptfs_check_passwd_hw(passwd);
+#endif
+
+    rc = test_mount_encrypted_fs(&crypt_ftr, passwd,
+                                 DATA_MNT_POINT, CRYPTO_BLOCK_DEVICE);
+
+    if (rc) {
+        SLOGE("Password did not match");
+        return rc;
+    }
+
+    if (crypt_ftr.flags & CRYPT_FORCE_COMPLETE) {
+        // Here we have a default actual password but a real password
+        // we must test against the scrypted value
+        // First, we must delete the crypto block device that
+        // test_mount_encrypted_fs leaves behind as a side effect
+        delete_crypto_blk_dev(CRYPTO_BLOCK_DEVICE);
+        rc = test_mount_encrypted_fs(&crypt_ftr, DEFAULT_PASSWORD,
+                                     DATA_MNT_POINT, CRYPTO_BLOCK_DEVICE);
+        if (rc) {
+            SLOGE("Default password did not match on reboot encryption");
+            return rc;
+        }
+    }
+
+    return rc;
+}
+
+int cryptfs_verify_passwd(const char *passwd)
+{
+    struct crypt_mnt_ftr crypt_ftr;
+    unsigned char decrypted_master_key[MAX_KEY_LEN];
+    char encrypted_state[PROPERTY_VALUE_MAX];
+    int rc;
+
+    property_get("ro.crypto.state", encrypted_state, "");
+    if (strcmp(encrypted_state, "encrypted") ) {
+        SLOGE("device not encrypted, aborting");
+        return -2;
+    }
+
+    if (!master_key_saved) {
+        SLOGE("encrypted fs not yet mounted, aborting");
+        return -1;
+    }
+
+    if (!saved_mount_point) {
+        SLOGE("encrypted fs failed to save mount point, aborting");
+        return -1;
+    }
+
+    if (get_crypt_ftr_and_key(&crypt_ftr)) {
+        SLOGE("Error getting crypt footer and key\n");
+        return -1;
+    }
+
+    if (crypt_ftr.flags & CRYPT_MNT_KEY_UNENCRYPTED) {
+        /* If the device has no password, then just say the password is valid */
+        rc = 0;
+    } else {
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+        if(is_hw_disk_encryption((char*)crypt_ftr.crypto_type_name)) {
+            if (verify_hw_fde_passwd(passwd, &crypt_ftr) >= 0)
+              rc = 0;
+            else
+              rc = -1;
+        } else {
+            decrypt_master_key(passwd, decrypted_master_key, &crypt_ftr, 0, 0);
+            if (!memcmp(decrypted_master_key, saved_master_key, crypt_ftr.keysize)) {
+                /* They match, the password is correct */
+                rc = 0;
+            } else {
+              /* If incorrect, sleep for a bit to prevent dictionary attacks */
+                sleep(1);
+                rc = 1;
+            }
+        }
+#else
+        decrypt_master_key(passwd, decrypted_master_key, &crypt_ftr, 0, 0);
+        if (!memcmp(decrypted_master_key, saved_master_key, crypt_ftr.keysize)) {
+            /* They match, the password is correct */
+            rc = 0;
+        } else {
+            /* If incorrect, sleep for a bit to prevent dictionary attacks */
+            sleep(1);
+            rc = 1;
+        }
+#endif
+    }
+
+    return rc;
+}
+
+/* Returns type of the password, default, pattern, pin or password.
+ */
+int cryptfs_get_password_type(void)
+{
+    struct crypt_mnt_ftr crypt_ftr;
+
+    if (get_crypt_ftr_and_key(&crypt_ftr)) {
+        SLOGE("Error getting crypt footer and key\n");
+        return -1;
+    }
+
+    if (crypt_ftr.flags & CRYPT_INCONSISTENT_STATE) {
+        return -1;
+    }
+
+    return crypt_ftr.crypt_type;
+}
+
+int cryptfs_get_master_key(struct crypt_mnt_ftr* ftr, const char* password,
+                           unsigned char* master_key)
+{
+    int rc;
+
+    unsigned char* intermediate_key = 0;
+    size_t intermediate_key_size = 0;
+
+    if (password == 0 || *password == 0) {
+        password = DEFAULT_PASSWORD;
+    }
+
+    rc = decrypt_master_key(password, master_key, ftr, &intermediate_key,
+                            &intermediate_key_size);
+
+    if (rc) {
+        SLOGE("Can't calculate intermediate key");
+        return rc;
+    }
+
+    int N = 1 << ftr->N_factor;
+    int r = 1 << ftr->r_factor;
+    int p = 1 << ftr->p_factor;
+
+    unsigned char scrypted_intermediate_key[sizeof(ftr->scrypted_intermediate_key)];
+
+    rc = crypto_scrypt(intermediate_key, intermediate_key_size,
+                       ftr->salt, sizeof(ftr->salt), N, r, p,
+                       scrypted_intermediate_key,
+                       sizeof(scrypted_intermediate_key));
+
+    free(intermediate_key);
+
+    if (rc) {
+        SLOGE("Can't scrypt intermediate key");
+        return rc;
+    }
+
+    return memcmp(scrypted_intermediate_key, ftr->scrypted_intermediate_key,
+                  intermediate_key_size);
+}
+
diff --git a/crypto/fde/cryptfs.h b/crypto/fde/cryptfs.h
new file mode 100644
index 0000000..c67113a
--- /dev/null
+++ b/crypto/fde/cryptfs.h
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2010 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_CRYPTFS_H
+#define ANDROID_VOLD_CRYPTFS_H
+
+/* This structure starts 16,384 bytes before the end of a hardware
+ * partition that is encrypted, or in a separate partition.  It's location
+ * is specified by a property set in init.<device>.rc.
+ * The structure allocates 48 bytes for a key, but the real key size is
+ * specified in the struct.  Currently, the code is hardcoded to use 128
+ * bit keys.
+ * The fields after salt are only valid in rev 1.1 and later stuctures.
+ * Obviously, the filesystem does not include the last 16 kbytes
+ * of the partition if the crypt_mnt_ftr lives at the end of the
+ * partition.
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <cutils/properties.h>
+
+/* The current cryptfs version */
+#define CURRENT_MAJOR_VERSION 1
+#define CURRENT_MINOR_VERSION 3
+
+#define CRYPT_FOOTER_OFFSET 0x4000
+#define CRYPT_FOOTER_TO_PERSIST_OFFSET 0x1000
+#define CRYPT_PERSIST_DATA_SIZE 0x1000
+
+#define MAX_CRYPTO_TYPE_NAME_LEN 64
+
+#define MAX_KEY_LEN 48