Merge tag 'android-12.0.0_r7' into android-11
Android 12.0.0 release 7
Change-Id: Idbc61c74c1f983c7e8c7c9269059997eefefdc4d
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 52de770..87915f2
--- a/Android.bp
+++ b/Android.bp
@@ -1,16 +1,6 @@
-// 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",
+]
// *** THIS PACKAGE HAS SPECIAL LICENSING CONDITIONS. PLEASE
// CONSULT THE OWNERS AND opensource-licensing@google.com BEFORE
@@ -48,90 +38,95 @@
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",
+ "soong/makevars.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",
- "update_metadata-protos",
+ "libvintf",
+ "libhidl-gen-utils",
+ "librecovery_utils",
+ "libc++fs"
],
-}
-
-cc_library_static {
- name: "librecovery",
- recovery_available: true,
-
- defaults: [
- "librecovery_defaults",
- ],
-
- srcs: [
- "recovery.cpp",
- ],
-
- shared_libs: [
- "librecovery_ui",
- ],
+ required: [
+ "init_recovery.rc",
+ "ueventd.rc.recovery",
+ "libdl_android.bootstrap",
+ "ziptool.recovery"
+ ]
}
prebuilt_etc {
@@ -141,98 +136,3 @@
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: [
- "librecovery_utils",
- ],
-
- init_rc: [
- "recovery-persist.rc",
- ],
-}
-
-// 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 96af417..302b143
--- a/Android.mk
+++ b/Android.mk
@@ -13,70 +13,687 @@
# 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)
+
+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 \
+ kernel_module_loader.cpp \
+ partition.cpp \
+ partitionmanager.cpp \
+ progresstracking.cpp \
+ startupArgs.cpp \
+ twrp-functions.cpp \
+ twrpDigestDriver.cpp \
+ openrecoveryscript.cpp \
+ tarWrite.c \
+ twrpAdbBuFifo.cpp \
+ twrpRepacker.cpp
+
+ifeq ($(TW_EXCLUDE_APEX),)
+ LOCAL_SRC_FILES += twrpApex.cpp
+else
+ LOCAL_CFLAGS += -DTW_EXCLUDE_APEX
+endif
+
+LOCAL_STATIC_LIBRARIES += libavb libtwrpinstall libminadbd_services libinit libsnapshot_nobinder
+LOCAL_SHARED_LIBRARIES += libfs_mgr libhardware android.hardware.boot@1.0 android.hardware.boot@1.1 libprotobuf-cpp-lite liblp libutils libhidlbase
+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 \
+
+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/extras \
+ system/core/adb \
+ system/core/libmodprobe/include \
+ system/core/libsparse \
+ external/zlib \
+ system/core/libziparchive/include \
+ external/freetype/include \
+ external/boringssl/include \
+ external/libcxx/include \
+ external/libselinux/include \
+ external/libpng \
+ $(LOCAL_PATH)/gui/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 \
+ $(LOCAL_PATH)/libpixelflinger/include \
+ $(LOCAL_PATH)/minuitwrp/include \
+ $(LOCAL_PATH)/twinstall/include
-# librecovery_ui_ext (shared library)
-# ===================================
-include $(CLEAR_VARS)
+LOCAL_STATIC_LIBRARIES += libguitwrp libmodprobe
+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 libdl_android.bootstrap
-LOCAL_MODULE := librecovery_ui_ext
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-MIT SPDX-license-identifier-OFL
-LOCAL_LICENSE_CONDITIONS := by_exception_only notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/NOTICE
+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
+ TW_EXCLUDE_TZDATA := true
+ TW_EXCLUDE_NANO := true
+ TW_EXCLUDE_BASH := 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
+ TWRP_REQUIRED_MODULES += libhardware android.hardware.boot@1.0-service android.hardware.boot@1.0-service.rc \
+ android.hardware.boot@1.1-service android.hardware.boot@1.1-service.rc android.hardware.boot@1.1.xml
+endif
+
+ifeq ($(PRODUCT_USE_DYNAMIC_PARTITIONS),true)
+ LOCAL_CFLAGS += -DPRODUCT_USE_DYNAMIC_PARTITIONS=1
+ TWRP_REQUIRED_MODULES += android.hardware.health@2.1-service android.hardware.health@2.1-impl.recovery android.hardware.health@2.1-service.rc android.hardware.health@2.1.xml
+ TWRP_REQUIRED_MODULES += android.hardware.health@2.0-service android.hardware.health@2.0-impl.recovery android.hardware.health@2.0-service.rc
+ ifeq ($(TW_EXCLUDE_LPDUMP),)
+ TWRP_REQUIRED_MODULES += lpdump lpdumpd.rc
+ endif
+endif
+
+ifeq ($(TW_USES_VENDOR_LIBS),true)
+ LOCAL_CFLAGS += -DUSE_VENDOR_LIBS=1
+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
+ifeq ($(BOARD_USES_RECOVERY_AS_BOOT), true)
+ LOCAL_CFLAGS += -DBOARD_USES_RECOVERY_AS_BOOT
+endif
+ifeq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE), true)
+ LOCAL_CFLAGS += -DBOARD_BUILD_SYSTEM_ROOT_IMAGE
+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
+ifneq ($(TW_ADDITIONAL_APEX_FILES),)
+ LOCAL_CFLAGS += -DTW_ADDITIONAL_APEX_FILES=$(TW_ADDITIONAL_APEX_FILES)
+endif
+ifneq ($(TW_LOAD_VENDOR_MODULES),)
+ LOCAL_CFLAGS += -DTW_LOAD_VENDOR_MODULES=$(TW_LOAD_VENDOR_MODULES)
+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 libstatssocket.recovery
+ LOCAL_C_INCLUDES += external/boringssl/src/include bootable/recovery/crypto
+ LOCAL_C_INCLUDES += $(commands_TWRP_local_path)/crypto/fscrypt
+ 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),)
+ 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
+ifeq ($(TW_EXCLUDE_NANO), true)
+ LOCAL_CFLAGS += -DTW_EXCLUDE_NANO
+endif
+
+LOCAL_C_INCLUDES += system/vold \
+
+TWRP_REQUIRED_MODULES += \
+ relink_libraries \
+ relink_binaries \
+ twrp_ramdisk \
+ bc \
+ 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 \
+ adbd_system_api_recovery \
+ me.twrp.twrpapp.apk \
+ privapp-permissions-twrpapp.xml \
+ adbd_system_api_recovery \
+ libsync.recovery \
+ libmodprobe
+
+ifneq ($(TW_EXCLUDE_TZDATA), true)
+TWRP_REQUIRED_MODULES += \
+ tzdata_twrp
+endif
+
+ifneq ($(TW_EXCLUDE_NANO), true)
+TWRP_REQUIRED_MODULES += \
+ nano_twrp \
+ nano.rc
+endif
+
+ifneq ($(TW_EXCLUDE_BASH), true)
+ ifneq ($(wildcard external/bash/.),)
+ TWRP_REQUIRED_MODULES += \
+ bash_twrp
+ endif
+endif
+
+ifeq ($(TW_INCLUDE_REPACKTOOLS), true)
+TWRP_REQUIRED_MODULES += \
+ magiskboot
+endif
+
+ifeq ($(TW_INCLUDE_RESETPROP), true)
+TWRP_REQUIRED_MODULES += \
+ resetprop
+endif
+
+TWRP_REQUIRED_MODULES += \
+ hwservicemanager \
+ hwservicemanager.rc \
+ vndservicemanager \
+ vndservicemanager.rc
+
+ifneq ($(TW_INCLUDE_CRYPTO),)
+TWRP_REQUIRED_MODULES += \
+ 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
+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_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-MIT SPDX-license-identifier-OFL
-LOCAL_LICENSE_CONDITIONS := by_exception_only notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/NOTICE
+LOCAL_MODULE := file_contexts_text
+LOCAL_MODULE_TAGS := optional
+LOCAL_REQUIRED_MODULES := file_contexts.bin
-ifeq ($(TARGET_USERIMAGES_USE_F2FS),true)
-LOCAL_REQUIRED_MODULES += \
- make_f2fs.recovery \
- sload_f2fs.recovery
-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 5ab19d1..820a8b7
--- a/README.md
+++ b/README.md
@@ -1,159 +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`.
-
-Localization of the background texts
-------------------------------------
-
-The recovery image supports localization of several background texts, e.g. installing, error,
-factory reset warnings, etc. For devices using `xxhdpi` and `xxxhdpi`, the build system generates
-these localization images dynamically since android-10 when building the recovery image. While
-the static images under res-*dpi/images/ is used for other display resolutions and as a
-backup.
-
-Check the invocation of the image_generator tool in the [makefile]. And the detailed usage of the
-image_generator is documented [here](./tools/image_generator/README.md).
-
-[makefile]: https://android.googlesource.com/platform/build/+/refs/heads/master/core/Makefile#1800
+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/fde/Android.mk b/crypto/fde/Android.mk
new file mode 100755
index 0000000..faf7da8
--- /dev/null
+++ b/crypto/fde/Android.mk
@@ -0,0 +1,126 @@
+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
+
+LOCAL_C_INCLUDES += $(commands_TWRP_local_path)/crypto/fscrypt
+
+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..fa0dc78
--- /dev/null
+++ b/crypto/fde/cryptfs.cpp
@@ -0,0 +1,1848 @@
+/*
+ * 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"
+#include "Keymaster.h"
+#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>
+#include <hardware/keymaster2.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,
+ keymaster2_device_t **keymaster2_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;
+ *keymaster2_dev = NULL;
+ if (mod->module_api_version == KEYMASTER_MODULE_API_VERSION_2_0) {
+ printf("Found keymaster2 module, using keymaster2 API.\n");
+ rc = keymaster2_open(mod, keymaster2_dev);
+ } else 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;
+ *keymaster2_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;
+ keymaster2_device_t *keymaster2_dev = 0;
+ if (keymaster_init(&keymaster0_dev, &keymaster1_dev, &keymaster2_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,
+ ¶ms,
+ 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,
+ ¶m_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,
+ ¶m_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;
+ }
+ else if (keymaster2_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_key_param_t config_params[] = {
+ // Set these to crazy values so we don't need to synchronize
+ // the recovery with system updates.
+ // key upgrades will be required; it will be upgraded in-memory
+ keymaster_param_int(KM_TAG_OS_VERSION, 999999),
+ keymaster_param_int(KM_TAG_OS_PATCHLEVEL, 209912),
+ };
+ keymaster_key_param_set_t config_param_set = { config_params, sizeof(config_params)/sizeof(*config_params) };
+ keymaster2_dev->configure(keymaster2_dev, &config_param_set);
+ keymaster_error_t error = keymaster2_dev->begin(keymaster2_dev, KM_PURPOSE_SIGN, &key,
+ ¶m_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 = keymaster2_dev->begin(keymaster2_dev, KM_PURPOSE_SIGN, &key,
+ ¶m_set, NULL /* out_params */,
+ &op_handle);
+ }
+
+ if (error == KM_ERROR_KEY_REQUIRES_UPGRADE) {
+ // Upgrade key in-memory if required
+ // Do not actually write it back; just keep it in memory
+ const keymaster_key_blob_t key_to_upd = key;
+ keymaster2_dev->upgrade_key(keymaster2_dev, &key_to_upd, &config_param_set, &key);
+ error = keymaster2_dev->begin(keymaster2_dev, KM_PURPOSE_SIGN, &key,
+ ¶m_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 = keymaster2_dev->update(keymaster2_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");
+ keymaster2_dev->abort(keymaster2_dev, op_handle);
+ rc = -1;
+ goto out;
+ }
+
+ keymaster_blob_t tmp_sig;
+ error = keymaster2_dev->finish(keymaster2_dev, op_handle, NULL /* in_params */,
+ NULL, 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("fde::get_crypt_ftr_and_key::Unexpected value for crypto key location: %s\n", fname);
+ 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++) {
+ int ret = ioctl(fd, DM_TABLE_LOAD, io);
+ if (!ret) {
+ SLOGI("ioctl err: %d", ret);
+ 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
+#define SALT_LEN 16
+#define SCRYPT_LEN 32
+
+/* definitions of flags in the structure below */
+#define CRYPT_MNT_KEY_UNENCRYPTED 0x1 /* The key for the partition is not encrypted. */
+#define CRYPT_ENCRYPTION_IN_PROGRESS 0x2 /* Encryption partially completed,
+ encrypted_upto valid*/
+#define CRYPT_INCONSISTENT_STATE 0x4 /* Set when starting encryption, clear when
+ exit cleanly, either through success or
+ correctly marked partial encryption */
+#define CRYPT_DATA_CORRUPT 0x8 /* Set when encryption is fine, but the
+ underlying volume is corrupt */
+#define CRYPT_FORCE_ENCRYPTION 0x10 /* Set when it is time to encrypt this
+ volume on boot. Everything in this
+ structure is set up correctly as
+ though device is encrypted except
+ that the master key is encrypted with the
+ default password. */
+#define CRYPT_FORCE_COMPLETE 0x20 /* Set when the above encryption cycle is
+ complete. On next cryptkeeper entry, match
+ the password. If it matches fix the master
+ key and remove this flag. */
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+/* This flag is used to transition from L->M upgrade. L release passed
+ * a byte for every nible of user password while M release is passing
+ * ascii value of user password.
+ * Random flag value is chosen so that it does not conflict with other use cases
+ */
+#define CRYPT_ASCII_PASSWORD_UPDATED 0x1000
+#endif
+
+/* Allowed values for type in the structure below */
+#define CRYPT_TYPE_PASSWORD 0 /* master_key is encrypted with a password
+ * Must be zero to be compatible with pre-L
+ * devices where type is always password.*/
+#define CRYPT_TYPE_DEFAULT 1 /* master_key is encrypted with default
+ * password */
+#define CRYPT_TYPE_PATTERN 2 /* master_key is encrypted with a pattern */
+#define CRYPT_TYPE_PIN 3 /* master_key is encrypted with a pin */
+#define CRYPT_TYPE_MAX_TYPE 3 /* type cannot be larger than this value */
+
+#define CRYPT_MNT_MAGIC 0xD0B5B1C4
+#define PERSIST_DATA_MAGIC 0xE950CD44
+
+/* Key Derivation Function algorithms */
+#define KDF_PBKDF2 1
+#define KDF_SCRYPT 2
+/* Algorithms 3 & 4 deprecated before shipping outside of google, so removed */
+#define KDF_SCRYPT_KEYMASTER_UNPADDED 3
+#define KDF_SCRYPT_KEYMASTER_BADLY_PADDED 4
+#define KDF_SCRYPT_KEYMASTER 5
+
+/* Maximum allowed keymaster blob size. */
+#define KEYMASTER_BLOB_SIZE 2048
+
+/* __le32 and __le16 defined in system/extras/ext4_utils/ext4_utils.h */
+#define __le8 unsigned char
+
+#if !defined(SHA256_DIGEST_LENGTH)
+#define SHA256_DIGEST_LENGTH 32
+#endif
+
+struct crypt_mnt_ftr {
+ __le32 magic; /* See above */
+ __le16 major_version;
+ __le16 minor_version;
+ __le32 ftr_size; /* in bytes, not including key following */
+ __le32 flags; /* See above */
+ __le32 keysize; /* in bytes */
+ __le32 crypt_type; /* how master_key is encrypted. Must be a
+ * CRYPT_TYPE_XXX value */
+ __le64 fs_size; /* Size of the encrypted fs, in 512 byte sectors */
+ __le32 failed_decrypt_count; /* count of # of failed attempts to decrypt and
+ mount, set to 0 on successful mount */
+ unsigned char crypto_type_name[MAX_CRYPTO_TYPE_NAME_LEN]; /* The type of encryption
+ needed to decrypt this
+ partition, null terminated */
+ __le32 spare2; /* ignored */
+ unsigned char master_key[MAX_KEY_LEN]; /* The encrypted key for decrypting the filesystem */
+ unsigned char salt[SALT_LEN]; /* The salt used for this encryption */
+ __le64 persist_data_offset[2]; /* Absolute offset to both copies of crypt_persist_data
+ * on device with that info, either the footer of the
+ * real_blkdevice or the metadata partition. */
+
+ __le32 persist_data_size; /* The number of bytes allocated to each copy of the
+ * persistent data table*/
+
+ __le8 kdf_type; /* The key derivation function used. */
+
+ /* scrypt parameters. See www.tarsnap.com/scrypt/scrypt.pdf */
+ __le8 N_factor; /* (1 << N) */
+ __le8 r_factor; /* (1 << r) */
+ __le8 p_factor; /* (1 << p) */
+ __le64 encrypted_upto; /* If we are in state CRYPT_ENCRYPTION_IN_PROGRESS and
+ we have to stop (e.g. power low) this is the last
+ encrypted 512 byte sector.*/
+ __le8 hash_first_block[SHA256_DIGEST_LENGTH]; /* When CRYPT_ENCRYPTION_IN_PROGRESS
+ set, hash of first block, used
+ to validate before continuing*/
+
+ /* key_master key, used to sign the derived key which is then used to generate
+ * the intermediate key
+ * This key should be used for no other purposes! We use this key to sign unpadded
+ * data, which is acceptable but only if the key is not reused elsewhere. */
+ __le8 keymaster_blob[KEYMASTER_BLOB_SIZE];
+ __le32 keymaster_blob_size;
+
+ /* Store scrypt of salted intermediate key. When decryption fails, we can
+ check if this matches, and if it does, we know that the problem is with the
+ drive, and there is no point in asking the user for more passwords.
+
+ Note that if any part of this structure is corrupt, this will not match and
+ we will continue to believe the user entered the wrong password. In that
+ case the only solution is for the user to enter a password enough times to
+ force a wipe.
+
+ Note also that there is no need to worry about migration. If this data is
+ wrong, we simply won't recognise a right password, and will continue to
+ prompt. On the first password change, this value will be populated and
+ then we will be OK.
+ */
+ unsigned char scrypted_intermediate_key[SCRYPT_LEN];
+
+ /* sha of this structure with this element set to zero
+ Used when encrypting on reboot to validate structure before doing something
+ fatal
+ */
+ unsigned char sha256[SHA256_DIGEST_LENGTH];
+};
+
+#define DATA_MNT_POINT "/data"
+
+/* Return values for cryptfs_crypto_complete */
+#define CRYPTO_COMPLETE_NOT_ENCRYPTED 1
+#define CRYPTO_COMPLETE_ENCRYPTED 0
+#define CRYPTO_COMPLETE_BAD_METADATA (-1)
+#define CRYPTO_COMPLETE_PARTIAL (-2)
+#define CRYPTO_COMPLETE_INCONSISTENT (-3)
+#define CRYPTO_COMPLETE_CORRUPT (-4)
+
+/* Return values for cryptfs_enable_inplace*() */
+#define ENABLE_INPLACE_OK 0
+#define ENABLE_INPLACE_ERR_OTHER (-1)
+#define ENABLE_INPLACE_ERR_DEV (-2) /* crypto_blkdev issue */
+
+typedef int (*kdf_func)(const char* passwd, const unsigned char* salt, unsigned char* ikey,
+ void* params);
+
+int cryptfs_check_passwd(const char* pw);
+int cryptfs_verify_passwd(const char* pw);
+int cryptfs_setup_ext_volume(const char* label, const char* real_blkdev, const unsigned char* key, int keysize, char* out_crypto_blkdev);
+int cryptfs_revert_ext_volume(const char* label);
+int cryptfs_get_password_type(void);
+
+uint32_t cryptfs_get_keysize();
+const char* cryptfs_get_crypto_name();
+
+void set_partition_data(const char* block_device, const char* key_location, const char* fs);
+int cryptfs_check_footer();
+int delete_crypto_blk_dev(const char *name);
+
+#endif /* ANDROID_VOLD_CRYPTFS_H */
diff --git a/crypto/fde/main.cpp b/crypto/fde/main.cpp
new file mode 100644
index 0000000..7051a6d
--- /dev/null
+++ b/crypto/fde/main.cpp
@@ -0,0 +1,47 @@
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <linux/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 <errno.h>
+#include <linux/kdev_t.h>
+#include <time.h>
+#include "cryptfs.h"
+#include "cutils/properties.h"
+#include "crypto_scrypt.h"
+
+void usage() {
+ printf(" Usage:\n");
+ printf(" twrpdec /path/to/userdata /path/to/metadata filesystem password\n");
+ printf("\n");
+ printf(" The metadata path is the path to the footer. If no metadata\n");
+ printf(" partition is present then use footer for this argument.\n");
+ printf("\n");
+ printf(" Example:\n");
+ printf(" twrpdec /dev/block/bootdevice/by-name/userdata footer ext4 0000\n");
+}
+
+int main(int argc, char **argv) {
+ if (argc != 5) {
+ usage();
+ return -1;
+ }
+ set_partition_data(argv[1], argv[2], argv[3]);
+ //int ret = cryptfs_check_passwd("30303030");
+ int ret = cryptfs_check_passwd(argv[4]);
+ return 0;
+}
diff --git a/crypto/fscrypt/Android.mk b/crypto/fscrypt/Android.mk
new file mode 100755
index 0000000..d9fb6ef
--- /dev/null
+++ b/crypto/fscrypt/Android.mk
@@ -0,0 +1,87 @@
+LOCAL_PATH := $(call my-dir)
+ifeq ($(TW_INCLUDE_CRYPTO), true)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libtwrpfscrypt
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := -Wno-unused-variable -Wno-sign-compare -Wno-unused-parameter -Wno-comment -Wno-missing-field-initializers \
+ -DHAVE_LIBKEYUTILS -std=gnu++2a -Wno-macro-redefined -Wno-unused-function -fpic
+LOCAL_SRC_FILES := FsCrypt.cpp Decrypt.cpp ScryptParameters.cpp fscrypt_policy.cpp Utils.cpp HashPassword.cpp \
+ KeyUtil.cpp Keymaster.cpp KeyStorage.cpp MetadataCrypt.cpp KeyBuffer.cpp \
+ Process.cpp EncryptInplace.cpp Weaver1.cpp cryptfs.cpp Checkpoint.cpp CryptoType.cpp VoldUtil.cpp
+LOCAL_SHARED_LIBRARIES := libselinux libc libc++ libext4_utils libbase libcrypto libcutils \
+libkeymaster_messages libhardware libprotobuf-cpp-lite libfscrypt android.hardware.confirmationui@1.0 \
+android.hardware.keymaster@3.0 libkeystore_binder libhidlbase libutils libbinder android.hardware.gatekeeper@1.0 \
+libfs_mgr android.hardware.keymaster@4.0 android.hardware.keymaster@4.1 libkeymaster4support libkeymaster4_1support \
+libf2fs_sparseblock libkeystore_parcelables libkeystore_aidl android.hardware.weaver@1.0 libkeyutils liblog libhwbinder \
+libchrome android.hardware.boot@1.0 libbootloader_message
+LOCAL_STATIC_LIBRARIES := libscrypt_static libvold_binder libc++fs
+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 \
+ system/extras/libfscrypt/include \
+ 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/vold/model \
+ system/vold/ \
+ system/extras/f2fs_utils/ \
+ bootable/recovery/bootloader_message/include
+
+ifeq ($(TW_USE_FSCRYPT_POLICY), 1)
+ LOCAL_CFLAGS += -DUSE_FSCRYPT_POLICY_V1
+else
+ LOCAL_CFLAGS += -DUSE_FSCRYPT_POLICY_V2
+endif
+
+ifneq ($(wildcard hardware/libhardware/include/hardware/keymaster0.h),)
+ LOCAL_CFLAGS += -DTW_CRYPTO_HAVE_KEYMASTERX
+ LOCAL_C_INCLUDES += external/boringssl/src/include
+endif
+
+LOCAL_REQUIRED_MODULES := keystore_auth keystore
+LOCAL_CLANG := true
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := fscryptpolicyget
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/bin
+LOCAL_C_INCLUDES += system/extras/libfscrypt/include
+LOCAL_SRC_FILES := fscryptpolicyget.cpp
+LOCAL_SHARED_LIBRARIES := libtwrpfscrypt
+
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := twrpfbe
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/bin
+LOCAL_SRC_FILES := main.cpp
+LOCAL_SHARED_LIBRARIES := libtwrpfscrypt
+
+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)/system/bin
+LOCAL_SRC_FILES := keystore_auth.cpp
+LOCAL_SHARED_LIBRARIES := libc libkeystore_binder libutils libbinder liblog
+LOCAL_CFLAGS += -DUSE_SECURITY_NAMESPACE
+LOCAL_SHARED_LIBRARIES += libkeystore_aidl
+
+include $(BUILD_EXECUTABLE)
+
+endif
diff --git a/crypto/fscrypt/Checkpoint.cpp b/crypto/fscrypt/Checkpoint.cpp
new file mode 100644
index 0000000..fac0d9f
--- /dev/null
+++ b/crypto/fscrypt/Checkpoint.cpp
@@ -0,0 +1,744 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "Checkpoint"
+#include "Checkpoint.h"
+#include "VoldUtil.h"
+#include "VolumeManager.h"
+
+#include <fstream>
+#include <list>
+#include <memory>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/unique_fd.h>
+#include <android/hardware/boot/1.0/IBootControl.h>
+#include <cutils/android_reboot.h>
+#include <fcntl.h>
+#include <fs_mgr.h>
+#include <linux/fs.h>
+#include <mntent.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <unistd.h>
+
+using android::base::GetBoolProperty;
+using android::base::GetUintProperty;
+using android::base::SetProperty;
+using android::binder::Status;
+using android::fs_mgr::Fstab;
+using android::fs_mgr::ReadDefaultFstab;
+using android::fs_mgr::ReadFstabFromFile;
+using android::hardware::hidl_string;
+using android::hardware::boot::V1_0::BoolResult;
+using android::hardware::boot::V1_0::CommandResult;
+using android::hardware::boot::V1_0::IBootControl;
+using android::hardware::boot::V1_0::Slot;
+
+
+namespace {
+const std::string kMetadataCPFile = "/metadata/vold/checkpoint";
+
+android::binder::Status error(const std::string& msg) {
+ PLOG(ERROR) << msg;
+ return android::binder::Status::fromServiceSpecificError(errno, android::String8(msg.c_str()));
+}
+
+android::binder::Status error(int error, const std::string& msg) {
+ LOG(ERROR) << msg;
+ return android::binder::Status::fromServiceSpecificError(error, android::String8(msg.c_str()));
+}
+
+bool setBowState(std::string const& block_device, std::string const& state) {
+ std::string bow_device = fs_mgr_find_bow_device(block_device);
+ if (bow_device.empty()) return false;
+
+ if (!android::base::WriteStringToFile(state, bow_device + "/bow/state")) {
+ PLOG(ERROR) << "Failed to write to file " << bow_device + "/bow/state";
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace
+
+Status cp_supportsCheckpoint(bool& result) {
+ result = false;
+
+ for (const auto& entry : fstab_default) {
+ if (entry.fs_mgr_flags.checkpoint_blk || entry.fs_mgr_flags.checkpoint_fs) {
+ result = true;
+ return Status::ok();
+ }
+ }
+ return Status::ok();
+}
+
+Status cp_supportsBlockCheckpoint(bool& result) {
+ result = false;
+
+ for (const auto& entry : fstab_default) {
+ if (entry.fs_mgr_flags.checkpoint_blk) {
+ result = true;
+ return Status::ok();
+ }
+ }
+ return Status::ok();
+}
+
+Status cp_supportsFileCheckpoint(bool& result) {
+ result = false;
+
+ for (const auto& entry : fstab_default) {
+ if (entry.fs_mgr_flags.checkpoint_fs) {
+ result = true;
+ return Status::ok();
+ }
+ }
+ return Status::ok();
+}
+
+Status cp_startCheckpoint(int retry) {
+ bool result;
+ if (!cp_supportsCheckpoint(result).isOk() || !result)
+ return error(ENOTSUP, "Checkpoints not supported");
+
+ if (retry < -1) return error(EINVAL, "Retry count must be more than -1");
+ std::string content = std::to_string(retry + 1);
+ if (retry == -1) {
+ android::sp<IBootControl> module = IBootControl::getService();
+ if (module) {
+ std::string suffix;
+ auto cb = [&suffix](hidl_string s) { suffix = s; };
+ if (module->getSuffix(module->getCurrentSlot(), cb).isOk()) content += " " + suffix;
+ }
+ }
+ if (!android::base::WriteStringToFile(content, kMetadataCPFile))
+ return error("Failed to write checkpoint file");
+ return Status::ok();
+}
+
+namespace {
+
+volatile bool isCheckpointing = false;
+
+volatile bool needsCheckpointWasCalled = false;
+
+// Protects isCheckpointing, needsCheckpointWasCalled and code that makes decisions based on status
+// of isCheckpointing
+std::mutex isCheckpointingLock;
+}
+
+Status cp_commitChanges() {
+ std::lock_guard<std::mutex> lock(isCheckpointingLock);
+
+ if (!isCheckpointing) {
+ return Status::ok();
+ }
+ if (android::base::GetProperty("persist.vold.dont_commit_checkpoint", "0") == "1") {
+ LOG(WARNING)
+ << "NOT COMMITTING CHECKPOINT BECAUSE persist.vold.dont_commit_checkpoint IS 1";
+ return Status::ok();
+ }
+ // TWRP should not mark errors on slots
+ // android::sp<IBootControl> module = IBootControl::getService();
+ // if (module) {
+ // CommandResult cr;
+ // module->markBootSuccessful([&cr](CommandResult result) { cr = result; });
+ // if (!cr.success)
+ // return error(EINVAL, "Error marking booted successfully: " + std::string(cr.errMsg));
+ // LOG(INFO) << "Marked slot as booted successfully.";
+ // // Clears the warm reset flag for next reboot.
+ // if (!SetProperty("ota.warm_reset", "0")) {
+ // LOG(WARNING) << "Failed to reset the warm reset flag";
+ // }
+ // }
+ // Must take action for list of mounted checkpointed things here
+ // To do this, we walk the list of mounted file systems.
+ // But we also need to get the matching fstab entries to see
+ // the original flags
+ std::string err_str;
+
+ Fstab mounts;
+ if (!ReadFstabFromFile("/proc/mounts", &mounts)) {
+ return error(EINVAL, "Failed to get /proc/mounts");
+ }
+
+ // Walk mounted file systems
+ for (const auto& mount_rec : mounts) {
+ const auto fstab_rec = GetEntryForMountPoint(&fstab_default, mount_rec.mount_point);
+ if (!fstab_rec) continue;
+
+ if (fstab_rec->fs_mgr_flags.checkpoint_fs) {
+ if (fstab_rec->fs_type == "f2fs") {
+ std::string options = mount_rec.fs_options + ",checkpoint=enable";
+ if (mount(mount_rec.blk_device.c_str(), mount_rec.mount_point.c_str(), "none",
+ MS_REMOUNT | fstab_rec->flags, options.c_str())) {
+ return error(EINVAL, "Failed to remount");
+ }
+ }
+ } else if (fstab_rec->fs_mgr_flags.checkpoint_blk) {
+ if (!setBowState(mount_rec.blk_device, "2"))
+ return error(EINVAL, "Failed to set bow state");
+ }
+ }
+ SetProperty("vold.checkpoint_committed", "1");
+ LOG(INFO) << "Checkpoint has been committed.";
+ isCheckpointing = false;
+ if (!android::base::RemoveFileIfExists(kMetadataCPFile, &err_str))
+ return error(err_str.c_str());
+
+ return Status::ok();
+}
+
+namespace {
+void abort_metadata_file() {
+ std::string oldContent, newContent;
+ int retry = 0;
+ struct stat st;
+ int result = stat(kMetadataCPFile.c_str(), &st);
+
+ // If the file doesn't exist, we aren't managing a checkpoint retry counter
+ if (result != 0) return;
+ if (!android::base::ReadFileToString(kMetadataCPFile, &oldContent)) {
+ PLOG(ERROR) << "Failed to read checkpoint file";
+ return;
+ }
+ std::string retryContent = oldContent.substr(0, oldContent.find_first_of(" "));
+
+ if (!android::base::ParseInt(retryContent, &retry)) {
+ PLOG(ERROR) << "Could not parse retry count";
+ return;
+ }
+ if (retry > 0) {
+ newContent = "0";
+ if (!android::base::WriteStringToFile(newContent, kMetadataCPFile))
+ PLOG(ERROR) << "Could not write checkpoint file";
+ }
+}
+} // namespace
+
+void cp_abortChanges(const std::string& message, bool retry) {
+ if (!cp_needsCheckpoint()) return;
+ if (!retry) abort_metadata_file();
+ android_reboot(ANDROID_RB_RESTART2, 0, message.c_str());
+}
+
+bool cp_needsRollback() {
+ std::string content;
+ bool ret;
+
+ ret = android::base::ReadFileToString(kMetadataCPFile, &content);
+ if (ret) {
+ if (content == "0") return true;
+ if (content.substr(0, 3) == "-1 ") {
+ std::string oldSuffix = content.substr(3);
+ android::sp<IBootControl> module = IBootControl::getService();
+ std::string newSuffix;
+
+ if (module) {
+ auto cb = [&newSuffix](hidl_string s) { newSuffix = s; };
+ module->getSuffix(module->getCurrentSlot(), cb);
+ if (oldSuffix == newSuffix) return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool cp_needsCheckpoint() {
+ std::lock_guard<std::mutex> lock(isCheckpointingLock);
+
+ // Make sure we only return true during boot. See b/138952436 for discussion
+ if (needsCheckpointWasCalled) return isCheckpointing;
+ needsCheckpointWasCalled = true;
+
+ bool ret;
+ std::string content;
+ android::sp<IBootControl> module = IBootControl::getService();
+
+ if (isCheckpointing) return isCheckpointing;
+
+ if (module && module->isSlotMarkedSuccessful(module->getCurrentSlot()) == BoolResult::FALSE) {
+ isCheckpointing = true;
+ return true;
+ }
+ ret = android::base::ReadFileToString(kMetadataCPFile, &content);
+ if (ret) {
+ ret = content != "0";
+ isCheckpointing = ret;
+ return ret;
+ }
+ return false;
+}
+
+bool cp_isCheckpointing() {
+ return isCheckpointing;
+}
+
+namespace {
+const std::string kSleepTimeProp = "ro.sys.cp_msleeptime";
+const uint32_t msleeptime_default = 1000; // 1 s
+const uint32_t max_msleeptime = 3600000; // 1 h
+
+const std::string kMinFreeBytesProp = "ro.sys.cp_min_free_bytes";
+const uint64_t min_free_bytes_default = 100 * (1 << 20); // 100 MiB
+
+const std::string kCommitOnFullProp = "ro.sys.cp_commit_on_full";
+const bool commit_on_full_default = true;
+
+static void cp_healthDaemon(std::string mnt_pnt, std::string blk_device, bool is_fs_cp) {
+ struct statvfs data;
+ uint32_t msleeptime = GetUintProperty(kSleepTimeProp, msleeptime_default, max_msleeptime);
+ uint64_t min_free_bytes =
+ GetUintProperty(kMinFreeBytesProp, min_free_bytes_default, (uint64_t)-1);
+ bool commit_on_full = GetBoolProperty(kCommitOnFullProp, commit_on_full_default);
+
+ struct timespec req;
+ req.tv_sec = msleeptime / 1000;
+ msleeptime %= 1000;
+ req.tv_nsec = msleeptime * 1000000;
+ while (isCheckpointing) {
+ uint64_t free_bytes = 0;
+ if (is_fs_cp) {
+ statvfs(mnt_pnt.c_str(), &data);
+ free_bytes = ((uint64_t) data.f_bavail) * data.f_frsize;
+ } else {
+ std::string bow_device = fs_mgr_find_bow_device(blk_device);
+ if (!bow_device.empty()) {
+ std::string content;
+ if (android::base::ReadFileToString(bow_device + "/bow/free", &content)) {
+ free_bytes = std::strtoull(content.c_str(), NULL, 10);
+ }
+ }
+ }
+ if (free_bytes < min_free_bytes) {
+ if (commit_on_full) {
+ LOG(INFO) << "Low space for checkpointing. Commiting changes";
+ cp_commitChanges();
+ break;
+ } else {
+ LOG(INFO) << "Low space for checkpointing. Rebooting";
+ cp_abortChanges("checkpoint,low_space", false);
+ break;
+ }
+ }
+ nanosleep(&req, NULL);
+ }
+}
+
+} // namespace
+
+Status cp_prepareCheckpoint() {
+ // Log to notify CTS - see b/137924328 for context
+ LOG(INFO) << "cp_prepareCheckpoint called";
+ std::lock_guard<std::mutex> lock(isCheckpointingLock);
+ if (!isCheckpointing) {
+ return Status::ok();
+ }
+
+ Fstab mounts;
+ if (!ReadFstabFromFile("/proc/mounts", &mounts)) {
+ return error(EINVAL, "Failed to get /proc/mounts");
+ }
+
+ for (const auto& mount_rec : mounts) {
+ const auto fstab_rec = GetEntryForMountPoint(&fstab_default, mount_rec.mount_point);
+ if (!fstab_rec) continue;
+
+ if (fstab_rec->fs_mgr_flags.checkpoint_blk) {
+ android::base::unique_fd fd(
+ TEMP_FAILURE_RETRY(open(mount_rec.mount_point.c_str(), O_RDONLY | O_CLOEXEC)));
+ if (fd == -1) {
+ PLOG(ERROR) << "Failed to open mount point" << mount_rec.mount_point;
+ continue;
+ }
+
+ struct fstrim_range range = {};
+ range.len = ULLONG_MAX;
+ nsecs_t start = systemTime(SYSTEM_TIME_BOOTTIME);
+ if (ioctl(fd, FITRIM, &range)) {
+ PLOG(ERROR) << "Failed to trim " << mount_rec.mount_point;
+ continue;
+ }
+ nsecs_t time = systemTime(SYSTEM_TIME_BOOTTIME) - start;
+ LOG(INFO) << "Trimmed " << range.len << " bytes on " << mount_rec.mount_point << " in "
+ << nanoseconds_to_milliseconds(time) << "ms for checkpoint";
+
+ setBowState(mount_rec.blk_device, "1");
+ }
+ if (fstab_rec->fs_mgr_flags.checkpoint_blk || fstab_rec->fs_mgr_flags.checkpoint_fs) {
+ std::thread(cp_healthDaemon, std::string(mount_rec.mount_point),
+ std::string(mount_rec.blk_device),
+ fstab_rec->fs_mgr_flags.checkpoint_fs == 1)
+ .detach();
+ }
+ }
+ return Status::ok();
+}
+
+namespace {
+const int kSectorSize = 512;
+
+typedef uint64_t sector_t;
+
+struct log_entry {
+ sector_t source; // in sectors of size kSectorSize
+ sector_t dest; // in sectors of size kSectorSize
+ uint32_t size; // in bytes
+ uint32_t checksum;
+} __attribute__((packed));
+
+struct log_sector_v1_0 {
+ uint32_t magic;
+ uint16_t header_version;
+ uint16_t header_size;
+ uint32_t block_size;
+ uint32_t count;
+ uint32_t sequence;
+ uint64_t sector0;
+} __attribute__((packed));
+
+// MAGIC is BOW in ascii
+const int kMagic = 0x00574f42;
+// Partially restored MAGIC is WOB in ascii
+const int kPartialRestoreMagic = 0x00424f57;
+
+void crc32(const void* data, size_t n_bytes, uint32_t* crc) {
+ static uint32_t table[0x100] = {
+ 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535,
+ 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD,
+ 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D,
+ 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
+ 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4,
+ 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,
+ 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC,
+ 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
+ 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB,
+ 0xB6662D3D,
+
+ 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5,
+ 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D,
+ 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED,
+ 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
+ 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074,
+ 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC,
+ 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C,
+ 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
+ 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B,
+ 0xC0BA6CAD,
+
+ 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615,
+ 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D,
+ 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D,
+ 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
+ 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4,
+ 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C,
+ 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C,
+ 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
+ 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B,
+ 0x5BDEAE1D,
+
+ 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785,
+ 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D,
+ 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD,
+ 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
+ 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354,
+ 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC,
+ 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C,
+ 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
+ 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B,
+ 0x2D02EF8D};
+
+ for (size_t i = 0; i < n_bytes; ++i) {
+ *crc ^= ((uint8_t*)data)[i];
+ *crc = table[(uint8_t)*crc] ^ *crc >> 8;
+ }
+}
+
+// A map of relocations.
+// The map must be initialized so that relocations[0] = 0
+// During restore, we replay the log records in reverse, copying from dest to
+// source
+// To validate, we must be able to read the 'dest' sectors as though they had
+// been copied but without actually copying. This map represents how the sectors
+// would have been moved. To read a sector s, find the index <= s and read
+// relocations[index] + s - index
+typedef std::map<sector_t, sector_t> Relocations;
+
+void relocate(Relocations& relocations, sector_t dest, sector_t source, int count) {
+ // Find first one we're equal to or greater than
+ auto s = --relocations.upper_bound(source);
+
+ // Take slice
+ Relocations slice;
+ slice[dest] = source - s->first + s->second;
+ ++s;
+
+ // Add rest of elements
+ for (; s != relocations.end() && s->first < source + count; ++s)
+ slice[dest - source + s->first] = s->second;
+
+ // Split range at end of dest
+ auto dest_end = --relocations.upper_bound(dest + count);
+ relocations[dest + count] = dest + count - dest_end->first + dest_end->second;
+
+ // Remove all elements in [dest, dest + count)
+ relocations.erase(relocations.lower_bound(dest), relocations.lower_bound(dest + count));
+
+ // Add new elements
+ relocations.insert(slice.begin(), slice.end());
+}
+
+// A map of sectors that have been written to.
+// The final entry must always be False.
+// When we restart the restore after an interruption, we must take care that
+// when we copy from dest to source, that the block we copy to was not
+// previously copied from.
+// i e. A->B C->A; If we replay this sequence, we end up copying C->B
+// We must save our partial result whenever we finish a page, or when we copy
+// to a location that was copied from earlier (our source is an earlier dest)
+typedef std::map<sector_t, bool> Used_Sectors;
+
+bool checkCollision(Used_Sectors& used_sectors, sector_t start, sector_t end) {
+ auto second_overlap = used_sectors.upper_bound(start);
+ auto first_overlap = --second_overlap;
+
+ if (first_overlap->second) {
+ return true;
+ } else if (second_overlap != used_sectors.end() && second_overlap->first < end) {
+ return true;
+ }
+ return false;
+}
+
+void markUsed(Used_Sectors& used_sectors, sector_t start, sector_t end) {
+ auto start_pos = used_sectors.insert_or_assign(start, true).first;
+ auto end_pos = used_sectors.insert_or_assign(end, false).first;
+
+ if (start_pos == used_sectors.begin() || !std::prev(start_pos)->second) {
+ start_pos++;
+ }
+ if (std::next(end_pos) != used_sectors.end() && !std::next(end_pos)->second) {
+ end_pos++;
+ }
+ if (start_pos->first < end_pos->first) {
+ used_sectors.erase(start_pos, end_pos);
+ }
+}
+
+// Restores the given log_entry's data from dest -> source
+// If that entry is a log sector, set the magic to kPartialRestoreMagic and flush.
+void restoreSector(int device_fd, Used_Sectors& used_sectors, std::vector<char>& ls_buffer,
+ log_entry* le, std::vector<char>& buffer) {
+ log_sector_v1_0& ls = *reinterpret_cast<log_sector_v1_0*>(&ls_buffer[0]);
+ uint32_t index = le - ((log_entry*)&ls_buffer[ls.header_size]);
+ int count = (le->size - 1) / kSectorSize + 1;
+
+ if (checkCollision(used_sectors, le->source, le->source + count)) {
+ fsync(device_fd);
+ lseek64(device_fd, 0, SEEK_SET);
+ ls.count = index + 1;
+ ls.magic = kPartialRestoreMagic;
+ write(device_fd, &ls_buffer[0], ls.block_size);
+ fsync(device_fd);
+ used_sectors.clear();
+ used_sectors[0] = false;
+ }
+
+ markUsed(used_sectors, le->dest, le->dest + count);
+
+ if (index == 0 && ls.sequence != 0) {
+ log_sector_v1_0* next = reinterpret_cast<log_sector_v1_0*>(&buffer[0]);
+ if (next->magic == kMagic) {
+ next->magic = kPartialRestoreMagic;
+ }
+ }
+
+ lseek64(device_fd, le->source * kSectorSize, SEEK_SET);
+ write(device_fd, &buffer[0], le->size);
+
+ if (index == 0) {
+ fsync(device_fd);
+ }
+}
+
+// Read from the device
+// If we are validating, the read occurs as though the relocations had happened
+std::vector<char> relocatedRead(int device_fd, Relocations const& relocations, bool validating,
+ sector_t sector, uint32_t size, uint32_t block_size) {
+ if (!validating) {
+ std::vector<char> buffer(size);
+ lseek64(device_fd, sector * kSectorSize, SEEK_SET);
+ read(device_fd, &buffer[0], size);
+ return buffer;
+ }
+
+ std::vector<char> buffer(size);
+ for (uint32_t i = 0; i < size; i += block_size, sector += block_size / kSectorSize) {
+ auto relocation = --relocations.upper_bound(sector);
+ lseek64(device_fd, (sector + relocation->second - relocation->first) * kSectorSize,
+ SEEK_SET);
+ read(device_fd, &buffer[i], block_size);
+ }
+
+ return buffer;
+}
+
+} // namespace
+
+Status cp_restoreCheckpoint(const std::string& blockDevice, int restore_limit) {
+ bool validating = true;
+ std::string action = "Validating";
+ int restore_count = 0;
+
+ for (;;) {
+ Relocations relocations;
+ relocations[0] = 0;
+ Status status = Status::ok();
+
+ LOG(INFO) << action << " checkpoint on " << blockDevice;
+ android::base::unique_fd device_fd(open(blockDevice.c_str(), O_RDWR | O_CLOEXEC));
+ if (device_fd < 0) return error("Cannot open " + blockDevice);
+
+ log_sector_v1_0 original_ls;
+ read(device_fd, reinterpret_cast<char*>(&original_ls), sizeof(original_ls));
+ if (original_ls.magic == kPartialRestoreMagic) {
+ validating = false;
+ action = "Restoring";
+ } else if (original_ls.magic != kMagic) {
+ return error(EINVAL, "No magic");
+ }
+
+ LOG(INFO) << action << " " << original_ls.sequence << " log sectors";
+
+ for (int sequence = original_ls.sequence; sequence >= 0 && status.isOk(); sequence--) {
+ auto ls_buffer = relocatedRead(device_fd, relocations, validating, 0,
+ original_ls.block_size, original_ls.block_size);
+ log_sector_v1_0& ls = *reinterpret_cast<log_sector_v1_0*>(&ls_buffer[0]);
+
+ Used_Sectors used_sectors;
+ used_sectors[0] = false;
+
+ if (ls.magic != kMagic && (ls.magic != kPartialRestoreMagic || validating)) {
+ status = error(EINVAL, "No magic");
+ break;
+ }
+
+ if (ls.block_size != original_ls.block_size) {
+ status = error(EINVAL, "Block size mismatch");
+ break;
+ }
+
+ if ((int)ls.sequence != sequence) {
+ status = error(EINVAL, "Expecting log sector " + std::to_string(sequence) +
+ " but got " + std::to_string(ls.sequence));
+ break;
+ }
+
+ LOG(INFO) << action << " from log sector " << ls.sequence;
+ for (log_entry* le =
+ reinterpret_cast<log_entry*>(&ls_buffer[ls.header_size]) + ls.count - 1;
+ le >= reinterpret_cast<log_entry*>(&ls_buffer[ls.header_size]); --le) {
+ // This is very noisy - limit to DEBUG only
+ LOG(VERBOSE) << action << " " << le->size << " bytes from sector " << le->dest
+ << " to " << le->source << " with checksum " << std::hex
+ << le->checksum;
+
+ auto buffer = relocatedRead(device_fd, relocations, validating, le->dest, le->size,
+ ls.block_size);
+ uint32_t checksum = le->source / (ls.block_size / kSectorSize);
+ for (size_t i = 0; i < le->size; i += ls.block_size) {
+ crc32(&buffer[i], ls.block_size, &checksum);
+ }
+
+ if (le->checksum && checksum != le->checksum) {
+ status = error(EINVAL, "Checksums don't match");
+ break;
+ }
+
+ if (validating) {
+ relocate(relocations, le->source, le->dest, (le->size - 1) / kSectorSize + 1);
+ } else {
+ restoreSector(device_fd, used_sectors, ls_buffer, le, buffer);
+ restore_count++;
+ if (restore_limit && restore_count >= restore_limit) {
+ status = error(EAGAIN, "Hit the test limit");
+ break;
+ }
+ }
+ }
+ }
+
+ if (!status.isOk()) {
+ if (!validating) {
+ LOG(ERROR) << "Checkpoint restore failed even though checkpoint validation passed";
+ return status;
+ }
+
+ LOG(WARNING) << "Checkpoint validation failed - attempting to roll forward";
+ auto buffer = relocatedRead(device_fd, relocations, false, original_ls.sector0,
+ original_ls.block_size, original_ls.block_size);
+ lseek64(device_fd, 0, SEEK_SET);
+ write(device_fd, &buffer[0], original_ls.block_size);
+ return Status::ok();
+ }
+
+ if (!validating) break;
+
+ validating = false;
+ action = "Restoring";
+ }
+
+ return Status::ok();
+}
+
+Status cp_markBootAttempt() {
+ std::string oldContent, newContent;
+ int retry = 0;
+ struct stat st;
+ int result = stat(kMetadataCPFile.c_str(), &st);
+
+ // If the file doesn't exist, we aren't managing a checkpoint retry counter
+ if (result != 0) return Status::ok();
+ if (!android::base::ReadFileToString(kMetadataCPFile, &oldContent))
+ return error("Failed to read checkpoint file");
+ std::string retryContent = oldContent.substr(0, oldContent.find_first_of(" "));
+
+ if (!android::base::ParseInt(retryContent, &retry))
+ return error(EINVAL, "Could not parse retry count");
+ if (retry > 0) {
+ retry--;
+
+ newContent = std::to_string(retry);
+ if (!android::base::WriteStringToFile(newContent, kMetadataCPFile))
+ return error("Could not write checkpoint file");
+ }
+ return Status::ok();
+}
+
+void cp_resetCheckpoint() {
+ std::lock_guard<std::mutex> lock(isCheckpointingLock);
+ needsCheckpointWasCalled = false;
+}
diff --git a/crypto/fscrypt/Checkpoint.h b/crypto/fscrypt/Checkpoint.h
new file mode 100644
index 0000000..cd17c78
--- /dev/null
+++ b/crypto/fscrypt/Checkpoint.h
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+#ifndef _CHECKPOINT_H
+#define _CHECKPOINT_H
+
+#include <binder/Status.h>
+#include <string>
+
+
+android::binder::Status cp_supportsCheckpoint(bool& result);
+
+android::binder::Status cp_supportsBlockCheckpoint(bool& result);
+
+android::binder::Status cp_supportsFileCheckpoint(bool& result);
+
+android::binder::Status cp_startCheckpoint(int retry);
+
+android::binder::Status cp_commitChanges();
+
+void cp_abortChanges(const std::string& message, bool retry);
+
+bool cp_needsRollback();
+
+bool cp_needsCheckpoint();
+
+bool cp_isCheckpointing();
+
+android::binder::Status cp_prepareCheckpoint();
+
+android::binder::Status cp_restoreCheckpoint(const std::string& mountPoint, int count = 0);
+
+android::binder::Status cp_markBootAttempt();
+
+void cp_resetCheckpoint();
+
+#endif
diff --git a/crypto/fscrypt/CryptoType.cpp b/crypto/fscrypt/CryptoType.cpp
new file mode 100644
index 0000000..cf1ef42
--- /dev/null
+++ b/crypto/fscrypt/CryptoType.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2020 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 "CryptoType.h"
+
+#include <string.h>
+
+#include <android-base/logging.h>
+#include <cutils/properties.h>
+
+
+const CryptoType& lookup_crypto_algorithm(const CryptoType table[], int table_len,
+ const CryptoType& default_alg, const char* property) {
+ char paramstr[PROPERTY_VALUE_MAX];
+
+ property_get(property, paramstr, default_alg.get_config_name());
+ for (int i = 0; i < table_len; i++) {
+ if (strcmp(paramstr, table[i].get_config_name()) == 0) {
+ return table[i];
+ }
+ }
+ LOG(ERROR) << "Invalid name (" << paramstr << ") for " << property << ". Defaulting to "
+ << default_alg.get_config_name() << ".";
+ return default_alg;
+}
diff --git a/crypto/fscrypt/CryptoType.h b/crypto/fscrypt/CryptoType.h
new file mode 100644
index 0000000..79ea93d
--- /dev/null
+++ b/crypto/fscrypt/CryptoType.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#pragma once
+
+#include <stdlib.h>
+
+
+// Struct representing an encryption algorithm supported by vold.
+// "config_name" represents the name we give the algorithm in
+// read-only properties and fstab files
+// "kernel_name" is the name we present to the Linux kernel
+// "keysize" is the size of the key in bytes.
+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(size_t size) const {
+ return CryptoType(this->config_name, this->kernel_name, size);
+ }
+ constexpr CryptoType set_config_name(const char* property) const {
+ return CryptoType(property, this->kernel_name, this->keysize);
+ }
+ constexpr CryptoType set_kernel_name(const char* crypto) const {
+ return CryptoType(this->config_name, crypto, this->keysize);
+ }
+
+ constexpr const char* get_config_name() const { return config_name; }
+ constexpr const char* get_kernel_name() const { return kernel_name; }
+ constexpr size_t get_keysize() const { return keysize; }
+
+ private:
+ const char* config_name;
+ const char* kernel_name;
+ size_t keysize;
+
+ constexpr CryptoType(const char* property, const char* crypto, size_t ksize)
+ : config_name(property), kernel_name(crypto), keysize(ksize) {}
+};
+
+// Use the named android property to look up a type from the table
+// If the property is not set or matches no table entry, return the default.
+const CryptoType& lookup_crypto_algorithm(const CryptoType table[], int table_len,
+ const CryptoType& default_alg, const char* property);
+
+// Some useful types
+
+constexpr CryptoType invalid_crypto_type = CryptoType();
+
+constexpr CryptoType aes_256_xts = CryptoType()
+ .set_config_name("aes-256-xts")
+ .set_kernel_name("aes-xts-plain64")
+ .set_keysize(64);
+
+constexpr CryptoType adiantum = CryptoType()
+ .set_config_name("adiantum")
+ .set_kernel_name("xchacha12,aes-adiantum-plain64")
+ .set_keysize(32);
+
+// Support compile-time validation of a crypto type table
+
+template <typename T, size_t N>
+constexpr size_t array_length(T (&)[N]) {
+ return N;
+}
+
+constexpr bool isValidCryptoType(size_t max_keylen, const CryptoType& crypto_type) {
+ return ((crypto_type.get_config_name() != nullptr) &&
+ (crypto_type.get_kernel_name() != nullptr) &&
+ (crypto_type.get_keysize() <= max_keylen));
+}
+
+// Confirms that all supported_crypto_types have a small enough keysize and
+// had both set_config_name() and set_kernel_name() called.
+// 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 max_keylen, const CryptoType types[],
+ size_t len) {
+ return len == 0 || (isValidCryptoType(max_keylen, types[len - 1]) &&
+ validateSupportedCryptoTypes(max_keylen, types, len - 1));
+}
diff --git a/crypto/fscrypt/Decrypt.cpp b/crypto/fscrypt/Decrypt.cpp
new file mode 100755
index 0000000..0217358
--- /dev/null
+++ b/crypto/fscrypt/Decrypt.cpp
@@ -0,0 +1,1220 @@
+/*
+ * Copyright (C) 2016 - 2020 The TeamWin Recovery Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Decrypt.h"
+#include "FsCrypt.h"
+#include <fscrypt/fscrypt.h>
+
+#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
+#include "keystore_client.pb.h"
+#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 <future>
+#include <algorithm>
+
+#include <android-base/file.h>
+#include <base/threading/platform_thread.h>
+#include <android/hardware/confirmationui/1.0/types.h>
+#include <android/security/BnConfirmationPromptCallback.h>
+#include <android/security/keystore/IKeystoreService.h>
+#include <android/hardware/gatekeeper/1.0/IGatekeeper.h>
+
+#include <binder/IServiceManager.h>
+#include <binder/IPCThreadState.h>
+#include <hardware/hw_auth_token.h>
+
+#include <keystore/keystore.h>
+#include <keystore/keystore_client.h>
+#include <keystore/keystore_client_impl.h>
+#include <keystore/KeystoreResponse.h>
+#include <keystore/keystore_hidl_support.h>
+#include <keystore/keystore_promises.h>
+#include <keystore/keystore_return_types.h>
+#include <keystore/keymaster_types.h>
+#include <keymasterV4_1/Keymaster.h>
+#include <keystore/OperationResult.h>
+#include "keystore_client.pb.h"
+
+#include <keymasterV4_1/authorization_set.h>
+#include <keymasterV4_1/keymaster_utils.h>
+
+extern "C" {
+#include "crypto_scrypt.h"
+}
+
+#include "fscrypt_policy.h"
+#include "fscrypt-common.h"
+#include "HashPassword.h"
+#include "KeyStorage.h"
+#include "android/os/IVold.h"
+
+using android::security::keystore::IKeystoreService;
+using keystore::KeystoreResponsePromise;
+using keystore::OperationResultPromise;
+using android::security::keymaster::OperationResult;
+using android::hardware::keymaster::V4_1::support::blob2hidlVec;
+
+
+inline std::string hidlVec2String(const ::keystore::hidl_vec<uint8_t>& value) {
+ return std::string(reinterpret_cast<const std::string::value_type*>(&value[0]), value.size());
+}
+
+static bool lookup_ref_key_internal(std::map<userid_t, android::fscrypt::EncryptionPolicy> key_map, const uint8_t* policy, userid_t* user_id) {
+#ifdef USE_FSCRYPT_POLICY_V1
+ char policy_string_hex[FS_KEY_DESCRIPTOR_SIZE_HEX];
+ char key_map_hex[FS_KEY_DESCRIPTOR_SIZE_HEX];
+ bytes_to_hex(policy, FS_KEY_DESCRIPTOR_SIZE, policy_string_hex);
+#else
+ char policy_string_hex[FSCRYPT_KEY_IDENTIFIER_HEX_SIZE];
+ char key_map_hex[FSCRYPT_KEY_IDENTIFIER_HEX_SIZE];
+ bytes_to_hex(policy, FSCRYPT_KEY_IDENTIFIER_SIZE, policy_string_hex);
+#endif
+
+ for (std::map<userid_t, android::fscrypt::EncryptionPolicy>::iterator it=key_map.begin(); it!=key_map.end(); ++it) {
+#ifdef USE_FSCRYPT_POLICY_V1
+ bytes_to_hex(reinterpret_cast<const uint8_t*>(&it->second.key_raw_ref[0]), FS_KEY_DESCRIPTOR_SIZE, key_map_hex);
+#else
+ bytes_to_hex(reinterpret_cast<const uint8_t*>(&it->second.key_raw_ref[0]), FSCRYPT_KEY_IDENTIFIER_SIZE, key_map_hex);
+#endif
+ std::string key_map_hex_string = std::string(key_map_hex);
+ if (key_map_hex_string == policy_string_hex) {
+ *user_id = it->first;
+ return true;
+ }
+ }
+ return false;
+}
+
+#ifdef USE_FSCRYPT_POLICY_V1
+extern "C" bool lookup_ref_key(fscrypt_policy_v1* fep, uint8_t* policy_type) {
+#else
+extern "C" bool lookup_ref_key(fscrypt_policy_v2* fep, uint8_t* policy_type) {
+#endif
+ userid_t user_id = 0;
+ std::string policy_type_string;
+
+#ifdef USE_FSCRYPT_POLICY_V1
+ char policy_hex[FS_KEY_DESCRIPTOR_SIZE_HEX];
+ bytes_to_hex(fep->master_key_descriptor, FS_KEY_DESCRIPTOR_SIZE, policy_hex);
+ if (std::strncmp((const char*)fep->master_key_descriptor, de_key_raw_ref.c_str(), FS_KEY_DESCRIPTOR_SIZE) == 0) {
+ policy_type_string = SYSTEM_DE_FSCRYPT_POLICY;
+ memcpy(policy_type, policy_type_string.data(), policy_type_string.size());
+ return true;
+ }
+ if (!lookup_ref_key_internal(s_de_policies, fep->master_key_descriptor, &user_id)) {
+ if (!lookup_ref_key_internal(s_ce_policies, fep->master_key_descriptor, &user_id)) {
+ return false;
+ } else {
+ policy_type_string = USER_CE_FSCRYPT_POLICY + std::to_string(user_id);
+ }
+ } else {
+ policy_type_string = USER_DE_FSCRYPT_POLICY + std::to_string(user_id);
+ }
+#else
+ char policy_hex[FSCRYPT_KEY_IDENTIFIER_HEX_SIZE];
+ bytes_to_hex(fep->master_key_identifier, FSCRYPT_KEY_IDENTIFIER_SIZE, policy_hex);
+ if (std::strncmp((const char*)fep->master_key_identifier, de_key_raw_ref.c_str(), FSCRYPT_KEY_IDENTIFIER_SIZE) == 0) {
+ policy_type_string = SYSTEM_DE_FSCRYPT_POLICY;
+ memcpy(policy_type, policy_type_string.data(), policy_type_string.size());
+ return true;
+ }
+ if (!lookup_ref_key_internal(s_de_policies, fep->master_key_identifier, &user_id)) {
+ if (!lookup_ref_key_internal(s_ce_policies, fep->master_key_identifier, &user_id)) {
+ return false;
+ } else {
+ policy_type_string = USER_CE_FSCRYPT_POLICY + std::to_string(user_id);
+ }
+ } else {
+ policy_type_string = USER_DE_FSCRYPT_POLICY + std::to_string(user_id);
+ }
+#endif
+
+ memcpy(policy_type, policy_type_string.data(), policy_type_string.size());
+ LOG(INFO) << "storing policy type: " << policy_type;
+ return true;
+}
+
+extern "C" bool lookup_ref_tar(const uint8_t* policy_type, uint8_t* policy) {
+ std::string policy_type_string = std::string((char *) policy_type);
+#ifdef USE_FSCRYPT_POLICY_V1
+ char policy_hex[FS_KEY_DESCRIPTOR_SIZE_HEX];
+ bytes_to_hex(policy_type, FS_KEY_DESCRIPTOR_SIZE, policy_hex);
+#else
+ char policy_hex[FSCRYPT_KEY_IDENTIFIER_HEX_SIZE];
+ bytes_to_hex(policy_type, FSCRYPT_KEY_IDENTIFIER_SIZE, policy_hex);
+#endif
+
+ userid_t user_id = atoi(policy_type_string.substr(3, 4).c_str());
+
+ // TODO Update version # and make magic strings
+#ifdef USE_FSCRYPT_POLICY_V1
+ if (policy_type_string.substr(0,1) != FSCRYPT_V1) {
+#else
+ if (policy_type_string.substr(0,1) != FSCRYPT_V2) {
+#endif
+ LOG(ERROR) << "Unexpected version:" << policy_type[0];
+ return false;
+ }
+
+ if (policy_type_string.substr(1, 2) == SYSTEM_DE_KEY) {
+ memcpy(policy, de_key_raw_ref.data(), de_key_raw_ref.size());
+ return true;
+ }
+
+ std::string raw_ref;
+
+ if (policy_type_string.substr(1, 1) == USER_DE_KEY) {
+ if (lookup_key_ref(s_de_policies, user_id, &raw_ref)) {
+ memcpy(policy, raw_ref.data(), raw_ref.size());
+ } else
+ return false;
+ } else if (policy_type_string.substr(1, 1) == USER_CE_KEY) {
+ if (lookup_key_ref(s_ce_policies, user_id, &raw_ref)) {
+ memcpy(policy, raw_ref.data(), raw_ref.size());
+ } else
+ return false;
+ } else {
+ LOG(ERROR) << "unknown policy type: " << policy_type;
+ return false;
+ }
+ return true;
+}
+
+bool Decrypt_DE() {
+ if (!fscrypt_initialize_systemwide_keys()) { // this deals with the overarching device encryption
+ printf("fscrypt_initialize_systemwide_keys returned fail\n");
+ return false;
+ }
+ if (!fscrypt_init_user0()) {
+ printf("fscrypt_init_user0 returned fail\n");
+ return false;
+ }
+ return true;
+}
+
+// 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 */
+android::sp<IBinder> getKeystoreBinder() {
+ android::sp<IServiceManager> sm = android::defaultServiceManager();
+ return sm->getService(String16("android.security.keystore"));
+}
+
+android::sp<IBinder> getKeystoreBinderRetry() {
+ printf("Starting keystore...\n");
+ property_set("ctl.start", "keystore");
+ int retry_count = 50;
+ android::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 = "";
+
+ android::ProcessState::self()->startThreadPool();
+
+ 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
+ android::sp<IBinder> binder = getKeystoreBinderRetry();
+ android::sp<IKeystoreService> service = interface_cast<IKeystoreService>(binder);
+
+ 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_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");
+ std::string keystore_alias = mKey_Prefix;
+ keystore_alias += keystore_alias_subid;
+ String16 keystore_alias16(keystore_alias.data(), keystore_alias.size());
+ int32_t error_code;
+ unsigned char* cipher_text = (unsigned char*)byteptr + 12; // The cipher text comes immediately after the IV
+ std::string cipher_text_str(byteptr, byteptr + spblob_data.size() - 14);
+
+ ::keystore::hidl_vec<uint8_t> cipher_text_hidlvec;
+ ::keystore::AuthorizationSetBuilder begin_params;
+
+ cipher_text_hidlvec.setToExternal(cipher_text, spblob_data.size() - 14 /* 1 each for version and SYNTHETIC_PASSWORD_PASSWORD_BASED and 12 for the iv */);
+ 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);
+ android::security::keymaster::KeymasterArguments empty_params;
+ android::hardware::keymaster::V4_0::KeyPurpose decryptPurpose = android::hardware::keymaster::V4_0::KeyPurpose::DECRYPT;
+ android::sp<android::IBinder> decryptAuthToken(new android::BBinder);
+
+ android::sp<OperationResultPromise> promise = new OperationResultPromise;
+ auto future = promise->get_future();
+ auto binder_result = service->begin(promise, decryptAuthToken, keystore_alias16, (int32_t)decryptPurpose, true,
+ android::security::keymaster::KeymasterArguments(begin_params.hidl_data()),
+ entropy, -1, &error_code);
+ if (!binder_result.isOk()) {
+ printf("communication error while calling keystore\n");
+ return disk_decryption_secret_key;
+ }
+ ::keystore::KeyStoreNativeReturnCode rc(error_code);
+ if (!rc.isOk()) {
+ printf("Keystore begin returned: %u\n", error_code);
+ return disk_decryption_secret_key;
+ }
+ OperationResult result = future.get();
+ std::map<uint64_t, android::sp<android::IBinder>> active_operations_;
+ uint64_t next_virtual_handle_ = 1;
+ active_operations_[next_virtual_handle_] = result.token;
+
+ // 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
+ future = {};
+ promise = new OperationResultPromise();
+ future = promise->get_future();
+ binder_result = service->update(promise, active_operations_[next_virtual_handle_], empty_params, cipher_text_hidlvec, &error_code);
+ rc = ::keystore::KeyStoreNativeReturnCode(error_code);
+ if (!rc.isOk()) {
+ printf("Keystore update returned: %d\n", error_code);
+ return disk_decryption_secret_key;
+ }
+ result = future.get();
+ if (!result.resultCode.isOk()) {
+ printf("update failed: %d\n", error_code);
+ return disk_decryption_secret_key;
+ }
+
+ size_t keystore_result_size = 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, &result.data[0], result.data.size());
+ future = {};
+ promise = new OperationResultPromise();
+ future = promise->get_future();
+
+ auto hidlSignature = blob2hidlVec("");
+ auto hidlInput = blob2hidlVec(disk_decryption_secret_key);
+ binder_result = service->finish(promise, active_operations_[next_virtual_handle_], empty_params, hidlInput, hidlSignature, ::keystore::hidl_vec<uint8_t>(), &error_code);
+ if (!binder_result.isOk()) {
+ printf("communication error while calling keystore\n");
+ free(keystore_result);
+ return disk_decryption_secret_key;
+ }
+ rc = ::keystore::KeyStoreNativeReturnCode(error_code);
+ if (!rc.isOk()) {
+ printf("Keystore finish returned: %d\n", error_code);
+ return disk_decryption_secret_key;
+ }
+ result = future.get();
+ if (!result.resultCode.isOk()) {
+ printf("finish failed: %d\n", error_code);
+ return disk_decryption_secret_key;
+ }
+ 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;
+ // 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) {
+ 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);
+ if (!secret_key) {
+ printf("malloc failure on secret key\n");
+ return disk_decryption_secret_key;
+ }
+ 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 = android::os::IVold::STORAGE_FLAG_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 (!fscrypt_unlock_user_key(user_id, 0, token.c_str(), secret.c_str())) {
+ printf("fscrypt_unlock_user_key returned fail\n");
+ return Free_Return(retval, weaver_key, &pwd);
+ }
+
+ if (!fscrypt_prepare_user_storage("", user_id, 0, flags)) {
+ printf("failed to fscrypt_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);
+}
+
+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) {
+ 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;
+ }
+ // In Android type 1 is pattern
+ // In Android <11 type 2 is PIN or password
+ // In Android 11+ type 3 is PIN and type 4 is password
+ if (pwd.password_type == 2) {
+ printf("password type: password/PIN\n");
+ return 1; // In TWRP this means password or PIN (Android <11)
+ } else if (pwd.password_type == 4) {
+ printf("password type: password\n");
+ return 1; // In TWRP this means password
+ } else if (pwd.password_type == 1) {
+ printf("password type: pattern\n");
+ return 2; // In TWRP this means pattern
+ } else if (pwd.password_type == 3) {
+ printf("password type: PIN\n");
+ return 3; // In TWRP this means PIN
+ }
+ printf("using default password\n");
+ return 0; // We'll try the default password
+ }
+ 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 = android::os::IVold::STORAGE_FLAG_CE;
+
+ if (Default_Password) {
+ if (!fscrypt_unlock_user_key(user_id, 0, "!", "!")) {
+ printf("unlock_user_key returned fail\n");
+ return false;
+ }
+ if (!fscrypt_prepare_user_storage("", user_id, 0, flags)) {
+ printf("failed to fscrypt_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) {
+ printf("Using synthetic password method\n");
+ return Decrypt_User_Synth_Pass(user_id, Password);
+ }
+ // 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;
+ 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;
+ }
+
+ 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 (!fscrypt_unlock_user_key(user_id, 0, token_hex, secret.c_str())) {
+ printf("fscrypt_unlock_user_key returned fail\n");
+ return false;
+ }
+
+ if (!fscrypt_prepare_user_storage("", user_id, 0, flags)) {
+ printf("failed to fscrypt_prepare_user_storage\n");
+ return false;
+ }
+ printf("User %i Decrypted Successfully!\n", user_id);
+ return true;
+}
diff --git a/crypto/fscrypt/Decrypt.h b/crypto/fscrypt/Decrypt.h
new file mode 100755
index 0000000..f397749
--- /dev/null
+++ b/crypto/fscrypt/Decrypt.h
@@ -0,0 +1,37 @@
+/*
+ * 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;
+static constexpr int STORAGE_FLAG_CE = 2;
+
+
+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/fscrypt/EncryptInplace.cpp b/crypto/fscrypt/EncryptInplace.cpp
new file mode 100644
index 0000000..9d304da
--- /dev/null
+++ b/crypto/fscrypt/EncryptInplace.cpp
@@ -0,0 +1,630 @@
+/*
+ * 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 "EncryptInplace.h"
+
+#include <ext4_utils/ext4.h>
+#include <ext4_utils/ext4_utils.h>
+#include <f2fs_sparseblock.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include <algorithm>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+
+// HORRIBLE HACK, FIXME
+#include "cryptfs.h"
+
+// FIXME horrible cut-and-paste code
+static inline int unix_read(int fd, void* buff, int len) {
+ return TEMP_FAILURE_RETRY(read(fd, buff, len));
+}
+
+static inline int unix_write(int fd, const void* buff, int len) {
+ return TEMP_FAILURE_RETRY(write(fd, buff, len));
+}
+
+#define CRYPT_SECTORS_PER_BUFSIZE (CRYPT_INPLACE_BUFSIZE / CRYPT_SECTOR_SIZE)
+
+/* aligned 32K writes tends to make flash happy.
+ * SD card association recommends it.
+ */
+#ifndef CONFIG_HW_DISK_ENCRYPTION
+#define BLOCKS_AT_A_TIME 8
+#else
+#define BLOCKS_AT_A_TIME 1024
+#endif
+
+struct encryptGroupsData {
+ int realfd;
+ int cryptofd;
+ off64_t numblocks;
+ off64_t one_pct, cur_pct, new_pct;
+ off64_t blocks_already_done, tot_numblocks;
+ off64_t used_blocks_already_done, tot_used_blocks;
+ const char* real_blkdev;
+ const char* crypto_blkdev;
+ int count;
+ off64_t offset;
+ char* buffer;
+ off64_t last_written_sector;
+ int completed;
+ time_t time_started;
+ int remaining_time;
+ bool set_progress_properties;
+};
+
+static void update_progress(struct encryptGroupsData* data, int is_used) {
+ data->blocks_already_done++;
+
+ if (is_used) {
+ data->used_blocks_already_done++;
+ }
+ if (data->tot_used_blocks) {
+ data->new_pct = data->used_blocks_already_done / data->one_pct;
+ } else {
+ data->new_pct = data->blocks_already_done / data->one_pct;
+ }
+
+ if (!data->set_progress_properties) return;
+
+ if (data->new_pct > data->cur_pct) {
+ char buf[8];
+ data->cur_pct = data->new_pct;
+ snprintf(buf, sizeof(buf), "%" PRId64, data->cur_pct);
+ android::base::SetProperty("vold.encrypt_progress", buf);
+ }
+
+ if (data->cur_pct >= 5) {
+ struct timespec time_now;
+ if (clock_gettime(CLOCK_MONOTONIC, &time_now)) {
+ LOG(WARNING) << "Error getting time";
+ } else {
+ double elapsed_time = difftime(time_now.tv_sec, data->time_started);
+ off64_t remaining_blocks = data->tot_used_blocks - data->used_blocks_already_done;
+ int remaining_time =
+ (int)(elapsed_time * remaining_blocks / data->used_blocks_already_done);
+
+ // Change time only if not yet set, lower, or a lot higher for
+ // best user experience
+ if (data->remaining_time == -1 || remaining_time < data->remaining_time ||
+ remaining_time > data->remaining_time + 60) {
+ char buf[8];
+ snprintf(buf, sizeof(buf), "%d", remaining_time);
+ android::base::SetProperty("vold.encrypt_time_remaining", buf);
+ data->remaining_time = remaining_time;
+ }
+ }
+ }
+}
+
+static void log_progress(struct encryptGroupsData const* data, bool completed) {
+ // Precondition - if completed data = 0 else data != 0
+
+ // Track progress so we can skip logging blocks
+ static off64_t offset = -1;
+
+ // Need to close existing 'Encrypting from' log?
+ if (completed || (offset != -1 && data->offset != offset)) {
+ LOG(INFO) << "Encrypted to sector " << offset / info.block_size * CRYPT_SECTOR_SIZE;
+ offset = -1;
+ }
+
+ // Need to start new 'Encrypting from' log?
+ if (!completed && offset != data->offset) {
+ LOG(INFO) << "Encrypting from sector " << data->offset / info.block_size * CRYPT_SECTOR_SIZE;
+ }
+
+ // Update offset
+ if (!completed) {
+ offset = data->offset + (off64_t)data->count * info.block_size;
+ }
+}
+
+static int flush_outstanding_data(struct encryptGroupsData* data) {
+ if (data->count == 0) {
+ return 0;
+ }
+
+ LOG(DEBUG) << "Copying " << data->count << " blocks at offset " << data->offset;
+
+ if (pread64(data->realfd, data->buffer, info.block_size * data->count, data->offset) <= 0) {
+ LOG(ERROR) << "Error reading real_blkdev " << data->real_blkdev << " for inplace encrypt";
+ return -1;
+ }
+
+ if (pwrite64(data->cryptofd, data->buffer, info.block_size * data->count, data->offset) <= 0) {
+ LOG(ERROR) << "Error writing crypto_blkdev " << data->crypto_blkdev
+ << " for inplace encrypt";
+ return -1;
+ } else {
+ log_progress(data, false);
+ }
+
+ data->count = 0;
+ data->last_written_sector =
+ (data->offset + data->count) / info.block_size * CRYPT_SECTOR_SIZE - 1;
+ return 0;
+}
+
+static int encrypt_groups(struct encryptGroupsData* data) {
+ unsigned int i;
+ u8* block_bitmap = 0;
+ unsigned int block;
+ off64_t ret;
+ int rc = -1;
+
+ data->buffer = (char*)malloc(info.block_size * BLOCKS_AT_A_TIME);
+ if (!data->buffer) {
+ LOG(ERROR) << "Failed to allocate crypto buffer";
+ goto errout;
+ }
+
+ block_bitmap = (u8*)malloc(info.block_size);
+ if (!block_bitmap) {
+ LOG(ERROR) << "failed to allocate block bitmap";
+ goto errout;
+ }
+
+ for (i = 0; i < aux_info.groups; ++i) {
+ LOG(INFO) << "Encrypting group " << i;
+
+ u32 first_block = aux_info.first_data_block + i * info.blocks_per_group;
+ u32 block_count = std::min(info.blocks_per_group, (u32)(aux_info.len_blocks - first_block));
+
+ off64_t offset = (u64)info.block_size * aux_info.bg_desc[i].bg_block_bitmap;
+
+ ret = pread64(data->realfd, block_bitmap, info.block_size, offset);
+ if (ret != (int)info.block_size) {
+ LOG(ERROR) << "failed to read all of block group bitmap " << i;
+ goto errout;
+ }
+
+ offset = (u64)info.block_size * first_block;
+
+ data->count = 0;
+
+ for (block = 0; block < block_count; block++) {
+ int used = (aux_info.bg_desc[i].bg_flags & EXT4_BG_BLOCK_UNINIT)
+ ? 0
+ : bitmap_get_bit(block_bitmap, block);
+ update_progress(data, used);
+ if (used) {
+ if (data->count == 0) {
+ data->offset = offset;
+ }
+ data->count++;
+ } else {
+ if (flush_outstanding_data(data)) {
+ goto errout;
+ }
+ }
+
+ offset += info.block_size;
+
+ /* Write data if we are aligned or buffer size reached */
+ if (offset % (info.block_size * BLOCKS_AT_A_TIME) == 0 ||
+ data->count == BLOCKS_AT_A_TIME) {
+ if (flush_outstanding_data(data)) {
+ goto errout;
+ }
+ }
+ }
+ if (flush_outstanding_data(data)) {
+ goto errout;
+ }
+ }
+
+ data->completed = 1;
+ rc = 0;
+
+errout:
+ log_progress(0, true);
+ free(data->buffer);
+ free(block_bitmap);
+ return rc;
+}
+
+static int cryptfs_enable_inplace_ext4(const char* crypto_blkdev, const char* real_blkdev,
+ off64_t size, off64_t* size_already_done, off64_t tot_size,
+ off64_t previously_encrypted_upto,
+ bool set_progress_properties) {
+ u32 i;
+ struct encryptGroupsData data;
+ int rc; // Can't initialize without causing warning -Wclobbered
+ int retries = RETRY_MOUNT_ATTEMPTS;
+ struct timespec time_started = {0};
+
+ if (previously_encrypted_upto > *size_already_done) {
+ LOG(DEBUG) << "Not fast encrypting since resuming part way through";
+ return -1;
+ }
+
+ memset(&data, 0, sizeof(data));
+ data.real_blkdev = real_blkdev;
+ data.crypto_blkdev = crypto_blkdev;
+ data.set_progress_properties = set_progress_properties;
+
+ LOG(DEBUG) << "Opening" << real_blkdev;
+ if ((data.realfd = open(real_blkdev, O_RDWR | O_CLOEXEC)) < 0) {
+ PLOG(ERROR) << "Error opening real_blkdev " << real_blkdev << " for inplace encrypt";
+ rc = -1;
+ goto errout;
+ }
+
+ LOG(DEBUG) << "Opening" << crypto_blkdev;
+ // Wait until the block device appears. Re-use the mount retry values since it is reasonable.
+ while ((data.cryptofd = open(crypto_blkdev, O_WRONLY | O_CLOEXEC)) < 0) {
+ if (--retries) {
+ PLOG(ERROR) << "Error opening crypto_blkdev " << crypto_blkdev
+ << " for ext4 inplace encrypt, retrying";
+ sleep(RETRY_MOUNT_DELAY_SECONDS);
+ } else {
+ PLOG(ERROR) << "Error opening crypto_blkdev " << crypto_blkdev
+ << " for ext4 inplace encrypt";
+ rc = ENABLE_INPLACE_ERR_DEV;
+ goto errout;
+ }
+ }
+
+ if (setjmp(setjmp_env)) { // NOLINT
+ LOG(ERROR) << "Reading ext4 extent caused an exception";
+ rc = -1;
+ goto errout;
+ }
+
+ if (read_ext(data.realfd, 0) != 0) {
+ LOG(ERROR) << "Failed to read ext4 extent";
+ rc = -1;
+ goto errout;
+ }
+
+ data.numblocks = size / CRYPT_SECTORS_PER_BUFSIZE;
+ data.tot_numblocks = tot_size / CRYPT_SECTORS_PER_BUFSIZE;
+ data.blocks_already_done = *size_already_done / CRYPT_SECTORS_PER_BUFSIZE;
+
+ LOG(INFO) << "Encrypting ext4 filesystem in place...";
+
+ data.tot_used_blocks = data.numblocks;
+ for (i = 0; i < aux_info.groups; ++i) {
+ data.tot_used_blocks -= aux_info.bg_desc[i].bg_free_blocks_count;
+ }
+
+ data.one_pct = data.tot_used_blocks / 100;
+ data.cur_pct = 0;
+
+ if (clock_gettime(CLOCK_MONOTONIC, &time_started)) {
+ LOG(WARNING) << "Error getting time at start";
+ // Note - continue anyway - we'll run with 0
+ }
+ data.time_started = time_started.tv_sec;
+ data.remaining_time = -1;
+
+ rc = encrypt_groups(&data);
+ if (rc) {
+ LOG(ERROR) << "Error encrypting groups";
+ goto errout;
+ }
+
+ *size_already_done += data.completed ? size : data.last_written_sector;
+ rc = 0;
+
+errout:
+ close(data.realfd);
+ close(data.cryptofd);
+
+ return rc;
+}
+
+static void log_progress_f2fs(u64 block, bool completed) {
+ // Precondition - if completed data = 0 else data != 0
+
+ // Track progress so we can skip logging blocks
+ static u64 last_block = (u64)-1;
+
+ // Need to close existing 'Encrypting from' log?
+ if (completed || (last_block != (u64)-1 && block != last_block + 1)) {
+ LOG(INFO) << "Encrypted to block " << last_block;
+ last_block = -1;
+ }
+
+ // Need to start new 'Encrypting from' log?
+ if (!completed && (last_block == (u64)-1 || block != last_block + 1)) {
+ LOG(INFO) << "Encrypting from block " << block;
+ }
+
+ // Update offset
+ if (!completed) {
+ last_block = block;
+ }
+}
+
+static int encrypt_one_block_f2fs(u64 pos, void* data) {
+ struct encryptGroupsData* priv_dat = (struct encryptGroupsData*)data;
+
+ priv_dat->blocks_already_done = pos - 1;
+ update_progress(priv_dat, 1);
+
+ off64_t offset = pos * CRYPT_INPLACE_BUFSIZE;
+
+ if (pread64(priv_dat->realfd, priv_dat->buffer, CRYPT_INPLACE_BUFSIZE, offset) <= 0) {
+ LOG(ERROR) << "Error reading real_blkdev " << priv_dat->crypto_blkdev
+ << " for f2fs inplace encrypt";
+ return -1;
+ }
+
+ if (pwrite64(priv_dat->cryptofd, priv_dat->buffer, CRYPT_INPLACE_BUFSIZE, offset) <= 0) {
+ LOG(ERROR) << "Error writing crypto_blkdev " << priv_dat->crypto_blkdev
+ << " for f2fs inplace encrypt";
+ return -1;
+ } else {
+ log_progress_f2fs(pos, false);
+ }
+
+ return 0;
+}
+
+static int cryptfs_enable_inplace_f2fs(const char* crypto_blkdev, const char* real_blkdev,
+ off64_t size, off64_t* size_already_done, off64_t tot_size,
+ off64_t previously_encrypted_upto,
+ bool set_progress_properties) {
+ struct encryptGroupsData data;
+ struct f2fs_info* f2fs_info = NULL;
+ int rc = ENABLE_INPLACE_ERR_OTHER;
+ struct timespec time_started = {0};
+
+ if (previously_encrypted_upto > *size_already_done) {
+ LOG(DEBUG) << "Not fast encrypting since resuming part way through";
+ return ENABLE_INPLACE_ERR_OTHER;
+ }
+ memset(&data, 0, sizeof(data));
+ data.real_blkdev = real_blkdev;
+ data.crypto_blkdev = crypto_blkdev;
+ data.set_progress_properties = set_progress_properties;
+ data.realfd = -1;
+ data.cryptofd = -1;
+ if ((data.realfd = open64(real_blkdev, O_RDWR | O_CLOEXEC)) < 0) {
+ PLOG(ERROR) << "Error opening real_blkdev " << real_blkdev << " for f2fs inplace encrypt";
+ goto errout;
+ }
+ if ((data.cryptofd = open64(crypto_blkdev, O_WRONLY | O_CLOEXEC)) < 0) {
+ PLOG(ERROR) << "Error opening crypto_blkdev " << crypto_blkdev
+ << " for f2fs inplace encrypt";
+ rc = ENABLE_INPLACE_ERR_DEV;
+ goto errout;
+ }
+
+ f2fs_info = generate_f2fs_info(data.realfd);
+ if (!f2fs_info) goto errout;
+
+ data.numblocks = size / CRYPT_SECTORS_PER_BUFSIZE;
+ data.tot_numblocks = tot_size / CRYPT_SECTORS_PER_BUFSIZE;
+ data.blocks_already_done = *size_already_done / CRYPT_SECTORS_PER_BUFSIZE;
+
+ data.tot_used_blocks = get_num_blocks_used(f2fs_info);
+
+ data.one_pct = data.tot_used_blocks / 100;
+ data.cur_pct = 0;
+ if (clock_gettime(CLOCK_MONOTONIC, &time_started)) {
+ LOG(WARNING) << "Error getting time at start";
+ // Note - continue anyway - we'll run with 0
+ }
+ data.time_started = time_started.tv_sec;
+ data.remaining_time = -1;
+
+
+ data.buffer = (char*)malloc(f2fs_info->block_size);
+ if (!data.buffer) {
+ LOG(ERROR) << "Failed to allocate crypto buffer";
+ goto errout;
+ }
+
+ data.count = 0;
+
+ /* Currently, this either runs to completion, or hits a nonrecoverable error */
+ rc = run_on_used_blocks(data.blocks_already_done, f2fs_info, &encrypt_one_block_f2fs, &data);
+
+ if (rc) {
+ LOG(ERROR) << "Error in running over f2fs blocks";
+ rc = ENABLE_INPLACE_ERR_OTHER;
+ goto errout;
+ }
+
+ *size_already_done += size;
+ rc = 0;
+
+errout:
+ if (rc) LOG(ERROR) << "Failed to encrypt f2fs filesystem on " << real_blkdev;
+
+ log_progress_f2fs(0, true);
+ free(f2fs_info);
+ free(data.buffer);
+ close(data.realfd);
+ close(data.cryptofd);
+
+ return rc;
+}
+
+static int cryptfs_enable_inplace_full(const char* crypto_blkdev, const char* real_blkdev,
+ off64_t size, off64_t* size_already_done, off64_t tot_size,
+ off64_t previously_encrypted_upto,
+ bool set_progress_properties) {
+ int realfd, cryptofd;
+ char* buf[CRYPT_INPLACE_BUFSIZE];
+ int rc = ENABLE_INPLACE_ERR_OTHER;
+ off64_t numblocks, i, remainder;
+ off64_t one_pct, cur_pct, new_pct;
+ off64_t blocks_already_done, tot_numblocks;
+
+ if ((realfd = open(real_blkdev, O_RDONLY | O_CLOEXEC)) < 0) {
+ PLOG(ERROR) << "Error opening real_blkdev " << real_blkdev << " for inplace encrypt";
+ return ENABLE_INPLACE_ERR_OTHER;
+ }
+
+ if ((cryptofd = open(crypto_blkdev, O_WRONLY | O_CLOEXEC)) < 0) {
+ PLOG(ERROR) << "Error opening crypto_blkdev " << crypto_blkdev << " for inplace encrypt";
+ close(realfd);
+ return ENABLE_INPLACE_ERR_DEV;
+ }
+
+ /* This is pretty much a simple loop of reading 4K, and writing 4K.
+ * The size passed in is the number of 512 byte sectors in the filesystem.
+ * So compute the number of whole 4K blocks we should read/write,
+ * and the remainder.
+ */
+ numblocks = size / CRYPT_SECTORS_PER_BUFSIZE;
+ remainder = size % CRYPT_SECTORS_PER_BUFSIZE;
+ tot_numblocks = tot_size / CRYPT_SECTORS_PER_BUFSIZE;
+ blocks_already_done = *size_already_done / CRYPT_SECTORS_PER_BUFSIZE;
+
+ LOG(ERROR) << "Encrypting filesystem in place...";
+
+ i = previously_encrypted_upto + 1 - *size_already_done;
+
+ if (lseek64(realfd, i * CRYPT_SECTOR_SIZE, SEEK_SET) < 0) {
+ PLOG(ERROR) << "Cannot seek to previously encrypted point on " << real_blkdev;
+ goto errout;
+ }
+
+ if (lseek64(cryptofd, i * CRYPT_SECTOR_SIZE, SEEK_SET) < 0) {
+ PLOG(ERROR) << "Cannot seek to previously encrypted point on " << crypto_blkdev;
+ goto errout;
+ }
+
+ for (; i < size && i % CRYPT_SECTORS_PER_BUFSIZE != 0; ++i) {
+ if (unix_read(realfd, buf, CRYPT_SECTOR_SIZE) <= 0) {
+ PLOG(ERROR) << "Error reading initial sectors from real_blkdev " << real_blkdev
+ << " for inplace encrypt";
+ goto errout;
+ }
+ if (unix_write(cryptofd, buf, CRYPT_SECTOR_SIZE) <= 0) {
+ PLOG(ERROR) << "Error writing initial sectors to crypto_blkdev " << crypto_blkdev
+ << " for inplace encrypt";
+ goto errout;
+ } else {
+ LOG(INFO) << "Encrypted 1 block at " << i;
+ }
+ }
+
+ one_pct = tot_numblocks / 100;
+ cur_pct = 0;
+ /* process the majority of the filesystem in blocks */
+ for (i /= CRYPT_SECTORS_PER_BUFSIZE; i < numblocks; i++) {
+ new_pct = (i + blocks_already_done) / one_pct;
+ if (set_progress_properties && new_pct > cur_pct) {
+ char property_buf[8];
+
+ cur_pct = new_pct;
+ snprintf(property_buf, sizeof(property_buf), "%" PRId64, cur_pct);
+ android::base::SetProperty("vold.encrypt_progress", property_buf);
+ }
+ if (unix_read(realfd, buf, CRYPT_INPLACE_BUFSIZE) <= 0) {
+ PLOG(ERROR) << "Error reading real_blkdev " << real_blkdev << " for inplace encrypt";
+ goto errout;
+ }
+ if (unix_write(cryptofd, buf, CRYPT_INPLACE_BUFSIZE) <= 0) {
+ PLOG(ERROR) << "Error writing crypto_blkdev " << crypto_blkdev << " for inplace encrypt";
+ goto errout;
+ } else {
+ LOG(DEBUG) << "Encrypted " << CRYPT_SECTORS_PER_BUFSIZE << " block at "
+ << i * CRYPT_SECTORS_PER_BUFSIZE;
+ }
+ }
+
+ /* Do any remaining sectors */
+ for (i = 0; i < remainder; i++) {
+ if (unix_read(realfd, buf, CRYPT_SECTOR_SIZE) <= 0) {
+ LOG(ERROR) << "Error reading final sectors from real_blkdev " << real_blkdev
+ << " for inplace encrypt";
+ goto errout;
+ }
+ if (unix_write(cryptofd, buf, CRYPT_SECTOR_SIZE) <= 0) {
+ LOG(ERROR) << "Error writing final sectors to crypto_blkdev " << crypto_blkdev
+ << " for inplace encrypt";
+ goto errout;
+ } else {
+ LOG(INFO) << "Encrypted 1 block at next location";
+ }
+ }
+
+ *size_already_done += size;
+ rc = 0;
+
+errout:
+ close(realfd);
+ close(cryptofd);
+
+ return rc;
+}
+
+/* returns on of the ENABLE_INPLACE_* return codes */
+int cryptfs_enable_inplace(const char* crypto_blkdev, const char* real_blkdev, off64_t size,
+ off64_t* size_already_done, off64_t tot_size,
+ off64_t previously_encrypted_upto, bool set_progress_properties) {
+ int rc_ext4, rc_f2fs, rc_full;
+ LOG(DEBUG) << "cryptfs_enable_inplace(" << crypto_blkdev << ", " << real_blkdev << ", " << size
+ << ", " << size_already_done << ", " << tot_size << ", " << previously_encrypted_upto
+ << ", " << set_progress_properties << ")";
+ if (previously_encrypted_upto) {
+ LOG(DEBUG) << "Continuing encryption from " << previously_encrypted_upto;
+ }
+
+ if (*size_already_done + size < previously_encrypted_upto) {
+ LOG(DEBUG) << "cryptfs_enable_inplace already done";
+ *size_already_done += size;
+ return 0;
+ }
+
+ /* TODO: identify filesystem type.
+ * As is, cryptfs_enable_inplace_ext4 will fail on an f2fs partition, and
+ * then we will drop down to cryptfs_enable_inplace_f2fs.
+ * */
+ if ((rc_ext4 = cryptfs_enable_inplace_ext4(crypto_blkdev, real_blkdev, size, size_already_done,
+ tot_size, previously_encrypted_upto,
+ set_progress_properties)) == 0) {
+ LOG(DEBUG) << "cryptfs_enable_inplace_ext4 success";
+ return 0;
+ }
+ LOG(DEBUG) << "cryptfs_enable_inplace_ext4()=" << rc_ext4;
+
+ if ((rc_f2fs = cryptfs_enable_inplace_f2fs(crypto_blkdev, real_blkdev, size, size_already_done,
+ tot_size, previously_encrypted_upto,
+ set_progress_properties)) == 0) {
+ LOG(DEBUG) << "cryptfs_enable_inplace_f2fs success";
+ return 0;
+ }
+ LOG(DEBUG) << "cryptfs_enable_inplace_f2fs()=" << rc_f2fs;
+
+ rc_full =
+ cryptfs_enable_inplace_full(crypto_blkdev, real_blkdev, size, size_already_done, tot_size,
+ previously_encrypted_upto, set_progress_properties);
+ LOG(DEBUG) << "cryptfs_enable_inplace_full()=" << rc_full;
+
+ /* Hack for b/17898962, the following is the symptom... */
+ if (rc_ext4 == ENABLE_INPLACE_ERR_DEV && rc_f2fs == ENABLE_INPLACE_ERR_DEV &&
+ rc_full == ENABLE_INPLACE_ERR_DEV) {
+ LOG(DEBUG) << "ENABLE_INPLACE_ERR_DEV";
+ return ENABLE_INPLACE_ERR_DEV;
+ }
+ return rc_full;
+}
diff --git a/crypto/fscrypt/EncryptInplace.h b/crypto/fscrypt/EncryptInplace.h
new file mode 100644
index 0000000..a2b46cf
--- /dev/null
+++ b/crypto/fscrypt/EncryptInplace.h
@@ -0,0 +1,36 @@
+/*
+ * 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 _ENCRYPT_INPLACE_H
+#define _ENCRYPT_INPLACE_H
+
+#include <sys/types.h>
+
+#define CRYPT_INPLACE_BUFSIZE 4096
+#define CRYPT_SECTOR_SIZE 512
+#define RETRY_MOUNT_ATTEMPTS 10
+#define RETRY_MOUNT_DELAY_SECONDS 1
+
+/* Return values for cryptfs_enable_inplace() */
+#define ENABLE_INPLACE_OK 0
+#define ENABLE_INPLACE_ERR_OTHER (-1)
+#define ENABLE_INPLACE_ERR_DEV (-2) /* crypto_blkdev issue */
+
+int cryptfs_enable_inplace(const char* crypto_blkdev, const char* real_blkdev, off64_t size,
+ off64_t* size_already_done, off64_t tot_size,
+ off64_t previously_encrypted_upto, bool set_progress_properties);
+
+#endif
diff --git a/crypto/fscrypt/FsCrypt.cpp b/crypto/fscrypt/FsCrypt.cpp
new file mode 100755
index 0000000..6284fa8
--- /dev/null
+++ b/crypto/fscrypt/FsCrypt.cpp
@@ -0,0 +1,1038 @@
+/*
+ * 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 "FsCrypt.h"
+
+#include "KeyStorage.h"
+#include "KeyUtil.h"
+#include "Utils.h"
+#include "VoldUtil.h"
+
+#include <algorithm>
+#include <map>
+#include <optional>
+#include <set>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <selinux/android.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <private/android_filesystem_config.h>
+#include <private/android_projectid_config.h>
+
+#include "android/os/IVold.h"
+
+#define EMULATED_USES_SELINUX 0
+#define MANAGE_MISC_DIRS 0
+
+#include <cutils/fs.h>
+#include <cutils/properties.h>
+
+#include <fscrypt/fscrypt.h>
+#include <keyutils.h>
+#include <libdm/dm.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+
+#include "fscrypt-common.h"
+
+using android::base::Basename;
+using android::base::Realpath;
+using android::base::StartsWith;
+using android::base::StringPrintf;
+using android::fs_mgr::GetEntryForMountPoint;
+using ::BuildDataPath;
+using ::IsFilesystemSupported;
+using ::kEmptyAuthentication;
+using ::KeyBuffer;
+using ::KeyGeneration;
+using ::retrieveKey;
+using ::retrieveOrGenerateKey;
+using ::SetQuotaInherit;
+using ::SetQuotaProjectId;
+using ::writeStringToFile;
+using namespace android::fscrypt;
+using namespace android::dm;
+
+// Map user ids to encryption policies
+std::map<userid_t, EncryptionPolicy> s_de_policies;
+std::map<userid_t, EncryptionPolicy> s_ce_policies;
+std::string de_key_raw_ref;
+
+namespace {
+
+const std::string device_key_dir = std::string() + DATA_MNT_POINT + fscrypt_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";
+
+// Some users are ephemeral, don't try to wipe their keys from disk
+std::set<userid_t> s_ephemeral_users;
+
+} // namespace
+
+// Returns KeyGeneration suitable for key as described in EncryptionOptions
+static KeyGeneration makeGen(const EncryptionOptions& options) {
+ return KeyGeneration{FSCRYPT_MAX_KEY_SIZE, true, options.use_hw_wrapped_key};
+}
+
+static bool fscrypt_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;
+ 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(INFO) << "Skipping non-key " << entry->d_name;
+ 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";
+}
+
+static bool get_ce_key_new_path(const std::string& directory_path,
+ const std::vector<std::string>& paths, std::string* ce_key_path) {
+ if (paths.empty()) {
+ *ce_key_path = get_ce_key_current_path(directory_path);
+ return true;
+ }
+ for (unsigned int i = 0; i < UINT_MAX; i++) {
+ auto const candidate = StringPrintf("%s/cx%010u", directory_path.c_str(), i);
+ if (paths[0] < candidate) {
+ *ce_key_path = candidate;
+ return true;
+ }
+ }
+ return false;
+}
+
+// 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) {
+ ::destroyKey(other_path);
+ }
+ }
+ auto const current_path = get_ce_key_current_path(directory_path);
+ if (to_fix != current_path) {
+ LOG(INFO) << "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;
+ return;
+ }
+ }
+ ::FsyncDirectory(directory_path);
+}
+
+static bool read_and_fixate_user_ce_key(userid_t user_id,
+ const ::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(INFO) << "Trying user CE key " << ce_key_path;
+ if (retrieveKey(ce_key_path, auth, ce_key, true)) {
+ LOG(INFO) << "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 IsEmmcStorage(const std::string& blk_device) {
+ // Handle symlinks.
+ std::string real_path;
+ if (!Realpath(blk_device, &real_path)) {
+ real_path = blk_device;
+ }
+
+ // Handle logical volumes.
+ auto& dm = DeviceMapper::Instance();
+ for (;;) {
+ auto parent = dm.GetParentBlockDeviceByPath(real_path);
+ if (!parent.has_value()) break;
+ real_path = *parent;
+ }
+
+ // Now we should have the "real" block device.
+ LOG(INFO) << "IsEmmcStorage(): blk_device = " << blk_device << ", real_path=" << real_path;
+ return StartsWith(Basename(real_path), "mmcblk");
+}
+
+// Retrieve the options to use for encryption policies on the /data filesystem.
+static bool get_data_file_encryption_options(EncryptionOptions* options) {
+ if (fstab_default.empty()) {
+ if (!ReadDefaultFstab(&fstab_default)) {
+ PLOG(ERROR) << "Failed to open default fstab";
+ return false;
+ }
+ }
+ auto entry = GetEntryForMountPoint(&fstab_default, DATA_MNT_POINT);
+ if (entry == nullptr) {
+ LOG(ERROR) << "No mount point entry for " << DATA_MNT_POINT;
+ return false;
+ }
+ if (!ParseOptions(entry->encryption_options, options)) {
+ LOG(ERROR) << "Unable to parse encryption options for " << DATA_MNT_POINT ": "
+ << entry->encryption_options;
+ return false;
+ }
+ if (options->version == 1) {
+ options->use_hw_wrapped_key =
+ GetEntryForMountPoint(&fstab_default, DATA_MNT_POINT)->fs_mgr_flags.wrapped_key;
+ }
+ if ((options->flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32) &&
+ !IsEmmcStorage(entry->blk_device)) {
+ LOG(ERROR) << "The emmc_optimized encryption flag is only allowed on eMMC storage. Remove "
+ "this flag from the device's fstab";
+ return false;
+ }
+ return true;
+}
+
+static bool install_storage_key(const std::string& mountpoint, const EncryptionOptions& options,
+ const KeyBuffer& key, EncryptionPolicy* policy) {
+ KeyBuffer ephemeral_wrapped_key;
+ if (options.use_hw_wrapped_key) {
+ if (!exportWrappedStorageKey(key, &ephemeral_wrapped_key)) {
+ LOG(ERROR) << "Failed to get ephemeral wrapped key";
+ return false;
+ }
+ }
+ return installKey(mountpoint, options, options.use_hw_wrapped_key ? ephemeral_wrapped_key : key,
+ policy);
+}
+
+// Retrieve the options to use for encryption policies on adoptable storage.
+static bool get_volume_file_encryption_options(EncryptionOptions* options) {
+ // If we give the empty string, libfscrypt will use the default (currently XTS)
+ auto contents_mode = android::base::GetProperty("ro.crypto.volume.contents_mode", "");
+ // HEH as default was always a mistake. Use the libfscrypt default (CTS)
+ // for devices launching on versions above Android 10.
+ auto first_api_level = GetFirstApiLevel();
+ constexpr uint64_t pre_gki_level = 29;
+ auto filenames_mode =
+ android::base::GetProperty("ro.crypto.volume.filenames_mode",
+ first_api_level > pre_gki_level ? "" : "aes-256-heh");
+ auto options_string = android::base::GetProperty("ro.crypto.volume.options",
+ contents_mode + ":" + filenames_mode);
+ if (!ParseOptionsForApiLevel(first_api_level, options_string, options)) {
+ LOG(ERROR) << "Unable to parse volume encryption options: " << options_string;
+ return false;
+ }
+ if (options->flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32) {
+ LOG(ERROR) << "The emmc_optimized encryption flag is only allowed on eMMC storage. Remove "
+ "this flag from ro.crypto.volume.options";
+ return false;
+ }
+ return true;
+}
+
+bool is_metadata_wrapped_key_supported() {
+ if (fstab_default.empty()) {
+ if (!ReadDefaultFstab(&fstab_default)) {
+ PLOG(ERROR) << "Failed to open default fstab";
+ return false;
+ }
+ }
+ return GetEntryForMountPoint(&fstab_default, METADATA_MNT_POINT)->fs_mgr_flags.wrapped_key;
+}
+
+static bool read_and_install_user_ce_key(userid_t user_id,
+ const ::KeyAuthentication& auth) {
+ if (s_ce_policies.count(user_id) != 0) return true;
+ EncryptionOptions options;
+ if (!get_data_file_encryption_options(&options)) return false;
+ KeyBuffer ce_key;
+ if (!read_and_fixate_user_ce_key(user_id, auth, &ce_key)) return false;
+ EncryptionPolicy ce_policy;
+ if (!install_storage_key(DATA_MNT_POINT, options, ce_key, &ce_policy)) return false;
+ s_ce_policies[user_id] = ce_policy;
+ LOG(INFO) << "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(INFO) << "Preparing: " << dir;
+ if (fs_prepare_dir(dir.c_str(), mode, uid, gid) != 0) {
+ PLOG(ERROR) << "Failed to prepare " << dir;
+ return false;
+ }
+ return true;
+}
+
+static bool destroy_dir(const std::string& dir) {
+ LOG(INFO) << "Destroying: " << dir;
+ if (rmdir(dir.c_str()) != 0 && errno != ENOENT) {
+ PLOG(ERROR) << "Failed to destroy " << dir;
+ 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) {
+ LOG(INFO) << "fscrypt::create_and_install_user_keys";
+ EncryptionOptions options;
+ if (!get_data_file_encryption_options(&options)) return false;
+ KeyBuffer de_key, ce_key;
+ if (!generateStorageKey(makeGen(options), &de_key)) return false;
+ if (!generateStorageKey(makeGen(options), &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 (!::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 (!::storeKeyAtomically(get_de_key_path(user_id), user_key_temp,
+ kEmptyAuthentication, de_key))
+ return false;
+ }
+ EncryptionPolicy de_policy;
+ if (!install_storage_key(DATA_MNT_POINT, options, de_key, &de_policy)) return false;
+ s_de_policies[user_id] = de_policy;
+ LOG(INFO) << "fscrypt::added de_policy";
+ EncryptionPolicy ce_policy;
+ if (!install_storage_key(DATA_MNT_POINT, options, ce_key, &ce_policy)) return false;
+ s_ce_policies[user_id] = ce_policy;
+ LOG(INFO) << "Created keys for user " << user_id;
+ return true;
+}
+
+bool lookup_policy(const std::map<userid_t, EncryptionPolicy>& key_map, userid_t user_id,
+ EncryptionPolicy* policy) {
+ auto refi = key_map.find(user_id);
+ if (refi == key_map.end()) {
+ LOG(ERROR) << "Cannot find key for " << user_id;
+ return false;
+ }
+ *policy = 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() {
+ LOG(INFO) << "fscrypt::load_all_de_keys";
+ EncryptionOptions options;
+ if (!get_data_file_encryption_options(&options)) return false;
+ 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(INFO) << "Skipping non-de-key " << entry->d_name;
+ continue;
+ }
+ userid_t user_id = std::stoi(entry->d_name);
+ auto key_path = de_dir + "/" + entry->d_name;
+ KeyBuffer de_key;
+ if (!retrieveKey(key_path, kEmptyAuthentication, &de_key, true)) return false;
+ EncryptionPolicy de_policy;
+ if (!install_storage_key(DATA_MNT_POINT, options, de_key, &de_policy)) return false;
+ auto ret = s_de_policies.insert({user_id, de_policy});
+ LOG(INFO) << "fscrypt::load_all_de_keys::s_de_policies::size::" << s_de_policies.size();
+ if (!ret.second && ret.first->second != de_policy) {
+ LOG(INFO) << "DE policy for user" << user_id << " changed";
+ return false;
+ }
+ LOG(INFO) << "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");
+ }
+ // fscrypt: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;
+}
+
+// Attempt to reinstall CE keys for users that we think are unlocked.
+static bool try_reload_ce_keys() {
+ for (const auto& it : s_ce_policies) {
+ if (!::reloadKeyFromSessionKeyring(DATA_MNT_POINT, it.second)) {
+ LOG(ERROR) << "Failed to load CE key from session keyring for user " << it.first;
+ return false;
+ }
+ }
+ return true;
+}
+
+bool fscrypt_initialize_systemwide_keys() {
+ LOG(INFO) << "fscrypt_initialize_systemwide_keys";
+
+ EncryptionOptions options;
+ if (!get_data_file_encryption_options(&options)) return false;
+
+ KeyBuffer device_key;
+ if (!retrieveOrGenerateKey(device_key_path, device_key_temp, kEmptyAuthentication,
+ makeGen(options), &device_key))
+ return false;
+
+ EncryptionPolicy device_policy;
+ if (!install_storage_key(DATA_MNT_POINT, options, device_key, &device_policy)) return false;
+
+ std::string options_string;
+ if (!OptionsToString(device_policy.options, &options_string)) {
+ LOG(ERROR) << "Unable to serialize options";
+ return false;
+ }
+ std::string options_filename = std::string(DATA_MNT_POINT) + fscrypt_key_mode;
+ if (!::writeStringToFile(options_string, options_filename)) return false;
+
+ std::string ref_filename = std::string(DATA_MNT_POINT) + fscrypt_key_ref;
+ de_key_raw_ref = device_policy.key_raw_ref;
+ if (!::writeStringToFile(device_policy.key_raw_ref, ref_filename)) return false;
+ LOG(INFO) << "Wrote system DE key reference to:" << ref_filename;
+
+ KeyBuffer per_boot_key;
+ if (!generateStorageKey(makeGen(options), &per_boot_key)) return false;
+ EncryptionPolicy per_boot_policy;
+ if (!install_storage_key(DATA_MNT_POINT, options, per_boot_key, &per_boot_policy)) return false;
+ std::string per_boot_ref_filename = std::string("/data") + fscrypt_key_per_boot_ref;
+ if (!::writeStringToFile(per_boot_policy.key_raw_ref, per_boot_ref_filename))
+ return false;
+ LOG(INFO) << "Wrote per boot key reference to:" << per_boot_ref_filename;
+
+ if (!::FsyncDirectory(device_key_dir)) return false;
+ return true;
+}
+
+bool fscrypt_init_user0() {
+ if (fstab_default.empty()) {
+ if (!ReadDefaultFstab(&fstab_default)) {
+ PLOG(ERROR) << "Failed to open default fstab";
+ return -1;
+ }
+ }
+ LOG(INFO) << "fscrypt_init_user0";
+ if (fscrypt_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 (!::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;
+ }
+ // We can only safely prepare DE storage here, since CE keys are probably
+ // entangled with user credentials. The framework will always prepare CE
+ // storage once CE keys are installed.
+ LOG(INFO) << "attempting fscrypt_prepare_user_storage";
+ if (!fscrypt_prepare_user_storage("", 0, 0, android::os::IVold::STORAGE_FLAG_DE)) {
+ LOG(ERROR) << "Failed to prepare user 0 storage";
+ return false;
+ }
+
+ // If this is a non-FBE device that recently left an emulated mode,
+ // restore user data directories to known-good state.
+ if (!fscrypt_is_native() && !fscrypt_is_emulated()) {
+ LOG(INFO) << "unlocking data media";
+ fscrypt_unlock_user_key(0, 0, "!", "!");
+ }
+
+ // In some scenarios (e.g. userspace reboot) we might unmount userdata
+ // without doing a hard reboot. If CE keys were stored in fs keyring then
+ // they will be lost after unmount. Attempt to re-install them.
+ if (fscrypt_is_native() && ::isFsKeyringSupported()) {
+ LOG(INFO) << "reloading ce keys";
+ if (!try_reload_ce_keys()) return false;
+ }
+
+ return true;
+}
+
+bool fscrypt_vold_create_user_key(userid_t user_id, int serial, bool ephemeral) {
+ LOG(INFO) << "fscrypt_vold_create_user_key for " << user_id << " serial " << serial;
+ if (!fscrypt_is_native()) {
+ return true;
+ }
+ // FIXME test for existence of key that is not loaded yet
+ if (s_ce_policies.count(user_id) != 0) {
+ LOG(ERROR) << "Already exists, can't fscrypt_vold_create_user_key for " << user_id
+ << " serial " << serial;
+ // FIXME should we fail the command?
+ return true;
+ }
+ if (!create_and_install_user_keys(user_id, ephemeral)) {
+ return false;
+ }
+ return true;
+}
+
+// "Lock" all encrypted directories whose key has been removed. This is needed
+// in the case where the keys are being put in the session keyring (rather in
+// the newer filesystem-level keyrings), because removing a key from the session
+// keyring doesn't affect inodes in the kernel's inode cache whose per-file key
+// was already set up. So to remove the per-file keys and make the files
+// "appear encrypted", these inodes must be evicted.
+//
+// To do this, sync() to clean all dirty inodes, then drop all reclaimable slab
+// objects systemwide. This is overkill, but it's the best available method
+// currently. Don't use drop_caches mode "3" because that also evicts pagecache
+// for in-use files; all files relevant here are already closed and sync'ed.
+static void drop_caches_if_needed() {
+ if (::isFsKeyringSupported()) {
+ return;
+ }
+ sync();
+ if (!writeStringToFile("2", "/proc/sys/vm/drop_caches")) {
+ PLOG(ERROR) << "Failed to drop caches during key eviction";
+ }
+}
+
+static bool evict_ce_key(userid_t user_id) {
+ bool success = true;
+ EncryptionPolicy policy;
+ // If we haven't loaded the CE key, no need to evict it.
+ if (lookup_policy(s_ce_policies, user_id, &policy)) {
+ success &= ::evictKey(DATA_MNT_POINT, policy);
+ drop_caches_if_needed();
+ }
+ s_ce_policies.erase(user_id);
+ return success;
+}
+
+bool fscrypt_destroy_user_key(userid_t user_id) {
+ LOG(INFO) << "fscrypt_destroy_user_key(" << user_id << ")";
+ if (!fscrypt_is_native()) {
+ return true;
+ }
+ bool success = true;
+ success &= evict_ce_key(user_id);
+ EncryptionPolicy de_policy;
+ success &= lookup_policy(s_de_policies, user_id, &de_policy) &&
+ ::evictKey(DATA_MNT_POINT, de_policy);
+ s_de_policies.erase(user_id);
+ auto it = s_ephemeral_users.find(user_id);
+ if (it != s_ephemeral_users.end()) {
+ s_ephemeral_users.erase(it);
+ } else {
+ for (auto const path : get_ce_key_paths(get_ce_key_directory_path(user_id))) {
+ success &= ::destroyKey(path);
+ }
+ auto de_key_path = get_de_key_path(user_id);
+ if (::pathExists(de_key_path)) {
+ success &= ::destroyKey(de_key_path);
+ } else {
+ LOG(INFO) << "Not present so not erasing: " << de_key_path;
+ }
+ }
+ return success;
+}
+
+static bool emulated_lock(const std::string& path) {
+ if (chmod(path.c_str(), 0000) != 0) {
+ PLOG(ERROR) << "Failed to chmod " << path;
+ return false;
+ }
+#if EMULATED_USES_SELINUX
+ if (setfilecon(path.c_str(), "u:object_r:storage_stub_file:s0") != 0) {
+ PLOG(WARNING) << "Failed to setfilecon " << path;
+ return false;
+ }
+#endif
+ 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;
+ // FIXME temporary workaround for b/26713622
+ if (fscrypt_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;
+ // FIXME temporary workaround for b/26713622
+ if (fscrypt_is_emulated()) return false;
+ }
+#endif
+ return true;
+}
+
+static bool parse_hex(const std::string& hex, std::string* result) {
+ if (hex == "!") {
+ *result = "";
+ return true;
+ }
+ if (::HexToStr(hex, *result) != 0) {
+ LOG(ERROR) << "Invalid FBE hex string"; // Don't log the string for security reasons
+ return false;
+ }
+ return true;
+}
+
+static std::optional<::KeyAuthentication> authentication_from_hex(
+ const std::string& token_hex, const std::string& secret_hex) {
+ std::string token, secret;
+ if (!parse_hex(token_hex, &token)) return std::optional<::KeyAuthentication>();
+ if (!parse_hex(secret_hex, &secret)) return std::optional<::KeyAuthentication>();
+ if (secret.empty()) {
+ return kEmptyAuthentication;
+ } else {
+ return ::KeyAuthentication(token, secret);
+ }
+}
+
+static std::string volkey_path(const std::string& misc_path, const std::string& volume_uuid) {
+ return misc_path + "/vold/volume_keys/" + volume_uuid + "/default";
+}
+
+static std::string volume_secdiscardable_path(const std::string& volume_uuid) {
+ return systemwide_volume_key_dir + "/" + volume_uuid + "/secdiscardable";
+}
+
+static bool read_or_create_volkey(const std::string& misc_path, const std::string& volume_uuid,
+ EncryptionPolicy* policy) {
+ auto secdiscardable_path = volume_secdiscardable_path(volume_uuid);
+ std::string secdiscardable_hash;
+ if (::pathExists(secdiscardable_path)) {
+ if (!readSecdiscardable(secdiscardable_path, &secdiscardable_hash))
+ return false;
+ } else {
+ if (fs_mkdirs(secdiscardable_path.c_str(), 0700) != 0) {
+ PLOG(ERROR) << "Creating directories for: " << secdiscardable_path;
+ return false;
+ }
+ if (!::createSecdiscardable(secdiscardable_path, &secdiscardable_hash))
+ return false;
+ }
+ auto key_path = volkey_path(misc_path, volume_uuid);
+ if (fs_mkdirs(key_path.c_str(), 0700) != 0) {
+ PLOG(ERROR) << "Creating directories for: " << key_path;
+ return false;
+ }
+ ::KeyAuthentication auth("", secdiscardable_hash);
+
+ EncryptionOptions options;
+ if (!get_volume_file_encryption_options(&options)) return false;
+ KeyBuffer key;
+ if (!retrieveOrGenerateKey(key_path, key_path + "_tmp", auth, makeGen(options), &key))
+ return false;
+ if (!install_storage_key(BuildDataPath(volume_uuid), options, key, policy)) return false;
+ return true;
+}
+
+static bool destroy_volkey(const std::string& misc_path, const std::string& volume_uuid) {
+ auto path = volkey_path(misc_path, volume_uuid);
+ if (!::pathExists(path)) return true;
+ return ::destroyKey(path);
+}
+
+static bool fscrypt_rewrap_user_key(userid_t user_id, int serial,
+ const ::KeyAuthentication& retrieve_auth,
+ const ::KeyAuthentication& store_auth) {
+ if (s_ephemeral_users.count(user_id) != 0) return true;
+ auto const directory_path = get_ce_key_directory_path(user_id);
+ KeyBuffer ce_key;
+ std::string ce_key_current_path = get_ce_key_current_path(directory_path);
+ if (retrieveKey(ce_key_current_path, retrieve_auth, &ce_key, true)) {
+ LOG(INFO) << "Successfully retrieved key";
+ // TODO(147732812): Remove this once Locksettingservice is fixed.
+ // Currently it calls fscrypt_clear_user_key_auth with a secret when lockscreen is
+ // changed from swipe to none or vice-versa
+ } else if (retrieveKey(ce_key_current_path, kEmptyAuthentication, &ce_key, true)) {
+ LOG(INFO) << "Successfully retrieved key with empty auth";
+ } else {
+ LOG(ERROR) << "Failed to retrieve key for user " << user_id;
+ 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 (!::storeKeyAtomically(ce_key_path, user_key_temp, store_auth, ce_key))
+ return false;
+ if (!::FsyncDirectory(directory_path)) return false;
+ return true;
+}
+
+bool fscrypt_add_user_key_auth(userid_t user_id, int serial, const std::string& token_hex,
+ const std::string& secret_hex) {
+ LOG(INFO) << "fscrypt_add_user_key_auth " << user_id << " serial=" << serial
+ << " token_present=" << (token_hex != "!");
+ if (!fscrypt_is_native()) return true;
+ auto auth = authentication_from_hex(token_hex, secret_hex);
+ if (!auth) return false;
+ return fscrypt_rewrap_user_key(user_id, serial, kEmptyAuthentication, *auth);
+}
+
+bool fscrypt_clear_user_key_auth(userid_t user_id, int serial, const std::string& token_hex,
+ const std::string& secret_hex) {
+ LOG(INFO) << "fscrypt_clear_user_key_auth " << user_id << " serial=" << serial
+ << " token_present=" << (token_hex != "!");
+ if (!fscrypt_is_native()) return true;
+ auto auth = authentication_from_hex(token_hex, secret_hex);
+ if (!auth) return false;
+ return fscrypt_rewrap_user_key(user_id, serial, *auth, kEmptyAuthentication);
+}
+
+bool fscrypt_fixate_newest_user_key_auth(userid_t user_id) {
+ LOG(INFO) << "fscrypt_fixate_newest_user_key_auth " << user_id;
+ if (!fscrypt_is_native()) return true;
+ if (s_ephemeral_users.count(user_id) != 0) return true;
+ auto const directory_path = get_ce_key_directory_path(user_id);
+ auto const paths = get_ce_key_paths(directory_path);
+ if (paths.empty()) {
+ LOG(ERROR) << "No ce keys present, cannot fixate for user " << user_id;
+ return false;
+ }
+ fixate_user_ce_key(directory_path, paths[0], paths);
+ return true;
+}
+
+// TODO: rename to 'install' for consistency, and take flags to know which keys to install
+bool fscrypt_unlock_user_key(userid_t user_id, int serial, const std::string& token_hex,
+ const std::string& secret_hex) {
+ LOG(INFO) << "fscrypt_unlock_user_key " << user_id << " serial=" << serial
+ << " token_present=" << (token_hex != "!");
+ if (fscrypt_is_native()) {
+ if (s_ce_policies.count(user_id) != 0) {
+ LOG(WARNING) << "Tried to unlock already-unlocked key for user " << user_id;
+ return true;
+ }
+ auto auth = authentication_from_hex(token_hex, secret_hex);
+ if (!auth) return false;
+ if (!read_and_install_user_ce_key(user_id, *auth)) {
+ LOG(ERROR) << "Couldn't read key for " << user_id;
+ 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(::BuildDataSystemCePath(user_id), 0771) ||
+ !emulated_unlock(::BuildDataMiscCePath(user_id), 01771) ||
+ !emulated_unlock(::BuildDataMediaCePath("", user_id), 0770) ||
+ !emulated_unlock(::BuildDataUserCePath("", user_id), 0771)) {
+ LOG(ERROR) << "Failed to unlock user " << user_id;
+ return false;
+ }
+ }
+ return true;
+}
+
+// TODO: rename to 'evict' for consistency
+bool fscrypt_lock_user_key(userid_t user_id) {
+ LOG(INFO) << "fscrypt_lock_user_key " << user_id;
+ if (fscrypt_is_native()) {
+ return evict_ce_key(user_id);
+ } else if (fscrypt_is_emulated()) {
+ // When in emulation mode, we just use chmod
+ if (!emulated_lock(::BuildDataSystemCePath(user_id)) ||
+ !emulated_lock(::BuildDataMiscCePath(user_id)) ||
+ !emulated_lock(::BuildDataMediaCePath("", user_id)) ||
+ !emulated_lock(::BuildDataUserCePath("", user_id))) {
+ LOG(ERROR) << "Failed to lock user " << user_id;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool prepare_subdirs(const std::string& action, const std::string& volume_uuid,
+ userid_t user_id, int flags) {
+ if (0 != ::ForkExecvp(
+ std::vector<std::string>{prepare_subdirs_path, action, volume_uuid,
+ std::to_string(user_id), std::to_string(flags)})) {
+ LOG(ERROR) << "vold_prepare_subdirs failed";
+ return false;
+ }
+ return true;
+}
+
+bool fscrypt_prepare_user_storage(const std::string& volume_uuid, userid_t user_id, int serial,
+ int flags) {
+ LOG(INFO) << "fscrypt_prepare_user_storage for volume " << escape_empty(volume_uuid)
+ << ", user " << user_id << ", serial " << serial << ", flags " << flags;
+
+ if (flags & android::os::IVold::STORAGE_FLAG_DE) {
+ // DE_sys key
+ auto system_legacy_path = ::BuildDataSystemLegacyPath(user_id);
+ auto misc_legacy_path = ::BuildDataMiscLegacyPath(user_id);
+ auto profiles_de_path = ::BuildDataProfilesDePath(user_id);
+
+ // DE_n key
+ auto system_de_path = ::BuildDataSystemDePath(user_id);
+ auto misc_de_path = ::BuildDataMiscDePath(user_id);
+ auto vendor_de_path = ::BuildDataVendorDePath(user_id);
+ auto user_de_path = ::BuildDataUserDePath(volume_uuid, user_id);
+
+ if (volume_uuid.empty()) {
+ if (!prepare_dir(system_legacy_path, 0700, AID_SYSTEM, AID_SYSTEM)) return false;
+#if MANAGE_MISC_DIRS
+ if (!prepare_dir(misc_legacy_path, 0750, multiuser_get_uid(user_id, AID_SYSTEM),
+ multiuser_get_uid(user_id, AID_EVERYBODY)))
+ return false;
+#endif
+ if (!prepare_dir(profiles_de_path, 0771, AID_SYSTEM, AID_SYSTEM)) return false;
+
+ if (!prepare_dir(system_de_path, 0770, AID_SYSTEM, AID_SYSTEM)) return false;
+ if (!prepare_dir(misc_de_path, 01771, AID_SYSTEM, AID_MISC)) return false;
+ if (!prepare_dir(vendor_de_path, 0771, AID_ROOT, AID_ROOT)) return false;
+ }
+ if (!prepare_dir(user_de_path, 0771, AID_SYSTEM, AID_SYSTEM)) return false;
+ if (fscrypt_is_native()) {
+ EncryptionPolicy de_policy;
+ if (volume_uuid.empty()) {
+ if (!lookup_policy(s_de_policies, user_id, &de_policy)) return false;
+ if (!EnsurePolicy(de_policy, system_de_path)) return false;
+ if (!EnsurePolicy(de_policy, misc_de_path)) return false;
+ if (!EnsurePolicy(de_policy, vendor_de_path)) return false;
+ } else {
+ if (!read_or_create_volkey(misc_de_path, volume_uuid, &de_policy)) return false;
+ }
+ if (!EnsurePolicy(de_policy, user_de_path)) return false;
+ }
+ }
+
+ if (flags & android::os::IVold::STORAGE_FLAG_CE) {
+ // CE_n key
+ auto system_ce_path = ::BuildDataSystemCePath(user_id);
+ auto misc_ce_path = ::BuildDataMiscCePath(user_id);
+ auto vendor_ce_path = ::BuildDataVendorCePath(user_id);
+ auto media_ce_path = ::BuildDataMediaCePath(volume_uuid, user_id);
+ auto user_ce_path = ::BuildDataUserCePath(volume_uuid, user_id);
+
+ if (volume_uuid.empty()) {
+ if (!prepare_dir(system_ce_path, 0770, AID_SYSTEM, AID_SYSTEM)) return false;
+ if (!prepare_dir(misc_ce_path, 01771, AID_SYSTEM, AID_MISC)) return false;
+ if (!prepare_dir(vendor_ce_path, 0771, AID_ROOT, AID_ROOT)) return false;
+ }
+ if (!prepare_dir(media_ce_path, 0770, AID_MEDIA_RW, AID_MEDIA_RW)) return false;
+
+ if (!prepare_dir(user_ce_path, 0771, AID_SYSTEM, AID_SYSTEM)) return false;
+
+ if (fscrypt_is_native()) {
+ EncryptionPolicy ce_policy;
+ if (volume_uuid.empty()) {
+ if (!lookup_policy(s_ce_policies, user_id, &ce_policy)) return false;
+ if (!EnsurePolicy(ce_policy, system_ce_path)) return false;
+ if (!EnsurePolicy(ce_policy, misc_ce_path)) return false;
+ if (!EnsurePolicy(ce_policy, vendor_ce_path)) return false;
+ } else {
+ if (!read_or_create_volkey(misc_ce_path, volume_uuid, &ce_policy)) return false;
+ }
+ if (!EnsurePolicy(ce_policy, media_ce_path)) return false;
+ if (!EnsurePolicy(ce_policy, user_ce_path)) return false;
+ }
+
+ if (volume_uuid.empty()) {
+ // Now that credentials have been installed, we can run restorecon
+ // over these paths
+ // NOTE: these paths need to be kept in sync with libselinux
+ ::RestoreconRecursive(system_ce_path);
+ ::RestoreconRecursive(vendor_ce_path);
+ ::RestoreconRecursive(misc_ce_path);
+ }
+ }
+ if (!prepare_subdirs("prepare", volume_uuid, user_id, flags)) return false;
+
+ return true;
+}
+
+bool fscrypt_destroy_user_storage(const std::string& volume_uuid, userid_t user_id, int flags) {
+ LOG(INFO) << "fscrypt_destroy_user_storage for volume " << escape_empty(volume_uuid)
+ << ", user " << user_id << ", flags " << flags;
+ bool res = true;
+
+ res &= prepare_subdirs("destroy", volume_uuid, user_id, flags);
+
+ if (flags & android::os::IVold::STORAGE_FLAG_CE) {
+ // CE_n key
+ auto system_ce_path = ::BuildDataSystemCePath(user_id);
+ auto misc_ce_path = ::BuildDataMiscCePath(user_id);
+ auto vendor_ce_path = ::BuildDataVendorCePath(user_id);
+ auto media_ce_path = ::BuildDataMediaCePath(volume_uuid, user_id);
+ auto user_ce_path = ::BuildDataUserCePath(volume_uuid, user_id);
+
+ res &= destroy_dir(media_ce_path);
+ res &= destroy_dir(user_ce_path);
+ if (volume_uuid.empty()) {
+ res &= destroy_dir(system_ce_path);
+ res &= destroy_dir(misc_ce_path);
+ res &= destroy_dir(vendor_ce_path);
+ } else {
+ if (fscrypt_is_native()) {
+ res &= destroy_volkey(misc_ce_path, volume_uuid);
+ }
+ }
+ }
+
+ if (flags & android::os::IVold::STORAGE_FLAG_DE) {
+ // DE_sys key
+ auto system_legacy_path = ::BuildDataSystemLegacyPath(user_id);
+ auto misc_legacy_path = ::BuildDataMiscLegacyPath(user_id);
+ auto profiles_de_path = ::BuildDataProfilesDePath(user_id);
+
+ // DE_n key
+ auto system_de_path = ::BuildDataSystemDePath(user_id);
+ auto misc_de_path = ::BuildDataMiscDePath(user_id);
+ auto vendor_de_path = ::BuildDataVendorDePath(user_id);
+ auto user_de_path = ::BuildDataUserDePath(volume_uuid, user_id);
+
+ res &= destroy_dir(user_de_path);
+ if (volume_uuid.empty()) {
+ res &= destroy_dir(system_legacy_path);
+#if MANAGE_MISC_DIRS
+ res &= destroy_dir(misc_legacy_path);
+#endif
+ res &= destroy_dir(profiles_de_path);
+ res &= destroy_dir(system_de_path);
+ res &= destroy_dir(misc_de_path);
+ res &= destroy_dir(vendor_de_path);
+ } else {
+ if (fscrypt_is_native()) {
+ res &= destroy_volkey(misc_de_path, volume_uuid);
+ }
+ }
+ }
+
+ return res;
+}
+
+static bool destroy_volume_keys(const std::string& directory_path, const std::string& volume_uuid) {
+ auto dirp = std::unique_ptr<DIR, int (*)(DIR*)>(opendir(directory_path.c_str()), closedir);
+ if (!dirp) {
+ PLOG(ERROR) << "Unable to open directory: " + directory_path;
+ return false;
+ }
+ bool res = true;
+ for (;;) {
+ errno = 0;
+ auto const entry = readdir(dirp.get());
+ if (!entry) {
+ if (errno) {
+ PLOG(ERROR) << "Unable to read directory: " + directory_path;
+ return false;
+ }
+ break;
+ }
+ if (entry->d_type != DT_DIR || entry->d_name[0] == '.') {
+ LOG(INFO) << "Skipping non-user " << entry->d_name;
+ continue;
+ }
+ res &= destroy_volkey(directory_path + "/" + entry->d_name, volume_uuid);
+ }
+ return res;
+}
+
+bool fscrypt_destroy_volume_keys(const std::string& volume_uuid) {
+ bool res = true;
+ LOG(INFO) << "fscrypt_destroy_volume_keys for volume " << escape_empty(volume_uuid);
+ auto secdiscardable_path = volume_secdiscardable_path(volume_uuid);
+ res &= ::runSecdiscardSingle(secdiscardable_path);
+ res &= destroy_volume_keys("/data/misc_ce", volume_uuid);
+ res &= destroy_volume_keys("/data/misc_de", volume_uuid);
+ return res;
+}
+
+bool lookup_key_ref(const std::map<userid_t, android::fscrypt::EncryptionPolicy>& 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.key_raw_ref;
+ return true;
+}
diff --git a/crypto/fscrypt/FsCrypt.h b/crypto/fscrypt/FsCrypt.h
new file mode 100755
index 0000000..0cb38c1
--- /dev/null
+++ b/crypto/fscrypt/FsCrypt.h
@@ -0,0 +1,50 @@
+/*
+ * 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 <fscrypt/fscrypt.h>
+#include <cutils/multiuser.h>
+
+using namespace android::fscrypt;
+
+bool fscrypt_initialize_systemwide_keys();
+
+bool fscrypt_init_user0();
+bool fscrypt_vold_create_user_key(userid_t user_id, int serial, bool ephemeral);
+bool fscrypt_destroy_user_key(userid_t user_id);
+bool fscrypt_add_user_key_auth(userid_t user_id, int serial, const std::string& token,
+ const std::string& secret);
+bool fscrypt_clear_user_key_auth(userid_t user_id, int serial, const std::string& token,
+ const std::string& secret);
+bool fscrypt_fixate_newest_user_key_auth(userid_t user_id);
+
+bool fscrypt_unlock_user_key(userid_t user_id, int serial, const std::string& token,
+ const std::string& secret);
+bool fscrypt_lock_user_key(userid_t user_id);
+
+bool fscrypt_prepare_user_storage(const std::string& volume_uuid, userid_t user_id, int serial,
+ int flags);
+bool is_metadata_wrapped_key_supported();
+bool fscrypt_destroy_user_storage(const std::string& volume_uuid, userid_t user_id, int flags);
+
+bool fscrypt_destroy_volume_keys(const std::string& volume_uuid);
+
+bool lookup_key_ref(const std::map<userid_t, android::fscrypt::EncryptionPolicy>& key_map, userid_t user_id,
+ std::string* raw_ref);
+bool lookup_policy(const std::map<userid_t, EncryptionPolicy>& key_map, userid_t user_id,
+ EncryptionPolicy* policy);
\ No newline at end of file
diff --git a/crypto/fscrypt/HashPassword.cpp b/crypto/fscrypt/HashPassword.cpp
new file mode 100644
index 0000000..07ecb1f
--- /dev/null
+++ b/crypto/fscrypt/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, ÷r, 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/fscrypt/HashPassword.h b/crypto/fscrypt/HashPassword.h
new file mode 100644
index 0000000..73880b1
--- /dev/null
+++ b/crypto/fscrypt/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/fscrypt/KeyBuffer.cpp b/crypto/fscrypt/KeyBuffer.cpp
new file mode 100644
index 0000000..e7aede5
--- /dev/null
+++ b/crypto/fscrypt/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/fscrypt/KeyBuffer.h b/crypto/fscrypt/KeyBuffer.h
new file mode 100644
index 0000000..2de9ac9
--- /dev/null
+++ b/crypto/fscrypt/KeyBuffer.h
@@ -0,0 +1,58 @@
+/*
+ * 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>
+
+
+/**
+ * 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);
+
+#endif
+
diff --git a/crypto/fscrypt/KeyStorage.cpp b/crypto/fscrypt/KeyStorage.cpp
new file mode 100755
index 0000000..edb23a2
--- /dev/null
+++ b/crypto/fscrypt/KeyStorage.cpp
@@ -0,0 +1,637 @@
+/*
+ * 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 "Checkpoint.h"
+#include "Keymaster.h"
+#include "ScryptParameters.h"
+#include "Utils.h"
+
+#include <thread>
+#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/properties.h>
+#include <android-base/unique_fd.h>
+
+#include <cutils/properties.h>
+
+#include <hardware/hw_auth_token.h>
+#include <keymasterV4_1/authorization_set.h>
+#include <keymasterV4_1/keymaster_utils.h>
+
+extern "C" {
+
+#include "crypto_scrypt.h"
+}
+
+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 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";
+ paramBuilder.Authorization(km::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());
+ auto user_id = at->user_id; // Make a copy because at->user_id is unaligned.
+ paramBuilder.Authorization(km::TAG_USER_SECURE_ID, user_id);
+ paramBuilder.Authorization(km::TAG_USER_AUTH_TYPE, km::HardwareAuthenticatorType::PASSWORD);
+ paramBuilder.Authorization(km::TAG_AUTH_TIMEOUT, AUTH_TIMEOUT);
+ }
+
+ auto paramsWithRollback = paramBuilder;
+ paramsWithRollback.Authorization(km::TAG_ROLLBACK_RESISTANCE);
+
+ // Generate rollback-resistant key if possible.
+ return keymaster.generateKey(paramsWithRollback, key) ||
+ keymaster.generateKey(paramBuilder, key);
+}
+
+bool generateWrappedStorageKey(KeyBuffer* key) {
+ Keymaster keymaster;
+ if (!keymaster) return false;
+ std::string key_temp;
+ auto paramBuilder = km::AuthorizationSetBuilder().AesEncryptionKey(AES_KEY_BYTES * 8);
+ km::KeyParameter param1;
+ param1.tag = static_cast<::android::hardware::keymaster::V4_0::Tag>(
+ android::hardware::keymaster::V4_0::KM_TAG_FBE_ICE);
+ param1.f.boolValue = true;
+ paramBuilder.push_back(param1);
+ 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 exportWrappedStorageKey(const KeyBuffer& kmKey, KeyBuffer* key) {
+ Keymaster keymaster;
+ if (!keymaster) return false;
+ std::string key_temp;
+
+ auto ret = keymaster.exportKey(kmKey, &key_temp);
+ if (ret != km::ErrorCode::OK) {
+ if (ret == km::ErrorCode::KEY_REQUIRES_UPGRADE) {
+ std::string kmKeyStr(reinterpret_cast<const char*>(kmKey.data()), kmKey.size());
+ std::string Keystr;
+ if (!keymaster.upgradeKey(kmKeyStr, km::AuthorizationSet(), &Keystr)) return false;
+ KeyBuffer upgradedKey = KeyBuffer(Keystr.size());
+ memcpy(reinterpret_cast<void*>(upgradedKey.data()), Keystr.c_str(), upgradedKey.size());
+ ret = keymaster.exportKey(upgradedKey, &key_temp);
+ if (ret != km::ErrorCode::OK) return false;
+ } else {
+ return false;
+ }
+ } *key = KeyBuffer(key_temp.size());
+ memcpy(reinterpret_cast<void*>(key->data()), key_temp.c_str(), key->size());
+ return true;
+}
+
+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(INFO) << "Supplying auth token to Keymaster";
+ 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;
+ return false;
+ }
+ return true;
+}
+
+static bool readRandomBytesOrLog(size_t count, std::string* out) {
+ auto status = ReadRandomBytes(count, *out);
+ if (status != android::OK) {
+ LOG(ERROR) << "Random read failed with status: " << status;
+ 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 void deferedKmDeleteKey(const std::string& kmkey) {
+ while (!android::base::WaitForProperty("vold.checkpoint_committed", "1")) {
+ LOG(ERROR) << "Wait for boot timed out";
+ }
+ Keymaster keymaster;
+ if (!keymaster || !keymaster.deleteKey(kmkey)) {
+ LOG(ERROR) << "Defered Key deletion failed during upgrade";
+ }
+}
+
+bool kmDeleteKey(Keymaster& keymaster, const std::string& kmKey) {
+ bool needs_cp = cp_needsCheckpoint();
+
+ if (needs_cp) {
+ std::thread(deferedKmDeleteKey, kmKey).detach();
+ LOG(INFO) << "Deferring Key deletion during upgrade";
+ return true;
+ } else {
+ return keymaster.deleteKey(kmKey);
+ }
+}
+
+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, bool keepOld) {
+ 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: " << dir;
+ 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 (!keepOld) {
+ if (rename(newKeyPath.c_str(), kmKeyPath.c_str()) != 0) {
+ PLOG(ERROR) << "Unable to move upgraded key to location: " << kmKeyPath;
+ return KeymasterOperation();
+ }
+ if (!::FsyncDirectory(dir)) {
+ LOG(ERROR) << "Key dir sync failed: " << dir;
+ return KeymasterOperation();
+ }
+ if (!kmDeleteKey(keymaster, 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 km::AuthorizationSet& keyParams,
+ const km::HardwareAuthToken& authToken, const KeyBuffer& message,
+ std::string* ciphertext, bool keepOld) {
+ km::AuthorizationSet opParams;
+ km::AuthorizationSet outParams;
+ auto opHandle = begin(keymaster, dir, km::KeyPurpose::ENCRYPT, keyParams, opParams, authToken,
+ &outParams, keepOld);
+ if (!opHandle) return false;
+ auto nonceBlob = outParams.GetTagValue(km::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 km::AuthorizationSet& keyParams,
+ const km::HardwareAuthToken& authToken,
+ const std::string& ciphertext, KeyBuffer* message,
+ bool keepOld) {
+ 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, keepOld);
+ 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_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();
+}
+
+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;
+ 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,
+ KeyBuffer* plaintext) {
+ if (ciphertext.size() < GCM_NONCE_BYTES + GCM_MAC_BYTES) {
+ LOG(ERROR) << "GCM ciphertext too small: " << ciphertext.size();
+ 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;
+ 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 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;
+ 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) != android::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_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,
+ false))
+ return false;
+ } else {
+ if (!encryptWithoutKeymaster(appId, key, &encryptedKey)) return false;
+ }
+ if (!writeStringToFile(encryptedKey, dir + "/" + kFn_encrypted_key)) return false;
+ if (!FsyncDirectory(dir)) 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;
+ return false;
+ }
+ if (pathExists(tmp_path)) {
+ LOG(DEBUG) << "Already exists, destroying: " << tmp_path;
+ 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;
+ return false;
+ }
+ LOG(DEBUG) << "Created key: " << key_path;
+ return true;
+}
+
+bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, KeyBuffer* key,
+ bool keepOld) {
+ 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_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,
+ keepOld))
+ 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;
+}
+
+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.
+ 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";
+ success = false;
+ }
+ success &= recursiveDeleteKey(dir);
+ return success;
+}
diff --git a/crypto/fscrypt/KeyStorage.h b/crypto/fscrypt/KeyStorage.h
new file mode 100755
index 0000000..2c88820
--- /dev/null
+++ b/crypto/fscrypt/KeyStorage.h
@@ -0,0 +1,75 @@
+/*
+ * 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 "KeyBuffer.h"
+
+#include <string>
+
+
+// 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(const std::string& t, const 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;
+
+// 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,
+ bool keepOld);
+
+// 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);
+
+// Generate wrapped storage key using keymaster. Uses STORAGE_KEY tag in keymaster.
+bool generateWrappedStorageKey(KeyBuffer* key);
+// Export the per-boot boot wrapped storage key using keymaster.
+bool exportWrappedStorageKey(const KeyBuffer& kmKey, KeyBuffer* key);
+
+#endif
diff --git a/crypto/fscrypt/KeyUtil.cpp b/crypto/fscrypt/KeyUtil.cpp
new file mode 100755
index 0000000..b7119a6
--- /dev/null
+++ b/crypto/fscrypt/KeyUtil.cpp
@@ -0,0 +1,445 @@
+/*
+ * 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 <fcntl.h>
+#include <linux/fscrypt.h>
+#include <openssl/sha.h>
+#include <sys/ioctl.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <keyutils.h>
+
+#include <fscrypt_uapi.h>
+#include "FsCrypt.h"
+#include "KeyStorage.h"
+#include "Utils.h"
+
+
+const KeyGeneration neverGen() {
+ return KeyGeneration{0, false, false};
+}
+
+static bool randomKey(size_t size, KeyBuffer* key) {
+ *key = KeyBuffer(size);
+ if (ReadRandomBytes(key->size(), key->data()) != 0) {
+ // TODO status_t plays badly with PLOG, fix it.
+ LOG(ERROR) << "Random read failed";
+ return false;
+ }
+ return true;
+}
+
+bool generateStorageKey(const KeyGeneration& gen, KeyBuffer* key) {
+ if (!gen.allow_gen) return false;
+ if (gen.use_hw_wrapped_key) {
+ if (gen.keysize != FSCRYPT_MAX_KEY_SIZE) {
+ LOG(ERROR) << "Cannot generate a wrapped key " << gen.keysize << " bytes long";
+ return false;
+ }
+ return generateWrappedStorageKey(key);
+ } else {
+ return randomKey(gen.keysize, key);
+ }
+}
+
+// Return true if the kernel supports the ioctls to add/remove fscrypt keys
+// directly to/from the filesystem.
+bool isFsKeyringSupported(void) {
+ static bool initialized = false;
+ static bool supported;
+
+ if (!initialized) {
+ android::base::unique_fd fd(open("/data", O_RDONLY | O_DIRECTORY | O_CLOEXEC));
+
+ // FS_IOC_ADD_ENCRYPTION_KEY with a NULL argument will fail with ENOTTY
+ // if the ioctl isn't supported. Otherwise it will fail with another
+ // error code such as EFAULT.
+ errno = 0;
+ (void)ioctl(fd, FS_IOC_ADD_ENCRYPTION_KEY, NULL);
+ if (errno == ENOTTY) {
+ LOG(INFO) << "Kernel doesn't support FS_IOC_ADD_ENCRYPTION_KEY. Falling back to "
+ "session keyring";
+ supported = false;
+ } else {
+ if (errno != EFAULT) {
+ PLOG(WARNING) << "Unexpected error from FS_IOC_ADD_ENCRYPTION_KEY";
+ }
+ LOG(INFO) << "Detected support for FS_IOC_ADD_ENCRYPTION_KEY";
+ supported = true;
+ android::base::SetProperty("ro.crypto.uses_fs_ioc_add_encryption_key", "true");
+ }
+ // There's no need to check for FS_IOC_REMOVE_ENCRYPTION_KEY, since it's
+ // guaranteed to be available if FS_IOC_ADD_ENCRYPTION_KEY is. There's
+ // also no need to check for support on external volumes separately from
+ // /data, since either the kernel supports the ioctls on all
+ // fscrypt-capable filesystems or it doesn't.
+
+ initialized = true;
+ }
+ return supported;
+}
+
+// Get raw keyref - used to make keyname and to pass to ioctl
+static std::string generateKeyRef(const uint8_t* 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(FSCRYPT_KEY_DESCRIPTOR_SIZE <= SHA512_DIGEST_LENGTH,
+ "Hash too short for descriptor");
+ return std::string((char*)key_ref2, FSCRYPT_KEY_DESCRIPTOR_SIZE);
+}
+
+static bool fillKey(const KeyBuffer& key, fscrypt_key* fs_key) {
+ if (key.size() != FSCRYPT_MAX_KEY_SIZE) {
+ LOG(ERROR) << "Wrong size key " << key.size();
+ return false;
+ }
+ static_assert(FSCRYPT_MAX_KEY_SIZE == sizeof(fs_key->raw), "Mismatch of max key sizes");
+ fs_key->mode = 0; // unused by kernel
+ memcpy(fs_key->raw, key.data(), key.size());
+ fs_key->size = key.size();
+ return true;
+}
+
+static char const* const NAME_PREFIXES[] = {"ext4", "f2fs", "fscrypt", nullptr};
+
+static std::string keyrefstring(const std::string& raw_ref) {
+ std::ostringstream o;
+ for (unsigned char i : raw_ref) {
+ o << std::hex << std::setw(2) << std::setfill('0') << (int)i;
+ }
+ return o.str();
+}
+
+static std::string buildLegacyKeyName(const std::string& prefix, const std::string& raw_ref) {
+ return prefix + ":" + keyrefstring(raw_ref);
+}
+
+// Get the ID of the keyring we store all fscrypt keys in when the kernel is too
+// old to support FS_IOC_ADD_ENCRYPTION_KEY and FS_IOC_REMOVE_ENCRYPTION_KEY.
+static bool fscryptKeyring(key_serial_t* device_keyring) {
+ *device_keyring = keyctl_search(KEY_SPEC_SESSION_KEYRING, "keyring", "fscrypt", 0);
+ if (*device_keyring == -1) {
+ PLOG(ERROR) << "Unable to find device keyring";
+ return false;
+ }
+ return true;
+}
+
+// Add an encryption key of type "logon" to the global session keyring.
+static bool installKeyLegacy(const KeyBuffer& key, const std::string& raw_ref) {
+ // Place fscrypt_key into automatically zeroing buffer.
+ KeyBuffer fsKeyBuffer(sizeof(fscrypt_key));
+ fscrypt_key& fs_key = *reinterpret_cast<fscrypt_key*>(fsKeyBuffer.data());
+
+ if (!fillKey(key, &fs_key)) return false;
+ key_serial_t device_keyring;
+ if (!fscryptKeyring(&device_keyring)) return false;
+ for (char const* const* name_prefix = NAME_PREFIXES; *name_prefix != nullptr; name_prefix++) {
+ auto ref = buildLegacyKeyName(*name_prefix, raw_ref);
+ key_serial_t key_id =
+ add_key("logon", ref.c_str(), (void*)&fs_key, sizeof(fs_key), device_keyring);
+ if (key_id == -1) {
+ PLOG(ERROR) << "Failed to insert key into keyring " << device_keyring;
+ return false;
+ }
+ LOG(INFO) << "Added key " << key_id << " (" << ref << ") to keyring " << device_keyring
+ << " in process " << getpid();
+ }
+ return true;
+}
+
+// Installs fscrypt-provisioning key into session level kernel keyring.
+// This allows for the given key to be installed back into filesystem keyring.
+// For more context see reloadKeyFromSessionKeyring.
+static bool installProvisioningKey(const KeyBuffer& key, const std::string& ref,
+ const fscrypt_key_specifier& key_spec) {
+ key_serial_t device_keyring;
+ if (!fscryptKeyring(&device_keyring)) return false;
+
+ // Place fscrypt_provisioning_key_payload into automatically zeroing buffer.
+ KeyBuffer buf(sizeof(fscrypt_provisioning_key_payload) + key.size(), 0);
+ fscrypt_provisioning_key_payload& provisioning_key =
+ *reinterpret_cast<fscrypt_provisioning_key_payload*>(buf.data());
+ memcpy(provisioning_key.raw, key.data(), key.size());
+ provisioning_key.type = key_spec.type;
+
+ key_serial_t key_id = add_key("fscrypt-provisioning", ref.c_str(), (void*)&provisioning_key,
+ buf.size(), device_keyring);
+ if (key_id == -1) {
+ PLOG(ERROR) << "Failed to insert fscrypt-provisioning key for " << ref
+ << " into session keyring";
+ return false;
+ }
+ LOG(INFO) << "Added fscrypt-provisioning key for " << ref << " to session keyring";
+ return true;
+}
+
+// Build a struct fscrypt_key_specifier for use in the key management ioctls.
+static bool buildKeySpecifier(fscrypt_key_specifier* spec, const EncryptionPolicy& policy) {
+ switch (policy.options.version) {
+ case 1:
+ if (policy.key_raw_ref.size() != FSCRYPT_KEY_DESCRIPTOR_SIZE) {
+ LOG(ERROR) << "Invalid key specifier size for v1 encryption policy: "
+ << policy.key_raw_ref.size();
+ return false;
+ }
+ spec->type = FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR;
+ memcpy(spec->u.descriptor, policy.key_raw_ref.c_str(), FSCRYPT_KEY_DESCRIPTOR_SIZE);
+ return true;
+ case 2:
+ if (policy.key_raw_ref.size() != FSCRYPT_KEY_IDENTIFIER_SIZE) {
+ LOG(ERROR) << "Invalid key specifier size for v2 encryption policy: "
+ << policy.key_raw_ref.size();
+ return false;
+ }
+ spec->type = FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER;
+ memcpy(spec->u.identifier, policy.key_raw_ref.c_str(), FSCRYPT_KEY_IDENTIFIER_SIZE);
+ return true;
+ default:
+ LOG(ERROR) << "Invalid encryption policy version: " << policy.options.version;
+ return false;
+ }
+}
+
+// Installs key into keyring of a filesystem mounted on |mountpoint|.
+//
+// It's callers responsibility to fill key specifier, and either arg->raw or arg->key_id.
+//
+// In case arg->key_spec.type equals to FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER
+// arg->key_spec.u.identifier will be populated with raw key reference generated
+// by kernel.
+//
+// For documentation on difference between arg->raw and arg->key_id see
+// https://www.kernel.org/doc/html/latest/filesystems/fscrypt.html#fs-ioc-add-encryption-key
+static bool installFsKeyringKey(const std::string& mountpoint, const EncryptionOptions& options,
+ fscrypt_add_key_arg* arg) {
+ if (options.use_hw_wrapped_key) arg->flags |= FSCRYPT_ADD_KEY_FLAG_WRAPPED;
+
+ android::base::unique_fd fd(open(mountpoint.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC));
+ if (fd == -1) {
+ PLOG(ERROR) << "Failed to open " << mountpoint << " to install key";
+ return false;
+ }
+
+ if (ioctl(fd, FS_IOC_ADD_ENCRYPTION_KEY, arg) != 0) {
+ PLOG(ERROR) << "Failed to install fscrypt key to " << mountpoint;
+ return false;
+ }
+
+ return true;
+}
+
+bool installKey(const std::string& mountpoint, const EncryptionOptions& options,
+ const KeyBuffer& key, EncryptionPolicy* policy) {
+ policy->options = options;
+ // Put the fscrypt_add_key_arg in an automatically-zeroing buffer, since we
+ // have to copy the raw key into it.
+ KeyBuffer arg_buf(sizeof(struct fscrypt_add_key_arg) + key.size(), 0);
+ struct fscrypt_add_key_arg* arg = (struct fscrypt_add_key_arg*)arg_buf.data();
+
+ // Initialize the "key specifier", which is like a name for the key.
+ switch (options.version) {
+ case 1:
+ // A key for a v1 policy is specified by an arbitrary 8-byte
+ // "descriptor", which must be provided by userspace. We use the
+ // first 8 bytes from the double SHA-512 of the key itself.
+ if (options.use_hw_wrapped_key) {
+ // 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.
+ policy->key_raw_ref = generateKeyRef((const uint8_t*)key.data(), key.size()/2);
+ } else {
+ policy->key_raw_ref = generateKeyRef((const uint8_t*)key.data(), key.size());
+ }
+ if (!isFsKeyringSupported()) {
+ return installKeyLegacy(key, policy->key_raw_ref);
+ }
+ if (!buildKeySpecifier(&arg->key_spec, *policy)) {
+ return false;
+ }
+ break;
+ case 2:
+ // A key for a v2 policy is specified by an 16-byte "identifier",
+ // which is a cryptographic hash of the key itself which the kernel
+ // computes and returns. Any user-provided value is ignored; we
+ // just need to set the specifier type to indicate that we're adding
+ // this type of key.
+ arg->key_spec.type = FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER;
+ break;
+ default:
+ LOG(ERROR) << "Invalid encryption policy version: " << options.version;
+ return false;
+ }
+
+ arg->raw_size = key.size();
+ memcpy(arg->raw, key.data(), key.size());
+
+ if (!installFsKeyringKey(mountpoint, options, arg)) return false;
+
+ if (arg->key_spec.type == FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER) {
+ // Retrieve the key identifier that the kernel computed.
+ policy->key_raw_ref =
+ std::string((char*)arg->key_spec.u.identifier, FSCRYPT_KEY_IDENTIFIER_SIZE);
+ }
+ std::string ref = keyrefstring(policy->key_raw_ref);
+ LOG(INFO) << "Installed fscrypt key with ref " << ref << " to " << mountpoint;
+
+ if (!installProvisioningKey(key, ref, arg->key_spec)) return false;
+ return true;
+}
+
+// Remove an encryption key of type "logon" from the global session keyring.
+static bool evictKeyLegacy(const std::string& raw_ref) {
+ key_serial_t device_keyring;
+ if (!fscryptKeyring(&device_keyring)) return false;
+ bool success = true;
+ for (char const* const* name_prefix = NAME_PREFIXES; *name_prefix != nullptr; name_prefix++) {
+ auto ref = buildLegacyKeyName(*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(ERROR) << "Unlinked key with serial " << key_serial << " ref " << ref;
+ }
+ }
+ return success;
+}
+
+static bool evictProvisioningKey(const std::string& ref) {
+ key_serial_t device_keyring;
+ if (!fscryptKeyring(&device_keyring)) {
+ return false;
+ }
+
+ auto key_serial = keyctl_search(device_keyring, "fscrypt-provisioning", ref.c_str(), 0);
+ if (key_serial == -1 && errno != ENOKEY) {
+ PLOG(ERROR) << "Error searching session keyring for fscrypt-provisioning key for " << ref;
+ return false;
+ }
+
+ if (key_serial != -1 && keyctl_unlink(key_serial, device_keyring) != 0) {
+ PLOG(ERROR) << "Failed to unlink fscrypt-provisioning key for " << ref
+ << " from session keyring";
+ return false;
+ }
+ return true;
+}
+
+bool evictKey(const std::string& mountpoint, const EncryptionPolicy& policy) {
+ if (policy.options.version == 1 && !isFsKeyringSupported()) {
+ return evictKeyLegacy(policy.key_raw_ref);
+ }
+
+ android::base::unique_fd fd(open(mountpoint.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC));
+ if (fd == -1) {
+ PLOG(ERROR) << "Failed to open " << mountpoint << " to evict key";
+ return false;
+ }
+
+ struct fscrypt_remove_key_arg arg;
+ memset(&arg, 0, sizeof(arg));
+
+ if (!buildKeySpecifier(&arg.key_spec, policy)) {
+ return false;
+ }
+
+ std::string ref = keyrefstring(policy.key_raw_ref);
+
+ if (ioctl(fd, FS_IOC_REMOVE_ENCRYPTION_KEY, &arg) != 0) {
+ PLOG(ERROR) << "Failed to evict fscrypt key with ref " << ref << " from " << mountpoint;
+ return false;
+ }
+
+ LOG(ERROR) << "Evicted fscrypt key with ref " << ref << " from " << mountpoint;
+ if (arg.removal_status_flags & FSCRYPT_KEY_REMOVAL_STATUS_FLAG_OTHER_USERS) {
+ // Should never happen because keys are only added/removed as root.
+ LOG(ERROR) << "Unexpected case: key with ref " << ref << " is still added by other users!";
+ } else if (arg.removal_status_flags & FSCRYPT_KEY_REMOVAL_STATUS_FLAG_FILES_BUSY) {
+ LOG(ERROR) << "Files still open after removing key with ref " << ref
+ << ". These files were not locked!";
+ }
+
+ if (!evictProvisioningKey(ref)) return false;
+ return true;
+}
+
+bool retrieveOrGenerateKey(const std::string& key_path, const std::string& tmp_path,
+ const KeyAuthentication& key_authentication, const KeyGeneration& gen,
+ KeyBuffer* key, bool keepOld) {
+ if (pathExists(key_path)) {
+ LOG(INFO) << "Key exists, using: " << key_path;
+ if (!retrieveKey(key_path, key_authentication, key, keepOld)) return false;
+ } else {
+ if (!gen.allow_gen) {
+ LOG(ERROR) << "No key found in " << key_path;
+ return false;
+ }
+ LOG(INFO) << "Creating new key in " << key_path;
+ if (!::generateStorageKey(gen, key)) return false;
+ if (!storeKeyAtomically(key_path, tmp_path, key_authentication, *key)) return false;
+ }
+ return true;
+}
+
+bool reloadKeyFromSessionKeyring(const std::string& mountpoint, const EncryptionPolicy& policy) {
+ key_serial_t device_keyring;
+ if (!fscryptKeyring(&device_keyring)) {
+ return false;
+ }
+
+ std::string ref = keyrefstring(policy.key_raw_ref);
+ auto key_serial = keyctl_search(device_keyring, "fscrypt-provisioning", ref.c_str(), 0);
+ if (key_serial == -1) {
+ PLOG(ERROR) << "Failed to find fscrypt-provisioning key for " << ref
+ << " in session keyring";
+ return false;
+ }
+
+ LOG(INFO) << "Installing fscrypt-provisioning key for " << ref << " back into " << mountpoint
+ << " fs-keyring";
+
+ struct fscrypt_add_key_arg arg;
+ memset(&arg, 0, sizeof(arg));
+ if (!buildKeySpecifier(&arg.key_spec, policy)) return false;
+ arg.key_id = key_serial;
+ if (!installFsKeyringKey(mountpoint, policy.options, &arg)) return false;
+
+ return true;
+}
diff --git a/crypto/fscrypt/KeyUtil.h b/crypto/fscrypt/KeyUtil.h
new file mode 100755
index 0000000..dcfcde8
--- /dev/null
+++ b/crypto/fscrypt/KeyUtil.h
@@ -0,0 +1,83 @@
+/*
+ * 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 "KeyStorage.h"
+
+#include <fscrypt/fscrypt.h>
+
+#include <memory>
+#include <string>
+
+
+using namespace android::fscrypt;
+
+// Description of how to generate a key when needed.
+struct KeyGeneration {
+ size_t keysize;
+ bool allow_gen;
+ bool use_hw_wrapped_key;
+};
+
+// Generate a key as specified in KeyGeneration
+bool generateStorageKey(const KeyGeneration& gen, KeyBuffer* key);
+
+// Returns a key with allow_gen false so generateStorageKey returns false;
+// this is used to indicate to retrieveOrGenerateKey that a key should not
+// be generated.
+const KeyGeneration neverGen();
+
+bool isFsKeyringSupported(void);
+
+// Install a file-based encryption key to the kernel, for use by encrypted files
+// on the specified filesystem using the specified encryption policy version.
+//
+// For v1 policies, we use FS_IOC_ADD_ENCRYPTION_KEY if the kernel supports it.
+// Otherwise we add the key to the global session keyring as a "logon" key.
+//
+// For v2 policies, we always use FS_IOC_ADD_ENCRYPTION_KEY; it's the only way
+// the kernel supports.
+//
+// If kernel supports FS_IOC_ADD_ENCRYPTION_KEY, also installs key of
+// fscrypt-provisioning type to the global session keyring. This makes it
+// possible to unmount and then remount mountpoint without losing the file-based
+// key.
+//
+// Returns %true on success, %false on failure. On success also sets *policy
+// to the EncryptionPolicy used to refer to this key.
+bool installKey(const std::string& mountpoint, const EncryptionOptions& options,
+ const KeyBuffer& key, EncryptionPolicy* policy);
+
+// Evict a file-based encryption key from the kernel.
+//
+// This undoes the effect of installKey().
+//
+// If the kernel doesn't support the filesystem-level keyring, the caller is
+// responsible for dropping caches.
+bool evictKey(const std::string& mountpoint, const EncryptionPolicy& policy);
+
+bool retrieveOrGenerateKey(const std::string& key_path, const std::string& tmp_path,
+ const KeyAuthentication& key_authentication, const KeyGeneration& gen,
+ KeyBuffer* key, bool keepOld = true);
+
+// Re-installs a file-based encryption key of fscrypt-provisioning type from the
+// global session keyring back into fs keyring of the mountpoint.
+bool reloadKeyFromSessionKeyring(const std::string& mountpoint, const EncryptionPolicy& policy);
+
+#endif
diff --git a/crypto/fscrypt/Keymaster.cpp b/crypto/fscrypt/Keymaster.cpp
new file mode 100755
index 0000000..c5b7bc3
--- /dev/null
+++ b/crypto/fscrypt/Keymaster.cpp
@@ -0,0 +1,377 @@
+/*
+ * 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 <keymasterV4_1/authorization_set.h>
+#include <keymasterV4_1/keymaster_utils.h>
+
+
+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();
+ mDevice = nullptr;
+ return false;
+ }
+ if (km_error != km::ErrorCode::OK) {
+ LOG(ERROR) << "update failed, code " << int32_t(km_error);
+ mDevice = nullptr;
+ return false;
+ }
+ if (inputConsumed > inputLen) {
+ LOG(ERROR) << "update reported too much input consumed";
+ 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();
+ return false;
+ }
+ if (km_error != km::ErrorCode::OK) {
+ LOG(ERROR) << "finish failed, code " << int32_t(km_error);
+ 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();
+}
+
+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();
+ return false;
+ }
+ if (km_error != km::ErrorCode::OK) {
+ LOG(ERROR) << "generate_key failed, code " << int32_t(km_error);
+ return false;
+ }
+ return true;
+}
+
+km::ErrorCode Keymaster::exportKey(const KeyBuffer& kmKey, std::string* key) {
+ auto kmKeyBlob = km::support::blob2hidlVec(std::string(kmKey.data(), kmKey.size()));
+ 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(km::KeyFormat::RAW, kmKeyBlob, {}, {}, 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) {
+ 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();
+ return false;
+ }
+ if (km_error != km::ErrorCode::OK) {
+ LOG(ERROR) << "upgrade_key failed, code " << int32_t(km_error);
+ 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();
+ return KeymasterOperation(km::ErrorCode::UNKNOWN_ERROR);
+ }
+ if (km_error != km::ErrorCode::OK) {
+ LOG(ERROR) << "begin failed, code " << int32_t(km_error);
+ return KeymasterOperation(km_error);
+ }
+ return KeymasterOperation(mDevice.get(), mOpHandle);
+}
+
+bool Keymaster::isSecure() {
+ return mDevice->halVersion().securityLevel != km::SecurityLevel::SOFTWARE;
+}
+
+void Keymaster::earlyBootEnded() {
+ auto devices = KmDevice::enumerateAvailableDevices();
+ for (auto& dev : devices) {
+ auto error = dev->earlyBootEnded();
+ if (!error.isOk()) {
+ LOG(ERROR) << "earlyBootEnded call failed: " << error.description() << " for "
+ << dev->halVersion().keymasterName;
+ }
+ km::V4_1_ErrorCode km_error = error;
+ if (km_error != km::V4_1_ErrorCode::OK && km_error != km::V4_1_ErrorCode::UNIMPLEMENTED) {
+ LOG(ERROR) << "Error reporting early boot ending to keymaster: "
+ << static_cast<int32_t>(km_error) << " for "
+ << dev->halVersion().keymasterName;
+ }
+ }
+}
+
+int keymaster_compatibility_cryptfs_scrypt() {
+ Keymaster dev;
+ if (!dev) {
+ LOG(ERROR) << "Failed to initiate keymaster session";
+ 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";
+ return false;
+ }
+ *out_size = towrite.size();
+ if (buffer_size < towrite.size()) {
+ LOG(ERROR) << "Buffer too small " << buffer_size << " < " << towrite.size();
+ 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";
+ 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";
+ 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";
+ return KeymasterSignResult::error;
+ }
+ if (!key_blob || !object || !signature_buffer || !signature_buffer_size) {
+ LOG(ERROR) << __FILE__ << ":" << __LINE__ << ":Invalid argument";
+ 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";
+ return KeymasterSignResult::upgrade;
+ }
+
+ if (op.errorCode() != km::ErrorCode::OK) {
+ LOG(ERROR) << "Error starting keymaster signature transaction: " << int32_t(op.errorCode());
+ return KeymasterSignResult::error;
+ }
+
+ if (!op.updateCompletely(input, &output)) {
+ LOG(ERROR) << "Error sending data to keymaster signature transaction: "
+ << uint32_t(op.errorCode());
+ return KeymasterSignResult::error;
+ }
+
+ if (!op.finish(&output)) {
+ LOG(ERROR) << "Error finalizing keymaster signature transaction: "
+ << int32_t(op.errorCode());
+ return KeymasterSignResult::error;
+ }
+
+ *signature_buffer = reinterpret_cast<uint8_t*>(malloc(output.size()));
+ if (*signature_buffer == nullptr) {
+ LOG(ERROR) << "Error allocation buffer for keymaster signature";
+ 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/fscrypt/Keymaster.h b/crypto/fscrypt/Keymaster.h
new file mode 100644
index 0000000..6f74db4
--- /dev/null
+++ b/crypto/fscrypt/Keymaster.h
@@ -0,0 +1,171 @@
+/*
+ * 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 "KeyBuffer.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include <android-base/macros.h>
+#include <keymasterV4_1/Keymaster.h>
+#include <keymasterV4_1/authorization_set.h>
+
+namespace km {
+
+using namespace ::android::hardware::keymaster::V4_1;
+
+// Surprisingly -- to me, at least -- this is totally fine. You can re-define symbols that were
+// brought in via a using directive (the "using namespace") above. In general this seems like a
+// dangerous thing to rely on, but in this case its implications are simple and straightforward:
+// km::ErrorCode refers to the 4.0 ErrorCode, though we pull everything else from 4.1.
+using ErrorCode = ::android::hardware::keymaster::V4_0::ErrorCode;
+using V4_1_ErrorCode = ::android::hardware::keymaster::V4_1::ErrorCode;
+
+} // namespace km
+
+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() const { return mError == km::ErrorCode::OK; }
+ km::ErrorCode errorCode() const { 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);
+ // Exports a keymaster key with STORAGE_KEY tag wrapped with a per-boot ephemeral key
+ km::ErrorCode exportKey(const KeyBuffer& kmKey, 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();
+
+ // Tell all Keymaster instances that early boot has ended and early boot-only keys can no longer
+ // be created or used.
+ static void earlyBootEnded();
+
+ private:
+ android::sp<KmDevice> mDevice;
+ DISALLOW_COPY_AND_ASSIGN(Keymaster);
+ static bool hmacKeyGenerated;
+};
+
+// 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/fscrypt/MetadataCrypt.cpp b/crypto/fscrypt/MetadataCrypt.cpp
new file mode 100755
index 0000000..853e81e
--- /dev/null
+++ b/crypto/fscrypt/MetadataCrypt.cpp
@@ -0,0 +1,387 @@
+/*
+ * 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 "MetadataCrypt.h"
+#include "KeyBuffer.h"
+
+#include <algorithm>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <fcntl.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <cutils/fs.h>
+#include <fs_mgr.h>
+#include <libdm/dm.h>
+
+#include "Checkpoint.h"
+#include "CryptoType.h"
+#include "EncryptInplace.h"
+#include "FsCrypt.h"
+#include "KeyStorage.h"
+#include "KeyUtil.h"
+#include "Keymaster.h"
+#include "Utils.h"
+#include "VoldUtil.h"
+
+#define TABLE_LOAD_RETRIES 10
+
+
+using android::fs_mgr::FstabEntry;
+using android::fs_mgr::GetEntryForMountPoint;
+using ::KeyBuffer;
+using namespace android::dm;
+
+// Parsed from metadata options
+struct CryptoOptions {
+ struct CryptoType cipher = invalid_crypto_type;
+ bool use_legacy_options_format = false;
+ bool set_dun = true; // Non-legacy driver always sets DUN
+ bool use_hw_wrapped_key = false;
+};
+
+static const std::string kDmNameUserdata = "userdata";
+
+static const char* kFn_keymaster_key_blob = "keymaster_key_blob";
+static const char* kFn_keymaster_key_blob_upgraded = "keymaster_key_blob_upgraded";
+
+// The first entry in this table is the default crypto type.
+constexpr CryptoType supported_crypto_types[] = {aes_256_xts, adiantum};
+
+static_assert(validateSupportedCryptoTypes(64, supported_crypto_types,
+ array_length(supported_crypto_types)),
+ "We have a CryptoType which was incompletely constructed.");
+
+constexpr CryptoType legacy_aes_256_xts =
+ CryptoType().set_config_name("aes-256-xts").set_kernel_name("AES-256-XTS").set_keysize(64);
+
+static_assert(isValidCryptoType(64, legacy_aes_256_xts),
+ "We have a CryptoType which was incompletely constructed.");
+
+// Returns KeyGeneration suitable for key as described in CryptoOptions
+const KeyGeneration makeGen(const CryptoOptions& options) {
+ return KeyGeneration{options.cipher.get_keysize(), true, options.use_hw_wrapped_key};
+}
+
+static bool mount_via_fs_mgr(const char* mount_point, const char* blk_device) {
+ // We're about to mount data not verified by verified boot. Tell Keymaster instances that early
+ // boot has ended.
+ ::Keymaster::earlyBootEnded();
+
+ // fs_mgr_do_mount runs fsck. Use setexeccon to run trusted
+ // partitions in the fsck domain.
+ if (setexeccon(::sFsckContext)) {
+ PLOG(ERROR) << "Failed to setexeccon";
+ return false;
+ }
+
+ if (fstab_default.empty()) {
+ if (!ReadDefaultFstab(&fstab_default)) {
+ PLOG(ERROR) << "Failed to open default fstab";
+ return -1;
+ }
+ }
+ auto mount_rc = fs_mgr_do_mount(&fstab_default, const_cast<char*>(mount_point),
+ const_cast<char*>(blk_device), nullptr,
+ ::cp_needsCheckpoint(), true);
+ 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(INFO) << "mount_via_fs_mgr::Mounted " << mount_point;
+ return true;
+}
+
+// Note: It is possible to orphan a key if it is removed before deleting
+// Update this once keymaster APIs change, and we have a proper commit.
+static void commit_key(const std::string& dir) {
+ while (!android::base::WaitForProperty("vold.checkpoint_committed", "1")) {
+ LOG(ERROR) << "Wait for boot timed out";
+ }
+ Keymaster keymaster;
+ auto keyPath = dir + "/" + kFn_keymaster_key_blob;
+ auto newKeyPath = dir + "/" + kFn_keymaster_key_blob_upgraded;
+ std::string key;
+
+ if (!android::base::ReadFileToString(keyPath, &key)) {
+ LOG(ERROR) << "Failed to read old key: " << dir;
+ return;
+ }
+ if (rename(newKeyPath.c_str(), keyPath.c_str()) != 0) {
+ PLOG(ERROR) << "Unable to move upgraded key to location: " << keyPath;
+ return;
+ }
+ if (!keymaster.deleteKey(key)) {
+ LOG(ERROR) << "Key deletion failed during upgrade, continuing anyway: " << dir;
+ }
+ LOG(INFO) << "Old Key deleted: " << dir;
+}
+
+static bool read_key(const std::string& metadata_key_dir, const KeyGeneration& gen,
+ KeyBuffer* key) {
+ if (metadata_key_dir.empty()) {
+ LOG(ERROR) << "Failed to get metadata_key_dir";
+ return false;
+ }
+ std::string sKey;
+ auto dir = metadata_key_dir + "/key";
+ LOG(INFO) << "metadata_key_dir/key: " << dir;
+ if (fs_mkdirs(dir.c_str(), 0700)) {
+ PLOG(ERROR) << "Creating directories: " << dir;
+ return false;
+ }
+ auto temp = metadata_key_dir + "/tmp";
+ auto newKeyPath = dir + "/" + kFn_keymaster_key_blob_upgraded;
+ /* If we have a leftover upgraded key, delete it.
+ * We either failed an update and must return to the old key,
+ * or we rebooted before commiting the keys in a freak accident.
+ * Either way, we can re-upgrade the key if we need to.
+ */
+
+ Keymaster keymaster;
+ if (pathExists(newKeyPath)) {
+ if (!android::base::ReadFileToString(newKeyPath, &sKey))
+ LOG(ERROR) << "Failed to read incomplete key: " << dir;
+ else if (!keymaster.deleteKey(sKey))
+ LOG(ERROR) << "Incomplete key deletion failed, continuing anyway: " << dir;
+ else
+ unlink(newKeyPath.c_str());
+ }
+ bool needs_cp = cp_needsCheckpoint();
+ if (!retrieveOrGenerateKey(dir, temp, kEmptyAuthentication, gen, key, true)) return false;
+ if (needs_cp && pathExists(newKeyPath)) std::thread(commit_key, dir).detach();
+ return true;
+}
+
+static bool get_number_of_sectors(const std::string& real_blkdev, uint64_t* nr_sec) {
+ if (::GetBlockDev512Sectors(real_blkdev, nr_sec) != android::OK) {
+ PLOG(ERROR) << "Unable to measure size of " << real_blkdev;
+ return false;
+ }
+ return true;
+}
+
+static bool create_crypto_blk_dev(const std::string& dm_name, const std::string& blk_device,
+ const KeyBuffer& key, const CryptoOptions& options,
+ std::string* crypto_blkdev, uint64_t* nr_sec) {
+ if (!get_number_of_sectors(blk_device, nr_sec)) return false;
+ // TODO(paulcrowley): don't hardcode that DmTargetDefaultKey uses 4096-byte
+ // sectors
+ *nr_sec &= ~7;
+
+ KeyBuffer module_key;
+ if (options.use_hw_wrapped_key) {
+ if (!exportWrappedStorageKey(key, &module_key)) {
+ LOG(ERROR) << "Failed to get ephemeral wrapped key";
+ return false;
+ }
+ } else {
+ module_key = key;
+ }
+
+ KeyBuffer hex_key_buffer;
+ if (::StrToHex(module_key, hex_key_buffer) != android::OK) {
+ LOG(ERROR) << "Failed to turn key to hex";
+ return false;
+ }
+ std::string hex_key(hex_key_buffer.data(), hex_key_buffer.size());
+
+ auto target = std::make_unique<DmTargetDefaultKey>(0, *nr_sec, options.cipher.get_kernel_name(),
+ hex_key, blk_device, 0);
+ if (options.use_legacy_options_format) target->SetUseLegacyOptionsFormat();
+ if (options.set_dun) target->SetSetDun();
+ if (options.use_hw_wrapped_key) target->SetWrappedKeyV0();
+
+ DmTable table;
+ table.AddTarget(std::move(target));
+
+ auto& dm = DeviceMapper::Instance();
+ for (int i = 0;; i++) {
+ if (dm.CreateDevice(dm_name, table)) {
+ break;
+ }
+ if (i + 1 >= TABLE_LOAD_RETRIES) {
+ PLOG(ERROR) << "Could not create default-key device " << dm_name;
+ return false;
+ }
+ PLOG(INFO) << "Could not create default-key device, retrying";
+ usleep(500000);
+ }
+
+ if (!dm.GetDmDevicePathByName(dm_name, crypto_blkdev)) {
+ LOG(ERROR) << "Cannot retrieve default-key device status " << dm_name;
+ return false;
+ }
+ std::stringstream ss;
+ ss << *crypto_blkdev;
+ LOG(INFO) << "Created device: " << ss.str();
+ return true;
+}
+
+static const CryptoType& lookup_cipher(const std::string& cipher_name) {
+ if (cipher_name.empty()) return supported_crypto_types[0];
+ for (size_t i = 0; i < array_length(supported_crypto_types); i++) {
+ if (cipher_name == supported_crypto_types[i].get_config_name()) {
+ return supported_crypto_types[i];
+ }
+ }
+ return invalid_crypto_type;
+}
+
+static bool parse_options(const std::string& options_string, CryptoOptions* options) {
+ auto parts = android::base::Split(options_string, ":");
+ if (parts.size() < 1 || parts.size() > 2) {
+ LOG(ERROR) << "Invalid metadata encryption option: " << options_string;
+ return false;
+ }
+ std::string cipher_name = parts[0];
+ options->cipher = lookup_cipher(cipher_name);
+ if (options->cipher.get_kernel_name() == nullptr) {
+ LOG(ERROR) << "No metadata cipher named " << cipher_name << " found";
+ return false;
+ }
+
+ if (parts.size() == 2) {
+ if (parts[1] == "wrappedkey_v0") {
+ options->use_hw_wrapped_key = true;
+ } else {
+ LOG(ERROR) << "Invalid metadata encryption flag: " << parts[1];
+ return false;
+ }
+ }
+ return true;
+}
+
+bool fscrypt_mount_metadata_encrypted(const std::string& blk_device, const std::string& mount_point,
+ bool needs_encrypt) {
+ LOG(INFO) << "fscrypt_mount_metadata_encrypted: " << mount_point << " " << needs_encrypt;
+ auto encrypted_state = android::base::GetProperty("ro.crypto.state", "");
+ if (encrypted_state != "" && encrypted_state != "encrypted") {
+ LOG(ERROR) << "fscrypt_enable_crypto got unexpected starting state: " << encrypted_state;
+ return false;
+ }
+ if (fstab_default.empty()) {
+ if (!ReadDefaultFstab(&fstab_default)) {
+ PLOG(ERROR) << "Failed to open default fstab";
+ return -1;
+ }
+ }
+ auto data_rec = GetEntryForMountPoint(&fstab_default, mount_point);
+ if (!data_rec) {
+ LOG(ERROR) << "Failed to get data_rec for " << mount_point;
+ return false;
+ }
+
+ constexpr unsigned int pre_gki_level = 29;
+ unsigned int options_format_version = android::base::GetUintProperty<unsigned int>(
+ "ro.crypto.dm_default_key.options_format.version",
+ (GetFirstApiLevel() <= pre_gki_level ? 1 : 2));
+
+ CryptoOptions options;
+ if (options_format_version == 1) {
+ if (!data_rec->metadata_encryption.empty()) {
+ LOG(ERROR) << "metadata_encryption options cannot be set in legacy mode";
+ return false;
+ }
+ options.cipher = legacy_aes_256_xts;
+ options.use_legacy_options_format = true;
+ if (is_metadata_wrapped_key_supported()) {
+ options.use_hw_wrapped_key = true;
+ LOG(INFO) << "metadata_wrapped_key_true";
+ }
+ options.set_dun = android::base::GetBoolProperty("ro.crypto.set_dun", false);
+ if (!options.set_dun && data_rec->fs_mgr_flags.checkpoint_blk) {
+ LOG(ERROR)
+ << "Block checkpoints and metadata encryption require ro.crypto.set_dun option";
+ return false;
+ }
+ } else if (options_format_version == 2) {
+ if (!parse_options(data_rec->metadata_encryption, &options)) return false;
+ } else {
+ LOG(ERROR) << "Unknown options_format_version: " << options_format_version;
+ return false;
+ }
+ auto gen = needs_encrypt ? makeGen(options) : neverGen();
+ KeyBuffer key;
+ if (!read_key(data_rec->metadata_key_dir, gen, &key)) return false;
+
+ std::string crypto_blkdev;
+ uint64_t nr_sec;
+ if (!create_crypto_blk_dev(kDmNameUserdata, blk_device, key, options, &crypto_blkdev, &nr_sec))
+ 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(crypto_blkdev.data(), blk_device.data(), 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(INFO) << "Mounting metadata-encrypted filesystem:" << mount_point;
+ mount_via_fs_mgr(mount_point.c_str(), crypto_blkdev.c_str());
+ android::base::SetProperty("ro.crypto.fs_crypto_blkdev", crypto_blkdev);
+
+ // Record that there's at least one fstab entry with metadata encryption
+ if (!android::base::SetProperty("ro.crypto.metadata.enabled", "true")) {
+ LOG(WARNING) << "failed to set ro.crypto.metadata.enabled"; // This isn't fatal
+ }
+ return true;
+}
+
+static bool get_volume_options(CryptoOptions* options) {
+ return parse_options(android::base::GetProperty("ro.crypto.volume.metadata.encryption", ""),
+ options);
+}
+
+bool defaultkey_volume_keygen(KeyGeneration* gen) {
+ CryptoOptions options;
+ if (!get_volume_options(&options)) return false;
+ *gen = makeGen(options);
+ return true;
+}
+
+bool defaultkey_setup_ext_volume(const std::string& label, const std::string& blk_device,
+ const KeyBuffer& key, std::string* out_crypto_blkdev) {
+ LOG(ERROR) << "defaultkey_setup_ext_volume: " << label << " " << blk_device;
+
+ CryptoOptions options;
+ if (!get_volume_options(&options)) return false;
+ uint64_t nr_sec;
+ return create_crypto_blk_dev(label, blk_device, key, options, out_crypto_blkdev, &nr_sec);
+}
diff --git a/crypto/fscrypt/MetadataCrypt.h b/crypto/fscrypt/MetadataCrypt.h
new file mode 100644
index 0000000..0370664
--- /dev/null
+++ b/crypto/fscrypt/MetadataCrypt.h
@@ -0,0 +1,35 @@
+/*
+ * 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>
+
+#include "KeyBuffer.h"
+#include "KeyUtil.h"
+
+
+bool fscrypt_mount_metadata_encrypted(const std::string& block_device,
+ const std::string& mount_point, bool needs_encrypt);
+
+bool defaultkey_volume_keygen(KeyGeneration* gen);
+
+bool defaultkey_setup_ext_volume(const std::string& label, const std::string& blk_device,
+ const KeyBuffer& key,
+ std::string* out_crypto_blkdev);
+
+#endif
diff --git a/crypto/fscrypt/Process.cpp b/crypto/fscrypt/Process.cpp
new file mode 100644
index 0000000..277d6a3
--- /dev/null
+++ b/crypto/fscrypt/Process.cpp
@@ -0,0 +1,180 @@
+/*
+ * 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.
+ */
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fts.h>
+#include <poll.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <fstream>
+#include <mntent.h>
+#include <unordered_set>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include "Process.h"
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace vold {
+
+static bool checkMaps(const std::string& path, const std::string& prefix) {
+ bool found = false;
+ auto file = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
+ if (!file) {
+ return false;
+ }
+
+ char* buf = nullptr;
+ size_t len = 0;
+ while (getline(&buf, &len, file.get()) != -1) {
+ std::string line(buf);
+ std::string::size_type pos = line.find('/');
+ if (pos != std::string::npos) {
+ line = line.substr(pos);
+ if (android::base::StartsWith(line, prefix)) {
+ LOG(WARNING) << "Found map " << path << " referencing " << line;
+ found = true;
+ break;
+ }
+ }
+ }
+ free(buf);
+
+ return found;
+}
+
+static bool checkSymlink(const std::string& path, const std::string& prefix) {
+ std::string res;
+ if (android::base::Readlink(path, &res)) {
+ if (android::base::StartsWith(res, prefix)) {
+ LOG(WARNING) << "Found symlink " << path << " referencing " << res;
+ return true;
+ }
+ }
+ return false;
+}
+
+// TODO: Refactor the code with KillProcessesWithOpenFiles().
+int KillProcessesWithMounts(const std::string& prefix, int signal) {
+ std::unordered_set<pid_t> pids;
+
+ auto proc_d = std::unique_ptr<DIR, int (*)(DIR*)>(opendir("/proc"), closedir);
+ if (!proc_d) {
+ PLOG(ERROR) << "Failed to open proc";
+ return -1;
+ }
+
+ struct dirent* proc_de;
+ while ((proc_de = readdir(proc_d.get())) != nullptr) {
+ // We only care about valid PIDs
+ pid_t pid;
+ if (proc_de->d_type != DT_DIR) continue;
+ if (!android::base::ParseInt(proc_de->d_name, &pid)) continue;
+
+ // Look for references to prefix
+ std::string mounts_file(StringPrintf("/proc/%d/mounts", pid));
+ auto fp = std::unique_ptr<FILE, int (*)(FILE*)>(
+ setmntent(mounts_file.c_str(), "r"), endmntent);
+ if (!fp) {
+ PLOG(WARNING) << "Failed to open " << mounts_file;
+ continue;
+ }
+
+ // Check if obb directory is mounted, and get all packages of mounted app data directory.
+ mntent* mentry;
+ while ((mentry = getmntent(fp.get())) != nullptr) {
+ if (android::base::StartsWith(mentry->mnt_dir, prefix)) {
+ pids.insert(pid);
+ break;
+ }
+ }
+ }
+ if (signal != 0) {
+ for (const auto& pid : pids) {
+ LOG(WARNING) << "Killing pid "<< pid << " with signal " << strsignal(signal) <<
+ " because it has a mount with prefix " << prefix;
+ kill(pid, signal);
+ }
+ }
+ return pids.size();
+}
+
+int KillProcessesWithOpenFiles(const std::string& prefix, int signal) {
+ std::unordered_set<pid_t> pids;
+
+ auto proc_d = std::unique_ptr<DIR, int (*)(DIR*)>(opendir("/proc"), closedir);
+ if (!proc_d) {
+ PLOG(ERROR) << "Failed to open proc";
+ return -1;
+ }
+
+ struct dirent* proc_de;
+ while ((proc_de = readdir(proc_d.get())) != nullptr) {
+ // We only care about valid PIDs
+ pid_t pid;
+ if (proc_de->d_type != DT_DIR) continue;
+ if (!android::base::ParseInt(proc_de->d_name, &pid)) continue;
+
+ // Look for references to prefix
+ bool found = false;
+ auto path = StringPrintf("/proc/%d", pid);
+ found |= checkMaps(path + "/maps", prefix);
+ found |= checkSymlink(path + "/cwd", prefix);
+ found |= checkSymlink(path + "/root", prefix);
+ found |= checkSymlink(path + "/exe", prefix);
+
+ auto fd_path = path + "/fd";
+ auto fd_d = std::unique_ptr<DIR, int (*)(DIR*)>(opendir(fd_path.c_str()), closedir);
+ if (!fd_d) {
+ PLOG(WARNING) << "Failed to open " << fd_path;
+ } else {
+ struct dirent* fd_de;
+ while ((fd_de = readdir(fd_d.get())) != nullptr) {
+ if (fd_de->d_type != DT_LNK) continue;
+ found |= checkSymlink(fd_path + "/" + fd_de->d_name, prefix);
+ }
+ }
+
+ if (found) {
+ pids.insert(pid);
+ }
+ }
+ if (signal != 0) {
+ for (const auto& pid : pids) {
+ LOG(WARNING) << "Sending " << strsignal(signal) << " to " << pid;
+ kill(pid, signal);
+ }
+ }
+ return pids.size();
+}
+
+} // namespace vold
+} // namespace android
diff --git a/crypto/fscrypt/Process.h b/crypto/fscrypt/Process.h
new file mode 100644
index 0000000..1c59812
--- /dev/null
+++ b/crypto/fscrypt/Process.h
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+#ifndef _PROCESS_H
+#define _PROCESS_H
+
+namespace android {
+namespace vold {
+
+int KillProcessesWithOpenFiles(const std::string& path, int signal);
+int KillProcessesWithMounts(const std::string& path, int signal);
+
+} // namespace vold
+} // namespace android
+
+#endif
diff --git a/crypto/fscrypt/ScryptParameters.cpp b/crypto/fscrypt/ScryptParameters.cpp
new file mode 100644
index 0000000..669809b
--- /dev/null
+++ b/crypto/fscrypt/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/fscrypt/ScryptParameters.h b/crypto/fscrypt/ScryptParameters.h
new file mode 100644
index 0000000..1b43ea5
--- /dev/null
+++ b/crypto/fscrypt/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/fscrypt/Utils.cpp b/crypto/fscrypt/Utils.cpp
new file mode 100755
index 0000000..3b0eda0
--- /dev/null
+++ b/crypto/fscrypt/Utils.cpp
@@ -0,0 +1,1622 @@
+/*
+ * 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 "Process.h"
+#include "sehandle.h"
+
+#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <cutils/fs.h>
+#include <logwrap/logwrap.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_projectid_config.h>
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <linux/posix_acl.h>
+#include <linux/posix_acl_xattr.h>
+#include <mntent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/xattr.h>
+#include <unistd.h>
+
+#include <filesystem>
+#include <list>
+#include <mutex>
+#include <regex>
+#include <thread>
+
+#ifndef UMOUNT_NOFOLLOW
+#define UMOUNT_NOFOLLOW 0x00000008 /* Don't follow symlink on umount */
+#endif
+
+using namespace std::chrono_literals;
+using android::base::EndsWith;
+using android::base::ReadFileToString;
+using android::base::StartsWith;
+using android::base::StringPrintf;
+using android::base::unique_fd;
+
+struct selabel_handle* sehandle;
+
+security_context_t sBlkidContext = nullptr;
+security_context_t sBlkidUntrustedContext = nullptr;
+security_context_t sFsckContext = nullptr;
+security_context_t sFsckUntrustedContext = nullptr;
+
+bool sSleepOnUnmount = true;
+
+static const char* kBlkidPath = "/system/bin/blkid";
+static const char* kKeyPath = "/data/misc/vold";
+
+static const char* kProcDevices = "/proc/devices";
+static const char* kProcFilesystems = "/proc/filesystems";
+
+static const char* kAndroidDir = "/Android/";
+static const char* kAppDataDir = "/Android/data/";
+static const char* kAppMediaDir = "/Android/media/";
+static const char* kAppObbDir = "/Android/obb/";
+
+static const char* kMediaProviderCtx = "u:r:mediaprovider:";
+static const char* kMediaProviderAppCtx = "u:r:mediaprovider_app:";
+
+// Lock used to protect process-level SELinux changes from racing with each
+// other between multiple threads.
+static std::mutex kSecurityLock;
+
+std::string GetFuseMountPathForUser(userid_t user_id, const std::string& relative_upper_path) {
+ return StringPrintf("/mnt/user/%d/%s", user_id, relative_upper_path.c_str());
+}
+
+android::status_t CreateDeviceNode(const std::string& path, dev_t dev) {
+ std::lock_guard<std::mutex> lock(kSecurityLock);
+ const char* cpath = path.c_str();
+ android::status_t res = 0;
+
+ char* secontext = nullptr;
+ if (sehandle) {
+ if (!selabel_lookup(sehandle, &secontext, cpath, S_IFBLK)) {
+ setfscreatecon(secontext);
+ }
+ }
+
+ mode_t mode = 0660 | S_IFBLK;
+ if (mknod(cpath, mode, dev) < 0) {
+ if (errno != EEXIST) {
+ PLOG(ERROR) << "Failed to create device node for " << major(dev) << ":" << minor(dev)
+ << " at " << path;
+ res = -errno;
+ }
+ }
+
+ if (secontext) {
+ setfscreatecon(nullptr);
+ freecon(secontext);
+ }
+
+ return res;
+}
+
+android::status_t DestroyDeviceNode(const std::string& path) {
+ const char* cpath = path.c_str();
+ if (TEMP_FAILURE_RETRY(unlink(cpath))) {
+ return -errno;
+ } else {
+ return android::OK;
+ }
+}
+
+// Sets a default ACL on the directory.
+int SetDefaultAcl(const std::string& path, mode_t mode, uid_t uid, gid_t gid,
+ std::vector<gid_t> additionalGids) {
+ if (IsSdcardfsUsed()) {
+ // sdcardfs magically takes care of this
+ return android::OK;
+ }
+
+ size_t num_entries = 3 + (additionalGids.size() > 0 ? additionalGids.size() + 1 : 0);
+ size_t size = sizeof(posix_acl_xattr_header) + num_entries * sizeof(posix_acl_xattr_entry);
+ auto buf = std::make_unique<uint8_t[]>(size);
+
+ posix_acl_xattr_header* acl_header = reinterpret_cast<posix_acl_xattr_header*>(buf.get());
+ acl_header->a_version = POSIX_ACL_XATTR_VERSION;
+
+ posix_acl_xattr_entry* entry =
+ reinterpret_cast<posix_acl_xattr_entry*>(buf.get() + sizeof(posix_acl_xattr_header));
+
+ int tag_index = 0;
+
+ entry[tag_index].e_tag = ACL_USER_OBJ;
+ // The existing mode_t mask has the ACL in the lower 9 bits:
+ // the lowest 3 for "other", the next 3 the group, the next 3 for the owner
+ // Use the mode_t masks to get these bits out, and shift them to get the
+ // correct value per entity.
+ //
+ // Eg if mode_t = 0700, rwx for the owner, then & S_IRWXU >> 6 results in 7
+ entry[tag_index].e_perm = (mode & S_IRWXU) >> 6;
+ entry[tag_index].e_id = uid;
+ tag_index++;
+
+ entry[tag_index].e_tag = ACL_GROUP_OBJ;
+ entry[tag_index].e_perm = (mode & S_IRWXG) >> 3;
+ entry[tag_index].e_id = gid;
+ tag_index++;
+
+ if (additionalGids.size() > 0) {
+ for (gid_t additional_gid : additionalGids) {
+ entry[tag_index].e_tag = ACL_GROUP;
+ entry[tag_index].e_perm = (mode & S_IRWXG) >> 3;
+ entry[tag_index].e_id = additional_gid;
+ tag_index++;
+ }
+
+ entry[tag_index].e_tag = ACL_MASK;
+ entry[tag_index].e_perm = (mode & S_IRWXG) >> 3;
+ entry[tag_index].e_id = 0;
+ tag_index++;
+ }
+
+ entry[tag_index].e_tag = ACL_OTHER;
+ entry[tag_index].e_perm = mode & S_IRWXO;
+ entry[tag_index].e_id = 0;
+
+ int ret = setxattr(path.c_str(), XATTR_NAME_POSIX_ACL_DEFAULT, acl_header, size, 0);
+
+ if (ret != 0) {
+ PLOG(ERROR) << "Failed to set default ACL on " << path;
+ }
+
+ return ret;
+}
+
+int SetQuotaInherit(const std::string& path) {
+ unsigned long flags;
+
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
+ if (fd == -1) {
+ PLOG(ERROR) << "Failed to open " << path << " to set project id inheritance.";
+ return -1;
+ }
+
+ int ret = ioctl(fd, FS_IOC_GETFLAGS, &flags);
+ if (ret == -1) {
+ PLOG(ERROR) << "Failed to get flags for " << path << " to set project id inheritance.";
+ return ret;
+ }
+
+ flags |= FS_PROJINHERIT_FL;
+
+ ret = ioctl(fd, FS_IOC_SETFLAGS, &flags);
+ if (ret == -1) {
+ PLOG(ERROR) << "Failed to set flags for " << path << " to set project id inheritance.";
+ return ret;
+ }
+
+ return 0;
+}
+
+int SetQuotaProjectId(const std::string& path, long projectId) {
+ struct fsxattr fsx;
+
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
+ if (fd == -1) {
+ PLOG(ERROR) << "Failed to open " << path << " to set project id.";
+ return -1;
+ }
+
+ int ret = ioctl(fd, FS_IOC_FSGETXATTR, &fsx);
+ if (ret == -1) {
+ PLOG(ERROR) << "Failed to get extended attributes for " << path << " to get project id.";
+ return ret;
+ }
+
+ fsx.fsx_projid = projectId;
+ return ioctl(fd, FS_IOC_FSSETXATTR, &fsx);
+}
+
+int PrepareDirWithProjectId(const std::string& path, mode_t mode, uid_t uid, gid_t gid,
+ long projectId) {
+ int ret = fs_prepare_dir(path.c_str(), mode, uid, gid);
+
+ if (ret != 0) {
+ return ret;
+ }
+
+ if (!IsSdcardfsUsed()) {
+ ret = SetQuotaProjectId(path, projectId);
+ }
+
+ return ret;
+}
+
+static int FixupAppDir(const std::string& path, mode_t mode, uid_t uid, gid_t gid, long projectId) {
+ namespace fs = std::filesystem;
+
+ // Setup the directory itself correctly
+ int ret = PrepareDirWithProjectId(path, mode, uid, gid, projectId);
+ if (ret != android::OK) {
+ return ret;
+ }
+
+ // Fixup all of its file entries
+ for (const auto& itEntry : fs::directory_iterator(path)) {
+ ret = lchown(itEntry.path().c_str(), uid, gid);
+ if (ret != 0) {
+ return ret;
+ }
+
+ ret = chmod(itEntry.path().c_str(), mode);
+ if (ret != 0) {
+ return ret;
+ }
+
+ if (!IsSdcardfsUsed()) {
+ ret = SetQuotaProjectId(itEntry.path(), projectId);
+ if (ret != 0) {
+ return ret;
+ }
+ }
+ }
+
+ return android::OK;
+}
+
+int PrepareAppDirFromRoot(const std::string& path, const std::string& root, int appUid,
+ bool fixupExisting) {
+ long projectId;
+ size_t pos;
+ int ret = 0;
+ bool sdcardfsSupport = IsSdcardfsUsed();
+
+ // Make sure the Android/ directories exist and are setup correctly
+ ret = PrepareAndroidDirs(root);
+ if (ret != 0) {
+ LOG(ERROR) << "Failed to prepare Android/ directories.";
+ return ret;
+ }
+
+ // Now create the application-specific subdir(s)
+ // path is something like /data/media/0/Android/data/com.foo/files
+ // First, chop off the volume root, eg /data/media/0
+ std::string pathFromRoot = path.substr(root.length());
+
+ uid_t uid = appUid;
+ gid_t gid = AID_MEDIA_RW;
+ std::vector<gid_t> additionalGids;
+ std::string appDir;
+
+ // Check that the next part matches one of the allowed Android/ dirs
+ if (StartsWith(pathFromRoot, kAppDataDir)) {
+ appDir = kAppDataDir;
+ if (!sdcardfsSupport) {
+ gid = AID_EXT_DATA_RW;
+ // Also add the app's own UID as a group; since apps belong to a group
+ // that matches their UID, this ensures that they will always have access to
+ // the files created in these dirs, even if they are created by other processes
+ additionalGids.push_back(uid);
+ }
+ } else if (StartsWith(pathFromRoot, kAppMediaDir)) {
+ appDir = kAppMediaDir;
+ if (!sdcardfsSupport) {
+ gid = AID_MEDIA_RW;
+ }
+ } else if (StartsWith(pathFromRoot, kAppObbDir)) {
+ appDir = kAppObbDir;
+ if (!sdcardfsSupport) {
+ gid = AID_EXT_OBB_RW;
+ // See comments for kAppDataDir above
+ additionalGids.push_back(uid);
+ }
+ } else {
+ LOG(ERROR) << "Invalid application directory: " << path;
+ return -EINVAL;
+ }
+
+ // mode = 770, plus sticky bit on directory to inherit GID when apps
+ // create subdirs
+ mode_t mode = S_IRWXU | S_IRWXG | S_ISGID;
+ // the project ID for application-specific directories is directly
+ // derived from their uid
+
+ // Chop off the generic application-specific part, eg /Android/data/
+ // this leaves us with something like com.foo/files/
+ std::string leftToCreate = pathFromRoot.substr(appDir.length());
+ if (!EndsWith(leftToCreate, "/")) {
+ leftToCreate += "/";
+ }
+ std::string pathToCreate = root + appDir;
+ int depth = 0;
+ // Derive initial project ID
+ if (appDir == kAppDataDir || appDir == kAppMediaDir) {
+ projectId = uid - AID_APP_START + PROJECT_ID_EXT_DATA_START;
+ } else if (appDir == kAppObbDir) {
+ projectId = uid - AID_APP_START + PROJECT_ID_EXT_OBB_START;
+ }
+
+ while ((pos = leftToCreate.find('/')) != std::string::npos) {
+ std::string component = leftToCreate.substr(0, pos + 1);
+ leftToCreate = leftToCreate.erase(0, pos + 1);
+ pathToCreate = pathToCreate + component;
+
+ if (appDir == kAppDataDir && depth == 1 && component == "cache/") {
+ // All dirs use the "app" project ID, except for the cache dirs in
+ // Android/data, eg Android/data/com.foo/cache
+ // Note that this "sticks" - eg subdirs of this dir need the same
+ // project ID.
+ projectId = uid - AID_APP_START + PROJECT_ID_EXT_CACHE_START;
+ }
+
+ if (fixupExisting && access(pathToCreate.c_str(), F_OK) == 0) {
+ // Fixup all files in this existing directory with the correct UID/GID
+ // and project ID.
+ ret = FixupAppDir(pathToCreate, mode, uid, gid, projectId);
+ } else {
+ ret = PrepareDirWithProjectId(pathToCreate, mode, uid, gid, projectId);
+ }
+
+ if (ret != 0) {
+ return ret;
+ }
+
+ if (depth == 0) {
+ // Set the default ACL on the top-level application-specific directories,
+ // to ensure that even if applications run with a umask of 0077,
+ // new directories within these directories will allow the GID
+ // specified here to write; this is necessary for apps like
+ // installers and MTP, that require access here.
+ //
+ // See man (5) acl for more details.
+ ret = SetDefaultAcl(pathToCreate, mode, uid, gid, additionalGids);
+ if (ret != 0) {
+ return ret;
+ }
+
+ if (!sdcardfsSupport) {
+ // Set project ID inheritance, so that future subdirectories inherit the
+ // same project ID
+ ret = SetQuotaInherit(pathToCreate);
+ if (ret != 0) {
+ return ret;
+ }
+ }
+ }
+
+ depth++;
+ }
+
+ return android::OK;
+}
+
+android::status_t PrepareDir(const std::string& path, mode_t mode, uid_t uid, gid_t gid) {
+ std::lock_guard<std::mutex> lock(kSecurityLock);
+ const char* cpath = path.c_str();
+
+ char* secontext = nullptr;
+ if (sehandle) {
+ if (!selabel_lookup(sehandle, &secontext, cpath, S_IFDIR)) {
+ setfscreatecon(secontext);
+ }
+ }
+
+ int res = fs_prepare_dir(cpath, mode, uid, gid);
+
+ if (secontext) {
+ setfscreatecon(nullptr);
+ freecon(secontext);
+ }
+
+ if (res == 0) {
+ return android::OK;
+ } else {
+ return -errno;
+ }
+}
+
+android::status_t ForceUnmount(const std::string& path) {
+ const char* cpath = path.c_str();
+ if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) {
+ return android::OK;
+ }
+ // Apps might still be handling eject request, so wait before
+ // we start sending signals
+ if (sSleepOnUnmount) sleep(5);
+
+ android::vold::KillProcessesWithOpenFiles(path, SIGINT);
+ if (sSleepOnUnmount) sleep(5);
+ if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) {
+ return android::OK;
+ }
+
+ android::vold::KillProcessesWithOpenFiles(path, SIGTERM);
+ if (sSleepOnUnmount) sleep(5);
+ if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) {
+ return android::OK;
+ }
+
+ android::vold::KillProcessesWithOpenFiles(path, SIGKILL);
+ if (sSleepOnUnmount) sleep(5);
+ if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) {
+ return android::OK;
+ }
+ PLOG(INFO) << "ForceUnmount failed";
+ return -errno;
+}
+
+android::status_t KillProcessesWithMountPrefix(const std::string& path) {
+ if (android::vold::KillProcessesWithMounts(path, SIGINT) == 0) {
+ return android::OK;
+ }
+ if (sSleepOnUnmount) sleep(5);
+
+ if (android::vold::KillProcessesWithMounts(path, SIGTERM) == 0) {
+ return android::OK;
+ }
+ if (sSleepOnUnmount) sleep(5);
+
+ if (android::vold::KillProcessesWithMounts(path, SIGKILL) == 0) {
+ return android::OK;
+ }
+ if (sSleepOnUnmount) sleep(5);
+
+ // Send SIGKILL a second time to determine if we've
+ // actually killed everyone mount
+ if (android::vold::KillProcessesWithMounts(path, SIGKILL) == 0) {
+ return android::OK;
+ }
+ PLOG(ERROR) << "Failed to kill processes using " << path;
+ return -EBUSY;
+}
+
+android::status_t KillProcessesUsingPath(const std::string& path) {
+ if (android::vold::KillProcessesWithOpenFiles(path, SIGINT) == 0) {
+ return android::OK;
+ }
+ if (sSleepOnUnmount) sleep(5);
+
+ if (android::vold::KillProcessesWithOpenFiles(path, SIGTERM) == 0) {
+ return android::OK;
+ }
+ if (sSleepOnUnmount) sleep(5);
+
+ if (android::vold::KillProcessesWithOpenFiles(path, SIGKILL) == 0) {
+ return android::OK;
+ }
+ if (sSleepOnUnmount) sleep(5);
+
+ // Send SIGKILL a second time to determine if we've
+ // actually killed everyone with open files
+ if (android::vold::KillProcessesWithOpenFiles(path, SIGKILL) == 0) {
+ return android::OK;
+ }
+ PLOG(ERROR) << "Failed to kill processes using " << path;
+ return -EBUSY;
+}
+
+android::status_t BindMount(const std::string& source, const std::string& target) {
+ if (UnmountTree(target) < 0) {
+ return -errno;
+ }
+ if (TEMP_FAILURE_RETRY(mount(source.c_str(), target.c_str(), nullptr, MS_BIND, nullptr)) < 0) {
+ PLOG(ERROR) << "Failed to bind mount " << source << " to " << target;
+ return -errno;
+ }
+ return android::OK;
+}
+
+android::status_t Symlink(const std::string& target, const std::string& linkpath) {
+ if (Unlink(linkpath) < 0) {
+ return -errno;
+ }
+ if (TEMP_FAILURE_RETRY(symlink(target.c_str(), linkpath.c_str())) < 0) {
+ PLOG(ERROR) << "Failed to create symlink " << linkpath << " to " << target;
+ return -errno;
+ }
+ return android::OK;
+}
+
+android::status_t Unlink(const std::string& linkpath) {
+ if (TEMP_FAILURE_RETRY(unlink(linkpath.c_str())) < 0 && errno != EINVAL && errno != ENOENT) {
+ PLOG(ERROR) << "Failed to unlink " << linkpath;
+ return -errno;
+ }
+ return android::OK;
+}
+
+android::status_t CreateDir(const std::string& dir, mode_t mode) {
+ struct stat sb;
+ if (TEMP_FAILURE_RETRY(stat(dir.c_str(), &sb)) == 0) {
+ if (S_ISDIR(sb.st_mode)) {
+ return android::OK;
+ } else if (TEMP_FAILURE_RETRY(unlink(dir.c_str())) == -1) {
+ PLOG(ERROR) << "Failed to unlink " << dir;
+ return -errno;
+ }
+ } else if (errno != ENOENT) {
+ PLOG(ERROR) << "Failed to stat " << dir;
+ return -errno;
+ }
+ if (TEMP_FAILURE_RETRY(mkdir(dir.c_str(), mode)) == -1 && errno != EEXIST) {
+ PLOG(ERROR) << "Failed to mkdir " << dir;
+ return -errno;
+ }
+ return android::OK;
+}
+
+bool FindValue(const std::string& raw, const std::string& key, std::string* value) {
+ auto qual = key + "=\"";
+ size_t start = 0;
+ while (true) {
+ start = raw.find(qual, start);
+ if (start == std::string::npos) return false;
+ if (start == 0 || raw[start - 1] == ' ') {
+ break;
+ }
+ start += 1;
+ }
+ start += qual.length